Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

Signed-off-by: Justin Iurman <justin.iurman@uliege.be>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Justin Iurman and committed by
David S. Miller
9ee11f0f db67f219

+557 -1
+13
include/linux/ioam6.h
··· 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 */
+3
include/linux/ipv6.h
··· 76 76 __s32 disable_policy; 77 77 __s32 ndisc_tclass; 78 78 __s32 rpl_seg_enabled; 79 + __u32 ioam6_id; 80 + __u32 ioam6_id_wide; 81 + __u8 ioam6_enabled; 79 82 80 83 struct ctl_table_header *sysctl_header; 81 84 };
+64
include/net/ioam6.h
··· 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 */
+3
include/net/netns/ipv6.h
··· 51 51 int max_dst_opts_len; 52 52 int max_hbh_opts_len; 53 53 int seg6_flowlabel; 54 + u32 ioam6_id; 55 + u64 ioam6_id_wide; 54 56 bool skip_notify_on_dev_down; 55 57 u8 fib_notify_on_flag_change; 56 58 }; ··· 112 110 spinlock_t lock; 113 111 u32 seq; 114 112 } ip6addrlbl_table; 113 + struct ioam6_pernet_data *ioam6_data; 115 114 }; 116 115 117 116 #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
+1
include/uapi/linux/in6.h
··· 145 145 #define IPV6_TLV_PADN 1 146 146 #define IPV6_TLV_ROUTERALERT 5 147 147 #define IPV6_TLV_CALIPSO 7 /* RFC 5570 */ 148 + #define IPV6_TLV_IOAM 49 /* TEMPORARY IANA allocation for IOAM */ 148 149 #define IPV6_TLV_JUMBO 194 149 150 #define IPV6_TLV_HAO 201 /* home address option */ 150 151
+9
include/uapi/linux/ioam6.h
··· 12 12 #include <asm/byteorder.h> 13 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 25 * IPv6 IOAM Option Header 17 26 */
+3
include/uapi/linux/ipv6.h
··· 190 190 DEVCONF_NDISC_TCLASS, 191 191 DEVCONF_RPL_SEG_ENABLED, 192 192 DEVCONF_RA_DEFRTR_METRIC, 193 + DEVCONF_IOAM6_ENABLED, 194 + DEVCONF_IOAM6_ID, 195 + DEVCONF_IOAM6_ID_WIDE, 193 196 DEVCONF_MAX 194 197 }; 195 198
+1 -1
net/ipv6/Makefile
··· 10 10 route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \ 11 11 raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o ping.o \ 12 12 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 14 14 15 15 ipv6-offload := ip6_offload.o tcpv6_offload.o exthdrs_offload.o 16 16
+37
net/ipv6/addrconf.c
··· 89 89 #include <linux/proc_fs.h> 90 90 #include <linux/seq_file.h> 91 91 #include <linux/export.h> 92 + #include <linux/ioam6.h> 92 93 93 94 #define INFINITY_LIFE_TIME 0xFFFFFFFF 94 95 95 96 #define IPV6_MAX_STRLEN \ 96 97 sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") 98 + 99 + static u32 ioam6_if_id_max = U16_MAX; 97 100 98 101 static inline u32 cstamp_delta(unsigned long cstamp) 99 102 { ··· 240 237 .addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64, 241 238 .disable_policy = 0, 242 239 .rpl_seg_enabled = 0, 240 + .ioam6_enabled = 0, 241 + .ioam6_id = IOAM6_DEFAULT_IF_ID, 242 + .ioam6_id_wide = IOAM6_DEFAULT_IF_ID_WIDE, 243 243 }; 244 244 245 245 static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { ··· 299 293 .addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64, 300 294 .disable_policy = 0, 301 295 .rpl_seg_enabled = 0, 296 + .ioam6_enabled = 0, 297 + .ioam6_id = IOAM6_DEFAULT_IF_ID, 298 + .ioam6_id_wide = IOAM6_DEFAULT_IF_ID_WIDE, 302 299 }; 303 300 304 301 /* Check if link is ready: is it up and is a valid qdisc available */ ··· 5533 5524 array[DEVCONF_DISABLE_POLICY] = cnf->disable_policy; 5534 5525 array[DEVCONF_NDISC_TCLASS] = cnf->ndisc_tclass; 5535 5526 array[DEVCONF_RPL_SEG_ENABLED] = cnf->rpl_seg_enabled; 5527 + array[DEVCONF_IOAM6_ENABLED] = cnf->ioam6_enabled; 5528 + array[DEVCONF_IOAM6_ID] = cnf->ioam6_id; 5529 + array[DEVCONF_IOAM6_ID_WIDE] = cnf->ioam6_id_wide; 5536 5530 } 5537 5531 5538 5532 static inline size_t inet6_ifla6_size(void) ··· 6941 6929 .maxlen = sizeof(int), 6942 6930 .mode = 0644, 6943 6931 .proc_handler = proc_dointvec, 6932 + }, 6933 + { 6934 + .procname = "ioam6_enabled", 6935 + .data = &ipv6_devconf.ioam6_enabled, 6936 + .maxlen = sizeof(u8), 6937 + .mode = 0644, 6938 + .proc_handler = proc_dou8vec_minmax, 6939 + .extra1 = (void *)SYSCTL_ZERO, 6940 + .extra2 = (void *)SYSCTL_ONE, 6941 + }, 6942 + { 6943 + .procname = "ioam6_id", 6944 + .data = &ipv6_devconf.ioam6_id, 6945 + .maxlen = sizeof(u32), 6946 + .mode = 0644, 6947 + .proc_handler = proc_douintvec_minmax, 6948 + .extra1 = (void *)SYSCTL_ZERO, 6949 + .extra2 = (void *)&ioam6_if_id_max, 6950 + }, 6951 + { 6952 + .procname = "ioam6_id_wide", 6953 + .data = &ipv6_devconf.ioam6_id_wide, 6954 + .maxlen = sizeof(u32), 6955 + .mode = 0644, 6956 + .proc_handler = proc_douintvec, 6944 6957 }, 6945 6958 { 6946 6959 /* sentinel */
+10
net/ipv6/af_inet6.c
··· 62 62 #include <net/rpl.h> 63 63 #include <net/compat.h> 64 64 #include <net/xfrm.h> 65 + #include <net/ioam6.h> 65 66 66 67 #include <linux/uaccess.h> 67 68 #include <linux/mroute6.h> ··· 962 961 net->ipv6.sysctl.fib_notify_on_flag_change = 0; 963 962 atomic_set(&net->ipv6.fib6_sernum, 1); 964 963 964 + net->ipv6.sysctl.ioam6_id = IOAM6_DEFAULT_ID; 965 + net->ipv6.sysctl.ioam6_id_wide = IOAM6_DEFAULT_ID_WIDE; 966 + 965 967 err = ipv6_init_mibs(net); 966 968 if (err) 967 969 return err; ··· 1195 1191 if (err) 1196 1192 goto rpl_fail; 1197 1193 1194 + err = ioam6_init(); 1195 + if (err) 1196 + goto ioam6_fail; 1197 + 1198 1198 err = igmp6_late_init(); 1199 1199 if (err) 1200 1200 goto igmp6_late_err; ··· 1221 1213 igmp6_late_cleanup(); 1222 1214 #endif 1223 1215 igmp6_late_err: 1216 + ioam6_exit(); 1217 + ioam6_fail: 1224 1218 rpl_exit(); 1225 1219 rpl_fail: 1226 1220 seg6_exit();
+61
net/ipv6/exthdrs.c
··· 49 49 #include <net/seg6_hmac.h> 50 50 #endif 51 51 #include <net/rpl.h> 52 + #include <linux/ioam6.h> 53 + #include <net/ioam6.h> 54 + #include <net/dst_metadata.h> 52 55 53 56 #include <linux/uaccess.h> 54 57 ··· 931 928 return false; 932 929 } 933 930 931 + /* IOAM */ 932 + 933 + static bool ipv6_hop_ioam(struct sk_buff *skb, int optoff) 934 + { 935 + struct ioam6_trace_hdr *trace; 936 + struct ioam6_namespace *ns; 937 + struct ioam6_hdr *hdr; 938 + 939 + /* Bad alignment (must be 4n-aligned) */ 940 + if (optoff & 3) 941 + goto drop; 942 + 943 + /* Ignore if IOAM is not enabled on ingress */ 944 + if (!__in6_dev_get(skb->dev)->cnf.ioam6_enabled) 945 + goto ignore; 946 + 947 + /* Truncated Option header */ 948 + hdr = (struct ioam6_hdr *)(skb_network_header(skb) + optoff); 949 + if (hdr->opt_len < 2) 950 + goto drop; 951 + 952 + switch (hdr->type) { 953 + case IOAM6_TYPE_PREALLOC: 954 + /* Truncated Pre-allocated Trace header */ 955 + if (hdr->opt_len < 2 + sizeof(*trace)) 956 + goto drop; 957 + 958 + /* Malformed Pre-allocated Trace header */ 959 + trace = (struct ioam6_trace_hdr *)((u8 *)hdr + sizeof(*hdr)); 960 + if (hdr->opt_len < 2 + sizeof(*trace) + trace->remlen * 4) 961 + goto drop; 962 + 963 + /* Ignore if the IOAM namespace is unknown */ 964 + ns = ioam6_namespace(ipv6_skb_net(skb), trace->namespace_id); 965 + if (!ns) 966 + goto ignore; 967 + 968 + if (!skb_valid_dst(skb)) 969 + ip6_route_input(skb); 970 + 971 + ioam6_fill_trace_data(skb, ns, trace); 972 + break; 973 + default: 974 + break; 975 + } 976 + 977 + ignore: 978 + return true; 979 + 980 + drop: 981 + kfree_skb(skb); 982 + return false; 983 + } 984 + 934 985 /* Jumbo payload */ 935 986 936 987 static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff) ··· 1055 998 { 1056 999 .type = IPV6_TLV_ROUTERALERT, 1057 1000 .func = ipv6_hop_ra, 1001 + }, 1002 + { 1003 + .type = IPV6_TLV_IOAM, 1004 + .func = ipv6_hop_ioam, 1058 1005 }, 1059 1006 { 1060 1007 .type = IPV6_TLV_JUMBO,
+333
net/ipv6/ioam6.c
··· 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 + }
+19
net/ipv6/sysctl_net_ipv6.c
··· 21 21 #ifdef CONFIG_NETLABEL 22 22 #include <net/calipso.h> 23 23 #endif 24 + #include <linux/ioam6.h> 24 25 25 26 static int two = 2; 26 27 static int three = 3; ··· 29 28 static int auto_flowlabels_max = IP6_AUTO_FLOW_LABEL_MAX; 30 29 static u32 rt6_multipath_hash_fields_all_mask = 31 30 FIB_MULTIPATH_HASH_FIELD_ALL_MASK; 31 + static u32 ioam6_id_max = IOAM6_DEFAULT_ID; 32 + static u64 ioam6_id_wide_max = IOAM6_DEFAULT_ID_WIDE; 32 33 33 34 static int proc_rt6_multipath_hash_policy(struct ctl_table *table, int write, 34 35 void *buffer, size_t *lenp, loff_t *ppos) ··· 198 195 .proc_handler = proc_dou8vec_minmax, 199 196 .extra1 = SYSCTL_ZERO, 200 197 .extra2 = &two, 198 + }, 199 + { 200 + .procname = "ioam6_id", 201 + .data = &init_net.ipv6.sysctl.ioam6_id, 202 + .maxlen = sizeof(u32), 203 + .mode = 0644, 204 + .proc_handler = proc_douintvec_minmax, 205 + .extra2 = &ioam6_id_max, 206 + }, 207 + { 208 + .procname = "ioam6_id_wide", 209 + .data = &init_net.ipv6.sysctl.ioam6_id_wide, 210 + .maxlen = sizeof(u64), 211 + .mode = 0644, 212 + .proc_handler = proc_doulongvec_minmax, 213 + .extra2 = &ioam6_id_wide_max, 201 214 }, 202 215 { } 203 216 };