summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/subdir.am2
-rw-r--r--lib/tc.c88
-rw-r--r--lib/tc.h151
-rw-r--r--zebra/debug.c13
-rw-r--r--zebra/debug.h5
-rw-r--r--zebra/dplane_fpm_nl.c11
-rw-r--r--zebra/interface.c11
-rw-r--r--zebra/kernel_netlink.c43
-rw-r--r--zebra/kernel_socket.c11
-rw-r--r--zebra/rtread_netlink.c7
-rw-r--r--zebra/rtread_sysctl.c5
-rw-r--r--zebra/subdir.am2
-rw-r--r--zebra/tc_netlink.c710
-rw-r--r--zebra/tc_netlink.h25
-rw-r--r--zebra/tc_socket.c1
-rw-r--r--zebra/zebra_dplane.c450
-rw-r--r--zebra/zebra_dplane.h71
-rw-r--r--zebra/zebra_nhg.c11
-rw-r--r--zebra/zebra_ns.c2
-rw-r--r--zebra/zebra_rib.c11
-rw-r--r--zebra/zebra_router.c15
-rw-r--r--zebra/zebra_router.h4
-rw-r--r--zebra/zebra_tc.c444
-rw-r--r--zebra/zebra_tc.h79
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 */