netlink/filter_test.go
Brenden Blanco fadc1088f6 Add support for xdp set/get of a bpf program (#156)
* Add netlink definitions for extra IFLAs
  The relevant IFLA_* are defined in the kernel but not in the syscall
  package.
* Parameterize the return value of loadSimpleBpf
  Allow the return value of the bpf program created by loadSimpleBpf to
  be specified by the caller. Before this, the value was hardcoded to 1.
* Add support for a new IFLA that enables using a bpf program as a
  filter early in the driver path of some NICs.
* Add a test for set/get of an xdp program. Since currently, the XDP
  IFLA is optional, check that the hardware supports it before trying to
  set the field.

Signed-off-by: Brenden Blanco <bblanco@plumgrid.com>
2016-08-25 11:08:23 -07:00

473 lines
9.5 KiB
Go

package netlink
import (
"syscall"
"testing"
)
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 := QdiscList(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")
}
filter := &U32{
FilterAttrs: FilterAttrs{
LinkIndex: link.Attrs().Index,
Parent: MakeHandle(0xffff, 0),
Priority: 1,
Protocol: syscall.ETH_P_IP,
},
RedirIndex: 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")
}
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 = QdiscList(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 := QdiscList(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: syscall.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")
}
for i := range fw.Rtab {
if fw.Rtab[i] != filter.Rtab[i] {
t.Fatal("Rtab doesn't match")
}
if fw.Ptab[i] != filter.Ptab[i] {
t.Fatal("Ptab 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 = QdiscList(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 := QdiscList(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)
}
filter := &U32{
FilterAttrs: FilterAttrs{
LinkIndex: link.Attrs().Index,
Parent: MakeHandle(0xffff, 0),
Priority: 1,
Protocol: syscall.ETH_P_ALL,
},
ClassId: MakeHandle(1, 1),
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")
}
bpfAction, ok := u32.Actions[0].(*BpfAction)
if !ok {
t.Fatal("Action[0] is the wrong type")
}
if bpfAction.Fd != fd {
t.Fatal("Action Fd does not match")
}
if _, ok := u32.Actions[1].(*MirredAction); !ok {
t.Fatal("Action[1] 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 = QdiscList(link)
if err != nil {
t.Fatal(err)
}
if len(qdiscs) != 0 {
t.Fatal("Failed to remove qdisc")
}
}
func TestFilterClsActBpfAddDel(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)
}
attrs := QdiscAttrs{
LinkIndex: link.Attrs().Index,
Handle: MakeHandle(0xffff, 0),
Parent: HANDLE_CLSACT,
}
qdisc := &GenericQdisc{
QdiscAttrs: attrs,
QdiscType: "clsact",
}
// This feature was added in kernel 4.5
if err := QdiscAdd(qdisc); err != nil {
t.Skipf("Failed adding clsact qdisc, unsupported kernel")
}
qdiscs, err := QdiscList(link)
if err != nil {
t.Fatal(err)
}
if len(qdiscs) != 1 {
t.Fatal("Failed to add qdisc")
}
if q, ok := qdiscs[0].(*GenericQdisc); !ok || q.Type() != "clsact" {
t.Fatal("qdisc is the wrong type")
}
filterattrs := FilterAttrs{
LinkIndex: link.Attrs().Index,
Parent: HANDLE_MIN_EGRESS,
Handle: MakeHandle(0, 1),
Protocol: syscall.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 = QdiscList(link)
if err != nil {
t.Fatal(err)
}
if len(qdiscs) != 0 {
t.Fatal("Failed to remove qdisc")
}
}