Skip to content

Commit

Permalink
ipv6: ioam: Data plane support for Pre-allocated Trace
Browse files Browse the repository at this point in the history
Implement support for processing the IOAM Pre-allocated Trace with IPv6,
see [1] and [2]. Introduce a new IPv6 Hop-by-Hop TLV option, see IANA [3].

A new per-interface sysctl is introduced. The value is a boolean to accept (=1)
or ignore (=0, by default) IPv6 IOAM options on ingress for an interface:
 - net.ipv6.conf.XXX.ioam6_enabled

Two other sysctls are introduced to define IOAM IDs, represented by an integer.
They are respectively per-namespace and per-interface:
 - net.ipv6.ioam6_id
 - net.ipv6.conf.XXX.ioam6_id

The value of the first one represents the IOAM ID of the node itself (u32; max
and default value = U32_MAX>>8, due to hop limit concatenation) while the other
represents the IOAM ID of an interface (u16; max and default value = U16_MAX).

Each "ioam6_id" sysctl has a "_wide" equivalent:
 - net.ipv6.ioam6_id_wide
 - net.ipv6.conf.XXX.ioam6_id_wide

The value of the first one represents the wide IOAM ID of the node itself (u64;
max and default value = U64_MAX>>8, due to hop limit concatenation) while the
other represents the wide IOAM ID of an interface (u32; max and default value
= U32_MAX).

The use of short and wide equivalents is not exclusive, a deployment could
choose to leverage both. For example, net.ipv6.conf.XXX.ioam6_id (short format)
could be an identifier for a physical interface, whereas
net.ipv6.conf.XXX.ioam6_id_wide (wide format) could be an identifier for a
logical sub-interface. Documentation about new sysctls is provided at the end
of this patchset.

Two relativistic hash tables are used: one for IOAM namespaces, the other for
IOAM schemas. A namespace can only have a single active schema and a schema
can only be attached to a single namespace (1:1 relationship).

  [1] https://tools.ietf.org/html/draft-ietf-ippm-ioam-ipv6-options
  [2] https://tools.ietf.org/html/draft-ietf-ippm-ioam-data
  [3] https://www.iana.org/assignments/ipv6-parameters/ipv6-parameters.xhtml#ipv6-parameters-2

Signed-off-by: Justin Iurman <justin.iurman@uliege.be>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Justin Iurman authored and David S. Miller committed Jul 21, 2021
1 parent db67f21 commit 9ee11f0
Show file tree
Hide file tree
Showing 13 changed files with 557 additions and 1 deletion.
13 changes: 13 additions & 0 deletions include/linux/ioam6.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* IPv6 IOAM
*
* Author:
* Justin Iurman <justin.iurman@uliege.be>
*/
#ifndef _LINUX_IOAM6_H
#define _LINUX_IOAM6_H

#include <uapi/linux/ioam6.h>

#endif /* _LINUX_IOAM6_H */
3 changes: 3 additions & 0 deletions include/linux/ipv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ struct ipv6_devconf {
__s32 disable_policy;
__s32 ndisc_tclass;
__s32 rpl_seg_enabled;
__u32 ioam6_id;
__u32 ioam6_id_wide;
__u8 ioam6_enabled;

struct ctl_table_header *sysctl_header;
};
Expand Down
64 changes: 64 additions & 0 deletions include/net/ioam6.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* IPv6 IOAM implementation
*
* Author:
* Justin Iurman <justin.iurman@uliege.be>
*/

#ifndef _NET_IOAM6_H
#define _NET_IOAM6_H

#include <linux/net.h>
#include <linux/ipv6.h>
#include <linux/ioam6.h>
#include <linux/rhashtable-types.h>

struct ioam6_namespace {
struct rhash_head head;
struct rcu_head rcu;

struct ioam6_schema __rcu *schema;

__be16 id;
__be32 data;
__be64 data_wide;
};

struct ioam6_schema {
struct rhash_head head;
struct rcu_head rcu;

struct ioam6_namespace __rcu *ns;

u32 id;
int len;
__be32 hdr;

u8 data[0];
};

struct ioam6_pernet_data {
struct mutex lock;
struct rhashtable namespaces;
struct rhashtable schemas;
};

static inline struct ioam6_pernet_data *ioam6_pernet(struct net *net)
{
#if IS_ENABLED(CONFIG_IPV6)
return net->ipv6.ioam6_data;
#else
return NULL;
#endif
}

struct ioam6_namespace *ioam6_namespace(struct net *net, __be16 id);
void ioam6_fill_trace_data(struct sk_buff *skb,
struct ioam6_namespace *ns,
struct ioam6_trace_hdr *trace);

int ioam6_init(void);
void ioam6_exit(void);

#endif /* _NET_IOAM6_H */
3 changes: 3 additions & 0 deletions include/net/netns/ipv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ struct netns_sysctl_ipv6 {
int max_dst_opts_len;
int max_hbh_opts_len;
int seg6_flowlabel;
u32 ioam6_id;
u64 ioam6_id_wide;
bool skip_notify_on_dev_down;
u8 fib_notify_on_flag_change;
};
Expand Down Expand Up @@ -110,6 +112,7 @@ struct netns_ipv6 {
spinlock_t lock;
u32 seq;
} ip6addrlbl_table;
struct ioam6_pernet_data *ioam6_data;
};

#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
Expand Down
1 change: 1 addition & 0 deletions include/uapi/linux/in6.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ struct in6_flowlabel_req {
#define IPV6_TLV_PADN 1
#define IPV6_TLV_ROUTERALERT 5
#define IPV6_TLV_CALIPSO 7 /* RFC 5570 */
#define IPV6_TLV_IOAM 49 /* TEMPORARY IANA allocation for IOAM */
#define IPV6_TLV_JUMBO 194
#define IPV6_TLV_HAO 201 /* home address option */

Expand Down
9 changes: 9 additions & 0 deletions include/uapi/linux/ioam6.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@
#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
*/
Expand Down
3 changes: 3 additions & 0 deletions include/uapi/linux/ipv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,9 @@ enum {
DEVCONF_NDISC_TCLASS,
DEVCONF_RPL_SEG_ENABLED,
DEVCONF_RA_DEFRTR_METRIC,
DEVCONF_IOAM6_ENABLED,
DEVCONF_IOAM6_ID,
DEVCONF_IOAM6_ID_WIDE,
DEVCONF_MAX
};

Expand Down
2 changes: 1 addition & 1 deletion net/ipv6/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \
route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \
raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o ping.o \
exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o \
udp_offload.o seg6.o fib6_notifier.o rpl.o
udp_offload.o seg6.o fib6_notifier.o rpl.o ioam6.o

ipv6-offload := ip6_offload.o tcpv6_offload.o exthdrs_offload.o

Expand Down
37 changes: 37 additions & 0 deletions net/ipv6/addrconf.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,15 @@
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/export.h>
#include <linux/ioam6.h>

#define INFINITY_LIFE_TIME 0xFFFFFFFF

#define IPV6_MAX_STRLEN \
sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")

static u32 ioam6_if_id_max = U16_MAX;

static inline u32 cstamp_delta(unsigned long cstamp)
{
return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
Expand Down Expand Up @@ -237,6 +240,9 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
.addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64,
.disable_policy = 0,
.rpl_seg_enabled = 0,
.ioam6_enabled = 0,
.ioam6_id = IOAM6_DEFAULT_IF_ID,
.ioam6_id_wide = IOAM6_DEFAULT_IF_ID_WIDE,
};

static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
Expand Down Expand Up @@ -293,6 +299,9 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
.addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64,
.disable_policy = 0,
.rpl_seg_enabled = 0,
.ioam6_enabled = 0,
.ioam6_id = IOAM6_DEFAULT_IF_ID,
.ioam6_id_wide = IOAM6_DEFAULT_IF_ID_WIDE,
};

/* Check if link is ready: is it up and is a valid qdisc available */
Expand Down Expand Up @@ -5524,6 +5533,9 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
array[DEVCONF_DISABLE_POLICY] = cnf->disable_policy;
array[DEVCONF_NDISC_TCLASS] = cnf->ndisc_tclass;
array[DEVCONF_RPL_SEG_ENABLED] = cnf->rpl_seg_enabled;
array[DEVCONF_IOAM6_ENABLED] = cnf->ioam6_enabled;
array[DEVCONF_IOAM6_ID] = cnf->ioam6_id;
array[DEVCONF_IOAM6_ID_WIDE] = cnf->ioam6_id_wide;
}

static inline size_t inet6_ifla6_size(void)
Expand Down Expand Up @@ -6930,6 +6942,31 @@ static const struct ctl_table addrconf_sysctl[] = {
.mode = 0644,
.proc_handler = proc_dointvec,
},
{
.procname = "ioam6_enabled",
.data = &ipv6_devconf.ioam6_enabled,
.maxlen = sizeof(u8),
.mode = 0644,
.proc_handler = proc_dou8vec_minmax,
.extra1 = (void *)SYSCTL_ZERO,
.extra2 = (void *)SYSCTL_ONE,
},
{
.procname = "ioam6_id",
.data = &ipv6_devconf.ioam6_id,
.maxlen = sizeof(u32),
.mode = 0644,
.proc_handler = proc_douintvec_minmax,
.extra1 = (void *)SYSCTL_ZERO,
.extra2 = (void *)&ioam6_if_id_max,
},
{
.procname = "ioam6_id_wide",
.data = &ipv6_devconf.ioam6_id_wide,
.maxlen = sizeof(u32),
.mode = 0644,
.proc_handler = proc_douintvec,
},
{
/* sentinel */
}
Expand Down
10 changes: 10 additions & 0 deletions net/ipv6/af_inet6.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
#include <net/rpl.h>
#include <net/compat.h>
#include <net/xfrm.h>
#include <net/ioam6.h>

#include <linux/uaccess.h>
#include <linux/mroute6.h>
Expand Down Expand Up @@ -961,6 +962,9 @@ static int __net_init inet6_net_init(struct net *net)
net->ipv6.sysctl.fib_notify_on_flag_change = 0;
atomic_set(&net->ipv6.fib6_sernum, 1);

net->ipv6.sysctl.ioam6_id = IOAM6_DEFAULT_ID;
net->ipv6.sysctl.ioam6_id_wide = IOAM6_DEFAULT_ID_WIDE;

err = ipv6_init_mibs(net);
if (err)
return err;
Expand Down Expand Up @@ -1191,6 +1195,10 @@ static int __init inet6_init(void)
if (err)
goto rpl_fail;

err = ioam6_init();
if (err)
goto ioam6_fail;

err = igmp6_late_init();
if (err)
goto igmp6_late_err;
Expand All @@ -1213,6 +1221,8 @@ static int __init inet6_init(void)
igmp6_late_cleanup();
#endif
igmp6_late_err:
ioam6_exit();
ioam6_fail:
rpl_exit();
rpl_fail:
seg6_exit();
Expand Down
61 changes: 61 additions & 0 deletions net/ipv6/exthdrs.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@
#include <net/seg6_hmac.h>
#endif
#include <net/rpl.h>
#include <linux/ioam6.h>
#include <net/ioam6.h>
#include <net/dst_metadata.h>

#include <linux/uaccess.h>

Expand Down Expand Up @@ -928,6 +931,60 @@ static bool ipv6_hop_ra(struct sk_buff *skb, int optoff)
return false;
}

/* IOAM */

static bool ipv6_hop_ioam(struct sk_buff *skb, int optoff)
{
struct ioam6_trace_hdr *trace;
struct ioam6_namespace *ns;
struct ioam6_hdr *hdr;

/* Bad alignment (must be 4n-aligned) */
if (optoff & 3)
goto drop;

/* Ignore if IOAM is not enabled on ingress */
if (!__in6_dev_get(skb->dev)->cnf.ioam6_enabled)
goto ignore;

/* Truncated Option header */
hdr = (struct ioam6_hdr *)(skb_network_header(skb) + optoff);
if (hdr->opt_len < 2)
goto drop;

switch (hdr->type) {
case IOAM6_TYPE_PREALLOC:
/* Truncated Pre-allocated Trace header */
if (hdr->opt_len < 2 + sizeof(*trace))
goto drop;

/* Malformed Pre-allocated Trace header */
trace = (struct ioam6_trace_hdr *)((u8 *)hdr + sizeof(*hdr));
if (hdr->opt_len < 2 + sizeof(*trace) + trace->remlen * 4)
goto drop;

/* Ignore if the IOAM namespace is unknown */
ns = ioam6_namespace(ipv6_skb_net(skb), trace->namespace_id);
if (!ns)
goto ignore;

if (!skb_valid_dst(skb))
ip6_route_input(skb);

ioam6_fill_trace_data(skb, ns, trace);
break;
default:
break;
}

ignore:
return true;

drop:
kfree_skb(skb);
return false;
}

/* Jumbo payload */

static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
Expand Down Expand Up @@ -999,6 +1056,10 @@ static const struct tlvtype_proc tlvprochopopt_lst[] = {
.type = IPV6_TLV_ROUTERALERT,
.func = ipv6_hop_ra,
},
{
.type = IPV6_TLV_IOAM,
.func = ipv6_hop_ioam,
},
{
.type = IPV6_TLV_JUMBO,
.func = ipv6_hop_jumbo,
Expand Down
Loading

0 comments on commit 9ee11f0

Please sign in to comment.