diff options
Diffstat (limited to 'sys/net/if_tun.c')
-rw-r--r-- | sys/net/if_tun.c | 84 |
1 files changed, 77 insertions, 7 deletions
diff --git a/sys/net/if_tun.c b/sys/net/if_tun.c index b313b18..efd57a2 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> @@ -105,13 +106,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 *); @@ -119,6 +129,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, caddr_t); +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; @@ -158,15 +173,45 @@ static struct cdevsw tun_cdevsw = { .d_name = TUNNAME, }; +static int +tun_clone_create(struct if_clone *ifc, int unit, caddr_t params) +{ + 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 || priv_check_cred(cred, PRIV_NET_IFCREATE, 0) != 0) + return; + if (strcmp(name, TUNNAME) == 0) { u = -1; } else if (dev_stdclone(name, NULL, TUNNAME, &u) != 1) @@ -174,17 +219,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, NULL); } static void @@ -206,6 +263,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) { @@ -219,8 +287,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); @@ -281,7 +351,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; @@ -299,8 +369,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; @@ -331,7 +401,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; } |