when we detect we're blocked, only drop packets often enough to make sure the existing queue's size is non-increasing, and always drop the worst packet from a random flow with odds based on the total size of packets queued for that flow

This commit is contained in:
Arceliar 2020-05-17 12:09:40 -05:00
parent 6e92af1cd2
commit 7720e169f2
2 changed files with 22 additions and 30 deletions

View File

@ -1,6 +1,7 @@
package yggdrasil package yggdrasil
import ( import (
"math/rand"
"time" "time"
) )
@ -28,32 +29,19 @@ func (q *packetQueue) drop() bool {
if q.size == 0 { if q.size == 0 {
return false return false
} }
// TODO? drop from a random stream // select a random stream, odds based on stream size
// odds proportional to size? bandwidth? offset := rand.Uint64() % q.size
// always using the worst is exploitable -> flood 1 packet per random stream
// find the stream that's using the most bandwidth
now := time.Now()
var worst pqStreamID var worst pqStreamID
for id := range q.streams { var size uint64
worst = id
break // get a random ID to start
}
worstStream := q.streams[worst]
worstSize := float64(worstStream.size)
worstAge := now.Sub(worstStream.infos[0].time).Seconds()
for id, stream := range q.streams { for id, stream := range q.streams {
thisSize := float64(stream.size) worst = id
thisAge := now.Sub(stream.infos[0].time).Seconds() size += stream.size
// cross multiply to avoid division by zero issues if size >= offset {
if worstSize*thisAge < thisSize*worstAge { break
// worstSize/worstAge < thisSize/thisAge -> this uses more bandwidth
worst = id
worstStream = stream
worstSize = thisSize
worstAge = thisAge
} }
} }
// Drop the oldest packet from the worst stream // drop the oldest packet from the stream
worstStream := q.streams[worst]
packet := worstStream.infos[0].packet packet := worstStream.infos[0].packet
worstStream.infos = worstStream.infos[1:] worstStream.infos = worstStream.infos[1:]
worstStream.size -= uint64(len(packet)) worstStream.size -= uint64(len(packet))

View File

@ -110,6 +110,7 @@ type peer struct {
queue packetQueue queue packetQueue
seq uint64 // this and idle are used to detect when to drop packets from queue seq uint64 // this and idle are used to detect when to drop packets from queue
idle bool idle bool
drop bool // set to true if we're dropping packets from the queue
} }
func (ps *peers) updateTables(from phony.Actor, table *lookupTable) { func (ps *peers) updateTables(from phony.Actor, table *lookupTable) {
@ -275,13 +276,19 @@ func (p *peer) sendPacketsFrom(from phony.Actor, packets [][]byte) {
} }
func (p *peer) _sendPackets(packets [][]byte) { func (p *peer) _sendPackets(packets [][]byte) {
size := p.queue.size
for _, packet := range packets { for _, packet := range packets {
p.queue.push(packet) p.queue.push(packet)
} }
if p.idle { switch {
case p.idle:
p.idle = false p.idle = false
p._handleIdle() p._handleIdle()
} else { case p.drop:
for p.queue.size > size {
p.queue.drop()
}
default:
p.intf.notifyQueued(p.seq) p.intf.notifyQueued(p.seq)
} }
} }
@ -303,17 +310,14 @@ func (p *peer) _handleIdle() {
p.intf.out(packets) p.intf.out(packets)
} else { } else {
p.idle = true p.idle = true
p.drop = false
} }
} }
func (p *peer) dropFromQueue(from phony.Actor, seq uint64) { func (p *peer) dropFromQueue(from phony.Actor, seq uint64) {
p.Act(from, func() { p.Act(from, func() {
switch { if seq == p.seq {
case seq != p.seq: p.drop = true
case p.queue.size < streamMsgSize:
case p.queue.drop():
p.core.log.Debugln("DEBUG dropped:", p.port, p.queue.size)
p.intf.notifyQueued(p.seq)
} }
}) })
} }