aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/ah6.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/ah6.c')
-rw-r--r--net/ipv6/ah6.c125
1 files changed, 106 insertions, 19 deletions
diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c
index d31c0d6c0448..b0d83e8e4252 100644
--- a/net/ipv6/ah6.c
+++ b/net/ipv6/ah6.c
@@ -24,7 +24,6 @@
24 * This file is derived from net/ipv4/ah.c. 24 * This file is derived from net/ipv4/ah.c.
25 */ 25 */
26 26
27#include <linux/config.h>
28#include <linux/module.h> 27#include <linux/module.h>
29#include <net/ip.h> 28#include <net/ip.h>
30#include <net/ah.h> 29#include <net/ah.h>
@@ -75,6 +74,66 @@ bad:
75 return 0; 74 return 0;
76} 75}
77 76
77#ifdef CONFIG_IPV6_MIP6
78/**
79 * ipv6_rearrange_destopt - rearrange IPv6 destination options header
80 * @iph: IPv6 header
81 * @destopt: destionation options header
82 */
83static void ipv6_rearrange_destopt(struct ipv6hdr *iph, struct ipv6_opt_hdr *destopt)
84{
85 u8 *opt = (u8 *)destopt;
86 int len = ipv6_optlen(destopt);
87 int off = 0;
88 int optlen = 0;
89
90 off += 2;
91 len -= 2;
92
93 while (len > 0) {
94
95 switch (opt[off]) {
96
97 case IPV6_TLV_PAD0:
98 optlen = 1;
99 break;
100 default:
101 if (len < 2)
102 goto bad;
103 optlen = opt[off+1]+2;
104 if (len < optlen)
105 goto bad;
106
107 /* Rearrange the source address in @iph and the
108 * addresses in home address option for final source.
109 * See 11.3.2 of RFC 3775 for details.
110 */
111 if (opt[off] == IPV6_TLV_HAO) {
112 struct in6_addr final_addr;
113 struct ipv6_destopt_hao *hao;
114
115 hao = (struct ipv6_destopt_hao *)&opt[off];
116 if (hao->length != sizeof(hao->addr)) {
117 if (net_ratelimit())
118 printk(KERN_WARNING "destopt hao: invalid header length: %u\n", hao->length);
119 goto bad;
120 }
121 ipv6_addr_copy(&final_addr, &hao->addr);
122 ipv6_addr_copy(&hao->addr, &iph->saddr);
123 ipv6_addr_copy(&iph->saddr, &final_addr);
124 }
125 break;
126 }
127
128 off += optlen;
129 len -= optlen;
130 }
131 /* Note: ok if len == 0 */
132bad:
133 return;
134}
135#endif
136
78/** 137/**
79 * ipv6_rearrange_rthdr - rearrange IPv6 routing header 138 * ipv6_rearrange_rthdr - rearrange IPv6 routing header
80 * @iph: IPv6 header 139 * @iph: IPv6 header
@@ -114,7 +173,7 @@ static void ipv6_rearrange_rthdr(struct ipv6hdr *iph, struct ipv6_rt_hdr *rthdr)
114 ipv6_addr_copy(&iph->daddr, &final_addr); 173 ipv6_addr_copy(&iph->daddr, &final_addr);
115} 174}
116 175
117static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len) 176static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len, int dir)
118{ 177{
119 union { 178 union {
120 struct ipv6hdr *iph; 179 struct ipv6hdr *iph;
@@ -129,8 +188,12 @@ static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len)
129 188
130 while (exthdr.raw < end) { 189 while (exthdr.raw < end) {
131 switch (nexthdr) { 190 switch (nexthdr) {
132 case NEXTHDR_HOP:
133 case NEXTHDR_DEST: 191 case NEXTHDR_DEST:
192#ifdef CONFIG_IPV6_MIP6
193 if (dir == XFRM_POLICY_OUT)
194 ipv6_rearrange_destopt(iph, exthdr.opth);
195#endif
196 case NEXTHDR_HOP:
134 if (!zero_out_mutable_opts(exthdr.opth)) { 197 if (!zero_out_mutable_opts(exthdr.opth)) {
135 LIMIT_NETDEBUG( 198 LIMIT_NETDEBUG(
136 KERN_WARNING "overrun %sopts\n", 199 KERN_WARNING "overrun %sopts\n",
@@ -165,6 +228,9 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb)
165 u8 nexthdr; 228 u8 nexthdr;
166 char tmp_base[8]; 229 char tmp_base[8];
167 struct { 230 struct {
231#ifdef CONFIG_IPV6_MIP6
232 struct in6_addr saddr;
233#endif
168 struct in6_addr daddr; 234 struct in6_addr daddr;
169 char hdrs[0]; 235 char hdrs[0];
170 } *tmp_ext; 236 } *tmp_ext;
@@ -189,10 +255,15 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb)
189 err = -ENOMEM; 255 err = -ENOMEM;
190 goto error; 256 goto error;
191 } 257 }
258#ifdef CONFIG_IPV6_MIP6
259 memcpy(tmp_ext, &top_iph->saddr, extlen);
260#else
192 memcpy(tmp_ext, &top_iph->daddr, extlen); 261 memcpy(tmp_ext, &top_iph->daddr, extlen);
262#endif
193 err = ipv6_clear_mutable_options(top_iph, 263 err = ipv6_clear_mutable_options(top_iph,
194 extlen - sizeof(*tmp_ext) + 264 extlen - sizeof(*tmp_ext) +
195 sizeof(*top_iph)); 265 sizeof(*top_iph),
266 XFRM_POLICY_OUT);
196 if (err) 267 if (err)
197 goto error_free_iph; 268 goto error_free_iph;
198 } 269 }
@@ -214,13 +285,20 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb)
214 ah->spi = x->id.spi; 285 ah->spi = x->id.spi;
215 ah->seq_no = htonl(++x->replay.oseq); 286 ah->seq_no = htonl(++x->replay.oseq);
216 xfrm_aevent_doreplay(x); 287 xfrm_aevent_doreplay(x);
217 ahp->icv(ahp, skb, ah->auth_data); 288 err = ah_mac_digest(ahp, skb, ah->auth_data);
289 if (err)
290 goto error_free_iph;
291 memcpy(ah->auth_data, ahp->work_icv, ahp->icv_trunc_len);
218 292
219 err = 0; 293 err = 0;
220 294
221 memcpy(top_iph, tmp_base, sizeof(tmp_base)); 295 memcpy(top_iph, tmp_base, sizeof(tmp_base));
222 if (tmp_ext) { 296 if (tmp_ext) {
297#ifdef CONFIG_IPV6_MIP6
298 memcpy(&top_iph->saddr, tmp_ext, extlen);
299#else
223 memcpy(&top_iph->daddr, tmp_ext, extlen); 300 memcpy(&top_iph->daddr, tmp_ext, extlen);
301#endif
224error_free_iph: 302error_free_iph:
225 kfree(tmp_ext); 303 kfree(tmp_ext);
226 } 304 }
@@ -252,6 +330,7 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb)
252 u16 hdr_len; 330 u16 hdr_len;
253 u16 ah_hlen; 331 u16 ah_hlen;
254 int nexthdr; 332 int nexthdr;
333 int err = -EINVAL;
255 334
256 if (!pskb_may_pull(skb, sizeof(struct ip_auth_hdr))) 335 if (!pskb_may_pull(skb, sizeof(struct ip_auth_hdr)))
257 goto out; 336 goto out;
@@ -279,7 +358,7 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb)
279 if (!tmp_hdr) 358 if (!tmp_hdr)
280 goto out; 359 goto out;
281 memcpy(tmp_hdr, skb->nh.raw, hdr_len); 360 memcpy(tmp_hdr, skb->nh.raw, hdr_len);
282 if (ipv6_clear_mutable_options(skb->nh.ipv6h, hdr_len)) 361 if (ipv6_clear_mutable_options(skb->nh.ipv6h, hdr_len, XFRM_POLICY_IN))
283 goto free_out; 362 goto free_out;
284 skb->nh.ipv6h->priority = 0; 363 skb->nh.ipv6h->priority = 0;
285 skb->nh.ipv6h->flow_lbl[0] = 0; 364 skb->nh.ipv6h->flow_lbl[0] = 0;
@@ -293,8 +372,11 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb)
293 memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len); 372 memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len);
294 memset(ah->auth_data, 0, ahp->icv_trunc_len); 373 memset(ah->auth_data, 0, ahp->icv_trunc_len);
295 skb_push(skb, hdr_len); 374 skb_push(skb, hdr_len);
296 ahp->icv(ahp, skb, ah->auth_data); 375 err = ah_mac_digest(ahp, skb, ah->auth_data);
297 if (memcmp(ah->auth_data, auth_data, ahp->icv_trunc_len)) { 376 if (err)
377 goto free_out;
378 err = -EINVAL;
379 if (memcmp(ahp->work_icv, auth_data, ahp->icv_trunc_len)) {
298 LIMIT_NETDEBUG(KERN_WARNING "ipsec ah authentication error\n"); 380 LIMIT_NETDEBUG(KERN_WARNING "ipsec ah authentication error\n");
299 x->stats.integrity_failed++; 381 x->stats.integrity_failed++;
300 goto free_out; 382 goto free_out;
@@ -311,7 +393,7 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb)
311free_out: 393free_out:
312 kfree(tmp_hdr); 394 kfree(tmp_hdr);
313out: 395out:
314 return -EINVAL; 396 return err;
315} 397}
316 398
317static void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 399static void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
@@ -339,6 +421,7 @@ static int ah6_init_state(struct xfrm_state *x)
339{ 421{
340 struct ah_data *ahp = NULL; 422 struct ah_data *ahp = NULL;
341 struct xfrm_algo_desc *aalg_desc; 423 struct xfrm_algo_desc *aalg_desc;
424 struct crypto_hash *tfm;
342 425
343 if (!x->aalg) 426 if (!x->aalg)
344 goto error; 427 goto error;
@@ -356,24 +439,27 @@ static int ah6_init_state(struct xfrm_state *x)
356 439
357 ahp->key = x->aalg->alg_key; 440 ahp->key = x->aalg->alg_key;
358 ahp->key_len = (x->aalg->alg_key_len+7)/8; 441 ahp->key_len = (x->aalg->alg_key_len+7)/8;
359 ahp->tfm = crypto_alloc_tfm(x->aalg->alg_name, 0); 442 tfm = crypto_alloc_hash(x->aalg->alg_name, 0, CRYPTO_ALG_ASYNC);
360 if (!ahp->tfm) 443 if (IS_ERR(tfm))
444 goto error;
445
446 ahp->tfm = tfm;
447 if (crypto_hash_setkey(tfm, ahp->key, ahp->key_len))
361 goto error; 448 goto error;
362 ahp->icv = ah_hmac_digest;
363 449
364 /* 450 /*
365 * Lookup the algorithm description maintained by xfrm_algo, 451 * Lookup the algorithm description maintained by xfrm_algo,
366 * verify crypto transform properties, and store information 452 * verify crypto transform properties, and store information
367 * we need for AH processing. This lookup cannot fail here 453 * we need for AH processing. This lookup cannot fail here
368 * after a successful crypto_alloc_tfm(). 454 * after a successful crypto_alloc_hash().
369 */ 455 */
370 aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name, 0); 456 aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name, 0);
371 BUG_ON(!aalg_desc); 457 BUG_ON(!aalg_desc);
372 458
373 if (aalg_desc->uinfo.auth.icv_fullbits/8 != 459 if (aalg_desc->uinfo.auth.icv_fullbits/8 !=
374 crypto_tfm_alg_digestsize(ahp->tfm)) { 460 crypto_hash_digestsize(tfm)) {
375 printk(KERN_INFO "AH: %s digestsize %u != %hu\n", 461 printk(KERN_INFO "AH: %s digestsize %u != %hu\n",
376 x->aalg->alg_name, crypto_tfm_alg_digestsize(ahp->tfm), 462 x->aalg->alg_name, crypto_hash_digestsize(tfm),
377 aalg_desc->uinfo.auth.icv_fullbits/8); 463 aalg_desc->uinfo.auth.icv_fullbits/8);
378 goto error; 464 goto error;
379 } 465 }
@@ -388,7 +474,7 @@ static int ah6_init_state(struct xfrm_state *x)
388 goto error; 474 goto error;
389 475
390 x->props.header_len = XFRM_ALIGN8(sizeof(struct ipv6_auth_hdr) + ahp->icv_trunc_len); 476 x->props.header_len = XFRM_ALIGN8(sizeof(struct ipv6_auth_hdr) + ahp->icv_trunc_len);
391 if (x->props.mode) 477 if (x->props.mode == XFRM_MODE_TUNNEL)
392 x->props.header_len += sizeof(struct ipv6hdr); 478 x->props.header_len += sizeof(struct ipv6hdr);
393 x->data = ahp; 479 x->data = ahp;
394 480
@@ -397,7 +483,7 @@ static int ah6_init_state(struct xfrm_state *x)
397error: 483error:
398 if (ahp) { 484 if (ahp) {
399 kfree(ahp->work_icv); 485 kfree(ahp->work_icv);
400 crypto_free_tfm(ahp->tfm); 486 crypto_free_hash(ahp->tfm);
401 kfree(ahp); 487 kfree(ahp);
402 } 488 }
403 return -EINVAL; 489 return -EINVAL;
@@ -412,7 +498,7 @@ static void ah6_destroy(struct xfrm_state *x)
412 498
413 kfree(ahp->work_icv); 499 kfree(ahp->work_icv);
414 ahp->work_icv = NULL; 500 ahp->work_icv = NULL;
415 crypto_free_tfm(ahp->tfm); 501 crypto_free_hash(ahp->tfm);
416 ahp->tfm = NULL; 502 ahp->tfm = NULL;
417 kfree(ahp); 503 kfree(ahp);
418} 504}
@@ -425,7 +511,8 @@ static struct xfrm_type ah6_type =
425 .init_state = ah6_init_state, 511 .init_state = ah6_init_state,
426 .destructor = ah6_destroy, 512 .destructor = ah6_destroy,
427 .input = ah6_input, 513 .input = ah6_input,
428 .output = ah6_output 514 .output = ah6_output,
515 .hdr_offset = xfrm6_find_1stfragopt,
429}; 516};
430 517
431static struct inet6_protocol ah6_protocol = { 518static struct inet6_protocol ah6_protocol = {