yggdrasil-go/src/yggdrasil/packetqueue.go
2020-05-16 17:07:47 -05:00

125 lines
3.3 KiB
Go

package yggdrasil
import (
"time"
)
type pqStreamID string
type pqPacketInfo struct {
packet []byte
time time.Time
}
type pqStream struct {
infos []pqPacketInfo
size uint64
}
// TODO separate queues per e.g. traffic flow
type packetQueue struct {
streams map[pqStreamID]pqStream
size uint64
}
// drop will remove a packet from the queue, returning it to the pool
// returns true if a packet was removed, false otherwise
func (q *packetQueue) drop() bool {
if q.size == 0 {
return false
}
// TODO? drop from a random stream
// odds proportional to size? bandwidth?
// 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
for id := range q.streams {
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 {
thisSize := float64(stream.size)
thisAge := now.Sub(stream.infos[0].time).Seconds()
// cross multiply to avoid division by zero issues
if worstSize*thisAge < thisSize*worstAge {
// worstSize/worstAge < thisSize/thisAge -> this uses more bandwidth
worst = id
worstStream = stream
worstSize = thisSize
worstAge = thisAge
}
}
// Drop the oldest packet from the worst stream
packet := worstStream.infos[0].packet
worstStream.infos = worstStream.infos[1:]
worstStream.size -= uint64(len(packet))
q.size -= uint64(len(packet))
pool_putBytes(packet)
// save the modified stream to queues
if len(worstStream.infos) > 0 {
q.streams[worst] = worstStream
} else {
delete(q.streams, worst)
}
return true
}
func (q *packetQueue) push(packet []byte) {
if q.streams == nil {
q.streams = make(map[pqStreamID]pqStream)
}
// get stream
id := pqStreamID(peer_getPacketCoords(packet)) // just coords for now
stream := q.streams[id]
// update stream
stream.infos = append(stream.infos, pqPacketInfo{packet, time.Now()})
stream.size += uint64(len(packet))
// save update to queues
q.streams[id] = stream
q.size += uint64(len(packet))
}
func (q *packetQueue) pop() ([]byte, bool) {
if len(q.streams) > 0 {
// get the stream that uses the least bandwidth
now := time.Now()
var best pqStreamID
for id := range q.streams {
best = id
break // get a random ID to start
}
bestStream := q.streams[best]
bestSize := float64(bestStream.size)
bestAge := now.Sub(bestStream.infos[0].time).Seconds()
for id, stream := range q.streams {
thisSize := float64(stream.size)
thisAge := now.Sub(stream.infos[0].time).Seconds()
// cross multiply to avoid division by zero issues
if bestSize*thisAge > thisSize*bestAge {
// bestSize/bestAge > thisSize/thisAge -> this uses less bandwidth
best = id
bestStream = stream
bestSize = thisSize
bestAge = thisAge
}
}
// get the oldest packet from the best stream
packet := bestStream.infos[0].packet
bestStream.infos = bestStream.infos[1:]
bestStream.size -= uint64(len(packet))
q.size -= uint64(len(packet))
// save the modified stream to queues
if len(bestStream.infos) > 0 {
q.streams[best] = bestStream
} else {
delete(q.streams, best)
}
return packet, true
}
return nil, false
}