Branch data Line data Source code
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 : 0 : 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 : 0 : memset(&fl6, 0, sizeof(fl6)); 36 : 0 : fl6.flowi6_oif = l3mdev_master_ifindex_by_index(net, oif); 37 : 0 : fl6.flowi6_flags = FLOWI_FLAG_SKIP_NH_OIF; 38 : 0 : fl6.flowi6_mark = mark; 39 : 0 : memcpy(&fl6.daddr, daddr, sizeof(fl6.daddr)); 40 : 0 : if (saddr) 41 : 0 : memcpy(&fl6.saddr, saddr, sizeof(fl6.saddr)); 42 : : 43 : : dst = ip6_route_output(net, NULL, &fl6); 44 : : 45 : 0 : err = dst->error; 46 : 0 : if (dst->error) { 47 : 0 : dst_release(dst); 48 : : dst = ERR_PTR(err); 49 : : } 50 : : 51 : 0 : return dst; 52 : : } 53 : : 54 : 0 : 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 : 0 : dst = xfrm6_dst_lookup(net, 0, oif, NULL, daddr, mark); 62 : 0 : if (IS_ERR(dst)) 63 : : return -EHOSTUNREACH; 64 : : 65 : 0 : dev = ip6_dst_idev(dst)->dev; 66 : 0 : ipv6_dev_get_saddr(dev_net(dev), dev, &daddr->in6, 0, &saddr->in6); 67 : 0 : dst_release(dst); 68 : 0 : return 0; 69 : : } 70 : : 71 : 0 : static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, 72 : : const struct flowi *fl) 73 : : { 74 : 0 : struct rt6_info *rt = (struct rt6_info *)xdst->route; 75 : : 76 : 0 : xdst->u.dst.dev = dev; 77 : 0 : dev_hold(dev); 78 : : 79 : 0 : xdst->u.rt6.rt6i_idev = in6_dev_get(dev); 80 : 0 : if (!xdst->u.rt6.rt6i_idev) { 81 : 0 : dev_put(dev); 82 : 0 : return -ENODEV; 83 : : } 84 : : 85 : : /* Sheit... I remember I did this right. Apparently, 86 : : * it was magically lost, so this code needs audit */ 87 : 0 : xdst->u.rt6.rt6i_flags = rt->rt6i_flags & (RTF_ANYCAST | 88 : : RTF_LOCAL); 89 : 0 : xdst->route_cookie = rt6_get_cookie(rt); 90 : 0 : xdst->u.rt6.rt6i_gateway = rt->rt6i_gateway; 91 : 0 : xdst->u.rt6.rt6i_dst = rt->rt6i_dst; 92 : 0 : xdst->u.rt6.rt6i_src = rt->rt6i_src; 93 : 0 : INIT_LIST_HEAD(&xdst->u.rt6.rt6i_uncached); 94 : 0 : rt6_uncached_list_add(&xdst->u.rt6); 95 : 0 : atomic_inc(&dev_net(dev)->ipv6.rt6_stats->fib_rt_uncache); 96 : : 97 : 0 : return 0; 98 : : } 99 : : 100 : 0 : 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 : 0 : struct dst_entry *path = xdst->route; 106 : : 107 : 0 : path->ops->update_pmtu(path, sk, skb, mtu, confirm_neigh); 108 : 0 : } 109 : : 110 : 0 : 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 : 0 : struct dst_entry *path = xdst->route; 115 : : 116 : 0 : path->ops->redirect(path, sk, skb); 117 : 0 : } 118 : : 119 : 0 : static void xfrm6_dst_destroy(struct dst_entry *dst) 120 : : { 121 : : struct xfrm_dst *xdst = (struct xfrm_dst *)dst; 122 : : 123 : 0 : if (likely(xdst->u.rt6.rt6i_idev)) 124 : 0 : in6_dev_put(xdst->u.rt6.rt6i_idev); 125 : : dst_destroy_metrics_generic(dst); 126 : 0 : if (xdst->u.rt6.rt6i_uncached_list) 127 : 0 : rt6_uncached_list_del(&xdst->u.rt6); 128 : 0 : xfrm_dst_destroy(xdst); 129 : 0 : } 130 : : 131 : 0 : static void xfrm6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, 132 : : int unregister) 133 : : { 134 : : struct xfrm_dst *xdst; 135 : : 136 : 0 : if (!unregister) 137 : 0 : return; 138 : : 139 : : xdst = (struct xfrm_dst *)dst; 140 : 0 : if (xdst->u.rt6.rt6i_idev->dev == dev) { 141 : : struct inet6_dev *loopback_idev = 142 : 0 : in6_dev_get(dev_net(dev)->loopback_dev); 143 : : 144 : : do { 145 : 0 : in6_dev_put(xdst->u.rt6.rt6i_idev); 146 : 0 : 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 : 0 : } while (xdst->u.dst.xfrm); 150 : : 151 : : __in6_dev_put(loopback_idev); 152 : : } 153 : : 154 : 0 : 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 : 3 : static int __init xfrm6_policy_init(void) 177 : : { 178 : 3 : return xfrm_policy_register_afinfo(&xfrm6_policy_afinfo, AF_INET6); 179 : : } 180 : : 181 : : static void xfrm6_policy_fini(void) 182 : : { 183 : 0 : 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 : 3 : 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 : 3 : if (!net_eq(net, &init_net)) { 205 : 1 : table = kmemdup(table, sizeof(xfrm6_policy_table), GFP_KERNEL); 206 : 1 : if (!table) 207 : : goto err_alloc; 208 : : 209 : 1 : table[0].data = &net->xfrm.xfrm6_dst_ops.gc_thresh; 210 : : } 211 : : 212 : 3 : hdr = register_net_sysctl(net, "net/ipv6", table); 213 : 3 : if (!hdr) 214 : : goto err_reg; 215 : : 216 : 3 : net->ipv6.sysctl.xfrm6_hdr = hdr; 217 : 3 : return 0; 218 : : 219 : : err_reg: 220 : 0 : if (!net_eq(net, &init_net)) 221 : 0 : kfree(table); 222 : : err_alloc: 223 : : return -ENOMEM; 224 : : } 225 : : 226 : 1 : static void __net_exit xfrm6_net_sysctl_exit(struct net *net) 227 : : { 228 : : struct ctl_table *table; 229 : : 230 : 1 : if (!net->ipv6.sysctl.xfrm6_hdr) 231 : 1 : return; 232 : : 233 : 1 : table = net->ipv6.sysctl.xfrm6_hdr->ctl_table_arg; 234 : 1 : unregister_net_sysctl_table(net->ipv6.sysctl.xfrm6_hdr); 235 : 1 : if (!net_eq(net, &init_net)) 236 : 1 : 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 : 3 : static int __net_init xfrm6_net_init(struct net *net) 250 : : { 251 : : int ret; 252 : : 253 : 3 : 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 : 3 : if (ret) 257 : : return ret; 258 : : 259 : 3 : ret = xfrm6_net_sysctl_init(net); 260 : 3 : if (ret) 261 : : dst_entries_destroy(&net->xfrm.xfrm6_dst_ops); 262 : : 263 : 3 : return ret; 264 : : } 265 : : 266 : 1 : static void __net_exit xfrm6_net_exit(struct net *net) 267 : : { 268 : 1 : xfrm6_net_sysctl_exit(net); 269 : : dst_entries_destroy(&net->xfrm.xfrm6_dst_ops); 270 : 1 : } 271 : : 272 : : static struct pernet_operations xfrm6_net_ops = { 273 : : .init = xfrm6_net_init, 274 : : .exit = xfrm6_net_exit, 275 : : }; 276 : : 277 : 3 : int __init xfrm6_init(void) 278 : : { 279 : : int ret; 280 : : 281 : 3 : ret = xfrm6_policy_init(); 282 : 3 : if (ret) 283 : : goto out; 284 : 3 : ret = xfrm6_state_init(); 285 : 3 : if (ret) 286 : : goto out_policy; 287 : : 288 : 3 : ret = xfrm6_protocol_init(); 289 : 3 : if (ret) 290 : : goto out_state; 291 : : 292 : 3 : register_pernet_subsys(&xfrm6_net_ops); 293 : : out: 294 : 3 : return ret; 295 : : out_state: 296 : 0 : xfrm6_state_fini(); 297 : : out_policy: 298 : : xfrm6_policy_fini(); 299 : : goto out; 300 : : } 301 : : 302 : 0 : void xfrm6_fini(void) 303 : : { 304 : 0 : unregister_pernet_subsys(&xfrm6_net_ops); 305 : 0 : xfrm6_protocol_fini(); 306 : : xfrm6_policy_fini(); 307 : 0 : xfrm6_state_fini(); 308 : 0 : }