forked from calibrae-project/spawn
-
Notifications
You must be signed in to change notification settings - Fork 0
/
dynamicbanscore.go
124 lines (111 loc) · 4.05 KB
/
dynamicbanscore.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
package connmgr
import (
"fmt"
"math"
"sync"
"time"
)
const (
// Halflife defines the time (in seconds) by which the transient part of the ban score decays to one half of it's
// original value.
Halflife = 60
// lambda is the decaying constant.
lambda = math.Ln2 / Halflife
// Lifetime defines the maximum age of the transient part of the ban score to be considered a non-zero score (in
// seconds).
Lifetime = 1800
// precomputedLen defines the amount of decay factors (one per second) that should be precomputed at initialization.
precomputedLen = 64
)
// precomputedFactor stores precomputed exponential decay factors for the first 'precomputedLen' seconds starting from t
// == 0.
var precomputedFactor [precomputedLen]float64
// init precomputes decay factors.
func init() {
for i := range precomputedFactor {
precomputedFactor[i] = math.Exp(-1.0 * float64(i) * lambda)
}
}
// decayFactor returns the decay factor at t seconds, using precalculated values if available, or calculating the factor
// if needed.
func decayFactor(t int64) float64 {
if t < precomputedLen {
return precomputedFactor[t]
}
return math.Exp(-1.0 * float64(t) * lambda)
}
// DynamicBanScore provides dynamic ban scores consisting of a persistent and a decaying component. The persistent score
// could be utilized to create simple additive banning policies similar to those found in other bitcoin node
// implementations.
//
// The decaying score enables the creation of evasive logic which handles misbehaving peers (especially application
// layer DoS attacks) gracefully by disconnecting and banning peers attempting various kinds of flooding.
//
// DynamicBanScore allows these two approaches to be used in tandem. Zero value: Values of type DynamicBanScore are
// immediately ready for use upon declaration.
type DynamicBanScore struct {
lastUnix int64
transient float64
persistent uint32
mtx sync.Mutex
}
// String returns the ban score as a human-readable string.
func (s *DynamicBanScore) String() string {
s.mtx.Lock()
r := fmt.Sprintf("persistent %v + transient %v at %v = %v as of now",
s.persistent, s.transient, s.lastUnix, s.Int(),
)
s.mtx.Unlock()
return r
}
// Int returns the current ban score, the sum of the persistent and decaying scores. This function is safe for
// concurrent access.
func (s *DynamicBanScore) Int() uint32 {
s.mtx.Lock()
r := s.int(time.Now())
s.mtx.Unlock()
return r
}
// Increase increases both the persistent and decaying scores by the values passed as parameters. The resulting score is
// returned. This function is safe for concurrent access.
func (s *DynamicBanScore) Increase(persistent, transient uint32) uint32 {
s.mtx.Lock()
r := s.increase(persistent, transient, time.Now())
s.mtx.Unlock()
return r
}
// Reset set both persistent and decaying scores to zero. This function is safe for concurrent access.
func (s *DynamicBanScore) Reset() {
s.mtx.Lock()
s.persistent = 0
s.transient = 0
s.lastUnix = 0
s.mtx.Unlock()
}
// int returns the ban score, the sum of the persistent and decaying scores at a given point in time. This function is
// not safe for concurrent access. It is intended to be used internally and during testing.
func (s *DynamicBanScore) int(t time.Time) uint32 {
dt := t.Unix() - s.lastUnix
if s.transient < 1 || dt < 0 || Lifetime < dt {
return s.persistent
}
return s.persistent + uint32(s.transient*decayFactor(dt))
}
// increase increases the persistent, the decaying or both scores by the values passed as parameters. The resulting
// score is calculated as if the action was carried out at the point time represented by the third parameter. The
// resulting score is returned. This function is not safe for concurrent access.
func (s *DynamicBanScore) increase(persistent, transient uint32, t time.Time) uint32 {
s.persistent += persistent
tu := t.Unix()
dt := tu - s.lastUnix
if transient > 0 {
if Lifetime < dt {
s.transient = 0
} else if s.transient > 1 && dt > 0 {
s.transient *= decayFactor(dt)
}
s.transient += float64(transient)
s.lastUnix = tu
}
return s.persistent + uint32(s.transient)
}