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
__xfrm4_dst_lookup(struct net * net,struct flowi4 * fl4,int tos,int oif,const xfrm_address_t * saddr,const xfrm_address_t * daddr,u32 mark)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
xfrm4_dst_lookup(struct net * net,int tos,int oif,const xfrm_address_t * saddr,const xfrm_address_t * daddr,u32 mark)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
xfrm4_get_saddr(struct net * net,int oif,xfrm_address_t * saddr,xfrm_address_t * daddr,u32 mark)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
xfrm4_fill_dst(struct xfrm_dst * xdst,struct net_device * dev,const struct flowi * fl)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
xfrm4_update_pmtu(struct dst_entry * dst,struct sock * sk,struct sk_buff * skb,u32 mtu,bool confirm_neigh)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
xfrm4_redirect(struct dst_entry * dst,struct sock * sk,struct sk_buff * skb)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
xfrm4_dst_destroy(struct dst_entry * dst)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
xfrm4_net_sysctl_init(struct net * net)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
xfrm4_net_sysctl_exit(struct net * net)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 */
xfrm4_net_sysctl_init(struct net * net)199318d3cc0SArnd Bergmann static inline int xfrm4_net_sysctl_init(struct net *net)
200a8a572a6SDan Streetman {
201a8a572a6SDan Streetman return 0;
202a8a572a6SDan Streetman }
203a8a572a6SDan Streetman
xfrm4_net_sysctl_exit(struct net * net)204318d3cc0SArnd Bergmann static inline void xfrm4_net_sysctl_exit(struct net *net)
205a8a572a6SDan Streetman {
206a8a572a6SDan Streetman }
207a8a572a6SDan Streetman #endif
208a8a572a6SDan Streetman
xfrm4_net_init(struct net * net)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
xfrm4_net_exit(struct net * net)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
xfrm4_policy_init(void)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
xfrm4_init(void)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