Skip to content

Commit 93014e1

Browse files
authored
fix(dcutr): Fix end to end tests and add legacy behavior flag (default=true) (#3044)
1 parent 3e51326 commit 93014e1

File tree

11 files changed

+1205
-221
lines changed

11 files changed

+1205
-221
lines changed

p2p/net/simconn/router.go

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
package simconn
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"net"
7+
"sync"
8+
"time"
9+
)
10+
11+
type PacketReciever interface {
12+
RecvPacket(p Packet)
13+
}
14+
15+
// PerfectRouter is a router that has no latency or jitter and can route to
16+
// every node
17+
type PerfectRouter struct {
18+
mu sync.Mutex
19+
nodes map[net.Addr]PacketReciever
20+
}
21+
22+
// SendPacket implements Router.
23+
func (r *PerfectRouter) SendPacket(p Packet) error {
24+
r.mu.Lock()
25+
defer r.mu.Unlock()
26+
conn, ok := r.nodes[p.To]
27+
if !ok {
28+
return errors.New("unknown destination")
29+
}
30+
31+
conn.RecvPacket(p)
32+
return nil
33+
}
34+
35+
func (r *PerfectRouter) AddNode(addr net.Addr, conn PacketReciever) {
36+
r.mu.Lock()
37+
defer r.mu.Unlock()
38+
if r.nodes == nil {
39+
r.nodes = make(map[net.Addr]PacketReciever)
40+
}
41+
r.nodes[addr] = conn
42+
}
43+
44+
func (r *PerfectRouter) RemoveNode(addr net.Addr) {
45+
delete(r.nodes, addr)
46+
}
47+
48+
var _ Router = &PerfectRouter{}
49+
50+
type DelayedPacketReciever struct {
51+
inner PacketReciever
52+
delay time.Duration
53+
}
54+
55+
func (r *DelayedPacketReciever) RecvPacket(p Packet) {
56+
time.AfterFunc(r.delay, func() { r.inner.RecvPacket(p) })
57+
}
58+
59+
type FixedLatencyRouter struct {
60+
PerfectRouter
61+
latency time.Duration
62+
}
63+
64+
func (r *FixedLatencyRouter) SendPacket(p Packet) error {
65+
return r.PerfectRouter.SendPacket(p)
66+
}
67+
68+
func (r *FixedLatencyRouter) AddNode(addr net.Addr, conn PacketReciever) {
69+
r.PerfectRouter.AddNode(addr, &DelayedPacketReciever{
70+
inner: conn,
71+
delay: r.latency,
72+
})
73+
}
74+
75+
var _ Router = &FixedLatencyRouter{}
76+
77+
type simpleNodeFirewall struct {
78+
mu sync.Mutex
79+
publiclyReachable bool
80+
packetsOutTo map[string]struct{}
81+
node *SimConn
82+
}
83+
84+
func (f *simpleNodeFirewall) MarkPacketSentOut(p Packet) {
85+
f.mu.Lock()
86+
defer f.mu.Unlock()
87+
if f.packetsOutTo == nil {
88+
f.packetsOutTo = make(map[string]struct{})
89+
}
90+
f.packetsOutTo[p.To.String()] = struct{}{}
91+
}
92+
93+
func (f *simpleNodeFirewall) IsPacketInAllowed(p Packet) bool {
94+
f.mu.Lock()
95+
defer f.mu.Unlock()
96+
if f.publiclyReachable {
97+
return true
98+
}
99+
100+
_, ok := f.packetsOutTo[p.From.String()]
101+
return ok
102+
}
103+
104+
func (f *simpleNodeFirewall) String() string {
105+
return fmt.Sprintf("public: %v, packetsOutTo: %v", f.publiclyReachable, f.packetsOutTo)
106+
}
107+
108+
type SimpleFirewallRouter struct {
109+
mu sync.Mutex
110+
nodes map[string]*simpleNodeFirewall
111+
}
112+
113+
func (r *SimpleFirewallRouter) String() string {
114+
r.mu.Lock()
115+
defer r.mu.Unlock()
116+
nodes := make([]string, 0, len(r.nodes))
117+
for _, node := range r.nodes {
118+
nodes = append(nodes, node.String())
119+
}
120+
return fmt.Sprintf("%v", nodes)
121+
}
122+
123+
func (r *SimpleFirewallRouter) SendPacket(p Packet) error {
124+
r.mu.Lock()
125+
defer r.mu.Unlock()
126+
toNode, exists := r.nodes[p.To.String()]
127+
if !exists {
128+
return errors.New("unknown destination")
129+
}
130+
131+
// Record that this node is sending a packet to the destination
132+
fromNode, exists := r.nodes[p.From.String()]
133+
if !exists {
134+
return errors.New("unknown source")
135+
}
136+
fromNode.MarkPacketSentOut(p)
137+
138+
if !toNode.IsPacketInAllowed(p) {
139+
return nil // Silently drop blocked packets
140+
}
141+
142+
toNode.node.RecvPacket(p)
143+
return nil
144+
}
145+
146+
func (r *SimpleFirewallRouter) AddNode(addr net.Addr, conn *SimConn) {
147+
r.mu.Lock()
148+
defer r.mu.Unlock()
149+
if r.nodes == nil {
150+
r.nodes = make(map[string]*simpleNodeFirewall)
151+
}
152+
r.nodes[addr.String()] = &simpleNodeFirewall{
153+
packetsOutTo: make(map[string]struct{}),
154+
node: conn,
155+
}
156+
}
157+
158+
func (r *SimpleFirewallRouter) AddPubliclyReachableNode(addr net.Addr, conn *SimConn) {
159+
r.mu.Lock()
160+
defer r.mu.Unlock()
161+
if r.nodes == nil {
162+
r.nodes = make(map[string]*simpleNodeFirewall)
163+
}
164+
r.nodes[addr.String()] = &simpleNodeFirewall{
165+
publiclyReachable: true,
166+
node: conn,
167+
}
168+
}
169+
170+
func (r *SimpleFirewallRouter) RemoveNode(addr net.Addr) {
171+
r.mu.Lock()
172+
defer r.mu.Unlock()
173+
if r.nodes == nil {
174+
return
175+
}
176+
delete(r.nodes, addr.String())
177+
}
178+
179+
var _ Router = &SimpleFirewallRouter{}

p2p/net/simconn/simconn.go

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
package simconn
2+
3+
import (
4+
"errors"
5+
"net"
6+
"slices"
7+
"sync"
8+
"sync/atomic"
9+
"time"
10+
)
11+
12+
var ErrDeadlineExceeded = errors.New("deadline exceeded")
13+
14+
type Router interface {
15+
SendPacket(p Packet) error
16+
}
17+
18+
type Packet struct {
19+
To net.Addr
20+
From net.Addr
21+
buf []byte
22+
}
23+
24+
type SimConn struct {
25+
mu sync.Mutex
26+
closed bool
27+
closedChan chan struct{}
28+
29+
packetsSent atomic.Uint64
30+
packetsRcvd atomic.Uint64
31+
bytesSent atomic.Int64
32+
bytesRcvd atomic.Int64
33+
34+
router Router
35+
36+
myAddr *net.UDPAddr
37+
myLocalAddr net.Addr
38+
packetsToRead chan Packet
39+
40+
readDeadline time.Time
41+
writeDeadline time.Time
42+
}
43+
44+
// NewSimConn creates a new simulated connection with the specified parameters
45+
func NewSimConn(addr *net.UDPAddr, rtr Router) *SimConn {
46+
return &SimConn{
47+
router: rtr,
48+
myAddr: addr,
49+
packetsToRead: make(chan Packet, 512), // buffered channel to prevent blocking
50+
closedChan: make(chan struct{}),
51+
}
52+
}
53+
54+
type ConnStats struct {
55+
BytesSent int
56+
BytesRcvd int
57+
PacketsSent int
58+
PacketsRcvd int
59+
}
60+
61+
func (c *SimConn) Stats() ConnStats {
62+
return ConnStats{
63+
BytesSent: int(c.bytesSent.Load()),
64+
BytesRcvd: int(c.bytesRcvd.Load()),
65+
PacketsSent: int(c.packetsSent.Load()),
66+
PacketsRcvd: int(c.packetsRcvd.Load()),
67+
}
68+
}
69+
70+
// SetLocalAddr only changes what `.LocalAddr()` returns.
71+
// Packets will still come From the initially configured addr.
72+
func (c *SimConn) SetLocalAddr(addr net.Addr) {
73+
c.myLocalAddr = addr
74+
}
75+
76+
func (c *SimConn) RecvPacket(p Packet) {
77+
c.mu.Lock()
78+
if c.closed {
79+
c.mu.Unlock()
80+
return
81+
}
82+
c.mu.Unlock()
83+
c.packetsRcvd.Add(1)
84+
c.bytesRcvd.Add(int64(len(p.buf)))
85+
86+
select {
87+
case c.packetsToRead <- p:
88+
default:
89+
// drop the packet if the channel is full
90+
}
91+
}
92+
93+
var _ net.PacketConn = &SimConn{}
94+
95+
// Close implements net.PacketConn
96+
func (c *SimConn) Close() error {
97+
c.mu.Lock()
98+
defer c.mu.Unlock()
99+
if c.closed {
100+
return nil
101+
}
102+
c.closed = true
103+
close(c.closedChan)
104+
return nil
105+
}
106+
107+
// ReadFrom implements net.PacketConn
108+
func (c *SimConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
109+
c.mu.Lock()
110+
if c.closed {
111+
c.mu.Unlock()
112+
return 0, nil, net.ErrClosed
113+
}
114+
deadline := c.readDeadline
115+
c.mu.Unlock()
116+
117+
if !deadline.IsZero() && time.Now().After(deadline) {
118+
return 0, nil, ErrDeadlineExceeded
119+
}
120+
121+
var pkt Packet
122+
if !deadline.IsZero() {
123+
select {
124+
case pkt = <-c.packetsToRead:
125+
case <-time.After(time.Until(deadline)):
126+
return 0, nil, ErrDeadlineExceeded
127+
}
128+
} else {
129+
pkt = <-c.packetsToRead
130+
}
131+
132+
n = copy(p, pkt.buf)
133+
// if the provided buffer is not enough to read the whole packet, we drop
134+
// the rest of the data. this is similar to what `recvfrom` does on Linux
135+
// and macOS.
136+
return n, pkt.From, nil
137+
}
138+
139+
// WriteTo implements net.PacketConn
140+
func (c *SimConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
141+
c.mu.Lock()
142+
if c.closed {
143+
c.mu.Unlock()
144+
return 0, net.ErrClosed
145+
}
146+
deadline := c.writeDeadline
147+
c.mu.Unlock()
148+
149+
if !deadline.IsZero() && time.Now().After(deadline) {
150+
return 0, ErrDeadlineExceeded
151+
}
152+
153+
c.packetsSent.Add(1)
154+
c.bytesSent.Add(int64(len(p)))
155+
156+
pkt := Packet{
157+
From: c.myAddr,
158+
To: addr,
159+
buf: slices.Clone(p),
160+
}
161+
return len(p), c.router.SendPacket(pkt)
162+
}
163+
164+
func (c *SimConn) UnicastAddr() net.Addr {
165+
return c.myAddr
166+
}
167+
168+
// LocalAddr implements net.PacketConn
169+
func (c *SimConn) LocalAddr() net.Addr {
170+
if c.myLocalAddr != nil {
171+
return c.myLocalAddr
172+
}
173+
return c.myAddr
174+
}
175+
176+
// SetDeadline implements net.PacketConn
177+
func (c *SimConn) SetDeadline(t time.Time) error {
178+
c.mu.Lock()
179+
defer c.mu.Unlock()
180+
c.readDeadline = t
181+
c.writeDeadline = t
182+
return nil
183+
}
184+
185+
// SetReadDeadline implements net.PacketConn
186+
func (c *SimConn) SetReadDeadline(t time.Time) error {
187+
c.mu.Lock()
188+
defer c.mu.Unlock()
189+
c.readDeadline = t
190+
return nil
191+
}
192+
193+
// SetWriteDeadline implements net.PacketConn
194+
func (c *SimConn) SetWriteDeadline(t time.Time) error {
195+
c.mu.Lock()
196+
defer c.mu.Unlock()
197+
c.writeDeadline = t
198+
return nil
199+
}
200+
201+
func IntToPublicIPv4(n int) net.IP {
202+
n += 1
203+
// Avoid private IP ranges
204+
b := make([]byte, 4)
205+
b[0] = byte((n>>24)&0xFF | 1)
206+
b[1] = byte((n >> 16) & 0xFF)
207+
b[2] = byte((n >> 8) & 0xFF)
208+
b[3] = byte(n & 0xFF)
209+
210+
ip := net.IPv4(b[0], b[1], b[2], b[3])
211+
212+
// Check and modify if it's in private ranges
213+
if ip.IsPrivate() {
214+
b[0] = 1 // Use 1.x.x.x as public range
215+
}
216+
217+
return ip
218+
}

0 commit comments

Comments
 (0)