linux/net/ipv6/xfrm6_policy.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * xfrm6_policy.c: based on xfrm4_policy.c
   4 *
   5 * Authors:
   6 *      Mitsuru KANDA @USAGI
   7 *      Kazunori MIYAZAWA @USAGI
   8 *      Kunihiro Ishiguro <kunihiro@ipinfusion.com>
   9 *              IPv6 support
  10 *      YOSHIFUJI Hideaki
  11 *              Split up af-specific portion
  12 *
  13 */
  14
  15#include <linux/err.h>
  16#include <linux/kernel.h>
  17#include <linux/netdevice.h>
  18#include <net/addrconf.h>
  19#include <net/dst.h>
  20#include <net/xfrm.h>
  21#include <net/ip.h>
  22#include <net/ipv6.h>
  23#include <net/ip6_route.h>
  24#include <net/l3mdev.h>
  25
  26static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, int oif,
  27                                          const xfrm_address_t *saddr,
  28                                          const xfrm_address_t *daddr,
  29                                          u32 mark)
  30{
  31        struct flowi6 fl6;
  32        struct dst_entry *dst;
  33        int err;
  34
  35        memset(&fl6, 0, sizeof(fl6));
  36        fl6.flowi6_oif = l3mdev_master_ifindex_by_index(net, oif);
  37        fl6.flowi6_flags = FLOWI_FLAG_SKIP_NH_OIF;
  38        fl6.flowi6_mark = mark;
  39        memcpy(&fl6.daddr, daddr, sizeof(fl6.daddr));
  40        if (saddr)
  41                memcpy(&fl6.saddr, saddr, sizeof(fl6.saddr));
  42
  43        dst = ip6_route_output(net, NULL, &fl6);
  44
  45        err = dst->error;
  46        if (dst->error) {
  47                dst_release(dst);
  48                dst = ERR_PTR(err);
  49        }
  50
  51        return dst;
  52}
  53
  54static int xfrm6_get_saddr(struct net *net, int oif,
  55                           xfrm_address_t *saddr, xfrm_address_t *daddr,
  56                           u32 mark)
  57{
  58        struct dst_entry *dst;
  59        struct net_device *dev;
  60
  61        dst = xfrm6_dst_lookup(net, 0, oif, NULL, daddr, mark);
  62        if (IS_ERR(dst))
  63                return -EHOSTUNREACH;
  64
  65        dev = ip6_dst_idev(dst)->dev;
  66        ipv6_dev_get_saddr(dev_net(dev), dev, &daddr->in6, 0, &saddr->in6);
  67        dst_release(dst);
  68        return 0;
  69}
  70
  71static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev,
  72                          const struct flowi *fl)
  73{
  74        struct rt6_info *rt = (struct rt6_info *)xdst->route;
  75
  76        xdst->u.dst.dev = dev;
  77        dev_hold(dev);
  78
  79        xdst->u.rt6.rt6i_idev = in6_dev_get(dev);
  80        if (!xdst->u.rt6.rt6i_idev) {
  81                dev_put(dev);
  82                return -ENODEV;
  83        }
  84
  85        /* Sheit... I remember I did this right. Apparently,
  86         * it was magically lost, so this code needs audit */
  87        xdst->u.rt6.rt6i_flags = rt->rt6i_flags & (RTF_ANYCAST |
  88                                                   RTF_LOCAL);
  89        xdst->route_cookie = rt6_get_cookie(rt);
  90        xdst->u.rt6.rt6i_gateway = rt->rt6i_gateway;
  91        xdst->u.rt6.rt6i_dst = rt->rt6i_dst;
  92        xdst->u.rt6.rt6i_src = rt->rt6i_src;
  93        INIT_LIST_HEAD(&xdst->u.rt6.rt6i_uncached);
  94        rt6_uncached_list_add(&xdst->u.rt6);
  95        atomic_inc(&dev_net(dev)->ipv6.rt6_stats->fib_rt_uncache);
  96
  97        return 0;
  98}
  99
 100static void xfrm6_update_pmtu(struct dst_entry *dst, struct sock *sk,
 101                              struct sk_buff *skb, u32 mtu)
 102{
 103        struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
 104        struct dst_entry *path = xdst->route;
 105
 106        path->ops->update_pmtu(path, sk, skb, mtu);
 107}
 108
 109static void xfrm6_redirect(struct dst_entry *dst, struct sock *sk,
 110                           struct sk_buff *skb)
 111{
 112        struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
 113        struct dst_entry *path = xdst->route;
 114
 115        path->ops->redirect(path, sk, skb);
 116}
 117
 118static void xfrm6_dst_destroy(struct dst_entry *dst)
 119{
 120        struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
 121
 122        if (likely(xdst->u.rt6.rt6i_idev))
 123                in6_dev_put(xdst->u.rt6.rt6i_idev);
 124        dst_destroy_metrics_generic(dst);
 125        if (xdst->u.rt6.rt6i_uncached_list)
 126                rt6_uncached_list_del(&xdst->u.rt6);
 127        xfrm_dst_destroy(xdst);
 128}
 129
 130static void xfrm6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
 131                             int unregister)
 132{
 133        struct xfrm_dst *xdst;
 134
 135        if (!unregister)
 136                return;
 137
 138        xdst = (struct xfrm_dst *)dst;
 139        if (xdst->u.rt6.rt6i_idev->dev == dev) {
 140                struct inet6_dev *loopback_idev =
 141                        in6_dev_get(dev_net(dev)->loopback_dev);
 142
 143                do {
 144                        in6_dev_put(xdst->u.rt6.rt6i_idev);
 145                        xdst->u.rt6.rt6i_idev = loopback_idev;
 146                        in6_dev_hold(loopback_idev);
 147                        xdst = (struct xfrm_dst *)xfrm_dst_child(&xdst->u.dst);
 148                } while (xdst->u.dst.xfrm);
 149
 150                __in6_dev_put(loopback_idev);
 151        }
 152
 153        xfrm_dst_ifdown(dst, dev);
 154}
 155
 156static struct dst_ops xfrm6_dst_ops_template = {
 157        .family =               AF_INET6,
 158        .update_pmtu =          xfrm6_update_pmtu,
 159        .redirect =             xfrm6_redirect,
 160        .cow_metrics =          dst_cow_metrics_generic,
 161        .destroy =              xfrm6_dst_destroy,
 162        .ifdown =               xfrm6_dst_ifdown,
 163        .local_out =            __ip6_local_out,
 164        .gc_thresh =            32768,
 165};
 166
 167static const struct xfrm_policy_afinfo xfrm6_policy_afinfo = {
 168        .dst_ops =              &xfrm6_dst_ops_template,
 169        .dst_lookup =           xfrm6_dst_lookup,
 170        .get_saddr =            xfrm6_get_saddr,
 171        .fill_dst =             xfrm6_fill_dst,
 172        .blackhole_route =      ip6_blackhole_route,
 173};
 174
 175static int __init xfrm6_policy_init(void)
 176{
 177        return xfrm_policy_register_afinfo(&xfrm6_policy_afinfo, AF_INET6);
 178}
 179
 180static void xfrm6_policy_fini(void)
 181{
 182        xfrm_policy_unregister_afinfo(&xfrm6_policy_afinfo);
 183}
 184
 185#ifdef CONFIG_SYSCTL
 186static struct ctl_table xfrm6_policy_table[] = {
 187        {
 188                .procname       = "xfrm6_gc_thresh",
 189                .data           = &init_net.xfrm.xfrm6_dst_ops.gc_thresh,
 190                .maxlen         = sizeof(int),
 191                .mode           = 0644,
 192                .proc_handler   = proc_dointvec,
 193        },
 194        { }
 195};
 196
 197static int __net_init xfrm6_net_sysctl_init(struct net *net)
 198{
 199        struct ctl_table *table;
 200        struct ctl_table_header *hdr;
 201
 202        table = xfrm6_policy_table;
 203        if (!net_eq(net, &init_net)) {
 204                table = kmemdup(table, sizeof(xfrm6_policy_table), GFP_KERNEL);
 205                if (!table)
 206                        goto err_alloc;
 207
 208                table[0].data = &net->xfrm.xfrm6_dst_ops.gc_thresh;
 209        }
 210
 211        hdr = register_net_sysctl(net, "net/ipv6", table);
 212        if (!hdr)
 213                goto err_reg;
 214
 215        net->ipv6.sysctl.xfrm6_hdr = hdr;
 216        return 0;
 217
 218err_reg:
 219        if (!net_eq(net, &init_net))
 220                kfree(table);
 221err_alloc:
 222        return -ENOMEM;
 223}
 224
 225static void __net_exit xfrm6_net_sysctl_exit(struct net *net)
 226{
 227        struct ctl_table *table;
 228
 229        if (!net->ipv6.sysctl.xfrm6_hdr)
 230                return;
 231
 232        table = net->ipv6.sysctl.xfrm6_hdr->ctl_table_arg;
 233        unregister_net_sysctl_table(net->ipv6.sysctl.xfrm6_hdr);
 234        if (!net_eq(net, &init_net))
 235                kfree(table);
 236}
 237#else /* CONFIG_SYSCTL */
 238static inline int xfrm6_net_sysctl_init(struct net *net)
 239{
 240        return 0;
 241}
 242
 243static inline void xfrm6_net_sysctl_exit(struct net *net)
 244{
 245}
 246#endif
 247
 248static int __net_init xfrm6_net_init(struct net *net)
 249{
 250        int ret;
 251
 252        memcpy(&net->xfrm.xfrm6_dst_ops, &xfrm6_dst_ops_template,
 253               sizeof(xfrm6_dst_ops_template));
 254        ret = dst_entries_init(&net->xfrm.xfrm6_dst_ops);
 255        if (ret)
 256                return ret;
 257
 258        ret = xfrm6_net_sysctl_init(net);
 259        if (ret)
 260                dst_entries_destroy(&net->xfrm.xfrm6_dst_ops);
 261
 262        return ret;
 263}
 264
 265static void __net_exit xfrm6_net_exit(struct net *net)
 266{
 267        xfrm6_net_sysctl_exit(net);
 268        dst_entries_destroy(&net->xfrm.xfrm6_dst_ops);
 269}
 270
 271static struct pernet_operations xfrm6_net_ops = {
 272        .init   = xfrm6_net_init,
 273        .exit   = xfrm6_net_exit,
 274};
 275
 276int __init xfrm6_init(void)
 277{
 278        int ret;
 279
 280        ret = xfrm6_policy_init();
 281        if (ret)
 282                goto out;
 283        ret = xfrm6_state_init();
 284        if (ret)
 285                goto out_policy;
 286
 287        ret = xfrm6_protocol_init();
 288        if (ret)
 289                goto out_state;
 290
 291        register_pernet_subsys(&xfrm6_net_ops);
 292out:
 293        return ret;
 294out_state:
 295        xfrm6_state_fini();
 296out_policy:
 297        xfrm6_policy_fini();
 298        goto out;
 299}
 300
 301void xfrm6_fini(void)
 302{
 303        unregister_pernet_subsys(&xfrm6_net_ops);
 304        xfrm6_protocol_fini();
 305        xfrm6_policy_fini();
 306        xfrm6_state_fini();
 307}
 308