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>
This commit is contained in:
Siger Yang 2022-09-06 15:10:11 +08:00
parent daa602b593
commit c317d3f246
No known key found for this signature in database
GPG Key ID: 25F5E75AF97D5067
24 changed files with 1949 additions and 245 deletions

View File

@ -123,6 +123,7 @@ lib_libfrr_la_SOURCES = \
lib/printf/glue.c \ lib/printf/glue.c \
lib/routing_nb.c \ lib/routing_nb.c \
lib/routing_nb_config.c \ lib/routing_nb_config.c \
lib/tc.c \
# end # end
nodist_lib_libfrr_la_SOURCES = \ nodist_lib_libfrr_la_SOURCES = \
@ -275,6 +276,7 @@ pkginclude_HEADERS += \
lib/zlog_live.h \ lib/zlog_live.h \
lib/zlog_targets.h \ lib/zlog_targets.h \
lib/pbr.h \ lib/pbr.h \
lib/tc.h \
lib/routing_nb.h \ lib/routing_nb.h \
\ \
lib/assert/assert.h \ lib/assert/assert.h \

88
lib/tc.c Normal file
View File

@ -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;
}

151
lib/tc.h Normal file
View File

@ -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 */

View File

@ -42,6 +42,7 @@ unsigned long zebra_debug_nexthop;
unsigned long zebra_debug_evpn_mh; unsigned long zebra_debug_evpn_mh;
unsigned long zebra_debug_pbr; unsigned long zebra_debug_pbr;
unsigned long zebra_debug_neigh; unsigned long zebra_debug_neigh;
unsigned long zebra_debug_tc;
DEFINE_HOOK(zebra_debug_show_debugging, (struct vty *vty), (vty)); DEFINE_HOOK(zebra_debug_show_debugging, (struct vty *vty), (vty));
@ -374,6 +375,17 @@ DEFPY (debug_zebra_neigh,
return CMD_SUCCESS; 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, DEFPY (debug_zebra_mlag,
debug_zebra_mlag_cmd, debug_zebra_mlag_cmd,
"[no$no] debug zebra mlag", "[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_nexthop_cmd);
install_element(ENABLE_NODE, &debug_zebra_pbr_cmd); install_element(ENABLE_NODE, &debug_zebra_pbr_cmd);
install_element(ENABLE_NODE, &debug_zebra_neigh_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, &debug_zebra_dplane_dpdk_cmd);
install_element(ENABLE_NODE, &no_debug_zebra_events_cmd); install_element(ENABLE_NODE, &no_debug_zebra_events_cmd);
install_element(ENABLE_NODE, &no_debug_zebra_nht_cmd); install_element(ENABLE_NODE, &no_debug_zebra_nht_cmd);

View File

@ -75,6 +75,8 @@ extern "C" {
#define ZEBRA_DEBUG_NEIGH 0x01 #define ZEBRA_DEBUG_NEIGH 0x01
#define ZEBRA_DEBUG_TC 0x01
/* Debug related macro. */ /* Debug related macro. */
#define IS_ZEBRA_DEBUG_EVENT (zebra_debug_event & ZEBRA_DEBUG_EVENT) #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_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_event;
extern unsigned long zebra_debug_packet; extern unsigned long zebra_debug_packet;
extern unsigned long zebra_debug_kernel; 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_evpn_mh;
extern unsigned long zebra_debug_pbr; extern unsigned long zebra_debug_pbr;
extern unsigned long zebra_debug_neigh; extern unsigned long zebra_debug_neigh;
extern unsigned long zebra_debug_tc;
extern void zebra_debug_init(void); extern void zebra_debug_init(void);

View File

@ -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_INSTALL:
case DPLANE_OP_INTF_UPDATE: case DPLANE_OP_INTF_UPDATE:
case DPLANE_OP_INTF_DELETE: case DPLANE_OP_INTF_DELETE:
case DPLANE_OP_TC_INSTALL: case DPLANE_OP_TC_QDISC_INSTALL:
case DPLANE_OP_TC_UPDATE: case DPLANE_OP_TC_QDISC_UNINSTALL:
case DPLANE_OP_TC_DELETE: 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: case DPLANE_OP_NONE:
break; break;

View File

@ -1573,9 +1573,14 @@ void zebra_if_dplane_result(struct zebra_dplane_ctx *ctx)
case DPLANE_OP_IPSET_ENTRY_DELETE: case DPLANE_OP_IPSET_ENTRY_DELETE:
case DPLANE_OP_NEIGH_TABLE_UPDATE: case DPLANE_OP_NEIGH_TABLE_UPDATE:
case DPLANE_OP_GRE_SET: case DPLANE_OP_GRE_SET:
case DPLANE_OP_TC_INSTALL: case DPLANE_OP_TC_QDISC_INSTALL:
case DPLANE_OP_TC_UPDATE: case DPLANE_OP_TC_QDISC_UNINSTALL:
case DPLANE_OP_TC_DELETE: 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 */ break; /* should never hit here */
} }
} }

View File

@ -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); return netlink_nexthop_change(h, ns_id, startup);
case RTM_DELNEXTHOP: case RTM_DELNEXTHOP:
return netlink_nexthop_change(h, ns_id, startup); 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 */ /* Messages handled in the dplane thread */
case RTM_NEWADDR: case RTM_NEWADDR:
@ -1640,10 +1649,17 @@ static enum netlink_msg_status nl_put_msg(struct nl_batch *bth,
case DPLANE_OP_INTF_DELETE: case DPLANE_OP_INTF_DELETE:
return netlink_put_intf_update_msg(bth, ctx); return netlink_put_intf_update_msg(bth, ctx);
case DPLANE_OP_TC_INSTALL: case DPLANE_OP_TC_QDISC_INSTALL:
case DPLANE_OP_TC_UPDATE: case DPLANE_OP_TC_QDISC_UNINSTALL:
case DPLANE_OP_TC_DELETE: return netlink_put_tc_qdisc_update_msg(bth, ctx);
return netlink_put_tc_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; return FRR_NETLINK_ERROR;
@ -1757,15 +1773,16 @@ void kernel_init(struct zebra_ns *zns)
* RTNLGRP_XXX to a bit position for ourself * RTNLGRP_XXX to a bit position for ourself
*/ */
groups = RTMGRP_LINK | groups = RTMGRP_LINK |
RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_ROUTE |
RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_IFADDR |
RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_ROUTE |
RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_IFADDR |
RTMGRP_IPV4_MROUTE | RTMGRP_IPV4_MROUTE |
RTMGRP_NEIGH | RTMGRP_NEIGH |
((uint32_t) 1 << (RTNLGRP_IPV4_RULE - 1)) | ((uint32_t) 1 << (RTNLGRP_IPV4_RULE - 1)) |
((uint32_t) 1 << (RTNLGRP_IPV6_RULE - 1)) | ((uint32_t) 1 << (RTNLGRP_IPV6_RULE - 1)) |
((uint32_t) 1 << (RTNLGRP_NEXTHOP - 1)); ((uint32_t) 1 << (RTNLGRP_NEXTHOP - 1)) |
((uint32_t) 1 << (RTNLGRP_TC - 1));
dplane_groups = (RTMGRP_LINK | dplane_groups = (RTMGRP_LINK |
RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_IFADDR |

View File

@ -1594,9 +1594,14 @@ void kernel_update_multi(struct dplane_ctx_q *ctx_list)
res = kernel_intf_update(ctx); res = kernel_intf_update(ctx);
break; break;
case DPLANE_OP_TC_INSTALL: case DPLANE_OP_TC_QDISC_INSTALL:
case DPLANE_OP_TC_UPDATE: case DPLANE_OP_TC_QDISC_UNINSTALL:
case DPLANE_OP_TC_DELETE: 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); res = kernel_tc_update(ctx);
break; break;

View File

@ -26,8 +26,10 @@
#include "vty.h" #include "vty.h"
#include "zebra/rt.h" #include "zebra/rt.h"
#include "zebra/zebra_pbr.h" #include "zebra/zebra_pbr.h"
#include "zebra/zebra_tc.h"
#include "zebra/rt_netlink.h" #include "zebra/rt_netlink.h"
#include "zebra/rule_netlink.h" #include "zebra/rule_netlink.h"
#include "zebra/tc_netlink.h"
void route_read(struct zebra_ns *zns) void route_read(struct zebra_ns *zns)
{ {
@ -71,4 +73,9 @@ void kernel_read_pbr_rules(struct zebra_ns *zns)
netlink_rules_read(zns); netlink_rules_read(zns);
} }
void kernel_read_tc_qdisc(struct zebra_ns *zns)
{
netlink_qdisc_read(zns);
}
#endif /* GNU_LINUX */ #endif /* GNU_LINUX */

View File

@ -30,6 +30,7 @@
#include "zebra/rt.h" #include "zebra/rt.h"
#include "zebra/kernel_socket.h" #include "zebra/kernel_socket.h"
#include "zebra/zebra_pbr.h" #include "zebra/zebra_pbr.h"
#include "zebra/zebra_tc.h"
#include "zebra/zebra_errors.h" #include "zebra/zebra_errors.h"
/* Kernel routing table read up by sysctl function. */ /* 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) */ #endif /* !defined(GNU_LINUX) */

View File

@ -100,6 +100,7 @@ zebra_zebra_SOURCES = \
zebra/zebra_routemap_nb_config.c \ zebra/zebra_routemap_nb_config.c \
zebra/zebra_script.c \ zebra/zebra_script.c \
zebra/zebra_srte.c \ zebra/zebra_srte.c \
zebra/zebra_tc.c \
zebra/zebra_trace.c \ zebra/zebra_trace.c \
zebra/zebra_vrf.c \ zebra/zebra_vrf.c \
zebra/zebra_vty.c \ zebra/zebra_vty.c \
@ -175,6 +176,7 @@ noinst_HEADERS += \
zebra/zebra_router.h \ zebra/zebra_router.h \
zebra/zebra_script.h \ zebra/zebra_script.h \
zebra/zebra_srte.h \ zebra/zebra_srte.h \
zebra/zebra_tc.h \
zebra/zebra_trace.h \ zebra/zebra_trace.h \
zebra/zebra_vrf.h \ zebra/zebra_vrf.h \
zebra/zebra_vxlan.h \ zebra/zebra_vxlan.h \

View File

@ -22,6 +22,8 @@
#ifdef HAVE_NETLINK #ifdef HAVE_NETLINK
#include <linux/pkt_cls.h>
#include <linux/pkt_sched.h>
#include <netinet/if_ether.h> #include <netinet/if_ether.h>
#include <sys/socket.h> #include <sys/socket.h>
@ -29,34 +31,24 @@
#include "prefix.h" #include "prefix.h"
#include "vrf.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/zserv.h"
#include "zebra/zebra_ns.h" #include "zebra/zebra_ns.h"
#include "zebra/zebra_vrf.h"
#include "zebra/rt.h" #include "zebra/rt.h"
#include "zebra/interface.h" #include "zebra/interface.h"
#include "zebra/debug.h" #include "zebra/debug.h"
#include "zebra/rtadv.h"
#include "zebra/kernel_netlink.h" #include "zebra/kernel_netlink.h"
#include "zebra/tc_netlink.h" #include "zebra/tc_netlink.h"
#include "zebra/zebra_errors.h" #include "zebra/zebra_errors.h"
#include "zebra/zebra_dplane.h" #include "zebra/zebra_dplane.h"
#include "zebra/zebra_tc.h"
#include "zebra/zebra_trace.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_FREQ_DEFAULT (100)
#define TC_MAJOR_BASE (0x1000u) /* some magic number */
#define TC_QDISC_MAJOR_ZEBRA (0xbeef0000u)
#define TC_MINOR_NOCLASS (0xffffu) #define TC_MINOR_NOCLASS (0xffffu)
#define TC_FILTER_MASK (0x8000u)
#define TIME_UNITS_PER_SEC (1000000) #define TIME_UNITS_PER_SEC (1000000)
#define xmittime(r, s) (TIME_UNITS_PER_SEC * ((double)(s) / (double)(r))) #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; 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, static void tc_calc_rate_table(struct tc_ratespec *ratespec, uint32_t *table,
uint32_t mtu) 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) void *data, size_t datalen)
{ {
struct nlsock *nl; struct nlsock *nl;
const char *kind_str = NULL;
const char *kind = "htb";
struct tc_htb_glob htb_glob = {
.rate2quantum = 10, .version = 3, .defcls = TC_MINOR_NOCLASS};
struct rtattr *nest; 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_family = AF_UNSPEC;
req->t.tcm_ifindex = dplane_ctx_get_ifindex(ctx); 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; 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);
nest = nl_attr_nest(&req->n, datalen, TCA_OPTIONS); kind_str = dplane_ctx_tc_qdisc_get_kind_str(ctx);
nl_attr_put(&req->n, datalen, TCA_HTB_INIT, &htb_glob, nl_attr_put(&req->n, datalen, TCA_KIND, kind_str,
sizeof(htb_glob)); strlen(kind_str) + 1);
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); 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, static ssize_t netlink_tclass_msg_encode(int cmd, struct zebra_dplane_ctx *ctx,
void *data, size_t datalen) void *data, size_t datalen)
{ {
enum dplane_op_e op = dplane_ctx_get_op(ctx);
struct nlsock *nl; struct nlsock *nl;
struct tc_htb_opt htb_opt = {}; const char *kind_str = NULL;
uint64_t rate, ceil;
uint64_t buffer, cbuffer;
/* TODO: fetch mtu from interface */
uint32_t mtu = 0;
uint32_t rtab[256];
uint32_t ctab[256];
struct rtattr *nest; struct rtattr *nest;
@ -268,167 +261,299 @@ 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_len = NLMSG_LENGTH(sizeof(struct tcmsg));
req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; 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_type = cmd;
req->n.nlmsg_pid = nl->snl.nl_pid; req->n.nlmsg_pid = nl->snl.nl_pid;
req->t.tcm_family = AF_UNSPEC; req->t.tcm_family = AF_UNSPEC;
req->t.tcm_ifindex = dplane_ctx_get_ifindex(ctx); 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); req->t.tcm_handle = TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA,
ceil = dplane_ctx_tc_get_ceil(ctx); dplane_ctx_tc_class_get_handle(ctx));
req->t.tcm_parent = TC_H_MAKE(TC_QDISC_MAJOR_ZEBRA, 0);
req->t.tcm_info = 0;
ceil = ceil < rate ? rate : ceil; kind_str = dplane_ctx_tc_class_get_kind_str(ctx);
htb_opt.rate.rate = (rate >> 32 != 0) ? ~0U : rate; if (op == DPLANE_OP_TC_CLASS_ADD || op == DPLANE_OP_TC_CLASS_UPDATE) {
htb_opt.ceil.rate = (ceil >> 32 != 0) ? ~0U : ceil; 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));
buffer = rate / tc_get_freq(), cbuffer = ceil / tc_get_freq(); nl_attr_put(&req->n, datalen, TCA_KIND, kind_str,
strlen(kind_str) + 1);
htb_opt.buffer = buffer; nest = nl_attr_nest(&req->n, datalen, TCA_OPTIONS);
htb_opt.cbuffer = cbuffer;
tc_calc_rate_table(&htb_opt.rate, rtab, mtu); switch (dplane_ctx_tc_class_get_kind(ctx)) {
tc_calc_rate_table(&htb_opt.ceil, ctab, mtu); case TC_QDISC_HTB: {
struct tc_htb_opt htb_opt = {};
htb_opt.ceil.mpu = htb_opt.rate.mpu = 0; uint64_t rate = dplane_ctx_tc_class_get_rate(ctx),
htb_opt.ceil.overhead = htb_opt.rate.overhead = 0; ceil = dplane_ctx_tc_class_get_ceil(ctx);
nest = nl_attr_nest(&req->n, datalen, TCA_OPTIONS); uint64_t buffer, cbuffer;
if (rate >> 32 != 0) { /* TODO: fetch mtu from interface */
nl_attr_put(&req->n, datalen, TCA_HTB_CEIL64, &rate, uint32_t mtu = 1500;
sizeof(rate));
uint32_t rtab[256];
uint32_t ctab[256];
ceil = MAX(rate, ceil);
htb_opt.rate.rate = (rate >> 32 != 0) ? ~0U : rate;
htb_opt.ceil.rate = (ceil >> 32 != 0) ? ~0U : ceil;
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));
}
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));
nl_attr_nest_end(&req->n, nest);
return NLMSG_ALIGN(req->n.nlmsg_len); return NLMSG_ALIGN(req->n.nlmsg_len);
} }
/* static int netlink_tfilter_flower_port_type(uint8_t ip_proto, bool src)
* Traffic control filter encoding (only "flower" supported)
*/
static ssize_t netlink_tfilter_msg_encode(int cmd, struct zebra_dplane_ctx *ctx,
void *data, size_t datalen)
{ {
struct nlsock *nl; if (ip_proto == IPPROTO_TCP)
struct rtattr *nest; return src ? TCA_FLOWER_KEY_TCP_SRC : TCA_FLOWER_KEY_TCP_DST;
else if (ip_proto == IPPROTO_UDP)
const char *kind = "flower"; return src ? TCA_FLOWER_KEY_UDP_SRC : TCA_FLOWER_KEY_UDP_DST;
else if (ip_proto == IPPROTO_SCTP)
uint16_t priority; return src ? TCA_FLOWER_KEY_SCTP_SRC : TCA_FLOWER_KEY_SCTP_DST;
uint16_t protocol; else
uint32_t classid; return -1;
uint32_t filter_bm; }
uint32_t flags = 0;
static void netlink_tfilter_flower_put_options(struct nlmsghdr *n,
size_t datalen,
struct zebra_dplane_ctx *ctx)
{
struct inet_prefix addr; 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);
struct { if (filter_bm & TC_FLOWER_SRC_IP) {
struct nlmsghdr n; const struct prefix *src_p =
struct tcmsg t; dplane_ctx_tc_filter_get_src_ip(ctx);
char buf[0];
} *req = (void *)data;
if (datalen < sizeof(*req))
return 0;
nl = kernel_netlink_nlsock_lookup(dplane_ctx_get_ns_sock(ctx));
memset(req, 0, sizeof(*req));
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;
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);
/* 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);
req->t.tcm_info = tc_make_handle(priority, protocol);
req->t.tcm_handle = 1;
req->t.tcm_parent = tc_get_handle(ctx, 0);
nl_attr_put(&req->n, datalen, TCA_KIND, kind, strlen(kind) + 1);
nest = nl_attr_nest(&req->n, datalen, TCA_OPTIONS);
nl_attr_put(&req->n, datalen, TCA_FLOWER_CLASSID, &classid,
sizeof(classid));
if (filter_bm & TC_FILTER_SRC_IP) {
const struct prefix *src_p = dplane_ctx_tc_get_src_ip(ctx);
if (tc_flower_get_inet_prefix(src_p, &addr) != 0) if (tc_flower_get_inet_prefix(src_p, &addr) != 0)
return 0; return;
nl_attr_put(&req->n, datalen, nl_attr_put(n, datalen,
(addr.family == AF_INET) ? TCA_FLOWER_KEY_IPV4_SRC (addr.family == AF_INET) ? TCA_FLOWER_KEY_IPV4_SRC
: TCA_FLOWER_KEY_IPV6_SRC, : TCA_FLOWER_KEY_IPV6_SRC,
addr.data, addr.bytelen); addr.data, addr.bytelen);
if (tc_flower_get_inet_mask(src_p, &addr) != 0) if (tc_flower_get_inet_mask(src_p, &addr) != 0)
return 0; return;
nl_attr_put(&req->n, datalen, nl_attr_put(n, datalen,
(addr.family == AF_INET) (addr.family == AF_INET)
? TCA_FLOWER_KEY_IPV4_SRC_MASK ? TCA_FLOWER_KEY_IPV4_SRC_MASK
: TCA_FLOWER_KEY_IPV6_SRC_MASK, : TCA_FLOWER_KEY_IPV6_SRC_MASK,
addr.data, addr.bytelen); addr.data, addr.bytelen);
} }
if (filter_bm & TC_FILTER_DST_IP) { if (filter_bm & TC_FLOWER_DST_IP) {
const struct prefix *dst_p = dplane_ctx_tc_get_dst_ip(ctx); const struct prefix *dst_p =
dplane_ctx_tc_filter_get_dst_ip(ctx);
if (tc_flower_get_inet_prefix(dst_p, &addr) != 0) if (tc_flower_get_inet_prefix(dst_p, &addr) != 0)
return 0; return;
nl_attr_put(&req->n, datalen, nl_attr_put(n, datalen,
(addr.family == AF_INET) ? TCA_FLOWER_KEY_IPV4_DST (addr.family == AF_INET) ? TCA_FLOWER_KEY_IPV4_DST
: TCA_FLOWER_KEY_IPV6_DST, : TCA_FLOWER_KEY_IPV6_DST,
addr.data, addr.bytelen); addr.data, addr.bytelen);
if (tc_flower_get_inet_mask(dst_p, &addr) != 0) if (tc_flower_get_inet_mask(dst_p, &addr) != 0)
return 0; return;
nl_attr_put(&req->n, datalen, nl_attr_put(n, datalen,
(addr.family == AF_INET) (addr.family == AF_INET)
? TCA_FLOWER_KEY_IPV4_DST_MASK ? TCA_FLOWER_KEY_IPV4_DST_MASK
: TCA_FLOWER_KEY_IPV6_DST_MASK, : TCA_FLOWER_KEY_IPV6_DST_MASK,
addr.data, addr.bytelen); addr.data, addr.bytelen);
} }
if (filter_bm & TC_FILTER_IP_PROTOCOL) { if (filter_bm & TC_FLOWER_IP_PROTOCOL) {
nl_attr_put8(&req->n, datalen, TCA_FLOWER_KEY_IP_PROTO, nl_attr_put8(n, datalen, TCA_FLOWER_KEY_IP_PROTO,
dplane_ctx_tc_get_ip_proto(ctx)); dplane_ctx_tc_filter_get_ip_proto(ctx));
} }
nl_attr_put32(&req->n, datalen, TCA_FLOWER_FLAGS, flags); if (filter_bm & TC_FLOWER_SRC_PORT) {
uint16_t min, max;
nl_attr_put16(&req->n, datalen, TCA_FLOWER_KEY_ETH_TYPE, protocol); min = dplane_ctx_tc_filter_get_src_port_min(ctx);
nl_attr_nest_end(&req->n, nest); 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
*/
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;
const char *kind_str = NULL;
struct rtattr *nest;
uint16_t priority;
uint16_t protocol;
struct {
struct nlmsghdr n;
struct tcmsg t;
char buf[0];
} *req = (void *)data;
if (datalen < sizeof(*req))
return 0;
nl = kernel_netlink_nlsock_lookup(dplane_ctx_get_ns_sock(ctx));
memset(req, 0, sizeof(*req));
req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST;
if (op == DPLANE_OP_TC_FILTER_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);
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); return NLMSG_ALIGN(req->n.nlmsg_len);
} }
@ -439,27 +564,318 @@ static ssize_t netlink_newqdisc_msg_encoder(struct zebra_dplane_ctx *ctx,
return netlink_qdisc_msg_encode(RTM_NEWQDISC, ctx, buf, 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, static ssize_t netlink_newtclass_msg_encoder(struct zebra_dplane_ctx *ctx,
void *buf, size_t buflen) void *buf, size_t buflen)
{ {
return netlink_tclass_msg_encode(RTM_NEWTCLASS, ctx, buf, buflen); return netlink_tclass_msg_encode(RTM_NEWTCLASS, ctx, buf, buflen);
} }
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);
}
static ssize_t netlink_newtfilter_msg_encoder(struct zebra_dplane_ctx *ctx, static ssize_t netlink_newtfilter_msg_encoder(struct zebra_dplane_ctx *ctx,
void *buf, size_t buflen) void *buf, size_t buflen)
{ {
return netlink_tfilter_msg_encode(RTM_NEWTFILTER, ctx, buf, buflen); return netlink_tfilter_msg_encode(RTM_NEWTFILTER, ctx, buf, buflen);
} }
enum netlink_msg_status netlink_put_tc_update_msg(struct nl_batch *bth, static ssize_t netlink_deltfilter_msg_encoder(struct zebra_dplane_ctx *ctx,
struct zebra_dplane_ctx *ctx) void *buf, size_t buflen)
{ {
/* TODO: error handling and other actions (delete, replace, ...) */ return netlink_tfilter_msg_encode(RTM_DELTFILTER, ctx, buf, buflen);
}
netlink_batch_add_msg(bth, ctx, netlink_newqdisc_msg_encoder, false); enum netlink_msg_status
netlink_batch_add_msg(bth, ctx, netlink_newtclass_msg_encoder, false); netlink_put_tc_qdisc_update_msg(struct nl_batch *bth,
return netlink_batch_add_msg(bth, ctx, netlink_newtfilter_msg_encoder, struct zebra_dplane_ctx *ctx)
false); {
enum dplane_op_e op;
enum netlink_msg_status ret;
op = dplane_ctx_get_op(ctx);
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;
}
return ret;
}
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;
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;
}
return ret;
}
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;
}
return ret;
}
/*
* 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;
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 (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]);
*/
}
if (h->nlmsg_type == RTM_NEWQDISC) {
if (startup &&
TC_H_MAJ(tcm->tcm_handle) == TC_QDISC_MAJOR_ZEBRA) {
enum zebra_dplane_result ret;
ret = dplane_tc_qdisc_uninstall(&qdisc);
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;
}
int netlink_tclass_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
{
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;
}
int netlink_tfilter_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
{
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;
}
int netlink_qdisc_read(struct zebra_ns *zns)
{
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;
}
int netlink_tfilter_read_for_interface(struct zebra_ns *zns, ifindex_t ifindex)
{
int ret;
struct zebra_dplane_info dp_info;
zebra_dplane_info_from_zns(&dp_info, zns, true);
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 */ #endif /* HAVE_NETLINK */

View File

@ -48,7 +48,30 @@ enum {
}; };
extern enum netlink_msg_status 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 #ifdef __cplusplus
} }

View File

@ -27,6 +27,7 @@
#include "zebra/rt.h" #include "zebra/rt.h"
#include "zebra/zebra_dplane.h" #include "zebra/zebra_dplane.h"
#include "zebra/zebra_errors.h" #include "zebra/zebra_errors.h"
#include "zebra/zebra_tc.h"
enum zebra_dplane_result kernel_tc_update(struct zebra_dplane_ctx *ctx) enum zebra_dplane_result kernel_tc_update(struct zebra_dplane_ctx *ctx)
{ {

View File

@ -37,6 +37,7 @@
#include "zebra/debug.h" #include "zebra/debug.h"
#include "zebra/zebra_pbr.h" #include "zebra/zebra_pbr.h"
#include "zebra/zebra_neigh.h" #include "zebra/zebra_neigh.h"
#include "zebra/zebra_tc.h"
#include "printfrr.h" #include "printfrr.h"
/* Memory types */ /* Memory types */
@ -313,23 +314,36 @@ struct dplane_netconf_info {
enum dplane_netconf_status_e linkdown_val; enum dplane_netconf_status_e linkdown_val;
}; };
/* struct dplane_tc_qdisc_info {
* Traffic control contexts for the dplane enum tc_qdisc_kind kind;
*/ const char *kind_str;
struct dplane_tc_info { };
/* Rate spec (unit: Bytes/s) */
struct dplane_tc_class_info {
uint32_t handle;
enum tc_qdisc_kind kind;
const char *kind_str;
uint64_t rate; uint64_t rate;
uint64_t ceil; uint64_t ceil;
};
/* TODO: custom burst */ struct dplane_tc_filter_info {
uint32_t handle;
/* Filter components for "tfilter" */ uint16_t priority;
enum tc_filter_kind kind;
const char *kind_str;
uint32_t filter_bm; uint32_t filter_bm;
uint16_t eth_proto;
uint8_t ip_proto;
struct prefix src_ip; struct prefix src_ip;
struct prefix dst_ip; struct prefix dst_ip;
uint8_t ip_proto; uint16_t src_port_min;
uint16_t src_port_max;
/* TODO: more filter components */ 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_mac_info macinfo;
struct dplane_neigh_info neigh; struct dplane_neigh_info neigh;
struct dplane_rule_info rule; 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_iptable iptable;
struct zebra_pbr_ipset ipset; struct zebra_pbr_ipset ipset;
struct { 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_INSTALL:
case DPLANE_OP_INTF_UPDATE: case DPLANE_OP_INTF_UPDATE:
case DPLANE_OP_INTF_DELETE: case DPLANE_OP_INTF_DELETE:
case DPLANE_OP_TC_INSTALL: case DPLANE_OP_TC_QDISC_INSTALL:
case DPLANE_OP_TC_UPDATE: case DPLANE_OP_TC_QDISC_UNINSTALL:
case DPLANE_OP_TC_DELETE: 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; break;
case DPLANE_OP_IPSET_ENTRY_ADD: case DPLANE_OP_IPSET_ENTRY_ADD:
@ -1127,14 +1148,29 @@ const char *dplane_op2str(enum dplane_op_e op)
ret = "INTF_DELETE"; ret = "INTF_DELETE";
break; break;
case DPLANE_OP_TC_INSTALL: case DPLANE_OP_TC_QDISC_INSTALL:
ret = "TC_INSTALL"; ret = "TC_QDISC_INSTALL";
break; break;
case DPLANE_OP_TC_UPDATE: case DPLANE_OP_TC_QDISC_UNINSTALL:
ret = "TC_UPDATE"; ret = "TC_QDISC_UNINSTALL";
break; break;
case DPLANE_OP_TC_DELETE: case DPLANE_OP_TC_CLASS_ADD:
ret = "TC_DELETE"; 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_FILTER_DELETE:
ret = "TC_FILTER_DELETE";
break;
case DPLANE_OP_TC_FILTER_UPDATE:
ret = "TC__FILTER_UPDATE";
break; 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; 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); DPLANE_CTX_VALID(ctx);
return ctx->u.tc.rate; return ctx->u.tc_qdisc.kind;
} }
uint64_t dplane_ctx_tc_get_ceil(const struct zebra_dplane_ctx *ctx) const char *dplane_ctx_tc_qdisc_get_kind_str(const struct zebra_dplane_ctx *ctx)
{ {
DPLANE_CTX_VALID(ctx); DPLANE_CTX_VALID(ctx);
return ctx->u.tc.ceil; return ctx->u.tc_qdisc.kind_str;
} }
uint32_t dplane_ctx_tc_get_filter_bm(const struct zebra_dplane_ctx *ctx) uint32_t dplane_ctx_tc_class_get_handle(const struct zebra_dplane_ctx *ctx)
{ {
DPLANE_CTX_VALID(ctx); DPLANE_CTX_VALID(ctx);
return ctx->u.tc.filter_bm; 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_filter.handle;
}
uint16_t dplane_ctx_tc_filter_get_eth_proto(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
return ctx->u.tc_filter.eth_proto;
}
uint32_t dplane_ctx_tc_filter_get_filter_bm(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
return ctx->u.tc_filter.filter_bm;
} }
const struct prefix * 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); DPLANE_CTX_VALID(ctx);
return &(ctx->u.tc.src_ip); 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_filter.src_port_max;
} }
const struct prefix * 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); DPLANE_CTX_VALID(ctx);
return &(ctx->u.tc.dst_ip); return &ctx->u.tc_filter.dst_ip;
} }
uint8_t dplane_ctx_tc_get_ip_proto(const struct zebra_dplane_ctx *ctx) uint16_t
dplane_ctx_tc_filter_get_dst_port_min(const struct zebra_dplane_ctx *ctx)
{ {
DPLANE_CTX_VALID(ctx); DPLANE_CTX_VALID(ctx);
return ctx->u.tc.ip_proto; return ctx->u.tc_filter.dst_port_min;
}
uint16_t
dplane_ctx_tc_filter_get_dst_port_max(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
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; 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; 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_op = op;
ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; 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 */ /* TODO: init traffic control qdisc */
zns = zebra_ns_lookup(NS_DEFAULT); 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; 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 * 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; 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; enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
int ret; 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 */ /* 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) if (ret == AOK)
ret = dplane_update_enqueue(ctx); ret = dplane_update_enqueue(ctx);
@ -3545,9 +3783,118 @@ done:
return result; 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; break;
/* TODO: more detailed log */ /* TODO: more detailed log */
case DPLANE_OP_TC_INSTALL: case DPLANE_OP_TC_QDISC_INSTALL:
case DPLANE_OP_TC_UPDATE: case DPLANE_OP_TC_QDISC_UNINSTALL:
case DPLANE_OP_TC_DELETE: zlog_debug("Dplane tc qdisc ifidx %u",
zlog_debug("Dplane tc ifidx %u", dplane_ctx_get_ifindex(ctx)); 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; break;
} }
} }
@ -5881,9 +6236,14 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx)
1, memory_order_relaxed); 1, memory_order_relaxed);
break; break;
case DPLANE_OP_TC_INSTALL: case DPLANE_OP_TC_QDISC_INSTALL:
case DPLANE_OP_TC_UPDATE: case DPLANE_OP_TC_QDISC_UNINSTALL:
case DPLANE_OP_TC_DELETE: 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) if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
atomic_fetch_add_explicit(&zdplane_info.dg_tcs_errors, atomic_fetch_add_explicit(&zdplane_info.dg_tcs_errors,
1, memory_order_relaxed); 1, memory_order_relaxed);

View File

@ -195,9 +195,14 @@ enum dplane_op_e {
DPLANE_OP_INTF_DELETE, DPLANE_OP_INTF_DELETE,
/* Traffic control */ /* Traffic control */
DPLANE_OP_TC_INSTALL, DPLANE_OP_TC_QDISC_INSTALL,
DPLANE_OP_TC_UPDATE, DPLANE_OP_TC_QDISC_UNINSTALL,
DPLANE_OP_TC_DELETE, 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); uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx *ctx);
/* Accessors for traffic control context */ /* Accessors for traffic control context */
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);
uint64_t dplane_ctx_tc_get_ceil(const struct zebra_dplane_ctx *ctx); const char *
uint32_t dplane_ctx_tc_get_filter_bm(const struct zebra_dplane_ctx *ctx); 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 * 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 * 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);
uint8_t dplane_ctx_tc_get_ip_proto(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_nexthops(struct zebra_dplane_ctx *ctx, struct nexthop *nh);
void dplane_ctx_set_backup_nhg(struct zebra_dplane_ctx *ctx, 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); 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); struct zebra_tc_qdisc;
enum zebra_dplane_result dplane_tc_delete(void); 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. * Link layer operations for the dataplane.

View File

@ -3168,9 +3168,14 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx)
case DPLANE_OP_INTF_INSTALL: case DPLANE_OP_INTF_INSTALL:
case DPLANE_OP_INTF_UPDATE: case DPLANE_OP_INTF_UPDATE:
case DPLANE_OP_INTF_DELETE: case DPLANE_OP_INTF_DELETE:
case DPLANE_OP_TC_INSTALL: case DPLANE_OP_TC_QDISC_INSTALL:
case DPLANE_OP_TC_UPDATE: case DPLANE_OP_TC_QDISC_UNINSTALL:
case DPLANE_OP_TC_DELETE: 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; break;
} }
} }

View File

@ -34,6 +34,7 @@
#include "zebra_netns_notify.h" #include "zebra_netns_notify.h"
#include "zebra_netns_id.h" #include "zebra_netns_id.h"
#include "zebra_pbr.h" #include "zebra_pbr.h"
#include "zebra_tc.h"
#include "rib.h" #include "rib.h"
#include "table_manager.h" #include "table_manager.h"
#include "zebra_errors.h" #include "zebra_errors.h"
@ -127,6 +128,7 @@ int zebra_ns_enable(ns_id_t ns_id, void **info)
interface_list(zns); interface_list(zns);
route_read(zns); route_read(zns);
kernel_read_pbr_rules(zns); kernel_read_pbr_rules(zns);
kernel_read_tc_qdisc(zns);
return 0; return 0;
} }

View File

@ -4710,9 +4710,14 @@ static void rib_process_dplane_results(struct thread *thread)
zebra_if_dplane_result(ctx); zebra_if_dplane_result(ctx);
break; break;
case DPLANE_OP_TC_INSTALL: case DPLANE_OP_TC_QDISC_INSTALL:
case DPLANE_OP_TC_UPDATE: case DPLANE_OP_TC_QDISC_UNINSTALL:
case DPLANE_OP_TC_DELETE: 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; break;
/* Some op codes not handled here */ /* Some op codes not handled here */

View File

@ -30,6 +30,7 @@
#include "zebra_mlag.h" #include "zebra_mlag.h"
#include "zebra_nhg.h" #include "zebra_nhg.h"
#include "zebra_neigh.h" #include "zebra_neigh.h"
#include "zebra/zebra_tc.h"
#include "debug.h" #include "debug.h"
#include "zebra_script.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, hash_create_size(8, zebra_nhg_id_key, zebra_nhg_hash_id_equal,
"Zebra Router Nexthop Groups ID index"); "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.asic_offloaded = asic_offload;
zrouter.notify_on_ack = notify_on_ack; zrouter.notify_on_ack = notify_on_ack;

View File

@ -172,6 +172,10 @@ struct zebra_router {
struct hash *iptable_hash; struct hash *iptable_hash;
struct hash *qdisc_hash;
struct hash *class_hash;
struct hash *filter_hash;
/* A sequence number used for tracking routes */ /* A sequence number used for tracking routes */
_Atomic uint32_t sequence_num; _Atomic uint32_t sequence_num;

444
zebra/zebra_tc.c Normal file
View File

@ -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__);
}

79
zebra/zebra_tc.h Normal file
View File

@ -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 */