From 75c69d151c8e72e42dfb4b1966428e9cf79cb7f4 Mon Sep 17 00:00:00 2001 From: Javier Garcia Date: Thu, 22 Apr 2021 15:52:27 +0200 Subject: [PATCH 1/4] pathd. TED support . Client to link State - [part 1/4] - pathd will act as a client to for the configured igp. - pathd must be configured to activate and receive data from igp. !pathd config snippet segment-routing traffic-eng mpls-te on mpls-te import ospfv2 Signed-off-by: Javier Garcia --- pathd/path_main.c | 4 + pathd/path_ted.c | 726 +++++++++++++++++++++++++++++++++++++++++++++ pathd/path_ted.h | 178 +++++++++++ pathd/path_zebra.c | 56 +++- pathd/pathd.c | 15 + pathd/pathd.h | 2 + pathd/subdir.am | 8 +- 7 files changed, 987 insertions(+), 2 deletions(-) create mode 100644 pathd/path_ted.c create mode 100644 pathd/path_ted.h 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_ted.c b/pathd/path_ted.c new file mode 100644 index 000000000000..01ada92258d7 --- /dev/null +++ b/pathd/path_ted.c @@ -0,0 +1,726 @@ +/* + * 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 "prefix.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); +static int path_ted_cli_debug_config_write(struct vty *vty); +static int path_ted_cli_debug_set_all(uint32_t flags, bool set); + +extern struct zclient *zclient; + +struct ted_state ted_state_g = {}; + +/* + * 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) +{ + PATH_TED_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; + else { + ted_state_g.import = IMPORT_UNKNOWN; + return 1; + } + + if (ls_register(zclient, false /*client*/) != 0) { + PATH_TED_ERROR("%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) { + PATH_TED_DEBUG("%s: PATHD-TED: Importing %s data ON", + __func__, + PATH_TED_IGP_PRINT(ted_state_g.import)); + } else { + PATH_TED_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) { + PATH_TED_ERROR( + "%s: PATHD-TED: Unable to unregister Link State", + __func__); + status = 1; + } else { + ted_state_g.import = IMPORT_UNKNOWN; + PATH_TED_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) { + PATH_TED_WARN("PATHD TED ls_ted not initialized"); + return false; + } + + return true; +} + +/** + * 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) { + PATH_TED_ERROR("%s Unable to initialize TED Key [%d] ASN [%d] Name [%s]", + __func__, TED_KEY, TED_ASN, TED_NAME); + } else { + PATH_TED_INFO("%s Initialize TED Key [%d] ASN [%d] Name [%s]", + __func__, TED_KEY, TED_ASN, TED_NAME); + } + + return ted; +} + +uint32_t path_ted_rcvd_message(struct ls_message *msg) +{ + if (!path_ted_is_initialized()) + return 1; + + if (msg == NULL) { + PATH_TED_ERROR("%s: [rcv ted] TED received NULL message ", + __func__); + return 1; + } + + if (path_ted_get_current_igp(msg->data.node->adv.origin)) + return 1; + + switch (msg->type) { + case LS_MSG_TYPE_NODE: + ls_msg2vertex(ted_state_g.ted, msg, true /*hard delete*/); + break; + + case LS_MSG_TYPE_ATTRIBUTES: + ls_msg2edge(ted_state_g.ted, msg, true /*ĥard delete*/); + break; + + case LS_MSG_TYPE_PREFIX: + ls_msg2subnet(ted_state_g.ted, msg, true /*hard delete*/); + break; + + default: + PATH_TED_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; + uint64_t key; + + if (!path_ted_is_initialized()) + return MPLS_LABEL_NONE; + + if (!local || !remote) + return MPLS_LABEL_NONE; + + switch (local->ipa_type) { + case IPADDR_V4: + /* We have local and remote ip */ + /* so check all attributes in ted */ + key = ((uint64_t)ntohl(local->ip._v4_addr.s_addr)) & 0xffffffff; + edge = ls_find_edge_by_key(ted_state_g.ted, key); + if (edge) { + if (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: + key = (uint64_t)(local->ip._v6_addr.s6_addr32[0] & 0xffffffff) + | ((uint64_t)local->ip._v6_addr.s6_addr32[1] << 32); + edge = ls_find_edge_by_key(ted_state_g.ted, key); + if (edge) { + if ((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; + + if (!prefix) + return MPLS_LABEL_NONE; + + switch (prefix->family) { + case AF_INET: + case AF_INET6: + subnet = ls_find_subnet(ted_state_g.ted, *prefix); + if (subnet) { + if ((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; + + if (!prefix) + return MPLS_LABEL_NONE; + + switch (prefix->family) { + case AF_INET: + case AF_INET6: + subnet = ls_find_subnet(ted_state_g.ted, *prefix); + if (subnet && subnet->vertex + && subnet->vertex->outgoing_edges) { + /* 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; +} + +DEFPY (debug_path_ted, + debug_path_ted_cmd, + "[no] debug pathd mpls-te", + NO_STR + DEBUG_STR + "path debugging\n" + "ted debugging\n") +{ + uint32_t mode = DEBUG_NODE2MODE(vty->node); + bool no_debug = (no != NULL); + + DEBUG_MODE_SET(&ted_state_g.dbg, mode, !no); + DEBUG_FLAGS_SET(&ted_state_g.dbg, PATH_TED_DEBUG_BASIC, !no_debug); + return CMD_SUCCESS; +} + +/* + * 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) { + PATH_TED_DEBUG("%s: PATHD-TED: Enabled ON -> ON.", __func__); + return CMD_SUCCESS; + } + + ted_state_g.ted = path_ted_create_ted(); + ted_state_g.enabled = true; + PATH_TED_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) { + PATH_TED_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; + PATH_TED_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 { + PATH_TED_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; +} + +/* + * Config Write functions + */ + +int path_ted_cli_debug_config_write(struct vty *vty) +{ + if (DEBUG_MODE_CHECK(&ted_state_g.dbg, DEBUG_MODE_CONF)) { + if (DEBUG_FLAGS_CHECK(&ted_state_g.dbg, PATH_TED_DEBUG_BASIC)) + vty_out(vty, "debug pathd mpls-te\n"); + return 1; + } + return 0; +} + +int path_ted_cli_debug_set_all(uint32_t flags, bool set) +{ + DEBUG_FLAGS_SET(&ted_state_g.dbg, flags, set); + + /* If all modes have been turned off, don't preserve options. */ + if (!DEBUG_MODE_CHECK(&ted_state_g.dbg, DEBUG_MODE_ALL)) + DEBUG_CLEAR(&ted_state_g.dbg); + + return 0; +} + +/** + * 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); + + install_element(CONFIG_NODE, &debug_path_ted_cmd); + install_element(ENABLE_NODE, &debug_path_ted_cmd); + + hook_register(nb_client_debug_config_write, + path_ted_cli_debug_config_write); + hook_register(nb_client_debug_set_all, path_ted_cli_debug_set_all); +} + +/** + * 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; + + status = ls_request_sync(zclient); + if (status == -1) { + PATH_TED_ERROR( + "%s: PATHD-TED: Opaque error asking for TED sync ", + __func__); + return status; + } else { + PATH_TED_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; + + PATH_TED_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) { + PATH_TED_ERROR( + "%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) { + PATH_TED_ERROR( + "%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..c6897b152032 --- /dev/null +++ b/pathd/path_ted.h @@ -0,0 +1,178 @@ +/* + * 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 +#include "linklist.h" +#include "log.h" +#include "command.h" +#include "stream.h" +#include "prefix.h" +#include "zclient.h" +#include "link_state.h" + +extern struct ted_state ted_state_g; +#define TIMER_RETRY_DELAY 5 /* Timeout in seconds between ls sync request */ +#define TED_KEY 1 +#define TED_ASN 1 +#define TED_NAME "PATHD TED" + +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; + struct debug dbg; +}; +/* Debug flags. */ +#define PATH_TED_DEBUG_BASIC 0x01 +#define PATH_TED_DEBUG(fmt, ...) \ + do { \ + if (DEBUG_FLAGS_CHECK(&ted_state_g.dbg, PATH_TED_DEBUG_BASIC)) \ + DEBUGD(&ted_state_g.dbg, "mpls-te: " fmt, ##__VA_ARGS__); \ + } while (0) + +#define PATH_TED_ERROR(fmt, ...) \ + do { \ + if (DEBUG_FLAGS_CHECK(&ted_state_g.dbg, PATH_TED_DEBUG_BASIC)) \ + DEBUGE(&ted_state_g.dbg, "mpls-te: " fmt, ##__VA_ARGS__); \ + } while (0) +#define PATH_TED_WARN(fmt, ...) \ + do { \ + if (DEBUG_FLAGS_CHECK(&ted_state_g.dbg, PATH_TED_DEBUG_BASIC)) \ + DEBUGW(&ted_state_g.dbg, "mpls-te: " fmt, ##__VA_ARGS__); \ + } while (0) +#define PATH_TED_INFO(fmt, ...) \ + do { \ + if (DEBUG_FLAGS_CHECK(&ted_state_g.dbg, PATH_TED_DEBUG_BASIC)) \ + DEBUGI(&ted_state_g.dbg, "mpls-te: " fmt, ##__VA_ARGS__); \ + } while (0) + +/* 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 8c9357460f5f..53d834f360b9 100644 --- a/pathd/path_zebra.c +++ b/pathd/path_zebra.c @@ -32,9 +32,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 */ @@ -267,6 +272,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. * @@ -283,6 +336,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/pathd.c b/pathd/pathd.c index ae8218631584..62785333ec72 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 @@ -329,6 +330,14 @@ srte_policy_best_candidate(const struct srte_policy *policy) 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 +1033,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 diff --git a/pathd/pathd.h b/pathd/pathd.h index 9c4d256ceff2..6a5fdb3c24db 100644 --- a/pathd/pathd.h +++ b/pathd/pathd.h @@ -372,6 +372,7 @@ struct srte_policy *srte_policy_find(uint32_t color, struct ipaddr *endpoint); 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,6 +409,7 @@ 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); diff --git a/pathd/subdir.am b/pathd/subdir.am index 0666e8d3c8c5..693afabb39ac 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 From f2b9485d6f5aa1508334a7e92d26d95faf0e1733 Mon Sep 17 00:00:00 2001 From: Javier Garcia Date: Thu, 22 Apr 2021 16:04:42 +0200 Subject: [PATCH 2/4] pathd. TED support . Validation of candidate path - [part 2/4] - Explicit segment list nai will be resolved to corresponded sid. - Dynamic segment list (from pce) will be validated. - If segment list could not be resolved or validated won't be used. - Now this new config is supported segment-list sl-1 index 10 nai prefix 10.1.2.1/32 iface 1 index 30 nai adjacency 10.2.5.2 10.2.5.5 index 40 nai prefix 10.10.10.5/32 algorithm 0 Signed-off-by: Javier Garcia --- pathd/path_cli.c | 297 ++++++++++++++++++++++++++++++++++----- pathd/path_nb.c | 9 ++ pathd/path_nb.h | 2 + pathd/path_nb_config.c | 41 +++++- pathd/path_pcep_config.c | 87 +++++++----- pathd/pathd.c | 263 +++++++++++++++++++++++++++++++++- pathd/pathd.h | 68 ++++++++- yang/frr-pathd.yang | 109 ++++++++------ 8 files changed, 752 insertions(+), 124 deletions(-) diff --git a/pathd/path_cli.c b/pathd/path_cli.c index ecb667f98589..7a28449e4e97 100644 --- a/pathd/path_cli.c +++ b/pathd/path_cli.c @@ -34,6 +34,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 @@ -47,6 +48,18 @@ static int config_write_segment_routing(struct vty *vty); static int config_write_traffic_eng(struct vty *vty); static int config_write_segment_lists(struct vty *vty); static int config_write_sr_policies(struct vty *vty); +static int segment_list_has_src_dst( + struct vty *vty, char *xpath, long index, const char *index_str, + struct in_addr adj_src_ipv4, struct in_addr adj_dst_ipv4, + struct in6_addr adj_src_ipv6, struct in6_addr adj_dst_ipv6, + const char *adj_src_ipv4_str, const char *adj_dst_ipv4_str, + const char *adj_src_ipv6_str, const char *adj_dst_ipv6_str); +static int segment_list_has_prefix( + struct vty *vty, char *xpath, long index, const char *index_str, + const struct prefix_ipv4 *prefix_ipv4, const char *prefix_ipv4_str, + const struct prefix_ipv6 *prefix_ipv6, const char *prefix_ipv6_str, + const char *has_algo, long algo, const char *algo_str, + const char *has_iface_id, long iface_id, const char *iface_id_str); DEFINE_MTYPE_STATIC(PATHD, PATH_CLI, "Client"); @@ -144,6 +157,7 @@ DEFPY(show_srte_policy, return CMD_SUCCESS; } + /* * Show detailed SR-TE info */ @@ -295,56 +309,227 @@ void cli_show_srte_segment_list(struct vty *vty, struct lyd_node *dnode, yang_dnode_get_string(dnode, "./name")); } +static int segment_list_has_src_dst( + struct vty *vty, char *xpath, long index, const char *index_str, + struct in_addr adj_src_ipv4, struct in_addr adj_dst_ipv4, + struct in6_addr adj_src_ipv6, struct in6_addr adj_dst_ipv6, + const char *adj_src_ipv4_str, const char *adj_dst_ipv4_str, + const char *adj_src_ipv6_str, const char *adj_dst_ipv6_str) +{ + const char *node_src_id; + uint32_t ted_sid = MPLS_LABEL_NONE; + + struct ipaddr ip_src = {}; + struct ipaddr ip_dst = {}; + if (adj_src_ipv4_str != NULL) { + ip_src.ipa_type = IPADDR_V4; + ip_src.ip._v4_addr = adj_src_ipv4; + ip_dst.ipa_type = IPADDR_V4; + ip_dst.ip._v4_addr = adj_dst_ipv4; + } else if (adj_src_ipv6_str != NULL) { + ip_src.ipa_type = IPADDR_V6; + ip_src.ip._v6_addr = adj_src_ipv6; + ip_dst.ipa_type = IPADDR_V6; + ip_dst.ip._v6_addr = adj_dst_ipv6; + } else { + return CMD_ERR_NO_MATCH; + } + ted_sid = path_ted_query_type_f(&ip_src, &ip_dst); + if (ted_sid == MPLS_LABEL_NONE) { + zlog_warn( + "%s: [rcv ted] CLI NOT FOUND Continue query_type_f SRC (%pIA) DST (%pIA)!", + __func__, &ip_src, &ip_dst); + } + /* type */ + snprintf(xpath, XPATH_MAXLEN, "./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, XPATH_MAXLEN, "./segment[index='%s']/nai/local-address", + index_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, node_src_id); + snprintf(xpath, XPATH_MAXLEN, + "./segment[index='%s']/nai/remote-address", index_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, + adj_dst_ipv4_str ? adj_dst_ipv4_str + : adj_dst_ipv6_str); + return CMD_SUCCESS; +} +int segment_list_has_prefix( + struct vty *vty, char *xpath, long index, const char *index_str, + const struct prefix_ipv4 *prefix_ipv4, const char *prefix_ipv4_str, + const struct prefix_ipv6 *prefix_ipv6, const char *prefix_ipv6_str, + const char *has_algo, long algo, const char *algo_str, + const char *has_iface_id, long iface_id, const char *iface_id_str) +{ + char buf_prefix[INET6_ADDRSTRLEN]; + + uint32_t ted_sid = MPLS_LABEL_NONE; + struct prefix prefix_cli = {}; + struct ipaddr pre_ipaddr = {}; + /* prefix with algorithm or local interface id */ + /* Type */ + snprintf(xpath, XPATH_MAXLEN, "./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)); + pre_ipaddr.ipa_type = IPADDR_V4; + pre_ipaddr.ip._v4_addr = prefix_cli.u.prefix4; + } else if (prefix_ipv6_str != NULL) { + 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)); + pre_ipaddr.ipa_type = IPADDR_V6; + pre_ipaddr.ip._v6_addr = prefix_cli.u.prefix6; + } else { + return CMD_ERR_NO_MATCH; + } + snprintf(xpath, XPATH_MAXLEN, "./segment[index='%s']/nai/local-address", + index_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, buf_prefix); + snprintf(xpath, XPATH_MAXLEN, + "./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, XPATH_MAXLEN, + "./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, XPATH_MAXLEN, + "./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) { + zlog_err( + "%s: [rcv ted] CLI NOT FOUND Continue query_type_c PREFIX (%pIA/%d) ALGO (%ld) sid:(%d)!", + __func__, &pre_ipaddr, 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) { + zlog_err( + "%s: [rcv ted] CLI NOT FOUND Continue query_type_e PREFIX (%pIA/%d) IFACE (%ld) sid:(%d)!", + __func__, &pre_ipaddr, prefix_cli.prefixlen, + iface_id, ted_sid); + } + } + return CMD_SUCCESS; +} /* * 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; + int status = CMD_SUCCESS; + 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_nai != NULL) { - snprintf(xpath, sizeof(xpath), "./segment[index='%s']/nai/type", - index_str); - if (node_ipv4_str != NULL) { - nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, - "ipv4_node"); - node_id = node_ipv4_str; - } else if (node_ipv6_str != NULL) { - nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, - "ipv6_node"); - node_id = node_ipv6_str; - } else { - return CMD_ERR_NO_MATCH; - } + if (has_mpls_label != NULL) { snprintf(xpath, sizeof(xpath), - "./segment[index='%s']/nai/local-address", index_str); - nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, node_id); + "./segment[index='%s']/sid-value", index_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, label_str); + return nb_cli_apply_changes(vty, NULL); + } + + if (has_adj != NULL) { + status = segment_list_has_src_dst(vty, xpath, index, index_str, + adj_src_ipv4, adj_dst_ipv4, + adj_src_ipv6, adj_dst_ipv6, + adj_src_ipv4_str, adj_dst_ipv4_str, + adj_dst_ipv6_str, adj_src_ipv6_str); + if (status != CMD_SUCCESS) + return status; } else { - snprintf(xpath, sizeof(xpath), "./segment[index='%s']/nai", - index_str); - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + segment_list_has_prefix( + vty, xpath, index, index_str, prefix_ipv4, + prefix_ipv4_str, prefix_ipv6, prefix_ipv6_str, has_algo, + algo, algo_str, has_iface_id, iface_id, iface_id_str); + if (status != CMD_SUCCESS) + return status; } return nb_cli_apply_changes(vty, NULL); @@ -369,23 +554,60 @@ 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"); } @@ -1038,6 +1260,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_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 aacbca4ae9fa..609f0915598e 100644 --- a/pathd/path_pcep_config.c +++ b/pathd/path_pcep_config.c @@ -246,6 +246,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; @@ -278,6 +282,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; @@ -297,40 +302,44 @@ 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; } - if (path->first_hop != NULL) { - snprintf(segment_list_name_buff, sizeof(segment_list_name_buff), - "%s-%u", path->name, path->plsp_id); - segment_list_name = segment_list_name_buff; - - segment_list = srte_segment_list_add(segment_list_name); - segment_list->protocol_origin = path->update_origin; - strlcpy(segment_list->originator, path->originator, - sizeof(segment_list->originator)); - SET_FLAG(segment_list->flags, F_SEGMENT_LIST_NEW); - SET_FLAG(segment_list->flags, F_SEGMENT_LIST_MODIFIED); - - for (hop = path->first_hop, index = 10; hop != NULL; - hop = hop->next, index += 10) { - assert(hop->has_sid); - assert(hop->is_mpls); - - segment = srte_segment_entry_add(segment_list, index); - - segment->sid_value = (mpls_label_t)hop->sid.mpls.label; - SET_FLAG(segment->segment_list->flags, - 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 (path->first_hop == NULL) + return PATH_NB_ERR; + + snprintf(segment_list_name_buff, sizeof(segment_list_name_buff), + "%s-%u", path->name, path->plsp_id); + segment_list_name = segment_list_name_buff; + + segment_list = srte_segment_list_add(segment_list_name); + segment_list->protocol_origin = path->update_origin; + strlcpy(segment_list->originator, path->originator, + sizeof(segment_list->originator)); + SET_FLAG(segment_list->flags, F_SEGMENT_LIST_NEW); + SET_FLAG(segment_list->flags, F_SEGMENT_LIST_MODIFIED); + + for (hop = path->first_hop, index = 10; hop != NULL; + hop = hop->next, index += 10) { + assert(hop->has_sid); + assert(hop->is_mpls); + + segment = srte_segment_entry_add(segment_list, index); + + segment->sid_value = (mpls_label_t)hop->sid.mpls.label; + SET_FLAG(segment->segment_list->flags, F_SEGMENT_LIST_MODIFIED); + + if (!hop->has_nai) + continue; + 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++; } candidate->lsp->segment_list = segment_list; @@ -352,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; } @@ -402,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_NODE; + case SRTE_SEGMENT_NAI_TYPE_IPV6_LOCAL_IFACE: + return PCEP_SR_SUBOBJ_NAI_IPV6_NODE; + case SRTE_SEGMENT_NAI_TYPE_IPV4_ALGORITHM: + return PCEP_SR_SUBOBJ_NAI_IPV4_NODE; + case SRTE_SEGMENT_NAI_TYPE_IPV6_ALGORITHM: + return PCEP_SR_SUBOBJ_NAI_IPV6_NODE; default: return PCEP_SR_SUBOBJ_NAI_UNKNOWN; } diff --git a/pathd/pathd.c b/pathd/pathd.c index 62785333ec72..2462b08306f6 100644 --- a/pathd/pathd.c +++ b/pathd/pathd.c @@ -189,14 +189,20 @@ 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)); + if (!segment || !local_ip || !remote_ip) + return 1; + switch (type) { case SRTE_SEGMENT_NAI_TYPE_IPV4_NODE: case SRTE_SEGMENT_NAI_TYPE_IPV6_NODE: @@ -205,6 +211,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, @@ -212,12 +219,68 @@ 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 || !s_entry) + return; + + 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); + } } /** @@ -288,6 +351,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) { + PATH_TED_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. * @@ -323,7 +485,9 @@ 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; } @@ -535,6 +699,7 @@ void srte_candidate_set_bandwidth(struct srte_candidate *candidate, { struct srte_policy *policy = candidate->policy; char endpoint[46]; + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); zlog_debug( "SR-TE(%s, %u): candidate %s %sconfig bandwidth set to %f B/s", @@ -1139,3 +1304,91 @@ 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 (!entry || !prefix_cli) + return 0; + + 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 (!entry || !prefix_cli) + return 0; + + 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 (!entry || !local || !remote) + return 0; + + 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 6a5fdb3c24db..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,14 +377,18 @@ 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); @@ -414,4 +434,40 @@ 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/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'"; + } } } } From f008db919d4b04f33d99bb6e975aa16b64fdcfa9 Mon Sep 17 00:00:00 2001 From: Javier Garcia Date: Thu, 22 Apr 2021 16:08:52 +0200 Subject: [PATCH 3/4] pathd. TED support . Topotest - [part 3/4] Signed-off-by: Javier Garcia --- .../topotests/ospf-sr-te-topo1/dst/zebra.conf | 23 + .../topotests/ospf-sr-te-topo1/rt1/bgpd.conf | 16 + .../topotests/ospf-sr-te-topo1/rt1/ospfd.conf | 36 + .../topotests/ospf-sr-te-topo1/rt1/pathd.conf | 27 + .../rt1/step2/show_operational_data.ref | 13 + .../show_operational_data_with_candidate.ref | 20 + ...operational_data_with_single_candidate.ref | 20 + ...w_operational_data_with_two_candidates.ref | 25 + .../topotests/ospf-sr-te-topo1/rt1/zebra.conf | 21 + .../topotests/ospf-sr-te-topo1/rt2/ospfd.conf | 47 ++ .../topotests/ospf-sr-te-topo1/rt2/zebra.conf | 35 + .../topotests/ospf-sr-te-topo1/rt3/ospfd.conf | 46 ++ .../topotests/ospf-sr-te-topo1/rt3/zebra.conf | 33 + .../topotests/ospf-sr-te-topo1/rt4/ospfd.conf | 53 ++ .../topotests/ospf-sr-te-topo1/rt4/zebra.conf | 43 ++ .../topotests/ospf-sr-te-topo1/rt5/ospfd.conf | 53 ++ .../topotests/ospf-sr-te-topo1/rt5/zebra.conf | 43 ++ .../topotests/ospf-sr-te-topo1/rt6/bgpd.conf | 12 + .../topotests/ospf-sr-te-topo1/rt6/ospfd.conf | 41 ++ .../topotests/ospf-sr-te-topo1/rt6/pathd.conf | 25 + .../rt6/step2/show_operational_data.ref | 13 + .../show_operational_data_with_candidate.ref | 19 + ...operational_data_with_single_candidate.ref | 19 + ...w_operational_data_with_two_candidates.ref | 23 + .../topotests/ospf-sr-te-topo1/rt6/zebra.conf | 38 ++ .../ospf-sr-te-topo1/test_ospf_sr_te_topo1.py | 640 ++++++++++++++++++ 26 files changed, 1384 insertions(+) create mode 100644 tests/topotests/ospf-sr-te-topo1/dst/zebra.conf create mode 100644 tests/topotests/ospf-sr-te-topo1/rt1/bgpd.conf create mode 100644 tests/topotests/ospf-sr-te-topo1/rt1/ospfd.conf create mode 100644 tests/topotests/ospf-sr-te-topo1/rt1/pathd.conf create mode 100644 tests/topotests/ospf-sr-te-topo1/rt1/step2/show_operational_data.ref create mode 100644 tests/topotests/ospf-sr-te-topo1/rt1/step2/show_operational_data_with_candidate.ref create mode 100644 tests/topotests/ospf-sr-te-topo1/rt1/step3/show_operational_data_with_single_candidate.ref create mode 100644 tests/topotests/ospf-sr-te-topo1/rt1/step3/show_operational_data_with_two_candidates.ref create mode 100644 tests/topotests/ospf-sr-te-topo1/rt1/zebra.conf create mode 100644 tests/topotests/ospf-sr-te-topo1/rt2/ospfd.conf create mode 100644 tests/topotests/ospf-sr-te-topo1/rt2/zebra.conf create mode 100644 tests/topotests/ospf-sr-te-topo1/rt3/ospfd.conf create mode 100644 tests/topotests/ospf-sr-te-topo1/rt3/zebra.conf create mode 100644 tests/topotests/ospf-sr-te-topo1/rt4/ospfd.conf create mode 100644 tests/topotests/ospf-sr-te-topo1/rt4/zebra.conf create mode 100644 tests/topotests/ospf-sr-te-topo1/rt5/ospfd.conf create mode 100644 tests/topotests/ospf-sr-te-topo1/rt5/zebra.conf create mode 100644 tests/topotests/ospf-sr-te-topo1/rt6/bgpd.conf create mode 100644 tests/topotests/ospf-sr-te-topo1/rt6/ospfd.conf create mode 100644 tests/topotests/ospf-sr-te-topo1/rt6/pathd.conf create mode 100644 tests/topotests/ospf-sr-te-topo1/rt6/step2/show_operational_data.ref create mode 100644 tests/topotests/ospf-sr-te-topo1/rt6/step2/show_operational_data_with_candidate.ref create mode 100644 tests/topotests/ospf-sr-te-topo1/rt6/step3/show_operational_data_with_single_candidate.ref create mode 100644 tests/topotests/ospf-sr-te-topo1/rt6/step3/show_operational_data_with_two_candidates.ref create mode 100644 tests/topotests/ospf-sr-te-topo1/rt6/zebra.conf create mode 100755 tests/topotests/ospf-sr-te-topo1/test_ospf_sr_te_topo1.py 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/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/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/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/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..6c1122ab7298 --- /dev/null +++ b/tests/topotests/ospf-sr-te-topo1/test_ospf_sr_te_topo1.py @@ -0,0 +1,640 @@ +#!/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=0.5) + 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 + ) + + +def check_bsid(rt, bsid, fn_name, positive): + """ + Search for a bsid in rt1 and rt6 + Positive means that check is true is bsid is found + Positive="False" means that check is true is bsid is NOT found + """ + + logger.info('Checking "%s" bsid "%s" for router "%s" ', positive, bsid, rt) + + count = 0 + candidate_key = bsid + candidate_output = "" + # First wait for convergence + tgen = get_topogen() + while count < 30: + matched = False + matched_key = False + sleep(1) + count += 1 + router = tgen.gears[rt] + candidate_output = router.vtysh_cmd("show mpls table json") + candidate_output_json = json.loads(candidate_output) + for item in candidate_output_json.items(): + # logger.info('item "%s"', item) + if item[0] == candidate_key: + matched_key = True + if positive: + break + if positive: + if matched_key: + matched = True + assertmsg = "{} don't has entry {} but is was expected".format( + router.name, candidate_key) + else: + if not matched_key: + matched = True + assertmsg = "{} has entry {} but is wans't expected".format( + router.name, candidate_key) + if matched: + logger.info('Success "%s" in "%s"', router.name, fn_name) + return + assert matched, assertmsg + + +# +# 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 OSPF convergence / label distribution") + + check_bsid("rt1", "1111", test_srte_init_step1.__name__, False) + check_bsid("rt6", "6666", test_srte_init_step1.__name__, False) + + +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") + check_bsid(rname, "1111" if rname == "rt1" else "6666", test_srte_init_step1.__name__, True) + 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) + check_bsid(rname, bsid, test_srte_init_step1.__name__, False) + create_sr_policy(rname, endpoint, bsid) + add_candidate_path(rname, endpoint, 100, "default") + check_bsid(rname, "1111" if rname == "rt1" else "6666", test_srte_init_step1.__name__, True) + 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") + check_bsid(rname, "1111" if rname == "rt1" else "6666", test_srte_init_step1.__name__, True) + 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") + check_bsid(rname, "1111" if rname == "rt1" else "6666", test_srte_init_step1.__name__, True) + 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") + check_bsid(rname, "1111" if rname == "rt1" else "6666", test_srte_init_step1.__name__, True) + 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 + check_bsid(rname, "1111" if rname == "rt1" else "6666", test_srte_init_step1.__name__, True) + 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)) From f7d5bdcf5c7f2b40a3634d63af31f988f338eb3a Mon Sep 17 00:00:00 2001 From: Javier Garcia Date: Thu, 22 Apr 2021 16:09:54 +0200 Subject: [PATCH 4/4] pathd. Pathd TED support . Documentation update - [part 4/4] - As an example of pathd and igp (ospfd) config: ! igp ospfv2 snippet interface eth0 ip ospf network point-to-point ! router ospf$ mpls-te on mpls-te export ... !pathd snippet segment-routing traffic-eng mpls-te on mpls-te import ospfv2 segment-list sl-1 index 10 nai adjacency 10.1.2.11 10.1.2.1 index 20 nai adjacency 10.1.20.1 10.1.20.2 index 30 nai adjacency 10.2.5.2 10.2.5.5 ! policy color 5 endpoint 10.10.10.5 name five binding-sid 5555 candidate-path preference 600 name cp51 explicit segment-list sl-1 candidate-path preference 500 name cp52-dyn dynamic Signed-off-by: Javier Garcia --- doc/user/pathd.rst | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/doc/user/pathd.rst b/doc/user/pathd.rst index 5fc3837839d4..c40efffc88db 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 selected igp + .. 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.