forked from Minki/linux
[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:
parent
1ab6eb62b0
commit
759e5d0064
@ -1372,6 +1372,7 @@ static inline void __net_timestamp(struct sk_buff *skb)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern __sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len);
|
||||||
extern __sum16 __skb_checksum_complete(struct sk_buff *skb);
|
extern __sum16 __skb_checksum_complete(struct sk_buff *skb);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -72,10 +72,7 @@ struct sk_buff;
|
|||||||
*/
|
*/
|
||||||
static inline __sum16 __udp_lib_checksum_complete(struct sk_buff *skb)
|
static inline __sum16 __udp_lib_checksum_complete(struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
if (! UDP_SKB_CB(skb)->partial_cov)
|
return __skb_checksum_complete_head(skb, UDP_SKB_CB(skb)->cscov);
|
||||||
return __skb_checksum_complete(skb);
|
|
||||||
return csum_fold(skb_checksum(skb, 0, UDP_SKB_CB(skb)->cscov,
|
|
||||||
skb->csum));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int udp_lib_checksum_complete(struct sk_buff *skb)
|
static inline int udp_lib_checksum_complete(struct sk_buff *skb)
|
||||||
|
@ -47,11 +47,10 @@ static inline int udplite_checksum_init(struct sk_buff *skb, struct udphdr *uh)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
UDP_SKB_CB(skb)->partial_cov = 0;
|
|
||||||
cscov = ntohs(uh->len);
|
cscov = ntohs(uh->len);
|
||||||
|
|
||||||
if (cscov == 0) /* Indicates that full coverage is required. */
|
if (cscov == 0) /* Indicates that full coverage is required. */
|
||||||
cscov = skb->len;
|
;
|
||||||
else if (cscov < 8 || cscov > skb->len) {
|
else if (cscov < 8 || cscov > skb->len) {
|
||||||
/*
|
/*
|
||||||
* Coverage length violates RFC 3828: log and discard silently.
|
* Coverage length violates RFC 3828: log and discard silently.
|
||||||
@ -60,42 +59,16 @@ static inline int udplite_checksum_init(struct sk_buff *skb, struct udphdr *uh)
|
|||||||
cscov, skb->len);
|
cscov, skb->len);
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
} else if (cscov < skb->len)
|
} else if (cscov < skb->len) {
|
||||||
UDP_SKB_CB(skb)->partial_cov = 1;
|
UDP_SKB_CB(skb)->partial_cov = 1;
|
||||||
|
UDP_SKB_CB(skb)->cscov = cscov;
|
||||||
UDP_SKB_CB(skb)->cscov = cscov;
|
if (skb->ip_summed == CHECKSUM_COMPLETE)
|
||||||
|
skb->ip_summed = CHECKSUM_NONE;
|
||||||
/*
|
}
|
||||||
* There is no known NIC manufacturer supporting UDP-Lite yet,
|
|
||||||
* hence ip_summed is always (re-)set to CHECKSUM_NONE.
|
|
||||||
*/
|
|
||||||
skb->ip_summed = CHECKSUM_NONE;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static __inline__ int udplite4_csum_init(struct sk_buff *skb, struct udphdr *uh)
|
|
||||||
{
|
|
||||||
int rc = udplite_checksum_init(skb, uh);
|
|
||||||
|
|
||||||
if (!rc)
|
|
||||||
skb->csum = csum_tcpudp_nofold(skb->nh.iph->saddr,
|
|
||||||
skb->nh.iph->daddr,
|
|
||||||
skb->len, IPPROTO_UDPLITE, 0);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static __inline__ int udplite6_csum_init(struct sk_buff *skb, struct udphdr *uh)
|
|
||||||
{
|
|
||||||
int rc = udplite_checksum_init(skb, uh);
|
|
||||||
|
|
||||||
if (!rc)
|
|
||||||
skb->csum = ~csum_unfold(csum_ipv6_magic(&skb->nh.ipv6h->saddr,
|
|
||||||
&skb->nh.ipv6h->daddr,
|
|
||||||
skb->len, IPPROTO_UDPLITE, 0));
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int udplite_sender_cscov(struct udp_sock *up, struct udphdr *uh)
|
static inline int udplite_sender_cscov(struct udp_sock *up, struct udphdr *uh)
|
||||||
{
|
{
|
||||||
int cscov = up->len;
|
int cscov = up->len;
|
||||||
|
@ -411,11 +411,11 @@ fault:
|
|||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
__sum16 __skb_checksum_complete(struct sk_buff *skb)
|
__sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len)
|
||||||
{
|
{
|
||||||
__sum16 sum;
|
__sum16 sum;
|
||||||
|
|
||||||
sum = csum_fold(skb_checksum(skb, 0, skb->len, skb->csum));
|
sum = csum_fold(skb_checksum(skb, 0, len, skb->csum));
|
||||||
if (likely(!sum)) {
|
if (likely(!sum)) {
|
||||||
if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE))
|
if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE))
|
||||||
netdev_rx_csum_fault(skb->dev);
|
netdev_rx_csum_fault(skb->dev);
|
||||||
@ -423,6 +423,12 @@ __sum16 __skb_checksum_complete(struct sk_buff *skb)
|
|||||||
}
|
}
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(__skb_checksum_complete_head);
|
||||||
|
|
||||||
|
__sum16 __skb_checksum_complete(struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
return __skb_checksum_complete_head(skb, skb->len);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(__skb_checksum_complete);
|
EXPORT_SYMBOL(__skb_checksum_complete);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -810,7 +810,9 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
|
|||||||
struct inet_sock *inet = inet_sk(sk);
|
struct inet_sock *inet = inet_sk(sk);
|
||||||
struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name;
|
struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name;
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
int copied, err, copy_only, is_udplite = IS_UDPLITE(sk);
|
unsigned int ulen, copied;
|
||||||
|
int err;
|
||||||
|
int is_udplite = IS_UDPLITE(sk);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check any passed addresses
|
* Check any passed addresses
|
||||||
@ -826,28 +828,25 @@ try_again:
|
|||||||
if (!skb)
|
if (!skb)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
copied = skb->len - sizeof(struct udphdr);
|
ulen = skb->len - sizeof(struct udphdr);
|
||||||
if (copied > len) {
|
copied = len;
|
||||||
copied = len;
|
if (copied > ulen)
|
||||||
|
copied = ulen;
|
||||||
|
else if (copied < ulen)
|
||||||
msg->msg_flags |= MSG_TRUNC;
|
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
|
||||||
* UDP: checksum may have been computed in HW,
|
* coverage checksum (UDP-Lite), do it before the copy.
|
||||||
* (re-)compute it if message is truncated.
|
|
||||||
* UDP-Lite: always needs to checksum, no HW support.
|
|
||||||
*/
|
*/
|
||||||
copy_only = (skb->ip_summed==CHECKSUM_UNNECESSARY);
|
|
||||||
|
|
||||||
if (is_udplite || (!copy_only && msg->msg_flags&MSG_TRUNC)) {
|
if (copied < ulen || UDP_SKB_CB(skb)->partial_cov) {
|
||||||
if (__udp_lib_checksum_complete(skb))
|
if (udp_lib_checksum_complete(skb))
|
||||||
goto csum_copy_err;
|
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),
|
err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr),
|
||||||
msg->msg_iov, copied );
|
msg->msg_iov, copied );
|
||||||
else {
|
else {
|
||||||
@ -875,7 +874,7 @@ try_again:
|
|||||||
|
|
||||||
err = copied;
|
err = copied;
|
||||||
if (flags & MSG_TRUNC)
|
if (flags & MSG_TRUNC)
|
||||||
err = skb->len - sizeof(struct udphdr);
|
err = ulen;
|
||||||
|
|
||||||
out_free:
|
out_free:
|
||||||
skb_free_datagram(sk, skb);
|
skb_free_datagram(sk, skb);
|
||||||
@ -1095,10 +1094,9 @@ int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sk->sk_filter && skb->ip_summed != CHECKSUM_UNNECESSARY) {
|
if (sk->sk_filter) {
|
||||||
if (__udp_lib_checksum_complete(skb))
|
if (udp_lib_checksum_complete(skb))
|
||||||
goto drop;
|
goto drop;
|
||||||
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((rc = sock_queue_rcv_skb(sk,skb)) < 0) {
|
if ((rc = sock_queue_rcv_skb(sk,skb)) < 0) {
|
||||||
@ -1166,25 +1164,36 @@ static int __udp4_lib_mcast_deliver(struct sk_buff *skb,
|
|||||||
* Otherwise, csum completion requires chacksumming packet body,
|
* Otherwise, csum completion requires chacksumming packet body,
|
||||||
* including udp header and folding it to skb->csum.
|
* including udp header and folding it to skb->csum.
|
||||||
*/
|
*/
|
||||||
static inline void udp4_csum_init(struct sk_buff *skb, struct udphdr *uh)
|
static inline int udp4_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) {
|
if (uh->check == 0) {
|
||||||
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||||
} else if (skb->ip_summed == CHECKSUM_COMPLETE) {
|
} else if (skb->ip_summed == CHECKSUM_COMPLETE) {
|
||||||
if (!csum_tcpudp_magic(skb->nh.iph->saddr, skb->nh.iph->daddr,
|
if (!csum_tcpudp_magic(skb->nh.iph->saddr, skb->nh.iph->daddr,
|
||||||
skb->len, IPPROTO_UDP, skb->csum ))
|
skb->len, proto, skb->csum))
|
||||||
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||||
}
|
}
|
||||||
if (skb->ip_summed != CHECKSUM_UNNECESSARY)
|
if (skb->ip_summed != CHECKSUM_UNNECESSARY)
|
||||||
skb->csum = csum_tcpudp_nofold(skb->nh.iph->saddr,
|
skb->csum = csum_tcpudp_nofold(skb->nh.iph->saddr,
|
||||||
skb->nh.iph->daddr,
|
skb->nh.iph->daddr,
|
||||||
skb->len, IPPROTO_UDP, 0);
|
skb->len, proto, 0);
|
||||||
/* Probably, we should checksum udp header (it should be in cache
|
/* Probably, we should checksum udp header (it should be in cache
|
||||||
* in any case) and data in tiny packets (< rx copybreak).
|
* in any case) and data in tiny packets (< rx copybreak).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* UDP = UDP-Lite with a non-partial checksum coverage */
|
return 0;
|
||||||
UDP_SKB_CB(skb)->partial_cov = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1192,7 +1201,7 @@ static inline void udp4_csum_init(struct sk_buff *skb, struct udphdr *uh)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
int __udp4_lib_rcv(struct sk_buff *skb, struct hlist_head udptable[],
|
int __udp4_lib_rcv(struct sk_buff *skb, struct hlist_head udptable[],
|
||||||
int is_udplite)
|
int proto)
|
||||||
{
|
{
|
||||||
struct sock *sk;
|
struct sock *sk;
|
||||||
struct udphdr *uh = skb->h.uh;
|
struct udphdr *uh = skb->h.uh;
|
||||||
@ -1211,19 +1220,16 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct hlist_head udptable[],
|
|||||||
if (ulen > skb->len)
|
if (ulen > skb->len)
|
||||||
goto short_packet;
|
goto short_packet;
|
||||||
|
|
||||||
if(! is_udplite ) { /* UDP validates ulen. */
|
if (proto == IPPROTO_UDP) {
|
||||||
|
/* UDP validates ulen. */
|
||||||
if (ulen < sizeof(*uh) || pskb_trim_rcsum(skb, ulen))
|
if (ulen < sizeof(*uh) || pskb_trim_rcsum(skb, ulen))
|
||||||
goto short_packet;
|
goto short_packet;
|
||||||
uh = skb->h.uh;
|
uh = skb->h.uh;
|
||||||
|
|
||||||
udp4_csum_init(skb, uh);
|
|
||||||
|
|
||||||
} else { /* UDP-Lite validates cscov. */
|
|
||||||
if (udplite4_csum_init(skb, uh))
|
|
||||||
goto csum_error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (udp4_csum_init(skb, uh, proto))
|
||||||
|
goto csum_error;
|
||||||
|
|
||||||
if(rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST))
|
if(rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST))
|
||||||
return __udp4_lib_mcast_deliver(skb, uh, saddr, daddr, udptable);
|
return __udp4_lib_mcast_deliver(skb, uh, saddr, daddr, udptable);
|
||||||
|
|
||||||
@ -1250,7 +1256,7 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct hlist_head udptable[],
|
|||||||
if (udp_lib_checksum_complete(skb))
|
if (udp_lib_checksum_complete(skb))
|
||||||
goto csum_error;
|
goto csum_error;
|
||||||
|
|
||||||
UDP_INC_STATS_BH(UDP_MIB_NOPORTS, is_udplite);
|
UDP_INC_STATS_BH(UDP_MIB_NOPORTS, proto == IPPROTO_UDPLITE);
|
||||||
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
|
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1262,7 +1268,7 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct hlist_head udptable[],
|
|||||||
|
|
||||||
short_packet:
|
short_packet:
|
||||||
LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: short packet: From %u.%u.%u.%u:%u %d/%d to %u.%u.%u.%u:%u\n",
|
LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: short packet: From %u.%u.%u.%u:%u %d/%d to %u.%u.%u.%u:%u\n",
|
||||||
is_udplite? "-Lite" : "",
|
proto == IPPROTO_UDPLITE ? "-Lite" : "",
|
||||||
NIPQUAD(saddr),
|
NIPQUAD(saddr),
|
||||||
ntohs(uh->source),
|
ntohs(uh->source),
|
||||||
ulen,
|
ulen,
|
||||||
@ -1277,21 +1283,21 @@ csum_error:
|
|||||||
* the network is concerned, anyway) as per 4.1.3.4 (MUST).
|
* the network is concerned, anyway) as per 4.1.3.4 (MUST).
|
||||||
*/
|
*/
|
||||||
LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: bad checksum. From %d.%d.%d.%d:%d to %d.%d.%d.%d:%d ulen %d\n",
|
LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: bad checksum. From %d.%d.%d.%d:%d to %d.%d.%d.%d:%d ulen %d\n",
|
||||||
is_udplite? "-Lite" : "",
|
proto == IPPROTO_UDPLITE ? "-Lite" : "",
|
||||||
NIPQUAD(saddr),
|
NIPQUAD(saddr),
|
||||||
ntohs(uh->source),
|
ntohs(uh->source),
|
||||||
NIPQUAD(daddr),
|
NIPQUAD(daddr),
|
||||||
ntohs(uh->dest),
|
ntohs(uh->dest),
|
||||||
ulen);
|
ulen);
|
||||||
drop:
|
drop:
|
||||||
UDP_INC_STATS_BH(UDP_MIB_INERRORS, is_udplite);
|
UDP_INC_STATS_BH(UDP_MIB_INERRORS, proto == IPPROTO_UDPLITE);
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
__inline__ int udp_rcv(struct sk_buff *skb)
|
__inline__ int udp_rcv(struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
return __udp4_lib_rcv(skb, udp_hash, 0);
|
return __udp4_lib_rcv(skb, udp_hash, IPPROTO_UDP);
|
||||||
}
|
}
|
||||||
|
|
||||||
int udp_destroy_sock(struct sock *sk)
|
int udp_destroy_sock(struct sock *sk)
|
||||||
@ -1486,15 +1492,11 @@ unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait)
|
|||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
|
|
||||||
spin_lock_bh(&rcvq->lock);
|
spin_lock_bh(&rcvq->lock);
|
||||||
while ((skb = skb_peek(rcvq)) != NULL) {
|
while ((skb = skb_peek(rcvq)) != NULL &&
|
||||||
if (udp_lib_checksum_complete(skb)) {
|
udp_lib_checksum_complete(skb)) {
|
||||||
UDP_INC_STATS_BH(UDP_MIB_INERRORS, is_lite);
|
UDP_INC_STATS_BH(UDP_MIB_INERRORS, is_lite);
|
||||||
__skb_unlink(skb, rcvq);
|
__skb_unlink(skb, rcvq);
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
} else {
|
|
||||||
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
spin_unlock_bh(&rcvq->lock);
|
spin_unlock_bh(&rcvq->lock);
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ static int udplite_v4_get_port(struct sock *sk, unsigned short snum)
|
|||||||
|
|
||||||
static int udplite_rcv(struct sk_buff *skb)
|
static int udplite_rcv(struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
return __udp4_lib_rcv(skb, udplite_hash, 1);
|
return __udp4_lib_rcv(skb, udplite_hash, IPPROTO_UDPLITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void udplite_err(struct sk_buff *skb, u32 info)
|
static void udplite_err(struct sk_buff *skb, u32 info)
|
||||||
|
@ -120,8 +120,9 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk,
|
|||||||
struct ipv6_pinfo *np = inet6_sk(sk);
|
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||||
struct inet_sock *inet = inet_sk(sk);
|
struct inet_sock *inet = inet_sk(sk);
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
size_t copied;
|
unsigned int ulen, copied;
|
||||||
int err, copy_only, is_udplite = IS_UDPLITE(sk);
|
int err;
|
||||||
|
int is_udplite = IS_UDPLITE(sk);
|
||||||
|
|
||||||
if (addr_len)
|
if (addr_len)
|
||||||
*addr_len=sizeof(struct sockaddr_in6);
|
*addr_len=sizeof(struct sockaddr_in6);
|
||||||
@ -134,24 +135,25 @@ try_again:
|
|||||||
if (!skb)
|
if (!skb)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
copied = skb->len - sizeof(struct udphdr);
|
ulen = skb->len - sizeof(struct udphdr);
|
||||||
if (copied > len) {
|
copied = len;
|
||||||
copied = len;
|
if (copied > ulen)
|
||||||
|
copied = ulen;
|
||||||
|
else if (copied < ulen)
|
||||||
msg->msg_flags |= MSG_TRUNC;
|
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 (copied < ulen || UDP_SKB_CB(skb)->partial_cov) {
|
||||||
if (__udp_lib_checksum_complete(skb))
|
if (udp_lib_checksum_complete(skb))
|
||||||
goto csum_copy_err;
|
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),
|
err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr),
|
||||||
msg->msg_iov, copied );
|
msg->msg_iov, copied );
|
||||||
else {
|
else {
|
||||||
@ -194,7 +196,7 @@ try_again:
|
|||||||
|
|
||||||
err = copied;
|
err = copied;
|
||||||
if (flags & MSG_TRUNC)
|
if (flags & MSG_TRUNC)
|
||||||
err = skb->len - sizeof(struct udphdr);
|
err = ulen;
|
||||||
|
|
||||||
out_free:
|
out_free:
|
||||||
skb_free_datagram(sk, skb);
|
skb_free_datagram(sk, skb);
|
||||||
@ -368,9 +370,20 @@ out:
|
|||||||
return 0;
|
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) {
|
if (uh->check == 0) {
|
||||||
/* RFC 2460 section 8.1 says that we SHOULD log
|
/* RFC 2460 section 8.1 says that we SHOULD log
|
||||||
this error. Well, it is reasonable.
|
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 &&
|
if (skb->ip_summed == CHECKSUM_COMPLETE &&
|
||||||
!csum_ipv6_magic(&skb->nh.ipv6h->saddr, &skb->nh.ipv6h->daddr,
|
!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;
|
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||||
|
|
||||||
if (skb->ip_summed != CHECKSUM_UNNECESSARY)
|
if (skb->ip_summed != CHECKSUM_UNNECESSARY)
|
||||||
skb->csum = ~csum_unfold(csum_ipv6_magic(&skb->nh.ipv6h->saddr,
|
skb->csum = ~csum_unfold(csum_ipv6_magic(&skb->nh.ipv6h->saddr,
|
||||||
&skb->nh.ipv6h->daddr,
|
&skb->nh.ipv6h->daddr,
|
||||||
skb->len, IPPROTO_UDP,
|
skb->len, proto, 0));
|
||||||
0));
|
|
||||||
|
|
||||||
return (UDP_SKB_CB(skb)->partial_cov = 0);
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int __udp6_lib_rcv(struct sk_buff **pskb, struct hlist_head udptable[],
|
int __udp6_lib_rcv(struct sk_buff **pskb, struct hlist_head udptable[],
|
||||||
int is_udplite)
|
int proto)
|
||||||
{
|
{
|
||||||
struct sk_buff *skb = *pskb;
|
struct sk_buff *skb = *pskb;
|
||||||
struct sock *sk;
|
struct sock *sk;
|
||||||
@ -413,7 +425,8 @@ int __udp6_lib_rcv(struct sk_buff **pskb, struct hlist_head udptable[],
|
|||||||
if (ulen > skb->len)
|
if (ulen > skb->len)
|
||||||
goto short_packet;
|
goto short_packet;
|
||||||
|
|
||||||
if(! is_udplite ) { /* UDP validates ulen. */
|
if (proto == IPPROTO_UDP) {
|
||||||
|
/* UDP validates ulen. */
|
||||||
|
|
||||||
/* Check for jumbo payload */
|
/* Check for jumbo payload */
|
||||||
if (ulen == 0)
|
if (ulen == 0)
|
||||||
@ -429,15 +442,11 @@ int __udp6_lib_rcv(struct sk_buff **pskb, struct hlist_head udptable[],
|
|||||||
daddr = &skb->nh.ipv6h->daddr;
|
daddr = &skb->nh.ipv6h->daddr;
|
||||||
uh = skb->h.uh;
|
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
|
* 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))
|
if (udp_lib_checksum_complete(skb))
|
||||||
goto discard;
|
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);
|
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:
|
short_packet:
|
||||||
LIMIT_NETDEBUG(KERN_DEBUG "UDP%sv6: short packet: %d/%u\n",
|
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:
|
discard:
|
||||||
UDP6_INC_STATS_BH(UDP_MIB_INERRORS, is_udplite);
|
UDP6_INC_STATS_BH(UDP_MIB_INERRORS, proto == IPPROTO_UDPLITE);
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static __inline__ int udpv6_rcv(struct sk_buff **pskb)
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -19,7 +19,7 @@ DEFINE_SNMP_STAT(struct udp_mib, udplite_stats_in6) __read_mostly;
|
|||||||
|
|
||||||
static int udplitev6_rcv(struct sk_buff **pskb)
|
static int udplitev6_rcv(struct sk_buff **pskb)
|
||||||
{
|
{
|
||||||
return __udp6_lib_rcv(pskb, udplite_hash, 1);
|
return __udp6_lib_rcv(pskb, udplite_hash, IPPROTO_UDPLITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void udplitev6_err(struct sk_buff *skb,
|
static void udplitev6_err(struct sk_buff *skb,
|
||||||
|
Loading…
Reference in New Issue
Block a user