OpenBSD Ethertap Support FOR OpenBSD 3.3 http://diehard.n-r-g.com/ Here is port of FreeBSD tap(4) - Ethernet tunnel software network interface. I tested it on a i386 and sparc64. It does not implement all of the features of FreeBSDs tap(4) driver, mainly the VMware stuff is probably not fully functional. You can apply the patch with: cd /sys patch -p0 < tap-3.3-20031016.patch This will install the two new files if_tap.c and if_tap.h and modify multiple configuration files (sys/conf.h, conf/files and the architecture dependent conf.c file). The patch was tested on i386 and sparc64 and for those two architectures I have added the conf.c change. On other architectures the file /sys/arch/$ARCH/$ARCH/conf.c needs to be modified. You can use the i386 file and the tun definitions as template. NOTE: I'm currently not able to test the 3.3 version because I do no longer have such a test system. Next, you need to add the following line to your kernel config: pseudo-device tap N where N is the number of tap devices you would like to have. 1 or 2 should normally be enough. Now reconfigure and rebuild your kernel: config GENERIC make depend && make Install the new kernel and reboot. An 'ifconfig -a' should now list the new tap interface, if not you probably did not add the pseudo-device directive to the kernel config. Last but not least make the /dev/tap0 device by issuing the following command: mknod /dev/tap0 c 44 0 # for the first tap interface mknod /dev/tap0 c 44 1 # for the second tap interface and so on until N-1 Untested are at the moment: - ALTQ integration - VMware support - non-INET protocols Index: arch/i386/i386/conf.c =================================================================== RCS file: /cvs/src/sys/arch/i386/i386/conf.c,v retrieving revision 1.104 diff -u -p -r1.104 conf.c --- arch/i386/i386/conf.c 23 Sep 2003 16:51:11 -0000 1.104 +++ arch/i386/i386/conf.c 16 Oct 2003 15:44:58 -0000 @@ -159,6 +159,7 @@ cdev_decl(pms); cdev_decl(cy); cdev_decl(mcd); #include "tun.h" +#include "tap.h" #include "audio.h" #include "midi.h" #include "sequencer.h" @@ -264,7 +265,7 @@ struct cdevsw cdevsw[] = #else cdev_notdef(), /* 43 */ #endif - cdev_notdef(), /* 44 */ + cdev_bpftun_init(NTAP,tap), /* 44: XXX hijacked for now */ cdev_random_init(1,random), /* 45: random data source */ cdev_ocis_init(NPCTR,pctr), /* 46: pentium performance counters */ cdev_disk_init(NRD,rd), /* 47: ram disk driver */ Index: arch/sparc64/sparc64/conf.c =================================================================== RCS file: /cvs/src/sys/arch/sparc64/sparc64/conf.c,v retrieving revision 1.36 diff -u -p -r1.36 conf.c --- arch/sparc64/sparc64/conf.c 27 Jun 2003 16:57:14 -0000 1.36 +++ arch/sparc64/sparc64/conf.c 16 Oct 2003 15:45:01 -0000 @@ -54,6 +54,7 @@ #include "pty.h" #include "bpfilter.h" #include "tun.h" +#include "tap.h" #include "audio.h" #include "vnd.h" #include "ccd.h" @@ -199,7 +200,7 @@ struct cdevsw cdevsw[] = cdev_notdef(), /* 41 */ cdev_notdef(), /* 42: SMD disk */ cdev_svr4_net_init(NSVR4_NET,svr4_net), /* 43: svr4 net pseudo-device */ - cdev_notdef(), /* 44 */ + cdev_bpftun_init(NTAP,tap), /* 44: XXX hijacked for now */ cdev_notdef(), /* 45 */ cdev_notdef(), /* 46 */ cdev_notdef(), /* 47 */ Index: conf/files =================================================================== RCS file: /cvs/src/sys/conf/files,v retrieving revision 1.282 diff -u -p -r1.282 files --- conf/files 7 Oct 2003 07:08:45 -0000 1.282 +++ conf/files 16 Oct 2003 15:45:01 -0000 @@ -355,6 +355,7 @@ pseudo-device loop: ifnet pseudo-device sl: ifnet pseudo-device ppp: ifnet pseudo-device tun: ifnet +pseudo-device tap: ifnet pseudo-device bpfilter: ifnet pseudo-device strip: ifnet pseudo-device enc: ifnet @@ -649,6 +650,7 @@ file net/ppp-deflate.c ppp_deflate file net/zlib.c ppp_deflate | ipsec | crypto file net/if_tokensubr.c token needs-flag file net/if_tun.c tun needs-count +file net/if_tap.c tap needs-count file net/if_bridge.c bridge needs-count file net/bridgestp.c bridge file net/if_vlan.c vlan needs-count Index: sys/conf.h =================================================================== RCS file: /cvs/src/sys/sys/conf.h,v retrieving revision 1.72 diff -u -p -r1.72 conf.h --- sys/conf.h 23 Sep 2003 16:51:13 -0000 1.72 +++ sys/conf.h 16 Oct 2003 15:45:03 -0000 @@ -581,6 +581,7 @@ cdev_decl(bpf); cdev_decl(pf); cdev_decl(tun); +cdev_decl(tap); cdev_decl(random); --- net/if_tap.h Thu Jan 1 01:00:00 1970 +++ net/if_tap.h Wed Oct 15 16:28:58 2003 @@ -0,0 +1,86 @@ +/* + * 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. + */ + +/* + * $OpenBSD$ + * $FreeBSD: src/sys/net/if_tap.h,v 1.1 2000/07/20 17:01:10 nsayer Exp $ + * $Id: if_tap.h,v 0.7 2000/07/12 04:12:51 max Exp $ + */ + +/* + * Ported to OpenBSD by Claudio Jeker. + */ + +#ifndef _NET_IF_TAP_H_ +#define _NET_IF_TAP_H_ + +/* maximum receive packet size (hard limit) */ +#define TAPMRU 16384 + +/* known TAP 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) +#define TAP_VMNET (1 << 4) +#define TAP_NBIO (1 << 5) +#define TAP_STAYUP (1 << 6) + +struct tapinfo { + int baudrate; /* linespeed */ + short mtu; /* maximum transmission unit */ + u_char type; /* ethernet, tokenring, etc. */ + u_char flags; /* tap flags */ +}; + +/* 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_ */ --- net/if_tap.c Thu Jan 1 01:00:00 1970 +++ net/if_tap.c Wed Oct 15 16:28:58 2003 @@ -0,0 +1,650 @@ +/* + * 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. + */ + +/* + * $OpenBSD$ + * $FreeBSD: src/sys/net/if_tap.c,v 1.4 2000/09/19 10:28:41 phk Exp $ + * $Id: if_tap.c,v 0.21 2000/07/23 21:46:02 max Exp $ + */ + +/* + * Ported to OpenBSD by Claudio Jeker + */ +/* #define TAP_DEBUG 1 */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#ifdef INET +#include +#include +#include +#include +#include +#endif + +#include "bpfilter.h" +#if NBPFILTER > 0 +#include +#endif + + +#include + +struct tap_softc { + struct arpcom arpcom; /* ethernet common data */ +#define tap_if arpcom.ac_if + + u_short tap_flags; /* misc flags */ + u_int8_t ether_addr[ETHER_ADDR_LEN]; /* ether addr of the remote side */ + /* information needed for async IO */ + pid_t tap_pgid; /* the process group - if any */ + uid_t tap_siguid; /* uid for process that set tun_pgid */ + uid_t tap_sigeuid; /* euid for process that set tun_pgid */ + struct selinfo tap_rsel; /* read select */ + struct selinfo tap_wsel; /* write select (not used) */ +}; + +#ifdef TAP_DEBUG +int tapdebug = TAP_DEBUG; +#define TAPDEBUG(a) (tapdebug? printf a : 0) +#else +#define TAPDEBUG(a) /* (tapdebug? printf a : 0) */ +#endif + +struct tap_softc *tapctl; +int ntap; + +extern int ifqmaxlen; + +void tapattach(int); +int tapopen(dev_t, int, int, struct proc *); +int tapclose(dev_t, int, int, struct proc *); +int tapifioctl(struct ifnet *, u_long, caddr_t); +int tapioctl(dev_t, u_long, caddr_t, int, struct proc *); +int tapread(dev_t, struct uio *, int); +int tapwrite(dev_t, struct uio *, int); +int tapselect(dev_t, int, struct proc *); +int tapkqfilter(dev_t, struct knote *); + + +static int tapinit(struct tap_softc *); +static void tapifstart(struct ifnet *); + +void +tapattach(int n) +{ + int i; + struct ifnet *ifp; + u_int32_t macaddr_rnd; + + ntap = n; + tapctl = malloc(ntap * sizeof(*tapctl), M_DEVBUF, M_WAITOK); + bzero(tapctl, ntap * sizeof(*tapctl)); + for (i = 0; i < ntap; i++) { + tapctl[i].tap_flags = TAP_INITED; + + /* generate fake MAC address: 00 bd xx xx xx unit_no */ + tapctl[i].arpcom.ac_enaddr[0] = 0x00; + tapctl[i].arpcom.ac_enaddr[1] = 0xbd; + /* + * XXX use of random() by anything except the scheduler is + * normally invalid, but this is boot time, so pre-scheduler, + * and the random subsystem is not alive yet + */ + macaddr_rnd = random(); + bcopy(&macaddr_rnd, &tapctl[i].arpcom.ac_enaddr[2], + sizeof(u_int32_t)); + tapctl[i].arpcom.ac_enaddr[5] = (u_char)i+1; + + ifp = &tapctl[i].tap_if; + snprintf(ifp->if_xname, sizeof ifp->if_xname, "tap%d", i); + ifp->if_softc = &tapctl[i]; + ifp->if_mtu = ETHERMTU; + ifp->if_ioctl = tapifioctl; + ifp->if_output = ether_output; + ifp->if_start = tapifstart; + ifp->if_flags = (IFF_BROADCAST|IFF_SIMPLEX|IFF_MULTICAST); + IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); + IFQ_SET_READY(&ifp->if_snd); + if_attach(ifp); + ether_ifattach(ifp); + + } +} + + +/* + * tap open - must be superuser & the device must be + * configured in + */ +int +tapopen(dev_t dev, int flag, int mode, struct proc *p) +{ + struct tap_softc *tp; + struct ifnet *ifp; + int unit, error; + + if ((error = suser(p->p_ucred, &p->p_acflag)) != 0) + return (error); + + if ((unit = minor(dev)) >= ntap) + return (ENXIO); + + tp = &tapctl[unit]; + if (tp->tap_flags & TAP_OPEN) + return EBUSY; + + ifp = &tp->tap_if; + bcopy(tp->arpcom.ac_enaddr, tp->ether_addr, sizeof(tp->ether_addr)); + tp->tap_flags |= TAP_OPEN; + tapinit(tp); + TAPDEBUG(("%s: open\n", ifp->if_xname)); + return (0); +} + +/* + * tapclose - close the device; if closing the real device, flush pending + * output and (unless set STAYUP) bring down the interface. + */ +int +tapclose(dev_t dev, int flag, int mode, struct proc *p) +{ + int unit, s; + struct tap_softc *tp; + struct ifnet *ifp; + + if ((unit = minor(dev)) >= ntap) + return (ENXIO); + + tp = &tapctl[unit]; + ifp = &tp->tap_if; + tp->tap_flags &= ~TAP_OPEN; + + /* + * junk all pending output + */ + s = splimp(); + IFQ_PURGE(&ifp->if_snd); + splx(s); + + if ((ifp->if_flags & IFF_UP) && !(tp->tap_flags & TAP_STAYUP)) { + s = splimp(); + if_down(ifp); + if (ifp->if_flags & IFF_RUNNING) { + /* find internet addresses and delete routes */ + struct ifaddr *ifa; + TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { +#ifdef INET + if (ifa->ifa_addr->sa_family == AF_INET) { + rtinit(ifa, (int)RTM_DELETE, 0); + } +#endif + /* XXX INET6 and other protocols ? */ + } + } + splx(s); + } + tp->tap_pgid = 0; + selwakeup(&tp->tap_rsel); + + TAPDEBUG(("%s: closed\n", ifp->if_xname)); + return (0); +} + +/* + * network interface initialization function + */ +static int +tapinit(struct tap_softc *tp) +{ + struct ifnet *ifp = &tp->tap_if; + + TAPDEBUG(("%s: tapinit\n", ifp->if_xname)); + TAPDEBUG(("%s: ethernet address: %s\n", ifp->if_xname, + ether_sprintf(tp->arpcom.ac_enaddr))); + + ifp->if_flags |= IFF_UP | IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; /* we are never active */ + + return 0; +} + +/* + * Process an ioctl request on network interface + */ +int +tapifioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct tap_softc *tp = (struct tap_softc *)(ifp->if_softc); + struct ifreq *ifr = (struct ifreq *) data; + struct ifaddr *ifa = (struct ifaddr *)data; + int error = 0, s; + + s = splimp(); + if ((error = ether_ioctl(ifp, &tp->arpcom, cmd, data)) > 0) { + splx(s); + return (error); + } + switch (cmd) { + case SIOCSIFADDR: + tapinit(tp); + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + arp_ifinit(&tp->arpcom, ifa); + break; +#endif + default: + break; + } + break; + case SIOCSIFMTU: + if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > 16384) + error = EINVAL; + else { + ifp->if_mtu = ifr->ifr_mtu; + } + break; + case SIOCSIFFLAGS: /* XXX -- just like vmnet does */ + case SIOCADDMULTI: + case SIOCDELMULTI: + break; + /* + * CJ - I'm not interested in vmnet I will add it somewhen, + * but first I need to test it. Currently I don't have a test app. + */ +#if 0 + case SIOCADDMULTI: + case SIOCDELMULTI: + error = (command == SIOCADDMULTI) ? + ether_addmulti(ifr, &tp->arpcom) : + ether_delmulti(ifr, &tp->arpcom); + if (error == ENETRESET) { + /* + * Multicast list has changed; set the hardware + * filter accordingly. The good thing is we do + * not have a hardware filter (: + */ + error = 0; + } + break; + /* XXX stuff that could be cool */ + case SIOCSIFPHYADDR: + break; +#endif + default: + error = EINVAL; + } + splx(s); + return (error); +} + +/* + * the cdevsw interface is now pretty minimal. + */ +int +tapioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + int unit, s; + struct tap_softc *tp; + struct tapinfo *tapp; + struct ifnet *ifp; + struct mbuf *m; + + if ((unit = minor(dev)) >= ntap) + return (ENXIO); + + tp = &tapctl[unit]; + + ifp = &tp->tap_if; + + s = splimp(); + switch (cmd) { + case TAPSIFINFO: + tapp = (struct tapinfo *)data; + ifp->if_baudrate = tapp->baudrate; + ifp->if_mtu = tapp->mtu; + ifp->if_type = tapp->type; + ifp->if_flags = tapp->flags; + break; + case TAPGIFINFO: + tapp = (struct tapinfo *)data; + tapp->baudrate = ifp->if_baudrate; + tapp->mtu = ifp->if_mtu; + tapp->type = ifp->if_type; + ifp->if_flags = tapp->flags; + break; +#ifdef TAP_DEBUG + case TAPSDEBUG: + tapdebug = *(int *)data; + break; + case TAPGDEBUG: + *(int *)data = tapdebug; + break; +#endif + case FIONBIO: + if (*(int *)data) + tp->tap_flags |= TAP_NBIO; + else + tp->tap_flags &= ~TAP_NBIO; + break; + case FIOASYNC: + if (*(int *)data) + tp->tap_flags |= TAP_ASYNC; + else + tp->tap_flags &= ~TAP_ASYNC; + break; + case FIONREAD: + IFQ_POLL(&tp->tap_if.if_snd, m); + if (m != NULL) + *(int *)data = m->m_pkthdr.len; + else + *(int *)data = 0; + break; + case TIOCSPGRP: + tp->tap_pgid = *(int *)data; + tp->tap_siguid = p->p_cred->p_ruid; + tp->tap_sigeuid = p->p_ucred->cr_uid; + break; + case TIOCGPGRP: + *(int *)data = tp->tap_pgid; + break; + + /* 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; + + ifp->if_flags = f | (ifp->if_flags & IFF_CANTCHANGE); + } + break; + case OSIOCGIFADDR: /* get MAC address of the remote side */ + case SIOCGIFADDR: /* NOTE: this is the user program view */ + bcopy(tp->ether_addr, data, sizeof(tp->ether_addr)); + break; + case SIOCSIFADDR: /* set MAC address of the remote side */ + bcopy(data, tp->ether_addr, sizeof(tp->ether_addr)); + break; + default: + splx(s); + return (ENOTTY); + } + splx(s); + return (0); +} + + +/* + * The cdevsw read interface - reads a packet at a time, or at + * least as much of a packet as can be read. + */ +int +tapread(dev_t dev, struct uio *uio, int ioflag) +{ + int unit; + struct tap_softc *tp; + struct ifnet *ifp; + struct mbuf *m, *m0; + int error = 0, len, s; + + if ((unit = minor(dev)) >= ntap) + return (ENXIO); + + tp = &tapctl[unit]; + ifp = &tp->tap_if; + TAPDEBUG(("%s: read\n", ifp->if_xname)); + if ((tp->tap_flags & TAP_READY) != TAP_READY) { + TAPDEBUG(("%s: not ready 0%o\n", ifp->if_xname, + tp->tap_flags)); + return EHOSTDOWN; + } + + tp->tap_flags &= ~TAP_RWAIT; + + s = splimp(); + do { + while ((tp->tap_flags & TAP_READY) != TAP_READY) + if ((error = tsleep((caddr_t)tp, + (PZERO+1)|PCATCH, "tapread", 0)) != 0) { + splx(s); + return (error); + } + IFQ_DEQUEUE(&ifp->if_snd, m0); + if (m0 == 0) { + if (tp->tap_flags & TAP_NBIO && ioflag & IO_NDELAY) { + splx(s); + return EWOULDBLOCK; + } + tp->tap_flags |= TAP_RWAIT; + if ((error = tsleep((caddr_t)tp, + (PZERO + 1)|PCATCH, "tapread", 0)) != 0) { + splx(s); + return (error); + } + } + } while (m0 == NULL); + splx(s); + +#if NBPFILTER > 0 + /* feed packet to bpf */ + if (ifp->if_bpf != NULL) + bpf_mtap(ifp->if_bpf, m0); +#endif + + /* xfer packet to user space */ + while (m0 && uio->uio_resid > 0 && error == 0) { + len = min(uio->uio_resid, m0->m_len); + if (len != 0) + error = uiomove(mtod(m0, caddr_t), len, uio); + MFREE(m0, m); + m0 = m; + } + + if (m0) { + TAPDEBUG(("Dropping mbuf\n")); + m_freem(m0); + } + if (error) + ifp->if_ierrors++; + + return error; +} + +/* + * the cdevsw write interface - an atomic write is a packet - or else! + */ +int +tapwrite(dev_t dev, struct uio *uio, int ioflag) +{ + int unit; + struct ifnet *ifp; + struct mbuf *top, **mp, *m; + int error=0, tlen, mlen; + + if ((unit = minor(dev)) >= ntap) + return (ENXIO); + + ifp = &tapctl[unit].tap_if; + TAPDEBUG(("%s: tapwrite\n", ifp->if_xname)); + + if (uio->uio_resid == 0 || uio->uio_resid > TAPMRU) { + TAPDEBUG(("%s: len=%d!\n", ifp->if_xname, uio->uio_resid)); + return EMSGSIZE; + } + 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) { + if (top) + m_freem (top); + ifp->if_ierrors++; + return error; + } + + top->m_pkthdr.len = tlen; + top->m_pkthdr.rcvif = ifp; + /* + * Ethernet bridge is handled in ether_input but bpf isn't + */ +#if NBPFILTER > 0 + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, top); +#endif /* NBPFILTER > 0 */ + + ether_input_mbuf(ifp, top); + ifp->if_ipackets++; /* ibytes are counted in ether_input */ + + return (0); +} + +/* + * tapselect - the select 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. + */ +int +tapselect(dev_t dev, int rw, struct proc *p) +{ + int unit, s; + struct tap_softc *tp; + struct ifnet *ifp; + + if ((unit = minor(dev)) >= ntap) + return (ENXIO); + + tp = &tapctl[unit]; + ifp = &tp->tap_if; + s = splimp(); + TAPDEBUG(("%s: tapselect\n", ifp->if_xname)); + + switch (rw) { + case FREAD: + if (ifp->if_snd.ifq_len > 0) { + splx(s); + TAPDEBUG(("%s: tapselect q=%d\n", ifp->if_xname, + ifp->if_snd.ifq_len)); + return 1; + } + selrecord(curproc, &tp->tap_rsel); + break; + case FWRITE: + splx(s); + return 1; + } + splx(s); + TAPDEBUG(("%s: tapselect waiting\n", ifp->if_xname)); + return 0; +} + +/* Does not currently work */ + +int +tapkqfilter(dev_t dev, struct knote *kn) +{ + return (1); +} + +/* + * Start packet transmission on the interface. + */ +static void +tapifstart(struct ifnet *ifp) +{ + struct tap_softc *tp = ifp->if_softc; + struct mbuf *m; + + TAPDEBUG(("%s: tapifstart q=%d\n", ifp->if_xname, ifp->if_snd.ifq_len)); + + IFQ_POLL(&ifp->if_snd, m); + if (m != NULL) { + ifp->if_opackets++; + if (tp->tap_flags & TAP_RWAIT) { + tp->tap_flags &= ~TAP_RWAIT; + wakeup((caddr_t)tp); + } + if (tp->tap_flags & TAP_ASYNC && tp->tap_pgid) + csignal(tp->tap_pgid, SIGIO, + tp->tap_siguid, tp->tap_sigeuid); + selwakeup(&tp->tap_rsel); + } +}