udp_control.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. package bwtest
  2. import (
  3. "context"
  4. "fmt"
  5. "net"
  6. "sync"
  7. "time"
  8. "github.com/vishvananda/netlink"
  9. )
  10. const udpFeedbackVersion = "receiver-v1"
  11. const udpProbeType uint8 = 4
  12. // UDP without a requested limit probes capacity using receiver feedback instead
  13. // of enqueuing an unbounded amount of traffic ahead of the reverse direction.
  14. type udpPacer struct {
  15. mu sync.Mutex
  16. limiter *RateLimiter
  17. automatic bool
  18. rate int64
  19. started, lastFeedback, lastChange time.Time
  20. lastLoss time.Time
  21. delivered int64
  22. adjustPackets, adjustLost int64
  23. }
  24. func newUDPPacer(limit int64) *udpPacer {
  25. now := time.Now()
  26. automatic := limit <= 0
  27. if automatic {
  28. limit = 2_000_000
  29. }
  30. return &udpPacer{limiter: NewRateLimiter(limit), automatic: automatic, rate: limit, started: now, lastFeedback: now, lastChange: now}
  31. }
  32. func (p *udpPacer) wait(ctx context.Context, n int) bool {
  33. // Take only a small token-accounting lock; feedback and other streams can
  34. // update the rate while this stream waits for its next datagram.
  35. for {
  36. p.mu.Lock()
  37. if p.automatic && time.Since(p.lastFeedback) > 2*time.Second && time.Since(p.lastChange) > time.Second {
  38. p.setRate(max(int64(128000), p.rate/2))
  39. p.lastChange = time.Now()
  40. }
  41. rl := p.limiter
  42. now := time.Now()
  43. rl.tokens += now.Sub(rl.lastCheckTime).Seconds() * float64(p.rate) / 8
  44. rl.lastCheckTime = now
  45. cap := max(float64(n), float64(p.rate)/8*.02)
  46. if rl.tokens > cap {
  47. rl.tokens = cap
  48. }
  49. if rl.tokens >= float64(n) {
  50. rl.tokens -= float64(n)
  51. p.mu.Unlock()
  52. return ctx.Err() == nil
  53. }
  54. delay := time.Duration((float64(n) - rl.tokens) / (float64(p.rate) / 8) * float64(time.Second))
  55. p.mu.Unlock()
  56. if delay > 20*time.Millisecond {
  57. delay = 20 * time.Millisecond
  58. }
  59. timer := time.NewTimer(delay)
  60. select {
  61. case <-ctx.Done():
  62. timer.Stop()
  63. return false
  64. case <-timer.C:
  65. }
  66. }
  67. }
  68. func (p *udpPacer) setRate(rate int64) { p.rate = rate; p.limiter.bps = rate }
  69. func (p *udpPacer) observe(bytes, packets, lost int64, now time.Time) {
  70. p.mu.Lock()
  71. defer p.mu.Unlock()
  72. dt := now.Sub(p.lastFeedback).Seconds()
  73. delta := bytes - p.delivered
  74. if dt <= 0 || delta < 0 {
  75. return
  76. }
  77. p.lastFeedback = now
  78. p.delivered = bytes
  79. if !p.automatic || now.Sub(p.started) < time.Second || now.Sub(p.lastChange) < 450*time.Millisecond {
  80. return
  81. }
  82. // Loss counts can decrease when delayed packets arrive. Compare cumulative
  83. // counters since the previous decision and never interpret reordering as
  84. // negative loss. Older peers without packet counts still use byte feedback.
  85. packetDelta := max(int64(0), packets-p.adjustPackets)
  86. lossDelta := max(int64(0), lost-p.adjustLost)
  87. p.adjustPackets, p.adjustLost = packets, lost
  88. deliveredRate := int64(float64(delta) * 8 / dt)
  89. switch {
  90. case packetDelta+lossDelta >= 10 && lossDelta*200 >= packetDelta+lossDelta:
  91. p.setRate(max(int64(128000), min(p.rate*8/10, deliveredRate*9/10)))
  92. p.lastLoss = now
  93. case deliveredRate < p.rate*7/10:
  94. p.setRate(max(int64(128000), deliveredRate*11/10))
  95. case deliveredRate >= p.rate*85/100 && (p.lastLoss.IsZero() || now.Sub(p.lastLoss) >= 2*time.Second):
  96. p.setRate(min(int64(10_000_000_000), p.rate*115/100))
  97. }
  98. p.lastChange = now
  99. }
  100. func udpPayloadSize(requested int, ip net.IP) (int, error) {
  101. routes, err := netlink.RouteGet(ip)
  102. if err != nil {
  103. return 0, fmt.Errorf("bandwidth target route: %w", err)
  104. }
  105. if len(routes) == 0 {
  106. return 0, fmt.Errorf("no route to bandwidth target")
  107. }
  108. link, err := netlink.LinkByIndex(routes[0].LinkIndex)
  109. if err != nil {
  110. return 0, err
  111. }
  112. mtu := link.Attrs().MTU
  113. if routes[0].MTU > 0 {
  114. mtu = min(mtu, routes[0].MTU)
  115. }
  116. overhead := 28
  117. if ip.To4() == nil {
  118. overhead = 48
  119. }
  120. size := min(requested, mtu-overhead)
  121. if size < 64 {
  122. return 0, fmt.Errorf("target MTU is too small for bandwidth datagrams")
  123. }
  124. return size, nil
  125. }
  126. func udpProbe(hash uint64) []byte {
  127. b := make([]byte, HeaderSize)
  128. EncodeHeader(b, PacketHeader{Magic: MagicBytes, Version: ProtoVer, PacketType: udpProbeType, SessionHash: hash})
  129. return b
  130. }
  131. // Each UDP stream has its own sequence space. Late packets within the bounded
  132. // reorder window undo inferred losses, and duplicate datagrams are not counted.
  133. type udpSequenceState struct {
  134. max uint64
  135. seen [1024]uint64
  136. }
  137. type udpTracker struct {
  138. streams map[uint8]*udpSequenceState
  139. lost int64
  140. }
  141. func (t *udpTracker) accept(stream uint8, seq uint64) (bool, int64) {
  142. if seq == 0 {
  143. return false, t.lost
  144. }
  145. if t.streams == nil {
  146. t.streams = make(map[uint8]*udpSequenceState)
  147. }
  148. s := t.streams[stream]
  149. if s == nil {
  150. s = &udpSequenceState{}
  151. t.streams[stream] = s
  152. }
  153. if seq > s.max {
  154. t.lost += int64(seq - s.max - 1)
  155. s.max = seq
  156. } else {
  157. if s.max-seq >= 1024 || s.seen[seq%1024] == seq {
  158. return false, t.lost
  159. }
  160. if t.lost > 0 {
  161. t.lost--
  162. }
  163. }
  164. s.seen[seq%1024] = seq
  165. return true, t.lost
  166. }