173 lines
3.3 KiB
Go
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 supervisor 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 newSupervisor(
|
|
writeToUDPAddrPort func([]byte, netip.AddrPort) (int, error),
|
|
rt *atomic.Pointer[routingTable],
|
|
privKey []byte,
|
|
) *supervisor {
|
|
|
|
routes := rt.Load()
|
|
|
|
s := &supervisor{
|
|
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 *supervisor) Start() {
|
|
for i := range s.peers {
|
|
go s.peers[i].Run()
|
|
}
|
|
}
|
|
|
|
func (s *supervisor) HandleControlMsg(destIP byte, msg any) {
|
|
s.peers[destIP].HandleControlMsg(msg)
|
|
}
|
|
|
|
func (s *supervisor) 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 *supervisor) publish(rp remotePeer) {
|
|
s.lock.Lock()
|
|
defer s.lock.Unlock()
|
|
|
|
s.staged.Peers[rp.IP] = rp
|
|
s.ensureRelay()
|
|
copy := s.staged
|
|
s.shared.Store(©)
|
|
}
|
|
|
|
func (s *supervisor) 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)
|
|
}
|
|
}
|
|
}
|