dynamic loading of network stack no longer needed
This commit is contained in:
19
zto/ext/tap-mac/README.txt
Normal file
19
zto/ext/tap-mac/README.txt
Normal file
@@ -0,0 +1,19 @@
|
||||
This is a hack of tuntaposx. It's here for two reasons:
|
||||
|
||||
1) There seem to be issues with large MTUs in the original tuntap code,
|
||||
so we set up our zt0 tap with the correct ZeroTier MTU as the default.
|
||||
|
||||
2) Lots of other mac products (VPNs, etc.) ship their own tap device
|
||||
drivers that like to conflict with one another. This gives us no
|
||||
choice but to play along. But we call our tap device zt0, which means
|
||||
it won't conflict with everyone else's tap0.
|
||||
|
||||
3) It's nice to call the device zt0, same as Linux, for consistency across
|
||||
*nix platforms. Mac does not seem to support interface renaming.
|
||||
|
||||
This will be placed in the ZeroTier home as a kext and is auto-loaded by the
|
||||
ZeroTier One binary if /dev/zt0 is not found. It can also be auto-updated.
|
||||
|
||||
See this page for the original:
|
||||
|
||||
http://tuntaposx.sourceforge.net
|
||||
95
zto/ext/tap-mac/tuntap/Makefile
Normal file
95
zto/ext/tap-mac/tuntap/Makefile
Normal file
@@ -0,0 +1,95 @@
|
||||
# Lets have a version, at last!
|
||||
TUNTAP_VERSION = 20150118
|
||||
|
||||
# BASE install directory
|
||||
BASE=
|
||||
|
||||
all: tap.kext
|
||||
|
||||
keysetup:
|
||||
-security delete-keychain net.sf.tuntaposx.tmp
|
||||
security create-keychain -p $$(head -c 32 /dev/urandom | hexdump -e '"%02x"') \
|
||||
net.sf.tuntaposx.tmp
|
||||
security set-keychain-settings -lut 60 net.sf.tuntaposx.tmp
|
||||
security import identity.p12 -k net.sf.tuntaposx.tmp -f pkcs12 \
|
||||
-P $$(read -sp 'identity passphrase: ' pw && echo "$$pw") -A
|
||||
security find-identity -v net.sf.tuntaposx.tmp | \
|
||||
awk -F \" '$$2 ~ /^Developer ID Application:/ { print $$2 }' > .signing_identity
|
||||
security find-identity -v net.sf.tuntaposx.tmp | \
|
||||
awk -F \" '$$2 ~ /^Developer ID Installer:/ { print $$2 }' > .installer_identity
|
||||
|
||||
pkgbuild/%.pkg: %.kext
|
||||
mkdir -p pkgbuild/$*_root/Library/Extensions
|
||||
cp -pR $*.kext pkgbuild/$*_root/Library/Extensions
|
||||
mkdir -p pkgbuild/$*_root/Library/LaunchDaemons
|
||||
cp pkg/launchd/net.sf.tuntaposx.$*.plist pkgbuild/$*_root/Library/LaunchDaemons
|
||||
pkgbuild --root pkgbuild/$*_root \
|
||||
--component-plist pkg/components/$*.plist \
|
||||
--scripts pkg/scripts/$* pkgbuild/$*.pkg
|
||||
|
||||
tuntap_$(TUNTAP_VERSION).pkg: pkgbuild/tap.pkg pkgbuild/tun.pkg
|
||||
productbuild --distribution pkg/distribution.xml --package-path pkgbuild \
|
||||
--resources pkg/res.dummy \
|
||||
tuntap_$(TUNTAP_VERSION).pkg ; \
|
||||
pkgutil --expand tuntap_$(TUNTAP_VERSION).pkg pkgbuild/tuntap_pkg.d
|
||||
cp -pR pkg/res/ pkgbuild/tuntap_pkg.d/Resources
|
||||
pkgutil --flatten pkgbuild/tuntap_pkg.d tuntap_$(TUNTAP_VERSION).pkg
|
||||
if test -s ".installer_identity"; then \
|
||||
productsign --sign "$$(cat .installer_identity)" --keychain net.sf.tuntaposx.tmp \
|
||||
tuntap_$(TUNTAP_VERSION).pkg tuntap_$(TUNTAP_VERSION).pkg.signed ; \
|
||||
mv tuntap_$(TUNTAP_VERSION).pkg.signed tuntap_$(TUNTAP_VERSION).pkg ; \
|
||||
fi
|
||||
|
||||
pkg: tuntap_$(TUNTAP_VERSION).pkg
|
||||
tar czf tuntap_$(TUNTAP_VERSION).tar.gz \
|
||||
README.installer README tuntap_$(TUNTAP_VERSION).pkg
|
||||
|
||||
# Install targets
|
||||
# They are provided for the gentoo ebuild, but should work just fine for other people as well.
|
||||
install_%_kext: %.kext
|
||||
mkdir -p $(BASE)/Library/Extensions
|
||||
cp -pR $*.kext $(BASE)/Library/Extensions/
|
||||
chown -R root:wheel $(BASE)/Library/Extensions/$*.kext
|
||||
mkdir -p $(BASE)/Library/LaunchDaemons
|
||||
cp pkg/launchd/net.sf.tuntaposx.$*.plist $(BASE)/Library/LaunchDaemons
|
||||
chown -R root:wheel $(BASE)/Library/LaunchDaemons/net.sf.tuntaposx.$*.plist
|
||||
|
||||
install: install_tap_kext install_tun_kext
|
||||
|
||||
tarball: clean
|
||||
touch tuntap_$(TUNTAP_VERSION)_src.tar.gz
|
||||
tar czf tuntap_$(TUNTAP_VERSION)_src.tar.gz \
|
||||
-C .. \
|
||||
--exclude "tuntap/identity.p12" \
|
||||
--exclude "tuntap/tuntap_$(TUNTAP_VERSION)_src.tar.gz" \
|
||||
--exclude "tuntap/tuntap_$(TUNTAP_VERSION).tar.gz" \
|
||||
--exclude "tuntap/tuntap_$(TUNTAP_VERSION).pkg" \
|
||||
--exclude "*/.*" \
|
||||
tuntap
|
||||
|
||||
clean:
|
||||
cd src/tap && make -f Makefile clean
|
||||
cd src/tun && make -f Makefile clean
|
||||
-rm -rf pkgbuild
|
||||
-rm -rf tuntap_$(TUNTAP_VERSION).pkg
|
||||
-rm -f tuntap_$(TUNTAP_VERSION).tar.gz
|
||||
-rm -f tuntap_$(TUNTAP_VERSION)_src.tar.gz
|
||||
|
||||
%.kext:
|
||||
cd src/$* && make TUNTAP_VERSION=$(TUNTAP_VERSION) -f Makefile all
|
||||
if test -s ".signing_identity"; then \
|
||||
codesign -fv --keychain net.sf.tuntaposx.tmp -s "$$(cat .signing_identity)" \
|
||||
$*.kext ; \
|
||||
fi
|
||||
|
||||
test:
|
||||
# configd messes with interface flags, issuing SIOCSIFFLAGS ioctls upon receiving kernel
|
||||
# events indicating protocols have been attached and detached. Unfortunately, configd does
|
||||
# this asynchronously, making the SIOCSIFFLAGS changes totally unpredictable when we bring
|
||||
# our interfaces up and down in rapid succession during our tests. I haven't found a good
|
||||
# way to suppress or handle this mess other than disabling configd temporarily.
|
||||
killall -STOP configd
|
||||
-PYTHONPATH=test python test/tuntap/tuntap_tests.py --tests='$(TESTS)'
|
||||
killall -CONT configd
|
||||
|
||||
.PHONY: test
|
||||
206
zto/ext/tap-mac/tuntap/src/lock.cc
Normal file
206
zto/ext/tap-mac/tuntap/src/lock.cc
Normal file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* 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 <kern/clock.h>
|
||||
|
||||
#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 nanoseconds)
|
||||
{
|
||||
if (lck != NULL) {
|
||||
uint64_t abstime;
|
||||
nanoseconds_to_absolutetime(nanoseconds, &abstime);
|
||||
lck_rw_sleep_deadline(lck, LCK_SLEEP_DEFAULT, cond, THREAD_INTERRUPTIBLE, abstime);
|
||||
}
|
||||
}
|
||||
|
||||
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 nanoseconds)
|
||||
{
|
||||
slock.sleep(cond, nanoseconds);
|
||||
}
|
||||
|
||||
void
|
||||
tt_gate::wakeup(void* cond)
|
||||
{
|
||||
slock.wakeup(cond);
|
||||
}
|
||||
|
||||
160
zto/ext/tap-mac/tuntap/src/lock.h
Normal file
160
zto/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
zto/ext/tap-mac/tuntap/src/mem.cc
Normal file
76
zto/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
zto/ext/tap-mac/tuntap/src/mem.h
Normal file
48
zto/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
zto/ext/tap-mac/tuntap/src/tap/Info.plist
Normal file
36
zto/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
zto/ext/tap-mac/tuntap/src/tap/Makefile
Normal file
60
zto/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
|
||||
CFLAGS = -Wall -Werror -mkernel -force_cpusubtype_ALL \
|
||||
-nostdinc -fno-builtin -fno-stack-protector -msoft-float -fno-common \
|
||||
-arch x86_64 \
|
||||
-DKERNEL -DAPPLE -DKERNEL_PRIVATE -DTUNTAP_VERSION=\"$(TUNTAP_VERSION)\" \
|
||||
-DTAP_KEXT_VERSION=\"$(TAP_KEXT_VERSION)\"
|
||||
CCFLAGS = $(CFLAGS)
|
||||
LDFLAGS = -Wall -Werror -arch x86_64 -Xlinker -kext -nostdlib -lkmodc++ -lkmod -lcc_kext
|
||||
|
||||
CCP = clang -x c++
|
||||
CC = clang -x c
|
||||
LD = clang
|
||||
|
||||
all: $(KMOD_BIN) bundle
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
|
||||
.cc.o:
|
||||
$(CCP) $(CCFLAGS) $(INCLUDE) -c $< -o $@
|
||||
|
||||
$(KMOD_BIN): $(OBJS)
|
||||
$(LD) $(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
zto/ext/tap-mac/tuntap/src/tap/kmod.cc
Normal file
93
zto/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)
|
||||
|
||||
}
|
||||
|
||||
533
zto/ext/tap-mac/tuntap/src/tap/tap.cc
Normal file
533
zto/ext/tap-mac/tuntap/src/tap/tap.cc
Normal file
@@ -0,0 +1,533 @@
|
||||
/*
|
||||
* 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 <mach/thread_policy.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) */
|
||||
|
||||
/* thread_policy_set is exported in Mach.kext, but commented in mach/thread_policy.h in the
|
||||
* Kernel.Framework headers (why?). Add a local declaration to work around that.
|
||||
*/
|
||||
extern "C" {
|
||||
kern_return_t thread_policy_set(
|
||||
thread_t thread,
|
||||
thread_policy_flavor_t flavor,
|
||||
thread_policy_t policy_info,
|
||||
mach_msg_type_number_t count);
|
||||
}
|
||||
|
||||
static unsigned char ETHER_BROADCAST_ADDR[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
|
||||
/* members */
|
||||
tap_interface::tap_interface() {
|
||||
bzero(attached_protos, sizeof(attached_protos));
|
||||
input_thread = THREAD_NULL;
|
||||
}
|
||||
|
||||
bool
|
||||
tap_interface::initialize(unsigned short major, unsigned short unit)
|
||||
{
|
||||
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));
|
||||
|
||||
/* Inject an empty packet to trigger the input thread calling demux(), which will unblock
|
||||
* thread_sync_lock. This is part of a hack to avoid a kernel crash on re-attaching
|
||||
* interfaces, see comment in shutdown_interface for more information.
|
||||
*/
|
||||
mbuf_t empty_mbuf;
|
||||
mbuf_gethdr(MBUF_WAITOK, MBUF_TYPE_DATA, &empty_mbuf);
|
||||
if (empty_mbuf != NULL) {
|
||||
mbuf_pkthdr_setrcvif(empty_mbuf, ifp);
|
||||
mbuf_pkthdr_setlen(empty_mbuf, 0);
|
||||
mbuf_pkthdr_setheader(empty_mbuf, mbuf_data(empty_mbuf));
|
||||
mbuf_set_csum_performed(empty_mbuf, 0, 0);
|
||||
if (ifnet_input(ifp, empty_mbuf, NULL) == 0) {
|
||||
auto_lock l(&thread_sync_lock);
|
||||
for (int i = 0; i < 100 && input_thread == THREAD_NULL; ++i) {
|
||||
dprintf("input thread not found, waiting...\n");
|
||||
thread_sync_lock.sleep(&input_thread, 10000000);
|
||||
}
|
||||
} else {
|
||||
mbuf_freem(empty_mbuf);
|
||||
}
|
||||
}
|
||||
if (input_thread == THREAD_NULL)
|
||||
dprintf("Failed to determine input thread!\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
/* There's a race condition in the kernel that may cause crashes when quickly re-attaching
|
||||
* interfaces. The crash happens when the interface gets re-attached before the input thread
|
||||
* for the interface managed to terminate, in which case an assert on the input_waiting flag
|
||||
* to be clear triggers in ifnet_attach. The bug is really that there's no synchronization
|
||||
* for terminating the input thread. To work around this, the following code does add the
|
||||
* missing synchronization to wait for the input thread to terminate. Of course, threading
|
||||
* primitives available to kexts are few, and I'm not aware of a way to wait for a thread to
|
||||
* terminate. Hence, the code calls thread_policy_set (passing bogus parameters) in a loop,
|
||||
* until it returns KERN_TERMINATED. Since this is all rather fragile, there's an upper
|
||||
* limit on the loop iteratations we're willing to make, so this terminates eventually even
|
||||
* if things change on the kernel side eventually.
|
||||
*/
|
||||
if (input_thread != THREAD_NULL) {
|
||||
dprintf("Waiting for input thread...\n");
|
||||
kern_return_t result = 0;
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
result = thread_policy_set(input_thread, -1, NULL, 0);
|
||||
dprintf("thread_policy_set result: %d\n", result);
|
||||
if (result == KERN_TERMINATED) {
|
||||
dprintf("Input thread terminated.\n");
|
||||
thread_deallocate(input_thread);
|
||||
input_thread = THREAD_NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
auto_lock l(&thread_sync_lock);
|
||||
thread_sync_lock.sleep(&input_thread, 10000000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
errno_t
|
||||
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");
|
||||
|
||||
/* Make note of what input thread this interface is running on. This is part of a hack to
|
||||
* avoid a crash on re-attaching interfaces, see comment in shutdown_interface for details.
|
||||
*/
|
||||
if (input_thread == THREAD_NULL) {
|
||||
auto_lock l(&thread_sync_lock);
|
||||
input_thread = current_thread();
|
||||
thread_reference(input_thread);
|
||||
thread_sync_lock.wakeup(&input_thread);
|
||||
}
|
||||
|
||||
/* size check */
|
||||
if (mbuf_len(m) < sizeof(struct ether_header))
|
||||
return ENOENT;
|
||||
|
||||
/* 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();
|
||||
}
|
||||
|
||||
111
zto/ext/tap-mac/tuntap/src/tap/tap.h
Normal file
111
zto/ext/tap-mac/tuntap/src/tap/tap.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include <kern/thread.h>
|
||||
|
||||
}
|
||||
|
||||
#define TAP_FAMILY_NAME ((char *) "zt")
|
||||
#define TAP_IF_COUNT 32 /* max number of tap interfaces */
|
||||
#define TAP_MTU 2800
|
||||
#define TAP_LLADDR tap_lladdr
|
||||
|
||||
/* the mac address of our interfaces. note that the last byte will be replaced by the unit number */
|
||||
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 {
|
||||
public:
|
||||
tap_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];
|
||||
|
||||
/* The input thread for the network interface. */
|
||||
thread_t input_thread;
|
||||
|
||||
/* 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__ */
|
||||
|
||||
963
zto/ext/tap-mac/tuntap/src/tuntap.cc
Normal file
963
zto/ext/tap-mac/tuntap/src/tuntap.cc
Normal file
@@ -0,0 +1,963 @@
|
||||
/*
|
||||
* 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. */
|
||||
thread_sync_lock.lock();
|
||||
while (!interface_detached)
|
||||
thread_sync_lock.sleep(&interface_detached);
|
||||
thread_sync_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;
|
||||
unsigned int max_data_len = ifnet_mtu(ifp) + ifnet_hdrlen(ifp);
|
||||
int error;
|
||||
|
||||
/* stuff the data into the mbuf(s) */
|
||||
mb = first;
|
||||
while (uio_resid(uio) > 0) {
|
||||
/* copy a chunk. enforce mtu (don't know if this is correct behaviour) */
|
||||
chunk_len = min(max_data_len - copied, min(uio_resid(uio), mlen));
|
||||
error = uiomove((caddr_t) mbuf_data(mb), chunk_len, uio);
|
||||
if (error) {
|
||||
log(LOG_ERR, "tuntap: could not copy data from userspace: %d\n", error);
|
||||
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 >= max_data_len)
|
||||
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() */
|
||||
thread_sync_lock.lock();
|
||||
interface_detached = true;
|
||||
thread_sync_lock.wakeup(&interface_detached);
|
||||
thread_sync_lock.unlock();
|
||||
|
||||
dprintf("if_detached done\n");
|
||||
}
|
||||
|
||||
301
zto/ext/tap-mac/tuntap/src/tuntap.h
Normal file
301
zto/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 thread_sync_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
zto/ext/tap-mac/tuntap/src/tuntap_mgr.cc
Normal file
372
zto/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
zto/ext/tap-mac/tuntap/src/util.h
Normal file
46
zto/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__ */
|
||||
|
||||
Reference in New Issue
Block a user