summaryrefslogtreecommitdiffstats
path: root/sys/net/if_tun.c
diff options
context:
space:
mode:
authorbrian <brian@FreeBSD.org>2001-04-03 01:22:15 +0000
committerbrian <brian@FreeBSD.org>2001-04-03 01:22:15 +0000
commit093bd1b31909de4259bfe764416b111bc1d8e051 (patch)
tree6fb69d1b0584c6e50bc011d45ac0e34c8f45dca7 /sys/net/if_tun.c
parentf4dcb13a7f6be1117a3fb2f647475b4e0b28b95b (diff)
downloadFreeBSD-src-093bd1b31909de4259bfe764416b111bc1d8e051.zip
FreeBSD-src-093bd1b31909de4259bfe764416b111bc1d8e051.tar.gz
Allow MOD_UNLOADs of if_tun, and handle event handler registration
failures in MOD_LOAD. Dodge duplicate make_dev() calls by (ab)using dev->si_drv2 to remember if we created the device node via a dev_clone callback before the d_open call.
Diffstat (limited to 'sys/net/if_tun.c')
-rw-r--r--sys/net/if_tun.c52
1 files changed, 42 insertions, 10 deletions
diff --git a/sys/net/if_tun.c b/sys/net/if_tun.c
index e9b840e..af94023 100644
--- a/sys/net/if_tun.c
+++ b/sys/net/if_tun.c
@@ -57,6 +57,7 @@ static void tuncreate __P((dev_t dev));
#define TUNDEBUG if (tundebug) printf
static int tundebug = 0;
+static struct tun_softc *tunhead = NULL;
SYSCTL_INT(_debug, OID_AUTO, if_tun_debug, CTLFLAG_RW, &tundebug, 0, "");
static int tunoutput __P((struct ifnet *, struct mbuf *, struct sockaddr *,
@@ -106,20 +107,45 @@ tun_clone(arg, name, namelen, dev)
return;
*dev = make_dev(&tun_cdevsw, unit2minor(u),
UID_ROOT, GID_WHEEL, 0600, "tun%d", u);
-
+ (*dev)->si_drv2 = (void *)1; /* Mark it as make_dev()'d */
}
static int
tun_modevent(module_t mod, int type, void *data)
-{
+{
+ static eventhandler_tag tag;
+ struct tun_softc *tp;
+ dev_t dev;
+ int err;
+
switch (type) {
case MOD_LOAD:
- EVENTHANDLER_REGISTER(dev_clone, tun_clone, 0, 1000);
- cdevsw_add(&tun_cdevsw);
+ tag = EVENTHANDLER_REGISTER(dev_clone, tun_clone, 0, 1000);
+ if (tag == NULL)
+ return (ENOMEM);
+ err = cdevsw_add(&tun_cdevsw);
+ if (err != 0) {
+ EVENTHANDLER_DEREGISTER(dev_clone, tag);
+ return (err);
+ }
break;
case MOD_UNLOAD:
- printf("if_tun module unload - not possible for this module type\n");
- return EINVAL;
+ while (tunhead != NULL) {
+ if (tunhead->tun_flags & TUN_OPEN)
+ return (EBUSY);
+ tp = tunhead;
+ dev = makedev(tun_cdevsw.d_maj,
+ unit2minor(tp->tun_if.if_unit));
+ KASSERT(dev->si_drv1 == tp, ("Bad makedev result"));
+ tunhead = tp->next;
+ bpfdetach(&tp->tun_if);
+ if_detach(&tp->tun_if);
+ KASSERT(dev->si_drv2 != NULL, ("Bad si_drv2 value"));
+ destroy_dev(dev);
+ FREE(tp, M_TUN);
+ }
+ EVENTHANDLER_DEREGISTER(dev_clone, tag);
+ break;
}
return 0;
}
@@ -154,11 +180,16 @@ tuncreate(dev)
struct tun_softc *sc;
struct ifnet *ifp;
- dev = make_dev(&tun_cdevsw, minor(dev),
- UID_UUCP, GID_DIALER, 0600, "tun%d", dev2unit(dev));
+ if (dev->si_drv2 == NULL) {
+ dev = make_dev(&tun_cdevsw, minor(dev),
+ UID_UUCP, GID_DIALER, 0600, "tun%d", dev2unit(dev));
+ dev->si_drv2 = (void *)1;
+ }
MALLOC(sc, struct tun_softc *, sizeof(*sc), M_TUN, M_WAITOK | M_ZERO);
sc->tun_flags = TUN_INITED;
+ sc->next = tunhead;
+ tunhead = sc;
ifp = &sc->tun_if;
ifp->if_unit = dev2unit(dev);
@@ -177,8 +208,9 @@ tuncreate(dev)
}
/*
- * tunnel open - must be superuser & the device must be
- * configured in
+ * tunnel open. We assume that any tun_clone() call is immediately
+ * followed by a call to tunopen() - otherwise nothing will ever
+ * destroy_dev() the specinfo.
*/
static int
tunopen(dev, flag, mode, p)
OpenPOWER on IntegriCloud