Skip to content

Commit

Permalink
Add cBPF ops support to BpfFilter
Browse files Browse the repository at this point in the history
  • Loading branch information
stffabi committed Sep 11, 2023
1 parent 63484bb commit 4f2580e
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 0 deletions.
8 changes: 8 additions & 0 deletions filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ type BpfFilter struct {
DirectAction bool
Id int
Tag string
Ops []SockFilter
}

func (filter *BpfFilter) Type() string {
Expand All @@ -418,3 +419,10 @@ func (filter *GenericFilter) Attrs() *FilterAttrs {
func (filter *GenericFilter) Type() string {
return filter.FilterType
}

type SockFilter struct {
Code uint16
Jt uint8
Jf uint8
K uint32
}
66 changes: 66 additions & 0 deletions filter_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/hex"
"errors"
"fmt"
"math"
"net"
"syscall"

Expand Down Expand Up @@ -371,6 +372,18 @@ func (h *Handle) filterModify(filter Filter, proto, flags int) error {
if filter.Fd >= 0 {
options.AddRtAttr(nl.TCA_BPF_FD, nl.Uint32Attr((uint32(filter.Fd))))
}
if len(filter.Ops) > 0 {
if filter.Fd >= 0 {
return fmt.Errorf("only Ops or Fd can be specified on a BpfFilter")
}

opsLen, ops, err := serializeSockFilter(filter.Ops)
if err != nil {
return err
}
options.AddRtAttr(nl.TCA_BPF_OPS_LEN, nl.Uint16Attr(opsLen))
options.AddRtAttr(nl.TCA_BPF_OPS, ops)
}
if filter.Name != "" {
options.AddRtAttr(nl.TCA_BPF_NAME, nl.ZeroTerminated(filter.Name))
}
Expand Down Expand Up @@ -934,7 +947,10 @@ func parseFwData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {

func parseBpfData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
bpf := filter.(*BpfFilter)
bpf.Fd = -1
detailed := true
var opsLen uint16
var ops []byte
for _, datum := range data {
switch datum.Attr.Type {
case nl.TCA_BPF_FD:
Expand All @@ -952,6 +968,17 @@ func parseBpfData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error)
bpf.Id = int(native.Uint32(datum.Value[0:4]))
case nl.TCA_BPF_TAG:
bpf.Tag = hex.EncodeToString(datum.Value)
case nl.TCA_BPF_OPS_LEN:
opsLen = native.Uint16(datum.Value[0:2])
case nl.TCA_BPF_OPS:
ops = datum.Value
}
}
if opsLen > 0 {
var err error
bpf.Ops, err = deserializeSockFilter(opsLen, ops)
if err != nil {
return detailed, err
}
}
return detailed, nil
Expand Down Expand Up @@ -1039,3 +1066,42 @@ func SerializeRtab(rtab [256]uint32) []byte {
_ = binary.Write(&w, native, rtab)
return w.Bytes()
}

func deserializeSockFilter(opsLen uint16, ops []byte) ([]SockFilter, error) {
if excp := int(opsLen) * 8; len(ops) != excp {
return nil, fmt.Errorf("unexpected ops length %d, expected %d", len(ops), excp)
}

ins := make([]SockFilter, opsLen)
for i := 0; i < len(ins); i++ {
ins[i] = SockFilter{
Code: native.Uint16(ops[0:]),
Jt: ops[2],
Jf: ops[3],
K: native.Uint32(ops[4:]),
}

ops = ops[8:]
}

return ins, nil
}

func serializeSockFilter(rawIns []SockFilter) (uint16, []byte, error) {
opsLen := len(rawIns)
if opsLen > math.MaxUint16 {
return 0, nil, fmt.Errorf("too many bpf instructions, max %d", math.MaxUint16)
}

ops := make([]byte, 8*opsLen)
b := ops
for _, ins := range rawIns {
native.PutUint16(b[0:], ins.Code)
b[2] = ins.Jt
b[3] = ins.Jf
native.PutUint32(b[4:], ins.K)

b = b[8:]
}
return uint16(opsLen), ops, nil
}
81 changes: 81 additions & 0 deletions filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1156,6 +1156,87 @@ func TestFilterClsActBpfAddDel(t *testing.T) {
}
}

func TestFilterClsActCBpfAddDel(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,
}

filter := &BpfFilter{
FilterAttrs: filterattrs,
Fd: -1,
Name: "simple",
DirectAction: true,
Ops: []SockFilter{
SockFilter{
Code: 0x0006,
Jt: 0x00,
Jf: 0x00,
K: 0x00000002,
},
},
}

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 len(filter.Ops) != 1 ||
bpf.Ops[0].Code != filter.Ops[0].Code ||
bpf.Ops[0].Jt != filter.Ops[0].Jt ||
bpf.Ops[0].Jf != filter.Ops[0].Jf ||
bpf.Ops[0].K != filter.Ops[0].K {

t.Fatal("Filter Ops 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)
Expand Down

0 comments on commit 4f2580e

Please sign in to comment.