linux/net/ipv6/xfrm6_policy.c
<<
>>
Prefs
   1/*
   2 * xfrm6_policy.c: based on xfrm4_policy.c
   3 *
   4 * Authors:
   5 *      Mitsuru KANDA @USAGI
   6 *      Kazunori MIYAZAWA @USAGI
   7 *      Kunihiro Ishiguro <kunihiro@ipinfusion.com>
   8 *              IPv6 support
   9 *      YOSHIFUJI Hideaki
  10 *              Split up af-specific portion
  11 *
  12 */
  13
  14#include <linux/err.h>
  15#include <linux/kernel.h>
  16#include <linux/netdevice.h>
  17#include <net/addrconf.h>
  18#include <net/dst.h>
  19#include <net/xfrm.h>
  20#include <net/ip.h>
  21#include <net/ipv6.h>
  22#include <net/ip6_route.h>
  23#if IS_ENABLED(CONFIG_IPV6_MIP6)
  24#include <net/mip6.h>
  25#endif
  26
  27static struct xfrm_policy_afinfo xfrm6_policy_afinfo;
  28
  29static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos,
  30                                          const xfrm_address_t *saddr,
  31                                          const xfrm_address_t *daddr)
  32{
  33        struct flowi6 fl6;
  34        struct dst_entry *dst;
  35        int err;
  36
  37        memset(&fl6, 0, sizeof(fl6));
  38        memcpy(&fl6.daddr, daddr, sizeof(fl6.daddr));
  39        if (saddr)
  40                memcpy(&fl6.saddr, saddr, sizeof(fl6.saddr));
  41
  42        dst = ip6_route_output(net, NULL, &fl6);
  43
  44        err = dst->error;
  45        if (dst->error) {
  46                dst_release(dst);
  47                dst = ERR_PTR(err);
  48        }
  49
  50        return dst;
  51}
  52
  53static int xfrm6_get_saddr(struct net *net,
  54                           xfrm_address_t *saddr, xfrm_address_t *daddr)
  55{
  56        struct dst_entry *dst;
  57        struct net_device *dev;
  58
  59        dst = xfrm6_dst_lookup(net, 0, NULL, daddr);
  60        if (IS_ERR(dst))
  61                return -EHOSTUNREACH;
  62
  63        dev = ip6_dst_idev(dst)->dev;
  64        ipv6_dev_get_saddr(dev_net(dev), dev,
  65                           (struct in6_addr *)&daddr->a6, 0,
  66                           (struct in6_addr *)&saddr->a6);
  67        dst_release(dst);
  68        return 0;
  69}
  70
  71static int xfrm6_get_tos(const struct flowi *fl)
  72{
  73        return 0;
  74}
  75
  76static void xfrm6_init_dst(struct net *net, struct xfrm_dst *xdst)
  77{
  78        struct rt6_info *rt = (struct rt6_info *)xdst;
  79
  80        rt6_init_peer(rt, net->ipv6.peers);
  81}
  82
  83static int xfrm6_init_path(struct xfrm_dst *path, struct dst_entry *dst,
  84                           int nfheader_len)
  85{
  86        if (dst->ops->family == AF_INET6) {
  87                struct rt6_info *rt = (struct rt6_info*)dst;
  88                if (rt->rt6i_node)
  89                        path->path_cookie = rt->rt6i_node->fn_sernum;
  90        }
  91
  92        path->u.rt6.rt6i_nfheader_len = nfheader_len;
  93
  94        return 0;
  95}
  96
  97static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev,
  98                          const struct flowi *fl)
  99{
 100        struct rt6_info *rt = (struct rt6_info*)xdst->route;
 101
 102        xdst->u.dst.dev = dev;
 103        dev_hold(dev);
 104
 105        xdst->u.rt6.rt6i_idev = in6_dev_get(dev);
 106        if (!xdst->u.rt6.rt6i_idev) {
 107                dev_put(dev);
 108                return -ENODEV;
 109        }
 110
 111        rt6_transfer_peer(&xdst->u.rt6, rt);
 112
 113        /* Sheit... I remember I did this right. Apparently,
 114         * it was magically lost, so this code needs audit */
 115        xdst->u.rt6.rt6i_flags = rt->rt6i_flags & (RTF_ANYCAST |
 116                                                   RTF_LOCAL);
 117        xdst->u.rt6.rt6i_metric = rt->rt6i_metric;
 118        xdst->u.rt6.rt6i_node = rt->rt6i_node;
 119        if (rt->rt6i_node)
 120                xdst->route_cookie = rt->rt6i_node->fn_sernum;
 121        xdst->u.rt6.rt6i_gateway = rt->rt6i_gateway;
 122        xdst->u.rt6.rt6i_dst = rt->rt6i_dst;
 123        xdst->u.rt6.rt6i_src = rt->rt6i_src;
 124
 125        return 0;
 126}
 127
 128static inline void
 129_decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse)
 130{
 131        struct flowi6 *fl6 = &fl->u.ip6;
 132        int onlyproto = 0;
 133        u16 offset = skb_network_header_len(skb);
 134        const struct ipv6hdr *hdr = ipv6_hdr(skb);
 135        struct ipv6_opt_hdr *exthdr;
 136        const unsigned char *nh = skb_network_header(skb);
 137        u8 nexthdr = nh[IP6CB(skb)->nhoff];
 138
 139        memset(fl6, 0, sizeof(struct flowi6));
 140        fl6->flowi6_mark = skb->mark;
 141
 142        fl6->daddr = reverse ? hdr->saddr : hdr->daddr;
 143        fl6->saddr = reverse ? hdr->daddr : hdr->saddr;
 144
 145        while (nh + offset + 1 < skb->data ||
 146               pskb_may_pull(skb, nh + offset + 1 - skb->data)) {
 147                nh = skb_network_header(skb);
 148                exthdr = (struct ipv6_opt_hdr *)(nh + offset);
 149
 150                switch (nexthdr) {
 151                case NEXTHDR_FRAGMENT:
 152                        onlyproto = 1;
 153                case NEXTHDR_ROUTING:
 154                case NEXTHDR_HOP:
 155                case NEXTHDR_DEST:
 156                        offset += ipv6_optlen(exthdr);
 157                        nexthdr = exthdr->nexthdr;
 158                        exthdr = (struct ipv6_opt_hdr *)(nh + offset);
 159                        break;
 160
 161                case IPPROTO_UDP:
 162                case IPPROTO_UDPLITE:
 163                case IPPROTO_TCP:
 164                case IPPROTO_SCTP:
 165                case IPPROTO_DCCP:
 166                        if (!onlyproto && (nh + offset + 4 < skb->data ||
 167                             pskb_may_pull(skb, nh + offset + 4 - skb->data))) {
 168                                __be16 *ports = (__be16 *)exthdr;
 169
 170                                fl6->fl6_sport = ports[!!reverse];
 171                                fl6->fl6_dport = ports[!reverse];
 172                        }
 173                        fl6->flowi6_proto = nexthdr;
 174                        return;
 175
 176                case IPPROTO_ICMPV6:
 177                        if (!onlyproto && pskb_may_pull(skb, nh + offset + 2 - skb->data)) {
 178                                u8 *icmp = (u8 *)exthdr;
 179
 180                                fl6->fl6_icmp_type = icmp[0];
 181                                fl6->fl6_icmp_code = icmp[1];
 182                        }
 183                        fl6->flowi6_proto = nexthdr;
 184                        return;
 185
 186#if IS_ENABLED(CONFIG_IPV6_MIP6)
 187                case IPPROTO_MH:
 188                        if (!onlyproto && pskb_may_pull(skb, nh + offset + 3 - skb->data)) {
 189                                struct ip6_mh *mh;
 190                                mh = (struct ip6_mh *)exthdr;
 191
 192                                fl6->fl6_mh_type = mh->ip6mh_type;
 193                        }
 194                        fl6->flowi6_proto = nexthdr;
 195                        return;
 196#endif
 197
 198                /* XXX Why are there these headers? */
 199                case IPPROTO_AH:
 200                case IPPROTO_ESP:
 201                case IPPROTO_COMP:
 202                default:
 203                        fl6->fl6_ipsec_spi = 0;
 204                        fl6->flowi6_proto = nexthdr;
 205                        return;
 206                }
 207        }
 208}
 209
 210static inline int xfrm6_garbage_collect(struct dst_ops *ops)
 211{
 212        struct net *net = container_of(ops, struct net, xfrm.xfrm6_dst_ops);
 213
 214        xfrm6_policy_afinfo.garbage_collect(net);
 215        return dst_entries_get_fast(ops) > ops->gc_thresh * 2;
 216}
 217
 218static void xfrm6_update_pmtu(struct dst_entry *dst, struct sock *sk,
 219                              struct sk_buff *skb, u32 mtu)
 220{
 221        struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
 222        struct dst_entry *path = xdst->route;
 223
 224        path->ops->update_pmtu(path, sk, skb, mtu);
 225}
 226
 227static void xfrm6_redirect(struct dst_entry *dst, struct sock *sk,
 228                           struct sk_buff *skb)
 229{
 230        struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
 231        struct dst_entry *path = xdst->route;
 232
 233        path->ops->redirect(path, sk, skb);
 234}
 235
 236static void xfrm6_dst_destroy(struct dst_entry *dst)
 237{
 238        struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
 239
 240        if (likely(xdst->u.rt6.rt6i_idev))
 241                in6_dev_put(xdst->u.rt6.rt6i_idev);
 242        dst_destroy_metrics_generic(dst);
 243        if (rt6_has_peer(&xdst->u.rt6)) {
 244                struct inet_peer *peer = rt6_peer_ptr(&xdst->u.rt6);
 245                inet_putpeer(peer);
 246        }
 247        xfrm_dst_destroy(xdst);
 248}
 249
 250static void xfrm6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
 251                             int unregister)
 252{
 253        struct xfrm_dst *xdst;
 254
 255        if (!unregister)
 256                return;
 257
 258        xdst = (struct xfrm_dst *)dst;
 259        if (xdst->u.rt6.rt6i_idev->dev == dev) {
 260                struct inet6_dev *loopback_idev =
 261                        in6_dev_get(dev_net(dev)->loopback_dev);
 262                BUG_ON(!loopback_idev);
 263
 264                do {
 265                        in6_dev_put(xdst->u.rt6.rt6i_idev);
 266                        xdst->u.rt6.rt6i_idev = loopback_idev;
 267                        in6_dev_hold(loopback_idev);
 268                        xdst = (struct xfrm_dst *)xdst->u.dst.child;
 269                } while (xdst->u.dst.xfrm);
 270
 271                __in6_dev_put(loopback_idev);
 272        }
 273
 274        xfrm_dst_ifdown(dst, dev);
 275}
 276
 277static struct dst_ops xfrm6_dst_ops = {
 278        .family =               AF_INET6,
 279        .protocol =             cpu_to_be16(ETH_P_IPV6),
 280        .gc =                   xfrm6_garbage_collect,
 281        .update_pmtu =          xfrm6_update_pmtu,
 282        .redirect =             xfrm6_redirect,
 283        .cow_metrics =          dst_cow_metrics_generic,
 284        .destroy =              xfrm6_dst_destroy,
 285        .ifdown =               xfrm6_dst_ifdown,
 286        .local_out =            __ip6_local_out,
 287        .gc_thresh =            1024,
 288};
 289
 290static struct xfrm_policy_afinfo xfrm6_policy_afinfo = {
 291        .family =               AF_INET6,
 292        .dst_ops =              &xfrm6_dst_ops,
 293        .dst_lookup =           xfrm6_dst_lookup,
 294        .get_saddr =            xfrm6_get_saddr,
 295        .decode_session =       _decode_session6,
 296        .get_tos =              xfrm6_get_tos,
 297        .init_dst =             xfrm6_init_dst,
 298        .init_path =            xfrm6_init_path,
 299        .fill_dst =             xfrm6_fill_dst,
 300        .blackhole_route =      ip6_blackhole_route,
 301};
 302
 303static int __init xfrm6_policy_init(void)
 304{
 305        return xfrm_policy_register_afinfo(&xfrm6_policy_afinfo);
 306}
 307
 308static void xfrm6_policy_fini(void)
 309{
 310        xfrm_policy_unregister_afinfo(&xfrm6_policy_afinfo);
 311}
 312
 313#ifdef CONFIG_SYSCTL
 314static struct ctl_table xfrm6_policy_table[] = {
 315        {
 316                .procname       = "xfrm6_gc_thresh",
 317                .data           = &init_net.xfrm.xfrm6_dst_ops.gc_thresh,
 318                .maxlen         = sizeof(int),
 319                .mode           = 0644,
 320                .proc_handler   = proc_dointvec,
 321        },
 322        { }
 323};
 324
 325static int __net_init xfrm6_net_init(struct net *net)
 326{
 327        struct ctl_table *table;
 328        struct ctl_table_header *hdr;
 329
 330        table = xfrm6_policy_table;
 331        if (!net_eq(net, &init_net)) {
 332                table = kmemdup(table, sizeof(xfrm6_policy_table), GFP_KERNEL);
 333                if (!table)
 334                        goto err_alloc;
 335
 336                table[0].data = &net->xfrm.xfrm6_dst_ops.gc_thresh;
 337        }
 338
 339        hdr = register_net_sysctl(net, "net/ipv6", table);
 340        if (!hdr)
 341                goto err_reg;
 342
 343        net->ipv6.sysctl.xfrm6_hdr = hdr;
 344        return 0;
 345
 346err_reg:
 347        if (!net_eq(net, &init_net))
 348                kfree(table);
 349err_alloc:
 350        return -ENOMEM;
 351}
 352
 353static void __net_exit xfrm6_net_exit(struct net *net)
 354{
 355        struct ctl_table *table;
 356
 357        if (net->ipv6.sysctl.xfrm6_hdr == NULL)
 358                return;
 359
 360        table = net->ipv6.sysctl.xfrm6_hdr->ctl_table_arg;
 361        unregister_net_sysctl_table(net->ipv6.sysctl.xfrm6_hdr);
 362        if (!net_eq(net, &init_net))
 363                kfree(table);
 364}
 365
 366static struct pernet_operations xfrm6_net_ops = {
 367        .init   = xfrm6_net_init,
 368        .exit   = xfrm6_net_exit,
 369};
 370#endif
 371
 372int __init xfrm6_init(void)
 373{
 374        int ret;
 375
 376        dst_entries_init(&xfrm6_dst_ops);
 377
 378        ret = xfrm6_policy_init();
 379        if (ret) {
 380                dst_entries_destroy(&xfrm6_dst_ops);
 381                goto out;
 382        }
 383        ret = xfrm6_state_init();
 384        if (ret)
 385                goto out_policy;
 386
 387#ifdef CONFIG_SYSCTL
 388        register_pernet_subsys(&xfrm6_net_ops);
 389#endif
 390out:
 391        return ret;
 392out_policy:
 393        xfrm6_policy_fini();
 394        goto out;
 395}
 396
 397void xfrm6_fini(void)
 398{
 399#ifdef CONFIG_SYSCTL
 400        unregister_pernet_subsys(&xfrm6_net_ops);
 401#endif
 402        xfrm6_policy_fini();
 403        xfrm6_state_fini();
 404        dst_entries_destroy(&xfrm6_dst_ops);
 405}
 406