1 | /* |
2 | * xfrm4_output.c - Common IPsec encapsulation code for IPv4. |
3 | * Copyright (c) 2004 Herbert Xu <herbert@gondor.apana.org.au> |
4 | * |
5 | * This program is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU General Public License |
7 | * as published by the Free Software Foundation; either version |
8 | * 2 of the License, or (at your option) any later version. |
9 | */ |
10 | |
11 | #include <linux/if_ether.h> |
12 | #include <linux/kernel.h> |
13 | #include <linux/module.h> |
14 | #include <linux/skbuff.h> |
15 | #include <linux/netfilter_ipv4.h> |
16 | #include <net/dst.h> |
17 | #include <net/ip.h> |
18 | #include <net/xfrm.h> |
19 | #include <net/icmp.h> |
20 | |
21 | static int xfrm4_tunnel_check_size(struct sk_buff *skb) |
22 | { |
23 | int mtu, ret = 0; |
24 | |
25 | if (IPCB(skb)->flags & IPSKB_XFRM_TUNNEL_SIZE) |
26 | goto out; |
27 | |
28 | if (!(ip_hdr(skb)->frag_off & htons(IP_DF)) || skb->ignore_df) |
29 | goto out; |
30 | |
31 | mtu = dst_mtu(skb_dst(skb)); |
32 | if ((!skb_is_gso(skb) && skb->len > mtu) || |
33 | (skb_is_gso(skb) && |
34 | !skb_gso_validate_network_len(skb, ip_skb_dst_mtu(skb->sk, skb)))) { |
35 | skb->protocol = htons(ETH_P_IP); |
36 | |
37 | if (skb->sk) |
38 | xfrm_local_error(skb, mtu); |
39 | else |
40 | icmp_send(skb, ICMP_DEST_UNREACH, |
41 | ICMP_FRAG_NEEDED, htonl(mtu)); |
42 | ret = -EMSGSIZE; |
43 | } |
44 | out: |
45 | return ret; |
46 | } |
47 | |
48 | int (struct xfrm_state *x, struct sk_buff *skb) |
49 | { |
50 | int err; |
51 | |
52 | err = xfrm4_tunnel_check_size(skb); |
53 | if (err) |
54 | return err; |
55 | |
56 | XFRM_MODE_SKB_CB(skb)->protocol = ip_hdr(skb)->protocol; |
57 | |
58 | return xfrm4_extract_header(skb); |
59 | } |
60 | |
61 | int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb) |
62 | { |
63 | int err; |
64 | |
65 | err = xfrm_inner_extract_output(x, skb); |
66 | if (err) |
67 | return err; |
68 | |
69 | IPCB(skb)->flags |= IPSKB_XFRM_TUNNEL_SIZE; |
70 | skb->protocol = htons(ETH_P_IP); |
71 | |
72 | return x->outer_mode->output2(x, skb); |
73 | } |
74 | EXPORT_SYMBOL(xfrm4_prepare_output); |
75 | |
76 | int xfrm4_output_finish(struct sock *sk, struct sk_buff *skb) |
77 | { |
78 | memset(IPCB(skb), 0, sizeof(*IPCB(skb))); |
79 | |
80 | #ifdef CONFIG_NETFILTER |
81 | IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED; |
82 | #endif |
83 | |
84 | return xfrm_output(sk, skb); |
85 | } |
86 | |
87 | static int __xfrm4_output(struct net *net, struct sock *sk, struct sk_buff *skb) |
88 | { |
89 | struct xfrm_state *x = skb_dst(skb)->xfrm; |
90 | |
91 | #ifdef CONFIG_NETFILTER |
92 | if (!x) { |
93 | IPCB(skb)->flags |= IPSKB_REROUTED; |
94 | return dst_output(net, sk, skb); |
95 | } |
96 | #endif |
97 | |
98 | return x->outer_mode->afinfo->output_finish(sk, skb); |
99 | } |
100 | |
101 | int xfrm4_output(struct net *net, struct sock *sk, struct sk_buff *skb) |
102 | { |
103 | return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, |
104 | net, sk, skb, NULL, skb_dst(skb)->dev, |
105 | __xfrm4_output, |
106 | !(IPCB(skb)->flags & IPSKB_REROUTED)); |
107 | } |
108 | |
109 | void xfrm4_local_error(struct sk_buff *skb, u32 mtu) |
110 | { |
111 | struct iphdr *hdr; |
112 | |
113 | hdr = skb->encapsulation ? inner_ip_hdr(skb) : ip_hdr(skb); |
114 | ip_local_error(skb->sk, EMSGSIZE, hdr->daddr, |
115 | inet_sk(skb->sk)->inet_dport, mtu); |
116 | } |
117 | |