//go:build linux // +build linux package netlink import ( "net" "reflect" "testing" "time" "github.com/vishvananda/netlink/nl" "golang.org/x/sys/unix" ) func TestFilterAddDel(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) } if len(qdiscs) != 1 { t.Fatal("Failed to add qdisc") } _, ok := qdiscs[0].(*Ingress) if !ok { t.Fatal("Qdisc is the wrong type") } classId := MakeHandle(1, 1) filter := &U32{ FilterAttrs: FilterAttrs{ LinkIndex: link.Attrs().Index, Parent: MakeHandle(0xffff, 0), Priority: 1, Protocol: unix.ETH_P_IP, }, RedirIndex: redir.Attrs().Index, ClassId: classId, } 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 u32.ClassId != classId { t.Fatalf("ClassId of the filter is the wrong value") } 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) } if len(qdiscs) != 0 { t.Fatal("Failed to remove qdisc") } } func TestFilterReplace(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) } filter := &U32{ FilterAttrs: FilterAttrs{ LinkIndex: link.Attrs().Index, Parent: MakeHandle(0xffff, 0), Priority: 1, Protocol: unix.ETH_P_IP, }, RedirIndex: redir.Attrs().Index, ClassId: MakeHandle(1, 1), } if err := FilterReplace(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 replace filter") } if err := FilterReplace(filter); err != nil { t.Fatal(err) } } func TestAdvancedFilterAddDel(t *testing.T) { tearDown := setUpNetlinkTest(t) defer tearDown() if err := LinkAdd(&Ifb{LinkAttrs{Name: "baz"}}); err != nil { t.Fatal(err) } link, err := LinkByName("baz") if err != nil { t.Fatal(err) } if err := LinkSetUp(link); err != nil { t.Fatal(err) } index := link.Attrs().Index qdiscHandle := MakeHandle(0x1, 0x0) qdiscAttrs := QdiscAttrs{ LinkIndex: index, Handle: qdiscHandle, Parent: HANDLE_ROOT, } qdisc := NewHtb(qdiscAttrs) if err := QdiscAdd(qdisc); err != nil { t.Fatal(err) } qdiscs, err := SafeQdiscList(link) if err != nil { t.Fatal(err) } if len(qdiscs) != 1 { t.Fatal("Failed to add qdisc") } _, ok := qdiscs[0].(*Htb) if !ok { t.Fatal("Qdisc is the wrong type") } classId := MakeHandle(0x1, 0x46cb) classAttrs := ClassAttrs{ LinkIndex: index, Parent: qdiscHandle, Handle: classId, } htbClassAttrs := HtbClassAttrs{ Rate: 512 * 1024, Buffer: 32 * 1024, } htbClass := NewHtbClass(classAttrs, htbClassAttrs) if err = ClassReplace(htbClass); err != nil { t.Fatalf("Failed to add a HTB class: %v", err) } classes, err := SafeClassList(link, qdiscHandle) if err != nil { t.Fatal(err) } if len(classes) != 1 { t.Fatal("Failed to add class") } _, ok = classes[0].(*HtbClass) if !ok { t.Fatal("Class is the wrong type") } htid := MakeHandle(0x0010, 0000) divisor := uint32(1) hashTable := &U32{ FilterAttrs: FilterAttrs{ LinkIndex: index, Handle: htid, Parent: qdiscHandle, Priority: 1, Protocol: unix.ETH_P_ALL, }, Divisor: divisor, } cHashTable := *hashTable if err := FilterAdd(hashTable); err != nil { t.Fatal(err) } // Check if the hash table is identical before and after FilterAdd. if !reflect.DeepEqual(cHashTable, *hashTable) { t.Fatalf("Hash table %v and %v are not equal", cHashTable, *hashTable) } u32SelKeys := []TcU32Key{ { Mask: 0xff, Val: 80, Off: 20, OffMask: 0, }, { Mask: 0xffff, Val: 0x146ca, Off: 32, OffMask: 0, }, } handle := MakeHandle(0x0000, 0001) filter := &U32{ FilterAttrs: FilterAttrs{ LinkIndex: index, Handle: handle, Parent: qdiscHandle, Priority: 1, Protocol: unix.ETH_P_ALL, }, Sel: &TcU32Sel{ Keys: u32SelKeys, Flags: TC_U32_TERMINAL, }, ClassId: classId, Hash: htid, Actions: []Action{}, } // Copy filter. cFilter := *filter if err := FilterAdd(filter); err != nil { t.Fatal(err) } // Check if the filter is identical before and after FilterAdd. if !reflect.DeepEqual(cFilter, *filter) { t.Fatalf("U32 %v and %v are not equal", cFilter, *filter) } filters, err := FilterList(link, qdiscHandle) 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") } // Endianness checks if u32.Sel.Offmask != filter.Sel.Offmask { t.Fatal("The endianness of TcU32Key.Sel.Offmask is wrong") } if u32.Sel.Hmask != filter.Sel.Hmask { t.Fatal("The endianness of TcU32Key.Sel.Hmask is wrong") } for i, key := range u32.Sel.Keys { if key.Mask != filter.Sel.Keys[i].Mask { t.Fatal("The endianness of TcU32Key.Mask is wrong") } if key.Val != filter.Sel.Keys[i].Val { t.Fatal("The endianness of TcU32Key.Val is wrong") } } if u32.Handle != (handle | htid) { t.Fatalf("The handle is wrong. expected %v but actually %v", (handle | htid), u32.Handle) } if u32.Hash != htid { t.Fatal("The hash table ID is wrong") } if err := FilterDel(u32); err != nil { t.Fatal(err) } filters, err = FilterList(link, qdiscHandle) if err != nil { t.Fatal(err) } if len(filters) != 0 { t.Fatal("Failed to remove filter") } if err = ClassDel(htbClass); err != nil { t.Fatalf("Failed to delete a HTP class: %v", err) } classes, err = SafeClassList(link, qdiscHandle) if err != nil { t.Fatal(err) } if len(classes) != 0 { t.Fatal("Failed to remove class") } if err := QdiscDel(qdisc); err != nil { t.Fatal(err) } qdiscs, err = SafeQdiscList(link) if err != nil { t.Fatal(err) } if len(qdiscs) != 0 { t.Fatal("Failed to remove qdisc") } } func TestFilterFwAddDel(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) } attrs := QdiscAttrs{ LinkIndex: link.Attrs().Index, Handle: MakeHandle(0xffff, 0), Parent: HANDLE_ROOT, } qdisc := NewHtb(attrs) if err := QdiscAdd(qdisc); err != nil { t.Fatal(err) } qdiscs, err := SafeQdiscList(link) if err != nil { t.Fatal(err) } if len(qdiscs) != 1 { t.Fatal("Failed to add qdisc") } _, ok := qdiscs[0].(*Htb) if !ok { t.Fatal("Qdisc is the wrong type") } classattrs := ClassAttrs{ LinkIndex: link.Attrs().Index, Parent: MakeHandle(0xffff, 0), Handle: MakeHandle(0xffff, 2), } htbclassattrs := HtbClassAttrs{ Rate: 1234000, Cbuffer: 1690, } class := NewHtbClass(classattrs, htbclassattrs) if err := ClassAdd(class); err != nil { t.Fatal(err) } classes, err := SafeClassList(link, MakeHandle(0xffff, 2)) if err != nil { t.Fatal(err) } if len(classes) != 1 { t.Fatal("Failed to add class") } police := NewPoliceAction() police.Burst = 12345 police.Rate = 1234 police.PeakRate = 2345 police.Action = TcAct(TC_POLICE_SHOT) filterattrs := FilterAttrs{ LinkIndex: link.Attrs().Index, Parent: MakeHandle(0xffff, 0), Handle: MakeHandle(0, 0x6), Priority: 1, Protocol: unix.ETH_P_IP, } filter := FwFilter{ FilterAttrs: filterattrs, ClassId: MakeHandle(0xffff, 2), Police: police, } 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") } fw, ok := filters[0].(*FwFilter) if !ok { t.Fatal("Filter is the wrong type") } if fw.Police.Rate != filter.Police.Rate { t.Fatal("Police Rate doesn't match") } if fw.ClassId != filter.ClassId { t.Fatal("ClassId doesn't match") } if fw.InDev != filter.InDev { t.Fatal("InDev doesn't match") } if fw.Police.AvRate != filter.Police.AvRate { t.Fatal("AvRate doesn't match") } 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 := ClassDel(class); err != nil { t.Fatal(err) } classes, err = SafeClassList(link, MakeHandle(0xffff, 0)) if err != nil { t.Fatal(err) } if len(classes) != 0 { t.Fatal("Failed to remove class") } if err := QdiscDel(qdisc); err != nil { t.Fatal(err) } qdiscs, err = SafeQdiscList(link) if err != nil { t.Fatal(err) } if len(qdiscs) != 0 { t.Fatal("Failed to remove qdisc") } } func TestFilterFwActAddDel(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) } if len(qdiscs) != 1 { t.Fatal("Failed to add qdisc") } _, ok := qdiscs[0].(*Ingress) if !ok { t.Fatal("Qdisc is the wrong type") } classId := MakeHandle(1, 1) filter := &FwFilter{ FilterAttrs: FilterAttrs{ LinkIndex: link.Attrs().Index, Parent: MakeHandle(0xffff, 0), Priority: 1, Protocol: unix.ETH_P_ALL, Handle: MakeHandle(0, 0x6), }, ClassId: classId, Actions: []Action{ &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") } fw, ok := filters[0].(*FwFilter) if !ok { t.Fatal("Filter is the wrong type") } if len(fw.Actions) != 1 { t.Fatalf("Too few Actions in filter") } if fw.ClassId != classId { t.Fatalf("ClassId of the filter is the wrong value") } mia, ok := fw.Actions[0].(*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.MirredAction != TCA_EGRESS_REDIR { t.Fatal("MirredAction isn't TCA_EGRESS_REDIR") } if mia.Ifindex != redir.Attrs().Index { t.Fatal("Unmatched redirect index") } 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) } if len(qdiscs) != 0 { t.Fatal("Failed to remove qdisc") } } func TestFilterU32BpfAddDel(t *testing.T) { t.Skipf("Fd does not match in ci") 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) } if len(qdiscs) != 1 { t.Fatal("Failed to add qdisc") } _, ok := qdiscs[0].(*Ingress) if !ok { t.Fatal("Qdisc is the wrong type") } fd, err := loadSimpleBpf(BPF_PROG_TYPE_SCHED_ACT, 1) if err != nil { t.Skipf("Loading bpf program failed: %s", err) } 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{ &BpfAction{Fd: fd, Name: "simple"}, &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 bpfAction, ok := u32.Actions[0].(*BpfAction) if !ok { bpfAction, ok = u32.Actions[1].(*BpfAction) if !ok { t.Fatal("Action is the wrong type") } } if bpfAction.Fd != fd { t.Fatalf("Action Fd does not match %d != %d", bpfAction.Fd, fd) } if _, ok := u32.Actions[0].(*MirredAction); !ok { if _, ok := u32.Actions[1].(*MirredAction); !ok { t.Fatal("Action is the wrong type") } } 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) } if len(qdiscs) != 0 { t.Fatal("Failed to remove qdisc") } } func TestFilterU32ConnmarkAddDel(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) } if len(qdiscs) != 1 { t.Fatal("Failed to add qdisc") } _, ok := qdiscs[0].(*Ingress) if !ok { t.Fatal("Qdisc is the wrong type") } 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{ &ConnmarkAction{ ActionAttrs: ActionAttrs{ Action: TC_ACT_PIPE, }, }, &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 cma, ok := u32.Actions[0].(*ConnmarkAction) if !ok { cma, ok = u32.Actions[1].(*ConnmarkAction) if !ok { t.Fatal("Unable to find connmark action") } } if cma.Attrs().Action != TC_ACT_PIPE { t.Fatal("Connmark action isn't TC_ACT_PIPE") } 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) } if len(qdiscs) != 0 { t.Fatal("Failed to remove qdisc") } } func TestFilterU32CsumAddDel(t *testing.T) { tearDown := setUpNetlinkTest(t) defer tearDown() if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil { t.Fatalf("add link foo error: %v", err) } link, err := LinkByName("foo") if err != nil { t.Fatalf("add link foo error: %v", err) } if err := LinkSetUp(link); err != nil { t.Fatalf("set foo link up error: %v", 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.Fatalf("get qdisc error: %v", err) } found := false for _, v := range qdiscs { if _, ok := v.(*Ingress); ok { found = true break } } if !found { t.Fatal("Qdisc is the wrong type") } 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{ &CsumAction{ ActionAttrs: ActionAttrs{ Action: TC_ACT_PIPE, }, UpdateFlags: TCA_CSUM_UPDATE_FLAG_TCP, }, }, } if err := FilterAdd(filter); err != nil { t.Fatal(err) } filters, err := FilterList(link, MakeHandle(0xffff, 0)) if err != nil { t.Fatalf("get filter error: %v", err) } if len(filters) != 1 { t.Fatalf("the count filters error, expect: 1, acutal: %d", len(filters)) } ft, ok := filters[0].(*U32) if !ok { t.Fatal("Filter is the wrong type") } if ft.LinkIndex != link.Attrs().Index { t.Fatal("link index error") } if len(ft.Actions) != 1 { t.Fatalf("filter has wrong number of actions, expect: 1, acutal: %d", len(filters)) } csum, ok := ft.Actions[0].(*CsumAction) if !ok { t.Fatal("action is the wrong type") } if csum.Attrs().Action != TC_ACT_PIPE { t.Fatal("Csum action isn't TC_ACT_PIPE") } if csum.UpdateFlags != TCA_CSUM_UPDATE_FLAG_TCP { t.Fatalf("Csum action isn't TCA_CSUM_UPDATE_FLAG_TCP, got %d", csum.UpdateFlags) } if err := FilterDel(ft); 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") } } func setupLinkForTestWithQdisc(t *testing.T, linkName string) (Qdisc, Link) { if err := LinkAdd(&Ifb{LinkAttrs{Name: linkName}}); err != nil { t.Fatal(err) } link, err := LinkByName(linkName) if err != nil { t.Fatal(err) } if err := LinkSetUp(link); err != nil { t.Fatal(err) } qdisc := &Clsact{ QdiscAttrs: QdiscAttrs{ LinkIndex: link.Attrs().Index, Handle: MakeHandle(0xffff, 0), Parent: HANDLE_CLSACT, }, } if err := QdiscAdd(qdisc); err != nil { t.Fatal(err) } qdiscs, err := SafeQdiscList(link) if err != nil { t.Fatal(err) } if len(qdiscs) != 1 { t.Fatal("Failed to add qdisc", len(qdiscs)) } if q, ok := qdiscs[0].(*Clsact); !ok || q.Type() != "clsact" { t.Fatal("qdisc is the wrong type") } return qdiscs[0], link } func TestFilterClsActBpfAddDel(t *testing.T) { t.Skipf("Fd does not match in ci") // This feature was added in kernel 4.5 minKernelRequired(t, 4, 5) tearDown := setUpNetlinkTest(t) defer tearDown() qdisc, link := setupLinkForTestWithQdisc(t, "foo") filterattrs := FilterAttrs{ LinkIndex: link.Attrs().Index, Parent: HANDLE_MIN_EGRESS, Handle: MakeHandle(0, 1), Protocol: unix.ETH_P_ALL, Priority: 1, } fd, err := loadSimpleBpf(BPF_PROG_TYPE_SCHED_CLS, 1) if err != nil { t.Skipf("Loading bpf program failed: %s", err) } filter := &BpfFilter{ FilterAttrs: filterattrs, Fd: fd, Name: "simple", DirectAction: true, } if filter.Fd < 0 { t.Skipf("Failed to load bpf program") } if err := FilterAdd(filter); err != nil { t.Fatal(err) } filters, err := FilterList(link, HANDLE_MIN_EGRESS) if err != nil { t.Fatal(err) } if len(filters) != 1 { t.Fatal("Failed to add filter") } bpf, ok := filters[0].(*BpfFilter) if !ok { t.Fatal("Filter is the wrong type") } if bpf.Fd != filter.Fd { t.Fatal("Filter Fd does not match") } if bpf.DirectAction != filter.DirectAction { t.Fatal("Filter DirectAction does not match") } if err := FilterDel(filter); err != nil { t.Fatal(err) } filters, err = FilterList(link, HANDLE_MIN_EGRESS) 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) } if len(qdiscs) != 0 { t.Fatal("Failed to remove qdisc") } } func TestFilterMatchAllAddDel(t *testing.T) { // This classifier was added in kernel 4.7 minKernelRequired(t, 4, 7) tearDown := setUpNetlinkTest(t) defer tearDown() _, link := setupLinkForTestWithQdisc(t, "foo") _, link2 := setupLinkForTestWithQdisc(t, "bar") filter := &MatchAll{ FilterAttrs: FilterAttrs{ LinkIndex: link.Attrs().Index, Parent: HANDLE_MIN_EGRESS, Priority: 32000, Protocol: unix.ETH_P_ALL, }, Actions: []Action{ &MirredAction{ ActionAttrs: ActionAttrs{ Action: TC_ACT_STOLEN, }, MirredAction: TCA_EGRESS_REDIR, Ifindex: link2.Attrs().Index, }, }, } if err := FilterAdd(filter); err != nil { t.Fatal(err) } filters, err := FilterList(link, HANDLE_MIN_EGRESS) if err != nil { t.Fatal(err) } if len(filters) != 1 { t.Fatal("Failed to add filter") } matchall, ok := filters[0].(*MatchAll) if !ok { t.Fatal("Filter is the wrong type") } if matchall.Priority != 32000 { t.Fatal("Filter priority does not match") } if len(matchall.Actions) != 1 { t.Fatal("Filter has no actions") } mirredAction, ok := matchall.Actions[0].(*MirredAction) if !ok { t.Fatal("Action does not match") } if mirredAction.Ifindex != link2.Attrs().Index { t.Fatal("Action ifindex does not match") } if err := FilterDel(filter); err != nil { t.Fatal(err) } filters, err = FilterList(link, HANDLE_MIN_EGRESS) if err != nil { t.Fatal(err) } if len(filters) != 0 { t.Fatal("Failed to remove filter") } } func TestFilterU32TunnelKeyAddDel(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") } tunnelAct := NewTunnelKeyAction() tunnelAct.SrcAddr = net.IPv4(10, 10, 10, 1) tunnelAct.DstAddr = net.IPv4(10, 10, 10, 2) tunnelAct.KeyID = 0x01 tunnelAct.Action = TCA_TUNNEL_KEY_SET tunnelAct.DestPort = 8472 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{ tunnelAct, &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 tun, ok := u32.Actions[0].(*TunnelKeyAction) if !ok { tun, ok = u32.Actions[1].(*TunnelKeyAction) if !ok { t.Fatal("Unable to find tunnel action") } } if tun.Attrs().Action != TC_ACT_PIPE { t.Fatal("TunnelKey action isn't TC_ACT_PIPE") } if !tun.SrcAddr.Equal(tunnelAct.SrcAddr) { t.Fatal("Action SrcAddr doesn't match") } if !tun.DstAddr.Equal(tunnelAct.DstAddr) { t.Fatal("Action DstAddr doesn't match") } if tun.KeyID != tunnelAct.KeyID { t.Fatal("Action KeyID doesn't match") } if tun.DestPort != tunnelAct.DestPort { t.Fatal("Action DestPort doesn't match") } if tun.Action != tunnelAct.Action { t.Fatal("Action 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") } } 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 mask := uint32(0xff) skbedit.Mask = &mask 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.Mask == nil || *edit.Mask != *skbedit.Mask { t.Fatal("Action Mask 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") } } func TestFilterU32LinkOption(t *testing.T) { tearDown := setUpNetlinkTest(t) defer tearDown() if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil { t.Fatalf("add link foo error: %v", err) } link, err := LinkByName("foo") if err != nil { t.Fatalf("add link foo error: %v", err) } if err := LinkSetUp(link); err != nil { t.Fatalf("set foo link up error: %v", 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.Fatalf("get qdisc error: %v", err) } found := false for _, v := range qdiscs { if _, ok := v.(*Ingress); ok { found = true break } } if !found { t.Fatal("Qdisc is the wrong type") } htid := uint32(10) size := uint32(8) priority := uint16(200) u32Table := &U32{ FilterAttrs: FilterAttrs{ LinkIndex: link.Attrs().Index, Handle: htid << 20, Parent: MakeHandle(0xffff, 0), Priority: priority, Protocol: unix.ETH_P_ALL, }, Divisor: size, } if err := FilterAdd(u32Table); err != nil { t.Fatal(err) } u32 := &U32{ FilterAttrs: FilterAttrs{ LinkIndex: link.Attrs().Index, Parent: MakeHandle(0xffff, 0), Handle: 1, Priority: priority, Protocol: unix.ETH_P_ALL, }, Link: uint32(htid << 20), Sel: &TcU32Sel{ Nkeys: 1, Flags: TC_U32_TERMINAL | TC_U32_VAROFFSET, Hmask: 0x0000ff00, Hoff: 0, Offshift: 8, Keys: []TcU32Key{ { Mask: 0, Val: 0, Off: 0, }, }, }, } if err := FilterAdd(u32); err != nil { t.Fatal(err) } filters, err := FilterList(link, MakeHandle(0xffff, 0)) if err != nil { t.Fatalf("get filter error: %v", err) } if len(filters) != 1 { t.Fatalf("the count filters error, expect: 1, acutal: %d", len(filters)) } ft, ok := filters[0].(*U32) if !ok { t.Fatal("Filter is the wrong type") } if ft.LinkIndex != link.Attrs().Index { t.Fatal("link index error") } if ft.Link != htid<<20 { t.Fatal("hash table id error") } if ft.Priority != priority { t.Fatal("priority error") } if err := FilterDel(ft); 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") } } func TestFilterFlowerAddDel(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") } testMask := net.CIDRMask(24, 32) ipproto := new(nl.IPProto) *ipproto = nl.IPPROTO_TCP filter := &Flower{ FilterAttrs: FilterAttrs{ LinkIndex: link.Attrs().Index, Parent: MakeHandle(0xffff, 0), Priority: 1, Protocol: unix.ETH_P_ALL, }, DestIP: net.ParseIP("1.0.0.1"), DestIPMask: testMask, SrcIP: net.ParseIP("2.0.0.1"), SrcIPMask: testMask, EthType: unix.ETH_P_IP, EncDestIP: net.ParseIP("3.0.0.1"), EncDestIPMask: testMask, EncSrcIP: net.ParseIP("4.0.0.1"), EncSrcIPMask: testMask, EncDestPort: 8472, EncKeyId: 1234, IPProto: ipproto, DestPort: 1111, SrcPort: 1111, Actions: []Action{ &MirredAction{ ActionAttrs: ActionAttrs{ Action: TC_ACT_STOLEN, }, MirredAction: TCA_EGRESS_REDIR, Ifindex: redir.Attrs().Index, }, &GenericAction{ ActionAttrs: ActionAttrs{ Action: getTcActGotoChain(), }, Chain: 20, }, }, } if err := FilterAdd(filter); err != nil { t.Fatal(err) } time.Sleep(time.Second) filters, err := FilterList(link, MakeHandle(0xffff, 0)) if err != nil { t.Fatal(err) } if len(filters) != 1 { t.Fatal("Failed to add filter") } flower, ok := filters[0].(*Flower) if !ok { t.Fatal("Filter is the wrong type") } if filter.EthType != flower.EthType { t.Fatalf("Flower EthType doesn't match") } if !filter.DestIP.Equal(flower.DestIP) { t.Fatalf("Flower DestIP doesn't match") } if !filter.SrcIP.Equal(flower.SrcIP) { t.Fatalf("Flower SrcIP doesn't match") } if !reflect.DeepEqual(filter.DestIPMask, testMask) { t.Fatalf("Flower DestIPMask doesn't match") } if !reflect.DeepEqual(filter.SrcIPMask, testMask) { t.Fatalf("Flower SrcIPMask doesn't match") } if !filter.EncDestIP.Equal(flower.EncDestIP) { t.Fatalf("Flower EncDestIP doesn't match") } if !filter.EncSrcIP.Equal(flower.EncSrcIP) { t.Fatalf("Flower EncSrcIP doesn't match") } if !reflect.DeepEqual(filter.EncDestIPMask, testMask) { t.Fatalf("Flower EncDestIPMask doesn't match") } if !reflect.DeepEqual(filter.EncSrcIPMask, testMask) { t.Fatalf("Flower EncSrcIPMask doesn't match") } if filter.EncKeyId != flower.EncKeyId { t.Fatalf("Flower EncKeyId doesn't match") } if filter.EncDestPort != flower.EncDestPort { t.Fatalf("Flower EncDestPort doesn't match") } if flower.IPProto == nil || *filter.IPProto != *flower.IPProto { t.Fatalf("Flower IPProto doesn't match") } if filter.DestPort != flower.DestPort { t.Fatalf("Flower DestPort doesn't match") } if filter.SrcPort != flower.SrcPort { t.Fatalf("Flower SrcPort doesn't match") } mia, ok := flower.Actions[0].(*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.Timestamp == nil || mia.Timestamp.Installed == 0 { t.Fatal("Incorrect mirred action timestamp") } if mia.Statistics == nil { t.Fatal("Incorrect mirred action stats") } ga, ok := flower.Actions[1].(*GenericAction) if !ok { t.Fatal("Unable to find generic action") } if ga.Attrs().Action != getTcActGotoChain() { t.Fatal("Generic action isn't TC_ACT_GOTO_CHAIN") } if ga.Timestamp == nil || ga.Timestamp.Installed == 0 { t.Fatal("Incorrect generic action timestamp") } if ga.Statistics == nil { t.Fatal("Incorrect generic action stats") } 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") } } func TestFilterIPv6FlowerPedit(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") } testMask := net.CIDRMask(64, 128) ipproto := new(nl.IPProto) *ipproto = nl.IPPROTO_TCP filter := &Flower{ FilterAttrs: FilterAttrs{ LinkIndex: link.Attrs().Index, Parent: MakeHandle(0xffff, 0), Priority: 1, Protocol: unix.ETH_P_ALL, }, DestIP: net.ParseIP("ffff::fff1"), DestIPMask: testMask, EthType: unix.ETH_P_IPV6, IPProto: ipproto, DestPort: 6666, Actions: []Action{}, } peditAction := NewPeditAction() peditAction.Proto = uint8(nl.IPPROTO_TCP) peditAction.SrcPort = 7777 peditAction.SrcIP = net.ParseIP("ffff::fff2") filter.Actions = append(filter.Actions, peditAction) miaAction := &MirredAction{ ActionAttrs: ActionAttrs{ Action: TC_ACT_REDIRECT, }, MirredAction: TCA_EGRESS_REDIR, Ifindex: redir.Attrs().Index, } filter.Actions = append(filter.Actions, miaAction) 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") } flower, ok := filters[0].(*Flower) if !ok { t.Fatal("Filter is the wrong type") } if filter.EthType != flower.EthType { t.Fatalf("Flower EthType doesn't match") } if !filter.DestIP.Equal(flower.DestIP) { t.Fatalf("Flower DestIP doesn't match") } if !reflect.DeepEqual(filter.DestIPMask, testMask) { t.Fatalf("Flower DestIPMask doesn't match") } if flower.IPProto == nil || *filter.IPProto != *flower.IPProto { t.Fatalf("Flower IPProto doesn't match") } if filter.DestPort != flower.DestPort { t.Fatalf("Flower DestPort doesn't match") } _, ok = flower.Actions[0].(*PeditAction) if !ok { t.Fatal("Unable to find pedit action") } _, ok = flower.Actions[1].(*MirredAction) if !ok { t.Fatal("Unable to find mirred action") } 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") } } func TestFilterU32PoliceAddDel(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") } 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, }, 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 p, ok := u32.Actions[0].(*PoliceAction) if !ok { 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.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.PeakRate != policePeakRate { t.Fatal("Action PeakRate doesn't match") } if p.LinkLayer != nl.LINKLAYER_ETHERNET { t.Fatal("Action LinkLayer 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") } } func TestFilterU32DirectPoliceAddDel(t *testing.T) { tearDown := setUpNetlinkTest(t) defer tearDown() if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil { t.Fatal(err) } link, err := LinkByName("foo") if err != nil { t.Fatal(err) } if err := LinkSetUp(link); 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) } 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, Police: police, } 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 u32.Police == nil { t.Fatalf("No police in filter") } if u32.Police.Rate != policeRate { t.Fatal("Filter Rate doesn't match") } if u32.Police.PeakRate != policePeakRate { t.Fatal("Filter PeakRate doesn't match") } if u32.Police.LinkLayer != nl.LINKLAYER_ETHERNET { t.Fatal("Filter LinkLayer doesn't match") } 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") } } func TestFilterChainAddDel(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) } if len(qdiscs) != 1 { t.Fatal("Failed to add qdisc") } _, ok := qdiscs[0].(*Ingress) if !ok { t.Fatal("Qdisc is the wrong type") } classId := MakeHandle(1, 1) chainVal := new(uint32) *chainVal = 20 filter := &U32{ FilterAttrs: FilterAttrs{ LinkIndex: link.Attrs().Index, Parent: MakeHandle(0xffff, 0), Priority: 1, Protocol: unix.ETH_P_IP, Chain: chainVal, }, RedirIndex: redir.Attrs().Index, ClassId: classId, } 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") } filterChain := filters[0].Attrs().Chain if filterChain != nil && *filterChain != *chainVal { t.Fatalf("Chain of the filter is the wrong value") } 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) } if len(qdiscs) != 0 { t.Fatal("Failed to remove qdisc") } }