History

ipv6: ioam: Data plane support for Pre-allocated Trace

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

Justin Iurman committed 5 years ago

Commit 9ee11f01 parent db67f2113 files · +557 −1Browse files

Unified · Split

13 files
  1. include/linux/ioam6.h +13 −0
  2. include/linux/ipv6.h +3 −0
  3. include/net/ioam6.h +64 −0
  4. include/net/netns/ipv6.h +3 −0
  5. include/uapi/linux/in6.h +1 −0
  6. include/uapi/linux/ioam6.h +9 −0
  7. include/uapi/linux/ipv6.h +3 −0
  8. net/ipv6/Makefile +1 −1
  9. net/ipv6/addrconf.c +37 −0
  10. net/ipv6/af_inet6.c +10 −0
  11. net/ipv6/exthdrs.c +61 −0
  12. net/ipv6/ioam6.c +333 −0
  13. net/ipv6/sysctl_net_ipv6.c +19 −0
include/linux/ioam6.h+13−0
@@ -0,0 +1,13 @@
1+/* SPDX-License-Identifier: GPL-2.0+ */
2+/*
3+ * IPv6 IOAM
4+ *
5+ * Author:
6+ * Justin Iurman <justin.iurman@uliege.be>
7+ */
8+#ifndef _LINUX_IOAM6_H
9+#define _LINUX_IOAM6_H
10+
11+#include <uapi/linux/ioam6.h>
12+
13+#endif /* _LINUX_IOAM6_H */
include/linux/ipv6.h+3−0
@@ -76,6 +76,9 @@ struct ipv6_devconf { · 75 unmodified lines
7676 __s32 disable_policy;
7777 __s32 ndisc_tclass;
7878 __s32 rpl_seg_enabled;
79+ __u32 ioam6_id;
80+ __u32 ioam6_id_wide;
81+ __u8 ioam6_enabled;
7982
8083 struct ctl_table_header *sysctl_header;
8184 };
include/net/ioam6.h+64−0
@@ -0,0 +1,64 @@
1+/* SPDX-License-Identifier: GPL-2.0+ */
2+/*
3+ * IPv6 IOAM implementation
4+ *
5+ * Author:
6+ * Justin Iurman <justin.iurman@uliege.be>
7+ */
8+
9+#ifndef _NET_IOAM6_H
10+#define _NET_IOAM6_H
11+
12+#include <linux/net.h>
13+#include <linux/ipv6.h>
14+#include <linux/ioam6.h>
15+#include <linux/rhashtable-types.h>
16+
17+struct ioam6_namespace {
18+ struct rhash_head head;
19+ struct rcu_head rcu;
20+
21+ struct ioam6_schema __rcu *schema;
22+
23+ __be16 id;
24+ __be32 data;
25+ __be64 data_wide;
26+};
27+
28+struct ioam6_schema {
29+ struct rhash_head head;
30+ struct rcu_head rcu;
31+
32+ struct ioam6_namespace __rcu *ns;
33+
34+ u32 id;
35+ int len;
36+ __be32 hdr;
37+
38+ u8 data[0];
39+};
40+
41+struct ioam6_pernet_data {
42+ struct mutex lock;
43+ struct rhashtable namespaces;
44+ struct rhashtable schemas;
45+};
46+
47+static inline struct ioam6_pernet_data *ioam6_pernet(struct net *net)
48+{
49+#if IS_ENABLED(CONFIG_IPV6)
50+ return net->ipv6.ioam6_data;
51+#else
52+ return NULL;
53+#endif
54+}
55+
56+struct ioam6_namespace *ioam6_namespace(struct net *net, __be16 id);
57+void ioam6_fill_trace_data(struct sk_buff *skb,
58+ struct ioam6_namespace *ns,
59+ struct ioam6_trace_hdr *trace);
60+
61+int ioam6_init(void);
62+void ioam6_exit(void);
63+
64+#endif /* _NET_IOAM6_H */
include/net/netns/ipv6.h+3−0
@@ -51,6 +51,8 @@ struct netns_sysctl_ipv6 { · 50 unmodified lines
5151 int max_dst_opts_len;
5252 int max_hbh_opts_len;
5353 int seg6_flowlabel;
54+ u32 ioam6_id;
55+ u64 ioam6_id_wide;
5456 bool skip_notify_on_dev_down;
5557 u8 fib_notify_on_flag_change;
5658 };
@@ -110,6 +112,7 @@ struct netns_ipv6 { · 53 unmodified lines
110112 spinlock_t lock;
111113 u32 seq;
112114 } ip6addrlbl_table;
115+ struct ioam6_pernet_data *ioam6_data;
113116 };
114117
115118 #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
include/uapi/linux/in6.h+1−0
@@ -145,6 +145,7 @@ struct in6_flowlabel_req { · 144 unmodified lines
145145 #define IPV6_TLV_PADN 1
146146 #define IPV6_TLV_ROUTERALERT 5
147147 #define IPV6_TLV_CALIPSO 7 /* RFC 5570 */
148+#define IPV6_TLV_IOAM 49 /* TEMPORARY IANA allocation for IOAM */
148149 #define IPV6_TLV_JUMBO 194
149150 #define IPV6_TLV_HAO 201 /* home address option */
150151
include/uapi/linux/ioam6.h+9−0
@@ -12,6 +12,15 @@ · 11 unmodified lines
1212 #include <asm/byteorder.h>
1313 #include <linux/types.h>
1414
15+#define IOAM6_U16_UNAVAILABLE U16_MAX
16+#define IOAM6_U32_UNAVAILABLE U32_MAX
17+#define IOAM6_U64_UNAVAILABLE U64_MAX
18+
19+#define IOAM6_DEFAULT_ID (IOAM6_U32_UNAVAILABLE >> 8)
20+#define IOAM6_DEFAULT_ID_WIDE (IOAM6_U64_UNAVAILABLE >> 8)
21+#define IOAM6_DEFAULT_IF_ID IOAM6_U16_UNAVAILABLE
22+#define IOAM6_DEFAULT_IF_ID_WIDE IOAM6_U32_UNAVAILABLE
23+
1524 /*
1625 * IPv6 IOAM Option Header
1726 */
include/uapi/linux/ipv6.h+3−0
@@ -190,6 +190,9 @@ enum { · 189 unmodified lines
190190 DEVCONF_NDISC_TCLASS,
191191 DEVCONF_RPL_SEG_ENABLED,
192192 DEVCONF_RA_DEFRTR_METRIC,
193+ DEVCONF_IOAM6_ENABLED,
194+ DEVCONF_IOAM6_ID,
195+ DEVCONF_IOAM6_ID_WIDE,
193196 DEVCONF_MAX
194197 };
195198
net/ipv6/Makefile+1−1
@@ -10,7 +10,7 @@ ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \ · 9 unmodified lines
1010 route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \
1111 raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o ping.o \
1212 exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o \
13− udp_offload.o seg6.o fib6_notifier.o rpl.o
13+ udp_offload.o seg6.o fib6_notifier.o rpl.o ioam6.o
1414
1515 ipv6-offload := ip6_offload.o tcpv6_offload.o exthdrs_offload.o
1616
net/ipv6/addrconf.c+37−0
@@ -89,12 +89,15 @@ · 88 unmodified lines
8989 #include <linux/proc_fs.h>
9090 #include <linux/seq_file.h>
9191 #include <linux/export.h>
92+#include <linux/ioam6.h>
9293
9394 #define INFINITY_LIFE_TIME 0xFFFFFFFF
9495
9596 #define IPV6_MAX_STRLEN \
9697 sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")
9798
99+static u32 ioam6_if_id_max = U16_MAX;
100+
98101 static inline u32 cstamp_delta(unsigned long cstamp)
99102 {
100103 return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
@@ -237,6 +240,9 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { · 136 unmodified lines
237240 .addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64,
238241 .disable_policy = 0,
239242 .rpl_seg_enabled = 0,
243+ .ioam6_enabled = 0,
244+ .ioam6_id = IOAM6_DEFAULT_IF_ID,
245+ .ioam6_id_wide = IOAM6_DEFAULT_IF_ID_WIDE,
240246 };
241247
242248 static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
@@ -293,6 +299,9 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { · 50 unmodified lines
293299 .addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64,
294300 .disable_policy = 0,
295301 .rpl_seg_enabled = 0,
302+ .ioam6_enabled = 0,
303+ .ioam6_id = IOAM6_DEFAULT_IF_ID,
304+ .ioam6_id_wide = IOAM6_DEFAULT_IF_ID_WIDE,
296305 };
297306
298307 /* Check if link is ready: is it up and is a valid qdisc available */
@@ -5524,6 +5533,9 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, · 5225 unmodified lines
55245533 array[DEVCONF_DISABLE_POLICY] = cnf->disable_policy;
55255534 array[DEVCONF_NDISC_TCLASS] = cnf->ndisc_tclass;
55265535 array[DEVCONF_RPL_SEG_ENABLED] = cnf->rpl_seg_enabled;
5536+ array[DEVCONF_IOAM6_ENABLED] = cnf->ioam6_enabled;
5537+ array[DEVCONF_IOAM6_ID] = cnf->ioam6_id;
5538+ array[DEVCONF_IOAM6_ID_WIDE] = cnf->ioam6_id_wide;
55275539 }
55285540
55295541 static inline size_t inet6_ifla6_size(void)
@@ -6930,6 +6942,31 @@ static const struct ctl_table addrconf_sysctl[] = { · 1400 unmodified lines
69306942 .mode = 0644,
69316943 .proc_handler = proc_dointvec,
69326944 },
6945+ {
6946+ .procname = "ioam6_enabled",
6947+ .data = &ipv6_devconf.ioam6_enabled,
6948+ .maxlen = sizeof(u8),
6949+ .mode = 0644,
6950+ .proc_handler = proc_dou8vec_minmax,
6951+ .extra1 = (void *)SYSCTL_ZERO,
6952+ .extra2 = (void *)SYSCTL_ONE,
6953+ },
6954+ {
6955+ .procname = "ioam6_id",
6956+ .data = &ipv6_devconf.ioam6_id,
6957+ .maxlen = sizeof(u32),
6958+ .mode = 0644,
6959+ .proc_handler = proc_douintvec_minmax,
6960+ .extra1 = (void *)SYSCTL_ZERO,
6961+ .extra2 = (void *)&ioam6_if_id_max,
6962+ },
6963+ {
6964+ .procname = "ioam6_id_wide",
6965+ .data = &ipv6_devconf.ioam6_id_wide,
6966+ .maxlen = sizeof(u32),
6967+ .mode = 0644,
6968+ .proc_handler = proc_douintvec,
6969+ },
69336970 {
69346971 /* sentinel */
69356972 }
net/ipv6/af_inet6.c+10−0
@@ -62,6 +62,7 @@ · 61 unmodified lines
6262 #include <net/rpl.h>
6363 #include <net/compat.h>
6464 #include <net/xfrm.h>
65+#include <net/ioam6.h>
6566
6667 #include <linux/uaccess.h>
6768 #include <linux/mroute6.h>
@@ -961,6 +962,9 @@ static int __net_init inet6_net_init(struct net *net) · 893 unmodified lines
961962 net->ipv6.sysctl.fib_notify_on_flag_change = 0;
962963 atomic_set(&net->ipv6.fib6_sernum, 1);
963964
965+ net->ipv6.sysctl.ioam6_id = IOAM6_DEFAULT_ID;
966+ net->ipv6.sysctl.ioam6_id_wide = IOAM6_DEFAULT_ID_WIDE;
967+
964968 err = ipv6_init_mibs(net);
965969 if (err)
966970 return err;
@@ -1191,6 +1195,10 @@ static int __init inet6_init(void) · 224 unmodified lines
11911195 if (err)
11921196 goto rpl_fail;
11931197
1198+ err = ioam6_init();
1199+ if (err)
1200+ goto ioam6_fail;
1201+
11941202 err = igmp6_late_init();
11951203 if (err)
11961204 goto igmp6_late_err;
@@ -1213,6 +1221,8 @@ sysctl_fail: · 16 unmodified lines
12131221 igmp6_late_cleanup();
12141222 #endif
12151223 igmp6_late_err:
1224+ ioam6_exit();
1225+ioam6_fail:
12161226 rpl_exit();
12171227 rpl_fail:
12181228 seg6_exit();
net/ipv6/exthdrs.c+61−0
@@ -49,6 +49,9 @@ · 48 unmodified lines
4949 #include <net/seg6_hmac.h>
5050 #endif
5151 #include <net/rpl.h>
52+#include <linux/ioam6.h>
53+#include <net/ioam6.h>
54+#include <net/dst_metadata.h>
5255
5356 #include <linux/uaccess.h>
5457
@@ -928,6 +931,60 @@ static bool ipv6_hop_ra(struct sk_buff *skb, int optoff) · 873 unmodified lines
928931 return false;
929932 }
930933
934+/* IOAM */
935+
936+static bool ipv6_hop_ioam(struct sk_buff *skb, int optoff)
937+{
938+ struct ioam6_trace_hdr *trace;
939+ struct ioam6_namespace *ns;
940+ struct ioam6_hdr *hdr;
941+
942+ /* Bad alignment (must be 4n-aligned) */
943+ if (optoff & 3)
944+ goto drop;
945+
946+ /* Ignore if IOAM is not enabled on ingress */
947+ if (!__in6_dev_get(skb->dev)->cnf.ioam6_enabled)
948+ goto ignore;
949+
950+ /* Truncated Option header */
951+ hdr = (struct ioam6_hdr *)(skb_network_header(skb) + optoff);
952+ if (hdr->opt_len < 2)
953+ goto drop;
954+
955+ switch (hdr->type) {
956+ case IOAM6_TYPE_PREALLOC:
957+ /* Truncated Pre-allocated Trace header */
958+ if (hdr->opt_len < 2 + sizeof(*trace))
959+ goto drop;
960+
961+ /* Malformed Pre-allocated Trace header */
962+ trace = (struct ioam6_trace_hdr *)((u8 *)hdr + sizeof(*hdr));
963+ if (hdr->opt_len < 2 + sizeof(*trace) + trace->remlen * 4)
964+ goto drop;
965+
966+ /* Ignore if the IOAM namespace is unknown */
967+ ns = ioam6_namespace(ipv6_skb_net(skb), trace->namespace_id);
968+ if (!ns)
969+ goto ignore;
970+
971+ if (!skb_valid_dst(skb))
972+ ip6_route_input(skb);
973+
974+ ioam6_fill_trace_data(skb, ns, trace);
975+ break;
976+ default:
977+ break;
978+ }
979+
980+ignore:
981+ return true;
982+
983+drop:
984+ kfree_skb(skb);
985+ return false;
986+}
987+
931988 /* Jumbo payload */
932989
933990 static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
@@ -999,6 +1056,10 @@ static const struct tlvtype_proc tlvprochopopt_lst[] = { · 65 unmodified lines
9991056 .type = IPV6_TLV_ROUTERALERT,
10001057 .func = ipv6_hop_ra,
10011058 },
1059+ {
1060+ .type = IPV6_TLV_IOAM,
1061+ .func = ipv6_hop_ioam,
1062+ },
10021063 {
10031064 .type = IPV6_TLV_JUMBO,
10041065 .func = ipv6_hop_jumbo,
net/ipv6/ioam6.c+333−0
@@ -0,0 +1,333 @@
1+// SPDX-License-Identifier: GPL-2.0+
2+/*
3+ * IPv6 IOAM implementation
4+ *
5+ * Author:
6+ * Justin Iurman <justin.iurman@uliege.be>
7+ */
8+
9+#include <linux/errno.h>
10+#include <linux/types.h>
11+#include <linux/kernel.h>
12+#include <linux/net.h>
13+#include <linux/ioam6.h>
14+#include <linux/rhashtable.h>
15+
16+#include <net/addrconf.h>
17+#include <net/ioam6.h>
18+
19+static void ioam6_ns_release(struct ioam6_namespace *ns)
20+{
21+ kfree_rcu(ns, rcu);
22+}
23+
24+static void ioam6_sc_release(struct ioam6_schema *sc)
25+{
26+ kfree_rcu(sc, rcu);
27+}
28+
29+static void ioam6_free_ns(void *ptr, void *arg)
30+{
31+ struct ioam6_namespace *ns = (struct ioam6_namespace *)ptr;
32+
33+ if (ns)
34+ ioam6_ns_release(ns);
35+}
36+
37+static void ioam6_free_sc(void *ptr, void *arg)
38+{
39+ struct ioam6_schema *sc = (struct ioam6_schema *)ptr;
40+
41+ if (sc)
42+ ioam6_sc_release(sc);
43+}
44+
45+static int ioam6_ns_cmpfn(struct rhashtable_compare_arg *arg, const void *obj)
46+{
47+ const struct ioam6_namespace *ns = obj;
48+
49+ return (ns->id != *(__be16 *)arg->key);
50+}
51+
52+static int ioam6_sc_cmpfn(struct rhashtable_compare_arg *arg, const void *obj)
53+{
54+ const struct ioam6_schema *sc = obj;
55+
56+ return (sc->id != *(u32 *)arg->key);
57+}
58+
59+static const struct rhashtable_params rht_ns_params = {
60+ .key_len = sizeof(__be16),
61+ .key_offset = offsetof(struct ioam6_namespace, id),
62+ .head_offset = offsetof(struct ioam6_namespace, head),
63+ .automatic_shrinking = true,
64+ .obj_cmpfn = ioam6_ns_cmpfn,
65+};
66+
67+static const struct rhashtable_params rht_sc_params = {
68+ .key_len = sizeof(u32),
69+ .key_offset = offsetof(struct ioam6_schema, id),
70+ .head_offset = offsetof(struct ioam6_schema, head),
71+ .automatic_shrinking = true,
72+ .obj_cmpfn = ioam6_sc_cmpfn,
73+};
74+
75+struct ioam6_namespace *ioam6_namespace(struct net *net, __be16 id)
76+{
77+ struct ioam6_pernet_data *nsdata = ioam6_pernet(net);
78+
79+ return rhashtable_lookup_fast(&nsdata->namespaces, &id, rht_ns_params);
80+}
81+
82+static void __ioam6_fill_trace_data(struct sk_buff *skb,
83+ struct ioam6_namespace *ns,
84+ struct ioam6_trace_hdr *trace,
85+ struct ioam6_schema *sc,
86+ u8 sclen)
87+{
88+ struct __kernel_sock_timeval ts;
89+ u64 raw64;
90+ u32 raw32;
91+ u16 raw16;
92+ u8 *data;
93+ u8 byte;
94+
95+ data = trace->data + trace->remlen * 4 - trace->nodelen * 4 - sclen * 4;
96+
97+ /* hop_lim and node_id */
98+ if (trace->type.bit0) {
99+ byte = ipv6_hdr(skb)->hop_limit;
100+ if (skb->dev)
101+ byte--;
102+
103+ raw32 = dev_net(skb->dev)->ipv6.sysctl.ioam6_id;
104+
105+ *(__be32 *)data = cpu_to_be32((byte << 24) | raw32);
106+ data += sizeof(__be32);
107+ }
108+
109+ /* ingress_if_id and egress_if_id */
110+ if (trace->type.bit1) {
111+ if (!skb->dev)
112+ raw16 = IOAM6_U16_UNAVAILABLE;
113+ else
114+ raw16 = (__force u16)__in6_dev_get(skb->dev)->cnf.ioam6_id;
115+
116+ *(__be16 *)data = cpu_to_be16(raw16);
117+ data += sizeof(__be16);
118+
119+ if (skb_dst(skb)->dev->flags & IFF_LOOPBACK)
120+ raw16 = IOAM6_U16_UNAVAILABLE;
121+ else
122+ raw16 = (__force u16)__in6_dev_get(skb_dst(skb)->dev)->cnf.ioam6_id;
123+
124+ *(__be16 *)data = cpu_to_be16(raw16);
125+ data += sizeof(__be16);
126+ }
127+
128+ /* timestamp seconds */
129+ if (trace->type.bit2) {
130+ if (!skb->tstamp)
131+ __net_timestamp(skb);
132+
133+ skb_get_new_timestamp(skb, &ts);
134+
135+ *(__be32 *)data = cpu_to_be32((u32)ts.tv_sec);
136+ data += sizeof(__be32);
137+ }
138+
139+ /* timestamp subseconds */
140+ if (trace->type.bit3) {
141+ if (!skb->tstamp)
142+ __net_timestamp(skb);
143+
144+ if (!trace->type.bit2)
145+ skb_get_new_timestamp(skb, &ts);
146+
147+ *(__be32 *)data = cpu_to_be32((u32)ts.tv_usec);
148+ data += sizeof(__be32);
149+ }
150+
151+ /* transit delay */
152+ if (trace->type.bit4) {
153+ *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
154+ data += sizeof(__be32);
155+ }
156+
157+ /* namespace data */
158+ if (trace->type.bit5) {
159+ *(__be32 *)data = ns->data;
160+ data += sizeof(__be32);
161+ }
162+
163+ /* queue depth */
164+ if (trace->type.bit6) {
165+ *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
166+ data += sizeof(__be32);
167+ }
168+
169+ /* checksum complement */
170+ if (trace->type.bit7) {
171+ *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
172+ data += sizeof(__be32);
173+ }
174+
175+ /* hop_lim and node_id (wide) */
176+ if (trace->type.bit8) {
177+ byte = ipv6_hdr(skb)->hop_limit;
178+ if (skb->dev)
179+ byte--;
180+
181+ raw64 = dev_net(skb->dev)->ipv6.sysctl.ioam6_id_wide;
182+
183+ *(__be64 *)data = cpu_to_be64(((u64)byte << 56) | raw64);
184+ data += sizeof(__be64);
185+ }
186+
187+ /* ingress_if_id and egress_if_id (wide) */
188+ if (trace->type.bit9) {
189+ if (!skb->dev)
190+ raw32 = IOAM6_U32_UNAVAILABLE;
191+ else
192+ raw32 = __in6_dev_get(skb->dev)->cnf.ioam6_id_wide;
193+
194+ *(__be32 *)data = cpu_to_be32(raw32);
195+ data += sizeof(__be32);
196+
197+ if (skb_dst(skb)->dev->flags & IFF_LOOPBACK)
198+ raw32 = IOAM6_U32_UNAVAILABLE;
199+ else
200+ raw32 = __in6_dev_get(skb_dst(skb)->dev)->cnf.ioam6_id_wide;
201+
202+ *(__be32 *)data = cpu_to_be32(raw32);
203+ data += sizeof(__be32);
204+ }
205+
206+ /* namespace data (wide) */
207+ if (trace->type.bit10) {
208+ *(__be64 *)data = ns->data_wide;
209+ data += sizeof(__be64);
210+ }
211+
212+ /* buffer occupancy */
213+ if (trace->type.bit11) {
214+ *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
215+ data += sizeof(__be32);
216+ }
217+
218+ /* opaque state snapshot */
219+ if (trace->type.bit22) {
220+ if (!sc) {
221+ *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE >> 8);
222+ } else {
223+ *(__be32 *)data = sc->hdr;
224+ data += sizeof(__be32);
225+
226+ memcpy(data, sc->data, sc->len);
227+ }
228+ }
229+}
230+
231+/* called with rcu_read_lock() */
232+void ioam6_fill_trace_data(struct sk_buff *skb,
233+ struct ioam6_namespace *ns,
234+ struct ioam6_trace_hdr *trace)
235+{
236+ struct ioam6_schema *sc;
237+ u8 sclen = 0;
238+
239+ /* Skip if Overflow flag is set OR
240+ * if an unknown type (bit 12-21) is set
241+ */
242+ if (trace->overflow ||
243+ trace->type.bit12 | trace->type.bit13 | trace->type.bit14 |
244+ trace->type.bit15 | trace->type.bit16 | trace->type.bit17 |
245+ trace->type.bit18 | trace->type.bit19 | trace->type.bit20 |
246+ trace->type.bit21) {
247+ return;
248+ }
249+
250+ /* NodeLen does not include Opaque State Snapshot length. We need to
251+ * take it into account if the corresponding bit is set (bit 22) and
252+ * if the current IOAM namespace has an active schema attached to it
253+ */
254+ sc = rcu_dereference(ns->schema);
255+ if (trace->type.bit22) {
256+ sclen = sizeof_field(struct ioam6_schema, hdr) / 4;
257+
258+ if (sc)
259+ sclen += sc->len / 4;
260+ }
261+
262+ /* If there is no space remaining, we set the Overflow flag and we
263+ * skip without filling the trace
264+ */
265+ if (!trace->remlen || trace->remlen < trace->nodelen + sclen) {
266+ trace->overflow = 1;
267+ return;
268+ }
269+
270+ __ioam6_fill_trace_data(skb, ns, trace, sc, sclen);
271+ trace->remlen -= trace->nodelen + sclen;
272+}
273+
274+static int __net_init ioam6_net_init(struct net *net)
275+{
276+ struct ioam6_pernet_data *nsdata;
277+ int err = -ENOMEM;
278+
279+ nsdata = kzalloc(sizeof(*nsdata), GFP_KERNEL);
280+ if (!nsdata)
281+ goto out;
282+
283+ mutex_init(&nsdata->lock);
284+ net->ipv6.ioam6_data = nsdata;
285+
286+ err = rhashtable_init(&nsdata->namespaces, &rht_ns_params);
287+ if (err)
288+ goto free_nsdata;
289+
290+ err = rhashtable_init(&nsdata->schemas, &rht_sc_params);
291+ if (err)
292+ goto free_rht_ns;
293+
294+out:
295+ return err;
296+free_rht_ns:
297+ rhashtable_destroy(&nsdata->namespaces);
298+free_nsdata:
299+ kfree(nsdata);
300+ net->ipv6.ioam6_data = NULL;
301+ goto out;
302+}
303+
304+static void __net_exit ioam6_net_exit(struct net *net)
305+{
306+ struct ioam6_pernet_data *nsdata = ioam6_pernet(net);
307+
308+ rhashtable_free_and_destroy(&nsdata->namespaces, ioam6_free_ns, NULL);
309+ rhashtable_free_and_destroy(&nsdata->schemas, ioam6_free_sc, NULL);
310+
311+ kfree(nsdata);
312+}
313+
314+static struct pernet_operations ioam6_net_ops = {
315+ .init = ioam6_net_init,
316+ .exit = ioam6_net_exit,
317+};
318+
319+int __init ioam6_init(void)
320+{
321+ int err = register_pernet_subsys(&ioam6_net_ops);
322+
323+ if (err)
324+ return err;
325+
326+ pr_info("In-situ OAM (IOAM) with IPv6\n");
327+ return 0;
328+}
329+
330+void ioam6_exit(void)
331+{
332+ unregister_pernet_subsys(&ioam6_net_ops);
333+}
net/ipv6/sysctl_net_ipv6.c+19−0
@@ -21,6 +21,7 @@ · 20 unmodified lines
2121 #ifdef CONFIG_NETLABEL
2222 #include <net/calipso.h>
2323 #endif
24+#include <linux/ioam6.h>
2425
2526 static int two = 2;
2627 static int three = 3;
@@ -28,6 +29,8 @@ static int flowlabel_reflect_max = 0x7; · 1 unmodified line
2829 static int auto_flowlabels_max = IP6_AUTO_FLOW_LABEL_MAX;
2930 static u32 rt6_multipath_hash_fields_all_mask =
3031 FIB_MULTIPATH_HASH_FIELD_ALL_MASK;
32+static u32 ioam6_id_max = IOAM6_DEFAULT_ID;
33+static u64 ioam6_id_wide_max = IOAM6_DEFAULT_ID_WIDE;
3134
3235 static int proc_rt6_multipath_hash_policy(struct ctl_table *table, int write,
3336 void *buffer, size_t *lenp, loff_t *ppos)
@@ -196,6 +199,22 @@ static struct ctl_table ipv6_table_template[] = { · 162 unmodified lines
196199 .extra1 = SYSCTL_ZERO,
197200 .extra2 = &two,
198201 },
202+ {
203+ .procname = "ioam6_id",
204+ .data = &init_net.ipv6.sysctl.ioam6_id,
205+ .maxlen = sizeof(u32),
206+ .mode = 0644,
207+ .proc_handler = proc_douintvec_minmax,
208+ .extra2 = &ioam6_id_max,
209+ },
210+ {
211+ .procname = "ioam6_id_wide",
212+ .data = &init_net.ipv6.sysctl.ioam6_id_wide,
213+ .maxlen = sizeof(u64),
214+ .mode = 0644,
215+ .proc_handler = proc_doulongvec_minmax,
216+ .extra2 = &ioam6_id_wide_max,
217+ },
199218 { }
200219 };
201220