File: bits.go

package info (click to toggle)
nebula 1.10.3%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 1,884 kB
  • sloc: makefile: 190; sh: 100
file content (112 lines) | stat: -rw-r--r-- 3,335 bytes parent folder | download | duplicates (2)
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
package nebula

import (
	"github.com/rcrowley/go-metrics"
	"github.com/sirupsen/logrus"
)

type Bits struct {
	length             uint64
	current            uint64
	bits               []bool
	lostCounter        metrics.Counter
	dupeCounter        metrics.Counter
	outOfWindowCounter metrics.Counter
}

func NewBits(bits uint64) *Bits {
	b := &Bits{
		length:             bits,
		bits:               make([]bool, bits, bits),
		current:            0,
		lostCounter:        metrics.GetOrRegisterCounter("network.packets.lost", nil),
		dupeCounter:        metrics.GetOrRegisterCounter("network.packets.duplicate", nil),
		outOfWindowCounter: metrics.GetOrRegisterCounter("network.packets.out_of_window", nil),
	}

	// There is no counter value 0, mark it to avoid counting a lost packet later.
	b.bits[0] = true
	b.current = 0
	return b
}

func (b *Bits) Check(l *logrus.Logger, i uint64) bool {
	// If i is the next number, return true.
	if i > b.current {
		return true
	}

	// If i is within the window, check if it's been set already.
	if i > b.current-b.length || i < b.length && b.current < b.length {
		return !b.bits[i%b.length]
	}

	// Not within the window
	if l.Level >= logrus.DebugLevel {
		l.Debugf("rejected a packet (top) %d %d\n", b.current, i)
	}
	return false
}

func (b *Bits) Update(l *logrus.Logger, i uint64) bool {
	// If i is the next number, return true and update current.
	if i == b.current+1 {
		// Check if the oldest bit was lost since we are shifting the window by 1 and occupying it with this counter
		// The very first window can only be tracked as lost once we are on the 2nd window or greater
		if b.bits[i%b.length] == false && i > b.length {
			b.lostCounter.Inc(1)
		}
		b.bits[i%b.length] = true
		b.current = i
		return true
	}

	// If i is a jump, adjust the window, record lost, update current, and return true
	if i > b.current {
		lost := int64(0)
		// Zero out the bits between the current and the new counter value, limited by the window size,
		// since the window is shifting
		for n := b.current + 1; n <= min(i, b.current+b.length); n++ {
			if b.bits[n%b.length] == false && n > b.length {
				lost++
			}
			b.bits[n%b.length] = false
		}

		// Only record any skipped packets as a result of the window moving further than the window length
		// Any loss within the new window will be accounted for in future calls
		lost += max(0, int64(i-b.current-b.length))
		b.lostCounter.Inc(lost)

		b.bits[i%b.length] = true
		b.current = i
		return true
	}

	// If i is within the current window but below the current counter,
	// Check to see if it's a duplicate
	if i > b.current-b.length || i < b.length && b.current < b.length {
		if b.current == i || b.bits[i%b.length] == true {
			if l.Level >= logrus.DebugLevel {
				l.WithField("receiveWindow", m{"accepted": false, "currentCounter": b.current, "incomingCounter": i, "reason": "duplicate"}).
					Debug("Receive window")
			}
			b.dupeCounter.Inc(1)
			return false
		}

		b.bits[i%b.length] = true
		return true
	}

	// In all other cases, fail and don't change current.
	b.outOfWindowCounter.Inc(1)
	if l.Level >= logrus.DebugLevel {
		l.WithField("accepted", false).
			WithField("currentCounter", b.current).
			WithField("incomingCounter", i).
			WithField("reason", "nonsense").
			Debug("Receive window")
	}
	return false
}