mirror of
https://github.com/vishvananda/netlink
synced 2024-12-27 17:12:23 +00:00
Add advanced u32 match feature support
This patch adds the advanced u32 match feature support with the selection keys. It enables the users to crate a complex filter as follow for instance: $ tc filter add dev eth0 parent 1: prio 1 u32 match ip dport 80 0xff \ match u32 0x146ca 0xffff at 32 flowid 1:46cb To expose TcU32Sel and TcU32Key defined in nl package, this patch copies them to filte.go and their values that require Big-endianness are handled appropriately in FilterAdd function in filter_linux.go. Signed-off-by: Taku Fukushima <taku@soracom.jp>
This commit is contained in:
parent
0ac4d252c6
commit
8bf4a3aacd
41
filter.go
41
filter.go
@ -1,6 +1,10 @@
|
||||
package netlink
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
)
|
||||
|
||||
type Filter interface {
|
||||
Attrs() *FilterAttrs
|
||||
@ -180,11 +184,46 @@ func NewMirredAction(redirIndex int) *MirredAction {
|
||||
}
|
||||
}
|
||||
|
||||
// Constants used in TcU32Sel.Flags.
|
||||
const (
|
||||
TC_U32_TERMINAL = nl.TC_U32_TERMINAL
|
||||
TC_U32_OFFSET = nl.TC_U32_OFFSET
|
||||
TC_U32_VAROFFSET = nl.TC_U32_VAROFFSET
|
||||
TC_U32_EAT = nl.TC_U32_EAT
|
||||
)
|
||||
|
||||
// Sel of the U32 filters that contains multiple TcU32Key. This is the copy
|
||||
// and the frontend representation of nl.TcU32Sel. It is serialized into canonical
|
||||
// nl.TcU32Sel with the appropriate endianness.
|
||||
type TcU32Sel struct {
|
||||
Flags uint8
|
||||
Offshift uint8
|
||||
Nkeys uint8
|
||||
Pad uint8
|
||||
Offmask uint16
|
||||
Off uint16
|
||||
Offoff int16
|
||||
Hoff int16
|
||||
Hmask uint32
|
||||
Keys []TcU32Key
|
||||
}
|
||||
|
||||
// TcU32Key contained of Sel in the U32 filters. This is the copy and the frontend
|
||||
// representation of nl.TcU32Key. It is serialized into chanonical nl.TcU32Sel
|
||||
// with the appropriate endianness.
|
||||
type TcU32Key struct {
|
||||
Mask uint32
|
||||
Val uint32
|
||||
Off int32
|
||||
OffMask int32
|
||||
}
|
||||
|
||||
// U32 filters on many packet related properties
|
||||
type U32 struct {
|
||||
FilterAttrs
|
||||
ClassId uint32
|
||||
RedirIndex int
|
||||
Sel *TcU32Sel
|
||||
Actions []Action
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
)
|
||||
@ -128,12 +129,34 @@ func (h *Handle) FilterAdd(filter Filter) error {
|
||||
|
||||
options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
|
||||
if u32, ok := filter.(*U32); ok {
|
||||
// match all
|
||||
sel := nl.TcU32Sel{
|
||||
Nkeys: 1,
|
||||
Flags: nl.TC_U32_TERMINAL,
|
||||
// Convert TcU32Sel into nl.TcU32Sel as it is without copy.
|
||||
sel := (*nl.TcU32Sel)(unsafe.Pointer(u32.Sel))
|
||||
if sel == nil {
|
||||
// match all
|
||||
sel = &nl.TcU32Sel{
|
||||
Nkeys: 1,
|
||||
Flags: nl.TC_U32_TERMINAL,
|
||||
}
|
||||
sel.Keys = append(sel.Keys, nl.TcU32Key{})
|
||||
}
|
||||
sel.Keys = append(sel.Keys, nl.TcU32Key{})
|
||||
|
||||
if native != networkOrder {
|
||||
// Copy Tcu32Sel.
|
||||
cSel := sel
|
||||
keys := make([]nl.TcU32Key, cap(sel.Keys))
|
||||
copy(keys, sel.Keys)
|
||||
cSel.Keys = keys
|
||||
sel = cSel
|
||||
|
||||
// Handle the endianness of attributes
|
||||
sel.Offmask = native.Uint16(htons(sel.Offmask))
|
||||
sel.Hmask = native.Uint32(htonl(sel.Hmask))
|
||||
for _, key := range sel.Keys {
|
||||
key.Mask = native.Uint32(htonl(key.Mask))
|
||||
key.Val = native.Uint32(htonl(key.Val))
|
||||
}
|
||||
}
|
||||
sel.Nkeys = uint8(len(sel.Keys))
|
||||
nl.NewRtAttrChild(options, nl.TCA_U32_SEL, sel.Serialize())
|
||||
if u32.ClassId != 0 {
|
||||
nl.NewRtAttrChild(options, nl.TCA_U32_CLASSID, nl.Uint32Attr(u32.ClassId))
|
||||
@ -425,6 +448,16 @@ func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error)
|
||||
case nl.TCA_U32_SEL:
|
||||
detailed = true
|
||||
sel := nl.DeserializeTcU32Sel(datum.Value)
|
||||
u32.Sel = (*TcU32Sel)(unsafe.Pointer(sel))
|
||||
if native != networkOrder {
|
||||
// Handle the endianness of attributes
|
||||
u32.Sel.Offmask = native.Uint16(htons(sel.Offmask))
|
||||
u32.Sel.Hmask = native.Uint32(htonl(sel.Hmask))
|
||||
for _, key := range u32.Sel.Keys {
|
||||
key.Mask = native.Uint32(htonl(key.Mask))
|
||||
key.Val = native.Uint32(htonl(key.Val))
|
||||
}
|
||||
}
|
||||
// only parse if we have a very basic redirect
|
||||
if sel.Flags&nl.TC_U32_TERMINAL == 0 || sel.Nkeys != 1 {
|
||||
return detailed, nil
|
||||
|
157
filter_test.go
157
filter_test.go
@ -101,6 +101,163 @@ func TestFilterAddDel(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdvancedFilterAddDel(t *testing.T) {
|
||||
tearDown := setUpNetlinkTest(t)
|
||||
defer tearDown()
|
||||
if err := LinkAdd(&Ifb{LinkAttrs{Name: "baz"}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
link, err := LinkByName("baz")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := LinkSetUp(link); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
index := link.Attrs().Index
|
||||
|
||||
qdiscHandle := MakeHandle(0x1, 0x0)
|
||||
qdiscAttrs := QdiscAttrs{
|
||||
LinkIndex: index,
|
||||
Handle: qdiscHandle,
|
||||
Parent: HANDLE_ROOT,
|
||||
}
|
||||
|
||||
qdisc := NewHtb(qdiscAttrs)
|
||||
if err := QdiscAdd(qdisc); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
qdiscs, err := QdiscList(link)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(qdiscs) != 1 {
|
||||
t.Fatal("Failed to add qdisc")
|
||||
}
|
||||
_, ok := qdiscs[0].(*Htb)
|
||||
if !ok {
|
||||
t.Fatal("Qdisc is the wrong type")
|
||||
}
|
||||
|
||||
classId := MakeHandle(0x1, 0x46cb)
|
||||
classAttrs := ClassAttrs{
|
||||
LinkIndex: index,
|
||||
Parent: qdiscHandle,
|
||||
Handle: classId,
|
||||
}
|
||||
htbClassAttrs := HtbClassAttrs{
|
||||
Rate: 512 * 1024,
|
||||
Buffer: 32 * 1024,
|
||||
}
|
||||
htbClass := NewHtbClass(classAttrs, htbClassAttrs)
|
||||
if err = ClassReplace(htbClass); err != nil {
|
||||
t.Fatalf("Failed to add a HTB class: %v", err)
|
||||
}
|
||||
classes, err := ClassList(link, qdiscHandle)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(classes) != 1 {
|
||||
t.Fatal("Failed to add class")
|
||||
}
|
||||
_, ok = classes[0].(*HtbClass)
|
||||
if !ok {
|
||||
t.Fatal("Class is the wrong type")
|
||||
}
|
||||
|
||||
u32SelKeys := []TcU32Key{
|
||||
TcU32Key{
|
||||
Mask: 0xff,
|
||||
Val: 80,
|
||||
Off: 20,
|
||||
OffMask: 0,
|
||||
},
|
||||
TcU32Key{
|
||||
Mask: 0xffff,
|
||||
Val: 0x146ca,
|
||||
Off: 32,
|
||||
OffMask: 0,
|
||||
},
|
||||
}
|
||||
filter := &U32{
|
||||
FilterAttrs: FilterAttrs{
|
||||
LinkIndex: index,
|
||||
Parent: qdiscHandle,
|
||||
Priority: 1,
|
||||
Protocol: syscall.ETH_P_ALL,
|
||||
},
|
||||
Sel: &TcU32Sel{
|
||||
Keys: u32SelKeys,
|
||||
Flags: TC_U32_TERMINAL,
|
||||
},
|
||||
ClassId: classId,
|
||||
Actions: []Action{},
|
||||
}
|
||||
if err := FilterAdd(filter); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
filters, err := FilterList(link, qdiscHandle)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(filters) != 1 {
|
||||
t.Fatal("Failed to add filter")
|
||||
}
|
||||
|
||||
u32, ok := filters[0].(*U32)
|
||||
if !ok {
|
||||
t.Fatal("Filter is the wrong type")
|
||||
}
|
||||
// Endianness checks
|
||||
if u32.Sel.Offmask != filter.Sel.Offmask {
|
||||
t.Fatal("The endianness of TcU32Key.Sel.Offmask is wrong")
|
||||
}
|
||||
if u32.Sel.Hmask != filter.Sel.Hmask {
|
||||
t.Fatal("The endianness of TcU32Key.Sel.Hmask is wrong")
|
||||
}
|
||||
for i, key := range u32.Sel.Keys {
|
||||
if key.Mask != filter.Sel.Keys[i].Mask {
|
||||
t.Fatal("The endianness of TcU32Key.Mask is wrong")
|
||||
}
|
||||
if key.Val != filter.Sel.Keys[i].Val {
|
||||
t.Fatal("The endianness of TcU32Key.Val is wrong")
|
||||
}
|
||||
}
|
||||
|
||||
if err := FilterDel(filter); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
filters, err = FilterList(link, qdiscHandle)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(filters) != 0 {
|
||||
t.Fatal("Failed to remove filter")
|
||||
}
|
||||
|
||||
if err = ClassDel(htbClass); err != nil {
|
||||
t.Fatalf("Failed to delete a HTP class: %v", err)
|
||||
}
|
||||
classes, err = ClassList(link, qdiscHandle)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(classes) != 0 {
|
||||
t.Fatal("Failed to remove class")
|
||||
}
|
||||
|
||||
if err := QdiscDel(qdisc); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
qdiscs, err = QdiscList(link)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(qdiscs) != 0 {
|
||||
t.Fatal("Failed to remove qdisc")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterFwAddDel(t *testing.T) {
|
||||
tearDown := setUpNetlinkTest(t)
|
||||
defer tearDown()
|
||||
|
Loading…
Reference in New Issue
Block a user