From ed8931371a8063912d9cf485a35447e791ee6f83 Mon Sep 17 00:00:00 2001 From: Zhiyuan Hou Date: Wed, 11 Dec 2019 17:03:31 +0800 Subject: [PATCH] filter, action: Add support for skbedit Signed-off-by: Zhiyuan Hou --- filter.go | 24 +++++++ filter_linux.go | 41 ++++++++++++ filter_test.go | 172 ++++++++++++++++++++++++++++++++++++++++++++++++ nl/tc_linux.go | 29 ++++++++ 4 files changed, 266 insertions(+) diff --git a/filter.go b/filter.go index 2cc6fc6..88792ea 100644 --- a/filter.go +++ b/filter.go @@ -235,6 +235,30 @@ func NewTunnelKeyAction() *TunnelKeyAction { } } +type SkbEditAction struct { + ActionAttrs + QueueMapping *uint16 + PType *uint16 + Priority *uint32 + Mark *uint32 +} + +func (action *SkbEditAction) Type() string { + return "skbedit" +} + +func (action *SkbEditAction) Attrs() *ActionAttrs { + return &action.ActionAttrs +} + +func NewSkbEditAction() *SkbEditAction { + return &SkbEditAction{ + ActionAttrs: ActionAttrs{ + Action: TC_ACT_PIPE, + }, + } +} + // MatchAll filters match all packets type MatchAll struct { FilterAttrs diff --git a/filter_linux.go b/filter_linux.go index b052ba8..c56f314 100644 --- a/filter_linux.go +++ b/filter_linux.go @@ -457,6 +457,26 @@ func EncodeActions(attr *nl.RtAttr, actions []Action) error { return fmt.Errorf("invalid dst addr %s for tunnel_key action", action.DstAddr) } } + 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)) + } case *ConnmarkAction: table := attr.AddRtAttr(tabIndex, nil) tabIndex++ @@ -516,6 +536,8 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) { action = &GenericAction{} case "tunnel_key": action = &TunnelKeyAction{} + case "skbedit": + action = &SkbEditAction{} default: break nextattr } @@ -551,6 +573,25 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) { case nl.TCA_TUNNEL_KEY_ENC_IPV4_DST: action.(*TunnelKeyAction).DstAddr = net.IP(adatum.Value[:]) } + 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_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 "bpf": switch adatum.Attr.Type { case nl.TCA_ACT_BPF_PARMS: diff --git a/filter_test.go b/filter_test.go index cfb87b0..6e3e559 100644 --- a/filter_test.go +++ b/filter_test.go @@ -1134,3 +1134,175 @@ func TestFilterU32TunnelKeyAddDel(t *testing.T) { t.Fatal("Failed to remove qdisc") } } + +func TestFilterU32SkbEditAddDel(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil { + t.Fatal(err) + } + if err := LinkAdd(&Ifb{LinkAttrs{Name: "bar"}}); err != nil { + t.Fatal(err) + } + link, err := LinkByName("foo") + if err != nil { + t.Fatal(err) + } + if err := LinkSetUp(link); err != nil { + t.Fatal(err) + } + redir, err := LinkByName("bar") + if err != nil { + t.Fatal(err) + } + if err := LinkSetUp(redir); err != nil { + t.Fatal(err) + } + + qdisc := &Ingress{ + QdiscAttrs: QdiscAttrs{ + LinkIndex: link.Attrs().Index, + Handle: MakeHandle(0xffff, 0), + Parent: HANDLE_INGRESS, + }, + } + if err := QdiscAdd(qdisc); err != nil { + t.Fatal(err) + } + qdiscs, err := SafeQdiscList(link) + if err != nil { + t.Fatal(err) + } + + found := false + for _, v := range qdiscs { + if _, ok := v.(*Ingress); ok { + found = true + break + } + } + if !found { + t.Fatal("Qdisc is the wrong type") + } + + skbedit := NewSkbEditAction() + ptype := uint16(unix.PACKET_HOST) + skbedit.PType = &ptype + priority := uint32(0xff) + skbedit.Priority = &priority + mark := uint32(0xfe) + skbedit.Mark = &mark + mapping := uint16(0xf) + skbedit.QueueMapping = &mapping + + 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{ + skbedit, + &MirredAction{ + ActionAttrs: ActionAttrs{ + Action: TC_ACT_STOLEN, + }, + MirredAction: TCA_EGRESS_REDIR, + Ifindex: redir.Attrs().Index, + }, + }, + } + + 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") + } + + 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 + edit, ok := u32.Actions[0].(*SkbEditAction) + if !ok { + edit, ok = u32.Actions[1].(*SkbEditAction) + if !ok { + t.Fatal("Unable to find tunnel action") + } + } + + if edit.Attrs().Action != TC_ACT_PIPE { + t.Fatal("SkbEdit action isn't TC_ACT_PIPE") + } + if edit.PType == nil || *edit.PType != *skbedit.PType { + t.Fatal("Action PType doesn't match") + } + if edit.QueueMapping == nil || *edit.QueueMapping != *skbedit.QueueMapping { + t.Fatal("Action QueueMapping doesn't match") + } + if edit.Mark == nil || *edit.Mark != *skbedit.Mark { + t.Fatal("Action Mark doesn't match") + } + if edit.Priority == nil || *edit.Priority != *skbedit.Priority { + t.Fatal("Action Priority doesn't match") + } + + mia, ok := u32.Actions[0].(*MirredAction) + if !ok { + 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 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 { + t.Fatal(err) + } + qdiscs, err = SafeQdiscList(link) + if err != nil { + t.Fatal(err) + } + + found = false + for _, v := range qdiscs { + if _, ok := v.(*Ingress); ok { + found = true + break + } + } + if found { + t.Fatal("Failed to remove qdisc") + } +} diff --git a/nl/tc_linux.go b/nl/tc_linux.go index 61ea9a5..501f554 100644 --- a/nl/tc_linux.go +++ b/nl/tc_linux.go @@ -92,6 +92,7 @@ const ( SizeofTcConnmark = SizeofTcGen + 0x04 SizeofTcMirred = SizeofTcGen + 0x08 SizeofTcTunnelKey = SizeofTcGen + 0x04 + SizeofTcSkbEdit = SizeofTcGen SizeofTcPolice = 2*SizeofTcRateSpec + 0x20 ) @@ -754,6 +755,34 @@ func (x *TcTunnelKey) Serialize() []byte { return (*(*[SizeofTcTunnelKey]byte)(unsafe.Pointer(x)))[:] } +const ( + TCA_SKBEDIT_UNSPEC = iota + TCA_SKBEDIT_TM + TCA_SKBEDIT_PARMS + TCA_SKBEDIT_PRIORITY + TCA_SKBEDIT_QUEUE_MAPPING + TCA_SKBEDIT_MARK + TCA_SKBEDIT_PAD + TCA_SKBEDIT_PTYPE + TCA_SKBEDIT_MAX = TCA_SKBEDIT_MARK +) + +type TcSkbEdit struct { + TcGen +} + +func (x *TcSkbEdit) Len() int { + return SizeofTcSkbEdit +} + +func DeserializeSkbEdit(b []byte) *TcSkbEdit { + return (*TcSkbEdit)(unsafe.Pointer(&b[0:SizeofTcSkbEdit][0])) +} + +func (x *TcSkbEdit) Serialize() []byte { + return (*(*[SizeofTcSkbEdit]byte)(unsafe.Pointer(x)))[:] +} + // struct tc_police { // __u32 index; // int action;