diff --git a/etc/MAKEDEV b/etc/MAKEDEV index ca8fa918b338..025d36eb362a 100644 --- a/etc/MAKEDEV +++ b/etc/MAKEDEV @@ -128,6 +128,7 @@ # gsc Genius GS-4500 hand scanner # joy pc joystick # tun* Tunneling IP device +# tap* Ethernet Tunneling device # snp* tty snoop devices # spigot Video Spigot video acquisition card # ctx* Cortex-I video acquisition card @@ -256,6 +257,7 @@ all) sh MAKEDEV urio0 # cdev, USB devices too sh MAKEDEV bpf0 bpf1 bpf2 bpf3 # cdev, network sh MAKEDEV ipl tun0 tun1 tun2 tun3 # cdev, network + sh MAKEDEV tap0 tap1 tap2 tap3 # cdev, network sh MAKEDEV ch0 perfmon tw0 # cdev, miscellaneous sh MAKEDEV apm apmctl card0 card1 card2 card3 # cdev, laptop sh MAKEDEV pass4 xpt2 # cdev, CAM @@ -290,6 +292,7 @@ fixit) sh MAKEDEV smb0 # cdev, SMBus device sh MAKEDEV ums0 # cdev, USB devices sh MAKEDEV tun0 # cdev, network + sh MAKEDEV tap0 # cdev, network sh MAKEDEV ch0 # cdev, miscellaneous sh MAKEDEV apm apmctl card0 # cdev, laptop sh MAKEDEV pass1 xpt1 # cdev, CAM @@ -1441,6 +1444,15 @@ tun*) done ;; +tap*) + ntap=`expr $i : 'tap\(.*\)$'` + unit=0 + while [ $unit -le $ntap ]; do + mknod tap$unit c 149 $unit root:network + unit=$(($unit + 1)) + done + ;; + sysmouse) mknod sysmouse c 12 128 mknod consolectl c 12 255 diff --git a/share/man/man4/tap.4 b/share/man/man4/tap.4 new file mode 100644 index 000000000000..fba5466cd0bd --- /dev/null +++ b/share/man/man4/tap.4 @@ -0,0 +1,217 @@ +.\" $FreeBSD$ +.\" Based on PR#2411 +.\" +.Dd July 9, 2000 +.Os +.Dt TAP 4 +.Sh NAME +.Nm tap +.Nd Ethernet tunnel software network interface +.Sh SYNOPSIS +.Cd pseudo-device tap +.Sh DESCRIPTION +The +.Nm tap +interface is a software loopback mechanism that can be loosely +described as the network interface analog of the +.Xr pty 4 , +that is, +.Nm tap +does for network interfaces what the +.Nm pty +driver does for terminals. +.Pp +The +.Nm tap +driver, like the +.Nm pty +driver, provides two interfaces: an interface like the usual facility +it is simulating +.Po +an Ethernet network interface in the case of +.Nm tap , +or a terminal for +.Nm pty +.Pc , +and a character-special device +.Dq control +interface. +.Pp +The network interfaces are named +.Sy tap Ns Ar 0 , +.Sy tap Ns Ar 1 , +etc, as many as were made by +.Xr MAKEDEV 8 . +Each one supports the usual Ethernet network-interface +.Xr ioctl 2 Ns s , +such as +.Dv SIOCSIFADDR +and +.Dv SIOCSIFNETMASK , +and thus can be used with +.Xr ifconfig 8 +like any other Ethernet interface. When the system chooses to transmit +an Ethernet frame on the network interface, the frame can be read from +the control device +.Po +it appears as +.Dq input +there +.Pc ; +writing an Ethernet frame to the control device generates an input frame on +the network interface, as if the +.Pq non-existent +hardware had just received it. +.Pp +The Ethernet tunnel device, normally +.Pa /dev/tap Ns Sy N , +is exclusive-open +.Po +it cannot be opened if it is already open +.Pc +and is restricted to the super-user. +A +.Fn read +call will return an error +.Pq Er EHOSTDOWN +if the interface is not +.Dq ready . +Once the interface is ready, +.Fn read +will return an Ethernet frame if one is available; if not, it will +either block until one is or return +.Er EWOULDBLOCK , +depending on whether non-blocking I/O has been enabled. If the frame +is longer than is allowed for in the buffer passed to +.Fn read , +the extra data will be silently dropped. +.Pp +A +.Xr write 2 +call passes an Ethernet frame in to be +.Dq received +on the pseudo-interface. Each +.Fn write +call supplies exactly one frame; the frame length is taken from the +amount of data provided to +.Fn write . +Writes will not block; if the frame cannot be accepted +for a transient reason +.Pq e.g., no buffer space available , +it is silently dropped; if the reason is not transient +.Pq e.g., frame too large , +an error is returned. +The following +.Xr ioctl 2 +calls are supported +.Pq defined in Aq Pa net/if_tap.h Ns : +.Bl -tag -width VMIO_SIOCSETMACADDR +.It Dv TAPSDEBUG +The argument should be a pointer to an +.Va int ; +this sets the internal debugging variable to that value. What, if +anything, this variable controls is not documented here; see the source +code. +.It Dv TAPGDEBUG +The argument should be a pointer to an +.Va int ; +this stores the internal debugging variable's value into it. +.It Dv FIONBIO +Turn non-blocking I/O for reads off or on, according as the argument +.Va int Ns 's +value is or isn't zero +.Pq Writes are always nonblocking . +.It Dv FIOASYNC +Turn asynchronous I/O for reads +.Po +i.e., generation of +.Dv SIGIO +when data is available to be read +.Pc +off or on, according as the argument +.Va int Ns 's +value is or isn't zero. +.It Dv FIONREAD +If any frames are queued to be read, store the size of the first one into the argument +.Va int ; +otherwise, store zero. +.It Dv TIOCSPGRP +Set the process group to receive +.Dv SIGIO +signals, when asynchronous I/O is enabled, to the argument +.Va int +value. +.It Dv TIOCGPGRP +Retrieve the process group value for +.Dv SIGIO +signals into the argument +.Va int +value. +.It SIOCGIFADDR +Retrieve the Media Access Control +.Pq MAC +address. This command should be executed on descriptor, associated with +control device +.Pq Pa /dev/tap Ns Sy N . +The +.Va buffer , +which is passed as argument, is expected to have enought space to store +.Pq MAC +address. +.It SIOCSIFADDR +Set the Media Access Control +.Pq MAC +address. This command should be executed on a descriptor, associated with +control device +.Pq Pa /dev/tap Ns Sy N . +.El +.Pp +The control device also supports +.Xr select 2 +for read; selecting for write is pointless, and always succeeds, since +writes are always non-blocking. +.Pp +On the last close of the data device, by default, the interface is +brought down +.Po +as if with +.Dq ifconfig tap Ns Sy N No down +.Pc . +All queued frames are thrown away. If the interface is up when the data +device is not open output frames are always thrown away rather than +letting them pile up. +.Pp +The +.Nm tap +device is also can be used with VMware port as a replacement +of VMnet device driver. The driver uses minor number to select between +.Nm tap +and +.Nm vmnet +devices. VMnet minor numbering is +.Va 0x10000 ++ +.Va N . +Where +.Va N +is a VMnet unit number. In this case control device is expected to be +.Pa /dev/vmnet Ns Sy N , +and network interface will be +.Sy vmnet Ns Ar N . +Everything else is the same. +.Pp +In addition to mentioned above +.Xr ioctl 2 +there are additional one for VMware port. +.Bl -tag -width VMIO_SIOCSETMACADDR +.It VMIO_SIOCSIFFLAGS +VMware +.Dv SIOCSIFFLAGS . +.El +.Sh SEE ALSO +.Xr inet 4 , +.Xr intro 4 +.\" .Sh BUGS +.Sh AUTHORS +This man page has been obtained from +.Bx Free . diff --git a/sys/conf/files b/sys/conf/files index c70269c6557b..ad4ba1c57059 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -581,6 +581,7 @@ net/if_sl.c optional sl net/if_spppsubr.c optional sppp net/if_stf.c count stf net/if_tun.c optional tun +net/if_tap.c optional tap net/if_vlan.c count vlan net/intrq.c standard net/net_osdep.c standard diff --git a/sys/net/if_tap.c b/sys/net/if_tap.c new file mode 100644 index 000000000000..7b73dbf7ce4f --- /dev/null +++ b/sys/net/if_tap.c @@ -0,0 +1,785 @@ +/* + * Copyright (C) 1999-2000 by Maksim Yevmenkin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * BASED ON: + * ------------------------------------------------------------------------- + * + * Copyright (c) 1988, Julian Onions + * Nottingham University 1987. + */ + +/* + * $FreeBSD$ + * $Id: if_tap.c,v 0.19 2000/07/20 02:32:27 max Exp $ + */ + +#include "opt_inet.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + + +#define CDEV_NAME "tap" +#define CDEV_MAJOR 149 +#define TAPDEBUG if (tapdebug) printf + +#define TAP "tap" +#define VMNET "vmnet" +#define VMNET_DEV_MASK 0x00010000 + +/* module */ +static int tapmodevent __P((module_t, int, void *)); + +/* device */ +static void tapcreate __P((dev_t)); + +/* network interface */ +static void tapifstart __P((struct ifnet *)); +static int tapifioctl __P((struct ifnet *, u_long, caddr_t)); +static void tapifinit __P((void *)); + +/* character device */ +static d_open_t tapopen; +static d_close_t tapclose; +static d_read_t tapread; +static d_write_t tapwrite; +static d_ioctl_t tapioctl; +static d_poll_t tappoll; + +static struct cdevsw tap_cdevsw = { + /* open */ tapopen, + /* close */ tapclose, + /* read */ tapread, + /* write */ tapwrite, + /* ioctl */ tapioctl, + /* poll */ tappoll, + /* mmap */ nommap, + /* startegy */ nostrategy, + /* dev name */ CDEV_NAME, + /* dev major */ CDEV_MAJOR, + /* dump */ nodump, + /* psize */ nopsize, + /* flags */ 0, + /* bmaj */ -1 +}; + +static int taprefcnt = 0; /* module ref. counter */ +static int taplastunit = -1; /* max. open unit number */ +static int tapdebug = 0; /* debug flag */ + +MALLOC_DECLARE(M_TAP); +MALLOC_DEFINE(M_TAP, CDEV_NAME, "Ethernet tunnel interface"); +SYSCTL_INT(_debug, OID_AUTO, if_tap_debug, CTLFLAG_RW, &tapdebug, 0, ""); +DEV_MODULE(if_tap, tapmodevent, NULL); + +/* + * tapmodevent + * + * module event handler + */ +static int +tapmodevent(mod, type, data) + module_t mod; + int type; + void *data; +{ + static int attached = 0; + struct ifnet *ifp = NULL; + int unit, s; + + switch (type) { + case MOD_LOAD: + if (attached) + return (EEXIST); + + cdevsw_add(&tap_cdevsw); + attached = 1; + break; + + case MOD_UNLOAD: + if (taprefcnt > 0) + return (EBUSY); + + cdevsw_remove(&tap_cdevsw); + + unit = 0; + while (unit <= taplastunit) { + s = splimp(); + TAILQ_FOREACH(ifp, &ifnet, if_link) + if ((strcmp(ifp->if_name, TAP) == 0) || + (strcmp(ifp->if_name, VMNET) == 0)) + if (ifp->if_unit == unit) + break; + splx(s); + + if (ifp != NULL) { + struct tap_softc *tp = ifp->if_softc; + + TAPDEBUG("detaching %s%d. taplastunit = %d\n", + ifp->if_name, unit, taplastunit); + + ether_ifdetach(ifp, 1); + destroy_dev(tp->tap_dev); + FREE(tp, M_TAP); + } + else + unit ++; + } + + attached = 0; + break; + + default: + return (EOPNOTSUPP); + } + + return (0); +} /* tapmodevent */ + + +/* + * tapcreate + * + * to create interface + */ +static void +tapcreate(dev) + dev_t dev; +{ + struct ifnet *ifp = NULL; + struct tap_softc *tp = NULL; + unsigned short macaddr_hi; + int unit; + char *name = NULL; + + /* allocate driver storage and create device */ + MALLOC(tp, struct tap_softc *, sizeof(*tp), M_TAP, M_WAITOK); + bzero(tp, sizeof(*tp)); + + /* select device: tap or vmnet */ + if (minor(dev) & VMNET_DEV_MASK) { + name = VMNET; + unit = lminor(dev) & 0xff; + } + else { + name = TAP; + unit = lminor(dev); + } + + tp->tap_dev = make_dev(&tap_cdevsw, minor(dev), UID_UUCP, GID_DIALER, + 0600, "%s%d", name, unit); + tp->tap_dev->si_drv1 = dev->si_drv1 = tp; + + /* generate fake MAC address: 00 bd xx xx xx unit_no */ + macaddr_hi = htons(0x00bd); + bcopy(&macaddr_hi, &tp->arpcom.ac_enaddr[0], sizeof(short)); + bcopy(&ticks, &tp->arpcom.ac_enaddr[2], sizeof(long)); + tp->arpcom.ac_enaddr[5] = (u_char)unit; + + /* fill the rest and attach interface */ + ifp = &tp->tap_if; + ifp->if_softc = tp; + + ifp->if_unit = unit; + if (unit > taplastunit) + taplastunit = unit; + + ifp->if_name = name; + ifp->if_init = tapifinit; + ifp->if_output = ether_output; + ifp->if_start = tapifstart; + ifp->if_ioctl = tapifioctl; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = (IFF_BROADCAST|IFF_SIMPLEX|IFF_MULTICAST); + ifp->if_snd.ifq_maxlen = ifqmaxlen; + + ether_ifattach(ifp, 1); + + tp->tap_flags = TAP_INITED; +} /* tapcreate */ + + +/* + * tapopen + * + * to open tunnel. must be superuser + */ +static int +tapopen(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + struct tap_softc *tp = NULL; + int error; + + if ((error = suser(p)) != 0) + return (error); + + tp = dev->si_drv1; + if (tp == NULL) { + tapcreate(dev); + tp = dev->si_drv1; + } + + if (tp->tap_flags & TAP_OPEN) + return (EBUSY); + + tp->tap_pid = p->p_pid; + tp->tap_flags |= TAP_OPEN; + taprefcnt ++; + + TAPDEBUG("%s%d is open. refcnt = %d, taplastunit = %d\n", + tp->tap_if.if_name, tp->tap_if.if_unit, taprefcnt, taplastunit); + + return (0); +} /* tapopen */ + + +/* + * tapclose + * + * close the device - mark i/f down & delete routing info + */ +static int +tapclose(dev, foo, bar, p) + dev_t dev; + int foo; + int bar; + struct proc *p; +{ + int s; + struct tap_softc *tp = dev->si_drv1; + struct ifnet *ifp = &tp->tap_if; + struct mbuf *m = NULL; + + /* junk all pending output */ + + s = splimp(); + do { + IF_DEQUEUE(&ifp->if_snd, m); + if (m != NULL) + m_freem(m); + } while (m != NULL); + splx(s); + + if (ifp->if_flags & IFF_UP) { + s = splimp(); + if_down(ifp); + if (ifp->if_flags & IFF_RUNNING) { + /* find internet addresses and delete routes */ + struct ifaddr *ifa = NULL; + + for (ifa = ifp->if_addrhead.tqh_first; ifa; + ifa = ifa->ifa_link.tqe_next) { + if (ifa->ifa_addr->sa_family == AF_INET) { + rtinit(ifa, (int)RTM_DELETE, 0); + + /* remove address from interface */ + bzero(ifa->ifa_addr, + sizeof(*(ifa->ifa_addr))); + bzero(ifa->ifa_dstaddr, + sizeof(*(ifa->ifa_dstaddr))); + bzero(ifa->ifa_netmask, + sizeof(*(ifa->ifa_netmask))); + } + } + + ifp->if_flags &= ~IFF_RUNNING; + } + splx(s); + } + + funsetown(tp->tap_sigio); + selwakeup(&tp->tap_rsel); + + tp->tap_flags &= ~TAP_OPEN; + tp->tap_pid = 0; + + taprefcnt --; + if (taprefcnt < 0) { + taprefcnt = 0; + printf("%s%d refcnt = %d is out of sync. set refcnt to 0\n", + ifp->if_name, ifp->if_unit, taprefcnt); + } + + TAPDEBUG("%s%d is closed. refcnt = %d, taplastunit = %d\n", + ifp->if_name, ifp->if_unit, taprefcnt, taplastunit); + + return (0); +} /* tapclose */ + + +/* + * tapifinit + * + * network interface initialization function + */ +static void +tapifinit(xtp) + void *xtp; +{ + struct tap_softc *tp = (struct tap_softc *)xtp; + struct ifnet *ifp = &tp->tap_if; + + TAPDEBUG("initializing %s%d\n", ifp->if_name, ifp->if_unit); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + /* attempt to start output */ + tapifstart(ifp); +} /* tapifinit */ + + +/* + * tapifioctl + * + * Process an ioctl request on network interface + */ +int +tapifioctl(ifp, cmd, data) + struct ifnet *ifp; + u_long cmd; + caddr_t data; +{ + struct tap_softc *tp = (struct tap_softc *)(ifp->if_softc); + struct ifstat *ifs = NULL; + int s, dummy; + + switch (cmd) { + case SIOCSIFADDR: + case SIOCGIFADDR: + case SIOCSIFMTU: + s = splimp(); + dummy = ether_ioctl(ifp, cmd, data); + splx(s); + return (dummy); + + case SIOCSIFFLAGS: /* XXX -- just like vmnet does */ + case SIOCADDMULTI: + case SIOCDELMULTI: + break; + + case SIOCGIFSTATUS: + s = splimp(); + ifs = (struct ifstat *)data; + dummy = strlen(ifs->ascii); + if (tp->tap_pid != 0 && dummy < sizeof(ifs->ascii)) + snprintf(ifs->ascii + dummy, + sizeof(ifs->ascii) - dummy, + "\tOpened by PID %d\n", tp->tap_pid); + splx(s); + break; + + default: + return (EINVAL); + } + + return (0); +} /* tapifioctl */ + + +/* + * tapifstart + * + * queue packets from higher level ready to put out + */ +static void +tapifstart(ifp) + struct ifnet *ifp; +{ + struct tap_softc *tp = ifp->if_softc; + int s; + + TAPDEBUG("%s%d starting\n", ifp->if_name, ifp->if_unit); + + if ((tp->tap_flags & TAP_READY) != TAP_READY) { + struct mbuf *m = NULL; + + TAPDEBUG("%s%d not ready. tap_flags = 0x%x\n", + ifp->if_name, ifp->if_unit, tp->tap_flags); + + s = splimp(); + do { + IF_DEQUEUE(&ifp->if_snd, m); + if (m != NULL) + m_freem(m); + ifp->if_oerrors ++; + } while (m != NULL); + splx(s); + + return; + } + + s = splimp(); + ifp->if_flags |= IFF_OACTIVE; + + if (ifp->if_snd.ifq_len != 0) { + if (tp->tap_flags & TAP_RWAIT) { + tp->tap_flags &= ~TAP_RWAIT; + wakeup((caddr_t)tp); + } + + if ((tp->tap_flags & TAP_ASYNC) && (tp->tap_sigio != NULL)) + pgsigio(tp->tap_sigio, SIGIO, 0); + + selwakeup(&tp->tap_rsel); + ifp->if_opackets ++; /* obytes are counted in ether_output */ + } + + ifp->if_flags &= ~IFF_OACTIVE; + splx(s); +} /* tapifstart */ + + +/* + * tapioctl + * + * the cdevsw interface is now pretty minimal + */ +static int +tapioctl(dev, cmd, data, flag, p) + dev_t dev; + u_long cmd; + caddr_t data; + int flag; + struct proc *p; +{ + struct tap_softc *tp = dev->si_drv1; + struct ifnet *ifp = &tp->tap_if; + struct tapinfo *tapp = NULL; + int s; + + switch (cmd) { + case TAPSIFINFO: + s = splimp(); + tapp = (struct tapinfo *)data; + ifp->if_mtu = tapp->mtu; + ifp->if_type = tapp->type; + ifp->if_baudrate = tapp->baudrate; + splx(s); + break; + + case TAPGIFINFO: + tapp = (struct tapinfo *)data; + tapp->mtu = ifp->if_mtu; + tapp->type = ifp->if_type; + tapp->baudrate = ifp->if_baudrate; + break; + + case TAPSDEBUG: + tapdebug = *(int *)data; + break; + + case TAPGDEBUG: + *(int *)data = tapdebug; + break; + + case FIONBIO: + break; + + case FIOASYNC: + if (*(int *)data) + tp->tap_flags |= TAP_ASYNC; + else + tp->tap_flags &= ~TAP_ASYNC; + break; + + case FIONREAD: + s = splimp(); + if (ifp->if_snd.ifq_head) { + struct mbuf *mb = ifp->if_snd.ifq_head; + + for(*(int *)data = 0; mb != 0; mb = mb->m_next) + *(int *)data += mb->m_len; + } + else + *(int *)data = 0; + splx(s); + break; + + case FIOSETOWN: + return (fsetown(*(int *)data, &tp->tap_sigio)); + + case FIOGETOWN: + *(int *)data = fgetown(tp->tap_sigio); + return (0); + + /* this is deprecated, FIOSETOWN should be used instead */ + case TIOCSPGRP: + return (fsetown(-(*(int *)data), &tp->tap_sigio)); + + /* this is deprecated, FIOGETOWN should be used instead */ + case TIOCGPGRP: + *(int *)data = -fgetown(tp->tap_sigio); + return (0); + + /* VMware/VMnet port ioctl's */ + + case SIOCGIFFLAGS: /* get ifnet flags */ + bcopy(&ifp->if_flags, data, sizeof(ifp->if_flags)); + break; + + case VMIO_SIOCSIFFLAGS: { /* VMware/VMnet SIOCSIFFLAGS */ + short f = *(short *)data; + + f &= 0x0fff; + f &= ~IFF_CANTCHANGE; + f |= IFF_UP; + + s = splimp(); + ifp->if_flags = f | (ifp->if_flags & IFF_CANTCHANGE); + splx(s); + } break; + + case OSIOCGIFADDR: /* get MAC address */ + case SIOCGIFADDR: + bcopy(tp->arpcom.ac_enaddr, data, ETHER_ADDR_LEN); + break; + + case SIOCSIFADDR: /* set MAC address */ + s = splimp(); + bcopy(data, tp->arpcom.ac_enaddr, ETHER_ADDR_LEN); + splx(s); + break; + + default: + return (ENOTTY); + } + return (0); +} /* tapioctl */ + + +/* + * tapread + * + * the cdevsw read interface - reads a packet at a time, or at + * least as much of a packet as can be read + */ +static int +tapread(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + struct tap_softc *tp = dev->si_drv1; + struct ifnet *ifp = &tp->tap_if; + struct mbuf *m = NULL, *m0 = NULL; + int error = 0, len, s; + + TAPDEBUG("%s%d reading\n", ifp->if_name, ifp->if_unit); + + if ((tp->tap_flags & TAP_READY) != TAP_READY) { + TAPDEBUG("%s%d not ready. tap_flags = 0x%x\n", + ifp->if_name, ifp->if_unit, tp->tap_flags); + return (EHOSTDOWN); + } + + tp->tap_flags &= ~TAP_RWAIT; + + /* sleep until we get a packet */ + do { + s = splimp(); + IF_DEQUEUE(&ifp->if_snd, m0); + splx(s); + + if (m0 == NULL) { + if (flag & IO_NDELAY) + return (EWOULDBLOCK); + + tp->tap_flags |= TAP_RWAIT; + error = tsleep((caddr_t)tp,PCATCH|(PZERO+1),"taprd",0); + if (error) + return (error); + } + } while (m0 == 0); + + /* feed packet to bpf */ + if (ifp->if_bpf != NULL) + bpf_mtap(ifp, m0); + + /* xfer packet to user space */ + while ((m0 != NULL) && (uio->uio_resid > 0) && (error == 0)) { + len = min(uio->uio_resid, m0->m_len); + if (len == 0) + break; + + error = uiomove(mtod(m0, caddr_t), len, uio); + MFREE(m0, m); + m0 = m; + } + + if (m0 != NULL) { + TAPDEBUG("%s%d dropping mbuf\n", ifp->if_name, ifp->if_unit); + m_freem(m0); + } + + return (error); +} /* tapread */ + + +/* + * tapwrite + * + * the cdevsw write interface - an atomic write is a packet - or else! + */ +static int +tapwrite(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + struct tap_softc *tp = dev->si_drv1; + struct ifnet *ifp = &tp->tap_if; + struct mbuf *top = NULL, **mp = NULL, *m = NULL; + struct ether_header *eh = NULL; + int error = 0, tlen, mlen; + + TAPDEBUG("%s%d writting\n", ifp->if_name, ifp->if_unit); + + if (uio->uio_resid == 0) + return (0); + + if ((uio->uio_resid < 0) || (uio->uio_resid > TAPMRU)) { + TAPDEBUG("%s%d invalid packet len = %d\n", + ifp->if_name, ifp->if_unit, uio->uio_resid); + return (EIO); + } + tlen = uio->uio_resid; + + /* get a header mbuf */ + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return (ENOBUFS); + mlen = MHLEN; + + top = 0; + mp = ⊤ + while ((error == 0) && (uio->uio_resid > 0)) { + m->m_len = min(mlen, uio->uio_resid); + error = uiomove(mtod(m, caddr_t), m->m_len, uio); + *mp = m; + mp = &m->m_next; + if (uio->uio_resid > 0) { + MGET(m, M_DONTWAIT, MT_DATA); + if (m == 0) { + error = ENOBUFS; + break; + } + mlen = MLEN; + } + } + if (error) { + ifp->if_ierrors ++; + if (top) + m_freem(top); + return (error); + } + + top->m_pkthdr.len = tlen; + top->m_pkthdr.rcvif = ifp; + + /* + * Ethernet bridge and bpf are handled in ether_input + * + * adjust mbuf and give packet to the ether_input + */ + + eh = mtod(top, struct ether_header *); + m_adj(top, sizeof(struct ether_header)); + ether_input(ifp, eh, top); + ifp->if_ipackets ++; /* ibytes are counted in ether_input */ + + return (0); +} /* tapwrite */ + + +/* + * tappoll + * + * the poll interface, this is only useful on reads + * really. the write detect always returns true, write never blocks + * anyway, it either accepts the packet or drops it + */ +static int +tappoll(dev, events, p) + dev_t dev; + int events; + struct proc *p; +{ + struct tap_softc *tp = dev->si_drv1; + struct ifnet *ifp = &tp->tap_if; + int s, revents = 0; + + TAPDEBUG("%s%d polling\n", ifp->if_name, ifp->if_unit); + + s = splimp(); + if (events & (POLLIN | POLLRDNORM)) { + if (ifp->if_snd.ifq_len > 0) { + TAPDEBUG("%s%d have data in queue. len = %d\n", + ifp->if_name,ifp->if_unit, ifp->if_snd.ifq_len); + revents |= (events & (POLLIN | POLLRDNORM)); + } + else { + TAPDEBUG("%s%d waiting for data\n", + ifp->if_name, ifp->if_unit); + selrecord(p, &tp->tap_rsel); + } + } + + if (events & (POLLOUT | POLLWRNORM)) + revents |= (events & (POLLOUT | POLLWRNORM)); + + splx(s); + return (revents); +} /* tappoll */ diff --git a/sys/net/if_tap.h b/sys/net/if_tap.h new file mode 100644 index 000000000000..30f742129191 --- /dev/null +++ b/sys/net/if_tap.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 1999-2000 by Maksim Yevmenkin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * BASED ON: + * ------------------------------------------------------------------------- + * + * Copyright (c) 1988, Julian Onions + * Nottingham University 1987. + */ + +/* + * $FreeBSD$ + * $Id: if_tap.h,v 0.7 2000/07/12 04:12:51 max Exp $ + */ + +#ifndef _NET_IF_TAP_H_ +#define _NET_IF_TAP_H_ + +/* refer to if_tapvar.h for the softc stuff */ + +/* maximum receive packet size (hard limit) */ +#define TAPMRU 16384 + +struct tapinfo { + int baudrate; /* linespeed */ + short mtu; /* maximum transmission unit */ + u_char type; /* ethernet, tokenring, etc. */ + u_char dummy; /* place holder */ +}; + +/* ioctl's for get/set debug */ +#define TAPSDEBUG _IOW('t', 90, int) +#define TAPGDEBUG _IOR('t', 89, int) +#define TAPSIFINFO _IOW('t', 91, struct tapinfo) +#define TAPGIFINFO _IOR('t', 92, struct tapinfo) + +/* VMware ioctl's */ +#define VMIO_SIOCSIFFLAGS _IO('V', 0) +#define VMIO_SIOCSKEEP _IO('V', 1) +#define VMIO_SIOCSIFBR _IO('V', 2) +#define VMIO_SIOCSLADRF _IO('V', 3) + +/* XXX -- unimplemented */ +#define VMIO_SIOCSETMACADDR _IO('V', 4) + +/* XXX -- not used? */ +#define VMIO_SIOCPORT _IO('V', 5) +#define VMIO_SIOCBRIDGE _IO('V', 6) +#define VMIO_SIOCNETIF _IO('V', 7) + +#endif /* !_NET_IF_TAP_H_ */ diff --git a/sys/net/if_tapvar.h b/sys/net/if_tapvar.h new file mode 100644 index 000000000000..c48ff1c83cc8 --- /dev/null +++ b/sys/net/if_tapvar.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 1999-2000 by Maksim Yevmenkin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * BASED ON: + * ------------------------------------------------------------------------- + * + * Copyright (c) 1998 Brian Somers + * All rights reserved. + * + * Copyright (c) 1988, Julian Onions + * Nottingham University 1987. + */ + +/* + * $FreeBSD$ + * $Id: if_tapvar.h,v 0.6 2000/07/11 02:16:08 max Exp $ + */ + +#ifndef _NET_IF_TAPVAR_H_ +#define _NET_IF_TAPVAR_H_ + +struct tap_softc { + struct arpcom arpcom; /* ethernet common data */ +#define tap_if arpcom.ac_if + dev_t tap_dev; /* device */ + + u_short tap_flags; /* misc flags */ +#define TAP_OPEN (1 << 0) +#define TAP_INITED (1 << 1) +#define TAP_RWAIT (1 << 2) +#define TAP_ASYNC (1 << 3) +#define TAP_READY (TAP_OPEN|TAP_INITED) + + pid_t tap_pid; /* PID of process to open */ + struct sigio *tap_sigio; /* information for async I/O */ + struct selinfo tap_rsel; /* read select */ +}; + +#endif /* !_NET_IF_TAPVAR_H_ */