#include "vrf.h"
#include "srv6.h"
#include "lib/json.h"
+#include "termtable.h"
#include "zebra/zserv.h"
#include "zebra/zebra_router.h"
return CMD_SUCCESS;
}
+static const char *show_srv6_sid_seg6_action(enum seg6local_action_t behavior)
+{
+ switch (behavior) {
+ case ZEBRA_SEG6_LOCAL_ACTION_END:
+ return "uN";
+ case ZEBRA_SEG6_LOCAL_ACTION_END_X:
+ return "uA";
+ case ZEBRA_SEG6_LOCAL_ACTION_END_DX6:
+ return "uDX6";
+ case ZEBRA_SEG6_LOCAL_ACTION_END_DX4:
+ return "uDX4";
+ case ZEBRA_SEG6_LOCAL_ACTION_END_DT6:
+ return "uDT6";
+ case ZEBRA_SEG6_LOCAL_ACTION_END_DT4:
+ return "uDT4";
+ case ZEBRA_SEG6_LOCAL_ACTION_END_DT46:
+ return "uDT46";
+ case ZEBRA_SEG6_LOCAL_ACTION_UNSPEC:
+ return "unspec";
+ case ZEBRA_SEG6_LOCAL_ACTION_END_T:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_DX2:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_B6:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_B6_ENCAP:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_BM:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_S:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_AS:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_AM:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_BPF:
+ break;
+ }
+
+ return "unknown";
+}
+
+static const char *show_srv6_sid_seg6_context(char *str, size_t size, const struct srv6_sid_ctx *ctx,
+ enum seg6local_action_t behavior)
+{
+ struct vrf *vrf;
+ struct interface *ifp;
+
+ switch (behavior) {
+ case ZEBRA_SEG6_LOCAL_ACTION_END:
+ break;
+ case ZEBRA_SEG6_LOCAL_ACTION_END_X:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_DX6:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_DX4:
+ RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) {
+ ifp = if_lookup_by_index(ctx->ifindex, vrf->vrf_id);
+ if (ifp)
+ snprintf(str, size, "Interface '%s'", ifp->name);
+ }
+ break;
+ case ZEBRA_SEG6_LOCAL_ACTION_END_T:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_DT6:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_DT4:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_DT46:
+ vrf = vrf_lookup_by_id(ctx->vrf_id);
+ snprintf(str, size, "VRF '%s'", vrf ? vrf->name : "<unknown>");
+ break;
+ case ZEBRA_SEG6_LOCAL_ACTION_END_DX2:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_B6:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_B6_ENCAP:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_BM:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_S:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_AS:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_AM:
+ case ZEBRA_SEG6_LOCAL_ACTION_END_BPF:
+ case ZEBRA_SEG6_LOCAL_ACTION_UNSPEC:
+ break;
+ }
+
+ return str;
+}
+
+static void do_show_srv6_sid_line(struct ttable *tt, struct zebra_srv6_sid *sid)
+{
+ struct listnode *node;
+ struct zserv *client;
+ char clients[256];
+ char ctx[256] = {};
+ char behavior[256] = {};
+ char alloc_mode_str[10] = {};
+ char locator_name[SRV6_LOCNAME_SIZE];
+ int ret;
+
+ /* Zclients */
+ if (listcount(sid->client_list)) {
+ bool first = true;
+ int i = 0;
+ for (ALL_LIST_ELEMENTS_RO(sid->client_list, node, client)) {
+ if (first) {
+ ret = snprintf(clients + i, sizeof(clients) - i, "%s(%d)",
+ zebra_route_string(client->proto), client->instance);
+ first = false;
+ } else {
+ ret = snprintf(clients + i, sizeof(clients) - i, ", %s(%d)",
+ zebra_route_string(client->proto), client->instance);
+ }
+
+ if (ret > 0)
+ i += ret;
+ }
+ }
+
+ /* Behavior */
+ if (sid->locator) {
+ if ((sid->locator->sid_format &&
+ sid->locator->sid_format->type == SRV6_SID_FORMAT_TYPE_USID) ||
+ (!sid->locator->sid_format &&
+ CHECK_FLAG(sid->locator->flags, SRV6_LOCATOR_USID))) {
+ snprintf(behavior, sizeof(behavior), "%s",
+ show_srv6_sid_seg6_action(sid->ctx->ctx.behavior));
+ } else {
+ snprintf(behavior, sizeof(behavior), "%s",
+ seg6local_action2str(sid->ctx->ctx.behavior));
+ }
+ }
+
+ /* SID context */
+ show_srv6_sid_seg6_context(ctx, sizeof(ctx), &sid->ctx->ctx, sid->ctx->ctx.behavior);
+
+ if (strlen(ctx) == 0)
+ snprintf(ctx, sizeof(ctx), "-");
+
+ if (sid->locator)
+ snprintf(locator_name, sizeof(locator_name), "%s", sid->locator->name);
+ else
+ snprintf(locator_name, sizeof(locator_name), "-");
+
+ snprintf(alloc_mode_str, sizeof(alloc_mode_str), "%s",
+ srv6_sid_alloc_mode2str(sid->alloc_mode));
+
+ ttable_add_row(tt, "%pI6|%s|%s|%s|%s|%s", &sid->value, behavior, ctx, clients, locator_name,
+ alloc_mode_str);
+}
+
+static void do_show_srv6_sid_json(struct vty *vty, json_object **json, struct srv6_locator *locator,
+ struct zebra_srv6_sid_ctx *sid_ctx)
+{
+ json_object *json_sid_ctx = NULL;
+ json_object *json_sid = NULL;
+ json_object *json_sid_clients = NULL;
+ json_object *json_sid_client = NULL;
+ struct vrf *vrf;
+ struct zebra_vrf *zvrf;
+ struct interface *ifp;
+ struct listnode *node;
+ struct zserv *client;
+ char buf[256];
+
+ if (!sid_ctx || !sid_ctx->sid)
+ return;
+
+ if (locator && sid_ctx->sid->locator != locator)
+ return;
+
+ json_sid = json_object_new_object();
+ json_sid_ctx = json_object_new_object();
+
+ json_object_string_addf(json_sid, "sid", "%pI6", &sid_ctx->sid->value);
+ if ((sid_ctx->sid->locator->sid_format &&
+ sid_ctx->sid->locator->sid_format->type == SRV6_SID_FORMAT_TYPE_USID) ||
+ (!sid_ctx->sid->locator->sid_format &&
+ CHECK_FLAG(sid_ctx->sid->locator->flags, SRV6_LOCATOR_USID))) {
+ json_object_string_add(json_sid, "behavior",
+ show_srv6_sid_seg6_action(sid_ctx->ctx.behavior));
+ } else {
+ json_object_string_add(json_sid, "behavior",
+ seg6local_action2str(sid_ctx->ctx.behavior));
+ }
+
+ if (sid_ctx->ctx.vrf_id) {
+ json_object_int_add(json_sid_ctx, "vrfId", sid_ctx->ctx.vrf_id);
+
+ vrf = vrf_lookup_by_id(sid_ctx->ctx.vrf_id);
+ if (vrf)
+ json_object_string_add(json_sid_ctx, "vrfName", vrf->name);
+
+ zvrf = vrf_info_lookup(sid_ctx->ctx.vrf_id);
+ if (vrf)
+ json_object_int_add(json_sid_ctx, "table", zvrf->table_id);
+ }
+ if (sid_ctx->ctx.ifindex) {
+ json_object_int_add(json_sid_ctx, "interfaceIndex", sid_ctx->ctx.ifindex);
+ RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) {
+ ifp = if_lookup_by_index(sid_ctx->ctx.ifindex, vrf->vrf_id);
+ if (ifp)
+ json_object_string_add(json_sid_ctx, "interfaceName", ifp->name);
+ }
+ }
+ if (memcmp(&sid_ctx->ctx.nh6, &in6addr_any, sizeof(struct in6_addr)) != 0) {
+ json_object_string_addf(json_sid_ctx, "nexthopIpv6Address", "%pI6",
+ &sid_ctx->ctx.nh6);
+ }
+ json_object_object_add(json_sid, "context", json_sid_ctx);
+
+ json_object_string_add(json_sid, "locator", sid_ctx->sid->locator->name);
+ json_object_string_add(json_sid, "allocationMode",
+ srv6_sid_alloc_mode2str(sid_ctx->sid->alloc_mode));
+
+ /* Zclients */
+ json_sid_clients = json_object_new_array();
+ if (listcount(sid_ctx->sid->client_list)) {
+ for (ALL_LIST_ELEMENTS_RO(sid_ctx->sid->client_list, node, client)) {
+ json_sid_client = json_object_new_object();
+ json_object_string_add(json_sid_client, "protocol",
+ zebra_route_string(client->proto));
+ json_object_int_add(json_sid_client, "instance", client->instance);
+ json_object_array_add(json_sid_clients, json_sid_client);
+ }
+ }
+ json_object_object_add(json_sid, "clients", json_sid_clients);
+
+ json_object_object_add(*json, inet_ntop(AF_INET6, &sid_ctx->sid->value, buf, sizeof(buf)),
+ json_sid);
+}
+
+static void do_show_srv6_sid_specific(struct vty *vty, json_object **json,
+ struct srv6_locator *locator,
+ struct zebra_srv6_sid_ctx *sid_ctx)
+{
+ struct ttable *tt;
+
+ if (json) {
+ do_show_srv6_sid_json(vty, json, locator, sid_ctx);
+ } else {
+ /* Prepare table. */
+ tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
+
+ ttable_add_row(tt, "SID|Behavior|Context|Daemon/Instance|Locator|AllocationType");
+ tt->style.cell.rpad = 2;
+ tt->style.corner = ' ';
+ ttable_restyle(tt);
+ ttable_rowseps(tt, 0, BOTTOM, true, '-');
+
+ if (!sid_ctx || !sid_ctx->sid)
+ return;
+
+ if (locator && sid_ctx->sid->locator != locator)
+ return;
+
+ do_show_srv6_sid_line(tt, sid_ctx->sid);
+
+ ttable_colseps(tt, 1, RIGHT, true, ' ');
+ ttable_colseps(tt, 2, LEFT, true, ' ');
+ ttable_colseps(tt, 2, RIGHT, true, ' ');
+ ttable_colseps(tt, 3, LEFT, true, ' ');
+ ttable_colseps(tt, 3, RIGHT, true, ' ');
+ ttable_colseps(tt, 4, LEFT, true, ' ');
+ ttable_colseps(tt, 4, RIGHT, true, ' ');
+ ttable_colseps(tt, 5, LEFT, true, ' ');
+
+ /* Dump the generated table. */
+ if (tt->nrows > 1) {
+ char *table;
+
+ table = ttable_dump(tt, "\n");
+ vty_out(vty, "%s\n", table);
+ XFREE(MTYPE_TMP_TTABLE, table);
+ }
+ ttable_del(tt);
+ }
+}
+
+static void do_show_srv6_sid_all(struct vty *vty, json_object **json, struct srv6_locator *locator)
+{
+ struct zebra_srv6 *srv6 = zebra_srv6_get_default();
+ struct zebra_srv6_sid_ctx *ctx;
+ struct listnode *node;
+ struct ttable *tt;
+
+ if (json) {
+ for (ALL_LIST_ELEMENTS_RO(srv6->sids, node, ctx)) {
+ /* Skip contexts not associated with any SID */
+ if (!ctx->sid)
+ continue;
+
+ /* Skip SIDs from locators we are not interested in */
+ if (locator && ctx->sid->locator != locator)
+ continue;
+
+ do_show_srv6_sid_json(vty, json, locator, ctx);
+ }
+ } else {
+ /* Prepare table. */
+ tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
+ ttable_add_row(tt, "SID|Behavior|Context|Daemon/Instance|Locator|AllocationType");
+ tt->style.cell.rpad = 2;
+ tt->style.corner = ' ';
+ ttable_restyle(tt);
+ ttable_rowseps(tt, 0, BOTTOM, true, '-');
+
+ for (ALL_LIST_ELEMENTS_RO(srv6->sids, node, ctx)) {
+ /* Skip contexts not associated with any SID */
+ if (!ctx->sid)
+ continue;
+
+ /* Skip SIDs from locators we are not interested in */
+ if (locator && ctx->sid->locator != locator)
+ continue;
+
+ do_show_srv6_sid_line(tt, ctx->sid);
+ }
+
+ ttable_colseps(tt, 1, RIGHT, true, ' ');
+ ttable_colseps(tt, 2, LEFT, true, ' ');
+ ttable_colseps(tt, 2, RIGHT, true, ' ');
+ ttable_colseps(tt, 3, LEFT, true, ' ');
+ ttable_colseps(tt, 3, RIGHT, true, ' ');
+ ttable_colseps(tt, 4, LEFT, true, ' ');
+ ttable_colseps(tt, 4, RIGHT, true, ' ');
+ ttable_colseps(tt, 5, LEFT, true, ' ');
+
+ /* Dump the generated table. */
+ if (tt->nrows > 1) {
+ char *table;
+
+ table = ttable_dump(tt, "\n");
+ vty_out(vty, "%s\n", table);
+ XFREE(MTYPE_TMP_TTABLE, table);
+ }
+ ttable_del(tt);
+ }
+}
+
+DEFPY (show_srv6_sid,
+ show_srv6_sid_cmd,
+ "show segment-routing srv6 [locator NAME$locator_name] sid [X:X::X:X$sid_value] [json]",
+ SHOW_STR
+ "Segment Routing\n"
+ "Segment Routing SRv6\n"
+ "Locator Information\n"
+ "Locator Name\n"
+ "SID\n"
+ "SID value\n"
+ JSON_STR)
+{
+ bool uj = use_json(argc, argv);
+ struct zebra_srv6 *srv6 = zebra_srv6_get_default();
+ struct srv6_locator *locator = NULL;
+ struct zebra_srv6_sid_ctx *sid_ctx = NULL, *c;
+ struct listnode *node;
+ json_object *json = NULL;
+
+ if (uj)
+ json = json_object_new_object();
+
+ if (locator_name) {
+ locator = zebra_srv6_locator_lookup(locator_name);
+ if (!locator) {
+ if (uj)
+ vty_json(vty, json); /* Return empty json */
+ else
+ vty_out(vty, "%% Can't find the SRv6 locator\n");
+ return CMD_WARNING;
+ }
+ }
+
+ if (!IPV6_ADDR_SAME(&sid_value, &in6addr_any)) {
+ for (ALL_LIST_ELEMENTS_RO(srv6->sids, node, c)) {
+ if (c->sid && IPV6_ADDR_SAME(&c->sid->value, &sid_value)) {
+ sid_ctx = c;
+ break;
+ }
+ }
+
+ if (!sid_ctx) {
+ if (uj)
+ vty_json(vty, json); /* Return empty json */
+ else
+ vty_out(vty, "%% Can't find the SRv6 SID\n");
+ return CMD_WARNING;
+ }
+ }
+
+ if (locator && sid_ctx)
+ if (!sid_ctx->sid || sid_ctx->sid->locator != locator) {
+ if (uj)
+ vty_json(vty, json); /* Return empty json */
+ else
+ vty_out(vty, "%% Can't find the SRv6 SID in the provided locator\n");
+ return CMD_WARNING;
+ }
+
+ if (sid_ctx)
+ do_show_srv6_sid_specific(vty, uj ? &json : NULL, locator, sid_ctx);
+ else
+ do_show_srv6_sid_all(vty, uj ? &json : NULL, locator);
+
+ if (uj)
+ vty_json(vty, json);
+
+ return CMD_SUCCESS;
+}
+
DEFUN_NOSH (segment_routing,
segment_routing_cmd,
"segment-routing",
install_element(VIEW_NODE, &show_srv6_locator_cmd);
install_element(VIEW_NODE, &show_srv6_locator_detail_cmd);
install_element(VIEW_NODE, &show_srv6_manager_cmd);
+ install_element(VIEW_NODE, &show_srv6_sid_cmd);
}