Changeset View
Standalone View
sys/net/if_tuntap.c
Show First 20 Lines • Show All 78 Lines • ▼ Show 20 Lines | |||||
#include <net/ethernet.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_dl.h> | ||||
#include <net/if_media.h> | #include <net/if_media.h> | ||||
#include <net/if_types.h> | #include <net/if_types.h> | ||||
#include <net/if_vlan_var.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> | ||||
#include <netinet/ip.h> | |||||
#include <netinet/ip6.h> | |||||
#include <netinet6/ip6_var.h> | |||||
#include <netinet/udp.h> | |||||
#include <netinet/tcp.h> | |||||
#endif | #endif | ||||
#include <net/bpf.h> | #include <net/bpf.h> | ||||
#include <net/if_tap.h> | #include <net/if_tap.h> | ||||
#include <net/if_tun.h> | #include <net/if_tun.h> | ||||
#include <dev/virtio/network/virtio_net.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; | 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 | ||||
Show All 24 Lines | #define TUN_READY (TUN_OPEN | TUN_INITED) | ||||
struct ifnet *tun_ifp; /* the interface */ | struct ifnet *tun_ifp; /* the interface */ | ||||
struct sigio *tun_sigio; /* async I/O info */ | struct sigio *tun_sigio; /* async I/O info */ | ||||
struct tuntap_driver *tun_drv; /* appropriate driver */ | struct tuntap_driver *tun_drv; /* appropriate driver */ | ||||
struct selinfo tun_rsel; /* read select */ | struct selinfo tun_rsel; /* read select */ | ||||
struct mtx tun_mtx; /* softc field mutex */ | struct mtx tun_mtx; /* softc field mutex */ | ||||
struct cv tun_cv; /* for ref'd dev destroy */ | struct cv tun_cv; /* for ref'd dev destroy */ | ||||
struct ether_addr tun_ether; /* remote address */ | struct ether_addr tun_ether; /* remote address */ | ||||
int tun_busy; /* busy count */ | int tun_busy; /* busy count */ | ||||
int tun_vhdrlen; /* virtio-net header length */ | |||||
}; | }; | ||||
#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_LOCK(tp) mtx_lock(&(tp)->tun_mtx) | ||||
#define TUN_UNLOCK(tp) mtx_unlock(&(tp)->tun_mtx) | #define TUN_UNLOCK(tp) mtx_unlock(&(tp)->tun_mtx) | ||||
#define TUN_LOCK_ASSERT(tp) mtx_assert(&(tp)->tun_mtx, MA_OWNED); | #define TUN_LOCK_ASSERT(tp) mtx_assert(&(tp)->tun_mtx, MA_OWNED); | ||||
#define TUN_VMIO_FLAG_MASK 0x0fff | #define TUN_VMIO_FLAG_MASK 0x0fff | ||||
/* | /* | ||||
* Interface capabilities of a tap device that supports the virtio-net | |||||
* header. | |||||
*/ | |||||
#define TAP_VNET_HDR_CAPS (IFCAP_HWCSUM | IFCAP_HWCSUM_IPV6 \ | |||||
| IFCAP_VLAN_HWCSUM \ | |||||
| IFCAP_TSO | IFCAP_LRO \ | |||||
| IFCAP_VLAN_HWTSO) | |||||
#define TAP_ALL_OFFLOAD (CSUM_TSO | CSUM_TCP | CSUM_UDP |\ | |||||
CSUM_TCP_IPV6 | CSUM_UDP_IPV6) | |||||
/* | |||||
* 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 the drivers' *clones, | * the exception of tundebug, which is used unlocked, and the drivers' *clones, | ||||
* which are static after setup. | * which are static after setup. | ||||
*/ | */ | ||||
static struct mtx tunmtx; | static struct mtx tunmtx; | ||||
static eventhandler_tag arrival_tag; | static eventhandler_tag arrival_tag; | ||||
static eventhandler_tag clone_tag; | static eventhandler_tag clone_tag; | ||||
static const char tunname[] = "tun"; | static const char tunname[] = "tun"; | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | |||||
static void tunstart(struct ifnet *); | static void tunstart(struct ifnet *); | ||||
static void tunstart_l2(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 tap_clone_match(struct if_clone *ifc, const char *name); | ||||
static int vmnet_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 void tun_vnethdr_set(struct ifnet *ifp, int vhdrlen); | |||||
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 20 Lines • Show All 650 Lines • ▼ Show 20 Lines | tuncreate(struct cdev *dev, struct tuntap_driver *drv) | ||||
u_char type; | 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 = drv->ident_flags; | sc->tun_flags = drv->ident_flags; | ||||
sc->tun_dev = dev; | sc->tun_dev = dev; | ||||
sc->tun_drv = drv; | sc->tun_drv = drv; | ||||
mtx_lock(&tunmtx); | mtx_lock(&tunmtx); | ||||
kevans: Redundant -- the softc will always be allocated w/ M_ZERO. You should reset it in tunclose… | |||||
Done Inline ActionsThanks! I completely missed the pid update thing. vmaffione: Thanks! I completely missed the pid update thing. | |||||
Done Inline ActionsSorry, I actually just meant the instance in tunclose where we reset the pid to 0 -- TUNSIFPID historically doesn't mean a whole lot, besides one process opened it then handed it off to a worker process. I don't know if that should necessarily imply a vnethdr reset or not, but I'm also not that familiar with virtio stuff. kevans: Sorry, I actually just meant the instance in tunclose where we reset the pid to 0 -- TUNSIFPID… | |||||
Done Inline ActionsNo, in case of handoff to a worker process it does not make sense to remove the virtio-net header. The worker process will just deal with the tap configuration set by the other process. I removed the call on TUNSIFPID. vmaffione: No, in case of handoff to a worker process it does not make sense to remove the virtio-net… | |||||
TAILQ_INSERT_TAIL(&tunhead, sc, tun_list); | TAILQ_INSERT_TAIL(&tunhead, sc, tun_list); | ||||
mtx_unlock(&tunmtx); | mtx_unlock(&tunmtx); | ||||
iflags = IFF_MULTICAST; | iflags = IFF_MULTICAST; | ||||
if ((sc->tun_flags & TUN_L2) != 0) { | if ((sc->tun_flags & TUN_L2) != 0) { | ||||
type = IFT_ETHER; | type = IFT_ETHER; | ||||
iflags |= IFF_BROADCAST | IFF_SIMPLEX; | iflags |= IFF_BROADCAST | IFF_SIMPLEX; | ||||
} else { | } else { | ||||
▲ Show 20 Lines • Show All 246 Lines • ▼ Show 20 Lines | out: | ||||
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; | ||||
tun_vnethdr_set(ifp, 0); | |||||
tun_unbusy_locked(tp); | tun_unbusy_locked(tp); | ||||
TUN_UNLOCK(tp); | TUN_UNLOCK(tp); | ||||
return (0); | return (0); | ||||
} | } | ||||
static void | static void | ||||
tuninit(struct ifnet *ifp) | tuninit(struct ifnet *ifp) | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | |||||
tunifinit(void *xtp) | tunifinit(void *xtp) | ||||
{ | { | ||||
struct tuntap_softc *tp; | struct tuntap_softc *tp; | ||||
tp = (struct tuntap_softc *)xtp; | tp = (struct tuntap_softc *)xtp; | ||||
tuninit(tp->tun_ifp); | tuninit(tp->tun_ifp); | ||||
} | } | ||||
/* | /* | ||||
Done Inline ActionsThis multi-line comment should be reformatted to move beginning/end markers to newlines. kevans: This multi-line comment should be reformatted to move beginning/end markers to newlines. | |||||
* To be called under TUN_LOCK. Update ifp->if_hwassist according to the | |||||
* current value of ifp->if_capenable. | |||||
*/ | |||||
static void | |||||
tun_caps_changed(struct ifnet *ifp) | |||||
{ | |||||
uint64_t hwassist = 0; | |||||
Done Inline ActionsI've added TUN_LOCK_ASSERT in the interim -- do consider passing in the tuntap_softc and asserting. =-) kevans: I've added TUN_LOCK_ASSERT in the interim -- do consider passing in the tuntap_softc and… | |||||
TUN_LOCK_ASSERT((struct tuntap_softc *)ifp->if_softc); | |||||
if (ifp->if_capenable & IFCAP_TXCSUM) | |||||
hwassist |= CSUM_TCP | CSUM_UDP; | |||||
if (ifp->if_capenable & IFCAP_TXCSUM_IPV6) | |||||
hwassist |= CSUM_TCP_IPV6 | |||||
| CSUM_UDP_IPV6; | |||||
if (ifp->if_capenable & IFCAP_TSO4) | |||||
hwassist |= CSUM_IP_TSO; | |||||
if (ifp->if_capenable & IFCAP_TSO6) | |||||
hwassist |= CSUM_IP6_TSO; | |||||
ifp->if_hwassist = hwassist; | |||||
} | |||||
/* | |||||
* To be called under TUN_LOCK. Update tp->tun_vhdrlen and adjust | |||||
* if_capabilities and if_capenable as needed. | |||||
*/ | |||||
static void | |||||
tun_vnethdr_set(struct ifnet *ifp, int vhdrlen) | |||||
{ | |||||
struct tuntap_softc *tp = (struct tuntap_softc *)ifp->if_softc; | |||||
TUN_LOCK_ASSERT(tp); | |||||
if (tp->tun_vhdrlen == vhdrlen) | |||||
return; | |||||
/* | |||||
* Update if_capabilities to reflect the | |||||
* functionalities offered by the virtio-net | |||||
* header. | |||||
*/ | |||||
if (vhdrlen != 0) | |||||
ifp->if_capabilities |= | |||||
TAP_VNET_HDR_CAPS; | |||||
else | |||||
ifp->if_capabilities &= | |||||
~TAP_VNET_HDR_CAPS; | |||||
/* | |||||
* Disable any capabilities that we don't | |||||
* support anymore. | |||||
*/ | |||||
ifp->if_capenable &= ifp->if_capabilities; | |||||
tun_caps_changed(ifp); | |||||
tp->tun_vhdrlen = vhdrlen; | |||||
TUNDEBUG(ifp, "vnet_hdr_len=%d, if_capabilities=%x\n", | |||||
vhdrlen, ifp->if_capabilities); | |||||
} | |||||
/* | |||||
* 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 tuntap_softc *tp; | struct tuntap_softc *tp; | ||||
struct ifstat *ifs; | struct ifstat *ifs; | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | case SIOCGIFMEDIA: | ||||
if (tp->tun_flags & TUN_OPEN) | if (tp->tun_flags & TUN_OPEN) | ||||
ifmr->ifm_status |= IFM_ACTIVE; | ifmr->ifm_status |= IFM_ACTIVE; | ||||
ifmr->ifm_current = ifmr->ifm_active; | ifmr->ifm_current = ifmr->ifm_active; | ||||
if (dummy >= 1) { | if (dummy >= 1) { | ||||
int media = IFM_ETHER; | int media = IFM_ETHER; | ||||
error = copyout(&media, ifmr->ifm_ulist, sizeof(int)); | error = copyout(&media, ifmr->ifm_ulist, sizeof(int)); | ||||
} | } | ||||
break; | break; | ||||
case SIOCSIFCAP: | |||||
TUN_LOCK(tp); | |||||
ifp->if_capenable = ifr->ifr_reqcap; | |||||
tun_caps_changed(ifp); | |||||
TUN_UNLOCK(tp); | |||||
VLAN_CAPABILITIES(ifp); | |||||
break; | |||||
default: | default: | ||||
if (l2tun) { | if (l2tun) { | ||||
error = ether_ioctl(ifp, cmd, data); | error = ether_ioctl(ifp, cmd, data); | ||||
} else { | } else { | ||||
error = EINVAL; | error = EINVAL; | ||||
} | } | ||||
} | } | ||||
bad: | bad: | ||||
▲ Show 20 Lines • Show All 94 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, *ifrp; | struct ifreq ifr, *ifrp; | ||||
struct tuntap_softc *tp = dev->si_drv1; | struct tuntap_softc *tp = dev->si_drv1; | ||||
struct ifnet *ifp = TUN2IFP(tp); | |||||
struct tuninfo *tunp; | struct tuninfo *tunp; | ||||
int error, iflags; | int error, iflags, ival; | ||||
Done Inline ActionsThese two lines can be merged now. markj: These two lines can be merged now. | |||||
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ | |||||
defined(COMPAT_FREEBSD4) | |||||
int ival; | |||||
#endif | |||||
bool l2tun; | bool l2tun; | ||||
l2tun = (tp->tun_flags & TUN_L2) != 0; | l2tun = (tp->tun_flags & TUN_L2) != 0; | ||||
if (l2tun) { | if (l2tun) { | ||||
/* tap specific ioctls */ | /* tap specific ioctls */ | ||||
switch(cmd) { | switch(cmd) { | ||||
/* VMware/VMnet port ioctl's */ | /* VMware/VMnet port ioctl's */ | ||||
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ | #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ | ||||
defined(COMPAT_FREEBSD4) | defined(COMPAT_FREEBSD4) | ||||
case _IO('V', 0): | case _IO('V', 0): | ||||
ival = IOCPARM_IVAL(data); | ival = IOCPARM_IVAL(data); | ||||
data = (caddr_t)&ival; | data = (caddr_t)&ival; | ||||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | ||||
#endif | #endif | ||||
case VMIO_SIOCSIFFLAGS: /* VMware/VMnet SIOCSIFFLAGS */ | case VMIO_SIOCSIFFLAGS: /* VMware/VMnet SIOCSIFFLAGS */ | ||||
iflags = *(int *)data; | iflags = *(int *)data; | ||||
iflags &= TUN_VMIO_FLAG_MASK; | iflags &= TUN_VMIO_FLAG_MASK; | ||||
iflags &= ~IFF_CANTCHANGE; | iflags &= ~IFF_CANTCHANGE; | ||||
iflags |= IFF_UP; | iflags |= IFF_UP; | ||||
TUN_LOCK(tp); | TUN_LOCK(tp); | ||||
TUN2IFP(tp)->if_flags = iflags | | ifp->if_flags = iflags | | ||||
(TUN2IFP(tp)->if_flags & IFF_CANTCHANGE); | (ifp->if_flags & IFF_CANTCHANGE); | ||||
TUN_UNLOCK(tp); | TUN_UNLOCK(tp); | ||||
return (0); | return (0); | ||||
case SIOCGIFADDR: /* get MAC address of the remote side */ | case SIOCGIFADDR: /* get MAC address of the remote side */ | ||||
TUN_LOCK(tp); | TUN_LOCK(tp); | ||||
bcopy(&tp->tun_ether.octet, data, | bcopy(&tp->tun_ether.octet, data, | ||||
sizeof(tp->tun_ether.octet)); | sizeof(tp->tun_ether.octet)); | ||||
TUN_UNLOCK(tp); | TUN_UNLOCK(tp); | ||||
return (0); | return (0); | ||||
case SIOCSIFADDR: /* set MAC address of the remote side */ | case SIOCSIFADDR: /* set MAC address of the remote side */ | ||||
TUN_LOCK(tp); | TUN_LOCK(tp); | ||||
bcopy(data, &tp->tun_ether.octet, | bcopy(data, &tp->tun_ether.octet, | ||||
sizeof(tp->tun_ether.octet)); | sizeof(tp->tun_ether.octet)); | ||||
TUN_UNLOCK(tp); | TUN_UNLOCK(tp); | ||||
return (0); | return (0); | ||||
case TAPSVNETHDR: | |||||
ival = *(int *)data; | |||||
if (ival != 0 && | |||||
ival != sizeof(struct virtio_net_hdr) && | |||||
ival != sizeof(struct virtio_net_hdr_mrg_rxbuf)) { | |||||
return (EINVAL); | |||||
} | } | ||||
TUN_LOCK(tp); | |||||
tun_vnethdr_set(ifp, ival); | |||||
TUN_UNLOCK(tp); | |||||
Done Inline ActionsThis multi-line comment and the one a couple lines later should be reformatted to move start/end markers to newlines. kevans: This multi-line comment and the one a couple lines later should be reformatted to move… | |||||
return (0); | |||||
case TAPGVNETHDR: | |||||
TUN_LOCK(tp); | |||||
*(int *)data = tp->tun_vhdrlen; | |||||
TUN_UNLOCK(tp); | |||||
return (0); | |||||
} | |||||
/* Fall through to the common ioctls if unhandled */ | /* Fall through to the common ioctls if unhandled */ | ||||
} else { | } else { | ||||
switch (cmd) { | switch (cmd) { | ||||
case TUNSLMODE: | case TUNSLMODE: | ||||
TUN_LOCK(tp); | 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 | ||||
Done Inline ActionsThis section can be simplified with some slight reorganization: if (tp->tun_vhdrlen != ival) { if (tp->tun_vhdrlen == 0) ifp->if_capabilities |= TAP_VNET_HDR_CAPS; else if (ival == 0) ifp->if_capabilities &= ~TAP_VNET_HDR_CAPS; tp->tun_vhdrlen = ival; ifp->if_capenable = ifp->if_capabilities; ifp->if_hwassist = 0; if (ifp->if_capenable & IFCAP_TXCSUM) ifp->if_hwassist |= CSUM_TCP | CSUM_UDP; if (ifp->if_capenable & IFCAP_TXCSUM_IPV6) ifp->if_hwassist |= CSUM_TCP_IPV6 | CSUM_UDP_IPV6; if (ifp->if_capenable & IFCAP_TSO4) ifp->if_hwassist |= CSUM_IP_TSO; if (ifp->if_capenable & IFCAP_TSO6) ifp->if_hwassist |= CSUM_IP6_TSO; } freqlabs: This section can be simplified with some slight reorganization:
```
if (tp->tun_vhdrlen !=… | |||||
Done Inline ActionsYes, indeed, thanks. vmaffione: Yes, indeed, thanks. | |||||
tp->tun_flags &= ~TUN_LMODE; | tp->tun_flags &= ~TUN_LMODE; | ||||
TUN_UNLOCK(tp); | TUN_UNLOCK(tp); | ||||
return (0); | return (0); | ||||
case TUNSIFHEAD: | case TUNSIFHEAD: | ||||
TUN_LOCK(tp); | TUN_LOCK(tp); | ||||
if (*(int *)data) { | if (*(int *)data) { | ||||
tp->tun_flags |= TUN_IFHEAD; | tp->tun_flags |= TUN_IFHEAD; | ||||
Show All 27 Lines | case TUNSIFMODE: | ||||
default: | default: | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
return (0); | return (0); | ||||
case TUNSIFPID: | case TUNSIFPID: | ||||
TUN_LOCK(tp); | TUN_LOCK(tp); | ||||
tp->tun_pid = curthread->td_proc->p_pid; | tp->tun_pid = curthread->td_proc->p_pid; | ||||
tun_vnethdr_set(ifp, 0); | |||||
TUN_UNLOCK(tp); | TUN_UNLOCK(tp); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* Fall through to the common ioctls if unhandled */ | /* Fall through to the common ioctls if unhandled */ | ||||
} | } | ||||
switch (cmd) { | switch (cmd) { | ||||
▲ Show 20 Lines • Show All 84 Lines • ▼ Show 20 Lines | |||||
* 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 tuntap_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; | size_t len; | ||||
int error = 0; | |||||
TUNDEBUG (ifp, "read\n"); | TUNDEBUG (ifp, "read\n"); | ||||
TUN_LOCK(tp); | TUN_LOCK(tp); | ||||
if ((tp->tun_flags & TUN_READY) != TUN_READY) { | if ((tp->tun_flags & TUN_READY) != TUN_READY) { | ||||
TUN_UNLOCK(tp); | 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; | ||||
for (;;) { | for (;;) { | ||||
IFQ_DEQUEUE(&ifp->if_snd, m); | IFQ_DEQUEUE(&ifp->if_snd, m); | ||||
if (m != NULL) | if (m != NULL) | ||||
Done Inline ActionsInverting this check would further simplify things here: for (;;) { IFQ_DEQUEUE(&ifp->if_snd, m); if (m != NULL) break; if (flag & O_NONBLOCK) { TUN_UNLOCK(tp); return (EWOULDBLOCK); } tp->tun_flags |= TUN_RWAIT; error = mtx_sleep(tp, &tp->tun_mtx, PCATCH | (PZERO + 1), "tunread", 0); if (error != 0) { TUN_UNLOCK(tp); return (error); } } freqlabs: Inverting this check would further simplify things here:
```
for (;;) {
IFQ_DEQUEUE… | |||||
Done Inline ActionsI know, but I did not want to drop more unrelated changes. Maybe I should just submit those with a separate patch. vmaffione: I know, but I did not want to drop more unrelated changes. Maybe I should just submit those… | |||||
Done Inline ActionsPlease do. =) kevans: Please do. =) | |||||
Done Inline ActionsI will. vmaffione: I will. | |||||
break; | break; | ||||
if (flag & O_NONBLOCK) { | if (flag & O_NONBLOCK) { | ||||
TUN_UNLOCK(tp); | 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), | ||||
Done Inline ActionsWhy this change? markj: Why this change? | |||||
Done Inline ActionsUnrelated change, removed. vmaffione: Unrelated change, removed. | |||||
"tunread", 0); | "tunread", 0); | ||||
if (error != 0) { | if (error != 0) { | ||||
TUN_UNLOCK(tp); | TUN_UNLOCK(tp); | ||||
return (error); | return (error); | ||||
} | } | ||||
} | } | ||||
TUN_UNLOCK(tp); | TUN_UNLOCK(tp); | ||||
if ((tp->tun_flags & TUN_L2) != 0) | if ((tp->tun_flags & TUN_L2) != 0) | ||||
BPF_MTAP(ifp, m); | BPF_MTAP(ifp, m); | ||||
len = min(tp->tun_vhdrlen, uio->uio_resid); | |||||
Done Inline Actionsmin() will truncate resid to 32 bits. markj: min() will truncate resid to 32 bits. | |||||
Done Inline ActionsI've changed len to be size_t... should this do the trick? vmaffione: I've changed `len` to be size_t... should this do the trick? | |||||
if (len > 0) { | |||||
struct virtio_net_hdr_mrg_rxbuf vhdr; | |||||
bzero(&vhdr, sizeof(vhdr)); | |||||
if (m->m_pkthdr.csum_flags & TAP_ALL_OFFLOAD) { | |||||
m = virtio_net_tx_offload(ifp, m, false, &vhdr.hdr); | |||||
} | |||||
TUNDEBUG(ifp, "txvhdr: f %u, gt %u, hl %u, " | |||||
"gs %u, cs %u, co %u\n", vhdr.hdr.flags, | |||||
vhdr.hdr.gso_type, vhdr.hdr.hdr_len, | |||||
vhdr.hdr.gso_size, vhdr.hdr.csum_start, | |||||
vhdr.hdr.csum_offset); | |||||
error = uiomove(&vhdr, len, uio); | |||||
} | |||||
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); | ||||
} | } | ||||
static int | static int | ||||
tunwrite_l2(struct tuntap_softc *tp, struct mbuf *m) | tunwrite_l2(struct tuntap_softc *tp, struct mbuf *m, | ||||
struct virtio_net_hdr_mrg_rxbuf *vhdr) | |||||
{ | { | ||||
struct ether_header *eh; | struct ether_header *eh; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
ifp = TUN2IFP(tp); | ifp = TUN2IFP(tp); | ||||
/* | /* | ||||
* Only pass a unicast frame to ether_input(), if it would | * Only pass a unicast frame to ether_input(), if it would | ||||
* actually have been received by non-virtual hardware. | * actually have been received by non-virtual hardware. | ||||
*/ | */ | ||||
if (m->m_len < sizeof(struct ether_header)) { | if (m->m_len < sizeof(struct ether_header)) { | ||||
m_freem(m); | m_freem(m); | ||||
return (0); | return (0); | ||||
} | } | ||||
eh = mtod(m, struct ether_header *); | eh = mtod(m, struct ether_header *); | ||||
if (eh && (ifp->if_flags & IFF_PROMISC) == 0 && | if (eh && (ifp->if_flags & IFF_PROMISC) == 0 && | ||||
!ETHER_IS_MULTICAST(eh->ether_dhost) && | !ETHER_IS_MULTICAST(eh->ether_dhost) && | ||||
bcmp(eh->ether_dhost, IF_LLADDR(ifp), ETHER_ADDR_LEN) != 0) { | bcmp(eh->ether_dhost, IF_LLADDR(ifp), ETHER_ADDR_LEN) != 0) { | ||||
m_freem(m); | m_freem(m); | ||||
return (0); | return (0); | ||||
} | } | ||||
if (vhdr != NULL && virtio_net_rx_csum(m, &vhdr->hdr)) { | |||||
m_freem(m); | |||||
return (0); | |||||
} | |||||
/* Pass packet up to parent. */ | /* Pass packet up to parent. */ | ||||
CURVNET_SET(ifp->if_vnet); | CURVNET_SET(ifp->if_vnet); | ||||
(*ifp->if_input)(ifp, m); | (*ifp->if_input)(ifp, m); | ||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
/* ibytes are counted in parent */ | /* ibytes are counted in parent */ | ||||
if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); | if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); | ||||
return (0); | return (0); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
/* | /* | ||||
* the cdevsw write interface - an atomic write is a packet - or else! | * 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(struct cdev *dev, struct uio *uio, int flag) | ||||
{ | { | ||||
struct virtio_net_hdr_mrg_rxbuf vhdr; | |||||
struct tuntap_softc *tp; | struct tuntap_softc *tp; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
struct mbuf *m; | struct mbuf *m; | ||||
uint32_t mru; | uint32_t mru; | ||||
int align; | int align, vhdrlen, error; | ||||
Done Inline Actionserror would be more consistent with the rest of the file. freqlabs: `error` would be more consistent with the rest of the file. | |||||
bool l2tun; | bool l2tun; | ||||
tp = dev->si_drv1; | tp = dev->si_drv1; | ||||
ifp = TUN2IFP(tp); | ifp = TUN2IFP(tp); | ||||
TUNDEBUG(ifp, "tunwrite\n"); | TUNDEBUG(ifp, "tunwrite\n"); | ||||
if ((ifp->if_flags & IFF_UP) != IFF_UP) | if ((ifp->if_flags & IFF_UP) != IFF_UP) | ||||
/* ignore silently */ | /* ignore silently */ | ||||
return (0); | return (0); | ||||
if (uio->uio_resid == 0) | if (uio->uio_resid == 0) | ||||
return (0); | return (0); | ||||
l2tun = (tp->tun_flags & TUN_L2) != 0; | l2tun = (tp->tun_flags & TUN_L2) != 0; | ||||
align = 0; | |||||
mru = l2tun ? TAPMRU : TUNMRU; | mru = l2tun ? TAPMRU : TUNMRU; | ||||
if (l2tun) | vhdrlen = tp->tun_vhdrlen; | ||||
align = 0; | |||||
if (l2tun) { | |||||
align = ETHER_ALIGN; | align = ETHER_ALIGN; | ||||
else if ((tp->tun_flags & TUN_IFHEAD) != 0) | mru += vhdrlen; | ||||
} else if ((tp->tun_flags & TUN_IFHEAD) != 0) | |||||
mru += sizeof(uint32_t); /* family */ | mru += sizeof(uint32_t); /* family */ | ||||
if (uio->uio_resid < 0 || uio->uio_resid > mru) { | if (uio->uio_resid < 0 || uio->uio_resid > mru) { | ||||
TUNDEBUG(ifp, "len=%zd!\n", uio->uio_resid); | TUNDEBUG(ifp, "len=%zd!\n", uio->uio_resid); | ||||
return (EIO); | return (EIO); | ||||
} | } | ||||
if (vhdrlen > 0) { | |||||
error = uiomove(&vhdr, vhdrlen, uio); | |||||
if (error != 0) | |||||
Done Inline Actionsif (error != 0) would be more consistent with the dominant convention used in this file. freqlabs: `if (error != 0)` would be more consistent with the dominant convention used in this file. | |||||
return (error); | |||||
TUNDEBUG(ifp, "txvhdr: f %u, gt %u, hl %u, " | |||||
"gs %u, cs %u, co %u\n", vhdr.hdr.flags, | |||||
vhdr.hdr.gso_type, vhdr.hdr.hdr_len, | |||||
vhdr.hdr.gso_size, vhdr.hdr.csum_start, | |||||
vhdr.hdr.csum_offset); | |||||
} | |||||
if ((m = m_uiotombuf(uio, M_NOWAIT, 0, align, M_PKTHDR)) == NULL) { | if ((m = m_uiotombuf(uio, M_NOWAIT, 0, align, M_PKTHDR)) == NULL) { | ||||
if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); | if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); | ||||
return (ENOBUFS); | return (ENOBUFS); | ||||
} | } | ||||
m->m_pkthdr.rcvif = ifp; | m->m_pkthdr.rcvif = ifp; | ||||
#ifdef MAC | #ifdef MAC | ||||
mac_ifnet_create_mbuf(ifp, m); | mac_ifnet_create_mbuf(ifp, m); | ||||
#endif | #endif | ||||
if (l2tun) | if (l2tun) | ||||
return (tunwrite_l2(tp, m)); | return (tunwrite_l2(tp, m, vhdrlen > 0 ? &vhdr : NULL)); | ||||
return (tunwrite_l3(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. | ||||
▲ Show 20 Lines • Show All 107 Lines • Show Last 20 Lines |
Redundant -- the softc will always be allocated w/ M_ZERO. You should reset it in tunclose, though, around when we reset the tun_pid.