summaryrefslogtreecommitdiffstats
path: root/sharpd/sharp_zebra.c
diff options
context:
space:
mode:
Diffstat (limited to 'sharpd/sharp_zebra.c')
-rw-r--r--sharpd/sharp_zebra.c1089
1 files changed, 1089 insertions, 0 deletions
diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c
new file mode 100644
index 0000000..c095fec
--- /dev/null
+++ b/sharpd/sharp_zebra.c
@@ -0,0 +1,1089 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Zebra connect code.
+ * Copyright (C) Cumulus Networks, Inc.
+ * Donald Sharp
+ */
+#include <zebra.h>
+
+#include "frrevent.h"
+#include "command.h"
+#include "network.h"
+#include "prefix.h"
+#include "stream.h"
+#include "memory.h"
+#include "zclient.h"
+#include "nexthop.h"
+#include "nexthop_group.h"
+#include "link_state.h"
+#include "tc.h"
+
+#include "sharp_globals.h"
+#include "sharp_nht.h"
+#include "sharp_zebra.h"
+
+/* Zebra structure to hold current status. */
+struct zclient *zclient = NULL;
+
+/* For registering threads. */
+extern struct event_loop *master;
+
+/* Privs info */
+extern struct zebra_privs_t sharp_privs;
+
+DEFINE_MTYPE_STATIC(SHARPD, ZC, "Test zclients");
+
+/* Struct to hold list of test zclients */
+struct sharp_zclient {
+ struct sharp_zclient *prev;
+ struct sharp_zclient *next;
+ struct zclient *client;
+};
+
+/* Head of test zclient list */
+static struct sharp_zclient *sharp_clients_head;
+
+static int sharp_opaque_handler(ZAPI_CALLBACK_ARGS);
+
+/* Utility to add a test zclient struct to the list */
+static void add_zclient(struct zclient *client)
+{
+ struct sharp_zclient *node;
+
+ node = XCALLOC(MTYPE_ZC, sizeof(struct sharp_zclient));
+
+ node->client = client;
+
+ node->next = sharp_clients_head;
+ if (sharp_clients_head)
+ sharp_clients_head->prev = node;
+ sharp_clients_head = node;
+}
+
+/* Interface addition message from zebra. */
+static int sharp_ifp_create(struct interface *ifp)
+{
+ return 0;
+}
+
+static int sharp_ifp_destroy(struct interface *ifp)
+{
+ return 0;
+}
+
+static int interface_address_add(ZAPI_CALLBACK_ARGS)
+{
+ zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
+
+ return 0;
+}
+
+static int interface_address_delete(ZAPI_CALLBACK_ARGS)
+{
+ struct connected *c;
+
+ c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
+
+ if (!c)
+ return 0;
+
+ connected_free(&c);
+ return 0;
+}
+
+static int sharp_ifp_up(struct interface *ifp)
+{
+ return 0;
+}
+
+static int sharp_ifp_down(struct interface *ifp)
+{
+ return 0;
+}
+
+int sharp_install_lsps_helper(bool install_p, bool update_p,
+ const struct prefix *p, uint8_t type,
+ int instance, uint32_t in_label,
+ const struct nexthop_group *nhg,
+ const struct nexthop_group *backup_nhg)
+{
+ struct zapi_labels zl = {};
+ struct zapi_nexthop *znh;
+ const struct nexthop *nh;
+ int i, cmd, ret;
+
+ zl.type = ZEBRA_LSP_SHARP;
+ zl.local_label = in_label;
+
+ if (p) {
+ SET_FLAG(zl.message, ZAPI_LABELS_FTN);
+ prefix_copy(&zl.route.prefix, p);
+ zl.route.type = type;
+ zl.route.instance = instance;
+ }
+
+ /* List of nexthops is optional for delete */
+ i = 0;
+ if (nhg) {
+ for (ALL_NEXTHOPS_PTR(nhg, nh)) {
+ znh = &zl.nexthops[i];
+
+ /* Must have labels to be useful */
+ if (nh->nh_label == NULL ||
+ nh->nh_label->num_labels == 0)
+ continue;
+
+ if (nh->type == NEXTHOP_TYPE_IFINDEX ||
+ nh->type == NEXTHOP_TYPE_BLACKHOLE)
+ /* Hmm - can't really deal with these types */
+ continue;
+
+ ret = zapi_nexthop_from_nexthop(znh, nh);
+ if (ret < 0)
+ return -1;
+
+ i++;
+ if (i >= MULTIPATH_NUM)
+ break;
+ }
+ }
+
+ /* Whoops - no nexthops isn't very useful for install */
+ if (i == 0 && install_p)
+ return -1;
+
+ zl.nexthop_num = i;
+
+ /* Add optional backup nexthop info. Since these are used by index,
+ * we can't just skip over an invalid backup nexthop: we will
+ * invalidate the entire operation.
+ */
+ if (backup_nhg != NULL) {
+ i = 0;
+ for (ALL_NEXTHOPS_PTR(backup_nhg, nh)) {
+ znh = &zl.backup_nexthops[i];
+
+ /* Must have labels to be useful */
+ if (nh->nh_label == NULL ||
+ nh->nh_label->num_labels == 0)
+ return -1;
+
+ if (nh->type == NEXTHOP_TYPE_IFINDEX ||
+ nh->type == NEXTHOP_TYPE_BLACKHOLE)
+ /* Hmm - can't really deal with these types */
+ return -1;
+
+ ret = zapi_nexthop_from_nexthop(znh, nh);
+ if (ret < 0)
+ return -1;
+
+ i++;
+ if (i >= MULTIPATH_NUM)
+ break;
+ }
+
+ if (i > 0)
+ SET_FLAG(zl.message, ZAPI_LABELS_HAS_BACKUPS);
+
+ zl.backup_nexthop_num = i;
+ }
+
+
+ if (install_p) {
+ if (update_p)
+ cmd = ZEBRA_MPLS_LABELS_REPLACE;
+ else
+ cmd = ZEBRA_MPLS_LABELS_ADD;
+ } else {
+ cmd = ZEBRA_MPLS_LABELS_DELETE;
+ }
+
+ if (zebra_send_mpls_labels(zclient, cmd, &zl) == ZCLIENT_SEND_FAILURE)
+ return -1;
+
+ return 0;
+}
+
+enum where_to_restart {
+ SHARP_INSTALL_ROUTES_RESTART,
+ SHARP_DELETE_ROUTES_RESTART,
+};
+
+struct buffer_delay {
+ struct prefix p;
+ uint32_t count;
+ uint32_t routes;
+ vrf_id_t vrf_id;
+ uint8_t instance;
+ uint32_t nhgid;
+ uint32_t flags;
+ const struct nexthop_group *nhg;
+ const struct nexthop_group *backup_nhg;
+ enum where_to_restart restart;
+ char *opaque;
+} wb;
+
+/*
+ * route_add - Encodes a route to zebra
+ *
+ * This function returns true when the route was buffered
+ * by the underlying stream system
+ */
+static bool route_add(const struct prefix *p, vrf_id_t vrf_id, uint8_t instance,
+ uint32_t nhgid, const struct nexthop_group *nhg,
+ const struct nexthop_group *backup_nhg, uint32_t flags,
+ char *opaque)
+{
+ struct zapi_route api;
+ struct zapi_nexthop *api_nh;
+ struct nexthop *nh;
+ int i = 0;
+
+ memset(&api, 0, sizeof(api));
+ api.vrf_id = vrf_id;
+ api.type = ZEBRA_ROUTE_SHARP;
+ api.instance = instance;
+ api.safi = SAFI_UNICAST;
+ memcpy(&api.prefix, p, sizeof(*p));
+
+ api.flags = flags;
+ SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
+
+ /* Only send via ID if nhgroup has been successfully installed */
+ if (nhgid && sharp_nhgroup_id_is_installed(nhgid)) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_NHG);
+ api.nhgid = nhgid;
+ } else {
+ for (ALL_NEXTHOPS_PTR(nhg, nh)) {
+ /* Check if we set a VNI label */
+ if (nh->nh_label &&
+ (nh->nh_label_type == ZEBRA_LSP_EVPN))
+ SET_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE);
+
+ api_nh = &api.nexthops[i];
+
+ zapi_nexthop_from_nexthop(api_nh, nh);
+
+ i++;
+ }
+ api.nexthop_num = i;
+ }
+
+ /* Include backup nexthops, if present */
+ if (backup_nhg && backup_nhg->nexthop) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_BACKUP_NEXTHOPS);
+
+ i = 0;
+ for (ALL_NEXTHOPS_PTR(backup_nhg, nh)) {
+ api_nh = &api.backup_nexthops[i];
+
+ zapi_backup_nexthop_from_nexthop(api_nh, nh);
+
+ i++;
+ }
+
+ api.backup_nexthop_num = i;
+ }
+
+ if (strlen(opaque)) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_OPAQUE);
+ api.opaque.length = strlen(opaque) + 1;
+ assert(api.opaque.length <= ZAPI_MESSAGE_OPAQUE_LENGTH);
+ memcpy(api.opaque.data, opaque, api.opaque.length);
+ }
+
+ if (zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api) ==
+ ZCLIENT_SEND_BUFFERED)
+ return true;
+ else
+ return false;
+}
+
+/*
+ * route_delete - Encodes a route for deletion to zebra
+ *
+ * This function returns true when the route sent was
+ * buffered by the underlying stream system.
+ */
+static bool route_delete(struct prefix *p, vrf_id_t vrf_id, uint8_t instance)
+{
+ struct zapi_route api;
+
+ memset(&api, 0, sizeof(api));
+ api.vrf_id = vrf_id;
+ api.type = ZEBRA_ROUTE_SHARP;
+ api.safi = SAFI_UNICAST;
+ api.instance = instance;
+ memcpy(&api.prefix, p, sizeof(*p));
+
+ if (zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api) ==
+ ZCLIENT_SEND_BUFFERED)
+ return true;
+ else
+ return false;
+}
+
+static void sharp_install_routes_restart(struct prefix *p, uint32_t count,
+ vrf_id_t vrf_id, uint8_t instance,
+ uint32_t nhgid,
+ const struct nexthop_group *nhg,
+ const struct nexthop_group *backup_nhg,
+ uint32_t routes, uint32_t flags,
+ char *opaque)
+{
+ uint32_t temp, i;
+ bool v4 = false;
+
+ if (p->family == AF_INET) {
+ v4 = true;
+ temp = ntohl(p->u.prefix4.s_addr);
+ } else
+ temp = ntohl(p->u.val32[3]);
+
+ for (i = count; i < routes; i++) {
+ bool buffered = route_add(p, vrf_id, (uint8_t)instance, nhgid,
+ nhg, backup_nhg, flags, opaque);
+ if (v4)
+ p->u.prefix4.s_addr = htonl(++temp);
+ else
+ p->u.val32[3] = htonl(++temp);
+
+ if (buffered) {
+ wb.p = *p;
+ wb.count = i + 1;
+ wb.routes = routes;
+ wb.vrf_id = vrf_id;
+ wb.instance = instance;
+ wb.nhgid = nhgid;
+ wb.nhg = nhg;
+ wb.flags = flags;
+ wb.backup_nhg = backup_nhg;
+ wb.opaque = opaque;
+ wb.restart = SHARP_INSTALL_ROUTES_RESTART;
+
+ return;
+ }
+ }
+}
+
+void sharp_install_routes_helper(struct prefix *p, vrf_id_t vrf_id,
+ uint8_t instance, uint32_t nhgid,
+ const struct nexthop_group *nhg,
+ const struct nexthop_group *backup_nhg,
+ uint32_t routes, uint32_t flags, char *opaque)
+{
+ zlog_debug("Inserting %u routes", routes);
+
+ /* Only use backup route/nexthops if present */
+ if (backup_nhg && (backup_nhg->nexthop == NULL))
+ backup_nhg = NULL;
+
+ monotime(&sg.r.t_start);
+ sharp_install_routes_restart(p, 0, vrf_id, instance, nhgid, nhg,
+ backup_nhg, routes, flags, opaque);
+}
+
+static void sharp_remove_routes_restart(struct prefix *p, uint32_t count,
+ vrf_id_t vrf_id, uint8_t instance,
+ uint32_t routes)
+{
+ uint32_t temp, i;
+ bool v4 = false;
+
+ if (p->family == AF_INET) {
+ v4 = true;
+ temp = ntohl(p->u.prefix4.s_addr);
+ } else
+ temp = ntohl(p->u.val32[3]);
+
+ for (i = count; i < routes; i++) {
+ bool buffered = route_delete(p, vrf_id, (uint8_t)instance);
+
+ if (v4)
+ p->u.prefix4.s_addr = htonl(++temp);
+ else
+ p->u.val32[3] = htonl(++temp);
+
+ if (buffered) {
+ wb.p = *p;
+ wb.count = i + 1;
+ wb.vrf_id = vrf_id;
+ wb.instance = instance;
+ wb.routes = routes;
+ wb.restart = SHARP_DELETE_ROUTES_RESTART;
+
+ return;
+ }
+ }
+}
+
+void sharp_remove_routes_helper(struct prefix *p, vrf_id_t vrf_id,
+ uint8_t instance, uint32_t routes)
+{
+ zlog_debug("Removing %u routes", routes);
+
+ monotime(&sg.r.t_start);
+
+ sharp_remove_routes_restart(p, 0, vrf_id, instance, routes);
+}
+
+static void handle_repeated(bool installed)
+{
+ struct prefix p = sg.r.orig_prefix;
+ sg.r.repeat--;
+
+ if (sg.r.repeat <= 0)
+ return;
+
+ if (installed) {
+ sg.r.removed_routes = 0;
+ sharp_remove_routes_helper(&p, sg.r.vrf_id, sg.r.inst,
+ sg.r.total_routes);
+ }
+
+ if (!installed) {
+ sg.r.installed_routes = 0;
+ sharp_install_routes_helper(
+ &p, sg.r.vrf_id, sg.r.inst, sg.r.nhgid,
+ &sg.r.nhop_group, &sg.r.backup_nhop_group,
+ sg.r.total_routes, sg.r.flags, sg.r.opaque);
+ }
+}
+
+static void sharp_zclient_buffer_ready(void)
+{
+ switch (wb.restart) {
+ case SHARP_INSTALL_ROUTES_RESTART:
+ sharp_install_routes_restart(
+ &wb.p, wb.count, wb.vrf_id, wb.instance, wb.nhgid,
+ wb.nhg, wb.backup_nhg, wb.routes, wb.flags, wb.opaque);
+ return;
+ case SHARP_DELETE_ROUTES_RESTART:
+ sharp_remove_routes_restart(&wb.p, wb.count, wb.vrf_id,
+ wb.instance, wb.routes);
+ return;
+ }
+}
+
+static int route_notify_owner(ZAPI_CALLBACK_ARGS)
+{
+ struct timeval r;
+ struct prefix p;
+ enum zapi_route_notify_owner note;
+ uint32_t table;
+
+ if (!zapi_route_notify_decode(zclient->ibuf, &p, &table, &note, NULL,
+ NULL))
+ return -1;
+
+ switch (note) {
+ case ZAPI_ROUTE_INSTALLED:
+ sg.r.installed_routes++;
+ if (sg.r.total_routes == sg.r.installed_routes) {
+ monotime(&sg.r.t_end);
+ timersub(&sg.r.t_end, &sg.r.t_start, &r);
+ zlog_debug("Installed All Items %jd.%ld",
+ (intmax_t)r.tv_sec, (long)r.tv_usec);
+ handle_repeated(true);
+ }
+ break;
+ case ZAPI_ROUTE_FAIL_INSTALL:
+ zlog_debug("Failed install of route");
+ break;
+ case ZAPI_ROUTE_BETTER_ADMIN_WON:
+ zlog_debug("Better Admin Distance won over us");
+ break;
+ case ZAPI_ROUTE_REMOVED:
+ sg.r.removed_routes++;
+ if (sg.r.total_routes == sg.r.removed_routes) {
+ monotime(&sg.r.t_end);
+ timersub(&sg.r.t_end, &sg.r.t_start, &r);
+ zlog_debug("Removed all Items %jd.%ld",
+ (intmax_t)r.tv_sec, (long)r.tv_usec);
+ handle_repeated(false);
+ }
+ break;
+ case ZAPI_ROUTE_REMOVE_FAIL:
+ zlog_debug("Route removal Failure");
+ break;
+ }
+ return 0;
+}
+
+static void zebra_connected(struct zclient *zclient)
+{
+ zclient_send_reg_requests(zclient, VRF_DEFAULT);
+
+ /*
+ * Do not actually turn this on yet
+ * This is just the start of the infrastructure needed here
+ * This can be fixed at a later time.
+ *
+ * zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP,
+ * ZEBRA_ROUTE_ALL, 0, VRF_DEFAULT);
+ */
+}
+
+void vrf_label_add(vrf_id_t vrf_id, afi_t afi, mpls_label_t label)
+{
+ zclient_send_vrf_label(zclient, vrf_id, afi, label, ZEBRA_LSP_SHARP);
+}
+
+void nhg_add(uint32_t id, const struct nexthop_group *nhg,
+ const struct nexthop_group *backup_nhg)
+{
+ struct zapi_nhg api_nhg = {};
+ struct zapi_nexthop *api_nh;
+ struct nexthop *nh;
+ bool is_valid = true;
+
+ api_nhg.id = id;
+
+ api_nhg.resilience = nhg->nhgr;
+
+ for (ALL_NEXTHOPS_PTR(nhg, nh)) {
+ if (api_nhg.nexthop_num >= MULTIPATH_NUM) {
+ zlog_warn(
+ "%s: number of nexthops greater than max multipath size, truncating",
+ __func__);
+ break;
+ }
+
+ /* Unresolved nexthops will lead to failure - only send
+ * nexthops that zebra will consider valid.
+ */
+ if (nh->ifindex == 0)
+ continue;
+
+ api_nh = &api_nhg.nexthops[api_nhg.nexthop_num];
+
+ zapi_nexthop_from_nexthop(api_nh, nh);
+ api_nhg.nexthop_num++;
+ }
+
+ if (api_nhg.nexthop_num == 0) {
+ zlog_debug("%s: nhg %u not sent: no valid nexthops", __func__,
+ id);
+ is_valid = false;
+ goto done;
+ }
+
+ if (backup_nhg) {
+ for (ALL_NEXTHOPS_PTR(backup_nhg, nh)) {
+ if (api_nhg.backup_nexthop_num >= MULTIPATH_NUM) {
+ zlog_warn(
+ "%s: number of backup nexthops greater than max multipath size, truncating",
+ __func__);
+ break;
+ }
+
+ /* Unresolved nexthop: will be rejected by zebra.
+ * That causes a problem, since the primary nexthops
+ * rely on array indexing into the backup nexthops. If
+ * that array isn't valid, the backup indexes won't be
+ * valid.
+ */
+ if (nh->ifindex == 0) {
+ zlog_debug("%s: nhg %u: invalid backup nexthop",
+ __func__, id);
+ is_valid = false;
+ break;
+ }
+
+ api_nh = &api_nhg.backup_nexthops
+ [api_nhg.backup_nexthop_num];
+
+ zapi_backup_nexthop_from_nexthop(api_nh, nh);
+ api_nhg.backup_nexthop_num++;
+ }
+ }
+
+done:
+ if (is_valid)
+ zclient_nhg_send(zclient, ZEBRA_NHG_ADD, &api_nhg);
+}
+
+void nhg_del(uint32_t id)
+{
+ struct zapi_nhg api_nhg = {};
+
+ api_nhg.id = id;
+
+ zclient_nhg_send(zclient, ZEBRA_NHG_DEL, &api_nhg);
+}
+
+void sharp_zebra_nexthop_watch(struct prefix *p, vrf_id_t vrf_id, bool import,
+ bool watch, bool connected)
+{
+ int command;
+
+ command = ZEBRA_NEXTHOP_REGISTER;
+
+ if (!watch)
+ command = ZEBRA_NEXTHOP_UNREGISTER;
+
+ if (zclient_send_rnh(zclient, command, p, SAFI_UNICAST, connected,
+ false, vrf_id) == ZCLIENT_SEND_FAILURE)
+ zlog_warn("%s: Failure to send nexthop to zebra", __func__);
+}
+
+static int sharp_debug_nexthops(struct zapi_route *api)
+{
+ int i;
+
+ if (api->nexthop_num == 0) {
+ zlog_debug(" Not installed");
+ return 0;
+ }
+
+ for (i = 0; i < api->nexthop_num; i++) {
+ struct zapi_nexthop *znh = &api->nexthops[i];
+
+ switch (znh->type) {
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ case NEXTHOP_TYPE_IPV4:
+ zlog_debug(
+ " Nexthop %pI4, type: %d, ifindex: %d, vrf: %d, label_num: %d",
+ &znh->gate.ipv4.s_addr, znh->type, znh->ifindex,
+ znh->vrf_id, znh->label_num);
+ break;
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ case NEXTHOP_TYPE_IPV6:
+ zlog_debug(
+ " Nexthop %pI6, type: %d, ifindex: %d, vrf: %d, label_num: %d",
+ &znh->gate.ipv6, znh->type, znh->ifindex,
+ znh->vrf_id, znh->label_num);
+ break;
+ case NEXTHOP_TYPE_IFINDEX:
+ zlog_debug(" Nexthop IFINDEX: %d, ifindex: %d",
+ znh->type, znh->ifindex);
+ break;
+ case NEXTHOP_TYPE_BLACKHOLE:
+ zlog_debug(" Nexthop blackhole");
+ break;
+ }
+ }
+
+ return i;
+}
+static int sharp_nexthop_update(ZAPI_CALLBACK_ARGS)
+{
+ struct sharp_nh_tracker *nht;
+ struct zapi_route nhr;
+ struct prefix matched;
+
+ if (!zapi_nexthop_update_decode(zclient->ibuf, &matched, &nhr)) {
+ zlog_err("%s: Decode of update failed", __func__);
+ return 0;
+ }
+
+ zlog_debug("Received update for %pFX actual match: %pFX metric: %u",
+ &matched, &nhr.prefix, nhr.metric);
+
+ nht = sharp_nh_tracker_get(&matched);
+ nht->nhop_num = nhr.nexthop_num;
+ nht->updates++;
+
+ sharp_debug_nexthops(&nhr);
+
+ return 0;
+}
+
+static int sharp_redistribute_route(ZAPI_CALLBACK_ARGS)
+{
+ struct zapi_route api;
+
+ if (zapi_route_decode(zclient->ibuf, &api) < 0)
+ zlog_warn("%s: Decode of redistribute failed: %d", __func__,
+ ZEBRA_REDISTRIBUTE_ROUTE_ADD);
+
+ zlog_debug("%s: %pFX (%s)", zserv_command_string(cmd), &api.prefix,
+ zebra_route_string(api.type));
+
+ sharp_debug_nexthops(&api);
+
+ return 0;
+}
+
+void sharp_redistribute_vrf(struct vrf *vrf, int type)
+{
+ zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP, type,
+ 0, vrf->vrf_id);
+}
+
+static zclient_handler *const sharp_opaque_handlers[] = {
+ [ZEBRA_OPAQUE_MESSAGE] = sharp_opaque_handler,
+};
+
+/* Add a zclient with a specified session id, for testing. */
+int sharp_zclient_create(uint32_t session_id)
+{
+ struct zclient *client;
+ struct sharp_zclient *node;
+
+ /* Check for duplicates */
+ for (node = sharp_clients_head; node != NULL; node = node->next) {
+ if (node->client->session_id == session_id)
+ return -1;
+ }
+
+ client = zclient_new(master, &zclient_options_default,
+ sharp_opaque_handlers,
+ array_size(sharp_opaque_handlers));
+ client->sock = -1;
+ client->session_id = session_id;
+
+ zclient_init(client, ZEBRA_ROUTE_SHARP, 0, &sharp_privs);
+
+ /* Enqueue on the list of test clients */
+ add_zclient(client);
+
+ return 0;
+}
+
+/* Delete one of the extra test zclients */
+int sharp_zclient_delete(uint32_t session_id)
+{
+ struct sharp_zclient *node;
+
+ /* Search for session */
+ for (node = sharp_clients_head; node != NULL; node = node->next) {
+ if (node->client->session_id == session_id) {
+ /* Dequeue from list */
+ if (node->next)
+ node->next->prev = node->prev;
+ if (node->prev)
+ node->prev->next = node->next;
+ if (node == sharp_clients_head)
+ sharp_clients_head = node->next;
+
+ /* Clean up zclient */
+ zclient_stop(node->client);
+ zclient_free(node->client);
+
+ /* Free memory */
+ XFREE(MTYPE_ZC, node);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static const char *const type2txt[] = {"Generic", "Vertex", "Edge", "Subnet"};
+static const char *const status2txt[] = {"Unknown", "New", "Update",
+ "Delete", "Sync", "Orphan"};
+/* Handler for opaque messages */
+static int sharp_opaque_handler(ZAPI_CALLBACK_ARGS)
+{
+ struct stream *s;
+ struct zapi_opaque_msg info;
+ struct ls_element *lse;
+
+ s = zclient->ibuf;
+
+ if (zclient_opaque_decode(s, &info) != 0)
+ return -1;
+
+ zlog_debug("%s: [%u] received opaque type %u", __func__,
+ zclient->session_id, info.type);
+
+ if (info.type == LINK_STATE_UPDATE) {
+ lse = ls_stream2ted(sg.ted, s, true);
+ if (lse) {
+ zlog_debug(" |- Got %s %s from Link State Database",
+ status2txt[lse->status],
+ type2txt[lse->type]);
+ lse->status = SYNC;
+ } else
+ zlog_debug(
+ "%s: Error to convert Stream into Link State",
+ __func__);
+ }
+
+ return 0;
+}
+
+/* Handler for opaque notification messages */
+static int sharp_opq_notify_handler(ZAPI_CALLBACK_ARGS)
+{
+ struct stream *s;
+ struct zapi_opaque_notif_info info;
+
+ s = zclient->ibuf;
+
+ if (zclient_opaque_notif_decode(s, &info) != 0)
+ return -1;
+
+ if (info.reg)
+ zlog_debug("%s: received opaque notification REG, type %u => %d/%d/%d",
+ __func__, info.msg_type, info.proto, info.instance,
+ info.session_id);
+ else
+ zlog_debug("%s: received opaque notification UNREG, type %u",
+ __func__, info.msg_type);
+
+ return 0;
+}
+
+/*
+ * Send OPAQUE messages, using subtype 'type'.
+ */
+void sharp_opaque_send(uint32_t type, uint32_t proto, uint32_t instance,
+ uint32_t session_id, uint32_t count)
+{
+ uint8_t buf[32];
+ int ret;
+ uint32_t i;
+
+ /* Prepare a small payload */
+ for (i = 0; i < sizeof(buf); i++) {
+ if (type < 255)
+ buf[i] = type;
+ else
+ buf[i] = 255;
+ }
+
+ /* Send some messages - broadcast and unicast are supported */
+ for (i = 0; i < count; i++) {
+ if (proto == 0)
+ ret = zclient_send_opaque(zclient, type, buf,
+ sizeof(buf));
+ else
+ ret = zclient_send_opaque_unicast(zclient, type, proto,
+ instance, session_id,
+ buf, sizeof(buf));
+ if (ret == ZCLIENT_SEND_FAILURE) {
+ zlog_debug("%s: send_opaque() failed => %d", __func__,
+ ret);
+ break;
+ }
+ }
+}
+
+/*
+ * Register/unregister for opaque notifications from zebra about 'type'.
+ */
+void sharp_zebra_opaque_notif_reg(bool is_reg, uint32_t type)
+{
+ if (is_reg)
+ zclient_opaque_request_notify(zclient, type);
+ else
+ zclient_opaque_drop_notify(zclient, type);
+}
+
+/*
+ * Send OPAQUE registration messages, using subtype 'type'.
+ */
+void sharp_opaque_reg_send(bool is_reg, uint32_t proto, uint32_t instance,
+ uint32_t session_id, uint32_t type)
+{
+ struct stream *s;
+
+ s = zclient->obuf;
+ stream_reset(s);
+
+ if (is_reg)
+ zclient_create_header(s, ZEBRA_OPAQUE_REGISTER, VRF_DEFAULT);
+ else
+ zclient_create_header(s, ZEBRA_OPAQUE_UNREGISTER, VRF_DEFAULT);
+
+ /* Send sub-type */
+ stream_putl(s, type);
+
+ /* Add zclient info */
+ stream_putc(s, proto);
+ stream_putw(s, instance);
+ stream_putl(s, session_id);
+
+ /* Put length at the first point of the stream. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ (void)zclient_send_message(zclient);
+}
+
+/* Link State registration */
+void sharp_zebra_register_te(void)
+{
+ /* First register to received Link State Update messages */
+ zclient_register_opaque(zclient, LINK_STATE_UPDATE);
+
+ /* Then, request initial TED with SYNC message */
+ ls_request_sync(zclient);
+}
+
+void sharp_zebra_send_arp(const struct interface *ifp, const struct prefix *p)
+{
+ zclient_send_neigh_discovery_req(zclient, ifp, p);
+}
+
+static int nhg_notify_owner(ZAPI_CALLBACK_ARGS)
+{
+ enum zapi_nhg_notify_owner note;
+ uint32_t id;
+
+ if (!zapi_nhg_notify_decode(zclient->ibuf, &id, &note))
+ return -1;
+
+ switch (note) {
+ case ZAPI_NHG_INSTALLED:
+ sharp_nhgroup_id_set_installed(id, true);
+ zlog_debug("Installed nhg %u", id);
+ break;
+ case ZAPI_NHG_FAIL_INSTALL:
+ zlog_debug("Failed install of nhg %u", id);
+ break;
+ case ZAPI_NHG_REMOVED:
+ zlog_debug("Removed nhg %u", id);
+ break;
+ case ZAPI_NHG_REMOVE_FAIL:
+ zlog_debug("Failed removal of nhg %u", id);
+ break;
+ }
+
+ return 0;
+}
+
+int sharp_zebra_srv6_manager_get_locator_chunk(const char *locator_name)
+{
+ return srv6_manager_get_locator_chunk(zclient, locator_name);
+}
+
+int sharp_zebra_srv6_manager_release_locator_chunk(const char *locator_name)
+{
+ return srv6_manager_release_locator_chunk(zclient, locator_name);
+}
+
+static int sharp_zebra_process_srv6_locator_chunk(ZAPI_CALLBACK_ARGS)
+{
+ struct stream *s = NULL;
+ struct srv6_locator_chunk s6c = {};
+ struct listnode *node, *nnode;
+ struct sharp_srv6_locator *loc;
+
+ s = zclient->ibuf;
+ zapi_srv6_locator_chunk_decode(s, &s6c);
+
+ for (ALL_LIST_ELEMENTS(sg.srv6_locators, node, nnode, loc)) {
+ struct prefix_ipv6 *chunk = NULL;
+ struct listnode *chunk_node;
+ struct prefix_ipv6 *c;
+
+ if (strcmp(loc->name, s6c.locator_name) != 0) {
+ zlog_err("%s: Locator name unmatch %s:%s", __func__,
+ loc->name, s6c.locator_name);
+ continue;
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(loc->chunks, chunk_node, c))
+ if (!prefix_cmp(c, &s6c.prefix))
+ return 0;
+
+ chunk = prefix_ipv6_new();
+ *chunk = s6c.prefix;
+ listnode_add(loc->chunks, chunk);
+ return 0;
+ }
+
+ zlog_err("%s: can't get locator_chunk!!", __func__);
+ return 0;
+}
+
+int sharp_zebra_send_interface_protodown(struct interface *ifp, bool down)
+{
+ zlog_debug("Sending zebra to set %s protodown %s", ifp->name,
+ down ? "on" : "off");
+
+ if (zclient_send_interface_protodown(zclient, ifp->vrf->vrf_id, ifp,
+ down) == ZCLIENT_SEND_FAILURE)
+ return -1;
+
+ return 0;
+}
+
+int sharp_zebra_send_tc_filter_rate(struct interface *ifp,
+ const struct prefix *source,
+ const struct prefix *destination,
+ uint8_t ip_proto, uint16_t src_port,
+ uint16_t dst_port, uint64_t rate)
+{
+#define SHARPD_TC_HANDLE 0x0001
+ struct stream *s;
+
+ s = zclient->obuf;
+
+ struct tc_qdisc q = {.ifindex = ifp->ifindex, .kind = TC_QDISC_HTB};
+
+ zapi_tc_qdisc_encode(ZEBRA_TC_QDISC_INSTALL, s, &q);
+ if (zclient_send_message(zclient) == ZCLIENT_SEND_FAILURE)
+ return -1;
+
+ struct tc_class c = {.ifindex = ifp->ifindex,
+ .handle = SHARPD_TC_HANDLE & 0xffff,
+ .kind = TC_QDISC_HTB,
+ .u.htb.ceil = rate,
+ .u.htb.rate = rate};
+
+ zapi_tc_class_encode(ZEBRA_TC_CLASS_ADD, s, &c);
+ if (zclient_send_message(zclient) == ZCLIENT_SEND_FAILURE)
+ return -1;
+
+ struct tc_filter f = {.ifindex = ifp->ifindex,
+ .handle = SHARPD_TC_HANDLE,
+ .priority = 0x1,
+ .kind = TC_FILTER_FLOWER,
+ .u.flower.filter_bm = 0};
+
+#ifdef ETH_P_IP
+ f.protocol = ETH_P_IP;
+#else
+ f.protocol = 0x0800;
+#endif
+
+ f.u.flower.filter_bm |= TC_FLOWER_IP_PROTOCOL;
+ f.u.flower.ip_proto = ip_proto;
+ f.u.flower.filter_bm |= TC_FLOWER_SRC_IP;
+ prefix_copy(&f.u.flower.src_ip, source);
+ f.u.flower.filter_bm |= TC_FLOWER_DST_IP;
+ prefix_copy(&f.u.flower.dst_ip, destination);
+ f.u.flower.filter_bm |= TC_FLOWER_SRC_PORT;
+ f.u.flower.src_port_min = f.u.flower.src_port_max = src_port;
+ f.u.flower.filter_bm |= TC_FLOWER_DST_PORT;
+ f.u.flower.dst_port_min = f.u.flower.dst_port_max = dst_port;
+ f.u.flower.classid = SHARPD_TC_HANDLE & 0xffff;
+
+ zapi_tc_filter_encode(ZEBRA_TC_FILTER_ADD, s, &f);
+ if (zclient_send_message(zclient) == ZCLIENT_SEND_FAILURE)
+ return -1;
+
+ return 0;
+}
+
+static zclient_handler *const sharp_handlers[] = {
+ [ZEBRA_INTERFACE_ADDRESS_ADD] = interface_address_add,
+ [ZEBRA_INTERFACE_ADDRESS_DELETE] = interface_address_delete,
+ [ZEBRA_ROUTE_NOTIFY_OWNER] = route_notify_owner,
+ [ZEBRA_NEXTHOP_UPDATE] = sharp_nexthop_update,
+ [ZEBRA_NHG_NOTIFY_OWNER] = nhg_notify_owner,
+ [ZEBRA_REDISTRIBUTE_ROUTE_ADD] = sharp_redistribute_route,
+ [ZEBRA_REDISTRIBUTE_ROUTE_DEL] = sharp_redistribute_route,
+ [ZEBRA_OPAQUE_MESSAGE] = sharp_opaque_handler,
+ [ZEBRA_OPAQUE_NOTIFY] = sharp_opq_notify_handler,
+ [ZEBRA_SRV6_MANAGER_GET_LOCATOR_CHUNK] =
+ sharp_zebra_process_srv6_locator_chunk,
+};
+
+void sharp_zebra_init(void)
+{
+ struct zclient_options opt = {.receive_notify = true};
+
+ if_zapi_callbacks(sharp_ifp_create, sharp_ifp_up, sharp_ifp_down,
+ sharp_ifp_destroy);
+
+ zclient = zclient_new(master, &opt, sharp_handlers,
+ array_size(sharp_handlers));
+
+ zclient_init(zclient, ZEBRA_ROUTE_SHARP, 0, &sharp_privs);
+ zclient->zebra_connected = zebra_connected;
+ zclient->zebra_buffer_write_ready = sharp_zclient_buffer_ready;
+}