]> git.proxmox.com Git - mirror_frr.git/blobdiff - zebra/rt_netlink.c
isisd: implement the 'lsp-too-large' notification
[mirror_frr.git] / zebra / rt_netlink.c
index 9e02193dfd53cba99327b3d00718c5a0739fab83..8ce963b37b52bce522547d38295d51fd148598cf 100644 (file)
@@ -890,13 +890,16 @@ static int netlink_request_route(struct zebra_ns *zns, int family, int type)
 int netlink_route_read(struct zebra_ns *zns)
 {
        int ret;
+       struct zebra_dplane_info dp_info;
+
+       zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/);
 
        /* Get IPv4 routing table. */
        ret = netlink_request_route(zns, AF_INET, RTM_GETROUTE);
        if (ret < 0)
                return ret;
        ret = netlink_parse_info(netlink_route_change_read_unicast,
-                                &zns->netlink_cmd, zns, 0, 1);
+                                &zns->netlink_cmd, &dp_info, 0, 1);
        if (ret < 0)
                return ret;
 
@@ -905,7 +908,7 @@ int netlink_route_read(struct zebra_ns *zns)
        if (ret < 0)
                return ret;
        ret = netlink_parse_info(netlink_route_change_read_unicast,
-                                &zns->netlink_cmd, zns, 0, 1);
+                                &zns->netlink_cmd, &dp_info, 0, 1);
        if (ret < 0)
                return ret;
 
@@ -1440,21 +1443,21 @@ static int netlink_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla,
                            0);
 }
 
-/* Routing table change via netlink interface. */
-/* Update flag indicates whether this is a "replace" or not. */
-static int netlink_route_multipath(int cmd, const struct prefix *p,
-                                  const struct prefix *src_p,
-                                  struct route_entry *re,
-                                  int update)
+/*
+ * Routing table change via netlink interface, using a dataplane context object
+ */
+static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx)
 {
        int bytelen;
        struct sockaddr_nl snl;
        struct nexthop *nexthop = NULL;
        unsigned int nexthop_num;
-       int family = PREFIX_FAMILY(p);
+       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;
@@ -1462,27 +1465,37 @@ static int netlink_route_multipath(int cmd, const struct prefix *p,
                char buf[NL_PKT_BUF_SIZE];
        } req;
 
-       struct zebra_ns *zns;
-       struct zebra_vrf *zvrf = vrf_info_lookup(re->vrf_id);
+       p = dplane_ctx_get_dest(ctx);
+       src_p = dplane_ctx_get_src(ctx);
 
-       zns = zvrf->zns;
-       memset(&req, 0, sizeof req - NL_PKT_BUF_SIZE);
+       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 ((cmd == RTM_NEWROUTE) && update)
-               req.n.nlmsg_flags |= NLM_F_REPLACE;
+
+       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 = zns->netlink_cmd.snl.nl_pid;
+
+       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_protocol = zebra2proto(re->type);
        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
@@ -1494,9 +1507,9 @@ static int netlink_route_multipath(int cmd, const struct prefix *p,
        if (cmd != RTM_DELROUTE)
                req.r.rtm_type = RTN_UNICAST;
 
-       addattr_l(&req.n, sizeof req, RTA_DST, &p->u.prefix, bytelen);
+       addattr_l(&req.n, sizeof(req), RTA_DST, &p->u.prefix, bytelen);
        if (src_p)
-               addattr_l(&req.n, sizeof req, RTA_SRC, &src_p->u.prefix,
+               addattr_l(&req.n, sizeof(req), RTA_SRC, &src_p->u.prefix,
                          bytelen);
 
        /* Metric. */
@@ -1506,20 +1519,31 @@ static int netlink_route_multipath(int cmd, const struct prefix *p,
         * path(s)
         * by the routing protocol and for communicating with protocol peers.
         */
-       addattr32(&req.n, sizeof req, RTA_PRIORITY, NL_DEFAULT_ROUTE_METRIC);
+       addattr32(&req.n, sizeof(req), RTA_PRIORITY, NL_DEFAULT_ROUTE_METRIC);
+
 #if defined(SUPPORT_REALMS)
-       if (re->tag > 0 && re->tag <= 255)
-               addattr32(&req.n, sizeof req, RTA_FLOW, re->tag);
+       {
+               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. */
-       if (re->table < 256)
-               req.r.rtm_table = re->table;
+       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, re->table);
+               addattr32(&req.n, sizeof(req), RTA_TABLE, table_id);
        }
 
-       _netlink_route_debug(cmd, p, family, zvrf_id(zvrf), re->table);
+       _netlink_route_debug(cmd, p, family, dplane_ctx_get_vrf(ctx), table_id);
 
        /*
         * If we are not updating the route and we have received
@@ -1527,41 +1551,42 @@ static int netlink_route_multipath(int cmd, const struct prefix *p,
         * prefix information to tell the kernel to schwack
         * it.
         */
-       if (!update && cmd == RTM_DELROUTE)
+       if (cmd == RTM_DELROUTE)
                goto skip;
 
-       if (re->mtu || re->nexthop_mtu) {
+       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 = re->mtu;
-               if (!mtu || (re->nexthop_mtu && re->nexthop_mtu < mtu))
-                       mtu = re->nexthop_mtu;
+               uint32_t mtu = dplane_ctx_get_mtu(ctx);
+               uint32_t nexthop_mtu = dplane_ctx_get_nh_mtu(ctx);
+
+               if (!mtu || (nexthop_mtu && nexthop_mtu < mtu))
+                       mtu = nexthop_mtu;
                rta->rta_type = RTA_METRICS;
                rta->rta_len = RTA_LENGTH(0);
-               rta_addattr_l(rta, NL_PKT_BUF_SIZE, RTAX_MTU, &mtu, sizeof mtu);
+               rta_addattr_l(rta, NL_PKT_BUF_SIZE,
+                             RTAX_MTU, &mtu, sizeof(mtu));
                addattr_l(&req.n, NL_PKT_BUF_SIZE, RTA_METRICS, RTA_DATA(rta),
                          RTA_PAYLOAD(rta));
        }
 
        /* Count overall nexthops so we can decide whether to use singlepath
-        * or multipath case. */
+        * or multipath case.
+        */
        nexthop_num = 0;
-       for (ALL_NEXTHOPS(re->ng, nexthop)) {
+       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;
-               if (cmd == RTM_DELROUTE
-                   && !CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB))
-                       continue;
 
                nexthop_num++;
        }
 
        /* Singlepath case. */
-       if (nexthop_num == 1 || multipath_num == 1) {
+       if (nexthop_num == 1) {
                nexthop_num = 0;
-               for (ALL_NEXTHOPS(re->ng, nexthop)) {
+               for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) {
                        /*
                         * So we want to cover 2 types of blackhole
                         * routes here:
@@ -1585,70 +1610,61 @@ static int netlink_route_multipath(int cmd, const struct prefix *p,
                        }
                        if (CHECK_FLAG(nexthop->flags,
                                       NEXTHOP_FLAG_RECURSIVE)) {
-                               if (!setsrc) {
-                                       if (family == AF_INET) {
-                                               if (nexthop->rmap_src.ipv4
-                                                           .s_addr
-                                                   != 0) {
-                                                       src.ipv4 =
-                                                               nexthop->rmap_src
-                                                                       .ipv4;
-                                                       setsrc = 1;
-                                               } else if (nexthop->src.ipv4
-                                                                  .s_addr
-                                                          != 0) {
-                                                       src.ipv4 =
-                                                               nexthop->src
-                                                                       .ipv4;
-                                                       setsrc = 1;
-                                               }
-                                       } else if (family == AF_INET6) {
-                                               if (!IN6_IS_ADDR_UNSPECIFIED(
-                                                           &nexthop->rmap_src
-                                                                    .ipv6)) {
-                                                       src.ipv6 =
-                                                               nexthop->rmap_src
-                                                                       .ipv6;
-                                                       setsrc = 1;
-                                               } else if (
-                                                       !IN6_IS_ADDR_UNSPECIFIED(
-                                                               &nexthop->src
-                                                                        .ipv6)) {
-                                                       src.ipv6 =
-                                                               nexthop->src
-                                                                       .ipv6;
-                                                       setsrc = 1;
-                                               }
+
+                               if (setsrc)
+                                       continue;
+
+                               if (family == AF_INET) {
+                                       if (nexthop->rmap_src.ipv4.s_addr
+                                           != 0) {
+                                               src.ipv4 =
+                                                       nexthop->rmap_src.ipv4;
+                                               setsrc = 1;
+                                       } else if (nexthop->src.ipv4.s_addr
+                                                  != 0) {
+                                               src.ipv4 =
+                                                       nexthop->src.ipv4;
+                                               setsrc = 1;
+                                       }
+                               } else if (family == AF_INET6) {
+                                       if (!IN6_IS_ADDR_UNSPECIFIED(
+                                                   &nexthop->rmap_src.ipv6)) {
+                                               src.ipv6 =
+                                                       nexthop->rmap_src.ipv6;
+                                               setsrc = 1;
+                                       } else if (
+                                               !IN6_IS_ADDR_UNSPECIFIED(
+                                                       &nexthop->src.ipv6)) {
+                                               src.ipv6 =
+                                                       nexthop->src.ipv6;
+                                               setsrc = 1;
                                        }
                                }
                                continue;
                        }
 
                        if ((cmd == RTM_NEWROUTE
-                            && NEXTHOP_IS_ACTIVE(nexthop->flags))
-                           || (cmd == RTM_DELROUTE
-                               && CHECK_FLAG(nexthop->flags,
-                                             NEXTHOP_FLAG_FIB))) {
+                            && 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);
+                                       &req.r, sizeof(req), cmd);
                                nexthop_num++;
                                break;
                        }
                }
                if (setsrc && (cmd == RTM_NEWROUTE)) {
                        if (family == AF_INET)
-                               addattr_l(&req.n, sizeof req, RTA_PREFSRC,
+                               addattr_l(&req.n, sizeof(req), RTA_PREFSRC,
                                          &src.ipv4, bytelen);
                        else if (family == AF_INET6)
-                               addattr_l(&req.n, sizeof req, RTA_PREFSRC,
+                               addattr_l(&req.n, sizeof(req), RTA_PREFSRC,
                                          &src.ipv6, bytelen);
                }
-       } else {
+       } else {    /* Multipath case */
                char buf[NL_PKT_BUF_SIZE];
                struct rtattr *rta = (void *)buf;
                struct rtnexthop *rtnh;
@@ -1659,57 +1675,45 @@ static int netlink_route_multipath(int cmd, const struct prefix *p,
                rtnh = RTA_DATA(rta);
 
                nexthop_num = 0;
-               for (ALL_NEXTHOPS(re->ng, nexthop)) {
-                       if (nexthop_num >= multipath_num)
-                               break;
-
+               for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) {
                        if (CHECK_FLAG(nexthop->flags,
                                       NEXTHOP_FLAG_RECURSIVE)) {
                                /* This only works for IPv4 now */
-                               if (!setsrc) {
-                                       if (family == AF_INET) {
-                                               if (nexthop->rmap_src.ipv4
-                                                           .s_addr
-                                                   != 0) {
-                                                       src.ipv4 =
-                                                               nexthop->rmap_src
-                                                                       .ipv4;
-                                                       setsrc = 1;
-                                               } else if (nexthop->src.ipv4
-                                                                  .s_addr
-                                                          != 0) {
-                                                       src.ipv4 =
-                                                               nexthop->src
-                                                                       .ipv4;
-                                                       setsrc = 1;
-                                               }
-                                       } else if (family == AF_INET6) {
-                                               if (!IN6_IS_ADDR_UNSPECIFIED(
-                                                           &nexthop->rmap_src
-                                                                    .ipv6)) {
-                                                       src.ipv6 =
-                                                               nexthop->rmap_src
-                                                                       .ipv6;
-                                                       setsrc = 1;
-                                               } else if (
-                                                       !IN6_IS_ADDR_UNSPECIFIED(
-                                                               &nexthop->src
-                                                                        .ipv6)) {
-                                                       src.ipv6 =
-                                                               nexthop->src
-                                                                       .ipv6;
-                                                       setsrc = 1;
-                                               }
+                               if (setsrc)
+                                       continue;
+
+                               if (family == AF_INET) {
+                                       if (nexthop->rmap_src.ipv4.s_addr
+                                           != 0) {
+                                               src.ipv4 =
+                                                       nexthop->rmap_src.ipv4;
+                                               setsrc = 1;
+                                       } else if (nexthop->src.ipv4.s_addr
+                                                  != 0) {
+                                               src.ipv4 =
+                                                       nexthop->src.ipv4;
+                                               setsrc = 1;
+                                       }
+                               } else if (family == AF_INET6) {
+                                       if (!IN6_IS_ADDR_UNSPECIFIED(
+                                                   &nexthop->rmap_src.ipv6)) {
+                                               src.ipv6 =
+                                                       nexthop->rmap_src.ipv6;
+                                               setsrc = 1;
+                                       } else if (
+                                               !IN6_IS_ADDR_UNSPECIFIED(
+                                                       &nexthop->src.ipv6)) {
+                                               src.ipv6 =
+                                                       nexthop->src.ipv6;
+                                               setsrc = 1;
                                        }
                                }
+
                                continue;
                        }
 
                        if ((cmd == RTM_NEWROUTE
-                            && NEXTHOP_IS_ACTIVE(nexthop->flags))
-                           || (cmd == RTM_DELROUTE
-                               && CHECK_FLAG(nexthop->flags,
-                                             NEXTHOP_FLAG_FIB))) {
+                            && NEXTHOP_IS_ACTIVE(nexthop->flags))) {
                                routedesc = nexthop->rparent
                                                    ? "recursive, multipath"
                                                    : "multipath";
@@ -1732,10 +1736,10 @@ static int netlink_route_multipath(int cmd, const struct prefix *p,
                }
                if (setsrc && (cmd == RTM_NEWROUTE)) {
                        if (family == AF_INET)
-                               addattr_l(&req.n, sizeof req, RTA_PREFSRC,
+                               addattr_l(&req.n, sizeof(req), RTA_PREFSRC,
                                          &src.ipv4, bytelen);
                        else if (family == AF_INET6)
-                               addattr_l(&req.n, sizeof req, RTA_PREFSRC,
+                               addattr_l(&req.n, sizeof(req), RTA_PREFSRC,
                                          &src.ipv6, bytelen);
                        if (IS_ZEBRA_DEBUG_KERNEL)
                                zlog_debug("Setting source");
@@ -1757,12 +1761,12 @@ static int netlink_route_multipath(int cmd, const struct prefix *p,
 skip:
 
        /* Destination netlink address. */
-       memset(&snl, 0, sizeof snl);
+       memset(&snl, 0, sizeof(snl));
        snl.nl_family = AF_NETLINK;
 
        /* Talk to netlink socket. */
-       return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns,
-                           0);
+       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)
@@ -1818,25 +1822,30 @@ int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in)
        return suc;
 }
 
-enum dp_req_result kernel_route_rib(struct route_node *rn,
                                  const struct prefix *p,
-                                   const struct prefix *src_p,
-                                   struct route_entry *old,
-                                   struct route_entry *new)
+/*
* Update or delete a prefix from the kernel,
+ * using info from a dataplane context.
+ */
+enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx)
 {
-       int ret = 0;
-
-       assert(old || new);
-
-       if (new) {
-               if (p->family == AF_INET || v6_rr_semantics)
-                       ret = netlink_route_multipath(RTM_NEWROUTE, p, src_p,
-                                                     new, (old) ? 1 : 0);
-               else {
+       int cmd, ret;
+       const struct prefix *p = dplane_ctx_get_dest(ctx);
+       struct nexthop *nexthop;
+
+       if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_DELETE) {
+               cmd = RTM_DELROUTE;
+       } 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 than an add.
+                        * 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.
@@ -1845,27 +1854,31 @@ enum dp_req_result kernel_route_rib(struct route_node *rn,
                         * of the route delete.  If that happens yeah we're
                         * screwed.
                         */
-                       if (old)
-                               netlink_route_multipath(RTM_DELROUTE, p, src_p,
-                                                       old, 0);
-                       ret = netlink_route_multipath(RTM_NEWROUTE, p, src_p,
-                                                     new, 0);
+                       (void)netlink_route_multipath(RTM_DELROUTE, ctx);
+                       cmd = RTM_NEWROUTE;
                }
-               kernel_route_rib_pass_fail(rn, p, new,
-                                          (!ret) ? DP_INSTALL_SUCCESS
-                                                 : DP_INSTALL_FAILURE);
-               return DP_REQUEST_SUCCESS;
+
+       } else {
+               return ZEBRA_DPLANE_REQUEST_FAILURE;
        }
 
-       if (old) {
-               ret = netlink_route_multipath(RTM_DELROUTE, p, src_p, old, 0);
+       ret = netlink_route_multipath(cmd, ctx);
+       if ((cmd == RTM_NEWROUTE) && (ret == 0)) {
+               /* Update installed nexthops to signal which have been
+                * installed.
+                */
+               for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) {
+                       if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+                               continue;
 
-               kernel_route_rib_pass_fail(rn, p, old,
-                                          (!ret) ? DP_DELETE_SUCCESS
-                                                 : DP_DELETE_FAILURE);
+                       if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) {
+                               SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
+                       }
+               }
        }
 
-       return DP_REQUEST_SUCCESS;
+       return (ret == 0 ?
+               ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE);
 }
 
 int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla,
@@ -2102,8 +2115,8 @@ static int netlink_macfdb_table(struct nlmsghdr *h, ns_id_t ns_id, int startup)
 }
 
 /* Request for MAC FDB information from the kernel */
-static int netlink_request_macs(struct zebra_ns *zns, int family, int type,
-                               ifindex_t master_ifindex)
+static int netlink_request_macs(struct nlsock *netlink_cmd, int family,
+                               int type, ifindex_t master_ifindex)
 {
        struct {
                struct nlmsghdr n;
@@ -2119,7 +2132,7 @@ static int netlink_request_macs(struct zebra_ns *zns, int family, int type,
        if (master_ifindex)
                addattr32(&req.n, sizeof(req), IFLA_MASTER, master_ifindex);
 
-       return netlink_request(&zns->netlink_cmd, &req.n);
+       return netlink_request(netlink_cmd, &req.n);
 }
 
 /*
@@ -2129,15 +2142,19 @@ static int netlink_request_macs(struct zebra_ns *zns, int family, int type,
 int netlink_macfdb_read(struct zebra_ns *zns)
 {
        int ret;
+       struct zebra_dplane_info dp_info;
+
+       zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/);
 
        /* Get bridge FDB table. */
-       ret = netlink_request_macs(zns, AF_BRIDGE, RTM_GETNEIGH, 0);
+       ret = netlink_request_macs(&zns->netlink_cmd, AF_BRIDGE, RTM_GETNEIGH,
+                                  0);
        if (ret < 0)
                return ret;
        /* We are reading entire table. */
        filter_vlan = 0;
-       ret = netlink_parse_info(netlink_macfdb_table, &zns->netlink_cmd, zns,
-                                0, 1);
+       ret = netlink_parse_info(netlink_macfdb_table, &zns->netlink_cmd,
+                                &dp_info, 0, 1);
 
        return ret;
 }
@@ -2152,8 +2169,10 @@ int netlink_macfdb_read_for_bridge(struct zebra_ns *zns, struct interface *ifp,
        struct zebra_if *br_zif;
        struct zebra_if *zif;
        struct zebra_l2info_vxlan *vxl;
+       struct zebra_dplane_info dp_info;
        int ret = 0;
 
+       zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/);
 
        /* Save VLAN we're filtering on, if needed. */
        br_zif = (struct zebra_if *)br_if->info;
@@ -2164,12 +2183,12 @@ int netlink_macfdb_read_for_bridge(struct zebra_ns *zns, struct interface *ifp,
 
        /* Get bridge FDB table for specific bridge - we do the VLAN filtering.
         */
-       ret = netlink_request_macs(zns, AF_BRIDGE, RTM_GETNEIGH,
+       ret = netlink_request_macs(&zns->netlink_cmd, AF_BRIDGE, RTM_GETNEIGH,
                                   br_if->ifindex);
        if (ret < 0)
                return ret;
-       ret = netlink_parse_info(netlink_macfdb_table, &zns->netlink_cmd, zns,
-                                0, 0);
+       ret = netlink_parse_info(netlink_macfdb_table, &zns->netlink_cmd,
+                                &dp_info, 0, 0);
 
        /* Reset VLAN filter. */
        filter_vlan = 0;
@@ -2191,7 +2210,7 @@ static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid,
        struct interface *br_if;
        struct zebra_if *br_zif;
        char buf[ETHER_ADDR_STRLEN];
-       int vid_present = 0, dst_present = 0;
+       int vid_present = 0;
        char vid_buf[20];
        char dst_buf[30];
        struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id);
@@ -2225,7 +2244,6 @@ static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid,
        req.ndm.ndm_ifindex = ifp->ifindex;
        dst_alen = 4; // TODO: hardcoded
        addattr_l(&req.n, sizeof(req), NDA_DST, &vtep_ip, dst_alen);
-       dst_present = 1;
        sprintf(dst_buf, " dst %s", inet_ntoa(vtep_ip));
        br_zif = (struct zebra_if *)br_if->info;
        if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif) && vid > 0) {
@@ -2241,13 +2259,31 @@ static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid,
                           nl_family_to_str(req.ndm.ndm_family), ifp->name,
                           ifp->ifindex, vid_present ? vid_buf : "",
                           sticky ? "sticky " : "",
-                          prefix_mac2str(mac, buf, sizeof(buf)),
-                          dst_present ? dst_buf : "");
+                          prefix_mac2str(mac, buf, sizeof(buf)), dst_buf);
 
        return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns,
                            0);
 }
 
+/*
+ * In the event the kernel deletes ipv4 link-local neighbor entries created for
+ * 5549 support, re-install them.
+ */
+static void netlink_handle_5549(struct ndmsg *ndm, struct zebra_if *zif,
+                               struct interface *ifp, struct ipaddr *ip)
+{
+       if (ndm->ndm_family != AF_INET)
+               return;
+
+       if (!zif->v6_2_v4_ll_neigh_entry)
+               return;
+
+       if (ipv4_ll.s_addr != ip->ip._v4_addr.s_addr)
+               return;
+
+       if_nbr_ipv6ll_to_ipv4ll_neigh_update(ifp, &zif->v6_2_v4_ll_addr6, true);
+}
+
 #define NUD_VALID                                                              \
        (NUD_PERMANENT | NUD_NOARP | NUD_REACHABLE | NUD_PROBE | NUD_STALE     \
         | NUD_DELAY)
@@ -2293,29 +2329,16 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
        ip.ipa_type = (ndm->ndm_family == AF_INET) ? IPADDR_V4 : IPADDR_V6;
        memcpy(&ip.ip.addr, RTA_DATA(tb[NDA_DST]), RTA_PAYLOAD(tb[NDA_DST]));
 
-       /* Drop some "permanent" entries. */
-       if (ndm->ndm_state & NUD_PERMANENT) {
-               char b[16] = "169.254.0.1";
-               struct in_addr ipv4_ll;
-
-               if (ndm->ndm_family != AF_INET)
-                       return 0;
-
-               if (!zif->v6_2_v4_ll_neigh_entry)
-                       return 0;
-
-               if (h->nlmsg_type != RTM_DELNEIGH)
-                       return 0;
-
-               inet_pton(AF_INET, b, &ipv4_ll);
-               if (ipv4_ll.s_addr != ip.ip._v4_addr.s_addr)
-                       return 0;
-
-               if_nbr_ipv6ll_to_ipv4ll_neigh_update(
-                       ifp, &zif->v6_2_v4_ll_addr6, true);
+       /* if kernel deletes our rfc5549 neighbor entry, re-install it */
+       if (h->nlmsg_type == RTM_DELNEIGH && (ndm->ndm_state & NUD_PERMANENT)) {
+               netlink_handle_5549(ndm, zif, ifp, &ip);
                return 0;
        }
 
+       /* if kernel marks our rfc5549 neighbor entry invalid, re-install it */
+       if (h->nlmsg_type == RTM_NEWNEIGH && !(ndm->ndm_state & NUD_VALID))
+               netlink_handle_5549(ndm, zif, ifp, &ip);
+
        /* The neighbor is present on an SVI. From this, we locate the
         * underlying
         * bridge because we're only interested in neighbors on a VxLAN bridge.
@@ -2419,8 +2442,8 @@ static int netlink_neigh_table(struct nlmsghdr *h, ns_id_t ns_id, int startup)
 }
 
 /* Request for IP neighbor information from the kernel */
-static int netlink_request_neigh(struct zebra_ns *zns, int family, int type,
-                                ifindex_t ifindex)
+static int netlink_request_neigh(struct nlsock *netlink_cmd, int family,
+                                int type, ifindex_t ifindex)
 {
        struct {
                struct nlmsghdr n;
@@ -2436,7 +2459,7 @@ static int netlink_request_neigh(struct zebra_ns *zns, int family, int type,
        if (ifindex)
                addattr32(&req.n, sizeof(req), NDA_IFINDEX, ifindex);
 
-       return netlink_request(&zns->netlink_cmd, &req.n);
+       return netlink_request(netlink_cmd, &req.n);
 }
 
 /*
@@ -2446,13 +2469,17 @@ static int netlink_request_neigh(struct zebra_ns *zns, int family, int type,
 int netlink_neigh_read(struct zebra_ns *zns)
 {
        int ret;
+       struct zebra_dplane_info dp_info;
+
+       zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/);
 
        /* Get IP neighbor table. */
-       ret = netlink_request_neigh(zns, AF_UNSPEC, RTM_GETNEIGH, 0);
+       ret = netlink_request_neigh(&zns->netlink_cmd, AF_UNSPEC, RTM_GETNEIGH,
+                                   0);
        if (ret < 0)
                return ret;
-       ret = netlink_parse_info(netlink_neigh_table, &zns->netlink_cmd, zns, 0,
-                                1);
+       ret = netlink_parse_info(netlink_neigh_table, &zns->netlink_cmd,
+                                &dp_info, 0, 1);
 
        return ret;
 }
@@ -2464,13 +2491,16 @@ int netlink_neigh_read(struct zebra_ns *zns)
 int netlink_neigh_read_for_vlan(struct zebra_ns *zns, struct interface *vlan_if)
 {
        int ret = 0;
+       struct zebra_dplane_info dp_info;
+
+       zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/);
 
-       ret = netlink_request_neigh(zns, AF_UNSPEC, RTM_GETNEIGH,
+       ret = netlink_request_neigh(&zns->netlink_cmd, AF_UNSPEC, RTM_GETNEIGH,
                                    vlan_if->ifindex);
        if (ret < 0)
                return ret;
-       ret = netlink_parse_info(netlink_neigh_table, &zns->netlink_cmd, zns, 0,
-                                0);
+       ret = netlink_parse_info(netlink_neigh_table, &zns->netlink_cmd,
+                                &dp_info, 0, 0);
 
        return ret;
 }
@@ -2659,7 +2689,7 @@ int netlink_mpls_multipath(int cmd, zebra_lsp_t *lsp)
        /* Fill nexthops (paths) based on single-path or multipath. The paths
         * chosen depend on the operation.
         */
-       if (nexthop_num == 1 || multipath_num == 1) {
+       if (nexthop_num == 1) {
                routedesc = "single-path";
                _netlink_mpls_debug(cmd, lsp->ile.in_label, routedesc);
 
@@ -2706,9 +2736,6 @@ int netlink_mpls_multipath(int cmd, zebra_lsp_t *lsp)
                        if (!nexthop)
                                continue;
 
-                       if (nexthop_num >= multipath_num)
-                               break;
-
                        if ((cmd == RTM_NEWROUTE
                             && (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED)
                                 && CHECK_FLAG(nexthop->flags,