mirror of
https://github.com/ZeroTier/ZeroTierOne
synced 2025-08-21 13:54:15 -07:00
Upgrade LZ4, remove extraneous files, put tap-mac into ext/ to declutter root.
This commit is contained in:
parent
9455b1cc81
commit
6b8c90bffd
33 changed files with 815 additions and 1952 deletions
141
ext/tap-mac/tuntap/Changelog
Normal file
141
ext/tap-mac/tuntap/Changelog
Normal file
|
@ -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
|
||||
|
12
ext/tap-mac/tuntap/Makefile
Normal file
12
ext/tap-mac/tuntap/Makefile
Normal file
|
@ -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
|
85
ext/tap-mac/tuntap/README
Normal file
85
ext/tap-mac/tuntap/README
Normal file
|
@ -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.
|
||||
|
18
ext/tap-mac/tuntap/README.zerotier-build
Normal file
18
ext/tap-mac/tuntap/README.zerotier-build
Normal file
|
@ -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.
|
201
ext/tap-mac/tuntap/src/lock.cc
Normal file
201
ext/tap-mac/tuntap/src/lock.cc
Normal file
|
@ -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);
|
||||
}
|
||||
|
160
ext/tap-mac/tuntap/src/lock.h
Normal file
160
ext/tap-mac/tuntap/src/lock.h
Normal file
|
@ -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__ */
|
||||
|
76
ext/tap-mac/tuntap/src/mem.cc
Normal file
76
ext/tap-mac/tuntap/src/mem.cc
Normal file
|
@ -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);
|
||||
}
|
||||
|
48
ext/tap-mac/tuntap/src/mem.h
Normal file
48
ext/tap-mac/tuntap/src/mem.h
Normal file
|
@ -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__ */
|
||||
|
36
ext/tap-mac/tuntap/src/tap/Info.plist
Normal file
36
ext/tap-mac/tuntap/src/tap/Info.plist
Normal file
|
@ -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>
|
||||
|
60
ext/tap-mac/tuntap/src/tap/Makefile
Normal file
60
ext/tap-mac/tuntap/src/tap/Makefile
Normal file
|
@ -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 = $(HOME)/Code/llvm-g++-Xcode4.6.2/bin/llvm-g++
|
||||
CC = $(HOME)/Code/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)
|
||||
|
93
ext/tap-mac/tuntap/src/tap/kmod.cc
Normal file
93
ext/tap-mac/tuntap/src/tap/kmod.cc
Normal file
|
@ -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)
|
||||
|
||||
}
|
||||
|
452
ext/tap-mac/tuntap/src/tap/tap.cc
Normal file
452
ext/tap-mac/tuntap/src/tap/tap.cc
Normal file
|
@ -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();
|
||||
}
|
||||
|
103
ext/tap-mac/tuntap/src/tap/tap.h
Normal file
103
ext/tap-mac/tuntap/src/tap/tap.h
Normal file
|
@ -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__ */
|
||||
|
966
ext/tap-mac/tuntap/src/tuntap.cc
Normal file
966
ext/tap-mac/tuntap/src/tuntap.cc
Normal file
|
@ -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");
|
||||
}
|
||||
|
301
ext/tap-mac/tuntap/src/tuntap.h
Normal file
301
ext/tap-mac/tuntap/src/tuntap.h
Normal file
|
@ -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__ */
|
||||
|
372
ext/tap-mac/tuntap/src/tuntap_mgr.cc
Normal file
372
ext/tap-mac/tuntap/src/tuntap_mgr.cc
Normal file
|
@ -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;
|
||||
}
|
||||
|
46
ext/tap-mac/tuntap/src/util.h
Normal file
46
ext/tap-mac/tuntap/src/util.h
Normal file
|
@ -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__ */
|
||||
|
|
@ -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>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
Description = "ethertap device kernel extension";
|
||||
Provides = ("ethertap");
|
||||
Requires = ("Network Configuration");
|
||||
OrderPreference = "None";
|
||||
}
|
33
ext/tap-mac/tuntap/startup_item/tap/tap
Executable file
33
ext/tap-mac/tuntap/startup_item/tap/tap
Executable file
|
@ -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"
|
|
@ -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>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
Description = "ip tunnel device kernel extension";
|
||||
Provides = ("tun");
|
||||
Requires = ("Network Configuration");
|
||||
OrderPreference = "None";
|
||||
}
|
33
ext/tap-mac/tuntap/startup_item/tun/tun
Executable file
33
ext/tap-mac/tuntap/startup_item/tun/tun
Executable file
|
@ -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"
|
Loading…
Add table
Add a link
Reference in a new issue