Annotation of src/sys/net/if_tun.c, Revision 1.140
1.140 ! maya 1: /* $NetBSD: if_tun.c,v 1.139 2017/05/24 06:52:14 pgoyette Exp $ */
1.14 cgd 2:
1.1 cgd 3: /*
1.8 deraadt 4: * Copyright (c) 1988, Julian Onions <jpo@cs.nott.ac.uk>
5: * Nottingham University 1987.
1.1 cgd 6: *
7: * This source may be freely distributed, however I would be interested
8: * in any changes that are made.
1.6 deraadt 9: *
1.1 cgd 10: * This driver takes packets off the IP i/f and hands them up to a
1.21 scottr 11: * user process to have its wicked way with. This driver has its
1.1 cgd 12: * roots in a similar driver written by Phil Cockcroft (formerly) at
1.27 mycroft 13: * UCL. This driver is based much more on read/write/poll mode of
1.1 cgd 14: * operation though.
15: */
1.48 lukem 16:
1.136 skrll 17: /*
18: * tun - tunnel software network interface.
19: */
20:
1.48 lukem 21: #include <sys/cdefs.h>
1.140 ! maya 22: __KERNEL_RCSID(0, "$NetBSD: if_tun.c,v 1.139 2017/05/24 06:52:14 pgoyette Exp $");
1.33 jonathan 23:
1.123 pooka 24: #ifdef _KERNEL_OPT
1.33 jonathan 25: #include "opt_inet.h"
1.123 pooka 26: #endif
1.8 deraadt 27:
1.6 deraadt 28: #include <sys/param.h>
1.136 skrll 29:
1.6 deraadt 30: #include <sys/buf.h>
1.135 skrll 31: #include <sys/conf.h>
32: #include <sys/cpu.h>
33: #include <sys/device.h>
1.8 deraadt 34: #include <sys/file.h>
1.135 skrll 35: #include <sys/ioctl.h>
1.89 elad 36: #include <sys/kauth.h>
1.136 skrll 37: #include <sys/kmem.h>
38: #include <sys/lwp.h>
1.135 skrll 39: #include <sys/mbuf.h>
40: #include <sys/module.h>
1.115 rmind 41: #include <sys/mutex.h>
1.135 skrll 42: #include <sys/poll.h>
43: #include <sys/select.h>
44: #include <sys/signalvar.h>
45: #include <sys/socket.h>
1.6 deraadt 46:
1.135 skrll 47: #include <net/bpf.h>
1.6 deraadt 48: #include <net/if.h>
1.69 tron 49: #include <net/if_types.h>
1.6 deraadt 50: #include <net/route.h>
1.1 cgd 51:
52: #ifdef INET
1.6 deraadt 53: #include <netinet/in.h>
54: #include <netinet/in_systm.h>
55: #include <netinet/in_var.h>
56: #include <netinet/ip.h>
1.30 is 57: #include <netinet/if_inarp.h>
1.1 cgd 58: #endif
59:
1.8 deraadt 60: #include <net/if_tun.h>
61:
1.122 christos 62: #include "ioconf.h"
63:
1.29 christos 64: #define TUNDEBUG if (tundebug) printf
1.6 deraadt 65: int tundebug = 0;
1.1 cgd 66:
1.6 deraadt 67: extern int ifqmaxlen;
1.78 thorpej 68:
69: static LIST_HEAD(, tun_softc) tun_softc_list;
70: static LIST_HEAD(, tun_softc) tunz_softc_list;
1.117 skrll 71: static kmutex_t tun_softc_lock;
1.6 deraadt 72:
1.97 christos 73: static int tun_ioctl(struct ifnet *, u_long, void *);
1.96 dyoung 74: static int tun_output(struct ifnet *, struct mbuf *,
1.125 ozaki-r 75: const struct sockaddr *, const struct rtentry *rt);
1.78 thorpej 76: static int tun_clone_create(struct if_clone *, int);
77: static int tun_clone_destroy(struct ifnet *);
1.6 deraadt 78:
1.78 thorpej 79: static struct if_clone tun_cloner =
1.46 atatat 80: IF_CLONE_INITIALIZER("tun", tun_clone_create, tun_clone_destroy);
81:
1.78 thorpej 82: static void tunattach0(struct tun_softc *);
1.132 ozaki-r 83: static void tun_enable(struct tun_softc *, const struct ifaddr *);
1.106 ad 84: static void tun_i_softintr(void *);
85: static void tun_o_softintr(void *);
1.50 itojun 86: #ifdef ALTQ
1.78 thorpej 87: static void tunstart(struct ifnet *);
1.50 itojun 88: #endif
1.78 thorpej 89: static struct tun_softc *tun_find_unit(dev_t);
90: static struct tun_softc *tun_find_zunit(int);
1.53 gehenna 91:
1.78 thorpej 92: static dev_type_open(tunopen);
93: static dev_type_close(tunclose);
94: static dev_type_read(tunread);
95: static dev_type_write(tunwrite);
96: static dev_type_ioctl(tunioctl);
97: static dev_type_poll(tunpoll);
98: static dev_type_kqfilter(tunkqfilter);
1.53 gehenna 99:
100: const struct cdevsw tun_cdevsw = {
1.116 dholland 101: .d_open = tunopen,
102: .d_close = tunclose,
103: .d_read = tunread,
104: .d_write = tunwrite,
105: .d_ioctl = tunioctl,
106: .d_stop = nostop,
107: .d_tty = notty,
108: .d_poll = tunpoll,
109: .d_mmap = nommap,
110: .d_kqfilter = tunkqfilter,
1.120 dholland 111: .d_discard = nodiscard,
1.136 skrll 112: .d_flag = D_OTHER | D_MPSAFE
1.53 gehenna 113: };
1.6 deraadt 114:
1.130 ozaki-r 115: #ifdef _MODULE
116: devmajor_t tun_bmajor = -1, tun_cmajor = -1;
117: #endif
118:
1.8 deraadt 119: void
1.94 christos 120: tunattach(int unused)
1.8 deraadt 121: {
1.46 atatat 122:
1.128 christos 123: /*
124: * Nothing to do here, initialization is handled by the
1.129 ozaki-r 125: * module initialization code in tuninit() below).
1.128 christos 126: */
127: }
128:
129: static void
130: tuninit(void)
131: {
132:
1.117 skrll 133: mutex_init(&tun_softc_lock, MUTEX_DEFAULT, IPL_NET);
1.46 atatat 134: LIST_INIT(&tun_softc_list);
1.70 pk 135: LIST_INIT(&tunz_softc_list);
1.46 atatat 136: if_clone_attach(&tun_cloner);
1.130 ozaki-r 137: #ifdef _MODULE
138: devsw_attach("tun", NULL, &tun_bmajor, &tun_cdevsw, &tun_cmajor);
139: #endif
1.46 atatat 140: }
141:
1.128 christos 142: static int
143: tundetach(void)
144: {
145: int error = 0;
146:
147: if (!LIST_EMPTY(&tun_softc_list) || !LIST_EMPTY(&tunz_softc_list))
148: error = EBUSY;
149:
1.130 ozaki-r 150: #ifdef _MODULE
151: if (error == 0)
152: error = devsw_detach(NULL, &tun_cdevsw);
153: #endif
1.128 christos 154: if (error == 0) {
155: if_clone_detach(&tun_cloner);
156: mutex_destroy(&tun_softc_lock);
157: }
158:
159: return error;
160: }
161:
1.70 pk 162: /*
163: * Find driver instance from dev_t.
164: * Returns with tp locked (if found).
165: */
166: static struct tun_softc *
1.78 thorpej 167: tun_find_unit(dev_t dev)
1.70 pk 168: {
169: struct tun_softc *tp;
170: int unit = minor(dev);
171:
1.117 skrll 172: mutex_enter(&tun_softc_lock);
1.70 pk 173: LIST_FOREACH(tp, &tun_softc_list, tun_list)
174: if (unit == tp->tun_unit)
175: break;
176: if (tp)
1.115 rmind 177: mutex_enter(&tp->tun_lock);
1.117 skrll 178: mutex_exit(&tun_softc_lock);
1.70 pk 179:
1.135 skrll 180: return tp;
1.70 pk 181: }
182:
183: /*
184: * Find zombie driver instance by unit number.
185: * Remove tp from list and return it unlocked (if found).
186: */
187: static struct tun_softc *
1.78 thorpej 188: tun_find_zunit(int unit)
1.70 pk 189: {
190: struct tun_softc *tp;
191:
1.117 skrll 192: mutex_enter(&tun_softc_lock);
1.70 pk 193: LIST_FOREACH(tp, &tunz_softc_list, tun_list)
194: if (unit == tp->tun_unit)
195: break;
196: if (tp)
197: LIST_REMOVE(tp, tun_list);
1.117 skrll 198: mutex_exit(&tun_softc_lock);
1.137 skrll 199: KASSERTMSG(!tp || (tp->tun_flags & (TUN_INITED|TUN_OPEN)) == TUN_OPEN,
1.136 skrll 200: "tun%d: inconsistent flags: %x", unit, tp->tun_flags);
1.70 pk 201:
1.135 skrll 202: return tp;
1.70 pk 203: }
204:
1.78 thorpej 205: static int
206: tun_clone_create(struct if_clone *ifc, int unit)
1.46 atatat 207: {
1.70 pk 208: struct tun_softc *tp;
1.46 atatat 209:
1.70 pk 210: if ((tp = tun_find_zunit(unit)) == NULL) {
1.136 skrll 211: tp = kmem_zalloc(sizeof(*tp), KM_SLEEP);
1.46 atatat 212:
1.70 pk 213: tp->tun_unit = unit;
1.115 rmind 214: mutex_init(&tp->tun_lock, MUTEX_DEFAULT, IPL_NET);
1.136 skrll 215: cv_init(&tp->tun_cv, "tunread");
1.104 rmind 216: selinit(&tp->tun_rsel);
217: selinit(&tp->tun_wsel);
1.70 pk 218: } else {
219: /* Revive tunnel instance; clear ifp part */
220: (void)memset(&tp->tun_if, 0, sizeof(struct ifnet));
221: }
1.46 atatat 222:
1.107 christos 223: if_initname(&tp->tun_if, ifc->ifc_name, unit);
1.70 pk 224: tunattach0(tp);
225: tp->tun_flags |= TUN_INITED;
1.106 ad 226: tp->tun_osih = softint_establish(SOFTINT_CLOCK, tun_o_softintr, tp);
227: tp->tun_isih = softint_establish(SOFTINT_CLOCK, tun_i_softintr, tp);
1.46 atatat 228:
1.117 skrll 229: mutex_enter(&tun_softc_lock);
1.70 pk 230: LIST_INSERT_HEAD(&tun_softc_list, tp, tun_list);
1.117 skrll 231: mutex_exit(&tun_softc_lock);
1.46 atatat 232:
1.135 skrll 233: return 0;
1.46 atatat 234: }
235:
1.78 thorpej 236: static void
237: tunattach0(struct tun_softc *tp)
1.46 atatat 238: {
1.70 pk 239: struct ifnet *ifp;
1.46 atatat 240:
1.70 pk 241: ifp = &tp->tun_if;
242: ifp->if_softc = tp;
1.46 atatat 243: ifp->if_mtu = TUNMTU;
244: ifp->if_ioctl = tun_ioctl;
245: ifp->if_output = tun_output;
1.50 itojun 246: #ifdef ALTQ
247: ifp->if_start = tunstart;
248: #endif
1.46 atatat 249: ifp->if_flags = IFF_POINTOPOINT;
1.69 tron 250: ifp->if_type = IFT_TUNNEL;
1.46 atatat 251: ifp->if_snd.ifq_maxlen = ifqmaxlen;
252: ifp->if_collisions = 0;
253: ifp->if_ierrors = 0;
254: ifp->if_oerrors = 0;
255: ifp->if_ipackets = 0;
256: ifp->if_opackets = 0;
1.58 jdolecek 257: ifp->if_ibytes = 0;
258: ifp->if_obytes = 0;
1.46 atatat 259: ifp->if_dlt = DLT_NULL;
1.50 itojun 260: IFQ_SET_READY(&ifp->if_snd);
1.46 atatat 261: if_attach(ifp);
262: if_alloc_sadl(ifp);
1.113 joerg 263: bpf_attach(ifp, DLT_NULL, sizeof(uint32_t));
1.46 atatat 264: }
265:
1.78 thorpej 266: static int
267: tun_clone_destroy(struct ifnet *ifp)
1.46 atatat 268: {
269: struct tun_softc *tp = (void *)ifp;
1.136 skrll 270: bool zombie = false;
1.8 deraadt 271:
1.115 rmind 272: IF_PURGE(&ifp->if_snd);
273: ifp->if_flags &= ~IFF_RUNNING;
274:
1.117 skrll 275: mutex_enter(&tun_softc_lock);
1.115 rmind 276: mutex_enter(&tp->tun_lock);
1.46 atatat 277: LIST_REMOVE(tp, tun_list);
1.70 pk 278: if (tp->tun_flags & TUN_OPEN) {
1.136 skrll 279: /* Hang on to storage until last close. */
1.70 pk 280: tp->tun_flags &= ~TUN_INITED;
281: LIST_INSERT_HEAD(&tunz_softc_list, tp, tun_list);
1.136 skrll 282: zombie = true;
1.70 pk 283: }
1.117 skrll 284: mutex_exit(&tun_softc_lock);
1.46 atatat 285:
286: if (tp->tun_flags & TUN_RWAIT) {
287: tp->tun_flags &= ~TUN_RWAIT;
1.136 skrll 288: cv_broadcast(&tp->tun_cv);
1.46 atatat 289: }
1.104 rmind 290: selnotify(&tp->tun_rsel, 0, 0);
1.8 deraadt 291:
1.115 rmind 292: mutex_exit(&tp->tun_lock);
1.70 pk 293:
1.106 ad 294: if (tp->tun_flags & TUN_ASYNC && tp->tun_pgid)
295: fownsignal(tp->tun_pgid, SIGIO, POLL_HUP, 0, NULL);
296:
1.113 joerg 297: bpf_detach(ifp);
1.46 atatat 298: if_detach(ifp);
299:
1.104 rmind 300: if (!zombie) {
301: seldestroy(&tp->tun_rsel);
302: seldestroy(&tp->tun_wsel);
1.106 ad 303: softint_disestablish(tp->tun_osih);
304: softint_disestablish(tp->tun_isih);
1.115 rmind 305: mutex_destroy(&tp->tun_lock);
1.139 pgoyette 306: cv_destroy(&tp->tun_cv);
1.136 skrll 307: kmem_free(tp, sizeof(*tp));
1.104 rmind 308: }
1.73 peter 309:
1.135 skrll 310: return 0;
1.8 deraadt 311: }
312:
1.6 deraadt 313: /*
314: * tunnel open - must be superuser & the device must be
315: * configured in
316: */
1.78 thorpej 317: static int
1.94 christos 318: tunopen(dev_t dev, int flag, int mode, struct lwp *l)
1.6 deraadt 319: {
320: struct ifnet *ifp;
1.8 deraadt 321: struct tun_softc *tp;
1.117 skrll 322: int error;
1.1 cgd 323:
1.111 elad 324: error = kauth_authorize_network(l->l_cred, KAUTH_NETWORK_INTERFACE_TUN,
325: KAUTH_REQ_NETWORK_INTERFACE_TUN_ADD, NULL, NULL, NULL);
326: if (error)
1.135 skrll 327: return error;
1.5 deraadt 328:
1.46 atatat 329: tp = tun_find_unit(dev);
1.52 atatat 330:
1.70 pk 331: if (tp == NULL) {
1.52 atatat 332: (void)tun_clone_create(&tun_cloner, minor(dev));
333: tp = tun_find_unit(dev);
1.70 pk 334: if (tp == NULL) {
1.136 skrll 335: return ENXIO;
1.70 pk 336: }
1.52 atatat 337: }
1.46 atatat 338:
339: if (tp->tun_flags & TUN_OPEN) {
1.136 skrll 340: mutex_exit(&tp->tun_lock);
341: return EBUSY;
1.46 atatat 342: }
343:
1.6 deraadt 344: ifp = &tp->tun_if;
345: tp->tun_flags |= TUN_OPEN;
1.24 thorpej 346: TUNDEBUG("%s: open\n", ifp->if_xname);
1.136 skrll 347:
1.115 rmind 348: mutex_exit(&tp->tun_lock);
1.136 skrll 349:
1.135 skrll 350: return error;
1.1 cgd 351: }
352:
1.6 deraadt 353: /*
354: * tunclose - close the device - mark i/f down & delete
355: * routing info
356: */
357: int
1.94 christos 358: tunclose(dev_t dev, int flag, int mode,
359: struct lwp *l)
1.6 deraadt 360: {
1.46 atatat 361: struct tun_softc *tp;
362: struct ifnet *ifp;
1.6 deraadt 363:
1.70 pk 364: if ((tp = tun_find_zunit(minor(dev))) != NULL) {
365: /* interface was "destroyed" before the close */
1.104 rmind 366: seldestroy(&tp->tun_rsel);
367: seldestroy(&tp->tun_wsel);
1.106 ad 368: softint_disestablish(tp->tun_osih);
369: softint_disestablish(tp->tun_isih);
1.115 rmind 370: mutex_destroy(&tp->tun_lock);
1.136 skrll 371: kmem_free(tp, sizeof(*tp));
372: return 0;
1.70 pk 373: }
1.46 atatat 374:
1.70 pk 375: if ((tp = tun_find_unit(dev)) == NULL)
376: goto out_nolock;
1.46 atatat 377:
378: ifp = &tp->tun_if;
379:
1.10 andrew 380: tp->tun_flags &= ~TUN_OPEN;
1.6 deraadt 381:
1.115 rmind 382: tp->tun_pgid = 0;
383: selnotify(&tp->tun_rsel, 0, 0);
384:
385: TUNDEBUG ("%s: closed\n", ifp->if_xname);
386: mutex_exit(&tp->tun_lock);
387:
1.6 deraadt 388: /*
389: * junk all pending output
390: */
1.50 itojun 391: IFQ_PURGE(&ifp->if_snd);
1.6 deraadt 392:
393: if (ifp->if_flags & IFF_UP) {
394: if_down(ifp);
1.8 deraadt 395: if (ifp->if_flags & IFF_RUNNING) {
1.17 mycroft 396: /* find internet addresses and delete routes */
1.39 augustss 397: struct ifaddr *ifa;
1.127 ozaki-r 398: IFADDR_READER_FOREACH(ifa, ifp) {
1.80 rpaulo 399: #if defined(INET) || defined(INET6)
400: if (ifa->ifa_addr->sa_family == AF_INET ||
401: ifa->ifa_addr->sa_family == AF_INET6) {
1.18 mycroft 402: rtinit(ifa, (int)RTM_DELETE,
1.26 pk 403: tp->tun_flags & TUN_DSTADDR
404: ? RTF_HOST
405: : 0);
1.18 mycroft 406: }
1.38 itojun 407: #endif
1.11 deraadt 408: }
1.8 deraadt 409: }
1.6 deraadt 410: }
1.70 pk 411: out_nolock:
1.135 skrll 412: return 0;
1.1 cgd 413: }
414:
1.26 pk 415: static void
1.132 ozaki-r 416: tun_enable(struct tun_softc *tp, const struct ifaddr *ifa)
1.6 deraadt 417: {
418: struct ifnet *ifp = &tp->tun_if;
1.8 deraadt 419:
1.128 christos 420: TUNDEBUG("%s: %s\n", __func__, ifp->if_xname);
1.6 deraadt 421:
1.115 rmind 422: mutex_enter(&tp->tun_lock);
1.6 deraadt 423: ifp->if_flags |= IFF_UP | IFF_RUNNING;
1.8 deraadt 424:
1.26 pk 425: tp->tun_flags &= ~(TUN_IASET|TUN_DSTADDR);
1.132 ozaki-r 426:
427: switch (ifa->ifa_addr->sa_family) {
1.38 itojun 428: #ifdef INET
1.132 ozaki-r 429: case AF_INET: {
430: struct sockaddr_in *sin;
431:
432: sin = satosin(ifa->ifa_addr);
433: if (sin && sin->sin_addr.s_addr)
434: tp->tun_flags |= TUN_IASET;
1.11 deraadt 435:
1.132 ozaki-r 436: if (ifp->if_flags & IFF_POINTOPOINT) {
437: sin = satosin(ifa->ifa_dstaddr);
1.17 mycroft 438: if (sin && sin->sin_addr.s_addr)
1.132 ozaki-r 439: tp->tun_flags |= TUN_DSTADDR;
1.11 deraadt 440: }
1.132 ozaki-r 441: break;
442: }
1.38 itojun 443: #endif
1.80 rpaulo 444: #ifdef INET6
1.132 ozaki-r 445: case AF_INET6: {
446: struct sockaddr_in6 *sin;
1.80 rpaulo 447:
1.132 ozaki-r 448: sin = satosin6(ifa->ifa_addr);
449: if (!IN6_IS_ADDR_UNSPECIFIED(&sin->sin6_addr))
450: tp->tun_flags |= TUN_IASET;
451:
452: if (ifp->if_flags & IFF_POINTOPOINT) {
453: sin = satosin6(ifa->ifa_dstaddr);
454: if (sin && !IN6_IS_ADDR_UNSPECIFIED(&sin->sin6_addr))
455: tp->tun_flags |= TUN_DSTADDR;
456: } else
457: tp->tun_flags &= ~TUN_DSTADDR;
458: break;
459: }
1.80 rpaulo 460: #endif /* INET6 */
1.132 ozaki-r 461: default:
462: break;
1.18 mycroft 463: }
1.115 rmind 464: mutex_exit(&tp->tun_lock);
1.1 cgd 465: }
466:
467: /*
468: * Process an ioctl request.
469: */
1.78 thorpej 470: static int
1.97 christos 471: tun_ioctl(struct ifnet *ifp, u_long cmd, void *data)
1.6 deraadt 472: {
1.46 atatat 473: struct tun_softc *tp = (struct tun_softc *)(ifp->if_softc);
1.121 roy 474: struct ifreq *ifr = (struct ifreq *)data;
475: struct ifaddr *ifa = (struct ifaddr *)data;
1.136 skrll 476: int error = 0;
1.6 deraadt 477:
1.61 itojun 478: switch (cmd) {
1.108 dyoung 479: case SIOCINITIFADDR:
1.132 ozaki-r 480: tun_enable(tp, ifa);
1.121 roy 481: ifa->ifa_rtrequest = p2p_rtrequest;
1.24 thorpej 482: TUNDEBUG("%s: address set\n", ifp->if_xname);
1.6 deraadt 483: break;
1.26 pk 484: case SIOCSIFBRDADDR:
485: TUNDEBUG("%s: broadcast address set\n", ifp->if_xname);
486: break;
1.102 dyoung 487: case SIOCSIFMTU:
1.31 matt 488: if (ifr->ifr_mtu > TUNMTU || ifr->ifr_mtu < 576) {
1.102 dyoung 489: error = EINVAL;
490: break;
1.31 matt 491: }
492: TUNDEBUG("%s: interface mtu set\n", ifp->if_xname);
1.102 dyoung 493: if ((error = ifioctl_common(ifp, cmd, data)) == ENETRESET)
494: error = 0;
1.32 matt 495: break;
496: case SIOCADDMULTI:
1.102 dyoung 497: case SIOCDELMULTI:
498: if (ifr == NULL) {
1.32 matt 499: error = EAFNOSUPPORT; /* XXX */
500: break;
501: }
1.98 dyoung 502: switch (ifreq_getaddr(cmd, ifr)->sa_family) {
1.32 matt 503: #ifdef INET
504: case AF_INET:
505: break;
506: #endif
1.80 rpaulo 507: #ifdef INET6
508: case AF_INET6:
509: break;
510: #endif
1.32 matt 511: default:
512: error = EAFNOSUPPORT;
513: break;
514: }
1.31 matt 515: break;
1.6 deraadt 516: default:
1.108 dyoung 517: error = ifioctl_common(ifp, cmd, data);
1.6 deraadt 518: }
1.70 pk 519:
1.135 skrll 520: return error;
1.1 cgd 521: }
522:
523: /*
1.22 christos 524: * tun_output - queue packets from higher level ready to put out.
1.1 cgd 525: */
1.78 thorpej 526: static int
1.96 dyoung 527: tun_output(struct ifnet *ifp, struct mbuf *m0, const struct sockaddr *dst,
1.125 ozaki-r 528: const struct rtentry *rt)
1.6 deraadt 529: {
1.24 thorpej 530: struct tun_softc *tp = ifp->if_softc;
1.51 itojun 531: int error;
1.80 rpaulo 532: #if defined(INET) || defined(INET6)
1.75 christos 533: int mlen;
1.80 rpaulo 534: uint32_t *af;
1.38 itojun 535: #endif
1.6 deraadt 536:
1.115 rmind 537: mutex_enter(&tp->tun_lock);
1.24 thorpej 538: TUNDEBUG ("%s: tun_output\n", ifp->if_xname);
1.1 cgd 539:
1.8 deraadt 540: if ((tp->tun_flags & TUN_READY) != TUN_READY) {
1.24 thorpej 541: TUNDEBUG ("%s: not ready 0%o\n", ifp->if_xname,
542: tp->tun_flags);
1.70 pk 543: error = EHOSTDOWN;
1.138 maya 544: mutex_exit(&tp->tun_lock);
1.70 pk 545: goto out;
1.8 deraadt 546: }
1.136 skrll 547: // XXXrmind
548: mutex_exit(&tp->tun_lock);
1.8 deraadt 549:
1.50 itojun 550: /*
551: * if the queueing discipline needs packet classification,
552: * do it before prepending link headers.
553: */
1.124 knakahar 554: IFQ_CLASSIFY(&ifp->if_snd, m0, dst->sa_family);
1.70 pk 555:
1.113 joerg 556: bpf_mtap_af(ifp, dst->sa_family, m0);
1.8 deraadt 557:
1.6 deraadt 558: switch(dst->sa_family) {
1.79 rpaulo 559: #ifdef INET6
560: case AF_INET6:
561: #endif
1.1 cgd 562: #ifdef INET
1.6 deraadt 563: case AF_INET:
1.79 rpaulo 564: #endif
565: #if defined(INET) || defined(INET6)
1.26 pk 566: if (tp->tun_flags & TUN_PREPADDR) {
567: /* Simple link-layer header */
568: M_PREPEND(m0, dst->sa_len, M_DONTWAIT);
569: if (m0 == NULL) {
570: IF_DROP(&ifp->if_snd);
1.70 pk 571: error = ENOBUFS;
572: goto out;
1.26 pk 573: }
1.136 skrll 574: memcpy(mtod(m0, char *), dst, dst->sa_len);
1.85 rpaulo 575: }
576:
577: if (tp->tun_flags & TUN_IFHEAD) {
1.81 rpaulo 578: /* Prepend the address family */
1.82 rpaulo 579: M_PREPEND(m0, sizeof(*af), M_DONTWAIT);
1.80 rpaulo 580: if (m0 == NULL) {
581: IF_DROP(&ifp->if_snd);
582: error = ENOBUFS;
583: goto out;
584: }
585: af = mtod(m0,uint32_t *);
586: *af = htonl(dst->sa_family);
1.86 rpaulo 587: } else {
1.115 rmind 588: #ifdef INET
1.86 rpaulo 589: if (dst->sa_family != AF_INET)
590: #endif
591: {
592: error = EAFNOSUPPORT;
593: goto out;
594: }
1.26 pk 595: }
1.36 sommerfe 596: /* FALLTHROUGH */
597: case AF_UNSPEC:
1.124 knakahar 598: IFQ_ENQUEUE(&ifp->if_snd, m0, error);
1.50 itojun 599: if (error) {
1.6 deraadt 600: ifp->if_collisions++;
1.70 pk 601: error = EAFNOSUPPORT;
1.115 rmind 602: m0 = NULL;
1.70 pk 603: goto out;
1.6 deraadt 604: }
1.58 jdolecek 605: mlen = m0->m_pkthdr.len;
1.6 deraadt 606: ifp->if_opackets++;
1.58 jdolecek 607: ifp->if_obytes += mlen;
1.6 deraadt 608: break;
1.1 cgd 609: #endif
1.6 deraadt 610: default:
1.70 pk 611: error = EAFNOSUPPORT;
612: goto out;
1.6 deraadt 613: }
614:
1.136 skrll 615: mutex_enter(&tp->tun_lock);
1.6 deraadt 616: if (tp->tun_flags & TUN_RWAIT) {
617: tp->tun_flags &= ~TUN_RWAIT;
1.136 skrll 618: cv_broadcast(&tp->tun_cv);
1.6 deraadt 619: }
1.64 jdolecek 620: if (tp->tun_flags & TUN_ASYNC && tp->tun_pgid)
1.106 ad 621: softint_schedule(tp->tun_isih);
1.64 jdolecek 622:
1.104 rmind 623: selnotify(&tp->tun_rsel, 0, 0);
1.138 maya 624:
625: mutex_exit(&tp->tun_lock);
1.70 pk 626: out:
1.115 rmind 627: if (error && m0) {
628: m_freem(m0);
629: }
630: return 0;
1.1 cgd 631: }
632:
1.106 ad 633: static void
634: tun_i_softintr(void *cookie)
635: {
636: struct tun_softc *tp = cookie;
637:
638: if (tp->tun_flags & TUN_ASYNC && tp->tun_pgid)
639: fownsignal(tp->tun_pgid, SIGIO, POLL_IN, POLLIN|POLLRDNORM,
640: NULL);
641: }
642:
643: static void
644: tun_o_softintr(void *cookie)
645: {
646: struct tun_softc *tp = cookie;
647:
648: if (tp->tun_flags & TUN_ASYNC && tp->tun_pgid)
649: fownsignal(tp->tun_pgid, SIGIO, POLL_OUT, POLLOUT|POLLWRNORM,
650: NULL);
651: }
652:
1.1 cgd 653: /*
654: * the cdevsw interface is now pretty minimal.
655: */
1.6 deraadt 656: int
1.97 christos 657: tunioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
1.6 deraadt 658: {
1.46 atatat 659: struct tun_softc *tp;
1.136 skrll 660: int error = 0;
1.46 atatat 661:
662: tp = tun_find_unit(dev);
663:
664: /* interface was "destroyed" already */
1.70 pk 665: if (tp == NULL) {
1.136 skrll 666: return ENXIO;
1.70 pk 667: }
1.6 deraadt 668:
669: switch (cmd) {
670: case TUNSDEBUG:
671: tundebug = *(int *)data;
672: break;
1.26 pk 673:
1.6 deraadt 674: case TUNGDEBUG:
675: *(int *)data = tundebug;
676: break;
1.26 pk 677:
678: case TUNSIFMODE:
1.31 matt 679: switch (*(int *)data & (IFF_POINTOPOINT|IFF_BROADCAST)) {
1.26 pk 680: case IFF_POINTOPOINT:
681: case IFF_BROADCAST:
682: if (tp->tun_if.if_flags & IFF_UP) {
1.70 pk 683: error = EBUSY;
684: goto out;
1.26 pk 685: }
686: tp->tun_if.if_flags &=
1.31 matt 687: ~(IFF_BROADCAST|IFF_POINTOPOINT|IFF_MULTICAST);
1.26 pk 688: tp->tun_if.if_flags |= *(int *)data;
689: break;
690: default:
1.70 pk 691: error = EINVAL;
692: goto out;
1.26 pk 693: }
694: break;
695:
696: case TUNSLMODE:
1.87 rpaulo 697: if (*(int *)data) {
1.26 pk 698: tp->tun_flags |= TUN_PREPADDR;
1.87 rpaulo 699: tp->tun_flags &= ~TUN_IFHEAD;
700: } else
1.26 pk 701: tp->tun_flags &= ~TUN_PREPADDR;
702: break;
703:
1.84 rpaulo 704: case TUNSIFHEAD:
1.87 rpaulo 705: if (*(int *)data) {
1.84 rpaulo 706: tp->tun_flags |= TUN_IFHEAD;
1.88 rpaulo 707: tp->tun_flags &= ~TUN_PREPADDR;
1.87 rpaulo 708: } else
1.84 rpaulo 709: tp->tun_flags &= ~TUN_IFHEAD;
710: break;
711:
712: case TUNGIFHEAD:
713: *(int *)data = (tp->tun_flags & TUN_IFHEAD);
714: break;
715:
1.6 deraadt 716: case FIONBIO:
717: if (*(int *)data)
718: tp->tun_flags |= TUN_NBIO;
719: else
720: tp->tun_flags &= ~TUN_NBIO;
721: break;
1.26 pk 722:
1.6 deraadt 723: case FIOASYNC:
724: if (*(int *)data)
725: tp->tun_flags |= TUN_ASYNC;
726: else
727: tp->tun_flags &= ~TUN_ASYNC;
728: break;
1.26 pk 729:
1.6 deraadt 730: case FIONREAD:
731: if (tp->tun_if.if_snd.ifq_head)
1.19 pk 732: *(int *)data = tp->tun_if.if_snd.ifq_head->m_pkthdr.len;
1.70 pk 733: else
1.6 deraadt 734: *(int *)data = 0;
735: break;
1.26 pk 736:
1.6 deraadt 737: case TIOCSPGRP:
1.64 jdolecek 738: case FIOSETOWN:
1.105 ad 739: error = fsetown(&tp->tun_pgid, cmd, data);
1.6 deraadt 740: break;
1.26 pk 741:
1.6 deraadt 742: case TIOCGPGRP:
1.64 jdolecek 743: case FIOGETOWN:
1.105 ad 744: error = fgetown(tp->tun_pgid, cmd, data);
1.6 deraadt 745: break;
1.26 pk 746:
1.6 deraadt 747: default:
1.70 pk 748: error = ENOTTY;
1.6 deraadt 749: }
1.70 pk 750:
751: out:
1.115 rmind 752: mutex_exit(&tp->tun_lock);
1.136 skrll 753:
1.135 skrll 754: return error;
1.1 cgd 755: }
756:
757: /*
1.6 deraadt 758: * The cdevsw read interface - reads a packet at a time, or at
759: * least as much of a packet as can be read.
1.1 cgd 760: */
1.6 deraadt 761: int
1.94 christos 762: tunread(dev_t dev, struct uio *uio, int ioflag)
1.6 deraadt 763: {
1.46 atatat 764: struct tun_softc *tp;
765: struct ifnet *ifp;
1.6 deraadt 766: struct mbuf *m, *m0;
1.136 skrll 767: int error = 0, len;
1.46 atatat 768:
769: tp = tun_find_unit(dev);
770:
771: /* interface was "destroyed" already */
1.70 pk 772: if (tp == NULL) {
1.136 skrll 773: return ENXIO;
1.70 pk 774: }
1.46 atatat 775:
776: ifp = &tp->tun_if;
1.6 deraadt 777:
1.24 thorpej 778: TUNDEBUG ("%s: read\n", ifp->if_xname);
1.8 deraadt 779: if ((tp->tun_flags & TUN_READY) != TUN_READY) {
1.26 pk 780: TUNDEBUG ("%s: not ready 0%o\n", ifp->if_xname, tp->tun_flags);
1.70 pk 781: error = EHOSTDOWN;
782: goto out;
1.8 deraadt 783: }
784:
1.6 deraadt 785: tp->tun_flags &= ~TUN_RWAIT;
786:
787: do {
1.50 itojun 788: IFQ_DEQUEUE(&ifp->if_snd, m0);
1.6 deraadt 789: if (m0 == 0) {
790: if (tp->tun_flags & TUN_NBIO) {
1.70 pk 791: error = EWOULDBLOCK;
792: goto out;
1.6 deraadt 793: }
794: tp->tun_flags |= TUN_RWAIT;
1.136 skrll 795: if (cv_wait_sig(&tp->tun_cv, &tp->tun_lock)) {
1.70 pk 796: error = EINTR;
1.136 skrll 797: goto out;
1.26 pk 798: }
1.6 deraadt 799: }
800: } while (m0 == 0);
1.70 pk 801:
1.115 rmind 802: mutex_exit(&tp->tun_lock);
1.6 deraadt 803:
1.70 pk 804: /* Copy the mbuf chain */
1.6 deraadt 805: while (m0 && uio->uio_resid > 0 && error == 0) {
1.13 deraadt 806: len = min(uio->uio_resid, m0->m_len);
1.45 itojun 807: if (len != 0)
1.97 christos 808: error = uiomove(mtod(m0, void *), len, uio);
1.133 christos 809: m0 = m = m_free(m0);
1.6 deraadt 810: }
811:
812: if (m0) {
813: TUNDEBUG("Dropping mbuf\n");
814: m_freem(m0);
815: }
1.19 pk 816: if (error)
817: ifp->if_ierrors++;
1.70 pk 818:
1.135 skrll 819: return error;
1.70 pk 820:
821: out:
1.115 rmind 822: mutex_exit(&tp->tun_lock);
1.136 skrll 823:
1.135 skrll 824: return error;
1.1 cgd 825: }
826:
827: /*
828: * the cdevsw write interface - an atomic write is a packet - or else!
829: */
1.6 deraadt 830: int
1.94 christos 831: tunwrite(dev_t dev, struct uio *uio, int ioflag)
1.6 deraadt 832: {
1.46 atatat 833: struct tun_softc *tp;
834: struct ifnet *ifp;
1.6 deraadt 835: struct mbuf *top, **mp, *m;
1.118 rmind 836: pktqueue_t *pktq;
1.26 pk 837: struct sockaddr dst;
1.136 skrll 838: int error = 0, tlen, mlen;
1.80 rpaulo 839: uint32_t family;
1.6 deraadt 840:
1.46 atatat 841: tp = tun_find_unit(dev);
1.70 pk 842: if (tp == NULL) {
1.136 skrll 843: /* Interface was "destroyed" already. */
844: return ENXIO;
1.70 pk 845: }
846:
847: /* Unlock until we've got the data */
1.115 rmind 848: mutex_exit(&tp->tun_lock);
1.46 atatat 849:
850: ifp = &tp->tun_if;
851:
1.24 thorpej 852: TUNDEBUG("%s: tunwrite\n", ifp->if_xname);
1.6 deraadt 853:
1.26 pk 854: if (tp->tun_flags & TUN_PREPADDR) {
1.46 atatat 855: if (uio->uio_resid < sizeof(dst)) {
1.70 pk 856: error = EIO;
857: goto out0;
1.46 atatat 858: }
1.97 christos 859: error = uiomove((void *)&dst, sizeof(dst), uio);
1.26 pk 860: if (dst.sa_len > sizeof(dst)) {
861: /* Duh.. */
862: int n = dst.sa_len - sizeof(dst);
1.136 skrll 863: while (n--) {
864: char discard;
865: error = uiomove(&discard, 1, uio);
866: if (error) {
1.70 pk 867: goto out0;
1.46 atatat 868: }
1.136 skrll 869: }
1.26 pk 870: }
1.85 rpaulo 871: } else if (tp->tun_flags & TUN_IFHEAD) {
1.80 rpaulo 872: if (uio->uio_resid < sizeof(family)){
873: error = EIO;
874: goto out0;
875: }
1.97 christos 876: error = uiomove((void *)&family, sizeof(family), uio);
1.80 rpaulo 877: dst.sa_family = ntohl(family);
1.85 rpaulo 878: } else {
879: #ifdef INET
880: dst.sa_family = AF_INET;
881: #endif
1.26 pk 882: }
883:
1.54 simonb 884: if (uio->uio_resid > TUNMTU) {
1.37 mjacob 885: TUNDEBUG("%s: len=%lu!\n", ifp->if_xname,
886: (unsigned long)uio->uio_resid);
1.70 pk 887: error = EIO;
888: goto out0;
1.6 deraadt 889: }
1.26 pk 890:
891: switch (dst.sa_family) {
892: #ifdef INET
893: case AF_INET:
1.118 rmind 894: pktq = ip_pktq;
1.26 pk 895: break;
896: #endif
1.79 rpaulo 897: #ifdef INET6
898: case AF_INET6:
1.118 rmind 899: pktq = ip6_pktq;
1.83 rpaulo 900: break;
1.79 rpaulo 901: #endif
1.26 pk 902: default:
1.70 pk 903: error = EAFNOSUPPORT;
904: goto out0;
1.26 pk 905: }
906:
1.8 deraadt 907: tlen = uio->uio_resid;
908:
909: /* get a header mbuf */
910: MGETHDR(m, M_DONTWAIT, MT_DATA);
1.46 atatat 911: if (m == NULL) {
1.136 skrll 912: return ENOBUFS;
1.46 atatat 913: }
1.8 deraadt 914: mlen = MHLEN;
915:
1.68 tron 916: top = NULL;
1.6 deraadt 917: mp = ⊤
918: while (error == 0 && uio->uio_resid > 0) {
1.13 deraadt 919: m->m_len = min(mlen, uio->uio_resid);
1.97 christos 920: error = uiomove(mtod(m, void *), m->m_len, uio);
1.6 deraadt 921: *mp = m;
922: mp = &m->m_next;
1.68 tron 923: if (error == 0 && uio->uio_resid > 0) {
924: MGET(m, M_DONTWAIT, MT_DATA);
925: if (m == NULL) {
1.8 deraadt 926: error = ENOBUFS;
927: break;
928: }
929: mlen = MLEN;
930: }
1.6 deraadt 931: }
932: if (error) {
1.68 tron 933: if (top != NULL)
1.136 skrll 934: m_freem(top);
1.19 pk 935: ifp->if_ierrors++;
1.70 pk 936: goto out0;
1.6 deraadt 937: }
938:
1.8 deraadt 939: top->m_pkthdr.len = tlen;
1.126 ozaki-r 940: m_set_rcvif(top, ifp);
1.8 deraadt 941:
1.113 joerg 942: bpf_mtap_af(ifp, dst.sa_family, top);
1.6 deraadt 943:
1.115 rmind 944: mutex_enter(&tp->tun_lock);
1.70 pk 945: if ((tp->tun_flags & TUN_INITED) == 0) {
946: /* Interface was destroyed */
947: error = ENXIO;
948: goto out;
949: }
1.119 ws 950: if (__predict_false(!pktq_enqueue(pktq, top, 0))) {
1.6 deraadt 951: ifp->if_collisions++;
1.115 rmind 952: mutex_exit(&tp->tun_lock);
1.70 pk 953: error = ENOBUFS;
1.119 ws 954: m_freem(top);
1.136 skrll 955: goto out0;
1.6 deraadt 956: }
957: ifp->if_ipackets++;
1.58 jdolecek 958: ifp->if_ibytes += tlen;
1.70 pk 959: out:
1.115 rmind 960: mutex_exit(&tp->tun_lock);
1.70 pk 961: out0:
1.135 skrll 962: return error;
1.1 cgd 963: }
964:
1.50 itojun 965: #ifdef ALTQ
966: /*
967: * Start packet transmission on the interface.
968: * when the interface queue is rate-limited by ALTQ or TBR,
969: * if_start is needed to drain packets from the queue in order
970: * to notify readers when outgoing packets become ready.
971: */
972: static void
1.78 thorpej 973: tunstart(struct ifnet *ifp)
1.50 itojun 974: {
975: struct tun_softc *tp = ifp->if_softc;
976:
977: if (!ALTQ_IS_ENABLED(&ifp->if_snd) && !TBR_IS_ENABLED(&ifp->if_snd))
978: return;
979:
1.115 rmind 980: mutex_enter(&tp->tun_lock);
1.70 pk 981: if (!IF_IS_EMPTY(&ifp->if_snd)) {
1.50 itojun 982: if (tp->tun_flags & TUN_RWAIT) {
983: tp->tun_flags &= ~TUN_RWAIT;
1.136 skrll 984: cv_broadcast(&tp->tun_cv);
1.50 itojun 985: }
1.64 jdolecek 986: if (tp->tun_flags & TUN_ASYNC && tp->tun_pgid)
1.106 ad 987: softint_schedule(tp->tun_osih);
1.70 pk 988:
1.104 rmind 989: selnotify(&tp->tun_rsel, 0, 0);
1.50 itojun 990: }
1.115 rmind 991: mutex_exit(&tp->tun_lock);
1.50 itojun 992: }
993: #endif /* ALTQ */
1.1 cgd 994: /*
1.27 mycroft 995: * tunpoll - the poll interface, this is only useful on reads
1.6 deraadt 996: * really. The write detect always returns true, write never blocks
997: * anyway, it either accepts the packet or drops it.
998: */
999: int
1.78 thorpej 1000: tunpoll(dev_t dev, int events, struct lwp *l)
1.6 deraadt 1001: {
1.46 atatat 1002: struct tun_softc *tp;
1003: struct ifnet *ifp;
1.136 skrll 1004: int revents = 0;
1.46 atatat 1005:
1006: tp = tun_find_unit(dev);
1.136 skrll 1007: if (tp == NULL) {
1008: /* Interface was "destroyed" already. */
1009: return 0;
1010: }
1.46 atatat 1011: ifp = &tp->tun_if;
1.6 deraadt 1012:
1.27 mycroft 1013: TUNDEBUG("%s: tunpoll\n", ifp->if_xname);
1.6 deraadt 1014:
1.35 veego 1015: if (events & (POLLIN | POLLRDNORM)) {
1.70 pk 1016: if (!IFQ_IS_EMPTY(&ifp->if_snd)) {
1.27 mycroft 1017: TUNDEBUG("%s: tunpoll q=%d\n", ifp->if_xname,
1.24 thorpej 1018: ifp->if_snd.ifq_len);
1.27 mycroft 1019: revents |= events & (POLLIN | POLLRDNORM);
1020: } else {
1021: TUNDEBUG("%s: tunpoll waiting\n", ifp->if_xname);
1.77 christos 1022: selrecord(l, &tp->tun_rsel);
1.6 deraadt 1023: }
1.35 veego 1024: }
1.27 mycroft 1025:
1026: if (events & (POLLOUT | POLLWRNORM))
1027: revents |= events & (POLLOUT | POLLWRNORM);
1028:
1.115 rmind 1029: mutex_exit(&tp->tun_lock);
1.136 skrll 1030:
1.135 skrll 1031: return revents;
1.56 jdolecek 1032: }
1033:
1034: static void
1035: filt_tunrdetach(struct knote *kn)
1036: {
1037: struct tun_softc *tp = kn->kn_hook;
1038:
1.136 skrll 1039: mutex_enter(&tp->tun_lock);
1.57 christos 1040: SLIST_REMOVE(&tp->tun_rsel.sel_klist, kn, knote, kn_selnext);
1.136 skrll 1041: mutex_exit(&tp->tun_lock);
1.56 jdolecek 1042: }
1043:
1044: static int
1.94 christos 1045: filt_tunread(struct knote *kn, long hint)
1.56 jdolecek 1046: {
1047: struct tun_softc *tp = kn->kn_hook;
1048: struct ifnet *ifp = &tp->tun_if;
1049: struct mbuf *m;
1050:
1.136 skrll 1051: mutex_enter(&tp->tun_lock);
1.56 jdolecek 1052: IF_POLL(&ifp->if_snd, m);
1053: if (m == NULL) {
1.136 skrll 1054: mutex_exit(&tp->tun_lock);
1.135 skrll 1055: return 0;
1.56 jdolecek 1056: }
1057:
1058: for (kn->kn_data = 0; m != NULL; m = m->m_next)
1059: kn->kn_data += m->m_len;
1060:
1.136 skrll 1061: mutex_exit(&tp->tun_lock);
1062:
1.135 skrll 1063: return 1;
1.56 jdolecek 1064: }
1065:
1.140 ! maya 1066: static const struct filterops tunread_filtops = {
! 1067: .f_isfd = 1,
! 1068: .f_attach = NULL,
! 1069: .f_detach = filt_tunrdetach,
! 1070: .f_event = filt_tunread,
! 1071: };
1.56 jdolecek 1072:
1.140 ! maya 1073: static const struct filterops tun_seltrue_filtops = {
! 1074: .f_isfd = 1,
! 1075: .f_attach = NULL,
! 1076: .f_detach = filt_tunrdetach,
! 1077: .f_event = filt_seltrue,
! 1078: };
1.56 jdolecek 1079:
1080: int
1081: tunkqfilter(dev_t dev, struct knote *kn)
1082: {
1.70 pk 1083: struct tun_softc *tp;
1.56 jdolecek 1084: struct klist *klist;
1.136 skrll 1085: int rv = 0;
1.70 pk 1086:
1087: tp = tun_find_unit(dev);
1088: if (tp == NULL)
1089: goto out_nolock;
1.56 jdolecek 1090:
1091: switch (kn->kn_filter) {
1092: case EVFILT_READ:
1.57 christos 1093: klist = &tp->tun_rsel.sel_klist;
1.56 jdolecek 1094: kn->kn_fop = &tunread_filtops;
1095: break;
1096:
1097: case EVFILT_WRITE:
1.57 christos 1098: klist = &tp->tun_rsel.sel_klist;
1.56 jdolecek 1099: kn->kn_fop = &tun_seltrue_filtops;
1100: break;
1101:
1102: default:
1.100 pooka 1103: rv = EINVAL;
1.70 pk 1104: goto out;
1.56 jdolecek 1105: }
1106:
1107: kn->kn_hook = tp;
1108:
1109: SLIST_INSERT_HEAD(klist, kn, kn_selnext);
1.70 pk 1110:
1111: out:
1.115 rmind 1112: mutex_exit(&tp->tun_lock);
1.70 pk 1113: out_nolock:
1.135 skrll 1114: return rv;
1.1 cgd 1115: }
1.128 christos 1116:
1117: /*
1118: * Module infrastructure
1119: */
1120: #include "if_module.h"
1121:
1122: IF_MODULE(MODULE_CLASS_DRIVER, tun, "")
CVSweb <webmaster@jp.NetBSD.org>