FreeBSD/Linux Kernel Cross Reference
sys/net/if_tap.c
1 /*
2 * Copyright (C) 1999-2000 by Maksim Yevmenkin <m_evmenkin@yahoo.com>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * BASED ON:
27 * -------------------------------------------------------------------------
28 *
29 * Copyright (c) 1988, Julian Onions <jpo@cs.nott.ac.uk>
30 * Nottingham University 1987.
31 */
32
33 /*
34 * $FreeBSD: releng/5.2/sys/net/if_tap.c 122352 2003-11-09 09:17:26Z tanimura $
35 * $Id: if_tap.c,v 0.21 2000/07/23 21:46:02 max Exp $
36 */
37
38 #include "opt_inet.h"
39
40 #include <sys/param.h>
41 #include <sys/conf.h>
42 #include <sys/filedesc.h>
43 #include <sys/filio.h>
44 #include <sys/kernel.h>
45 #include <sys/malloc.h>
46 #include <sys/mbuf.h>
47 #include <sys/poll.h>
48 #include <sys/proc.h>
49 #include <sys/signalvar.h>
50 #include <sys/socket.h>
51 #include <sys/sockio.h>
52 #include <sys/sysctl.h>
53 #include <sys/systm.h>
54 #include <sys/ttycom.h>
55 #include <sys/uio.h>
56 #include <sys/vnode.h>
57 #include <machine/bus.h> /* XXX: Shouldn't really be required! */
58 #include <sys/rman.h>
59 #include <sys/queue.h>
60
61 #include <net/bpf.h>
62 #include <net/ethernet.h>
63 #include <net/if.h>
64 #include <net/if_arp.h>
65 #include <net/route.h>
66
67 #include <netinet/in.h>
68
69 #include <net/if_tapvar.h>
70 #include <net/if_tap.h>
71
72
73 #define CDEV_NAME "tap"
74 #define CDEV_MAJOR 149
75 #define TAPDEBUG if (tapdebug) printf
76
77 #define TAP "tap"
78 #define VMNET "vmnet"
79 #define TAPMAXUNIT 0x7fff
80 #define VMNET_DEV_MASK 0x00800000
81 /* 0x007f00ff */
82
83 /* module */
84 static int tapmodevent(module_t, int, void *);
85
86 /* device */
87 static void tapclone(void *, char *, int, dev_t *);
88 static void tapcreate(dev_t);
89
90 /* network interface */
91 static void tapifstart(struct ifnet *);
92 static int tapifioctl(struct ifnet *, u_long, caddr_t);
93 static void tapifinit(void *);
94
95 /* character device */
96 static d_open_t tapopen;
97 static d_close_t tapclose;
98 static d_read_t tapread;
99 static d_write_t tapwrite;
100 static d_ioctl_t tapioctl;
101 static d_poll_t tappoll;
102
103 static struct cdevsw tap_cdevsw = {
104 .d_open = tapopen,
105 .d_close = tapclose,
106 .d_read = tapread,
107 .d_write = tapwrite,
108 .d_ioctl = tapioctl,
109 .d_poll = tappoll,
110 .d_name = CDEV_NAME,
111 .d_maj = CDEV_MAJOR,
112 };
113
114 static int tapdebug = 0; /* debug flag */
115 static SLIST_HEAD(, tap_softc) taphead; /* first device */
116 static udev_t tapbasedev = NOUDEV; /* base device */
117 static struct rman tapdevunits[2]; /* device units */
118 #define tapunits tapdevunits
119 #define vmnetunits (tapdevunits + 1)
120
121 MALLOC_DECLARE(M_TAP);
122 MALLOC_DEFINE(M_TAP, CDEV_NAME, "Ethernet tunnel interface");
123 SYSCTL_INT(_debug, OID_AUTO, if_tap_debug, CTLFLAG_RW, &tapdebug, 0, "");
124 DEV_MODULE(if_tap, tapmodevent, NULL);
125
126 /*
127 * tapmodevent
128 *
129 * module event handler
130 */
131 static int
132 tapmodevent(mod, type, data)
133 module_t mod;
134 int type;
135 void *data;
136 {
137 static eventhandler_tag eh_tag = NULL;
138 struct tap_softc *tp = NULL;
139 struct ifnet *ifp = NULL;
140 int error, s;
141
142 switch (type) {
143 case MOD_LOAD:
144 /* initialize resources */
145 tapunits->rm_type = RMAN_ARRAY;
146 tapunits->rm_descr = "open tap units";
147 vmnetunits->rm_type = RMAN_ARRAY;
148 vmnetunits->rm_descr = "open vmnet units";
149
150 error = rman_init(tapunits);
151 if (error != 0)
152 goto bail;
153
154 error = rman_init(vmnetunits);
155 if (error != 0)
156 goto bail1;
157
158 error = rman_manage_region(tapunits, 0, TAPMAXUNIT);
159 if (error != 0)
160 goto bail2;
161
162 error = rman_manage_region(vmnetunits, 0, TAPMAXUNIT);
163 if (error != 0)
164 goto bail2;
165
166 /* intitialize device */
167
168 SLIST_INIT(&taphead);
169
170 eh_tag = EVENTHANDLER_REGISTER(dev_clone, tapclone, 0, 1000);
171 if (eh_tag == NULL) {
172 error = ENOMEM;
173 goto bail2;
174 }
175
176
177 return (0);
178 bail2:
179 rman_fini(vmnetunits);
180 bail1:
181 rman_fini(tapunits);
182 bail:
183 return (error);
184
185 case MOD_UNLOAD:
186 SLIST_FOREACH(tp, &taphead, tap_next)
187 if (tp->tap_unit != NULL)
188 return (EBUSY);
189
190 EVENTHANDLER_DEREGISTER(dev_clone, eh_tag);
191
192 error = rman_fini(tapunits);
193 KASSERT((error == 0), ("Could not fini tap units"));
194 error = rman_fini(vmnetunits);
195 KASSERT((error == 0), ("Could not fini vmnet units"));
196
197 while ((tp = SLIST_FIRST(&taphead)) != NULL) {
198 SLIST_REMOVE_HEAD(&taphead, tap_next);
199
200 ifp = &tp->tap_if;
201
202 TAPDEBUG("detaching %s\n", ifp->if_xname);
203
204 KASSERT(!(tp->tap_flags & TAP_OPEN),
205 ("%s flags is out of sync", ifp->if_xname));
206
207 /* XXX makedev check? nah.. not right now :) */
208
209 s = splimp();
210 ether_ifdetach(ifp);
211 splx(s);
212
213 free(tp, M_TAP);
214 }
215
216 if (tapbasedev != NOUDEV)
217 destroy_dev(udev2dev(tapbasedev, 0));
218
219
220 break;
221
222 default:
223 return (EOPNOTSUPP);
224 }
225
226 return (0);
227 } /* tapmodevent */
228
229
230 /*
231 * DEVFS handler
232 *
233 * We need to support two kind of devices - tap and vmnet
234 */
235 static void
236 tapclone(arg, name, namelen, dev)
237 void *arg;
238 char *name;
239 int namelen;
240 dev_t *dev;
241 {
242 int unit, minor = 0 /* XXX avoid warning */ , error;
243 char *device_name = name;
244 struct resource *r = NULL;
245
246 if (*dev != NODEV)
247 return;
248
249 if (strcmp(device_name, TAP) == 0) {
250 /* get first free tap unit */
251 r = rman_reserve_resource(tapunits, 0, TAPMAXUNIT, 1,
252 RF_ALLOCATED | RF_ACTIVE, NULL);
253 unit = rman_get_start(r);
254 minor = unit2minor(unit);
255 }
256 else if (strcmp(device_name, VMNET) == 0) {
257 /* get first free vmnet unit */
258 r = rman_reserve_resource(vmnetunits, 0, TAPMAXUNIT, 1,
259 RF_ALLOCATED | RF_ACTIVE, NULL);
260 unit = rman_get_start(r);
261 minor = unit2minor(unit) | VMNET_DEV_MASK;
262 }
263
264 if (r != NULL) { /* need cloning */
265 TAPDEBUG("%s%d is available. minor = %#x\n",
266 device_name, unit, minor);
267
268 error = rman_release_resource(r);
269 KASSERT((error == 0), ("Could not release tap/vmnet unit"));
270
271 /* check if device for the unit has been created */
272 *dev = makedev(CDEV_MAJOR, minor);
273 if ((*dev)->si_flags & SI_NAMED) {
274 TAPDEBUG("%s%d device exists. minor = %#x\n",
275 device_name, unit, minor);
276 return; /* device has been created */
277 }
278 } else { /* try to match name/unit, first try tap then vmnet */
279 device_name = TAP;
280 if (dev_stdclone(name, NULL, device_name, &unit) != 1) {
281 device_name = VMNET;
282
283 if (dev_stdclone(name, NULL, device_name, &unit) != 1)
284 return;
285
286 minor = unit2minor(unit) | VMNET_DEV_MASK;
287 } else
288 minor = unit2minor(unit);
289 }
290
291 TAPDEBUG("make_dev(%s%d). minor = %#x\n", device_name, unit, minor);
292
293 *dev = make_dev(&tap_cdevsw, minor, UID_ROOT, GID_WHEEL, 0600, "%s%d",
294 device_name, unit);
295
296 if (tapbasedev == NOUDEV)
297 tapbasedev = (*dev)->si_udev;
298 else {
299 (*dev)->si_flags |= SI_CHEAPCLONE;
300 dev_depends(udev2dev(tapbasedev, 0), *dev);
301 }
302 } /* tapclone */
303
304
305 /*
306 * tapcreate
307 *
308 * to create interface
309 */
310 static void
311 tapcreate(dev)
312 dev_t dev;
313 {
314 struct ifnet *ifp = NULL;
315 struct tap_softc *tp = NULL;
316 unsigned short macaddr_hi;
317 int unit, s;
318 char *name = NULL;
319
320 /* allocate driver storage and create device */
321 MALLOC(tp, struct tap_softc *, sizeof(*tp), M_TAP, M_WAITOK | M_ZERO);
322 SLIST_INSERT_HEAD(&taphead, tp, tap_next);
323
324 unit = dev2unit(dev) & TAPMAXUNIT;
325
326 /* select device: tap or vmnet */
327 if (minor(dev) & VMNET_DEV_MASK) {
328 name = VMNET;
329 tp->tap_flags |= TAP_VMNET;
330 } else
331 name = TAP;
332
333 TAPDEBUG("tapcreate(%s%d). minor = %#x\n", name, unit, minor(dev));
334
335 if (!(dev->si_flags & SI_NAMED))
336 dev = make_dev(&tap_cdevsw, minor(dev), UID_ROOT, GID_WHEEL,
337 0600, "%s%d", name, unit);
338
339 /* generate fake MAC address: 00 bd xx xx xx unit_no */
340 macaddr_hi = htons(0x00bd);
341 bcopy(&macaddr_hi, &tp->arpcom.ac_enaddr[0], sizeof(short));
342 bcopy(&ticks, &tp->arpcom.ac_enaddr[2], sizeof(long));
343 tp->arpcom.ac_enaddr[5] = (u_char)unit;
344
345 /* fill the rest and attach interface */
346 ifp = &tp->tap_if;
347 ifp->if_softc = tp;
348 if_initname(ifp, name, unit);
349 ifp->if_init = tapifinit;
350 ifp->if_start = tapifstart;
351 ifp->if_ioctl = tapifioctl;
352 ifp->if_mtu = ETHERMTU;
353 ifp->if_flags = (IFF_BROADCAST|IFF_SIMPLEX|IFF_MULTICAST);
354 ifp->if_snd.ifq_maxlen = ifqmaxlen;
355
356 dev->si_drv1 = tp;
357
358 s = splimp();
359 ether_ifattach(ifp, tp->arpcom.ac_enaddr);
360 splx(s);
361
362 tp->tap_flags |= TAP_INITED;
363
364 TAPDEBUG("interface %s is created. minor = %#x\n",
365 ifp->if_xname, minor(dev));
366 } /* tapcreate */
367
368
369 /*
370 * tapopen
371 *
372 * to open tunnel. must be superuser
373 */
374 static int
375 tapopen(dev, flag, mode, td)
376 dev_t dev;
377 int flag;
378 int mode;
379 struct thread *td;
380 {
381 struct tap_softc *tp = NULL;
382 int unit, error;
383 struct resource *r = NULL;
384
385 if ((error = suser(td)) != 0)
386 return (error);
387
388 unit = dev2unit(dev) & TAPMAXUNIT;
389
390 if (minor(dev) & VMNET_DEV_MASK)
391 r = rman_reserve_resource(vmnetunits, unit, unit, 1,
392 RF_ALLOCATED | RF_ACTIVE, NULL);
393 else
394 r = rman_reserve_resource(tapunits, unit, unit, 1,
395 RF_ALLOCATED | RF_ACTIVE, NULL);
396
397 if (r == NULL)
398 return (EBUSY);
399
400 dev->si_flags &= ~SI_CHEAPCLONE;
401
402 tp = dev->si_drv1;
403 if (tp == NULL) {
404 tapcreate(dev);
405 tp = dev->si_drv1;
406 }
407
408 KASSERT(!(tp->tap_flags & TAP_OPEN),
409 ("%s flags is out of sync", tp->tap_if.if_xname));
410
411 bcopy(tp->arpcom.ac_enaddr, tp->ether_addr, sizeof(tp->ether_addr));
412
413 tp->tap_unit = r;
414 tp->tap_pid = td->td_proc->p_pid;
415 tp->tap_flags |= TAP_OPEN;
416
417 TAPDEBUG("%s is open. minor = %#x\n",
418 tp->tap_if.if_xname, minor(dev));
419
420 return (0);
421 } /* tapopen */
422
423
424 /*
425 * tapclose
426 *
427 * close the device - mark i/f down & delete routing info
428 */
429 static int
430 tapclose(dev, foo, bar, td)
431 dev_t dev;
432 int foo;
433 int bar;
434 struct thread *td;
435 {
436 int s, error;
437 struct tap_softc *tp = dev->si_drv1;
438 struct ifnet *ifp = &tp->tap_if;
439
440 KASSERT((tp->tap_unit != NULL),
441 ("%s is not open", ifp->if_xname));
442
443 /* junk all pending output */
444 IF_DRAIN(&ifp->if_snd);
445
446 /*
447 * do not bring the interface down, and do not anything with
448 * interface, if we are in VMnet mode. just close the device.
449 */
450
451 if (((tp->tap_flags & TAP_VMNET) == 0) && (ifp->if_flags & IFF_UP)) {
452 s = splimp();
453 if_down(ifp);
454 if (ifp->if_flags & IFF_RUNNING) {
455 /* find internet addresses and delete routes */
456 struct ifaddr *ifa = NULL;
457
458 TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
459 if (ifa->ifa_addr->sa_family == AF_INET) {
460 rtinit(ifa, (int)RTM_DELETE, 0);
461
462 /* remove address from interface */
463 bzero(ifa->ifa_addr,
464 sizeof(*(ifa->ifa_addr)));
465 bzero(ifa->ifa_dstaddr,
466 sizeof(*(ifa->ifa_dstaddr)));
467 bzero(ifa->ifa_netmask,
468 sizeof(*(ifa->ifa_netmask)));
469 }
470 }
471
472 ifp->if_flags &= ~IFF_RUNNING;
473 }
474 splx(s);
475 }
476
477 funsetown(&tp->tap_sigio);
478 selwakeuppri(&tp->tap_rsel, PZERO+1);
479
480 tp->tap_flags &= ~TAP_OPEN;
481 tp->tap_pid = 0;
482 error = rman_release_resource(tp->tap_unit);
483 KASSERT((error == 0),
484 ("%s could not release unit", ifp->if_xname));
485 tp->tap_unit = NULL;
486
487 TAPDEBUG("%s is closed. minor = %#x\n",
488 ifp->if_xname, minor(dev));
489
490 return (0);
491 } /* tapclose */
492
493
494 /*
495 * tapifinit
496 *
497 * network interface initialization function
498 */
499 static void
500 tapifinit(xtp)
501 void *xtp;
502 {
503 struct tap_softc *tp = (struct tap_softc *)xtp;
504 struct ifnet *ifp = &tp->tap_if;
505
506 TAPDEBUG("initializing %s\n", ifp->if_xname);
507
508 ifp->if_flags |= IFF_RUNNING;
509 ifp->if_flags &= ~IFF_OACTIVE;
510
511 /* attempt to start output */
512 tapifstart(ifp);
513 } /* tapifinit */
514
515
516 /*
517 * tapifioctl
518 *
519 * Process an ioctl request on network interface
520 */
521 static int
522 tapifioctl(ifp, cmd, data)
523 struct ifnet *ifp;
524 u_long cmd;
525 caddr_t data;
526 {
527 struct tap_softc *tp = (struct tap_softc *)(ifp->if_softc);
528 struct ifstat *ifs = NULL;
529 int s, dummy;
530
531 switch (cmd) {
532 case SIOCSIFFLAGS: /* XXX -- just like vmnet does */
533 case SIOCADDMULTI:
534 case SIOCDELMULTI:
535 break;
536
537 case SIOCGIFSTATUS:
538 s = splimp();
539 ifs = (struct ifstat *)data;
540 dummy = strlen(ifs->ascii);
541 if (tp->tap_pid != 0 && dummy < sizeof(ifs->ascii))
542 snprintf(ifs->ascii + dummy,
543 sizeof(ifs->ascii) - dummy,
544 "\tOpened by PID %d\n", tp->tap_pid);
545 splx(s);
546 break;
547
548 default:
549 s = splimp();
550 dummy = ether_ioctl(ifp, cmd, data);
551 splx(s);
552 return (dummy);
553 }
554
555 return (0);
556 } /* tapifioctl */
557
558
559 /*
560 * tapifstart
561 *
562 * queue packets from higher level ready to put out
563 */
564 static void
565 tapifstart(ifp)
566 struct ifnet *ifp;
567 {
568 struct tap_softc *tp = ifp->if_softc;
569 int s;
570
571 TAPDEBUG("%s starting\n", ifp->if_xname);
572
573 /*
574 * do not junk pending output if we are in VMnet mode.
575 * XXX: can this do any harm because of queue overflow?
576 */
577
578 if (((tp->tap_flags & TAP_VMNET) == 0) &&
579 ((tp->tap_flags & TAP_READY) != TAP_READY)) {
580 struct mbuf *m = NULL;
581
582 TAPDEBUG("%s not ready, tap_flags = 0x%x\n", ifp->if_xname,
583 tp->tap_flags);
584
585 s = splimp();
586 do {
587 IF_DEQUEUE(&ifp->if_snd, m);
588 if (m != NULL)
589 m_freem(m);
590 ifp->if_oerrors ++;
591 } while (m != NULL);
592 splx(s);
593
594 return;
595 }
596
597 s = splimp();
598 ifp->if_flags |= IFF_OACTIVE;
599
600 if (ifp->if_snd.ifq_len != 0) {
601 if (tp->tap_flags & TAP_RWAIT) {
602 tp->tap_flags &= ~TAP_RWAIT;
603 wakeup(tp);
604 }
605
606 if ((tp->tap_flags & TAP_ASYNC) && (tp->tap_sigio != NULL))
607 pgsigio(&tp->tap_sigio, SIGIO, 0);
608
609 selwakeuppri(&tp->tap_rsel, PZERO+1);
610 ifp->if_opackets ++; /* obytes are counted in ether_output */
611 }
612
613 ifp->if_flags &= ~IFF_OACTIVE;
614 splx(s);
615 } /* tapifstart */
616
617
618 /*
619 * tapioctl
620 *
621 * the cdevsw interface is now pretty minimal
622 */
623 static int
624 tapioctl(dev, cmd, data, flag, td)
625 dev_t dev;
626 u_long cmd;
627 caddr_t data;
628 int flag;
629 struct thread *td;
630 {
631 struct tap_softc *tp = dev->si_drv1;
632 struct ifnet *ifp = &tp->tap_if;
633 struct tapinfo *tapp = NULL;
634 int s;
635 int f;
636
637 switch (cmd) {
638 case TAPSIFINFO:
639 s = splimp();
640 tapp = (struct tapinfo *)data;
641 ifp->if_mtu = tapp->mtu;
642 ifp->if_type = tapp->type;
643 ifp->if_baudrate = tapp->baudrate;
644 splx(s);
645 break;
646
647 case TAPGIFINFO:
648 tapp = (struct tapinfo *)data;
649 tapp->mtu = ifp->if_mtu;
650 tapp->type = ifp->if_type;
651 tapp->baudrate = ifp->if_baudrate;
652 break;
653
654 case TAPSDEBUG:
655 tapdebug = *(int *)data;
656 break;
657
658 case TAPGDEBUG:
659 *(int *)data = tapdebug;
660 break;
661
662 case FIONBIO:
663 break;
664
665 case FIOASYNC:
666 s = splimp();
667 if (*(int *)data)
668 tp->tap_flags |= TAP_ASYNC;
669 else
670 tp->tap_flags &= ~TAP_ASYNC;
671 splx(s);
672 break;
673
674 case FIONREAD:
675 s = splimp();
676 if (ifp->if_snd.ifq_head) {
677 struct mbuf *mb = ifp->if_snd.ifq_head;
678
679 for(*(int *)data = 0;mb != NULL;mb = mb->m_next)
680 *(int *)data += mb->m_len;
681 } else
682 *(int *)data = 0;
683 splx(s);
684 break;
685
686 case FIOSETOWN:
687 return (fsetown(*(int *)data, &tp->tap_sigio));
688
689 case FIOGETOWN:
690 *(int *)data = fgetown(&tp->tap_sigio);
691 return (0);
692
693 /* this is deprecated, FIOSETOWN should be used instead */
694 case TIOCSPGRP:
695 return (fsetown(-(*(int *)data), &tp->tap_sigio));
696
697 /* this is deprecated, FIOGETOWN should be used instead */
698 case TIOCGPGRP:
699 *(int *)data = -fgetown(&tp->tap_sigio);
700 return (0);
701
702 /* VMware/VMnet port ioctl's */
703
704 case SIOCGIFFLAGS: /* get ifnet flags */
705 bcopy(&ifp->if_flags, data, sizeof(ifp->if_flags));
706 break;
707
708 case VMIO_SIOCSIFFLAGS: /* VMware/VMnet SIOCSIFFLAGS */
709 f = *(int *)data;
710 f &= 0x0fff;
711 f &= ~IFF_CANTCHANGE;
712 f |= IFF_UP;
713
714 s = splimp();
715 ifp->if_flags = f | (ifp->if_flags & IFF_CANTCHANGE);
716 splx(s);
717 break;
718
719 case OSIOCGIFADDR: /* get MAC address of the remote side */
720 case SIOCGIFADDR:
721 bcopy(tp->ether_addr, data, sizeof(tp->ether_addr));
722 break;
723
724 case SIOCSIFADDR: /* set MAC address of the remote side */
725 bcopy(data, tp->ether_addr, sizeof(tp->ether_addr));
726 break;
727
728 default:
729 return (ENOTTY);
730 }
731 return (0);
732 } /* tapioctl */
733
734
735 /*
736 * tapread
737 *
738 * the cdevsw read interface - reads a packet at a time, or at
739 * least as much of a packet as can be read
740 */
741 static int
742 tapread(dev, uio, flag)
743 dev_t dev;
744 struct uio *uio;
745 int flag;
746 {
747 struct tap_softc *tp = dev->si_drv1;
748 struct ifnet *ifp = &tp->tap_if;
749 struct mbuf *m = NULL;
750 int error = 0, len, s;
751
752 TAPDEBUG("%s reading, minor = %#x\n", ifp->if_xname, minor(dev));
753
754 if ((tp->tap_flags & TAP_READY) != TAP_READY) {
755 TAPDEBUG("%s not ready. minor = %#x, tap_flags = 0x%x\n",
756 ifp->if_xname, minor(dev), tp->tap_flags);
757
758 return (EHOSTDOWN);
759 }
760
761 tp->tap_flags &= ~TAP_RWAIT;
762
763 /* sleep until we get a packet */
764 do {
765 s = splimp();
766 IF_DEQUEUE(&ifp->if_snd, m);
767 splx(s);
768
769 if (m == NULL) {
770 if (flag & IO_NDELAY)
771 return (EWOULDBLOCK);
772
773 tp->tap_flags |= TAP_RWAIT;
774 error = tsleep(tp,PCATCH|(PZERO+1),"taprd",0);
775 if (error)
776 return (error);
777 }
778 } while (m == NULL);
779
780 /* feed packet to bpf */
781 BPF_MTAP(ifp, m);
782
783 /* xfer packet to user space */
784 while ((m != NULL) && (uio->uio_resid > 0) && (error == 0)) {
785 len = min(uio->uio_resid, m->m_len);
786 if (len == 0)
787 break;
788
789 error = uiomove(mtod(m, void *), len, uio);
790 m = m_free(m);
791 }
792
793 if (m != NULL) {
794 TAPDEBUG("%s dropping mbuf, minor = %#x\n", ifp->if_xname,
795 minor(dev));
796 m_freem(m);
797 }
798
799 return (error);
800 } /* tapread */
801
802
803 /*
804 * tapwrite
805 *
806 * the cdevsw write interface - an atomic write is a packet - or else!
807 */
808 static int
809 tapwrite(dev, uio, flag)
810 dev_t dev;
811 struct uio *uio;
812 int flag;
813 {
814 struct tap_softc *tp = dev->si_drv1;
815 struct ifnet *ifp = &tp->tap_if;
816 struct mbuf *top = NULL, **mp = NULL, *m = NULL;
817 int error = 0, tlen, mlen;
818
819 TAPDEBUG("%s writting, minor = %#x\n",
820 ifp->if_xname, minor(dev));
821
822 if (uio->uio_resid == 0)
823 return (0);
824
825 if ((uio->uio_resid < 0) || (uio->uio_resid > TAPMRU)) {
826 TAPDEBUG("%s invalid packet len = %d, minor = %#x\n",
827 ifp->if_xname, uio->uio_resid, minor(dev));
828
829 return (EIO);
830 }
831 tlen = uio->uio_resid;
832
833 /* get a header mbuf */
834 MGETHDR(m, M_DONTWAIT, MT_DATA);
835 if (m == NULL)
836 return (ENOBUFS);
837 mlen = MHLEN;
838
839 top = 0;
840 mp = ⊤
841 while ((error == 0) && (uio->uio_resid > 0)) {
842 m->m_len = min(mlen, uio->uio_resid);
843 error = uiomove(mtod(m, void *), m->m_len, uio);
844 *mp = m;
845 mp = &m->m_next;
846 if (uio->uio_resid > 0) {
847 MGET(m, M_DONTWAIT, MT_DATA);
848 if (m == NULL) {
849 error = ENOBUFS;
850 break;
851 }
852 mlen = MLEN;
853 }
854 }
855 if (error) {
856 ifp->if_ierrors ++;
857 if (top)
858 m_freem(top);
859 return (error);
860 }
861
862 top->m_pkthdr.len = tlen;
863 top->m_pkthdr.rcvif = ifp;
864
865 /* Pass packet up to parent. */
866 (*ifp->if_input)(ifp, top);
867 ifp->if_ipackets ++; /* ibytes are counted in parent */
868
869 return (0);
870 } /* tapwrite */
871
872
873 /*
874 * tappoll
875 *
876 * the poll interface, this is only useful on reads
877 * really. the write detect always returns true, write never blocks
878 * anyway, it either accepts the packet or drops it
879 */
880 static int
881 tappoll(dev, events, td)
882 dev_t dev;
883 int events;
884 struct thread *td;
885 {
886 struct tap_softc *tp = dev->si_drv1;
887 struct ifnet *ifp = &tp->tap_if;
888 int s, revents = 0;
889
890 TAPDEBUG("%s polling, minor = %#x\n",
891 ifp->if_xname, minor(dev));
892
893 s = splimp();
894 if (events & (POLLIN | POLLRDNORM)) {
895 if (ifp->if_snd.ifq_len > 0) {
896 TAPDEBUG("%s have data in queue. len = %d, " \
897 "minor = %#x\n", ifp->if_xname,
898 ifp->if_snd.ifq_len, minor(dev));
899
900 revents |= (events & (POLLIN | POLLRDNORM));
901 } else {
902 TAPDEBUG("%s waiting for data, minor = %#x\n",
903 ifp->if_xname, minor(dev));
904
905 selrecord(td, &tp->tap_rsel);
906 }
907 }
908
909 if (events & (POLLOUT | POLLWRNORM))
910 revents |= (events & (POLLOUT | POLLWRNORM));
911
912 splx(s);
913 return (revents);
914 } /* tappoll */
Cache object: 4800cc90ce2ce1f9dca24369749bc7a4
|