diff options
author | Siger Yang <siger.yang@outlook.com> | 2022-09-06 09:10:11 +0200 |
---|---|---|
committer | Siger Yang <siger.yang@outlook.com> | 2022-11-22 15:35:35 +0100 |
commit | c317d3f246769e8261df0f9d1eb787bea7b5da06 (patch) | |
tree | 5fe488123f08250f2c5db8c70bb092ea6c1e3ed5 | |
parent | zebra: update tc netlink / socket license header (diff) | |
download | frr-c317d3f246769e8261df0f9d1eb787bea7b5da06.tar.xz frr-c317d3f246769e8261df0f9d1eb787bea7b5da06.zip |
zebra: traffic control state management
This allows Zebra to manage QDISC, TCLASS, TFILTER in kernel and do cleaning
jobs when it starts up.
Signed-off-by: Siger Yang <siger.yang@outlook.com>
-rw-r--r-- | lib/subdir.am | 2 | ||||
-rw-r--r-- | lib/tc.c | 88 | ||||
-rw-r--r-- | lib/tc.h | 151 | ||||
-rw-r--r-- | zebra/debug.c | 13 | ||||
-rw-r--r-- | zebra/debug.h | 5 | ||||
-rw-r--r-- | zebra/dplane_fpm_nl.c | 11 | ||||
-rw-r--r-- | zebra/interface.c | 11 | ||||
-rw-r--r-- | zebra/kernel_netlink.c | 43 | ||||
-rw-r--r-- | zebra/kernel_socket.c | 11 | ||||
-rw-r--r-- | zebra/rtread_netlink.c | 7 | ||||
-rw-r--r-- | zebra/rtread_sysctl.c | 5 | ||||
-rw-r--r-- | zebra/subdir.am | 2 | ||||
-rw-r--r-- | zebra/tc_netlink.c | 710 | ||||
-rw-r--r-- | zebra/tc_netlink.h | 25 | ||||
-rw-r--r-- | zebra/tc_socket.c | 1 | ||||
-rw-r--r-- | zebra/zebra_dplane.c | 450 | ||||
-rw-r--r-- | zebra/zebra_dplane.h | 71 | ||||
-rw-r--r-- | zebra/zebra_nhg.c | 11 | ||||
-rw-r--r-- | zebra/zebra_ns.c | 2 | ||||
-rw-r--r-- | zebra/zebra_rib.c | 11 | ||||
-rw-r--r-- | zebra/zebra_router.c | 15 | ||||
-rw-r--r-- | zebra/zebra_router.h | 4 | ||||
-rw-r--r-- | zebra/zebra_tc.c | 444 | ||||
-rw-r--r-- | zebra/zebra_tc.h | 79 |
24 files changed, 1938 insertions, 234 deletions
diff --git a/lib/subdir.am b/lib/subdir.am index ea6cb9339..1a8c18482 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -123,6 +123,7 @@ lib_libfrr_la_SOURCES = \ lib/printf/glue.c \ lib/routing_nb.c \ lib/routing_nb_config.c \ + lib/tc.c \ # end nodist_lib_libfrr_la_SOURCES = \ @@ -275,6 +276,7 @@ pkginclude_HEADERS += \ lib/zlog_live.h \ lib/zlog_targets.h \ lib/pbr.h \ + lib/tc.h \ lib/routing_nb.h \ \ lib/assert/assert.h \ diff --git a/lib/tc.c b/lib/tc.c new file mode 100644 index 000000000..1bc01ed3f --- /dev/null +++ b/lib/tc.c @@ -0,0 +1,88 @@ +/* + * Traffic Control (TC) main library + * Copyright (C) 2022 Shichu Yang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "tc.h" + +int tc_getrate(const char *str, uint64_t *rate) +{ + char *endp; + uint64_t raw = strtoull(str, &endp, 10); + + if (endp == str) + return -1; + + /* if the string only contains a number, it must be valid rate (bps) */ + bool valid = (*endp == '\0'); + + const char *p = endp; + bool bytes = false, binary_base = false; + int power = 0; + + while (*p) { + if (strcmp(p, "Bps") == 0) { + bytes = true; + valid = true; + break; + } else if (strcmp(p, "bit") == 0) { + valid = true; + break; + } + switch (*p) { + case 'k': + case 'K': + power = 1; + break; + case 'm': + case 'M': + power = 2; + break; + case 'g': + case 'G': + power = 3; + break; + case 't': + case 'T': + power = 4; + break; + case 'i': + case 'I': + if (power != 0) + binary_base = true; + else + return -1; + break; + default: + return -1; + } + p++; + } + + if (!valid) + return -1; + + for (int i = 0; i < power; i++) + raw *= binary_base ? 1024ULL : 1000ULL; + + if (bytes) + *rate = raw; + else + *rate = raw / 8ULL; + + return 0; +} diff --git a/lib/tc.h b/lib/tc.h new file mode 100644 index 000000000..ec68e1f4c --- /dev/null +++ b/lib/tc.h @@ -0,0 +1,151 @@ +/* + * Traffic Control (TC) main header + * Copyright (C) 2022 Shichu Yang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _TC_H +#define _TC_H + +#include <zebra.h> +#include "stream.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define TC_STR "Traffic Control\n" + +/* qdisc definitions */ + +/* qdisc kind (same as class kinds) */ +enum tc_qdisc_kind { + TC_QDISC_UNSPEC, + TC_QDISC_HTB, + TC_QDISC_NOQUEUE, +}; + +struct tc_qdisc_htb { + /* currently no members */ +}; + +struct tc_qdisc { + ifindex_t ifindex; + + enum tc_qdisc_kind kind; + union { + struct tc_qdisc_htb htb; + } u; +}; + +/* class definitions */ + +/* since classes share the same kinds of qdisc, duplicates omitted */ +struct tc_class_htb { + uint64_t rate; + uint64_t ceil; +}; + +struct tc_class { + ifindex_t ifindex; + uint32_t handle; + + enum tc_qdisc_kind kind; + union { + struct tc_class_htb htb; + } u; +}; + +/* filter definitions */ + +/* filter kinds */ +enum tc_filter_kind { + TC_FILTER_UNSPEC, + TC_FILTER_BPF, + TC_FILTER_FLOW, + TC_FILTER_FLOWER, + TC_FILTER_U32, +}; + +struct tc_bpf { + /* TODO: fill in */ +}; + +struct tc_flow { + /* TODO: fill in */ +}; + +struct tc_flower { + uint32_t classid; + +#define TC_FLOWER_IP_PROTOCOL (1 << 0) +#define TC_FLOWER_SRC_IP (1 << 1) +#define TC_FLOWER_DST_IP (1 << 2) +#define TC_FLOWER_SRC_PORT (1 << 3) +#define TC_FLOWER_DST_PORT (1 << 4) +#define TC_FLOWER_DSFIELD (1 << 5) + + uint32_t filter_bm; + + uint8_t ip_proto; + + struct prefix src_ip; + struct prefix dst_ip; + + uint16_t src_port_min; + uint16_t src_port_max; + uint16_t dst_port_min; + uint16_t dst_port_max; + + uint8_t dsfield; + uint8_t dsfield_mask; +}; + +struct tc_u32 { + /* TODO: fill in */ +}; + +struct tc_filter { + ifindex_t ifindex; + uint32_t handle; + + uint32_t priority; + uint16_t protocol; + + enum tc_filter_kind kind; + + union { + struct tc_bpf bpf; + struct tc_flow flow; + struct tc_flower flower; + struct tc_u32 u32; + } u; +}; + +extern int tc_getrate(const char *str, uint64_t *rate); + +extern int zapi_tc_qdisc_encode(uint8_t cmd, struct stream *s, + struct tc_qdisc *qdisc); +extern int zapi_tc_class_encode(uint8_t cmd, struct stream *s, + struct tc_class *class); +extern int zapi_tc_filter_encode(uint8_t cmd, struct stream *s, + struct tc_filter *filter); + +#ifdef __cplusplus +} +#endif + +#endif /* _TC_H */ diff --git a/zebra/debug.c b/zebra/debug.c index 16aac7909..953f0423a 100644 --- a/zebra/debug.c +++ b/zebra/debug.c @@ -42,6 +42,7 @@ unsigned long zebra_debug_nexthop; unsigned long zebra_debug_evpn_mh; unsigned long zebra_debug_pbr; unsigned long zebra_debug_neigh; +unsigned long zebra_debug_tc; DEFINE_HOOK(zebra_debug_show_debugging, (struct vty *vty), (vty)); @@ -374,6 +375,17 @@ DEFPY (debug_zebra_neigh, return CMD_SUCCESS; } +DEFUN (debug_zebra_tc, + debug_zebra_tc_cmd, + "debug zebra tc", + DEBUG_STR + "Zebra configuration\n" + "Debug zebra tc events\n") +{ + SET_FLAG(zebra_debug_tc, ZEBRA_DEBUG_TC); + return CMD_SUCCESS; +} + DEFPY (debug_zebra_mlag, debug_zebra_mlag_cmd, "[no$no] debug zebra mlag", @@ -797,6 +809,7 @@ void zebra_debug_init(void) install_element(ENABLE_NODE, &debug_zebra_nexthop_cmd); install_element(ENABLE_NODE, &debug_zebra_pbr_cmd); install_element(ENABLE_NODE, &debug_zebra_neigh_cmd); + install_element(ENABLE_NODE, &debug_zebra_tc_cmd); install_element(ENABLE_NODE, &debug_zebra_dplane_dpdk_cmd); install_element(ENABLE_NODE, &no_debug_zebra_events_cmd); install_element(ENABLE_NODE, &no_debug_zebra_nht_cmd); diff --git a/zebra/debug.h b/zebra/debug.h index 73546de63..e0c6a9e2b 100644 --- a/zebra/debug.h +++ b/zebra/debug.h @@ -75,6 +75,8 @@ extern "C" { #define ZEBRA_DEBUG_NEIGH 0x01 +#define ZEBRA_DEBUG_TC 0x01 + /* Debug related macro. */ #define IS_ZEBRA_DEBUG_EVENT (zebra_debug_event & ZEBRA_DEBUG_EVENT) @@ -133,6 +135,8 @@ extern "C" { #define IS_ZEBRA_DEBUG_NEIGH (zebra_debug_neigh & ZEBRA_DEBUG_NEIGH) +#define IS_ZEBRA_DEBUG_TC (zebra_debug_tc & ZEBRA_DEBUG_TC) + extern unsigned long zebra_debug_event; extern unsigned long zebra_debug_packet; extern unsigned long zebra_debug_kernel; @@ -149,6 +153,7 @@ extern unsigned long zebra_debug_nexthop; extern unsigned long zebra_debug_evpn_mh; extern unsigned long zebra_debug_pbr; extern unsigned long zebra_debug_neigh; +extern unsigned long zebra_debug_tc; extern void zebra_debug_init(void); diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c index c5e1c113c..6c95be29d 100644 --- a/zebra/dplane_fpm_nl.c +++ b/zebra/dplane_fpm_nl.c @@ -816,9 +816,14 @@ static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx) case DPLANE_OP_INTF_INSTALL: case DPLANE_OP_INTF_UPDATE: case DPLANE_OP_INTF_DELETE: - case DPLANE_OP_TC_INSTALL: - case DPLANE_OP_TC_UPDATE: - case DPLANE_OP_TC_DELETE: + case DPLANE_OP_TC_QDISC_INSTALL: + case DPLANE_OP_TC_QDISC_UNINSTALL: + case DPLANE_OP_TC_CLASS_ADD: + case DPLANE_OP_TC_CLASS_DELETE: + case DPLANE_OP_TC_CLASS_UPDATE: + case DPLANE_OP_TC_FILTER_ADD: + case DPLANE_OP_TC_FILTER_DELETE: + case DPLANE_OP_TC_FILTER_UPDATE: case DPLANE_OP_NONE: break; diff --git a/zebra/interface.c b/zebra/interface.c index 32703b59b..d61d3620f 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -1573,9 +1573,14 @@ void zebra_if_dplane_result(struct zebra_dplane_ctx *ctx) case DPLANE_OP_IPSET_ENTRY_DELETE: case DPLANE_OP_NEIGH_TABLE_UPDATE: case DPLANE_OP_GRE_SET: - case DPLANE_OP_TC_INSTALL: - case DPLANE_OP_TC_UPDATE: - case DPLANE_OP_TC_DELETE: + case DPLANE_OP_TC_QDISC_INSTALL: + case DPLANE_OP_TC_QDISC_UNINSTALL: + case DPLANE_OP_TC_CLASS_ADD: + case DPLANE_OP_TC_CLASS_DELETE: + case DPLANE_OP_TC_CLASS_UPDATE: + case DPLANE_OP_TC_FILTER_ADD: + case DPLANE_OP_TC_FILTER_DELETE: + case DPLANE_OP_TC_FILTER_UPDATE: break; /* should never hit here */ } } diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index a8b56bb8f..54d9561e2 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -423,6 +423,15 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, return netlink_nexthop_change(h, ns_id, startup); case RTM_DELNEXTHOP: return netlink_nexthop_change(h, ns_id, startup); + case RTM_NEWQDISC: + case RTM_DELQDISC: + return netlink_qdisc_change(h, ns_id, startup); + case RTM_NEWTCLASS: + case RTM_DELTCLASS: + return netlink_tclass_change(h, ns_id, startup); + case RTM_NEWTFILTER: + case RTM_DELTFILTER: + return netlink_tfilter_change(h, ns_id, startup); /* Messages handled in the dplane thread */ case RTM_NEWADDR: @@ -1640,10 +1649,17 @@ static enum netlink_msg_status nl_put_msg(struct nl_batch *bth, case DPLANE_OP_INTF_DELETE: return netlink_put_intf_update_msg(bth, ctx); - case DPLANE_OP_TC_INSTALL: - case DPLANE_OP_TC_UPDATE: - case DPLANE_OP_TC_DELETE: - return netlink_put_tc_update_msg(bth, ctx); + case DPLANE_OP_TC_QDISC_INSTALL: + case DPLANE_OP_TC_QDISC_UNINSTALL: + return netlink_put_tc_qdisc_update_msg(bth, ctx); + case DPLANE_OP_TC_CLASS_ADD: + case DPLANE_OP_TC_CLASS_DELETE: + case DPLANE_OP_TC_CLASS_UPDATE: + return netlink_put_tc_class_update_msg(bth, ctx); + case DPLANE_OP_TC_FILTER_ADD: + case DPLANE_OP_TC_FILTER_DELETE: + case DPLANE_OP_TC_FILTER_UPDATE: + return netlink_put_tc_filter_update_msg(bth, ctx); } return FRR_NETLINK_ERROR; @@ -1757,15 +1773,16 @@ void kernel_init(struct zebra_ns *zns) * RTNLGRP_XXX to a bit position for ourself */ groups = RTMGRP_LINK | - RTMGRP_IPV4_ROUTE | - RTMGRP_IPV4_IFADDR | - RTMGRP_IPV6_ROUTE | - RTMGRP_IPV6_IFADDR | - RTMGRP_IPV4_MROUTE | - RTMGRP_NEIGH | - ((uint32_t) 1 << (RTNLGRP_IPV4_RULE - 1)) | - ((uint32_t) 1 << (RTNLGRP_IPV6_RULE - 1)) | - ((uint32_t) 1 << (RTNLGRP_NEXTHOP - 1)); + RTMGRP_IPV4_ROUTE | + RTMGRP_IPV4_IFADDR | + RTMGRP_IPV6_ROUTE | + RTMGRP_IPV6_IFADDR | + RTMGRP_IPV4_MROUTE | + RTMGRP_NEIGH | + ((uint32_t) 1 << (RTNLGRP_IPV4_RULE - 1)) | + ((uint32_t) 1 << (RTNLGRP_IPV6_RULE - 1)) | + ((uint32_t) 1 << (RTNLGRP_NEXTHOP - 1)) | + ((uint32_t) 1 << (RTNLGRP_TC - 1)); dplane_groups = (RTMGRP_LINK | RTMGRP_IPV4_IFADDR | diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index e76d8c0cc..f84d31cc4 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -1594,9 +1594,14 @@ void kernel_update_multi(struct dplane_ctx_q *ctx_list) res = kernel_intf_update(ctx); break; - case DPLANE_OP_TC_INSTALL: - case DPLANE_OP_TC_UPDATE: - case DPLANE_OP_TC_DELETE: + case DPLANE_OP_TC_QDISC_INSTALL: + case DPLANE_OP_TC_QDISC_UNINSTALL: + case DPLANE_OP_TC_CLASS_ADD: + case DPLANE_OP_TC_CLASS_DELETE: + case DPLANE_OP_TC_CLASS_UPDATE: + case DPLANE_OP_TC_FILTER_ADD: + case DPLANE_OP_TC_FILTER_DELETE: + case DPLANE_OP_TC_FILTER_UPDATE: res = kernel_tc_update(ctx); break; diff --git a/zebra/rtread_netlink.c b/zebra/rtread_netlink.c index f70b006ac..8990f66ef 100644 --- a/zebra/rtread_netlink.c +++ b/zebra/rtread_netlink.c @@ -26,8 +26,10 @@ #include "vty.h" #include "zebra/rt.h" #include "zebra/zebra_pbr.h" +#include "zebra/zebra_tc.h" #include "zebra/rt_netlink.h" #include "zebra/rule_netlink.h" +#include "zebra/tc_netlink.h" void route_read(struct zebra_ns *zns) { @@ -71,4 +73,9 @@ void kernel_read_pbr_rules(struct zebra_ns *zns) netlink_rules_read(zns); } +void kernel_read_tc_qdisc(struct zebra_ns *zns) +{ + netlink_qdisc_read(zns); +} + #endif /* GNU_LINUX */ diff --git a/zebra/rtread_sysctl.c b/zebra/rtread_sysctl.c index 594f7c2dd..35dde0e68 100644 --- a/zebra/rtread_sysctl.c +++ b/zebra/rtread_sysctl.c @@ -30,6 +30,7 @@ #include "zebra/rt.h" #include "zebra/kernel_socket.h" #include "zebra/zebra_pbr.h" +#include "zebra/zebra_tc.h" #include "zebra/zebra_errors.h" /* Kernel routing table read up by sysctl function. */ @@ -108,4 +109,8 @@ void kernel_read_pbr_rules(struct zebra_ns *zns) { } +void kernel_read_tc_qdisc(struct zebra_ns *zns) +{ +} + #endif /* !defined(GNU_LINUX) */ diff --git a/zebra/subdir.am b/zebra/subdir.am index 984293149..5c4a87b93 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -100,6 +100,7 @@ zebra_zebra_SOURCES = \ zebra/zebra_routemap_nb_config.c \ zebra/zebra_script.c \ zebra/zebra_srte.c \ + zebra/zebra_tc.c \ zebra/zebra_trace.c \ zebra/zebra_vrf.c \ zebra/zebra_vty.c \ @@ -175,6 +176,7 @@ noinst_HEADERS += \ zebra/zebra_router.h \ zebra/zebra_script.h \ zebra/zebra_srte.h \ + zebra/zebra_tc.h \ zebra/zebra_trace.h \ zebra/zebra_vrf.h \ zebra/zebra_vxlan.h \ diff --git a/zebra/tc_netlink.c b/zebra/tc_netlink.c index 6950bbbc5..1fa80e846 100644 --- a/zebra/tc_netlink.c +++ b/zebra/tc_netlink.c @@ -22,6 +22,8 @@ #ifdef HAVE_NETLINK +#include <linux/pkt_cls.h> +#include <linux/pkt_sched.h> #include <netinet/if_ether.h> #include <sys/socket.h> @@ -29,34 +31,24 @@ #include "prefix.h" #include "vrf.h" -#include <linux/fib_rules.h> -#include <linux/pkt_cls.h> -#include <linux/pkt_sched.h> #include "zebra/zserv.h" #include "zebra/zebra_ns.h" -#include "zebra/zebra_vrf.h" #include "zebra/rt.h" #include "zebra/interface.h" #include "zebra/debug.h" -#include "zebra/rtadv.h" #include "zebra/kernel_netlink.h" #include "zebra/tc_netlink.h" #include "zebra/zebra_errors.h" #include "zebra/zebra_dplane.h" +#include "zebra/zebra_tc.h" #include "zebra/zebra_trace.h" -/* TODO: move these bitflags to zebra_tc.h */ -#define TC_FILTER_SRC_IP (1 << 0) -#define TC_FILTER_DST_IP (1 << 1) -#define TC_FILTER_IP_PROTOCOL (1 << 9) - #define TC_FREQ_DEFAULT (100) -#define TC_MAJOR_BASE (0x1000u) +/* some magic number */ +#define TC_QDISC_MAJOR_ZEBRA (0xbeef0000u) #define TC_MINOR_NOCLASS (0xffffu) -#define TC_FILTER_MASK (0x8000u) - #define TIME_UNITS_PER_SEC (1000000) #define xmittime(r, s) (TIME_UNITS_PER_SEC * ((double)(s) / (double)(r))) @@ -78,19 +70,6 @@ static uint32_t tc_get_freq(void) return freq == 0 ? TC_FREQ_DEFAULT : freq; } -static inline uint32_t tc_make_handle(uint16_t major, uint16_t minor) -{ - return (major) << 16 | (minor); -} - -static inline uint32_t tc_get_handle(struct zebra_dplane_ctx *ctx, - uint16_t minor) -{ - uint16_t major = TC_MAJOR_BASE + (uint16_t)dplane_ctx_get_ifindex(ctx); - - return tc_make_handle(major, minor); -} - static void tc_calc_rate_table(struct tc_ratespec *ratespec, uint32_t *table, uint32_t mtu) { @@ -186,11 +165,7 @@ static ssize_t netlink_qdisc_msg_encode(int cmd, struct zebra_dplane_ctx *ctx, void *data, size_t datalen) { struct nlsock *nl; - - const char *kind = "htb"; - - struct tc_htb_glob htb_glob = { - .rate2quantum = 10, .version = 3, .defcls = TC_MINOR_NOCLASS}; + const char *kind_str = NULL; struct rtattr *nest; @@ -218,16 +193,41 @@ static ssize_t netlink_qdisc_msg_encode(int cmd, struct zebra_dplane_ctx *ctx, req->t.tcm_family = AF_UNSPEC; req->t.tcm_ifindex = dplane_ctx_get_ifindex(ctx); - req->t.tcm_handle = tc_get_handle(ctx, 0); + req->t.tcm_info = 0; + req->t.tcm_handle = 0; req->t.tcm_parent = TC_H_ROOT; - nl_attr_put(&req->n, datalen, TCA_KIND, kind, strlen(kind) + 1); + if (cmd == RTM_NEWQDISC) { + req->t.tcm_handle = TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA, 0); + + kind_str = dplane_ctx_tc_qdisc_get_kind_str(ctx); - nest = nl_attr_nest(&req->n, datalen, TCA_OPTIONS); + nl_attr_put(&req->n, datalen, TCA_KIND, kind_str, + strlen(kind_str) + 1); - nl_attr_put(&req->n, datalen, TCA_HTB_INIT, &htb_glob, - sizeof(htb_glob)); - nl_attr_nest_end(&req->n, nest); + nest = nl_attr_nest(&req->n, datalen, TCA_OPTIONS); + + switch (dplane_ctx_tc_qdisc_get_kind(ctx)) { + case TC_QDISC_HTB: { + struct tc_htb_glob htb_glob = { + .rate2quantum = 10, + .version = 3, + .defcls = TC_MINOR_NOCLASS}; + nl_attr_put(&req->n, datalen, TCA_HTB_INIT, &htb_glob, + sizeof(htb_glob)); + break; + } + case TC_QDISC_NOQUEUE: + break; + default: + break; + /* not implemented */ + } + + nl_attr_nest_end(&req->n, nest); + } else { + /* ifindex are enough for del/get qdisc */ + } return NLMSG_ALIGN(req->n.nlmsg_len); } @@ -238,17 +238,10 @@ static ssize_t netlink_qdisc_msg_encode(int cmd, struct zebra_dplane_ctx *ctx, static ssize_t netlink_tclass_msg_encode(int cmd, struct zebra_dplane_ctx *ctx, void *data, size_t datalen) { - struct nlsock *nl; - struct tc_htb_opt htb_opt = {}; - - uint64_t rate, ceil; - uint64_t buffer, cbuffer; + enum dplane_op_e op = dplane_ctx_get_op(ctx); - /* TODO: fetch mtu from interface */ - uint32_t mtu = 0; - - uint32_t rtab[256]; - uint32_t ctab[256]; + struct nlsock *nl; + const char *kind_str = NULL; struct rtattr *nest; @@ -268,73 +261,239 @@ static ssize_t netlink_tclass_msg_encode(int cmd, struct zebra_dplane_ctx *ctx, req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; + if (op == DPLANE_OP_TC_CLASS_UPDATE) + req->n.nlmsg_flags |= NLM_F_REPLACE; + req->n.nlmsg_type = cmd; req->n.nlmsg_pid = nl->snl.nl_pid; req->t.tcm_family = AF_UNSPEC; req->t.tcm_ifindex = dplane_ctx_get_ifindex(ctx); - req->t.tcm_handle = tc_get_handle(ctx, 1); - req->t.tcm_parent = tc_get_handle(ctx, 0); - rate = dplane_ctx_tc_get_rate(ctx); - ceil = dplane_ctx_tc_get_ceil(ctx); + req->t.tcm_handle = TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA, + dplane_ctx_tc_class_get_handle(ctx)); + req->t.tcm_parent = TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA, 0); + req->t.tcm_info = 0; + + kind_str = dplane_ctx_tc_class_get_kind_str(ctx); + + if (op == DPLANE_OP_TC_CLASS_ADD || op == DPLANE_OP_TC_CLASS_UPDATE) { + zlog_debug("netlink tclass encoder: op: %s kind: %s handle: %u", + op == DPLANE_OP_TC_CLASS_UPDATE ? "update" : "add", + kind_str, dplane_ctx_tc_class_get_handle(ctx)); + + nl_attr_put(&req->n, datalen, TCA_KIND, kind_str, + strlen(kind_str) + 1); + + nest = nl_attr_nest(&req->n, datalen, TCA_OPTIONS); - ceil = ceil < rate ? rate : ceil; + switch (dplane_ctx_tc_class_get_kind(ctx)) { + case TC_QDISC_HTB: { + struct tc_htb_opt htb_opt = {}; - htb_opt.rate.rate = (rate >> 32 != 0) ? ~0U : rate; - htb_opt.ceil.rate = (ceil >> 32 != 0) ? ~0U : ceil; + uint64_t rate = dplane_ctx_tc_class_get_rate(ctx), + ceil = dplane_ctx_tc_class_get_ceil(ctx); - buffer = rate / tc_get_freq(), cbuffer = ceil / tc_get_freq(); + uint64_t buffer, cbuffer; - htb_opt.buffer = buffer; - htb_opt.cbuffer = cbuffer; + /* TODO: fetch mtu from interface */ + uint32_t mtu = 1500; - tc_calc_rate_table(&htb_opt.rate, rtab, mtu); - tc_calc_rate_table(&htb_opt.ceil, ctab, mtu); + uint32_t rtab[256]; + uint32_t ctab[256]; - htb_opt.ceil.mpu = htb_opt.rate.mpu = 0; - htb_opt.ceil.overhead = htb_opt.rate.overhead = 0; + ceil = MAX(rate, ceil); - nest = nl_attr_nest(&req->n, datalen, TCA_OPTIONS); + htb_opt.rate.rate = (rate >> 32 != 0) ? ~0U : rate; + htb_opt.ceil.rate = (ceil >> 32 != 0) ? ~0U : ceil; - if (rate >> 32 != 0) { - nl_attr_put(&req->n, datalen, TCA_HTB_CEIL64, &rate, - sizeof(rate)); + buffer = rate / tc_get_freq() + mtu; + cbuffer = ceil / tc_get_freq() + mtu; + + htb_opt.buffer = buffer; + htb_opt.cbuffer = cbuffer; + + tc_calc_rate_table(&htb_opt.rate, rtab, mtu); + tc_calc_rate_table(&htb_opt.ceil, ctab, mtu); + + htb_opt.ceil.mpu = htb_opt.rate.mpu = 0; + htb_opt.ceil.overhead = htb_opt.rate.overhead = 0; + + if (rate >> 32 != 0) { + nl_attr_put(&req->n, datalen, TCA_HTB_RATE64, + &rate, sizeof(rate)); + } + + if (ceil >> 32 != 0) { + nl_attr_put(&req->n, datalen, TCA_HTB_CEIL64, + &ceil, sizeof(ceil)); + } + + nl_attr_put(&req->n, datalen, TCA_HTB_PARMS, &htb_opt, + sizeof(htb_opt)); + + nl_attr_put(&req->n, datalen, TCA_HTB_RTAB, rtab, + sizeof(rtab)); + nl_attr_put(&req->n, datalen, TCA_HTB_CTAB, ctab, + sizeof(ctab)); + break; + } + default: + break; + } + + nl_attr_nest_end(&req->n, nest); } - if (ceil >> 32 != 0) { - nl_attr_put(&req->n, datalen, TCA_HTB_CEIL64, &ceil, - sizeof(ceil)); + return NLMSG_ALIGN(req->n.nlmsg_len); +} + +static int netlink_tfilter_flower_port_type(uint8_t ip_proto, bool src) +{ + if (ip_proto == IPPROTO_TCP) + return src ? TCA_FLOWER_KEY_TCP_SRC : TCA_FLOWER_KEY_TCP_DST; + else if (ip_proto == IPPROTO_UDP) + return src ? TCA_FLOWER_KEY_UDP_SRC : TCA_FLOWER_KEY_UDP_DST; + else if (ip_proto == IPPROTO_SCTP) + return src ? TCA_FLOWER_KEY_SCTP_SRC : TCA_FLOWER_KEY_SCTP_DST; + else + return -1; +} + +static void netlink_tfilter_flower_put_options(struct nlmsghdr *n, + size_t datalen, + struct zebra_dplane_ctx *ctx) +{ + struct inet_prefix addr; + uint32_t flags = 0, classid; + uint8_t protocol = htons(dplane_ctx_tc_filter_get_eth_proto(ctx)); + uint32_t filter_bm = dplane_ctx_tc_filter_get_filter_bm(ctx); + + if (filter_bm & TC_FLOWER_SRC_IP) { + const struct prefix *src_p = + dplane_ctx_tc_filter_get_src_ip(ctx); + + if (tc_flower_get_inet_prefix(src_p, &addr) != 0) + return; + + nl_attr_put(n, datalen, + (addr.family == AF_INET) ? TCA_FLOWER_KEY_IPV4_SRC + : TCA_FLOWER_KEY_IPV6_SRC, + addr.data, addr.bytelen); + + if (tc_flower_get_inet_mask(src_p, &addr) != 0) + return; + + nl_attr_put(n, datalen, + (addr.family == AF_INET) + ? TCA_FLOWER_KEY_IPV4_SRC_MASK + : TCA_FLOWER_KEY_IPV6_SRC_MASK, + addr.data, addr.bytelen); } - nl_attr_put(&req->n, datalen, TCA_HTB_PARMS, &htb_opt, sizeof(htb_opt)); + if (filter_bm & TC_FLOWER_DST_IP) { + const struct prefix *dst_p = + dplane_ctx_tc_filter_get_dst_ip(ctx); - nl_attr_put(&req->n, datalen, TCA_HTB_RTAB, rtab, sizeof(rtab)); - nl_attr_put(&req->n, datalen, TCA_HTB_CTAB, ctab, sizeof(ctab)); - nl_attr_nest_end(&req->n, nest); + if (tc_flower_get_inet_prefix(dst_p, &addr) != 0) + return; - return NLMSG_ALIGN(req->n.nlmsg_len); + nl_attr_put(n, datalen, + (addr.family == AF_INET) ? TCA_FLOWER_KEY_IPV4_DST + : TCA_FLOWER_KEY_IPV6_DST, + addr.data, addr.bytelen); + + if (tc_flower_get_inet_mask(dst_p, &addr) != 0) + return; + + nl_attr_put(n, datalen, + (addr.family == AF_INET) + ? TCA_FLOWER_KEY_IPV4_DST_MASK + : TCA_FLOWER_KEY_IPV6_DST_MASK, + addr.data, addr.bytelen); + } + + if (filter_bm & TC_FLOWER_IP_PROTOCOL) { + nl_attr_put8(n, datalen, TCA_FLOWER_KEY_IP_PROTO, + dplane_ctx_tc_filter_get_ip_proto(ctx)); + } + + if (filter_bm & TC_FLOWER_SRC_PORT) { + uint16_t min, max; + + min = dplane_ctx_tc_filter_get_src_port_min(ctx); + max = dplane_ctx_tc_filter_get_src_port_max(ctx); + + if (max > min) { + nl_attr_put16(n, datalen, TCA_FLOWER_KEY_PORT_SRC_MIN, + htons(min)); + + nl_attr_put16(n, datalen, TCA_FLOWER_KEY_PORT_SRC_MAX, + htons(max)); + } else { + int type = netlink_tfilter_flower_port_type( + dplane_ctx_tc_filter_get_ip_proto(ctx), true); + + if (type < 0) + return; + + nl_attr_put16(n, datalen, type, htons(min)); + } + } + + if (filter_bm & TC_FLOWER_DST_PORT) { + uint16_t min = dplane_ctx_tc_filter_get_dst_port_min(ctx), + max = dplane_ctx_tc_filter_get_dst_port_max(ctx); + + if (max > min) { + nl_attr_put16(n, datalen, TCA_FLOWER_KEY_PORT_DST_MIN, + htons(min)); + + nl_attr_put16(n, datalen, TCA_FLOWER_KEY_PORT_DST_MAX, + htons(max)); + } else { + int type = netlink_tfilter_flower_port_type( + dplane_ctx_tc_filter_get_ip_proto(ctx), false); + + if (type < 0) + return; + + nl_attr_put16(n, datalen, type, htons(min)); + } + } + + if (filter_bm & TC_FLOWER_DSFIELD) { + nl_attr_put8(n, datalen, TCA_FLOWER_KEY_IP_TOS, + dplane_ctx_tc_filter_get_dsfield(ctx)); + nl_attr_put8(n, datalen, TCA_FLOWER_KEY_IP_TOS_MASK, + dplane_ctx_tc_filter_get_dsfield_mask(ctx)); + } + + classid = TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA, + dplane_ctx_tc_filter_get_classid(ctx)); + nl_attr_put32(n, datalen, TCA_FLOWER_CLASSID, classid); + + nl_attr_put32(n, datalen, TCA_FLOWER_FLAGS, flags); + + nl_attr_put16(n, datalen, TCA_FLOWER_KEY_ETH_TYPE, protocol); } /* - * Traffic control filter encoding (only "flower" supported) + * Traffic control filter encoding */ static ssize_t netlink_tfilter_msg_encode(int cmd, struct zebra_dplane_ctx *ctx, void *data, size_t datalen) { + enum dplane_op_e op = dplane_ctx_get_op(ctx); + struct nlsock *nl; - struct rtattr *nest; + const char *kind_str = NULL; - const char *kind = "flower"; + struct rtattr *nest; uint16_t priority; uint16_t protocol; - uint32_t classid; - uint32_t filter_bm; - uint32_t flags = 0; - - struct inet_prefix addr; struct { struct nlmsghdr n; @@ -352,7 +511,8 @@ static ssize_t netlink_tfilter_msg_encode(int cmd, struct zebra_dplane_ctx *ctx, req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; - req->n.nlmsg_flags |= NLM_F_EXCL; + if (op == DPLANE_OP_TC_FILTER_UPDATE) + req->n.nlmsg_flags |= NLM_F_REPLACE; req->n.nlmsg_type = cmd; @@ -361,105 +521,361 @@ static ssize_t netlink_tfilter_msg_encode(int cmd, struct zebra_dplane_ctx *ctx, req->t.tcm_family = AF_UNSPEC; req->t.tcm_ifindex = dplane_ctx_get_ifindex(ctx); - /* TODO: priority and layer-3 protocol support */ - priority = 0; - protocol = htons(ETH_P_IP); - classid = tc_get_handle(ctx, 1); - filter_bm = dplane_ctx_tc_get_filter_bm(ctx); + priority = dplane_ctx_tc_filter_get_priority(ctx); + protocol = htons(dplane_ctx_tc_filter_get_eth_proto(ctx)); + + req->t.tcm_info = TC_H_MAKE(priority << 16, protocol); + req->t.tcm_handle = dplane_ctx_tc_filter_get_handle(ctx); + req->t.tcm_parent = TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA, 0); + + kind_str = dplane_ctx_tc_filter_get_kind_str(ctx); + + if (op == DPLANE_OP_TC_FILTER_ADD || op == DPLANE_OP_TC_FILTER_UPDATE) { + nl_attr_put(&req->n, datalen, TCA_KIND, kind_str, + strlen(kind_str) + 1); + + zlog_debug( + "netlink tfilter encoder: op: %s priority: %u protocol: %u kind: %s handle: %u filter_bm: %u ip_proto: %u", + op == DPLANE_OP_TC_FILTER_UPDATE ? "update" : "add", + priority, protocol, kind_str, + dplane_ctx_tc_filter_get_handle(ctx), + dplane_ctx_tc_filter_get_filter_bm(ctx), + dplane_ctx_tc_filter_get_ip_proto(ctx)); + + nest = nl_attr_nest(&req->n, datalen, TCA_OPTIONS); + switch (dplane_ctx_tc_filter_get_kind(ctx)) { + case TC_FILTER_FLOWER: { + netlink_tfilter_flower_put_options(&req->n, datalen, + ctx); + break; + } + default: + break; + } + nl_attr_nest_end(&req->n, nest); + } + + return NLMSG_ALIGN(req->n.nlmsg_len); +} + +static ssize_t netlink_newqdisc_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) +{ + return netlink_qdisc_msg_encode(RTM_NEWQDISC, ctx, buf, buflen); +} + +static ssize_t netlink_delqdisc_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) +{ + return netlink_qdisc_msg_encode(RTM_DELQDISC, ctx, buf, buflen); +} + +static ssize_t netlink_newtclass_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) +{ + return netlink_tclass_msg_encode(RTM_NEWTCLASS, ctx, buf, buflen); +} - req->t.tcm_info = tc_make_handle(priority, protocol); +static ssize_t netlink_deltclass_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) +{ + return netlink_tclass_msg_encode(RTM_DELTCLASS, ctx, buf, buflen); +} - req->t.tcm_handle = 1; - req->t.tcm_parent = tc_get_handle(ctx, 0); +static ssize_t netlink_newtfilter_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) +{ + return netlink_tfilter_msg_encode(RTM_NEWTFILTER, ctx, buf, buflen); +} - nl_attr_put(&req->n, datalen, TCA_KIND, kind, strlen(kind) + 1); - nest = nl_attr_nest(&req->n, datalen, TCA_OPTIONS); +static ssize_t netlink_deltfilter_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) +{ + return netlink_tfilter_msg_encode(RTM_DELTFILTER, ctx, buf, buflen); +} - nl_attr_put(&req->n, datalen, TCA_FLOWER_CLASSID, &classid, - sizeof(classid)); +enum netlink_msg_status +netlink_put_tc_qdisc_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx) +{ + enum dplane_op_e op; + enum netlink_msg_status ret; - if (filter_bm & TC_FILTER_SRC_IP) { - const struct prefix *src_p = dplane_ctx_tc_get_src_ip(ctx); + op = dplane_ctx_get_op(ctx); - if (tc_flower_get_inet_prefix(src_p, &addr) != 0) - return 0; + if (op == DPLANE_OP_TC_QDISC_INSTALL) { + ret = netlink_batch_add_msg( + bth, ctx, netlink_newqdisc_msg_encoder, false); + } else if (op == DPLANE_OP_TC_QDISC_UNINSTALL) { + ret = netlink_batch_add_msg( + bth, ctx, netlink_delqdisc_msg_encoder, false); + } else { + return FRR_NETLINK_ERROR; + } - nl_attr_put(&req->n, datalen, - (addr.family == AF_INET) ? TCA_FLOWER_KEY_IPV4_SRC - : TCA_FLOWER_KEY_IPV6_SRC, - addr.data, addr.bytelen); + return ret; +} - if (tc_flower_get_inet_mask(src_p, &addr) != 0) - return 0; +enum netlink_msg_status +netlink_put_tc_class_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx) +{ + enum dplane_op_e op; + enum netlink_msg_status ret; - nl_attr_put(&req->n, datalen, - (addr.family == AF_INET) - ? TCA_FLOWER_KEY_IPV4_SRC_MASK - : TCA_FLOWER_KEY_IPV6_SRC_MASK, - addr.data, addr.bytelen); + op = dplane_ctx_get_op(ctx); + + if (op == DPLANE_OP_TC_CLASS_ADD || op == DPLANE_OP_TC_CLASS_UPDATE) { + ret = netlink_batch_add_msg( + bth, ctx, netlink_newtclass_msg_encoder, false); + } else if (op == DPLANE_OP_TC_CLASS_DELETE) { + ret = netlink_batch_add_msg( + bth, ctx, netlink_deltclass_msg_encoder, false); + } else { + return FRR_NETLINK_ERROR; } - if (filter_bm & TC_FILTER_DST_IP) { - const struct prefix *dst_p = dplane_ctx_tc_get_dst_ip(ctx); + return ret; +} - if (tc_flower_get_inet_prefix(dst_p, &addr) != 0) - return 0; +enum netlink_msg_status +netlink_put_tc_filter_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx) +{ + enum dplane_op_e op; + enum netlink_msg_status ret; + + op = dplane_ctx_get_op(ctx); + + if (op == DPLANE_OP_TC_FILTER_ADD) { + ret = netlink_batch_add_msg( + bth, ctx, netlink_newtfilter_msg_encoder, false); + } else if (op == DPLANE_OP_TC_FILTER_UPDATE) { + /* + * Replace will fail if either filter type or the number of + * filter options is changed, so DEL then NEW + * + * TFILTER may have refs to TCLASS. + */ + + (void)netlink_batch_add_msg( + bth, ctx, netlink_deltfilter_msg_encoder, false); + ret = netlink_batch_add_msg( + bth, ctx, netlink_newtfilter_msg_encoder, false); + } else if (op == DPLANE_OP_TC_FILTER_DELETE) { + ret = netlink_batch_add_msg( + bth, ctx, netlink_deltfilter_msg_encoder, false); + } else { + return FRR_NETLINK_ERROR; + } - nl_attr_put(&req->n, datalen, - (addr.family == AF_INET) ? TCA_FLOWER_KEY_IPV4_DST - : TCA_FLOWER_KEY_IPV6_DST, - addr.data, addr.bytelen); + return ret; +} - if (tc_flower_get_inet_mask(dst_p, &addr) != 0) - return 0; +/* + * Request filters from the kernel + */ +static int netlink_request_filters(struct zebra_ns *zns, int family, int type, + ifindex_t ifindex) +{ + struct { + struct nlmsghdr n; + struct tcmsg tc; + } req; - nl_attr_put(&req->n, datalen, - (addr.family == AF_INET) - ? TCA_FLOWER_KEY_IPV4_DST_MASK - : TCA_FLOWER_KEY_IPV6_DST_MASK, - addr.data, addr.bytelen); + memset(&req, 0, sizeof(req)); + req.n.nlmsg_type = type; + req.n.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); + req.tc.tcm_family = family; + req.tc.tcm_ifindex = ifindex; + + return netlink_request(&zns->netlink_cmd, &req); +} + +/* + * Request queue discipline from the kernel + */ +static int netlink_request_qdiscs(struct zebra_ns *zns, int family, int type) +{ + struct { + struct nlmsghdr n; + struct tcmsg tc; + } req; + + memset(&req, 0, sizeof(req)); + req.n.nlmsg_type = type; + req.n.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); + req.tc.tcm_family = family; + + return netlink_request(&zns->netlink_cmd, &req); +} + +int netlink_qdisc_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) +{ + struct tcmsg *tcm; + struct zebra_tc_qdisc qdisc = {}; + + int len; + struct rtattr *tb[TCA_MAX + 1]; + + frrtrace(3, frr_zebra, netlink_tc_qdisc_change, h, ns_id, startup); + + len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct tcmsg)); + + if (len < 0) { + zlog_err( + "%s: Message received from netlink is of a broken size %d %zu", + __func__, h->nlmsg_len, + (size_t)NLMSG_LENGTH(sizeof(struct tcmsg))); + return -1; + } + + tcm = NLMSG_DATA(h); + netlink_parse_rtattr(tb, TCA_MAX, TCA_RTA(tcm), len); + + const char *kind_str = (const char *)RTA_DATA(tb[TCA_KIND]); + + enum tc_qdisc_kind kind = tc_qdisc_str2kind(kind_str); + + qdisc.qdisc.ifindex = tcm->tcm_ifindex; + + switch (kind) { + case TC_QDISC_NOQUEUE: + /* "noqueue" is the default qdisc */ + break; + default: + break; } - if (filter_bm & TC_FILTER_IP_PROTOCOL) { - nl_attr_put8(&req->n, datalen, TCA_FLOWER_KEY_IP_PROTO, - dplane_ctx_tc_get_ip_proto(ctx)); + if (tb[TCA_OPTIONS] != NULL) { + struct rtattr *options[TCA_HTB_MAX + 1]; + + netlink_parse_rtattr_nested(options, TCA_HTB_MAX, + tb[TCA_OPTIONS]); + + /* TODO: more details */ + /* struct tc_htb_glob *glob = RTA_DATA(options[TCA_HTB_INIT]); + */ } - nl_attr_put32(&req->n, datalen, TCA_FLOWER_FLAGS, flags); + if (h->nlmsg_type == RTM_NEWQDISC) { + if (startup && + TC_H_MAJ(tcm->tcm_handle) == TC_QDISC_MAJOR_ZEBRA) { + enum zebra_dplane_result ret; - nl_attr_put16(&req->n, datalen, TCA_FLOWER_KEY_ETH_TYPE, protocol); - nl_attr_nest_end(&req->n, nest); + ret = dplane_tc_qdisc_uninstall(&qdisc); - return NLMSG_ALIGN(req->n.nlmsg_len); + zlog_debug("%s: %s leftover qdisc: ifindex %d kind %s", + __func__, + ((ret == ZEBRA_DPLANE_REQUEST_FAILURE) + ? "Failed to remove" + : "Removed"), + qdisc.qdisc.ifindex, kind_str); + } + } + + return 0; } -static ssize_t netlink_newqdisc_msg_encoder(struct zebra_dplane_ctx *ctx, - void *buf, size_t buflen) +int netlink_tclass_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) { - return netlink_qdisc_msg_encode(RTM_NEWQDISC, ctx, buf, buflen); + struct tcmsg *tcm; + + int len; + struct rtattr *tb[TCA_MAX + 1]; + + frrtrace(3, frr_zebra, netlink_tc_class_change, h, ns_id, startup); + + len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct tcmsg)); + + if (len < 0) { + zlog_err( + "%s: Message received from netlink is of a broken size %d %zu", + __func__, h->nlmsg_len, + (size_t)NLMSG_LENGTH(sizeof(struct tcmsg))); + return -1; + } + + tcm = NLMSG_DATA(h); + netlink_parse_rtattr(tb, TCA_MAX, TCA_RTA(tcm), len); + + + if (tb[TCA_OPTIONS] != NULL) { + struct rtattr *options[TCA_HTB_MAX + 1]; + + netlink_parse_rtattr_nested(options, TCA_HTB_MAX, + tb[TCA_OPTIONS]); + + /* TODO: more details */ + /* struct tc_htb_opt *opt = RTA_DATA(options[TCA_HTB_PARMS]); */ + } + + return 0; } -static ssize_t netlink_newtclass_msg_encoder(struct zebra_dplane_ctx *ctx, - void *buf, size_t buflen) +int netlink_tfilter_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) { - return netlink_tclass_msg_encode(RTM_NEWTCLASS, ctx, buf, buflen); + struct tcmsg *tcm; + + int len; + struct rtattr *tb[TCA_MAX + 1]; + + frrtrace(3, frr_zebra, netlink_tc_filter_change, h, ns_id, startup); + + len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct tcmsg)); + + if (len < 0) { + zlog_err( + "%s: Message received from netlink is of a broken size %d %zu", + __func__, h->nlmsg_len, + (size_t)NLMSG_LENGTH(sizeof(struct tcmsg))); + return -1; + } + + tcm = NLMSG_DATA(h); + netlink_parse_rtattr(tb, TCA_MAX, TCA_RTA(tcm), len); + + return 0; } -static ssize_t netlink_newtfilter_msg_encoder(struct zebra_dplane_ctx *ctx, - void *buf, size_t buflen) +int netlink_qdisc_read(struct zebra_ns *zns) { - return netlink_tfilter_msg_encode(RTM_NEWTFILTER, ctx, buf, buflen); + int ret; + struct zebra_dplane_info dp_info; + + zebra_dplane_info_from_zns(&dp_info, zns, true); + + ret = netlink_request_qdiscs(zns, AF_UNSPEC, RTM_GETQDISC); + if (ret < 0) + return ret; + + ret = netlink_parse_info(netlink_qdisc_change, &zns->netlink_cmd, + &dp_info, 0, true); + if (ret < 0) + return ret; + + return 0; } -enum netlink_msg_status netlink_put_tc_update_msg(struct nl_batch *bth, - struct zebra_dplane_ctx *ctx) +int netlink_tfilter_read_for_interface(struct zebra_ns *zns, ifindex_t ifindex) { - /* TODO: error handling and other actions (delete, replace, ...) */ + int ret; + struct zebra_dplane_info dp_info; + + zebra_dplane_info_from_zns(&dp_info, zns, true); - netlink_batch_add_msg(bth, ctx, netlink_newqdisc_msg_encoder, false); - netlink_batch_add_msg(bth, ctx, netlink_newtclass_msg_encoder, false); - return netlink_batch_add_msg(bth, ctx, netlink_newtfilter_msg_encoder, - false); + ret = netlink_request_filters(zns, AF_UNSPEC, RTM_GETTFILTER, ifindex); + if (ret < 0) + return ret; + + ret = netlink_parse_info(netlink_tfilter_change, &zns->netlink_cmd, + &dp_info, 0, true); + if (ret < 0) + return ret; + + return 0; } #endif /* HAVE_NETLINK */ diff --git a/zebra/tc_netlink.h b/zebra/tc_netlink.h index c43ccb260..166bd917c 100644 --- a/zebra/tc_netlink.h +++ b/zebra/tc_netlink.h @@ -48,7 +48,30 @@ enum { }; extern enum netlink_msg_status -netlink_put_tc_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx); +netlink_put_tc_qdisc_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx); +extern enum netlink_msg_status +netlink_put_tc_class_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx); +extern enum netlink_msg_status +netlink_put_tc_filter_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx); + +/** + * "filter" & "class" in the following become "tfilter" & "tclass" for + * the sake of consistency with kernel message types (RTM_NEWTFILTER etc.) + */ + +extern int netlink_qdisc_read(struct zebra_ns *zns); +extern int netlink_tfilter_read_for_interface(struct zebra_ns *zns, + ifindex_t ifindex); + +extern int netlink_tfilter_change(struct nlmsghdr *h, ns_id_t ns_id, + int startup); +extern int netlink_tclass_change(struct nlmsghdr *h, ns_id_t ns_id, + int startup); +extern int netlink_qdisc_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); + #ifdef __cplusplus } diff --git a/zebra/tc_socket.c b/zebra/tc_socket.c index bf9323b16..17373c7af 100644 --- a/zebra/tc_socket.c +++ b/zebra/tc_socket.c @@ -27,6 +27,7 @@ #include "zebra/rt.h" #include "zebra/zebra_dplane.h" #include "zebra/zebra_errors.h" +#include "zebra/zebra_tc.h" enum zebra_dplane_result kernel_tc_update(struct zebra_dplane_ctx *ctx) { diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 6a691a222..6b9005d64 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -37,6 +37,7 @@ #include "zebra/debug.h" #include "zebra/zebra_pbr.h" #include "zebra/zebra_neigh.h" +#include "zebra/zebra_tc.h" #include "printfrr.h" /* Memory types */ @@ -313,23 +314,36 @@ struct dplane_netconf_info { enum dplane_netconf_status_e linkdown_val; }; -/* - * Traffic control contexts for the dplane - */ -struct dplane_tc_info { - /* Rate spec (unit: Bytes/s) */ +struct dplane_tc_qdisc_info { + enum tc_qdisc_kind kind; + const char *kind_str; +}; + +struct dplane_tc_class_info { + uint32_t handle; + enum tc_qdisc_kind kind; + const char *kind_str; uint64_t rate; uint64_t ceil; +}; - /* TODO: custom burst */ - - /* Filter components for "tfilter" */ +struct dplane_tc_filter_info { + uint32_t handle; + uint16_t priority; + enum tc_filter_kind kind; + const char *kind_str; uint32_t filter_bm; + uint16_t eth_proto; + uint8_t ip_proto; struct prefix src_ip; struct prefix dst_ip; - uint8_t ip_proto; - - /* TODO: more filter components */ + uint16_t src_port_min; + uint16_t src_port_max; + uint16_t dst_port_min; + uint16_t dst_port_max; + uint8_t dsfield; + uint8_t dsfield_mask; + uint32_t classid; }; /* @@ -381,7 +395,9 @@ struct zebra_dplane_ctx { struct dplane_mac_info macinfo; struct dplane_neigh_info neigh; struct dplane_rule_info rule; - struct dplane_tc_info tc; + struct dplane_tc_qdisc_info tc_qdisc; + struct dplane_tc_class_info tc_class; + struct dplane_tc_filter_info tc_filter; struct zebra_pbr_iptable iptable; struct zebra_pbr_ipset ipset; struct { @@ -800,9 +816,14 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx) case DPLANE_OP_INTF_INSTALL: case DPLANE_OP_INTF_UPDATE: case DPLANE_OP_INTF_DELETE: - case DPLANE_OP_TC_INSTALL: - case DPLANE_OP_TC_UPDATE: - case DPLANE_OP_TC_DELETE: + case DPLANE_OP_TC_QDISC_INSTALL: + case DPLANE_OP_TC_QDISC_UNINSTALL: + case DPLANE_OP_TC_CLASS_ADD: + case DPLANE_OP_TC_CLASS_DELETE: + case DPLANE_OP_TC_CLASS_UPDATE: + case DPLANE_OP_TC_FILTER_ADD: + case DPLANE_OP_TC_FILTER_DELETE: + case DPLANE_OP_TC_FILTER_UPDATE: break; case DPLANE_OP_IPSET_ENTRY_ADD: @@ -1127,14 +1148,29 @@ const char *dplane_op2str(enum dplane_op_e op) ret = "INTF_DELETE"; break; - case DPLANE_OP_TC_INSTALL: - ret = "TC_INSTALL"; + case DPLANE_OP_TC_QDISC_INSTALL: + ret = "TC_QDISC_INSTALL"; + break; + case DPLANE_OP_TC_QDISC_UNINSTALL: + ret = "TC_QDISC_UNINSTALL"; + break; + case DPLANE_OP_TC_CLASS_ADD: + ret = "TC_CLASS_ADD"; + break; + case DPLANE_OP_TC_CLASS_DELETE: + ret = "TC_CLASS_DELETE"; + break; + case DPLANE_OP_TC_CLASS_UPDATE: + ret = "TC_CLASS_UPDATE"; + break; + case DPLANE_OP_TC_FILTER_ADD: + ret = "TC_FILTER_ADD"; break; - case DPLANE_OP_TC_UPDATE: - ret = "TC_UPDATE"; + case DPLANE_OP_TC_FILTER_DELETE: + ret = "TC_FILTER_DELETE"; break; - case DPLANE_OP_TC_DELETE: - ret = "TC_DELETE"; + case DPLANE_OP_TC_FILTER_UPDATE: + ret = "TC__FILTER_UPDATE"; break; } @@ -1455,48 +1491,175 @@ uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx *ctx) return ctx->u.rinfo.zd_old_distance; } -uint64_t dplane_ctx_tc_get_rate(const struct zebra_dplane_ctx *ctx) +int dplane_ctx_tc_qdisc_get_kind(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.tc_qdisc.kind; +} + +const char *dplane_ctx_tc_qdisc_get_kind_str(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.tc_qdisc.kind_str; +} + +uint32_t dplane_ctx_tc_class_get_handle(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.tc_class.handle; +} + +int dplane_ctx_tc_class_get_kind(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.tc_class.kind; +} + +const char *dplane_ctx_tc_class_get_kind_str(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.tc_class.kind_str; +} + +uint64_t dplane_ctx_tc_class_get_rate(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.tc_class.rate; +} + +uint64_t dplane_ctx_tc_class_get_ceil(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.tc_class.ceil; +} + +int dplane_ctx_tc_filter_get_kind(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.tc_filter.kind; +} + +const char * +dplane_ctx_tc_filter_get_kind_str(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.tc_filter.kind_str; +} + +uint32_t dplane_ctx_tc_filter_get_priority(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.tc_filter.priority; +} + +uint32_t dplane_ctx_tc_filter_get_handle(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return ctx->u.tc.rate; + return ctx->u.tc_filter.handle; } -uint64_t dplane_ctx_tc_get_ceil(const struct zebra_dplane_ctx *ctx) +uint16_t dplane_ctx_tc_filter_get_eth_proto(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return ctx->u.tc.ceil; + return ctx->u.tc_filter.eth_proto; } -uint32_t dplane_ctx_tc_get_filter_bm(const struct zebra_dplane_ctx *ctx) +uint32_t dplane_ctx_tc_filter_get_filter_bm(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return ctx->u.tc.filter_bm; + return ctx->u.tc_filter.filter_bm; } const struct prefix * -dplane_ctx_tc_get_src_ip(const struct zebra_dplane_ctx *ctx) +dplane_ctx_tc_filter_get_src_ip(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &ctx->u.tc_filter.src_ip; +} + +uint16_t +dplane_ctx_tc_filter_get_src_port_min(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.tc_filter.src_port_min; +} + + +uint16_t +dplane_ctx_tc_filter_get_src_port_max(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return &(ctx->u.tc.src_ip); + return ctx->u.tc_filter.src_port_max; } const struct prefix * -dplane_ctx_tc_get_dst_ip(const struct zebra_dplane_ctx *ctx) +dplane_ctx_tc_filter_get_dst_ip(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &ctx->u.tc_filter.dst_ip; +} + +uint16_t +dplane_ctx_tc_filter_get_dst_port_min(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return &(ctx->u.tc.dst_ip); + return ctx->u.tc_filter.dst_port_min; } -uint8_t dplane_ctx_tc_get_ip_proto(const struct zebra_dplane_ctx *ctx) + +uint16_t +dplane_ctx_tc_filter_get_dst_port_max(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return ctx->u.tc.ip_proto; + return ctx->u.tc_filter.dst_port_max; +} + +uint8_t dplane_ctx_tc_filter_get_ip_proto(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.tc_filter.ip_proto; +} + +uint8_t dplane_ctx_tc_filter_get_dsfield(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.tc_filter.dsfield; +} + +uint8_t +dplane_ctx_tc_filter_get_dsfield_mask(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.tc_filter.dsfield_mask; +} + +uint32_t dplane_ctx_tc_filter_get_classid(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.tc_filter.classid; } /* @@ -2771,7 +2934,9 @@ done: return ret; } -int dplane_ctx_tc_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op) +static int dplane_ctx_tc_qdisc_init(struct zebra_dplane_ctx *ctx, + enum dplane_op_e op, + const struct zebra_tc_qdisc *qdisc) { int ret = EINVAL; @@ -2779,6 +2944,9 @@ int dplane_ctx_tc_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op) ctx->zd_op = op; ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; + ctx->zd_ifindex = qdisc->qdisc.ifindex; + ctx->u.tc_qdisc.kind = qdisc->qdisc.kind; + ctx->u.tc_qdisc.kind_str = tc_qdisc_kind2str(qdisc->qdisc.kind); /* TODO: init traffic control qdisc */ zns = zebra_ns_lookup(NS_DEFAULT); @@ -2790,6 +2958,74 @@ int dplane_ctx_tc_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op) return ret; } +static int dplane_ctx_tc_class_init(struct zebra_dplane_ctx *ctx, + enum dplane_op_e op, + struct zebra_tc_class *class) +{ + int ret = EINVAL; + + struct zebra_ns *zns = NULL; + + ctx->zd_op = op; + ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; + ctx->zd_ifindex = class->class.ifindex; + + ctx->u.tc_class.handle = class->class.handle; + ctx->u.tc_class.kind = class->class.kind; + ctx->u.tc_class.kind_str = tc_qdisc_kind2str(class->class.kind); + ctx->u.tc_class.rate = class->class.u.htb.rate; + ctx->u.tc_class.ceil = class->class.u.htb.ceil; + + zns = zebra_ns_lookup(NS_DEFAULT); + + dplane_ctx_ns_init(ctx, zns, true); + + ret = AOK; + + return ret; +} + +static int dplane_ctx_tc_filter_init(struct zebra_dplane_ctx *ctx, + enum dplane_op_e op, + struct zebra_tc_filter *filter) +{ + int ret = EINVAL; + + struct zebra_ns *zns = NULL; + + ctx->zd_op = op; + ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; + ctx->zd_ifindex = filter->filter.ifindex; + + ctx->u.tc_filter.eth_proto = filter->filter.protocol; + ctx->u.tc_filter.ip_proto = filter->filter.u.flower.ip_proto; + + ctx->u.tc_filter.kind = filter->filter.kind; + ctx->u.tc_filter.kind_str = tc_filter_kind2str(filter->filter.kind); + + ctx->u.tc_filter.filter_bm = filter->filter.u.flower.filter_bm; + prefix_copy(&ctx->u.tc_filter.src_ip, &filter->filter.u.flower.src_ip); + ctx->u.tc_filter.src_port_min = filter->filter.u.flower.src_port_min; + ctx->u.tc_filter.src_port_max = filter->filter.u.flower.src_port_max; + prefix_copy(&ctx->u.tc_filter.dst_ip, &filter->filter.u.flower.dst_ip); + ctx->u.tc_filter.dst_port_min = filter->filter.u.flower.dst_port_min; + ctx->u.tc_filter.dst_port_max = filter->filter.u.flower.dst_port_max; + ctx->u.tc_filter.dsfield = filter->filter.u.flower.dsfield; + ctx->u.tc_filter.dsfield_mask = filter->filter.u.flower.dsfield_mask; + ctx->u.tc_filter.classid = filter->filter.u.flower.classid; + + ctx->u.tc_filter.priority = filter->filter.priority; + ctx->u.tc_filter.handle = filter->filter.handle; + + zns = zebra_ns_lookup(NS_DEFAULT); + + dplane_ctx_ns_init(ctx, zns, true); + + ret = AOK; + + return ret; +} + /** * dplane_ctx_nexthop_init() - Initialize a context block for a nexthop update * @@ -3509,7 +3745,9 @@ dplane_route_update_internal(struct route_node *rn, return result; } -static enum zebra_dplane_result dplane_tc_update_internal(enum dplane_op_e op) +static enum zebra_dplane_result +tc_qdisc_update_internal(enum dplane_op_e op, + const struct zebra_tc_qdisc *qdisc) { enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; int ret; @@ -3524,7 +3762,7 @@ static enum zebra_dplane_result dplane_tc_update_internal(enum dplane_op_e op) } /* Init context with info from zebra data structs */ - ret = dplane_ctx_tc_init(ctx, op); + ret = dplane_ctx_tc_qdisc_init(ctx, op, qdisc); if (ret == AOK) ret = dplane_update_enqueue(ctx); @@ -3545,9 +3783,118 @@ done: return result; } -enum zebra_dplane_result dplane_tc_update(void) +static enum zebra_dplane_result +tc_class_update_internal(enum dplane_op_e op, struct zebra_tc_class *class) { - return dplane_tc_update_internal(DPLANE_OP_TC_UPDATE); + enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; + int ret; + struct zebra_dplane_ctx *ctx = NULL; + + /* Obtain context block */ + ctx = dplane_ctx_alloc(); + + if (!ctx) { + ret = ENOMEM; + goto done; + } + + /* Init context with info from zebra data structs */ + ret = dplane_ctx_tc_class_init(ctx, op, class); + + if (ret == AOK) + ret = dplane_update_enqueue(ctx); + +done: + /* Update counter */ + atomic_fetch_add_explicit(&zdplane_info.dg_tcs_in, 1, + memory_order_relaxed); + if (ret == AOK) { + result = ZEBRA_DPLANE_REQUEST_QUEUED; + } else { + atomic_fetch_add_explicit(&zdplane_info.dg_tcs_errors, 1, + memory_order_relaxed); + if (ctx) + dplane_ctx_free(&ctx); + } + + return result; +} + +static enum zebra_dplane_result +tc_filter_update_internal(enum dplane_op_e op, struct zebra_tc_filter *filter) +{ + enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; + int ret; + struct zebra_dplane_ctx *ctx = NULL; + + /* Obtain context block */ + ctx = dplane_ctx_alloc(); + + if (!ctx) { + ret = ENOMEM; + goto done; + } + + /* Init context with info from zebra data structs */ + ret = dplane_ctx_tc_filter_init(ctx, op, filter); + + if (ret == AOK) + ret = dplane_update_enqueue(ctx); + +done: + /* Update counter */ + atomic_fetch_add_explicit(&zdplane_info.dg_tcs_in, 1, + memory_order_relaxed); + if (ret == AOK) { + result = ZEBRA_DPLANE_REQUEST_QUEUED; + } else { + atomic_fetch_add_explicit(&zdplane_info.dg_tcs_errors, 1, + memory_order_relaxed); + if (ctx) + dplane_ctx_free(&ctx); + } + + return result; +} + +enum zebra_dplane_result dplane_tc_qdisc_install(struct zebra_tc_qdisc *qdisc) +{ + return tc_qdisc_update_internal(DPLANE_OP_TC_QDISC_INSTALL, qdisc); +} + +enum zebra_dplane_result dplane_tc_qdisc_uninstall(struct zebra_tc_qdisc *qdisc) +{ + return tc_qdisc_update_internal(DPLANE_OP_TC_QDISC_UNINSTALL, qdisc); +} + +enum zebra_dplane_result dplane_tc_class_add(struct zebra_tc_class *class) +{ + return tc_class_update_internal(DPLANE_OP_TC_CLASS_ADD, class); +} + +enum zebra_dplane_result dplane_tc_class_delete(struct zebra_tc_class *class) +{ + return tc_class_update_internal(DPLANE_OP_TC_CLASS_DELETE, class); +} + +enum zebra_dplane_result dplane_tc_class_update(struct zebra_tc_class *class) +{ + return tc_class_update_internal(DPLANE_OP_TC_CLASS_UPDATE, class); +} + +enum zebra_dplane_result dplane_tc_filter_add(struct zebra_tc_filter *filter) +{ + return tc_filter_update_internal(DPLANE_OP_TC_FILTER_ADD, filter); +} + +enum zebra_dplane_result dplane_tc_filter_delete(struct zebra_tc_filter *filter) +{ + return tc_filter_update_internal(DPLANE_OP_TC_FILTER_DELETE, filter); +} + +enum zebra_dplane_result dplane_tc_filter_update(struct zebra_tc_filter *filter) +{ + return tc_filter_update_internal(DPLANE_OP_TC_FILTER_UPDATE, filter); } /** @@ -5733,10 +6080,18 @@ static void kernel_dplane_log_detail(struct zebra_dplane_ctx *ctx) break; /* TODO: more detailed log */ - case DPLANE_OP_TC_INSTALL: - case DPLANE_OP_TC_UPDATE: - case DPLANE_OP_TC_DELETE: - zlog_debug("Dplane tc ifidx %u", dplane_ctx_get_ifindex(ctx)); + case DPLANE_OP_TC_QDISC_INSTALL: + case DPLANE_OP_TC_QDISC_UNINSTALL: + zlog_debug("Dplane tc qdisc ifidx %u", + dplane_ctx_get_ifindex(ctx)); + break; + case DPLANE_OP_TC_CLASS_ADD: + case DPLANE_OP_TC_CLASS_DELETE: + case DPLANE_OP_TC_CLASS_UPDATE: + break; + case DPLANE_OP_TC_FILTER_ADD: + case DPLANE_OP_TC_FILTER_DELETE: + case DPLANE_OP_TC_FILTER_UPDATE: break; } } @@ -5881,9 +6236,14 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx) 1, memory_order_relaxed); break; - case DPLANE_OP_TC_INSTALL: - case DPLANE_OP_TC_UPDATE: - case DPLANE_OP_TC_DELETE: + case DPLANE_OP_TC_QDISC_INSTALL: + case DPLANE_OP_TC_QDISC_UNINSTALL: + case DPLANE_OP_TC_CLASS_ADD: + case DPLANE_OP_TC_CLASS_DELETE: + case DPLANE_OP_TC_CLASS_UPDATE: + case DPLANE_OP_TC_FILTER_ADD: + case DPLANE_OP_TC_FILTER_DELETE: + case DPLANE_OP_TC_FILTER_UPDATE: if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) atomic_fetch_add_explicit(&zdplane_info.dg_tcs_errors, 1, memory_order_relaxed); diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 8b239a9ba..b9fd176de 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -195,9 +195,14 @@ enum dplane_op_e { DPLANE_OP_INTF_DELETE, /* Traffic control */ - DPLANE_OP_TC_INSTALL, - DPLANE_OP_TC_UPDATE, - DPLANE_OP_TC_DELETE, + DPLANE_OP_TC_QDISC_INSTALL, + DPLANE_OP_TC_QDISC_UNINSTALL, + DPLANE_OP_TC_CLASS_ADD, + DPLANE_OP_TC_CLASS_DELETE, + DPLANE_OP_TC_CLASS_UPDATE, + DPLANE_OP_TC_FILTER_ADD, + DPLANE_OP_TC_FILTER_DELETE, + DPLANE_OP_TC_FILTER_UPDATE }; /* @@ -384,14 +389,42 @@ void dplane_ctx_set_distance(struct zebra_dplane_ctx *ctx, uint8_t distance); uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx *ctx); /* Accessors for traffic control context */ -uint64_t dplane_ctx_tc_get_rate(const struct zebra_dplane_ctx *ctx); -uint64_t dplane_ctx_tc_get_ceil(const struct zebra_dplane_ctx *ctx); -uint32_t dplane_ctx_tc_get_filter_bm(const struct zebra_dplane_ctx *ctx); +int dplane_ctx_tc_qdisc_get_kind(const struct zebra_dplane_ctx *ctx); +const char * +dplane_ctx_tc_qdisc_get_kind_str(const struct zebra_dplane_ctx *ctx); + +uint32_t dplane_ctx_tc_class_get_handle(const struct zebra_dplane_ctx *ctx); +int dplane_ctx_tc_class_get_kind(const struct zebra_dplane_ctx *ctx); +const char * +dplane_ctx_tc_class_get_kind_str(const struct zebra_dplane_ctx *ctx); +uint64_t dplane_ctx_tc_class_get_rate(const struct zebra_dplane_ctx *ctx); +uint64_t dplane_ctx_tc_class_get_ceil(const struct zebra_dplane_ctx *ctx); + +int dplane_ctx_tc_filter_get_kind(const struct zebra_dplane_ctx *ctx); +const char * +dplane_ctx_tc_filter_get_kind_str(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_tc_filter_get_priority(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_tc_filter_get_handle(const struct zebra_dplane_ctx *ctx); +uint16_t dplane_ctx_tc_filter_get_minor(const struct zebra_dplane_ctx *ctx); +uint16_t dplane_ctx_tc_filter_get_eth_proto(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_tc_filter_get_filter_bm(const struct zebra_dplane_ctx *ctx); const struct prefix * -dplane_ctx_tc_get_src_ip(const struct zebra_dplane_ctx *ctx); +dplane_ctx_tc_filter_get_src_ip(const struct zebra_dplane_ctx *ctx); +uint16_t +dplane_ctx_tc_filter_get_src_port_min(const struct zebra_dplane_ctx *ctx); +uint16_t +dplane_ctx_tc_filter_get_src_port_max(const struct zebra_dplane_ctx *ctx); const struct prefix * -dplane_ctx_tc_get_dst_ip(const struct zebra_dplane_ctx *ctx); -uint8_t dplane_ctx_tc_get_ip_proto(const struct zebra_dplane_ctx *ctx); +dplane_ctx_tc_filter_get_dst_ip(const struct zebra_dplane_ctx *ctx); +uint16_t +dplane_ctx_tc_filter_get_dst_port_min(const struct zebra_dplane_ctx *ctx); +uint16_t +dplane_ctx_tc_filter_get_dst_port_max(const struct zebra_dplane_ctx *ctx); +uint8_t dplane_ctx_tc_filter_get_ip_proto(const struct zebra_dplane_ctx *ctx); +uint8_t dplane_ctx_tc_filter_get_dsfield(const struct zebra_dplane_ctx *ctx); +uint8_t +dplane_ctx_tc_filter_get_dsfield_mask(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_tc_filter_get_classid(const struct zebra_dplane_ctx *ctx); void dplane_ctx_set_nexthops(struct zebra_dplane_ctx *ctx, struct nexthop *nh); void dplane_ctx_set_backup_nhg(struct zebra_dplane_ctx *ctx, @@ -723,11 +756,23 @@ enum zebra_dplane_result dplane_intf_update(const struct interface *ifp); enum zebra_dplane_result dplane_intf_delete(const struct interface *ifp); /* - * Enqueue interface link changes for the dataplane. + * Enqueue tc link changes for the dataplane. */ -enum zebra_dplane_result dplane_tc_add(void); -enum zebra_dplane_result dplane_tc_update(void); -enum zebra_dplane_result dplane_tc_delete(void); + +struct zebra_tc_qdisc; +struct zebra_tc_class; +struct zebra_tc_filter; +enum zebra_dplane_result dplane_tc_qdisc_install(struct zebra_tc_qdisc *qdisc); +enum zebra_dplane_result +dplane_tc_qdisc_uninstall(struct zebra_tc_qdisc *qdisc); +enum zebra_dplane_result dplane_tc_class_add(struct zebra_tc_class *class); +enum zebra_dplane_result dplane_tc_class_delete(struct zebra_tc_class *class); +enum zebra_dplane_result dplane_tc_class_update(struct zebra_tc_class *class); +enum zebra_dplane_result dplane_tc_filter_add(struct zebra_tc_filter *filter); +enum zebra_dplane_result +dplane_tc_filter_delete(struct zebra_tc_filter *filter); +enum zebra_dplane_result +dplane_tc_filter_update(struct zebra_tc_filter *filter); /* * Link layer operations for the dataplane. diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 5fadd4c82..286cc0292 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -3168,9 +3168,14 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx) case DPLANE_OP_INTF_INSTALL: case DPLANE_OP_INTF_UPDATE: case DPLANE_OP_INTF_DELETE: - case DPLANE_OP_TC_INSTALL: - case DPLANE_OP_TC_UPDATE: - case DPLANE_OP_TC_DELETE: + case DPLANE_OP_TC_QDISC_INSTALL: + case DPLANE_OP_TC_QDISC_UNINSTALL: + case DPLANE_OP_TC_CLASS_ADD: + case DPLANE_OP_TC_CLASS_DELETE: + case DPLANE_OP_TC_CLASS_UPDATE: + case DPLANE_OP_TC_FILTER_ADD: + case DPLANE_OP_TC_FILTER_DELETE: + case DPLANE_OP_TC_FILTER_UPDATE: break; } } diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 13fd97249..f52c5e005 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -34,6 +34,7 @@ #include "zebra_netns_notify.h" #include "zebra_netns_id.h" #include "zebra_pbr.h" +#include "zebra_tc.h" #include "rib.h" #include "table_manager.h" #include "zebra_errors.h" @@ -127,6 +128,7 @@ int zebra_ns_enable(ns_id_t ns_id, void **info) interface_list(zns); route_read(zns); kernel_read_pbr_rules(zns); + kernel_read_tc_qdisc(zns); return 0; } diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 54ef4768e..656588bb8 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -4710,9 +4710,14 @@ static void rib_process_dplane_results(struct thread *thread) zebra_if_dplane_result(ctx); break; - case DPLANE_OP_TC_INSTALL: - case DPLANE_OP_TC_UPDATE: - case DPLANE_OP_TC_DELETE: + case DPLANE_OP_TC_QDISC_INSTALL: + case DPLANE_OP_TC_QDISC_UNINSTALL: + case DPLANE_OP_TC_CLASS_ADD: + case DPLANE_OP_TC_CLASS_DELETE: + case DPLANE_OP_TC_CLASS_UPDATE: + case DPLANE_OP_TC_FILTER_ADD: + case DPLANE_OP_TC_FILTER_DELETE: + case DPLANE_OP_TC_FILTER_UPDATE: break; /* Some op codes not handled here */ diff --git a/zebra/zebra_router.c b/zebra/zebra_router.c index c66849863..b8923ef57 100644 --- a/zebra/zebra_router.c +++ b/zebra/zebra_router.c @@ -30,6 +30,7 @@ #include "zebra_mlag.h" #include "zebra_nhg.h" #include "zebra_neigh.h" +#include "zebra/zebra_tc.h" #include "debug.h" #include "zebra_script.h" @@ -312,6 +313,20 @@ void zebra_router_init(bool asic_offload, bool notify_on_ack) hash_create_size(8, zebra_nhg_id_key, zebra_nhg_hash_id_equal, "Zebra Router Nexthop Groups ID index"); + zrouter.rules_hash = + hash_create_size(8, zebra_pbr_rules_hash_key, + zebra_pbr_rules_hash_equal, "Rules Hash"); + + zrouter.qdisc_hash = + hash_create_size(8, zebra_tc_qdisc_hash_key, + zebra_tc_qdisc_hash_equal, "TC (qdisc) Hash"); + zrouter.class_hash = hash_create_size(8, zebra_tc_class_hash_key, + zebra_tc_class_hash_equal, + "TC (classes) Hash"); + zrouter.filter_hash = hash_create_size(8, zebra_tc_filter_hash_key, + zebra_tc_filter_hash_equal, + "TC (filter) Hash"); + zrouter.asic_offloaded = asic_offload; zrouter.notify_on_ack = notify_on_ack; diff --git a/zebra/zebra_router.h b/zebra/zebra_router.h index 992bcd5c0..069437ef4 100644 --- a/zebra/zebra_router.h +++ b/zebra/zebra_router.h @@ -172,6 +172,10 @@ struct zebra_router { struct hash *iptable_hash; + struct hash *qdisc_hash; + struct hash *class_hash; + struct hash *filter_hash; + /* A sequence number used for tracking routes */ _Atomic uint32_t sequence_num; diff --git a/zebra/zebra_tc.c b/zebra/zebra_tc.c new file mode 100644 index 000000000..09d9986fb --- /dev/null +++ b/zebra/zebra_tc.c @@ -0,0 +1,444 @@ +/* + * Zebra Traffic Control (TC) main handling. + * + * Copyright (C) 2022 Shichu Yang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> + +#include <jhash.h> +#include <hash.h> +#include <memory.h> +#include <hook.h> + +#include "zebra/zebra_router.h" +#include "zebra/zebra_dplane.h" +#include "zebra/zebra_tc.h" +#include "zebra/debug.h" + +DEFINE_MTYPE_STATIC(ZEBRA, TC_QDISC, "TC queue discipline"); +DEFINE_MTYPE_STATIC(ZEBRA, TC_CLASS, "TC class"); +DEFINE_MTYPE_STATIC(ZEBRA, TC_FILTER, "TC filter"); + +const struct message tc_qdisc_kinds[] = { + {TC_QDISC_HTB, "htb"}, + {TC_QDISC_NOQUEUE, "noqueue"}, + {0}, +}; + +const struct message tc_filter_kinds[] = { + {TC_FILTER_BPF, "bpf"}, + {TC_FILTER_FLOW, "flow"}, + {TC_FILTER_FLOWER, "flower"}, + {TC_FILTER_U32, "u32"}, + {0}, +}; + +const struct message *tc_class_kinds = tc_qdisc_kinds; + +static uint32_t lookup_key(const struct message *mz, const char *msg, + uint32_t nf) +{ + static struct message nt = {0}; + uint32_t rz = nf ? nf : UINT32_MAX; + const struct message *pnt; + + for (pnt = mz; memcmp(pnt, &nt, sizeof(struct message)); pnt++) + if (strcmp(pnt->str, msg) == 0) { + rz = pnt->key; + break; + } + return rz; +} + +const char *tc_qdisc_kind2str(uint32_t type) +{ + return lookup_msg(tc_qdisc_kinds, type, "Unrecognized QDISC Type"); +} + +enum tc_qdisc_kind tc_qdisc_str2kind(const char *type) +{ + return lookup_key(tc_qdisc_kinds, type, TC_QDISC_UNSPEC); +} + +uint32_t zebra_tc_qdisc_hash_key(const void *arg) +{ + const struct zebra_tc_qdisc *qdisc; + uint32_t key; + + qdisc = arg; + + key = jhash_1word(qdisc->qdisc.ifindex, 0); + + return key; +} + +bool zebra_tc_qdisc_hash_equal(const void *arg1, const void *arg2) +{ + const struct zebra_tc_qdisc *q1, *q2; + + q1 = (const struct zebra_tc_qdisc *)arg1; + q2 = (const struct zebra_tc_qdisc *)arg2; + + if (q1->qdisc.ifindex != q2->qdisc.ifindex) + return false; + + return true; +} + +struct tc_qdisc_ifindex_lookup { + struct zebra_tc_qdisc *qdisc; + ifindex_t ifindex; +}; + + +static int tc_qdisc_lookup_ifindex_walker(struct hash_bucket *b, void *data) +{ + struct tc_qdisc_ifindex_lookup *lookup = data; + struct zebra_tc_qdisc *qdisc = b->data; + + if (lookup->ifindex == qdisc->qdisc.ifindex) { + lookup->qdisc = qdisc; + return HASHWALK_ABORT; + } + + return HASHWALK_CONTINUE; +} + +static struct zebra_tc_qdisc * +tc_qdisc_lookup_ifindex(struct zebra_tc_qdisc *qdisc) +{ + struct tc_qdisc_ifindex_lookup lookup; + + lookup.ifindex = qdisc->qdisc.ifindex; + lookup.qdisc = NULL; + hash_walk(zrouter.rules_hash, &tc_qdisc_lookup_ifindex_walker, &lookup); + + return lookup.qdisc; +} + +static void *tc_qdisc_alloc_intern(void *arg) +{ + struct zebra_tc_qdisc *ztq; + struct zebra_tc_qdisc *new; + + ztq = (struct zebra_tc_qdisc *)arg; + + new = XCALLOC(MTYPE_TC_QDISC, sizeof(*new)); + + memcpy(new, ztq, sizeof(*ztq)); + + return new; +} + +static struct zebra_tc_qdisc *tc_qdisc_free(struct zebra_tc_qdisc *hash_data, + bool free_data) +{ + hash_release(zrouter.qdisc_hash, hash_data); + + if (free_data) { + XFREE(MTYPE_TC_QDISC, hash_data); + return NULL; + } + + return hash_data; +} + +static struct zebra_tc_qdisc *tc_qdisc_release(struct zebra_tc_qdisc *qdisc, + bool free_data) +{ + struct zebra_tc_qdisc *lookup; + + lookup = hash_lookup(zrouter.qdisc_hash, qdisc); + + if (!lookup) + return NULL; + + return tc_qdisc_free(lookup, free_data); +} + +void zebra_tc_qdisc_install(struct zebra_tc_qdisc *qdisc) +{ + if (IS_ZEBRA_DEBUG_TC) + zlog_debug("%s: install tc qdisc ifindex %d kind %s", __func__, + qdisc->qdisc.ifindex, + tc_qdisc_kind2str(qdisc->qdisc.kind)); + + struct zebra_tc_qdisc *found; + struct zebra_tc_qdisc *old; + struct zebra_tc_qdisc *new; + + found = tc_qdisc_lookup_ifindex(qdisc); + + if (found) { + if (!zebra_tc_qdisc_hash_equal(qdisc, found)) { + old = tc_qdisc_release(found, false); + (void)dplane_tc_qdisc_uninstall(old); + new = hash_get(zrouter.qdisc_hash, qdisc, + tc_qdisc_alloc_intern); + (void)dplane_tc_qdisc_install(new); + XFREE(MTYPE_TC_QDISC, old); + } + } else { + new = hash_get(zrouter.qdisc_hash, qdisc, + tc_qdisc_alloc_intern); + (void)dplane_tc_qdisc_install(new); + } +} + +void zebra_tc_qdisc_uninstall(struct zebra_tc_qdisc *qdisc) +{ + if (IS_ZEBRA_DEBUG_TC) + zlog_debug("%s: uninstall tc qdisc ifindex %d kind %s", + __func__, qdisc->qdisc.ifindex, + tc_qdisc_kind2str(qdisc->qdisc.kind)); + + (void)dplane_tc_qdisc_uninstall(qdisc); + + if (tc_qdisc_release(qdisc, true)) + zlog_debug("%s: tc qdisc being deleted we know nothing about", + __func__); +} + +uint32_t zebra_tc_class_hash_key(const void *arg) +{ + const struct zebra_tc_class *class; + uint32_t key; + + class = arg; + + key = jhash_2words(class->class.ifindex, class->class.handle, 0); + + return key; +} + +bool zebra_tc_class_hash_equal(const void *arg1, const void *arg2) +{ + const struct zebra_tc_class *c1, *c2; + + c1 = (const struct zebra_tc_class *)arg1; + c2 = (const struct zebra_tc_class *)arg2; + + if (c1->class.ifindex != c2->class.ifindex) + return false; + + if (c1->class.handle != c2->class.handle) + return false; + + return true; +} + +static void *tc_class_alloc_intern(void *arg) +{ + struct zebra_tc_class *class; + struct zebra_tc_class *new; + + class = (struct zebra_tc_class *)arg; + + new = XCALLOC(MTYPE_TC_CLASS, sizeof(*new)); + + memcpy(new, class, sizeof(*class)); + + return new; +} + +static struct zebra_tc_class *tc_class_free(struct zebra_tc_class *hash_data, + bool free_data) +{ + hash_release(zrouter.class_hash, hash_data); + + if (free_data) { + XFREE(MTYPE_TC_CLASS, hash_data); + return NULL; + } + + return hash_data; +} + +static struct zebra_tc_class *tc_class_release(struct zebra_tc_class *class, + bool free_data) +{ + struct zebra_tc_class *lookup; + + lookup = hash_lookup(zrouter.class_hash, class); + + if (!lookup) + return NULL; + + return tc_class_free(lookup, free_data); +} + +void zebra_tc_class_add(struct zebra_tc_class *class) +{ + if (IS_ZEBRA_DEBUG_TC) + zlog_debug( + "%s: add tc class ifindex %d handle %04x:%04x kind %s", + __func__, class->class.ifindex, + (class->class.handle & 0xffff0000u) >> 16, + class->class.handle & 0x0000ffffu, + tc_qdisc_kind2str(class->class.kind)); + + struct zebra_tc_class *found; + struct zebra_tc_class *new; + + /* + * We find the class in the hash by (ifindex, handle) directly, and by + * testing their deep equality to seek out whether it's an update. + * + * Currently deep equality is not checked since it will be okay to + * update the totally same class again. + */ + found = hash_lookup(zrouter.class_hash, class); + new = hash_get(zrouter.class_hash, class, tc_class_alloc_intern); + + if (found) + (void)dplane_tc_class_update(new); + else + (void)dplane_tc_class_add(new); +} + +void zebra_tc_class_delete(struct zebra_tc_class *class) +{ + if (IS_ZEBRA_DEBUG_TC) + zlog_debug( + "%s: delete tc class ifindex %d handle %04x:%04x kind %s", + __func__, class->class.ifindex, + (class->class.handle & 0xffff0000u) >> 16, + class->class.handle & 0x0000ffffu, + tc_qdisc_kind2str(class->class.kind)); + + (void)dplane_tc_class_delete(class); + + if (tc_class_release(class, true)) + zlog_debug("%s: tc class being deleted we know nothing about", + __func__); +} + +const char *tc_filter_kind2str(uint32_t type) +{ + return lookup_msg(tc_filter_kinds, type, "Unrecognized TFILTER Type"); +} + +enum tc_qdisc_kind tc_filter_str2kind(const char *type) +{ + return lookup_key(tc_filter_kinds, type, TC_FILTER_UNSPEC); +} + +uint32_t zebra_tc_filter_hash_key(const void *arg) +{ + const struct zebra_tc_filter *filter; + uint32_t key; + + filter = arg; + + key = jhash_2words(filter->filter.ifindex, filter->filter.handle, 0); + + return key; +} + +bool zebra_tc_filter_hash_equal(const void *arg1, const void *arg2) +{ + const struct zebra_tc_filter *f1, *f2; + + f1 = (const struct zebra_tc_filter *)arg1; + f2 = (const struct zebra_tc_filter *)arg2; + + if (f1->filter.ifindex != f2->filter.ifindex) + return false; + + if (f1->filter.handle != f2->filter.handle) + return false; + + return true; +} + +static struct zebra_tc_filter *tc_filter_free(struct zebra_tc_filter *hash_data, + bool free_data) +{ + hash_release(zrouter.filter_hash, hash_data); + + if (free_data) { + XFREE(MTYPE_TC_FILTER, hash_data); + return NULL; + } + + return hash_data; +} + +static struct zebra_tc_filter *tc_filter_release(struct zebra_tc_filter *filter, + bool free_data) +{ + struct zebra_tc_filter *lookup; + + lookup = hash_lookup(zrouter.filter_hash, filter); + + if (!lookup) + return NULL; + + return tc_filter_free(lookup, free_data); +} + +static void *tc_filter_alloc_intern(void *arg) +{ + struct zebra_tc_filter *ztf; + struct zebra_tc_filter *new; + + ztf = (struct zebra_tc_filter *)arg; + + new = XCALLOC(MTYPE_TC_FILTER, sizeof(*new)); + + memcpy(new, ztf, sizeof(*ztf)); + + return new; +} + +void zebra_tc_filter_add(struct zebra_tc_filter *filter) +{ + if (IS_ZEBRA_DEBUG_TC) + zlog_debug( + "%s: add tc filter ifindex %d priority %u handle %08x kind %s", + __func__, filter->filter.ifindex, + filter->filter.priority, filter->filter.handle, + tc_filter_kind2str(filter->filter.kind)); + + struct zebra_tc_filter *found; + struct zebra_tc_filter *new; + + found = hash_lookup(zrouter.filter_hash, filter); + new = hash_get(zrouter.filter_hash, filter, tc_filter_alloc_intern); + + if (found) + (void)dplane_tc_filter_update(new); + else + (void)dplane_tc_filter_add(new); +} + +void zebra_tc_filter_delete(struct zebra_tc_filter *filter) +{ + if (IS_ZEBRA_DEBUG_PBR) + zlog_debug( + "%s: delete tc filter ifindex %d priority %u handle %08x kind %s", + __func__, filter->filter.ifindex, + filter->filter.priority, filter->filter.handle, + tc_filter_kind2str(filter->filter.kind)); + + (void)dplane_tc_filter_delete(filter); + + if (tc_filter_release(filter, true)) + zlog_debug("%s: tc filter being deleted we know nothing about", + __func__); +} diff --git a/zebra/zebra_tc.h b/zebra/zebra_tc.h new file mode 100644 index 000000000..832972b71 --- /dev/null +++ b/zebra/zebra_tc.h @@ -0,0 +1,79 @@ +/* + * Zebra Traffic Control (TC) Data structures and definitions + * These are public definitions referenced by multiple files. + * + * Copyright (C) 2022 Shichu Yang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _ZEBRA_TC_H +#define _ZEBRA_TC_H + +#include <zebra.h> +#include "rt.h" +#include "tc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct zebra_tc_qdisc { + int sock; + + struct tc_qdisc qdisc; +}; + +struct zebra_tc_class { + int sock; + + struct tc_class class; +}; + +struct zebra_tc_filter { + int sock; + + struct tc_filter filter; +}; + +const char *tc_qdisc_kind2str(uint32_t type); +enum tc_qdisc_kind tc_qdisc_str2kind(const char *type); + +uint32_t zebra_tc_qdisc_hash_key(const void *arg); +bool zebra_tc_qdisc_hash_equal(const void *arg1, const void *arg2); +void zebra_tc_qdisc_install(struct zebra_tc_qdisc *qdisc); +void zebra_tc_qdisc_uninstall(struct zebra_tc_qdisc *qdisc); + +uint32_t zebra_tc_class_hash_key(const void *arg); +bool zebra_tc_class_hash_equal(const void *arg1, const void *arg2); +void zebra_tc_class_add(struct zebra_tc_class *class); +void zebra_tc_class_delete(struct zebra_tc_class *class); + +const char *tc_filter_kind2str(uint32_t type); +enum tc_qdisc_kind tc_filter_str2kind(const char *type); +void zebra_tc_filter_add(struct zebra_tc_filter *filter); +void zebra_tc_filter_delete(struct zebra_tc_filter *filter); + +void zebra_tc_filters_free(void *arg); +uint32_t zebra_tc_filter_hash_key(const void *arg); +bool zebra_tc_filter_hash_equal(const void *arg1, const void *arg2); + +void kernel_read_tc_qdisc(struct zebra_ns *zns); + +#ifdef __cplusplus +} +#endif + +#endif /* _ZEBRA_TC_H */ |