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                              bool confirm_neigh)
 103{
 104        struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
 105        struct dst_entry *path = xdst->route;
 106
 107        path->ops->update_pmtu(path, sk, skb, mtu, confirm_neigh);
 108}
 109
 110static void xfrm6_redirect(struct dst_entry *dst, struct sock *sk,
 111                           struct sk_buff *skb)
 112{
 113        struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
 114        struct dst_entry *path = xdst->route;
 115
 116        path->ops->redirect(path, sk, skb);
 117}
 118
 119static void xfrm6_dst_destroy(struct dst_entry *dst)
 120{
 121        struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
 122
 123        if (likely(xdst->u.rt6.rt6i_idev))
 124                in6_dev_put(xdst->u.rt6.rt6i_idev);
 125        dst_destroy_metrics_generic(dst);
 126        if (xdst->u.rt6.rt6i_uncached_list)
 127                rt6_uncached_list_del(&xdst->u.rt6);
 128        xfrm_dst_destroy(xdst);
 129}
 130
 131static void xfrm6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
 132                             int unregister)
 133{
 134        struct xfrm_dst *xdst;
 135
 136        if (!unregister)
 137                return;
 138
 139        xdst = (struct xfrm_dst *)dst;
 140        if (xdst->u.rt6.rt6i_idev->dev == dev) {
 141                struct inet6_dev *loopback_idev =
 142                        in6_dev_get(dev_net(dev)->loopback_dev);
 143
 144                do {
 145                        in6_dev_put(xdst->u.rt6.rt6i_idev);
 146                        xdst->u.rt6.rt6i_idev = loopback_idev;
 147                        in6_dev_hold(loopback_idev);
 148                        xdst = (struct xfrm_dst *)xfrm_dst_child(&xdst->u.dst);
 149                } while (xdst->u.dst.xfrm);
 150
 151                __in6_dev_put(loopback_idev);
 152        }
 153
 154        xfrm_dst_ifdown(dst, dev);
 155}
 156
 157static struct dst_ops xfrm6_dst_ops_template = {
 158        .family =               AF_INET6,
 159        .update_pmtu =          xfrm6_update_pmtu,
 160        .redirect =             xfrm6_redirect,
 161        .cow_metrics =          dst_cow_metrics_generic,
 162        .destroy =              xfrm6_dst_destroy,
 163        .ifdown =               xfrm6_dst_ifdown,
 164        .local_out =            __ip6_local_out,
 165        .gc_thresh =            32768,
 166};
 167
 168static const struct xfrm_policy_afinfo xfrm6_policy_afinfo = {
 169        .dst_ops =              &xfrm6_dst_ops_template,
 170        .dst_lookup =           xfrm6_dst_lookup,
 171        .get_saddr =            xfrm6_get_saddr,
 172        .fill_dst =             xfrm6_fill_dst,
 173        .blackhole_route =      ip6_blackhole_route,
 174};
 175
 176static int __init xfrm6_policy_init(void)
 177{
 178        return xfrm_policy_register_afinfo(&xfrm6_policy_afinfo, AF_INET6);
 179}
 180
 181static void xfrm6_policy_fini(void)
 182{
 183        xfrm_policy_unregister_afinfo(&xfrm6_policy_afinfo);
 184}
 185
 186#ifdef CONFIG_SYSCTL
 187static struct ctl_table xfrm6_policy_table[] = {
 188        {
 189                .procname       = "xfrm6_gc_thresh",
 190                .data           = &init_net.xfrm.xfrm6_dst_ops.gc_thresh,
 191                .maxlen         = sizeof(int),
 192                .mode           = 0644,
 193                .proc_handler   = proc_dointvec,
 194        },
 195        { }
 196};
 197
 198static int __net_init xfrm6_net_sysctl_init(struct net *net)
 199{
 200        struct ctl_table *table;
 201        struct ctl_table_header *hdr;
 202
 203        table = xfrm6_policy_table;
 204        if (!net_eq(net, &init_net)) {
 205                table = kmemdup(table, sizeof(xfrm6_policy_table), GFP_KERNEL);
 206                if (!table)
 207                        goto err_alloc;
 208
 209                table[0].data = &net->xfrm.xfrm6_dst_ops.gc_thresh;
 210        }
 211
 212        hdr = register_net_sysctl(net, "net/ipv6", table);
 213        if (!hdr)
 214                goto err_reg;
 215
 216        net->ipv6.sysctl.xfrm6_hdr = hdr;
 217        return 0;
 218
 219err_reg:
 220        if (!net_eq(net, &init_net))
 221                kfree(table);
 222err_alloc:
 223        return -ENOMEM;
 224}
 225
 226static void __net_exit xfrm6_net_sysctl_exit(struct net *net)
 227{
 228        struct ctl_table *table;
 229
 230        if (!net->ipv6.sysctl.xfrm6_hdr)
 231                return;
 232
 233        table = net->ipv6.sysctl.xfrm6_hdr->ctl_table_arg;
 234        unregister_net_sysctl_table(net->ipv6.sysctl.xfrm6_hdr);
 235        if (!net_eq(net, &init_net))
 236                kfree(table);
 237}
 238#else /* CONFIG_SYSCTL */
 239static inline int xfrm6_net_sysctl_init(struct net *net)
 240{
 241        return 0;
 242}
 243
 244static inline void xfrm6_net_sysctl_exit(struct net *net)
 245{
 246}
 247#endif
 248
 249static int __net_init xfrm6_net_init(struct net *net)
 250{
 251        int ret;
 252
 253        memcpy(&net->xfrm.xfrm6_dst_ops, &xfrm6_dst_ops_template,
 254               sizeof(xfrm6_dst_ops_template));
 255        ret = dst_entries_init(&net->xfrm.xfrm6_dst_ops);
 256        if (ret)
 257                return ret;
 258
 259        ret = xfrm6_net_sysctl_init(net);
 260        if (ret)
 261                dst_entries_destroy(&net->xfrm.xfrm6_dst_ops);
 262
 263        return ret;
 264}
 265
 266static void __net_exit xfrm6_net_exit(struct net *net)
 267{
 268        xfrm6_net_sysctl_exit(net);
 269        dst_entries_destroy(&net->xfrm.xfrm6_dst_ops);
 270}
 271
 272static struct pernet_operations xfrm6_net_ops = {
 273        .init   = xfrm6_net_init,
 274        .exit   = xfrm6_net_exit,
 275};
 276
 277int __init xfrm6_init(void)
 278{
 279        int ret;
 280
 281        ret = xfrm6_policy_init();
 282        if (ret)
 283                goto out;
 284        ret = xfrm6_state_init();
 285        if (ret)
 286                goto out_policy;
 287
 288        ret = xfrm6_protocol_init();
 289        if (ret)
 290                goto out_state;
 291
 292        register_pernet_subsys(&xfrm6_net_ops);
 293out:
 294        return ret;
 295out_state:
 296        xfrm6_state_fini();
 297out_policy:
 298        xfrm6_policy_fini();
 299        goto out;
 300}
 301
 302void xfrm6_fini(void)
 303{
 304        unregister_pernet_subsys(&xfrm6_net_ops);
 305        xfrm6_protocol_fini();
 306        xfrm6_policy_fini();
 307        xfrm6_state_fini();
 308}
 309