summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--zebra/kernel_netlink.c43
-rw-r--r--zebra/kernel_netlink.h9
-rw-r--r--zebra/rib.h3
-rw-r--r--zebra/rt.h6
-rw-r--r--zebra/rt_netlink.c387
-rw-r--r--zebra/rt_socket.c5
-rw-r--r--zebra/zapi_msg.c14
-rw-r--r--zebra/zapi_msg.h2
-rw-r--r--zebra/zebra_dplane.c736
-rw-r--r--zebra/zebra_dplane.h178
-rw-r--r--zebra/zebra_memory.h1
-rw-r--r--zebra/zebra_ns.h35
-rw-r--r--zebra/zebra_rib.c73
-rw-r--r--zebra/zebra_vrf.h2
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.
*