/
connection_gater.go
214 lines (197 loc) · 6.63 KB
/
connection_gater.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
package p2p
import (
"fmt"
"github.com/amazechain/amc/conf"
"net"
"runtime"
"github.com/libp2p/go-libp2p/core/control"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr/net"
)
const (
// Limit for rate limiter when processing new inbound dials.
ipLimit = 4
// Burst limit for inbound dials.
ipBurst = 8
// High watermark buffer signifies the buffer till which
// we will handle inbound requests.
highWatermarkBuffer = 10
)
// InterceptPeerDial tests whether we're permitted to Dial the specified peer.
func (_ *Service) InterceptPeerDial(_ peer.ID) (allow bool) {
return true
}
// InterceptAddrDial tests whether we're permitted to dial the specified
// multiaddr for the given peer.
func (s *Service) InterceptAddrDial(pid peer.ID, m multiaddr.Multiaddr) (allow bool) {
// Disallow bad peers from dialing in.
if s.peers.IsBad(pid) {
return false
}
return filterConnections(s.addrFilter, m)
}
// InterceptAccept checks whether the incidental inbound connection is allowed.
func (s *Service) InterceptAccept(n network.ConnMultiaddrs) (allow bool) {
// Deny all incoming connections before we are ready
if !s.started {
return false
}
if !s.validateDial(n.RemoteMultiaddr()) {
// Allow other go-routines to run in the event
// we receive a large amount of junk connections.
runtime.Gosched()
log.Trace("Not accepting inbound dial from ip address", "peer", n.RemoteMultiaddr(), "reason", "exceeded dial limit")
return false
}
if s.isPeerAtLimit(true /* inbound */) {
log.Trace("Not accepting inbound dial", "peer", n.RemoteMultiaddr(), "reason", "at peer limit")
return false
}
return filterConnections(s.addrFilter, n.RemoteMultiaddr())
}
// InterceptSecured tests whether a given connection, now authenticated,
// is allowed.
func (_ *Service) InterceptSecured(_ network.Direction, _ peer.ID, _ network.ConnMultiaddrs) (allow bool) {
return true
}
// InterceptUpgraded tests whether a fully capable connection is allowed.
func (_ *Service) InterceptUpgraded(_ network.Conn) (allow bool, reason control.DisconnectReason) {
return true, 0
}
func (s *Service) validateDial(addr multiaddr.Multiaddr) bool {
ip, err := manet.ToIP(addr)
if err != nil {
return false
}
remaining := s.ipLimiter.Remaining(ip.String())
if remaining <= 0 {
return false
}
s.ipLimiter.Add(ip.String(), 1)
return true
}
var privateCIDRList = []string{
// Private ip addresses specified by rfc-1918.
// See: https://tools.ietf.org/html/rfc1918
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
// Reserved address space for CGN devices, specified by rfc-6598
// See: https://tools.ietf.org/html/rfc6598
"100.64.0.0/10",
// IPv4 Link-Local addresses, specified by rfc-3926
// See: https://tools.ietf.org/html/rfc3927
"169.254.0.0/16",
}
// configureFilter looks at the provided allow lists and
// deny lists to appropriately create a filter.
func configureFilter(cfg *conf.P2PConfig) (*multiaddr.Filters, error) {
addrFilter := multiaddr.NewFilters()
var privErr error
switch {
case cfg.AllowListCIDR == "public":
cfg.DenyListCIDR = append(cfg.DenyListCIDR, privateCIDRList...)
case cfg.AllowListCIDR == "private":
addrFilter, privErr = privateCIDRFilter(addrFilter, multiaddr.ActionAccept)
if privErr != nil {
return nil, privErr
}
case cfg.AllowListCIDR != "":
_, ipnet, err := net.ParseCIDR(cfg.AllowListCIDR)
if err != nil {
return nil, err
}
addrFilter.AddFilter(*ipnet, multiaddr.ActionAccept)
}
// Configure from provided deny list in the config.
if len(cfg.DenyListCIDR) > 0 {
for _, cidr := range cfg.DenyListCIDR {
// If an entry in the deny list is "private", we iterate through the
// private addresses and add them to the filter. Likewise, if the deny
// list is "public", then we add all private address to the accept filter,
switch {
case cidr == "private":
addrFilter, privErr = privateCIDRFilter(addrFilter, multiaddr.ActionDeny)
if privErr != nil {
return nil, privErr
}
continue
case cidr == "public":
addrFilter, privErr = privateCIDRFilter(addrFilter, multiaddr.ActionAccept)
if privErr != nil {
return nil, privErr
}
continue
}
_, ipnet, err := net.ParseCIDR(cidr)
if err != nil {
return nil, err
}
// Check if the address already has an action associated with it
// If this address was previously accepted, log a warning before placing
// it in the deny filter
action, _ := addrFilter.ActionForFilter(*ipnet)
if action == multiaddr.ActionAccept {
log.Warn(fmt.Sprintf("Address %s is in conflict with previous rule.", ipnet.String()))
}
addrFilter.AddFilter(*ipnet, multiaddr.ActionDeny)
}
}
return addrFilter, nil
}
// helper function to either accept or deny all private addresses
// if a new rule for a private address is in conflict with a previous one, log a warning
func privateCIDRFilter(addrFilter *multiaddr.Filters, action multiaddr.Action) (*multiaddr.Filters, error) {
for _, privCidr := range privateCIDRList {
_, ipnet, err := net.ParseCIDR(privCidr)
if err != nil {
return nil, err
}
// Get the current filter action for the address
// If it conflicts with the action given by the function call,
// log a warning
curAction, _ := addrFilter.ActionForFilter(*ipnet)
switch {
case action == multiaddr.ActionAccept:
if curAction == multiaddr.ActionDeny {
log.Warn(fmt.Sprintf("Address %s is in conflict with previous rule.", ipnet.String()))
}
case action == multiaddr.ActionDeny:
if curAction == multiaddr.ActionAccept {
log.Warn(fmt.Sprintf("Address %s is in conflict with previous rule.", ipnet.String()))
}
}
addrFilter.AddFilter(*ipnet, action)
}
return addrFilter, nil
}
// filterConnections checks the appropriate ip subnets from our
// filter and decides what to do with them. By default libp2p
// accepts all incoming dials, so if we have an allow list
// we will reject all inbound dials except for those in the
// appropriate ip subnets.
func filterConnections(f *multiaddr.Filters, a multiaddr.Multiaddr) bool {
acceptedNets := f.FiltersForAction(multiaddr.ActionAccept)
restrictConns := len(acceptedNets) != 0
// If we have an allow list added in, we by default reject all
// connection attempts except for those coming in from the
// appropriate ip subnets.
if restrictConns {
ip, err := manet.ToIP(a)
if err != nil {
log.Trace(fmt.Sprintf("Multiaddress has invalid ip: %v", err))
return false
}
found := false
for _, ipnet := range acceptedNets {
if ipnet.Contains(ip) {
found = true
break
}
}
return found
}
return !f.AddrBlocked(a)
}