/net/ipv6/xfrm6_policy.c

http://github.com/mirrors/linux · C · 308 lines · 241 code · 52 blank · 15 comment · 21 complexity · f474d5aec52fb46d69b3bb8efff50617 MD5 · raw file

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