github.com/slackhq/nebula@v1.9.0/bits.go (about)

     1  package nebula
     2  
     3  import (
     4  	"github.com/rcrowley/go-metrics"
     5  	"github.com/sirupsen/logrus"
     6  )
     7  
     8  type Bits struct {
     9  	length             uint64
    10  	current            uint64
    11  	bits               []bool
    12  	firstSeen          bool
    13  	lostCounter        metrics.Counter
    14  	dupeCounter        metrics.Counter
    15  	outOfWindowCounter metrics.Counter
    16  }
    17  
    18  func NewBits(bits uint64) *Bits {
    19  	return &Bits{
    20  		length:             bits,
    21  		bits:               make([]bool, bits, bits),
    22  		current:            0,
    23  		lostCounter:        metrics.GetOrRegisterCounter("network.packets.lost", nil),
    24  		dupeCounter:        metrics.GetOrRegisterCounter("network.packets.duplicate", nil),
    25  		outOfWindowCounter: metrics.GetOrRegisterCounter("network.packets.out_of_window", nil),
    26  	}
    27  }
    28  
    29  func (b *Bits) Check(l logrus.FieldLogger, i uint64) bool {
    30  	// If i is the next number, return true.
    31  	if i > b.current || (i == 0 && b.firstSeen == false && b.current < b.length) {
    32  		return true
    33  	}
    34  
    35  	// If i is within the window, check if it's been set already. The first window will fail this check
    36  	if i > b.current-b.length {
    37  		return !b.bits[i%b.length]
    38  	}
    39  
    40  	// If i is within the first window
    41  	if i < b.length {
    42  		return !b.bits[i%b.length]
    43  	}
    44  
    45  	// Not within the window
    46  	l.Debugf("rejected a packet (top) %d %d\n", b.current, i)
    47  	return false
    48  }
    49  
    50  func (b *Bits) Update(l *logrus.Logger, i uint64) bool {
    51  	// If i is the next number, return true and update current.
    52  	if i == b.current+1 {
    53  		// Report missed packets, we can only understand what was missed after the first window has been gone through
    54  		if i > b.length && b.bits[i%b.length] == false {
    55  			b.lostCounter.Inc(1)
    56  		}
    57  		b.bits[i%b.length] = true
    58  		b.current = i
    59  		return true
    60  	}
    61  
    62  	// If i packet is greater than current but less than the maximum length of our bitmap,
    63  	// flip everything in between to false and move ahead.
    64  	if i > b.current && i < b.current+b.length {
    65  		// In between current and i need to be zero'd to allow those packets to come in later
    66  		for n := b.current + 1; n < i; n++ {
    67  			b.bits[n%b.length] = false
    68  		}
    69  
    70  		b.bits[i%b.length] = true
    71  		b.current = i
    72  		//l.Debugf("missed %d packets between %d and %d\n", i-b.current, i, b.current)
    73  		return true
    74  	}
    75  
    76  	// If i is greater than the delta between current and the total length of our bitmap,
    77  	// just flip everything in the map and move ahead.
    78  	if i >= b.current+b.length {
    79  		// The current window loss will be accounted for later, only record the jump as loss up until then
    80  		lost := maxInt64(0, int64(i-b.current-b.length))
    81  		//TODO: explain this
    82  		if b.current == 0 {
    83  			lost++
    84  		}
    85  
    86  		for n := range b.bits {
    87  			// Don't want to count the first window as a loss
    88  			//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
    89  			//if b.bits[n] == false {
    90  			//	lost++
    91  			//}
    92  			b.bits[n] = false
    93  		}
    94  
    95  		b.lostCounter.Inc(lost)
    96  
    97  		if l.Level >= logrus.DebugLevel {
    98  			l.WithField("receiveWindow", m{"accepted": true, "currentCounter": b.current, "incomingCounter": i, "reason": "window shifting"}).
    99  				Debug("Receive window")
   100  		}
   101  		b.bits[i%b.length] = true
   102  		b.current = i
   103  		return true
   104  	}
   105  
   106  	// Allow for the 0 packet to come in within the first window
   107  	if i == 0 && b.firstSeen == false && b.current < b.length {
   108  		b.firstSeen = true
   109  		b.bits[i%b.length] = true
   110  		return true
   111  	}
   112  
   113  	// If i is within the window of current minus length (the total pat window size),
   114  	// allow it and flip to true but to NOT change current. We also have to account for the first window
   115  	if ((b.current >= b.length && i > b.current-b.length) || (b.current < b.length && i < b.length)) && i <= b.current {
   116  		if b.current == i {
   117  			if l.Level >= logrus.DebugLevel {
   118  				l.WithField("receiveWindow", m{"accepted": false, "currentCounter": b.current, "incomingCounter": i, "reason": "duplicate"}).
   119  					Debug("Receive window")
   120  			}
   121  			b.dupeCounter.Inc(1)
   122  			return false
   123  		}
   124  
   125  		if b.bits[i%b.length] == true {
   126  			if l.Level >= logrus.DebugLevel {
   127  				l.WithField("receiveWindow", m{"accepted": false, "currentCounter": b.current, "incomingCounter": i, "reason": "old duplicate"}).
   128  					Debug("Receive window")
   129  			}
   130  			b.dupeCounter.Inc(1)
   131  			return false
   132  		}
   133  
   134  		b.bits[i%b.length] = true
   135  		return true
   136  
   137  	}
   138  
   139  	// In all other cases, fail and don't change current.
   140  	b.outOfWindowCounter.Inc(1)
   141  	if l.Level >= logrus.DebugLevel {
   142  		l.WithField("accepted", false).
   143  			WithField("currentCounter", b.current).
   144  			WithField("incomingCounter", i).
   145  			WithField("reason", "nonsense").
   146  			Debug("Receive window")
   147  	}
   148  	return false
   149  }
   150  
   151  func maxInt64(a, b int64) int64 {
   152  	if a > b {
   153  		return a
   154  	}
   155  
   156  	return b
   157  }