diff options
-rw-r--r-- | zebra/kernel_netlink.c | 43 | ||||
-rw-r--r-- | zebra/kernel_netlink.h | 9 | ||||
-rw-r--r-- | zebra/rib.h | 3 | ||||
-rw-r--r-- | zebra/rt.h | 6 | ||||
-rw-r--r-- | zebra/rt_netlink.c | 387 | ||||
-rw-r--r-- | zebra/rt_socket.c | 5 | ||||
-rw-r--r-- | zebra/zapi_msg.c | 14 | ||||
-rw-r--r-- | zebra/zapi_msg.h | 2 | ||||
-rw-r--r-- | zebra/zebra_dplane.c | 736 | ||||
-rw-r--r-- | zebra/zebra_dplane.h | 178 | ||||
-rw-r--r-- | zebra/zebra_memory.h | 1 | ||||
-rw-r--r-- | zebra/zebra_ns.h | 35 | ||||
-rw-r--r-- | zebra/zebra_rib.c | 73 | ||||
-rw-r--r-- | 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 e6610f21b..cdfbdd230 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 d78958d72..f3de011b6 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 f3aead32d..97eae79f0 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 dbea29858..a4db8968a 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 795ee2703..bbaf89a7c 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 c49dc7bab..eddb0ebfb 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 149d2cb6a..ba734269d 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 29fe59bab..0dcfd5d74 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 c0e493986..7b548ea6d 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 <zebra.h> -#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 7cbef7453..42d88e9b8 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,22 +50,14 @@ 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 e3439d5f6..fcabab97c 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 ed70a34c0..ca909ef41 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 db610098f..620dfef3b 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 a39d74b08..7fef644fa 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. * |