if_tap.c revision 93593
163670Snsayer/*
263670Snsayer * Copyright (C) 1999-2000 by Maksim Yevmenkin <m_evmenkin@yahoo.com>
363670Snsayer * All rights reserved.
463670Snsayer *
563670Snsayer * Redistribution and use in source and binary forms, with or without
663670Snsayer * modification, are permitted provided that the following conditions
763670Snsayer * are met:
863670Snsayer * 1. Redistributions of source code must retain the above copyright
963670Snsayer *    notice, this list of conditions and the following disclaimer.
1063670Snsayer * 2. Redistributions in binary form must reproduce the above copyright
1163670Snsayer *    notice, this list of conditions and the following disclaimer in the
1263670Snsayer *    documentation and/or other materials provided with the distribution.
1363670Snsayer *
1463670Snsayer * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1563670Snsayer * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1663670Snsayer * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1763670Snsayer * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1863670Snsayer * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1963670Snsayer * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2063670Snsayer * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2163670Snsayer * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2263670Snsayer * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2363670Snsayer * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2463670Snsayer * SUCH DAMAGE.
2563670Snsayer *
2663670Snsayer * BASED ON:
2763670Snsayer * -------------------------------------------------------------------------
2863670Snsayer *
2963670Snsayer * Copyright (c) 1988, Julian Onions <jpo@cs.nott.ac.uk>
3063670Snsayer * Nottingham University 1987.
3163670Snsayer */
3263670Snsayer
3363670Snsayer/*
3463670Snsayer * $FreeBSD: head/sys/net/if_tap.c 93593 2002-04-01 21:31:13Z jhb $
3563803Snsayer * $Id: if_tap.c,v 0.21 2000/07/23 21:46:02 max Exp $
3663670Snsayer */
3763670Snsayer
3863670Snsayer#include "opt_inet.h"
3963670Snsayer
4063670Snsayer#include <sys/param.h>
4163670Snsayer#include <sys/conf.h>
4263670Snsayer#include <sys/filedesc.h>
4363670Snsayer#include <sys/filio.h>
4463670Snsayer#include <sys/kernel.h>
4563670Snsayer#include <sys/malloc.h>
4663670Snsayer#include <sys/mbuf.h>
4763670Snsayer#include <sys/poll.h>
4863670Snsayer#include <sys/proc.h>
4963670Snsayer#include <sys/signalvar.h>
5063670Snsayer#include <sys/socket.h>
5163670Snsayer#include <sys/sockio.h>
5263670Snsayer#include <sys/sysctl.h>
5363670Snsayer#include <sys/systm.h>
5463670Snsayer#include <sys/ttycom.h>
5563670Snsayer#include <sys/uio.h>
5663670Snsayer#include <sys/vnode.h>
5783043Sbrooks#include <machine/bus.h>	/* XXX: Shouldn't really be required! */
5883043Sbrooks#include <sys/rman.h>
5983043Sbrooks#include <sys/queue.h>
6063670Snsayer
6163670Snsayer#include <net/bpf.h>
6263670Snsayer#include <net/ethernet.h>
6363670Snsayer#include <net/if.h>
6463670Snsayer#include <net/if_arp.h>
6563670Snsayer#include <net/route.h>
6663670Snsayer
6763670Snsayer#include <netinet/in.h>
6863670Snsayer
6963670Snsayer#include <net/if_tapvar.h>
7063670Snsayer#include <net/if_tap.h>
7163670Snsayer
7263670Snsayer
7363670Snsayer#define CDEV_NAME	"tap"
7463670Snsayer#define CDEV_MAJOR	149
7563670Snsayer#define TAPDEBUG	if (tapdebug) printf
7663670Snsayer
7763670Snsayer#define TAP		"tap"
7863670Snsayer#define VMNET		"vmnet"
7983043Sbrooks#define TAPMAXUNIT	0x7fff
8083043Sbrooks#define VMNET_DEV_MASK	0x00800000
8183043Sbrooks		/*	0x007f00ff	*/
8263670Snsayer
8363670Snsayer/* module */
8493084Sbdestatic int 		tapmodevent(module_t, int, void *);
8563670Snsayer
8663670Snsayer/* device */
8793084Sbdestatic void		tapclone(void *, char *, int, dev_t *);
8893084Sbdestatic void		tapcreate(dev_t);
8963670Snsayer
9063670Snsayer/* network interface */
9193084Sbdestatic void		tapifstart(struct ifnet *);
9293084Sbdestatic int		tapifioctl(struct ifnet *, u_long, caddr_t);
9393084Sbdestatic void		tapifinit(void *);
9463670Snsayer
9563670Snsayer/* character device */
9663670Snsayerstatic d_open_t		tapopen;
9763670Snsayerstatic d_close_t	tapclose;
9863670Snsayerstatic d_read_t		tapread;
9963670Snsayerstatic d_write_t	tapwrite;
10063670Snsayerstatic d_ioctl_t	tapioctl;
10163670Snsayerstatic d_poll_t		tappoll;
10263670Snsayer
10363670Snsayerstatic struct cdevsw	tap_cdevsw = {
10463670Snsayer	/* open */	tapopen,
10563670Snsayer	/* close */	tapclose,
10663670Snsayer	/* read */	tapread,
10763670Snsayer	/* write */	tapwrite,
10863670Snsayer	/* ioctl */	tapioctl,
10963670Snsayer	/* poll */	tappoll,
11063670Snsayer	/* mmap */	nommap,
11163670Snsayer	/* startegy */	nostrategy,
11263670Snsayer	/* dev name */	CDEV_NAME,
11363670Snsayer	/* dev major */	CDEV_MAJOR,
11463670Snsayer	/* dump */	nodump,
11563670Snsayer	/* psize */	nopsize,
11663670Snsayer	/* flags */	0,
11763670Snsayer};
11863670Snsayer
11983043Sbrooksstatic int			tapdebug = 0;        /* debug flag   */
12083043Sbrooksstatic SLIST_HEAD(, tap_softc)	taphead;             /* first device */
12183043Sbrooksstatic udev_t			tapbasedev = NOUDEV; /* base device  */
12283043Sbrooksstatic struct rman		tapdevunits[2];      /* device units */
12383043Sbrooks#define		tapunits	tapdevunits
12483043Sbrooks#define		vmnetunits	(tapdevunits + 1)
12563670Snsayer
12663670SnsayerMALLOC_DECLARE(M_TAP);
12763670SnsayerMALLOC_DEFINE(M_TAP, CDEV_NAME, "Ethernet tunnel interface");
12863670SnsayerSYSCTL_INT(_debug, OID_AUTO, if_tap_debug, CTLFLAG_RW, &tapdebug, 0, "");
12963670SnsayerDEV_MODULE(if_tap, tapmodevent, NULL);
13063670Snsayer
13163670Snsayer/*
13263670Snsayer * tapmodevent
13363670Snsayer *
13463670Snsayer * module event handler
13563670Snsayer */
13663670Snsayerstatic int
13763670Snsayertapmodevent(mod, type, data)
13863670Snsayer	module_t	 mod;
13963670Snsayer	int		 type;
14063670Snsayer	void		*data;
14163670Snsayer{
14283043Sbrooks	static eventhandler_tag	 eh_tag = NULL;
14383043Sbrooks	struct tap_softc	*tp = NULL;
14483043Sbrooks	struct ifnet		*ifp = NULL;
14583043Sbrooks	int			 error, s;
14663670Snsayer
14763670Snsayer	switch (type) {
14863670Snsayer	case MOD_LOAD:
14983043Sbrooks		/* initialize resources */
15083043Sbrooks		tapunits->rm_type = RMAN_ARRAY;
15183043Sbrooks		tapunits->rm_descr = "open tap units";
15283043Sbrooks		vmnetunits->rm_type = RMAN_ARRAY;
15383043Sbrooks		vmnetunits->rm_descr = "open vmnet units";
15463670Snsayer
15583043Sbrooks		error = rman_init(tapunits);
15683043Sbrooks		if (error != 0)
15783043Sbrooks			goto bail;
15883043Sbrooks
15983043Sbrooks		error = rman_init(vmnetunits);
16083043Sbrooks		if (error != 0)
16183043Sbrooks			goto bail1;
16283043Sbrooks
16383043Sbrooks		error = rman_manage_region(tapunits, 0, TAPMAXUNIT);
16483043Sbrooks		if (error != 0)
16583043Sbrooks			goto bail2;
16683043Sbrooks
16783043Sbrooks		error = rman_manage_region(vmnetunits, 0, TAPMAXUNIT);
16883043Sbrooks		if (error != 0)
16983043Sbrooks			goto bail2;
17083043Sbrooks
17183043Sbrooks		/* intitialize device */
17283043Sbrooks
17383043Sbrooks		SLIST_INIT(&taphead);
17483043Sbrooks
17571602Sphk		eh_tag = EVENTHANDLER_REGISTER(dev_clone, tapclone, 0, 1000);
17683043Sbrooks		if (eh_tag == NULL) {
17783043Sbrooks			error = ENOMEM;
17883043Sbrooks			goto bail2;
17983043Sbrooks		}
18063670Snsayer
18183043Sbrooks		if (!devfs_present) {
18283043Sbrooks			error = cdevsw_add(&tap_cdevsw);
18383043Sbrooks			if (error != 0) {
18483043Sbrooks				EVENTHANDLER_DEREGISTER(dev_clone, eh_tag);
18583043Sbrooks				goto bail2;
18683043Sbrooks			}
18783043Sbrooks		}
18871602Sphk
18983043Sbrooks		return (0);
19083043Sbrooksbail2:
19183043Sbrooks		rman_fini(vmnetunits);
19283043Sbrooksbail1:
19383043Sbrooks		rman_fini(tapunits);
19483043Sbrooksbail:
19583043Sbrooks		return (error);
19663670Snsayer
19783043Sbrooks	case MOD_UNLOAD:
19883043Sbrooks		SLIST_FOREACH(tp, &taphead, tap_next)
19983043Sbrooks			if (tp->tap_unit != NULL)
20083043Sbrooks				return (EBUSY);
20183043Sbrooks
20271602Sphk		EVENTHANDLER_DEREGISTER(dev_clone, eh_tag);
20363670Snsayer
20483043Sbrooks		error = rman_fini(tapunits);
20583043Sbrooks		KASSERT((error == 0), ("Could not fini tap units"));
20683043Sbrooks		error = rman_fini(vmnetunits);
20783043Sbrooks		KASSERT((error == 0), ("Could not fini vmnet units"));
20871602Sphk
20983043Sbrooks		while ((tp = SLIST_FIRST(&taphead)) != NULL) {
21083043Sbrooks			SLIST_REMOVE_HEAD(&taphead, tap_next);
21183043Sbrooks
21283043Sbrooks			ifp = &tp->tap_if;
21383043Sbrooks
21483043Sbrooks			TAPDEBUG("detaching %s%d\n", ifp->if_name,ifp->if_unit);
21583043Sbrooks
21683043Sbrooks			KASSERT(!(tp->tap_flags & TAP_OPEN),
21783043Sbrooks				("%s%d flags is out of sync", ifp->if_name,
21883043Sbrooks				ifp->if_unit));
21983043Sbrooks
22083043Sbrooks			/* XXX makedev check? nah.. not right now :) */
22183043Sbrooks
22263670Snsayer			s = splimp();
22383043Sbrooks			ether_ifdetach(ifp, ETHER_BPF_SUPPORTED);
22463670Snsayer			splx(s);
22563670Snsayer
22683043Sbrooks			FREE(tp, M_TAP);
22783043Sbrooks		}
22863670Snsayer
22983043Sbrooks 		if (tapbasedev != NOUDEV)
23083043Sbrooks			destroy_dev(udev2dev(tapbasedev, 0));
23163670Snsayer
23283043Sbrooks                if (!devfs_present)
23383043Sbrooks                        cdevsw_remove(&tap_cdevsw);
23463670Snsayer
23583043Sbrooks		break;
23663670Snsayer
23763670Snsayer	default:
23863670Snsayer		return (EOPNOTSUPP);
23963670Snsayer	}
24063670Snsayer
24163670Snsayer	return (0);
24263670Snsayer} /* tapmodevent */
24363670Snsayer
24463670Snsayer
24563670Snsayer/*
24671602Sphk * DEVFS handler
24771602Sphk *
24871602Sphk * We need to support two kind of devices - tap and vmnet
24971602Sphk */
25071602Sphkstatic void
25171602Sphktapclone(arg, name, namelen, dev)
25271602Sphk	void	*arg;
25371602Sphk	char	*name;
25471602Sphk	int	 namelen;
25571602Sphk	dev_t	*dev;
25671602Sphk{
25783043Sbrooks	int		 unit, minor = 0 /* XXX avoid warning */ , error;
25883043Sbrooks	char		*device_name = name;
25983043Sbrooks	struct resource	*r = NULL;
26071602Sphk
26171602Sphk	if (*dev != NODEV)
26271602Sphk		return;
26371602Sphk
26483043Sbrooks	if (strcmp(device_name, TAP) == 0) {
26583043Sbrooks		/* get first free tap unit */
26683043Sbrooks		r = rman_reserve_resource(tapunits, 0, TAPMAXUNIT, 1,
26783043Sbrooks			RF_ALLOCATED | RF_ACTIVE, NULL);
26883043Sbrooks		unit = rman_get_start(r);
26983043Sbrooks		minor = unit2minor(unit);
27083043Sbrooks	}
27183043Sbrooks	else if (strcmp(device_name, VMNET) == 0) {
27283043Sbrooks		/* get first free vmnet unit */
27383043Sbrooks		r = rman_reserve_resource(vmnetunits, 0, TAPMAXUNIT, 1,
27483043Sbrooks			RF_ALLOCATED | RF_ACTIVE, NULL);
27583043Sbrooks		unit = rman_get_start(r);
27683043Sbrooks		minor = unit2minor(unit) | VMNET_DEV_MASK;
27783043Sbrooks	}
27871602Sphk
27983043Sbrooks	if (r != NULL) { /* need cloning */
28083043Sbrooks		TAPDEBUG("%s%d is available. minor = %#x\n",
28183043Sbrooks			device_name, unit, minor);
28271602Sphk
28383043Sbrooks		error = rman_release_resource(r);
28483043Sbrooks		KASSERT((error == 0), ("Could not release tap/vmnet unit"));
28583043Sbrooks
28683043Sbrooks		/* check if device for the unit has been created */
28783043Sbrooks		*dev = makedev(CDEV_MAJOR, minor);
28883043Sbrooks		if ((*dev)->si_flags & SI_NAMED) {
28983043Sbrooks			TAPDEBUG("%s%d device exists. minor = %#x\n",
29083043Sbrooks				device_name, unit, minor);
29183043Sbrooks			return; /* device has been created */
29283043Sbrooks		}
29383043Sbrooks	} else { /* try to match name/unit, first try tap then vmnet */
29483043Sbrooks		device_name = TAP;
29583043Sbrooks		if (dev_stdclone(name, NULL, device_name, &unit) != 1) {
29683043Sbrooks			device_name = VMNET;
29783043Sbrooks
29883043Sbrooks			if (dev_stdclone(name, NULL, device_name, &unit) != 1)
29983043Sbrooks				return;
30083043Sbrooks
30183043Sbrooks			minor = unit2minor(unit) | VMNET_DEV_MASK;
30283043Sbrooks		} else
30383043Sbrooks			minor = unit2minor(unit);
30471602Sphk	}
30571602Sphk
30683043Sbrooks	TAPDEBUG("make_dev(%s%d). minor = %#x\n", device_name, unit, minor);
30783043Sbrooks
30871602Sphk	*dev = make_dev(&tap_cdevsw, minor, UID_ROOT, GID_WHEEL, 0600, "%s%d",
30971602Sphk			device_name, unit);
31083043Sbrooks
31183043Sbrooks	if (tapbasedev == NOUDEV)
31283043Sbrooks		tapbasedev = (*dev)->si_udev;
31383043Sbrooks	else {
31483043Sbrooks		(*dev)->si_flags |= SI_CHEAPCLONE;
31583043Sbrooks		dev_depends(udev2dev(tapbasedev, 0), *dev);
31683043Sbrooks	}
31771602Sphk} /* tapclone */
31871602Sphk
31971602Sphk
32071602Sphk/*
32163670Snsayer * tapcreate
32263670Snsayer *
32363670Snsayer * to create interface
32463670Snsayer */
32563670Snsayerstatic void
32663670Snsayertapcreate(dev)
32763670Snsayer	dev_t	dev;
32863670Snsayer{
32963670Snsayer	struct ifnet		*ifp = NULL;
33063670Snsayer	struct tap_softc	*tp = NULL;
33163670Snsayer	unsigned short		 macaddr_hi;
33263803Snsayer	int			 unit, s;
33363670Snsayer	char			*name = NULL;
33463670Snsayer
33563670Snsayer	/* allocate driver storage and create device */
33669781Sdwmalone	MALLOC(tp, struct tap_softc *, sizeof(*tp), M_TAP, M_WAITOK | M_ZERO);
33783043Sbrooks	SLIST_INSERT_HEAD(&taphead, tp, tap_next);
33863670Snsayer
33983043Sbrooks	unit = dev2unit(dev) & TAPMAXUNIT;
34083043Sbrooks
34163670Snsayer	/* select device: tap or vmnet */
34263670Snsayer	if (minor(dev) & VMNET_DEV_MASK) {
34363670Snsayer		name = VMNET;
34463803Snsayer		tp->tap_flags |= TAP_VMNET;
34583043Sbrooks	} else
34663670Snsayer		name = TAP;
34763670Snsayer
34883043Sbrooks	TAPDEBUG("tapcreate(%s%d). minor = %#x\n", name, unit, minor(dev));
34983043Sbrooks
35083043Sbrooks	if (!(dev->si_flags & SI_NAMED))
35183043Sbrooks		dev = make_dev(&tap_cdevsw, minor(dev), UID_ROOT, GID_WHEEL,
35263670Snsayer						0600, "%s%d", name, unit);
35363670Snsayer
35463670Snsayer	/* generate fake MAC address: 00 bd xx xx xx unit_no */
35563670Snsayer	macaddr_hi = htons(0x00bd);
35663670Snsayer	bcopy(&macaddr_hi, &tp->arpcom.ac_enaddr[0], sizeof(short));
35763670Snsayer	bcopy(&ticks, &tp->arpcom.ac_enaddr[2], sizeof(long));
35863670Snsayer	tp->arpcom.ac_enaddr[5] = (u_char)unit;
35963670Snsayer
36063670Snsayer	/* fill the rest and attach interface */
36163670Snsayer	ifp = &tp->tap_if;
36263670Snsayer	ifp->if_softc = tp;
36363670Snsayer	ifp->if_unit = unit;
36463670Snsayer	ifp->if_name = name;
36563670Snsayer	ifp->if_init = tapifinit;
36663670Snsayer	ifp->if_output = ether_output;
36763670Snsayer	ifp->if_start = tapifstart;
36863670Snsayer	ifp->if_ioctl = tapifioctl;
36963670Snsayer	ifp->if_mtu = ETHERMTU;
37063670Snsayer	ifp->if_flags = (IFF_BROADCAST|IFF_SIMPLEX|IFF_MULTICAST);
37163670Snsayer	ifp->if_snd.ifq_maxlen = ifqmaxlen;
37263670Snsayer
37383043Sbrooks	dev->si_drv1 = tp;
37483043Sbrooks
37563803Snsayer	s = splimp();
37683043Sbrooks	ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
37763803Snsayer	splx(s);
37863670Snsayer
37963803Snsayer	tp->tap_flags |= TAP_INITED;
38063803Snsayer
38183043Sbrooks	TAPDEBUG("interface %s%d is created. minor = %#x\n",
38283043Sbrooks		ifp->if_name, ifp->if_unit, minor(dev));
38363670Snsayer} /* tapcreate */
38463670Snsayer
38563670Snsayer
38663670Snsayer/*
38763670Snsayer * tapopen
38863670Snsayer *
38963670Snsayer * to open tunnel. must be superuser
39063670Snsayer */
39163670Snsayerstatic int
39283366Sjuliantapopen(dev, flag, mode, td)
39363670Snsayer	dev_t		 dev;
39463670Snsayer	int		 flag;
39563670Snsayer	int		 mode;
39683366Sjulian	struct thread	*td;
39763670Snsayer{
39863670Snsayer	struct tap_softc	*tp = NULL;
39983043Sbrooks	int			 unit, error;
40083043Sbrooks	struct resource		*r = NULL;
40163670Snsayer
40293593Sjhb	if ((error = suser(td)) != 0)
40363670Snsayer		return (error);
40463670Snsayer
40583043Sbrooks	unit = dev2unit(dev) & TAPMAXUNIT;
40683043Sbrooks
40783043Sbrooks	if (minor(dev) & VMNET_DEV_MASK)
40883043Sbrooks		r = rman_reserve_resource(vmnetunits, unit, unit, 1,
40983043Sbrooks			RF_ALLOCATED | RF_ACTIVE, NULL);
41083043Sbrooks	else
41183043Sbrooks		r = rman_reserve_resource(tapunits, unit, unit, 1,
41283043Sbrooks			RF_ALLOCATED | RF_ACTIVE, NULL);
41383043Sbrooks
41483043Sbrooks	if (r == NULL)
41583043Sbrooks		return (EBUSY);
41683043Sbrooks
41783043Sbrooks	dev->si_flags &= ~SI_CHEAPCLONE;
41883043Sbrooks
41963670Snsayer	tp = dev->si_drv1;
42063670Snsayer	if (tp == NULL) {
42163670Snsayer		tapcreate(dev);
42263670Snsayer		tp = dev->si_drv1;
42363670Snsayer	}
42463670Snsayer
42583043Sbrooks	KASSERT(!(tp->tap_flags & TAP_OPEN),
42683043Sbrooks		("%s%d flags is out of sync", tp->tap_if.if_name, unit));
42763670Snsayer
42863861Snsayer	bcopy(tp->arpcom.ac_enaddr, tp->ether_addr, sizeof(tp->ether_addr));
42963861Snsayer
43083043Sbrooks	tp->tap_unit = r;
43183366Sjulian	tp->tap_pid = td->td_proc->p_pid;
43263670Snsayer	tp->tap_flags |= TAP_OPEN;
43363670Snsayer
43483043Sbrooks	TAPDEBUG("%s%d is open. minor = %#x\n",
43583043Sbrooks		tp->tap_if.if_name, unit, minor(dev));
43663670Snsayer
43763670Snsayer	return (0);
43863670Snsayer} /* tapopen */
43963670Snsayer
44063670Snsayer
44163670Snsayer/*
44263670Snsayer * tapclose
44363670Snsayer *
44463670Snsayer * close the device - mark i/f down & delete routing info
44563670Snsayer */
44663670Snsayerstatic int
44783366Sjuliantapclose(dev, foo, bar, td)
44863670Snsayer	dev_t		 dev;
44963670Snsayer	int		 foo;
45063670Snsayer	int		 bar;
45183366Sjulian	struct thread	*td;
45263670Snsayer{
45383043Sbrooks	int			 s, error;
45463670Snsayer	struct tap_softc	*tp = dev->si_drv1;
45563670Snsayer	struct ifnet		*ifp = &tp->tap_if;
45663670Snsayer
45787914Sjlemon	KASSERT((tp->tap_unit != NULL),
45883043Sbrooks		("%s%d is not open", ifp->if_name, ifp->if_unit));
45983043Sbrooks
46063670Snsayer	/* junk all pending output */
46183043Sbrooks	IF_DRAIN(&ifp->if_snd);
46263670Snsayer
46363803Snsayer	/*
46463803Snsayer	 * do not bring the interface down, and do not anything with
46563803Snsayer	 * interface, if we are in VMnet mode. just close the device.
46663803Snsayer	 */
46763803Snsayer
46863803Snsayer	if (((tp->tap_flags & TAP_VMNET) == 0) && (ifp->if_flags & IFF_UP)) {
46963670Snsayer		s = splimp();
47063670Snsayer		if_down(ifp);
47163670Snsayer		if (ifp->if_flags & IFF_RUNNING) {
47263670Snsayer			/* find internet addresses and delete routes */
47363670Snsayer			struct ifaddr	*ifa = NULL;
47463670Snsayer
47563803Snsayer			TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
47663670Snsayer				if (ifa->ifa_addr->sa_family == AF_INET) {
47763670Snsayer					rtinit(ifa, (int)RTM_DELETE, 0);
47863670Snsayer
47963670Snsayer					/* remove address from interface */
48063670Snsayer					bzero(ifa->ifa_addr,
48163670Snsayer						   sizeof(*(ifa->ifa_addr)));
48263670Snsayer					bzero(ifa->ifa_dstaddr,
48363670Snsayer						   sizeof(*(ifa->ifa_dstaddr)));
48463670Snsayer					bzero(ifa->ifa_netmask,
48563670Snsayer						   sizeof(*(ifa->ifa_netmask)));
48663670Snsayer				}
48763670Snsayer			}
48863670Snsayer
48963670Snsayer			ifp->if_flags &= ~IFF_RUNNING;
49063670Snsayer		}
49163670Snsayer		splx(s);
49263670Snsayer	}
49363670Snsayer
49463670Snsayer	funsetown(tp->tap_sigio);
49563670Snsayer	selwakeup(&tp->tap_rsel);
49663670Snsayer
49763670Snsayer	tp->tap_flags &= ~TAP_OPEN;
49863670Snsayer	tp->tap_pid = 0;
49983043Sbrooks	error = rman_release_resource(tp->tap_unit);
50083043Sbrooks	KASSERT((error == 0),
50183043Sbrooks		("%s%d could not release unit", ifp->if_name, ifp->if_unit));
50283043Sbrooks	tp->tap_unit = NULL;
50363670Snsayer
50483043Sbrooks	TAPDEBUG("%s%d is closed. minor = %#x\n",
50583043Sbrooks		ifp->if_name, ifp->if_unit, minor(dev));
50663670Snsayer
50763670Snsayer	return (0);
50863670Snsayer} /* tapclose */
50963670Snsayer
51063670Snsayer
51163670Snsayer/*
51263670Snsayer * tapifinit
51363670Snsayer *
51463670Snsayer * network interface initialization function
51563670Snsayer */
51663670Snsayerstatic void
51763670Snsayertapifinit(xtp)
51863670Snsayer	void	*xtp;
51963670Snsayer{
52063670Snsayer	struct tap_softc	*tp = (struct tap_softc *)xtp;
52163670Snsayer	struct ifnet		*ifp = &tp->tap_if;
52263670Snsayer
52383043Sbrooks	TAPDEBUG("initializing %s%d\n", ifp->if_name, ifp->if_unit);
52463670Snsayer
52563670Snsayer	ifp->if_flags |= IFF_RUNNING;
52663670Snsayer	ifp->if_flags &= ~IFF_OACTIVE;
52763670Snsayer
52863670Snsayer	/* attempt to start output */
52963670Snsayer	tapifstart(ifp);
53063670Snsayer} /* tapifinit */
53163670Snsayer
53263670Snsayer
53363670Snsayer/*
53463670Snsayer * tapifioctl
53563670Snsayer *
53663670Snsayer * Process an ioctl request on network interface
53763670Snsayer */
53863670Snsayerint
53963670Snsayertapifioctl(ifp, cmd, data)
54063670Snsayer	struct ifnet	*ifp;
54163670Snsayer	u_long		 cmd;
54263670Snsayer	caddr_t		 data;
54363670Snsayer{
54463670Snsayer	struct tap_softc 	*tp = (struct tap_softc *)(ifp->if_softc);
54563670Snsayer	struct ifstat		*ifs = NULL;
54663670Snsayer	int			 s, dummy;
54763670Snsayer
54863670Snsayer	switch (cmd) {
54963670Snsayer		case SIOCSIFADDR:
55063670Snsayer		case SIOCGIFADDR:
55163670Snsayer		case SIOCSIFMTU:
55263670Snsayer			s = splimp();
55363670Snsayer			dummy = ether_ioctl(ifp, cmd, data);
55463670Snsayer			splx(s);
55563670Snsayer			return (dummy);
55663670Snsayer
55763670Snsayer		case SIOCSIFFLAGS: /* XXX -- just like vmnet does */
55863670Snsayer		case SIOCADDMULTI:
55963670Snsayer		case SIOCDELMULTI:
56083043Sbrooks			break;
56163670Snsayer
56263670Snsayer		case SIOCGIFSTATUS:
56363670Snsayer			s = splimp();
56463670Snsayer			ifs = (struct ifstat *)data;
56563670Snsayer			dummy = strlen(ifs->ascii);
56663670Snsayer			if (tp->tap_pid != 0 && dummy < sizeof(ifs->ascii))
56763670Snsayer				snprintf(ifs->ascii + dummy,
56863670Snsayer					sizeof(ifs->ascii) - dummy,
56963670Snsayer					"\tOpened by PID %d\n", tp->tap_pid);
57063670Snsayer			splx(s);
57183043Sbrooks			break;
57263670Snsayer
57363670Snsayer		default:
57463670Snsayer			return (EINVAL);
57563670Snsayer	}
57663670Snsayer
57763670Snsayer	return (0);
57863670Snsayer} /* tapifioctl */
57963670Snsayer
58063670Snsayer
58163670Snsayer/*
58263670Snsayer * tapifstart
58363670Snsayer *
58463670Snsayer * queue packets from higher level ready to put out
58563670Snsayer */
58663670Snsayerstatic void
58763670Snsayertapifstart(ifp)
58863670Snsayer	struct ifnet	*ifp;
58963670Snsayer{
59063670Snsayer	struct tap_softc	*tp = ifp->if_softc;
59163670Snsayer	int			 s;
59263670Snsayer
59383043Sbrooks	TAPDEBUG("%s%d starting\n", ifp->if_name, ifp->if_unit);
59463670Snsayer
59563803Snsayer	/*
59663803Snsayer	 * do not junk pending output if we are in VMnet mode.
59763803Snsayer	 * XXX: can this do any harm because of queue overflow?
59863803Snsayer	 */
59963803Snsayer
60063803Snsayer	if (((tp->tap_flags & TAP_VMNET) == 0) &&
60163803Snsayer	    ((tp->tap_flags & TAP_READY) != TAP_READY)) {
60263670Snsayer		struct mbuf	*m = NULL;
60363670Snsayer
60483043Sbrooks		TAPDEBUG("%s%d not ready, tap_flags = 0x%x\n", ifp->if_name,
60583043Sbrooks			ifp->if_unit, tp->tap_flags);
60663670Snsayer
60763670Snsayer		s = splimp();
60863670Snsayer		do {
60963670Snsayer			IF_DEQUEUE(&ifp->if_snd, m);
61063670Snsayer			if (m != NULL)
61163670Snsayer				m_freem(m);
61263670Snsayer			ifp->if_oerrors ++;
61363670Snsayer		} while (m != NULL);
61463670Snsayer		splx(s);
61563670Snsayer
61663670Snsayer		return;
61763670Snsayer	}
61863670Snsayer
61963670Snsayer	s = splimp();
62063670Snsayer	ifp->if_flags |= IFF_OACTIVE;
62163670Snsayer
62263670Snsayer	if (ifp->if_snd.ifq_len != 0) {
62363670Snsayer		if (tp->tap_flags & TAP_RWAIT) {
62463670Snsayer			tp->tap_flags &= ~TAP_RWAIT;
62563670Snsayer			wakeup((caddr_t)tp);
62663670Snsayer		}
62763670Snsayer
62863670Snsayer		if ((tp->tap_flags & TAP_ASYNC) && (tp->tap_sigio != NULL))
62963670Snsayer			pgsigio(tp->tap_sigio, SIGIO, 0);
63063670Snsayer
63163670Snsayer		selwakeup(&tp->tap_rsel);
63263670Snsayer		ifp->if_opackets ++; /* obytes are counted in ether_output */
63363670Snsayer	}
63463670Snsayer
63563670Snsayer	ifp->if_flags &= ~IFF_OACTIVE;
63663670Snsayer	splx(s);
63763670Snsayer} /* tapifstart */
63863670Snsayer
63963670Snsayer
64063670Snsayer/*
64163670Snsayer * tapioctl
64263670Snsayer *
64363670Snsayer * the cdevsw interface is now pretty minimal
64463670Snsayer */
64563670Snsayerstatic int
64683366Sjuliantapioctl(dev, cmd, data, flag, td)
64763670Snsayer	dev_t		 dev;
64863670Snsayer	u_long		 cmd;
64963670Snsayer	caddr_t		 data;
65063670Snsayer	int		 flag;
65183366Sjulian	struct thread	*td;
65263670Snsayer{
65363670Snsayer	struct tap_softc	*tp = dev->si_drv1;
65463670Snsayer	struct ifnet		*ifp = &tp->tap_if;
65563670Snsayer 	struct tapinfo		*tapp = NULL;
65663670Snsayer	int			 s;
65783043Sbrooks	short			 f;
65863670Snsayer
65963670Snsayer	switch (cmd) {
66063670Snsayer 		case TAPSIFINFO:
66163670Snsayer			s = splimp();
66263670Snsayer 		        tapp = (struct tapinfo *)data;
66363670Snsayer 			ifp->if_mtu = tapp->mtu;
66463670Snsayer 			ifp->if_type = tapp->type;
66563670Snsayer 			ifp->if_baudrate = tapp->baudrate;
66663670Snsayer			splx(s);
66783043Sbrooks 			break;
66863670Snsayer
66963670Snsayer	 	case TAPGIFINFO:
67063670Snsayer 			tapp = (struct tapinfo *)data;
67163670Snsayer 			tapp->mtu = ifp->if_mtu;
67263670Snsayer 			tapp->type = ifp->if_type;
67363670Snsayer 			tapp->baudrate = ifp->if_baudrate;
67483043Sbrooks 			break;
67563670Snsayer
67663670Snsayer		case TAPSDEBUG:
67763670Snsayer			tapdebug = *(int *)data;
67883043Sbrooks			break;
67963670Snsayer
68063670Snsayer		case TAPGDEBUG:
68163670Snsayer			*(int *)data = tapdebug;
68283043Sbrooks			break;
68363670Snsayer
68463670Snsayer		case FIONBIO:
68583043Sbrooks			break;
68663670Snsayer
68763670Snsayer		case FIOASYNC:
68863803Snsayer			s = splimp();
68963670Snsayer			if (*(int *)data)
69063670Snsayer				tp->tap_flags |= TAP_ASYNC;
69163670Snsayer			else
69263670Snsayer				tp->tap_flags &= ~TAP_ASYNC;
69363803Snsayer			splx(s);
69483043Sbrooks			break;
69563670Snsayer
69663670Snsayer		case FIONREAD:
69763670Snsayer			s = splimp();
69863670Snsayer			if (ifp->if_snd.ifq_head) {
69963670Snsayer				struct mbuf	*mb = ifp->if_snd.ifq_head;
70063670Snsayer
70163803Snsayer				for(*(int *)data = 0;mb != NULL;mb = mb->m_next)
70263670Snsayer					*(int *)data += mb->m_len;
70383043Sbrooks			} else
70463670Snsayer				*(int *)data = 0;
70563670Snsayer			splx(s);
70683043Sbrooks			break;
70763670Snsayer
70863670Snsayer		case FIOSETOWN:
70963670Snsayer			return (fsetown(*(int *)data, &tp->tap_sigio));
71063670Snsayer
71163670Snsayer		case FIOGETOWN:
71263670Snsayer			*(int *)data = fgetown(tp->tap_sigio);
71363670Snsayer			return (0);
71463670Snsayer
71563670Snsayer		/* this is deprecated, FIOSETOWN should be used instead */
71663670Snsayer		case TIOCSPGRP:
71763670Snsayer			return (fsetown(-(*(int *)data), &tp->tap_sigio));
71863670Snsayer
71963670Snsayer		/* this is deprecated, FIOGETOWN should be used instead */
72063670Snsayer		case TIOCGPGRP:
72163670Snsayer			*(int *)data = -fgetown(tp->tap_sigio);
72263670Snsayer			return (0);
72363670Snsayer
72463670Snsayer		/* VMware/VMnet port ioctl's */
72563670Snsayer
72663670Snsayer		case SIOCGIFFLAGS:	/* get ifnet flags */
72763670Snsayer			bcopy(&ifp->if_flags, data, sizeof(ifp->if_flags));
72883043Sbrooks			break;
72963670Snsayer
73083043Sbrooks		case VMIO_SIOCSIFFLAGS: /* VMware/VMnet SIOCSIFFLAGS */
73183043Sbrooks			f = *(short *)data;
73263670Snsayer			f &= 0x0fff;
73363670Snsayer			f &= ~IFF_CANTCHANGE;
73463670Snsayer			f |= IFF_UP;
73563670Snsayer
73663670Snsayer			s = splimp();
73763670Snsayer			ifp->if_flags = f | (ifp->if_flags & IFF_CANTCHANGE);
73863670Snsayer			splx(s);
73983043Sbrooks			break;
74063670Snsayer
74163861Snsayer		case OSIOCGIFADDR:	/* get MAC address of the remote side */
74263670Snsayer		case SIOCGIFADDR:
74363861Snsayer			bcopy(tp->ether_addr, data, sizeof(tp->ether_addr));
74483043Sbrooks			break;
74563670Snsayer
74663861Snsayer		case SIOCSIFADDR:	/* set MAC address of the remote side */
74763861Snsayer			bcopy(data, tp->ether_addr, sizeof(tp->ether_addr));
74883043Sbrooks			break;
74963670Snsayer
75063670Snsayer		default:
75163670Snsayer			return (ENOTTY);
75263670Snsayer	}
75363670Snsayer	return (0);
75463670Snsayer} /* tapioctl */
75563670Snsayer
75663670Snsayer
75763670Snsayer/*
75863670Snsayer * tapread
75963670Snsayer *
76063670Snsayer * the cdevsw read interface - reads a packet at a time, or at
76163670Snsayer * least as much of a packet as can be read
76263670Snsayer */
76363670Snsayerstatic int
76463670Snsayertapread(dev, uio, flag)
76563670Snsayer	dev_t		 dev;
76663670Snsayer	struct uio	*uio;
76763670Snsayer	int		 flag;
76863670Snsayer{
76963670Snsayer	struct tap_softc	*tp = dev->si_drv1;
77063670Snsayer	struct ifnet		*ifp = &tp->tap_if;
77190227Sdillon	struct mbuf		*m = NULL;
77263670Snsayer	int			 error = 0, len, s;
77363670Snsayer
77483043Sbrooks	TAPDEBUG("%s%d reading, minor = %#x\n",
77583043Sbrooks		ifp->if_name, ifp->if_unit, minor(dev));
77663670Snsayer
77763670Snsayer	if ((tp->tap_flags & TAP_READY) != TAP_READY) {
77863803Snsayer		TAPDEBUG("%s%d not ready. minor = %#x, tap_flags = 0x%x\n",
77983043Sbrooks			ifp->if_name, ifp->if_unit, minor(dev), tp->tap_flags);
78063803Snsayer
78163670Snsayer		return (EHOSTDOWN);
78263670Snsayer	}
78363670Snsayer
78463670Snsayer	tp->tap_flags &= ~TAP_RWAIT;
78563670Snsayer
78663670Snsayer	/* sleep until we get a packet */
78763670Snsayer	do {
78863670Snsayer		s = splimp();
78990227Sdillon		IF_DEQUEUE(&ifp->if_snd, m);
79063670Snsayer		splx(s);
79163670Snsayer
79290227Sdillon		if (m == NULL) {
79363670Snsayer			if (flag & IO_NDELAY)
79463670Snsayer				return (EWOULDBLOCK);
79563670Snsayer
79663670Snsayer			tp->tap_flags |= TAP_RWAIT;
79763670Snsayer			error = tsleep((caddr_t)tp,PCATCH|(PZERO+1),"taprd",0);
79863670Snsayer			if (error)
79963670Snsayer				return (error);
80063670Snsayer		}
80190227Sdillon	} while (m == NULL);
80263670Snsayer
80363670Snsayer	/* feed packet to bpf */
80463670Snsayer	if (ifp->if_bpf != NULL)
80590227Sdillon		bpf_mtap(ifp, m);
80663670Snsayer
80763670Snsayer	/* xfer packet to user space */
80890227Sdillon	while ((m != NULL) && (uio->uio_resid > 0) && (error == 0)) {
80990227Sdillon		len = min(uio->uio_resid, m->m_len);
81063670Snsayer		if (len == 0)
81163670Snsayer			break;
81263670Snsayer
81390227Sdillon		error = uiomove(mtod(m, caddr_t), len, uio);
81490227Sdillon		m = m_free(m);
81563670Snsayer	}
81663670Snsayer
81790227Sdillon	if (m != NULL) {
81883043Sbrooks		TAPDEBUG("%s%d dropping mbuf, minor = %#x\n", ifp->if_name,
81983043Sbrooks			ifp->if_unit, minor(dev));
82090227Sdillon		m_freem(m);
82163670Snsayer	}
82263670Snsayer
82363670Snsayer	return (error);
82463670Snsayer} /* tapread */
82563670Snsayer
82663670Snsayer
82763670Snsayer/*
82863670Snsayer * tapwrite
82963670Snsayer *
83063670Snsayer * the cdevsw write interface - an atomic write is a packet - or else!
83163670Snsayer */
83263670Snsayerstatic int
83363670Snsayertapwrite(dev, uio, flag)
83463670Snsayer	dev_t		 dev;
83563670Snsayer	struct uio	*uio;
83663670Snsayer	int		 flag;
83763670Snsayer{
83863670Snsayer	struct tap_softc	*tp = dev->si_drv1;
83963670Snsayer	struct ifnet		*ifp = &tp->tap_if;
84063670Snsayer	struct mbuf		*top = NULL, **mp = NULL, *m = NULL;
84163670Snsayer	struct ether_header	*eh = NULL;
84263670Snsayer	int		 	 error = 0, tlen, mlen;
84363670Snsayer
84483043Sbrooks	TAPDEBUG("%s%d writting, minor = %#x\n",
84583043Sbrooks		ifp->if_name, ifp->if_unit, minor(dev));
84663670Snsayer
84763670Snsayer	if (uio->uio_resid == 0)
84863670Snsayer		return (0);
84963670Snsayer
85063670Snsayer	if ((uio->uio_resid < 0) || (uio->uio_resid > TAPMRU)) {
85163803Snsayer		TAPDEBUG("%s%d invalid packet len = %d, minor = %#x\n",
85283043Sbrooks			ifp->if_name, ifp->if_unit, uio->uio_resid, minor(dev));
85363803Snsayer
85463670Snsayer		return (EIO);
85563670Snsayer	}
85663670Snsayer	tlen = uio->uio_resid;
85763670Snsayer
85863670Snsayer	/* get a header mbuf */
85963670Snsayer	MGETHDR(m, M_DONTWAIT, MT_DATA);
86063670Snsayer	if (m == NULL)
86163670Snsayer		return (ENOBUFS);
86263670Snsayer	mlen = MHLEN;
86363670Snsayer
86463670Snsayer	top = 0;
86563670Snsayer	mp = &top;
86663670Snsayer	while ((error == 0) && (uio->uio_resid > 0)) {
86763670Snsayer		m->m_len = min(mlen, uio->uio_resid);
86863670Snsayer		error = uiomove(mtod(m, caddr_t), m->m_len, uio);
86963670Snsayer		*mp = m;
87063670Snsayer		mp = &m->m_next;
87163670Snsayer		if (uio->uio_resid > 0) {
87263670Snsayer			MGET(m, M_DONTWAIT, MT_DATA);
87363803Snsayer			if (m == NULL) {
87463670Snsayer				error = ENOBUFS;
87563670Snsayer				break;
87663670Snsayer			}
87763670Snsayer			mlen = MLEN;
87863670Snsayer		}
87963670Snsayer	}
88063670Snsayer	if (error) {
88163670Snsayer		ifp->if_ierrors ++;
88263670Snsayer		if (top)
88363670Snsayer			m_freem(top);
88463670Snsayer		return (error);
88563670Snsayer	}
88663670Snsayer
88763670Snsayer	top->m_pkthdr.len = tlen;
88863670Snsayer	top->m_pkthdr.rcvif = ifp;
88963670Snsayer
89063670Snsayer	/*
89163670Snsayer	 * Ethernet bridge and bpf are handled in ether_input
89263670Snsayer	 *
89363670Snsayer	 * adjust mbuf and give packet to the ether_input
89463670Snsayer	 */
89563670Snsayer
89663670Snsayer	eh = mtod(top, struct ether_header *);
89763670Snsayer	m_adj(top, sizeof(struct ether_header));
89863670Snsayer	ether_input(ifp, eh, top);
89963670Snsayer	ifp->if_ipackets ++; /* ibytes are counted in ether_input */
90063670Snsayer
90163670Snsayer	return (0);
90263670Snsayer} /* tapwrite */
90363670Snsayer
90463670Snsayer
90563670Snsayer/*
90663670Snsayer * tappoll
90763670Snsayer *
90863670Snsayer * the poll interface, this is only useful on reads
90963670Snsayer * really. the write detect always returns true, write never blocks
91063670Snsayer * anyway, it either accepts the packet or drops it
91163670Snsayer */
91263670Snsayerstatic int
91383366Sjuliantappoll(dev, events, td)
91463670Snsayer	dev_t		 dev;
91563670Snsayer	int		 events;
91683366Sjulian	struct thread	*td;
91763670Snsayer{
91863670Snsayer	struct tap_softc	*tp = dev->si_drv1;
91963670Snsayer	struct ifnet		*ifp = &tp->tap_if;
92063670Snsayer	int		 	 s, revents = 0;
92163670Snsayer
92283043Sbrooks	TAPDEBUG("%s%d polling, minor = %#x\n",
92383043Sbrooks		ifp->if_name, ifp->if_unit, minor(dev));
92463670Snsayer
92563670Snsayer	s = splimp();
92663670Snsayer	if (events & (POLLIN | POLLRDNORM)) {
92763670Snsayer		if (ifp->if_snd.ifq_len > 0) {
92863803Snsayer			TAPDEBUG("%s%d have data in queue. len = %d, " \
92963803Snsayer				"minor = %#x\n", ifp->if_name, ifp->if_unit,
93083043Sbrooks				ifp->if_snd.ifq_len, minor(dev));
93163803Snsayer
93263670Snsayer			revents |= (events & (POLLIN | POLLRDNORM));
93383043Sbrooks		} else {
93463803Snsayer			TAPDEBUG("%s%d waiting for data, minor = %#x\n",
93583043Sbrooks				ifp->if_name, ifp->if_unit, minor(dev));
93663803Snsayer
93783805Sjhb			selrecord(td, &tp->tap_rsel);
93863670Snsayer		}
93963670Snsayer	}
94063670Snsayer
94163670Snsayer	if (events & (POLLOUT | POLLWRNORM))
94263670Snsayer		revents |= (events & (POLLOUT | POLLWRNORM));
94363670Snsayer
94463670Snsayer	splx(s);
94563670Snsayer	return (revents);
94663670Snsayer} /* tappoll */
947