diff options
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/ah6.c | 30 |
1 files changed, 24 insertions, 6 deletions
diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index 89298124c136..6c5f0949e0ab 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c | |||
@@ -530,6 +530,10 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb) | |||
530 | int nexthdr; | 530 | int nexthdr; |
531 | int nfrags; | 531 | int nfrags; |
532 | int err = -ENOMEM; | 532 | int err = -ENOMEM; |
533 | int seqhi_len = 0; | ||
534 | __be32 *seqhi; | ||
535 | int sglists = 0; | ||
536 | struct scatterlist *seqhisg; | ||
533 | 537 | ||
534 | if (!pskb_may_pull(skb, sizeof(struct ip_auth_hdr))) | 538 | if (!pskb_may_pull(skb, sizeof(struct ip_auth_hdr))) |
535 | goto out; | 539 | goto out; |
@@ -566,14 +570,22 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb) | |||
566 | 570 | ||
567 | skb_push(skb, hdr_len); | 571 | skb_push(skb, hdr_len); |
568 | 572 | ||
569 | 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); | ||
570 | if (!work_iph) | 580 | if (!work_iph) |
571 | goto out; | 581 | goto out; |
572 | 582 | ||
573 | auth_data = ah_tmp_auth(work_iph, hdr_len); | 583 | auth_data = ah_tmp_auth((u8 *)work_iph, hdr_len); |
574 | 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); | ||
575 | req = ah_tmp_req(ahash, icv); | 586 | req = ah_tmp_req(ahash, icv); |
576 | sg = ah_req_sg(ahash, req); | 587 | sg = ah_req_sg(ahash, req); |
588 | seqhisg = sg + nfrags; | ||
577 | 589 | ||
578 | memcpy(work_iph, ip6h, hdr_len); | 590 | memcpy(work_iph, ip6h, hdr_len); |
579 | memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len); | 591 | memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len); |
@@ -588,10 +600,16 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb) | |||
588 | ip6h->flow_lbl[2] = 0; | 600 | ip6h->flow_lbl[2] = 0; |
589 | ip6h->hop_limit = 0; | 601 | ip6h->hop_limit = 0; |
590 | 602 | ||
591 | sg_init_table(sg, nfrags); | 603 | sg_init_table(sg, nfrags + sglists); |
592 | 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 | } | ||
593 | 611 | ||
594 | ahash_request_set_crypt(req, sg, icv, skb->len); | 612 | ahash_request_set_crypt(req, sg, icv, skb->len + seqhi_len); |
595 | ahash_request_set_callback(req, 0, ah6_input_done, skb); | 613 | ahash_request_set_callback(req, 0, ah6_input_done, skb); |
596 | 614 | ||
597 | AH_SKB_CB(skb)->tmp = work_iph; | 615 | AH_SKB_CB(skb)->tmp = work_iph; |