]> git.puffer.fish Git - matthieu/frr.git/commitdiff
zebra: start dataplane layer work
authorMark Stapp <mjs@voltanet.io>
Wed, 23 May 2018 16:20:43 +0000 (12:20 -0400)
committerMark Stapp <mjs@voltanet.io>
Thu, 25 Oct 2018 12:34:30 +0000 (08:34 -0400)
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 <mjs@voltanet.io>
14 files changed:
zebra/kernel_netlink.c
zebra/kernel_netlink.h
zebra/rib.h
zebra/rt.h
zebra/rt_netlink.c
zebra/rt_socket.c
zebra/zapi_msg.c
zebra/zapi_msg.h
zebra/zebra_dplane.c
zebra/zebra_dplane.h
zebra/zebra_memory.h
zebra/zebra_ns.h
zebra/zebra_rib.c
zebra/zebra_vrf.h

index e6610f21be4bf6d2bfb2dd6e57450d3738151603..cdfbdd2303488c7dbd6ea8e6a76146720d18348c 100644 (file)
@@ -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
index d78958d72e5777554266429b823b34cc3bb3da67..f3de011b66f5e21fac126e827f5f2e9e410a0191 100644 (file)
@@ -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 */
index f3aead32d804422f9591f31914ce9f283b5eabec..97eae79f03c276cf0289d344e3e4f8ae28749383 100644 (file)
@@ -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:
index dbea298584354578f610d47f0ce4954f0768fc11..a4db8968a5f3d4ae53e0b2e713c1bf09203a5506 100644 (file)
@@ -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 *);
index 795ee2703aa700c4d4b4839ed88f15d7fffe4d00..bbaf89a7c0d11b1448817170b08df82bbae4b3e1 100644 (file)
@@ -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)
 {
index c49dc7bab23fb7363e9a85d364132b0a187bc2cb..eddb0ebfb3b29318893b4a71df05949383b4a2f8 100644 (file)
@@ -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)
 {
index 149d2cb6a56c10943f44b080450fd26c628ebc80..ba734269d34936d2498c590cff56ea8a9de0cf66 100644 (file)
@@ -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)
 {
index 29fe59babfdaf89113a2b0e7b98ec9364658aaa5..0dcfd5d747c43cad59e878497757a7c9e0236074 100644 (file)
@@ -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);
index c0e4939860761bca24fea9c37d1f176f0fb6097a..7b548ea6dd7e734eb29a48ded685d75de1c6646c 100644 (file)
  * 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);
+}
index 7cbef7453c807952ae28f98b7b41f45e5dbf6053..42d88e9b8a234f24bd3bdcef83bbe59adb97c5d3 100644 (file)
 #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 */
index e3439d5f64d88ad6f11084882224e6ff9cc437bc..fcabab97c5ebb43ff577f2782fa2bd4b2da290dd 100644 (file)
@@ -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 */
index ed70a34c0bfbe1e8c06b97d598782c9ee6f1c456..ca909ef413b97c4a948d1937b0b2e143132658fe 100644 (file)
@@ -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);
index db610098f0fdb6c6e99a47afe2abb3e788ba7a04..620dfef3b398b5e2a131ce050a27215ec75de27d 100644 (file)
 #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);
 }
 
 /*
index a39d74b08b49955093ea4ef162be494a7fd12aaa..7fef644faceb5fdc58227ea799282174c2c391d6 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Zebra Vrf Header
  * Copyright (C) 2016 Cumulus Networks
- *                    Donald Sahrp
+ *                    Donald Sharp
  *
  * This file is part of Quagga.
  *