2 * Zebra Traffic Control (TC) interaction with the kernel using netlink.
4 * Copyright (C) 2022 Shichu Yang
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 #include <linux/pkt_cls.h>
26 #include <linux/pkt_sched.h>
27 #include <netinet/if_ether.h>
28 #include <sys/socket.h>
34 #include "zebra/zserv.h"
35 #include "zebra/zebra_ns.h"
37 #include "zebra/interface.h"
38 #include "zebra/debug.h"
39 #include "zebra/kernel_netlink.h"
40 #include "zebra/tc_netlink.h"
41 #include "zebra/zebra_errors.h"
42 #include "zebra/zebra_dplane.h"
43 #include "zebra/zebra_tc.h"
44 #include "zebra/zebra_trace.h"
46 #define TC_FREQ_DEFAULT (100)
48 /* some magic number */
49 #define TC_QDISC_MAJOR_ZEBRA (0xbeef0000u)
50 #define TC_MINOR_NOCLASS (0xffffu)
52 #define TIME_UNITS_PER_SEC (1000000)
53 #define xmittime(r, s) (TIME_UNITS_PER_SEC * ((double)(s) / (double)(r)))
55 static uint32_t tc_get_freq(void)
58 FILE *fp
= fopen("/proc/net/psched", "r");
63 if (fscanf(fp
, "%*08x%*08x%08x%08x", &nom
, &denom
) == 2) {
70 return freq
== 0 ? TC_FREQ_DEFAULT
: freq
;
73 static void tc_calc_rate_table(struct tc_ratespec
*ratespec
, uint32_t *table
,
83 while ((mtu
>> cell_log
) > 255)
87 for (int i
= 0; i
< 256; i
++)
88 table
[i
] = xmittime(ratespec
->rate
, (i
+ 1) << cell_log
);
90 ratespec
->cell_align
= -1;
91 ratespec
->cell_log
= cell_log
;
92 ratespec
->linklayer
= TC_LINKLAYER_ETHERNET
;
95 static int tc_flower_get_inet_prefix(const struct prefix
*prefix
,
96 struct inet_prefix
*addr
)
98 addr
->family
= prefix
->family
;
100 if (addr
->family
== AF_INET
) {
102 addr
->bitlen
= prefix
->prefixlen
;
104 addr
->flags
|= PREFIXLEN_SPECIFIED
;
105 addr
->flags
|= ADDRTYPE_INET
;
106 memcpy(addr
->data
, prefix
->u
.val32
, sizeof(prefix
->u
.val32
));
107 } else if (addr
->family
== AF_INET6
) {
109 addr
->bitlen
= prefix
->prefixlen
;
111 addr
->flags
|= PREFIXLEN_SPECIFIED
;
112 addr
->flags
|= ADDRTYPE_INET
;
113 memcpy(addr
->data
, prefix
->u
.val
, sizeof(prefix
->u
.val
));
121 static int tc_flower_get_inet_mask(const struct prefix
*prefix
,
122 struct inet_prefix
*addr
)
124 addr
->family
= prefix
->family
;
126 if (addr
->family
== AF_INET
) {
128 addr
->bitlen
= prefix
->prefixlen
;
130 addr
->flags
|= PREFIXLEN_SPECIFIED
;
131 addr
->flags
|= ADDRTYPE_INET
;
132 } else if (addr
->family
== AF_INET6
) {
134 addr
->bitlen
= prefix
->prefixlen
;
136 addr
->flags
|= PREFIXLEN_SPECIFIED
;
137 addr
->flags
|= ADDRTYPE_INET
;
142 memset(addr
->data
, 0xff, addr
->bytelen
);
144 int rest
= prefix
->prefixlen
;
146 for (int i
= 0; i
< addr
->bytelen
/ 4; i
++) {
149 } else if (rest
/ 32 >= 1) {
152 addr
->data
[i
] <<= 32 - rest
;
153 addr
->data
[i
] = htonl(addr
->data
[i
]);
162 * Traffic control queue discipline encoding (only "htb" supported)
164 static ssize_t
netlink_qdisc_msg_encode(int cmd
, struct zebra_dplane_ctx
*ctx
,
165 void *data
, size_t datalen
)
168 const char *kind_str
= NULL
;
176 } *req
= (void *)data
;
178 if (datalen
< sizeof(*req
))
181 nl
= kernel_netlink_nlsock_lookup(dplane_ctx_get_ns_sock(ctx
));
183 memset(req
, 0, sizeof(*req
));
185 req
->n
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct tcmsg
));
186 req
->n
.nlmsg_flags
= NLM_F_CREATE
| NLM_F_REQUEST
;
188 req
->n
.nlmsg_flags
|= NLM_F_REPLACE
;
190 req
->n
.nlmsg_type
= cmd
;
192 req
->n
.nlmsg_pid
= nl
->snl
.nl_pid
;
194 req
->t
.tcm_family
= AF_UNSPEC
;
195 req
->t
.tcm_ifindex
= dplane_ctx_get_ifindex(ctx
);
197 req
->t
.tcm_handle
= 0;
198 req
->t
.tcm_parent
= TC_H_ROOT
;
200 if (cmd
== RTM_NEWQDISC
) {
201 req
->t
.tcm_handle
= TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA
, 0);
203 kind_str
= dplane_ctx_tc_qdisc_get_kind_str(ctx
);
205 nl_attr_put(&req
->n
, datalen
, TCA_KIND
, kind_str
,
206 strlen(kind_str
) + 1);
208 nest
= nl_attr_nest(&req
->n
, datalen
, TCA_OPTIONS
);
210 switch (dplane_ctx_tc_qdisc_get_kind(ctx
)) {
212 struct tc_htb_glob htb_glob
= {
215 .defcls
= TC_MINOR_NOCLASS
};
216 nl_attr_put(&req
->n
, datalen
, TCA_HTB_INIT
, &htb_glob
,
220 case TC_QDISC_NOQUEUE
:
224 /* not implemented */
227 nl_attr_nest_end(&req
->n
, nest
);
229 /* ifindex are enough for del/get qdisc */
232 return NLMSG_ALIGN(req
->n
.nlmsg_len
);
236 * Traffic control class encoding
238 static ssize_t
netlink_tclass_msg_encode(int cmd
, struct zebra_dplane_ctx
*ctx
,
239 void *data
, size_t datalen
)
241 enum dplane_op_e op
= dplane_ctx_get_op(ctx
);
244 const char *kind_str
= NULL
;
252 } *req
= (void *)data
;
254 if (datalen
< sizeof(*req
))
257 nl
= kernel_netlink_nlsock_lookup(dplane_ctx_get_ns_sock(ctx
));
259 memset(req
, 0, sizeof(*req
));
261 req
->n
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct tcmsg
));
262 req
->n
.nlmsg_flags
= NLM_F_CREATE
| NLM_F_REQUEST
;
264 if (op
== DPLANE_OP_TC_CLASS_UPDATE
)
265 req
->n
.nlmsg_flags
|= NLM_F_REPLACE
;
267 req
->n
.nlmsg_type
= cmd
;
269 req
->n
.nlmsg_pid
= nl
->snl
.nl_pid
;
271 req
->t
.tcm_family
= AF_UNSPEC
;
272 req
->t
.tcm_ifindex
= dplane_ctx_get_ifindex(ctx
);
274 req
->t
.tcm_handle
= TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA
,
275 dplane_ctx_tc_class_get_handle(ctx
));
276 req
->t
.tcm_parent
= TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA
, 0);
279 kind_str
= dplane_ctx_tc_class_get_kind_str(ctx
);
281 if (op
== DPLANE_OP_TC_CLASS_ADD
|| op
== DPLANE_OP_TC_CLASS_UPDATE
) {
282 zlog_debug("netlink tclass encoder: op: %s kind: %s handle: %u",
283 op
== DPLANE_OP_TC_CLASS_UPDATE
? "update" : "add",
284 kind_str
, dplane_ctx_tc_class_get_handle(ctx
));
286 nl_attr_put(&req
->n
, datalen
, TCA_KIND
, kind_str
,
287 strlen(kind_str
) + 1);
289 nest
= nl_attr_nest(&req
->n
, datalen
, TCA_OPTIONS
);
291 switch (dplane_ctx_tc_class_get_kind(ctx
)) {
293 struct tc_htb_opt htb_opt
= {};
295 uint64_t rate
= dplane_ctx_tc_class_get_rate(ctx
),
296 ceil
= dplane_ctx_tc_class_get_ceil(ctx
);
298 uint64_t buffer
, cbuffer
;
300 /* TODO: fetch mtu from interface */
306 ceil
= MAX(rate
, ceil
);
308 htb_opt
.rate
.rate
= (rate
>> 32 != 0) ? ~0U : rate
;
309 htb_opt
.ceil
.rate
= (ceil
>> 32 != 0) ? ~0U : ceil
;
311 buffer
= rate
/ tc_get_freq() + mtu
;
312 cbuffer
= ceil
/ tc_get_freq() + mtu
;
314 htb_opt
.buffer
= buffer
;
315 htb_opt
.cbuffer
= cbuffer
;
317 tc_calc_rate_table(&htb_opt
.rate
, rtab
, mtu
);
318 tc_calc_rate_table(&htb_opt
.ceil
, ctab
, mtu
);
320 htb_opt
.ceil
.mpu
= htb_opt
.rate
.mpu
= 0;
321 htb_opt
.ceil
.overhead
= htb_opt
.rate
.overhead
= 0;
323 if (rate
>> 32 != 0) {
324 nl_attr_put(&req
->n
, datalen
, TCA_HTB_RATE64
,
325 &rate
, sizeof(rate
));
328 if (ceil
>> 32 != 0) {
329 nl_attr_put(&req
->n
, datalen
, TCA_HTB_CEIL64
,
330 &ceil
, sizeof(ceil
));
333 nl_attr_put(&req
->n
, datalen
, TCA_HTB_PARMS
, &htb_opt
,
336 nl_attr_put(&req
->n
, datalen
, TCA_HTB_RTAB
, rtab
,
338 nl_attr_put(&req
->n
, datalen
, TCA_HTB_CTAB
, ctab
,
346 nl_attr_nest_end(&req
->n
, nest
);
349 return NLMSG_ALIGN(req
->n
.nlmsg_len
);
352 static int netlink_tfilter_flower_port_type(uint8_t ip_proto
, bool src
)
354 if (ip_proto
== IPPROTO_TCP
)
355 return src
? TCA_FLOWER_KEY_TCP_SRC
: TCA_FLOWER_KEY_TCP_DST
;
356 else if (ip_proto
== IPPROTO_UDP
)
357 return src
? TCA_FLOWER_KEY_UDP_SRC
: TCA_FLOWER_KEY_UDP_DST
;
358 else if (ip_proto
== IPPROTO_SCTP
)
359 return src
? TCA_FLOWER_KEY_SCTP_SRC
: TCA_FLOWER_KEY_SCTP_DST
;
364 static void netlink_tfilter_flower_put_options(struct nlmsghdr
*n
,
366 struct zebra_dplane_ctx
*ctx
)
368 struct inet_prefix addr
;
369 uint32_t flags
= 0, classid
;
370 uint8_t protocol
= htons(dplane_ctx_tc_filter_get_eth_proto(ctx
));
371 uint32_t filter_bm
= dplane_ctx_tc_filter_get_filter_bm(ctx
);
373 if (filter_bm
& TC_FLOWER_SRC_IP
) {
374 const struct prefix
*src_p
=
375 dplane_ctx_tc_filter_get_src_ip(ctx
);
377 if (tc_flower_get_inet_prefix(src_p
, &addr
) != 0)
380 nl_attr_put(n
, datalen
,
381 (addr
.family
== AF_INET
) ? TCA_FLOWER_KEY_IPV4_SRC
382 : TCA_FLOWER_KEY_IPV6_SRC
,
383 addr
.data
, addr
.bytelen
);
385 if (tc_flower_get_inet_mask(src_p
, &addr
) != 0)
388 nl_attr_put(n
, datalen
,
389 (addr
.family
== AF_INET
)
390 ? TCA_FLOWER_KEY_IPV4_SRC_MASK
391 : TCA_FLOWER_KEY_IPV6_SRC_MASK
,
392 addr
.data
, addr
.bytelen
);
395 if (filter_bm
& TC_FLOWER_DST_IP
) {
396 const struct prefix
*dst_p
=
397 dplane_ctx_tc_filter_get_dst_ip(ctx
);
399 if (tc_flower_get_inet_prefix(dst_p
, &addr
) != 0)
402 nl_attr_put(n
, datalen
,
403 (addr
.family
== AF_INET
) ? TCA_FLOWER_KEY_IPV4_DST
404 : TCA_FLOWER_KEY_IPV6_DST
,
405 addr
.data
, addr
.bytelen
);
407 if (tc_flower_get_inet_mask(dst_p
, &addr
) != 0)
410 nl_attr_put(n
, datalen
,
411 (addr
.family
== AF_INET
)
412 ? TCA_FLOWER_KEY_IPV4_DST_MASK
413 : TCA_FLOWER_KEY_IPV6_DST_MASK
,
414 addr
.data
, addr
.bytelen
);
417 if (filter_bm
& TC_FLOWER_IP_PROTOCOL
) {
418 nl_attr_put8(n
, datalen
, TCA_FLOWER_KEY_IP_PROTO
,
419 dplane_ctx_tc_filter_get_ip_proto(ctx
));
422 if (filter_bm
& TC_FLOWER_SRC_PORT
) {
425 min
= dplane_ctx_tc_filter_get_src_port_min(ctx
);
426 max
= dplane_ctx_tc_filter_get_src_port_max(ctx
);
429 nl_attr_put16(n
, datalen
, TCA_FLOWER_KEY_PORT_SRC_MIN
,
432 nl_attr_put16(n
, datalen
, TCA_FLOWER_KEY_PORT_SRC_MAX
,
435 int type
= netlink_tfilter_flower_port_type(
436 dplane_ctx_tc_filter_get_ip_proto(ctx
), true);
441 nl_attr_put16(n
, datalen
, type
, htons(min
));
445 if (filter_bm
& TC_FLOWER_DST_PORT
) {
446 uint16_t min
= dplane_ctx_tc_filter_get_dst_port_min(ctx
),
447 max
= dplane_ctx_tc_filter_get_dst_port_max(ctx
);
450 nl_attr_put16(n
, datalen
, TCA_FLOWER_KEY_PORT_DST_MIN
,
453 nl_attr_put16(n
, datalen
, TCA_FLOWER_KEY_PORT_DST_MAX
,
456 int type
= netlink_tfilter_flower_port_type(
457 dplane_ctx_tc_filter_get_ip_proto(ctx
), false);
462 nl_attr_put16(n
, datalen
, type
, htons(min
));
466 if (filter_bm
& TC_FLOWER_DSFIELD
) {
467 nl_attr_put8(n
, datalen
, TCA_FLOWER_KEY_IP_TOS
,
468 dplane_ctx_tc_filter_get_dsfield(ctx
));
469 nl_attr_put8(n
, datalen
, TCA_FLOWER_KEY_IP_TOS_MASK
,
470 dplane_ctx_tc_filter_get_dsfield_mask(ctx
));
473 classid
= TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA
,
474 dplane_ctx_tc_filter_get_classid(ctx
));
475 nl_attr_put32(n
, datalen
, TCA_FLOWER_CLASSID
, classid
);
477 nl_attr_put32(n
, datalen
, TCA_FLOWER_FLAGS
, flags
);
479 nl_attr_put16(n
, datalen
, TCA_FLOWER_KEY_ETH_TYPE
, protocol
);
483 * Traffic control filter encoding
485 static ssize_t
netlink_tfilter_msg_encode(int cmd
, struct zebra_dplane_ctx
*ctx
,
486 void *data
, size_t datalen
)
488 enum dplane_op_e op
= dplane_ctx_get_op(ctx
);
491 const char *kind_str
= NULL
;
502 } *req
= (void *)data
;
504 if (datalen
< sizeof(*req
))
507 nl
= kernel_netlink_nlsock_lookup(dplane_ctx_get_ns_sock(ctx
));
509 memset(req
, 0, sizeof(*req
));
511 req
->n
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct tcmsg
));
512 req
->n
.nlmsg_flags
= NLM_F_CREATE
| NLM_F_REQUEST
;
514 if (op
== DPLANE_OP_TC_FILTER_UPDATE
)
515 req
->n
.nlmsg_flags
|= NLM_F_REPLACE
;
517 req
->n
.nlmsg_type
= cmd
;
519 req
->n
.nlmsg_pid
= nl
->snl
.nl_pid
;
521 req
->t
.tcm_family
= AF_UNSPEC
;
522 req
->t
.tcm_ifindex
= dplane_ctx_get_ifindex(ctx
);
524 priority
= dplane_ctx_tc_filter_get_priority(ctx
);
525 protocol
= htons(dplane_ctx_tc_filter_get_eth_proto(ctx
));
527 req
->t
.tcm_info
= TC_H_MAKE(priority
<< 16, protocol
);
528 req
->t
.tcm_handle
= dplane_ctx_tc_filter_get_handle(ctx
);
529 req
->t
.tcm_parent
= TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA
, 0);
531 kind_str
= dplane_ctx_tc_filter_get_kind_str(ctx
);
533 if (op
== DPLANE_OP_TC_FILTER_ADD
|| op
== DPLANE_OP_TC_FILTER_UPDATE
) {
534 nl_attr_put(&req
->n
, datalen
, TCA_KIND
, kind_str
,
535 strlen(kind_str
) + 1);
538 "netlink tfilter encoder: op: %s priority: %u protocol: %u kind: %s handle: %u filter_bm: %u ip_proto: %u",
539 op
== DPLANE_OP_TC_FILTER_UPDATE
? "update" : "add",
540 priority
, protocol
, kind_str
,
541 dplane_ctx_tc_filter_get_handle(ctx
),
542 dplane_ctx_tc_filter_get_filter_bm(ctx
),
543 dplane_ctx_tc_filter_get_ip_proto(ctx
));
545 nest
= nl_attr_nest(&req
->n
, datalen
, TCA_OPTIONS
);
546 switch (dplane_ctx_tc_filter_get_kind(ctx
)) {
547 case TC_FILTER_FLOWER
: {
548 netlink_tfilter_flower_put_options(&req
->n
, datalen
,
555 nl_attr_nest_end(&req
->n
, nest
);
558 return NLMSG_ALIGN(req
->n
.nlmsg_len
);
561 static ssize_t
netlink_newqdisc_msg_encoder(struct zebra_dplane_ctx
*ctx
,
562 void *buf
, size_t buflen
)
564 return netlink_qdisc_msg_encode(RTM_NEWQDISC
, ctx
, buf
, buflen
);
567 static ssize_t
netlink_delqdisc_msg_encoder(struct zebra_dplane_ctx
*ctx
,
568 void *buf
, size_t buflen
)
570 return netlink_qdisc_msg_encode(RTM_DELQDISC
, ctx
, buf
, buflen
);
573 static ssize_t
netlink_newtclass_msg_encoder(struct zebra_dplane_ctx
*ctx
,
574 void *buf
, size_t buflen
)
576 return netlink_tclass_msg_encode(RTM_NEWTCLASS
, ctx
, buf
, buflen
);
579 static ssize_t
netlink_deltclass_msg_encoder(struct zebra_dplane_ctx
*ctx
,
580 void *buf
, size_t buflen
)
582 return netlink_tclass_msg_encode(RTM_DELTCLASS
, ctx
, buf
, buflen
);
585 static ssize_t
netlink_newtfilter_msg_encoder(struct zebra_dplane_ctx
*ctx
,
586 void *buf
, size_t buflen
)
588 return netlink_tfilter_msg_encode(RTM_NEWTFILTER
, ctx
, buf
, buflen
);
591 static ssize_t
netlink_deltfilter_msg_encoder(struct zebra_dplane_ctx
*ctx
,
592 void *buf
, size_t buflen
)
594 return netlink_tfilter_msg_encode(RTM_DELTFILTER
, ctx
, buf
, buflen
);
597 enum netlink_msg_status
598 netlink_put_tc_qdisc_update_msg(struct nl_batch
*bth
,
599 struct zebra_dplane_ctx
*ctx
)
602 enum netlink_msg_status ret
;
604 op
= dplane_ctx_get_op(ctx
);
606 if (op
== DPLANE_OP_TC_QDISC_INSTALL
) {
607 ret
= netlink_batch_add_msg(
608 bth
, ctx
, netlink_newqdisc_msg_encoder
, false);
609 } else if (op
== DPLANE_OP_TC_QDISC_UNINSTALL
) {
610 ret
= netlink_batch_add_msg(
611 bth
, ctx
, netlink_delqdisc_msg_encoder
, false);
613 return FRR_NETLINK_ERROR
;
619 enum netlink_msg_status
620 netlink_put_tc_class_update_msg(struct nl_batch
*bth
,
621 struct zebra_dplane_ctx
*ctx
)
624 enum netlink_msg_status ret
;
626 op
= dplane_ctx_get_op(ctx
);
628 if (op
== DPLANE_OP_TC_CLASS_ADD
|| op
== DPLANE_OP_TC_CLASS_UPDATE
) {
629 ret
= netlink_batch_add_msg(
630 bth
, ctx
, netlink_newtclass_msg_encoder
, false);
631 } else if (op
== DPLANE_OP_TC_CLASS_DELETE
) {
632 ret
= netlink_batch_add_msg(
633 bth
, ctx
, netlink_deltclass_msg_encoder
, false);
635 return FRR_NETLINK_ERROR
;
641 enum netlink_msg_status
642 netlink_put_tc_filter_update_msg(struct nl_batch
*bth
,
643 struct zebra_dplane_ctx
*ctx
)
646 enum netlink_msg_status ret
;
648 op
= dplane_ctx_get_op(ctx
);
650 if (op
== DPLANE_OP_TC_FILTER_ADD
) {
651 ret
= netlink_batch_add_msg(
652 bth
, ctx
, netlink_newtfilter_msg_encoder
, false);
653 } else if (op
== DPLANE_OP_TC_FILTER_UPDATE
) {
655 * Replace will fail if either filter type or the number of
656 * filter options is changed, so DEL then NEW
658 * TFILTER may have refs to TCLASS.
661 (void)netlink_batch_add_msg(
662 bth
, ctx
, netlink_deltfilter_msg_encoder
, false);
663 ret
= netlink_batch_add_msg(
664 bth
, ctx
, netlink_newtfilter_msg_encoder
, false);
665 } else if (op
== DPLANE_OP_TC_FILTER_DELETE
) {
666 ret
= netlink_batch_add_msg(
667 bth
, ctx
, netlink_deltfilter_msg_encoder
, false);
669 return FRR_NETLINK_ERROR
;
676 * Request filters from the kernel
678 static int netlink_request_filters(struct zebra_ns
*zns
, int family
, int type
,
686 memset(&req
, 0, sizeof(req
));
687 req
.n
.nlmsg_type
= type
;
688 req
.n
.nlmsg_flags
= NLM_F_ROOT
| NLM_F_MATCH
| NLM_F_REQUEST
;
689 req
.n
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct tcmsg
));
690 req
.tc
.tcm_family
= family
;
691 req
.tc
.tcm_ifindex
= ifindex
;
693 return netlink_request(&zns
->netlink_cmd
, &req
);
697 * Request queue discipline from the kernel
699 static int netlink_request_qdiscs(struct zebra_ns
*zns
, int family
, int type
)
706 memset(&req
, 0, sizeof(req
));
707 req
.n
.nlmsg_type
= type
;
708 req
.n
.nlmsg_flags
= NLM_F_ROOT
| NLM_F_MATCH
| NLM_F_REQUEST
;
709 req
.n
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct tcmsg
));
710 req
.tc
.tcm_family
= family
;
712 return netlink_request(&zns
->netlink_cmd
, &req
);
715 int netlink_qdisc_change(struct nlmsghdr
*h
, ns_id_t ns_id
, int startup
)
718 struct zebra_tc_qdisc qdisc
= {};
721 struct rtattr
*tb
[TCA_MAX
+ 1];
723 frrtrace(3, frr_zebra
, netlink_tc_qdisc_change
, h
, ns_id
, startup
);
725 len
= h
->nlmsg_len
- NLMSG_LENGTH(sizeof(struct tcmsg
));
729 "%s: Message received from netlink is of a broken size %d %zu",
730 __func__
, h
->nlmsg_len
,
731 (size_t)NLMSG_LENGTH(sizeof(struct tcmsg
)));
736 netlink_parse_rtattr(tb
, TCA_MAX
, TCA_RTA(tcm
), len
);
738 const char *kind_str
= (const char *)RTA_DATA(tb
[TCA_KIND
]);
740 enum tc_qdisc_kind kind
= tc_qdisc_str2kind(kind_str
);
742 qdisc
.qdisc
.ifindex
= tcm
->tcm_ifindex
;
745 case TC_QDISC_NOQUEUE
:
746 /* "noqueue" is the default qdisc */
752 if (tb
[TCA_OPTIONS
] != NULL
) {
753 struct rtattr
*options
[TCA_HTB_MAX
+ 1];
755 netlink_parse_rtattr_nested(options
, TCA_HTB_MAX
,
758 /* TODO: more details */
759 /* struct tc_htb_glob *glob = RTA_DATA(options[TCA_HTB_INIT]);
763 if (h
->nlmsg_type
== RTM_NEWQDISC
) {
765 TC_H_MAJ(tcm
->tcm_handle
) == TC_QDISC_MAJOR_ZEBRA
) {
766 enum zebra_dplane_result ret
;
768 ret
= dplane_tc_qdisc_uninstall(&qdisc
);
770 zlog_debug("%s: %s leftover qdisc: ifindex %d kind %s",
772 ((ret
== ZEBRA_DPLANE_REQUEST_FAILURE
)
775 qdisc
.qdisc
.ifindex
, kind_str
);
782 int netlink_tclass_change(struct nlmsghdr
*h
, ns_id_t ns_id
, int startup
)
787 struct rtattr
*tb
[TCA_MAX
+ 1];
789 frrtrace(3, frr_zebra
, netlink_tc_class_change
, h
, ns_id
, startup
);
791 len
= h
->nlmsg_len
- NLMSG_LENGTH(sizeof(struct tcmsg
));
795 "%s: Message received from netlink is of a broken size %d %zu",
796 __func__
, h
->nlmsg_len
,
797 (size_t)NLMSG_LENGTH(sizeof(struct tcmsg
)));
802 netlink_parse_rtattr(tb
, TCA_MAX
, TCA_RTA(tcm
), len
);
805 if (tb
[TCA_OPTIONS
] != NULL
) {
806 struct rtattr
*options
[TCA_HTB_MAX
+ 1];
808 netlink_parse_rtattr_nested(options
, TCA_HTB_MAX
,
811 /* TODO: more details */
812 /* struct tc_htb_opt *opt = RTA_DATA(options[TCA_HTB_PARMS]); */
818 int netlink_tfilter_change(struct nlmsghdr
*h
, ns_id_t ns_id
, int startup
)
823 struct rtattr
*tb
[TCA_MAX
+ 1];
825 frrtrace(3, frr_zebra
, netlink_tc_filter_change
, h
, ns_id
, startup
);
827 len
= h
->nlmsg_len
- NLMSG_LENGTH(sizeof(struct tcmsg
));
831 "%s: Message received from netlink is of a broken size %d %zu",
832 __func__
, h
->nlmsg_len
,
833 (size_t)NLMSG_LENGTH(sizeof(struct tcmsg
)));
838 netlink_parse_rtattr(tb
, TCA_MAX
, TCA_RTA(tcm
), len
);
843 int netlink_qdisc_read(struct zebra_ns
*zns
)
846 struct zebra_dplane_info dp_info
;
848 zebra_dplane_info_from_zns(&dp_info
, zns
, true);
850 ret
= netlink_request_qdiscs(zns
, AF_UNSPEC
, RTM_GETQDISC
);
854 ret
= netlink_parse_info(netlink_qdisc_change
, &zns
->netlink_cmd
,
862 int netlink_tfilter_read_for_interface(struct zebra_ns
*zns
, ifindex_t ifindex
)
865 struct zebra_dplane_info dp_info
;
867 zebra_dplane_info_from_zns(&dp_info
, zns
, true);
869 ret
= netlink_request_filters(zns
, AF_UNSPEC
, RTM_GETTFILTER
, ifindex
);
873 ret
= netlink_parse_info(netlink_tfilter_change
, &zns
->netlink_cmd
,
881 #endif /* HAVE_NETLINK */