diff options
Diffstat (limited to 'ext/tap-mac/tuntap/src')
-rw-r--r-- | ext/tap-mac/tuntap/src/lock.cc | 15 | ||||
-rw-r--r-- | ext/tap-mac/tuntap/src/tap/Makefile | 20 | ||||
-rw-r--r-- | ext/tap-mac/tuntap/src/tap/tap.cc | 81 | ||||
-rw-r--r-- | ext/tap-mac/tuntap/src/tap/tap.h | 14 | ||||
-rw-r--r-- | ext/tap-mac/tuntap/src/tuntap.cc | 21 | ||||
-rw-r--r-- | ext/tap-mac/tuntap/src/tuntap.h | 2 |
6 files changed, 122 insertions, 31 deletions
diff --git a/ext/tap-mac/tuntap/src/lock.cc b/ext/tap-mac/tuntap/src/lock.cc index 0da48be2..9c78783a 100644 --- a/ext/tap-mac/tuntap/src/lock.cc +++ b/ext/tap-mac/tuntap/src/lock.cc @@ -31,6 +31,8 @@ extern "C" { +#include <kern/clock.h> + #include <sys/syslog.h> #include <sys/proc.h> @@ -120,10 +122,13 @@ tt_mutex::sleep(void *cond) } void -tt_mutex::sleep(void *cond, uint64_t timeout) +tt_mutex::sleep(void *cond, uint64_t nanoseconds) { - if (lck != NULL) - lck_rw_sleep_deadline(lck, LCK_SLEEP_DEFAULT, cond, THREAD_INTERRUPTIBLE, timeout); + if (lck != NULL) { + uint64_t abstime; + nanoseconds_to_absolutetime(nanoseconds, &abstime); + lck_rw_sleep_deadline(lck, LCK_SLEEP_DEFAULT, cond, THREAD_INTERRUPTIBLE, abstime); + } } void @@ -188,9 +193,9 @@ tt_gate::sleep(void* cond) } void -tt_gate::sleep(void* cond, uint64_t timeout) +tt_gate::sleep(void* cond, uint64_t nanoseconds) { - slock.sleep(cond, timeout); + slock.sleep(cond, nanoseconds); } void diff --git a/ext/tap-mac/tuntap/src/tap/Makefile b/ext/tap-mac/tuntap/src/tap/Makefile index ee1f5457..306a86d7 100644 --- a/ext/tap-mac/tuntap/src/tap/Makefile +++ b/ext/tap-mac/tuntap/src/tap/Makefile @@ -19,18 +19,18 @@ BUNDLE_SIGNATURE = ???? BUNDLE_PACKAGETYPE = KEXT BUNDLE_VERSION = $(TAP_KEXT_VERSION) -INCLUDE = -I.. -I/System/Library/Frameworks/Kernel.framework/Headers -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk/System/Library/Frameworks/Kernel.framework/Headers -CFLAGS = -Wall -mkernel -force_cpusubtype_ALL \ - -fno-builtin -fno-stack-protector -arch i386 -arch x86_64 \ - -DKERNEL -D__APPLE__ -DKERNEL_PRIVATE -DTUNTAP_VERSION=\"$(TUNTAP_VERSION)\" \ +INCLUDE = -I.. -I/System/Library/Frameworks/Kernel.framework/Headers +CFLAGS = -Wall -Werror -mkernel -force_cpusubtype_ALL \ + -nostdinc -fno-builtin -fno-stack-protector -msoft-float -fno-common \ + -arch x86_64 \ + -DKERNEL -DAPPLE -DKERNEL_PRIVATE -DTUNTAP_VERSION=\"$(TUNTAP_VERSION)\" \ -DTAP_KEXT_VERSION=\"$(TAP_KEXT_VERSION)\" CCFLAGS = $(CFLAGS) -LDFLAGS = -Wall -mkernel -nostdlib -r -lcc_kext -arch i386 -arch x86_64 -Xlinker -kext +LDFLAGS = -Wall -Werror -arch x86_64 -Xlinker -kext -nostdlib -lkmodc++ -lkmod -lcc_kext -#CCP = g++ -#CC = gcc -CCP = ../../../../llvm-g++-Xcode4.6.2/bin/llvm-g++ -CC = ../../../../llvm-g++-Xcode4.6.2/bin/llvm-gcc +CCP = clang -x c++ +CC = clang -x c +LD = clang all: $(KMOD_BIN) bundle @@ -40,7 +40,7 @@ all: $(KMOD_BIN) bundle $(CCP) $(CCFLAGS) $(INCLUDE) -c $< -o $@ $(KMOD_BIN): $(OBJS) - $(CCP) $(LDFLAGS) -o $(KMOD_BIN) $(OBJS) + $(LD) $(LDFLAGS) -o $(KMOD_BIN) $(OBJS) bundle: $(KMOD_BIN) rm -rf $(BUNDLE_DIR)/$(BUNDLE_NAME) diff --git a/ext/tap-mac/tuntap/src/tap/tap.cc b/ext/tap-mac/tuntap/src/tap/tap.cc index 149f1e71..b348a85e 100644 --- a/ext/tap-mac/tuntap/src/tap/tap.cc +++ b/ext/tap-mac/tuntap/src/tap/tap.cc @@ -38,6 +38,8 @@ extern "C" { #include <sys/random.h> #include <sys/kern_event.h> +#include <mach/thread_policy.h> + #include <net/if_types.h> #include <net/if_arp.h> #include <net/if_dl.h> @@ -89,10 +91,25 @@ struct ifmediareq32 { #define SIOCGIFMEDIA32 _IOWR('i', 56, struct ifmediareq32) /* get net media */ #define SIOCGIFMEDIA64 _IOWR('i', 56, struct ifmediareq64) /* get net media (64-bit) */ +/* thread_policy_set is exported in Mach.kext, but commented in mach/thread_policy.h in the + * Kernel.Framework headers (why?). Add a local declaration to work around that. + */ +extern "C" { +kern_return_t thread_policy_set( + thread_t thread, + thread_policy_flavor_t flavor, + thread_policy_t policy_info, + mach_msg_type_number_t count); +} static unsigned char ETHER_BROADCAST_ADDR[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; /* members */ +tap_interface::tap_interface() { + bzero(attached_protos, sizeof(attached_protos)); + input_thread = THREAD_NULL; +} + bool tap_interface::initialize(unsigned short major, unsigned short unit) { @@ -166,6 +183,30 @@ tap_interface::initialize_interface() */ bpfattach(ifp, DLT_EN10MB, ifnet_hdrlen(ifp)); + /* Inject an empty packet to trigger the input thread calling demux(), which will unblock + * thread_sync_lock. This is part of a hack to avoid a kernel crash on re-attaching + * interfaces, see comment in shutdown_interface for more information. + */ + mbuf_t empty_mbuf; + mbuf_gethdr(MBUF_WAITOK, MBUF_TYPE_DATA, &empty_mbuf); + if (empty_mbuf != NULL) { + mbuf_pkthdr_setrcvif(empty_mbuf, ifp); + mbuf_pkthdr_setlen(empty_mbuf, 0); + mbuf_pkthdr_setheader(empty_mbuf, mbuf_data(empty_mbuf)); + mbuf_set_csum_performed(empty_mbuf, 0, 0); + if (ifnet_input(ifp, empty_mbuf, NULL) == 0) { + auto_lock l(&thread_sync_lock); + for (int i = 0; i < 100 && input_thread == THREAD_NULL; ++i) { + dprintf("input thread not found, waiting...\n"); + thread_sync_lock.sleep(&input_thread, 10000000); + } + } else { + mbuf_freem(empty_mbuf); + } + } + if (input_thread == THREAD_NULL) + dprintf("Failed to determine input thread!\n"); + return 0; } @@ -186,6 +227,36 @@ tap_interface::shutdown_interface() cleanup_interface(); unregister_interface(); + + /* There's a race condition in the kernel that may cause crashes when quickly re-attaching + * interfaces. The crash happens when the interface gets re-attached before the input thread + * for the interface managed to terminate, in which case an assert on the input_waiting flag + * to be clear triggers in ifnet_attach. The bug is really that there's no synchronization + * for terminating the input thread. To work around this, the following code does add the + * missing synchronization to wait for the input thread to terminate. Of course, threading + * primitives available to kexts are few, and I'm not aware of a way to wait for a thread to + * terminate. Hence, the code calls thread_policy_set (passing bogus parameters) in a loop, + * until it returns KERN_TERMINATED. Since this is all rather fragile, there's an upper + * limit on the loop iteratations we're willing to make, so this terminates eventually even + * if things change on the kernel side eventually. + */ + if (input_thread != THREAD_NULL) { + dprintf("Waiting for input thread...\n"); + kern_return_t result = 0; + for (int i = 0; i < 100; ++i) { + result = thread_policy_set(input_thread, -1, NULL, 0); + dprintf("thread_policy_set result: %d\n", result); + if (result == KERN_TERMINATED) { + dprintf("Input thread terminated.\n"); + thread_deallocate(input_thread); + input_thread = THREAD_NULL; + break; + } + + auto_lock l(&thread_sync_lock); + thread_sync_lock.sleep(&input_thread, 10000000); + } + } } errno_t @@ -263,6 +334,16 @@ tap_interface::if_demux(mbuf_t m, char *header, protocol_family_t *proto) dprintf("tap: if_demux\n"); + /* Make note of what input thread this interface is running on. This is part of a hack to + * avoid a crash on re-attaching interfaces, see comment in shutdown_interface for details. + */ + if (input_thread == THREAD_NULL) { + auto_lock l(&thread_sync_lock); + input_thread = current_thread(); + thread_reference(input_thread); + thread_sync_lock.wakeup(&input_thread); + } + /* size check */ if (mbuf_len(m) < sizeof(struct ether_header)) return ENOENT; diff --git a/ext/tap-mac/tuntap/src/tap/tap.h b/ext/tap-mac/tuntap/src/tap/tap.h index 72339a95..a5164d4a 100644 --- a/ext/tap-mac/tuntap/src/tap/tap.h +++ b/ext/tap-mac/tuntap/src/tap/tap.h @@ -30,12 +30,15 @@ #include "tuntap.h" -#define TAP_FAMILY_NAME ((char *) "zt") +extern "C" { -#define TAP_IF_COUNT 32 /* max number of tap interfaces */ +#include <kern/thread.h> -#define TAP_MTU 2800 +} +#define TAP_FAMILY_NAME ((char *) "zt") +#define TAP_IF_COUNT 32 /* max number of tap interfaces */ +#define TAP_MTU 2800 #define TAP_LLADDR tap_lladdr /* the mac address of our interfaces. note that the last byte will be replaced by the unit number */ @@ -52,6 +55,8 @@ class tap_manager : public tuntap_manager { /* the tap network interface */ class tap_interface : public tuntap_interface { + public: + tap_interface(); protected: /* maximum number of protocols that can be attached */ @@ -67,6 +72,9 @@ class tap_interface : public tuntap_interface { protocol_family_t proto; } attached_protos[MAX_ATTACHED_PROTOS]; + /* The input thread for the network interface. */ + thread_t input_thread; + /* initializes the interface */ virtual bool initialize(unsigned short major, unsigned short unit); diff --git a/ext/tap-mac/tuntap/src/tuntap.cc b/ext/tap-mac/tuntap/src/tuntap.cc index 7fdbb795..d0f89018 100644 --- a/ext/tap-mac/tuntap/src/tuntap.cc +++ b/ext/tap-mac/tuntap/src/tuntap.cc @@ -384,10 +384,10 @@ tuntap_interface::unregister_interface() dprintf("interface detaching\n"); /* Wait until the interface has completely been detached. */ - detach_lock.lock(); + thread_sync_lock.lock(); while (!interface_detached) - detach_lock.sleep(&interface_detached); - detach_lock.unlock(); + thread_sync_lock.sleep(&interface_detached); + thread_sync_lock.unlock(); dprintf("interface detached\n"); @@ -642,15 +642,14 @@ tuntap_interface::cdev_write(uio_t uio, int ioflag) unsigned int mlen = mbuf_maxlen(first); unsigned int chunk_len; unsigned int copied = 0; + unsigned int max_data_len = ifnet_mtu(ifp) + ifnet_hdrlen(ifp); int error; /* stuff the data into the mbuf(s) */ mb = first; while (uio_resid(uio) > 0) { /* copy a chunk. enforce mtu (don't know if this is correct behaviour) */ - // ... evidently not :) -- Adam Ierymenko <adam.ierymenko@zerotier.com> - //chunk_len = min(ifnet_mtu(ifp), min(uio_resid(uio), mlen)); - chunk_len = min(uio_resid(uio),mlen); + chunk_len = min(max_data_len - copied, min(uio_resid(uio), mlen)); error = uiomove((caddr_t) mbuf_data(mb), chunk_len, uio); if (error) { log(LOG_ERR, "tuntap: could not copy data from userspace: %d\n", error); @@ -666,9 +665,7 @@ tuntap_interface::cdev_write(uio_t uio, int ioflag) copied += chunk_len; /* if done, break the loop */ - //if (uio_resid(uio) <= 0 || copied >= ifnet_mtu(ifp)) - // break; - if (uio_resid(uio) <= 0) + if (uio_resid(uio) <= 0 || copied >= max_data_len) break; /* allocate a new mbuf if the current is filled */ @@ -956,10 +953,10 @@ tuntap_interface::if_detached() dprintf("tuntap: if_detached\n"); /* wake unregister_interface() */ - detach_lock.lock(); + thread_sync_lock.lock(); interface_detached = true; - detach_lock.wakeup(&interface_detached); - detach_lock.unlock(); + thread_sync_lock.wakeup(&interface_detached); + thread_sync_lock.unlock(); dprintf("if_detached done\n"); } diff --git a/ext/tap-mac/tuntap/src/tuntap.h b/ext/tap-mac/tuntap/src/tuntap.h index f10d4a06..d5f398d0 100644 --- a/ext/tap-mac/tuntap/src/tuntap.h +++ b/ext/tap-mac/tuntap/src/tuntap.h @@ -197,7 +197,7 @@ class tuntap_interface { /* synchronization */ tt_mutex lock; tt_mutex bpf_lock; - tt_mutex detach_lock; + tt_mutex thread_sync_lock; /* the interface structure registered */ ifnet_t ifp; |