Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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")
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pls remove the blank line

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 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to define this excp inside this if?
I do not think it gives us much compared to

if len(ops) != 8*opsLen {
           [...]", len(ops), 8*opsLen)
}

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:]),
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe remove this blank line

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