diff options
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/ah6.c | 56 |
1 files changed, 45 insertions, 11 deletions
diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index 81e496a2e008..6c5f0949e0ab 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c | |||
@@ -346,6 +346,10 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb) | |||
346 | struct ip_auth_hdr *ah; | 346 | struct ip_auth_hdr *ah; |
347 | struct ah_data *ahp; | 347 | struct ah_data *ahp; |
348 | struct tmp_ext *iph_ext; | 348 | struct tmp_ext *iph_ext; |
349 | int seqhi_len = 0; | ||
350 | __be32 *seqhi; | ||
351 | int sglists = 0; | ||
352 | struct scatterlist *seqhisg; | ||
349 | 353 | ||
350 | ahp = x->data; | 354 | ahp = x->data; |
351 | ahash = ahp->ahash; | 355 | ahash = ahp->ahash; |
@@ -359,15 +363,22 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb) | |||
359 | if (extlen) | 363 | if (extlen) |
360 | extlen += sizeof(*iph_ext); | 364 | extlen += sizeof(*iph_ext); |
361 | 365 | ||
366 | if (x->props.flags & XFRM_STATE_ESN) { | ||
367 | sglists = 1; | ||
368 | seqhi_len = sizeof(*seqhi); | ||
369 | } | ||
362 | err = -ENOMEM; | 370 | err = -ENOMEM; |
363 | iph_base = ah_alloc_tmp(ahash, nfrags, IPV6HDR_BASELEN + extlen); | 371 | iph_base = ah_alloc_tmp(ahash, nfrags + sglists, IPV6HDR_BASELEN + |
372 | extlen + seqhi_len); | ||
364 | if (!iph_base) | 373 | if (!iph_base) |
365 | goto out; | 374 | goto out; |
366 | 375 | ||
367 | iph_ext = ah_tmp_ext(iph_base); | 376 | iph_ext = ah_tmp_ext(iph_base); |
368 | icv = ah_tmp_icv(ahash, iph_ext, extlen); | 377 | seqhi = (__be32 *)((char *)iph_ext + extlen); |
378 | icv = ah_tmp_icv(ahash, seqhi, seqhi_len); | ||
369 | req = ah_tmp_req(ahash, icv); | 379 | req = ah_tmp_req(ahash, icv); |
370 | sg = ah_req_sg(ahash, req); | 380 | sg = ah_req_sg(ahash, req); |
381 | seqhisg = sg + nfrags; | ||
371 | 382 | ||
372 | ah = ip_auth_hdr(skb); | 383 | ah = ip_auth_hdr(skb); |
373 | memset(ah->auth_data, 0, ahp->icv_trunc_len); | 384 | memset(ah->auth_data, 0, ahp->icv_trunc_len); |
@@ -411,10 +422,15 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb) | |||
411 | ah->spi = x->id.spi; | 422 | ah->spi = x->id.spi; |
412 | ah->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low); | 423 | ah->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low); |
413 | 424 | ||
414 | sg_init_table(sg, nfrags); | 425 | sg_init_table(sg, nfrags + sglists); |
415 | skb_to_sgvec(skb, sg, 0, skb->len); | 426 | skb_to_sgvec_nomark(skb, sg, 0, skb->len); |
416 | 427 | ||
417 | ahash_request_set_crypt(req, sg, icv, skb->len); | 428 | if (x->props.flags & XFRM_STATE_ESN) { |
429 | /* Attach seqhi sg right after packet payload */ | ||
430 | *seqhi = htonl(XFRM_SKB_CB(skb)->seq.output.hi); | ||
431 | sg_set_buf(seqhisg, seqhi, seqhi_len); | ||
432 | } | ||
433 | ahash_request_set_crypt(req, sg, icv, skb->len + seqhi_len); | ||
418 | ahash_request_set_callback(req, 0, ah6_output_done, skb); | 434 | ahash_request_set_callback(req, 0, ah6_output_done, skb); |
419 | 435 | ||
420 | AH_SKB_CB(skb)->tmp = iph_base; | 436 | AH_SKB_CB(skb)->tmp = iph_base; |
@@ -514,6 +530,10 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb) | |||
514 | int nexthdr; | 530 | int nexthdr; |
515 | int nfrags; | 531 | int nfrags; |
516 | int err = -ENOMEM; | 532 | int err = -ENOMEM; |
533 | int seqhi_len = 0; | ||
534 | __be32 *seqhi; | ||
535 | int sglists = 0; | ||
536 | struct scatterlist *seqhisg; | ||
517 | 537 | ||
518 | if (!pskb_may_pull(skb, sizeof(struct ip_auth_hdr))) | 538 | if (!pskb_may_pull(skb, sizeof(struct ip_auth_hdr))) |
519 | goto out; | 539 | goto out; |
@@ -550,14 +570,22 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb) | |||
550 | 570 | ||
551 | skb_push(skb, hdr_len); | 571 | skb_push(skb, hdr_len); |
552 | 572 | ||
553 | work_iph = ah_alloc_tmp(ahash, nfrags, hdr_len + ahp->icv_trunc_len); | 573 | if (x->props.flags & XFRM_STATE_ESN) { |
574 | sglists = 1; | ||
575 | seqhi_len = sizeof(*seqhi); | ||
576 | } | ||
577 | |||
578 | work_iph = ah_alloc_tmp(ahash, nfrags + sglists, hdr_len + | ||
579 | ahp->icv_trunc_len + seqhi_len); | ||
554 | if (!work_iph) | 580 | if (!work_iph) |
555 | goto out; | 581 | goto out; |
556 | 582 | ||
557 | auth_data = ah_tmp_auth(work_iph, hdr_len); | 583 | auth_data = ah_tmp_auth((u8 *)work_iph, hdr_len); |
558 | icv = ah_tmp_icv(ahash, auth_data, ahp->icv_trunc_len); | 584 | seqhi = (__be32 *)(auth_data + ahp->icv_trunc_len); |
585 | icv = ah_tmp_icv(ahash, seqhi, seqhi_len); | ||
559 | req = ah_tmp_req(ahash, icv); | 586 | req = ah_tmp_req(ahash, icv); |
560 | sg = ah_req_sg(ahash, req); | 587 | sg = ah_req_sg(ahash, req); |
588 | seqhisg = sg + nfrags; | ||
561 | 589 | ||
562 | memcpy(work_iph, ip6h, hdr_len); | 590 | memcpy(work_iph, ip6h, hdr_len); |
563 | memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len); | 591 | memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len); |
@@ -572,10 +600,16 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb) | |||
572 | ip6h->flow_lbl[2] = 0; | 600 | ip6h->flow_lbl[2] = 0; |
573 | ip6h->hop_limit = 0; | 601 | ip6h->hop_limit = 0; |
574 | 602 | ||
575 | sg_init_table(sg, nfrags); | 603 | sg_init_table(sg, nfrags + sglists); |
576 | skb_to_sgvec(skb, sg, 0, skb->len); | 604 | skb_to_sgvec_nomark(skb, sg, 0, skb->len); |
605 | |||
606 | if (x->props.flags & XFRM_STATE_ESN) { | ||
607 | /* Attach seqhi sg right after packet payload */ | ||
608 | *seqhi = XFRM_SKB_CB(skb)->seq.input.hi; | ||
609 | sg_set_buf(seqhisg, seqhi, seqhi_len); | ||
610 | } | ||
577 | 611 | ||
578 | ahash_request_set_crypt(req, sg, icv, skb->len); | 612 | ahash_request_set_crypt(req, sg, icv, skb->len + seqhi_len); |
579 | ahash_request_set_callback(req, 0, ah6_input_done, skb); | 613 | ahash_request_set_callback(req, 0, ah6_input_done, skb); |
580 | 614 | ||
581 | AH_SKB_CB(skb)->tmp = work_iph; | 615 | AH_SKB_CB(skb)->tmp = work_iph; |