package node import ( "fmt" "io" "net" "os" "syscall" "golang.org/x/sys/unix" ) // Get next packet, returning packet, ip, and possible error. func readNextPacket(iface io.ReadWriteCloser, buf []byte) ([]byte, byte, error) { var ( version byte ip byte ) for { n, err := iface.Read(buf[:cap(buf)]) if err != nil { return nil, ip, err } if n < 20 { continue // Packet too short. } buf = buf[:n] version = buf[0] >> 4 switch version { case 4: ip = buf[19] case 6: if len(buf) < 40 { continue // Packet too short. } ip = buf[39] default: continue // Invalid version. } return buf, ip, nil } } 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 }