diff options
author | bms <bms@FreeBSD.org> | 2007-02-04 16:32:46 +0000 |
---|---|---|
committer | bms <bms@FreeBSD.org> | 2007-02-04 16:32:46 +0000 |
commit | 77c2e113090f513f7876ee5e02f61ab600b319cf (patch) | |
tree | 2b8098cffbe0971d05ca5c30201a51b937a1e7f9 /sys/net/if_tap.c | |
parent | 4e9c971afca066f77117383709ba6fa8ee6a6f12 (diff) | |
download | FreeBSD-src-77c2e113090f513f7876ee5e02f61ab600b319cf.zip FreeBSD-src-77c2e113090f513f7876ee5e02f61ab600b319cf.tar.gz |
Implement ifnet cloning for tun(4)/tap(4).
Make devfs cloning a sysctl/tunable which defaults to on.
If devfs cloning is enabled, only the super-user may create
tun(4)/tap(4)/vmnet(4) instances. Devfs cloning is still enabled by
default; it may be disabled from the loader or via sysctl with
"net.link.tap.devfs_cloning" and "net.link.tun.devfs_cloning".
Disabling its use affects potentially all tun(4)/tap(4) consumers
including OpenSSH, OpenVPN and VMware.
PR: 105228 (potentially also 90413, 105570)
Submitted by: Landon Fuller
Tested by: Andrej Tobola
Approved by: core (rwatson)
MFC after: 4 weeks
Diffstat (limited to 'sys/net/if_tap.c')
-rw-r--r-- | sys/net/if_tap.c | 158 |
1 files changed, 126 insertions, 32 deletions
diff --git a/sys/net/if_tap.c b/sys/net/if_tap.c index 653d618..9fa10b5 100644 --- a/sys/net/if_tap.c +++ b/sys/net/if_tap.c @@ -62,6 +62,7 @@ #include <net/bpf.h> #include <net/ethernet.h> #include <net/if.h> +#include <net/if_clone.h> #include <net/if_dl.h> #include <net/route.h> #include <net/if_types.h> @@ -93,6 +94,14 @@ static void tapifstart(struct ifnet *); static int tapifioctl(struct ifnet *, u_long, caddr_t); static void tapifinit(void *); +static int tap_clone_create(struct if_clone *, int, caddr_t); +static void tap_clone_destroy(struct ifnet *); +static int vmnet_clone_create(struct if_clone *, int, caddr_t); +static void vmnet_clone_destroy(struct ifnet *); + +IFC_SIMPLE_DECLARE(tap, 0); +IFC_SIMPLE_DECLARE(vmnet, 0); + /* character device */ static d_open_t tapopen; static d_close_t tapclose; @@ -142,6 +151,7 @@ static struct cdevsw tap_cdevsw = { static struct mtx tapmtx; static int tapdebug = 0; /* debug flag */ static int tapuopen = 0; /* allow user open() */ +static int tapdclone = 1; /* enable devfs cloning */ static SLIST_HEAD(, tap_softc) taphead; /* first device */ static struct clonedevs *tapclones; @@ -154,10 +164,87 @@ 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, devfs_cloning, CTLFLAG_RW, &tapdclone, 0, + "Enably legacy devfs interface creation"); SYSCTL_INT(_net_link_tap, OID_AUTO, debug, CTLFLAG_RW, &tapdebug, 0, ""); +TUNABLE_INT("net.link.tap.devfs_cloning", &tapdclone); + DEV_MODULE(if_tap, tapmodevent, NULL); +static int +tap_clone_create(struct if_clone *ifc, int unit, caddr_t params) +{ + struct cdev *dev; + int i; + int extra; + + if (strcmp(ifc->ifc_name, VMNET) == 0) + extra = VMNET_DEV_MASK; + else + extra = 0; + + /* find any existing device, or allocate new unit number */ + i = clone_create(&tapclones, &tap_cdevsw, &unit, &dev, extra); + if (i) { + dev = make_dev(&tap_cdevsw, unit2minor(unit | extra), + UID_ROOT, GID_WHEEL, 0600, "%s%d", ifc->ifc_name, unit); + if (dev != NULL) { + dev_ref(dev); + dev->si_flags |= SI_CHEAPCLONE; + } + } + + tapcreate(dev); + return (0); +} + +/* vmnet devices are tap devices in disguise */ +static int +vmnet_clone_create(struct if_clone *ifc, int unit, caddr_t params) +{ + return tap_clone_create(ifc, unit, params); +} + +static void +tap_destroy(struct tap_softc *tp) +{ + struct ifnet *ifp = tp->tap_ifp; + int s; + + /* Unlocked read. */ + KASSERT(!(tp->tap_flags & TAP_OPEN), + ("%s flags is out of sync", ifp->if_xname)); + + knlist_destroy(&tp->tap_rsel.si_note); + destroy_dev(tp->tap_dev); + s = splimp(); + ether_ifdetach(ifp); + if_free_type(ifp, IFT_ETHER); + splx(s); + + mtx_destroy(&tp->tap_mtx); + free(tp, M_TAP); +} + +static void +tap_clone_destroy(struct ifnet *ifp) +{ + struct tap_softc *tp = ifp->if_softc; + + mtx_lock(&tapmtx); + SLIST_REMOVE(&taphead, tp, tap_softc, tap_next); + mtx_unlock(&tapmtx); + tap_destroy(tp); +} + +/* vmnet devices are tap devices in disguise */ +static void +vmnet_clone_destroy(struct ifnet *ifp) +{ + tap_clone_destroy(ifp); +} + /* * tapmodevent * @@ -169,7 +256,6 @@ tapmodevent(module_t mod, int type, void *data) static eventhandler_tag eh_tag = NULL; struct tap_softc *tp = NULL; struct ifnet *ifp = NULL; - int s; switch (type) { case MOD_LOAD: @@ -186,6 +272,8 @@ tapmodevent(module_t mod, int type, void *data) mtx_destroy(&tapmtx); return (ENOMEM); } + if_clone_attach(&tap_cloner); + if_clone_attach(&vmnet_cloner); return (0); case MOD_UNLOAD: @@ -207,6 +295,8 @@ tapmodevent(module_t mod, int type, void *data) mtx_unlock(&tapmtx); EVENTHANDLER_DEREGISTER(dev_clone, eh_tag); + if_clone_detach(&tap_cloner); + if_clone_detach(&vmnet_cloner); mtx_lock(&tapmtx); while ((tp = SLIST_FIRST(&taphead)) != NULL) { @@ -217,19 +307,7 @@ tapmodevent(module_t mod, int type, void *data) TAPDEBUG("detaching %s\n", ifp->if_xname); - /* Unlocked read. */ - KASSERT(!(tp->tap_flags & TAP_OPEN), - ("%s flags is out of sync", ifp->if_xname)); - - knlist_destroy(&tp->tap_rsel.si_note); - destroy_dev(tp->tap_dev); - s = splimp(); - ether_ifdetach(ifp); - if_free_type(ifp, IFT_ETHER); - splx(s); - - mtx_destroy(&tp->tap_mtx); - free(tp, M_TAP); + tap_destroy(tp); mtx_lock(&tapmtx); } mtx_unlock(&tapmtx); @@ -255,38 +333,63 @@ tapmodevent(module_t mod, int type, void *data) static void tapclone(void *arg, struct ucred *cred, char *name, int namelen, struct cdev **dev) { + char devname[SPECNAMELEN + 1]; + int i, unit, append_unit; int extra; - int i, unit; - char *device_name = name; if (*dev != NULL) return; - device_name = TAP; + /* + * If tap cloning is enabled, only the superuser can create + * an interface. + */ + if (!tapdclone || priv_check_cred(cred, PRIV_NET_IFCREATE, 0) != 0) + return; + + unit = 0; + append_unit = 0; extra = 0; + + /* We're interested in only tap/vmnet devices. */ if (strcmp(name, TAP) == 0) { unit = -1; } else if (strcmp(name, VMNET) == 0) { - device_name = VMNET; - extra = VMNET_DEV_MASK; unit = -1; - } else if (dev_stdclone(name, NULL, device_name, &unit) != 1) { - device_name = VMNET; extra = VMNET_DEV_MASK; - if (dev_stdclone(name, NULL, device_name, &unit) != 1) + } else if (dev_stdclone(name, NULL, TAP, &unit) != 1) { + if (dev_stdclone(name, NULL, VMNET, &unit) != 1) { return; + } else { + extra = VMNET_DEV_MASK; + } } + if (unit == -1) + append_unit = 1; + /* find any existing device, or allocate new unit number */ i = clone_create(&tapclones, &tap_cdevsw, &unit, dev, extra); if (i) { + if (append_unit) { + /* + * We were passed 'tun' or 'tap', with no unit specified + * so we'll need to append it now. + */ + namelen = snprintf(devname, sizeof(devname), "%s%d", name, + unit); + name = devname; + } + *dev = make_dev(&tap_cdevsw, unit2minor(unit | extra), - UID_ROOT, GID_WHEEL, 0600, "%s%d", device_name, unit); + UID_ROOT, GID_WHEEL, 0600, "%s", name); if (*dev != NULL) { dev_ref(*dev); (*dev)->si_flags |= SI_CHEAPCLONE; } } + + if_clone_create(name, namelen, NULL); } /* tapclone */ @@ -385,16 +488,7 @@ tapopen(struct cdev *dev, int flag, int mode, struct thread *td) if ((dev2unit(dev) & CLONE_UNITMASK) > TAPMAXUNIT) return (ENXIO); - /* - * XXXRW: Non-atomic test-and-set of si_drv1. Currently protected - * by Giant, but the race actually exists under memory pressure as - * well even when running with Giant, as malloc() may sleep. - */ tp = dev->si_drv1; - if (tp == NULL) { - tapcreate(dev); - tp = dev->si_drv1; - } mtx_lock(&tp->tap_mtx); if (tp->tap_flags & TAP_OPEN) { |