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        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
  68static struct genl_family seg6_genl_family;
  69
  70static 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
  83static 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
 151out_unlock:
 152        mutex_unlock(&sdata->lock);
 153        return err;
 154}
 155
 156#else
 157
 158static int seg6_genl_sethmac(struct sk_buff *skb, struct genl_info *info)
 159{
 160        return -ENOTSUPP;
 161}
 162
 163#endif
 164
 165static 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
 194static 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
 221nla_put_failure:
 222        rcu_read_unlock();
 223free_msg:
 224        nlmsg_free(msg);
 225        return -ENOMEM;
 226}
 227
 228#ifdef CONFIG_IPV6_SEG6_HMAC
 229
 230static 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
 242static 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
 258nla_put_failure:
 259        genlmsg_cancel(skb, hdr);
 260        return -EMSGSIZE;
 261}
 262
 263static 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
 285static 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
 296static 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
 327done:
 328        rhashtable_walk_stop(iter);
 329        return ret;
 330}
 331
 332#else
 333
 334static int seg6_genl_dumphmac_start(struct netlink_callback *cb)
 335{
 336        return 0;
 337}
 338
 339static int seg6_genl_dumphmac_done(struct netlink_callback *cb)
 340{
 341        return 0;
 342}
 343
 344static int seg6_genl_dumphmac(struct sk_buff *skb, struct netlink_callback *cb)
 345{
 346        return -ENOTSUPP;
 347}
 348
 349#endif
 350
 351static 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
 376static 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
 388static struct pernet_operations ip6_segments_ops = {
 389        .init = seg6_net_init,
 390        .exit = seg6_net_exit,
 391};
 392
 393static 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
 422static 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
 435int __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
 465out:
 466        return err;
 467#ifdef CONFIG_IPV6_SEG6_HMAC
 468out_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
 475out_unregister_pernet:
 476        unregister_pernet_subsys(&ip6_segments_ops);
 477#endif
 478out_unregister_genl:
 479        genl_unregister_family(&seg6_genl_family);
 480        goto out;
 481}
 482
 483void 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}
 494