diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2007-03-25 23:10:56 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-04-26 01:23:51 -0400 |
commit | 759e5d006462d53fb708daa8284b4ad909415da1 (patch) | |
tree | edcc4e9d975199b3fe5e2aadc3d1e06824755e75 /net/ipv6/udp.c | |
parent | 1ab6eb62b02e0949a392fb19bf31ba59ae1022b1 (diff) |
[UDP]: Clean up UDP-Lite receive checksum
This patch eliminates some duplicate code for the verification of
receive checksums between UDP-Lite and UDP. It does this by
introducing __skb_checksum_complete_head which is identical to
__skb_checksum_complete_head apart from the fact that it takes
a length parameter rather than computing the first skb->len bytes.
As a result UDP-Lite will be able to use hardware checksum offload
for packets which do not use partial coverage checksums. It also
means that UDP-Lite loopback no longer does unnecessary checksum
verification.
If any NICs start support UDP-Lite this would also start working
automatically.
This patch removes the assumption that msg_flags has MSG_TRUNC clear
upon entry in recvmsg.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/udp.c')
-rw-r--r-- | net/ipv6/udp.c | 74 |
1 files changed, 42 insertions, 32 deletions
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 3413fc22ce4a..733371689795 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c | |||
@@ -120,8 +120,9 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk, | |||
120 | struct ipv6_pinfo *np = inet6_sk(sk); | 120 | struct ipv6_pinfo *np = inet6_sk(sk); |
121 | struct inet_sock *inet = inet_sk(sk); | 121 | struct inet_sock *inet = inet_sk(sk); |
122 | struct sk_buff *skb; | 122 | struct sk_buff *skb; |
123 | size_t copied; | 123 | unsigned int ulen, copied; |
124 | int err, copy_only, is_udplite = IS_UDPLITE(sk); | 124 | int err; |
125 | int is_udplite = IS_UDPLITE(sk); | ||
125 | 126 | ||
126 | if (addr_len) | 127 | if (addr_len) |
127 | *addr_len=sizeof(struct sockaddr_in6); | 128 | *addr_len=sizeof(struct sockaddr_in6); |
@@ -134,24 +135,25 @@ try_again: | |||
134 | if (!skb) | 135 | if (!skb) |
135 | goto out; | 136 | goto out; |
136 | 137 | ||
137 | copied = skb->len - sizeof(struct udphdr); | 138 | ulen = skb->len - sizeof(struct udphdr); |
138 | if (copied > len) { | 139 | copied = len; |
139 | copied = len; | 140 | if (copied > ulen) |
141 | copied = ulen; | ||
142 | else if (copied < ulen) | ||
140 | msg->msg_flags |= MSG_TRUNC; | 143 | msg->msg_flags |= MSG_TRUNC; |
141 | } | ||
142 | 144 | ||
143 | /* | 145 | /* |
144 | * Decide whether to checksum and/or copy data. | 146 | * If checksum is needed at all, try to do it while copying the |
147 | * data. If the data is truncated, or if we only want a partial | ||
148 | * coverage checksum (UDP-Lite), do it before the copy. | ||
145 | */ | 149 | */ |
146 | copy_only = (skb->ip_summed==CHECKSUM_UNNECESSARY); | ||
147 | 150 | ||
148 | if (is_udplite || (!copy_only && msg->msg_flags&MSG_TRUNC)) { | 151 | if (copied < ulen || UDP_SKB_CB(skb)->partial_cov) { |
149 | if (__udp_lib_checksum_complete(skb)) | 152 | if (udp_lib_checksum_complete(skb)) |
150 | goto csum_copy_err; | 153 | goto csum_copy_err; |
151 | copy_only = 1; | ||
152 | } | 154 | } |
153 | 155 | ||
154 | if (copy_only) | 156 | if (skb->ip_summed == CHECKSUM_UNNECESSARY) |
155 | err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), | 157 | err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), |
156 | msg->msg_iov, copied ); | 158 | msg->msg_iov, copied ); |
157 | else { | 159 | else { |
@@ -194,7 +196,7 @@ try_again: | |||
194 | 196 | ||
195 | err = copied; | 197 | err = copied; |
196 | if (flags & MSG_TRUNC) | 198 | if (flags & MSG_TRUNC) |
197 | err = skb->len - sizeof(struct udphdr); | 199 | err = ulen; |
198 | 200 | ||
199 | out_free: | 201 | out_free: |
200 | skb_free_datagram(sk, skb); | 202 | skb_free_datagram(sk, skb); |
@@ -368,9 +370,20 @@ out: | |||
368 | return 0; | 370 | return 0; |
369 | } | 371 | } |
370 | 372 | ||
371 | static inline int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh) | 373 | static inline int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, |
372 | 374 | int proto) | |
373 | { | 375 | { |
376 | int err; | ||
377 | |||
378 | UDP_SKB_CB(skb)->partial_cov = 0; | ||
379 | UDP_SKB_CB(skb)->cscov = skb->len; | ||
380 | |||
381 | if (proto == IPPROTO_UDPLITE) { | ||
382 | err = udplite_checksum_init(skb, uh); | ||
383 | if (err) | ||
384 | return err; | ||
385 | } | ||
386 | |||
374 | if (uh->check == 0) { | 387 | if (uh->check == 0) { |
375 | /* RFC 2460 section 8.1 says that we SHOULD log | 388 | /* RFC 2460 section 8.1 says that we SHOULD log |
376 | this error. Well, it is reasonable. | 389 | this error. Well, it is reasonable. |
@@ -380,20 +393,19 @@ static inline int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh) | |||
380 | } | 393 | } |
381 | if (skb->ip_summed == CHECKSUM_COMPLETE && | 394 | if (skb->ip_summed == CHECKSUM_COMPLETE && |
382 | !csum_ipv6_magic(&skb->nh.ipv6h->saddr, &skb->nh.ipv6h->daddr, | 395 | !csum_ipv6_magic(&skb->nh.ipv6h->saddr, &skb->nh.ipv6h->daddr, |
383 | skb->len, IPPROTO_UDP, skb->csum )) | 396 | skb->len, proto, skb->csum)) |
384 | skb->ip_summed = CHECKSUM_UNNECESSARY; | 397 | skb->ip_summed = CHECKSUM_UNNECESSARY; |
385 | 398 | ||
386 | if (skb->ip_summed != CHECKSUM_UNNECESSARY) | 399 | if (skb->ip_summed != CHECKSUM_UNNECESSARY) |
387 | skb->csum = ~csum_unfold(csum_ipv6_magic(&skb->nh.ipv6h->saddr, | 400 | skb->csum = ~csum_unfold(csum_ipv6_magic(&skb->nh.ipv6h->saddr, |
388 | &skb->nh.ipv6h->daddr, | 401 | &skb->nh.ipv6h->daddr, |
389 | skb->len, IPPROTO_UDP, | 402 | skb->len, proto, 0)); |
390 | 0)); | ||
391 | 403 | ||
392 | return (UDP_SKB_CB(skb)->partial_cov = 0); | 404 | return 0; |
393 | } | 405 | } |
394 | 406 | ||
395 | int __udp6_lib_rcv(struct sk_buff **pskb, struct hlist_head udptable[], | 407 | int __udp6_lib_rcv(struct sk_buff **pskb, struct hlist_head udptable[], |
396 | int is_udplite) | 408 | int proto) |
397 | { | 409 | { |
398 | struct sk_buff *skb = *pskb; | 410 | struct sk_buff *skb = *pskb; |
399 | struct sock *sk; | 411 | struct sock *sk; |
@@ -413,7 +425,8 @@ int __udp6_lib_rcv(struct sk_buff **pskb, struct hlist_head udptable[], | |||
413 | if (ulen > skb->len) | 425 | if (ulen > skb->len) |
414 | goto short_packet; | 426 | goto short_packet; |
415 | 427 | ||
416 | if(! is_udplite ) { /* UDP validates ulen. */ | 428 | if (proto == IPPROTO_UDP) { |
429 | /* UDP validates ulen. */ | ||
417 | 430 | ||
418 | /* Check for jumbo payload */ | 431 | /* Check for jumbo payload */ |
419 | if (ulen == 0) | 432 | if (ulen == 0) |
@@ -429,15 +442,11 @@ int __udp6_lib_rcv(struct sk_buff **pskb, struct hlist_head udptable[], | |||
429 | daddr = &skb->nh.ipv6h->daddr; | 442 | daddr = &skb->nh.ipv6h->daddr; |
430 | uh = skb->h.uh; | 443 | uh = skb->h.uh; |
431 | } | 444 | } |
432 | |||
433 | if (udp6_csum_init(skb, uh)) | ||
434 | goto discard; | ||
435 | |||
436 | } else { /* UDP-Lite validates cscov. */ | ||
437 | if (udplite6_csum_init(skb, uh)) | ||
438 | goto discard; | ||
439 | } | 445 | } |
440 | 446 | ||
447 | if (udp6_csum_init(skb, uh, proto)) | ||
448 | goto discard; | ||
449 | |||
441 | /* | 450 | /* |
442 | * Multicast receive code | 451 | * Multicast receive code |
443 | */ | 452 | */ |
@@ -459,7 +468,7 @@ int __udp6_lib_rcv(struct sk_buff **pskb, struct hlist_head udptable[], | |||
459 | 468 | ||
460 | if (udp_lib_checksum_complete(skb)) | 469 | if (udp_lib_checksum_complete(skb)) |
461 | goto discard; | 470 | goto discard; |
462 | UDP6_INC_STATS_BH(UDP_MIB_NOPORTS, is_udplite); | 471 | UDP6_INC_STATS_BH(UDP_MIB_NOPORTS, proto == IPPROTO_UDPLITE); |
463 | 472 | ||
464 | icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0, dev); | 473 | icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0, dev); |
465 | 474 | ||
@@ -475,17 +484,18 @@ int __udp6_lib_rcv(struct sk_buff **pskb, struct hlist_head udptable[], | |||
475 | 484 | ||
476 | short_packet: | 485 | short_packet: |
477 | LIMIT_NETDEBUG(KERN_DEBUG "UDP%sv6: short packet: %d/%u\n", | 486 | LIMIT_NETDEBUG(KERN_DEBUG "UDP%sv6: short packet: %d/%u\n", |
478 | is_udplite? "-Lite" : "", ulen, skb->len); | 487 | proto == IPPROTO_UDPLITE ? "-Lite" : "", |
488 | ulen, skb->len); | ||
479 | 489 | ||
480 | discard: | 490 | discard: |
481 | UDP6_INC_STATS_BH(UDP_MIB_INERRORS, is_udplite); | 491 | UDP6_INC_STATS_BH(UDP_MIB_INERRORS, proto == IPPROTO_UDPLITE); |
482 | kfree_skb(skb); | 492 | kfree_skb(skb); |
483 | return(0); | 493 | return(0); |
484 | } | 494 | } |
485 | 495 | ||
486 | static __inline__ int udpv6_rcv(struct sk_buff **pskb) | 496 | static __inline__ int udpv6_rcv(struct sk_buff **pskb) |
487 | { | 497 | { |
488 | return __udp6_lib_rcv(pskb, udp_hash, 0); | 498 | return __udp6_lib_rcv(pskb, udp_hash, IPPROTO_UDP); |
489 | } | 499 | } |
490 | 500 | ||
491 | /* | 501 | /* |