aboutsummaryrefslogtreecommitdiffstats
path: root/ip
diff options
context:
space:
mode:
authorJustin Iurman <justin.iurman@uliege.be>2021-08-01 14:45:50 +0200
committerDavid Ahern <dsahern@kernel.org>2021-08-02 11:33:05 -0600
commit29098125834c2e160c1c2e17980db022f54c7cac (patch)
tree94ec566978177a7d16dba23d97a55a0f3328ca6e /ip
parente53f4cd504214f2d7e7870c91a949dbb8d9f91d4 (diff)
downloadiproute2-29098125834c2e160c1c2e17980db022f54c7cac.tar.gz
Add, show, link, remove IOAM namespaces and schemas
This patch provides support for adding, listing and removing IOAM namespaces and schemas with iproute2. When adding an IOAM namespace, both "data" (=u32) and "wide" (=u64) are optional. Therefore, you can either have none, one of them, or both at the same time. When adding an IOAM schema, there is no restriction on "DATA" except its size (see IOAM6_MAX_SCHEMA_DATA_LEN). By default, an IOAM namespace has no active IOAM schema (meaning an IOAM namespace is not linked to an IOAM schema), and an IOAM schema is not considered as "active" (meaning an IOAM schema is not linked to an IOAM namespace). It is possible to link an IOAM namespace with an IOAM schema, thanks to the last command below (meaning the IOAM schema will be considered as "active" for the specific IOAM namespace). $ ip ioam Usage: ip ioam { COMMAND | help } ip ioam namespace show ip ioam namespace add ID [ data DATA32 ] [ wide DATA64 ] ip ioam namespace del ID ip ioam schema show ip ioam schema add ID DATA ip ioam schema del ID ip ioam namespace set ID schema { ID | none } Signed-off-by: Justin Iurman <justin.iurman@uliege.be> Signed-off-by: David Ahern <dsahern@kernel.org>
Diffstat (limited to 'ip')
-rw-r--r--ip/Makefile2
-rw-r--r--ip/ip.c3
-rw-r--r--ip/ip_common.h1
-rw-r--r--ip/ipioam6.c337
4 files changed, 341 insertions, 2 deletions
diff --git a/ip/Makefile b/ip/Makefile
index b03af29b2..2ae9df89b 100644
--- a/ip/Makefile
+++ b/ip/Makefile
@@ -11,7 +11,7 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \
iplink_bridge.o iplink_bridge_slave.o ipfou.o iplink_ipvlan.o \
iplink_geneve.o iplink_vrf.o iproute_lwtunnel.o ipmacsec.o ipila.o \
ipvrf.o iplink_xstats.o ipseg6.o iplink_netdevsim.o iplink_rmnet.o \
- ipnexthop.o ipmptcp.o iplink_bareudp.o iplink_wwan.o
+ ipnexthop.o ipmptcp.o iplink_bareudp.o iplink_wwan.o ipioam6.o
RTMONOBJ=rtmon.o
diff --git a/ip/ip.c b/ip/ip.c
index 8e4c6eb58..e7ffeaff3 100644
--- a/ip/ip.c
+++ b/ip/ip.c
@@ -64,7 +64,7 @@ static void usage(void)
fprintf(stderr,
"Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }\n"
" ip [ -force ] -batch filename\n"
- "where OBJECT := { address | addrlabel | fou | help | ila | l2tp | link |\n"
+ "where OBJECT := { address | addrlabel | fou | help | ila | ioam | l2tp | link |\n"
" macsec | maddress | monitor | mptcp | mroute | mrule |\n"
" neighbor | neighbour | netconf | netns | nexthop | ntable |\n"
" ntbl | route | rule | sr | tap | tcpmetrics |\n"
@@ -121,6 +121,7 @@ static const struct cmd {
{ "sr", do_seg6 },
{ "nexthop", do_ipnh },
{ "mptcp", do_mptcp },
+ { "ioam", do_ioam6 },
{ "help", do_help },
{ 0 }
};
diff --git a/ip/ip_common.h b/ip/ip_common.h
index b5b2b082b..ad018183e 100644
--- a/ip/ip_common.h
+++ b/ip/ip_common.h
@@ -90,6 +90,7 @@ int netns_identify_pid(const char *pidstr, char *name, int len);
int do_seg6(int argc, char **argv);
int do_ipnh(int argc, char **argv);
int do_mptcp(int argc, char **argv);
+int do_ioam6(int argc, char **argv);
int iplink_get(char *name, __u32 filt_mask);
int iplink_ifla_xstats(int argc, char **argv);
diff --git a/ip/ipioam6.c b/ip/ipioam6.c
new file mode 100644
index 000000000..253d0b663
--- /dev/null
+++ b/ip/ipioam6.c
@@ -0,0 +1,337 @@
+// 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_null(PRINT_ANY, "data", "", NULL);
+ else
+ print_hex(PRINT_ANY, "data", ", data %#010x",
+ rta_getattr_u32(attrs[IOAM6_ATTR_NS_DATA]));
+
+ if (!attrs[IOAM6_ATTR_NS_DATA_WIDE])
+ print_null(PRINT_ANY, "wide", "", NULL);
+ else
+ print_0xhex(PRINT_ANY, "wide", ", wide %#018lx",
+ rta_getattr_u64(attrs[IOAM6_ATTR_NS_DATA_WIDE]));
+
+ print_null(PRINT_ANY, "", "\n", NULL);
+}
+
+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_null(PRINT_ANY, "", "\n", NULL);
+}
+
+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();
+}