Files
vppn/peer/on_hub.go
2026-06-07 09:33:21 +02:00

113 lines
2.0 KiB
Go

package peer
import (
"log"
"net/netip"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"vppn/peer/control"
)
func (a *App) onAddPeer(p HubPeer) {
a.onRemovePeer(p.PubKey)
peer := &Peer{
wgPeer: wgtypes.Peer{PublicKey: p.PubKey},
VPNIP: p.VPNIP,
IsRelay: p.IsRelay,
IsPublic: p.IsPublic,
Endpoint4: p.EndpointV4,
Endpoint6: p.EndpointV6,
Role: roleFor(a.isPublic, a.vpnIP, p),
}
endpoint := peer.PreferredEndpoint()
if peer.IsPublic && !endpoint.IsValid() {
// The peer is misconfigured.
// TODO: Log here.
return
}
a.peersByKey[p.PubKey] = peer
a.peersByIP[peer.VPNIP] = peer
if !peer.IsPublic {
a.devAddPeer(peer)
return
}
a.devAddDirect(peer, endpoint)
}
func (a *App) onRemovePeer(key wgtypes.Key) {
peer, exists := a.peersByKey[key]
if !exists {
return
}
a.devRemove(peer)
delete(a.peersByKey, key)
delete(a.peersByIP, peer.VPNIP)
if peer == a.relay {
a.relay = nil
a.switchActiveRelay()
}
}
// switchActiveRelay promotes the lowest-latency relay peer to active.
func (a *App) switchActiveRelay() {
if a.relay != nil {
a.devAddDirect(a.relay, a.relay.WGEndpoint())
a.relay = nil
}
var best *Peer
for _, p := range a.peersByKey {
if !p.CanRelay() {
continue
}
if best == nil || betterRelay(p, best) {
best = p
}
}
if best == nil {
log.Printf("no relay available")
return
}
a.devSetRelay(best, best.WGEndpoint())
a.relay = best
}
// TODO: Why not < ??
// betterRelay reports whether a is a better relay candidate than b.
// Prefers lower RTT; treats zero RTT (no measurement yet) as worst case.
func betterRelay(a, b *Peer) bool {
if a.RTT == 0 {
return false
}
if b.RTT == 0 {
return true
}
return a.RTT < b.RTT
}
func preferredEndpoint(v4, v6 netip.AddrPort) netip.AddrPort {
if v4.IsValid() {
return v4
}
return v6
}
func roleFor(selfIsPublic bool, selfIP netip.Addr, p HubPeer) control.Role {
if !selfIsPublic && p.IsPublic {
return control.Client
}
if selfIsPublic && !p.IsPublic {
return control.Server
}
return control.RoleFor(selfIP, p.VPNIP)
}