summaryrefslogtreecommitdiffstats
path: root/pathd/path_zebra.c
diff options
context:
space:
mode:
Diffstat (limited to 'pathd/path_zebra.c')
-rw-r--r--pathd/path_zebra.c360
1 files changed, 360 insertions, 0 deletions
diff --git a/pathd/path_zebra.c b/pathd/path_zebra.c
new file mode 100644
index 0000000..a98532c
--- /dev/null
+++ b/pathd/path_zebra.c
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2020 NetDEF, 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 General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "thread.h"
+#include "log.h"
+#include "lib_errors.h"
+#include "if.h"
+#include "prefix.h"
+#include "zclient.h"
+#include "network.h"
+#include "stream.h"
+#include "linklist.h"
+#include "nexthop.h"
+#include "vrf.h"
+#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 int path_zebra_opaque_msg_handler(ZAPI_CALLBACK_ARGS);
+
+struct zclient *zclient;
+static struct zclient *zclient_sync;
+
+/* Global Variables */
+bool g_has_router_id_v4 = false;
+bool g_has_router_id_v6 = false;
+struct in_addr g_router_id_v4;
+struct in6_addr g_router_id_v6;
+pthread_mutex_t g_router_id_v4_mtx = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t g_router_id_v6_mtx = PTHREAD_MUTEX_INITIALIZER;
+
+/**
+ * Gives the IPv4 router ID received from Zebra.
+ *
+ * @param router_id The in_addr strucure where to store the router id
+ * @return true if the router ID was available, false otherwise
+ */
+bool get_ipv4_router_id(struct in_addr *router_id)
+{
+ bool retval = false;
+ assert(router_id != NULL);
+ pthread_mutex_lock(&g_router_id_v4_mtx);
+ if (g_has_router_id_v4) {
+ memcpy(router_id, &g_router_id_v4, sizeof(*router_id));
+ retval = true;
+ }
+ pthread_mutex_unlock(&g_router_id_v4_mtx);
+ return retval;
+}
+
+/**
+ * Gives the IPv6 router ID received from Zebra.
+ *
+ * @param router_id The in6_addr strucure where to store the router id
+ * @return true if the router ID was available, false otherwise
+ */
+bool get_ipv6_router_id(struct in6_addr *router_id)
+{
+ bool retval = false;
+ assert(router_id != NULL);
+ pthread_mutex_lock(&g_router_id_v6_mtx);
+ if (g_has_router_id_v6) {
+ memcpy(router_id, &g_router_id_v6, sizeof(*router_id));
+ retval = true;
+ }
+ pthread_mutex_unlock(&g_router_id_v6_mtx);
+ return retval;
+}
+
+static void path_zebra_connected(struct zclient *zclient)
+{
+ struct srte_policy *policy;
+
+ zclient_send_reg_requests(zclient, VRF_DEFAULT);
+ zclient_send_router_id_update(zclient, ZEBRA_ROUTER_ID_ADD, AFI_IP6,
+ VRF_DEFAULT);
+
+ RB_FOREACH (policy, srte_policy_head, &srte_policies) {
+ struct srte_candidate *candidate;
+ struct srte_segment_list *segment_list;
+
+ candidate = policy->best_candidate;
+ if (!candidate)
+ continue;
+
+ segment_list = candidate->lsp->segment_list;
+ if (!segment_list)
+ continue;
+
+ path_zebra_add_sr_policy(policy, segment_list);
+ }
+}
+
+static int path_zebra_sr_policy_notify_status(ZAPI_CALLBACK_ARGS)
+{
+ struct zapi_sr_policy zapi_sr_policy;
+ struct srte_policy *policy;
+ struct srte_candidate *best_candidate_path;
+
+ if (zapi_sr_policy_notify_status_decode(zclient->ibuf, &zapi_sr_policy))
+ return -1;
+
+ policy = srte_policy_find(zapi_sr_policy.color,
+ &zapi_sr_policy.endpoint);
+ if (!policy)
+ return -1;
+
+ best_candidate_path = policy->best_candidate;
+ if (!best_candidate_path)
+ return -1;
+
+ srte_candidate_status_update(best_candidate_path,
+ zapi_sr_policy.status);
+
+ return 0;
+}
+
+/* Router-id update message from zebra. */
+static int path_zebra_router_id_update(ZAPI_CALLBACK_ARGS)
+{
+ struct prefix pref;
+ const char *family;
+ char buf[PREFIX2STR_BUFFER];
+ zebra_router_id_update_read(zclient->ibuf, &pref);
+ if (pref.family == AF_INET) {
+ pthread_mutex_lock(&g_router_id_v4_mtx);
+ memcpy(&g_router_id_v4, &pref.u.prefix4,
+ sizeof(g_router_id_v4));
+ g_has_router_id_v4 = true;
+ inet_ntop(AF_INET, &g_router_id_v4, buf, sizeof(buf));
+ pthread_mutex_unlock(&g_router_id_v4_mtx);
+ family = "IPv4";
+ } else if (pref.family == AF_INET6) {
+ pthread_mutex_lock(&g_router_id_v6_mtx);
+ memcpy(&g_router_id_v6, &pref.u.prefix6,
+ sizeof(g_router_id_v6));
+ g_has_router_id_v6 = true;
+ inet_ntop(AF_INET6, &g_router_id_v6, buf, sizeof(buf));
+ pthread_mutex_unlock(&g_router_id_v6_mtx);
+ family = "IPv6";
+ } else {
+ zlog_warn("Unexpected router ID address family for vrf %u: %u",
+ vrf_id, pref.family);
+ return 0;
+ }
+ zlog_info("%s Router Id updated for VRF %u: %s", family, vrf_id, buf);
+ return 0;
+}
+
+/**
+ * Adds a segment routing policy to Zebra.
+ *
+ * @param policy The policy to add
+ * @param segment_list The segment list for the policy
+ */
+void path_zebra_add_sr_policy(struct srte_policy *policy,
+ struct srte_segment_list *segment_list)
+{
+ struct zapi_sr_policy zp = {};
+ struct srte_segment_entry *segment;
+
+ zp.color = policy->color;
+ zp.endpoint = policy->endpoint;
+ strlcpy(zp.name, policy->name, sizeof(zp.name));
+ zp.segment_list.type = ZEBRA_LSP_SRTE;
+ zp.segment_list.local_label = policy->binding_sid;
+ zp.segment_list.label_num = 0;
+ RB_FOREACH (segment, srte_segment_entry_head, &segment_list->segments)
+ zp.segment_list.labels[zp.segment_list.label_num++] =
+ segment->sid_value;
+ policy->status = SRTE_POLICY_STATUS_GOING_UP;
+
+ (void)zebra_send_sr_policy(zclient, ZEBRA_SR_POLICY_SET, &zp);
+}
+
+/**
+ * Deletes a segment policy from Zebra.
+ *
+ * @param policy The policy to remove
+ */
+void path_zebra_delete_sr_policy(struct srte_policy *policy)
+{
+ struct zapi_sr_policy zp = {};
+
+ zp.color = policy->color;
+ zp.endpoint = policy->endpoint;
+ strlcpy(zp.name, policy->name, sizeof(zp.name));
+ zp.segment_list.type = ZEBRA_LSP_SRTE;
+ zp.segment_list.local_label = policy->binding_sid;
+ zp.segment_list.label_num = 0;
+ policy->status = SRTE_POLICY_STATUS_DOWN;
+
+ (void)zebra_send_sr_policy(zclient, ZEBRA_SR_POLICY_DELETE, &zp);
+}
+
+/**
+ * Allocates a label from Zebra's label manager.
+ *
+ * @param label the label to be allocated
+ * @return 0 if the label has been allocated, -1 otherwise
+ */
+int path_zebra_request_label(mpls_label_t label)
+{
+ int ret;
+ uint32_t start, end;
+
+ ret = lm_get_label_chunk(zclient_sync, 0, label, 1, &start, &end);
+ if (ret < 0) {
+ zlog_warn("%s: error getting label range!", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Releases a previously allocated label from Zebra's label manager.
+ *
+ * @param label The label to release
+ * @return 0 ifthe label has beel released, -1 otherwise
+ */
+void path_zebra_release_label(mpls_label_t label)
+{
+ int ret;
+
+ ret = lm_release_label_chunk(zclient_sync, label, label);
+ if (ret < 0)
+ zlog_warn("%s: error releasing label range!", __func__);
+}
+
+static void path_zebra_label_manager_connect(void)
+{
+ /* Connect to label manager. */
+ while (zclient_socket_connect(zclient_sync) < 0) {
+ zlog_warn("%s: error connecting synchronous zclient!",
+ __func__);
+ sleep(1);
+ }
+ set_nonblocking(zclient_sync->sock);
+
+ /* Send hello to notify zebra this is a synchronous client */
+ while (zclient_send_hello(zclient_sync) < 0) {
+ zlog_warn("%s: Error sending hello for synchronous zclient!",
+ __func__);
+ sleep(1);
+ }
+
+ while (lm_label_manager_connect(zclient_sync, 0) != 0) {
+ zlog_warn("%s: error connecting to label manager!", __func__);
+ sleep(1);
+ }
+}
+
+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;
+}
+
+static zclient_handler *const path_handlers[] = {
+ [ZEBRA_SR_POLICY_NOTIFY_STATUS] = path_zebra_sr_policy_notify_status,
+ [ZEBRA_ROUTER_ID_UPDATE] = path_zebra_router_id_update,
+ [ZEBRA_OPAQUE_MESSAGE] = path_zebra_opaque_msg_handler,
+};
+
+/**
+ * Initializes Zebra asynchronous connection.
+ *
+ * @param master The master thread
+ */
+void path_zebra_init(struct thread_master *master)
+{
+ struct zclient_options options = zclient_options_default;
+ options.synchronous = true;
+
+ /* Initialize asynchronous zclient. */
+ zclient = zclient_new(master, &zclient_options_default, path_handlers,
+ array_size(path_handlers));
+ zclient_init(zclient, ZEBRA_ROUTE_SRTE, 0, &pathd_privs);
+ zclient->zebra_connected = path_zebra_connected;
+
+ /* Initialize special zclient for synchronous message exchanges. */
+ zclient_sync = zclient_new(master, &options, NULL, 0);
+ zclient_sync->sock = -1;
+ zclient_sync->redist_default = ZEBRA_ROUTE_SRTE;
+ zclient_sync->instance = 1;
+ zclient_sync->privs = &pathd_privs;
+
+ /* Connect to the LM. */
+ path_zebra_label_manager_connect();
+}
+
+void path_zebra_stop(void)
+{
+ zclient_stop(zclient);
+ zclient_free(zclient);
+}