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_SCHED_ACT
|
||||||
BPF_PROG_TYPE_TRACEPOINT
|
BPF_PROG_TYPE_TRACEPOINT
|
||||||
BPF_PROG_TYPE_XDP
|
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 {
|
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
|
||||||
|
)
|
159
route_linux.go
159
route_linux.go
|
@ -460,6 +460,152 @@ func (e *SEG6LocalEncap) Equal(x Encap) bool {
|
||||||
return true
|
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 {
|
type Via struct {
|
||||||
AddrFamily int
|
AddrFamily int
|
||||||
Addr net.IP
|
Addr net.IP
|
||||||
|
@ -628,7 +774,13 @@ func (h *Handle) routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_ENCAP, buf))
|
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 {
|
if route.Src != nil {
|
||||||
|
@ -1129,6 +1281,11 @@ func deserializeRoute(m []byte) (Route, error) {
|
||||||
if err := e.Decode(encap.Value); err != nil {
|
if err := e.Decode(encap.Value); err != nil {
|
||||||
return route, err
|
return route, err
|
||||||
}
|
}
|
||||||
|
case nl.LWTUNNEL_ENCAP_BPF:
|
||||||
|
e = &BpfEncap{}
|
||||||
|
if err := e.Decode(encap.Value); err != nil {
|
||||||
|
return route, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
route.Encap = e
|
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) {
|
func TestMTURouteAddDel(t *testing.T) {
|
||||||
_, err := RouteList(nil, FAMILY_V4)
|
_, err := RouteList(nil, FAMILY_V4)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in New Issue