| Lorenz Brun | 153c9c1 | 2025-01-07 17:44:45 +0100 | [diff] [blame] | 1 | From 5aceb9e681cd6c82a2eccc25e1452d72d991c613 Mon Sep 17 00:00:00 2001 |
| 2 | From: Mateusz Zalega <mateusz@monogon.tech> |
| 3 | Date: Wed, 25 Jan 2023 11:20:06 +0000 |
| 4 | Subject: [PATCH] Support "sample" filter action |
| 5 | |
| 6 | This change adds support for packet sampling using "psample" kernel |
| 7 | module. |
| 8 | --- |
| 9 | filter.go | 23 +++++++++ |
| 10 | filter_linux.go | 25 +++++++++ |
| 11 | filter_test.go | 132 ++++++++++++++++++++++++++++++++++++++++++++++++ |
| 12 | nl/tc_linux.go | 11 ++++ |
| 13 | 4 files changed, 191 insertions(+) |
| 14 | |
| 15 | diff --git a/filter.go b/filter.go |
| 16 | index 84e1ca7..e4f3167 100644 |
| 17 | --- a/filter.go |
| 18 | +++ b/filter.go |
| 19 | @@ -369,6 +369,29 @@ func NewPoliceAction() *PoliceAction { |
| 20 | } |
| 21 | } |
| 22 | |
| 23 | +type SampleAction struct { |
| 24 | + ActionAttrs |
| 25 | + Group uint32 |
| 26 | + Rate uint32 |
| 27 | + TruncSize uint32 |
| 28 | +} |
| 29 | + |
| 30 | +func (action *SampleAction) Type() string { |
| 31 | + return "sample" |
| 32 | +} |
| 33 | + |
| 34 | +func (action *SampleAction) Attrs() *ActionAttrs { |
| 35 | + return &action.ActionAttrs |
| 36 | +} |
| 37 | + |
| 38 | +func NewSampleAction() *SampleAction { |
| 39 | + return &SampleAction{ |
| 40 | + ActionAttrs: ActionAttrs{ |
| 41 | + Action: TC_ACT_PIPE, |
| 42 | + }, |
| 43 | + } |
| 44 | +} |
| 45 | + |
| 46 | // MatchAll filters match all packets |
| 47 | type MatchAll struct { |
| 48 | FilterAttrs |
| 49 | diff --git a/filter_linux.go b/filter_linux.go |
| 50 | index 1930661..d61e357 100644 |
| 51 | --- a/filter_linux.go |
| 52 | +++ b/filter_linux.go |
| 53 | @@ -705,6 +705,17 @@ func EncodeActions(attr *nl.RtAttr, actions []Action) error { |
| 54 | aopts.AddRtAttr(nl.TCA_ACT_BPF_PARMS, gen.Serialize()) |
| 55 | aopts.AddRtAttr(nl.TCA_ACT_BPF_FD, nl.Uint32Attr(uint32(action.Fd))) |
| 56 | aopts.AddRtAttr(nl.TCA_ACT_BPF_NAME, nl.ZeroTerminated(action.Name)) |
| 57 | + case *SampleAction: |
| 58 | + table := attr.AddRtAttr(tabIndex, nil) |
| 59 | + tabIndex++ |
| 60 | + table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("sample")) |
| 61 | + aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil) |
| 62 | + gen := nl.TcGen{} |
| 63 | + toTcGen(action.Attrs(), &gen) |
| 64 | + aopts.AddRtAttr(nl.TCA_ACT_SAMPLE_PARMS, gen.Serialize()) |
| 65 | + aopts.AddRtAttr(nl.TCA_ACT_SAMPLE_RATE, nl.Uint32Attr(action.Rate)) |
| 66 | + aopts.AddRtAttr(nl.TCA_ACT_SAMPLE_PSAMPLE_GROUP, nl.Uint32Attr(action.Group)) |
| 67 | + aopts.AddRtAttr(nl.TCA_ACT_SAMPLE_TRUNC_SIZE, nl.Uint32Attr(action.TruncSize)) |
| 68 | case *GenericAction: |
| 69 | table := attr.AddRtAttr(tabIndex, nil) |
| 70 | tabIndex++ |
| 71 | @@ -790,6 +801,8 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) { |
| 72 | action = &ConnmarkAction{} |
| 73 | case "csum": |
| 74 | action = &CsumAction{} |
| 75 | + case "sample": |
| 76 | + action = &SampleAction{} |
| 77 | case "gact": |
| 78 | action = &GenericAction{} |
| 79 | case "tunnel_key": |
| 80 | @@ -902,6 +915,18 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) { |
| 81 | tcTs := nl.DeserializeTcf(adatum.Value) |
| 82 | actionTimestamp = toTimeStamp(tcTs) |
| 83 | } |
| 84 | + case "sample": |
| 85 | + switch adatum.Attr.Type { |
| 86 | + case nl.TCA_ACT_SAMPLE_PARMS: |
| 87 | + gen := *nl.DeserializeTcGen(adatum.Value) |
| 88 | + toAttrs(&gen, action.Attrs()) |
| 89 | + case nl.TCA_ACT_SAMPLE_RATE: |
| 90 | + action.(*SampleAction).Rate = native.Uint32(adatum.Value[0:4]) |
| 91 | + case nl.TCA_ACT_SAMPLE_PSAMPLE_GROUP: |
| 92 | + action.(*SampleAction).Group = native.Uint32(adatum.Value[0:4]) |
| 93 | + case nl.TCA_ACT_SAMPLE_TRUNC_SIZE: |
| 94 | + action.(*SampleAction).TruncSize = native.Uint32(adatum.Value[0:4]) |
| 95 | + } |
| 96 | case "gact": |
| 97 | switch adatum.Attr.Type { |
| 98 | case nl.TCA_GACT_PARMS: |
| 99 | diff --git a/filter_test.go b/filter_test.go |
| 100 | index 3a49f1b..774e7d6 100644 |
| 101 | --- a/filter_test.go |
| 102 | +++ b/filter_test.go |
| 103 | @@ -2471,3 +2471,135 @@ func TestFilterChainAddDel(t *testing.T) { |
| 104 | t.Fatal("Failed to remove qdisc") |
| 105 | } |
| 106 | } |
| 107 | + |
| 108 | +func TestFilterSampleAddDel(t *testing.T) { |
| 109 | + minKernelRequired(t, 4, 11) |
| 110 | + if _, err := GenlFamilyGet("psample"); err != nil { |
| 111 | + t.Skip("psample genetlink family unavailable - is CONFIG_PSAMPLE enabled?") |
| 112 | + } |
| 113 | + |
| 114 | + tearDown := setUpNetlinkTest(t) |
| 115 | + defer tearDown() |
| 116 | + if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil { |
| 117 | + t.Fatal(err) |
| 118 | + } |
| 119 | + link, err := LinkByName("foo") |
| 120 | + if err != nil { |
| 121 | + t.Fatal(err) |
| 122 | + } |
| 123 | + if err := LinkSetUp(link); err != nil { |
| 124 | + t.Fatal(err) |
| 125 | + } |
| 126 | + |
| 127 | + qdisc := &Ingress{ |
| 128 | + QdiscAttrs: QdiscAttrs{ |
| 129 | + LinkIndex: link.Attrs().Index, |
| 130 | + Handle: MakeHandle(0xffff, 0), |
| 131 | + Parent: HANDLE_INGRESS, |
| 132 | + }, |
| 133 | + } |
| 134 | + if err := QdiscAdd(qdisc); err != nil { |
| 135 | + t.Fatal(err) |
| 136 | + } |
| 137 | + qdiscs, err := SafeQdiscList(link) |
| 138 | + if err != nil { |
| 139 | + t.Fatal(err) |
| 140 | + } |
| 141 | + |
| 142 | + found := false |
| 143 | + for _, v := range qdiscs { |
| 144 | + if _, ok := v.(*Ingress); ok { |
| 145 | + found = true |
| 146 | + break |
| 147 | + } |
| 148 | + } |
| 149 | + if !found { |
| 150 | + t.Fatal("Qdisc is the wrong type") |
| 151 | + } |
| 152 | + |
| 153 | + sample := NewSampleAction() |
| 154 | + sample.Group = 7 |
| 155 | + sample.Rate = 12 |
| 156 | + sample.TruncSize = 200 |
| 157 | + |
| 158 | + classId := MakeHandle(1, 1) |
| 159 | + filter := &MatchAll{ |
| 160 | + FilterAttrs: FilterAttrs{ |
| 161 | + LinkIndex: link.Attrs().Index, |
| 162 | + Parent: MakeHandle(0xffff, 0), |
| 163 | + Priority: 1, |
| 164 | + Protocol: unix.ETH_P_ALL, |
| 165 | + }, |
| 166 | + ClassId: classId, |
| 167 | + Actions: []Action{ |
| 168 | + sample, |
| 169 | + }, |
| 170 | + } |
| 171 | + |
| 172 | + if err := FilterAdd(filter); err != nil { |
| 173 | + t.Fatal(err) |
| 174 | + } |
| 175 | + |
| 176 | + filters, err := FilterList(link, MakeHandle(0xffff, 0)) |
| 177 | + if err != nil { |
| 178 | + t.Fatal(err) |
| 179 | + } |
| 180 | + if len(filters) != 1 { |
| 181 | + t.Fatal("Failed to add filter") |
| 182 | + } |
| 183 | + mf, ok := filters[0].(*MatchAll) |
| 184 | + if !ok { |
| 185 | + t.Fatal("Filter is the wrong type") |
| 186 | + } |
| 187 | + |
| 188 | + if len(mf.Actions) < 1 { |
| 189 | + t.Fatalf("Too few Actions in filter") |
| 190 | + } |
| 191 | + if mf.ClassId != classId { |
| 192 | + t.Fatalf("ClassId of the filter is the wrong value") |
| 193 | + } |
| 194 | + |
| 195 | + lsample, ok := mf.Actions[0].(*SampleAction) |
| 196 | + if !ok { |
| 197 | + t.Fatal("Unable to find sample action") |
| 198 | + } |
| 199 | + if lsample.Group != sample.Group { |
| 200 | + t.Fatalf("Inconsistent sample action group") |
| 201 | + } |
| 202 | + if lsample.Rate != sample.Rate { |
| 203 | + t.Fatalf("Inconsistent sample action rate") |
| 204 | + } |
| 205 | + if lsample.TruncSize != sample.TruncSize { |
| 206 | + t.Fatalf("Inconsistent sample truncation size") |
| 207 | + } |
| 208 | + |
| 209 | + if err := FilterDel(filter); err != nil { |
| 210 | + t.Fatal(err) |
| 211 | + } |
| 212 | + filters, err = FilterList(link, MakeHandle(0xffff, 0)) |
| 213 | + if err != nil { |
| 214 | + t.Fatal(err) |
| 215 | + } |
| 216 | + if len(filters) != 0 { |
| 217 | + t.Fatal("Failed to remove filter") |
| 218 | + } |
| 219 | + |
| 220 | + if err := QdiscDel(qdisc); err != nil { |
| 221 | + t.Fatal(err) |
| 222 | + } |
| 223 | + qdiscs, err = SafeQdiscList(link) |
| 224 | + if err != nil { |
| 225 | + t.Fatal(err) |
| 226 | + } |
| 227 | + |
| 228 | + found = false |
| 229 | + for _, v := range qdiscs { |
| 230 | + if _, ok := v.(*Ingress); ok { |
| 231 | + found = true |
| 232 | + break |
| 233 | + } |
| 234 | + } |
| 235 | + if found { |
| 236 | + t.Fatal("Failed to remove qdisc") |
| 237 | + } |
| 238 | +} |
| 239 | diff --git a/nl/tc_linux.go b/nl/tc_linux.go |
| 240 | index 0720729..db3ca1c 100644 |
| 241 | --- a/nl/tc_linux.go |
| 242 | +++ b/nl/tc_linux.go |
| 243 | @@ -77,6 +77,17 @@ const ( |
| 244 | TCA_ACT_MAX |
| 245 | ) |
| 246 | |
| 247 | +const ( |
| 248 | + TCA_ACT_SAMPLE_UNSPEC = iota |
| 249 | + TCA_ACT_SAMPLE_TM |
| 250 | + TCA_ACT_SAMPLE_PARMS |
| 251 | + TCA_ACT_SAMPLE_RATE |
| 252 | + TCA_ACT_SAMPLE_TRUNC_SIZE |
| 253 | + TCA_ACT_SAMPLE_PSAMPLE_GROUP |
| 254 | + TCA_ACT_SAMPLE_PAD |
| 255 | + TCA_ACT_SAMPLE_MAX |
| 256 | +) |
| 257 | + |
| 258 | const ( |
| 259 | TCA_PRIO_UNSPEC = iota |
| 260 | TCA_PRIO_MQ |
| 261 | -- |
| 262 | 2.47.0 |
| 263 | |