summaryrefslogtreecommitdiff
path: root/zebra/zebra_mpls.c
diff options
context:
space:
mode:
Diffstat (limited to 'zebra/zebra_mpls.c')
-rw-r--r--zebra/zebra_mpls.c916
1 files changed, 915 insertions, 1 deletions
diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c
index db1f318595..892aec965a 100644
--- a/zebra/zebra_mpls.c
+++ b/zebra/zebra_mpls.c
@@ -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);
@@ -109,6 +159,810 @@ label_cmp (const void *p1, const void *p2)
}
/*
+ * 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.
*/
static void
@@ -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);
}
@@ -509,6 +1396,19 @@ zebra_mpls_static_lsp_del (struct zebra_vrf *zvrf, mpls_label_t in_label,
}
/*
+ * 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).
*/
int
@@ -519,6 +1419,19 @@ 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)
+{
+ 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);
}