package peer import ( "log" "net" "net/netip" "time" "vppn/peer/control" ) var _ ControlConn = (*udpControlConn)(nil) type udpControlConn struct { conn *net.UDPConn } // newUDPControlConn opens a UDP socket bound to localIP:port. func newUDPControlConn(localIP netip.Addr, port uint16) (*udpControlConn, error) { addr := net.UDPAddrFromAddrPort(netip.AddrPortFrom(localIP, port)) conn, err := net.ListenUDP("udp4", addr) if err != nil { return nil, err } return &udpControlConn{conn: conn}, nil } func (c *udpControlConn) SendPing(dst netip.AddrPort, ping control.Ping, buf []byte) error { _, err := c.conn.WriteToUDP(ping.Marshal(buf), net.UDPAddrFromAddrPort(dst)) return err } // run reads incoming ping packets and forwards them to ch until ctx is done. // Call this in a goroutine before starting the App event loop. func (c *udpControlConn) run(ch chan<- PingEvent) { const errorTimeout = 8 * time.Second var buf [control.Size]byte for { n, src, err := c.conn.ReadFromUDP(buf[:]) if err != nil { log.Printf("control read: %v", err) time.Sleep(errorTimeout) continue } if n != control.Size { continue } ping, err := control.Unmarshal(buf) if err != nil { log.Printf("control unmarshal: %v", err) continue } srcIP, ok := netip.AddrFromSlice(src.IP) if !ok { continue } ch <- PingEvent{srcVPNIP: srcIP.Unmap(), ping: ping} } } func (c *udpControlConn) Close() error { return c.conn.Close() }