This commit is contained in:
jdl
2026-06-07 20:30:31 +02:00
parent 747d73890f
commit 3877ec5f35
15 changed files with 173 additions and 80 deletions

View File

@@ -147,6 +147,7 @@ func (a *API) Network_List() ([]*Network, error) {
func (a *API) Peer_CreateNew(p *Peer) error { func (a *API) Peer_CreateNew(p *Peer) error {
p.Version = idgen.NextID(0) p.Version = idgen.NextID(0)
p.WGPubKey = []byte{} p.WGPubKey = []byte{}
p.SignPubKey = []byte{}
p.APIKey = idgen.NewToken() p.APIKey = idgen.NewToken()
return db.Peer_Insert(a.db, p) return db.Peer_Insert(a.db, p)
@@ -158,6 +159,7 @@ func (a *API) Peer_Init(peer *Peer, args m.PeerInitArgs) error {
peer.Version = idgen.NextID(0) peer.Version = idgen.NextID(0)
peer.WGPubKey = args.WGPubKey peer.WGPubKey = args.WGPubKey
peer.SignPubKey = args.SignPubKey
return db.Peer_UpdateFull(a.db, peer) return db.Peer_UpdateFull(a.db, peer)
} }

View File

@@ -339,19 +339,20 @@ func Network_List(
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
type Peer struct { type Peer struct {
NetworkID int64 NetworkID int64
PeerIP byte PeerIP byte
Version int64 Version int64
APIKey string APIKey string
Name string Name string
Addr4 []byte Addr4 []byte
Addr6 []byte Addr6 []byte
Port uint16 Port uint16
Relay bool Relay bool
WGPubKey []byte WGPubKey []byte
SignPubKey []byte
} }
const Peer_SelectQuery = "SELECT NetworkID,PeerIP,Version,APIKey,Name,Addr4,Addr6,Port,Relay,WGPubKey FROM peers" const Peer_SelectQuery = "SELECT NetworkID,PeerIP,Version,APIKey,Name,Addr4,Addr6,Port,Relay,WGPubKey,SignPubKey FROM peers"
func Peer_Insert( func Peer_Insert(
tx TX, tx TX,
@@ -362,7 +363,7 @@ func Peer_Insert(
return err return err
} }
_, err = tx.Exec("INSERT INTO peers(NetworkID,PeerIP,Version,APIKey,Name,Addr4,Addr6,Port,Relay,WGPubKey) VALUES(?,?,?,?,?,?,?,?,?,?)", row.NetworkID, row.PeerIP, row.Version, row.APIKey, row.Name, row.Addr4, row.Addr6, row.Port, row.Relay, row.WGPubKey) _, err = tx.Exec("INSERT INTO peers(NetworkID,PeerIP,Version,APIKey,Name,Addr4,Addr6,Port,Relay,WGPubKey,SignPubKey) VALUES(?,?,?,?,?,?,?,?,?,?,?)", row.NetworkID, row.PeerIP, row.Version, row.APIKey, row.Name, row.Addr4, row.Addr6, row.Port, row.Relay, row.WGPubKey, row.SignPubKey)
return err return err
} }
@@ -403,7 +404,7 @@ func Peer_UpdateFull(
return err return err
} }
result, err := tx.Exec("UPDATE peers SET Version=?,APIKey=?,Name=?,Addr4=?,Addr6=?,Port=?,Relay=?,WGPubKey=? WHERE NetworkID=? AND PeerIP=?", row.Version, row.APIKey, row.Name, row.Addr4, row.Addr6, row.Port, row.Relay, row.WGPubKey, row.NetworkID, row.PeerIP) result, err := tx.Exec("UPDATE peers SET Version=?,APIKey=?,Name=?,Addr4=?,Addr6=?,Port=?,Relay=?,WGPubKey=?,SignPubKey=? WHERE NetworkID=? AND PeerIP=?", row.Version, row.APIKey, row.Name, row.Addr4, row.Addr6, row.Port, row.Relay, row.WGPubKey, row.SignPubKey, row.NetworkID, row.PeerIP)
if err != nil { if err != nil {
return err return err
} }
@@ -455,8 +456,8 @@ func Peer_Get(
err error, err error,
) { ) {
row = &Peer{} row = &Peer{}
r := tx.QueryRow("SELECT NetworkID,PeerIP,Version,APIKey,Name,Addr4,Addr6,Port,Relay,WGPubKey FROM peers WHERE NetworkID=? AND PeerIP=?", NetworkID, PeerIP) r := tx.QueryRow("SELECT NetworkID,PeerIP,Version,APIKey,Name,Addr4,Addr6,Port,Relay,WGPubKey,SignPubKey FROM peers WHERE NetworkID=? AND PeerIP=?", NetworkID, PeerIP)
if err = r.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.Addr4, &row.Addr6, &row.Port, &row.Relay, &row.WGPubKey); err != nil { if err = r.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.Addr4, &row.Addr6, &row.Port, &row.Relay, &row.WGPubKey, &row.SignPubKey); err != nil {
row = nil row = nil
} }
return return
@@ -472,7 +473,7 @@ func Peer_GetWhere(
) { ) {
row = &Peer{} row = &Peer{}
r := tx.QueryRow(query, args...) r := tx.QueryRow(query, args...)
if err = r.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.Addr4, &row.Addr6, &row.Port, &row.Relay, &row.WGPubKey); err != nil { if err = r.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.Addr4, &row.Addr6, &row.Port, &row.Relay, &row.WGPubKey, &row.SignPubKey); err != nil {
row = nil row = nil
} }
return return
@@ -494,7 +495,7 @@ func Peer_Iterate(
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
row := &Peer{} row := &Peer{}
err := rows.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.Addr4, &row.Addr6, &row.Port, &row.Relay, &row.WGPubKey) err := rows.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.Addr4, &row.Addr6, &row.Port, &row.Relay, &row.WGPubKey, &row.SignPubKey)
if !yield(row, err) { if !yield(row, err) {
return return
} }

View File

@@ -19,5 +19,6 @@ TABLE peers OF Peer (
Addr6 []byte, Addr6 []byte,
Port uint16, Port uint16,
Relay bool, Relay bool,
WGPubKey []byte NoUpdate WGPubKey []byte NoUpdate,
SignPubKey []byte NoUpdate
); );

View File

@@ -20,5 +20,6 @@ CREATE TABLE peers (
Port INTEGER NOT NULL, Port INTEGER NOT NULL,
Relay INTEGER NOT NULL DEFAULT 0, -- Boolean if peer will forward packets. Relay INTEGER NOT NULL DEFAULT 0, -- Boolean if peer will forward packets.
WGPubKey BLOB NOT NULL, WGPubKey BLOB NOT NULL,
SignPubKey BLOB NOT NULL
PRIMARY KEY(NetworkID, PeerIP) PRIMARY KEY(NetworkID, PeerIP)
) WITHOUT ROWID; ) WITHOUT ROWID;

View File

@@ -313,6 +313,15 @@ func (a *App) _peerInit(peer *api.Peer, w http.ResponseWriter, r *http.Request)
return err return err
} }
if len(args.WGPubKey) != 32 {
http.Error(w, "invalid WGPubKey", http.StatusBadRequest)
return nil
}
if len(args.SignPubKey) != 32 {
http.Error(w, "invalid SignPubKey", http.StatusBadRequest)
return nil
}
net, err := a.api.Network_Get(peer.NetworkID) net, err := a.api.Network_Get(peer.NetworkID)
if err != nil { if err != nil {
return err return err
@@ -353,14 +362,15 @@ func (a *App) peersArray(networkID int64) (peers [256]*m.Peer, err error) {
for _, p := range l { for _, p := range l {
if len(p.WGPubKey) != 0 { if len(p.WGPubKey) != 0 {
peers[p.PeerIP] = &m.Peer{ peers[p.PeerIP] = &m.Peer{
PeerIP: p.PeerIP, PeerIP: p.PeerIP,
Version: p.Version, Version: p.Version,
Name: p.Name, Name: p.Name,
Addr4: p.Addr4, Addr4: p.Addr4,
Addr6: p.Addr6, Addr6: p.Addr6,
Port: p.Port, Port: p.Port,
Relay: p.Relay, Relay: p.Relay,
WGPubKey: p.WGPubKey, WGPubKey: p.WGPubKey,
SignPubKey: p.SignPubKey,
} }
} }
} }

View File

@@ -2,7 +2,8 @@
package m package m
type PeerInitArgs struct { type PeerInitArgs struct {
WGPubKey []byte WGPubKey []byte
SignPubKey []byte
} }
type PeerInitResp struct { type PeerInitResp struct {
@@ -18,8 +19,9 @@ type Peer struct {
Addr4 []byte Addr4 []byte
Addr6 []byte Addr6 []byte
Port uint16 Port uint16
Relay bool Relay bool
WGPubKey []byte WGPubKey []byte
SignPubKey []byte
} }
type NetworkState struct { type NetworkState struct {

View File

@@ -29,6 +29,7 @@ type HubPeer struct {
IsPublic bool IsPublic bool
EndpointV4 netip.AddrPort // zero if none EndpointV4 netip.AddrPort // zero if none
EndpointV6 netip.AddrPort // zero if none EndpointV6 netip.AddrPort // zero if none
SignPubKey [32]byte
} }
type PingEvent struct { type PingEvent struct {
@@ -36,10 +37,10 @@ type PingEvent struct {
ping control.Ping ping control.Ping
} }
// MulticastEvent carries a raw signed beacon for verification in the event loop.
type MulticastEvent struct { type MulticastEvent struct {
pubKey wgtypes.Key signed []byte // nacl/sign signed beacon (64-byte sig || 35-byte payload)
vpnIP netip.Addr src netip.Addr // physical LAN source address
endpoint netip.AddrPort
} }
// App is the peer application. All mutable state lives here and is // App is the peer application. All mutable state lives here and is

View File

@@ -71,8 +71,14 @@ func (hp *HubPoller) poll() {
log.Printf("[HubPoller] fetch: %v", err) log.Printf("[HubPoller] fetch: %v", err)
return return
} }
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
log.Printf("[HubPoller] unexpected status %d", resp.StatusCode)
return
}
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
_ = resp.Body.Close()
if err != nil { if err != nil {
log.Printf("[HubPoller] read body: %v", err) log.Printf("[HubPoller] read body: %v", err)
return return
@@ -93,7 +99,7 @@ func (hp *HubPoller) apply(state m.NetworkState) {
netAddr := hp.vpnNet.Addr().As4() netAddr := hp.vpnNet.Addr().As4()
for _, p := range state.Peers { for _, p := range state.Peers {
if p == nil || len(p.WGPubKey) != wgtypes.KeyLen { if p == nil || len(p.WGPubKey) != wgtypes.KeyLen || len(p.SignPubKey) != 32 {
continue continue
} }
@@ -138,6 +144,8 @@ func hubPeerFrom(pubKey wgtypes.Key, vpnIP netip.Addr, p *m.Peer) HubPeer {
ep6 = netip.AddrPortFrom(addr, p.Port) ep6 = netip.AddrPortFrom(addr, p.Port)
} }
} }
var signPubKey [32]byte
copy(signPubKey[:], p.SignPubKey)
return HubPeer{ return HubPeer{
PubKey: pubKey, PubKey: pubKey,
VPNIP: vpnIP, VPNIP: vpnIP,
@@ -145,5 +153,6 @@ func hubPeerFrom(pubKey wgtypes.Key, vpnIP netip.Addr, p *m.Peer) HubPeer {
IsPublic: ep4.IsValid() || ep6.IsValid(), IsPublic: ep4.IsValid() || ep6.IsValid(),
EndpointV4: ep4, EndpointV4: ep4,
EndpointV6: ep6, EndpointV6: ep6,
SignPubKey: signPubKey,
} }
} }

View File

@@ -2,6 +2,7 @@ package peer
import ( import (
"bytes" "bytes"
"crypto/rand"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
@@ -10,6 +11,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"golang.org/x/crypto/nacl/sign"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"vppn/m" "vppn/m"
@@ -19,6 +21,7 @@ import (
// loaded on every subsequent run. // loaded on every subsequent run.
type LocalState struct { type LocalState struct {
PrivKey wgtypes.Key PrivKey wgtypes.Key
SignKey [64]byte // nacl/sign Ed25519 private key
VPNIP netip.Addr VPNIP netip.Addr
VPNNet netip.Prefix VPNNet netip.Prefix
WGPort uint16 WGPort uint16
@@ -28,7 +31,8 @@ type LocalState struct {
// localStateJSON is the on-disk representation. // localStateJSON is the on-disk representation.
type localStateJSON struct { type localStateJSON struct {
PrivKey string `json:"priv_key"` // standard base64 PrivKey string `json:"priv_key"` // standard base64
SignKey string `json:"sign_key"` // standard base64
VPNIP netip.Addr `json:"vpn_ip"` VPNIP netip.Addr `json:"vpn_ip"`
VPNNet netip.Prefix `json:"vpn_net"` VPNNet netip.Prefix `json:"vpn_net"`
WGPort uint16 `json:"wg_port"` WGPort uint16 `json:"wg_port"`
@@ -60,8 +64,17 @@ func LoadOrInit(statePath, hubURL, apiKey string) (LocalState, error) {
} }
func initFromHub(hubURL, apiKey string, privKey wgtypes.Key) (LocalState, error) { func initFromHub(hubURL, apiKey string, privKey wgtypes.Key) (LocalState, error) {
pubKey := privKey.PublicKey() wgPubKey := privKey.PublicKey()
body, _ := json.Marshal(m.PeerInitArgs{WGPubKey: pubKey[:]})
signPubKey, signPrivKey, err := sign.GenerateKey(rand.Reader)
if err != nil {
return LocalState{}, fmt.Errorf("generate sign key: %w", err)
}
body, _ := json.Marshal(m.PeerInitArgs{
WGPubKey: wgPubKey[:],
SignPubKey: signPubKey[:],
})
req, err := http.NewRequest(http.MethodPost, hubURL+"/peer/init/", bytes.NewReader(body)) req, err := http.NewRequest(http.MethodPost, hubURL+"/peer/init/", bytes.NewReader(body))
if err != nil { if err != nil {
@@ -105,6 +118,7 @@ func initFromHub(hubURL, apiKey string, privKey wgtypes.Key) (LocalState, error)
return LocalState{ return LocalState{
PrivKey: privKey, PrivKey: privKey,
SignKey: *signPrivKey,
VPNIP: vpnIP, VPNIP: vpnIP,
VPNNet: vpnNet, VPNNet: vpnNet,
WGPort: wgPort, WGPort: wgPort,
@@ -126,8 +140,18 @@ func parseLocalState(data []byte) (LocalState, error) {
if err != nil { if err != nil {
return LocalState{}, fmt.Errorf("invalid key: %w", err) return LocalState{}, fmt.Errorf("invalid key: %w", err)
} }
signKeyBytes, err := base64.StdEncoding.DecodeString(j.SignKey)
if err != nil {
return LocalState{}, fmt.Errorf("decode sign key: %w", err)
}
if len(signKeyBytes) != 64 {
return LocalState{}, fmt.Errorf("invalid sign key length: %d", len(signKeyBytes))
}
var signKey [64]byte
copy(signKey[:], signKeyBytes)
return LocalState{ return LocalState{
PrivKey: key, PrivKey: key,
SignKey: signKey,
VPNIP: j.VPNIP, VPNIP: j.VPNIP,
VPNNet: j.VPNNet, VPNNet: j.VPNNet,
WGPort: j.WGPort, WGPort: j.WGPort,
@@ -139,6 +163,7 @@ func parseLocalState(data []byte) (LocalState, error) {
func saveLocalState(path string, s LocalState) error { func saveLocalState(path string, s LocalState) error {
j := localStateJSON{ j := localStateJSON{
PrivKey: base64.StdEncoding.EncodeToString(s.PrivKey[:]), PrivKey: base64.StdEncoding.EncodeToString(s.PrivKey[:]),
SignKey: base64.StdEncoding.EncodeToString(s.SignKey[:]),
VPNIP: s.VPNIP, VPNIP: s.VPNIP,
VPNNet: s.VPNNet, VPNNet: s.VPNNet,
WGPort: s.WGPort, WGPort: s.WGPort,

View File

@@ -8,11 +8,13 @@ import (
"net/netip" "net/netip"
"time" "time"
"golang.org/x/crypto/nacl/sign"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
) )
const ( const (
mcBeaconLen = 35 // 1 VPN IP byte + 32 WG pubkey + 2 WG listen port mcBeaconLen = 35 // 1 VPN IP byte + 32 WG pubkey + 2 WG listen port
mcSignedBeaconLen = sign.Overhead + mcBeaconLen // 64-byte nacl/sign prefix + payload
mcBroadcastInterval = 32 * time.Second mcBroadcastInterval = 32 * time.Second
mcErrorRetryInterval = 16 * time.Second mcErrorRetryInterval = 16 * time.Second
) )
@@ -21,18 +23,23 @@ var mcAddr = net.UDPAddrFromAddrPort(netip.AddrPortFrom(
netip.AddrFrom4([4]byte{224, 0, 0, 157}), netip.AddrFrom4([4]byte{224, 0, 0, 157}),
4560)) 4560))
// RunMCWriter broadcasts a beacon on the local multicast group every // RunMCWriter broadcasts a signed beacon on the local multicast group every
// mcBroadcastInterval so that LAN peers can discover our WireGuard endpoint. // mcBroadcastInterval so that LAN peers can discover our WireGuard endpoint.
func RunMCWriter(selfVPNIP netip.Addr, pubKey wgtypes.Key, wgPort uint16) { func RunMCWriter(selfVPNIP netip.Addr, pubKey wgtypes.Key, wgPort uint16, signKey *[64]byte) {
conn, err := net.ListenMulticastUDP("udp", nil, mcAddr) conn, err := net.ListenMulticastUDP("udp", nil, mcAddr)
if err != nil { if err != nil {
log.Fatalf("[MCWriter] bind: %v", err) log.Printf("[MCWriter] bind: %v", err)
return
} }
beacon := buildBeacon(selfVPNIP, pubKey, wgPort) payload := buildBeacon(selfVPNIP, pubKey, wgPort)
signed := sign.Sign(nil, payload, signKey)
if _, err := conn.WriteToUDP(signed, mcAddr); err != nil {
log.Printf("[MCWriter] write: %v", err)
}
for range time.Tick(mcBroadcastInterval) { for range time.Tick(mcBroadcastInterval) {
if _, err := conn.WriteToUDP(beacon, mcAddr); err != nil { if _, err := conn.WriteToUDP(signed, mcAddr); err != nil {
log.Printf("[MCWriter] write: %v", err) log.Printf("[MCWriter] write: %v", err)
} }
} }
@@ -64,7 +71,7 @@ func runMCReaderInner(vpnNet netip.Prefix, selfVPNIP netip.Addr, ch chan<- Multi
} }
defer conn.Close() defer conn.Close()
buf := make([]byte, 64) buf := make([]byte, mcSignedBeaconLen+1) // +1 to detect oversized packets
netAddr := vpnNet.Addr().As4() netAddr := vpnNet.Addr().As4()
for { for {
@@ -73,29 +80,25 @@ func runMCReaderInner(vpnNet netip.Prefix, selfVPNIP netip.Addr, ch chan<- Multi
if err != nil { if err != nil {
return fmt.Errorf("read: %w", err) return fmt.Errorf("read: %w", err)
} }
if n != mcBeaconLen { if n != mcSignedBeaconLen {
continue continue
} }
// Peek at VPN IP byte (first byte of payload after 64-byte sig prefix)
// to skip our own beacon before verifying.
octets := netAddr octets := netAddr
octets[3] = buf[0] octets[3] = buf[sign.Overhead]
vpnIP := netip.AddrFrom4(octets) vpnIP := netip.AddrFrom4(octets)
if vpnIP == selfVPNIP { if vpnIP == selfVPNIP {
continue continue
} }
pubKey, err := wgtypes.NewKey(buf[1:33]) signed := make([]byte, mcSignedBeaconLen)
if err != nil { copy(signed, buf[:mcSignedBeaconLen])
continue
}
wgPort := binary.BigEndian.Uint16(buf[33:35])
endpoint := netip.AddrPortFrom(src.Addr().Unmap(), wgPort)
ch <- MulticastEvent{ ch <- MulticastEvent{
pubKey: pubKey, signed: signed,
vpnIP: vpnIP, src: src.Addr().Unmap(),
endpoint: endpoint,
} }
} }
} }

View File

@@ -58,7 +58,7 @@ func New(
go cc.run(pingCh) go cc.run(pingCh)
go poller.Run() go poller.Run()
go RunMCWriter(state.VPNIP, state.PrivKey.PublicKey(), state.WGPort) go RunMCWriter(state.VPNIP, state.PrivKey.PublicKey(), state.WGPort, &state.SignKey)
if !state.IsPublic { if !state.IsPublic {
go RunMCReader(state.VPNNet, state.VPNIP, multicastCh) go RunMCReader(state.VPNNet, state.VPNIP, multicastCh)
} }

View File

@@ -15,14 +15,15 @@ func (a *App) onAddPeer(p HubPeer) {
a.onRemovePeer(p.PubKey) a.onRemovePeer(p.PubKey)
peer := &Peer{ peer := &Peer{
wgPeer: wgtypes.Peer{PublicKey: p.PubKey}, wgPeer: wgtypes.Peer{PublicKey: p.PubKey},
VPNIP: p.VPNIP, VPNIP: p.VPNIP,
IsRelay: p.IsRelay, IsRelay: p.IsRelay,
IsPublic: p.IsPublic, IsPublic: p.IsPublic,
Endpoint4: p.EndpointV4, Endpoint4: p.EndpointV4,
Endpoint6: p.EndpointV6, Endpoint6: p.EndpointV6,
RTT: time.Duration(math.MaxInt64) * time.Nanosecond, RTT: time.Duration(math.MaxInt64) * time.Nanosecond,
Role: roleFor(a.isPublic, a.vpnIP, p), Role: roleFor(a.isPublic, a.vpnIP, p),
SignPubKey: p.SignPubKey,
} }
endpoint := peer.PreferredEndpoint() endpoint := peer.PreferredEndpoint()

View File

@@ -1,13 +1,29 @@
package peer package peer
import "net/netip" import (
"encoding/binary"
"net/netip"
"golang.org/x/crypto/nacl/sign"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
func (a *App) onMulticastDiscovery(e MulticastEvent) { func (a *App) onMulticastDiscovery(e MulticastEvent) {
if a.isPublic { if a.isPublic {
return return
} }
peer, ok := a.peersByKey[e.pubKey] // Peek at the VPN IP byte to find the sender peer before verifying.
// nacl/sign prepends a 64-byte signature, so payload starts at offset sign.Overhead.
if len(e.signed) != mcSignedBeaconLen {
return
}
netAddr := a.vpnNet.Addr().As4()
octets := netAddr
octets[3] = e.signed[sign.Overhead]
vpnIP := netip.AddrFrom4(octets)
peer, ok := a.peersByIP[vpnIP]
if !ok { if !ok {
return return
} }
@@ -16,11 +32,28 @@ func (a *App) onMulticastDiscovery(e MulticastEvent) {
return return
} }
payload, ok := sign.Open(nil, e.signed, &peer.SignPubKey)
if !ok {
return
}
// payload: [1 VPN IP byte][32 WG pubkey][2 WG port]
wgPubKey, err := wgtypes.NewKey(payload[1:33])
if err != nil || wgPubKey != peer.PubKey() {
return
}
wgPort := binary.BigEndian.Uint16(payload[33:35])
endpoint := netip.AddrPortFrom(e.src, wgPort)
if !endpoint.IsValid() {
return
}
var v4, v6 netip.AddrPort var v4, v6 netip.AddrPort
if e.endpoint.Addr().Is4() { if e.src.Is4() {
v4 = e.endpoint v4 = endpoint
} else { } else {
v6 = e.endpoint v6 = endpoint
} }
a.addProbe(peer, v4, v6) a.addProbe(peer, v4, v6)

View File

@@ -19,14 +19,15 @@ const (
) )
type Peer struct { type Peer struct {
wgPeer wgtypes.Peer wgPeer wgtypes.Peer
VPNIP netip.Addr // VPN IP address. VPNIP netip.Addr // VPN IP address.
IsRelay bool // Peer is a relay. IsRelay bool // Peer is a relay.
IsPublic bool // Peer has a public IP. IsPublic bool // Peer has a public IP.
Endpoint4 netip.AddrPort // Reported IPv4 endpoint. Endpoint4 netip.AddrPort // Reported IPv4 endpoint.
Endpoint6 netip.AddrPort // Reported IPv6 endpoint. Endpoint6 netip.AddrPort // Reported IPv6 endpoint.
RTT time.Duration // Round-trip time. RTT time.Duration // Round-trip time.
Role control.Role // Client initiates pings; server responds. Role control.Role // Client initiates pings; server responds.
SignPubKey [32]byte // nacl/sign public key for verifying multicast beacons.
} }
// PubKey is the wireguard public key. // PubKey is the wireguard public key.

View File

@@ -173,9 +173,12 @@ func (d *Device) RemovePeer(pubKey wgtypes.Key) error {
}) })
} }
// EnableForwarding enables IPv4 forwarding on the interface, required for // EnableForwarding enables IPv4 forwarding globally and on the interface,
// relay peers that forward traffic between VPN peers. // required for relay peers that forward traffic between VPN peers.
func (d *Device) EnableForwarding() error { func (d *Device) EnableForwarding() error {
if err := os.WriteFile("/proc/sys/net/ipv4/ip_forward", []byte("1\n"), 0644); err != nil {
return err
}
path := fmt.Sprintf("/proc/sys/net/ipv4/conf/%s/forwarding", d.name) path := fmt.Sprintf("/proc/sys/net/ipv4/conf/%s/forwarding", d.name)
return os.WriteFile(path, []byte("1\n"), 0644) return os.WriteFile(path, []byte("1\n"), 0644)
} }