Deleted Added
full compact
1/* $NetBSD: if_tun.c,v 1.14 1994/06/29 06:36:25 cgd Exp $ */
2
3/*
4 * Copyright (c) 1988, Julian Onions <jpo@cs.nott.ac.uk>
5 * Nottingham University 1987.
6 *
7 * This source may be freely distributed, however I would be interested
8 * in any changes that are made.
9 *
10 * This driver takes packets off the IP i/f and hands them up to a
11 * user process to have its wicked way with. This driver has it's
12 * roots in a similar driver written by Phil Cockcroft (formerly) at
13 * UCL. This driver is based much more on read/write/poll mode of
14 * operation though.
15 *
16 * $FreeBSD: head/sys/net/if_tun.c 69781 2000-12-08 21:51:06Z dwmalone $
16 * $FreeBSD: head/sys/net/if_tun.c 71862 2001-01-31 07:58:58Z peter $
17 */
18
19#include "opt_inet.h"
20
21#include <sys/param.h>
22#include <sys/proc.h>
23#include <sys/systm.h>
24#include <sys/mbuf.h>
25#include <sys/module.h>
26#include <sys/socket.h>
27#include <sys/filio.h>
28#include <sys/sockio.h>
29#include <sys/ttycom.h>
30#include <sys/poll.h>
31#include <sys/signalvar.h>
32#include <sys/filedesc.h>
33#include <sys/kernel.h>
34#include <sys/sysctl.h>
35#include <sys/conf.h>
36#include <sys/uio.h>
37#include <sys/vnode.h>
38#include <sys/malloc.h>
39
40#include <net/if.h>
41#include <net/if_types.h>
42#include <net/route.h>
43#include <net/intrq.h>
44
45#ifdef INET
46#include <netinet/in.h>
47#endif
48
49#include <net/bpf.h>
50
51#include <net/if_tunvar.h>
52#include <net/if_tun.h>
53
54static MALLOC_DEFINE(M_TUN, "tun", "Tunnel Interface");
55
55static void tunattach __P((void *));
56PSEUDO_SET(tunattach, if_tun);
57
56static void tuncreate __P((dev_t dev));
57
58#define TUNDEBUG if (tundebug) printf
59static int tundebug = 0;
60SYSCTL_INT(_debug, OID_AUTO, if_tun_debug, CTLFLAG_RW, &tundebug, 0, "");
61
62static int tunoutput __P((struct ifnet *, struct mbuf *, struct sockaddr *,
63 struct rtentry *rt));
64static int tunifioctl __P((struct ifnet *, u_long, caddr_t));
65static int tuninit __P((struct ifnet *));
66static void tunstart __P((struct ifnet *));
67
68static d_open_t tunopen;
69static d_close_t tunclose;
70static d_read_t tunread;
71static d_write_t tunwrite;
72static d_ioctl_t tunioctl;
73static d_poll_t tunpoll;
74
75#define CDEV_MAJOR 52
76static struct cdevsw tun_cdevsw = {
77 /* open */ tunopen,
78 /* close */ tunclose,
79 /* read */ tunread,
80 /* write */ tunwrite,
81 /* ioctl */ tunioctl,
82 /* poll */ tunpoll,
83 /* mmap */ nommap,
84 /* strategy */ nostrategy,
85 /* name */ "tun",
86 /* maj */ CDEV_MAJOR,
87 /* dump */ nodump,
88 /* psize */ nopsize,
89 /* flags */ 0,
90 /* bmaj */ -1
91};
92
93static void tun_clone __P((void *arg, char *name, int namelen, dev_t *dev));
94
95static void
96tun_clone(arg, name, namelen, dev)
97 void *arg;
98 char *name;
99 int namelen;
100 dev_t *dev;
101{
102 int u;
103
104 if (*dev != NODEV)
105 return;
106 if (dev_stdclone(name, NULL, "tun", &u) != 1)
107 return;
108 /* XXX: minor encoding if u > 255 */
109 *dev = make_dev(&tun_cdevsw, u,
110 UID_UUCP, GID_DIALER, 0600, "tun%d", u);
111
112}
113
116static void
117tunattach(dummy)
118 void *dummy;
119{
114static int
115tun_modevent(module_t mod, int type, void *data)
116{
117 switch (type) {
118 case MOD_LOAD:
119 EVENTHANDLER_REGISTER(dev_clone, tun_clone, 0, 1000);
120 cdevsw_add(&tun_cdevsw);
121 break;
122 case MOD_UNLOAD:
123 printf("if_tun module unload - not possible for this module type\n");
124 return EINVAL;
125 }
126 return 0;
127}
128
121 EVENTHANDLER_REGISTER(dev_clone, tun_clone, 0, 1000);
122 cdevsw_add(&tun_cdevsw);
123}
129static moduledata_t tun_mod = {
130 "if_tun",
131 tun_modevent,
132 0
133};
134
135DECLARE_MODULE(if_tun, tun_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
136
137static void
138tunstart(ifp)
139 struct ifnet *ifp;
140{
141 struct tun_softc *tp = ifp->if_softc;
142
143 if (tp->tun_flags & TUN_RWAIT) {
144 tp->tun_flags &= ~TUN_RWAIT;
145 wakeup((caddr_t)tp);
146 }
147 if (tp->tun_flags & TUN_ASYNC && tp->tun_sigio)
148 pgsigio(tp->tun_sigio, SIGIO, 0);
149 selwakeup(&tp->tun_rsel);
150}
151
152static void
153tuncreate(dev)
154 dev_t dev;
155{
156 struct tun_softc *sc;
157 struct ifnet *ifp;
158
159 dev = make_dev(&tun_cdevsw, minor(dev),
160 UID_UUCP, GID_DIALER, 0600, "tun%d", dev2unit(dev));
161
162 MALLOC(sc, struct tun_softc *, sizeof(*sc), M_TUN, M_WAITOK | M_ZERO);
163 sc->tun_flags = TUN_INITED;
164
165 ifp = &sc->tun_if;
166 ifp->if_unit = dev2unit(dev);
167 ifp->if_name = "tun";
168 ifp->if_mtu = TUNMTU;
169 ifp->if_ioctl = tunifioctl;
170 ifp->if_output = tunoutput;
171 ifp->if_start = tunstart;
172 ifp->if_flags = IFF_POINTOPOINT | IFF_MULTICAST;
173 ifp->if_type = IFT_PPP;
174 ifp->if_snd.ifq_maxlen = ifqmaxlen;
175 ifp->if_softc = sc;
176 if_attach(ifp);
177 bpfattach(ifp, DLT_NULL, sizeof(u_int));
178 dev->si_drv1 = sc;
179}
180
181/*
182 * tunnel open - must be superuser & the device must be
183 * configured in
184 */
185static int
186tunopen(dev, flag, mode, p)
187 dev_t dev;
188 int flag, mode;
189 struct proc *p;
190{
191 struct ifnet *ifp;
192 struct tun_softc *tp;
193 register int error;
194
195 error = suser(p);
196 if (error)
197 return (error);
198
199 tp = dev->si_drv1;
200 if (!tp) {
201 tuncreate(dev);
202 tp = dev->si_drv1;
203 }
204 if (tp->tun_flags & TUN_OPEN)
205 return EBUSY;
206 tp->tun_pid = p->p_pid;
207 ifp = &tp->tun_if;
208 tp->tun_flags |= TUN_OPEN;
209 TUNDEBUG("%s%d: open\n", ifp->if_name, ifp->if_unit);
210 return (0);
211}
212
213/*
214 * tunclose - close the device - mark i/f down & delete
215 * routing info
216 */
217static int
218tunclose(dev, foo, bar, p)
219 dev_t dev;
220 int foo;
221 int bar;
222 struct proc *p;
223{
224 register int s;
225 struct tun_softc *tp;
226 struct ifnet *ifp;
227
228 tp = dev->si_drv1;
229 ifp = &tp->tun_if;
230
231 tp->tun_flags &= ~TUN_OPEN;
232 tp->tun_pid = 0;
233
234 /*
235 * junk all pending output
236 */
237 IF_DRAIN(&ifp->if_snd);
238
239 if (ifp->if_flags & IFF_UP) {
240 s = splimp();
241 if_down(ifp);
242 splx(s);
243 }
244
245 if (ifp->if_flags & IFF_RUNNING) {
246 register struct ifaddr *ifa;
247
248 s = splimp();
249 /* find internet addresses and delete routes */
250 for (ifa = ifp->if_addrhead.tqh_first; ifa;
251 ifa = ifa->ifa_link.tqe_next)
252 if (ifa->ifa_addr->sa_family == AF_INET)
253 rtinit(ifa, (int)RTM_DELETE,
254 tp->tun_flags & TUN_DSTADDR ? RTF_HOST : 0);
255 ifp->if_flags &= ~IFF_RUNNING;
256 splx(s);
257 }
258
259 funsetown(tp->tun_sigio);
260 selwakeup(&tp->tun_rsel);
261
262 TUNDEBUG ("%s%d: closed\n", ifp->if_name, ifp->if_unit);
263 return (0);
264}
265
266static int
267tuninit(ifp)
268 struct ifnet *ifp;
269{
270 struct tun_softc *tp = ifp->if_softc;
271 register struct ifaddr *ifa;
272 int error = 0;
273
274 TUNDEBUG("%s%d: tuninit\n", ifp->if_name, ifp->if_unit);
275
276 ifp->if_flags |= IFF_UP | IFF_RUNNING;
277 getmicrotime(&ifp->if_lastchange);
278
279 for (ifa = ifp->if_addrhead.tqh_first; ifa;
280 ifa = ifa->ifa_link.tqe_next) {
281 if (ifa->ifa_addr == NULL)
282 error = EFAULT;
283 /* XXX: Should maybe return straight off? */
284 else {
285#ifdef INET
286 if (ifa->ifa_addr->sa_family == AF_INET) {
287 struct sockaddr_in *si;
288
289 si = (struct sockaddr_in *)ifa->ifa_addr;
290 if (si->sin_addr.s_addr)
291 tp->tun_flags |= TUN_IASET;
292
293 si = (struct sockaddr_in *)ifa->ifa_dstaddr;
294 if (si && si->sin_addr.s_addr)
295 tp->tun_flags |= TUN_DSTADDR;
296 }
297#endif
298 }
299 }
300 return (error);
301}
302
303/*
304 * Process an ioctl request.
305 */
306int
307tunifioctl(ifp, cmd, data)
308 struct ifnet *ifp;
309 u_long cmd;
310 caddr_t data;
311{
312 struct ifreq *ifr = (struct ifreq *)data;
313 struct tun_softc *tp = ifp->if_softc;
314 struct ifstat *ifs;
315 int error = 0, s;
316
317 s = splimp();
318 switch(cmd) {
319 case SIOCGIFSTATUS:
320 ifs = (struct ifstat *)data;
321 if (tp->tun_pid)
322 sprintf(ifs->ascii + strlen(ifs->ascii),
323 "\tOpened by PID %d\n", tp->tun_pid);
324 break;
325 case SIOCSIFADDR:
326 error = tuninit(ifp);
327 TUNDEBUG("%s%d: address set, error=%d\n",
328 ifp->if_name, ifp->if_unit, error);
329 break;
330 case SIOCSIFDSTADDR:
331 error = tuninit(ifp);
332 TUNDEBUG("%s%d: destination address set, error=%d\n",
333 ifp->if_name, ifp->if_unit, error);
334 break;
335 case SIOCSIFMTU:
336 ifp->if_mtu = ifr->ifr_mtu;
337 TUNDEBUG("%s%d: mtu set\n",
338 ifp->if_name, ifp->if_unit);
339 break;
340 case SIOCADDMULTI:
341 case SIOCDELMULTI:
342 break;
343 default:
344 error = EINVAL;
345 }
346 splx(s);
347 return (error);
348}
349
350/*
351 * tunoutput - queue packets from higher level ready to put out.
352 */
353int
354tunoutput(ifp, m0, dst, rt)
355 struct ifnet *ifp;
356 struct mbuf *m0;
357 struct sockaddr *dst;
358 struct rtentry *rt;
359{
360 struct tun_softc *tp = ifp->if_softc;
361
362 TUNDEBUG ("%s%d: tunoutput\n", ifp->if_name, ifp->if_unit);
363
364 if ((tp->tun_flags & TUN_READY) != TUN_READY) {
365 TUNDEBUG ("%s%d: not ready 0%o\n", ifp->if_name,
366 ifp->if_unit, tp->tun_flags);
367 m_freem (m0);
368 return EHOSTDOWN;
369 }
370
371 /* BPF write needs to be handled specially */
372 if (dst->sa_family == AF_UNSPEC) {
373 dst->sa_family = *(mtod(m0, int *));
374 m0->m_len -= sizeof(int);
375 m0->m_pkthdr.len -= sizeof(int);
376 m0->m_data += sizeof(int);
377 }
378
379 if (ifp->if_bpf) {
380 /*
381 * We need to prepend the address family as
382 * a four byte field. Cons up a dummy header
383 * to pacify bpf. This is safe because bpf
384 * will only read from the mbuf (i.e., it won't
385 * try to free it or keep a pointer to it).
386 */
387 struct mbuf m;
388 uint32_t af = dst->sa_family;
389
390 m.m_next = m0;
391 m.m_len = 4;
392 m.m_data = (char *)&af;
393
394 bpf_mtap(ifp, &m);
395 }
396
397 /* prepend sockaddr? this may abort if the mbuf allocation fails */
398 if (tp->tun_flags & TUN_LMODE) {
399 /* allocate space for sockaddr */
400 M_PREPEND(m0, dst->sa_len, M_DONTWAIT);
401
402 /* if allocation failed drop packet */
403 if (m0 == NULL) {
404 ifp->if_iqdrops++;
405 ifp->if_oerrors++;
406 return (ENOBUFS);
407 } else {
408 bcopy(dst, m0->m_data, dst->sa_len);
409 }
410 }
411
412 if (tp->tun_flags & TUN_IFHEAD) {
413 /* Prepend the address family */
414 M_PREPEND(m0, 4, M_DONTWAIT);
415
416 /* if allocation failed drop packet */
417 if (m0 == NULL) {
418 ifp->if_iqdrops++;
419 ifp->if_oerrors++;
420 return ENOBUFS;
421 } else
422 *(u_int32_t *)m0->m_data = htonl(dst->sa_family);
423 } else {
424#ifdef INET
425 if (dst->sa_family != AF_INET)
426#endif
427 {
428 m_freem(m0);
429 return EAFNOSUPPORT;
430 }
431 }
432
433 if (! IF_HANDOFF(&ifp->if_snd, m0, ifp)) {
434 ifp->if_collisions++;
435 return ENOBUFS;
436 }
437 ifp->if_opackets++;
438 return 0;
439}
440
441/*
442 * the cdevsw interface is now pretty minimal.
443 */
444static int
445tunioctl(dev, cmd, data, flag, p)
446 dev_t dev;
447 u_long cmd;
448 caddr_t data;
449 int flag;
450 struct proc *p;
451{
452 int s;
453 struct tun_softc *tp = dev->si_drv1;
454 struct tuninfo *tunp;
455
456 switch (cmd) {
457 case TUNSIFINFO:
458 tunp = (struct tuninfo *)data;
459 if (tunp->mtu < IF_MINMTU)
460 return (EINVAL);
461 tp->tun_if.if_mtu = tunp->mtu;
462 tp->tun_if.if_type = tunp->type;
463 tp->tun_if.if_baudrate = tunp->baudrate;
464 break;
465 case TUNGIFINFO:
466 tunp = (struct tuninfo *)data;
467 tunp->mtu = tp->tun_if.if_mtu;
468 tunp->type = tp->tun_if.if_type;
469 tunp->baudrate = tp->tun_if.if_baudrate;
470 break;
471 case TUNSDEBUG:
472 tundebug = *(int *)data;
473 break;
474 case TUNGDEBUG:
475 *(int *)data = tundebug;
476 break;
477 case TUNSLMODE:
478 if (*(int *)data) {
479 tp->tun_flags |= TUN_LMODE;
480 tp->tun_flags &= ~TUN_IFHEAD;
481 } else
482 tp->tun_flags &= ~TUN_LMODE;
483 break;
484 case TUNSIFHEAD:
485 if (*(int *)data) {
486 tp->tun_flags |= TUN_IFHEAD;
487 tp->tun_flags &= ~TUN_LMODE;
488 } else
489 tp->tun_flags &= ~TUN_IFHEAD;
490 break;
491 case TUNGIFHEAD:
492 *(int *)data = (tp->tun_flags & TUN_IFHEAD) ? 1 : 0;
493 break;
494 case TUNSIFMODE:
495 /* deny this if UP */
496 if (tp->tun_if.if_flags & IFF_UP)
497 return(EBUSY);
498
499 switch (*(int *)data) {
500 case IFF_POINTOPOINT:
501 tp->tun_if.if_flags |= IFF_POINTOPOINT;
502 tp->tun_if.if_flags &= ~IFF_BROADCAST;
503 break;
504 case IFF_BROADCAST:
505 tp->tun_if.if_flags &= ~IFF_POINTOPOINT;
506 tp->tun_if.if_flags |= IFF_BROADCAST;
507 break;
508 default:
509 return(EINVAL);
510 }
511 break;
512 case TUNSIFPID:
513 tp->tun_pid = curproc->p_pid;
514 break;
515 case FIONBIO:
516 break;
517 case FIOASYNC:
518 if (*(int *)data)
519 tp->tun_flags |= TUN_ASYNC;
520 else
521 tp->tun_flags &= ~TUN_ASYNC;
522 break;
523 case FIONREAD:
524 s = splimp();
525 if (tp->tun_if.if_snd.ifq_head) {
526 struct mbuf *mb = tp->tun_if.if_snd.ifq_head;
527 for( *(int *)data = 0; mb != 0; mb = mb->m_next)
528 *(int *)data += mb->m_len;
529 } else
530 *(int *)data = 0;
531 splx(s);
532 break;
533 case FIOSETOWN:
534 return (fsetown(*(int *)data, &tp->tun_sigio));
535
536 case FIOGETOWN:
537 *(int *)data = fgetown(tp->tun_sigio);
538 return (0);
539
540 /* This is deprecated, FIOSETOWN should be used instead. */
541 case TIOCSPGRP:
542 return (fsetown(-(*(int *)data), &tp->tun_sigio));
543
544 /* This is deprecated, FIOGETOWN should be used instead. */
545 case TIOCGPGRP:
546 *(int *)data = -fgetown(tp->tun_sigio);
547 return (0);
548
549 default:
550 return (ENOTTY);
551 }
552 return (0);
553}
554
555/*
556 * The cdevsw read interface - reads a packet at a time, or at
557 * least as much of a packet as can be read.
558 */
559static int
560tunread(dev, uio, flag)
561 dev_t dev;
562 struct uio *uio;
563 int flag;
564{
565 struct tun_softc *tp = dev->si_drv1;
566 struct ifnet *ifp = &tp->tun_if;
567 struct mbuf *m, *m0;
568 int error=0, len, s;
569
570 TUNDEBUG ("%s%d: read\n", ifp->if_name, ifp->if_unit);
571 if ((tp->tun_flags & TUN_READY) != TUN_READY) {
572 TUNDEBUG ("%s%d: not ready 0%o\n", ifp->if_name,
573 ifp->if_unit, tp->tun_flags);
574 return EHOSTDOWN;
575 }
576
577 tp->tun_flags &= ~TUN_RWAIT;
578
579 s = splimp();
580 do {
581 IF_DEQUEUE(&ifp->if_snd, m0);
582 if (m0 == 0) {
583 if (flag & IO_NDELAY) {
584 splx(s);
585 return EWOULDBLOCK;
586 }
587 tp->tun_flags |= TUN_RWAIT;
588 if((error = tsleep((caddr_t)tp, PCATCH | (PZERO + 1),
589 "tunread", 0)) != 0) {
590 splx(s);
591 return error;
592 }
593 }
594 } while (m0 == 0);
595 splx(s);
596
597 while (m0 && uio->uio_resid > 0 && error == 0) {
598 len = min(uio->uio_resid, m0->m_len);
599 if (len == 0)
600 break;
601 error = uiomove(mtod(m0, caddr_t), len, uio);
602 MFREE(m0, m);
603 m0 = m;
604 }
605
606 if (m0) {
607 TUNDEBUG("Dropping mbuf\n");
608 m_freem(m0);
609 }
610 return error;
611}
612
613/*
614 * the cdevsw write interface - an atomic write is a packet - or else!
615 */
616static int
617tunwrite(dev, uio, flag)
618 dev_t dev;
619 struct uio *uio;
620 int flag;
621{
622 struct tun_softc *tp = dev->si_drv1;
623 struct ifnet *ifp = &tp->tun_if;
624 struct mbuf *top, **mp, *m;
625 int error=0, tlen, mlen;
626 uint32_t family;
627
628 TUNDEBUG("%s%d: tunwrite\n", ifp->if_name, ifp->if_unit);
629
630 if (uio->uio_resid == 0)
631 return 0;
632
633 if (uio->uio_resid < 0 || uio->uio_resid > TUNMRU) {
634 TUNDEBUG("%s%d: len=%d!\n", ifp->if_name, ifp->if_unit,
635 uio->uio_resid);
636 return EIO;
637 }
638 tlen = uio->uio_resid;
639
640 /* get a header mbuf */
641 MGETHDR(m, M_DONTWAIT, MT_DATA);
642 if (m == NULL)
643 return ENOBUFS;
644 mlen = MHLEN;
645
646 top = 0;
647 mp = &top;
648 while (error == 0 && uio->uio_resid > 0) {
649 m->m_len = min(mlen, uio->uio_resid);
650 error = uiomove(mtod (m, caddr_t), m->m_len, uio);
651 *mp = m;
652 mp = &m->m_next;
653 if (uio->uio_resid > 0) {
654 MGET (m, M_DONTWAIT, MT_DATA);
655 if (m == 0) {
656 error = ENOBUFS;
657 break;
658 }
659 mlen = MLEN;
660 }
661 }
662 if (error) {
663 if (top)
664 m_freem (top);
665 ifp->if_ierrors++;
666 return error;
667 }
668
669 top->m_pkthdr.len = tlen;
670 top->m_pkthdr.rcvif = ifp;
671
672 if (ifp->if_bpf) {
673 if (tp->tun_flags & TUN_IFHEAD) {
674 /*
675 * Conveniently, we already have a 4-byte address
676 * family prepended to our packet !
677 * Inconveniently, it's in the wrong byte order !
678 */
679 if ((top = m_pullup(top, sizeof(family))) == NULL)
680 return ENOBUFS;
681 *mtod(top, u_int32_t *) =
682 ntohl(*mtod(top, u_int32_t *));
683 bpf_mtap(ifp, top);
684 *mtod(top, u_int32_t *) =
685 htonl(*mtod(top, u_int32_t *));
686 } else {
687 /*
688 * We need to prepend the address family as
689 * a four byte field. Cons up a dummy header
690 * to pacify bpf. This is safe because bpf
691 * will only read from the mbuf (i.e., it won't
692 * try to free it or keep a pointer to it).
693 */
694 struct mbuf m;
695 uint32_t af = AF_INET;
696
697 m.m_next = top;
698 m.m_len = 4;
699 m.m_data = (char *)&af;
700
701 bpf_mtap(ifp, &m);
702 }
703 }
704
705 if (tp->tun_flags & TUN_IFHEAD) {
706 if (top->m_len < sizeof(family) &&
707 (top = m_pullup(top, sizeof(family))) == NULL)
708 return ENOBUFS;
709 family = ntohl(*mtod(top, u_int32_t *));
710 m_adj(top, sizeof(family));
711 } else
712 family = AF_INET;
713
714 ifp->if_ibytes += top->m_pkthdr.len;
715 ifp->if_ipackets++;
716
717 return family_enqueue(family, top);
718}
719
720/*
721 * tunpoll - the poll interface, this is only useful on reads
722 * really. The write detect always returns true, write never blocks
723 * anyway, it either accepts the packet or drops it.
724 */
725static int
726tunpoll(dev, events, p)
727 dev_t dev;
728 int events;
729 struct proc *p;
730{
731 int s;
732 struct tun_softc *tp = dev->si_drv1;
733 struct ifnet *ifp = &tp->tun_if;
734 int revents = 0;
735
736 s = splimp();
737 TUNDEBUG("%s%d: tunpoll\n", ifp->if_name, ifp->if_unit);
738
739 if (events & (POLLIN | POLLRDNORM)) {
740 if (ifp->if_snd.ifq_len > 0) {
741 TUNDEBUG("%s%d: tunpoll q=%d\n", ifp->if_name,
742 ifp->if_unit, ifp->if_snd.ifq_len);
743 revents |= events & (POLLIN | POLLRDNORM);
744 } else {
745 TUNDEBUG("%s%d: tunpoll waiting\n", ifp->if_name,
746 ifp->if_unit);
747 selrecord(p, &tp->tun_rsel);
748 }
749 }
750 if (events & (POLLOUT | POLLWRNORM))
751 revents |= events & (POLLOUT | POLLWRNORM);
752
753 splx(s);
754 return (revents);
755}