diff options
author | Florian Westphal <fw@strlen.de> | 2019-03-29 16:16:28 -0400 |
---|---|---|
committer | Steffen Klassert <steffen.klassert@secunet.com> | 2019-04-08 03:14:55 -0400 |
commit | b3284df1c86f7ac078dcb8fb250fe3d6437e740c (patch) | |
tree | 116d8518d917cc43bf0e3954b8aafa16db93e5c6 /net/xfrm/xfrm_input.c | |
parent | 7613b92b1ae37141704948b77e8762c5de896510 (diff) |
xfrm: remove input2 indirection from xfrm_mode
No external dependencies on any module, place this in the core.
Increase is about 1800 byte for xfrm_input.o.
The beet helpers get added to internal header, as they can be reused
from xfrm_output.c in the next patch (kernel contains several
copies of them in the xfrm{4,6}_mode_beet.c files).
Before:
text data bss dec filename
5578 176 2364 8118 net/xfrm/xfrm_input.o
1180 64 0 1244 net/ipv4/xfrm4_mode_beet.o
171 40 0 211 net/ipv4/xfrm4_mode_transport.o
1163 40 0 1203 net/ipv4/xfrm4_mode_tunnel.o
1083 52 0 1135 net/ipv6/xfrm6_mode_beet.o
172 40 0 212 net/ipv6/xfrm6_mode_ro.o
172 40 0 212 net/ipv6/xfrm6_mode_transport.o
1056 40 0 1096 net/ipv6/xfrm6_mode_tunnel.o
After:
text data bss dec filename
7373 200 2364 9937 net/xfrm/xfrm_input.o
587 44 0 631 net/ipv4/xfrm4_mode_beet.o
171 32 0 203 net/ipv4/xfrm4_mode_transport.o
649 32 0 681 net/ipv4/xfrm4_mode_tunnel.o
625 44 0 669 net/ipv6/xfrm6_mode_beet.o
172 32 0 204 net/ipv6/xfrm6_mode_ro.o
172 32 0 204 net/ipv6/xfrm6_mode_transport.o
599 32 0 631 net/ipv6/xfrm6_mode_tunnel.o
v2: pass inner_mode to xfrm_inner_mode_encap_remove to fix
AF_UNSPEC selector breakage (bisected by Benedict Wong)
Signed-off-by: Florian Westphal <fw@strlen.de>
Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
Diffstat (limited to 'net/xfrm/xfrm_input.c')
-rw-r--r-- | net/xfrm/xfrm_input.c | 185 |
1 files changed, 184 insertions, 1 deletions
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c index 0edf3fb73585..e0fd9561ffe5 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c | |||
@@ -21,6 +21,8 @@ | |||
21 | #include <net/ip_tunnels.h> | 21 | #include <net/ip_tunnels.h> |
22 | #include <net/ip6_tunnel.h> | 22 | #include <net/ip6_tunnel.h> |
23 | 23 | ||
24 | #include "xfrm_inout.h" | ||
25 | |||
24 | struct xfrm_trans_tasklet { | 26 | struct xfrm_trans_tasklet { |
25 | struct tasklet_struct tasklet; | 27 | struct tasklet_struct tasklet; |
26 | struct sk_buff_head queue; | 28 | struct sk_buff_head queue; |
@@ -166,6 +168,187 @@ int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, __be32 *spi, __be32 *seq) | |||
166 | } | 168 | } |
167 | EXPORT_SYMBOL(xfrm_parse_spi); | 169 | EXPORT_SYMBOL(xfrm_parse_spi); |
168 | 170 | ||
171 | static int xfrm4_remove_beet_encap(struct xfrm_state *x, struct sk_buff *skb) | ||
172 | { | ||
173 | struct iphdr *iph; | ||
174 | int optlen = 0; | ||
175 | int err = -EINVAL; | ||
176 | |||
177 | if (unlikely(XFRM_MODE_SKB_CB(skb)->protocol == IPPROTO_BEETPH)) { | ||
178 | struct ip_beet_phdr *ph; | ||
179 | int phlen; | ||
180 | |||
181 | if (!pskb_may_pull(skb, sizeof(*ph))) | ||
182 | goto out; | ||
183 | |||
184 | ph = (struct ip_beet_phdr *)skb->data; | ||
185 | |||
186 | phlen = sizeof(*ph) + ph->padlen; | ||
187 | optlen = ph->hdrlen * 8 + (IPV4_BEET_PHMAXLEN - phlen); | ||
188 | if (optlen < 0 || optlen & 3 || optlen > 250) | ||
189 | goto out; | ||
190 | |||
191 | XFRM_MODE_SKB_CB(skb)->protocol = ph->nexthdr; | ||
192 | |||
193 | if (!pskb_may_pull(skb, phlen)) | ||
194 | goto out; | ||
195 | __skb_pull(skb, phlen); | ||
196 | } | ||
197 | |||
198 | skb_push(skb, sizeof(*iph)); | ||
199 | skb_reset_network_header(skb); | ||
200 | skb_mac_header_rebuild(skb); | ||
201 | |||
202 | xfrm4_beet_make_header(skb); | ||
203 | |||
204 | iph = ip_hdr(skb); | ||
205 | |||
206 | iph->ihl += optlen / 4; | ||
207 | iph->tot_len = htons(skb->len); | ||
208 | iph->daddr = x->sel.daddr.a4; | ||
209 | iph->saddr = x->sel.saddr.a4; | ||
210 | iph->check = 0; | ||
211 | iph->check = ip_fast_csum(skb_network_header(skb), iph->ihl); | ||
212 | err = 0; | ||
213 | out: | ||
214 | return err; | ||
215 | } | ||
216 | |||
217 | static void ipip_ecn_decapsulate(struct sk_buff *skb) | ||
218 | { | ||
219 | struct iphdr *inner_iph = ipip_hdr(skb); | ||
220 | |||
221 | if (INET_ECN_is_ce(XFRM_MODE_SKB_CB(skb)->tos)) | ||
222 | IP_ECN_set_ce(inner_iph); | ||
223 | } | ||
224 | |||
225 | static int xfrm4_remove_tunnel_encap(struct xfrm_state *x, struct sk_buff *skb) | ||
226 | { | ||
227 | int err = -EINVAL; | ||
228 | |||
229 | if (XFRM_MODE_SKB_CB(skb)->protocol != IPPROTO_IPIP) | ||
230 | goto out; | ||
231 | |||
232 | if (!pskb_may_pull(skb, sizeof(struct iphdr))) | ||
233 | goto out; | ||
234 | |||
235 | err = skb_unclone(skb, GFP_ATOMIC); | ||
236 | if (err) | ||
237 | goto out; | ||
238 | |||
239 | if (x->props.flags & XFRM_STATE_DECAP_DSCP) | ||
240 | ipv4_copy_dscp(XFRM_MODE_SKB_CB(skb)->tos, ipip_hdr(skb)); | ||
241 | if (!(x->props.flags & XFRM_STATE_NOECN)) | ||
242 | ipip_ecn_decapsulate(skb); | ||
243 | |||
244 | skb_reset_network_header(skb); | ||
245 | skb_mac_header_rebuild(skb); | ||
246 | if (skb->mac_len) | ||
247 | eth_hdr(skb)->h_proto = skb->protocol; | ||
248 | |||
249 | err = 0; | ||
250 | |||
251 | out: | ||
252 | return err; | ||
253 | } | ||
254 | |||
255 | static void ipip6_ecn_decapsulate(struct sk_buff *skb) | ||
256 | { | ||
257 | struct ipv6hdr *inner_iph = ipipv6_hdr(skb); | ||
258 | |||
259 | if (INET_ECN_is_ce(XFRM_MODE_SKB_CB(skb)->tos)) | ||
260 | IP6_ECN_set_ce(skb, inner_iph); | ||
261 | } | ||
262 | |||
263 | static int xfrm6_remove_tunnel_encap(struct xfrm_state *x, struct sk_buff *skb) | ||
264 | { | ||
265 | int err = -EINVAL; | ||
266 | |||
267 | if (XFRM_MODE_SKB_CB(skb)->protocol != IPPROTO_IPV6) | ||
268 | goto out; | ||
269 | if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) | ||
270 | goto out; | ||
271 | |||
272 | err = skb_unclone(skb, GFP_ATOMIC); | ||
273 | if (err) | ||
274 | goto out; | ||
275 | |||
276 | if (x->props.flags & XFRM_STATE_DECAP_DSCP) | ||
277 | ipv6_copy_dscp(ipv6_get_dsfield(ipv6_hdr(skb)), | ||
278 | ipipv6_hdr(skb)); | ||
279 | if (!(x->props.flags & XFRM_STATE_NOECN)) | ||
280 | ipip6_ecn_decapsulate(skb); | ||
281 | |||
282 | skb_reset_network_header(skb); | ||
283 | skb_mac_header_rebuild(skb); | ||
284 | if (skb->mac_len) | ||
285 | eth_hdr(skb)->h_proto = skb->protocol; | ||
286 | |||
287 | err = 0; | ||
288 | |||
289 | out: | ||
290 | return err; | ||
291 | } | ||
292 | |||
293 | static int xfrm6_remove_beet_encap(struct xfrm_state *x, struct sk_buff *skb) | ||
294 | { | ||
295 | struct ipv6hdr *ip6h; | ||
296 | int size = sizeof(struct ipv6hdr); | ||
297 | int err; | ||
298 | |||
299 | err = skb_cow_head(skb, size + skb->mac_len); | ||
300 | if (err) | ||
301 | goto out; | ||
302 | |||
303 | __skb_push(skb, size); | ||
304 | skb_reset_network_header(skb); | ||
305 | skb_mac_header_rebuild(skb); | ||
306 | |||
307 | xfrm6_beet_make_header(skb); | ||
308 | |||
309 | ip6h = ipv6_hdr(skb); | ||
310 | ip6h->payload_len = htons(skb->len - size); | ||
311 | ip6h->daddr = x->sel.daddr.in6; | ||
312 | ip6h->saddr = x->sel.saddr.in6; | ||
313 | err = 0; | ||
314 | out: | ||
315 | return err; | ||
316 | } | ||
317 | |||
318 | /* Remove encapsulation header. | ||
319 | * | ||
320 | * The IP header will be moved over the top of the encapsulation | ||
321 | * header. | ||
322 | * | ||
323 | * On entry, the transport header shall point to where the IP header | ||
324 | * should be and the network header shall be set to where the IP | ||
325 | * header currently is. skb->data shall point to the start of the | ||
326 | * payload. | ||
327 | */ | ||
328 | static int | ||
329 | xfrm_inner_mode_encap_remove(struct xfrm_state *x, | ||
330 | const struct xfrm_mode *inner_mode, | ||
331 | struct sk_buff *skb) | ||
332 | { | ||
333 | switch (inner_mode->encap) { | ||
334 | case XFRM_MODE_BEET: | ||
335 | if (inner_mode->family == AF_INET) | ||
336 | return xfrm4_remove_beet_encap(x, skb); | ||
337 | if (inner_mode->family == AF_INET6) | ||
338 | return xfrm6_remove_beet_encap(x, skb); | ||
339 | break; | ||
340 | case XFRM_MODE_TUNNEL: | ||
341 | if (inner_mode->family == AF_INET) | ||
342 | return xfrm4_remove_tunnel_encap(x, skb); | ||
343 | if (inner_mode->family == AF_INET6) | ||
344 | return xfrm6_remove_tunnel_encap(x, skb); | ||
345 | break; | ||
346 | } | ||
347 | |||
348 | WARN_ON_ONCE(1); | ||
349 | return -EOPNOTSUPP; | ||
350 | } | ||
351 | |||
169 | static int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb) | 352 | static int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb) |
170 | { | 353 | { |
171 | struct xfrm_mode *inner_mode = x->inner_mode; | 354 | struct xfrm_mode *inner_mode = x->inner_mode; |
@@ -182,7 +365,7 @@ static int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb) | |||
182 | } | 365 | } |
183 | 366 | ||
184 | skb->protocol = inner_mode->afinfo->eth_proto; | 367 | skb->protocol = inner_mode->afinfo->eth_proto; |
185 | return inner_mode->input2(x, skb); | 368 | return xfrm_inner_mode_encap_remove(x, inner_mode, skb); |
186 | } | 369 | } |
187 | 370 | ||
188 | /* Remove encapsulation header. | 371 | /* Remove encapsulation header. |