Files
vppn/peer/device.go
2026-06-14 08:15:00 +02:00

80 lines
2.4 KiB
Go

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) {
log.Printf("RELAYED: %s - %s ", p.Name, p.VPNIP.String())
devRetry(p.VPNIP, "AddPeer", func() error { return a.dev.AddPeer(p.PubKey()) })
p.State = StateRelayed
}
func (a *App) devAddDirect(p *Peer, endpoint netip.AddrPort) {
log.Printf("DIRECT: %s - %s @ %s", p.Name, p.VPNIP.String(), endpoint.String())
devRetry(p.VPNIP, "AddDirect", func() error { return a.dev.AddDirect(p.PubKey(), endpoint, p.VPNIP) })
p.State = StateDirect
}
func (a *App) devSetRelay(p *Peer, endpoint netip.AddrPort) {
log.Printf("RELAY: %s - %s @ %s", p.Name, p.VPNIP.String(), endpoint.String())
devRetry(p.VPNIP, "SetRelay", func() error { return a.dev.SetRelay(p.PubKey(), endpoint, a.vpnNet) })
p.State = StateDirect // Direct connection. The app marks peer as relay.
}
func (a *App) devPromote(p *Peer) {
ep := p.WGEndpoint()
if ep.IsValid() {
log.Printf("PROMOTED: %s - %s @ %s", p.Name, p.VPNIP.String(), p.WGEndpoint().String())
} else {
log.Printf("DIRECT: %s - %s (waiting for handshake)", p.Name, p.VPNIP.String())
}
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) {
log.Printf("PROBE: %s - %s @ %s", p.Name, p.VPNIP.String(), endpoint.String())
devRetry(p.VPNIP, "AddProbe", func() error { return a.dev.AddProbe(p.PubKey(), endpoint) })
p.State = StateProbing
}
func (a *App) devRemove(p *Peer) {
log.Printf("REMOVED: %s - %s", p.Name, p.VPNIP.String())
devRetry(p.VPNIP, "RemovePeer", func() error { return a.dev.RemovePeer(p.PubKey()) })
}