Last active
July 1, 2018 13:36
-
-
Save pjperez/a01e3dfd4f89bb122a6ed372eadfd64d to your computer and use it in GitHub Desktop.
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
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