Changeset View
Standalone View
sys/net/if_tuntap.c
- This file was moved from sys/net/if_tun.c.
/* $NetBSD: if_tun.c,v 1.14 1994/06/29 06:36:25 cgd Exp $ */ | /* $NetBSD: if_tun.c,v 1.14 1994/06/29 06:36:25 cgd Exp $ */ | ||||
/*- | /*- | ||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD | |||||
rgrimes: This tag is minimally incomplete, there are addition license conditions in this file. | |||||
* | |||||
* Copyright (C) 1999-2000 by Maksim Yevmenkin <m_evmenkin@yahoo.com> | |||||
* All rights reserved. | |||||
* Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org> | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
* SUCH DAMAGE. | |||||
* | |||||
Not Done Inline ActionsI am a bit iffy on adding a "license" which is in conflict with the already existing one, ie it places additional restriction that did not exist before. Is this really needed? Did you add this (kyle), or did someone else add this to some source (Maksim Yevmenkin) your drawing from? rgrimes: I am a bit iffy on adding a "license" which is in conflict with the already existing one, ie it… | |||||
Done Inline ActionsInitially added by myself, but given that the tap code I merged in should maintain Maksim Yevmenkin's notice/license I'm not sure how to represent this. kevans: Initially added by myself, but given that the tap code I merged in should maintain Maksim… | |||||
Not Done Inline ActionsThis is not in conflict with the current license, per se. The other license is a crappy one from the early days that doesn't grant even the right to make a derived work (just distribution). The intention is clearly more, but it's poorly drafted so it's unclear. imp: This is not in conflict with the current license, per se. The other license is a crappy one… | |||||
Done Inline ActionsI started trying to get in contact with Julian via e-mail to see if he'd be willing to re-license this as BSD-2-Clause just to simplify the matter. Both of the e-mail addresses I had found for him bounced, so I'm trying other methods. He allowed OpenBSD to change the license back in 2002, so I see no reason he wouldn't be OK with it... if I can get in contact. kevans: I started trying to get in contact with Julian via e-mail to see if he'd be willing to re… | |||||
* BASED ON: | |||||
* ------------------------------------------------------------------------- | |||||
* | |||||
* Copyright (c) 1988, Julian Onions <jpo@cs.nott.ac.uk> | * Copyright (c) 1988, Julian Onions <jpo@cs.nott.ac.uk> | ||||
* Nottingham University 1987. | * Nottingham University 1987. | ||||
* | * | ||||
* This source may be freely distributed, however I would be interested | * This source may be freely distributed, however I would be interested | ||||
* in any changes that are made. | * in any changes that are made. | ||||
* | * | ||||
* This driver takes packets off the IP i/f and hands them up to a | * This driver takes packets off the IP i/f and hands them up to a | ||||
* user process to have its wicked way with. This driver has it's | * user process to have its wicked way with. This driver has it's | ||||
Show All 28 Lines | |||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/conf.h> | #include <sys/conf.h> | ||||
#include <sys/uio.h> | #include <sys/uio.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/random.h> | #include <sys/random.h> | ||||
#include <sys/ctype.h> | #include <sys/ctype.h> | ||||
#include <net/ethernet.h> | |||||
#include <net/if.h> | #include <net/if.h> | ||||
#include <net/if_var.h> | #include <net/if_var.h> | ||||
#include <net/if_clone.h> | #include <net/if_clone.h> | ||||
#include <net/if_dl.h> | |||||
#include <net/if_media.h> | |||||
#include <net/if_types.h> | #include <net/if_types.h> | ||||
#include <net/netisr.h> | #include <net/netisr.h> | ||||
#include <net/route.h> | #include <net/route.h> | ||||
#include <net/vnet.h> | #include <net/vnet.h> | ||||
#ifdef INET | #ifdef INET | ||||
#include <netinet/in.h> | #include <netinet/in.h> | ||||
#endif | #endif | ||||
#include <net/bpf.h> | #include <net/bpf.h> | ||||
#include <net/if_tap.h> | |||||
#include <net/if_tun.h> | #include <net/if_tun.h> | ||||
#include <sys/queue.h> | #include <sys/queue.h> | ||||
#include <sys/condvar.h> | #include <sys/condvar.h> | ||||
#include <security/mac/mac_framework.h> | #include <security/mac/mac_framework.h> | ||||
struct tuntap_driver; | |||||
/* | /* | ||||
* tun_list is protected by global tunmtx. Other mutable fields are | * tun_list is protected by global tunmtx. Other mutable fields are | ||||
* protected by tun->tun_mtx, or by their owning subsystem. tun_dev is | * protected by tun->tun_mtx, or by their owning subsystem. tun_dev is | ||||
* static for the duration of a tunnel interface. | * static for the duration of a tunnel interface. | ||||
*/ | */ | ||||
struct tun_softc { | struct tuntap_softc { | ||||
TAILQ_ENTRY(tun_softc) tun_list; | TAILQ_ENTRY(tuntap_softc) tun_list; | ||||
struct cdev *tun_dev; | struct cdev *tun_dev; | ||||
u_short tun_flags; /* misc flags */ | u_short tun_flags; /* misc flags */ | ||||
#define TUN_OPEN 0x0001 | #define TUN_OPEN 0x0001 | ||||
#define TUN_INITED 0x0002 | #define TUN_INITED 0x0002 | ||||
#define TUN_RCOLL 0x0004 | #define TUN_RCOLL 0x0004 | ||||
#define TUN_IASET 0x0008 | #define TUN_IASET 0x0008 | ||||
#define TUN_DSTADDR 0x0010 | #define TUN_DSTADDR 0x0010 | ||||
#define TUN_LMODE 0x0020 | #define TUN_LMODE 0x0020 | ||||
#define TUN_RWAIT 0x0040 | #define TUN_RWAIT 0x0040 | ||||
#define TUN_ASYNC 0x0080 | #define TUN_ASYNC 0x0080 | ||||
#define TUN_IFHEAD 0x0100 | #define TUN_IFHEAD 0x0100 | ||||
#define TUN_DYING 0x0200 | #define TUN_DYING 0x0200 | ||||
#define TUN_L2 0x0400 | |||||
#define TUN_VMNET 0x0800 | |||||
#define TUN_READY (TUN_OPEN | TUN_INITED) | #define TUN_READY (TUN_OPEN | TUN_INITED) | ||||
pid_t tun_pid; /* owning pid */ | pid_t tun_pid; /* owning pid */ | ||||
struct ifnet *tun_ifp; /* the interface */ | struct ifnet *tun_ifp; /* the interface */ | ||||
struct sigio *tun_sigio; /* information for async I/O */ | struct sigio *tun_sigio; /* information for async I/O */ | ||||
struct tuntap_driver *tun_drv; /* appropriate driver */ | |||||
struct selinfo tun_rsel; /* read select */ | struct selinfo tun_rsel; /* read select */ | ||||
struct mtx tun_mtx; /* protect mutable softc fields */ | struct mtx tun_mtx; /* protect mutable softc fields */ | ||||
struct cv tun_cv; /* protect against ref'd dev destroy */ | struct cv tun_cv; /* protect against ref'd dev destroy */ | ||||
struct ether_addr tun_ether; /* remote address */ | |||||
}; | }; | ||||
#define TUN2IFP(sc) ((sc)->tun_ifp) | #define TUN2IFP(sc) ((sc)->tun_ifp) | ||||
#define TUNDEBUG if (tundebug) if_printf | #define TUNDEBUG if (tundebug) if_printf | ||||
#define TUN_LOCK(tp) mtx_lock(&(tp)->tun_mtx) | |||||
#define TUN_UNLOCK(tp) mtx_unlock(&(tp)->tun_mtx) | |||||
#define TUN_VMIO_FLAG_MASK 0x0fff | |||||
/* | /* | ||||
* All mutable global variables in if_tun are locked using tunmtx, with | * All mutable global variables in if_tun are locked using tunmtx, with | ||||
* the exception of tundebug, which is used unlocked, and tunclones, | * the exception of tundebug, which is used unlocked, and the drivers' *clones, | ||||
* which is static after setup. | * which are static after setup. | ||||
*/ | */ | ||||
static struct mtx tunmtx; | static struct mtx tunmtx; | ||||
static eventhandler_tag tag; | static eventhandler_tag tag; | ||||
static const char tunname[] = "tun"; | static const char tunname[] = "tun"; | ||||
static const char tapname[] = "tap"; | |||||
static const char vmnetname[] = "vmnet"; | |||||
static MALLOC_DEFINE(M_TUN, tunname, "Tunnel Interface"); | static MALLOC_DEFINE(M_TUN, tunname, "Tunnel Interface"); | ||||
static int tundebug = 0; | static int tundebug = 0; | ||||
static int tundclone = 1; | static int tundclone = 1; | ||||
static struct clonedevs *tunclones; | static int tap_allow_uopen = 0; /* allow user open() */ | ||||
Done Inline ActionsWould it be possible to make the name slightly more descriptive, like, tap_allow_uopen? It would ease understanding tunopen() / tunclone() code :-) melifaro: Would it be possible to make the name slightly more descriptive, like, tap_allow_uopen? It… | |||||
static TAILQ_HEAD(,tun_softc) tunhead = TAILQ_HEAD_INITIALIZER(tunhead); | static int tapuponopen = 0; /* IFF_UP on open() */ | ||||
static int tapdclone = 1; /* enable devfs cloning */ | |||||
static TAILQ_HEAD(,tuntap_softc) tunhead = TAILQ_HEAD_INITIALIZER(tunhead); | |||||
SYSCTL_INT(_debug, OID_AUTO, if_tun_debug, CTLFLAG_RW, &tundebug, 0, ""); | SYSCTL_INT(_debug, OID_AUTO, if_tun_debug, CTLFLAG_RW, &tundebug, 0, ""); | ||||
static struct sx tun_ioctl_sx; | static struct sx tun_ioctl_sx; | ||||
SX_SYSINIT(tun_ioctl_sx, &tun_ioctl_sx, "tun_ioctl"); | SX_SYSINIT(tun_ioctl_sx, &tun_ioctl_sx, "tun_ioctl"); | ||||
SYSCTL_DECL(_net_link); | SYSCTL_DECL(_net_link); | ||||
/* tun */ | |||||
static SYSCTL_NODE(_net_link, OID_AUTO, tun, CTLFLAG_RW, 0, | static SYSCTL_NODE(_net_link, OID_AUTO, tun, CTLFLAG_RW, 0, | ||||
"IP tunnel software network interface."); | "IP tunnel software network interface."); | ||||
SYSCTL_INT(_net_link_tun, OID_AUTO, devfs_cloning, CTLFLAG_RWTUN, &tundclone, 0, | SYSCTL_INT(_net_link_tun, OID_AUTO, devfs_cloning, CTLFLAG_RWTUN, &tundclone, 0, | ||||
"Enable legacy devfs interface creation."); | "Enable legacy devfs interface creation."); | ||||
/* tap */ | |||||
static 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, &tap_allow_uopen, 0, | |||||
"Allow user to open /dev/tap (based on node permissions)"); | |||||
SYSCTL_INT(_net_link_tap, OID_AUTO, up_on_open, CTLFLAG_RW, &tapuponopen, 0, | |||||
"Bring interface up when /dev/tap is opened"); | |||||
SYSCTL_INT(_net_link_tap, OID_AUTO, devfs_cloning, CTLFLAG_RWTUN, &tapdclone, 0, | |||||
"Enable legacy devfs interface creation"); | |||||
SYSCTL_INT(_net_link_tap, OID_AUTO, debug, CTLFLAG_RW, &tundebug, 0, ""); | |||||
static int tuntap_name2info(const char *name, int *unit, int *flags); | |||||
static void tunclone(void *arg, struct ucred *cred, char *name, | static void tunclone(void *arg, struct ucred *cred, char *name, | ||||
int namelen, struct cdev **dev); | int namelen, struct cdev **dev); | ||||
static void tuncreate(const char *name, struct cdev *dev); | static void tuncreate(struct cdev *dev, struct tuntap_driver *); | ||||
static int tunifioctl(struct ifnet *, u_long, caddr_t); | static int tunifioctl(struct ifnet *, u_long, caddr_t); | ||||
static void tuninit(struct ifnet *); | static void tuninit(struct ifnet *); | ||||
static int tunmodevent(module_t, int, void *); | static void tunifinit(void *xtp); | ||||
static int tuntapmodevent(module_t, int, void *); | |||||
static int tunoutput(struct ifnet *, struct mbuf *, | static int tunoutput(struct ifnet *, struct mbuf *, | ||||
const struct sockaddr *, struct route *ro); | const struct sockaddr *, struct route *ro); | ||||
static void tunstart(struct ifnet *); | static void tunstart(struct ifnet *); | ||||
static void tunstart_l2(struct ifnet *); | |||||
static int tun_clone_match(struct if_clone *ifc, const char *name); | static int tun_clone_match(struct if_clone *ifc, const char *name); | ||||
static int tap_clone_match(struct if_clone *ifc, const char *name); | |||||
static int vmnet_clone_match(struct if_clone *ifc, const char *name); | |||||
static int tun_clone_create(struct if_clone *, char *, size_t, caddr_t); | static int tun_clone_create(struct if_clone *, char *, size_t, caddr_t); | ||||
static int tun_clone_destroy(struct if_clone *, struct ifnet *); | static int tun_clone_destroy(struct if_clone *, struct ifnet *); | ||||
static struct unrhdr *tun_unrhdr; | |||||
VNET_DEFINE_STATIC(struct if_clone *, tun_cloner); | |||||
#define V_tun_cloner VNET(tun_cloner) | |||||
static d_open_t tunopen; | static d_open_t tunopen; | ||||
static d_close_t tunclose; | static d_close_t tunclose; | ||||
static d_read_t tunread; | static d_read_t tunread; | ||||
static d_write_t tunwrite; | static d_write_t tunwrite; | ||||
static d_ioctl_t tunioctl; | static d_ioctl_t tunioctl; | ||||
static d_poll_t tunpoll; | static d_poll_t tunpoll; | ||||
static d_kqfilter_t tunkqfilter; | static d_kqfilter_t tunkqfilter; | ||||
Show All 11 Lines | |||||
static struct filterops tun_write_filterops = { | static struct filterops tun_write_filterops = { | ||||
.f_isfd = 1, | .f_isfd = 1, | ||||
.f_attach = NULL, | .f_attach = NULL, | ||||
.f_detach = tunkqdetach, | .f_detach = tunkqdetach, | ||||
.f_event = tunkqwrite, | .f_event = tunkqwrite, | ||||
}; | }; | ||||
static struct cdevsw tun_cdevsw = { | #define TUN_DRIVER_IDENT_MASK (TUN_L2 | TUN_VMNET) | ||||
static struct tuntap_driver { | |||||
int tun_flags; | |||||
struct unrhdr *unrhdr; | |||||
struct cdevsw cdevsw; | |||||
struct clonedevs *clones; | |||||
ifc_match_t *clone_match_fn; | |||||
ifc_create_t *clone_create_fn; | |||||
ifc_destroy_t *clone_destroy_fn; | |||||
} tuntap_drivers[] = { | |||||
{ | |||||
.tun_flags = 0, | |||||
.cdevsw = { | |||||
.d_version = D_VERSION, | .d_version = D_VERSION, | ||||
.d_flags = D_NEEDMINOR, | .d_flags = D_NEEDMINOR, | ||||
.d_open = tunopen, | .d_open = tunopen, | ||||
.d_close = tunclose, | .d_close = tunclose, | ||||
.d_read = tunread, | .d_read = tunread, | ||||
.d_write = tunwrite, | .d_write = tunwrite, | ||||
.d_ioctl = tunioctl, | .d_ioctl = tunioctl, | ||||
.d_poll = tunpoll, | .d_poll = tunpoll, | ||||
.d_kqfilter = tunkqfilter, | .d_kqfilter = tunkqfilter, | ||||
.d_name = tunname, | .d_name = tunname, | ||||
}, | |||||
.clone_match_fn = tun_clone_match, | |||||
.clone_create_fn = tun_clone_create, | |||||
.clone_destroy_fn = tun_clone_destroy, | |||||
}, | |||||
{ | |||||
.tun_flags = TUN_L2, | |||||
.cdevsw = { | |||||
.d_version = D_VERSION, | |||||
.d_flags = D_NEEDMINOR, | |||||
.d_open = tunopen, | |||||
.d_close = tunclose, | |||||
.d_read = tunread, | |||||
.d_write = tunwrite, | |||||
.d_ioctl = tunioctl, | |||||
.d_poll = tunpoll, | |||||
.d_kqfilter = tunkqfilter, | |||||
.d_name = tapname, | |||||
}, | |||||
.clone_match_fn = tap_clone_match, | |||||
.clone_create_fn = tun_clone_create, | |||||
.clone_destroy_fn = tun_clone_destroy, | |||||
}, | |||||
{ | |||||
.tun_flags = TUN_L2 | TUN_VMNET, | |||||
.cdevsw = { | |||||
.d_version = D_VERSION, | |||||
.d_flags = D_NEEDMINOR, | |||||
.d_open = tunopen, | |||||
.d_close = tunclose, | |||||
.d_read = tunread, | |||||
.d_write = tunwrite, | |||||
.d_ioctl = tunioctl, | |||||
.d_poll = tunpoll, | |||||
.d_kqfilter = tunkqfilter, | |||||
.d_name = vmnetname, | |||||
}, | |||||
.clone_match_fn = vmnet_clone_match, | |||||
.clone_create_fn = tun_clone_create, | |||||
.clone_destroy_fn = tun_clone_destroy, | |||||
}, | |||||
}; | }; | ||||
struct tuntap_driver_cloner { | |||||
SLIST_ENTRY(tuntap_driver_cloner) link; | |||||
struct tuntap_driver *drv; | |||||
struct if_clone *cloner; | |||||
}; | |||||
VNET_DEFINE_STATIC(SLIST_HEAD(, tuntap_driver_cloner), tuntap_driver_cloners) = | |||||
SLIST_HEAD_INITIALIZER(tuntap_driver_cloners); | |||||
#define V_tuntap_driver_cloners VNET(tuntap_driver_cloners) | |||||
/* | |||||
* Sets unit and/or flags given the device name. Must be called with correct | |||||
* vnet context. | |||||
*/ | |||||
static int | static int | ||||
tuntap_name2info(const char *name, int *outunit, int *outflags) | |||||
{ | |||||
struct tuntap_driver *drv; | |||||
struct tuntap_driver_cloner *drvc; | |||||
char *dname; | |||||
int flags, unit; | |||||
bool found; | |||||
if (name == NULL) | |||||
return (EINVAL); | |||||
/* | |||||
* Needed for dev_stdclone, but dev_stdclone will not modify, it just | |||||
* wants to be able to pass back a char * through the second param. We | |||||
* will always set that as NULL here, so we'll fake it. | |||||
*/ | |||||
dname = __DECONST(char *, name); | |||||
found = false; | |||||
KASSERT(!SLIST_EMPTY(&V_tuntap_driver_cloners), | |||||
("tuntap_driver_cloners failed to initialize")); | |||||
SLIST_FOREACH(drvc, &V_tuntap_driver_cloners, link) { | |||||
KASSERT(drvc->drv != NULL, | |||||
("tuntap_driver_cloners entry not properly initialized")); | |||||
drv = drvc->drv; | |||||
if (strcmp(name, drv->cdevsw.d_name) == 0) { | |||||
found = true; | |||||
unit = -1; | |||||
flags = drv->tun_flags; | |||||
break; | |||||
} | |||||
if (dev_stdclone(dname, NULL, drv->cdevsw.d_name, &unit) == 1) { | |||||
found = true; | |||||
flags = drv->tun_flags; | |||||
break; | |||||
} | |||||
} | |||||
if (!found) | |||||
return (ENXIO); | |||||
if (outunit != NULL) | |||||
*outunit = unit; | |||||
if (outflags != NULL) | |||||
*outflags = flags; | |||||
return (0); | |||||
} | |||||
/* | |||||
* Get driver information from a set of flags specified. Masks the identifying | |||||
* part of the flags and compares it against all of the available | |||||
* tuntap_drivers. Must be called with correct vnet context. | |||||
*/ | |||||
static struct tuntap_driver * | |||||
tuntap_driver_from_flags(int tun_flags) | |||||
{ | |||||
struct tuntap_driver *drv; | |||||
struct tuntap_driver_cloner *drvc; | |||||
KASSERT(!SLIST_EMPTY(&V_tuntap_driver_cloners), | |||||
("tuntap_driver_cloners failed to initialize")); | |||||
SLIST_FOREACH(drvc, &V_tuntap_driver_cloners, link) { | |||||
KASSERT(drvc->drv != NULL, | |||||
("tuntap_driver_cloners entry not properly initialized")); | |||||
drv = drvc->drv; | |||||
if ((tun_flags & TUN_DRIVER_IDENT_MASK) == drv->tun_flags) | |||||
return (drv); | |||||
} | |||||
return (NULL); | |||||
} | |||||
static int | |||||
tun_clone_match(struct if_clone *ifc, const char *name) | tun_clone_match(struct if_clone *ifc, const char *name) | ||||
{ | { | ||||
if (strncmp(tunname, name, 3) == 0 && | int tunflags; | ||||
(name[3] == '\0' || isdigit(name[3]))) | |||||
if (tuntap_name2info(name, NULL, &tunflags) == 0) { | |||||
if ((tunflags & TUN_L2) == 0) | |||||
return (1); | return (1); | ||||
} | |||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
tap_clone_match(struct if_clone *ifc, const char *name) | |||||
{ | |||||
int tunflags; | |||||
if (tuntap_name2info(name, NULL, &tunflags) == 0) { | |||||
if ((tunflags & (TUN_L2 | TUN_VMNET)) == TUN_L2) | |||||
return (1); | |||||
} | |||||
return (0); | |||||
} | |||||
static int | |||||
vmnet_clone_match(struct if_clone *ifc, const char *name) | |||||
{ | |||||
int tunflags; | |||||
if (tuntap_name2info(name, NULL, &tunflags) == 0) { | |||||
if ((tunflags & TUN_VMNET) != 0) | |||||
return (1); | |||||
} | |||||
return (0); | |||||
} | |||||
static int | |||||
tun_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) | tun_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) | ||||
{ | { | ||||
struct tuntap_driver *drv; | |||||
struct cdev *dev; | struct cdev *dev; | ||||
int err, unit, i; | int err, i, tunflags, unit; | ||||
err = ifc_name2unit(name, &unit); | tunflags = 0; | ||||
/* The name here tells us exactly what we're creating */ | |||||
err = tuntap_name2info(name, &unit, &tunflags); | |||||
if (err != 0) | if (err != 0) | ||||
return (err); | return (err); | ||||
drv = tuntap_driver_from_flags(tunflags); | |||||
if (drv == NULL) | |||||
return (ENXIO); | |||||
if (unit != -1) { | if (unit != -1) { | ||||
/* If this unit number is still available that/s okay. */ | /* If this unit number is still available that/s okay. */ | ||||
if (alloc_unr_specific(tun_unrhdr, unit) == -1) | if (alloc_unr_specific(drv->unrhdr, unit) == -1) | ||||
return (EEXIST); | return (EEXIST); | ||||
} else { | } else { | ||||
unit = alloc_unr(tun_unrhdr); | unit = alloc_unr(drv->unrhdr); | ||||
} | } | ||||
snprintf(name, IFNAMSIZ, "%s%d", tunname, unit); | snprintf(name, IFNAMSIZ, "%s%d", drv->cdevsw.d_name, unit); | ||||
/* find any existing device, or allocate new unit number */ | /* find any existing device, or allocate new unit number */ | ||||
i = clone_create(&tunclones, &tun_cdevsw, &unit, &dev, 0); | i = clone_create(&drv->clones, &drv->cdevsw, &unit, &dev, 0); | ||||
if (i) { | if (i) { | ||||
/* No preexisting struct cdev *, create one */ | /* No preexisting struct cdev *, create one */ | ||||
dev = make_dev(&tun_cdevsw, unit, | dev = make_dev(&drv->cdevsw, unit, UID_UUCP, GID_DIALER, 0600, | ||||
UID_UUCP, GID_DIALER, 0600, "%s%d", tunname, unit); | "%s%d", drv->cdevsw.d_name, unit); | ||||
} | } | ||||
tuncreate(tunname, dev); | |||||
tuncreate(dev, drv); | |||||
return (0); | return (0); | ||||
} | } | ||||
static void | static void | ||||
tunclone(void *arg, struct ucred *cred, char *name, int namelen, | tunclone(void *arg, struct ucred *cred, char *name, int namelen, | ||||
struct cdev **dev) | struct cdev **dev) | ||||
{ | { | ||||
char devname[SPECNAMELEN + 1]; | char devname[SPECNAMELEN + 1]; | ||||
int u, i, append_unit; | struct tuntap_driver *drv; | ||||
int append_unit, i, u, tunflags; | |||||
bool mayclone; | |||||
if (*dev != NULL) | if (*dev != NULL) | ||||
return; | return; | ||||
tunflags = 0; | |||||
CURVNET_SET(CRED_TO_VNET(cred)); | |||||
if (tuntap_name2info(name, &u, &tunflags) != 0) | |||||
goto out; /* Not recognized */ | |||||
if (u != -1 && u > IF_MAXUNIT) | |||||
goto out; /* Unit number too high */ | |||||
mayclone = priv_check_cred(cred, PRIV_NET_IFCREATE) == 0; | |||||
if ((tunflags & TUN_L2) != 0) { | |||||
/* tap/vmnet allow user open with a sysctl */ | |||||
mayclone = (mayclone || tap_allow_uopen) && tapdclone; | |||||
} else { | |||||
mayclone = mayclone && tundclone; | |||||
} | |||||
/* | /* | ||||
* If tun cloning is enabled, only the superuser can create an | * If tun cloning is enabled, only the superuser can create an | ||||
* interface. | * interface. | ||||
*/ | */ | ||||
if (!tundclone || priv_check_cred(cred, PRIV_NET_IFCREATE) != 0) | if (!mayclone) | ||||
return; | goto out; | ||||
if (strcmp(name, tunname) == 0) { | |||||
u = -1; | |||||
} else if (dev_stdclone(name, NULL, tunname, &u) != 1) | |||||
return; /* Don't recognise the name */ | |||||
if (u != -1 && u > IF_MAXUNIT) | |||||
return; /* Unit number too high */ | |||||
if (u == -1) | if (u == -1) | ||||
append_unit = 1; | append_unit = 1; | ||||
else | else | ||||
append_unit = 0; | append_unit = 0; | ||||
CURVNET_SET(CRED_TO_VNET(cred)); | drv = tuntap_driver_from_flags(tunflags); | ||||
if (drv == NULL) | |||||
goto out; | |||||
/* find any existing device, or allocate new unit number */ | /* find any existing device, or allocate new unit number */ | ||||
i = clone_create(&tunclones, &tun_cdevsw, &u, dev, 0); | i = clone_create(&drv->clones, &drv->cdevsw, &u, dev, 0); | ||||
if (i) { | if (i) { | ||||
if (append_unit) { | if (append_unit) { | ||||
namelen = snprintf(devname, sizeof(devname), "%s%d", | namelen = snprintf(devname, sizeof(devname), "%s%d", | ||||
name, u); | name, u); | ||||
name = devname; | name = devname; | ||||
} | } | ||||
/* No preexisting struct cdev *, create one */ | /* No preexisting struct cdev *, create one */ | ||||
*dev = make_dev_credf(MAKEDEV_REF, &tun_cdevsw, u, cred, | *dev = make_dev_credf(MAKEDEV_REF, &drv->cdevsw, u, cred, | ||||
UID_UUCP, GID_DIALER, 0600, "%s", name); | UID_UUCP, GID_DIALER, 0600, "%s", name); | ||||
} | } | ||||
if_clone_create(name, namelen, NULL); | if_clone_create(name, namelen, NULL); | ||||
out: | |||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
} | } | ||||
static void | static void | ||||
tun_destroy(struct tun_softc *tp) | tun_destroy(struct tuntap_softc *tp) | ||||
{ | { | ||||
struct cdev *dev; | struct cdev *dev; | ||||
mtx_lock(&tp->tun_mtx); | TUN_LOCK(tp); | ||||
tp->tun_flags |= TUN_DYING; | tp->tun_flags |= TUN_DYING; | ||||
if ((tp->tun_flags & TUN_OPEN) != 0) | if ((tp->tun_flags & TUN_OPEN) != 0) | ||||
cv_wait_unlock(&tp->tun_cv, &tp->tun_mtx); | cv_wait_unlock(&tp->tun_cv, &tp->tun_mtx); | ||||
else | else | ||||
mtx_unlock(&tp->tun_mtx); | TUN_UNLOCK(tp); | ||||
CURVNET_SET(TUN2IFP(tp)->if_vnet); | CURVNET_SET(TUN2IFP(tp)->if_vnet); | ||||
sx_xlock(&tun_ioctl_sx); | sx_xlock(&tun_ioctl_sx); | ||||
TUN2IFP(tp)->if_softc = NULL; | TUN2IFP(tp)->if_softc = NULL; | ||||
sx_xunlock(&tun_ioctl_sx); | sx_xunlock(&tun_ioctl_sx); | ||||
dev = tp->tun_dev; | dev = tp->tun_dev; | ||||
bpfdetach(TUN2IFP(tp)); | bpfdetach(TUN2IFP(tp)); | ||||
if_detach(TUN2IFP(tp)); | if_detach(TUN2IFP(tp)); | ||||
free_unr(tun_unrhdr, TUN2IFP(tp)->if_dunit); | free_unr(tp->tun_drv->unrhdr, TUN2IFP(tp)->if_dunit); | ||||
if_free(TUN2IFP(tp)); | if_free(TUN2IFP(tp)); | ||||
destroy_dev(dev); | destroy_dev(dev); | ||||
seldrain(&tp->tun_rsel); | seldrain(&tp->tun_rsel); | ||||
knlist_clear(&tp->tun_rsel.si_note, 0); | knlist_clear(&tp->tun_rsel.si_note, 0); | ||||
knlist_destroy(&tp->tun_rsel.si_note); | knlist_destroy(&tp->tun_rsel.si_note); | ||||
mtx_destroy(&tp->tun_mtx); | mtx_destroy(&tp->tun_mtx); | ||||
cv_destroy(&tp->tun_cv); | cv_destroy(&tp->tun_cv); | ||||
free(tp, M_TUN); | free(tp, M_TUN); | ||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
} | } | ||||
static int | static int | ||||
tun_clone_destroy(struct if_clone *ifc, struct ifnet *ifp) | tun_clone_destroy(struct if_clone *ifc __unused, struct ifnet *ifp) | ||||
{ | { | ||||
struct tun_softc *tp = ifp->if_softc; | struct tuntap_softc *tp = ifp->if_softc; | ||||
mtx_lock(&tunmtx); | mtx_lock(&tunmtx); | ||||
TAILQ_REMOVE(&tunhead, tp, tun_list); | TAILQ_REMOVE(&tunhead, tp, tun_list); | ||||
mtx_unlock(&tunmtx); | mtx_unlock(&tunmtx); | ||||
tun_destroy(tp); | tun_destroy(tp); | ||||
return (0); | return (0); | ||||
} | } | ||||
static void | static void | ||||
vnet_tun_init(const void *unused __unused) | vnet_tun_init(const void *unused __unused) | ||||
{ | { | ||||
V_tun_cloner = if_clone_advanced(tunname, 0, tun_clone_match, | struct tuntap_driver *drv; | ||||
tun_clone_create, tun_clone_destroy); | struct tuntap_driver_cloner *drvc; | ||||
int i; | |||||
for (i = 0; i < nitems(tuntap_drivers); ++i) { | |||||
drv = &tuntap_drivers[i]; | |||||
drvc = malloc(sizeof(*drvc), M_TUN, M_WAITOK | M_ZERO); | |||||
drvc->drv = drv; | |||||
drvc->cloner = if_clone_advanced(drv->cdevsw.d_name, 0, | |||||
drv->clone_match_fn, drv->clone_create_fn, | |||||
drv->clone_destroy_fn); | |||||
SLIST_INSERT_HEAD(&V_tuntap_driver_cloners, drvc, link); | |||||
}; | |||||
} | } | ||||
VNET_SYSINIT(vnet_tun_init, SI_SUB_PROTO_IF, SI_ORDER_ANY, | VNET_SYSINIT(vnet_tun_init, SI_SUB_PROTO_IF, SI_ORDER_ANY, | ||||
vnet_tun_init, NULL); | vnet_tun_init, NULL); | ||||
static void | static void | ||||
vnet_tun_uninit(const void *unused __unused) | vnet_tun_uninit(const void *unused __unused) | ||||
{ | { | ||||
if_clone_detach(V_tun_cloner); | struct tuntap_driver_cloner *drvc; | ||||
while (!SLIST_EMPTY(&V_tuntap_driver_cloners)) { | |||||
drvc = SLIST_FIRST(&V_tuntap_driver_cloners); | |||||
SLIST_REMOVE_HEAD(&V_tuntap_driver_cloners, link); | |||||
if_clone_detach(drvc->cloner); | |||||
free(drvc, M_TUN); | |||||
} | } | ||||
} | |||||
VNET_SYSUNINIT(vnet_tun_uninit, SI_SUB_PROTO_IF, SI_ORDER_ANY, | VNET_SYSUNINIT(vnet_tun_uninit, SI_SUB_PROTO_IF, SI_ORDER_ANY, | ||||
vnet_tun_uninit, NULL); | vnet_tun_uninit, NULL); | ||||
static void | static void | ||||
tun_uninit(const void *unused __unused) | tun_uninit(const void *unused __unused) | ||||
{ | { | ||||
struct tun_softc *tp; | struct tuntap_driver *drv; | ||||
struct tuntap_softc *tp; | |||||
int i; | |||||
EVENTHANDLER_DEREGISTER(dev_clone, tag); | EVENTHANDLER_DEREGISTER(dev_clone, tag); | ||||
drain_dev_clone_events(); | drain_dev_clone_events(); | ||||
mtx_lock(&tunmtx); | mtx_lock(&tunmtx); | ||||
while ((tp = TAILQ_FIRST(&tunhead)) != NULL) { | while ((tp = TAILQ_FIRST(&tunhead)) != NULL) { | ||||
TAILQ_REMOVE(&tunhead, tp, tun_list); | TAILQ_REMOVE(&tunhead, tp, tun_list); | ||||
mtx_unlock(&tunmtx); | mtx_unlock(&tunmtx); | ||||
tun_destroy(tp); | tun_destroy(tp); | ||||
mtx_lock(&tunmtx); | mtx_lock(&tunmtx); | ||||
} | } | ||||
mtx_unlock(&tunmtx); | mtx_unlock(&tunmtx); | ||||
delete_unrhdr(tun_unrhdr); | for (i = 0; i < nitems(tuntap_drivers); ++i) { | ||||
clone_cleanup(&tunclones); | drv = &tuntap_drivers[i]; | ||||
delete_unrhdr(drv->unrhdr); | |||||
clone_cleanup(&drv->clones); | |||||
} | |||||
mtx_destroy(&tunmtx); | mtx_destroy(&tunmtx); | ||||
} | } | ||||
SYSUNINIT(tun_uninit, SI_SUB_PROTO_IF, SI_ORDER_ANY, tun_uninit, NULL); | SYSUNINIT(tun_uninit, SI_SUB_PROTO_IF, SI_ORDER_ANY, tun_uninit, NULL); | ||||
static int | static int | ||||
tunmodevent(module_t mod, int type, void *data) | tuntapmodevent(module_t mod, int type, void *data) | ||||
{ | { | ||||
struct tuntap_driver *drv; | |||||
int i; | |||||
switch (type) { | switch (type) { | ||||
case MOD_LOAD: | case MOD_LOAD: | ||||
mtx_init(&tunmtx, "tunmtx", NULL, MTX_DEF); | mtx_init(&tunmtx, "tunmtx", NULL, MTX_DEF); | ||||
clone_setup(&tunclones); | for (i = 0; i < nitems(tuntap_drivers); ++i) { | ||||
tun_unrhdr = new_unrhdr(0, IF_MAXUNIT, &tunmtx); | drv = &tuntap_drivers[i]; | ||||
clone_setup(&drv->clones); | |||||
drv->unrhdr = new_unrhdr(0, IF_MAXUNIT, &tunmtx); | |||||
} | |||||
tag = EVENTHANDLER_REGISTER(dev_clone, tunclone, 0, 1000); | tag = EVENTHANDLER_REGISTER(dev_clone, tunclone, 0, 1000); | ||||
if (tag == NULL) | if (tag == NULL) | ||||
return (ENOMEM); | return (ENOMEM); | ||||
break; | break; | ||||
case MOD_UNLOAD: | case MOD_UNLOAD: | ||||
/* See tun_uninit, so it's done after the vnet_sysuninit() */ | /* See tun_uninit, so it's done after the vnet_sysuninit() */ | ||||
break; | break; | ||||
default: | default: | ||||
return EOPNOTSUPP; | return EOPNOTSUPP; | ||||
} | } | ||||
return 0; | return 0; | ||||
} | } | ||||
static moduledata_t tun_mod = { | static moduledata_t tuntap_mod = { | ||||
"if_tun", | "if_tuntap", | ||||
tunmodevent, | tuntapmodevent, | ||||
0 | 0 | ||||
}; | }; | ||||
DECLARE_MODULE(if_tun, tun_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); | DECLARE_MODULE(if_tuntap, tuntap_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); | ||||
MODULE_VERSION(if_tun, 1); | MODULE_VERSION(if_tuntap, 1); | ||||
static void | static void | ||||
tunstart(struct ifnet *ifp) | tunstart(struct ifnet *ifp) | ||||
{ | { | ||||
struct tun_softc *tp = ifp->if_softc; | struct tuntap_softc *tp = ifp->if_softc; | ||||
struct mbuf *m; | struct mbuf *m; | ||||
TUNDEBUG(ifp,"%s starting\n", ifp->if_xname); | TUNDEBUG(ifp, "starting\n"); | ||||
if (ALTQ_IS_ENABLED(&ifp->if_snd)) { | if (ALTQ_IS_ENABLED(&ifp->if_snd)) { | ||||
IFQ_LOCK(&ifp->if_snd); | IFQ_LOCK(&ifp->if_snd); | ||||
IFQ_POLL_NOLOCK(&ifp->if_snd, m); | IFQ_POLL_NOLOCK(&ifp->if_snd, m); | ||||
if (m == NULL) { | if (m == NULL) { | ||||
IFQ_UNLOCK(&ifp->if_snd); | IFQ_UNLOCK(&ifp->if_snd); | ||||
return; | return; | ||||
} | } | ||||
IFQ_UNLOCK(&ifp->if_snd); | IFQ_UNLOCK(&ifp->if_snd); | ||||
} | } | ||||
mtx_lock(&tp->tun_mtx); | TUN_LOCK(tp); | ||||
if (tp->tun_flags & TUN_RWAIT) { | if (tp->tun_flags & TUN_RWAIT) { | ||||
tp->tun_flags &= ~TUN_RWAIT; | tp->tun_flags &= ~TUN_RWAIT; | ||||
wakeup(tp); | wakeup(tp); | ||||
} | } | ||||
selwakeuppri(&tp->tun_rsel, PZERO + 1); | selwakeuppri(&tp->tun_rsel, PZERO + 1); | ||||
KNOTE_LOCKED(&tp->tun_rsel.si_note, 0); | KNOTE_LOCKED(&tp->tun_rsel.si_note, 0); | ||||
if (tp->tun_flags & TUN_ASYNC && tp->tun_sigio) { | if (tp->tun_flags & TUN_ASYNC && tp->tun_sigio) { | ||||
mtx_unlock(&tp->tun_mtx); | TUN_UNLOCK(tp); | ||||
pgsigio(&tp->tun_sigio, SIGIO, 0); | pgsigio(&tp->tun_sigio, SIGIO, 0); | ||||
} else | } else | ||||
mtx_unlock(&tp->tun_mtx); | TUN_UNLOCK(tp); | ||||
} | } | ||||
/* | |||||
* tunstart_l2 | |||||
* | |||||
* queue packets from higher level ready to put out | |||||
*/ | |||||
static void | |||||
tunstart_l2(struct ifnet *ifp) | |||||
{ | |||||
struct tuntap_softc *tp = ifp->if_softc; | |||||
TUNDEBUG(ifp, "starting\n"); | |||||
/* | |||||
* do not junk pending output if we are in VMnet mode. | |||||
* XXX: can this do any harm because of queue overflow? | |||||
*/ | |||||
TUN_LOCK(tp); | |||||
Done Inline ActionsWould it be possible to convert direct mtx_lock() calls to something like TUN_LOCK() / TUN_UNLOCK() ? melifaro: Would it be possible to convert direct mtx_lock() calls to something like TUN_LOCK() /… | |||||
if (((tp->tun_flags & TUN_VMNET) == 0) && | |||||
((tp->tun_flags & TUN_READY) != TUN_READY)) { | |||||
struct mbuf *m; | |||||
/* Unlocked read. */ | |||||
TUNDEBUG(ifp, "not ready, tun_flags = 0x%x\n", tp->tun_flags); | |||||
for (;;) { | |||||
IF_DEQUEUE(&ifp->if_snd, m); | |||||
if (m != NULL) { | |||||
m_freem(m); | |||||
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | |||||
} else | |||||
break; | |||||
} | |||||
TUN_UNLOCK(tp); | |||||
return; | |||||
} | |||||
ifp->if_drv_flags |= IFF_DRV_OACTIVE; | |||||
if (!IFQ_IS_EMPTY(&ifp->if_snd)) { | |||||
if (tp->tun_flags & TUN_RWAIT) { | |||||
tp->tun_flags &= ~TUN_RWAIT; | |||||
wakeup(tp); | |||||
} | |||||
if ((tp->tun_flags & TUN_ASYNC) && (tp->tun_sigio != NULL)) { | |||||
TUN_UNLOCK(tp); | |||||
pgsigio(&tp->tun_sigio, SIGIO, 0); | |||||
TUN_LOCK(tp); | |||||
} | |||||
selwakeuppri(&tp->tun_rsel, PZERO+1); | |||||
KNOTE_LOCKED(&tp->tun_rsel.si_note, 0); | |||||
if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); /* obytes are counted in ether_output */ | |||||
} | |||||
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; | |||||
TUN_UNLOCK(tp); | |||||
} /* tunstart_l2 */ | |||||
/* XXX: should return an error code so it can fail. */ | /* XXX: should return an error code so it can fail. */ | ||||
static void | static void | ||||
tuncreate(const char *name, struct cdev *dev) | tuncreate(struct cdev *dev, struct tuntap_driver *drv) | ||||
{ | { | ||||
struct tun_softc *sc; | struct tuntap_softc *sc; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
struct ether_addr eaddr; | |||||
int iflags; | |||||
u_char type; | |||||
sc = malloc(sizeof(*sc), M_TUN, M_WAITOK | M_ZERO); | sc = malloc(sizeof(*sc), M_TUN, M_WAITOK | M_ZERO); | ||||
mtx_init(&sc->tun_mtx, "tun_mtx", NULL, MTX_DEF); | mtx_init(&sc->tun_mtx, "tun_mtx", NULL, MTX_DEF); | ||||
cv_init(&sc->tun_cv, "tun_condvar"); | cv_init(&sc->tun_cv, "tun_condvar"); | ||||
sc->tun_flags = TUN_INITED; | sc->tun_flags = drv->tun_flags; | ||||
sc->tun_dev = dev; | sc->tun_dev = dev; | ||||
sc->tun_drv = drv; | |||||
mtx_lock(&tunmtx); | mtx_lock(&tunmtx); | ||||
TAILQ_INSERT_TAIL(&tunhead, sc, tun_list); | TAILQ_INSERT_TAIL(&tunhead, sc, tun_list); | ||||
mtx_unlock(&tunmtx); | mtx_unlock(&tunmtx); | ||||
ifp = sc->tun_ifp = if_alloc(IFT_PPP); | iflags = IFF_MULTICAST; | ||||
if ((sc->tun_flags & TUN_L2) != 0) { | |||||
type = IFT_ETHER; | |||||
iflags |= IFF_BROADCAST | IFF_SIMPLEX; | |||||
} else { | |||||
type = IFT_PPP; | |||||
iflags |= IFF_POINTOPOINT; | |||||
} | |||||
ifp = sc->tun_ifp = if_alloc(type); | |||||
if (ifp == NULL) | if (ifp == NULL) | ||||
panic("%s%d: failed to if_alloc() interface.\n", | panic("%s%d: failed to if_alloc() interface.\n", | ||||
name, dev2unit(dev)); | drv->cdevsw.d_name, dev2unit(dev)); | ||||
if_initname(ifp, name, dev2unit(dev)); | |||||
ifp->if_mtu = TUNMTU; | |||||
ifp->if_ioctl = tunifioctl; | |||||
ifp->if_output = tunoutput; | |||||
ifp->if_start = tunstart; | |||||
ifp->if_flags = IFF_POINTOPOINT | IFF_MULTICAST; | |||||
ifp->if_softc = sc; | ifp->if_softc = sc; | ||||
if_initname(ifp, drv->cdevsw.d_name, dev2unit(dev)); | |||||
ifp->if_ioctl = tunifioctl; | |||||
ifp->if_flags = iflags; | |||||
IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); | IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); | ||||
ifp->if_snd.ifq_drv_maxlen = 0; | |||||
IFQ_SET_READY(&ifp->if_snd); | |||||
knlist_init_mtx(&sc->tun_rsel.si_note, &sc->tun_mtx); | knlist_init_mtx(&sc->tun_rsel.si_note, &sc->tun_mtx); | ||||
ifp->if_capabilities |= IFCAP_LINKSTATE; | ifp->if_capabilities |= IFCAP_LINKSTATE; | ||||
ifp->if_capenable |= IFCAP_LINKSTATE; | ifp->if_capenable |= IFCAP_LINKSTATE; | ||||
if ((sc->tun_flags & TUN_L2) != 0) { | |||||
ifp->if_mtu = ETHERMTU; | |||||
ifp->if_init = tunifinit; | |||||
ifp->if_start = tunstart_l2; | |||||
ether_gen_addr(ifp, &eaddr); | |||||
ether_ifattach(ifp, eaddr.octet); | |||||
} else { | |||||
ifp->if_mtu = TUNMTU; | |||||
ifp->if_start = tunstart; | |||||
ifp->if_output = tunoutput; | |||||
ifp->if_snd.ifq_drv_maxlen = 0; | |||||
IFQ_SET_READY(&ifp->if_snd); | |||||
if_attach(ifp); | if_attach(ifp); | ||||
bpfattach(ifp, DLT_NULL, sizeof(u_int32_t)); | bpfattach(ifp, DLT_NULL, sizeof(u_int32_t)); | ||||
} | |||||
dev->si_drv1 = sc; | dev->si_drv1 = sc; | ||||
TUN_LOCK(sc); | |||||
sc->tun_flags |= TUN_INITED; | |||||
TUN_UNLOCK(sc); | |||||
TUNDEBUG(ifp, "interface %s is created, minor = %#x\n", | TUNDEBUG(ifp, "interface %s is created, minor = %#x\n", | ||||
ifp->if_xname, dev2unit(dev)); | ifp->if_xname, dev2unit(dev)); | ||||
} | } | ||||
static int | static int | ||||
tunopen(struct cdev *dev, int flag, int mode, struct thread *td) | tunopen(struct cdev *dev, int flag, int mode, struct thread *td) | ||||
{ | { | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
struct tun_softc *tp; | struct tuntap_driver *drv; | ||||
struct tuntap_softc *tp; | |||||
int error, tunflags; | |||||
tunflags = 0; | |||||
CURVNET_SET(TD_TO_VNET(td)); | |||||
error = tuntap_name2info(dev->si_name, NULL, &tunflags); | |||||
if (error != 0) { | |||||
CURVNET_RESTORE(); | |||||
return (error); /* Shouldn't happen */ | |||||
} | |||||
if ((tunflags & TUN_L2) != 0) { | |||||
/* Restrict? */ | |||||
if (tap_allow_uopen == 0) { | |||||
error = priv_check(td, PRIV_NET_TAP); | |||||
if (error != 0) { | |||||
CURVNET_RESTORE(); | |||||
return (error); | |||||
} | |||||
} | |||||
} | |||||
/* | /* | ||||
* XXXRW: Non-atomic test and set of dev->si_drv1 requires | * XXXRW: Non-atomic test and set of dev->si_drv1 requires | ||||
* synchronization. | * synchronization. | ||||
*/ | */ | ||||
tp = dev->si_drv1; | tp = dev->si_drv1; | ||||
if (!tp) { | if (!tp) { | ||||
tuncreate(tunname, dev); | drv = tuntap_driver_from_flags(tunflags); | ||||
if (drv == NULL) { | |||||
CURVNET_RESTORE(); | |||||
return (ENXIO); | |||||
} | |||||
tuncreate(dev, drv); | |||||
tp = dev->si_drv1; | tp = dev->si_drv1; | ||||
} | } | ||||
mtx_lock(&tp->tun_mtx); | TUN_LOCK(tp); | ||||
if ((tp->tun_flags & (TUN_OPEN | TUN_DYING)) != 0) { | if ((tp->tun_flags & (TUN_OPEN | TUN_DYING)) != 0) { | ||||
mtx_unlock(&tp->tun_mtx); | TUN_UNLOCK(tp); | ||||
CURVNET_RESTORE(); | |||||
return (EBUSY); | return (EBUSY); | ||||
} | } | ||||
ifp = TUN2IFP(tp); | |||||
if ((tp->tun_flags & TUN_L2) != 0) { | |||||
bcopy(IF_LLADDR(ifp), tp->tun_ether.octet, | |||||
sizeof(tp->tun_ether.octet)); | |||||
ifp->if_drv_flags |= IFF_DRV_RUNNING; | |||||
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; | |||||
if (tapuponopen) | |||||
ifp->if_flags |= IFF_UP; | |||||
} | |||||
tp->tun_pid = td->td_proc->p_pid; | tp->tun_pid = td->td_proc->p_pid; | ||||
tp->tun_flags |= TUN_OPEN; | tp->tun_flags |= TUN_OPEN; | ||||
ifp = TUN2IFP(tp); | |||||
if_link_state_change(ifp, LINK_STATE_UP); | if_link_state_change(ifp, LINK_STATE_UP); | ||||
TUNDEBUG(ifp, "open\n"); | TUNDEBUG(ifp, "open\n"); | ||||
mtx_unlock(&tp->tun_mtx); | TUN_UNLOCK(tp); | ||||
CURVNET_RESTORE(); | |||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* tunclose - close the device - mark i/f down & delete | * tunclose - close the device - mark i/f down & delete | ||||
* routing info | * routing info | ||||
*/ | */ | ||||
static int | static int | ||||
tunclose(struct cdev *dev, int foo, int bar, struct thread *td) | tunclose(struct cdev *dev, int foo, int bar, struct thread *td) | ||||
{ | { | ||||
struct tun_softc *tp; | struct tuntap_softc *tp; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
bool l2tun; | |||||
tp = dev->si_drv1; | tp = dev->si_drv1; | ||||
ifp = TUN2IFP(tp); | ifp = TUN2IFP(tp); | ||||
mtx_lock(&tp->tun_mtx); | TUN_LOCK(tp); | ||||
/* | /* | ||||
* Simply close the device if this isn't the controlling process. This | * Simply close the device if this isn't the controlling process. This | ||||
* may happen if, for instance, the tunnel has been handed off to | * may happen if, for instance, the tunnel has been handed off to | ||||
* another process. The original controller should be able to close it | * another process. The original controller should be able to close it | ||||
* without putting us into an inconsistent state. | * without putting us into an inconsistent state. | ||||
*/ | */ | ||||
if (td->td_proc->p_pid != tp->tun_pid) { | if (td->td_proc->p_pid != tp->tun_pid) { | ||||
mtx_unlock(&tp->tun_mtx); | TUN_UNLOCK(tp); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* junk all pending output | * junk all pending output | ||||
*/ | */ | ||||
CURVNET_SET(ifp->if_vnet); | CURVNET_SET(ifp->if_vnet); | ||||
l2tun = false; | |||||
if ((tp->tun_flags & TUN_L2) != 0) { | |||||
l2tun = true; | |||||
IF_DRAIN(&ifp->if_snd); | |||||
} else { | |||||
IFQ_PURGE(&ifp->if_snd); | IFQ_PURGE(&ifp->if_snd); | ||||
} | |||||
/* For vmnet, we won't do most of the address/route bits */ | |||||
if ((tp->tun_flags & TUN_VMNET) != 0) | |||||
goto out; | |||||
if (ifp->if_flags & IFF_UP) { | if (ifp->if_flags & IFF_UP) { | ||||
mtx_unlock(&tp->tun_mtx); | TUN_UNLOCK(tp); | ||||
if_down(ifp); | if_down(ifp); | ||||
mtx_lock(&tp->tun_mtx); | TUN_LOCK(tp); | ||||
} | } | ||||
/* Delete all addresses and routes which reference this interface. */ | /* Delete all addresses and routes which reference this interface. */ | ||||
if (ifp->if_drv_flags & IFF_DRV_RUNNING) { | if (ifp->if_drv_flags & IFF_DRV_RUNNING) { | ||||
struct ifaddr *ifa; | struct ifaddr *ifa; | ||||
ifp->if_drv_flags &= ~IFF_DRV_RUNNING; | ifp->if_drv_flags &= ~IFF_DRV_RUNNING; | ||||
mtx_unlock(&tp->tun_mtx); | TUN_UNLOCK(tp); | ||||
CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { | CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { | ||||
/* deal w/IPv4 PtP destination; unlocked read */ | /* deal w/IPv4 PtP destination; unlocked read */ | ||||
if (ifa->ifa_addr->sa_family == AF_INET) { | if (!l2tun && ifa->ifa_addr->sa_family == AF_INET) { | ||||
rtinit(ifa, (int)RTM_DELETE, | rtinit(ifa, (int)RTM_DELETE, | ||||
tp->tun_flags & TUN_DSTADDR ? RTF_HOST : 0); | tp->tun_flags & TUN_DSTADDR ? RTF_HOST : 0); | ||||
} else { | } else { | ||||
rtinit(ifa, (int)RTM_DELETE, 0); | rtinit(ifa, (int)RTM_DELETE, 0); | ||||
} | } | ||||
} | } | ||||
if_purgeaddrs(ifp); | if_purgeaddrs(ifp); | ||||
mtx_lock(&tp->tun_mtx); | TUN_LOCK(tp); | ||||
} | } | ||||
out: | |||||
if_link_state_change(ifp, LINK_STATE_DOWN); | if_link_state_change(ifp, LINK_STATE_DOWN); | ||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
funsetown(&tp->tun_sigio); | funsetown(&tp->tun_sigio); | ||||
selwakeuppri(&tp->tun_rsel, PZERO + 1); | selwakeuppri(&tp->tun_rsel, PZERO + 1); | ||||
KNOTE_LOCKED(&tp->tun_rsel.si_note, 0); | KNOTE_LOCKED(&tp->tun_rsel.si_note, 0); | ||||
TUNDEBUG (ifp, "closed\n"); | TUNDEBUG (ifp, "closed\n"); | ||||
tp->tun_flags &= ~TUN_OPEN; | tp->tun_flags &= ~TUN_OPEN; | ||||
tp->tun_pid = 0; | tp->tun_pid = 0; | ||||
cv_broadcast(&tp->tun_cv); | cv_broadcast(&tp->tun_cv); | ||||
mtx_unlock(&tp->tun_mtx); | TUN_UNLOCK(tp); | ||||
return (0); | return (0); | ||||
} | } | ||||
static void | static void | ||||
tuninit(struct ifnet *ifp) | tuninit(struct ifnet *ifp) | ||||
{ | { | ||||
struct tun_softc *tp = ifp->if_softc; | struct tuntap_softc *tp = ifp->if_softc; | ||||
#ifdef INET | #ifdef INET | ||||
struct ifaddr *ifa; | struct ifaddr *ifa; | ||||
#endif | #endif | ||||
TUNDEBUG(ifp, "tuninit\n"); | TUNDEBUG(ifp, "tuninit\n"); | ||||
mtx_lock(&tp->tun_mtx); | TUN_LOCK(tp); | ||||
ifp->if_flags |= IFF_UP; | |||||
ifp->if_drv_flags |= IFF_DRV_RUNNING; | ifp->if_drv_flags |= IFF_DRV_RUNNING; | ||||
if ((tp->tun_flags & TUN_L2) == 0) { | |||||
ifp->if_flags |= IFF_UP; | |||||
getmicrotime(&ifp->if_lastchange); | getmicrotime(&ifp->if_lastchange); | ||||
#ifdef INET | #ifdef INET | ||||
if_addr_rlock(ifp); | if_addr_rlock(ifp); | ||||
CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { | CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { | ||||
if (ifa->ifa_addr->sa_family == AF_INET) { | if (ifa->ifa_addr->sa_family == AF_INET) { | ||||
struct sockaddr_in *si; | struct sockaddr_in *si; | ||||
si = (struct sockaddr_in *)ifa->ifa_addr; | si = (struct sockaddr_in *)ifa->ifa_addr; | ||||
if (si->sin_addr.s_addr) | if (si->sin_addr.s_addr) | ||||
tp->tun_flags |= TUN_IASET; | tp->tun_flags |= TUN_IASET; | ||||
si = (struct sockaddr_in *)ifa->ifa_dstaddr; | si = (struct sockaddr_in *)ifa->ifa_dstaddr; | ||||
if (si && si->sin_addr.s_addr) | if (si && si->sin_addr.s_addr) | ||||
tp->tun_flags |= TUN_DSTADDR; | tp->tun_flags |= TUN_DSTADDR; | ||||
} | } | ||||
} | } | ||||
if_addr_runlock(ifp); | if_addr_runlock(ifp); | ||||
#endif | #endif | ||||
mtx_unlock(&tp->tun_mtx); | TUN_UNLOCK(tp); | ||||
} else { | |||||
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; | |||||
TUN_UNLOCK(tp); | |||||
/* attempt to start output */ | |||||
tunstart_l2(ifp); | |||||
} | } | ||||
} | |||||
/* | /* | ||||
* Used only for l2 tunnel. | |||||
*/ | |||||
static void | |||||
tunifinit(void *xtp) | |||||
{ | |||||
struct tuntap_softc *tp; | |||||
tp = (struct tuntap_softc *)xtp; | |||||
tuninit(tp->tun_ifp); | |||||
} | |||||
/* | |||||
* Process an ioctl request. | * Process an ioctl request. | ||||
*/ | */ | ||||
static int | static int | ||||
tunifioctl(struct ifnet *ifp, u_long cmd, caddr_t data) | tunifioctl(struct ifnet *ifp, u_long cmd, caddr_t data) | ||||
{ | { | ||||
struct ifreq *ifr = (struct ifreq *)data; | struct ifreq *ifr = (struct ifreq *)data; | ||||
struct tun_softc *tp; | struct tuntap_softc *tp; | ||||
struct ifstat *ifs; | struct ifstat *ifs; | ||||
int error = 0; | struct ifmediareq *ifmr; | ||||
int dummy, error = 0; | |||||
bool l2tun; | |||||
ifmr = NULL; | |||||
sx_xlock(&tun_ioctl_sx); | sx_xlock(&tun_ioctl_sx); | ||||
tp = ifp->if_softc; | tp = ifp->if_softc; | ||||
if (tp == NULL) { | if (tp == NULL) { | ||||
error = ENXIO; | error = ENXIO; | ||||
goto bad; | goto bad; | ||||
} | } | ||||
l2tun = (tp->tun_flags & TUN_L2) != 0; | |||||
switch(cmd) { | switch(cmd) { | ||||
case SIOCGIFSTATUS: | case SIOCGIFSTATUS: | ||||
ifs = (struct ifstat *)data; | ifs = (struct ifstat *)data; | ||||
mtx_lock(&tp->tun_mtx); | TUN_LOCK(tp); | ||||
if (tp->tun_pid) | if (tp->tun_pid) | ||||
snprintf(ifs->ascii, sizeof(ifs->ascii), | snprintf(ifs->ascii, sizeof(ifs->ascii), | ||||
"\tOpened by PID %d\n", tp->tun_pid); | "\tOpened by PID %d\n", tp->tun_pid); | ||||
else | else | ||||
ifs->ascii[0] = '\0'; | ifs->ascii[0] = '\0'; | ||||
mtx_unlock(&tp->tun_mtx); | TUN_UNLOCK(tp); | ||||
break; | break; | ||||
case SIOCSIFADDR: | case SIOCSIFADDR: | ||||
tuninit(ifp); | tuninit(ifp); | ||||
TUNDEBUG(ifp, "address set\n"); | TUNDEBUG(ifp, "address set\n"); | ||||
break; | break; | ||||
case SIOCSIFMTU: | case SIOCSIFMTU: | ||||
ifp->if_mtu = ifr->ifr_mtu; | ifp->if_mtu = ifr->ifr_mtu; | ||||
TUNDEBUG(ifp, "mtu set\n"); | TUNDEBUG(ifp, "mtu set\n"); | ||||
break; | break; | ||||
case SIOCSIFFLAGS: | case SIOCSIFFLAGS: | ||||
case SIOCADDMULTI: | case SIOCADDMULTI: | ||||
case SIOCDELMULTI: | case SIOCDELMULTI: | ||||
break; | break; | ||||
case SIOCGIFMEDIA: | |||||
if (!l2tun) { | |||||
error = EINVAL; | |||||
break; | |||||
} | |||||
ifmr = (struct ifmediareq *)data; | |||||
dummy = ifmr->ifm_count; | |||||
ifmr->ifm_count = 1; | |||||
ifmr->ifm_status = IFM_AVALID; | |||||
ifmr->ifm_active = IFM_ETHER; | |||||
if (tp->tun_flags & TUN_OPEN) | |||||
ifmr->ifm_status |= IFM_ACTIVE; | |||||
ifmr->ifm_current = ifmr->ifm_active; | |||||
if (dummy >= 1) { | |||||
int media = IFM_ETHER; | |||||
error = copyout(&media, ifmr->ifm_ulist, sizeof(int)); | |||||
} | |||||
break; | |||||
default: | default: | ||||
if (l2tun) { | |||||
error = ether_ioctl(ifp, cmd, data); | |||||
} else { | |||||
error = EINVAL; | error = EINVAL; | ||||
} | } | ||||
} | |||||
bad: | bad: | ||||
sx_xunlock(&tun_ioctl_sx); | sx_xunlock(&tun_ioctl_sx); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* tunoutput - queue packets from higher level ready to put out. | * tunoutput - queue packets from higher level ready to put out. | ||||
*/ | */ | ||||
static int | static int | ||||
tunoutput(struct ifnet *ifp, struct mbuf *m0, const struct sockaddr *dst, | tunoutput(struct ifnet *ifp, struct mbuf *m0, const struct sockaddr *dst, | ||||
struct route *ro) | struct route *ro) | ||||
{ | { | ||||
struct tun_softc *tp = ifp->if_softc; | struct tuntap_softc *tp = ifp->if_softc; | ||||
u_short cached_tun_flags; | u_short cached_tun_flags; | ||||
int error; | int error; | ||||
u_int32_t af; | u_int32_t af; | ||||
TUNDEBUG (ifp, "tunoutput\n"); | TUNDEBUG (ifp, "tunoutput\n"); | ||||
#ifdef MAC | #ifdef MAC | ||||
error = mac_ifnet_check_transmit(ifp, m0); | error = mac_ifnet_check_transmit(ifp, m0); | ||||
if (error) { | if (error) { | ||||
m_freem(m0); | m_freem(m0); | ||||
return (error); | return (error); | ||||
} | } | ||||
#endif | #endif | ||||
/* Could be unlocked read? */ | /* Could be unlocked read? */ | ||||
mtx_lock(&tp->tun_mtx); | TUN_LOCK(tp); | ||||
cached_tun_flags = tp->tun_flags; | cached_tun_flags = tp->tun_flags; | ||||
mtx_unlock(&tp->tun_mtx); | TUN_UNLOCK(tp); | ||||
if ((cached_tun_flags & TUN_READY) != TUN_READY) { | if ((cached_tun_flags & TUN_READY) != TUN_READY) { | ||||
TUNDEBUG (ifp, "not ready 0%o\n", tp->tun_flags); | TUNDEBUG (ifp, "not ready 0%o\n", tp->tun_flags); | ||||
m_freem (m0); | m_freem (m0); | ||||
return (EHOSTDOWN); | return (EHOSTDOWN); | ||||
} | } | ||||
if ((ifp->if_flags & IFF_UP) != IFF_UP) { | if ((ifp->if_flags & IFF_UP) != IFF_UP) { | ||||
m_freem (m0); | m_freem (m0); | ||||
▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* the cdevsw interface is now pretty minimal. | * the cdevsw interface is now pretty minimal. | ||||
*/ | */ | ||||
static int | static int | ||||
tunioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, | tunioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, | ||||
struct thread *td) | struct thread *td) | ||||
{ | { | ||||
struct ifreq ifr; | struct ifreq ifr, *ifrp; | ||||
struct tun_softc *tp = dev->si_drv1; | struct tuntap_softc *tp = dev->si_drv1; | ||||
struct tuninfo *tunp; | struct tuninfo *tunp; | ||||
int error; | int error, iflags; | ||||
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ | |||||
defined(COMPAT_FREEBSD4) | |||||
int ival; | |||||
#endif | |||||
bool l2tun; | |||||
l2tun = (tp->tun_flags & TUN_L2) != 0; | |||||
if (l2tun) { | |||||
/* tap specific ioctls */ | |||||
switch (cmd) { | switch(cmd) { | ||||
case TAPGIFNAME: | |||||
ifrp = (struct ifreq *)data; | |||||
strlcpy(ifrp->ifr_name, TUN2IFP(tp)->if_xname, | |||||
IFNAMSIZ); | |||||
return (0); | |||||
/* VMware/VMnet port ioctl's */ | |||||
#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 */ | |||||
iflags = *(int *)data; | |||||
Done Inline ActionsWould it be possible to use more descriptive variable name and a named bitmask? melifaro: Would it be possible to use more descriptive variable name and a named bitmask? | |||||
Done Inline ActionsAh, sorry, I missed this one- I'll do this in the next round. kevans: Ah, sorry, I missed this one- I'll do this in the next round. | |||||
iflags &= TUN_VMIO_FLAG_MASK; | |||||
iflags &= ~IFF_CANTCHANGE; | |||||
iflags |= IFF_UP; | |||||
TUN_LOCK(tp); | |||||
TUN2IFP(tp)->if_flags = iflags | | |||||
(TUN2IFP(tp)->if_flags & IFF_CANTCHANGE); | |||||
TUN_UNLOCK(tp); | |||||
return (0); | |||||
case SIOCGIFADDR: /* get MAC address of the remote side */ | |||||
TUN_LOCK(tp); | |||||
bcopy(&tp->tun_ether.octet, data, | |||||
sizeof(tp->tun_ether.octet)); | |||||
TUN_UNLOCK(tp); | |||||
return (0); | |||||
case SIOCSIFADDR: /* set MAC address of the remote side */ | |||||
TUN_LOCK(tp); | |||||
bcopy(data, &tp->tun_ether.octet, | |||||
sizeof(tp->tun_ether.octet)); | |||||
TUN_UNLOCK(tp); | |||||
return (0); | |||||
} | |||||
/* Fall through to the common ioctls if unhandled */ | |||||
} | |||||
switch (cmd) { | |||||
case TUNSIFINFO: | case TUNSIFINFO: | ||||
tunp = (struct tuninfo *)data; | tunp = (struct tuninfo *)data; | ||||
if (TUN2IFP(tp)->if_type != tunp->type) | if (TUN2IFP(tp)->if_type != tunp->type) | ||||
return (EPROTOTYPE); | return (EPROTOTYPE); | ||||
mtx_lock(&tp->tun_mtx); | TUN_LOCK(tp); | ||||
if (TUN2IFP(tp)->if_mtu != tunp->mtu) { | if (TUN2IFP(tp)->if_mtu != tunp->mtu) { | ||||
strlcpy(ifr.ifr_name, if_name(TUN2IFP(tp)), IFNAMSIZ); | strlcpy(ifr.ifr_name, if_name(TUN2IFP(tp)), IFNAMSIZ); | ||||
ifr.ifr_mtu = tunp->mtu; | ifr.ifr_mtu = tunp->mtu; | ||||
CURVNET_SET(TUN2IFP(tp)->if_vnet); | CURVNET_SET(TUN2IFP(tp)->if_vnet); | ||||
error = ifhwioctl(SIOCSIFMTU, TUN2IFP(tp), | error = ifhwioctl(SIOCSIFMTU, TUN2IFP(tp), | ||||
(caddr_t)&ifr, td); | (caddr_t)&ifr, td); | ||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
if (error) { | if (error) { | ||||
mtx_unlock(&tp->tun_mtx); | TUN_UNLOCK(tp); | ||||
return (error); | return (error); | ||||
} | } | ||||
} | } | ||||
TUN2IFP(tp)->if_baudrate = tunp->baudrate; | TUN2IFP(tp)->if_baudrate = tunp->baudrate; | ||||
mtx_unlock(&tp->tun_mtx); | TUN_UNLOCK(tp); | ||||
break; | break; | ||||
case TUNGIFINFO: | case TUNGIFINFO: | ||||
tunp = (struct tuninfo *)data; | tunp = (struct tuninfo *)data; | ||||
mtx_lock(&tp->tun_mtx); | TUN_LOCK(tp); | ||||
tunp->mtu = TUN2IFP(tp)->if_mtu; | tunp->mtu = TUN2IFP(tp)->if_mtu; | ||||
tunp->type = TUN2IFP(tp)->if_type; | tunp->type = TUN2IFP(tp)->if_type; | ||||
tunp->baudrate = TUN2IFP(tp)->if_baudrate; | tunp->baudrate = TUN2IFP(tp)->if_baudrate; | ||||
mtx_unlock(&tp->tun_mtx); | TUN_UNLOCK(tp); | ||||
break; | break; | ||||
case TUNSDEBUG: | case TUNSDEBUG: | ||||
tundebug = *(int *)data; | tundebug = *(int *)data; | ||||
break; | break; | ||||
case TUNGDEBUG: | case TUNGDEBUG: | ||||
*(int *)data = tundebug; | *(int *)data = tundebug; | ||||
break; | break; | ||||
case TUNSLMODE: | case TUNSLMODE: | ||||
mtx_lock(&tp->tun_mtx); | TUN_LOCK(tp); | ||||
if (*(int *)data) { | if (*(int *)data) { | ||||
tp->tun_flags |= TUN_LMODE; | tp->tun_flags |= TUN_LMODE; | ||||
tp->tun_flags &= ~TUN_IFHEAD; | tp->tun_flags &= ~TUN_IFHEAD; | ||||
} else | } else | ||||
tp->tun_flags &= ~TUN_LMODE; | tp->tun_flags &= ~TUN_LMODE; | ||||
mtx_unlock(&tp->tun_mtx); | TUN_UNLOCK(tp); | ||||
break; | break; | ||||
case TUNSIFHEAD: | case TUNSIFHEAD: | ||||
mtx_lock(&tp->tun_mtx); | TUN_LOCK(tp); | ||||
if (*(int *)data) { | if (*(int *)data) { | ||||
tp->tun_flags |= TUN_IFHEAD; | tp->tun_flags |= TUN_IFHEAD; | ||||
tp->tun_flags &= ~TUN_LMODE; | tp->tun_flags &= ~TUN_LMODE; | ||||
} else | } else | ||||
tp->tun_flags &= ~TUN_IFHEAD; | tp->tun_flags &= ~TUN_IFHEAD; | ||||
mtx_unlock(&tp->tun_mtx); | TUN_UNLOCK(tp); | ||||
break; | break; | ||||
case TUNGIFHEAD: | case TUNGIFHEAD: | ||||
mtx_lock(&tp->tun_mtx); | TUN_LOCK(tp); | ||||
*(int *)data = (tp->tun_flags & TUN_IFHEAD) ? 1 : 0; | *(int *)data = (tp->tun_flags & TUN_IFHEAD) ? 1 : 0; | ||||
mtx_unlock(&tp->tun_mtx); | TUN_UNLOCK(tp); | ||||
break; | break; | ||||
case TUNSIFMODE: | case TUNSIFMODE: | ||||
/* deny this if UP */ | /* deny this if UP */ | ||||
if (TUN2IFP(tp)->if_flags & IFF_UP) | if (TUN2IFP(tp)->if_flags & IFF_UP) | ||||
return(EBUSY); | return(EBUSY); | ||||
switch (*(int *)data & ~IFF_MULTICAST) { | switch (*(int *)data & ~IFF_MULTICAST) { | ||||
case IFF_POINTOPOINT: | case IFF_POINTOPOINT: | ||||
case IFF_BROADCAST: | case IFF_BROADCAST: | ||||
mtx_lock(&tp->tun_mtx); | TUN_LOCK(tp); | ||||
TUN2IFP(tp)->if_flags &= | TUN2IFP(tp)->if_flags &= | ||||
~(IFF_BROADCAST|IFF_POINTOPOINT|IFF_MULTICAST); | ~(IFF_BROADCAST|IFF_POINTOPOINT|IFF_MULTICAST); | ||||
TUN2IFP(tp)->if_flags |= *(int *)data; | TUN2IFP(tp)->if_flags |= *(int *)data; | ||||
mtx_unlock(&tp->tun_mtx); | TUN_UNLOCK(tp); | ||||
break; | break; | ||||
default: | default: | ||||
return(EINVAL); | return(EINVAL); | ||||
} | } | ||||
break; | break; | ||||
case TUNSIFPID: | case TUNSIFPID: | ||||
mtx_lock(&tp->tun_mtx); | TUN_LOCK(tp); | ||||
tp->tun_pid = curthread->td_proc->p_pid; | tp->tun_pid = curthread->td_proc->p_pid; | ||||
mtx_unlock(&tp->tun_mtx); | TUN_UNLOCK(tp); | ||||
break; | break; | ||||
case FIONBIO: | case FIONBIO: | ||||
break; | break; | ||||
case FIOASYNC: | case FIOASYNC: | ||||
mtx_lock(&tp->tun_mtx); | TUN_LOCK(tp); | ||||
if (*(int *)data) | if (*(int *)data) | ||||
tp->tun_flags |= TUN_ASYNC; | tp->tun_flags |= TUN_ASYNC; | ||||
else | else | ||||
tp->tun_flags &= ~TUN_ASYNC; | tp->tun_flags &= ~TUN_ASYNC; | ||||
mtx_unlock(&tp->tun_mtx); | TUN_UNLOCK(tp); | ||||
break; | break; | ||||
case FIONREAD: | case FIONREAD: | ||||
if (!IFQ_IS_EMPTY(&TUN2IFP(tp)->if_snd)) { | if (!IFQ_IS_EMPTY(&TUN2IFP(tp)->if_snd)) { | ||||
struct mbuf *mb; | struct mbuf *mb; | ||||
IFQ_LOCK(&TUN2IFP(tp)->if_snd); | IFQ_LOCK(&TUN2IFP(tp)->if_snd); | ||||
IFQ_POLL_NOLOCK(&TUN2IFP(tp)->if_snd, mb); | IFQ_POLL_NOLOCK(&TUN2IFP(tp)->if_snd, mb); | ||||
for (*(int *)data = 0; mb != NULL; mb = mb->m_next) | for (*(int *)data = 0; mb != NULL; mb = mb->m_next) | ||||
*(int *)data += mb->m_len; | *(int *)data += mb->m_len; | ||||
Show All 25 Lines | |||||
/* | /* | ||||
* The cdevsw read interface - reads a packet at a time, or at | * The cdevsw read interface - reads a packet at a time, or at | ||||
* least as much of a packet as can be read. | * least as much of a packet as can be read. | ||||
*/ | */ | ||||
static int | static int | ||||
tunread(struct cdev *dev, struct uio *uio, int flag) | tunread(struct cdev *dev, struct uio *uio, int flag) | ||||
{ | { | ||||
struct tun_softc *tp = dev->si_drv1; | struct tuntap_softc *tp = dev->si_drv1; | ||||
struct ifnet *ifp = TUN2IFP(tp); | struct ifnet *ifp = TUN2IFP(tp); | ||||
struct mbuf *m; | struct mbuf *m; | ||||
int error=0, len; | int error=0, len; | ||||
TUNDEBUG (ifp, "read\n"); | TUNDEBUG (ifp, "read\n"); | ||||
mtx_lock(&tp->tun_mtx); | TUN_LOCK(tp); | ||||
if ((tp->tun_flags & TUN_READY) != TUN_READY) { | if ((tp->tun_flags & TUN_READY) != TUN_READY) { | ||||
mtx_unlock(&tp->tun_mtx); | TUN_UNLOCK(tp); | ||||
TUNDEBUG (ifp, "not ready 0%o\n", tp->tun_flags); | TUNDEBUG (ifp, "not ready 0%o\n", tp->tun_flags); | ||||
return (EHOSTDOWN); | return (EHOSTDOWN); | ||||
} | } | ||||
tp->tun_flags &= ~TUN_RWAIT; | tp->tun_flags &= ~TUN_RWAIT; | ||||
do { | do { | ||||
IFQ_DEQUEUE(&ifp->if_snd, m); | IFQ_DEQUEUE(&ifp->if_snd, m); | ||||
if (m == NULL) { | if (m == NULL) { | ||||
if (flag & O_NONBLOCK) { | if (flag & O_NONBLOCK) { | ||||
mtx_unlock(&tp->tun_mtx); | TUN_UNLOCK(tp); | ||||
return (EWOULDBLOCK); | return (EWOULDBLOCK); | ||||
} | } | ||||
tp->tun_flags |= TUN_RWAIT; | tp->tun_flags |= TUN_RWAIT; | ||||
error = mtx_sleep(tp, &tp->tun_mtx, PCATCH | (PZERO + 1), | error = mtx_sleep(tp, &tp->tun_mtx, PCATCH | (PZERO + 1), | ||||
"tunread", 0); | "tunread", 0); | ||||
if (error != 0) { | if (error != 0) { | ||||
mtx_unlock(&tp->tun_mtx); | TUN_UNLOCK(tp); | ||||
return (error); | return (error); | ||||
} | } | ||||
} | } | ||||
} while (m == NULL); | } while (m == NULL); | ||||
mtx_unlock(&tp->tun_mtx); | TUN_UNLOCK(tp); | ||||
if ((tp->tun_flags & TUN_L2) != 0) | |||||
BPF_MTAP(ifp, m); | |||||
while (m && uio->uio_resid > 0 && error == 0) { | while (m && uio->uio_resid > 0 && error == 0) { | ||||
len = min(uio->uio_resid, m->m_len); | len = min(uio->uio_resid, m->m_len); | ||||
if (len != 0) | if (len != 0) | ||||
error = uiomove(mtod(m, void *), len, uio); | error = uiomove(mtod(m, void *), len, uio); | ||||
m = m_free(m); | m = m_free(m); | ||||
} | } | ||||
if (m) { | if (m) { | ||||
TUNDEBUG(ifp, "Dropping mbuf\n"); | TUNDEBUG(ifp, "Dropping mbuf\n"); | ||||
m_freem(m); | m_freem(m); | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | |||||
* the cdevsw write interface - an atomic write is a packet - or else! | |||||
*/ | |||||
static int | static int | ||||
tunwrite(struct cdev *dev, struct uio *uio, int flag) | tunwrite_l2(struct tuntap_softc *tp, struct mbuf *m) | ||||
{ | { | ||||
struct tun_softc *tp = dev->si_drv1; | struct ether_header *eh; | ||||
struct ifnet *ifp = TUN2IFP(tp); | struct ifnet *ifp; | ||||
struct mbuf *m; | |||||
uint32_t family, mru; | |||||
int isr; | |||||
TUNDEBUG(ifp, "tunwrite\n"); | ifp = TUN2IFP(tp); | ||||
if ((ifp->if_flags & IFF_UP) != IFF_UP) | /* | ||||
/* ignore silently */ | * Only pass a unicast frame to ether_input(), if it would | ||||
* actually have been received by non-virtual hardware. | |||||
*/ | |||||
if (m->m_len < sizeof(struct ether_header)) { | |||||
m_freem(m); | |||||
return (0); | return (0); | ||||
} | |||||
if (uio->uio_resid == 0) | eh = mtod(m, struct ether_header *); | ||||
return (0); | |||||
mru = TUNMRU; | if (eh && (ifp->if_flags & IFF_PROMISC) == 0 && | ||||
if (tp->tun_flags & TUN_IFHEAD) | !ETHER_IS_MULTICAST(eh->ether_dhost) && | ||||
mru += sizeof(family); | bcmp(eh->ether_dhost, IF_LLADDR(ifp), ETHER_ADDR_LEN) != 0) { | ||||
if (uio->uio_resid < 0 || uio->uio_resid > mru) { | m_freem(m); | ||||
TUNDEBUG(ifp, "len=%zd!\n", uio->uio_resid); | return (0); | ||||
return (EIO); | |||||
} | } | ||||
if ((m = m_uiotombuf(uio, M_NOWAIT, 0, 0, M_PKTHDR)) == NULL) { | /* Pass packet up to parent. */ | ||||
if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); | CURVNET_SET(ifp->if_vnet); | ||||
return (ENOBUFS); | (*ifp->if_input)(ifp, m); | ||||
CURVNET_RESTORE(); | |||||
/* ibytes are counted in parent */ | |||||
if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); | |||||
return (0); | |||||
} | } | ||||
m->m_pkthdr.rcvif = ifp; | static int | ||||
#ifdef MAC | tunwrite_l3(struct tuntap_softc *tp, struct mbuf *m) | ||||
mac_ifnet_create_mbuf(ifp, m); | { | ||||
#endif | struct ifnet *ifp; | ||||
int family, isr; | |||||
ifp = TUN2IFP(tp); | |||||
/* Could be unlocked read? */ | /* Could be unlocked read? */ | ||||
mtx_lock(&tp->tun_mtx); | TUN_LOCK(tp); | ||||
if (tp->tun_flags & TUN_IFHEAD) { | if (tp->tun_flags & TUN_IFHEAD) { | ||||
mtx_unlock(&tp->tun_mtx); | TUN_UNLOCK(tp); | ||||
if (m->m_len < sizeof(family) && | if (m->m_len < sizeof(family) && | ||||
(m = m_pullup(m, sizeof(family))) == NULL) | (m = m_pullup(m, sizeof(family))) == NULL) | ||||
return (ENOBUFS); | return (ENOBUFS); | ||||
family = ntohl(*mtod(m, u_int32_t *)); | family = ntohl(*mtod(m, u_int32_t *)); | ||||
m_adj(m, sizeof(family)); | m_adj(m, sizeof(family)); | ||||
} else { | } else { | ||||
Done Inline ActionsI may be wrong, but from a quick glance, BPF_MTAP() seem to the only difference between tunread() and tunread_l2(). IF true, maybe it is worth unifying these two? melifaro: I may be wrong, but from a quick glance, BPF_MTAP() seem to the only difference between tunread… | |||||
mtx_unlock(&tp->tun_mtx); | TUN_UNLOCK(tp); | ||||
family = AF_INET; | family = AF_INET; | ||||
} | } | ||||
BPF_MTAP2(ifp, &family, sizeof(family), m); | BPF_MTAP2(ifp, &family, sizeof(family), m); | ||||
switch (family) { | switch (family) { | ||||
#ifdef INET | #ifdef INET | ||||
case AF_INET: | case AF_INET: | ||||
Show All 15 Lines | #endif | ||||
CURVNET_SET(ifp->if_vnet); | CURVNET_SET(ifp->if_vnet); | ||||
M_SETFIB(m, ifp->if_fib); | M_SETFIB(m, ifp->if_fib); | ||||
netisr_dispatch(isr, m); | netisr_dispatch(isr, m); | ||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* the cdevsw write interface - an atomic write is a packet - or else! | |||||
*/ | |||||
static int | |||||
tunwrite(struct cdev *dev, struct uio *uio, int flag) | |||||
{ | |||||
struct tuntap_softc *tp; | |||||
struct ifnet *ifp; | |||||
struct mbuf *m; | |||||
uint32_t mru; | |||||
int align; | |||||
bool l2tun; | |||||
tp = dev->si_drv1; | |||||
ifp = TUN2IFP(tp); | |||||
TUNDEBUG(ifp, "tunwrite\n"); | |||||
if ((ifp->if_flags & IFF_UP) != IFF_UP) | |||||
/* ignore silently */ | |||||
return (0); | |||||
if (uio->uio_resid == 0) | |||||
return (0); | |||||
l2tun = (tp->tun_flags & TUN_L2) != 0; | |||||
align = 0; | |||||
mru = l2tun ? TAPMRU : TUNMRU; | |||||
if (l2tun) | |||||
align = ETHER_ALIGN; | |||||
else if ((tp->tun_flags & TUN_IFHEAD) != 0) | |||||
mru += sizeof(uint32_t); /* family */ | |||||
if (uio->uio_resid < 0 || uio->uio_resid > mru) { | |||||
TUNDEBUG(ifp, "len=%zd!\n", uio->uio_resid); | |||||
return (EIO); | |||||
} | |||||
if ((m = m_uiotombuf(uio, M_NOWAIT, 0, align, M_PKTHDR)) == NULL) { | |||||
if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); | |||||
return (ENOBUFS); | |||||
} | |||||
m->m_pkthdr.rcvif = ifp; | |||||
#ifdef MAC | |||||
mac_ifnet_create_mbuf(ifp, m); | |||||
#endif | |||||
if (l2tun) | |||||
return (tunwrite_l2(tp, m)); | |||||
return (tunwrite_l3(tp, m)); | |||||
} | |||||
/* | |||||
* tunpoll - the poll interface, this is only useful on reads | * tunpoll - the poll interface, this is only useful on reads | ||||
* really. The write detect always returns true, write never blocks | * really. The write detect always returns true, write never blocks | ||||
* anyway, it either accepts the packet or drops it. | * anyway, it either accepts the packet or drops it. | ||||
*/ | */ | ||||
static int | static int | ||||
tunpoll(struct cdev *dev, int events, struct thread *td) | tunpoll(struct cdev *dev, int events, struct thread *td) | ||||
{ | { | ||||
struct tun_softc *tp = dev->si_drv1; | struct tuntap_softc *tp = dev->si_drv1; | ||||
struct ifnet *ifp = TUN2IFP(tp); | struct ifnet *ifp = TUN2IFP(tp); | ||||
int revents = 0; | int revents = 0; | ||||
struct mbuf *m; | |||||
TUNDEBUG(ifp, "tunpoll\n"); | TUNDEBUG(ifp, "tunpoll\n"); | ||||
if (events & (POLLIN | POLLRDNORM)) { | if (events & (POLLIN | POLLRDNORM)) { | ||||
IFQ_LOCK(&ifp->if_snd); | IFQ_LOCK(&ifp->if_snd); | ||||
IFQ_POLL_NOLOCK(&ifp->if_snd, m); | if (!IFQ_IS_EMPTY(&ifp->if_snd)) { | ||||
if (m != NULL) { | |||||
TUNDEBUG(ifp, "tunpoll q=%d\n", ifp->if_snd.ifq_len); | TUNDEBUG(ifp, "tunpoll q=%d\n", ifp->if_snd.ifq_len); | ||||
revents |= events & (POLLIN | POLLRDNORM); | revents |= events & (POLLIN | POLLRDNORM); | ||||
} else { | } else { | ||||
TUNDEBUG(ifp, "tunpoll waiting\n"); | TUNDEBUG(ifp, "tunpoll waiting\n"); | ||||
selrecord(td, &tp->tun_rsel); | selrecord(td, &tp->tun_rsel); | ||||
} | } | ||||
IFQ_UNLOCK(&ifp->if_snd); | IFQ_UNLOCK(&ifp->if_snd); | ||||
} | } | ||||
if (events & (POLLOUT | POLLWRNORM)) | if (events & (POLLOUT | POLLWRNORM)) | ||||
revents |= events & (POLLOUT | POLLWRNORM); | revents |= events & (POLLOUT | POLLWRNORM); | ||||
return (revents); | return (revents); | ||||
} | } | ||||
/* | /* | ||||
* tunkqfilter - support for the kevent() system call. | * tunkqfilter - support for the kevent() system call. | ||||
*/ | */ | ||||
static int | static int | ||||
tunkqfilter(struct cdev *dev, struct knote *kn) | tunkqfilter(struct cdev *dev, struct knote *kn) | ||||
{ | { | ||||
struct tun_softc *tp = dev->si_drv1; | struct tuntap_softc *tp = dev->si_drv1; | ||||
struct ifnet *ifp = TUN2IFP(tp); | struct ifnet *ifp = TUN2IFP(tp); | ||||
switch(kn->kn_filter) { | switch(kn->kn_filter) { | ||||
case EVFILT_READ: | case EVFILT_READ: | ||||
TUNDEBUG(ifp, "%s kqfilter: EVFILT_READ, minor = %#x\n", | TUNDEBUG(ifp, "%s kqfilter: EVFILT_READ, minor = %#x\n", | ||||
ifp->if_xname, dev2unit(dev)); | ifp->if_xname, dev2unit(dev)); | ||||
kn->kn_fop = &tun_read_filterops; | kn->kn_fop = &tun_read_filterops; | ||||
break; | break; | ||||
Show All 18 Lines | |||||
/* | /* | ||||
* Return true of there is data in the interface queue. | * Return true of there is data in the interface queue. | ||||
*/ | */ | ||||
static int | static int | ||||
tunkqread(struct knote *kn, long hint) | tunkqread(struct knote *kn, long hint) | ||||
{ | { | ||||
int ret; | int ret; | ||||
struct tun_softc *tp = kn->kn_hook; | struct tuntap_softc *tp = kn->kn_hook; | ||||
struct cdev *dev = tp->tun_dev; | struct cdev *dev = tp->tun_dev; | ||||
struct ifnet *ifp = TUN2IFP(tp); | struct ifnet *ifp = TUN2IFP(tp); | ||||
if ((kn->kn_data = ifp->if_snd.ifq_len) > 0) { | if ((kn->kn_data = ifp->if_snd.ifq_len) > 0) { | ||||
TUNDEBUG(ifp, | TUNDEBUG(ifp, | ||||
"%s have data in the queue. Len = %d, minor = %#x\n", | "%s have data in the queue. Len = %d, minor = %#x\n", | ||||
ifp->if_xname, ifp->if_snd.ifq_len, dev2unit(dev)); | ifp->if_xname, ifp->if_snd.ifq_len, dev2unit(dev)); | ||||
ret = 1; | ret = 1; | ||||
} else { | } else { | ||||
TUNDEBUG(ifp, | TUNDEBUG(ifp, | ||||
"%s waiting for data, minor = %#x\n", ifp->if_xname, | "%s waiting for data, minor = %#x\n", ifp->if_xname, | ||||
dev2unit(dev)); | dev2unit(dev)); | ||||
ret = 0; | ret = 0; | ||||
} | } | ||||
return (ret); | return (ret); | ||||
} | } | ||||
/* | /* | ||||
* Always can write, always return MTU in kn->data. | * Always can write, always return MTU in kn->data. | ||||
*/ | */ | ||||
static int | static int | ||||
tunkqwrite(struct knote *kn, long hint) | tunkqwrite(struct knote *kn, long hint) | ||||
{ | { | ||||
struct tun_softc *tp = kn->kn_hook; | struct tuntap_softc *tp = kn->kn_hook; | ||||
struct ifnet *ifp = TUN2IFP(tp); | struct ifnet *ifp = TUN2IFP(tp); | ||||
kn->kn_data = ifp->if_mtu; | kn->kn_data = ifp->if_mtu; | ||||
return (1); | return (1); | ||||
} | } | ||||
static void | static void | ||||
tunkqdetach(struct knote *kn) | tunkqdetach(struct knote *kn) | ||||
{ | { | ||||
struct tun_softc *tp = kn->kn_hook; | struct tuntap_softc *tp = kn->kn_hook; | ||||
knlist_remove(&tp->tun_rsel.si_note, kn, 0); | knlist_remove(&tp->tun_rsel.si_note, kn, 0); | ||||
} | } |
This tag is minimally incomplete, there are addition license conditions in this file.