Files
vppn/peer/endpointreporter.go
2026-06-06 18:46:46 +02:00

89 lines
2.0 KiB
Go

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())
}
}