Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 1 addition & 9 deletions router/dataplane_hbird.go
Original file line number Diff line number Diff line change
Expand Up @@ -368,14 +368,6 @@ func (p *scionPacketProcessor) validatePathMetaTimestamp() {
}
}

// Converts a flyover bandwidth value to bytes per second
func convertResBw(bw uint16) float64 {

// In this implementation, we choose to allow reservations up to 64 kBps
// Since the bandwidth field has 10 bits, we multiply by 64 to reach the target range
return float64(bw * 64)
}

func (p *scionPacketProcessor) checkReservationBandwidth() disposition {
// Only check bandwidth if packet is given priority.
// Bandwidth check is NOT performed for late packets that have flyover but no priority.
Expand All @@ -391,7 +383,7 @@ func (p *scionPacketProcessor) checkReservationBandwidth() disposition {

// Get the token bucket or add a new one.
resKey := uint64(p.flyoverField.ResID) + uint64(ingress)<<22 + uint64(egress)<<38
resBw := convertResBw(p.flyoverField.Bw)
resBw := tokenbucket.ConvertBW(p.flyoverField.Bw)
now := time.Now()
v, _ := p.d.tokenBuckets.LoadOrStore(
resKey,
Expand Down
129 changes: 75 additions & 54 deletions router/dataplane_hbird_test.go

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion router/dataplane_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import (
underlayconn "github.com/scionproto/scion/private/underlay/conn"
"github.com/scionproto/scion/router/control"
"github.com/scionproto/scion/router/mock_router"
pr "github.com/scionproto/scion/router/priority"
)

var testKey = []byte("testkey_xxxxxxxx")
Expand Down Expand Up @@ -466,7 +467,7 @@ func TestSlowPathProcessing(t *testing.T) {
dp := tc.prepareDP(ctrl)

rp := tc.mockMsg()
pkt := NewPacket(rp, nil, nil, tc.srcInterface, 0)
pkt := NewPacket(rp, nil, nil, tc.srcInterface, 0, pr.WithBestEffort)
pkt.Link = newMockLink(tc.srcInterface)

processor := newPacketProcessor(dp)
Expand Down
35 changes: 18 additions & 17 deletions router/dataplane_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import (
"github.com/scionproto/scion/router"
"github.com/scionproto/scion/router/control"
"github.com/scionproto/scion/router/mock_router"
pr "github.com/scionproto/scion/router/priority"
)

var (
Expand Down Expand Up @@ -877,7 +878,7 @@ func TestProcessPkt(t *testing.T) {
if afterProcessing {
dstAddr = &net.UDPAddr{IP: dst.IP().AsSlice(), Port: dstUDPPort}
}
return router.NewPacket(toBytes(t, spkt, dpath), nil, dstAddr, ingress, egress)
return router.NewPacket(toBytes(t, spkt, dpath), nil, dstAddr, ingress, egress, pr.WithBestEffort)
},
assertFunc: notDiscarded,
},
Expand Down Expand Up @@ -920,7 +921,7 @@ func TestProcessPkt(t *testing.T) {
if afterProcessing {
dstAddr = &net.UDPAddr{IP: dst.IP().AsSlice(), Port: topology.EndhostPort}
}
return router.NewPacket(toBytes(t, spkt, dpath), nil, dstAddr, ingress, egress)
return router.NewPacket(toBytes(t, spkt, dpath), nil, dstAddr, ingress, egress, pr.WithBestEffort)
},
assertFunc: discarded,
},
Expand Down Expand Up @@ -952,7 +953,7 @@ func TestProcessPkt(t *testing.T) {
dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].Mac)
egress = 1
}
return router.NewPacket(toBytes(t, spkt, dpath), nil, nil, ingress, egress)
return router.NewPacket(toBytes(t, spkt, dpath), nil, nil, ingress, egress, pr.WithBestEffort)
},
assertFunc: notDiscarded,
},
Expand Down Expand Up @@ -984,7 +985,7 @@ func TestProcessPkt(t *testing.T) {
dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].Mac)
egress = 2
}
return router.NewPacket(toBytes(t, spkt, dpath), nil, nil, ingress, egress)
return router.NewPacket(toBytes(t, spkt, dpath), nil, nil, ingress, egress, pr.WithBestEffort)
},
assertFunc: notDiscarded,
},
Expand Down Expand Up @@ -1018,7 +1019,7 @@ func TestProcessPkt(t *testing.T) {
} else {
dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].Mac)
}
return router.NewPacket(toBytes(t, spkt, dpath), nil, nil, ingress, egress)
return router.NewPacket(toBytes(t, spkt, dpath), nil, nil, ingress, egress, pr.WithBestEffort)
},
assertFunc: notDiscarded,
},
Expand Down Expand Up @@ -1085,7 +1086,7 @@ func TestProcessPkt(t *testing.T) {
// it is still the same. That is the key behavior.
egress = 2
}
return router.NewPacket(toBytes(t, spkt, dpath), nil, nil, ingress, egress)
return router.NewPacket(toBytes(t, spkt, dpath), nil, nil, ingress, egress, pr.WithBestEffort)
},
assertFunc: notDiscarded,
},
Expand Down Expand Up @@ -1157,7 +1158,7 @@ func TestProcessPkt(t *testing.T) {
// of HF1 will fail. Otherwise, this isn't visible because we changed segment.
egress = 1
}
return router.NewPacket(toBytes(t, spkt, dpath), nil, nil, ingress, egress)
return router.NewPacket(toBytes(t, spkt, dpath), nil, nil, ingress, egress, pr.WithBestEffort)
},
assertFunc: notDiscarded,
},
Expand Down Expand Up @@ -1230,7 +1231,7 @@ func TestProcessPkt(t *testing.T) {
dpath.InfoFields[1].UpdateSegID(dpath.HopFields[2].Mac)
egress = 2
}
return router.NewPacket(toBytes(t, spkt, dpath), nil, nil, ingress, egress)
return router.NewPacket(toBytes(t, spkt, dpath), nil, nil, ingress, egress, pr.WithBestEffort)
},
assertFunc: notDiscarded,
},
Expand Down Expand Up @@ -1309,7 +1310,7 @@ func TestProcessPkt(t *testing.T) {
// this test.
dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].Mac)
}
return router.NewPacket(toBytes(t, spkt, dpath), nil, nil, ingress, egress)
return router.NewPacket(toBytes(t, spkt, dpath), nil, nil, ingress, egress, pr.WithBestEffort)
},
assertFunc: notDiscarded,
},
Expand Down Expand Up @@ -1342,7 +1343,7 @@ func TestProcessPkt(t *testing.T) {
// The link is specific to the sibling. It has the address. So we don't expect:
// dstAddr = &net.UDPAddr{IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}
}
return router.NewPacket(toBytes(t, spkt, dpath), nil, dstAddr, ingress, egress)
return router.NewPacket(toBytes(t, spkt, dpath), nil, dstAddr, ingress, egress, pr.WithBestEffort)
},
assertFunc: notDiscarded,
},
Expand Down Expand Up @@ -1400,7 +1401,7 @@ func TestProcessPkt(t *testing.T) {
dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].Mac)
}

return router.NewPacket(toBytes(t, spkt, dpath), nil, dstAddr, ingress, egress)
return router.NewPacket(toBytes(t, spkt, dpath), nil, dstAddr, ingress, egress, pr.WithBestEffort)
},
assertFunc: notDiscarded,
},
Expand Down Expand Up @@ -1442,7 +1443,7 @@ func TestProcessPkt(t *testing.T) {
Port: dstUDPPort,
}
}
return router.NewPacket(toBytes(t, spkt, dpath), nil, dstAddr, ingress, egress)
return router.NewPacket(toBytes(t, spkt, dpath), nil, dstAddr, ingress, egress, pr.WithBestEffort)
},
assertFunc: notDiscarded,
},
Expand Down Expand Up @@ -1507,7 +1508,7 @@ func TestProcessPkt(t *testing.T) {
Port: dstUDPPort,
}
}
return router.NewPacket(toBytes(t, spkt, dpath), nil, dstAddr, ingress, egress)
return router.NewPacket(toBytes(t, spkt, dpath), nil, dstAddr, ingress, egress, pr.WithBestEffort)
},
assertFunc: notDiscarded,
},
Expand Down Expand Up @@ -1547,7 +1548,7 @@ func TestProcessPkt(t *testing.T) {
}
ingress := uint16(2)
egress := uint16(21)
return router.NewPacket(toBytes(t, spkt, dpath), nil, nil, ingress, egress)
return router.NewPacket(toBytes(t, spkt, dpath), nil, nil, ingress, egress, pr.WithBestEffort)
},
assertFunc: discarded,
},
Expand Down Expand Up @@ -1606,7 +1607,7 @@ func TestProcessPkt(t *testing.T) {
require.NoError(t, sp.IncPath())
egress = 1
}
return router.NewPacket(toBytes(t, spkt, sp), nil, nil, ingress, egress)
return router.NewPacket(toBytes(t, spkt, sp), nil, nil, ingress, egress, pr.WithBestEffort)
},
assertFunc: notDiscarded,
},
Expand Down Expand Up @@ -1649,7 +1650,7 @@ func TestProcessPkt(t *testing.T) {
dpath.Info.UpdateSegID(dpath.FirstHop.Mac)
egress = 2
}
return router.NewPacket(toBytes(t, spkt, dpath), nil, nil, ingress, egress)
return router.NewPacket(toBytes(t, spkt, dpath), nil, nil, ingress, egress, pr.WithBestEffort)
},
assertFunc: notDiscarded,
},
Expand Down Expand Up @@ -1905,7 +1906,7 @@ func toIP(
} else {
egress = 0
}
return router.NewPacket(toBytes(t, spkt, path), nil, dstAddr, ingress, egress)
return router.NewPacket(toBytes(t, spkt, path), nil, dstAddr, ingress, egress, pr.WithBestEffort)
}

func computeMAC(t *testing.T, key []byte, info path.InfoField, hf path.HopField) [path.MacLen]byte {
Expand Down
11 changes: 10 additions & 1 deletion router/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/scionproto/scion/router/bfd"
"github.com/scionproto/scion/router/control"
"github.com/scionproto/scion/router/mock_router"
pr "github.com/scionproto/scion/router/priority"
)

var (
Expand Down Expand Up @@ -76,7 +77,12 @@ func newMockLink(ingress uint16) Link { return &MockLink{ifID: ingress} }

// NewPacket makes a mock packet. It has shortcomings which makes it unsuited for some tests: it
// refers to a mock link that has the scope Internal in all cases, and a blank remote address.
func NewPacket(raw []byte, src, dst *net.UDPAddr, ingress, egress uint16) *Packet {
func NewPacket(
raw []byte,
src, dst *net.UDPAddr,
ingress, egress uint16,
priority pr.PriorityLabel,
) *Packet {
pktBuf := &([bufSize]byte{})
p := Packet{
buffer: pktBuf,
Expand All @@ -92,6 +98,9 @@ func NewPacket(raw []byte, src, dst *net.UDPAddr, ingress, egress uint16) *Packe
}
p.RawPacket = p.RawPacket[:len(raw)]
copy(p.RawPacket, raw)

p.PriorityLabel = priority

return &p
}

Expand Down
1 change: 1 addition & 0 deletions router/tokenbucket/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ go_test(
deps = [
":go_default_library",
"@com_github_stretchr_testify//assert:go_default_library",
"@com_github_stretchr_testify//require:go_default_library",
],
)
105 changes: 89 additions & 16 deletions router/tokenbucket/tokenbucket.go
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
package tokenbucket

import (
"fmt"
"math/bits"
"sync"
"time"
)

type TokenBucket struct {
CurrentTokens float64
CurrentTokens int64
LastTimeApplied time.Time

//Burst Size
CBS float64
// Committed Burst Size (burst). In bytes per second.
CBS int64

//In bytes per second
CIR float64
// Committed Information Rate (rate). In bytes per second.
CIR int64

// Lock
lock sync.Mutex
}

// Initializes a new tockenbucket for the given burstSize and rate
func NewTokenBucket(initialTime time.Time, burstSize float64, rate float64) *TokenBucket {
func NewTokenBucket(initialTime time.Time, burstSize int64, rate int64) *TokenBucket {
return &TokenBucket{
CurrentTokens: rate,
CIR: rate,
Expand All @@ -30,14 +32,14 @@ func NewTokenBucket(initialTime time.Time, burstSize float64, rate float64) *Tok
}

// Sets a new rate for the token bucket
func (t *TokenBucket) SetRate(rate float64) {
func (t *TokenBucket) SetRate(rate int64) {
t.lock.Lock()
defer t.lock.Unlock()
t.CIR = rate
}

// Sets a new burst size for the token bucket
func (t *TokenBucket) SetBurstSize(burstSize float64) {
func (t *TokenBucket) SetBurstSize(burstSize int64) {
t.lock.Lock()
defer t.lock.Unlock()
t.CBS = burstSize
Expand All @@ -52,22 +54,93 @@ func (t *TokenBucket) Apply(size int, now time.Time) bool {
// Apply() is expected to be called from different threads
// As a consequence, it is possible for now to be older than LastTimeApplied
if !now.Before(t.LastTimeApplied) {
t.CurrentTokens += now.Sub(t.LastTimeApplied).Seconds() * t.CIR
t.CurrentTokens += now.Sub(t.LastTimeApplied).Nanoseconds() * t.CIR / (1e9)
t.CurrentTokens = min(t.CurrentTokens, t.CBS)
t.LastTimeApplied = now
}
if t.CurrentTokens >= float64(size) {
t.CurrentTokens -= float64(size)
if t.CurrentTokens >= int64(size) {
t.CurrentTokens -= int64(size)
return true
}
return false
}

// This function calculates the minimal value of two float64.
func min(a float64, b float64) float64 {
if a > b {
return b
// ConvertBW converts a 10-bit Hummingbird bandwidth code into bytes per second.
//
// The 10 bits are interpreted as:
// - bits [9:5]: exponent e (5 bits)
// - bits [4:0]: mantissa m (5 bits)
//
// The decoded real bandwidth is:
// - m, if e == 0
// - (m + 32) * 2^(e-1), if e > 0
//
// ConvertBW is monotonic over the valid encoded range [0, 1023].
func ConvertBW(bw uint16) int64 {
// e=0: 0..31
// e=1: 32..63
// e=2: 64,66,68,..126
// e=3: 128,132,..252
// e=31: ~ 2^35..2^36

exponent := bw >> 5
mantissa := bw & 0x1f

var bytesPerSecond int64
if exponent == 0 {
// For exponent=0, the value is represented directly by mantissa.
bytesPerSecond = int64(mantissa)
} else {
return a
// For exponent>0, restore the implicit +32 and scale by 2^(exponent-1):
// result = (mantissa + 32) * 2^(exponent - 1)
bytesPerSecond = int64(mantissa+32) << (exponent - 1)
}

return bytesPerSecond
}

const maxEncodedBW = (1 << 10) - 1

var maxRealBw = ConvertBW(maxEncodedBW)

// RealBwToEncoded converts real bandwidth in bytes per second to a 10-bit
// Hummingbird bandwidth code.
//
// Quantization policy is ceil: when an exact representation does not exist,
// the returned code is the smallest encodable value whose decoded bandwidth is
// greater than or equal to bw.
//
// Errors:
// - bw < 0
// - bw exceeds the maximum representable value ConvertBW(1023)
func RealBwToEncoded(bw int64) (uint16, error) {
switch {
case bw < 0:
return 0, fmt.Errorf("bandwidth must be non-negative: %d", bw)
case bw == 0:
return 0, nil
case bw > maxRealBw:
return 0, fmt.Errorf("bandwidth %d exceeds max representable %d", bw, maxRealBw)
case bw <= 31:
// exponent=0 directly represents 0..31 via mantissa.
return uint16(bw), nil
default:
}

// For exponent>0, decoded bandwidth is:
// (mantissa + 32) * 2^(exponent - 1), with mantissa in [0, 31].
//
// First choose the minimum exponent e such that bw <= 63*2^(e-1).
// Let x = ceil(bw/63). Then e-1 = ceil(log2(x)).
x := uint64((bw + 62) / 63)
exponentMinus1 := bits.Len64(x - 1)
exponent := uint16(exponentMinus1 + 1)

// With the chosen exponent, compute the minimum q=(mantissa+32) satisfying:
// q * 2^(e-1) >= bw => q = ceil(bw / 2^(e-1)).
scale := int64(1) << exponentMinus1
q := (bw + scale - 1) / scale
mantissa := uint16(q - 32)

return (exponent << 5) | mantissa, nil
}
Loading
Loading