root/sys/net/tap/if_tap.c
/*
 * Copyright (C) 1999-2000 by Maksim Yevmenkin <m_evmenkin@yahoo.com>
 * 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 <jpo@cs.nott.ac.uk>
 * Nottingham University 1987.
 */

/*
 * $FreeBSD: src/sys/net/if_tap.c,v 1.3.2.3 2002/04/14 21:41:48 luigi Exp $
 * $Id: if_tap.c,v 0.21 2000/07/23 21:46:02 max Exp $
 */

#include "opt_inet.h"

#include <sys/param.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/filedesc.h>
#include <sys/filio.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/proc.h>
#include <sys/caps.h>
#include <sys/signalvar.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
#include <sys/ttycom.h>
#include <sys/uio.h>
#include <sys/vnode.h>
#include <sys/serialize.h>
#include <sys/thread2.h>
#include <sys/mplock2.h>
#include <sys/devfs.h>
#include <sys/queue.h>

#include <net/bpf.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/if_arp.h>
#include <net/if_clone.h>
#include <net/if_media.h>
#include <net/ifq_var.h>
#include <net/route.h>

#include <netinet/in.h>

#include "if_tapvar.h"
#include "if_tap.h"

#define TAP_IFFLAGS     (IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST)

#define TAP             "tap"
#define TAPDEBUG        if (tapdebug) if_printf

/* module */
static int              tapmodevent(module_t, int, void *);

/* device */
static struct tap_softc *tapcreate(cdev_t, int);
static void             tapdestroy(struct tap_softc *);

/* clone */
static int              tap_clone_create(struct if_clone *, int,
                                         caddr_t, caddr_t);
static int              tap_clone_destroy(struct ifnet *);

/* network interface */
static void             tapifinit(void *);
static void             tapifstart(struct ifnet *, struct ifaltq_subque *);
static int              tapifioctl(struct ifnet *, u_long, caddr_t,
                                   struct ucred *);
static void             tapifstop(struct tap_softc *, int);
static void             tapifflags(struct tap_softc *);

/* character device */
static d_open_t         tapopen;
static d_clone_t        tapclone;
static d_close_t        tapclose;
static d_read_t         tapread;
static d_write_t        tapwrite;
static d_ioctl_t        tapioctl;
static d_kqfilter_t     tapkqfilter;

static struct dev_ops   tap_ops = {
        { TAP, 0, 0 },
        .d_open =       tapopen,
        .d_close =      tapclose,
        .d_read =       tapread,
        .d_write =      tapwrite,
        .d_ioctl =      tapioctl,
        .d_kqfilter =   tapkqfilter
};

/* kqueue support */
static void             tap_filter_detach(struct knote *);
static int              tap_filter_read(struct knote *, long);
static int              tap_filter_write(struct knote *, long);

static struct filterops tapread_filtops = {
        FILTEROP_ISFD,
        NULL,
        tap_filter_detach,
        tap_filter_read
};
static struct filterops tapwrite_filtops = {
        FILTEROP_ISFD,
        NULL,
        tap_filter_detach,
        tap_filter_write
};

static int              tapdebug = 0;           /* debug flag */
static int              taprefcnt = 0;          /* module ref. counter */
static int              tapuopen = 0;           /* allow user open() */
static int              tapuponopen = 0;        /* IFF_UP when opened */

static MALLOC_DEFINE(M_TAP, TAP, "Ethernet tunnel interface");

static DEVFS_DEFINE_CLONE_BITMAP(tap);

struct if_clone tap_cloner = IF_CLONE_INITIALIZER(
        TAP, tap_clone_create, tap_clone_destroy, 0, IF_MAXUNIT);

static SLIST_HEAD(,tap_softc) tap_listhead =
        SLIST_HEAD_INITIALIZER(&tap_listhead);

SYSCTL_INT(_debug, OID_AUTO, if_tap_debug, CTLFLAG_RW, &tapdebug, 0, "");
SYSCTL_DECL(_net_link);
SYSCTL_NODE(_net_link, OID_AUTO, tap, CTLFLAG_RW, 0,
            "Ethernet tunnel software network interface");
SYSCTL_INT(_net_link_tap, OID_AUTO, user_open, CTLFLAG_RW, &tapuopen, 0,
           "Allow user to open /dev/tap (based on node permissions)");
SYSCTL_INT(_net_link_tap, OID_AUTO, up_on_open, CTLFLAG_RW, &tapuponopen, 0,
           "Bring interface up when /dev/tap is opened");
SYSCTL_INT(_net_link_tap, OID_AUTO, debug, CTLFLAG_RW, &tapdebug, 0, "");
SYSCTL_INT(_net_link_tap, OID_AUTO, refcnt, CTLFLAG_RD, &taprefcnt, 0,
           "Number of opened devices");

DEV_MODULE(if_tap, tapmodevent, NULL);

/*
 * tapmodevent
 *
 * module event handler
 */
static int
tapmodevent(module_t mod, int type, void *data)
{
        static cdev_t dev = NULL;
        struct tap_softc *sc, *sc_tmp;

        switch (type) {
        case MOD_LOAD:
                dev = make_autoclone_dev(&tap_ops, &DEVFS_CLONE_BITMAP(tap),
                                         tapclone, UID_ROOT, GID_WHEEL,
                                         0600, TAP);
                SLIST_INIT(&tap_listhead);
                if_clone_attach(&tap_cloner);
                break;

        case MOD_UNLOAD:
                if (taprefcnt > 0)
                        return (EBUSY);

                if_clone_detach(&tap_cloner);

                SLIST_FOREACH_MUTABLE(sc, &tap_listhead, tap_link, sc_tmp)
                        tapdestroy(sc);

                dev_ops_remove_all(&tap_ops);
                destroy_autoclone_dev(dev, &DEVFS_CLONE_BITMAP(tap));
                break;

        default:
                return (EOPNOTSUPP);
        }

        return (0);
}


/*
 * Create or clone an interface
 */
static struct tap_softc *
tapcreate(cdev_t dev, int flags)
{
        struct tap_softc *sc;
        struct ifnet *ifp;
        uint8_t ether_addr[ETHER_ADDR_LEN];
        int unit = minor(dev);

        sc = kmalloc(sizeof(*sc), M_TAP, M_WAITOK | M_ZERO);
        dev->si_drv1 = sc;
        sc->tap_dev = dev;
        sc->tap_flags |= flags;

        reference_dev(dev); /* device association */

        /* generate fake MAC address: 00 bd xx xx xx unit_no */
        ether_addr[0] = 0x00;
        ether_addr[1] = 0xbd;
        bcopy(&ticks, &ether_addr[2], 3);
        ether_addr[5] = (u_char)unit;

        /* fill the rest and attach interface */
        ifp = &sc->tap_if;
        if_initname(ifp, TAP, unit);
        ifp->if_type = IFT_ETHER;
        ifp->if_init = tapifinit;
        ifp->if_start = tapifstart;
        ifp->if_ioctl = tapifioctl;
        ifp->if_mtu = ETHERMTU;
        ifp->if_flags = TAP_IFFLAGS;
        ifp->if_softc = sc;
        ifq_set_maxlen(&ifp->if_snd, ifqmaxlen);
        ifq_set_ready(&ifp->if_snd);

        ether_ifattach(ifp, ether_addr, NULL);

        sc->tap_flags |= TAP_INITED;
        sc->tap_devq.ifq_maxlen = ifqmaxlen;

        SLIST_INSERT_HEAD(&tap_listhead, sc, tap_link);

        TAPDEBUG(ifp, "created, minor = %#x, flags = 0x%x\n",
                 unit, sc->tap_flags);
        return (sc);
}

static struct tap_softc *
tapfind(int unit)
{
        struct tap_softc *sc;

        SLIST_FOREACH(sc, &tap_listhead, tap_link) {
                if (minor(sc->tap_dev) == unit)
                        return (sc);
        }
        return (NULL);
}

/*
 * Create a new tap instance via ifconfig.
 */
static int
tap_clone_create(struct if_clone *ifc __unused, int unit,
                 caddr_t params __unused, caddr_t data)
{
        struct tap_softc *sc;
        cdev_t dev = (cdev_t)data;
        int flags;

        if (tapfind(unit) != NULL)
                return (EEXIST);

        if (dev == NULL) {
                if (!devfs_clone_bitmap_chk(&DEVFS_CLONE_BITMAP(tap), unit)) {
                        devfs_clone_bitmap_set(&DEVFS_CLONE_BITMAP(tap), unit);
                        dev = make_dev(&tap_ops, unit, UID_ROOT, GID_WHEEL,
                                       0600, "%s%d", TAP, unit);
                } else {
                        dev = devfs_find_device_by_name("%s%d", TAP, unit);
                        if (dev == NULL)
                                return (ENOENT);
                }
                flags = TAP_MANUALMAKE;
        } else {
                flags = 0;
        }

        if ((sc = tapcreate(dev, flags)) == NULL)
                return (ENOMEM);

        sc->tap_flags |= TAP_CLONE;

        TAPDEBUG(&sc->tap_if, "clone created, minor = %#x, flags = 0x%x\n",
                 minor(sc->tap_dev), sc->tap_flags);

        return (0);
}

/*
 * Open the tap device.
 */
static int
tapopen(struct dev_open_args *ap)
{
        cdev_t dev = NULL;
        struct tap_softc *sc = NULL;
        struct ifnet *ifp = NULL;
        int error;

        if (tapuopen == 0 &&
            (error = caps_priv_check(ap->a_cred, SYSCAP_RESTRICTEDROOT)) != 0)
        {
                return (error);
        }

        get_mplock();

        dev = ap->a_head.a_dev;
        sc = dev->si_drv1;
        if (sc == NULL && (sc = tapcreate(dev, 0)) == NULL) {
                rel_mplock();
                return (ENOMEM);
        }
        if (sc->tap_flags & TAP_OPEN) {
                rel_mplock();
                return (EBUSY);
        }

        ifp = &sc->tap_if;
        bcopy(sc->arpcom.ac_enaddr, sc->ether_addr, sizeof(sc->ether_addr));

        if (curthread->td_proc)
                fsetown(curthread->td_proc->p_pid, &sc->tap_sigtd);
        sc->tap_flags |= TAP_OPEN;
        taprefcnt++;

        if (tapuponopen && (ifp->if_flags & IFF_UP) == 0) {
                crit_enter();
                if_up(ifp);
                crit_exit();

                ifnet_serialize_all(ifp);
                tapifflags(sc);
                ifnet_deserialize_all(ifp);

                sc->tap_flags |= TAP_CLOSEDOWN;
        }

        TAPDEBUG(ifp, "opened, minor = %#x. Module refcnt = %d\n",
                 minor(dev), taprefcnt);

        rel_mplock();
        return (0);
}

static int
tapclone(struct dev_clone_args *ap)
{
        char ifname[IFNAMSIZ];
        int unit;

        unit = devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(tap), 0);
        ksnprintf(ifname, IFNAMSIZ, "%s%d", TAP, unit);
        ap->a_dev = make_only_dev(&tap_ops, unit, UID_ROOT, GID_WHEEL,
                                  0600, "%s", ifname);

        /*
         * Use the if_clone framework to create cloned device/interface,
         * so the two clone methods (autoclone device /dev/tap; ifconfig
         * clone) are consistent and can be mix used.
         *
         * Need to pass the cdev_t because the device created by
         * 'make_only_dev()' doesn't appear in '/dev' yet so that it can't
         * be found by 'devfs_find_device_by_name()' in 'tap_clone_create()'.
         */
        return (if_clone_create(ifname, IFNAMSIZ, NULL, (caddr_t)ap->a_dev));
}

/*
 * Close the tap device: bring the interface down & delete routing info.
 */
static int
tapclose(struct dev_close_args *ap)
{
        cdev_t dev = ap->a_head.a_dev;
        struct tap_softc *sc = dev->si_drv1;
        struct ifnet *ifp;
        int unit = minor(dev);
        int clear_flags = 0;
        char ifname[IFNAMSIZ];

        KASSERT(sc != NULL,
                ("try closing the already destroyed %s%d", TAP, unit));
        ifp = &sc->tap_if;

        get_mplock();

        /* Junk all pending output */
        ifq_purge_all(&ifp->if_snd);

        /*
         * If the interface is not cloned, we always bring it down.
         *
         * If the interface is cloned, then we bring it down during
         * closing only if it was brought up during opening.
         */
        if ((sc->tap_flags & TAP_CLONE) == 0 ||
            (sc->tap_flags & TAP_CLOSEDOWN)) {
                if (ifp->if_flags & IFF_UP)
                        if_down(ifp);
                clear_flags = 1;
        }
        ifnet_serialize_all(ifp);
        tapifstop(sc, clear_flags);
        ifnet_deserialize_all(ifp);

        if ((sc->tap_flags & TAP_MANUALMAKE) == 0) {
                if_purgeaddrs_nolink(ifp);

                EVENTHANDLER_INVOKE(ifnet_detach_event, ifp);

                /* Announce the departure of the interface. */
                rt_ifannouncemsg(ifp, IFAN_DEPARTURE);
        }

        funsetown(&sc->tap_sigio);
        sc->tap_sigio = NULL;
        KNOTE(&sc->tap_rkq.ki_note, 0);

        sc->tap_flags &= ~TAP_OPEN;
        funsetown(&sc->tap_sigtd);
        sc->tap_sigtd = NULL;

        taprefcnt--;
        if (taprefcnt < 0) {
                taprefcnt = 0;
                if_printf(ifp, ". Module refcnt = %d is out of sync! "
                          "Force refcnt to be 0.\n", taprefcnt);
        }

        TAPDEBUG(ifp, "closed, minor = %#x. Module refcnt = %d\n",
                 unit, taprefcnt);

        /* Only auto-destroy if the interface was not manually created. */
        if ((sc->tap_flags & TAP_MANUALMAKE) == 0) {
                ksnprintf(ifname, IFNAMSIZ, "%s%d", TAP, unit);
                if_clone_destroy(ifname);
        }

        rel_mplock();
        return (0);
}


/*
 * Destroy a tap instance: the interface and the associated device.
 */
static void
tapdestroy(struct tap_softc *sc)
{
        struct ifnet *ifp = &sc->tap_if;
        cdev_t dev = sc->tap_dev;
        int unit = minor(dev);

        TAPDEBUG(ifp, "destroyed, minor = %#x. Module refcnt = %d\n",
                 unit, taprefcnt);

        ifnet_serialize_all(ifp);
        tapifstop(sc, 1);
        ifnet_deserialize_all(ifp);
        ether_ifdetach(ifp);

        sc->tap_dev = NULL;
        dev->si_drv1 = NULL;
        release_dev(dev); /* device disassociation */

        /* Also destroy the cloned device */
        destroy_dev(dev);
        devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(tap), unit);

        SLIST_REMOVE(&tap_listhead, sc, tap_softc, tap_link);
        kfree(sc, M_TAP);
}


/*
 * Destroy a tap instance that is created via ifconfig.
 */
static int
tap_clone_destroy(struct ifnet *ifp)
{
        struct tap_softc *sc = ifp->if_softc;

        if (sc->tap_flags & TAP_OPEN)
                return (EBUSY);
        if ((sc->tap_flags & TAP_CLONE) == 0)
                return (EINVAL);

        TAPDEBUG(ifp, "clone destroyed, minor = %#x, flags = 0x%x\n",
                 minor(sc->tap_dev), sc->tap_flags);
        tapdestroy(sc);

        return (0);
}


/*
 * tapifinit
 *
 * Network interface initialization function (called with if serializer held)
 *
 * MPSAFE
 */
static void
tapifinit(void *xtp)
{
        struct tap_softc *sc = xtp;
        struct ifnet *ifp = &sc->tap_if;
        struct ifaltq_subque *ifsq = ifq_get_subq_default(&ifp->if_snd);

        TAPDEBUG(ifp, "initializing, minor = %#x, flags = 0x%x\n",
                 minor(sc->tap_dev), sc->tap_flags);

        ASSERT_IFNET_SERIALIZED_ALL(ifp);

        tapifstop(sc, 1);

        ifp->if_flags |= IFF_RUNNING;
        ifsq_clr_oactive(ifsq);

        /* attempt to start output */
        tapifstart(ifp, ifsq);
}

static void
tapifflags(struct tap_softc *sc)
{
        struct ifnet *ifp = &sc->tap_if;

        ASSERT_IFNET_SERIALIZED_ALL(ifp);

        if (ifp->if_flags & IFF_UP) {
                if ((ifp->if_flags & IFF_RUNNING) == 0)
                        tapifinit(sc);
        } else {
                tapifstop(sc, 1);
        }
}

/*
 * tapifioctl
 *
 * Process an ioctl request on network interface (called with if serializer
 * held).
 *
 * MPSAFE
 */
static int
tapifioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr)
{
        struct tap_softc *sc = ifp->if_softc;
        struct ifreq *ifr = NULL;
        struct ifstat *ifs = NULL;
        struct ifmediareq *ifmr = NULL;
        int error = 0;
        int dummy;

        switch (cmd) {
        case SIOCADDMULTI:
        case SIOCDELMULTI:
                break;

        case SIOCSIFMTU:
                ifr = (struct ifreq *)data;
                ifp->if_mtu = ifr->ifr_mtu;
                TAPDEBUG(ifp, "mtu set\n");
                break;

        case SIOCSIFADDR:
        case SIOCGIFADDR:
                error = ether_ioctl(ifp, cmd, data);
                break;

        case SIOCSIFFLAGS:
                tapifflags(sc);
                break;

        case SIOCGIFMEDIA:
                /*
                 * The bridge code needs this when running the
                 * spanning tree protocol.
                 */
                ifmr = (struct ifmediareq *)data;
                dummy = ifmr->ifm_count;
                ifmr->ifm_count = 1;
                ifmr->ifm_status = IFM_AVALID;
                ifmr->ifm_active = IFM_ETHER;
                if (sc->tap_flags & TAP_OPEN)
                        ifmr->ifm_status |= IFM_ACTIVE;
                ifmr->ifm_current = ifmr->ifm_active;
                if (dummy >= 1) {
                        int media = IFM_ETHER;
                        error = copyout(&media, ifmr->ifm_ulist, sizeof(int));
                }
                break;

        case SIOCGIFSTATUS:
                ifs = (struct ifstat *)data;
                dummy = strlen(ifs->ascii);
                if ((sc->tap_flags & TAP_OPEN) && dummy < sizeof(ifs->ascii)) {
                        if (sc->tap_sigtd && sc->tap_sigtd->sio_proc) {
                                ksnprintf(ifs->ascii + dummy,
                                    sizeof(ifs->ascii) - dummy,
                                    "\tOpened by pid %d\n",
                                    (int)sc->tap_sigtd->sio_proc->p_pid);
                        } else {
                                ksnprintf(ifs->ascii + dummy,
                                    sizeof(ifs->ascii) - dummy,
                                    "\tOpened by <unknown>\n");
                        }
                }
                break;

        default:
                error = EINVAL;
                break;
        }

        return (error);
}

/*
 * tapifstart
 *
 * Queue packets from higher level ready to put out (called with if serializer
 * held)
 *
 * MPSAFE
 */
static void
tapifstart(struct ifnet *ifp, struct ifaltq_subque *ifsq)
{
        struct tap_softc *sc = ifp->if_softc;
        struct ifqueue *ifq;
        struct mbuf *m;
        int has_data = 0;

        ASSERT_ALTQ_SQ_DEFAULT(ifp, ifsq);
        TAPDEBUG(ifp, "starting, minor = %#x\n", minor(sc->tap_dev));

        if ((sc->tap_flags & TAP_READY) != TAP_READY) {
                TAPDEBUG(ifp, "not ready, minor = %#x, flags = 0x%x\n",
                         minor(sc->tap_dev), sc->tap_flags);
                ifsq_purge(ifsq);
                return;
        }

        ifsq_set_oactive(ifsq);

        ifq = &sc->tap_devq;
        while ((m = ifsq_dequeue(ifsq)) != NULL) {
                if (IF_QFULL(ifq)) {
                        IF_DROP(ifq);
                        IFNET_STAT_INC(ifp, oerrors, 1);
                        m_freem(m);
                } else {
                        IF_ENQUEUE(ifq, m);
                        IFNET_STAT_INC(ifp, opackets, 1);
                        has_data = 1;
                }
        }

        if (has_data) {
                if (sc->tap_flags & TAP_RWAIT) {
                        sc->tap_flags &= ~TAP_RWAIT;
                        wakeup((caddr_t)sc);
                }

                KNOTE(&sc->tap_rkq.ki_note, 0);

                if ((sc->tap_flags & TAP_ASYNC) && (sc->tap_sigio != NULL)) {
                        get_mplock();
                        pgsigio(sc->tap_sigio, SIGIO, 0);
                        rel_mplock();
                }
        }

        ifsq_clr_oactive(ifsq);
}

static void
tapifstop(struct tap_softc *sc, int clear_flags)
{
        struct ifnet *ifp = &sc->tap_if;

        ASSERT_IFNET_SERIALIZED_ALL(ifp);
        IF_DRAIN(&sc->tap_devq);
        sc->tap_flags &= ~TAP_CLOSEDOWN;
        if (clear_flags) {
                ifp->if_flags &= ~IFF_RUNNING;
                ifsq_clr_oactive(ifq_get_subq_default(&ifp->if_snd));
        }
}

/*
 * tapioctl
 *
 * The ops interface is now pretty minimal.  Called via fileops with nothing
 * held.
 *
 * MPSAFE
 */
static int
tapioctl(struct dev_ioctl_args *ap)
{
        cdev_t dev = ap->a_head.a_dev;
        caddr_t data = ap->a_data;
        struct tap_softc *sc = dev->si_drv1;
        struct ifnet *ifp = &sc->tap_if;
        struct ifreq *ifr;
        struct tapinfo *tapp = NULL;
        struct mbuf *mb;
        int error;

        ifnet_serialize_all(ifp);
        error = 0;

        switch (ap->a_cmd) {
        case TAPSIFINFO:
                tapp = (struct tapinfo *)data;
                if (ifp->if_type != tapp->type)
                        return (EPROTOTYPE);
                ifp->if_mtu = tapp->mtu;
                ifp->if_baudrate = tapp->baudrate;
                break;

        case TAPGIFINFO:
                tapp = (struct tapinfo *)data;
                tapp->mtu = ifp->if_mtu;
                tapp->type = ifp->if_type;
                tapp->baudrate = ifp->if_baudrate;
                break;

        case TAPGIFNAME:
                ifr = (struct ifreq *)data;
                strlcpy(ifr->ifr_name, ifp->if_xname, IFNAMSIZ);
                break;

        case TAPSDEBUG:
                tapdebug = *(int *)data;
                break;

        case TAPGDEBUG:
                *(int *)data = tapdebug;
                break;

        case FIOASYNC:
                if (*(int *)data)
                        sc->tap_flags |= TAP_ASYNC;
                else
                        sc->tap_flags &= ~TAP_ASYNC;
                break;

        case FIONREAD:
                /* Take a look at devq first */
                IF_POLL(&sc->tap_devq, mb);
                if (mb != NULL) {
                        *(int *)data = 0;
                        for(; mb != NULL; mb = mb->m_next)
                                *(int *)data += mb->m_len;
                } else {
                        *(int *)data = ifsq_poll_pktlen(
                            ifq_get_subq_default(&ifp->if_snd));
                }
                break;

        case FIOSETOWN:
                error = fsetown(*(int *)data, &sc->tap_sigio);
                if (error == 0)
                        error = fsetown(*(int *)data, &sc->tap_sigtd);
                break;

        case FIOGETOWN:
                *(int *)data = fgetown(&sc->tap_sigio);
                break;

        /* this is deprecated, FIOSETOWN should be used instead */
        case TIOCSPGRP:
                error = fsetown(-(*(int *)data), &sc->tap_sigio);
                break;

        /* this is deprecated, FIOGETOWN should be used instead */
        case TIOCGPGRP:
                *(int *)data = -fgetown(&sc->tap_sigio);
                break;

        /*
         * Support basic control of the network interface via the device file.
         * e.g., vke(4) currently uses the 'SIOCGIFADDR' ioctl.
         */

        case SIOCGIFFLAGS:
                bcopy(&ifp->if_flags, data, sizeof(ifp->if_flags));
                break;

        case SIOCGIFADDR:
                bcopy(sc->ether_addr, data, sizeof(sc->ether_addr));
                break;

        case SIOCSIFADDR:
                bcopy(data, sc->ether_addr, sizeof(sc->ether_addr));
                break;

        default:
                error = ENOTTY;
                break;
        }

        ifnet_deserialize_all(ifp);
        return (error);
}


/*
 * tapread
 *
 * The ops read interface - reads a packet at a time, or at
 * least as much of a packet as can be read.
 *
 * Called from the fileops interface with nothing held.
 *
 * MPSAFE
 */
static int
tapread(struct dev_read_args *ap)
{
        cdev_t dev = ap->a_head.a_dev;
        struct uio *uio = ap->a_uio;
        struct tap_softc *sc = dev->si_drv1;
        struct ifnet *ifp = &sc->tap_if;
        struct mbuf *m0 = NULL;
        int error = 0, len;

        TAPDEBUG(ifp, "reading, minor = %#x\n", minor(dev));

        if ((sc->tap_flags & TAP_READY) != TAP_READY) {
                TAPDEBUG(ifp, "not ready, minor = %#x, flags = 0x%x\n",
                         minor(dev), sc->tap_flags);

                return (EHOSTDOWN);
        }

        sc->tap_flags &= ~TAP_RWAIT;

        /* sleep until we get a packet */
        do {
                ifnet_serialize_all(ifp);
                IF_DEQUEUE(&sc->tap_devq, m0);
                if (m0 == NULL) {
                        if (ap->a_ioflag & IO_NDELAY) {
                                ifnet_deserialize_all(ifp);
                                return (EWOULDBLOCK);
                        }
                        sc->tap_flags |= TAP_RWAIT;
                        tsleep_interlock(sc, PCATCH);
                        ifnet_deserialize_all(ifp);
                        error = tsleep(sc, PCATCH | PINTERLOCKED, "taprd", 0);
                        if (error)
                                return (error);
                } else {
                        ifnet_deserialize_all(ifp);
                }
        } while (m0 == NULL);

        BPF_MTAP(ifp, m0);

        /* xfer packet to user space */
        while ((m0 != NULL) && (uio->uio_resid > 0) && (error == 0)) {
                len = (int)szmin(uio->uio_resid, m0->m_len);
                if (len == 0)
                        break;

                error = uiomove(mtod(m0, caddr_t), (size_t)len, uio);
                m0 = m_free(m0);
        }

        if (m0 != NULL) {
                TAPDEBUG(ifp, "dropping mbuf, minor = %#x\n", minor(dev));
                m_freem(m0);
        }

        return (error);
}

/*
 * tapwrite
 *
 * The ops write interface - an atomic write is a packet - or else!
 *
 * Called from the fileops interface with nothing held.
 *
 * MPSAFE
 */
static int
tapwrite(struct dev_write_args *ap)
{
        cdev_t dev = ap->a_head.a_dev;
        struct uio *uio = ap->a_uio;
        struct tap_softc *sc = dev->si_drv1;
        struct ifnet *ifp = &sc->tap_if;
        struct mbuf *top = NULL, **mp = NULL, *m = NULL;
        int error;
        size_t tlen, mlen;

        TAPDEBUG(ifp, "writing, minor = %#x\n", minor(dev));

        if ((sc->tap_flags & TAP_READY) != TAP_READY) {
                TAPDEBUG(ifp, "not ready, minor = %#x, flags = 0x%x\n",
                         minor(dev), sc->tap_flags);
                return (EHOSTDOWN);
        }

        if (uio->uio_resid == 0)
                return (0);

        if (uio->uio_resid > TAPMRU) {
                TAPDEBUG(ifp, "invalid packet len = %zu, minor = %#x\n",
                         uio->uio_resid, minor(dev));

                return (EIO);
        }
        tlen = uio->uio_resid;

        /* get a header mbuf */
        MGETHDR(m, M_WAITOK, MT_DATA);
        mlen = MHLEN;

        top = NULL;
        mp = &top;
        error = 0;

        while (error == 0 && uio->uio_resid > 0) {
                m->m_len = (int)szmin(mlen, uio->uio_resid);
                error = uiomove(mtod(m, caddr_t), (size_t)m->m_len, uio);
                *mp = m;
                mp = &m->m_next;
                if (uio->uio_resid > 0) {
                        MGET(m, M_WAITOK, MT_DATA);
                        mlen = MLEN;
                }
        }
        if (error) {
                IFNET_STAT_INC(ifp, ierrors, 1);
                if (top)
                        m_freem(top);
                return (error);
        }

        top->m_pkthdr.len = (int)tlen;
        top->m_pkthdr.rcvif = ifp;

        /*
         * Ethernet bridge and bpf are handled in ether_input
         *
         * adjust mbuf and give packet to the ether_input
         */
        ifnet_serialize_all(ifp);
        ifp->if_input(ifp, top, NULL, -1);
        IFNET_STAT_INC(ifp, ipackets, 1); /* ibytes are counted in ether_input */
        ifnet_deserialize_all(ifp);

        return (0);
}


/*
 * tapkqfilter - called from the fileops interface with nothing held
 *
 * MPSAFE
 */
static int
tapkqfilter(struct dev_kqfilter_args *ap)
{
        cdev_t dev = ap->a_head.a_dev;
        struct knote *kn = ap->a_kn;
        struct tap_softc *sc = dev->si_drv1;
        struct klist *list;

        list = &sc->tap_rkq.ki_note;
        ap->a_result =0;

        switch (kn->kn_filter) {
        case EVFILT_READ:
                kn->kn_fop = &tapread_filtops;
                kn->kn_hook = (void *)sc;
                break;
        case EVFILT_WRITE:
                kn->kn_fop = &tapwrite_filtops;
                kn->kn_hook = (void *)sc;
                break;
        default:
                ap->a_result = EOPNOTSUPP;
                return (0);
        }

        knote_insert(list, kn);
        return (0);
}

static int
tap_filter_read(struct knote *kn, long hint)
{
        struct tap_softc *sc = (struct tap_softc *)kn->kn_hook;
        int ready = 0;

        if (!IF_QEMPTY(&sc->tap_devq)) {        /* XXX serializer */
                if ((kn->kn_sfflags & NOTE_HUPONLY) == 0)
                        ready = 1;
        }
        return ready;
}

static int
tap_filter_write(struct knote *kn, long hint)
{
        /* Always ready for a write */
        return (1);
}

static void
tap_filter_detach(struct knote *kn)
{
        struct tap_softc *sc = (struct tap_softc *)kn->kn_hook;

        knote_remove(&sc->tap_rkq.ki_note, kn);
}