lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <20251106151529.453026-2-zahari.doychev@linux.com>
Date: Thu,  6 Nov 2025 16:15:27 +0100
From: Zahari Doychev <zahari.doychev@...ux.com>
To: donald.hunter@...il.com,
	kuba@...nel.org
Cc: davem@...emloft.net,
	edumazet@...gle.com,
	pabeni@...hat.com,
	horms@...nel.org,
	jacob.e.keller@...el.com,
	ast@...erby.net,
	matttbe@...nel.org,
	netdev@...r.kernel.org,
	jhs@...atatu.com,
	xiyou.wangcong@...il.com,
	jiri@...nulli.us,
	johannes@...solutions.net,
	zahari.doychev@...ux.com
Subject: [PATCH v2 1/3] ynl: samples: add tc filter example

Add a sample tool demonstrating how to add, dump, and delete a
flower filter with two VLAN push actions. The example can be
invoked as:

  # ./tc-filter-add p2

   flower pref 2211
   flower vlan_id: 255
   action order: 1 vlan push id 255
   action order: 2 vlan push id 555

This verifies correct handling of tc action attributes for
multiple VLAN push actions.

Signed-off-by: Zahari Doychev <zahari.doychev@...ux.com>
---
 tools/net/ynl/Makefile.deps           |   1 +
 tools/net/ynl/samples/.gitignore      |   1 +
 tools/net/ynl/samples/tc-filter-add.c | 308 ++++++++++++++++++++++++++
 3 files changed, 310 insertions(+)
 create mode 100644 tools/net/ynl/samples/tc-filter-add.c

diff --git a/tools/net/ynl/Makefile.deps b/tools/net/ynl/Makefile.deps
index 865fd2e8519e..96c390af060e 100644
--- a/tools/net/ynl/Makefile.deps
+++ b/tools/net/ynl/Makefile.deps
@@ -47,4 +47,5 @@ CFLAGS_tc:= $(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \
 	$(call get_hdr_inc,_TC_MIRRED_H,tc_act/tc_mirred.h) \
 	$(call get_hdr_inc,_TC_SKBEDIT_H,tc_act/tc_skbedit.h) \
 	$(call get_hdr_inc,_TC_TUNNEL_KEY_H,tc_act/tc_tunnel_key.h)
+CFLAGS_tc-filter-add:=$(CFLAGS_tc)
 CFLAGS_tcp_metrics:=$(call get_hdr_inc,_LINUX_TCP_METRICS_H,tcp_metrics.h)
diff --git a/tools/net/ynl/samples/.gitignore b/tools/net/ynl/samples/.gitignore
index 7f5fca7682d7..05087ee323ba 100644
--- a/tools/net/ynl/samples/.gitignore
+++ b/tools/net/ynl/samples/.gitignore
@@ -7,3 +7,4 @@ rt-addr
 rt-link
 rt-route
 tc
+tc-filter-add
diff --git a/tools/net/ynl/samples/tc-filter-add.c b/tools/net/ynl/samples/tc-filter-add.c
new file mode 100644
index 000000000000..297f8151ca86
--- /dev/null
+++ b/tools/net/ynl/samples/tc-filter-add.c
@@ -0,0 +1,308 @@
+// 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 ", kind);
+
+	if (flower->_present.key_vlan_id)
+		printf("vlan_id: %u\n", flower->key_vlan_id);
+
+	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\n", f->kind, (f->_hdr.tcm_info >> 16));
+}
+
+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 = { .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(2);
+	if (!acts) {
+		fprintf(stderr, "tc_act_attrs_alloc\n");
+		tc_newtfilter_req_free(req);
+		return -1;
+	}
+	memset(acts, 0, sizeof(*acts) * 2);
+
+	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, 255);
+	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, 2);
+
+	tc_act_attrs_set_kind(&acts[0], "vlan");
+	tc_act_attrs_set_options_vlan_parms(&acts[0], &p, sizeof(p));
+	tc_act_attrs_set_options_vlan_push_vlan_id(&acts[0], 255);
+	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], 555);
+
+	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 failedq\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;
+}
+
+int main(int argc, char **argv)
+{
+	struct ynl_error yerr;
+	struct ynl_sock *ys;
+	int ifi;
+
+	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))
+		goto err_destroy;
+
+	if (tc_filter_add(ys, ifi))
+		goto err_destroy;
+
+	if (tc_filter_show(ys, ifi))
+		goto err_destroy;
+
+	tc_filter_del(ys, ifi);
+	tc_clsact_del(ys, ifi);
+
+err_destroy:
+	ynl_sock_destroy(ys);
+	return 2;
+}
-- 
2.51.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ