xref: /linux/tools/net/ynl/samples/tc-filter-add.c (revision 84318277d6334c6981ab326d4acc87c6a6ddc9b8)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <stdio.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <arpa/inet.h>
6 #include <linux/pkt_sched.h>
7 #include <linux/tc_act/tc_vlan.h>
8 #include <linux/tc_act/tc_gact.h>
9 #include <linux/if_ether.h>
10 #include <net/if.h>
11 
12 #include <ynl.h>
13 
14 #include "tc-user.h"
15 
16 #define TC_HANDLE (0xFFFF << 16)
17 
18 const char *vlan_act_name(struct tc_vlan *p)
19 {
20 	switch (p->v_action) {
21 	case TCA_VLAN_ACT_POP:
22 		return "pop";
23 	case TCA_VLAN_ACT_PUSH:
24 		return "push";
25 	case TCA_VLAN_ACT_MODIFY:
26 		return "modify";
27 	default:
28 		break;
29 	}
30 
31 	return "not supported";
32 }
33 
34 const char *gact_act_name(struct tc_gact *p)
35 {
36 	switch (p->action) {
37 	case TC_ACT_SHOT:
38 		return "drop";
39 	case TC_ACT_OK:
40 		return "ok";
41 	case TC_ACT_PIPE:
42 		return "pipe";
43 	default:
44 		break;
45 	}
46 
47 	return "not supported";
48 }
49 
50 static void print_vlan(struct tc_act_vlan_attrs *vlan)
51 {
52 	printf("%s ", vlan_act_name(vlan->parms));
53 	if (vlan->_present.push_vlan_id)
54 		printf("id %u ", vlan->push_vlan_id);
55 	if (vlan->_present.push_vlan_protocol)
56 		printf("protocol %#x ", ntohs(vlan->push_vlan_protocol));
57 	if (vlan->_present.push_vlan_priority)
58 		printf("priority %u ", vlan->push_vlan_priority);
59 }
60 
61 static void print_gact(struct tc_act_gact_attrs *gact)
62 {
63 	struct tc_gact *p = gact->parms;
64 
65 	printf("%s ", gact_act_name(p));
66 }
67 
68 static void flower_print(struct tc_flower_attrs *flower, const char *kind)
69 {
70 	struct tc_act_attrs *a;
71 	unsigned int i;
72 
73 	printf("%s:\n", kind);
74 
75 	if (flower->_present.key_vlan_id)
76 		printf("  vlan_id: %u\n", flower->key_vlan_id);
77 	if (flower->_present.key_vlan_prio)
78 		printf("  vlan_prio: %u\n", flower->key_vlan_prio);
79 	if (flower->_present.key_num_of_vlans)
80 		printf("  num_of_vlans: %u\n", flower->key_num_of_vlans);
81 
82 	for (i = 0; i < flower->_count.act; i++) {
83 		a = &flower->act[i];
84 		printf("action order: %i %s ", i + 1, a->kind);
85 		if (a->options._present.vlan)
86 			print_vlan(&a->options.vlan);
87 		else if (a->options._present.gact)
88 			print_gact(&a->options.gact);
89 		printf("\n");
90 	}
91 	printf("\n");
92 }
93 
94 static void tc_filter_print(struct tc_gettfilter_rsp *f)
95 {
96 	struct tc_options_msg *opt = &f->options;
97 
98 	if (opt->_present.flower)
99 		flower_print(&opt->flower, f->kind);
100 	else if (f->_len.kind)
101 		printf("%s pref %u proto: %#x\n", f->kind,
102 		       (f->_hdr.tcm_info >> 16),
103 			ntohs(TC_H_MIN(f->_hdr.tcm_info)));
104 }
105 
106 static int tc_filter_add(struct ynl_sock *ys, int ifi)
107 {
108 	struct tc_newtfilter_req *req;
109 	struct tc_act_attrs *acts;
110 	struct tc_vlan p = {
111 		.action = TC_ACT_PIPE,
112 		.v_action = TCA_VLAN_ACT_PUSH
113 	};
114 	__u16 flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE;
115 	int ret;
116 
117 	req = tc_newtfilter_req_alloc();
118 	if (!req) {
119 		fprintf(stderr, "tc_newtfilter_req_alloc failed\n");
120 		return -1;
121 	}
122 	memset(req, 0, sizeof(*req));
123 
124 	acts = tc_act_attrs_alloc(3);
125 	if (!acts) {
126 		fprintf(stderr, "tc_act_attrs_alloc\n");
127 		tc_newtfilter_req_free(req);
128 		return -1;
129 	}
130 	memset(acts, 0, sizeof(*acts) * 3);
131 
132 	req->_hdr.tcm_ifindex = ifi;
133 	req->_hdr.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS);
134 	req->_hdr.tcm_info = TC_H_MAKE(1 << 16, htons(ETH_P_8021Q));
135 	req->chain = 0;
136 
137 	tc_newtfilter_req_set_nlflags(req, flags);
138 	tc_newtfilter_req_set_kind(req, "flower");
139 	tc_newtfilter_req_set_options_flower_key_vlan_id(req, 100);
140 	tc_newtfilter_req_set_options_flower_key_vlan_prio(req, 5);
141 	tc_newtfilter_req_set_options_flower_key_num_of_vlans(req, 3);
142 
143 	__tc_newtfilter_req_set_options_flower_act(req, acts, 3);
144 
145 	/* Skip action at index 0 because in TC, the action array
146 	 * index starts at 1, with each index defining the action's
147 	 * order. In contrast, in YNL indexed arrays start at index 0.
148 	 */
149 	tc_act_attrs_set_kind(&acts[1], "vlan");
150 	tc_act_attrs_set_options_vlan_parms(&acts[1], &p, sizeof(p));
151 	tc_act_attrs_set_options_vlan_push_vlan_id(&acts[1], 200);
152 	tc_act_attrs_set_kind(&acts[2], "vlan");
153 	tc_act_attrs_set_options_vlan_parms(&acts[2], &p, sizeof(p));
154 	tc_act_attrs_set_options_vlan_push_vlan_id(&acts[2], 300);
155 
156 	tc_newtfilter_req_set_options_flower_flags(req, 0);
157 	tc_newtfilter_req_set_options_flower_key_eth_type(req, htons(0x8100));
158 
159 	ret = tc_newtfilter(ys, req);
160 	if (ret)
161 		fprintf(stderr, "tc_newtfilter: %s\n", ys->err.msg);
162 
163 	tc_newtfilter_req_free(req);
164 
165 	return ret;
166 }
167 
168 static int tc_filter_show(struct ynl_sock *ys, int ifi)
169 {
170 	struct tc_gettfilter_req_dump *req;
171 	struct tc_gettfilter_list *rsp;
172 
173 	req = tc_gettfilter_req_dump_alloc();
174 	if (!req) {
175 		fprintf(stderr, "tc_gettfilter_req_dump_alloc failed\n");
176 		return -1;
177 	}
178 	memset(req, 0, sizeof(*req));
179 
180 	req->_hdr.tcm_ifindex = ifi;
181 	req->_hdr.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS);
182 	req->_present.chain = 1;
183 	req->chain = 0;
184 
185 	rsp = tc_gettfilter_dump(ys, req);
186 	tc_gettfilter_req_dump_free(req);
187 	if (!rsp) {
188 		fprintf(stderr, "YNL: %s\n", ys->err.msg);
189 		return -1;
190 	}
191 
192 	if (ynl_dump_empty(rsp))
193 		fprintf(stderr, "Error: no filters reported\n");
194 	else
195 		ynl_dump_foreach(rsp, flt) tc_filter_print(flt);
196 
197 	tc_gettfilter_list_free(rsp);
198 
199 	return 0;
200 }
201 
202 static int tc_filter_del(struct ynl_sock *ys, int ifi)
203 {
204 	struct tc_deltfilter_req *req;
205 	__u16 flags = NLM_F_REQUEST;
206 	int ret;
207 
208 	req = tc_deltfilter_req_alloc();
209 	if (!req) {
210 		fprintf(stderr, "tc_deltfilter_req_alloc failed\n");
211 		return -1;
212 	}
213 	memset(req, 0, sizeof(*req));
214 
215 	req->_hdr.tcm_ifindex = ifi;
216 	req->_hdr.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS);
217 	req->_hdr.tcm_info = TC_H_MAKE(1 << 16, htons(ETH_P_8021Q));
218 	tc_deltfilter_req_set_nlflags(req, flags);
219 
220 	ret = tc_deltfilter(ys, req);
221 	if (ret)
222 		fprintf(stderr, "tc_deltfilter failed: %s\n", ys->err.msg);
223 
224 	tc_deltfilter_req_free(req);
225 
226 	return ret;
227 }
228 
229 static int tc_clsact_add(struct ynl_sock *ys, int ifi)
230 {
231 	struct tc_newqdisc_req *req;
232 	__u16 flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE;
233 	int ret;
234 
235 	req = tc_newqdisc_req_alloc();
236 	if (!req) {
237 		fprintf(stderr, "tc_newqdisc_req_alloc failed\n");
238 		return -1;
239 	}
240 	memset(req, 0, sizeof(*req));
241 
242 	req->_hdr.tcm_ifindex = ifi;
243 	req->_hdr.tcm_parent = TC_H_CLSACT;
244 	req->_hdr.tcm_handle = TC_HANDLE;
245 	tc_newqdisc_req_set_nlflags(req, flags);
246 	tc_newqdisc_req_set_kind(req, "clsact");
247 
248 	ret = tc_newqdisc(ys, req);
249 	if (ret)
250 		fprintf(stderr, "tc_newqdisc failed: %s\n", ys->err.msg);
251 
252 	tc_newqdisc_req_free(req);
253 
254 	return ret;
255 }
256 
257 static int tc_clsact_del(struct ynl_sock *ys, int ifi)
258 {
259 	struct tc_delqdisc_req *req;
260 	__u16 flags = NLM_F_REQUEST;
261 	int ret;
262 
263 	req = tc_delqdisc_req_alloc();
264 	if (!req) {
265 		fprintf(stderr, "tc_delqdisc_req_alloc failed\n");
266 		return -1;
267 	}
268 	memset(req, 0, sizeof(*req));
269 
270 	req->_hdr.tcm_ifindex = ifi;
271 	req->_hdr.tcm_parent = TC_H_CLSACT;
272 	req->_hdr.tcm_handle = TC_HANDLE;
273 	tc_delqdisc_req_set_nlflags(req, flags);
274 
275 	ret = tc_delqdisc(ys, req);
276 	if (ret)
277 		fprintf(stderr, "tc_delqdisc failed: %s\n", ys->err.msg);
278 
279 	tc_delqdisc_req_free(req);
280 
281 	return ret;
282 }
283 
284 static int tc_filter_config(struct ynl_sock *ys, int ifi)
285 {
286 	int ret = 0;
287 
288 	if (tc_filter_add(ys, ifi))
289 		return -1;
290 
291 	ret = tc_filter_show(ys, ifi);
292 
293 	if (tc_filter_del(ys, ifi))
294 		return -1;
295 
296 	return ret;
297 }
298 
299 int main(int argc, char **argv)
300 {
301 	struct ynl_error yerr;
302 	struct ynl_sock *ys;
303 	int ifi, ret = 0;
304 
305 	if (argc < 2) {
306 		fprintf(stderr, "Usage: %s <interface_name>\n", argv[0]);
307 		return 1;
308 	}
309 	ifi = if_nametoindex(argv[1]);
310 	if (!ifi) {
311 		perror("if_nametoindex");
312 		return 1;
313 	}
314 
315 	ys = ynl_sock_create(&ynl_tc_family, &yerr);
316 	if (!ys) {
317 		fprintf(stderr, "YNL: %s\n", yerr.msg);
318 		return 1;
319 	}
320 
321 	if (tc_clsact_add(ys, ifi)) {
322 		ret = 2;
323 		goto err_destroy;
324 	}
325 
326 	if (tc_filter_config(ys, ifi))
327 		ret = 3;
328 
329 	if (tc_clsact_del(ys, ifi))
330 		ret = 4;
331 
332 err_destroy:
333 	ynl_sock_destroy(ys);
334 	return ret;
335 }
336