Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/net/ynl/tests/tc.c
170959 views
1
// SPDX-License-Identifier: GPL-2.0
2
#define _GNU_SOURCE
3
#include <sched.h>
4
#include <stdio.h>
5
#include <string.h>
6
#include <stdlib.h>
7
#include <arpa/inet.h>
8
#include <linux/pkt_sched.h>
9
#include <linux/tc_act/tc_vlan.h>
10
#include <linux/tc_act/tc_gact.h>
11
#include <linux/if_ether.h>
12
#include <net/if.h>
13
14
#include <ynl.h>
15
16
#include <kselftest_harness.h>
17
18
#include "tc-user.h"
19
20
#define TC_HANDLE (0xFFFF << 16)
21
22
static bool tc_qdisc_print(struct __test_metadata *_metadata,
23
struct tc_getqdisc_rsp *q)
24
{
25
bool was_fq_codel = false;
26
char ifname[IF_NAMESIZE];
27
const char *name;
28
29
name = if_indextoname(q->_hdr.tcm_ifindex, ifname);
30
EXPECT_TRUE((bool)name);
31
ksft_print_msg("%16s: ", name ?: "no-name");
32
33
if (q->_len.kind) {
34
printf("%s ", q->kind);
35
36
if (q->options._present.fq_codel) {
37
struct tc_fq_codel_attrs *fq_codel;
38
struct tc_fq_codel_xstats *stats;
39
40
fq_codel = &q->options.fq_codel;
41
stats = q->stats2.app.fq_codel;
42
43
EXPECT_EQ(true,
44
fq_codel->_present.limit &&
45
fq_codel->_present.target &&
46
q->stats2.app._len.fq_codel);
47
48
if (fq_codel->_present.limit)
49
printf("limit: %dp ", fq_codel->limit);
50
if (fq_codel->_present.target)
51
printf("target: %dms ",
52
(fq_codel->target + 500) / 1000);
53
if (q->stats2.app._len.fq_codel)
54
printf("new_flow_cnt: %d ",
55
stats->qdisc_stats.new_flow_count);
56
was_fq_codel = true;
57
}
58
}
59
printf("\n");
60
61
return was_fq_codel;
62
}
63
64
static const char *vlan_act_name(struct tc_vlan *p)
65
{
66
switch (p->v_action) {
67
case TCA_VLAN_ACT_POP:
68
return "pop";
69
case TCA_VLAN_ACT_PUSH:
70
return "push";
71
case TCA_VLAN_ACT_MODIFY:
72
return "modify";
73
default:
74
break;
75
}
76
77
return "not supported";
78
}
79
80
static const char *gact_act_name(struct tc_gact *p)
81
{
82
switch (p->action) {
83
case TC_ACT_SHOT:
84
return "drop";
85
case TC_ACT_OK:
86
return "ok";
87
case TC_ACT_PIPE:
88
return "pipe";
89
default:
90
break;
91
}
92
93
return "not supported";
94
}
95
96
static void print_vlan(struct tc_act_vlan_attrs *vlan)
97
{
98
printf("%s ", vlan_act_name(vlan->parms));
99
if (vlan->_present.push_vlan_id)
100
printf("id %u ", vlan->push_vlan_id);
101
if (vlan->_present.push_vlan_protocol)
102
printf("protocol %#x ", ntohs(vlan->push_vlan_protocol));
103
if (vlan->_present.push_vlan_priority)
104
printf("priority %u ", vlan->push_vlan_priority);
105
}
106
107
static void print_gact(struct tc_act_gact_attrs *gact)
108
{
109
struct tc_gact *p = gact->parms;
110
111
printf("%s ", gact_act_name(p));
112
}
113
114
static void flower_print(struct tc_flower_attrs *flower, const char *kind)
115
{
116
struct tc_act_attrs *a;
117
unsigned int i;
118
119
ksft_print_msg("%s:\n", kind);
120
121
if (flower->_present.key_vlan_id)
122
ksft_print_msg(" vlan_id: %u\n", flower->key_vlan_id);
123
if (flower->_present.key_vlan_prio)
124
ksft_print_msg(" vlan_prio: %u\n", flower->key_vlan_prio);
125
if (flower->_present.key_num_of_vlans)
126
ksft_print_msg(" num_of_vlans: %u\n",
127
flower->key_num_of_vlans);
128
129
for (i = 0; i < flower->_count.act; i++) {
130
a = &flower->act[i];
131
ksft_print_msg("action order: %i %s ", i + 1, a->kind);
132
if (a->options._present.vlan)
133
print_vlan(&a->options.vlan);
134
else if (a->options._present.gact)
135
print_gact(&a->options.gact);
136
printf("\n");
137
}
138
}
139
140
static void tc_filter_print(struct __test_metadata *_metadata,
141
struct tc_gettfilter_rsp *f)
142
{
143
struct tc_options_msg *opt = &f->options;
144
145
if (opt->_present.flower) {
146
EXPECT_TRUE((bool)f->_len.kind);
147
flower_print(&opt->flower, f->kind);
148
} else if (f->_len.kind) {
149
ksft_print_msg("%s pref %u proto: %#x\n", f->kind,
150
(f->_hdr.tcm_info >> 16),
151
ntohs(TC_H_MIN(f->_hdr.tcm_info)));
152
}
153
}
154
155
static int tc_clsact_add(struct ynl_sock *ys, int ifi)
156
{
157
struct tc_newqdisc_req *req;
158
int ret;
159
160
req = tc_newqdisc_req_alloc();
161
if (!req)
162
return -1;
163
memset(req, 0, sizeof(*req));
164
165
req->_hdr.tcm_ifindex = ifi;
166
req->_hdr.tcm_parent = TC_H_CLSACT;
167
req->_hdr.tcm_handle = TC_HANDLE;
168
tc_newqdisc_req_set_nlflags(req,
169
NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE);
170
tc_newqdisc_req_set_kind(req, "clsact");
171
172
ret = tc_newqdisc(ys, req);
173
tc_newqdisc_req_free(req);
174
175
return ret;
176
}
177
178
static int tc_clsact_del(struct ynl_sock *ys, int ifi)
179
{
180
struct tc_delqdisc_req *req;
181
int ret;
182
183
req = tc_delqdisc_req_alloc();
184
if (!req)
185
return -1;
186
memset(req, 0, sizeof(*req));
187
188
req->_hdr.tcm_ifindex = ifi;
189
req->_hdr.tcm_parent = TC_H_CLSACT;
190
req->_hdr.tcm_handle = TC_HANDLE;
191
tc_delqdisc_req_set_nlflags(req, NLM_F_REQUEST);
192
193
ret = tc_delqdisc(ys, req);
194
tc_delqdisc_req_free(req);
195
196
return ret;
197
}
198
199
static int tc_filter_add(struct ynl_sock *ys, int ifi)
200
{
201
struct tc_newtfilter_req *req;
202
struct tc_act_attrs *acts;
203
struct tc_vlan p = {
204
.action = TC_ACT_PIPE,
205
.v_action = TCA_VLAN_ACT_PUSH
206
};
207
int ret;
208
209
req = tc_newtfilter_req_alloc();
210
if (!req)
211
return -1;
212
memset(req, 0, sizeof(*req));
213
214
acts = tc_act_attrs_alloc(3);
215
if (!acts) {
216
tc_newtfilter_req_free(req);
217
return -1;
218
}
219
memset(acts, 0, sizeof(*acts) * 3);
220
221
req->_hdr.tcm_ifindex = ifi;
222
req->_hdr.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS);
223
req->_hdr.tcm_info = TC_H_MAKE(1 << 16, htons(ETH_P_8021Q));
224
req->chain = 0;
225
226
tc_newtfilter_req_set_nlflags(req, NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE);
227
tc_newtfilter_req_set_kind(req, "flower");
228
tc_newtfilter_req_set_options_flower_key_vlan_id(req, 100);
229
tc_newtfilter_req_set_options_flower_key_vlan_prio(req, 5);
230
tc_newtfilter_req_set_options_flower_key_num_of_vlans(req, 3);
231
232
__tc_newtfilter_req_set_options_flower_act(req, acts, 3);
233
234
/* Skip action at index 0 because in TC, the action array
235
* index starts at 1, with each index defining the action's
236
* order. In contrast, in YNL indexed arrays start at index 0.
237
*/
238
tc_act_attrs_set_kind(&acts[1], "vlan");
239
tc_act_attrs_set_options_vlan_parms(&acts[1], &p, sizeof(p));
240
tc_act_attrs_set_options_vlan_push_vlan_id(&acts[1], 200);
241
tc_act_attrs_set_kind(&acts[2], "vlan");
242
tc_act_attrs_set_options_vlan_parms(&acts[2], &p, sizeof(p));
243
tc_act_attrs_set_options_vlan_push_vlan_id(&acts[2], 300);
244
245
tc_newtfilter_req_set_options_flower_flags(req, 0);
246
tc_newtfilter_req_set_options_flower_key_eth_type(req, htons(0x8100));
247
248
ret = tc_newtfilter(ys, req);
249
tc_newtfilter_req_free(req);
250
251
return ret;
252
}
253
254
static int tc_filter_del(struct ynl_sock *ys, int ifi)
255
{
256
struct tc_deltfilter_req *req;
257
int ret;
258
259
req = tc_deltfilter_req_alloc();
260
if (!req)
261
return -1;
262
memset(req, 0, sizeof(*req));
263
264
req->_hdr.tcm_ifindex = ifi;
265
req->_hdr.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS);
266
req->_hdr.tcm_info = TC_H_MAKE(1 << 16, htons(ETH_P_8021Q));
267
tc_deltfilter_req_set_nlflags(req, NLM_F_REQUEST);
268
269
ret = tc_deltfilter(ys, req);
270
tc_deltfilter_req_free(req);
271
272
return ret;
273
}
274
275
FIXTURE(tc)
276
{
277
struct ynl_sock *ys;
278
int ifindex;
279
};
280
281
FIXTURE_SETUP(tc)
282
{
283
struct ynl_error yerr;
284
int ret;
285
286
ret = unshare(CLONE_NEWNET);
287
ASSERT_EQ(0, ret);
288
289
self->ifindex = 1; /* loopback */
290
291
self->ys = ynl_sock_create(&ynl_tc_family, &yerr);
292
ASSERT_NE(NULL, self->ys) {
293
TH_LOG("failed to create tc socket: %s", yerr.msg);
294
}
295
}
296
297
FIXTURE_TEARDOWN(tc)
298
{
299
ynl_sock_destroy(self->ys);
300
}
301
302
TEST_F(tc, qdisc)
303
{
304
struct tc_getqdisc_req_dump *dreq;
305
struct tc_newqdisc_req *add_req;
306
struct tc_delqdisc_req *del_req;
307
struct tc_getqdisc_list *rsp;
308
bool found = false;
309
int ret;
310
311
add_req = tc_newqdisc_req_alloc();
312
ASSERT_NE(NULL, add_req);
313
memset(add_req, 0, sizeof(*add_req));
314
315
add_req->_hdr.tcm_ifindex = self->ifindex;
316
add_req->_hdr.tcm_parent = TC_H_ROOT;
317
tc_newqdisc_req_set_nlflags(add_req,
318
NLM_F_REQUEST | NLM_F_CREATE);
319
tc_newqdisc_req_set_kind(add_req, "fq_codel");
320
321
ret = tc_newqdisc(self->ys, add_req);
322
tc_newqdisc_req_free(add_req);
323
ASSERT_EQ(0, ret) {
324
TH_LOG("qdisc add failed: %s", self->ys->err.msg);
325
}
326
327
dreq = tc_getqdisc_req_dump_alloc();
328
ASSERT_NE(NULL, dreq);
329
rsp = tc_getqdisc_dump(self->ys, dreq);
330
tc_getqdisc_req_dump_free(dreq);
331
ASSERT_NE(NULL, rsp) {
332
TH_LOG("dump failed: %s", self->ys->err.msg);
333
}
334
ASSERT_FALSE(ynl_dump_empty(rsp));
335
336
ynl_dump_foreach(rsp, qdisc) {
337
found |= tc_qdisc_print(_metadata, qdisc);
338
}
339
tc_getqdisc_list_free(rsp);
340
EXPECT_TRUE(found);
341
342
del_req = tc_delqdisc_req_alloc();
343
ASSERT_NE(NULL, del_req);
344
memset(del_req, 0, sizeof(*del_req));
345
346
del_req->_hdr.tcm_ifindex = self->ifindex;
347
del_req->_hdr.tcm_parent = TC_H_ROOT;
348
tc_delqdisc_req_set_nlflags(del_req, NLM_F_REQUEST);
349
350
ret = tc_delqdisc(self->ys, del_req);
351
tc_delqdisc_req_free(del_req);
352
EXPECT_EQ(0, ret) {
353
TH_LOG("qdisc del failed: %s", self->ys->err.msg);
354
}
355
}
356
357
TEST_F(tc, flower)
358
{
359
struct tc_gettfilter_req_dump *dreq;
360
struct tc_gettfilter_list *rsp;
361
bool found = false;
362
int ret;
363
364
ret = tc_clsact_add(self->ys, self->ifindex);
365
if (ret)
366
SKIP(return, "clsact not supported: %s", self->ys->err.msg);
367
368
ret = tc_filter_add(self->ys, self->ifindex);
369
ASSERT_EQ(0, ret) {
370
TH_LOG("filter add failed: %s", self->ys->err.msg);
371
}
372
373
dreq = tc_gettfilter_req_dump_alloc();
374
ASSERT_NE(NULL, dreq);
375
memset(dreq, 0, sizeof(*dreq));
376
dreq->_hdr.tcm_ifindex = self->ifindex;
377
dreq->_hdr.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS);
378
dreq->_present.chain = 1;
379
dreq->chain = 0;
380
381
rsp = tc_gettfilter_dump(self->ys, dreq);
382
tc_gettfilter_req_dump_free(dreq);
383
ASSERT_NE(NULL, rsp) {
384
TH_LOG("filter dump failed: %s", self->ys->err.msg);
385
}
386
387
ynl_dump_foreach(rsp, flt) {
388
tc_filter_print(_metadata, flt);
389
if (flt->options._present.flower) {
390
EXPECT_EQ(100, flt->options.flower.key_vlan_id);
391
EXPECT_EQ(5, flt->options.flower.key_vlan_prio);
392
found = true;
393
}
394
}
395
tc_gettfilter_list_free(rsp);
396
EXPECT_TRUE(found);
397
398
ret = tc_filter_del(self->ys, self->ifindex);
399
EXPECT_EQ(0, ret) {
400
TH_LOG("filter del failed: %s", self->ys->err.msg);
401
}
402
403
ret = tc_clsact_del(self->ys, self->ifindex);
404
EXPECT_EQ(0, ret) {
405
TH_LOG("clsact del failed: %s", self->ys->err.msg);
406
}
407
}
408
409
TEST_HARNESS_MAIN
410
411