udp_poll breaks vpnc

From: Pete Zaitcev
Date: Wed Dec 15 2004 - 16:16:55 EST


Hi, Guys:

I found that the attached patch breaks VPNC. By looking at strace, it never
gets any poll events about arriving encrypted data. It may be a bug in VPNC,
but this is a rather old binary which I used even on 2.4...

Unfortunately, I cannot investigate it closer at this time, because of, uhh,
some work commitments. Sorry. I do not insist that we back this out, but
let it be known that something was broken.

Cheers,
-- Pete

diff -urp -X dontdiff linux-2.6.10-rc2-bk8-usb/include/net/udp.h linux-2.6.10-rc3-usb/include/net/udp.h
--- linux-2.6.10-rc2-bk8-usb/include/net/udp.h 2004-08-19 17:16:10.000000000 -0700
+++ linux-2.6.10-rc3-usb/include/net/udp.h 2004-12-15 02:01:19.000000000 -0800
@@ -71,6 +71,8 @@ extern int udp_sendmsg(struct kiocb *ioc
extern int udp_rcv(struct sk_buff *skb);
extern int udp_ioctl(struct sock *sk, int cmd, unsigned long arg);
extern int udp_disconnect(struct sock *sk, int flags);
+extern unsigned int udp_poll(struct file *file, struct socket *sock,
+ poll_table *wait);

DECLARE_SNMP_STAT(struct udp_mib, udp_statistics);
#define UDP_INC_STATS(field) SNMP_INC_STATS(udp_statistics, field)
diff -urp -X dontdiff linux-2.6.10-rc2-bk8-usb/net/ipv4/af_inet.c linux-2.6.10-rc3-usb/net/ipv4/af_inet.c
--- linux-2.6.10-rc2-bk8-usb/net/ipv4/af_inet.c 2004-11-23 09:54:15.000000000 -0800
+++ linux-2.6.10-rc3-usb/net/ipv4/af_inet.c 2004-12-15 02:01:21.000000000 -0800
@@ -809,7 +809,7 @@ struct proto_ops inet_dgram_ops = {
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.getname = inet_getname,
- .poll = datagram_poll,
+ .poll = udp_poll,
.ioctl = inet_ioctl,
.listen = sock_no_listen,
.shutdown = inet_shutdown,
diff -urp -X dontdiff linux-2.6.10-rc2-bk8-usb/net/ipv4/udp.c linux-2.6.10-rc3-usb/net/ipv4/udp.c
--- linux-2.6.10-rc2-bk8-usb/net/ipv4/udp.c 2004-11-23 09:54:15.000000000 -0800
+++ linux-2.6.10-rc3-usb/net/ipv4/udp.c 2004-12-15 02:01:21.000000000 -0800
@@ -1303,6 +1303,52 @@ static int udp_getsockopt(struct sock *s
return 0;
}

+/**
+ * udp_poll - wait for a UDP event.
+ * @file - file struct
+ * @sock - socket
+ * @wait - poll table
+ *
+ * This is same as datagram poll, except for the special case of
+ * blocking sockets. If application is using a blocking fd
+ * and a packet with checksum error is in the queue;
+ * then it could get return from select indicating data available
+ * but then block when reading it. Add special case code
+ * to work around these arguably broken applications.
+ */
+unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait)
+{
+ unsigned int mask = datagram_poll(file, sock, wait);
+ struct sock *sk = sock->sk;
+
+ /* Check for false positives due to checksum errors */
+ if ( (mask & POLLRDNORM) &&
+ !(file->f_flags & O_NONBLOCK) &&
+ !(sk->sk_shutdown & RCV_SHUTDOWN)){
+ struct sk_buff_head *rcvq = &sk->sk_receive_queue;
+ struct sk_buff *skb;
+
+ spin_lock_irq(&rcvq->lock);
+ while ((skb = skb_peek(rcvq)) != NULL) {
+ if (udp_checksum_complete(skb)) {
+ UDP_INC_STATS_BH(UDP_MIB_INERRORS);
+ __skb_unlink(skb, rcvq);
+ kfree_skb(skb);
+ } else {
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ break;
+ }
+ }
+ spin_unlock_irq(&rcvq->lock);
+
+ /* nothing to see, move along */
+ if (skb == NULL)
+ mask &= ~(POLLIN | POLLRDNORM);
+ }
+
+ return mask;
+
+}

struct proto udp_prot = {
.name = "UDP",
@@ -1517,6 +1563,7 @@ EXPORT_SYMBOL(udp_ioctl);
EXPORT_SYMBOL(udp_port_rover);
EXPORT_SYMBOL(udp_prot);
EXPORT_SYMBOL(udp_sendmsg);
+EXPORT_SYMBOL(udp_poll);

#ifdef CONFIG_PROC_FS
EXPORT_SYMBOL(udp_proc_register);
diff -urp -X dontdiff linux-2.6.10-rc2-bk8-usb/net/ipv6/af_inet6.c linux-2.6.10-rc3-usb/net/ipv6/af_inet6.c
--- linux-2.6.10-rc2-bk8-usb/net/ipv6/af_inet6.c 2004-11-23 09:54:15.000000000 -0800
+++ linux-2.6.10-rc3-usb/net/ipv6/af_inet6.c 2004-12-15 02:01:21.000000000 -0800
@@ -501,7 +501,7 @@ struct proto_ops inet6_dgram_ops = {
.socketpair = sock_no_socketpair, /* a do nothing */
.accept = sock_no_accept, /* a do nothing */
.getname = inet6_getname,
- .poll = datagram_poll, /* ok */
+ .poll = udp_poll, /* ok */
.ioctl = inet6_ioctl, /* must change */
.listen = sock_no_listen, /* ok */
.shutdown = inet_shutdown, /* ok */
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/