netlink/filter_test.go

763 lines
16 KiB
Go

// +build linux
package netlink
import (
"reflect"
"testing"
"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 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 := ClassList(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 = ClassList(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 := ClassList(link, MakeHandle(0xffff, 2))
if err != nil {
t.Fatal(err)
}
if len(classes) != 1 {
t.Fatal("Failed to add class")
}
filterattrs := FilterAttrs{
LinkIndex: link.Attrs().Index,
Parent: MakeHandle(0xffff, 0),
Handle: MakeHandle(0, 0x6),
Priority: 1,
Protocol: unix.ETH_P_IP,
}
fwattrs := FilterFwAttrs{
Buffer: 12345,
Rate: 1234,
PeakRate: 2345,
Action: TC_POLICE_SHOT,
ClassId: MakeHandle(0xffff, 2),
}
filter, err := NewFw(filterattrs, fwattrs)
if err != nil {
t.Fatal(err)
}
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].(*Fw)
if !ok {
t.Fatal("Filter is the wrong type")
}
if fw.Police.Rate.Rate != filter.Police.Rate.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.AvRate != filter.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 = ClassList(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 TestFilterU32BpfAddDel(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")
}
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.Fatal("Action Fd does not match")
}
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 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)
}
attrs := QdiscAttrs{
LinkIndex: link.Attrs().Index,
Handle: MakeHandle(0xffff, 0),
Parent: HANDLE_CLSACT,
}
qdisc := &GenericQdisc{
QdiscAttrs: attrs,
QdiscType: "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].(*GenericQdisc); !ok || q.Type() != "clsact" {
t.Fatal("qdisc is the wrong type")
}
return qdiscs[0], link
}
func TestFilterClsActBpfAddDel(t *testing.T) {
// 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")
}
}