summaryrefslogtreecommitdiffstats
path: root/ip/ipioam6.c
diff options
context:
space:
mode:
Diffstat (limited to 'ip/ipioam6.c')
-rw-r--r--ip/ipioam6.c333
1 files changed, 333 insertions, 0 deletions
diff --git a/ip/ipioam6.c b/ip/ipioam6.c
new file mode 100644
index 0000000..b63d7d5
--- /dev/null
+++ b/ip/ipioam6.c
@@ -0,0 +1,333 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ioam6.c "ip ioam"
+ *
+ * Author: Justin Iurman <justin.iurman@uliege.be>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <linux/genetlink.h>
+#include <linux/ioam6_genl.h>
+
+#include "utils.h"
+#include "ip_common.h"
+#include "libgenl.h"
+#include "json_print.h"
+
+static void usage(void)
+{
+ fprintf(stderr,
+ "Usage: ip ioam { COMMAND | help }\n"
+ " ip ioam namespace show\n"
+ " ip ioam namespace add ID [ data DATA32 ] [ wide DATA64 ]\n"
+ " ip ioam namespace del ID\n"
+ " ip ioam schema show\n"
+ " ip ioam schema add ID DATA\n"
+ " ip ioam schema del ID\n"
+ " ip ioam namespace set ID schema { ID | none }\n");
+ exit(-1);
+}
+
+static struct rtnl_handle grth = { .fd = -1 };
+static int genl_family = -1;
+
+#define IOAM6_REQUEST(_req, _bufsiz, _cmd, _flags) \
+ GENL_REQUEST(_req, _bufsiz, genl_family, 0, \
+ IOAM6_GENL_VERSION, _cmd, _flags)
+
+static struct {
+ unsigned int cmd;
+ __u32 sc_id;
+ __u32 ns_data;
+ __u64 ns_data_wide;
+ __u16 ns_id;
+ bool has_ns_data;
+ bool has_ns_data_wide;
+ bool sc_none;
+ __u8 sc_data[IOAM6_MAX_SCHEMA_DATA_LEN];
+} opts;
+
+static void print_namespace(struct rtattr *attrs[])
+{
+ print_uint(PRINT_ANY, "namespace", "namespace %u",
+ rta_getattr_u16(attrs[IOAM6_ATTR_NS_ID]));
+
+ if (attrs[IOAM6_ATTR_SC_ID])
+ print_uint(PRINT_ANY, "schema", " [schema %u]",
+ rta_getattr_u32(attrs[IOAM6_ATTR_SC_ID]));
+
+ if (attrs[IOAM6_ATTR_NS_DATA])
+ print_hex(PRINT_ANY, "data", ", data %#010x",
+ rta_getattr_u32(attrs[IOAM6_ATTR_NS_DATA]));
+
+ if (attrs[IOAM6_ATTR_NS_DATA_WIDE])
+ print_0xhex(PRINT_ANY, "wide", ", wide %#018lx",
+ rta_getattr_u64(attrs[IOAM6_ATTR_NS_DATA_WIDE]));
+
+ print_nl();
+}
+
+static void print_schema(struct rtattr *attrs[])
+{
+ __u8 data[IOAM6_MAX_SCHEMA_DATA_LEN];
+ int len, i = 0;
+
+ print_uint(PRINT_ANY, "schema", "schema %u",
+ rta_getattr_u32(attrs[IOAM6_ATTR_SC_ID]));
+
+ if (attrs[IOAM6_ATTR_NS_ID])
+ print_uint(PRINT_ANY, "namespace", " [namespace %u]",
+ rta_getattr_u16(attrs[IOAM6_ATTR_NS_ID]));
+
+ len = RTA_PAYLOAD(attrs[IOAM6_ATTR_SC_DATA]);
+ memcpy(data, RTA_DATA(attrs[IOAM6_ATTR_SC_DATA]), len);
+
+ print_null(PRINT_ANY, "data", ", data:", NULL);
+ while (i < len) {
+ print_hhu(PRINT_ANY, "", " %02x", data[i]);
+ i++;
+ }
+ print_nl();
+}
+
+static int process_msg(struct nlmsghdr *n, void *arg)
+{
+ struct rtattr *attrs[IOAM6_ATTR_MAX + 1];
+ struct genlmsghdr *ghdr;
+ int len = n->nlmsg_len;
+
+ if (n->nlmsg_type != genl_family)
+ return -1;
+
+ len -= NLMSG_LENGTH(GENL_HDRLEN);
+ if (len < 0)
+ return -1;
+
+ ghdr = NLMSG_DATA(n);
+ parse_rtattr(attrs, IOAM6_ATTR_MAX, (void *)ghdr + GENL_HDRLEN, len);
+
+ open_json_object(NULL);
+ switch (ghdr->cmd) {
+ case IOAM6_CMD_DUMP_NAMESPACES:
+ print_namespace(attrs);
+ break;
+ case IOAM6_CMD_DUMP_SCHEMAS:
+ print_schema(attrs);
+ break;
+ }
+ close_json_object();
+
+ return 0;
+}
+
+static int ioam6_do_cmd(void)
+{
+ IOAM6_REQUEST(req, 1056, opts.cmd, NLM_F_REQUEST);
+ int dump = 0;
+
+ if (genl_init_handle(&grth, IOAM6_GENL_NAME, &genl_family))
+ exit(1);
+
+ req.n.nlmsg_type = genl_family;
+
+ switch (opts.cmd) {
+ case IOAM6_CMD_ADD_NAMESPACE:
+ addattr16(&req.n, sizeof(req), IOAM6_ATTR_NS_ID, opts.ns_id);
+ if (opts.has_ns_data)
+ addattr32(&req.n, sizeof(req), IOAM6_ATTR_NS_DATA,
+ opts.ns_data);
+ if (opts.has_ns_data_wide)
+ addattr64(&req.n, sizeof(req), IOAM6_ATTR_NS_DATA_WIDE,
+ opts.ns_data_wide);
+ break;
+ case IOAM6_CMD_DEL_NAMESPACE:
+ addattr16(&req.n, sizeof(req), IOAM6_ATTR_NS_ID, opts.ns_id);
+ break;
+ case IOAM6_CMD_DUMP_NAMESPACES:
+ case IOAM6_CMD_DUMP_SCHEMAS:
+ dump = 1;
+ break;
+ case IOAM6_CMD_ADD_SCHEMA:
+ addattr32(&req.n, sizeof(req), IOAM6_ATTR_SC_ID, opts.sc_id);
+ addattr_l(&req.n, sizeof(req), IOAM6_ATTR_SC_DATA, opts.sc_data,
+ strlen((const char *)opts.sc_data));
+ break;
+ case IOAM6_CMD_DEL_SCHEMA:
+ addattr32(&req.n, sizeof(req), IOAM6_ATTR_SC_ID, opts.sc_id);
+ break;
+ case IOAM6_CMD_NS_SET_SCHEMA:
+ addattr16(&req.n, sizeof(req), IOAM6_ATTR_NS_ID, opts.ns_id);
+ if (opts.sc_none)
+ addattr(&req.n, sizeof(req), IOAM6_ATTR_SC_NONE);
+ else
+ addattr32(&req.n, sizeof(req), IOAM6_ATTR_SC_ID,
+ opts.sc_id);
+ break;
+ }
+
+ if (!dump) {
+ if (rtnl_talk(&grth, &req.n, NULL) < 0)
+ return -1;
+ } else {
+ req.n.nlmsg_flags |= NLM_F_DUMP;
+ req.n.nlmsg_seq = grth.dump = ++grth.seq;
+ if (rtnl_send(&grth, &req, req.n.nlmsg_len) < 0) {
+ perror("Failed to send dump request");
+ exit(1);
+ }
+
+ new_json_obj(json);
+ if (rtnl_dump_filter(&grth, process_msg, stdout) < 0) {
+ fprintf(stderr, "Dump terminated\n");
+ exit(1);
+ }
+ delete_json_obj();
+ fflush(stdout);
+ }
+
+ return 0;
+}
+
+int do_ioam6(int argc, char **argv)
+{
+ bool maybe_wide = false;
+
+ if (argc < 1 || strcmp(*argv, "help") == 0)
+ usage();
+
+ memset(&opts, 0, sizeof(opts));
+
+ if (strcmp(*argv, "namespace") == 0) {
+ NEXT_ARG();
+
+ if (strcmp(*argv, "show") == 0) {
+ opts.cmd = IOAM6_CMD_DUMP_NAMESPACES;
+
+ } else if (strcmp(*argv, "add") == 0) {
+ NEXT_ARG();
+
+ if (get_u16(&opts.ns_id, *argv, 0))
+ invarg("Invalid namespace ID", *argv);
+
+ if (NEXT_ARG_OK()) {
+ NEXT_ARG_FWD();
+
+ if (strcmp(*argv, "data") == 0) {
+ NEXT_ARG();
+
+ if (get_u32(&opts.ns_data, *argv, 0))
+ invarg("Invalid data", *argv);
+
+ maybe_wide = true;
+ opts.has_ns_data = true;
+
+ } else if (strcmp(*argv, "wide") == 0) {
+ NEXT_ARG();
+
+ if (get_u64(&opts.ns_data_wide, *argv, 16))
+ invarg("Invalid wide data", *argv);
+
+ opts.has_ns_data_wide = true;
+
+ } else {
+ invarg("Invalid argument", *argv);
+ }
+ }
+
+ if (NEXT_ARG_OK()) {
+ NEXT_ARG_FWD();
+
+ if (!maybe_wide || strcmp(*argv, "wide") != 0)
+ invarg("Unexpected argument", *argv);
+
+ NEXT_ARG();
+
+ if (get_u64(&opts.ns_data_wide, *argv, 16))
+ invarg("Invalid wide data", *argv);
+
+ opts.has_ns_data_wide = true;
+ }
+
+ opts.cmd = IOAM6_CMD_ADD_NAMESPACE;
+
+ } else if (strcmp(*argv, "del") == 0) {
+ NEXT_ARG();
+
+ if (get_u16(&opts.ns_id, *argv, 0))
+ invarg("Invalid namespace ID", *argv);
+
+ opts.cmd = IOAM6_CMD_DEL_NAMESPACE;
+
+ } else if (strcmp(*argv, "set") == 0) {
+ NEXT_ARG();
+
+ if (get_u16(&opts.ns_id, *argv, 0))
+ invarg("Invalid namespace ID", *argv);
+
+ NEXT_ARG();
+
+ if (strcmp(*argv, "schema") != 0)
+ invarg("Unknown", *argv);
+
+ NEXT_ARG();
+
+ if (strcmp(*argv, "none") == 0) {
+ opts.sc_none = true;
+
+ } else {
+ if (get_u32(&opts.sc_id, *argv, 0))
+ invarg("Invalid schema ID", *argv);
+
+ opts.sc_none = false;
+ }
+
+ opts.cmd = IOAM6_CMD_NS_SET_SCHEMA;
+
+ } else {
+ invarg("Unknown", *argv);
+ }
+
+ } else if (strcmp(*argv, "schema") == 0) {
+ NEXT_ARG();
+
+ if (strcmp(*argv, "show") == 0) {
+ opts.cmd = IOAM6_CMD_DUMP_SCHEMAS;
+
+ } else if (strcmp(*argv, "add") == 0) {
+ NEXT_ARG();
+
+ if (get_u32(&opts.sc_id, *argv, 0))
+ invarg("Invalid schema ID", *argv);
+
+ NEXT_ARG();
+
+ if (strlen(*argv) > IOAM6_MAX_SCHEMA_DATA_LEN)
+ invarg("Schema DATA too big", *argv);
+
+ memcpy(opts.sc_data, *argv, strlen(*argv));
+ opts.cmd = IOAM6_CMD_ADD_SCHEMA;
+
+ } else if (strcmp(*argv, "del") == 0) {
+ NEXT_ARG();
+
+ if (get_u32(&opts.sc_id, *argv, 0))
+ invarg("Invalid schema ID", *argv);
+
+ opts.cmd = IOAM6_CMD_DEL_SCHEMA;
+
+ } else {
+ invarg("Unknown", *argv);
+ }
+
+ } else {
+ invarg("Unknown", *argv);
+ }
+
+ return ioam6_do_cmd();
+}