510 lines
13 KiB
Go
510 lines
13 KiB
Go
package peer
|
|
|
|
import (
|
|
"net/netip"
|
|
"testing"
|
|
"time"
|
|
"vppn/m"
|
|
|
|
"git.crumpington.com/lib/go/ratelimiter"
|
|
)
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
type PeerStateControlMsg struct {
|
|
Peer RemotePeer
|
|
Packet any
|
|
}
|
|
|
|
type PeerStateTestHarness struct {
|
|
State PeerState
|
|
Published RemotePeer
|
|
Sent []PeerStateControlMsg
|
|
}
|
|
|
|
func NewPeerStateTestHarness() *PeerStateTestHarness {
|
|
h := &PeerStateTestHarness{}
|
|
|
|
keys := generateKeys()
|
|
|
|
state := &State{
|
|
publish: func(rp RemotePeer) {
|
|
h.Published = rp
|
|
},
|
|
sendControlPacket: func(rp RemotePeer, pkt Marshaller) {
|
|
h.Sent = append(h.Sent, PeerStateControlMsg{rp, pkt})
|
|
},
|
|
localIP: 2,
|
|
remoteIP: 3,
|
|
privKey: keys.PrivKey,
|
|
pubAddrs: newPubAddrStore(netip.AddrPort{}),
|
|
limiter: ratelimiter.New(ratelimiter.Config{
|
|
FillPeriod: 20 * time.Millisecond,
|
|
MaxWaitCount: 1,
|
|
}),
|
|
}
|
|
|
|
h.State = EnterStateDisconnected(state)
|
|
return h
|
|
}
|
|
|
|
func (h *PeerStateTestHarness) PeerUpdate(p *m.Peer) {
|
|
if s := h.State.OnPeerUpdate(p); s != nil {
|
|
h.State = s
|
|
}
|
|
}
|
|
|
|
func (h *PeerStateTestHarness) OnSyn(msg controlMsg[PacketSyn]) {
|
|
if s := h.State.OnSyn(msg); s != nil {
|
|
h.State = s
|
|
}
|
|
}
|
|
|
|
func (h *PeerStateTestHarness) OnProbe(msg controlMsg[PacketProbe]) {
|
|
if s := h.State.OnProbe(msg); s != nil {
|
|
h.State = s
|
|
}
|
|
}
|
|
|
|
func (h *PeerStateTestHarness) OnPingTimer() {
|
|
if s := h.State.OnPingTimer(); s != nil {
|
|
h.State = s
|
|
}
|
|
}
|
|
|
|
func (h *PeerStateTestHarness) ConfigServer_Public(t *testing.T) *StateServer {
|
|
keys := generateKeys()
|
|
|
|
state := h.State.(*StateDisconnected)
|
|
state.localAddr = addrPort4(1, 1, 1, 2, 200)
|
|
|
|
peer := &m.Peer{
|
|
PeerIP: 3,
|
|
PublicIP: []byte{1, 1, 1, 3},
|
|
Port: 456,
|
|
PubKey: keys.PubKey,
|
|
PubSignKey: keys.PubSignKey,
|
|
}
|
|
|
|
h.PeerUpdate(peer)
|
|
assertEqual(t, h.Published.Up, false)
|
|
return assertType[*StateServer](t, h.State)
|
|
}
|
|
|
|
func (h *PeerStateTestHarness) ConfigServer_Relayed(t *testing.T) *StateServer {
|
|
keys := generateKeys()
|
|
peer := &m.Peer{
|
|
PeerIP: 3,
|
|
Port: 456,
|
|
PubKey: keys.PubKey,
|
|
PubSignKey: keys.PubSignKey,
|
|
}
|
|
|
|
h.PeerUpdate(peer)
|
|
assertEqual(t, h.Published.Up, false)
|
|
return assertType[*StateServer](t, h.State)
|
|
}
|
|
|
|
func (h *PeerStateTestHarness) ConfigClientDirect(t *testing.T) *StateClientDirect {
|
|
keys := generateKeys()
|
|
peer := &m.Peer{
|
|
PeerIP: 3,
|
|
PublicIP: []byte{1, 2, 3, 4},
|
|
Port: 456,
|
|
PubKey: keys.PubKey,
|
|
PubSignKey: keys.PubSignKey,
|
|
}
|
|
|
|
h.PeerUpdate(peer)
|
|
assertEqual(t, h.Published.Up, false)
|
|
return assertType[*StateClientDirect](t, h.State)
|
|
}
|
|
|
|
func (h *PeerStateTestHarness) ConfigClientRelayed(t *testing.T) *StateClientRelayed {
|
|
keys := generateKeys()
|
|
|
|
state := h.State.(*StateDisconnected)
|
|
state.remoteIP = 1
|
|
|
|
peer := &m.Peer{
|
|
PeerIP: 3,
|
|
Port: 456,
|
|
PubKey: keys.PubKey,
|
|
PubSignKey: keys.PubSignKey,
|
|
}
|
|
|
|
h.PeerUpdate(peer)
|
|
assertEqual(t, h.Published.Up, false)
|
|
return assertType[*StateClientRelayed](t, h.State)
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
func TestPeerState_OnPeerUpdate_nilPeer(t *testing.T) {
|
|
h := NewPeerStateTestHarness()
|
|
h.PeerUpdate(nil)
|
|
assertType[*StateDisconnected](t, h.State)
|
|
}
|
|
|
|
func TestPeerState_OnPeerUpdate_publicLocalIsServer(t *testing.T) {
|
|
keys := generateKeys()
|
|
h := NewPeerStateTestHarness()
|
|
|
|
state := h.State.(*StateDisconnected)
|
|
state.localAddr = addrPort4(1, 1, 1, 2, 200)
|
|
|
|
peer := &m.Peer{
|
|
PeerIP: 3,
|
|
Port: 456,
|
|
PubKey: keys.PubKey,
|
|
PubSignKey: keys.PubSignKey,
|
|
}
|
|
|
|
h.PeerUpdate(peer)
|
|
assertEqual(t, h.Published.Up, false)
|
|
assertType[*StateServer](t, h.State)
|
|
}
|
|
|
|
func TestPeerState_OnPeerUpdate_serverDirect(t *testing.T) {
|
|
h := NewPeerStateTestHarness()
|
|
h.ConfigServer_Public(t)
|
|
}
|
|
|
|
func TestPeerState_OnPeerUpdate_serverRelayed(t *testing.T) {
|
|
h := NewPeerStateTestHarness()
|
|
h.ConfigServer_Relayed(t)
|
|
}
|
|
|
|
func TestPeerState_OnPeerUpdate_clientDirect(t *testing.T) {
|
|
h := NewPeerStateTestHarness()
|
|
h.ConfigClientDirect(t)
|
|
}
|
|
|
|
func TestPeerState_OnPeerUpdate_clientRelayed(t *testing.T) {
|
|
h := NewPeerStateTestHarness()
|
|
h.ConfigClientRelayed(t)
|
|
}
|
|
|
|
func TestStateServer_directSyn(t *testing.T) {
|
|
h := NewPeerStateTestHarness()
|
|
h.ConfigServer_Relayed(t)
|
|
|
|
assertEqual(t, h.Published.Up, false)
|
|
|
|
synMsg := controlMsg[PacketSyn]{
|
|
SrcIP: 3,
|
|
SrcAddr: addrPort4(1, 1, 1, 3, 300),
|
|
Packet: PacketSyn{
|
|
TraceID: newTraceID(),
|
|
//SentAt: time.Now().UnixMilli(),
|
|
//SharedKeyType: 1,
|
|
Direct: true,
|
|
},
|
|
}
|
|
|
|
h.State.OnSyn(synMsg)
|
|
|
|
assertEqual(t, len(h.Sent), 1)
|
|
ack := assertType[PacketAck](t, h.Sent[0].Packet)
|
|
assertEqual(t, ack.TraceID, synMsg.Packet.TraceID)
|
|
assertEqual(t, h.Sent[0].Peer.IP, 3)
|
|
assertEqual(t, ack.PossibleAddrs[0].IsValid(), false)
|
|
assertEqual(t, h.Published.Up, true)
|
|
}
|
|
|
|
func TestStateServer_relayedSyn(t *testing.T) {
|
|
h := NewPeerStateTestHarness()
|
|
state := h.ConfigServer_Relayed(t)
|
|
|
|
state.pubAddrs.Store(addrPort4(4, 5, 6, 7, 1234))
|
|
|
|
assertEqual(t, h.Published.Up, false)
|
|
|
|
synMsg := controlMsg[PacketSyn]{
|
|
SrcIP: 3,
|
|
SrcAddr: addrPort4(1, 1, 1, 3, 300),
|
|
Packet: PacketSyn{
|
|
TraceID: newTraceID(),
|
|
//SentAt: time.Now().UnixMilli(),
|
|
//SharedKeyType: 1,
|
|
Direct: false,
|
|
},
|
|
}
|
|
synMsg.Packet.PossibleAddrs[0] = addrPort4(1, 1, 1, 3, 300)
|
|
synMsg.Packet.PossibleAddrs[1] = addrPort4(2, 2, 2, 3, 300)
|
|
|
|
h.State.OnSyn(synMsg)
|
|
|
|
assertEqual(t, len(h.Sent), 3)
|
|
|
|
ack := assertType[PacketAck](t, h.Sent[0].Packet)
|
|
assertEqual(t, ack.TraceID, synMsg.Packet.TraceID)
|
|
assertEqual(t, h.Sent[0].Peer.IP, 3)
|
|
assertEqual(t, ack.PossibleAddrs[0], addrPort4(4, 5, 6, 7, 1234))
|
|
assertEqual(t, ack.PossibleAddrs[1].IsValid(), false)
|
|
assertEqual(t, h.Published.Up, true)
|
|
|
|
assertType[PacketProbe](t, h.Sent[1].Packet)
|
|
assertType[PacketProbe](t, h.Sent[2].Packet)
|
|
assertEqual(t, h.Sent[1].Peer.DirectAddr, addrPort4(1, 1, 1, 3, 300))
|
|
assertEqual(t, h.Sent[2].Peer.DirectAddr, addrPort4(2, 2, 2, 3, 300))
|
|
}
|
|
|
|
func TestStateServer_onProbe(t *testing.T) {
|
|
h := NewPeerStateTestHarness()
|
|
h.ConfigServer_Relayed(t)
|
|
assertEqual(t, h.Published.Up, false)
|
|
|
|
probeMsg := controlMsg[PacketProbe]{
|
|
SrcIP: 3,
|
|
SrcAddr: addrPort4(1, 1, 1, 3, 300),
|
|
Packet: PacketProbe{TraceID: newTraceID()},
|
|
}
|
|
|
|
h.State.OnProbe(probeMsg)
|
|
|
|
assertEqual(t, len(h.Sent), 1)
|
|
|
|
probe := assertType[PacketProbe](t, h.Sent[0].Packet)
|
|
assertEqual(t, probe.TraceID, probeMsg.Packet.TraceID)
|
|
assertEqual(t, h.Sent[0].Peer.DirectAddr, addrPort4(1, 1, 1, 3, 300))
|
|
}
|
|
|
|
func TestStateServer_OnPingTimer_timeout(t *testing.T) {
|
|
h := NewPeerStateTestHarness()
|
|
h.ConfigServer_Relayed(t)
|
|
|
|
synMsg := controlMsg[PacketSyn]{
|
|
SrcIP: 3,
|
|
SrcAddr: addrPort4(1, 1, 1, 3, 300),
|
|
Packet: PacketSyn{
|
|
TraceID: newTraceID(),
|
|
//SentAt: time.Now().UnixMilli(),
|
|
//SharedKeyType: 1,
|
|
Direct: true,
|
|
},
|
|
}
|
|
|
|
h.State.OnSyn(synMsg)
|
|
assertEqual(t, len(h.Sent), 1)
|
|
assertEqual(t, h.Published.Up, true)
|
|
|
|
// Ping shouldn't timeout.
|
|
h.OnPingTimer()
|
|
assertEqual(t, h.Published.Up, true)
|
|
|
|
// Advance the time, then ping.
|
|
state := assertType[*StateServer](t, h.State)
|
|
state.lastSeen = time.Now().Add(-timeoutInterval - time.Second)
|
|
|
|
h.OnPingTimer()
|
|
assertEqual(t, h.Published.Up, false)
|
|
}
|
|
|
|
func TestStateClientDirect_OnAck(t *testing.T) {
|
|
h := NewPeerStateTestHarness()
|
|
h.ConfigClientDirect(t)
|
|
|
|
assertEqual(t, h.Published.Up, false)
|
|
|
|
// On entering the state, a SYN should have been sent.
|
|
assertEqual(t, len(h.Sent), 1)
|
|
syn := assertType[PacketSyn](t, h.Sent[0].Packet)
|
|
|
|
ack := controlMsg[PacketAck]{
|
|
Packet: PacketAck{TraceID: syn.TraceID},
|
|
}
|
|
h.State.OnAck(ack)
|
|
assertEqual(t, h.Published.Up, true)
|
|
}
|
|
|
|
func TestStateClientDirect_OnAck_incorrectTraceID(t *testing.T) {
|
|
h := NewPeerStateTestHarness()
|
|
h.ConfigClientDirect(t)
|
|
|
|
assertEqual(t, h.Published.Up, false)
|
|
|
|
// On entering the state, a SYN should have been sent.
|
|
assertEqual(t, len(h.Sent), 1)
|
|
syn := assertType[PacketSyn](t, h.Sent[0].Packet)
|
|
|
|
ack := controlMsg[PacketAck]{
|
|
Packet: PacketAck{TraceID: syn.TraceID + 1},
|
|
}
|
|
h.State.OnAck(ack)
|
|
assertEqual(t, h.Published.Up, false)
|
|
}
|
|
|
|
func TestStateClientDirect_OnPingTimer(t *testing.T) {
|
|
h := NewPeerStateTestHarness()
|
|
h.ConfigClientDirect(t)
|
|
|
|
// On entering the state, a SYN should have been sent.
|
|
assertEqual(t, len(h.Sent), 1)
|
|
assertType[PacketSyn](t, h.Sent[0].Packet)
|
|
|
|
h.OnPingTimer()
|
|
|
|
// On ping timer, another syn should be sent. Additionally, we should remain
|
|
// in the same state.
|
|
assertEqual(t, len(h.Sent), 2)
|
|
assertType[PacketSyn](t, h.Sent[1].Packet)
|
|
assertType[*StateClientDirect](t, h.State)
|
|
assertEqual(t, h.Published.Up, false)
|
|
}
|
|
|
|
func TestStateClientDirect_OnPingTimer_timeout(t *testing.T) {
|
|
h := NewPeerStateTestHarness()
|
|
h.ConfigClientDirect(t)
|
|
|
|
assertEqual(t, h.Published.Up, false)
|
|
|
|
// On entering the state, a SYN should have been sent.
|
|
assertEqual(t, len(h.Sent), 1)
|
|
syn := assertType[PacketSyn](t, h.Sent[0].Packet)
|
|
|
|
ack := controlMsg[PacketAck]{
|
|
Packet: PacketAck{TraceID: syn.TraceID},
|
|
}
|
|
h.State.OnAck(ack)
|
|
assertEqual(t, h.Published.Up, true)
|
|
|
|
state := assertType[*StateClientDirect](t, h.State)
|
|
state.lastSeen = time.Now().Add(-(timeoutInterval + time.Second))
|
|
|
|
h.OnPingTimer()
|
|
|
|
// On ping timer, we should timeout, causing the client to reset. Another SYN
|
|
// will be sent when re-entering the state, but the connection should be down.
|
|
assertEqual(t, len(h.Sent), 2)
|
|
assertType[PacketSyn](t, h.Sent[1].Packet)
|
|
assertType[*StateClientDirect](t, h.State)
|
|
assertEqual(t, h.Published.Up, false)
|
|
}
|
|
|
|
func TestStateClientRelayed_OnAck(t *testing.T) {
|
|
h := NewPeerStateTestHarness()
|
|
h.ConfigClientRelayed(t)
|
|
|
|
assertEqual(t, h.Published.Up, false)
|
|
|
|
// On entering the state, a SYN should have been sent.
|
|
assertEqual(t, len(h.Sent), 1)
|
|
syn := assertType[PacketSyn](t, h.Sent[0].Packet)
|
|
|
|
ack := controlMsg[PacketAck]{
|
|
Packet: PacketAck{TraceID: syn.TraceID},
|
|
}
|
|
h.State.OnAck(ack)
|
|
assertEqual(t, h.Published.Up, true)
|
|
}
|
|
|
|
func TestStateClientRelayed_OnPingTimer_noAddrs(t *testing.T) {
|
|
h := NewPeerStateTestHarness()
|
|
h.ConfigClientRelayed(t)
|
|
|
|
assertEqual(t, h.Published.Up, false)
|
|
|
|
// On entering the state, a SYN should have been sent.
|
|
assertEqual(t, len(h.Sent), 1)
|
|
|
|
// If we haven't had an ack yet, we won't have addresses to probe. Therefore
|
|
// we'll have just one more syn packet sent.
|
|
h.OnPingTimer()
|
|
assertEqual(t, len(h.Sent), 2)
|
|
}
|
|
|
|
func TestStateClientRelayed_OnPingTimer_withAddrs(t *testing.T) {
|
|
h := NewPeerStateTestHarness()
|
|
h.ConfigClientRelayed(t)
|
|
|
|
assertEqual(t, h.Published.Up, false)
|
|
|
|
// On entering the state, a SYN should have been sent.
|
|
assertEqual(t, len(h.Sent), 1)
|
|
|
|
syn := assertType[PacketSyn](t, h.Sent[0].Packet)
|
|
|
|
ack := controlMsg[PacketAck]{Packet: PacketAck{TraceID: syn.TraceID}}
|
|
ack.Packet.PossibleAddrs[0] = addrPort4(1, 1, 1, 1, 300)
|
|
ack.Packet.PossibleAddrs[1] = addrPort4(1, 1, 1, 2, 300)
|
|
|
|
h.State.OnAck(ack)
|
|
|
|
// Add a local discovery address. Note that the port will be configured port
|
|
// and no the one provided here.
|
|
h.State.OnLocalDiscovery(controlMsg[PacketLocalDiscovery]{
|
|
SrcIP: 3,
|
|
SrcAddr: addrPort4(2, 2, 2, 3, 300),
|
|
})
|
|
|
|
// We should see one SYN and three probe packets.
|
|
h.OnPingTimer()
|
|
assertEqual(t, len(h.Sent), 5)
|
|
assertType[PacketSyn](t, h.Sent[1].Packet)
|
|
assertType[PacketProbe](t, h.Sent[2].Packet)
|
|
assertType[PacketProbe](t, h.Sent[3].Packet)
|
|
assertType[PacketProbe](t, h.Sent[4].Packet)
|
|
|
|
assertEqual(t, h.Sent[2].Peer.DirectAddr, addrPort4(1, 1, 1, 1, 300))
|
|
assertEqual(t, h.Sent[3].Peer.DirectAddr, addrPort4(1, 1, 1, 2, 300))
|
|
assertEqual(t, h.Sent[4].Peer.DirectAddr, addrPort4(2, 2, 2, 3, 456))
|
|
}
|
|
|
|
func TestStateClientRelayed_OnPingTimer_timeout(t *testing.T) {
|
|
h := NewPeerStateTestHarness()
|
|
h.ConfigClientRelayed(t)
|
|
|
|
// On entering the state, a SYN should have been sent.
|
|
assertEqual(t, len(h.Sent), 1)
|
|
syn := assertType[PacketSyn](t, h.Sent[0].Packet)
|
|
|
|
ack := controlMsg[PacketAck]{
|
|
Packet: PacketAck{TraceID: syn.TraceID},
|
|
}
|
|
h.State.OnAck(ack)
|
|
assertEqual(t, h.Published.Up, true)
|
|
|
|
state := assertType[*StateClientRelayed](t, h.State)
|
|
state.lastSeen = time.Now().Add(-(timeoutInterval + time.Second))
|
|
|
|
h.OnPingTimer()
|
|
|
|
// On ping timer, we should timeout, causing the client to reset. Another SYN
|
|
// will be sent when re-entering the state, but the connection should be down.
|
|
assertEqual(t, len(h.Sent), 2)
|
|
assertType[PacketSyn](t, h.Sent[1].Packet)
|
|
assertType[*StateClientRelayed](t, h.State)
|
|
assertEqual(t, h.Published.Up, false)
|
|
}
|
|
|
|
func TestStateClientRelayed_OnProbe_unknownAddr(t *testing.T) {
|
|
h := NewPeerStateTestHarness()
|
|
h.ConfigClientRelayed(t)
|
|
|
|
h.OnProbe(controlMsg[PacketProbe]{
|
|
Packet: PacketProbe{TraceID: newTraceID()},
|
|
})
|
|
|
|
assertType[*StateClientRelayed](t, h.State)
|
|
}
|
|
|
|
func TestStateClientRelayed_OnProbe_upgradeDirect(t *testing.T) {
|
|
h := NewPeerStateTestHarness()
|
|
h.ConfigClientRelayed(t)
|
|
|
|
syn := assertType[PacketSyn](t, h.Sent[0].Packet)
|
|
|
|
ack := controlMsg[PacketAck]{Packet: PacketAck{TraceID: syn.TraceID}}
|
|
ack.Packet.PossibleAddrs[0] = addrPort4(1, 1, 1, 1, 300)
|
|
ack.Packet.PossibleAddrs[1] = addrPort4(1, 1, 1, 2, 300)
|
|
|
|
h.State.OnAck(ack)
|
|
h.OnPingTimer()
|
|
|
|
probe := assertType[PacketProbe](t, h.Sent[2].Packet)
|
|
h.OnProbe(controlMsg[PacketProbe]{Packet: probe})
|
|
|
|
assertType[*StateClientDirect](t, h.State)
|
|
}
|