From 5749ac83a8adca45703e60ab56383e29ed732532 Mon Sep 17 00:00:00 2001 From: Louis Scalbert Date: Mon, 12 Dec 2022 16:00:58 +0100 Subject: [PATCH] isisd: add ASLA support Add the support of ASLA with the following TLV: - Extended IS Reachability (already defined TLV 22) - Application-Specific Link Attributes (Sub-TLV 16) (to enable the Flex-Algo flag on a link) - Admin-group (Sub-Sub-TLV 3) - Extended Admin-group (Sub-Sub-TLV 14) Signed-off-by: Louis Scalbert --- isisd/isis_te.c | 170 ++++++++++++++- isisd/isis_tlvs.c | 517 ++++++++++++++++++++++++++++++++++++++++++++++ isisd/isis_tlvs.h | 58 ++++++ 3 files changed, 744 insertions(+), 1 deletion(-) diff --git a/isisd/isis_te.c b/isisd/isis_te.c index 70b0633fa5..c734ff95db 100644 --- a/isisd/isis_te.c +++ b/isisd/isis_te.c @@ -164,6 +164,154 @@ void isis_mpls_te_term(struct isis_area *area) XFREE(MTYPE_ISIS_MPLS_TE, area->mta); } +static void isis_link_params_update_asla(struct isis_circuit *circuit, + struct interface *ifp) +{ + struct isis_asla_subtlvs *asla; + struct listnode *node, *nnode; + struct isis_ext_subtlvs *ext = circuit->ext; + int i; + + if (!HAS_LINK_PARAMS(ifp)) { + list_delete_all_node(ext->aslas); + return; + } + +#ifndef FABRICD + /* RFC 8919 Application Specific Link-Attributes + * is required by flex-algo application ISIS_SABM_FLAG_X + */ + if (list_isempty(circuit->area->flex_algos->flex_algos)) + isis_tlvs_free_asla(ext, ISIS_SABM_FLAG_X); + else + isis_tlvs_find_alloc_asla(ext, ISIS_SABM_FLAG_X); +#endif /* ifndef FABRICD */ + + if (list_isempty(ext->aslas)) + return; + + for (ALL_LIST_ELEMENTS(ext->aslas, node, nnode, asla)) { + asla->legacy = circuit->area->asla_legacy_flag; + RESET_SUBTLV(asla); + + if (asla->legacy) + continue; + + /* Fulfill ASLA subTLVs from interface link parameters */ + if (IS_PARAM_SET(ifp->link_params, LP_ADM_GRP)) { + asla->admin_group = ifp->link_params->admin_grp; + SET_SUBTLV(asla, EXT_ADM_GRP); + } else + UNSET_SUBTLV(asla, EXT_ADM_GRP); + + if (IS_PARAM_SET(ifp->link_params, LP_EXTEND_ADM_GRP)) { + admin_group_copy(&asla->ext_admin_group, + &ifp->link_params->ext_admin_grp); + SET_SUBTLV(asla, EXT_EXTEND_ADM_GRP); + } else + UNSET_SUBTLV(asla, EXT_EXTEND_ADM_GRP); + + /* Send admin-group zero for better compatibility + * https://www.rfc-editor.org/rfc/rfc7308#section-2.3.2 + */ + if (circuit->area->admin_group_send_zero && + !IS_SUBTLV(asla, EXT_ADM_GRP) && + !IS_SUBTLV(asla, EXT_EXTEND_ADM_GRP)) { + asla->admin_group = 0; + SET_SUBTLV(asla, EXT_ADM_GRP); + admin_group_clear(&asla->ext_admin_group); + admin_group_allow_explicit_zero(&asla->ext_admin_group); + SET_SUBTLV(asla, EXT_EXTEND_ADM_GRP); + } + + if (IS_PARAM_SET(ifp->link_params, LP_TE_METRIC)) { + asla->te_metric = ifp->link_params->te_metric; + SET_SUBTLV(asla, EXT_TE_METRIC); + } else + UNSET_SUBTLV(asla, EXT_TE_METRIC); + + if (IS_PARAM_SET(ifp->link_params, LP_DELAY)) { + asla->delay = ifp->link_params->av_delay; + SET_SUBTLV(asla, EXT_DELAY); + } else + UNSET_SUBTLV(asla, EXT_DELAY); + + if (IS_PARAM_SET(ifp->link_params, LP_MM_DELAY)) { + asla->min_delay = ifp->link_params->min_delay; + asla->max_delay = ifp->link_params->max_delay; + SET_SUBTLV(asla, EXT_MM_DELAY); + } else { + UNSET_SUBTLV(asla, EXT_MM_DELAY); + } + + if (asla->standard_apps == ISIS_SABM_FLAG_X) + /* Flex-Algo ASLA does not need the following TE + * sub-TLVs + */ + continue; + + if (IS_PARAM_SET(ifp->link_params, LP_MAX_BW)) { + asla->max_bw = ifp->link_params->max_bw; + SET_SUBTLV(asla, EXT_MAX_BW); + } else + UNSET_SUBTLV(asla, EXT_MAX_BW); + + if (IS_PARAM_SET(ifp->link_params, LP_MAX_RSV_BW)) { + asla->max_rsv_bw = ifp->link_params->max_rsv_bw; + SET_SUBTLV(asla, EXT_MAX_RSV_BW); + } else + UNSET_SUBTLV(asla, EXT_MAX_RSV_BW); + + if (IS_PARAM_SET(ifp->link_params, LP_UNRSV_BW)) { + for (i = 0; i < MAX_CLASS_TYPE; i++) + asla->unrsv_bw[i] = + ifp->link_params->unrsv_bw[i]; + SET_SUBTLV(asla, EXT_UNRSV_BW); + } else + UNSET_SUBTLV(asla, EXT_UNRSV_BW); + + if (IS_PARAM_SET(ifp->link_params, LP_DELAY_VAR)) { + asla->delay_var = ifp->link_params->delay_var; + SET_SUBTLV(asla, EXT_DELAY_VAR); + } else + UNSET_SUBTLV(asla, EXT_DELAY_VAR); + + if (IS_PARAM_SET(ifp->link_params, LP_PKT_LOSS)) { + asla->pkt_loss = ifp->link_params->pkt_loss; + SET_SUBTLV(asla, EXT_PKT_LOSS); + } else + UNSET_SUBTLV(asla, EXT_PKT_LOSS); + + if (IS_PARAM_SET(ifp->link_params, LP_RES_BW)) { + asla->res_bw = ifp->link_params->res_bw; + SET_SUBTLV(asla, EXT_RES_BW); + } else + UNSET_SUBTLV(asla, EXT_RES_BW); + + if (IS_PARAM_SET(ifp->link_params, LP_AVA_BW)) { + asla->ava_bw = ifp->link_params->ava_bw; + SET_SUBTLV(asla, EXT_AVA_BW); + } else + UNSET_SUBTLV(asla, EXT_AVA_BW); + + if (IS_PARAM_SET(ifp->link_params, LP_USE_BW)) { + asla->use_bw = ifp->link_params->use_bw; + SET_SUBTLV(asla, EXT_USE_BW); + } else + UNSET_SUBTLV(asla, EXT_USE_BW); + } + + + for (ALL_LIST_ELEMENTS(ext->aslas, node, nnode, asla)) { + if (!asla->legacy && NO_SUBTLV(asla) && + admin_group_nb_words(&asla->ext_admin_group) == 0) + /* remove ASLA without info from the list of ASLAs to + * not send void ASLA + */ + isis_tlvs_del_asla_flex_algo(ext, asla); + } +} + /* Main initialization / update function of the MPLS TE Circuit context */ /* Call when interface TE Link parameters are modified */ void isis_link_params_update(struct isis_circuit *circuit, @@ -210,6 +358,19 @@ void isis_link_params_update(struct isis_circuit *circuit, } else UNSET_SUBTLV(ext, EXT_EXTEND_ADM_GRP); + /* Send admin-group zero for better compatibility + * https://www.rfc-editor.org/rfc/rfc7308#section-2.3.2 + */ + if (circuit->area->admin_group_send_zero && + !IS_SUBTLV(ext, EXT_ADM_GRP) && + !IS_SUBTLV(ext, EXT_EXTEND_ADM_GRP)) { + ext->adm_group = 0; + SET_SUBTLV(ext, EXT_ADM_GRP); + admin_group_clear(&ext->ext_admin_group); + admin_group_allow_explicit_zero(&ext->ext_admin_group); + SET_SUBTLV(ext, EXT_EXTEND_ADM_GRP); + } + /* If known, register local IPv4 addr from ip_addr list */ if (listcount(circuit->ip_addrs) != 0) { addr = (struct prefix_ipv4 *)listgetdata( @@ -331,6 +492,8 @@ void isis_link_params_update(struct isis_circuit *circuit, ext->status = 0; } + isis_link_params_update_asla(circuit, ifp); + return; } @@ -503,7 +666,12 @@ int isis_mpls_te_update(struct interface *ifp) isis_link_params_update(circuit, ifp); /* ... and LSP */ - if (circuit->area && IS_MPLS_TE(circuit->area->mta)) + if (circuit->area && + (IS_MPLS_TE(circuit->area->mta) +#ifndef FABRICD + || !list_isempty(circuit->area->flex_algos->flex_algos) +#endif /* ifndef FABRICD */ + )) lsp_regenerate_schedule(circuit->area, circuit->is_type, 0); rc = 0; diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c index 8eaa095b33..368973dfd9 100644 --- a/isisd/isis_tlvs.c +++ b/isisd/isis_tlvs.c @@ -123,6 +123,7 @@ struct isis_ext_subtlvs *isis_alloc_ext_subtlvs(void) ext = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(struct isis_ext_subtlvs)); init_item_list(&ext->adj_sid); init_item_list(&ext->lan_sid); + ext->aslas = list_new(); admin_group_init(&ext->ext_admin_group); @@ -132,6 +133,8 @@ struct isis_ext_subtlvs *isis_alloc_ext_subtlvs(void) void isis_del_ext_subtlvs(struct isis_ext_subtlvs *ext) { struct isis_item *item, *next_item; + struct listnode *node, *nnode; + struct isis_asla_subtlvs *asla; if (!ext) return; @@ -146,6 +149,11 @@ void isis_del_ext_subtlvs(struct isis_ext_subtlvs *ext) XFREE(MTYPE_ISIS_SUBTLV, item); } + for (ALL_LIST_ELEMENTS(ext->aslas, node, nnode, asla)) + isis_tlvs_del_asla_flex_algo(ext, asla); + + list_delete(&ext->aslas); + admin_group_term(&ext->ext_admin_group); XFREE(MTYPE_ISIS_SUBTLV, ext); @@ -162,6 +170,8 @@ copy_item_ext_subtlvs(struct isis_ext_subtlvs *exts, uint16_t mtid) struct isis_ext_subtlvs *rv = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*rv)); struct isis_adj_sid *adj; struct isis_lan_adj_sid *lan; + struct listnode *node, *nnode; + struct isis_asla_subtlvs *new_asla, *asla; /* Copy the Extended IS main part */ memcpy(rv, exts, sizeof(struct isis_ext_subtlvs)); @@ -226,12 +236,133 @@ copy_item_ext_subtlvs(struct isis_ext_subtlvs *exts, uint16_t mtid) SET_SUBTLV(rv, EXT_LAN_ADJ_SID); } + rv->aslas = list_new(); + + for (ALL_LIST_ELEMENTS(exts->aslas, node, nnode, asla)) { + new_asla = XCALLOC(MTYPE_ISIS_SUBTLV, + sizeof(struct isis_asla_subtlvs)); + memcpy(new_asla, asla, sizeof(struct isis_asla_subtlvs)); + + new_asla->ext_admin_group.bitmap.data = NULL; + admin_group_copy(&new_asla->ext_admin_group, + &asla->ext_admin_group); + + listnode_add(rv->aslas, new_asla); + } + rv->ext_admin_group.bitmap.data = NULL; admin_group_copy(&rv->ext_admin_group, &exts->ext_admin_group); return rv; } +static void format_item_asla_subtlvs(struct isis_asla_subtlvs *asla, + struct sbuf *buf, int indent) +{ + char admin_group_buf[ADMIN_GROUP_PRINT_MAX_SIZE]; + + sbuf_push(buf, indent, "Application Specific Link Attributes:\n"); + sbuf_push(buf, indent + 2, + "L flag: %u, SA-Length: %u, UDA-Length: %u\n", asla->legacy, + asla->standard_apps_length, asla->user_def_apps_length); + sbuf_push(buf, indent + 2, "Standard Applications: 0x%02x", + asla->standard_apps); + if (asla->standard_apps) { + uint8_t bit = asla->standard_apps; + if (bit & ISIS_SABM_FLAG_R) + sbuf_push(buf, 0, " RSVP-TE"); + if (bit & ISIS_SABM_FLAG_S) + sbuf_push(buf, 0, " SR-Policy"); + if (bit & ISIS_SABM_FLAG_L) + sbuf_push(buf, 0, " Loop-Free-Alternate"); + if (bit & ISIS_SABM_FLAG_X) + sbuf_push(buf, 0, " Flex-Algo"); + } + sbuf_push(buf, 0, "\n"); + sbuf_push(buf, indent + 2, "User Defined Applications: 0x%02x\n", + asla->user_def_apps); + + if (IS_SUBTLV(asla, EXT_ADM_GRP)) { + sbuf_push(buf, indent + 2, "Admin Group: 0x%08x\n", + asla->admin_group); + sbuf_push(buf, indent + 4, "Bit positions: %s\n", + admin_group_standard_print( + admin_group_buf, + indent + 2 + strlen("Admin Group: "), + asla->admin_group)); + } + if (IS_SUBTLV(asla, EXT_EXTEND_ADM_GRP) && + admin_group_nb_words(&asla->ext_admin_group) != 0) { + sbuf_push(buf, indent + 2, "Ext Admin Group: %s\n", + admin_group_string( + admin_group_buf, ADMIN_GROUP_PRINT_MAX_SIZE, + indent + 2 + strlen("Ext Admin Group: "), + &asla->ext_admin_group)); + admin_group_print(admin_group_buf, + indent + 2 + strlen("Ext Admin Group: "), + &asla->ext_admin_group); + if (admin_group_buf[0] != '\0' && + (buf->pos + strlen(admin_group_buf) + + SBUF_DEFAULT_SIZE / 2) < buf->size) + sbuf_push(buf, indent + 4, "Bit positions: %s\n", + admin_group_buf); + } + if (IS_SUBTLV(asla, EXT_MAX_BW)) + sbuf_push(buf, indent + 2, + "Maximum Bandwidth: %g (Bytes/sec)\n", asla->max_bw); + if (IS_SUBTLV(asla, EXT_MAX_RSV_BW)) + sbuf_push(buf, indent + 2, + "Maximum Reservable Bandwidth: %g (Bytes/sec)\n", + asla->max_rsv_bw); + if (IS_SUBTLV(asla, EXT_UNRSV_BW)) { + sbuf_push(buf, indent + 2, "Unreserved Bandwidth:\n"); + for (int j = 0; j < MAX_CLASS_TYPE; j += 2) { + sbuf_push( + buf, indent + 2, + "[%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)\n", + j, asla->unrsv_bw[j], j + 1, + asla->unrsv_bw[j + 1]); + } + } + if (IS_SUBTLV(asla, EXT_TE_METRIC)) + sbuf_push(buf, indent + 2, "Traffic Engineering Metric: %u\n", + asla->te_metric); + /* Extended metrics */ + if (IS_SUBTLV(asla, EXT_DELAY)) + sbuf_push(buf, indent + 2, + "%s Average Link Delay: %u (micro-sec)\n", + IS_ANORMAL(asla->delay) ? "Anomalous" : "Normal", + asla->delay); + if (IS_SUBTLV(asla, EXT_MM_DELAY)) { + sbuf_push(buf, indent + 2, + "%s Min/Max Link Delay: %u / %u (micro-sec)\n", + IS_ANORMAL(asla->min_delay) ? "Anomalous" : "Normal", + asla->min_delay & TE_EXT_MASK, + asla->max_delay & TE_EXT_MASK); + } + if (IS_SUBTLV(asla, EXT_DELAY_VAR)) { + sbuf_push(buf, indent + 2, "Delay Variation: %u (micro-sec)\n", + asla->delay_var & TE_EXT_MASK); + } + if (IS_SUBTLV(asla, EXT_PKT_LOSS)) + sbuf_push(buf, indent + 2, "%s Link Packet Loss: %g (%%)\n", + IS_ANORMAL(asla->pkt_loss) ? "Anomalous" : "Normal", + (float)((asla->pkt_loss & TE_EXT_MASK) * + LOSS_PRECISION)); + if (IS_SUBTLV(asla, EXT_RES_BW)) + sbuf_push(buf, indent + 2, + "Unidir. Residual Bandwidth: %g (Bytes/sec)\n", + asla->res_bw); + if (IS_SUBTLV(asla, EXT_AVA_BW)) + sbuf_push(buf, indent + 2, + "Unidir. Available Bandwidth: %g (Bytes/sec)\n", + asla->ava_bw); + if (IS_SUBTLV(asla, EXT_USE_BW)) + sbuf_push(buf, indent + 2, + "Unidir. Utilized Bandwidth: %g (Bytes/sec)\n", + asla->use_bw); +} + /* mtid parameter is used to manage multi-topology i.e. IPv4 / IPv6 */ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts, struct sbuf *buf, struct json_object *json, @@ -240,6 +371,8 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts, char admin_group_buf[ADMIN_GROUP_PRINT_MAX_SIZE]; char aux_buf[255]; char cnt_buf[255]; + struct isis_asla_subtlvs *asla; + struct listnode *node; /* Standard metrics */ if (IS_SUBTLV(exts, EXT_ADM_GRP)) { @@ -688,6 +821,8 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts, lan->neighbor_id); } } + for (ALL_LIST_ELEMENTS_RO(exts->aslas, node, asla)) + format_item_asla_subtlvs(asla, buf, indent); } static void free_item_ext_subtlvs(struct isis_ext_subtlvs *exts) @@ -695,10 +830,124 @@ static void free_item_ext_subtlvs(struct isis_ext_subtlvs *exts) isis_del_ext_subtlvs(exts); } +static int pack_item_ext_subtlv_asla(struct isis_asla_subtlvs *asla, + struct stream *s, size_t *min_len) +{ + size_t subtlv_len; + size_t subtlv_len_pos; + + /* Sub TLV header */ + stream_putc(s, ISIS_SUBTLV_ASLA); + + subtlv_len_pos = stream_get_endp(s); + stream_putc(s, 0); /* length will be filled later */ + + /* SABM Flag/Length */ + if (asla->legacy) + stream_putc(s, ASLA_LEGACY_FLAG | asla->standard_apps_length); + else + stream_putc(s, asla->standard_apps_length); + stream_putc(s, asla->user_def_apps_length); /* UDABM Flag/Length */ + stream_putc(s, asla->standard_apps); + stream_putc(s, asla->user_def_apps); + + /* Administrative Group */ + if (IS_SUBTLV(asla, EXT_ADM_GRP)) { + stream_putc(s, ISIS_SUBTLV_ADMIN_GRP); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putl(s, asla->admin_group); + } + + /* Extended Administrative Group */ + if (IS_SUBTLV(asla, EXT_EXTEND_ADM_GRP) && + admin_group_nb_words(&asla->ext_admin_group) != 0) { + size_t ag_length; + size_t ag_length_pos; + struct admin_group *ag; + + stream_putc(s, ISIS_SUBTLV_EXT_ADMIN_GRP); + ag_length_pos = stream_get_endp(s); + stream_putc(s, 0); /* length will be filled later*/ + + ag = &asla->ext_admin_group; + for (size_t i = 0; i < admin_group_nb_words(ag); i++) + stream_putl(s, ag->bitmap.data[i]); + + ag_length = stream_get_endp(s) - ag_length_pos - 1; + stream_putc_at(s, ag_length_pos, ag_length); + } + + if (IS_SUBTLV(asla, EXT_MAX_BW)) { + stream_putc(s, ISIS_SUBTLV_MAX_BW); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putf(s, asla->max_bw); + } + if (IS_SUBTLV(asla, EXT_MAX_RSV_BW)) { + stream_putc(s, ISIS_SUBTLV_MAX_RSV_BW); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putf(s, asla->max_rsv_bw); + } + if (IS_SUBTLV(asla, EXT_UNRSV_BW)) { + stream_putc(s, ISIS_SUBTLV_UNRSV_BW); + stream_putc(s, ISIS_SUBTLV_UNRSV_BW_SIZE); + for (int j = 0; j < MAX_CLASS_TYPE; j++) + stream_putf(s, asla->unrsv_bw[j]); + } + if (IS_SUBTLV(asla, EXT_TE_METRIC)) { + stream_putc(s, ISIS_SUBTLV_TE_METRIC); + stream_putc(s, ISIS_SUBTLV_TE_METRIC_SIZE); + stream_put3(s, asla->te_metric); + } + if (IS_SUBTLV(asla, EXT_DELAY)) { + stream_putc(s, ISIS_SUBTLV_AV_DELAY); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putl(s, asla->delay); + } + if (IS_SUBTLV(asla, EXT_MM_DELAY)) { + stream_putc(s, ISIS_SUBTLV_MM_DELAY); + stream_putc(s, ISIS_SUBTLV_MM_DELAY_SIZE); + stream_putl(s, asla->min_delay); + stream_putl(s, asla->max_delay); + } + if (IS_SUBTLV(asla, EXT_DELAY_VAR)) { + stream_putc(s, ISIS_SUBTLV_DELAY_VAR); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putl(s, asla->delay_var); + } + if (IS_SUBTLV(asla, EXT_PKT_LOSS)) { + stream_putc(s, ISIS_SUBTLV_PKT_LOSS); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putl(s, asla->pkt_loss); + } + if (IS_SUBTLV(asla, EXT_RES_BW)) { + stream_putc(s, ISIS_SUBTLV_RES_BW); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putf(s, asla->res_bw); + } + if (IS_SUBTLV(asla, EXT_AVA_BW)) { + stream_putc(s, ISIS_SUBTLV_AVA_BW); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putf(s, asla->ava_bw); + } + if (IS_SUBTLV(asla, EXT_USE_BW)) { + stream_putc(s, ISIS_SUBTLV_USE_BW); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putf(s, asla->use_bw); + } + + subtlv_len = stream_get_endp(s) - subtlv_len_pos - 1; + stream_putc_at(s, subtlv_len_pos, subtlv_len); + + return 0; +} + static int pack_item_ext_subtlvs(struct isis_ext_subtlvs *exts, struct stream *s, size_t *min_len) { + struct isis_asla_subtlvs *asla; + struct listnode *node; uint8_t size; + int ret; if (STREAM_WRITEABLE(s) < ISIS_SUBTLV_MAX_SIZE) { *min_len = ISIS_SUBTLV_MAX_SIZE; @@ -862,6 +1111,219 @@ static int pack_item_ext_subtlvs(struct isis_ext_subtlvs *exts, } } + for (ALL_LIST_ELEMENTS_RO(exts->aslas, node, asla)) { + ret = pack_item_ext_subtlv_asla(asla, s, min_len); + if (ret < 0) + return ret; + } + + return 0; +} + +static int unpack_item_ext_subtlv_asla(uint16_t mtid, uint8_t subtlv_len, + struct stream *s, struct sbuf *log, + int indent, + struct isis_ext_subtlvs *exts) +{ + /* Standard App Identifier Bit Flags/Length */ + uint8_t sabm_flag_len; + /* User-defined App Identifier Bit Flags/Length */ + uint8_t uabm_flag_len; + uint8_t sabm[ASLA_APP_IDENTIFIER_BIT_LENGTH] = {0}; + uint8_t uabm[ASLA_APP_IDENTIFIER_BIT_LENGTH] = {0}; + uint8_t readable; + uint8_t subsubtlv_type; + uint8_t subsubtlv_len; + size_t nb_groups; + struct isis_asla_subtlvs *asla; + + if (subtlv_len < ISIS_SUBSUBTLV_HDR_SIZE) { + TLV_SIZE_MISMATCH(log, indent, "ASLA"); + return -1; + } + + + asla = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*asla)); + + admin_group_init(&asla->ext_admin_group); + + + sabm_flag_len = stream_getc(s); + uabm_flag_len = stream_getc(s); + asla->legacy = CHECK_FLAG(sabm_flag_len, ASLA_LEGACY_FLAG); + asla->standard_apps_length = ASLA_APPS_LENGTH_MASK & sabm_flag_len; + asla->user_def_apps_length = ASLA_APPS_LENGTH_MASK & uabm_flag_len; + + for (int i = 0; i < asla->standard_apps_length; i++) + sabm[i] = stream_getc(s); + for (int i = 0; i < asla->user_def_apps_length; i++) + uabm[i] = stream_getc(s); + + asla->standard_apps = sabm[0]; + asla->user_def_apps = uabm[0]; + + readable = subtlv_len - 4; + while (readable > 0) { + if (readable < ISIS_SUBSUBTLV_HDR_SIZE) { + TLV_SIZE_MISMATCH(log, indent, "ASLA Sub TLV"); + return -1; + } + + subsubtlv_type = stream_getc(s); + subsubtlv_len = stream_getc(s); + readable -= ISIS_SUBSUBTLV_HDR_SIZE; + + + switch (subsubtlv_type) { + case ISIS_SUBTLV_ADMIN_GRP: + if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) { + TLV_SIZE_MISMATCH(log, indent, + "ASLA Adm Group"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->admin_group = stream_getl(s); + SET_SUBTLV(asla, EXT_ADM_GRP); + } + break; + + case ISIS_SUBTLV_EXT_ADMIN_GRP: + nb_groups = subsubtlv_len / sizeof(uint32_t); + for (size_t i = 0; i < nb_groups; i++) { + uint32_t val = stream_getl(s); + + admin_group_bulk_set(&asla->ext_admin_group, + val, i); + } + SET_SUBTLV(asla, EXT_EXTEND_ADM_GRP); + break; + case ISIS_SUBTLV_MAX_BW: + if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) { + TLV_SIZE_MISMATCH(log, indent, + "Maximum Bandwidth"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->max_bw = stream_getf(s); + SET_SUBTLV(asla, EXT_MAX_BW); + } + break; + case ISIS_SUBTLV_MAX_RSV_BW: + if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) { + TLV_SIZE_MISMATCH( + log, indent, + "Maximum Reservable Bandwidth"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->max_rsv_bw = stream_getf(s); + SET_SUBTLV(asla, EXT_MAX_RSV_BW); + } + break; + case ISIS_SUBTLV_UNRSV_BW: + if (subsubtlv_len != ISIS_SUBTLV_UNRSV_BW_SIZE) { + TLV_SIZE_MISMATCH(log, indent, + "Unreserved Bandwidth"); + stream_forward_getp(s, subsubtlv_len); + } else { + for (int i = 0; i < MAX_CLASS_TYPE; i++) + asla->unrsv_bw[i] = stream_getf(s); + SET_SUBTLV(asla, EXT_UNRSV_BW); + } + break; + case ISIS_SUBTLV_TE_METRIC: + if (subsubtlv_len != ISIS_SUBTLV_TE_METRIC_SIZE) { + TLV_SIZE_MISMATCH(log, indent, + "Traffic Engineering Metric"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->te_metric = stream_get3(s); + SET_SUBTLV(asla, EXT_TE_METRIC); + } + break; + /* Extended Metrics as defined in RFC 7810 */ + case ISIS_SUBTLV_AV_DELAY: + if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) { + TLV_SIZE_MISMATCH(log, indent, + "Average Link Delay"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->delay = stream_getl(s); + SET_SUBTLV(asla, EXT_DELAY); + } + break; + case ISIS_SUBTLV_MM_DELAY: + if (subsubtlv_len != ISIS_SUBTLV_MM_DELAY_SIZE) { + TLV_SIZE_MISMATCH(log, indent, + "Min/Max Link Delay"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->min_delay = stream_getl(s); + asla->max_delay = stream_getl(s); + SET_SUBTLV(asla, EXT_MM_DELAY); + } + break; + case ISIS_SUBTLV_DELAY_VAR: + if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) { + TLV_SIZE_MISMATCH(log, indent, + "Delay Variation"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->delay_var = stream_getl(s); + SET_SUBTLV(asla, EXT_DELAY_VAR); + } + break; + case ISIS_SUBTLV_PKT_LOSS: + if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) { + TLV_SIZE_MISMATCH(log, indent, + "Link Packet Loss"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->pkt_loss = stream_getl(s); + SET_SUBTLV(asla, EXT_PKT_LOSS); + } + break; + case ISIS_SUBTLV_RES_BW: + if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) { + TLV_SIZE_MISMATCH( + log, indent, + "Unidirectional Residual Bandwidth"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->res_bw = stream_getf(s); + SET_SUBTLV(asla, EXT_RES_BW); + } + break; + case ISIS_SUBTLV_AVA_BW: + if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) { + TLV_SIZE_MISMATCH( + log, indent, + "Unidirectional Available Bandwidth"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->ava_bw = stream_getf(s); + SET_SUBTLV(asla, EXT_AVA_BW); + } + break; + case ISIS_SUBTLV_USE_BW: + if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) { + TLV_SIZE_MISMATCH( + log, indent, + "Unidirectional Utilized Bandwidth"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->use_bw = stream_getf(s); + SET_SUBTLV(asla, EXT_USE_BW); + } + break; + default: + zlog_debug("unknown (t,l)=(%u,%u)", subsubtlv_type, + subsubtlv_len); + stream_forward_getp(s, subsubtlv_len); + break; + } + readable -= subsubtlv_len; + } + + listnode_add(exts->aslas, asla); + return 0; } @@ -1209,6 +1671,13 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, SET_SUBTLV(exts, EXT_LAN_ADJ_SID); } break; + case ISIS_SUBTLV_ASLA: + if (unpack_item_ext_subtlv_asla(mtid, subtlv_len, s, + log, indent, + exts) < 0) { + sbuf_push(log, indent, "TLV parse error"); + } + break; default: /* Skip unknown TLV */ stream_forward_getp(s, subtlv_len); @@ -5873,6 +6342,54 @@ void isis_tlvs_del_lan_adj_sid(struct isis_ext_subtlvs *exts, UNSET_SUBTLV(exts, EXT_LAN_ADJ_SID); } +void isis_tlvs_del_asla_flex_algo(struct isis_ext_subtlvs *ext, + struct isis_asla_subtlvs *asla) +{ + admin_group_term(&asla->ext_admin_group); + listnode_delete(ext->aslas, asla); + XFREE(MTYPE_ISIS_SUBTLV, asla); +} + +struct isis_asla_subtlvs * +isis_tlvs_find_alloc_asla(struct isis_ext_subtlvs *ext, uint8_t standard_apps) +{ + struct isis_asla_subtlvs *asla; + struct listnode *node; + + if (!list_isempty(ext->aslas)) { + for (ALL_LIST_ELEMENTS_RO(ext->aslas, node, asla)) { + if (CHECK_FLAG(asla->standard_apps, standard_apps)) + return asla; + } + } + + asla = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(struct isis_asla_subtlvs)); + admin_group_init(&asla->ext_admin_group); + SET_FLAG(asla->standard_apps, standard_apps); + SET_FLAG(asla->user_def_apps, standard_apps); + asla->standard_apps_length = ASLA_APP_IDENTIFIER_BIT_LENGTH; + asla->user_def_apps_length = ASLA_APP_IDENTIFIER_BIT_LENGTH; + + listnode_add(ext->aslas, asla); + return asla; +} + +void isis_tlvs_free_asla(struct isis_ext_subtlvs *ext, uint8_t standard_apps) +{ + struct isis_asla_subtlvs *asla; + struct listnode *node; + + if (!ext) + return; + + for (ALL_LIST_ELEMENTS_RO(ext->aslas, node, asla)) { + if (!CHECK_FLAG(asla->standard_apps, standard_apps)) + continue; + isis_tlvs_del_asla_flex_algo(ext, asla); + break; + } +} + void isis_tlvs_add_extended_ip_reach(struct isis_tlvs *tlvs, struct prefix_ipv4 *dest, uint32_t metric, bool external, diff --git a/isisd/isis_tlvs.h b/isisd/isis_tlvs.h index d5c96d7ab0..331cad93e9 100644 --- a/isisd/isis_tlvs.h +++ b/isisd/isis_tlvs.h @@ -400,6 +400,9 @@ enum isis_tlv_type { ISIS_SUBTLV_AVA_BW = 38, ISIS_SUBTLV_USE_BW = 39, + /* RFC 8919 */ + ISIS_SUBTLV_ASLA = 16, + ISIS_SUBTLV_MAX = 40, }; @@ -434,6 +437,8 @@ enum ext_subtlv_size { /* RFC 7308 */ ISIS_SUBTLV_EXT_ADMIN_GRP = 14, + ISIS_SUBSUBTLV_HDR_SIZE = 2, + ISIS_SUBTLV_MAX_SIZE = 180, }; @@ -441,6 +446,8 @@ enum ext_subtlv_size { #define SET_SUBTLV(s, t) ((s->status) |= (t)) #define UNSET_SUBTLV(s, t) ((s->status) &= ~(t)) #define IS_SUBTLV(s, t) (s->status & t) +#define RESET_SUBTLV(s) (s->status = 0) +#define NO_SUBTLV(s) (s->status == 0) #define EXT_DISABLE 0x000000 #define EXT_ADM_GRP 0x000001 @@ -512,6 +519,45 @@ struct isis_ext_subtlvs { /* Segment Routing Adjacency & LAN Adjacency Segment ID */ struct isis_item_list adj_sid; struct isis_item_list lan_sid; + + struct list *aslas; +}; + +/* RFC 8919 */ +#define ISIS_SABM_FLAG_R 0x80 /* RSVP-TE */ +#define ISIS_SABM_FLAG_S 0x40 /* Segment Routing Policy */ +#define ISIS_SABM_FLAG_L 0x20 /* Loop-Free Alternate */ +#define ISIS_SABM_FLAG_X 0x10 /* Flex-Algorithm - RFC9350 */ + +#define ASLA_APP_IDENTIFIER_BIT_LENGTH 1 +#define ASLA_LEGACY_FLAG 0x80 +#define ASLA_APPS_LENGTH_MASK 0x7f + +struct isis_asla_subtlvs { + uint32_t status; + + /* Application Specific Link Attribute - RFC 8919 */ + bool legacy; /* L-Flag */ + uint8_t standard_apps_length; + uint8_t user_def_apps_length; + uint8_t standard_apps; + uint8_t user_def_apps; + + /* Sub-TLV list - rfc8919 section-3.1 */ + uint32_t admin_group; + struct admin_group ext_admin_group; /* Res. Class/Color - RFC 7308 */ + float max_bw; /* Maximum Bandwidth - RFC 5305 */ + float max_rsv_bw; /* Maximum Reservable Bandwidth - RFC 5305 */ + float unrsv_bw[8]; /* Unreserved Bandwidth - RFC 5305 */ + uint32_t te_metric; /* Traffic Engineering Metric - RFC 5305 */ + uint32_t delay; /* Average Link Delay - RFC 8570 */ + uint32_t min_delay; /* Low Link Delay - RFC 8570 */ + uint32_t max_delay; /* High Link Delay - RFC 8570 */ + uint32_t delay_var; /* Link Delay Variation i.e. Jitter - RFC 8570 */ + uint32_t pkt_loss; /* Unidirectional Link Packet Loss - RFC 8570 */ + float res_bw; /* Unidirectional Residual Bandwidth - RFC 8570 */ + float ava_bw; /* Unidirectional Available Bandwidth - RFC 8570 */ + float use_bw; /* Unidirectional Utilized Bandwidth - RFC 8570 */ }; #define IS_COMPAT_MT_TLV(tlv_type) \ @@ -542,6 +588,12 @@ struct list *isis_fragment_tlvs(struct isis_tlvs *tlvs, size_t size); #define ISIS_MT_AT_MASK 0x4000 #endif +/* RFC 8919 */ +#define ISIS_SABM_FLAG_R 0x80 /* RSVP-TE */ +#define ISIS_SABM_FLAG_S 0x40 /* Segment Routing Policy */ +#define ISIS_SABM_FLAG_L 0x20 /* Loop-Free Alternate */ +#define ISIS_SABM_FLAG_X 0x10 /* Flex-Algorithm - RFC9350 */ + void isis_tlvs_add_auth(struct isis_tlvs *tlvs, struct isis_passwd *passwd); void isis_tlvs_add_area_addresses(struct isis_tlvs *tlvs, struct list *addresses); @@ -606,6 +658,12 @@ void isis_tlvs_add_lan_adj_sid(struct isis_ext_subtlvs *exts, void isis_tlvs_del_lan_adj_sid(struct isis_ext_subtlvs *exts, struct isis_lan_adj_sid *lan); +void isis_tlvs_del_asla_flex_algo(struct isis_ext_subtlvs *ext, + struct isis_asla_subtlvs *asla); +struct isis_asla_subtlvs * +isis_tlvs_find_alloc_asla(struct isis_ext_subtlvs *ext, uint8_t standard_apps); +void isis_tlvs_free_asla(struct isis_ext_subtlvs *ext, uint8_t standard_apps); + void isis_tlvs_add_oldstyle_reach(struct isis_tlvs *tlvs, uint8_t *id, uint8_t metric); void isis_tlvs_add_extended_reach(struct isis_tlvs *tlvs, uint16_t mtid, -- 2.39.5