Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 1 Mar 2006 11:14:22 GMT
From:      Vilmos Nebehaj <vili@huwico.hu>
To:        freebsd-gnats-submit@FreeBSD.org
Subject:   kern/93976: if_tun doesn't handle kqueue(2)
Message-ID:  <200603011114.k21BEMSR038405@www.freebsd.org>
Resent-Message-ID: <200603011120.k21BK40x047425@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         93976
>Category:       kern
>Synopsis:       if_tun doesn't handle kqueue(2)
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Wed Mar 01 11:20:03 GMT 2006
>Closed-Date:
>Last-Modified:
>Originator:     Vilmos Nebehaj
>Release:        FreeBSD 6.0
>Organization:
>Environment:
FreeBSD oszoo.oszoo 6.0-RELEASE-p4 FreeBSD 6.0-RELEASE-p4 #0: Mon Feb 27 16:55:18 CET 2006
root@oszoo.oszoo:/usr/obj/usr/src/sys/GENERIC  i386
>Description:
The same as with if_tap in http://www.freebsd.org/cgi/query-pr.cgi?pr=kern/93897 . Kevent returns an error if someone tries to use it on a tun device (/dev/tunX), because kqueue bits are missing from if_tun.c.I've prepared a patch to address this issue: http://innoidea.com/~vili/if_tun.diff , it cleanly
applies against -CURRENT and RELENG_6_0 too.

Index: if_tun.c
===================================================================
RCS file: /home/ncvs/src/sys/net/if_tun.c,v
retrieving revision 1.152.2.2
diff -u -r1.152.2.2 if_tun.c
--- if_tun.c	25 Aug 2005 05:01:20 -0000	1.152.2.2
+++ if_tun.c	1 Mar 2006 10:47:48 -0000
@@ -117,6 +117,17 @@
 		    struct rtentry *rt);
 static void	tunstart(struct ifnet *);
 
+/* kqueue(2) */
+static int		tun_kqfilter(struct cdev *, struct knote *);
+static int		tun_kqread(struct knote *, long);
+static int		tun_kqwrite(struct knote *, long);
+static void		tun_kqdetach(struct knote *);
+
+static struct filterops	tun_read_filterops = { 1, NULL, tun_kqdetach,
+	tun_kqread };
+static struct filterops	tun_write_filterops = { 1, NULL, tun_kqdetach,
+	tun_kqwrite };
+
 static d_open_t		tunopen;
 static d_close_t	tunclose;
 static d_read_t		tunread;
@@ -134,6 +145,7 @@
 	.d_ioctl =	tunioctl,
 	.d_poll =	tunpoll,
 	.d_name =	TUNNAME,
+	.d_kqfilter =	tun_kqfilter,
 };
 
 static void
@@ -252,6 +264,7 @@
 	} else
 		mtx_unlock(&tp->tun_mtx);
 	selwakeuppri(&tp->tun_rsel, PZERO + 1);
+	KNOTE(&tp->tun_rsel.si_note, 0, 0);
 }
 
 /* XXX: should return an error code so it can fail. */
@@ -323,6 +336,9 @@
 	tp->tun_flags |= TUN_OPEN;
 	mtx_unlock(&tp->tun_mtx);
 	ifp = TUN2IFP(tp);
+
+	knlist_init(&tp->tun_rsel.si_note, NULL, NULL, NULL, NULL);
+
 	TUNDEBUG(ifp, "open\n");
 
 	return (0);
@@ -376,6 +392,10 @@
 
 	funsetown(&tp->tun_sigio);
 	selwakeuppri(&tp->tun_rsel, PZERO + 1);
+	KNOTE(&tp->tun_rsel.si_note, 0, 0);
+
+	knlist_destroy(&tp->tun_rsel.si_note);
+
 	TUNDEBUG (ifp, "closed\n");
 	return (0);
 }
@@ -862,4 +882,84 @@
 
 	splx(s);
 	return (revents);
+}
+
+static int
+tun_kqfilter(struct cdev *dev, struct knote *kn)
+{
+    	int			s;
+	struct tun_softc	*tp = dev->si_drv1;
+	struct ifnet		*ifp = tp->tun_ifp;
+
+	s = splimp();
+	switch (kn->kn_filter) {
+	case EVFILT_READ:
+		TUNDEBUG(ifp, "kqfilter: EVFILT_READ, minor = %#x\n",
+			minor(dev));
+		kn->kn_fop = &tun_read_filterops;
+		break;
+	case EVFILT_WRITE:
+		TUNDEBUG(ifp, "kqfilter: EVFILT_WRITE, minor = %#x\n",
+			minor(dev));
+		kn->kn_fop = &tun_write_filterops;
+		break;
+	default:
+		TUNDEBUG(ifp, "kqfilter: invalid filter, minor = %#x\n",
+			minor(dev));
+		splx(s);
+		return EINVAL;
+		break;
+	}
+	splx(s);
+
+	kn->kn_hook = (caddr_t)dev;
+	knlist_add(&tp->tun_rsel.si_note, kn, 0);
+
+	return 0;
+}
+
+/* Return true if there is data in the interface queue. */
+static int
+tun_kqread(struct knote *kn, long hint)
+{
+	int			ret, s;
+	struct cdev		*dev = (struct cdev *)kn->kn_hook;
+	struct tun_softc	*tp = dev->si_drv1;
+	struct ifnet		*ifp = tp->tun_ifp;
+
+	s = splimp();
+	if ((kn->kn_data = ifp->if_snd.ifq_len) > 0) {
+		TUNDEBUG(ifp, "have data in queue. len = %d, " \
+			"minor = %#x\n", ifp->if_snd.ifq_len, minor(dev));
+		ret = 1;
+	} else {
+		TUNDEBUG(ifp, "waiting for data, minor = %#x\n", minor(dev));
+		ret = 0;
+	}
+	splx(s);
+
+	return ret;
+}
+
+/* Always can write. Return the MTU in kn->data. */
+static int
+tun_kqwrite(struct knote *kn, long hint)
+{
+	int			s;
+	struct tun_softc	*tp = ((struct cdev *)kn->kn_hook)->si_drv1;
+	struct ifnet		*ifp = tp->tun_ifp;
+
+	s = splimp();
+	kn->kn_data = ifp->if_mtu;
+	splx(s);
+
+	return 1;
+}
+
+static void
+tun_kqdetach(struct knote *kn)
+{
+	struct tun_softc	*tp = ((struct cdev *)kn->kn_hook)->si_drv1;
+
+	knlist_remove(&tp->tun_rsel.si_note, kn, 0);
 }

>How-To-Repeat:
Use kqueue/kevent on a file descriptor obtained from opening /dev/tunX.
>Fix:
I've prepared a patch to address this issue: http://innoidea.com/~vili/if_tun.diff , it cleanly
applies against -CURRENT and RELENG_6_0 too.

Index: if_tun.c
===================================================================
RCS file: /home/ncvs/src/sys/net/if_tun.c,v
retrieving revision 1.152.2.2
diff -u -r1.152.2.2 if_tun.c
--- if_tun.c	25 Aug 2005 05:01:20 -0000	1.152.2.2
+++ if_tun.c	1 Mar 2006 10:47:48 -0000
@@ -117,6 +117,17 @@
 		    struct rtentry *rt);
 static void	tunstart(struct ifnet *);
 
+/* kqueue(2) */
+static int		tun_kqfilter(struct cdev *, struct knote *);
+static int		tun_kqread(struct knote *, long);
+static int		tun_kqwrite(struct knote *, long);
+static void		tun_kqdetach(struct knote *);
+
+static struct filterops	tun_read_filterops = { 1, NULL, tun_kqdetach,
+	tun_kqread };
+static struct filterops	tun_write_filterops = { 1, NULL, tun_kqdetach,
+	tun_kqwrite };
+
 static d_open_t		tunopen;
 static d_close_t	tunclose;
 static d_read_t		tunread;
@@ -134,6 +145,7 @@
 	.d_ioctl =	tunioctl,
 	.d_poll =	tunpoll,
 	.d_name =	TUNNAME,
+	.d_kqfilter =	tun_kqfilter,
 };
 
 static void
@@ -252,6 +264,7 @@
 	} else
 		mtx_unlock(&tp->tun_mtx);
 	selwakeuppri(&tp->tun_rsel, PZERO + 1);
+	KNOTE(&tp->tun_rsel.si_note, 0, 0);
 }
 
 /* XXX: should return an error code so it can fail. */
@@ -323,6 +336,9 @@
 	tp->tun_flags |= TUN_OPEN;
 	mtx_unlock(&tp->tun_mtx);
 	ifp = TUN2IFP(tp);
+
+	knlist_init(&tp->tun_rsel.si_note, NULL, NULL, NULL, NULL);
+
 	TUNDEBUG(ifp, "open\n");
 
 	return (0);
@@ -376,6 +392,10 @@
 
 	funsetown(&tp->tun_sigio);
 	selwakeuppri(&tp->tun_rsel, PZERO + 1);
+	KNOTE(&tp->tun_rsel.si_note, 0, 0);
+
+	knlist_destroy(&tp->tun_rsel.si_note);
+
 	TUNDEBUG (ifp, "closed\n");
 	return (0);
 }
@@ -862,4 +882,84 @@
 
 	splx(s);
 	return (revents);
+}
+
+static int
+tun_kqfilter(struct cdev *dev, struct knote *kn)
+{
+    	int			s;
+	struct tun_softc	*tp = dev->si_drv1;
+	struct ifnet		*ifp = tp->tun_ifp;
+
+	s = splimp();
+	switch (kn->kn_filter) {
+	case EVFILT_READ:
+		TUNDEBUG(ifp, "kqfilter: EVFILT_READ, minor = %#x\n",
+			minor(dev));
+		kn->kn_fop = &tun_read_filterops;
+		break;
+	case EVFILT_WRITE:
+		TUNDEBUG(ifp, "kqfilter: EVFILT_WRITE, minor = %#x\n",
+			minor(dev));
+		kn->kn_fop = &tun_write_filterops;
+		break;
+	default:
+		TUNDEBUG(ifp, "kqfilter: invalid filter, minor = %#x\n",
+			minor(dev));
+		splx(s);
+		return EINVAL;
+		break;
+	}
+	splx(s);
+
+	kn->kn_hook = (caddr_t)dev;
+	knlist_add(&tp->tun_rsel.si_note, kn, 0);
+
+	return 0;
+}
+
+/* Return true if there is data in the interface queue. */
+static int
+tun_kqread(struct knote *kn, long hint)
+{
+	int			ret, s;
+	struct cdev		*dev = (struct cdev *)kn->kn_hook;
+	struct tun_softc	*tp = dev->si_drv1;
+	struct ifnet		*ifp = tp->tun_ifp;
+
+	s = splimp();
+	if ((kn->kn_data = ifp->if_snd.ifq_len) > 0) {
+		TUNDEBUG(ifp, "have data in queue. len = %d, " \
+			"minor = %#x\n", ifp->if_snd.ifq_len, minor(dev));
+		ret = 1;
+	} else {
+		TUNDEBUG(ifp, "waiting for data, minor = %#x\n", minor(dev));
+		ret = 0;
+	}
+	splx(s);
+
+	return ret;
+}
+
+/* Always can write. Return the MTU in kn->data. */
+static int
+tun_kqwrite(struct knote *kn, long hint)
+{
+	int			s;
+	struct tun_softc	*tp = ((struct cdev *)kn->kn_hook)->si_drv1;
+	struct ifnet		*ifp = tp->tun_ifp;
+
+	s = splimp();
+	kn->kn_data = ifp->if_mtu;
+	splx(s);
+
+	return 1;
+}
+
+static void
+tun_kqdetach(struct knote *kn)
+{
+	struct tun_softc	*tp = ((struct cdev *)kn->kn_hook)->si_drv1;
+
+	knlist_remove(&tp->tun_rsel.si_note, kn, 0);
 }

>Release-Note:
>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200603011114.k21BEMSR038405>