From 7cdb1a8445ecff8ad0f2eb532df5eb2112d921e0 Mon Sep 17 00:00:00 2001 From: Mark Stapp Date: Wed, 23 May 2018 12:20:43 -0400 Subject: [PATCH 01/22] zebra: start dataplane layer work Reduce or eliminate use of global zebra_ns structs in a couple of netlink/kernel code paths, so that those paths can potentially be made asynch eventually. Slide netlink_talk_info into place to remove dependency on core zebra structs; add accessors for dplane context block Start init of route context from zebra core re and rn structs; start queueing and event handling for incoming route updates. Expose netlink apis that don't rely on zebra core structs; add parallel route-update code path using the dplane ctx; simplest possible event loop to process queued route' updates. Signed-off-by: Mark Stapp --- zebra/kernel_netlink.c | 43 ++- zebra/kernel_netlink.h | 9 +- zebra/rib.h | 3 + zebra/rt.h | 6 + zebra/rt_netlink.c | 387 +++++++++++++++++++++- zebra/rt_socket.c | 5 + zebra/zapi_msg.c | 14 + zebra/zapi_msg.h | 2 + zebra/zebra_dplane.c | 736 ++++++++++++++++++++++++++++++++++++++++- zebra/zebra_dplane.h | 178 ++++++++-- zebra/zebra_memory.h | 1 + zebra/zebra_ns.h | 35 ++ zebra/zebra_rib.c | 73 +++- zebra/zebra_vrf.h | 2 +- 14 files changed, 1454 insertions(+), 40 deletions(-) diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index e6610f21be..cdfbdd2303 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -136,6 +136,7 @@ extern uint32_t nl_rcvbufsize; extern struct zebra_privs_t zserv_privs; + int netlink_talk_filter(struct nlmsghdr *h, ns_id_t ns_id, int startup) { /* @@ -678,7 +679,8 @@ static void netlink_parse_extended_ack(struct nlmsghdr *h) * the filter. */ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int), - struct nlsock *nl, struct zebra_dplane_info *zns, + const struct nlsock *nl, + const struct zebra_dplane_info *zns, int count, int startup) { int status; @@ -919,28 +921,27 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int), } /* - * netlink_talk + * netlink_talk_info * * sendmsg() to netlink socket then recvmsg(). * Calls netlink_parse_info to parse returned data * * filter -> The filter to read final results from kernel * nlmsghdr -> The data to send to the kernel - * nl -> The netlink socket information - * zns -> The zebra namespace information + * zns_info -> The netlink socket information * startup -> Are we reading in under startup conditions * This is passed through eventually to filter. */ -int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), - struct nlmsghdr *n, struct nlsock *nl, struct zebra_ns *zns, - int startup) +int netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), + struct nlmsghdr *n, + const struct zebra_dplane_info *dp_info, int startup) { int status = 0; struct sockaddr_nl snl; struct iovec iov; struct msghdr msg; int save_errno = 0; - struct zebra_dplane_info dp_info; + const struct nlsock *nl; memset(&snl, 0, sizeof snl); memset(&iov, 0, sizeof iov); @@ -955,7 +956,8 @@ int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), snl.nl_family = AF_NETLINK; - n->nlmsg_seq = ++nl->seq; + nl = &(dp_info->nls); + n->nlmsg_seq = nl->seq; n->nlmsg_pid = nl->snl.nl_pid; if (IS_ZEBRA_DEBUG_KERNEL) @@ -987,8 +989,29 @@ int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), * Get reply from netlink socket. * The reply should either be an acknowlegement or an error. */ + return netlink_parse_info(filter, nl, dp_info, 0, startup); +} + +/* + * Synchronous version of netlink_talk_info. Converts args to suit the + * common version, which is suitable for both sync and async use. + * + */ +int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), + struct nlmsghdr *n, struct nlsock *nl, struct zebra_ns *zns, + int startup) +{ + struct zebra_dplane_info dp_info; + + /* Increment sequence number before capturing snapshot of ns socket + * info. + */ + nl->seq++; + + /* Capture info in intermediate info struct */ zebra_dplane_info_from_zns(&dp_info, zns, (nl == &(zns->netlink_cmd))); - return netlink_parse_info(filter, nl, &dp_info, 0, startup); + + return (netlink_talk_info(filter, n, &dp_info, startup)); } /* Issue request message to kernel via netlink socket. GET messages diff --git a/zebra/kernel_netlink.h b/zebra/kernel_netlink.h index d78958d72e..f3de011b66 100644 --- a/zebra/kernel_netlink.h +++ b/zebra/kernel_netlink.h @@ -52,12 +52,19 @@ extern bool netlink_read; extern void netlink_read_init(const char *fname); #endif /* HANDLE_NETLINK_FUZZING */ extern int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int), - struct nlsock *nl, struct zebra_dplane_info *zns, + const struct nlsock *nl, + const struct zebra_dplane_info *zns, int count, int startup); extern int netlink_talk_filter(struct nlmsghdr *h, ns_id_t ns, int startup); extern int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), struct nlmsghdr *n, struct nlsock *nl, struct zebra_ns *zns, int startup); +/* Version with 'info' struct only */ +int netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), + struct nlmsghdr *n, + const struct zebra_dplane_info *dp_info, + int startup); + extern int netlink_request(struct nlsock *nl, struct nlmsghdr *n); #endif /* HAVE_NETLINK */ diff --git a/zebra/rib.h b/zebra/rib.h index f3aead32d8..97eae79f03 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -91,6 +91,9 @@ struct route_entry { /* Nexthop information. */ uint8_t nexthop_num; uint8_t nexthop_active_num; + + /* Sequence value incremented for each dataplane operation */ + uint32_t dplane_sequence; }; /* meta-queue structure: diff --git a/zebra/rt.h b/zebra/rt.h index dbea298584..a4db8968a5 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -58,6 +58,12 @@ extern void kernel_route_rib_pass_fail(struct route_node *rn, struct route_entry *re, enum zebra_dplane_status res); +/* + * Update or delete a prefix from the kernel, + * using info from a dataplane context. + */ +extern enum zebra_dplane_result kernel_route_update(dplane_ctx_h ctx); + extern int kernel_address_add_ipv4(struct interface *, struct connected *); extern int kernel_address_delete_ipv4(struct interface *, struct connected *); extern int kernel_address_add_ipv6(struct interface *, struct connected *); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 795ee2703a..bbaf89a7c0 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1651,7 +1651,7 @@ static int netlink_route_multipath(int cmd, const struct prefix *p, addattr_l(&req.n, sizeof req, RTA_PREFSRC, &src.ipv6, bytelen); } - } else { + } else { /* Multipath case */ char buf[NL_PKT_BUF_SIZE]; struct rtattr *rta = (void *)buf; struct rtnexthop *rtnh; @@ -1768,6 +1768,346 @@ skip: 0); } +/* + * Routing table change via netlink interface, using a dataplane context object + */ +static int netlink_route_multipath_ctx(int cmd, dplane_ctx_h ctx) +{ + int bytelen; + struct sockaddr_nl snl; + struct nexthop *nexthop = NULL; + unsigned int nexthop_num; + int family; + const char *routedesc; + int setsrc = 0; + union g_addr src; + const struct prefix *p, *src_p; + uint32_t table_id; + + struct { + struct nlmsghdr n; + struct rtmsg r; + char buf[NL_PKT_BUF_SIZE]; + } req; + + p = dplane_ctx_get_dest(ctx); + src_p = dplane_ctx_get_src(ctx); + + family = PREFIX_FAMILY(p); + + memset(&req, 0, sizeof req - NL_PKT_BUF_SIZE); + + bytelen = (family == AF_INET ? 4 : 16); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; + + if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_UPDATE) { + if ((p->family == AF_INET) || v6_rr_semantics) + req.n.nlmsg_flags |= NLM_F_REPLACE; + } + + req.n.nlmsg_type = cmd; + + req.n.nlmsg_pid = dplane_ctx_get_ns(ctx)->nls.snl.nl_pid; + + req.r.rtm_family = family; + req.r.rtm_dst_len = p->prefixlen; + req.r.rtm_src_len = src_p ? src_p->prefixlen : 0; + req.r.rtm_scope = RT_SCOPE_UNIVERSE; + + if (cmd == RTM_DELROUTE) { + req.r.rtm_protocol = zebra2proto(dplane_ctx_get_old_type(ctx)); + } else { + req.r.rtm_protocol = zebra2proto(dplane_ctx_get_type(ctx)); + } + + /* + * blackhole routes are not RTN_UNICAST, they are + * RTN_ BLACKHOLE|UNREACHABLE|PROHIBIT + * so setting this value as a RTN_UNICAST would + * cause the route lookup of just the prefix + * to fail. So no need to specify this for + * the RTM_DELROUTE case + */ + if (cmd != RTM_DELROUTE) + req.r.rtm_type = RTN_UNICAST; + + addattr_l(&req.n, sizeof req, RTA_DST, &p->u.prefix, bytelen); + if (src_p) + addattr_l(&req.n, sizeof req, RTA_SRC, &src_p->u.prefix, + bytelen); + + /* Metric. */ + /* Hardcode the metric for all routes coming from zebra. Metric isn't + * used + * either by the kernel or by zebra. Its purely for calculating best + * path(s) + * by the routing protocol and for communicating with protocol peers. + */ + addattr32(&req.n, sizeof req, RTA_PRIORITY, NL_DEFAULT_ROUTE_METRIC); + +#if defined(SUPPORT_REALMS) + { + route_tag_t tag; + + if (cmd == RTM_DELROUTE) { + tag = dplane_ctx_get_old_tag(ctx); + } else { + tag = dplane_ctx_get_tag(ctx); + } + + if (tag > 0 && tag <= 255) + addattr32(&req.n, sizeof req, RTA_FLOW, tag); + } +#endif + /* Table corresponding to this route. */ + table_id = dplane_ctx_get_table(ctx); + if (table_id < 256) + req.r.rtm_table = table_id; + else { + req.r.rtm_table = RT_TABLE_UNSPEC; + addattr32(&req.n, sizeof req, RTA_TABLE, table_id); + } + + _netlink_route_debug(cmd, p, family, dplane_ctx_get_vrf(ctx), table_id); + + /* + * If we are not updating the route and we have received + * a route delete, then all we need to fill in is the + * prefix information to tell the kernel to schwack + * it. + */ + if (cmd == RTM_DELROUTE) + goto skip; + + if (dplane_ctx_get_mtu(ctx) || dplane_ctx_get_nh_mtu(ctx)) { + char buf[NL_PKT_BUF_SIZE]; + struct rtattr *rta = (void *)buf; + uint32_t mtu = dplane_ctx_get_mtu(ctx); + uint32_t nexthop_mtu = dplane_ctx_get_nh_mtu(ctx); + if (!mtu || (nexthop_mtu && nexthop_mtu < mtu)) + mtu = nexthop_mtu; + rta->rta_type = RTA_METRICS; + rta->rta_len = RTA_LENGTH(0); + rta_addattr_l(rta, NL_PKT_BUF_SIZE, RTAX_MTU, &mtu, sizeof mtu); + addattr_l(&req.n, NL_PKT_BUF_SIZE, RTA_METRICS, RTA_DATA(rta), + RTA_PAYLOAD(rta)); + } + + /* Count overall nexthops so we can decide whether to use singlepath + * or multipath case. */ + nexthop_num = 0; + for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + if (cmd == RTM_NEWROUTE && !NEXTHOP_IS_ACTIVE(nexthop->flags)) + continue; + + nexthop_num++; + } + + /* Singlepath case. */ + if (nexthop_num == 1 || multipath_num == 1) { + nexthop_num = 0; + for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) { + /* + * So we want to cover 2 types of blackhole + * routes here: + * 1) A normal blackhole route( ala from a static + * install. + * 2) A recursively resolved blackhole route + */ + if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE) { + switch (nexthop->bh_type) { + case BLACKHOLE_ADMINPROHIB: + req.r.rtm_type = RTN_PROHIBIT; + break; + case BLACKHOLE_REJECT: + req.r.rtm_type = RTN_UNREACHABLE; + break; + default: + req.r.rtm_type = RTN_BLACKHOLE; + break; + } + goto skip; + } + if (CHECK_FLAG(nexthop->flags, + NEXTHOP_FLAG_RECURSIVE)) { + if (!setsrc) { + if (family == AF_INET) { + if (nexthop->rmap_src.ipv4 + .s_addr + != 0) { + src.ipv4 = + nexthop->rmap_src + .ipv4; + setsrc = 1; + } else if (nexthop->src.ipv4 + .s_addr + != 0) { + src.ipv4 = + nexthop->src + .ipv4; + setsrc = 1; + } + } else if (family == AF_INET6) { + if (!IN6_IS_ADDR_UNSPECIFIED( + &nexthop->rmap_src + .ipv6)) { + src.ipv6 = + nexthop->rmap_src + .ipv6; + setsrc = 1; + } else if ( + !IN6_IS_ADDR_UNSPECIFIED( + &nexthop->src + .ipv6)) { + src.ipv6 = + nexthop->src + .ipv6; + setsrc = 1; + } + } + } + continue; + } + + if ((cmd == RTM_NEWROUTE + && NEXTHOP_IS_ACTIVE(nexthop->flags))) { + routedesc = nexthop->rparent + ? "recursive, single-path" + : "single-path"; + + _netlink_route_build_singlepath( + routedesc, bytelen, nexthop, &req.n, + &req.r, sizeof req, cmd); + nexthop_num++; + break; + } + } + if (setsrc && (cmd == RTM_NEWROUTE)) { + if (family == AF_INET) + addattr_l(&req.n, sizeof req, RTA_PREFSRC, + &src.ipv4, bytelen); + else if (family == AF_INET6) + addattr_l(&req.n, sizeof req, RTA_PREFSRC, + &src.ipv6, bytelen); + } + } else { /* Multipath case */ + char buf[NL_PKT_BUF_SIZE]; + struct rtattr *rta = (void *)buf; + struct rtnexthop *rtnh; + union g_addr *src1 = NULL; + + rta->rta_type = RTA_MULTIPATH; + rta->rta_len = RTA_LENGTH(0); + rtnh = RTA_DATA(rta); + + nexthop_num = 0; + for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) { + if (nexthop_num >= multipath_num) + break; + + if (CHECK_FLAG(nexthop->flags, + NEXTHOP_FLAG_RECURSIVE)) { + /* This only works for IPv4 now */ + if (!setsrc) { + if (family == AF_INET) { + if (nexthop->rmap_src.ipv4 + .s_addr + != 0) { + src.ipv4 = + nexthop->rmap_src + .ipv4; + setsrc = 1; + } else if (nexthop->src.ipv4 + .s_addr + != 0) { + src.ipv4 = + nexthop->src + .ipv4; + setsrc = 1; + } + } else if (family == AF_INET6) { + if (!IN6_IS_ADDR_UNSPECIFIED( + &nexthop->rmap_src + .ipv6)) { + src.ipv6 = + nexthop->rmap_src + .ipv6; + setsrc = 1; + } else if ( + !IN6_IS_ADDR_UNSPECIFIED( + &nexthop->src + .ipv6)) { + src.ipv6 = + nexthop->src + .ipv6; + setsrc = 1; + } + } + } + continue; + } + + if ((cmd == RTM_NEWROUTE + && NEXTHOP_IS_ACTIVE(nexthop->flags))) { + routedesc = nexthop->rparent + ? "recursive, multipath" + : "multipath"; + nexthop_num++; + + _netlink_route_build_multipath( + routedesc, bytelen, nexthop, rta, rtnh, + &req.r, &src1); + rtnh = RTNH_NEXT(rtnh); + + if (!setsrc && src1) { + if (family == AF_INET) + src.ipv4 = src1->ipv4; + else if (family == AF_INET6) + src.ipv6 = src1->ipv6; + + setsrc = 1; + } + } + } + if (setsrc && (cmd == RTM_NEWROUTE)) { + if (family == AF_INET) + addattr_l(&req.n, sizeof req, RTA_PREFSRC, + &src.ipv4, bytelen); + else if (family == AF_INET6) + addattr_l(&req.n, sizeof req, RTA_PREFSRC, + &src.ipv6, bytelen); + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("Setting source"); + } + + if (rta->rta_len > RTA_LENGTH(0)) + addattr_l(&req.n, NL_PKT_BUF_SIZE, RTA_MULTIPATH, + RTA_DATA(rta), RTA_PAYLOAD(rta)); + } + + /* If there is no useful nexthop then return. */ + if (nexthop_num == 0) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "netlink_route_multipath(): No useful nexthop."); + return 0; + } + +skip: + + /* Destination netlink address. */ + memset(&snl, 0, sizeof snl); + snl.nl_family = AF_NETLINK; + + /* Talk to netlink socket. */ + return netlink_talk_info(netlink_talk_filter, &req.n, + dplane_ctx_get_ns(ctx), 0); +} + int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in) { uint32_t actual_table; @@ -1871,6 +2211,51 @@ enum zebra_dplane_result kernel_route_rib(struct route_node *rn, return ZEBRA_DPLANE_REQUEST_SUCCESS; } +/* + * Update or delete a prefix from the kernel, + * using info from a dataplane context. + */ +enum zebra_dplane_result kernel_route_update(dplane_ctx_h ctx) +{ + int cmd, ret; + const struct prefix *p = dplane_ctx_get_dest(ctx); + + if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_DELETE) { + cmd = RTM_DELROUTE; + } else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_INSTALL) { + cmd = RTM_NEWROUTE; + } else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_UPDATE) { + + if (p->family == AF_INET || v6_rr_semantics) { + /* Single 'replace' operation */ + cmd = RTM_NEWROUTE; + } else { + /* + * So v6 route replace semantics are not in + * the kernel at this point as I understand it. + * so let's do a delete then an add. + * In the future once v6 route replace semantics + * are in we can figure out what to do here to + * allow working with old and new kernels. + * + * I'm also intentionally ignoring the failure case + * of the route delete. If that happens yeah we're + * screwed. + */ + ret = netlink_route_multipath_ctx(RTM_DELROUTE, ctx); + cmd = RTM_NEWROUTE; + } + + } else { + return ZEBRA_DPLANE_REQUEST_FAILURE; + } + + ret = netlink_route_multipath_ctx(cmd, ctx); + + return (ret == 0 ? + ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE); +} + int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, int llalen, ns_id_t ns_id) { diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index c49dc7bab2..eddb0ebfb3 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -433,6 +433,11 @@ enum zebra_dplane_result kernel_route_rib(struct route_node *rn, return ZEBRA_DPLANE_REQUEST_SUCCESS; } +enum zebra_dplane_result kernel_route_update(dplane_ctx_h ctx) +{ + return ZEBRA_DPLANE_REQUEST_FAILURE; +} + int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, int llalen, ns_id_t ns_id) { diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 149d2cb6a5..ba734269d3 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -740,6 +740,20 @@ int zsend_route_notify_owner(struct route_entry *re, const struct prefix *p, re->table, note)); } +/* + * Route-owner notification using info from dataplane update context. + */ +int zsend_route_notify_owner_ctx(dplane_ctx_h ctx, + enum zapi_route_notify_owner note) +{ + return (route_notify_internal(dplane_ctx_get_dest(ctx), + dplane_ctx_get_type(ctx), + dplane_ctx_get_instance(ctx), + dplane_ctx_get_vrf(ctx), + dplane_ctx_get_table(ctx), + note)); +} + void zsend_rule_notify_owner(struct zebra_pbr_rule *rule, enum zapi_rule_notify_owner note) { diff --git a/zebra/zapi_msg.h b/zebra/zapi_msg.h index 29fe59babf..0dcfd5d747 100644 --- a/zebra/zapi_msg.h +++ b/zebra/zapi_msg.h @@ -70,6 +70,8 @@ extern int zsend_pw_update(struct zserv *client, struct zebra_pw *pw); extern int zsend_route_notify_owner(struct route_entry *re, const struct prefix *p, enum zapi_route_notify_owner note); +extern int zsend_route_notify_owner_ctx(dplane_ctx_h ctx, + enum zapi_route_notify_owner note); extern void zsend_rule_notify_owner(struct zebra_pbr_rule *rule, enum zapi_rule_notify_owner note); diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index c0e4939860..7b548ea6dd 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -17,5 +17,737 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include -#include "zebra_dplane.h" +#include "lib/zebra.h" +#include "lib/memory.h" +#include "lib/frr_pthread.h" +#include "lib/queue.h" +#include "zebra/zebra_memory.h" +#include "zebra/zserv.h" +#include "zebra/zebra_dplane.h" +#include "zebra/rt.h" +#include "zebra/debug.h" + +/* Memory type for context blocks */ +DEFINE_MTYPE(ZEBRA, DP_CTX, "Zebra DPlane Ctx") + +#ifndef AOK +# define AOK 0 +#endif + +/* Validation value for context blocks */ +const uint32_t DPLANE_CTX_MAGIC = 0xb97a557f; + +/* Validation check macro for context blocks */ +/* #define DPLANE_DEBUG 1 */ + +#ifdef DPLANE_DEBUG + +# define DPLANE_CTX_VALID(p) \ + (assert((p) && ((p)->zd_magic == DPLANE_CTX_MAGIC))) + +#else + +# define DPLANE_CTX_VALID(p) \ + if ((p) && ((p)->zd_magic == DPLANE_CTX_MAGIC)) { ; } + +#endif /* DPLANE_DEBUG */ + +/* + * The context block used to exchange info about route updates across + * the boundary between the zebra main context (and pthread) and the + * dataplane layer (and pthread). + */ +struct zebra_dplane_ctx_s { + + /* Operation code */ + dplane_op_e zd_op; + + /* Status on return */ + enum zebra_dplane_result zd_status; + + /* TODO -- internal/sub-operation status? */ + enum zebra_dplane_status zd_remote_status; + enum zebra_dplane_status zd_kernel_status; + + /* Dest and (optional) source prefixes */ + struct prefix zd_dest; + struct prefix zd_src; + + bool zd_is_update; + + uint32_t zd_seq; + uint32_t zd_old_seq; + vrf_id_t zd_vrf_id; + uint32_t zd_table_id; + + int zd_type; + int zd_old_type; + + afi_t zd_afi; + safi_t zd_safi; + + route_tag_t zd_tag; + route_tag_t zd_old_tag; + uint32_t zd_metric; + uint16_t zd_instance; + uint16_t zd_old_instance; + + uint8_t zd_distance; + uint8_t zd_old_distance; + + uint32_t zd_mtu; + uint32_t zd_nexthop_mtu; + + /* Namespace info */ + struct zebra_dplane_info zd_ns_info; + + /* Nexthops */ + struct nexthop_group zd_ng; + + /* Embedded list linkage */ + TAILQ_ENTRY(zebra_dplane_ctx_s) zd_q_entries; + + /* Magic validation value */ + uint32_t zd_magic; +}; + +/* + * Registration block for one dataplane provider. + */ +struct zebra_dplane_provider_s { + /* Name */ + char dp_name[DPLANE_PROVIDER_NAMELEN + 1]; + + /* Priority, for ordering among providers */ + uint8_t dp_priority; + + /* Id value */ + uint32_t dp_id; + + /* Event pointer for use by the dplane thread */ + struct thread *dp_t_event; + + /* Embedded list linkage */ + TAILQ_ENTRY(zebra_dplane_dest_s) dp_q_providers; + +}; + +/* + * Globals + */ +static struct zebra_dplane_globals_s { + /* Mutex to control access to dataplane components */ + pthread_mutex_t dg_mutex; + + /* Results callback registered by zebra 'core' */ + dplane_results_fp dg_results_cb; + + /* Route-update context queue inbound to the dataplane */ + TAILQ_HEAD(zdg_ctx_q, zebra_dplane_ctx_s) dg_route_ctx_q; + + /* Ordered list of providers */ + TAILQ_HEAD(zdg_prov_q, zebra_dplane_provider_s) dg_providers_q; + + /* Event-delivery context 'master' for the dplane */ + struct thread_master *dg_master; + + /* Event/'thread' pointer for queued updates */ + struct thread *dg_t_update; + +} zdplane_g; + +/* + * Lock and unlock for interactions with the zebra 'core' + */ +#define DPLANE_LOCK() pthread_mutex_lock(&zdplane_g.dg_mutex) + +#define DPLANE_UNLOCK() pthread_mutex_unlock(&zdplane_g.dg_mutex) + +/* Prototypes */ +static int dplane_route_process(struct thread *event); + +/* + * Public APIs + */ + +/* + * Allocate an opaque context block + */ +dplane_ctx_h dplane_ctx_alloc(void) +{ + struct zebra_dplane_ctx_s *p; + + p = XCALLOC(MTYPE_DP_CTX, sizeof(struct zebra_dplane_ctx_s)); + if (p) { + p->zd_magic = DPLANE_CTX_MAGIC; + } + + return (p); +} + +/* + * Free memory for a dataplane results context block. + */ +static void dplane_ctx_free(dplane_ctx_h *pctx) +{ + if (pctx) { + DPLANE_CTX_VALID(*pctx); + + /* Free embedded nexthops */ + if ((*pctx)->zd_ng.nexthop) { + /* This deals with recursive nexthops too */ + nexthops_free((*pctx)->zd_ng.nexthop); + } + + /* Clear validation value */ + (*pctx)->zd_magic = 0; + + XFREE(MTYPE_DP_CTX, *pctx); + *pctx = NULL; + } +} + +/* + * Return a context block to the dplane module after processing + */ +void dplane_ctx_fini(dplane_ctx_h *pctx) +{ + /* TODO -- enqueue for next provider; for now, just free */ + dplane_ctx_free(pctx); +} + +/* Enqueue a context block */ +void dplane_ctx_enqueue_tail(struct dplane_ctx_q_s *q, dplane_ctx_h ctx) +{ + TAILQ_INSERT_TAIL(q, ctx, zd_q_entries); +} + +/* Dequeue a context block from the head of a list */ +void dplane_ctx_dequeue(struct dplane_ctx_q_s *q, dplane_ctx_h *ctxp) +{ + dplane_ctx_h ctx = TAILQ_FIRST(q); + if (ctx) { + TAILQ_REMOVE(q, ctx, zd_q_entries); + } + + *ctxp = ctx; +} + +/* + * Accessors for information from the context object + */ +enum zebra_dplane_result dplane_ctx_get_status(const dplane_ctx_h ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->zd_status); +} + +dplane_op_e dplane_ctx_get_op(const dplane_ctx_h ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->zd_op); +} + +const char *dplane_op2str(dplane_op_e op) +{ + const char *ret = "UNKNOWN"; + + switch(op) { + case DPLANE_OP_NONE: + ret = "NONE"; + break; + + /* Route update */ + case DPLANE_OP_ROUTE_INSTALL: + ret = "ROUTE_INSTALL"; + break; + case DPLANE_OP_ROUTE_UPDATE: + ret = "ROUTE_UPDATE"; + break; + case DPLANE_OP_ROUTE_DELETE: + ret = "ROUTE_DELETE"; + break; + + }; + + return (ret); +} + +const struct prefix *dplane_ctx_get_dest(const dplane_ctx_h ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (&(ctx->zd_dest)); +} + +/* Source prefix is a little special - use convention like prefix-len of zero + * and all-zeroes address means "no src prefix"? or ... return NULL in that case? + */ +const struct prefix *dplane_ctx_get_src(const dplane_ctx_h ctx) +{ + DPLANE_CTX_VALID(ctx); + + if (ctx->zd_src.prefixlen == 0 && + IN6_IS_ADDR_UNSPECIFIED(&(ctx->zd_src.u.prefix6))) { + return (NULL); + } else { + return (&(ctx->zd_src)); + } +} + +bool dplane_ctx_is_update(const dplane_ctx_h ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->zd_is_update); +} + +uint32_t dplane_ctx_get_seq(const dplane_ctx_h ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->zd_seq); +} + +uint32_t dplane_ctx_get_old_seq(const dplane_ctx_h ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->zd_old_seq); +} + +vrf_id_t dplane_ctx_get_vrf(const dplane_ctx_h ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->zd_vrf_id); +} + +int dplane_ctx_get_type(const dplane_ctx_h ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->zd_type); +} + +int dplane_ctx_get_old_type(const dplane_ctx_h ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->zd_old_type); +} + +afi_t dplane_ctx_get_afi(const dplane_ctx_h ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->zd_afi); +} + +safi_t dplane_ctx_get_safi(const dplane_ctx_h ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->zd_safi); +} + +uint32_t dplane_ctx_get_table(const dplane_ctx_h ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->zd_table_id); +} + +route_tag_t dplane_ctx_get_tag(const dplane_ctx_h ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->zd_tag); +} + +route_tag_t dplane_ctx_get_old_tag(const dplane_ctx_h ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->zd_old_tag); +} + +uint16_t dplane_ctx_get_instance(const dplane_ctx_h ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->zd_instance); +} + +uint16_t dplane_ctx_get_old_instance(const dplane_ctx_h ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->zd_instance); +} + +uint32_t dplane_ctx_get_metric(const dplane_ctx_h ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->zd_metric); +} + +uint32_t dplane_ctx_get_mtu(const dplane_ctx_h ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->zd_mtu); +} + +uint32_t dplane_ctx_get_nh_mtu(const dplane_ctx_h ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->zd_nexthop_mtu); +} + +uint8_t dplane_ctx_get_distance(const dplane_ctx_h ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->zd_distance); +} + +uint8_t dplane_ctx_get_old_distance(const dplane_ctx_h ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->zd_old_distance); +} + +const struct nexthop_group *dplane_ctx_get_ng(const dplane_ctx_h ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (&(ctx->zd_ng)); +} + +const struct zebra_dplane_info *dplane_ctx_get_ns(const dplane_ctx_h ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (&(ctx->zd_ns_info)); +} + +/* + * End of dplane context accessors + */ + +/* + * Initialize a context block for a route update from zebra data structs. + */ +static int dplane_ctx_route_init(dplane_ctx_h ctx, + dplane_op_e op, + struct route_node *rn, + struct route_entry *re) +{ + int ret = EINVAL; + const struct route_table *table = NULL; + const rib_table_info_t *info; + const struct prefix *p, *src_p; + struct zebra_ns *zns; + struct zebra_vrf *zvrf; + + if (!ctx || !rn || !re) { + goto done; + } + + ctx->zd_op = op; + + ctx->zd_type = re->type; + + /* Prefixes: dest, and optional source */ + srcdest_rnode_prefixes(rn, &p, &src_p); + + prefix_copy(&(ctx->zd_dest), p); + + if (src_p) { + prefix_copy(&(ctx->zd_src), src_p); + } else { + memset(&(ctx->zd_src), 0, sizeof(ctx->zd_src)); + } + + ctx->zd_table_id = re->table; + + ctx->zd_metric = re->metric; + ctx->zd_vrf_id = re->vrf_id; + ctx->zd_mtu = re->mtu; + ctx->zd_nexthop_mtu = re->nexthop_mtu; + ctx->zd_instance = re->instance; + ctx->zd_tag = re->tag; + ctx->zd_distance = re->distance; + + table = srcdest_rnode_table(rn); + info = table->info; + + ctx->zd_afi = info->afi; + ctx->zd_safi = info->safi; + + /* Extract ns info - can't use pointers to 'core' structs */ + zvrf = vrf_info_lookup(re->vrf_id); + zns = zvrf->zns; + + zebra_dplane_info_from_zns(&(ctx->zd_ns_info), zns, true /*is_cmd*/); + +#if defined(HAVE_NETLINK) + /* Increment message counter after copying to context struct - may need + * two messages in some 'update' cases. + */ + if (op == DPLANE_OP_ROUTE_UPDATE) { + zns->netlink_cmd.seq += 2; + } else { + zns->netlink_cmd.seq++; + } +#endif /* NETLINK*/ + + /* Copy nexthops; recursive info is included too */ + copy_nexthops(&(ctx->zd_ng.nexthop), re->ng.nexthop, NULL); + + /* Trying out the sequence number idea, so we can try to detect + * when a result is stale. + */ + re->dplane_sequence++; + ctx->zd_seq = re->dplane_sequence; + + ret = AOK; + +done: + return ret; +} + +/* + * Enqueue a new route update, + * and ensure an event is active for the dataplane thread. + */ +static int dplane_route_enqueue(dplane_ctx_h ctx) +{ + int ret = EINVAL; + + /* Enqueue for processing by the dataplane thread */ + DPLANE_LOCK(); + { + TAILQ_INSERT_TAIL(&zdplane_g.dg_route_ctx_q, ctx, zd_q_entries); + } + DPLANE_UNLOCK(); + + /* Ensure that an event for the dataplane thread is active */ + thread_add_event(zdplane_g.dg_master, dplane_route_process, NULL, 0, + &zdplane_g.dg_t_update); + + ret = AOK; + + return (ret); +} + +/* + * Attempt to dequeue a route-update block + */ +static dplane_ctx_h dplane_route_dequeue(void) +{ + dplane_ctx_h ctx = NULL; + + DPLANE_LOCK(); + { + ctx = TAILQ_FIRST(&zdplane_g.dg_route_ctx_q); + if (ctx) { + TAILQ_REMOVE(&zdplane_g.dg_route_ctx_q, + ctx, zd_q_entries); + } + } + DPLANE_UNLOCK(); + + return (ctx); +} + +/* + * Utility that prepares a route update and enqueues it for processing + */ +static int dplane_route_update_internal(struct route_node *rn, + struct route_entry *re, + struct route_entry *old_re, + dplane_op_e op) +{ + int ret = EINVAL; + dplane_ctx_h ctx = NULL; + + /* Obtain context block */ + ctx = dplane_ctx_alloc(); + if (ctx == NULL) { + ret = ENOMEM; + goto done; + } + + /* Init context with info from zebra data structs */ + ret = dplane_ctx_route_init(ctx, op, rn, re); + if (ret == AOK) { + /* Capture some extra info for update case + * where there's a different 'old' route. + */ + if ((op == DPLANE_OP_ROUTE_UPDATE) && old_re && (old_re != re)) { + ctx->zd_is_update = true; + + old_re->dplane_sequence++; + ctx->zd_old_seq = old_re->dplane_sequence; + + ctx->zd_old_tag = old_re->tag; + ctx->zd_old_type = old_re->type; + ctx->zd_old_instance = old_re->instance; + ctx->zd_old_distance = old_re->distance; + } + + /* Enqueue context for processing */ + ret = dplane_route_enqueue(ctx); + } + +done: + if (ret != AOK && ctx) { + dplane_ctx_free(&ctx); + } + + return (ret); +} + +/* + * Enqueue a route 'add' for the dataplane. + */ +int dplane_route_add(struct route_node *rn, + struct route_entry *re) +{ + int ret = EINVAL; + + if (rn == NULL || re == NULL) { + goto done; + } + + ret = dplane_route_update_internal(rn, re, NULL, + DPLANE_OP_ROUTE_INSTALL); + +done: + + return (ret); +} + +/* + * Enqueue a route update for the dataplane. + */ +int dplane_route_update(struct route_node *rn, + struct route_entry *re, + struct route_entry *old_re) +{ + int ret = EINVAL; + + if (rn == NULL || re == NULL) { + goto done; + } + + ret = dplane_route_update_internal(rn, re, old_re, + DPLANE_OP_ROUTE_UPDATE); + +done: + + return (ret); +} + +/* + * Enqueue a route removal for the dataplane. + */ +int dplane_route_delete(struct route_node *rn, + struct route_entry *re) +{ + int ret = EINVAL; + + if (rn == NULL || re == NULL) { + goto done; + } + + ret = dplane_route_update_internal(rn, re, NULL, + DPLANE_OP_ROUTE_DELETE); + +done: + + return (ret); +} + +/* + * Event handler function for routing updates + */ +static int dplane_route_process(struct thread *event) +{ + enum zebra_dplane_result res; + dplane_ctx_h ctx; + + while (1) { + /* TODO -- limit number of updates per cycle? */ + ctx = dplane_route_dequeue(); + if (ctx == NULL) { + break; + } + + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { + char dest_str[PREFIX_STRLEN]; + + prefix2str(dplane_ctx_get_dest(ctx), + dest_str, sizeof(dest_str)); + + zlog_debug("%u:%s Dplane update ctx %p op %s", + dplane_ctx_get_vrf(ctx), dest_str, + ctx, dplane_op2str(dplane_ctx_get_op(ctx))); + } + + res = kernel_route_update(ctx); + + ctx->zd_status = res; + + /* TODO -- support series of providers */ + + /* Enqueue result to zebra main context */ + (*zdplane_g.dg_results_cb)(ctx); + + ctx = NULL; + } + + return (0); +} + +/* + * Zebra registers a results callback with the dataplane system + */ +int dplane_results_register(dplane_results_fp fp) +{ + zdplane_g.dg_results_cb = fp; + return (AOK); +} + +/* + * Initialize the dataplane module during startup, internal/private version + */ +static void zebra_dplane_init_internal(struct zebra_t *zebra) +{ + memset(&zdplane_g, 0, sizeof(zdplane_g)); + + pthread_mutex_init(&zdplane_g.dg_mutex, NULL); + + TAILQ_INIT(&zdplane_g.dg_route_ctx_q); + TAILQ_INIT(&zdplane_g.dg_providers_q); + + /* TODO -- using zebra core event thread temporarily */ + zdplane_g.dg_master = zebra->master; + + return; +} + +/* + * Initialize the dataplane module at startup. + */ +void zebra_dplane_init(void) +{ + zebra_dplane_init_internal(&zebrad); +} diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 7cbef7453c..42d88e9b8a 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -20,24 +20,22 @@ #ifndef _ZEBRA_DPLANE_H #define _ZEBRA_DPLANE_H 1 -#include "zebra.h" -#include "zserv.h" -#include "prefix.h" -#include "nexthop.h" -#include "nexthop_group.h" +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/nexthop.h" +#include "lib/nexthop_group.h" +#include "lib/openbsd-queue.h" +#include "zebra/zebra_ns.h" +#include "zebra/rib.h" +#include "zebra/zserv.h" -/* - * API between the zebra dataplane system and the main zebra processing - * context. - */ - /* Key netlink info from zebra ns */ struct zebra_dplane_info { ns_id_t ns_id; #if defined(HAVE_NETLINK) - uint32_t nl_pid; + struct nlsock nls; bool is_cmd; #endif }; @@ -52,21 +50,13 @@ zebra_dplane_info_from_zns(struct zebra_dplane_info *zns_info, #if defined(HAVE_NETLINK) zns_info->is_cmd = is_cmd; if (is_cmd) { - zns_info->nl_pid = zns->netlink_cmd.snl.nl_pid; + zns_info->nls = zns->netlink_cmd; } else { - zns_info->nl_pid = zns->netlink.snl.nl_pid; + zns_info->nls = zns->netlink; } #endif /* NETLINK */ } -/* - * Enqueue a route install or update for the dataplane. - */ - -/* - * Enqueue a route removal for the dataplane. - */ - /* * Result codes used when returning status back to the main zebra context. */ @@ -96,4 +86,150 @@ enum zebra_dplane_result { ZEBRA_DPLANE_REQUEST_FAILURE, }; +/* + * API between the zebra dataplane system and the main zebra processing + * context. + */ + +/* + * Enqueue a route install or update for the dataplane. + */ +typedef enum { + DPLANE_OP_NONE = 0, + + /* Route update */ + DPLANE_OP_ROUTE_INSTALL, + DPLANE_OP_ROUTE_UPDATE, + DPLANE_OP_ROUTE_DELETE, + +} dplane_op_e; + +/* + * Enqueue a route removal for the dataplane. + */ + +/* + * Opaque context block used to exchange info between the main zebra + * context and the dataplane module(s). If these are two independent pthreads, + * they cannot share existing global data structures safely. + */ +typedef struct zebra_dplane_ctx_s * dplane_ctx_h; + +/* Define a tailq list type for context blocks. The list is exposed/public, + * but the internal linkage in the context struct is private, so there + * are accessor apis that support enqueue and dequeue. + */ +TAILQ_HEAD(dplane_ctx_q_s, zebra_dplane_ctx_s); + +/* + * Allocate an opaque context block, currently for a route update. + */ +dplane_ctx_h dplane_ctx_alloc(void); + +/* Return a dataplane results context block after use; the caller's pointer will + * be cleared. + */ +void dplane_ctx_fini(dplane_ctx_h *pctx); + +/* Enqueue a context block to caller's tailq. This just exists so that the + * context struct can remain opaque. + */ +void dplane_ctx_enqueue_tail(struct dplane_ctx_q_s *q, dplane_ctx_h ctx); + +/* Dequeue a context block from the head of caller's tailq */ +void dplane_ctx_dequeue(struct dplane_ctx_q_s *q, dplane_ctx_h *ctxp); + +/* + * Accessors for information from the context object + */ +enum zebra_dplane_result dplane_ctx_get_status(const dplane_ctx_h ctx); + +dplane_op_e dplane_ctx_get_op(const dplane_ctx_h ctx); +const char *dplane_op2str(dplane_op_e op); + +const struct prefix *dplane_ctx_get_dest(const dplane_ctx_h ctx); + +/* Source prefix is a little special - use convention like prefix-len of zero + * and all-zeroes address means "no src prefix"? or ... return NULL in that case? + */ +const struct prefix *dplane_ctx_get_src(const dplane_ctx_h ctx); + +bool dplane_ctx_is_update(const dplane_ctx_h ctx); +uint32_t dplane_ctx_get_seq(const dplane_ctx_h ctx); +uint32_t dplane_ctx_get_old_seq(const dplane_ctx_h ctx); +vrf_id_t dplane_ctx_get_vrf(const dplane_ctx_h ctx); +int dplane_ctx_get_type(const dplane_ctx_h ctx); +int dplane_ctx_get_old_type(const dplane_ctx_h ctx); +afi_t dplane_ctx_get_afi(const dplane_ctx_h ctx); +safi_t dplane_ctx_get_safi(const dplane_ctx_h ctx); +uint32_t dplane_ctx_get_table(const dplane_ctx_h ctx); +route_tag_t dplane_ctx_get_tag(const dplane_ctx_h ctx); +route_tag_t dplane_ctx_get_old_tag(const dplane_ctx_h ctx); +uint16_t dplane_ctx_get_instance(const dplane_ctx_h ctx); +uint16_t dplane_ctx_get_old_instance(const dplane_ctx_h ctx); +uint32_t dplane_ctx_get_metric(const dplane_ctx_h ctx); +uint32_t dplane_ctx_get_mtu(const dplane_ctx_h ctx); +uint32_t dplane_ctx_get_nh_mtu(const dplane_ctx_h ctx); +uint8_t dplane_ctx_get_distance(const dplane_ctx_h ctx); +uint8_t dplane_ctx_get_old_distance(const dplane_ctx_h ctx); + +const struct nexthop_group *dplane_ctx_get_ng(const dplane_ctx_h ctx); +const struct zebra_dplane_info *dplane_ctx_get_ns(const dplane_ctx_h ctx); + +/* + * Enqueue route change operations for the dataplane. + */ +int dplane_route_add(struct route_node *rn, + struct route_entry *re); + +int dplane_route_update(struct route_node *rn, + struct route_entry *re, + struct route_entry *old_re); + +int dplane_route_delete(struct route_node *rn, + struct route_entry *re); + +/* Opaque handle to a dataplane provider plugin */ +typedef struct zebra_dplane_provider_s *dplane_provider_h; + +#define DPLANE_PROVIDER_NAMELEN 64 + +/* Priority or ordering values for providers. The idea is that there may be + * some pre-processing, followed by an external or remote dataplane, + * followed by the kernel, followed by some post-processing step (such as + * the fpm output stream.) + */ +typedef enum { + DPLANE_PRIO_NONE = 0, + DPLANE_PRIO_PREPROCESS, + DPLANE_PRIO_PRE_KERNEL, + DPLANE_PRIO_KERNEL, + DPLANE_PRIO_POSTPROCESS, +} dplane_provider_prio_e; + +/* Provider's entry-point to process a context block */ +typedef int (*dplane_provider_process_fp)(dplane_ctx_h ctx); + +/* Provider registration */ +int dplane_provider_register(const char *name, + dplane_provider_prio_e prio, + dplane_provider_process_fp fp); + +/* + * Results are returned to zebra core via a callback + */ +typedef int (*dplane_results_fp)(const dplane_ctx_h ctx); + +/* + * Zebra registers a results callback with the dataplane. The callback is + * called in the dataplane thread context, so the expectation is that the + * context is queued (or that processing is very limited). + */ +int dplane_results_register(dplane_results_fp fp); + +/* + * Initialize the dataplane modules at zebra startup. + */ +void zebra_dplane_init(void); + #endif /* _ZEBRA_DPLANE_H */ diff --git a/zebra/zebra_memory.h b/zebra/zebra_memory.h index e3439d5f64..fcabab97c5 100644 --- a/zebra/zebra_memory.h +++ b/zebra/zebra_memory.h @@ -34,5 +34,6 @@ DECLARE_MTYPE(STATIC_ROUTE) DECLARE_MTYPE(RIB_DEST) DECLARE_MTYPE(RIB_TABLE_INFO) DECLARE_MTYPE(RNH) +DECLARE_MTYPE(DP_CTX) #endif /* _QUAGGA_ZEBRA_MEMORY_H */ diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index ed70a34c0b..ca909ef413 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -87,6 +87,41 @@ struct zebra_ns { struct ns *ns; }; +/* Key netlink info from zebra ns, passed from the zebra main context + * to the dataplane/kernel context (which might be in a different pthread). + */ +struct zebra_ns_info { + ns_id_t ns_id; + +#if defined(HAVE_NETLINK) + struct nlsock nls; + uint32_t nl_cmd_pid; + bool is_cmd; +#endif +}; + +/* Utility to fill in zns info from main zns struct */ +static inline void zebra_ns_info_from_ns(struct zebra_ns_info *zns_info, + const struct zebra_ns *zns, + bool is_cmd) +{ + zns_info->ns_id = zns->ns_id; + +#if defined(HAVE_NETLINK) + /* Need to know whether we're using the 'command' netlink socket, + * and need to know its port-id to handle some test/filtering + * cases. + */ + zns_info->is_cmd = is_cmd; + zns_info->nl_cmd_pid = zns->netlink_cmd.snl.nl_pid; + if (is_cmd) { + zns_info->nls = zns->netlink_cmd; + } else { + zns_info->nls = zns->netlink; + } +#endif /* NETLINK */ +} + struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id); int zebra_ns_init(void); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index db610098f0..620dfef3b3 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -52,6 +52,15 @@ #include "zebra/zebra_routemap.h" #include "zebra/zebra_vrf.h" #include "zebra/zebra_vxlan.h" +#include "zebra/zapi_msg.h" +#include "zebra/zebra_dplane.h" + +/* + * Event, list, and mutex for delivery of dataplane results + */ +static pthread_mutex_t dplane_mutex; +static struct thread *t_dplane; +static struct dplane_ctx_q_s rib_dplane_q; DEFINE_HOOK(rib_update, (struct route_node * rn, const char *reason), (rn, reason)) @@ -1268,12 +1277,12 @@ static void rib_uninstall(struct route_node *rn, struct route_entry *re) if (info->safi == SAFI_UNICAST) hook_call(rib_update, rn, "rib_uninstall"); - if (!RIB_SYSTEM_ROUTE(re)) - rib_uninstall_kernel(rn, re); - /* If labeled-unicast route, uninstall transit LSP. */ if (zebra_rib_labeled_unicast(re)) zebra_mpls_lsp_uninstall(info->zvrf, rn, re); + + if (!RIB_SYSTEM_ROUTE(re)) + rib_uninstall_kernel(rn, re); } if (CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED)) { @@ -1784,7 +1793,8 @@ static void rib_process(struct route_node *rn) if (IS_ZEBRA_DEBUG_RIB_DETAILED) { zlog_debug( - "%u:%s: After processing: old_selected %p new_selected %p old_fib %p new_fib %p", + "%u:%s: After processing: old_selected %p " + "new_selected %p old_fib %p new_fib %p", vrf_id, buf, (void *)old_selected, (void *)new_selected, (void *)old_fib, (void *)new_fib); } @@ -3002,10 +3012,65 @@ void rib_close_table(struct route_table *table) } } +/* + * + */ +static int rib_process_dplane_results(struct thread *thread) +{ + dplane_ctx_h ctx; + + do { + /* Take lock controlling queue of results */ + pthread_mutex_lock(&dplane_mutex); + { + /* Dequeue context block */ + dplane_ctx_dequeue(&rib_dplane_q, &ctx); + } + pthread_mutex_unlock(&dplane_mutex); + + if (ctx) { + dplane_ctx_fini(&ctx); + } else { + break; + } + + } while(1); + + return (0); +} + +/* + * Results are returned from the dataplane subsystem, in the context of + * the dataplane thread. We enqueue the results here for processing by + * the main thread later. + */ +static int rib_dplane_results(dplane_ctx_h ctx) +{ + /* Take lock controlling queue of results */ + pthread_mutex_lock(&dplane_mutex); + { + /* Enqueue context block */ + dplane_ctx_enqueue_tail(&rib_dplane_q, ctx); + } + pthread_mutex_unlock(&dplane_mutex); + + /* Ensure event is signalled to zebra main thread */ + thread_add_event(zebrad.master, rib_process_dplane_results, NULL, 0, + &t_dplane); + + return (0); +} + /* Routing information base initialize. */ void rib_init(void) { rib_queue_init(&zebrad); + + /* Init dataplane, and register for results */ + pthread_mutex_init(&dplane_mutex, NULL); + TAILQ_INIT(&rib_dplane_q); + zebra_dplane_init(); + dplane_results_register(rib_dplane_results); } /* diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index a39d74b08b..7fef644fac 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -1,7 +1,7 @@ /* * Zebra Vrf Header * Copyright (C) 2016 Cumulus Networks - * Donald Sahrp + * Donald Sharp * * This file is part of Quagga. * From e5ac2adf171d5557fdc7fe3effea6e219fd69630 Mon Sep 17 00:00:00 2001 From: Mark Stapp Date: Tue, 26 Jun 2018 14:28:58 -0400 Subject: [PATCH 02/22] zebra: wip: early version of dplane result handler Early try at a result handler for async dplane route updates Signed-off-by: Mark Stapp --- zebra/zebra_rib.c | 248 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 247 insertions(+), 1 deletion(-) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 620dfef3b3..c99b5d784f 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -1868,6 +1868,252 @@ static void rib_process(struct route_node *rn) rib_gc_dest(rn); } +/* + * Utility to match route with dplane context data + */ +static bool rib_route_match_ctx(const struct route_entry *re, + const dplane_ctx_h ctx, bool is_update) +{ + bool result = false; + + if (is_update) { + /* + * In 'update' case, we test info about the 'previous' or + * 'old' route + */ + if ((re->type == dplane_ctx_get_old_type(ctx)) && + (re->instance == dplane_ctx_get_old_instance(ctx))) { + result = true; + + /* TODO -- we're using this extra test, but it's not + * exactly clear why. + */ + if (re->type == ZEBRA_ROUTE_STATIC && + (re->distance != dplane_ctx_get_old_distance(ctx) || + re->tag != dplane_ctx_get_old_tag(ctx))) { + result = false; + } + } + + } else { + /* + * Ordinary, single-route case using primary context info + */ + if ((dplane_ctx_get_op(ctx) != DPLANE_OP_ROUTE_DELETE) && + CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED)) { + /* Skip route that's been deleted */ + goto done; + } + + if ((re->type == dplane_ctx_get_type(ctx)) && + (re->instance == dplane_ctx_get_instance(ctx))) { + result = true; + + /* TODO -- we're using this extra test, but it's not + * exactly clear why. + */ + if (re->type == ZEBRA_ROUTE_STATIC && + (re->distance != dplane_ctx_get_distance(ctx) || + re->tag != dplane_ctx_get_tag(ctx))) { + result = false; + } + } + } + +done: + + return (result); +} + +/* + * TODO - WIP + */ +static void rib_process_after(dplane_ctx_h ctx) +{ + struct route_table *table = NULL; + struct route_node *rn = NULL; + struct route_entry *re = NULL, *old_re = NULL, *rib; + bool is_update = false; + struct nexthop *nexthop; + char dest_str[PREFIX_STRLEN]; + dplane_op_e op; + enum zebra_dplane_result status; + const struct prefix *dest_pfx, *src_pfx; + + /* Locate rn and re(s) from ctx */ + + table = zebra_vrf_table_with_table_id(dplane_ctx_get_afi(ctx), + dplane_ctx_get_safi(ctx), + dplane_ctx_get_vrf(ctx), + dplane_ctx_get_table(ctx)); + if (table == NULL) { + if (IS_ZEBRA_DEBUG_DPLANE) { + zlog_debug("Failed to process dplane results: no table " + "for afi %d, safi %d, vrf %u", + dplane_ctx_get_afi(ctx), + dplane_ctx_get_safi(ctx), + dplane_ctx_get_vrf(ctx)); + } + goto done; + } + + dest_pfx = dplane_ctx_get_dest(ctx); + + /* Note well: only capturing the prefix string if debug is enabled here; + * unconditional log messages will have to generate the string. + */ + if (IS_ZEBRA_DEBUG_DPLANE) { + prefix2str(dest_pfx, dest_str, sizeof(dest_str)); + } + + src_pfx = dplane_ctx_get_src(ctx); + rn = srcdest_rnode_get(table, dplane_ctx_get_dest(ctx), + src_pfx ? (struct prefix_ipv6 * )src_pfx : NULL); + if (rn == NULL) { + if (IS_ZEBRA_DEBUG_DPLANE) { + zlog_debug("Failed to process dplane results: no " + "route for %u:%s", + dplane_ctx_get_vrf(ctx), dest_str); + } + goto done; + } + + srcdest_rnode_prefixes(rn, &dest_pfx, &src_pfx); + + op = dplane_ctx_get_op(ctx); + status = dplane_ctx_get_status(ctx); + + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { + zlog_debug("%u:%s Processing dplane ctx %p, op %s result %d", + dplane_ctx_get_vrf(ctx), dest_str, ctx, + dplane_op2str(op), status); + } + + if (op == DPLANE_OP_ROUTE_DELETE) { + /* + * In the delete case, the zebra core datastructs were + * updated (or removed) at the time the delete was issued, + * so we're just notifying the route owner. + */ + if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) { + zsend_route_notify_owner_ctx(ctx, ZAPI_ROUTE_REMOVED); + } else { + zsend_route_notify_owner_ctx(ctx, + ZAPI_ROUTE_FAIL_INSTALL); + + zlog_warn("%u:%s: Route Deletion failure", + dplane_ctx_get_vrf(ctx), + prefix2str(dest_pfx, + dest_str, sizeof(dest_str))); + } + + /* Nothing more to do in delete case */ + goto done; + } + + /* + * Update is a bit of a special case, where we may have both old and new + * routes to post-process. + */ + is_update = dplane_ctx_is_update(ctx); + + /* + * Take a pass through the routes, look for matches with the context + * info. + */ + RNODE_FOREACH_RE(rn, rib) { + + if (re == NULL) { + if (rib_route_match_ctx(rib, ctx, false)) { + re = rib; + } + } + + /* Check for old route match */ + if (is_update && (old_re == NULL)) { + if (rib_route_match_ctx(rib, ctx, true /*is_update*/)) { + old_re = rib; + } + } + + /* Have we found the routes we need to work on? */ + if (re && ((!is_update || old_re))) { + break; + } + } + + /* + * Check sequence number(s) to detect stale results before continuing + */ + if (re && (re->dplane_sequence != dplane_ctx_get_seq(ctx))) { + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { + zlog_debug("%u:%s Stale dplane result for re %p", + dplane_ctx_get_vrf(ctx), dest_str, re); + } + re = NULL; + } + + if (old_re && + (old_re->dplane_sequence != dplane_ctx_get_old_seq(ctx))) { + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { + zlog_debug("%u:%s Stale dplane result for old_re %p", + dplane_ctx_get_vrf(ctx), dest_str, old_re); + } + old_re = NULL; + } + + /* + * Here's sort of a tough one: the route update result is stale. + * Is it better to use the context block info to generate + * redist and owner notification, or is it better to wait + * for the up-to-date result to arrive? + */ + if (re == NULL) { + /* TODO -- for now, only expose up-to-date results */ + goto done; + } + + if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) { + /* Set nexthop FIB flags */ + for (ALL_NEXTHOPS(re->ng, nexthop)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + else + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + } + + /* Redistribute */ + /* TODO -- still calling the redist api using the route_entries, + * and there's a corner-case here: if there's no client + * for the 'new' route, a redist deleting the 'old' route + * will be sent. But if the 'old' context info was stale, + * 'old_re' will be NULL here and that delete will not be sent. + */ + redistribute_update(dest_pfx, src_pfx, re, old_re); + + /* Notify route owner */ + zsend_route_notify_owner(re, + dest_pfx, ZAPI_ROUTE_INSTALLED); + + } else { + zsend_route_notify_owner(re, dest_pfx, + ZAPI_ROUTE_FAIL_INSTALL); + + zlog_warn("%u:%s: Route install failed", + dplane_ctx_get_vrf(ctx), + prefix2str(dest_pfx, + dest_str, sizeof(dest_str))); + } + +done: + + /* Return context to dataplane module */ + dplane_ctx_fini(&ctx); +} + /* Take a list of route_node structs and return 1, if there was a record * picked from it and processed by rib_process(). Don't process more, * than one RN record; operate only in the specified sub-queue. @@ -3029,7 +3275,7 @@ static int rib_process_dplane_results(struct thread *thread) pthread_mutex_unlock(&dplane_mutex); if (ctx) { - dplane_ctx_fini(&ctx); + rib_process_after(ctx); } else { break; } From 655d681a4415670ab1e7b19446619bb751d64dd0 Mon Sep 17 00:00:00 2001 From: Mark Stapp Date: Tue, 26 Jun 2018 15:10:13 -0400 Subject: [PATCH 03/22] zebra: update dplane route api return type Use the dp_req_result enum type for the route update apis. Signed-off-by: Mark Stapp --- zebra/zebra_dplane.c | 42 +++++++++++++++++++++--------------------- zebra/zebra_dplane.h | 18 +++++++----------- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 7b548ea6dd..43b08641a9 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -570,11 +570,13 @@ static dplane_ctx_h dplane_route_dequeue(void) /* * Utility that prepares a route update and enqueues it for processing */ -static int dplane_route_update_internal(struct route_node *rn, - struct route_entry *re, - struct route_entry *old_re, - dplane_op_e op) +static enum zebra_dplane_result +dplane_route_update_internal(struct route_node *rn, + struct route_entry *re, + struct route_entry *old_re, + dplane_op_e op) { + enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; int ret = EINVAL; dplane_ctx_h ctx = NULL; @@ -608,20 +610,22 @@ static int dplane_route_update_internal(struct route_node *rn, } done: - if (ret != AOK && ctx) { + if (ret == AOK) { + result = ZEBRA_DPLANE_REQUEST_QUEUED; + } else if (ctx) { dplane_ctx_free(&ctx); - } + } - return (ret); + return (result); } /* * Enqueue a route 'add' for the dataplane. */ -int dplane_route_add(struct route_node *rn, - struct route_entry *re) +enum zebra_dplane_result dplane_route_add(struct route_node *rn, + struct route_entry *re) { - int ret = EINVAL; + enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE; if (rn == NULL || re == NULL) { goto done; @@ -631,18 +635,17 @@ int dplane_route_add(struct route_node *rn, DPLANE_OP_ROUTE_INSTALL); done: - return (ret); } /* * Enqueue a route update for the dataplane. */ -int dplane_route_update(struct route_node *rn, - struct route_entry *re, - struct route_entry *old_re) +enum zebra_dplane_result dplane_route_update(struct route_node *rn, + struct route_entry *re, + struct route_entry *old_re) { - int ret = EINVAL; + enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE; if (rn == NULL || re == NULL) { goto done; @@ -650,19 +653,17 @@ int dplane_route_update(struct route_node *rn, ret = dplane_route_update_internal(rn, re, old_re, DPLANE_OP_ROUTE_UPDATE); - done: - return (ret); } /* * Enqueue a route removal for the dataplane. */ -int dplane_route_delete(struct route_node *rn, - struct route_entry *re) +enum zebra_dplane_result dplane_route_delete(struct route_node *rn, + struct route_entry *re) { - int ret = EINVAL; + enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE; if (rn == NULL || re == NULL) { goto done; @@ -672,7 +673,6 @@ int dplane_route_delete(struct route_node *rn, DPLANE_OP_ROUTE_DELETE); done: - return (ret); } diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 42d88e9b8a..364d77db43 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -104,10 +104,6 @@ typedef enum { } dplane_op_e; -/* - * Enqueue a route removal for the dataplane. - */ - /* * Opaque context block used to exchange info between the main zebra * context and the dataplane module(s). If these are two independent pthreads, @@ -179,15 +175,15 @@ const struct zebra_dplane_info *dplane_ctx_get_ns(const dplane_ctx_h ctx); /* * Enqueue route change operations for the dataplane. */ -int dplane_route_add(struct route_node *rn, - struct route_entry *re); +enum zebra_dplane_result dplane_route_add(struct route_node *rn, + struct route_entry *re); -int dplane_route_update(struct route_node *rn, - struct route_entry *re, - struct route_entry *old_re); +enum zebra_dplane_result dplane_route_update(struct route_node *rn, + struct route_entry *re, + struct route_entry *old_re); -int dplane_route_delete(struct route_node *rn, - struct route_entry *re); +enum zebra_dplane_result dplane_route_delete(struct route_node *rn, + struct route_entry *re); /* Opaque handle to a dataplane provider plugin */ typedef struct zebra_dplane_provider_s *dplane_provider_h; From 97f5b44182fc154e9ff7cc3ddc9622f0f4f53103 Mon Sep 17 00:00:00 2001 From: Mark Stapp Date: Wed, 27 Jun 2018 16:10:30 -0400 Subject: [PATCH 04/22] zebra: use async dplane route updates Enqueue updates to the dplane system; add a couple of stats. Signed-off-by: Mark Stapp --- zebra/zebra_rib.c | 83 ++++++++++++++++++++++++++++++++++------------- zebra/zebra_vrf.h | 2 ++ 2 files changed, 63 insertions(+), 22 deletions(-) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index c99b5d784f..784b92786a 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -1169,8 +1169,11 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re, { struct nexthop *nexthop; rib_table_info_t *info = srcdest_rnode_table_info(rn); - const struct prefix *p, *src_p; struct zebra_vrf *zvrf = vrf_info_lookup(re->vrf_id); + const struct prefix *p, *src_p; + enum zebra_dplane_result ret; + + rib_dest_t *dest = rib_dest_from_rnode(rn); srcdest_rnode_prefixes(rn, &p, &src_p); @@ -1202,24 +1205,40 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re, if (old && (old != re) && (old->type != re->type)) zsend_route_notify_owner(old, p, ZAPI_ROUTE_BETTER_ADMIN_WON); + /* Update fib selection */ + dest->selected_fib = re; + /* * Make sure we update the FPM any time we send new information to * the kernel. */ hook_call(rib_update, rn, "installing in kernel"); - switch (kernel_route_rib(rn, p, src_p, old, re)) { + + /* Send add or update */ + if (old && (old != re)) { + ret = dplane_route_update(rn, re, old); + } else { + ret = dplane_route_add(rn, re); + } + + switch (ret) { case ZEBRA_DPLANE_REQUEST_QUEUED: - flog_err( - EC_ZEBRA_DP_INVALID_RC, - "No current known DataPlane interfaces can return this, please fix"); + if (zvrf) + zvrf->installs_queued++; break; case ZEBRA_DPLANE_REQUEST_FAILURE: - flog_err( - EC_ZEBRA_DP_INSTALL_FAIL, - "No current known Rib Install Failure cases, please fix"); + { + char str[SRCDEST2STR_BUFFER]; + + srcdest_rnode2str(rn, str, sizeof(str)); + flog_err(EC_ZEBRA_DP_INSTALL_FAIL, + "%u:%s: Failed to enqueue dataplane install", + re->vrf_id, str); break; + } case ZEBRA_DPLANE_REQUEST_SUCCESS: - zvrf->installs++; + if (zvrf) + zvrf->installs++; break; } @@ -1231,11 +1250,8 @@ void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re) { struct nexthop *nexthop; rib_table_info_t *info = srcdest_rnode_table_info(rn); - const struct prefix *p, *src_p; struct zebra_vrf *zvrf = vrf_info_lookup(re->vrf_id); - srcdest_rnode_prefixes(rn, &p, &src_p); - if (info->safi != SAFI_UNICAST) { for (ALL_NEXTHOPS(re->ng, nexthop)) UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); @@ -1244,20 +1260,25 @@ void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re) /* * Make sure we update the FPM any time we send new information to - * the kernel. + * the dataplane. */ hook_call(rib_update, rn, "uninstalling from kernel"); - switch (kernel_route_rib(rn, p, src_p, re, NULL)) { + + switch (dplane_route_delete(rn, re)) { case ZEBRA_DPLANE_REQUEST_QUEUED: - flog_err( - EC_ZEBRA_DP_INVALID_RC, - "No current known DataPlane interfaces can return this, please fix"); + if (zvrf) + zvrf->removals_queued++; break; case ZEBRA_DPLANE_REQUEST_FAILURE: - flog_err( - EC_ZEBRA_DP_INSTALL_FAIL, - "No current known RIB Install Failure cases, please fix"); + { + char str[SRCDEST2STR_BUFFER]; + + srcdest_rnode2str(rn, str, sizeof(str)); + flog_err(EC_ZEBRA_DP_INSTALL_FAIL, + "%u:%s: Failed to enqueue dataplane uninstall", + re->vrf_id, str); break; + } case ZEBRA_DPLANE_REQUEST_SUCCESS: if (zvrf) zvrf->removals++; @@ -1272,6 +1293,7 @@ static void rib_uninstall(struct route_node *rn, struct route_entry *re) { rib_table_info_t *info = srcdest_rnode_table_info(rn); rib_dest_t *dest = rib_dest_from_rnode(rn); + struct nexthop *nexthop; if (dest && dest->selected_fib == re) { if (info->safi == SAFI_UNICAST) @@ -1283,6 +1305,11 @@ static void rib_uninstall(struct route_node *rn, struct route_entry *re) if (!RIB_SYSTEM_ROUTE(re)) rib_uninstall_kernel(rn, re); + + dest->selected_fib = NULL; + + for (ALL_NEXTHOPS(re->ng, nexthop)) + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); } if (CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED)) { @@ -1926,16 +1953,18 @@ done: } /* - * TODO - WIP + * TODO - WIP version of route-update processing after async dataplane + * update. */ static void rib_process_after(dplane_ctx_h ctx) { struct route_table *table = NULL; + struct zebra_vrf *zvrf = NULL; struct route_node *rn = NULL; struct route_entry *re = NULL, *old_re = NULL, *rib; bool is_update = false; struct nexthop *nexthop; - char dest_str[PREFIX_STRLEN]; + char dest_str[PREFIX_STRLEN] = ""; dplane_op_e op; enum zebra_dplane_result status; const struct prefix *dest_pfx, *src_pfx; @@ -1957,6 +1986,8 @@ static void rib_process_after(dplane_ctx_h ctx) goto done; } + zvrf = vrf_info_lookup(dplane_ctx_get_vrf(ctx)); + dest_pfx = dplane_ctx_get_dest(ctx); /* Note well: only capturing the prefix string if debug is enabled here; @@ -1997,6 +2028,10 @@ static void rib_process_after(dplane_ctx_h ctx) */ if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) { zsend_route_notify_owner_ctx(ctx, ZAPI_ROUTE_REMOVED); + + if (zvrf) { + zvrf->removals++; + } } else { zsend_route_notify_owner_ctx(ctx, ZAPI_ROUTE_FAIL_INSTALL); @@ -2085,6 +2120,10 @@ static void rib_process_after(dplane_ctx_h ctx) UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); } + if (zvrf) { + zvrf->installs++; + } + /* Redistribute */ /* TODO -- still calling the redist api using the route_entries, * and there's a corner-case here: if there's no client diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index 7fef644fac..ef02ca63e5 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -133,6 +133,8 @@ struct zebra_vrf { /* Route Installs */ uint64_t installs; uint64_t removals; + uint64_t installs_queued; + uint64_t removals_queued; uint64_t neigh_updates; uint64_t lsp_installs; uint64_t lsp_removals; From ae2992851bcf4bf832d282bb1e8945de0da1974a Mon Sep 17 00:00:00 2001 From: Mark Stapp Date: Wed, 27 Jun 2018 16:51:32 -0400 Subject: [PATCH 05/22] zebra: capture old data for route delete Need to capture 'old' values in conversion from re to dplane context. Signed-off-by: Mark Stapp --- zebra/zebra_dplane.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 43b08641a9..7ec0b6e9cb 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -463,6 +463,7 @@ static int dplane_ctx_route_init(dplane_ctx_h ctx, ctx->zd_op = op; ctx->zd_type = re->type; + ctx->zd_old_type = re->type; /* Prefixes: dest, and optional source */ srcdest_rnode_prefixes(rn, &p, &src_p); @@ -483,6 +484,7 @@ static int dplane_ctx_route_init(dplane_ctx_h ctx, ctx->zd_nexthop_mtu = re->nexthop_mtu; ctx->zd_instance = re->instance; ctx->zd_tag = re->tag; + ctx->zd_old_tag = re->tag; ctx->zd_distance = re->distance; table = srcdest_rnode_table(rn); From 8cb41cd624536f435dacb4096d3d024e729baca1 Mon Sep 17 00:00:00 2001 From: Mark Stapp Date: Wed, 27 Jun 2018 17:13:26 -0400 Subject: [PATCH 06/22] zebra: set SELECTED flag in rib_process Set SELECTED re immediately in rib_process, without expecting that fib install has completed. Remove premature redistribute call also. Signed-off-by: Mark Stapp --- zebra/zebra_rib.c | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 784b92786a..bd13248f56 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -1839,22 +1839,12 @@ static void rib_process(struct route_node *rn) else if (old_fib) rib_process_del_fib(zvrf, rn, old_fib); - /* Redistribute SELECTED entry */ + /* Update SELECTED entry */ if (old_selected != new_selected || selected_changed) { - struct nexthop *nexthop = NULL; - /* Check if we have a FIB route for the destination, otherwise, - * don't redistribute it */ - if (new_fib) { - for (ALL_NEXTHOPS(new_fib->ng, nexthop)) { - if (CHECK_FLAG(nexthop->flags, - NEXTHOP_FLAG_FIB)) { - break; - } - } + if (new_selected) { + SET_FLAG(new_selected->flags, ZEBRA_FLAG_SELECTED); } - if (!nexthop) - new_selected = NULL; if (new_selected && new_selected != new_fib) { nexthop_active_update(rn, new_selected, 1); @@ -1868,14 +1858,6 @@ static void rib_process(struct route_node *rn) UNSET_FLAG(old_selected->flags, ZEBRA_FLAG_SELECTED); } - - if (new_selected) { - /* Install new or replace existing redistributed entry - */ - SET_FLAG(new_selected->flags, ZEBRA_FLAG_SELECTED); - redistribute_update(p, src_p, new_selected, - old_selected); - } } /* Remove all RE entries queued for removal */ From b8e0423d91609f098eb7bce3f628e0cda7ebdd05 Mon Sep 17 00:00:00 2001 From: Mark Stapp Date: Mon, 9 Jul 2018 16:00:09 -0400 Subject: [PATCH 07/22] zebra: start sketching dataplane provider api Initial WIP api to add providers into the zebra dataplane system, with some simple ordering/prioritization. Signed-off-by: Mark Stapp --- zebra/zebra_dplane.c | 92 ++++++++++++++++++++++++++++++++++++++++---- zebra/zebra_dplane.h | 1 + zebra/zebra_memory.h | 1 + 3 files changed, 86 insertions(+), 8 deletions(-) diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 7ec0b6e9cb..03cad8fb35 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -29,6 +29,7 @@ /* Memory type for context blocks */ DEFINE_MTYPE(ZEBRA, DP_CTX, "Zebra DPlane Ctx") +DEFINE_MTYPE(ZEBRA, DP_PROV, "Zebra DPlane Provider") #ifndef AOK # define AOK 0 @@ -104,6 +105,8 @@ struct zebra_dplane_ctx_s { /* Nexthops */ struct nexthop_group zd_ng; + /* TODO -- use fixed array of nexthops, to avoid mallocs? */ + /* Embedded list linkage */ TAILQ_ENTRY(zebra_dplane_ctx_s) zd_q_entries; @@ -125,10 +128,12 @@ struct zebra_dplane_provider_s { uint32_t dp_id; /* Event pointer for use by the dplane thread */ - struct thread *dp_t_event; +// struct thread *dp_t_event; + + dplane_provider_process_fp dp_fp; /* Embedded list linkage */ - TAILQ_ENTRY(zebra_dplane_dest_s) dp_q_providers; + TAILQ_ENTRY(zebra_dplane_provider_s) dp_q_providers; }; @@ -148,6 +153,9 @@ static struct zebra_dplane_globals_s { /* Ordered list of providers */ TAILQ_HEAD(zdg_prov_q, zebra_dplane_provider_s) dg_providers_q; + /* Counter used to assign ids to providers */ + uint32_t dg_provider_id; + /* Event-delivery context 'master' for the dplane */ struct thread_master *dg_master; @@ -171,12 +179,16 @@ static int dplane_route_process(struct thread *event); */ /* - * Allocate an opaque context block + * Allocate a dataplane update context */ dplane_ctx_h dplane_ctx_alloc(void) { struct zebra_dplane_ctx_s *p; + /* TODO -- just alloc'ing memory, but would like to maintain + * a pool + */ + p = XCALLOC(MTYPE_DP_CTX, sizeof(struct zebra_dplane_ctx_s)); if (p) { p->zd_magic = DPLANE_CTX_MAGIC; @@ -186,13 +198,17 @@ dplane_ctx_h dplane_ctx_alloc(void) } /* - * Free memory for a dataplane results context block. + * Free a dataplane results context. */ static void dplane_ctx_free(dplane_ctx_h *pctx) { if (pctx) { DPLANE_CTX_VALID(*pctx); + /* TODO -- just freeing memory, but would like to maintain + * a pool + */ + /* Free embedded nexthops */ if ((*pctx)->zd_ng.nexthop) { /* This deals with recursive nexthops too */ @@ -513,6 +529,8 @@ static int dplane_ctx_route_init(dplane_ctx_h ctx, /* Copy nexthops; recursive info is included too */ copy_nexthops(&(ctx->zd_ng.nexthop), re->ng.nexthop, NULL); + /* TODO -- maybe use array of nexthops to avoid allocs? */ + /* Trying out the sequence number idea, so we can try to detect * when a result is stale. */ @@ -595,7 +613,8 @@ dplane_route_update_internal(struct route_node *rn, /* Capture some extra info for update case * where there's a different 'old' route. */ - if ((op == DPLANE_OP_ROUTE_UPDATE) && old_re && (old_re != re)) { + if ((op == DPLANE_OP_ROUTE_UPDATE) && + old_re && (old_re != re)) { ctx->zd_is_update = true; old_re->dplane_sequence++; @@ -693,6 +712,8 @@ static int dplane_route_process(struct thread *event) break; } + /* TODO -- support series of providers */ + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { char dest_str[PREFIX_STRLEN]; @@ -704,12 +725,11 @@ static int dplane_route_process(struct thread *event) ctx, dplane_op2str(dplane_ctx_get_op(ctx))); } + /* Initially, just doing kernel-facing update here */ res = kernel_route_update(ctx); ctx->zd_status = res; - /* TODO -- support series of providers */ - /* Enqueue result to zebra main context */ (*zdplane_g.dg_results_cb)(ctx); @@ -719,6 +739,60 @@ static int dplane_route_process(struct thread *event) return (0); } +/* + * Provider registration + */ +int dplane_provider_register(const char *name, + dplane_provider_prio_e prio, + dplane_provider_process_fp fp) +{ + int ret = 0; + struct zebra_dplane_provider_s *p, *last; + + /* Validate */ + if (fp == NULL) { + ret = EINVAL; + goto done; + } + + if (prio <= DPLANE_PRIO_NONE || + prio >= DPLANE_PRIO_LAST) { + ret = EINVAL; + goto done; + } + + /* Allocate and init new provider struct */ + p = XCALLOC(MTYPE_DP_PROV, sizeof(struct zebra_dplane_provider_s)); + if (p == NULL) { + ret = ENOMEM; + goto done; + } + + strncpy(p->dp_name, name, DPLANE_PROVIDER_NAMELEN); + p->dp_name[DPLANE_PROVIDER_NAMELEN] = '\0'; + + p->dp_priority = prio; + p->dp_fp = fp; + + p->dp_id = ++zdplane_g.dg_provider_id; + + /* Insert into list ordered by priority */ + TAILQ_FOREACH(last, &zdplane_g.dg_providers_q, dp_q_providers) { + if (last->dp_priority > p->dp_priority) { + break; + } + } + + if (last) { + TAILQ_INSERT_BEFORE(last, p, dp_q_providers); + } else { + TAILQ_INSERT_TAIL(&zdplane_g.dg_providers_q, p, dp_q_providers); + } + +done: + return (ret); +} + /* * Zebra registers a results callback with the dataplane system */ @@ -740,6 +814,8 @@ static void zebra_dplane_init_internal(struct zebra_t *zebra) TAILQ_INIT(&zdplane_g.dg_route_ctx_q); TAILQ_INIT(&zdplane_g.dg_providers_q); + /* TODO -- register kernel 'provider' during init */ + /* TODO -- using zebra core event thread temporarily */ zdplane_g.dg_master = zebra->master; @@ -747,7 +823,7 @@ static void zebra_dplane_init_internal(struct zebra_t *zebra) } /* - * Initialize the dataplane module at startup. + * Initialize the dataplane module at startup; called by zebra rib_init() */ void zebra_dplane_init(void) { diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 364d77db43..fcc70e99bf 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -201,6 +201,7 @@ typedef enum { DPLANE_PRIO_PRE_KERNEL, DPLANE_PRIO_KERNEL, DPLANE_PRIO_POSTPROCESS, + DPLANE_PRIO_LAST } dplane_provider_prio_e; /* Provider's entry-point to process a context block */ diff --git a/zebra/zebra_memory.h b/zebra/zebra_memory.h index fcabab97c5..de55478de2 100644 --- a/zebra/zebra_memory.h +++ b/zebra/zebra_memory.h @@ -35,5 +35,6 @@ DECLARE_MTYPE(RIB_DEST) DECLARE_MTYPE(RIB_TABLE_INFO) DECLARE_MTYPE(RNH) DECLARE_MTYPE(DP_CTX) +DECLARE_MTYPE(DP_PROV) #endif /* _QUAGGA_ZEBRA_MEMORY_H */ From 5709131cec0a3ebd9739b287b8cd2bb5c421357f Mon Sep 17 00:00:00 2001 From: Mark Stapp Date: Wed, 11 Jul 2018 11:08:47 -0400 Subject: [PATCH 08/22] zebra: resolve style issues in dplane commit Resolve (most) style issues in the initial zebra dataplane commit branch. Signed-off-by: Mark Stapp --- zebra/debug.c | 1 + zebra/kernel_netlink.c | 2 +- zebra/redistribute.h | 5 +- zebra/rt_netlink.c | 168 +++++++++++++++++++---------------------- zebra/zebra_dplane.c | 150 ++++++++++++++++-------------------- zebra/zebra_dplane.h | 26 +++---- zebra/zebra_ns.h | 5 +- zebra/zebra_rib.c | 41 ++++------ 8 files changed, 175 insertions(+), 223 deletions(-) diff --git a/zebra/debug.c b/zebra/debug.c index 19582bb090..0eb06d7f25 100644 --- a/zebra/debug.c +++ b/zebra/debug.c @@ -275,6 +275,7 @@ DEFUN (debug_zebra_dplane, "Detailed debug information\n") { int idx = 0; + SET_FLAG(zebra_debug_dplane, ZEBRA_DEBUG_DPLANE); if (argv_find(argv, argc, "detailed", &idx)) diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index cdfbdd2303..08219924fb 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -1011,7 +1011,7 @@ int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), /* Capture info in intermediate info struct */ zebra_dplane_info_from_zns(&dp_info, zns, (nl == &(zns->netlink_cmd))); - return (netlink_talk_info(filter, n, &dp_info, startup)); + return netlink_talk_info(filter, n, &dp_info, startup); } /* Issue request message to kernel via netlink socket. GET messages diff --git a/zebra/redistribute.h b/zebra/redistribute.h index f67480da9c..f0dc79574c 100644 --- a/zebra/redistribute.h +++ b/zebra/redistribute.h @@ -38,10 +38,11 @@ extern void zebra_redistribute_default_delete(ZAPI_HANDLER_ARGS); extern void redistribute_update(const struct prefix *p, const struct prefix *src_p, - struct route_entry *, struct route_entry *); + struct route_entry *re, + struct route_entry *prev_re); extern void redistribute_delete(const struct prefix *p, const struct prefix *src_p, - struct route_entry *); + struct route_entry *re); extern void zebra_interface_up_update(struct interface *); extern void zebra_interface_down_update(struct interface *); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index bbaf89a7c0..e5fc27bcca 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1795,7 +1795,7 @@ static int netlink_route_multipath_ctx(int cmd, dplane_ctx_h ctx) family = PREFIX_FAMILY(p); - memset(&req, 0, sizeof req - NL_PKT_BUF_SIZE); + memset(&req, 0, sizeof(req) - NL_PKT_BUF_SIZE); bytelen = (family == AF_INET ? 4 : 16); @@ -1816,11 +1816,10 @@ static int netlink_route_multipath_ctx(int cmd, dplane_ctx_h ctx) req.r.rtm_src_len = src_p ? src_p->prefixlen : 0; req.r.rtm_scope = RT_SCOPE_UNIVERSE; - if (cmd == RTM_DELROUTE) { + if (cmd == RTM_DELROUTE) req.r.rtm_protocol = zebra2proto(dplane_ctx_get_old_type(ctx)); - } else { + else req.r.rtm_protocol = zebra2proto(dplane_ctx_get_type(ctx)); - } /* * blackhole routes are not RTN_UNICAST, they are @@ -1833,9 +1832,9 @@ static int netlink_route_multipath_ctx(int cmd, dplane_ctx_h ctx) if (cmd != RTM_DELROUTE) req.r.rtm_type = RTN_UNICAST; - addattr_l(&req.n, sizeof req, RTA_DST, &p->u.prefix, bytelen); + addattr_l(&req.n, sizeof(req), RTA_DST, &p->u.prefix, bytelen); if (src_p) - addattr_l(&req.n, sizeof req, RTA_SRC, &src_p->u.prefix, + addattr_l(&req.n, sizeof(req), RTA_SRC, &src_p->u.prefix, bytelen); /* Metric. */ @@ -1845,20 +1844,19 @@ static int netlink_route_multipath_ctx(int cmd, dplane_ctx_h ctx) * path(s) * by the routing protocol and for communicating with protocol peers. */ - addattr32(&req.n, sizeof req, RTA_PRIORITY, NL_DEFAULT_ROUTE_METRIC); + addattr32(&req.n, sizeof(req), RTA_PRIORITY, NL_DEFAULT_ROUTE_METRIC); #if defined(SUPPORT_REALMS) { route_tag_t tag; - if (cmd == RTM_DELROUTE) { + if (cmd == RTM_DELROUTE) tag = dplane_ctx_get_old_tag(ctx); - } else { + else tag = dplane_ctx_get_tag(ctx); - } if (tag > 0 && tag <= 255) - addattr32(&req.n, sizeof req, RTA_FLOW, tag); + addattr32(&req.n, sizeof(req), RTA_FLOW, tag); } #endif /* Table corresponding to this route. */ @@ -1867,7 +1865,7 @@ static int netlink_route_multipath_ctx(int cmd, dplane_ctx_h ctx) req.r.rtm_table = table_id; else { req.r.rtm_table = RT_TABLE_UNSPEC; - addattr32(&req.n, sizeof req, RTA_TABLE, table_id); + addattr32(&req.n, sizeof(req), RTA_TABLE, table_id); } _netlink_route_debug(cmd, p, family, dplane_ctx_get_vrf(ctx), table_id); @@ -1886,17 +1884,20 @@ static int netlink_route_multipath_ctx(int cmd, dplane_ctx_h ctx) struct rtattr *rta = (void *)buf; uint32_t mtu = dplane_ctx_get_mtu(ctx); uint32_t nexthop_mtu = dplane_ctx_get_nh_mtu(ctx); + if (!mtu || (nexthop_mtu && nexthop_mtu < mtu)) mtu = nexthop_mtu; rta->rta_type = RTA_METRICS; rta->rta_len = RTA_LENGTH(0); - rta_addattr_l(rta, NL_PKT_BUF_SIZE, RTAX_MTU, &mtu, sizeof mtu); + rta_addattr_l(rta, NL_PKT_BUF_SIZE, + RTAX_MTU, &mtu, sizeof(mtu)); addattr_l(&req.n, NL_PKT_BUF_SIZE, RTA_METRICS, RTA_DATA(rta), RTA_PAYLOAD(rta)); } /* Count overall nexthops so we can decide whether to use singlepath - * or multipath case. */ + * or multipath case. + */ nexthop_num = 0; for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) { if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) @@ -1934,43 +1935,36 @@ static int netlink_route_multipath_ctx(int cmd, dplane_ctx_h ctx) } if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) { - if (!setsrc) { - if (family == AF_INET) { - if (nexthop->rmap_src.ipv4 - .s_addr - != 0) { - src.ipv4 = - nexthop->rmap_src - .ipv4; - setsrc = 1; - } else if (nexthop->src.ipv4 - .s_addr - != 0) { - src.ipv4 = - nexthop->src - .ipv4; - setsrc = 1; - } - } else if (family == AF_INET6) { - if (!IN6_IS_ADDR_UNSPECIFIED( - &nexthop->rmap_src - .ipv6)) { - src.ipv6 = - nexthop->rmap_src - .ipv6; - setsrc = 1; - } else if ( - !IN6_IS_ADDR_UNSPECIFIED( - &nexthop->src - .ipv6)) { - src.ipv6 = - nexthop->src - .ipv6; - setsrc = 1; - } + + if (setsrc) + continue; + + if (family == AF_INET) { + if (nexthop->rmap_src.ipv4.s_addr + != 0) { + src.ipv4 = + nexthop->rmap_src.ipv4; + setsrc = 1; + } else if (nexthop->src.ipv4.s_addr + != 0) { + src.ipv4 = + nexthop->src.ipv4; + setsrc = 1; + } + } else if (family == AF_INET6) { + if (!IN6_IS_ADDR_UNSPECIFIED( + &nexthop->rmap_src.ipv6)) { + src.ipv6 = + nexthop->rmap_src.ipv6; + setsrc = 1; + } else if ( + !IN6_IS_ADDR_UNSPECIFIED( + &nexthop->src.ipv6)) { + src.ipv6 = + nexthop->src.ipv6; + setsrc = 1; } } - continue; } if ((cmd == RTM_NEWROUTE @@ -1981,17 +1975,17 @@ static int netlink_route_multipath_ctx(int cmd, dplane_ctx_h ctx) _netlink_route_build_singlepath( routedesc, bytelen, nexthop, &req.n, - &req.r, sizeof req, cmd); + &req.r, sizeof(req), cmd); nexthop_num++; break; } } if (setsrc && (cmd == RTM_NEWROUTE)) { if (family == AF_INET) - addattr_l(&req.n, sizeof req, RTA_PREFSRC, + addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &src.ipv4, bytelen); else if (family == AF_INET6) - addattr_l(&req.n, sizeof req, RTA_PREFSRC, + addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &src.ipv6, bytelen); } } else { /* Multipath case */ @@ -2012,43 +2006,35 @@ static int netlink_route_multipath_ctx(int cmd, dplane_ctx_h ctx) if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) { /* This only works for IPv4 now */ - if (!setsrc) { - if (family == AF_INET) { - if (nexthop->rmap_src.ipv4 - .s_addr - != 0) { - src.ipv4 = - nexthop->rmap_src - .ipv4; - setsrc = 1; - } else if (nexthop->src.ipv4 - .s_addr - != 0) { - src.ipv4 = - nexthop->src - .ipv4; - setsrc = 1; - } - } else if (family == AF_INET6) { - if (!IN6_IS_ADDR_UNSPECIFIED( - &nexthop->rmap_src - .ipv6)) { - src.ipv6 = - nexthop->rmap_src - .ipv6; - setsrc = 1; - } else if ( - !IN6_IS_ADDR_UNSPECIFIED( - &nexthop->src - .ipv6)) { - src.ipv6 = - nexthop->src - .ipv6; - setsrc = 1; - } + if (setsrc) + continue; + + if (family == AF_INET) { + if (nexthop->rmap_src.ipv4.s_addr + != 0) { + src.ipv4 = + nexthop->rmap_src.ipv4; + setsrc = 1; + } else if (nexthop->src.ipv4.s_addr + != 0) { + src.ipv4 = + nexthop->src.ipv4; + setsrc = 1; + } + } else if (family == AF_INET6) { + if (!IN6_IS_ADDR_UNSPECIFIED( + &nexthop->rmap_src.ipv6)) { + src.ipv6 = + nexthop->rmap_src.ipv6; + setsrc = 1; + } else if ( + !IN6_IS_ADDR_UNSPECIFIED( + &nexthop->src.ipv6)) { + src.ipv6 = + nexthop->src.ipv6; + setsrc = 1; } } - continue; } if ((cmd == RTM_NEWROUTE @@ -2075,10 +2061,10 @@ static int netlink_route_multipath_ctx(int cmd, dplane_ctx_h ctx) } if (setsrc && (cmd == RTM_NEWROUTE)) { if (family == AF_INET) - addattr_l(&req.n, sizeof req, RTA_PREFSRC, + addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &src.ipv4, bytelen); else if (family == AF_INET6) - addattr_l(&req.n, sizeof req, RTA_PREFSRC, + addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &src.ipv6, bytelen); if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("Setting source"); @@ -2100,7 +2086,7 @@ static int netlink_route_multipath_ctx(int cmd, dplane_ctx_h ctx) skip: /* Destination netlink address. */ - memset(&snl, 0, sizeof snl); + memset(&snl, 0, sizeof(snl)); snl.nl_family = AF_NETLINK; /* Talk to netlink socket. */ @@ -2242,7 +2228,7 @@ enum zebra_dplane_result kernel_route_update(dplane_ctx_h ctx) * of the route delete. If that happens yeah we're * screwed. */ - ret = netlink_route_multipath_ctx(RTM_DELROUTE, ctx); + (void )netlink_route_multipath_ctx(RTM_DELROUTE, ctx); cmd = RTM_NEWROUTE; } diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 03cad8fb35..8fec9fbce7 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -43,13 +43,12 @@ const uint32_t DPLANE_CTX_MAGIC = 0xb97a557f; #ifdef DPLANE_DEBUG -# define DPLANE_CTX_VALID(p) \ - (assert((p) && ((p)->zd_magic == DPLANE_CTX_MAGIC))) +# define DPLANE_CTX_VALID(p) \ + assert((p) && ((p)->zd_magic == DPLANE_CTX_MAGIC)) #else -# define DPLANE_CTX_VALID(p) \ - if ((p) && ((p)->zd_magic == DPLANE_CTX_MAGIC)) { ; } +# define DPLANE_CTX_VALID(p) #endif /* DPLANE_DEBUG */ @@ -61,7 +60,7 @@ const uint32_t DPLANE_CTX_MAGIC = 0xb97a557f; struct zebra_dplane_ctx_s { /* Operation code */ - dplane_op_e zd_op; + enum dplane_op_e zd_op; /* Status on return */ enum zebra_dplane_result zd_status; @@ -127,9 +126,6 @@ struct zebra_dplane_provider_s { /* Id value */ uint32_t dp_id; - /* Event pointer for use by the dplane thread */ -// struct thread *dp_t_event; - dplane_provider_process_fp dp_fp; /* Embedded list linkage */ @@ -181,20 +177,18 @@ static int dplane_route_process(struct thread *event); /* * Allocate a dataplane update context */ -dplane_ctx_h dplane_ctx_alloc(void) +static dplane_ctx_h dplane_ctx_alloc(void) { struct zebra_dplane_ctx_s *p; /* TODO -- just alloc'ing memory, but would like to maintain * a pool */ - p = XCALLOC(MTYPE_DP_CTX, sizeof(struct zebra_dplane_ctx_s)); - if (p) { + if (p) p->zd_magic = DPLANE_CTX_MAGIC; - } - return (p); + return p; } /* @@ -242,9 +236,9 @@ void dplane_ctx_enqueue_tail(struct dplane_ctx_q_s *q, dplane_ctx_h ctx) void dplane_ctx_dequeue(struct dplane_ctx_q_s *q, dplane_ctx_h *ctxp) { dplane_ctx_h ctx = TAILQ_FIRST(q); - if (ctx) { + + if (ctx) TAILQ_REMOVE(q, ctx, zd_q_entries); - } *ctxp = ctx; } @@ -256,21 +250,21 @@ enum zebra_dplane_result dplane_ctx_get_status(const dplane_ctx_h ctx) { DPLANE_CTX_VALID(ctx); - return (ctx->zd_status); + return ctx->zd_status; } -dplane_op_e dplane_ctx_get_op(const dplane_ctx_h ctx) +enum dplane_op_e dplane_ctx_get_op(const dplane_ctx_h ctx) { DPLANE_CTX_VALID(ctx); - return (ctx->zd_op); + return ctx->zd_op; } -const char *dplane_op2str(dplane_op_e op) +const char *dplane_op2str(enum dplane_op_e op) { const char *ret = "UNKNOWN"; - switch(op) { + switch (op) { case DPLANE_OP_NONE: ret = "NONE"; break; @@ -288,28 +282,26 @@ const char *dplane_op2str(dplane_op_e op) }; - return (ret); + return ret; } const struct prefix *dplane_ctx_get_dest(const dplane_ctx_h ctx) { DPLANE_CTX_VALID(ctx); - return (&(ctx->zd_dest)); + return &(ctx->zd_dest); } -/* Source prefix is a little special - use convention like prefix-len of zero - * and all-zeroes address means "no src prefix"? or ... return NULL in that case? - */ +/* Source prefix is a little special - return NULL for "no src prefix" */ const struct prefix *dplane_ctx_get_src(const dplane_ctx_h ctx) { DPLANE_CTX_VALID(ctx); if (ctx->zd_src.prefixlen == 0 && IN6_IS_ADDR_UNSPECIFIED(&(ctx->zd_src.u.prefix6))) { - return (NULL); + return NULL; } else { - return (&(ctx->zd_src)); + return &(ctx->zd_src); } } @@ -317,140 +309,140 @@ bool dplane_ctx_is_update(const dplane_ctx_h ctx) { DPLANE_CTX_VALID(ctx); - return (ctx->zd_is_update); + return ctx->zd_is_update; } uint32_t dplane_ctx_get_seq(const dplane_ctx_h ctx) { DPLANE_CTX_VALID(ctx); - return (ctx->zd_seq); + return ctx->zd_seq; } uint32_t dplane_ctx_get_old_seq(const dplane_ctx_h ctx) { DPLANE_CTX_VALID(ctx); - return (ctx->zd_old_seq); + return ctx->zd_old_seq; } vrf_id_t dplane_ctx_get_vrf(const dplane_ctx_h ctx) { DPLANE_CTX_VALID(ctx); - return (ctx->zd_vrf_id); + return ctx->zd_vrf_id; } int dplane_ctx_get_type(const dplane_ctx_h ctx) { DPLANE_CTX_VALID(ctx); - return (ctx->zd_type); + return ctx->zd_type; } int dplane_ctx_get_old_type(const dplane_ctx_h ctx) { DPLANE_CTX_VALID(ctx); - return (ctx->zd_old_type); + return ctx->zd_old_type; } afi_t dplane_ctx_get_afi(const dplane_ctx_h ctx) { DPLANE_CTX_VALID(ctx); - return (ctx->zd_afi); + return ctx->zd_afi; } safi_t dplane_ctx_get_safi(const dplane_ctx_h ctx) { DPLANE_CTX_VALID(ctx); - return (ctx->zd_safi); + return ctx->zd_safi; } uint32_t dplane_ctx_get_table(const dplane_ctx_h ctx) { DPLANE_CTX_VALID(ctx); - return (ctx->zd_table_id); + return ctx->zd_table_id; } route_tag_t dplane_ctx_get_tag(const dplane_ctx_h ctx) { DPLANE_CTX_VALID(ctx); - return (ctx->zd_tag); + return ctx->zd_tag; } route_tag_t dplane_ctx_get_old_tag(const dplane_ctx_h ctx) { DPLANE_CTX_VALID(ctx); - return (ctx->zd_old_tag); + return ctx->zd_old_tag; } uint16_t dplane_ctx_get_instance(const dplane_ctx_h ctx) { DPLANE_CTX_VALID(ctx); - return (ctx->zd_instance); + return ctx->zd_instance; } uint16_t dplane_ctx_get_old_instance(const dplane_ctx_h ctx) { DPLANE_CTX_VALID(ctx); - return (ctx->zd_instance); + return ctx->zd_instance; } uint32_t dplane_ctx_get_metric(const dplane_ctx_h ctx) { DPLANE_CTX_VALID(ctx); - return (ctx->zd_metric); + return ctx->zd_metric; } uint32_t dplane_ctx_get_mtu(const dplane_ctx_h ctx) { DPLANE_CTX_VALID(ctx); - return (ctx->zd_mtu); + return ctx->zd_mtu; } uint32_t dplane_ctx_get_nh_mtu(const dplane_ctx_h ctx) { DPLANE_CTX_VALID(ctx); - return (ctx->zd_nexthop_mtu); + return ctx->zd_nexthop_mtu; } uint8_t dplane_ctx_get_distance(const dplane_ctx_h ctx) { DPLANE_CTX_VALID(ctx); - return (ctx->zd_distance); + return ctx->zd_distance; } uint8_t dplane_ctx_get_old_distance(const dplane_ctx_h ctx) { DPLANE_CTX_VALID(ctx); - return (ctx->zd_old_distance); + return ctx->zd_old_distance; } const struct nexthop_group *dplane_ctx_get_ng(const dplane_ctx_h ctx) { DPLANE_CTX_VALID(ctx); - return (&(ctx->zd_ng)); + return &(ctx->zd_ng); } const struct zebra_dplane_info *dplane_ctx_get_ns(const dplane_ctx_h ctx) { DPLANE_CTX_VALID(ctx); - return (&(ctx->zd_ns_info)); + return &(ctx->zd_ns_info); } /* @@ -461,7 +453,7 @@ const struct zebra_dplane_info *dplane_ctx_get_ns(const dplane_ctx_h ctx) * Initialize a context block for a route update from zebra data structs. */ static int dplane_ctx_route_init(dplane_ctx_h ctx, - dplane_op_e op, + enum dplane_op_e op, struct route_node *rn, struct route_entry *re) { @@ -472,9 +464,8 @@ static int dplane_ctx_route_init(dplane_ctx_h ctx, struct zebra_ns *zns; struct zebra_vrf *zvrf; - if (!ctx || !rn || !re) { + if (!ctx || !rn || !re) goto done; - } ctx->zd_op = op; @@ -486,11 +477,10 @@ static int dplane_ctx_route_init(dplane_ctx_h ctx, prefix_copy(&(ctx->zd_dest), p); - if (src_p) { + if (src_p) prefix_copy(&(ctx->zd_src), src_p); - } else { + else memset(&(ctx->zd_src), 0, sizeof(ctx->zd_src)); - } ctx->zd_table_id = re->table; @@ -519,11 +509,10 @@ static int dplane_ctx_route_init(dplane_ctx_h ctx, /* Increment message counter after copying to context struct - may need * two messages in some 'update' cases. */ - if (op == DPLANE_OP_ROUTE_UPDATE) { + if (op == DPLANE_OP_ROUTE_UPDATE) zns->netlink_cmd.seq += 2; - } else { + else zns->netlink_cmd.seq++; - } #endif /* NETLINK*/ /* Copy nexthops; recursive info is included too */ @@ -564,7 +553,7 @@ static int dplane_route_enqueue(dplane_ctx_h ctx) ret = AOK; - return (ret); + return ret; } /* @@ -584,7 +573,7 @@ static dplane_ctx_h dplane_route_dequeue(void) } DPLANE_UNLOCK(); - return (ctx); + return ctx; } /* @@ -594,7 +583,7 @@ static enum zebra_dplane_result dplane_route_update_internal(struct route_node *rn, struct route_entry *re, struct route_entry *old_re, - dplane_op_e op) + enum dplane_op_e op) { enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; int ret = EINVAL; @@ -631,13 +620,12 @@ dplane_route_update_internal(struct route_node *rn, } done: - if (ret == AOK) { + if (ret == AOK) result = ZEBRA_DPLANE_REQUEST_QUEUED; - } else if (ctx) { + else if (ctx) dplane_ctx_free(&ctx); - } - return (result); + return result; } /* @@ -648,15 +636,14 @@ enum zebra_dplane_result dplane_route_add(struct route_node *rn, { enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE; - if (rn == NULL || re == NULL) { + if (rn == NULL || re == NULL) goto done; - } ret = dplane_route_update_internal(rn, re, NULL, DPLANE_OP_ROUTE_INSTALL); done: - return (ret); + return ret; } /* @@ -668,14 +655,13 @@ enum zebra_dplane_result dplane_route_update(struct route_node *rn, { enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE; - if (rn == NULL || re == NULL) { + if (rn == NULL || re == NULL) goto done; - } ret = dplane_route_update_internal(rn, re, old_re, DPLANE_OP_ROUTE_UPDATE); done: - return (ret); + return ret; } /* @@ -686,15 +672,14 @@ enum zebra_dplane_result dplane_route_delete(struct route_node *rn, { enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE; - if (rn == NULL || re == NULL) { + if (rn == NULL || re == NULL) goto done; - } ret = dplane_route_update_internal(rn, re, NULL, DPLANE_OP_ROUTE_DELETE); done: - return (ret); + return ret; } /* @@ -708,9 +693,8 @@ static int dplane_route_process(struct thread *event) while (1) { /* TODO -- limit number of updates per cycle? */ ctx = dplane_route_dequeue(); - if (ctx == NULL) { + if (ctx == NULL) break; - } /* TODO -- support series of providers */ @@ -736,14 +720,14 @@ static int dplane_route_process(struct thread *event) ctx = NULL; } - return (0); + return 0; } /* * Provider registration */ int dplane_provider_register(const char *name, - dplane_provider_prio_e prio, + enum dplane_provider_prio_e prio, dplane_provider_process_fp fp) { int ret = 0; @@ -778,19 +762,17 @@ int dplane_provider_register(const char *name, /* Insert into list ordered by priority */ TAILQ_FOREACH(last, &zdplane_g.dg_providers_q, dp_q_providers) { - if (last->dp_priority > p->dp_priority) { + if (last->dp_priority > p->dp_priority) break; - } } - if (last) { + if (last) TAILQ_INSERT_BEFORE(last, p, dp_q_providers); - } else { + else TAILQ_INSERT_TAIL(&zdplane_g.dg_providers_q, p, dp_q_providers); - } done: - return (ret); + return ret; } /* @@ -799,7 +781,7 @@ done: int dplane_results_register(dplane_results_fp fp) { zdplane_g.dg_results_cb = fp; - return (AOK); + return AOK; } /* @@ -818,8 +800,6 @@ static void zebra_dplane_init_internal(struct zebra_t *zebra) /* TODO -- using zebra core event thread temporarily */ zdplane_g.dg_master = zebra->master; - - return; } /* diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index fcc70e99bf..4cd75961a3 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -94,7 +94,7 @@ enum zebra_dplane_result { /* * Enqueue a route install or update for the dataplane. */ -typedef enum { +enum dplane_op_e { DPLANE_OP_NONE = 0, /* Route update */ @@ -102,14 +102,14 @@ typedef enum { DPLANE_OP_ROUTE_UPDATE, DPLANE_OP_ROUTE_DELETE, -} dplane_op_e; +}; /* * Opaque context block used to exchange info between the main zebra * context and the dataplane module(s). If these are two independent pthreads, * they cannot share existing global data structures safely. */ -typedef struct zebra_dplane_ctx_s * dplane_ctx_h; +typedef struct zebra_dplane_ctx_s *dplane_ctx_h; /* Define a tailq list type for context blocks. The list is exposed/public, * but the internal linkage in the context struct is private, so there @@ -117,11 +117,6 @@ typedef struct zebra_dplane_ctx_s * dplane_ctx_h; */ TAILQ_HEAD(dplane_ctx_q_s, zebra_dplane_ctx_s); -/* - * Allocate an opaque context block, currently for a route update. - */ -dplane_ctx_h dplane_ctx_alloc(void); - /* Return a dataplane results context block after use; the caller's pointer will * be cleared. */ @@ -140,13 +135,13 @@ void dplane_ctx_dequeue(struct dplane_ctx_q_s *q, dplane_ctx_h *ctxp); */ enum zebra_dplane_result dplane_ctx_get_status(const dplane_ctx_h ctx); -dplane_op_e dplane_ctx_get_op(const dplane_ctx_h ctx); -const char *dplane_op2str(dplane_op_e op); +enum dplane_op_e dplane_ctx_get_op(const dplane_ctx_h ctx); +const char *dplane_op2str(enum dplane_op_e op); const struct prefix *dplane_ctx_get_dest(const dplane_ctx_h ctx); -/* Source prefix is a little special - use convention like prefix-len of zero - * and all-zeroes address means "no src prefix"? or ... return NULL in that case? +/* Source prefix is a little special - use convention to return NULL + * to mean "no src prefix" */ const struct prefix *dplane_ctx_get_src(const dplane_ctx_h ctx); @@ -186,7 +181,6 @@ enum zebra_dplane_result dplane_route_delete(struct route_node *rn, struct route_entry *re); /* Opaque handle to a dataplane provider plugin */ -typedef struct zebra_dplane_provider_s *dplane_provider_h; #define DPLANE_PROVIDER_NAMELEN 64 @@ -195,21 +189,21 @@ typedef struct zebra_dplane_provider_s *dplane_provider_h; * followed by the kernel, followed by some post-processing step (such as * the fpm output stream.) */ -typedef enum { +enum dplane_provider_prio_e { DPLANE_PRIO_NONE = 0, DPLANE_PRIO_PREPROCESS, DPLANE_PRIO_PRE_KERNEL, DPLANE_PRIO_KERNEL, DPLANE_PRIO_POSTPROCESS, DPLANE_PRIO_LAST -} dplane_provider_prio_e; +}; /* Provider's entry-point to process a context block */ typedef int (*dplane_provider_process_fp)(dplane_ctx_h ctx); /* Provider registration */ int dplane_provider_register(const char *name, - dplane_provider_prio_e prio, + enum dplane_provider_prio_e prio, dplane_provider_process_fp fp); /* diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index ca909ef413..037c7322b7 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -114,11 +114,10 @@ static inline void zebra_ns_info_from_ns(struct zebra_ns_info *zns_info, */ zns_info->is_cmd = is_cmd; zns_info->nl_cmd_pid = zns->netlink_cmd.snl.nl_pid; - if (is_cmd) { + if (is_cmd) zns_info->nls = zns->netlink_cmd; - } else { + else zns_info->nls = zns->netlink; - } #endif /* NETLINK */ } diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index bd13248f56..496e0f10b7 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -1215,11 +1215,10 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re, hook_call(rib_update, rn, "installing in kernel"); /* Send add or update */ - if (old && (old != re)) { + if (old && (old != re)) ret = dplane_route_update(rn, re, old); - } else { + else ret = dplane_route_add(rn, re); - } switch (ret) { case ZEBRA_DPLANE_REQUEST_QUEUED: @@ -1842,9 +1841,8 @@ static void rib_process(struct route_node *rn) /* Update SELECTED entry */ if (old_selected != new_selected || selected_changed) { - if (new_selected) { + if (new_selected) SET_FLAG(new_selected->flags, ZEBRA_FLAG_SELECTED); - } if (new_selected && new_selected != new_fib) { nexthop_active_update(rn, new_selected, 1); @@ -1947,7 +1945,7 @@ static void rib_process_after(dplane_ctx_h ctx) bool is_update = false; struct nexthop *nexthop; char dest_str[PREFIX_STRLEN] = ""; - dplane_op_e op; + enum dplane_op_e op; enum zebra_dplane_result status; const struct prefix *dest_pfx, *src_pfx; @@ -1975,13 +1973,12 @@ static void rib_process_after(dplane_ctx_h ctx) /* Note well: only capturing the prefix string if debug is enabled here; * unconditional log messages will have to generate the string. */ - if (IS_ZEBRA_DEBUG_DPLANE) { + if (IS_ZEBRA_DEBUG_DPLANE) prefix2str(dest_pfx, dest_str, sizeof(dest_str)); - } src_pfx = dplane_ctx_get_src(ctx); rn = srcdest_rnode_get(table, dplane_ctx_get_dest(ctx), - src_pfx ? (struct prefix_ipv6 * )src_pfx : NULL); + src_pfx ? (struct prefix_ipv6 *)src_pfx : NULL); if (rn == NULL) { if (IS_ZEBRA_DEBUG_DPLANE) { zlog_debug("Failed to process dplane results: no " @@ -2011,9 +2008,8 @@ static void rib_process_after(dplane_ctx_h ctx) if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) { zsend_route_notify_owner_ctx(ctx, ZAPI_ROUTE_REMOVED); - if (zvrf) { + if (zvrf) zvrf->removals++; - } } else { zsend_route_notify_owner_ctx(ctx, ZAPI_ROUTE_FAIL_INSTALL); @@ -2041,22 +2037,19 @@ static void rib_process_after(dplane_ctx_h ctx) RNODE_FOREACH_RE(rn, rib) { if (re == NULL) { - if (rib_route_match_ctx(rib, ctx, false)) { + if (rib_route_match_ctx(rib, ctx, false)) re = rib; - } } /* Check for old route match */ if (is_update && (old_re == NULL)) { - if (rib_route_match_ctx(rib, ctx, true /*is_update*/)) { + if (rib_route_match_ctx(rib, ctx, true /*is_update*/)) old_re = rib; - } } /* Have we found the routes we need to work on? */ - if (re && ((!is_update || old_re))) { + if (re && ((!is_update || old_re))) break; - } } /* @@ -2102,9 +2095,8 @@ static void rib_process_after(dplane_ctx_h ctx) UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); } - if (zvrf) { + if (zvrf) zvrf->installs++; - } /* Redistribute */ /* TODO -- still calling the redist api using the route_entries, @@ -3295,15 +3287,14 @@ static int rib_process_dplane_results(struct thread *thread) } pthread_mutex_unlock(&dplane_mutex); - if (ctx) { + if (ctx) rib_process_after(ctx); - } else { + else break; - } - } while(1); + } while (1); - return (0); + return 0; } /* @@ -3325,7 +3316,7 @@ static int rib_dplane_results(dplane_ctx_h ctx) thread_add_event(zebrad.master, rib_process_dplane_results, NULL, 0, &t_dplane); - return (0); + return 0; } /* Routing information base initialize. */ From 5af4b34689ab774dc9c51eab717b57a036b2daca Mon Sep 17 00:00:00 2001 From: Mark Stapp Date: Thu, 19 Jul 2018 14:55:02 -0400 Subject: [PATCH 09/22] zebra: ensure redist of system routes We need a bit of special handling for system routes, which need to be offered for redistribution even though they won't be passing through the dplane system. Signed-off-by: Mark Stapp --- zebra/zebra_rib.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 496e0f10b7..87bf081a5d 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -1841,14 +1841,23 @@ static void rib_process(struct route_node *rn) /* Update SELECTED entry */ if (old_selected != new_selected || selected_changed) { - if (new_selected) - SET_FLAG(new_selected->flags, ZEBRA_FLAG_SELECTED); - if (new_selected && new_selected != new_fib) { nexthop_active_update(rn, new_selected, 1); UNSET_FLAG(new_selected->status, ROUTE_ENTRY_CHANGED); } + if (new_selected) { + SET_FLAG(new_selected->flags, ZEBRA_FLAG_SELECTED); + + /* Special case: new route is system route, so + * dataplane update will not be done - ensure we + * redistribute the route. + */ + if (RIB_SYSTEM_ROUTE(new_selected)) + redistribute_update(p, src_p, new_selected, + old_selected); + } + if (old_selected) { if (!new_selected) redistribute_delete(p, src_p, old_selected); From fe2c53d4ea87c63762547fdee42ae49e1577a681 Mon Sep 17 00:00:00 2001 From: Mark Stapp Date: Fri, 20 Jul 2018 14:43:32 -0400 Subject: [PATCH 10/22] zebra: Fix style issues Clean up a couple of checkstyle reports in the dataplane commit. Signed-off-by: Mark Stapp --- zebra/rt_netlink.c | 2 +- zebra/zebra_dplane.h | 3 +-- zebra/zebra_rib.c | 9 +++------ 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index e5fc27bcca..ab1d80f900 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -2228,7 +2228,7 @@ enum zebra_dplane_result kernel_route_update(dplane_ctx_h ctx) * of the route delete. If that happens yeah we're * screwed. */ - (void )netlink_route_multipath_ctx(RTM_DELROUTE, ctx); + (void)netlink_route_multipath_ctx(RTM_DELROUTE, ctx); cmd = RTM_NEWROUTE; } diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 4cd75961a3..2bcc85c36b 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -180,8 +180,7 @@ enum zebra_dplane_result dplane_route_update(struct route_node *rn, enum zebra_dplane_result dplane_route_delete(struct route_node *rn, struct route_entry *re); -/* Opaque handle to a dataplane provider plugin */ - +/* Support string name for a dataplane provider */ #define DPLANE_PROVIDER_NAMELEN 64 /* Priority or ordering values for providers. The idea is that there may be diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 87bf081a5d..b89d285118 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -1819,8 +1819,7 @@ static void rib_process(struct route_node *rn) if (IS_ZEBRA_DEBUG_RIB_DETAILED) { zlog_debug( - "%u:%s: After processing: old_selected %p " - "new_selected %p old_fib %p new_fib %p", + "%u:%s: After processing: old_selected %p new_selected %p old_fib %p new_fib %p", vrf_id, buf, (void *)old_selected, (void *)new_selected, (void *)old_fib, (void *)new_fib); } @@ -1966,8 +1965,7 @@ static void rib_process_after(dplane_ctx_h ctx) dplane_ctx_get_table(ctx)); if (table == NULL) { if (IS_ZEBRA_DEBUG_DPLANE) { - zlog_debug("Failed to process dplane results: no table " - "for afi %d, safi %d, vrf %u", + zlog_debug("Failed to process dplane results: no table for afi %d, safi %d, vrf %u", dplane_ctx_get_afi(ctx), dplane_ctx_get_safi(ctx), dplane_ctx_get_vrf(ctx)); @@ -1990,8 +1988,7 @@ static void rib_process_after(dplane_ctx_h ctx) src_pfx ? (struct prefix_ipv6 *)src_pfx : NULL); if (rn == NULL) { if (IS_ZEBRA_DEBUG_DPLANE) { - zlog_debug("Failed to process dplane results: no " - "route for %u:%s", + zlog_debug("Failed to process dplane results: no route for %u:%s", dplane_ctx_get_vrf(ctx), dest_str); } goto done; From 1bcea841b12f27b694da9b25c17c5a2d286404c3 Mon Sep 17 00:00:00 2001 From: Mark Stapp Date: Mon, 6 Aug 2018 16:19:45 -0400 Subject: [PATCH 11/22] zebra: netlink fuzzing path correction Correct use of netlink_parse_info() in the netlink fuzzing path. Also clarify a couple of comments about pthreads. Signed-off-by: Mark Stapp --- zebra/kernel_netlink.c | 8 +++++++- zebra/zebra_dplane.c | 6 ++++-- zebra/zebra_rib.c | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 08219924fb..0a58c2e262 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -314,11 +314,17 @@ bool netlink_read; */ void netlink_read_init(const char *fname) { + struct zebra_dplane_info dp_info; + snprintf(netlink_fuzz_file, MAXPATHLEN, "%s", fname); /* Creating this fake socket for testing purposes */ struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); - netlink_parse_info(netlink_information_fetch, &zns->netlink, zns, 1, 0); + /* Capture key info from zns struct */ + zebra_dplane_info_from_zns(&dp_info, zns, false); + + netlink_parse_info(netlink_information_fetch, &zns->netlink, + &dp_info, 1, 0); } /** diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 8fec9fbce7..7bc623fdf0 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -740,7 +740,7 @@ int dplane_provider_register(const char *name, } if (prio <= DPLANE_PRIO_NONE || - prio >= DPLANE_PRIO_LAST) { + prio > DPLANE_PRIO_LAST) { ret = EINVAL; goto done; } @@ -798,7 +798,9 @@ static void zebra_dplane_init_internal(struct zebra_t *zebra) /* TODO -- register kernel 'provider' during init */ - /* TODO -- using zebra core event thread temporarily */ + /* TODO -- start dataplane pthread. We're using the zebra + * core/main thread temporarily + */ zdplane_g.dg_master = zebra->master; } diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index b89d285118..964885f47e 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -3305,7 +3305,7 @@ static int rib_process_dplane_results(struct thread *thread) /* * Results are returned from the dataplane subsystem, in the context of - * the dataplane thread. We enqueue the results here for processing by + * the dataplane pthread. We enqueue the results here for processing by * the main thread later. */ static int rib_dplane_results(dplane_ctx_h ctx) From 18c37974253c7406f4aab3a16c45b11e0be2f832 Mon Sep 17 00:00:00 2001 From: Mark Stapp Date: Tue, 7 Aug 2018 16:00:33 -0400 Subject: [PATCH 12/22] zebra: add shutdown callback for dplane providers Support fini or shutdown callback for dplane modules, to give them an opportunity to cleanup. Signed-off-by: Mark Stapp --- zebra/zebra_dplane.c | 30 +++++++++++++++++++++++++++--- zebra/zebra_dplane.h | 6 +++++- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 7bc623fdf0..5e3aa6a4e9 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -18,6 +18,7 @@ */ #include "lib/zebra.h" +#include "lib/libfrr.h" #include "lib/memory.h" #include "lib/frr_pthread.h" #include "lib/queue.h" @@ -128,6 +129,8 @@ struct zebra_dplane_provider_s { dplane_provider_process_fp dp_fp; + dplane_provider_fini_fp dp_fini; + /* Embedded list linkage */ TAILQ_ENTRY(zebra_dplane_provider_s) dp_q_providers; @@ -728,7 +731,8 @@ static int dplane_route_process(struct thread *event) */ int dplane_provider_register(const char *name, enum dplane_provider_prio_e prio, - dplane_provider_process_fp fp) + dplane_provider_process_fp fp, + dplane_provider_fini_fp fini_fp) { int ret = 0; struct zebra_dplane_provider_s *p, *last; @@ -753,10 +757,14 @@ int dplane_provider_register(const char *name, } strncpy(p->dp_name, name, DPLANE_PROVIDER_NAMELEN); - p->dp_name[DPLANE_PROVIDER_NAMELEN] = '\0'; + p->dp_name[DPLANE_PROVIDER_NAMELEN] = '\0'; /* Belt-and-suspenders */ p->dp_priority = prio; p->dp_fp = fp; + p->dp_fini = fini_fp; + + /* Lock the lock - the dplane pthread may be running */ + DPLANE_LOCK(); p->dp_id = ++zdplane_g.dg_provider_id; @@ -771,6 +779,9 @@ int dplane_provider_register(const char *name, else TAILQ_INSERT_TAIL(&zdplane_g.dg_providers_q, p, dp_q_providers); + /* And unlock */ + DPLANE_UNLOCK(); + done: return ret; } @@ -796,7 +807,7 @@ static void zebra_dplane_init_internal(struct zebra_t *zebra) TAILQ_INIT(&zdplane_g.dg_route_ctx_q); TAILQ_INIT(&zdplane_g.dg_providers_q); - /* TODO -- register kernel 'provider' during init */ + /* TODO -- register default kernel 'provider' during init */ /* TODO -- start dataplane pthread. We're using the zebra * core/main thread temporarily @@ -804,10 +815,23 @@ static void zebra_dplane_init_internal(struct zebra_t *zebra) zdplane_g.dg_master = zebra->master; } +/* + * Shutdown, de-init hook callback. This runs pretty early during shutdown. + */ +static int zebra_dplane_fini(void) +{ + /* TODO -- stop thread, clean queues */ + + return 0; +} + /* * Initialize the dataplane module at startup; called by zebra rib_init() */ void zebra_dplane_init(void) { zebra_dplane_init_internal(&zebrad); + + /* Register for shutdown/de-init */ + hook_register(frr_early_fini, zebra_dplane_fini); } diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 2bcc85c36b..49c98879fe 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -200,10 +200,14 @@ enum dplane_provider_prio_e { /* Provider's entry-point to process a context block */ typedef int (*dplane_provider_process_fp)(dplane_ctx_h ctx); +/* Provider's entry-point for shutdown and cleanup */ +typedef int (*dplane_provider_fini_fp)(void); + /* Provider registration */ int dplane_provider_register(const char *name, enum dplane_provider_prio_e prio, - dplane_provider_process_fp fp); + dplane_provider_process_fp fp, + dplane_provider_fini_fp fini_fp); /* * Results are returned to zebra core via a callback From 01ce7cbac1895e8308e6c213336a28b4176ef354 Mon Sep 17 00:00:00 2001 From: Mark Stapp Date: Fri, 17 Aug 2018 15:25:24 -0400 Subject: [PATCH 13/22] zebra: update routing socket path Update route-socket path for route updates using the async dplane module. Signed-off-by: Mark Stapp --- zebra/kernel_socket.c | 1 + zebra/rt_socket.c | 92 +++++++++++++++++++++++++++++++------------ zebra/zebra_dplane.c | 30 ++++++++++++++ zebra/zebra_dplane.h | 2 + 4 files changed, 99 insertions(+), 26 deletions(-) diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index 8df5a37640..d236d488d4 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -48,6 +48,7 @@ #include "zebra/kernel_socket.h" #include "zebra/rib.h" #include "zebra/zebra_errors.h" +#include "zebra/zebra_ptm.h" extern struct zebra_privs_t zserv_privs; diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index eddb0ebfb3..99c4474e69 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -91,7 +91,7 @@ static int kernel_rtm_add_labels(struct mpls_label_stack *nh_label, /* Interface between zebra message and rtm message. */ static int kernel_rtm_ipv4(int cmd, const struct prefix *p, - struct route_entry *re) + const struct nexthop_group *ng, uint32_t metric) { struct sockaddr_in *mask = NULL; @@ -126,7 +126,7 @@ static int kernel_rtm_ipv4(int cmd, const struct prefix *p, #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ /* Make gateway. */ - for (ALL_NEXTHOPS(re->ng, nexthop)) { + for (ALL_NEXTHOPS_PTR(ng, nexthop)) { if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) continue; @@ -139,8 +139,7 @@ static int kernel_rtm_ipv4(int cmd, const struct prefix *p, * other than ADD and DELETE? */ if ((cmd == RTM_ADD && NEXTHOP_IS_ACTIVE(nexthop->flags)) - || (cmd == RTM_DELETE - && CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB))) { + || (cmd == RTM_DELETE)) { if (nexthop->type == NEXTHOP_TYPE_IPV4 || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) { sin_gate.sin_addr = nexthop->gate.ipv4; @@ -181,14 +180,13 @@ static int kernel_rtm_ipv4(int cmd, const struct prefix *p, (union sockunion *)mask, gate ? (union sockunion *)&sin_gate : NULL, - smplsp, ifindex, bh_type, re->metric); + smplsp, ifindex, bh_type, metric); if (IS_ZEBRA_DEBUG_RIB) { if (!gate) { zlog_debug( - "%s: %s: attention! gate not found for re %p", - __func__, prefix_buf, re); - route_entry_dump(p, NULL, re); + "%s: %s: attention! gate not found for re", + __func__, prefix_buf); } else inet_ntop(AF_INET, &sin_gate.sin_addr, gate_buf, INET_ADDRSTRLEN); @@ -247,8 +245,9 @@ static int kernel_rtm_ipv4(int cmd, const struct prefix *p, /* If there was no useful nexthop, then complain. */ if (nexthop_num == 0) { if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("%s: No useful nexthops were found in RIB entry %p", - __func__, re); + zlog_debug("%s: No useful nexthops were found in RIB prefix %s", + __func__, prefix2str(p, prefix_buf, + sizeof(prefix_buf))); return 1; } @@ -281,7 +280,7 @@ static int sin6_masklen(struct in6_addr mask) /* Interface between zebra message and rtm message. */ static int kernel_rtm_ipv6(int cmd, const struct prefix *p, - struct route_entry *re) + const struct nexthop_group *ng, uint32_t metric) { struct sockaddr_in6 *mask; struct sockaddr_in6 sin_dest, sin_mask, sin_gate; @@ -312,7 +311,7 @@ static int kernel_rtm_ipv6(int cmd, const struct prefix *p, #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ /* Make gateway. */ - for (ALL_NEXTHOPS(re->ng, nexthop)) { + for (ALL_NEXTHOPS_PTR(ng, nexthop)) { if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) continue; @@ -367,7 +366,7 @@ static int kernel_rtm_ipv6(int cmd, const struct prefix *p, error = rtm_write(cmd, (union sockunion *)&sin_dest, (union sockunion *)mask, gate ? (union sockunion *)&sin_gate : NULL, - smplsp, ifindex, bh_type, re->metric); + smplsp, ifindex, bh_type, metric); (void)error; nexthop_num++; @@ -383,17 +382,65 @@ static int kernel_rtm_ipv6(int cmd, const struct prefix *p, return 0; /*XXX*/ } -static int kernel_rtm(int cmd, const struct prefix *p, struct route_entry *re) +static int kernel_rtm(int cmd, const struct prefix *p, + const struct nexthop_group *ng, uint32_t metric) { switch (PREFIX_FAMILY(p)) { case AF_INET: - return kernel_rtm_ipv4(cmd, p, re); + return kernel_rtm_ipv4(cmd, p, ng, metric); case AF_INET6: - return kernel_rtm_ipv6(cmd, p, re); + return kernel_rtm_ipv6(cmd, p, ng, metric); } return 0; } +/* + * Update or delete a prefix from the kernel, + * using info from a dataplane context struct. + */ +enum zebra_dplane_result kernel_route_update(dplane_ctx_h ctx) +{ + enum zebra_dplane_result res = ZEBRA_DPLANE_REQUEST_SUCCESS; + + if (dplane_ctx_get_src(ctx) != NULL) { + zlog_err("route add: IPv6 sourcedest routes unsupported!"); + res = ZEBRA_DPLANE_REQUEST_FAILURE; + goto done; + } + + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog_err("Can't raise privileges"); + + if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_DELETE) + kernel_rtm(RTM_DELETE, dplane_ctx_get_dest(ctx), + dplane_ctx_get_ng(ctx), dplane_ctx_get_metric(ctx)); + else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_INSTALL) + kernel_rtm(RTM_ADD, dplane_ctx_get_dest(ctx), + dplane_ctx_get_ng(ctx), dplane_ctx_get_metric(ctx)); + else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_UPDATE) { + /* + * Must do delete and add separately - no update available + */ + kernel_rtm(RTM_DELETE, dplane_ctx_get_dest(ctx), + dplane_ctx_get_old_ng(ctx), + dplane_ctx_get_old_metric(ctx)); + + kernel_rtm(RTM_ADD, dplane_ctx_get_dest(ctx), + dplane_ctx_get_ng(ctx), dplane_ctx_get_metric(ctx)); + } else { + zlog_err("Invalid routing socket update op %u", + dplane_ctx_get_op(ctx)); + res = ZEBRA_DPLANE_REQUEST_FAILURE; + } + + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); + +done: + + return res; +} + enum zebra_dplane_result kernel_route_rib(struct route_node *rn, const struct prefix *p, const struct prefix *src_p, @@ -409,13 +456,11 @@ enum zebra_dplane_result kernel_route_rib(struct route_node *rn, } frr_elevate_privs(&zserv_privs) { - if (old) - route |= kernel_rtm(RTM_DELETE, p, old); - + route |= kernel_rtm(RTM_DELETE, p, + &old->ng, old->metric); if (new) - route |= kernel_rtm(RTM_ADD, p, new); - + route |= kernel_rtm(RTM_ADD, p, &new->ng, new->metric); } if (new) { @@ -433,11 +478,6 @@ enum zebra_dplane_result kernel_route_rib(struct route_node *rn, return ZEBRA_DPLANE_REQUEST_SUCCESS; } -enum zebra_dplane_result kernel_route_update(dplane_ctx_h ctx) -{ - return ZEBRA_DPLANE_REQUEST_FAILURE; -} - int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, int llalen, ns_id_t ns_id) { diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 5e3aa6a4e9..4f54b3440b 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -90,6 +90,7 @@ struct zebra_dplane_ctx_s { route_tag_t zd_tag; route_tag_t zd_old_tag; uint32_t zd_metric; + uint32_t zd_old_metric; uint16_t zd_instance; uint16_t zd_old_instance; @@ -105,6 +106,9 @@ struct zebra_dplane_ctx_s { /* Nexthops */ struct nexthop_group zd_ng; + /* "Previous" nexthops, used only in route update case without netlink */ + struct nexthop_group zd_old_ng; + /* TODO -- use fixed array of nexthops, to avoid mallocs? */ /* Embedded list linkage */ @@ -212,6 +216,10 @@ static void dplane_ctx_free(dplane_ctx_h *pctx) nexthops_free((*pctx)->zd_ng.nexthop); } + if ((*pctx)->zd_old_ng.nexthop) { + nexthops_free((*pctx)->zd_old_ng.nexthop); + } + /* Clear validation value */ (*pctx)->zd_magic = 0; @@ -406,6 +414,13 @@ uint32_t dplane_ctx_get_metric(const dplane_ctx_h ctx) return ctx->zd_metric; } +uint32_t dplane_ctx_get_old_metric(const dplane_ctx_h ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->zd_old_metric; +} + uint32_t dplane_ctx_get_mtu(const dplane_ctx_h ctx) { DPLANE_CTX_VALID(ctx); @@ -441,6 +456,13 @@ const struct nexthop_group *dplane_ctx_get_ng(const dplane_ctx_h ctx) return &(ctx->zd_ng); } +const struct nexthop_group *dplane_ctx_get_old_ng(const dplane_ctx_h ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &(ctx->zd_old_ng); +} + const struct zebra_dplane_info *dplane_ctx_get_ns(const dplane_ctx_h ctx) { DPLANE_CTX_VALID(ctx); @@ -488,6 +510,7 @@ static int dplane_ctx_route_init(dplane_ctx_h ctx, ctx->zd_table_id = re->table; ctx->zd_metric = re->metric; + ctx->zd_old_metric = re->metric; ctx->zd_vrf_id = re->vrf_id; ctx->zd_mtu = re->mtu; ctx->zd_nexthop_mtu = re->nexthop_mtu; @@ -616,6 +639,13 @@ dplane_route_update_internal(struct route_node *rn, ctx->zd_old_type = old_re->type; ctx->zd_old_instance = old_re->instance; ctx->zd_old_distance = old_re->distance; + ctx->zd_old_metric = old_re->metric; + +#ifndef HAVE_NETLINK + /* Capture previous re's nexthops too for bsd, sigh */ + copy_nexthops(&(ctx->zd_old_ng.nexthop), + old_re->ng.nexthop, NULL); +#endif /* !HAVE_NETLINK */ } /* Enqueue context for processing */ diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 49c98879fe..88e671e615 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -159,6 +159,7 @@ route_tag_t dplane_ctx_get_old_tag(const dplane_ctx_h ctx); uint16_t dplane_ctx_get_instance(const dplane_ctx_h ctx); uint16_t dplane_ctx_get_old_instance(const dplane_ctx_h ctx); uint32_t dplane_ctx_get_metric(const dplane_ctx_h ctx); +uint32_t dplane_ctx_get_old_metric(const dplane_ctx_h ctx); uint32_t dplane_ctx_get_mtu(const dplane_ctx_h ctx); uint32_t dplane_ctx_get_nh_mtu(const dplane_ctx_h ctx); uint8_t dplane_ctx_get_distance(const dplane_ctx_h ctx); @@ -166,6 +167,7 @@ uint8_t dplane_ctx_get_old_distance(const dplane_ctx_h ctx); const struct nexthop_group *dplane_ctx_get_ng(const dplane_ctx_h ctx); const struct zebra_dplane_info *dplane_ctx_get_ns(const dplane_ctx_h ctx); +const struct nexthop_group *dplane_ctx_get_old_ng(const dplane_ctx_h ctx); /* * Enqueue route change operations for the dataplane. From 1d11b21f33c41fca2d95e9e0a0b14608f429ef2b Mon Sep 17 00:00:00 2001 From: Mark Stapp Date: Fri, 17 Aug 2018 15:50:09 -0400 Subject: [PATCH 14/22] zebra: add dplane show commands Add first pass at show commands for the zebra dplane. Add some stats counters to show. Start prep for correct shutdown processing, and for multiple providers. Signed-off-by: Mark Stapp --- zebra/main.c | 2 + zebra/zebra_dplane.c | 108 +++++++++++++++++++++++++++++++++++++------ zebra/zebra_dplane.h | 20 +++++++- zebra/zebra_vty.c | 41 +++++++++++++++- 4 files changed, 156 insertions(+), 15 deletions(-) diff --git a/zebra/main.c b/zebra/main.c index 148bcab7e4..33730be923 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -171,6 +171,8 @@ static void sigint(void) route_map_finish(); list_delete(&zebrad.client_list); + zebra_dplane_finish(); + work_queue_free_and_null(&zebrad.ribq); meta_queue_free(zebrad.mq); diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 4f54b3440b..468611e6f6 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -17,11 +17,13 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "lib/zebra.h" #include "lib/libfrr.h" -#include "lib/memory.h" +#include "lib/debug.h" +#include "lib/frratomic.h" #include "lib/frr_pthread.h" +#include "lib/memory.h" #include "lib/queue.h" +#include "lib/zebra.h" #include "zebra/zebra_memory.h" #include "zebra/zserv.h" #include "zebra/zebra_dplane.h" @@ -135,6 +137,9 @@ struct zebra_dplane_provider_s { dplane_provider_fini_fp dp_fini; + _Atomic uint64_t dp_in_counter; + _Atomic uint64_t dp_error_counter; + /* Embedded list linkage */ TAILQ_ENTRY(zebra_dplane_provider_s) dp_q_providers; @@ -150,15 +155,22 @@ static struct zebra_dplane_globals_s { /* Results callback registered by zebra 'core' */ dplane_results_fp dg_results_cb; + /* Sentinel for shutdown */ + volatile bool dg_run; + /* Route-update context queue inbound to the dataplane */ TAILQ_HEAD(zdg_ctx_q, zebra_dplane_ctx_s) dg_route_ctx_q; /* Ordered list of providers */ TAILQ_HEAD(zdg_prov_q, zebra_dplane_provider_s) dg_providers_q; - /* Counter used to assign ids to providers */ + /* Counter used to assign internal ids to providers */ uint32_t dg_provider_id; + _Atomic uint64_t dg_routes_in; + _Atomic uint32_t dg_routes_queued; + _Atomic uint64_t dg_route_errors; + /* Event-delivery context 'master' for the dplane */ struct thread_master *dg_master; @@ -653,10 +665,19 @@ dplane_route_update_internal(struct route_node *rn, } done: - if (ret == AOK) + /* Update counters */ + atomic_fetch_add_explicit(&zdplane_g.dg_routes_in, 1, + memory_order_relaxed); + + if (ret == AOK) { + atomic_fetch_add_explicit(&zdplane_g.dg_routes_queued, 1, + memory_order_relaxed); result = ZEBRA_DPLANE_REQUEST_QUEUED; - else if (ctx) + } else if (ctx) { + atomic_fetch_add_explicit(&zdplane_g.dg_route_errors, 1, + memory_order_relaxed); dplane_ctx_free(&ctx); + } return result; } @@ -724,12 +745,18 @@ static int dplane_route_process(struct thread *event) dplane_ctx_h ctx; while (1) { + /* Check for shutdown */ + if (!zdplane_g.dg_run) + break; + /* TODO -- limit number of updates per cycle? */ ctx = dplane_route_dequeue(); if (ctx == NULL) break; - /* TODO -- support series of providers */ + /* Update counter */ + atomic_fetch_sub_explicit(&zdplane_g.dg_routes_queued, 1, + memory_order_relaxed); if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { char dest_str[PREFIX_STRLEN]; @@ -737,14 +764,20 @@ static int dplane_route_process(struct thread *event) prefix2str(dplane_ctx_get_dest(ctx), dest_str, sizeof(dest_str)); - zlog_debug("%u:%s Dplane update ctx %p op %s", + zlog_debug("%u:%s Dplane route update ctx %p op %s", dplane_ctx_get_vrf(ctx), dest_str, ctx, dplane_op2str(dplane_ctx_get_op(ctx))); } + /* TODO -- support series of providers */ + /* Initially, just doing kernel-facing update here */ res = kernel_route_update(ctx); + if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) + atomic_fetch_add_explicit(&zdplane_g.dg_route_errors, 1, + memory_order_relaxed); + ctx->zd_status = res; /* Enqueue result to zebra main context */ @@ -756,6 +789,38 @@ static int dplane_route_process(struct thread *event) return 0; } +/* + * Handler for 'show dplane' + */ +int dplane_show_helper(struct vty *vty, bool detailed) +{ + uint64_t queued, errs, incoming; + + incoming = atomic_load_explicit(&zdplane_g.dg_routes_in, + memory_order_relaxed); + queued = atomic_load_explicit(&zdplane_g.dg_routes_queued, + memory_order_relaxed); + errs = atomic_load_explicit(&zdplane_g.dg_route_errors, + memory_order_relaxed); + + vty_out(vty, "Route updates: %"PRIu64"\n", incoming); + vty_out(vty, "Route update errors: %"PRIu64"\n", errs); + vty_out(vty, "Route update queue depth: %"PRIu64"\n", queued); + + return CMD_SUCCESS; +} + +/* + * Handler for 'show dplane providers' + */ +int dplane_show_provs_helper(struct vty *vty, bool detailed) +{ + vty_out(vty, "Zebra dataplane providers:%s\n", + (detailed ? " (detailed)" : "")); + + return CMD_SUCCESS; +} + /* * Provider registration */ @@ -839,6 +904,8 @@ static void zebra_dplane_init_internal(struct zebra_t *zebra) /* TODO -- register default kernel 'provider' during init */ + zdplane_g.dg_run = true; + /* TODO -- start dataplane pthread. We're using the zebra * core/main thread temporarily */ @@ -846,13 +913,26 @@ static void zebra_dplane_init_internal(struct zebra_t *zebra) } /* - * Shutdown, de-init hook callback. This runs pretty early during shutdown. + * Shutdown, de-init api. This runs pretty late during shutdown, + * because zebra tries to free/remove/uninstall all routes during shutdown. + * NB: This runs in the main zebra thread context. */ -static int zebra_dplane_fini(void) +void zebra_dplane_finish(void) { - /* TODO -- stop thread, clean queues */ + /* Wait until all pending updates are processed */ + + /* Stop dplane thread, if it's running */ + + zdplane_g.dg_run = false; + + THREAD_OFF(zdplane_g.dg_t_update); + + /* Notify provider(s) of shutdown */ + + /* Clean-up provider objects */ + + /* Clean queue(s) */ - return 0; } /* @@ -862,6 +942,8 @@ void zebra_dplane_init(void) { zebra_dplane_init_internal(&zebrad); - /* Register for shutdown/de-init */ - hook_register(frr_early_fini, zebra_dplane_fini); + /* Finalize/cleanup code is called quite late during zebra shutdown; + * zebra expects to try to clean up all vrfs and all routes during + * shutdown, so the dplane must be available until very late. + */ } diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 88e671e615..aa7443cf85 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -182,6 +182,17 @@ enum zebra_dplane_result dplane_route_update(struct route_node *rn, enum zebra_dplane_result dplane_route_delete(struct route_node *rn, struct route_entry *re); +/* + * Vty/cli apis + */ +int dplane_show_helper(struct vty *vty, bool detailed); +int dplane_show_provs_helper(struct vty *vty, bool detailed); + + +/* + * Dataplane providers: modules that consume dataplane events. + */ + /* Support string name for a dataplane provider */ #define DPLANE_PROVIDER_NAMELEN 64 @@ -224,8 +235,15 @@ typedef int (*dplane_results_fp)(const dplane_ctx_h ctx); int dplane_results_register(dplane_results_fp fp); /* - * Initialize the dataplane modules at zebra startup. + * Initialize the dataplane modules at zebra startup. This is currently called + * by the rib module. */ void zebra_dplane_init(void); +/* Finalize/cleanup api, called quite late during zebra shutdown. + * Zebra expects to try to clean up all vrfs and all routes during + * shutdown, so the dplane must be available until very late. + */ +void zebra_dplane_finish(void); + #endif /* _ZEBRA_DPLANE_H */ diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index fdedb30f84..d9881b98c5 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -2567,6 +2567,44 @@ DEFUN (no_ipv6_forwarding, return CMD_SUCCESS; } +/* Display dataplane info */ +DEFUN (show_dataplane, + show_dataplane_cmd, + "show zebra dplane [detailed]", + SHOW_STR + ZEBRA_STR + "Zebra dataplane information\n" + "Detailed output\n") +{ + int idx = 0; + bool detailed = false; + + if (argv_find(argv, argc, "detailed", &idx)) + detailed = true; + + return dplane_show_helper(vty, detailed); +} + +/* Display dataplane providers info */ +DEFUN (show_dataplane_providers, + show_dataplane_providers_cmd, + "show zebra dplane providers [detailed]", + SHOW_STR + ZEBRA_STR + "Zebra dataplane information\n" + "Zebra dataplane provider information\n" + "Detailed output\n") +{ + int idx = 0; + bool detailed = false; + + if (argv_find(argv, argc, "detailed", &idx)) + detailed = true; + + return dplane_show_provs_helper(vty, detailed); +} + + /* Table configuration write function. */ static int config_write_table(struct vty *vty) { @@ -2697,5 +2735,6 @@ void zebra_vty_init(void) install_element(VRF_NODE, &vrf_vni_mapping_cmd); install_element(VRF_NODE, &no_vrf_vni_mapping_cmd); - + install_element(VIEW_NODE, &show_dataplane_cmd); + install_element(VIEW_NODE, &show_dataplane_providers_cmd); } From f183e380fae61b7c1f89fed6e32ed5a9d1ede8a8 Mon Sep 17 00:00:00 2001 From: Mark Stapp Date: Fri, 17 Aug 2018 16:53:24 -0400 Subject: [PATCH 15/22] zebra: add handy res2str utility Add a 2str utility for dplane result codes; use it in a debug or two. Signed-off-by: Mark Stapp --- zebra/rt_netlink.c | 21 +++++++++++++ zebra/rt_socket.c | 75 +++++++++++++++++++++++--------------------- zebra/zebra_dplane.c | 29 ++++++++++++++++- zebra/zebra_dplane.h | 1 + zebra/zebra_rib.c | 48 ++++++++++++++++++++++------ zebra/zebra_rnh.c | 17 +++++++++- 6 files changed, 144 insertions(+), 47 deletions(-) diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index ab1d80f900..909a155eeb 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1965,6 +1965,7 @@ static int netlink_route_multipath_ctx(int cmd, dplane_ctx_h ctx) setsrc = 1; } } + continue; } if ((cmd == RTM_NEWROUTE @@ -2205,6 +2206,7 @@ enum zebra_dplane_result kernel_route_update(dplane_ctx_h ctx) { int cmd, ret; const struct prefix *p = dplane_ctx_get_dest(ctx); + struct nexthop *nexthop; if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_DELETE) { cmd = RTM_DELROUTE; @@ -2237,6 +2239,25 @@ enum zebra_dplane_result kernel_route_update(dplane_ctx_h ctx) } ret = netlink_route_multipath_ctx(cmd, ctx); + if ((cmd == RTM_NEWROUTE) && (ret == 0)) { + /* Update installed nexthops to signal which have been + * installed. + */ + for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + + /* If we're only allowed a single nh, don't + * continue. + */ + if (multipath_num == 1) + break; + } + } + } return (ret == 0 ? ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE); diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index 99c4474e69..d5146c6433 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -182,7 +182,7 @@ static int kernel_rtm_ipv4(int cmd, const struct prefix *p, : NULL, smplsp, ifindex, bh_type, metric); - if (IS_ZEBRA_DEBUG_RIB) { + if (IS_ZEBRA_DEBUG_KERNEL) { if (!gate) { zlog_debug( "%s: %s: attention! gate not found for re", @@ -197,10 +197,15 @@ static int kernel_rtm_ipv4(int cmd, const struct prefix *p, * did its work. */ case ZEBRA_ERR_NOERROR: nexthop_num++; - if (IS_ZEBRA_DEBUG_RIB) + if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( "%s: %s: successfully did NH %s", __func__, prefix_buf, gate_buf); + + if (cmd == RTM_ADD) + SET_FLAG(nexthop->flags, + NEXTHOP_FLAG_FIB); + break; /* The only valid case for this error is kernel's @@ -216,14 +221,8 @@ static int kernel_rtm_ipv4(int cmd, const struct prefix *p, "%s: rtm_write() returned %d for command %d", __func__, error, cmd); continue; - break; - /* Given that our NEXTHOP_FLAG_FIB matches real kernel - * FIB, it isn't - * normal to get any other messages in ANY case. - */ - case ZEBRA_ERR_RTNOEXIST: - case ZEBRA_ERR_RTUNREACH: + /* Note any unexpected status returns */ default: flog_err( EC_LIB_SYSTEM_CALL, @@ -236,7 +235,7 @@ static int kernel_rtm_ipv4(int cmd, const struct prefix *p, break; } } /* if (cmd and flags make sense) */ - else if (IS_ZEBRA_DEBUG_RIB) + else if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("%s: odd command %s for flags %d", __func__, lookup_msg(rtm_type_str, cmd, NULL), nexthop->flags); @@ -367,7 +366,10 @@ static int kernel_rtm_ipv6(int cmd, const struct prefix *p, (union sockunion *)mask, gate ? (union sockunion *)&sin_gate : NULL, smplsp, ifindex, bh_type, metric); - (void)error; + + /* Update installed nexthop info on success */ + if ((cmd == RTM_ADD) && (error == ZEBRA_ERR_NOERROR)) + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); nexthop_num++; } @@ -408,33 +410,34 @@ enum zebra_dplane_result kernel_route_update(dplane_ctx_h ctx) goto done; } - if (zserv_privs.change(ZPRIVS_RAISE)) - zlog_err("Can't raise privileges"); + frr_elevate_privs(ZPRIVS_RAISE) { - if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_DELETE) - kernel_rtm(RTM_DELETE, dplane_ctx_get_dest(ctx), - dplane_ctx_get_ng(ctx), dplane_ctx_get_metric(ctx)); - else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_INSTALL) - kernel_rtm(RTM_ADD, dplane_ctx_get_dest(ctx), - dplane_ctx_get_ng(ctx), dplane_ctx_get_metric(ctx)); - else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_UPDATE) { - /* - * Must do delete and add separately - no update available - */ - kernel_rtm(RTM_DELETE, dplane_ctx_get_dest(ctx), - dplane_ctx_get_old_ng(ctx), - dplane_ctx_get_old_metric(ctx)); + if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_DELETE) + kernel_rtm(RTM_DELETE, dplane_ctx_get_dest(ctx), + dplane_ctx_get_ng(ctx), + dplane_ctx_get_metric(ctx)); + else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_INSTALL) + kernel_rtm(RTM_ADD, dplane_ctx_get_dest(ctx), + dplane_ctx_get_ng(ctx), + dplane_ctx_get_metric(ctx)); + else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_UPDATE) { + /* Must do delete and add separately - + * no update available + */ + kernel_rtm(RTM_DELETE, dplane_ctx_get_dest(ctx), + dplane_ctx_get_old_ng(ctx), + dplane_ctx_get_old_metric(ctx)); - kernel_rtm(RTM_ADD, dplane_ctx_get_dest(ctx), - dplane_ctx_get_ng(ctx), dplane_ctx_get_metric(ctx)); - } else { - zlog_err("Invalid routing socket update op %u", - dplane_ctx_get_op(ctx)); - res = ZEBRA_DPLANE_REQUEST_FAILURE; - } - - if (zserv_privs.change(ZPRIVS_LOWER)) - zlog_err("Can't lower privileges"); + kernel_rtm(RTM_ADD, dplane_ctx_get_dest(ctx), + dplane_ctx_get_ng(ctx), + dplane_ctx_get_metric(ctx)); + } else { + zlog_err("Invalid routing socket update op %s (%u)", + dplane_op2str(dplane_ctx_get_op(ctx)), + dplane_ctx_get_op(ctx)); + res = ZEBRA_DPLANE_REQUEST_FAILURE; + } + } /* Elevated privs */ done: diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 468611e6f6..2bc8e45ebb 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -308,6 +308,25 @@ const char *dplane_op2str(enum dplane_op_e op) return ret; } +const char *dplane_res2str(enum zebra_dplane_result res) +{ + const char *ret = ""; + + switch (res) { + case ZEBRA_DPLANE_REQUEST_FAILURE: + ret = "FAILURE"; + break; + case ZEBRA_DPLANE_REQUEST_QUEUED: + ret = "QUEUED"; + break; + case ZEBRA_DPLANE_REQUEST_SUCCESS: + ret = "SUCCESS"; + break; + }; + + return ret; +} + const struct prefix *dplane_ctx_get_dest(const dplane_ctx_h ctx) { DPLANE_CTX_VALID(ctx); @@ -500,6 +519,7 @@ static int dplane_ctx_route_init(dplane_ctx_h ctx, const struct prefix *p, *src_p; struct zebra_ns *zns; struct zebra_vrf *zvrf; + struct nexthop *nexthop; if (!ctx || !rn || !re) goto done; @@ -558,6 +578,11 @@ static int dplane_ctx_route_init(dplane_ctx_h ctx, /* TODO -- maybe use array of nexthops to avoid allocs? */ + /* Ensure that the dplane's nexthop flag is clear. */ + for (ALL_NEXTHOPS(ctx->zd_ng, nexthop)) { + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + } + /* Trying out the sequence number idea, so we can try to detect * when a result is stale. */ @@ -654,7 +679,9 @@ dplane_route_update_internal(struct route_node *rn, ctx->zd_old_metric = old_re->metric; #ifndef HAVE_NETLINK - /* Capture previous re's nexthops too for bsd, sigh */ + /* For bsd, capture previous re's nexthops too, sigh. + * We'll need these to do per-nexthop deletes. + */ copy_nexthops(&(ctx->zd_old_ng.nexthop), old_re->ng.nexthop, NULL); #endif /* !HAVE_NETLINK */ diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index aa7443cf85..37cd2ca7f1 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -134,6 +134,7 @@ void dplane_ctx_dequeue(struct dplane_ctx_q_s *q, dplane_ctx_h *ctxp); * Accessors for information from the context object */ enum zebra_dplane_result dplane_ctx_get_status(const dplane_ctx_h ctx); +const char *dplane_res2str(enum zebra_dplane_result res); enum dplane_op_e dplane_ctx_get_op(const dplane_ctx_h ctx); const char *dplane_op2str(enum dplane_op_e op); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 964885f47e..7a3b902b19 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -1951,7 +1951,7 @@ static void rib_process_after(dplane_ctx_h ctx) struct route_node *rn = NULL; struct route_entry *re = NULL, *old_re = NULL, *rib; bool is_update = false; - struct nexthop *nexthop; + struct nexthop *nexthop, *ctx_nexthop; char dest_str[PREFIX_STRLEN] = ""; enum dplane_op_e op; enum zebra_dplane_result status; @@ -2000,9 +2000,9 @@ static void rib_process_after(dplane_ctx_h ctx) status = dplane_ctx_get_status(ctx); if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { - zlog_debug("%u:%s Processing dplane ctx %p, op %s result %d", + zlog_debug("%u:%s Processing dplane ctx %p, op %s result %s", dplane_ctx_get_vrf(ctx), dest_str, ctx, - dplane_op2str(op), status); + dplane_op2str(op), dplane_res2str(status)); } if (op == DPLANE_OP_ROUTE_DELETE) { @@ -2090,19 +2090,34 @@ static void rib_process_after(dplane_ctx_h ctx) } if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) { - /* Set nexthop FIB flags */ - for (ALL_NEXTHOPS(re->ng, nexthop)) { + /* Update zebra nexthop FIB flag for each + * nexthop that was installed. + */ + for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), ctx_nexthop)) { + + for (ALL_NEXTHOPS(re->ng, nexthop)) { + if (nexthop_same(ctx_nexthop, nexthop)) + break; + } + + if (nexthop == NULL) + continue; + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) continue; - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + if (CHECK_FLAG(ctx_nexthop->flags, + NEXTHOP_FLAG_FIB)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); else UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); } - if (zvrf) + if (zvrf) { zvrf->installs++; + /* Set flag for nexthop tracking processing */ + zvrf->flags |= ZEBRA_VRF_RIB_SCHEDULED; + } /* Redistribute */ /* TODO -- still calling the redist api using the route_entries, @@ -2179,9 +2194,9 @@ static unsigned int process_subq(struct list *subq, uint8_t qindex) } /* - * All meta queues have been processed. Trigger next-hop evaluation. + * Perform next-hop tracking processing after RIB updates. */ -static void meta_queue_process_complete(struct work_queue *dummy) +static void do_nht_processing(void) { struct vrf *vrf; struct zebra_vrf *zvrf; @@ -2196,6 +2211,10 @@ static void meta_queue_process_complete(struct work_queue *dummy) if (zvrf == NULL || !(zvrf->flags & ZEBRA_VRF_RIB_SCHEDULED)) continue; + if (IS_ZEBRA_DEBUG_RIB_DETAILED || IS_ZEBRA_DEBUG_NHT) + zlog_debug("NHT processing check for zvrf %s", + zvrf_name(zvrf)); + zvrf->flags &= ~ZEBRA_VRF_RIB_SCHEDULED; zebra_evaluate_rnh(zvrf, AF_INET, 0, RNH_NEXTHOP_TYPE, NULL); zebra_evaluate_rnh(zvrf, AF_INET, 0, RNH_IMPORT_CHECK_TYPE, @@ -2217,6 +2236,14 @@ static void meta_queue_process_complete(struct work_queue *dummy) } } +/* + * All meta queues have been processed. Trigger next-hop evaluation. + */ +static void meta_queue_process_complete(struct work_queue *dummy) +{ + do_nht_processing(); +} + /* Dispatch the meta queue by picking, processing and unlocking the next RN from * a non-empty sub-queue with lowest priority. wq is equal to zebra->ribq and * data @@ -3300,6 +3327,9 @@ static int rib_process_dplane_results(struct thread *thread) } while (1); + /* Check for nexthop tracking processing after finishing with results */ + do_nht_processing(); + return 0; } diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 0a531990dc..838a623ef1 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -530,6 +530,7 @@ zebra_rnh_resolve_nexthop_entry(struct zebra_vrf *zvrf, int family, struct route_table *route_table; struct route_node *rn; struct route_entry *re; + struct nexthop *nexthop; *prn = NULL; @@ -562,12 +563,26 @@ zebra_rnh_resolve_nexthop_entry(struct zebra_vrf *zvrf, int family, if (!CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED)) continue; + /* Just being SELECTED isn't quite enough - must + * have an installed nexthop to be useful. + */ + for (nexthop = re->ng.nexthop; nexthop; + nexthop = nexthop->next) + if ((CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB) + || CHECK_FLAG(nexthop->flags, + NEXTHOP_FLAG_RECURSIVE)) + && CHECK_FLAG(nexthop->flags, + NEXTHOP_FLAG_ACTIVE)) + break; + + if (nexthop == NULL) + continue; + if (CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED)) { if ((re->type == ZEBRA_ROUTE_CONNECT) || (re->type == ZEBRA_ROUTE_STATIC)) break; if (re->type == ZEBRA_ROUTE_NHRP) { - struct nexthop *nexthop; for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) From 4dfd7a021dc798ee0cb23fee7987788477f042d8 Mon Sep 17 00:00:00 2001 From: Mark Stapp Date: Mon, 27 Aug 2018 16:03:37 -0400 Subject: [PATCH 16/22] zebra: support zebra shutdown and cleanup Dplane support for zebra's route cleanup during shutdown (clean shutdown via SIGINT, anyway.) The dplane has the opportunity to process incoming updates, and then triggers final cleanup in zebra's main thread. Signed-off-by: Mark Stapp --- zebra/main.c | 15 ++++- zebra/zebra_dplane.c | 154 ++++++++++++++++++++++++++++++++++++++----- zebra/zebra_dplane.h | 12 +++- zebra/zebra_rnh.c | 25 ++++--- zebra/zserv.h | 3 + 5 files changed, 182 insertions(+), 27 deletions(-) diff --git a/zebra/main.c b/zebra/main.c index 33730be923..955ab0445e 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -147,6 +147,8 @@ static void sigint(void) frr_early_fini(); + zebra_dplane_pre_finish(); + for (ALL_LIST_ELEMENTS(zebrad.client_list, ln, nn, client)) zserv_close_client(client); @@ -171,7 +173,18 @@ static void sigint(void) route_map_finish(); list_delete(&zebrad.client_list); + + /* Indicate that all new dplane work has been enqueued */ zebra_dplane_finish(); +} + +/* TODO */ +int zebra_finalize(struct thread *dummy) +{ + zlog_info("Zebra final shutdown"); + + /* Stop dplane thread and finish any cleanup */ + zebra_dplane_shutdown(); work_queue_free_and_null(&zebrad.ribq); meta_queue_free(zebrad.mq); @@ -420,7 +433,7 @@ int main(int argc, char **argv) /* RNH init */ zebra_rnh_init(); - + /* Error init */ zebra_error_init(); diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 2bc8e45ebb..1e90fd15ce 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -108,7 +108,7 @@ struct zebra_dplane_ctx_s { /* Nexthops */ struct nexthop_group zd_ng; - /* "Previous" nexthops, used only in route update case without netlink */ + /* "Previous" nexthops, used only in route updates without netlink */ struct nexthop_group zd_old_ng; /* TODO -- use fixed array of nexthops, to avoid mallocs? */ @@ -155,7 +155,10 @@ static struct zebra_dplane_globals_s { /* Results callback registered by zebra 'core' */ dplane_results_fp dg_results_cb; - /* Sentinel for shutdown */ + /* Sentinel for beginning of shutdown */ + volatile bool dg_is_shutdown; + + /* Sentinel for end of shutdown */ volatile bool dg_run; /* Route-update context queue inbound to the dataplane */ @@ -169,6 +172,7 @@ static struct zebra_dplane_globals_s { _Atomic uint64_t dg_routes_in; _Atomic uint32_t dg_routes_queued; + _Atomic uint32_t dg_routes_queued_max; _Atomic uint64_t dg_route_errors; /* Event-delivery context 'master' for the dplane */ @@ -177,6 +181,9 @@ static struct zebra_dplane_globals_s { /* Event/'thread' pointer for queued updates */ struct thread *dg_t_update; + /* Event pointer for pending shutdown check loop */ + struct thread *dg_t_shutdown_check; + } zdplane_g; /* @@ -229,6 +236,7 @@ static void dplane_ctx_free(dplane_ctx_h *pctx) } if ((*pctx)->zd_old_ng.nexthop) { + /* This deals with recursive nexthops too */ nexthops_free((*pctx)->zd_old_ng.nexthop); } @@ -579,9 +587,8 @@ static int dplane_ctx_route_init(dplane_ctx_h ctx, /* TODO -- maybe use array of nexthops to avoid allocs? */ /* Ensure that the dplane's nexthop flag is clear. */ - for (ALL_NEXTHOPS(ctx->zd_ng, nexthop)) { + for (ALL_NEXTHOPS(ctx->zd_ng, nexthop)) UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - } /* Trying out the sequence number idea, so we can try to detect * when a result is stale. @@ -697,8 +704,26 @@ done: memory_order_relaxed); if (ret == AOK) { - atomic_fetch_add_explicit(&zdplane_g.dg_routes_queued, 1, - memory_order_relaxed); + uint32_t high, curr; + + curr = atomic_fetch_add_explicit(&zdplane_g.dg_routes_queued, 1, + memory_order_seq_cst); + + /* We don't have add_and_fetch - sigh */ + curr++; + + /* Maybe update high-water counter also */ + high = atomic_load_explicit(&zdplane_g.dg_routes_queued_max, + memory_order_seq_cst); + while (high < curr) { + if (atomic_compare_exchange_weak_explicit( + &zdplane_g.dg_routes_queued_max, + &high, curr, + memory_order_seq_cst, + memory_order_seq_cst)) + break; + } + result = ZEBRA_DPLANE_REQUEST_QUEUED; } else if (ctx) { atomic_fetch_add_explicit(&zdplane_g.dg_route_errors, 1, @@ -821,18 +846,24 @@ static int dplane_route_process(struct thread *event) */ int dplane_show_helper(struct vty *vty, bool detailed) { - uint64_t queued, errs, incoming; + uint64_t queued, queue_max, errs, incoming; + /* Using atomics because counters are being changed in different + * contexts. + */ incoming = atomic_load_explicit(&zdplane_g.dg_routes_in, memory_order_relaxed); queued = atomic_load_explicit(&zdplane_g.dg_routes_queued, memory_order_relaxed); + queue_max = atomic_load_explicit(&zdplane_g.dg_routes_queued_max, + memory_order_relaxed); errs = atomic_load_explicit(&zdplane_g.dg_route_errors, memory_order_relaxed); vty_out(vty, "Route updates: %"PRIu64"\n", incoming); vty_out(vty, "Route update errors: %"PRIu64"\n", errs); vty_out(vty, "Route update queue depth: %"PRIu64"\n", queued); + vty_out(vty, "Route update queue max: %"PRIu64"\n", queue_max); return CMD_SUCCESS; } @@ -939,14 +970,108 @@ static void zebra_dplane_init_internal(struct zebra_t *zebra) zdplane_g.dg_master = zebra->master; } +/* Indicates zebra shutdown/exit is in progress. Some operations may be + * simplified or skipped during shutdown processing. + */ +bool dplane_is_in_shutdown(void) +{ + return zdplane_g.dg_is_shutdown; +} + +/* + * Early or pre-shutdown, de-init notification api. This runs pretty + * early during zebra shutdown, as a signal to stop new work and prepare + * for updates generated by shutdown/cleanup activity, as zebra tries to + * remove everything it's responsible for. + * NB: This runs in the main zebra thread context. + */ +void zebra_dplane_pre_finish(void) +{ + if (IS_ZEBRA_DEBUG_DPLANE) + zlog_debug("Zebra dataplane pre-fini called"); + + zdplane_g.dg_is_shutdown = true; + + /* Notify provider(s) of pending shutdown */ +} + +/* + * Utility to determine whether work remains enqueued within the dplane; + * used during system shutdown processing. + */ +static bool dplane_work_pending(void) +{ + dplane_ctx_h ctx; + + /* TODO -- just checking incoming/pending work for now */ + DPLANE_LOCK(); + { + ctx = TAILQ_FIRST(&zdplane_g.dg_route_ctx_q); + } + DPLANE_UNLOCK(); + + return (ctx != NULL); +} + +/* + * Shutdown-time intermediate callback, used to determine when all pending + * in-flight updates are done. If there's still work to do, reschedules itself. + * If all work is done, schedules an event to the main zebra thread for + * final zebra shutdown. + * This runs in the dplane pthread context. + */ +static int dplane_check_shutdown_status(struct thread *event) +{ + if (IS_ZEBRA_DEBUG_DPLANE) + zlog_debug("Zebra dataplane shutdown status check called"); + + if (dplane_work_pending()) { + /* Reschedule dplane check on a short timer */ + thread_add_timer_msec(zdplane_g.dg_master, + dplane_check_shutdown_status, + NULL, 100, + &zdplane_g.dg_t_shutdown_check); + + /* TODO - give up and stop waiting after a short time? */ + + } else { + /* We appear to be done - schedule a final callback event + * for the zebra main pthread. + */ + thread_add_event(zebrad.master, zebra_finalize, NULL, 0, NULL); + } + + return 0; +} + /* * Shutdown, de-init api. This runs pretty late during shutdown, - * because zebra tries to free/remove/uninstall all routes during shutdown. + * after zebra has tried to free/remove/uninstall all routes during shutdown. + * At this point, dplane work may still remain to be done, so we can't just + * blindly terminate. If there's still work to do, we'll periodically check + * and when done, we'll enqueue a task to the zebra main thread for final + * termination processing. + * * NB: This runs in the main zebra thread context. */ void zebra_dplane_finish(void) { - /* Wait until all pending updates are processed */ + if (IS_ZEBRA_DEBUG_DPLANE) + zlog_debug("Zebra dataplane fini called"); + + thread_add_event(zdplane_g.dg_master, + dplane_check_shutdown_status, NULL, 0, + &zdplane_g.dg_t_shutdown_check); +} + +/* + * Final phase of shutdown, after all work enqueued to dplane has been + * processed. This is called from the zebra main pthread context. + */ +void zebra_dplane_shutdown(void) +{ + if (IS_ZEBRA_DEBUG_DPLANE) + zlog_debug("Zebra dataplane shutdown called"); /* Stop dplane thread, if it's running */ @@ -954,12 +1079,14 @@ void zebra_dplane_finish(void) THREAD_OFF(zdplane_g.dg_t_update); - /* Notify provider(s) of shutdown */ + /* TODO */ + /* frr_pthread_stop(...) */ + + /* Notify provider(s) of final shutdown */ /* Clean-up provider objects */ /* Clean queue(s) */ - } /* @@ -968,9 +1095,4 @@ void zebra_dplane_finish(void) void zebra_dplane_init(void) { zebra_dplane_init_internal(&zebrad); - - /* Finalize/cleanup code is called quite late during zebra shutdown; - * zebra expects to try to clean up all vrfs and all routes during - * shutdown, so the dplane must be available until very late. - */ } diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 37cd2ca7f1..0962563135 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -170,6 +170,11 @@ const struct nexthop_group *dplane_ctx_get_ng(const dplane_ctx_h ctx); const struct zebra_dplane_info *dplane_ctx_get_ns(const dplane_ctx_h ctx); const struct nexthop_group *dplane_ctx_get_old_ng(const dplane_ctx_h ctx); +/* Indicates zebra shutdown/exit is in progress. Some operations may be + * simplified or skipped during shutdown processing. + */ +bool dplane_is_in_shutdown(void); + /* * Enqueue route change operations for the dataplane. */ @@ -241,10 +246,15 @@ int dplane_results_register(dplane_results_fp fp); */ void zebra_dplane_init(void); -/* Finalize/cleanup api, called quite late during zebra shutdown. +/* Finalize/cleanup apis, one called early as shutdown is starting, + * one called late at the end of zebra shutdown, and then one called + * from the zebra main thread to stop the dplane thread free all resources. + * * Zebra expects to try to clean up all vrfs and all routes during * shutdown, so the dplane must be available until very late. */ +void zebra_dplane_pre_finish(void); void zebra_dplane_finish(void); +void zebra_dplane_shutdown(void); #endif /* _ZEBRA_DPLANE_H */ diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 838a623ef1..bae596ec51 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -518,6 +518,18 @@ static void zebra_rnh_process_pbr_tables(int family, } } +/* + * Utility to determine whether a candidate nexthop is useable. We make this + * check in a couple of places, so this is a single home for the logic we + * use. + */ +static bool rnh_nexthop_valid(const struct nexthop *nh) +{ + return ((CHECK_FLAG(nh->flags, NEXTHOP_FLAG_FIB) + || CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE)) + && CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ACTIVE)); +} + /* * Determine appropriate route (route entry) resolving a tracked * nexthop. @@ -567,13 +579,10 @@ zebra_rnh_resolve_nexthop_entry(struct zebra_vrf *zvrf, int family, * have an installed nexthop to be useful. */ for (nexthop = re->ng.nexthop; nexthop; - nexthop = nexthop->next) - if ((CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB) - || CHECK_FLAG(nexthop->flags, - NEXTHOP_FLAG_RECURSIVE)) - && CHECK_FLAG(nexthop->flags, - NEXTHOP_FLAG_ACTIVE)) + nexthop = nexthop->next) { + if (rnh_nexthop_valid(nexthop)) break; + } if (nexthop == NULL) continue; @@ -904,9 +913,7 @@ static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type, nump = stream_get_endp(s); stream_putc(s, 0); for (nh = re->ng.nexthop; nh; nh = nh->next) - if ((CHECK_FLAG(nh->flags, NEXTHOP_FLAG_FIB) - || CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE)) - && CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ACTIVE)) { + if (rnh_nexthop_valid(nh)) { stream_putc(s, nh->type); switch (nh->type) { case NEXTHOP_TYPE_IPV4: diff --git a/zebra/zserv.h b/zebra/zserv.h index aaefd78eea..987c67635d 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -251,4 +251,7 @@ extern void zserv_close_client(struct zserv *client); extern void zserv_read_file(char *input); #endif +/* TODO */ +int zebra_finalize(struct thread *event); + #endif /* _ZEBRA_ZEBRA_H */ From 14c8b173d2a55804ba105c351774f374b3fd0820 Mon Sep 17 00:00:00 2001 From: Mark Stapp Date: Wed, 29 Aug 2018 16:15:18 -0400 Subject: [PATCH 17/22] zebra: remove old apis after new dplane work Replaced or out-grew a few zebra internal apis during async dataplane work; removing them. Signed-off-by: Mark Stapp --- zebra/rt.h | 27 ---- zebra/rt_netlink.c | 381 +-------------------------------------------- zebra/rt_socket.c | 37 ----- zebra/zebra_rib.c | 69 -------- 4 files changed, 3 insertions(+), 511 deletions(-) diff --git a/zebra/rt.h b/zebra/rt.h index a4db8968a5..6e45514381 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -31,33 +31,6 @@ #include "zebra/zebra_mpls.h" #include "zebra/zebra_dplane.h" -/* - * Install/delete the specified prefix p from the kernel - * - * old = NULL, new = pointer - Install new - * old = pointer, new = pointer - Route replace Old w/ New - * old = pointer, new = NULL, - Route Delete - * - * Please note not all kernels support route replace - * semantics so we will end up with a delete than - * a re-add. - */ -extern enum zebra_dplane_result kernel_route_rib(struct route_node *rn, - const struct prefix *p, - const struct prefix *src_p, - struct route_entry *old, - struct route_entry *new); - -/* - * So route install/failure may not be immediately known - * so let's separate it out and allow the result to - * be passed back up. - */ -extern void kernel_route_rib_pass_fail(struct route_node *rn, - const struct prefix *p, - struct route_entry *re, - enum zebra_dplane_status res); - /* * Update or delete a prefix from the kernel, * using info from a dataplane context. diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 909a155eeb..d2e01adad3 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1443,335 +1443,10 @@ static int netlink_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla, 0); } -/* Routing table change via netlink interface. */ -/* Update flag indicates whether this is a "replace" or not. */ -static int netlink_route_multipath(int cmd, const struct prefix *p, - const struct prefix *src_p, - struct route_entry *re, - int update) -{ - int bytelen; - struct sockaddr_nl snl; - struct nexthop *nexthop = NULL; - unsigned int nexthop_num; - int family = PREFIX_FAMILY(p); - const char *routedesc; - int setsrc = 0; - union g_addr src; - - struct { - struct nlmsghdr n; - struct rtmsg r; - char buf[NL_PKT_BUF_SIZE]; - } req; - - struct zebra_ns *zns; - struct zebra_vrf *zvrf = vrf_info_lookup(re->vrf_id); - - zns = zvrf->zns; - memset(&req, 0, sizeof req - NL_PKT_BUF_SIZE); - - bytelen = (family == AF_INET ? 4 : 16); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); - req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; - if ((cmd == RTM_NEWROUTE) && update) - req.n.nlmsg_flags |= NLM_F_REPLACE; - req.n.nlmsg_type = cmd; - req.n.nlmsg_pid = zns->netlink_cmd.snl.nl_pid; - - req.r.rtm_family = family; - req.r.rtm_dst_len = p->prefixlen; - req.r.rtm_src_len = src_p ? src_p->prefixlen : 0; - req.r.rtm_protocol = zebra2proto(re->type); - req.r.rtm_scope = RT_SCOPE_UNIVERSE; - - /* - * blackhole routes are not RTN_UNICAST, they are - * RTN_ BLACKHOLE|UNREACHABLE|PROHIBIT - * so setting this value as a RTN_UNICAST would - * cause the route lookup of just the prefix - * to fail. So no need to specify this for - * the RTM_DELROUTE case - */ - if (cmd != RTM_DELROUTE) - req.r.rtm_type = RTN_UNICAST; - - addattr_l(&req.n, sizeof req, RTA_DST, &p->u.prefix, bytelen); - if (src_p) - addattr_l(&req.n, sizeof req, RTA_SRC, &src_p->u.prefix, - bytelen); - - /* Metric. */ - /* Hardcode the metric for all routes coming from zebra. Metric isn't - * used - * either by the kernel or by zebra. Its purely for calculating best - * path(s) - * by the routing protocol and for communicating with protocol peers. - */ - addattr32(&req.n, sizeof req, RTA_PRIORITY, NL_DEFAULT_ROUTE_METRIC); -#if defined(SUPPORT_REALMS) - if (re->tag > 0 && re->tag <= 255) - addattr32(&req.n, sizeof req, RTA_FLOW, re->tag); -#endif - /* Table corresponding to this route. */ - if (re->table < 256) - req.r.rtm_table = re->table; - else { - req.r.rtm_table = RT_TABLE_UNSPEC; - addattr32(&req.n, sizeof req, RTA_TABLE, re->table); - } - - _netlink_route_debug(cmd, p, family, zvrf_id(zvrf), re->table); - - /* - * If we are not updating the route and we have received - * a route delete, then all we need to fill in is the - * prefix information to tell the kernel to schwack - * it. - */ - if (!update && cmd == RTM_DELROUTE) - goto skip; - - if (re->mtu || re->nexthop_mtu) { - char buf[NL_PKT_BUF_SIZE]; - struct rtattr *rta = (void *)buf; - uint32_t mtu = re->mtu; - if (!mtu || (re->nexthop_mtu && re->nexthop_mtu < mtu)) - mtu = re->nexthop_mtu; - rta->rta_type = RTA_METRICS; - rta->rta_len = RTA_LENGTH(0); - rta_addattr_l(rta, NL_PKT_BUF_SIZE, RTAX_MTU, &mtu, sizeof mtu); - addattr_l(&req.n, NL_PKT_BUF_SIZE, RTA_METRICS, RTA_DATA(rta), - RTA_PAYLOAD(rta)); - } - - /* Count overall nexthops so we can decide whether to use singlepath - * or multipath case. */ - nexthop_num = 0; - for (ALL_NEXTHOPS(re->ng, nexthop)) { - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - continue; - if (cmd == RTM_NEWROUTE && !NEXTHOP_IS_ACTIVE(nexthop->flags)) - continue; - if (cmd == RTM_DELROUTE - && !CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) - continue; - - nexthop_num++; - } - - /* Singlepath case. */ - if (nexthop_num == 1 || multipath_num == 1) { - nexthop_num = 0; - for (ALL_NEXTHOPS(re->ng, nexthop)) { - /* - * So we want to cover 2 types of blackhole - * routes here: - * 1) A normal blackhole route( ala from a static - * install. - * 2) A recursively resolved blackhole route - */ - if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE) { - switch (nexthop->bh_type) { - case BLACKHOLE_ADMINPROHIB: - req.r.rtm_type = RTN_PROHIBIT; - break; - case BLACKHOLE_REJECT: - req.r.rtm_type = RTN_UNREACHABLE; - break; - default: - req.r.rtm_type = RTN_BLACKHOLE; - break; - } - goto skip; - } - if (CHECK_FLAG(nexthop->flags, - NEXTHOP_FLAG_RECURSIVE)) { - if (!setsrc) { - if (family == AF_INET) { - if (nexthop->rmap_src.ipv4 - .s_addr - != 0) { - src.ipv4 = - nexthop->rmap_src - .ipv4; - setsrc = 1; - } else if (nexthop->src.ipv4 - .s_addr - != 0) { - src.ipv4 = - nexthop->src - .ipv4; - setsrc = 1; - } - } else if (family == AF_INET6) { - if (!IN6_IS_ADDR_UNSPECIFIED( - &nexthop->rmap_src - .ipv6)) { - src.ipv6 = - nexthop->rmap_src - .ipv6; - setsrc = 1; - } else if ( - !IN6_IS_ADDR_UNSPECIFIED( - &nexthop->src - .ipv6)) { - src.ipv6 = - nexthop->src - .ipv6; - setsrc = 1; - } - } - } - continue; - } - - if ((cmd == RTM_NEWROUTE - && NEXTHOP_IS_ACTIVE(nexthop->flags)) - || (cmd == RTM_DELROUTE - && CHECK_FLAG(nexthop->flags, - NEXTHOP_FLAG_FIB))) { - routedesc = nexthop->rparent - ? "recursive, single-path" - : "single-path"; - - _netlink_route_build_singlepath( - routedesc, bytelen, nexthop, &req.n, - &req.r, sizeof req, cmd); - nexthop_num++; - break; - } - } - if (setsrc && (cmd == RTM_NEWROUTE)) { - if (family == AF_INET) - addattr_l(&req.n, sizeof req, RTA_PREFSRC, - &src.ipv4, bytelen); - else if (family == AF_INET6) - addattr_l(&req.n, sizeof req, RTA_PREFSRC, - &src.ipv6, bytelen); - } - } else { /* Multipath case */ - char buf[NL_PKT_BUF_SIZE]; - struct rtattr *rta = (void *)buf; - struct rtnexthop *rtnh; - union g_addr *src1 = NULL; - - rta->rta_type = RTA_MULTIPATH; - rta->rta_len = RTA_LENGTH(0); - rtnh = RTA_DATA(rta); - - nexthop_num = 0; - for (ALL_NEXTHOPS(re->ng, nexthop)) { - if (nexthop_num >= multipath_num) - break; - - if (CHECK_FLAG(nexthop->flags, - NEXTHOP_FLAG_RECURSIVE)) { - /* This only works for IPv4 now */ - if (!setsrc) { - if (family == AF_INET) { - if (nexthop->rmap_src.ipv4 - .s_addr - != 0) { - src.ipv4 = - nexthop->rmap_src - .ipv4; - setsrc = 1; - } else if (nexthop->src.ipv4 - .s_addr - != 0) { - src.ipv4 = - nexthop->src - .ipv4; - setsrc = 1; - } - } else if (family == AF_INET6) { - if (!IN6_IS_ADDR_UNSPECIFIED( - &nexthop->rmap_src - .ipv6)) { - src.ipv6 = - nexthop->rmap_src - .ipv6; - setsrc = 1; - } else if ( - !IN6_IS_ADDR_UNSPECIFIED( - &nexthop->src - .ipv6)) { - src.ipv6 = - nexthop->src - .ipv6; - setsrc = 1; - } - } - } - continue; - } - - if ((cmd == RTM_NEWROUTE - && NEXTHOP_IS_ACTIVE(nexthop->flags)) - || (cmd == RTM_DELROUTE - && CHECK_FLAG(nexthop->flags, - NEXTHOP_FLAG_FIB))) { - routedesc = nexthop->rparent - ? "recursive, multipath" - : "multipath"; - nexthop_num++; - - _netlink_route_build_multipath( - routedesc, bytelen, nexthop, rta, rtnh, - &req.r, &src1); - rtnh = RTNH_NEXT(rtnh); - - if (!setsrc && src1) { - if (family == AF_INET) - src.ipv4 = src1->ipv4; - else if (family == AF_INET6) - src.ipv6 = src1->ipv6; - - setsrc = 1; - } - } - } - if (setsrc && (cmd == RTM_NEWROUTE)) { - if (family == AF_INET) - addattr_l(&req.n, sizeof req, RTA_PREFSRC, - &src.ipv4, bytelen); - else if (family == AF_INET6) - addattr_l(&req.n, sizeof req, RTA_PREFSRC, - &src.ipv6, bytelen); - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("Setting source"); - } - - if (rta->rta_len > RTA_LENGTH(0)) - addattr_l(&req.n, NL_PKT_BUF_SIZE, RTA_MULTIPATH, - RTA_DATA(rta), RTA_PAYLOAD(rta)); - } - - /* If there is no useful nexthop then return. */ - if (nexthop_num == 0) { - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "netlink_route_multipath(): No useful nexthop."); - return 0; - } - -skip: - - /* Destination netlink address. */ - memset(&snl, 0, sizeof snl); - snl.nl_family = AF_NETLINK; - - /* Talk to netlink socket. */ - return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, - 0); -} - /* * Routing table change via netlink interface, using a dataplane context object */ -static int netlink_route_multipath_ctx(int cmd, dplane_ctx_h ctx) +static int netlink_route_multipath(int cmd, dplane_ctx_h ctx) { int bytelen; struct sockaddr_nl snl; @@ -2148,56 +1823,6 @@ int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in) return suc; } -enum zebra_dplane_result kernel_route_rib(struct route_node *rn, - const struct prefix *p, - const struct prefix *src_p, - struct route_entry *old, - struct route_entry *new) -{ - int ret = 0; - - assert(old || new); - - if (new) { - if (p->family == AF_INET || v6_rr_semantics) - ret = netlink_route_multipath(RTM_NEWROUTE, p, src_p, - new, (old) ? 1 : 0); - else { - /* - * So v6 route replace semantics are not in - * the kernel at this point as I understand it. - * So let's do a delete than an add. - * In the future once v6 route replace semantics - * are in we can figure out what to do here to - * allow working with old and new kernels. - * - * I'm also intentionally ignoring the failure case - * of the route delete. If that happens yeah we're - * screwed. - */ - if (old) - netlink_route_multipath(RTM_DELROUTE, p, src_p, - old, 0); - ret = netlink_route_multipath(RTM_NEWROUTE, p, src_p, - new, 0); - } - kernel_route_rib_pass_fail(rn, p, new, - (!ret) ? ZEBRA_DPLANE_INSTALL_SUCCESS - : ZEBRA_DPLANE_INSTALL_FAILURE); - return ZEBRA_DPLANE_REQUEST_SUCCESS; - } - - if (old) { - ret = netlink_route_multipath(RTM_DELROUTE, p, src_p, old, 0); - - kernel_route_rib_pass_fail(rn, p, old, - (!ret) ? ZEBRA_DPLANE_DELETE_SUCCESS - : ZEBRA_DPLANE_DELETE_FAILURE); - } - - return ZEBRA_DPLANE_REQUEST_SUCCESS; -} - /* * Update or delete a prefix from the kernel, * using info from a dataplane context. @@ -2230,7 +1855,7 @@ enum zebra_dplane_result kernel_route_update(dplane_ctx_h ctx) * of the route delete. If that happens yeah we're * screwed. */ - (void)netlink_route_multipath_ctx(RTM_DELROUTE, ctx); + (void)netlink_route_multipath(RTM_DELROUTE, ctx); cmd = RTM_NEWROUTE; } @@ -2238,7 +1863,7 @@ enum zebra_dplane_result kernel_route_update(dplane_ctx_h ctx) return ZEBRA_DPLANE_REQUEST_FAILURE; } - ret = netlink_route_multipath_ctx(cmd, ctx); + ret = netlink_route_multipath(cmd, ctx); if ((cmd == RTM_NEWROUTE) && (ret == 0)) { /* Update installed nexthops to signal which have been * installed. diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index d5146c6433..27a0eb26d2 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -444,43 +444,6 @@ done: return res; } -enum zebra_dplane_result kernel_route_rib(struct route_node *rn, - const struct prefix *p, - const struct prefix *src_p, - struct route_entry *old, - struct route_entry *new) -{ - int route = 0; - - if (src_p && src_p->prefixlen) { - flog_warn(EC_ZEBRA_UNSUPPORTED_V6_SRCDEST, - "%s: IPv6 sourcedest routes unsupported!", __func__); - return ZEBRA_DPLANE_REQUEST_FAILURE; - } - - frr_elevate_privs(&zserv_privs) { - if (old) - route |= kernel_rtm(RTM_DELETE, p, - &old->ng, old->metric); - if (new) - route |= kernel_rtm(RTM_ADD, p, &new->ng, new->metric); - } - - if (new) { - kernel_route_rib_pass_fail( - rn, p, new, - (!route) ? ZEBRA_DPLANE_INSTALL_SUCCESS - : ZEBRA_DPLANE_INSTALL_FAILURE); - } else { - kernel_route_rib_pass_fail(rn, p, old, - (!route) - ? ZEBRA_DPLANE_DELETE_SUCCESS - : ZEBRA_DPLANE_DELETE_FAILURE); - } - - return ZEBRA_DPLANE_REQUEST_SUCCESS; -} - int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, int llalen, ns_id_t ns_id) { diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 7a3b902b19..740641b660 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -1092,75 +1092,6 @@ int zebra_rib_labeled_unicast(struct route_entry *re) return 1; } -void kernel_route_rib_pass_fail(struct route_node *rn, const struct prefix *p, - struct route_entry *re, - enum zebra_dplane_status res) -{ - struct nexthop *nexthop; - char buf[PREFIX_STRLEN]; - rib_dest_t *dest; - - dest = rib_dest_from_rnode(rn); - - switch (res) { - case ZEBRA_DPLANE_INSTALL_SUCCESS: - dest->selected_fib = re; - for (ALL_NEXTHOPS(re->ng, nexthop)) { - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - continue; - - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - else - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - } - zsend_route_notify_owner(re, p, ZAPI_ROUTE_INSTALLED); - break; - case ZEBRA_DPLANE_INSTALL_FAILURE: - /* - * I am not sure this is the right thing to do here - * but the code always set selected_fib before - * this assignment was moved here. - */ - dest->selected_fib = re; - - zsend_route_notify_owner(re, p, ZAPI_ROUTE_FAIL_INSTALL); - flog_err(EC_ZEBRA_DP_INSTALL_FAIL, - "%u:%s: Route install failed", re->vrf_id, - prefix2str(p, buf, sizeof(buf))); - break; - case ZEBRA_DPLANE_DELETE_SUCCESS: - /* - * The case where selected_fib is not re is - * when we have received a system route - * that is overriding our installed route - * as such we should leave the selected_fib - * pointer alone - */ - if (dest->selected_fib == re) - dest->selected_fib = NULL; - for (ALL_NEXTHOPS(re->ng, nexthop)) - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - - zsend_route_notify_owner(re, p, ZAPI_ROUTE_REMOVED); - break; - case ZEBRA_DPLANE_DELETE_FAILURE: - /* - * Should we set this to NULL if the - * delete fails? - */ - dest->selected_fib = NULL; - flog_err(EC_ZEBRA_DP_DELETE_FAIL, - "%u:%s: Route Deletion failure", re->vrf_id, - prefix2str(p, buf, sizeof(buf))); - - zsend_route_notify_owner(re, p, ZAPI_ROUTE_REMOVE_FAIL); - break; - case ZEBRA_DPLANE_STATUS_NONE: - break; - } -} - /* Update flag indicates whether this is a "replace" or not. Currently, this * is only used for IPv4. */ From 25779064810c2c8d8d72e961a7f30d42db5b24a9 Mon Sep 17 00:00:00 2001 From: Mark Stapp Date: Wed, 19 Sep 2018 13:25:12 -0400 Subject: [PATCH 18/22] zebra: revise struct names to resolve review comments Use standard type naming and remove use of typedef to resolve some review comments. Signed-off-by: Mark Stapp --- zebra/rt.h | 3 +- zebra/rt_netlink.c | 4 +- zebra/rt_socket.c | 2 +- zebra/zapi_msg.c | 2 +- zebra/zapi_msg.h | 2 +- zebra/zebra_dplane.c | 208 +++++++++++++++++++++---------------------- zebra/zebra_dplane.h | 73 ++++++++------- zebra/zebra_rib.c | 11 +-- 8 files changed, 154 insertions(+), 151 deletions(-) diff --git a/zebra/rt.h b/zebra/rt.h index 6e45514381..70ac6f635c 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -35,7 +35,8 @@ * Update or delete a prefix from the kernel, * using info from a dataplane context. */ -extern enum zebra_dplane_result kernel_route_update(dplane_ctx_h ctx); +extern enum zebra_dplane_result kernel_route_update( + struct zebra_dplane_ctx *ctx); extern int kernel_address_add_ipv4(struct interface *, struct connected *); extern int kernel_address_delete_ipv4(struct interface *, struct connected *); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index d2e01adad3..a65d477f03 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1446,7 +1446,7 @@ static int netlink_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla, /* * Routing table change via netlink interface, using a dataplane context object */ -static int netlink_route_multipath(int cmd, dplane_ctx_h ctx) +static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx) { int bytelen; struct sockaddr_nl snl; @@ -1827,7 +1827,7 @@ int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in) * Update or delete a prefix from the kernel, * using info from a dataplane context. */ -enum zebra_dplane_result kernel_route_update(dplane_ctx_h ctx) +enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx) { int cmd, ret; const struct prefix *p = dplane_ctx_get_dest(ctx); diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index 27a0eb26d2..99a7ca5d55 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -400,7 +400,7 @@ static int kernel_rtm(int cmd, const struct prefix *p, * Update or delete a prefix from the kernel, * using info from a dataplane context struct. */ -enum zebra_dplane_result kernel_route_update(dplane_ctx_h ctx) +enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx) { enum zebra_dplane_result res = ZEBRA_DPLANE_REQUEST_SUCCESS; diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index ba734269d3..ae10555953 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -743,7 +743,7 @@ int zsend_route_notify_owner(struct route_entry *re, const struct prefix *p, /* * Route-owner notification using info from dataplane update context. */ -int zsend_route_notify_owner_ctx(dplane_ctx_h ctx, +int zsend_route_notify_owner_ctx(const struct zebra_dplane_ctx *ctx, enum zapi_route_notify_owner note) { return (route_notify_internal(dplane_ctx_get_dest(ctx), diff --git a/zebra/zapi_msg.h b/zebra/zapi_msg.h index 0dcfd5d747..11b469e144 100644 --- a/zebra/zapi_msg.h +++ b/zebra/zapi_msg.h @@ -70,7 +70,7 @@ extern int zsend_pw_update(struct zserv *client, struct zebra_pw *pw); extern int zsend_route_notify_owner(struct route_entry *re, const struct prefix *p, enum zapi_route_notify_owner note); -extern int zsend_route_notify_owner_ctx(dplane_ctx_h ctx, +extern int zsend_route_notify_owner_ctx(const struct zebra_dplane_ctx *ctx, enum zapi_route_notify_owner note); extern void zsend_rule_notify_owner(struct zebra_pbr_rule *rule, diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 1e90fd15ce..641e73b652 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -38,16 +38,13 @@ DEFINE_MTYPE(ZEBRA, DP_PROV, "Zebra DPlane Provider") # define AOK 0 #endif -/* Validation value for context blocks */ -const uint32_t DPLANE_CTX_MAGIC = 0xb97a557f; - /* Validation check macro for context blocks */ /* #define DPLANE_DEBUG 1 */ #ifdef DPLANE_DEBUG -# define DPLANE_CTX_VALID(p) \ - assert((p) && ((p)->zd_magic == DPLANE_CTX_MAGIC)) +# define DPLANE_CTX_VALID(p) \ + assert((p) != NULL) #else @@ -60,7 +57,7 @@ const uint32_t DPLANE_CTX_MAGIC = 0xb97a557f; * the boundary between the zebra main context (and pthread) and the * dataplane layer (and pthread). */ -struct zebra_dplane_ctx_s { +struct zebra_dplane_ctx { /* Operation code */ enum dplane_op_e zd_op; @@ -114,16 +111,13 @@ struct zebra_dplane_ctx_s { /* TODO -- use fixed array of nexthops, to avoid mallocs? */ /* Embedded list linkage */ - TAILQ_ENTRY(zebra_dplane_ctx_s) zd_q_entries; - - /* Magic validation value */ - uint32_t zd_magic; + TAILQ_ENTRY(zebra_dplane_ctx) zd_q_entries; }; /* * Registration block for one dataplane provider. */ -struct zebra_dplane_provider_s { +struct zebra_dplane_provider { /* Name */ char dp_name[DPLANE_PROVIDER_NAMELEN + 1]; @@ -141,14 +135,14 @@ struct zebra_dplane_provider_s { _Atomic uint64_t dp_error_counter; /* Embedded list linkage */ - TAILQ_ENTRY(zebra_dplane_provider_s) dp_q_providers; + TAILQ_ENTRY(zebra_dplane_provider) dp_q_providers; }; /* * Globals */ -static struct zebra_dplane_globals_s { +static struct zebra_dplane_globals { /* Mutex to control access to dataplane components */ pthread_mutex_t dg_mutex; @@ -162,10 +156,10 @@ static struct zebra_dplane_globals_s { volatile bool dg_run; /* Route-update context queue inbound to the dataplane */ - TAILQ_HEAD(zdg_ctx_q, zebra_dplane_ctx_s) dg_route_ctx_q; + TAILQ_HEAD(zdg_ctx_q, zebra_dplane_ctx) dg_route_ctx_q; /* Ordered list of providers */ - TAILQ_HEAD(zdg_prov_q, zebra_dplane_provider_s) dg_providers_q; + TAILQ_HEAD(zdg_prov_q, zebra_dplane_provider) dg_providers_q; /* Counter used to assign internal ids to providers */ uint32_t dg_provider_id; @@ -184,14 +178,14 @@ static struct zebra_dplane_globals_s { /* Event pointer for pending shutdown check loop */ struct thread *dg_t_shutdown_check; -} zdplane_g; +} zdplane_info; /* * Lock and unlock for interactions with the zebra 'core' */ -#define DPLANE_LOCK() pthread_mutex_lock(&zdplane_g.dg_mutex) +#define DPLANE_LOCK() pthread_mutex_lock(&zdplane_info.dg_mutex) -#define DPLANE_UNLOCK() pthread_mutex_unlock(&zdplane_g.dg_mutex) +#define DPLANE_UNLOCK() pthread_mutex_unlock(&zdplane_info.dg_mutex) /* Prototypes */ static int dplane_route_process(struct thread *event); @@ -203,16 +197,14 @@ static int dplane_route_process(struct thread *event); /* * Allocate a dataplane update context */ -static dplane_ctx_h dplane_ctx_alloc(void) +static struct zebra_dplane_ctx *dplane_ctx_alloc(void) { - struct zebra_dplane_ctx_s *p; + struct zebra_dplane_ctx *p; /* TODO -- just alloc'ing memory, but would like to maintain * a pool */ - p = XCALLOC(MTYPE_DP_CTX, sizeof(struct zebra_dplane_ctx_s)); - if (p) - p->zd_magic = DPLANE_CTX_MAGIC; + p = XCALLOC(MTYPE_DP_CTX, sizeof(struct zebra_dplane_ctx)); return p; } @@ -220,7 +212,7 @@ static dplane_ctx_h dplane_ctx_alloc(void) /* * Free a dataplane results context. */ -static void dplane_ctx_free(dplane_ctx_h *pctx) +static void dplane_ctx_free(struct zebra_dplane_ctx **pctx) { if (pctx) { DPLANE_CTX_VALID(*pctx); @@ -240,9 +232,6 @@ static void dplane_ctx_free(dplane_ctx_h *pctx) nexthops_free((*pctx)->zd_old_ng.nexthop); } - /* Clear validation value */ - (*pctx)->zd_magic = 0; - XFREE(MTYPE_DP_CTX, *pctx); *pctx = NULL; } @@ -251,22 +240,23 @@ static void dplane_ctx_free(dplane_ctx_h *pctx) /* * Return a context block to the dplane module after processing */ -void dplane_ctx_fini(dplane_ctx_h *pctx) +void dplane_ctx_fini(struct zebra_dplane_ctx **pctx) { /* TODO -- enqueue for next provider; for now, just free */ dplane_ctx_free(pctx); } /* Enqueue a context block */ -void dplane_ctx_enqueue_tail(struct dplane_ctx_q_s *q, dplane_ctx_h ctx) +void dplane_ctx_enqueue_tail(struct dplane_ctx_q *q, + const struct zebra_dplane_ctx *ctx) { - TAILQ_INSERT_TAIL(q, ctx, zd_q_entries); + TAILQ_INSERT_TAIL(q, (struct zebra_dplane_ctx *)ctx, zd_q_entries); } /* Dequeue a context block from the head of a list */ -void dplane_ctx_dequeue(struct dplane_ctx_q_s *q, dplane_ctx_h *ctxp) +void dplane_ctx_dequeue(struct dplane_ctx_q *q, struct zebra_dplane_ctx **ctxp) { - dplane_ctx_h ctx = TAILQ_FIRST(q); + struct zebra_dplane_ctx *ctx = TAILQ_FIRST(q); if (ctx) TAILQ_REMOVE(q, ctx, zd_q_entries); @@ -277,14 +267,15 @@ void dplane_ctx_dequeue(struct dplane_ctx_q_s *q, dplane_ctx_h *ctxp) /* * Accessors for information from the context object */ -enum zebra_dplane_result dplane_ctx_get_status(const dplane_ctx_h ctx) +enum zebra_dplane_result dplane_ctx_get_status( + const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); return ctx->zd_status; } -enum dplane_op_e dplane_ctx_get_op(const dplane_ctx_h ctx) +enum dplane_op_e dplane_ctx_get_op(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -335,7 +326,7 @@ const char *dplane_res2str(enum zebra_dplane_result res) return ret; } -const struct prefix *dplane_ctx_get_dest(const dplane_ctx_h ctx) +const struct prefix *dplane_ctx_get_dest(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -343,7 +334,7 @@ const struct prefix *dplane_ctx_get_dest(const dplane_ctx_h ctx) } /* Source prefix is a little special - return NULL for "no src prefix" */ -const struct prefix *dplane_ctx_get_src(const dplane_ctx_h ctx) +const struct prefix *dplane_ctx_get_src(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -355,154 +346,157 @@ const struct prefix *dplane_ctx_get_src(const dplane_ctx_h ctx) } } -bool dplane_ctx_is_update(const dplane_ctx_h ctx) +bool dplane_ctx_is_update(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); return ctx->zd_is_update; } -uint32_t dplane_ctx_get_seq(const dplane_ctx_h ctx) +uint32_t dplane_ctx_get_seq(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); return ctx->zd_seq; } -uint32_t dplane_ctx_get_old_seq(const dplane_ctx_h ctx) +uint32_t dplane_ctx_get_old_seq(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); return ctx->zd_old_seq; } -vrf_id_t dplane_ctx_get_vrf(const dplane_ctx_h ctx) +vrf_id_t dplane_ctx_get_vrf(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); return ctx->zd_vrf_id; } -int dplane_ctx_get_type(const dplane_ctx_h ctx) +int dplane_ctx_get_type(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); return ctx->zd_type; } -int dplane_ctx_get_old_type(const dplane_ctx_h ctx) +int dplane_ctx_get_old_type(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); return ctx->zd_old_type; } -afi_t dplane_ctx_get_afi(const dplane_ctx_h ctx) +afi_t dplane_ctx_get_afi(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); return ctx->zd_afi; } -safi_t dplane_ctx_get_safi(const dplane_ctx_h ctx) +safi_t dplane_ctx_get_safi(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); return ctx->zd_safi; } -uint32_t dplane_ctx_get_table(const dplane_ctx_h ctx) +uint32_t dplane_ctx_get_table(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); return ctx->zd_table_id; } -route_tag_t dplane_ctx_get_tag(const dplane_ctx_h ctx) +route_tag_t dplane_ctx_get_tag(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); return ctx->zd_tag; } -route_tag_t dplane_ctx_get_old_tag(const dplane_ctx_h ctx) +route_tag_t dplane_ctx_get_old_tag(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); return ctx->zd_old_tag; } -uint16_t dplane_ctx_get_instance(const dplane_ctx_h ctx) +uint16_t dplane_ctx_get_instance(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); return ctx->zd_instance; } -uint16_t dplane_ctx_get_old_instance(const dplane_ctx_h ctx) +uint16_t dplane_ctx_get_old_instance(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); return ctx->zd_instance; } -uint32_t dplane_ctx_get_metric(const dplane_ctx_h ctx) +uint32_t dplane_ctx_get_metric(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); return ctx->zd_metric; } -uint32_t dplane_ctx_get_old_metric(const dplane_ctx_h ctx) +uint32_t dplane_ctx_get_old_metric(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); return ctx->zd_old_metric; } -uint32_t dplane_ctx_get_mtu(const dplane_ctx_h ctx) +uint32_t dplane_ctx_get_mtu(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); return ctx->zd_mtu; } -uint32_t dplane_ctx_get_nh_mtu(const dplane_ctx_h ctx) +uint32_t dplane_ctx_get_nh_mtu(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); return ctx->zd_nexthop_mtu; } -uint8_t dplane_ctx_get_distance(const dplane_ctx_h ctx) +uint8_t dplane_ctx_get_distance(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); return ctx->zd_distance; } -uint8_t dplane_ctx_get_old_distance(const dplane_ctx_h ctx) +uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); return ctx->zd_old_distance; } -const struct nexthop_group *dplane_ctx_get_ng(const dplane_ctx_h ctx) +const struct nexthop_group *dplane_ctx_get_ng( + const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); return &(ctx->zd_ng); } -const struct nexthop_group *dplane_ctx_get_old_ng(const dplane_ctx_h ctx) +const struct nexthop_group *dplane_ctx_get_old_ng( + const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); return &(ctx->zd_old_ng); } -const struct zebra_dplane_info *dplane_ctx_get_ns(const dplane_ctx_h ctx) +const struct zebra_dplane_info *dplane_ctx_get_ns( + const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -516,7 +510,7 @@ const struct zebra_dplane_info *dplane_ctx_get_ns(const dplane_ctx_h ctx) /* * Initialize a context block for a route update from zebra data structs. */ -static int dplane_ctx_route_init(dplane_ctx_h ctx, +static int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, struct route_node *rn, struct route_entry *re) @@ -606,20 +600,21 @@ done: * Enqueue a new route update, * and ensure an event is active for the dataplane thread. */ -static int dplane_route_enqueue(dplane_ctx_h ctx) +static int dplane_route_enqueue(struct zebra_dplane_ctx *ctx) { int ret = EINVAL; /* Enqueue for processing by the dataplane thread */ DPLANE_LOCK(); { - TAILQ_INSERT_TAIL(&zdplane_g.dg_route_ctx_q, ctx, zd_q_entries); + TAILQ_INSERT_TAIL(&zdplane_info.dg_route_ctx_q, ctx, + zd_q_entries); } DPLANE_UNLOCK(); /* Ensure that an event for the dataplane thread is active */ - thread_add_event(zdplane_g.dg_master, dplane_route_process, NULL, 0, - &zdplane_g.dg_t_update); + thread_add_event(zdplane_info.dg_master, dplane_route_process, NULL, 0, + &zdplane_info.dg_t_update); ret = AOK; @@ -629,15 +624,15 @@ static int dplane_route_enqueue(dplane_ctx_h ctx) /* * Attempt to dequeue a route-update block */ -static dplane_ctx_h dplane_route_dequeue(void) +static struct zebra_dplane_ctx *dplane_route_dequeue(void) { - dplane_ctx_h ctx = NULL; + struct zebra_dplane_ctx *ctx = NULL; DPLANE_LOCK(); { - ctx = TAILQ_FIRST(&zdplane_g.dg_route_ctx_q); + ctx = TAILQ_FIRST(&zdplane_info.dg_route_ctx_q); if (ctx) { - TAILQ_REMOVE(&zdplane_g.dg_route_ctx_q, + TAILQ_REMOVE(&zdplane_info.dg_route_ctx_q, ctx, zd_q_entries); } } @@ -657,7 +652,7 @@ dplane_route_update_internal(struct route_node *rn, { enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; int ret = EINVAL; - dplane_ctx_h ctx = NULL; + struct zebra_dplane_ctx *ctx = NULL; /* Obtain context block */ ctx = dplane_ctx_alloc(); @@ -700,24 +695,24 @@ dplane_route_update_internal(struct route_node *rn, done: /* Update counters */ - atomic_fetch_add_explicit(&zdplane_g.dg_routes_in, 1, + atomic_fetch_add_explicit(&zdplane_info.dg_routes_in, 1, memory_order_relaxed); if (ret == AOK) { uint32_t high, curr; - curr = atomic_fetch_add_explicit(&zdplane_g.dg_routes_queued, 1, - memory_order_seq_cst); + curr = atomic_fetch_add_explicit(&zdplane_info.dg_routes_queued, + 1, memory_order_seq_cst); /* We don't have add_and_fetch - sigh */ curr++; /* Maybe update high-water counter also */ - high = atomic_load_explicit(&zdplane_g.dg_routes_queued_max, + high = atomic_load_explicit(&zdplane_info.dg_routes_queued_max, memory_order_seq_cst); while (high < curr) { if (atomic_compare_exchange_weak_explicit( - &zdplane_g.dg_routes_queued_max, + &zdplane_info.dg_routes_queued_max, &high, curr, memory_order_seq_cst, memory_order_seq_cst)) @@ -726,7 +721,7 @@ done: result = ZEBRA_DPLANE_REQUEST_QUEUED; } else if (ctx) { - atomic_fetch_add_explicit(&zdplane_g.dg_route_errors, 1, + atomic_fetch_add_explicit(&zdplane_info.dg_route_errors, 1, memory_order_relaxed); dplane_ctx_free(&ctx); } @@ -794,11 +789,11 @@ done: static int dplane_route_process(struct thread *event) { enum zebra_dplane_result res; - dplane_ctx_h ctx; + struct zebra_dplane_ctx *ctx; while (1) { /* Check for shutdown */ - if (!zdplane_g.dg_run) + if (!zdplane_info.dg_run) break; /* TODO -- limit number of updates per cycle? */ @@ -807,7 +802,7 @@ static int dplane_route_process(struct thread *event) break; /* Update counter */ - atomic_fetch_sub_explicit(&zdplane_g.dg_routes_queued, 1, + atomic_fetch_sub_explicit(&zdplane_info.dg_routes_queued, 1, memory_order_relaxed); if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { @@ -827,13 +822,13 @@ static int dplane_route_process(struct thread *event) res = kernel_route_update(ctx); if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) - atomic_fetch_add_explicit(&zdplane_g.dg_route_errors, 1, - memory_order_relaxed); + atomic_fetch_add_explicit(&zdplane_info.dg_route_errors, + 1, memory_order_relaxed); ctx->zd_status = res; /* Enqueue result to zebra main context */ - (*zdplane_g.dg_results_cb)(ctx); + (*zdplane_info.dg_results_cb)(ctx); ctx = NULL; } @@ -851,13 +846,13 @@ int dplane_show_helper(struct vty *vty, bool detailed) /* Using atomics because counters are being changed in different * contexts. */ - incoming = atomic_load_explicit(&zdplane_g.dg_routes_in, + incoming = atomic_load_explicit(&zdplane_info.dg_routes_in, memory_order_relaxed); - queued = atomic_load_explicit(&zdplane_g.dg_routes_queued, + queued = atomic_load_explicit(&zdplane_info.dg_routes_queued, memory_order_relaxed); - queue_max = atomic_load_explicit(&zdplane_g.dg_routes_queued_max, + queue_max = atomic_load_explicit(&zdplane_info.dg_routes_queued_max, memory_order_relaxed); - errs = atomic_load_explicit(&zdplane_g.dg_route_errors, + errs = atomic_load_explicit(&zdplane_info.dg_route_errors, memory_order_relaxed); vty_out(vty, "Route updates: %"PRIu64"\n", incoming); @@ -888,7 +883,7 @@ int dplane_provider_register(const char *name, dplane_provider_fini_fp fini_fp) { int ret = 0; - struct zebra_dplane_provider_s *p, *last; + struct zebra_dplane_provider *p, *last; /* Validate */ if (fp == NULL) { @@ -903,7 +898,7 @@ int dplane_provider_register(const char *name, } /* Allocate and init new provider struct */ - p = XCALLOC(MTYPE_DP_PROV, sizeof(struct zebra_dplane_provider_s)); + p = XCALLOC(MTYPE_DP_PROV, sizeof(struct zebra_dplane_provider)); if (p == NULL) { ret = ENOMEM; goto done; @@ -919,10 +914,10 @@ int dplane_provider_register(const char *name, /* Lock the lock - the dplane pthread may be running */ DPLANE_LOCK(); - p->dp_id = ++zdplane_g.dg_provider_id; + p->dp_id = ++zdplane_info.dg_provider_id; /* Insert into list ordered by priority */ - TAILQ_FOREACH(last, &zdplane_g.dg_providers_q, dp_q_providers) { + TAILQ_FOREACH(last, &zdplane_info.dg_providers_q, dp_q_providers) { if (last->dp_priority > p->dp_priority) break; } @@ -930,7 +925,8 @@ int dplane_provider_register(const char *name, if (last) TAILQ_INSERT_BEFORE(last, p, dp_q_providers); else - TAILQ_INSERT_TAIL(&zdplane_g.dg_providers_q, p, dp_q_providers); + TAILQ_INSERT_TAIL(&zdplane_info.dg_providers_q, p, + dp_q_providers); /* And unlock */ DPLANE_UNLOCK(); @@ -944,7 +940,7 @@ done: */ int dplane_results_register(dplane_results_fp fp) { - zdplane_g.dg_results_cb = fp; + zdplane_info.dg_results_cb = fp; return AOK; } @@ -953,21 +949,21 @@ int dplane_results_register(dplane_results_fp fp) */ static void zebra_dplane_init_internal(struct zebra_t *zebra) { - memset(&zdplane_g, 0, sizeof(zdplane_g)); + memset(&zdplane_info, 0, sizeof(zdplane_info)); - pthread_mutex_init(&zdplane_g.dg_mutex, NULL); + pthread_mutex_init(&zdplane_info.dg_mutex, NULL); - TAILQ_INIT(&zdplane_g.dg_route_ctx_q); - TAILQ_INIT(&zdplane_g.dg_providers_q); + TAILQ_INIT(&zdplane_info.dg_route_ctx_q); + TAILQ_INIT(&zdplane_info.dg_providers_q); /* TODO -- register default kernel 'provider' during init */ - zdplane_g.dg_run = true; + zdplane_info.dg_run = true; /* TODO -- start dataplane pthread. We're using the zebra * core/main thread temporarily */ - zdplane_g.dg_master = zebra->master; + zdplane_info.dg_master = zebra->master; } /* Indicates zebra shutdown/exit is in progress. Some operations may be @@ -975,7 +971,7 @@ static void zebra_dplane_init_internal(struct zebra_t *zebra) */ bool dplane_is_in_shutdown(void) { - return zdplane_g.dg_is_shutdown; + return zdplane_info.dg_is_shutdown; } /* @@ -990,7 +986,7 @@ void zebra_dplane_pre_finish(void) if (IS_ZEBRA_DEBUG_DPLANE) zlog_debug("Zebra dataplane pre-fini called"); - zdplane_g.dg_is_shutdown = true; + zdplane_info.dg_is_shutdown = true; /* Notify provider(s) of pending shutdown */ } @@ -1001,12 +997,12 @@ void zebra_dplane_pre_finish(void) */ static bool dplane_work_pending(void) { - dplane_ctx_h ctx; + struct zebra_dplane_ctx *ctx; /* TODO -- just checking incoming/pending work for now */ DPLANE_LOCK(); { - ctx = TAILQ_FIRST(&zdplane_g.dg_route_ctx_q); + ctx = TAILQ_FIRST(&zdplane_info.dg_route_ctx_q); } DPLANE_UNLOCK(); @@ -1027,10 +1023,10 @@ static int dplane_check_shutdown_status(struct thread *event) if (dplane_work_pending()) { /* Reschedule dplane check on a short timer */ - thread_add_timer_msec(zdplane_g.dg_master, + thread_add_timer_msec(zdplane_info.dg_master, dplane_check_shutdown_status, NULL, 100, - &zdplane_g.dg_t_shutdown_check); + &zdplane_info.dg_t_shutdown_check); /* TODO - give up and stop waiting after a short time? */ @@ -1059,9 +1055,9 @@ void zebra_dplane_finish(void) if (IS_ZEBRA_DEBUG_DPLANE) zlog_debug("Zebra dataplane fini called"); - thread_add_event(zdplane_g.dg_master, + thread_add_event(zdplane_info.dg_master, dplane_check_shutdown_status, NULL, 0, - &zdplane_g.dg_t_shutdown_check); + &zdplane_info.dg_t_shutdown_check); } /* @@ -1075,9 +1071,9 @@ void zebra_dplane_shutdown(void) /* Stop dplane thread, if it's running */ - zdplane_g.dg_run = false; + zdplane_info.dg_run = false; - THREAD_OFF(zdplane_g.dg_t_update); + THREAD_OFF(zdplane_info.dg_t_update); /* TODO */ /* frr_pthread_stop(...) */ diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 0962563135..65f5350b2a 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -105,70 +105,75 @@ enum dplane_op_e { }; /* - * Opaque context block used to exchange info between the main zebra + * The dataplane context struct is used to exchange info between the main zebra * context and the dataplane module(s). If these are two independent pthreads, * they cannot share existing global data structures safely. */ -typedef struct zebra_dplane_ctx_s *dplane_ctx_h; /* Define a tailq list type for context blocks. The list is exposed/public, * but the internal linkage in the context struct is private, so there * are accessor apis that support enqueue and dequeue. */ -TAILQ_HEAD(dplane_ctx_q_s, zebra_dplane_ctx_s); +TAILQ_HEAD(dplane_ctx_q, zebra_dplane_ctx); /* Return a dataplane results context block after use; the caller's pointer will * be cleared. */ -void dplane_ctx_fini(dplane_ctx_h *pctx); +void dplane_ctx_fini(struct zebra_dplane_ctx **pctx); /* Enqueue a context block to caller's tailq. This just exists so that the * context struct can remain opaque. */ -void dplane_ctx_enqueue_tail(struct dplane_ctx_q_s *q, dplane_ctx_h ctx); +void dplane_ctx_enqueue_tail(struct dplane_ctx_q *q, + const struct zebra_dplane_ctx *ctx); /* Dequeue a context block from the head of caller's tailq */ -void dplane_ctx_dequeue(struct dplane_ctx_q_s *q, dplane_ctx_h *ctxp); +void dplane_ctx_dequeue(struct dplane_ctx_q *q, struct zebra_dplane_ctx **ctxp); /* * Accessors for information from the context object */ -enum zebra_dplane_result dplane_ctx_get_status(const dplane_ctx_h ctx); +enum zebra_dplane_result dplane_ctx_get_status( + const struct zebra_dplane_ctx *ctx); const char *dplane_res2str(enum zebra_dplane_result res); -enum dplane_op_e dplane_ctx_get_op(const dplane_ctx_h ctx); +enum dplane_op_e dplane_ctx_get_op(const struct zebra_dplane_ctx *ctx); const char *dplane_op2str(enum dplane_op_e op); -const struct prefix *dplane_ctx_get_dest(const dplane_ctx_h ctx); +const struct prefix *dplane_ctx_get_dest(const struct zebra_dplane_ctx *ctx); /* Source prefix is a little special - use convention to return NULL * to mean "no src prefix" */ -const struct prefix *dplane_ctx_get_src(const dplane_ctx_h ctx); +const struct prefix *dplane_ctx_get_src(const struct zebra_dplane_ctx *ctx); -bool dplane_ctx_is_update(const dplane_ctx_h ctx); -uint32_t dplane_ctx_get_seq(const dplane_ctx_h ctx); -uint32_t dplane_ctx_get_old_seq(const dplane_ctx_h ctx); -vrf_id_t dplane_ctx_get_vrf(const dplane_ctx_h ctx); -int dplane_ctx_get_type(const dplane_ctx_h ctx); -int dplane_ctx_get_old_type(const dplane_ctx_h ctx); -afi_t dplane_ctx_get_afi(const dplane_ctx_h ctx); -safi_t dplane_ctx_get_safi(const dplane_ctx_h ctx); -uint32_t dplane_ctx_get_table(const dplane_ctx_h ctx); -route_tag_t dplane_ctx_get_tag(const dplane_ctx_h ctx); -route_tag_t dplane_ctx_get_old_tag(const dplane_ctx_h ctx); -uint16_t dplane_ctx_get_instance(const dplane_ctx_h ctx); -uint16_t dplane_ctx_get_old_instance(const dplane_ctx_h ctx); -uint32_t dplane_ctx_get_metric(const dplane_ctx_h ctx); -uint32_t dplane_ctx_get_old_metric(const dplane_ctx_h ctx); -uint32_t dplane_ctx_get_mtu(const dplane_ctx_h ctx); -uint32_t dplane_ctx_get_nh_mtu(const dplane_ctx_h ctx); -uint8_t dplane_ctx_get_distance(const dplane_ctx_h ctx); -uint8_t dplane_ctx_get_old_distance(const dplane_ctx_h ctx); +bool dplane_ctx_is_update(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_get_seq(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_get_old_seq(const struct zebra_dplane_ctx *ctx); +vrf_id_t dplane_ctx_get_vrf(const struct zebra_dplane_ctx *ctx); +int dplane_ctx_get_type(const struct zebra_dplane_ctx *ctx); +int dplane_ctx_get_old_type(const struct zebra_dplane_ctx *ctx); +afi_t dplane_ctx_get_afi(const struct zebra_dplane_ctx *ctx); +safi_t dplane_ctx_get_safi(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_get_table(const struct zebra_dplane_ctx *ctx); +route_tag_t dplane_ctx_get_tag(const struct zebra_dplane_ctx *ctx); +route_tag_t dplane_ctx_get_old_tag(const struct zebra_dplane_ctx *ctx); +uint16_t dplane_ctx_get_instance(const struct zebra_dplane_ctx *ctx); +uint16_t dplane_ctx_get_old_instance(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_get_metric(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_get_old_metric(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_get_mtu(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_get_nh_mtu(const struct zebra_dplane_ctx *ctx); +uint8_t dplane_ctx_get_distance(const struct zebra_dplane_ctx *ctx); +uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx *ctx); -const struct nexthop_group *dplane_ctx_get_ng(const dplane_ctx_h ctx); -const struct zebra_dplane_info *dplane_ctx_get_ns(const dplane_ctx_h ctx); -const struct nexthop_group *dplane_ctx_get_old_ng(const dplane_ctx_h ctx); +const struct nexthop_group *dplane_ctx_get_ng( + const struct zebra_dplane_ctx *ctx); +const struct nexthop_group *dplane_ctx_get_old_ng( + const struct zebra_dplane_ctx *ctx); + +const struct zebra_dplane_info *dplane_ctx_get_ns( + const struct zebra_dplane_ctx *ctx); /* Indicates zebra shutdown/exit is in progress. Some operations may be * simplified or skipped during shutdown processing. @@ -217,7 +222,7 @@ enum dplane_provider_prio_e { }; /* Provider's entry-point to process a context block */ -typedef int (*dplane_provider_process_fp)(dplane_ctx_h ctx); +typedef int (*dplane_provider_process_fp)(struct zebra_dplane_ctx *ctx); /* Provider's entry-point for shutdown and cleanup */ typedef int (*dplane_provider_fini_fp)(void); @@ -231,7 +236,7 @@ int dplane_provider_register(const char *name, /* * Results are returned to zebra core via a callback */ -typedef int (*dplane_results_fp)(const dplane_ctx_h ctx); +typedef int (*dplane_results_fp)(const struct zebra_dplane_ctx *ctx); /* * Zebra registers a results callback with the dataplane. The callback is diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 740641b660..a080bba659 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -60,7 +60,7 @@ */ static pthread_mutex_t dplane_mutex; static struct thread *t_dplane; -static struct dplane_ctx_q_s rib_dplane_q; +static struct dplane_ctx_q rib_dplane_q; DEFINE_HOOK(rib_update, (struct route_node * rn, const char *reason), (rn, reason)) @@ -1818,7 +1818,8 @@ static void rib_process(struct route_node *rn) * Utility to match route with dplane context data */ static bool rib_route_match_ctx(const struct route_entry *re, - const dplane_ctx_h ctx, bool is_update) + const struct zebra_dplane_ctx *ctx, + bool is_update) { bool result = false; @@ -1875,7 +1876,7 @@ done: * TODO - WIP version of route-update processing after async dataplane * update. */ -static void rib_process_after(dplane_ctx_h ctx) +static void rib_process_after(struct zebra_dplane_ctx *ctx) { struct route_table *table = NULL; struct zebra_vrf *zvrf = NULL; @@ -3240,7 +3241,7 @@ void rib_close_table(struct route_table *table) */ static int rib_process_dplane_results(struct thread *thread) { - dplane_ctx_h ctx; + struct zebra_dplane_ctx *ctx; do { /* Take lock controlling queue of results */ @@ -3269,7 +3270,7 @@ static int rib_process_dplane_results(struct thread *thread) * the dataplane pthread. We enqueue the results here for processing by * the main thread later. */ -static int rib_dplane_results(dplane_ctx_h ctx) +static int rib_dplane_results(const struct zebra_dplane_ctx *ctx) { /* Take lock controlling queue of results */ pthread_mutex_lock(&dplane_mutex); From 91f16812585a6f579345afc25e709a7e82d8a3ec Mon Sep 17 00:00:00 2001 From: Mark Stapp Date: Fri, 21 Sep 2018 14:54:02 -0400 Subject: [PATCH 19/22] zebra: limit queued route updates Impose a configurable limit on the number of route updates that can be queued towards the dataplane subsystem. Signed-off-by: Mark Stapp --- zebra/zebra_dplane.c | 89 +++++++++++++++++++++++++++++++------------- zebra/zebra_dplane.h | 11 ++++++ zebra/zebra_rib.c | 19 +++++++++- zebra/zebra_vty.c | 34 +++++++++++++++++ 4 files changed, 126 insertions(+), 27 deletions(-) diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 641e73b652..9d75ae940a 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -38,6 +38,10 @@ DEFINE_MTYPE(ZEBRA, DP_PROV, "Zebra DPlane Provider") # define AOK 0 #endif +/* Default value for max queued incoming updates */ +const uint32_t DPLANE_DEFAULT_MAX_QUEUED = 200; + + /* Validation check macro for context blocks */ /* #define DPLANE_DEBUG 1 */ @@ -164,6 +168,9 @@ static struct zebra_dplane_globals { /* Counter used to assign internal ids to providers */ uint32_t dg_provider_id; + /* Limit number of pending, unprocessed updates */ + _Atomic uint32_t dg_max_queued_updates; + _Atomic uint64_t dg_routes_in; _Atomic uint32_t dg_routes_queued; _Atomic uint32_t dg_routes_queued_max; @@ -507,6 +514,37 @@ const struct zebra_dplane_info *dplane_ctx_get_ns( * End of dplane context accessors */ +/* + * Retrieve the limit on the number of pending, unprocessed updates. + */ +uint32_t dplane_get_in_queue_limit(void) +{ + return atomic_load_explicit(&zdplane_info.dg_max_queued_updates, + memory_order_relaxed); +} + +/* + * Configure limit on the number of pending, queued updates. + */ +void dplane_set_in_queue_limit(uint32_t limit, bool set) +{ + /* Reset to default on 'unset' */ + if (!set) + limit = DPLANE_DEFAULT_MAX_QUEUED; + + atomic_store_explicit(&zdplane_info.dg_max_queued_updates, limit, + memory_order_relaxed); +} + +/* + * Retrieve the current queue depth of incoming, unprocessed updates + */ +uint32_t dplane_get_in_queue_len(void) +{ + return atomic_load_explicit(&zdplane_info.dg_routes_queued, + memory_order_seq_cst); +} + /* * Initialize a context block for a route update from zebra data structs. */ @@ -603,6 +641,7 @@ done: static int dplane_route_enqueue(struct zebra_dplane_ctx *ctx) { int ret = EINVAL; + uint32_t high, curr; /* Enqueue for processing by the dataplane thread */ DPLANE_LOCK(); @@ -612,6 +651,21 @@ static int dplane_route_enqueue(struct zebra_dplane_ctx *ctx) } DPLANE_UNLOCK(); + curr = atomic_add_fetch_explicit(&zdplane_info.dg_routes_queued, + 1, memory_order_seq_cst); + + /* Maybe update high-water counter also */ + high = atomic_load_explicit(&zdplane_info.dg_routes_queued_max, + memory_order_seq_cst); + while (high < curr) { + if (atomic_compare_exchange_weak_explicit( + &zdplane_info.dg_routes_queued_max, + &high, curr, + memory_order_seq_cst, + memory_order_seq_cst)) + break; + } + /* Ensure that an event for the dataplane thread is active */ thread_add_event(zdplane_info.dg_master, dplane_route_process, NULL, 0, &zdplane_info.dg_t_update); @@ -694,33 +748,13 @@ dplane_route_update_internal(struct route_node *rn, } done: - /* Update counters */ + /* Update counter */ atomic_fetch_add_explicit(&zdplane_info.dg_routes_in, 1, memory_order_relaxed); - if (ret == AOK) { - uint32_t high, curr; - - curr = atomic_fetch_add_explicit(&zdplane_info.dg_routes_queued, - 1, memory_order_seq_cst); - - /* We don't have add_and_fetch - sigh */ - curr++; - - /* Maybe update high-water counter also */ - high = atomic_load_explicit(&zdplane_info.dg_routes_queued_max, - memory_order_seq_cst); - while (high < curr) { - if (atomic_compare_exchange_weak_explicit( - &zdplane_info.dg_routes_queued_max, - &high, curr, - memory_order_seq_cst, - memory_order_seq_cst)) - break; - } - + if (ret == AOK) result = ZEBRA_DPLANE_REQUEST_QUEUED; - } else if (ctx) { + else if (ctx) { atomic_fetch_add_explicit(&zdplane_info.dg_route_errors, 1, memory_order_relaxed); dplane_ctx_free(&ctx); @@ -828,7 +862,7 @@ static int dplane_route_process(struct thread *event) ctx->zd_status = res; /* Enqueue result to zebra main context */ - (*zdplane_info.dg_results_cb)(ctx); + zdplane_info.dg_results_cb(ctx); ctx = NULL; } @@ -841,13 +875,15 @@ static int dplane_route_process(struct thread *event) */ int dplane_show_helper(struct vty *vty, bool detailed) { - uint64_t queued, queue_max, errs, incoming; + uint64_t queued, limit, queue_max, errs, incoming; /* Using atomics because counters are being changed in different * contexts. */ incoming = atomic_load_explicit(&zdplane_info.dg_routes_in, memory_order_relaxed); + limit = atomic_load_explicit(&zdplane_info.dg_max_queued_updates, + memory_order_relaxed); queued = atomic_load_explicit(&zdplane_info.dg_routes_queued, memory_order_relaxed); queue_max = atomic_load_explicit(&zdplane_info.dg_routes_queued_max, @@ -857,6 +893,7 @@ int dplane_show_helper(struct vty *vty, bool detailed) vty_out(vty, "Route updates: %"PRIu64"\n", incoming); vty_out(vty, "Route update errors: %"PRIu64"\n", errs); + vty_out(vty, "Route update queue limit: %"PRIu64"\n", limit); vty_out(vty, "Route update queue depth: %"PRIu64"\n", queued); vty_out(vty, "Route update queue max: %"PRIu64"\n", queue_max); @@ -956,6 +993,8 @@ static void zebra_dplane_init_internal(struct zebra_t *zebra) TAILQ_INIT(&zdplane_info.dg_route_ctx_q); TAILQ_INIT(&zdplane_info.dg_providers_q); + zdplane_info.dg_max_queued_updates = DPLANE_DEFAULT_MAX_QUEUED; + /* TODO -- register default kernel 'provider' during init */ zdplane_info.dg_run = true; diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 65f5350b2a..999e0f39e4 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -193,6 +193,17 @@ enum zebra_dplane_result dplane_route_update(struct route_node *rn, enum zebra_dplane_result dplane_route_delete(struct route_node *rn, struct route_entry *re); +/* Retrieve the limit on the number of pending, unprocessed updates. */ +uint32_t dplane_get_in_queue_limit(void); + +/* Configure limit on the number of pending, queued updates. If 'unset', reset + * to default value. + */ +void dplane_set_in_queue_limit(uint32_t limit, bool set); + +/* Retrieve the current queue depth of incoming, unprocessed updates */ +uint32_t dplane_get_in_queue_len(void); + /* * Vty/cli apis */ diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index a080bba659..0d1a149694 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -1873,8 +1873,7 @@ done: } /* - * TODO - WIP version of route-update processing after async dataplane - * update. + * Route-update results processing after async dataplane update. */ static void rib_process_after(struct zebra_dplane_ctx *ctx) { @@ -2185,6 +2184,22 @@ static wq_item_status meta_queue_process(struct work_queue *dummy, void *data) { struct meta_queue *mq = data; unsigned i; + uint32_t queue_len, queue_limit; + + /* Ensure there's room for more dataplane updates */ + queue_limit = dplane_get_in_queue_limit(); + queue_len = dplane_get_in_queue_len(); + if (queue_len > queue_limit) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("meta_queue_process: dplane queue len %u, " + "limit %u, retrying", queue_len, queue_limit); + + /* Ensure that the meta-queue is actually enqueued */ + if (work_queue_empty(zebrad.ribq)) + work_queue_add(zebrad.ribq, zebrad.mq); + + return WQ_QUEUE_BLOCKED; + } for (i = 0; i < MQ_SIZE; i++) if (process_subq(mq->subq[i], i)) { diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index d9881b98c5..14ca813436 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -2604,6 +2604,38 @@ DEFUN (show_dataplane_providers, return dplane_show_provs_helper(vty, detailed); } +/* Configure dataplane incoming queue limit */ +DEFUN (zebra_dplane_queue_limit, + zebra_dplane_queue_limit_cmd, + "zebra dplane limit (0-10000)", + ZEBRA_STR + "Zebra dataplane\n" + "Limit incoming queued updates\n" + "Number of queued updates\n") +{ + uint32_t limit = 0; + + limit = strtoul(argv[3]->arg, NULL, 10); + + dplane_set_in_queue_limit(limit, true); + + return CMD_SUCCESS; +} + +/* Reset dataplane queue limit to default value */ +DEFUN (no_zebra_dplane_queue_limit, + no_zebra_dplane_queue_limit_cmd, + "no zebra dplane limit [(0-10000)]", + NO_STR + ZEBRA_STR + "Zebra dataplane\n" + "Limit incoming queued updates\n" + "Number of queued updates\n") +{ + dplane_set_in_queue_limit(0, false); + + return CMD_SUCCESS; +} /* Table configuration write function. */ static int config_write_table(struct vty *vty) @@ -2737,4 +2769,6 @@ void zebra_vty_init(void) install_element(VIEW_NODE, &show_dataplane_cmd); install_element(VIEW_NODE, &show_dataplane_providers_cmd); + install_element(CONFIG_NODE, &zebra_dplane_queue_limit_cmd); + install_element(CONFIG_NODE, &no_zebra_dplane_queue_limit_cmd); } From 71077c48607aa9cf466958989f512e2d695307a2 Mon Sep 17 00:00:00 2001 From: Mark Stapp Date: Fri, 21 Sep 2018 16:21:15 -0400 Subject: [PATCH 20/22] zebra: improve safety of netns_notify_close() Additional check to ensure the notify event is still valid before calling the thread lib api. Signed-off-by: Mark Stapp --- zebra/zebra_netns_notify.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c index 2608ffd7a1..4d2aefa236 100644 --- a/zebra/zebra_netns_notify.c +++ b/zebra/zebra_netns_notify.c @@ -353,8 +353,11 @@ void zebra_ns_notify_close(void) if (zebra_netns_notify_current->u.fd > 0) fd = zebra_netns_notify_current->u.fd; - thread_cancel(zebra_netns_notify_current); - /* auto-removal of inotify items */ + + if (zebra_netns_notify_current->master != NULL) + thread_cancel(zebra_netns_notify_current); + + /* auto-removal of notify items */ if (fd > 0) close(fd); } From 8b962e77595b499be610aee5f8ce6132f062affd Mon Sep 17 00:00:00 2001 From: Mark Stapp Date: Wed, 26 Sep 2018 09:34:43 -0400 Subject: [PATCH 21/22] zebra: rebase dataplane, align with master Rebase and pick up dataplane changes on master, including renamed structs and enums. Signed-off-by: Mark Stapp --- zebra/kernel_netlink.c | 4 +--- zebra/kernel_netlink.h | 5 ++--- zebra/zebra_dplane.c | 4 ++-- zebra/zebra_ns.h | 34 ---------------------------------- zebra/zebra_rib.c | 4 ++-- 5 files changed, 7 insertions(+), 44 deletions(-) diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 0a58c2e262..0772c59b92 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -934,7 +934,7 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int), * * filter -> The filter to read final results from kernel * nlmsghdr -> The data to send to the kernel - * zns_info -> The netlink socket information + * dp_info -> The dataplane and netlink socket information * startup -> Are we reading in under startup conditions * This is passed through eventually to filter. */ @@ -990,7 +990,6 @@ int netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), return -1; } - /* * Get reply from netlink socket. * The reply should either be an acknowlegement or an error. @@ -1001,7 +1000,6 @@ int netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), /* * Synchronous version of netlink_talk_info. Converts args to suit the * common version, which is suitable for both sync and async use. - * */ int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), struct nlmsghdr *n, struct nlsock *nl, struct zebra_ns *zns, diff --git a/zebra/kernel_netlink.h b/zebra/kernel_netlink.h index f3de011b66..9918729eb6 100644 --- a/zebra/kernel_netlink.h +++ b/zebra/kernel_netlink.h @@ -53,7 +53,7 @@ extern void netlink_read_init(const char *fname); #endif /* HANDLE_NETLINK_FUZZING */ extern int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int), const struct nlsock *nl, - const struct zebra_dplane_info *zns, + const struct zebra_dplane_info *dp_info, int count, int startup); extern int netlink_talk_filter(struct nlmsghdr *h, ns_id_t ns, int startup); extern int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), @@ -62,8 +62,7 @@ extern int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), /* Version with 'info' struct only */ int netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), struct nlmsghdr *n, - const struct zebra_dplane_info *dp_info, - int startup); + const struct zebra_dplane_info *dp_info, int startup); extern int netlink_request(struct nlsock *nl, struct nlmsghdr *n); diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 9d75ae940a..0581edfd6d 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -70,8 +70,8 @@ struct zebra_dplane_ctx { enum zebra_dplane_result zd_status; /* TODO -- internal/sub-operation status? */ - enum zebra_dplane_status zd_remote_status; - enum zebra_dplane_status zd_kernel_status; + enum zebra_dplane_result zd_remote_status; + enum zebra_dplane_result zd_kernel_status; /* Dest and (optional) source prefixes */ struct prefix zd_dest; diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index 037c7322b7..ed70a34c0b 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -87,40 +87,6 @@ struct zebra_ns { struct ns *ns; }; -/* Key netlink info from zebra ns, passed from the zebra main context - * to the dataplane/kernel context (which might be in a different pthread). - */ -struct zebra_ns_info { - ns_id_t ns_id; - -#if defined(HAVE_NETLINK) - struct nlsock nls; - uint32_t nl_cmd_pid; - bool is_cmd; -#endif -}; - -/* Utility to fill in zns info from main zns struct */ -static inline void zebra_ns_info_from_ns(struct zebra_ns_info *zns_info, - const struct zebra_ns *zns, - bool is_cmd) -{ - zns_info->ns_id = zns->ns_id; - -#if defined(HAVE_NETLINK) - /* Need to know whether we're using the 'command' netlink socket, - * and need to know its port-id to handle some test/filtering - * cases. - */ - zns_info->is_cmd = is_cmd; - zns_info->nl_cmd_pid = zns->netlink_cmd.snl.nl_pid; - if (is_cmd) - zns_info->nls = zns->netlink_cmd; - else - zns_info->nls = zns->netlink; -#endif /* NETLINK */ -} - struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id); int zebra_ns_init(void); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 0d1a149694..becdfaada7 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -2191,8 +2191,8 @@ static wq_item_status meta_queue_process(struct work_queue *dummy, void *data) queue_len = dplane_get_in_queue_len(); if (queue_len > queue_limit) { if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("meta_queue_process: dplane queue len %u, " - "limit %u, retrying", queue_len, queue_limit); + zlog_debug("rib queue: dplane queue len %u, limit %u, retrying", + queue_len, queue_limit); /* Ensure that the meta-queue is actually enqueued */ if (work_queue_empty(zebrad.ribq)) From ff2460d5972a21363dab55cf874fc740380e5e55 Mon Sep 17 00:00:00 2001 From: Mark Stapp Date: Mon, 15 Oct 2018 11:14:07 -0400 Subject: [PATCH 22/22] zebra: only perform shutdown signal processing once Avoid running the shutdown/sigint handler code more than once. With the async dataplane, once shutdown has been initiated, the completion of all async updates triggers final shutdown of the zebra main pthread. During that time, avoid taking and processing a second signal, such as SIGINT or SIGTERM. Signed-off-by: Mark Stapp --- zebra/main.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/zebra/main.c b/zebra/main.c index 955ab0445e..253b3087fb 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -142,6 +142,12 @@ static void sigint(void) struct zebra_vrf *zvrf; struct listnode *ln, *nn; struct zserv *client; + static bool sigint_done; + + if (sigint_done) + return; + + sigint_done = true; zlog_notice("Terminating on signal"); @@ -174,11 +180,17 @@ static void sigint(void) list_delete(&zebrad.client_list); - /* Indicate that all new dplane work has been enqueued */ + /* Indicate that all new dplane work has been enqueued. When that + * work is complete, the dataplane will enqueue an event + * with the 'finalize' function. + */ zebra_dplane_finish(); } -/* TODO */ +/* + * Final shutdown step for the zebra main thread. This is run after all + * async update processing has completed. + */ int zebra_finalize(struct thread *dummy) { zlog_info("Zebra final shutdown");