diff options
| author | Bruce M Simpson <bms@FreeBSD.org> | 2007-03-04 14:34:42 +0000 |
|---|---|---|
| committer | Bruce M Simpson <bms@FreeBSD.org> | 2007-03-04 14:34:42 +0000 |
| commit | 7c12c9ef5febb17d939e9c15f7695cc434ca6040 (patch) | |
| tree | c7ef8f5c53b22a1ff6c064025bf3ee5fbaa676e4 /sys/net | |
| parent | 5a71d8bbbcb16282d85bfbb90557d19dff3e6c57 (diff) | |
Notes
Diffstat (limited to 'sys/net')
| -rw-r--r-- | sys/net/if_tap.c | 173 | ||||
| -rw-r--r-- | sys/net/if_tun.c | 122 |
2 files changed, 231 insertions, 64 deletions
diff --git a/sys/net/if_tap.c b/sys/net/if_tap.c index 20b37e553c99..5b8390553664 100644 --- a/sys/net/if_tap.c +++ b/sys/net/if_tap.c @@ -61,6 +61,8 @@ #include <net/ethernet.h> #include <net/if.h> #include <net/if_arp.h> +#include <net/if_clone.h> +#include <net/if_dl.h> #include <net/route.h> #include <net/if_types.h> @@ -91,6 +93,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); +static void tap_clone_destroy(struct ifnet *); +static int vmnet_clone_create(struct if_clone *, int); +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; @@ -140,6 +150,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; @@ -152,10 +163,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) +{ + 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) +{ + return tap_clone_create(ifc, unit); +} + +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 * @@ -167,7 +255,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: @@ -184,6 +271,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: @@ -205,6 +294,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) { @@ -215,19 +306,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); @@ -253,38 +332,60 @@ 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 (!tapdclone || + (!tapuopen && suser_cred(cred, SUSER_ALLOWJAIL) != 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); } /* tapclone */ @@ -372,24 +473,18 @@ tapopen(struct cdev *dev, int flag, int mode, struct thread *td) { struct tap_softc *tp = NULL; struct ifnet *ifp = NULL; - int s; + int error, s; - if (tapuopen == 0 && suser(td) != 0) - return (EPERM); + if (tapuopen == 0) { + error = suser(td); + if (error != 0) + return (error); + } 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) { @@ -496,7 +591,7 @@ tapifinit(void *xtp) static int tapifioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { - struct tap_softc *tp = (struct tap_softc *)(ifp->if_softc); + struct tap_softc *tp = ifp->if_softc; struct ifstat *ifs = NULL; int s, dummy; @@ -612,7 +707,10 @@ tapioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td struct tapinfo *tapp = NULL; int s; int f; +#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ + defined(COMPAT_FREEBSD4) int ival; +#endif switch (cmd) { case TAPSIFINFO: @@ -687,10 +785,13 @@ tapioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td bcopy(&ifp->if_flags, data, sizeof(ifp->if_flags)); break; +#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ + defined(COMPAT_FREEBSD4) case _IO('V', 0): ival = IOCPARM_IVAL(data); data = (caddr_t)&ival; /* FALLTHROUGH */ +#endif case VMIO_SIOCSIFFLAGS: /* VMware/VMnet SIOCSIFFLAGS */ f = *(int *)data; f &= 0x0fff; diff --git a/sys/net/if_tun.c b/sys/net/if_tun.c index f3eab702bb23..075ffedfc358 100644 --- a/sys/net/if_tun.c +++ b/sys/net/if_tun.c @@ -45,6 +45,7 @@ #include <sys/random.h> #include <net/if.h> +#include <net/if_clone.h> #include <net/if_types.h> #include <net/netisr.h> #include <net/route.h> @@ -103,13 +104,22 @@ struct tun_softc { static struct mtx tunmtx; static MALLOC_DEFINE(M_TUN, TUNNAME, "Tunnel Interface"); static int tundebug = 0; +static int tundclone = 1; static struct clonedevs *tunclones; static TAILQ_HEAD(,tun_softc) tunhead = TAILQ_HEAD_INITIALIZER(tunhead); SYSCTL_INT(_debug, OID_AUTO, if_tun_debug, CTLFLAG_RW, &tundebug, 0, ""); +SYSCTL_DECL(_net_link); +SYSCTL_NODE(_net_link, OID_AUTO, tun, CTLFLAG_RW, 0, + "IP tunnel software network interface."); +SYSCTL_INT(_net_link_tun, OID_AUTO, devfs_cloning, CTLFLAG_RW, &tundclone, 0, + "Enable legacy devfs interface creation."); + +TUNABLE_INT("net.link.tun.devfs_cloning", &tundclone); + static void tunclone(void *arg, struct ucred *cred, char *name, int namelen, struct cdev **dev); -static void tuncreate(struct cdev *dev); +static void tuncreate(const char *name, struct cdev *dev); static int tunifioctl(struct ifnet *, u_long, caddr_t); static int tuninit(struct ifnet *); static int tunmodevent(module_t, int, void *); @@ -117,6 +127,11 @@ static int tunoutput(struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *rt); static void tunstart(struct ifnet *); +static int tun_clone_create(struct if_clone *, int); +static void tun_clone_destroy(struct ifnet *); + +IFC_SIMPLE_DECLARE(tun, 0); + static d_open_t tunopen; static d_close_t tunclose; static d_read_t tunread; @@ -156,15 +171,45 @@ static struct cdevsw tun_cdevsw = { .d_name = TUNNAME, }; +static int +tun_clone_create(struct if_clone *ifc, int unit) +{ + struct cdev *dev; + int i; + + /* find any existing device, or allocate new unit number */ + i = clone_create(&tunclones, &tun_cdevsw, &unit, &dev, 0); + if (i) { + /* No preexisting struct cdev *, create one */ + dev = make_dev(&tun_cdevsw, unit2minor(unit), + UID_UUCP, GID_DIALER, 0600, "%s%d", ifc->ifc_name, unit); + if (dev != NULL) { + dev_ref(dev); + dev->si_flags |= SI_CHEAPCLONE; + } + } + tuncreate(ifc->ifc_name, dev); + + return (0); +} + static void tunclone(void *arg, struct ucred *cred, char *name, int namelen, struct cdev **dev) { - int u, i; + char devname[SPECNAMELEN + 1]; + int u, i, append_unit; if (*dev != NULL) return; + /* + * If tun cloning is enabled, only the superuser can create an + * interface. + */ + if (!tundclone || suser_cred(cred, SUSER_ALLOWJAIL) != 0) + return; + if (strcmp(name, TUNNAME) == 0) { u = -1; } else if (dev_stdclone(name, NULL, TUNNAME, &u) != 1) @@ -172,17 +217,29 @@ tunclone(void *arg, struct ucred *cred, char *name, int namelen, if (u != -1 && u > IF_MAXUNIT) return; /* Unit number too high */ + if (u == -1) + append_unit = 1; + else + append_unit = 0; + /* find any existing device, or allocate new unit number */ i = clone_create(&tunclones, &tun_cdevsw, &u, dev, 0); if (i) { + if (append_unit) { + namelen = snprintf(devname, sizeof(devname), "%s%d", name, + u); + name = devname; + } /* No preexisting struct cdev *, create one */ *dev = make_dev(&tun_cdevsw, unit2minor(u), - UID_UUCP, GID_DIALER, 0600, "tun%d", u); + UID_UUCP, GID_DIALER, 0600, "%s", name); if (*dev != NULL) { dev_ref(*dev); (*dev)->si_flags |= SI_CHEAPCLONE; } } + + if_clone_create(name, namelen); } static void @@ -204,6 +261,17 @@ tun_destroy(struct tun_softc *tp) free(tp, M_TUN); } +static void +tun_clone_destroy(struct ifnet *ifp) +{ + struct tun_softc *tp = ifp->if_softc; + + mtx_lock(&tunmtx); + TAILQ_REMOVE(&tunhead, tp, tun_list); + mtx_unlock(&tunmtx); + tun_destroy(tp); +} + static int tunmodevent(module_t mod, int type, void *data) { @@ -217,8 +285,10 @@ tunmodevent(module_t mod, int type, void *data) tag = EVENTHANDLER_REGISTER(dev_clone, tunclone, 0, 1000); if (tag == NULL) return (ENOMEM); + if_clone_attach(&tun_cloner); break; case MOD_UNLOAD: + if_clone_detach(&tun_cloner); EVENTHANDLER_DEREGISTER(dev_clone, tag); mtx_lock(&tunmtx); @@ -279,7 +349,7 @@ tunstart(struct ifnet *ifp) /* XXX: should return an error code so it can fail. */ static void -tuncreate(struct cdev *dev) +tuncreate(const char *name, struct cdev *dev) { struct tun_softc *sc; struct ifnet *ifp; @@ -297,8 +367,8 @@ tuncreate(struct cdev *dev) ifp = sc->tun_ifp = if_alloc(IFT_PPP); if (ifp == NULL) panic("%s%d: failed to if_alloc() interface.\n", - TUNNAME, dev2unit(dev)); - if_initname(ifp, TUNNAME, dev2unit(dev)); + name, dev2unit(dev)); + if_initname(ifp, name, dev2unit(dev)); ifp->if_mtu = TUNMTU; ifp->if_ioctl = tunifioctl; ifp->if_output = tunoutput; @@ -329,7 +399,7 @@ tunopen(struct cdev *dev, int flag, int mode, struct thread *td) */ tp = dev->si_drv1; if (!tp) { - tuncreate(dev); + tuncreate(TUNNAME, dev); tp = dev->si_drv1; } @@ -425,29 +495,23 @@ tuninit(struct ifnet *ifp) ifp->if_drv_flags |= IFF_DRV_RUNNING; getmicrotime(&ifp->if_lastchange); - for (ifa = TAILQ_FIRST(&ifp->if_addrhead); ifa; - ifa = TAILQ_NEXT(ifa, ifa_link)) { - if (ifa->ifa_addr == NULL) - error = EFAULT; - /* XXX: Should maybe return straight off? */ - else { #ifdef INET - if (ifa->ifa_addr->sa_family == AF_INET) { - struct sockaddr_in *si; + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + if (ifa->ifa_addr->sa_family == AF_INET) { + struct sockaddr_in *si; - si = (struct sockaddr_in *)ifa->ifa_addr; - mtx_lock(&tp->tun_mtx); - if (si->sin_addr.s_addr) - tp->tun_flags |= TUN_IASET; + si = (struct sockaddr_in *)ifa->ifa_addr; + mtx_lock(&tp->tun_mtx); + if (si->sin_addr.s_addr) + tp->tun_flags |= TUN_IASET; - si = (struct sockaddr_in *)ifa->ifa_dstaddr; - if (si && si->sin_addr.s_addr) - tp->tun_flags |= TUN_DSTADDR; - mtx_unlock(&tp->tun_mtx); - } -#endif + si = (struct sockaddr_in *)ifa->ifa_dstaddr; + if (si && si->sin_addr.s_addr) + tp->tun_flags |= TUN_DSTADDR; + mtx_unlock(&tp->tun_mtx); } } +#endif return (error); } @@ -607,9 +671,11 @@ tunioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td tunp = (struct tuninfo *)data; if (tunp->mtu < IF_MINMTU) return (EINVAL); - if (TUN2IFP(tp)->if_mtu != tunp->mtu - && (error = suser(td)) != 0) - return (error); + if (TUN2IFP(tp)->if_mtu != tunp->mtu) { + error = suser(td); + if (error != 0) + return (error); + } TUN2IFP(tp)->if_mtu = tunp->mtu; TUN2IFP(tp)->if_type = tunp->type; TUN2IFP(tp)->if_baudrate = tunp->baudrate; |
