wip: client/server working.
This commit is contained in:
parent
5b34b3311b
commit
c7d3fe1ed8
@ -5,6 +5,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"runtime/debug"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -22,6 +23,7 @@ func newConnWriter(conn *net.UDPConn) *connWriter {
|
|||||||
func (w *connWriter) WriteTo(packet []byte, addr netip.AddrPort) {
|
func (w *connWriter) WriteTo(packet []byte, addr netip.AddrPort) {
|
||||||
w.lock.Lock()
|
w.lock.Lock()
|
||||||
if _, err := w.conn.WriteToUDPAddrPort(packet, addr); err != nil {
|
if _, err := w.conn.WriteToUDPAddrPort(packet, addr); err != nil {
|
||||||
|
debug.PrintStack()
|
||||||
log.Fatalf("Failed to write to UDP port: %v", err)
|
log.Fatalf("Failed to write to UDP port: %v", err)
|
||||||
}
|
}
|
||||||
w.lock.Unlock()
|
w.lock.Unlock()
|
||||||
|
@ -10,7 +10,7 @@ const (
|
|||||||
controlHeaderSize = 24
|
controlHeaderSize = 24
|
||||||
dataStreamID = 1
|
dataStreamID = 1
|
||||||
dataHeaderSize = 12
|
dataHeaderSize = 12
|
||||||
forwardStreamID = 3
|
relayStreamID = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
type header struct {
|
type header struct {
|
||||||
|
10
node/main.go
10
node/main.go
@ -114,7 +114,6 @@ func main(netName, listenIP string, port uint16) {
|
|||||||
go newHubPoller(netName, conf, peers).Run()
|
go newHubPoller(netName, conf, peers).Run()
|
||||||
go readFromConn(conf.PeerIP, conn, peers)
|
go readFromConn(conf.PeerIP, conn, peers)
|
||||||
readFromIFace(iface, peers)
|
readFromIFace(iface, peers)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@ -157,12 +156,7 @@ func readFromConn(localIP byte, conn *net.UDPConn, peers remotePeers) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
h.Parse(data)
|
h.Parse(data)
|
||||||
|
peers[h.SourceIP].HandlePacket(remoteAddr, h, data)
|
||||||
if h.DestIP == localIP {
|
|
||||||
peers[h.SourceIP].HandlePacket(remoteAddr, h, data)
|
|
||||||
} else {
|
|
||||||
peers[h.DestIP].ForwardPacket(data)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,6 +177,6 @@ func readFromIFace(iface io.ReadWriteCloser, peers remotePeers) {
|
|||||||
log.Fatalf("Failed to read from interface: %v", err)
|
log.Fatalf("Failed to read from interface: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
peers[remoteIP].SendData(packet)
|
peers[remoteIP].HandleInterfacePacket(packet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,10 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
var errMalformedPacket = errors.New("malformed packet")
|
var (
|
||||||
|
errMalformedPacket = errors.New("malformed packet")
|
||||||
|
errUnknownPacketType = errors.New("unknown packet type")
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
packetTypePing = iota + 1
|
packetTypePing = iota + 1
|
||||||
@ -22,6 +25,18 @@ type controlPacket struct {
|
|||||||
Payload any
|
Payload any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *controlPacket) ParsePayload(buf []byte) (err error) {
|
||||||
|
switch buf[0] {
|
||||||
|
case packetTypePing:
|
||||||
|
p.Payload, err = parsePingPacket(buf)
|
||||||
|
case packetTypePong:
|
||||||
|
p.Payload, err = parsePongPacket(buf)
|
||||||
|
default:
|
||||||
|
return errUnknownPacketType
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
// A pingPacket is sent from a node acting as a client, to a node acting
|
// A pingPacket is sent from a node acting as a client, to a node acting
|
||||||
@ -32,9 +47,9 @@ type pingPacket struct {
|
|||||||
SharedKey [32]byte
|
SharedKey [32]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPingPacket(sharedKey []byte) (pp pingPacket) {
|
func newPingPacket(sharedKey [32]byte) (pp pingPacket) {
|
||||||
pp.SentAt = time.Now().UnixMilli()
|
pp.SentAt = time.Now().UnixMilli()
|
||||||
copy(pp.SharedKey[:], sharedKey)
|
copy(pp.SharedKey[:], sharedKey[:])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ func TestPacketPing(t *testing.T) {
|
|||||||
|
|
||||||
buf := make([]byte, bufferSize)
|
buf := make([]byte, bufferSize)
|
||||||
|
|
||||||
p := newPingPacket(sharedKey)
|
p := newPingPacket([32]byte(sharedKey))
|
||||||
out := p.Marshal(buf)
|
out := p.Marshal(buf)
|
||||||
|
|
||||||
p2, err := parsePingPacket(out)
|
p2, err := parsePingPacket(out)
|
||||||
|
214
node/peer-states.go
Normal file
214
node/peer-states.go
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
package node
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/netip"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
"vppn/m"
|
||||||
|
)
|
||||||
|
|
||||||
|
type peerState interface {
|
||||||
|
Name() string
|
||||||
|
OnPeerUpdate(*m.Peer) peerState
|
||||||
|
OnPing(netip.AddrPort, pingPacket) peerState
|
||||||
|
OnPong(netip.AddrPort, pongPacket) peerState
|
||||||
|
OnPingTimer() peerState
|
||||||
|
OnTimeoutTimer() peerState
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type stateBase struct {
|
||||||
|
// The purpose of this state machine is to manage this published data.
|
||||||
|
published *atomic.Pointer[peerData]
|
||||||
|
|
||||||
|
// The other remote peers.
|
||||||
|
peers *remotePeers
|
||||||
|
|
||||||
|
// Immutable data.
|
||||||
|
localIP byte
|
||||||
|
localPub bool
|
||||||
|
remoteIP byte
|
||||||
|
privKey []byte
|
||||||
|
conn *connWriter
|
||||||
|
|
||||||
|
// For sending to peer.
|
||||||
|
counter *uint64
|
||||||
|
|
||||||
|
// Mutable peer data.
|
||||||
|
peer *m.Peer
|
||||||
|
remotePub bool
|
||||||
|
data peerData // Local copy of shared data. See publish().
|
||||||
|
|
||||||
|
// Timers
|
||||||
|
pingTimer *time.Timer
|
||||||
|
timeoutTimer *time.Timer
|
||||||
|
|
||||||
|
buf []byte
|
||||||
|
encBuf []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sb *stateBase) Name() string { return "idle" }
|
||||||
|
|
||||||
|
func (s *stateBase) OnPeerUpdate(peer *m.Peer) peerState {
|
||||||
|
// Both nil: no change.
|
||||||
|
if peer == nil && s.peer == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// No change.
|
||||||
|
if peer != nil && s.peer != nil && s.peer.Version == peer.Version {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s.peer = peer
|
||||||
|
|
||||||
|
s.data = peerData{}
|
||||||
|
s.data.controlCipher = newControlCipher(s.privKey, peer.EncPubKey)
|
||||||
|
|
||||||
|
ip, isValid := netip.AddrFromSlice(peer.PublicIP)
|
||||||
|
if isValid {
|
||||||
|
s.remotePub = true
|
||||||
|
s.data.remoteAddr = netip.AddrPortFrom(ip, peer.Port)
|
||||||
|
s.data.relay = peer.Mediator
|
||||||
|
|
||||||
|
if s.localPub && s.localIP < s.remoteIP {
|
||||||
|
return newStateServer(s)
|
||||||
|
}
|
||||||
|
return newStateClient(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.localPub {
|
||||||
|
return newStateServer(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: return newStateMediated(a/b)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stateBase) OnPing(rAddr netip.AddrPort, p pingPacket) peerState { return nil }
|
||||||
|
func (s *stateBase) OnPong(rAddr netip.AddrPort, p pongPacket) peerState { return nil }
|
||||||
|
func (s *stateBase) OnPingTimer() peerState { return nil }
|
||||||
|
func (s *stateBase) OnTimeoutTimer() peerState { return nil }
|
||||||
|
|
||||||
|
// Helpers.
|
||||||
|
|
||||||
|
func (s *stateBase) resetPingTimer() { s.pingTimer.Reset(pingInterval) }
|
||||||
|
func (s *stateBase) resetTimeoutTimer() { s.timeoutTimer.Reset(timeoutInterval) }
|
||||||
|
func (s *stateBase) stopPingTimer() { s.pingTimer.Stop() }
|
||||||
|
func (s *stateBase) stopTimeoutTimer() { s.timeoutTimer.Stop() }
|
||||||
|
|
||||||
|
func (s *stateBase) logf(msg string, args ...any) {
|
||||||
|
log.Printf(fmt.Sprintf("[%03d] ", s.remoteIP)+msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stateBase) publish() {
|
||||||
|
data := s.data
|
||||||
|
s.published.Store(&data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stateBase) sendPing(sharedKey [32]byte) {
|
||||||
|
s.sendControlPacket(newPingPacket(sharedKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stateBase) sendPong(ping pingPacket) {
|
||||||
|
s.sendControlPacket(newPongPacket(ping.SentAt))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stateBase) sendControlPacket(pkt interface{ Marshal([]byte) []byte }) {
|
||||||
|
buf := pkt.Marshal(s.buf)
|
||||||
|
h := header{
|
||||||
|
StreamID: controlStreamID,
|
||||||
|
Counter: atomic.AddUint64(s.counter, 1),
|
||||||
|
SourceIP: s.localIP,
|
||||||
|
DestIP: s.remoteIP,
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = s.data.controlCipher.Encrypt(h, buf, s.encBuf)
|
||||||
|
if s.data.relayIP == 0 {
|
||||||
|
s.conn.WriteTo(buf, s.data.remoteAddr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Relay!
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type stateClient struct {
|
||||||
|
sharedKey [32]byte
|
||||||
|
*stateBase
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStateClient(b *stateBase) peerState {
|
||||||
|
s := &stateClient{stateBase: b}
|
||||||
|
s.publish()
|
||||||
|
|
||||||
|
s.data.dataCipher = newDataCipher()
|
||||||
|
s.sharedKey = s.data.dataCipher.Key()
|
||||||
|
|
||||||
|
s.sendPing(s.sharedKey)
|
||||||
|
s.resetPingTimer()
|
||||||
|
s.resetTimeoutTimer()
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stateClient) Name() string { return "client" }
|
||||||
|
|
||||||
|
func (s *stateClient) OnPong(addr netip.AddrPort, p pongPacket) peerState {
|
||||||
|
if !s.data.up {
|
||||||
|
s.data.up = true
|
||||||
|
s.publish()
|
||||||
|
}
|
||||||
|
s.resetTimeoutTimer()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stateClient) OnPingTimer() peerState {
|
||||||
|
s.sendPing(s.sharedKey)
|
||||||
|
s.resetPingTimer()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stateClient) OnTimeoutTimer() peerState {
|
||||||
|
s.data.up = false
|
||||||
|
s.publish()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type stateServer struct {
|
||||||
|
*stateBase
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStateServer(b *stateBase) peerState {
|
||||||
|
s := &stateServer{b}
|
||||||
|
s.publish()
|
||||||
|
s.stopPingTimer()
|
||||||
|
s.stopTimeoutTimer()
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stateServer) Name() string { return "server" }
|
||||||
|
|
||||||
|
func (s *stateServer) OnPing(addr netip.AddrPort, p pingPacket) peerState {
|
||||||
|
if addr != s.data.remoteAddr {
|
||||||
|
s.logf("Got new peer address: %v", addr)
|
||||||
|
s.data.remoteAddr = addr
|
||||||
|
s.data.up = true
|
||||||
|
s.publish()
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.data.dataCipher == nil || p.SharedKey != s.data.dataCipher.Key() {
|
||||||
|
s.logf("Got new shared key.")
|
||||||
|
s.data.dataCipher = newDataCipherFromKey(p.SharedKey)
|
||||||
|
s.publish()
|
||||||
|
}
|
||||||
|
|
||||||
|
s.sendPong(p)
|
||||||
|
return nil
|
||||||
|
}
|
@ -1,10 +1,6 @@
|
|||||||
package node
|
package node
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
"math/rand"
|
|
||||||
"net/netip"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
"time"
|
||||||
"vppn/m"
|
"vppn/m"
|
||||||
)
|
)
|
||||||
@ -15,263 +11,64 @@ const (
|
|||||||
timeoutInterval = 20 * time.Second
|
timeoutInterval = 20 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
type stateFunc func() stateFunc
|
func (rp *remotePeer) supervise(
|
||||||
|
conf m.PeerConfig,
|
||||||
type peerSuper struct {
|
remoteIP byte,
|
||||||
*remotePeer
|
conn *connWriter,
|
||||||
|
peers *remotePeers,
|
||||||
peer *m.Peer
|
) {
|
||||||
remotePublic bool
|
|
||||||
peerData peerData
|
|
||||||
|
|
||||||
pktBuf []byte
|
|
||||||
encBuf []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPeerSuper(rp *remotePeer) *peerSuper {
|
|
||||||
return &peerSuper{
|
|
||||||
remotePeer: rp,
|
|
||||||
peer: nil,
|
|
||||||
pktBuf: make([]byte, bufferSize),
|
|
||||||
encBuf: make([]byte, bufferSize),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rp *peerSuper) Run() {
|
|
||||||
defer panicHandler()
|
defer panicHandler()
|
||||||
state := rp.stateInit
|
|
||||||
for {
|
|
||||||
state = state()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
base := &stateBase{
|
||||||
|
published: rp.published,
|
||||||
func (rp *peerSuper) stateInit() stateFunc {
|
peers: rp.peers,
|
||||||
//rp.logf("STATE: Init")
|
localIP: rp.localIP,
|
||||||
|
remoteIP: rp.remoteIP,
|
||||||
x := peerData{}
|
privKey: conf.EncPrivKey,
|
||||||
rp.shared.Store(&x)
|
localPub: addrIsValid(conf.PublicIP),
|
||||||
|
conn: rp.conn,
|
||||||
rp.peerData.relay = false
|
counter: &rp.counter,
|
||||||
rp.peerData.controlCipher = nil
|
pingTimer: time.NewTimer(time.Second),
|
||||||
rp.peerData.dataCipher = nil
|
timeoutTimer: time.NewTimer(time.Second),
|
||||||
rp.peerData.remoteAddr = zeroAddrPort
|
buf: make([]byte, bufferSize),
|
||||||
rp.peerData.relayIP = 0
|
encBuf: make([]byte, bufferSize),
|
||||||
|
|
||||||
if rp.peer == nil {
|
|
||||||
return rp.stateDisconnected
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var addr netip.Addr
|
base.pingTimer.Stop()
|
||||||
addr, rp.remotePublic = netip.AddrFromSlice(rp.peer.PublicIP)
|
base.timeoutTimer.Stop()
|
||||||
if rp.remotePublic {
|
|
||||||
rp.peerData.remoteAddr = netip.AddrPortFrom(addr, rp.peer.Port)
|
|
||||||
} else {
|
|
||||||
rp.peerData.relay = false
|
|
||||||
}
|
|
||||||
|
|
||||||
rp.peerData.controlCipher = newControlCipher(rp.privKey, rp.peer.EncPubKey)
|
|
||||||
|
|
||||||
return rp.stateSelectRole()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
func (rp *peerSuper) stateDisconnected() stateFunc {
|
|
||||||
//rp.logf("STATE: Disconnected")
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-rp.controlPackets:
|
|
||||||
// Drop
|
|
||||||
case rp.peer = <-rp.peerUpdates:
|
|
||||||
return rp.stateInit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
func (rp *peerSuper) stateSelectRole() stateFunc {
|
|
||||||
rp.logf("STATE: SelectRole")
|
|
||||||
|
|
||||||
if !rp.localPublic && !rp.remotePublic {
|
|
||||||
return rp.stateSelectMediator
|
|
||||||
}
|
|
||||||
|
|
||||||
if !rp.localPublic {
|
|
||||||
return rp.stateServer
|
|
||||||
} else if !rp.remotePublic {
|
|
||||||
return rp.stateClient
|
|
||||||
}
|
|
||||||
|
|
||||||
if rp.localIP < rp.remoteIP {
|
|
||||||
return rp.stateClient
|
|
||||||
}
|
|
||||||
return rp.stateServer
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
func (rp *peerSuper) stateSelectMediator() stateFunc {
|
|
||||||
rp.logf("STATE: SelectMediator")
|
|
||||||
|
|
||||||
for {
|
|
||||||
log.Printf("Selecting mediator...")
|
|
||||||
if ip := rp.selectMediator(); ip != 0 {
|
|
||||||
rp.logf("Got mediator: %d", ip)
|
|
||||||
rp.peerData.relayIP = ip
|
|
||||||
|
|
||||||
if rp.localIP < rp.remoteIP {
|
|
||||||
return rp.stateClient
|
|
||||||
}
|
|
||||||
return rp.stateServer
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-time.After(pingInterval):
|
|
||||||
continue
|
|
||||||
case rp.peer = <-rp.peerUpdates:
|
|
||||||
return rp.stateInit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rp *peerSuper) selectMediator() byte {
|
|
||||||
possible := make([]byte, 0, 8)
|
|
||||||
for _, peer := range rp.peers {
|
|
||||||
if peer.canRelay() {
|
|
||||||
rp.logf("relay: %v", peer.shared.Load())
|
|
||||||
possible = append(possible, peer.remoteIP)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(possible) == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return possible[rand.Intn(len(possible))]
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// The remote is a server.
|
|
||||||
func (rp *peerSuper) stateServer() stateFunc {
|
|
||||||
rp.logf("STATE: Server")
|
|
||||||
rp.peerData.dataCipher = newDataCipher()
|
|
||||||
rp.updateShared()
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
pingTimer = time.NewTimer(pingInterval)
|
curState peerState = base
|
||||||
timeoutTimer = time.NewTimer(timeoutInterval)
|
nextState peerState
|
||||||
ping = pingPacket{SharedKey: ([32]byte)(rp.peerData.dataCipher.Key())}
|
|
||||||
)
|
)
|
||||||
defer pingTimer.Stop()
|
|
||||||
defer timeoutTimer.Stop()
|
|
||||||
|
|
||||||
ping.SentAt = time.Now().UnixMilli()
|
|
||||||
rp.sendControlPacket(ping)
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
nextState = nil
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-pingTimer.C:
|
case peer := <-rp.peerUpdates:
|
||||||
ping.SentAt = time.Now().UnixMilli()
|
nextState = curState.OnPeerUpdate(peer)
|
||||||
rp.sendControlPacket(ping)
|
|
||||||
pingTimer.Reset(pingInterval)
|
|
||||||
|
|
||||||
case cPkt := <-rp.controlPackets:
|
case pkt := <-rp.controlPackets:
|
||||||
if _, ok := cPkt.Payload.(pongPacket); ok {
|
switch p := pkt.Payload.(type) {
|
||||||
timeoutTimer.Reset(timeoutInterval)
|
case pingPacket:
|
||||||
|
nextState = curState.OnPing(pkt.RemoteAddr, p)
|
||||||
|
case pongPacket:
|
||||||
|
nextState = curState.OnPong(pkt.RemoteAddr, p)
|
||||||
|
default:
|
||||||
|
// Unknown packet type.
|
||||||
}
|
}
|
||||||
|
|
||||||
case <-timeoutTimer.C:
|
case <-base.pingTimer.C:
|
||||||
if rp.peerData.relayIP != 0 {
|
nextState = curState.OnPingTimer()
|
||||||
rp.logf("Timeout (server, relay)")
|
|
||||||
return rp.stateSelectMediator
|
|
||||||
} else {
|
|
||||||
rp.logf("Timeout (server)")
|
|
||||||
}
|
|
||||||
|
|
||||||
case rp.peer = <-rp.peerUpdates:
|
case <-base.timeoutTimer.C:
|
||||||
return rp.stateInit
|
nextState = curState.OnTimeoutTimer()
|
||||||
|
}
|
||||||
|
|
||||||
|
if nextState != nil {
|
||||||
|
rp.logf("%s --> %s", curState.Name(), nextState.Name())
|
||||||
|
curState = nextState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// The remote is a client.
|
|
||||||
func (rp *peerSuper) stateClient() stateFunc {
|
|
||||||
rp.logf("STATE: Client")
|
|
||||||
rp.updateShared()
|
|
||||||
|
|
||||||
var (
|
|
||||||
currentKey = [32]byte{}
|
|
||||||
timeoutTimer = time.NewTimer(timeoutInterval)
|
|
||||||
)
|
|
||||||
|
|
||||||
defer timeoutTimer.Stop()
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case cPkt := <-rp.controlPackets:
|
|
||||||
if cPkt.RemoteAddr != rp.peerData.remoteAddr {
|
|
||||||
rp.peerData.remoteAddr = cPkt.RemoteAddr
|
|
||||||
rp.logf("Got new remote address: %v", cPkt.RemoteAddr)
|
|
||||||
rp.updateShared()
|
|
||||||
}
|
|
||||||
|
|
||||||
ping, ok := cPkt.Payload.(pingPacket)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if ping.SharedKey != currentKey {
|
|
||||||
rp.logf("Connected with new shared key")
|
|
||||||
currentKey = ping.SharedKey
|
|
||||||
rp.peerData.up = true
|
|
||||||
rp.peerData.dataCipher = newDataCipherFromKey(currentKey)
|
|
||||||
rp.updateShared()
|
|
||||||
}
|
|
||||||
|
|
||||||
timeoutTimer.Reset(timeoutInterval)
|
|
||||||
rp.sendControlPacket(newPongPacket(ping.SentAt))
|
|
||||||
|
|
||||||
case <-timeoutTimer.C:
|
|
||||||
if rp.peerData.relayIP != 0 {
|
|
||||||
rp.logf("Timeout (server, relay)")
|
|
||||||
return rp.stateSelectMediator
|
|
||||||
} else {
|
|
||||||
rp.logf("Timeout (server)")
|
|
||||||
}
|
|
||||||
|
|
||||||
case rp.peer = <-rp.peerUpdates:
|
|
||||||
return rp.stateInit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
func (rp *peerSuper) updateShared() {
|
|
||||||
data := rp.peerData
|
|
||||||
rp.shared.Store(&data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
func (rp *peerSuper) sendControlPacket(pkt interface{ Marshal([]byte) []byte }) {
|
|
||||||
buf := pkt.Marshal(rp.pktBuf)
|
|
||||||
h := header{
|
|
||||||
StreamID: controlStreamID,
|
|
||||||
Counter: atomic.AddUint64(&rp.counter, 1),
|
|
||||||
SourceIP: rp.localIP,
|
|
||||||
DestIP: rp.remoteIP,
|
|
||||||
}
|
|
||||||
buf = rp.peerData.controlCipher.Encrypt(h, buf, rp.encBuf)
|
|
||||||
if rp.peerData.relayIP == 0 {
|
|
||||||
rp.conn.WriteTo(buf, rp.peerData.remoteAddr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
rp.peers[rp.peerData.relayIP].RelayControlData(buf)
|
|
||||||
}
|
|
||||||
|
129
node/peer.go
129
node/peer.go
@ -22,19 +22,14 @@ type peerData struct {
|
|||||||
|
|
||||||
type remotePeer struct {
|
type remotePeer struct {
|
||||||
// Immutable data.
|
// Immutable data.
|
||||||
localIP byte
|
localIP byte
|
||||||
remoteIP byte
|
remoteIP byte
|
||||||
privKey []byte
|
iface *ifWriter
|
||||||
localPublic bool // True if local node is public.
|
conn *connWriter
|
||||||
iface *ifWriter
|
|
||||||
conn *connWriter
|
|
||||||
|
|
||||||
// Shared state.
|
// Shared state.
|
||||||
peers *remotePeers
|
peers *remotePeers
|
||||||
shared *atomic.Pointer[peerData]
|
published *atomic.Pointer[peerData]
|
||||||
|
|
||||||
// Only used in HandlePeerUpdate.
|
|
||||||
peerVersion int64
|
|
||||||
|
|
||||||
// Only used in HandlePacket / Not synchronized.
|
// Only used in HandlePacket / Not synchronized.
|
||||||
dupCheck *dupCheck
|
dupCheck *dupCheck
|
||||||
@ -55,12 +50,10 @@ func newRemotePeer(conf m.PeerConfig, remoteIP byte, iface *ifWriter, conn *conn
|
|||||||
rp := &remotePeer{
|
rp := &remotePeer{
|
||||||
localIP: conf.PeerIP,
|
localIP: conf.PeerIP,
|
||||||
remoteIP: remoteIP,
|
remoteIP: remoteIP,
|
||||||
privKey: conf.EncPrivKey,
|
|
||||||
localPublic: addrIsValid(conf.PublicIP),
|
|
||||||
iface: iface,
|
iface: iface,
|
||||||
conn: conn,
|
conn: conn,
|
||||||
peers: peers,
|
peers: peers,
|
||||||
shared: &atomic.Pointer[peerData]{},
|
published: &atomic.Pointer[peerData]{},
|
||||||
dupCheck: newDupCheck(0),
|
dupCheck: newDupCheck(0),
|
||||||
decryptBuf: make([]byte, bufferSize),
|
decryptBuf: make([]byte, bufferSize),
|
||||||
encryptBuf: make([]byte, bufferSize),
|
encryptBuf: make([]byte, bufferSize),
|
||||||
@ -70,10 +63,10 @@ func newRemotePeer(conf m.PeerConfig, remoteIP byte, iface *ifWriter, conn *conn
|
|||||||
}
|
}
|
||||||
|
|
||||||
pd := peerData{}
|
pd := peerData{}
|
||||||
rp.shared.Store(&pd)
|
rp.published.Store(&pd)
|
||||||
|
|
||||||
go newPeerSuper(rp).Run()
|
|
||||||
|
|
||||||
|
//go newPeerSuper(rp).Run()
|
||||||
|
go rp.supervise(conf, remoteIP, conn, peers)
|
||||||
return rp
|
return rp
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,10 +75,7 @@ func (rp *remotePeer) logf(msg string, args ...any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (rp *remotePeer) HandlePeerUpdate(peer *m.Peer) {
|
func (rp *remotePeer) HandlePeerUpdate(peer *m.Peer) {
|
||||||
if peer != nil && peer.Version != rp.peerVersion {
|
rp.peerUpdates <- peer
|
||||||
rp.peerUpdates <- peer
|
|
||||||
rp.peerVersion = peer.Version
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@ -101,6 +91,9 @@ func (rp *remotePeer) HandlePacket(addr netip.AddrPort, h header, data []byte) {
|
|||||||
case dataStreamID:
|
case dataStreamID:
|
||||||
rp.handleDataPacket(data)
|
rp.handleDataPacket(data)
|
||||||
|
|
||||||
|
case relayStreamID:
|
||||||
|
rp.handleRelayPacket(h, data)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
rp.logf("Unknown stream ID: %d", h.StreamID)
|
rp.logf("Unknown stream ID: %d", h.StreamID)
|
||||||
}
|
}
|
||||||
@ -109,8 +102,9 @@ func (rp *remotePeer) HandlePacket(addr netip.AddrPort, h header, data []byte) {
|
|||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
func (rp *remotePeer) handleControlPacket(addr netip.AddrPort, h header, data []byte) {
|
func (rp *remotePeer) handleControlPacket(addr netip.AddrPort, h header, data []byte) {
|
||||||
shared := rp.shared.Load()
|
shared := rp.published.Load()
|
||||||
if shared.controlCipher == nil {
|
if shared.controlCipher == nil {
|
||||||
|
log.Printf("Shared: %+v", *shared)
|
||||||
rp.logf("Not connected (control).")
|
rp.logf("Not connected (control).")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -141,19 +135,7 @@ func (rp *remotePeer) handleControlPacket(addr netip.AddrPort, h header, data []
|
|||||||
RemoteAddr: addr,
|
RemoteAddr: addr,
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
if err := pkt.ParsePayload(out); err != nil {
|
||||||
|
|
||||||
switch out[0] {
|
|
||||||
case packetTypePing:
|
|
||||||
pkt.Payload, err = parsePingPacket(out)
|
|
||||||
case packetTypePong:
|
|
||||||
pkt.Payload, err = parsePongPacket(out)
|
|
||||||
default:
|
|
||||||
rp.logf("Unknown control packet type: %d", out[0])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
rp.logf("Failed to parse control packet: %v", err)
|
rp.logf("Failed to parse control packet: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -168,7 +150,7 @@ func (rp *remotePeer) handleControlPacket(addr netip.AddrPort, h header, data []
|
|||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
func (rp *remotePeer) handleDataPacket(data []byte) {
|
func (rp *remotePeer) handleDataPacket(data []byte) {
|
||||||
shared := rp.shared.Load()
|
shared := rp.published.Load()
|
||||||
if shared.dataCipher == nil {
|
if shared.dataCipher == nil {
|
||||||
rp.logf("Not connected (recv).")
|
rp.logf("Not connected (recv).")
|
||||||
return
|
return
|
||||||
@ -185,34 +167,65 @@ func (rp *remotePeer) handleDataPacket(data []byte) {
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func (rp *remotePeer) handleRelayPacket(h header, data []byte) {
|
||||||
|
shared := rp.published.Load()
|
||||||
|
if shared.dataCipher == nil {
|
||||||
|
rp.logf("Not connected (recv).")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dec, ok := shared.dataCipher.Decrypt(data, rp.decryptBuf)
|
||||||
|
if !ok {
|
||||||
|
rp.logf("Failed to decrypt data packet.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rp.peers[h.DestIP].sendDirect(dec)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
// SendData sends data coming from the interface going to the network.
|
// SendData sends data coming from the interface going to the network.
|
||||||
//
|
//
|
||||||
// This function is called by a single thread.
|
// This function is called by a single thread.
|
||||||
func (rp *remotePeer) SendData(data []byte) {
|
func (rp *remotePeer) SendData(data []byte) {
|
||||||
rp.sendData(dataStreamID, data)
|
rp.sendData(dataStreamID, rp.remoteIP, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
func (rp *remotePeer) HandleInterfacePacket(data []byte) {
|
||||||
|
shared := rp.published.Load()
|
||||||
|
|
||||||
func (rp *remotePeer) RelayControlData(data []byte) {
|
if shared.dataCipher == nil {
|
||||||
rp.sendData(forwardStreamID, data)
|
rp.logf("Not connected (handle interface).")
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
func (rp *remotePeer) ForwardPacket(data []byte) {
|
|
||||||
shared := rp.shared.Load()
|
|
||||||
if shared.remoteAddr == zeroAddrPort {
|
|
||||||
rp.logf("Not connected (forward).")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rp.conn.WriteTo(data, shared.remoteAddr)
|
|
||||||
|
h := header{
|
||||||
|
StreamID: dataStreamID,
|
||||||
|
Counter: atomic.AddUint64(&rp.counter, 1),
|
||||||
|
SourceIP: rp.localIP,
|
||||||
|
DestIP: rp.remoteIP,
|
||||||
|
}
|
||||||
|
|
||||||
|
enc := shared.dataCipher.Encrypt(h, data, rp.encryptBuf)
|
||||||
|
|
||||||
|
if shared.relayIP != 0 {
|
||||||
|
rp.peers[shared.relayIP].RelayData(shared.relayIP, enc)
|
||||||
|
} else {
|
||||||
|
rp.SendData(data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
func (rp *remotePeer) sendData(streamID byte, data []byte) {
|
func (rp *remotePeer) RelayData(destIP byte, data []byte) {
|
||||||
shared := rp.shared.Load()
|
rp.sendData(relayStreamID, destIP, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func (rp *remotePeer) sendData(streamID byte, destIP byte, data []byte) {
|
||||||
|
shared := rp.published.Load()
|
||||||
if shared.dataCipher == nil || shared.remoteAddr == zeroAddrPort {
|
if shared.dataCipher == nil || shared.remoteAddr == zeroAddrPort {
|
||||||
rp.logf("Not connected (send).")
|
rp.logf("Not connected (send).")
|
||||||
return
|
return
|
||||||
@ -222,16 +235,18 @@ func (rp *remotePeer) sendData(streamID byte, data []byte) {
|
|||||||
StreamID: streamID,
|
StreamID: streamID,
|
||||||
Counter: atomic.AddUint64(&rp.counter, 1),
|
Counter: atomic.AddUint64(&rp.counter, 1),
|
||||||
SourceIP: rp.localIP,
|
SourceIP: rp.localIP,
|
||||||
DestIP: rp.remoteIP,
|
DestIP: destIP,
|
||||||
}
|
}
|
||||||
|
|
||||||
enc := shared.dataCipher.Encrypt(h, data, rp.encryptBuf)
|
enc := shared.dataCipher.Encrypt(h, data, rp.encryptBuf)
|
||||||
rp.conn.WriteTo(enc, shared.remoteAddr)
|
rp.conn.WriteTo(enc, shared.remoteAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
func (rp *remotePeer) sendDirect(data []byte) {
|
||||||
|
shared := rp.published.Load()
|
||||||
func (rp *remotePeer) canRelay() bool {
|
if shared.remoteAddr == zeroAddrPort {
|
||||||
shared := rp.shared.Load()
|
rp.logf("Not connected (send).")
|
||||||
return shared.relay && shared.up
|
return
|
||||||
|
}
|
||||||
|
rp.conn.WriteTo(data, shared.remoteAddr)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user