Merge branch 'ipv6-oam' into next
Justin Iurman says:
====================
The IOAM patchset was merged recently (see net-next commits [1,2,3,4,5,6]).
Therefore, this patchset provides support for IOAM inside iproute2, as well as
manpage documentation. Here is a summary of added features inside iproute2.
(1) configure IOAM namespaces and schemas:
$ 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 }
(2) provide a new encap type to insert the IOAM pre-allocated trace:
$ ip -6 ro ad fc00::1/128 encap ioam6 trace prealloc type 0x800000 ns 1 size 12 dev eth0
[1] db67f219fc9365a0c456666ed7c134d43ab0be8a
[2] 9ee11f0fff205b4b3df9750bff5e94f97c71b6a0
[3] 8c6f6fa6772696be0c047a711858084b38763728
[4] 3edede08ff37c6a9370510508d5eeb54890baf47
[5] de8e80a54c96d2b75377e0e5319a64d32c88c690
[6] 968691c777af78d2daa2ee87cfaeeae825255a58
====================
Signed-off-by: David Ahern <dsahern@kernel.org>
This commit is contained in:
commit
e8763fc9ab
|
|
@ -0,0 +1,133 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
|
||||
/*
|
||||
* IPv6 IOAM implementation
|
||||
*
|
||||
* Author:
|
||||
* Justin Iurman <justin.iurman@uliege.be>
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_IOAM6_H
|
||||
#define _LINUX_IOAM6_H
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define IOAM6_U16_UNAVAILABLE U16_MAX
|
||||
#define IOAM6_U32_UNAVAILABLE U32_MAX
|
||||
#define IOAM6_U64_UNAVAILABLE U64_MAX
|
||||
|
||||
#define IOAM6_DEFAULT_ID (IOAM6_U32_UNAVAILABLE >> 8)
|
||||
#define IOAM6_DEFAULT_ID_WIDE (IOAM6_U64_UNAVAILABLE >> 8)
|
||||
#define IOAM6_DEFAULT_IF_ID IOAM6_U16_UNAVAILABLE
|
||||
#define IOAM6_DEFAULT_IF_ID_WIDE IOAM6_U32_UNAVAILABLE
|
||||
|
||||
/*
|
||||
* IPv6 IOAM Option Header
|
||||
*/
|
||||
struct ioam6_hdr {
|
||||
__u8 opt_type;
|
||||
__u8 opt_len;
|
||||
__u8 :8; /* reserved */
|
||||
#define IOAM6_TYPE_PREALLOC 0
|
||||
__u8 type;
|
||||
} __attribute__((packed));
|
||||
|
||||
/*
|
||||
* IOAM Trace Header
|
||||
*/
|
||||
struct ioam6_trace_hdr {
|
||||
__be16 namespace_id;
|
||||
|
||||
#if defined(__LITTLE_ENDIAN_BITFIELD)
|
||||
|
||||
__u8 :1, /* unused */
|
||||
:1, /* unused */
|
||||
overflow:1,
|
||||
nodelen:5;
|
||||
|
||||
__u8 remlen:7,
|
||||
:1; /* unused */
|
||||
|
||||
union {
|
||||
__be32 type_be32;
|
||||
|
||||
struct {
|
||||
__u32 bit7:1,
|
||||
bit6:1,
|
||||
bit5:1,
|
||||
bit4:1,
|
||||
bit3:1,
|
||||
bit2:1,
|
||||
bit1:1,
|
||||
bit0:1,
|
||||
bit15:1, /* unused */
|
||||
bit14:1, /* unused */
|
||||
bit13:1, /* unused */
|
||||
bit12:1, /* unused */
|
||||
bit11:1,
|
||||
bit10:1,
|
||||
bit9:1,
|
||||
bit8:1,
|
||||
bit23:1, /* reserved */
|
||||
bit22:1,
|
||||
bit21:1, /* unused */
|
||||
bit20:1, /* unused */
|
||||
bit19:1, /* unused */
|
||||
bit18:1, /* unused */
|
||||
bit17:1, /* unused */
|
||||
bit16:1, /* unused */
|
||||
:8; /* reserved */
|
||||
} type;
|
||||
};
|
||||
|
||||
#elif defined(__BIG_ENDIAN_BITFIELD)
|
||||
|
||||
__u8 nodelen:5,
|
||||
overflow:1,
|
||||
:1, /* unused */
|
||||
:1; /* unused */
|
||||
|
||||
__u8 :1, /* unused */
|
||||
remlen:7;
|
||||
|
||||
union {
|
||||
__be32 type_be32;
|
||||
|
||||
struct {
|
||||
__u32 bit0:1,
|
||||
bit1:1,
|
||||
bit2:1,
|
||||
bit3:1,
|
||||
bit4:1,
|
||||
bit5:1,
|
||||
bit6:1,
|
||||
bit7:1,
|
||||
bit8:1,
|
||||
bit9:1,
|
||||
bit10:1,
|
||||
bit11:1,
|
||||
bit12:1, /* unused */
|
||||
bit13:1, /* unused */
|
||||
bit14:1, /* unused */
|
||||
bit15:1, /* unused */
|
||||
bit16:1, /* unused */
|
||||
bit17:1, /* unused */
|
||||
bit18:1, /* unused */
|
||||
bit19:1, /* unused */
|
||||
bit20:1, /* unused */
|
||||
bit21:1, /* unused */
|
||||
bit22:1,
|
||||
bit23:1, /* reserved */
|
||||
:8; /* reserved */
|
||||
} type;
|
||||
};
|
||||
|
||||
#else
|
||||
#error "Please fix <asm/byteorder.h>"
|
||||
#endif
|
||||
|
||||
#define IOAM6_TRACE_DATA_SIZE_MAX 244
|
||||
__u8 data[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
#endif /* _LINUX_IOAM6_H */
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
|
||||
/*
|
||||
* IPv6 IOAM Generic Netlink API
|
||||
*
|
||||
* Author:
|
||||
* Justin Iurman <justin.iurman@uliege.be>
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_IOAM6_GENL_H
|
||||
#define _LINUX_IOAM6_GENL_H
|
||||
|
||||
#define IOAM6_GENL_NAME "IOAM6"
|
||||
#define IOAM6_GENL_VERSION 0x1
|
||||
|
||||
enum {
|
||||
IOAM6_ATTR_UNSPEC,
|
||||
|
||||
IOAM6_ATTR_NS_ID, /* u16 */
|
||||
IOAM6_ATTR_NS_DATA, /* u32 */
|
||||
IOAM6_ATTR_NS_DATA_WIDE,/* u64 */
|
||||
|
||||
#define IOAM6_MAX_SCHEMA_DATA_LEN (255 * 4)
|
||||
IOAM6_ATTR_SC_ID, /* u32 */
|
||||
IOAM6_ATTR_SC_DATA, /* Binary */
|
||||
IOAM6_ATTR_SC_NONE, /* Flag */
|
||||
|
||||
IOAM6_ATTR_PAD,
|
||||
|
||||
__IOAM6_ATTR_MAX,
|
||||
};
|
||||
|
||||
#define IOAM6_ATTR_MAX (__IOAM6_ATTR_MAX - 1)
|
||||
|
||||
enum {
|
||||
IOAM6_CMD_UNSPEC,
|
||||
|
||||
IOAM6_CMD_ADD_NAMESPACE,
|
||||
IOAM6_CMD_DEL_NAMESPACE,
|
||||
IOAM6_CMD_DUMP_NAMESPACES,
|
||||
|
||||
IOAM6_CMD_ADD_SCHEMA,
|
||||
IOAM6_CMD_DEL_SCHEMA,
|
||||
IOAM6_CMD_DUMP_SCHEMAS,
|
||||
|
||||
IOAM6_CMD_NS_SET_SCHEMA,
|
||||
|
||||
__IOAM6_CMD_MAX,
|
||||
};
|
||||
|
||||
#define IOAM6_CMD_MAX (__IOAM6_CMD_MAX - 1)
|
||||
|
||||
#endif /* _LINUX_IOAM6_GENL_H */
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
|
||||
/*
|
||||
* IPv6 IOAM Lightweight Tunnel API
|
||||
*
|
||||
* Author:
|
||||
* Justin Iurman <justin.iurman@uliege.be>
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_IOAM6_IPTUNNEL_H
|
||||
#define _LINUX_IOAM6_IPTUNNEL_H
|
||||
|
||||
enum {
|
||||
IOAM6_IPTUNNEL_UNSPEC,
|
||||
IOAM6_IPTUNNEL_TRACE, /* struct ioam6_trace_hdr */
|
||||
__IOAM6_IPTUNNEL_MAX,
|
||||
};
|
||||
|
||||
#define IOAM6_IPTUNNEL_MAX (__IOAM6_IPTUNNEL_MAX - 1)
|
||||
|
||||
#endif /* _LINUX_IOAM6_IPTUNNEL_H */
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
3
ip/ip.c
3
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 }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -101,8 +101,8 @@ static void usage(void)
|
|||
"TIME := NUMBER[s|ms]\n"
|
||||
"BOOL := [1|0]\n"
|
||||
"FEATURES := ecn\n"
|
||||
"ENCAPTYPE := [ mpls | ip | ip6 | seg6 | seg6local | rpl ]\n"
|
||||
"ENCAPHDR := [ MPLSLABEL | SEG6HDR | SEG6LOCAL ]\n"
|
||||
"ENCAPTYPE := [ mpls | ip | ip6 | seg6 | seg6local | rpl | ioam6 ]\n"
|
||||
"ENCAPHDR := [ MPLSLABEL | SEG6HDR | SEG6LOCAL | IOAM6HDR ]\n"
|
||||
"SEG6HDR := [ mode SEGMODE ] segs ADDR1,ADDRi,ADDRn [hmac HMACKEYID] [cleanup]\n"
|
||||
"SEGMODE := [ encap | inline ]\n"
|
||||
"SEG6LOCAL := action ACTION [ OPTIONS ] [ count ]\n"
|
||||
|
|
@ -112,6 +112,7 @@ static void usage(void)
|
|||
"OPTIONS := OPTION [ OPTIONS ]\n"
|
||||
"OPTION := { srh SEG6HDR | nh4 ADDR | nh6 ADDR | iif DEV | oif DEV |\n"
|
||||
" table TABLEID | vrftable TABLEID | endpoint PROGNAME }\n"
|
||||
"IOAM6HDR := trace prealloc type IOAM6_TRACE_TYPE ns IOAM6_NAMESPACE size IOAM6_TRACE_SIZE\n"
|
||||
"ROUTE_GET_FLAGS := [ fibmatch ]\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@
|
|||
#include <linux/seg6_hmac.h>
|
||||
#include <linux/seg6_local.h>
|
||||
#include <linux/if_tunnel.h>
|
||||
#include <linux/ioam6.h>
|
||||
#include <linux/ioam6_iptunnel.h>
|
||||
|
||||
static const char *format_encap_type(int type)
|
||||
{
|
||||
|
|
@ -54,6 +56,8 @@ static const char *format_encap_type(int type)
|
|||
return "seg6local";
|
||||
case LWTUNNEL_ENCAP_RPL:
|
||||
return "rpl";
|
||||
case LWTUNNEL_ENCAP_IOAM6:
|
||||
return "ioam6";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
|
|
@ -90,6 +94,8 @@ static int read_encap_type(const char *name)
|
|||
return LWTUNNEL_ENCAP_SEG6_LOCAL;
|
||||
else if (strcmp(name, "rpl") == 0)
|
||||
return LWTUNNEL_ENCAP_RPL;
|
||||
else if (strcmp(name, "ioam6") == 0)
|
||||
return LWTUNNEL_ENCAP_IOAM6;
|
||||
else if (strcmp(name, "help") == 0)
|
||||
encap_type_usage();
|
||||
|
||||
|
|
@ -204,6 +210,25 @@ static void print_encap_rpl(FILE *fp, struct rtattr *encap)
|
|||
print_rpl_srh(fp, srh);
|
||||
}
|
||||
|
||||
static void print_encap_ioam6(FILE *fp, struct rtattr *encap)
|
||||
{
|
||||
struct rtattr *tb[IOAM6_IPTUNNEL_MAX + 1];
|
||||
struct ioam6_trace_hdr *trace;
|
||||
|
||||
parse_rtattr_nested(tb, IOAM6_IPTUNNEL_MAX, encap);
|
||||
|
||||
if (!tb[IOAM6_IPTUNNEL_TRACE])
|
||||
return;
|
||||
|
||||
trace = RTA_DATA(tb[IOAM6_IPTUNNEL_TRACE]);
|
||||
|
||||
print_null(PRINT_ANY, "trace", "trace ", NULL);
|
||||
print_null(PRINT_ANY, "prealloc", "prealloc ", NULL);
|
||||
print_hex(PRINT_ANY, "type", "type %#08x ", ntohl(trace->type_be32) >> 8);
|
||||
print_uint(PRINT_ANY, "ns", "ns %u ", ntohs(trace->namespace_id));
|
||||
print_uint(PRINT_ANY, "size", "size %u ", trace->remlen * 4);
|
||||
}
|
||||
|
||||
static const char *seg6_action_names[SEG6_LOCAL_ACTION_MAX + 1] = {
|
||||
[SEG6_LOCAL_ACTION_END] = "End",
|
||||
[SEG6_LOCAL_ACTION_END_X] = "End.X",
|
||||
|
|
@ -657,6 +682,9 @@ void lwt_print_encap(FILE *fp, struct rtattr *encap_type,
|
|||
case LWTUNNEL_ENCAP_RPL:
|
||||
print_encap_rpl(fp, encap);
|
||||
break;
|
||||
case LWTUNNEL_ENCAP_IOAM6:
|
||||
print_encap_ioam6(fp, encap);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -853,6 +881,102 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int parse_encap_ioam6(struct rtattr *rta, size_t len, int *argcp,
|
||||
char ***argvp)
|
||||
{
|
||||
struct ioam6_trace_hdr *trace;
|
||||
char **argv = *argvp;
|
||||
int argc = *argcp;
|
||||
int ns_found = 0;
|
||||
__u16 size = 0;
|
||||
__u32 type = 0;
|
||||
__u16 ns;
|
||||
|
||||
trace = calloc(1, sizeof(*trace));
|
||||
if (!trace)
|
||||
return -1;
|
||||
|
||||
if (strcmp(*argv, "trace"))
|
||||
missarg("trace");
|
||||
|
||||
NEXT_ARG();
|
||||
if (strcmp(*argv, "prealloc"))
|
||||
missarg("prealloc");
|
||||
|
||||
while (NEXT_ARG_OK()) {
|
||||
NEXT_ARG_FWD();
|
||||
|
||||
if (strcmp(*argv, "type") == 0) {
|
||||
NEXT_ARG();
|
||||
|
||||
if (type)
|
||||
duparg2("type", *argv);
|
||||
|
||||
if (get_u32(&type, *argv, 0) || !type)
|
||||
invarg("Invalid type", *argv);
|
||||
|
||||
trace->type_be32 = htonl(type << 8);
|
||||
|
||||
} else if (strcmp(*argv, "ns") == 0) {
|
||||
NEXT_ARG();
|
||||
|
||||
if (ns_found++)
|
||||
duparg2("ns", *argv);
|
||||
|
||||
if (!type)
|
||||
missarg("type");
|
||||
|
||||
if (get_u16(&ns, *argv, 0))
|
||||
invarg("Invalid namespace ID", *argv);
|
||||
|
||||
trace->namespace_id = htons(ns);
|
||||
|
||||
} else if (strcmp(*argv, "size") == 0) {
|
||||
NEXT_ARG();
|
||||
|
||||
if (size)
|
||||
duparg2("size", *argv);
|
||||
|
||||
if (!type)
|
||||
missarg("type");
|
||||
if (!ns_found)
|
||||
missarg("ns");
|
||||
|
||||
if (get_u16(&size, *argv, 0) || !size)
|
||||
invarg("Invalid size", *argv);
|
||||
|
||||
if (size % 4)
|
||||
invarg("Size must be a 4-octet multiple", *argv);
|
||||
if (size > IOAM6_TRACE_DATA_SIZE_MAX)
|
||||
invarg("Size too big", *argv);
|
||||
|
||||
trace->remlen = (__u8)(size / 4);
|
||||
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!type)
|
||||
missarg("type");
|
||||
if (!ns_found)
|
||||
missarg("ns");
|
||||
if (!size)
|
||||
missarg("size");
|
||||
|
||||
if (rta_addattr_l(rta, len, IOAM6_IPTUNNEL_TRACE, trace,
|
||||
sizeof(*trace))) {
|
||||
free(trace);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*argcp = argc + 1;
|
||||
*argvp = argv - 1;
|
||||
|
||||
free(trace);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct lwt_x {
|
||||
struct rtattr *rta;
|
||||
size_t len;
|
||||
|
|
@ -1744,6 +1868,9 @@ int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp,
|
|||
case LWTUNNEL_ENCAP_RPL:
|
||||
ret = parse_encap_rpl(rta, len, &argc, &argv);
|
||||
break;
|
||||
case LWTUNNEL_ENCAP_IOAM6:
|
||||
ret = parse_encap_ioam6(rta, len, &argc, &argv);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Error: unsupported encap type\n");
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
.TH IP\-IOAM 8 "05 Jul 2021" "iproute2" "Linux"
|
||||
.SH "NAME"
|
||||
ip-ioam \- IPv6 In-situ OAM (IOAM)
|
||||
.SH SYNOPSIS
|
||||
.sp
|
||||
.ad l
|
||||
.in +8
|
||||
.ti -8
|
||||
.B ip ioam
|
||||
.RI " { " COMMAND " | "
|
||||
.BR help " }"
|
||||
.sp
|
||||
.ti -8
|
||||
|
||||
.ti -8
|
||||
.B ip ioam namespace show
|
||||
|
||||
.ti -8
|
||||
.B ip ioam namespace add
|
||||
.I ID
|
||||
.BR " [ "
|
||||
.B data
|
||||
.I DATA32
|
||||
.BR "]"
|
||||
.BR " [ "
|
||||
.B wide
|
||||
.I DATA64
|
||||
.BR "]"
|
||||
|
||||
.ti -8
|
||||
.B ip ioam namespace del
|
||||
.I ID
|
||||
|
||||
.ti -8
|
||||
.B ip ioam schema show
|
||||
|
||||
.ti -8
|
||||
.B ip ioam schema add
|
||||
.I ID DATA
|
||||
|
||||
.ti -8
|
||||
.B ip ioam schema del
|
||||
.I ID
|
||||
|
||||
.ti -8
|
||||
.B ip ioam namespace set
|
||||
.I ID
|
||||
.B schema
|
||||
.RI " { " ID " | "
|
||||
.BR none " }"
|
||||
|
||||
.SH DESCRIPTION
|
||||
The \fBip ioam\fR command is used to configure IPv6 In-situ OAM (IOAM6)
|
||||
internal parameters, namely IOAM namespaces and schemas.
|
||||
.PP
|
||||
Those parameters also include the mapping between an IOAM namespace and an IOAM
|
||||
schema.
|
||||
|
||||
.SH EXAMPLES
|
||||
.PP
|
||||
.SS Configure an IOAM namespace (ID = 1) with both data (32 bits) and wide data (64 bits)
|
||||
.nf
|
||||
# ip ioam namespace add 1 data 0xdeadbeef wide 0xcafec0caf00dc0de
|
||||
.PP
|
||||
.SS Link an existing IOAM schema (ID = 7) to an existing IOAM namespace (ID = 1)
|
||||
.nf
|
||||
# ip ioam namespace set 1 schema 7
|
||||
.SH SEE ALSO
|
||||
.br
|
||||
.BR ip-route (8)
|
||||
.SH AUTHOR
|
||||
Justin Iurman <justin.iurman@uliege.be>
|
||||
|
|
@ -190,7 +190,7 @@ throw " | " unreachable " | " prohibit " | " blackhole " | " nat " ]"
|
|||
.ti -8
|
||||
.IR ENCAP " := [ "
|
||||
.IR ENCAP_MPLS " | " ENCAP_IP " | " ENCAP_BPF " | "
|
||||
.IR ENCAP_SEG6 " | " ENCAP_SEG6LOCAL " ] "
|
||||
.IR ENCAP_SEG6 " | " ENCAP_SEG6LOCAL " | " ENCAP_IOAM6 " ] "
|
||||
|
||||
.ti -8
|
||||
.IR ENCAP_MPLS " := "
|
||||
|
|
@ -243,6 +243,18 @@ throw " | " unreachable " | " prohibit " | " blackhole " | " nat " ]"
|
|||
.IR SEG6_ACTION_PARAM " ] [ "
|
||||
.BR count " ] "
|
||||
|
||||
.ti -8
|
||||
.IR ENCAP_IOAM6 " := "
|
||||
.B ioam6
|
||||
.BR trace
|
||||
.BR prealloc
|
||||
.BR type
|
||||
.IR IOAM6_TRACE_TYPE
|
||||
.BR ns
|
||||
.IR IOAM6_NAMESPACE
|
||||
.BR size
|
||||
.IR IOAM6_TRACE_SIZE
|
||||
|
||||
.ti -8
|
||||
.IR ROUTE_GET_FLAGS " := "
|
||||
.BR " [ "
|
||||
|
|
@ -717,6 +729,9 @@ is a string specifying the supported encapsulation type. Namely:
|
|||
.sp
|
||||
.BI seg6local
|
||||
- local SRv6 segment processing
|
||||
.sp
|
||||
.BI ioam6
|
||||
- encapsulation type IPv6 IOAM
|
||||
|
||||
.in -8
|
||||
.I ENCAPHDR
|
||||
|
|
@ -896,6 +911,20 @@ Additionally, encapsulate the matching packet within an outer IPv6 header
|
|||
followed by the specified SRH. The destination address of the outer IPv6
|
||||
header is set to the first segment of the new SRH. The source
|
||||
address is set as described in \fBip-sr\fR(8).
|
||||
.in -2
|
||||
|
||||
.B ioam6
|
||||
.in +2
|
||||
.I IOAM6_TRACE_TYPE
|
||||
- List of IOAM data required in the trace, represented by a bitfield (24 bits).
|
||||
.sp
|
||||
|
||||
.I IOAM6_NAMESPACE
|
||||
- Numerical value to represent an IOAM namespace. See \fBip-ioam\fR(8).
|
||||
.sp
|
||||
|
||||
.I IOAM6_TRACE_SIZE
|
||||
- Size, in octets, of the pre-allocated trace data block.
|
||||
.in -4
|
||||
|
||||
.in -8
|
||||
|
|
@ -1220,6 +1249,11 @@ ip -6 route add 2001:db8:1::/64 encap seg6local action End.DT46 vrftable 100 dev
|
|||
Adds an IPv6 route with SRv6 decapsulation and forward with lookup in VRF table.
|
||||
.RE
|
||||
.PP
|
||||
ip -6 route add 2001:db8:1::/64 encap ioam6 trace prealloc type 0x800000 ns 1 size 12 dev eth0
|
||||
.RS 4
|
||||
Adds an IPv6 route with an IOAM Pre-allocated Trace encapsulation that only includes the hop limit and the node id, configured for the IOAM namespace 1 and a pre-allocated data block of 12 octets.
|
||||
.RE
|
||||
.PP
|
||||
ip route add 10.1.1.0/30 nhid 10
|
||||
.RS 4
|
||||
Adds an ipv4 route using nexthop object with id 10.
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ ip \- show / manipulate routing, network devices, interfaces and tunnels
|
|||
.BR link " | " address " | " addrlabel " | " route " | " rule " | " neigh " | "\
|
||||
ntable " | " tunnel " | " tuntap " | " maddress " | " mroute " | " mrule " | "\
|
||||
monitor " | " xfrm " | " netns " | " l2tp " | " tcp_metrics " | " token " | "\
|
||||
macsec " | " vrf " | " mptcp " }"
|
||||
macsec " | " vrf " | " mptcp " | " ioam " }"
|
||||
.sp
|
||||
|
||||
.ti -8
|
||||
|
|
@ -252,6 +252,10 @@ readability.
|
|||
.B addrlabel
|
||||
- label configuration for protocol address selection.
|
||||
|
||||
.TP
|
||||
.B ioam
|
||||
- manage IOAM namespaces and IOAM schemas.
|
||||
|
||||
.TP
|
||||
.B l2tp
|
||||
- tunnel ethernet over IP (L2TPv3).
|
||||
|
|
@ -405,6 +409,7 @@ was written by Alexey N. Kuznetsov and added in Linux 2.2.
|
|||
.SH SEE ALSO
|
||||
.BR ip-address (8),
|
||||
.BR ip-addrlabel (8),
|
||||
.BR ip-ioam (8),
|
||||
.BR ip-l2tp (8),
|
||||
.BR ip-link (8),
|
||||
.BR ip-maddress (8),
|
||||
|
|
|
|||
Loading…
Reference in New Issue