This commit is contained in:
jdl 2024-12-13 19:14:48 +01:00
parent 24517c02f1
commit d9233af4c9
5 changed files with 122 additions and 72 deletions

View File

@ -8,6 +8,10 @@
* Peer: local peer discovery - part of RoutingProcessor * Peer: local peer discovery - part of RoutingProcessor
* Peer: update hub w/ latest port on startup * Peer: update hub w/ latest port on startup
## Learnings
* Encryption / decryption is 20x faster than signing/opening.
## Principles ## Principles
* Creates an IPv4/24 network with a maximum of 254 peers. (1-254) * Creates an IPv4/24 network with a maximum of 254 peers. (1-254)
@ -107,3 +111,11 @@ TimeoutStopSec=24
[Install] [Install]
WantedBy=default.target WantedBy=default.target
``` ```
---
## Sub-packets
If we make our MTU large, like 8k, our computations become more efficient.
We can send packets with header like:

View File

@ -78,7 +78,7 @@ func (h *connHandler) mainLoop() {
state = state.HandlePong(w) state = state.HandlePong(w)
case <-data.pingTimer.C: case <-data.pingTimer.C:
state.HandleSendPing() data.HandleSendPing()
case <-data.timeoutTimer.C: case <-data.timeoutTimer.C:
log.Printf("[%s] Connection timeout.", state.Name()) log.Printf("[%s] Connection timeout.", state.Name())
@ -86,7 +86,7 @@ func (h *connHandler) mainLoop() {
} }
if state.Name() != name { if state.Name() != name {
log.Printf("[%03d] STATE: %s --> %s", data.peerIP, name, state.Name()) log.Printf("[%03d] STATE: %s", data.peerIP, state.Name())
name = state.Name() name = state.Name()
} }
} }

View File

@ -17,7 +17,6 @@ func logState(s connState, msg string, args ...any) {
type connState interface { type connState interface {
Name() string Name() string
HandleMediatorUpdate(ip byte) connState HandleMediatorUpdate(ip byte) connState
HandleSendPing()
HandlePing(wrapper[Ping]) connState HandlePing(wrapper[Ping]) connState
HandlePong(wrapper[Pong]) connState HandlePong(wrapper[Pong]) connState
HandleTimeout() connState HandleTimeout() connState
@ -33,11 +32,11 @@ func newConnStateFromPeer(update peerUpdate, data *connData) connState {
} }
if _, isPublic := netip.AddrFromSlice(peer.PublicIP); isPublic { if _, isPublic := netip.AddrFromSlice(peer.PublicIP); isPublic {
return newConnUnconnectedServer(data, peer) return newStateServerDown(data, peer)
} else if data.server { } else if data.server {
return newConnUnconnectedClient(data, peer) return newStateClientDown(data, peer)
} else { } else {
return newConnUnconnectedMediator(data, peer) return newStateMediatedDown(data, peer)
} }
} }
@ -91,15 +90,15 @@ func (c connNull) HandleTimeout() connState {
// Unconnected Server // // Unconnected Server //
//////////////////////// ////////////////////////
type connUnconnectedServer struct { type stateServerDown struct {
*connData *connData
} }
func newConnUnconnectedServer(data *connData, peer *m.Peer) connState { func newStateServerDown(data *connData, peer *m.Peer) connState {
addr, _ := netip.AddrFromSlice(peer.PublicIP) addr, _ := netip.AddrFromSlice(peer.PublicIP)
pubAddr := netip.AddrPortFrom(addr, peer.Port) pubAddr := netip.AddrPortFrom(addr, peer.Port)
c := connUnconnectedServer{data} c := stateServerDown{data}
c.peer = peer c.peer = peer
c.encSharedKey = computeSharedKey(peer.EncPubKey, c.encPrivKey) c.encSharedKey = computeSharedKey(peer.EncPubKey, c.encPrivKey)
c.publicAddr = pubAddr c.publicAddr = pubAddr
@ -113,26 +112,26 @@ func newConnUnconnectedServer(data *connData, peer *m.Peer) connState {
return c return c
} }
func (c connUnconnectedServer) Name() string { func (c stateServerDown) Name() string {
return "ServerUnconnected" return "Server:DOWN"
} }
func (c connUnconnectedServer) HandleMediatorUpdate(ip byte) connState { func (c stateServerDown) HandleMediatorUpdate(ip byte) connState {
// Server connection doesn't use a mediator. // Server connection doesn't use a mediator.
c.mediatorIP = ip c.mediatorIP = ip
return c return c
} }
func (c connUnconnectedServer) HandlePing(w wrapper[Ping]) connState { func (c stateServerDown) HandlePing(w wrapper[Ping]) connState {
logState(c, "Ignoring ping.") logState(c, "Ignoring ping.")
return c return c
} }
func (c connUnconnectedServer) HandlePong(w wrapper[Pong]) connState { func (c stateServerDown) HandlePong(w wrapper[Pong]) connState {
return newConnConnectedServer(c.connData, w) return newStateServerUp(c.connData, w)
} }
func (c connUnconnectedServer) HandleTimeout() connState { func (c stateServerDown) HandleTimeout() connState {
logState(c, "Unexpected timeout.") logState(c, "Unexpected timeout.")
return c return c
} }
@ -141,12 +140,12 @@ func (c connUnconnectedServer) HandleTimeout() connState {
// Connected Server // // Connected Server //
////////////////////// //////////////////////
type connConnectedServer struct { type stateServerUp struct {
*connData *connData
} }
func newConnConnectedServer(data *connData, w wrapper[Pong]) connState { func newStateServerUp(data *connData, w wrapper[Pong]) connState {
c := connConnectedServer{data} c := stateServerUp{data}
c.pingTimer.Reset(pingInterval) c.pingTimer.Reset(pingInterval)
c.timeoutTimer.Reset(timeoutInterval) c.timeoutTimer.Reset(timeoutInterval)
c.addr = w.SrcAddr c.addr = w.SrcAddr
@ -156,43 +155,43 @@ func newConnConnectedServer(data *connData, w wrapper[Pong]) connState {
return c return c
} }
func (c connConnectedServer) Name() string { func (c stateServerUp) Name() string {
return "ServerConnected" return "Server:UP"
} }
func (c connConnectedServer) HandleMediatorUpdate(ip byte) connState { func (c stateServerUp) HandleMediatorUpdate(ip byte) connState {
// Server connection doesn't use a mediator. // Server connection doesn't use a mediator.
c.mediatorIP = ip c.mediatorIP = ip
return c return c
} }
func (c connConnectedServer) HandlePing(w wrapper[Ping]) connState { func (c stateServerUp) HandlePing(w wrapper[Ping]) connState {
logState(c, "Ignoring ping.") logState(c, "Ignoring ping.")
return c return c
} }
func (c connConnectedServer) HandlePong(w wrapper[Pong]) connState { func (c stateServerUp) HandlePong(w wrapper[Pong]) connState {
c.timeoutTimer.Reset(timeoutInterval) c.timeoutTimer.Reset(timeoutInterval)
return c return c
} }
func (c connConnectedServer) HandleTimeout() connState { func (c stateServerUp) HandleTimeout() connState {
return newConnUnconnectedServer(c.connData, c.peer) return newStateServerDown(c.connData, c.peer)
} }
//////////////////////// ////////////////////////
// Unconnected Client // // Unconnected Client //
//////////////////////// ////////////////////////
type connUnconnectedClient struct { type stateClientDown struct {
*connData *connData
} }
func newConnUnconnectedClient(data *connData, peer *m.Peer) connState { func newStateClientDown(data *connData, peer *m.Peer) connState {
addr, _ := netip.AddrFromSlice(peer.PublicIP) addr, _ := netip.AddrFromSlice(peer.PublicIP)
pubAddr := netip.AddrPortFrom(addr, peer.Port) pubAddr := netip.AddrPortFrom(addr, peer.Port)
c := connUnconnectedClient{data} c := stateClientDown{data}
c.peer = peer c.peer = peer
c.publicAddr = pubAddr c.publicAddr = pubAddr
c.encPrivKey = data.encPrivKey c.encPrivKey = data.encPrivKey
@ -208,28 +207,28 @@ func newConnUnconnectedClient(data *connData, peer *m.Peer) connState {
return c return c
} }
func (c connUnconnectedClient) Name() string { func (c stateClientDown) Name() string {
return "ClientUnconnected" return "Client:DOWN"
} }
func (c connUnconnectedClient) HandleMediatorUpdate(ip byte) connState { func (c stateClientDown) HandleMediatorUpdate(ip byte) connState {
// Client connection doesn't use a mediator. // Client connection doesn't use a mediator.
c.mediatorIP = ip c.mediatorIP = ip
return c return c
} }
func (c connUnconnectedClient) HandlePing(w wrapper[Ping]) connState { func (c stateClientDown) HandlePing(w wrapper[Ping]) connState {
next := newConnConnectedClient(c.connData, w) next := newStateClientUp(c.connData, w)
c.sendPong(w) // Have to send after transitionsing so route is ok. c.sendPong(w) // Have to send after transitionsing so route is ok.
return next return next
} }
func (c connUnconnectedClient) HandlePong(w wrapper[Pong]) connState { func (c stateClientDown) HandlePong(w wrapper[Pong]) connState {
logState(c, "Ignorning pong.") logState(c, "Ignorning pong.")
return c return c
} }
func (c connUnconnectedClient) HandleTimeout() connState { func (c stateClientDown) HandleTimeout() connState {
logState(c, "Unexpected timeout.") logState(c, "Unexpected timeout.")
return c return c
} }
@ -238,12 +237,12 @@ func (c connUnconnectedClient) HandleTimeout() connState {
// Connected Client // // Connected Client //
////////////////////// //////////////////////
type connConnectedClient struct { type stateClientUp struct {
*connData *connData
} }
func newConnConnectedClient(data *connData, w wrapper[Ping]) connState { func newStateClientUp(data *connData, w wrapper[Ping]) connState {
c := connConnectedClient{data} c := stateClientUp{data}
c.addr = w.SrcAddr c.addr = w.SrcAddr
c.viaIP = 0 c.viaIP = 0
c.up = true c.up = true
@ -254,17 +253,17 @@ func newConnConnectedClient(data *connData, w wrapper[Ping]) connState {
return c return c
} }
func (c connConnectedClient) Name() string { func (c stateClientUp) Name() string {
return "ClientConnected" return "Client:UP"
} }
func (c connConnectedClient) HandleMediatorUpdate(ip byte) connState { func (c stateClientUp) HandleMediatorUpdate(ip byte) connState {
// Client connection doesn't use a mediator. // Client connection doesn't use a mediator.
c.mediatorIP = ip c.mediatorIP = ip
return c return c
} }
func (c connConnectedClient) HandlePing(w wrapper[Ping]) connState { func (c stateClientUp) HandlePing(w wrapper[Ping]) connState {
// The connection is from a client. If the client's address changes, we // The connection is from a client. If the client's address changes, we
// should follow that change. // should follow that change.
if c.addr != w.SrcAddr { if c.addr != w.SrcAddr {
@ -276,28 +275,28 @@ func (c connConnectedClient) HandlePing(w wrapper[Ping]) connState {
return c return c
} }
func (c connConnectedClient) HandlePong(w wrapper[Pong]) connState { func (c stateClientUp) HandlePong(w wrapper[Pong]) connState {
logState(c, "Ignoring pong.") logState(c, "Ignoring pong.")
return c return c
} }
func (c connConnectedClient) HandleTimeout() connState { func (c stateClientUp) HandleTimeout() connState {
return newConnUnconnectedClient(c.connData, c.peer) return newStateClientDown(c.connData, c.peer)
} }
////////////////////////// //////////////////////////
// Unconnected Mediator // // Unconnected Mediator //
////////////////////////// //////////////////////////
type connUnconnectedMediator struct { type stateMediatedDown struct {
*connData *connData
} }
func newConnUnconnectedMediator(data *connData, peer *m.Peer) connState { func newStateMediatedDown(data *connData, peer *m.Peer) connState {
addr, _ := netip.AddrFromSlice(peer.PublicIP) addr, _ := netip.AddrFromSlice(peer.PublicIP)
pubAddr := netip.AddrPortFrom(addr, peer.Port) pubAddr := netip.AddrPortFrom(addr, peer.Port)
c := connUnconnectedMediator{data} c := stateMediatedDown{data}
c.peer = peer c.peer = peer
c.publicAddr = pubAddr c.publicAddr = pubAddr
c.encPrivKey = data.encPrivKey c.encPrivKey = data.encPrivKey
@ -312,35 +311,35 @@ func newConnUnconnectedMediator(data *connData, peer *m.Peer) connState {
// If we have a mediator route, we can connect. // If we have a mediator route, we can connect.
if mRoute := c.routes[c.mediatorIP].Load(); mRoute != nil { if mRoute := c.routes[c.mediatorIP].Load(); mRoute != nil {
return newConnConnectedMediator(data, mRoute) return newStateMediatedUp(data, mRoute)
} }
return c return c
} }
func (c connUnconnectedMediator) Name() string { func (c stateMediatedDown) Name() string {
return "MediatorUnconnected" return "Mediated:DOWN"
} }
func (c connUnconnectedMediator) HandleMediatorUpdate(ip byte) connState { func (c stateMediatedDown) HandleMediatorUpdate(ip byte) connState {
c.mediatorIP = ip c.mediatorIP = ip
if mRoute := c.routes[c.mediatorIP].Load(); mRoute != nil { if mRoute := c.routes[c.mediatorIP].Load(); mRoute != nil {
return newConnConnectedMediator(c.connData, mRoute) return newStateMediatedUp(c.connData, mRoute)
} }
return c return c
} }
func (c connUnconnectedMediator) HandlePing(w wrapper[Ping]) connState { func (c stateMediatedDown) HandlePing(w wrapper[Ping]) connState {
logState(c, "Ignorning ping.") logState(c, "Ignorning ping.")
return c return c
} }
func (c connUnconnectedMediator) HandlePong(w wrapper[Pong]) connState { func (c stateMediatedDown) HandlePong(w wrapper[Pong]) connState {
logState(c, "Ignorning pong.") logState(c, "Ignorning pong.")
return c return c
} }
func (c connUnconnectedMediator) HandleTimeout() connState { func (c stateMediatedDown) HandleTimeout() connState {
logState(c, "Unexpected timeout.") logState(c, "Unexpected timeout.")
return c return c
} }
@ -349,12 +348,12 @@ func (c connUnconnectedMediator) HandleTimeout() connState {
// Connected Mediator // // Connected Mediator //
//////////////////////// ////////////////////////
type connConnectedMediator struct { type stateMediatedUp struct {
*connData *connData
} }
func newConnConnectedMediator(data *connData, route *route) connState { func newStateMediatedUp(data *connData, route *route) connState {
c := connConnectedMediator{data} c := stateMediatedUp{data}
c.addr = route.Addr c.addr = route.Addr
c.viaIP = route.PeerIP c.viaIP = route.PeerIP
c.up = true c.up = true
@ -366,28 +365,28 @@ func newConnConnectedMediator(data *connData, route *route) connState {
return c return c
} }
func (c connConnectedMediator) Name() string { func (c stateMediatedUp) Name() string {
return "MediatorConnected" return "Mediated:UP"
} }
func (c connConnectedMediator) HandleMediatorUpdate(ip byte) connState { func (c stateMediatedUp) HandleMediatorUpdate(ip byte) connState {
c.mediatorIP = ip c.mediatorIP = ip
if mRoute := c.routes[c.mediatorIP].Load(); mRoute != nil { if mRoute := c.routes[c.mediatorIP].Load(); mRoute != nil {
return newConnConnectedMediator(c.connData, mRoute) return newStateMediatedUp(c.connData, mRoute)
} }
return newConnUnconnectedMediator(c.connData, c.peer) return newStateMediatedDown(c.connData, c.peer)
} }
func (c connConnectedMediator) HandlePing(w wrapper[Ping]) connState { func (c stateMediatedUp) HandlePing(w wrapper[Ping]) connState {
logState(c, "Ignoring ping.") logState(c, "Ignoring ping.")
return c return c
} }
func (c connConnectedMediator) HandlePong(w wrapper[Pong]) connState { func (c stateMediatedUp) HandlePong(w wrapper[Pong]) connState {
logState(c, "Ignoring pong.") logState(c, "Ignoring pong.")
return c return c
} }
func (c connConnectedMediator) HandleTimeout() connState { func (c stateMediatedUp) HandleTimeout() connState {
return newConnUnconnectedMediator(c.connData, c.peer) return newStateMediatedDown(c.connData, c.peer)
} }

View File

@ -106,6 +106,22 @@ func BenchmarkDecryptPacket(b *testing.B) {
} }
} }
func BenchmarkSignPacket(b *testing.B) {
_, privKey1, err := sign.GenerateKey(rand.Reader)
if err != nil {
b.Fatal(err)
}
original := make([]byte, 8192)
rand.Read(original)
out := make([]byte, 9000)
b.ResetTimer()
for i := 0; i < b.N; i++ {
signPacket(privKey1[:], original, out)
}
}
func TestSignOpenPacket(t *testing.T) { func TestSignOpenPacket(t *testing.T) {
pubKey, privKey, err := sign.GenerateKey(rand.Reader) pubKey, privKey, err := sign.GenerateKey(rand.Reader)
if err != nil { if err != nil {
@ -127,3 +143,22 @@ func TestSignOpenPacket(t *testing.T) {
t.Fatal("not equal") t.Fatal("not equal")
} }
} }
func BenchmarkOpenPacket(b *testing.B) {
pubKey, privKey, err := sign.GenerateKey(rand.Reader)
if err != nil {
b.Fatal(err)
}
packet := make([]byte, MTU)
rand.Read(packet)
signedPacket := signPacket(privKey[:], packet, make([]byte, 9000))
out := make([]byte, BUFFER_SIZE)
b.ResetTimer()
for i := 0; i < b.N; i++ {
out, _ = openPacket(pubKey[:], signedPacket, out)
}
}

View File

@ -57,12 +57,10 @@ NEXT_PACKET:
} }
if nonce.Counter <= counters[nonce.StreamID][nonce.SourceIP] { if nonce.Counter <= counters[nonce.StreamID][nonce.SourceIP] {
log.Printf("Dropping packet with bad counter: %d <= %d", nonce.Counter, counters[nonce.StreamID][nonce.SourceIP]) log.Printf("Dropping packet with bad counter: -%d", counters[nonce.StreamID][nonce.SourceIP]-nonce.Counter)
goto NEXT_PACKET goto NEXT_PACKET
} }
counters[nonce.StreamID][nonce.SourceIP] = nonce.Counter
route = peer.router.GetRoute(nonce.SourceIP) route = peer.router.GetRoute(nonce.SourceIP)
if route == nil { if route == nil {
log.Printf("Dropping packet without route: %+v", nonce) log.Printf("Dropping packet without route: %+v", nonce)
@ -87,6 +85,9 @@ DECRYPT:
goto NEXT_PACKET goto NEXT_PACKET
} }
// Only updated after verification.
counters[nonce.StreamID][nonce.SourceIP] = nonce.Counter
switch nonce.StreamID { switch nonce.StreamID {
case STREAM_DATA: case STREAM_DATA:
goto WRITE_IFACE_DATA goto WRITE_IFACE_DATA
@ -119,6 +120,9 @@ VALIDATE_SIGNATURE:
goto NEXT_PACKET goto NEXT_PACKET
} }
// Only updated after verification.
counters[nonce.StreamID][nonce.SourceIP] = nonce.Counter
route = peer.router.GetRoute(nonce.DestIP) route = peer.router.GetRoute(nonce.DestIP)
if route == nil || !route.Up { if route == nil || !route.Up {
log.Printf("Dropping mediated packet, route not available: %v", nonce) log.Printf("Dropping mediated packet, route not available: %v", nonce)