package peer import ( "errors" "log" "net/netip" "syscall" "time" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" ) // devRetry calls fn up to 6 times with exponential backoff, retrying on EBUSY // (transient netlink contention during WireGuard handshake/rekey). Fatal on any other error. func devRetry(vpnIP netip.Addr, op string, fn func() error) { const attempts = 6 timeout := 10 * time.Millisecond for i := range attempts { err := fn() if err == nil { return } if errors.Is(err, syscall.EBUSY) && i < attempts-1 { time.Sleep(timeout) timeout *= 2 continue } log.Fatalf("%s %v: %v", op, vpnIP, err) } } func (a *App) devPeers() []wgtypes.Peer { peers, err := a.dev.Peers() if err != nil { log.Fatalf("Failed to get peers %v: %v", a.vpnIP, err) } return peers } func (a *App) devAddPeer(p *Peer) { devRetry(p.VPNIP, "AddPeer", func() error { return a.dev.AddPeer(p.PubKey()) }) p.State = StateRelayed } func (a *App) devAddDirect(p *Peer, endpoint netip.AddrPort) { devRetry(p.VPNIP, "AddDirect", func() error { return a.dev.AddDirect(p.PubKey(), endpoint, p.VPNIP) }) p.State = StateDirect } func (a *App) devAddRelayed(p *Peer, relayVPNIP netip.Addr) { port := ForwarderBasePort + uint16(p.VPNIP.As4()[3]) endpoint := netip.AddrPortFrom(relayVPNIP, port) devRetry(p.VPNIP, "AddRelayed", func() error { return a.dev.AddDirect(p.PubKey(), endpoint, p.VPNIP) }) p.State = StateRelayed } func (a *App) devSetRelay(p *Peer, endpoint netip.AddrPort) { devRetry(p.VPNIP, "SetRelay", func() error { return a.dev.SetRelay(p.PubKey(), endpoint, a.vpnNet) }) } func (a *App) devPromote(p *Peer) { devRetry(p.VPNIP, "Promote", func() error { return a.dev.Promote(p.PubKey(), p.VPNIP) }) p.State = StateDirect } func (a *App) devAddProbe(p *Peer, endpoint netip.AddrPort) { devRetry(p.VPNIP, "AddProbe", func() error { return a.dev.AddProbe(p.PubKey(), endpoint) }) p.State = StateProbing } func (a *App) devRemove(p *Peer) { devRetry(p.VPNIP, "RemovePeer", func() error { return a.dev.RemovePeer(p.PubKey()) }) }