Cleanup
This commit is contained in:
		| @@ -3,6 +3,9 @@ | |||||||
| ## Roadmap | ## Roadmap | ||||||
|  |  | ||||||
| * Node: use symmetric encryption after handshake | * 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 | * Use default port 456 | ||||||
| * Remove signing key from hub | * Remove signing key from hub | ||||||
| * Peer: UDP hole-punching | * Peer: UDP hole-punching | ||||||
|   | |||||||
| @@ -1,9 +0,0 @@ | |||||||
| digraph d { |  | ||||||
|   init -> null; |  | ||||||
|   init -> unconnectedServer; |  | ||||||
|   init -> unconnectedClient; |  | ||||||
|   init -> unconnectedMediated; |  | ||||||
|   unconnectedServer -> connectedServer; |  | ||||||
|   unconnectedClient -> connectedClient; |  | ||||||
|   unconnectedMediated -> connectedMediated; |  | ||||||
| } |  | ||||||
| @@ -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) |  | ||||||
| } |  | ||||||
| @@ -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: |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -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) |  | ||||||
| } |  | ||||||
| @@ -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 |  | ||||||
| } |  | ||||||
| @@ -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[:] |  | ||||||
| } |  | ||||||
| @@ -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) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -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 |  | ||||||
| } |  | ||||||
| @@ -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) |  | ||||||
| } |  | ||||||
| @@ -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. |  | ||||||
| ) |  | ||||||
							
								
								
									
										79
									
								
								peer/main.go
									
									
									
									
									
								
							
							
						
						
									
										79
									
								
								peer/main.go
									
									
									
									
									
								
							| @@ -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.") |  | ||||||
| } |  | ||||||
| @@ -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 |  | ||||||
| } |  | ||||||
| @@ -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) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -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 |  | ||||||
| } |  | ||||||
							
								
								
									
										138
									
								
								peer/packets.go
									
									
									
									
									
								
							
							
						
						
									
										138
									
								
								peer/packets.go
									
									
									
									
									
								
							| @@ -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 |  | ||||||
| } |  | ||||||
| @@ -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) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -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 |  | ||||||
| } |  | ||||||
							
								
								
									
										65
									
								
								peer/peer.go
									
									
									
									
									
								
							
							
						
						
									
										65
									
								
								peer/peer.go
									
									
									
									
									
								
							| @@ -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() |  | ||||||
| } |  | ||||||
| @@ -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) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -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) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -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]}) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -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 |  | ||||||
| } |  | ||||||
| @@ -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, |  | ||||||
| 	}) |  | ||||||
| } |  | ||||||
							
								
								
									
										156
									
								
								peer/startup.go
									
									
									
									
									
								
							
							
						
						
									
										156
									
								
								peer/startup.go
									
									
									
									
									
								
							| @@ -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 |  | ||||||
| } |  | ||||||
		Reference in New Issue
	
	Block a user