| 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/ioam6_genl.h> | 
|---|
| 15 | #include <linux/rhashtable.h> | 
|---|
| 16 | #include <linux/netdevice.h> | 
|---|
| 17 |  | 
|---|
| 18 | #include <net/addrconf.h> | 
|---|
| 19 | #include <net/genetlink.h> | 
|---|
| 20 | #include <net/ioam6.h> | 
|---|
| 21 | #include <net/sch_generic.h> | 
|---|
| 22 |  | 
|---|
| 23 | static void ioam6_ns_release(struct ioam6_namespace *ns) | 
|---|
| 24 | { | 
|---|
| 25 | kfree_rcu(ns, rcu); | 
|---|
| 26 | } | 
|---|
| 27 |  | 
|---|
| 28 | static void ioam6_sc_release(struct ioam6_schema *sc) | 
|---|
| 29 | { | 
|---|
| 30 | kfree_rcu(sc, rcu); | 
|---|
| 31 | } | 
|---|
| 32 |  | 
|---|
| 33 | static void ioam6_free_ns(void *ptr, void *arg) | 
|---|
| 34 | { | 
|---|
| 35 | struct ioam6_namespace *ns = (struct ioam6_namespace *)ptr; | 
|---|
| 36 |  | 
|---|
| 37 | if (ns) | 
|---|
| 38 | ioam6_ns_release(ns); | 
|---|
| 39 | } | 
|---|
| 40 |  | 
|---|
| 41 | static void ioam6_free_sc(void *ptr, void *arg) | 
|---|
| 42 | { | 
|---|
| 43 | struct ioam6_schema *sc = (struct ioam6_schema *)ptr; | 
|---|
| 44 |  | 
|---|
| 45 | if (sc) | 
|---|
| 46 | ioam6_sc_release(sc); | 
|---|
| 47 | } | 
|---|
| 48 |  | 
|---|
| 49 | static int ioam6_ns_cmpfn(struct rhashtable_compare_arg *arg, const void *obj) | 
|---|
| 50 | { | 
|---|
| 51 | const struct ioam6_namespace *ns = obj; | 
|---|
| 52 |  | 
|---|
| 53 | return (ns->id != *(__be16 *)arg->key); | 
|---|
| 54 | } | 
|---|
| 55 |  | 
|---|
| 56 | static int ioam6_sc_cmpfn(struct rhashtable_compare_arg *arg, const void *obj) | 
|---|
| 57 | { | 
|---|
| 58 | const struct ioam6_schema *sc = obj; | 
|---|
| 59 |  | 
|---|
| 60 | return (sc->id != *(u32 *)arg->key); | 
|---|
| 61 | } | 
|---|
| 62 |  | 
|---|
| 63 | static const struct rhashtable_params rht_ns_params = { | 
|---|
| 64 | .key_len		= sizeof(__be16), | 
|---|
| 65 | .key_offset		= offsetof(struct ioam6_namespace, id), | 
|---|
| 66 | .head_offset		= offsetof(struct ioam6_namespace, head), | 
|---|
| 67 | .automatic_shrinking	= true, | 
|---|
| 68 | .obj_cmpfn		= ioam6_ns_cmpfn, | 
|---|
| 69 | }; | 
|---|
| 70 |  | 
|---|
| 71 | static const struct rhashtable_params rht_sc_params = { | 
|---|
| 72 | .key_len		= sizeof(u32), | 
|---|
| 73 | .key_offset		= offsetof(struct ioam6_schema, id), | 
|---|
| 74 | .head_offset		= offsetof(struct ioam6_schema, head), | 
|---|
| 75 | .automatic_shrinking	= true, | 
|---|
| 76 | .obj_cmpfn		= ioam6_sc_cmpfn, | 
|---|
| 77 | }; | 
|---|
| 78 |  | 
|---|
| 79 | static struct genl_family ioam6_genl_family; | 
|---|
| 80 |  | 
|---|
| 81 | static const struct nla_policy ioam6_genl_policy_addns[] = { | 
|---|
| 82 | [IOAM6_ATTR_NS_ID]	= { .type = NLA_U16 }, | 
|---|
| 83 | [IOAM6_ATTR_NS_DATA]	= { .type = NLA_U32 }, | 
|---|
| 84 | [IOAM6_ATTR_NS_DATA_WIDE] = { .type = NLA_U64 }, | 
|---|
| 85 | }; | 
|---|
| 86 |  | 
|---|
| 87 | static const struct nla_policy ioam6_genl_policy_delns[] = { | 
|---|
| 88 | [IOAM6_ATTR_NS_ID]	= { .type = NLA_U16 }, | 
|---|
| 89 | }; | 
|---|
| 90 |  | 
|---|
| 91 | static const struct nla_policy ioam6_genl_policy_addsc[] = { | 
|---|
| 92 | [IOAM6_ATTR_SC_ID]	= { .type = NLA_U32 }, | 
|---|
| 93 | [IOAM6_ATTR_SC_DATA]	= { .type = NLA_BINARY, | 
|---|
| 94 | .len = IOAM6_MAX_SCHEMA_DATA_LEN }, | 
|---|
| 95 | }; | 
|---|
| 96 |  | 
|---|
| 97 | static const struct nla_policy ioam6_genl_policy_delsc[] = { | 
|---|
| 98 | [IOAM6_ATTR_SC_ID]	= { .type = NLA_U32 }, | 
|---|
| 99 | }; | 
|---|
| 100 |  | 
|---|
| 101 | static const struct nla_policy ioam6_genl_policy_ns_sc[] = { | 
|---|
| 102 | [IOAM6_ATTR_NS_ID]	= { .type = NLA_U16 }, | 
|---|
| 103 | [IOAM6_ATTR_SC_ID]	= { .type = NLA_U32 }, | 
|---|
| 104 | [IOAM6_ATTR_SC_NONE]	= { .type = NLA_FLAG }, | 
|---|
| 105 | }; | 
|---|
| 106 |  | 
|---|
| 107 | static int ioam6_genl_addns(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 108 | { | 
|---|
| 109 | struct ioam6_pernet_data *nsdata; | 
|---|
| 110 | struct ioam6_namespace *ns; | 
|---|
| 111 | u64 data64; | 
|---|
| 112 | u32 data32; | 
|---|
| 113 | __be16 id; | 
|---|
| 114 | int err; | 
|---|
| 115 |  | 
|---|
| 116 | if (!info->attrs[IOAM6_ATTR_NS_ID]) | 
|---|
| 117 | return -EINVAL; | 
|---|
| 118 |  | 
|---|
| 119 | id = cpu_to_be16(nla_get_u16(info->attrs[IOAM6_ATTR_NS_ID])); | 
|---|
| 120 | nsdata = ioam6_pernet(net: genl_info_net(info)); | 
|---|
| 121 |  | 
|---|
| 122 | mutex_lock(lock: &nsdata->lock); | 
|---|
| 123 |  | 
|---|
| 124 | ns = rhashtable_lookup_fast(ht: &nsdata->namespaces, key: &id, params: rht_ns_params); | 
|---|
| 125 | if (ns) { | 
|---|
| 126 | err = -EEXIST; | 
|---|
| 127 | goto out_unlock; | 
|---|
| 128 | } | 
|---|
| 129 |  | 
|---|
| 130 | ns = kzalloc(sizeof(*ns), GFP_KERNEL); | 
|---|
| 131 | if (!ns) { | 
|---|
| 132 | err = -ENOMEM; | 
|---|
| 133 | goto out_unlock; | 
|---|
| 134 | } | 
|---|
| 135 |  | 
|---|
| 136 | ns->id = id; | 
|---|
| 137 |  | 
|---|
| 138 | data32 = nla_get_u32_default(nla: info->attrs[IOAM6_ATTR_NS_DATA], | 
|---|
| 139 | IOAM6_U32_UNAVAILABLE); | 
|---|
| 140 |  | 
|---|
| 141 | data64 = nla_get_u64_default(nla: info->attrs[IOAM6_ATTR_NS_DATA_WIDE], | 
|---|
| 142 | IOAM6_U64_UNAVAILABLE); | 
|---|
| 143 |  | 
|---|
| 144 | ns->data = cpu_to_be32(data32); | 
|---|
| 145 | ns->data_wide = cpu_to_be64(data64); | 
|---|
| 146 |  | 
|---|
| 147 | err = rhashtable_lookup_insert_fast(ht: &nsdata->namespaces, obj: &ns->head, | 
|---|
| 148 | params: rht_ns_params); | 
|---|
| 149 | if (err) | 
|---|
| 150 | kfree(objp: ns); | 
|---|
| 151 |  | 
|---|
| 152 | out_unlock: | 
|---|
| 153 | mutex_unlock(lock: &nsdata->lock); | 
|---|
| 154 | return err; | 
|---|
| 155 | } | 
|---|
| 156 |  | 
|---|
| 157 | static int ioam6_genl_delns(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 158 | { | 
|---|
| 159 | struct ioam6_pernet_data *nsdata; | 
|---|
| 160 | struct ioam6_namespace *ns; | 
|---|
| 161 | struct ioam6_schema *sc; | 
|---|
| 162 | __be16 id; | 
|---|
| 163 | int err; | 
|---|
| 164 |  | 
|---|
| 165 | if (!info->attrs[IOAM6_ATTR_NS_ID]) | 
|---|
| 166 | return -EINVAL; | 
|---|
| 167 |  | 
|---|
| 168 | id = cpu_to_be16(nla_get_u16(info->attrs[IOAM6_ATTR_NS_ID])); | 
|---|
| 169 | nsdata = ioam6_pernet(net: genl_info_net(info)); | 
|---|
| 170 |  | 
|---|
| 171 | mutex_lock(lock: &nsdata->lock); | 
|---|
| 172 |  | 
|---|
| 173 | ns = rhashtable_lookup_fast(ht: &nsdata->namespaces, key: &id, params: rht_ns_params); | 
|---|
| 174 | if (!ns) { | 
|---|
| 175 | err = -ENOENT; | 
|---|
| 176 | goto out_unlock; | 
|---|
| 177 | } | 
|---|
| 178 |  | 
|---|
| 179 | sc = rcu_dereference_protected(ns->schema, | 
|---|
| 180 | lockdep_is_held(&nsdata->lock)); | 
|---|
| 181 |  | 
|---|
| 182 | err = rhashtable_remove_fast(ht: &nsdata->namespaces, obj: &ns->head, | 
|---|
| 183 | params: rht_ns_params); | 
|---|
| 184 | if (err) | 
|---|
| 185 | goto out_unlock; | 
|---|
| 186 |  | 
|---|
| 187 | if (sc) | 
|---|
| 188 | rcu_assign_pointer(sc->ns, NULL); | 
|---|
| 189 |  | 
|---|
| 190 | ioam6_ns_release(ns); | 
|---|
| 191 |  | 
|---|
| 192 | out_unlock: | 
|---|
| 193 | mutex_unlock(lock: &nsdata->lock); | 
|---|
| 194 | return err; | 
|---|
| 195 | } | 
|---|
| 196 |  | 
|---|
| 197 | static int __ioam6_genl_dumpns_element(struct ioam6_namespace *ns, | 
|---|
| 198 | u32 portid, | 
|---|
| 199 | u32 seq, | 
|---|
| 200 | u32 flags, | 
|---|
| 201 | struct sk_buff *skb, | 
|---|
| 202 | u8 cmd) | 
|---|
| 203 | { | 
|---|
| 204 | struct ioam6_schema *sc; | 
|---|
| 205 | u64 data64; | 
|---|
| 206 | u32 data32; | 
|---|
| 207 | void *hdr; | 
|---|
| 208 |  | 
|---|
| 209 | hdr = genlmsg_put(skb, portid, seq, family: &ioam6_genl_family, flags, cmd); | 
|---|
| 210 | if (!hdr) | 
|---|
| 211 | return -ENOMEM; | 
|---|
| 212 |  | 
|---|
| 213 | data32 = be32_to_cpu(ns->data); | 
|---|
| 214 | data64 = be64_to_cpu(ns->data_wide); | 
|---|
| 215 |  | 
|---|
| 216 | if (nla_put_u16(skb, attrtype: IOAM6_ATTR_NS_ID, be16_to_cpu(ns->id)) || | 
|---|
| 217 | (data32 != IOAM6_U32_UNAVAILABLE && | 
|---|
| 218 | nla_put_u32(skb, attrtype: IOAM6_ATTR_NS_DATA, value: data32)) || | 
|---|
| 219 | (data64 != IOAM6_U64_UNAVAILABLE && | 
|---|
| 220 | nla_put_u64_64bit(skb, attrtype: IOAM6_ATTR_NS_DATA_WIDE, | 
|---|
| 221 | value: data64, padattr: IOAM6_ATTR_PAD))) | 
|---|
| 222 | goto nla_put_failure; | 
|---|
| 223 |  | 
|---|
| 224 | rcu_read_lock(); | 
|---|
| 225 |  | 
|---|
| 226 | sc = rcu_dereference(ns->schema); | 
|---|
| 227 | if (sc && nla_put_u32(skb, attrtype: IOAM6_ATTR_SC_ID, value: sc->id)) { | 
|---|
| 228 | rcu_read_unlock(); | 
|---|
| 229 | goto nla_put_failure; | 
|---|
| 230 | } | 
|---|
| 231 |  | 
|---|
| 232 | rcu_read_unlock(); | 
|---|
| 233 |  | 
|---|
| 234 | genlmsg_end(skb, hdr); | 
|---|
| 235 | return 0; | 
|---|
| 236 |  | 
|---|
| 237 | nla_put_failure: | 
|---|
| 238 | genlmsg_cancel(skb, hdr); | 
|---|
| 239 | return -EMSGSIZE; | 
|---|
| 240 | } | 
|---|
| 241 |  | 
|---|
| 242 | static int ioam6_genl_dumpns_start(struct netlink_callback *cb) | 
|---|
| 243 | { | 
|---|
| 244 | struct ioam6_pernet_data *nsdata = ioam6_pernet(net: sock_net(sk: cb->skb->sk)); | 
|---|
| 245 | struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0]; | 
|---|
| 246 |  | 
|---|
| 247 | if (!iter) { | 
|---|
| 248 | iter = kmalloc(sizeof(*iter), GFP_KERNEL); | 
|---|
| 249 | if (!iter) | 
|---|
| 250 | return -ENOMEM; | 
|---|
| 251 |  | 
|---|
| 252 | cb->args[0] = (long)iter; | 
|---|
| 253 | } | 
|---|
| 254 |  | 
|---|
| 255 | rhashtable_walk_enter(ht: &nsdata->namespaces, iter); | 
|---|
| 256 |  | 
|---|
| 257 | return 0; | 
|---|
| 258 | } | 
|---|
| 259 |  | 
|---|
| 260 | static int ioam6_genl_dumpns_done(struct netlink_callback *cb) | 
|---|
| 261 | { | 
|---|
| 262 | struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0]; | 
|---|
| 263 |  | 
|---|
| 264 | rhashtable_walk_exit(iter); | 
|---|
| 265 | kfree(objp: iter); | 
|---|
| 266 |  | 
|---|
| 267 | return 0; | 
|---|
| 268 | } | 
|---|
| 269 |  | 
|---|
| 270 | static int ioam6_genl_dumpns(struct sk_buff *skb, struct netlink_callback *cb) | 
|---|
| 271 | { | 
|---|
| 272 | struct rhashtable_iter *iter; | 
|---|
| 273 | struct ioam6_namespace *ns; | 
|---|
| 274 | int err; | 
|---|
| 275 |  | 
|---|
| 276 | iter = (struct rhashtable_iter *)cb->args[0]; | 
|---|
| 277 | rhashtable_walk_start(iter); | 
|---|
| 278 |  | 
|---|
| 279 | for (;;) { | 
|---|
| 280 | ns = rhashtable_walk_next(iter); | 
|---|
| 281 |  | 
|---|
| 282 | if (IS_ERR(ptr: ns)) { | 
|---|
| 283 | if (PTR_ERR(ptr: ns) == -EAGAIN) | 
|---|
| 284 | continue; | 
|---|
| 285 | err = PTR_ERR(ptr: ns); | 
|---|
| 286 | goto done; | 
|---|
| 287 | } else if (!ns) { | 
|---|
| 288 | break; | 
|---|
| 289 | } | 
|---|
| 290 |  | 
|---|
| 291 | err = __ioam6_genl_dumpns_element(ns, | 
|---|
| 292 | NETLINK_CB(cb->skb).portid, | 
|---|
| 293 | seq: cb->nlh->nlmsg_seq, | 
|---|
| 294 | NLM_F_MULTI, | 
|---|
| 295 | skb, | 
|---|
| 296 | cmd: IOAM6_CMD_DUMP_NAMESPACES); | 
|---|
| 297 | if (err) | 
|---|
| 298 | goto done; | 
|---|
| 299 | } | 
|---|
| 300 |  | 
|---|
| 301 | err = skb->len; | 
|---|
| 302 |  | 
|---|
| 303 | done: | 
|---|
| 304 | rhashtable_walk_stop(iter); | 
|---|
| 305 | return err; | 
|---|
| 306 | } | 
|---|
| 307 |  | 
|---|
| 308 | static int ioam6_genl_addsc(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 309 | { | 
|---|
| 310 | struct ioam6_pernet_data *nsdata; | 
|---|
| 311 | int len, len_aligned, err; | 
|---|
| 312 | struct ioam6_schema *sc; | 
|---|
| 313 | u32 id; | 
|---|
| 314 |  | 
|---|
| 315 | if (!info->attrs[IOAM6_ATTR_SC_ID] || !info->attrs[IOAM6_ATTR_SC_DATA]) | 
|---|
| 316 | return -EINVAL; | 
|---|
| 317 |  | 
|---|
| 318 | id = nla_get_u32(nla: info->attrs[IOAM6_ATTR_SC_ID]); | 
|---|
| 319 | nsdata = ioam6_pernet(net: genl_info_net(info)); | 
|---|
| 320 |  | 
|---|
| 321 | mutex_lock(lock: &nsdata->lock); | 
|---|
| 322 |  | 
|---|
| 323 | sc = rhashtable_lookup_fast(ht: &nsdata->schemas, key: &id, params: rht_sc_params); | 
|---|
| 324 | if (sc) { | 
|---|
| 325 | err = -EEXIST; | 
|---|
| 326 | goto out_unlock; | 
|---|
| 327 | } | 
|---|
| 328 |  | 
|---|
| 329 | len = nla_len(nla: info->attrs[IOAM6_ATTR_SC_DATA]); | 
|---|
| 330 | len_aligned = ALIGN(len, 4); | 
|---|
| 331 |  | 
|---|
| 332 | sc = kzalloc(sizeof(*sc) + len_aligned, GFP_KERNEL); | 
|---|
| 333 | if (!sc) { | 
|---|
| 334 | err = -ENOMEM; | 
|---|
| 335 | goto out_unlock; | 
|---|
| 336 | } | 
|---|
| 337 |  | 
|---|
| 338 | sc->id = id; | 
|---|
| 339 | sc->len = len_aligned; | 
|---|
| 340 | sc->hdr = cpu_to_be32(sc->id | ((u8)(sc->len / 4) << 24)); | 
|---|
| 341 | nla_memcpy(dest: sc->data, src: info->attrs[IOAM6_ATTR_SC_DATA], count: len); | 
|---|
| 342 |  | 
|---|
| 343 | err = rhashtable_lookup_insert_fast(ht: &nsdata->schemas, obj: &sc->head, | 
|---|
| 344 | params: rht_sc_params); | 
|---|
| 345 | if (err) | 
|---|
| 346 | goto free_sc; | 
|---|
| 347 |  | 
|---|
| 348 | out_unlock: | 
|---|
| 349 | mutex_unlock(lock: &nsdata->lock); | 
|---|
| 350 | return err; | 
|---|
| 351 | free_sc: | 
|---|
| 352 | kfree(objp: sc); | 
|---|
| 353 | goto out_unlock; | 
|---|
| 354 | } | 
|---|
| 355 |  | 
|---|
| 356 | static int ioam6_genl_delsc(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 357 | { | 
|---|
| 358 | struct ioam6_pernet_data *nsdata; | 
|---|
| 359 | struct ioam6_namespace *ns; | 
|---|
| 360 | struct ioam6_schema *sc; | 
|---|
| 361 | int err; | 
|---|
| 362 | u32 id; | 
|---|
| 363 |  | 
|---|
| 364 | if (!info->attrs[IOAM6_ATTR_SC_ID]) | 
|---|
| 365 | return -EINVAL; | 
|---|
| 366 |  | 
|---|
| 367 | id = nla_get_u32(nla: info->attrs[IOAM6_ATTR_SC_ID]); | 
|---|
| 368 | nsdata = ioam6_pernet(net: genl_info_net(info)); | 
|---|
| 369 |  | 
|---|
| 370 | mutex_lock(lock: &nsdata->lock); | 
|---|
| 371 |  | 
|---|
| 372 | sc = rhashtable_lookup_fast(ht: &nsdata->schemas, key: &id, params: rht_sc_params); | 
|---|
| 373 | if (!sc) { | 
|---|
| 374 | err = -ENOENT; | 
|---|
| 375 | goto out_unlock; | 
|---|
| 376 | } | 
|---|
| 377 |  | 
|---|
| 378 | ns = rcu_dereference_protected(sc->ns, lockdep_is_held(&nsdata->lock)); | 
|---|
| 379 |  | 
|---|
| 380 | err = rhashtable_remove_fast(ht: &nsdata->schemas, obj: &sc->head, | 
|---|
| 381 | params: rht_sc_params); | 
|---|
| 382 | if (err) | 
|---|
| 383 | goto out_unlock; | 
|---|
| 384 |  | 
|---|
| 385 | if (ns) | 
|---|
| 386 | rcu_assign_pointer(ns->schema, NULL); | 
|---|
| 387 |  | 
|---|
| 388 | ioam6_sc_release(sc); | 
|---|
| 389 |  | 
|---|
| 390 | out_unlock: | 
|---|
| 391 | mutex_unlock(lock: &nsdata->lock); | 
|---|
| 392 | return err; | 
|---|
| 393 | } | 
|---|
| 394 |  | 
|---|
| 395 | static int __ioam6_genl_dumpsc_element(struct ioam6_schema *sc, | 
|---|
| 396 | u32 portid, u32 seq, u32 flags, | 
|---|
| 397 | struct sk_buff *skb, u8 cmd) | 
|---|
| 398 | { | 
|---|
| 399 | struct ioam6_namespace *ns; | 
|---|
| 400 | void *hdr; | 
|---|
| 401 |  | 
|---|
| 402 | hdr = genlmsg_put(skb, portid, seq, family: &ioam6_genl_family, flags, cmd); | 
|---|
| 403 | if (!hdr) | 
|---|
| 404 | return -ENOMEM; | 
|---|
| 405 |  | 
|---|
| 406 | if (nla_put_u32(skb, attrtype: IOAM6_ATTR_SC_ID, value: sc->id) || | 
|---|
| 407 | nla_put(skb, attrtype: IOAM6_ATTR_SC_DATA, attrlen: sc->len, data: sc->data)) | 
|---|
| 408 | goto nla_put_failure; | 
|---|
| 409 |  | 
|---|
| 410 | rcu_read_lock(); | 
|---|
| 411 |  | 
|---|
| 412 | ns = rcu_dereference(sc->ns); | 
|---|
| 413 | if (ns && nla_put_u16(skb, attrtype: IOAM6_ATTR_NS_ID, be16_to_cpu(ns->id))) { | 
|---|
| 414 | rcu_read_unlock(); | 
|---|
| 415 | goto nla_put_failure; | 
|---|
| 416 | } | 
|---|
| 417 |  | 
|---|
| 418 | rcu_read_unlock(); | 
|---|
| 419 |  | 
|---|
| 420 | genlmsg_end(skb, hdr); | 
|---|
| 421 | return 0; | 
|---|
| 422 |  | 
|---|
| 423 | nla_put_failure: | 
|---|
| 424 | genlmsg_cancel(skb, hdr); | 
|---|
| 425 | return -EMSGSIZE; | 
|---|
| 426 | } | 
|---|
| 427 |  | 
|---|
| 428 | static int ioam6_genl_dumpsc_start(struct netlink_callback *cb) | 
|---|
| 429 | { | 
|---|
| 430 | struct ioam6_pernet_data *nsdata = ioam6_pernet(net: sock_net(sk: cb->skb->sk)); | 
|---|
| 431 | struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0]; | 
|---|
| 432 |  | 
|---|
| 433 | if (!iter) { | 
|---|
| 434 | iter = kmalloc(sizeof(*iter), GFP_KERNEL); | 
|---|
| 435 | if (!iter) | 
|---|
| 436 | return -ENOMEM; | 
|---|
| 437 |  | 
|---|
| 438 | cb->args[0] = (long)iter; | 
|---|
| 439 | } | 
|---|
| 440 |  | 
|---|
| 441 | rhashtable_walk_enter(ht: &nsdata->schemas, iter); | 
|---|
| 442 |  | 
|---|
| 443 | return 0; | 
|---|
| 444 | } | 
|---|
| 445 |  | 
|---|
| 446 | static int ioam6_genl_dumpsc_done(struct netlink_callback *cb) | 
|---|
| 447 | { | 
|---|
| 448 | struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0]; | 
|---|
| 449 |  | 
|---|
| 450 | rhashtable_walk_exit(iter); | 
|---|
| 451 | kfree(objp: iter); | 
|---|
| 452 |  | 
|---|
| 453 | return 0; | 
|---|
| 454 | } | 
|---|
| 455 |  | 
|---|
| 456 | static int ioam6_genl_dumpsc(struct sk_buff *skb, struct netlink_callback *cb) | 
|---|
| 457 | { | 
|---|
| 458 | struct rhashtable_iter *iter; | 
|---|
| 459 | struct ioam6_schema *sc; | 
|---|
| 460 | int err; | 
|---|
| 461 |  | 
|---|
| 462 | iter = (struct rhashtable_iter *)cb->args[0]; | 
|---|
| 463 | rhashtable_walk_start(iter); | 
|---|
| 464 |  | 
|---|
| 465 | for (;;) { | 
|---|
| 466 | sc = rhashtable_walk_next(iter); | 
|---|
| 467 |  | 
|---|
| 468 | if (IS_ERR(ptr: sc)) { | 
|---|
| 469 | if (PTR_ERR(ptr: sc) == -EAGAIN) | 
|---|
| 470 | continue; | 
|---|
| 471 | err = PTR_ERR(ptr: sc); | 
|---|
| 472 | goto done; | 
|---|
| 473 | } else if (!sc) { | 
|---|
| 474 | break; | 
|---|
| 475 | } | 
|---|
| 476 |  | 
|---|
| 477 | err = __ioam6_genl_dumpsc_element(sc, | 
|---|
| 478 | NETLINK_CB(cb->skb).portid, | 
|---|
| 479 | seq: cb->nlh->nlmsg_seq, | 
|---|
| 480 | NLM_F_MULTI, | 
|---|
| 481 | skb, | 
|---|
| 482 | cmd: IOAM6_CMD_DUMP_SCHEMAS); | 
|---|
| 483 | if (err) | 
|---|
| 484 | goto done; | 
|---|
| 485 | } | 
|---|
| 486 |  | 
|---|
| 487 | err = skb->len; | 
|---|
| 488 |  | 
|---|
| 489 | done: | 
|---|
| 490 | rhashtable_walk_stop(iter); | 
|---|
| 491 | return err; | 
|---|
| 492 | } | 
|---|
| 493 |  | 
|---|
| 494 | static int ioam6_genl_ns_set_schema(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 495 | { | 
|---|
| 496 | struct ioam6_namespace *ns, *ns_ref; | 
|---|
| 497 | struct ioam6_schema *sc, *sc_ref; | 
|---|
| 498 | struct ioam6_pernet_data *nsdata; | 
|---|
| 499 | __be16 ns_id; | 
|---|
| 500 | u32 sc_id; | 
|---|
| 501 | int err; | 
|---|
| 502 |  | 
|---|
| 503 | if (!info->attrs[IOAM6_ATTR_NS_ID] || | 
|---|
| 504 | (!info->attrs[IOAM6_ATTR_SC_ID] && | 
|---|
| 505 | !info->attrs[IOAM6_ATTR_SC_NONE])) | 
|---|
| 506 | return -EINVAL; | 
|---|
| 507 |  | 
|---|
| 508 | ns_id = cpu_to_be16(nla_get_u16(info->attrs[IOAM6_ATTR_NS_ID])); | 
|---|
| 509 | nsdata = ioam6_pernet(net: genl_info_net(info)); | 
|---|
| 510 |  | 
|---|
| 511 | mutex_lock(lock: &nsdata->lock); | 
|---|
| 512 |  | 
|---|
| 513 | ns = rhashtable_lookup_fast(ht: &nsdata->namespaces, key: &ns_id, params: rht_ns_params); | 
|---|
| 514 | if (!ns) { | 
|---|
| 515 | err = -ENOENT; | 
|---|
| 516 | goto out_unlock; | 
|---|
| 517 | } | 
|---|
| 518 |  | 
|---|
| 519 | if (info->attrs[IOAM6_ATTR_SC_NONE]) { | 
|---|
| 520 | sc = NULL; | 
|---|
| 521 | } else { | 
|---|
| 522 | sc_id = nla_get_u32(nla: info->attrs[IOAM6_ATTR_SC_ID]); | 
|---|
| 523 | sc = rhashtable_lookup_fast(ht: &nsdata->schemas, key: &sc_id, | 
|---|
| 524 | params: rht_sc_params); | 
|---|
| 525 | if (!sc) { | 
|---|
| 526 | err = -ENOENT; | 
|---|
| 527 | goto out_unlock; | 
|---|
| 528 | } | 
|---|
| 529 | } | 
|---|
| 530 |  | 
|---|
| 531 | sc_ref = rcu_dereference_protected(ns->schema, | 
|---|
| 532 | lockdep_is_held(&nsdata->lock)); | 
|---|
| 533 | if (sc_ref) | 
|---|
| 534 | rcu_assign_pointer(sc_ref->ns, NULL); | 
|---|
| 535 | rcu_assign_pointer(ns->schema, sc); | 
|---|
| 536 |  | 
|---|
| 537 | if (sc) { | 
|---|
| 538 | ns_ref = rcu_dereference_protected(sc->ns, | 
|---|
| 539 | lockdep_is_held(&nsdata->lock)); | 
|---|
| 540 | if (ns_ref) | 
|---|
| 541 | rcu_assign_pointer(ns_ref->schema, NULL); | 
|---|
| 542 | rcu_assign_pointer(sc->ns, ns); | 
|---|
| 543 | } | 
|---|
| 544 |  | 
|---|
| 545 | err = 0; | 
|---|
| 546 |  | 
|---|
| 547 | out_unlock: | 
|---|
| 548 | mutex_unlock(lock: &nsdata->lock); | 
|---|
| 549 | return err; | 
|---|
| 550 | } | 
|---|
| 551 |  | 
|---|
| 552 | static const struct genl_ops ioam6_genl_ops[] = { | 
|---|
| 553 | { | 
|---|
| 554 | .cmd	= IOAM6_CMD_ADD_NAMESPACE, | 
|---|
| 555 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 556 | .doit	= ioam6_genl_addns, | 
|---|
| 557 | .flags	= GENL_ADMIN_PERM, | 
|---|
| 558 | .policy	= ioam6_genl_policy_addns, | 
|---|
| 559 | .maxattr = ARRAY_SIZE(ioam6_genl_policy_addns) - 1, | 
|---|
| 560 | }, | 
|---|
| 561 | { | 
|---|
| 562 | .cmd	= IOAM6_CMD_DEL_NAMESPACE, | 
|---|
| 563 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 564 | .doit	= ioam6_genl_delns, | 
|---|
| 565 | .flags	= GENL_ADMIN_PERM, | 
|---|
| 566 | .policy	= ioam6_genl_policy_delns, | 
|---|
| 567 | .maxattr = ARRAY_SIZE(ioam6_genl_policy_delns) - 1, | 
|---|
| 568 | }, | 
|---|
| 569 | { | 
|---|
| 570 | .cmd	= IOAM6_CMD_DUMP_NAMESPACES, | 
|---|
| 571 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 572 | .start	= ioam6_genl_dumpns_start, | 
|---|
| 573 | .dumpit	= ioam6_genl_dumpns, | 
|---|
| 574 | .done	= ioam6_genl_dumpns_done, | 
|---|
| 575 | .flags	= GENL_ADMIN_PERM, | 
|---|
| 576 | }, | 
|---|
| 577 | { | 
|---|
| 578 | .cmd	= IOAM6_CMD_ADD_SCHEMA, | 
|---|
| 579 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 580 | .doit	= ioam6_genl_addsc, | 
|---|
| 581 | .flags	= GENL_ADMIN_PERM, | 
|---|
| 582 | .policy	= ioam6_genl_policy_addsc, | 
|---|
| 583 | .maxattr = ARRAY_SIZE(ioam6_genl_policy_addsc) - 1, | 
|---|
| 584 | }, | 
|---|
| 585 | { | 
|---|
| 586 | .cmd	= IOAM6_CMD_DEL_SCHEMA, | 
|---|
| 587 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 588 | .doit	= ioam6_genl_delsc, | 
|---|
| 589 | .flags	= GENL_ADMIN_PERM, | 
|---|
| 590 | .policy	= ioam6_genl_policy_delsc, | 
|---|
| 591 | .maxattr = ARRAY_SIZE(ioam6_genl_policy_delsc) - 1, | 
|---|
| 592 | }, | 
|---|
| 593 | { | 
|---|
| 594 | .cmd	= IOAM6_CMD_DUMP_SCHEMAS, | 
|---|
| 595 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 596 | .start	= ioam6_genl_dumpsc_start, | 
|---|
| 597 | .dumpit	= ioam6_genl_dumpsc, | 
|---|
| 598 | .done	= ioam6_genl_dumpsc_done, | 
|---|
| 599 | .flags	= GENL_ADMIN_PERM, | 
|---|
| 600 | }, | 
|---|
| 601 | { | 
|---|
| 602 | .cmd	= IOAM6_CMD_NS_SET_SCHEMA, | 
|---|
| 603 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 604 | .doit	= ioam6_genl_ns_set_schema, | 
|---|
| 605 | .flags	= GENL_ADMIN_PERM, | 
|---|
| 606 | .policy	= ioam6_genl_policy_ns_sc, | 
|---|
| 607 | .maxattr = ARRAY_SIZE(ioam6_genl_policy_ns_sc) - 1, | 
|---|
| 608 | }, | 
|---|
| 609 | }; | 
|---|
| 610 |  | 
|---|
| 611 | #define IOAM6_GENL_EV_GRP_OFFSET 0 | 
|---|
| 612 |  | 
|---|
| 613 | static const struct genl_multicast_group ioam6_mcgrps[] = { | 
|---|
| 614 | [IOAM6_GENL_EV_GRP_OFFSET] = { .name = IOAM6_GENL_EV_GRP_NAME, | 
|---|
| 615 | .flags = GENL_MCAST_CAP_NET_ADMIN }, | 
|---|
| 616 | }; | 
|---|
| 617 |  | 
|---|
| 618 | static int ioam6_event_put_trace(struct sk_buff *skb, | 
|---|
| 619 | struct ioam6_trace_hdr *trace, | 
|---|
| 620 | unsigned int len) | 
|---|
| 621 | { | 
|---|
| 622 | if (nla_put_u16(skb, attrtype: IOAM6_EVENT_ATTR_TRACE_NAMESPACE, | 
|---|
| 623 | be16_to_cpu(trace->namespace_id)) || | 
|---|
| 624 | nla_put_u8(skb, attrtype: IOAM6_EVENT_ATTR_TRACE_NODELEN, value: trace->nodelen) || | 
|---|
| 625 | nla_put_u32(skb, attrtype: IOAM6_EVENT_ATTR_TRACE_TYPE, | 
|---|
| 626 | be32_to_cpu(trace->type_be32)) || | 
|---|
| 627 | nla_put(skb, attrtype: IOAM6_EVENT_ATTR_TRACE_DATA, | 
|---|
| 628 | attrlen: len - sizeof(struct ioam6_trace_hdr) - trace->remlen * 4, | 
|---|
| 629 | data: trace->data + trace->remlen * 4)) | 
|---|
| 630 | return 1; | 
|---|
| 631 |  | 
|---|
| 632 | return 0; | 
|---|
| 633 | } | 
|---|
| 634 |  | 
|---|
| 635 | void ioam6_event(enum ioam6_event_type type, struct net *net, gfp_t gfp, | 
|---|
| 636 | void *opt, unsigned int opt_len) | 
|---|
| 637 | { | 
|---|
| 638 | struct nlmsghdr *nlh; | 
|---|
| 639 | struct sk_buff *skb; | 
|---|
| 640 |  | 
|---|
| 641 | if (!genl_has_listeners(family: &ioam6_genl_family, net, | 
|---|
| 642 | IOAM6_GENL_EV_GRP_OFFSET)) | 
|---|
| 643 | return; | 
|---|
| 644 |  | 
|---|
| 645 | skb = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); | 
|---|
| 646 | if (!skb) | 
|---|
| 647 | return; | 
|---|
| 648 |  | 
|---|
| 649 | nlh = genlmsg_put(skb, portid: 0, seq: 0, family: &ioam6_genl_family, flags: 0, cmd: type); | 
|---|
| 650 | if (!nlh) | 
|---|
| 651 | goto nla_put_failure; | 
|---|
| 652 |  | 
|---|
| 653 | switch (type) { | 
|---|
| 654 | case IOAM6_EVENT_UNSPEC: | 
|---|
| 655 | WARN_ON_ONCE(1); | 
|---|
| 656 | break; | 
|---|
| 657 | case IOAM6_EVENT_TRACE: | 
|---|
| 658 | if (ioam6_event_put_trace(skb, trace: (struct ioam6_trace_hdr *)opt, | 
|---|
| 659 | len: opt_len)) | 
|---|
| 660 | goto nla_put_failure; | 
|---|
| 661 | break; | 
|---|
| 662 | } | 
|---|
| 663 |  | 
|---|
| 664 | genlmsg_end(skb, hdr: nlh); | 
|---|
| 665 | genlmsg_multicast_netns(family: &ioam6_genl_family, net, skb, portid: 0, | 
|---|
| 666 | IOAM6_GENL_EV_GRP_OFFSET, flags: gfp); | 
|---|
| 667 | return; | 
|---|
| 668 |  | 
|---|
| 669 | nla_put_failure: | 
|---|
| 670 | nlmsg_free(skb); | 
|---|
| 671 | } | 
|---|
| 672 |  | 
|---|
| 673 | static struct genl_family ioam6_genl_family __ro_after_init = { | 
|---|
| 674 | .name		= IOAM6_GENL_NAME, | 
|---|
| 675 | .version	= IOAM6_GENL_VERSION, | 
|---|
| 676 | .netnsok	= true, | 
|---|
| 677 | .parallel_ops	= true, | 
|---|
| 678 | .ops		= ioam6_genl_ops, | 
|---|
| 679 | .n_ops		= ARRAY_SIZE(ioam6_genl_ops), | 
|---|
| 680 | .resv_start_op	= IOAM6_CMD_NS_SET_SCHEMA + 1, | 
|---|
| 681 | .mcgrps		= ioam6_mcgrps, | 
|---|
| 682 | .n_mcgrps	= ARRAY_SIZE(ioam6_mcgrps), | 
|---|
| 683 | .module		= THIS_MODULE, | 
|---|
| 684 | }; | 
|---|
| 685 |  | 
|---|
| 686 | struct ioam6_namespace *ioam6_namespace(struct net *net, __be16 id) | 
|---|
| 687 | { | 
|---|
| 688 | struct ioam6_pernet_data *nsdata = ioam6_pernet(net); | 
|---|
| 689 |  | 
|---|
| 690 | return rhashtable_lookup_fast(ht: &nsdata->namespaces, key: &id, params: rht_ns_params); | 
|---|
| 691 | } | 
|---|
| 692 |  | 
|---|
| 693 | static void __ioam6_fill_trace_data(struct sk_buff *skb, | 
|---|
| 694 | struct ioam6_namespace *ns, | 
|---|
| 695 | struct ioam6_trace_hdr *trace, | 
|---|
| 696 | struct ioam6_schema *sc, | 
|---|
| 697 | u8 sclen, bool is_input) | 
|---|
| 698 | { | 
|---|
| 699 | struct net_device *dev = skb_dst_dev(skb); | 
|---|
| 700 | struct timespec64 ts; | 
|---|
| 701 | ktime_t tstamp; | 
|---|
| 702 | u64 raw64; | 
|---|
| 703 | u32 raw32; | 
|---|
| 704 | u16 raw16; | 
|---|
| 705 | u8 *data; | 
|---|
| 706 | u8 byte; | 
|---|
| 707 |  | 
|---|
| 708 | data = trace->data + trace->remlen * 4 - trace->nodelen * 4 - sclen * 4; | 
|---|
| 709 |  | 
|---|
| 710 | /* hop_lim and node_id */ | 
|---|
| 711 | if (trace->type.bit0) { | 
|---|
| 712 | byte = ipv6_hdr(skb)->hop_limit; | 
|---|
| 713 | if (is_input) | 
|---|
| 714 | byte--; | 
|---|
| 715 |  | 
|---|
| 716 | raw32 = dev_net(dev)->ipv6.sysctl.ioam6_id; | 
|---|
| 717 |  | 
|---|
| 718 | *(__be32 *)data = cpu_to_be32((byte << 24) | raw32); | 
|---|
| 719 | data += sizeof(__be32); | 
|---|
| 720 | } | 
|---|
| 721 |  | 
|---|
| 722 | /* ingress_if_id and egress_if_id */ | 
|---|
| 723 | if (trace->type.bit1) { | 
|---|
| 724 | if (!skb->dev) | 
|---|
| 725 | raw16 = IOAM6_U16_UNAVAILABLE; | 
|---|
| 726 | else | 
|---|
| 727 | raw16 = (__force u16)READ_ONCE(__in6_dev_get(skb->dev)->cnf.ioam6_id); | 
|---|
| 728 |  | 
|---|
| 729 | *(__be16 *)data = cpu_to_be16(raw16); | 
|---|
| 730 | data += sizeof(__be16); | 
|---|
| 731 |  | 
|---|
| 732 | if (dev->flags & IFF_LOOPBACK) | 
|---|
| 733 | raw16 = IOAM6_U16_UNAVAILABLE; | 
|---|
| 734 | else | 
|---|
| 735 | raw16 = (__force u16)READ_ONCE(__in6_dev_get(dev)->cnf.ioam6_id); | 
|---|
| 736 |  | 
|---|
| 737 | *(__be16 *)data = cpu_to_be16(raw16); | 
|---|
| 738 | data += sizeof(__be16); | 
|---|
| 739 | } | 
|---|
| 740 |  | 
|---|
| 741 | /* timestamp seconds */ | 
|---|
| 742 | if (trace->type.bit2) { | 
|---|
| 743 | if (!skb->dev) { | 
|---|
| 744 | *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE); | 
|---|
| 745 | } else { | 
|---|
| 746 | tstamp = skb_tstamp_cond(skb, cond: true); | 
|---|
| 747 | ts = ktime_to_timespec64(tstamp); | 
|---|
| 748 |  | 
|---|
| 749 | *(__be32 *)data = cpu_to_be32((u32)ts.tv_sec); | 
|---|
| 750 | } | 
|---|
| 751 | data += sizeof(__be32); | 
|---|
| 752 | } | 
|---|
| 753 |  | 
|---|
| 754 | /* timestamp subseconds */ | 
|---|
| 755 | if (trace->type.bit3) { | 
|---|
| 756 | if (!skb->dev) { | 
|---|
| 757 | *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE); | 
|---|
| 758 | } else { | 
|---|
| 759 | if (!trace->type.bit2) { | 
|---|
| 760 | tstamp = skb_tstamp_cond(skb, cond: true); | 
|---|
| 761 | ts = ktime_to_timespec64(tstamp); | 
|---|
| 762 | } | 
|---|
| 763 |  | 
|---|
| 764 | *(__be32 *)data = cpu_to_be32((u32)(ts.tv_nsec / NSEC_PER_USEC)); | 
|---|
| 765 | } | 
|---|
| 766 | data += sizeof(__be32); | 
|---|
| 767 | } | 
|---|
| 768 |  | 
|---|
| 769 | /* transit delay */ | 
|---|
| 770 | if (trace->type.bit4) { | 
|---|
| 771 | *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE); | 
|---|
| 772 | data += sizeof(__be32); | 
|---|
| 773 | } | 
|---|
| 774 |  | 
|---|
| 775 | /* namespace data */ | 
|---|
| 776 | if (trace->type.bit5) { | 
|---|
| 777 | *(__be32 *)data = ns->data; | 
|---|
| 778 | data += sizeof(__be32); | 
|---|
| 779 | } | 
|---|
| 780 |  | 
|---|
| 781 | /* queue depth */ | 
|---|
| 782 | if (trace->type.bit6) { | 
|---|
| 783 | struct netdev_queue *queue; | 
|---|
| 784 | struct Qdisc *qdisc; | 
|---|
| 785 | __u32 qlen, backlog; | 
|---|
| 786 |  | 
|---|
| 787 | if (dev->flags & IFF_LOOPBACK) { | 
|---|
| 788 | *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE); | 
|---|
| 789 | } else { | 
|---|
| 790 | queue = skb_get_tx_queue(dev, skb); | 
|---|
| 791 | qdisc = rcu_dereference(queue->qdisc); | 
|---|
| 792 | qdisc_qstats_qlen_backlog(sch: qdisc, qlen: &qlen, backlog: &backlog); | 
|---|
| 793 |  | 
|---|
| 794 | *(__be32 *)data = cpu_to_be32(backlog); | 
|---|
| 795 | } | 
|---|
| 796 | data += sizeof(__be32); | 
|---|
| 797 | } | 
|---|
| 798 |  | 
|---|
| 799 | /* checksum complement */ | 
|---|
| 800 | if (trace->type.bit7) { | 
|---|
| 801 | *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE); | 
|---|
| 802 | data += sizeof(__be32); | 
|---|
| 803 | } | 
|---|
| 804 |  | 
|---|
| 805 | /* hop_lim and node_id (wide) */ | 
|---|
| 806 | if (trace->type.bit8) { | 
|---|
| 807 | byte = ipv6_hdr(skb)->hop_limit; | 
|---|
| 808 | if (is_input) | 
|---|
| 809 | byte--; | 
|---|
| 810 |  | 
|---|
| 811 | raw64 = dev_net(dev)->ipv6.sysctl.ioam6_id_wide; | 
|---|
| 812 |  | 
|---|
| 813 | *(__be64 *)data = cpu_to_be64(((u64)byte << 56) | raw64); | 
|---|
| 814 | data += sizeof(__be64); | 
|---|
| 815 | } | 
|---|
| 816 |  | 
|---|
| 817 | /* ingress_if_id and egress_if_id (wide) */ | 
|---|
| 818 | if (trace->type.bit9) { | 
|---|
| 819 | if (!skb->dev) | 
|---|
| 820 | raw32 = IOAM6_U32_UNAVAILABLE; | 
|---|
| 821 | else | 
|---|
| 822 | raw32 = READ_ONCE(__in6_dev_get(skb->dev)->cnf.ioam6_id_wide); | 
|---|
| 823 |  | 
|---|
| 824 | *(__be32 *)data = cpu_to_be32(raw32); | 
|---|
| 825 | data += sizeof(__be32); | 
|---|
| 826 |  | 
|---|
| 827 | if (dev->flags & IFF_LOOPBACK) | 
|---|
| 828 | raw32 = IOAM6_U32_UNAVAILABLE; | 
|---|
| 829 | else | 
|---|
| 830 | raw32 = READ_ONCE(__in6_dev_get(dev)->cnf.ioam6_id_wide); | 
|---|
| 831 |  | 
|---|
| 832 | *(__be32 *)data = cpu_to_be32(raw32); | 
|---|
| 833 | data += sizeof(__be32); | 
|---|
| 834 | } | 
|---|
| 835 |  | 
|---|
| 836 | /* namespace data (wide) */ | 
|---|
| 837 | if (trace->type.bit10) { | 
|---|
| 838 | *(__be64 *)data = ns->data_wide; | 
|---|
| 839 | data += sizeof(__be64); | 
|---|
| 840 | } | 
|---|
| 841 |  | 
|---|
| 842 | /* buffer occupancy */ | 
|---|
| 843 | if (trace->type.bit11) { | 
|---|
| 844 | *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE); | 
|---|
| 845 | data += sizeof(__be32); | 
|---|
| 846 | } | 
|---|
| 847 |  | 
|---|
| 848 | /* bit12 undefined: filled with empty value */ | 
|---|
| 849 | if (trace->type.bit12) { | 
|---|
| 850 | *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE); | 
|---|
| 851 | data += sizeof(__be32); | 
|---|
| 852 | } | 
|---|
| 853 |  | 
|---|
| 854 | /* bit13 undefined: filled with empty value */ | 
|---|
| 855 | if (trace->type.bit13) { | 
|---|
| 856 | *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE); | 
|---|
| 857 | data += sizeof(__be32); | 
|---|
| 858 | } | 
|---|
| 859 |  | 
|---|
| 860 | /* bit14 undefined: filled with empty value */ | 
|---|
| 861 | if (trace->type.bit14) { | 
|---|
| 862 | *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE); | 
|---|
| 863 | data += sizeof(__be32); | 
|---|
| 864 | } | 
|---|
| 865 |  | 
|---|
| 866 | /* bit15 undefined: filled with empty value */ | 
|---|
| 867 | if (trace->type.bit15) { | 
|---|
| 868 | *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE); | 
|---|
| 869 | data += sizeof(__be32); | 
|---|
| 870 | } | 
|---|
| 871 |  | 
|---|
| 872 | /* bit16 undefined: filled with empty value */ | 
|---|
| 873 | if (trace->type.bit16) { | 
|---|
| 874 | *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE); | 
|---|
| 875 | data += sizeof(__be32); | 
|---|
| 876 | } | 
|---|
| 877 |  | 
|---|
| 878 | /* bit17 undefined: filled with empty value */ | 
|---|
| 879 | if (trace->type.bit17) { | 
|---|
| 880 | *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE); | 
|---|
| 881 | data += sizeof(__be32); | 
|---|
| 882 | } | 
|---|
| 883 |  | 
|---|
| 884 | /* bit18 undefined: filled with empty value */ | 
|---|
| 885 | if (trace->type.bit18) { | 
|---|
| 886 | *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE); | 
|---|
| 887 | data += sizeof(__be32); | 
|---|
| 888 | } | 
|---|
| 889 |  | 
|---|
| 890 | /* bit19 undefined: filled with empty value */ | 
|---|
| 891 | if (trace->type.bit19) { | 
|---|
| 892 | *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE); | 
|---|
| 893 | data += sizeof(__be32); | 
|---|
| 894 | } | 
|---|
| 895 |  | 
|---|
| 896 | /* bit20 undefined: filled with empty value */ | 
|---|
| 897 | if (trace->type.bit20) { | 
|---|
| 898 | *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE); | 
|---|
| 899 | data += sizeof(__be32); | 
|---|
| 900 | } | 
|---|
| 901 |  | 
|---|
| 902 | /* bit21 undefined: filled with empty value */ | 
|---|
| 903 | if (trace->type.bit21) { | 
|---|
| 904 | *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE); | 
|---|
| 905 | data += sizeof(__be32); | 
|---|
| 906 | } | 
|---|
| 907 |  | 
|---|
| 908 | /* opaque state snapshot */ | 
|---|
| 909 | if (trace->type.bit22) { | 
|---|
| 910 | if (!sc) { | 
|---|
| 911 | *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE >> 8); | 
|---|
| 912 | } else { | 
|---|
| 913 | *(__be32 *)data = sc->hdr; | 
|---|
| 914 | data += sizeof(__be32); | 
|---|
| 915 |  | 
|---|
| 916 | memcpy(to: data, from: sc->data, len: sc->len); | 
|---|
| 917 | } | 
|---|
| 918 | } | 
|---|
| 919 | } | 
|---|
| 920 |  | 
|---|
| 921 | /* called with rcu_read_lock() */ | 
|---|
| 922 | void ioam6_fill_trace_data(struct sk_buff *skb, | 
|---|
| 923 | struct ioam6_namespace *ns, | 
|---|
| 924 | struct ioam6_trace_hdr *trace, | 
|---|
| 925 | bool is_input) | 
|---|
| 926 | { | 
|---|
| 927 | struct ioam6_schema *sc; | 
|---|
| 928 | u8 sclen = 0; | 
|---|
| 929 |  | 
|---|
| 930 | /* Skip if Overflow flag is set | 
|---|
| 931 | */ | 
|---|
| 932 | if (trace->overflow) | 
|---|
| 933 | return; | 
|---|
| 934 |  | 
|---|
| 935 | /* NodeLen does not include Opaque State Snapshot length. We need to | 
|---|
| 936 | * take it into account if the corresponding bit is set (bit 22) and | 
|---|
| 937 | * if the current IOAM namespace has an active schema attached to it | 
|---|
| 938 | */ | 
|---|
| 939 | sc = rcu_dereference(ns->schema); | 
|---|
| 940 | if (trace->type.bit22) { | 
|---|
| 941 | sclen = sizeof_field(struct ioam6_schema, hdr) / 4; | 
|---|
| 942 |  | 
|---|
| 943 | if (sc) | 
|---|
| 944 | sclen += sc->len / 4; | 
|---|
| 945 | } | 
|---|
| 946 |  | 
|---|
| 947 | /* If there is no space remaining, we set the Overflow flag and we | 
|---|
| 948 | * skip without filling the trace | 
|---|
| 949 | */ | 
|---|
| 950 | if (!trace->remlen || trace->remlen < trace->nodelen + sclen) { | 
|---|
| 951 | trace->overflow = 1; | 
|---|
| 952 | return; | 
|---|
| 953 | } | 
|---|
| 954 |  | 
|---|
| 955 | __ioam6_fill_trace_data(skb, ns, trace, sc, sclen, is_input); | 
|---|
| 956 | trace->remlen -= trace->nodelen + sclen; | 
|---|
| 957 | } | 
|---|
| 958 |  | 
|---|
| 959 | static int __net_init ioam6_net_init(struct net *net) | 
|---|
| 960 | { | 
|---|
| 961 | struct ioam6_pernet_data *nsdata; | 
|---|
| 962 | int err = -ENOMEM; | 
|---|
| 963 |  | 
|---|
| 964 | nsdata = kzalloc(sizeof(*nsdata), GFP_KERNEL); | 
|---|
| 965 | if (!nsdata) | 
|---|
| 966 | goto out; | 
|---|
| 967 |  | 
|---|
| 968 | mutex_init(&nsdata->lock); | 
|---|
| 969 | net->ipv6.ioam6_data = nsdata; | 
|---|
| 970 |  | 
|---|
| 971 | err = rhashtable_init(&nsdata->namespaces, &rht_ns_params); | 
|---|
| 972 | if (err) | 
|---|
| 973 | goto free_nsdata; | 
|---|
| 974 |  | 
|---|
| 975 | err = rhashtable_init(&nsdata->schemas, &rht_sc_params); | 
|---|
| 976 | if (err) | 
|---|
| 977 | goto free_rht_ns; | 
|---|
| 978 |  | 
|---|
| 979 | out: | 
|---|
| 980 | return err; | 
|---|
| 981 | free_rht_ns: | 
|---|
| 982 | rhashtable_destroy(ht: &nsdata->namespaces); | 
|---|
| 983 | free_nsdata: | 
|---|
| 984 | kfree(objp: nsdata); | 
|---|
| 985 | net->ipv6.ioam6_data = NULL; | 
|---|
| 986 | goto out; | 
|---|
| 987 | } | 
|---|
| 988 |  | 
|---|
| 989 | static void __net_exit ioam6_net_exit(struct net *net) | 
|---|
| 990 | { | 
|---|
| 991 | struct ioam6_pernet_data *nsdata = ioam6_pernet(net); | 
|---|
| 992 |  | 
|---|
| 993 | rhashtable_free_and_destroy(ht: &nsdata->namespaces, free_fn: ioam6_free_ns, NULL); | 
|---|
| 994 | rhashtable_free_and_destroy(ht: &nsdata->schemas, free_fn: ioam6_free_sc, NULL); | 
|---|
| 995 |  | 
|---|
| 996 | kfree(objp: nsdata); | 
|---|
| 997 | } | 
|---|
| 998 |  | 
|---|
| 999 | static struct pernet_operations ioam6_net_ops = { | 
|---|
| 1000 | .init = ioam6_net_init, | 
|---|
| 1001 | .exit = ioam6_net_exit, | 
|---|
| 1002 | }; | 
|---|
| 1003 |  | 
|---|
| 1004 | int __init ioam6_init(void) | 
|---|
| 1005 | { | 
|---|
| 1006 | int err = register_pernet_subsys(&ioam6_net_ops); | 
|---|
| 1007 | if (err) | 
|---|
| 1008 | goto out; | 
|---|
| 1009 |  | 
|---|
| 1010 | err = genl_register_family(family: &ioam6_genl_family); | 
|---|
| 1011 | if (err) | 
|---|
| 1012 | goto out_unregister_pernet_subsys; | 
|---|
| 1013 |  | 
|---|
| 1014 | #ifdef CONFIG_IPV6_IOAM6_LWTUNNEL | 
|---|
| 1015 | err = ioam6_iptunnel_init(); | 
|---|
| 1016 | if (err) | 
|---|
| 1017 | goto out_unregister_genl; | 
|---|
| 1018 | #endif | 
|---|
| 1019 |  | 
|---|
| 1020 | pr_info( "In-situ OAM (IOAM) with IPv6\n"); | 
|---|
| 1021 |  | 
|---|
| 1022 | out: | 
|---|
| 1023 | return err; | 
|---|
| 1024 | #ifdef CONFIG_IPV6_IOAM6_LWTUNNEL | 
|---|
| 1025 | out_unregister_genl: | 
|---|
| 1026 | genl_unregister_family(&ioam6_genl_family); | 
|---|
| 1027 | #endif | 
|---|
| 1028 | out_unregister_pernet_subsys: | 
|---|
| 1029 | unregister_pernet_subsys(&ioam6_net_ops); | 
|---|
| 1030 | goto out; | 
|---|
| 1031 | } | 
|---|
| 1032 |  | 
|---|
| 1033 | void ioam6_exit(void) | 
|---|
| 1034 | { | 
|---|
| 1035 | #ifdef CONFIG_IPV6_IOAM6_LWTUNNEL | 
|---|
| 1036 | ioam6_iptunnel_exit(); | 
|---|
| 1037 | #endif | 
|---|
| 1038 | genl_unregister_family(family: &ioam6_genl_family); | 
|---|
| 1039 | unregister_pernet_subsys(&ioam6_net_ops); | 
|---|
| 1040 | } | 
|---|
| 1041 |  | 
|---|