WIP - cleanup / local discovery
This commit is contained in:
		| @@ -6,7 +6,6 @@ import ( | ||||
| 	"crypto/rand" | ||||
| ) | ||||
|  | ||||
| // TODO: Use [32]byte for simplicity everywhere. | ||||
| type dataCipher struct { | ||||
| 	key  [32]byte | ||||
| 	aead cipher.AEAD | ||||
| @@ -20,7 +19,6 @@ func newDataCipher() *dataCipher { | ||||
| 	return newDataCipherFromKey(key) | ||||
| } | ||||
|  | ||||
| // key must be 32 bytes. | ||||
| func newDataCipherFromKey(key [32]byte) *dataCipher { | ||||
| 	block, err := aes.NewCipher(key[:]) | ||||
| 	if err != nil { | ||||
|   | ||||
							
								
								
									
										13
									
								
								node/cipher-discovery.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								node/cipher-discovery.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| package node | ||||
|  | ||||
| /* | ||||
| func signData(privKey *[64]byte, h header, data, out []byte) []byte { | ||||
| 	out = out[:headerSize] | ||||
| 	h.Marshal(out) | ||||
| 	return sign.Sign(out, data, privKey) | ||||
| } | ||||
|  | ||||
| func openData(pubKey *[32]byte, signed, out []byte) (data []byte, ok bool) { | ||||
| 	return sign.Open(out[:0], signed[headerSize:], pubKey) | ||||
| } | ||||
| */ | ||||
| @@ -25,7 +25,7 @@ func (w *connWriter) WriteTo(packet []byte, addr netip.AddrPort) { | ||||
| 	// packets may fail to be sent in a timely manner causing timeouts. | ||||
| 	w.lock.Lock() | ||||
| 	if _, err := w.conn.WriteToUDPAddrPort(packet, addr); err != nil { | ||||
| 		log.Fatalf("Failed to write to UDP port: %v", err) | ||||
| 		log.Printf("Failed to write to UDP port: %v", err) | ||||
| 	} | ||||
| 	w.lock.Unlock() | ||||
| } | ||||
|   | ||||
| @@ -20,6 +20,7 @@ type peerRoute struct { | ||||
| 	Up            bool // True if data can be sent on the route. | ||||
| 	Relay         bool // True if the peer is a relay. | ||||
| 	Direct        bool // True if this is a direct connection. | ||||
| 	PubSignKey    []byte | ||||
| 	ControlCipher *controlCipher | ||||
| 	DataCipher    *dataCipher | ||||
| 	RemoteAddr    netip.AddrPort // Remote address if directly connected. | ||||
| @@ -27,10 +28,11 @@ type peerRoute struct { | ||||
|  | ||||
| var ( | ||||
| 	// Configuration for this peer. | ||||
| 	netName    string | ||||
| 	localIP    byte | ||||
| 	localPub   bool | ||||
| 	privateKey []byte | ||||
| 	netName     string | ||||
| 	localIP     byte | ||||
| 	localPub    bool | ||||
| 	privKey     []byte | ||||
| 	privSignKey []byte | ||||
|  | ||||
| 	// Shared interface for writing. | ||||
| 	_iface *ifWriter | ||||
| @@ -80,7 +82,7 @@ var ( | ||||
| 	}() | ||||
|  | ||||
| 	// Managed by the relayManager. | ||||
| 	discoveryPackets chan controlPacket | ||||
| 	localAddr        *atomic.Pointer[netip.AddrPort] // May be nil. | ||||
| 	relayIP          *atomic.Pointer[byte]           // May be nil. | ||||
| 	discoveryPackets = make(chan controlPacket, 256) | ||||
| 	localAddr        = &atomic.Pointer[netip.AddrPort]{} | ||||
| 	relayIP          = &atomic.Pointer[byte]{} | ||||
| ) | ||||
|   | ||||
							
								
								
									
										75
									
								
								node/localbroadcaster.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								node/localbroadcaster.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,75 @@ | ||||
| package node | ||||
|  | ||||
| import ( | ||||
| 	"encoding/binary" | ||||
| 	"log" | ||||
| 	"net" | ||||
| 	"net/netip" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| func localBroadcaster() { | ||||
| 	var ( | ||||
| 		buf1 = make([]byte, bufferSize) | ||||
| 		buf2 = make([]byte, bufferSize) | ||||
| 	) | ||||
| 	time.Sleep(4 * time.Second) | ||||
| 	doBroadcast(buf1, buf2) | ||||
| 	for range time.Tick(32 * time.Second) { | ||||
| 		doBroadcast(buf1, buf2) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func doBroadcast(buf1, buf2 []byte) { | ||||
| 	ifaces, err := net.Interfaces() | ||||
| 	if err != nil { | ||||
| 		log.Printf("Failed to list interfaces: %v", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	for _, iface := range ifaces { | ||||
| 		if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagRunning == 0 { | ||||
| 			continue | ||||
| 		} | ||||
| 		if iface.Flags&net.FlagPointToPoint != 0 { | ||||
| 			continue | ||||
| 		} | ||||
| 		if iface.Flags&net.FlagBroadcast == 0 { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		addrs, err := iface.Addrs() | ||||
| 		if err != nil { | ||||
| 			log.Printf("Failed to get interface addresses: %v", err) | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		for _, addr := range addrs { | ||||
| 			ipNet, ok := addr.(*net.IPNet) | ||||
| 			if !ok { | ||||
| 				continue | ||||
| 			} | ||||
| 			ip4 := ipNet.IP.To4() | ||||
| 			if ip4 == nil { | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			ip, ok := lastAddr(ipNet) | ||||
| 			if !ok { | ||||
| 				log.Printf("Failed to find broadcast address: %v", ipNet) | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			log.Printf("Broadcasting on address: %v", ip) | ||||
| 			//addr := netip.AddrPortFrom(ip, 456) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // works when the n is a prefix, otherwise... | ||||
| func lastAddr(n *net.IPNet) (netip.Addr, bool) { | ||||
| 	ip := make(net.IP, len(n.IP.To4())) | ||||
| 	binary.BigEndian.PutUint32(ip, binary.BigEndian.Uint32(n.IP.To4())| | ||||
| 		^binary.BigEndian.Uint32(net.IP(n.Mask).To4())) | ||||
| 	return netip.AddrFromSlice(ip) | ||||
| } | ||||
							
								
								
									
										101
									
								
								node/localdiscovery.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								node/localdiscovery.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | ||||
| package node | ||||
|  | ||||
| import ( | ||||
| 	"log" | ||||
| 	"net" | ||||
| 	"net/netip" | ||||
| 	"time" | ||||
|  | ||||
| 	"golang.org/x/crypto/nacl/sign" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	signOverhead  = 64 | ||||
| 	multicastIP   = netip.AddrFrom4([4]byte{224, 0, 0, 157}) | ||||
| 	multicastAddr = net.UDPAddrFromAddrPort(netip.AddrPortFrom(multicastIP, 4560)) | ||||
| ) | ||||
|  | ||||
| func localDiscovery() { | ||||
| 	conn, err := net.ListenMulticastUDP("udp", nil, multicastAddr) | ||||
| 	if err != nil { | ||||
| 		log.Printf("Failed to bind to multicast address: %v", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	go sendLocalDiscovery(conn) | ||||
| 	go recvLocalDiscovery(conn) | ||||
| } | ||||
|  | ||||
| func sendLocalDiscovery(conn *net.UDPConn) { | ||||
| 	var ( | ||||
| 		buf1 = make([]byte, bufferSize) | ||||
| 		buf2 = make([]byte, bufferSize) | ||||
| 	) | ||||
|  | ||||
| 	for range time.Tick(16 * time.Second) { | ||||
| 		signed := buildLocalDiscoveryPacket(buf1, buf2) | ||||
| 		if _, err := conn.WriteToUDP(signed, multicastAddr); err != nil { | ||||
| 			log.Printf("Failed to write multicast UDP packet: %v", err) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func recvLocalDiscovery(conn *net.UDPConn) { | ||||
| 	var ( | ||||
| 		raw = make([]byte, bufferSize) | ||||
| 		buf = make([]byte, bufferSize) | ||||
| 	) | ||||
|  | ||||
| 	for { | ||||
| 		n, remoteAddr, err := conn.ReadFromUDPAddrPort(raw[:bufferSize]) | ||||
| 		if err != nil { | ||||
| 			log.Fatalf("Failed to read from UDP port (multicast): %v", err) | ||||
| 		} | ||||
|  | ||||
| 		raw = raw[:n] | ||||
| 		h, ok := openLocalDiscoveryPacket(raw, buf) | ||||
| 		if !ok { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		pkt := controlPacket{ | ||||
| 			SrcIP:   h.SourceIP, | ||||
| 			SrcAddr: remoteAddr, | ||||
| 			Payload: localDiscoveryPacket{}, | ||||
| 		} | ||||
|  | ||||
| 		select { | ||||
| 		case controlPackets[h.SourceIP] <- pkt: | ||||
| 		default: | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func buildLocalDiscoveryPacket(buf1, buf2 []byte) []byte { | ||||
| 	h := header{ | ||||
| 		StreamID: controlStreamID, | ||||
| 		Counter:  0, | ||||
| 		SourceIP: localIP, | ||||
| 		DestIP:   255, | ||||
| 	} | ||||
| 	out := buf1[:headerSize] | ||||
| 	h.Marshal(out) | ||||
| 	return sign.Sign(buf2[:0], out, (*[64]byte)(privSignKey)) | ||||
| } | ||||
|  | ||||
| func openLocalDiscoveryPacket(raw, buf []byte) (h header, ok bool) { | ||||
| 	if len(raw) != headerSize+signOverhead { | ||||
| 		ok = false | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	h.Parse(raw[signOverhead:]) | ||||
| 	route := routingTable[h.SourceIP].Load() | ||||
| 	if route == nil || route.PubSignKey == nil { | ||||
| 		ok = false | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	_, ok = sign.Open(buf[:0], raw, (*[32]byte)(route.PubSignKey)) | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										35
									
								
								node/localdiscovery_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								node/localdiscovery_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| package node | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto/rand" | ||||
| 	"testing" | ||||
|  | ||||
| 	"golang.org/x/crypto/nacl/sign" | ||||
| ) | ||||
|  | ||||
| func TestLocalDiscoveryPacketSigning(t *testing.T) { | ||||
| 	localIP = 32 | ||||
|  | ||||
| 	var ( | ||||
| 		buf1                      = make([]byte, bufferSize) | ||||
| 		buf2                      = make([]byte, bufferSize) | ||||
| 		pubSignKey, privSigKey, _ = sign.GenerateKey(rand.Reader) | ||||
| 	) | ||||
|  | ||||
| 	privSignKey = privSigKey[:] | ||||
| 	route := routingTable[localIP].Load() | ||||
| 	route.IP = byte(localIP) | ||||
| 	route.PubSignKey = pubSignKey[0:32] | ||||
| 	routingTable[localIP].Store(route) | ||||
|  | ||||
| 	out := buildLocalDiscoveryPacket(buf1, buf2) | ||||
|  | ||||
| 	h, ok := openLocalDiscoveryPacket(bytes.Clone(out), buf1) | ||||
| 	if !ok { | ||||
| 		t.Fatal(h, ok) | ||||
| 	} | ||||
| 	if h.StreamID != controlStreamID || h.SourceIP != localIP || h.DestIP != 255 { | ||||
| 		t.Fatal(h) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										26
									
								
								node/main.go
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								node/main.go
									
									
									
									
									
								
							| @@ -11,7 +11,6 @@ import ( | ||||
| 	"net/netip" | ||||
| 	"os" | ||||
| 	"runtime/debug" | ||||
| 	"sync/atomic" | ||||
| 	"vppn/m" | ||||
| ) | ||||
|  | ||||
| @@ -50,10 +49,6 @@ func Main() { | ||||
| } | ||||
|  | ||||
| func mainInit(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) | ||||
| @@ -102,14 +97,14 @@ func main(listenIP string, port uint16) { | ||||
| 		log.Fatalf("Failed to open UDP port: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	conn.SetReadBuffer(1024 * 1024 * 8) | ||||
| 	conn.SetWriteBuffer(1024 * 1024 * 8) | ||||
|  | ||||
| 	// Intialize globals. | ||||
| 	_iface = newIFWriter(iface) | ||||
| 	_conn = newConnWriter(conn) | ||||
|  | ||||
| 	localIP = config.PeerIP | ||||
| 	discoveryPackets = make(chan controlPacket, 256) | ||||
| 	localAddr = &atomic.Pointer[netip.AddrPort]{} | ||||
| 	relayIP = &atomic.Pointer[byte]{} | ||||
|  | ||||
| 	ip, ok := netip.AddrFromSlice(config.PublicIP) | ||||
| 	if ok { | ||||
| @@ -118,7 +113,8 @@ func main(listenIP string, port uint16) { | ||||
| 		localAddr.Store(&addr) | ||||
| 	} | ||||
|  | ||||
| 	privateKey = config.PrivKey | ||||
| 	privKey = config.PrivKey | ||||
| 	privSignKey = config.PrivSignKey | ||||
|  | ||||
| 	// Start supervisors. | ||||
| 	for i := range 256 { | ||||
| @@ -130,7 +126,9 @@ func main(listenIP string, port uint16) { | ||||
| 	} else { | ||||
| 		go addrDiscoveryClient() | ||||
| 		go relayManager() | ||||
| 		go localDiscovery() | ||||
| 	} | ||||
|  | ||||
| 	go newHubPoller(config).Run() | ||||
| 	go readFromConn(conn) | ||||
| 	readFromIFace(iface) | ||||
| @@ -206,17 +204,17 @@ func handleControlPacket(addr netip.AddrPort, h header, data, decBuf []byte) { | ||||
|  | ||||
| 	out, ok := route.ControlCipher.Decrypt(data, decBuf) | ||||
| 	if !ok { | ||||
| 		//log.Printf("Failed to decrypt control packet.") | ||||
| 		log.Printf("Failed to decrypt control packet.") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	if len(out) == 0 { | ||||
| 		//log.Printf("Empty control packet from: %d", h.SourceIP) | ||||
| 		log.Printf("Empty control packet from: %d", h.SourceIP) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	if dupChecks[h.SourceIP].IsDup(h.Counter) { | ||||
| 		//log.Printf("[%03d] Duplicate control packet: %d", h.SourceIP, h.Counter) | ||||
| 		log.Printf("[%03d] Duplicate control packet: %d", h.SourceIP, h.Counter) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| @@ -252,7 +250,7 @@ func handleControlPacket(addr netip.AddrPort, h header, data, decBuf []byte) { | ||||
| func handleDataPacket(h header, data []byte, decBuf []byte) { | ||||
| 	route := routingTable[h.SourceIP].Load() | ||||
| 	if !route.Up { | ||||
| 		//log.Printf("Not connected (recv).") | ||||
| 		log.Printf("Not connected (recv).") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| @@ -263,7 +261,7 @@ func handleDataPacket(h header, data []byte, decBuf []byte) { | ||||
| 	} | ||||
|  | ||||
| 	if dupChecks[h.SourceIP].IsDup(h.Counter) { | ||||
| 		//log.Printf("[%03d] Duplicate data packet: %d", h.SourceIP, h.Counter) | ||||
| 		log.Printf("[%03d] Duplicate data packet: %d", h.SourceIP, h.Counter) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
|   | ||||
							
								
								
									
										1
									
								
								node/messages.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								node/messages.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| package node | ||||
| @@ -138,3 +138,7 @@ func parseProbePacket(buf []byte) (p probePacket, err error) { | ||||
| 		Error() | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // ---------------------------------------------------------------------------- | ||||
|  | ||||
| type localDiscoveryPacket struct{} | ||||
|   | ||||
| @@ -10,7 +10,6 @@ import ( | ||||
| func TestPacketSyn(t *testing.T) { | ||||
| 	in := synPacket{ | ||||
| 		TraceID:  newTraceID(), | ||||
| 		RelayIP:  4, | ||||
| 		FromAddr: netip.AddrPortFrom(netip.AddrFrom4([4]byte{4, 5, 6, 7}), 22), | ||||
| 	} | ||||
| 	rand.Read(in.SharedKey[:]) | ||||
|   | ||||
| @@ -115,7 +115,8 @@ func (s *peerSupervisor) _peerUpdate(peer *m.Peer) stateFunc { | ||||
| 	} | ||||
|  | ||||
| 	s.staged.IP = s.remoteIP | ||||
| 	s.staged.ControlCipher = newControlCipher(privateKey, peer.PubKey) | ||||
| 	s.staged.ControlCipher = newControlCipher(privKey, peer.PubKey) | ||||
| 	s.staged.PubSignKey = peer.PubSignKey | ||||
| 	s.staged.DataCipher = newDataCipher() | ||||
|  | ||||
| 	if ip, isValid := netip.AddrFromSlice(peer.PublicIP); isValid { | ||||
| @@ -241,7 +242,7 @@ func (s *peerSupervisor) client() stateFunc { | ||||
| 		probe     probePacket | ||||
| 		probeAddr netip.AddrPort | ||||
|  | ||||
| 		lAddr netip.AddrPort | ||||
| 		remoteAddr netip.AddrPort | ||||
|  | ||||
| 		timeoutTimer = time.NewTimer(timeoutInterval) | ||||
| 		pingTimer    = time.NewTimer(pingInterval) | ||||
| @@ -306,9 +307,9 @@ func (s *peerSupervisor) client() stateFunc { | ||||
| 			// Send syn. | ||||
|  | ||||
| 			syn.FromAddr = getLocalAddr() | ||||
| 			if syn.FromAddr != lAddr { | ||||
| 			if syn.FromAddr != remoteAddr { | ||||
| 				syn.TraceID = newTraceID() | ||||
| 				lAddr = syn.FromAddr | ||||
| 				remoteAddr = syn.FromAddr | ||||
| 			} | ||||
|  | ||||
| 			s.sendControlPacket(syn) | ||||
| @@ -319,6 +320,9 @@ func (s *peerSupervisor) client() stateFunc { | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			// TODO: Check if we have local address. | ||||
| 			// TODO: Send local probe | ||||
|  | ||||
| 			if !ack.FromAddr.IsValid() { | ||||
| 				continue | ||||
| 			} | ||||
|   | ||||
							
								
								
									
										1
									
								
								node/signing.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								node/signing.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| package node | ||||
		Reference in New Issue
	
	Block a user