if_tap.c revision 126845
1276305Sngie/* 2236769Sobrien * Copyright (C) 1999-2000 by Maksim Yevmenkin <m_evmenkin@yahoo.com> 3236769Sobrien * All rights reserved. 4236769Sobrien * 5236769Sobrien * Redistribution and use in source and binary forms, with or without 6236769Sobrien * modification, are permitted provided that the following conditions 7236769Sobrien * are met: 8236769Sobrien * 1. Redistributions of source code must retain the above copyright 9236769Sobrien * notice, this list of conditions and the following disclaimer. 10236769Sobrien * 2. Redistributions in binary form must reproduce the above copyright 11236769Sobrien * notice, this list of conditions and the following disclaimer in the 12236769Sobrien * documentation and/or other materials provided with the distribution. 13236769Sobrien * 14236769Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15236769Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16236769Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17236769Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18236769Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19236769Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20236769Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21236769Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22236769Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23236769Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24236769Sobrien * SUCH DAMAGE. 25236769Sobrien * 26236769Sobrien * BASED ON: 27236769Sobrien * ------------------------------------------------------------------------- 28236769Sobrien * 29236769Sobrien * Copyright (c) 1988, Julian Onions <jpo@cs.nott.ac.uk> 30236769Sobrien * Nottingham University 1987. 31236769Sobrien */ 32236769Sobrien 33236769Sobrien/* 34236769Sobrien * $FreeBSD: head/sys/net/if_tap.c 126845 2004-03-11 12:58:55Z phk $ 35236769Sobrien * $Id: if_tap.c,v 0.21 2000/07/23 21:46:02 max Exp $ 36236769Sobrien */ 37236769Sobrien 38236769Sobrien#include "opt_inet.h" 39236769Sobrien 40236769Sobrien#include <sys/param.h> 41236769Sobrien#include <sys/conf.h> 42236769Sobrien#include <sys/filedesc.h> 43236769Sobrien#include <sys/filio.h> 44236769Sobrien#include <sys/kernel.h> 45236769Sobrien#include <sys/malloc.h> 46236769Sobrien#include <sys/mbuf.h> 47236769Sobrien#include <sys/poll.h> 48236769Sobrien#include <sys/proc.h> 49236769Sobrien#include <sys/signalvar.h> 50236769Sobrien#include <sys/socket.h> 51236769Sobrien#include <sys/sockio.h> 52236769Sobrien#include <sys/sysctl.h> 53236769Sobrien#include <sys/systm.h> 54236769Sobrien#include <sys/ttycom.h> 55236769Sobrien#include <sys/uio.h> 56236769Sobrien#include <sys/vnode.h> 57236769Sobrien#include <sys/queue.h> 58236769Sobrien 59236769Sobrien#include <net/bpf.h> 60236769Sobrien#include <net/ethernet.h> 61236769Sobrien#include <net/if.h> 62236769Sobrien#include <net/if_arp.h> 63236769Sobrien#include <net/route.h> 64236769Sobrien 65236769Sobrien#include <netinet/in.h> 66236769Sobrien 67236769Sobrien#include <net/if_tapvar.h> 68236769Sobrien#include <net/if_tap.h> 69236769Sobrien 70236769Sobrien 71236769Sobrien#define CDEV_NAME "tap" 72236769Sobrien#define TAPDEBUG if (tapdebug) printf 73276305Sngie 74236769Sobrien#define TAP "tap" 75236769Sobrien#define VMNET "vmnet" 76236769Sobrien#define TAPMAXUNIT 0x7fff 77236769Sobrien#define VMNET_DEV_MASK CLONE_FLAG0 78236769Sobrien 79236769Sobrien/* module */ 80276305Sngiestatic int tapmodevent(module_t, int, void *); 81236769Sobrien 82236769Sobrien/* device */ 83236769Sobrienstatic void tapclone(void *, char *, int, dev_t *); 84236769Sobrienstatic void tapcreate(dev_t); 85236769Sobrien 86236769Sobrien/* network interface */ 87236769Sobrienstatic void tapifstart(struct ifnet *); 88236769Sobrienstatic int tapifioctl(struct ifnet *, u_long, caddr_t); 89236769Sobrienstatic void tapifinit(void *); 90236769Sobrien 91236769Sobrien/* character device */ 92236769Sobrienstatic d_open_t tapopen; 93236769Sobrienstatic d_close_t tapclose; 94236769Sobrienstatic d_read_t tapread; 95236769Sobrienstatic d_write_t tapwrite; 96236769Sobrienstatic d_ioctl_t tapioctl; 97236769Sobrienstatic d_poll_t tappoll; 98236769Sobrien 99236769Sobrienstatic struct cdevsw tap_cdevsw = { 100236769Sobrien .d_version = D_VERSION, 101236769Sobrien .d_flags = D_PSEUDO | D_NEEDGIANT, 102236769Sobrien .d_open = tapopen, 103236769Sobrien .d_close = tapclose, 104236769Sobrien .d_read = tapread, 105236769Sobrien .d_write = tapwrite, 106236769Sobrien .d_ioctl = tapioctl, 107236769Sobrien .d_poll = tappoll, 108236769Sobrien .d_name = CDEV_NAME, 109236769Sobrien}; 110236769Sobrien 111236769Sobrienstatic int tapdebug = 0; /* debug flag */ 112236769Sobrienstatic SLIST_HEAD(, tap_softc) taphead; /* first device */ 113236769Sobrienstatic struct clonedevs *tapclones; 114236769Sobrien 115236769SobrienMALLOC_DECLARE(M_TAP); 116236769SobrienMALLOC_DEFINE(M_TAP, CDEV_NAME, "Ethernet tunnel interface"); 117236769SobrienSYSCTL_INT(_debug, OID_AUTO, if_tap_debug, CTLFLAG_RW, &tapdebug, 0, ""); 118236769SobrienDEV_MODULE(if_tap, tapmodevent, NULL); 119236769Sobrien 120236769Sobrien/* 121236769Sobrien * tapmodevent 122236769Sobrien * 123236769Sobrien * module event handler 124236769Sobrien */ 125236769Sobrienstatic int 126236769Sobrientapmodevent(mod, type, data) 127236769Sobrien module_t mod; 128236769Sobrien int type; 129236769Sobrien void *data; 130236769Sobrien{ 131236769Sobrien static eventhandler_tag eh_tag = NULL; 132236769Sobrien struct tap_softc *tp = NULL; 133236769Sobrien struct ifnet *ifp = NULL; 134236769Sobrien int s; 135236769Sobrien 136236769Sobrien switch (type) { 137236769Sobrien case MOD_LOAD: 138236769Sobrien 139236769Sobrien /* intitialize device */ 140236769Sobrien 141236769Sobrien SLIST_INIT(&taphead); 142236769Sobrien 143236769Sobrien clone_setup(&tapclones); 144236769Sobrien eh_tag = EVENTHANDLER_REGISTER(dev_clone, tapclone, 0, 1000); 145236769Sobrien if (eh_tag == NULL) 146236769Sobrien return (ENOMEM); 147236769Sobrien return (0); 148236769Sobrien 149236769Sobrien case MOD_UNLOAD: 150236769Sobrien SLIST_FOREACH(tp, &taphead, tap_next) 151236769Sobrien if (tp->tap_flags & TAP_OPEN) 152236769Sobrien return (EBUSY); 153236769Sobrien 154236769Sobrien EVENTHANDLER_DEREGISTER(dev_clone, eh_tag); 155236769Sobrien 156236769Sobrien while ((tp = SLIST_FIRST(&taphead)) != NULL) { 157236769Sobrien SLIST_REMOVE_HEAD(&taphead, tap_next); 158236769Sobrien 159236769Sobrien ifp = &tp->tap_if; 160236769Sobrien 161236769Sobrien TAPDEBUG("detaching %s\n", ifp->if_xname); 162236769Sobrien 163236769Sobrien KASSERT(!(tp->tap_flags & TAP_OPEN), 164236769Sobrien ("%s flags is out of sync", ifp->if_xname)); 165236769Sobrien 166236769Sobrien destroy_dev(tp->tap_dev); 167236769Sobrien s = splimp(); 168236769Sobrien ether_ifdetach(ifp); 169236769Sobrien splx(s); 170236769Sobrien 171236769Sobrien free(tp, M_TAP); 172236769Sobrien } 173236769Sobrien clone_cleanup(&tapclones); 174236769Sobrien 175236769Sobrien break; 176236769Sobrien 177236769Sobrien default: 178236769Sobrien return (EOPNOTSUPP); 179236769Sobrien } 180236769Sobrien 181236769Sobrien return (0); 182236769Sobrien} /* tapmodevent */ 183236769Sobrien 184236769Sobrien 185236769Sobrien/* 186236769Sobrien * DEVFS handler 187236769Sobrien * 188236769Sobrien * We need to support two kind of devices - tap and vmnet 189236769Sobrien */ 190236769Sobrienstatic void 191236769Sobrientapclone(arg, name, namelen, dev) 192236769Sobrien void *arg; 193236769Sobrien char *name; 194236769Sobrien int namelen; 195236769Sobrien dev_t *dev; 196236769Sobrien{ 197236769Sobrien u_int extra; 198236769Sobrien int i, unit; 199236769Sobrien char *device_name = name; 200236769Sobrien 201236769Sobrien if (*dev != NODEV) 202236769Sobrien return; 203236769Sobrien 204236769Sobrien device_name = TAP; 205236769Sobrien extra = 0; 206236769Sobrien if (strcmp(name, TAP) == 0) { 207236769Sobrien unit = -1; 208236769Sobrien } else if (strcmp(name, VMNET) == 0) { 209236769Sobrien device_name = VMNET; 210236769Sobrien extra = VMNET_DEV_MASK; 211236769Sobrien unit = -1; 212236769Sobrien } else if (dev_stdclone(name, NULL, device_name, &unit) != 1) { 213236769Sobrien device_name = VMNET; 214236769Sobrien extra = VMNET_DEV_MASK; 215236769Sobrien if (dev_stdclone(name, NULL, device_name, &unit) != 1) 216236769Sobrien return; 217236769Sobrien } 218236769Sobrien 219236769Sobrien /* find any existing device, or allocate new unit number */ 220236769Sobrien i = clone_create(&tapclones, &tap_cdevsw, &unit, dev, extra); 221236769Sobrien if (i) { 222236769Sobrien *dev = make_dev(&tap_cdevsw, unit2minor(unit | extra), 223236769Sobrien UID_ROOT, GID_WHEEL, 0600, "%s%d", device_name, unit); 224276305Sngie if (*dev != NULL) 225276305Sngie (*dev)->si_flags |= SI_CHEAPCLONE; 226276305Sngie } 227236769Sobrien} /* tapclone */ 228236769Sobrien 229236769Sobrien 230236769Sobrien/* 231236769Sobrien * tapcreate 232236769Sobrien * 233236769Sobrien * to create interface 234236769Sobrien */ 235236769Sobrienstatic void 236236769Sobrientapcreate(dev) 237236769Sobrien dev_t dev; 238236769Sobrien{ 239236769Sobrien struct ifnet *ifp = NULL; 240236769Sobrien struct tap_softc *tp = NULL; 241236769Sobrien unsigned short macaddr_hi; 242236769Sobrien int unit, s; 243236769Sobrien char *name = NULL; 244236769Sobrien 245236769Sobrien dev->si_flags &= ~SI_CHEAPCLONE; 246236769Sobrien 247236769Sobrien /* allocate driver storage and create device */ 248236769Sobrien MALLOC(tp, struct tap_softc *, sizeof(*tp), M_TAP, M_WAITOK | M_ZERO); 249236769Sobrien SLIST_INSERT_HEAD(&taphead, tp, tap_next); 250236769Sobrien 251236769Sobrien unit = dev2unit(dev); 252236769Sobrien 253236769Sobrien /* select device: tap or vmnet */ 254236769Sobrien if (unit & VMNET_DEV_MASK) { 255236769Sobrien name = VMNET; 256236769Sobrien tp->tap_flags |= TAP_VMNET; 257236769Sobrien } else 258236769Sobrien name = TAP; 259236769Sobrien 260236769Sobrien unit &= TAPMAXUNIT; 261236769Sobrien 262236769Sobrien TAPDEBUG("tapcreate(%s%d). minor = %#x\n", name, unit, minor(dev)); 263236769Sobrien 264236769Sobrien /* generate fake MAC address: 00 bd xx xx xx unit_no */ 265236769Sobrien macaddr_hi = htons(0x00bd); 266236769Sobrien bcopy(&macaddr_hi, &tp->arpcom.ac_enaddr[0], sizeof(short)); 267236769Sobrien bcopy(&ticks, &tp->arpcom.ac_enaddr[2], sizeof(long)); 268236769Sobrien tp->arpcom.ac_enaddr[5] = (u_char)unit; 269236769Sobrien 270236769Sobrien /* fill the rest and attach interface */ 271236769Sobrien ifp = &tp->tap_if; 272236769Sobrien ifp->if_softc = tp; 273236769Sobrien if_initname(ifp, name, unit); 274236769Sobrien ifp->if_init = tapifinit; 275236769Sobrien ifp->if_start = tapifstart; 276236769Sobrien ifp->if_ioctl = tapifioctl; 277236769Sobrien ifp->if_mtu = ETHERMTU; 278236769Sobrien ifp->if_flags = (IFF_BROADCAST|IFF_SIMPLEX|IFF_MULTICAST); 279236769Sobrien ifp->if_snd.ifq_maxlen = ifqmaxlen; 280236769Sobrien 281236769Sobrien dev->si_drv1 = tp; 282236769Sobrien tp->tap_dev = dev; 283236769Sobrien 284236769Sobrien s = splimp(); 285236769Sobrien ether_ifattach(ifp, tp->arpcom.ac_enaddr); 286236769Sobrien splx(s); 287236769Sobrien 288236769Sobrien tp->tap_flags |= TAP_INITED; 289236769Sobrien 290236769Sobrien TAPDEBUG("interface %s is created. minor = %#x\n", 291236769Sobrien ifp->if_xname, minor(dev)); 292236769Sobrien} /* tapcreate */ 293236769Sobrien 294236769Sobrien 295236769Sobrien/* 296236769Sobrien * tapopen 297236769Sobrien * 298236769Sobrien * to open tunnel. must be superuser 299236769Sobrien */ 300236769Sobrienstatic int 301236769Sobrientapopen(dev, flag, mode, td) 302236769Sobrien dev_t dev; 303236769Sobrien int flag; 304236769Sobrien int mode; 305236769Sobrien struct thread *td; 306236769Sobrien{ 307236769Sobrien struct tap_softc *tp = NULL; 308236769Sobrien int error; 309236769Sobrien 310236769Sobrien if ((error = suser(td)) != 0) 311236769Sobrien return (error); 312236769Sobrien 313236769Sobrien if ((dev2unit(dev) & CLONE_UNITMASK) > TAPMAXUNIT) 314236769Sobrien return (ENXIO); 315236769Sobrien 316236769Sobrien tp = dev->si_drv1; 317236769Sobrien if (tp == NULL) { 318236769Sobrien tapcreate(dev); 319236769Sobrien tp = dev->si_drv1; 320236769Sobrien } 321236769Sobrien 322236769Sobrien KASSERT(!(tp->tap_flags & TAP_OPEN), 323236769Sobrien ("%s flags is out of sync", tp->tap_if.if_xname)); 324236769Sobrien 325236769Sobrien bcopy(tp->arpcom.ac_enaddr, tp->ether_addr, sizeof(tp->ether_addr)); 326236769Sobrien 327236769Sobrien tp->tap_pid = td->td_proc->p_pid; 328236769Sobrien tp->tap_flags |= TAP_OPEN; 329236769Sobrien 330236769Sobrien TAPDEBUG("%s is open. minor = %#x\n", 331236769Sobrien tp->tap_if.if_xname, minor(dev)); 332236769Sobrien 333236769Sobrien return (0); 334236769Sobrien} /* tapopen */ 335236769Sobrien 336236769Sobrien 337236769Sobrien/* 338236769Sobrien * tapclose 339236769Sobrien * 340236769Sobrien * close the device - mark i/f down & delete routing info 341236769Sobrien */ 342236769Sobrienstatic int 343236769Sobrientapclose(dev, foo, bar, td) 344236769Sobrien dev_t dev; 345236769Sobrien int foo; 346236769Sobrien int bar; 347236769Sobrien struct thread *td; 348236769Sobrien{ 349236769Sobrien struct tap_softc *tp = dev->si_drv1; 350236769Sobrien struct ifnet *ifp = &tp->tap_if; 351236769Sobrien int s; 352236769Sobrien 353236769Sobrien /* junk all pending output */ 354236769Sobrien IF_DRAIN(&ifp->if_snd); 355236769Sobrien 356236769Sobrien /* 357236769Sobrien * do not bring the interface down, and do not anything with 358236769Sobrien * interface, if we are in VMnet mode. just close the device. 359236769Sobrien */ 360236769Sobrien 361236769Sobrien if (((tp->tap_flags & TAP_VMNET) == 0) && (ifp->if_flags & IFF_UP)) { 362236769Sobrien s = splimp(); 363236769Sobrien if_down(ifp); 364236769Sobrien if (ifp->if_flags & IFF_RUNNING) { 365236769Sobrien /* find internet addresses and delete routes */ 366236769Sobrien struct ifaddr *ifa = NULL; 367236769Sobrien 368236769Sobrien TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { 369236769Sobrien if (ifa->ifa_addr->sa_family == AF_INET) { 370236769Sobrien rtinit(ifa, (int)RTM_DELETE, 0); 371236769Sobrien 372236769Sobrien /* remove address from interface */ 373236769Sobrien bzero(ifa->ifa_addr, 374236769Sobrien sizeof(*(ifa->ifa_addr))); 375236769Sobrien bzero(ifa->ifa_dstaddr, 376236769Sobrien sizeof(*(ifa->ifa_dstaddr))); 377236769Sobrien bzero(ifa->ifa_netmask, 378236769Sobrien sizeof(*(ifa->ifa_netmask))); 379236769Sobrien } 380236769Sobrien } 381236769Sobrien 382236769Sobrien ifp->if_flags &= ~IFF_RUNNING; 383236769Sobrien } 384236769Sobrien splx(s); 385236769Sobrien } 386236769Sobrien 387236769Sobrien funsetown(&tp->tap_sigio); 388236769Sobrien selwakeuppri(&tp->tap_rsel, PZERO+1); 389236769Sobrien 390236769Sobrien tp->tap_flags &= ~TAP_OPEN; 391236769Sobrien tp->tap_pid = 0; 392236769Sobrien 393236769Sobrien TAPDEBUG("%s is closed. minor = %#x\n", 394236769Sobrien ifp->if_xname, minor(dev)); 395236769Sobrien 396236769Sobrien return (0); 397236769Sobrien} /* tapclose */ 398236769Sobrien 399236769Sobrien 400236769Sobrien/* 401236769Sobrien * tapifinit 402236769Sobrien * 403236769Sobrien * network interface initialization function 404236769Sobrien */ 405236769Sobrienstatic void 406236769Sobrientapifinit(xtp) 407236769Sobrien void *xtp; 408236769Sobrien{ 409236769Sobrien struct tap_softc *tp = (struct tap_softc *)xtp; 410236769Sobrien struct ifnet *ifp = &tp->tap_if; 411236769Sobrien 412236769Sobrien TAPDEBUG("initializing %s\n", ifp->if_xname); 413236769Sobrien 414236769Sobrien ifp->if_flags |= IFF_RUNNING; 415236769Sobrien ifp->if_flags &= ~IFF_OACTIVE; 416236769Sobrien 417236769Sobrien /* attempt to start output */ 418236769Sobrien tapifstart(ifp); 419236769Sobrien} /* tapifinit */ 420236769Sobrien 421236769Sobrien 422236769Sobrien/* 423236769Sobrien * tapifioctl 424236769Sobrien * 425236769Sobrien * Process an ioctl request on network interface 426236769Sobrien */ 427236769Sobrienstatic int 428236769Sobrientapifioctl(ifp, cmd, data) 429236769Sobrien struct ifnet *ifp; 430236769Sobrien u_long cmd; 431236769Sobrien caddr_t data; 432236769Sobrien{ 433236769Sobrien struct tap_softc *tp = (struct tap_softc *)(ifp->if_softc); 434236769Sobrien struct ifstat *ifs = NULL; 435236769Sobrien int s, dummy; 436236769Sobrien 437236769Sobrien switch (cmd) { 438236769Sobrien case SIOCSIFFLAGS: /* XXX -- just like vmnet does */ 439236769Sobrien case SIOCADDMULTI: 440236769Sobrien case SIOCDELMULTI: 441236769Sobrien break; 442236769Sobrien 443236769Sobrien case SIOCGIFSTATUS: 444236769Sobrien s = splimp(); 445236769Sobrien ifs = (struct ifstat *)data; 446236769Sobrien dummy = strlen(ifs->ascii); 447236769Sobrien if (tp->tap_pid != 0 && dummy < sizeof(ifs->ascii)) 448236769Sobrien snprintf(ifs->ascii + dummy, 449236769Sobrien sizeof(ifs->ascii) - dummy, 450236769Sobrien "\tOpened by PID %d\n", tp->tap_pid); 451236769Sobrien splx(s); 452236769Sobrien break; 453236769Sobrien 454236769Sobrien default: 455236769Sobrien s = splimp(); 456236769Sobrien dummy = ether_ioctl(ifp, cmd, data); 457236769Sobrien splx(s); 458236769Sobrien return (dummy); 459236769Sobrien } 460236769Sobrien 461236769Sobrien return (0); 462236769Sobrien} /* tapifioctl */ 463236769Sobrien 464236769Sobrien 465236769Sobrien/* 466236769Sobrien * tapifstart 467 * 468 * queue packets from higher level ready to put out 469 */ 470static void 471tapifstart(ifp) 472 struct ifnet *ifp; 473{ 474 struct tap_softc *tp = ifp->if_softc; 475 int s; 476 477 TAPDEBUG("%s starting\n", ifp->if_xname); 478 479 /* 480 * do not junk pending output if we are in VMnet mode. 481 * XXX: can this do any harm because of queue overflow? 482 */ 483 484 if (((tp->tap_flags & TAP_VMNET) == 0) && 485 ((tp->tap_flags & TAP_READY) != TAP_READY)) { 486 struct mbuf *m = NULL; 487 488 TAPDEBUG("%s not ready, tap_flags = 0x%x\n", ifp->if_xname, 489 tp->tap_flags); 490 491 s = splimp(); 492 do { 493 IF_DEQUEUE(&ifp->if_snd, m); 494 if (m != NULL) 495 m_freem(m); 496 ifp->if_oerrors ++; 497 } while (m != NULL); 498 splx(s); 499 500 return; 501 } 502 503 s = splimp(); 504 ifp->if_flags |= IFF_OACTIVE; 505 506 if (ifp->if_snd.ifq_len != 0) { 507 if (tp->tap_flags & TAP_RWAIT) { 508 tp->tap_flags &= ~TAP_RWAIT; 509 wakeup(tp); 510 } 511 512 if ((tp->tap_flags & TAP_ASYNC) && (tp->tap_sigio != NULL)) 513 pgsigio(&tp->tap_sigio, SIGIO, 0); 514 515 selwakeuppri(&tp->tap_rsel, PZERO+1); 516 ifp->if_opackets ++; /* obytes are counted in ether_output */ 517 } 518 519 ifp->if_flags &= ~IFF_OACTIVE; 520 splx(s); 521} /* tapifstart */ 522 523 524/* 525 * tapioctl 526 * 527 * the cdevsw interface is now pretty minimal 528 */ 529static int 530tapioctl(dev, cmd, data, flag, td) 531 dev_t dev; 532 u_long cmd; 533 caddr_t data; 534 int flag; 535 struct thread *td; 536{ 537 struct tap_softc *tp = dev->si_drv1; 538 struct ifnet *ifp = &tp->tap_if; 539 struct tapinfo *tapp = NULL; 540 int s; 541 int f; 542 543 switch (cmd) { 544 case TAPSIFINFO: 545 s = splimp(); 546 tapp = (struct tapinfo *)data; 547 ifp->if_mtu = tapp->mtu; 548 ifp->if_type = tapp->type; 549 ifp->if_baudrate = tapp->baudrate; 550 splx(s); 551 break; 552 553 case TAPGIFINFO: 554 tapp = (struct tapinfo *)data; 555 tapp->mtu = ifp->if_mtu; 556 tapp->type = ifp->if_type; 557 tapp->baudrate = ifp->if_baudrate; 558 break; 559 560 case TAPSDEBUG: 561 tapdebug = *(int *)data; 562 break; 563 564 case TAPGDEBUG: 565 *(int *)data = tapdebug; 566 break; 567 568 case FIONBIO: 569 break; 570 571 case FIOASYNC: 572 s = splimp(); 573 if (*(int *)data) 574 tp->tap_flags |= TAP_ASYNC; 575 else 576 tp->tap_flags &= ~TAP_ASYNC; 577 splx(s); 578 break; 579 580 case FIONREAD: 581 s = splimp(); 582 if (ifp->if_snd.ifq_head) { 583 struct mbuf *mb = ifp->if_snd.ifq_head; 584 585 for(*(int *)data = 0;mb != NULL;mb = mb->m_next) 586 *(int *)data += mb->m_len; 587 } else 588 *(int *)data = 0; 589 splx(s); 590 break; 591 592 case FIOSETOWN: 593 return (fsetown(*(int *)data, &tp->tap_sigio)); 594 595 case FIOGETOWN: 596 *(int *)data = fgetown(&tp->tap_sigio); 597 return (0); 598 599 /* this is deprecated, FIOSETOWN should be used instead */ 600 case TIOCSPGRP: 601 return (fsetown(-(*(int *)data), &tp->tap_sigio)); 602 603 /* this is deprecated, FIOGETOWN should be used instead */ 604 case TIOCGPGRP: 605 *(int *)data = -fgetown(&tp->tap_sigio); 606 return (0); 607 608 /* VMware/VMnet port ioctl's */ 609 610 case SIOCGIFFLAGS: /* get ifnet flags */ 611 bcopy(&ifp->if_flags, data, sizeof(ifp->if_flags)); 612 break; 613 614 case VMIO_SIOCSIFFLAGS: /* VMware/VMnet SIOCSIFFLAGS */ 615 f = *(int *)data; 616 f &= 0x0fff; 617 f &= ~IFF_CANTCHANGE; 618 f |= IFF_UP; 619 620 s = splimp(); 621 ifp->if_flags = f | (ifp->if_flags & IFF_CANTCHANGE); 622 splx(s); 623 break; 624 625 case OSIOCGIFADDR: /* get MAC address of the remote side */ 626 case SIOCGIFADDR: 627 bcopy(tp->ether_addr, data, sizeof(tp->ether_addr)); 628 break; 629 630 case SIOCSIFADDR: /* set MAC address of the remote side */ 631 bcopy(data, tp->ether_addr, sizeof(tp->ether_addr)); 632 break; 633 634 default: 635 return (ENOTTY); 636 } 637 return (0); 638} /* tapioctl */ 639 640 641/* 642 * tapread 643 * 644 * the cdevsw read interface - reads a packet at a time, or at 645 * least as much of a packet as can be read 646 */ 647static int 648tapread(dev, uio, flag) 649 dev_t dev; 650 struct uio *uio; 651 int flag; 652{ 653 struct tap_softc *tp = dev->si_drv1; 654 struct ifnet *ifp = &tp->tap_if; 655 struct mbuf *m = NULL; 656 int error = 0, len, s; 657 658 TAPDEBUG("%s reading, minor = %#x\n", ifp->if_xname, minor(dev)); 659 660 if ((tp->tap_flags & TAP_READY) != TAP_READY) { 661 TAPDEBUG("%s not ready. minor = %#x, tap_flags = 0x%x\n", 662 ifp->if_xname, minor(dev), tp->tap_flags); 663 664 return (EHOSTDOWN); 665 } 666 667 tp->tap_flags &= ~TAP_RWAIT; 668 669 /* sleep until we get a packet */ 670 do { 671 s = splimp(); 672 IF_DEQUEUE(&ifp->if_snd, m); 673 splx(s); 674 675 if (m == NULL) { 676 if (flag & IO_NDELAY) 677 return (EWOULDBLOCK); 678 679 tp->tap_flags |= TAP_RWAIT; 680 error = tsleep(tp,PCATCH|(PZERO+1),"taprd",0); 681 if (error) 682 return (error); 683 } 684 } while (m == NULL); 685 686 /* feed packet to bpf */ 687 BPF_MTAP(ifp, m); 688 689 /* xfer packet to user space */ 690 while ((m != NULL) && (uio->uio_resid > 0) && (error == 0)) { 691 len = min(uio->uio_resid, m->m_len); 692 if (len == 0) 693 break; 694 695 error = uiomove(mtod(m, void *), len, uio); 696 m = m_free(m); 697 } 698 699 if (m != NULL) { 700 TAPDEBUG("%s dropping mbuf, minor = %#x\n", ifp->if_xname, 701 minor(dev)); 702 m_freem(m); 703 } 704 705 return (error); 706} /* tapread */ 707 708 709/* 710 * tapwrite 711 * 712 * the cdevsw write interface - an atomic write is a packet - or else! 713 */ 714static int 715tapwrite(dev, uio, flag) 716 dev_t dev; 717 struct uio *uio; 718 int flag; 719{ 720 struct tap_softc *tp = dev->si_drv1; 721 struct ifnet *ifp = &tp->tap_if; 722 struct mbuf *top = NULL, **mp = NULL, *m = NULL; 723 int error = 0, tlen, mlen; 724 725 TAPDEBUG("%s writting, minor = %#x\n", 726 ifp->if_xname, minor(dev)); 727 728 if (uio->uio_resid == 0) 729 return (0); 730 731 if ((uio->uio_resid < 0) || (uio->uio_resid > TAPMRU)) { 732 TAPDEBUG("%s invalid packet len = %d, minor = %#x\n", 733 ifp->if_xname, uio->uio_resid, minor(dev)); 734 735 return (EIO); 736 } 737 tlen = uio->uio_resid; 738 739 /* get a header mbuf */ 740 MGETHDR(m, M_DONTWAIT, MT_DATA); 741 if (m == NULL) 742 return (ENOBUFS); 743 mlen = MHLEN; 744 745 top = 0; 746 mp = ⊤ 747 while ((error == 0) && (uio->uio_resid > 0)) { 748 m->m_len = min(mlen, uio->uio_resid); 749 error = uiomove(mtod(m, void *), m->m_len, uio); 750 *mp = m; 751 mp = &m->m_next; 752 if (uio->uio_resid > 0) { 753 MGET(m, M_DONTWAIT, MT_DATA); 754 if (m == NULL) { 755 error = ENOBUFS; 756 break; 757 } 758 mlen = MLEN; 759 } 760 } 761 if (error) { 762 ifp->if_ierrors ++; 763 if (top) 764 m_freem(top); 765 return (error); 766 } 767 768 top->m_pkthdr.len = tlen; 769 top->m_pkthdr.rcvif = ifp; 770 771 /* Pass packet up to parent. */ 772 (*ifp->if_input)(ifp, top); 773 ifp->if_ipackets ++; /* ibytes are counted in parent */ 774 775 return (0); 776} /* tapwrite */ 777 778 779/* 780 * tappoll 781 * 782 * the poll interface, this is only useful on reads 783 * really. the write detect always returns true, write never blocks 784 * anyway, it either accepts the packet or drops it 785 */ 786static int 787tappoll(dev, events, td) 788 dev_t dev; 789 int events; 790 struct thread *td; 791{ 792 struct tap_softc *tp = dev->si_drv1; 793 struct ifnet *ifp = &tp->tap_if; 794 int s, revents = 0; 795 796 TAPDEBUG("%s polling, minor = %#x\n", 797 ifp->if_xname, minor(dev)); 798 799 s = splimp(); 800 if (events & (POLLIN | POLLRDNORM)) { 801 if (ifp->if_snd.ifq_len > 0) { 802 TAPDEBUG("%s have data in queue. len = %d, " \ 803 "minor = %#x\n", ifp->if_xname, 804 ifp->if_snd.ifq_len, minor(dev)); 805 806 revents |= (events & (POLLIN | POLLRDNORM)); 807 } else { 808 TAPDEBUG("%s waiting for data, minor = %#x\n", 809 ifp->if_xname, minor(dev)); 810 811 selrecord(td, &tp->tap_rsel); 812 } 813 } 814 815 if (events & (POLLOUT | POLLWRNORM)) 816 revents |= (events & (POLLOUT | POLLWRNORM)); 817 818 splx(s); 819 return (revents); 820} /* tappoll */ 821