Skip to content

Commit

Permalink
Support "sample" filter action
Browse files Browse the repository at this point in the history
This change adds support for packet sampling using "psample" kernel
module.
  • Loading branch information
Mateusz Zalega authored and lorenz committed Feb 2, 2025
1 parent 7c2350b commit 1859701
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 0 deletions.
23 changes: 23 additions & 0 deletions filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,29 @@ func NewPoliceAction() *PoliceAction {
}
}

type SampleAction struct {
ActionAttrs
Group uint32
Rate uint32
TruncSize uint32
}

func (action *SampleAction) Type() string {
return "sample"
}

func (action *SampleAction) Attrs() *ActionAttrs {
return &action.ActionAttrs
}

func NewSampleAction() *SampleAction {
return &SampleAction{
ActionAttrs: ActionAttrs{
Action: TC_ACT_PIPE,
},
}
}

// MatchAll filters match all packets
type MatchAll struct {
FilterAttrs
Expand Down
25 changes: 25 additions & 0 deletions filter_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,17 @@ func EncodeActions(attr *nl.RtAttr, actions []Action) error {
aopts.AddRtAttr(nl.TCA_ACT_BPF_PARMS, gen.Serialize())
aopts.AddRtAttr(nl.TCA_ACT_BPF_FD, nl.Uint32Attr(uint32(action.Fd)))
aopts.AddRtAttr(nl.TCA_ACT_BPF_NAME, nl.ZeroTerminated(action.Name))
case *SampleAction:
table := attr.AddRtAttr(tabIndex, nil)
tabIndex++
table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("sample"))
aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil)
gen := nl.TcGen{}
toTcGen(action.Attrs(), &gen)
aopts.AddRtAttr(nl.TCA_ACT_SAMPLE_PARMS, gen.Serialize())
aopts.AddRtAttr(nl.TCA_ACT_SAMPLE_RATE, nl.Uint32Attr(action.Rate))
aopts.AddRtAttr(nl.TCA_ACT_SAMPLE_PSAMPLE_GROUP, nl.Uint32Attr(action.Group))
aopts.AddRtAttr(nl.TCA_ACT_SAMPLE_TRUNC_SIZE, nl.Uint32Attr(action.TruncSize))
case *GenericAction:
table := attr.AddRtAttr(tabIndex, nil)
tabIndex++
Expand Down Expand Up @@ -825,6 +836,8 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) {
action = &ConnmarkAction{}
case "csum":
action = &CsumAction{}
case "sample":
action = &SampleAction{}
case "gact":
action = &GenericAction{}
case "vlan":
Expand Down Expand Up @@ -949,6 +962,18 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) {
tcTs := nl.DeserializeTcf(adatum.Value)
actionTimestamp = toTimeStamp(tcTs)
}
case "sample":
switch adatum.Attr.Type {
case nl.TCA_ACT_SAMPLE_PARMS:
gen := *nl.DeserializeTcGen(adatum.Value)
toAttrs(&gen, action.Attrs())
case nl.TCA_ACT_SAMPLE_RATE:
action.(*SampleAction).Rate = native.Uint32(adatum.Value[0:4])
case nl.TCA_ACT_SAMPLE_PSAMPLE_GROUP:
action.(*SampleAction).Group = native.Uint32(adatum.Value[0:4])
case nl.TCA_ACT_SAMPLE_TRUNC_SIZE:
action.(*SampleAction).TruncSize = native.Uint32(adatum.Value[0:4])
}
case "gact":
switch adatum.Attr.Type {
case nl.TCA_GACT_PARMS:
Expand Down
132 changes: 132 additions & 0 deletions filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2599,3 +2599,135 @@ func TestFilterChainAddDel(t *testing.T) {
t.Fatal("Failed to remove qdisc")
}
}

func TestFilterSampleAddDel(t *testing.T) {
minKernelRequired(t, 4, 11)
if _, err := GenlFamilyGet("psample"); err != nil {
t.Skip("psample genetlink family unavailable - is CONFIG_PSAMPLE enabled?")
}

tearDown := setUpNetlinkTest(t)
defer tearDown()
if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil {
t.Fatal(err)
}
link, err := LinkByName("foo")
if err != nil {
t.Fatal(err)
}
if err := LinkSetUp(link); err != nil {
t.Fatal(err)
}

qdisc := &Ingress{
QdiscAttrs: QdiscAttrs{
LinkIndex: link.Attrs().Index,
Handle: MakeHandle(0xffff, 0),
Parent: HANDLE_INGRESS,
},
}
if err := QdiscAdd(qdisc); err != nil {
t.Fatal(err)
}
qdiscs, err := SafeQdiscList(link)
if err != nil {
t.Fatal(err)
}

found := false
for _, v := range qdiscs {
if _, ok := v.(*Ingress); ok {
found = true
break
}
}
if !found {
t.Fatal("Qdisc is the wrong type")
}

sample := NewSampleAction()
sample.Group = 7
sample.Rate = 12
sample.TruncSize = 200

classId := MakeHandle(1, 1)
filter := &MatchAll{
FilterAttrs: FilterAttrs{
LinkIndex: link.Attrs().Index,
Parent: MakeHandle(0xffff, 0),
Priority: 1,
Protocol: unix.ETH_P_ALL,
},
ClassId: classId,
Actions: []Action{
sample,
},
}

if err := FilterAdd(filter); err != nil {
t.Fatal(err)
}

filters, err := FilterList(link, MakeHandle(0xffff, 0))
if err != nil {
t.Fatal(err)
}
if len(filters) != 1 {
t.Fatal("Failed to add filter")
}
mf, ok := filters[0].(*MatchAll)
if !ok {
t.Fatal("Filter is the wrong type")
}

if len(mf.Actions) < 1 {
t.Fatalf("Too few Actions in filter")
}
if mf.ClassId != classId {
t.Fatalf("ClassId of the filter is the wrong value")
}

lsample, ok := mf.Actions[0].(*SampleAction)
if !ok {
t.Fatal("Unable to find sample action")
}
if lsample.Group != sample.Group {
t.Fatalf("Inconsistent sample action group")
}
if lsample.Rate != sample.Rate {
t.Fatalf("Inconsistent sample action rate")
}
if lsample.TruncSize != sample.TruncSize {
t.Fatalf("Inconsistent sample truncation size")
}

if err := FilterDel(filter); err != nil {
t.Fatal(err)
}
filters, err = FilterList(link, MakeHandle(0xffff, 0))
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)
}

found = false
for _, v := range qdiscs {
if _, ok := v.(*Ingress); ok {
found = true
break
}
}
if found {
t.Fatal("Failed to remove qdisc")
}
}
11 changes: 11 additions & 0 deletions nl/tc_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,17 @@ const (
TCA_ACT_MAX
)

const (
TCA_ACT_SAMPLE_UNSPEC = iota
TCA_ACT_SAMPLE_TM
TCA_ACT_SAMPLE_PARMS
TCA_ACT_SAMPLE_RATE
TCA_ACT_SAMPLE_TRUNC_SIZE
TCA_ACT_SAMPLE_PSAMPLE_GROUP
TCA_ACT_SAMPLE_PAD
TCA_ACT_SAMPLE_MAX
)

const (
TCA_PRIO_UNSPEC = iota
TCA_PRIO_MQ
Expand Down

0 comments on commit 1859701

Please sign in to comment.