diff --git a/router/dataplane_hbird.go b/router/dataplane_hbird.go index 02f5e9ed01..d64356b056 100644 --- a/router/dataplane_hbird.go +++ b/router/dataplane_hbird.go @@ -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. @@ -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, diff --git a/router/dataplane_hbird_test.go b/router/dataplane_hbird_test.go index 45fdea2c5f..2228c0e63c 100644 --- a/router/dataplane_hbird_test.go +++ b/router/dataplane_hbird_test.go @@ -109,7 +109,8 @@ func TestProcessHbirdPacket(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, }, @@ -142,7 +143,8 @@ func TestProcessHbirdPacket(t *testing.T) { dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.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, }, @@ -175,7 +177,8 @@ func TestProcessHbirdPacket(t *testing.T) { dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.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, }, @@ -210,7 +213,8 @@ func TestProcessHbirdPacket(t *testing.T) { } else { dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.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, }, @@ -239,7 +243,8 @@ func TestProcessHbirdPacket(t *testing.T) { dpath.Base.PathMeta.CurrHF = 4 ingress := uint16(1) egress := uint16(0) - 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, }, @@ -306,7 +311,8 @@ func TestProcessHbirdPacket(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, }, @@ -378,7 +384,8 @@ func TestProcessHbirdPacket(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, }, @@ -451,7 +458,8 @@ func TestProcessHbirdPacket(t *testing.T) { dpath.InfoFields[1].UpdateSegID(dpath.HopFields[2].HopField.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, }, @@ -530,7 +538,8 @@ func TestProcessHbirdPacket(t *testing.T) { // this test. dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.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, }, @@ -564,7 +573,8 @@ func TestProcessHbirdPacket(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, }, @@ -623,7 +633,8 @@ func TestProcessHbirdPacket(t *testing.T) { dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.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, }, @@ -645,7 +656,7 @@ func TestProcessHbirdPacket(t *testing.T) { {HopField: path.HopField{ConsIngress: 41, ConsEgress: 40}}, {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, {HopField: path.HopField{ConsIngress: 1, ConsEgress: 0}, - Flyover: true, ResStartTime: 123, Duration: 304, Bw: 16}, + Flyover: true, ResStartTime: 123, Duration: 304, Bw: 129}, } dpath.Base.PathMeta.SegLen[0] = 6 + 5 // 2 hops + 1 flyover dpath.Base.NumLines = 6 + 5 @@ -660,7 +671,8 @@ func TestProcessHbirdPacket(t *testing.T) { dpath.HopFields[2].HopField) 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.WithPriority) }, assertFunc: notDiscarded, }, @@ -680,11 +692,11 @@ func TestProcessHbirdPacket(t *testing.T) { spkt.SrcIA = addr.MustParseIA("1-ff00:0:110") dpath.HopFields = []hummingbird.FlyoverHopField{ {HopField: path.HopField{ConsIngress: 0, ConsEgress: 1}, - Flyover: true, ResStartTime: 123, Duration: 304, Bw: 16}, + Flyover: true, ResStartTime: 123, Duration: 304, Bw: 129}, {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}, - Flyover: true, ResStartTime: 123, Duration: 304, Bw: 16}, + Flyover: true, ResStartTime: 123, Duration: 304, Bw: 129}, {HopField: path.HopField{ConsIngress: 41, ConsEgress: 40}, - Flyover: true, ResStartTime: 123, Duration: 304, Bw: 16}, + Flyover: true, ResStartTime: 123, Duration: 304, Bw: 129}, } dpath.Base.PathMeta.CurrHF = 0 dpath.Base.PathMeta.SegLen[0] = 5 * 3 // 3 flyovers @@ -700,7 +712,8 @@ func TestProcessHbirdPacket(t *testing.T) { dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.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.WithPriority) }, assertFunc: notDiscarded, }, @@ -720,11 +733,11 @@ func TestProcessHbirdPacket(t *testing.T) { spkt.SrcIA = addr.MustParseIA("1-ff00:0:110") dpath.HopFields = []hummingbird.FlyoverHopField{ {HopField: path.HopField{ConsIngress: 0, ConsEgress: 1}, - Flyover: true, ResStartTime: 5, Duration: 2, Bw: 16}, + Flyover: true, ResStartTime: 5, Duration: 2, Bw: 129}, {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}, - Flyover: true, ResStartTime: 123, Duration: 304, Bw: 16}, + Flyover: true, ResStartTime: 123, Duration: 304, Bw: 129}, {HopField: path.HopField{ConsIngress: 41, ConsEgress: 40}, - Flyover: true, ResStartTime: 123, Duration: 304, Bw: 16}, + Flyover: true, ResStartTime: 123, Duration: 304, Bw: 129}, } dpath.Base.PathMeta.CurrHF = 0 dpath.Base.PathMeta.SegLen[0] = 5 * 3 // 3 flyovers @@ -733,18 +746,17 @@ func TestProcessHbirdPacket(t *testing.T) { dpath.InfoFields[0], dpath.HopFields[0], dpath.Base.PathMeta) ingress := uint16(0) egress := uint16(0) - var pkt *router.Packet + priority := pr.WithPriority if afterProcessing { dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) assert.NoError(t, dpath.IncPath(hummingbird.FlyoverLines)) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) egress = 1 - pkt = router.NewPacket(toBytes(t, spkt, dpath), nil, nil, ingress, egress) - pkt.PriorityLabel = pr.WithBestEffort - return pkt + priority = pr.WithBestEffort } - return router.NewPacket(toBytes(t, spkt, dpath), nil, nil, ingress, egress) + return router.NewPacket(toBytes(t, spkt, dpath), nil, nil, ingress, egress, + priority) }, assertFunc: notDiscarded, }, @@ -768,9 +780,9 @@ func TestProcessHbirdPacket(t *testing.T) { {HopField: path.HopField{ConsIngress: 0, ConsEgress: 1}, Flyover: true, ResStartTime: 123, Duration: 304, Bw: 1}, {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}, - Flyover: true, ResStartTime: 123, Duration: 304, Bw: 16}, + Flyover: true, ResStartTime: 123, Duration: 304, Bw: 1}, {HopField: path.HopField{ConsIngress: 41, ConsEgress: 40}, - Flyover: true, ResStartTime: 123, Duration: 304, Bw: 16}, + Flyover: true, ResStartTime: 123, Duration: 304, Bw: 1}, } dpath.Base.PathMeta.CurrHF = 0 dpath.Base.PathMeta.SegLen[0] = 5 * 3 // 3 flyovers @@ -785,7 +797,8 @@ func TestProcessHbirdPacket(t *testing.T) { scionudpLayer.SrcPort = uint16(srcUDPPort) scionudpLayer.DstPort = uint16(dstUDPPort) scionudpLayer.SetNetworkLayerForChecksum(spkt) - err := gopacket.SerializeLayers(buffer, gopacket.SerializeOptions{FixLengths: true}, + err := gopacket.SerializeLayers(buffer, + gopacket.SerializeOptions{FixLengths: true}, spkt, scionudpLayer, gopacket.Payload(largePayload)) require.NoError(t, err) return buffer.Bytes() @@ -793,18 +806,17 @@ func TestProcessHbirdPacket(t *testing.T) { ingress := uint16(0) egress := uint16(0) - var pkt *router.Packet + priority := pr.WithPriority if afterProcessing { dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) assert.NoError(t, dpath.IncPath(hummingbird.FlyoverLines)) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) egress = 1 - pkt = router.NewPacket(serializeLargePayload(spkt, dpath), nil, nil, ingress, egress) - pkt.PriorityLabel = pr.WithBestEffort - return pkt + priority = pr.WithBestEffort } - return router.NewPacket(serializeLargePayload(spkt, dpath), nil, nil, ingress, egress) + return router.NewPacket(serializeLargePayload(spkt, dpath), nil, nil, + ingress, egress, priority) }, assertFunc: notDiscarded, }, @@ -825,7 +837,7 @@ func TestProcessHbirdPacket(t *testing.T) { dpath.HopFields = []hummingbird.FlyoverHopField{ {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, {HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, - Flyover: true, Bw: 5, ResStartTime: 123, Duration: 304}, + Flyover: true, Bw: 129, ResStartTime: 123, Duration: 304}, {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, } @@ -843,7 +855,8 @@ func TestProcessHbirdPacket(t *testing.T) { dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.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.WithPriority) }, assertFunc: notDiscarded, }, @@ -864,7 +877,7 @@ func TestProcessHbirdPacket(t *testing.T) { dpath.HopFields = []hummingbird.FlyoverHopField{ {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, {HopField: path.HopField{ConsIngress: 2, ConsEgress: 1}, - Flyover: true, ResID: 42, ResStartTime: 5, Duration: 301, Bw: 16}, + Flyover: true, ResID: 42, ResStartTime: 5, Duration: 301, Bw: 129}, {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, } dpath.Base.NumLines = 11 @@ -884,7 +897,8 @@ func TestProcessHbirdPacket(t *testing.T) { dpath.InfoFields[0].UpdateSegID( computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField)) } - return router.NewPacket(toBytes(t, spkt, dpath), nil, nil, ingress, egress) + return router.NewPacket(toBytes(t, spkt, dpath), nil, nil, ingress, egress, + pr.WithPriority) }, assertFunc: notDiscarded, }, @@ -906,7 +920,7 @@ func TestProcessHbirdPacket(t *testing.T) { dpath.HopFields = []hummingbird.FlyoverHopField{ {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, {HopField: path.HopField{ConsIngress: 1, ConsEgress: 3}, - Flyover: true, ResID: 42, ResStartTime: 5, Duration: 301, Bw: 16}, + Flyover: true, ResID: 42, ResStartTime: 5, Duration: 301, Bw: 129}, {HopField: path.HopField{ConsIngress: 50, ConsEgress: 51}}, } dpath.Base.NumLines = 11 @@ -917,7 +931,8 @@ func TestProcessHbirdPacket(t *testing.T) { var dstAddr *net.UDPAddr ingress := uint16(1) egress := uint16(3) - return router.NewPacket(toBytes(t, spkt, dpath), nil, dstAddr, ingress, egress) + return router.NewPacket(toBytes(t, spkt, dpath), nil, dstAddr, ingress, egress, + pr.WithPriority) }, assertFunc: notDiscarded, }, @@ -956,7 +971,7 @@ func TestProcessHbirdPacket(t *testing.T) { HopFields: []hummingbird.FlyoverHopField{ {HopField: path.HopField{ConsIngress: 31, ConsEgress: 0}}, // Src, {HopField: path.HopField{ConsIngress: 0, ConsEgress: 51}, - Flyover: true, Bw: 5, ResStartTime: 5, Duration: 310}, // IA 110 + Flyover: true, Bw: 129, ResStartTime: 5, Duration: 310}, // IA 110 // xover here. {HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}}, // IA 110 {HopField: path.HopField{ConsIngress: 0, ConsEgress: 1}}, // Dst @@ -973,7 +988,7 @@ func TestProcessHbirdPacket(t *testing.T) { if afterProcessing { dpath.HopFields[1].Flyover = false dpath.HopFields[2].Flyover = true - dpath.HopFields[2].Bw = 5 + dpath.HopFields[2].Bw = 129 dpath.HopFields[2].ResStartTime = 5 dpath.HopFields[2].Duration = 310 dpath.HopFields[1].HopField.Mac = @@ -998,7 +1013,8 @@ func TestProcessHbirdPacket(t *testing.T) { dpath.HopFields[1], )) } - return router.NewPacket(toBytes(t, spkt, dpath), nil, dstAddr, ingress, egress) + return router.NewPacket(toBytes(t, spkt, dpath), nil, dstAddr, ingress, egress, + pr.WithPriority) }, assertFunc: notDiscarded, }, @@ -1039,7 +1055,7 @@ func TestProcessHbirdPacket(t *testing.T) { {HopField: path.HopField{ConsIngress: 0, ConsEgress: 51}}, // IA 110 // xover here. {HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}, // IA 110 - Flyover: true, Bw: 5, ResStartTime: 5, Duration: 310}, + Flyover: true, Bw: 129, ResStartTime: 5, Duration: 310}, {HopField: path.HopField{ConsIngress: 0, ConsEgress: 1}}, // Dst }, } @@ -1052,7 +1068,7 @@ func TestProcessHbirdPacket(t *testing.T) { // Restore flyover to xover ingress hop from the egress one. dpath.HopFields[2].Flyover = false dpath.HopFields[1].Flyover = true - dpath.HopFields[1].Bw = 5 + dpath.HopFields[1].Bw = 129 dpath.HopFields[1].ResStartTime = 5 dpath.HopFields[1].Duration = 310 dpath.HopFields[2].HopField.Mac = @@ -1061,7 +1077,8 @@ func TestProcessHbirdPacket(t *testing.T) { dpath.PathMeta.SegLen[1] -= 2 require.NoError(t, dpath.IncPath(hummingbird.FlyoverLines)) } - pkt := router.NewPacket(toBytes(t, spkt, dpath), nil, nil, ingress, egress) + pkt := router.NewPacket(toBytes(t, spkt, dpath), nil, nil, ingress, egress, + pr.WithPriority) // Replace the link of the packet with the one from dataplane. ifaces := router.ExtractInterfaces(dp) // At the xover egress border router, the packet enters the BR via 0, but the @@ -1110,7 +1127,7 @@ func TestProcessHbirdPacket(t *testing.T) { HopFields: []hummingbird.FlyoverHopField{ {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, {HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, - Flyover: true, Bw: 5, ResStartTime: 123, Duration: 304}, + Flyover: true, Bw: 129, ResStartTime: 123, Duration: 304}, {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, }, } @@ -1137,7 +1154,8 @@ func TestProcessHbirdPacket(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.WithPriority) }, assertFunc: notDiscarded, }, @@ -1178,7 +1196,7 @@ func TestProcessHbirdPacket(t *testing.T) { HopFields: []hummingbird.FlyoverHopField{ {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, {HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, - Flyover: true, Bw: 5, ResStartTime: 123, Duration: 304}, + Flyover: true, Bw: 129, ResStartTime: 123, Duration: 304}, {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, }, } @@ -1211,7 +1229,8 @@ func TestProcessHbirdPacket(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.WithPriority) }, assertFunc: notDiscarded, }, @@ -1255,7 +1274,7 @@ func TestProcessHbirdPacket(t *testing.T) { {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, {HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, - Flyover: true, Bw: 5, ResStartTime: 123, Duration: 304}, + Flyover: true, Bw: 129, ResStartTime: 123, Duration: 304}, {HopField: path.HopField{ConsIngress: 50, ConsEgress: 51}}, // There has to be a 4th hop to make // the 3rd router agree that the packet @@ -1287,7 +1306,8 @@ func TestProcessHbirdPacket(t *testing.T) { dpath.InfoFields[1].UpdateSegID(dpath.HopFields[2].HopField.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.WithPriority) }, assertFunc: notDiscarded, }, @@ -1328,7 +1348,7 @@ func TestProcessHbirdPacket(t *testing.T) { HopFields: []hummingbird.FlyoverHopField{ {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, {HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, - Flyover: true, Bw: 5, ResStartTime: 123, Duration: 304}, + Flyover: true, Bw: 129, ResStartTime: 123, Duration: 304}, {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, {HopField: path.HopField{ConsIngress: 50, ConsEgress: 51}}, // The second segment (4th hop) has to be @@ -1371,7 +1391,8 @@ func TestProcessHbirdPacket(t *testing.T) { t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) dpath.InfoFields[0].UpdateSegID(scionMac) } - return router.NewPacket(toBytes(t, spkt, dpath), nil, nil, ingress, egress) + return router.NewPacket(toBytes(t, spkt, dpath), nil, nil, ingress, egress, + pr.WithPriority) }, assertFunc: notDiscarded, }, @@ -1443,7 +1464,7 @@ func TestProcessHbirdSCMP(t *testing.T) { raw := toBytes(t, spkt, dpath) original := bytes.Clone(raw) - pkt := router.NewPacket(raw, nil, nil, 1, 0) + pkt := router.NewPacket(raw, nil, nil, 1, 0, pr.WithBestEffort) pkt.Link = router.ExtractInterfaces(dp)[1] return pkt, original }, diff --git a/router/dataplane_internal_test.go b/router/dataplane_internal_test.go index 91a4d71430..e3955d4695 100644 --- a/router/dataplane_internal_test.go +++ b/router/dataplane_internal_test.go @@ -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") @@ -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) diff --git a/router/dataplane_test.go b/router/dataplane_test.go index 93339017f5..d6c71f724e 100644 --- a/router/dataplane_test.go +++ b/router/dataplane_test.go @@ -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 ( @@ -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, }, @@ -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, }, @@ -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, }, @@ -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, }, @@ -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, }, @@ -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, }, @@ -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, }, @@ -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, }, @@ -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, }, @@ -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, }, @@ -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, }, @@ -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, }, @@ -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, }, @@ -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, }, @@ -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, }, @@ -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, }, @@ -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 { diff --git a/router/export_test.go b/router/export_test.go index 6ae1abf728..93f36d33fc 100644 --- a/router/export_test.go +++ b/router/export_test.go @@ -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 ( @@ -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, @@ -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 } diff --git a/router/tokenbucket/BUILD.bazel b/router/tokenbucket/BUILD.bazel index e8d2faccc2..a894d58108 100644 --- a/router/tokenbucket/BUILD.bazel +++ b/router/tokenbucket/BUILD.bazel @@ -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", ], ) diff --git a/router/tokenbucket/tokenbucket.go b/router/tokenbucket/tokenbucket.go index aa9f45198a..892f743d33 100644 --- a/router/tokenbucket/tokenbucket.go +++ b/router/tokenbucket/tokenbucket.go @@ -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, @@ -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 @@ -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 } diff --git a/router/tokenbucket/tokenbucket_test.go b/router/tokenbucket/tokenbucket_test.go index 5a545d27c0..63023a6fce 100644 --- a/router/tokenbucket/tokenbucket_test.go +++ b/router/tokenbucket/tokenbucket_test.go @@ -15,10 +15,12 @@ package tokenbucket_test import ( + "fmt" "testing" "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/scionproto/scion/router/tokenbucket" ) @@ -141,3 +143,115 @@ func TestTokenBucketAlgorithm(t *testing.T) { }) } } + +// TestConvertBW checks that ConvertBW(BW uint16) works as expected. +// The 10 bits of BW are divided into 5 for mantissa, and 5 for exponent. +// Always positive and integer. +// The expected value comes from this definition: +// +// result = mantissa iff exponent = 0 +// result = (mantissa+32) * 2^(exponent - 1) iff exponent > 0 +func TestConvertBW(t *testing.T) { + testCases := map[string]struct { + bw uint16 + expected int64 + }{} + + addCase := func(name string, mantissa uint16, exponent uint16, expected int64) { + bw := (exponent << 5) | mantissa + testCases[name] = struct { + bw uint16 + expected int64 + }{ + bw: bw, + expected: expected, + } + } + + // All cases for exponent=0: + // result = mantissa + for mantissa := uint16(0); mantissa < 32; mantissa++ { + addCase(fmt.Sprintf("m%d_e0", mantissa), mantissa, 0, int64(mantissa)) + } + + // All cases for exponent=1: + // result = (mantissa+32) * 2^(1-1) = (mantissa+32) * 1 + for mantissa := uint16(0); mantissa < 32; mantissa++ { + addCase(fmt.Sprintf("m%d_e1", mantissa), mantissa, 1, int64(mantissa+32)) + } + + // Edge mantissas across all exponents: + // mantissa = 0 -> result = (0+32) * 2^(e-1), for e>0 + // mantissa = 31 -> result = (31+32) * 2^(e-1), for e>0 + for exponent := uint16(0); exponent < 32; exponent++ { + if exponent == 0 { + addCase("m0_e0_edge", 0, 0, 0) + addCase("m31_e0_edge", 31, 0, 31) + continue + } + addCase(fmt.Sprintf("m0_e%d", exponent), 0, exponent, int64(32)<<(exponent-1)) + addCase(fmt.Sprintf("m31_e%d", exponent), 31, exponent, int64(63)<<(exponent-1)) + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + assert.Equal(t, tc.expected, tokenbucket.ConvertBW(tc.bw)) + }) + } +} + +// TestRealBwToEncodedRoundTrip verifies real->encoded->real behavior under ceil quantization. +func TestRealBwToEncodedRoundTrip(t *testing.T) { + // Include boundaries and interior values that are often non-representable. + realBws := []int64{ + 0, 1, 2, 30, 31, 32, 33, 62, 63, 64, 65, 66, + 127, 128, 129, 1023, 1024, 1025, + tokenbucket.ConvertBW(1023) - 1, + tokenbucket.ConvertBW(1023), + } + + for _, realBw := range realBws { + t.Run(fmt.Sprintf("%d", realBw), func(t *testing.T) { + encoded, err := tokenbucket.RealBwToEncoded(realBw) + require.NoError(t, err) + + decoded := tokenbucket.ConvertBW(encoded) + // Ceil guarantee: never under-encode requested bandwidth. + assert.GreaterOrEqual(t, decoded, realBw, "real=%d encoded=%d decoded=%d", + realBw, encoded, decoded) + + if encoded > 0 { + // Minimality guarantee: previous codepoint is strictly below requested bandwidth. + prevDecoded := tokenbucket.ConvertBW(encoded - 1) + assert.Less(t, prevDecoded, realBw, "real=%d encoded=%d prevDecoded=%d", + realBw, encoded, prevDecoded) + } + }) + } +} + +// TestEncodedToRealToEncodedIdempotent verifies that already-representable +// values stay stable through real->encoded conversion. +func TestEncodedToRealToEncodedIdempotent(t *testing.T) { + for encoded := uint16(0); encoded < 1024; encoded++ { + t.Run(fmt.Sprintf("%d", encoded), func(t *testing.T) { + realBw := tokenbucket.ConvertBW(encoded) + encoded2, err := tokenbucket.RealBwToEncoded(realBw) + require.NoError(t, err) + assert.Equal(t, encoded, encoded2, "encoded=%d real=%d", encoded, realBw) + }) + } +} + +func TestRealBwToEncodedErrors(t *testing.T) { + t.Run("-1", func(t *testing.T) { + _, err := tokenbucket.RealBwToEncoded(-1) + require.Error(t, err) + }) + + t.Run("1023", func(t *testing.T) { + maxRealBw := tokenbucket.ConvertBW(1023) + _, err := tokenbucket.RealBwToEncoded(maxRealBw + 1) + require.Error(t, err) + }) +}