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