diff --git a/doc/user/pathd.rst b/doc/user/pathd.rst index 5fc3837839d4..0719bee79edd 100644 --- a/doc/user/pathd.rst +++ b/doc/user/pathd.rst @@ -28,25 +28,8 @@ documented elsewhere. PCEP Support ============ -To build the PCC for pathd, the externall library `pceplib 1.2 `_ is required. +A pceplib is included in the frr source tree and build by default. -To build FRR with support for PCEP the following steps must be followed: - - - Checkout and build pceplib: - -``` -$ git clone https://github.com/volta-networks/pceplib -$ cd pceplib -$ make -$ make install -$ export PCEPLIB_ROOT=$PWD -``` - - - Configure FRR with the extra parameters: - -``` ---enable-pcep LDFLAGS="-L${PCEPLIB_ROOT}/install/lib" CPPFLAGS="-I${PCEPLIB_ROOT}/install/include" -``` To start pathd with pcep support the extra parameter `-M pathd_pcep` should be passed to the pathd daemon. @@ -62,10 +45,18 @@ Example: debug pathd pcep basic segment-routing traffic-eng + mpls-te on + mpls-te import ospfv2 segment-list SL1 index 10 mpls label 16010 index 20 mpls label 16030 ! + segment-list SL2 + index 10 nai prefix 10.1.2.1/32 iface 1 + index 20 nai adjacency 10.1.20.1 10.1.20.2 + index 30 nai prefix 10.10.10.5/32 algorithm 0 + index 40 mpls label 18001 + ! policy color 1 endpoint 1.1.1.1 name default binding-sid 4000 @@ -113,11 +104,22 @@ Configuration Commands Configure segment routing traffic engineering. +.. clicmd:: [no] mpls-te + + Activate/Deactivate use of internal Traffic Engineering Database + +.. clicmd:: [no] mpls-te import + + Load data from the igp selected + .. clicmd:: segment-list NAME Delete or start a segment list definition. -.. clicmd:: index INDEX mpls label LABEL [nai node ADDRESS] +.. clicmd:: index INDEX mpls label LABEL +.. clicmd:: index INDEX nai adjacency A.B.C.D A.B.C.D +.. clicmd:: index INDEX nai prefix A.B.C.D/M algorithm <0|1> +.. clicmd:: index INDEX nai prefix A.B.C.D/M iface (0-65535) Delete or specify a segment in a segment list definition. diff --git a/pathd/path_cli.c b/pathd/path_cli.c index cf14aa8c6168..f6b3256c46dc 100644 --- a/pathd/path_cli.c +++ b/pathd/path_cli.c @@ -32,6 +32,7 @@ #ifndef VTYSH_EXTRACT_PL #include "pathd/path_cli_clippy.c" #endif +#include "pathd/path_ted.h" #define XPATH_MAXATTRSIZE 64 #define XPATH_MAXKEYSIZE 42 @@ -142,6 +143,7 @@ DEFPY(show_srte_policy, return CMD_SUCCESS; } + /* * Show detailed SR-TE info */ @@ -296,49 +298,204 @@ void cli_show_srte_segment_list(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-pathd:pathd/srte/segment-list/segment */ -DEFPY(srte_segment_list_segment, - srte_segment_list_segment_cmd, - "index (0-4294967295)$index mpls label (16-1048575)$label " +/* clang-format off */ +DEFPY(srte_segment_list_segment, srte_segment_list_segment_cmd, + "index (0-4294967295)$index <[mpls$has_mpls_label label (16-1048575)$label] " + "|" "[nai$has_nai <" - "node " - ">]", + "prefix " + "" + "| adjacency$has_adj " + "" + ">]" + ">", "Index\n" "Index Value\n" "MPLS or IP Label\n" "Label\n" "Label Value\n" "Segment NAI\n" - "NAI node identifier\n" - "NAI IPv4 node identifier\n" - "NAI IPv6 node identifier\n") + "NAI prefix identifier\n" + "NAI IPv4 prefix identifier\n" + "NAI IPv6 prefix identifier\n" + "IGP Algorithm\n" + "Algorithm Value SPF or Strict-SPF\n" + "Interface Id\n" + "Interface Value\n" + "ADJ identifier\n" + "ADJ IPv4 src identifier\n" + "ADJ IPv4 dst identifier\n" + "ADJ IPv6 src identifier\n" + "ADJ IPv6 dst identifier\n") +/* clang-format on */ { char xpath[XPATH_MAXLEN]; - const char *node_id; + const char *node_src_id; + char buf_prefix[INET6_ADDRSTRLEN]; + + char buf[INET6_ADDRSTRLEN]; + char buf2[INET6_ADDRSTRLEN]; + uint32_t ted_sid = MPLS_LABEL_NONE; + struct prefix prefix_cli = {0}; snprintf(xpath, sizeof(xpath), "./segment[index='%s']", index_str); nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf(xpath, sizeof(xpath), "./segment[index='%s']/sid-value", - index_str); - nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, label_str); + if (has_mpls_label != NULL) { + snprintf(xpath, sizeof(xpath), + "./segment[index='%s']/sid-value", index_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, label_str); + } if (has_nai != NULL) { - snprintf(xpath, sizeof(xpath), "./segment[index='%s']/nai/type", - index_str); - if (node_ipv4_str != NULL) { + if (has_adj != NULL) { + struct ipaddr ip_src = {.ipa_type = IPADDR_V4, + .ip._v4_addr = adj_src_ipv4}; + struct ipaddr ip_dst = {.ipa_type = IPADDR_V4, + .ip._v4_addr = adj_dst_ipv4}; + ted_sid = path_ted_query_type_f(&ip_src, &ip_dst); + if (ted_sid == MPLS_LABEL_NONE) { + inet_ntop(AF_INET, &adj_src_ipv4, buf, + sizeof(buf)); + inet_ntop(AF_INET, &adj_dst_ipv4, buf, + sizeof(buf2)); + zlog_warn( + "%s: [rcv ted] CLI NOT FOUND Continue query_type_f SRC (%pIA) DST (%pIA)!", + __func__, &ip_src, &ip_dst); + } + /* type */ + snprintf(xpath, sizeof(xpath), + "./segment[index='%s']/nai/type", index_str); + if (adj_src_ipv4_str != NULL) { + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, + "ipv4_adjacency"); + node_src_id = adj_src_ipv4_str; + } else if (adj_src_ipv6_str != NULL) { + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, + "ipv6_adjacency"); + node_src_id = adj_src_ipv6_str; + } else { + return CMD_ERR_NO_MATCH; + } + /* addresses */ + snprintf(xpath, sizeof(xpath), + "./segment[index='%s']/nai/local-address", + index_str); nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, - "ipv4_node"); - node_id = node_ipv4_str; - } else if (node_ipv6_str != NULL) { + node_src_id); + snprintf(xpath, sizeof(xpath), + "./segment[index='%s']/nai/remote-address", + index_str); nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, - "ipv6_node"); - node_id = node_ipv6_str; + adj_dst_ipv4_str + ? adj_dst_ipv4_str + : adj_dst_ipv6_str); } else { - return CMD_ERR_NO_MATCH; + /* prefix with algorithm or local interface id */ + /* Type */ + snprintf(xpath, sizeof(xpath), + "./segment[index='%s']/nai/type", index_str); + if (has_iface_id != NULL) { + if (prefix_ipv4_str != NULL) { + nb_cli_enqueue_change( + vty, xpath, NB_OP_MODIFY, + "ipv4_local_iface"); + } else if (prefix_ipv6_str != NULL) { + nb_cli_enqueue_change( + vty, xpath, NB_OP_MODIFY, + "ipv6_local_iface"); + } else { + return CMD_ERR_NO_MATCH; + } + } else { + if (prefix_ipv4_str != NULL) { + nb_cli_enqueue_change(vty, xpath, + NB_OP_MODIFY, + "ipv4_algo"); + } else if (prefix_ipv6_str != NULL) { + nb_cli_enqueue_change(vty, xpath, + NB_OP_MODIFY, + "ipv6_algo"); + } else { + return CMD_ERR_NO_MATCH; + } + } + /* Prefix */ + if (prefix_ipv4_str != NULL) { + if (!str2prefix(prefix_ipv4_str, &prefix_cli)) { + vty_out(vty, "%% Malformed prefix\n"); + return CMD_WARNING_CONFIG_FAILED; + } + inet_ntop(AF_INET, &prefix_cli.u.prefix4, + buf_prefix, sizeof(buf_prefix)); + } else { + if (!str2prefix(prefix_ipv6_str, &prefix_cli)) { + vty_out(vty, "%% Malformed prefix\n"); + return CMD_WARNING_CONFIG_FAILED; + } + inet_ntop(AF_INET6, &prefix_cli.u.prefix6, + buf_prefix, sizeof(buf_prefix)); + } + snprintf(xpath, sizeof(xpath), + "./segment[index='%s']/nai/local-address", + index_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, + buf_prefix); + snprintf(xpath, sizeof(xpath), + "./segment[index='%s']/nai/local-prefix-len", + index_str); + nb_cli_enqueue_change( + vty, xpath, NB_OP_MODIFY, + prefix_ipv4_str + ? strchr(prefix_ipv4_str, '/') + 1 + : strchr(prefix_ipv6_str, '/') + 1); + /* Alg / Iface */ + if (has_algo != NULL) { + snprintf(xpath, sizeof(xpath), + "./segment[index='%s']/nai/algorithm", + index_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, + algo_str); + } else { + if (has_iface_id != NULL) { + snprintf( + xpath, sizeof(xpath), + "./segment[index='%s']/nai/local-interface", + index_str); + nb_cli_enqueue_change(vty, xpath, + NB_OP_MODIFY, + iface_id_str); + } + } + if (has_algo != NULL) { + ted_sid = path_ted_query_type_c(&prefix_cli, + algo); + if (ted_sid == MPLS_LABEL_NONE) { + inet_ntop(AF_INET, + &prefix_cli.u.prefix4, buf, + sizeof(buf)); + zlog_err( + "%s: [rcv ted] CLI NOT FOUND Continue query_type_c PREFIX (%s(%d)) ALGO (%ld) sid:(%d)!", + __func__, buf, + prefix_cli.prefixlen, algo, + ted_sid); + } + } + if (has_iface_id != NULL) { + ted_sid = path_ted_query_type_e(&prefix_cli, + iface_id); + if (ted_sid == MPLS_LABEL_NONE) { + inet_ntop(AF_INET, + &prefix_cli.u.prefix4, buf, + sizeof(buf)); + zlog_err( + "%s: [rcv ted] CLI NOT FOUND Continue query_type_e PREFIX (%s/%d) IFACE (%ld) sid:(%d)!", + __func__, buf, + prefix_cli.prefixlen, iface_id, + ted_sid); + } + } } - snprintf(xpath, sizeof(xpath), - "./segment[index='%s']/nai/local-address", index_str); - nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, node_id); } else { snprintf(xpath, sizeof(xpath), "./segment[index='%s']/nai", index_str); @@ -367,23 +524,59 @@ void cli_show_srte_segment_list_segment(struct vty *vty, struct lyd_node *dnode, bool show_defaults) { - vty_out(vty, " index %s mpls label %s", - yang_dnode_get_string(dnode, "./index"), - yang_dnode_get_string(dnode, "./sid-value")); + vty_out(vty, " index %s ", yang_dnode_get_string(dnode, "./index")); + if (yang_dnode_exists(dnode, "./sid-value")) { + vty_out(vty, " mpls label %s", + yang_dnode_get_string(dnode, "./sid-value")); + } if (yang_dnode_exists(dnode, "./nai")) { struct ipaddr addr; + struct ipaddr addr_rmt; switch (yang_dnode_get_enum(dnode, "./nai/type")) { case SRTE_SEGMENT_NAI_TYPE_IPV4_NODE: + case SRTE_SEGMENT_NAI_TYPE_IPV4_LOCAL_IFACE: + case SRTE_SEGMENT_NAI_TYPE_IPV4_ALGORITHM: yang_dnode_get_ip(&addr, dnode, "./nai/local-address"); - vty_out(vty, " nai node %pI4", &addr.ipaddr_v4); + vty_out(vty, " nai prefix %pI4", &addr.ipaddr_v4); break; case SRTE_SEGMENT_NAI_TYPE_IPV6_NODE: + case SRTE_SEGMENT_NAI_TYPE_IPV6_LOCAL_IFACE: + case SRTE_SEGMENT_NAI_TYPE_IPV6_ALGORITHM: yang_dnode_get_ip(&addr, dnode, "./nai/local-address"); - vty_out(vty, " nai node %pI6", &addr.ipaddr_v6); + vty_out(vty, " nai prefix %pI6", &addr.ipaddr_v6); + break; + case SRTE_SEGMENT_NAI_TYPE_IPV4_ADJACENCY: + yang_dnode_get_ip(&addr, dnode, "./nai/local-address"); + yang_dnode_get_ip(&addr_rmt, dnode, + "./nai/remote-address"); + vty_out(vty, " nai adjacency %pI4", &addr.ipaddr_v4); + vty_out(vty, " %pI4", &addr_rmt.ipaddr_v4); + break; + case SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY: + yang_dnode_get_ip(&addr, dnode, "./nai/local-address"); + yang_dnode_get_ip(&addr_rmt, dnode, + "./nai/remote-address"); + vty_out(vty, " nai adjacency %pI6", &addr.ipaddr_v6); + vty_out(vty, " %pI6", &addr_rmt.ipaddr_v6); break; default: break; } + if (yang_dnode_exists(dnode, "./nai/local-prefix-len")) { + vty_out(vty, "/%s", + yang_dnode_get_string( + dnode, "./nai/local-prefix-len")); + } + if (yang_dnode_exists(dnode, "./nai/local-interface")) { + vty_out(vty, " iface %s", + yang_dnode_get_string(dnode, + "./nai/local-interface")); + } + if (yang_dnode_exists(dnode, "./nai/algorithm")) { + vty_out(vty, " algorithm %s", + yang_dnode_get_string(dnode, + "./nai/algorithm")); + } } vty_out(vty, "\n"); } @@ -1036,6 +1229,7 @@ int config_write_segment_routing(struct vty *vty) int config_write_traffic_eng(struct vty *vty) { vty_out(vty, " traffic-eng\n"); + path_ted_config_write(vty); return 1; } diff --git a/pathd/path_main.c b/pathd/path_main.c index f54ab736c4b9..8d884752065a 100644 --- a/pathd/path_main.c +++ b/pathd/path_main.c @@ -33,6 +33,7 @@ #include "path_nb.h" #include "path_zebra.h" #include "path_errors.h" +#include "path_ted.h" char backup_config_file[256]; @@ -70,6 +71,8 @@ static void sighup(void) static void sigint(void) { zlog_notice("Terminating on signal"); + zlog_notice("Unregisterfrom opaque,etc "); + pathd_shutdown(); exit(0); } @@ -146,6 +149,7 @@ int main(int argc, char **argv, char **envp) path_error_init(); path_zebra_init(master); path_cli_init(); + path_ted_init(master); frr_config_fork(); frr_run(master); diff --git a/pathd/path_nb.c b/pathd/path_nb.c index a210e31b9c1b..9c622883bc21 100644 --- a/pathd/path_nb.c +++ b/pathd/path_nb.c @@ -90,6 +90,7 @@ const struct frr_yang_module_info frr_pathd_info = { .xpath = "/frr-pathd:pathd/srte/segment-list/segment/sid-value", .cbs = { .modify = pathd_srte_segment_list_segment_sid_value_modify, + .destroy = pathd_srte_segment_list_segment_sid_value_destroy, }, .priority = NB_DFLT_PRIORITY - 1 }, @@ -114,6 +115,10 @@ const struct frr_yang_module_info frr_pathd_info = { .xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai/local-interface", .cbs = {.modify = dummy_modify, .destroy = dummy_destroy} }, + { + .xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai/local-prefix-len", + .cbs = {.modify = dummy_modify, .destroy = dummy_destroy} + }, { .xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai/remote-address", .cbs = {.modify = dummy_modify, .destroy = dummy_destroy} @@ -122,6 +127,10 @@ const struct frr_yang_module_info frr_pathd_info = { .xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai/remote-interface", .cbs = {.modify = dummy_modify, .destroy = dummy_destroy} }, + { + .xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai/algorithm", + .cbs = {.modify = dummy_modify, .destroy = dummy_destroy} + }, { .xpath = "/frr-pathd:pathd/srte/policy", .cbs = { diff --git a/pathd/path_nb.h b/pathd/path_nb.h index 3a0b3863ce7b..caeadd9cccc1 100644 --- a/pathd/path_nb.h +++ b/pathd/path_nb.h @@ -43,6 +43,8 @@ int pathd_srte_segment_list_segment_nai_destroy( struct nb_cb_destroy_args *args); void pathd_srte_segment_list_segment_nai_apply_finish( struct nb_cb_apply_finish_args *args); +int pathd_srte_segment_list_segment_sid_value_destroy( + struct nb_cb_destroy_args *args); int pathd_srte_policy_create(struct nb_cb_create_args *args); int pathd_srte_policy_destroy(struct nb_cb_destroy_args *args); const void *pathd_srte_policy_get_next(struct nb_cb_get_next_args *args); diff --git a/pathd/path_nb_config.c b/pathd/path_nb_config.c index af54f5bce230..5b0f5b44e5c8 100644 --- a/pathd/path_nb_config.c +++ b/pathd/path_nb_config.c @@ -160,6 +160,22 @@ int pathd_srte_segment_list_segment_sid_value_modify( return NB_OK; } +int pathd_srte_segment_list_segment_sid_value_destroy( + struct nb_cb_destroy_args *args) +{ + struct srte_segment_entry *segment; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + segment = nb_running_get_entry(args->dnode, NULL, true); + segment->sid_value = MPLS_LABEL_NONE; + SET_FLAG(segment->segment_list->flags, F_SEGMENT_LIST_MODIFIED); + + return NB_OK; +} + + int pathd_srte_segment_list_segment_nai_destroy(struct nb_cb_destroy_args *args) { struct srte_segment_entry *segment; @@ -184,6 +200,8 @@ void pathd_srte_segment_list_segment_nai_apply_finish( enum srte_segment_nai_type type; struct ipaddr local_addr, remote_addr; uint32_t local_iface = 0, remote_iface = 0; + uint8_t algo = 0, local_prefix_len = 0; + const char *algo_buf, *local_prefix_len_buf; segment = nb_running_get_entry(args->dnode, NULL, true); type = yang_dnode_get_enum(args->dnode, "./type"); @@ -207,12 +225,31 @@ void pathd_srte_segment_list_segment_nai_apply_finish( remote_iface = yang_dnode_get_uint32(args->dnode, "./remote-interface"); break; + case SRTE_SEGMENT_NAI_TYPE_IPV4_ALGORITHM: + algo_buf = yang_dnode_get_string(args->dnode, "./algorithm"); + algo = atoi(algo_buf); + local_prefix_len_buf = yang_dnode_get_string( + args->dnode, "./local-prefix-len"); + local_prefix_len = atoi(local_prefix_len_buf); + break; + case SRTE_SEGMENT_NAI_TYPE_IPV4_LOCAL_IFACE: + local_iface = + yang_dnode_get_uint32(args->dnode, "./local-interface"); + local_prefix_len_buf = yang_dnode_get_string( + args->dnode, "./local-prefix-len"); + local_prefix_len = atoi(local_prefix_len_buf); + break; default: break; } - srte_segment_entry_set_nai(segment, type, &local_addr, local_iface, - &remote_addr, remote_iface); + zlog_debug(" Segment list name (%d) index (%s) ", segment->index, + segment->segment_list->name); + if (srte_segment_entry_set_nai(segment, type, &local_addr, local_iface, + &remote_addr, remote_iface, algo, + local_prefix_len)) + SET_FLAG(segment->segment_list->flags, + F_SEGMENT_LIST_SID_CONFLICT); } /* diff --git a/pathd/path_pcep_config.c b/pathd/path_pcep_config.c index 107475bec9e0..6e14061fce66 100644 --- a/pathd/path_pcep_config.c +++ b/pathd/path_pcep_config.c @@ -244,6 +244,10 @@ path_pcep_config_list_path_hops(struct srte_segment_list *segment_list) switch (segment->nai_type) { case SRTE_SEGMENT_NAI_TYPE_IPV4_NODE: case SRTE_SEGMENT_NAI_TYPE_IPV6_NODE: + case SRTE_SEGMENT_NAI_TYPE_IPV4_LOCAL_IFACE: + case SRTE_SEGMENT_NAI_TYPE_IPV6_LOCAL_IFACE: + case SRTE_SEGMENT_NAI_TYPE_IPV4_ALGORITHM: + case SRTE_SEGMENT_NAI_TYPE_IPV6_ALGORITHM: memcpy(&hop->nai.local_addr, &segment->nai_local_addr, sizeof(struct ipaddr)); break; @@ -276,6 +280,7 @@ int path_pcep_config_update_path(struct path *path) assert(path->nbkey.preference != 0); assert(path->nbkey.endpoint.ipa_type == IPADDR_V4); + int number_of_sid_clashed = 0; struct path_hop *hop; struct path_metric *metric; int index; @@ -295,6 +300,7 @@ int path_pcep_config_update_path(struct path *path) if (candidate->lsp->segment_list) { SET_FLAG(candidate->lsp->segment_list->flags, F_SEGMENT_LIST_DELETED); + srte_segment_list_del(candidate->lsp->segment_list); candidate->lsp->segment_list = NULL; } @@ -322,12 +328,17 @@ int path_pcep_config_update_path(struct path *path) F_SEGMENT_LIST_MODIFIED); if (hop->has_nai) - srte_segment_entry_set_nai( - segment, srte_nai_type(hop->nai.type), - &hop->nai.local_addr, - hop->nai.local_iface, - &hop->nai.remote_addr, - hop->nai.remote_iface); + if (srte_segment_entry_set_nai( + segment, + srte_nai_type(hop->nai.type), + &hop->nai.local_addr, + hop->nai.local_iface, + &hop->nai.remote_addr, + hop->nai.remote_iface, 0, 0) + == PATH_SID_ERROR) + /* TED queries don't match PCE */ + /* Don't apply srte,zebra changes */ + number_of_sid_clashed++; } } @@ -350,7 +361,11 @@ int path_pcep_config_update_path(struct path *path) candidate->lsp->objfun = path->pce_objfun; } - srte_apply_changes(); + if (number_of_sid_clashed) + SET_FLAG(segment->segment_list->flags, + F_SEGMENT_LIST_SID_CONFLICT); + else + srte_apply_changes(); return 0; } @@ -400,6 +415,16 @@ enum pcep_sr_subobj_nai pcep_nai_type(enum srte_segment_nai_type type) return PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY; case SRTE_SEGMENT_NAI_TYPE_IPV4_UNNUMBERED_ADJACENCY: return PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY; + case SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY_LINK_LOCAL_ADDRESSES: + return PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY; + case SRTE_SEGMENT_NAI_TYPE_IPV4_LOCAL_IFACE: + return PCEP_SR_SUBOBJ_NAI_IPV4_LOCAL_IFACE; + case SRTE_SEGMENT_NAI_TYPE_IPV6_LOCAL_IFACE: + return PCEP_SR_SUBOBJ_NAI_IPV6_LOCAL_IFACE; + case SRTE_SEGMENT_NAI_TYPE_IPV4_ALGORITHM: + return PCEP_SR_SUBOBJ_NAI_IPV4_ALGORITHM; + case SRTE_SEGMENT_NAI_TYPE_IPV6_ALGORITHM: + return PCEP_SR_SUBOBJ_NAI_IPV6_ALGORITHM; default: return PCEP_SR_SUBOBJ_NAI_UNKNOWN; } diff --git a/pathd/path_ted.c b/pathd/path_ted.c new file mode 100644 index 000000000000..8b7a0f24e001 --- /dev/null +++ b/pathd/path_ted.c @@ -0,0 +1,780 @@ +/* + * Copyright (C) 2020 Volta Networks, Inc + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include "stdlib.h" + +#include + +#include "memory.h" +#include "log.h" +#include "command.h" +#include + +#include "pathd.h" +#include "pathd/path_errors.h" +#include "pathd/path_ted.h" + +#ifndef VTYSH_EXTRACT_PL +#include "pathd/path_ted_clippy.c" +#endif + +static struct ls_ted *path_ted_create_ted(void); +static void path_ted_register_vty(void); +static void path_ted_unregister_vty(void); +static uint32_t path_ted_start_importing_igp(const char *daemon_str); +static uint32_t path_ted_stop_importing_igp(void); +static enum zclient_send_status path_ted_link_state_sync(void); +static int path_ted_timer_handler_sync(struct thread *thread); +static int path_ted_timer_handler_refresh(struct thread *thread); + +extern struct zclient *zclient; + +#define TIMER_RETRY_DELAY 5 /* Timeout in seconds between ls sync request */ +enum igp_import { + IMPORT_UNKNOWN = 0, + IMPORT_ISIS, + IMPORT_OSPFv2, + IMPORT_OSPFv3 +}; +struct ted_state { + struct thread_master *main; + /* Status of TED: enable or disable */ + bool enabled; + /* From which igp is going to receive data */ + enum igp_import import; + /* The TED itself as in link_state.h */ + struct ls_ted *ted; + /* Timer for ted sync */ + struct thread *t_link_state_sync; + /* Timer for refresh sid in segment list */ + struct thread *t_segment_list_refresh; + /* delay interval in seconds */ + uint32_t link_state_delay_interval; + /* delay interval refresh in seconds */ + uint32_t segment_list_refresh_interval; +}; +static struct ted_state ted_state_g = {0}; + +static const uint32_t TED_KEY = 1; +static const uint32_t TED_ASN = 1; +static const char *TED_NAME = "PATHD TED"; + +/* + * path_path_ted public API function implementations + */ + +void path_ted_init(struct thread_master *master) +{ + ted_state_g.main = master; + ted_state_g.link_state_delay_interval = TIMER_RETRY_DELAY; + ted_state_g.segment_list_refresh_interval = TIMER_RETRY_DELAY; + path_ted_register_vty(); + path_ted_segment_list_refresh(); +} + +uint32_t path_ted_teardown(void) +{ + zlog_debug("%s : TED [%p]", __func__, ted_state_g.ted); + path_ted_unregister_vty(); + path_ted_stop_importing_igp(); + ls_ted_del_all(ted_state_g.ted); + path_ted_timer_sync_cancel(); + path_ted_timer_refresh_cancel(); + return 0; +} + +/** + * Set all needed to receive igp data. + * + * @return true if ok + * + */ +uint32_t path_ted_start_importing_igp(const char *daemon_str) +{ + uint32_t status = 0; + + if (strcmp(daemon_str, "ospfv2") == 0) + ted_state_g.import = IMPORT_OSPFv2; + else if (strcmp(daemon_str, "ospfv3") == 0) { + ted_state_g.import = IMPORT_UNKNOWN; + return 1; + } else if (strcmp(daemon_str, "isis") == 0) + ted_state_g.import = IMPORT_ISIS; + + if (ls_register(zclient, false /*client*/) != 0) { + zlog_err("%s: PATHD-TED: Unable to register Link State", + __func__); + ted_state_g.import = IMPORT_UNKNOWN; + status = 1; + } else { + if (path_ted_link_state_sync() != -1) { + zlog_debug("%s: PATHD-TED: Importing %s data ON", + __func__, + PATH_TED_IGP_PRINT(ted_state_g.import)); + } else { + zlog_warn("%s: PATHD-TED: Importing %s data OFF", + __func__, + PATH_TED_IGP_PRINT(ted_state_g.import)); + ted_state_g.import = IMPORT_UNKNOWN; + } + } + return status; +} + +/** + * Unset all needed to receive igp data. + * + * @return true if ok + * + */ +uint32_t path_ted_stop_importing_igp(void) +{ + uint32_t status = 0; + + if (ted_state_g.import != IMPORT_UNKNOWN) { + if (ls_unregister(zclient, false /*client*/) != 0) { + zlog_err( + "%s: PATHD-TED: Unable to unregister Link State", + __func__); + status = 1; + } else { + ted_state_g.import = IMPORT_UNKNOWN; + zlog_debug("%s: PATHD-TED: Importing igp data OFF", + __func__); + path_ted_timer_sync_cancel(); + } + } + return status; +} +/** + * Check for ted status + * + * @return true if ok + * + */ +bool path_ted_is_initialized(void) +{ + if (ted_state_g.ted == NULL) { + zlog_warn("PATHD TED ls_ted not initialized"); + return false; + } + + return true; +} + +/* + * Internal util functions + */ + +/** + * Creates an empty ted + * + * @param void + * + * @return Ptr to ted or NULL + */ +struct ls_ted *path_ted_create_ted() +{ + struct ls_ted *ted = ls_ted_new(TED_KEY, TED_NAME, TED_ASN); + + if (ted == NULL) { + zlog_err("Unable to initialize TED Key [%d] ASN [%d] Name [%s]", + TED_KEY, TED_ASN, TED_NAME); + } else { + zlog_info("Initialize TED Key [%d] ASN [%d] Name [%s]", TED_KEY, + TED_ASN, TED_NAME); + } + + return ted; +} + +uint32_t path_ted_rcvd_message(struct ls_message *msg) +{ + if (!path_ted_is_initialized()) + return MPLS_LABEL_NONE; + + if (msg == NULL) { + zlog_err("%s: [rcv ted] TED received NULL message ", __func__); + return 1; + } + + if (path_ted_get_current_igp(msg->data.node->adv.origin)) + return 1; + + + assert(msg != NULL); + + switch (msg->type) { + case LS_MSG_TYPE_NODE: + ls_msg2vertex(ted_state_g.ted, msg, true /*hard delete*/); + switch (msg->event) { + case LS_MSG_EVENT_ADD: + zlog_debug( + "%s: [rcv ted] TED received Node message add", + __func__); + break; + case LS_MSG_EVENT_DELETE: + zlog_debug( + "%s: [rcv ted] TED received Node message del", + __func__); + break; + case LS_MSG_EVENT_UPDATE: + zlog_debug( + "%s: [rcv ted] TED received Node message upd", + __func__); + break; + case LS_MSG_EVENT_SYNC: + zlog_debug( + "%s: [rcv ted] TED received Node message syn", + __func__); + break; + case LS_MSG_EVENT_UNDEF: + zlog_debug( + "%s: [rcv ted] TED received Node message und", + __func__); + default: + break; + } + break; + + case LS_MSG_TYPE_ATTRIBUTES: + ls_msg2edge(ted_state_g.ted, msg, true /*ĥard delete*/); + switch (msg->event) { + case LS_MSG_EVENT_ADD: + zlog_debug( + "%s: [rcv ted] TED received Attributes message add", + __func__); + break; + case LS_MSG_EVENT_DELETE: + zlog_debug( + "%s: [rcv ted] TED received Attributes message del", + __func__); + break; + case LS_MSG_EVENT_UPDATE: + zlog_debug( + "%s: [rcv ted] TED received Attributes message upd", + __func__); + break; + case LS_MSG_EVENT_SYNC: + zlog_debug( + "%s: [rcv ted] TED received Attributes message syn", + __func__); + break; + case LS_MSG_EVENT_UNDEF: + zlog_debug( + "%s: [rcv ted] TED received Attributes message und", + __func__); + default: + break; + } + break; + + case LS_MSG_TYPE_PREFIX: + ls_msg2subnet(ted_state_g.ted, msg, true /*hard delete*/); + switch (msg->event) { + case LS_MSG_EVENT_ADD: + zlog_debug( + "%s: [rcv ted] TED received Prefix message add", + __func__); + break; + case LS_MSG_EVENT_DELETE: + zlog_debug( + "%s: [rcv ted] TED received Prefix message del", + __func__); + break; + case LS_MSG_EVENT_UPDATE: + zlog_debug( + "%s: [rcv ted] TED received Prefix message upd", + __func__); + break; + case LS_MSG_EVENT_SYNC: + zlog_debug( + "%s: [rcv ted] TED received Prefix message syn", + __func__); + break; + case LS_MSG_EVENT_UNDEF: + zlog_debug( + "%s: [rcv ted] TED received Prefix message und", + __func__); + default: + break; + } + break; + + default: + zlog_debug( + "%s: [rcv ted] TED received unknown message type [%d]", + __func__, msg->type); + break; + } + return 0; +} + +uint32_t path_ted_query_type_f(struct ipaddr *local, struct ipaddr *remote) +{ + uint32_t sid = MPLS_LABEL_NONE; + struct ls_edge *edge; + + if (!path_ted_is_initialized()) + return MPLS_LABEL_NONE; + + switch (local->ipa_type) { + case IPADDR_V4: + /* We have local and remote ip */ + /* so check all attributes in ted */ + frr_each (edges, &ted_state_g.ted->edges, edge) { + if (edge->attributes->standard.local.s_addr + == local->ip._v4_addr.s_addr + && edge->attributes->standard.remote.s_addr + == remote->ip._v4_addr.s_addr + && CHECK_FLAG(edge->attributes->flags, + LS_ATTR_ADJ_SID)) { + sid = edge->attributes->adj_sid[0] + .sid; /* from primary */ + break; + } + } + break; + case IPADDR_V6: + frr_each (edges, &ted_state_g.ted->edges, edge) { + if (memcmp(&edge->attributes->standard.local6, + &local->ip._v6_addr, + sizeof(local->ip._v6_addr)) + && (memcmp(&edge->attributes->standard.remote6, + &remote->ip._v6_addr, + sizeof(remote->ip._v6_addr)) + && CHECK_FLAG(edge->attributes->flags, + LS_ATTR_ADJ_SID))) { + sid = edge->attributes->adj_sid[0] + .sid; /* from primary */ + break; + } + } + break; + case IPADDR_NONE: + break; + } + + return sid; +} + +uint32_t path_ted_query_type_c(struct prefix *prefix, uint8_t algo) +{ + uint32_t sid = MPLS_LABEL_NONE; + struct ls_subnet *subnet; + + if (!path_ted_is_initialized()) + return MPLS_LABEL_NONE; + + switch (prefix->family) { + case AF_INET: + case AF_INET6: + frr_each (subnets, &ted_state_g.ted->subnets, subnet) { + if (prefix_same(&subnet->ls_pref->pref, prefix) + && (CHECK_FLAG(subnet->ls_pref->flags, LS_PREF_SR)) + && (subnet->ls_pref->sr.algo == algo)) + sid = subnet->ls_pref->sr.sid; + } + break; + default: + break; + } + + return sid; +} + +uint32_t path_ted_query_type_e(struct prefix *prefix, uint32_t iface_id) +{ + uint32_t sid = MPLS_LABEL_NONE; + struct ls_subnet *subnet; + struct listnode *lst_node; + struct ls_edge *edge; + + if (!path_ted_is_initialized()) + return MPLS_LABEL_NONE; + + switch (prefix->family) { + case AF_INET: + case AF_INET6: + frr_each (subnets, &ted_state_g.ted->subnets, subnet) { + if (prefix_same(&subnet->ls_pref->pref, prefix)) { + /* from the vertex linked in subnet */ + /* loop over outgoing edges */ + for (ALL_LIST_ELEMENTS_RO( + subnet->vertex->outgoing_edges, + lst_node, edge)) { + /* and look for ifaceid */ + /* so get sid of attribute */ + if (CHECK_FLAG(edge->attributes->flags, + LS_ATTR_LOCAL_ID) + && edge->attributes->standard + .local_id + == iface_id) { + sid = subnet->ls_pref->sr.sid; + break; + } + } + } + } + break; + default: + break; + } + + return sid; +} + +/* + * Followings are vty command functions. + */ +/* clang-format off */ +DEFUN (path_ted_on, + path_ted_on_cmd, + "mpls-te on", + NO_STR + "Enable the TE database (TED) functionality\n") +/* clang-format on */ +{ + + if (ted_state_g.enabled) { + zlog_debug("%s: PATHD-TED: Enabled ON -> ON.", __func__); + return CMD_SUCCESS; + } + + ted_state_g.ted = path_ted_create_ted(); + ted_state_g.enabled = true; + zlog_debug("%s: PATHD-TED: Enabled OFF -> ON.", __func__); + + return CMD_SUCCESS; +} + +/* clang-format off */ +DEFUN (no_path_ted, + no_path_ted_cmd, + "no mpls-te [on]", + NO_STR + NO_STR + "Disable the TE Database functionality\n") +/* clang-format on */ +{ + if (ted_state_g.enabled) { + zlog_debug("%s: PATHD-TED: OFF -> OFF", __func__); + return CMD_SUCCESS; + } + + /* Remove TED */ + ls_ted_del_all(ted_state_g.ted); + ted_state_g.enabled = false; + zlog_debug("%s: PATHD-TED: ON -> OFF", __func__); + ted_state_g.import = IMPORT_UNKNOWN; + if (ls_unregister(zclient, false /*client*/) != 0) { + vty_out(vty, "Unable to unregister Link State\n"); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +/* clang-format off */ +DEFPY(path_ted_import, + path_ted_import_cmd, + "mpls-te import $import_daemon", + "Enable the TE database (TED) fill with remote igp data\n" + "import\n" + "Origin ospfv2\n" + "Origin ospfv3\n" + "Origin isis\n") +/* clang-format on */ +{ + + if (ted_state_g.enabled) + if (path_ted_start_importing_igp(import_daemon)) { + vty_out(vty, "Unable to start importing\n"); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +/* clang-format off */ +DEFUN (no_path_ted_import, + no_path_ted_import_cmd, + "no mpls-te import", + NO_STR + NO_STR + "Disable the TE Database fill with remote igp data\n") +/* clang-format on */ +{ + + if (ted_state_g.import) { + if (path_ted_stop_importing_igp()) { + vty_out(vty, "Unable to stop importing\n"); + return CMD_WARNING; + } else { + zlog_debug( + "%s: PATHD-TED: Importing igp data already OFF", + __func__); + } + } + return CMD_SUCCESS; +} + +/* clang-format off */ +DEFPY (show_pahtd_ted_db, + show_pathd_ted_db_cmd, + "show pathd ted database $ver_json ", + "show command\n" + "pathd daemon\n" + "traffic eng\n" + "database\n" + "verbose output\n" + "Show complete received TED database\n") +/* clang-format on */ +{ + bool st_json = false; + json_object *json = NULL; + + if (!ted_state_g.enabled) { + vty_out(vty, "PATHD TED database is not enabled\n"); + return CMD_WARNING; + } + if (strcmp(ver_json, "json") == 0) { + st_json = true; + json = json_object_new_object(); + } + /* Show the complete TED */ + ls_show_ted(ted_state_g.ted, vty, json, !st_json); + if (st_json) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return CMD_SUCCESS; +} + +/** + * Help fn to show ted related configuration + * + * @param vty + * + * @return Status + */ +uint32_t path_ted_config_write(struct vty *vty) +{ + + if (ted_state_g.enabled) { + vty_out(vty, " mpls-te on\n"); + switch (ted_state_g.import) { + case IMPORT_ISIS: + vty_out(vty, " mpls-te import isis\n"); + break; + case IMPORT_OSPFv2: + vty_out(vty, " mpls-te import ospfv2\n"); + break; + case IMPORT_OSPFv3: + vty_out(vty, " mpls-te import ospfv3\n"); + break; + default: + break; + } + } + return 0; +} + +/** + * Register the fn's for CLI and hook for config show + * + * @param void + * + */ +static void path_ted_register_vty(void) +{ + install_element(VIEW_NODE, &show_pathd_ted_db_cmd); + install_element(SR_TRAFFIC_ENG_NODE, &path_ted_on_cmd); + install_element(SR_TRAFFIC_ENG_NODE, &no_path_ted_cmd); + install_element(SR_TRAFFIC_ENG_NODE, &path_ted_import_cmd); + install_element(SR_TRAFFIC_ENG_NODE, &no_path_ted_import_cmd); +} + +/** + * UnRegister the fn's for CLI and hook for config show + * + * @param void + * + */ +static void path_ted_unregister_vty(void) +{ + uninstall_element(VIEW_NODE, &show_pathd_ted_db_cmd); + uninstall_element(SR_TRAFFIC_ENG_NODE, &path_ted_on_cmd); + uninstall_element(SR_TRAFFIC_ENG_NODE, &no_path_ted_cmd); + uninstall_element(SR_TRAFFIC_ENG_NODE, &path_ted_import_cmd); + uninstall_element(SR_TRAFFIC_ENG_NODE, &no_path_ted_import_cmd); +} + +/** + * Ask igp for a complete TED so far + * + * @param void + * + * @return zclient status + */ +enum zclient_send_status path_ted_link_state_sync(void) +{ + enum zclient_send_status status; + + ls_request_sync(zclient); + status = ls_request_sync(zclient); + if (status == -1) { + zlog_err("%s: PATHD-TED: Opaque error asking for TED sync ", + __func__); + return status; + } else { + zlog_debug("%s: PATHD-TED: Opaque asked for TED sync ", + __func__); + } + thread_add_timer(ted_state_g.main, path_ted_timer_handler_sync, + &ted_state_g, ted_state_g.link_state_delay_interval, + &ted_state_g.t_link_state_sync); + + return status; +} + +/** + * Timer cb for check link state sync + * + * @param thread Current thread + * + * @return status + */ +int path_ted_timer_handler_sync(struct thread *thread) +{ + /* data unpacking */ + struct ted_state *data = THREAD_ARG(thread); + + assert(data != NULL); + /* Retry the sync */ + return path_ted_link_state_sync(); +} + +/** + * refresg segment list and create timer to keep up updated + * + * @param void + * + * @return status + */ +int path_ted_segment_list_refresh(void) +{ + int status = 0; + + path_ted_timer_refresh_cancel(); + thread_add_timer(ted_state_g.main, path_ted_timer_handler_refresh, + &ted_state_g, + ted_state_g.segment_list_refresh_interval, + &ted_state_g.t_segment_list_refresh); + + return status; +} + +/** + * Timer cb for refreshing sid in segment lists + * + * @param void + * + * @return status + */ +int path_ted_timer_handler_refresh(struct thread *thread) +{ + if (!path_ted_is_initialized()) + return MPLS_LABEL_NONE; + + zlog_debug("%s: PATHD-TED: Refresh sid from current TED", __func__); + /* data unpacking */ + struct ted_state *data = THREAD_ARG(thread); + + assert(data != NULL); + + srte_policy_update_ted_sid(); + return 0; +} + +/** + * Cancel sync timer + * + * @param void + * + * @return void status + */ +void path_ted_timer_sync_cancel(void) +{ + if (ted_state_g.t_link_state_sync != NULL) { + thread_cancel(&ted_state_g.t_link_state_sync); + ted_state_g.t_link_state_sync = NULL; + } +} + +/** + * Cancel refresh timer + * + * @param void + * + * @return void status + */ +void path_ted_timer_refresh_cancel(void) +{ + if (ted_state_g.t_segment_list_refresh != NULL) { + thread_cancel(&ted_state_g.t_segment_list_refresh); + ted_state_g.t_segment_list_refresh = NULL; + } +} + +/** + * Check which igp is configured + * + * @param igp who want to check against config- + * + * @return status + */ +uint32_t path_ted_get_current_igp(uint32_t igp) +{ + switch (igp) { + case ISIS_L1: + case ISIS_L2: + if (ted_state_g.import != IMPORT_ISIS) { + zlog_err( + "%s: [rcv ted] Incorrect igp origin wait (%s) got (%s) ", + __func__, + PATH_TED_IGP_PRINT(ted_state_g.import), + LS_IGP_PRINT(igp)); + return 1; + } + break; + case OSPFv2: + if (ted_state_g.import != IMPORT_OSPFv2) { + zlog_err( + "%s: [rcv ted] Incorrect igp origin wait (%s) got (%s) ", + __func__, + PATH_TED_IGP_PRINT(ted_state_g.import), + LS_IGP_PRINT(igp)); + return 1; + } + break; + case STATIC: + break; + } + return 0; +} diff --git a/pathd/path_ted.h b/pathd/path_ted.h new file mode 100644 index 000000000000..76858959d6d3 --- /dev/null +++ b/pathd/path_ted.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2020 Volta Networks, Inc + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#ifndef _PATH_TED_H +#define _PATH_TED_H + +#ifdef __cplusplus + +extern "C" { +#endif + +#include + +#include + +#include "linklist.h" +#include "log.h" +#include "command.h" +#include "stream.h" +#include "prefix.h" +#include "zclient.h" +#include "link_state.h" + +/* TED management functions */ +bool path_ted_is_initialized(void); +void path_ted_init(struct thread_master *master); +uint32_t path_ted_teardown(void); +void path_ted_timer_sync_cancel(void); +void path_ted_timer_refresh_cancel(void); +int path_ted_segment_list_refresh(void); + +/* TED configuration functions */ +uint32_t path_ted_config_write(struct vty *vty); + +/* TED util functions */ +/* clang-format off */ +#define LS_MSG_EVENT_PRINT(event) event == LS_MSG_EVENT_ADD?"add"\ + : event == LS_MSG_EVENT_DELETE?"del"\ + : event == LS_MSG_EVENT_UPDATE?"upd"\ + : event == LS_MSG_EVENT_SYNC?"syn"\ + : event == LS_MSG_EVENT_SYNC?"und" : "none" +#define LS_MSG_TYPE_PRINT(type) type == LS_MSG_TYPE_NODE?"node"\ + : type == LS_MSG_TYPE_ATTRIBUTES?"att"\ + : type == LS_MSG_TYPE_PREFIX?"pre" : "none" +#define LS_IGP_PRINT(type) type == ISIS_L1?"ISIS_L1"\ + : type == ISIS_L2?"ISIS_L2"\ + : type == DIRECT?"DIRECT"\ + : type == STATIC?"STATIC"\ + : type == OSPFv2?"OSPFv2" : "none" +#define PATH_TED_IGP_PRINT(type) type == IMPORT_OSPFv2?"OSPFv2"\ + : type == IMPORT_OSPFv3?"OSPFv3"\ + : type == IMPORT_ISIS?"ISIS" : "none" +/* clang-format on */ + + +uint32_t path_ted_get_current_igp(uint32_t); +/* TED Query functions */ + +/* + * Type of queries from draft-ietf-spring-segment-routing-policy-07 for types + * f,c,e + */ + +/** + * Search for sid based in prefix and optional algo + * + * @param prefix Net prefix to resolv + * @param algo Algorithm for link state + * + * @return sid of attribute + */ +uint32_t path_ted_query_type_c(struct prefix *prefix, uint8_t algo); + +/** + * Search for sid based in prefix and interface id + * + * @param prefix Net prefix to resolv + * @param iface_id The interface id + * + * @return sid of attribute + */ +uint32_t path_ted_query_type_e(struct prefix *prefix, uint32_t iface_id); + +/** + * Search for sid based in local, remote pair + * + * @param local local ip of attribute + * @param remote remote ip of attribute + * + * @return sid of attribute + */ +uint32_t path_ted_query_type_f(struct ipaddr *local, struct ipaddr *remote); + + +/** + * Handle the received opaque msg + * + * @param msg Holds the ted data + * + * @return sid of attribute + */ +uint32_t path_ted_rcvd_message(struct ls_message *msg); + +#ifdef __cplusplus +} +#endif + +#endif /* _PATH_TED_H */ diff --git a/pathd/path_zebra.c b/pathd/path_zebra.c index 276bc9289c4d..c557b24848aa 100644 --- a/pathd/path_zebra.c +++ b/pathd/path_zebra.c @@ -30,9 +30,14 @@ #include "typesafe.h" #include "pathd/pathd.h" +#include "pathd/path_ted.h" #include "pathd/path_zebra.h" +#include "lib/command.h" +#include "lib/link_state.h" -static struct zclient *zclient; +static int path_zebra_opaque_msg_handler(ZAPI_CALLBACK_ARGS); + +struct zclient *zclient; static struct zclient *zclient_sync; /* Global Variables */ @@ -81,6 +86,13 @@ bool get_ipv6_router_id(struct in6_addr *router_id) return retval; } +void path_zebra_destroy(void) +{ + zclient_stop(zclient); + zclient_free(zclient); + zclient = NULL; +} + static void path_zebra_connected(struct zclient *zclient) { struct srte_policy *policy; @@ -265,6 +277,54 @@ static void path_zebra_label_manager_connect(void) } } +static int path_zebra_opaque_msg_handler(ZAPI_CALLBACK_ARGS) +{ + int ret = 0; + struct stream *s; + struct zapi_opaque_msg info; + + s = zclient->ibuf; + + if (zclient_opaque_decode(s, &info) != 0) + return -1; + + switch (info.type) { + case LINK_STATE_UPDATE: + case LINK_STATE_SYNC: + /* Start receiving ls data so cancel request sync timer */ + path_ted_timer_sync_cancel(); + + struct ls_message *msg = ls_parse_msg(s); + + if (msg) { + zlog_debug("%s: [rcv ted] ls (%s) msg (%s)-(%s) !", + __func__, + info.type == LINK_STATE_UPDATE + ? "LINK_STATE_UPDATE" + : "LINK_STATE_SYNC", + LS_MSG_TYPE_PRINT(msg->type), + LS_MSG_EVENT_PRINT(msg->event)); + } else { + zlog_err( + "%s: [rcv ted] Could not parse LinkState stream message.", + __func__); + return -1; + } + + ret = path_ted_rcvd_message(msg); + ls_delete_msg(msg); + /* Update local configuration after process update. */ + path_ted_segment_list_refresh(); + break; + default: + zlog_debug("%s: [rcv ted] unknown opaque event (%d) !", + __func__, info.type); + break; + } + + return ret; +} + /** * Initializes Zebra asynchronous connection. * @@ -281,6 +341,7 @@ void path_zebra_init(struct thread_master *master) zclient->zebra_connected = path_zebra_connected; zclient->sr_policy_notify_status = path_zebra_sr_policy_notify_status; zclient->router_id_update = path_zebra_router_id_update; + zclient->opaque_msg_handler = path_zebra_opaque_msg_handler; /* Initialize special zclient for synchronous message exchanges. */ zclient_sync = zclient_new(master, &options); diff --git a/pathd/path_zebra.h b/pathd/path_zebra.h index 42a7123dd464..e7461a9eeec4 100644 --- a/pathd/path_zebra.h +++ b/pathd/path_zebra.h @@ -30,5 +30,6 @@ void path_zebra_delete_sr_policy(struct srte_policy *policy); int path_zebra_request_label(mpls_label_t label); void path_zebra_release_label(mpls_label_t label); void path_zebra_init(struct thread_master *master); +void path_zebra_destroy(void); #endif /* _FRR_PATH_MPLS_H_ */ diff --git a/pathd/pathd.c b/pathd/pathd.c index ae8218631584..c03ad9aea45c 100644 --- a/pathd/pathd.c +++ b/pathd/pathd.c @@ -26,6 +26,7 @@ #include "pathd/pathd.h" #include "pathd/path_zebra.h" #include "pathd/path_debug.h" +#include "pathd/path_ted.h" #define HOOK_DELAY 3 @@ -188,11 +189,14 @@ void srte_segment_entry_del(struct srte_segment_entry *segment) * @param type The remote address of the adjacency * @param type The remote interface index of the unumbered adjacency */ -void srte_segment_entry_set_nai(struct srte_segment_entry *segment, - enum srte_segment_nai_type type, - struct ipaddr *local_ip, uint32_t local_iface, - struct ipaddr *remote_ip, uint32_t remote_iface) +int srte_segment_entry_set_nai(struct srte_segment_entry *segment, + enum srte_segment_nai_type type, + struct ipaddr *local_ip, uint32_t local_iface, + struct ipaddr *remote_ip, uint32_t remote_iface, + uint8_t algo, uint8_t pref_len) { + int32_t status = 0; + struct prefix pre = {0}; segment->nai_type = type; memcpy(&segment->nai_local_addr, local_ip, sizeof(struct ipaddr)); @@ -204,6 +208,7 @@ void srte_segment_entry_set_nai(struct srte_segment_entry *segment, case SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY: memcpy(&segment->nai_remote_addr, remote_ip, sizeof(struct ipaddr)); + status = srte_ted_do_query_type_f(segment, local_ip, remote_ip); break; case SRTE_SEGMENT_NAI_TYPE_IPV4_UNNUMBERED_ADJACENCY: memcpy(&segment->nai_remote_addr, remote_ip, @@ -211,12 +216,65 @@ void srte_segment_entry_set_nai(struct srte_segment_entry *segment, segment->nai_local_iface = local_iface; segment->nai_remote_iface = remote_iface; break; + case SRTE_SEGMENT_NAI_TYPE_IPV6_ALGORITHM: + pre.family = AF_INET6; + pre.prefixlen = pref_len; + pre.u.prefix6 = local_ip->ip._v6_addr; + segment->nai_local_prefix_len = pref_len; + segment->nai_algorithm = algo; + status = srte_ted_do_query_type_c(segment, &pre, algo); + break; + case SRTE_SEGMENT_NAI_TYPE_IPV4_ALGORITHM: + pre.family = AF_INET; + pre.prefixlen = pref_len; + pre.u.prefix4 = local_ip->ip._v4_addr; + segment->nai_local_prefix_len = pref_len; + segment->nai_algorithm = algo; + status = srte_ted_do_query_type_c(segment, &pre, algo); + break; + case SRTE_SEGMENT_NAI_TYPE_IPV6_LOCAL_IFACE: + pre.family = AF_INET6; + pre.prefixlen = pref_len; + pre.u.prefix6 = local_ip->ip._v6_addr; + segment->nai_local_prefix_len = pref_len; + segment->nai_local_iface = local_iface; + status = srte_ted_do_query_type_e(segment, &pre, local_iface); + break; + case SRTE_SEGMENT_NAI_TYPE_IPV4_LOCAL_IFACE: + pre.family = AF_INET; + pre.prefixlen = pref_len; + pre.u.prefix4 = local_ip->ip._v4_addr; + segment->nai_local_prefix_len = pref_len; + segment->nai_local_iface = local_iface; + status = srte_ted_do_query_type_e(segment, &pre, local_iface); + break; default: segment->nai_local_addr.ipa_type = IPADDR_NONE; segment->nai_local_iface = 0; segment->nai_remote_addr.ipa_type = IPADDR_NONE; segment->nai_remote_iface = 0; } + return status; +} + +/** + * Mark segment as modified depending in protocol and sid conditions + * + * @param protocol_origin Origin of the segment list + * @param s_list Ptr to segment list with flags,sid to modidy + * @param s_entry Ptr to segment entry with sid to modidy + * @param ted_sid The sid from ted query + * @return void + */ +void srte_segment_set_local_modification(struct srte_segment_list *s_list, + struct srte_segment_entry *s_entry, + uint32_t ted_sid) +{ + if (s_list->protocol_origin == SRTE_ORIGIN_LOCAL + && s_entry->sid_value != ted_sid) { + s_entry->sid_value = ted_sid; + SET_FLAG(s_list->flags, F_SEGMENT_LIST_MODIFIED); + } } /** @@ -287,6 +345,105 @@ struct srte_policy *srte_policy_find(uint32_t color, struct ipaddr *endpoint) return RB_FIND(srte_policy_head, &srte_policies, &search); } +/* + * After new data from igp,local and pce the segment list : + * Mark as invalid for origin pce if cannot be validated + * Updated for origin local + */ +int srte_policy_update_ted_sid(void) +{ + + int number_of_sid_clashed = 0; + struct srte_segment_list *s_list; + struct srte_segment_entry *s_entry; + + if (!path_ted_is_initialized()) + return 0; + if (RB_EMPTY(srte_segment_list_head, &srte_segment_lists)) + return 0; + + RB_FOREACH (s_list, srte_segment_list_head, &srte_segment_lists) { + if (CHECK_FLAG(s_list->flags, F_SEGMENT_LIST_DELETED)) + continue; + RB_FOREACH (s_entry, srte_segment_entry_head, + &s_list->segments) { + zlog_debug( + "%s:PATHD-TED: SL: Name: %s index:(%d) sid:(%d) prefix_len:(%d) local iface:(%d) algorithm:(%d)", + __func__, s_list->name, s_entry->index, + s_entry->sid_value, + s_entry->nai_local_prefix_len, + s_entry->nai_local_iface, + s_entry->nai_algorithm); + struct prefix prefix_cli = {0}; + + switch (s_entry->nai_type) { + case SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY: + case SRTE_SEGMENT_NAI_TYPE_IPV4_ADJACENCY: + number_of_sid_clashed += + srte_ted_do_query_type_f( + s_entry, + &s_entry->nai_local_addr, + &s_entry->nai_remote_addr); + break; + case SRTE_SEGMENT_NAI_TYPE_IPV6_LOCAL_IFACE: + prefix_cli.family = AF_INET6; + prefix_cli.prefixlen = + s_entry->nai_local_prefix_len; + prefix_cli.u.prefix6 = + s_entry->nai_local_addr.ip._v6_addr; + number_of_sid_clashed += + srte_ted_do_query_type_e( + s_entry, &prefix_cli, + s_entry->nai_local_iface); + break; + case SRTE_SEGMENT_NAI_TYPE_IPV4_LOCAL_IFACE: + prefix_cli.family = AF_INET; + prefix_cli.prefixlen = + s_entry->nai_local_prefix_len; + prefix_cli.u.prefix4 = + s_entry->nai_local_addr.ip._v4_addr; + number_of_sid_clashed += + srte_ted_do_query_type_e( + s_entry, &prefix_cli, + s_entry->nai_local_iface); + break; + case SRTE_SEGMENT_NAI_TYPE_IPV6_ALGORITHM: + prefix_cli.family = AF_INET6; + prefix_cli.prefixlen = + s_entry->nai_local_prefix_len; + prefix_cli.u.prefix6 = + s_entry->nai_local_addr.ip._v6_addr; + number_of_sid_clashed += + srte_ted_do_query_type_c( + s_entry, &prefix_cli, + s_entry->nai_algorithm); + break; + case SRTE_SEGMENT_NAI_TYPE_IPV4_ALGORITHM: + prefix_cli.family = AF_INET; + prefix_cli.prefixlen = + s_entry->nai_local_prefix_len; + prefix_cli.u.prefix4 = + s_entry->nai_local_addr.ip._v4_addr; + number_of_sid_clashed += + srte_ted_do_query_type_c( + s_entry, &prefix_cli, + s_entry->nai_algorithm); + break; + default: + break; + } + } + if (number_of_sid_clashed) { + SET_FLAG(s_list->flags, F_SEGMENT_LIST_SID_CONFLICT); + number_of_sid_clashed = 0; + } else + UNSET_FLAG(s_list->flags, F_SEGMENT_LIST_SID_CONFLICT); + } + srte_apply_changes(); + + return 0; +} + /** * Update a policy binding SID. * @@ -322,13 +479,23 @@ srte_policy_best_candidate(const struct srte_policy *policy) &policy->candidate_paths) { /* search for highest preference with existing segment list */ if (!CHECK_FLAG(candidate->flags, F_CANDIDATE_DELETED) - && candidate->lsp->segment_list) + && candidate->lsp->segment_list + && (!CHECK_FLAG(candidate->lsp->segment_list->flags, + F_SEGMENT_LIST_SID_CONFLICT))) return candidate; } return NULL; } +void srte_clean_zebra(void) +{ + struct srte_policy *policy, *safe_pol; + + RB_FOREACH_SAFE (policy, srte_policy_head, &srte_policies, safe_pol) + srte_policy_del(policy); +} + /** * Apply changes defined by setting the policies, candidate paths * and segment lists modification flags NEW, MODIFIED and DELETED. @@ -1024,6 +1191,12 @@ const char *srte_origin2str(enum srte_protocol_origin origin) } } +void pathd_shutdown(void) +{ + path_ted_teardown(); + srte_clean_zebra(); +} + void trigger_pathd_candidate_created(struct srte_candidate *candidate) { /* The hook is called asynchronously to let the PCEP module @@ -1124,3 +1297,79 @@ const char *srte_candidate_metric_name(enum srte_candidate_metric_type type) return "UNKNOWN"; } } + +int32_t srte_ted_do_query_type_c(struct srte_segment_entry *entry, + struct prefix *prefix_cli, uint32_t algo) +{ + int32_t status = 0; + uint32_t ted_sid = MPLS_LABEL_NONE; + + if (!path_ted_is_initialized()) + return 0; + ted_sid = path_ted_query_type_c(prefix_cli, algo); + if (ted_sid == MPLS_LABEL_NONE) { + zlog_warn(" %s: PATHD-TED: SL: ERROR query C : ted-sid (%d)", + __func__, ted_sid); + } else { + zlog_debug("%s: PATHD-TED: SL: Sucess query C : ted-sid (%d)", + __func__, ted_sid); + } + if (CHECK_SID(entry->segment_list->protocol_origin, ted_sid, + entry->sid_value)) { + status = PATH_SID_ERROR; + } else + srte_segment_set_local_modification(entry->segment_list, entry, + ted_sid); + return status; +} + +int32_t srte_ted_do_query_type_e(struct srte_segment_entry *entry, + struct prefix *prefix_cli, + uint32_t local_iface) +{ + int32_t status = 0; + uint32_t ted_sid = MPLS_LABEL_NONE; + + if (!path_ted_is_initialized()) + return 0; + ted_sid = path_ted_query_type_e(prefix_cli, local_iface); + if (ted_sid == MPLS_LABEL_NONE) { + zlog_warn(" %s: PATHD-TED: SL: ERROR query E : ted-sid (%d)", + __func__, ted_sid); + } else { + zlog_debug("%s: PATHD-TED: SL: Sucess query E : ted-sid (%d)", + __func__, ted_sid); + } + if (CHECK_SID(entry->segment_list->protocol_origin, ted_sid, + entry->sid_value)) { + status = PATH_SID_ERROR; + } else + srte_segment_set_local_modification(entry->segment_list, entry, + ted_sid); + return status; +} + +int32_t srte_ted_do_query_type_f(struct srte_segment_entry *entry, + struct ipaddr *local, struct ipaddr *remote) +{ + int32_t status = 0; + uint32_t ted_sid = MPLS_LABEL_NONE; + + if (!path_ted_is_initialized()) + return status; + ted_sid = path_ted_query_type_f(local, remote); + if (ted_sid == MPLS_LABEL_NONE) { + zlog_warn("%s:SL: ERROR query F : ted-sid (%d)", __func__, + ted_sid); + } else { + zlog_debug("%s:SL: Sucess query F : ted-sid (%d)", __func__, + ted_sid); + } + if (CHECK_SID(entry->segment_list->protocol_origin, ted_sid, + entry->sid_value)) { + status = PATH_SID_ERROR; + } else + srte_segment_set_local_modification(entry->segment_list, entry, + ted_sid); + return status; +} diff --git a/pathd/pathd.h b/pathd/pathd.h index 9c4d256ceff2..7d38272e856d 100644 --- a/pathd/pathd.h +++ b/pathd/pathd.h @@ -24,6 +24,13 @@ #include "lib/ipaddr.h" #include "lib/srte.h" #include "lib/hook.h" +#include "lib/prefix.h" + +#define PATH_SID_ERROR 1 +#define PATH_SID_NO_ERROR 0 +#define CHECK_SID(or, ts, es) \ + ((or == SRTE_ORIGIN_PCEP && (ts == MPLS_LABEL_NONE || es != ts)) \ + || (or == SRTE_ORIGIN_LOCAL && ts == MPLS_LABEL_NONE)) DECLARE_MGROUP(PATHD); @@ -100,7 +107,12 @@ enum srte_segment_nai_type { SRTE_SEGMENT_NAI_TYPE_IPV6_NODE = 2, SRTE_SEGMENT_NAI_TYPE_IPV4_ADJACENCY = 3, SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY = 4, - SRTE_SEGMENT_NAI_TYPE_IPV4_UNNUMBERED_ADJACENCY = 5 + SRTE_SEGMENT_NAI_TYPE_IPV4_UNNUMBERED_ADJACENCY = 5, + SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY_LINK_LOCAL_ADDRESSES = 6, + SRTE_SEGMENT_NAI_TYPE_IPV4_LOCAL_IFACE = 7, + SRTE_SEGMENT_NAI_TYPE_IPV6_LOCAL_IFACE = 8, + SRTE_SEGMENT_NAI_TYPE_IPV4_ALGORITHM = 9, + SRTE_SEGMENT_NAI_TYPE_IPV6_ALGORITHM = 10 }; enum objfun_type { @@ -175,6 +187,9 @@ struct srte_segment_entry { /* NAI remote interface when nai type is not IPv4 unnumbered adjacency */ uint32_t nai_remote_iface; + /* Support draft-ietf-spring-segment-routing-policy sl types queries*/ + uint8_t nai_local_prefix_len; + uint8_t nai_algorithm; }; RB_HEAD(srte_segment_entry_head, srte_segment_entry); RB_PROTOTYPE(srte_segment_entry_head, srte_segment_entry, entry, @@ -200,6 +215,7 @@ struct srte_segment_list { #define F_SEGMENT_LIST_NEW 0x0002 #define F_SEGMENT_LIST_MODIFIED 0x0004 #define F_SEGMENT_LIST_DELETED 0x0008 +#define F_SEGMENT_LIST_SID_CONFLICT 0x0010 }; RB_HEAD(srte_segment_list_head, srte_segment_list); RB_PROTOTYPE(srte_segment_list_head, srte_segment_list, entry, @@ -361,17 +377,22 @@ struct srte_segment_list *srte_segment_list_find(const char *name); struct srte_segment_entry * srte_segment_entry_add(struct srte_segment_list *segment_list, uint32_t index); void srte_segment_entry_del(struct srte_segment_entry *segment); -void srte_segment_entry_set_nai(struct srte_segment_entry *segment, - enum srte_segment_nai_type type, - struct ipaddr *local_ip, uint32_t local_iface, - struct ipaddr *remote_ip, - uint32_t remote_iface); +int srte_segment_entry_set_nai(struct srte_segment_entry *segment, + enum srte_segment_nai_type type, + struct ipaddr *local_ip, uint32_t local_iface, + struct ipaddr *remote_ip, uint32_t remote_iface, + uint8_t algo, uint8_t pref_len); +void srte_segment_set_local_modification(struct srte_segment_list *s_list, + struct srte_segment_entry *s_entry, + uint32_t ted_sid); struct srte_policy *srte_policy_add(uint32_t color, struct ipaddr *endpoint); void srte_policy_del(struct srte_policy *policy); struct srte_policy *srte_policy_find(uint32_t color, struct ipaddr *endpoint); +int srte_policy_update_ted_sid(void); void srte_policy_update_binding_sid(struct srte_policy *policy, uint32_t binding_sid); void srte_apply_changes(void); +void srte_clean_zebra(void); void srte_policy_apply_changes(struct srte_policy *policy); struct srte_candidate *srte_candidate_add(struct srte_policy *policy, uint32_t preference); @@ -408,8 +429,45 @@ srte_segment_entry_find(struct srte_segment_list *segment_list, uint32_t index); void srte_candidate_status_update(struct srte_candidate *candidate, int status); void srte_candidate_unset_segment_list(const char *originator, bool force); const char *srte_origin2str(enum srte_protocol_origin origin); +void pathd_shutdown(void); /* path_cli.c */ void path_cli_init(void); + +/** + * Search for sid based in prefix and algorithm + * + * @param Prefix The prefix to use + * @param algo Algorithm we want to query for + * @param ted_sid Sid to query + * + * @return void + */ +int32_t srte_ted_do_query_type_c(struct srte_segment_entry *entry, + struct prefix *prefix_cli, uint32_t algo); + +/** + * Search for sid based in prefix and interface id + * + * @param Prefix The prefix to use + * @param local_iface The id of interface + * @param ted_sid Sid to query + * + * @return void + */ +int32_t srte_ted_do_query_type_e(struct srte_segment_entry *entry, + struct prefix *prefix_cli, + uint32_t local_iface); +/** + * Search for sid based in local and remote ip + * + * @param entry entry to update + * @param local Local addr for query + * @param remote Local addr for query + * + * @return void + */ +int32_t srte_ted_do_query_type_f(struct srte_segment_entry *entry, + struct ipaddr *local, struct ipaddr *remote); #endif /* _FRR_PATHD_H_ */ diff --git a/pathd/subdir.am b/pathd/subdir.am index 4eabdd2ac54f..bc5391c98728 100644 --- a/pathd/subdir.am +++ b/pathd/subdir.am @@ -5,7 +5,10 @@ if PATHD noinst_LIBRARIES += pathd/libpath.a sbin_PROGRAMS += pathd/pathd -vtysh_scan += pathd/path_cli.c +vtysh_scan += \ + pathd/path_cli.c \ + pathd/path_ted.c \ + #end vtysh_daemons += pathd # TODO add man page #man8 += $(MANBUILD)/pathd.8 @@ -24,6 +27,7 @@ pathd_libpath_a_SOURCES = \ pathd/path_nb.c \ pathd/path_nb_config.c \ pathd/path_nb_state.c \ + pathd/path_ted.c \ pathd/path_zebra.c \ pathd/pathd.c \ # end @@ -31,6 +35,7 @@ pathd_libpath_a_SOURCES = \ clippy_scan += \ pathd/path_cli.c \ pathd/path_pcep_cli.c \ + pathd/path_ted.c \ # end noinst_HEADERS += \ @@ -44,6 +49,7 @@ noinst_HEADERS += \ pathd/path_pcep_lib.h \ pathd/path_pcep_config.h \ pathd/path_pcep_pcc.h \ + pathd/path_ted.h \ pathd/path_zebra.h \ pathd/pathd.h \ # end diff --git a/pceplib/pcep_msg_objects.h b/pceplib/pcep_msg_objects.h index 959a6f8cf6cb..e2036756099b 100644 --- a/pceplib/pcep_msg_objects.h +++ b/pceplib/pcep_msg_objects.h @@ -548,6 +548,12 @@ enum pcep_sr_subobj_nai { PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY = 4, PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY = 5, PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY = 6, + /* Support for explicit segment*/ + /* listdraft-ietf-spring-segment-routing-policy-07*/ + PCEP_SR_SUBOBJ_NAI_IPV4_LOCAL_IFACE = 7, + PCEP_SR_SUBOBJ_NAI_IPV6_LOCAL_IFACE = 8, + PCEP_SR_SUBOBJ_NAI_IPV4_ALGORITHM = 9, + PCEP_SR_SUBOBJ_NAI_IPV6_ALGORITHM = 10, PCEP_SR_SUBOBJ_NAI_UNKNOWN }; @@ -564,8 +570,8 @@ struct pcep_ro_subobj_sr { bool flag_c; bool flag_m; - /* The SID and NAI are optional depending on the flags, - * and the NAI can be variable length */ + /* The SID and NAI are optional depending on the flags,*/ + /* and the NAI can be variable length */ uint32_t sid; double_linked_list *nai_list; /* double linked list of in_addr or in6_addr */ diff --git a/tests/topotests/ospf-sr-te-topo1/dst/zebra.conf b/tests/topotests/ospf-sr-te-topo1/dst/zebra.conf new file mode 100644 index 000000000000..4cb50fdb27eb --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/dst/zebra.conf @@ -0,0 +1,23 @@ +log file zebra.log +! +hostname dst +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 9.9.9.2/32 + ipv6 address 2001:db8:1066::2/128 +! +interface eth-rt6 + ip address 10.0.11.2/24 + link-params + enable + exit-link-params +! +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf-sr-te-topo1/rt1/bgpd.conf b/tests/topotests/ospf-sr-te-topo1/rt1/bgpd.conf new file mode 100644 index 000000000000..efc03701b53a --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt1/bgpd.conf @@ -0,0 +1,16 @@ +log file bgpd.log +! +router bgp 1 + bgp router-id 1.1.1.1 + neighbor 6.6.6.6 remote-as 1 + neighbor 6.6.6.6 update-source lo + ! + address-family ipv4 unicast + redistribute static + neighbor 6.6.6.6 next-hop-self + neighbor 6.6.6.6 route-map SET_SR_POLICY in + exit-address-family +! +route-map SET_SR_POLICY permit 10 + set sr-te color 1 +! diff --git a/tests/topotests/ospf-sr-te-topo1/rt1/ospfd.conf b/tests/topotests/ospf-sr-te-topo1/rt1/ospfd.conf new file mode 100644 index 000000000000..225ac93528cf --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt1/ospfd.conf @@ -0,0 +1,36 @@ +password 1 +hostname rt1 +log file ospfd.log +! +debug ospf sr +debug ospf te +debug ospf event +debug ospf lsa +debug ospf zebra +! +interface lo + ip ospf area 0.0.0.0 +! +interface eth-sw1 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 0.0.0.0 +! +router ospf + ospf router-id 1.1.1.1 + network 1.1.1.1/32 area 0.0.0.0 + network 10.0.0.0/16 area 0.0.0.0 + capability opaque + !ospf opaque-lsa + mpls-te on + mpls-te export + mpls-te router-address 1.1.1.1 + router-info area 0.0.0.0 + passive-interface lo + segment-routing on + segment-routing global-block 16000 23999 + !segment-routing local-block 15000 15999 + segment-routing node-msd 8 + segment-routing prefix 1.1.1.1/32 index 10 +! diff --git a/tests/topotests/ospf-sr-te-topo1/rt1/pathd.conf b/tests/topotests/ospf-sr-te-topo1/rt1/pathd.conf new file mode 100644 index 000000000000..55d5857f5d08 --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt1/pathd.conf @@ -0,0 +1,27 @@ +log file pathd.log +! +hostname rt1 +! +segment-routing + traffic-eng + mpls-te on + mpls-te import ospfv2 + segment-list default + index 10 nai adjacency 10.0.1.1 10.0.1.2 + index 20 nai adjacency 10.0.2.2 10.0.2.4 + index 30 nai adjacency 10.0.7.4 10.0.7.6 + + + ! + segment-list test + index 10 nai adjacency 10.0.1.1 10.0.1.2 + index 20 nai adjacency 10.0.2.2 10.0.2.4 + index 30 nai adjacency 10.0.6.4 10.0.6.5 + index 40 nai adjacency 10.0.8.5 10.0.8.6 + ! + policy color 1 endpoint 6.6.6.6 + name default + binding-sid 1111 + ! + ! +! diff --git a/tests/topotests/ospf-sr-te-topo1/rt1/step1/show_mpls_table_with_candidate.ref b/tests/topotests/ospf-sr-te-topo1/rt1/step1/show_mpls_table_with_candidate.ref new file mode 100644 index 000000000000..4c290caeb0c1 --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt1/step1/show_mpls_table_with_candidate.ref @@ -0,0 +1,132 @@ +{ + "1111":{ + "inLabel":1111, + "installed":true, + "nexthops":[ + { + "type":"SR-TE", + "outLabel":"*", + "outLabelStack":[ + "*", + "*" + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "15000":{ + "inLabel":15000, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "15001":{ + "inLabel":15001, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16030, + "outLabelStack":[ + 16030 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16040, + "outLabelStack":[ + 16040 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16050, + "outLabelStack":[ + 16050 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16060, + "outLabelStack":[ + 16060 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + } +} + diff --git a/tests/topotests/ospf-sr-te-topo1/rt1/step1/show_mpls_table_without_candidate.ref b/tests/topotests/ospf-sr-te-topo1/rt1/step1/show_mpls_table_without_candidate.ref new file mode 100644 index 000000000000..29d993a3c77a --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt1/step1/show_mpls_table_without_candidate.ref @@ -0,0 +1,114 @@ +{ + "15000":{ + "inLabel":15000, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "15001":{ + "inLabel":15001, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16030, + "outLabelStack":[ + 16030 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16040, + "outLabelStack":[ + 16040 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16050, + "outLabelStack":[ + 16050 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16060, + "outLabelStack":[ + 16060 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + } +} diff --git a/tests/topotests/ospf-sr-te-topo1/rt1/step2/show_operational_data.ref b/tests/topotests/ospf-sr-te-topo1/rt1/step2/show_operational_data.ref new file mode 100644 index 000000000000..4ef8d946f2c3 --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt1/step2/show_operational_data.ref @@ -0,0 +1,13 @@ +{ + "frr-pathd:pathd": { + "srte": { + "policy": [ + { + "color": 1, + "endpoint": "6.6.6.6", + "is-operational": false + } + ] + } + } +} diff --git a/tests/topotests/ospf-sr-te-topo1/rt1/step2/show_operational_data_with_candidate.ref b/tests/topotests/ospf-sr-te-topo1/rt1/step2/show_operational_data_with_candidate.ref new file mode 100644 index 000000000000..9b28f6a42b4d --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt1/step2/show_operational_data_with_candidate.ref @@ -0,0 +1,20 @@ +{ + "frr-pathd:pathd": { + "srte": { + "policy": [ + { + "color": 1, + "endpoint": "6.6.6.6", + "is-operational": true, + "candidate-path": [ + { + "preference": 100, + "discriminator": "*", + "is-best-candidate-path": true + } + ] + } + ] + } + } +} diff --git a/tests/topotests/ospf-sr-te-topo1/rt1/step3/show_operational_data_with_single_candidate.ref b/tests/topotests/ospf-sr-te-topo1/rt1/step3/show_operational_data_with_single_candidate.ref new file mode 100644 index 000000000000..9b28f6a42b4d --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt1/step3/show_operational_data_with_single_candidate.ref @@ -0,0 +1,20 @@ +{ + "frr-pathd:pathd": { + "srte": { + "policy": [ + { + "color": 1, + "endpoint": "6.6.6.6", + "is-operational": true, + "candidate-path": [ + { + "preference": 100, + "discriminator": "*", + "is-best-candidate-path": true + } + ] + } + ] + } + } +} diff --git a/tests/topotests/ospf-sr-te-topo1/rt1/step3/show_operational_data_with_two_candidates.ref b/tests/topotests/ospf-sr-te-topo1/rt1/step3/show_operational_data_with_two_candidates.ref new file mode 100644 index 000000000000..249117198a59 --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt1/step3/show_operational_data_with_two_candidates.ref @@ -0,0 +1,25 @@ +{ + "frr-pathd:pathd": { + "srte": { + "policy": [ + { + "color": 1, + "endpoint": "6.6.6.6", + "is-operational": true, + "candidate-path": [ + { + "preference": 100, + "discriminator": "*", + "is-best-candidate-path": false + }, + { + "preference": 200, + "discriminator": "*", + "is-best-candidate-path": true + } + ] + } + ] + } + } +} diff --git a/tests/topotests/ospf-sr-te-topo1/rt1/step4/show_mpls_table.ref b/tests/topotests/ospf-sr-te-topo1/rt1/step4/show_mpls_table.ref new file mode 100644 index 000000000000..12644cc95686 --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt1/step4/show_mpls_table.ref @@ -0,0 +1,133 @@ +{ + "1111":{ + "inLabel":1111, + "installed":true, + "nexthops":[ + { + "type":"SR-TE", + "outLabel":"*", + "outLabelStack":[ + "*", + "*", + "*" + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "15000":{ + "inLabel":15000, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "15001":{ + "inLabel":15001, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16030, + "outLabelStack":[ + 16030 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16040, + "outLabelStack":[ + 16040 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16050, + "outLabelStack":[ + 16050 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16060, + "outLabelStack":[ + 16060 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + } + +} diff --git a/tests/topotests/ospf-sr-te-topo1/rt1/step4/show_mpls_table_invalid_nai.ref b/tests/topotests/ospf-sr-te-topo1/rt1/step4/show_mpls_table_invalid_nai.ref new file mode 100644 index 000000000000..4c290caeb0c1 --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt1/step4/show_mpls_table_invalid_nai.ref @@ -0,0 +1,132 @@ +{ + "1111":{ + "inLabel":1111, + "installed":true, + "nexthops":[ + { + "type":"SR-TE", + "outLabel":"*", + "outLabelStack":[ + "*", + "*" + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "15000":{ + "inLabel":15000, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "15001":{ + "inLabel":15001, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16030, + "outLabelStack":[ + 16030 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16040, + "outLabelStack":[ + 16040 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16050, + "outLabelStack":[ + 16050 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16060, + "outLabelStack":[ + 16060 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + } +} + diff --git a/tests/topotests/ospf-sr-te-topo1/rt1/zebra.conf b/tests/topotests/ospf-sr-te-topo1/rt1/zebra.conf new file mode 100644 index 000000000000..dd686ea3da7e --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt1/zebra.conf @@ -0,0 +1,21 @@ +log file zebra.log +! +hostname rt1 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 1.1.1.1/32 +! +interface eth-sw1 + ip address 10.0.1.1/24 + link-params + enable + exit-link-params +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf-sr-te-topo1/rt2/ospfd.conf b/tests/topotests/ospf-sr-te-topo1/rt2/ospfd.conf new file mode 100644 index 000000000000..f6a7bbb621a4 --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt2/ospfd.conf @@ -0,0 +1,47 @@ +hostname rt2 +log file ospfd.log +! +debug ospf sr +debug ospf te +debug ospf event +debug ospf lsa +debug ospf zebra +! +interface lo + ip ospf area 0.0.0.0 +! +interface eth-sw1 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 0.0.0.0 +! +interface eth-rt4-1 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 0.0.0.0 +! +interface eth-rt4-2 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 0.0.0.0 +! +router ospf + ospf router-id 2.2.2.2 + network 2.2.2.2/32 area 0.0.0.0 + network 10.0.0.0/16 area 0.0.0.0 + capability opaque + !ospf opaque-lsa + mpls-te on + !mpls-te export + mpls-te router-address 2.2.2.2 + router-info area 0.0.0.0 + passive-interface lo + segment-routing on + segment-routing global-block 16000 23999 + !segment-routing local-block 15000 15999 + segment-routing node-msd 8 + segment-routing prefix 2.2.2.2/32 index 20 +! diff --git a/tests/topotests/ospf-sr-te-topo1/rt2/zebra.conf b/tests/topotests/ospf-sr-te-topo1/rt2/zebra.conf new file mode 100644 index 000000000000..ddd50ba52015 --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt2/zebra.conf @@ -0,0 +1,35 @@ +log file zebra.log +! +hostname rt2 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 2.2.2.2/32 +! +interface eth-sw1 + ip address 10.0.1.2/24 + link-params + enable + exit-link-params +! +interface eth-rt4-1 + ip address 10.0.2.2/24 + link-params + enable + exit-link-params +! +! +interface eth-rt4-2 + ip address 10.0.3.2/24 + link-params + enable + exit-link-params +! +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf-sr-te-topo1/rt3/ospfd.conf b/tests/topotests/ospf-sr-te-topo1/rt3/ospfd.conf new file mode 100644 index 000000000000..5f71cd848462 --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt3/ospfd.conf @@ -0,0 +1,46 @@ +hostname rt3 +log file ospfd.log +! +debug ospf sr +debug ospf te +debug ospf event +debug ospf lsa +debug ospf zebra +! +interface lo + ip ospf area 0.0.0.0 +! +interface eth-sw1 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 0.0.0.0 +! +interface eth-rt5-1 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 0.0.0.0 +! +interface eth-rt5-2 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 0.0.0.0 +! +router ospf + ospf router-id 3.3.3.3 + network 3.3.3.3/32 area 0.0.0.0 + network 10.0.0.0/16 area 0.0.0.0 + capability opaque + !ospf opaque-lsa + mpls-te on + !mpls-te export + mpls-te router-address 3.3.3.3 + router-info area 0.0.0.0 + segment-routing on + segment-routing global-block 16000 23999 + !segment-routing local-block 15000 15999 + segment-routing node-msd 8 + segment-routing prefix 3.3.3.3/32 index 30 +! diff --git a/tests/topotests/ospf-sr-te-topo1/rt3/zebra.conf b/tests/topotests/ospf-sr-te-topo1/rt3/zebra.conf new file mode 100644 index 000000000000..0825b5c8bfa4 --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt3/zebra.conf @@ -0,0 +1,33 @@ +log file zebra.log +! +hostname rt3 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 3.3.3.3/32 +! +interface eth-sw1 + ip address 10.0.1.3/24 + link-params + enable + exit-link-params +!! +interface eth-rt5-1 + ip address 10.0.4.3/24 + link-params + enable + exit-link-params +!! +interface eth-rt5-2 + ip address 10.0.5.3/24 + link-params + enable + exit-link-params +!! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf-sr-te-topo1/rt4/ospfd.conf b/tests/topotests/ospf-sr-te-topo1/rt4/ospfd.conf new file mode 100644 index 000000000000..d4862cd2337a --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt4/ospfd.conf @@ -0,0 +1,53 @@ +hostname rt4 +log file ospfd.log +! +debug ospf sr +debug ospf te +debug ospf event +debug ospf lsa +debug ospf zebra +! +interface lo + ip ospf area 0.0.0.0 +! +interface eth-rt2-1 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 0.0.0.0 +! +interface eth-rt2-2 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 0.0.0.0 +! +interface eth-rt5 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 0.0.0.0 +! +interface eth-rt6 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 0.0.0.0 +! +router ospf + ospf router-id 4.4.4.4 + network 4.4.4.4/32 area 0.0.0.0 + network 10.0.0.0/16 area 0.0.0.0 + capability opaque + !ospf opaque-lsa + mpls-te on + !mpls-te export + mpls-te router-address 4.4.4.4 + router-info area 0.0.0.0 + passive-interface lo + segment-routing on + segment-routing global-block 16000 23999 + !segment-routing local-block 15000 15999 + segment-routing node-msd 8 + segment-routing prefix 4.4.4.4/32 index 40 +! diff --git a/tests/topotests/ospf-sr-te-topo1/rt4/zebra.conf b/tests/topotests/ospf-sr-te-topo1/rt4/zebra.conf new file mode 100644 index 000000000000..c6d1f4f40e4b --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt4/zebra.conf @@ -0,0 +1,43 @@ +log file zebra.log +! +hostname rt4 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 4.4.4.4/32 +! +interface eth-rt2-1 + ip address 10.0.2.4/24 + link-params + enable + exit-link-params +!! +! +interface eth-rt2-2 + ip address 10.0.3.4/24 + link-params + enable + exit-link-params +!! +! +interface eth-rt5 + ip address 10.0.6.4/24 + link-params + enable + exit-link-params +!! +! +interface eth-rt6 + ip address 10.0.7.4/24 + link-params + enable + exit-link-params +!! +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf-sr-te-topo1/rt5/ospfd.conf b/tests/topotests/ospf-sr-te-topo1/rt5/ospfd.conf new file mode 100644 index 000000000000..fdc0dcfdb7db --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt5/ospfd.conf @@ -0,0 +1,53 @@ +hostname rt5 +log file ospfd.log +! +debug ospf sr +debug ospf te +debug ospf event +debug ospf lsa +debug ospf zebra +! +interface lo + ip ospf area 0.0.0.0 +! +interface eth-rt3-1 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 0.0.0.0 +! +interface eth-rt3-2 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 0.0.0.0 +! +interface eth-rt4 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 0.0.0.0 +! +interface eth-rt6 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 0.0.0.0 +! +router ospf + ospf router-id 5.5.5.5 + network 5.5.5.5/32 area 0.0.0.0 + network 10.0.0.0/16 area 0.0.0.0 + capability opaque +! ospf opaque-lsa + mpls-te on +! mpls-te export + mpls-te router-address 5.5.5.5 + router-info area 0.0.0.0 + passive-interface lo + segment-routing on + segment-routing global-block 16000 23999 +! segment-routing local-block 15000 15999 + segment-routing node-msd 8 + segment-routing prefix 5.5.5.5/32 index 50 +! diff --git a/tests/topotests/ospf-sr-te-topo1/rt5/zebra.conf b/tests/topotests/ospf-sr-te-topo1/rt5/zebra.conf new file mode 100644 index 000000000000..96b732d3988e --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt5/zebra.conf @@ -0,0 +1,43 @@ +log file zebra.log +! +hostname rt5 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 5.5.5.5/32 +! +interface eth-rt3-1 + ip address 10.0.4.5/24 + link-params + enable + exit-link-params +!! +! +interface eth-rt3-2 + ip address 10.0.5.5/24 + link-params + enable + exit-link-params +!! +! +interface eth-rt4 + ip address 10.0.6.5/24 + link-params + enable + exit-link-params +!! +! +interface eth-rt6 + ip address 10.0.8.5/24 + link-params + enable + exit-link-params +!! +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf-sr-te-topo1/rt6/bgpd.conf b/tests/topotests/ospf-sr-te-topo1/rt6/bgpd.conf new file mode 100644 index 000000000000..e72ee52fce6a --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt6/bgpd.conf @@ -0,0 +1,12 @@ +log file bgpd.log +! +router bgp 1 + bgp router-id 6.6.6.6 + neighbor 1.1.1.1 remote-as 1 + neighbor 1.1.1.1 update-source lo + ! + address-family ipv4 unicast + redistribute static + neighbor 1.1.1.1 next-hop-self + exit-address-family +! diff --git a/tests/topotests/ospf-sr-te-topo1/rt6/ospfd.conf b/tests/topotests/ospf-sr-te-topo1/rt6/ospfd.conf new file mode 100644 index 000000000000..c06565be0b26 --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt6/ospfd.conf @@ -0,0 +1,41 @@ +hostname rt6 +log file ospfd.log +! +debug ospf sr +debug ospf te +debug ospf event +debug ospf lsa +debug ospf zebra +! +interface lo + ip ospf area 0.0.0.0 +! +interface eth-rt4 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 0.0.0.0 +! +interface eth-rt5 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 0.0.0.0 +! +router ospf + ospf router-id 6.6.6.6 + network 6.6.6.6/32 area 0.0.0.0 + network 10.0.0.0/16 area 0.0.0.0 + capability opaque +! ospf opaque-lsa + mpls-te on + mpls-te export + mpls-te router-address 6.6.6.6 + router-info area 0.0.0.0 + passive-interface lo + segment-routing on + segment-routing global-block 16000 23999 +! segment-routing local-block 15000 15999 + segment-routing node-msd 8 + segment-routing prefix 6.6.6.6/32 index 60 +! diff --git a/tests/topotests/ospf-sr-te-topo1/rt6/pathd.conf b/tests/topotests/ospf-sr-te-topo1/rt6/pathd.conf new file mode 100644 index 000000000000..696df2214b62 --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt6/pathd.conf @@ -0,0 +1,25 @@ +log file pathd.log +! +hostname rt6 +! +segment-routing + traffic-eng + mpls-te on + mpls-te import ospfv2 + segment-list default + index 10 nai adjacency 10.0.7.6 10.0.7.4 + index 20 nai adjacency 10.0.2.4 10.0.2.2 + index 30 nai adjacency 10.0.1.2 10.0.1.1 + ! + segment-list test + index 10 nai adjacency 10.0.8.6 10.0.8.5 + index 20 nai adjacency 10.0.6.5 10.0.6.4 + index 30 nai adjacency 10.0.2.4 10.0.2.2 + index 40 nai adjacency 10.0.1.2 10.0.1.1 + ! + policy color 1 endpoint 1.1.1.1 + name default + binding-sid 6666 + ! + ! +! diff --git a/tests/topotests/ospf-sr-te-topo1/rt6/step1/show_mpls_table_with_candidate.ref b/tests/topotests/ospf-sr-te-topo1/rt6/step1/show_mpls_table_with_candidate.ref new file mode 100644 index 000000000000..c3e4861446ce --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt6/step1/show_mpls_table_with_candidate.ref @@ -0,0 +1,164 @@ +{ + "6666":{ + "inLabel":6666, + "installed":true, + "nexthops":[ + { + "type":"SR-TE", + "outLabel":"*", + "outLabelStack":[ + "*", + "*" + ], + "distance":150, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "15000":{ + "inLabel":15000, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "15001":{ + "inLabel":15001, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "15002":{ + "inLabel":15002, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "15003":{ + "inLabel":15003, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16010, + "outLabelStack":[ + 16010 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16020, + "outLabelStack":[ + 16020 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16030, + "outLabelStack":[ + 16030 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + } +} + diff --git a/tests/topotests/ospf-sr-te-topo1/rt6/step1/show_mpls_table_without_candidate.ref b/tests/topotests/ospf-sr-te-topo1/rt6/step1/show_mpls_table_without_candidate.ref new file mode 100644 index 000000000000..14e0f5cbe11d --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt6/step1/show_mpls_table_without_candidate.ref @@ -0,0 +1,146 @@ +{ + "15000":{ + "inLabel":15000, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "15001":{ + "inLabel":15001, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "15002":{ + "inLabel":15002, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "15003":{ + "inLabel":15003, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16010, + "outLabelStack":[ + 16010 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16020, + "outLabelStack":[ + 16020 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16030, + "outLabelStack":[ + 16030 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + } +} diff --git a/tests/topotests/ospf-sr-te-topo1/rt6/step2/show_operational_data.ref b/tests/topotests/ospf-sr-te-topo1/rt6/step2/show_operational_data.ref new file mode 100644 index 000000000000..241c80bdd7b1 --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt6/step2/show_operational_data.ref @@ -0,0 +1,13 @@ +{ + "frr-pathd:pathd": { + "srte": { + "policy": [ + { + "color": 1, + "endpoint": "1.1.1.1", + "is-operational": false + } + ] + } + } +} diff --git a/tests/topotests/ospf-sr-te-topo1/rt6/step2/show_operational_data_with_candidate.ref b/tests/topotests/ospf-sr-te-topo1/rt6/step2/show_operational_data_with_candidate.ref new file mode 100644 index 000000000000..20ea69e38639 --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt6/step2/show_operational_data_with_candidate.ref @@ -0,0 +1,19 @@ +{ + "frr-pathd:pathd": { + "srte": { + "policy": [ + { + "color": 1, + "endpoint": "1.1.1.1", + "is-operational": true, + "candidate-path": [ + { + "preference": 100, + "is-best-candidate-path": true + } + ] + } + ] + } + } +} diff --git a/tests/topotests/ospf-sr-te-topo1/rt6/step3/show_operational_data_with_single_candidate.ref b/tests/topotests/ospf-sr-te-topo1/rt6/step3/show_operational_data_with_single_candidate.ref new file mode 100644 index 000000000000..20ea69e38639 --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt6/step3/show_operational_data_with_single_candidate.ref @@ -0,0 +1,19 @@ +{ + "frr-pathd:pathd": { + "srte": { + "policy": [ + { + "color": 1, + "endpoint": "1.1.1.1", + "is-operational": true, + "candidate-path": [ + { + "preference": 100, + "is-best-candidate-path": true + } + ] + } + ] + } + } +} diff --git a/tests/topotests/ospf-sr-te-topo1/rt6/step3/show_operational_data_with_two_candidates.ref b/tests/topotests/ospf-sr-te-topo1/rt6/step3/show_operational_data_with_two_candidates.ref new file mode 100644 index 000000000000..10cafe9091b4 --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt6/step3/show_operational_data_with_two_candidates.ref @@ -0,0 +1,23 @@ +{ + "frr-pathd:pathd": { + "srte": { + "policy": [ + { + "color": 1, + "endpoint": "1.1.1.1", + "is-operational": true, + "candidate-path": [ + { + "preference": 100, + "is-best-candidate-path": false + }, + { + "preference": 200, + "is-best-candidate-path": true + } + ] + } + ] + } + } +} diff --git a/tests/topotests/ospf-sr-te-topo1/rt6/step4/show_mpls_table.ref b/tests/topotests/ospf-sr-te-topo1/rt6/step4/show_mpls_table.ref new file mode 100644 index 000000000000..593d3b88900a --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt6/step4/show_mpls_table.ref @@ -0,0 +1,164 @@ +{ + "6666":{ + "inLabel":6666, + "installed":true, + "nexthops":[ + { + "type":"SR-TE", + "outLabel":"*", + "outLabelStack":[ + "*", + "*", + "*" + ], + "distance":150, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "15000":{ + "inLabel":15000, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "15001":{ + "inLabel":15001, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "15002":{ + "inLabel":15002, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "15003":{ + "inLabel":15003, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16010, + "outLabelStack":[ + 16010 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16020, + "outLabelStack":[ + 16020 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16030, + "outLabelStack":[ + 16030 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + } +} diff --git a/tests/topotests/ospf-sr-te-topo1/rt6/step4/show_mpls_table_invalid_nai.ref b/tests/topotests/ospf-sr-te-topo1/rt6/step4/show_mpls_table_invalid_nai.ref new file mode 100644 index 000000000000..c3e4861446ce --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt6/step4/show_mpls_table_invalid_nai.ref @@ -0,0 +1,164 @@ +{ + "6666":{ + "inLabel":6666, + "installed":true, + "nexthops":[ + { + "type":"SR-TE", + "outLabel":"*", + "outLabelStack":[ + "*", + "*" + ], + "distance":150, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "15000":{ + "inLabel":15000, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "15001":{ + "inLabel":15001, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "15002":{ + "inLabel":15002, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "15003":{ + "inLabel":15003, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16010, + "outLabelStack":[ + 16010 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16020, + "outLabelStack":[ + 16020 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":16030, + "outLabelStack":[ + 16030 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + } +} + diff --git a/tests/topotests/ospf-sr-te-topo1/rt6/zebra.conf b/tests/topotests/ospf-sr-te-topo1/rt6/zebra.conf new file mode 100644 index 000000000000..360837c4ca10 --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/rt6/zebra.conf @@ -0,0 +1,38 @@ +log file zebra.log +! +hostname rt6 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 6.6.6.6/32 +! +interface eth-rt4 + ip address 10.0.7.6/24 + link-params + enable + exit-link-params +!! +! +interface eth-rt5 + ip address 10.0.8.6/24 + link-params + enable + exit-link-params +!! +! +interface eth-dst + ip address 10.0.11.1/24 + link-params + enable + exit-link-params +!! +! +ip forwarding +! +ip route 9.9.9.2/32 10.0.11.2 +! +line vty +! diff --git a/tests/topotests/ospf-sr-te-topo1/test_ospf_sr_te_topo1.py b/tests/topotests/ospf-sr-te-topo1/test_ospf_sr_te_topo1.py new file mode 100755 index 000000000000..e8000fc072f3 --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/test_ospf_sr_te_topo1.py @@ -0,0 +1,604 @@ +#!/usr/bin/env python + +# +# test_ospf_sr_te_topo1.py +# +# Copyright (c) 2021 by +# Volta Networks +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_ospf_sr_te_topo1.py: + + +---------+ + | | + | RT1 | + | 1.1.1.1 | + | | + +---------+ + |eth-sw1 + | + | + | + +---------+ | +---------+ + | | | | | + | RT2 |eth-sw1 | eth-sw1| RT3 | + | 2.2.2.2 +----------+ + 3.3.3.3 | + | | 10.0.1.0/24 | | + +---------+ +---------+ + eth-rt4-1| eth-rt5-1| |eth-rt5-2 + | | | + 10.0.2.0/24| 10.0.4.0/24| |10.0.5.0/24 + | | | + eth-rt2-1| eth-rt3-1| |eth-rt3-2 + +---------+ +---------+ + | | | | + | RT4 | 10.0.6.0/24 | RT5 | + | 4.4.4.4 +---------------------+ 5.5.5.5 | + | |eth-rt5 eth-rt4| | + +---------+ +---------+ + eth-rt6| |eth-rt6 + | | + 10.0.7.0/24| |10.0.8.0/24 + | +---------+ | + | | | | + | | RT6 | | + +----------+ 6.6.6.6 +-----------+ + eth-rt4| |eth-rt5 + +---------+ + |eth-dst (.1) + | + |10.0.11.0/24 + | + |eth-rt6 (.2) + +---------+ + | | + | DST | + | 9.9.9.2 | + | | + +---------+ + +""" + +import os +import sys +import pytest +import json +import re +from time import sleep +from functools import partial + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd, pytest.mark.pathd] + + +class TemplateTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # + # Define FRR Routers + # + for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "dst"]: + tgen.add_router(router) + + # + # Define connections + # + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["rt1"], nodeif="eth-sw1") + switch.add_link(tgen.gears["rt2"], nodeif="eth-sw1") + #switch.add_link(tgen.gears["rt3"], nodeif="eth-sw1") + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4-1") + switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2-1") + + #switch = tgen.add_switch("s3") + #switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4-2") + #switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2-2") + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5-1") + switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3-1") + + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5-2") + switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3-2") + + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5") + switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4") + + switch = tgen.add_switch("s7") + switch.add_link(tgen.gears["rt4"], nodeif="eth-rt6") + switch.add_link(tgen.gears["rt6"], nodeif="eth-rt4") + + switch = tgen.add_switch("s8") + switch.add_link(tgen.gears["rt5"], nodeif="eth-rt6") + switch.add_link(tgen.gears["rt6"], nodeif="eth-rt5") + + switch = tgen.add_switch("s9") + switch.add_link(tgen.gears["rt6"], nodeif="eth-dst") + switch.add_link(tgen.gears["dst"], nodeif="eth-rt6") + + +def setup_module(mod): + "Sets up the pytest environment" + + tgen = Topogen(TemplateTopo, mod.__name__) + + frrdir = tgen.config.get(tgen.CONFIG_SECTION, "frrdir") + if not os.path.isfile(os.path.join(frrdir, "pathd")): + pytest.skip("pathd daemon wasn't built in:"+frrdir) + + tgen.start_topology() + + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_PATH, os.path.join(CWD, "{}/pathd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def setup_testcase(msg): + logger.info(msg) + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + return tgen + + +def print_cmd_result(rname, command): + print(get_topogen().gears[rname].vtysh_cmd(command, isjson=False)) + + +def compare_json_test(router, command, reference, exact): + output = router.vtysh_cmd(command, isjson=True) + result = topotest.json_cmp(output, reference) + + # Note: topotest.json_cmp() just checks on inclusion of keys. + # For exact matching also compare the other way around. + if not result and exact: + return topotest.json_cmp(reference, output) + else: + return result + + +def cmp_json_output(rname, command, reference, exact=False): + "Compare router JSON output" + + logger.info('Comparing router "%s" "%s" output', rname, command) + + tgen = get_topogen() + filename = "{}/{}/{}".format(CWD, rname, reference) + expected = json.loads(open(filename).read()) + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial(compare_json_test, tgen.gears[rname], command, expected, exact) + _, diff = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) + assert diff is None, assertmsg + + +def cmp_json_output_exact(rname, command, reference): + return cmp_json_output(rname, command, reference, True) + + +def add_candidate_path(rname, endpoint, pref, name, segment_list="default"): + get_topogen().net[rname].cmd( + """ \ + vtysh -c "conf t" \ + -c "segment-routing" \ + -c "traffic-eng" \ + -c "policy color 1 endpoint """ + + endpoint + + """" \ + -c "candidate-path preference """ + + str(pref) + + """ name """ + + name + + """ explicit segment-list """ + + segment_list + + '''"''' + ) + + +def delete_candidate_path(rname, endpoint, pref): + get_topogen().net[rname].cmd( + """ \ + vtysh -c "conf t" \ + -c "segment-routing" \ + -c "traffic-eng" \ + -c "policy color 1 endpoint """ + + endpoint + + """" \ + -c "no candidate-path preference """ + + str(pref) + + '''"''' + ) + + +def add_segment(rname, name, index, label): + get_topogen().net[rname].cmd( + """ \ + vtysh -c "conf t" \ + -c "segment-routing" \ + -c "traffic-eng" \ + -c "segment-list """ + + name + + """" \ + -c "index """ + + str(index) + + """ mpls label """ + + str(label) + + '''"''' + ) + + +def delete_segment(rname, name, index): + get_topogen().net[rname].cmd( + """ \ + vtysh -c "conf t" \ + -c "segment-routing" \ + -c "traffic-eng" \ + -c "segment-list """ + + name + + """" \ + -c "no index """ + + str(index) + + '''"''' + ) + + +def add_segment_adj(rname, name, index, src, dst): + get_topogen().net[rname].cmd( + """ \ + vtysh -c "conf t" \ + -c "segment-routing" \ + -c "traffic-eng" \ + -c "segment-list """ + + name + + """" \ + -c "index """ + + str(index) + + """ nai adjacency """ + + str(src) + + """ """ + + str(dst) + + '''"''' + ) + + +def create_sr_policy(rname, endpoint, bsid): + get_topogen().net[rname].cmd( + """ \ + vtysh -c "conf t" \ + -c "segment-routing" \ + -c "traffic-eng" \ + -c "policy color 1 endpoint """ + + endpoint + + """" \ + -c "name default" \ + -c "binding-sid """ + + str(bsid) + + '''"''' + ) + + +def delete_sr_policy(rname, endpoint): + get_topogen().net[rname].cmd( + """ \ + vtysh -c "conf t" \ + -c "segment-routing" \ + -c "traffic-eng" \ + -c "no policy color 1 endpoint """ + + endpoint + + '''"''' + ) + + +def create_prefix_sid(rname, prefix, sid): + get_topogen().net[rname].cmd( + """ \ + vtysh -c "conf t" \ + -c "router ospf " \ + -c "segment-routing prefix """ + + prefix + + " index " + + str(sid) + + '''"''' + ) + + +def delete_prefix_sid(rname, prefix): + get_topogen().net[rname].cmd( + ''' \ + vtysh -c "conf t" \ + -c "router ospf " \ + -c "no segment-routing prefix "''' + + prefix + ) + + +# +# Step 1 +# +# Checking the MPLS table using a single SR Policy and a single Candidate Path +# Segment list are base in adjacency that query TED +# +def test_srte_init_step1(): + setup_testcase("Test (step 1): wait for OSPF convergence / label distribution") + + for rname in ["rt1", "rt6"]: + cmp_json_output( + rname, "show mpls table json", "step1/show_mpls_table_without_candidate.ref" + ) + + +def test_srte_add_candidate_check_mpls_table_step1(): + setup_testcase("Test (step 1): check MPLS table regarding the added Candidate Path") + + for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]: + add_candidate_path(rname, endpoint, 100, "default") + cmp_json_output( + rname, "show mpls table json", "step1/show_mpls_table_with_candidate.ref" + ) + delete_candidate_path(rname, endpoint, 100) + + +def test_srte_reinstall_sr_policy_check_mpls_table_step1(): + setup_testcase( + "Test (step 1): check MPLS table after the SR Policy was removed and reinstalled" + ) + + for rname, endpoint, bsid in [("rt1", "6.6.6.6", 1111), ("rt6", "1.1.1.1", 6666)]: + add_candidate_path(rname, endpoint, 100, "default") + delete_sr_policy(rname, endpoint) + cmp_json_output( + rname, "show mpls table json", "step1/show_mpls_table_without_candidate.ref" + ) + create_sr_policy(rname, endpoint, bsid) + add_candidate_path(rname, endpoint, 100, "default") + cmp_json_output( + rname, "show mpls table json", "step1/show_mpls_table_with_candidate.ref" + ) + delete_candidate_path(rname, endpoint, 100) + + +# +# Step 2 +# +# Checking pathd operational data using a single SR Policy and a single Candidate Path +# Segment list are base in adjacency that query TED +# +def test_srte_bare_policy_step2(): + setup_testcase("Test (step 2): bare SR Policy should not be operational") + + for rname in ["rt1", "rt6"]: + cmp_json_output_exact( + rname, + "show yang operational-data /frr-pathd:pathd pathd", + "step2/show_operational_data.ref", + ) + + +def test_srte_add_candidate_check_operational_data_step2(): + setup_testcase( + "Test (step 2): add single Candidate Path, SR Policy should be operational" + ) + + for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]: + add_candidate_path(rname, endpoint, 100, "default") + cmp_json_output( + rname, + "show yang operational-data /frr-pathd:pathd pathd", + "step2/show_operational_data_with_candidate.ref", + ) + + +def test_srte_config_remove_candidate_check_operational_data_step2(): + setup_testcase( + "Test (step 2): remove single Candidate Path, SR Policy should not be operational anymore" + ) + + for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]: + delete_candidate_path(rname, endpoint, 100) + cmp_json_output_exact( + rname, + "show yang operational-data /frr-pathd:pathd pathd", + "step2/show_operational_data.ref", + ) + + +# +# Step 3 +# +# Testing the Candidate Path selection +# Segment list are based in adjacencies resolved by query TED +# +def test_srte_add_two_candidates_step3(): + setup_testcase("Test (step 3): second Candidate Path has higher Priority") + + for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]: + for pref, cand_name in [("100", "first"), ("200", "second")]: + add_candidate_path(rname, endpoint, pref, cand_name) + cmp_json_output( + rname, + "show yang operational-data /frr-pathd:pathd pathd", + "step3/show_operational_data_with_two_candidates.ref", + ) + + # cleanup + for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]: + for pref in ["100", "200"]: + delete_candidate_path(rname, endpoint, pref) + + +def test_srte_add_two_candidates_with_reverse_priority_step3(): + setup_testcase("Test (step 3): second Candidate Path has lower Priority") + + # Use reversed priorities here + for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]: + for pref, cand_name in [("200", "first"), ("100", "second")]: + add_candidate_path(rname, endpoint, pref, cand_name) + cmp_json_output( + rname, + "show yang operational-data /frr-pathd:pathd pathd", + "step3/show_operational_data_with_two_candidates.ref", + ) + + # cleanup + for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]: + for pref in ["100", "200"]: + delete_candidate_path(rname, endpoint, pref) + + +def test_srte_remove_best_candidate_step3(): + setup_testcase("Test (step 3): delete the Candidate Path with higher priority") + + for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]: + for pref, cand_name in [("100", "first"), ("200", "second")]: + add_candidate_path(rname, endpoint, pref, cand_name) + + # Delete candidate with higher priority + for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]: + delete_candidate_path(rname, endpoint, 200) + + # Candidate with lower priority should get active now + for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]: + cmp_json_output( + rname, + "show yang operational-data /frr-pathd:pathd pathd", + "step3/show_operational_data_with_single_candidate.ref", + ) + # cleanup + delete_candidate_path(rname, endpoint, 100) + + +# +# Step 4 +# +# Checking MPLS table with a single SR Policy and a Candidate Path with different Segment Lists and other modifications +# Segment list are base in adjacency that query TED +# +def test_srte_change_segment_list_check_mpls_table_step4(): + setup_testcase("Test (step 4): check MPLS table for changed Segment List") + + for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]: + add_candidate_path(rname, endpoint, 100, "default") + # now change the segment list name + add_candidate_path(rname, endpoint, 100, "default", "test") + cmp_json_output(rname, "show mpls table json", "step4/show_mpls_table.ref") + delete_segment(rname, "test", 10) + delete_segment(rname, "test", 20) + delete_segment(rname, "test", 30) + delete_segment(rname, "test", 40) + if rname == "rt1": + add_segment_adj(rname, "test", 10, "10.0.1.1", "10.0.1.2") + add_segment_adj(rname, "test", 20, "10.0.2.2", "10.0.2.4") + add_segment_adj(rname, "test", 30, "10.0.6.4", "10.0.6.5") + add_segment_adj(rname, "test", 40, "10.0.8.5", "10.0.8.6") + else: + add_segment_adj(rname, "test", 10, "10.0.8.6", "10.0.8.5") + add_segment_adj(rname, "test", 20, "10.0.6.5", "10.0.6.4") + add_segment_adj(rname, "test", 30, "10.0.2.4", "10.0.2.2") + add_segment_adj(rname, "test", 40, "10.0.1.2", "10.0.1.1") + cmp_json_output(rname, "show mpls table json", "step4/show_mpls_table.ref") + delete_candidate_path(rname, endpoint, 100) + + +def test_srte_change_sl_priority_error_ted_check_mpls_table_step4(): + setup_testcase("Test (step 4): check MPLS table keeps low prio sl") + + for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]: + add_candidate_path(rname, endpoint, 100, "default") + # now change the segment list name + add_candidate_path(rname, endpoint, 200, "test", "test") + cmp_json_output(rname, "show mpls table json", "step4/show_mpls_table.ref") + delete_segment(rname, "test", 10) + delete_segment(rname, "test", 20) + delete_segment(rname, "test", 30) + delete_segment(rname, "test", 40) + # These won't resolv + if rname == "rt1": + add_segment_adj(rname, "test", 10, "10.0.1.99", "10.0.1.99") + add_segment_adj(rname, "test", 20, "10.0.2.99", "10.0.2.99") + add_segment_adj(rname, "test", 30, "10.0.6.99", "10.0.6.99") + add_segment_adj(rname, "test", 40, "10.0.8.99", "10.0.8.99") + else: + add_segment_adj(rname, "test", 10, "10.0.8.99", "10.0.8.99") + add_segment_adj(rname, "test", 20, "10.0.6.99", "10.0.6.99") + add_segment_adj(rname, "test", 30, "10.0.2.99", "10.0.2.99") + add_segment_adj(rname, "test", 40, "10.0.1.99", "10.0.1.99") + # So policy sticks with default sl even higher prio + cmp_json_output(rname, "show mpls table json", "step4/show_mpls_table_invalid_nai.ref") + delete_candidate_path(rname, endpoint, 100) + + +# Memory leak test template +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/yang/frr-pathd.yang b/yang/frr-pathd.yang index 03f0d3b02400..30f9875a6da7 100644 --- a/yang/frr-pathd.yang +++ b/yang/frr-pathd.yang @@ -84,51 +84,71 @@ module frr-pathd { leaf index { type uint32; description "Segment index"; - } - leaf sid-value { - type rt-types:mpls-label; + } + leaf sid-value { + type rt-types:mpls-label; + description "MPLS label value"; + } + container nai { + presence "The segment has a Node or Adjacency Identifier"; + leaf type { + description "NAI type"; mandatory true; - description "MPLS label value"; - } - container nai { - presence "The segement has a Node or Adjacency Identifier"; - leaf type { - description "NAI type"; - mandatory true; - type enumeration { - enum ipv4_node { - value 1; - description "IPv4 node identifier"; - } - enum ipv6_node { - value 2; - description "IPv6 node identifier"; - } - enum ipv4_adjacency { - value 3; - description "IPv4 adjacency"; - } - enum ipv6_adjacency { - value 4; - description "IPv6 adjacency"; - } - enum ipv4_unnumbered_adjacency { - value 5; - description "IPv4 unnumbered adjacency"; - } + type enumeration { + enum ipv4_node { + value 1; + description "IPv4 node identifier"; + } + enum ipv6_node { + value 2; + description "IPv6 node identifier"; + } + enum ipv4_adjacency { + value 3; + description "IPv4 adjacency"; + } + enum ipv6_adjacency { + value 4; + description "IPv6 adjacency"; + } + enum ipv4_unnumbered_adjacency { + value 5; + description "IPv4 unnumbered adjacency"; + } + enum ipv4_local_iface { + value 7; + description "IPv4 prefix with local interface id"; + } + enum ipv6_local_iface { + value 8; + description "IPv6 prefix with local interface id"; + } + enum ipv4_algo { + value 9; + description "IPv4 prefix with optional algorithm"; + } + enum ipv6_algo { + value 10; + description "IPv6 prefix with optional algorithm"; } } - leaf local-address { - type inet:ip-address; - mandatory true; - } - leaf local-interface { - type uint32; - mandatory true; - when "../type = 'ipv4_unnumbered_adjacency'"; - } - leaf remote-address { - type inet:ip-address; + } + leaf local-address { + type inet:ip-address; + mandatory true; + } + leaf local-prefix-len { + type uint8; + mandatory true; + when "../type = 'ipv4_local_iface' or ../type = 'ipv6_local_iface' or ../type = 'ipv4_algo' or ../type = 'ipv6_algo'"; + } + leaf local-interface { + type uint32; + mandatory true; + when "../type = 'ipv4_local_iface' or ../type = 'ipv6_local_iface' or ../type = 'ipv4_unnumbered_adjacency'"; + } + leaf remote-address { + type inet:ip-address; mandatory true; when "../type = 'ipv4_adjacency' or ../type = 'ipv6_adjacency' or ../type = 'ipv4_unnumbered_adjacency'"; } @@ -137,6 +157,11 @@ module frr-pathd { mandatory true; when "../type = 'ipv4_unnumbered_adjacency'"; } + leaf algorithm { + type uint8; + mandatory true; + when "../type = 'ipv4_algo' or ../type = 'ipv6_algo'"; + } } } }