diff options
Diffstat (limited to 'net/ipv4/esp4.c')
-rw-r--r-- | net/ipv4/esp4.c | 185 |
1 files changed, 66 insertions, 119 deletions
diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index 73bfcae8af9c..09590f356086 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c | |||
@@ -12,13 +12,6 @@ | |||
12 | #include <net/protocol.h> | 12 | #include <net/protocol.h> |
13 | #include <net/udp.h> | 13 | #include <net/udp.h> |
14 | 14 | ||
15 | /* decapsulation data for use when post-processing */ | ||
16 | struct esp_decap_data { | ||
17 | xfrm_address_t saddr; | ||
18 | __u16 sport; | ||
19 | __u8 proto; | ||
20 | }; | ||
21 | |||
22 | static int esp_output(struct xfrm_state *x, struct sk_buff *skb) | 15 | static int esp_output(struct xfrm_state *x, struct sk_buff *skb) |
23 | { | 16 | { |
24 | int err; | 17 | int err; |
@@ -150,6 +143,10 @@ static int esp_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struc | |||
150 | int elen = skb->len - sizeof(struct ip_esp_hdr) - esp->conf.ivlen - alen; | 143 | int elen = skb->len - sizeof(struct ip_esp_hdr) - esp->conf.ivlen - alen; |
151 | int nfrags; | 144 | int nfrags; |
152 | int encap_len = 0; | 145 | int encap_len = 0; |
146 | u8 nexthdr[2]; | ||
147 | struct scatterlist *sg; | ||
148 | u8 workbuf[60]; | ||
149 | int padlen; | ||
153 | 150 | ||
154 | if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr))) | 151 | if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr))) |
155 | goto out; | 152 | goto out; |
@@ -185,122 +182,82 @@ static int esp_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struc | |||
185 | if (esp->conf.ivlen) | 182 | if (esp->conf.ivlen) |
186 | crypto_cipher_set_iv(esp->conf.tfm, esph->enc_data, crypto_tfm_alg_ivsize(esp->conf.tfm)); | 183 | crypto_cipher_set_iv(esp->conf.tfm, esph->enc_data, crypto_tfm_alg_ivsize(esp->conf.tfm)); |
187 | 184 | ||
188 | { | 185 | sg = &esp->sgbuf[0]; |
189 | u8 nexthdr[2]; | ||
190 | struct scatterlist *sg = &esp->sgbuf[0]; | ||
191 | u8 workbuf[60]; | ||
192 | int padlen; | ||
193 | |||
194 | if (unlikely(nfrags > ESP_NUM_FAST_SG)) { | ||
195 | sg = kmalloc(sizeof(struct scatterlist)*nfrags, GFP_ATOMIC); | ||
196 | if (!sg) | ||
197 | goto out; | ||
198 | } | ||
199 | skb_to_sgvec(skb, sg, sizeof(struct ip_esp_hdr) + esp->conf.ivlen, elen); | ||
200 | crypto_cipher_decrypt(esp->conf.tfm, sg, sg, elen); | ||
201 | if (unlikely(sg != &esp->sgbuf[0])) | ||
202 | kfree(sg); | ||
203 | |||
204 | if (skb_copy_bits(skb, skb->len-alen-2, nexthdr, 2)) | ||
205 | BUG(); | ||
206 | 186 | ||
207 | padlen = nexthdr[0]; | 187 | if (unlikely(nfrags > ESP_NUM_FAST_SG)) { |
208 | if (padlen+2 >= elen) | 188 | sg = kmalloc(sizeof(struct scatterlist)*nfrags, GFP_ATOMIC); |
189 | if (!sg) | ||
209 | goto out; | 190 | goto out; |
210 | |||
211 | /* ... check padding bits here. Silly. :-) */ | ||
212 | |||
213 | if (x->encap && decap && decap->decap_type) { | ||
214 | struct esp_decap_data *encap_data; | ||
215 | struct udphdr *uh = (struct udphdr *) (iph+1); | ||
216 | |||
217 | encap_data = (struct esp_decap_data *) (decap->decap_data); | ||
218 | encap_data->proto = 0; | ||
219 | |||
220 | switch (decap->decap_type) { | ||
221 | case UDP_ENCAP_ESPINUDP: | ||
222 | case UDP_ENCAP_ESPINUDP_NON_IKE: | ||
223 | encap_data->proto = AF_INET; | ||
224 | encap_data->saddr.a4 = iph->saddr; | ||
225 | encap_data->sport = uh->source; | ||
226 | encap_len = (void*)esph - (void*)uh; | ||
227 | break; | ||
228 | |||
229 | default: | ||
230 | goto out; | ||
231 | } | ||
232 | } | ||
233 | |||
234 | iph->protocol = nexthdr[1]; | ||
235 | pskb_trim(skb, skb->len - alen - padlen - 2); | ||
236 | memcpy(workbuf, skb->nh.raw, iph->ihl*4); | ||
237 | skb->h.raw = skb_pull(skb, sizeof(struct ip_esp_hdr) + esp->conf.ivlen); | ||
238 | skb->nh.raw += encap_len + sizeof(struct ip_esp_hdr) + esp->conf.ivlen; | ||
239 | memcpy(skb->nh.raw, workbuf, iph->ihl*4); | ||
240 | skb->nh.iph->tot_len = htons(skb->len); | ||
241 | } | 191 | } |
192 | skb_to_sgvec(skb, sg, sizeof(struct ip_esp_hdr) + esp->conf.ivlen, elen); | ||
193 | crypto_cipher_decrypt(esp->conf.tfm, sg, sg, elen); | ||
194 | if (unlikely(sg != &esp->sgbuf[0])) | ||
195 | kfree(sg); | ||
242 | 196 | ||
243 | return 0; | 197 | if (skb_copy_bits(skb, skb->len-alen-2, nexthdr, 2)) |
198 | BUG(); | ||
244 | 199 | ||
245 | out: | 200 | padlen = nexthdr[0]; |
246 | return -EINVAL; | 201 | if (padlen+2 >= elen) |
247 | } | 202 | goto out; |
248 | 203 | ||
249 | static int esp_post_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_buff *skb) | 204 | /* ... check padding bits here. Silly. :-) */ |
250 | { | ||
251 | |||
252 | if (x->encap) { | ||
253 | struct xfrm_encap_tmpl *encap; | ||
254 | struct esp_decap_data *decap_data; | ||
255 | 205 | ||
256 | encap = x->encap; | 206 | if (x->encap) { |
257 | decap_data = (struct esp_decap_data *)(decap->decap_data); | 207 | struct xfrm_encap_tmpl *encap = x->encap; |
208 | struct udphdr *uh; | ||
258 | 209 | ||
259 | /* first, make sure that the decap type == the encap type */ | ||
260 | if (encap->encap_type != decap->decap_type) | 210 | if (encap->encap_type != decap->decap_type) |
261 | return -EINVAL; | 211 | goto out; |
262 | 212 | ||
263 | switch (encap->encap_type) { | 213 | uh = (struct udphdr *)(iph + 1); |
264 | default: | 214 | encap_len = (void*)esph - (void*)uh; |
265 | case UDP_ENCAP_ESPINUDP: | 215 | |
266 | case UDP_ENCAP_ESPINUDP_NON_IKE: | 216 | /* |
267 | /* | 217 | * 1) if the NAT-T peer's IP or port changed then |
268 | * 1) if the NAT-T peer's IP or port changed then | 218 | * advertize the change to the keying daemon. |
269 | * advertize the change to the keying daemon. | 219 | * This is an inbound SA, so just compare |
270 | * This is an inbound SA, so just compare | 220 | * SRC ports. |
271 | * SRC ports. | 221 | */ |
272 | */ | 222 | if (iph->saddr != x->props.saddr.a4 || |
273 | if (decap_data->proto == AF_INET && | 223 | uh->source != encap->encap_sport) { |
274 | (decap_data->saddr.a4 != x->props.saddr.a4 || | 224 | xfrm_address_t ipaddr; |
275 | decap_data->sport != encap->encap_sport)) { | 225 | |
276 | xfrm_address_t ipaddr; | 226 | ipaddr.a4 = iph->saddr; |
277 | 227 | km_new_mapping(x, &ipaddr, uh->source); | |
278 | ipaddr.a4 = decap_data->saddr.a4; | 228 | |
279 | km_new_mapping(x, &ipaddr, decap_data->sport); | 229 | /* XXX: perhaps add an extra |
280 | 230 | * policy check here, to see | |
281 | /* XXX: perhaps add an extra | 231 | * if we should allow or |
282 | * policy check here, to see | 232 | * reject a packet from a |
283 | * if we should allow or | 233 | * different source |
284 | * reject a packet from a | 234 | * address/port. |
285 | * different source | ||
286 | * address/port. | ||
287 | */ | ||
288 | } | ||
289 | |||
290 | /* | ||
291 | * 2) ignore UDP/TCP checksums in case | ||
292 | * of NAT-T in Transport Mode, or | ||
293 | * perform other post-processing fixes | ||
294 | * as per * draft-ietf-ipsec-udp-encaps-06, | ||
295 | * section 3.1.2 | ||
296 | */ | 235 | */ |
297 | if (!x->props.mode) | ||
298 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
299 | |||
300 | break; | ||
301 | } | 236 | } |
237 | |||
238 | /* | ||
239 | * 2) ignore UDP/TCP checksums in case | ||
240 | * of NAT-T in Transport Mode, or | ||
241 | * perform other post-processing fixes | ||
242 | * as per draft-ietf-ipsec-udp-encaps-06, | ||
243 | * section 3.1.2 | ||
244 | */ | ||
245 | if (!x->props.mode) | ||
246 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
302 | } | 247 | } |
248 | |||
249 | iph->protocol = nexthdr[1]; | ||
250 | pskb_trim(skb, skb->len - alen - padlen - 2); | ||
251 | memcpy(workbuf, skb->nh.raw, iph->ihl*4); | ||
252 | skb->h.raw = skb_pull(skb, sizeof(struct ip_esp_hdr) + esp->conf.ivlen); | ||
253 | skb->nh.raw += encap_len + sizeof(struct ip_esp_hdr) + esp->conf.ivlen; | ||
254 | memcpy(skb->nh.raw, workbuf, iph->ihl*4); | ||
255 | skb->nh.iph->tot_len = htons(skb->len); | ||
256 | |||
303 | return 0; | 257 | return 0; |
258 | |||
259 | out: | ||
260 | return -EINVAL; | ||
304 | } | 261 | } |
305 | 262 | ||
306 | static u32 esp4_get_max_size(struct xfrm_state *x, int mtu) | 263 | static u32 esp4_get_max_size(struct xfrm_state *x, int mtu) |
@@ -458,7 +415,6 @@ static struct xfrm_type esp_type = | |||
458 | .destructor = esp_destroy, | 415 | .destructor = esp_destroy, |
459 | .get_max_size = esp4_get_max_size, | 416 | .get_max_size = esp4_get_max_size, |
460 | .input = esp_input, | 417 | .input = esp_input, |
461 | .post_input = esp_post_input, | ||
462 | .output = esp_output | 418 | .output = esp_output |
463 | }; | 419 | }; |
464 | 420 | ||
@@ -470,15 +426,6 @@ static struct net_protocol esp4_protocol = { | |||
470 | 426 | ||
471 | static int __init esp4_init(void) | 427 | static int __init esp4_init(void) |
472 | { | 428 | { |
473 | struct xfrm_decap_state decap; | ||
474 | |||
475 | if (sizeof(struct esp_decap_data) > | ||
476 | sizeof(decap.decap_data)) { | ||
477 | extern void decap_data_too_small(void); | ||
478 | |||
479 | decap_data_too_small(); | ||
480 | } | ||
481 | |||
482 | if (xfrm_register_type(&esp_type, AF_INET) < 0) { | 429 | if (xfrm_register_type(&esp_type, AF_INET) < 0) { |
483 | printk(KERN_INFO "ip esp init: can't add xfrm type\n"); | 430 | printk(KERN_INFO "ip esp init: can't add xfrm type\n"); |
484 | return -EAGAIN; | 431 | return -EAGAIN; |