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