summaryrefslogtreecommitdiffstats
path: root/pathd/path_ted.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--pathd/path_ted.c721
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;
+}