package peer import ( "log" "math" "net/netip" "time" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" "vppn/m" "vppn/peer/control" ) func (a *App) onAddPeer(p m.Peer) { a.onRemovePeer(p.WGPubKey) octets := a.vpnNet.Addr().As4() octets[3] = p.PeerIP vpnIP := netip.AddrFrom4(octets) peer := &Peer{ wgPeer: wgtypes.Peer{PublicKey: p.WGPubKey}, VPNIP: vpnIP, Name: p.Name, IsRelay: p.Relay, IsPublic: p.IsPublic(), EndpointV4: p.Endpoint4(), EndpointV6: p.Endpoint6(), RTT: time.Duration(math.MaxInt64) * time.Nanosecond, Role: roleFor(a.isPublic, a.vpnIP, p.IsPublic(), vpnIP), SignPubKey: p.SignPubKey, } a.peersByKey[p.WGPubKey] = peer a.peersByIP[peer.VPNIP] = peer defer a.updateHosts() if !peer.IsPublic { if a.isPublic { // Public nodes accept traffic from non-public peers as soon as they // initiate a handshake. Set /32 AllowedIPs now; WireGuard learns the // endpoint from the incoming handshake automatically. a.devPromote(peer) } else { a.devAddPeer(peer) } return } a.devAddDirect(peer, peer.PreferredEndpoint()) } 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) a.updateHosts() 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 { // If we have a relay, it's public, so should go back to being a direct // peer - this will convert it's /24 to a /32. a.devAddDirect(a.relay, a.relay.PreferredEndpoint()) a.relay = nil } var best *Peer for _, p := range a.peersByKey { if !p.CanRelay() { continue } if best == nil || p.RTT < best.RTT { best = p } } if best == nil { log.Printf("no relay available") return } a.devSetRelay(best, best.PreferredEndpoint()) a.relay = best } func roleFor(selfIsPublic bool, selfIP netip.Addr, peerIsPublic bool, peerVPNIP netip.Addr) control.Role { if !selfIsPublic && peerIsPublic { return control.Client } if selfIsPublic && !peerIsPublic { return control.Server } return control.RoleFor(selfIP, peerVPNIP) }