WIP: Simple working client/server
This commit is contained in:
		
							
								
								
									
										1
									
								
								stage1/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								stage1/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| ## Stage1: Point-to-point Tunnel w/ no Encryption | ||||
							
								
								
									
										32
									
								
								stage1/client.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								stage1/client.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| package stage1 | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| 	"net/netip" | ||||
| 	"runtime/debug" | ||||
| ) | ||||
|  | ||||
| func RunClient(serverAddrStr string) { | ||||
| 	defer func() { | ||||
| 		if r := recover(); r != nil { | ||||
| 			fmt.Printf("%v", r) | ||||
| 			debug.PrintStack() | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	iface, err := openInterface(network, clientIP, netName) | ||||
| 	must(err) | ||||
|  | ||||
| 	myAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", port)) | ||||
| 	must(err) | ||||
|  | ||||
| 	conn, err := net.ListenUDP("udp", myAddr) | ||||
| 	must(err) | ||||
|  | ||||
| 	serverAddr, err := netip.ParseAddrPort(fmt.Sprintf("%s:%d", serverAddrStr, port)) | ||||
| 	must(err) | ||||
|  | ||||
| 	go readFromIFace(iface, conn, serverIP, serverAddr) | ||||
| 	readFromConn(iface, conn) | ||||
| } | ||||
							
								
								
									
										7
									
								
								stage1/cmd/client/build.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										7
									
								
								stage1/cmd/client/build.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| go build | ||||
|  | ||||
| scp client kevin:/home/jdl/tmp | ||||
| ssh root@home "setcap cap_net_admin+iep /home/jdl/tmp/client" | ||||
| ssh home "/home/jdl/tmp/client 192.168.1.21" | ||||
							
								
								
									
										14
									
								
								stage1/cmd/client/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								stage1/cmd/client/main.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"vppn/stage1" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| 	if len(os.Args) != 2 { | ||||
| 		log.Fatalf("Usage: %s <addr:port>", os.Args[0]) | ||||
| 	} | ||||
| 	stage1.RunClient(os.Args[1]) | ||||
| } | ||||
							
								
								
									
										4
									
								
								stage1/cmd/server/build.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										4
									
								
								stage1/cmd/server/build.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| go build | ||||
| sudo setcap cap_net_admin+iep server | ||||
							
								
								
									
										14
									
								
								stage1/cmd/server/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								stage1/cmd/server/main.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"vppn/stage1" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| 	if len(os.Args) != 2 { | ||||
| 		log.Fatalf("Usage: %s <addr>", os.Args[0]) | ||||
| 	} | ||||
| 	stage1.RunServer(os.Args[1]) | ||||
| } | ||||
							
								
								
									
										142
									
								
								stage1/interface.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								stage1/interface.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,142 @@ | ||||
| package stage1 | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"net" | ||||
| 	"os" | ||||
| 	"syscall" | ||||
|  | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	if_mtu       = 1200 | ||||
| 	if_queue_len = 1000 | ||||
| ) | ||||
|  | ||||
| func openInterface(network []byte, localIP byte, name string) (io.ReadWriteCloser, error) { | ||||
| 	if len(network) != 4 { | ||||
| 		return nil, fmt.Errorf("expected network to be 4 bytes, got %d", len(network)) | ||||
| 	} | ||||
| 	ip := net.IPv4(network[0], network[1], network[2], localIP) | ||||
|  | ||||
| 	////////////////////////// | ||||
| 	// Create TUN Interface // | ||||
| 	////////////////////////// | ||||
|  | ||||
| 	tunFD, err := syscall.Open("/dev/net/tun", syscall.O_RDWR|unix.O_CLOEXEC, 0600) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to open TUN device: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	// New interface request. | ||||
| 	req, err := unix.NewIfreq(name) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to create new TUN interface request: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	// Flags: | ||||
| 	// | ||||
| 	// IFF_NO_PI => don't add packet info data to packets sent to the interface. | ||||
| 	// IFF_TUN   => create a TUN device handling IP packets. | ||||
| 	req.SetUint16(unix.IFF_NO_PI | unix.IFF_TUN) | ||||
|  | ||||
| 	err = unix.IoctlIfreq(tunFD, unix.TUNSETIFF, req) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to set TUN device settings: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	// Name may not be exactly the same? | ||||
| 	name = req.Name() | ||||
|  | ||||
| 	///////////// | ||||
| 	// Set MTU // | ||||
| 	///////////// | ||||
|  | ||||
| 	// We need a socket file descriptor to set other options for some reason. | ||||
| 	sockFD, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, unix.IPPROTO_IP) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to open socket: %w", err) | ||||
| 	} | ||||
| 	defer unix.Close(sockFD) | ||||
|  | ||||
| 	req, err = unix.NewIfreq(name) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to create MTU interface request: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	req.SetUint32(if_mtu) | ||||
| 	if err = unix.IoctlIfreq(sockFD, unix.SIOCSIFMTU, req); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to set interface MTU: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	////////////////////// | ||||
| 	// Set Queue Length // | ||||
| 	////////////////////// | ||||
|  | ||||
| 	req, err = unix.NewIfreq(name) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to create IP interface request: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	req.SetUint16(if_queue_len) | ||||
| 	if err = unix.IoctlIfreq(sockFD, unix.SIOCSIFTXQLEN, req); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to set interface queue length: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	///////////////////// | ||||
| 	// Set IP and Mask // | ||||
| 	///////////////////// | ||||
|  | ||||
| 	req, err = unix.NewIfreq(name) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to create IP interface request: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	if err := req.SetInet4Addr(ip.To4()); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to set interface request IP: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	if err = unix.IoctlIfreq(sockFD, unix.SIOCSIFADDR, req); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to set interface IP: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	// SET MASK - must happen after setting address. | ||||
| 	req, err = unix.NewIfreq(name) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to create mask interface request: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	if err := req.SetInet4Addr(net.IPv4(255, 255, 255, 0).To4()); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to set interface request mask: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	if err := unix.IoctlIfreq(sockFD, unix.SIOCSIFNETMASK, req); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to set interface mask: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	//////////////////////// | ||||
| 	// Bring Interface Up // | ||||
| 	//////////////////////// | ||||
|  | ||||
| 	req, err = unix.NewIfreq(name) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to create up interface request: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	// Get current flags. | ||||
| 	if err = unix.IoctlIfreq(sockFD, unix.SIOCGIFFLAGS, req); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to get interface flags: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	flags := req.Uint16() | unix.IFF_UP | unix.IFF_RUNNING | ||||
|  | ||||
| 	// Set UP flag / broadcast flags. | ||||
| 	req.SetUint16(flags) | ||||
| 	if err = unix.IoctlIfreq(sockFD, unix.SIOCSIFFLAGS, req); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to set interface up: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	return os.NewFile(uintptr(tunFD), "tun"), nil | ||||
| } | ||||
							
								
								
									
										109
									
								
								stage1/server.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								stage1/server.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,109 @@ | ||||
| package stage1 | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	"net" | ||||
| 	"net/netip" | ||||
| 	"runtime/debug" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	network    = []byte{10, 1, 1, 0} | ||||
| 	serverIP   = byte(1) | ||||
| 	clientIP   = byte(2) | ||||
| 	port       = uint16(5151) | ||||
| 	netName    = "testnet" | ||||
| 	bufferSize = if_mtu * 2 | ||||
| ) | ||||
|  | ||||
| func must(err error) { | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func RunServer(clientAddrStr string) { | ||||
| 	defer func() { | ||||
| 		if r := recover(); r != nil { | ||||
| 			fmt.Printf("%v", r) | ||||
| 			debug.PrintStack() | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	iface, err := openInterface(network, serverIP, netName) | ||||
| 	must(err) | ||||
|  | ||||
| 	myAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", port)) | ||||
| 	must(err) | ||||
|  | ||||
| 	conn, err := net.ListenUDP("udp", myAddr) | ||||
| 	must(err) | ||||
|  | ||||
| 	clientAddr, err := netip.ParseAddrPort(fmt.Sprintf("%s:%d", clientAddrStr, port)) | ||||
| 	must(err) | ||||
|  | ||||
| 	go readFromIFace(iface, conn, clientIP, clientAddr) | ||||
| 	readFromConn(iface, conn) | ||||
| } | ||||
|  | ||||
| func readFromIFace(iface io.ReadWriteCloser, conn *net.UDPConn, remoteIP byte, remoteAddr netip.AddrPort) { | ||||
| 	var ( | ||||
| 		n       int | ||||
| 		packet  = make([]byte, bufferSize) | ||||
| 		version byte | ||||
| 		ip      byte | ||||
| 		err     error | ||||
| 	) | ||||
|  | ||||
| 	for { | ||||
| 		n, err = iface.Read(packet[:bufferSize]) | ||||
| 		must(err) | ||||
| 		packet = packet[:n] | ||||
|  | ||||
| 		if len(packet) < 20 { | ||||
| 			log.Printf("Dropping small packet: %d", n) | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		packet = packet[:n] | ||||
| 		version = packet[0] >> 4 | ||||
|  | ||||
| 		switch version { | ||||
| 		case 4: | ||||
| 			ip = packet[19] | ||||
| 		case 6: | ||||
| 			ip = packet[39] | ||||
| 		default: | ||||
| 			log.Printf("Dropping packet with IP version: %d", version) | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		if ip != remoteIP { | ||||
| 			log.Printf("Dropping packet for incorrect IP: %d", ip) | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		_, err = conn.WriteToUDPAddrPort(packet, remoteAddr) | ||||
| 		must(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func readFromConn(iface io.ReadWriteCloser, conn *net.UDPConn) { | ||||
| 	var ( | ||||
| 		n      int | ||||
| 		packet = make([]byte, bufferSize) | ||||
| 		err    error | ||||
| 	) | ||||
|  | ||||
| 	for { | ||||
| 		// We assume that we're only receiving packets from one source. | ||||
| 		n, err = conn.Read(packet[:bufferSize]) | ||||
| 		must(err) | ||||
|  | ||||
| 		packet = packet[:n] | ||||
| 		_, err = iface.Write(packet) | ||||
| 		must(err) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										1
									
								
								stage1/startup.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								stage1/startup.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| package stage1 | ||||
		Reference in New Issue
	
	Block a user