aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/raw.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/raw.c')
-rw-r--r--net/ipv6/raw.c53
1 files changed, 40 insertions, 13 deletions
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index 5488ad0de4f6..3e2ad0a70412 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -34,6 +34,7 @@
34#include <linux/netfilter_ipv6.h> 34#include <linux/netfilter_ipv6.h>
35#include <asm/uaccess.h> 35#include <asm/uaccess.h>
36#include <asm/ioctls.h> 36#include <asm/ioctls.h>
37#include <asm/bug.h>
37 38
38#include <net/ip.h> 39#include <net/ip.h>
39#include <net/sock.h> 40#include <net/sock.h>
@@ -452,12 +453,15 @@ csum_copy_err:
452} 453}
453 454
454static int rawv6_push_pending_frames(struct sock *sk, struct flowi *fl, 455static int rawv6_push_pending_frames(struct sock *sk, struct flowi *fl,
455 struct raw6_sock *rp, int len) 456 struct raw6_sock *rp)
456{ 457{
458 struct inet_sock *inet = inet_sk(sk);
457 struct sk_buff *skb; 459 struct sk_buff *skb;
458 int err = 0; 460 int err = 0;
459 u16 *csum; 461 int offset;
462 int len;
460 u32 tmp_csum; 463 u32 tmp_csum;
464 u16 csum;
461 465
462 if (!rp->checksum) 466 if (!rp->checksum)
463 goto send; 467 goto send;
@@ -465,10 +469,10 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi *fl,
465 if ((skb = skb_peek(&sk->sk_write_queue)) == NULL) 469 if ((skb = skb_peek(&sk->sk_write_queue)) == NULL)
466 goto out; 470 goto out;
467 471
468 if (rp->offset + 1 < len) 472 offset = rp->offset;
469 csum = (u16 *)(skb->h.raw + rp->offset); 473 if (offset >= inet->cork.length - 1) {
470 else {
471 err = -EINVAL; 474 err = -EINVAL;
475 ip6_flush_pending_frames(sk);
472 goto out; 476 goto out;
473 } 477 }
474 478
@@ -479,23 +483,46 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi *fl,
479 */ 483 */
480 tmp_csum = skb->csum; 484 tmp_csum = skb->csum;
481 } else { 485 } else {
486 struct sk_buff *csum_skb = NULL;
482 tmp_csum = 0; 487 tmp_csum = 0;
483 488
484 skb_queue_walk(&sk->sk_write_queue, skb) { 489 skb_queue_walk(&sk->sk_write_queue, skb) {
485 tmp_csum = csum_add(tmp_csum, skb->csum); 490 tmp_csum = csum_add(tmp_csum, skb->csum);
491
492 if (csum_skb)
493 continue;
494
495 len = skb->len - (skb->h.raw - skb->data);
496 if (offset >= len) {
497 offset -= len;
498 continue;
499 }
500
501 csum_skb = skb;
486 } 502 }
503
504 skb = csum_skb;
487 } 505 }
488 506
507 offset += skb->h.raw - skb->data;
508 if (skb_copy_bits(skb, offset, &csum, 2))
509 BUG();
510
489 /* in case cksum was not initialized */ 511 /* in case cksum was not initialized */
490 if (unlikely(*csum)) 512 if (unlikely(csum))
491 tmp_csum = csum_sub(tmp_csum, *csum); 513 tmp_csum = csum_sub(tmp_csum, csum);
514
515 tmp_csum = csum_ipv6_magic(&fl->fl6_src,
516 &fl->fl6_dst,
517 inet->cork.length, fl->proto, tmp_csum);
518
519 if (tmp_csum == 0)
520 tmp_csum = -1;
492 521
493 *csum = csum_ipv6_magic(&fl->fl6_src, 522 csum = tmp_csum;
494 &fl->fl6_dst, 523 if (skb_store_bits(skb, offset, &csum, 2))
495 len, fl->proto, tmp_csum); 524 BUG();
496 525
497 if (*csum == 0)
498 *csum = -1;
499send: 526send:
500 err = ip6_push_pending_frames(sk); 527 err = ip6_push_pending_frames(sk);
501out: 528out:
@@ -774,7 +801,7 @@ back_from_confirm:
774 if (err) 801 if (err)
775 ip6_flush_pending_frames(sk); 802 ip6_flush_pending_frames(sk);
776 else if (!(msg->msg_flags & MSG_MORE)) 803 else if (!(msg->msg_flags & MSG_MORE))
777 err = rawv6_push_pending_frames(sk, &fl, rp, len); 804 err = rawv6_push_pending_frames(sk, &fl, rp);
778 } 805 }
779done: 806done:
780 ip6_dst_store(sk, dst, 807 ip6_dst_store(sk, dst,