mirror of https://github.com/slackhq/nebula.git
158 lines
4.5 KiB
Go
158 lines
4.5 KiB
Go
|
package nebula
|
||
|
|
||
|
import (
|
||
|
"github.com/rcrowley/go-metrics"
|
||
|
"github.com/sirupsen/logrus"
|
||
|
)
|
||
|
|
||
|
type Bits struct {
|
||
|
length uint64
|
||
|
current uint64
|
||
|
bits []bool
|
||
|
firstSeen bool
|
||
|
lostCounter metrics.Counter
|
||
|
dupeCounter metrics.Counter
|
||
|
outOfWindowCounter metrics.Counter
|
||
|
}
|
||
|
|
||
|
func NewBits(bits uint64) *Bits {
|
||
|
return &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),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (b *Bits) Check(i uint64) bool {
|
||
|
// If i is the next number, return true.
|
||
|
if i > b.current || (i == 0 && b.firstSeen == false && b.current < b.length) {
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// If i is within the window, check if it's been set already. The first window will fail this check
|
||
|
if i > b.current-b.length {
|
||
|
return !b.bits[i%b.length]
|
||
|
}
|
||
|
|
||
|
// If i is within the first window
|
||
|
if i < b.length {
|
||
|
return !b.bits[i%b.length]
|
||
|
}
|
||
|
|
||
|
// Not within the window
|
||
|
l.Debugf("rejected a packet (top) %d %d\n", b.current, i)
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
func (b *Bits) Update(i uint64) bool {
|
||
|
// If i is the next number, return true and update current.
|
||
|
if i == b.current+1 {
|
||
|
// Report missed packets, we can only understand what was missed after the first window has been gone through
|
||
|
if i > b.length && b.bits[i%b.length] == false {
|
||
|
b.lostCounter.Inc(1)
|
||
|
}
|
||
|
b.bits[i%b.length] = true
|
||
|
b.current = i
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// If i packet is greater than current but less than the maximum length of our bitmap,
|
||
|
// flip everything in between to false and move ahead.
|
||
|
if i > b.current && i < b.current+b.length {
|
||
|
// In between current and i need to be zero'd to allow those packets to come in later
|
||
|
for n := b.current + 1; n < i; n++ {
|
||
|
b.bits[n%b.length] = false
|
||
|
}
|
||
|
|
||
|
b.bits[i%b.length] = true
|
||
|
b.current = i
|
||
|
//l.Debugf("missed %d packets between %d and %d\n", i-b.current, i, b.current)
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// If i is greater than the delta between current and the total length of our bitmap,
|
||
|
// just flip everything in the map and move ahead.
|
||
|
if i >= b.current+b.length {
|
||
|
// The current window loss will be accounted for later, only record the jump as loss up until then
|
||
|
lost := maxInt64(0, int64(i-b.current-b.length))
|
||
|
//TODO: explain this
|
||
|
if b.current == 0 {
|
||
|
lost++
|
||
|
}
|
||
|
|
||
|
for n := range b.bits {
|
||
|
// Don't want to count the first window as a loss
|
||
|
//TODO: this is likely wrong, we are wanting to track only the bit slots that we aren't going to track anymore and this is marking everything as missed
|
||
|
//if b.bits[n] == false {
|
||
|
// lost++
|
||
|
//}
|
||
|
b.bits[n] = false
|
||
|
}
|
||
|
|
||
|
b.lostCounter.Inc(lost)
|
||
|
|
||
|
if l.Level >= logrus.DebugLevel {
|
||
|
l.WithField("receiveWindow", m{"accepted": true, "currentCounter": b.current, "incomingCounter": i, "reason": "window shifting"}).
|
||
|
Debug("Receive window")
|
||
|
}
|
||
|
b.bits[i%b.length] = true
|
||
|
b.current = i
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// Allow for the 0 packet to come in within the first window
|
||
|
if i == 0 && b.firstSeen == false && b.current < b.length {
|
||
|
b.firstSeen = true
|
||
|
b.bits[i%b.length] = true
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// If i is within the window of current minus length (the total pat window size),
|
||
|
// allow it and flip to true but to NOT change current. We also have to account for the first window
|
||
|
if ((b.current >= b.length && i > b.current-b.length) || (b.current < b.length && i < b.length)) && i <= b.current {
|
||
|
if b.current == i {
|
||
|
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
|
||
|
}
|
||
|
|
||
|
if b.bits[i%b.length] == true {
|
||
|
if l.Level >= logrus.DebugLevel {
|
||
|
l.WithField("receiveWindow", m{"accepted": false, "currentCounter": b.current, "incomingCounter": i, "reason": "old 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
|
||
|
}
|
||
|
|
||
|
func maxInt64(a, b int64) int64 {
|
||
|
if a > b {
|
||
|
return a
|
||
|
}
|
||
|
|
||
|
return b
|
||
|
}
|