Commit 968691c7 authored by Justin Iurman's avatar Justin Iurman Committed by David S. Miller

selftests: net: Test for the IOAM insertion with IPv6

This test evaluates the IOAM insertion for IPv6 by checking the IOAM data
integrity on the receiver.

The topology is formed by 3 nodes: Alpha (sender), Beta (router in-between)
and Gamma (receiver). An IOAM domain is configured from Alpha to Gamma only,
which means not on the reverse path. When Gamma is the destination, Alpha
adds an IOAM option (Pre-allocated Trace) inside a Hop-by-hop and fills the
trace with its own IOAM data. Beta and Gamma also fill the trace. The IOAM
data integrity is checked on Gamma, by comparing with the pre-defined IOAM
configuration (see below).

    +-------------------+            +-------------------+
    |                   |            |                   |
    |    alpha netns    |            |    gamma netns    |
    |                   |            |                   |
    |  +-------------+  |            |  +-------------+  |
    |  |    veth0    |  |            |  |    veth0    |  |
    |  |  db01::2/64 |  |            |  |  db02::2/64 |  |
    |  +-------------+  |            |  +-------------+  |
    |         .         |            |         .         |
    +-------------------+            +-------------------+
              .                                .
              .                                .
              .                                .
    +----------------------------------------------------+
    |         .                                .         |
    |  +-------------+                  +-------------+  |
    |  |    veth0    |                  |    veth1    |  |
    |  |  db01::1/64 | ................ |  db02::1/64 |  |
    |  +-------------+                  +-------------+  |
    |                                                    |
    |                      beta netns                    |
    |                                                    |
    +--------------------------+-------------------------+

~~~~~~~~~~~~~~~~~~~~~~
| IOAM configuration |
~~~~~~~~~~~~~~~~~~~~~~

Alpha
+-----------------------------------------------------------+
| Type                | Value                               |
+-----------------------------------------------------------+
| Node ID             | 1                                   |
+-----------------------------------------------------------+
| Node Wide ID        | 11111111                            |
+-----------------------------------------------------------+
| Ingress ID          | 0xffff (default value)              |
+-----------------------------------------------------------+
| Ingress Wide ID     | 0xffffffff (default value)          |
+-----------------------------------------------------------+
| Egress ID           | 101                                 |
+-----------------------------------------------------------+
| Egress Wide ID      | 101101                              |
+-----------------------------------------------------------+
| Namespace Data      | 0xdeadbee0                          |
+-----------------------------------------------------------+
| Namespace Wide Data | 0xcafec0caf00dc0de                  |
+-----------------------------------------------------------+
| Schema ID           | 777                                 |
+-----------------------------------------------------------+
| Schema Data         | something that will be 4n-aligned   |
+-----------------------------------------------------------+

Note: When Gamma is the destination, Alpha adds an IOAM Pre-allocated Trace
      option inside a Hop-by-hop, where 164 bytes are pre-allocated for the
      trace, with 123 as the IOAM-Namespace and with 0xfff00200 as the trace
      type (= all available options at this time). As a result, and based on
      IOAM configurations here, only both Alpha and Beta should be capable of
      inserting their IOAM data while Gamma won't have enough space and will
      set the overflow bit.

Beta
+-----------------------------------------------------------+
| Type                | Value                               |
+-----------------------------------------------------------+
| Node ID             | 2                                   |
+-----------------------------------------------------------+
| Node Wide ID        | 22222222                            |
+-----------------------------------------------------------+
| Ingress ID          | 201                                 |
+-----------------------------------------------------------+
| Ingress Wide ID     | 201201                              |
+-----------------------------------------------------------+
| Egress ID           | 202                                 |
+-----------------------------------------------------------+
| Egress Wide ID      | 202202                              |
+-----------------------------------------------------------+
| Namespace Data      | 0xdeadbee1                          |
+-----------------------------------------------------------+
| Namespace Wide Data | 0xcafec0caf11dc0de                  |
+-----------------------------------------------------------+
| Schema ID           | 0xffffff (= None)                   |
+-----------------------------------------------------------+
| Schema Data         |                                     |
+-----------------------------------------------------------+

Gamma
+-----------------------------------------------------------+
| Type                | Value                               |
+-----------------------------------------------------------+
| Node ID             | 3                                   |
+-----------------------------------------------------------+
| Node Wide ID        | 33333333                            |
+-----------------------------------------------------------+
| Ingress ID          | 301                                 |
+-----------------------------------------------------------+
| Ingress Wide ID     | 301301                              |
+-----------------------------------------------------------+
| Egress ID           | 0xffff (default value)              |
+-----------------------------------------------------------+
| Egress Wide ID      | 0xffffffff (default value)          |
+-----------------------------------------------------------+
| Namespace Data      | 0xdeadbee2                          |
+-----------------------------------------------------------+
| Namespace Wide Data | 0xcafec0caf22dc0de                  |
+-----------------------------------------------------------+
| Schema ID           | 0xffffff (= None)                   |
+-----------------------------------------------------------+
| Schema Data         |                                     |
+-----------------------------------------------------------+
Signed-off-by: default avatarJustin Iurman <justin.iurman@uliege.be>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent de8e80a5
......@@ -25,6 +25,7 @@ TEST_PROGS += bareudp.sh
TEST_PROGS += unicast_extensions.sh
TEST_PROGS += udpgro_fwd.sh
TEST_PROGS += veth.sh
TEST_PROGS += ioam6.sh
TEST_PROGS_EXTENDED := in_netns.sh
TEST_GEN_FILES = socket nettest
TEST_GEN_FILES += psock_fanout psock_tpacket msg_zerocopy reuseport_addr_any
......@@ -36,6 +37,7 @@ TEST_GEN_FILES += fin_ack_lat
TEST_GEN_FILES += reuseaddr_ports_exhausted
TEST_GEN_FILES += hwtstamp_config rxtimestamp timestamping txtimestamp
TEST_GEN_FILES += ipsec
TEST_GEN_FILES += ioam6_parser
TEST_GEN_PROGS = reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa
TEST_GEN_PROGS += reuseport_dualstack reuseaddr_conflict tls
......
......@@ -42,3 +42,4 @@ CONFIG_NET_CLS_FLOWER=m
CONFIG_NET_ACT_TUNNEL_KEY=m
CONFIG_NET_ACT_MIRRED=m
CONFIG_BAREUDP=m
CONFIG_IPV6_IOAM6_LWTUNNEL=y
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0+
/*
* Author: Justin Iurman (justin.iurman@uliege.be)
*
* IOAM parser for IPv6, see ioam6.sh for details.
*/
#include <asm/byteorder.h>
#include <linux/const.h>
#include <linux/if_ether.h>
#include <linux/ioam6.h>
#include <linux/ipv6.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
struct node_args {
__u32 id;
__u64 wide;
__u16 ingr_id;
__u16 egr_id;
__u32 ingr_wide;
__u32 egr_wide;
__u32 ns_data;
__u64 ns_wide;
__u32 sc_id;
__u8 hop_limit;
__u8 *sc_data; /* NULL when sc_id = 0xffffff (default empty value) */
};
/* expected args per node, in that order */
enum {
NODE_ARG_HOP_LIMIT,
NODE_ARG_ID,
NODE_ARG_WIDE,
NODE_ARG_INGR_ID,
NODE_ARG_INGR_WIDE,
NODE_ARG_EGR_ID,
NODE_ARG_EGR_WIDE,
NODE_ARG_NS_DATA,
NODE_ARG_NS_WIDE,
NODE_ARG_SC_ID,
__NODE_ARG_MAX,
};
#define NODE_ARGS_SIZE __NODE_ARG_MAX
struct args {
__u16 ns_id;
__u32 trace_type;
__u8 n_node;
__u8 *ifname;
struct node_args node[0];
};
/* expected args, in that order */
enum {
ARG_IFNAME,
ARG_N_NODE,
ARG_NS_ID,
ARG_TRACE_TYPE,
__ARG_MAX,
};
#define ARGS_SIZE __ARG_MAX
int check_ioam6_node_data(__u8 **p, struct ioam6_trace_hdr *trace, __u8 hlim,
__u32 id, __u64 wide, __u16 ingr_id, __u32 ingr_wide,
__u16 egr_id, __u32 egr_wide, __u32 ns_data,
__u64 ns_wide, __u32 sc_id, __u8 *sc_data)
{
__u64 raw64;
__u32 raw32;
__u8 sc_len;
if (trace->type.bit0) {
raw32 = __be32_to_cpu(*((__u32 *)*p));
if (hlim != (raw32 >> 24) || id != (raw32 & 0xffffff))
return 1;
*p += sizeof(__u32);
}
if (trace->type.bit1) {
raw32 = __be32_to_cpu(*((__u32 *)*p));
if (ingr_id != (raw32 >> 16) || egr_id != (raw32 & 0xffff))
return 1;
*p += sizeof(__u32);
}
if (trace->type.bit2)
*p += sizeof(__u32);
if (trace->type.bit3)
*p += sizeof(__u32);
if (trace->type.bit4) {
if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
return 1;
*p += sizeof(__u32);
}
if (trace->type.bit5) {
if (__be32_to_cpu(*((__u32 *)*p)) != ns_data)
return 1;
*p += sizeof(__u32);
}
if (trace->type.bit6) {
if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
return 1;
*p += sizeof(__u32);
}
if (trace->type.bit7) {
if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
return 1;
*p += sizeof(__u32);
}
if (trace->type.bit8) {
raw64 = __be64_to_cpu(*((__u64 *)*p));
if (hlim != (raw64 >> 56) || wide != (raw64 & 0xffffffffffffff))
return 1;
*p += sizeof(__u64);
}
if (trace->type.bit9) {
if (__be32_to_cpu(*((__u32 *)*p)) != ingr_wide)
return 1;
*p += sizeof(__u32);
if (__be32_to_cpu(*((__u32 *)*p)) != egr_wide)
return 1;
*p += sizeof(__u32);
}
if (trace->type.bit10) {
if (__be64_to_cpu(*((__u64 *)*p)) != ns_wide)
return 1;
*p += sizeof(__u64);
}
if (trace->type.bit11) {
if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
return 1;
*p += sizeof(__u32);
}
if (trace->type.bit22) {
raw32 = __be32_to_cpu(*((__u32 *)*p));
sc_len = sc_data ? __ALIGN_KERNEL(strlen(sc_data), 4) : 0;
if (sc_len != (raw32 >> 24) * 4 || sc_id != (raw32 & 0xffffff))
return 1;
*p += sizeof(__u32);
if (sc_data) {
if (strncmp(*p, sc_data, strlen(sc_data)))
return 1;
*p += strlen(sc_data);
sc_len -= strlen(sc_data);
while (sc_len--) {
if (**p != '\0')
return 1;
*p += sizeof(__u8);
}
}
}
return 0;
}
int check_ioam6_trace(struct ioam6_trace_hdr *trace, struct args *args)
{
__u8 *p;
int i;
if (__be16_to_cpu(trace->namespace_id) != args->ns_id ||
__be32_to_cpu(trace->type_be32) != args->trace_type)
return 1;
p = trace->data + trace->remlen * 4;
for (i = args->n_node - 1; i >= 0; i--) {
if (check_ioam6_node_data(&p, trace,
args->node[i].hop_limit,
args->node[i].id,
args->node[i].wide,
args->node[i].ingr_id,
args->node[i].ingr_wide,
args->node[i].egr_id,
args->node[i].egr_wide,
args->node[i].ns_data,
args->node[i].ns_wide,
args->node[i].sc_id,
args->node[i].sc_data))
return 1;
}
return 0;
}
int parse_node_args(int *argcp, char ***argvp, struct node_args *node)
{
char **argv = *argvp;
if (*argcp < NODE_ARGS_SIZE)
return 1;
node->hop_limit = strtoul(argv[NODE_ARG_HOP_LIMIT], NULL, 10);
if (!node->hop_limit) {
node->hop_limit = strtoul(argv[NODE_ARG_HOP_LIMIT], NULL, 16);
if (!node->hop_limit)
return 1;
}
node->id = strtoul(argv[NODE_ARG_ID], NULL, 10);
if (!node->id) {
node->id = strtoul(argv[NODE_ARG_ID], NULL, 16);
if (!node->id)
return 1;
}
node->wide = strtoull(argv[NODE_ARG_WIDE], NULL, 10);
if (!node->wide) {
node->wide = strtoull(argv[NODE_ARG_WIDE], NULL, 16);
if (!node->wide)
return 1;
}
node->ingr_id = strtoul(argv[NODE_ARG_INGR_ID], NULL, 10);
if (!node->ingr_id) {
node->ingr_id = strtoul(argv[NODE_ARG_INGR_ID], NULL, 16);
if (!node->ingr_id)
return 1;
}
node->ingr_wide = strtoul(argv[NODE_ARG_INGR_WIDE], NULL, 10);
if (!node->ingr_wide) {
node->ingr_wide = strtoul(argv[NODE_ARG_INGR_WIDE], NULL, 16);
if (!node->ingr_wide)
return 1;
}
node->egr_id = strtoul(argv[NODE_ARG_EGR_ID], NULL, 10);
if (!node->egr_id) {
node->egr_id = strtoul(argv[NODE_ARG_EGR_ID], NULL, 16);
if (!node->egr_id)
return 1;
}
node->egr_wide = strtoul(argv[NODE_ARG_EGR_WIDE], NULL, 10);
if (!node->egr_wide) {
node->egr_wide = strtoul(argv[NODE_ARG_EGR_WIDE], NULL, 16);
if (!node->egr_wide)
return 1;
}
node->ns_data = strtoul(argv[NODE_ARG_NS_DATA], NULL, 16);
if (!node->ns_data)
return 1;
node->ns_wide = strtoull(argv[NODE_ARG_NS_WIDE], NULL, 16);
if (!node->ns_wide)
return 1;
node->sc_id = strtoul(argv[NODE_ARG_SC_ID], NULL, 10);
if (!node->sc_id) {
node->sc_id = strtoul(argv[NODE_ARG_SC_ID], NULL, 16);
if (!node->sc_id)
return 1;
}
*argcp -= NODE_ARGS_SIZE;
*argvp += NODE_ARGS_SIZE;
if (node->sc_id != 0xffffff) {
if (!*argcp)
return 1;
node->sc_data = argv[NODE_ARG_SC_ID + 1];
*argcp -= 1;
*argvp += 1;
}
return 0;
}
struct args *parse_args(int argc, char **argv)
{
struct args *args;
int n_node, i;
if (argc < ARGS_SIZE)
goto out;
n_node = strtoul(argv[ARG_N_NODE], NULL, 10);
if (!n_node || n_node > 10)
goto out;
args = calloc(1, sizeof(*args) + n_node * sizeof(struct node_args));
if (!args)
goto out;
args->ns_id = strtoul(argv[ARG_NS_ID], NULL, 10);
if (!args->ns_id)
goto free;
args->trace_type = strtoul(argv[ARG_TRACE_TYPE], NULL, 16);
if (!args->trace_type)
goto free;
args->n_node = n_node;
args->ifname = argv[ARG_IFNAME];
argv += ARGS_SIZE;
argc -= ARGS_SIZE;
for (i = 0; i < n_node; i++) {
if (parse_node_args(&argc, &argv, &args->node[i]))
goto free;
}
if (argc)
goto free;
return args;
free:
free(args);
out:
return NULL;
}
int main(int argc, char **argv)
{
int ret, fd, pkts, size, hoplen, found;
struct ioam6_trace_hdr *ioam6h;
struct ioam6_hdr *opt;
struct ipv6hdr *ip6h;
__u8 buffer[400], *p;
struct args *args;
args = parse_args(argc - 1, argv + 1);
if (!args) {
ret = 1;
goto out;
}
fd = socket(AF_PACKET, SOCK_DGRAM, __cpu_to_be16(ETH_P_IPV6));
if (!fd) {
ret = 1;
goto out;
}
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
args->ifname, strlen(args->ifname))) {
ret = 1;
goto close;
}
pkts = 0;
found = 0;
while (pkts < 3 && !found) {
size = recv(fd, buffer, sizeof(buffer), 0);
ip6h = (struct ipv6hdr *)buffer;
pkts++;
if (ip6h->nexthdr == IPPROTO_HOPOPTS) {
p = buffer + sizeof(*ip6h);
hoplen = (p[1] + 1) << 3;
p += sizeof(struct ipv6_hopopt_hdr);
while (hoplen > 0) {
opt = (struct ioam6_hdr *)p;
if (opt->opt_type == IPV6_TLV_IOAM &&
opt->type == IOAM6_TYPE_PREALLOC) {
found = 1;
p += sizeof(*opt);
ioam6h = (struct ioam6_trace_hdr *)p;
ret = check_ioam6_trace(ioam6h, args);
break;
}
p += opt->opt_len + 2;
hoplen -= opt->opt_len + 2;
}
}
}
if (!found)
ret = 1;
close:
close(fd);
out:
free(args);
return ret;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment