1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Zebra Traffic Control (TC) interaction with the kernel using netlink.
5 * Copyright (C) 2022 Shichu Yang
12 #include <linux/pkt_cls.h>
13 #include <linux/pkt_sched.h>
14 #include <netinet/if_ether.h>
15 #include <sys/socket.h>
21 #include "zebra/zserv.h"
22 #include "zebra/zebra_ns.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"
33 #define TC_FREQ_DEFAULT (100)
35 /* some magic number */
36 #define TC_QDISC_MAJOR_ZEBRA (0xbeef0000u)
37 #define TC_MINOR_NOCLASS (0xffffu)
39 #define TIME_UNITS_PER_SEC (1000000)
40 #define xmittime(r, s) (TIME_UNITS_PER_SEC * ((double)(s) / (double)(r)))
42 static uint32_t tc_get_freq(void)
45 FILE *fp
= fopen("/proc/net/psched", "r");
50 if (fscanf(fp
, "%*08x%*08x%08x%08x", &nom
, &denom
) == 2) {
57 return freq
== 0 ? TC_FREQ_DEFAULT
: freq
;
60 static void tc_calc_rate_table(struct tc_ratespec
*ratespec
, uint32_t *table
,
70 while ((mtu
>> cell_log
) > 255)
74 for (int i
= 0; i
< 256; i
++)
75 table
[i
] = xmittime(ratespec
->rate
, (i
+ 1) << cell_log
);
77 ratespec
->cell_align
= -1;
78 ratespec
->cell_log
= cell_log
;
79 ratespec
->linklayer
= TC_LINKLAYER_ETHERNET
;
82 static int tc_flower_get_inet_prefix(const struct prefix
*prefix
,
83 struct inet_prefix
*addr
)
85 addr
->family
= prefix
->family
;
87 if (addr
->family
== AF_INET
) {
89 addr
->bitlen
= prefix
->prefixlen
;
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
) {
96 addr
->bitlen
= prefix
->prefixlen
;
98 addr
->flags
|= PREFIXLEN_SPECIFIED
;
99 addr
->flags
|= ADDRTYPE_INET
;
100 memcpy(addr
->data
, prefix
->u
.val
, sizeof(prefix
->u
.val
));
108 static int tc_flower_get_inet_mask(const struct prefix
*prefix
,
109 struct inet_prefix
*addr
)
111 addr
->family
= prefix
->family
;
113 if (addr
->family
== AF_INET
) {
115 addr
->bitlen
= prefix
->prefixlen
;
117 addr
->flags
|= PREFIXLEN_SPECIFIED
;
118 addr
->flags
|= ADDRTYPE_INET
;
119 } else if (addr
->family
== AF_INET6
) {
121 addr
->bitlen
= prefix
->prefixlen
;
123 addr
->flags
|= PREFIXLEN_SPECIFIED
;
124 addr
->flags
|= ADDRTYPE_INET
;
129 memset(addr
->data
, 0xff, addr
->bytelen
);
131 int rest
= prefix
->prefixlen
;
133 for (int i
= 0; i
< addr
->bytelen
/ 4; i
++) {
136 } else if (rest
/ 32 >= 1) {
139 addr
->data
[i
] <<= 32 - rest
;
140 addr
->data
[i
] = htonl(addr
->data
[i
]);
149 * Traffic control queue discipline encoding (only "htb" supported)
151 static ssize_t
netlink_qdisc_msg_encode(int cmd
, struct zebra_dplane_ctx
*ctx
,
152 void *data
, size_t datalen
)
155 const char *kind_str
= NULL
;
163 } *req
= (void *)data
;
165 if (datalen
< sizeof(*req
))
168 nl
= kernel_netlink_nlsock_lookup(dplane_ctx_get_ns_sock(ctx
));
170 memset(req
, 0, sizeof(*req
));
172 req
->n
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct tcmsg
));
173 req
->n
.nlmsg_flags
= NLM_F_CREATE
| NLM_F_REQUEST
;
175 req
->n
.nlmsg_flags
|= NLM_F_REPLACE
;
177 req
->n
.nlmsg_type
= cmd
;
179 req
->n
.nlmsg_pid
= nl
->snl
.nl_pid
;
181 req
->t
.tcm_family
= AF_UNSPEC
;
182 req
->t
.tcm_ifindex
= dplane_ctx_get_ifindex(ctx
);
184 req
->t
.tcm_handle
= 0;
185 req
->t
.tcm_parent
= TC_H_ROOT
;
187 if (cmd
== RTM_NEWQDISC
) {
188 req
->t
.tcm_handle
= TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA
, 0);
190 kind_str
= dplane_ctx_tc_qdisc_get_kind_str(ctx
);
192 nl_attr_put(&req
->n
, datalen
, TCA_KIND
, kind_str
,
193 strlen(kind_str
) + 1);
195 nest
= nl_attr_nest(&req
->n
, datalen
, TCA_OPTIONS
);
197 switch (dplane_ctx_tc_qdisc_get_kind(ctx
)) {
199 struct tc_htb_glob htb_glob
= {
202 .defcls
= TC_MINOR_NOCLASS
};
203 nl_attr_put(&req
->n
, datalen
, TCA_HTB_INIT
, &htb_glob
,
207 case TC_QDISC_NOQUEUE
:
211 /* not implemented */
214 nl_attr_nest_end(&req
->n
, nest
);
216 /* ifindex are enough for del/get qdisc */
219 return NLMSG_ALIGN(req
->n
.nlmsg_len
);
223 * Traffic control class encoding
225 static ssize_t
netlink_tclass_msg_encode(int cmd
, struct zebra_dplane_ctx
*ctx
,
226 void *data
, size_t datalen
)
228 enum dplane_op_e op
= dplane_ctx_get_op(ctx
);
231 const char *kind_str
= NULL
;
239 } *req
= (void *)data
;
241 if (datalen
< sizeof(*req
))
244 nl
= kernel_netlink_nlsock_lookup(dplane_ctx_get_ns_sock(ctx
));
246 memset(req
, 0, sizeof(*req
));
248 req
->n
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct tcmsg
));
249 req
->n
.nlmsg_flags
= NLM_F_CREATE
| NLM_F_REQUEST
;
251 if (op
== DPLANE_OP_TC_CLASS_UPDATE
)
252 req
->n
.nlmsg_flags
|= NLM_F_REPLACE
;
254 req
->n
.nlmsg_type
= cmd
;
256 req
->n
.nlmsg_pid
= nl
->snl
.nl_pid
;
258 req
->t
.tcm_family
= AF_UNSPEC
;
259 req
->t
.tcm_ifindex
= dplane_ctx_get_ifindex(ctx
);
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);
266 kind_str
= dplane_ctx_tc_class_get_kind_str(ctx
);
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
));
273 nl_attr_put(&req
->n
, datalen
, TCA_KIND
, kind_str
,
274 strlen(kind_str
) + 1);
276 nest
= nl_attr_nest(&req
->n
, datalen
, TCA_OPTIONS
);
278 switch (dplane_ctx_tc_class_get_kind(ctx
)) {
280 struct tc_htb_opt htb_opt
= {};
282 uint64_t rate
= dplane_ctx_tc_class_get_rate(ctx
),
283 ceil
= dplane_ctx_tc_class_get_ceil(ctx
);
285 uint64_t buffer
, cbuffer
;
287 /* TODO: fetch mtu from interface */
293 ceil
= MAX(rate
, ceil
);
295 htb_opt
.rate
.rate
= (rate
>> 32 != 0) ? ~0U : rate
;
296 htb_opt
.ceil
.rate
= (ceil
>> 32 != 0) ? ~0U : ceil
;
298 buffer
= rate
/ tc_get_freq() + mtu
;
299 cbuffer
= ceil
/ tc_get_freq() + mtu
;
301 htb_opt
.buffer
= buffer
;
302 htb_opt
.cbuffer
= cbuffer
;
304 tc_calc_rate_table(&htb_opt
.rate
, rtab
, mtu
);
305 tc_calc_rate_table(&htb_opt
.ceil
, ctab
, mtu
);
307 htb_opt
.ceil
.mpu
= htb_opt
.rate
.mpu
= 0;
308 htb_opt
.ceil
.overhead
= htb_opt
.rate
.overhead
= 0;
310 if (rate
>> 32 != 0) {
311 nl_attr_put(&req
->n
, datalen
, TCA_HTB_RATE64
,
312 &rate
, sizeof(rate
));
315 if (ceil
>> 32 != 0) {
316 nl_attr_put(&req
->n
, datalen
, TCA_HTB_CEIL64
,
317 &ceil
, sizeof(ceil
));
320 nl_attr_put(&req
->n
, datalen
, TCA_HTB_PARMS
, &htb_opt
,
323 nl_attr_put(&req
->n
, datalen
, TCA_HTB_RTAB
, rtab
,
325 nl_attr_put(&req
->n
, datalen
, TCA_HTB_CTAB
, ctab
,
333 nl_attr_nest_end(&req
->n
, nest
);
336 return NLMSG_ALIGN(req
->n
.nlmsg_len
);
339 static int netlink_tfilter_flower_port_type(uint8_t ip_proto
, bool src
)
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
;
351 static void netlink_tfilter_flower_put_options(struct nlmsghdr
*n
,
353 struct zebra_dplane_ctx
*ctx
)
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
);
360 if (filter_bm
& TC_FLOWER_SRC_IP
) {
361 const struct prefix
*src_p
=
362 dplane_ctx_tc_filter_get_src_ip(ctx
);
364 if (tc_flower_get_inet_prefix(src_p
, &addr
) != 0)
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
);
372 if (tc_flower_get_inet_mask(src_p
, &addr
) != 0)
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
);
382 if (filter_bm
& TC_FLOWER_DST_IP
) {
383 const struct prefix
*dst_p
=
384 dplane_ctx_tc_filter_get_dst_ip(ctx
);
386 if (tc_flower_get_inet_prefix(dst_p
, &addr
) != 0)
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
);
394 if (tc_flower_get_inet_mask(dst_p
, &addr
) != 0)
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
);
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
));
409 if (filter_bm
& TC_FLOWER_SRC_PORT
) {
412 min
= dplane_ctx_tc_filter_get_src_port_min(ctx
);
413 max
= dplane_ctx_tc_filter_get_src_port_max(ctx
);
416 nl_attr_put16(n
, datalen
, TCA_FLOWER_KEY_PORT_SRC_MIN
,
419 nl_attr_put16(n
, datalen
, TCA_FLOWER_KEY_PORT_SRC_MAX
,
422 int type
= netlink_tfilter_flower_port_type(
423 dplane_ctx_tc_filter_get_ip_proto(ctx
), true);
428 nl_attr_put16(n
, datalen
, type
, htons(min
));
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
);
437 nl_attr_put16(n
, datalen
, TCA_FLOWER_KEY_PORT_DST_MIN
,
440 nl_attr_put16(n
, datalen
, TCA_FLOWER_KEY_PORT_DST_MAX
,
443 int type
= netlink_tfilter_flower_port_type(
444 dplane_ctx_tc_filter_get_ip_proto(ctx
), false);
449 nl_attr_put16(n
, datalen
, type
, htons(min
));
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
));
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
);
464 nl_attr_put32(n
, datalen
, TCA_FLOWER_FLAGS
, flags
);
466 nl_attr_put16(n
, datalen
, TCA_FLOWER_KEY_ETH_TYPE
, protocol
);
470 * Traffic control filter encoding
472 static ssize_t
netlink_tfilter_msg_encode(int cmd
, struct zebra_dplane_ctx
*ctx
,
473 void *data
, size_t datalen
)
475 enum dplane_op_e op
= dplane_ctx_get_op(ctx
);
478 const char *kind_str
= NULL
;
489 } *req
= (void *)data
;
491 if (datalen
< sizeof(*req
))
494 nl
= kernel_netlink_nlsock_lookup(dplane_ctx_get_ns_sock(ctx
));
496 memset(req
, 0, sizeof(*req
));
498 req
->n
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct tcmsg
));
499 req
->n
.nlmsg_flags
= NLM_F_CREATE
| NLM_F_REQUEST
;
501 if (op
== DPLANE_OP_TC_FILTER_UPDATE
)
502 req
->n
.nlmsg_flags
|= NLM_F_REPLACE
;
504 req
->n
.nlmsg_type
= cmd
;
506 req
->n
.nlmsg_pid
= nl
->snl
.nl_pid
;
508 req
->t
.tcm_family
= AF_UNSPEC
;
509 req
->t
.tcm_ifindex
= dplane_ctx_get_ifindex(ctx
);
511 priority
= dplane_ctx_tc_filter_get_priority(ctx
);
512 protocol
= htons(dplane_ctx_tc_filter_get_eth_proto(ctx
));
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);
518 kind_str
= dplane_ctx_tc_filter_get_kind_str(ctx
);
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);
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
));
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
,
542 nl_attr_nest_end(&req
->n
, nest
);
545 return NLMSG_ALIGN(req
->n
.nlmsg_len
);
548 static ssize_t
netlink_newqdisc_msg_encoder(struct zebra_dplane_ctx
*ctx
,
549 void *buf
, size_t buflen
)
551 return netlink_qdisc_msg_encode(RTM_NEWQDISC
, ctx
, buf
, buflen
);
554 static ssize_t
netlink_delqdisc_msg_encoder(struct zebra_dplane_ctx
*ctx
,
555 void *buf
, size_t buflen
)
557 return netlink_qdisc_msg_encode(RTM_DELQDISC
, ctx
, buf
, buflen
);
560 static ssize_t
netlink_newtclass_msg_encoder(struct zebra_dplane_ctx
*ctx
,
561 void *buf
, size_t buflen
)
563 return netlink_tclass_msg_encode(RTM_NEWTCLASS
, ctx
, buf
, buflen
);
566 static ssize_t
netlink_deltclass_msg_encoder(struct zebra_dplane_ctx
*ctx
,
567 void *buf
, size_t buflen
)
569 return netlink_tclass_msg_encode(RTM_DELTCLASS
, ctx
, buf
, buflen
);
572 static ssize_t
netlink_newtfilter_msg_encoder(struct zebra_dplane_ctx
*ctx
,
573 void *buf
, size_t buflen
)
575 return netlink_tfilter_msg_encode(RTM_NEWTFILTER
, ctx
, buf
, buflen
);
578 static ssize_t
netlink_deltfilter_msg_encoder(struct zebra_dplane_ctx
*ctx
,
579 void *buf
, size_t buflen
)
581 return netlink_tfilter_msg_encode(RTM_DELTFILTER
, ctx
, buf
, buflen
);
584 enum netlink_msg_status
585 netlink_put_tc_qdisc_update_msg(struct nl_batch
*bth
,
586 struct zebra_dplane_ctx
*ctx
)
589 enum netlink_msg_status ret
;
591 op
= dplane_ctx_get_op(ctx
);
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);
600 return FRR_NETLINK_ERROR
;
606 enum netlink_msg_status
607 netlink_put_tc_class_update_msg(struct nl_batch
*bth
,
608 struct zebra_dplane_ctx
*ctx
)
611 enum netlink_msg_status ret
;
613 op
= dplane_ctx_get_op(ctx
);
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);
622 return FRR_NETLINK_ERROR
;
628 enum netlink_msg_status
629 netlink_put_tc_filter_update_msg(struct nl_batch
*bth
,
630 struct zebra_dplane_ctx
*ctx
)
633 enum netlink_msg_status ret
;
635 op
= dplane_ctx_get_op(ctx
);
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
) {
642 * Replace will fail if either filter type or the number of
643 * filter options is changed, so DEL then NEW
645 * TFILTER may have refs to TCLASS.
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);
656 return FRR_NETLINK_ERROR
;
663 * Request filters from the kernel
665 static int netlink_request_filters(struct zebra_ns
*zns
, int family
, int type
,
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
;
680 return netlink_request(&zns
->netlink_cmd
, &req
);
684 * Request queue discipline from the kernel
686 static int netlink_request_qdiscs(struct zebra_ns
*zns
, int family
, int type
)
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
;
699 return netlink_request(&zns
->netlink_cmd
, &req
);
702 int netlink_qdisc_change(struct nlmsghdr
*h
, ns_id_t ns_id
, int startup
)
705 struct zebra_tc_qdisc qdisc
= {};
708 struct rtattr
*tb
[TCA_MAX
+ 1];
710 frrtrace(3, frr_zebra
, netlink_tc_qdisc_change
, h
, ns_id
, startup
);
712 len
= h
->nlmsg_len
- NLMSG_LENGTH(sizeof(struct tcmsg
));
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
)));
723 netlink_parse_rtattr(tb
, TCA_MAX
, TCA_RTA(tcm
), len
);
725 const char *kind_str
= (const char *)RTA_DATA(tb
[TCA_KIND
]);
727 enum tc_qdisc_kind kind
= tc_qdisc_str2kind(kind_str
);
729 qdisc
.qdisc
.ifindex
= tcm
->tcm_ifindex
;
732 case TC_QDISC_NOQUEUE
:
733 /* "noqueue" is the default qdisc */
736 case TC_QDISC_UNSPEC
:
740 if (tb
[TCA_OPTIONS
] != NULL
) {
741 struct rtattr
*options
[TCA_HTB_MAX
+ 1];
743 netlink_parse_rtattr_nested(options
, TCA_HTB_MAX
,
746 /* TODO: more details */
747 /* struct tc_htb_glob *glob = RTA_DATA(options[TCA_HTB_INIT]);
751 if (h
->nlmsg_type
== RTM_NEWQDISC
) {
753 TC_H_MAJ(tcm
->tcm_handle
) == TC_QDISC_MAJOR_ZEBRA
) {
754 enum zebra_dplane_result ret
;
756 ret
= dplane_tc_qdisc_uninstall(&qdisc
);
758 zlog_debug("%s: %s leftover qdisc: ifindex %d kind %s",
760 ((ret
== ZEBRA_DPLANE_REQUEST_FAILURE
)
763 qdisc
.qdisc
.ifindex
, kind_str
);
770 int netlink_tclass_change(struct nlmsghdr
*h
, ns_id_t ns_id
, int startup
)
775 struct rtattr
*tb
[TCA_MAX
+ 1];
777 frrtrace(3, frr_zebra
, netlink_tc_class_change
, h
, ns_id
, startup
);
779 len
= h
->nlmsg_len
- NLMSG_LENGTH(sizeof(struct tcmsg
));
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
)));
790 netlink_parse_rtattr(tb
, TCA_MAX
, TCA_RTA(tcm
), len
);
793 if (tb
[TCA_OPTIONS
] != NULL
) {
794 struct rtattr
*options
[TCA_HTB_MAX
+ 1];
796 netlink_parse_rtattr_nested(options
, TCA_HTB_MAX
,
799 /* TODO: more details */
800 /* struct tc_htb_opt *opt = RTA_DATA(options[TCA_HTB_PARMS]); */
806 int netlink_tfilter_change(struct nlmsghdr
*h
, ns_id_t ns_id
, int startup
)
811 struct rtattr
*tb
[TCA_MAX
+ 1];
813 frrtrace(3, frr_zebra
, netlink_tc_filter_change
, h
, ns_id
, startup
);
815 len
= h
->nlmsg_len
- NLMSG_LENGTH(sizeof(struct tcmsg
));
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
)));
826 netlink_parse_rtattr(tb
, TCA_MAX
, TCA_RTA(tcm
), len
);
831 int netlink_qdisc_read(struct zebra_ns
*zns
)
834 struct zebra_dplane_info dp_info
;
836 zebra_dplane_info_from_zns(&dp_info
, zns
, true);
838 ret
= netlink_request_qdiscs(zns
, AF_UNSPEC
, RTM_GETQDISC
);
842 ret
= netlink_parse_info(netlink_qdisc_change
, &zns
->netlink_cmd
,
850 int netlink_tfilter_read_for_interface(struct zebra_ns
*zns
, ifindex_t ifindex
)
853 struct zebra_dplane_info dp_info
;
855 zebra_dplane_info_from_zns(&dp_info
, zns
, true);
857 ret
= netlink_request_filters(zns
, AF_UNSPEC
, RTM_GETTFILTER
, ifindex
);
861 ret
= netlink_parse_info(netlink_tfilter_change
, &zns
->netlink_cmd
,
869 #endif /* HAVE_NETLINK */