diff options
Diffstat (limited to 'ext/tap-mac')
24 files changed, 3289 insertions, 0 deletions
diff --git a/ext/tap-mac/README.txt b/ext/tap-mac/README.txt new file mode 100644 index 00000000..177b936f --- /dev/null +++ b/ext/tap-mac/README.txt @@ -0,0 +1,19 @@ +This is a hack of tuntaposx. It's here for two reasons: + +1) There seem to be issues with large MTUs in the original tuntap code, + so we set up our zt0 tap with the correct ZeroTier MTU as the default. + +2) Lots of other mac products (VPNs, etc.) ship their own tap device + drivers that like to conflict with one another. This gives us no + choice but to play along. But we call our tap device zt0, which means + it won't conflict with everyone else's tap0. + +3) It's nice to call the device zt0, same as Linux, for consistency across + *nix platforms. Mac does not seem to support interface renaming. + +This will be placed in the ZeroTier home as a kext and is auto-loaded by the +ZeroTier One binary if /dev/zt0 is not found. It can also be auto-updated. + +See this page for the original: + +http://tuntaposx.sourceforge.net diff --git a/ext/tap-mac/tuntap/Changelog b/ext/tap-mac/tuntap/Changelog new file mode 100644 index 00000000..4599a132 --- /dev/null +++ b/ext/tap-mac/tuntap/Changelog @@ -0,0 +1,141 @@ + +November 1, 2011: + o Make the netmask address family fix work without knowledge of the struct + ifaddr definition. This fixes a crash on Lion, where the layout of the + structure has been changed, but at the cost of the fix no longer working for + IPv6. I think this is OK though, since mDNSResponder has been fixed on + Leopard and beyond to no longer require the hack. + o Proper multicast address checking for tun; multicast should now work reliably + with IP and IPv6 on tun also. + o A quite comprehensive test suite has been added that allows for quick release + testing. + o PPC support has been dropped due to XCode 4 no longer supporting PPC arch. + +September 13, 2009: + o Change linker options to produce 64 bit kext bundle for Snow Leopard. + o Switch from kmem_alloc and friends to OSAlloc for memory allocation and + avoid the delay() call. Respective symbols are not available on 64 bit + kernels anymore. + +September 5, 2009: + o Initial Snow Leopard port. Thanks to various people contributing patches in + the bugtracker. The new official version can only be compiled on Snow + Leopard but has been tested to work on all Tiger, Leopard and Snow Leopard + o Clean up unused locking code and switch to rwlocks even for simple mutexes, + which avoids a symbol incompatibility for Tiger and Leopard. + o Clean up compilation flags in the Makefiles. + +July 4, 2008: + o Adapt the former Leopard package to also be installable on Tiger systems. + This obsoletes the Tiger version, both Leopard and Tiger are now supported + by a single package. + +June 7, 2008: + o Protect the selwakeup() call by the lock. This fixes incorrect select() + behaviour, thanks to Roland Wendelin for reporting this. + o Fix tuntap_mbuf_queue::size initialization + o Use a proper wait condition for synchronization when detaching the network + interface. The old code would crash if the if_detached() handler was called + from a different thread than unregister_interface(). + +January 21, 2008: + o Work around an issue in the Darwin kernel. When unregistering an interface, + addresses are not properly removed from the interface. This leads to + crashes and other problems when reusing the interface. Introduce an ugly + hack that tries to remove all interface addresses when shutting the + interface down. + o Fix a small mbuf leak that could occur when the output queue was full. + Thanks to Oleg Dolgov for reporting this. + +December 21, 2007: + o Fix paths in the startup item postflight scripts + o Check if_ioctl arguments more defensively after a report of a panic after + receiving a NULL arg. + +November 14, 2007: + o I have done a complete rework of the installer package generation. The + package is now edited in PackageMaker. The distribution package can still + be built from the commandline though. + o Fix incorrect permission & ownership of the installed files. + +Oktober 11, 2007: + o Fix the permissions of the postflight scripts. Installer packages should work + again. + o Drop the kmod and kmodc++ in the linker command, they seem to be unneeded + with Leopard. + +September 20, 2007: + o Initial Leopard port, it's basically the latest Tiger version with some + Leopard-related fixes and s/Tiger/Leopard/g + o I have switched to a proper version management system (git) and could + remove some of the CVS hacks subsequently. + o The installation packages have been reworked a bit, they now install into + /System/Extensions and /System/StartupItems directly by using + DestinationPaths. + +May 13, 2006: + o This version is not stable, it may crash, sorry. + o Universal binaries that run on ppc and intel macs. + o Adds tap MAC address randomization + o Redesigned locking. + o Better multicast support + o mDNSResponder workaround, so that the tap interfaces should get picked up + now. Note that we are fixing ifconfig/kernel behaviour here. + o All tapX and tunX devices are visible in /dev at all times, network + interfaces still created dynamically, though. + o Startup items moved to /Library/StartupItems + +May 17, 2005: + o Initial Tiger port. We now have KPI-style interfaces. I guess the Tiger + version is little slower than the Panther version because of all the + wrapping and hiding in the kernel. + o The kernel extensions moved to /Library/Extensions. That is the place where + non-Apple kexts are supposed to live. + +April 21, 2005: + o I added support in tun for AF prepending like some BSDs do. Thanks to Dennis + kSchneider for mailing the initial patch. You can also set the value of + AF_INET6 to be used. + o I finally found that major bug causing crashes (especially on multiprocessor + machines). It also caused a memory leak (lost mbufs), and might have caused + performance/througput/data-loss problems. Everyone is recommended to upgrade. + +April 6, 2005: + o I rewrote the common part concerning the tun and tap initialization and + cleanup. This should make the code more maintainable (less duplication). + o The devices now reinitialize to the state they were started in when they + are closed by an application. This concerns IP addresses for example. + o I changed the package building system to use PackageMaker.app in batch + mode. The packages also check for version 10.3 now, so nobody should be + able to install tun/tap on 10.2 using installer packages. Furthermore I + have sprinkled some warnings telling you not to use tun/tap on SMP machines + over the installation process ;-) + o Some minor locking fixes. + +November 19, 2004: + o Jamie Wood reported that the packet queue in the driver could be considered + empty even if there were packets in it. This was probably caused by a + synchronization problem that should be fixed now. People encountering + timeouts etc. should try the new version. + o I finally implemented support for changing the interface MTU. The driver + enforces the MTU when writing packets to the character device now. However, + packets coming from the kernel are not checked. + +September 9, 2004: + o Marcello Teodori told me that the tun driver wasn't working with openvpn. + The problem was the fcntl call, fixed that. Should work now. Thanks + Marcello! + o changed the tun driver not to prepend the address family field before each + and every packet (which is the behaviour of OpenBSD). As there is currently + only IPv4 and IPv6 support there is no problem with the standard tun + approach used on other OSes. This should make the driver much more + compatible. + o Did a script and makefile support so that the installer packages can now be + built from the command prompt. Unfortunately this might break things + someday as I am not using the 'official' way to build the packages + o Cleaned up installer packages a little. + +August 24, 2004: + o initial version put online + o basic tun/tap support, tap working with qemu + diff --git a/ext/tap-mac/tuntap/Makefile b/ext/tap-mac/tuntap/Makefile new file mode 100644 index 00000000..8d79577e --- /dev/null +++ b/ext/tap-mac/tuntap/Makefile @@ -0,0 +1,12 @@ +TUNTAP_VERSION = 20131028 +BASE= + +all: tap.kext + +clean: + cd src/tap && make -f Makefile clean + +tap.kext: + cd src/tap && make TUNTAP_VERSION=$(TUNTAP_VERSION) -f Makefile all + +.PHONY: test diff --git a/ext/tap-mac/tuntap/README b/ext/tap-mac/tuntap/README new file mode 100644 index 00000000..6a9e526b --- /dev/null +++ b/ext/tap-mac/tuntap/README @@ -0,0 +1,85 @@ + +tun/tap driver for Mac OS X +=========================== + +This is an experimental IP tunnel/ethertap driver for Mac OS X/Darwin. It +provides /dev/tunX and /dev/tapX devices. The maximum number of devices can be +configured at compile time, it is currently set to 16. That should be enough in +most cases. + +The driver ships as two kernel extensions, one for tap and one for tun. They are +located in /Library/Extensions and can also be loaded and unloaded by hand. If +you install the startup item, the system will load them automatically at +startup (tun and tap startup items get installed in /Library/StartupItems). + +Operation & Programming notes +============================= + +tapX are ethertap devices which provide an interface to the kernel's ethernet +layer. Packets can be read from and written to the /dev/tapX character devices +one at a time (same name as the interface that shows up in ifconfig). + +tunX are IP tunnel devices. These can be used to exchange IP packets with the +kernel. You will get single packets for each read() and should write() packets +one at a time to /dev/tunX. + +There are some special ioctls with the tun devices that allow you to have them +prepend the address family of the packet when reading it from /dev/tunX. Using +this mode the driver also expects you put this 4-byte address family field +(network byte order) in front of the packets you write to /dev/tunX. + +Here are the ioctls to setup up address prepending mode (for convenience there +also is a header called tun_ioctls.h in the source package that you can use) +Set the int argument to one if you want to have AF prepending, use 0 if you want +to switch it off. + +#define TUNSIFHEAD _IOW('t', 96, int) +#define TUNGIFHEAD _IOR('t', 97, int) + +Prepending mode is off by default. Currently it is not recommended to switch the +mode while packets are in flight on the device. + +The character devices are always visible in the filesystem as /dev/tunX and +/dev/tapX. The number of available character devices is a compile time constant +and is currently fixed to 16. Each character devices is associated with a +network interface of the same name. The network interfaces are only created when +the corresponding character device is opened by a program and will be removed +when the character device is closed. + +The character devices currently provide a pretty minimal interface. Whole +packets are read and written using a singe read/write call. File descriptors +opened on the devices can also be select()ed and support O_NONBLOCK. +Asynchronous i/o and some ioctls are currently unimplemented, but implementing +them shouldn't be very hard. Do it yourself or contact me if you can't live +without. + +There is another limitation imposed by the Darwin 8 kernel. It concerns the +poll() system call; Darwin currently does *not* support that for (character) +devices. Use select() instead. + +The interfaces can be configured using ifconfig, the tap devices also support +setting the MAC address to be used. Both tun and tap should be ready for IPv6. +Just setup addresses and routing as you would do with other interfaces. + +Please contact me if you find any bugs or have suggestions. + +Enjoy! + +Mattias +<mattias.nissler@gmx.de> + + +Uninstalling +============ + +The installer packages for OS X currently don't have support for uninstall as +the installer doesn't provide it. Remove the following directories if you want +to completely remove the files installed: + +/Library/Extensions/tap.kext +/Library/Extensions/tun.kext +/Library/StartupItems/tap +/Library/StartupItems/tun + +Unload the the kernel extensions or reboot and you're done. + diff --git a/ext/tap-mac/tuntap/README.zerotier-build b/ext/tap-mac/tuntap/README.zerotier-build new file mode 100644 index 00000000..20d6d461 --- /dev/null +++ b/ext/tap-mac/tuntap/README.zerotier-build @@ -0,0 +1,18 @@ +Building the tap for both x86_64 and i386 requires an older version of the +Xcode tools than what now ships for Mavericks (10.9). The newer version +does not support creating i386 kernel images. + +These can be obtained from: + +https://developer.apple.com/downloads + +It requires a bit of a dance to unpack the package and obtain an unpacked +tree, but once it's there you can change the line in tap/Makefile and +build for both architectures. + +This will go on until i386 is thoroughly legacy, at which point we'll +probably start just supporting x86_64. But that might be a while. We want +to support old Macs through their entire useful life. + +Since this build is irritating, a pre-built copy is packaged in ext/ and +is installed by 'make install'. So users shouldn't have to build this. diff --git a/ext/tap-mac/tuntap/src/lock.cc b/ext/tap-mac/tuntap/src/lock.cc new file mode 100644 index 00000000..0da48be2 --- /dev/null +++ b/ext/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/ext/tap-mac/tuntap/src/lock.h b/ext/tap-mac/tuntap/src/lock.h new file mode 100644 index 00000000..51d3299a --- /dev/null +++ b/ext/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/ext/tap-mac/tuntap/src/mem.cc b/ext/tap-mac/tuntap/src/mem.cc new file mode 100644 index 00000000..cd3264fa --- /dev/null +++ b/ext/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/ext/tap-mac/tuntap/src/mem.h b/ext/tap-mac/tuntap/src/mem.h new file mode 100644 index 00000000..4d06fd8c --- /dev/null +++ b/ext/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/ext/tap-mac/tuntap/src/tap/Info.plist b/ext/tap-mac/tuntap/src/tap/Info.plist new file mode 100644 index 00000000..bb9b03fd --- /dev/null +++ b/ext/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/ext/tap-mac/tuntap/src/tap/Makefile b/ext/tap-mac/tuntap/src/tap/Makefile new file mode 100644 index 00000000..ee1f5457 --- /dev/null +++ b/ext/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++ +#CC = gcc +CCP = ../../../../llvm-g++-Xcode4.6.2/bin/llvm-g++ +CC = ../../../../llvm-g++-Xcode4.6.2/bin/llvm-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/ext/tap-mac/tuntap/src/tap/kmod.cc b/ext/tap-mac/tuntap/src/tap/kmod.cc new file mode 100644 index 00000000..f9c4a40e --- /dev/null +++ b/ext/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/ext/tap-mac/tuntap/src/tap/tap.cc b/ext/tap-mac/tuntap/src/tap/tap.cc new file mode 100644 index 00000000..149f1e71 --- /dev/null +++ b/ext/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/ext/tap-mac/tuntap/src/tap/tap.h b/ext/tap-mac/tuntap/src/tap/tap.h new file mode 100644 index 00000000..0ef9e159 --- /dev/null +++ b/ext/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 2800 + +#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/ext/tap-mac/tuntap/src/tuntap.cc b/ext/tap-mac/tuntap/src/tuntap.cc new file mode 100644 index 00000000..7fdbb795 --- /dev/null +++ b/ext/tap-mac/tuntap/src/tuntap.cc @@ -0,0 +1,966 @@ +/* + * 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) */ + // ... evidently not :) -- Adam Ierymenko <adam.ierymenko@zerotier.com> + //chunk_len = min(ifnet_mtu(ifp), min(uio_resid(uio), mlen)); + chunk_len = min(uio_resid(uio),mlen); + 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; + if (uio_resid(uio) <= 0) + 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/ext/tap-mac/tuntap/src/tuntap.h b/ext/tap-mac/tuntap/src/tuntap.h new file mode 100644 index 00000000..f10d4a06 --- /dev/null +++ b/ext/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/ext/tap-mac/tuntap/src/tuntap_mgr.cc b/ext/tap-mac/tuntap/src/tuntap_mgr.cc new file mode 100644 index 00000000..f41394e9 --- /dev/null +++ b/ext/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/ext/tap-mac/tuntap/src/util.h b/ext/tap-mac/tuntap/src/util.h new file mode 100644 index 00000000..0f6955e8 --- /dev/null +++ b/ext/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__ */ + diff --git a/ext/tap-mac/tuntap/startup_item/tap/Resources/English.lproj/Localizable.strings b/ext/tap-mac/tuntap/startup_item/tap/Resources/English.lproj/Localizable.strings new file mode 100644 index 00000000..4d3b29ca --- /dev/null +++ b/ext/tap-mac/tuntap/startup_item/tap/Resources/English.lproj/Localizable.strings @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd"> +<plist version="0.9"> +<dict> + <key>ethertap device kernel extension</key> + <string>ethertap kernel extension</string> + <key>Initializing tap devices</key> + <string>Initializing tap devices</string> +</dict> +</plist> + diff --git a/ext/tap-mac/tuntap/startup_item/tap/StartupParameters.plist b/ext/tap-mac/tuntap/startup_item/tap/StartupParameters.plist new file mode 100644 index 00000000..fe9cf001 --- /dev/null +++ b/ext/tap-mac/tuntap/startup_item/tap/StartupParameters.plist @@ -0,0 +1,6 @@ +{ + Description = "ethertap device kernel extension"; + Provides = ("ethertap"); + Requires = ("Network Configuration"); + OrderPreference = "None"; +} diff --git a/ext/tap-mac/tuntap/startup_item/tap/tap b/ext/tap-mac/tuntap/startup_item/tap/tap new file mode 100755 index 00000000..d7600991 --- /dev/null +++ b/ext/tap-mac/tuntap/startup_item/tap/tap @@ -0,0 +1,33 @@ +#!/bin/sh + +## +# load the tap kext +## + +. /etc/rc.common + +StartService () +{ + ConsoleMessage "Initializing tap devices" + + if [ -d /Library/Extensions/tap.kext ]; then + kextload /Library/Extensions/tap.kext + fi +} + +StopService () +{ + if [ -d /Library/Extensions/tap.kext ]; then + kextunload /Library/Extensions/tap.kext + fi +} + +RestartService () +{ + if [ -d /Library/Extensions/tap.kext ]; then + kextunload /Library/Extensions/tap.kext + kextload /Library/Extensions/tap.kext + fi +} + +RunService "$1" diff --git a/ext/tap-mac/tuntap/startup_item/tun/Resources/English.lproj/Localizable.strings b/ext/tap-mac/tuntap/startup_item/tun/Resources/English.lproj/Localizable.strings new file mode 100644 index 00000000..054790e7 --- /dev/null +++ b/ext/tap-mac/tuntap/startup_item/tun/Resources/English.lproj/Localizable.strings @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd"> +<plist version="0.9"> +<dict> + <key>ip tunnel device kernel extension</key> + <string>ip tunnel kernel extension</string> + <key>Initializing tun devices</key> + <string>Initializing tun devices</string> +</dict> +</plist> + diff --git a/ext/tap-mac/tuntap/startup_item/tun/StartupParameters.plist b/ext/tap-mac/tuntap/startup_item/tun/StartupParameters.plist new file mode 100644 index 00000000..71a92626 --- /dev/null +++ b/ext/tap-mac/tuntap/startup_item/tun/StartupParameters.plist @@ -0,0 +1,6 @@ +{ + Description = "ip tunnel device kernel extension"; + Provides = ("tun"); + Requires = ("Network Configuration"); + OrderPreference = "None"; +} diff --git a/ext/tap-mac/tuntap/startup_item/tun/tun b/ext/tap-mac/tuntap/startup_item/tun/tun new file mode 100755 index 00000000..40b1b411 --- /dev/null +++ b/ext/tap-mac/tuntap/startup_item/tun/tun @@ -0,0 +1,33 @@ +#!/bin/sh + +## +# load the tun kext +## + +. /etc/rc.common + +StartService () +{ + ConsoleMessage "Initializing tun devices" + + if [ -d /Library/Extensions/tun.kext ]; then + kextload /Library/Extensions/tun.kext + fi +} + +StopService () +{ + if [ -d /Library/Extensions/tun.kext ]; then + kextunload /Library/Extensions/tun.kext + fi +} + +RestartService () +{ + if [ -d /Library/Extensions/tun.kext ]; then + kextunload /Library/Extensions/tun.kext + kextload /Library/Extensions/tun.kext + fi +} + +RunService "$1" |