diff --git a/bgpd/bgp_label.c b/bgpd/bgp_label.c index e2c4a54044..0c331c5d59 100644 --- a/bgpd/bgp_label.c +++ b/bgpd/bgp_label.c @@ -125,11 +125,14 @@ bgp_adv_label (struct bgp_node *rn, struct bgp_info *ri, struct peer *to, } void -bgp_reg_dereg_for_label (struct bgp_node *rn, int reg) +bgp_reg_dereg_for_label (struct bgp_node *rn, struct bgp_info *ri, + int reg) { struct stream *s; struct prefix *p; int command; + u_int16_t flags = 0; + size_t flags_pos = 0; /* Check socket. */ if (!zclient || zclient->sock < 0) @@ -140,17 +143,29 @@ bgp_reg_dereg_for_label (struct bgp_node *rn, int reg) stream_reset (s); command = (reg) ? ZEBRA_FEC_REGISTER : ZEBRA_FEC_UNREGISTER; zclient_create_header (s, command, VRF_DEFAULT); + flags_pos = stream_get_endp (s); /* save position of 'flags' */ + stream_putw(s, flags); /* initial flags */ stream_putw(s, PREFIX_FAMILY(p)); stream_put_prefix(s, p); - stream_putw_at (s, 0, stream_get_endp (s)); - if (reg) - SET_FLAG (rn->flags, BGP_NODE_REGISTERED_FOR_LABEL); + { + assert (ri); + if (ri->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LABEL_INDEX)) + { + assert (ri->attr->extra); + flags |= ZEBRA_FEC_REGISTER_LABEL_INDEX; + stream_putl (s, ri->attr->extra->label_index); + } + SET_FLAG (rn->flags, BGP_NODE_REGISTERED_FOR_LABEL); + } else UNSET_FLAG (rn->flags, BGP_NODE_REGISTERED_FOR_LABEL); - zclient_send_message(zclient); - return; + /* Set length and flags */ + stream_putw_at (s, 0, stream_get_endp (s)); + stream_putw_at (s, flags_pos, flags); + + zclient_send_message(zclient); } static int diff --git a/bgpd/bgp_label.h b/bgpd/bgp_label.h index a7e7d5c47b..49a7b945ab 100644 --- a/bgpd/bgp_label.h +++ b/bgpd/bgp_label.h @@ -30,7 +30,8 @@ struct bgp_node; struct bgp_info; struct peer; -extern void bgp_reg_dereg_for_label (struct bgp_node *rn, int reg); +extern void bgp_reg_dereg_for_label (struct bgp_node *rn, struct bgp_info *ri, + int reg); extern int bgp_parse_fec_update(void); extern u_char * bgp_adv_label(struct bgp_node *rn, struct bgp_info *ri, struct peer *to, afi_t afi, safi_t safi); @@ -84,15 +85,15 @@ bgp_unset_valid_label (u_char *t) } static inline void -bgp_register_for_label (struct bgp_node *rn) +bgp_register_for_label (struct bgp_node *rn, struct bgp_info *ri) { - bgp_reg_dereg_for_label (rn, 1); + bgp_reg_dereg_for_label (rn, ri, 1); } static inline void bgp_unregister_for_label (struct bgp_node *rn) { - bgp_reg_dereg_for_label (rn, 0); + bgp_reg_dereg_for_label (rn, NULL, 0); } /* Label stream to value */ diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 451b54edab..b016122a7f 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -298,6 +298,20 @@ bgp_pcount_adjust (struct bgp_node *rn, struct bgp_info *ri) } } +static int +bgp_label_index_differs (struct bgp_info *ri1, struct bgp_info *ri2) +{ + u_int32_t ri1_label_index = BGP_INVALID_LABEL_INDEX; + u_int32_t ri2_label_index = BGP_INVALID_LABEL_INDEX; + + if (ri1->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LABEL_INDEX)) + ri1_label_index = ri1->attr->extra->label_index; + + if (ri2->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LABEL_INDEX)) + ri2_label_index = ri2->attr->extra->label_index; + + return (!(ri1_label_index == ri2_label_index)); +} /* Set/unset bgp_info flags, adjusting any other state as needed. * This is here primarily to keep prefix-count in check. @@ -1908,7 +1922,6 @@ bgp_process_main (struct work_queue *wq, void *data) struct bgp_info *new_select; struct bgp_info *old_select; struct bgp_info_pair old_and_new; - int label_valid; /* Is it end of initial update? (after startup) */ if (!rn) @@ -1935,28 +1948,31 @@ bgp_process_main (struct work_queue *wq, void *data) /* Do we need to allocate or free labels? * Right now, since we only deal with per-prefix labels, it is not necessary - * to do this upon changes to best path. + * to do this upon changes to best path except of the label index changes. */ bgp_table_lock (bgp_node_table (rn)); if (bgp_labeled_safi (safi)) { - label_valid = bgp_is_valid_label (rn->local_label); - if (!old_select && new_select && !label_valid) + if (new_select) { - if (new_select->sub_type == BGP_ROUTE_STATIC && - new_select->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LABEL_INDEX)) + if (!old_select || + bgp_label_index_differs (new_select, old_select) || + new_select->sub_type != old_select->sub_type) { - label_ntop (MPLS_IMP_NULL_LABEL, 1, rn->local_label); - bgp_set_valid_label(rn->local_label); + if (new_select->sub_type == BGP_ROUTE_STATIC && + new_select->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LABEL_INDEX)) + { + if (CHECK_FLAG (rn->flags, BGP_NODE_REGISTERED_FOR_LABEL)) + bgp_unregister_for_label (rn); + label_ntop (MPLS_IMP_NULL_LABEL, 1, rn->local_label); + bgp_set_valid_label(rn->local_label); + } + else + bgp_register_for_label (rn, new_select); } - else - bgp_register_for_label (rn); - } - else if (old_select && !new_select) - { - if (CHECK_FLAG (rn->flags, BGP_NODE_REGISTERED_FOR_LABEL)) - bgp_unregister_for_label (rn); } + else if (CHECK_FLAG (rn->flags, BGP_NODE_REGISTERED_FOR_LABEL)) + bgp_unregister_for_label (rn); } /* If best route remains the same and this is not due to user-initiated diff --git a/lib/mpls.h b/lib/mpls.h index 5a91883753..f4f360c957 100644 --- a/lib/mpls.h +++ b/lib/mpls.h @@ -123,6 +123,11 @@ mpls_lse_decode (mpls_lse_t lse, mpls_label_t *label, *ttl = MPLS_LABEL_TTL(local_lse); } +/* Invalid label index value (when used with BGP Prefix-SID). Should + * match the BGP definition. + */ +#define MPLS_INVALID_LABEL_INDEX 0xFFFFFFFF + /* Printable string for labels (with consideration for reserved values). */ static inline char * diff --git a/lib/zebra.h b/lib/zebra.h index ef261eedc1..cd72dc67f8 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -393,6 +393,9 @@ extern const char *zserv_command_string (unsigned int command); #define ZEBRA_FLAG_SCOPE_LINK 0x100 #define ZEBRA_FLAG_FIB_OVERRIDE 0x200 +/* Zebra FEC flags. */ +#define ZEBRA_FEC_REGISTER_LABEL_INDEX 0x1 + #ifndef INADDR_LOOPBACK #define INADDR_LOOPBACK 0x7f000001 /* Internet address 127.0.0.1. */ #endif diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 85ce147d25..42738f8fb9 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -60,6 +60,10 @@ extern struct zebra_t zebrad; /* static function declarations */ +static void +fec_evaluate (struct zebra_vrf *zvrf, int add); +static u_int32_t +fec_derive_label_from_index (struct zebra_vrf *vrf, zebra_fec_t *fec); static int lsp_install (struct zebra_vrf *zvrf, mpls_label_t label, struct route_node *rn, struct rib *rib); @@ -77,7 +81,7 @@ static zebra_fec_t * fec_find (struct route_table *table, struct prefix *p); static zebra_fec_t * fec_add (struct route_table *table, struct prefix *p, mpls_label_t label, - u_int32_t flags); + u_int32_t flags, u_int32_t label_index); static int fec_del (zebra_fec_t *fec); @@ -352,6 +356,84 @@ lsp_uninstall (struct zebra_vrf *zvrf, mpls_label_t label) return 0; } +/* + * This function is invoked upon change to label block configuration; it + * will walk all registered FECs with label-index and appropriately update + * their local labels and trigger client updates. + */ +static void +fec_evaluate (struct zebra_vrf *zvrf, int add) +{ + struct route_node *rn; + zebra_fec_t *fec; + u_int32_t old_label, new_label; + int af; + char buf[BUFSIZ]; + + for (af = AFI_IP; af < AFI_MAX; af++) + { + for (rn = route_top(zvrf->fec_table[af]); rn; rn = route_next(rn)) + { + if ((fec = rn->info) == NULL) + continue; + + /* Skip configured FECs and those without a label index. */ + if (fec->flags & FEC_FLAG_CONFIGURED || + fec->label_index == MPLS_INVALID_LABEL_INDEX) + continue; + + if (IS_ZEBRA_DEBUG_MPLS) + prefix2str(&rn->p, buf, BUFSIZ); + + /* Save old label, determine new label. */ + old_label = fec->label; + if (add) + { + new_label = zvrf->mpls_srgb.start_label + fec->label_index; + if (new_label >= zvrf->mpls_srgb.end_label) + new_label = MPLS_INVALID_LABEL; + } + else + new_label = MPLS_INVALID_LABEL; + + /* If label has changed, update FEC and clients. */ + if (new_label == old_label) + continue; + + if (IS_ZEBRA_DEBUG_MPLS) + zlog_debug ("Update fec %s new label %u upon label block %s", + buf, new_label, add ? "ADD" : "DEL"); + + fec->label = new_label; + fec_update_clients (fec); + + /* Update label forwarding entries appropriately */ + fec_change_update_lsp (zvrf, fec, old_label); + } + } +} + +/* + * Derive (if possible) and update the local label for the FEC based on + * its label index. The index is "acceptable" if it falls within the + * globally configured label block (SRGB). + */ +static u_int32_t +fec_derive_label_from_index (struct zebra_vrf *zvrf, zebra_fec_t *fec) +{ + u_int32_t label; + + if (fec->label_index != MPLS_INVALID_LABEL_INDEX && + zvrf->mpls_srgb.start_label && + ((label = zvrf->mpls_srgb.start_label + fec->label_index) < + zvrf->mpls_srgb.end_label)) + fec->label = label; + else + fec->label = MPLS_INVALID_LABEL; + + return fec->label; +} + /* * There is a change for this FEC. Install or uninstall label forwarding * entries, as appropriate. @@ -457,6 +539,8 @@ fec_print (zebra_fec_t *fec, struct vty *vty) prefix2str(&rn->p, buf, BUFSIZ); vty_out(vty, "%s%s", buf, VTY_NEWLINE); vty_out(vty, " Label: %s", label2str(fec->label, buf, BUFSIZ)); + if (fec->label_index != MPLS_INVALID_LABEL_INDEX) + vty_out(vty, ", Label Index: %u", fec->label_index); vty_out(vty, "%s", VTY_NEWLINE); if (!list_isempty(fec->client_list)) { @@ -491,7 +575,7 @@ fec_find (struct route_table *table, struct prefix *p) */ static zebra_fec_t * fec_add (struct route_table *table, struct prefix *p, - mpls_label_t label, u_int32_t flags) + mpls_label_t label, u_int32_t flags, u_int32_t label_index) { struct route_node *rn; zebra_fec_t *fec; @@ -519,6 +603,7 @@ fec_add (struct route_table *table, struct prefix *p, else route_unlock_node (rn); /* for the route_node_get */ + fec->label_index = label_index; fec->flags = flags; return fec; @@ -1743,14 +1828,21 @@ zebra_mpls_lsp_uninstall (struct zebra_vrf *zvrf, struct route_node *rn, struct /* * Registration from a client for the label binding for a FEC. If a binding * already exists, it is informed to the client. + * NOTE: If there is a manually configured label binding, that is used. + * Otherwise, if aa label index is specified, it means we have to allocate the + * label from a locally configured label block (SRGB), if one exists and index + * is acceptable. */ int zebra_mpls_fec_register (struct zebra_vrf *zvrf, struct prefix *p, - struct zserv *client) + u_int32_t label_index, struct zserv *client) { struct route_table *table; zebra_fec_t *fec; char buf[BUFSIZ]; + int new_client; + int label_change = 0; + u_int32_t old_label; table = zvrf->fec_table[family2afi(PREFIX_FAMILY(p))]; if (!table) @@ -1763,7 +1855,7 @@ zebra_mpls_fec_register (struct zebra_vrf *zvrf, struct prefix *p, fec = fec_find (table, p); if (!fec) { - fec = fec_add (table, p, MPLS_INVALID_LABEL, 0); + fec = fec_add (table, p, MPLS_INVALID_LABEL, 0, label_index); if (!fec) { prefix2str(p, buf, BUFSIZ); @@ -1771,27 +1863,60 @@ zebra_mpls_fec_register (struct zebra_vrf *zvrf, struct prefix *p, buf, zebra_route_string(client->proto)); return -1; } + + old_label = MPLS_INVALID_LABEL; + new_client = 1; } else { - if (listnode_lookup(fec->client_list, client)) + /* Client may register same FEC with different label index. */ + new_client = (listnode_lookup(fec->client_list, client) == NULL); + if (!new_client && fec->label_index == label_index) /* Duplicate register */ return 0; + + /* Save current label, update label index */ + old_label = fec->label; + fec->label_index = label_index; } - listnode_add (fec->client_list, client); + if (new_client) + listnode_add (fec->client_list, client); if (IS_ZEBRA_DEBUG_MPLS) - zlog_debug("FEC %s registered by client %s", - buf, zebra_route_string(client->proto)); + zlog_debug("FEC %s Label Index %u %s by client %s", + buf, label_index, new_client ? "registered" : "updated", + zebra_route_string(client->proto)); - if (fec->label != MPLS_INVALID_LABEL) + /* If not a configured FEC, derive the local label (from label index) + * or reset it. + */ + if (!(fec->flags & FEC_FLAG_CONFIGURED)) + { + fec_derive_label_from_index (zvrf, fec); + + /* If no label change, exit. */ + if (fec->label == old_label) + return 0; + + label_change = 1; + } + + /* If new client or label change, update client and install or uninstall + * label forwarding entry as needed. + */ + /* Inform client of label, if needed. */ + if ((new_client && fec->label != MPLS_INVALID_LABEL) || + label_change) { if (IS_ZEBRA_DEBUG_MPLS) zlog_debug ("Update client label %u", fec->label); fec_send (fec, client); } + if (new_client || label_change) + return fec_change_update_lsp (zvrf, fec, old_label); + return 0; } @@ -1830,8 +1955,17 @@ zebra_mpls_fec_unregister (struct zebra_vrf *zvrf, struct prefix *p, zlog_debug("FEC %s unregistered by client %s", buf, zebra_route_string(client->proto)); - if (list_isempty(fec->client_list) && (fec->label == MPLS_INVALID_LABEL)) - fec_del (fec); + /* If not a configured entry, delete the FEC if no other clients. Before + * deleting, see if any LSP needs to be uninstalled. + */ + if (!(fec->flags & FEC_FLAG_CONFIGURED) && + list_isempty(fec->client_list)) + { + mpls_label_t old_label = fec->label; + fec->label = MPLS_INVALID_LABEL; /* reset */ + fec_change_update_lsp (zvrf, fec, old_label); + fec_del (fec); + } return 0; } @@ -1943,7 +2077,8 @@ zebra_mpls_static_fec_add (struct zebra_vrf *zvrf, struct prefix *p, fec = fec_find (table, p); if (!fec) { - fec = fec_add (table, p, in_label, FEC_FLAG_CONFIGURED); + fec = fec_add (table, p, in_label, FEC_FLAG_CONFIGURED, + MPLS_INVALID_LABEL_INDEX); if (!fec) { prefix2str(p, buf, BUFSIZ); @@ -1979,6 +2114,8 @@ zebra_mpls_static_fec_add (struct zebra_vrf *zvrf, struct prefix *p, /* * Remove static FEC to label binding. If there are no clients registered * for this FEC, delete the FEC; else notify clients + * Note: Upon delete of static binding, if label index exists for this FEC, + * client may need to be updated with derived label. */ int zebra_mpls_static_fec_del (struct zebra_vrf *zvrf, struct prefix *p) @@ -2003,7 +2140,8 @@ zebra_mpls_static_fec_del (struct zebra_vrf *zvrf, struct prefix *p) if (IS_ZEBRA_DEBUG_MPLS) { prefix2str(p, buf, BUFSIZ); - zlog_debug ("Delete fec %s", buf); + zlog_debug ("Delete fec %s label index %u", + buf, fec->label_index); } old_label = fec->label; @@ -2017,6 +2155,12 @@ zebra_mpls_static_fec_del (struct zebra_vrf *zvrf, struct prefix *p) return 0; } + /* Derive the local label (from label index) or reset it. */ + fec_derive_label_from_index (zvrf, fec); + + /* If there is a label change, update clients. */ + if (fec->label == old_label) + return 0; fec_update_clients (fec); /* Update label forwarding entries appropriately */ @@ -2744,6 +2888,9 @@ zebra_mpls_label_block_add (struct zebra_vrf *zvrf, u_int32_t start_label, { zvrf->mpls_srgb.start_label = start_label; zvrf->mpls_srgb.end_label = end_label; + + /* Evaluate registered FECs to see if any get a label or not. */ + fec_evaluate (zvrf, 1); return 0; } @@ -2755,6 +2902,9 @@ zebra_mpls_label_block_del (struct zebra_vrf *zvrf) { zvrf->mpls_srgb.start_label = 0; zvrf->mpls_srgb.end_label = 0; + + /* Process registered FECs to clear their local label, if needed. */ + fec_evaluate (zvrf, 0); return 0; } diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index f8e95fa100..e271edd2eb 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -159,6 +159,9 @@ struct zebra_fec_t_ /* In-label - either statically bound or derived from label block. */ mpls_label_t label; + /* Label index (into global label block), if valid */ + u_int32_t label_index; + /* Flags. */ u_int32_t flags; #define FEC_FLAG_CONFIGURED (1 << 0) @@ -217,10 +220,14 @@ zebra_mpls_lsp_uninstall (struct zebra_vrf *zvrf, struct route_node *rn, struct /* * Registration from a client for the label binding for a FEC. If a binding * already exists, it is informed to the client. + * NOTE: If there is a manually configured label binding, that is used. + * Otherwise, if aa label index is specified, it means we have to allocate the + * label from a locally configured label block (SRGB), if one exists and index + * is acceptable. */ int zebra_mpls_fec_register (struct zebra_vrf *zvrf, struct prefix *p, - struct zserv *client); + u_int32_t label_index, struct zserv *client); /* * Deregistration from a client for the label binding for a FEC. The FEC @@ -265,6 +272,8 @@ zebra_mpls_static_fec_add (struct zebra_vrf *zvrf, struct prefix *p, /* * Remove static FEC to label binding. If there are no clients registered * for this FEC, delete the FEC; else notify clients. + * Note: Upon delete of static binding, if label index exists for this FEC, + * client may need to be updated with derived label. */ int zebra_mpls_static_fec_del (struct zebra_vrf *zvrf, struct prefix *p); diff --git a/zebra/zebra_mpls_null.c b/zebra/zebra_mpls_null.c index 12176c024e..6b0a91de02 100644 --- a/zebra/zebra_mpls_null.c +++ b/zebra/zebra_mpls_null.c @@ -175,7 +175,7 @@ zebra_mpls_print_fec (struct vty *vty, struct zebra_vrf *zvrf, struct prefix *p) int zebra_mpls_fec_register (struct zebra_vrf *zvrf, struct prefix *p, - struct zserv *client) + u_int32_t label_index, struct zserv *client) { return 0; } diff --git a/zebra/zserv.c b/zebra/zserv.c index c11f1bf3fd..7c8e6de765 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -942,6 +942,8 @@ zserv_fec_register (struct zserv *client, int sock, u_short length) struct zebra_vrf *zvrf; u_short l = 0; struct prefix p; + u_int16_t flags; + u_int32_t label_index = MPLS_INVALID_LABEL_INDEX; s = client->ibuf; zvrf = vrf_info_lookup(VRF_DEFAULT); @@ -950,12 +952,18 @@ zserv_fec_register (struct zserv *client, int sock, u_short length) while (l < length) { + flags = stream_getw(s); p.family = stream_getw(s); p.prefixlen = stream_getc(s); l += 5; stream_get(&p.u.prefix, s, PSIZE(p.prefixlen)); l += PSIZE(p.prefixlen); - zebra_mpls_fec_register (zvrf, &p, client); + if (flags & ZEBRA_FEC_REGISTER_LABEL_INDEX) + { + label_index = stream_getl(s); + l += 4; + } + zebra_mpls_fec_register (zvrf, &p, label_index, client); } return 0; @@ -969,6 +977,7 @@ zserv_fec_unregister (struct zserv *client, int sock, u_short length) struct zebra_vrf *zvrf; u_short l = 0; struct prefix p; + u_int16_t flags; s = client->ibuf; zvrf = vrf_info_lookup(VRF_DEFAULT); @@ -977,6 +986,7 @@ zserv_fec_unregister (struct zserv *client, int sock, u_short length) while (l < length) { + flags = stream_getw(s); p.family = stream_getw(s); p.prefixlen = stream_getc(s); l += 5;