From: Jakub Kicinski <kuba@kernel.org>
To: davem@davemloft.net
Cc: netdev@vger.kernel.org, edumazet@google.com, pabeni@redhat.com,
andrew+netdev@lunn.ch, horms@kernel.org, donald.hunter@gmail.com,
liuhangbin@gmail.com, matttbe@kernel.org,
Jakub Kicinski <kuba@kernel.org>
Subject: [PATCH net-next v2 05/10] tools: ynl: convert tc and tc-filter-add samples to selftest
Date: Fri, 6 Mar 2026 19:36:25 -0800 [thread overview]
Message-ID: <20260307033630.1396085-6-kuba@kernel.org> (raw)
In-Reply-To: <20260307033630.1396085-1-kuba@kernel.org>
Convert tc.c and tc-filter-add.c to produce KTAP output with
kselftest_harness. Merge the two tests together. They both
test TC one is testing qdisc and the other classifiers but
they can easily live in a single selftest.
Make the test spawn a new netns, and run the operations on
lo to avoid onerous setup and cleanup.
TAP version 13
1..2
# Starting 2 tests from 1 test cases.
# RUN tc.qdisc ...
# lo: fq_codel limit: 10240p target: 5ms new_flow_cnt: 0
# OK tc.qdisc
ok 1 tc.qdisc
# RUN tc.flower ...
# flower pref 1 proto: 0x8100
# flower:
# vlan_id: 100
# vlan_prio: 5
# num_of_vlans: 3
# action order: 1 vlan push id 200 protocol 0x8100 priority 0
# action order: 2 vlan push id 300 protocol 0x8100 priority 0
# OK tc.flower
ok 2 tc.flower
# PASSED: 2 / 2 tests passed.
# Totals: pass:2 fail:0 xfail:0 xpass:0 skip:0 error:0
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
tools/net/ynl/tests/Makefile | 4 +-
tools/net/ynl/tests/tc-filter-add.c | 335 -----------------------
tools/net/ynl/tests/tc.c | 405 +++++++++++++++++++++++++---
tools/net/ynl/tests/config | 6 +
4 files changed, 374 insertions(+), 376 deletions(-)
delete mode 100644 tools/net/ynl/tests/tc-filter-add.c
diff --git a/tools/net/ynl/tests/Makefile b/tools/net/ynl/tests/Makefile
index 08d1146d91ce..524092a8de7e 100644
--- a/tools/net/ynl/tests/Makefile
+++ b/tools/net/ynl/tests/Makefile
@@ -22,6 +22,7 @@ TEST_GEN_PROGS := \
netdev \
ovs \
rt-link \
+ tc \
# end of TEST_GEN_PROGS
BINS := \
@@ -29,13 +30,10 @@ BINS := \
ethtool \
rt-addr \
rt-route \
- tc \
- tc-filter-add \
# end of BINS
CFLAGS_netdev:=$(CFLAGS_netdev) $(CFLAGS_rt-link)
CFLAGS_ovs:=$(CFLAGS_ovs_datapath)
-CFLAGS_tc-filter-add:=$(CFLAGS_tc)
include $(wildcard *.d)
diff --git a/tools/net/ynl/tests/tc-filter-add.c b/tools/net/ynl/tests/tc-filter-add.c
deleted file mode 100644
index 97871e9e9edc..000000000000
--- a/tools/net/ynl/tests/tc-filter-add.c
+++ /dev/null
@@ -1,335 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <arpa/inet.h>
-#include <linux/pkt_sched.h>
-#include <linux/tc_act/tc_vlan.h>
-#include <linux/tc_act/tc_gact.h>
-#include <linux/if_ether.h>
-#include <net/if.h>
-
-#include <ynl.h>
-
-#include "tc-user.h"
-
-#define TC_HANDLE (0xFFFF << 16)
-
-const char *vlan_act_name(struct tc_vlan *p)
-{
- switch (p->v_action) {
- case TCA_VLAN_ACT_POP:
- return "pop";
- case TCA_VLAN_ACT_PUSH:
- return "push";
- case TCA_VLAN_ACT_MODIFY:
- return "modify";
- default:
- break;
- }
-
- return "not supported";
-}
-
-const char *gact_act_name(struct tc_gact *p)
-{
- switch (p->action) {
- case TC_ACT_SHOT:
- return "drop";
- case TC_ACT_OK:
- return "ok";
- case TC_ACT_PIPE:
- return "pipe";
- default:
- break;
- }
-
- return "not supported";
-}
-
-static void print_vlan(struct tc_act_vlan_attrs *vlan)
-{
- printf("%s ", vlan_act_name(vlan->parms));
- if (vlan->_present.push_vlan_id)
- printf("id %u ", vlan->push_vlan_id);
- if (vlan->_present.push_vlan_protocol)
- printf("protocol %#x ", ntohs(vlan->push_vlan_protocol));
- if (vlan->_present.push_vlan_priority)
- printf("priority %u ", vlan->push_vlan_priority);
-}
-
-static void print_gact(struct tc_act_gact_attrs *gact)
-{
- struct tc_gact *p = gact->parms;
-
- printf("%s ", gact_act_name(p));
-}
-
-static void flower_print(struct tc_flower_attrs *flower, const char *kind)
-{
- struct tc_act_attrs *a;
- unsigned int i;
-
- printf("%s:\n", kind);
-
- if (flower->_present.key_vlan_id)
- printf(" vlan_id: %u\n", flower->key_vlan_id);
- if (flower->_present.key_vlan_prio)
- printf(" vlan_prio: %u\n", flower->key_vlan_prio);
- if (flower->_present.key_num_of_vlans)
- printf(" num_of_vlans: %u\n", flower->key_num_of_vlans);
-
- for (i = 0; i < flower->_count.act; i++) {
- a = &flower->act[i];
- printf("action order: %i %s ", i + 1, a->kind);
- if (a->options._present.vlan)
- print_vlan(&a->options.vlan);
- else if (a->options._present.gact)
- print_gact(&a->options.gact);
- printf("\n");
- }
- printf("\n");
-}
-
-static void tc_filter_print(struct tc_gettfilter_rsp *f)
-{
- struct tc_options_msg *opt = &f->options;
-
- if (opt->_present.flower)
- flower_print(&opt->flower, f->kind);
- else if (f->_len.kind)
- printf("%s pref %u proto: %#x\n", f->kind,
- (f->_hdr.tcm_info >> 16),
- ntohs(TC_H_MIN(f->_hdr.tcm_info)));
-}
-
-static int tc_filter_add(struct ynl_sock *ys, int ifi)
-{
- struct tc_newtfilter_req *req;
- struct tc_act_attrs *acts;
- struct tc_vlan p = {
- .action = TC_ACT_PIPE,
- .v_action = TCA_VLAN_ACT_PUSH
- };
- __u16 flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE;
- int ret;
-
- req = tc_newtfilter_req_alloc();
- if (!req) {
- fprintf(stderr, "tc_newtfilter_req_alloc failed\n");
- return -1;
- }
- memset(req, 0, sizeof(*req));
-
- acts = tc_act_attrs_alloc(3);
- if (!acts) {
- fprintf(stderr, "tc_act_attrs_alloc\n");
- tc_newtfilter_req_free(req);
- return -1;
- }
- memset(acts, 0, sizeof(*acts) * 3);
-
- req->_hdr.tcm_ifindex = ifi;
- req->_hdr.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS);
- req->_hdr.tcm_info = TC_H_MAKE(1 << 16, htons(ETH_P_8021Q));
- req->chain = 0;
-
- tc_newtfilter_req_set_nlflags(req, flags);
- tc_newtfilter_req_set_kind(req, "flower");
- tc_newtfilter_req_set_options_flower_key_vlan_id(req, 100);
- tc_newtfilter_req_set_options_flower_key_vlan_prio(req, 5);
- tc_newtfilter_req_set_options_flower_key_num_of_vlans(req, 3);
-
- __tc_newtfilter_req_set_options_flower_act(req, acts, 3);
-
- /* Skip action at index 0 because in TC, the action array
- * index starts at 1, with each index defining the action's
- * order. In contrast, in YNL indexed arrays start at index 0.
- */
- tc_act_attrs_set_kind(&acts[1], "vlan");
- tc_act_attrs_set_options_vlan_parms(&acts[1], &p, sizeof(p));
- tc_act_attrs_set_options_vlan_push_vlan_id(&acts[1], 200);
- tc_act_attrs_set_kind(&acts[2], "vlan");
- tc_act_attrs_set_options_vlan_parms(&acts[2], &p, sizeof(p));
- tc_act_attrs_set_options_vlan_push_vlan_id(&acts[2], 300);
-
- tc_newtfilter_req_set_options_flower_flags(req, 0);
- tc_newtfilter_req_set_options_flower_key_eth_type(req, htons(0x8100));
-
- ret = tc_newtfilter(ys, req);
- if (ret)
- fprintf(stderr, "tc_newtfilter: %s\n", ys->err.msg);
-
- tc_newtfilter_req_free(req);
-
- return ret;
-}
-
-static int tc_filter_show(struct ynl_sock *ys, int ifi)
-{
- struct tc_gettfilter_req_dump *req;
- struct tc_gettfilter_list *rsp;
-
- req = tc_gettfilter_req_dump_alloc();
- if (!req) {
- fprintf(stderr, "tc_gettfilter_req_dump_alloc failed\n");
- return -1;
- }
- memset(req, 0, sizeof(*req));
-
- req->_hdr.tcm_ifindex = ifi;
- req->_hdr.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS);
- req->_present.chain = 1;
- req->chain = 0;
-
- rsp = tc_gettfilter_dump(ys, req);
- tc_gettfilter_req_dump_free(req);
- if (!rsp) {
- fprintf(stderr, "YNL: %s\n", ys->err.msg);
- return -1;
- }
-
- if (ynl_dump_empty(rsp))
- fprintf(stderr, "Error: no filters reported\n");
- else
- ynl_dump_foreach(rsp, flt) tc_filter_print(flt);
-
- tc_gettfilter_list_free(rsp);
-
- return 0;
-}
-
-static int tc_filter_del(struct ynl_sock *ys, int ifi)
-{
- struct tc_deltfilter_req *req;
- __u16 flags = NLM_F_REQUEST;
- int ret;
-
- req = tc_deltfilter_req_alloc();
- if (!req) {
- fprintf(stderr, "tc_deltfilter_req_alloc failed\n");
- return -1;
- }
- memset(req, 0, sizeof(*req));
-
- req->_hdr.tcm_ifindex = ifi;
- req->_hdr.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS);
- req->_hdr.tcm_info = TC_H_MAKE(1 << 16, htons(ETH_P_8021Q));
- tc_deltfilter_req_set_nlflags(req, flags);
-
- ret = tc_deltfilter(ys, req);
- if (ret)
- fprintf(stderr, "tc_deltfilter failed: %s\n", ys->err.msg);
-
- tc_deltfilter_req_free(req);
-
- return ret;
-}
-
-static int tc_clsact_add(struct ynl_sock *ys, int ifi)
-{
- struct tc_newqdisc_req *req;
- __u16 flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE;
- int ret;
-
- req = tc_newqdisc_req_alloc();
- if (!req) {
- fprintf(stderr, "tc_newqdisc_req_alloc failed\n");
- return -1;
- }
- memset(req, 0, sizeof(*req));
-
- req->_hdr.tcm_ifindex = ifi;
- req->_hdr.tcm_parent = TC_H_CLSACT;
- req->_hdr.tcm_handle = TC_HANDLE;
- tc_newqdisc_req_set_nlflags(req, flags);
- tc_newqdisc_req_set_kind(req, "clsact");
-
- ret = tc_newqdisc(ys, req);
- if (ret)
- fprintf(stderr, "tc_newqdisc failed: %s\n", ys->err.msg);
-
- tc_newqdisc_req_free(req);
-
- return ret;
-}
-
-static int tc_clsact_del(struct ynl_sock *ys, int ifi)
-{
- struct tc_delqdisc_req *req;
- __u16 flags = NLM_F_REQUEST;
- int ret;
-
- req = tc_delqdisc_req_alloc();
- if (!req) {
- fprintf(stderr, "tc_delqdisc_req_alloc failed\n");
- return -1;
- }
- memset(req, 0, sizeof(*req));
-
- req->_hdr.tcm_ifindex = ifi;
- req->_hdr.tcm_parent = TC_H_CLSACT;
- req->_hdr.tcm_handle = TC_HANDLE;
- tc_delqdisc_req_set_nlflags(req, flags);
-
- ret = tc_delqdisc(ys, req);
- if (ret)
- fprintf(stderr, "tc_delqdisc failed: %s\n", ys->err.msg);
-
- tc_delqdisc_req_free(req);
-
- return ret;
-}
-
-static int tc_filter_config(struct ynl_sock *ys, int ifi)
-{
- int ret = 0;
-
- if (tc_filter_add(ys, ifi))
- return -1;
-
- ret = tc_filter_show(ys, ifi);
-
- if (tc_filter_del(ys, ifi))
- return -1;
-
- return ret;
-}
-
-int main(int argc, char **argv)
-{
- struct ynl_error yerr;
- struct ynl_sock *ys;
- int ifi, ret = 0;
-
- if (argc < 2) {
- fprintf(stderr, "Usage: %s <interface_name>\n", argv[0]);
- return 1;
- }
- ifi = if_nametoindex(argv[1]);
- if (!ifi) {
- perror("if_nametoindex");
- return 1;
- }
-
- ys = ynl_sock_create(&ynl_tc_family, &yerr);
- if (!ys) {
- fprintf(stderr, "YNL: %s\n", yerr.msg);
- return 1;
- }
-
- if (tc_clsact_add(ys, ifi)) {
- ret = 2;
- goto err_destroy;
- }
-
- if (tc_filter_config(ys, ifi))
- ret = 3;
-
- if (tc_clsact_del(ys, ifi))
- ret = 4;
-
-err_destroy:
- ynl_sock_destroy(ys);
- return ret;
-}
diff --git a/tools/net/ynl/tests/tc.c b/tools/net/ynl/tests/tc.c
index 0bfff0fdd792..6ff13876578d 100644
--- a/tools/net/ynl/tests/tc.c
+++ b/tools/net/ynl/tests/tc.c
@@ -1,21 +1,34 @@
// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <sched.h>
#include <stdio.h>
#include <string.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <linux/pkt_sched.h>
+#include <linux/tc_act/tc_vlan.h>
+#include <linux/tc_act/tc_gact.h>
+#include <linux/if_ether.h>
+#include <net/if.h>
#include <ynl.h>
-#include <net/if.h>
+#include <kselftest_harness.h>
#include "tc-user.h"
-static void tc_qdisc_print(struct tc_getqdisc_rsp *q)
+#define TC_HANDLE (0xFFFF << 16)
+
+static bool tc_qdisc_print(struct __test_metadata *_metadata,
+ struct tc_getqdisc_rsp *q)
{
+ bool was_fq_codel = false;
char ifname[IF_NAMESIZE];
const char *name;
name = if_indextoname(q->_hdr.tcm_ifindex, ifname);
- if (name)
- printf("%16s: ", name);
+ EXPECT_TRUE((bool)name);
+ ksft_print_msg("%16s: ", name ?: "no-name");
if (q->_len.kind) {
printf("%s ", q->kind);
@@ -27,6 +40,11 @@ static void tc_qdisc_print(struct tc_getqdisc_rsp *q)
fq_codel = &q->options.fq_codel;
stats = q->stats2.app.fq_codel;
+ EXPECT_EQ(true,
+ fq_codel->_present.limit &&
+ fq_codel->_present.target &&
+ q->stats2.app._len.fq_codel);
+
if (fq_codel->_present.limit)
printf("limit: %dp ", fq_codel->limit);
if (fq_codel->_present.target)
@@ -35,46 +53,357 @@ static void tc_qdisc_print(struct tc_getqdisc_rsp *q)
if (q->stats2.app._len.fq_codel)
printf("new_flow_cnt: %d ",
stats->qdisc_stats.new_flow_count);
+ was_fq_codel = true;
}
}
-
printf("\n");
+
+ return was_fq_codel;
}
-int main(int argc, char **argv)
+static const char *vlan_act_name(struct tc_vlan *p)
{
- struct tc_getqdisc_req_dump *req;
- struct tc_getqdisc_list *rsp;
- struct ynl_error yerr;
- struct ynl_sock *ys;
-
- ys = ynl_sock_create(&ynl_tc_family, &yerr);
- if (!ys) {
- fprintf(stderr, "YNL: %s\n", yerr.msg);
- return 1;
+ switch (p->v_action) {
+ case TCA_VLAN_ACT_POP:
+ return "pop";
+ case TCA_VLAN_ACT_PUSH:
+ return "push";
+ case TCA_VLAN_ACT_MODIFY:
+ return "modify";
+ default:
+ break;
}
- req = tc_getqdisc_req_dump_alloc();
- if (!req)
- goto err_destroy;
-
- rsp = tc_getqdisc_dump(ys, req);
- tc_getqdisc_req_dump_free(req);
- if (!rsp)
- goto err_close;
-
- if (ynl_dump_empty(rsp))
- fprintf(stderr, "Error: no addresses reported\n");
- ynl_dump_foreach(rsp, qdisc)
- tc_qdisc_print(qdisc);
- tc_getqdisc_list_free(rsp);
-
- ynl_sock_destroy(ys);
- return 0;
-
-err_close:
- fprintf(stderr, "YNL: %s\n", ys->err.msg);
-err_destroy:
- ynl_sock_destroy(ys);
- return 2;
+ return "not supported";
}
+
+static const char *gact_act_name(struct tc_gact *p)
+{
+ switch (p->action) {
+ case TC_ACT_SHOT:
+ return "drop";
+ case TC_ACT_OK:
+ return "ok";
+ case TC_ACT_PIPE:
+ return "pipe";
+ default:
+ break;
+ }
+
+ return "not supported";
+}
+
+static void print_vlan(struct tc_act_vlan_attrs *vlan)
+{
+ printf("%s ", vlan_act_name(vlan->parms));
+ if (vlan->_present.push_vlan_id)
+ printf("id %u ", vlan->push_vlan_id);
+ if (vlan->_present.push_vlan_protocol)
+ printf("protocol %#x ", ntohs(vlan->push_vlan_protocol));
+ if (vlan->_present.push_vlan_priority)
+ printf("priority %u ", vlan->push_vlan_priority);
+}
+
+static void print_gact(struct tc_act_gact_attrs *gact)
+{
+ struct tc_gact *p = gact->parms;
+
+ printf("%s ", gact_act_name(p));
+}
+
+static void flower_print(struct tc_flower_attrs *flower, const char *kind)
+{
+ struct tc_act_attrs *a;
+ unsigned int i;
+
+ ksft_print_msg("%s:\n", kind);
+
+ if (flower->_present.key_vlan_id)
+ ksft_print_msg(" vlan_id: %u\n", flower->key_vlan_id);
+ if (flower->_present.key_vlan_prio)
+ ksft_print_msg(" vlan_prio: %u\n", flower->key_vlan_prio);
+ if (flower->_present.key_num_of_vlans)
+ ksft_print_msg(" num_of_vlans: %u\n",
+ flower->key_num_of_vlans);
+
+ for (i = 0; i < flower->_count.act; i++) {
+ a = &flower->act[i];
+ ksft_print_msg("action order: %i %s ", i + 1, a->kind);
+ if (a->options._present.vlan)
+ print_vlan(&a->options.vlan);
+ else if (a->options._present.gact)
+ print_gact(&a->options.gact);
+ printf("\n");
+ }
+}
+
+static void tc_filter_print(struct __test_metadata *_metadata,
+ struct tc_gettfilter_rsp *f)
+{
+ struct tc_options_msg *opt = &f->options;
+
+ if (opt->_present.flower) {
+ EXPECT_TRUE((bool)f->_len.kind);
+ flower_print(&opt->flower, f->kind);
+ } else if (f->_len.kind) {
+ ksft_print_msg("%s pref %u proto: %#x\n", f->kind,
+ (f->_hdr.tcm_info >> 16),
+ ntohs(TC_H_MIN(f->_hdr.tcm_info)));
+ }
+}
+
+static int tc_clsact_add(struct ynl_sock *ys, int ifi)
+{
+ struct tc_newqdisc_req *req;
+ int ret;
+
+ req = tc_newqdisc_req_alloc();
+ if (!req)
+ return -1;
+ memset(req, 0, sizeof(*req));
+
+ req->_hdr.tcm_ifindex = ifi;
+ req->_hdr.tcm_parent = TC_H_CLSACT;
+ req->_hdr.tcm_handle = TC_HANDLE;
+ tc_newqdisc_req_set_nlflags(req,
+ NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE);
+ tc_newqdisc_req_set_kind(req, "clsact");
+
+ ret = tc_newqdisc(ys, req);
+ tc_newqdisc_req_free(req);
+
+ return ret;
+}
+
+static int tc_clsact_del(struct ynl_sock *ys, int ifi)
+{
+ struct tc_delqdisc_req *req;
+ int ret;
+
+ req = tc_delqdisc_req_alloc();
+ if (!req)
+ return -1;
+ memset(req, 0, sizeof(*req));
+
+ req->_hdr.tcm_ifindex = ifi;
+ req->_hdr.tcm_parent = TC_H_CLSACT;
+ req->_hdr.tcm_handle = TC_HANDLE;
+ tc_delqdisc_req_set_nlflags(req, NLM_F_REQUEST);
+
+ ret = tc_delqdisc(ys, req);
+ tc_delqdisc_req_free(req);
+
+ return ret;
+}
+
+static int tc_filter_add(struct ynl_sock *ys, int ifi)
+{
+ struct tc_newtfilter_req *req;
+ struct tc_act_attrs *acts;
+ struct tc_vlan p = {
+ .action = TC_ACT_PIPE,
+ .v_action = TCA_VLAN_ACT_PUSH
+ };
+ int ret;
+
+ req = tc_newtfilter_req_alloc();
+ if (!req)
+ return -1;
+ memset(req, 0, sizeof(*req));
+
+ acts = tc_act_attrs_alloc(3);
+ if (!acts) {
+ tc_newtfilter_req_free(req);
+ return -1;
+ }
+ memset(acts, 0, sizeof(*acts) * 3);
+
+ req->_hdr.tcm_ifindex = ifi;
+ req->_hdr.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS);
+ req->_hdr.tcm_info = TC_H_MAKE(1 << 16, htons(ETH_P_8021Q));
+ req->chain = 0;
+
+ tc_newtfilter_req_set_nlflags(req, NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE);
+ tc_newtfilter_req_set_kind(req, "flower");
+ tc_newtfilter_req_set_options_flower_key_vlan_id(req, 100);
+ tc_newtfilter_req_set_options_flower_key_vlan_prio(req, 5);
+ tc_newtfilter_req_set_options_flower_key_num_of_vlans(req, 3);
+
+ __tc_newtfilter_req_set_options_flower_act(req, acts, 3);
+
+ /* Skip action at index 0 because in TC, the action array
+ * index starts at 1, with each index defining the action's
+ * order. In contrast, in YNL indexed arrays start at index 0.
+ */
+ tc_act_attrs_set_kind(&acts[1], "vlan");
+ tc_act_attrs_set_options_vlan_parms(&acts[1], &p, sizeof(p));
+ tc_act_attrs_set_options_vlan_push_vlan_id(&acts[1], 200);
+ tc_act_attrs_set_kind(&acts[2], "vlan");
+ tc_act_attrs_set_options_vlan_parms(&acts[2], &p, sizeof(p));
+ tc_act_attrs_set_options_vlan_push_vlan_id(&acts[2], 300);
+
+ tc_newtfilter_req_set_options_flower_flags(req, 0);
+ tc_newtfilter_req_set_options_flower_key_eth_type(req, htons(0x8100));
+
+ ret = tc_newtfilter(ys, req);
+ tc_newtfilter_req_free(req);
+
+ return ret;
+}
+
+static int tc_filter_del(struct ynl_sock *ys, int ifi)
+{
+ struct tc_deltfilter_req *req;
+ int ret;
+
+ req = tc_deltfilter_req_alloc();
+ if (!req)
+ return -1;
+ memset(req, 0, sizeof(*req));
+
+ req->_hdr.tcm_ifindex = ifi;
+ req->_hdr.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS);
+ req->_hdr.tcm_info = TC_H_MAKE(1 << 16, htons(ETH_P_8021Q));
+ tc_deltfilter_req_set_nlflags(req, NLM_F_REQUEST);
+
+ ret = tc_deltfilter(ys, req);
+ tc_deltfilter_req_free(req);
+
+ return ret;
+}
+
+FIXTURE(tc)
+{
+ struct ynl_sock *ys;
+ int ifindex;
+};
+
+FIXTURE_SETUP(tc)
+{
+ struct ynl_error yerr;
+ int ret;
+
+ ret = unshare(CLONE_NEWNET);
+ ASSERT_EQ(0, ret);
+
+ self->ifindex = 1; /* loopback */
+
+ self->ys = ynl_sock_create(&ynl_tc_family, &yerr);
+ ASSERT_NE(NULL, self->ys) {
+ TH_LOG("failed to create tc socket: %s", yerr.msg);
+ }
+}
+
+FIXTURE_TEARDOWN(tc)
+{
+ ynl_sock_destroy(self->ys);
+}
+
+TEST_F(tc, qdisc)
+{
+ struct tc_getqdisc_req_dump *dreq;
+ struct tc_newqdisc_req *add_req;
+ struct tc_delqdisc_req *del_req;
+ struct tc_getqdisc_list *rsp;
+ bool found = false;
+ int ret;
+
+ add_req = tc_newqdisc_req_alloc();
+ ASSERT_NE(NULL, add_req);
+ memset(add_req, 0, sizeof(*add_req));
+
+ add_req->_hdr.tcm_ifindex = self->ifindex;
+ add_req->_hdr.tcm_parent = TC_H_ROOT;
+ tc_newqdisc_req_set_nlflags(add_req,
+ NLM_F_REQUEST | NLM_F_CREATE);
+ tc_newqdisc_req_set_kind(add_req, "fq_codel");
+
+ ret = tc_newqdisc(self->ys, add_req);
+ tc_newqdisc_req_free(add_req);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("qdisc add failed: %s", self->ys->err.msg);
+ }
+
+ dreq = tc_getqdisc_req_dump_alloc();
+ ASSERT_NE(NULL, dreq);
+ rsp = tc_getqdisc_dump(self->ys, dreq);
+ tc_getqdisc_req_dump_free(dreq);
+ ASSERT_NE(NULL, rsp) {
+ TH_LOG("dump failed: %s", self->ys->err.msg);
+ }
+ ASSERT_FALSE(ynl_dump_empty(rsp));
+
+ ynl_dump_foreach(rsp, qdisc) {
+ found |= tc_qdisc_print(_metadata, qdisc);
+ }
+ tc_getqdisc_list_free(rsp);
+ EXPECT_TRUE(found);
+
+ del_req = tc_delqdisc_req_alloc();
+ ASSERT_NE(NULL, del_req);
+ memset(del_req, 0, sizeof(*del_req));
+
+ del_req->_hdr.tcm_ifindex = self->ifindex;
+ del_req->_hdr.tcm_parent = TC_H_ROOT;
+ tc_delqdisc_req_set_nlflags(del_req, NLM_F_REQUEST);
+
+ ret = tc_delqdisc(self->ys, del_req);
+ tc_delqdisc_req_free(del_req);
+ EXPECT_EQ(0, ret) {
+ TH_LOG("qdisc del failed: %s", self->ys->err.msg);
+ }
+}
+
+TEST_F(tc, flower)
+{
+ struct tc_gettfilter_req_dump *dreq;
+ struct tc_gettfilter_list *rsp;
+ bool found = false;
+ int ret;
+
+ ret = tc_clsact_add(self->ys, self->ifindex);
+ if (ret)
+ SKIP(return, "clsact not supported: %s", self->ys->err.msg);
+
+ ret = tc_filter_add(self->ys, self->ifindex);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("filter add failed: %s", self->ys->err.msg);
+ }
+
+ dreq = tc_gettfilter_req_dump_alloc();
+ ASSERT_NE(NULL, dreq);
+ memset(dreq, 0, sizeof(*dreq));
+ dreq->_hdr.tcm_ifindex = self->ifindex;
+ dreq->_hdr.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS);
+ dreq->_present.chain = 1;
+ dreq->chain = 0;
+
+ rsp = tc_gettfilter_dump(self->ys, dreq);
+ tc_gettfilter_req_dump_free(dreq);
+ ASSERT_NE(NULL, rsp) {
+ TH_LOG("filter dump failed: %s", self->ys->err.msg);
+ }
+
+ ynl_dump_foreach(rsp, flt) {
+ tc_filter_print(_metadata, flt);
+ if (flt->options._present.flower) {
+ EXPECT_EQ(100, flt->options.flower.key_vlan_id);
+ EXPECT_EQ(5, flt->options.flower.key_vlan_prio);
+ found = true;
+ }
+ }
+ tc_gettfilter_list_free(rsp);
+ EXPECT_TRUE(found);
+
+ ret = tc_filter_del(self->ys, self->ifindex);
+ EXPECT_EQ(0, ret) {
+ TH_LOG("filter del failed: %s", self->ys->err.msg);
+ }
+
+ ret = tc_clsact_del(self->ys, self->ifindex);
+ EXPECT_EQ(0, ret) {
+ TH_LOG("clsact del failed: %s", self->ys->err.msg);
+ }
+}
+
+TEST_HARNESS_MAIN
diff --git a/tools/net/ynl/tests/config b/tools/net/ynl/tests/config
index b4c58d86a6c2..75c0fe72391f 100644
--- a/tools/net/ynl/tests/config
+++ b/tools/net/ynl/tests/config
@@ -1,7 +1,13 @@
CONFIG_DUMMY=m
CONFIG_INET_DIAG=y
CONFIG_IPV6=y
+CONFIG_NET_ACT_VLAN=m
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_CLS_FLOWER=m
+CONFIG_NET_SCH_FQ_CODEL=m
+CONFIG_NET_SCH_INGRESS=m
CONFIG_NET_NS=y
+CONFIG_NET_SCHED=y
CONFIG_NETDEVSIM=m
CONFIG_NETKIT=y
CONFIG_OPENVSWITCH=m
--
2.53.0
next prev parent reply other threads:[~2026-03-07 3:36 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-07 3:36 [PATCH net-next v2 00/10] tools: ynl: convert samples into selftests Jakub Kicinski
2026-03-07 3:36 ` [PATCH net-next v2 01/10] tools: ynl: move samples to tests Jakub Kicinski
2026-03-07 3:36 ` [PATCH net-next v2 02/10] tools: ynl: convert netdev sample to selftest Jakub Kicinski
2026-03-07 3:36 ` [PATCH net-next v2 03/10] tools: ynl: convert ovs " Jakub Kicinski
2026-03-07 3:36 ` [PATCH net-next v2 04/10] tools: ynl: convert rt-link " Jakub Kicinski
2026-03-07 3:36 ` Jakub Kicinski [this message]
2026-03-07 3:36 ` [PATCH net-next v2 06/10] tools: ynl: add netdevsim wrapper library for YNL tests Jakub Kicinski
2026-03-07 3:36 ` [PATCH net-next v2 07/10] tools: ynl: convert devlink sample to selftest Jakub Kicinski
2026-03-07 3:36 ` [PATCH net-next v2 08/10] tools: ynl: convert ethtool " Jakub Kicinski
2026-03-07 3:36 ` [PATCH net-next v2 09/10] tools: ynl: convert rt-addr " Jakub Kicinski
2026-03-07 3:36 ` [PATCH net-next v2 10/10] tools: ynl: convert rt-route " Jakub Kicinski
2026-03-08 17:23 ` [PATCH net-next v2 00/10] tools: ynl: convert samples into selftests Donald Hunter
2026-03-10 0:10 ` patchwork-bot+netdevbpf
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260307033630.1396085-6-kuba@kernel.org \
--to=kuba@kernel.org \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=donald.hunter@gmail.com \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=liuhangbin@gmail.com \
--cc=matttbe@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.