From 8a368830399aec57f9c4ef3fcf11b7cbbce29cb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Fogh?= Date: Fri, 2 May 2025 14:26:41 +0000 Subject: [PATCH] Support for 64bit rate limits. --- class_linux.go | 18 ++- filter.go | 4 +- filter_linux.go | 366 ++++++++++++++++++++++++++---------------------- filter_test.go | 299 +++++++++++++++++++++------------------ nl/tc_linux.go | 7 +- qdisc_linux.go | 10 +- 6 files changed, 386 insertions(+), 318 deletions(-) diff --git a/class_linux.go b/class_linux.go index 08fb16c2..b62b1ee1 100644 --- a/class_linux.go +++ b/class_linux.go @@ -163,13 +163,13 @@ func classPayload(req *nl.NetlinkRequest, class Class) error { mtu := 1600 var rtab [256]uint32 var ctab [256]uint32 - tcrate := nl.TcRateSpec{Rate: uint32(htb.Rate)} - if CalcRtable(&tcrate, rtab[:], cellLog, uint32(mtu), linklayer) < 0 { + tcrate := nl.TcRateSpec{} + if CalcRtable(htb.Rate, &tcrate, rtab[:], cellLog, uint32(mtu), linklayer) < 0 { return errors.New("HTB: failed to calculate rate table") } opt.Rate = tcrate - tcceil := nl.TcRateSpec{Rate: uint32(htb.Ceil)} - if CalcRtable(&tcceil, ctab[:], ccellLog, uint32(mtu), linklayer) < 0 { + tcceil := nl.TcRateSpec{} + if CalcRtable(htb.Ceil, &tcceil, ctab[:], ccellLog, uint32(mtu), linklayer) < 0 { return errors.New("HTB: failed to calculate ceil rate table") } opt.Ceil = tcceil @@ -311,8 +311,14 @@ func parseHtbClassData(class Class, data []syscall.NetlinkRouteAttr) (bool, erro switch datum.Attr.Type { case nl.TCA_HTB_PARMS: opt := nl.DeserializeTcHtbCopt(datum.Value) - htb.Rate = uint64(opt.Rate.Rate) - htb.Ceil = uint64(opt.Ceil.Rate) + // htb.Rate may already have been set via nl.TCA_HTB_RATE64. + if htb.Rate == 0 { + htb.Rate = uint64(opt.Rate.Rate) + } + // htb.Ceil may already have been set via nl.TCA_HTB_CEIL64. + if htb.Ceil == 0 { + htb.Ceil = uint64(opt.Ceil.Rate) + } htb.Buffer = opt.Buffer htb.Cbuffer = opt.Cbuffer htb.Quantum = opt.Quantum diff --git a/filter.go b/filter.go index fbb3b6a5..3e2131ac 100644 --- a/filter.go +++ b/filter.go @@ -366,12 +366,12 @@ func NewSkbEditAction() *SkbEditAction { type PoliceAction struct { ActionAttrs - Rate uint32 // in byte per second + Rate uint64 // in byte per second Burst uint32 // in byte RCellLog int Mtu uint32 Mpu uint16 // in byte - PeakRate uint32 // in byte per second + PeakRate uint64 // in byte per second PCellLog int AvRate uint32 // in byte per second Overhead uint16 diff --git a/filter_linux.go b/filter_linux.go index 231b5734..a27aad00 100644 --- a/filter_linux.go +++ b/filter_linux.go @@ -6,6 +6,7 @@ import ( "encoding/hex" "errors" "fmt" + "math" "net" "syscall" @@ -576,24 +577,22 @@ func encodePolice(attr *nl.RtAttr, action *PoliceAction) error { police.Bindcnt = int32(action.Attrs().Bindcnt) police.Capab = uint32(action.Attrs().Capab) police.Refcnt = int32(action.Attrs().Refcnt) - police.Rate.Rate = action.Rate - police.PeakRate.Rate = action.PeakRate police.Action = int32(action.ExceedAction) - if police.Rate.Rate != 0 { + if action.Rate != 0 { police.Rate.Mpu = action.Mpu police.Rate.Overhead = action.Overhead - if CalcRtable(&police.Rate, rtab[:], action.RCellLog, action.Mtu, action.LinkLayer) < 0 { + if CalcRtable(action.Rate, &police.Rate, rtab[:], action.RCellLog, action.Mtu, action.LinkLayer) < 0 { return errors.New("TBF: failed to calculate rate table") } - police.Burst = Xmittime(uint64(police.Rate.Rate), action.Burst) + police.Burst = Xmittime(action.Rate, action.Burst) } police.Mtu = action.Mtu - if police.PeakRate.Rate != 0 { + if action.PeakRate != 0 { police.PeakRate.Mpu = action.Mpu police.PeakRate.Overhead = action.Overhead - if CalcRtable(&police.PeakRate, ptab[:], action.PCellLog, action.Mtu, action.LinkLayer) < 0 { + if CalcRtable(action.PeakRate, &police.PeakRate, ptab[:], action.PCellLog, action.Mtu, action.LinkLayer) < 0 { return errors.New("POLICE: failed to calculate peak rate table") } } @@ -601,9 +600,15 @@ func encodePolice(attr *nl.RtAttr, action *PoliceAction) error { attr.AddRtAttr(nl.TCA_POLICE_TBF, police.Serialize()) if police.Rate.Rate != 0 { attr.AddRtAttr(nl.TCA_POLICE_RATE, SerializeRtab(rtab)) + if action.Rate > math.MaxUint32 { + attr.AddRtAttr(nl.TCA_POLICE_RATE64, nl.Uint64Attr(action.Rate)) + } } if police.PeakRate.Rate != 0 { attr.AddRtAttr(nl.TCA_POLICE_PEAKRATE, SerializeRtab(ptab)) + if action.PeakRate > math.MaxUint32 { + attr.AddRtAttr(nl.TCA_POLICE_PEAKRATE64, nl.Uint64Attr(action.PeakRate)) + } } if action.AvRate != 0 { attr.AddRtAttr(nl.TCA_POLICE_AVRATE, nl.Uint32Attr(action.AvRate)) @@ -788,27 +793,45 @@ func EncodeActions(attr *nl.RtAttr, actions []Action) error { return nil } -func parsePolice(data syscall.NetlinkRouteAttr, police *PoliceAction) { - switch data.Attr.Type { - case nl.TCA_POLICE_RESULT: - police.NotExceedAction = TcPolAct(native.Uint32(data.Value[0:4])) - case nl.TCA_POLICE_AVRATE: - police.AvRate = native.Uint32(data.Value[0:4]) - case nl.TCA_POLICE_TBF: - p := *nl.DeserializeTcPolice(data.Value) - police.ActionAttrs = ActionAttrs{} - police.Attrs().Index = int(p.Index) - police.Attrs().Bindcnt = int(p.Bindcnt) - police.Attrs().Capab = int(p.Capab) - police.Attrs().Refcnt = int(p.Refcnt) - police.ExceedAction = TcPolAct(p.Action) - police.Rate = p.Rate.Rate - police.PeakRate = p.PeakRate.Rate - police.Burst = Xmitsize(uint64(p.Rate.Rate), p.Burst) - police.Mtu = p.Mtu - police.LinkLayer = int(p.Rate.Linklayer) & nl.TC_LINKLAYER_MASK - police.Overhead = p.Rate.Overhead +func parsePolice(indata []syscall.NetlinkRouteAttr) *PoliceAction { + var police PoliceAction + var burstTicks uint32 + for _, data := range indata { + switch data.Attr.Type { + case nl.TCA_POLICE_RESULT: + police.NotExceedAction = TcPolAct(native.Uint32(data.Value[0:4])) + case nl.TCA_POLICE_AVRATE: + police.AvRate = native.Uint32(data.Value[0:4]) + case nl.TCA_POLICE_TBF: + p := *nl.DeserializeTcPolice(data.Value) + police.ActionAttrs = ActionAttrs{} + police.Attrs().Index = int(p.Index) + police.Attrs().Bindcnt = int(p.Bindcnt) + police.Attrs().Capab = int(p.Capab) + police.Attrs().Refcnt = int(p.Refcnt) + police.ExceedAction = TcPolAct(p.Action) + // police.Rate may already have been set via nl.TCA_POLICE_RATE64. + if police.Rate == 0 { + police.Rate = uint64(p.Rate.Rate) + } + // police.PeakRate may already have been set via nl.TCA_POLICE_PEAKRATE64. + if police.PeakRate == 0 { + police.PeakRate = uint64(p.PeakRate.Rate) + } + burstTicks = p.Burst + police.Mtu = p.Mtu + police.LinkLayer = int(p.Rate.Linklayer) & nl.TC_LINKLAYER_MASK + police.Overhead = p.Rate.Overhead + case nl.TCA_POLICE_RATE64: + police.Rate = native.Uint64(data.Value[0:8]) + case nl.TCA_POLICE_PEAKRATE64: + police.PeakRate = native.Uint64(data.Value[0:8]) + } } + // Outside the loop, since we must use the final values of police.Rate + // and burstTicks. + police.Burst = Xmitsize(police.Rate, burstTicks) + return &police } func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) { @@ -859,136 +882,138 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) { if err != nil { return nil, err } - for _, adatum := range adata { - switch actionType { - case "mirred": - switch adatum.Attr.Type { - case nl.TCA_MIRRED_PARMS: - mirred := *nl.DeserializeTcMirred(adatum.Value) - action.(*MirredAction).ActionAttrs = ActionAttrs{} - toAttrs(&mirred.TcGen, action.Attrs()) - action.(*MirredAction).Ifindex = int(mirred.Ifindex) - action.(*MirredAction).MirredAction = MirredAct(mirred.Eaction) - case nl.TCA_MIRRED_TM: - tcTs := nl.DeserializeTcf(adatum.Value) - actionTimestamp = toTimeStamp(tcTs) - } - case "vlan": - switch adatum.Attr.Type { - case nl.TCA_VLAN_PARMS: - vlan := *nl.DeserializeTcVlan(adatum.Value) - action.(*VlanAction).ActionAttrs = ActionAttrs{} - toAttrs(&vlan.TcGen, action.Attrs()) - action.(*VlanAction).Action = VlanAct(vlan.Action) - case nl.TCA_VLAN_PUSH_VLAN_ID: - vlanId := native.Uint16(adatum.Value[0:2]) - action.(*VlanAction).VlanID = vlanId - } - case "tunnel_key": - switch adatum.Attr.Type { - case nl.TCA_TUNNEL_KEY_PARMS: - tun := *nl.DeserializeTunnelKey(adatum.Value) - action.(*TunnelKeyAction).ActionAttrs = ActionAttrs{} - toAttrs(&tun.TcGen, action.Attrs()) - action.(*TunnelKeyAction).Action = TunnelKeyAct(tun.Action) - case nl.TCA_TUNNEL_KEY_ENC_KEY_ID: - action.(*TunnelKeyAction).KeyID = networkOrder.Uint32(adatum.Value[0:4]) - case nl.TCA_TUNNEL_KEY_ENC_IPV6_SRC, nl.TCA_TUNNEL_KEY_ENC_IPV4_SRC: - action.(*TunnelKeyAction).SrcAddr = adatum.Value[:] - case nl.TCA_TUNNEL_KEY_ENC_IPV6_DST, nl.TCA_TUNNEL_KEY_ENC_IPV4_DST: - action.(*TunnelKeyAction).DstAddr = adatum.Value[:] - case nl.TCA_TUNNEL_KEY_ENC_DST_PORT: - action.(*TunnelKeyAction).DestPort = ntohs(adatum.Value) - case nl.TCA_TUNNEL_KEY_TM: - tcTs := nl.DeserializeTcf(adatum.Value) - actionTimestamp = toTimeStamp(tcTs) - } - case "skbedit": - switch adatum.Attr.Type { - case nl.TCA_SKBEDIT_PARMS: - skbedit := *nl.DeserializeSkbEdit(adatum.Value) - action.(*SkbEditAction).ActionAttrs = ActionAttrs{} - toAttrs(&skbedit.TcGen, action.Attrs()) - case nl.TCA_SKBEDIT_MARK: - mark := native.Uint32(adatum.Value[0:4]) - action.(*SkbEditAction).Mark = &mark - case nl.TCA_SKBEDIT_MASK: - mask := native.Uint32(adatum.Value[0:4]) - action.(*SkbEditAction).Mask = &mask - case nl.TCA_SKBEDIT_PRIORITY: - priority := native.Uint32(adatum.Value[0:4]) - action.(*SkbEditAction).Priority = &priority - case nl.TCA_SKBEDIT_PTYPE: - ptype := native.Uint16(adatum.Value[0:2]) - action.(*SkbEditAction).PType = &ptype - case nl.TCA_SKBEDIT_QUEUE_MAPPING: - mapping := native.Uint16(adatum.Value[0:2]) - action.(*SkbEditAction).QueueMapping = &mapping - case nl.TCA_SKBEDIT_TM: - tcTs := nl.DeserializeTcf(adatum.Value) - actionTimestamp = toTimeStamp(tcTs) - } - case "bpf": - switch adatum.Attr.Type { - case nl.TCA_ACT_BPF_PARMS: - gen := *nl.DeserializeTcGen(adatum.Value) - toAttrs(&gen, action.Attrs()) - case nl.TCA_ACT_BPF_FD: - action.(*BpfAction).Fd = int(native.Uint32(adatum.Value[0:4])) - case nl.TCA_ACT_BPF_NAME: - action.(*BpfAction).Name = string(adatum.Value[:len(adatum.Value)-1]) - case nl.TCA_ACT_BPF_TM: - tcTs := nl.DeserializeTcf(adatum.Value) - actionTimestamp = toTimeStamp(tcTs) - } - case "connmark": - switch adatum.Attr.Type { - case nl.TCA_CONNMARK_PARMS: - connmark := *nl.DeserializeTcConnmark(adatum.Value) - action.(*ConnmarkAction).ActionAttrs = ActionAttrs{} - toAttrs(&connmark.TcGen, action.Attrs()) - action.(*ConnmarkAction).Zone = connmark.Zone - case nl.TCA_CONNMARK_TM: - tcTs := nl.DeserializeTcf(adatum.Value) - actionTimestamp = toTimeStamp(tcTs) - } - case "csum": - switch adatum.Attr.Type { - case nl.TCA_CSUM_PARMS: - csum := *nl.DeserializeTcCsum(adatum.Value) - action.(*CsumAction).ActionAttrs = ActionAttrs{} - toAttrs(&csum.TcGen, action.Attrs()) - action.(*CsumAction).UpdateFlags = CsumUpdateFlags(csum.UpdateFlags) - case nl.TCA_CSUM_TM: - tcTs := nl.DeserializeTcf(adatum.Value) - actionTimestamp = toTimeStamp(tcTs) - } - case "sample": - switch adatum.Attr.Type { - case nl.TCA_ACT_SAMPLE_PARMS: - gen := *nl.DeserializeTcGen(adatum.Value) - toAttrs(&gen, action.Attrs()) - case nl.TCA_ACT_SAMPLE_RATE: - action.(*SampleAction).Rate = native.Uint32(adatum.Value[0:4]) - case nl.TCA_ACT_SAMPLE_PSAMPLE_GROUP: - action.(*SampleAction).Group = native.Uint32(adatum.Value[0:4]) - case nl.TCA_ACT_SAMPLE_TRUNC_SIZE: - action.(*SampleAction).TruncSize = native.Uint32(adatum.Value[0:4]) - } - case "gact": - switch adatum.Attr.Type { - case nl.TCA_GACT_PARMS: - gen := *nl.DeserializeTcGen(adatum.Value) - toAttrs(&gen, action.Attrs()) - if action.Attrs().Action.String() == "goto" { - action.(*GenericAction).Chain = TC_ACT_EXT_VAL_MASK & gen.Action + if actionType == "police" { + action = parsePolice(adata) + } else { + for _, adatum := range adata { + switch actionType { + case "mirred": + switch adatum.Attr.Type { + case nl.TCA_MIRRED_PARMS: + mirred := *nl.DeserializeTcMirred(adatum.Value) + action.(*MirredAction).ActionAttrs = ActionAttrs{} + toAttrs(&mirred.TcGen, action.Attrs()) + action.(*MirredAction).Ifindex = int(mirred.Ifindex) + action.(*MirredAction).MirredAction = MirredAct(mirred.Eaction) + case nl.TCA_MIRRED_TM: + tcTs := nl.DeserializeTcf(adatum.Value) + actionTimestamp = toTimeStamp(tcTs) + } + case "vlan": + switch adatum.Attr.Type { + case nl.TCA_VLAN_PARMS: + vlan := *nl.DeserializeTcVlan(adatum.Value) + action.(*VlanAction).ActionAttrs = ActionAttrs{} + toAttrs(&vlan.TcGen, action.Attrs()) + action.(*VlanAction).Action = VlanAct(vlan.Action) + case nl.TCA_VLAN_PUSH_VLAN_ID: + vlanId := native.Uint16(adatum.Value[0:2]) + action.(*VlanAction).VlanID = vlanId + } + case "tunnel_key": + switch adatum.Attr.Type { + case nl.TCA_TUNNEL_KEY_PARMS: + tun := *nl.DeserializeTunnelKey(adatum.Value) + action.(*TunnelKeyAction).ActionAttrs = ActionAttrs{} + toAttrs(&tun.TcGen, action.Attrs()) + action.(*TunnelKeyAction).Action = TunnelKeyAct(tun.Action) + case nl.TCA_TUNNEL_KEY_ENC_KEY_ID: + action.(*TunnelKeyAction).KeyID = networkOrder.Uint32(adatum.Value[0:4]) + case nl.TCA_TUNNEL_KEY_ENC_IPV6_SRC, nl.TCA_TUNNEL_KEY_ENC_IPV4_SRC: + action.(*TunnelKeyAction).SrcAddr = adatum.Value[:] + case nl.TCA_TUNNEL_KEY_ENC_IPV6_DST, nl.TCA_TUNNEL_KEY_ENC_IPV4_DST: + action.(*TunnelKeyAction).DstAddr = adatum.Value[:] + case nl.TCA_TUNNEL_KEY_ENC_DST_PORT: + action.(*TunnelKeyAction).DestPort = ntohs(adatum.Value) + case nl.TCA_TUNNEL_KEY_TM: + tcTs := nl.DeserializeTcf(adatum.Value) + actionTimestamp = toTimeStamp(tcTs) + } + case "skbedit": + switch adatum.Attr.Type { + case nl.TCA_SKBEDIT_PARMS: + skbedit := *nl.DeserializeSkbEdit(adatum.Value) + action.(*SkbEditAction).ActionAttrs = ActionAttrs{} + toAttrs(&skbedit.TcGen, action.Attrs()) + case nl.TCA_SKBEDIT_MARK: + mark := native.Uint32(adatum.Value[0:4]) + action.(*SkbEditAction).Mark = &mark + case nl.TCA_SKBEDIT_MASK: + mask := native.Uint32(adatum.Value[0:4]) + action.(*SkbEditAction).Mask = &mask + case nl.TCA_SKBEDIT_PRIORITY: + priority := native.Uint32(adatum.Value[0:4]) + action.(*SkbEditAction).Priority = &priority + case nl.TCA_SKBEDIT_PTYPE: + ptype := native.Uint16(adatum.Value[0:2]) + action.(*SkbEditAction).PType = &ptype + case nl.TCA_SKBEDIT_QUEUE_MAPPING: + mapping := native.Uint16(adatum.Value[0:2]) + action.(*SkbEditAction).QueueMapping = &mapping + case nl.TCA_SKBEDIT_TM: + tcTs := nl.DeserializeTcf(adatum.Value) + actionTimestamp = toTimeStamp(tcTs) + } + case "bpf": + switch adatum.Attr.Type { + case nl.TCA_ACT_BPF_PARMS: + gen := *nl.DeserializeTcGen(adatum.Value) + toAttrs(&gen, action.Attrs()) + case nl.TCA_ACT_BPF_FD: + action.(*BpfAction).Fd = int(native.Uint32(adatum.Value[0:4])) + case nl.TCA_ACT_BPF_NAME: + action.(*BpfAction).Name = string(adatum.Value[:len(adatum.Value)-1]) + case nl.TCA_ACT_BPF_TM: + tcTs := nl.DeserializeTcf(adatum.Value) + actionTimestamp = toTimeStamp(tcTs) + } + case "connmark": + switch adatum.Attr.Type { + case nl.TCA_CONNMARK_PARMS: + connmark := *nl.DeserializeTcConnmark(adatum.Value) + action.(*ConnmarkAction).ActionAttrs = ActionAttrs{} + toAttrs(&connmark.TcGen, action.Attrs()) + action.(*ConnmarkAction).Zone = connmark.Zone + case nl.TCA_CONNMARK_TM: + tcTs := nl.DeserializeTcf(adatum.Value) + actionTimestamp = toTimeStamp(tcTs) + } + case "csum": + switch adatum.Attr.Type { + case nl.TCA_CSUM_PARMS: + csum := *nl.DeserializeTcCsum(adatum.Value) + action.(*CsumAction).ActionAttrs = ActionAttrs{} + toAttrs(&csum.TcGen, action.Attrs()) + action.(*CsumAction).UpdateFlags = CsumUpdateFlags(csum.UpdateFlags) + case nl.TCA_CSUM_TM: + tcTs := nl.DeserializeTcf(adatum.Value) + actionTimestamp = toTimeStamp(tcTs) + } + case "sample": + switch adatum.Attr.Type { + case nl.TCA_ACT_SAMPLE_PARMS: + gen := *nl.DeserializeTcGen(adatum.Value) + toAttrs(&gen, action.Attrs()) + case nl.TCA_ACT_SAMPLE_RATE: + action.(*SampleAction).Rate = native.Uint32(adatum.Value[0:4]) + case nl.TCA_ACT_SAMPLE_PSAMPLE_GROUP: + action.(*SampleAction).Group = native.Uint32(adatum.Value[0:4]) + case nl.TCA_ACT_SAMPLE_TRUNC_SIZE: + action.(*SampleAction).TruncSize = native.Uint32(adatum.Value[0:4]) + } + case "gact": + switch adatum.Attr.Type { + case nl.TCA_GACT_PARMS: + gen := *nl.DeserializeTcGen(adatum.Value) + toAttrs(&gen, action.Attrs()) + if action.Attrs().Action.String() == "goto" { + action.(*GenericAction).Chain = TC_ACT_EXT_VAL_MASK & gen.Action + } + case nl.TCA_GACT_TM: + tcTs := nl.DeserializeTcf(adatum.Value) + actionTimestamp = toTimeStamp(tcTs) } - case nl.TCA_GACT_TM: - tcTs := nl.DeserializeTcf(adatum.Value) - actionTimestamp = toTimeStamp(tcTs) } - case "police": - parsePolice(adatum, action.(*PoliceAction)) } } case nl.TCA_ACT_STATS: @@ -1041,12 +1066,11 @@ func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) } } case nl.TCA_U32_POLICE: - var police PoliceAction - adata, _ := nl.ParseRouteAttr(datum.Value) - for _, aattr := range adata { - parsePolice(aattr, &police) + adata, err := nl.ParseRouteAttr(datum.Value) + if err != nil { + return detailed, err } - u32.Police = &police + u32.Police = parsePolice(adata) case nl.TCA_U32_CLASSID: u32.ClassId = native.Uint32(datum.Value) case nl.TCA_U32_DIVISOR: @@ -1072,12 +1096,11 @@ func parseFwData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) { case nl.TCA_FW_INDEV: fw.InDev = string(datum.Value[:len(datum.Value)-1]) case nl.TCA_FW_POLICE: - var police PoliceAction - adata, _ := nl.ParseRouteAttr(datum.Value) - for _, aattr := range adata { - parsePolice(aattr, &police) + adata, err := nl.ParseRouteAttr(datum.Value) + if err != nil { + return detailed, err } - fw.Police = &police + fw.Police = parsePolice(adata) case nl.TCA_FW_ACT: tables, err := nl.ParseRouteAttr(datum.Value) if err != nil { @@ -1164,10 +1187,8 @@ func AdjustSize(sz uint, mpu uint, linklayer int) uint { } } -func CalcRtable(rate *nl.TcRateSpec, rtab []uint32, cellLog int, mtu uint32, linklayer int) int { - bps := rate.Rate +func CalcRtable(bps uint64, rate *nl.TcRateSpec, rtab []uint32, cellLog int, mtu uint32, linklayer int) int { mpu := rate.Mpu - var sz uint if mtu == 0 { mtu = 2047 } @@ -1178,8 +1199,13 @@ func CalcRtable(rate *nl.TcRateSpec, rtab []uint32, cellLog int, mtu uint32, lin } } for i := 0; i < 256; i++ { - sz = AdjustSize(uint((i+1)< math.MaxUint32 { + rate.Rate = math.MaxUint32 + } else { + rate.Rate = uint32(bps) } rate.CellAlign = -1 rate.CellLog = uint8(cellLog) diff --git a/filter_test.go b/filter_test.go index aab00d6f..4cb821da 100644 --- a/filter_test.go +++ b/filter_test.go @@ -2275,113 +2275,125 @@ func TestFilterU32PoliceAddDel(t *testing.T) { t.Fatal("Qdisc is the wrong type") } - const ( - policeRate = 0x40000000 // 1 Gbps - policeBurst = 0x19000 // 100 KB - policePeakRate = 0x4000 // 16 Kbps - ) - - police := NewPoliceAction() - police.Rate = policeRate - police.PeakRate = policePeakRate - police.Burst = policeBurst - police.ExceedAction = TC_POLICE_SHOT - police.NotExceedAction = TC_POLICE_UNSPEC - - classId := MakeHandle(1, 1) - filter := &U32{ - FilterAttrs: FilterAttrs{ - LinkIndex: link.Attrs().Index, - Parent: MakeHandle(0xffff, 0), - Priority: 1, - Protocol: unix.ETH_P_ALL, - }, - ClassId: classId, - Actions: []Action{ - police, - &MirredAction{ - ActionAttrs: ActionAttrs{ - Action: TC_ACT_STOLEN, + tests := []struct { + rate uint64 + peakRate uint64 + burst uint32 + }{ + // Fits in a uint32 + {0x400000, 0x20000, 0x4000}, + // Doesn't fit in a uint32 + {0x80000000000, 0x80000000000, 0x20000}} + + for _, tt := range tests { + + police := NewPoliceAction() + police.Rate = tt.rate + police.PeakRate = tt.peakRate + police.Burst = tt.burst + police.ExceedAction = TC_POLICE_SHOT + police.NotExceedAction = TC_POLICE_UNSPEC + + classId := MakeHandle(1, 1) + filter := &U32{ + FilterAttrs: FilterAttrs{ + LinkIndex: link.Attrs().Index, + Parent: MakeHandle(0xffff, 0), + Priority: 1, + Protocol: unix.ETH_P_ALL, + }, + ClassId: classId, + Actions: []Action{ + police, + &MirredAction{ + ActionAttrs: ActionAttrs{ + Action: TC_ACT_STOLEN, + }, + MirredAction: TCA_EGRESS_REDIR, + Ifindex: redir.Attrs().Index, }, - MirredAction: TCA_EGRESS_REDIR, - Ifindex: redir.Attrs().Index, }, - }, - } + } - if err := FilterAdd(filter); err != nil { - t.Fatal(err) - } + if err := FilterAdd(filter); err != nil { + t.Fatal(err) + } - filters, err := FilterList(link, MakeHandle(0xffff, 0)) - if err != nil { - t.Fatal(err) - } - if len(filters) != 1 { - t.Fatal("Failed to add filter") - } - u32, ok := filters[0].(*U32) - if !ok { - t.Fatal("Filter is the wrong type") - } + filters, err := FilterList(link, MakeHandle(0xffff, 0)) + if err != nil { + t.Fatal(err) + } + if len(filters) != 1 { + t.Fatal("Failed to add filter") + } + u32, ok := filters[0].(*U32) + if !ok { + t.Fatal("Filter is the wrong type") + } - if len(u32.Actions) != 2 { - t.Fatalf("Too few Actions in filter") - } - if u32.ClassId != classId { - t.Fatalf("ClassId of the filter is the wrong value") - } + if len(u32.Actions) != 2 { + t.Fatalf("Too few Actions in filter") + } + if u32.ClassId != classId { + t.Fatalf("ClassId of the filter is the wrong value") + } - // actions can be returned in reverse order - p, ok := u32.Actions[0].(*PoliceAction) - if !ok { - p, ok = u32.Actions[1].(*PoliceAction) + // actions can be returned in reverse order + p, ok := u32.Actions[0].(*PoliceAction) if !ok { - t.Fatal("Unable to find police action") + p, ok = u32.Actions[1].(*PoliceAction) + if !ok { + t.Fatal("Unable to find police action") + } } - } - if p.ExceedAction != TC_POLICE_SHOT { - t.Fatal("Police ExceedAction isn't TC_POLICE_SHOT") - } + if p.ExceedAction != TC_POLICE_SHOT { + t.Fatal("Police ExceedAction isn't TC_POLICE_SHOT") + } - if p.NotExceedAction != TC_POLICE_UNSPEC { - t.Fatal("Police NotExceedAction isn't TC_POLICE_UNSPEC") - } + if p.NotExceedAction != TC_POLICE_UNSPEC { + t.Fatal("Police NotExceedAction isn't TC_POLICE_UNSPEC") + } - if p.Rate != policeRate { - t.Fatal("Action Rate doesn't match") - } + if p.Rate != tt.rate { + t.Fatal("Action Rate doesn't match") + } - if p.PeakRate != policePeakRate { - t.Fatal("Action PeakRate doesn't match") - } + if p.PeakRate != tt.peakRate { + t.Fatal("Action PeakRate doesn't match") + } - if p.LinkLayer != nl.LINKLAYER_ETHERNET { - t.Fatal("Action LinkLayer doesn't match") - } + // Burst doesn't match exactly because of rounding errors. + if frac, eps := float64(p.Burst)/float64(tt.burst), 1e-5; frac < 1-eps && frac > 1+eps { + t.Fatal("Filter Burst doesn't match") + } - mia, ok := u32.Actions[0].(*MirredAction) - if !ok { - mia, ok = u32.Actions[1].(*MirredAction) + if p.LinkLayer != nl.LINKLAYER_ETHERNET { + t.Fatal("Action LinkLayer doesn't match") + } + + mia, ok := u32.Actions[0].(*MirredAction) if !ok { - t.Fatal("Unable to find mirred action") + mia, ok = u32.Actions[1].(*MirredAction) + if !ok { + t.Fatal("Unable to find mirred action") + } } - } - if mia.Attrs().Action != TC_ACT_STOLEN { - t.Fatal("Mirred action isn't TC_ACT_STOLEN") - } + if mia.Attrs().Action != TC_ACT_STOLEN { + t.Fatal("Mirred action isn't TC_ACT_STOLEN") + } - if err := FilterDel(filter); err != nil { - t.Fatal(err) - } - filters, err = FilterList(link, MakeHandle(0xffff, 0)) - if err != nil { - t.Fatal(err) - } - if len(filters) != 0 { - t.Fatal("Failed to remove filter") + if err := FilterDel(filter); err != nil { + t.Fatal(err) + } + filters, err = FilterList(link, MakeHandle(0xffff, 0)) + if err != nil { + t.Fatal(err) + } + if len(filters) != 0 { + t.Fatal("Failed to remove filter") + } } if err := QdiscDel(qdisc); err != nil { @@ -2429,61 +2441,74 @@ func TestFilterU32DirectPoliceAddDel(t *testing.T) { t.Fatal(err) } - const ( - policeRate = 0x40000000 // 1 Gbps - policeBurst = 0x19000 // 100 KB - policePeakRate = 0x4000 // 16 Kbps - ) - - police := NewPoliceAction() - police.Rate = policeRate - police.PeakRate = policePeakRate - police.Burst = policeBurst - police.ExceedAction = TC_POLICE_SHOT - police.NotExceedAction = TC_POLICE_UNSPEC + tests := []struct { + rate uint64 + peakRate uint64 + burst uint32 + }{ + // Fits in a uint32 + {0x400000, 0x20000, 0x4000}, + // Doesn't fit in a uint32 + {0x80000000000, 0x80000000000, 0x8000}} + + for _, tt := range tests { + + police := NewPoliceAction() + police.Rate = tt.rate + police.PeakRate = tt.peakRate + police.Burst = tt.burst + police.ExceedAction = TC_POLICE_SHOT + police.NotExceedAction = TC_POLICE_UNSPEC + + classId := MakeHandle(1, 1) + filter := &U32{ + FilterAttrs: FilterAttrs{ + LinkIndex: link.Attrs().Index, + Parent: MakeHandle(0xffff, 0), + Handle: 0x80000600, + Priority: 1, + Protocol: unix.ETH_P_ALL, + }, + ClassId: classId, + Police: police, + } - classId := MakeHandle(1, 1) - filter := &U32{ - FilterAttrs: FilterAttrs{ - LinkIndex: link.Attrs().Index, - Parent: MakeHandle(0xffff, 0), - Priority: 1, - Protocol: unix.ETH_P_ALL, - }, - ClassId: classId, - Police: police, - } + if err := FilterReplace(filter); err != nil { + t.Fatal(err) + } - if err := FilterAdd(filter); err != nil { - t.Fatal(err) - } + filters, err := FilterList(link, MakeHandle(0xffff, 0)) + if err != nil { + t.Fatal(err) + } + if len(filters) != 1 { + t.Fatal("Failed to add filter") + } + u32, ok := filters[0].(*U32) + if !ok { + t.Fatal("Filter is the wrong type") + } - filters, err := FilterList(link, MakeHandle(0xffff, 0)) - if err != nil { - t.Fatal(err) - } - if len(filters) != 1 { - t.Fatal("Failed to add filter") - } - u32, ok := filters[0].(*U32) - if !ok { - t.Fatal("Filter is the wrong type") - } + if u32.Police == nil { + t.Fatalf("No police in filter") + } - if u32.Police == nil { - t.Fatalf("No police in filter") - } + if u32.Police.Rate != tt.rate { + t.Fatal("Filter Rate doesn't match") + } - if u32.Police.Rate != policeRate { - t.Fatal("Filter Rate doesn't match") - } + if u32.Police.PeakRate != tt.peakRate { + t.Fatal("Filter PeakRate doesn't match") + } - if u32.Police.PeakRate != policePeakRate { - t.Fatal("Filter PeakRate doesn't match") - } + // Burst doesn't match exactly because of rounding errors. + if frac, eps := float64(u32.Police.Burst)/float64(tt.burst), 1e-5; frac < 1-eps && frac > 1+eps { + t.Fatal("Filter Burst doesn't match") + } - if u32.Police.LinkLayer != nl.LINKLAYER_ETHERNET { - t.Fatal("Filter LinkLayer doesn't match") + if u32.Police.LinkLayer != nl.LINKLAYER_ETHERNET { + t.Fatal("Filter LinkLayer doesn't match") + } } if err := QdiscDel(qdisc); err != nil { diff --git a/nl/tc_linux.go b/nl/tc_linux.go index 50f4a6d0..b402d1b3 100644 --- a/nl/tc_linux.go +++ b/nl/tc_linux.go @@ -33,7 +33,12 @@ const ( TCA_POLICE_PEAKRATE TCA_POLICE_AVRATE TCA_POLICE_RESULT - TCA_POLICE_MAX = TCA_POLICE_RESULT + TCA_POLICE_TM + TCA_POLICE_PAD + TCA_POLICE_RATE64 + TCA_POLICE_PEAKRATE64 + TCA_POLICE_PKTRATE64 + TCA_POLICE_PKTBURST64 ) // Message types diff --git a/qdisc_linux.go b/qdisc_linux.go index 22cf0e58..c1ac1541 100644 --- a/qdisc_linux.go +++ b/qdisc_linux.go @@ -664,8 +664,14 @@ func parseTbfData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error { switch datum.Attr.Type { case nl.TCA_TBF_PARMS: opt := nl.DeserializeTcTbfQopt(datum.Value) - tbf.Rate = uint64(opt.Rate.Rate) - tbf.Peakrate = uint64(opt.Peakrate.Rate) + // tbf.Rate may already have been set via nl.TCA_TBF_RATE64. + if tbf.Rate == 0 { + tbf.Rate = uint64(opt.Rate.Rate) + } + // tbf.Peakrate may already have been set via nl.TCA_TBF_PRATE64. + if tbf.Peakrate == 0 { + tbf.Peakrate = uint64(opt.Peakrate.Rate) + } tbf.Limit = opt.Limit tbf.Buffer = opt.Buffer case nl.TCA_TBF_RATE64: