FILE
xfrm6_policy.c
File preview with syntax highlighting.
linux-6.19.14/net/ipv6/xfrm6_policy.c
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(const struct xfrm_dst_lookup_params *params) 27{ 28 struct flowi6 fl6; 29 struct dst_entry *dst; 30 int err; 31 32 memset(&fl6, 0, sizeof(fl6)); 33 fl6.flowi6_l3mdev = l3mdev_master_ifindex_by_index(params->net, 34 params->oif); 35 fl6.flowi6_mark = params->mark; 36 memcpy(&fl6.daddr, params->daddr, sizeof(fl6.daddr)); 37 if (params->saddr) 38 memcpy(&fl6.saddr, params->saddr, sizeof(fl6.saddr)); 39 40 fl6.flowi4_proto = params->ipproto; 41 fl6.uli = params->uli; 42 43 dst = ip6_route_output(params->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(xfrm_address_t *saddr, 55 const struct xfrm_dst_lookup_params *params) 56{ 57 struct dst_entry *dst; 58 struct net_device *dev; 59 struct inet6_dev *idev; 60 int err; 61 62 dst = xfrm6_dst_lookup(params); 63 if (IS_ERR(dst)) 64 return -EHOSTUNREACH; 65 66 idev = ip6_dst_idev(dst); 67 if (!idev) { 68 dst_release(dst); 69 return -EHOSTUNREACH; 70 } 71 dev = idev->dev; 72 err = ipv6_dev_get_saddr(dev_net(dev), dev, ¶ms->daddr->in6, 0, 73 &saddr->in6); 74 dst_release(dst); 75 if (err) 76 return -EHOSTUNREACH; 77 return 0; 78} 79 80static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, 81 const struct flowi *fl) 82{ 83 struct rt6_info *rt = dst_rt6_info(xdst->route); 84 85 xdst->u.dst.dev = dev; 86 netdev_hold(dev, &xdst->u.dst.dev_tracker, GFP_ATOMIC); 87 88 xdst->u.rt6.rt6i_idev = in6_dev_get(dev); 89 if (!xdst->u.rt6.rt6i_idev) { 90 netdev_put(dev, &xdst->u.dst.dev_tracker); 91 return -ENODEV; 92 } 93 94 /* Sheit... I remember I did this right. Apparently, 95 * it was magically lost, so this code needs audit */ 96 xdst->u.rt6.rt6i_flags = rt->rt6i_flags & (RTF_ANYCAST | 97 RTF_LOCAL); 98 xdst->route_cookie = rt6_get_cookie(rt); 99 xdst->u.rt6.rt6i_gateway = rt->rt6i_gateway; 100 xdst->u.rt6.rt6i_dst = rt->rt6i_dst; 101 xdst->u.rt6.rt6i_src = rt->rt6i_src; 102 rt6_uncached_list_add(&xdst->u.rt6); 103 104 return 0; 105} 106 107static void xfrm6_update_pmtu(struct dst_entry *dst, struct sock *sk, 108 struct sk_buff *skb, u32 mtu, 109 bool confirm_neigh) 110{ 111 struct xfrm_dst *xdst = (struct xfrm_dst *)dst; 112 struct dst_entry *path = xdst->route; 113 114 path->ops->update_pmtu(path, sk, skb, mtu, confirm_neigh); 115} 116 117static void xfrm6_redirect(struct dst_entry *dst, struct sock *sk, 118 struct sk_buff *skb) 119{ 120 struct xfrm_dst *xdst = (struct xfrm_dst *)dst; 121 struct dst_entry *path = xdst->route; 122 123 path->ops->redirect(path, sk, skb); 124} 125 126static void xfrm6_dst_destroy(struct dst_entry *dst) 127{ 128 struct xfrm_dst *xdst = (struct xfrm_dst *)dst; 129 130 dst_destroy_metrics_generic(dst); 131 rt6_uncached_list_del(&xdst->u.rt6); 132 if (likely(xdst->u.rt6.rt6i_idev)) 133 in6_dev_put(xdst->u.rt6.rt6i_idev); 134 xfrm_dst_destroy(xdst); 135} 136 137static void xfrm6_dst_ifdown(struct dst_entry *dst, struct net_device *dev) 138{ 139 struct xfrm_dst *xdst; 140 141 xdst = (struct xfrm_dst *)dst; 142 if (xdst->u.rt6.rt6i_idev->dev == dev) { 143 struct inet6_dev *loopback_idev = 144 in6_dev_get(dev_net(dev)->loopback_dev); 145 146 do { 147 in6_dev_put(xdst->u.rt6.rt6i_idev); 148 xdst->u.rt6.rt6i_idev = loopback_idev; 149 in6_dev_hold(loopback_idev); 150 xdst = (struct xfrm_dst *)xfrm_dst_child(&xdst->u.dst); 151 } while (xdst->u.dst.xfrm); 152 153 __in6_dev_put(loopback_idev); 154 } 155 156 xfrm_dst_ifdown(dst, dev); 157} 158 159static struct dst_ops xfrm6_dst_ops_template = { 160 .family = AF_INET6, 161 .update_pmtu = xfrm6_update_pmtu, 162 .redirect = xfrm6_redirect, 163 .cow_metrics = dst_cow_metrics_generic, 164 .destroy = xfrm6_dst_destroy, 165 .ifdown = xfrm6_dst_ifdown, 166 .local_out = __ip6_local_out, 167 .gc_thresh = 32768, 168}; 169 170static const struct xfrm_policy_afinfo xfrm6_policy_afinfo = { 171 .dst_ops = &xfrm6_dst_ops_template, 172 .dst_lookup = xfrm6_dst_lookup, 173 .get_saddr = xfrm6_get_saddr, 174 .fill_dst = xfrm6_fill_dst, 175 .blackhole_route = ip6_blackhole_route, 176}; 177 178static int __init xfrm6_policy_init(void) 179{ 180 return xfrm_policy_register_afinfo(&xfrm6_policy_afinfo, AF_INET6); 181} 182 183static void xfrm6_policy_fini(void) 184{ 185 xfrm_policy_unregister_afinfo(&xfrm6_policy_afinfo); 186} 187 188#ifdef CONFIG_SYSCTL 189static struct ctl_table xfrm6_policy_table[] = { 190 { 191 .procname = "xfrm6_gc_thresh", 192 .data = &init_net.xfrm.xfrm6_dst_ops.gc_thresh, 193 .maxlen = sizeof(int), 194 .mode = 0644, 195 .proc_handler = proc_dointvec, 196 }, 197}; 198 199static int __net_init xfrm6_net_sysctl_init(struct net *net) 200{ 201 struct ctl_table *table; 202 struct ctl_table_header *hdr; 203 204 table = xfrm6_policy_table; 205 if (!net_eq(net, &init_net)) { 206 table = kmemdup(table, sizeof(xfrm6_policy_table), GFP_KERNEL); 207 if (!table) 208 goto err_alloc; 209 210 table[0].data = &net->xfrm.xfrm6_dst_ops.gc_thresh; 211 } 212 213 hdr = register_net_sysctl_sz(net, "net/ipv6", table, 214 ARRAY_SIZE(xfrm6_policy_table)); 215 if (!hdr) 216 goto err_reg; 217 218 net->ipv6.sysctl.xfrm6_hdr = hdr; 219 return 0; 220 221err_reg: 222 if (!net_eq(net, &init_net)) 223 kfree(table); 224err_alloc: 225 return -ENOMEM; 226} 227 228static void __net_exit xfrm6_net_sysctl_exit(struct net *net) 229{ 230 const struct ctl_table *table; 231 232 if (!net->ipv6.sysctl.xfrm6_hdr) 233 return; 234 235 table = net->ipv6.sysctl.xfrm6_hdr->ctl_table_arg; 236 unregister_net_sysctl_table(net->ipv6.sysctl.xfrm6_hdr); 237 if (!net_eq(net, &init_net)) 238 kfree(table); 239} 240#else /* CONFIG_SYSCTL */ 241static inline int xfrm6_net_sysctl_init(struct net *net) 242{ 243 return 0; 244} 245 246static inline void xfrm6_net_sysctl_exit(struct net *net) 247{ 248} 249#endif 250 251static int __net_init xfrm6_net_init(struct net *net) 252{ 253 int ret; 254 255 memcpy(&net->xfrm.xfrm6_dst_ops, &xfrm6_dst_ops_template, 256 sizeof(xfrm6_dst_ops_template)); 257 ret = dst_entries_init(&net->xfrm.xfrm6_dst_ops); 258 if (ret) 259 return ret; 260 261 ret = xfrm6_net_sysctl_init(net); 262 if (ret) 263 dst_entries_destroy(&net->xfrm.xfrm6_dst_ops); 264 265 return ret; 266} 267 268static void __net_exit xfrm6_net_exit(struct net *net) 269{ 270 xfrm6_net_sysctl_exit(net); 271 dst_entries_destroy(&net->xfrm.xfrm6_dst_ops); 272} 273 274static struct pernet_operations xfrm6_net_ops = { 275 .init = xfrm6_net_init, 276 .exit = xfrm6_net_exit, 277}; 278 279int __init xfrm6_init(void) 280{ 281 int ret; 282 283 ret = xfrm6_policy_init(); 284 if (ret) 285 goto out; 286 ret = xfrm6_state_init(); 287 if (ret) 288 goto out_policy; 289 290 ret = xfrm6_protocol_init(); 291 if (ret) 292 goto out_state; 293 294 ret = register_pernet_subsys(&xfrm6_net_ops); 295 if (ret) 296 goto out_protocol; 297 298 ret = xfrm_nat_keepalive_init(AF_INET6); 299 if (ret) 300 goto out_nat_keepalive; 301out: 302 return ret; 303out_nat_keepalive: 304 unregister_pernet_subsys(&xfrm6_net_ops); 305out_protocol: 306 xfrm6_protocol_fini(); 307out_state: 308 xfrm6_state_fini(); 309out_policy: 310 xfrm6_policy_fini(); 311 goto out; 312} 313 314void xfrm6_fini(void) 315{ 316 xfrm_nat_keepalive_fini(AF_INET6); 317 unregister_pernet_subsys(&xfrm6_net_ops); 318 xfrm6_protocol_fini(); 319 xfrm6_policy_fini(); 320 xfrm6_state_fini(); 321}
~/
Read-only file preview.