Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  SR-IPv6 implementation
0004  *
0005  *  Author:
0006  *  David Lebrun <david.lebrun@uclouvain.be>
0007  */
0008 
0009 #include <linux/errno.h>
0010 #include <linux/types.h>
0011 #include <linux/socket.h>
0012 #include <linux/net.h>
0013 #include <linux/in6.h>
0014 #include <linux/slab.h>
0015 #include <linux/rhashtable.h>
0016 
0017 #include <net/ipv6.h>
0018 #include <net/protocol.h>
0019 
0020 #include <net/seg6.h>
0021 #include <net/genetlink.h>
0022 #include <linux/seg6.h>
0023 #include <linux/seg6_genl.h>
0024 #ifdef CONFIG_IPV6_SEG6_HMAC
0025 #include <net/seg6_hmac.h>
0026 #endif
0027 
0028 bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len, bool reduced)
0029 {
0030     unsigned int tlv_offset;
0031     int max_last_entry;
0032     int trailing;
0033 
0034     if (srh->type != IPV6_SRCRT_TYPE_4)
0035         return false;
0036 
0037     if (((srh->hdrlen + 1) << 3) != len)
0038         return false;
0039 
0040     if (!reduced && srh->segments_left > srh->first_segment) {
0041         return false;
0042     } else {
0043         max_last_entry = (srh->hdrlen / 2) - 1;
0044 
0045         if (srh->first_segment > max_last_entry)
0046             return false;
0047 
0048         if (srh->segments_left > srh->first_segment + 1)
0049             return false;
0050     }
0051 
0052     tlv_offset = sizeof(*srh) + ((srh->first_segment + 1) << 4);
0053 
0054     trailing = len - tlv_offset;
0055     if (trailing < 0)
0056         return false;
0057 
0058     while (trailing) {
0059         struct sr6_tlv *tlv;
0060         unsigned int tlv_len;
0061 
0062         if (trailing < sizeof(*tlv))
0063             return false;
0064 
0065         tlv = (struct sr6_tlv *)((unsigned char *)srh + tlv_offset);
0066         tlv_len = sizeof(*tlv) + tlv->len;
0067 
0068         trailing -= tlv_len;
0069         if (trailing < 0)
0070             return false;
0071 
0072         tlv_offset += tlv_len;
0073     }
0074 
0075     return true;
0076 }
0077 
0078 struct ipv6_sr_hdr *seg6_get_srh(struct sk_buff *skb, int flags)
0079 {
0080     struct ipv6_sr_hdr *srh;
0081     int len, srhoff = 0;
0082 
0083     if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, &flags) < 0)
0084         return NULL;
0085 
0086     if (!pskb_may_pull(skb, srhoff + sizeof(*srh)))
0087         return NULL;
0088 
0089     srh = (struct ipv6_sr_hdr *)(skb->data + srhoff);
0090 
0091     len = (srh->hdrlen + 1) << 3;
0092 
0093     if (!pskb_may_pull(skb, srhoff + len))
0094         return NULL;
0095 
0096     /* note that pskb_may_pull may change pointers in header;
0097      * for this reason it is necessary to reload them when needed.
0098      */
0099     srh = (struct ipv6_sr_hdr *)(skb->data + srhoff);
0100 
0101     if (!seg6_validate_srh(srh, len, true))
0102         return NULL;
0103 
0104     return srh;
0105 }
0106 
0107 /* Determine if an ICMP invoking packet contains a segment routing
0108  * header.  If it does, extract the offset to the true destination
0109  * address, which is in the first segment address.
0110  */
0111 void seg6_icmp_srh(struct sk_buff *skb, struct inet6_skb_parm *opt)
0112 {
0113     __u16 network_header = skb->network_header;
0114     struct ipv6_sr_hdr *srh;
0115 
0116     /* Update network header to point to the invoking packet
0117      * inside the ICMP packet, so we can use the seg6_get_srh()
0118      * helper.
0119      */
0120     skb_reset_network_header(skb);
0121 
0122     srh = seg6_get_srh(skb, 0);
0123     if (!srh)
0124         goto out;
0125 
0126     if (srh->type != IPV6_SRCRT_TYPE_4)
0127         goto out;
0128 
0129     opt->flags |= IP6SKB_SEG6;
0130     opt->srhoff = (unsigned char *)srh - skb->data;
0131 
0132 out:
0133     /* Restore the network header back to the ICMP packet */
0134     skb->network_header = network_header;
0135 }
0136 
0137 static struct genl_family seg6_genl_family;
0138 
0139 static const struct nla_policy seg6_genl_policy[SEG6_ATTR_MAX + 1] = {
0140     [SEG6_ATTR_DST]             = { .type = NLA_BINARY,
0141         .len = sizeof(struct in6_addr) },
0142     [SEG6_ATTR_DSTLEN]          = { .type = NLA_S32, },
0143     [SEG6_ATTR_HMACKEYID]       = { .type = NLA_U32, },
0144     [SEG6_ATTR_SECRET]          = { .type = NLA_BINARY, },
0145     [SEG6_ATTR_SECRETLEN]       = { .type = NLA_U8, },
0146     [SEG6_ATTR_ALGID]           = { .type = NLA_U8, },
0147     [SEG6_ATTR_HMACINFO]        = { .type = NLA_NESTED, },
0148 };
0149 
0150 #ifdef CONFIG_IPV6_SEG6_HMAC
0151 
0152 static int seg6_genl_sethmac(struct sk_buff *skb, struct genl_info *info)
0153 {
0154     struct net *net = genl_info_net(info);
0155     struct seg6_pernet_data *sdata;
0156     struct seg6_hmac_info *hinfo;
0157     u32 hmackeyid;
0158     char *secret;
0159     int err = 0;
0160     u8 algid;
0161     u8 slen;
0162 
0163     sdata = seg6_pernet(net);
0164 
0165     if (!info->attrs[SEG6_ATTR_HMACKEYID] ||
0166         !info->attrs[SEG6_ATTR_SECRETLEN] ||
0167         !info->attrs[SEG6_ATTR_ALGID])
0168         return -EINVAL;
0169 
0170     hmackeyid = nla_get_u32(info->attrs[SEG6_ATTR_HMACKEYID]);
0171     slen = nla_get_u8(info->attrs[SEG6_ATTR_SECRETLEN]);
0172     algid = nla_get_u8(info->attrs[SEG6_ATTR_ALGID]);
0173 
0174     if (hmackeyid == 0)
0175         return -EINVAL;
0176 
0177     if (slen > SEG6_HMAC_SECRET_LEN)
0178         return -EINVAL;
0179 
0180     mutex_lock(&sdata->lock);
0181     hinfo = seg6_hmac_info_lookup(net, hmackeyid);
0182 
0183     if (!slen) {
0184         err = seg6_hmac_info_del(net, hmackeyid);
0185 
0186         goto out_unlock;
0187     }
0188 
0189     if (!info->attrs[SEG6_ATTR_SECRET]) {
0190         err = -EINVAL;
0191         goto out_unlock;
0192     }
0193 
0194     if (slen > nla_len(info->attrs[SEG6_ATTR_SECRET])) {
0195         err = -EINVAL;
0196         goto out_unlock;
0197     }
0198 
0199     if (hinfo) {
0200         err = seg6_hmac_info_del(net, hmackeyid);
0201         if (err)
0202             goto out_unlock;
0203     }
0204 
0205     secret = (char *)nla_data(info->attrs[SEG6_ATTR_SECRET]);
0206 
0207     hinfo = kzalloc(sizeof(*hinfo), GFP_KERNEL);
0208     if (!hinfo) {
0209         err = -ENOMEM;
0210         goto out_unlock;
0211     }
0212 
0213     memcpy(hinfo->secret, secret, slen);
0214     hinfo->slen = slen;
0215     hinfo->alg_id = algid;
0216     hinfo->hmackeyid = hmackeyid;
0217 
0218     err = seg6_hmac_info_add(net, hmackeyid, hinfo);
0219     if (err)
0220         kfree(hinfo);
0221 
0222 out_unlock:
0223     mutex_unlock(&sdata->lock);
0224     return err;
0225 }
0226 
0227 #else
0228 
0229 static int seg6_genl_sethmac(struct sk_buff *skb, struct genl_info *info)
0230 {
0231     return -ENOTSUPP;
0232 }
0233 
0234 #endif
0235 
0236 static int seg6_genl_set_tunsrc(struct sk_buff *skb, struct genl_info *info)
0237 {
0238     struct net *net = genl_info_net(info);
0239     struct in6_addr *val, *t_old, *t_new;
0240     struct seg6_pernet_data *sdata;
0241 
0242     sdata = seg6_pernet(net);
0243 
0244     if (!info->attrs[SEG6_ATTR_DST])
0245         return -EINVAL;
0246 
0247     val = nla_data(info->attrs[SEG6_ATTR_DST]);
0248     t_new = kmemdup(val, sizeof(*val), GFP_KERNEL);
0249     if (!t_new)
0250         return -ENOMEM;
0251 
0252     mutex_lock(&sdata->lock);
0253 
0254     t_old = sdata->tun_src;
0255     rcu_assign_pointer(sdata->tun_src, t_new);
0256 
0257     mutex_unlock(&sdata->lock);
0258 
0259     synchronize_net();
0260     kfree(t_old);
0261 
0262     return 0;
0263 }
0264 
0265 static int seg6_genl_get_tunsrc(struct sk_buff *skb, struct genl_info *info)
0266 {
0267     struct net *net = genl_info_net(info);
0268     struct in6_addr *tun_src;
0269     struct sk_buff *msg;
0270     void *hdr;
0271 
0272     msg = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
0273     if (!msg)
0274         return -ENOMEM;
0275 
0276     hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
0277               &seg6_genl_family, 0, SEG6_CMD_GET_TUNSRC);
0278     if (!hdr)
0279         goto free_msg;
0280 
0281     rcu_read_lock();
0282     tun_src = rcu_dereference(seg6_pernet(net)->tun_src);
0283 
0284     if (nla_put(msg, SEG6_ATTR_DST, sizeof(struct in6_addr), tun_src))
0285         goto nla_put_failure;
0286 
0287     rcu_read_unlock();
0288 
0289     genlmsg_end(msg, hdr);
0290     return genlmsg_reply(msg, info);
0291 
0292 nla_put_failure:
0293     rcu_read_unlock();
0294 free_msg:
0295     nlmsg_free(msg);
0296     return -ENOMEM;
0297 }
0298 
0299 #ifdef CONFIG_IPV6_SEG6_HMAC
0300 
0301 static int __seg6_hmac_fill_info(struct seg6_hmac_info *hinfo,
0302                  struct sk_buff *msg)
0303 {
0304     if (nla_put_u32(msg, SEG6_ATTR_HMACKEYID, hinfo->hmackeyid) ||
0305         nla_put_u8(msg, SEG6_ATTR_SECRETLEN, hinfo->slen) ||
0306         nla_put(msg, SEG6_ATTR_SECRET, hinfo->slen, hinfo->secret) ||
0307         nla_put_u8(msg, SEG6_ATTR_ALGID, hinfo->alg_id))
0308         return -1;
0309 
0310     return 0;
0311 }
0312 
0313 static int __seg6_genl_dumphmac_element(struct seg6_hmac_info *hinfo,
0314                     u32 portid, u32 seq, u32 flags,
0315                     struct sk_buff *skb, u8 cmd)
0316 {
0317     void *hdr;
0318 
0319     hdr = genlmsg_put(skb, portid, seq, &seg6_genl_family, flags, cmd);
0320     if (!hdr)
0321         return -ENOMEM;
0322 
0323     if (__seg6_hmac_fill_info(hinfo, skb) < 0)
0324         goto nla_put_failure;
0325 
0326     genlmsg_end(skb, hdr);
0327     return 0;
0328 
0329 nla_put_failure:
0330     genlmsg_cancel(skb, hdr);
0331     return -EMSGSIZE;
0332 }
0333 
0334 static int seg6_genl_dumphmac_start(struct netlink_callback *cb)
0335 {
0336     struct net *net = sock_net(cb->skb->sk);
0337     struct seg6_pernet_data *sdata;
0338     struct rhashtable_iter *iter;
0339 
0340     sdata = seg6_pernet(net);
0341     iter = (struct rhashtable_iter *)cb->args[0];
0342 
0343     if (!iter) {
0344         iter = kmalloc(sizeof(*iter), GFP_KERNEL);
0345         if (!iter)
0346             return -ENOMEM;
0347 
0348         cb->args[0] = (long)iter;
0349     }
0350 
0351     rhashtable_walk_enter(&sdata->hmac_infos, iter);
0352 
0353     return 0;
0354 }
0355 
0356 static int seg6_genl_dumphmac_done(struct netlink_callback *cb)
0357 {
0358     struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
0359 
0360     rhashtable_walk_exit(iter);
0361 
0362     kfree(iter);
0363 
0364     return 0;
0365 }
0366 
0367 static int seg6_genl_dumphmac(struct sk_buff *skb, struct netlink_callback *cb)
0368 {
0369     struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
0370     struct seg6_hmac_info *hinfo;
0371     int ret;
0372 
0373     rhashtable_walk_start(iter);
0374 
0375     for (;;) {
0376         hinfo = rhashtable_walk_next(iter);
0377 
0378         if (IS_ERR(hinfo)) {
0379             if (PTR_ERR(hinfo) == -EAGAIN)
0380                 continue;
0381             ret = PTR_ERR(hinfo);
0382             goto done;
0383         } else if (!hinfo) {
0384             break;
0385         }
0386 
0387         ret = __seg6_genl_dumphmac_element(hinfo,
0388                            NETLINK_CB(cb->skb).portid,
0389                            cb->nlh->nlmsg_seq,
0390                            NLM_F_MULTI,
0391                            skb, SEG6_CMD_DUMPHMAC);
0392         if (ret)
0393             goto done;
0394     }
0395 
0396     ret = skb->len;
0397 
0398 done:
0399     rhashtable_walk_stop(iter);
0400     return ret;
0401 }
0402 
0403 #else
0404 
0405 static int seg6_genl_dumphmac_start(struct netlink_callback *cb)
0406 {
0407     return 0;
0408 }
0409 
0410 static int seg6_genl_dumphmac_done(struct netlink_callback *cb)
0411 {
0412     return 0;
0413 }
0414 
0415 static int seg6_genl_dumphmac(struct sk_buff *skb, struct netlink_callback *cb)
0416 {
0417     return -ENOTSUPP;
0418 }
0419 
0420 #endif
0421 
0422 static int __net_init seg6_net_init(struct net *net)
0423 {
0424     struct seg6_pernet_data *sdata;
0425 
0426     sdata = kzalloc(sizeof(*sdata), GFP_KERNEL);
0427     if (!sdata)
0428         return -ENOMEM;
0429 
0430     mutex_init(&sdata->lock);
0431 
0432     sdata->tun_src = kzalloc(sizeof(*sdata->tun_src), GFP_KERNEL);
0433     if (!sdata->tun_src) {
0434         kfree(sdata);
0435         return -ENOMEM;
0436     }
0437 
0438     net->ipv6.seg6_data = sdata;
0439 
0440 #ifdef CONFIG_IPV6_SEG6_HMAC
0441     if (seg6_hmac_net_init(net)) {
0442         kfree(rcu_dereference_raw(sdata->tun_src));
0443         kfree(sdata);
0444         return -ENOMEM;
0445     }
0446 #endif
0447 
0448     return 0;
0449 }
0450 
0451 static void __net_exit seg6_net_exit(struct net *net)
0452 {
0453     struct seg6_pernet_data *sdata = seg6_pernet(net);
0454 
0455 #ifdef CONFIG_IPV6_SEG6_HMAC
0456     seg6_hmac_net_exit(net);
0457 #endif
0458 
0459     kfree(rcu_dereference_raw(sdata->tun_src));
0460     kfree(sdata);
0461 }
0462 
0463 static struct pernet_operations ip6_segments_ops = {
0464     .init = seg6_net_init,
0465     .exit = seg6_net_exit,
0466 };
0467 
0468 static const struct genl_ops seg6_genl_ops[] = {
0469     {
0470         .cmd    = SEG6_CMD_SETHMAC,
0471         .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
0472         .doit   = seg6_genl_sethmac,
0473         .flags  = GENL_ADMIN_PERM,
0474     },
0475     {
0476         .cmd    = SEG6_CMD_DUMPHMAC,
0477         .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
0478         .start  = seg6_genl_dumphmac_start,
0479         .dumpit = seg6_genl_dumphmac,
0480         .done   = seg6_genl_dumphmac_done,
0481         .flags  = GENL_ADMIN_PERM,
0482     },
0483     {
0484         .cmd    = SEG6_CMD_SET_TUNSRC,
0485         .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
0486         .doit   = seg6_genl_set_tunsrc,
0487         .flags  = GENL_ADMIN_PERM,
0488     },
0489     {
0490         .cmd    = SEG6_CMD_GET_TUNSRC,
0491         .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
0492         .doit   = seg6_genl_get_tunsrc,
0493         .flags  = GENL_ADMIN_PERM,
0494     },
0495 };
0496 
0497 static struct genl_family seg6_genl_family __ro_after_init = {
0498     .hdrsize    = 0,
0499     .name       = SEG6_GENL_NAME,
0500     .version    = SEG6_GENL_VERSION,
0501     .maxattr    = SEG6_ATTR_MAX,
0502     .policy = seg6_genl_policy,
0503     .netnsok    = true,
0504     .parallel_ops   = true,
0505     .ops        = seg6_genl_ops,
0506     .n_ops      = ARRAY_SIZE(seg6_genl_ops),
0507     .module     = THIS_MODULE,
0508 };
0509 
0510 int __init seg6_init(void)
0511 {
0512     int err;
0513 
0514     err = genl_register_family(&seg6_genl_family);
0515     if (err)
0516         goto out;
0517 
0518     err = register_pernet_subsys(&ip6_segments_ops);
0519     if (err)
0520         goto out_unregister_genl;
0521 
0522 #ifdef CONFIG_IPV6_SEG6_LWTUNNEL
0523     err = seg6_iptunnel_init();
0524     if (err)
0525         goto out_unregister_pernet;
0526 
0527     err = seg6_local_init();
0528     if (err)
0529         goto out_unregister_pernet;
0530 #endif
0531 
0532 #ifdef CONFIG_IPV6_SEG6_HMAC
0533     err = seg6_hmac_init();
0534     if (err)
0535         goto out_unregister_iptun;
0536 #endif
0537 
0538     pr_info("Segment Routing with IPv6\n");
0539 
0540 out:
0541     return err;
0542 #ifdef CONFIG_IPV6_SEG6_HMAC
0543 out_unregister_iptun:
0544 #ifdef CONFIG_IPV6_SEG6_LWTUNNEL
0545     seg6_local_exit();
0546     seg6_iptunnel_exit();
0547 #endif
0548 #endif
0549 #ifdef CONFIG_IPV6_SEG6_LWTUNNEL
0550 out_unregister_pernet:
0551     unregister_pernet_subsys(&ip6_segments_ops);
0552 #endif
0553 out_unregister_genl:
0554     genl_unregister_family(&seg6_genl_family);
0555     goto out;
0556 }
0557 
0558 void seg6_exit(void)
0559 {
0560 #ifdef CONFIG_IPV6_SEG6_HMAC
0561     seg6_hmac_exit();
0562 #endif
0563 #ifdef CONFIG_IPV6_SEG6_LWTUNNEL
0564     seg6_iptunnel_exit();
0565 #endif
0566     unregister_pernet_subsys(&ip6_segments_ops);
0567     genl_unregister_family(&seg6_genl_family);
0568 }