diff options
Diffstat (limited to 'tap-mac/tuntap/src')
-rw-r--r-- | tap-mac/tuntap/src/lock.cc | 201 | ||||
-rw-r--r-- | tap-mac/tuntap/src/lock.h | 160 | ||||
-rw-r--r-- | tap-mac/tuntap/src/mem.cc | 76 | ||||
-rw-r--r-- | tap-mac/tuntap/src/mem.h | 48 | ||||
-rw-r--r-- | tap-mac/tuntap/src/tap/Info.plist | 36 | ||||
-rw-r--r-- | tap-mac/tuntap/src/tap/Makefile | 60 | ||||
-rw-r--r-- | tap-mac/tuntap/src/tap/kmod.cc | 93 | ||||
-rw-r--r-- | tap-mac/tuntap/src/tap/tap.cc | 452 | ||||
-rw-r--r-- | tap-mac/tuntap/src/tap/tap.h | 103 | ||||
-rw-r--r-- | tap-mac/tuntap/src/tun/Info.plist | 36 | ||||
-rw-r--r-- | tap-mac/tuntap/src/tun/Makefile | 59 | ||||
-rw-r--r-- | tap-mac/tuntap/src/tun/kmod.cc | 93 | ||||
-rw-r--r-- | tap-mac/tuntap/src/tun/tun.cc | 424 | ||||
-rw-r--r-- | tap-mac/tuntap/src/tun/tun.h | 123 | ||||
-rw-r--r-- | tap-mac/tuntap/src/tun/tun_inet6_proto.c | 87 | ||||
-rw-r--r-- | tap-mac/tuntap/src/tun/tun_inet_proto.c | 87 | ||||
-rw-r--r-- | tap-mac/tuntap/src/tun/tun_ioctls.h | 38 | ||||
-rw-r--r-- | tap-mac/tuntap/src/tuntap.cc | 962 | ||||
-rw-r--r-- | tap-mac/tuntap/src/tuntap.h | 301 | ||||
-rw-r--r-- | tap-mac/tuntap/src/tuntap_mgr.cc | 372 | ||||
-rw-r--r-- | tap-mac/tuntap/src/util.h | 46 |
21 files changed, 3857 insertions, 0 deletions
diff --git a/tap-mac/tuntap/src/lock.cc b/tap-mac/tuntap/src/lock.cc new file mode 100644 index 00000000..0da48be2 --- /dev/null +++ b/tap-mac/tuntap/src/lock.cc @@ -0,0 +1,201 @@ +/* + * ip tunnel/ethertap device for MacOSX. + * + * Locking implementation. + */ +/* + * Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de> + * + * 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. + * 3. The name of the author may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + */ + +#include "lock.h" + +extern "C" { + +#include <sys/syslog.h> +#include <sys/proc.h> + +} + +#if 0 +#define dprintf(...) log(LOG_INFO, __VA_ARGS__) +#else +#define dprintf(...) +#endif + +/* class tt_lock */ +lck_grp_t *tt_lock::tt_lck_grp = NULL; + +bool +tt_lock::initialize() +{ + /* init if necessary */ + if (tt_lck_grp == NULL) { + dprintf("initing lock group\n"); + tt_lck_grp = lck_grp_alloc_init("tuntap locks", LCK_GRP_ATTR_NULL); + + if (tt_lck_grp == NULL) { + /* if something fails, the lock won't work */ + log(LOG_ERR, "tuntap: could not allocate locking group\n"); + return false; + } + } + + return true; +} + +void +tt_lock::shutdown() +{ + /* free the locking group */ + if (tt_lck_grp != NULL) { + dprintf("freeing lock group\n"); + lck_grp_free(tt_lck_grp); + tt_lck_grp = NULL; + } +} + +/* tt_mutex */ +tt_mutex::tt_mutex() +{ + /* fail if locking group not initialized */ + if (tt_lck_grp == NULL) + return; + + /* allocate the lock */ + lck = lck_rw_alloc_init(tt_lck_grp, NULL); + + if (lck == NULL) + log(LOG_ERR, "tuntap: could not allocate mutex\n"); +} + +tt_mutex::~tt_mutex() +{ + /* if the lock doesn't exist, this will be a no-op */ + if (lck == NULL) + return; + + /* free the lock */ + lck_rw_free(lck, tt_lck_grp); +} + +void +tt_mutex::lock() +{ + if (lck != NULL) + lck_rw_lock_exclusive(lck); +} + +void +tt_mutex::unlock() +{ + if (lck != NULL) + lck_rw_unlock_exclusive(lck); +} + +void +tt_mutex::sleep(void *cond) +{ + if (lck != NULL) + lck_rw_sleep(lck, LCK_SLEEP_DEFAULT, cond, THREAD_INTERRUPTIBLE); +} + +void +tt_mutex::sleep(void *cond, uint64_t timeout) +{ + if (lck != NULL) + lck_rw_sleep_deadline(lck, LCK_SLEEP_DEFAULT, cond, THREAD_INTERRUPTIBLE, timeout); +} + +void +tt_mutex::wakeup(void *cond) +{ + if (lck != NULL) + ::wakeup(cond); +} + +/* tt_gate */ +tt_gate::tt_gate() + : ticket_number(0), + population(0) +{ +} + +void +tt_gate::enter() +{ + /* just try to grab the lock, increase the ticket number and the population */ + auto_lock l(&slock); + ticket_number++; + population++; +} + +void +tt_gate::exit() +{ + auto_lock l(&slock); + ticket_number--; + population--; +} + +bool +tt_gate::is_anyone_in() +{ + return population != 0; +} + +unsigned int +tt_gate::get_ticket_number() +{ + return ticket_number; +} + +void +tt_gate::lock() +{ + slock.lock(); +} + +void +tt_gate::unlock() +{ + slock.unlock(); +} + +void +tt_gate::sleep(void* cond) +{ + slock.sleep(cond); +} + +void +tt_gate::sleep(void* cond, uint64_t timeout) +{ + slock.sleep(cond, timeout); +} + +void +tt_gate::wakeup(void* cond) +{ + slock.wakeup(cond); +} + diff --git a/tap-mac/tuntap/src/lock.h b/tap-mac/tuntap/src/lock.h new file mode 100644 index 00000000..51d3299a --- /dev/null +++ b/tap-mac/tuntap/src/lock.h @@ -0,0 +1,160 @@ +/* + * ip tunnel/ethertap device for MacOSX. + * + * Locking is not as straightforward for Tiger. So declare our own locking class. + */ +/* + * Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de> + * + * 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. + * 3. The name of the author may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + */ + +#ifndef __LOCK_H__ +#define __LOCK_H__ + +extern "C" { + +#include <kern/locks.h> +#include <sys/param.h> + +} + +/* our own locking class. declares the common interface of the locking primitives. */ +class tt_lock { + + protected: + /* locking group */ + static lck_grp_t *tt_lck_grp; + + public: + /* be virtual */ + virtual ~tt_lock() { }; + + /* static intialization (inits the locking group) */ + static bool initialize(); + static void shutdown(); + + /* locking */ + virtual void lock() = 0; + virtual void unlock() = 0; + + /* monitor primitives */ + virtual void sleep(void* cond) = 0; + virtual void sleep(void* cond, uint64_t) = 0; + virtual void wakeup(void* cond) = 0; +}; + +/* simple mutex */ +class tt_mutex : public tt_lock { + + private: + /* underlying darwin lock */ + lck_rw_t *lck; + + public: + tt_mutex(); + virtual ~tt_mutex(); + + void lock(); + void unlock(); + + /* monitor primitives */ + void sleep(void* cond); + void sleep(void* cond, uint64_t); + void wakeup(void* cond); +}; + +/* A very special locking class that we use to track threads that enter and leave the character + * device service functions. They call enter() before entering the actual service routinge and + * exit() when done. enter() only permits them to pass when the gate isn't locked. Furthermore, the + * gate assigns ticket numbers to everyone that passes the gate, so you can check whether more + * threads came through. See tuntap_mgr::shutdown() for how we use that stuff. + */ +class tt_gate : public tt_lock { + + private: + /* synchronization lock */ + tt_mutex slock; + /* ticket number */ + unsigned int ticket_number; + /* count of threads that are in */ + unsigned int population; + + public: + /* construct a new gate */ + tt_gate(); + + /* enter - pass the gate */ + void enter(); + /* exit - pass the gate */ + void exit(); + + /* check whether anyone is in */ + bool is_anyone_in(); + /* gets the next ticket number */ + unsigned int get_ticket_number(); + + /* lock the gate */ + void lock(); + /* unlock the gate */ + void unlock(); + + /* monitor primitives */ + void sleep(void* cond); + void sleep(void* cond, uint64_t); + void wakeup(void* cond); +}; + +/* auto_lock and auto_rwlock serve as automatic lock managers: Create an object, passing the + * tt_[rw]lock you want to lock to have it grab the lock. When the object goes out of scope, the + * destructor of the class will release the lock. + */ +class auto_lock { + + protected: + /* the lock we hold */ + tt_lock *l; + + public: + auto_lock(tt_lock *m) + : l(m) + { + lock(); + } + + ~auto_lock() + { + unlock(); + } + + void lock() + { + l->lock(); + } + + void unlock() + { + l->unlock(); + } +}; + +#endif /* __LOCK_H__ */ + diff --git a/tap-mac/tuntap/src/mem.cc b/tap-mac/tuntap/src/mem.cc new file mode 100644 index 00000000..cd3264fa --- /dev/null +++ b/tap-mac/tuntap/src/mem.cc @@ -0,0 +1,76 @@ +/* + * ip tunnel/ethertap device for MacOSX. Common functionality of tap_interface and tun_interface. + * + * Memory management implementation. + */ +/* + * Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de> + * + * 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. + * 3. The name of the author may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + */ + +#include "mem.h" + +extern "C" { + +#include <libkern/OSMalloc.h> + +} + +#if 0 +#define dprintf(...) log(LOG_INFO, __VA_ARGS__) +#else +#define dprintf(...) +#endif + +static int inited = 0; +static OSMallocTag tag; + +void +mem_initialize(const char* name) { + + if (!inited) { + tag = OSMalloc_Tagalloc(name, OSMT_DEFAULT); + inited = 1; + } +} + +void +mem_shutdown() { + + if (inited) { + OSMalloc_Tagfree(tag); + inited = 0; + } +} + +void * +mem_alloc(uint32_t size) { + + return OSMalloc(size, tag); +} + +void +mem_free(void *addr, uint32_t size) { + + OSFree(addr, size, tag); +} + diff --git a/tap-mac/tuntap/src/mem.h b/tap-mac/tuntap/src/mem.h new file mode 100644 index 00000000..4d06fd8c --- /dev/null +++ b/tap-mac/tuntap/src/mem.h @@ -0,0 +1,48 @@ +/* + * ip tunnel/ethertap device for MacOSX. Common functionality of tap_interface and tun_interface. + * + * Memory management. + */ +/* + * Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de> + * + * 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. + * 3. The name of the author may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + */ + +#ifndef __MEM_H__ +#define __MEM_H__ + +extern "C" { + +#include <stdint.h> + +} + +/* Memory manager initalization and shutdown */ +void mem_initialize(const char *name); +void mem_shutdown(); + +/* Memory allocation functions */ +void *mem_alloc(uint32_t size); +void mem_free(void *addr, uint32_t size); + +#endif /* __MEM_H__ */ + diff --git a/tap-mac/tuntap/src/tap/Info.plist b/tap-mac/tuntap/src/tap/Info.plist new file mode 100644 index 00000000..bb9b03fd --- /dev/null +++ b/tap-mac/tuntap/src/tap/Info.plist @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>@@CFBUNDLEDEVELOPMENTREGION@@</string> + <key>CFBundleExecutable</key> + <string>@@CFBUNDLEEXECUTABLE@@</string> + <key>CFBundleIdentifier</key> + <string>@@CFBUNDLEIDENTIFIER@@</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>@@CFBUNDLEEXECUTABLE@@</string> + <key>CFBundlePackageType</key> + <string>@@CFBUNDLEPACKAGETYPE@@</string> + <key>CFBundleShortVersionString</key> + <string>@@CFBUNDLEVERSION@@</string> + <key>CFBundleSignature</key> + <string>@@CFBUNDLESIGNATURE@@</string> + <key>CFBundleVersion</key> + <string>1.0</string> + <key>OSBundleLibraries</key> + <dict> + <key>com.apple.kpi.mach</key> + <string>8.0</string> + <key>com.apple.kpi.bsd</key> + <string>8.0</string> + <key>com.apple.kpi.libkern</key> + <string>8.0</string> + <key>com.apple.kpi.unsupported</key> + <string>8.0</string> + </dict> +</dict> +</plist> + diff --git a/tap-mac/tuntap/src/tap/Makefile b/tap-mac/tuntap/src/tap/Makefile new file mode 100644 index 00000000..a69b967e --- /dev/null +++ b/tap-mac/tuntap/src/tap/Makefile @@ -0,0 +1,60 @@ +# +# ethertap driver for MacOSX +# +# Makefile +# +# (c) 2004, 2005, 2006, 2007, 2008 Mattias Nissler +# + +OBJS = ../tuntap.o ../tuntap_mgr.o ../lock.o ../mem.o kmod.o tap.o +KMOD_BIN = tap +BUNDLE_DIR = ../.. +BUNDLE_NAME = tap.kext + +TAP_KEXT_VERSION = $(TUNTAP_VERSION) + +BUNDLE_REGION = English +BUNDLE_IDENTIFIER = com.zerotier.tap +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)\" \ + -DTAP_KEXT_VERSION=\"$(TAP_KEXT_VERSION)\" +CCFLAGS = $(CFLAGS) +LDFLAGS = -Wall -mkernel -nostdlib -r -lcc_kext -arch i386 -arch x86_64 -Xlinker -kext + +#CCP = g++ +CCP = $(HOME)/Code/llvm-g++-Xcode4.6.2/bin/llvm-g++ + +CC = gcc + +all: $(KMOD_BIN) bundle + +.c.o: + $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ +.cc.o: + $(CCP) $(CCFLAGS) $(INCLUDE) -c $< -o $@ + +$(KMOD_BIN): $(OBJS) + $(CCP) $(LDFLAGS) -o $(KMOD_BIN) $(OBJS) + +bundle: $(KMOD_BIN) + rm -rf $(BUNDLE_DIR)/$(BUNDLE_NAME) + mkdir -p $(BUNDLE_DIR)/$(BUNDLE_NAME)/Contents/MacOS + cp $(KMOD_BIN) $(BUNDLE_DIR)/$(BUNDLE_NAME)/Contents/MacOS + sed -e "s/@@CFBUNDLEEXECUTABLE@@/$(KMOD_BIN)/" \ + -e "s/@@CFBUNDLEDEVELOPMENTREGION@@/$(BUNDLE_REGION)/" \ + -e "s/@@CFBUNDLEIDENTIFIER@@/$(BUNDLE_IDENTIFIER)/" \ + -e "s/@@CFBUNDLESIGNATURE@@/$(BUNDLE_SIGNATURE)/" \ + -e "s/@@CFBUNDLEPACKAGETYPE@@/$(BUNDLE_PACKAGETYPE)/" \ + -e "s/@@CFBUNDLEVERSION@@/$(BUNDLE_VERSION)/" \ + Info.plist > $(BUNDLE_DIR)/$(BUNDLE_NAME)/Contents/Info.plist + +clean: + -rm -f $(OBJS) $(KMOD_BIN) + -rm -rf $(BUNDLE_DIR)/$(BUNDLE_NAME) + diff --git a/tap-mac/tuntap/src/tap/kmod.cc b/tap-mac/tuntap/src/tap/kmod.cc new file mode 100644 index 00000000..f9c4a40e --- /dev/null +++ b/tap-mac/tuntap/src/tap/kmod.cc @@ -0,0 +1,93 @@ +/* + * ethertap device for MacOSX. + * + * Kext definition (it is a mach kmod really...) + */ +/* + * Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de> + * + * 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. + * 3. The name of the author may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + */ + +#include "tap.h" +#include "mem.h" + +extern "C" { + +#include <sys/param.h> + +#include <mach/kmod.h> + +static tap_manager *mgr; + +/* + * start function. called when the kext gets loaded. + */ +static kern_return_t tap_module_start(struct kmod_info *ki, void *data) +{ + mem_initialize(TAP_FAMILY_NAME); + + /* initialize locking */ + if (!tt_lock::initialize()) + return KMOD_RETURN_FAILURE; + + /* create a tap manager that will handle the rest */ + mgr = new tap_manager(); + + if (mgr != NULL) { + if (mgr->initialize(TAP_IF_COUNT, (char *) TAP_FAMILY_NAME)) + return KMOD_RETURN_SUCCESS; + + delete mgr; + mgr = NULL; + /* clean up locking */ + tt_lock::shutdown(); + } + + return KMOD_RETURN_FAILURE; +} + +/* + * stop function. called when the kext should be unloaded. unloading can be prevented by + * returning failure + */ +static kern_return_t tap_module_stop(struct kmod_info *ki, void *data) +{ + if (mgr != NULL) { + if (!mgr->shutdown()) + return KMOD_RETURN_FAILURE; + + delete mgr; + mgr = NULL; + } + + /* clean up locking */ + tt_lock::shutdown(); + + mem_shutdown(); + + return KMOD_RETURN_SUCCESS; +} + +KMOD_DECL(tap, TAP_KEXT_VERSION) + +} + diff --git a/tap-mac/tuntap/src/tap/tap.cc b/tap-mac/tuntap/src/tap/tap.cc new file mode 100644 index 00000000..149f1e71 --- /dev/null +++ b/tap-mac/tuntap/src/tap/tap.cc @@ -0,0 +1,452 @@ +/* + * ethertap device for macosx. + * + * tap_interface class definition + */ +/* + * Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de> + * + * 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. + * 3. The name of the author may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + */ + +#include "tap.h" + +extern "C" { + +#include <sys/systm.h> +#include <sys/syslog.h> +#include <sys/param.h> +#include <sys/sockio.h> +#include <sys/random.h> +#include <sys/kern_event.h> + +#include <net/if_types.h> +#include <net/if_arp.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/dlil.h> +#include <net/ethernet.h> + +} + +#if 0 +#define dprintf(...) log(LOG_INFO, __VA_ARGS__) +#else +#define dprintf(...) +#endif + +// These declarations are missing in the Kernel.framework headers, put present in userspace :-/ +#pragma pack(4) +struct ifmediareq { + char ifm_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + int ifm_current; /* current media options */ + int ifm_mask; /* don't care mask */ + int ifm_status; /* media status */ + int ifm_active; /* active options */ + int ifm_count; /* # entries in ifm_ulist array */ + int *ifm_ulist; /* media words */ +}; + +struct ifmediareq64 { + char ifm_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + int ifm_current; /* current media options */ + int ifm_mask; /* don't care mask */ + int ifm_status; /* media status */ + int ifm_active; /* active options */ + int ifm_count; /* # entries in ifm_ulist array */ + user64_addr_t ifmu_ulist __attribute__((aligned(8))); +}; + +struct ifmediareq32 { + char ifm_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + int ifm_current; /* current media options */ + int ifm_mask; /* don't care mask */ + int ifm_status; /* media status */ + int ifm_active; /* active options */ + int ifm_count; /* # entries in ifm_ulist array */ + user32_addr_t ifmu_ulist; /* 32-bit pointer */ +}; +#pragma pack() + +#define SIOCGIFMEDIA32 _IOWR('i', 56, struct ifmediareq32) /* get net media */ +#define SIOCGIFMEDIA64 _IOWR('i', 56, struct ifmediareq64) /* get net media (64-bit) */ + + +static unsigned char ETHER_BROADCAST_ADDR[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +/* members */ +bool +tap_interface::initialize(unsigned short major, unsigned short unit) +{ + this->unit = unit; + this->family_name = TAP_FAMILY_NAME; + this->family = IFNET_FAMILY_ETHERNET; + this->type = IFT_ETHER; + bzero(unique_id, UIDLEN); + snprintf(unique_id, UIDLEN, "%s%d", family_name, unit); + + dprintf("tap: starting interface %s%d\n", TAP_FAMILY_NAME, unit); + + /* register character device */ + if (!tuntap_interface::register_chardev(major)) + return false; + + return true; +} + +void +tap_interface::shutdown() +{ + dprintf("tap: shutting down tap interface %s%d\n", TAP_FAMILY_NAME, unit); + + unregister_chardev(); +} + +int +tap_interface::initialize_interface() +{ + struct sockaddr_dl lladdr; + lladdr.sdl_len = sizeof(lladdr); + lladdr.sdl_family = AF_LINK; + lladdr.sdl_alen = ETHER_ADDR_LEN; + lladdr.sdl_nlen = lladdr.sdl_slen = 0; + + /* generate a random MAC address */ + read_random(LLADDR(&lladdr), ETHER_ADDR_LEN); + + /* clear multicast bit and set local assignment bit (see IEEE 802) */ + (LLADDR(&lladdr))[0] &= 0xfe; + (LLADDR(&lladdr))[0] |= 0x02; + + dprintf("tap: random tap address: %02x:%02x:%02x:%02x:%02x:%02x\n", + (LLADDR(&lladdr))[0] & 0xff, + (LLADDR(&lladdr))[1] & 0xff, + (LLADDR(&lladdr))[2] & 0xff, + (LLADDR(&lladdr))[3] & 0xff, + (LLADDR(&lladdr))[4] & 0xff, + (LLADDR(&lladdr))[5] & 0xff); + + /* register interface */ + if (!tuntap_interface::register_interface(&lladdr, ETHER_BROADCAST_ADDR, ETHER_ADDR_LEN)) + return EIO; + + /* Set link level address. Yes, we need to do that again. Darwin sucks. */ + errno_t err = ifnet_set_lladdr(ifp, LLADDR(&lladdr), ETHER_ADDR_LEN); + if (err) + dprintf("tap: failed to set lladdr on %s%d: %d\n", family_name, unit, err); + + /* set mtu */ + ifnet_set_mtu(ifp, TAP_MTU); + /* set header length */ + ifnet_set_hdrlen(ifp, sizeof(struct ether_header)); + /* add the broadcast flag */ + ifnet_set_flags(ifp, IFF_BROADCAST, IFF_BROADCAST); + + /* we must call bpfattach(). Otherwise we deadlock BPF while unloading. Seems to be a bug in + * the kernel, see bpfdetach() in net/bpf.c, it will return without releasing the lock if + * the interface wasn't attached. I wonder what they were smoking while writing it ;-) + */ + bpfattach(ifp, DLT_EN10MB, ifnet_hdrlen(ifp)); + + return 0; +} + +void +tap_interface::shutdown_interface() +{ + dprintf("tap: shutting down network interface of device %s%d\n", TAP_FAMILY_NAME, unit); + + /* detach all protocols */ + for (unsigned int i = 0; i < MAX_ATTACHED_PROTOS; i++) { + if (attached_protos[i].used) { + errno_t err = ifnet_detach_protocol(ifp, attached_protos[i].proto); + if (err) + log(LOG_WARNING, "tap: could not detach protocol %d from %s%d\n", + attached_protos[i].proto, TAP_FAMILY_NAME, unit); + } + } + + cleanup_interface(); + unregister_interface(); +} + +errno_t +tap_interface::if_ioctl(u_int32_t cmd, void *arg) +{ + dprintf("tap: if_ioctl cmd: %d (%x)\n", cmd & 0xff, cmd); + + switch (cmd) { + case SIOCSIFLLADDR: + { + /* set ethernet address */ + struct sockaddr *ea = &(((struct ifreq *) arg)->ifr_addr); + + dprintf("tap: SIOCSIFLLADDR family %d len %d\n", + ea->sa_family, ea->sa_len); + + /* check if it is really an ethernet address */ + if (ea->sa_family != AF_LINK || ea->sa_len != ETHER_ADDR_LEN) + return EINVAL; + + /* ok, copy */ + errno_t err = ifnet_set_lladdr(ifp, ea->sa_data, ETHER_ADDR_LEN); + if (err) { + dprintf("tap: failed to set lladdr on %s%d: %d\n", + family_name, unit, err); + return err; + } + + /* Generate a LINK_ON event. This necessary for configd to re-read + * the interface data and refresh the MAC address. Not doing so + * would result in the DHCP client using a stale MAC address... + */ + generate_link_event(KEV_DL_LINK_ON); + + return 0; + } + + case SIOCGIFMEDIA32: + case SIOCGIFMEDIA64: + { + struct ifmediareq *ifmr = (struct ifmediareq*) arg; + user_addr_t list = USER_ADDR_NULL; + + ifmr->ifm_current = IFM_ETHER; + ifmr->ifm_mask = 0; + ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE; + ifmr->ifm_active = IFM_ETHER; + ifmr->ifm_count = 1; + + if (cmd == SIOCGIFMEDIA64) + list = ((struct ifmediareq64*) ifmr)->ifmu_ulist; + else + list = CAST_USER_ADDR_T( + ((struct ifmediareq32*) ifmr)->ifmu_ulist); + + if (list != USER_ADDR_NULL) + return copyout(&ifmr->ifm_current, list, sizeof(int)); + + return 0; + } + + default: + /* let our superclass handle it */ + return tuntap_interface::if_ioctl(cmd, arg); + } + + return EOPNOTSUPP; +} + +errno_t +tap_interface::if_demux(mbuf_t m, char *header, protocol_family_t *proto) +{ + struct ether_header *eh = (struct ether_header *) header; + unsigned char lladdr[ETHER_ADDR_LEN]; + + dprintf("tap: if_demux\n"); + + /* size check */ + if (mbuf_len(m) < sizeof(struct ether_header)) + return ENOENT; + + /* catch broadcast and multicast (stolen from bsd/net/ether_if_module.c) */ + if (eh->ether_dhost[0] & 1) { + if (memcmp(ETHER_BROADCAST_ADDR, eh->ether_dhost, ETHER_ADDR_LEN) == 0) { + /* broadcast */ + dprintf("tap: broadcast packet.\n"); + mbuf_setflags_mask(m, MBUF_BCAST, MBUF_BCAST); + } else { + /* multicast */ + dprintf("tap: multicast packet.\n"); + mbuf_setflags_mask(m, MBUF_MCAST, MBUF_MCAST); + } + } else { + /* check wether the packet has our address */ + ifnet_lladdr_copy_bytes(ifp, lladdr, ETHER_ADDR_LEN); + if (memcmp(lladdr, eh->ether_dhost, ETHER_ADDR_LEN) != 0) + mbuf_setflags_mask(m, MBUF_PROMISC, MBUF_PROMISC); + } + + /* find the protocol */ + for (unsigned int i = 0; i < MAX_ATTACHED_PROTOS; i++) { + if (attached_protos[i].used && attached_protos[i].type == eh->ether_type) { + *proto = attached_protos[i].proto; + return 0; + } + } + + dprintf("tap: if_demux() failed to find proto.\n"); + + /* no matching proto found */ + return ENOENT; +} + +errno_t +tap_interface::if_framer(mbuf_t *m, const struct sockaddr *dest, const char *dest_linkaddr, + const char *frame_type) +{ + struct ether_header *eh; + mbuf_t nm = *m; + errno_t err; + + dprintf("tap: if_framer\n"); + + /* prepend the ethernet header */ + err = mbuf_prepend(&nm, sizeof (struct ether_header), MBUF_WAITOK); + if (err) { + dprintf("tap: could not prepend data to mbuf: %d\n", err); + return err; + } + *m = nm; + + /* fill the header */ + eh = (struct ether_header *) mbuf_data(*m); + memcpy(eh->ether_dhost, dest_linkaddr, ETHER_ADDR_LEN); + ifnet_lladdr_copy_bytes(ifp, eh->ether_shost, ETHER_ADDR_LEN); + eh->ether_type = *((u_int16_t *) frame_type); + + return 0; +} + +errno_t +tap_interface::if_add_proto(protocol_family_t proto, const struct ifnet_demux_desc *desc, + u_int32_t ndesc) +{ + errno_t err; + + dprintf("tap: if_add_proto proto %d\n", proto); + + for (unsigned int i = 0; i < ndesc; i++) { + /* try to add the protocol */ + err = add_one_proto(proto, desc[i]); + if (err != 0) { + /* if that fails, remove everything stored so far */ + if_del_proto(proto); + return err; + } + } + + return 0; +} + +errno_t +tap_interface::if_del_proto(protocol_family_t proto) +{ + dprintf("tap: if_del_proto proto %d\n", proto); + + /* delete all matching entries in attached_protos */ + for (unsigned int i = 0; i < MAX_ATTACHED_PROTOS; i++) { + if (attached_protos[i].proto == proto) + attached_protos[i].used = false; + } + + return 0; +} + +errno_t +tap_interface::if_check_multi(const struct sockaddr *maddr) +{ + dprintf("tap: if_check_multi family %d\n", maddr->sa_family); + + /* see whether it is a ethernet address with the multicast bit set */ + if (maddr->sa_family == AF_LINK) { + struct sockaddr_dl *dlmaddr = (struct sockaddr_dl *) maddr; + if (LLADDR(dlmaddr)[0] & 0x01) + return 0; + else + return EADDRNOTAVAIL; + } + + return EOPNOTSUPP; +} + +errno_t +tap_interface::add_one_proto(protocol_family_t proto, const struct ifnet_demux_desc &dd) +{ + int free = -1; + u_int16_t dt; + + /* we only support DLIL_DESC_ETYPE2 */ + if (dd.type != DLIL_DESC_ETYPE2 || dd.datalen != 2) { + log(LOG_WARNING, "tap: tap only supports DLIL_DESC_ETYPE2 protocols.\n"); + return EINVAL; + } + + dt = *((u_int16_t *) (dd.data)); + + /* see if the protocol is already registered */ + for (unsigned int i = 0; i < MAX_ATTACHED_PROTOS; i++) { + if (attached_protos[i].used) { + if (dt == attached_protos[i].type) { + /* already registered */ + if (attached_protos[i].proto == proto) { + /* matches the old entry */ + return 0; + } else + return EEXIST; + } + } else if (free == -1) + free = i; + } + + /* did we find a free entry? */ + if (free == -1) + /* is ENOBUFS correct? */ + return ENOBUFS; + + /* ok, save information */ + attached_protos[free].used = true; + attached_protos[free].type = dt; + attached_protos[free].proto = proto; + + return 0; +} + +/* This code is shamelessly stolen from if_bond.c */ +void +tap_interface::generate_link_event(u_int32_t code) +{ + struct { + struct kern_event_msg header; + u_int32_t unit; + char if_name[IFNAMSIZ]; + } event; + + bzero(&event, sizeof(event)); + event.header.total_size = sizeof(event); + event.header.vendor_code = KEV_VENDOR_APPLE; + event.header.kev_class = KEV_NETWORK_CLASS; + event.header.kev_subclass = KEV_DL_SUBCLASS; + event.header.event_code = code; + event.header.event_data[0] = family; + event.unit = (u_int32_t) unit; + strncpy(event.if_name, ifnet_name(ifp), IFNAMSIZ); + + ifnet_event(ifp, &event.header); +} + +/* tap_manager members */ +tuntap_interface * +tap_manager::create_interface() +{ + return new tap_interface(); +} + diff --git a/tap-mac/tuntap/src/tap/tap.h b/tap-mac/tuntap/src/tap/tap.h new file mode 100644 index 00000000..b3070676 --- /dev/null +++ b/tap-mac/tuntap/src/tap/tap.h @@ -0,0 +1,103 @@ +/* + * ethertap device for MacOSX. + */ +/* + * Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de> + * + * 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. + * 3. The name of the author may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + */ + +#ifndef __TAP_H__ +#define __TAP_H__ + +#include "tuntap.h" + +#define TAP_FAMILY_NAME ((char *) "zt") + +#define TAP_IF_COUNT 16 /* max number of tap interfaces */ + +#define TAP_MTU 4000 + +#define TAP_LLADDR tap_lladdr + +/* the mac address of our interfaces. note that the last byte will be replaced by the unit number */ +extern u_char tap_lladdr[]; + +/* tap manager */ +class tap_manager : public tuntap_manager { + + protected: + /* just define the interface creation method */ + virtual tuntap_interface *create_interface(); + +}; + +/* the tap network interface */ +class tap_interface : public tuntap_interface { + + protected: + /* maximum number of protocols that can be attached */ + static const unsigned int MAX_ATTACHED_PROTOS = 8; + + /* information about attached protocols for demuxing is stored here */ + struct { + /* whether this entry is used */ + bool used; + /* type in the ethernet header */ + u_int16_t type; + /* protocol passed to add_proto */ + protocol_family_t proto; + } attached_protos[MAX_ATTACHED_PROTOS]; + + /* initializes the interface */ + virtual bool initialize(unsigned short major, unsigned short unit); + + /* shuts the interface down */ + virtual void shutdown(); + + /* called when the character device is opened in order to intialize the network + * interface. + */ + virtual int initialize_interface(); + /* called when the character device is closed to shutdown the network interface */ + virtual void shutdown_interface(); + + /* override interface routines */ + virtual errno_t if_ioctl(u_int32_t cmd, void *arg); + virtual errno_t if_demux(mbuf_t m, char *header, protocol_family_t *proto); + virtual errno_t if_framer(mbuf_t *m, const struct sockaddr *dest, + const char *dest_linkaddr, const char *frame_type); + virtual errno_t if_add_proto(protocol_family_t proto, + const struct ifnet_demux_desc *ddesc, u_int32_t ndesc); + virtual errno_t if_del_proto(protocol_family_t proto); + virtual errno_t if_check_multi(const struct sockaddr *maddr); + + /* if_add_proto helper */ + errno_t add_one_proto(protocol_family_t proto, const struct ifnet_demux_desc &dd); + + /* generates a kernel event */ + void generate_link_event(u_int32_t code); + + friend class tap_manager; +}; + +#endif /* __TAP_H__ */ + diff --git a/tap-mac/tuntap/src/tun/Info.plist b/tap-mac/tuntap/src/tun/Info.plist new file mode 100644 index 00000000..bb9b03fd --- /dev/null +++ b/tap-mac/tuntap/src/tun/Info.plist @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>@@CFBUNDLEDEVELOPMENTREGION@@</string> + <key>CFBundleExecutable</key> + <string>@@CFBUNDLEEXECUTABLE@@</string> + <key>CFBundleIdentifier</key> + <string>@@CFBUNDLEIDENTIFIER@@</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>@@CFBUNDLEEXECUTABLE@@</string> + <key>CFBundlePackageType</key> + <string>@@CFBUNDLEPACKAGETYPE@@</string> + <key>CFBundleShortVersionString</key> + <string>@@CFBUNDLEVERSION@@</string> + <key>CFBundleSignature</key> + <string>@@CFBUNDLESIGNATURE@@</string> + <key>CFBundleVersion</key> + <string>1.0</string> + <key>OSBundleLibraries</key> + <dict> + <key>com.apple.kpi.mach</key> + <string>8.0</string> + <key>com.apple.kpi.bsd</key> + <string>8.0</string> + <key>com.apple.kpi.libkern</key> + <string>8.0</string> + <key>com.apple.kpi.unsupported</key> + <string>8.0</string> + </dict> +</dict> +</plist> + diff --git a/tap-mac/tuntap/src/tun/Makefile b/tap-mac/tuntap/src/tun/Makefile new file mode 100644 index 00000000..9ca6794d --- /dev/null +++ b/tap-mac/tuntap/src/tun/Makefile @@ -0,0 +1,59 @@ +# +# ip tunnel/ethertap driver for MacOSX +# +# Makefile +# +# (c) 2004, 2005, 2006, 2007, 2008 Mattias Nissler +# + +OBJS = ../tuntap.o ../tuntap_mgr.o ../lock.o ../mem.o \ + kmod.o tun_inet_proto.o tun_inet6_proto.o tun.o +KMOD_BIN = tun +BUNDLE_DIR = ../.. +BUNDLE_NAME = tun.kext + +TUN_KEXT_VERSION = $(TUNTAP_VERSION) + +BUNDLE_REGION = English +BUNDLE_IDENTIFIER = foo.tun +BUNDLE_SIGNATURE = ???? +BUNDLE_PACKAGETYPE = KEXT +BUNDLE_VERSION = $(TUN_KEXT_VERSION) + +INCLUDE = -I.. -I/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)\" \ + -DTUN_KEXT_VERSION=\"$(TUN_KEXT_VERSION)\" +CCFLAGS = $(CFLAGS) +LDFLAGS = -Wall -mkernel -nostdlib -r -lcc_kext -arch i386 -arch x86_64 -Xlinker -kext + +CCP = g++ +CC = gcc + +all: $(KMOD_BIN) bundle + +.c.o: + $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ +.cc.o: + $(CCP) $(CCFLAGS) $(INCLUDE) -c $< -o $@ + +$(KMOD_BIN): $(OBJS) + $(CCP) $(LDFLAGS) -o $(KMOD_BIN) $(OBJS) + +bundle: $(KMOD_BIN) + rm -rf $(BUNDLE_DIR)/$(BUNDLE_NAME) + mkdir -p $(BUNDLE_DIR)/$(BUNDLE_NAME)/Contents/MacOS + cp $(KMOD_BIN) $(BUNDLE_DIR)/$(BUNDLE_NAME)/Contents/MacOS + sed -e "s/@@CFBUNDLEEXECUTABLE@@/$(KMOD_BIN)/" \ + -e "s/@@CFBUNDLEDEVELOPMENTREGION@@/$(BUNDLE_REGION)/" \ + -e "s/@@CFBUNDLEIDENTIFIER@@/$(BUNDLE_IDENTIFIER)/" \ + -e "s/@@CFBUNDLESIGNATURE@@/$(BUNDLE_SIGNATURE)/" \ + -e "s/@@CFBUNDLEPACKAGETYPE@@/$(BUNDLE_PACKAGETYPE)/" \ + -e "s/@@CFBUNDLEVERSION@@/$(BUNDLE_VERSION)/" \ + Info.plist > $(BUNDLE_DIR)/$(BUNDLE_NAME)/Contents/Info.plist + +clean: + -rm -f $(OBJS) $(KMOD_BIN) + -rm -rf $(BUNDLE_DIR)/$(BUNDLE_NAME) + diff --git a/tap-mac/tuntap/src/tun/kmod.cc b/tap-mac/tuntap/src/tun/kmod.cc new file mode 100644 index 00000000..1c085506 --- /dev/null +++ b/tap-mac/tuntap/src/tun/kmod.cc @@ -0,0 +1,93 @@ +/* + * ip tunnel device for MacOSX. + * + * Kext definition (it is a mach kmod really...) + */ +/* + * Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de> + * + * 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. + * 3. The name of the author may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + */ + +#include "tun.h" +#include "mem.h" + +extern "C" { + +#include <sys/param.h> + +#include <mach/kmod.h> + +static tun_manager *mgr; + +/* + * start function. called when the kext gets loaded. + */ +static kern_return_t tun_module_start(struct kmod_info *ki, void *data) +{ + mem_initialize(TUN_FAMILY_NAME); + + /* initialize locking */ + if (!tt_lock::initialize()) + return KMOD_RETURN_FAILURE; + + /* create a tun manager that will handle the rest */ + mgr = new tun_manager(); + + if (mgr != NULL) { + if (mgr->initialize(TUN_IF_COUNT, TUN_FAMILY_NAME)) + return KMOD_RETURN_SUCCESS; + + delete mgr; + mgr = NULL; + /* clean up locking */ + tt_lock::shutdown(); + } + + return KMOD_RETURN_FAILURE; +} + +/* + * stop function. called when the kext should be unloaded. unloading can be prevented by + * returning failure + */ +static kern_return_t tun_module_stop(struct kmod_info *ki, void *data) +{ + if (mgr != NULL) { + if (!mgr->shutdown()) + return KMOD_RETURN_FAILURE; + + delete mgr; + mgr = NULL; + } + + /* clean up locking */ + tt_lock::shutdown(); + + mem_shutdown(); + + return KMOD_RETURN_SUCCESS; +} + +KMOD_DECL(tun, TUN_KEXT_VERSION) + +} + diff --git a/tap-mac/tuntap/src/tun/tun.cc b/tap-mac/tuntap/src/tun/tun.cc new file mode 100644 index 00000000..91694388 --- /dev/null +++ b/tap-mac/tuntap/src/tun/tun.cc @@ -0,0 +1,424 @@ +/* + * ip tunnel device for MacOSX. + * + * tun_interface class definition + */ +/* + * Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de> + * + * 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. + * 3. The name of the author may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + */ + +#include "tun.h" + +extern "C" { + +#include <sys/syslog.h> +#include <sys/param.h> + +#include <net/if_types.h> +#include <net/kpi_protocol.h> + +#include <netinet/ip.h> + +} + +#if 0 +#define dprintf(...) log(LOG_INFO, __VA_ARGS__) +#else +#define dprintf(...) +#endif + +/* members */ +bool +tun_interface::initialize(unsigned short major, unsigned short unit) +{ + this->unit = unit; + this->family_name = TUN_FAMILY_NAME; + this->family = IFNET_FAMILY_TUN; + this->type = IFT_OTHER; + bzero(unique_id, UIDLEN); + snprintf(unique_id, UIDLEN, "%s%d", family_name, unit); + + dprintf("tun: starting interface %s%d\n", family_name, unit); + + /* register character device */ + if (!tuntap_interface::register_chardev(major)) + return false; + + return true; +} + +void +tun_interface::shutdown() +{ + dprintf("tun: shutting down interface %s%d\n", family_name, unit); + + unregister_chardev(); +} + +int +tun_interface::initialize_interface() +{ + prepend_af = false; + + /* register interface */ + if (!tuntap_interface::register_interface(NULL, NULL, 0)) + return EIO; + + /* set mtu */ + ifnet_set_mtu(ifp, TUN_MTU); + /* set header length */ + ifnet_set_hdrlen(ifp, 0); + /* add the pointopoint flag */ + ifnet_set_flags(ifp, IFF_POINTOPOINT, IFF_POINTOPOINT); + + /* we must call bpfattach(). Otherwise we deadlock BPF while unloading. Seems to be a bug in + * the kernel, see bpfdetach() in net/bpf.c, it will return without releasing the lock if + * the interface wasn't attached. I wonder what they were smoking while writing it ;-) + */ + bpfattach(ifp, DLT_NULL, sizeof(u_int32_t)); + + return 0; +} + +void +tun_interface::shutdown_interface() +{ + dprintf("tun: shutting down network interface of %s%d\n", family_name, unit); + + /* detach all protocols */ + for (unsigned int i = 0; i < MAX_ATTACHED_PROTOS; i++) { + if (attached_protos[i].used) { + errno_t err = ifnet_detach_protocol(ifp, attached_protos[i].proto); + if (err) + log(LOG_WARNING, "tun: could not detach protocol %d from %s%d\n", + attached_protos[i].proto, family_name, unit); + } + } + + cleanup_interface(); + unregister_interface(); +} + +void +tun_interface::notify_bpf(mbuf_t mb, bool out) +{ + auto_lock l(&bpf_lock); + + if ((out && bpf_mode == BPF_MODE_OUTPUT) + || (!out && bpf_mode == BPF_MODE_INPUT) + || (bpf_mode == BPF_MODE_INPUT_OUTPUT)) { + /* see wether AF is prepended */ + if (!prepend_af) { + mbuf_t dummy_mb; + struct ip *iphdr; + u_int32_t af; + u_int8_t ipv; + errno_t err; + + /* see what we have: IPv4 or IPv6 */ + iphdr = (struct ip*) mbuf_data(mb); +#ifdef _IP_VHL + ipv = IP_VHL_V(iphdr->ip_vhl); +#else + ipv = iphdr->ip_v; +#endif + if (ipv == 4) + af = AF_INET; + else if (ipv == 6) + af = AF_INET6; + else { + /* what to do? */ + log(LOG_WARNING, "tun: unsupported IP version %d.\n", ipv); + return; + } + + /* prepend a dummy header */ + err = mbuf_get(MBUF_WAITOK, MBUF_TYPE_DATA, &dummy_mb); + if (err) { + log(LOG_WARNING, "tun: could not allocate temporary mbuf: %d\n", + err); + return; + } + + mbuf_setnext(dummy_mb, mb); + mbuf_setlen(dummy_mb, sizeof(u_int32_t)); + *((u_int32_t *) mbuf_data(dummy_mb)) = htonl(af); + + /* call bpf */ + (*bpf_callback)(ifp, dummy_mb); + + /* free the dummy mbuf */ + mbuf_free(dummy_mb); + } else { + /* just pass it through */ + (*bpf_callback)(ifp, mb); + } + } +} + +int +tun_interface::cdev_ioctl(u_long cmd, caddr_t data, int fflag, proc_t p) +{ + int error; + + /* if the superclass handles it, we're done */ + error = tuntap_interface::cdev_ioctl(cmd, data, fflag, p); + if (!error) + return 0; + + switch (cmd) { + case TUNSIFHEAD: + prepend_af = *((int *) data) ? true : false; + /* adjust header length. see tuntap_interface::cdev_write */ + ifnet_set_hdrlen(ifp, prepend_af ? sizeof(u_int32_t) : 0); + return 0; + case TUNGIFHEAD: + *((int *) data) = prepend_af; + return 0; + } + + return ENOTTY; +} + +errno_t +tun_interface::if_demux(mbuf_t m, char *header, protocol_family_t *proto) +{ + u_int32_t family; + + dprintf("tun: demux\n"); + + /* size check */ + if (mbuf_len(m) < sizeof(u_int32_t)) + return ENOENT; + + /* if we are prepending AF for output, we expect to also have it at the beginning of + * incoming packets */ + if (!prepend_af) { + struct ip *iphdr = (struct ip*) mbuf_data(m); + u_int8_t ipv; + + dprintf("tun_demux: m: %p data: %p\n", m, mbuf_data(m)); + +#ifdef _IPVHL + ipv = IP_VHL_V(iphdr->ip_vhl); +#else + ipv = iphdr->ip_v; +#endif + + if (ipv == 4) + family = AF_INET; + else if (ipv == 6) + family = AF_INET6; + else { + /* what to do? */ + log(LOG_WARNING, "tun: unsupported IP version %d\n", ipv); + return ENOENT; + } + } else { + family = ntohl(*((u_int32_t *) header)); + } + + /* find the protocol entry */ + for (unsigned int i = 0; i < MAX_ATTACHED_PROTOS; i++) { + if (attached_protos[i].used && attached_protos[i].family == family) { + *proto = attached_protos[i].proto; + return 0; + } + } + + log(LOG_WARNING, "tun: no protocol found for family %d\n", family); + + return ENOENT; +} + +errno_t +tun_interface::if_framer(mbuf_t *m, const struct sockaddr *dest, const char *dest_linkaddr, + const char *frame_type) +{ + dprintf("tun: framer\n"); + + /* check whether to prepend family field */ + if (prepend_af) { + errno_t err; + mbuf_t nm = *m; + + /* get space */ + err = mbuf_prepend(&nm, sizeof(u_int32_t), MBUF_WAITOK); + if (err) { + dprintf("tun: could not prepend data to mbuf: %d\n", err); + return err; + } + *m = nm; + + *((u_int32_t *) mbuf_data(*m)) = htonl(dest->sa_family); + } + + return 0; +} + +errno_t +tun_interface::if_add_proto(protocol_family_t proto, const struct ifnet_demux_desc *desc, + u_int32_t ndesc) +{ + errno_t err; + + dprintf("tun: if_add_proto proto %d\n", proto); + + for (unsigned int i = 0; i < ndesc; i++) { + /* try to add the protocol */ + err = add_one_proto(proto, desc[i]); + if (err != 0) { + /* if that fails, remove everything stored so far */ + if_del_proto(proto); + return err; + } + } + + return 0; +} + +errno_t +tun_interface::if_del_proto(protocol_family_t proto) +{ + dprintf("tun: if_del_proto proto %d\n", proto); + + /* delete all matching entries in attached_protos */ + for (unsigned int i = 0; i < MAX_ATTACHED_PROTOS; i++) { + if (attached_protos[i].proto == proto) + attached_protos[i].used = false; + } + + return 0; +} + +errno_t +tun_interface::if_check_multi(const struct sockaddr *maddr) +{ + dprintf("tun: check_multi family %d\n", maddr->sa_family); + + /* see whether it is an IPv4 multicast address */ + if (maddr->sa_family == AF_INET) { + struct sockaddr_in *imaddr = (struct sockaddr_in *) maddr; + + if (IN_MULTICAST(ntohl(imaddr->sin_addr.s_addr))) + return 0; + else + return EADDRNOTAVAIL; + } else if (maddr->sa_family == AF_INET6) { + struct sockaddr_in6 *imaddr = (struct sockaddr_in6 *) maddr; + + if (IN6_IS_ADDR_MULTICAST(&imaddr->sin6_addr)) + return 0; + else + return EADDRNOTAVAIL; + } + + return EOPNOTSUPP; +} + +errno_t +tun_interface::add_one_proto(protocol_family_t proto, const struct ifnet_demux_desc &dd) +{ + int free = -1; + + /* see if the protocol is already registered */ + for (unsigned int i = 0; i < MAX_ATTACHED_PROTOS; i++) { + if (attached_protos[i].used) { + if (dd.type == attached_protos[i].family) { + /* already registered */ + if (attached_protos[i].proto == proto) { + /* matches the old entry */ + return 0; + } else + return EEXIST; + } + } else if (free == -1) + free = i; + } + + /* did we find a free entry? */ + if (free == -1) + /* is ENOBUFS correct? */ + return ENOBUFS; + + dprintf("tun: adding proto family %d proto %d\n", dd.type, proto); + + /* ok, save information */ + attached_protos[free].used = true; + attached_protos[free].family = dd.type; + attached_protos[free].proto = proto; + + return 0; +} + +/* tun_manager members */ +tuntap_interface * +tun_manager::create_interface() +{ + return new tun_interface(); +} + +bool +tun_manager::shutdown() +{ + if (tuntap_inited) { + if (tuntap_manager::shutdown()) + tuntap_inited = false; + else + return false; + } + + /* unregister INET and INET6 protocol families */ + proto_unregister_plumber(PF_INET, IFNET_FAMILY_TUN); + proto_unregister_plumber(PF_INET6, IFNET_FAMILY_TUN); + + return true; +} + +bool +tun_manager::initialize(unsigned int count, char *family) +{ + errno_t err; + + tuntap_inited = false; + + /* register INET and INET6 protocol families */ + err = proto_register_plumber(PF_INET, IFNET_FAMILY_TUN, tun_inet_attach, tun_inet_detach); + if (err) { + log(LOG_ERR, "tun: could not register PF_INET protocol family: %d\n", err); + return false; + } + + err = proto_register_plumber(PF_INET6, IFNET_FAMILY_TUN, tun_inet6_attach, + tun_inet6_detach); + if (err) { + log(LOG_ERR, "tun: could not register PF_INET6 protocol family: %d\n", err); + return false; + } + + tuntap_inited = true; + + /* have the superclass handle the rest */ + return tuntap_manager::initialize(count, family); +} + diff --git a/tap-mac/tuntap/src/tun/tun.h b/tap-mac/tuntap/src/tun/tun.h new file mode 100644 index 00000000..8546dc8f --- /dev/null +++ b/tap-mac/tuntap/src/tun/tun.h @@ -0,0 +1,123 @@ +/* + * ip tunnel device for MacOSX. + */ +/* + * Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de> + * + * 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. + * 3. The name of the author may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + */ + +#ifndef __TUN_H__ +#define __TUN_H__ + +#include "tuntap.h" + +#define TUN_FAMILY_NAME ((char *) "tun") +#define TUN_IF_COUNT 16 /* max number of tun interfaces */ +#define TUN_MTU 1500 + +#include "tun_ioctls.h" + +extern "C" { + +errno_t tun_inet_attach(ifnet_t ifp, protocol_family_t proto); +void tun_inet_detach(ifnet_t ifp, protocol_family_t proto); +errno_t tun_inet6_attach(ifnet_t ifp, protocol_family_t proto); +void tun_inet6_detach(ifnet_t ifp, protocol_family_t proto); + +} + +/* tun_manager */ +class tun_manager : public tuntap_manager { + + protected: + /* create an interface */ + virtual tuntap_interface *create_interface(); + + /* whether we need to call tuntap_manager::shutdown() */ + bool tuntap_inited; + + public: + /* special initalize */ + virtual bool initialize(unsigned int count, char *family); + + /* special shutdown */ + virtual bool shutdown(); + +}; + +/* the tun network interface */ +class tun_interface : public tuntap_interface { + + protected: + /* maximum number of protocols that can be attached */ + static const unsigned int MAX_ATTACHED_PROTOS = 8; + + /* information about attached protocols for demuxing is stored here */ + struct { + /* whether this entry is used */ + bool used; + /* protocol family (this is equal to proto, but keep it seperated from + * Apple's KPI stuff...) */ + u_int32_t family; + /* protocol passed to add_proto */ + protocol_family_t proto; + } attached_protos[MAX_ATTACHED_PROTOS]; + + /* whether the address family field is prepended to each packet */ + bool prepend_af; + + /* intializes the interface */ + virtual bool initialize(unsigned short major, unsigned short int unit); + + /* shutdown the interface */ + virtual void shutdown(); + + /* called when the character device is opened in order to intialize the network + * interface. + */ + virtual int initialize_interface(); + /* called when the character device is closed to shutdown the network interface */ + virtual void shutdown_interface(); + + /* override interface routines */ + virtual errno_t if_demux(mbuf_t m, char *header, protocol_family_t *proto); + virtual errno_t if_framer(mbuf_t *m, const struct sockaddr *dest, + const char *dest_linkaddr, const char *frame_type); + virtual errno_t if_add_proto(protocol_family_t proto, + const struct ifnet_demux_desc *desc, u_int32_t ndesc); + virtual errno_t if_del_proto(protocol_family_t proto); + virtual errno_t if_check_multi(const struct sockaddr *maddr); + + /* helper to if_add_proto */ + virtual errno_t add_one_proto(protocol_family_t proto, + const struct ifnet_demux_desc &dd); + + /* override notify_bpf because we might need to prepend an address header */ + virtual void notify_bpf(mbuf_t mb, bool out); + + /* need to override cdev_ioctl to get our special ioctls */ + virtual int cdev_ioctl(u_long cmd, caddr_t data, int fflag, proc_t p); + +}; + +#endif /* __TUN_H__ */ + diff --git a/tap-mac/tuntap/src/tun/tun_inet6_proto.c b/tap-mac/tuntap/src/tun/tun_inet6_proto.c new file mode 100644 index 00000000..4461d4d9 --- /dev/null +++ b/tap-mac/tuntap/src/tun/tun_inet6_proto.c @@ -0,0 +1,87 @@ +/* + * ip tunnel device for MacOSX. This is the protocol module for PF_INET. + */ +/* + * Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de> + * + * 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. + * 3. The name of the author may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + */ + +#include <sys/kpi_mbuf.h> +#include <sys/socket.h> +#include <sys/errno.h> +#include <sys/param.h> + +#include <net/kpi_protocol.h> +#include <net/kpi_interface.h> + +static errno_t +tun_inet6_input(ifnet_t ifp, protocol_family_t protocol, mbuf_t m, char *header) +{ + /* input the packet */ + return proto_input(PF_INET6, m); +} + +static errno_t +tun_inet6_pre_output(ifnet_t ifp, protocol_family_t proto, mbuf_t *packet, + const struct sockaddr *dest, void *route, char *frame_type, char *dst_addr) +{ + + /* check wether the destination address is an inet address */ + if (dest->sa_family != AF_INET6) + return EAFNOSUPPORT; + + /* place the address family as frame type */ + *((uint32_t *) frame_type) = htonl(AF_INET6); + + return 0; +} + +errno_t +tun_inet6_attach(ifnet_t ifp, protocol_family_t proto) +{ + struct ifnet_attach_proto_param pr; + struct ifnet_demux_desc ddesc[1]; + + /* fill out pr and attach the protocol */ + ddesc[0].type = AF_INET6; + ddesc[0].data = NULL; + ddesc[0].datalen = 0; + pr.demux_array = ddesc; + pr.demux_count = 1; + pr.input = tun_inet6_input; + pr.pre_output = tun_inet6_pre_output; + pr.event = NULL; + pr.ioctl = NULL; + pr.detached = NULL; + pr.resolve = NULL; + pr.send_arp = NULL; + + return ifnet_attach_protocol(ifp, proto, &pr); +} + +void +tun_inet6_detach(ifnet_t ifp, protocol_family_t proto) +{ + /* just detach the protocol */ + ifnet_detach_protocol(ifp, proto); +} + diff --git a/tap-mac/tuntap/src/tun/tun_inet_proto.c b/tap-mac/tuntap/src/tun/tun_inet_proto.c new file mode 100644 index 00000000..38ecd26b --- /dev/null +++ b/tap-mac/tuntap/src/tun/tun_inet_proto.c @@ -0,0 +1,87 @@ +/* + * ip tunnel device for MacOSX. This is the protocol module for PF_INET. + */ +/* + * Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de> + * + * 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. + * 3. The name of the author may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + */ + +#include <sys/kpi_mbuf.h> +#include <sys/socket.h> +#include <sys/errno.h> +#include <sys/param.h> + +#include <net/kpi_protocol.h> +#include <net/kpi_interface.h> + +static errno_t +tun_inet_input(ifnet_t ifp, protocol_family_t protocol, mbuf_t m, char *header) +{ + /* input the packet */ + return proto_input(PF_INET, m); +} + +static errno_t +tun_inet_pre_output(ifnet_t ifp, protocol_family_t proto, mbuf_t *packet, + const struct sockaddr *dest, void *route, char *frame_type, char *dst_addr) +{ + + /* check wether the destination address is an inet address */ + if (dest->sa_family != AF_INET) + return EAFNOSUPPORT; + + /* place the address family as frame type */ + *((uint32_t *) frame_type) = htonl(AF_INET); + + return 0; +} + +errno_t +tun_inet_attach(ifnet_t ifp, protocol_family_t proto) +{ + struct ifnet_attach_proto_param pr; + struct ifnet_demux_desc ddesc[1]; + + /* fill out pr and attach the protocol */ + ddesc[0].type = AF_INET; + ddesc[0].data = NULL; + ddesc[0].datalen = 0; + pr.demux_array = ddesc; + pr.demux_count = 1; + pr.input = tun_inet_input; + pr.pre_output = tun_inet_pre_output; + pr.event = NULL; + pr.ioctl = NULL; + pr.detached = NULL; + pr.resolve = NULL; + pr.send_arp = NULL; + + return ifnet_attach_protocol(ifp, proto, &pr); +} + +void +tun_inet_detach(ifnet_t ifp, protocol_family_t proto) +{ + /* just detach the protocol */ + ifnet_detach_protocol(ifp, proto); +} + diff --git a/tap-mac/tuntap/src/tun/tun_ioctls.h b/tap-mac/tuntap/src/tun/tun_ioctls.h new file mode 100644 index 00000000..13501e53 --- /dev/null +++ b/tap-mac/tuntap/src/tun/tun_ioctls.h @@ -0,0 +1,38 @@ +/* + * ip tunnel device for MacOSX. + */ +/* + * Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de> + * + * 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. + * 3. The name of the author may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + */ + +#ifndef __TUN_IOCTLS_H__ +#define __TUN_IOCTLS_H__ + +/* Tun supports prepending a four byte address family field to each packet. These ioctls allow you + * to switch it on/off. Pass 1 as parameter to switch it on, pass 0 for off. + */ +#define TUNSIFHEAD _IOW('t', 96, int) +#define TUNGIFHEAD _IOR('t', 97, int) + +#endif /* __TUN_IOCTLS_H__ */ + diff --git a/tap-mac/tuntap/src/tuntap.cc b/tap-mac/tuntap/src/tuntap.cc new file mode 100644 index 00000000..941de94c --- /dev/null +++ b/tap-mac/tuntap/src/tuntap.cc @@ -0,0 +1,962 @@ +/* + * ip tunnel/ethertap device for MacOSX. Common functionality of tap_interface and tun_interface. + * + * tuntap_interface class definition + */ +/* + * Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de> + * + * 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. + * 3. The name of the author may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + */ + +#include "tuntap.h" + +#if 0 +#define dprintf(...) log(LOG_INFO, __VA_ARGS__) +#else +#define dprintf(...) +#endif + +extern "C" { + +#include <sys/conf.h> +#include <sys/syslog.h> +#include <sys/param.h> +#include <sys/filio.h> +#include <sys/sockio.h> +#include <sys/fcntl.h> +#include <sys/kpi_socket.h> + +#include <vm/vm_kern.h> + +#include <net/if_types.h> +#include <net/if_var.h> +#include <net/if_dl.h> +#include <net/if_arp.h> + +#include <miscfs/devfs/devfs.h> + +} + +extern "C" { + +/* interface service functions that delegate to the appropriate tuntap_interface instance */ +errno_t +tuntap_if_output(ifnet_t ifp, mbuf_t m) +{ + if (ifp != NULL) { + tuntap_interface *ttif = (tuntap_interface *) ifnet_softc(ifp); + if (ttif != NULL) + return ttif->if_output(m); + } + + if (m != NULL) + mbuf_freem_list(m); + + return ENODEV; +} + +errno_t +tuntap_if_ioctl(ifnet_t ifp, long unsigned int cmd, void *arg) +{ + if (ifp != NULL) { + tuntap_interface *ttif = (tuntap_interface *) ifnet_softc(ifp); + if (ttif != NULL) + return ttif->if_ioctl(cmd, arg); + } + + return ENODEV; +} + +errno_t +tuntap_if_set_bpf_tap(ifnet_t ifp, bpf_tap_mode mode, int (*cb)(ifnet_t, mbuf_t)) +{ + if (ifp != NULL) { + tuntap_interface *ttif = (tuntap_interface *) ifnet_softc(ifp); + if (ttif != NULL) + return ttif->if_set_bpf_tap(mode, cb); + } + + return ENODEV; +} + +errno_t +tuntap_if_demux(ifnet_t ifp, mbuf_t m, char *header, protocol_family_t *proto) +{ + if (ifp != NULL) { + tuntap_interface *ttif = (tuntap_interface *) ifnet_softc(ifp); + if (ttif != NULL) + return ttif->if_demux(m, header, proto); + } + + return ENODEV; +} + +errno_t +tuntap_if_framer(ifnet_t ifp, mbuf_t *m, const struct sockaddr *dest, const char *dest_linkaddr, + const char *frame_type) +{ + if (ifp != NULL) { + tuntap_interface *ttif = (tuntap_interface *) ifnet_softc(ifp); + if (ttif != NULL) + return ttif->if_framer(m, dest, dest_linkaddr, frame_type); + } + + return ENODEV; +} + +errno_t +tuntap_if_add_proto(ifnet_t ifp, protocol_family_t proto, const struct ifnet_demux_desc *ddesc, + u_int32_t ndesc) +{ + if (ifp != NULL) { + tuntap_interface *ttif = (tuntap_interface *) ifnet_softc(ifp); + if (ttif != NULL) + return ttif->if_add_proto(proto, ddesc, ndesc); + } + + return ENODEV; +} + +errno_t +tuntap_if_del_proto(ifnet_t ifp, protocol_family_t proto) +{ + if (ifp != NULL) { + tuntap_interface *ttif = (tuntap_interface *) ifnet_softc(ifp); + if (ttif != NULL) + return ttif->if_del_proto(proto); + } + + return ENODEV; +} + +errno_t +tuntap_if_check_multi(ifnet_t ifp, const struct sockaddr* maddr) +{ + if (ifp != NULL) + { + tuntap_interface *ttif = (tuntap_interface *) ifnet_softc(ifp); + if (ttif != NULL) + return ttif->if_check_multi(maddr); + } + + return ENODEV; +} + +void +tuntap_if_detached(ifnet_t ifp) +{ + if (ifp != NULL) { + tuntap_interface *ttif = (tuntap_interface *) ifnet_softc(ifp); + if (ttif != NULL) + ttif->if_detached(); + } +} + +errno_t +tuntap_if_noop_output(ifnet_t, mbuf_t) +{ + return ENODEV; +} + +errno_t +tuntap_if_noop_demux(ifnet_t, mbuf_t, char*, protocol_family_t*) +{ + return ENODEV; +} + +errno_t +tuntap_if_noop_add_proto(ifnet_t, protocol_family_t, const struct ifnet_demux_desc*, u_int32_t) +{ + return ENODEV; +} + +errno_t +tuntap_if_noop_del_proto(ifnet_t, protocol_family_t) +{ + return ENODEV; +} + +} /* extern "C" */ + +/* tuntap_mbuf_queue */ +tuntap_mbuf_queue::tuntap_mbuf_queue() +{ + head = tail = NULL; + size = 0; +} + +tuntap_mbuf_queue::~tuntap_mbuf_queue() +{ + clear(); +} + +bool +tuntap_mbuf_queue::enqueue(mbuf_t mb) +{ + if (size == QUEUE_SIZE) + return false; + + mbuf_setnextpkt(mb, NULL); + + if (head == NULL) + head = tail = mb; + else { + mbuf_setnextpkt(tail, mb); + tail = mb; + } + size++; + + return true; +} + +mbuf_t +tuntap_mbuf_queue::dequeue() +{ + mbuf_t ret; + + /* check wether there is a packet in the queue */ + if (head == NULL) + return NULL; + + /* fetch it */ + ret = head; + head = mbuf_nextpkt(head); + mbuf_setnextpkt(ret, NULL); + size--; + + return ret; +} + +void +tuntap_mbuf_queue::clear() +{ + /* free mbufs that are in the queue */ + if (head != NULL) + mbuf_freem_list(head); + + head = NULL; + tail = NULL; + size = 0; +} + +/* tuntap_interface members */ +tuntap_interface::tuntap_interface() +{ + /* initialize the members */ + ifp = NULL; + open = false; + block_io = true; + dev_handle = NULL; + pid = 0; + selthreadclear(&rsel); + bpf_mode = BPF_MODE_DISABLED; + bpf_callback = NULL; + bzero(unique_id, UIDLEN); + in_ioctl = false; +} + +tuntap_interface::~tuntap_interface() +{ +} + +bool +tuntap_interface::register_chardev(unsigned short major) +{ + /* register character device */ + dev_handle = devfs_make_node(makedev(major, unit), DEVFS_CHAR, 0, 0, 0660, "%s%d", + family_name, (int) unit); + + if (dev_handle == NULL) { + log(LOG_ERR, "tuntap: could not make /dev/%s%d\n", family_name, (int) unit); + return false; + } + + return true; +} + +void +tuntap_interface::unregister_chardev() +{ + dprintf("unregistering character device\n"); + + /* unregister character device */ + if (dev_handle != NULL) + devfs_remove(dev_handle); + dev_handle = NULL; +} + +bool +tuntap_interface::register_interface(const struct sockaddr_dl* lladdr, void *bcaddr, + u_int32_t bcaddrlen) +{ + struct ifnet_init_params ip; + errno_t err; + + dprintf("register_interface\n"); + + /* initialize an initialization info struct */ + ip.uniqueid_len = UIDLEN; + ip.uniqueid = unique_id; + ip.name = family_name; + ip.unit = unit; + ip.family = family; + ip.type = type; + ip.output = tuntap_if_output; + ip.demux = tuntap_if_demux; + ip.add_proto = tuntap_if_add_proto; + ip.del_proto = tuntap_if_del_proto; + ip.check_multi = tuntap_if_check_multi; + ip.framer = tuntap_if_framer; + ip.softc = this; + ip.ioctl = tuntap_if_ioctl; + ip.set_bpf_tap = tuntap_if_set_bpf_tap; + ip.detach = tuntap_if_detached; + ip.event = NULL; + ip.broadcast_addr = bcaddr; + ip.broadcast_len = bcaddrlen; + + dprintf("tuntap: tuntap_if_check_multi is at 0x%08x\n", (void*) tuntap_if_check_multi); + + /* allocate the interface */ + err = ifnet_allocate(&ip, &ifp); + if (err) { + log(LOG_ERR, "tuntap: could not allocate interface for %s%d: %d\n", family_name, + (int) unit, err); + ifp = NULL; + return false; + } + + /* activate the interface */ + err = ifnet_attach(ifp, lladdr); + if (err) { + log(LOG_ERR, "tuntap: could not attach interface %s%d: %d\n", family_name, + (int) unit, err); + ifnet_release(ifp); + ifp = NULL; + return false; + } + + dprintf("setting interface flags\n"); + + /* set interface flags */ + ifnet_set_flags(ifp, IFF_RUNNING | IFF_MULTICAST | IFF_SIMPLEX, (u_int16_t) ~0UL); + + dprintf("flags: %x\n", ifnet_flags(ifp)); + + return true; +} + +void +tuntap_interface::unregister_interface() +{ + errno_t err; + + dprintf("unregistering network interface\n"); + + if (ifp != NULL) { + interface_detached = false; + + /* detach interface */ + err = ifnet_detach(ifp); + if (err) + log(LOG_ERR, "tuntap: error detaching interface %s%d: %d\n", + family_name, unit, err); + + dprintf("interface detaching\n"); + + /* Wait until the interface has completely been detached. */ + detach_lock.lock(); + while (!interface_detached) + detach_lock.sleep(&interface_detached); + detach_lock.unlock(); + + dprintf("interface detached\n"); + + /* release the interface */ + ifnet_release(ifp); + + ifp = NULL; + } + + dprintf("network interface unregistered\n"); +} + +void +tuntap_interface::cleanup_interface() +{ + errno_t err; + ifaddr_t *addrs; + ifaddr_t *a; + struct ifreq ifr; + + /* mark the interface down */ + ifnet_set_flags(ifp, 0, IFF_UP | IFF_RUNNING); + + /* Unregister all interface addresses. This works around a deficiency in the Darwin kernel. + * If we don't remove all IP addresses that are attached to the interface it can happen that + * the IP code fails to clean them up itself. When the interface is recycled, the IP code + * might then think some addresses are still attached to the interface... + */ + + err = ifnet_get_address_list(ifp, &addrs); + if (!err) { + + /* Execute a SIOCDIFADDR ioctl for each address. For technical reasons, we can only + * do that with a socket of the appropriate family. So try to create a dummy socket. + * I know this is a little expensive, but better than crashing... + * + * This really sucks. + */ + for (a = addrs; *a != NULL; a++) { + /* initialize the request parameters */ + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", + ifnet_name(ifp), ifnet_unit(ifp)); + ifaddr_address(*a, &(ifr.ifr_addr), sizeof(ifr.ifr_addr)); + if (ifr.ifr_addr.sa_family != AF_INET) + continue; + + dprintf("trying to delete address of family %d\n", ifr.ifr_addr.sa_family); + + do_sock_ioctl(ifr.ifr_addr.sa_family, SIOCDIFADDR, &ifr); + } + + /* release the address list */ + ifnet_free_address_list(addrs); + } +} + +bool +tuntap_interface::idle() +{ + return !(open); +} + +void +tuntap_interface::notify_bpf(mbuf_t mb, bool out) +{ + auto_lock l(&bpf_lock); + + if ((out && bpf_mode == BPF_MODE_OUTPUT) + || (!out && bpf_mode == BPF_MODE_INPUT) + || (bpf_mode == BPF_MODE_INPUT_OUTPUT)) + (*bpf_callback)(ifp, mb); +} + +void +tuntap_interface::do_sock_ioctl(sa_family_t af, unsigned long cmd, void* arg) { + if (in_ioctl) { + log(LOG_ERR, "tuntap: ioctl recursion detected, aborting.\n"); + return; + } + + socket_t sock; + errno_t err = sock_socket(af, SOCK_RAW, 0, NULL, NULL, &sock); + if (err) { + log(LOG_ERR, "tuntap: failed to create socket: %d\n", err); + return; + } + + in_ioctl = true; + + /* issue the ioctl */ + err = sock_ioctl(sock, cmd, arg); + if (err) + log(LOG_ERR, "tuntap: socket ioctl %d failed: %d\n", cmd, err); + + in_ioctl = false; + + /* get rid of the socket */ + sock_close(sock); +} + +/* character device service methods */ +int +tuntap_interface::cdev_open(int flags, int devtype, proc_t p) +{ + dprintf("tuntap: cdev_open()\n"); + + /* grab the lock so that there can only be one thread inside */ + auto_lock l(&lock); + + /* check wether it is already open */ + if (open) + return EBUSY; + + /* bring the network interface up */ + int error = initialize_interface(); + if (error) + return error; + + open = true; + pid = proc_pid(p); + + return 0; +} + +int +tuntap_interface::cdev_close(int flags, int devtype, proc_t p) +{ + dprintf("tuntap: cdev_close()\n"); + + auto_lock l(&lock); + + if (open) { + open = false; + + /* shut down the network interface */ + shutdown_interface(); + + /* clear the queue */ + send_queue.clear(); + + /* wakeup the cdev thread and notify selects */ + wakeup(this); + selwakeup(&rsel); + + return 0; + } + + return EBADF; +} + +int +tuntap_interface::cdev_read(uio_t uio, int ioflag) +{ + auto_lock l(&lock); + + unsigned int nb = 0; + int error; + + dprintf("tuntap: cdev read\n"); + + if (!open || ifp == NULL || !(ifnet_flags(ifp) & IFF_UP)) + return EIO; + + /* fetch a new mbuf from the queue if necessary */ + mbuf_t cur_mbuf = NULL; + while (cur_mbuf == NULL) { + dprintf("tuntap: fetching new mbuf\n"); + + cur_mbuf = send_queue.dequeue(); + if (cur_mbuf == NULL) { + /* nothing in queue, block or return */ + if (!block_io) { + dprintf("tuntap: aborting (nbio)\n"); + return EWOULDBLOCK; + } else { + /* block */ + dprintf("tuntap: waiting\n"); + /* release the lock while waiting */ + l.unlock(); + error = msleep(this, NULL, PZERO | PCATCH, "tuntap", NULL); + + l.lock(); + + if (error) + return error; + + /* see whether the device was closed in the meantime */ + if (!open || ifp == NULL || !(ifnet_flags(ifp) & IFF_UP)) + return EIO; + + } + } + } + + /* notify bpf */ + notify_bpf(cur_mbuf, true); + + /* output what we have */ + do { + dprintf("tuntap: got new mbuf: %p uio_resid: %d\n", cur_mbuf, uio_resid(uio)); + + /* now we have an mbuf */ + int chunk_len = min(mbuf_len(cur_mbuf), uio_resid(uio)); + error = uiomove((char *) mbuf_data(cur_mbuf), chunk_len, uio); + if (error) { + mbuf_freem(cur_mbuf); + return error; + } + nb += chunk_len; + + dprintf("tuntap: moved %d bytes to userspace uio_resid: %d\n", chunk_len, + uio_resid(uio)); + + /* update cur_mbuf */ + cur_mbuf = mbuf_free(cur_mbuf); + + } while (uio_resid(uio) > 0 && cur_mbuf != NULL); + + /* update statistics */ + ifnet_stat_increment_out(ifp, 1, nb, 0); + + /* still data left? forget about that ;-) */ + if (cur_mbuf != NULL) + mbuf_freem(cur_mbuf); + + dprintf("tuntap: read done\n"); + + return 0; +} + +int +tuntap_interface::cdev_write(uio_t uio, int ioflag) +{ + auto_lock l(&lock); + + if (!open || ifp == NULL || !(ifnet_flags(ifp) & IFF_UP)) + return EIO; + + dprintf("tuntap: cdev write. uio_resid: %d\n", uio_resid(uio)); + + /* pack the data into an mbuf chain */ + mbuf_t first, mb; + + /* first we need an mbuf having a header */ + mbuf_gethdr(MBUF_WAITOK, MBUF_TYPE_DATA, &first); + if (first == NULL) { + log(LOG_ERR, "tuntap: could not get mbuf.\n"); + return ENOMEM; + } + mbuf_setlen(first, 0); + + unsigned int mlen = mbuf_maxlen(first); + unsigned int chunk_len; + unsigned int copied = 0; + 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) */ + chunk_len = min(ifnet_mtu(ifp), 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); + mbuf_freem(first); + return error; + } + + dprintf("tuntap: copied %d bytes, uio_resid %d\n", chunk_len, + uio_resid(uio)); + + mlen -= chunk_len; + mbuf_setlen(mb, mbuf_len(mb) + chunk_len); + copied += chunk_len; + + /* if done, break the loop */ + if (uio_resid(uio) <= 0 || copied >= ifnet_mtu(ifp)) + break; + + /* allocate a new mbuf if the current is filled */ + if (mlen == 0) { + mbuf_t next; + mbuf_get(MBUF_WAITOK, MBUF_TYPE_DATA, &next); + if (next == NULL) { + log(LOG_ERR, "tuntap: could not get mbuf.\n"); + mbuf_freem(first); + return ENOMEM; + } + mbuf_setnext(mb, next); + mb = next; + mbuf_setlen(mb, 0); + mlen = mbuf_maxlen(mb); + } + } + + /* fill in header info */ + mbuf_pkthdr_setrcvif(first, ifp); + mbuf_pkthdr_setlen(first, copied); + mbuf_pkthdr_setheader(first, mbuf_data(first)); + mbuf_set_csum_performed(first, 0, 0); + + /* update statistics */ + ifnet_stat_increment_in(ifp, 1, copied, 0); + + dprintf("tuntap: mbuf chain constructed. first: %p mb: %p len: %d data: %p\n", + first, mb, mbuf_len(first), mbuf_data(first)); + + /* notify bpf */ + notify_bpf(first, false); + + /* need to adjust the data pointer to point directly behind the linklevel header. The header + * itself is later accessed via m_pkthdr.header. Well, if something is ugly, here is it. + */ + mbuf_adj(first, ifnet_hdrlen(ifp)); + + /* pass the packet over to the network stack */ + error = ifnet_input(ifp, first, NULL); + + if (error) { + log(LOG_ERR, "tuntap: could not input packet into network stack.\n"); + mbuf_freem(first); + return error; + } + + return 0; +} + +int +tuntap_interface::cdev_ioctl(u_long cmd, caddr_t data, int fflag, proc_t p) +{ + auto_lock l(&lock); + + dprintf("tuntap: cdev ioctl: %d\n", (int) (cmd & 0xff)); + + switch (cmd) { + case FIONBIO: + /* set i/o mode */ + block_io = *((int *) data) ? false : true; + return 0; + case FIOASYNC: + /* don't allow switching it on */ + if (*((int *) data)) + return ENOTTY; + return 0; + } + + return ENOTTY; +} + +int +tuntap_interface::cdev_select(int which, void *wql, proc_t p) +{ + auto_lock l(&lock); + + int ret = 0; + + dprintf("tuntap: select. which: %d\n", which); + + switch (which) { + case FREAD: + /* check wether data is available */ + { + if (!send_queue.empty()) + ret = 1; + else { + dprintf("tuntap: select: waiting\n"); + selrecord(p, &rsel, wql); + } + } + break; + case FWRITE: + /* we are always writeable */ + ret = 1; + } + + return ret; +} + +/* interface service methods */ +errno_t +tuntap_interface::if_output(mbuf_t m) +{ + mbuf_t pkt; + + dprintf("tuntap: if output\n"); + + /* just to be sure */ + if (m == NULL) + return 0; + + if (!open || ifp == NULL || !(ifnet_flags(ifp) & IFF_UP)) { + mbuf_freem_list(m); + return EHOSTDOWN; + } + + /* check whether packet has a header */ + if ((mbuf_flags(m) & MBUF_PKTHDR) == 0) { + log(LOG_ERR, "tuntap: packet to be output has no mbuf header.\n"); + mbuf_freem_list(m); + return EINVAL; + } + + /* put the packet(s) into the output queue */ + while (m != NULL) { + /* keep pointer, iterate */ + pkt = m; + m = mbuf_nextpkt(m); + mbuf_setnextpkt(pkt, NULL); + + auto_lock l(&lock); + + if (!send_queue.enqueue(pkt)) { + mbuf_freem(pkt); + mbuf_freem_list(m); + return ENOBUFS; + } + } + + /* protect the wakeup calls with the lock, not sure they are safe. */ + { + auto_lock l(&lock); + + /* wakeup the cdev thread and notify selects */ + wakeup(this); + selwakeup(&rsel); + } + + return 0; +} + +errno_t +tuntap_interface::if_ioctl(u_int32_t cmd, void *arg) +{ + dprintf("tuntap: if ioctl: %d\n", (int) (cmd & 0xff)); + + switch (cmd) { + case SIOCSIFADDR: + { + dprintf("tuntap: if_ioctl: SIOCSIFADDR\n"); + + /* Unfortunately, ifconfig sets the address family field of an INET + * netmask to zero, which makes early mDNSresponder versions ignore + * the interface. Fix that here. This one is of the category "ugly + * workaround". Dumb Darwin... + * + * Meanwhile, Apple has fixed mDNSResponder, and recent versions of + * Leopard don't need this hack anymore. However, Tiger still has a + * broken version so we leave the hack in for now. + * + * TODO: Revisit when dropping Tiger support. + * + * Btw. If you configure other network interfaces using ifconfig, + * you run into the same problem. I still don't know how to make the + * tap devices show up in the network configuration panel... + */ + ifaddr_t ifa = (ifaddr_t) arg; + if (ifa == NULL) + return 0; + + sa_family_t af = ifaddr_address_family(ifa); + if (af != AF_INET) + return 0; + + struct ifaliasreq ifra; + int sa_size = sizeof(struct sockaddr); + if (ifaddr_address(ifa, &ifra.ifra_addr, sa_size) + || ifaddr_dstaddress(ifa, &ifra.ifra_broadaddr, sa_size) + || ifaddr_netmask(ifa, &ifra.ifra_mask, sa_size)) { + log(LOG_WARNING, + "tuntap: failed to parse interface address.\n"); + return 0; + } + + // Check that the address family fields match. If not, issue another + // SIOCAIFADDR to fix the entry. + if (ifra.ifra_addr.sa_family != af + || ifra.ifra_broadaddr.sa_family != af + || ifra.ifra_mask.sa_family != af) { + log(LOG_INFO, "tuntap: Fixing address family for %s%d\n", + family_name, unit); + + snprintf(ifra.ifra_name, sizeof(ifra.ifra_name), "%s%d", + family_name, unit); + ifra.ifra_addr.sa_family = af; + ifra.ifra_broadaddr.sa_family = af; + ifra.ifra_mask.sa_family = af; + + do_sock_ioctl(af, SIOCAIFADDR, &ifra); + } + + return 0; + } + + case SIOCSIFFLAGS: + return 0; + + case SIOCGIFSTATUS: + { + struct ifstat *stat = (struct ifstat *) arg; + int len; + char *p; + + if (stat == NULL) + return EINVAL; + + /* print status */ + len = strlen(stat->ascii); + p = stat->ascii + len; + if (open) { + snprintf(p, IFSTATMAX - len, "\topen (pid %u)\n", pid); + } else { + snprintf(p, IFSTATMAX - len, "\tclosed\n"); + } + + return 0; + } + + case SIOCSIFMTU: + { + struct ifreq *ifr = (struct ifreq *) arg; + + if (ifr == NULL) + return EINVAL; + + ifnet_set_mtu(ifp, ifr->ifr_mtu); + + return 0; + } + + case SIOCDIFADDR: + return 0; + + } + + return EOPNOTSUPP; +} + +errno_t +tuntap_interface::if_set_bpf_tap(bpf_tap_mode mode, int (*cb)(ifnet_t, mbuf_t)) +{ + dprintf("tuntap: mode %d\n", mode); + + auto_lock l(&bpf_lock); + + bpf_callback = cb; + bpf_mode = mode; + + return 0; +} + +errno_t +tuntap_interface::if_check_multi(const struct sockaddr *maddr) +{ + dprintf("tuntap: if_check_multi\n"); + + return EOPNOTSUPP; +} + +void +tuntap_interface::if_detached() +{ + dprintf("tuntap: if_detached\n"); + + /* wake unregister_interface() */ + detach_lock.lock(); + interface_detached = true; + detach_lock.wakeup(&interface_detached); + detach_lock.unlock(); + + dprintf("if_detached done\n"); +} + diff --git a/tap-mac/tuntap/src/tuntap.h b/tap-mac/tuntap/src/tuntap.h new file mode 100644 index 00000000..f10d4a06 --- /dev/null +++ b/tap-mac/tuntap/src/tuntap.h @@ -0,0 +1,301 @@ +/* + * ip tunnel/ethertap device for MacOSX. + * + * The class tuntaptap_interface contains the common functionality of tuntap_interface and + * tap_interface. + */ +/* + * Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de> + * + * 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. + * 3. The name of the author may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + */ + +#ifndef __TUNTAP_H__ +#define __TUNTAP_H__ + +#include "util.h" +#include "lock.h" + +extern "C" { + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/select.h> +#include <sys/systm.h> +#include <sys/kpi_mbuf.h> + +#include <kern/locks.h> + +#include <net/if.h> +#include <net/bpf.h> +#include <net/kpi_interface.h> + +} + +extern "C" { + +errno_t tuntap_if_output(ifnet_t ifp, mbuf_t m); +errno_t tuntap_if_ioctl(ifnet_t ifp, long unsigned int cmd, void *arg); +errno_t tuntap_if_set_bpf_tap(ifnet_t ifp, bpf_tap_mode mode, int (*cb)(ifnet_t, mbuf_t)); +errno_t tuntap_if_demux(ifnet_t ifp, mbuf_t m, char *header, protocol_family_t *proto); +errno_t tuntap_if_framer(ifnet_t ifp, mbuf_t *m, const struct sockaddr *dest, + const char *dest_linkaddr, const char *frame_type); +errno_t tuntap_if_add_proto(ifnet_t ifp, protocol_family_t proto, + const struct ifnet_demux_desc *ddesc, u_int32_t ndesc); +errno_t tuntap_if_del_proto(ifnet_t ifp, protocol_family_t proto); +errno_t tuntap_if_check_multi(ifnet_t ifp, const struct sockaddr *maddr); +void tuntap_if_detached(ifnet_t ifp); + +} + +/* forward declaration */ +class tuntap_interface; + +/* both interface families have their manager object that will create, initialize, shutdown and + * delete interfaces. This is (mostly) generic so it can be used both for tun and tap. The only + * exception is the interface creation, therefore this class is abstract. tun and tap have their own + * versions that simply fill in create_interface(). + */ +class tuntap_manager { + + protected: + /* manager cdev gate */ + tt_gate cdev_gate; + /* interface count */ + unsigned int count; + /* an array holding all the interface instances */ + tuntap_interface **tuntaps; + /* the major device number */ + int dev_major; + /* family name */ + char *family; + + /* wether static members are initialized */ + static bool statics_initialized; + + /* major-to-manager-map */ + static const int MAX_CDEV = 256; + static tuntap_manager *mgr_map[MAX_CDEV]; + + /* initializes static members */ + void initialize_statics(); + + public: + /* sets major device number, allocates the interface table. */ + bool initialize(unsigned int count, char *family); + + /* tries to shutdown the family. returns true if successful. the manager object may + * not be deleted if this wasn't called successfully. + */ + bool shutdown(); + + /* the destructor deletes allocated memory and unregisters the character device + * switch */ + virtual ~tuntap_manager(); + + /* here are the cdev routines for the class. They will figure out the manager object + * and call the service methods declared below. + */ + static int cdev_open(dev_t dev, int flags, int devtype, proc_t p); + static int cdev_close(dev_t dev, int flags, int devtype, proc_t p); + static int cdev_read(dev_t dev, uio_t uio, int ioflag); + static int cdev_write(dev_t dev, uio_t uio, int ioflag); + static int cdev_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, + proc_t p); + static int cdev_select(dev_t dev, int which, void *wql, proc_t p); + + protected: + /* Here are the actual service routines that will do the required things (creating + * interfaces and such) and forward to the interface's implementation. + */ + int do_cdev_open(dev_t dev, int flags, int devtype, proc_t p); + int do_cdev_close(dev_t dev, int flags, int devtype, proc_t p); + int do_cdev_read(dev_t dev, uio_t uio, int ioflag); + int do_cdev_write(dev_t dev, uio_t uio, int ioflag); + int do_cdev_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, proc_t p); + int do_cdev_select(dev_t dev, int which, void *wql, proc_t p); + + /* abstract method that will create an interface. Implemented by tun and tap */ + virtual tuntap_interface *create_interface() = 0; + + /* makes sure there is one idle interface available (if nothing fails */ + void ensure_idle_device(); + +}; + +/* a class implementing a mbuf packet queue. On Darwin 7 we had struct ifqueue, but that is now + * internal to the kernel for Darwin 8. So lets have our own. + */ +class tuntap_mbuf_queue { + + private: + /* output end of the queue. dequeueing takes mbufs from here */ + mbuf_t head; + /* input end. new mbufs are appended here. */ + mbuf_t tail; + + /* size */ + unsigned int size; + + /* maximum queue size */ + static const unsigned int QUEUE_SIZE = 128; + + public: + /* initialize new empty queue */ + tuntap_mbuf_queue(); + ~tuntap_mbuf_queue(); + + /* is the queue full? */ + bool full() { return size == QUEUE_SIZE; } + /* is it emtpy? */ + bool empty() { return size == 0; } + + /* enqueue an mbuf. returns true if there was space left, so the mbuf could be + * queued, false otherwise */ + bool enqueue(mbuf_t mb); + + /* tries to dequeue the next mbuf. If the queue is empty, NULL is returned */ + mbuf_t dequeue(); + + /* makes the queue empty, discarding any queue packets */ + void clear(); +}; + +class tuntap_interface { + + protected: + /* interface number */ + unsigned int unit; + /* family name */ + char *family_name; + /* family identifier */ + ifnet_family_t family; + /* interface type */ + u_int32_t type; + /* id string */ + static const unsigned int UIDLEN = 20; + char unique_id[UIDLEN]; + + /* synchronization */ + tt_mutex lock; + tt_mutex bpf_lock; + tt_mutex detach_lock; + + /* the interface structure registered */ + ifnet_t ifp; + /* whether the device has been opened */ + bool open; + /* whether we are doing blocking i/o */ + bool block_io; + /* whether the interface has properly been detached */ + bool interface_detached; + /* handle to the devfs node for the character device */ + void *dev_handle; + /* the pid of the process that opened the cdev, if any */ + pid_t pid; + /* read select info */ + struct selinfo rsel; + /* bpf mode, wether filtering is on or off */ + bpf_tap_mode bpf_mode; + /* bpf callback. called when packet arrives/leaves */ + int (*bpf_callback)(ifnet_t, mbuf_t); + /* pending packets queue (for output), must be accessed with the lock held */ + tuntap_mbuf_queue send_queue; + /* whether an ioctl that we issued is currently being processed */ + bool in_ioctl; + + /* protected constructor. initializes most of the members */ + tuntap_interface(); + virtual ~tuntap_interface(); + + /* initialize the device */ + virtual bool initialize(unsigned short major, unsigned short unit) = 0; + + /* character device management */ + virtual bool register_chardev(unsigned short major); + virtual void unregister_chardev(); + + /* network interface management */ + virtual bool register_interface(const struct sockaddr_dl *lladdr, + void *bcaddr, u_int32_t bcaddrlen); + virtual void unregister_interface(); + virtual void cleanup_interface(); + + /* called when the character device is opened in order to intialize the network + * interface. + */ + virtual int initialize_interface() = 0; + /* called when the character device is closed to shutdown the network interface */ + virtual void shutdown_interface() = 0; + + /* check wether the interface is idle (so it can be brought down) */ + virtual bool idle(); + + /* shut it down */ + virtual void shutdown() = 0; + + /* notifies BPF of a packet coming through */ + virtual void notify_bpf(mbuf_t mb, bool out); + + /* executes a socket ioctl through a temporary socket */ + virtual void do_sock_ioctl(sa_family_t af, unsigned long cmd, void* arg); + + /* character device service methods. Called by the manager */ + virtual int cdev_open(int flags, int devtype, proc_t p); + virtual int cdev_close(int flags, int devtype, proc_t p); + virtual int cdev_read(uio_t uio, int ioflag); + virtual int cdev_write(uio_t uio, int ioflag); + virtual int cdev_ioctl(u_long cmd, caddr_t data, int fflag, proc_t p); + virtual int cdev_select(int which, void *wql, proc_t p); + + /* interface functions. friends and implementation methods */ + friend errno_t tuntap_if_output(ifnet_t ifp, mbuf_t m); + friend errno_t tuntap_if_ioctl(ifnet_t ifp, long unsigned int cmd, void *arg); + friend errno_t tuntap_if_set_bpf_tap(ifnet_t ifp, bpf_tap_mode mode, + int (*cb)(ifnet_t, mbuf_t)); + friend errno_t tuntap_if_demux(ifnet_t ifp, mbuf_t m, char *header, + protocol_family_t *proto); + friend errno_t tuntap_if_framer(ifnet_t ifp, mbuf_t *m, const struct sockaddr *dest, + const char *dest_linkaddr, const char *frame_type); + friend errno_t tuntap_if_add_proto(ifnet_t ifp, protocol_family_t proto, + const struct ifnet_demux_desc *ddesc, u_int32_t ndesc); + friend errno_t tuntap_if_del_proto(ifnet_t ifp, protocol_family_t proto); + friend errno_t tuntap_if_check_multi(ifnet_t ifp, const struct sockaddr *maddr); + friend void tuntap_if_detached(ifnet_t ifp); + + virtual errno_t if_output(mbuf_t m); + virtual errno_t if_ioctl(u_int32_t cmd, void *arg); + virtual errno_t if_set_bpf_tap(bpf_tap_mode mode, int (*cb)(ifnet_t, mbuf_t)); + virtual errno_t if_demux(mbuf_t m, char *header, protocol_family_t *proto) = 0; + virtual errno_t if_framer(mbuf_t *m, const struct sockaddr *dest, + const char *dest_linkaddr, const char *frame_type) = 0; + virtual errno_t if_add_proto(protocol_family_t proto, + const struct ifnet_demux_desc *ddesc, u_int32_t ndesc) = 0; + virtual errno_t if_del_proto(protocol_family_t proto) = 0; + virtual errno_t if_check_multi(const struct sockaddr *maddr); + virtual void if_detached(); + + /* tuntap_manager feeds us with cdev input, so it is our friend */ + friend class tuntap_manager; +}; + +#endif /* __TUNTAP_H__ */ + diff --git a/tap-mac/tuntap/src/tuntap_mgr.cc b/tap-mac/tuntap/src/tuntap_mgr.cc new file mode 100644 index 00000000..f41394e9 --- /dev/null +++ b/tap-mac/tuntap/src/tuntap_mgr.cc @@ -0,0 +1,372 @@ +/* + * ip tunnel/ethertap device for MacOSX. + * + * tuntap_manager definition. + */ +/* + * Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de> + * + * 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. + * 3. The name of the author may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + */ + +#include "tuntap.h" +#include "mem.h" + +extern "C" { + +#include <sys/conf.h> +#include <sys/param.h> +#include <sys/syslog.h> +#include <sys/systm.h> + +#include <vm/vm_kern.h> + +#include <miscfs/devfs/devfs.h> + +} + +#if 0 +#define dprintf(...) log(LOG_INFO, __VA_ARGS__) +#else +#define dprintf(...) +#endif + +/* cdevsw for tuntap_manager */ +static struct cdevsw mgr_cdevsw = +{ + tuntap_manager::cdev_open, + tuntap_manager::cdev_close, + tuntap_manager::cdev_read, + tuntap_manager::cdev_write, + tuntap_manager::cdev_ioctl, + eno_stop, + eno_reset, + NULL, + tuntap_manager::cdev_select, + eno_mmap, + eno_strat, + eno_getc, + eno_putc, + 0 +}; + +/* tuntap_manager members */ +tuntap_manager *tuntap_manager::mgr_map[MAX_CDEV]; + +bool tuntap_manager::statics_initialized = false; + +/* static initializer */ +void +tuntap_manager::initialize_statics() +{ + dprintf("initializing mgr_map\n"); + + /* initialize the major-to-manager map */ + for (int i = 0; i < MAX_CDEV; i++) + mgr_map[i] = NULL; + + statics_initialized = true; +} + +bool +tuntap_manager::initialize(unsigned int count, char *family) +{ + this->count = count; + this->family = family; + this->tuntaps = NULL; + + if (!statics_initialized) + initialize_statics(); + + /* make sure noone can access the character devices until we are done */ + auto_lock l(&cdev_gate); + + /* register the switch for the tap character devices */ + dev_major = cdevsw_add(-1, &mgr_cdevsw); + if (dev_major == -1) { + log(LOG_ERR, "%s: could not register character device switch.\n", family); + return false; + } + + /* allocate memory for the interface instance table */ + tuntaps = (tuntap_interface **) mem_alloc(count * sizeof(tuntap_interface *)); + if (tuntaps == NULL) + { + log(LOG_ERR, "%s: no memory!\n", family); + return false; + } + + bzero(tuntaps, count * sizeof(tuntap_interface *)); + + /* Create the interfaces. This will only add the character devices. The network devices will + * be created upon open()ing the corresponding character devices. + */ + for (int i = 0; i < (int) count; i++) + { + tuntaps[i] = create_interface(); + + if (tuntaps[i] != NULL) + { + if (tuntaps[i]->initialize(dev_major, i)) + { + continue; + } + + /* error here. current interface needs to be shut down */ + i++; + } + + /* something went wrong. clean up. */ + while (--i >= 0) + { + tuntaps[i]->shutdown(); + delete tuntaps[i]; + } + + return false; + } + + /* register the new family in the mgr switch */ + mgr_map[dev_major] = this; + + log(LOG_INFO, "%s kernel extension version %s <mattias.nissler@gmx.de>\n", + family, TUNTAP_VERSION); + + return true; +} + +bool +tuntap_manager::shutdown() +{ + bool ok = true; + + /* we halt the whole thing while we check whether we can shutdown */ + auto_lock l(&cdev_gate); + + /* anyone in? */ + if (cdev_gate.is_anyone_in()) { + dprintf("tuntap_mgr: won't shutdown, threads still behind the gate."); + ok = false; + } else { + /* query the interfaces to see if shutting down is ok */ + if (tuntaps != NULL) { + for (unsigned int i = 0; i < count; i++) { + if (tuntaps[i] != NULL) + ok &= tuntaps[i]->idle(); + } + + /* if yes, do it now */ + if (ok) { + for (unsigned int i = 0; i < count; i++) { + if (tuntaps[i] != NULL) { + tuntaps[i]->shutdown(); + delete tuntaps[i]; + tuntaps[i] = NULL; + } + } + } + } + } + + /* unregister the character device switch */ + if (ok) { + if (dev_major != -1 && cdevsw_remove(dev_major, &mgr_cdevsw) == -1) { + log(LOG_WARNING, + "%s: character device switch got lost. strange.\n", family); + } + mgr_map[dev_major] = NULL; + dev_major = -1; + + /* at this point there is still a chance that some thread hangs at the cdev_gate in + * one of the cdev service functions. I can't imagine any way that would aviod this. + * So lets unblock the gate such that they fail. + */ + unsigned int old_number; + do { + old_number = cdev_gate.get_ticket_number(); + + dprintf("tuntap_manager: waiting for other threads to give up.\n"); + + /* wait one second */ + cdev_gate.sleep(&cdev_gate, 1000000); + + } while (cdev_gate.get_ticket_number() != old_number); + + /* I hope it is safe to unload now. */ + + } else { + log(LOG_WARNING, "%s: won't unload, at least one interface is busy.\n", family); + } + + dprintf("tuntap manager: shutdown %s\n", ok ? "ok" : "failed"); + + return ok; +} + +tuntap_manager::~tuntap_manager() +{ + dprintf("freeing interface table\n"); + + /* free memory */ + if (tuntaps != NULL) + mem_free(tuntaps, count * sizeof(tuntap_interface *)); +} + +/* service method dispatchers */ +int +tuntap_manager::cdev_open(dev_t dev, int flags, int devtype, proc_t p) +{ + return (mgr_map[major(dev)] == NULL ? ENOENT + : mgr_map[major(dev)]->do_cdev_open(dev, flags, devtype, p)); +} + +int +tuntap_manager::cdev_close(dev_t dev, int flags, int devtype, proc_t p) +{ + return (mgr_map[major(dev)] == NULL ? EBADF + : mgr_map[major(dev)]->do_cdev_close(dev, flags, devtype, p)); +} + +int +tuntap_manager::cdev_read(dev_t dev, uio_t uio, int ioflag) +{ + return (mgr_map[major(dev)] == NULL ? EBADF + : mgr_map[major(dev)]->do_cdev_read(dev, uio, ioflag)); +} + +int +tuntap_manager::cdev_write(dev_t dev, uio_t uio, int ioflag) +{ + return (mgr_map[major(dev)] == NULL ? EBADF + : mgr_map[major(dev)]->do_cdev_write(dev, uio, ioflag)); +} + +int +tuntap_manager::cdev_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, proc_t p) +{ + return (mgr_map[major(dev)] == NULL ? EBADF + : mgr_map[major(dev)]->do_cdev_ioctl(dev, cmd, data, fflag, p)); +} + +int +tuntap_manager::cdev_select(dev_t dev, int which, void *wql, proc_t p) +{ + return (mgr_map[major(dev)] == NULL ? EBADF + : mgr_map[major(dev)]->do_cdev_select(dev, which, wql, p)); +} + +/* character device service methods */ +int +tuntap_manager::do_cdev_open(dev_t dev, int flags, int devtype, proc_t p) +{ + int dmin = minor(dev); + int error = ENOENT; + + cdev_gate.enter(); + + if (dmin < (int) count && dmin >= 0 && tuntaps[dmin] != NULL) + error = tuntaps[dmin]->cdev_open(flags, devtype, p); + + cdev_gate.exit(); + + return error; +} + +int +tuntap_manager::do_cdev_close(dev_t dev, int flags, int devtype, proc_t p) +{ + int dmin = minor(dev); + int error = EBADF; + + cdev_gate.enter(); + + if (dmin < (int) count && dmin >= 0 && tuntaps[dmin] != NULL) + error = tuntaps[dmin]->cdev_close(flags, devtype, p); + + cdev_gate.exit(); + + return error; +} + +int +tuntap_manager::do_cdev_read(dev_t dev, uio_t uio, int ioflag) +{ + int dmin = minor(dev); + int error = EBADF; + + cdev_gate.enter(); + + if (dmin < (int) count && dmin >= 0 && tuntaps[dmin] != NULL) + error = tuntaps[dmin]->cdev_read(uio, ioflag); + + cdev_gate.exit(); + + return error; +} + +int +tuntap_manager::do_cdev_write(dev_t dev, uio_t uio, int ioflag) +{ + int dmin = minor(dev); + int error = EBADF; + + cdev_gate.enter(); + + if (dmin < (int) count && dmin >= 0 && tuntaps[dmin] != NULL) + error = tuntaps[dmin]->cdev_write(uio, ioflag); + + cdev_gate.exit(); + + return error; +} + +int +tuntap_manager::do_cdev_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, proc_t p) +{ + int dmin = minor(dev); + int error = EBADF; + + cdev_gate.enter(); + + if (dmin < (int) count && dmin >= 0 && tuntaps[dmin] != NULL) + error = tuntaps[dmin]->cdev_ioctl(cmd, data, fflag, p); + + cdev_gate.exit(); + + return error; +} + +int +tuntap_manager::do_cdev_select(dev_t dev, int which, void *wql, proc_t p) +{ + int dmin = minor(dev); + int error = EBADF; + + cdev_gate.enter(); + + if (dmin < (int) count && dmin >= 0 && tuntaps[dmin] != NULL) + error = tuntaps[dmin]->cdev_select(which, wql, p); + + cdev_gate.exit(); + + return error; +} + diff --git a/tap-mac/tuntap/src/util.h b/tap-mac/tuntap/src/util.h new file mode 100644 index 00000000..0f6955e8 --- /dev/null +++ b/tap-mac/tuntap/src/util.h @@ -0,0 +1,46 @@ +/* + * ip tunnel/ethertap device for MacOSX. + * + * Some utilities and misc stuff. + */ +/* + * Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de> + * + * 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. + * 3. The name of the author may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + */ + +#ifndef __UTIL_H__ +#define __UTIL_H__ + +extern "C" { + +/* In Darwin 8 (OS X Tiger) there is a problem with struct selinfo. It was made `private' to the + * kernel, so its definition is not available from the headers in Kernel.framework. However, we need + * to declare something :-( + */ +struct selinfo { + char data[128]; /* should be enough... */ +}; + +} /* extern "C" */ + +#endif /* __UTIL_H__ */ + |