vppn/peer/peersuper.go
2025-02-19 14:13:25 +01:00

173 lines
3.3 KiB
Go

package peer
import (
"log"
"math/rand"
"net/netip"
"sync"
"sync/atomic"
"time"
"git.crumpington.com/lib/go/ratelimiter"
)
type Super struct {
writeToUDPAddrPort func([]byte, netip.AddrPort) (int, error)
staged routingTable
shared *atomic.Pointer[routingTable]
peers [256]*PeerSuper
lock sync.Mutex
buf1 []byte
buf2 []byte
}
func NewSuper(
writeToUDPAddrPort func([]byte, netip.AddrPort) (int, error),
rt *atomic.Pointer[routingTable],
privKey []byte,
) *Super {
routes := rt.Load()
s := &Super{
writeToUDPAddrPort: writeToUDPAddrPort,
staged: *routes,
shared: rt,
buf1: newBuf(),
buf2: newBuf(),
}
pubAddrs := newPubAddrStore(routes.LocalAddr)
for i := range s.peers {
state := &pState{
publish: s.publish,
sendControlPacket: s.send,
localIP: routes.LocalIP,
remoteIP: byte(i),
privKey: privKey,
localAddr: routes.LocalAddr,
pubAddrs: pubAddrs,
staged: routes.Peers[i],
limiter: ratelimiter.New(ratelimiter.Config{
FillPeriod: 20 * time.Millisecond,
MaxWaitCount: 1,
}),
}
s.peers[i] = NewPeerSuper(state)
}
return s
}
func (s *Super) Start() {
for i := range s.peers {
go s.peers[i].Run()
}
}
func (s *Super) HandleControlMsg(destIP byte, msg any) {
s.peers[destIP].HandleControlMsg(msg)
}
func (s *Super) send(peer remotePeer, pkt Marshaller) {
s.lock.Lock()
defer s.lock.Unlock()
enc := peer.EncryptControlPacket(pkt, s.buf1, s.buf2)
if peer.Direct {
s.writeToUDPAddrPort(enc, peer.DirectAddr)
return
}
relay, ok := s.staged.GetRelay()
if !ok {
return
}
enc = relay.EncryptDataPacket(peer.IP, enc, s.buf1)
s.writeToUDPAddrPort(enc, relay.DirectAddr)
}
func (s *Super) publish(rp remotePeer) {
s.lock.Lock()
defer s.lock.Unlock()
s.staged.Peers[rp.IP] = rp
s.ensureRelay()
copy := s.staged
s.shared.Store(&copy)
}
func (s *Super) ensureRelay() {
if _, ok := s.staged.GetRelay(); ok {
return
}
// TODO: Random selection?
for _, peer := range s.staged.Peers {
if peer.Up && peer.Direct && peer.Relay {
s.staged.RelayIP = peer.IP
return
}
}
}
// ----------------------------------------------------------------------------
type PeerSuper struct {
messages chan any
state peerState
}
func NewPeerSuper(state *pState) *PeerSuper {
return &PeerSuper{
messages: make(chan any, 8),
state: state.OnPeerUpdate(nil),
}
}
func (s *PeerSuper) HandleControlMsg(msg any) {
select {
case s.messages <- msg:
default:
}
}
func (s *PeerSuper) Run() {
go func() {
// Randomize ping timers.
time.Sleep(time.Duration(rand.Intn(4000)) * time.Millisecond)
for range time.Tick(pingInterval) {
s.messages <- pingTimerMsg{}
}
}()
for rawMsg := range s.messages {
switch msg := rawMsg.(type) {
case peerUpdateMsg:
s.state = s.state.OnPeerUpdate(msg.Peer)
case controlMsg[packetSyn]:
s.state = s.state.OnSyn(msg)
case controlMsg[packetAck]:
s.state.OnAck(msg)
case controlMsg[packetProbe]:
s.state = s.state.OnProbe(msg)
case controlMsg[packetLocalDiscovery]:
s.state.OnLocalDiscovery(msg)
case pingTimerMsg:
s.state = s.state.OnPingTimer()
default:
log.Printf("WARNING: unknown message type: %+v", msg)
}
}
}