Files
vppn/peer/app.go
2026-06-07 18:12:44 +02:00

106 lines
2.1 KiB
Go

package peer
import (
"net/netip"
"os"
"os/signal"
"syscall"
"time"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"vppn/peer/control"
"vppn/peer/wginterface"
)
var _ WGDevice = (*wginterface.Device)(nil) // compile-time check: Device satisfies WGDevice
const (
ControlPort = 4561
PingInterval = 8 * time.Second
TimeoutInterval = 30 * time.Second
)
// HubPeer is a peer entry as reported by the hub poller.
type HubPeer struct {
PubKey wgtypes.Key
VPNIP netip.Addr
IsRelay bool
IsPublic bool
EndpointV4 netip.AddrPort // zero if none
EndpointV6 netip.AddrPort // zero if none
}
type PingEvent struct {
srcVPNIP netip.Addr
ping control.Ping
}
type MulticastEvent struct {
pubKey wgtypes.Key
vpnIP netip.Addr
endpoint netip.AddrPort
}
// App is the peer application. All mutable state lives here and is
// accessed only from the Run goroutine.
type App struct {
// Identity
vpnIP netip.Addr
vpnNet netip.Prefix
privKey wgtypes.Key
pubKey wgtypes.Key
isRelay bool
isPublic bool
// Infrastructure
dev WGDevice
controlConn ControlConn
// Peer state
relay *Peer
peersByKey map[wgtypes.Key]*Peer
peersByIP map[netip.Addr]*Peer
// Our own external endpoints, learned from Dst fields in incoming pings
selfV4 netip.AddrPort
selfV6 netip.AddrPort
// Event channels fed by background goroutines
hubAddCh <-chan HubPeer
hubRemoveCh <-chan wgtypes.Key
pingCh <-chan PingEvent
multicastCh <-chan MulticastEvent
}
// Run is the main event loop. It runs until SIGTERM/SIGINT.
func (a *App) Run() error {
ticker := time.NewTicker(PingInterval)
defer ticker.Stop()
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGTERM, syscall.SIGINT)
defer signal.Stop(sig)
for {
select {
case p := <-a.hubAddCh:
a.onAddPeer(p)
case key := <-a.hubRemoveCh:
a.onRemovePeer(key)
case e := <-a.pingCh:
a.onPing(e)
case e := <-a.multicastCh:
a.onMulticastDiscovery(e)
case <-ticker.C:
a.onTick()
case <-sig:
return a.onShutdown()
}
}
}
func (a *App) onShutdown() error {
return wginterface.Delete(a.dev.Name())
}