1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * xfrm6_policy.c: based on xfrm4_policy.c
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Authors:
61da177e4SLinus Torvalds * Mitsuru KANDA @USAGI
71da177e4SLinus Torvalds * Kazunori MIYAZAWA @USAGI
81da177e4SLinus Torvalds * Kunihiro Ishiguro <kunihiro@ipinfusion.com>
91da177e4SLinus Torvalds * IPv6 support
101da177e4SLinus Torvalds * YOSHIFUJI Hideaki
111da177e4SLinus Torvalds * Split up af-specific portion
121da177e4SLinus Torvalds *
131da177e4SLinus Torvalds */
141da177e4SLinus Torvalds
1566cdb3caSHerbert Xu #include <linux/err.h>
1666cdb3caSHerbert Xu #include <linux/kernel.h>
17aabc9761SHerbert Xu #include <linux/netdevice.h>
18aabc9761SHerbert Xu #include <net/addrconf.h>
1945ff5a3fSHerbert Xu #include <net/dst.h>
201da177e4SLinus Torvalds #include <net/xfrm.h>
211da177e4SLinus Torvalds #include <net/ip.h>
221da177e4SLinus Torvalds #include <net/ipv6.h>
231da177e4SLinus Torvalds #include <net/ip6_route.h>
24385add90SDavid Ahern #include <net/l3mdev.h>
251da177e4SLinus Torvalds
xfrm6_dst_lookup(const struct xfrm_dst_lookup_params * params)26ac1d820eSEyal Birger static struct dst_entry *xfrm6_dst_lookup(const struct xfrm_dst_lookup_params *params)
271da177e4SLinus Torvalds {
287e1dc7b6SDavid S. Miller struct flowi6 fl6;
2966cdb3caSHerbert Xu struct dst_entry *dst;
3066cdb3caSHerbert Xu int err;
3166cdb3caSHerbert Xu
327e1dc7b6SDavid S. Miller memset(&fl6, 0, sizeof(fl6));
33ac1d820eSEyal Birger fl6.flowi6_l3mdev = l3mdev_master_ifindex_by_index(params->net,
34ac1d820eSEyal Birger params->oif);
35ac1d820eSEyal Birger fl6.flowi6_mark = params->mark;
36ac1d820eSEyal Birger memcpy(&fl6.daddr, params->daddr, sizeof(fl6.daddr));
37ac1d820eSEyal Birger if (params->saddr)
38ac1d820eSEyal Birger memcpy(&fl6.saddr, params->saddr, sizeof(fl6.saddr));
3966cdb3caSHerbert Xu
40c1530660SEyal Birger fl6.flowi4_proto = params->ipproto;
41c1530660SEyal Birger fl6.uli = params->uli;
42c1530660SEyal Birger
43ac1d820eSEyal Birger dst = ip6_route_output(params->net, NULL, &fl6);
4466cdb3caSHerbert Xu
4566cdb3caSHerbert Xu err = dst->error;
4666cdb3caSHerbert Xu if (dst->error) {
474251320fSVille Nuorvala dst_release(dst);
4866cdb3caSHerbert Xu dst = ERR_PTR(err);
4966cdb3caSHerbert Xu }
5066cdb3caSHerbert Xu
5166cdb3caSHerbert Xu return dst;
521da177e4SLinus Torvalds }
531da177e4SLinus Torvalds
xfrm6_get_saddr(xfrm_address_t * saddr,const struct xfrm_dst_lookup_params * params)54ac1d820eSEyal Birger static int xfrm6_get_saddr(xfrm_address_t *saddr,
55ac1d820eSEyal Birger const struct xfrm_dst_lookup_params *params)
56a1e59abfSPatrick McHardy {
5766cdb3caSHerbert Xu struct dst_entry *dst;
58191cd582SBrian Haley struct net_device *dev;
59f897d717SEric Dumazet struct inet6_dev *idev;
60a1e59abfSPatrick McHardy
61ac1d820eSEyal Birger dst = xfrm6_dst_lookup(params);
6266cdb3caSHerbert Xu if (IS_ERR(dst))
63a1e59abfSPatrick McHardy return -EHOSTUNREACH;
6466cdb3caSHerbert Xu
65f897d717SEric Dumazet idev = ip6_dst_idev(dst);
66f897d717SEric Dumazet if (!idev) {
67f897d717SEric Dumazet dst_release(dst);
68f897d717SEric Dumazet return -EHOSTUNREACH;
69f897d717SEric Dumazet }
70f897d717SEric Dumazet dev = idev->dev;
71ac1d820eSEyal Birger ipv6_dev_get_saddr(dev_net(dev), dev, ¶ms->daddr->in6, 0,
72ac1d820eSEyal Birger &saddr->in6);
7366cdb3caSHerbert Xu dst_release(dst);
7466cdb3caSHerbert Xu return 0;
75a1e59abfSPatrick McHardy }
76a1e59abfSPatrick McHardy
xfrm6_fill_dst(struct xfrm_dst * xdst,struct net_device * dev,const struct flowi * fl)7787c1e12bSHerbert Xu static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev,
780c7b3eefSDavid S. Miller const struct flowi *fl)
7925ee3286SHerbert Xu {
80*797a4c1fSEric Dumazet struct rt6_info *rt = dst_rt6_info(xdst->route);
811da177e4SLinus Torvalds
8225ee3286SHerbert Xu xdst->u.dst.dev = dev;
83d62607c3SJakub Kicinski netdev_hold(dev, &xdst->u.dst.dev_tracker, GFP_ATOMIC);
841da177e4SLinus Torvalds
85bc8e4b95SNicolas Dichtel xdst->u.rt6.rt6i_idev = in6_dev_get(dev);
8684c4a9dfSCong Wang if (!xdst->u.rt6.rt6i_idev) {
87d62607c3SJakub Kicinski netdev_put(dev, &xdst->u.dst.dev_tracker);
8825ee3286SHerbert Xu return -ENODEV;
8984c4a9dfSCong Wang }
90c82f963eSMiika Komu
911da177e4SLinus Torvalds /* Sheit... I remember I did this right. Apparently,
921da177e4SLinus Torvalds * it was magically lost, so this code needs audit */
9325ee3286SHerbert Xu xdst->u.rt6.rt6i_flags = rt->rt6i_flags & (RTF_ANYCAST |
9425ee3286SHerbert Xu RTF_LOCAL);
95b197df4fSMartin KaFai Lau xdst->route_cookie = rt6_get_cookie(rt);
9625ee3286SHerbert Xu xdst->u.rt6.rt6i_gateway = rt->rt6i_gateway;
9725ee3286SHerbert Xu xdst->u.rt6.rt6i_dst = rt->rt6i_dst;
9825ee3286SHerbert Xu xdst->u.rt6.rt6i_src = rt->rt6i_src;
99510c321bSXin Long rt6_uncached_list_add(&xdst->u.rt6);
1001da177e4SLinus Torvalds
1011da177e4SLinus Torvalds return 0;
1021da177e4SLinus Torvalds }
1031da177e4SLinus Torvalds
xfrm6_update_pmtu(struct dst_entry * dst,struct sock * sk,struct sk_buff * skb,u32 mtu,bool confirm_neigh)1046700c270SDavid S. Miller static void xfrm6_update_pmtu(struct dst_entry *dst, struct sock *sk,
105bd085ef6SHangbin Liu struct sk_buff *skb, u32 mtu,
106bd085ef6SHangbin Liu bool confirm_neigh)
1071da177e4SLinus Torvalds {
1081da177e4SLinus Torvalds struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
1091da177e4SLinus Torvalds struct dst_entry *path = xdst->route;
1101da177e4SLinus Torvalds
111bd085ef6SHangbin Liu path->ops->update_pmtu(path, sk, skb, mtu, confirm_neigh);
1121da177e4SLinus Torvalds }
1131da177e4SLinus Torvalds
xfrm6_redirect(struct dst_entry * dst,struct sock * sk,struct sk_buff * skb)1146700c270SDavid S. Miller static void xfrm6_redirect(struct dst_entry *dst, struct sock *sk,
1156700c270SDavid S. Miller struct sk_buff *skb)
116ec18d9a2SDavid S. Miller {
117ec18d9a2SDavid S. Miller struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
118ec18d9a2SDavid S. Miller struct dst_entry *path = xdst->route;
119ec18d9a2SDavid S. Miller
1206700c270SDavid S. Miller path->ops->redirect(path, sk, skb);
121ec18d9a2SDavid S. Miller }
122ec18d9a2SDavid S. Miller
xfrm6_dst_destroy(struct dst_entry * dst)123aabc9761SHerbert Xu static void xfrm6_dst_destroy(struct dst_entry *dst)
124aabc9761SHerbert Xu {
125aabc9761SHerbert Xu struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
126aabc9761SHerbert Xu
12762fa8a84SDavid S. Miller dst_destroy_metrics_generic(dst);
128510c321bSXin Long rt6_uncached_list_del(&xdst->u.rt6);
129cc9b364bSZhang Changzhong if (likely(xdst->u.rt6.rt6i_idev))
130cc9b364bSZhang Changzhong in6_dev_put(xdst->u.rt6.rt6i_idev);
131aabc9761SHerbert Xu xfrm_dst_destroy(xdst);
132aabc9761SHerbert Xu }
133aabc9761SHerbert Xu
xfrm6_dst_ifdown(struct dst_entry * dst,struct net_device * dev)13443c28172SZhengchao Shao static void xfrm6_dst_ifdown(struct dst_entry *dst, struct net_device *dev)
135aabc9761SHerbert Xu {
136aabc9761SHerbert Xu struct xfrm_dst *xdst;
137aabc9761SHerbert Xu
138aabc9761SHerbert Xu xdst = (struct xfrm_dst *)dst;
139aabc9761SHerbert Xu if (xdst->u.rt6.rt6i_idev->dev == dev) {
1405a3e55d6SDenis V. Lunev struct inet6_dev *loopback_idev =
141c346dca1SYOSHIFUJI Hideaki in6_dev_get(dev_net(dev)->loopback_dev);
142aabc9761SHerbert Xu
143aabc9761SHerbert Xu do {
144aabc9761SHerbert Xu in6_dev_put(xdst->u.rt6.rt6i_idev);
145aabc9761SHerbert Xu xdst->u.rt6.rt6i_idev = loopback_idev;
146aabc9761SHerbert Xu in6_dev_hold(loopback_idev);
147b92cf4aaSDavid Miller xdst = (struct xfrm_dst *)xfrm_dst_child(&xdst->u.dst);
148aabc9761SHerbert Xu } while (xdst->u.dst.xfrm);
149aabc9761SHerbert Xu
150aabc9761SHerbert Xu __in6_dev_put(loopback_idev);
151aabc9761SHerbert Xu }
152aabc9761SHerbert Xu
153aabc9761SHerbert Xu xfrm_dst_ifdown(dst, dev);
154aabc9761SHerbert Xu }
155aabc9761SHerbert Xu
156a8a572a6SDan Streetman static struct dst_ops xfrm6_dst_ops_template = {
1571da177e4SLinus Torvalds .family = AF_INET6,
1581da177e4SLinus Torvalds .update_pmtu = xfrm6_update_pmtu,
159ec18d9a2SDavid S. Miller .redirect = xfrm6_redirect,
16062fa8a84SDavid S. Miller .cow_metrics = dst_cow_metrics_generic,
161aabc9761SHerbert Xu .destroy = xfrm6_dst_destroy,
162aabc9761SHerbert Xu .ifdown = xfrm6_dst_ifdown,
163862b82c6SHerbert Xu .local_out = __ip6_local_out,
1643c2a89ddSFlorian Westphal .gc_thresh = 32768,
1651da177e4SLinus Torvalds };
1661da177e4SLinus Torvalds
16737b10383SFlorian Westphal static const struct xfrm_policy_afinfo xfrm6_policy_afinfo = {
168a8a572a6SDan Streetman .dst_ops = &xfrm6_dst_ops_template,
1691da177e4SLinus Torvalds .dst_lookup = xfrm6_dst_lookup,
170a1e59abfSPatrick McHardy .get_saddr = xfrm6_get_saddr,
17125ee3286SHerbert Xu .fill_dst = xfrm6_fill_dst,
1722774c131SDavid S. Miller .blackhole_route = ip6_blackhole_route,
1731da177e4SLinus Torvalds };
1741da177e4SLinus Torvalds
xfrm6_policy_init(void)1750013cabaSDaniel Lezcano static int __init xfrm6_policy_init(void)
1761da177e4SLinus Torvalds {
177a2817d8bSFlorian Westphal return xfrm_policy_register_afinfo(&xfrm6_policy_afinfo, AF_INET6);
1781da177e4SLinus Torvalds }
1791da177e4SLinus Torvalds
xfrm6_policy_fini(void)1801da177e4SLinus Torvalds static void xfrm6_policy_fini(void)
1811da177e4SLinus Torvalds {
1821da177e4SLinus Torvalds xfrm_policy_unregister_afinfo(&xfrm6_policy_afinfo);
1831da177e4SLinus Torvalds }
1841da177e4SLinus Torvalds
185db71789cSDavid S. Miller #ifdef CONFIG_SYSCTL
186a44a4a00SNeil Horman static struct ctl_table xfrm6_policy_table[] = {
187a44a4a00SNeil Horman {
188a44a4a00SNeil Horman .procname = "xfrm6_gc_thresh",
189d7c7544cSAlexey Dobriyan .data = &init_net.xfrm.xfrm6_dst_ops.gc_thresh,
190a44a4a00SNeil Horman .maxlen = sizeof(int),
191a44a4a00SNeil Horman .mode = 0644,
192a44a4a00SNeil Horman .proc_handler = proc_dointvec,
193a44a4a00SNeil Horman },
194a44a4a00SNeil Horman { }
195a44a4a00SNeil Horman };
196a44a4a00SNeil Horman
xfrm6_net_sysctl_init(struct net * net)197a8a572a6SDan Streetman static int __net_init xfrm6_net_sysctl_init(struct net *net)
1988d068875SMichal Kubecek {
1998d068875SMichal Kubecek struct ctl_table *table;
2008d068875SMichal Kubecek struct ctl_table_header *hdr;
2018d068875SMichal Kubecek
2028d068875SMichal Kubecek table = xfrm6_policy_table;
2038d068875SMichal Kubecek if (!net_eq(net, &init_net)) {
2048d068875SMichal Kubecek table = kmemdup(table, sizeof(xfrm6_policy_table), GFP_KERNEL);
2058d068875SMichal Kubecek if (!table)
2068d068875SMichal Kubecek goto err_alloc;
2078d068875SMichal Kubecek
2088d068875SMichal Kubecek table[0].data = &net->xfrm.xfrm6_dst_ops.gc_thresh;
2098d068875SMichal Kubecek }
2108d068875SMichal Kubecek
211c899710fSJoel Granados hdr = register_net_sysctl_sz(net, "net/ipv6", table,
212c899710fSJoel Granados ARRAY_SIZE(xfrm6_policy_table));
2138d068875SMichal Kubecek if (!hdr)
2148d068875SMichal Kubecek goto err_reg;
2158d068875SMichal Kubecek
2168d068875SMichal Kubecek net->ipv6.sysctl.xfrm6_hdr = hdr;
2178d068875SMichal Kubecek return 0;
2188d068875SMichal Kubecek
2198d068875SMichal Kubecek err_reg:
2208d068875SMichal Kubecek if (!net_eq(net, &init_net))
2218d068875SMichal Kubecek kfree(table);
2228d068875SMichal Kubecek err_alloc:
2238d068875SMichal Kubecek return -ENOMEM;
2248d068875SMichal Kubecek }
2258d068875SMichal Kubecek
xfrm6_net_sysctl_exit(struct net * net)226a8a572a6SDan Streetman static void __net_exit xfrm6_net_sysctl_exit(struct net *net)
2278d068875SMichal Kubecek {
2288d068875SMichal Kubecek struct ctl_table *table;
2298d068875SMichal Kubecek
23063159f29SIan Morris if (!net->ipv6.sysctl.xfrm6_hdr)
2318d068875SMichal Kubecek return;
2328d068875SMichal Kubecek
2338d068875SMichal Kubecek table = net->ipv6.sysctl.xfrm6_hdr->ctl_table_arg;
2348d068875SMichal Kubecek unregister_net_sysctl_table(net->ipv6.sysctl.xfrm6_hdr);
2358d068875SMichal Kubecek if (!net_eq(net, &init_net))
2368d068875SMichal Kubecek kfree(table);
2378d068875SMichal Kubecek }
238a8a572a6SDan Streetman #else /* CONFIG_SYSCTL */
xfrm6_net_sysctl_init(struct net * net)239318d3cc0SArnd Bergmann static inline int xfrm6_net_sysctl_init(struct net *net)
240a8a572a6SDan Streetman {
241a8a572a6SDan Streetman return 0;
242a8a572a6SDan Streetman }
243a8a572a6SDan Streetman
xfrm6_net_sysctl_exit(struct net * net)244318d3cc0SArnd Bergmann static inline void xfrm6_net_sysctl_exit(struct net *net)
245a8a572a6SDan Streetman {
246a8a572a6SDan Streetman }
247a8a572a6SDan Streetman #endif
248a8a572a6SDan Streetman
xfrm6_net_init(struct net * net)249a8a572a6SDan Streetman static int __net_init xfrm6_net_init(struct net *net)
250a8a572a6SDan Streetman {
251a8a572a6SDan Streetman int ret;
252a8a572a6SDan Streetman
253a8a572a6SDan Streetman memcpy(&net->xfrm.xfrm6_dst_ops, &xfrm6_dst_ops_template,
254a8a572a6SDan Streetman sizeof(xfrm6_dst_ops_template));
255a8a572a6SDan Streetman ret = dst_entries_init(&net->xfrm.xfrm6_dst_ops);
256a8a572a6SDan Streetman if (ret)
257a8a572a6SDan Streetman return ret;
258a8a572a6SDan Streetman
259a8a572a6SDan Streetman ret = xfrm6_net_sysctl_init(net);
260a8a572a6SDan Streetman if (ret)
261a8a572a6SDan Streetman dst_entries_destroy(&net->xfrm.xfrm6_dst_ops);
262a8a572a6SDan Streetman
263a8a572a6SDan Streetman return ret;
264a8a572a6SDan Streetman }
265a8a572a6SDan Streetman
xfrm6_net_exit(struct net * net)266a8a572a6SDan Streetman static void __net_exit xfrm6_net_exit(struct net *net)
267a8a572a6SDan Streetman {
268a8a572a6SDan Streetman xfrm6_net_sysctl_exit(net);
269a8a572a6SDan Streetman dst_entries_destroy(&net->xfrm.xfrm6_dst_ops);
270a8a572a6SDan Streetman }
2718d068875SMichal Kubecek
2728d068875SMichal Kubecek static struct pernet_operations xfrm6_net_ops = {
2738d068875SMichal Kubecek .init = xfrm6_net_init,
2748d068875SMichal Kubecek .exit = xfrm6_net_exit,
2758d068875SMichal Kubecek };
276a44a4a00SNeil Horman
xfrm6_init(void)2770013cabaSDaniel Lezcano int __init xfrm6_init(void)
2781da177e4SLinus Torvalds {
2790013cabaSDaniel Lezcano int ret;
2800013cabaSDaniel Lezcano
281d7c7544cSAlexey Dobriyan ret = xfrm6_policy_init();
282a8a572a6SDan Streetman if (ret)
283d7c7544cSAlexey Dobriyan goto out;
284d7c7544cSAlexey Dobriyan ret = xfrm6_state_init();
285d7c7544cSAlexey Dobriyan if (ret)
286d7c7544cSAlexey Dobriyan goto out_policy;
287d7c7544cSAlexey Dobriyan
2887e14ea15SSteffen Klassert ret = xfrm6_protocol_init();
2897e14ea15SSteffen Klassert if (ret)
2907e14ea15SSteffen Klassert goto out_state;
2917e14ea15SSteffen Klassert
29240781bfbSChen Zhongjin ret = register_pernet_subsys(&xfrm6_net_ops);
29340781bfbSChen Zhongjin if (ret)
29440781bfbSChen Zhongjin goto out_protocol;
2950013cabaSDaniel Lezcano out:
2960013cabaSDaniel Lezcano return ret;
29740781bfbSChen Zhongjin out_protocol:
29840781bfbSChen Zhongjin xfrm6_protocol_fini();
2997e14ea15SSteffen Klassert out_state:
3007e14ea15SSteffen Klassert xfrm6_state_fini();
3010013cabaSDaniel Lezcano out_policy:
3020013cabaSDaniel Lezcano xfrm6_policy_fini();
3030013cabaSDaniel Lezcano goto out;
3041da177e4SLinus Torvalds }
3051da177e4SLinus Torvalds
xfrm6_fini(void)3061da177e4SLinus Torvalds void xfrm6_fini(void)
3071da177e4SLinus Torvalds {
3088d068875SMichal Kubecek unregister_pernet_subsys(&xfrm6_net_ops);
3097e14ea15SSteffen Klassert xfrm6_protocol_fini();
3101da177e4SLinus Torvalds xfrm6_policy_fini();
3111da177e4SLinus Torvalds xfrm6_state_fini();
3121da177e4SLinus Torvalds }
313