mirror of https://github.com/vishvananda/netlink
lwtunnel: add support for encap type bpf
add bpf program types add unit tests don't use strings to determine type fix camel case
This commit is contained in:
parent
53455394aa
commit
079db23e21
24
bpf_linux.go
24
bpf_linux.go
|
@ -16,6 +16,30 @@ const (
|
|||
BPF_PROG_TYPE_SCHED_ACT
|
||||
BPF_PROG_TYPE_TRACEPOINT
|
||||
BPF_PROG_TYPE_XDP
|
||||
BPF_PROG_TYPE_PERF_EVENT
|
||||
BPF_PROG_TYPE_CGROUP_SKB
|
||||
BPF_PROG_TYPE_CGROUP_SOCK
|
||||
BPF_PROG_TYPE_LWT_IN
|
||||
BPF_PROG_TYPE_LWT_OUT
|
||||
BPF_PROG_TYPE_LWT_XMIT
|
||||
BPF_PROG_TYPE_SOCK_OPS
|
||||
BPF_PROG_TYPE_SK_SKB
|
||||
BPF_PROG_TYPE_CGROUP_DEVICE
|
||||
BPF_PROG_TYPE_SK_MSG
|
||||
BPF_PROG_TYPE_RAW_TRACEPOINT
|
||||
BPF_PROG_TYPE_CGROUP_SOCK_ADDR
|
||||
BPF_PROG_TYPE_LWT_SEG6LOCAL
|
||||
BPF_PROG_TYPE_LIRC_MODE2
|
||||
BPF_PROG_TYPE_SK_REUSEPORT
|
||||
BPF_PROG_TYPE_FLOW_DISSECTOR
|
||||
BPF_PROG_TYPE_CGROUP_SYSCTL
|
||||
BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE
|
||||
BPF_PROG_TYPE_CGROUP_SOCKOPT
|
||||
BPF_PROG_TYPE_TRACING
|
||||
BPF_PROG_TYPE_STRUCT_OPS
|
||||
BPF_PROG_TYPE_EXT
|
||||
BPF_PROG_TYPE_LSM
|
||||
BPF_PROG_TYPE_SK_LOOKUP
|
||||
)
|
||||
|
||||
type BPFAttr struct {
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package nl
|
||||
|
||||
const (
|
||||
LWT_BPF_PROG_UNSPEC = iota
|
||||
LWT_BPF_PROG_FD
|
||||
LWT_BPF_PROG_NAME
|
||||
__LWT_BPF_PROG_MAX
|
||||
)
|
||||
|
||||
const (
|
||||
LWT_BPF_PROG_MAX = __LWT_BPF_PROG_MAX - 1
|
||||
)
|
||||
|
||||
const (
|
||||
LWT_BPF_UNSPEC = iota
|
||||
LWT_BPF_IN
|
||||
LWT_BPF_OUT
|
||||
LWT_BPF_XMIT
|
||||
LWT_BPF_XMIT_HEADROOM
|
||||
__LWT_BPF_MAX
|
||||
)
|
||||
|
||||
const (
|
||||
LWT_BPF_MAX = __LWT_BPF_MAX - 1
|
||||
)
|
||||
|
||||
const (
|
||||
LWT_BPF_MAX_HEADROOM = 256
|
||||
)
|
157
route_linux.go
157
route_linux.go
|
@ -460,6 +460,152 @@ func (e *SEG6LocalEncap) Equal(x Encap) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// Encap BPF definitions
|
||||
type bpfObj struct {
|
||||
progFd int
|
||||
progName string
|
||||
}
|
||||
type BpfEncap struct {
|
||||
progs [nl.LWT_BPF_MAX]bpfObj
|
||||
headroom int
|
||||
}
|
||||
|
||||
// SetProg adds a bpf function to the route via netlink RTA_ENCAP. The fd must be a bpf
|
||||
// program loaded with bpf(type=BPF_PROG_TYPE_LWT_*) matching the direction the program should
|
||||
// be applied to (LWT_BPF_IN, LWT_BPF_OUT, LWT_BPF_XMIT).
|
||||
func (e *BpfEncap) SetProg(mode, progFd int, progName string) error {
|
||||
if progFd <= 0 {
|
||||
return fmt.Errorf("lwt bpf SetProg: invalid fd")
|
||||
}
|
||||
if mode <= nl.LWT_BPF_UNSPEC || mode >= nl.LWT_BPF_XMIT_HEADROOM {
|
||||
return fmt.Errorf("lwt bpf SetProg:invalid mode")
|
||||
}
|
||||
e.progs[mode].progFd = progFd
|
||||
e.progs[mode].progName = fmt.Sprintf("%s[fd:%d]", progName, progFd)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetXmitHeadroom sets the xmit headroom (LWT_BPF_MAX_HEADROOM) via netlink RTA_ENCAP.
|
||||
// maximum headroom is LWT_BPF_MAX_HEADROOM
|
||||
func (e *BpfEncap) SetXmitHeadroom(headroom int) error {
|
||||
if headroom > nl.LWT_BPF_MAX_HEADROOM || headroom < 0 {
|
||||
return fmt.Errorf("invalid headroom size. range is 0 - %d", nl.LWT_BPF_MAX_HEADROOM)
|
||||
}
|
||||
e.headroom = headroom
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *BpfEncap) Type() int {
|
||||
return nl.LWTUNNEL_ENCAP_BPF
|
||||
}
|
||||
func (e *BpfEncap) Decode(buf []byte) error {
|
||||
if len(buf) < 4 {
|
||||
return fmt.Errorf("lwt bpf decode: lack of bytes")
|
||||
}
|
||||
native := nl.NativeEndian()
|
||||
attrs, err := nl.ParseRouteAttr(buf)
|
||||
if err != nil {
|
||||
return fmt.Errorf("lwt bpf decode: failed parsing attribute. err: %v", err)
|
||||
}
|
||||
for _, attr := range attrs {
|
||||
if int(attr.Attr.Type) < 1 {
|
||||
// nl.LWT_BPF_UNSPEC
|
||||
continue
|
||||
}
|
||||
if int(attr.Attr.Type) > nl.LWT_BPF_MAX {
|
||||
return fmt.Errorf("lwt bpf decode: received unknown attribute type: %d", attr.Attr.Type)
|
||||
}
|
||||
switch int(attr.Attr.Type) {
|
||||
case nl.LWT_BPF_MAX_HEADROOM:
|
||||
e.headroom = int(native.Uint32(attr.Value))
|
||||
default:
|
||||
bpfO := bpfObj{}
|
||||
parsedAttrs, err := nl.ParseRouteAttr(attr.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("lwt bpf decode: failed parsing route attribute")
|
||||
}
|
||||
for _, parsedAttr := range parsedAttrs {
|
||||
switch int(parsedAttr.Attr.Type) {
|
||||
case nl.LWT_BPF_PROG_FD:
|
||||
bpfO.progFd = int(native.Uint32(parsedAttr.Value))
|
||||
case nl.LWT_BPF_PROG_NAME:
|
||||
bpfO.progName = fmt.Sprintf("%s", parsedAttr.Value)
|
||||
default:
|
||||
return fmt.Errorf("lwt bpf decode: received unknown attribute: type: %d, len: %d", parsedAttr.Attr.Type, parsedAttr.Attr.Len)
|
||||
}
|
||||
}
|
||||
e.progs[attr.Attr.Type] = bpfO
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *BpfEncap) Encode() ([]byte, error) {
|
||||
buf := make([]byte, 0)
|
||||
native = nl.NativeEndian()
|
||||
for index, attr := range e.progs {
|
||||
nlMsg := nl.NewRtAttr(index, []byte{})
|
||||
if attr.progFd != 0 {
|
||||
nlMsg.AddRtAttr(nl.LWT_BPF_PROG_FD, nl.Uint32Attr(uint32(attr.progFd)))
|
||||
}
|
||||
if attr.progName != "" {
|
||||
nlMsg.AddRtAttr(nl.LWT_BPF_PROG_NAME, nl.ZeroTerminated(attr.progName))
|
||||
}
|
||||
if nlMsg.Len() > 4 {
|
||||
buf = append(buf, nlMsg.Serialize()...)
|
||||
}
|
||||
}
|
||||
if len(buf) <= 4 {
|
||||
return nil, fmt.Errorf("lwt bpf encode: bpf obj definitions returned empty buffer")
|
||||
}
|
||||
if e.headroom > 0 {
|
||||
hRoom := nl.NewRtAttr(nl.LWT_BPF_XMIT_HEADROOM, nl.Uint32Attr(uint32(e.headroom)))
|
||||
buf = append(buf, hRoom.Serialize()...)
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (e *BpfEncap) String() string {
|
||||
progs := make([]string, 0)
|
||||
for index, obj := range e.progs {
|
||||
empty := bpfObj{}
|
||||
switch index {
|
||||
case nl.LWT_BPF_IN:
|
||||
if obj != empty {
|
||||
progs = append(progs, fmt.Sprintf("in: %s", obj.progName))
|
||||
}
|
||||
case nl.LWT_BPF_OUT:
|
||||
if obj != empty {
|
||||
progs = append(progs, fmt.Sprintf("out: %s", obj.progName))
|
||||
}
|
||||
case nl.LWT_BPF_XMIT:
|
||||
if obj != empty {
|
||||
progs = append(progs, fmt.Sprintf("xmit: %s", obj.progName))
|
||||
}
|
||||
}
|
||||
}
|
||||
if e.headroom > 0 {
|
||||
progs = append(progs, fmt.Sprintf("xmit headroom: %d", e.headroom))
|
||||
}
|
||||
return strings.Join(progs, " ")
|
||||
}
|
||||
|
||||
func (e *BpfEncap) Equal(x Encap) bool {
|
||||
o, ok := x.(*BpfEncap)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if e.headroom != o.headroom {
|
||||
return false
|
||||
}
|
||||
for i, _ := range o.progs {
|
||||
if o.progs[i] != e.progs[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type Via struct {
|
||||
AddrFamily int
|
||||
Addr net.IP
|
||||
|
@ -628,9 +774,15 @@ func (h *Handle) routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch route.Encap.Type() {
|
||||
case nl.LWTUNNEL_ENCAP_BPF:
|
||||
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_ENCAP|unix.NLA_F_NESTED, buf))
|
||||
default:
|
||||
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_ENCAP, buf))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if route.Src != nil {
|
||||
srcFamily := nl.GetIPFamily(route.Src)
|
||||
if family != -1 && family != srcFamily {
|
||||
|
@ -1129,6 +1281,11 @@ func deserializeRoute(m []byte) (Route, error) {
|
|||
if err := e.Decode(encap.Value); err != nil {
|
||||
return route, err
|
||||
}
|
||||
case nl.LWTUNNEL_ENCAP_BPF:
|
||||
e = &BpfEncap{}
|
||||
if err := e.Decode(encap.Value); err != nil {
|
||||
return route, err
|
||||
}
|
||||
}
|
||||
route.Encap = e
|
||||
}
|
||||
|
|
|
@ -1350,6 +1350,56 @@ func TestSEG6LocalRoute6AddDel(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestBpfEncap(t *testing.T) {
|
||||
tCase := &BpfEncap{}
|
||||
if err := tCase.SetProg(nl.LWT_BPF_IN, 0, "test_in"); err == nil {
|
||||
t.Fatal("BpfEncap: inserting invalid FD did not return error")
|
||||
}
|
||||
if err := tCase.SetProg(nl.LWT_BPF_XMIT_HEADROOM, 23, "test_nout"); err == nil {
|
||||
t.Fatal("BpfEncap: inserting invalid mode did not return error")
|
||||
}
|
||||
if err := tCase.SetProg(nl.LWT_BPF_XMIT, 12, "test_xmit"); err != nil {
|
||||
t.Fatal("BpfEncap: inserting valid program option returned error")
|
||||
}
|
||||
if err := tCase.SetXmitHeadroom(12); err != nil {
|
||||
t.Fatal("BpfEncap: inserting valid headroom returned error")
|
||||
}
|
||||
if err := tCase.SetXmitHeadroom(nl.LWT_BPF_MAX_HEADROOM + 1); err == nil {
|
||||
t.Fatal("BpfEncap: inserting invalid headroom did not return error")
|
||||
}
|
||||
tCase = &BpfEncap{}
|
||||
|
||||
expected := &BpfEncap{
|
||||
progs: [nl.LWT_BPF_MAX]bpfObj{
|
||||
1: {
|
||||
progName: "test_in[fd:10]",
|
||||
progFd: 10,
|
||||
},
|
||||
2: {
|
||||
progName: "test_out[fd:11]",
|
||||
progFd: 11,
|
||||
},
|
||||
3: {
|
||||
progName: "test_xmit[fd:21]",
|
||||
progFd: 21,
|
||||
},
|
||||
},
|
||||
headroom: 128,
|
||||
}
|
||||
|
||||
_ = tCase.SetProg(1, 10, "test_in")
|
||||
_ = tCase.SetProg(2, 11, "test_out")
|
||||
_ = tCase.SetProg(3, 21, "test_xmit")
|
||||
_ = tCase.SetXmitHeadroom(128)
|
||||
if !tCase.Equal(expected) {
|
||||
t.Fatal("BpfEncap: equal comparison failed")
|
||||
}
|
||||
_ = tCase.SetProg(3, 21, "test2_xmit")
|
||||
if tCase.Equal(expected) {
|
||||
t.Fatal("BpfEncap: equal comparison succeeded when attributes differ")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMTURouteAddDel(t *testing.T) {
|
||||
_, err := RouteList(nil, FAMILY_V4)
|
||||
if err != nil {
|
||||
|
|
Loading…
Reference in New Issue