Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
jaym committed Sep 3, 2017
0 parents commit e9b9c45
Show file tree
Hide file tree
Showing 6 changed files with 246 additions and 0 deletions.
15 changes: 15 additions & 0 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions Gopkg.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"


[[constraint]]
name = "github.com/coreos/go-iptables"
version = "0.2.0"
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
default:
go build -o bin/lsrv cmd/lsrv.go
Empty file added bin/.gitkeep
Empty file.
25 changes: 25 additions & 0 deletions cmd/lsrv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package main

import (
"flag"
"fmt"

lsrv "github.com/jaym/lsrv"
)

var (
VERSION string = "0.0.1"
socket = flag.String("socket", "/tmp/lsrv.sock", "UNIX domain socket")
daemon = flag.Bool("daemon", false, "Run the lsrv daemon if true")
)

func main() {
flag.Parse()

if *daemon {
fmt.Println("Starting lsrv daemon")
lsrv.Spawn(*socket)
} else {
fmt.Println("Client mode")
}
}
178 changes: 178 additions & 0 deletions server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package lsrv

import (
"bufio"
"encoding/binary"
"log"
"net"
"strconv"
"strings"

"github.com/coreos/go-iptables/iptables"
)

type ServiceEntry struct {
service_name string
service_address string
service_port uint16

// The service will respond to the address/port below
dest_address string
dest_port uint16
}

func Spawn(socket_path string) error {
ipt, iperr := initializeIpTables()

if iperr != nil {
log.Fatal("Could not initialize IPTables: ", iperr)
}

ln, err := net.Listen("unix", socket_path)

if err != nil {
log.Fatal("Listen error: ", err)
return err
}

services := []ServiceEntry{}
start_ip := net.IPv4(172, 22, 0, 1)

for {
//TODO: err check
fd, _ := ln.Accept()
input := bufio.NewScanner(fd)

success := input.Scan()
if !success {
log.Println(input.Err())
fd.Close()
}

command := input.Text()
command_split := strings.Fields(command)

if len(command_split) == 0 {
log.Println("Could not parse command")
fd.Close()
}

switch command_split[0] {
case "ADD":
// ADD service_name service_ip service_port dest_port
// Returns:
// OK
if len(command_split) != 5 {
log.Println("Could not parse command")
fd.Close()
} else {
service_port, serr := strconv.ParseUint(command_split[3], 10, 16)
dest_port, derr := strconv.ParseUint(command_split[4], 10, 16)

if serr != nil || derr != nil {
log.Println("Could not parse port")
fd.Close()
break
}

entry := ServiceEntry{
service_name: command_split[1],
service_address: command_split[2],
service_port: uint16(service_port),
dest_address: ipAdd(start_ip, len(services)).String(),
dest_port: uint16(dest_port),
}

services = append(services, entry)
err = materialize(ipt, &services)

if err != nil {
log.Println(err)
break
}

log.Println("Added ", entry)
}
default:
log.Println("Unknown command: ", command_split[0])
}
fd.Close()
}

return nil
}

func materialize(ipt *iptables.IPTables, services *[]ServiceEntry) error {
// Recreate the chain. This is wasteful and not zero downtime, but
// it's easy
err := createChain(ipt)
if err != nil {
return err
}

for _, entry := range *services {
rulespec := []string{"-p", "tcp", "-d", entry.dest_address, "--dport",
strconv.FormatUint(uint64(entry.dest_port), 10), "-j", "DNAT",
"--to", entry.service_address + ":" + strconv.FormatUint(uint64(entry.service_port), 10)}
err = ipt.Append("nat", "LSRV", rulespec...)
if err != nil {
log.Fatal(err)
}
}

return nil
}

func ipAdd(start net.IP, add int) net.IP { // IPv4 only
start = start.To4()
result := make(net.IP, 4)
binary.BigEndian.PutUint32(result, binary.BigEndian.Uint32(start)+uint32(add))
return result
}

func initializeIpTables() (*iptables.IPTables, error) {
ipt, err := iptables.New()

if err != nil {
return nil, err
}

err = createChain(ipt)

if err != nil {
return nil, err
}

return ipt, nil
}

func createChain(ipt *iptables.IPTables) error {
cleanup(ipt)

ipt.NewChain("nat", "LSRV")
ipt.AppendUnique("nat", "OUTPUT", "-jLSRV")

return nil
}

func cleanup(ipt *iptables.IPTables) error {
chains, err := ipt.ListChains("nat")
if err != nil {
return err
}

containsChain := false
for _, elem := range chains {
if elem == "LSRV" {
containsChain = true
}
}

if containsChain {
log.Println("Deleting chain LSRV")
ipt.Delete("nat", "OUTPUT", "-jLSRV")
ipt.ClearChain("nat", "LSRV")
ipt.DeleteChain("nat", "LSRV")
}
return nil
}

0 comments on commit e9b9c45

Please sign in to comment.