18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * xfrm6_policy.c: based on xfrm4_policy.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: 68c2ecf20Sopenharmony_ci * Mitsuru KANDA @USAGI 78c2ecf20Sopenharmony_ci * Kazunori MIYAZAWA @USAGI 88c2ecf20Sopenharmony_ci * Kunihiro Ishiguro <kunihiro@ipinfusion.com> 98c2ecf20Sopenharmony_ci * IPv6 support 108c2ecf20Sopenharmony_ci * YOSHIFUJI Hideaki 118c2ecf20Sopenharmony_ci * Split up af-specific portion 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/err.h> 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 188c2ecf20Sopenharmony_ci#include <net/addrconf.h> 198c2ecf20Sopenharmony_ci#include <net/dst.h> 208c2ecf20Sopenharmony_ci#include <net/xfrm.h> 218c2ecf20Sopenharmony_ci#include <net/ip.h> 228c2ecf20Sopenharmony_ci#include <net/ipv6.h> 238c2ecf20Sopenharmony_ci#include <net/ip6_route.h> 248c2ecf20Sopenharmony_ci#include <net/l3mdev.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, int oif, 278c2ecf20Sopenharmony_ci const xfrm_address_t *saddr, 288c2ecf20Sopenharmony_ci const xfrm_address_t *daddr, 298c2ecf20Sopenharmony_ci u32 mark) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct flowi6 fl6; 328c2ecf20Sopenharmony_ci struct dst_entry *dst; 338c2ecf20Sopenharmony_ci int err; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci memset(&fl6, 0, sizeof(fl6)); 368c2ecf20Sopenharmony_ci fl6.flowi6_oif = l3mdev_master_ifindex_by_index(net, oif); 378c2ecf20Sopenharmony_ci fl6.flowi6_flags = FLOWI_FLAG_SKIP_NH_OIF; 388c2ecf20Sopenharmony_ci fl6.flowi6_mark = mark; 398c2ecf20Sopenharmony_ci memcpy(&fl6.daddr, daddr, sizeof(fl6.daddr)); 408c2ecf20Sopenharmony_ci if (saddr) 418c2ecf20Sopenharmony_ci memcpy(&fl6.saddr, saddr, sizeof(fl6.saddr)); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci dst = ip6_route_output(net, NULL, &fl6); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci err = dst->error; 468c2ecf20Sopenharmony_ci if (dst->error) { 478c2ecf20Sopenharmony_ci dst_release(dst); 488c2ecf20Sopenharmony_ci dst = ERR_PTR(err); 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci return dst; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic int xfrm6_get_saddr(struct net *net, int oif, 558c2ecf20Sopenharmony_ci xfrm_address_t *saddr, xfrm_address_t *daddr, 568c2ecf20Sopenharmony_ci u32 mark) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct dst_entry *dst; 598c2ecf20Sopenharmony_ci struct net_device *dev; 608c2ecf20Sopenharmony_ci struct inet6_dev *idev; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci dst = xfrm6_dst_lookup(net, 0, oif, NULL, daddr, mark); 638c2ecf20Sopenharmony_ci if (IS_ERR(dst)) 648c2ecf20Sopenharmony_ci return -EHOSTUNREACH; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci idev = ip6_dst_idev(dst); 678c2ecf20Sopenharmony_ci if (!idev) { 688c2ecf20Sopenharmony_ci dst_release(dst); 698c2ecf20Sopenharmony_ci return -EHOSTUNREACH; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci dev = idev->dev; 728c2ecf20Sopenharmony_ci ipv6_dev_get_saddr(dev_net(dev), dev, &daddr->in6, 0, &saddr->in6); 738c2ecf20Sopenharmony_ci dst_release(dst); 748c2ecf20Sopenharmony_ci return 0; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, 788c2ecf20Sopenharmony_ci const struct flowi *fl) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct rt6_info *rt = (struct rt6_info *)xdst->route; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci xdst->u.dst.dev = dev; 838c2ecf20Sopenharmony_ci dev_hold(dev); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci xdst->u.rt6.rt6i_idev = in6_dev_get(dev); 868c2ecf20Sopenharmony_ci if (!xdst->u.rt6.rt6i_idev) { 878c2ecf20Sopenharmony_ci dev_put(dev); 888c2ecf20Sopenharmony_ci return -ENODEV; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci /* Sheit... I remember I did this right. Apparently, 928c2ecf20Sopenharmony_ci * it was magically lost, so this code needs audit */ 938c2ecf20Sopenharmony_ci xdst->u.rt6.rt6i_flags = rt->rt6i_flags & (RTF_ANYCAST | 948c2ecf20Sopenharmony_ci RTF_LOCAL); 958c2ecf20Sopenharmony_ci xdst->route_cookie = rt6_get_cookie(rt); 968c2ecf20Sopenharmony_ci xdst->u.rt6.rt6i_gateway = rt->rt6i_gateway; 978c2ecf20Sopenharmony_ci xdst->u.rt6.rt6i_dst = rt->rt6i_dst; 988c2ecf20Sopenharmony_ci xdst->u.rt6.rt6i_src = rt->rt6i_src; 998c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&xdst->u.rt6.rt6i_uncached); 1008c2ecf20Sopenharmony_ci rt6_uncached_list_add(&xdst->u.rt6); 1018c2ecf20Sopenharmony_ci atomic_inc(&dev_net(dev)->ipv6.rt6_stats->fib_rt_uncache); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return 0; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic void xfrm6_update_pmtu(struct dst_entry *dst, struct sock *sk, 1078c2ecf20Sopenharmony_ci struct sk_buff *skb, u32 mtu, 1088c2ecf20Sopenharmony_ci bool confirm_neigh) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct xfrm_dst *xdst = (struct xfrm_dst *)dst; 1118c2ecf20Sopenharmony_ci struct dst_entry *path = xdst->route; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci path->ops->update_pmtu(path, sk, skb, mtu, confirm_neigh); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic void xfrm6_redirect(struct dst_entry *dst, struct sock *sk, 1178c2ecf20Sopenharmony_ci struct sk_buff *skb) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct xfrm_dst *xdst = (struct xfrm_dst *)dst; 1208c2ecf20Sopenharmony_ci struct dst_entry *path = xdst->route; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci path->ops->redirect(path, sk, skb); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic void xfrm6_dst_destroy(struct dst_entry *dst) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct xfrm_dst *xdst = (struct xfrm_dst *)dst; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci dst_destroy_metrics_generic(dst); 1308c2ecf20Sopenharmony_ci if (xdst->u.rt6.rt6i_uncached_list) 1318c2ecf20Sopenharmony_ci rt6_uncached_list_del(&xdst->u.rt6); 1328c2ecf20Sopenharmony_ci if (likely(xdst->u.rt6.rt6i_idev)) 1338c2ecf20Sopenharmony_ci in6_dev_put(xdst->u.rt6.rt6i_idev); 1348c2ecf20Sopenharmony_ci xfrm_dst_destroy(xdst); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic void xfrm6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, 1388c2ecf20Sopenharmony_ci int unregister) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci struct xfrm_dst *xdst; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (!unregister) 1438c2ecf20Sopenharmony_ci return; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci xdst = (struct xfrm_dst *)dst; 1468c2ecf20Sopenharmony_ci if (xdst->u.rt6.rt6i_idev->dev == dev) { 1478c2ecf20Sopenharmony_ci struct inet6_dev *loopback_idev = 1488c2ecf20Sopenharmony_ci in6_dev_get(dev_net(dev)->loopback_dev); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci do { 1518c2ecf20Sopenharmony_ci in6_dev_put(xdst->u.rt6.rt6i_idev); 1528c2ecf20Sopenharmony_ci xdst->u.rt6.rt6i_idev = loopback_idev; 1538c2ecf20Sopenharmony_ci in6_dev_hold(loopback_idev); 1548c2ecf20Sopenharmony_ci xdst = (struct xfrm_dst *)xfrm_dst_child(&xdst->u.dst); 1558c2ecf20Sopenharmony_ci } while (xdst->u.dst.xfrm); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci __in6_dev_put(loopback_idev); 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci xfrm_dst_ifdown(dst, dev); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic struct dst_ops xfrm6_dst_ops_template = { 1648c2ecf20Sopenharmony_ci .family = AF_INET6, 1658c2ecf20Sopenharmony_ci .update_pmtu = xfrm6_update_pmtu, 1668c2ecf20Sopenharmony_ci .redirect = xfrm6_redirect, 1678c2ecf20Sopenharmony_ci .cow_metrics = dst_cow_metrics_generic, 1688c2ecf20Sopenharmony_ci .destroy = xfrm6_dst_destroy, 1698c2ecf20Sopenharmony_ci .ifdown = xfrm6_dst_ifdown, 1708c2ecf20Sopenharmony_ci .local_out = __ip6_local_out, 1718c2ecf20Sopenharmony_ci .gc_thresh = 32768, 1728c2ecf20Sopenharmony_ci}; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic const struct xfrm_policy_afinfo xfrm6_policy_afinfo = { 1758c2ecf20Sopenharmony_ci .dst_ops = &xfrm6_dst_ops_template, 1768c2ecf20Sopenharmony_ci .dst_lookup = xfrm6_dst_lookup, 1778c2ecf20Sopenharmony_ci .get_saddr = xfrm6_get_saddr, 1788c2ecf20Sopenharmony_ci .fill_dst = xfrm6_fill_dst, 1798c2ecf20Sopenharmony_ci .blackhole_route = ip6_blackhole_route, 1808c2ecf20Sopenharmony_ci}; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic int __init xfrm6_policy_init(void) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci return xfrm_policy_register_afinfo(&xfrm6_policy_afinfo, AF_INET6); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic void xfrm6_policy_fini(void) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci xfrm_policy_unregister_afinfo(&xfrm6_policy_afinfo); 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL 1938c2ecf20Sopenharmony_cistatic struct ctl_table xfrm6_policy_table[] = { 1948c2ecf20Sopenharmony_ci { 1958c2ecf20Sopenharmony_ci .procname = "xfrm6_gc_thresh", 1968c2ecf20Sopenharmony_ci .data = &init_net.xfrm.xfrm6_dst_ops.gc_thresh, 1978c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 1988c2ecf20Sopenharmony_ci .mode = 0644, 1998c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 2008c2ecf20Sopenharmony_ci }, 2018c2ecf20Sopenharmony_ci { } 2028c2ecf20Sopenharmony_ci}; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int __net_init xfrm6_net_sysctl_init(struct net *net) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci struct ctl_table *table; 2078c2ecf20Sopenharmony_ci struct ctl_table_header *hdr; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci table = xfrm6_policy_table; 2108c2ecf20Sopenharmony_ci if (!net_eq(net, &init_net)) { 2118c2ecf20Sopenharmony_ci table = kmemdup(table, sizeof(xfrm6_policy_table), GFP_KERNEL); 2128c2ecf20Sopenharmony_ci if (!table) 2138c2ecf20Sopenharmony_ci goto err_alloc; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci table[0].data = &net->xfrm.xfrm6_dst_ops.gc_thresh; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci hdr = register_net_sysctl(net, "net/ipv6", table); 2198c2ecf20Sopenharmony_ci if (!hdr) 2208c2ecf20Sopenharmony_ci goto err_reg; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci net->ipv6.sysctl.xfrm6_hdr = hdr; 2238c2ecf20Sopenharmony_ci return 0; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cierr_reg: 2268c2ecf20Sopenharmony_ci if (!net_eq(net, &init_net)) 2278c2ecf20Sopenharmony_ci kfree(table); 2288c2ecf20Sopenharmony_cierr_alloc: 2298c2ecf20Sopenharmony_ci return -ENOMEM; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic void __net_exit xfrm6_net_sysctl_exit(struct net *net) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct ctl_table *table; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (!net->ipv6.sysctl.xfrm6_hdr) 2378c2ecf20Sopenharmony_ci return; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci table = net->ipv6.sysctl.xfrm6_hdr->ctl_table_arg; 2408c2ecf20Sopenharmony_ci unregister_net_sysctl_table(net->ipv6.sysctl.xfrm6_hdr); 2418c2ecf20Sopenharmony_ci if (!net_eq(net, &init_net)) 2428c2ecf20Sopenharmony_ci kfree(table); 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci#else /* CONFIG_SYSCTL */ 2458c2ecf20Sopenharmony_cistatic inline int xfrm6_net_sysctl_init(struct net *net) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci return 0; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic inline void xfrm6_net_sysctl_exit(struct net *net) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci#endif 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic int __net_init xfrm6_net_init(struct net *net) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci int ret; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci memcpy(&net->xfrm.xfrm6_dst_ops, &xfrm6_dst_ops_template, 2608c2ecf20Sopenharmony_ci sizeof(xfrm6_dst_ops_template)); 2618c2ecf20Sopenharmony_ci ret = dst_entries_init(&net->xfrm.xfrm6_dst_ops); 2628c2ecf20Sopenharmony_ci if (ret) 2638c2ecf20Sopenharmony_ci return ret; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci ret = xfrm6_net_sysctl_init(net); 2668c2ecf20Sopenharmony_ci if (ret) 2678c2ecf20Sopenharmony_ci dst_entries_destroy(&net->xfrm.xfrm6_dst_ops); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci return ret; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic void __net_exit xfrm6_net_exit(struct net *net) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci xfrm6_net_sysctl_exit(net); 2758c2ecf20Sopenharmony_ci dst_entries_destroy(&net->xfrm.xfrm6_dst_ops); 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic struct pernet_operations xfrm6_net_ops = { 2798c2ecf20Sopenharmony_ci .init = xfrm6_net_init, 2808c2ecf20Sopenharmony_ci .exit = xfrm6_net_exit, 2818c2ecf20Sopenharmony_ci}; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ciint __init xfrm6_init(void) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci int ret; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci ret = xfrm6_policy_init(); 2888c2ecf20Sopenharmony_ci if (ret) 2898c2ecf20Sopenharmony_ci goto out; 2908c2ecf20Sopenharmony_ci ret = xfrm6_state_init(); 2918c2ecf20Sopenharmony_ci if (ret) 2928c2ecf20Sopenharmony_ci goto out_policy; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci ret = xfrm6_protocol_init(); 2958c2ecf20Sopenharmony_ci if (ret) 2968c2ecf20Sopenharmony_ci goto out_state; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci ret = register_pernet_subsys(&xfrm6_net_ops); 2998c2ecf20Sopenharmony_ci if (ret) 3008c2ecf20Sopenharmony_ci goto out_protocol; 3018c2ecf20Sopenharmony_ciout: 3028c2ecf20Sopenharmony_ci return ret; 3038c2ecf20Sopenharmony_ciout_protocol: 3048c2ecf20Sopenharmony_ci xfrm6_protocol_fini(); 3058c2ecf20Sopenharmony_ciout_state: 3068c2ecf20Sopenharmony_ci xfrm6_state_fini(); 3078c2ecf20Sopenharmony_ciout_policy: 3088c2ecf20Sopenharmony_ci xfrm6_policy_fini(); 3098c2ecf20Sopenharmony_ci goto out; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_civoid xfrm6_fini(void) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci unregister_pernet_subsys(&xfrm6_net_ops); 3158c2ecf20Sopenharmony_ci xfrm6_protocol_fini(); 3168c2ecf20Sopenharmony_ci xfrm6_policy_fini(); 3178c2ecf20Sopenharmony_ci xfrm6_state_fini(); 3188c2ecf20Sopenharmony_ci} 319