Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
1da177e4 LT |
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> | |
1da177e4 LT |
6 | */ |
7 | ||
406ef77c | 8 | #include <linux/if_ether.h> |
36cf9acf HX |
9 | #include <linux/kernel.h> |
10 | #include <linux/module.h> | |
1da177e4 | 11 | #include <linux/skbuff.h> |
1da177e4 | 12 | #include <linux/icmpv6.h> |
16a6677f | 13 | #include <linux/netfilter_ipv6.h> |
36cf9acf | 14 | #include <net/dst.h> |
1da177e4 | 15 | #include <net/ipv6.h> |
ad0081e4 | 16 | #include <net/ip6_route.h> |
1da177e4 LT |
17 | #include <net/xfrm.h> |
18 | ||
3e50ddd8 | 19 | void xfrm6_local_rxpmtu(struct sk_buff *skb, u32 mtu) |
dd767856 SK |
20 | { |
21 | struct flowi6 fl6; | |
22 | struct sock *sk = skb->sk; | |
23 | ||
24 | fl6.flowi6_oif = sk->sk_bound_dev_if; | |
4e3fd7a0 | 25 | fl6.daddr = ipv6_hdr(skb)->daddr; |
dd767856 SK |
26 | |
27 | ipv6_local_rxpmtu(sk, &fl6, mtu); | |
28 | } | |
29 | ||
628e341f | 30 | void xfrm6_local_error(struct sk_buff *skb, u32 mtu) |
dd767856 SK |
31 | { |
32 | struct flowi6 fl6; | |
5d0ff542 | 33 | const struct ipv6hdr *hdr; |
dd767856 SK |
34 | struct sock *sk = skb->sk; |
35 | ||
5d0ff542 | 36 | hdr = skb->encapsulation ? inner_ipv6_hdr(skb) : ipv6_hdr(skb); |
dd767856 | 37 | fl6.fl6_dport = inet_sk(sk)->inet_dport; |
5d0ff542 | 38 | fl6.daddr = hdr->daddr; |
dd767856 SK |
39 | |
40 | ipv6_local_error(sk, EMSGSIZE, &fl6, mtu); | |
41 | } | |
42 | ||
7d8c6e39 EB |
43 | static int __xfrm6_output_finish(struct net *net, struct sock *sk, struct sk_buff *skb) |
44 | { | |
2ab6096d | 45 | return xfrm_output(sk, skb); |
7d8c6e39 EB |
46 | } |
47 | ||
4ff2980b LW |
48 | static int xfrm6_noneed_fragment(struct sk_buff *skb) |
49 | { | |
50 | struct frag_hdr *fh; | |
51 | u8 prevhdr = ipv6_hdr(skb)->nexthdr; | |
52 | ||
53 | if (prevhdr != NEXTHDR_FRAGMENT) | |
54 | return 0; | |
55 | fh = (struct frag_hdr *)(skb->data + sizeof(struct ipv6hdr)); | |
56 | if (fh->nexthdr == NEXTHDR_ESP || fh->nexthdr == NEXTHDR_AUTH) | |
57 | return 1; | |
58 | return 0; | |
59 | } | |
60 | ||
0c4b51f0 | 61 | static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb) |
ad0081e4 DS |
62 | { |
63 | struct dst_entry *dst = skb_dst(skb); | |
64 | struct xfrm_state *x = dst->xfrm; | |
40fc3054 | 65 | unsigned int mtu; |
93efac3f | 66 | bool toobig; |
0ea9d5e3 | 67 | |
5596732f SK |
68 | #ifdef CONFIG_NETFILTER |
69 | if (!x) { | |
70 | IP6CB(skb)->flags |= IP6SKB_REROUTED; | |
13206b6b | 71 | return dst_output(net, sk, skb); |
5596732f SK |
72 | } |
73 | #endif | |
74 | ||
93efac3f HX |
75 | if (x->props.mode != XFRM_MODE_TUNNEL) |
76 | goto skip_frag; | |
77 | ||
5a25cf1e HFS |
78 | if (skb->protocol == htons(ETH_P_IPV6)) |
79 | mtu = ip6_skb_dst_mtu(skb); | |
80 | else | |
81 | mtu = dst_mtu(skb_dst(skb)); | |
dd767856 | 82 | |
93efac3f HX |
83 | toobig = skb->len > mtu && !skb_is_gso(skb); |
84 | ||
ede64dd2 | 85 | if (toobig && xfrm6_local_dontfrag(skb->sk)) { |
dd767856 | 86 | xfrm6_local_rxpmtu(skb, mtu); |
215ab0f0 | 87 | kfree_skb(skb); |
dd767856 | 88 | return -EMSGSIZE; |
4ff2980b LW |
89 | } else if (toobig && xfrm6_noneed_fragment(skb)) { |
90 | skb->ignore_df = 1; | |
91 | goto skip_frag; | |
93efac3f | 92 | } else if (!skb->ignore_df && toobig && skb->sk) { |
628e341f | 93 | xfrm_local_error(skb, mtu); |
215ab0f0 | 94 | kfree_skb(skb); |
dd767856 SK |
95 | return -EMSGSIZE; |
96 | } | |
ad0081e4 | 97 | |
e57a3447 | 98 | if (toobig) |
7d8c6e39 EB |
99 | return ip6_fragment(net, sk, skb, |
100 | __xfrm6_output_finish); | |
93efac3f HX |
101 | |
102 | skip_frag: | |
2ab6096d | 103 | return xfrm_output(sk, skb); |
ad0081e4 DS |
104 | } |
105 | ||
ede2059d | 106 | int xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb) |
16a6677f | 107 | { |
29a26a56 | 108 | return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, |
28f8bfd1 | 109 | net, sk, skb, skb->dev, skb_dst(skb)->dev, |
29a26a56 | 110 | __xfrm6_output, |
5596732f | 111 | !(IP6CB(skb)->flags & IP6SKB_REROUTED)); |
16a6677f | 112 | } |