forked from libp2p/go-libp2p
-
Notifications
You must be signed in to change notification settings - Fork 0
/
constraints.go
125 lines (104 loc) · 3.04 KB
/
constraints.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
package relay
import (
"errors"
"sync"
"time"
asnutil "github.com/libp2p/go-libp2p-asn-util"
"github.com/libp2p/go-libp2p/core/peer"
ma "github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr/net"
)
var validity = 30 * time.Minute
var (
errTooManyReservations = errors.New("too many reservations")
errTooManyReservationsForPeer = errors.New("too many reservations for peer")
errTooManyReservationsForIP = errors.New("too many peers for IP address")
errTooManyReservationsForASN = errors.New("too many peers for ASN")
)
// constraints implements various reservation constraints
type constraints struct {
rc *Resources
mutex sync.Mutex
total []time.Time
peers map[peer.ID][]time.Time
ips map[string][]time.Time
asns map[string][]time.Time
}
// newConstraints creates a new constraints object.
// The methods are *not* thread-safe; an external lock must be held if synchronization
// is required.
func newConstraints(rc *Resources) *constraints {
return &constraints{
rc: rc,
peers: make(map[peer.ID][]time.Time),
ips: make(map[string][]time.Time),
asns: make(map[string][]time.Time),
}
}
// AddReservation adds a reservation for a given peer with a given multiaddr.
// If adding this reservation violates IP constraints, an error is returned.
func (c *constraints) AddReservation(p peer.ID, a ma.Multiaddr) error {
c.mutex.Lock()
defer c.mutex.Unlock()
now := time.Now()
c.cleanup(now)
if len(c.total) >= c.rc.MaxReservations {
return errTooManyReservations
}
ip, err := manet.ToIP(a)
if err != nil {
return errors.New("no IP address associated with peer")
}
peerReservations := c.peers[p]
if len(peerReservations) >= c.rc.MaxReservationsPerPeer {
return errTooManyReservationsForPeer
}
ipReservations := c.ips[ip.String()]
if len(ipReservations) >= c.rc.MaxReservationsPerIP {
return errTooManyReservationsForIP
}
var asnReservations []time.Time
var asn string
if ip.To4() == nil {
asn, _ = asnutil.Store.AsnForIPv6(ip)
if asn != "" {
asnReservations = c.asns[asn]
if len(asnReservations) >= c.rc.MaxReservationsPerASN {
return errTooManyReservationsForASN
}
}
}
expiry := now.Add(validity)
c.total = append(c.total, expiry)
peerReservations = append(peerReservations, expiry)
c.peers[p] = peerReservations
ipReservations = append(ipReservations, expiry)
c.ips[ip.String()] = ipReservations
if asn != "" {
asnReservations = append(asnReservations, expiry)
c.asns[asn] = asnReservations
}
return nil
}
func (c *constraints) cleanupList(l []time.Time, now time.Time) []time.Time {
var index int
for i, t := range l {
if t.After(now) {
break
}
index = i + 1
}
return l[index:]
}
func (c *constraints) cleanup(now time.Time) {
c.total = c.cleanupList(c.total, now)
for k, peerReservations := range c.peers {
c.peers[k] = c.cleanupList(peerReservations, now)
}
for k, ipReservations := range c.ips {
c.ips[k] = c.cleanupList(ipReservations, now)
}
for k, asnReservations := range c.asns {
c.asns[k] = c.cleanupList(asnReservations, now)
}
}