Files
netlink/filter_linux.go
peterk-akamai 7adcc3735c Fix bug in modify U32 filter, correct the number of Keys in Sel
When `func (h *Handle) filterModify(...)` handles an `U32` filter, it also corrects the endiannes for the `Mask` and `Val` in the filter's `Sel.Keys`. For this it creates a new Keys slice and copies the values from the old one. This new slice is created with an incorrect size, likely the intention was to specify its capacity, but instead the size is specified.

The old code happens to work correctly in practice when the number of keys is a power of 2. Otherwise empty (match all) keys are added to the end to make the number a power of 2.

This commit fixes the issue. It was well tested, here's an excerpt:

- Create a U32 filter with 5 Keys. The content of keys is irrelevant, only the number matters.
- Print the filter back with `tc filter show ...`.

The old behaviour:

```
filter parent ffff: protocol all pref 49150 u32 chain 0 fh 800::601 order 1537 key ht 800 bkt 0 *flowid :1 not_in_hw
  match 40000000/60000000 at 0
  match 07010723/ffffffff at 24
  match 07450767/ffffffff at 28
  match 07890733/ffffffff at 32
  match 07420801/ffe00000 at 36
  match 00000000/00000000 at 0
  match 00000000/00000000 at 0
  match 00000000/00000000 at 0
```
The last 3 entries were added by netlink.

New behaviour:
```
filter parent ffff: protocol all pref 49150 u32 chain 0 fh 800::801 order 2049 key ht 800 bkt 0 flowid :1 not_in_hw
  match 60000000/f0000000 at 0
  match 07010723/ffffffff at 24
  match 07450767/ffffffff at 28
  match 07890733/ffffffff at 32
  match 07400000/ffe00000 at 36
```
2025-06-20 09:58:53 -07:00

1230 lines
37 KiB
Go

package netlink
import (
"bytes"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"net"
"syscall"
"github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
)
// Constants used in TcU32Sel.Flags.
const (
TC_U32_TERMINAL = nl.TC_U32_TERMINAL
TC_U32_OFFSET = nl.TC_U32_OFFSET
TC_U32_VAROFFSET = nl.TC_U32_VAROFFSET
TC_U32_EAT = nl.TC_U32_EAT
)
// Sel of the U32 filters that contains multiple TcU32Key. This is the type
// alias and the frontend representation of nl.TcU32Sel. It is serialized into
// canonical nl.TcU32Sel with the appropriate endianness.
type TcU32Sel = nl.TcU32Sel
// TcU32Key contained of Sel in the U32 filters. This is the type alias and the
// frontend representation of nl.TcU32Key. It is serialized into chanonical
// nl.TcU32Sel with the appropriate endianness.
type TcU32Key = nl.TcU32Key
// U32 filters on many packet related properties
type U32 struct {
FilterAttrs
ClassId uint32
Divisor uint32 // Divisor MUST be power of 2.
Hash uint32
Link uint32
RedirIndex int
Sel *TcU32Sel
Actions []Action
Police *PoliceAction
}
func (filter *U32) Attrs() *FilterAttrs {
return &filter.FilterAttrs
}
func (filter *U32) Type() string {
return "u32"
}
type Flower struct {
FilterAttrs
ClassId uint32
DestIP net.IP
DestIPMask net.IPMask
SrcIP net.IP
SrcIPMask net.IPMask
EthType uint16
EncDestIP net.IP
EncDestIPMask net.IPMask
EncSrcIP net.IP
EncSrcIPMask net.IPMask
EncDestPort uint16
EncKeyId uint32
SrcMac net.HardwareAddr
DestMac net.HardwareAddr
VlanId uint16
SkipHw bool
SkipSw bool
IPProto *nl.IPProto
DestPort uint16
SrcPort uint16
SrcPortRangeMin uint16
SrcPortRangeMax uint16
DstPortRangeMin uint16
DstPortRangeMax uint16
Actions []Action
}
func (filter *Flower) Attrs() *FilterAttrs {
return &filter.FilterAttrs
}
func (filter *Flower) Type() string {
return "flower"
}
func (filter *Flower) encodeIP(parent *nl.RtAttr, ip net.IP, mask net.IPMask, v4Type, v6Type int, v4MaskType, v6MaskType int) {
ipType := v4Type
maskType := v4MaskType
encodeMask := mask
if mask == nil {
encodeMask = net.CIDRMask(32, 32)
}
v4IP := ip.To4()
if v4IP == nil {
ipType = v6Type
maskType = v6MaskType
if mask == nil {
encodeMask = net.CIDRMask(128, 128)
}
} else {
ip = v4IP
}
parent.AddRtAttr(ipType, ip)
parent.AddRtAttr(maskType, encodeMask)
}
func (filter *Flower) encode(parent *nl.RtAttr) error {
if filter.EthType != 0 {
parent.AddRtAttr(nl.TCA_FLOWER_KEY_ETH_TYPE, htons(filter.EthType))
}
if filter.SrcIP != nil {
filter.encodeIP(parent, filter.SrcIP, filter.SrcIPMask,
nl.TCA_FLOWER_KEY_IPV4_SRC, nl.TCA_FLOWER_KEY_IPV6_SRC,
nl.TCA_FLOWER_KEY_IPV4_SRC_MASK, nl.TCA_FLOWER_KEY_IPV6_SRC_MASK)
}
if filter.DestIP != nil {
filter.encodeIP(parent, filter.DestIP, filter.DestIPMask,
nl.TCA_FLOWER_KEY_IPV4_DST, nl.TCA_FLOWER_KEY_IPV6_DST,
nl.TCA_FLOWER_KEY_IPV4_DST_MASK, nl.TCA_FLOWER_KEY_IPV6_DST_MASK)
}
if filter.EncSrcIP != nil {
filter.encodeIP(parent, filter.EncSrcIP, filter.EncSrcIPMask,
nl.TCA_FLOWER_KEY_ENC_IPV4_SRC, nl.TCA_FLOWER_KEY_ENC_IPV6_SRC,
nl.TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK, nl.TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK)
}
if filter.EncDestIP != nil {
filter.encodeIP(parent, filter.EncDestIP, filter.EncSrcIPMask,
nl.TCA_FLOWER_KEY_ENC_IPV4_DST, nl.TCA_FLOWER_KEY_ENC_IPV6_DST,
nl.TCA_FLOWER_KEY_ENC_IPV4_DST_MASK, nl.TCA_FLOWER_KEY_ENC_IPV6_DST_MASK)
}
if filter.EncDestPort != 0 {
parent.AddRtAttr(nl.TCA_FLOWER_KEY_ENC_UDP_DST_PORT, htons(filter.EncDestPort))
}
if filter.EncKeyId != 0 {
parent.AddRtAttr(nl.TCA_FLOWER_KEY_ENC_KEY_ID, htonl(filter.EncKeyId))
}
if filter.SrcMac != nil {
parent.AddRtAttr(nl.TCA_FLOWER_KEY_ETH_SRC, filter.SrcMac)
}
if filter.DestMac != nil {
parent.AddRtAttr(nl.TCA_FLOWER_KEY_ETH_DST, filter.DestMac)
}
if filter.VlanId != 0 {
parent.AddRtAttr(nl.TCA_FLOWER_KEY_VLAN_ID, nl.Uint16Attr(filter.VlanId))
}
if filter.IPProto != nil {
ipproto := *filter.IPProto
parent.AddRtAttr(nl.TCA_FLOWER_KEY_IP_PROTO, ipproto.Serialize())
if filter.SrcPort != 0 {
switch ipproto {
case nl.IPPROTO_TCP:
parent.AddRtAttr(nl.TCA_FLOWER_KEY_TCP_SRC, htons(filter.SrcPort))
case nl.IPPROTO_UDP:
parent.AddRtAttr(nl.TCA_FLOWER_KEY_UDP_SRC, htons(filter.SrcPort))
case nl.IPPROTO_SCTP:
parent.AddRtAttr(nl.TCA_FLOWER_KEY_SCTP_SRC, htons(filter.SrcPort))
}
}
if filter.DestPort != 0 {
switch ipproto {
case nl.IPPROTO_TCP:
parent.AddRtAttr(nl.TCA_FLOWER_KEY_TCP_DST, htons(filter.DestPort))
case nl.IPPROTO_UDP:
parent.AddRtAttr(nl.TCA_FLOWER_KEY_UDP_DST, htons(filter.DestPort))
case nl.IPPROTO_SCTP:
parent.AddRtAttr(nl.TCA_FLOWER_KEY_SCTP_DST, htons(filter.DestPort))
}
}
}
if filter.SrcPortRangeMin != 0 && filter.SrcPortRangeMax != 0 {
parent.AddRtAttr(nl.TCA_FLOWER_KEY_PORT_SRC_MIN, htons(filter.SrcPortRangeMin))
parent.AddRtAttr(nl.TCA_FLOWER_KEY_PORT_SRC_MAX, htons(filter.SrcPortRangeMax))
}
if filter.DstPortRangeMin != 0 && filter.DstPortRangeMax != 0 {
parent.AddRtAttr(nl.TCA_FLOWER_KEY_PORT_DST_MIN, htons(filter.DstPortRangeMin))
parent.AddRtAttr(nl.TCA_FLOWER_KEY_PORT_DST_MAX, htons(filter.DstPortRangeMax))
}
if filter.ClassId != 0 {
parent.AddRtAttr(nl.TCA_FLOWER_CLASSID, nl.Uint32Attr(filter.ClassId))
}
var flags uint32 = 0
if filter.SkipHw {
flags |= nl.TCA_CLS_FLAGS_SKIP_HW
}
if filter.SkipSw {
flags |= nl.TCA_CLS_FLAGS_SKIP_SW
}
parent.AddRtAttr(nl.TCA_FLOWER_FLAGS, htonl(flags))
actionsAttr := parent.AddRtAttr(nl.TCA_FLOWER_ACT, nil)
if err := EncodeActions(actionsAttr, filter.Actions); err != nil {
return err
}
return nil
}
func (filter *Flower) decode(data []syscall.NetlinkRouteAttr) error {
for _, datum := range data {
switch datum.Attr.Type {
case nl.TCA_FLOWER_KEY_ETH_TYPE:
filter.EthType = ntohs(datum.Value)
case nl.TCA_FLOWER_KEY_IPV4_SRC, nl.TCA_FLOWER_KEY_IPV6_SRC:
filter.SrcIP = datum.Value
case nl.TCA_FLOWER_KEY_IPV4_SRC_MASK, nl.TCA_FLOWER_KEY_IPV6_SRC_MASK:
filter.SrcIPMask = datum.Value
case nl.TCA_FLOWER_KEY_IPV4_DST, nl.TCA_FLOWER_KEY_IPV6_DST:
filter.DestIP = datum.Value
case nl.TCA_FLOWER_KEY_IPV4_DST_MASK, nl.TCA_FLOWER_KEY_IPV6_DST_MASK:
filter.DestIPMask = datum.Value
case nl.TCA_FLOWER_KEY_ENC_IPV4_SRC, nl.TCA_FLOWER_KEY_ENC_IPV6_SRC:
filter.EncSrcIP = datum.Value
case nl.TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK, nl.TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK:
filter.EncSrcIPMask = datum.Value
case nl.TCA_FLOWER_KEY_ENC_IPV4_DST, nl.TCA_FLOWER_KEY_ENC_IPV6_DST:
filter.EncDestIP = datum.Value
case nl.TCA_FLOWER_KEY_ENC_IPV4_DST_MASK, nl.TCA_FLOWER_KEY_ENC_IPV6_DST_MASK:
filter.EncDestIPMask = datum.Value
case nl.TCA_FLOWER_KEY_ENC_UDP_DST_PORT:
filter.EncDestPort = ntohs(datum.Value)
case nl.TCA_FLOWER_KEY_ENC_KEY_ID:
filter.EncKeyId = ntohl(datum.Value)
case nl.TCA_FLOWER_KEY_ETH_SRC:
filter.SrcMac = datum.Value
case nl.TCA_FLOWER_KEY_ETH_DST:
filter.DestMac = datum.Value
case nl.TCA_FLOWER_KEY_VLAN_ID:
filter.VlanId = native.Uint16(datum.Value[0:2])
filter.EthType = unix.ETH_P_8021Q
case nl.TCA_FLOWER_KEY_IP_PROTO:
val := new(nl.IPProto)
*val = nl.IPProto(datum.Value[0])
filter.IPProto = val
case nl.TCA_FLOWER_KEY_TCP_SRC, nl.TCA_FLOWER_KEY_UDP_SRC, nl.TCA_FLOWER_KEY_SCTP_SRC:
filter.SrcPort = ntohs(datum.Value)
case nl.TCA_FLOWER_KEY_TCP_DST, nl.TCA_FLOWER_KEY_UDP_DST, nl.TCA_FLOWER_KEY_SCTP_DST:
filter.DestPort = ntohs(datum.Value)
case nl.TCA_FLOWER_ACT:
tables, err := nl.ParseRouteAttr(datum.Value)
if err != nil {
return err
}
filter.Actions, err = parseActions(tables)
if err != nil {
return err
}
case nl.TCA_FLOWER_FLAGS:
attr := nl.DeserializeUint32Bitfield(datum.Value)
skipSw := attr.Value & nl.TCA_CLS_FLAGS_SKIP_HW
skipHw := attr.Value & nl.TCA_CLS_FLAGS_SKIP_SW
if skipSw != 0 {
filter.SkipSw = true
}
if skipHw != 0 {
filter.SkipHw = true
}
case nl.TCA_FLOWER_KEY_PORT_SRC_MIN:
filter.SrcPortRangeMin = ntohs(datum.Value)
case nl.TCA_FLOWER_KEY_PORT_SRC_MAX:
filter.SrcPortRangeMax = ntohs(datum.Value)
case nl.TCA_FLOWER_KEY_PORT_DST_MIN:
filter.DstPortRangeMin = ntohs(datum.Value)
case nl.TCA_FLOWER_KEY_PORT_DST_MAX:
filter.DstPortRangeMax = ntohs(datum.Value)
case nl.TCA_FLOWER_CLASSID:
filter.ClassId = native.Uint32(datum.Value)
}
}
return nil
}
// FilterDel will delete a filter from the system.
// Equivalent to: `tc filter del $filter`
func FilterDel(filter Filter) error {
return pkgHandle.FilterDel(filter)
}
// FilterDel will delete a filter from the system.
// Equivalent to: `tc filter del $filter`
func (h *Handle) FilterDel(filter Filter) error {
return h.filterModify(filter, unix.RTM_DELTFILTER, 0)
}
// FilterAdd will add a filter to the system.
// Equivalent to: `tc filter add $filter`
func FilterAdd(filter Filter) error {
return pkgHandle.FilterAdd(filter)
}
// FilterAdd will add a filter to the system.
// Equivalent to: `tc filter add $filter`
func (h *Handle) FilterAdd(filter Filter) error {
return h.filterModify(filter, unix.RTM_NEWTFILTER, unix.NLM_F_CREATE|unix.NLM_F_EXCL)
}
// FilterReplace will replace a filter.
// Equivalent to: `tc filter replace $filter`
func FilterReplace(filter Filter) error {
return pkgHandle.FilterReplace(filter)
}
// FilterReplace will replace a filter.
// Equivalent to: `tc filter replace $filter`
func (h *Handle) FilterReplace(filter Filter) error {
return h.filterModify(filter, unix.RTM_NEWTFILTER, unix.NLM_F_CREATE)
}
func (h *Handle) filterModify(filter Filter, proto, flags int) error {
req := h.newNetlinkRequest(proto, flags|unix.NLM_F_ACK)
base := filter.Attrs()
msg := &nl.TcMsg{
Family: nl.FAMILY_ALL,
Ifindex: int32(base.LinkIndex),
Handle: base.Handle,
Parent: base.Parent,
Info: MakeHandle(base.Priority, nl.Swap16(base.Protocol)),
}
req.AddData(msg)
if filter.Attrs().Chain != nil {
req.AddData(nl.NewRtAttr(nl.TCA_CHAIN, nl.Uint32Attr(*filter.Attrs().Chain)))
}
req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(filter.Type())))
options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
switch filter := filter.(type) {
case *U32:
sel := filter.Sel
if sel == nil {
// match all
sel = &nl.TcU32Sel{
Nkeys: 1,
Flags: nl.TC_U32_TERMINAL,
}
sel.Keys = append(sel.Keys, nl.TcU32Key{})
}
if native != networkOrder {
// Copy TcU32Sel.
cSel := *sel
keys := make([]nl.TcU32Key, len(sel.Keys))
copy(keys, sel.Keys)
cSel.Keys = keys
sel = &cSel
// Handle the endianness of attributes
sel.Offmask = native.Uint16(htons(sel.Offmask))
sel.Hmask = native.Uint32(htonl(sel.Hmask))
for i, key := range sel.Keys {
sel.Keys[i].Mask = native.Uint32(htonl(key.Mask))
sel.Keys[i].Val = native.Uint32(htonl(key.Val))
}
}
sel.Nkeys = uint8(len(sel.Keys))
options.AddRtAttr(nl.TCA_U32_SEL, sel.Serialize())
if filter.ClassId != 0 {
options.AddRtAttr(nl.TCA_U32_CLASSID, nl.Uint32Attr(filter.ClassId))
}
if filter.Divisor != 0 {
if (filter.Divisor-1)&filter.Divisor != 0 {
return fmt.Errorf("illegal divisor %d. Must be a power of 2", filter.Divisor)
}
options.AddRtAttr(nl.TCA_U32_DIVISOR, nl.Uint32Attr(filter.Divisor))
}
if filter.Hash != 0 {
options.AddRtAttr(nl.TCA_U32_HASH, nl.Uint32Attr(filter.Hash))
}
if filter.Link != 0 {
options.AddRtAttr(nl.TCA_U32_LINK, nl.Uint32Attr(filter.Link))
}
if filter.Police != nil {
police := options.AddRtAttr(nl.TCA_U32_POLICE, nil)
if err := encodePolice(police, filter.Police); err != nil {
return err
}
}
actionsAttr := options.AddRtAttr(nl.TCA_U32_ACT, nil)
// backwards compatibility
if filter.RedirIndex != 0 {
filter.Actions = append([]Action{NewMirredAction(filter.RedirIndex)}, filter.Actions...)
}
if err := EncodeActions(actionsAttr, filter.Actions); err != nil {
return err
}
case *FwFilter:
if filter.Mask != 0 {
b := make([]byte, 4)
native.PutUint32(b, filter.Mask)
options.AddRtAttr(nl.TCA_FW_MASK, b)
}
if filter.InDev != "" {
options.AddRtAttr(nl.TCA_FW_INDEV, nl.ZeroTerminated(filter.InDev))
}
if filter.Police != nil {
police := options.AddRtAttr(nl.TCA_FW_POLICE, nil)
if err := encodePolice(police, filter.Police); err != nil {
return err
}
}
if filter.ClassId != 0 {
b := make([]byte, 4)
native.PutUint32(b, filter.ClassId)
options.AddRtAttr(nl.TCA_FW_CLASSID, b)
}
actionsAttr := options.AddRtAttr(nl.TCA_FW_ACT, nil)
if err := EncodeActions(actionsAttr, filter.Actions); err != nil {
return err
}
case *BpfFilter:
var bpfFlags uint32
if filter.ClassId != 0 {
options.AddRtAttr(nl.TCA_BPF_CLASSID, nl.Uint32Attr(filter.ClassId))
}
if filter.Fd >= 0 {
options.AddRtAttr(nl.TCA_BPF_FD, nl.Uint32Attr((uint32(filter.Fd))))
}
if filter.Name != "" {
options.AddRtAttr(nl.TCA_BPF_NAME, nl.ZeroTerminated(filter.Name))
}
if filter.DirectAction {
bpfFlags |= nl.TCA_BPF_FLAG_ACT_DIRECT
}
options.AddRtAttr(nl.TCA_BPF_FLAGS, nl.Uint32Attr(bpfFlags))
case *MatchAll:
actionsAttr := options.AddRtAttr(nl.TCA_MATCHALL_ACT, nil)
if err := EncodeActions(actionsAttr, filter.Actions); err != nil {
return err
}
if filter.ClassId != 0 {
options.AddRtAttr(nl.TCA_MATCHALL_CLASSID, nl.Uint32Attr(filter.ClassId))
}
case *Flower:
if err := filter.encode(options); err != nil {
return err
}
}
req.AddData(options)
_, err := req.Execute(unix.NETLINK_ROUTE, 0)
return err
}
// FilterList gets a list of filters in the system.
// Equivalent to: `tc filter show`.
//
// Generally returns nothing if link and parent are not specified.
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func FilterList(link Link, parent uint32) ([]Filter, error) {
return pkgHandle.FilterList(link, parent)
}
// FilterList gets a list of filters in the system.
// Equivalent to: `tc filter show`.
//
// Generally returns nothing if link and parent are not specified.
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) {
req := h.newNetlinkRequest(unix.RTM_GETTFILTER, unix.NLM_F_DUMP)
msg := &nl.TcMsg{
Family: nl.FAMILY_ALL,
Parent: parent,
}
if link != nil {
base := link.Attrs()
h.ensureIndex(base)
msg.Ifindex = int32(base.Index)
}
req.AddData(msg)
msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWTFILTER)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
var res []Filter
for _, m := range msgs {
msg := nl.DeserializeTcMsg(m)
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
if err != nil {
return nil, err
}
base := FilterAttrs{
LinkIndex: int(msg.Ifindex),
Handle: msg.Handle,
Parent: msg.Parent,
}
base.Priority, base.Protocol = MajorMinor(msg.Info)
base.Protocol = nl.Swap16(base.Protocol)
var filter Filter
filterType := ""
detailed := false
for _, attr := range attrs {
switch attr.Attr.Type {
case nl.TCA_KIND:
filterType = string(attr.Value[:len(attr.Value)-1])
switch filterType {
case "u32":
filter = &U32{}
case "fw":
filter = &FwFilter{}
case "bpf":
filter = &BpfFilter{}
case "matchall":
filter = &MatchAll{}
case "flower":
filter = &Flower{}
default:
filter = &GenericFilter{FilterType: filterType}
}
case nl.TCA_OPTIONS:
data, err := nl.ParseRouteAttr(attr.Value)
if err != nil {
return nil, err
}
switch filterType {
case "u32":
detailed, err = parseU32Data(filter, data)
if err != nil {
return nil, err
}
case "fw":
detailed, err = parseFwData(filter, data)
if err != nil {
return nil, err
}
case "bpf":
detailed, err = parseBpfData(filter, data)
if err != nil {
return nil, err
}
case "matchall":
detailed, err = parseMatchAllData(filter, data)
if err != nil {
return nil, err
}
case "flower":
detailed, err = parseFlowerData(filter, data)
if err != nil {
return nil, err
}
default:
detailed = true
}
case nl.TCA_CHAIN:
val := new(uint32)
*val = native.Uint32(attr.Value)
base.Chain = val
}
}
// only return the detailed version of the filter
if detailed {
*filter.Attrs() = base
res = append(res, filter)
}
}
return res, executeErr
}
func toTcGen(attrs *ActionAttrs, tcgen *nl.TcGen) {
tcgen.Index = uint32(attrs.Index)
tcgen.Capab = uint32(attrs.Capab)
tcgen.Action = int32(attrs.Action)
tcgen.Refcnt = int32(attrs.Refcnt)
tcgen.Bindcnt = int32(attrs.Bindcnt)
}
func toAttrs(tcgen *nl.TcGen, attrs *ActionAttrs) {
attrs.Index = int(tcgen.Index)
attrs.Capab = int(tcgen.Capab)
attrs.Action = TcAct(tcgen.Action)
attrs.Refcnt = int(tcgen.Refcnt)
attrs.Bindcnt = int(tcgen.Bindcnt)
}
func toTimeStamp(tcf *nl.Tcf) *ActionTimestamp {
return &ActionTimestamp{
Installed: tcf.Install,
LastUsed: tcf.LastUse,
Expires: tcf.Expires,
FirstUsed: tcf.FirstUse}
}
func encodePolice(attr *nl.RtAttr, action *PoliceAction) error {
var rtab [256]uint32
var ptab [256]uint32
police := nl.TcPolice{}
police.Index = uint32(action.Attrs().Index)
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 {
police.Rate.Mpu = action.Mpu
police.Rate.Overhead = action.Overhead
if CalcRtable(&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.Mtu = action.Mtu
if police.PeakRate.Rate != 0 {
police.PeakRate.Mpu = action.Mpu
police.PeakRate.Overhead = action.Overhead
if CalcRtable(&police.PeakRate, ptab[:], action.PCellLog, action.Mtu, action.LinkLayer) < 0 {
return errors.New("POLICE: failed to calculate peak rate table")
}
}
attr.AddRtAttr(nl.TCA_POLICE_TBF, police.Serialize())
if police.Rate.Rate != 0 {
attr.AddRtAttr(nl.TCA_POLICE_RATE, SerializeRtab(rtab))
}
if police.PeakRate.Rate != 0 {
attr.AddRtAttr(nl.TCA_POLICE_PEAKRATE, SerializeRtab(ptab))
}
if action.AvRate != 0 {
attr.AddRtAttr(nl.TCA_POLICE_AVRATE, nl.Uint32Attr(action.AvRate))
}
if action.NotExceedAction != 0 {
attr.AddRtAttr(nl.TCA_POLICE_RESULT, nl.Uint32Attr(uint32(action.NotExceedAction)))
}
return nil
}
func EncodeActions(attr *nl.RtAttr, actions []Action) error {
tabIndex := int(nl.TCA_ACT_TAB)
for _, action := range actions {
switch action := action.(type) {
default:
return fmt.Errorf("unknown action type %s", action.Type())
case *PoliceAction:
table := attr.AddRtAttr(tabIndex, nil)
tabIndex++
table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("police"))
aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil)
if err := encodePolice(aopts, action); err != nil {
return err
}
case *MirredAction:
table := attr.AddRtAttr(tabIndex, nil)
tabIndex++
table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("mirred"))
aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil)
mirred := nl.TcMirred{
Eaction: int32(action.MirredAction),
Ifindex: uint32(action.Ifindex),
}
toTcGen(action.Attrs(), &mirred.TcGen)
aopts.AddRtAttr(nl.TCA_MIRRED_PARMS, mirred.Serialize())
case *VlanAction:
table := attr.AddRtAttr(tabIndex, nil)
tabIndex++
table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("vlan"))
aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil)
vlan := nl.TcVlan{
Action: int32(action.Action),
}
toTcGen(action.Attrs(), &vlan.TcGen)
aopts.AddRtAttr(nl.TCA_VLAN_PARMS, vlan.Serialize())
if action.Action == TCA_VLAN_ACT_PUSH && action.VlanID == 0 {
return fmt.Errorf("vlan id is required for push action")
}
if action.VlanID != 0 {
aopts.AddRtAttr(nl.TCA_VLAN_PUSH_VLAN_ID, nl.Uint16Attr(action.VlanID))
}
case *TunnelKeyAction:
table := attr.AddRtAttr(tabIndex, nil)
tabIndex++
table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("tunnel_key"))
aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil)
tun := nl.TcTunnelKey{
Action: int32(action.Action),
}
toTcGen(action.Attrs(), &tun.TcGen)
aopts.AddRtAttr(nl.TCA_TUNNEL_KEY_PARMS, tun.Serialize())
if action.Action == TCA_TUNNEL_KEY_SET {
aopts.AddRtAttr(nl.TCA_TUNNEL_KEY_ENC_KEY_ID, htonl(action.KeyID))
if v4 := action.SrcAddr.To4(); v4 != nil {
aopts.AddRtAttr(nl.TCA_TUNNEL_KEY_ENC_IPV4_SRC, v4[:])
} else if v6 := action.SrcAddr.To16(); v6 != nil {
aopts.AddRtAttr(nl.TCA_TUNNEL_KEY_ENC_IPV6_SRC, v6[:])
} else {
return fmt.Errorf("invalid src addr %s for tunnel_key action", action.SrcAddr)
}
if v4 := action.DstAddr.To4(); v4 != nil {
aopts.AddRtAttr(nl.TCA_TUNNEL_KEY_ENC_IPV4_DST, v4[:])
} else if v6 := action.DstAddr.To16(); v6 != nil {
aopts.AddRtAttr(nl.TCA_TUNNEL_KEY_ENC_IPV6_DST, v6[:])
} else {
return fmt.Errorf("invalid dst addr %s for tunnel_key action", action.DstAddr)
}
if action.DestPort != 0 {
aopts.AddRtAttr(nl.TCA_TUNNEL_KEY_ENC_DST_PORT, htons(action.DestPort))
}
}
case *SkbEditAction:
table := attr.AddRtAttr(tabIndex, nil)
tabIndex++
table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("skbedit"))
aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil)
skbedit := nl.TcSkbEdit{}
toTcGen(action.Attrs(), &skbedit.TcGen)
aopts.AddRtAttr(nl.TCA_SKBEDIT_PARMS, skbedit.Serialize())
if action.QueueMapping != nil {
aopts.AddRtAttr(nl.TCA_SKBEDIT_QUEUE_MAPPING, nl.Uint16Attr(*action.QueueMapping))
}
if action.Priority != nil {
aopts.AddRtAttr(nl.TCA_SKBEDIT_PRIORITY, nl.Uint32Attr(*action.Priority))
}
if action.PType != nil {
aopts.AddRtAttr(nl.TCA_SKBEDIT_PTYPE, nl.Uint16Attr(*action.PType))
}
if action.Mark != nil {
aopts.AddRtAttr(nl.TCA_SKBEDIT_MARK, nl.Uint32Attr(*action.Mark))
}
if action.Mask != nil {
aopts.AddRtAttr(nl.TCA_SKBEDIT_MASK, nl.Uint32Attr(*action.Mask))
}
case *ConnmarkAction:
table := attr.AddRtAttr(tabIndex, nil)
tabIndex++
table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("connmark"))
aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil)
connmark := nl.TcConnmark{
Zone: action.Zone,
}
toTcGen(action.Attrs(), &connmark.TcGen)
aopts.AddRtAttr(nl.TCA_CONNMARK_PARMS, connmark.Serialize())
case *CsumAction:
table := attr.AddRtAttr(tabIndex, nil)
tabIndex++
table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("csum"))
aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil)
csum := nl.TcCsum{
UpdateFlags: uint32(action.UpdateFlags),
}
toTcGen(action.Attrs(), &csum.TcGen)
aopts.AddRtAttr(nl.TCA_CSUM_PARMS, csum.Serialize())
case *BpfAction:
table := attr.AddRtAttr(tabIndex, nil)
tabIndex++
table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("bpf"))
aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil)
gen := nl.TcGen{}
toTcGen(action.Attrs(), &gen)
aopts.AddRtAttr(nl.TCA_ACT_BPF_PARMS, gen.Serialize())
aopts.AddRtAttr(nl.TCA_ACT_BPF_FD, nl.Uint32Attr(uint32(action.Fd)))
aopts.AddRtAttr(nl.TCA_ACT_BPF_NAME, nl.ZeroTerminated(action.Name))
case *SampleAction:
table := attr.AddRtAttr(tabIndex, nil)
tabIndex++
table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("sample"))
aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil)
gen := nl.TcGen{}
toTcGen(action.Attrs(), &gen)
aopts.AddRtAttr(nl.TCA_ACT_SAMPLE_PARMS, gen.Serialize())
aopts.AddRtAttr(nl.TCA_ACT_SAMPLE_RATE, nl.Uint32Attr(action.Rate))
aopts.AddRtAttr(nl.TCA_ACT_SAMPLE_PSAMPLE_GROUP, nl.Uint32Attr(action.Group))
aopts.AddRtAttr(nl.TCA_ACT_SAMPLE_TRUNC_SIZE, nl.Uint32Attr(action.TruncSize))
case *GenericAction:
table := attr.AddRtAttr(tabIndex, nil)
tabIndex++
table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("gact"))
aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil)
gen := nl.TcGen{}
toTcGen(action.Attrs(), &gen)
aopts.AddRtAttr(nl.TCA_GACT_PARMS, gen.Serialize())
case *PeditAction:
table := attr.AddRtAttr(tabIndex, nil)
tabIndex++
pedit := nl.TcPedit{}
toTcGen(action.Attrs(), &pedit.Sel.TcGen)
if action.SrcMacAddr != nil {
pedit.SetEthSrc(action.SrcMacAddr)
}
if action.DstMacAddr != nil {
pedit.SetEthDst(action.DstMacAddr)
}
if action.SrcIP != nil {
pedit.SetSrcIP(action.SrcIP)
}
if action.DstIP != nil {
pedit.SetDstIP(action.DstIP)
}
if action.SrcPort != 0 {
pedit.SetSrcPort(action.SrcPort, action.Proto)
}
if action.DstPort != 0 {
pedit.SetDstPort(action.DstPort, action.Proto)
}
pedit.Encode(table)
}
}
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 parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) {
var actions []Action
for _, table := range tables {
var action Action
var actionType string
var actionnStatistic *ActionStatistic
var actionTimestamp *ActionTimestamp
aattrs, err := nl.ParseRouteAttr(table.Value)
if err != nil {
return nil, err
}
nextattr:
for _, aattr := range aattrs {
switch aattr.Attr.Type {
case nl.TCA_KIND:
actionType = string(aattr.Value[:len(aattr.Value)-1])
// only parse if the action is mirred or bpf
switch actionType {
case "mirred":
action = &MirredAction{}
case "bpf":
action = &BpfAction{}
case "connmark":
action = &ConnmarkAction{}
case "csum":
action = &CsumAction{}
case "sample":
action = &SampleAction{}
case "gact":
action = &GenericAction{}
case "vlan":
action = &VlanAction{}
case "tunnel_key":
action = &TunnelKeyAction{}
case "skbedit":
action = &SkbEditAction{}
case "police":
action = &PoliceAction{}
case "pedit":
action = &PeditAction{}
default:
break nextattr
}
case nl.TCA_OPTIONS:
adata, err := nl.ParseRouteAttr(aattr.Value)
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
}
case nl.TCA_GACT_TM:
tcTs := nl.DeserializeTcf(adatum.Value)
actionTimestamp = toTimeStamp(tcTs)
}
case "police":
parsePolice(adatum, action.(*PoliceAction))
}
}
case nl.TCA_ACT_STATS:
s, err := parseTcStats2(aattr.Value)
if err != nil {
return nil, err
}
actionnStatistic = (*ActionStatistic)(s)
}
}
if action != nil {
action.Attrs().Statistics = actionnStatistic
action.Attrs().Timestamp = actionTimestamp
actions = append(actions, action)
}
}
return actions, nil
}
func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
u32 := filter.(*U32)
detailed := false
for _, datum := range data {
switch datum.Attr.Type {
case nl.TCA_U32_SEL:
detailed = true
sel := nl.DeserializeTcU32Sel(datum.Value)
u32.Sel = sel
if native != networkOrder {
// Handle the endianness of attributes
u32.Sel.Offmask = native.Uint16(htons(sel.Offmask))
u32.Sel.Hmask = native.Uint32(htonl(sel.Hmask))
for i, key := range u32.Sel.Keys {
u32.Sel.Keys[i].Mask = native.Uint32(htonl(key.Mask))
u32.Sel.Keys[i].Val = native.Uint32(htonl(key.Val))
}
}
case nl.TCA_U32_ACT:
tables, err := nl.ParseRouteAttr(datum.Value)
if err != nil {
return detailed, err
}
u32.Actions, err = parseActions(tables)
if err != nil {
return detailed, err
}
for _, action := range u32.Actions {
if action, ok := action.(*MirredAction); ok {
u32.RedirIndex = int(action.Ifindex)
}
}
case nl.TCA_U32_POLICE:
var police PoliceAction
adata, _ := nl.ParseRouteAttr(datum.Value)
for _, aattr := range adata {
parsePolice(aattr, &police)
}
u32.Police = &police
case nl.TCA_U32_CLASSID:
u32.ClassId = native.Uint32(datum.Value)
case nl.TCA_U32_DIVISOR:
u32.Divisor = native.Uint32(datum.Value)
case nl.TCA_U32_HASH:
u32.Hash = native.Uint32(datum.Value)
case nl.TCA_U32_LINK:
u32.Link = native.Uint32(datum.Value)
}
}
return detailed, nil
}
func parseFwData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
fw := filter.(*FwFilter)
detailed := true
for _, datum := range data {
switch datum.Attr.Type {
case nl.TCA_FW_MASK:
fw.Mask = native.Uint32(datum.Value[0:4])
case nl.TCA_FW_CLASSID:
fw.ClassId = native.Uint32(datum.Value[0:4])
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)
}
fw.Police = &police
case nl.TCA_FW_ACT:
tables, err := nl.ParseRouteAttr(datum.Value)
if err != nil {
return detailed, err
}
fw.Actions, err = parseActions(tables)
if err != nil {
return detailed, err
}
}
}
return detailed, nil
}
func parseBpfData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
bpf := filter.(*BpfFilter)
detailed := true
for _, datum := range data {
switch datum.Attr.Type {
case nl.TCA_BPF_FD:
bpf.Fd = int(native.Uint32(datum.Value[0:4]))
case nl.TCA_BPF_NAME:
bpf.Name = string(datum.Value[:len(datum.Value)-1])
case nl.TCA_BPF_CLASSID:
bpf.ClassId = native.Uint32(datum.Value[0:4])
case nl.TCA_BPF_FLAGS:
flags := native.Uint32(datum.Value[0:4])
if (flags & nl.TCA_BPF_FLAG_ACT_DIRECT) != 0 {
bpf.DirectAction = true
}
case nl.TCA_BPF_ID:
bpf.Id = int(native.Uint32(datum.Value[0:4]))
case nl.TCA_BPF_TAG:
bpf.Tag = hex.EncodeToString(datum.Value)
}
}
return detailed, nil
}
func parseMatchAllData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
matchall := filter.(*MatchAll)
detailed := true
for _, datum := range data {
switch datum.Attr.Type {
case nl.TCA_MATCHALL_CLASSID:
matchall.ClassId = native.Uint32(datum.Value[0:4])
case nl.TCA_MATCHALL_ACT:
tables, err := nl.ParseRouteAttr(datum.Value)
if err != nil {
return detailed, err
}
matchall.Actions, err = parseActions(tables)
if err != nil {
return detailed, err
}
}
}
return detailed, nil
}
func parseFlowerData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
return true, filter.(*Flower).decode(data)
}
func AlignToAtm(size uint) uint {
var linksize, cells int
cells = int(size / nl.ATM_CELL_PAYLOAD)
if (size % nl.ATM_CELL_PAYLOAD) > 0 {
cells++
}
linksize = cells * nl.ATM_CELL_SIZE
return uint(linksize)
}
func AdjustSize(sz uint, mpu uint, linklayer int) uint {
if sz < mpu {
sz = mpu
}
switch linklayer {
case nl.LINKLAYER_ATM:
return AlignToAtm(sz)
default:
return sz
}
}
func CalcRtable(rate *nl.TcRateSpec, rtab []uint32, cellLog int, mtu uint32, linklayer int) int {
bps := rate.Rate
mpu := rate.Mpu
var sz uint
if mtu == 0 {
mtu = 2047
}
if cellLog < 0 {
cellLog = 0
for (mtu >> uint(cellLog)) > 255 {
cellLog++
}
}
for i := 0; i < 256; i++ {
sz = AdjustSize(uint((i+1)<<uint32(cellLog)), uint(mpu), linklayer)
rtab[i] = Xmittime(uint64(bps), uint32(sz))
}
rate.CellAlign = -1
rate.CellLog = uint8(cellLog)
rate.Linklayer = uint8(linklayer & nl.TC_LINKLAYER_MASK)
return cellLog
}
func DeserializeRtab(b []byte) [256]uint32 {
var rtab [256]uint32
r := bytes.NewReader(b)
_ = binary.Read(r, native, &rtab)
return rtab
}
func SerializeRtab(rtab [256]uint32) []byte {
var w bytes.Buffer
_ = binary.Write(&w, native, rtab)
return w.Bytes()
}