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
76 __s32 disable_policy;76 __s32 disable_policy;
77 __s32 ndisc_tclass;77 __s32 ndisc_tclass;
78 __s32 rpl_seg_enabled;78 __s32 rpl_seg_enabled;
79 __u32 ioam6_id;
80 __u32 ioam6_id_wide;
81 __u8 ioam6_enabled;
79 82
80 struct ctl_table_header *sysctl_header;83 struct ctl_table_header *sysctl_header;
81};84};
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
17struct 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
28struct 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
41struct ioam6_pernet_data {
42 struct mutex lock;
43 struct rhashtable namespaces;
44 struct rhashtable schemas;
45};
46
47static 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
56struct ioam6_namespace *ioam6_namespace(struct net *net, __be16 id);
57void ioam6_fill_trace_data(struct sk_buff *skb,
58 struct ioam6_namespace *ns,
59 struct ioam6_trace_hdr *trace);
60
61int ioam6_init(void);
62void 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
51 int max_dst_opts_len;51 int max_dst_opts_len;
52 int max_hbh_opts_len;52 int max_hbh_opts_len;
53 int seg6_flowlabel;53 int seg6_flowlabel;
54 u32 ioam6_id;
55 u64 ioam6_id_wide;
54 bool skip_notify_on_dev_down;56 bool skip_notify_on_dev_down;
55 u8 fib_notify_on_flag_change;57 u8 fib_notify_on_flag_change;
56};58};
@@ -110,6 +112,7 @@ struct netns_ipv6 { · 53 unmodified lines
110 spinlock_t lock;112 spinlock_t lock;
111 u32 seq;113 u32 seq;
112 } ip6addrlbl_table;114 } ip6addrlbl_table;
115 struct ioam6_pernet_data *ioam6_data;
113};116};
114 117
115#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)118#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
145#define IPV6_TLV_PADN 1145#define IPV6_TLV_PADN 1
146#define IPV6_TLV_ROUTERALERT 5146#define IPV6_TLV_ROUTERALERT 5
147#define IPV6_TLV_CALIPSO 7 /* RFC 5570 */147#define IPV6_TLV_CALIPSO 7 /* RFC 5570 */
148#define IPV6_TLV_IOAM 49 /* TEMPORARY IANA allocation for IOAM */
148#define IPV6_TLV_JUMBO 194149#define IPV6_TLV_JUMBO 194
149#define IPV6_TLV_HAO 201 /* home address option */150#define IPV6_TLV_HAO 201 /* home address option */
150 151
include/uapi/linux/ioam6.h+9−0
@@ -12,6 +12,15 @@ · 11 unmodified lines
12#include <asm/byteorder.h>12#include <asm/byteorder.h>
13#include <linux/types.h>13#include <linux/types.h>
14 14
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
15/*24/*
16 * IPv6 IOAM Option Header25 * IPv6 IOAM Option Header
17 */26 */
include/uapi/linux/ipv6.h+3−0
@@ -190,6 +190,9 @@ enum { · 189 unmodified lines
190 DEVCONF_NDISC_TCLASS,190 DEVCONF_NDISC_TCLASS,
191 DEVCONF_RPL_SEG_ENABLED,191 DEVCONF_RPL_SEG_ENABLED,
192 DEVCONF_RA_DEFRTR_METRIC,192 DEVCONF_RA_DEFRTR_METRIC,
193 DEVCONF_IOAM6_ENABLED,
194 DEVCONF_IOAM6_ID,
195 DEVCONF_IOAM6_ID_WIDE,
193 DEVCONF_MAX196 DEVCONF_MAX
194};197};
195 198
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
10 route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \10 route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \
11 raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o ping.o \11 raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o ping.o \
12 exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o \12 exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o \
13 udp_offload.o seg6.o fib6_notifier.o rpl.o13 udp_offload.o seg6.o fib6_notifier.o rpl.o ioam6.o
14 14
15ipv6-offload := ip6_offload.o tcpv6_offload.o exthdrs_offload.o15ipv6-offload := ip6_offload.o tcpv6_offload.o exthdrs_offload.o
16 16
net/ipv6/addrconf.c+37−0
@@ -89,12 +89,15 @@ · 88 unmodified lines
89#include <linux/proc_fs.h>89#include <linux/proc_fs.h>
90#include <linux/seq_file.h>90#include <linux/seq_file.h>
91#include <linux/export.h>91#include <linux/export.h>
92#include <linux/ioam6.h>
92 93
93#define INFINITY_LIFE_TIME 0xFFFFFFFF94#define INFINITY_LIFE_TIME 0xFFFFFFFF
94 95
95#define IPV6_MAX_STRLEN \96#define IPV6_MAX_STRLEN \
96 sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")97 sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")
97 98
99static u32 ioam6_if_id_max = U16_MAX;
100
98static inline u32 cstamp_delta(unsigned long cstamp)101static inline u32 cstamp_delta(unsigned long cstamp)
99{102{
100 return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;103 return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
@@ -237,6 +240,9 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { · 136 unmodified lines
237 .addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64,240 .addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64,
238 .disable_policy = 0,241 .disable_policy = 0,
239 .rpl_seg_enabled = 0,242 .rpl_seg_enabled = 0,
243 .ioam6_enabled = 0,
244 .ioam6_id = IOAM6_DEFAULT_IF_ID,
245 .ioam6_id_wide = IOAM6_DEFAULT_IF_ID_WIDE,
240};246};
241 247
242static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {248static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
@@ -293,6 +299,9 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { · 50 unmodified lines
293 .addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64,299 .addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64,
294 .disable_policy = 0,300 .disable_policy = 0,
295 .rpl_seg_enabled = 0,301 .rpl_seg_enabled = 0,
302 .ioam6_enabled = 0,
303 .ioam6_id = IOAM6_DEFAULT_IF_ID,
304 .ioam6_id_wide = IOAM6_DEFAULT_IF_ID_WIDE,
296};305};
297 306
298/* Check if link is ready: is it up and is a valid qdisc available */307/* 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
5524 array[DEVCONF_DISABLE_POLICY] = cnf->disable_policy;5533 array[DEVCONF_DISABLE_POLICY] = cnf->disable_policy;
5525 array[DEVCONF_NDISC_TCLASS] = cnf->ndisc_tclass;5534 array[DEVCONF_NDISC_TCLASS] = cnf->ndisc_tclass;
5526 array[DEVCONF_RPL_SEG_ENABLED] = cnf->rpl_seg_enabled;5535 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;
5527}5539}
5528 5540
5529static inline size_t inet6_ifla6_size(void)5541static inline size_t inet6_ifla6_size(void)
@@ -6930,6 +6942,31 @@ static const struct ctl_table addrconf_sysctl[] = { · 1400 unmodified lines
6930 .mode = 0644,6942 .mode = 0644,
6931 .proc_handler = proc_dointvec,6943 .proc_handler = proc_dointvec,
6932 },6944 },
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 },
6933 {6970 {
6934 /* sentinel */6971 /* sentinel */
6935 }6972 }
net/ipv6/af_inet6.c+10−0
@@ -62,6 +62,7 @@ · 61 unmodified lines
62#include <net/rpl.h>62#include <net/rpl.h>
63#include <net/compat.h>63#include <net/compat.h>
64#include <net/xfrm.h>64#include <net/xfrm.h>
65#include <net/ioam6.h>
65 66
66#include <linux/uaccess.h>67#include <linux/uaccess.h>
67#include <linux/mroute6.h>68#include <linux/mroute6.h>
@@ -961,6 +962,9 @@ static int __net_init inet6_net_init(struct net *net) · 893 unmodified lines
961 net->ipv6.sysctl.fib_notify_on_flag_change = 0;962 net->ipv6.sysctl.fib_notify_on_flag_change = 0;
962 atomic_set(&net->ipv6.fib6_sernum, 1);963 atomic_set(&net->ipv6.fib6_sernum, 1);
963 964
965 net->ipv6.sysctl.ioam6_id = IOAM6_DEFAULT_ID;
966 net->ipv6.sysctl.ioam6_id_wide = IOAM6_DEFAULT_ID_WIDE;
967
964 err = ipv6_init_mibs(net);968 err = ipv6_init_mibs(net);
965 if (err)969 if (err)
966 return err;970 return err;
@@ -1191,6 +1195,10 @@ static int __init inet6_init(void) · 224 unmodified lines
1191 if (err)1195 if (err)
1192 goto rpl_fail;1196 goto rpl_fail;
1193 1197
1198 err = ioam6_init();
1199 if (err)
1200 goto ioam6_fail;
1201
1194 err = igmp6_late_init();1202 err = igmp6_late_init();
1195 if (err)1203 if (err)
1196 goto igmp6_late_err;1204 goto igmp6_late_err;
@@ -1213,6 +1221,8 @@ sysctl_fail: · 16 unmodified lines
1213 igmp6_late_cleanup();1221 igmp6_late_cleanup();
1214#endif1222#endif
1215igmp6_late_err:1223igmp6_late_err:
1224 ioam6_exit();
1225ioam6_fail:
1216 rpl_exit();1226 rpl_exit();
1217rpl_fail:1227rpl_fail:
1218 seg6_exit();1228 seg6_exit();
net/ipv6/exthdrs.c+61−0
@@ -49,6 +49,9 @@ · 48 unmodified lines
49#include <net/seg6_hmac.h>49#include <net/seg6_hmac.h>
50#endif50#endif
51#include <net/rpl.h>51#include <net/rpl.h>
52#include <linux/ioam6.h>
53#include <net/ioam6.h>
54#include <net/dst_metadata.h>
52 55
53#include <linux/uaccess.h>56#include <linux/uaccess.h>
54 57
@@ -928,6 +931,60 @@ static bool ipv6_hop_ra(struct sk_buff *skb, int optoff) · 873 unmodified lines
928 return false;931 return false;
929}932}
930 933
934/* IOAM */
935
936static 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
980ignore:
981 return true;
982
983drop:
984 kfree_skb(skb);
985 return false;
986}
987
931/* Jumbo payload */988/* Jumbo payload */
932 989
933static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff)990static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
@@ -999,6 +1056,10 @@ static const struct tlvtype_proc tlvprochopopt_lst[] = { · 65 unmodified lines
999 .type = IPV6_TLV_ROUTERALERT,1056 .type = IPV6_TLV_ROUTERALERT,
1000 .func = ipv6_hop_ra,1057 .func = ipv6_hop_ra,
1001 },1058 },
1059 {
1060 .type = IPV6_TLV_IOAM,
1061 .func = ipv6_hop_ioam,
1062 },
1002 {1063 {
1003 .type = IPV6_TLV_JUMBO,1064 .type = IPV6_TLV_JUMBO,
1004 .func = ipv6_hop_jumbo,1065 .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
19static void ioam6_ns_release(struct ioam6_namespace *ns)
20{
21 kfree_rcu(ns, rcu);
22}
23
24static void ioam6_sc_release(struct ioam6_schema *sc)
25{
26 kfree_rcu(sc, rcu);
27}
28
29static 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
37static 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
45static 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
52static 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
59static 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
67static 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
75struct 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
82static 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() */
232void 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
274static 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
294out:
295 return err;
296free_rht_ns:
297 rhashtable_destroy(&nsdata->namespaces);
298free_nsdata:
299 kfree(nsdata);
300 net->ipv6.ioam6_data = NULL;
301 goto out;
302}
303
304static 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
314static struct pernet_operations ioam6_net_ops = {
315 .init = ioam6_net_init,
316 .exit = ioam6_net_exit,
317};
318
319int __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
330void 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
21#ifdef CONFIG_NETLABEL21#ifdef CONFIG_NETLABEL
22#include <net/calipso.h>22#include <net/calipso.h>
23#endif23#endif
24#include <linux/ioam6.h>
24 25
25static int two = 2;26static int two = 2;
26static int three = 3;27static int three = 3;
@@ -28,6 +29,8 @@ static int flowlabel_reflect_max = 0x7; · 1 unmodified line
28static int auto_flowlabels_max = IP6_AUTO_FLOW_LABEL_MAX;29static int auto_flowlabels_max = IP6_AUTO_FLOW_LABEL_MAX;
29static u32 rt6_multipath_hash_fields_all_mask =30static u32 rt6_multipath_hash_fields_all_mask =
30 FIB_MULTIPATH_HASH_FIELD_ALL_MASK;31 FIB_MULTIPATH_HASH_FIELD_ALL_MASK;
32static u32 ioam6_id_max = IOAM6_DEFAULT_ID;
33static u64 ioam6_id_wide_max = IOAM6_DEFAULT_ID_WIDE;
31 34
32static int proc_rt6_multipath_hash_policy(struct ctl_table *table, int write,35static int proc_rt6_multipath_hash_policy(struct ctl_table *table, int write,
33 void *buffer, size_t *lenp, loff_t *ppos)36 void *buffer, size_t *lenp, loff_t *ppos)
@@ -196,6 +199,22 @@ static struct ctl_table ipv6_table_template[] = { · 162 unmodified lines
196 .extra1 = SYSCTL_ZERO,199 .extra1 = SYSCTL_ZERO,
197 .extra2 = &two,200 .extra2 = &two,
198 },201 },
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 },
199 { }218 { }
200};219};
201 220