From ee4f5e012ca33862cc2bc481423af91fe19cca11 Mon Sep 17 00:00:00 2001 From: jdl Date: Wed, 18 Dec 2024 21:08:48 +0100 Subject: [PATCH] Cleanup --- README.md | 3 + peer/conn-states.dot | 9 -- peer/conndata.go | 82 ----------- peer/connhandler.go | 99 ------------- peer/connsender.go | 83 ----------- peer/connstate.go | 254 ---------------------------------- peer/crypto.go | 22 --- peer/crypto_test.go | 106 -------------- peer/duplist.go | 19 --- peer/files.go | 82 ----------- peer/globals.go | 14 -- peer/main.go | 79 ----------- peer/nonce.go | 33 ----- peer/nonce_test.go | 25 ---- peer/packets-helpers.go | 32 ----- peer/packets.go | 138 ------------------ peer/peer-ifreader.go | 70 ---------- peer/peer-netreader.go | 137 ------------------ peer/peer.go | 65 --------- peer/router-managemediator.go | 42 ------ peer/router-ping_test.go | 37 ----- peer/router-pollhub.go | 63 --------- peer/router-types.go | 34 ----- peer/router.go | 85 ------------ peer/startup.go | 156 --------------------- 25 files changed, 3 insertions(+), 1766 deletions(-) delete mode 100644 peer/conn-states.dot delete mode 100644 peer/conndata.go delete mode 100644 peer/connhandler.go delete mode 100644 peer/connsender.go delete mode 100644 peer/connstate.go delete mode 100644 peer/crypto.go delete mode 100644 peer/crypto_test.go delete mode 100644 peer/duplist.go delete mode 100644 peer/files.go delete mode 100644 peer/globals.go delete mode 100644 peer/main.go delete mode 100644 peer/nonce.go delete mode 100644 peer/nonce_test.go delete mode 100644 peer/packets-helpers.go delete mode 100644 peer/packets.go delete mode 100644 peer/peer-ifreader.go delete mode 100644 peer/peer-netreader.go delete mode 100644 peer/peer.go delete mode 100644 peer/router-managemediator.go delete mode 100644 peer/router-ping_test.go delete mode 100644 peer/router-pollhub.go delete mode 100644 peer/router-types.go delete mode 100644 peer/router.go delete mode 100644 peer/startup.go diff --git a/README.md b/README.md index 1a7af89..3aa4d04 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,9 @@ ## Roadmap * Node: use symmetric encryption after handshake +* AEAD-AES uses a 12 byte nonce. We need to shrink the header: + * Remove Forward and replace it with a HeaderFlags bitfield. + * Forward, Asym/Sym, ... * Use default port 456 * Remove signing key from hub * Peer: UDP hole-punching diff --git a/peer/conn-states.dot b/peer/conn-states.dot deleted file mode 100644 index 3c8c5f7..0000000 --- a/peer/conn-states.dot +++ /dev/null @@ -1,9 +0,0 @@ -digraph d { - init -> null; - init -> unconnectedServer; - init -> unconnectedClient; - init -> unconnectedMediated; - unconnectedServer -> connectedServer; - unconnectedClient -> connectedClient; - unconnectedMediated -> connectedMediated; -} \ No newline at end of file diff --git a/peer/conndata.go b/peer/conndata.go deleted file mode 100644 index 8b118c6..0000000 --- a/peer/conndata.go +++ /dev/null @@ -1,82 +0,0 @@ -package peer - -import ( - "net/netip" - "sync/atomic" - "time" - "vppn/m" -) - -const ( - pingInterval = time.Second * 8 - timeoutInterval = 32 * time.Second -) - -type connData struct { - // Shared data. - routes [MAX_IP]*atomic.Pointer[route] - route *atomic.Pointer[route] - - // Peer data. - server bool // Never changes. - peerIP byte // Never changes. - encPrivKey []byte // Never changes. - - peer *m.Peer // From hub. - encSharedKey []byte // From hub + private key. - publicAddr netip.AddrPort // From hub. - - // Connection establishment and maintenance. - pingTimer *time.Timer - timeoutTimer *time.Timer - - // Routing data. - addr netip.AddrPort - useMediator bool - up bool - - // For sending. - buf []byte - sender *safeConnSender -} - -func (d *connData) Route() *route { - return &route{ - PeerIP: d.peerIP, - Up: d.up, - Mediator: d.peer.Mediator, - EncSharedKey: d.encSharedKey, - Addr: d.addr, - useMediator: d.useMediator, - } -} - -func (d *connData) HandlePeerUpdate(state connState, update peerUpdate) connState { - if d.peer != nil && update.Peer != nil && d.peer.Version == update.Peer.Version { - return state - } - if d.peer == nil && update.Peer == nil { - return state - } - return newStateFromPeerUpdate(update, d) -} - -func (d *connData) HandleSendPing() { - route := d.route.Load() - req := Ping{SentAt: time.Now().UnixMilli()} - d.buf = req.Marshal(d.buf) - d.sender.send(PACKET_TYPE_PING, d.buf, route, nil) - d.pingTimer.Reset(pingInterval) -} - -func (d *connData) sendPong(w wrapper) { - ping := w.Packet.(*Ping) - route := d.route.Load() - pong := Pong{ - SentAt: ping.SentAt, - RecvdAt: time.Now().UnixMilli(), - } - - d.buf = pong.Marshal(d.buf) - d.sender.send(PACKET_TYPE_PONG, d.buf, route, nil) -} diff --git a/peer/connhandler.go b/peer/connhandler.go deleted file mode 100644 index cce64c0..0000000 --- a/peer/connhandler.go +++ /dev/null @@ -1,99 +0,0 @@ -package peer - -import ( - "fmt" - "log" - "runtime/debug" - "sync/atomic" - "time" -) - -type connHandler struct { - // Communication. - mediatorUpdates chan byte - peerUpdates chan peerUpdate - packets chan wrapper - - data *connData -} - -func newConnHandler( - server bool, - peerIP byte, - routes [MAX_IP]*atomic.Pointer[route], - encPrivKey []byte, - sender *safeConnSender, -) *connHandler { - d := &connData{ - server: server, - pingTimer: time.NewTimer(pingInterval), - timeoutTimer: time.NewTimer(timeoutInterval), - routes: routes, - route: routes[peerIP], - peerIP: peerIP, - encPrivKey: encPrivKey, - buf: make([]byte, BUFFER_SIZE), - sender: sender, - } - - h := &connHandler{ - mediatorUpdates: make(chan byte, 1), - peerUpdates: make(chan peerUpdate, 1), - packets: make(chan wrapper, 1), - data: d, - } - - go h.mainLoop() - return h -} - -func (h *connHandler) mainLoop() { - defer func() { - if r := recover(); r != nil { - fmt.Println("stacktrace from panic: \n" + string(debug.Stack())) - } - }() - - var ( - data = h.data - state = newConnNull(data) - name = state.Name() - ) - - for { - select { - - case update := <-h.peerUpdates: - state = data.HandlePeerUpdate(state, update) - - case <-data.pingTimer.C: - data.HandleSendPing() - - case w := <-h.packets: - state = state.HandlePacket(w) - - case <-data.timeoutTimer.C: - log.Printf("[%s] Connection timeout.", state.Name()) - state = state.HandleTimeout() - } - - if state.Name() != name { - log.Printf("[%03d] STATE: %s", data.peerIP, state.Name()) - name = state.Name() - } - } -} - -func (c *connHandler) HandlePacket(w wrapper) { - select { - case c.packets <- w: - default: - } -} - -func (c *connHandler) UpdatePeer(update peerUpdate) { - select { - case c.peerUpdates <- update: - default: - } -} diff --git a/peer/connsender.go b/peer/connsender.go deleted file mode 100644 index 4bf5916..0000000 --- a/peer/connsender.go +++ /dev/null @@ -1,83 +0,0 @@ -package peer - -import ( - "log" - "net" - "runtime/debug" - "sync" - "vppn/fasttime" -) - -type connSender struct { - conn *net.UDPConn - sourceIP byte - streamID byte - encrypted []byte - nonceBuf []byte - counter uint64 -} - -func newConnSender(conn *net.UDPConn, srcIP, streamID byte) *connSender { - return &connSender{ - conn: conn, - sourceIP: srcIP, - streamID: streamID, - encrypted: make([]byte, BUFFER_SIZE), - nonceBuf: make([]byte, NONCE_SIZE), - counter: uint64(fasttime.Now()) << 30, // Ensure counter is always increasing. - } -} - -func (cs *connSender) send(packetType byte, packet []byte, dstRoute, viaRoute *route) { - if dstRoute.useMediator && viaRoute == nil { - log.Printf("Dropping forwarded packet: no mediator.") - return - } - - cs.counter++ - - nonce := Nonce{ - Timestamp: fasttime.Now(), - Counter: cs.counter, - SourceIP: cs.sourceIP, - DestIP: dstRoute.PeerIP, - StreamID: cs.streamID, - PacketType: packetType, - } - - if dstRoute.useMediator { - nonce.ViaIP = viaRoute.PeerIP - } - - nonce.Marshal(cs.nonceBuf) - - addr := dstRoute.Addr - - encrypted := encryptPacket(dstRoute.EncSharedKey, cs.nonceBuf, packet, cs.encrypted) - if viaRoute != nil { - packet, encrypted = encrypted, packet - encrypted = encryptPacket(viaRoute.EncSharedKey, cs.nonceBuf, packet, encrypted) - addr = viaRoute.Addr - } - - if _, err := cs.conn.WriteToUDPAddrPort(encrypted, addr); err != nil { - log.Fatalf("Failed to write UDP packet: %v\n%s", err, debug.Stack()) - } -} - -// ---------------------------------------------------------------------------- - -type safeConnSender struct { - lock sync.Mutex - sender *connSender -} - -func newSafeConnSender(sender *connSender) *safeConnSender { - return &safeConnSender{sender: sender} -} - -func (s *safeConnSender) send(packetType byte, packet []byte, route, viaRoute *route) { - s.lock.Lock() - defer s.lock.Unlock() - s.sender.send(packetType, packet, route, viaRoute) -} diff --git a/peer/connstate.go b/peer/connstate.go deleted file mode 100644 index 87a322b..0000000 --- a/peer/connstate.go +++ /dev/null @@ -1,254 +0,0 @@ -package peer - -import ( - "log" - "net/netip" - "time" - "vppn/m" -) - -func logState(s connState, msg string, args ...any) { - log.Printf("["+s.Name()+"] "+msg, args...) -} - -// The connection state corresponds to what we're connected TO. -type connState interface { - Name() string - HandlePacket(wrapper) connState - HandleTimeout() connState -} - -// Helper functions. - -func newStateFromPeerUpdate(update peerUpdate, data *connData) connState { - if update.Peer != nil { - return newStateFromPeer(update.Peer, data) - } - return newConnNull(data) -} - -func newStateFromPeer(peer *m.Peer, data *connData) connState { - if _, isPublic := netip.AddrFromSlice(peer.PublicIP); isPublic { - return newStateServerDown(data, peer) - } else if data.server { - return newStateClientDown(data, peer) - } else { - return newStateMediated(data, peer) - } -} - -///////////////////// -// Null Connection // -///////////////////// - -type connNull struct{ *connData } - -func newConnNull(data *connData) connState { - c := connNull{data} - c.peer = nil - c.encSharedKey = nil - c.publicAddr = netip.AddrPort{} - c.pingTimer.Stop() - c.timeoutTimer.Stop() - c.addr = c.publicAddr - c.useMediator = false - c.up = false - c.route.Store(nil) - return c -} - -func (c connNull) Name() string { return "NoPeer" } - -func (c connNull) HandlePacket(w wrapper) connState { return c } - -func (c connNull) HandleTimeout() connState { - logState(c, "Unexpected timeout.") - return c -} - -//////////////////////// -// Unconnected Server // -//////////////////////// - -type stateServerDown struct{ *connData } - -func newStateServerDown(data *connData, peer *m.Peer) connState { - addr, _ := netip.AddrFromSlice(peer.PublicIP) - pubAddr := netip.AddrPortFrom(addr, peer.Port) - - c := stateServerDown{data} - c.peer = peer - c.encSharedKey = computeSharedKey(peer.EncPubKey, c.encPrivKey) - c.publicAddr = pubAddr - c.pingTimer.Reset(time.Second) // Ping right away to bring up. - c.timeoutTimer.Stop() // No timeouts yet. - c.addr = c.publicAddr - c.useMediator = false - c.up = false - c.route.Store(c.Route()) - - return c -} - -func (c stateServerDown) Name() string { return "Server:DOWN" } - -func (c stateServerDown) HandlePacket(w wrapper) connState { - switch p := w.Packet.(type) { - case *Pong: - return newStateServerUp(c.connData, w, p) - } - return c -} - -func (c stateServerDown) HandleTimeout() connState { - logState(c, "Unexpected timeout.") - return c -} - -////////////////////// -// Connected Server // -////////////////////// - -type stateServerUp struct{ *connData } - -func newStateServerUp(data *connData, w wrapper, pong *Pong) connState { - c := stateServerUp{data} - c.pingTimer.Reset(pingInterval) - c.timeoutTimer.Reset(timeoutInterval) - c.addr = w.SrcAddr - c.useMediator = false - c.up = true - c.route.Store(c.Route()) - return c -} - -func (c stateServerUp) Name() string { return "Server:UP" } - -func (c stateServerUp) HandlePacket(w wrapper) connState { - switch w.Packet.(type) { - case *Pong: - c.timeoutTimer.Reset(timeoutInterval) - } - return c - -} - -func (c stateServerUp) HandleTimeout() connState { - return newStateFromPeer(c.peer, c.connData) -} - -//////////////////////// -// Unconnected Client // -//////////////////////// - -type stateClientDown struct{ *connData } - -func newStateClientDown(data *connData, peer *m.Peer) connState { - addr, _ := netip.AddrFromSlice(peer.PublicIP) - pubAddr := netip.AddrPortFrom(addr, peer.Port) - - c := stateClientDown{data} - c.peer = peer - c.publicAddr = pubAddr - c.encPrivKey = data.encPrivKey - c.encSharedKey = computeSharedKey(peer.EncPubKey, c.encPrivKey) - c.addr = c.publicAddr - c.useMediator = false - c.up = false - c.route.Store(c.Route()) - - c.pingTimer.Stop() // Conncection is from client => pings incoming. - c.timeoutTimer.Stop() // No timeouts yet. - - return c -} - -func (c stateClientDown) Name() string { return "Client:DOWN" } - -func (c stateClientDown) HandlePacket(w wrapper) connState { - switch w.Packet.(type) { - case *Ping: - next := newStateClientUp(c.connData, w) - c.sendPong(w) - return next - } - return c -} - -func (c stateClientDown) HandleTimeout() connState { - logState(c, "Unexpected timeout.") - return c -} - -////////////////////// -// Connected Client // -////////////////////// - -type stateClientUp struct{ *connData } - -func newStateClientUp(data *connData, w wrapper) connState { - c := stateClientUp{data} - c.addr = w.SrcAddr - c.useMediator = false - c.up = true - c.route.Store(c.Route()) - - c.pingTimer.Stop() // Conncection is from client => pings incoming. - c.timeoutTimer.Reset(timeoutInterval) - return c -} - -func (c stateClientUp) Name() string { return "Client:UP" } - -func (c stateClientUp) HandlePacket(w wrapper) connState { - switch w.Packet.(type) { - case *Ping: - // The connection is from a client. If the client's address changes, we - // should follow that change. - if c.addr != w.SrcAddr { - c.addr = w.SrcAddr - c.route.Store(c.Route()) - } - c.sendPong(w) - c.timeoutTimer.Reset(timeoutInterval) - } - return c -} - -func (c stateClientUp) HandleTimeout() connState { - return newStateFromPeer(c.peer, c.connData) -} - -////////////// -// Mediated // -////////////// - -type stateMediated struct{ *connData } - -func newStateMediated(data *connData, peer *m.Peer) connState { - addr, _ := netip.AddrFromSlice(peer.PublicIP) - pubAddr := netip.AddrPortFrom(addr, peer.Port) - - c := stateMediated{data} - c.peer = peer - c.publicAddr = pubAddr - c.encPrivKey = data.encPrivKey - c.encSharedKey = computeSharedKey(peer.EncPubKey, c.encPrivKey) - c.addr = c.publicAddr - c.useMediator = true - c.up = true - c.route.Store(c.Route()) - - c.pingTimer.Stop() // No pings for mediators. - c.timeoutTimer.Stop() // No timeouts yet. - return c -} - -func (c stateMediated) Name() string { return "Mediated:UP" } - -func (c stateMediated) HandlePacket(w wrapper) connState { return c } - -func (c stateMediated) HandleTimeout() connState { - logState(c, "Unexpected timeout.") - return c -} diff --git a/peer/crypto.go b/peer/crypto.go deleted file mode 100644 index 3d27b32..0000000 --- a/peer/crypto.go +++ /dev/null @@ -1,22 +0,0 @@ -package peer - -import ( - "golang.org/x/crypto/nacl/box" -) - -func encryptPacket(sharedKey, nonce, packet, out []byte) []byte { - out = box.SealAfterPrecomputation(out[:0], packet, (*[24]byte)(nonce), (*[32]byte)(sharedKey)) - return append(out, nonce...) -} - -func decryptPacket(sharedKey, packet, out []byte) (decrypted []byte, ok bool) { - cut := len(packet) - NONCE_SIZE - decrypted, ok = box.OpenAfterPrecomputation(out[:0], packet[:cut], (*[24]byte)(packet[cut:]), (*[32]byte)(sharedKey)) - return decrypted, ok -} - -func computeSharedKey(peerPubKey, privKey []byte) []byte { - shared := [32]byte{} - box.Precompute(&shared, (*[32]byte)(peerPubKey), (*[32]byte)(privKey)) - return shared[:] -} diff --git a/peer/crypto_test.go b/peer/crypto_test.go deleted file mode 100644 index 623412e..0000000 --- a/peer/crypto_test.go +++ /dev/null @@ -1,106 +0,0 @@ -package peer - -import ( - "bytes" - "crypto/rand" - "testing" - - "golang.org/x/crypto/nacl/box" -) - -func TestEncryptDecryptPacket(t *testing.T) { - pubKey1, privKey1, err := box.GenerateKey(rand.Reader) - if err != nil { - t.Fatal(err) - } - - pubKey2, privKey2, err := box.GenerateKey(rand.Reader) - if err != nil { - t.Fatal(err) - } - - sharedEncKey := [32]byte{} - box.Precompute(&sharedEncKey, pubKey2, privKey1) - - sharedDecKey := [32]byte{} - box.Precompute(&sharedDecKey, pubKey1, privKey2) - - original := make([]byte, MTU) - rand.Read(original) - - nonce := make([]byte, NONCE_SIZE) - rand.Read(nonce) - - encrypted := make([]byte, BUFFER_SIZE) - encrypted = encryptPacket(sharedEncKey[:], nonce, original, encrypted) - - decrypted := make([]byte, MTU) - var ok bool - decrypted, ok = decryptPacket(sharedDecKey[:], encrypted, decrypted) - if !ok { - t.Fatal(ok) - } - - if !bytes.Equal(original, decrypted) { - t.Fatal("mismatch") - } -} - -func BenchmarkEncryptPacket(b *testing.B) { - _, privKey1, err := box.GenerateKey(rand.Reader) - if err != nil { - b.Fatal(err) - } - - pubKey2, _, err := box.GenerateKey(rand.Reader) - if err != nil { - b.Fatal(err) - } - - sharedEncKey := [32]byte{} - box.Precompute(&sharedEncKey, pubKey2, privKey1) - - original := make([]byte, MTU) - rand.Read(original) - - nonce := make([]byte, NONCE_SIZE) - rand.Read(nonce) - - encrypted := make([]byte, BUFFER_SIZE) - - for i := 0; i < b.N; i++ { - encrypted = encryptPacket(sharedEncKey[:], nonce, original, encrypted) - } -} - -func BenchmarkDecryptPacket(b *testing.B) { - pubKey1, privKey1, err := box.GenerateKey(rand.Reader) - if err != nil { - b.Fatal(err) - } - - pubKey2, privKey2, err := box.GenerateKey(rand.Reader) - if err != nil { - b.Fatal(err) - } - - sharedEncKey := [32]byte{} - box.Precompute(&sharedEncKey, pubKey2, privKey1) - - sharedDecKey := [32]byte{} - box.Precompute(&sharedDecKey, pubKey1, privKey2) - - original := make([]byte, MTU) - rand.Read(original) - - nonce := make([]byte, NONCE_SIZE) - rand.Read(nonce) - - encrypted := make([]byte, BUFFER_SIZE) - encrypted = encryptPacket(sharedEncKey[:], nonce, original, encrypted) - - decrypted := make([]byte, MTU) - for i := 0; i < b.N; i++ { - decrypted, _ = decryptPacket(sharedDecKey[:], encrypted, decrypted) - } -} diff --git a/peer/duplist.go b/peer/duplist.go deleted file mode 100644 index 3e6f3d9..0000000 --- a/peer/duplist.go +++ /dev/null @@ -1,19 +0,0 @@ -package peer - -const DUP_LIST_SIZE = 32 - -type dupList struct { - items [DUP_LIST_SIZE]uint64 - index int -} - -func (l *dupList) isDuplicate(in uint64) bool { - for _, i := range l.items { - if i == in { - return true - } - } - l.items[l.index] = in - l.index = (l.index + 1) % DUP_LIST_SIZE - return false -} diff --git a/peer/files.go b/peer/files.go deleted file mode 100644 index 3b74427..0000000 --- a/peer/files.go +++ /dev/null @@ -1,82 +0,0 @@ -package peer - -import ( - "encoding/json" - "log" - "os" - "path/filepath" - "vppn/m" -) - -func configDir(netName string) string { - d, err := os.UserHomeDir() - if err != nil { - log.Fatalf("Failed to get user home directory: %v", err) - } - return filepath.Join(d, ".vppn", netName) -} - -func peerConfigPath(netName string) string { - return filepath.Join(configDir(netName), "peer-config.json") -} - -func peerStatePath(netName string) string { - return filepath.Join(configDir(netName), "peer-state.json") -} - -func storeJson(x any, outPath string) error { - outDir := filepath.Dir(outPath) - _ = os.MkdirAll(outDir, 0700) - - tmpPath := outPath + ".tmp" - buf, err := json.Marshal(x) - if err != nil { - return err - } - - f, err := os.Create(tmpPath) - if err != nil { - return err - } - - if _, err := f.Write(buf); err != nil { - f.Close() - return err - } - - if err := f.Sync(); err != nil { - f.Close() - return err - } - - if err := f.Close(); err != nil { - return err - } - - return os.Rename(tmpPath, outPath) -} - -func storePeerConfig(netName string, pc m.PeerConfig) error { - return storeJson(pc, peerConfigPath(netName)) -} - -func storePeerState(netName string, ps m.NetworkState) error { - return storeJson(ps, peerStatePath(netName)) -} - -func loadJson(dataPath string, ptr any) error { - data, err := os.ReadFile(dataPath) - if err != nil { - return err - } - - return json.Unmarshal(data, ptr) -} - -func loadPeerConfig(netName string) (pc m.PeerConfig, err error) { - return pc, loadJson(peerConfigPath(netName), &pc) -} - -func loadPeerState(netName string) (ps m.NetworkState, err error) { - return ps, loadJson(peerStatePath(netName), &ps) -} diff --git a/peer/globals.go b/peer/globals.go deleted file mode 100644 index 636f895..0000000 --- a/peer/globals.go +++ /dev/null @@ -1,14 +0,0 @@ -package peer - -const ( - MAX_IP = 65 - DEFAULT_PORT = 515 - NONCE_SIZE = 24 - KEY_SIZE = 32 - SIG_SIZE = 64 - MTU = 1436 - BUFFER_SIZE = 1536 // Definitely big enough. - - STREAM_DATA = 0 - STREAM_ROUTING = 1 // Routing queries and responses. -) diff --git a/peer/main.go b/peer/main.go deleted file mode 100644 index d766ce2..0000000 --- a/peer/main.go +++ /dev/null @@ -1,79 +0,0 @@ -package peer - -import ( - "encoding/json" - "flag" - "fmt" - "io" - "log" - "net/http" - "os" - "runtime/debug" - "vppn/m" -) - -func Main() { - defer func() { - if r := recover(); r != nil { - fmt.Println("stacktrace from panic: \n" + string(debug.Stack())) - } - }() - - var ( - netName string - initURL string - listenIP string - port int - ) - - flag.StringVar(&netName, "name", "", "[REQUIRED] The network name.") - flag.StringVar(&initURL, "init-url", "", "Initializes peer from the hub URL.") - flag.StringVar(&listenIP, "listen-ip", "", "IP address to listen on.") - flag.IntVar(&port, "port", 0, "Port to listen on.") - flag.Parse() - - if netName == "" { - flag.Usage() - os.Exit(1) - } - - if initURL != "" { - mainInit(netName, initURL) - return - } - - peer, err := NewPeer(netName, listenIP, uint16(port)) - if err != nil { - log.Fatalf("Failed to create peer: %v", err) - } - - peer.Run() -} - -func mainInit(netName, initURL string) { - if _, err := loadPeerConfig(netName); err == nil { - log.Fatalf("Network is already initialized.") - } - - resp, err := http.Get(initURL) - if err != nil { - log.Fatalf("Failed to fetch data from hub: %v", err) - } - defer resp.Body.Close() - - data, err := io.ReadAll(resp.Body) - if err != nil { - log.Fatalf("Failed to read response body: %v", err) - } - - peerConfig := m.PeerConfig{} - if err := json.Unmarshal(data, &peerConfig); err != nil { - log.Fatalf("Failed to parse configuration: %v", err) - } - - if err := storePeerConfig(netName, peerConfig); err != nil { - log.Fatalf("Failed to store configuration: %v", err) - } - - log.Print("Initialization successful.") -} diff --git a/peer/nonce.go b/peer/nonce.go deleted file mode 100644 index b86e17d..0000000 --- a/peer/nonce.go +++ /dev/null @@ -1,33 +0,0 @@ -package peer - -import "unsafe" - -type Nonce struct { - Timestamp int64 - Counter uint64 - SourceIP byte - ViaIP byte - DestIP byte - StreamID byte // The stream, see STREAM_* constants - PacketType byte // The packet type. See PACKET_* constants. -} - -func (nonce *Nonce) Parse(nb []byte) { - nonce.Timestamp = *(*int64)(unsafe.Pointer(&nb[0])) - nonce.Counter = *(*uint64)(unsafe.Pointer(&nb[8])) - nonce.SourceIP = nb[16] - nonce.ViaIP = nb[17] - nonce.DestIP = nb[18] - nonce.StreamID = nb[19] - nonce.PacketType = nb[20] -} - -func (nonce Nonce) Marshal(buf []byte) { - *(*int64)(unsafe.Pointer(&buf[0])) = nonce.Timestamp - *(*uint64)(unsafe.Pointer(&buf[8])) = nonce.Counter - buf[16] = nonce.SourceIP - buf[17] = nonce.ViaIP - buf[18] = nonce.DestIP - buf[19] = nonce.StreamID - buf[20] = nonce.PacketType -} diff --git a/peer/nonce_test.go b/peer/nonce_test.go deleted file mode 100644 index e962fc9..0000000 --- a/peer/nonce_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package peer - -import ( - "testing" -) - -func TestMarshalParseNonce(t *testing.T) { - nIn := Nonce{ - Counter: 3212, - SourceIP: 34, - ViaIP: 20, - DestIP: 200, - StreamID: 4, - PacketType: 44, - } - - buf := make([]byte, NONCE_SIZE) - nIn.Marshal(buf) - - nOut := Nonce{} - nOut.Parse(buf) - if nIn != nOut { - t.Fatal(nIn, nOut) - } -} diff --git a/peer/packets-helpers.go b/peer/packets-helpers.go deleted file mode 100644 index ad1eb65..0000000 --- a/peer/packets-helpers.go +++ /dev/null @@ -1,32 +0,0 @@ -package peer - -import ( - "log" -) - -func unmarshalPacket(nonce Nonce, data []byte) Packet { - var packet Packet - - switch nonce.PacketType { - case PACKET_TYPE_PING: - packet = &Ping{} - case PACKET_TYPE_PONG: - packet = &Pong{} - case PACKET_TYPE_LIST_ADDR_REQ: - packet = &AddrListReq{} - case PACKET_TYPE_LIST_ADDR_RESP: - packet = &AddrListResp{} - default: - // TODO: Log. - return nil - } - - if len(data) < packet.Size() { - log.Printf("Short packet[%d]: %d", nonce.PacketType, len(data)) - return nil - } - - packet.Parse(data[:packet.Size()]) - - return packet -} diff --git a/peer/packets.go b/peer/packets.go deleted file mode 100644 index 1ccd64b..0000000 --- a/peer/packets.go +++ /dev/null @@ -1,138 +0,0 @@ -package peer - -import "unsafe" - -const ( - PACKET_TYPE_DATA = 0 - PACKET_TYPE_PING = 1 - PACKET_TYPE_PONG = 2 - PACKET_TYPE_CONN_REQ = 3 - PACKET_TYPE_CONN_RESP = 4 - PACKET_TYPE_CONN_RESP_ACK = 5 - PACKET_TYPE_LIST_ADDR_REQ = 6 - PACKET_TYPE_LIST_ADDR_RESP = 7 - - CONN_REQ_SIZE = 24 - CONN_RESP_SIZE = 8 - CONN_RESP_ACK_SIZE = 8 -) - -type Packet interface { - Size() int - Parse(buf []byte) - Marshal(buf []byte) []byte -} - -// ---------------------------------------------------------------------------- - -type AddrListReq struct{} // Nothing. - -func (r *AddrListReq) Size() int { return 0 } - -func (r *AddrListReq) Parse(buf []byte) {} - -func (r *AddrListReq) Marshal(buf []byte) []byte { return buf[:0] } - -// ---------------------------------------------------------------------------- - -type AddrListResp struct { - Addrs [][16]byte // All addresses are IPv6 or mapped IPv6. - Ports []uint16 // Ports. -} - -func (r *AddrListResp) Size() int { - return 18 * MAX_IP -} - -func (r *AddrListResp) Parse(buf []byte) { - r.Addrs = unsafe.Slice((*[16]byte)(unsafe.Pointer(&buf[0])), MAX_IP) - r.Ports = unsafe.Slice((*uint16)(unsafe.Pointer(&buf[16*MAX_IP])), MAX_IP) -} - -func (r *AddrListResp) Marshal(buf []byte) []byte { - buf = buf[:18:MAX_IP] - addrs := unsafe.Slice((*[16]byte)(unsafe.Pointer(&buf[0])), MAX_IP) - copy(addrs, r.Addrs) - ports := unsafe.Slice((*uint16)(unsafe.Pointer(&buf[16*MAX_IP])), MAX_IP) - copy(ports, r.Ports) - return buf -} - -// ---------------------------------------------------------------------------- - -type ConnReq struct { - ReqID uint64 - SentAt int64 - Tiebreaker int64 // To determine which side is client or server. -} - -func (r *ConnReq) Parse(buf []byte) { - r.ReqID = *(*uint64)(unsafe.Pointer(&buf[0])) - r.SentAt = *(*int64)(unsafe.Pointer(&buf[8])) - r.Tiebreaker = *(*int64)(unsafe.Pointer(&buf[16])) -} - -func (r ConnReq) Marshal(buf []byte) []byte { - buf = buf[:24] - *(*uint64)(unsafe.Pointer(&buf[0])) = r.ReqID - *(*int64)(unsafe.Pointer(&buf[8])) = r.SentAt - *(*int64)(unsafe.Pointer(&buf[16])) = r.Tiebreaker - return buf -} - -// ---------------------------------------------------------------------------- - -type ConnResp struct { - ReqID uint64 -} - -func (r *ConnResp) Parse(buf []byte) { - r.ReqID = *(*uint64)(unsafe.Pointer(&buf[0])) -} - -func (r ConnResp) Marshal(buf []byte) { - *(*uint64)(unsafe.Pointer(&buf[0])) = r.ReqID -} - -// ---------------------------------------------------------------------------- - -type ConnRespAck = ConnResp - -// ---------------------------------------------------------------------------- - -type Ping struct { - SentAt int64 // unix milli -} - -func (p *Ping) Size() int { return 8 } - -func (p *Ping) Parse(buf []byte) { - p.SentAt = *(*int64)(unsafe.Pointer(&buf[0])) -} - -func (p *Ping) Marshal(buf []byte) []byte { - buf = buf[:8] - *(*int64)(unsafe.Pointer(&buf[0])) = p.SentAt - return buf -} - -// ---------------------------------------------------------------------------- - -type Pong struct { - SentAt int64 // unix mili - RecvdAt int64 // unix mili -} - -func (p *Pong) Size() int { return 16 } - -func (p *Pong) Parse(buf []byte) { - p.SentAt = *(*int64)(unsafe.Pointer(&buf[0])) - p.RecvdAt = *(*int64)(unsafe.Pointer(&buf[8])) -} - -func (p *Pong) Marshal(buf []byte) []byte { - buf = buf[:16] - *(*int64)(unsafe.Pointer(&buf[0])) = p.SentAt - *(*int64)(unsafe.Pointer(&buf[8])) = p.RecvdAt - return buf -} diff --git a/peer/peer-ifreader.go b/peer/peer-ifreader.go deleted file mode 100644 index 63ac0c1..0000000 --- a/peer/peer-ifreader.go +++ /dev/null @@ -1,70 +0,0 @@ -package peer - -import ( - "fmt" - "log" - "runtime/debug" -) - -func (peer *Peer) ifReader() { - defer func() { - if r := recover(); r != nil { - fmt.Println("stacktrace from panic: \n" + string(debug.Stack())) - } - }() - - var ( - sender = newConnSender(peer.conn, peer.ip, STREAM_DATA) - n int - destIP byte - router = peer.router - viaRoute *route - route *route - iface = peer.iface - err error - packet = make([]byte, BUFFER_SIZE) - version byte - ) - - for { - n, err = iface.Read(packet[:BUFFER_SIZE]) - if err != nil { - log.Fatalf("Failed to read from interface: %v", err) - } - - if n < 20 { - log.Printf("Dropping small packet: %d", n) - continue - } - - packet = packet[:n] - version = packet[0] >> 4 - - switch version { - case 4: - destIP = packet[19] - case 6: - destIP = packet[39] - default: - log.Printf("Dropping packet with IP version: %d", version) - } - - route = router.GetRoute(destIP) - if route == nil || !route.Up { - log.Printf("Dropping packet for non-existent IP: %d", destIP) - continue - } - - if route.useMediator { - viaRoute = router.GetMediator() - if viaRoute == nil || !viaRoute.Up { - log.Printf("Dropping packet due to no mediator: %d", destIP) - continue - } - } else { - viaRoute = nil - } - - sender.send(PACKET_TYPE_DATA, packet, route, viaRoute) - } -} diff --git a/peer/peer-netreader.go b/peer/peer-netreader.go deleted file mode 100644 index 9586210..0000000 --- a/peer/peer-netreader.go +++ /dev/null @@ -1,137 +0,0 @@ -package peer - -import ( - "fmt" - "log" - "net/netip" - "runtime/debug" - "vppn/fasttime" -) - -func (peer *Peer) netReader() { - defer func() { - if r := recover(); r != nil { - fmt.Println("stacktrace from panic: \n" + string(debug.Stack())) - } - }() - - var ( - dupLists = [MAX_IP]dupList{} - n int - srcAddr netip.AddrPort - nonce Nonce - packet = make([]byte, BUFFER_SIZE) - decrypted = make([]byte, BUFFER_SIZE) - route *route - ok bool - err error - conn = peer.conn - ip = peer.ip - counters = [2][256]uint64{} // Counter by stream and IP. - ) - -NEXT_PACKET: - - n, srcAddr, err = conn.ReadFromUDPAddrPort(packet[:BUFFER_SIZE]) - if err != nil { - log.Fatalf("Failed to read UDP packet: %v", err) - } - srcAddr = netip.AddrPortFrom(srcAddr.Addr().Unmap(), srcAddr.Port()) - - if n < NONCE_SIZE { - log.Printf("Dropping short UDP packet: %d", n) - goto NEXT_PACKET - } - - packet = packet[:n] - nonce.Parse(packet[n-NONCE_SIZE:]) - - // Drop after 8 seconds. - if nonce.Timestamp < fasttime.Now()-8 { - log.Printf("Dropping old packet: %d", nonce.Timestamp) - goto NEXT_PACKET - } - - if nonce.StreamID > 1 { - log.Printf("Dropping invalid stream ID: %+v", nonce) - goto NEXT_PACKET - } - - if nonce.Counter <= counters[nonce.StreamID][nonce.SourceIP] { - log.Printf("Dropping packet with bad counter: %d (-%d) - %v", nonce.Counter, counters[nonce.StreamID][nonce.SourceIP]-nonce.Counter, srcAddr) - goto NEXT_PACKET - } - - route = peer.router.GetRoute(nonce.SourceIP) - if route == nil { - log.Printf("Dropping packet without route: %+v", nonce) - goto NEXT_PACKET - } - - decrypted, ok = decryptPacket(route.EncSharedKey, packet, decrypted) - if !ok { - log.Printf("Failed to decrypt packet: %v", nonce) - goto NEXT_PACKET - } - - // Only updated after we've decrypted. - if nonce.Counter > counters[nonce.StreamID][nonce.SourceIP] { - counters[nonce.StreamID][nonce.SourceIP] = nonce.Counter - } - - switch ip { - case nonce.DestIP: - goto PROCESS_LOCAL - case nonce.ViaIP: - goto FORWARD - default: - log.Printf("Invalid nonce: %+v", nonce) - goto NEXT_PACKET - } - -PROCESS_LOCAL: - - switch nonce.StreamID { - case STREAM_DATA: - goto WRITE_IFACE_DATA - case STREAM_ROUTING: - goto WRITE_ROUTING_PACKET - default: - log.Printf("Invalid stream ID: %d", nonce.StreamID) - goto NEXT_PACKET - } - -WRITE_IFACE_DATA: - - if _, err = peer.iface.Write(decrypted); err != nil { - log.Fatalf("Failed to write to interface: %v", err) - } - - goto NEXT_PACKET - -WRITE_ROUTING_PACKET: - - peer.router.HandlePacket(srcAddr, nonce, decrypted) - - goto NEXT_PACKET - -FORWARD: - - route = peer.router.GetRoute(nonce.DestIP) - if route == nil || !route.Up { - log.Printf("Dropping mediated packet, route not available: %v", nonce) - goto NEXT_PACKET - } - - // We don't forward twice. - if route.useMediator { - log.Printf("Dropping double-forward packet: %v", nonce) - goto NEXT_PACKET - } - - if _, err = conn.WriteToUDPAddrPort(decrypted, route.Addr); err != nil { - log.Fatalf("Failed to forward packet: %v", err) - } - - goto NEXT_PACKET -} diff --git a/peer/peer.go b/peer/peer.go deleted file mode 100644 index fa50ac2..0000000 --- a/peer/peer.go +++ /dev/null @@ -1,65 +0,0 @@ -package peer - -import ( - "io" - "log" - "net" - "net/netip" -) - -type Peer struct { - ip byte // Last byte of IPv4 address. - hubAddr string - apiKey string - isServer bool - isMediator bool - encPubKey []byte - encPrivKey []byte - conn *net.UDPConn - iface io.ReadWriteCloser - - router *Router -} - -func NewPeer(netName, listenIP string, port uint16) (*Peer, error) { - conf, err := loadPeerConfig(netName) - if err != nil { - return nil, err - } - - peer := &Peer{ - ip: conf.PeerIP, - hubAddr: conf.HubAddress, - isMediator: conf.Mediator, - apiKey: conf.APIKey, - encPubKey: conf.EncPubKey, - encPrivKey: conf.EncPrivKey, - } - - _, peer.isServer = netip.AddrFromSlice(conf.PublicIP) - - port = determinePort(conf.Port, port) - - peer.conn, err = openUDPConn(listenIP, port) - if err != nil { - return nil, err - } - - peer.router = NewRouter(conf, peer.conn) - - peer.iface, err = openInterface(conf.Network, conf.PeerIP, netName) - if err != nil { - log.Fatal(err) - } - - if err != nil { - return nil, err - } - - return peer, nil -} - -func (p *Peer) Run() { - go p.netReader() - p.ifReader() -} diff --git a/peer/router-managemediator.go b/peer/router-managemediator.go deleted file mode 100644 index ec2d005..0000000 --- a/peer/router-managemediator.go +++ /dev/null @@ -1,42 +0,0 @@ -package peer - -import ( - "log" - "math/rand" - "time" -) - -func (r *Router) manageMediator() { - var ( - ip = byte(0) - mediators = make([]*route, 0, 32) - ) - - for range time.Tick(8 * time.Second) { - // If the current mediator is up, keep it. - route := r.routes[ip].Load() - if route != nil && route.Up { - continue - } - - // If the current mediator is up, keep it. - mediators = mediators[:0] - - for i := range r.routes { - route := r.routes[i].Load() - if route == nil || !route.Mediator { - continue - } - - mediators = append(mediators, route) - } - - if len(mediators) == 0 { - r.mediatorIP.Store(nil) - } else { - ip = mediators[rand.Intn(len(mediators))].PeerIP - log.Printf("Got mediator IP: %d", ip) - r.mediatorIP.Store(&ip) - } - } -} diff --git a/peer/router-ping_test.go b/peer/router-ping_test.go deleted file mode 100644 index 69f4480..0000000 --- a/peer/router-ping_test.go +++ /dev/null @@ -1,37 +0,0 @@ -package peer - -import ( - "reflect" - "testing" -) - -func TestMarshalParsePingReq(t *testing.T) { - in := Ping{ - SentAt: 4553252, - } - - buf := make([]byte, PING_SIZE) - in.Marshal(buf) - - out := Ping{} - out.Parse(buf) - if !reflect.DeepEqual(in, out) { - t.Fatal(in, out) - } -} - -func TestMarshalParsePingResp(t *testing.T) { - in := Pong{ - SentAt: 4553252, - RecvdAt: 4553253, - } - - buf := make([]byte, PONG_SIZE) - in.Marshal(buf) - - out := Pong{} - out.Parse(buf) - if !reflect.DeepEqual(in, out) { - t.Fatal(in, out) - } -} diff --git a/peer/router-pollhub.go b/peer/router-pollhub.go deleted file mode 100644 index 88d7dac..0000000 --- a/peer/router-pollhub.go +++ /dev/null @@ -1,63 +0,0 @@ -package peer - -import ( - "encoding/json" - "io" - "log" - "net/http" - "net/url" - "time" - "vppn/m" -) - -func (r *Router) pollHub() { - u, err := url.Parse(r.conf.HubAddress) - if err != nil { - log.Fatalf("Failed to parse hub address %s: %v", r.conf.HubAddress, err) - } - u.Path = "/peer/fetch-state/" - - client := &http.Client{Timeout: 8 * time.Second} - - req := &http.Request{ - Method: http.MethodGet, - URL: u, - Header: http.Header{}, - } - req.SetBasicAuth("", r.conf.APIKey) - - // TODO: Before we start polling, load state from the file system. - r._pollHub(client, req) - - for range time.Tick(32 * time.Second) { - r._pollHub(client, req) - } -} - -func (r *Router) _pollHub(client *http.Client, req *http.Request) { - var state m.NetworkState - - log.Printf("Fetching peer state from %s...", r.conf.HubAddress) - resp, err := client.Do(req) - if err != nil { - log.Printf("Failed to fetch peer state: %v", err) - return - } - body, err := io.ReadAll(resp.Body) - _ = resp.Body.Close() - if err != nil { - log.Printf("Failed to read body from hub: %v", err) - return - } - - if err := json.Unmarshal(body, &state); err != nil { - log.Printf("Failed to unmarshal response from hub: %v", err) - return - } - - for i := range r.conns { - if r.conns[i] != nil { - r.conns[i].UpdatePeer(peerUpdate{PeerIP: byte(i), Peer: state.Peers[i]}) - } - } -} diff --git a/peer/router-types.go b/peer/router-types.go deleted file mode 100644 index aa1ce40..0000000 --- a/peer/router-types.go +++ /dev/null @@ -1,34 +0,0 @@ -package peer - -import ( - "net/netip" - "vppn/m" -) - -// ---------------------------------------------------------------------------- - -// A route is used by a peer to send or receive packets. A peer won't send -// packets on a route that isn't up, but will process incoming packets on -// that route. -type route struct { - PeerIP byte - Up bool - Mediator bool - EncSharedKey []byte // Shared key for encoding / decoding packets. - Addr netip.AddrPort - useMediator bool -} - -type peerUpdate struct { - PeerIP byte - *m.Peer // nil => delete. -} - -// ---------------------------------------------------------------------------- - -// Wrapper for routing packets. -type wrapper struct { - Packet Packet - Nonce Nonce - SrcAddr netip.AddrPort -} diff --git a/peer/router.go b/peer/router.go deleted file mode 100644 index 3a4c90c..0000000 --- a/peer/router.go +++ /dev/null @@ -1,85 +0,0 @@ -package peer - -import ( - "log" - "net" - "net/netip" - "sync/atomic" - "vppn/m" -) - -type Router struct { - conf m.PeerConfig - - // Routes used by the peer. - conns [MAX_IP]*connHandler - routes [MAX_IP]*atomic.Pointer[route] - addrs [MAX_IP]*atomic.Pointer[netip.AddrPort] - mediatorIP *atomic.Pointer[byte] -} - -func NewRouter(conf m.PeerConfig, conn *net.UDPConn) *Router { - r := &Router{ - conf: conf, - mediatorIP: &atomic.Pointer[byte]{}, - } - - for i := range r.routes { - r.routes[i] = &atomic.Pointer[route]{} - } - - _, isServer := netip.AddrFromSlice(conf.PublicIP) - - sender := newConnSender(conn, conf.PeerIP, STREAM_ROUTING) - - for i := range r.conns { - if byte(i) != conf.PeerIP { - r.conns[i] = newConnHandler( - isServer, - byte(i), - r.routes, - conf.EncPrivKey, - newSafeConnSender(sender)) - } - } - - go r.pollHub() - if !isServer { - go r.manageMediator() - } - - return r -} - -// ---------------------------------------------------------------------------- -// Peer Methods -// ---------------------------------------------------------------------------- - -func (rm *Router) GetRoute(ip byte) *route { - return rm.routes[ip].Load() -} - -func (rm *Router) GetMediator() *route { - if ip := rm.mediatorIP.Load(); ip != nil { - return rm.GetRoute(*ip) - } - return nil -} - -func (r *Router) HandlePacket(src netip.AddrPort, nonce Nonce, data []byte) { - if nonce.SourceIP == r.conf.PeerIP { - log.Printf("Packet to self...") - return - } - - packet := unmarshalPacket(nonce, data) - if packet == nil { - return - } - - r.conns[nonce.SourceIP].HandlePacket(wrapper{ - Packet: packet, - Nonce: nonce, - SrcAddr: src, - }) -} diff --git a/peer/startup.go b/peer/startup.go deleted file mode 100644 index d2e6c52..0000000 --- a/peer/startup.go +++ /dev/null @@ -1,156 +0,0 @@ -package peer - -import ( - "fmt" - "io" - "net" - "os" - "syscall" - - "golang.org/x/sys/unix" -) - -func determinePort(confPort, portFromCommandLine uint16) uint16 { - if portFromCommandLine != 0 { - return portFromCommandLine - } - if confPort != 0 { - return confPort - } - return DEFAULT_PORT -} - -func openUDPConn(listenIP string, port uint16) (*net.UDPConn, error) { - myAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", listenIP, port)) - if err != nil { - return nil, fmt.Errorf("failed to construct UDP address: %w", err) - } - - return net.ListenUDP("udp", myAddr) -} - -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(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(1000) - 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 -}