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>
This commit is contained in:
Brenden Blanco
2016-08-25 14:08:23 -04:00
committed by Vish Ishaya
parent d710fbade4
commit fadc1088f6
6 changed files with 124 additions and 13 deletions

View File

@@ -8,11 +8,11 @@ package netlink
#include <stdint.h>
#include <unistd.h>
static int load_simple_bpf(int prog_type) {
static int load_simple_bpf(int prog_type, int ret) {
#ifdef __NR_bpf
// { return 1; }
// { return ret; }
__u64 __attribute__((aligned(8))) insns[] = {
0x00000001000000b7ull,
0x00000000000000b7ull | ((__u64)ret<<32),
0x0000000000000095ull,
};
__u8 __attribute__((aligned(8))) license[] = "ASL2";
@@ -51,10 +51,12 @@ const (
BPF_PROG_TYPE_KPROBE
BPF_PROG_TYPE_SCHED_CLS
BPF_PROG_TYPE_SCHED_ACT
BPF_PROG_TYPE_TRACEPOINT
BPF_PROG_TYPE_XDP
)
// loadSimpleBpf loads a trivial bpf program for testing purposes
func loadSimpleBpf(progType BpfProgType) (int, error) {
fd, err := C.load_simple_bpf(C.int(progType))
func loadSimpleBpf(progType BpfProgType, ret int) (int, error) {
fd, err := C.load_simple_bpf(C.int(progType), C.int(ret))
return int(fd), err
}

View File

@@ -290,7 +290,7 @@ func TestFilterU32BpfAddDel(t *testing.T) {
t.Fatal("Qdisc is the wrong type")
}
fd, err := loadSimpleBpf(BPF_PROG_TYPE_SCHED_ACT)
fd, err := loadSimpleBpf(BPF_PROG_TYPE_SCHED_ACT, 1)
if err != nil {
t.Skipf("Loading bpf program failed: %s", err)
}
@@ -411,7 +411,7 @@ func TestFilterClsActBpfAddDel(t *testing.T) {
Protocol: syscall.ETH_P_ALL,
Priority: 1,
}
fd, err := loadSimpleBpf(BPF_PROG_TYPE_SCHED_CLS)
fd, err := loadSimpleBpf(BPF_PROG_TYPE_SCHED_CLS, 1)
if err != nil {
t.Skipf("Loading bpf program failed: %s", err)
}

View File

@@ -32,6 +32,7 @@ type LinkAttrs struct {
Alias string
Statistics *LinkStatistics
Promisc int
Xdp *LinkXdp
}
// NewLinkAttrs returns LinkAttrs structure filled with default values
@@ -70,6 +71,11 @@ type LinkStatistics struct {
TxCompressed uint32
}
type LinkXdp struct {
Fd int
Attached bool
}
// Device links cannot be created via netlink. These links
// are links created by udev like 'lo' and 'etho0'
type Device struct {

View File

@@ -416,6 +416,23 @@ func (h *Handle) LinkSetNsFd(link Link, fd int) error {
return err
}
// LinkSetXdpFd adds a bpf function to the driver. The fd must be a bpf
// program loaded with bpf(type=BPF_PROG_TYPE_XDP)
func LinkSetXdpFd(link Link, fd int) error {
base := link.Attrs()
ensureIndex(base)
req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Index = int32(base.Index)
req.AddData(msg)
addXdpAttrs(&LinkXdp{Fd: fd}, req)
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
return err
}
func boolAttr(val bool) []byte {
var v uint8
if val {
@@ -693,6 +710,10 @@ func (h *Handle) LinkAdd(link Link) error {
req.AddData(attr)
}
if base.Xdp != nil {
addXdpAttrs(base.Xdp, req)
}
linkInfo := nl.NewRtAttr(syscall.IFLA_LINKINFO, nil)
nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_KIND, nl.NonZeroTerminated(link.Type()))
@@ -1005,6 +1026,12 @@ func linkDeserialize(m []byte) (Link, error) {
base.Alias = string(attr.Value[:len(attr.Value)-1])
case syscall.IFLA_STATS:
base.Statistics = parseLinkStats(attr.Value[:])
case nl.IFLA_XDP:
xdp, err := parseLinkXdp(attr.Value[:])
if err != nil {
return nil, err
}
base.Xdp = xdp
}
}
// Links that don't have IFLA_INFO_KIND are hardware devices
@@ -1436,3 +1463,28 @@ func parseGretapData(link Link, data []syscall.NetlinkRouteAttr) {
func parseLinkStats(data []byte) *LinkStatistics {
return (*LinkStatistics)(unsafe.Pointer(&data[0:SizeofLinkStats][0]))
}
func addXdpAttrs(xdp *LinkXdp, req *nl.NetlinkRequest) {
attrs := nl.NewRtAttr(nl.IFLA_XDP|syscall.NLA_F_NESTED, nil)
b := make([]byte, 4)
native.PutUint32(b, uint32(xdp.Fd))
nl.NewRtAttrChild(attrs, nl.IFLA_XDP_FD, b)
req.AddData(attrs)
}
func parseLinkXdp(data []byte) (*LinkXdp, error) {
attrs, err := nl.ParseRouteAttr(data)
if err != nil {
return nil, err
}
xdp := &LinkXdp{}
for _, attr := range attrs {
switch attr.Attr.Type {
case nl.IFLA_XDP_FD:
xdp.Fd = int(native.Uint32(attr.Value[0:4]))
case nl.IFLA_XDP_ATTACHED:
xdp.Attached = attr.Value[0] != 0
}
}
return xdp, nil
}

View File

@@ -942,3 +942,30 @@ func TestLinkStats(t *testing.T) {
t.Fatalf("veth ends counters differ:\n%v\n%v", v0Stats, v1Stats)
}
}
func TestLinkXdp(t *testing.T) {
links, err := LinkList()
if err != nil {
t.Fatal(err)
}
var testXdpLink Link
for _, link := range links {
if link.Attrs().Xdp != nil && !link.Attrs().Xdp.Attached {
testXdpLink = link
break
}
}
if testXdpLink == nil {
t.Skipf("No link supporting XDP found")
}
fd, err := loadSimpleBpf(BPF_PROG_TYPE_XDP, 2 /*XDP_PASS*/)
if err != nil {
t.Skipf("Loading bpf program failed: %s", err)
}
if err := LinkSetXdpFd(testXdpLink, fd); err != nil {
t.Fatal(err)
}
if err := LinkSetXdpFd(testXdpLink, -1); err != nil {
t.Fatal(err)
}
}

View File

@@ -1,13 +1,35 @@
package nl
import (
"syscall"
"unsafe"
)
const (
DEFAULT_CHANGE = 0xFFFFFFFF
// doesn't exist in syscall
IFLA_VFINFO_LIST = 0x16
IFLA_VFINFO_LIST = syscall.IFLA_IFALIAS + 1 + iota
IFLA_STATS64
IFLA_VF_PORTS
IFLA_PORT_SELF
IFLA_AF_SPEC
IFLA_GROUP
IFLA_NET_NS_FD
IFLA_EXT_MASK
IFLA_PROMISCUITY
IFLA_NUM_TX_QUEUES
IFLA_NUM_RX_QUEUES
IFLA_CARRIER
IFLA_PHYS_PORT_ID
IFLA_CARRIER_CHANGES
IFLA_PHYS_SWITCH_ID
IFLA_LINK_NETNSID
IFLA_PHYS_PORT_NAME
IFLA_PROTO_DOWN
IFLA_GSO_MAX_SEGS
IFLA_GSO_MAX_SIZE
IFLA_PAD
IFLA_XDP
)
const (
@@ -89,11 +111,6 @@ const (
IFLA_IPVLAN_MAX = IFLA_IPVLAN_MODE
)
const (
// not defined in syscall
IFLA_NET_NS_FD = 28
)
const (
IFLA_MACVLAN_UNSPEC = iota
IFLA_MACVLAN_MODE
@@ -394,3 +411,10 @@ func DeserializeVfRssQueryEn(b []byte) *VfRssQueryEn {
func (msg *VfRssQueryEn) Serialize() []byte {
return (*(*[SizeofVfRssQueryEn]byte)(unsafe.Pointer(msg)))[:]
}
const (
IFLA_XDP_UNSPEC = iota
IFLA_XDP_FD /* fd of xdp program to attach, or -1 to remove */
IFLA_XDP_ATTACHED /* read-only bool indicating if prog is attached */
IFLA_XDP_MAX = IFLA_XDP_ATTACHED
)