[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>
This commit is contained in:
committed by
David S. Miller
parent
1ab6eb62b0
commit
759e5d0064
@@ -120,8 +120,9 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk,
|
||||
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||
struct inet_sock *inet = inet_sk(sk);
|
||||
struct sk_buff *skb;
|
||||
size_t copied;
|
||||
int err, copy_only, is_udplite = IS_UDPLITE(sk);
|
||||
unsigned int ulen, copied;
|
||||
int err;
|
||||
int is_udplite = IS_UDPLITE(sk);
|
||||
|
||||
if (addr_len)
|
||||
*addr_len=sizeof(struct sockaddr_in6);
|
||||
@@ -134,24 +135,25 @@ try_again:
|
||||
if (!skb)
|
||||
goto out;
|
||||
|
||||
copied = skb->len - sizeof(struct udphdr);
|
||||
if (copied > len) {
|
||||
copied = len;
|
||||
ulen = skb->len - sizeof(struct udphdr);
|
||||
copied = len;
|
||||
if (copied > ulen)
|
||||
copied = ulen;
|
||||
else if (copied < ulen)
|
||||
msg->msg_flags |= MSG_TRUNC;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decide whether to checksum and/or copy data.
|
||||
* If checksum is needed at all, try to do it while copying the
|
||||
* data. If the data is truncated, or if we only want a partial
|
||||
* coverage checksum (UDP-Lite), do it before the copy.
|
||||
*/
|
||||
copy_only = (skb->ip_summed==CHECKSUM_UNNECESSARY);
|
||||
|
||||
if (is_udplite || (!copy_only && msg->msg_flags&MSG_TRUNC)) {
|
||||
if (__udp_lib_checksum_complete(skb))
|
||||
if (copied < ulen || UDP_SKB_CB(skb)->partial_cov) {
|
||||
if (udp_lib_checksum_complete(skb))
|
||||
goto csum_copy_err;
|
||||
copy_only = 1;
|
||||
}
|
||||
|
||||
if (copy_only)
|
||||
if (skb->ip_summed == CHECKSUM_UNNECESSARY)
|
||||
err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr),
|
||||
msg->msg_iov, copied );
|
||||
else {
|
||||
@@ -194,7 +196,7 @@ try_again:
|
||||
|
||||
err = copied;
|
||||
if (flags & MSG_TRUNC)
|
||||
err = skb->len - sizeof(struct udphdr);
|
||||
err = ulen;
|
||||
|
||||
out_free:
|
||||
skb_free_datagram(sk, skb);
|
||||
@@ -368,9 +370,20 @@ out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh)
|
||||
|
||||
static inline int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh,
|
||||
int proto)
|
||||
{
|
||||
int err;
|
||||
|
||||
UDP_SKB_CB(skb)->partial_cov = 0;
|
||||
UDP_SKB_CB(skb)->cscov = skb->len;
|
||||
|
||||
if (proto == IPPROTO_UDPLITE) {
|
||||
err = udplite_checksum_init(skb, uh);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (uh->check == 0) {
|
||||
/* RFC 2460 section 8.1 says that we SHOULD log
|
||||
this error. Well, it is reasonable.
|
||||
@@ -380,20 +393,19 @@ static inline int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh)
|
||||
}
|
||||
if (skb->ip_summed == CHECKSUM_COMPLETE &&
|
||||
!csum_ipv6_magic(&skb->nh.ipv6h->saddr, &skb->nh.ipv6h->daddr,
|
||||
skb->len, IPPROTO_UDP, skb->csum ))
|
||||
skb->len, proto, skb->csum))
|
||||
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
|
||||
if (skb->ip_summed != CHECKSUM_UNNECESSARY)
|
||||
skb->csum = ~csum_unfold(csum_ipv6_magic(&skb->nh.ipv6h->saddr,
|
||||
&skb->nh.ipv6h->daddr,
|
||||
skb->len, IPPROTO_UDP,
|
||||
0));
|
||||
skb->len, proto, 0));
|
||||
|
||||
return (UDP_SKB_CB(skb)->partial_cov = 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __udp6_lib_rcv(struct sk_buff **pskb, struct hlist_head udptable[],
|
||||
int is_udplite)
|
||||
int proto)
|
||||
{
|
||||
struct sk_buff *skb = *pskb;
|
||||
struct sock *sk;
|
||||
@@ -413,7 +425,8 @@ int __udp6_lib_rcv(struct sk_buff **pskb, struct hlist_head udptable[],
|
||||
if (ulen > skb->len)
|
||||
goto short_packet;
|
||||
|
||||
if(! is_udplite ) { /* UDP validates ulen. */
|
||||
if (proto == IPPROTO_UDP) {
|
||||
/* UDP validates ulen. */
|
||||
|
||||
/* Check for jumbo payload */
|
||||
if (ulen == 0)
|
||||
@@ -429,15 +442,11 @@ int __udp6_lib_rcv(struct sk_buff **pskb, struct hlist_head udptable[],
|
||||
daddr = &skb->nh.ipv6h->daddr;
|
||||
uh = skb->h.uh;
|
||||
}
|
||||
|
||||
if (udp6_csum_init(skb, uh))
|
||||
goto discard;
|
||||
|
||||
} else { /* UDP-Lite validates cscov. */
|
||||
if (udplite6_csum_init(skb, uh))
|
||||
goto discard;
|
||||
}
|
||||
|
||||
if (udp6_csum_init(skb, uh, proto))
|
||||
goto discard;
|
||||
|
||||
/*
|
||||
* Multicast receive code
|
||||
*/
|
||||
@@ -459,7 +468,7 @@ int __udp6_lib_rcv(struct sk_buff **pskb, struct hlist_head udptable[],
|
||||
|
||||
if (udp_lib_checksum_complete(skb))
|
||||
goto discard;
|
||||
UDP6_INC_STATS_BH(UDP_MIB_NOPORTS, is_udplite);
|
||||
UDP6_INC_STATS_BH(UDP_MIB_NOPORTS, proto == IPPROTO_UDPLITE);
|
||||
|
||||
icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0, dev);
|
||||
|
||||
@@ -475,17 +484,18 @@ int __udp6_lib_rcv(struct sk_buff **pskb, struct hlist_head udptable[],
|
||||
|
||||
short_packet:
|
||||
LIMIT_NETDEBUG(KERN_DEBUG "UDP%sv6: short packet: %d/%u\n",
|
||||
is_udplite? "-Lite" : "", ulen, skb->len);
|
||||
proto == IPPROTO_UDPLITE ? "-Lite" : "",
|
||||
ulen, skb->len);
|
||||
|
||||
discard:
|
||||
UDP6_INC_STATS_BH(UDP_MIB_INERRORS, is_udplite);
|
||||
UDP6_INC_STATS_BH(UDP_MIB_INERRORS, proto == IPPROTO_UDPLITE);
|
||||
kfree_skb(skb);
|
||||
return(0);
|
||||
}
|
||||
|
||||
static __inline__ int udpv6_rcv(struct sk_buff **pskb)
|
||||
{
|
||||
return __udp6_lib_rcv(pskb, udp_hash, 0);
|
||||
return __udp6_lib_rcv(pskb, udp_hash, IPPROTO_UDP);
|
||||
}
|
||||
|
||||
/*
|
||||
|
Reference in New Issue
Block a user