|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. ! 3: * ! 4: * @APPLE_LICENSE_HEADER_START@ ! 5: * ! 6: * The contents of this file constitute Original Code as defined in and ! 7: * are subject to the Apple Public Source License Version 1.1 (the ! 8: * "License"). You may not use this file except in compliance with the ! 9: * License. Please obtain a copy of the License at ! 10: * http://www.apple.com/publicsource and read it before using this file. ! 11: * ! 12: * This Original Code and all software distributed under the License are ! 13: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER ! 14: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, ! 15: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, ! 16: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the ! 17: * License for the specific language governing rights and limitations ! 18: * under the License. ! 19: * ! 20: * @APPLE_LICENSE_HEADER_END@ ! 21: */ ! 22: /* $NetBSD: if_tun.c,v 1.14 1994/06/29 06:36:25 cgd Exp $ */ ! 23: ! 24: /* ! 25: * Copyright (c) 1988, Julian Onions <[email protected]> ! 26: * Nottingham University 1987. ! 27: * ! 28: * This source may be freely distributed, however I would be interested ! 29: * in any changes that are made. ! 30: * ! 31: * This driver takes packets off the IP i/f and hands them up to a ! 32: * user process to have its wicked way with. This driver has it's ! 33: * roots in a similar driver written by Phil Cockcroft (formerly) at ! 34: * UCL. This driver is based much more on read/write/poll mode of ! 35: * operation though. ! 36: */ ! 37: ! 38: #include "tun.h" ! 39: #if NTUN > 0 ! 40: ! 41: #include "opt_devfs.h" ! 42: #include "opt_inet.h" ! 43: ! 44: #include <sys/param.h> ! 45: #include <sys/proc.h> ! 46: #include <sys/systm.h> ! 47: #include <sys/mbuf.h> ! 48: #include <sys/socket.h> ! 49: #include <sys/filio.h> ! 50: #include <sys/sockio.h> ! 51: #include <sys/ttycom.h> ! 52: #include <sys/poll.h> ! 53: #include <sys/signalvar.h> ! 54: #include <sys/filedesc.h> ! 55: #include <sys/kernel.h> ! 56: #include <sys/sysctl.h> ! 57: #if DEVFS ! 58: #include <sys/devfsext.h> ! 59: #endif /*DEVFS*/ ! 60: #include <sys/conf.h> ! 61: #include <sys/uio.h> ! 62: #include <sys/vnode.h> ! 63: ! 64: #include <net/if.h> ! 65: #include <net/netisr.h> ! 66: #include <net/route.h> ! 67: ! 68: #if INET ! 69: #include <netinet/in.h> ! 70: #include <netinet/in_var.h> ! 71: #endif ! 72: ! 73: #if NS ! 74: #include <netns/ns.h> ! 75: #include <netns/ns_if.h> ! 76: #endif ! 77: ! 78: #include "bpfilter.h" ! 79: #if NBPFILTER > 0 ! 80: #include <net/bpf.h> ! 81: #endif ! 82: ! 83: #include <net/if_tunvar.h> ! 84: #include <net/if_tun.h> ! 85: ! 86: static void tunattach __P((void *)); ! 87: PSEUDO_SET(tunattach, if_tun); ! 88: ! 89: #define TUNDEBUG if (tundebug) printf ! 90: static int tundebug = 0; ! 91: SYSCTL_INT(_debug, OID_AUTO, if_tun_debug, CTLFLAG_RW, &tundebug, 0, ""); ! 92: ! 93: static struct tun_softc tunctl[NTUN]; ! 94: ! 95: static int tunoutput __P((struct ifnet *, struct mbuf *, struct sockaddr *, ! 96: struct rtentry *rt)); ! 97: static int tunifioctl __P((struct ifnet *, u_long, caddr_t)); ! 98: static int tuninit __P((int)); ! 99: ! 100: static d_open_t tunopen; ! 101: static d_close_t tunclose; ! 102: static d_read_t tunread; ! 103: static d_write_t tunwrite; ! 104: static d_ioctl_t tunioctl; ! 105: static d_poll_t tunpoll; ! 106: ! 107: #define CDEV_MAJOR 52 ! 108: static struct cdevsw tun_cdevsw = { ! 109: tunopen, tunclose, tunread, tunwrite, ! 110: tunioctl, nullstop, noreset, nodevtotty, ! 111: tunpoll, nommap, nostrategy, "tun", NULL, -1 ! 112: }; ! 113: ! 114: ! 115: static int tun_devsw_installed; ! 116: #if DEVFS ! 117: static void *tun_devfs_token[NTUN]; ! 118: #endif ! 119: ! 120: #define minor_val(n) ((((n) & ~0xff) << 8) | ((n) & 0xff)) ! 121: #define dev_val(n) (((n) >> 8) | ((n) & 0xff)) ! 122: ! 123: static void ! 124: tunattach(dummy) ! 125: void *dummy; ! 126: { ! 127: register int i; ! 128: struct ifnet *ifp; ! 129: dev_t dev; ! 130: ! 131: if ( tun_devsw_installed ) ! 132: return; ! 133: dev = makedev(CDEV_MAJOR, 0); ! 134: cdevsw_add(&dev, &tun_cdevsw, NULL); ! 135: tun_devsw_installed = 1; ! 136: for ( i = 0; i < NTUN; i++ ) { ! 137: #if DEVFS ! 138: tun_devfs_token[i] = devfs_add_devswf(&tun_cdevsw, minor_val(i), ! 139: DV_CHR, UID_UUCP, ! 140: GID_DIALER, 0600, ! 141: "tun%d", i); ! 142: #endif ! 143: tunctl[i].tun_flags = TUN_INITED; ! 144: ! 145: ifp = &tunctl[i].tun_if; ! 146: ifp->if_unit = i; ! 147: ifp->if_name = "tun"; ! 148: ifp->if_family = APPLE_IF_FAM_TUN; ! 149: ifp->if_mtu = TUNMTU; ! 150: ifp->if_ioctl = tunifioctl; ! 151: ifp->if_output = tunoutput; ! 152: ifp->if_flags = IFF_POINTOPOINT | IFF_MULTICAST; ! 153: ifp->if_snd.ifq_maxlen = ifqmaxlen; ! 154: if_attach(ifp); ! 155: #if NBPFILTER > 0 ! 156: bpfattach(ifp, DLT_NULL, sizeof(u_int)); ! 157: #endif ! 158: } ! 159: } ! 160: ! 161: /* ! 162: * tunnel open - must be superuser & the device must be ! 163: * configured in ! 164: */ ! 165: static int ! 166: tunopen(dev, flag, mode, p) ! 167: dev_t dev; ! 168: int flag, mode; ! 169: struct proc *p; ! 170: { ! 171: struct ifnet *ifp; ! 172: struct tun_softc *tp; ! 173: register int unit, error; ! 174: ! 175: error = suser(p->p_ucred, &p->p_acflag); ! 176: if (error) ! 177: return (error); ! 178: ! 179: if ((unit = dev_val(minor(dev))) >= NTUN) ! 180: return (ENXIO); ! 181: tp = &tunctl[unit]; ! 182: if (tp->tun_flags & TUN_OPEN) ! 183: return EBUSY; ! 184: ifp = &tp->tun_if; ! 185: tp->tun_flags |= TUN_OPEN; ! 186: TUNDEBUG("%s%d: open\n", ifp->if_name, ifp->if_unit); ! 187: return (0); ! 188: } ! 189: ! 190: /* ! 191: * tunclose - close the device - mark i/f down & delete ! 192: * routing info ! 193: */ ! 194: static int ! 195: tunclose(dev, foo, bar, p) ! 196: dev_t dev; ! 197: int foo; ! 198: int bar; ! 199: struct proc *p; ! 200: { ! 201: register int unit = dev_val(minor(dev)), s; ! 202: struct tun_softc *tp = &tunctl[unit]; ! 203: struct ifnet *ifp = &tp->tun_if; ! 204: struct mbuf *m; ! 205: ! 206: tp->tun_flags &= ~TUN_OPEN; ! 207: ! 208: /* ! 209: * junk all pending output ! 210: */ ! 211: do { ! 212: s = splimp(); ! 213: IF_DEQUEUE(&ifp->if_snd, m); ! 214: splx(s); ! 215: if (m) ! 216: m_freem(m); ! 217: } while (m); ! 218: ! 219: if (ifp->if_flags & IFF_UP) { ! 220: s = splimp(); ! 221: if_down(ifp); ! 222: if (ifp->if_flags & IFF_RUNNING) { ! 223: /* find internet addresses and delete routes */ ! 224: register struct ifaddr *ifa; ! 225: for (ifa = ifp->if_addrhead.tqh_first; ifa; ! 226: ifa = ifa->ifa_link.tqe_next) { ! 227: if (ifa->ifa_addr->sa_family == AF_INET) { ! 228: rtinit(ifa, (int)RTM_DELETE, ! 229: tp->tun_flags & TUN_DSTADDR ? RTF_HOST : 0); ! 230: } ! 231: } ! 232: } ! 233: splx(s); ! 234: } ! 235: funsetown(tp->tun_sigio); ! 236: selwakeup(&tp->tun_rsel); ! 237: ! 238: TUNDEBUG ("%s%d: closed\n", ifp->if_name, ifp->if_unit); ! 239: return (0); ! 240: } ! 241: ! 242: static int ! 243: tuninit(unit) ! 244: int unit; ! 245: { ! 246: struct tun_softc *tp = &tunctl[unit]; ! 247: struct ifnet *ifp = &tp->tun_if; ! 248: register struct ifaddr *ifa; ! 249: ! 250: TUNDEBUG("%s%d: tuninit\n", ifp->if_name, ifp->if_unit); ! 251: ! 252: ifp->if_flags |= IFF_UP | IFF_RUNNING; ! 253: getmicrotime(&ifp->if_lastchange); ! 254: ! 255: for (ifa = ifp->if_addrhead.tqh_first; ifa; ! 256: ifa = ifa->ifa_link.tqe_next) { ! 257: #if INET ! 258: if (ifa->ifa_addr->sa_family == AF_INET) { ! 259: struct sockaddr_in *si; ! 260: ! 261: si = (struct sockaddr_in *)ifa->ifa_addr; ! 262: if (si && si->sin_addr.s_addr) ! 263: tp->tun_flags |= TUN_IASET; ! 264: ! 265: si = (struct sockaddr_in *)ifa->ifa_dstaddr; ! 266: if (si && si->sin_addr.s_addr) ! 267: tp->tun_flags |= TUN_DSTADDR; ! 268: } ! 269: #endif ! 270: } ! 271: return 0; ! 272: } ! 273: ! 274: /* ! 275: * Process an ioctl request. ! 276: */ ! 277: int ! 278: tunifioctl(ifp, cmd, data) ! 279: struct ifnet *ifp; ! 280: u_long cmd; ! 281: caddr_t data; ! 282: { ! 283: register struct ifreq *ifr = (struct ifreq *)data; ! 284: int error = 0, s; ! 285: ! 286: s = splimp(); ! 287: switch(cmd) { ! 288: case SIOCSIFADDR: ! 289: tuninit(ifp->if_unit); ! 290: TUNDEBUG("%s%d: address set\n", ! 291: ifp->if_name, ifp->if_unit); ! 292: break; ! 293: case SIOCSIFDSTADDR: ! 294: tuninit(ifp->if_unit); ! 295: TUNDEBUG("%s%d: destination address set\n", ! 296: ifp->if_name, ifp->if_unit); ! 297: break; ! 298: case SIOCSIFMTU: ! 299: ifp->if_mtu = ifr->ifr_mtu; ! 300: TUNDEBUG("%s%d: mtu set\n", ! 301: ifp->if_name, ifp->if_unit); ! 302: break; ! 303: case SIOCADDMULTI: ! 304: case SIOCDELMULTI: ! 305: break; ! 306: ! 307: ! 308: default: ! 309: error = EINVAL; ! 310: } ! 311: splx(s); ! 312: return (error); ! 313: } ! 314: ! 315: /* ! 316: * tunoutput - queue packets from higher level ready to put out. ! 317: */ ! 318: int ! 319: tunoutput(ifp, m0, dst, rt) ! 320: struct ifnet *ifp; ! 321: struct mbuf *m0; ! 322: struct sockaddr *dst; ! 323: struct rtentry *rt; ! 324: { ! 325: struct tun_softc *tp = &tunctl[ifp->if_unit]; ! 326: int s; ! 327: ! 328: TUNDEBUG ("%s%d: tunoutput\n", ifp->if_name, ifp->if_unit); ! 329: ! 330: if ((tp->tun_flags & TUN_READY) != TUN_READY) { ! 331: TUNDEBUG ("%s%d: not ready 0%o\n", ifp->if_name, ! 332: ifp->if_unit, tp->tun_flags); ! 333: m_freem (m0); ! 334: return EHOSTDOWN; ! 335: } ! 336: ! 337: #if NBPFILTER > 0 ! 338: /* BPF write needs to be handled specially */ ! 339: if (dst->sa_family == AF_UNSPEC) { ! 340: dst->sa_family = *(mtod(m0, int *)); ! 341: m0->m_len -= sizeof(int); ! 342: m0->m_pkthdr.len -= sizeof(int); ! 343: m0->m_data += sizeof(int); ! 344: } ! 345: ! 346: if (ifp->if_bpf) { ! 347: /* ! 348: * We need to prepend the address family as ! 349: * a four byte field. Cons up a dummy header ! 350: * to pacify bpf. This is safe because bpf ! 351: * will only read from the mbuf (i.e., it won't ! 352: * try to free it or keep a pointer to it). ! 353: */ ! 354: struct mbuf m; ! 355: u_int af = dst->sa_family; ! 356: ! 357: m.m_next = m0; ! 358: m.m_len = 4; ! 359: m.m_data = (char *)⁡ ! 360: ! 361: bpf_mtap(ifp, &m); ! 362: } ! 363: #endif ! 364: ! 365: switch(dst->sa_family) { ! 366: #if INET ! 367: case AF_INET: ! 368: s = splimp(); ! 369: if (IF_QFULL(&ifp->if_snd)) { ! 370: IF_DROP(&ifp->if_snd); ! 371: m_freem(m0); ! 372: splx(s); ! 373: ifp->if_collisions++; ! 374: return (ENOBUFS); ! 375: } ! 376: ifp->if_obytes += m0->m_pkthdr.len; ! 377: IF_ENQUEUE(&ifp->if_snd, m0); ! 378: splx(s); ! 379: ifp->if_opackets++; ! 380: break; ! 381: #endif ! 382: default: ! 383: m_freem(m0); ! 384: return EAFNOSUPPORT; ! 385: } ! 386: ! 387: if (tp->tun_flags & TUN_RWAIT) { ! 388: tp->tun_flags &= ~TUN_RWAIT; ! 389: wakeup((caddr_t)tp); ! 390: } ! 391: if (tp->tun_flags & TUN_ASYNC && tp->tun_sigio) ! 392: pgsigio(tp->tun_sigio, SIGIO, 0); ! 393: selwakeup(&tp->tun_rsel); ! 394: return 0; ! 395: } ! 396: ! 397: /* ! 398: * the cdevsw interface is now pretty minimal. ! 399: */ ! 400: static int ! 401: tunioctl(dev, cmd, data, flag, p) ! 402: dev_t dev; ! 403: u_long cmd; ! 404: caddr_t data; ! 405: int flag; ! 406: struct proc *p; ! 407: { ! 408: int unit = dev_val(minor(dev)), s; ! 409: struct tun_softc *tp = &tunctl[unit]; ! 410: struct tuninfo *tunp; ! 411: ! 412: switch (cmd) { ! 413: case TUNSIFINFO: ! 414: tunp = (struct tuninfo *)data; ! 415: tp->tun_if.if_mtu = tunp->mtu; ! 416: tp->tun_if.if_type = tunp->type; ! 417: tp->tun_if.if_baudrate = tunp->baudrate; ! 418: break; ! 419: case TUNGIFINFO: ! 420: tunp = (struct tuninfo *)data; ! 421: tunp->mtu = tp->tun_if.if_mtu; ! 422: tunp->type = tp->tun_if.if_type; ! 423: tunp->baudrate = tp->tun_if.if_baudrate; ! 424: break; ! 425: case TUNSDEBUG: ! 426: tundebug = *(int *)data; ! 427: break; ! 428: case TUNGDEBUG: ! 429: *(int *)data = tundebug; ! 430: break; ! 431: case FIONBIO: ! 432: break; ! 433: case FIOASYNC: ! 434: if (*(int *)data) ! 435: tp->tun_flags |= TUN_ASYNC; ! 436: else ! 437: tp->tun_flags &= ~TUN_ASYNC; ! 438: break; ! 439: case FIONREAD: ! 440: s = splimp(); ! 441: if (tp->tun_if.if_snd.ifq_head) { ! 442: struct mbuf *mb = tp->tun_if.if_snd.ifq_head; ! 443: for( *(int *)data = 0; mb != 0; mb = mb->m_next) ! 444: *(int *)data += mb->m_len; ! 445: } else ! 446: *(int *)data = 0; ! 447: splx(s); ! 448: break; ! 449: case FIOSETOWN: ! 450: return (fsetown(*(int *)data, &tp->tun_sigio)); ! 451: ! 452: case FIOGETOWN: ! 453: *(int *)data = fgetown(tp->tun_sigio); ! 454: return (0); ! 455: ! 456: /* This is deprecated, FIOSETOWN should be used instead. */ ! 457: case TIOCSPGRP: ! 458: return (fsetown(-(*(int *)data), &tp->tun_sigio)); ! 459: ! 460: /* This is deprecated, FIOGETOWN should be used instead. */ ! 461: case TIOCGPGRP: ! 462: *(int *)data = -fgetown(tp->tun_sigio); ! 463: return (0); ! 464: ! 465: default: ! 466: return (ENOTTY); ! 467: } ! 468: return (0); ! 469: } ! 470: ! 471: /* ! 472: * The cdevsw read interface - reads a packet at a time, or at ! 473: * least as much of a packet as can be read. ! 474: */ ! 475: static int ! 476: tunread(dev, uio, flag) ! 477: dev_t dev; ! 478: struct uio *uio; ! 479: int flag; ! 480: { ! 481: int unit = dev_val(minor(dev)); ! 482: struct tun_softc *tp = &tunctl[unit]; ! 483: struct ifnet *ifp = &tp->tun_if; ! 484: struct mbuf *m, *m0; ! 485: int error=0, len, s; ! 486: ! 487: TUNDEBUG ("%s%d: read\n", ifp->if_name, ifp->if_unit); ! 488: if ((tp->tun_flags & TUN_READY) != TUN_READY) { ! 489: TUNDEBUG ("%s%d: not ready 0%o\n", ifp->if_name, ! 490: ifp->if_unit, tp->tun_flags); ! 491: return EHOSTDOWN; ! 492: } ! 493: ! 494: tp->tun_flags &= ~TUN_RWAIT; ! 495: ! 496: s = splimp(); ! 497: do { ! 498: IF_DEQUEUE(&ifp->if_snd, m0); ! 499: if (m0 == 0) { ! 500: if (flag & IO_NDELAY) { ! 501: splx(s); ! 502: return EWOULDBLOCK; ! 503: } ! 504: tp->tun_flags |= TUN_RWAIT; ! 505: if( error = tsleep((caddr_t)tp, PCATCH | (PZERO + 1), ! 506: "tunread", 0)) { ! 507: splx(s); ! 508: return error; ! 509: } ! 510: } ! 511: } while (m0 == 0); ! 512: splx(s); ! 513: ! 514: while (m0 && uio->uio_resid > 0 && error == 0) { ! 515: len = min(uio->uio_resid, m0->m_len); ! 516: if (len == 0) ! 517: break; ! 518: error = uiomove(mtod(m0, caddr_t), len, uio); ! 519: MFREE(m0, m); ! 520: m0 = m; ! 521: } ! 522: ! 523: if (m0) { ! 524: TUNDEBUG("Dropping mbuf\n"); ! 525: m_freem(m0); ! 526: } ! 527: return error; ! 528: } ! 529: ! 530: /* ! 531: * the cdevsw write interface - an atomic write is a packet - or else! ! 532: */ ! 533: static int ! 534: tunwrite(dev, uio, flag) ! 535: dev_t dev; ! 536: struct uio *uio; ! 537: int flag; ! 538: { ! 539: int unit = dev_val(minor(dev)); ! 540: struct ifnet *ifp = &tunctl[unit].tun_if; ! 541: struct mbuf *top, **mp, *m; ! 542: int error=0, s, tlen, mlen; ! 543: ! 544: TUNDEBUG("%s%d: tunwrite\n", ifp->if_name, ifp->if_unit); ! 545: ! 546: if (uio->uio_resid < 0 || uio->uio_resid > TUNMRU) { ! 547: TUNDEBUG("%s%d: len=%d!\n", ifp->if_name, ifp->if_unit, ! 548: uio->uio_resid); ! 549: return EIO; ! 550: } ! 551: tlen = uio->uio_resid; ! 552: ! 553: /* get a header mbuf */ ! 554: MGETHDR(m, M_DONTWAIT, MT_DATA); ! 555: if (m == NULL) ! 556: return ENOBUFS; ! 557: mlen = MHLEN; ! 558: ! 559: top = 0; ! 560: mp = ⊤ ! 561: while (error == 0 && uio->uio_resid > 0) { ! 562: m->m_len = min(mlen, uio->uio_resid); ! 563: error = uiomove(mtod (m, caddr_t), m->m_len, uio); ! 564: *mp = m; ! 565: mp = &m->m_next; ! 566: if (uio->uio_resid > 0) { ! 567: MGET (m, M_DONTWAIT, MT_DATA); ! 568: if (m == 0) { ! 569: error = ENOBUFS; ! 570: break; ! 571: } ! 572: mlen = MLEN; ! 573: } ! 574: } ! 575: if (error) { ! 576: if (top) ! 577: m_freem (top); ! 578: return error; ! 579: } ! 580: ! 581: top->m_pkthdr.len = tlen; ! 582: top->m_pkthdr.rcvif = ifp; ! 583: ! 584: #if NBPFILTER > 0 ! 585: if (ifp->if_bpf) { ! 586: /* ! 587: * We need to prepend the address family as ! 588: * a four byte field. Cons up a dummy header ! 589: * to pacify bpf. This is safe because bpf ! 590: * will only read from the mbuf (i.e., it won't ! 591: * try to free it or keep a pointer to it). ! 592: */ ! 593: struct mbuf m; ! 594: u_int af = AF_INET; ! 595: ! 596: m.m_next = top; ! 597: m.m_len = 4; ! 598: m.m_data = (char *)⁡ ! 599: ! 600: bpf_mtap(ifp, &m); ! 601: } ! 602: #endif ! 603: ! 604: #if INET ! 605: s = splimp(); ! 606: if (IF_QFULL (&ipintrq)) { ! 607: IF_DROP(&ipintrq); ! 608: splx(s); ! 609: ifp->if_collisions++; ! 610: m_freem(top); ! 611: return ENOBUFS; ! 612: } ! 613: IF_ENQUEUE(&ipintrq, top); ! 614: splx(s); ! 615: ifp->if_ibytes += tlen; ! 616: ifp->if_ipackets++; ! 617: schednetisr(NETISR_IP); ! 618: #endif ! 619: return error; ! 620: } ! 621: ! 622: /* ! 623: * tunpoll - the poll interface, this is only useful on reads ! 624: * really. The write detect always returns true, write never blocks ! 625: * anyway, it either accepts the packet or drops it. ! 626: */ ! 627: static int ! 628: tunpoll(dev, events, p) ! 629: dev_t dev; ! 630: int events; ! 631: struct proc *p; ! 632: { ! 633: int unit = dev_val(minor(dev)), s; ! 634: struct tun_softc *tp = &tunctl[unit]; ! 635: struct ifnet *ifp = &tp->tun_if; ! 636: int revents = 0; ! 637: ! 638: s = splimp(); ! 639: TUNDEBUG("%s%d: tunpoll\n", ifp->if_name, ifp->if_unit); ! 640: ! 641: if (events & (POLLIN | POLLRDNORM)) ! 642: if (ifp->if_snd.ifq_len > 0) { ! 643: TUNDEBUG("%s%d: tunpoll q=%d\n", ifp->if_name, ! 644: ifp->if_unit, ifp->if_snd.ifq_len); ! 645: revents |= events & (POLLIN | POLLRDNORM); ! 646: } else { ! 647: TUNDEBUG("%s%d: tunpoll waiting\n", ifp->if_name, ! 648: ifp->if_unit); ! 649: selrecord(p, &tp->tun_rsel); ! 650: } ! 651: ! 652: if (events & (POLLOUT | POLLWRNORM)) ! 653: revents |= events & (POLLOUT | POLLWRNORM); ! 654: ! 655: splx(s); ! 656: return (revents); ! 657: } ! 658: ! 659: ! 660: #endif /* NTUN */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.