package peer import ( "log" "net/netip" "time" "golang.zx2c4.com/wireguard/wgctrl" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" ) const reporterInterval = 5 * time.Second // EndpointReporter runs on relay peers only. It polls wgctrl every 5s and, // for each peer whose LastHandshakeTime has changed, sends MsgYourEndpoint to // that peer's VPN IP so it learns its own external WG endpoint. type EndpointReporter struct { client *wgctrl.Client devName string network []byte netName string control *ControlServer lastTimes map[wgtypes.Key]time.Time } func NewEndpointReporter(g Globals, cs *ControlServer, netName string) *EndpointReporter { return &EndpointReporter{ client: g.WGClient, devName: g.WGDevName, network: g.Network, netName: netName, control: cs, lastTimes: make(map[wgtypes.Key]time.Time), } } func (er *EndpointReporter) Run() { for range time.Tick(reporterInterval) { er.poll() } } func (er *EndpointReporter) poll() { dev, err := er.client.Device(er.devName) if err != nil { log.Printf("[EndpointReporter] get device: %v", err) return } state, err := loadNetworkState(er.netName) if err != nil { log.Printf("[EndpointReporter] load state: %v", err) return } // Build WGPubKey → VPN IP byte index from current network state. keyToIP := make(map[wgtypes.Key]byte, len(dev.Peers)) for _, p := range state.Peers { if p == nil || len(p.WGPubKey) != wgtypes.KeyLen { continue } key, err := wgtypes.NewKey(p.WGPubKey) if err == nil { keyToIP[key] = p.PeerIP } } for _, p := range dev.Peers { if p.Endpoint == nil || p.LastHandshakeTime.IsZero() { continue } if p.LastHandshakeTime == er.lastTimes[p.PublicKey] { continue } peerIPByte, ok := keyToIP[p.PublicKey] if !ok { continue } er.lastTimes[p.PublicKey] = p.LastHandshakeTime peerVPNIP := netip.AddrFrom4([4]byte{ er.network[0], er.network[1], er.network[2], peerIPByte, }) er.control.SendYourEndpoint(peerVPNIP, p.Endpoint.AddrPort()) } }