Skip to content

Instantly share code, notes, and snippets.

@pjperez
Last active July 1, 2018 13:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pjperez/a01e3dfd4f89bb122a6ed372eadfd64d to your computer and use it in GitHub Desktop.
Save pjperez/a01e3dfd4f89bb122a6ed372eadfd64d to your computer and use it in GitHub Desktop.
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