Skip to content

Commit b952c16

Browse files
committed
rate limiting and selectivity of autonat svc
* limits addresses for a peer (at most 4 chosen) - fix #39 * clears addresses before dialing back - fix #38 * global rate limit of 30 responses per (1 - 1.25 min) - fix #36 * only dial back on the source IP - fix #32
1 parent 9ad01ee commit b952c16

File tree

2 files changed

+80
-9
lines changed

2 files changed

+80
-9
lines changed

p2p/host/autonat/svc.go

+50-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package autonat
22

33
import (
4+
"bytes"
45
"context"
6+
"math/rand"
7+
"net"
58
"sync"
69
"time"
710

@@ -24,8 +27,11 @@ const P_CIRCUIT = 290
2427
var (
2528
AutoNATServiceDialTimeout = 15 * time.Second
2629
AutoNATServiceResetInterval = 1 * time.Minute
30+
AutoNATServiceResetJitter = 15 * time.Second
2731

28-
AutoNATServiceThrottle = 3
32+
AutoNATServiceThrottle = 3
33+
AutoNATGlobalThrottle = 30
34+
AutoNATMaxPeerAddresses = 4
2935
)
3036

3137
// AutoNATService provides NAT autodetection services to other peers
@@ -34,8 +40,9 @@ type AutoNATService struct {
3440
dialer host.Host
3541

3642
// rate limiter
37-
mx sync.Mutex
38-
reqs map[peer.ID]int
43+
mx sync.Mutex
44+
reqs map[peer.ID]int
45+
globalReqs int
3946
}
4047

4148
// NewAutoNATService creates a new AutoNATService instance attached to a host
@@ -96,6 +103,21 @@ func (as *AutoNATService) handleStream(s network.Stream) {
96103
}
97104
}
98105

106+
// Optimistically extract the net.IP host from a multiaddress.
107+
func addrToIP(addr ma.Multiaddr) net.IP {
108+
if v4, err := addr.ValueForProtocol(ma.P_IP4); err == nil {
109+
if c, err := ma.NewComponent(ma.ProtocolWithCode(ma.P_IP4).Name, v4); err == nil {
110+
return net.IP(c.RawValue())
111+
}
112+
}
113+
if v6, err := addr.ValueForProtocol(ma.P_IP6); err == nil {
114+
if c, err := ma.NewComponent(ma.ProtocolWithCode(ma.P_IP6).Name, v6); err == nil {
115+
return net.IP(c.RawValue())
116+
}
117+
}
118+
return nil
119+
}
120+
99121
func (as *AutoNATService) handleDial(p peer.ID, obsaddr ma.Multiaddr, mpi *pb.Message_PeerInfo) *pb.Message_DialResponse {
100122
if mpi == nil {
101123
return newDialResponseError(pb.Message_E_BAD_REQUEST, "missing peer info")
@@ -113,13 +135,15 @@ func (as *AutoNATService) handleDial(p peer.ID, obsaddr ma.Multiaddr, mpi *pb.Me
113135
}
114136
}
115137

116-
addrs := make([]ma.Multiaddr, 0)
138+
addrs := make([]ma.Multiaddr, 0, AutoNATMaxPeerAddresses)
117139
seen := make(map[string]struct{})
118140

119141
// add observed addr to the list of addresses to dial
142+
var obsHost net.IP
120143
if !as.skipDial(obsaddr) {
121144
addrs = append(addrs, obsaddr)
122145
seen[obsaddr.String()] = struct{}{}
146+
obsHost = addrToIP(obsaddr)
123147
}
124148

125149
for _, maddr := range mpi.GetAddrs() {
@@ -129,10 +153,22 @@ func (as *AutoNATService) handleDial(p peer.ID, obsaddr ma.Multiaddr, mpi *pb.Me
129153
continue
130154
}
131155

156+
if len(addrs) >= AutoNATMaxPeerAddresses {
157+
continue
158+
}
159+
132160
if as.skipDial(addr) {
133161
continue
134162
}
135163

164+
if err != nil {
165+
log.Debugf("Unexpected public, non-IP multiaddr: %s", err)
166+
continue
167+
}
168+
if !bytes.Equal(obsHost, addrToIP(addr)) {
169+
continue
170+
}
171+
136172
str := addr.String()
137173
_, ok := seen[str]
138174
if ok {
@@ -169,16 +205,19 @@ func (as *AutoNATService) doDial(pi peer.AddrInfo) *pb.Message_DialResponse {
169205
// rate limit check
170206
as.mx.Lock()
171207
count := as.reqs[pi.ID]
172-
if count >= AutoNATServiceThrottle {
208+
if count >= AutoNATServiceThrottle || as.globalReqs >= AutoNATGlobalThrottle {
173209
as.mx.Unlock()
174210
return newDialResponseError(pb.Message_E_DIAL_REFUSED, "too many dials")
175211
}
176212
as.reqs[pi.ID] = count + 1
213+
as.globalReqs++
177214
as.mx.Unlock()
178215

179216
ctx, cancel := context.WithTimeout(as.ctx, AutoNATServiceDialTimeout)
180217
defer cancel()
181218

219+
as.dialer.Peerstore().ClearAddrs(pi.ID)
220+
182221
err := as.dialer.Connect(ctx, pi)
183222
if err != nil {
184223
log.Debugf("error dialing %s: %s", pi.ID.Pretty(), err.Error())
@@ -200,16 +239,18 @@ func (as *AutoNATService) doDial(pi peer.AddrInfo) *pb.Message_DialResponse {
200239
}
201240

202241
func (as *AutoNATService) resetRateLimiter() {
203-
ticker := time.NewTicker(AutoNATServiceResetInterval)
204-
defer ticker.Stop()
242+
timer := time.NewTimer(AutoNATServiceResetInterval)
243+
defer timer.Stop()
205244

206245
for {
207246
select {
208-
case <-ticker.C:
247+
case <-timer.C:
209248
as.mx.Lock()
210249
as.reqs = make(map[peer.ID]int)
250+
as.globalReqs = 0
211251
as.mx.Unlock()
212-
252+
jitter := rand.Float32() * float32(AutoNATServiceResetJitter)
253+
timer.Reset(AutoNATServiceResetInterval + time.Duration(int64(jitter)))
213254
case <-as.ctx.Done():
214255
return
215256
}

p2p/host/autonat/svc_test.go

+30
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/libp2p/go-libp2p-core/peer"
1212

1313
autonat "github.com/libp2p/go-libp2p-autonat"
14+
ma "github.com/multiformats/go-multiaddr"
1415
manet "github.com/multiformats/go-multiaddr-net"
1516
)
1617

@@ -101,6 +102,8 @@ func TestAutoNATServiceDialRateLimiter(t *testing.T) {
101102
AutoNATServiceThrottle = 1
102103
save4 := manet.Private4
103104
manet.Private4 = []*net.IPNet{}
105+
save5 := AutoNATServiceResetJitter
106+
AutoNATServiceResetJitter = 0 * time.Second
104107

105108
hs, _ := makeAutoNATService(ctx, t)
106109
hc, ac := makeAutoNATClient(ctx, t)
@@ -131,4 +134,31 @@ func TestAutoNATServiceDialRateLimiter(t *testing.T) {
131134
AutoNATServiceResetInterval = save2
132135
AutoNATServiceThrottle = save3
133136
manet.Private4 = save4
137+
AutoNATServiceResetJitter = save5
138+
}
139+
140+
func TestAddrToIP(t *testing.T) {
141+
addr, _ := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/0")
142+
if !addrToIP(addr).Equal(net.IPv4(127, 0, 0, 1)) {
143+
t.Fatal("addrToIP of ipv4 localhost incorrect!")
144+
}
145+
146+
addr, _ = ma.NewMultiaddr("/ip4/192.168.0.1/tcp/6")
147+
if !addrToIP(addr).Equal(net.IPv4(192, 168, 0, 1)) {
148+
t.Fatal("addrToIP of ipv4 incorrect!")
149+
}
150+
151+
addr, _ = ma.NewMultiaddr("/ip6/::ffff:127.0.0.1/tcp/111")
152+
if !addrToIP(addr).Equal(net.ParseIP("::ffff:127.0.0.1")) {
153+
t.Fatal("addrToIP of ipv6 incorrect!")
154+
}
155+
addr, _ = ma.NewMultiaddr("/ip6zone/eth0/ip6/fe80::1")
156+
if !addrToIP(addr).Equal(net.ParseIP("fe80::1")) {
157+
t.Fatal("addrToIP of ip6zone incorrect!")
158+
}
159+
160+
addr, _ = ma.NewMultiaddr("/unix/a/b/c/d")
161+
if addrToIP(addr) != nil {
162+
t.Fatal("invalid addrToIP populates")
163+
}
134164
}

0 commit comments

Comments
 (0)