Blame view

net/ipv4/xfrm4_output.c 3.36 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
  /*
   * xfrm4_output.c - Common IPsec encapsulation code for IPv4.
   * Copyright (c) 2004 Herbert Xu <herbert@gondor.apana.org.au>
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
4
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5
6
7
8
9
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * as published by the Free Software Foundation; either version
   * 2 of the License, or (at your option) any later version.
   */
16a6677fd   Patrick McHardy   [XFRM]: Netfilter...
10
  #include <linux/compiler.h>
09b8f7a93   Herbert Xu   [IPSEC]: Handle G...
11
12
  #include <linux/if_ether.h>
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
14
  #include <linux/skbuff.h>
  #include <linux/spinlock.h>
16a6677fd   Patrick McHardy   [XFRM]: Netfilter...
15
  #include <linux/netfilter_ipv4.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
17
18
  #include <net/ip.h>
  #include <net/xfrm.h>
  #include <net/icmp.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
20
21
22
  static int xfrm4_tunnel_check_size(struct sk_buff *skb)
  {
  	int mtu, ret = 0;
  	struct dst_entry *dst;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
25
26
27
  
  	if (IPCB(skb)->flags & IPSKB_XFRM_TUNNEL_SIZE)
  		goto out;
  
  	IPCB(skb)->flags |= IPSKB_XFRM_TUNNEL_SIZE;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
28

eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
29
  	if (!(ip_hdr(skb)->frag_off & htons(IP_DF)) || skb->local_df)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
32
33
34
35
36
37
38
39
40
  		goto out;
  
  	dst = skb->dst;
  	mtu = dst_mtu(dst);
  	if (skb->len > mtu) {
  		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
  		ret = -EMSGSIZE;
  	}
  out:
  	return ret;
  }
16a6677fd   Patrick McHardy   [XFRM]: Netfilter...
41
  static int xfrm4_output_one(struct sk_buff *skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
42
43
44
45
  {
  	struct dst_entry *dst = skb->dst;
  	struct xfrm_state *x = dst->xfrm;
  	int err;
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
46

84fa7933a   Patrick McHardy   [NET]: Replace CH...
47
48
  	if (skb->ip_summed == CHECKSUM_PARTIAL) {
  		err = skb_checksum_help(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49
50
51
  		if (err)
  			goto error_nolock;
  	}
7e49e6de3   Masahide NAKAMURA   [XFRM]: Add XFRM_...
52
  	if (x->props.mode == XFRM_MODE_TUNNEL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
53
54
55
56
  		err = xfrm4_tunnel_check_size(skb);
  		if (err)
  			goto error_nolock;
  	}
16a6677fd   Patrick McHardy   [XFRM]: Netfilter...
57
58
59
60
61
  	do {
  		spin_lock_bh(&x->lock);
  		err = xfrm_state_check(x, skb);
  		if (err)
  			goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62

eb878e845   Jamal Hadi Salim   [IPSEC]: output m...
63
  		err = x->mode->output(x, skb);
b59f45d0b   Herbert Xu   [IPSEC] xfrm: Abs...
64
65
  		if (err)
  			goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66

16a6677fd   Patrick McHardy   [XFRM]: Netfilter...
67
68
69
  		err = x->type->output(x, skb);
  		if (err)
  			goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70

16a6677fd   Patrick McHardy   [XFRM]: Netfilter...
71
72
  		x->curlft.bytes += skb->len;
  		x->curlft.packets++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73

16a6677fd   Patrick McHardy   [XFRM]: Netfilter...
74
  		spin_unlock_bh(&x->lock);
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
75

16a6677fd   Patrick McHardy   [XFRM]: Netfilter...
76
77
78
79
80
81
  		if (!(skb->dst = dst_pop(dst))) {
  			err = -EHOSTUNREACH;
  			goto error_nolock;
  		}
  		dst = skb->dst;
  		x = dst->xfrm;
7e49e6de3   Masahide NAKAMURA   [XFRM]: Add XFRM_...
82
  	} while (x && (x->props.mode != XFRM_MODE_TUNNEL));
16a6677fd   Patrick McHardy   [XFRM]: Netfilter...
83

3e3850e98   Patrick McHardy   [NETFILTER]: Fix ...
84
  	IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED;
16a6677fd   Patrick McHardy   [XFRM]: Netfilter...
85
  	err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
87
88
89
90
91
92
93
94
  
  out_exit:
  	return err;
  error:
  	spin_unlock_bh(&x->lock);
  error_nolock:
  	kfree_skb(skb);
  	goto out_exit;
  }
16a6677fd   Patrick McHardy   [XFRM]: Netfilter...
95

09b8f7a93   Herbert Xu   [IPSEC]: Handle G...
96
  static int xfrm4_output_finish2(struct sk_buff *skb)
16a6677fd   Patrick McHardy   [XFRM]: Netfilter...
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
  {
  	int err;
  
  	while (likely((err = xfrm4_output_one(skb)) == 0)) {
  		nf_reset(skb);
  
  		err = nf_hook(PF_INET, NF_IP_LOCAL_OUT, &skb, NULL,
  			      skb->dst->dev, dst_output);
  		if (unlikely(err != 1))
  			break;
  
  		if (!skb->dst->xfrm)
  			return dst_output(skb);
  
  		err = nf_hook(PF_INET, NF_IP_POST_ROUTING, &skb, NULL,
09b8f7a93   Herbert Xu   [IPSEC]: Handle G...
112
  			      skb->dst->dev, xfrm4_output_finish2);
16a6677fd   Patrick McHardy   [XFRM]: Netfilter...
113
114
115
116
117
118
  		if (unlikely(err != 1))
  			break;
  	}
  
  	return err;
  }
09b8f7a93   Herbert Xu   [IPSEC]: Handle G...
119
120
121
122
123
124
125
126
127
128
  static int xfrm4_output_finish(struct sk_buff *skb)
  {
  	struct sk_buff *segs;
  
  #ifdef CONFIG_NETFILTER
  	if (!skb->dst->xfrm) {
  		IPCB(skb)->flags |= IPSKB_REROUTED;
  		return dst_output(skb);
  	}
  #endif
89114afd4   Herbert Xu   [NET] gso: Add sk...
129
  	if (!skb_is_gso(skb))
09b8f7a93   Herbert Xu   [IPSEC]: Handle G...
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
  		return xfrm4_output_finish2(skb);
  
  	skb->protocol = htons(ETH_P_IP);
  	segs = skb_gso_segment(skb, 0);
  	kfree_skb(skb);
  	if (unlikely(IS_ERR(segs)))
  		return PTR_ERR(segs);
  
  	do {
  		struct sk_buff *nskb = segs->next;
  		int err;
  
  		segs->next = NULL;
  		err = xfrm4_output_finish2(segs);
  
  		if (unlikely(err)) {
  			while ((segs = nskb)) {
  				nskb = segs->next;
  				segs->next = NULL;
  				kfree_skb(segs);
  			}
  			return err;
  		}
  
  		segs = nskb;
  	} while (segs);
  
  	return 0;
  }
16a6677fd   Patrick McHardy   [XFRM]: Netfilter...
159
160
  int xfrm4_output(struct sk_buff *skb)
  {
48d5cad87   Patrick McHardy   [XFRM]: Fix SNAT-...
161
162
163
  	return NF_HOOK_COND(PF_INET, NF_IP_POST_ROUTING, skb, NULL, skb->dst->dev,
  			    xfrm4_output_finish,
  			    !(IPCB(skb)->flags & IPSKB_REROUTED));
16a6677fd   Patrick McHardy   [XFRM]: Netfilter...
164
  }