#include "routemap.h"
#include "stream.h"
#include "nexthop.h"
+#include "termtable.h"
#include "lib/json.h"
#include "zebra/rib.h"
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,
- ifindex_t ifindex, mpls_label_t out_label);
+ ifindex_t ifindex, uint8_t num_labels,
+ mpls_label_t *labels);
static int nhlfe_del(zebra_nhlfe_t *snhlfe);
static void nhlfe_out_label_update(zebra_nhlfe_t *nhlfe,
struct mpls_label_stack *nh_label);
static int snhlfe_del(zebra_snhlfe_t *snhlfe);
static int snhlfe_del_all(zebra_slsp_t *slsp);
static char *snhlfe2str(zebra_snhlfe_t *snhlfe, char *buf, int size);
+static void mpls_lsp_uninstall_all_type(struct hash_bucket *bucket, void *ctxt);
+static void mpls_ftn_uninstall_all(struct zebra_vrf *zvrf,
+ int afi, enum lsp_types_t lsp_type);
/* Static functions */
* the label advertised by the recursive nexthop (plus we don't have the
* logic yet to push multiple labels).
*/
- for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) {
+ for (nexthop = re->nhe->nhg->nexthop;
+ nexthop; nexthop = nexthop->next) {
/* Skip inactive and recursive entries. */
if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
continue;
/* Add LSP entry to this nexthop */
nhlfe = nhlfe_add(lsp, lsp_type, nexthop->type,
&nexthop->gate, nexthop->ifindex,
- nexthop->nh_label->label[0]);
+ nexthop->nh_label->num_labels,
+ nexthop->nh_label->label);
if (!nhlfe)
return -1;
|| !CHECK_FLAG(match->flags, ZEBRA_FLAG_SELECTED))
continue;
- for (match_nh = match->ng.nexthop; match_nh;
+ for (match_nh = match->nhe->nhg->nexthop; match_nh;
match_nh = match_nh->next) {
if (match->type == ZEBRA_ROUTE_CONNECT
|| nexthop->ifindex == match_nh->ifindex) {
break;
}
- if (!match || !match->ng.nexthop)
+ if (!match || !match->nhe->nhg->nexthop)
return 0;
- nexthop->ifindex = match->ng.nexthop->ifindex;
+ nexthop->ifindex = match->nhe->nhg->nexthop->ifindex;
return 1;
}
inet_ntop(AF_INET, &nexthop->gate.ipv4, buf, size);
break;
case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf, size);
break;
case NEXTHOP_TYPE_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,
- ifindex_t ifindex, mpls_label_t out_label)
+ ifindex_t ifindex, uint8_t num_labels,
+ mpls_label_t labels[])
{
zebra_nhlfe_t *nhlfe;
struct nexthop *nexthop;
XFREE(MTYPE_NHLFE, nhlfe);
return NULL;
}
- nexthop_add_labels(nexthop, lsp_type, 1, &out_label);
+ nexthop_add_labels(nexthop, lsp_type, num_labels, labels);
nexthop->vrf_id = VRF_DEFAULT;
nexthop->type = gtype;
enum nexthop_types_t gtype,
union g_addr *gate,
ifindex_t ifindex,
- mpls_label_t out_label)
+ uint8_t num_labels,
+ mpls_label_t out_labels[])
{
/* Just a public pass-through to the internal implementation */
- return nhlfe_add(lsp, lsp_type, gtype, gate, ifindex, out_label);
+ return nhlfe_add(lsp, lsp_type, gtype, gate, ifindex, num_labels,
+ out_labels);
}
/*
return 0;
}
+struct lsp_uninstall_args {
+ struct hash *lsp_table;
+ enum lsp_types_t type;
+};
+
+/*
+ * Cleanup MPLS labels registered by this client.
+ */
+static int zebra_mpls_cleanup_zclient_labels(struct zserv *client)
+{
+ struct vrf *vrf;
+ struct zebra_vrf *zvrf;
+
+ RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) {
+ struct lsp_uninstall_args args;
+
+ zvrf = vrf->info;
+ if (!zvrf)
+ continue;
+
+ /* Cleanup LSPs. */
+ args.lsp_table = zvrf->lsp_table;
+ args.type = lsp_type_from_re_type(client->proto);
+ hash_iterate(zvrf->lsp_table, mpls_lsp_uninstall_all_type,
+ &args);
+
+ /* Cleanup FTNs. */
+ mpls_ftn_uninstall_all(zvrf, AFI_IP,
+ lsp_type_from_re_type(client->proto));
+ mpls_ftn_uninstall_all(zvrf, AFI_IP6,
+ lsp_type_from_re_type(client->proto));
+ }
+
+ return 0;
+}
+
/*
* Return FEC (if any) to which this label is bound.
* Note: Only works for per-prefix binding and when the label is not
fec_print(rn->info, vty);
}
+static void mpls_zebra_nhg_update(struct route_entry *re, afi_t afi,
+ struct nexthop_group *new_grp)
+{
+ struct nhg_hash_entry *nhe;
+
+ nhe = zebra_nhg_rib_find(0, new_grp, afi);
+
+ route_entry_update_nhe(re, nhe);
+}
+
static bool mpls_ftn_update_nexthop(int add, struct nexthop *nexthop,
enum lsp_types_t type, mpls_label_t label)
{
*/
int mpls_ftn_update(int add, struct zebra_vrf *zvrf, enum lsp_types_t type,
struct prefix *prefix, enum nexthop_types_t gtype,
- union g_addr *gate, ifindex_t ifindex, uint8_t distance,
- mpls_label_t out_label)
+ union g_addr *gate, ifindex_t ifindex, uint8_t route_type,
+ unsigned short route_instance, mpls_label_t out_label)
{
struct route_table *table;
struct route_node *rn;
struct route_entry *re;
struct nexthop *nexthop;
+ struct nexthop_group new_grp = {};
bool found;
+ afi_t afi = family2afi(prefix->family);
/* Lookup table. */
- table = zebra_vrf_table(family2afi(prefix->family), SAFI_UNICAST,
- zvrf_id(zvrf));
+ table = zebra_vrf_table(afi, SAFI_UNICAST, zvrf_id(zvrf));
if (!table)
return -1;
RNODE_FOREACH_RE (rn, re) {
if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED))
continue;
- if (re->distance == distance)
+ if (re->type == route_type && re->instance == route_instance)
break;
}
if (re == NULL)
return -1;
+ /*
+ * Copy over current nexthops into a temporary group.
+ * We can't just change the values here since we are hashing
+ * on labels. We need to create a whole new group
+ */
+ nexthop_group_copy(&new_grp, re->nhe->nhg);
+
found = false;
- for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) {
+ for (nexthop = new_grp.nexthop; nexthop; nexthop = nexthop->next) {
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
continue;
if (!mpls_ftn_update_nexthop(add, nexthop, type,
out_label))
- return 0;
+ break;
found = true;
break;
case NEXTHOP_TYPE_IPV6:
continue;
if (!mpls_ftn_update_nexthop(add, nexthop, type,
out_label))
- return 0;
+ break;
found = true;
break;
default:
}
}
- if (!found)
+ if (found) {
+ SET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
+ SET_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED);
+
+ mpls_zebra_nhg_update(re, afi, &new_grp);
+
+ rib_queue_add(rn);
+ }
+
+ nexthops_free(new_grp.nexthop);
+
+ return found ? 0 : -1;
+}
+
+int mpls_ftn_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type,
+ struct prefix *prefix, uint8_t route_type,
+ unsigned short route_instance)
+{
+ struct route_table *table;
+ struct route_node *rn;
+ struct route_entry *re;
+ struct nexthop *nexthop;
+ struct nexthop_group new_grp = {};
+ afi_t afi = family2afi(prefix->family);
+
+ /* Lookup table. */
+ table = zebra_vrf_table(afi, SAFI_UNICAST, zvrf_id(zvrf));
+ if (!table)
return -1;
+ /* Lookup existing route */
+ rn = route_node_get(table, prefix);
+ RNODE_FOREACH_RE (rn, re) {
+ if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED))
+ continue;
+ if (re->type == route_type && re->instance == route_instance)
+ break;
+ }
+ if (re == NULL)
+ return -1;
+
+ nexthop_group_copy(&new_grp, re->nhe->nhg);
+
+ for (nexthop = new_grp.nexthop; nexthop; nexthop = nexthop->next)
+ nexthop_del_labels(nexthop);
+
SET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
SET_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED);
+
+ mpls_zebra_nhg_update(re, afi, &new_grp);
+
+ nexthops_free(new_grp.nexthop);
+
rib_queue_add(rn);
return 0;
* the out-label for an existing NHLFE (update case).
*/
int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type,
- mpls_label_t in_label, mpls_label_t out_label,
- enum nexthop_types_t gtype, union g_addr *gate,
- ifindex_t ifindex)
+ mpls_label_t in_label, uint8_t num_out_labels,
+ mpls_label_t out_labels[], enum nexthop_types_t gtype,
+ union g_addr *gate, ifindex_t ifindex)
{
struct hash *lsp_table;
zebra_ile_t tmp_ile;
/* Clear deleted flag (in case it was set) */
UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED);
- if (nh->nh_label->label[0] == out_label)
+ if (nh->nh_label->num_labels == num_out_labels
+ && !memcmp(nh->nh_label->label, out_labels,
+ sizeof(mpls_label_t) * num_out_labels))
/* No change */
return 0;
if (IS_ZEBRA_DEBUG_MPLS) {
+ char buf2[BUFSIZ];
+ char buf3[BUFSIZ];
+
nhlfe2str(nhlfe, buf, BUFSIZ);
+ mpls_label2str(num_out_labels, out_labels, buf2,
+ sizeof(buf2), 0);
+ mpls_label2str(nh->nh_label->num_labels,
+ nh->nh_label->label, buf3, sizeof(buf3),
+ 0);
+
zlog_debug(
"LSP in-label %u type %d nexthop %s "
- "out-label changed to %u (old %u)",
- in_label, type, buf, out_label,
- nh->nh_label->label[0]);
+ "out-label(s) changed to %s (old %s)",
+ in_label, type, buf, buf2, buf3);
}
- /* Update out label, trigger processing. */
- nh->nh_label->label[0] = out_label;
+ /* Update out label(s), trigger processing. */
+ if (nh->nh_label->num_labels == num_out_labels)
+ memcpy(nh->nh_label->label, out_labels,
+ sizeof(mpls_label_t) * num_out_labels);
+ else {
+ nexthop_del_labels(nh);
+ nexthop_add_labels(nh, type, num_out_labels,
+ out_labels);
+ }
} else {
/* Add LSP entry to this nexthop */
- nhlfe = nhlfe_add(lsp, type, gtype, gate, ifindex, out_label);
+ nhlfe = nhlfe_add(lsp, type, gtype, gate, ifindex,
+ num_out_labels, out_labels);
if (!nhlfe)
return -1;
if (IS_ZEBRA_DEBUG_MPLS) {
+ char buf2[BUFSIZ];
+
nhlfe2str(nhlfe, buf, BUFSIZ);
+ mpls_label2str(num_out_labels, out_labels, buf2,
+ sizeof(buf2), 0);
+
zlog_debug(
"Add LSP in-label %u type %d nexthop %s "
- "out-label %u",
- in_label, type, buf, out_label);
+ "out-label(s) %s",
+ in_label, type, buf, buf2);
}
lsp->addr_family = NHLFE_FAMILY(nhlfe);
return 0;
}
+int mpls_lsp_uninstall_all_vrf(struct zebra_vrf *zvrf, enum lsp_types_t type,
+ mpls_label_t in_label)
+{
+ struct hash *lsp_table;
+ zebra_ile_t tmp_ile;
+ zebra_lsp_t *lsp;
+
+ /* 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;
+
+ return mpls_lsp_uninstall_all(lsp_table, lsp, type);
+}
+
/*
- * Uninstall all LDP NHLFEs for a particular LSP forwarding entry.
+ * Uninstall all NHLFEs for a particular LSP forwarding entry.
* If no other NHLFEs exist, the entry would be deleted.
*/
-void mpls_ldp_lsp_uninstall_all(struct hash_bucket *bucket, void *ctxt)
+static void mpls_lsp_uninstall_all_type(struct hash_bucket *bucket, void *ctxt)
{
+ struct lsp_uninstall_args *args = ctxt;
zebra_lsp_t *lsp;
struct hash *lsp_table;
if (!lsp->nhlfe_list)
return;
- lsp_table = ctxt;
+ lsp_table = args->lsp_table;
if (!lsp_table)
return;
- mpls_lsp_uninstall_all(lsp_table, lsp, ZEBRA_LSP_LDP);
+ mpls_lsp_uninstall_all(lsp_table, lsp, args->type);
}
/*
- * Uninstall all LDP FEC-To-NHLFE (FTN) bindings of the given address-family.
+ * Uninstall all FEC-To-NHLFE (FTN) bindings of the given address-family and
+ * LSP type.
*/
-void mpls_ldp_ftn_uninstall_all(struct zebra_vrf *zvrf, int afi)
+static void mpls_ftn_uninstall_all(struct zebra_vrf *zvrf,
+ int afi, enum lsp_types_t lsp_type)
{
struct route_table *table;
struct route_node *rn;
for (rn = route_top(table); rn; rn = route_next(rn)) {
update = 0;
RNODE_FOREACH_RE (rn, re) {
- for (nexthop = re->ng.nexthop; nexthop;
+ struct nexthop_group new_grp = {};
+
+ nexthop_group_copy(&new_grp, re->nhe->nhg);
+
+ for (nexthop = new_grp.nexthop; nexthop;
nexthop = nexthop->next) {
- if (nexthop->nh_label_type != ZEBRA_LSP_LDP)
+ if (nexthop->nh_label_type != lsp_type)
continue;
nexthop_del_labels(nexthop);
ROUTE_ENTRY_LABELS_CHANGED);
update = 1;
}
+
+ if (CHECK_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED))
+ mpls_zebra_nhg_update(re, afi, &new_grp);
+
+ nexthops_free(new_grp.nexthop);
}
if (update)
}
/* (Re)Install LSP in the main table. */
- if (mpls_lsp_install(zvrf, ZEBRA_LSP_STATIC, in_label, out_label, gtype,
- gate, ifindex))
+ if (mpls_lsp_install(zvrf, ZEBRA_LSP_STATIC, in_label, 1, &out_label,
+ gtype, gate, ifindex))
return -1;
return 0;
json_object *json = NULL;
zebra_lsp_t *lsp = NULL;
zebra_nhlfe_t *nhlfe = NULL;
- struct nexthop *nexthop = NULL;
struct listnode *node = NULL;
struct list *lsp_list = hash_get_sorted_list(zvrf->lsp_table, lsp_cmp);
json, JSON_C_TO_STRING_PRETTY));
json_object_free(json);
} else {
- vty_out(vty, " Inbound Outbound\n");
- vty_out(vty, " Label Type Nexthop Label\n");
- vty_out(vty, "-------- ------- --------------- --------\n");
+ struct ttable *tt;
+
+ /* Prepare table. */
+ tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
+ ttable_add_row(tt, "Inbound Label|Type|Nexthop|Outbound Label");
+ tt->style.cell.rpad = 2;
+ tt->style.corner = '+';
+ ttable_restyle(tt);
+ ttable_rowseps(tt, 0, BOTTOM, true, '-');
for (ALL_LIST_ELEMENTS_RO(lsp_list, node, lsp)) {
for (nhlfe = lsp->nhlfe_list; nhlfe;
nhlfe = nhlfe->next) {
- vty_out(vty, "%8d %7s ", lsp->ile.in_label,
- nhlfe_type2str(nhlfe->type));
+ struct nexthop *nexthop;
+ const char *out_label_str;
+ char nh_buf[NEXTHOP_STRLEN];
+
nexthop = nhlfe->nexthop;
switch (nexthop->type) {
zns = zebra_ns_lookup(NS_DEFAULT);
ifp = if_lookup_by_index_per_ns(
- zns,
- nexthop->ifindex);
- if (ifp)
- vty_out(vty, "%15s", ifp->name);
- else
- vty_out(vty, "%15s", "Null");
-
+ zns, nexthop->ifindex);
+ snprintf(nh_buf, sizeof(nh_buf), "%s",
+ ifp ? ifp->name : "Null");
break;
}
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
- vty_out(vty, "%15s",
- inet_ntoa(nexthop->gate.ipv4));
+ inet_ntop(AF_INET, &nexthop->gate.ipv4,
+ nh_buf, sizeof(nh_buf));
break;
case NEXTHOP_TYPE_IPV6:
case NEXTHOP_TYPE_IPV6_IFINDEX:
- vty_out(vty, "%15s",
- inet_ntop(AF_INET6,
- &nexthop->gate.ipv6,
- buf, BUFSIZ));
+ inet_ntop(AF_INET6, &nexthop->gate.ipv6,
+ nh_buf, sizeof(nh_buf));
break;
default:
break;
}
if (nexthop->type != NEXTHOP_TYPE_IFINDEX)
- vty_out(vty, " %8s\n",
- mpls_label2str(
- nexthop->nh_label
- ->num_labels,
- &nexthop->nh_label
- ->label[0],
- buf, BUFSIZ, 1));
+ out_label_str = mpls_label2str(
+ nexthop->nh_label->num_labels,
+ &nexthop->nh_label->label[0],
+ buf, BUFSIZ, 1);
else
- vty_out(vty, "\n");
+ out_label_str = "-";
+
+ ttable_add_row(tt, "%u|%s|%s|%s",
+ lsp->ile.in_label,
+ nhlfe_type2str(nhlfe->type),
+ nh_buf, out_label_str);
}
}
- vty_out(vty, "\n");
+ /* Dump the generated table. */
+ if (tt->nrows > 1) {
+ char *table = ttable_dump(tt, "\n");
+ vty_out(vty, "%s\n", table);
+ XFREE(MTYPE_TMP, table);
+ }
+ ttable_del(tt);
}
list_delete(&lsp_list);
mpls_enabled = 1;
hook_register(zserv_client_close, zebra_mpls_cleanup_fecs_for_client);
+ hook_register(zserv_client_close, zebra_mpls_cleanup_zclient_labels);
}