Support "sample" filter action

This change adds support for packet sampling using "psample" kernel
module.
This commit is contained in:
Mateusz Zalega
2023-01-25 11:20:06 +00:00
committed by Alessandro Boch
parent dc4f225935
commit 1f910b7a22
4 changed files with 191 additions and 0 deletions

View File

@@ -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

View File

@@ -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++
@@ -826,6 +837,8 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) {
action = &ConnmarkAction{}
case "csum":
action = &CsumAction{}
case "sample":
action = &SampleAction{}
case "gact":
action = &GenericAction{}
case "vlan":
@@ -950,6 +963,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:

View File

@@ -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")
}
}

View File

@@ -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