| 1 | /* UNFINISHED CONVERSION TO 386BSD -wfj */ |
| 2 | |
| 3 | /* if_tun.c - tunnel interface module & driver */ |
| 4 | |
| 5 | /* |
| 6 | * Copyright (c) 1988, Julian Onions. |
| 7 | * |
| 8 | * This source may be freely distributed, however I would be interested |
| 9 | * in any changes that are made. |
| 10 | */ |
| 11 | |
| 12 | /* |
| 13 | * 90/02/06 15:03 - Fixed a bug in where TIOCGPGRP and TIOCSPGRP were |
| 14 | * mixed up. Anders Klemets - klemets@sics.se |
| 15 | * |
| 16 | * $Header: if_tun.c,v 1.13 88/07/11 08:28:51 jpo Exp $ |
| 17 | * |
| 18 | * $Log: if_tun.c,v $ |
| 19 | * Revision 1.13 88/07/11 08:28:51 jpo |
| 20 | * Some tidying up |
| 21 | * |
| 22 | * Revision 1.12 87/12/10 09:16:29 jpo |
| 23 | * Decided the vax/mtpr was unnecessay. |
| 24 | * |
| 25 | * Revision 1.11 87/12/10 09:10:36 jpo |
| 26 | * Fixed some minor things & 1 major bug. |
| 27 | * |
| 28 | * Revision 1.10 87/11/04 14:27:41 jpo |
| 29 | * A few sanity checks added. |
| 30 | * |
| 31 | * Revision 1.9 87/11/04 14:13:45 jpo |
| 32 | * Added some ioctls for non-blocking & async I/O |
| 33 | * |
| 34 | * Revision 1.8 87/10/19 10:28:14 jpo |
| 35 | * Another touch up (sigh) |
| 36 | * |
| 37 | * Revision 1.7 87/10/19 10:25:48 jpo |
| 38 | * Touch up. |
| 39 | * |
| 40 | * Revision 1.6 87/10/19 09:15:14 jpo |
| 41 | * Touch up. |
| 42 | * |
| 43 | * Revision 1.5 87/10/19 08:34:51 jpo |
| 44 | * General clean up - plus sun specific fixes |
| 45 | * |
| 46 | * Revision 1.4 87/10/16 17:10:12 jpo |
| 47 | * Purged all ioctl read/writes and non-standard routing stuff. |
| 48 | * |
| 49 | * Revision 1.3 87/10/05 11:57:09 jpo |
| 50 | * More debugging - in error mainly. |
| 51 | * |
| 52 | * Revision 1.2 87/10/04 18:29:45 jpo |
| 53 | * Select & read/write working. |
| 54 | */ |
| 55 | |
| 56 | #include "tun.h" |
| 57 | #if NTUN > 0 |
| 58 | |
| 59 | /* |
| 60 | * Tunnel driver. |
| 61 | * |
| 62 | * This driver takes packets off the IP i/f and hands them up to a |
| 63 | * user process to have it's wicked way with. This driver has it's |
| 64 | * roots in a similar driver written by Phil Cockcroft (formerly) at |
| 65 | * UCL. This driver is based much more on read/write/select mode of |
| 66 | * operation though. |
| 67 | * |
| 68 | * Julian Onions <jpo@cs.nott.ac.uk> |
| 69 | * Nottingham University 1987. |
| 70 | */ |
| 71 | |
| 72 | #include "param.h" |
| 73 | #include "systm.h" |
| 74 | #include "mbuf.h" |
| 75 | #include "buf.h" |
| 76 | #include "protosw.h" |
| 77 | #include "socket.h" |
| 78 | #include "ioctl.h" |
| 79 | #include "errno.h" |
| 80 | #include "syslog.h" |
| 81 | |
| 82 | #include "net/if.h" |
| 83 | #include "net/netisr.h" |
| 84 | #include "net/route.h" |
| 85 | |
| 86 | #ifdef INET |
| 87 | #include "netinet/in.h" |
| 88 | #include "netinet/in_systm.h" |
| 89 | #include "netinet/in_var.h" |
| 90 | #include "netinet/ip.h" |
| 91 | #include "netinet/if_ether.h" |
| 92 | #endif |
| 93 | |
| 94 | #ifdef NS |
| 95 | #include "netns/ns.h" |
| 96 | #include "netns/ns_if.h" |
| 97 | #endif |
| 98 | |
| 99 | #define TUNDEBUG if (tundebug) printf |
| 100 | |
| 101 | int tundebug = 0; |
| 102 | struct tunctl |
| 103 | { |
| 104 | u_short tun_flags; /* misc flags */ |
| 105 | struct ifnet tun_if; /* the interface */ |
| 106 | int tun_pgrp; /* the process group - if any */ |
| 107 | struct proc *tun_rsel; /* read select */ |
| 108 | struct proc *tun_wsel; /* write select (not used) */ |
| 109 | } tunctl[NTUN]; |
| 110 | |
| 111 | extern int ifqmaxlen; |
| 112 | |
| 113 | int tunoutput (), tunioctl (), tuninit (); |
| 114 | |
| 115 | /* tunnel open - must be superuser & the device must be configured in */ |
| 116 | tunopen (dev, flag) |
| 117 | dev_t dev; |
| 118 | { |
| 119 | register int unit; |
| 120 | struct tunctl *tp; |
| 121 | register struct ifnet *ifp; |
| 122 | |
| 123 | if (!suser ()) |
| 124 | return EACCES; |
| 125 | if ((unit = minor (dev)) >= NTUN) |
| 126 | return (ENXIO); |
| 127 | tp = &tunctl[unit]; |
| 128 | if (tp->tun_flags & TUN_OPEN) |
| 129 | return ENXIO; |
| 130 | if ((tp->tun_flags & TUN_INITED) == 0) { |
| 131 | tp->tun_flags = TUN_INITED; |
| 132 | tunattach (unit); |
| 133 | } |
| 134 | ifp = &tp->tun_if; |
| 135 | tp->tun_flags |= TUN_OPEN; |
| 136 | TUNDEBUG ("%s%d: open\n", ifp->if_name, ifp->if_unit); |
| 137 | return (0); |
| 138 | } |
| 139 | |
| 140 | /* tunclose - close the device - mark i/f down & delete routing info */ |
| 141 | tunclose (dev, flag) |
| 142 | dev_t dev; |
| 143 | { |
| 144 | int s; |
| 145 | int rcoll; |
| 146 | register int unit = minor (dev); |
| 147 | register struct tunctl *tp = &tunctl[unit]; |
| 148 | register struct ifnet *ifp = &tp->tun_if; |
| 149 | register struct mbuf *m; |
| 150 | |
| 151 | rcoll = tp->tun_flags & TUN_RCOLL; |
| 152 | tp->tun_flags &= TUN_INITED; |
| 153 | |
| 154 | /* |
| 155 | * junk all pending output |
| 156 | */ |
| 157 | do { |
| 158 | s = splimp (); |
| 159 | IF_DEQUEUE (&ifp->if_snd, m); |
| 160 | splx (s); |
| 161 | if (m) /* actually - m_freem checks this - but what the hell */ |
| 162 | m_freem (m); |
| 163 | } while (m != 0); |
| 164 | if (ifp->if_flags & IFF_UP) { |
| 165 | s = splimp (); |
| 166 | if_down (ifp); |
| 167 | if (ifp->if_flags & IFF_RUNNING) |
| 168 | rtinit (ifp->if_addrlist, (int)SIOCDELRT, RTF_HOST); |
| 169 | splx (s); |
| 170 | } |
| 171 | tp -> tun_pgrp = 0; |
| 172 | if (tp -> tun_rsel) |
| 173 | selwakeup (tp->tun_rsel, rcoll); |
| 174 | |
| 175 | tp -> tun_rsel = tp -> tun_wsel = (struct proc *)0; |
| 176 | |
| 177 | TUNDEBUG ("%s%d: closed\n", ifp->if_name, ifp->if_unit); |
| 178 | return (0); |
| 179 | } |
| 180 | |
| 181 | /* |
| 182 | * attach an interface N.B. argument is not same as other drivers |
| 183 | */ |
| 184 | int |
| 185 | tunattach (unit) |
| 186 | int unit; |
| 187 | { |
| 188 | register struct ifnet *ifp = &tunctl[unit].tun_if; |
| 189 | register struct sockaddr_in *sin; |
| 190 | |
| 191 | ifp->if_unit = unit; |
| 192 | ifp->if_name = "tun"; |
| 193 | ifp->if_mtu = TUNMTU; |
| 194 | ifp->if_ioctl = tunioctl; |
| 195 | ifp->if_output = tunoutput; |
| 196 | ifp->if_init = tuninit; |
| 197 | #ifndef BSD4_3 |
| 198 | sin = (struct sockaddr_in *) & ifp->if_addr; |
| 199 | sin->sin_family = AF_INET; |
| 200 | #endif |
| 201 | ifp->if_flags = IFF_POINTOPOINT; |
| 202 | ifp->if_snd.ifq_maxlen = ifqmaxlen; |
| 203 | ifp->if_collisions = ifp->if_ierrors = ifp->if_oerrors = 0; |
| 204 | ifp->if_ipackets = ifp->if_opackets = 0; |
| 205 | if_attach (ifp); |
| 206 | TUNDEBUG ("%s%d: tunattach\n", ifp->if_name, ifp->if_unit); |
| 207 | return 0; |
| 208 | } |
| 209 | |
| 210 | int |
| 211 | tuninit (unit) |
| 212 | int unit; |
| 213 | { |
| 214 | register struct tunctl *tp = &tunctl[unit]; |
| 215 | register struct ifnet *ifp = &tp->tun_if; |
| 216 | #ifndef BSD4_3 |
| 217 | register struct sockaddr_in *sin; |
| 218 | |
| 219 | sin = (struct sockaddr_in *) & ifp->if_addr; |
| 220 | if (sin->sin_addr.s_addr == 0) /* if address still unknown */ |
| 221 | return; |
| 222 | if_rtinit (ifp, RTF_UP); |
| 223 | #endif |
| 224 | ifp->if_flags |= IFF_UP | IFF_RUNNING; |
| 225 | tp->tun_flags |= TUN_IASET; |
| 226 | TUNDEBUG ("%s%d: tuninit\n", ifp->if_name, ifp->if_unit); |
| 227 | return 0; |
| 228 | } |
| 229 | |
| 230 | /* |
| 231 | * Process an ioctl request. |
| 232 | * The problem here is 4.2 pass a struct ifreq * to this routine, |
| 233 | * sun only passes a struct sockaddr * since 3.2 at least. This is |
| 234 | * rather upsetting! Also, sun doesn't pass the SIOCDSTADDR ioctl through |
| 235 | * so we can't detect this being set directly. This is the reason for |
| 236 | * tuncheckready. |
| 237 | * Under 4.3 and SunOs 4.0 we now get the SIOCSIFDSTADDR ioctl, and we get a |
| 238 | * struct in_ifaddr * for data. (tte) |
| 239 | */ |
| 240 | |
| 241 | #if !defined(BSD4_3) && defined(sun) |
| 242 | |
| 243 | /* |
| 244 | * workaround for not being able to detect DSTADDR being set. |
| 245 | */ |
| 246 | |
| 247 | tuncheckready (ifp) |
| 248 | struct ifnet *ifp; |
| 249 | { |
| 250 | struct tunctl *tp = &tunctl[ifp->if_unit]; |
| 251 | struct sockaddr_in *sin; |
| 252 | |
| 253 | sin = (struct sockaddr_in *) &ifp->if_dstaddr; |
| 254 | if (sin->sin_addr.s_addr == 0) |
| 255 | return 0; |
| 256 | tp -> tun_flags |= TUN_DSTADDR; |
| 257 | return 1; |
| 258 | } |
| 259 | #else |
| 260 | #define tuncheckready(dummy) 1 |
| 261 | #endif /* !defined(BSD4_3) && defined(sun) */ |
| 262 | |
| 263 | int |
| 264 | tunioctl (ifp, cmd, data) |
| 265 | register struct ifnet *ifp; |
| 266 | int cmd; |
| 267 | caddr_t data; |
| 268 | { |
| 269 | #ifndef BSD4_3 |
| 270 | #ifdef sun |
| 271 | struct sockaddr_in *sin = (struct sockaddr_in *) data; |
| 272 | #else |
| 273 | struct sockaddr_in *sin; |
| 274 | struct ifreq *ifr = (struct ifreq *) data; |
| 275 | #endif |
| 276 | #endif /* BSD4_3 */ |
| 277 | |
| 278 | int s = splimp (), error = 0; |
| 279 | struct tunctl *tp = &tunctl[ifp->if_unit]; |
| 280 | |
| 281 | switch (cmd) |
| 282 | { |
| 283 | case SIOCSIFADDR: |
| 284 | #ifndef BSD4_3 |
| 285 | if (ifp->if_flags & IFF_RUNNING) |
| 286 | if_rtinit (ifp, -1); /* delete previous route */ |
| 287 | #ifndef sun |
| 288 | sin = (struct sockaddr_in *)&ifr -> ifr_addr; |
| 289 | #endif |
| 290 | ifp->if_addr = *((struct sockaddr *) sin); |
| 291 | ifp->if_net = in_netof (sin->sin_addr); |
| 292 | ifp->if_host[0] = in_lnaof (sin->sin_addr); |
| 293 | #endif |
| 294 | tuninit (ifp->if_unit); |
| 295 | break; |
| 296 | case SIOCSIFDSTADDR: |
| 297 | tp->tun_flags |= TUN_DSTADDR; |
| 298 | #ifndef BSD4_3 |
| 299 | #ifndef sun |
| 300 | sin = (struct sockaddr_in *)&ifr -> ifr_addr; |
| 301 | #endif |
| 302 | ifp->if_dstaddr = *((struct sockaddr *)sin); |
| 303 | #endif BSD4_3 |
| 304 | TUNDEBUG ("%s%d: destination addres set\n", ifp->if_name, |
| 305 | ifp -> if_unit); |
| 306 | break; |
| 307 | default: |
| 308 | error = EINVAL; |
| 309 | } |
| 310 | splx (s); |
| 311 | return (error); |
| 312 | } |
| 313 | |
| 314 | /* |
| 315 | * tunoutput - queue packets from higher level ready to put out. |
| 316 | */ |
| 317 | tunoutput (ifp, m0, dst) |
| 318 | struct ifnet *ifp; |
| 319 | struct mbuf *m0; |
| 320 | struct sockaddr *dst; |
| 321 | { |
| 322 | struct tunctl *tp; |
| 323 | struct proc *p; |
| 324 | int s; |
| 325 | |
| 326 | TUNDEBUG ("%s%d: tunoutput\n", ifp->if_name, ifp->if_unit); |
| 327 | tp = &tunctl[ifp->if_unit]; |
| 328 | if ((tp->tun_flags & TUN_READY) != TUN_READY) { |
| 329 | if(tuncheckready(ifp) == 0) { |
| 330 | TUNDEBUG ("%s%d: not ready 0%o\n", ifp->if_name, |
| 331 | ifp->if_unit, tp->tun_flags); |
| 332 | m_freem (m0); |
| 333 | return EHOSTDOWN; |
| 334 | } |
| 335 | } |
| 336 | |
| 337 | switch (dst->sa_family) { |
| 338 | #ifdef INET |
| 339 | case AF_INET: |
| 340 | s = splimp (); |
| 341 | if (IF_QFULL (&ifp->if_snd)) |
| 342 | { |
| 343 | IF_DROP (&ifp->if_snd); |
| 344 | m_freem (m0); |
| 345 | splx (s); |
| 346 | ifp->if_collisions++; |
| 347 | return (ENOBUFS); |
| 348 | } |
| 349 | IF_ENQUEUE (&ifp->if_snd, m0); |
| 350 | splx (s); |
| 351 | ifp->if_opackets++; |
| 352 | break; |
| 353 | #endif |
| 354 | default: |
| 355 | m_freem (m0); |
| 356 | return EAFNOSUPPORT; |
| 357 | } |
| 358 | |
| 359 | if (tp->tun_flags & TUN_RWAIT) { |
| 360 | tp->tun_flags &= ~TUN_RWAIT; |
| 361 | wakeup ((caddr_t) tp); |
| 362 | } |
| 363 | if (tp->tun_flags & TUN_ASYNC && tp -> tun_pgrp != 0) { |
| 364 | if (tp->tun_pgrp > 0) |
| 365 | gsignal (tp->tun_pgrp, SIGIO); |
| 366 | else if ((p = pfind (-tp->tun_pgrp)) != 0) |
| 367 | psignal (p, SIGIO); |
| 368 | } |
| 369 | if (tp->tun_rsel) { |
| 370 | selwakeup (tp->tun_rsel, tp->tun_flags & TUN_RCOLL); |
| 371 | tp->tun_flags &= ~TUN_RCOLL; |
| 372 | tp->tun_rsel = (struct proc *) 0; |
| 373 | } |
| 374 | return 0; |
| 375 | } |
| 376 | |
| 377 | /* |
| 378 | * the cdevsw interface is now pretty minimal. |
| 379 | */ |
| 380 | |
| 381 | tuncioctl (dev, cmd, data, flag) |
| 382 | dev_t dev; |
| 383 | caddr_t data; |
| 384 | { |
| 385 | int unit = minor(dev); |
| 386 | struct tunctl *tp = &tunctl[unit]; |
| 387 | int s; |
| 388 | |
| 389 | switch (cmd) { |
| 390 | case TUNSDEBUG: |
| 391 | tundebug = *(int *)data; |
| 392 | break; |
| 393 | |
| 394 | case TUNGDEBUG: |
| 395 | *(int *)data = tundebug; |
| 396 | break; |
| 397 | |
| 398 | case FIONBIO: |
| 399 | if (*(int *)data) |
| 400 | tp->tun_flags |= TUN_NBIO; |
| 401 | else |
| 402 | tp->tun_flags &= ~TUN_NBIO; |
| 403 | break; |
| 404 | |
| 405 | case FIOASYNC: |
| 406 | if (*(int *)data) |
| 407 | tp->tun_flags |= TUN_ASYNC; |
| 408 | else |
| 409 | tp->tun_flags &= ~TUN_ASYNC; |
| 410 | break; |
| 411 | |
| 412 | case FIONREAD: |
| 413 | s = splimp (); |
| 414 | if (tp->tun_if.if_snd.ifq_head) |
| 415 | *(int *)data = tp->tun_if.if_snd.ifq_head->m_len; |
| 416 | else *(int *)data = 0; |
| 417 | splx (s); |
| 418 | break; |
| 419 | |
| 420 | case TIOCSPGRP: |
| 421 | tp->tun_pgrp = *(int *)data; |
| 422 | break; |
| 423 | |
| 424 | case TIOCGPGRP: |
| 425 | *(int *)data = tp->tun_pgrp; |
| 426 | break; |
| 427 | |
| 428 | default: |
| 429 | return (ENOTTY); |
| 430 | } |
| 431 | return (0); |
| 432 | } |
| 433 | |
| 434 | /* |
| 435 | * The cdevsw read interface - reads a packet at a time, or at least as much |
| 436 | * of a packet as can be read. |
| 437 | */ |
| 438 | tunread (dev, uio) |
| 439 | dev_t dev; |
| 440 | struct uio *uio; |
| 441 | { |
| 442 | register struct ifnet *ifp; |
| 443 | register struct mbuf *m, *m0; |
| 444 | int unit = minor (dev); |
| 445 | int len, s; |
| 446 | int error = 0; |
| 447 | struct tunctl *tp; |
| 448 | |
| 449 | tp = &tunctl[unit]; |
| 450 | ifp = &tp->tun_if; |
| 451 | TUNDEBUG ("%s%d: read\n", ifp->if_name, ifp->if_unit); |
| 452 | if ((tp->tun_flags & TUN_READY) != TUN_READY) { |
| 453 | if(tuncheckready(ifp) == 0) { |
| 454 | TUNDEBUG ("%s%d: not ready 0%o\n", ifp->if_name, |
| 455 | ifp->if_unit, tp->tun_flags); |
| 456 | return EHOSTDOWN; |
| 457 | } |
| 458 | } |
| 459 | |
| 460 | tp->tun_flags &= ~TUN_RWAIT; |
| 461 | |
| 462 | s = splimp (); |
| 463 | do { |
| 464 | IF_DEQUEUE (&ifp->if_snd, m0); |
| 465 | if (m0 == 0) { |
| 466 | if (tp -> tun_flags & TUN_NBIO) { |
| 467 | splx (s); |
| 468 | return EWOULDBLOCK; |
| 469 | } |
| 470 | tp->tun_flags |= TUN_RWAIT; |
| 471 | sleep ((caddr_t) tp, PZERO + 1); |
| 472 | } |
| 473 | } while (m0 == 0); |
| 474 | splx (s); |
| 475 | |
| 476 | while (m0 && uio->uio_resid > 0 && error == 0) { |
| 477 | len = MIN (uio->uio_resid, m0->m_len); |
| 478 | if (len == 0) |
| 479 | break; |
| 480 | error = uiomove (mtod (m0, caddr_t), len, |
| 481 | UIO_READ, uio); |
| 482 | MFREE (m0, m); |
| 483 | m0 = m; |
| 484 | } |
| 485 | |
| 486 | if (m0 != 0) { |
| 487 | TUNDEBUG ("Dropping mbuf\n"); |
| 488 | m_freem (m0); |
| 489 | } |
| 490 | return error; |
| 491 | } |
| 492 | |
| 493 | /* |
| 494 | * the cdevsw write interface - an atomic write is a packet - or else! |
| 495 | */ |
| 496 | |
| 497 | tunwrite (dev, uio) |
| 498 | int dev; |
| 499 | struct uio *uio; |
| 500 | { |
| 501 | int error = 0; |
| 502 | int unit = minor (dev); |
| 503 | struct mbuf *top, **mp, *m; |
| 504 | struct ifnet *ifp = &(tunctl[unit].tun_if); |
| 505 | int s; |
| 506 | |
| 507 | TUNDEBUG ("%s%d: tunwrite\n", ifp->if_name, ifp->if_unit); |
| 508 | |
| 509 | if (uio->uio_resid < 0 || uio->uio_resid > TUNMTU) { |
| 510 | TUNDEBUG ("%s%d: len=%d!\n", ifp->if_name, ifp->if_unit, |
| 511 | uio->uio_resid); |
| 512 | return EIO; |
| 513 | } |
| 514 | top = 0; |
| 515 | mp = ⊤ |
| 516 | while (error == 0 && uio->uio_resid > 0) { |
| 517 | MGET (m, M_DONTWAIT, MT_DATA); |
| 518 | if (m == 0) { |
| 519 | error = ENOBUFS; |
| 520 | break; |
| 521 | } |
| 522 | m->m_len = MIN (MLEN, uio->uio_resid); |
| 523 | error = uiomove (mtod (m, caddr_t), m->m_len, UIO_WRITE, uio); |
| 524 | *mp = m; |
| 525 | mp = &m->m_next; |
| 526 | } |
| 527 | if (error) { |
| 528 | if (top) |
| 529 | m_freem (top); |
| 530 | return error; |
| 531 | } |
| 532 | |
| 533 | #ifdef BSD4_3 |
| 534 | /* |
| 535 | * Place interface pointer before the data |
| 536 | * for the receiving protocol. |
| 537 | */ |
| 538 | if (top->m_off <= MMAXOFF && |
| 539 | top->m_off >= MMINOFF + sizeof(struct ifnet *)) { |
| 540 | top->m_off -= sizeof(struct ifnet *); |
| 541 | top->m_len += sizeof(struct ifnet *); |
| 542 | } else { |
| 543 | MGET(m, M_DONTWAIT, MT_HEADER); |
| 544 | if (m == (struct mbuf *)0) |
| 545 | return (ENOBUFS); |
| 546 | m->m_len = sizeof(struct ifnet *); |
| 547 | m->m_next = top; |
| 548 | top = m; |
| 549 | } |
| 550 | *(mtod(top, struct ifnet **)) = ifp; |
| 551 | #endif /* BSD4_3 */ |
| 552 | |
| 553 | s = splimp (); |
| 554 | if (IF_QFULL (&ipintrq)) { |
| 555 | IF_DROP (&ipintrq); |
| 556 | splx (s); |
| 557 | ifp->if_collisions++; |
| 558 | m_freem (top); |
| 559 | return ENOBUFS; |
| 560 | } |
| 561 | IF_ENQUEUE (&ipintrq, top); |
| 562 | splx (s); |
| 563 | ifp->if_ipackets++; |
| 564 | schednetisr (NETISR_IP); |
| 565 | return error; |
| 566 | } |
| 567 | |
| 568 | /* |
| 569 | * tunselect - the select interface, this is only useful on reads really. |
| 570 | * The write detect always returns true, write never blocks anyway, it either |
| 571 | * accepts the packet or drops it. |
| 572 | */ |
| 573 | tunselect (dev, rw) |
| 574 | dev_t dev; |
| 575 | int rw; |
| 576 | { |
| 577 | int unit = minor (dev); |
| 578 | register struct tunctl *tp = &tunctl[unit]; |
| 579 | struct ifnet *ifp = &tp->tun_if; |
| 580 | int s = splimp (); |
| 581 | |
| 582 | TUNDEBUG ("%s%d: tunselect\n", ifp->if_name, ifp->if_unit); |
| 583 | switch (rw) { |
| 584 | case FREAD: |
| 585 | if (ifp->if_snd.ifq_len > 0) { |
| 586 | splx (s); |
| 587 | TUNDEBUG ("%s%d: tunselect q=%d\n", ifp->if_name, |
| 588 | ifp->if_unit, ifp->if_snd.ifq_len); |
| 589 | return 1; |
| 590 | } |
| 591 | if (tp->tun_rsel && tp->tun_rsel->p_wchan == |
| 592 | (caddr_t) & selwait) |
| 593 | tp->tun_flags |= TUN_RCOLL; |
| 594 | else |
| 595 | tp->tun_rsel = u.u_procp; |
| 596 | break; |
| 597 | case FWRITE: |
| 598 | splx (s); |
| 599 | return 1; |
| 600 | } |
| 601 | splx (s); |
| 602 | TUNDEBUG ("%s%d: tunselect waiting\n", ifp->if_name, ifp->if_unit); |
| 603 | return 0; |
| 604 | } |
| 605 | #endif NTUN |