diff options
Diffstat (limited to 'pathd/path_ted.c')
-rw-r--r-- | pathd/path_ted.c | 721 |
1 files changed, 721 insertions, 0 deletions
diff --git a/pathd/path_ted.c b/pathd/path_ted.c new file mode 100644 index 0000000..316255a --- /dev/null +++ b/pathd/path_ted.c @@ -0,0 +1,721 @@ +/* + * 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 <https://www.gnu.org/licenses/>. + */ + +#include <zebra.h> + +#include <stdlib.h> + +#include "memory.h" +#include "log.h" +#include "command.h" +#include "prefix.h" +#include <lib/json.h> + +#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 void path_ted_timer_handler_sync(struct thread *thread); +static void 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)ntohl(local->ip._v6_addr.s6_addr32[2]) << 32 | + (uint64_t)ntohl(local->ip._v6_addr.s6_addr32[3]); + edge = ls_find_edge_by_key(ted_state_g.ted, key); + if (edge) { + if ((0 == memcmp(&edge->attributes->standard.remote6, + &remote->ip._v6_addr, + sizeof(remote->ip._v6_addr)) && + CHECK_FLAG(edge->attributes->flags, + LS_ATTR_ADJ_SID6))) { + sid = edge->attributes->adj_sid[ADJ_PRI_IPV6] + .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; +} + +/* + * Following 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 <ospfv2|ospfv3|isis>$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_pathd_ted_db, + show_pathd_ted_db_cmd, + "show pathd ted database <verbose|json>$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, "Traffic Engineering 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_json(vty, 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 + */ +void path_ted_timer_handler_sync(struct thread *thread) +{ + /* data unpacking */ + struct ted_state *data = THREAD_ARG(thread); + + assert(data != NULL); + /* Retry the sync */ + 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 + */ +void path_ted_timer_handler_refresh(struct thread *thread) +{ + if (!path_ted_is_initialized()) + return; + + 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(); +} + +/** + * 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; +} |