netlink/filter_test.go
Taku Fukushima dc00cf9d5c Add Hash to U32
This patch adds "Hash" attribute that represents the ID of the hash
table with which the filter is associated to U32 struct. If the hash
table is not created yet, a new hash table is created with the
specified ID.

Signed-off-by: Taku Fukushima <taku@soracom.jp>
2018-04-19 09:01:18 -07:00

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")
}
}