-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit e9b9c45
Showing
6 changed files
with
246 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |