.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * xfrm6_output.c - Common IPsec encapsulation code for IPv6. |
---|
3 | 4 | * Copyright (C) 2002 USAGI/WIDE Project |
---|
4 | 5 | * Copyright (c) 2004 Herbert Xu <herbert@gondor.apana.org.au> |
---|
5 | | - * |
---|
6 | | - * This program is free software; you can redistribute it and/or |
---|
7 | | - * modify it under the terms of the GNU General Public License |
---|
8 | | - * as published by the Free Software Foundation; either version |
---|
9 | | - * 2 of the License, or (at your option) any later version. |
---|
10 | 6 | */ |
---|
11 | 7 | |
---|
12 | 8 | #include <linux/if_ether.h> |
---|
.. | .. |
---|
27 | 23 | } |
---|
28 | 24 | EXPORT_SYMBOL(xfrm6_find_1stfragopt); |
---|
29 | 25 | |
---|
30 | | -static int xfrm6_local_dontfrag(struct sk_buff *skb) |
---|
31 | | -{ |
---|
32 | | - int proto; |
---|
33 | | - struct sock *sk = skb->sk; |
---|
34 | | - |
---|
35 | | - if (sk) { |
---|
36 | | - if (sk->sk_family != AF_INET6) |
---|
37 | | - return 0; |
---|
38 | | - |
---|
39 | | - proto = sk->sk_protocol; |
---|
40 | | - if (proto == IPPROTO_UDP || proto == IPPROTO_RAW) |
---|
41 | | - return inet6_sk(sk)->dontfrag; |
---|
42 | | - } |
---|
43 | | - |
---|
44 | | - return 0; |
---|
45 | | -} |
---|
46 | | - |
---|
47 | | -static void xfrm6_local_rxpmtu(struct sk_buff *skb, u32 mtu) |
---|
| 26 | +void xfrm6_local_rxpmtu(struct sk_buff *skb, u32 mtu) |
---|
48 | 27 | { |
---|
49 | 28 | struct flowi6 fl6; |
---|
50 | 29 | struct sock *sk = skb->sk; |
---|
.. | .. |
---|
68 | 47 | ipv6_local_error(sk, EMSGSIZE, &fl6, mtu); |
---|
69 | 48 | } |
---|
70 | 49 | |
---|
71 | | -static int xfrm6_tunnel_check_size(struct sk_buff *skb) |
---|
| 50 | +static int __xfrm6_output_finish(struct net *net, struct sock *sk, struct sk_buff *skb) |
---|
72 | 51 | { |
---|
73 | | - int mtu, ret = 0; |
---|
74 | | - struct dst_entry *dst = skb_dst(skb); |
---|
75 | | - |
---|
76 | | - if (skb->ignore_df) |
---|
77 | | - goto out; |
---|
78 | | - |
---|
79 | | - mtu = dst_mtu(dst); |
---|
80 | | - if (mtu < IPV6_MIN_MTU) |
---|
81 | | - mtu = IPV6_MIN_MTU; |
---|
82 | | - |
---|
83 | | - if ((!skb_is_gso(skb) && skb->len > mtu) || |
---|
84 | | - (skb_is_gso(skb) && |
---|
85 | | - !skb_gso_validate_network_len(skb, ip6_skb_dst_mtu(skb)))) { |
---|
86 | | - skb->dev = dst->dev; |
---|
87 | | - skb->protocol = htons(ETH_P_IPV6); |
---|
88 | | - |
---|
89 | | - if (xfrm6_local_dontfrag(skb)) |
---|
90 | | - xfrm6_local_rxpmtu(skb, mtu); |
---|
91 | | - else if (skb->sk) |
---|
92 | | - xfrm_local_error(skb, mtu); |
---|
93 | | - else |
---|
94 | | - icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); |
---|
95 | | - ret = -EMSGSIZE; |
---|
96 | | - } |
---|
97 | | -out: |
---|
98 | | - return ret; |
---|
99 | | -} |
---|
100 | | - |
---|
101 | | -int xfrm6_extract_output(struct xfrm_state *x, struct sk_buff *skb) |
---|
102 | | -{ |
---|
103 | | - int err; |
---|
104 | | - |
---|
105 | | - err = xfrm6_tunnel_check_size(skb); |
---|
106 | | - if (err) |
---|
107 | | - return err; |
---|
108 | | - |
---|
109 | | - XFRM_MODE_SKB_CB(skb)->protocol = ipv6_hdr(skb)->nexthdr; |
---|
110 | | - |
---|
111 | | - return xfrm6_extract_header(skb); |
---|
112 | | -} |
---|
113 | | - |
---|
114 | | -int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb) |
---|
115 | | -{ |
---|
116 | | - int err; |
---|
117 | | - |
---|
118 | | - err = xfrm_inner_extract_output(x, skb); |
---|
119 | | - if (err) |
---|
120 | | - return err; |
---|
121 | | - |
---|
122 | | - skb->ignore_df = 1; |
---|
123 | | - skb->protocol = htons(ETH_P_IPV6); |
---|
124 | | - |
---|
125 | | - return x->outer_mode->output2(x, skb); |
---|
126 | | -} |
---|
127 | | -EXPORT_SYMBOL(xfrm6_prepare_output); |
---|
128 | | - |
---|
129 | | -int xfrm6_output_finish(struct sock *sk, struct sk_buff *skb) |
---|
130 | | -{ |
---|
131 | | - memset(IP6CB(skb), 0, sizeof(*IP6CB(skb))); |
---|
132 | | - |
---|
133 | | - IP6CB(skb)->flags |= IP6SKB_XFRM_TRANSFORMED; |
---|
134 | | - |
---|
135 | 52 | return xfrm_output(sk, skb); |
---|
136 | 53 | } |
---|
137 | 54 | |
---|
138 | | -static int __xfrm6_output_finish(struct net *net, struct sock *sk, struct sk_buff *skb) |
---|
| 55 | +static int xfrm6_noneed_fragment(struct sk_buff *skb) |
---|
139 | 56 | { |
---|
140 | | - struct xfrm_state *x = skb_dst(skb)->xfrm; |
---|
| 57 | + struct frag_hdr *fh; |
---|
| 58 | + u8 prevhdr = ipv6_hdr(skb)->nexthdr; |
---|
141 | 59 | |
---|
142 | | - return x->outer_mode->afinfo->output_finish(sk, skb); |
---|
| 60 | + if (prevhdr != NEXTHDR_FRAGMENT) |
---|
| 61 | + return 0; |
---|
| 62 | + fh = (struct frag_hdr *)(skb->data + sizeof(struct ipv6hdr)); |
---|
| 63 | + if (fh->nexthdr == NEXTHDR_ESP || fh->nexthdr == NEXTHDR_AUTH) |
---|
| 64 | + return 1; |
---|
| 65 | + return 0; |
---|
143 | 66 | } |
---|
144 | 67 | |
---|
145 | 68 | static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb) |
---|
.. | .. |
---|
166 | 89 | |
---|
167 | 90 | toobig = skb->len > mtu && !skb_is_gso(skb); |
---|
168 | 91 | |
---|
169 | | - if (toobig && xfrm6_local_dontfrag(skb)) { |
---|
| 92 | + if (toobig && xfrm6_local_dontfrag(skb->sk)) { |
---|
170 | 93 | xfrm6_local_rxpmtu(skb, mtu); |
---|
171 | 94 | kfree_skb(skb); |
---|
172 | 95 | return -EMSGSIZE; |
---|
| 96 | + } else if (toobig && xfrm6_noneed_fragment(skb)) { |
---|
| 97 | + skb->ignore_df = 1; |
---|
| 98 | + goto skip_frag; |
---|
173 | 99 | } else if (!skb->ignore_df && toobig && skb->sk) { |
---|
174 | 100 | xfrm_local_error(skb, mtu); |
---|
175 | 101 | kfree_skb(skb); |
---|
.. | .. |
---|
181 | 107 | __xfrm6_output_finish); |
---|
182 | 108 | |
---|
183 | 109 | skip_frag: |
---|
184 | | - return x->outer_mode->afinfo->output_finish(sk, skb); |
---|
| 110 | + return xfrm_output(sk, skb); |
---|
185 | 111 | } |
---|
186 | 112 | |
---|
187 | 113 | int xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb) |
---|
188 | 114 | { |
---|
189 | 115 | return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, |
---|
190 | | - net, sk, skb, NULL, skb_dst(skb)->dev, |
---|
| 116 | + net, sk, skb, skb->dev, skb_dst(skb)->dev, |
---|
191 | 117 | __xfrm6_output, |
---|
192 | 118 | !(IP6CB(skb)->flags & IP6SKB_REROUTED)); |
---|
193 | 119 | } |
---|