-
Notifications
You must be signed in to change notification settings - Fork 1
/
ip4targets.go
88 lines (77 loc) · 1.74 KB
/
ip4targets.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
package main
import (
"context"
"encoding/binary"
"net"
"sync"
"time"
)
type ip4 [4]byte
type ip4target struct {
sync.Mutex
backoff
timestamp time.Time
}
type ip4targets struct {
baseTimeout time.Duration
targets map[ip4]*ip4target
}
func makeip4targets(ip4net *net.IPNet, baseTimeout time.Duration) ip4targets {
num := binary.BigEndian.Uint32([]byte(ip4net.IP))
mask := binary.BigEndian.Uint32([]byte(ip4net.Mask))
network := num & mask
broadcast := network | ^mask
network++
targets := make(map[ip4]*ip4target, broadcast-network)
backoff := backoff{timeout: baseTimeout, exp: 1}
for ; network < broadcast; network++ {
var ip [4]byte
binary.BigEndian.PutUint32(ip[:], network)
targets[ip] = &ip4target{backoff: backoff}
}
return ip4targets{baseTimeout: baseTimeout, targets: targets}
}
func (t ip4targets) reset(ip4 ip4) {
target := t.targets[ip4]
target.Lock()
target.backoff = backoff{timeout: t.baseTimeout, exp: 1}
target.timestamp = time.Now()
target.Unlock()
}
func (t ip4targets) loop(ctx context.Context, fn func(ip4) error) error {
ticker := time.NewTicker(tick)
defer ticker.Stop()
for {
anySent := false
for ip4, target := range t.targets {
if err := ctx.Err(); err != nil {
return err
}
now := time.Now()
target.Lock()
if !target.timestamp.IsZero() && now.Sub(target.timestamp) < target.timeout {
target.Unlock()
continue
}
target.timestamp = now
target.increaseTimeout(t.baseTimeout, maxTimeout)
target.Unlock()
if err := fn(ip4); err != nil {
return err
}
anySent = true
select {
case <-ticker.C:
case <-ctx.Done():
return ctx.Err()
}
}
if !anySent {
select {
case <-ticker.C:
case <-ctx.Done():
return ctx.Err()
}
}
}
}