/net/ipv6/xfrm6_output.c

http://github.com/mirrors/linux · C · 191 lines · 147 code · 38 blank · 6 comment · 30 complexity · 815f27fcabd6648c8ce29a8e9cf77a86 MD5 · raw file

  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * xfrm6_output.c - Common IPsec encapsulation code for IPv6.
  4. * Copyright (C) 2002 USAGI/WIDE Project
  5. * Copyright (c) 2004 Herbert Xu <herbert@gondor.apana.org.au>
  6. */
  7. #include <linux/if_ether.h>
  8. #include <linux/kernel.h>
  9. #include <linux/module.h>
  10. #include <linux/skbuff.h>
  11. #include <linux/icmpv6.h>
  12. #include <linux/netfilter_ipv6.h>
  13. #include <net/dst.h>
  14. #include <net/ipv6.h>
  15. #include <net/ip6_route.h>
  16. #include <net/xfrm.h>
  17. int xfrm6_find_1stfragopt(struct xfrm_state *x, struct sk_buff *skb,
  18. u8 **prevhdr)
  19. {
  20. return ip6_find_1stfragopt(skb, prevhdr);
  21. }
  22. EXPORT_SYMBOL(xfrm6_find_1stfragopt);
  23. static int xfrm6_local_dontfrag(struct sk_buff *skb)
  24. {
  25. int proto;
  26. struct sock *sk = skb->sk;
  27. if (sk) {
  28. if (sk->sk_family != AF_INET6)
  29. return 0;
  30. proto = sk->sk_protocol;
  31. if (proto == IPPROTO_UDP || proto == IPPROTO_RAW)
  32. return inet6_sk(sk)->dontfrag;
  33. }
  34. return 0;
  35. }
  36. static void xfrm6_local_rxpmtu(struct sk_buff *skb, u32 mtu)
  37. {
  38. struct flowi6 fl6;
  39. struct sock *sk = skb->sk;
  40. fl6.flowi6_oif = sk->sk_bound_dev_if;
  41. fl6.daddr = ipv6_hdr(skb)->daddr;
  42. ipv6_local_rxpmtu(sk, &fl6, mtu);
  43. }
  44. void xfrm6_local_error(struct sk_buff *skb, u32 mtu)
  45. {
  46. struct flowi6 fl6;
  47. const struct ipv6hdr *hdr;
  48. struct sock *sk = skb->sk;
  49. hdr = skb->encapsulation ? inner_ipv6_hdr(skb) : ipv6_hdr(skb);
  50. fl6.fl6_dport = inet_sk(sk)->inet_dport;
  51. fl6.daddr = hdr->daddr;
  52. ipv6_local_error(sk, EMSGSIZE, &fl6, mtu);
  53. }
  54. static int xfrm6_tunnel_check_size(struct sk_buff *skb)
  55. {
  56. int mtu, ret = 0;
  57. struct dst_entry *dst = skb_dst(skb);
  58. if (skb->ignore_df)
  59. goto out;
  60. mtu = dst_mtu(dst);
  61. if (mtu < IPV6_MIN_MTU)
  62. mtu = IPV6_MIN_MTU;
  63. if ((!skb_is_gso(skb) && skb->len > mtu) ||
  64. (skb_is_gso(skb) &&
  65. !skb_gso_validate_network_len(skb, ip6_skb_dst_mtu(skb)))) {
  66. skb->dev = dst->dev;
  67. skb->protocol = htons(ETH_P_IPV6);
  68. if (xfrm6_local_dontfrag(skb))
  69. xfrm6_local_rxpmtu(skb, mtu);
  70. else if (skb->sk)
  71. xfrm_local_error(skb, mtu);
  72. else
  73. icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
  74. ret = -EMSGSIZE;
  75. }
  76. out:
  77. return ret;
  78. }
  79. int xfrm6_extract_output(struct xfrm_state *x, struct sk_buff *skb)
  80. {
  81. int err;
  82. err = xfrm6_tunnel_check_size(skb);
  83. if (err)
  84. return err;
  85. XFRM_MODE_SKB_CB(skb)->protocol = ipv6_hdr(skb)->nexthdr;
  86. return xfrm6_extract_header(skb);
  87. }
  88. int xfrm6_output_finish(struct sock *sk, struct sk_buff *skb)
  89. {
  90. memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
  91. IP6CB(skb)->flags |= IP6SKB_XFRM_TRANSFORMED;
  92. return xfrm_output(sk, skb);
  93. }
  94. static int __xfrm6_output_state_finish(struct xfrm_state *x, struct sock *sk,
  95. struct sk_buff *skb)
  96. {
  97. const struct xfrm_state_afinfo *afinfo;
  98. int ret = -EAFNOSUPPORT;
  99. rcu_read_lock();
  100. afinfo = xfrm_state_afinfo_get_rcu(x->outer_mode.family);
  101. if (likely(afinfo))
  102. ret = afinfo->output_finish(sk, skb);
  103. else
  104. kfree_skb(skb);
  105. rcu_read_unlock();
  106. return ret;
  107. }
  108. static int __xfrm6_output_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
  109. {
  110. struct xfrm_state *x = skb_dst(skb)->xfrm;
  111. return __xfrm6_output_state_finish(x, sk, skb);
  112. }
  113. static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
  114. {
  115. struct dst_entry *dst = skb_dst(skb);
  116. struct xfrm_state *x = dst->xfrm;
  117. int mtu;
  118. bool toobig;
  119. #ifdef CONFIG_NETFILTER
  120. if (!x) {
  121. IP6CB(skb)->flags |= IP6SKB_REROUTED;
  122. return dst_output(net, sk, skb);
  123. }
  124. #endif
  125. if (x->props.mode != XFRM_MODE_TUNNEL)
  126. goto skip_frag;
  127. if (skb->protocol == htons(ETH_P_IPV6))
  128. mtu = ip6_skb_dst_mtu(skb);
  129. else
  130. mtu = dst_mtu(skb_dst(skb));
  131. toobig = skb->len > mtu && !skb_is_gso(skb);
  132. if (toobig && xfrm6_local_dontfrag(skb)) {
  133. xfrm6_local_rxpmtu(skb, mtu);
  134. kfree_skb(skb);
  135. return -EMSGSIZE;
  136. } else if (!skb->ignore_df && toobig && skb->sk) {
  137. xfrm_local_error(skb, mtu);
  138. kfree_skb(skb);
  139. return -EMSGSIZE;
  140. }
  141. if (toobig || dst_allfrag(skb_dst(skb)))
  142. return ip6_fragment(net, sk, skb,
  143. __xfrm6_output_finish);
  144. skip_frag:
  145. return __xfrm6_output_state_finish(x, sk, skb);
  146. }
  147. int xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
  148. {
  149. return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING,
  150. net, sk, skb, skb->dev, skb_dst(skb)->dev,
  151. __xfrm6_output,
  152. !(IP6CB(skb)->flags & IP6SKB_REROUTED));
  153. }