~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/net/ipv6/xfrm6_policy.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php