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 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 SkipHw bool SkipSw bool IPProto *nl.IPProto DestPort uint16 SrcPort 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.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)) } } } 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_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 } } } 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, cap(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. 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. 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, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWTFILTER) if err != nil { return nil, err } 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, nil } 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 *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 *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{} 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 "gact": action = &GenericAction{} 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 "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 "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)<