WIP: cleanup. Local peer discovery working.

This commit is contained in:
jdl
2024-12-30 15:38:08 +01:00
parent 8407fd5b48
commit bbf5202d30
49 changed files with 273 additions and 1632 deletions

View File

@@ -13,21 +13,17 @@ func addrDiscoveryServer() {
)
for {
pkt := <-discoveryPackets
msg := <-discoveryMessages
p := msg.Packet
p, ok := pkt.Payload.(addrDiscoveryPacket)
if !ok {
continue
}
route := routingTable[pkt.SrcIP].Load()
route := routingTable[msg.SrcIP].Load()
if route == nil || !route.RemoteAddr.IsValid() {
continue
}
_sendControlPacket(addrDiscoveryPacket{
TraceID: p.TraceID,
ToAddr: pkt.SrcAddr,
ToAddr: msg.SrcAddr,
}, *route, buf1, buf2)
}
}
@@ -46,9 +42,9 @@ func addrDiscoveryClient() {
for {
select {
case pkt := <-discoveryPackets:
p, ok := pkt.Payload.(addrDiscoveryPacket)
if !ok || p.TraceID != addrPacket.TraceID || !p.ToAddr.IsValid() || p.ToAddr == lAddr {
case msg := <-discoveryMessages:
p := msg.Packet
if p.TraceID != addrPacket.TraceID || !p.ToAddr.IsValid() || p.ToAddr == lAddr {
continue
}

View File

@@ -1,5 +0,0 @@
#!/bin/bash
go build
sudo setcap cap_net_admin+iep ./client
./client 144.76.78.93

View File

@@ -1,15 +0,0 @@
package main
import (
"log"
"os"
"vppn/node"
)
func main() {
if len(os.Args) != 2 {
log.Fatalf("Usage: %s <addr:port>", os.Args[0])
}
n := node.NewTmpNodeClient(os.Args[1])
n.RunClient()
}

View File

@@ -1,7 +0,0 @@
#!/bin/bash
go build
ssh kevin "killall server"
scp server kevin:/home/jdl/tmp/
ssh root@kevin "sudo setcap cap_net_admin+iep /home/jdl/tmp/server"
ssh kevin "/home/jdl/tmp/server"

View File

@@ -1,8 +0,0 @@
package main
import "vppn/node"
func main() {
n := node.NewTmpNodeServer()
n.RunServer()
}

View File

@@ -1,10 +1,10 @@
package node
import (
"net"
"net/netip"
"sync/atomic"
"time"
"vppn/m"
)
const (
@@ -13,6 +13,12 @@ const (
if_queue_len = 2048
controlCipherOverhead = 16
dataCipherOverhead = 16
signOverhead = 64
)
var (
multicastIP = netip.AddrFrom4([4]byte{224, 0, 0, 157})
multicastAddr = net.UDPAddrFromAddrPort(netip.AddrPortFrom(multicastIP, 4560))
)
type peerRoute struct {
@@ -56,18 +62,9 @@ var (
return
}()
// Channels for incoming control packets.
controlPackets [256]chan controlPacket = func() (out [256]chan controlPacket) {
messages [256]chan any = func() (out [256]chan any) {
for i := range out {
out[i] = make(chan controlPacket, 256)
}
return
}()
// Channels for incoming peer updates from the hub.
peerUpdates [256]chan *m.Peer = func() (out [256]chan *m.Peer) {
for i := range out {
out[i] = make(chan *m.Peer)
out[i] = make(chan any, 256)
}
return
}()
@@ -81,8 +78,10 @@ var (
return
}()
// Managed by the addrDiscovery* functions.
discoveryMessages = make(chan controlMsg[addrDiscoveryPacket], 256)
// Managed by the relayManager.
discoveryPackets = make(chan controlPacket, 256)
localAddr = &atomic.Pointer[netip.AddrPort]{}
relayIP = &atomic.Pointer[byte]{}
localAddr = &atomic.Pointer[netip.AddrPort]{}
relayIP = &atomic.Pointer[byte]{}
)

View File

@@ -86,7 +86,7 @@ func (hp *hubPoller) applyNetworkState(state m.NetworkState) {
for i, peer := range state.Peers {
if i != int(localIP) {
if peer != nil && peer.Version != hp.versions[i] {
peerUpdates[i] <- state.Peers[i]
messages[i] <- peerUpdateMsg{Peer: state.Peers[i]}
hp.versions[i] = peer.Version
}
}

View File

@@ -1,75 +0,0 @@
package node
import (
"encoding/binary"
"log"
"net"
"net/netip"
"time"
)
func localBroadcaster() {
var (
buf1 = make([]byte, bufferSize)
buf2 = make([]byte, bufferSize)
)
time.Sleep(4 * time.Second)
doBroadcast(buf1, buf2)
for range time.Tick(32 * time.Second) {
doBroadcast(buf1, buf2)
}
}
func doBroadcast(buf1, buf2 []byte) {
ifaces, err := net.Interfaces()
if err != nil {
log.Printf("Failed to list interfaces: %v", err)
return
}
for _, iface := range ifaces {
if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagRunning == 0 {
continue
}
if iface.Flags&net.FlagPointToPoint != 0 {
continue
}
if iface.Flags&net.FlagBroadcast == 0 {
continue
}
addrs, err := iface.Addrs()
if err != nil {
log.Printf("Failed to get interface addresses: %v", err)
continue
}
for _, addr := range addrs {
ipNet, ok := addr.(*net.IPNet)
if !ok {
continue
}
ip4 := ipNet.IP.To4()
if ip4 == nil {
continue
}
ip, ok := lastAddr(ipNet)
if !ok {
log.Printf("Failed to find broadcast address: %v", ipNet)
continue
}
log.Printf("Broadcasting on address: %v", ip)
//addr := netip.AddrPortFrom(ip, 456)
}
}
}
// works when the n is a prefix, otherwise...
func lastAddr(n *net.IPNet) (netip.Addr, bool) {
ip := make(net.IP, len(n.IP.To4()))
binary.BigEndian.PutUint32(ip, binary.BigEndian.Uint32(n.IP.To4())|
^binary.BigEndian.Uint32(net.IP(n.Mask).To4()))
return netip.AddrFromSlice(ip)
}

View File

@@ -3,18 +3,11 @@ package node
import (
"log"
"net"
"net/netip"
"time"
"golang.org/x/crypto/nacl/sign"
)
var (
signOverhead = 64
multicastIP = netip.AddrFrom4([4]byte{224, 0, 0, 157})
multicastAddr = net.UDPAddrFromAddrPort(netip.AddrPortFrom(multicastIP, 4560))
)
func localDiscovery() {
conn, err := net.ListenMulticastUDP("udp", nil, multicastAddr)
if err != nil {
@@ -32,8 +25,9 @@ func sendLocalDiscovery(conn *net.UDPConn) {
buf2 = make([]byte, bufferSize)
)
for range time.Tick(16 * time.Second) {
for range time.Tick(32 * time.Second) {
signed := buildLocalDiscoveryPacket(buf1, buf2)
log.Printf("Sending discovery packet...")
if _, err := conn.WriteToUDP(signed, multicastAddr); err != nil {
log.Printf("Failed to write multicast UDP packet: %v", err)
}
@@ -51,21 +45,24 @@ func recvLocalDiscovery(conn *net.UDPConn) {
if err != nil {
log.Fatalf("Failed to read from UDP port (multicast): %v", err)
}
log.Printf("Got local discovery packet...")
raw = raw[:n]
h, ok := openLocalDiscoveryPacket(raw, buf)
if !ok {
log.Printf("Failed to open discovery packet?")
continue
}
pkt := controlPacket{
msg := controlMsg[localDiscoveryPacket]{
SrcIP: h.SourceIP,
SrcAddr: remoteAddr,
Payload: localDiscoveryPacket{},
Packet: localDiscoveryPacket{},
}
log.Printf("Got local discovery packet from %d/%v...", h.SourceIP, remoteAddr)
select {
case controlPackets[h.SourceIP] <- pkt:
case messages[h.SourceIP] <- msg:
default:
}
}
@@ -92,6 +89,7 @@ func openLocalDiscoveryPacket(raw, buf []byte) (h header, ok bool) {
h.Parse(raw[signOverhead:])
route := routingTable[h.SourceIP].Load()
if route == nil || route.PubSignKey == nil {
log.Printf("Missing signing key")
ok = false
return
}

View File

@@ -11,6 +11,7 @@ import (
"net/netip"
"os"
"runtime/debug"
"time"
"vppn/m"
)
@@ -26,13 +27,11 @@ func Main() {
var (
initURL string
listenIP string
port int
)
flag.StringVar(&netName, "name", "", "[REQUIRED] The network name.")
flag.StringVar(&initURL, "init-url", "", "Initializes peer from the hub URL.")
flag.StringVar(&listenIP, "listen-ip", "", "IP address to listen on.")
flag.IntVar(&port, "port", 0, "Port to listen on.")
flag.Parse()
if netName == "" {
@@ -45,7 +44,7 @@ func Main() {
return
}
main(listenIP, uint16(port))
main(listenIP)
}
func mainInit(initURL string) {
@@ -74,20 +73,18 @@ func mainInit(initURL string) {
// ----------------------------------------------------------------------------
func main(listenIP string, port uint16) {
func main(listenIP string) {
config, err := loadPeerConfig(netName)
if err != nil {
log.Fatalf("Failed to load configuration: %v", err)
}
port = determinePort(config.Port, port)
iface, err := openInterface(config.Network, config.PeerIP, netName)
if err != nil {
log.Fatalf("Failed to open interface: %v", err)
}
myAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", listenIP, port))
myAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", listenIP, config.Port))
if err != nil {
log.Fatalf("Failed to resolve UDP address: %v", err)
}
@@ -129,6 +126,17 @@ func main(listenIP string, port uint16) {
go localDiscovery()
}
go func() {
for range time.Tick(pingInterval) {
for i := range messages {
select {
case messages[i] <- pingTimerMsg{}:
default:
}
}
}
}()
go newHubPoller(config).Run()
go readFromConn(conn)
readFromIFace(iface)
@@ -136,18 +144,6 @@ func main(listenIP string, port uint16) {
// ----------------------------------------------------------------------------
func determinePort(confPort, portFromCommandLine uint16) uint16 {
if portFromCommandLine != 0 {
return portFromCommandLine
}
if confPort != 0 {
return confPort
}
return 456
}
// ----------------------------------------------------------------------------
func readFromConn(conn *net.UDPConn) {
defer panicHandler()
@@ -218,31 +214,21 @@ func handleControlPacket(addr netip.AddrPort, h header, data, decBuf []byte) {
return
}
pkt := controlPacket{
SrcIP: h.SourceIP,
SrcAddr: addr,
}
if err := pkt.ParsePayload(out); err != nil {
msg, err := parseControlMsg(h.SourceIP, addr, out)
if err != nil {
log.Printf("Failed to parse control packet: %v", err)
return
}
switch pkt.Payload.(type) {
case addrDiscoveryPacket:
select {
case discoveryPackets <- pkt:
default:
log.Printf("Dropping discovery packet.")
}
if dm, ok := msg.(controlMsg[addrDiscoveryPacket]); ok {
discoveryMessages <- dm
return
}
select {
case messages[h.SourceIP] <- msg:
default:
select {
case controlPackets[h.SourceIP] <- pkt:
default:
log.Printf("Dropping control packet.")
}
log.Printf("Dropping control packet.")
}
}

View File

@@ -1 +1,66 @@
package node
import (
"net/netip"
"vppn/m"
)
// ----------------------------------------------------------------------------
type controlMsg[T any] struct {
SrcIP byte
SrcAddr netip.AddrPort
Packet T
}
func parseControlMsg(srcIP byte, srcAddr netip.AddrPort, buf []byte) (any, error) {
switch buf[0] {
case packetTypeSyn:
packet, err := parseSynPacket(buf)
return controlMsg[synPacket]{
SrcIP: srcIP,
SrcAddr: srcAddr,
Packet: packet,
}, err
case packetTypeSynAck:
packet, err := parseSynAckPacket(buf)
return controlMsg[synAckPacket]{
SrcIP: srcIP,
SrcAddr: srcAddr,
Packet: packet,
}, err
case packetTypeProbe:
packet, err := parseProbePacket(buf)
return controlMsg[probePacket]{
SrcIP: srcIP,
SrcAddr: srcAddr,
Packet: packet,
}, err
case packetTypeAddrDiscovery:
packet, err := parseAddrDiscoveryPacket(buf)
return controlMsg[addrDiscoveryPacket]{
SrcIP: srcIP,
SrcAddr: srcAddr,
Packet: packet,
}, err
default:
return nil, errUnknownPacketType
}
}
// ----------------------------------------------------------------------------
type peerUpdateMsg struct {
Peer *m.Peer
}
// ----------------------------------------------------------------------------
type pingTimerMsg struct{}
// ----------------------------------------------------------------------------

View File

@@ -20,30 +20,6 @@ const (
// ----------------------------------------------------------------------------
type controlPacket struct {
SrcIP byte
SrcAddr netip.AddrPort
Payload any
}
func (p *controlPacket) ParsePayload(buf []byte) (err error) {
switch buf[0] {
case packetTypeSyn:
p.Payload, err = parseSynPacket(buf)
case packetTypeSynAck:
p.Payload, err = parseSynAckPacket(buf)
case packetTypeProbe:
p.Payload, err = parseProbePacket(buf)
case packetTypeAddrDiscovery:
p.Payload, err = parseAddrDiscoveryPacket(buf)
default:
return errUnknownPacketType
}
return err
}
// ----------------------------------------------------------------------------
type synPacket struct {
TraceID uint64 // TraceID to match response w/ request.
SharedKey [32]byte // Our shared key.

View File

@@ -29,8 +29,7 @@ type peerSupervisor struct {
remotePub bool
// Incoming events.
peerUpdates chan *m.Peer
controlPackets chan controlPacket
messages chan any
// Buffers for sending control packets.
buf1 []byte
@@ -39,12 +38,11 @@ type peerSupervisor struct {
func newPeerSupervisor(i int) *peerSupervisor {
return &peerSupervisor{
published: routingTable[i],
remoteIP: byte(i),
peerUpdates: peerUpdates[i],
controlPackets: controlPackets[i],
buf1: make([]byte, bufferSize),
buf2: make([]byte, bufferSize),
published: routingTable[i],
remoteIP: byte(i),
messages: messages[i],
buf1: make([]byte, bufferSize),
buf2: make([]byte, bufferSize),
}
}
@@ -95,7 +93,12 @@ func (s *peerSupervisor) publish() {
// ----------------------------------------------------------------------------
func (s *peerSupervisor) noPeer() stateFunc {
return s.peerUpdate(<-s.peerUpdates)
for {
rawMsg := <-s.messages
if msg, ok := rawMsg.(peerUpdateMsg); ok {
return s.peerUpdate(msg.Peer)
}
}
}
// ----------------------------------------------------------------------------
@@ -149,75 +152,73 @@ func (s *peerSupervisor) server() stateFunc {
logf("DOWN")
var (
syn synPacket
timeoutTimer = time.NewTimer(timeoutInterval)
syn synPacket
lastSeen = time.Now()
)
// Timer will be restarted once we have established a connection.
timeoutTimer.Stop()
for {
select {
case peer := <-s.peerUpdates:
return s.peerUpdate(peer)
rawMsg := <-s.messages
switch msg := rawMsg.(type) {
case pkt := <-s.controlPackets:
switch p := pkt.Payload.(type) {
case peerUpdateMsg:
return s.peerUpdate(msg.Peer)
case synPacket:
timeoutTimer.Reset(timeoutInterval)
case controlMsg[synPacket]:
p := msg.Packet
lastSeen = time.Now()
// Before we can respond to this packet, we need to make sure the
// route is setup properly.
//
// The client will update the syn's TraceID whenever there's a change.
// The server will follow the client's request.
if p.TraceID != syn.TraceID || !s.staged.Up {
if p.Direct {
logf("UP - Direct")
} else {
logf("UP - Relayed")
}
syn = p
s.staged.Up = true
s.staged.Direct = syn.Direct
s.staged.DataCipher = newDataCipherFromKey(syn.SharedKey)
s.staged.RemoteAddr = pkt.SrcAddr
s.publish()
}
// We should always respond.
ack := synAckPacket{
TraceID: syn.TraceID,
FromAddr: getLocalAddr(),
}
s.sendControlPacket(ack)
if s.staged.Direct {
continue
}
if !syn.FromAddr.IsValid() {
continue
}
probe := probePacket{TraceID: newTraceID()}
s.sendControlPacketTo(probe, syn.FromAddr)
case probePacket:
if pkt.SrcAddr.IsValid() {
s.sendControlPacketTo(probePacket{TraceID: p.TraceID}, pkt.SrcAddr)
// Before we can respond to this packet, we need to make sure the
// route is setup properly.
//
// The client will update the syn's TraceID whenever there's a change.
// The server will follow the client's request.
if p.TraceID != syn.TraceID || !s.staged.Up {
if p.Direct {
logf("UP - Direct")
} else {
logf("Invalid probe address")
logf("UP - Relayed")
}
syn = p
s.staged.Up = true
s.staged.Direct = syn.Direct
s.staged.DataCipher = newDataCipherFromKey(syn.SharedKey)
s.staged.RemoteAddr = msg.SrcAddr
s.publish()
}
case <-timeoutTimer.C:
logf("Connection timeout")
s.staged.Up = false
s.publish()
// We should always respond.
ack := synAckPacket{
TraceID: syn.TraceID,
FromAddr: getLocalAddr(),
}
s.sendControlPacket(ack)
if s.staged.Direct {
continue
}
if !syn.FromAddr.IsValid() {
continue
}
probe := probePacket{TraceID: newTraceID()}
s.sendControlPacketTo(probe, syn.FromAddr)
case controlMsg[probePacket]:
if !msg.SrcAddr.IsValid() {
logf("Invalid probe address")
continue
}
s.sendControlPacketTo(probePacket{TraceID: msg.Packet.TraceID}, msg.SrcAddr)
case pingTimerMsg:
if time.Since(lastSeen) > timeoutInterval {
logf("Connection timeout")
s.staged.Up = false
s.publish()
}
}
}
}
@@ -237,91 +238,106 @@ func (s *peerSupervisor) client() stateFunc {
FromAddr: getLocalAddr(),
}
ack synAckPacket
lastSeen = time.Now()
ack synAckPacket
probe probePacket
probeAddr netip.AddrPort
remoteAddr netip.AddrPort
localProbe probePacket
localProbeAddr netip.AddrPort
timeoutTimer = time.NewTimer(timeoutInterval)
pingTimer = time.NewTimer(pingInterval)
lastLocalAddr netip.AddrPort
)
defer timeoutTimer.Stop()
defer pingTimer.Stop()
s.sendControlPacket(syn)
for {
select {
rawMsg := <-s.messages
switch msg := rawMsg.(type) {
case peer := <-s.peerUpdates:
return s.peerUpdate(peer)
case peerUpdateMsg:
return s.peerUpdate(msg.Peer)
case pkt := <-s.controlPackets:
switch p := pkt.Payload.(type) {
case controlMsg[synAckPacket]:
p := msg.Packet
case synAckPacket:
if p.TraceID != syn.TraceID {
continue // Hmm...
}
if p.TraceID != syn.TraceID {
continue // Hmm...
}
ack = p
timeoutTimer.Reset(timeoutInterval)
lastSeen = time.Now()
ack = msg.Packet
if !s.staged.Up {
if s.staged.Direct {
logf("UP - Direct")
} else {
logf("UP - Relayed")
}
s.staged.Up = true
s.publish()
}
case probePacket:
if !s.staged.Up {
if s.staged.Direct {
continue
logf("UP - Direct")
} else {
logf("UP - Relayed")
}
if p.TraceID != probe.TraceID {
continue
}
// Upgrade connection.
logf("UP - Direct")
s.staged.Direct = true
s.staged.RemoteAddr = probeAddr
s.staged.Up = true
s.publish()
syn.TraceID = newTraceID()
syn.Direct = true
syn.FromAddr = getLocalAddr()
s.sendControlPacket(syn)
}
case <-pingTimer.C:
// Send syn.
syn.FromAddr = getLocalAddr()
if syn.FromAddr != remoteAddr {
syn.TraceID = newTraceID()
remoteAddr = syn.FromAddr
}
s.sendControlPacket(syn)
pingTimer.Reset(pingInterval)
case controlMsg[probePacket]:
if s.staged.Direct {
continue
}
// TODO: Check if we have local address.
// TODO: Send local probe
p := msg.Packet
if p.TraceID != localProbe.TraceID && p.TraceID != probe.TraceID {
continue
}
// Upgrade connection.
s.staged.Direct = true
if p.TraceID == localProbe.TraceID {
logf("UP - Local")
s.staged.RemoteAddr = localProbeAddr
} else {
logf("UP - Direct")
s.staged.RemoteAddr = probeAddr
}
s.publish()
syn.TraceID = newTraceID()
syn.Direct = true
syn.FromAddr = getLocalAddr()
s.sendControlPacket(syn)
case controlMsg[localDiscoveryPacket]:
if s.staged.Direct {
continue
}
// Send probe.
//
// The source port will be the multicast port, so we'll have to
// construct the correct address using the peer's listed port.
localProbe = probePacket{TraceID: newTraceID()}
localProbeAddr = netip.AddrPortFrom(msg.SrcAddr.Addr(), s.peer.Port)
s.sendControlPacketTo(localProbe, localProbeAddr)
case pingTimerMsg:
if time.Since(lastSeen) > timeoutInterval {
logf("Connection timeout")
return s.peerUpdate(s.peer)
}
syn.FromAddr = getLocalAddr()
if syn.FromAddr != lastLocalAddr {
syn.TraceID = newTraceID()
lastLocalAddr = syn.FromAddr
}
s.sendControlPacket(syn)
if s.staged.Direct {
continue
}
if !ack.FromAddr.IsValid() {
continue
@@ -331,10 +347,6 @@ func (s *peerSupervisor) client() stateFunc {
probeAddr = ack.FromAddr
s.sendControlPacketTo(probe, ack.FromAddr)
case <-timeoutTimer.C:
logf("Connection timeout")
return s.peerUpdate(s.peer)
}
}
}

View File

@@ -1 +0,0 @@
package node