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