package main | |
import ( | |
"flag" | |
"fmt" | |
"log" | |
"net" | |
"os" | |
"os/exec" | |
"runtime" | |
"github.com/songgao/water" | |
"golang.org/x/net/ipv4" | |
) | |
const ( | |
// BUFFERSIZE of 1500 to read the whole packet from the interface | |
BUFFERSIZE = 1500 | |
// MTU at 1432 because encap takes (up to) 60 bytes IP header + 8 bytes UDP header | |
MTU = "1432" | |
) | |
// DEBUG should be false most of the times as the performance impact is significant. | |
// Set true for stdout debugging. | |
const DEBUG = false | |
// Config is passed as command-line flags | |
var ( | |
localIP = flag.String("local", "", "local address inside the tunnel") | |
remoteIP = flag.String("remote", "", "Remote peer public IP address") | |
port = flag.Int("port", 10001, "UDP port for communication, 9416 by default") | |
) | |
// Function to run /sbin/ip | |
func linuxSBINIP(args ...string) { | |
cmd := exec.Command("/sbin/ip", args...) | |
err := cmd.Run() | |
if nil != err { | |
log.Fatalln("Error running /sbin/ip:", err) | |
} | |
} | |
func main() { | |
flag.Parse() | |
if "" == *localIP { | |
flag.Usage() | |
log.Fatalln("\nlocal ip is not specified") | |
} | |
if "" == *remoteIP { | |
flag.Usage() | |
log.Fatalln("\nremote server is not specified") | |
} | |
// create TUN interface | |
ifaceconfig := water.Config{ | |
DeviceType: water.TUN, | |
} | |
iface, err := water.New(ifaceconfig) | |
if nil != err { | |
log.Fatalln("Unable to allocate TUN interface:", err) | |
} | |
log.Println("Interface allocated:", iface.Name()) | |
// set interface parameters | |
// Only Linux is supported at the moment | |
if runtime.GOOS == "linux" { | |
if os.Geteuid() == 0 { | |
linuxSBINIP("link", "set", "dev", iface.Name(), "mtu", MTU) | |
linuxSBINIP("addr", "add", *localIP, "dev", iface.Name()) | |
linuxSBINIP("link", "set", "dev", iface.Name(), "up") | |
} else { | |
panic("Program must be run with elevated permissions") | |
} | |
} else { | |
panic("OS not supported") | |
} | |
// resolve remote addr | |
remoteAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%v", *remoteIP, *port)) | |
if nil != err { | |
log.Fatalln("Unable to resolve remote addr:", err) | |
} | |
// listen to local socket | |
lstnAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%v", *port)) | |
if nil != err { | |
log.Fatalln("Unable to get UDP socket:", err) | |
} | |
lstnConn, err := net.ListenUDP("udp", lstnAddr) | |
if nil != err { | |
log.Fatalln("Unable to listen on UDP socket:", err) | |
} | |
defer lstnConn.Close() | |
// receive data on the UDP stream and send it through the TUN interface | |
go func() { | |
recvbuf := make([]byte, BUFFERSIZE) | |
for { | |
rxSize, addr, err := lstnConn.ReadFromUDP(recvbuf) | |
if DEBUG { | |
header, _ := ipv4.ParseHeader(recvbuf[:rxSize]) | |
fmt.Printf("Received %d bytes from %v: %+v\n", rxSize, addr, header) | |
if err != nil || rxSize == 0 { | |
fmt.Println("Error: ", err) | |
continue | |
} | |
} | |
// write the data received to the TUN interface | |
iface.Write(recvbuf[:rxSize]) | |
} | |
}() | |
// receive data on the TUN interface and send it through the UDP stream | |
sendbuf := make([]byte, BUFFERSIZE) | |
for { | |
txSize, err := iface.Read(sendbuf) | |
if err != nil { | |
panic(err) | |
} | |
if DEBUG { | |
header, _ := ipv4.ParseHeader(sendbuf[:txSize]) | |
fmt.Printf("Sending to remote: %+v (%+v)\n", header, err) | |
} | |
// write data to the UDP stream | |
lstnConn.WriteToUDP(sendbuf[:txSize], remoteAddr) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment