|
@@ -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);
|
|
|
|
- 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;
|
|
msg->msg_flags |= MSG_TRUNC;
|
|
- }
|
|
|
|
|
|
|
|
/*
|
|
/*
|
|
- * Decide whether to checksum and/or copy data.
|
|
|
|
- *
|
|
|
|
- * UDP: checksum may have been computed in HW,
|
|
|
|
- * (re-)compute it if message is truncated.
|
|
|
|
- * UDP-Lite: always needs to checksum, no HW support.
|
|
|
|
|
|
+ * 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;
|
|
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 (__udp_lib_checksum_complete(skb))
|
|
|
|
|
|
+ if (sk->sk_filter) {
|
|
|
|
+ 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 */
|
|
|
|
- UDP_SKB_CB(skb)->partial_cov = 0;
|
|
|
|
|
|
+ return 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) {
|
|
|
|
- if (udp_lib_checksum_complete(skb)) {
|
|
|
|
- UDP_INC_STATS_BH(UDP_MIB_INERRORS, is_lite);
|
|
|
|
- __skb_unlink(skb, rcvq);
|
|
|
|
- kfree_skb(skb);
|
|
|
|
- } else {
|
|
|
|
- skb->ip_summed = CHECKSUM_UNNECESSARY;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
|
|
+ while ((skb = skb_peek(rcvq)) != NULL &&
|
|
|
|
+ udp_lib_checksum_complete(skb)) {
|
|
|
|
+ UDP_INC_STATS_BH(UDP_MIB_INERRORS, is_lite);
|
|
|
|
+ __skb_unlink(skb, rcvq);
|
|
|
|
+ kfree_skb(skb);
|
|
}
|
|
}
|
|
spin_unlock_bh(&rcvq->lock);
|
|
spin_unlock_bh(&rcvq->lock);
|
|
|
|
|