From: Vivek Venkatraman Date: Thu, 9 Mar 2017 17:55:54 +0000 (-0500) Subject: bgpd, lib, zebra: Implement handling of BGP-Prefix-SID label Index X-Git-Tag: reindent-master-before~213^2~22 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=28d58fd7b166836029656c29ad8ed9a9e8a744ac;p=mirror%2Ffrr.git bgpd, lib, zebra: Implement handling of BGP-Prefix-SID label Index Implement BGP Prefix-SID IETF draft to be able to signal a labeled-unicast prefix with a label index (segment ID). This makes it easier to deploy global MPLS labels with BGP, even without other aspects of Segment Routing implemented. This patch implements the handling of the BGP-Prefix-SID Label Index attribute. When received from a peer and the index is acceptable, the local label is picked up from the SRGB and is programmed as the incoming label as well as advertised to peers. If the index is not acceptable, no local label is assigned. The outgoing label will always be the one advertised by the downstream neighbor. Signed-off-by: Vivek Venkatraman --- 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 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; - if (fec->label != MPLS_INVALID_LABEL) + 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;