]> git.puffer.fish Git - matthieu/frr.git/commitdiff
Quagga: Install label forwarding entries for statically configured LSPs
authorvivek <vivek@cumulusnetworks.com>
Sat, 16 Apr 2016 02:19:37 +0000 (19:19 -0700)
committerDonald Sharp <sharpd@cumulusnetworks.com>
Fri, 23 Sep 2016 13:30:56 +0000 (09:30 -0400)
Install the statically configured LSPs into the FIB (kernel). This is done
using the new attributes and definitions for MPLS in the kernel -
RTA_VIA, RTA_NEWDST and AF_MPLS.

Signed-off-by: Vivek Venkatraman <vivek@cumulusnetworks.com>
Reviewed-by: Donald Sharp <sharpd@cumulusnetworks.com>
Ticket:  CM-4804
Reviewed By: CCR-3088
Testing Done: Manual in SE-1

12 files changed:
lib/nexthop.c
lib/nexthop.h
zebra/connected.c
zebra/kernel_null.c
zebra/rib.h
zebra/rt.h
zebra/rt_netlink.c
zebra/zebra_mpls.c
zebra/zebra_mpls.h
zebra/zebra_mpls_null.c
zebra/zebra_rib.c
zebra/zserv.h

index 427f77f87ac69c4020f3dde60bc9965f7ad216b4..465cc948516abfe452837c5f50d89054f6d12630 100644 (file)
 #include "thread.h"
 #include "prefix.h"
 #include "nexthop.h"
+#include "mpls.h"
 
-DEFINE_MTYPE_STATIC(LIB, NEXTHOP, "Nexthop")
+DEFINE_MTYPE_STATIC(LIB, NEXTHOP,      "Nexthop")
+DEFINE_MTYPE_STATIC(LIB, NH_LABEL,     "Nexthop label")
 
 /* check if nexthops are same, non-recursive */
 int
@@ -138,6 +140,7 @@ copy_nexthops (struct nexthop **tnh, struct nexthop *nh)
 void
 nexthop_free (struct nexthop *nexthop)
 {
+  nexthop_del_labels (nexthop);
   if (nexthop->resolved)
     nexthops_free(nexthop->resolved);
   XFREE (MTYPE_NEXTHOP, nexthop);
@@ -156,6 +159,29 @@ nexthops_free (struct nexthop *nexthop)
     }
 }
 
+/* Update nexthop with label information. */
+void
+nexthop_add_labels (struct nexthop *nexthop, u_int8_t num_labels,
+                    mpls_label_t *label)
+{
+  struct nexthop_label *nh_label;
+  int i;
+
+  nh_label = XCALLOC (MTYPE_NH_LABEL, sizeof (struct nexthop_label));
+  nh_label->num_labels = num_labels;
+  for (i = 0; i < num_labels; i++)
+    nh_label->label[i] = *(label + i);
+  nexthop->nh_label = nh_label;
+}
+
+/* Free label information of nexthop, if present. */
+void
+nexthop_del_labels (struct nexthop *nexthop)
+{
+  if (nexthop->nh_label)
+    XFREE (MTYPE_NH_LABEL, nexthop->nh_label);
+}
+
 const char *
 nexthop2str (struct nexthop *nexthop, char *str, int size)
 {
index 801904306efaba7e3abef1d7a954d5100cf099f1..c06dfe0e25ac30997316d57bb9ceacb810ddf0bb 100644 (file)
@@ -84,6 +84,9 @@ struct nexthop
    * obtained by recursive resolution will be added to `resolved'.
    * Only one level of recursive resolution is currently supported. */
   struct nexthop *resolved;
+
+  /* Label(s) associated with this nexthop. */
+  struct nexthop_label *nh_label;
 };
 
 extern int zebra_rnh_ip_default_route;
@@ -106,6 +109,9 @@ void copy_nexthops (struct nexthop **tnh, struct nexthop *nh);
 void nexthop_free (struct nexthop *nexthop);
 void nexthops_free (struct nexthop *nexthop);
 
+void nexthop_add_labels (struct nexthop *, u_int8_t, mpls_label_t *);
+void nexthop_del_labels (struct nexthop *);
+
 extern const char *nexthop_type_to_str (enum nexthop_types_t nh_type);
 extern int nexthop_same_no_recurse (struct nexthop *next1, struct nexthop *next2);
 
index 290973a5cbf15b8b875a577751355dc7df84cad1..9737f8debab3c7ddb1754884a310ae9902742793 100644 (file)
@@ -38,6 +38,7 @@
 #include "zebra/interface.h"
 #include "zebra/connected.h"
 #include "zebra/rtadv.h"
+#include "zebra/zebra_mpls.h"
 
 /* communicate the withdrawal of a connected address */
 static void
@@ -213,6 +214,15 @@ connected_up_ipv4 (struct interface *ifp, struct connected *ifc)
     zlog_debug ("%u: IF %s IPv4 address add/up, scheduling RIB processing",
                 ifp->vrf_id, ifp->name);
   rib_update (ifp->vrf_id, RIB_UPDATE_IF_CHANGE);
+
+  /* Schedule LSP forwarding entries for processing, if appropriate. */
+  if (ifp->vrf_id == VRF_DEFAULT)
+    {
+      if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+        zlog_debug ("%u: IF %s IPv4 address add/up, scheduling MPLS processing",
+                ifp->vrf_id, ifp->name);
+      zebra_mpls_lsp_schedule (vrf_info_lookup(ifp->vrf_id));
+    }
 }
 
 /* Add connected IPv4 route to the interface. */
@@ -333,6 +343,15 @@ connected_down_ipv4 (struct interface *ifp, struct connected *ifc)
                 ifp->vrf_id, ifp->name);
 
   rib_update (ifp->vrf_id, RIB_UPDATE_IF_CHANGE);
+
+  /* Schedule LSP forwarding entries for processing, if appropriate. */
+  if (ifp->vrf_id == VRF_DEFAULT)
+    {
+      if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+        zlog_debug ("%u: IF %s IPv4 address add/up, scheduling MPLS processing",
+                ifp->vrf_id, ifp->name);
+      zebra_mpls_lsp_schedule (vrf_info_lookup(ifp->vrf_id));
+    }
 }
 
 /* Delete connected IPv4 route to the interface. */
@@ -359,6 +378,15 @@ connected_delete_ipv4 (struct interface *ifp, int flags, struct in_addr *addr,
                 ifp->vrf_id, ifp->name);
 
   rib_update (ifp->vrf_id, RIB_UPDATE_IF_CHANGE);
+
+  /* Schedule LSP forwarding entries for processing, if appropriate. */
+  if (ifp->vrf_id == VRF_DEFAULT)
+    {
+      if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+        zlog_debug ("%u: IF %s IPv4 address add/up, scheduling MPLS processing",
+                ifp->vrf_id, ifp->name);
+      zebra_mpls_lsp_schedule (vrf_info_lookup(ifp->vrf_id));
+    }
 }
 
 void
@@ -389,6 +417,15 @@ connected_up_ipv6 (struct interface *ifp, struct connected *ifc)
                 ifp->vrf_id, ifp->name);
 
   rib_update (ifp->vrf_id, RIB_UPDATE_IF_CHANGE);
+
+  /* Schedule LSP forwarding entries for processing, if appropriate. */
+  if (ifp->vrf_id == VRF_DEFAULT)
+    {
+      if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+        zlog_debug ("%u: IF %s IPv4 address add/up, scheduling MPLS processing",
+                ifp->vrf_id, ifp->name);
+      zebra_mpls_lsp_schedule (vrf_info_lookup(ifp->vrf_id));
+    }
 }
 
 /* Add connected IPv6 route to the interface. */
@@ -479,6 +516,15 @@ connected_down_ipv6 (struct interface *ifp, struct connected *ifc)
                 ifp->vrf_id, ifp->name);
 
   rib_update (ifp->vrf_id, RIB_UPDATE_IF_CHANGE);
+
+  /* Schedule LSP forwarding entries for processing, if appropriate. */
+  if (ifp->vrf_id == VRF_DEFAULT)
+    {
+      if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+        zlog_debug ("%u: IF %s IPv4 address add/up, scheduling MPLS processing",
+                ifp->vrf_id, ifp->name);
+      zebra_mpls_lsp_schedule (vrf_info_lookup(ifp->vrf_id));
+    }
 }
 
 void
@@ -504,6 +550,15 @@ connected_delete_ipv6 (struct interface *ifp, struct in6_addr *address,
                 ifp->vrf_id, ifp->name);
 
   rib_update (ifp->vrf_id, RIB_UPDATE_IF_CHANGE);
+
+  /* Schedule LSP forwarding entries for processing, if appropriate. */
+  if (ifp->vrf_id == VRF_DEFAULT)
+    {
+      if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+        zlog_debug ("%u: IF %s IPv4 address add/up, scheduling MPLS processing",
+                ifp->vrf_id, ifp->name);
+      zebra_mpls_lsp_schedule (vrf_info_lookup(ifp->vrf_id));
+    }
 }
 
 int
index 17b3c7bc8d421a1e25767f8648b897f48bc5ca7d..0802570e60f271667915bbcfd45b7b892c609ead 100644 (file)
@@ -30,6 +30,7 @@
 #include "zebra/connected.h"
 #include "zebra/rt_netlink.h"
 #include "zebra/rib.h"
+#include "zebra/zebra_mpls.h"
 
 int kernel_add_ipv4 (struct prefix *a, struct rib *b) { return 0; }
 int kernel_update_ipv4 (struct prefix *a, struct rib *b) { return 0; }
@@ -65,3 +66,9 @@ int kernel_neigh_update (int a, int b, uint32_t c, char *d, int e)
 void kernel_init (struct zebra_ns *zns) { return; }
 void kernel_terminate (struct zebra_ns *zns) { return; }
 void route_read (struct zebra_ns *zns) { return; }
+
+int kernel_add_lsp (zebra_lsp_t *l) { return 0; }
+
+int kernel_del_lsp (zebra_lsp_t *l) { return 0; }
+
+int kernel_upd_lsp (zebra_lsp_t *l) { return 0; }
index 9867323e6e28604e4405f5d8cde4c1816abce3c7..0f7f70ada7dbaa8f6f4e03eee58392de2656efd8 100644 (file)
@@ -30,6 +30,7 @@
 #include "nexthop.h"
 #include "vrf.h"
 #include "if.h"
+#include "mpls.h"
 
 #define DISTANCE_INFINITY  255
 #define ZEBRA_KERNEL_TABLE_MAX 252 /* support for no more than this rt tables */
@@ -373,6 +374,8 @@ extern struct route_table *rib_table_ipv6;
 extern int rib_gc_dest (struct route_node *rn);
 extern struct route_table *rib_tables_iter_next (rib_tables_iter_t *iter);
 
+extern u_char route_distance(int type);
+
 /*
  * Inline functions.
  */
index 46e71fa46e41a5114e5eb5c86ad87cd6ab652e5c..1137f880f0992d7847dd6411347d4846d39c9645 100644 (file)
@@ -26,6 +26,8 @@
 #include "prefix.h"
 #include "if.h"
 #include "zebra/rib.h"
+#include "zebra/zebra_ns.h"
+#include "zebra/zebra_mpls.h"
 
 extern int kernel_add_ipv4 (struct prefix *, struct rib *);
 extern int kernel_update_ipv4 (struct prefix *, struct rib *);
@@ -39,4 +41,8 @@ extern int kernel_add_ipv6 (struct prefix *, struct rib *);
 extern int kernel_update_ipv6 (struct prefix *, struct rib *);
 extern int kernel_delete_ipv6 (struct prefix *, struct rib *);
 
+extern int kernel_add_lsp (zebra_lsp_t *);
+extern int kernel_upd_lsp (zebra_lsp_t *);
+extern int kernel_del_lsp (zebra_lsp_t *);
+
 #endif /* _ZEBRA_RT_H */
index 41ecb1d0c4960025758d17f80d474429f37e55d5..8d19cbb5dcf1a3f6541038befbc91e1d16fdcb89 100644 (file)
@@ -40,6 +40,7 @@
 #include "privs.h"
 #include "nexthop.h"
 #include "vrf.h"
+#include "mpls.h"
 
 #include "zebra/zserv.h"
 #include "zebra/zebra_ns.h"
@@ -50,6 +51,7 @@
 #include "zebra/debug.h"
 #include "zebra/rtadv.h"
 #include "zebra/zebra_ptm.h"
+#include "zebra/zebra_mpls.h"
 
 #include "rt_netlink.h"
 
@@ -69,6 +71,27 @@ static const struct message nlmsg_str[] = {
   {0, NULL}
 };
 
+/* TODO - Temporary definitions, need to refine. */
+#ifndef AF_MPLS
+#define AF_MPLS 28
+#endif
+
+#ifndef RTA_VIA
+#define RTA_VIA                16
+#endif
+
+#ifndef RTA_NEWDST
+#define RTA_NEWDST     19
+#endif
+/* End of temporary definitions */
+
+struct gw_family_t
+{
+  u_int16_t     filler;
+  u_int16_t     family;
+  union g_addr  gate;
+};
+
 extern struct zebra_privs_t zserv_privs;
 
 extern u_int32_t nl_rcvbufsize;
@@ -439,10 +462,10 @@ netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *,
 
           /* OK we got netlink message. */
           if (IS_ZEBRA_DEBUG_KERNEL)
-            zlog_debug ("netlink_parse_info: %s type %s(%u), seq=%u, pid=%u",
+            zlog_debug ("netlink_parse_info: %s type %s(%u), len=%d, seq=%u, pid=%u",
                        nl->name,
                        lookup (nlmsg_str, h->nlmsg_type), h->nlmsg_type,
-                       h->nlmsg_seq, h->nlmsg_pid);
+                       h->nlmsg_len, h->nlmsg_seq, h->nlmsg_pid);
 
           /* skip unsolicited messages originating from command socket
            * linux sets the originators port-id for {NEW|DEL}ADDR messages,
@@ -961,6 +984,11 @@ netlink_routing_table (struct sockaddr_nl *snl, struct nlmsghdr *h,
   if (rtm->rtm_src_len != 0)
     return 0;
 
+  /* We don't care about change notifications for the MPLS table. */
+  /* TODO: Revisit this. */
+  if (rtm->rtm_family == AF_MPLS)
+    return 0;
+
   /* Table corresponding to route. */
   if (tb[RTA_TABLE])
     table = *(int *) RTA_DATA (tb[RTA_TABLE]);
@@ -1161,6 +1189,11 @@ netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
       return 0;
     }
 
+  /* We don't care about change notifications for the MPLS table. */
+  /* TODO: Revisit this. */
+  if (rtm->rtm_family == AF_MPLS)
+    return 0;
+
   len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct rtmsg));
   if (len < 0)
     return -1;
@@ -1723,10 +1756,10 @@ netlink_talk (struct nlmsghdr *n, struct nlsock *nl, struct zebra_ns *zns)
   n->nlmsg_flags |= NLM_F_ACK;
 
   if (IS_ZEBRA_DEBUG_KERNEL)
-    zlog_debug ("netlink_talk: %s type %s(%u), seq=%u flags 0x%x",
+    zlog_debug ("netlink_talk: %s type %s(%u), len=%d seq=%u flags 0x%x",
                nl->name,
                lookup (nlmsg_str, n->nlmsg_type), n->nlmsg_type,
-               n->nlmsg_seq, n->nlmsg_flags);
+               n->nlmsg_len, n->nlmsg_seq, n->nlmsg_flags);
 
   /* Send message to netlink interface. */
   if (zserv_privs.change (ZPRIVS_RAISE))
@@ -1757,6 +1790,60 @@ netlink_talk (struct nlmsghdr *n, struct nlsock *nl, struct zebra_ns *zns)
   return netlink_parse_info (netlink_talk_filter, nl, zns, 0);
 }
 
+static void
+_netlink_route_nl_add_gateway_info (u_char route_family, u_char gw_family,
+                                    struct nlmsghdr *nlmsg,
+                                    size_t req_size, int bytelen,
+                                    struct nexthop *nexthop)
+{
+  if (route_family == AF_MPLS)
+    {
+      struct gw_family_t gw_fam;
+
+      gw_fam.family = gw_family;
+      if (gw_family == AF_INET)
+        memcpy (&gw_fam.gate.ipv4, &nexthop->gate.ipv4, bytelen);
+      else
+        memcpy (&gw_fam.gate.ipv6, &nexthop->gate.ipv6, bytelen);
+      addattr_l (nlmsg, req_size, RTA_VIA, &gw_fam.family, bytelen+2);
+    }
+  else
+    {
+      if (gw_family == AF_INET)
+        addattr_l (nlmsg, req_size, RTA_GATEWAY, &nexthop->gate.ipv4, bytelen);
+      else
+        addattr_l (nlmsg, req_size, RTA_GATEWAY, &nexthop->gate.ipv6, bytelen);
+    }
+}
+
+static void
+_netlink_route_rta_add_gateway_info (u_char route_family, u_char gw_family,
+                                     struct rtattr *rta, struct rtnexthop *rtnh,
+                                     size_t req_size, int bytelen,
+                                     struct nexthop *nexthop)
+{
+  if (route_family == AF_MPLS)
+    {
+      struct gw_family_t gw_fam;
+
+      gw_fam.family = gw_family;
+      if (gw_family == AF_INET)
+        memcpy (&gw_fam.gate.ipv4, &nexthop->gate.ipv4, bytelen);
+      else
+        memcpy (&gw_fam.gate.ipv6, &nexthop->gate.ipv6, bytelen);
+      rta_addattr_l (rta, req_size, RTA_VIA, &gw_fam.family, bytelen+2);
+      rtnh->rtnh_len += RTA_LENGTH (bytelen + 2);
+    }
+  else
+    {
+      if (gw_family == AF_INET)
+        rta_addattr_l (rta, req_size, RTA_GATEWAY, &nexthop->gate.ipv4, bytelen);
+      else
+        rta_addattr_l (rta, req_size, RTA_GATEWAY, &nexthop->gate.ipv6, bytelen);
+      rtnh->rtnh_len += sizeof (struct rtattr) + bytelen;
+    }
+}
+
 /* This function takes a nexthop as argument and adds
  * the appropriate netlink attributes to an existing
  * netlink message.
@@ -1778,6 +1865,8 @@ _netlink_route_build_singlepath(
         size_t req_size,
        int cmd)
 {
+  mpls_lse_t out_lse;
+  char label_buf[100];
 
   if (rtmsg->rtm_family == AF_INET &&
       (nexthop->type == NEXTHOP_TYPE_IPV6
@@ -1805,14 +1894,28 @@ _netlink_route_build_singlepath(
       return;
     }
 
+  label_buf[0] = '\0';
+  if (rtmsg->rtm_family == AF_MPLS)
+    {
+      assert (nexthop->nh_label);
+
+      /* Fill out label, if present. */
+      if (nexthop->nh_label->label[0] != MPLS_IMP_NULL_LABEL)
+        {
+          out_lse = mpls_lse_encode (nexthop->nh_label->label[0], 0, 0, 1);
+          addattr_l (nlmsg, req_size, RTA_NEWDST, &out_lse, sizeof(mpls_lse_t));
+          sprintf (label_buf, "label %d", nexthop->nh_label->label[0]);
+        }
+    }
+
   if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ONLINK))
     rtmsg->rtm_flags |= RTNH_F_ONLINK;
 
   if (nexthop->type == NEXTHOP_TYPE_IPV4
       || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
     {
-      addattr_l (nlmsg, req_size, RTA_GATEWAY,
-                 &nexthop->gate.ipv4, bytelen);
+      _netlink_route_nl_add_gateway_info (rtmsg->rtm_family, AF_INET, nlmsg,
+                                          req_size, bytelen, nexthop);
 
       if (cmd == RTM_NEWROUTE)
        {
@@ -1826,16 +1929,16 @@ _netlink_route_build_singlepath(
 
       if (IS_ZEBRA_DEBUG_KERNEL)
         zlog_debug("netlink_route_multipath() (%s): "
-                   "nexthop via %s if %u",
+                   "nexthop via %s %s if %u",
                    routedesc,
                    inet_ntoa (nexthop->gate.ipv4),
-                   nexthop->ifindex);
+                   label_buf, nexthop->ifindex);
     }
   if (nexthop->type == NEXTHOP_TYPE_IPV6
       || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
     {
-      addattr_l (nlmsg, req_size, RTA_GATEWAY,
-                 &nexthop->gate.ipv6, bytelen);
+      _netlink_route_nl_add_gateway_info (rtmsg->rtm_family, AF_INET6, nlmsg,
+                                          req_size, bytelen, nexthop);
 
       if (cmd == RTM_NEWROUTE)
        {
@@ -1849,10 +1952,10 @@ _netlink_route_build_singlepath(
 
       if (IS_ZEBRA_DEBUG_KERNEL)
         zlog_debug("netlink_route_multipath() (%s): "
-                   "nexthop via %s if %u",
+                   "nexthop via %s %s if %u",
                    routedesc,
                    inet6_ntoa (nexthop->gate.ipv6),
-                   nexthop->ifindex);
+                   label_buf, nexthop->ifindex);
     }
   if (nexthop->type == NEXTHOP_TYPE_IFINDEX
       || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
@@ -1920,6 +2023,9 @@ _netlink_route_build_multipath(
         struct rtmsg *rtmsg,
         union g_addr **src)
 {
+  mpls_lse_t out_lse;
+  char label_buf[100];
+
   rtnh->rtnh_len = sizeof (*rtnh);
   rtnh->rtnh_flags = 0;
   rtnh->rtnh_hops = 0;
@@ -1952,6 +2058,21 @@ _netlink_route_build_multipath(
       return;
     }
 
+  label_buf[0] = '\0';
+  if (rtmsg->rtm_family == AF_MPLS)
+    {
+      assert (nexthop->nh_label);
+
+      /* Fill out label, if present. */
+      if (nexthop->nh_label->label[0] != MPLS_IMP_NULL_LABEL)
+        {
+          out_lse = mpls_lse_encode (nexthop->nh_label->label[0], 0, 0, 1);
+          rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_NEWDST,
+                         &out_lse, sizeof(mpls_lse_t));
+          rtnh->rtnh_len += RTA_LENGTH (sizeof(mpls_lse_t));
+          sprintf (label_buf, "label %d", nexthop->nh_label->label[0]);
+        }
+    }
 
   if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ONLINK))
     rtnh->rtnh_flags |= RTNH_F_ONLINK;
@@ -1959,10 +2080,8 @@ _netlink_route_build_multipath(
   if (nexthop->type == NEXTHOP_TYPE_IPV4
       || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
     {
-      rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY,
-                     &nexthop->gate.ipv4, bytelen);
-      rtnh->rtnh_len += sizeof (struct rtattr) + bytelen;
-
+      _netlink_route_rta_add_gateway_info (rtmsg->rtm_family, AF_INET, rta,
+                                     rtnh, NL_PKT_BUF_SIZE, bytelen, nexthop);
       if (nexthop->rmap_src.ipv4.s_addr)
         *src = &nexthop->rmap_src;
       else if (nexthop->src.ipv4.s_addr)
@@ -1970,17 +2089,16 @@ _netlink_route_build_multipath(
 
       if (IS_ZEBRA_DEBUG_KERNEL)
         zlog_debug("netlink_route_multipath() (%s): "
-                   "nexthop via %s if %u",
+                   "nexthop via %s %s if %u",
                    routedesc,
                    inet_ntoa (nexthop->gate.ipv4),
-                   nexthop->ifindex);
+                   label_buf, nexthop->ifindex);
     }
   if (nexthop->type == NEXTHOP_TYPE_IPV6
       || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
     {
-      rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY,
-                     &nexthop->gate.ipv6, bytelen);
-      rtnh->rtnh_len += sizeof (struct rtattr) + bytelen;
+      _netlink_route_rta_add_gateway_info (rtmsg->rtm_family, AF_INET6, rta,
+                                       rtnh, NL_PKT_BUF_SIZE, bytelen, nexthop);
 
       if (!IN6_IS_ADDR_UNSPECIFIED(&nexthop->rmap_src.ipv6))
         *src = &nexthop->rmap_src;
@@ -1989,10 +2107,10 @@ _netlink_route_build_multipath(
 
       if (IS_ZEBRA_DEBUG_KERNEL)
         zlog_debug("netlink_route_multipath() (%s): "
-                   "nexthop via %s if %u",
+                   "nexthop via %s %s if %u",
                    routedesc,
                    inet6_ntoa (nexthop->gate.ipv6),
-                   nexthop->ifindex);
+                   label_buf, nexthop->ifindex);
     }
   /* ifindex */
   if (nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX
@@ -2023,6 +2141,44 @@ _netlink_route_build_multipath(
     }
 }
 
+static inline void
+_netlink_mpls_build_singlepath(
+        const char *routedesc,
+        zebra_nhlfe_t *nhlfe,
+        struct nlmsghdr *nlmsg,
+        struct rtmsg *rtmsg,
+        size_t req_size,
+       int cmd)
+{
+  int bytelen;
+  u_char family;
+
+  family = NHLFE_FAMILY (nhlfe);
+  bytelen = (family == AF_INET ? 4 : 16);
+  _netlink_route_build_singlepath(routedesc, bytelen, nhlfe->nexthop,
+                                  nlmsg, rtmsg, req_size, cmd);
+}
+
+
+static inline void
+_netlink_mpls_build_multipath(
+        const char *routedesc,
+        zebra_nhlfe_t *nhlfe,
+        struct rtattr *rta,
+        struct rtnexthop *rtnh,
+        struct rtmsg *rtmsg,
+        union g_addr **src)
+{
+  int bytelen;
+  u_char family;
+
+  family = NHLFE_FAMILY (nhlfe);
+  bytelen = (family == AF_INET ? 4 : 16);
+  _netlink_route_build_multipath(routedesc, bytelen, nhlfe->nexthop,
+                                 rta, rtnh, rtmsg, src);
+}
+
+
 /* Log debug information for netlink_route_multipath
  * if debug logging is enabled.
  *
@@ -2048,11 +2204,22 @@ _netlink_route_debug(
       zlog_debug ("netlink_route_multipath() (%s): %s %s vrf %u type %s",
                  routedesc,
                  lookup (nlmsg_str, cmd),
-                 prefix2str (p, buf, sizeof(buf)),
-                 zvrf->vrf_id, nexthop_type_to_str (nexthop->type));
+                 prefix2str (p, buf, sizeof(buf)), zvrf->vrf_id,
+                 (nexthop) ? nexthop_type_to_str (nexthop->type) : "UNK");
     }
 }
 
+static void
+_netlink_mpls_debug(
+        int cmd,
+        u_int32_t label,
+        const char *routedesc)
+{
+  if (IS_ZEBRA_DEBUG_KERNEL)
+    zlog_debug ("netlink_mpls_multipath() (%s): %s %u/20",
+                routedesc, lookup (nlmsg_str, cmd), label);
+}
+
 static int
 netlink_neigh_update (int cmd, int ifindex, uint32_t addr, char *lla, int llalen)
 {
@@ -2493,6 +2660,272 @@ kernel_neigh_update (int add, int ifindex, uint32_t addr, char *lla, int llalen)
                              lla, llalen);
 }
 
+/*
+ * MPLS label forwarding table change via netlink interface.
+ */
+static int
+netlink_mpls_multipath (int cmd, zebra_lsp_t *lsp)
+{
+  mpls_lse_t lse;
+  zebra_nhlfe_t *nhlfe;
+  struct nexthop *nexthop = NULL;
+  int nexthop_num;
+  const char *routedesc;
+  struct zebra_ns *zns = zebra_ns_lookup (NS_DEFAULT);
+
+  struct
+  {
+    struct nlmsghdr n;
+    struct rtmsg r;
+    char buf[NL_PKT_BUF_SIZE];
+  } req;
+
+  memset (&req, 0, sizeof req - NL_PKT_BUF_SIZE);
+
+
+  /*
+   * Count # nexthops so we can decide whether to use singlepath
+   * or multipath case.
+   */
+  nexthop_num = 0;
+  for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next)
+    {
+      nexthop = nhlfe->nexthop;
+      if (!nexthop)
+        continue;
+      if (cmd == RTM_NEWROUTE)
+        {
+          /* Count all selected NHLFEs */
+          if (CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_SELECTED) &&
+              CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
+            nexthop_num++;
+        }
+      else /* DEL */
+        {
+          /* Count all installed NHLFEs */
+          if (CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED) &&
+              CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
+            nexthop_num++;
+        }
+    }
+
+  if (nexthop_num == 0) // unexpected
+    return 0;
+
+  req.n.nlmsg_len = NLMSG_LENGTH (sizeof (struct rtmsg));
+  req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST;
+  req.n.nlmsg_type = cmd;
+  req.r.rtm_family = AF_MPLS;
+  req.r.rtm_table = RT_TABLE_MAIN;
+  req.r.rtm_dst_len = MPLS_LABEL_LEN_BITS;
+  req.r.rtm_protocol = RTPROT_ZEBRA;
+  req.r.rtm_scope = RT_SCOPE_UNIVERSE;
+  req.r.rtm_type = RTN_UNICAST;
+
+  if (cmd == RTM_NEWROUTE)
+    /* We do a replace to handle update. */
+    req.n.nlmsg_flags |= NLM_F_REPLACE;
+
+  /* Fill destination */
+  lse = mpls_lse_encode (lsp->ile.in_label, 0, 0, 1);
+  addattr_l (&req.n, sizeof req, RTA_DST, &lse, sizeof(mpls_lse_t));
+
+  /* Fill nexthops (paths) based on single-path or multipath. The paths
+   * chosen depend on the operation.
+   */
+  if (nexthop_num == 1 || MULTIPATH_NUM == 1)
+    {
+      routedesc = "single hop";
+      _netlink_mpls_debug(cmd, lsp->ile.in_label, routedesc);
+
+      nexthop_num = 0;
+      for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next)
+        {
+          nexthop = nhlfe->nexthop;
+          if (!nexthop)
+            continue;
+
+          if ((cmd == RTM_NEWROUTE &&
+               (CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_SELECTED) &&
+                CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))) ||
+              (cmd == RTM_DELROUTE &&
+               (CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED) &&
+                CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))))
+            {
+              /* Add the gateway */
+              _netlink_mpls_build_singlepath(routedesc, nhlfe,
+                                             &req.n, &req.r, sizeof req, cmd);
+              if (cmd == RTM_NEWROUTE)
+                {
+                  SET_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED);
+                  SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+                }
+              else
+                {
+                  UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED);
+                  UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+                }
+              nexthop_num++;
+              break;
+            }
+        }
+    }
+  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);
+
+      routedesc = "multihop";
+      _netlink_mpls_debug(cmd, lsp->ile.in_label, routedesc);
+
+      nexthop_num = 0;
+      for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next)
+        {
+          nexthop = nhlfe->nexthop;
+          if (!nexthop)
+            continue;
+
+          if (MULTIPATH_NUM != 0 && nexthop_num >= MULTIPATH_NUM)
+            break;
+
+          if ((cmd == RTM_NEWROUTE &&
+               (CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_SELECTED) &&
+                CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))) ||
+              (cmd == RTM_DELROUTE &&
+               (CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED) &&
+                CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))))
+            {
+              nexthop_num++;
+
+              /* Build the multipath */
+              _netlink_mpls_build_multipath(routedesc, nhlfe, rta,
+                                            rtnh, &req.r, &src1);
+              rtnh = RTNH_NEXT (rtnh);
+
+              if (cmd == RTM_NEWROUTE)
+                {
+                  SET_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED);
+                  SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+                }
+              else
+                {
+                  UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED);
+                  UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+                }
+
+            }
+        }
+
+      /* Add the multipath */
+      if (rta->rta_len > RTA_LENGTH (0))
+        addattr_l (&req.n, NL_PKT_BUF_SIZE, RTA_MULTIPATH, RTA_DATA (rta),
+                   RTA_PAYLOAD (rta));
+    }
+
+  /* Talk to netlink socket. */
+  return netlink_talk (&req.n, &zns->netlink_cmd, zns);
+}
+
+/*
+ * Handle failure in LSP install, clear flags for NHLFE.
+ */
+static inline void
+clear_nhlfe_installed (zebra_lsp_t *lsp)
+{
+  zebra_nhlfe_t *nhlfe;
+  struct nexthop *nexthop;
+
+  for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next)
+    {
+      nexthop = nhlfe->nexthop;
+      if (!nexthop)
+        continue;
+
+      UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED);
+      UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+    }
+}
+
+/*
+ * Install Label Forwarding entry into the kernel.
+ */
+int
+kernel_add_lsp (zebra_lsp_t *lsp)
+{
+  int ret;
+
+  if (!lsp || !lsp->best_nhlfe) // unexpected
+    return -1;
+
+  UNSET_FLAG (lsp->flags, LSP_FLAG_CHANGED);
+  ret = netlink_mpls_multipath (RTM_NEWROUTE, lsp);
+  if (!ret)
+    SET_FLAG (lsp->flags, LSP_FLAG_INSTALLED);
+  else
+    clear_nhlfe_installed (lsp);
+
+  return ret;
+}
+
+/*
+ * Update Label Forwarding entry in the kernel. This means that the Label
+ * forwarding entry is already installed and needs an update - either a new
+ * path is to be added, an installed path has changed (e.g., outgoing label)
+ * or an installed path (but not all paths) has to be removed.
+ * TODO: Performs a DEL followed by ADD now, need to change to REPLACE. Note
+ * that REPLACE was originally implemented for IPv4 nexthops but removed as
+ * it was not functioning when moving from swap to PHP as that was signaled
+ * through the metric field (before kernel-MPLS). This shouldn't be an issue
+ * any longer, so REPLACE can be reintroduced.
+ */
+int
+kernel_upd_lsp (zebra_lsp_t *lsp)
+{
+  int ret;
+
+  if (!lsp || !lsp->best_nhlfe) // unexpected
+    return -1;
+
+  UNSET_FLAG (lsp->flags, LSP_FLAG_CHANGED);
+
+  /* First issue a DEL and clear the installed flag. */
+  netlink_mpls_multipath (RTM_DELROUTE, lsp);
+  UNSET_FLAG (lsp->flags, LSP_FLAG_INSTALLED);
+
+  /* Then issue an ADD. */
+  ret = netlink_mpls_multipath (RTM_NEWROUTE, lsp);
+  if (!ret)
+    SET_FLAG (lsp->flags, LSP_FLAG_INSTALLED);
+  else
+    clear_nhlfe_installed (lsp);
+
+  return ret;
+}
+
+/*
+ * Delete Label Forwarding entry from the kernel.
+ */
+int
+kernel_del_lsp (zebra_lsp_t *lsp)
+{
+  if (!lsp) // unexpected
+    return -1;
+
+  if (CHECK_FLAG (lsp->flags, LSP_FLAG_INSTALLED))
+    {
+      netlink_mpls_multipath (RTM_DELROUTE, lsp);
+      UNSET_FLAG (lsp->flags, LSP_FLAG_INSTALLED);
+    }
+
+  return 0;
+}
+
 extern struct thread_master *master;
 
 /* Kernel route reflection. */
index db1f31859539c01295138ebac39ab23bab609664..892aec965ad410273568c628b36528814099603c 100644 (file)
@@ -48,6 +48,7 @@
 
 DEFINE_MTYPE_STATIC(ZEBRA, LSP,                        "MPLS LSP object")
 DEFINE_MTYPE_STATIC(ZEBRA, SLSP,               "MPLS static LSP config")
+DEFINE_MTYPE_STATIC(ZEBRA, NHLFE,              "MPLS nexthop object")
 DEFINE_MTYPE_STATIC(ZEBRA, SNHLFE,             "MPLS static nexthop object")
 DEFINE_MTYPE_STATIC(ZEBRA, SNHLFE_IFNAME,      "MPLS static nexthop ifname")
 
@@ -59,6 +60,53 @@ static unsigned int
 label_hash (void *p);
 static int
 label_cmp (const void *p1, const void *p2);
+static int
+nhlfe_nexthop_active_ipv4 (zebra_nhlfe_t *nhlfe, struct nexthop *nexthop);
+static int
+nhlfe_nexthop_active_ipv6 (zebra_nhlfe_t *nhlfe, struct nexthop *nexthop);
+static int
+nhlfe_nexthop_active (zebra_nhlfe_t *nhlfe);
+static void
+lsp_select_best_nhlfe (zebra_lsp_t *lsp);
+static void
+lsp_uninstall_from_kernel (struct hash_backet *backet, void *ctxt);
+static void
+lsp_schedule (struct hash_backet *backet, void *ctxt);
+static wq_item_status
+lsp_process (struct work_queue *wq, void *data);
+static void
+lsp_processq_del (struct work_queue *wq, void *data);
+static void
+lsp_processq_complete (struct work_queue *wq);
+static int
+lsp_processq_add (zebra_lsp_t *lsp);
+static void *
+lsp_alloc (void *p);
+static char *
+nhlfe2str (zebra_nhlfe_t *nhlfe, char *buf, int size);
+static int
+nhlfe_nhop_match (zebra_nhlfe_t *nhlfe, enum nexthop_types_t gtype,
+                  union g_addr *gate, char *ifname, ifindex_t ifindex);
+static zebra_nhlfe_t *
+nhlfe_find (zebra_lsp_t *lsp, enum lsp_types_t lsp_type,
+            enum nexthop_types_t gtype, union g_addr *gate,
+            char *ifname, ifindex_t ifindex);
+static zebra_nhlfe_t *
+nhlfe_add (zebra_lsp_t *lsp, enum lsp_types_t lsp_type,
+           enum nexthop_types_t gtype, union g_addr *gate,
+           char *ifname, ifindex_t ifindex, mpls_label_t out_label);
+static int
+nhlfe_del (zebra_nhlfe_t *snhlfe);
+static int
+static_lsp_install (struct zebra_vrf *zvrf, mpls_label_t in_label,
+                    mpls_label_t out_label, enum nexthop_types_t gtype,
+                    union g_addr *gate, char *ifname, ifindex_t ifindex);
+static int
+static_lsp_uninstall (struct zebra_vrf *zvrf, mpls_label_t in_label,
+                      enum nexthop_types_t gtype, union g_addr *gate,
+                      char *ifname, ifindex_t ifindex);
+static int
+static_lsp_uninstall_all (struct zebra_vrf *zvrf, mpls_label_t in_label);
 static void
 lsp_config_write (struct hash_backet *backet, void *ctxt);
 static void *
@@ -79,6 +127,8 @@ static int
 snhlfe_del_all (zebra_slsp_t *slsp);
 static char *
 snhlfe2str (zebra_snhlfe_t *snhlfe, char *buf, int size);
+static void
+mpls_processq_init (struct zebra_t *zebra);
 
 
 
@@ -108,6 +158,810 @@ label_cmp (const void *p1, const void *p2)
   return (ile1->in_label == ile2->in_label);
 }
 
+/*
+ * Check if an IPv4 nexthop for a NHLFE is active. Update nexthop based on
+ * the passed flag.
+ * NOTE: Looking only for connected routes right now.
+ */
+static int
+nhlfe_nexthop_active_ipv4 (zebra_nhlfe_t *nhlfe, struct nexthop *nexthop)
+{
+  struct route_table *table;
+  struct prefix_ipv4 p;
+  struct route_node *rn;
+  struct rib *match;
+
+  table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, VRF_DEFAULT);
+  if (!table)
+    return 0;
+
+  /* Lookup nexthop in IPv4 routing table. */
+  memset (&p, 0, sizeof (struct prefix_ipv4));
+  p.family = AF_INET;
+  p.prefixlen = IPV4_MAX_PREFIXLEN;
+  p.prefix = nexthop->gate.ipv4;
+
+  rn = route_node_match (table, (struct prefix *) &p);
+  if (!rn)
+    return 0;
+
+  route_unlock_node (rn);
+
+  /* Locate a valid connected route. */
+  RNODE_FOREACH_RIB (rn, match)
+    {
+      if ((match->type == ZEBRA_ROUTE_CONNECT) &&
+          !CHECK_FLAG (match->status, RIB_ENTRY_REMOVED) &&
+          CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED))
+        break;
+    }
+
+  if (!match || !match->nexthop)
+    return 0;
+
+  nexthop->ifindex = match->nexthop->ifindex;
+  return 1;
+}
+
+
+/*
+ * Check if an IPv6 nexthop for a NHLFE is active. Update nexthop based on
+ * the passed flag.
+ * NOTE: Looking only for connected routes right now.
+ */
+static int
+nhlfe_nexthop_active_ipv6 (zebra_nhlfe_t *nhlfe, struct nexthop *nexthop)
+{
+  struct route_table *table;
+  struct prefix_ipv6 p;
+  struct route_node *rn;
+  struct rib *match;
+
+  table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, VRF_DEFAULT);
+  if (!table)
+    return 0;
+
+  /* Lookup nexthop in IPv6 routing table. */
+  memset (&p, 0, sizeof (struct prefix_ipv6));
+  p.family = AF_INET6;
+  p.prefixlen = IPV6_MAX_PREFIXLEN;
+  p.prefix = nexthop->gate.ipv6;
+
+  rn = route_node_match (table, (struct prefix *) &p);
+  if (!rn)
+    return 0;
+
+  route_unlock_node (rn);
+
+  /* Locate a valid connected route. */
+  RNODE_FOREACH_RIB (rn, match)
+    {
+      if ((match->type == ZEBRA_ROUTE_CONNECT) &&
+          !CHECK_FLAG (match->status, RIB_ENTRY_REMOVED) &&
+          CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED))
+        break;
+    }
+
+  if (!match || !match->nexthop)
+    return 0;
+
+  nexthop->ifindex = match->nexthop->ifindex;
+  return 1;
+}
+
+
+/*
+ * Check the nexthop reachability for a NHLFE and return if valid (reachable)
+ * or not.
+ * NOTE: Each NHLFE points to only 1 nexthop.
+ */
+static int
+nhlfe_nexthop_active (zebra_nhlfe_t *nhlfe)
+{
+  struct nexthop *nexthop;
+  struct interface *ifp;
+
+  nexthop = nhlfe->nexthop;
+  if (!nexthop) // unexpected
+    return 0;
+
+  /* Check on nexthop based on type. */
+  switch (nexthop->type)
+    {
+      case NEXTHOP_TYPE_IPV4:
+        if (nhlfe_nexthop_active_ipv4 (nhlfe, nexthop))
+          SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+        else
+          UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+        break;
+
+      case NEXTHOP_TYPE_IPV6:
+        if (nhlfe_nexthop_active_ipv6 (nhlfe, nexthop))
+          SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+        else
+          UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+        break;
+
+      case NEXTHOP_TYPE_IPV6_IFINDEX:
+        if (IN6_IS_ADDR_LINKLOCAL (&nexthop->gate.ipv6))
+          {
+            ifp = if_lookup_by_index (nexthop->ifindex);
+            if (ifp && if_is_operative(ifp))
+              SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+            else
+              UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+          }
+        else
+          {
+            if (nhlfe_nexthop_active_ipv6 (nhlfe, nexthop))
+              SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+            else
+              UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+          }
+        break;
+
+    default:
+      break;
+    }
+
+  return CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+}
+
+/*
+ * Walk through NHLFEs for a LSP forwarding entry, verify nexthop
+ * reachability and select the best. Multipath entries are also
+ * marked. This is invoked when an LSP scheduled for processing (due
+ * to some change) is examined.
+ */
+static void
+lsp_select_best_nhlfe (zebra_lsp_t *lsp)
+{
+  zebra_nhlfe_t *nhlfe;
+  zebra_nhlfe_t *best;
+  struct nexthop *nexthop;
+  int changed = 0;
+
+  if (!lsp)
+    return;
+
+  best = NULL;
+  lsp->num_ecmp = 0;
+  UNSET_FLAG (lsp->flags, LSP_FLAG_CHANGED);
+
+  /*
+   * First compute the best path, after checking nexthop status. We are only
+   * concerned with non-deleted NHLFEs.
+   */
+  for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next)
+    {
+      /* Clear selection flags. */
+      UNSET_FLAG (nhlfe->flags,
+                  (NHLFE_FLAG_SELECTED | NHLFE_FLAG_MULTIPATH));
+
+      if (!CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_DELETED) &&
+          nhlfe_nexthop_active (nhlfe))
+        {
+          if (!best || (nhlfe->distance < best->distance))
+            best = nhlfe;
+        }
+    }
+
+  lsp->best_nhlfe = best;
+  if (!lsp->best_nhlfe)
+    return;
+
+  /* Mark best NHLFE as selected. */
+  SET_FLAG (lsp->best_nhlfe->flags, NHLFE_FLAG_SELECTED);
+
+  /*
+   * If best path exists, see if there is ECMP. While doing this, note if a
+   * new (uninstalled) NHLFE has been selected, an installed entry that is
+   * still selected has a change or an installed entry is to be removed.
+   */
+  for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next)
+    {
+      int nh_chg, nh_sel, nh_inst;
+
+      nexthop = nhlfe->nexthop;
+      if (!nexthop) // unexpected
+        continue;
+
+      if (!CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_DELETED) &&
+          CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE) &&
+          (nhlfe->distance == lsp->best_nhlfe->distance))
+        {
+          SET_FLAG (nhlfe->flags, NHLFE_FLAG_SELECTED);
+          SET_FLAG (nhlfe->flags, NHLFE_FLAG_MULTIPATH);
+          lsp->num_ecmp++;
+        }
+
+      if (CHECK_FLAG (lsp->flags, LSP_FLAG_INSTALLED) &&
+          !changed)
+        {
+          nh_chg = CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_CHANGED);
+          nh_sel = CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_SELECTED);
+          nh_inst = CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED);
+
+          if ((nh_sel && !nh_inst) ||
+              (nh_sel && nh_inst && nh_chg) ||
+              (nh_inst && !nh_sel))
+            changed = 1;
+        }
+
+      /* We have finished examining, clear changed flag. */
+      UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_CHANGED);
+    }
+
+  if (changed)
+    SET_FLAG (lsp->flags, LSP_FLAG_CHANGED);
+}
+
+/*
+ * Delete LSP forwarding entry from kernel, if installed. Called upon
+ * process exit.
+ */
+static void
+lsp_uninstall_from_kernel (struct hash_backet *backet, void *ctxt)
+{
+  zebra_lsp_t *lsp;
+
+  lsp = (zebra_lsp_t *) backet->data;
+  if (CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED))
+    kernel_del_lsp (lsp);
+}
+
+/*
+ * Schedule LSP forwarding entry for processing. Called upon changes
+ * that may impact LSPs such as nexthop / connected route changes.
+ */
+static void
+lsp_schedule (struct hash_backet *backet, void *ctxt)
+{
+  zebra_lsp_t *lsp;
+
+  lsp = (zebra_lsp_t *) backet->data;
+  lsp_processq_add (lsp);
+}
+
+/*
+ * Process a LSP entry that is in the queue. Recalculate best NHLFE and
+ * any multipaths and update or delete from the kernel, as needed.
+ */
+static wq_item_status
+lsp_process (struct work_queue *wq, void *data)
+{
+  zebra_lsp_t *lsp;
+  zebra_nhlfe_t *oldbest, *newbest;
+  char buf[BUFSIZ], buf2[BUFSIZ];
+
+  lsp = (zebra_lsp_t *)data;
+  if (!lsp) // unexpected
+    return WQ_SUCCESS;
+
+  oldbest = lsp->best_nhlfe;
+
+  /* Select best NHLFE(s) */
+  lsp_select_best_nhlfe (lsp);
+
+  newbest = lsp->best_nhlfe;
+
+  if (IS_ZEBRA_DEBUG_MPLS)
+    {
+      if (oldbest)
+        nhlfe2str (oldbest, buf, BUFSIZ);
+      if (newbest)
+        nhlfe2str (newbest, buf2, BUFSIZ);
+      zlog_debug ("Process LSP in-label %u oldbest %s newbest %s "
+                  "flags 0x%x ecmp# %d",
+                  lsp->ile.in_label, oldbest ? buf : "NULL",
+                  newbest ? buf2 : "NULL", lsp->flags, lsp->num_ecmp);
+    }
+
+  if (!CHECK_FLAG (lsp->flags, LSP_FLAG_INSTALLED))
+    {
+      /* Not already installed */
+      if (newbest)
+        kernel_add_lsp (lsp);
+    }
+  else
+    {
+      /* Installed, may need an update and/or delete. */
+      if (!newbest)
+        kernel_del_lsp (lsp);
+      else if (CHECK_FLAG (lsp->flags, LSP_FLAG_CHANGED))
+        kernel_upd_lsp (lsp);
+    }
+
+  return WQ_SUCCESS;
+}
+
+
+/*
+ * Callback upon processing completion of a LSP forwarding entry.
+ */
+static void
+lsp_processq_del (struct work_queue *wq, void *data)
+{
+  struct zebra_vrf *zvrf;
+  zebra_lsp_t *lsp;
+  struct hash *lsp_table;
+  zebra_nhlfe_t *nhlfe, *nhlfe_next;
+
+  zvrf = vrf_info_lookup(VRF_DEFAULT);
+  assert (zvrf);
+
+  lsp_table = zvrf->lsp_table;
+  if (!lsp_table) // unexpected
+    return;
+
+  lsp = (zebra_lsp_t *)data;
+  if (!lsp) // unexpected
+    return;
+
+  /* Clear flag, remove any NHLFEs marked for deletion. If no NHLFEs exist,
+   * delete LSP entry also.
+   */
+  UNSET_FLAG (lsp->flags, LSP_FLAG_SCHEDULED);
+
+  for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe_next)
+    {
+      nhlfe_next = nhlfe->next;
+      if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED))
+        nhlfe_del (nhlfe);
+    }
+
+  if (!lsp->nhlfe_list)
+    {
+      if (IS_ZEBRA_DEBUG_MPLS)
+        zlog_debug ("Free LSP in-label %u flags 0x%x",
+                    lsp->ile.in_label, lsp->flags);
+
+      lsp = hash_release(lsp_table, &lsp->ile);
+      if (lsp)
+        XFREE(MTYPE_LSP, lsp);
+    }
+}
+
+/*
+ * Callback upon finishing the processing of all scheduled
+ * LSP forwarding entries.
+ */
+static void
+lsp_processq_complete (struct work_queue *wq)
+{
+  /* Nothing to do for now. */
+}
+
+/*
+ * Add LSP forwarding entry to queue for subsequent processing.
+ */
+static int
+lsp_processq_add (zebra_lsp_t *lsp)
+{
+  /* If already scheduled, exit. */
+  if (CHECK_FLAG (lsp->flags, LSP_FLAG_SCHEDULED))
+    return 0;
+
+  work_queue_add (zebrad.lsp_process_q, lsp);
+  SET_FLAG (lsp->flags, LSP_FLAG_SCHEDULED);
+  return 0;
+}
+
+/*
+ * Callback to allocate LSP forwarding table entry.
+ */
+static void *
+lsp_alloc (void *p)
+{
+  const zebra_ile_t *ile = p;
+  zebra_lsp_t *lsp;
+
+  lsp = XCALLOC (MTYPE_LSP, sizeof(zebra_lsp_t));
+  lsp->ile = *ile;
+
+  if (IS_ZEBRA_DEBUG_MPLS)
+    zlog_debug ("Alloc LSP in-label %u", lsp->ile.in_label);
+
+  return ((void *)lsp);
+}
+
+/*
+ * Create printable string for NHLFE entry.
+ */
+static char *
+nhlfe2str (zebra_nhlfe_t *nhlfe, char *buf, int size)
+{
+  struct nexthop *nexthop;
+
+  buf[0] = '\0';
+  nexthop = nhlfe->nexthop;
+  switch (nexthop->type)
+    {
+      case NEXTHOP_TYPE_IPV4:
+        inet_ntop (AF_INET, &nexthop->gate.ipv4, buf, size);
+        break;
+      case NEXTHOP_TYPE_IPV6:
+        inet_ntop (AF_INET6, &nexthop->gate.ipv6, buf, size);
+        break;
+      default:
+        break;
+    }
+
+  return buf;
+}
+
+/*
+ * Check if NHLFE matches with search info passed.
+ */
+static int
+nhlfe_nhop_match (zebra_nhlfe_t *nhlfe, enum nexthop_types_t gtype,
+                  union g_addr *gate, char *ifname, ifindex_t ifindex)
+{
+  struct nexthop *nhop;
+  u_char cmp = -1;
+
+  nhop = nhlfe->nexthop;
+  if (!nhop)
+    return -1;
+
+  if (nhop->type != gtype)
+    return -1;
+
+  switch (nhop->type)
+    {
+    case NEXTHOP_TYPE_IPV4:
+      cmp = memcmp(&(nhop->gate.ipv4), &(gate->ipv4),
+                  sizeof(struct in_addr));
+      break;
+    case NEXTHOP_TYPE_IPV6:
+    case NEXTHOP_TYPE_IPV6_IFINDEX:
+      cmp = memcmp(&(nhop->gate.ipv6), &(gate->ipv6),
+                  sizeof(struct in6_addr));
+      if (!cmp && nhop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
+        cmp = !(nhop->ifindex == ifindex);
+      break;
+    default:
+      break;
+    }
+
+  return cmp;
+}
+
+
+/*
+ * Locate NHLFE that matches with passed info.
+ */
+static zebra_nhlfe_t *
+nhlfe_find (zebra_lsp_t *lsp, enum lsp_types_t lsp_type,
+            enum nexthop_types_t gtype, union g_addr *gate,
+            char *ifname, ifindex_t ifindex)
+{
+  zebra_nhlfe_t *nhlfe;
+
+  if (!lsp)
+    return NULL;
+
+  for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next)
+    {
+      if (nhlfe->type != lsp_type)
+        continue;
+      if (!nhlfe_nhop_match (nhlfe, gtype, gate, ifname, ifindex))
+        break;
+    }
+
+  return nhlfe;
+}
+
+/*
+ * Add NHLFE. Base entry must have been created and duplicate
+ * check done.
+ */
+static zebra_nhlfe_t *
+nhlfe_add (zebra_lsp_t *lsp, enum lsp_types_t lsp_type,
+           enum nexthop_types_t gtype, union g_addr *gate,
+           char *ifname, ifindex_t ifindex, mpls_label_t out_label)
+{
+  zebra_nhlfe_t *nhlfe;
+  struct nexthop *nexthop;
+
+  if (!lsp)
+    return NULL;
+
+  nhlfe = XCALLOC(MTYPE_NHLFE, sizeof(zebra_nhlfe_t));
+  if (!nhlfe)
+    return NULL;
+
+  nhlfe->lsp = lsp;
+  nhlfe->type = lsp_type;
+  nhlfe->distance = lsp_distance (lsp_type);
+
+  nexthop = nexthop_new();
+  if (!nexthop)
+    {
+      XFREE (MTYPE_NHLFE, nhlfe);
+      return NULL;
+    }
+  nexthop_add_labels (nexthop, 1, &out_label);
+
+  nexthop->type = gtype;
+  switch (nexthop->type)
+    {
+    case NEXTHOP_TYPE_IPV4:
+      nexthop->gate.ipv4 = gate->ipv4;
+      break;
+    case NEXTHOP_TYPE_IPV6:
+    case NEXTHOP_TYPE_IPV6_IFINDEX:
+      nexthop->gate.ipv6 = gate->ipv6;
+      if (ifindex)
+        nexthop->ifindex = ifindex;
+      break;
+    default:
+      nexthop_free(nexthop);
+      XFREE (MTYPE_NHLFE, nhlfe);
+      return NULL;
+      break;
+    }
+
+  nhlfe->nexthop = nexthop;
+  if (lsp->nhlfe_list)
+    lsp->nhlfe_list->prev = nhlfe;
+  nhlfe->next = lsp->nhlfe_list;
+  lsp->nhlfe_list = nhlfe;
+
+  return nhlfe;
+}
+
+/*
+ * Delete NHLFE. Entry must be present on list.
+ */
+static int
+nhlfe_del (zebra_nhlfe_t *nhlfe)
+{
+  zebra_lsp_t *lsp;
+
+  if (!nhlfe)
+    return -1;
+
+  lsp = nhlfe->lsp;
+  if (!lsp)
+    return -1;
+
+  /* Free nexthop. */
+  if (nhlfe->nexthop)
+    nexthop_free(nhlfe->nexthop);
+
+  /* Unlink from LSP */
+  if (nhlfe->next)
+    nhlfe->next->prev = nhlfe->prev;
+  if (nhlfe->prev)
+    nhlfe->prev->next = nhlfe->next;
+  else
+    lsp->nhlfe_list = nhlfe->next;
+
+  XFREE (MTYPE_NHLFE, nhlfe);
+
+  return 0;
+}
+
+
+/*
+ * Install/update a static NHLFE for an LSP in the forwarding table. This may
+ * be a new LSP entry or a new NHLFE for an existing in-label or an update of
+ * the out-label for an existing NHLFE (update case).
+ */
+static int
+static_lsp_install (struct zebra_vrf *zvrf, mpls_label_t in_label,
+                    mpls_label_t out_label, enum nexthop_types_t gtype,
+                    union g_addr *gate, char *ifname, ifindex_t ifindex)
+{
+  struct hash *lsp_table;
+  zebra_ile_t tmp_ile;
+  zebra_lsp_t *lsp;
+  zebra_nhlfe_t *nhlfe;
+  char buf[BUFSIZ];
+
+  /* Lookup table. */
+  lsp_table = zvrf->lsp_table;
+  if (!lsp_table)
+    return -1;
+
+  /* If entry is present, exit. */
+  tmp_ile.in_label = in_label;
+  lsp = hash_get (lsp_table, &tmp_ile, lsp_alloc);
+  if (!lsp)
+    return -1;
+  nhlfe = nhlfe_find (lsp, ZEBRA_LSP_STATIC, gtype, gate, ifname, ifindex);
+  if (nhlfe)
+    {
+      struct nexthop *nh = nhlfe->nexthop;
+
+      assert (nh);
+      assert (nh->nh_label);
+
+      /* Clear deleted flag (in case it was set) */
+      UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_DELETED);
+      if (nh->nh_label->label[0] == out_label)
+        /* No change */
+        return 0;
+
+      if (IS_ZEBRA_DEBUG_MPLS)
+        {
+          nhlfe2str (nhlfe, buf, BUFSIZ);
+          zlog_debug ("LSP in-label %u type %d nexthop %s "
+                      "out-label changed to %u (old %u)",
+                      in_label, ZEBRA_LSP_STATIC, buf,
+                      out_label, nh->nh_label->label[0]);
+        }
+
+      /* Update out label, trigger processing. */
+      nh->nh_label->label[0] = out_label;
+    }
+  else
+    {
+      /* Add LSP entry to this nexthop */
+      nhlfe = nhlfe_add (lsp, ZEBRA_LSP_STATIC, gtype, gate,
+                         ifname, ifindex, out_label);
+      if (!nhlfe)
+        return -1;
+
+      if (IS_ZEBRA_DEBUG_MPLS)
+        {
+          nhlfe2str (nhlfe, buf, BUFSIZ);
+          zlog_debug ("Add LSP in-label %u type %d nexthop %s "
+                      "out-label %u",
+                      in_label, ZEBRA_LSP_STATIC, buf,
+                      out_label);
+        }
+
+      lsp->addr_family = NHLFE_FAMILY (nhlfe);
+    }
+
+  /* Mark NHLFE, queue LSP for processing. */
+  SET_FLAG(nhlfe->flags, NHLFE_FLAG_CHANGED);
+  if (lsp_processq_add (lsp))
+    return -1;
+
+  return 0;
+}
+
+/*
+ * Uninstall a particular static NHLFE in the forwarding table. If this is
+ * the only NHLFE, the entire LSP forwarding entry has to be deleted.
+ */
+static int
+static_lsp_uninstall (struct zebra_vrf *zvrf, mpls_label_t in_label,
+                      enum nexthop_types_t gtype, union g_addr *gate,
+                      char *ifname, ifindex_t ifindex)
+{
+  struct hash *lsp_table;
+  zebra_ile_t tmp_ile;
+  zebra_lsp_t *lsp;
+  zebra_nhlfe_t *nhlfe;
+  char buf[BUFSIZ];
+
+  /* Lookup table. */
+  lsp_table = zvrf->lsp_table;
+  if (!lsp_table)
+    return -1;
+
+  /* If entry is not present, exit. */
+  tmp_ile.in_label = in_label;
+  lsp = hash_lookup (lsp_table, &tmp_ile);
+  if (!lsp)
+    return 0;
+  nhlfe = nhlfe_find (lsp, ZEBRA_LSP_STATIC, gtype, gate, ifname, ifindex);
+  if (!nhlfe)
+    return 0;
+
+  if (IS_ZEBRA_DEBUG_MPLS)
+    {
+      nhlfe2str (nhlfe, buf, BUFSIZ);
+      zlog_debug ("Del LSP in-label %u type %d nexthop %s flags 0x%x",
+                  in_label, ZEBRA_LSP_STATIC, buf, nhlfe->flags);
+    }
+
+  /* Mark NHLFE for delete or directly delete, as appropriate. */
+  if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED))
+    {
+      UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_CHANGED);
+      SET_FLAG (nhlfe->flags, NHLFE_FLAG_DELETED);
+      if (lsp_processq_add (lsp))
+        return -1;
+    }
+  else
+    {
+      nhlfe_del (nhlfe);
+
+      /* Free LSP entry if no other NHLFEs and not scheduled. */
+      if (!lsp->nhlfe_list &&
+          !CHECK_FLAG (lsp->flags, LSP_FLAG_SCHEDULED))
+        {
+          if (IS_ZEBRA_DEBUG_MPLS)
+            zlog_debug ("Free LSP in-label %u flags 0x%x",
+                        lsp->ile.in_label, lsp->flags);
+
+          lsp = hash_release(lsp_table, &lsp->ile);
+          if (lsp)
+            XFREE(MTYPE_LSP, lsp);
+        }
+    }
+  return 0;
+}
+
+/*
+ * Uninstall all static NHLFEs for a particular LSP forwarding entry.
+ * If no other NHLFEs exist, the entry would be deleted.
+ */
+static int
+static_lsp_uninstall_all (struct zebra_vrf *zvrf, mpls_label_t in_label)
+{
+  struct hash *lsp_table;
+  zebra_ile_t tmp_ile;
+  zebra_lsp_t *lsp;
+  zebra_nhlfe_t *nhlfe, *nhlfe_next;
+  int schedule_lsp = 0;
+  char buf[BUFSIZ];
+
+  /* Lookup table. */
+  lsp_table = zvrf->lsp_table;
+  if (!lsp_table)
+    return -1;
+
+  /* If entry is not present, exit. */
+  tmp_ile.in_label = in_label;
+  lsp = hash_lookup (lsp_table, &tmp_ile);
+  if (!lsp || !lsp->nhlfe_list)
+    return 0;
+
+  /* Mark NHLFEs for delete or directly delete, as appropriate. */
+  for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe_next)
+    {
+      nhlfe_next = nhlfe->next;
+
+      /* Skip non-static NHLFEs */
+      if (nhlfe->type != ZEBRA_LSP_STATIC)
+        continue;
+
+      if (IS_ZEBRA_DEBUG_MPLS)
+        {
+          nhlfe2str (nhlfe, buf, BUFSIZ);
+          zlog_debug ("Del LSP in-label %u type %d nexthop %s flags 0x%x",
+                      in_label, ZEBRA_LSP_STATIC, buf, nhlfe->flags);
+        }
+
+      if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED))
+        {
+          UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_CHANGED);
+          SET_FLAG (nhlfe->flags, NHLFE_FLAG_DELETED);
+          schedule_lsp = 1;
+        }
+      else
+        {
+          nhlfe_del (nhlfe);
+        }
+    }
+
+  /* Queue LSP for processing, if needed, else delete. */
+  if (schedule_lsp)
+    {
+      if (lsp_processq_add (lsp))
+        return -1;
+    }
+  else if (!lsp->nhlfe_list &&
+           !CHECK_FLAG (lsp->flags, LSP_FLAG_SCHEDULED))
+    {
+      if (IS_ZEBRA_DEBUG_MPLS)
+        zlog_debug ("Free LSP in-label %u flags 0x%x",
+                    lsp->ile.in_label, lsp->flags);
+
+      lsp = hash_release(lsp_table, &lsp->ile);
+      if (lsp)
+        XFREE(MTYPE_LSP, lsp);
+    }
+
+  return 0;
+}
+
 /*
  * Write out static LSP configuration.
  */
@@ -318,6 +1172,27 @@ snhlfe2str (zebra_snhlfe_t *snhlfe, char *buf, int size)
   return buf;
 }
 
+/*
+ * Initialize work queue for processing changed LSPs.
+ */
+static void
+mpls_processq_init (struct zebra_t *zebra)
+{
+  zebra->lsp_process_q = work_queue_new (zebra->master, "LSP processing");
+  if (!zebra->lsp_process_q)
+    {
+      zlog_err ("%s: could not initialise work queue!", __func__);
+      return;
+    }
+
+  zebra->lsp_process_q->spec.workfunc = &lsp_process;
+  zebra->lsp_process_q->spec.del_item_data = &lsp_processq_del;
+  zebra->lsp_process_q->spec.errorfunc = NULL;
+  zebra->lsp_process_q->spec.completion_func = &lsp_processq_complete;
+  zebra->lsp_process_q->spec.max_retries = 0;
+  zebra->lsp_process_q->spec.hold = 10;
+}
+
 
 
 /* Public functions */
@@ -438,6 +1313,11 @@ zebra_mpls_static_lsp_add (struct zebra_vrf *zvrf, mpls_label_t in_label,
         }
     }
 
+  /* (Re)Install LSP in the main table. */
+  if (static_lsp_install (zvrf, in_label, out_label, gtype,
+                          gate, ifname, ifindex))
+    return -1;
+
   return 0;
 }
 
@@ -475,6 +1355,9 @@ zebra_mpls_static_lsp_del (struct zebra_vrf *zvrf, mpls_label_t in_label,
       if (IS_ZEBRA_DEBUG_MPLS)
         zlog_debug ("Del static LSP in-label %u", in_label);
 
+      /* Uninstall entire LSP from the main table. */
+      static_lsp_uninstall_all (zvrf, in_label);
+
       /* Delete all static NHLFEs */
       snhlfe_del_all (slsp);
     }
@@ -493,6 +1376,10 @@ zebra_mpls_static_lsp_del (struct zebra_vrf *zvrf, mpls_label_t in_label,
                       in_label, buf);
         }
 
+      /* Uninstall LSP from the main table. */
+      static_lsp_uninstall (zvrf, in_label, gtype,
+                            gate, ifname, ifindex);
+
       /* Delete static LSP NHLFE */
       snhlfe_del (snhlfe);
     }
@@ -508,6 +1395,19 @@ zebra_mpls_static_lsp_del (struct zebra_vrf *zvrf, mpls_label_t in_label,
   return 0;
 }
 
+/*
+ * Schedule all MPLS label forwarding entries for processing.
+ * Called upon changes that may affect one or more of them such as
+ * interface or nexthop state changes.
+ */
+void
+zebra_mpls_lsp_schedule (struct zebra_vrf *zvrf)
+{
+  if (!zvrf)
+    return;
+  hash_iterate(zvrf->lsp_table, lsp_schedule, NULL);
+}
+
 /*
  * Display MPLS LSP configuration of all static LSPs (VTY command handler).
  */
@@ -518,6 +1418,19 @@ zebra_mpls_write_lsp_config (struct vty *vty, struct zebra_vrf *zvrf)
   return (zvrf->slsp_table->count ? 1 : 0);
 }
 
+/*
+ * Called upon process exiting, need to delete LSP forwarding
+ * entries from the kernel.
+ * NOTE: Currently supported only for default VRF.
+ */
+void
+zebra_mpls_close_tables (struct zebra_vrf *zvrf)
+{
+  if (!zvrf)
+    return;
+  hash_iterate(zvrf->lsp_table, lsp_uninstall_from_kernel, NULL);
+}
+
 /*
  * Allocate MPLS tables for this VRF and do other initialization.
  * NOTE: Currently supported only for default VRF.
@@ -528,6 +1441,7 @@ zebra_mpls_init_tables (struct zebra_vrf *zvrf)
   if (!zvrf)
     return;
   zvrf->slsp_table = hash_create(label_hash, label_cmp);
+  zvrf->lsp_table = hash_create(label_hash, label_cmp);
 }
 
 /*
@@ -536,5 +1450,5 @@ zebra_mpls_init_tables (struct zebra_vrf *zvrf)
 void
 zebra_mpls_init (void)
 {
-  /* Filler for subsequent use. */
+  mpls_processq_init (&zebrad);
 }
index b62230ead6b6039c247eb72b6221177500badf11..5fb481781a991e8288f29ee21342b04225cb4491 100644 (file)
@@ -189,12 +189,28 @@ zebra_mpls_static_lsp_del (struct zebra_vrf *zvrf, mpls_label_t in_label,
                            enum nexthop_types_t gtype, union g_addr *gate,
                            char *ifname, ifindex_t ifindex);
 
+/*
+ * Schedule all MPLS label forwarding entries for processing.
+ * Called upon changes that may affect one or more of them such as
+ * interface or nexthop state changes.
+ */
+void
+zebra_mpls_lsp_schedule (struct zebra_vrf *zvrf);
+
 /*
  * Display MPLS LSP configuration of all static LSPs (VTY command handler).
  */
 int
 zebra_mpls_write_lsp_config (struct vty *vty, struct zebra_vrf *zvrf);
 
+/*
+ * Called upon process exiting, need to delete LSP forwarding
+ * entries from the kernel.
+ * NOTE: Currently supported only for default VRF.
+ */
+void
+zebra_mpls_close_tables (struct zebra_vrf *zvrf);
+
 /*
  * Allocate MPLS tables for this VRF.
  * NOTE: Currently supported only for default VRF.
@@ -208,4 +224,34 @@ zebra_mpls_init_tables (struct zebra_vrf *zvrf);
 void
 zebra_mpls_init (void);
 
+/* Inline functions. */
+
+/*
+ * Distance (priority) definition for LSP NHLFE.
+ */
+static inline u_char
+lsp_distance (enum lsp_types_t type)
+{
+  if (type == ZEBRA_LSP_STATIC)
+    return (route_distance (ZEBRA_ROUTE_STATIC));
+
+  return 150;
+}
+
+/*
+ * Map RIB type to LSP type. Used when labeled-routes from BGP
+ * are converted into LSPs.
+ */
+static inline enum lsp_types_t
+lsp_type_from_rib_type (int rib_type)
+{
+  switch (rib_type)
+    {
+      case ZEBRA_ROUTE_STATIC:
+        return ZEBRA_LSP_STATIC;
+      default:
+        return ZEBRA_LSP_INVALID;
+    }
+}
+
 #endif /*_ZEBRA_MPLS_H */
index a8c9aa33226fd7d6294fa64b4f8d660bc466dcf8..15169f0f2569d49e6af72079727b5b2a4312d166 100644 (file)
@@ -28,12 +28,22 @@ zebra_mpls_static_lsp_del (struct zebra_vrf *zvrf, mpls_label_t in_label,
   return 0;
 }
 
+void
+zebra_mpls_lsp_schedule (struct zebra_vrf *zvrf)
+{
+}
+
 int
 zebra_mpls_write_lsp_config (struct vty *vty, struct zebra_vrf *zvrf)
 {
   return 0;
 }
 
+void
+zebra_mpls_close_tables (struct zebra_vrf *zvrf)
+{
+}
+
 void
 zebra_mpls_init_tables (struct zebra_vrf *zvrf)
 {
index e238f8e8eb07a1c4b4331b61e2d658853ecad589..910610fc60c5f8701a09fac8f4fd64711b2fd956 100644 (file)
@@ -37,6 +37,7 @@
 #include "routemap.h"
 #include "nexthop.h"
 #include "vrf.h"
+#include "mpls.h"
 
 #include "zebra/rib.h"
 #include "zebra/rt.h"
@@ -117,6 +118,19 @@ _rnode_zlog(const char *_func, vrf_id_t vrf_id, struct route_node *rn, int prior
 #define rnode_info(node, ...) \
        _rnode_zlog(__func__, vrf_id, node, LOG_INFO, __VA_ARGS__)
 
+u_char
+route_distance (int type)
+{
+  u_char distance;
+
+  if ((unsigned)type >= array_size(route_info))
+    distance = 150;
+  else
+    distance = route_info[type].distance;
+
+  return distance;
+}
+
 int
 is_zebra_valid_kernel_table(u_int32_t table_id)
 {
@@ -3123,6 +3137,9 @@ rib_close (void)
       if (zvrf->other_table[AFI_IP6][table_id])
         rib_close_table (zvrf->other_table[AFI_IP6][table_id]);
     }
+
+  zebra_mpls_close_tables(zvrf);
+
 }
 
 /* Routing information base initialize. */
index 3667f5b2b6442706b142570ae7c1509c5297a23b..ce243dd6ac8a2d94670aa5179fd272c629923914 100644 (file)
@@ -130,6 +130,9 @@ struct zebra_t
   /* rib work queue */
   struct work_queue *ribq;
   struct meta_queue *mq;
+
+  /* LSP work queue */
+  struct work_queue *lsp_process_q;
 };
 extern struct zebra_t zebrad;