aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2005-11-10 16:01:24 -0500
committerDavid S. Miller <davem@davemloft.net>2005-11-10 16:01:24 -0500
commitfb286bb2990a107009dbf25f6ffebeb7df77f9be (patch)
tree0eede2c37f1b3831e59601933eebf6b82be75ffc /net/ipv4
parent1064e944d03eb7a72c0fa11236d5e69cfd877a71 (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/ipv4')
-rw-r--r--net/ipv4/icmp.c6
-rw-r--r--net/ipv4/igmp.c19
-rw-r--r--net/ipv4/ip_gre.c15
-rw-r--r--net/ipv4/netfilter/ip_conntrack_proto_icmp.c11
-rw-r--r--net/ipv4/tcp_ipv4.c24
-rw-r--r--net/ipv4/udp.c7
6 files changed, 40 insertions, 42 deletions
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index 175e093ec564..e3eceecd0496 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -934,11 +934,11 @@ int icmp_rcv(struct sk_buff *skb)
934 case CHECKSUM_HW: 934 case CHECKSUM_HW:
935 if (!(u16)csum_fold(skb->csum)) 935 if (!(u16)csum_fold(skb->csum))
936 break; 936 break;
937 LIMIT_NETDEBUG(KERN_DEBUG "icmp v4 hw csum failure\n"); 937 /* fall through */
938 case CHECKSUM_NONE: 938 case CHECKSUM_NONE:
939 if ((u16)csum_fold(skb_checksum(skb, 0, skb->len, 0))) 939 skb->csum = 0;
940 if (__skb_checksum_complete(skb))
940 goto error; 941 goto error;
941 default:;
942 } 942 }
943 943
944 if (!pskb_pull(skb, sizeof(struct icmphdr))) 944 if (!pskb_pull(skb, sizeof(struct icmphdr)))
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index c6247fc84060..c04607b49212 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -872,11 +872,18 @@ int igmp_rcv(struct sk_buff *skb)
872 return 0; 872 return 0;
873 } 873 }
874 874
875 if (!pskb_may_pull(skb, sizeof(struct igmphdr)) || 875 if (!pskb_may_pull(skb, sizeof(struct igmphdr)))
876 (u16)csum_fold(skb_checksum(skb, 0, len, 0))) { 876 goto drop;
877 in_dev_put(in_dev); 877
878 kfree_skb(skb); 878 switch (skb->ip_summed) {
879 return 0; 879 case CHECKSUM_HW:
880 if (!(u16)csum_fold(skb->csum))
881 break;
882 /* fall through */
883 case CHECKSUM_NONE:
884 skb->csum = 0;
885 if (__skb_checksum_complete(skb))
886 goto drop;
880 } 887 }
881 888
882 ih = skb->h.igmph; 889 ih = skb->h.igmph;
@@ -906,6 +913,8 @@ int igmp_rcv(struct sk_buff *skb)
906 default: 913 default:
907 NETDEBUG(KERN_DEBUG "New IGMP type=%d, why we do not know about it?\n", ih->type); 914 NETDEBUG(KERN_DEBUG "New IGMP type=%d, why we do not know about it?\n", ih->type);
908 } 915 }
916
917drop:
909 in_dev_put(in_dev); 918 in_dev_put(in_dev);
910 kfree_skb(skb); 919 kfree_skb(skb);
911 return 0; 920 return 0;
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 896ce3f8f53a..4e9c74b54b15 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -577,15 +577,16 @@ static int ipgre_rcv(struct sk_buff *skb)
577 goto drop_nolock; 577 goto drop_nolock;
578 578
579 if (flags&GRE_CSUM) { 579 if (flags&GRE_CSUM) {
580 if (skb->ip_summed == CHECKSUM_HW) { 580 switch (skb->ip_summed) {
581 case CHECKSUM_HW:
581 csum = (u16)csum_fold(skb->csum); 582 csum = (u16)csum_fold(skb->csum);
582 if (csum) 583 if (!csum)
583 skb->ip_summed = CHECKSUM_NONE; 584 break;
584 } 585 /* fall through */
585 if (skb->ip_summed == CHECKSUM_NONE) { 586 case CHECKSUM_NONE:
586 skb->csum = skb_checksum(skb, 0, skb->len, 0); 587 skb->csum = 0;
588 csum = __skb_checksum_complete(skb);
587 skb->ip_summed = CHECKSUM_HW; 589 skb->ip_summed = CHECKSUM_HW;
588 csum = (u16)csum_fold(skb->csum);
589 } 590 }
590 offset += 4; 591 offset += 4;
591 } 592 }
diff --git a/net/ipv4/netfilter/ip_conntrack_proto_icmp.c b/net/ipv4/netfilter/ip_conntrack_proto_icmp.c
index 5198f3a1e2cd..e4d6b268e8c4 100644
--- a/net/ipv4/netfilter/ip_conntrack_proto_icmp.c
+++ b/net/ipv4/netfilter/ip_conntrack_proto_icmp.c
@@ -13,6 +13,7 @@
13#include <linux/in.h> 13#include <linux/in.h>
14#include <linux/icmp.h> 14#include <linux/icmp.h>
15#include <linux/seq_file.h> 15#include <linux/seq_file.h>
16#include <linux/skbuff.h>
16#include <net/ip.h> 17#include <net/ip.h>
17#include <net/checksum.h> 18#include <net/checksum.h>
18#include <linux/netfilter.h> 19#include <linux/netfilter.h>
@@ -230,19 +231,15 @@ icmp_error(struct sk_buff *skb, enum ip_conntrack_info *ctinfo,
230 case CHECKSUM_HW: 231 case CHECKSUM_HW:
231 if (!(u16)csum_fold(skb->csum)) 232 if (!(u16)csum_fold(skb->csum))
232 break; 233 break;
233 if (LOG_INVALID(IPPROTO_ICMP)) 234 /* fall through */
234 nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
235 "ip_ct_icmp: bad HW ICMP checksum ");
236 return -NF_ACCEPT;
237 case CHECKSUM_NONE: 235 case CHECKSUM_NONE:
238 if ((u16)csum_fold(skb_checksum(skb, 0, skb->len, 0))) { 236 skb->csum = 0;
237 if (__skb_checksum_complete(skb)) {
239 if (LOG_INVALID(IPPROTO_ICMP)) 238 if (LOG_INVALID(IPPROTO_ICMP))
240 nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, 239 nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
241 "ip_ct_icmp: bad ICMP checksum "); 240 "ip_ct_icmp: bad ICMP checksum ");
242 return -NF_ACCEPT; 241 return -NF_ACCEPT;
243 } 242 }
244 default:
245 break;
246 } 243 }
247 244
248checksum_skipped: 245checksum_skipped:
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 634dabb558fd..ac1fcf5b4ebc 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1110,24 +1110,18 @@ static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb)
1110static int tcp_v4_checksum_init(struct sk_buff *skb) 1110static int tcp_v4_checksum_init(struct sk_buff *skb)
1111{ 1111{
1112 if (skb->ip_summed == CHECKSUM_HW) { 1112 if (skb->ip_summed == CHECKSUM_HW) {
1113 skb->ip_summed = CHECKSUM_UNNECESSARY;
1114 if (!tcp_v4_check(skb->h.th, skb->len, skb->nh.iph->saddr, 1113 if (!tcp_v4_check(skb->h.th, skb->len, skb->nh.iph->saddr,
1115 skb->nh.iph->daddr, skb->csum)) 1114 skb->nh.iph->daddr, skb->csum)) {
1115 skb->ip_summed = CHECKSUM_UNNECESSARY;
1116 return 0; 1116 return 0;
1117 1117 }
1118 LIMIT_NETDEBUG(KERN_DEBUG "hw tcp v4 csum failed\n");
1119 skb->ip_summed = CHECKSUM_NONE;
1120 } 1118 }
1119
1120 skb->csum = csum_tcpudp_nofold(skb->nh.iph->saddr, skb->nh.iph->daddr,
1121 skb->len, IPPROTO_TCP, 0);
1122
1121 if (skb->len <= 76) { 1123 if (skb->len <= 76) {
1122 if (tcp_v4_check(skb->h.th, skb->len, skb->nh.iph->saddr, 1124 return __skb_checksum_complete(skb);
1123 skb->nh.iph->daddr,
1124 skb_checksum(skb, 0, skb->len, 0)))
1125 return -1;
1126 skb->ip_summed = CHECKSUM_UNNECESSARY;
1127 } else {
1128 skb->csum = ~tcp_v4_check(skb->h.th, skb->len,
1129 skb->nh.iph->saddr,
1130 skb->nh.iph->daddr, 0);
1131 } 1125 }
1132 return 0; 1126 return 0;
1133} 1127}
@@ -1219,7 +1213,7 @@ int tcp_v4_rcv(struct sk_buff *skb)
1219 * provided case of th->doff==0 is elimineted. 1213 * provided case of th->doff==0 is elimineted.
1220 * So, we defer the checks. */ 1214 * So, we defer the checks. */
1221 if ((skb->ip_summed != CHECKSUM_UNNECESSARY && 1215 if ((skb->ip_summed != CHECKSUM_UNNECESSARY &&
1222 tcp_v4_checksum_init(skb) < 0)) 1216 tcp_v4_checksum_init(skb)))
1223 goto bad_packet; 1217 goto bad_packet;
1224 1218
1225 th = skb->h.th; 1219 th = skb->h.th;
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index e0bd1013cb0d..2422a5f7195d 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -761,7 +761,7 @@ int udp_ioctl(struct sock *sk, int cmd, unsigned long arg)
761 761
762static __inline__ int __udp_checksum_complete(struct sk_buff *skb) 762static __inline__ int __udp_checksum_complete(struct sk_buff *skb)
763{ 763{
764 return (unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum)); 764 return __skb_checksum_complete(skb);
765} 765}
766 766
767static __inline__ int udp_checksum_complete(struct sk_buff *skb) 767static __inline__ int udp_checksum_complete(struct sk_buff *skb)
@@ -1100,11 +1100,8 @@ static int udp_checksum_init(struct sk_buff *skb, struct udphdr *uh,
1100 if (uh->check == 0) { 1100 if (uh->check == 0) {
1101 skb->ip_summed = CHECKSUM_UNNECESSARY; 1101 skb->ip_summed = CHECKSUM_UNNECESSARY;
1102 } else if (skb->ip_summed == CHECKSUM_HW) { 1102 } else if (skb->ip_summed == CHECKSUM_HW) {
1103 skb->ip_summed = CHECKSUM_UNNECESSARY;
1104 if (!udp_check(uh, ulen, saddr, daddr, skb->csum)) 1103 if (!udp_check(uh, ulen, saddr, daddr, skb->csum))
1105 return 0; 1104 skb->ip_summed = CHECKSUM_UNNECESSARY;
1106 LIMIT_NETDEBUG(KERN_DEBUG "udp v4 hw csum failure.\n");
1107 skb->ip_summed = CHECKSUM_NONE;
1108 } 1105 }
1109 if (skb->ip_summed != CHECKSUM_UNNECESSARY) 1106 if (skb->ip_summed != CHECKSUM_UNNECESSARY)
1110 skb->csum = csum_tcpudp_nofold(saddr, daddr, ulen, IPPROTO_UDP, 0); 1107 skb->csum = csum_tcpudp_nofold(saddr, daddr, ulen, IPPROTO_UDP, 0);