/* * 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 #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 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 $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 $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; }