root/net/ipv6/xfrm6_policy.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. xfrm6_dst_lookup
  2. xfrm6_get_saddr
  3. xfrm6_fill_dst
  4. xfrm6_update_pmtu
  5. xfrm6_redirect
  6. xfrm6_dst_destroy
  7. xfrm6_dst_ifdown
  8. xfrm6_policy_init
  9. xfrm6_policy_fini
  10. xfrm6_net_sysctl_init
  11. xfrm6_net_sysctl_exit
  12. xfrm6_net_sysctl_init
  13. xfrm6_net_sysctl_exit
  14. xfrm6_net_init
  15. xfrm6_net_exit
  16. xfrm6_init
  17. xfrm6_fini

   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 
  26 static 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 
  54 static 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 
  71 static 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 
 100 static 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 
 110 static 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 
 119 static 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 
 131 static 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 
 157 static 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 
 168 static 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 
 176 static int __init xfrm6_policy_init(void)
 177 {
 178         return xfrm_policy_register_afinfo(&xfrm6_policy_afinfo, AF_INET6);
 179 }
 180 
 181 static void xfrm6_policy_fini(void)
 182 {
 183         xfrm_policy_unregister_afinfo(&xfrm6_policy_afinfo);
 184 }
 185 
 186 #ifdef CONFIG_SYSCTL
 187 static 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 
 198 static 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 
 219 err_reg:
 220         if (!net_eq(net, &init_net))
 221                 kfree(table);
 222 err_alloc:
 223         return -ENOMEM;
 224 }
 225 
 226 static 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 */
 239 static inline int xfrm6_net_sysctl_init(struct net *net)
 240 {
 241         return 0;
 242 }
 243 
 244 static inline void xfrm6_net_sysctl_exit(struct net *net)
 245 {
 246 }
 247 #endif
 248 
 249 static 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 
 266 static 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 
 272 static struct pernet_operations xfrm6_net_ops = {
 273         .init   = xfrm6_net_init,
 274         .exit   = xfrm6_net_exit,
 275 };
 276 
 277 int __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);
 293 out:
 294         return ret;
 295 out_state:
 296         xfrm6_state_fini();
 297 out_policy:
 298         xfrm6_policy_fini();
 299         goto out;
 300 }
 301 
 302 void xfrm6_fini(void)
 303 {
 304         unregister_pernet_subsys(&xfrm6_net_ops);
 305         xfrm6_protocol_fini();
 306         xfrm6_policy_fini();
 307         xfrm6_state_fini();
 308 }

/* [<][>][^][v][top][bottom][index][help] */