diff --git a/bgpd/bgp_label.c b/bgpd/bgp_label.c index dcaaea6868c7..a219c407dabd 100644 --- a/bgpd/bgp_label.c +++ b/bgpd/bgp_label.c @@ -120,20 +120,141 @@ mpls_label_t bgp_adv_label(struct bgp_node *rn, struct bgp_path_info *pi, return rn->local_label; } +/** + * This is passed as the callback function to bgp_labelpool.c:bgp_lp_get() + * by bgp_reg_dereg_for_label() when a label needs to be obtained from + * label pool. + * Note that it will reject the allocated label if a label index is found, + * because the label index supposes predictable labels + */ +int bgp_reg_for_label_callback(mpls_label_t new_label, void *labelid, + bool allocated) +{ + struct bgp_path_info *pi = (struct bgp_path_info *)labelid; + struct bgp_node *rn = (struct bgp_node *)pi->net; + char addr[PREFIX_STRLEN]; + + prefix2str(&rn->p, addr, PREFIX_STRLEN); + + if (BGP_DEBUG(labelpool, LABELPOOL)) + zlog_debug("%s: FEC %s label=%u, allocated=%d", __func__, addr, + new_label, allocated); + + if (!allocated) { + /* + * previously-allocated label is now invalid + */ + if (pi->attr->label_index == MPLS_INVALID_LABEL_INDEX + && pi->attr->label != MPLS_LABEL_NONE + && CHECK_FLAG(rn->flags, BGP_NODE_REGISTERED_FOR_LABEL)) { + bgp_unregister_for_label(rn); + label_ntop(MPLS_LABEL_IMPLICIT_NULL, 1, + &rn->local_label); + bgp_set_valid_label(&rn->local_label); + } + return 0; + } + + /* + * label index is assigned, this should be handled by SR-related code, + * so retry FEC registration and then reject label allocation for + * it to be released to label pool + */ + if (pi->attr->label_index != MPLS_INVALID_LABEL_INDEX) { + flog_err( + EC_BGP_LABEL, + "%s: FEC %s Rejecting allocated label %u as Label Index is %u", + __func__, addr, new_label, pi->attr->label_index); + + bgp_register_for_label(pi->net, pi); + + return -1; + } + + if (pi->attr->label != MPLS_INVALID_LABEL) { + if (new_label == pi->attr->label) { + /* already have same label, accept but do nothing */ + return 0; + } + /* Shouldn't happen: different label allocation */ + flog_err(EC_BGP_LABEL, + "%s: %s had label %u but got new assignment %u", + __func__, addr, pi->attr->label, new_label); + /* continue means use new one */ + } + + label_ntop(new_label, 1, &rn->local_label); + bgp_set_valid_label(&rn->local_label); + + /* + * Get back to registering the FEC + */ + bgp_register_for_label(pi->net, pi); + + return 0; +} + void bgp_reg_dereg_for_label(struct bgp_node *rn, struct bgp_path_info *pi, - int reg) + bool reg) { + bool with_label_index = false; struct stream *s; struct prefix *p; + mpls_label_t *local_label; int command; uint16_t flags = 0; size_t flags_pos = 0; + char addr[PREFIX_STRLEN]; + + p = &(rn->p); + local_label = &(rn->local_label); + /* this prevents the loop when we're called by + * bgp_reg_for_label_callback() + */ + bool have_label_to_reg = bgp_is_valid_label(local_label) + && label_pton(local_label) != MPLS_LABEL_IMPLICIT_NULL; + + if (reg) { + assert(pi); + /* + * Determine if we will let zebra should derive label from + * label index instead of bgpd requesting from label pool + */ + if (CHECK_FLAG(pi->attr->flag, + ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID)) + && pi->attr->label_index != BGP_INVALID_LABEL_INDEX) { + with_label_index = true; + } else { + /* + * If no label index was provided -- assume any label + * from label pool will do. This means that label index + * always takes precedence over auto-assigned labels. + */ + if (!have_label_to_reg) { + if (BGP_DEBUG(labelpool, LABELPOOL)) { + prefix2str(p, addr, PREFIX_STRLEN); + zlog_debug("%s: Requesting label from LP for %s", + __func__, addr); + } + /* bgp_reg_for_label_callback() will call back + * __func__ when it gets a label from the pool. + * This means we'll never register FECs without + * valid labels. + */ + bgp_lp_get(LP_TYPE_BGP_LU, pi, + bgp_reg_for_label_callback); + return; + } + } + } /* Check socket. */ if (!zclient || zclient->sock < 0) return; - p = &(rn->p); + /* If the route node has a local_label assigned or the + * path node has an MPLS SR label index allowing zebra to + * derive the label, proceed with registration. */ s = zclient->obuf; stream_reset(s); command = (reg) ? ZEBRA_FEC_REGISTER : ZEBRA_FEC_UNREGISTER; @@ -143,12 +264,12 @@ void bgp_reg_dereg_for_label(struct bgp_node *rn, struct bgp_path_info *pi, stream_putw(s, PREFIX_FAMILY(p)); stream_put_prefix(s, p); if (reg) { - assert(pi); - if (pi->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID)) { - if (pi->attr->label_index != BGP_INVALID_LABEL_INDEX) { - flags |= ZEBRA_FEC_REGISTER_LABEL_INDEX; - stream_putl(s, pi->attr->label_index); - } + if (have_label_to_reg) { + flags |= ZEBRA_FEC_REGISTER_LABEL; + stream_putl(s, label_pton(local_label)); + } else if (with_label_index) { + flags |= ZEBRA_FEC_REGISTER_LABEL_INDEX; + stream_putl(s, pi->attr->label_index); } SET_FLAG(rn->flags, BGP_NODE_REGISTERED_FOR_LABEL); } else diff --git a/bgpd/bgp_label.h b/bgpd/bgp_label.h index b0fc07e54770..89bc9aabb03b 100644 --- a/bgpd/bgp_label.h +++ b/bgpd/bgp_label.h @@ -30,8 +30,10 @@ struct bgp_node; struct bgp_path_info; struct peer; +extern int bgp_reg_for_label_callback(mpls_label_t new_label, void *labelid, + bool allocated); extern void bgp_reg_dereg_for_label(struct bgp_node *rn, - struct bgp_path_info *pi, int reg); + struct bgp_path_info *pi, bool reg); extern int bgp_parse_fec_update(void); extern mpls_label_t bgp_adv_label(struct bgp_node *rn, struct bgp_path_info *pi, struct peer *to, afi_t afi, safi_t safi); @@ -87,12 +89,12 @@ static inline void bgp_unset_valid_label(mpls_label_t *label) static inline void bgp_register_for_label(struct bgp_node *rn, struct bgp_path_info *pi) { - bgp_reg_dereg_for_label(rn, pi, 1); + bgp_reg_dereg_for_label(rn, pi, true); } static inline void bgp_unregister_for_label(struct bgp_node *rn) { - bgp_reg_dereg_for_label(rn, NULL, 0); + bgp_reg_dereg_for_label(rn, NULL, false); } /* Label stream to value */ diff --git a/bgpd/bgp_labelpool.h b/bgpd/bgp_labelpool.h index fa35cde0e126..0507e65489e4 100644 --- a/bgpd/bgp_labelpool.h +++ b/bgpd/bgp_labelpool.h @@ -29,6 +29,7 @@ * Types used in bgp_lp_get for debug tracking; add more as needed */ #define LP_TYPE_VRF 0x00000001 +#define LP_TYPE_BGP_LU 0x00000002 struct labelpool { struct skiplist *ledger; /* all requests */ diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 23002fb74ca8..2c361bef4d5b 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -2265,20 +2265,26 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, /* 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 except if the label - * index changes + * necessary to do this upon changes to best path. Exceptions: + * - label index has changed -> recalculate resulting label + * - path_info sub_type changed -> switch to/from implicit-null + * - no valid label (due to removed static label binding) -> get new one */ if (bgp->allocate_mpls_labels[afi][safi]) { if (new_select) { if (!old_select || bgp_label_index_differs(new_select, old_select) - || new_select->sub_type != old_select->sub_type) { + || new_select->sub_type != old_select->sub_type + || !bgp_is_valid_label(&rn->local_label)) { + /* Enforced penultimate hop popping: + * implicit-null for local routes, aggregate + * and redistributed routes + */ if (new_select->sub_type == BGP_ROUTE_STATIC - && new_select->attr->flag - & ATTR_FLAG_BIT( - BGP_ATTR_PREFIX_SID) - && new_select->attr->label_index - != BGP_INVALID_LABEL_INDEX) { + || new_select->sub_type + == BGP_ROUTE_AGGREGATE + || new_select->sub_type + == BGP_ROUTE_REDISTRIBUTE) { if (CHECK_FLAG( rn->flags, BGP_NODE_REGISTERED_FOR_LABEL)) diff --git a/lib/zclient.h b/lib/zclient.h index adb48b252a69..8fe711f31044 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -58,6 +58,10 @@ #define ZEBRA_IPTABLES_FORWARD 0 #define ZEBRA_IPTABLES_DROP 1 +/* Zebra FEC register command flags. */ +#define ZEBRA_FEC_REGISTER_LABEL 0x1 +#define ZEBRA_FEC_REGISTER_LABEL_INDEX 0x2 + extern struct sockaddr_storage zclient_addr; extern socklen_t zclient_addr_len; diff --git a/lib/zebra.h b/lib/zebra.h index 0f3f45f7ba18..09115951e9b6 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -452,9 +452,6 @@ extern const char *zserv_command_string(unsigned int command); */ #define ZEBRA_FLAG_ONLINK 0x80 -/* 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/zapi_msg.c b/zebra/zapi_msg.c index 9b84a6e58a5c..26a3cd5b42a0 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -1187,6 +1187,7 @@ static void zread_fec_register(ZAPI_HANDLER_ARGS) unsigned short l = 0; struct prefix p; uint16_t flags; + uint32_t label = MPLS_INVALID_LABEL; uint32_t label_index = MPLS_INVALID_LABEL_INDEX; s = msg; @@ -1229,12 +1230,15 @@ static void zread_fec_register(ZAPI_HANDLER_ARGS) l += 5; STREAM_GET(&p.u.prefix, s, PSIZE(p.prefixlen)); l += PSIZE(p.prefixlen); - if (flags & ZEBRA_FEC_REGISTER_LABEL_INDEX) { + if (flags & ZEBRA_FEC_REGISTER_LABEL) { + STREAM_GETL(s, label); + l += 4; + } else if (flags & ZEBRA_FEC_REGISTER_LABEL_INDEX) { STREAM_GETL(s, label_index); l += 4; - } else - label_index = MPLS_INVALID_LABEL_INDEX; - zebra_mpls_fec_register(zvrf, &p, label_index, client); + } + + zebra_mpls_fec_register(zvrf, &p, label, label_index, client); } stream_failure: diff --git a/zebra/zebra_errors.c b/zebra/zebra_errors.c index 32f665383245..d7c17829cdc6 100644 --- a/zebra/zebra_errors.c +++ b/zebra/zebra_errors.c @@ -85,6 +85,12 @@ static struct log_ref ferr_zebra_err[] = { .description = "A client requested a label binding for a new FEC, but Zebra was unable to add the FEC to its internal table.", .suggestion = "Notify a developer.", }, + { + .code = EC_ZEBRA_FEC_LABEL_INDEX_LABEL_CONFLICT, + .title = "Refused to add FEC for MPLS client with both label index and label specified", + .description = "A client requested a label binding for a new FEC specifying a label index and a label at the same time.", + .suggestion = "Notify a developer.", + }, { .code = EC_ZEBRA_FEC_RM_FAILED, .title = "Failed to remove FEC for MPLS client", diff --git a/zebra/zebra_errors.h b/zebra/zebra_errors.h index cf2d6a7cf51e..c3cdc4ed4217 100644 --- a/zebra/zebra_errors.h +++ b/zebra/zebra_errors.h @@ -37,6 +37,7 @@ enum zebra_log_refs { EC_ZEBRA_DP_INVALID_RC, EC_ZEBRA_WQ_NONEXISTENT, EC_ZEBRA_FEC_ADD_FAILED, + EC_ZEBRA_FEC_LABEL_INDEX_LABEL_CONFLICT, EC_ZEBRA_FEC_RM_FAILED, EC_ZEBRA_IRDP_LEN_MISMATCH, EC_ZEBRA_RNH_UNKNOWN_FAMILY, diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 5fe01161586c..c0764cd4b8c6 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -1813,17 +1813,22 @@ int zebra_mpls_lsp_uninstall(struct zebra_vrf *zvrf, struct route_node *rn, * NOTE: If there is a manually configured label binding, that is used. * Otherwise, if a 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. + * is acceptable. If no label index then just register the specified label. + * NOTE2: Either label or label_index is expected to be set to MPLS_INVALID_* + * by the calling function. Register requests with both will be rejected. */ int zebra_mpls_fec_register(struct zebra_vrf *zvrf, struct prefix *p, - uint32_t label_index, struct zserv *client) + uint32_t label, uint32_t label_index, + struct zserv *client) { struct route_table *table; zebra_fec_t *fec; char buf[BUFSIZ]; - int new_client; - int label_change = 0; + bool new_client; + bool label_change = false; uint32_t old_label; + bool have_label_index = (label_index != MPLS_INVALID_LABEL_INDEX); + bool is_configured_fec = false; /* indicate statically configured FEC */ table = zvrf->fec_table[family2afi(PREFIX_FAMILY(p))]; if (!table) @@ -1832,12 +1837,20 @@ int zebra_mpls_fec_register(struct zebra_vrf *zvrf, struct prefix *p, if (IS_ZEBRA_DEBUG_MPLS) prefix2str(p, buf, BUFSIZ); + if (label != MPLS_INVALID_LABEL && have_label_index) { + flog_err( + EC_ZEBRA_FEC_LABEL_INDEX_LABEL_CONFLICT, + "Rejecting FEC register for %s with both label %u and Label Index %u specified, client %s", + buf, label, label_index, + zebra_route_string(client->proto)); + return -1; + } + /* Locate FEC */ fec = fec_find(table, p); if (!fec) { - fec = fec_add(table, p, MPLS_INVALID_LABEL, 0, label_index); + fec = fec_add(table, p, label, 0, label_index); if (!fec) { - prefix2str(p, buf, BUFSIZ); flog_err( EC_ZEBRA_FEC_ADD_FAILED, "Failed to add FEC %s upon register, client %s", @@ -1846,16 +1859,19 @@ int zebra_mpls_fec_register(struct zebra_vrf *zvrf, struct prefix *p, } old_label = MPLS_INVALID_LABEL; - new_client = 1; + new_client = true; } else { + /* Check if the FEC has been statically defined in the config */ + is_configured_fec = fec->flags & FEC_FLAG_CONFIGURED; /* 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) + if (!new_client && fec->label_index == label_index + && fec->label == label) /* Duplicate register */ return 0; - /* Save current label, update label index */ + /* Save current label, update the FEC */ old_label = fec->label; fec->label_index = label_index; } @@ -1864,21 +1880,29 @@ int zebra_mpls_fec_register(struct zebra_vrf *zvrf, struct prefix *p, listnode_add(fec->client_list, client); if (IS_ZEBRA_DEBUG_MPLS) - 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. + zlog_debug("FEC %s label%s %u %s by client %s%s", buf, + have_label_index ? " index" : "", + have_label_index ? label_index : label, + new_client ? "registered" : "updated", + zebra_route_string(client->proto), + is_configured_fec + ? ", but using statically configured label" + : ""); + + /* If not a statically configured FEC, derive the local label + * from label index or use the provided label */ - if (!(fec->flags & FEC_FLAG_CONFIGURED)) { - fec_derive_label_from_index(zvrf, fec); + if (!is_configured_fec) { + if (have_label_index) + fec_derive_label_from_index(zvrf, fec); + else + fec->label = label; /* If no label change, exit. */ if (fec->label == old_label) return 0; - label_change = 1; + label_change = true; } /* If new client or label change, update client and install or uninstall @@ -2106,8 +2130,8 @@ int 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 label index %u", buf, - fec->label_index); + zlog_debug("Delete fec %s label %u label index %u", buf, + fec->label, fec->label_index); } old_label = fec->label; diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index 86bee129cfe5..c250fc4058cc 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -191,16 +191,9 @@ int zebra_mpls_lsp_install(struct zebra_vrf *zvrf, struct route_node *rn, int zebra_mpls_lsp_uninstall(struct zebra_vrf *zvrf, struct route_node *rn, struct route_entry *re); -/* - * 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, - uint32_t label_index, struct zserv *client); + uint32_t label, uint32_t label_index, + struct zserv *client); /* * Deregistration from a client for the label binding for a FEC. The FEC