1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * xfrm4_policy.c 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Changes: 61da177e4SLinus Torvalds * Kazunori MIYAZAWA @USAGI 71da177e4SLinus Torvalds * YOSHIFUJI Hideaki @USAGI 81da177e4SLinus Torvalds * Split up af-specific portion 91da177e4SLinus Torvalds * 101da177e4SLinus Torvalds */ 111da177e4SLinus Torvalds 1266cdb3caSHerbert Xu #include <linux/err.h> 1366cdb3caSHerbert Xu #include <linux/kernel.h> 14aabc9761SHerbert Xu #include <linux/inetdevice.h> 1545ff5a3fSHerbert Xu #include <net/dst.h> 161da177e4SLinus Torvalds #include <net/xfrm.h> 171da177e4SLinus Torvalds #include <net/ip.h> 18385add90SDavid Ahern #include <net/l3mdev.h> 191da177e4SLinus Torvalds 208f01cb08SDavid S. Miller static struct dst_entry *__xfrm4_dst_lookup(struct net *net, struct flowi4 *fl4, 2142a7b32bSDavid Ahern int tos, int oif, 225e6b930fSDavid S. Miller const xfrm_address_t *saddr, 23077fbac4SLorenzo Colitti const xfrm_address_t *daddr, 24077fbac4SLorenzo Colitti u32 mark) 251da177e4SLinus Torvalds { 2666cdb3caSHerbert Xu struct rtable *rt; 27a1e59abfSPatrick McHardy 288f01cb08SDavid S. Miller memset(fl4, 0, sizeof(*fl4)); 298f01cb08SDavid S. Miller fl4->daddr = daddr->a4; 308f01cb08SDavid S. Miller fl4->flowi4_tos = tos; 3140867d74SDavid Ahern fl4->flowi4_l3mdev = l3mdev_master_ifindex_by_index(net, oif); 32077fbac4SLorenzo Colitti fl4->flowi4_mark = mark; 3366cdb3caSHerbert Xu if (saddr) 348f01cb08SDavid S. Miller fl4->saddr = saddr->a4; 3566cdb3caSHerbert Xu 368f01cb08SDavid S. Miller rt = __ip_route_output_key(net, fl4); 37b23dd4feSDavid S. Miller if (!IS_ERR(rt)) 38b23dd4feSDavid S. Miller return &rt->dst; 39b23dd4feSDavid S. Miller 40b23dd4feSDavid S. Miller return ERR_CAST(rt); 41a1e59abfSPatrick McHardy } 4266cdb3caSHerbert Xu 4342a7b32bSDavid Ahern static struct dst_entry *xfrm4_dst_lookup(struct net *net, int tos, int oif, 448f01cb08SDavid S. Miller const xfrm_address_t *saddr, 45077fbac4SLorenzo Colitti const xfrm_address_t *daddr, 46077fbac4SLorenzo Colitti u32 mark) 478f01cb08SDavid S. Miller { 488f01cb08SDavid S. Miller struct flowi4 fl4; 498f01cb08SDavid S. Miller 50077fbac4SLorenzo Colitti return __xfrm4_dst_lookup(net, &fl4, tos, oif, saddr, daddr, mark); 518f01cb08SDavid S. Miller } 528f01cb08SDavid S. Miller 5342a7b32bSDavid Ahern static int xfrm4_get_saddr(struct net *net, int oif, 54077fbac4SLorenzo Colitti xfrm_address_t *saddr, xfrm_address_t *daddr, 55077fbac4SLorenzo Colitti u32 mark) 5666cdb3caSHerbert Xu { 5766cdb3caSHerbert Xu struct dst_entry *dst; 588f01cb08SDavid S. Miller struct flowi4 fl4; 5966cdb3caSHerbert Xu 60077fbac4SLorenzo Colitti dst = __xfrm4_dst_lookup(net, &fl4, 0, oif, NULL, daddr, mark); 6166cdb3caSHerbert Xu if (IS_ERR(dst)) 62a1e59abfSPatrick McHardy return -EHOSTUNREACH; 6366cdb3caSHerbert Xu 648f01cb08SDavid S. Miller saddr->a4 = fl4.saddr; 6566cdb3caSHerbert Xu dst_release(dst); 6666cdb3caSHerbert Xu return 0; 67a1e59abfSPatrick McHardy } 68a1e59abfSPatrick McHardy 6987c1e12bSHerbert Xu static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, 700c7b3eefSDavid S. Miller const struct flowi *fl) 7125ee3286SHerbert Xu { 72*05d6d492SEric Dumazet struct rtable *rt = dst_rtable(xdst->route); 737e1dc7b6SDavid S. Miller const struct flowi4 *fl4 = &fl->u.ip4; 741da177e4SLinus Torvalds 75b7323396SYan, Zheng xdst->u.rt.rt_iif = fl4->flowi4_iif; 761da177e4SLinus Torvalds 7725ee3286SHerbert Xu xdst->u.dst.dev = dev; 78d62607c3SJakub Kicinski netdev_hold(dev, &xdst->u.dst.dev_tracker, GFP_ATOMIC); 7943372262SMiika Komu 801da177e4SLinus Torvalds /* Sheit... I remember I did this right. Apparently, 811da177e4SLinus Torvalds * it was magically lost, so this code needs audit */ 829917e1e8SDavid S. Miller xdst->u.rt.rt_is_input = rt->rt_is_input; 8325ee3286SHerbert Xu xdst->u.rt.rt_flags = rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST | 8425ee3286SHerbert Xu RTCF_LOCAL); 8525ee3286SHerbert Xu xdst->u.rt.rt_type = rt->rt_type; 8677d5bc7eSDavid Ahern xdst->u.rt.rt_uses_gateway = rt->rt_uses_gateway; 871550c171SDavid Ahern xdst->u.rt.rt_gw_family = rt->rt_gw_family; 881550c171SDavid Ahern if (rt->rt_gw_family == AF_INET) 891550c171SDavid Ahern xdst->u.rt.rt_gw4 = rt->rt_gw4; 900f5f7d7bSDavid Ahern else if (rt->rt_gw_family == AF_INET6) 910f5f7d7bSDavid Ahern xdst->u.rt.rt_gw6 = rt->rt_gw6; 925943634fSDavid S. Miller xdst->u.rt.rt_pmtu = rt->rt_pmtu; 93d52e5a7eSSabrina Dubroca xdst->u.rt.rt_mtu_locked = rt->rt_mtu_locked; 94510c321bSXin Long rt_add_uncached_list(&xdst->u.rt); 951da177e4SLinus Torvalds 961da177e4SLinus Torvalds return 0; 971da177e4SLinus Torvalds } 981da177e4SLinus Torvalds 996700c270SDavid S. Miller static void xfrm4_update_pmtu(struct dst_entry *dst, struct sock *sk, 100bd085ef6SHangbin Liu struct sk_buff *skb, u32 mtu, 101bd085ef6SHangbin Liu bool confirm_neigh) 1021da177e4SLinus Torvalds { 1031da177e4SLinus Torvalds struct xfrm_dst *xdst = (struct xfrm_dst *)dst; 1041da177e4SLinus Torvalds struct dst_entry *path = xdst->route; 1051da177e4SLinus Torvalds 106bd085ef6SHangbin Liu path->ops->update_pmtu(path, sk, skb, mtu, confirm_neigh); 1071da177e4SLinus Torvalds } 1081da177e4SLinus Torvalds 1096700c270SDavid S. Miller static void xfrm4_redirect(struct dst_entry *dst, struct sock *sk, 1106700c270SDavid S. Miller struct sk_buff *skb) 11155be7a9cSDavid S. Miller { 11255be7a9cSDavid S. Miller struct xfrm_dst *xdst = (struct xfrm_dst *)dst; 11355be7a9cSDavid S. Miller struct dst_entry *path = xdst->route; 11455be7a9cSDavid S. Miller 1156700c270SDavid S. Miller path->ops->redirect(path, sk, skb); 11655be7a9cSDavid S. Miller } 11755be7a9cSDavid S. Miller 118aabc9761SHerbert Xu static void xfrm4_dst_destroy(struct dst_entry *dst) 119aabc9761SHerbert Xu { 120aabc9761SHerbert Xu struct xfrm_dst *xdst = (struct xfrm_dst *)dst; 121aabc9761SHerbert Xu 12262fa8a84SDavid S. Miller dst_destroy_metrics_generic(dst); 123510c321bSXin Long rt_del_uncached_list(&xdst->u.rt); 124aabc9761SHerbert Xu xfrm_dst_destroy(xdst); 125aabc9761SHerbert Xu } 126aabc9761SHerbert Xu 127a8a572a6SDan Streetman static struct dst_ops xfrm4_dst_ops_template = { 1281da177e4SLinus Torvalds .family = AF_INET, 1291da177e4SLinus Torvalds .update_pmtu = xfrm4_update_pmtu, 13055be7a9cSDavid S. Miller .redirect = xfrm4_redirect, 13162fa8a84SDavid S. Miller .cow_metrics = dst_cow_metrics_generic, 132aabc9761SHerbert Xu .destroy = xfrm4_dst_destroy, 13343c28172SZhengchao Shao .ifdown = xfrm_dst_ifdown, 134862b82c6SHerbert Xu .local_out = __ip_local_out, 1353c2a89ddSFlorian Westphal .gc_thresh = 32768, 1361da177e4SLinus Torvalds }; 1371da177e4SLinus Torvalds 13837b10383SFlorian Westphal static const struct xfrm_policy_afinfo xfrm4_policy_afinfo = { 139a8a572a6SDan Streetman .dst_ops = &xfrm4_dst_ops_template, 1401da177e4SLinus Torvalds .dst_lookup = xfrm4_dst_lookup, 141a1e59abfSPatrick McHardy .get_saddr = xfrm4_get_saddr, 14225ee3286SHerbert Xu .fill_dst = xfrm4_fill_dst, 1432774c131SDavid S. Miller .blackhole_route = ipv4_blackhole_route, 1441da177e4SLinus Torvalds }; 1451da177e4SLinus Torvalds 146f816700aSRandy Dunlap #ifdef CONFIG_SYSCTL 147a44a4a00SNeil Horman static struct ctl_table xfrm4_policy_table[] = { 148a44a4a00SNeil Horman { 149a44a4a00SNeil Horman .procname = "xfrm4_gc_thresh", 150d7c7544cSAlexey Dobriyan .data = &init_net.xfrm.xfrm4_dst_ops.gc_thresh, 151a44a4a00SNeil Horman .maxlen = sizeof(int), 152a44a4a00SNeil Horman .mode = 0644, 153a44a4a00SNeil Horman .proc_handler = proc_dointvec, 154a44a4a00SNeil Horman }, 155a44a4a00SNeil Horman }; 156a44a4a00SNeil Horman 157318d3cc0SArnd Bergmann static __net_init int xfrm4_net_sysctl_init(struct net *net) 1588d068875SMichal Kubecek { 1598d068875SMichal Kubecek struct ctl_table *table; 1608d068875SMichal Kubecek struct ctl_table_header *hdr; 1618d068875SMichal Kubecek 1628d068875SMichal Kubecek table = xfrm4_policy_table; 1638d068875SMichal Kubecek if (!net_eq(net, &init_net)) { 1648d068875SMichal Kubecek table = kmemdup(table, sizeof(xfrm4_policy_table), GFP_KERNEL); 1658d068875SMichal Kubecek if (!table) 1668d068875SMichal Kubecek goto err_alloc; 1678d068875SMichal Kubecek 1688d068875SMichal Kubecek table[0].data = &net->xfrm.xfrm4_dst_ops.gc_thresh; 1698d068875SMichal Kubecek } 1708d068875SMichal Kubecek 171c899710fSJoel Granados hdr = register_net_sysctl_sz(net, "net/ipv4", table, 172c899710fSJoel Granados ARRAY_SIZE(xfrm4_policy_table)); 1738d068875SMichal Kubecek if (!hdr) 1748d068875SMichal Kubecek goto err_reg; 1758d068875SMichal Kubecek 1768d068875SMichal Kubecek net->ipv4.xfrm4_hdr = hdr; 1778d068875SMichal Kubecek return 0; 1788d068875SMichal Kubecek 1798d068875SMichal Kubecek err_reg: 1808d068875SMichal Kubecek if (!net_eq(net, &init_net)) 1818d068875SMichal Kubecek kfree(table); 1828d068875SMichal Kubecek err_alloc: 1838d068875SMichal Kubecek return -ENOMEM; 1848d068875SMichal Kubecek } 1858d068875SMichal Kubecek 186318d3cc0SArnd Bergmann static __net_exit void xfrm4_net_sysctl_exit(struct net *net) 1878d068875SMichal Kubecek { 188bfa858f2SThomas Weißschuh const struct ctl_table *table; 1898d068875SMichal Kubecek 19051456b29SIan Morris if (!net->ipv4.xfrm4_hdr) 1918d068875SMichal Kubecek return; 1928d068875SMichal Kubecek 1938d068875SMichal Kubecek table = net->ipv4.xfrm4_hdr->ctl_table_arg; 1948d068875SMichal Kubecek unregister_net_sysctl_table(net->ipv4.xfrm4_hdr); 1958d068875SMichal Kubecek if (!net_eq(net, &init_net)) 1968d068875SMichal Kubecek kfree(table); 1978d068875SMichal Kubecek } 198a8a572a6SDan Streetman #else /* CONFIG_SYSCTL */ 199318d3cc0SArnd Bergmann static inline int xfrm4_net_sysctl_init(struct net *net) 200a8a572a6SDan Streetman { 201a8a572a6SDan Streetman return 0; 202a8a572a6SDan Streetman } 203a8a572a6SDan Streetman 204318d3cc0SArnd Bergmann static inline void xfrm4_net_sysctl_exit(struct net *net) 205a8a572a6SDan Streetman { 206a8a572a6SDan Streetman } 207a8a572a6SDan Streetman #endif 208a8a572a6SDan Streetman 209a8a572a6SDan Streetman static int __net_init xfrm4_net_init(struct net *net) 210a8a572a6SDan Streetman { 211a8a572a6SDan Streetman int ret; 212a8a572a6SDan Streetman 213a8a572a6SDan Streetman memcpy(&net->xfrm.xfrm4_dst_ops, &xfrm4_dst_ops_template, 214a8a572a6SDan Streetman sizeof(xfrm4_dst_ops_template)); 215a8a572a6SDan Streetman ret = dst_entries_init(&net->xfrm.xfrm4_dst_ops); 216a8a572a6SDan Streetman if (ret) 217a8a572a6SDan Streetman return ret; 218a8a572a6SDan Streetman 219a8a572a6SDan Streetman ret = xfrm4_net_sysctl_init(net); 220a8a572a6SDan Streetman if (ret) 221a8a572a6SDan Streetman dst_entries_destroy(&net->xfrm.xfrm4_dst_ops); 222a8a572a6SDan Streetman 223a8a572a6SDan Streetman return ret; 224a8a572a6SDan Streetman } 225a8a572a6SDan Streetman 226a8a572a6SDan Streetman static void __net_exit xfrm4_net_exit(struct net *net) 227a8a572a6SDan Streetman { 228a8a572a6SDan Streetman xfrm4_net_sysctl_exit(net); 229a8a572a6SDan Streetman dst_entries_destroy(&net->xfrm.xfrm4_dst_ops); 230a8a572a6SDan Streetman } 2318d068875SMichal Kubecek 2328d068875SMichal Kubecek static struct pernet_operations __net_initdata xfrm4_net_ops = { 2338d068875SMichal Kubecek .init = xfrm4_net_init, 2348d068875SMichal Kubecek .exit = xfrm4_net_exit, 2358d068875SMichal Kubecek }; 236a44a4a00SNeil Horman 2371da177e4SLinus Torvalds static void __init xfrm4_policy_init(void) 2381da177e4SLinus Torvalds { 239a2817d8bSFlorian Westphal xfrm_policy_register_afinfo(&xfrm4_policy_afinfo, AF_INET); 2401da177e4SLinus Torvalds } 2411da177e4SLinus Torvalds 242703fb94eSSteffen Klassert void __init xfrm4_init(void) 2431da177e4SLinus Torvalds { 244d7c7544cSAlexey Dobriyan xfrm4_state_init(); 245d7c7544cSAlexey Dobriyan xfrm4_policy_init(); 2462f32b51bSSteffen Klassert xfrm4_protocol_init(); 2478d068875SMichal Kubecek register_pernet_subsys(&xfrm4_net_ops); 2481da177e4SLinus Torvalds } 249