forked from libp2p/go-libp2p-relay-daemon
-
Notifications
You must be signed in to change notification settings - Fork 0
/
acl.go
166 lines (136 loc) · 3.61 KB
/
acl.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
package relaydaemon
import (
"fmt"
"net"
"sync"
"github.com/libp2p/go-libp2p/core/host"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
relayv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay"
ma "github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr/net"
)
// ACLFilter implements the libp2p relay ACL interface.
type ACLFilter struct {
allowPeers map[peer.ID]struct{}
allowSubnets []*net.IPNet
// peer address tracking for v1 relay ACL
mx sync.RWMutex
addrs map[peer.ID]map[ma.Multiaddr]struct{}
}
var _ relayv2.ACLFilter = (*ACLFilter)(nil)
// NewACL returns an implementation of the relay ACL interface using the given
// host and relay daemon ACL config.
func NewACL(h host.Host, cfg ACLConfig) (*ACLFilter, error) {
acl := &ACLFilter{}
if len(cfg.AllowPeers) > 0 {
acl.allowPeers = make(map[peer.ID]struct{})
for _, s := range cfg.AllowPeers {
p, err := peer.Decode(s)
if err != nil {
return nil, fmt.Errorf("error parsing peer ID: %w", err)
}
acl.allowPeers[p] = struct{}{}
}
}
if len(cfg.AllowSubnets) > 0 {
acl.allowSubnets = make([]*net.IPNet, 0, len(cfg.AllowSubnets))
for _, s := range cfg.AllowSubnets {
_, ipnet, err := net.ParseCIDR(s)
if err != nil {
return nil, fmt.Errorf("error parsing subnet: %w", err)
}
acl.allowSubnets = append(acl.allowSubnets, ipnet)
}
acl.addrs = make(map[peer.ID]map[ma.Multiaddr]struct{})
h.Network().Notify(&network.NotifyBundle{
ConnectedF: acl.Connected,
DisconnectedF: acl.Disconnected,
})
}
return acl, nil
}
// AllowReserve is relevant for the relayv2 ACL implementation.
func (a *ACLFilter) AllowReserve(p peer.ID, addr ma.Multiaddr) bool {
if len(a.allowPeers) > 0 {
_, ok := a.allowPeers[p]
if !ok {
return false
}
}
if len(a.allowSubnets) > 0 {
ip, err := manet.ToIP(addr)
if err != nil {
return false
}
for _, ipnet := range a.allowSubnets {
if ipnet.Contains(ip) {
return true
}
}
return false
}
return true
}
// AllowConnect is always true, as we are accepting any public node to be able
// to contact the nodes allowed to make reservations through this relay.
func (a *ACLFilter) AllowConnect(src peer.ID, srcAddr ma.Multiaddr, dest peer.ID) bool {
return true
}
// AllowHop is relevant for relayv1 ACL implementation.
func (a *ACLFilter) AllowHop(src, dest peer.ID) bool {
if len(a.allowPeers) > 0 {
_, ok := a.allowPeers[dest]
if !ok {
return false
}
}
if len(a.allowSubnets) > 0 {
a.mx.RLock()
defer a.mx.RUnlock()
addrs := a.addrs[dest]
for addr := range addrs {
ip, err := manet.ToIP(addr)
if err != nil {
continue
}
for _, ipnet := range a.allowSubnets {
if ipnet.Contains(ip) {
return true
}
}
}
return false
}
return true
}
// Connected handles the Connect notification and stores the address of the
// connected node so that the ACL can decide whether other nodes can connect
// to it (relayV1).
func (a *ACLFilter) Connected(n network.Network, c network.Conn) {
p := c.RemotePeer()
addr := c.RemoteMultiaddr()
a.mx.Lock()
defer a.mx.Unlock()
addrs, ok := a.addrs[p]
if !ok {
addrs = make(map[ma.Multiaddr]struct{})
a.addrs[p] = addrs
}
addrs[addr] = struct{}{}
}
// Disconnected handles the Disconnect notification and deletes the address of
// the disconnected node.
func (a *ACLFilter) Disconnected(n network.Network, c network.Conn) {
p := c.RemotePeer()
addr := c.RemoteMultiaddr()
a.mx.Lock()
defer a.mx.Unlock()
addrs, ok := a.addrs[p]
if ok {
delete(addrs, addr)
if len(addrs) == 0 {
delete(a.addrs, p)
}
}
}