]> git.proxmox.com Git - mirror_frr.git/blob - zebra/tc_netlink.c
zebra: Fix build error when `--disable-bfdd`
[mirror_frr.git] / zebra / tc_netlink.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Zebra Traffic Control (TC) interaction with the kernel using netlink.
4 *
5 * Copyright (C) 2022 Shichu Yang
6 */
7
8 #include <zebra.h>
9
10 #ifdef HAVE_NETLINK
11
12 #include <linux/pkt_cls.h>
13 #include <linux/pkt_sched.h>
14 #include <netinet/if_ether.h>
15 #include <sys/socket.h>
16
17 #include "if.h"
18 #include "prefix.h"
19 #include "vrf.h"
20
21 #include "zebra/zserv.h"
22 #include "zebra/zebra_ns.h"
23 #include "zebra/rt.h"
24 #include "zebra/interface.h"
25 #include "zebra/debug.h"
26 #include "zebra/kernel_netlink.h"
27 #include "zebra/tc_netlink.h"
28 #include "zebra/zebra_errors.h"
29 #include "zebra/zebra_dplane.h"
30 #include "zebra/zebra_tc.h"
31 #include "zebra/zebra_trace.h"
32
33 #define TC_FREQ_DEFAULT (100)
34
35 /* some magic number */
36 #define TC_QDISC_MAJOR_ZEBRA (0xbeef0000u)
37 #define TC_MINOR_NOCLASS (0xffffu)
38
39 #define TIME_UNITS_PER_SEC (1000000)
40 #define xmittime(r, s) (TIME_UNITS_PER_SEC * ((double)(s) / (double)(r)))
41
42 static uint32_t tc_get_freq(void)
43 {
44 int freq = 0;
45 FILE *fp = fopen("/proc/net/psched", "r");
46
47 if (fp) {
48 uint32_t nom, denom;
49
50 if (fscanf(fp, "%*08x%*08x%08x%08x", &nom, &denom) == 2) {
51 if (nom == 1000000)
52 freq = denom;
53 }
54 fclose(fp);
55 }
56
57 return freq == 0 ? TC_FREQ_DEFAULT : freq;
58 }
59
60 static void tc_calc_rate_table(struct tc_ratespec *ratespec, uint32_t *table,
61 uint32_t mtu)
62 {
63 if (mtu == 0)
64 mtu = 2047;
65
66 int cell_log = -1;
67
68 if (cell_log < 0) {
69 cell_log = 0;
70 while ((mtu >> cell_log) > 255)
71 cell_log++;
72 }
73
74 for (int i = 0; i < 256; i++)
75 table[i] = xmittime(ratespec->rate, (i + 1) << cell_log);
76
77 ratespec->cell_align = -1;
78 ratespec->cell_log = cell_log;
79 ratespec->linklayer = TC_LINKLAYER_ETHERNET;
80 }
81
82 static int tc_flower_get_inet_prefix(const struct prefix *prefix,
83 struct inet_prefix *addr)
84 {
85 addr->family = prefix->family;
86
87 if (addr->family == AF_INET) {
88 addr->bytelen = 4;
89 addr->bitlen = prefix->prefixlen;
90 addr->flags = 0;
91 addr->flags |= PREFIXLEN_SPECIFIED;
92 addr->flags |= ADDRTYPE_INET;
93 memcpy(addr->data, prefix->u.val32, sizeof(prefix->u.val32));
94 } else if (addr->family == AF_INET6) {
95 addr->bytelen = 16;
96 addr->bitlen = prefix->prefixlen;
97 addr->flags = 0;
98 addr->flags |= PREFIXLEN_SPECIFIED;
99 addr->flags |= ADDRTYPE_INET;
100 memcpy(addr->data, prefix->u.val, sizeof(prefix->u.val));
101 } else {
102 return -1;
103 }
104
105 return 0;
106 }
107
108 static int tc_flower_get_inet_mask(const struct prefix *prefix,
109 struct inet_prefix *addr)
110 {
111 addr->family = prefix->family;
112
113 if (addr->family == AF_INET) {
114 addr->bytelen = 4;
115 addr->bitlen = prefix->prefixlen;
116 addr->flags = 0;
117 addr->flags |= PREFIXLEN_SPECIFIED;
118 addr->flags |= ADDRTYPE_INET;
119 } else if (addr->family == AF_INET6) {
120 addr->bytelen = 16;
121 addr->bitlen = prefix->prefixlen;
122 addr->flags = 0;
123 addr->flags |= PREFIXLEN_SPECIFIED;
124 addr->flags |= ADDRTYPE_INET;
125 } else {
126 return -1;
127 }
128
129 memset(addr->data, 0xff, addr->bytelen);
130
131 int rest = prefix->prefixlen;
132
133 for (int i = 0; i < addr->bytelen / 4; i++) {
134 if (!rest) {
135 addr->data[i] = 0;
136 } else if (rest / 32 >= 1) {
137 rest -= 32;
138 } else {
139 addr->data[i] <<= 32 - rest;
140 addr->data[i] = htonl(addr->data[i]);
141 rest = 0;
142 }
143 }
144
145 return 0;
146 }
147
148 /*
149 * Traffic control queue discipline encoding (only "htb" supported)
150 */
151 static ssize_t netlink_qdisc_msg_encode(int cmd, struct zebra_dplane_ctx *ctx,
152 void *data, size_t datalen)
153 {
154 struct nlsock *nl;
155 const char *kind_str = NULL;
156
157 struct rtattr *nest;
158
159 struct {
160 struct nlmsghdr n;
161 struct tcmsg t;
162 char buf[0];
163 } *req = (void *)data;
164
165 if (datalen < sizeof(*req))
166 return 0;
167
168 nl = kernel_netlink_nlsock_lookup(dplane_ctx_get_ns_sock(ctx));
169
170 memset(req, 0, sizeof(*req));
171
172 req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
173 req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST;
174
175 req->n.nlmsg_flags |= NLM_F_REPLACE;
176
177 req->n.nlmsg_type = cmd;
178
179 req->n.nlmsg_pid = nl->snl.nl_pid;
180
181 req->t.tcm_family = AF_UNSPEC;
182 req->t.tcm_ifindex = dplane_ctx_get_ifindex(ctx);
183 req->t.tcm_info = 0;
184 req->t.tcm_handle = 0;
185 req->t.tcm_parent = TC_H_ROOT;
186
187 if (cmd == RTM_NEWQDISC) {
188 req->t.tcm_handle = TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA, 0);
189
190 kind_str = dplane_ctx_tc_qdisc_get_kind_str(ctx);
191
192 nl_attr_put(&req->n, datalen, TCA_KIND, kind_str,
193 strlen(kind_str) + 1);
194
195 nest = nl_attr_nest(&req->n, datalen, TCA_OPTIONS);
196
197 switch (dplane_ctx_tc_qdisc_get_kind(ctx)) {
198 case TC_QDISC_HTB: {
199 struct tc_htb_glob htb_glob = {
200 .rate2quantum = 10,
201 .version = 3,
202 .defcls = TC_MINOR_NOCLASS};
203 nl_attr_put(&req->n, datalen, TCA_HTB_INIT, &htb_glob,
204 sizeof(htb_glob));
205 break;
206 }
207 case TC_QDISC_NOQUEUE:
208 break;
209 default:
210 break;
211 /* not implemented */
212 }
213
214 nl_attr_nest_end(&req->n, nest);
215 } else {
216 /* ifindex are enough for del/get qdisc */
217 }
218
219 return NLMSG_ALIGN(req->n.nlmsg_len);
220 }
221
222 /*
223 * Traffic control class encoding
224 */
225 static ssize_t netlink_tclass_msg_encode(int cmd, struct zebra_dplane_ctx *ctx,
226 void *data, size_t datalen)
227 {
228 enum dplane_op_e op = dplane_ctx_get_op(ctx);
229
230 struct nlsock *nl;
231 const char *kind_str = NULL;
232
233 struct rtattr *nest;
234
235 struct {
236 struct nlmsghdr n;
237 struct tcmsg t;
238 char buf[0];
239 } *req = (void *)data;
240
241 if (datalen < sizeof(*req))
242 return 0;
243
244 nl = kernel_netlink_nlsock_lookup(dplane_ctx_get_ns_sock(ctx));
245
246 memset(req, 0, sizeof(*req));
247
248 req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
249 req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST;
250
251 if (op == DPLANE_OP_TC_CLASS_UPDATE)
252 req->n.nlmsg_flags |= NLM_F_REPLACE;
253
254 req->n.nlmsg_type = cmd;
255
256 req->n.nlmsg_pid = nl->snl.nl_pid;
257
258 req->t.tcm_family = AF_UNSPEC;
259 req->t.tcm_ifindex = dplane_ctx_get_ifindex(ctx);
260
261 req->t.tcm_handle = TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA,
262 dplane_ctx_tc_class_get_handle(ctx));
263 req->t.tcm_parent = TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA, 0);
264 req->t.tcm_info = 0;
265
266 kind_str = dplane_ctx_tc_class_get_kind_str(ctx);
267
268 if (op == DPLANE_OP_TC_CLASS_ADD || op == DPLANE_OP_TC_CLASS_UPDATE) {
269 zlog_debug("netlink tclass encoder: op: %s kind: %s handle: %u",
270 op == DPLANE_OP_TC_CLASS_UPDATE ? "update" : "add",
271 kind_str, dplane_ctx_tc_class_get_handle(ctx));
272
273 nl_attr_put(&req->n, datalen, TCA_KIND, kind_str,
274 strlen(kind_str) + 1);
275
276 nest = nl_attr_nest(&req->n, datalen, TCA_OPTIONS);
277
278 switch (dplane_ctx_tc_class_get_kind(ctx)) {
279 case TC_QDISC_HTB: {
280 struct tc_htb_opt htb_opt = {};
281
282 uint64_t rate = dplane_ctx_tc_class_get_rate(ctx),
283 ceil = dplane_ctx_tc_class_get_ceil(ctx);
284
285 uint64_t buffer, cbuffer;
286
287 /* TODO: fetch mtu from interface */
288 uint32_t mtu = 1500;
289
290 uint32_t rtab[256];
291 uint32_t ctab[256];
292
293 ceil = MAX(rate, ceil);
294
295 htb_opt.rate.rate = (rate >> 32 != 0) ? ~0U : rate;
296 htb_opt.ceil.rate = (ceil >> 32 != 0) ? ~0U : ceil;
297
298 buffer = rate / tc_get_freq() + mtu;
299 cbuffer = ceil / tc_get_freq() + mtu;
300
301 htb_opt.buffer = buffer;
302 htb_opt.cbuffer = cbuffer;
303
304 tc_calc_rate_table(&htb_opt.rate, rtab, mtu);
305 tc_calc_rate_table(&htb_opt.ceil, ctab, mtu);
306
307 htb_opt.ceil.mpu = htb_opt.rate.mpu = 0;
308 htb_opt.ceil.overhead = htb_opt.rate.overhead = 0;
309
310 if (rate >> 32 != 0) {
311 nl_attr_put(&req->n, datalen, TCA_HTB_RATE64,
312 &rate, sizeof(rate));
313 }
314
315 if (ceil >> 32 != 0) {
316 nl_attr_put(&req->n, datalen, TCA_HTB_CEIL64,
317 &ceil, sizeof(ceil));
318 }
319
320 nl_attr_put(&req->n, datalen, TCA_HTB_PARMS, &htb_opt,
321 sizeof(htb_opt));
322
323 nl_attr_put(&req->n, datalen, TCA_HTB_RTAB, rtab,
324 sizeof(rtab));
325 nl_attr_put(&req->n, datalen, TCA_HTB_CTAB, ctab,
326 sizeof(ctab));
327 break;
328 }
329 default:
330 break;
331 }
332
333 nl_attr_nest_end(&req->n, nest);
334 }
335
336 return NLMSG_ALIGN(req->n.nlmsg_len);
337 }
338
339 static int netlink_tfilter_flower_port_type(uint8_t ip_proto, bool src)
340 {
341 if (ip_proto == IPPROTO_TCP)
342 return src ? TCA_FLOWER_KEY_TCP_SRC : TCA_FLOWER_KEY_TCP_DST;
343 else if (ip_proto == IPPROTO_UDP)
344 return src ? TCA_FLOWER_KEY_UDP_SRC : TCA_FLOWER_KEY_UDP_DST;
345 else if (ip_proto == IPPROTO_SCTP)
346 return src ? TCA_FLOWER_KEY_SCTP_SRC : TCA_FLOWER_KEY_SCTP_DST;
347 else
348 return -1;
349 }
350
351 static void netlink_tfilter_flower_put_options(struct nlmsghdr *n,
352 size_t datalen,
353 struct zebra_dplane_ctx *ctx)
354 {
355 struct inet_prefix addr;
356 uint32_t flags = 0, classid;
357 uint8_t protocol = htons(dplane_ctx_tc_filter_get_eth_proto(ctx));
358 uint32_t filter_bm = dplane_ctx_tc_filter_get_filter_bm(ctx);
359
360 if (filter_bm & TC_FLOWER_SRC_IP) {
361 const struct prefix *src_p =
362 dplane_ctx_tc_filter_get_src_ip(ctx);
363
364 if (tc_flower_get_inet_prefix(src_p, &addr) != 0)
365 return;
366
367 nl_attr_put(n, datalen,
368 (addr.family == AF_INET) ? TCA_FLOWER_KEY_IPV4_SRC
369 : TCA_FLOWER_KEY_IPV6_SRC,
370 addr.data, addr.bytelen);
371
372 if (tc_flower_get_inet_mask(src_p, &addr) != 0)
373 return;
374
375 nl_attr_put(n, datalen,
376 (addr.family == AF_INET)
377 ? TCA_FLOWER_KEY_IPV4_SRC_MASK
378 : TCA_FLOWER_KEY_IPV6_SRC_MASK,
379 addr.data, addr.bytelen);
380 }
381
382 if (filter_bm & TC_FLOWER_DST_IP) {
383 const struct prefix *dst_p =
384 dplane_ctx_tc_filter_get_dst_ip(ctx);
385
386 if (tc_flower_get_inet_prefix(dst_p, &addr) != 0)
387 return;
388
389 nl_attr_put(n, datalen,
390 (addr.family == AF_INET) ? TCA_FLOWER_KEY_IPV4_DST
391 : TCA_FLOWER_KEY_IPV6_DST,
392 addr.data, addr.bytelen);
393
394 if (tc_flower_get_inet_mask(dst_p, &addr) != 0)
395 return;
396
397 nl_attr_put(n, datalen,
398 (addr.family == AF_INET)
399 ? TCA_FLOWER_KEY_IPV4_DST_MASK
400 : TCA_FLOWER_KEY_IPV6_DST_MASK,
401 addr.data, addr.bytelen);
402 }
403
404 if (filter_bm & TC_FLOWER_IP_PROTOCOL) {
405 nl_attr_put8(n, datalen, TCA_FLOWER_KEY_IP_PROTO,
406 dplane_ctx_tc_filter_get_ip_proto(ctx));
407 }
408
409 if (filter_bm & TC_FLOWER_SRC_PORT) {
410 uint16_t min, max;
411
412 min = dplane_ctx_tc_filter_get_src_port_min(ctx);
413 max = dplane_ctx_tc_filter_get_src_port_max(ctx);
414
415 if (max > min) {
416 nl_attr_put16(n, datalen, TCA_FLOWER_KEY_PORT_SRC_MIN,
417 htons(min));
418
419 nl_attr_put16(n, datalen, TCA_FLOWER_KEY_PORT_SRC_MAX,
420 htons(max));
421 } else {
422 int type = netlink_tfilter_flower_port_type(
423 dplane_ctx_tc_filter_get_ip_proto(ctx), true);
424
425 if (type < 0)
426 return;
427
428 nl_attr_put16(n, datalen, type, htons(min));
429 }
430 }
431
432 if (filter_bm & TC_FLOWER_DST_PORT) {
433 uint16_t min = dplane_ctx_tc_filter_get_dst_port_min(ctx),
434 max = dplane_ctx_tc_filter_get_dst_port_max(ctx);
435
436 if (max > min) {
437 nl_attr_put16(n, datalen, TCA_FLOWER_KEY_PORT_DST_MIN,
438 htons(min));
439
440 nl_attr_put16(n, datalen, TCA_FLOWER_KEY_PORT_DST_MAX,
441 htons(max));
442 } else {
443 int type = netlink_tfilter_flower_port_type(
444 dplane_ctx_tc_filter_get_ip_proto(ctx), false);
445
446 if (type < 0)
447 return;
448
449 nl_attr_put16(n, datalen, type, htons(min));
450 }
451 }
452
453 if (filter_bm & TC_FLOWER_DSFIELD) {
454 nl_attr_put8(n, datalen, TCA_FLOWER_KEY_IP_TOS,
455 dplane_ctx_tc_filter_get_dsfield(ctx));
456 nl_attr_put8(n, datalen, TCA_FLOWER_KEY_IP_TOS_MASK,
457 dplane_ctx_tc_filter_get_dsfield_mask(ctx));
458 }
459
460 classid = TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA,
461 dplane_ctx_tc_filter_get_classid(ctx));
462 nl_attr_put32(n, datalen, TCA_FLOWER_CLASSID, classid);
463
464 nl_attr_put32(n, datalen, TCA_FLOWER_FLAGS, flags);
465
466 nl_attr_put16(n, datalen, TCA_FLOWER_KEY_ETH_TYPE, protocol);
467 }
468
469 /*
470 * Traffic control filter encoding
471 */
472 static ssize_t netlink_tfilter_msg_encode(int cmd, struct zebra_dplane_ctx *ctx,
473 void *data, size_t datalen)
474 {
475 enum dplane_op_e op = dplane_ctx_get_op(ctx);
476
477 struct nlsock *nl;
478 const char *kind_str = NULL;
479
480 struct rtattr *nest;
481
482 uint16_t priority;
483 uint16_t protocol;
484
485 struct {
486 struct nlmsghdr n;
487 struct tcmsg t;
488 char buf[0];
489 } *req = (void *)data;
490
491 if (datalen < sizeof(*req))
492 return 0;
493
494 nl = kernel_netlink_nlsock_lookup(dplane_ctx_get_ns_sock(ctx));
495
496 memset(req, 0, sizeof(*req));
497
498 req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
499 req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST;
500
501 if (op == DPLANE_OP_TC_FILTER_UPDATE)
502 req->n.nlmsg_flags |= NLM_F_REPLACE;
503
504 req->n.nlmsg_type = cmd;
505
506 req->n.nlmsg_pid = nl->snl.nl_pid;
507
508 req->t.tcm_family = AF_UNSPEC;
509 req->t.tcm_ifindex = dplane_ctx_get_ifindex(ctx);
510
511 priority = dplane_ctx_tc_filter_get_priority(ctx);
512 protocol = htons(dplane_ctx_tc_filter_get_eth_proto(ctx));
513
514 req->t.tcm_info = TC_H_MAKE(priority << 16, protocol);
515 req->t.tcm_handle = dplane_ctx_tc_filter_get_handle(ctx);
516 req->t.tcm_parent = TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA, 0);
517
518 kind_str = dplane_ctx_tc_filter_get_kind_str(ctx);
519
520 if (op == DPLANE_OP_TC_FILTER_ADD || op == DPLANE_OP_TC_FILTER_UPDATE) {
521 nl_attr_put(&req->n, datalen, TCA_KIND, kind_str,
522 strlen(kind_str) + 1);
523
524 zlog_debug(
525 "netlink tfilter encoder: op: %s priority: %u protocol: %u kind: %s handle: %u filter_bm: %u ip_proto: %u",
526 op == DPLANE_OP_TC_FILTER_UPDATE ? "update" : "add",
527 priority, protocol, kind_str,
528 dplane_ctx_tc_filter_get_handle(ctx),
529 dplane_ctx_tc_filter_get_filter_bm(ctx),
530 dplane_ctx_tc_filter_get_ip_proto(ctx));
531
532 nest = nl_attr_nest(&req->n, datalen, TCA_OPTIONS);
533 switch (dplane_ctx_tc_filter_get_kind(ctx)) {
534 case TC_FILTER_FLOWER: {
535 netlink_tfilter_flower_put_options(&req->n, datalen,
536 ctx);
537 break;
538 }
539 default:
540 break;
541 }
542 nl_attr_nest_end(&req->n, nest);
543 }
544
545 return NLMSG_ALIGN(req->n.nlmsg_len);
546 }
547
548 static ssize_t netlink_newqdisc_msg_encoder(struct zebra_dplane_ctx *ctx,
549 void *buf, size_t buflen)
550 {
551 return netlink_qdisc_msg_encode(RTM_NEWQDISC, ctx, buf, buflen);
552 }
553
554 static ssize_t netlink_delqdisc_msg_encoder(struct zebra_dplane_ctx *ctx,
555 void *buf, size_t buflen)
556 {
557 return netlink_qdisc_msg_encode(RTM_DELQDISC, ctx, buf, buflen);
558 }
559
560 static ssize_t netlink_newtclass_msg_encoder(struct zebra_dplane_ctx *ctx,
561 void *buf, size_t buflen)
562 {
563 return netlink_tclass_msg_encode(RTM_NEWTCLASS, ctx, buf, buflen);
564 }
565
566 static ssize_t netlink_deltclass_msg_encoder(struct zebra_dplane_ctx *ctx,
567 void *buf, size_t buflen)
568 {
569 return netlink_tclass_msg_encode(RTM_DELTCLASS, ctx, buf, buflen);
570 }
571
572 static ssize_t netlink_newtfilter_msg_encoder(struct zebra_dplane_ctx *ctx,
573 void *buf, size_t buflen)
574 {
575 return netlink_tfilter_msg_encode(RTM_NEWTFILTER, ctx, buf, buflen);
576 }
577
578 static ssize_t netlink_deltfilter_msg_encoder(struct zebra_dplane_ctx *ctx,
579 void *buf, size_t buflen)
580 {
581 return netlink_tfilter_msg_encode(RTM_DELTFILTER, ctx, buf, buflen);
582 }
583
584 enum netlink_msg_status
585 netlink_put_tc_qdisc_update_msg(struct nl_batch *bth,
586 struct zebra_dplane_ctx *ctx)
587 {
588 enum dplane_op_e op;
589 enum netlink_msg_status ret;
590
591 op = dplane_ctx_get_op(ctx);
592
593 if (op == DPLANE_OP_TC_QDISC_INSTALL) {
594 ret = netlink_batch_add_msg(
595 bth, ctx, netlink_newqdisc_msg_encoder, false);
596 } else if (op == DPLANE_OP_TC_QDISC_UNINSTALL) {
597 ret = netlink_batch_add_msg(
598 bth, ctx, netlink_delqdisc_msg_encoder, false);
599 } else {
600 return FRR_NETLINK_ERROR;
601 }
602
603 return ret;
604 }
605
606 enum netlink_msg_status
607 netlink_put_tc_class_update_msg(struct nl_batch *bth,
608 struct zebra_dplane_ctx *ctx)
609 {
610 enum dplane_op_e op;
611 enum netlink_msg_status ret;
612
613 op = dplane_ctx_get_op(ctx);
614
615 if (op == DPLANE_OP_TC_CLASS_ADD || op == DPLANE_OP_TC_CLASS_UPDATE) {
616 ret = netlink_batch_add_msg(
617 bth, ctx, netlink_newtclass_msg_encoder, false);
618 } else if (op == DPLANE_OP_TC_CLASS_DELETE) {
619 ret = netlink_batch_add_msg(
620 bth, ctx, netlink_deltclass_msg_encoder, false);
621 } else {
622 return FRR_NETLINK_ERROR;
623 }
624
625 return ret;
626 }
627
628 enum netlink_msg_status
629 netlink_put_tc_filter_update_msg(struct nl_batch *bth,
630 struct zebra_dplane_ctx *ctx)
631 {
632 enum dplane_op_e op;
633 enum netlink_msg_status ret;
634
635 op = dplane_ctx_get_op(ctx);
636
637 if (op == DPLANE_OP_TC_FILTER_ADD) {
638 ret = netlink_batch_add_msg(
639 bth, ctx, netlink_newtfilter_msg_encoder, false);
640 } else if (op == DPLANE_OP_TC_FILTER_UPDATE) {
641 /*
642 * Replace will fail if either filter type or the number of
643 * filter options is changed, so DEL then NEW
644 *
645 * TFILTER may have refs to TCLASS.
646 */
647
648 (void)netlink_batch_add_msg(
649 bth, ctx, netlink_deltfilter_msg_encoder, false);
650 ret = netlink_batch_add_msg(
651 bth, ctx, netlink_newtfilter_msg_encoder, false);
652 } else if (op == DPLANE_OP_TC_FILTER_DELETE) {
653 ret = netlink_batch_add_msg(
654 bth, ctx, netlink_deltfilter_msg_encoder, false);
655 } else {
656 return FRR_NETLINK_ERROR;
657 }
658
659 return ret;
660 }
661
662 /*
663 * Request filters from the kernel
664 */
665 static int netlink_request_filters(struct zebra_ns *zns, int family, int type,
666 ifindex_t ifindex)
667 {
668 struct {
669 struct nlmsghdr n;
670 struct tcmsg tc;
671 } req;
672
673 memset(&req, 0, sizeof(req));
674 req.n.nlmsg_type = type;
675 req.n.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
676 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
677 req.tc.tcm_family = family;
678 req.tc.tcm_ifindex = ifindex;
679
680 return netlink_request(&zns->netlink_cmd, &req);
681 }
682
683 /*
684 * Request queue discipline from the kernel
685 */
686 static int netlink_request_qdiscs(struct zebra_ns *zns, int family, int type)
687 {
688 struct {
689 struct nlmsghdr n;
690 struct tcmsg tc;
691 } req;
692
693 memset(&req, 0, sizeof(req));
694 req.n.nlmsg_type = type;
695 req.n.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
696 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
697 req.tc.tcm_family = family;
698
699 return netlink_request(&zns->netlink_cmd, &req);
700 }
701
702 int netlink_qdisc_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
703 {
704 struct tcmsg *tcm;
705 struct zebra_tc_qdisc qdisc = {};
706
707 int len;
708 struct rtattr *tb[TCA_MAX + 1];
709
710 frrtrace(3, frr_zebra, netlink_tc_qdisc_change, h, ns_id, startup);
711
712 len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct tcmsg));
713
714 if (len < 0) {
715 zlog_err(
716 "%s: Message received from netlink is of a broken size %d %zu",
717 __func__, h->nlmsg_len,
718 (size_t)NLMSG_LENGTH(sizeof(struct tcmsg)));
719 return -1;
720 }
721
722 tcm = NLMSG_DATA(h);
723 netlink_parse_rtattr(tb, TCA_MAX, TCA_RTA(tcm), len);
724
725 const char *kind_str = (const char *)RTA_DATA(tb[TCA_KIND]);
726
727 enum tc_qdisc_kind kind = tc_qdisc_str2kind(kind_str);
728
729 qdisc.qdisc.ifindex = tcm->tcm_ifindex;
730
731 switch (kind) {
732 case TC_QDISC_NOQUEUE:
733 /* "noqueue" is the default qdisc */
734 break;
735 case TC_QDISC_HTB:
736 case TC_QDISC_UNSPEC:
737 break;
738 }
739
740 if (tb[TCA_OPTIONS] != NULL) {
741 struct rtattr *options[TCA_HTB_MAX + 1];
742
743 netlink_parse_rtattr_nested(options, TCA_HTB_MAX,
744 tb[TCA_OPTIONS]);
745
746 /* TODO: more details */
747 /* struct tc_htb_glob *glob = RTA_DATA(options[TCA_HTB_INIT]);
748 */
749 }
750
751 if (h->nlmsg_type == RTM_NEWQDISC) {
752 if (startup &&
753 TC_H_MAJ(tcm->tcm_handle) == TC_QDISC_MAJOR_ZEBRA) {
754 enum zebra_dplane_result ret;
755
756 ret = dplane_tc_qdisc_uninstall(&qdisc);
757
758 zlog_debug("%s: %s leftover qdisc: ifindex %d kind %s",
759 __func__,
760 ((ret == ZEBRA_DPLANE_REQUEST_FAILURE)
761 ? "Failed to remove"
762 : "Removed"),
763 qdisc.qdisc.ifindex, kind_str);
764 }
765 }
766
767 return 0;
768 }
769
770 int netlink_tclass_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
771 {
772 struct tcmsg *tcm;
773
774 int len;
775 struct rtattr *tb[TCA_MAX + 1];
776
777 frrtrace(3, frr_zebra, netlink_tc_class_change, h, ns_id, startup);
778
779 len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct tcmsg));
780
781 if (len < 0) {
782 zlog_err(
783 "%s: Message received from netlink is of a broken size %d %zu",
784 __func__, h->nlmsg_len,
785 (size_t)NLMSG_LENGTH(sizeof(struct tcmsg)));
786 return -1;
787 }
788
789 tcm = NLMSG_DATA(h);
790 netlink_parse_rtattr(tb, TCA_MAX, TCA_RTA(tcm), len);
791
792
793 if (tb[TCA_OPTIONS] != NULL) {
794 struct rtattr *options[TCA_HTB_MAX + 1];
795
796 netlink_parse_rtattr_nested(options, TCA_HTB_MAX,
797 tb[TCA_OPTIONS]);
798
799 /* TODO: more details */
800 /* struct tc_htb_opt *opt = RTA_DATA(options[TCA_HTB_PARMS]); */
801 }
802
803 return 0;
804 }
805
806 int netlink_tfilter_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
807 {
808 struct tcmsg *tcm;
809
810 int len;
811 struct rtattr *tb[TCA_MAX + 1];
812
813 frrtrace(3, frr_zebra, netlink_tc_filter_change, h, ns_id, startup);
814
815 len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct tcmsg));
816
817 if (len < 0) {
818 zlog_err(
819 "%s: Message received from netlink is of a broken size %d %zu",
820 __func__, h->nlmsg_len,
821 (size_t)NLMSG_LENGTH(sizeof(struct tcmsg)));
822 return -1;
823 }
824
825 tcm = NLMSG_DATA(h);
826 netlink_parse_rtattr(tb, TCA_MAX, TCA_RTA(tcm), len);
827
828 return 0;
829 }
830
831 int netlink_qdisc_read(struct zebra_ns *zns)
832 {
833 int ret;
834 struct zebra_dplane_info dp_info;
835
836 zebra_dplane_info_from_zns(&dp_info, zns, true);
837
838 ret = netlink_request_qdiscs(zns, AF_UNSPEC, RTM_GETQDISC);
839 if (ret < 0)
840 return ret;
841
842 ret = netlink_parse_info(netlink_qdisc_change, &zns->netlink_cmd,
843 &dp_info, 0, true);
844 if (ret < 0)
845 return ret;
846
847 return 0;
848 }
849
850 int netlink_tfilter_read_for_interface(struct zebra_ns *zns, ifindex_t ifindex)
851 {
852 int ret;
853 struct zebra_dplane_info dp_info;
854
855 zebra_dplane_info_from_zns(&dp_info, zns, true);
856
857 ret = netlink_request_filters(zns, AF_UNSPEC, RTM_GETTFILTER, ifindex);
858 if (ret < 0)
859 return ret;
860
861 ret = netlink_parse_info(netlink_tfilter_change, &zns->netlink_cmd,
862 &dp_info, 0, true);
863 if (ret < 0)
864 return ret;
865
866 return 0;
867 }
868
869 #endif /* HAVE_NETLINK */