WIP
This commit is contained in:
@@ -13,10 +13,19 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
mcBeaconLen = 35 // 1 VPN IP byte + 32 WG pubkey + 2 WG listen port
|
||||
// Beacon payload layout (after the 64-byte nacl/sign prefix):
|
||||
// [0] final octet of the sender's VPN IP
|
||||
// [1:33] WG public key
|
||||
// [33:35] WG listen port (big-endian uint16)
|
||||
// [35:43] send time, Unix seconds (big-endian int64) — freshness/replay gate
|
||||
mcBeaconLen = 43
|
||||
mcSignedBeaconLen = sign.Overhead + mcBeaconLen // 64-byte nacl/sign prefix + payload
|
||||
mcBroadcastInterval = 32 * time.Second
|
||||
mcErrorRetryInterval = 16 * time.Second
|
||||
// mcBeaconMaxAge bounds how far a beacon's timestamp may be from now (in
|
||||
// either direction) before the reader drops it: it tolerates modest clock
|
||||
// skew between roughly-synchronized (NTP) hosts and caps the replay window.
|
||||
mcBeaconMaxAge = 60 * time.Second
|
||||
)
|
||||
|
||||
var mcAddr = net.UDPAddrFromAddrPort(netip.AddrPortFrom(
|
||||
@@ -40,26 +49,33 @@ func runMCWriterInner(selfVPNIP netip.Addr, pubKey wgtypes.Key, wgPort uint16, s
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
payload := buildBeacon(selfVPNIP, pubKey, wgPort)
|
||||
// Re-sign on each send so the timestamp is fresh; a stale timestamp would be
|
||||
// dropped by receivers' freshness gate.
|
||||
send := func() error {
|
||||
payload := buildBeacon(selfVPNIP, pubKey, wgPort, time.Now().Unix())
|
||||
signed := sign.Sign(nil, payload, signKey)
|
||||
_, err := conn.WriteToUDP(signed, mcAddr)
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := conn.WriteToUDP(signed, mcAddr); err != nil {
|
||||
if err := send(); err != nil {
|
||||
log.Printf("[MCWriter] write: %v", err)
|
||||
}
|
||||
|
||||
for range time.Tick(mcBroadcastInterval) {
|
||||
if _, err := conn.WriteToUDP(signed, mcAddr); err != nil {
|
||||
if err := send(); err != nil {
|
||||
log.Printf("[MCWriter] write: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func buildBeacon(selfVPNIP netip.Addr, pubKey wgtypes.Key, wgPort uint16) []byte {
|
||||
func buildBeacon(selfVPNIP netip.Addr, pubKey wgtypes.Key, wgPort uint16, ts int64) []byte {
|
||||
beacon := make([]byte, mcBeaconLen)
|
||||
beacon[0] = selfVPNIP.As4()[3]
|
||||
copy(beacon[1:33], pubKey[:])
|
||||
binary.BigEndian.PutUint16(beacon[33:35], wgPort)
|
||||
binary.BigEndian.PutUint64(beacon[35:43], uint64(ts))
|
||||
return beacon
|
||||
}
|
||||
|
||||
@@ -97,8 +113,11 @@ func runMCReaderInner(vpnNet netip.Prefix, selfVPNIP netip.Addr, ch chan<- Multi
|
||||
continue
|
||||
}
|
||||
|
||||
// Peek at VPN IP byte (first byte of payload after 64-byte sig prefix)
|
||||
// to skip our own beacon before verifying.
|
||||
// Cheap pre-filters on the unverified payload, before the costly
|
||||
// signature check in the event loop: skip our own beacon, and drop
|
||||
// stale ones (replay/freshness gate). The timestamp is authenticated by
|
||||
// sign.Open later, so a forged-fresh timestamp still fails there — this
|
||||
// only spares us verifying old replays.
|
||||
octets := netAddr
|
||||
octets[3] = buf[sign.Overhead]
|
||||
vpnIP := netip.AddrFrom4(octets)
|
||||
@@ -106,6 +125,10 @@ func runMCReaderInner(vpnNet netip.Prefix, selfVPNIP netip.Addr, ch chan<- Multi
|
||||
continue
|
||||
}
|
||||
|
||||
if age := beaconAge(buf, time.Now()); age > mcBeaconMaxAge || age < -mcBeaconMaxAge {
|
||||
continue
|
||||
}
|
||||
|
||||
signed := make([]byte, mcSignedBeaconLen)
|
||||
copy(signed, buf[:mcSignedBeaconLen])
|
||||
|
||||
|
||||
@@ -96,6 +96,7 @@ func (a *App) switchActiveRelay() {
|
||||
}
|
||||
|
||||
func preferredEndpoint(v4, v6 netip.AddrPort) netip.AddrPort {
|
||||
// We always prefer v4 since all peers can connect to IPv4 addresses.
|
||||
if v4.IsValid() {
|
||||
return v4
|
||||
}
|
||||
|
||||
@@ -37,7 +37,8 @@ func (a *App) onMulticastDiscovery(e MulticastEvent) {
|
||||
return
|
||||
}
|
||||
|
||||
// payload: [1 VPN IP byte][32 WG pubkey][2 WG port]
|
||||
// payload: [1 VPN IP byte][32 WG pubkey][2 WG port][8 timestamp]
|
||||
// (timestamp freshness is gated in the reader before this point).
|
||||
wgPubKey, err := wgtypes.NewKey(payload[1:33])
|
||||
if err != nil || wgPubKey != peer.PubKey() {
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user