summaryrefslogtreecommitdiff
path: root/vendor/github.com/vishvananda/netlink/filter_linux.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/vishvananda/netlink/filter_linux.go')
-rw-r--r--vendor/github.com/vishvananda/netlink/filter_linux.go591
1 files changed, 591 insertions, 0 deletions
diff --git a/vendor/github.com/vishvananda/netlink/filter_linux.go b/vendor/github.com/vishvananda/netlink/filter_linux.go
new file mode 100644
index 000000000..dc0f90af8
--- /dev/null
+++ b/vendor/github.com/vishvananda/netlink/filter_linux.go
@@ -0,0 +1,591 @@
+package netlink
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "syscall"
+ "unsafe"
+
+ "github.com/vishvananda/netlink/nl"
+)
+
+// Fw filter filters on firewall marks
+// NOTE: this is in filter_linux because it refers to nl.TcPolice which
+// is defined in nl/tc_linux.go
+type Fw struct {
+ FilterAttrs
+ ClassId uint32
+ // TODO remove nl type from interface
+ Police nl.TcPolice
+ InDev string
+ // TODO Action
+ Mask uint32
+ AvRate uint32
+ Rtab [256]uint32
+ Ptab [256]uint32
+}
+
+func NewFw(attrs FilterAttrs, fattrs FilterFwAttrs) (*Fw, error) {
+ var rtab [256]uint32
+ var ptab [256]uint32
+ rcellLog := -1
+ pcellLog := -1
+ avrate := fattrs.AvRate / 8
+ police := nl.TcPolice{}
+ police.Rate.Rate = fattrs.Rate / 8
+ police.PeakRate.Rate = fattrs.PeakRate / 8
+ buffer := fattrs.Buffer
+ linklayer := nl.LINKLAYER_ETHERNET
+
+ if fattrs.LinkLayer != nl.LINKLAYER_UNSPEC {
+ linklayer = fattrs.LinkLayer
+ }
+
+ police.Action = int32(fattrs.Action)
+ if police.Rate.Rate != 0 {
+ police.Rate.Mpu = fattrs.Mpu
+ police.Rate.Overhead = fattrs.Overhead
+ if CalcRtable(&police.Rate, rtab, rcellLog, fattrs.Mtu, linklayer) < 0 {
+ return nil, errors.New("TBF: failed to calculate rate table")
+ }
+ police.Burst = uint32(Xmittime(uint64(police.Rate.Rate), uint32(buffer)))
+ }
+ police.Mtu = fattrs.Mtu
+ if police.PeakRate.Rate != 0 {
+ police.PeakRate.Mpu = fattrs.Mpu
+ police.PeakRate.Overhead = fattrs.Overhead
+ if CalcRtable(&police.PeakRate, ptab, pcellLog, fattrs.Mtu, linklayer) < 0 {
+ return nil, errors.New("POLICE: failed to calculate peak rate table")
+ }
+ }
+
+ return &Fw{
+ FilterAttrs: attrs,
+ ClassId: fattrs.ClassId,
+ InDev: fattrs.InDev,
+ Mask: fattrs.Mask,
+ Police: police,
+ AvRate: avrate,
+ Rtab: rtab,
+ Ptab: ptab,
+ }, nil
+}
+
+func (filter *Fw) Attrs() *FilterAttrs {
+ return &filter.FilterAttrs
+}
+
+func (filter *Fw) Type() string {
+ return "fw"
+}
+
+// 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 {
+ req := h.newNetlinkRequest(syscall.RTM_DELTFILTER, syscall.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)
+
+ _, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+ return err
+}
+
+// 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 {
+ native = nl.NativeEndian()
+ req := h.newNetlinkRequest(syscall.RTM_NEWTFILTER, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.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)
+ req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(filter.Type())))
+
+ options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
+ if u32, ok := filter.(*U32); ok {
+ // Convert TcU32Sel into nl.TcU32Sel as it is without copy.
+ sel := (*nl.TcU32Sel)(unsafe.Pointer(u32.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))
+ nl.NewRtAttrChild(options, nl.TCA_U32_SEL, sel.Serialize())
+ if u32.ClassId != 0 {
+ nl.NewRtAttrChild(options, nl.TCA_U32_CLASSID, nl.Uint32Attr(u32.ClassId))
+ }
+ actionsAttr := nl.NewRtAttrChild(options, nl.TCA_U32_ACT, nil)
+ // backwards compatibility
+ if u32.RedirIndex != 0 {
+ u32.Actions = append([]Action{NewMirredAction(u32.RedirIndex)}, u32.Actions...)
+ }
+ if err := EncodeActions(actionsAttr, u32.Actions); err != nil {
+ return err
+ }
+ } else if fw, ok := filter.(*Fw); ok {
+ if fw.Mask != 0 {
+ b := make([]byte, 4)
+ native.PutUint32(b, fw.Mask)
+ nl.NewRtAttrChild(options, nl.TCA_FW_MASK, b)
+ }
+ if fw.InDev != "" {
+ nl.NewRtAttrChild(options, nl.TCA_FW_INDEV, nl.ZeroTerminated(fw.InDev))
+ }
+ if (fw.Police != nl.TcPolice{}) {
+
+ police := nl.NewRtAttrChild(options, nl.TCA_FW_POLICE, nil)
+ nl.NewRtAttrChild(police, nl.TCA_POLICE_TBF, fw.Police.Serialize())
+ if (fw.Police.Rate != nl.TcRateSpec{}) {
+ payload := SerializeRtab(fw.Rtab)
+ nl.NewRtAttrChild(police, nl.TCA_POLICE_RATE, payload)
+ }
+ if (fw.Police.PeakRate != nl.TcRateSpec{}) {
+ payload := SerializeRtab(fw.Ptab)
+ nl.NewRtAttrChild(police, nl.TCA_POLICE_PEAKRATE, payload)
+ }
+ }
+ if fw.ClassId != 0 {
+ b := make([]byte, 4)
+ native.PutUint32(b, fw.ClassId)
+ nl.NewRtAttrChild(options, nl.TCA_FW_CLASSID, b)
+ }
+ } else if bpf, ok := filter.(*BpfFilter); ok {
+ var bpfFlags uint32
+ if bpf.ClassId != 0 {
+ nl.NewRtAttrChild(options, nl.TCA_BPF_CLASSID, nl.Uint32Attr(bpf.ClassId))
+ }
+ if bpf.Fd >= 0 {
+ nl.NewRtAttrChild(options, nl.TCA_BPF_FD, nl.Uint32Attr((uint32(bpf.Fd))))
+ }
+ if bpf.Name != "" {
+ nl.NewRtAttrChild(options, nl.TCA_BPF_NAME, nl.ZeroTerminated(bpf.Name))
+ }
+ if bpf.DirectAction {
+ bpfFlags |= nl.TCA_BPF_FLAG_ACT_DIRECT
+ }
+ nl.NewRtAttrChild(options, nl.TCA_BPF_FLAGS, nl.Uint32Attr(bpfFlags))
+ }
+
+ req.AddData(options)
+ _, err := req.Execute(syscall.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(syscall.RTM_GETTFILTER, syscall.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(syscall.NETLINK_ROUTE, syscall.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 = &Fw{}
+ case "bpf":
+ filter = &BpfFilter{}
+ 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
+ }
+ default:
+ detailed = true
+ }
+ }
+ }
+ // 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 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 *MirredAction:
+ table := nl.NewRtAttrChild(attr, tabIndex, nil)
+ tabIndex++
+ nl.NewRtAttrChild(table, nl.TCA_ACT_KIND, nl.ZeroTerminated("mirred"))
+ aopts := nl.NewRtAttrChild(table, nl.TCA_ACT_OPTIONS, nil)
+ mirred := nl.TcMirred{
+ Eaction: int32(action.MirredAction),
+ Ifindex: uint32(action.Ifindex),
+ }
+ toTcGen(action.Attrs(), &mirred.TcGen)
+ nl.NewRtAttrChild(aopts, nl.TCA_MIRRED_PARMS, mirred.Serialize())
+ case *BpfAction:
+ table := nl.NewRtAttrChild(attr, tabIndex, nil)
+ tabIndex++
+ nl.NewRtAttrChild(table, nl.TCA_ACT_KIND, nl.ZeroTerminated("bpf"))
+ aopts := nl.NewRtAttrChild(table, nl.TCA_ACT_OPTIONS, nil)
+ gen := nl.TcGen{}
+ toTcGen(action.Attrs(), &gen)
+ nl.NewRtAttrChild(aopts, nl.TCA_ACT_BPF_PARMS, gen.Serialize())
+ nl.NewRtAttrChild(aopts, nl.TCA_ACT_BPF_FD, nl.Uint32Attr(uint32(action.Fd)))
+ nl.NewRtAttrChild(aopts, nl.TCA_ACT_BPF_NAME, nl.ZeroTerminated(action.Name))
+ case *GenericAction:
+ table := nl.NewRtAttrChild(attr, tabIndex, nil)
+ tabIndex++
+ nl.NewRtAttrChild(table, nl.TCA_ACT_KIND, nl.ZeroTerminated("gact"))
+ aopts := nl.NewRtAttrChild(table, nl.TCA_ACT_OPTIONS, nil)
+ gen := nl.TcGen{}
+ toTcGen(action.Attrs(), &gen)
+ nl.NewRtAttrChild(aopts, nl.TCA_GACT_PARMS, gen.Serialize())
+ }
+ }
+ return nil
+}
+
+func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) {
+ var actions []Action
+ for _, table := range tables {
+ var action Action
+ var actionType string
+ 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 "gact":
+ action = &GenericAction{}
+ 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)
+ toAttrs(&mirred.TcGen, action.Attrs())
+ action.(*MirredAction).ActionAttrs = ActionAttrs{}
+ action.(*MirredAction).Ifindex = int(mirred.Ifindex)
+ action.(*MirredAction).MirredAction = MirredAct(mirred.Eaction)
+ }
+ 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 "gact":
+ switch adatum.Attr.Type {
+ case nl.TCA_GACT_PARMS:
+ gen := *nl.DeserializeTcGen(adatum.Value)
+ toAttrs(&gen, action.Attrs())
+ }
+ }
+ }
+ }
+ }
+ actions = append(actions, action)
+ }
+ return actions, nil
+}
+
+func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
+ native = nl.NativeEndian()
+ 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 = (*TcU32Sel)(unsafe.Pointer(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_CLASSID:
+ u32.ClassId = native.Uint32(datum.Value)
+ }
+ }
+ return detailed, nil
+}
+
+func parseFwData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
+ native = nl.NativeEndian()
+ fw := filter.(*Fw)
+ 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:
+ adata, _ := nl.ParseRouteAttr(datum.Value)
+ for _, aattr := range adata {
+ switch aattr.Attr.Type {
+ case nl.TCA_POLICE_TBF:
+ fw.Police = *nl.DeserializeTcPolice(aattr.Value)
+ case nl.TCA_POLICE_RATE:
+ fw.Rtab = DeserializeRtab(aattr.Value)
+ case nl.TCA_POLICE_PEAKRATE:
+ fw.Ptab = DeserializeRtab(aattr.Value)
+ }
+ }
+ }
+ }
+ return detailed, nil
+}
+
+func parseBpfData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
+ native = nl.NativeEndian()
+ 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
+ }
+ }
+ }
+ return detailed, nil
+}
+
+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 [256]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] = uint32(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
+ native := nl.NativeEndian()
+ r := bytes.NewReader(b)
+ _ = binary.Read(r, native, &rtab)
+ return rtab
+}
+
+func SerializeRtab(rtab [256]uint32) []byte {
+ native := nl.NativeEndian()
+ var w bytes.Buffer
+ _ = binary.Write(&w, native, rtab)
+ return w.Bytes()
+}