bits.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. package nebula
  2. import (
  3. "context"
  4. "log/slog"
  5. "github.com/rcrowley/go-metrics"
  6. )
  7. type Bits struct {
  8. length uint64
  9. current uint64
  10. bits []bool
  11. lostCounter metrics.Counter
  12. dupeCounter metrics.Counter
  13. outOfWindowCounter metrics.Counter
  14. }
  15. func NewBits(bits uint64) *Bits {
  16. b := &Bits{
  17. length: bits,
  18. bits: make([]bool, bits, bits),
  19. current: 0,
  20. lostCounter: metrics.GetOrRegisterCounter("network.packets.lost", nil),
  21. dupeCounter: metrics.GetOrRegisterCounter("network.packets.duplicate", nil),
  22. outOfWindowCounter: metrics.GetOrRegisterCounter("network.packets.out_of_window", nil),
  23. }
  24. // There is no counter value 0, mark it to avoid counting a lost packet later.
  25. b.bits[0] = true
  26. b.current = 0
  27. return b
  28. }
  29. func (b *Bits) Check(l *slog.Logger, i uint64) bool {
  30. // If i is the next number, return true.
  31. if i > b.current {
  32. return true
  33. }
  34. // If i is within the window, check if it's been set already.
  35. if i > b.current-b.length || i < b.length && b.current < b.length {
  36. return !b.bits[i%b.length]
  37. }
  38. // Not within the window
  39. if l.Enabled(context.Background(), slog.LevelDebug) {
  40. l.Debug("rejected a packet (top)",
  41. "current", b.current,
  42. "incoming", i,
  43. )
  44. }
  45. return false
  46. }
  47. func (b *Bits) Update(l *slog.Logger, i uint64) bool {
  48. // If i is the next number, return true and update current.
  49. if i == b.current+1 {
  50. // Check if the oldest bit was lost since we are shifting the window by 1 and occupying it with this counter
  51. // The very first window can only be tracked as lost once we are on the 2nd window or greater
  52. if b.bits[i%b.length] == false && i > b.length {
  53. b.lostCounter.Inc(1)
  54. }
  55. b.bits[i%b.length] = true
  56. b.current = i
  57. return true
  58. }
  59. // If i is a jump, adjust the window, record lost, update current, and return true
  60. if i > b.current {
  61. lost := int64(0)
  62. // Zero out the bits between the current and the new counter value, limited by the window size,
  63. // since the window is shifting
  64. for n := b.current + 1; n <= min(i, b.current+b.length); n++ {
  65. if b.bits[n%b.length] == false && n > b.length {
  66. lost++
  67. }
  68. b.bits[n%b.length] = false
  69. }
  70. // Only record any skipped packets as a result of the window moving further than the window length
  71. // Any loss within the new window will be accounted for in future calls
  72. lost += max(0, int64(i-b.current-b.length))
  73. b.lostCounter.Inc(lost)
  74. b.bits[i%b.length] = true
  75. b.current = i
  76. return true
  77. }
  78. // If i is within the current window but below the current counter,
  79. // Check to see if it's a duplicate
  80. if i > b.current-b.length || i < b.length && b.current < b.length {
  81. if b.current == i || b.bits[i%b.length] == true {
  82. if l.Enabled(context.Background(), slog.LevelDebug) {
  83. l.Debug("Receive window",
  84. "accepted", false,
  85. "currentCounter", b.current,
  86. "incomingCounter", i,
  87. "reason", "duplicate",
  88. )
  89. }
  90. b.dupeCounter.Inc(1)
  91. return false
  92. }
  93. b.bits[i%b.length] = true
  94. return true
  95. }
  96. // In all other cases, fail and don't change current.
  97. b.outOfWindowCounter.Inc(1)
  98. if l.Enabled(context.Background(), slog.LevelDebug) {
  99. l.Debug("Receive window",
  100. "accepted", false,
  101. "currentCounter", b.current,
  102. "incomingCounter", i,
  103. "reason", "nonsense",
  104. )
  105. }
  106. return false
  107. }