]>
Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
1da177e4 LT |
2 | /* |
3 | * xfrm4_output.c - Common IPsec encapsulation code for IPv4. | |
4 | * Copyright (c) 2004 Herbert Xu <[email protected]> | |
1da177e4 LT |
5 | */ |
6 | ||
09b8f7a9 HX |
7 | #include <linux/if_ether.h> |
8 | #include <linux/kernel.h> | |
36cf9acf | 9 | #include <linux/module.h> |
1da177e4 | 10 | #include <linux/skbuff.h> |
16a6677f | 11 | #include <linux/netfilter_ipv4.h> |
36cf9acf | 12 | #include <net/dst.h> |
1da177e4 LT |
13 | #include <net/ip.h> |
14 | #include <net/xfrm.h> | |
15 | #include <net/icmp.h> | |
16 | ||
1da177e4 LT |
17 | static int xfrm4_tunnel_check_size(struct sk_buff *skb) |
18 | { | |
19 | int mtu, ret = 0; | |
1da177e4 LT |
20 | |
21 | if (IPCB(skb)->flags & IPSKB_XFRM_TUNNEL_SIZE) | |
22 | goto out; | |
23 | ||
60ff7467 | 24 | if (!(ip_hdr(skb)->frag_off & htons(IP_DF)) || skb->ignore_df) |
1da177e4 LT |
25 | goto out; |
26 | ||
5a25cf1e | 27 | mtu = dst_mtu(skb_dst(skb)); |
d77e38e6 | 28 | if ((!skb_is_gso(skb) && skb->len > mtu) || |
80f5974d DA |
29 | (skb_is_gso(skb) && |
30 | !skb_gso_validate_network_len(skb, ip_skb_dst_mtu(skb->sk, skb)))) { | |
ca064bd8 SK |
31 | skb->protocol = htons(ETH_P_IP); |
32 | ||
b00897b8 | 33 | if (skb->sk) |
628e341f | 34 | xfrm_local_error(skb, mtu); |
b00897b8 SK |
35 | else |
36 | icmp_send(skb, ICMP_DEST_UNREACH, | |
37 | ICMP_FRAG_NEEDED, htonl(mtu)); | |
1da177e4 LT |
38 | ret = -EMSGSIZE; |
39 | } | |
40 | out: | |
41 | return ret; | |
42 | } | |
43 | ||
36cf9acf HX |
44 | int xfrm4_extract_output(struct xfrm_state *x, struct sk_buff *skb) |
45 | { | |
46 | int err; | |
47 | ||
48 | err = xfrm4_tunnel_check_size(skb); | |
49 | if (err) | |
50 | return err; | |
51 | ||
60d5fcfb HX |
52 | XFRM_MODE_SKB_CB(skb)->protocol = ip_hdr(skb)->protocol; |
53 | ||
36cf9acf HX |
54 | return xfrm4_extract_header(skb); |
55 | } | |
56 | ||
7026b1dd | 57 | int xfrm4_output_finish(struct sock *sk, struct sk_buff *skb) |
09b8f7a9 | 58 | { |
5596732f | 59 | memset(IPCB(skb), 0, sizeof(*IPCB(skb))); |
5596732f SK |
60 | |
61 | #ifdef CONFIG_NETFILTER | |
62 | IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED; | |
63 | #endif | |
64 | ||
7026b1dd | 65 | return xfrm_output(sk, skb); |
5596732f SK |
66 | } |
67 | ||
0c4b51f0 | 68 | static int __xfrm4_output(struct net *net, struct sock *sk, struct sk_buff *skb) |
5596732f SK |
69 | { |
70 | struct xfrm_state *x = skb_dst(skb)->xfrm; | |
733a5fac FW |
71 | const struct xfrm_state_afinfo *afinfo; |
72 | int ret = -EAFNOSUPPORT; | |
5596732f | 73 | |
09b8f7a9 | 74 | #ifdef CONFIG_NETFILTER |
5596732f | 75 | if (!x) { |
09b8f7a9 | 76 | IPCB(skb)->flags |= IPSKB_REROUTED; |
13206b6b | 77 | return dst_output(net, sk, skb); |
09b8f7a9 | 78 | } |
862b82c6 | 79 | #endif |
09b8f7a9 | 80 | |
733a5fac | 81 | rcu_read_lock(); |
c9500d7b | 82 | afinfo = xfrm_state_afinfo_get_rcu(x->outer_mode.family); |
733a5fac FW |
83 | if (likely(afinfo)) |
84 | ret = afinfo->output_finish(sk, skb); | |
85 | else | |
86 | kfree_skb(skb); | |
87 | rcu_read_unlock(); | |
88 | ||
89 | return ret; | |
09b8f7a9 HX |
90 | } |
91 | ||
ede2059d | 92 | int xfrm4_output(struct net *net, struct sock *sk, struct sk_buff *skb) |
16a6677f | 93 | { |
29a26a56 EB |
94 | return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, |
95 | net, sk, skb, NULL, skb_dst(skb)->dev, | |
96 | __xfrm4_output, | |
48d5cad8 | 97 | !(IPCB(skb)->flags & IPSKB_REROUTED)); |
16a6677f | 98 | } |
628e341f HFS |
99 | |
100 | void xfrm4_local_error(struct sk_buff *skb, u32 mtu) | |
101 | { | |
102 | struct iphdr *hdr; | |
103 | ||
104 | hdr = skb->encapsulation ? inner_ip_hdr(skb) : ip_hdr(skb); | |
105 | ip_local_error(skb->sk, EMSGSIZE, hdr->daddr, | |
106 | inet_sk(skb->sk)->inet_dport, mtu); | |
107 | } |