root/net/ipv6/seg6.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. seg6_validate_srh
  2. seg6_genl_sethmac
  3. seg6_genl_sethmac
  4. seg6_genl_set_tunsrc
  5. seg6_genl_get_tunsrc
  6. __seg6_hmac_fill_info
  7. __seg6_genl_dumphmac_element
  8. seg6_genl_dumphmac_start
  9. seg6_genl_dumphmac_done
  10. seg6_genl_dumphmac
  11. seg6_genl_dumphmac_start
  12. seg6_genl_dumphmac_done
  13. seg6_genl_dumphmac
  14. seg6_net_init
  15. seg6_net_exit
  16. seg6_init
  17. seg6_exit

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  *  SR-IPv6 implementation
   4  *
   5  *  Author:
   6  *  David Lebrun <david.lebrun@uclouvain.be>
   7  */
   8 
   9 #include <linux/errno.h>
  10 #include <linux/types.h>
  11 #include <linux/socket.h>
  12 #include <linux/net.h>
  13 #include <linux/in6.h>
  14 #include <linux/slab.h>
  15 #include <linux/rhashtable.h>
  16 
  17 #include <net/ipv6.h>
  18 #include <net/protocol.h>
  19 
  20 #include <net/seg6.h>
  21 #include <net/genetlink.h>
  22 #include <linux/seg6.h>
  23 #include <linux/seg6_genl.h>
  24 #ifdef CONFIG_IPV6_SEG6_HMAC
  25 #include <net/seg6_hmac.h>
  26 #endif
  27 
  28 bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len)
  29 {
  30         int trailing;
  31         unsigned int tlv_offset;
  32 
  33         if (srh->type != IPV6_SRCRT_TYPE_4)
  34                 return false;
  35 
  36         if (((srh->hdrlen + 1) << 3) != len)
  37                 return false;
  38 
  39         if (srh->segments_left > srh->first_segment)
  40                 return false;
  41 
  42         tlv_offset = sizeof(*srh) + ((srh->first_segment + 1) << 4);
  43 
  44         trailing = len - tlv_offset;
  45         if (trailing < 0)
  46                 return false;
  47 
  48         while (trailing) {
  49                 struct sr6_tlv *tlv;
  50                 unsigned int tlv_len;
  51 
  52                 if (trailing < sizeof(*tlv))
  53                         return false;
  54 
  55                 tlv = (struct sr6_tlv *)((unsigned char *)srh + tlv_offset);
  56                 tlv_len = sizeof(*tlv) + tlv->len;
  57 
  58                 trailing -= tlv_len;
  59                 if (trailing < 0)
  60                         return false;
  61 
  62                 tlv_offset += tlv_len;
  63         }
  64 
  65         return true;
  66 }
  67 
  68 static struct genl_family seg6_genl_family;
  69 
  70 static const struct nla_policy seg6_genl_policy[SEG6_ATTR_MAX + 1] = {
  71         [SEG6_ATTR_DST]                         = { .type = NLA_BINARY,
  72                 .len = sizeof(struct in6_addr) },
  73         [SEG6_ATTR_DSTLEN]                      = { .type = NLA_S32, },
  74         [SEG6_ATTR_HMACKEYID]           = { .type = NLA_U32, },
  75         [SEG6_ATTR_SECRET]                      = { .type = NLA_BINARY, },
  76         [SEG6_ATTR_SECRETLEN]           = { .type = NLA_U8, },
  77         [SEG6_ATTR_ALGID]                       = { .type = NLA_U8, },
  78         [SEG6_ATTR_HMACINFO]            = { .type = NLA_NESTED, },
  79 };
  80 
  81 #ifdef CONFIG_IPV6_SEG6_HMAC
  82 
  83 static int seg6_genl_sethmac(struct sk_buff *skb, struct genl_info *info)
  84 {
  85         struct net *net = genl_info_net(info);
  86         struct seg6_pernet_data *sdata;
  87         struct seg6_hmac_info *hinfo;
  88         u32 hmackeyid;
  89         char *secret;
  90         int err = 0;
  91         u8 algid;
  92         u8 slen;
  93 
  94         sdata = seg6_pernet(net);
  95 
  96         if (!info->attrs[SEG6_ATTR_HMACKEYID] ||
  97             !info->attrs[SEG6_ATTR_SECRETLEN] ||
  98             !info->attrs[SEG6_ATTR_ALGID])
  99                 return -EINVAL;
 100 
 101         hmackeyid = nla_get_u32(info->attrs[SEG6_ATTR_HMACKEYID]);
 102         slen = nla_get_u8(info->attrs[SEG6_ATTR_SECRETLEN]);
 103         algid = nla_get_u8(info->attrs[SEG6_ATTR_ALGID]);
 104 
 105         if (hmackeyid == 0)
 106                 return -EINVAL;
 107 
 108         if (slen > SEG6_HMAC_SECRET_LEN)
 109                 return -EINVAL;
 110 
 111         mutex_lock(&sdata->lock);
 112         hinfo = seg6_hmac_info_lookup(net, hmackeyid);
 113 
 114         if (!slen) {
 115                 if (!hinfo)
 116                         err = -ENOENT;
 117 
 118                 err = seg6_hmac_info_del(net, hmackeyid);
 119 
 120                 goto out_unlock;
 121         }
 122 
 123         if (!info->attrs[SEG6_ATTR_SECRET]) {
 124                 err = -EINVAL;
 125                 goto out_unlock;
 126         }
 127 
 128         if (hinfo) {
 129                 err = seg6_hmac_info_del(net, hmackeyid);
 130                 if (err)
 131                         goto out_unlock;
 132         }
 133 
 134         secret = (char *)nla_data(info->attrs[SEG6_ATTR_SECRET]);
 135 
 136         hinfo = kzalloc(sizeof(*hinfo), GFP_KERNEL);
 137         if (!hinfo) {
 138                 err = -ENOMEM;
 139                 goto out_unlock;
 140         }
 141 
 142         memcpy(hinfo->secret, secret, slen);
 143         hinfo->slen = slen;
 144         hinfo->alg_id = algid;
 145         hinfo->hmackeyid = hmackeyid;
 146 
 147         err = seg6_hmac_info_add(net, hmackeyid, hinfo);
 148         if (err)
 149                 kfree(hinfo);
 150 
 151 out_unlock:
 152         mutex_unlock(&sdata->lock);
 153         return err;
 154 }
 155 
 156 #else
 157 
 158 static int seg6_genl_sethmac(struct sk_buff *skb, struct genl_info *info)
 159 {
 160         return -ENOTSUPP;
 161 }
 162 
 163 #endif
 164 
 165 static int seg6_genl_set_tunsrc(struct sk_buff *skb, struct genl_info *info)
 166 {
 167         struct net *net = genl_info_net(info);
 168         struct in6_addr *val, *t_old, *t_new;
 169         struct seg6_pernet_data *sdata;
 170 
 171         sdata = seg6_pernet(net);
 172 
 173         if (!info->attrs[SEG6_ATTR_DST])
 174                 return -EINVAL;
 175 
 176         val = nla_data(info->attrs[SEG6_ATTR_DST]);
 177         t_new = kmemdup(val, sizeof(*val), GFP_KERNEL);
 178         if (!t_new)
 179                 return -ENOMEM;
 180 
 181         mutex_lock(&sdata->lock);
 182 
 183         t_old = sdata->tun_src;
 184         rcu_assign_pointer(sdata->tun_src, t_new);
 185 
 186         mutex_unlock(&sdata->lock);
 187 
 188         synchronize_net();
 189         kfree(t_old);
 190 
 191         return 0;
 192 }
 193 
 194 static int seg6_genl_get_tunsrc(struct sk_buff *skb, struct genl_info *info)
 195 {
 196         struct net *net = genl_info_net(info);
 197         struct in6_addr *tun_src;
 198         struct sk_buff *msg;
 199         void *hdr;
 200 
 201         msg = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 202         if (!msg)
 203                 return -ENOMEM;
 204 
 205         hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
 206                           &seg6_genl_family, 0, SEG6_CMD_GET_TUNSRC);
 207         if (!hdr)
 208                 goto free_msg;
 209 
 210         rcu_read_lock();
 211         tun_src = rcu_dereference(seg6_pernet(net)->tun_src);
 212 
 213         if (nla_put(msg, SEG6_ATTR_DST, sizeof(struct in6_addr), tun_src))
 214                 goto nla_put_failure;
 215 
 216         rcu_read_unlock();
 217 
 218         genlmsg_end(msg, hdr);
 219         return genlmsg_reply(msg, info);
 220 
 221 nla_put_failure:
 222         rcu_read_unlock();
 223 free_msg:
 224         nlmsg_free(msg);
 225         return -ENOMEM;
 226 }
 227 
 228 #ifdef CONFIG_IPV6_SEG6_HMAC
 229 
 230 static int __seg6_hmac_fill_info(struct seg6_hmac_info *hinfo,
 231                                  struct sk_buff *msg)
 232 {
 233         if (nla_put_u32(msg, SEG6_ATTR_HMACKEYID, hinfo->hmackeyid) ||
 234             nla_put_u8(msg, SEG6_ATTR_SECRETLEN, hinfo->slen) ||
 235             nla_put(msg, SEG6_ATTR_SECRET, hinfo->slen, hinfo->secret) ||
 236             nla_put_u8(msg, SEG6_ATTR_ALGID, hinfo->alg_id))
 237                 return -1;
 238 
 239         return 0;
 240 }
 241 
 242 static int __seg6_genl_dumphmac_element(struct seg6_hmac_info *hinfo,
 243                                         u32 portid, u32 seq, u32 flags,
 244                                         struct sk_buff *skb, u8 cmd)
 245 {
 246         void *hdr;
 247 
 248         hdr = genlmsg_put(skb, portid, seq, &seg6_genl_family, flags, cmd);
 249         if (!hdr)
 250                 return -ENOMEM;
 251 
 252         if (__seg6_hmac_fill_info(hinfo, skb) < 0)
 253                 goto nla_put_failure;
 254 
 255         genlmsg_end(skb, hdr);
 256         return 0;
 257 
 258 nla_put_failure:
 259         genlmsg_cancel(skb, hdr);
 260         return -EMSGSIZE;
 261 }
 262 
 263 static int seg6_genl_dumphmac_start(struct netlink_callback *cb)
 264 {
 265         struct net *net = sock_net(cb->skb->sk);
 266         struct seg6_pernet_data *sdata;
 267         struct rhashtable_iter *iter;
 268 
 269         sdata = seg6_pernet(net);
 270         iter = (struct rhashtable_iter *)cb->args[0];
 271 
 272         if (!iter) {
 273                 iter = kmalloc(sizeof(*iter), GFP_KERNEL);
 274                 if (!iter)
 275                         return -ENOMEM;
 276 
 277                 cb->args[0] = (long)iter;
 278         }
 279 
 280         rhashtable_walk_enter(&sdata->hmac_infos, iter);
 281 
 282         return 0;
 283 }
 284 
 285 static int seg6_genl_dumphmac_done(struct netlink_callback *cb)
 286 {
 287         struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
 288 
 289         rhashtable_walk_exit(iter);
 290 
 291         kfree(iter);
 292 
 293         return 0;
 294 }
 295 
 296 static int seg6_genl_dumphmac(struct sk_buff *skb, struct netlink_callback *cb)
 297 {
 298         struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
 299         struct seg6_hmac_info *hinfo;
 300         int ret;
 301 
 302         rhashtable_walk_start(iter);
 303 
 304         for (;;) {
 305                 hinfo = rhashtable_walk_next(iter);
 306 
 307                 if (IS_ERR(hinfo)) {
 308                         if (PTR_ERR(hinfo) == -EAGAIN)
 309                                 continue;
 310                         ret = PTR_ERR(hinfo);
 311                         goto done;
 312                 } else if (!hinfo) {
 313                         break;
 314                 }
 315 
 316                 ret = __seg6_genl_dumphmac_element(hinfo,
 317                                                    NETLINK_CB(cb->skb).portid,
 318                                                    cb->nlh->nlmsg_seq,
 319                                                    NLM_F_MULTI,
 320                                                    skb, SEG6_CMD_DUMPHMAC);
 321                 if (ret)
 322                         goto done;
 323         }
 324 
 325         ret = skb->len;
 326 
 327 done:
 328         rhashtable_walk_stop(iter);
 329         return ret;
 330 }
 331 
 332 #else
 333 
 334 static int seg6_genl_dumphmac_start(struct netlink_callback *cb)
 335 {
 336         return 0;
 337 }
 338 
 339 static int seg6_genl_dumphmac_done(struct netlink_callback *cb)
 340 {
 341         return 0;
 342 }
 343 
 344 static int seg6_genl_dumphmac(struct sk_buff *skb, struct netlink_callback *cb)
 345 {
 346         return -ENOTSUPP;
 347 }
 348 
 349 #endif
 350 
 351 static int __net_init seg6_net_init(struct net *net)
 352 {
 353         struct seg6_pernet_data *sdata;
 354 
 355         sdata = kzalloc(sizeof(*sdata), GFP_KERNEL);
 356         if (!sdata)
 357                 return -ENOMEM;
 358 
 359         mutex_init(&sdata->lock);
 360 
 361         sdata->tun_src = kzalloc(sizeof(*sdata->tun_src), GFP_KERNEL);
 362         if (!sdata->tun_src) {
 363                 kfree(sdata);
 364                 return -ENOMEM;
 365         }
 366 
 367         net->ipv6.seg6_data = sdata;
 368 
 369 #ifdef CONFIG_IPV6_SEG6_HMAC
 370         seg6_hmac_net_init(net);
 371 #endif
 372 
 373         return 0;
 374 }
 375 
 376 static void __net_exit seg6_net_exit(struct net *net)
 377 {
 378         struct seg6_pernet_data *sdata = seg6_pernet(net);
 379 
 380 #ifdef CONFIG_IPV6_SEG6_HMAC
 381         seg6_hmac_net_exit(net);
 382 #endif
 383 
 384         kfree(sdata->tun_src);
 385         kfree(sdata);
 386 }
 387 
 388 static struct pernet_operations ip6_segments_ops = {
 389         .init = seg6_net_init,
 390         .exit = seg6_net_exit,
 391 };
 392 
 393 static const struct genl_ops seg6_genl_ops[] = {
 394         {
 395                 .cmd    = SEG6_CMD_SETHMAC,
 396                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 397                 .doit   = seg6_genl_sethmac,
 398                 .flags  = GENL_ADMIN_PERM,
 399         },
 400         {
 401                 .cmd    = SEG6_CMD_DUMPHMAC,
 402                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 403                 .start  = seg6_genl_dumphmac_start,
 404                 .dumpit = seg6_genl_dumphmac,
 405                 .done   = seg6_genl_dumphmac_done,
 406                 .flags  = GENL_ADMIN_PERM,
 407         },
 408         {
 409                 .cmd    = SEG6_CMD_SET_TUNSRC,
 410                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 411                 .doit   = seg6_genl_set_tunsrc,
 412                 .flags  = GENL_ADMIN_PERM,
 413         },
 414         {
 415                 .cmd    = SEG6_CMD_GET_TUNSRC,
 416                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 417                 .doit   = seg6_genl_get_tunsrc,
 418                 .flags  = GENL_ADMIN_PERM,
 419         },
 420 };
 421 
 422 static struct genl_family seg6_genl_family __ro_after_init = {
 423         .hdrsize        = 0,
 424         .name           = SEG6_GENL_NAME,
 425         .version        = SEG6_GENL_VERSION,
 426         .maxattr        = SEG6_ATTR_MAX,
 427         .policy = seg6_genl_policy,
 428         .netnsok        = true,
 429         .parallel_ops   = true,
 430         .ops            = seg6_genl_ops,
 431         .n_ops          = ARRAY_SIZE(seg6_genl_ops),
 432         .module         = THIS_MODULE,
 433 };
 434 
 435 int __init seg6_init(void)
 436 {
 437         int err = -ENOMEM;
 438 
 439         err = genl_register_family(&seg6_genl_family);
 440         if (err)
 441                 goto out;
 442 
 443         err = register_pernet_subsys(&ip6_segments_ops);
 444         if (err)
 445                 goto out_unregister_genl;
 446 
 447 #ifdef CONFIG_IPV6_SEG6_LWTUNNEL
 448         err = seg6_iptunnel_init();
 449         if (err)
 450                 goto out_unregister_pernet;
 451 
 452         err = seg6_local_init();
 453         if (err)
 454                 goto out_unregister_pernet;
 455 #endif
 456 
 457 #ifdef CONFIG_IPV6_SEG6_HMAC
 458         err = seg6_hmac_init();
 459         if (err)
 460                 goto out_unregister_iptun;
 461 #endif
 462 
 463         pr_info("Segment Routing with IPv6\n");
 464 
 465 out:
 466         return err;
 467 #ifdef CONFIG_IPV6_SEG6_HMAC
 468 out_unregister_iptun:
 469 #ifdef CONFIG_IPV6_SEG6_LWTUNNEL
 470         seg6_local_exit();
 471         seg6_iptunnel_exit();
 472 #endif
 473 #endif
 474 #ifdef CONFIG_IPV6_SEG6_LWTUNNEL
 475 out_unregister_pernet:
 476         unregister_pernet_subsys(&ip6_segments_ops);
 477 #endif
 478 out_unregister_genl:
 479         genl_unregister_family(&seg6_genl_family);
 480         goto out;
 481 }
 482 
 483 void seg6_exit(void)
 484 {
 485 #ifdef CONFIG_IPV6_SEG6_HMAC
 486         seg6_hmac_exit();
 487 #endif
 488 #ifdef CONFIG_IPV6_SEG6_LWTUNNEL
 489         seg6_iptunnel_exit();
 490 #endif
 491         unregister_pernet_subsys(&ip6_segments_ops);
 492         genl_unregister_family(&seg6_genl_family);
 493 }

/* [<][>][^][v][top][bottom][index][help] */