diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2005-11-10 16:01:24 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2005-11-10 16:01:24 -0500 |
commit | fb286bb2990a107009dbf25f6ffebeb7df77f9be (patch) | |
tree | 0eede2c37f1b3831e59601933eebf6b82be75ffc /net/ipv6 | |
parent | 1064e944d03eb7a72c0fa11236d5e69cfd877a71 (diff) |
[NET]: Detect hardware rx checksum faults correctly
Here is the patch that introduces the generic skb_checksum_complete
which also checks for hardware RX checksum faults. If that happens,
it'll call netdev_rx_csum_fault which currently prints out a stack
trace with the device name. In future it can turn off RX checksum.
I've converted every spot under net/ that does RX checksum checks to
use skb_checksum_complete or __skb_checksum_complete with the
exceptions of:
* Those places where checksums are done bit by bit. These will call
netdev_rx_csum_fault directly.
* The following have not been completely checked/converted:
ipmr
ip_vs
netfilter
dccp
This patch is based on patches and suggestions from Stephen Hemminger
and David S. Miller.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/icmp.c | 21 | ||||
-rw-r--r-- | net/ipv6/raw.c | 42 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 20 | ||||
-rw-r--r-- | net/ipv6/udp.c | 25 |
4 files changed, 44 insertions, 64 deletions
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 23e540365a14..1bdf0fb8bf8a 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c | |||
@@ -585,17 +585,16 @@ static int icmpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp) | |||
585 | daddr = &skb->nh.ipv6h->daddr; | 585 | daddr = &skb->nh.ipv6h->daddr; |
586 | 586 | ||
587 | /* Perform checksum. */ | 587 | /* Perform checksum. */ |
588 | if (skb->ip_summed == CHECKSUM_HW) { | 588 | switch (skb->ip_summed) { |
589 | skb->ip_summed = CHECKSUM_UNNECESSARY; | 589 | case CHECKSUM_HW: |
590 | if (csum_ipv6_magic(saddr, daddr, skb->len, IPPROTO_ICMPV6, | 590 | if (!csum_ipv6_magic(saddr, daddr, skb->len, IPPROTO_ICMPV6, |
591 | skb->csum)) { | 591 | skb->csum)) |
592 | LIMIT_NETDEBUG(KERN_DEBUG "ICMPv6 hw checksum failed\n"); | 592 | break; |
593 | skb->ip_summed = CHECKSUM_NONE; | 593 | /* fall through */ |
594 | } | 594 | case CHECKSUM_NONE: |
595 | } | 595 | skb->csum = ~csum_ipv6_magic(saddr, daddr, skb->len, |
596 | if (skb->ip_summed == CHECKSUM_NONE) { | 596 | IPPROTO_ICMPV6, 0); |
597 | if (csum_ipv6_magic(saddr, daddr, skb->len, IPPROTO_ICMPV6, | 597 | if (__skb_checksum_complete(skb)) { |
598 | skb_checksum(skb, 0, skb->len, 0))) { | ||
599 | LIMIT_NETDEBUG(KERN_DEBUG "ICMPv6 checksum failed [%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x > %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]\n", | 598 | LIMIT_NETDEBUG(KERN_DEBUG "ICMPv6 checksum failed [%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x > %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]\n", |
600 | NIP6(*saddr), NIP6(*daddr)); | 599 | NIP6(*saddr), NIP6(*daddr)); |
601 | goto discard_it; | 600 | goto discard_it; |
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 651c79b41eeb..8e9628f1c4c5 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c | |||
@@ -298,13 +298,10 @@ void rawv6_err(struct sock *sk, struct sk_buff *skb, | |||
298 | static inline int rawv6_rcv_skb(struct sock * sk, struct sk_buff * skb) | 298 | static inline int rawv6_rcv_skb(struct sock * sk, struct sk_buff * skb) |
299 | { | 299 | { |
300 | if ((raw6_sk(sk)->checksum || sk->sk_filter) && | 300 | if ((raw6_sk(sk)->checksum || sk->sk_filter) && |
301 | skb->ip_summed != CHECKSUM_UNNECESSARY) { | 301 | skb_checksum_complete(skb)) { |
302 | if ((unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum))) { | 302 | /* FIXME: increment a raw6 drops counter here */ |
303 | /* FIXME: increment a raw6 drops counter here */ | 303 | kfree_skb(skb); |
304 | kfree_skb(skb); | 304 | return 0; |
305 | return 0; | ||
306 | } | ||
307 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
308 | } | 305 | } |
309 | 306 | ||
310 | /* Charge it to the socket. */ | 307 | /* Charge it to the socket. */ |
@@ -337,32 +334,25 @@ int rawv6_rcv(struct sock *sk, struct sk_buff *skb) | |||
337 | if (!rp->checksum) | 334 | if (!rp->checksum) |
338 | skb->ip_summed = CHECKSUM_UNNECESSARY; | 335 | skb->ip_summed = CHECKSUM_UNNECESSARY; |
339 | 336 | ||
340 | if (skb->ip_summed != CHECKSUM_UNNECESSARY) { | 337 | if (skb->ip_summed == CHECKSUM_HW) { |
341 | if (skb->ip_summed == CHECKSUM_HW) { | 338 | skb_postpull_rcsum(skb, skb->nh.raw, |
342 | skb_postpull_rcsum(skb, skb->nh.raw, | 339 | skb->h.raw - skb->nh.raw); |
343 | skb->h.raw - skb->nh.raw); | 340 | if (!csum_ipv6_magic(&skb->nh.ipv6h->saddr, |
341 | &skb->nh.ipv6h->daddr, | ||
342 | skb->len, inet->num, skb->csum)) | ||
344 | skb->ip_summed = CHECKSUM_UNNECESSARY; | 343 | skb->ip_summed = CHECKSUM_UNNECESSARY; |
345 | if (csum_ipv6_magic(&skb->nh.ipv6h->saddr, | ||
346 | &skb->nh.ipv6h->daddr, | ||
347 | skb->len, inet->num, skb->csum)) { | ||
348 | LIMIT_NETDEBUG(KERN_DEBUG "raw v6 hw csum failure.\n"); | ||
349 | skb->ip_summed = CHECKSUM_NONE; | ||
350 | } | ||
351 | } | ||
352 | if (skb->ip_summed == CHECKSUM_NONE) | ||
353 | skb->csum = ~csum_ipv6_magic(&skb->nh.ipv6h->saddr, | ||
354 | &skb->nh.ipv6h->daddr, | ||
355 | skb->len, inet->num, 0); | ||
356 | } | 344 | } |
345 | if (skb->ip_summed != CHECKSUM_UNNECESSARY) | ||
346 | skb->csum = ~csum_ipv6_magic(&skb->nh.ipv6h->saddr, | ||
347 | &skb->nh.ipv6h->daddr, | ||
348 | skb->len, inet->num, 0); | ||
357 | 349 | ||
358 | if (inet->hdrincl) { | 350 | if (inet->hdrincl) { |
359 | if (skb->ip_summed != CHECKSUM_UNNECESSARY && | 351 | if (skb_checksum_complete(skb)) { |
360 | (unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum))) { | ||
361 | /* FIXME: increment a raw6 drops counter here */ | 352 | /* FIXME: increment a raw6 drops counter here */ |
362 | kfree_skb(skb); | 353 | kfree_skb(skb); |
363 | return 0; | 354 | return 0; |
364 | } | 355 | } |
365 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
366 | } | 356 | } |
367 | 357 | ||
368 | rawv6_rcv_skb(sk, skb); | 358 | rawv6_rcv_skb(sk, skb); |
@@ -407,7 +397,7 @@ static int rawv6_recvmsg(struct kiocb *iocb, struct sock *sk, | |||
407 | if (skb->ip_summed==CHECKSUM_UNNECESSARY) { | 397 | if (skb->ip_summed==CHECKSUM_UNNECESSARY) { |
408 | err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); | 398 | err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); |
409 | } else if (msg->msg_flags&MSG_TRUNC) { | 399 | } else if (msg->msg_flags&MSG_TRUNC) { |
410 | if ((unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum))) | 400 | if (__skb_checksum_complete(skb)) |
411 | goto csum_copy_err; | 401 | goto csum_copy_err; |
412 | err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); | 402 | err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); |
413 | } else { | 403 | } else { |
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index d746d3b27efb..62c0e5bd931c 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c | |||
@@ -1401,20 +1401,18 @@ out: | |||
1401 | static int tcp_v6_checksum_init(struct sk_buff *skb) | 1401 | static int tcp_v6_checksum_init(struct sk_buff *skb) |
1402 | { | 1402 | { |
1403 | if (skb->ip_summed == CHECKSUM_HW) { | 1403 | if (skb->ip_summed == CHECKSUM_HW) { |
1404 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
1405 | if (!tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr, | 1404 | if (!tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr, |
1406 | &skb->nh.ipv6h->daddr,skb->csum)) | 1405 | &skb->nh.ipv6h->daddr,skb->csum)) { |
1406 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
1407 | return 0; | 1407 | return 0; |
1408 | LIMIT_NETDEBUG(KERN_DEBUG "hw tcp v6 csum failed\n"); | 1408 | } |
1409 | } | 1409 | } |
1410 | |||
1411 | skb->csum = ~tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr, | ||
1412 | &skb->nh.ipv6h->daddr, 0); | ||
1413 | |||
1410 | if (skb->len <= 76) { | 1414 | if (skb->len <= 76) { |
1411 | if (tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr, | 1415 | return __skb_checksum_complete(skb); |
1412 | &skb->nh.ipv6h->daddr,skb_checksum(skb, 0, skb->len, 0))) | ||
1413 | return -1; | ||
1414 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
1415 | } else { | ||
1416 | skb->csum = ~tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr, | ||
1417 | &skb->nh.ipv6h->daddr,0); | ||
1418 | } | 1416 | } |
1419 | return 0; | 1417 | return 0; |
1420 | } | 1418 | } |
@@ -1575,7 +1573,7 @@ static int tcp_v6_rcv(struct sk_buff **pskb, unsigned int *nhoffp) | |||
1575 | goto discard_it; | 1573 | goto discard_it; |
1576 | 1574 | ||
1577 | if ((skb->ip_summed != CHECKSUM_UNNECESSARY && | 1575 | if ((skb->ip_summed != CHECKSUM_UNNECESSARY && |
1578 | tcp_v6_checksum_init(skb) < 0)) | 1576 | tcp_v6_checksum_init(skb))) |
1579 | goto bad_packet; | 1577 | goto bad_packet; |
1580 | 1578 | ||
1581 | th = skb->h.th; | 1579 | th = skb->h.th; |
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index bf9519341fd3..e671153b47b2 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c | |||
@@ -248,7 +248,7 @@ try_again: | |||
248 | err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov, | 248 | err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov, |
249 | copied); | 249 | copied); |
250 | } else if (msg->msg_flags&MSG_TRUNC) { | 250 | } else if (msg->msg_flags&MSG_TRUNC) { |
251 | if ((unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum))) | 251 | if (__skb_checksum_complete(skb)) |
252 | goto csum_copy_err; | 252 | goto csum_copy_err; |
253 | err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov, | 253 | err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov, |
254 | copied); | 254 | copied); |
@@ -363,13 +363,10 @@ static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb) | |||
363 | return -1; | 363 | return -1; |
364 | } | 364 | } |
365 | 365 | ||
366 | if (skb->ip_summed != CHECKSUM_UNNECESSARY) { | 366 | if (skb_checksum_complete(skb)) { |
367 | if ((unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum))) { | 367 | UDP6_INC_STATS_BH(UDP_MIB_INERRORS); |
368 | UDP6_INC_STATS_BH(UDP_MIB_INERRORS); | 368 | kfree_skb(skb); |
369 | kfree_skb(skb); | 369 | return 0; |
370 | return 0; | ||
371 | } | ||
372 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
373 | } | 370 | } |
374 | 371 | ||
375 | if (sock_queue_rcv_skb(sk,skb)<0) { | 372 | if (sock_queue_rcv_skb(sk,skb)<0) { |
@@ -491,13 +488,10 @@ static int udpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp) | |||
491 | uh = skb->h.uh; | 488 | uh = skb->h.uh; |
492 | } | 489 | } |
493 | 490 | ||
494 | if (skb->ip_summed==CHECKSUM_HW) { | 491 | if (skb->ip_summed == CHECKSUM_HW && |
492 | !csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, skb->csum)) | ||
495 | skb->ip_summed = CHECKSUM_UNNECESSARY; | 493 | skb->ip_summed = CHECKSUM_UNNECESSARY; |
496 | if (csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, skb->csum)) { | 494 | |
497 | LIMIT_NETDEBUG(KERN_DEBUG "udp v6 hw csum failure.\n"); | ||
498 | skb->ip_summed = CHECKSUM_NONE; | ||
499 | } | ||
500 | } | ||
501 | if (skb->ip_summed != CHECKSUM_UNNECESSARY) | 495 | if (skb->ip_summed != CHECKSUM_UNNECESSARY) |
502 | skb->csum = ~csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, 0); | 496 | skb->csum = ~csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, 0); |
503 | 497 | ||
@@ -521,8 +515,7 @@ static int udpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp) | |||
521 | if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) | 515 | if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) |
522 | goto discard; | 516 | goto discard; |
523 | 517 | ||
524 | if (skb->ip_summed != CHECKSUM_UNNECESSARY && | 518 | if (skb_checksum_complete(skb)) |
525 | (unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum))) | ||
526 | goto discard; | 519 | goto discard; |
527 | UDP6_INC_STATS_BH(UDP_MIB_NOPORTS); | 520 | UDP6_INC_STATS_BH(UDP_MIB_NOPORTS); |
528 | 521 | ||