diff --git a/ext/bin/tap-mac/com.zerotier.tap.kext/Contents/Info.plist b/ext/bin/tap-mac/com.zerotier.tap.kext/Contents/Info.plist
new file mode 100644
index 000000000..c20eefa58
--- /dev/null
+++ b/ext/bin/tap-mac/com.zerotier.tap.kext/Contents/Info.plist
@@ -0,0 +1,36 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ English
+ CFBundleExecutable
+ tap
+ CFBundleIdentifier
+ com.zerotier.tap
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ tap
+ CFBundlePackageType
+ KEXT
+ CFBundleShortVersionString
+ 20150118
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ OSBundleLibraries
+
+ com.apple.kpi.mach
+ 8.0
+ com.apple.kpi.bsd
+ 8.0
+ com.apple.kpi.libkern
+ 8.0
+ com.apple.kpi.unsupported
+ 8.0
+
+
+
+
diff --git a/ext/bin/tap-mac/com.zerotier.tap.kext/Contents/MacOS/tap b/ext/bin/tap-mac/com.zerotier.tap.kext/Contents/MacOS/tap
new file mode 100755
index 000000000..48bf96255
Binary files /dev/null and b/ext/bin/tap-mac/com.zerotier.tap.kext/Contents/MacOS/tap differ
diff --git a/ext/bin/tap-mac/com.zerotier.tap.kext/Contents/_CodeSignature/CodeResources b/ext/bin/tap-mac/com.zerotier.tap.kext/Contents/_CodeSignature/CodeResources
new file mode 100644
index 000000000..0710b4008
--- /dev/null
+++ b/ext/bin/tap-mac/com.zerotier.tap.kext/Contents/_CodeSignature/CodeResources
@@ -0,0 +1,105 @@
+
+
+
+
+ files
+
+ files2
+
+ rules
+
+ ^Resources/
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^version.plist$
+
+
+ rules2
+
+ .*\.dSYM($|/)
+
+ weight
+ 11
+
+ ^(.*/)?\.DS_Store$
+
+ omit
+
+ weight
+ 2000
+
+ ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/
+
+ nested
+
+ weight
+ 10
+
+ ^.*
+
+ ^Info\.plist$
+
+ omit
+
+ weight
+ 20
+
+ ^PkgInfo$
+
+ omit
+
+ weight
+ 20
+
+ ^Resources/
+
+ weight
+ 20
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^[^/]+$
+
+ nested
+
+ weight
+ 10
+
+ ^embedded\.provisionprofile$
+
+ weight
+ 20
+
+ ^version\.plist$
+
+ weight
+ 20
+
+
+
+
diff --git a/ext/tap-mac/tuntap/Makefile b/ext/tap-mac/tuntap/Makefile
index 8d79577ee..53ab1a9d7 100644
--- a/ext/tap-mac/tuntap/Makefile
+++ b/ext/tap-mac/tuntap/Makefile
@@ -1,12 +1,95 @@
-TUNTAP_VERSION = 20131028
+# Lets have a version, at last!
+TUNTAP_VERSION = 20150118
+
+# BASE install directory
BASE=
all: tap.kext
+keysetup:
+ -security delete-keychain net.sf.tuntaposx.tmp
+ security create-keychain -p $$(head -c 32 /dev/urandom | hexdump -e '"%02x"') \
+ net.sf.tuntaposx.tmp
+ security set-keychain-settings -lut 60 net.sf.tuntaposx.tmp
+ security import identity.p12 -k net.sf.tuntaposx.tmp -f pkcs12 \
+ -P $$(read -sp 'identity passphrase: ' pw && echo "$$pw") -A
+ security find-identity -v net.sf.tuntaposx.tmp | \
+ awk -F \" '$$2 ~ /^Developer ID Application:/ { print $$2 }' > .signing_identity
+ security find-identity -v net.sf.tuntaposx.tmp | \
+ awk -F \" '$$2 ~ /^Developer ID Installer:/ { print $$2 }' > .installer_identity
+
+pkgbuild/%.pkg: %.kext
+ mkdir -p pkgbuild/$*_root/Library/Extensions
+ cp -pR $*.kext pkgbuild/$*_root/Library/Extensions
+ mkdir -p pkgbuild/$*_root/Library/LaunchDaemons
+ cp pkg/launchd/net.sf.tuntaposx.$*.plist pkgbuild/$*_root/Library/LaunchDaemons
+ pkgbuild --root pkgbuild/$*_root \
+ --component-plist pkg/components/$*.plist \
+ --scripts pkg/scripts/$* pkgbuild/$*.pkg
+
+tuntap_$(TUNTAP_VERSION).pkg: pkgbuild/tap.pkg pkgbuild/tun.pkg
+ productbuild --distribution pkg/distribution.xml --package-path pkgbuild \
+ --resources pkg/res.dummy \
+ tuntap_$(TUNTAP_VERSION).pkg ; \
+ pkgutil --expand tuntap_$(TUNTAP_VERSION).pkg pkgbuild/tuntap_pkg.d
+ cp -pR pkg/res/ pkgbuild/tuntap_pkg.d/Resources
+ pkgutil --flatten pkgbuild/tuntap_pkg.d tuntap_$(TUNTAP_VERSION).pkg
+ if test -s ".installer_identity"; then \
+ productsign --sign "$$(cat .installer_identity)" --keychain net.sf.tuntaposx.tmp \
+ tuntap_$(TUNTAP_VERSION).pkg tuntap_$(TUNTAP_VERSION).pkg.signed ; \
+ mv tuntap_$(TUNTAP_VERSION).pkg.signed tuntap_$(TUNTAP_VERSION).pkg ; \
+ fi
+
+pkg: tuntap_$(TUNTAP_VERSION).pkg
+ tar czf tuntap_$(TUNTAP_VERSION).tar.gz \
+ README.installer README tuntap_$(TUNTAP_VERSION).pkg
+
+# Install targets
+# They are provided for the gentoo ebuild, but should work just fine for other people as well.
+install_%_kext: %.kext
+ mkdir -p $(BASE)/Library/Extensions
+ cp -pR $*.kext $(BASE)/Library/Extensions/
+ chown -R root:wheel $(BASE)/Library/Extensions/$*.kext
+ mkdir -p $(BASE)/Library/LaunchDaemons
+ cp pkg/launchd/net.sf.tuntaposx.$*.plist $(BASE)/Library/LaunchDaemons
+ chown -R root:wheel $(BASE)/Library/LaunchDaemons/net.sf.tuntaposx.$*.plist
+
+install: install_tap_kext install_tun_kext
+
+tarball: clean
+ touch tuntap_$(TUNTAP_VERSION)_src.tar.gz
+ tar czf tuntap_$(TUNTAP_VERSION)_src.tar.gz \
+ -C .. \
+ --exclude "tuntap/identity.p12" \
+ --exclude "tuntap/tuntap_$(TUNTAP_VERSION)_src.tar.gz" \
+ --exclude "tuntap/tuntap_$(TUNTAP_VERSION).tar.gz" \
+ --exclude "tuntap/tuntap_$(TUNTAP_VERSION).pkg" \
+ --exclude "*/.*" \
+ tuntap
+
clean:
cd src/tap && make -f Makefile clean
+ cd src/tun && make -f Makefile clean
+ -rm -rf pkgbuild
+ -rm -rf tuntap_$(TUNTAP_VERSION).pkg
+ -rm -f tuntap_$(TUNTAP_VERSION).tar.gz
+ -rm -f tuntap_$(TUNTAP_VERSION)_src.tar.gz
-tap.kext:
- cd src/tap && make TUNTAP_VERSION=$(TUNTAP_VERSION) -f Makefile all
+%.kext:
+ cd src/$* && make TUNTAP_VERSION=$(TUNTAP_VERSION) -f Makefile all
+ if test -s ".signing_identity"; then \
+ codesign -fv --keychain net.sf.tuntaposx.tmp -s "$$(cat .signing_identity)" \
+ $*.kext ; \
+ fi
+
+test:
+ # configd messes with interface flags, issuing SIOCSIFFLAGS ioctls upon receiving kernel
+ # events indicating protocols have been attached and detached. Unfortunately, configd does
+ # this asynchronously, making the SIOCSIFFLAGS changes totally unpredictable when we bring
+ # our interfaces up and down in rapid succession during our tests. I haven't found a good
+ # way to suppress or handle this mess other than disabling configd temporarily.
+ killall -STOP configd
+ -PYTHONPATH=test python test/tuntap/tuntap_tests.py --tests='$(TESTS)'
+ killall -CONT configd
.PHONY: test
diff --git a/ext/tap-mac/tuntap/src/lock.cc b/ext/tap-mac/tuntap/src/lock.cc
index 0da48be22..9c78783a5 100644
--- a/ext/tap-mac/tuntap/src/lock.cc
+++ b/ext/tap-mac/tuntap/src/lock.cc
@@ -31,6 +31,8 @@
extern "C" {
+#include
+
#include
#include
@@ -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 ee1f5457f..306a86d7f 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 149f1e719..b348a85e8 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
#include
+#include
+
#include
#include
#include
@@ -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 72339a95c..a5164d4a1 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"
+extern "C" {
+
+#include
+
+}
+
#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 7fdbb7959..d0f89018b 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
- //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 f10d4a067..d5f398d01 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;