Put default MTU in tap back to 2800, clean up tun-related files and other things from the original pre-fork tap code base that we will never use.

This commit is contained in:
Adam Ierymenko 2013-10-28 15:18:06 -04:00
parent d290306bb3
commit 12b297a712
48 changed files with 8 additions and 3595 deletions

View File

@ -20,18 +20,4 @@ Edit Makefile.linux if you want to change between debug or release build.
-- Windows
Right now Windows builds using Visual Studio 2012. Technically it should also
build with other tools, but VS2012 is what your author is using.
Load the ZeroTierOne.sln solution. Typically you won't need to worry about
any projects other than ZeroTierOne, Service, and InstallerUpdater. SelfTest
builds the selftest program which can be used to test many aspects of the
core code base. TapDriver builds the Ethernet tap driver, which shouldn't
be something end users need to do and requires a driver signing key to create
a publishable driver that users can load without big red warnings.
To just test ZeroTier One itself, build ZeroTierOne and run the binary in
an administrator-mode command window.
Instructions for doing a full build will be forthcoming once all the details
are worked out. Windows is still a work in progress.
Here be dragons.

View File

@ -7,7 +7,7 @@ By convention, ZeroTier One will keep its state here on mac:
/Library/Application Support/ZeroTier/One
ZeroTier ships with a kernel extension for its own tap device, which it
stores in the above directory. To install this from source, type:
stores in the above directory. To install this, type:
sudo make -f Makefile.mac install-mac-tap

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>KEXT</string>
<key>CFBundleShortVersionString</key>
<string>20111101</string>
<string>20131028</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>

View File

@ -1,37 +0,0 @@
Building
========
If you want to build the tun/tap drivers, make sure you have Apple's Developer
Tools installed. Then from the top of the tun/tap source tree issue
# make
This will build the driver kexts and place them as tap.kext and tun.kext in the
top tun/tap directory. You can install these two kexts directly in
/Library/Extensions. As root, you can simply say
# make install_kext
Otherwise OS X will refuse to load the extensions. After that, they can be
loaded using kextload:
# kextload /Library/Extensions/tap.kext/
kextload: /Library/Extensions/tap.kext/ loaded successfully
# kextload /Library/Extensions/tun.kext/
kextload: /Library/Extensions/tun.kext/ loaded successfully
If you want to have the extensions loaded at boot time, use the startup items
provided in the directory startup_items. This can be done using the Makefile (as
root):
# make install_scripts
I have also included the files used to build the installer packages in the
directory pkg. They can be made by
# make pkg
This is all I have to say at the moment, feel free to mail me any bug reports,
problems and suggestions you have at <mattias.nissler@gmx.de>.

View File

@ -1,66 +1,12 @@
# Lets have a version, at last!
TUNTAP_VERSION = 20111101
# BASE install directory
TUNTAP_VERSION = 20131028
BASE=
all: tap.kext tun.kext
pkg: all
/Developer/Applications/Utilities/PackageMaker.app/Contents/MacOS/PackageMaker \
-d pkg/tuntap.pmdoc -o tuntap_$(TUNTAP_VERSION).pkg -v
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: tap.kext tun.kext
mkdir -p ${BASE}/Library/Extensions
cp -pR tap.kext ${BASE}/Library/Extensions/
chown -R root:wheel ${BASE}/Library/Extensions/tap.kext
cp -pR tun.kext ${BASE}/Library/Extensions/
chown -R root:wheel ${BASE}/Library/Extensions/tun.kext
install_scripts:
mkdir -p ${BASE}/Library/StartupItems
cp -pR startup_item/tap ${BASE}/Library/StartupItems/
chown -R root:wheel ${BASE}/Library/StartupItems/tap
cp -pR startup_item/tun ${BASE}/Library/StartupItems/
chown -R root:wheel ${BASE}/Library/StartupItems/tun
install: install_kext install_scripts
tarball: clean
touch tuntap_$(TUNTAP_VERSION)_src.tar.gz
tar czf tuntap_$(TUNTAP_VERSION)_src.tar.gz \
-C .. \
--exclude "tuntap/tuntap_$(TUNTAP_VERSION)_src.tar.gz" \
--exclude "tuntap/tuntap_$(TUNTAP_VERSION).tar.gz" \
--exclude "tuntap/tuntap_$(TUNTAP_VERSION).pkg" \
--exclude "*/.*" \
tuntap
all: tap.kext
clean:
cd src/tap && make -f Makefile clean
cd src/tun && make -f Makefile clean
-rm -rf tuntap_$(TUNTAP_VERSION).pkg
-rm -f tuntap_$(TUNTAP_VERSION).tar.gz
-rm -f tuntap_$(TUNTAP_VERSION)_src.tar.gz
tap.kext:
cd src/tap && make TUNTAP_VERSION=$(TUNTAP_VERSION) -f Makefile all
tun.kext:
cd src/tun && make TUNTAP_VERSION=$(TUNTAP_VERSION) -f Makefile all
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

View File

@ -1,12 +0,0 @@
tun/tap driver installer
========================
This binary distribution contains a single installer package. Just open it to
install TunTap on your computer. The installer allows to customize which parts
of the package are installed in case you only need either tun or tap.
Unless you really know what you are doing, you should also install the startup
item. If you do not install it, you will have to load the kernel extensions
manually after each reboot using kextload.

View File

@ -1,18 +0,0 @@
{\rtf1\ansi\ansicpg1252\cocoartf949
{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
{\colortbl;\red255\green255\blue255;}
{\*\listtable{\list\listtemplateid1\listhybrid{\listlevel\levelnfc0\levelnfcn0\leveljc2\leveljcn2\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{decimal\}.}{\leveltext\leveltemplateid0\'02\'05.;}{\levelnumbers\'01;}}{\listname ;}\listid1}}
{\*\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}}
\paperw11900\paperh16840\margl1440\margr1440\vieww9000\viewh8400\viewkind0
\deftab720
\pard\pardeftab720\sa320\ql\qnatural
\f0\fs28 \cf0 tun/tap driver for Mac OS X\uc0\u8232 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:\
\pard\tx220\tx720\pardeftab720\li720\fi-720\ql\qnatural
\ls1\ilvl0\cf0 {\listtext 1. }Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\
{\listtext 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.\
{\listtext 3. }The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.\
\
\pard\pardeftab720\sa320\ql\qnatural
\cf0 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.}

View File

@ -1,14 +0,0 @@
{\rtf1\ansi\ansicpg1252\cocoartf949
{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
{\colortbl;\red255\green255\blue255;}
\paperw11900\paperh16840\margl1440\margr1440\vieww9000\viewh8400\viewkind0
\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\tx9000\ri500\ql\qnatural\pardirnatural
\f0\fs28 \cf0 This will install the TunTap software on your computer. It provides IP Tunnel and ethertap kernel extensions.\
\
TunTap software is free (as in "free beer" as well as "freedom"). If you like it, you can support further development by donating money. In order to do so just click the image below.\
\
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
{\field{\*\fldinst{HYPERLINK "https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=mattias%2enissler%40gmx%2ede&item_name=TunTap%20driver%20development%20donations&no_shipping=1&no_note=1&tax=0&currency_code=EUR&bn=PP%2dDonationsBF&charset=UTF%2d8"}}{\fldrslt
\fs24 \cf0 {{\NeXTGraphic paypal_button.gif \width1240 \height620
}¬}}}}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -1,12 +0,0 @@
#!/bin/sh
# old versions resided in /System/Library, remove.
rm -r /System/Library/StartupItems/tap
# Fix ownership and permissions. PackageMaker gets this wrong *sigh*
chown -R root:wheel /Library/StartupItems/tap
chmod -R u=rwX,g=rX,o=rX /Library/StartupItems/tap
# exit successfully
exit 0

View File

@ -1,12 +0,0 @@
#!/bin/sh
# old versions resided in /System/Library, remove.
rm -r /System/Library/StartupItems/tun
# Fix ownership and permissions. PackageMaker gets this wrong *sigh*
chown -R root:wheel /Library/StartupItems/tun
chmod -R u=rwX,g=rX,o=rX /Library/StartupItems/tun
# exit successfully
exit 0

View File

@ -1,15 +0,0 @@
#!/bin/sh
# old versions resided in /System/Library, remove.
rm -r /System/Library/Extensions/tap.kext
# unload an old extension (might fail)
kextunload /Library/Extensions/tap.kext
# Fix ownership and permissions. PackageMaker gets this wrong *sigh*
chown -R root:wheel /Library/Extensions/tap.kext
chmod -R u=rwX,g=rX,o=rX /Library/Extensions/tap.kext
# load the new version
kextload /Library/Extensions/tap.kext

View File

@ -1,15 +0,0 @@
#!/bin/sh
# old versions resided in /System/Library, remove.
rm -r /System/Library/Extensions/tun.kext
# unload an old extension (might fail)
kextunload /Library/Extensions/tun.kext
# Fix ownership. The installer gets this wrong *sigh*
chown -R root:wheel /Library/Extensions/tun.kext
chmod -R u=rwX,g=rX,o=rX /Library/Extensions/tun.kext
# load the new version
kextload /Library/Extensions/tun.kext

View File

@ -1 +0,0 @@
<pkg-contents spec="1.12"/>

View File

@ -1 +0,0 @@
<pkgref spec="1.12" uuid="1EE6E557-1933-4D9E-BB87-D42DE08AB8D0"><config><identifier>tuntap.tun</identifier><version>1</version><description></description><post-install type="none"/><requireAuthorization/><installFrom relative="true" mod="true" includeRoot="true">../tun.kext</installFrom><installTo mod="true">/Library/Extensions</installTo><flags><followSymbolicLinks/></flags><packageStore type="internal"></packageStore><mod>scripts.scriptsDirectoryPath.isAbsoluteType</mod><mod>scripts.scriptsDirectoryPath.isRelativeType</mod><mod>installTo</mod><mod>scripts.postinstall.path</mod><mod>includeRoot</mod><mod>installFrom.isRelativeType</mod><mod>version</mod><mod>parent</mod><mod>scripts.scriptsDirectoryPath.path</mod><mod>identifier</mod><mod>requireAuthorization</mod><mod>installFrom.path</mod><mod>extraFiles</mod><mod>installTo.path</mod><mod>postInstall</mod></config><scripts><postinstall relative="true" mod="true">scripts/tun/postflight</postinstall><scripts-dir relative="true" mod="true">scripts/tun</scripts-dir></scripts><contents><component id="foo.tun" path="/Users/mattias/src/tuntap/tuntap_combined/tuntap/tun.kext" version="1.0"/><filter>/CVS$</filter><filter>/\.svn$</filter><filter>/\.cvsignore$</filter><filter>/\.cvspass$</filter><filter>/\.DS_Store$</filter></contents><extra-files/></pkgref>

View File

@ -1 +0,0 @@
<pkg-contents spec="1.12"/>

View File

@ -1 +0,0 @@
<pkgref spec="1.12" uuid="4CB20DA2-D296-427E-A225-F421EE511933"><config><identifier>tuntap.tap</identifier><version>1</version><description></description><post-install type="none"/><requireAuthorization/><installFrom relative="true" mod="true" includeRoot="true">../tap.kext</installFrom><installTo mod="true">/Library/Extensions</installTo><flags><followSymbolicLinks/></flags><packageStore type="internal"></packageStore><mod>installTo</mod><mod>scripts.scriptsDirectoryPath.path</mod><mod>scripts.postinstall.path</mod><mod>installFrom.path</mod><mod>scripts.scriptsDirectoryPath.isRelativeType</mod><mod>parent</mod><mod>scripts.scriptsDirectoryPath.isAbsoluteType</mod><mod>includeRoot</mod><mod>identifier</mod><mod>installTo.path</mod></config><scripts><postinstall relative="true" mod="true">scripts/tap/postflight</postinstall><scripts-dir relative="true" mod="true">scripts/tap</scripts-dir></scripts><contents><component id="foo.tap" path="/Users/mattias/src/tuntap/tuntap_combined/tuntap/tap.kext" version="1.0"/><filter>/CVS$</filter><filter>/\.svn$</filter><filter>/\.cvsignore$</filter><filter>/\.cvspass$</filter><filter>/\.DS_Store$</filter></contents></pkgref>

View File

@ -1 +0,0 @@
<pkg-contents spec="1.12"/>

View File

@ -1 +0,0 @@
<pkgref spec="1.12" uuid="F67DBB20-6DFB-43F2-8DF3-994CBC673785"><config><identifier>tuntap.startup_item.tap</identifier><version>1</version><description></description><post-install type="none"/><requireAuthorization/><installFrom relative="true" mod="true" includeRoot="true">../startup_item/tap</installFrom><installTo mod="true">/Library/StartupItems</installTo><flags><followSymbolicLinks/></flags><packageStore type="internal"></packageStore><mod>installTo</mod><mod>scripts.scriptsDirectoryPath.path</mod><mod>installFrom.path</mod><mod>scripts.scriptsDirectoryPath.isRelativeType</mod><mod>scripts.postinstall.path</mod><mod>parent</mod><mod>scripts.scriptsDirectoryPath.isAbsoluteType</mod><mod>includeRoot</mod><mod>identifier</mod><mod>installTo.path</mod></config><scripts><postinstall relative="true" mod="true">scripts/startup_item_tap/postflight</postinstall><scripts-dir relative="true" mod="true">scripts/startup_item_tap</scripts-dir></scripts></pkgref>

View File

@ -1 +0,0 @@
<pkg-contents spec="1.12"/>

View File

@ -1 +0,0 @@
<pkgref spec="1.12" uuid="D5616A79-9CE9-4280-B9C7-49B852DB4649"><config><identifier>tuntap.startup_item.tun</identifier><version>1</version><description></description><post-install type="none"/><requireAuthorization/><installFrom relative="true" mod="true" includeRoot="true">../startup_item/tun</installFrom><installTo mod="true">/Library/StartupItems</installTo><flags><followSymbolicLinks/></flags><packageStore type="internal"></packageStore><mod>installTo</mod><mod>scripts.scriptsDirectoryPath.path</mod><mod>scripts.postinstall.path</mod><mod>scripts.scriptsDirectoryPath.isRelativeType</mod><mod>identifier</mod><mod>parent</mod><mod>scripts.scriptsDirectoryPath.isAbsoluteType</mod><mod>installTo.path</mod><mod>includeRoot</mod><mod>installFrom.path</mod></config><scripts><postinstall relative="true" mod="true">scripts/startup_item_tun/postflight</postinstall><scripts-dir relative="true" mod="true">scripts/startup_item_tun</scripts-dir></scripts></pkgref>

View File

@ -1 +0,0 @@
<pkmkdoc spec="1.12"><properties><title>TunTap Installer package</title><build>/Users/mattias/Documents/tuntap.pkg</build><organization>tuntap.pkg</organization><userSees ui="both"/><min-target os="2"/><domain system="true"/></properties><distribution><versions min-spec="1.000000"/><scripts></scripts></distribution><contents><choice title="IP Tunnel kernel extension" id="tun" description="Provides virtual network interfaces allowing applications to exchange IP packets with the kernel." starts_selected="true" starts_enabled="true" starts_hidden="false"><pkgref id="tuntap.tun"/><choice-reqs><requirement id="tosv" operator="ge" value="'10.4'" selected="no" enabled="no" hidden="unchanged" startSelected="unchanged" startEnabled="unchanged" startHidden="unchanged"/><requirement id="sysc" operator="eq" value="'7'" selected="no" enabled="no" hidden="unchanged" startSelected="unchanged" startEnabled="unchanged" startHidden="unchanged"><argument>hw.cputype</argument></requirement></choice-reqs></choice><choice title="Ethertap kernel extension" id="tap" description="The ethertap kernel extensions allows applications to connect simulated ethernet segments to the kernel via virtual ethernet interfaces." starts_selected="true" starts_enabled="true" starts_hidden="false"><pkgref id="tuntap.tap"/><choice-reqs><requirement id="tosv" operator="ge" value="'10.4'" selected="no" enabled="no" hidden="unchanged" startSelected="unchanged" startEnabled="unchanged" startHidden="unchanged"/><requirement id="sysc" operator="eq" value="'7'" selected="no" enabled="no" hidden="unchanged" startSelected="unchanged" startEnabled="unchanged" startHidden="unchanged"><argument>hw.cputype</argument></requirement></choice-reqs></choice><choice title="TunTap Startup Item" id="startupitem" description="The TunTap startup item will load the kernel extensions at system boot. It is recommended you install this, otherwise you will have to load the kernel extensions manually after a reboot." starts_selected="true" starts_enabled="true" starts_hidden="false"><pkgref id="tuntap.startup_item.tap"/><pkgref id="tuntap.startup_item.tun"/></choice></contents><resources bg-scale="none" bg-align="topleft"><locale lang="en"><resource relative="true" mod="true" type="license">res/License.rtfd</resource><resource relative="true" mod="true" type="welcome">res/Welcome.rtfd</resource></locale></resources><requirements><requirement id="sysc" operator="eq" value="'7'"><argument>hw.cputype</argument><message-title>Machine architecture not supported</message-title><message>This package only works on Intel Macs.</message></requirement></requirements><flags/><extra-files/><item type="file">01tun.xml</item><item type="file">02tap.xml</item><item type="file">03tap.xml</item><item type="file">04tun.xml</item><mod>extraFiles</mod><mod>properties.title</mod><mod>properties.customizeOption</mod><mod>properties.anywhereDomain</mod><mod>properties.systemDomain</mod></pkmkdoc>

View File

@ -28,9 +28,9 @@ CCFLAGS = $(CFLAGS)
LDFLAGS = -Wall -mkernel -nostdlib -r -lcc_kext -arch i386 -arch x86_64 -Xlinker -kext
#CCP = g++
#CC = gcc
CCP = $(HOME)/Code/llvm-g++-Xcode4.6.2/bin/llvm-g++
CC = gcc
CC = $(HOME)/Code/llvm-g++-Xcode4.6.2/bin/llvm-gcc
all: $(KMOD_BIN) bundle

View File

@ -34,7 +34,7 @@
#define TAP_IF_COUNT 16 /* max number of tap interfaces */
#define TAP_MTU 4000
#define TAP_MTU 2800
#define TAP_LLADDR tap_lladdr

View File

@ -1,36 +0,0 @@
<?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>

View File

@ -1,59 +0,0 @@
#
# ip tunnel/ethertap driver for MacOSX
#
# Makefile
#
# (c) 2004, 2005, 2006, 2007, 2008 Mattias Nissler
#
OBJS = ../tuntap.o ../tuntap_mgr.o ../lock.o ../mem.o \
kmod.o tun_inet_proto.o tun_inet6_proto.o tun.o
KMOD_BIN = tun
BUNDLE_DIR = ../..
BUNDLE_NAME = tun.kext
TUN_KEXT_VERSION = $(TUNTAP_VERSION)
BUNDLE_REGION = English
BUNDLE_IDENTIFIER = foo.tun
BUNDLE_SIGNATURE = ????
BUNDLE_PACKAGETYPE = KEXT
BUNDLE_VERSION = $(TUN_KEXT_VERSION)
INCLUDE = -I.. -I/System/Library/Frameworks/Kernel.framework/Headers
CFLAGS = -Wall -mkernel -force_cpusubtype_ALL \
-fno-builtin -fno-stack-protector -arch i386 -arch x86_64 \
-DKERNEL -D__APPLE__ -DKERNEL_PRIVATE -DTUNTAP_VERSION=\"$(TUNTAP_VERSION)\" \
-DTUN_KEXT_VERSION=\"$(TUN_KEXT_VERSION)\"
CCFLAGS = $(CFLAGS)
LDFLAGS = -Wall -mkernel -nostdlib -r -lcc_kext -arch i386 -arch x86_64 -Xlinker -kext
CCP = g++
CC = gcc
all: $(KMOD_BIN) bundle
.c.o:
$(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
.cc.o:
$(CCP) $(CCFLAGS) $(INCLUDE) -c $< -o $@
$(KMOD_BIN): $(OBJS)
$(CCP) $(LDFLAGS) -o $(KMOD_BIN) $(OBJS)
bundle: $(KMOD_BIN)
rm -rf $(BUNDLE_DIR)/$(BUNDLE_NAME)
mkdir -p $(BUNDLE_DIR)/$(BUNDLE_NAME)/Contents/MacOS
cp $(KMOD_BIN) $(BUNDLE_DIR)/$(BUNDLE_NAME)/Contents/MacOS
sed -e "s/@@CFBUNDLEEXECUTABLE@@/$(KMOD_BIN)/" \
-e "s/@@CFBUNDLEDEVELOPMENTREGION@@/$(BUNDLE_REGION)/" \
-e "s/@@CFBUNDLEIDENTIFIER@@/$(BUNDLE_IDENTIFIER)/" \
-e "s/@@CFBUNDLESIGNATURE@@/$(BUNDLE_SIGNATURE)/" \
-e "s/@@CFBUNDLEPACKAGETYPE@@/$(BUNDLE_PACKAGETYPE)/" \
-e "s/@@CFBUNDLEVERSION@@/$(BUNDLE_VERSION)/" \
Info.plist > $(BUNDLE_DIR)/$(BUNDLE_NAME)/Contents/Info.plist
clean:
-rm -f $(OBJS) $(KMOD_BIN)
-rm -rf $(BUNDLE_DIR)/$(BUNDLE_NAME)

View File

@ -1,93 +0,0 @@
/*
* ip tunnel device for MacOSX.
*
* Kext definition (it is a mach kmod really...)
*/
/*
* Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de>
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials provided
* with the distribution.
* 3. The name of the author may not be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "tun.h"
#include "mem.h"
extern "C" {
#include <sys/param.h>
#include <mach/kmod.h>
static tun_manager *mgr;
/*
* start function. called when the kext gets loaded.
*/
static kern_return_t tun_module_start(struct kmod_info *ki, void *data)
{
mem_initialize(TUN_FAMILY_NAME);
/* initialize locking */
if (!tt_lock::initialize())
return KMOD_RETURN_FAILURE;
/* create a tun manager that will handle the rest */
mgr = new tun_manager();
if (mgr != NULL) {
if (mgr->initialize(TUN_IF_COUNT, TUN_FAMILY_NAME))
return KMOD_RETURN_SUCCESS;
delete mgr;
mgr = NULL;
/* clean up locking */
tt_lock::shutdown();
}
return KMOD_RETURN_FAILURE;
}
/*
* stop function. called when the kext should be unloaded. unloading can be prevented by
* returning failure
*/
static kern_return_t tun_module_stop(struct kmod_info *ki, void *data)
{
if (mgr != NULL) {
if (!mgr->shutdown())
return KMOD_RETURN_FAILURE;
delete mgr;
mgr = NULL;
}
/* clean up locking */
tt_lock::shutdown();
mem_shutdown();
return KMOD_RETURN_SUCCESS;
}
KMOD_DECL(tun, TUN_KEXT_VERSION)
}

View File

@ -1,424 +0,0 @@
/*
* ip tunnel device for MacOSX.
*
* tun_interface class definition
*/
/*
* Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de>
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials provided
* with the distribution.
* 3. The name of the author may not be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "tun.h"
extern "C" {
#include <sys/syslog.h>
#include <sys/param.h>
#include <net/if_types.h>
#include <net/kpi_protocol.h>
#include <netinet/ip.h>
}
#if 0
#define dprintf(...) log(LOG_INFO, __VA_ARGS__)
#else
#define dprintf(...)
#endif
/* members */
bool
tun_interface::initialize(unsigned short major, unsigned short unit)
{
this->unit = unit;
this->family_name = TUN_FAMILY_NAME;
this->family = IFNET_FAMILY_TUN;
this->type = IFT_OTHER;
bzero(unique_id, UIDLEN);
snprintf(unique_id, UIDLEN, "%s%d", family_name, unit);
dprintf("tun: starting interface %s%d\n", family_name, unit);
/* register character device */
if (!tuntap_interface::register_chardev(major))
return false;
return true;
}
void
tun_interface::shutdown()
{
dprintf("tun: shutting down interface %s%d\n", family_name, unit);
unregister_chardev();
}
int
tun_interface::initialize_interface()
{
prepend_af = false;
/* register interface */
if (!tuntap_interface::register_interface(NULL, NULL, 0))
return EIO;
/* set mtu */
ifnet_set_mtu(ifp, TUN_MTU);
/* set header length */
ifnet_set_hdrlen(ifp, 0);
/* add the pointopoint flag */
ifnet_set_flags(ifp, IFF_POINTOPOINT, IFF_POINTOPOINT);
/* we must call bpfattach(). Otherwise we deadlock BPF while unloading. Seems to be a bug in
* the kernel, see bpfdetach() in net/bpf.c, it will return without releasing the lock if
* the interface wasn't attached. I wonder what they were smoking while writing it ;-)
*/
bpfattach(ifp, DLT_NULL, sizeof(u_int32_t));
return 0;
}
void
tun_interface::shutdown_interface()
{
dprintf("tun: shutting down network interface of %s%d\n", family_name, unit);
/* detach all protocols */
for (unsigned int i = 0; i < MAX_ATTACHED_PROTOS; i++) {
if (attached_protos[i].used) {
errno_t err = ifnet_detach_protocol(ifp, attached_protos[i].proto);
if (err)
log(LOG_WARNING, "tun: could not detach protocol %d from %s%d\n",
attached_protos[i].proto, family_name, unit);
}
}
cleanup_interface();
unregister_interface();
}
void
tun_interface::notify_bpf(mbuf_t mb, bool out)
{
auto_lock l(&bpf_lock);
if ((out && bpf_mode == BPF_MODE_OUTPUT)
|| (!out && bpf_mode == BPF_MODE_INPUT)
|| (bpf_mode == BPF_MODE_INPUT_OUTPUT)) {
/* see wether AF is prepended */
if (!prepend_af) {
mbuf_t dummy_mb;
struct ip *iphdr;
u_int32_t af;
u_int8_t ipv;
errno_t err;
/* see what we have: IPv4 or IPv6 */
iphdr = (struct ip*) mbuf_data(mb);
#ifdef _IP_VHL
ipv = IP_VHL_V(iphdr->ip_vhl);
#else
ipv = iphdr->ip_v;
#endif
if (ipv == 4)
af = AF_INET;
else if (ipv == 6)
af = AF_INET6;
else {
/* what to do? */
log(LOG_WARNING, "tun: unsupported IP version %d.\n", ipv);
return;
}
/* prepend a dummy header */
err = mbuf_get(MBUF_WAITOK, MBUF_TYPE_DATA, &dummy_mb);
if (err) {
log(LOG_WARNING, "tun: could not allocate temporary mbuf: %d\n",
err);
return;
}
mbuf_setnext(dummy_mb, mb);
mbuf_setlen(dummy_mb, sizeof(u_int32_t));
*((u_int32_t *) mbuf_data(dummy_mb)) = htonl(af);
/* call bpf */
(*bpf_callback)(ifp, dummy_mb);
/* free the dummy mbuf */
mbuf_free(dummy_mb);
} else {
/* just pass it through */
(*bpf_callback)(ifp, mb);
}
}
}
int
tun_interface::cdev_ioctl(u_long cmd, caddr_t data, int fflag, proc_t p)
{
int error;
/* if the superclass handles it, we're done */
error = tuntap_interface::cdev_ioctl(cmd, data, fflag, p);
if (!error)
return 0;
switch (cmd) {
case TUNSIFHEAD:
prepend_af = *((int *) data) ? true : false;
/* adjust header length. see tuntap_interface::cdev_write */
ifnet_set_hdrlen(ifp, prepend_af ? sizeof(u_int32_t) : 0);
return 0;
case TUNGIFHEAD:
*((int *) data) = prepend_af;
return 0;
}
return ENOTTY;
}
errno_t
tun_interface::if_demux(mbuf_t m, char *header, protocol_family_t *proto)
{
u_int32_t family;
dprintf("tun: demux\n");
/* size check */
if (mbuf_len(m) < sizeof(u_int32_t))
return ENOENT;
/* if we are prepending AF for output, we expect to also have it at the beginning of
* incoming packets */
if (!prepend_af) {
struct ip *iphdr = (struct ip*) mbuf_data(m);
u_int8_t ipv;
dprintf("tun_demux: m: %p data: %p\n", m, mbuf_data(m));
#ifdef _IPVHL
ipv = IP_VHL_V(iphdr->ip_vhl);
#else
ipv = iphdr->ip_v;
#endif
if (ipv == 4)
family = AF_INET;
else if (ipv == 6)
family = AF_INET6;
else {
/* what to do? */
log(LOG_WARNING, "tun: unsupported IP version %d\n", ipv);
return ENOENT;
}
} else {
family = ntohl(*((u_int32_t *) header));
}
/* find the protocol entry */
for (unsigned int i = 0; i < MAX_ATTACHED_PROTOS; i++) {
if (attached_protos[i].used && attached_protos[i].family == family) {
*proto = attached_protos[i].proto;
return 0;
}
}
log(LOG_WARNING, "tun: no protocol found for family %d\n", family);
return ENOENT;
}
errno_t
tun_interface::if_framer(mbuf_t *m, const struct sockaddr *dest, const char *dest_linkaddr,
const char *frame_type)
{
dprintf("tun: framer\n");
/* check whether to prepend family field */
if (prepend_af) {
errno_t err;
mbuf_t nm = *m;
/* get space */
err = mbuf_prepend(&nm, sizeof(u_int32_t), MBUF_WAITOK);
if (err) {
dprintf("tun: could not prepend data to mbuf: %d\n", err);
return err;
}
*m = nm;
*((u_int32_t *) mbuf_data(*m)) = htonl(dest->sa_family);
}
return 0;
}
errno_t
tun_interface::if_add_proto(protocol_family_t proto, const struct ifnet_demux_desc *desc,
u_int32_t ndesc)
{
errno_t err;
dprintf("tun: if_add_proto proto %d\n", proto);
for (unsigned int i = 0; i < ndesc; i++) {
/* try to add the protocol */
err = add_one_proto(proto, desc[i]);
if (err != 0) {
/* if that fails, remove everything stored so far */
if_del_proto(proto);
return err;
}
}
return 0;
}
errno_t
tun_interface::if_del_proto(protocol_family_t proto)
{
dprintf("tun: if_del_proto proto %d\n", proto);
/* delete all matching entries in attached_protos */
for (unsigned int i = 0; i < MAX_ATTACHED_PROTOS; i++) {
if (attached_protos[i].proto == proto)
attached_protos[i].used = false;
}
return 0;
}
errno_t
tun_interface::if_check_multi(const struct sockaddr *maddr)
{
dprintf("tun: check_multi family %d\n", maddr->sa_family);
/* see whether it is an IPv4 multicast address */
if (maddr->sa_family == AF_INET) {
struct sockaddr_in *imaddr = (struct sockaddr_in *) maddr;
if (IN_MULTICAST(ntohl(imaddr->sin_addr.s_addr)))
return 0;
else
return EADDRNOTAVAIL;
} else if (maddr->sa_family == AF_INET6) {
struct sockaddr_in6 *imaddr = (struct sockaddr_in6 *) maddr;
if (IN6_IS_ADDR_MULTICAST(&imaddr->sin6_addr))
return 0;
else
return EADDRNOTAVAIL;
}
return EOPNOTSUPP;
}
errno_t
tun_interface::add_one_proto(protocol_family_t proto, const struct ifnet_demux_desc &dd)
{
int free = -1;
/* see if the protocol is already registered */
for (unsigned int i = 0; i < MAX_ATTACHED_PROTOS; i++) {
if (attached_protos[i].used) {
if (dd.type == attached_protos[i].family) {
/* already registered */
if (attached_protos[i].proto == proto) {
/* matches the old entry */
return 0;
} else
return EEXIST;
}
} else if (free == -1)
free = i;
}
/* did we find a free entry? */
if (free == -1)
/* is ENOBUFS correct? */
return ENOBUFS;
dprintf("tun: adding proto family %d proto %d\n", dd.type, proto);
/* ok, save information */
attached_protos[free].used = true;
attached_protos[free].family = dd.type;
attached_protos[free].proto = proto;
return 0;
}
/* tun_manager members */
tuntap_interface *
tun_manager::create_interface()
{
return new tun_interface();
}
bool
tun_manager::shutdown()
{
if (tuntap_inited) {
if (tuntap_manager::shutdown())
tuntap_inited = false;
else
return false;
}
/* unregister INET and INET6 protocol families */
proto_unregister_plumber(PF_INET, IFNET_FAMILY_TUN);
proto_unregister_plumber(PF_INET6, IFNET_FAMILY_TUN);
return true;
}
bool
tun_manager::initialize(unsigned int count, char *family)
{
errno_t err;
tuntap_inited = false;
/* register INET and INET6 protocol families */
err = proto_register_plumber(PF_INET, IFNET_FAMILY_TUN, tun_inet_attach, tun_inet_detach);
if (err) {
log(LOG_ERR, "tun: could not register PF_INET protocol family: %d\n", err);
return false;
}
err = proto_register_plumber(PF_INET6, IFNET_FAMILY_TUN, tun_inet6_attach,
tun_inet6_detach);
if (err) {
log(LOG_ERR, "tun: could not register PF_INET6 protocol family: %d\n", err);
return false;
}
tuntap_inited = true;
/* have the superclass handle the rest */
return tuntap_manager::initialize(count, family);
}

View File

@ -1,123 +0,0 @@
/*
* ip tunnel device for MacOSX.
*/
/*
* Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de>
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials provided
* with the distribution.
* 3. The name of the author may not be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __TUN_H__
#define __TUN_H__
#include "tuntap.h"
#define TUN_FAMILY_NAME ((char *) "tun")
#define TUN_IF_COUNT 16 /* max number of tun interfaces */
#define TUN_MTU 1500
#include "tun_ioctls.h"
extern "C" {
errno_t tun_inet_attach(ifnet_t ifp, protocol_family_t proto);
void tun_inet_detach(ifnet_t ifp, protocol_family_t proto);
errno_t tun_inet6_attach(ifnet_t ifp, protocol_family_t proto);
void tun_inet6_detach(ifnet_t ifp, protocol_family_t proto);
}
/* tun_manager */
class tun_manager : public tuntap_manager {
protected:
/* create an interface */
virtual tuntap_interface *create_interface();
/* whether we need to call tuntap_manager::shutdown() */
bool tuntap_inited;
public:
/* special initalize */
virtual bool initialize(unsigned int count, char *family);
/* special shutdown */
virtual bool shutdown();
};
/* the tun network interface */
class tun_interface : public tuntap_interface {
protected:
/* maximum number of protocols that can be attached */
static const unsigned int MAX_ATTACHED_PROTOS = 8;
/* information about attached protocols for demuxing is stored here */
struct {
/* whether this entry is used */
bool used;
/* protocol family (this is equal to proto, but keep it seperated from
* Apple's KPI stuff...) */
u_int32_t family;
/* protocol passed to add_proto */
protocol_family_t proto;
} attached_protos[MAX_ATTACHED_PROTOS];
/* whether the address family field is prepended to each packet */
bool prepend_af;
/* intializes the interface */
virtual bool initialize(unsigned short major, unsigned short int unit);
/* shutdown the interface */
virtual void shutdown();
/* called when the character device is opened in order to intialize the network
* interface.
*/
virtual int initialize_interface();
/* called when the character device is closed to shutdown the network interface */
virtual void shutdown_interface();
/* override interface routines */
virtual errno_t if_demux(mbuf_t m, char *header, protocol_family_t *proto);
virtual errno_t if_framer(mbuf_t *m, const struct sockaddr *dest,
const char *dest_linkaddr, const char *frame_type);
virtual errno_t if_add_proto(protocol_family_t proto,
const struct ifnet_demux_desc *desc, u_int32_t ndesc);
virtual errno_t if_del_proto(protocol_family_t proto);
virtual errno_t if_check_multi(const struct sockaddr *maddr);
/* helper to if_add_proto */
virtual errno_t add_one_proto(protocol_family_t proto,
const struct ifnet_demux_desc &dd);
/* override notify_bpf because we might need to prepend an address header */
virtual void notify_bpf(mbuf_t mb, bool out);
/* need to override cdev_ioctl to get our special ioctls */
virtual int cdev_ioctl(u_long cmd, caddr_t data, int fflag, proc_t p);
};
#endif /* __TUN_H__ */

View File

@ -1,87 +0,0 @@
/*
* ip tunnel device for MacOSX. This is the protocol module for PF_INET.
*/
/*
* Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de>
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials provided
* with the distribution.
* 3. The name of the author may not be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/kpi_mbuf.h>
#include <sys/socket.h>
#include <sys/errno.h>
#include <sys/param.h>
#include <net/kpi_protocol.h>
#include <net/kpi_interface.h>
static errno_t
tun_inet6_input(ifnet_t ifp, protocol_family_t protocol, mbuf_t m, char *header)
{
/* input the packet */
return proto_input(PF_INET6, m);
}
static errno_t
tun_inet6_pre_output(ifnet_t ifp, protocol_family_t proto, mbuf_t *packet,
const struct sockaddr *dest, void *route, char *frame_type, char *dst_addr)
{
/* check wether the destination address is an inet address */
if (dest->sa_family != AF_INET6)
return EAFNOSUPPORT;
/* place the address family as frame type */
*((uint32_t *) frame_type) = htonl(AF_INET6);
return 0;
}
errno_t
tun_inet6_attach(ifnet_t ifp, protocol_family_t proto)
{
struct ifnet_attach_proto_param pr;
struct ifnet_demux_desc ddesc[1];
/* fill out pr and attach the protocol */
ddesc[0].type = AF_INET6;
ddesc[0].data = NULL;
ddesc[0].datalen = 0;
pr.demux_array = ddesc;
pr.demux_count = 1;
pr.input = tun_inet6_input;
pr.pre_output = tun_inet6_pre_output;
pr.event = NULL;
pr.ioctl = NULL;
pr.detached = NULL;
pr.resolve = NULL;
pr.send_arp = NULL;
return ifnet_attach_protocol(ifp, proto, &pr);
}
void
tun_inet6_detach(ifnet_t ifp, protocol_family_t proto)
{
/* just detach the protocol */
ifnet_detach_protocol(ifp, proto);
}

View File

@ -1,87 +0,0 @@
/*
* ip tunnel device for MacOSX. This is the protocol module for PF_INET.
*/
/*
* Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de>
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials provided
* with the distribution.
* 3. The name of the author may not be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/kpi_mbuf.h>
#include <sys/socket.h>
#include <sys/errno.h>
#include <sys/param.h>
#include <net/kpi_protocol.h>
#include <net/kpi_interface.h>
static errno_t
tun_inet_input(ifnet_t ifp, protocol_family_t protocol, mbuf_t m, char *header)
{
/* input the packet */
return proto_input(PF_INET, m);
}
static errno_t
tun_inet_pre_output(ifnet_t ifp, protocol_family_t proto, mbuf_t *packet,
const struct sockaddr *dest, void *route, char *frame_type, char *dst_addr)
{
/* check wether the destination address is an inet address */
if (dest->sa_family != AF_INET)
return EAFNOSUPPORT;
/* place the address family as frame type */
*((uint32_t *) frame_type) = htonl(AF_INET);
return 0;
}
errno_t
tun_inet_attach(ifnet_t ifp, protocol_family_t proto)
{
struct ifnet_attach_proto_param pr;
struct ifnet_demux_desc ddesc[1];
/* fill out pr and attach the protocol */
ddesc[0].type = AF_INET;
ddesc[0].data = NULL;
ddesc[0].datalen = 0;
pr.demux_array = ddesc;
pr.demux_count = 1;
pr.input = tun_inet_input;
pr.pre_output = tun_inet_pre_output;
pr.event = NULL;
pr.ioctl = NULL;
pr.detached = NULL;
pr.resolve = NULL;
pr.send_arp = NULL;
return ifnet_attach_protocol(ifp, proto, &pr);
}
void
tun_inet_detach(ifnet_t ifp, protocol_family_t proto)
{
/* just detach the protocol */
ifnet_detach_protocol(ifp, proto);
}

View File

@ -1,38 +0,0 @@
/*
* ip tunnel device for MacOSX.
*/
/*
* Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de>
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials provided
* with the distribution.
* 3. The name of the author may not be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __TUN_IOCTLS_H__
#define __TUN_IOCTLS_H__
/* Tun supports prepending a four byte address family field to each packet. These ioctls allow you
* to switch it on/off. Pass 1 as parameter to switch it on, pass 0 for off.
*/
#define TUNSIFHEAD _IOW('t', 96, int)
#define TUNGIFHEAD _IOR('t', 97, int)
#endif /* __TUN_IOCTLS_H__ */

View File

@ -1,21 +0,0 @@
# 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.

View File

@ -1,173 +0,0 @@
# 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.
import errno
import fcntl
import io
import os
import struct
from tuntap import ioctl
class CharDevHarness(object):
"""
Base class for the tun and tap character device harnesses. Manages a single character
interface, keeps the file descriptor and handles I/O.
"""
_MAX_CHAR_DEV = 16
_MAX_PACKET_SIZE = 4096
def __init__(self, class_name, unit = None):
"""
Initializes the harness.
Args:
class_name: Path name pattern.
unit: The character device number.
"""
self._class_name = class_name
self._unit = unit
self._dev = None
def _openCharDev(self, unit):
"""
Opens the character device.
Args:
unit: The character device number.
"""
assert not self._dev
name = self._class_name % unit
self._dev = os.open(name, os.O_RDWR)
def open(self):
"""
Opens the character device.
"""
if self._unit != None:
self._openCharDev(self._unit)
return
# Try to open character devices in turn.
for i in xrange(0, self._MAX_CHAR_DEV):
try:
self._openCharDev(i)
self._unit = i
return
except OSError as e:
if e.errno != errno.EBUSY:
raise e
# All devices busy.
raise OSError(errno.EBUSY)
def close(self):
"""
Closes the character device.
"""
assert self._dev
os.close(self._dev)
self._dev = None
def fileno(self):
assert self._dev
return self._dev
def send(self, packet):
assert self._dev
os.write(self._dev, packet)
def ioctl(self, cmd, format, arg):
"""
Performs an ioctl on the character device.
Args:
cmd: the ioctl cmd identifier.
format: argument format.
arg: argument data tuple.
Returns:
Output argument tuple.
"""
assert self._dev
return struct.unpack(format, fcntl.ioctl(self._dev, cmd, struct.pack(format, arg)))
@property
def unit(self):
"""
Returns the interface unit, if known.
"""
return self._unit
class TunCharDevHarness(CharDevHarness):
"""
Character device harness for tun devices.
"""
TUNSIFHEAD = ioctl.IOC(ioctl.OUT, 't', 96, 'i')
TUNGIFHEAD = ioctl.IOC(ioctl.IN, 't', 97, 'i')
def __init__(self, unit = None):
"""
Initializes the harness.
Args:
unit: Character device index
"""
super(TunCharDevHarness, self).__init__('/dev/tun%d', unit = unit)
@property
def prependAF(self):
"""
Gets the AF prepending flag.
Returns:
A flag indicating whether packets on the char dev are prefixed with the AF number.
"""
return self.ioctl(self.TUNGIFHEAD, 'i', (0))[0]
@prependAF.setter
def prependAF(self, prependAF):
"""
Sets the AF prepending flag.
Args:
prependAF: whether the packets on the char dev are prefixed with the AF number.
"""
self.ioctl(self.TUNSIFHEAD, 'i', (prependAF))
class TapCharDevHarness(CharDevHarness):
"""
Character device harness for tap devices.
"""
def __init__(self, unit = None):
"""
Initializes the harness.
Args:
unit: Character device index
"""
super(TapCharDevHarness, self).__init__('/dev/tap%d', unit = unit)

View File

@ -1,279 +0,0 @@
# 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.
import ctypes
import ctypes.util
import errno
import fcntl
import socket
import struct
from tuntap import ioctl
from tuntap.sockaddr import SockaddrDl, SockaddrIn, SockaddrIn6
libc = ctypes.CDLL(ctypes.util.find_library('c'))
class struct_sockaddr(ctypes.Structure):
_fields_ = [ ('sa_len', ctypes.c_uint8),
('sa_family', ctypes.c_uint8) ]
class struct_ifaddrs(ctypes.Structure):
pass
struct_ifaddrs._fields_ = [ ('ifa_next', ctypes.POINTER(struct_ifaddrs)),
('ifa_name', ctypes.c_char_p),
('ifa_flags', ctypes.c_uint),
('ifa_addr', ctypes.POINTER(struct_sockaddr)),
('ifa_netmask', ctypes.POINTER(struct_sockaddr)),
('ifa_dstaddr', ctypes.POINTER(struct_sockaddr)),
('ifa_data', ctypes.c_void_p) ]
def decodeSockaddr(sockaddr):
if not sockaddr:
return None
data = ctypes.string_at(sockaddr, max(sockaddr.contents.sa_len, 16))
if sockaddr.contents.sa_family == SockaddrDl.AF_LINK:
return SockaddrDl.decode(data)
elif sockaddr.contents.sa_family == socket.AF_INET:
return SockaddrIn.decode(data)
elif sockaddr.contents.sa_family == socket.AF_INET6:
return SockaddrIn6.decode(data)
return None
def getIfAddrs(ifname):
ifaddrs = (ctypes.POINTER(struct_ifaddrs))()
assert not libc.getifaddrs(ctypes.byref(ifaddrs))
addrs = []
try:
entry = ifaddrs
while entry:
ia = entry.contents
entry = ia.ifa_next
if ia.ifa_name != ifname:
continue
addrs.append((decodeSockaddr(ia.ifa_addr),
decodeSockaddr(ia.ifa_netmask),
decodeSockaddr(ia.ifa_dstaddr)))
return addrs
finally:
libc.freeifaddrs(ifaddrs)
def ifNameToIndex(ifname):
libc.if_nametoindex.restype = ctypes.c_uint
index = libc.if_nametoindex(ifname)
if not index:
raise OSError(ctypes.get_errno)
return index
class Address(object):
"""
Wraps address parameters for an interface.
"""
def __init__(self, af, local, remote, dst, mask):
self.af = af
self.local = local
self.remote = remote
self.dst = dst
self.mask = mask
def __makeSaProperty(name):
def get(self):
addrmap = { socket.AF_INET: SockaddrIn,
socket.AF_INET6: SockaddrIn6 }
addr = getattr(self, name)
if self.af not in addrmap:
return None
if addr == None:
return addrmap[self.af](af = 0, addr = None)
return addrmap[self.af](af = self.af, addr = addr)
return property(get)
sa_local = __makeSaProperty('local')
sa_remote = __makeSaProperty('remote')
sa_dst = __makeSaProperty('dst')
sa_mask = __makeSaProperty('mask')
class InterfaceHarness(object):
"""
Base class for network interface harnesses. Provides helpers to configure the interface.
"""
SIOCSIFFLAGS = ioctl.IOC(ioctl.OUT, 'i', 16, '16s16s')
SIOCGIFFLAGS = ioctl.IOC(ioctl.INOUT, 'i', 17, '16s16s')
SIOCAIFADDR = ioctl.IOC(ioctl.OUT, 'i', 26, '16s16s16s16s')
SIOCAIFADDR_IN6 = ioctl.IOC(ioctl.OUT, 'i', 26, '16s28s28s28sIiiII')
SIOCSIFLLADDR = ioctl.IOC(ioctl.OUT, 'i', 60, '16s16s')
IFF_UP = 0x1
IFF_BROADCAST = 0x2
IFF_DEBUG = 0x4
IFF_LOOPBACK = 0x8
IFF_POINTOPOINT = 0x10
IFF_NOTRAILERS = 0x20
IFF_RUNNING = 0x40
IFF_NOARP = 0x80
IFF_PROMISC = 0x100
IFF_ALLMULTI = 0x200
IFF_OACTIVE = 0x400
IFF_SIMPLEX = 0x800
IFF_LINK0 = 0x1000
IFF_LINK1 = 0x2000
IFF_LINK2 = 0x4000
IFF_MULTICAST = 0x8000
def __init__(self, class_name, unit):
"""
Initializes the harness.
Args:
class_name: Interface class name.
unit: The interface number.
"""
self._class_name = class_name
self._unit = unit
def _ioctl(self, af, cmd, format, arg):
"""
Performs a socket ioctl.
Args:
af: address family.
cmd: the ioctl cmd.
format: argument format description.
arg: argument data tuple.
Returns:
Output data tuple.
"""
s = socket.socket(af, socket.SOCK_DGRAM)
try:
return struct.unpack(format, fcntl.ioctl(s, cmd, struct.pack(format, *arg)))
finally:
s.close()
@property
def flags(self):
"""
Retrieves the interface flags.
Returns:
The interface flags.
"""
return self._ioctl(socket.AF_INET, InterfaceHarness.SIOCGIFFLAGS,
'16sH', (self.name, 0))[1]
@flags.setter
def flags(self, flags):
"""
Sets new interface flags.
Args:
flags: new interface flags.
"""
self._ioctl(socket.AF_INET, InterfaceHarness.SIOCSIFFLAGS,
'16sH', (self.name, flags))
@property
def name(self):
"""
Gets the interface name.
Returns:
Full interface name.
"""
return "%s%d" % (self._class_name, self._unit)
@property
def index(self):
"""
Gets the interface index.
Returns:
Interface index.
"""
return ifNameToIndex(self.name)
def getAddrs(self, af = None):
def check(addr):
if addr and addr.af == af:
return addr
else:
return None
return filter(lambda (a, n, d): a != None,
map(lambda (a, n, d): (check(a), check(n), check(d)), getIfAddrs(self.name)))
@property
def lladdr(self):
entry = self.getAddrs(SockaddrDl.AF_LINK)
if entry:
return entry[0][0]
return None
@lladdr.setter
def lladdr(self, addr):
self._ioctl(socket.AF_INET, InterfaceHarness.SIOCSIFLLADDR,
'16sBB14s', (self.name, len(addr.addr), addr.af, addr.addr))
def addIfAddr(self, local, dst, mask):
"""
Set an interface address.
Args:
local: local address.
dst: broadcast address or destination address, respectively.
mask: the netmask.
"""
self._ioctl(socket.AF_INET, InterfaceHarness.SIOCAIFADDR,
'16s16s16s16s', (self.name, local.encode(), dst.encode(), mask.encode()))
def addIfAddr6(self, local, dst, mask):
"""
Set an INET6 address for the interface.
Args:
local: local address.
dst: destination address.
mask: the netmask.
"""
# This sometimes fails on Tiger with ENOBUFS. Just retry...
ntries = 0
while True:
try:
self._ioctl(socket.AF_INET6, InterfaceHarness.SIOCAIFADDR_IN6,
'16s28s28s28sIiiII',
(self.name, local.encode(), dst.encode(), mask.encode(),
0, 0, 0, 0xffffffff, 0xffffffff))
break
except IOError as e:
if e.errno != errno.ENOBUFS or ntries > 10:
raise e
ntries += 1

View File

@ -1,31 +0,0 @@
# 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.
import struct
VOID = 0x20000000
IN = 0x40000000
OUT = 0x80000000
INOUT = IN | OUT
def IOC(inout, group, num, format):
return inout | ((struct.calcsize(format) & 0x1fff) << 16) | (ord(group) << 8) | num

View File

@ -1,531 +0,0 @@
# 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.
import socket
class BinStruct(object):
"""
Handles packing and unpacking of binary data. It is vaguely inspired by the struct module but
taylored for bit-granular fields. Also, it's probably not very fast :)
"""
@staticmethod
def str2num(data, width):
if not data:
return 0
len, rem = divmod(width, 8)
val = 0
for i in range(len):
val = (val << 8) | ord(data[i])
if rem:
val = (val << rem) | (ord(data[len]) & ((1 << rem) - 1))
return val
@staticmethod
def num2str(val, width):
result = bytearray((width + 7) / 8)
p, rem = divmod(width, 8)
if rem:
result[p] = chr(val & ((1 << rem) - 1))
val >>= rem
while p > 0:
p -= 1
result[p] = chr(val & 0xff)
val >>= 8
return str(result)
def __init__(self, format):
"""
Initializes a BinStruct object that can encode and decode the binary structure specified in
the format parameters.
Args:
format: Specifies the format of the binary data. The syntax is
(<width><type>)*
where:
width is the width of a component in number of bits
type is indicates the type of the component and may be one of:
s: binary data
n: number
"""
self._format = []
id = lambda x, width : x or 0
typemap = {
'n': (id, id),
's': (BinStruct.num2str, BinStruct.str2num),
}
pos = 0
self._width = 0
while pos < len(format):
start = pos
while str.isdigit(format[pos]):
pos += 1
width = int(format[start:pos])
self._width += width
codec = typemap[format[pos]]
self._format.insert(0, (width, codec[0], codec[1]))
pos += 1
@property
def size(self):
return (self._width + 7) / 8
def pack(self, *values):
"""
Encodes the passed values according to this BinStruct's format definition.
Args:
values: The values to encode.
Returns:
The encoded struct as a binary string.
"""
assert len(values) == len(self._format)
val = 0
pos = len(self._format)
for value in values:
pos -= 1
(width, decode, encode) = self._format[pos]
val = (val << width) | (encode(value, width) & ((1 << width) - 1))
return BinStruct.num2str(val, self._width)
def unpack(self, data):
"""
Decodes a binary string according to the format definition.
Args:
data: The binary string to decode.
Returns:
A value tuple.
"""
assert len(data) >= self.size
val = BinStruct.str2num(data, self._width)
pos = len(self._format)
result = [ None for i in range(pos) ]
for (width, decode, encode) in self._format:
pos -= 1
result[pos] = decode(val & ((1 << width) - 1), width)
val >>= width
return tuple(result)
class Packet(object):
"""
Base class for packet encoding and decoding.
"""
def __init__(self, format, names, data = None, **initializer):
"""
Initializes the packet.
Args:
format: Binary format description.
names: Names for the packet fields.
data: Optional binary packet to decode.
initializer: Optional initialization values for the packet fields.
"""
self._struct = BinStruct(format)
self._names = names
self.__dict__.update(dict.fromkeys(self._names, None))
self.payload = None
if isinstance(data, str):
self.decode(data)
elif isinstance(data, Packet):
self.update(data)
self.__dict__.update(initializer)
def __repr__(self):
return repr(dict(map(lambda x : (x, getattr(self, x)), self._names + ('payload',))))
def _payloadPos(self):
"""
Returns: The payload position in the data buffer.
"""
return self._struct.size
def _decodePayload(self, data):
"""
Decodes the payload data.
Args:
data: Payload data buffer.
Returns:
The payload object.
"""
return data
def _encodePayload(self):
"""
Encodes the payload data.
Args:
payload: Payload object.
Returns:
Encoded payload byte string.
"""
if issubclass(self.payload.__class__, Packet):
return self.payload.encode()
return str(self.payload)
def _encodeFields(self, *fields):
"""
Takes a fields tuple and returns encoded field data.
Args:
fields: Field values.
Returns:
Tuple of encoded fields.
"""
return self._struct.pack(*fields)
def decode(self, data):
"""
Decode a binary packet.
Args:
data: Binary packet data to decode.
"""
fields = self._struct.unpack(data)
assert len(fields) == len(self._names)
self.__dict__.update(dict(zip(self._names, fields)))
self.payload = self._decodePayload(data[self._payloadPos():])
def update(self, data):
"""
Update the packet from a dictionary.
Args:
data: The dictionary to update from.
"""
self.__dict__.update(map(lambda x : (x, getattr(data, x)), self._names + ('payload',)))
if isinstance(self.payload, str):
self.payload = self._decodePayload(self.payload)
def encode(self):
"""
Encodes the packet into binary format.
Returns:
The packet data.
"""
fields = map(lambda x : getattr(self, x), self._names)
return self._encodeFields(*fields) + self._encodePayload()
@property
def headerLen(self):
"""
The size of the header according to the format.
Returns:
The header length.
"""
return self._struct.size
class TunAFFrame(Packet):
def __init__(self, data = None, **initializer):
super(TunAFFrame, self).__init__('32n', ('af',), data, **initializer)
def _decodePayload(self, data):
if self.af == socket.AF_INET:
return IPv4Packet(data)
elif self.af == socket.AF_INET6:
return IPv6Packet(data)
return data
class EthernetFrame(Packet):
TYPE_IPV4 = 0x0800
TYPE_ARP = 0x0806
TYPE_IPV6 = 0x86dd
def __init__(self, data = None, **initializer):
super(EthernetFrame, self).__init__('48s48s16n', ('dst', 'src', 'type'),
data, **initializer)
def _decodePayload(self, data):
if self.type == EthernetFrame.TYPE_IPV4:
return IPv4Packet(data)
elif self.type == EthernetFrame.TYPE_ARP:
return ARPPacket(data)
elif self.type == EthernetFrame.TYPE_IPV6:
return IPv6Packet(data)
return data
class ARPPacket(Packet):
HTYPE_ETHERNET = 0x01
HLEN_ETHERNET = 6
PTYPE_IPV4 = 0x0800
PLEN_IPV4 = 4
OPER_REQUEST = 1
OPER_REPLY = 2
def __init__(self, data = None, **initializer):
super(ARPPacket, self).__init__('16n16n8n8n16n48s32s48s32s',
('htype', 'ptype', 'hlen', 'plen', 'oper',
'sha', 'spa', 'tha', 'tpa'),
data, **initializer)
class IPv4Packet(Packet):
PROTO_ICMP = 0x01
PROTO_TCP = 0x06
PROTO_UDP = 0x11
class UDPPseudoHeader(Packet):
def __init__(self, data = None, **initializer):
super(IPv4Packet.UDPPseudoHeader, self).__init__('32s32s8s8n16n',
('src', 'dst',
'padding', 'proto', 'length'),
data, **initializer)
def __init__(self, data = None, **initializer):
super(IPv4Packet, self).__init__('4n4n6n2n16n16n2n14n8n8n16n32s32s',
('version', 'hdrlen', 'dscp', 'ecn',
'len', 'id', 'flags', 'fragoffset',
'ttl', 'proto', 'checksum', 'src', 'dst'),
data, **initializer)
def _payloadPos(self):
return self.hdrlen * 4
def _decodePayload(self, data):
if self.proto == IPv4Packet.PROTO_UDP:
return UDPPacket(data)
return data
@staticmethod
def computeChecksum(data):
"""
Computes the IPv4 header checksum.
Args:
Header in binary.
Returns:
The header checksum.
"""
sum = 0
for i in range(0, len(data) - 1, 2):
sum += ord(data[i]) << 8 | ord(data[i + 1])
if len(data) % 2 == 1:
sum += ord(data[-1]) << 8 | 0
return ~((sum & 0xffff) + (sum >> 16))
def encode(self):
payload = self._encodePayload()
hdrlen = self.hdrlen or 5
payloadlen = self.len or len(payload)
fields = [self.version or 4, hdrlen, self.dscp or 0, self.ecn or 0,
payloadlen + hdrlen * 4, self.id or 0, self.flags or 0,
self.fragoffset or 0, self.ttl or 255, self.proto, self.checksum or 0,
self.src, self.dst]
# Need to compute UDP checksum here since it includes the IPv4 pseudo header.
if (self.proto == IPv4Packet.PROTO_UDP and
issubclass(self.payload.__class__, UDPPacket) and
self.payload.checksum == None):
header = IPv4Packet.UDPPseudoHeader(src = self.src, dst = self.dst,
proto = IPv4Packet.PROTO_UDP, length = payloadlen,
payload = payload)
payload = UDPPacket(data = self.payload,
checksum = IPv4Packet.computeChecksum(header.encode())).encode()
header = self._encodeFields(*tuple(fields))
if self.checksum == None:
fields[10] = IPv4Packet.computeChecksum(header)
header = self._encodeFields(*tuple(fields))
return header + payload
class IPv6Packet(Packet):
PROTO_ICMP = 1
PROTO_TCP = 6
PROTO_UDP = 17
PROTO_ICMPV6 = 58
class UDPPseudoHeader(Packet):
def __init__(self, data = None, **initializer):
super(IPv6Packet.UDPPseudoHeader, self).__init__('128s128s32n24s8n',
('src', 'dst',
'length', 'padding', 'proto'),
data, **initializer)
def __init__(self, data = None, **initializer):
super(IPv6Packet, self).__init__('4n8n20n16n8n8n128s128s',
('version', 'traffic_class', 'flow_label',
'len', 'proto', 'hop_limit',
'src', 'dst'),
data, **initializer)
def _decodePayload(self, data):
if self.proto == IPv6Packet.PROTO_UDP:
return UDPPacket(data)
elif self.proto == IPv6Packet.PROTO_ICMPV6:
return ICMPV6Packet(data)
return data
def encode(self):
payload = self._encodePayload()
fields = [self.version or 6, self.traffic_class or 0, self.flow_label or 0,
self.len or len(payload), self.proto, self.hop_limit or 255,
self.src, self.dst]
# Need to compute checksum for UDP, ICMPV6 here since it includes the IPv6 pseudo header.
checksummedProtos = { IPv6Packet.PROTO_UDP: UDPPacket,
IPv6Packet.PROTO_ICMPV6: ICMPV6Packet }
payloadClass = checksummedProtos.get(self.proto)
if (payloadClass != None and
issubclass(self.payload.__class__, payloadClass) and
self.payload.checksum == None):
header = IPv6Packet.UDPPseudoHeader(src = self.src, dst = self.dst, length = fields[3],
proto = self.proto, payload = payload)
payload = payloadClass(data = self.payload,
checksum = IPv4Packet.computeChecksum(header.encode())).encode()
return self._encodeFields(*tuple(fields)) + payload
class ICMPV6Packet(Packet):
TYPE_NEIGHBOR_SOLICITATION = 135
TYPE_NEIGHBOR_ADVERTISMENT = 136
def __init__(self, data = None, **initializer):
super(ICMPV6Packet, self).__init__('8n8n16n',
('type', 'code', 'checksum'),
data, **initializer)
def _decodePayload(self, data):
if self.type == ICMPV6Packet.TYPE_NEIGHBOR_SOLICITATION:
return ICMPV6NeighborSolicitation(data)
elif self.type == ICMPV6Packet.TYPE_NEIGHBOR_ADVERTISMENT:
return ICMPV6NeighborAdvertisement(data)
return data
class ICMPV6NeighborDiscoveryOption(Packet):
TYPE_SOURCE_LINK_LAYER_ADDRESS = 1
TYPE_TARGET_LINK_LAYER_ADDRESS = 2
def __init__(self, data = None, **initializer):
super(ICMPV6NeighborDiscoveryOption, self).__init__('8n8n',
('type', 'length'),
data, **initializer)
def encode(self):
payload = self._encodePayload()
length = self.length
if length == None:
length = (len(payload) + 2 + 7) / 8
payload += '\x00' * (length * 8 - len(payload) - 2)
fields = [self.type, length]
header = self._encodeFields(*tuple(fields))
return header + payload
@staticmethod
def decodeOptions(data):
options = []
while len(data) > 2:
type = ord(data[0])
length = ord(data[1])
if len(data) < length * 8:
break
options.append(ICMPV6NeighborDiscoveryOption(type = type, length = length,
payload = data[0:length * 8]))
data = data[length * 8:]
return options
class ICMPV6NeighborSolicitation(Packet):
def __init__(self, data = None, **initializer):
super(ICMPV6NeighborSolicitation, self).__init__('32s128s',
('reserved', 'target'),
data, **initializer)
self.target_lladdr = initializer.get('src_lladdr')
def _decodePayload(self, data):
for option in ICMPV6NeighborDiscoveryOption.decodeOptions(data):
if option.type == ICMPV6NeighborDiscoveryOption.TYPE_SOURCE_LINK_LAYER_ADDRESS:
self.src_lladdr = option.payload
return None
def _encodePayload(self):
if self.src_lladdr:
return ICMPV6NeighborDiscoveryOption(
type = ICMPV6NeighborDiscoveryOption.TYPE_SOURCE_LINK_LAYER_ADDRESS,
payload = self.src_lladdr).encode()
return ''
class ICMPV6NeighborAdvertisement(Packet):
def __init__(self, data = None, **initializer):
super(ICMPV6NeighborAdvertisement, self).__init__('1n1n1n29s128s',
('router', 'solicited', 'override',
'reserved', 'target'),
data, **initializer)
self.target_lladdr = initializer.get('target_lladdr')
def _decodePayload(self, data):
for option in ICMPV6NeighborDiscoveryOptions.decodeOptions(data):
if option.type == ICMPV6NeighborDiscoveryOption.TYPE_TARGET_LINK_LAYER_ADDRESS:
self.target_lladdr = option.payload
return None
def _encodePayload(self):
if self.target_lladdr:
return ICMPV6NeighborDiscoveryOption(
type = ICMPV6NeighborDiscoveryOption.TYPE_TARGET_LINK_LAYER_ADDRESS,
payload = self.target_lladdr).encode()
return ''
class UDPPacket(Packet):
def __init__(self, data = None, **initializer):
super(UDPPacket, self).__init__('16n16n16n16n',
('src', 'dst', 'len', 'checksum'),
data, **initializer)
def encode(self):
payload = self._encodePayload()
packetlen = self.len or (len(payload) + self.headerLen)
fields = [self.src, self.dst, packetlen, self.checksum or 0]
header = self._encodeFields(*tuple(fields))
return header + payload

View File

@ -1,244 +0,0 @@
# 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.
import functools
import socket
from tuntap.tun_tap_harness import TunHarness, TapHarness
from tuntap.packet import (
ARPPacket,
EthernetFrame,
ICMPV6Packet,
ICMPV6NeighborAdvertisement,
ICMPV6NeighborSolicitation,
IPv4Packet,
IPv6Packet,
TunAFFrame,
UDPPacket
)
from tuntap.packet_reader import PacketReader, SelectPacketSource
class PacketCodec(object):
"""
Helper for tests that wish to send and receive packets. This provides the interface to send and
receive packets at the IP/IPv6 level on both the network interface and char dev sides.
"""
def __init__(self, af, listenAddress, newHarness, newPacketSource):
self._af = af
self._listenAddress = listenAddress
self._newHarness = newHarness
self._newPacketSource = newPacketSource
def __str__(self):
af_map = { socket.AF_INET: 'IN', socket.AF_INET6: 'IN6' }
return '<%s<%s, %s>>' % (self.__class__.__name__,
af_map[self._af],
self._newPacketSource.__name__)
def _decodePacket(self, packet):
return packet
def _framePacket(self, payload):
return payload
def _frameExpectation(self, expectation):
return expectation
@property
def af(self):
return self._af
@property
def addr(self):
if self._af == socket.AF_INET:
return self._harness.addr
elif self._af == socket.AF_INET6:
return self._harness.addr6
assert False
@property
def UDPPort(self):
return self._recvSock.getsockname()[1]
def start(self):
self._harness = self._newHarness()
self._harness.start()
self._harness.up()
self._sendSock = socket.socket(self.addr.af, socket.SOCK_DGRAM)
self._recvSock = socket.socket(self.addr.af, socket.SOCK_DGRAM)
self._recvSock.bind((self._listenAddress or self.addr.local, 0))
self._reader = PacketReader(source = self._newPacketSource(self._harness.char_dev.fileno()),
skip = True,
decode = lambda packet : self._decodePacket(packet))
self._sockReader = PacketReader(source = SelectPacketSource(self._recvSock.fileno()))
self._reader.start()
self._sockReader.start()
def stop(self):
self._sockReader.stop()
self._reader.stop()
self._harness.stop()
self._sendSock.close()
self._recvSock.close()
def sendUDP(self, payload, addr):
self._sendSock.sendto(payload, addr)
def expectUDP(self, expectation):
self._sockReader.expect(expectation)
def runUDP(self):
return self._sockReader.run()
def sendPacket(self, payload):
self._harness.char_dev.send(self._framePacket(payload))
def expectPacket(self, expectation):
self._reader.expect(self._frameExpectation(expectation))
def runPacket(self):
return self._reader.run()
class TunPacketCodec(PacketCodec):
def __init__(self, af, listenAddress, newPacketSource):
super(TunPacketCodec, self).__init__(af, listenAddress, TunHarness, newPacketSource)
def _decodePacket(self, packet):
# Look at the first byte to figure out whether it's IPv4 or IPv6.
version = (ord(packet[0]) & 0xf0) >> 4
if version == 4:
return IPv4Packet(packet)
elif version == 6:
return IPv6Packet(packet)
else:
return packet
class TunAFPacketCodec(PacketCodec):
def __init__(self, af, listenAddress, newPacketSource):
super(TunAFPacketCodec, self).__init__(af, listenAddress, TunHarness, newPacketSource)
def _decodePacket(self, packet):
return TunAFFrame(packet)
def _framePacket(self, payload):
return TunAFFrame(af = self.addr.af, payload = payload).encode()
def _frameExpectation(self, expectation):
return { 'af': self.addr.af,
'payload': expectation }
def start(self):
super(TunAFPacketCodec, self).start()
self._harness.char_dev.prependAF = 1
class TapPacketCodec(PacketCodec):
TYPE_MAP = { socket.AF_INET: EthernetFrame.TYPE_IPV4,
socket.AF_INET6: EthernetFrame.TYPE_IPV6 }
ETHER_ADDR_ANY = '\xff\xff\xff\xff\xff\xff'
ETHER_ADDR_REMOTE = '\x11\x22\x33\x44\x55\x66'
def __init__(self, af, listenAddress, newPacketSource):
super(TapPacketCodec, self).__init__(af, listenAddress, TapHarness, newPacketSource)
def _decodePacket(self, packet):
return EthernetFrame(packet)
def _framePacket(self, payload):
return EthernetFrame(src = TapPacketCodec.ETHER_ADDR_REMOTE,
dst = self._harness.interface.lladdr.addr,
type = TapPacketCodec.TYPE_MAP[self.addr.af],
payload = payload).encode()
def _frameExpectation(self, expectation):
return { 'type': TapPacketCodec.TYPE_MAP[self.addr.af],
'src': self._harness.interface.lladdr.addr,
'payload': expectation }
def _sendArpReply(self, packet):
reply = EthernetFrame(dst = packet.src,
src = TapPacketCodec.ETHER_ADDR_ANY,
type = EthernetFrame.TYPE_ARP,
payload = ARPPacket(htype = ARPPacket.HTYPE_ETHERNET,
ptype = ARPPacket.PTYPE_IPV4,
hlen = ARPPacket.HLEN_ETHERNET,
plen = ARPPacket.PLEN_IPV4,
oper = ARPPacket.OPER_REPLY,
sha = TapPacketCodec.ETHER_ADDR_REMOTE,
spa = packet.payload.tpa,
tha = packet.payload.sha,
tpa = packet.payload.spa))
self._harness.char_dev.send(reply.encode())
def _sendNeighborAdvertisement(self, packet):
reply = EthernetFrame(
dst = packet.payload.payload.payload.src_lladdr,
src = TapPacketCodec.ETHER_ADDR_ANY,
type = EthernetFrame.TYPE_IPV6,
payload = IPv6Packet(
src = socket.inet_pton(self.addr.af, self.addr.remote),
dst = packet.payload.src,
proto = IPv6Packet.PROTO_ICMPV6,
payload = ICMPV6Packet(
type = ICMPV6Packet.TYPE_NEIGHBOR_ADVERTISMENT,
payload = ICMPV6NeighborAdvertisement(
solicited = 1,
override = 1,
target = socket.inet_pton(self.addr.af, self.addr.remote),
target_lladdr = TapPacketCodec.ETHER_ADDR_REMOTE))))
self._harness.char_dev.send(reply.encode())
def start(self):
super(TapPacketCodec, self).start()
# Answer ARP resolution requests for the destination address.
self._reader.expect(
expectation = { 'type': EthernetFrame.TYPE_ARP,
'payload': { 'htype': ARPPacket.HTYPE_ETHERNET,
'ptype': ARPPacket.PTYPE_IPV4,
'hlen': ARPPacket.HLEN_ETHERNET,
'plen': ARPPacket.PLEN_IPV4,
'oper': ARPPacket.OPER_REQUEST,
'tpa': socket.inet_pton(self.addr.af, self.addr.remote) }},
times = None,
action = functools.partial(TapPacketCodec._sendArpReply, self))
# Answer Neighbor Solicitation requests for IPv6.
self._reader.expect(
expectation = {
'type': EthernetFrame.TYPE_IPV6,
'payload': {
'proto': IPv6Packet.PROTO_ICMPV6,
'payload': {
'type': ICMPV6Packet.TYPE_NEIGHBOR_SOLICITATION,
'payload': {
'target': socket.inet_pton(self.addr.af, self.addr.remote) }}}},
times = None,
action = functools.partial(TapPacketCodec._sendNeighborAdvertisement, self))

View File

@ -1,270 +0,0 @@
# 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.
import errno
import os
import Queue
import select
import signal
import socket
import pickle
import threading
MAX_PACKET_SIZE = 4096
def handleEAgain(fn, *args, **kwargs):
"""
Wraps a function call in loop, restarting on EAGAIN.
"""
while True:
try:
return fn(*args, **kwargs)
except EnvironmentError as e:
if e.errno != errno.EAGAIN:
raise
except:
raise
class BlockingPacketSource(object):
"""
In order to be able to test blocking reads and not hang forever if the expected data never
arrives, we do the blocking read call in a forked subprocess that forwards the data read from
the fd over a domain socket.
"""
def __init__(self, fd):
(self._rsock, wsock) = socket.socketpair(socket.AF_UNIX, socket.SOCK_DGRAM)
child = os.fork()
if child != 0:
wsock.close()
self._child = child
return
self._rsock.close()
# This is the read loop in the forked process and it won't quit until either the process
# gets killed or there is a read error.
try:
while True:
packet = handleEAgain(os.read, fd, MAX_PACKET_SIZE)
handleEAgain(wsock.send, pickle.dumps((0, packet)))
if len(packet) == 0:
break
except KeyboardInterrupt:
pass
except EnvironmentError as e:
handleEAgain(wsock.send, pickle.dumps((e.errno, '')))
finally:
os.close(fd)
wsock.close()
os._exit(os.EX_OK)
def read(self, killpipe):
(r, w, x) = select.select([self._rsock, killpipe], [], [])
if killpipe in r:
return None
if self._rsock in r:
try:
return handleEAgain(self._rsock.recv, MAX_PACKET_SIZE)
except EnvironmentError as e:
# If there's a read error on the subprocess, it'll close the socket.
if e.errno != errno.ECONNRESET:
raise e
return None
def stop(self):
os.kill(self._child, signal.SIGINT)
os.waitpid(self._child, 0)
self._rsock.close()
class SelectPacketSource(object):
"""
Reads data from a file descriptor, waiting for input using select().
"""
def __init__(self, fd):
self._fd = fd
def read(self, killpipe):
(r, w, x) = select.select([self._fd, killpipe], [], [])
if killpipe in r:
return None
if self._fd in r:
packet = handleEAgain(os.read, self._fd, MAX_PACKET_SIZE)
return pickle.dumps((0, packet))
return None
def stop(self):
pass
class Expectation(object):
"""
Describes an expectation. Expectations are specified as dictionaries to match the packet
against. Entries may specify nested dictionaries for recursive matching and callables can be
used as predicates. Any other entry will be compared to the corresponding value in the packet.
"""
def __init__(self, expectation, times, action):
self._expectation = expectation
self._times = times
self._action = action
@property
def active(self):
return self._times == None or self.pending
@property
def pending(self):
return self._times != None and self._times > 0
def check(self, packet):
#print 'Matching %s against %s' % (packet, self._expectation)
if self.active and Expectation._matches(packet, self._expectation):
if self._times:
self._times -= 1
if callable(self._action):
self._action(packet)
return True
return False
@staticmethod
def _matches(packet, expectation):
if isinstance(expectation, dict):
for (name, entry) in expectation.iteritems():
try:
val = getattr(packet, name)
except AttributeError:
return False
if not Expectation._matches(val, entry):
return False
return True
elif callable(expectation):
return expectation(packet)
else:
return packet == expectation
class PacketReader(object):
"""
Takes care of reading packets and matching them against expectations.
"""
def __init__(self, source, decode = str, skip = False):
"""
Initializes a new reader.
Args:
source: packet source to read packets from.
decode: packet decoding function.
skip: whether non-matching packets are to be skipped.
"""
self._source = source
self._decode = decode
self._skip = skip
self._expectations = []
self._packets = Queue.Queue()
self._shutdownPipe = os.pipe()
self._stop = threading.Event()
def start(self):
self._readThread = threading.Thread(target = self)
self._readThread.start()
def stop(self):
self._stop.set()
handleEAgain(os.write, self._shutdownPipe[1], 'stop')
self._readThread.join()
self._source.stop()
os.close(self._shutdownPipe[0])
os.close(self._shutdownPipe[1])
def __call__(self):
"""
Reading service function, runs in a separate thread.
"""
try:
while True:
packet = handleEAgain(self._source.read, self._shutdownPipe[0])
if not packet:
self._packets.put((0, ''))
break
self._packets.put(pickle.loads(packet))
except EnvironmentError as e:
# The read() is racing against stop(), ignore these situations.
if e.errno == EIO and self._stop.isSet():
self._packets.put((0, ''))
self._packets.put((e.errno, ''))
def expect(self, expectation, times = 1, action = None):
"""
Adds an expectation for a packet to be received.
Args:
expectation: Dictionary describing the expected packet.
times: Number of packets expected. None for unlimited.
action: A callback to run after the packet has been received.
"""
assert times != 0
self._expectations.append(Expectation(expectation, times, action))
@property
def expectationsPending(self):
for e in self._expectations:
if e.pending:
return True
return False
def run(self, timeout = 1):
"""
Runs the packet reader, waiting for all limited expectations to be met.
Args:
timeout: Wait timeout in seconds.
"""
while self.expectationsPending:
try:
(code, payload) = self._packets.get(True, timeout)
except Queue.Empty:
# No packet received.
break
if code != 0:
# read error, re-raise.
raise OSError((code, os.strerror(code)))
if len(payload) == 0:
# EOF on read.
break
# decode the packet and match it against expectation.
matches = False
for e in self._expectations:
if e.check(self._decode(payload)):
matches = True
break
if not matches and not self._skip:
return False
return not self.expectationsPending

View File

@ -1,112 +0,0 @@
# 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.
import socket
import struct
# from net/route.h
RTM_ADD = 0x1 # Add Route
RTM_DELETE = 0x2 # Delete Route
RTM_CHANGE = 0x3 # Change Metrics or flags
RTM_GET = 0x4 # Report Metrics
RTM_LOSING = 0x5 # Kernel Suspects Partitioning
RTM_REDIRECT = 0x6 # Told to use different route
RTM_MISS = 0x7 # Lookup failed on this address
RTM_LOCK = 0x8 # fix specified metrics
RTM_OLDADD = 0x9 # caused by SIOCADDRT
RTM_OLDDEL = 0xa # caused by SIOCDELRT
RTM_RESOLVE = 0xb # req to resolve dst to LL addr
RTM_NEWADDR = 0xc # address being added to iface
RTM_DELADDR = 0xd # address being removed from iface
RTM_IFINFO = 0xe # iface going up/down etc.
RTM_NEWMADDR = 0xf # mcast group membership being added to if
RTM_DELMADDR = 0x10 # mcast group membership being deleted
RTF_UP = 0x1 # route usable
RTF_GATEWAY = 0x2 # destination is a gateway
RTF_HOST = 0x4 # host entry (net otherwise)
RTF_REJECT = 0x8 # host or net unreachable
RTF_DYNAMIC = 0x10 # created dynamically (by redirect)
RTF_MODIFIED = 0x20 # modified dynamically (by redirect)
RTF_DONE = 0x40 # message confirmed
RTF_DELCLONE = 0x80 # delete cloned route
RTF_CLONING = 0x100 # generate new routes on use
RTF_XRESOLVE = 0x200 # external daemon resolves name
RTF_LLINFO = 0x400 # generated by link layer (e.g. ARP)
RTF_STATIC = 0x800 # manually added
RTF_BLACKHOLE = 0x1000 # just discard pkts (during updates)
RTF_PROTO2 = 0x4000 # protocol specific routing flag
RTF_PROTO1 = 0x8000 # protocol specific routing flag
RTF_PRCLONING = 0x10000 # protocol requires cloning
RTF_WASCLONED = 0x20000 # route generated through cloning
RTF_PROTO3 = 0x40000 # protocol specific routing flag
RTF_LOCAL = 0x200000 # route represents a local address
RTF_BROADCAST = 0x400000 # route represents a bcast address
RTF_MULTICAST = 0x800000 # route represents a mcast address
RTF_IFSCOPE = 0x1000000 # has valid interface scope
RTF_CONDEMNED = 0x2000000 # defunct; no longer modifiable
RTA_DST = 0x1 # destination sockaddr present
RTA_GATEWAY = 0x2 # gateway sockaddr present
RTA_NETMASK = 0x4 # netmask sockaddr present
RTA_GENMASK = 0x8 # cloning mask sockaddr present
RTA_IFP = 0x10 # interface name sockaddr present
RTA_IFA = 0x20 # interface addr sockaddr present
RTA_AUTHOR = 0x40 # sockaddr for author of redirect
RTA_BRD = 0x80 # for NEWADDR, broadcast or p-p dest addr
RTM_VERSION = 5
PF_ROUTE = 17
STRUCT_RTMSG = struct.Struct('HBBHiiHiiiI3Ii10I')
def _sendRouteMsg(type, index = 0, flags = 0, addrs = {}):
def add_addr((addr_flags, payload), (addr, flag)):
if not addr:
return (addr_flags, payload)
return (addr_flags | flag, payload + addr.encode())
(addr_flags, payload) = reduce(add_addr,
[ (addrs['dst'], RTA_DST),
(addrs['gateway'], RTA_GATEWAY),
(addrs['netmask'], RTA_NETMASK) ],
(0, ''))
msglen = STRUCT_RTMSG.size + len(payload)
data = STRUCT_RTMSG.pack(msglen, RTM_VERSION, type, index, flags, addr_flags, *((0,) * 19))
sock = socket.socket(PF_ROUTE, socket.SOCK_RAW)
try:
sock.send(data + payload)
finally:
sock.close()
def addNet(dst = None, gateway = None, netmask = None, interface = None):
flags = RTF_STATIC | RTF_UP
if gateway:
flags |= RTF_GATEWAY
elif interface:
gateway = interface
_sendRouteMsg(type = RTM_ADD, flags = flags,
addrs = dict(dst = dst, gateway = gateway, netmask = netmask))

View File

@ -1,124 +0,0 @@
# 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.
import socket
import struct
class SockaddrDl(object):
AF_LINK = 18
STRUCT = struct.Struct('BBH4B')
def __init__(self, name, addr, type, index = 0, af = AF_LINK):
self.af = af
self.index = index
self.type = type
self.name = name
self.addr = addr
def __repr__(self):
return 'SockaddrDl<%d, %d, %d, %s, %s>' % (self.af, self.index, self.type,
self.name, repr(self.addr))
def __eq__(self, other):
return (self.af == other.af and self.index == other.index and self.type == other.type and
self.name == other.name and self.addr == other.addr)
def encode(self):
# It's important to make this size 12 at least to meet sizeof(struct sockaddr_dl), routing
# setup chokes if it's not.
datalen = max(len(self.name) + len(self.addr), 12)
namelen = datalen - len(self.addr)
data = SockaddrDl.STRUCT.pack(SockaddrDl.STRUCT.size + datalen,
self.af, self.index, self.type,
namelen, len(self.addr), 0)
return data + self.name + '\x00' * (namelen - len(self.name)) + self.addr
@classmethod
def decode(self, data):
fields = SockaddrDl.STRUCT.unpack_from(data)
pname = SockaddrDl.STRUCT.size
paddr = pname + fields[4]
pend = paddr + fields[5]
return SockaddrDl(af = fields[1], index = fields[2], type = fields[3],
name = data[pname:paddr], addr = data[paddr:pend])
class SockaddrIn(object):
"""
Python wrapper for struct sockaddr_in.
"""
STRUCT = struct.Struct('BBH4s8x')
def __init__(self, addr, port = 0, af = socket.AF_INET):
self.addr = addr or '0.0.0.0'
self.port = port
self.af = af
def __repr__(self):
return 'SockaddrIn<%d, %d, %s>' % (self.af, self.port, self.addr)
def __eq__(self, other):
return self.encode() == other.encode()
def encode(self):
return SockaddrIn.STRUCT.pack(16, self.af, self.port, socket.inet_aton(self.addr))
@classmethod
def decode(cls, data):
t = SockaddrIn.STRUCT.unpack(data)
return SockaddrIn(addr = socket.inet_ntoa(t[3]), port = t[2], af = t[1])
class SockaddrIn6(object):
"""
Python wrapper for struct sockaddr_in6.
"""
STRUCT = struct.Struct('BBHI16sI')
def __init__(self, addr, port = 0, af = socket.AF_INET6, flowinfo = 0, scopeid = 0):
self.addr = addr or '::0'
self.port = port
self.af = af
self.flowinfo = flowinfo
self.scopeid = scopeid
def __repr__(self):
return 'SockaddrIn6<%d, %d, %s, %d, %d>' % (self.af, self.port, self.addr,
self.flowinfo, self.scopeid)
def __eq__(self, other):
return self.encode() == other.encode()
def encode(self):
return SockaddrIn6.STRUCT.pack(28, self.af, self.port, self.flowinfo,
socket.inet_pton(socket.AF_INET6, self.addr), self.scopeid)
@classmethod
def decode(cls, data):
t = SockaddrIn6.STRUCT.unpack(data)
return SockaddrIn6(addr = socket.inet_ntop(socket.AF_INET6, t[4]), port = t[2], af = t[1],
flowinfo = t[3], scopeid = t[5])

View File

@ -1,86 +0,0 @@
# 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.
import errno
import os
from tuntap.char_dev_harness import TunCharDevHarness, TapCharDevHarness
from unittest import TestCase
class TestCharDev(TestCase):
def __init__(self, name, newHarness):
super(TestCharDev, self).__init__(name)
self._newHarness = newHarness
def setUp(self):
self.char_dev = self._newHarness()
self.char_dev.open()
def tearDown(self):
self.char_dev.close()
def test_Open(self):
pass
def test_OpenTwiceBusy(self):
second = self._newHarness(self.char_dev.unit)
try:
second.open()
second.close()
self.fail()
except OSError as e:
self.assertEqual(errno.EBUSY, e.errno)
def test_ReadFails(self):
try:
os.read(self.char_dev.fileno(), 1)
self.fail()
except OSError as e:
self.assertEqual(errno.EIO, e.errno)
def test_WriteFails(self):
try:
os.write(self.char_dev.fileno(), '')
self.fail()
except OSError as e:
self.assertEqual(errno.EIO, e.errno)
class TestTunCharDev(TestCharDev):
def __init__(self, name):
super(TestTunCharDev, self).__init__(name, TunCharDevHarness)
def test_AFPrepend(self):
self.assertFalse(self.char_dev.prependAF)
self.char_dev.prependAF = 1
self.assertTrue(self.char_dev.prependAF)
self.char_dev.prependAF = 0
self.assertFalse(self.char_dev.prependAF)
class TestTapCharDev(TestCharDev):
def __init__(self, name):
super(TestTapCharDev, self).__init__(name, TapCharDevHarness)

View File

@ -1,120 +0,0 @@
# 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.
import errno
import socket
import unittest
from tuntap.char_dev_harness import TunCharDevHarness, TapCharDevHarness
from tuntap.interface_harness import Address, InterfaceHarness
from tuntap.sockaddr import SockaddrDl, SockaddrIn, SockaddrIn6
from tuntap.tun_tap_harness import TunHarness, TapHarness
class TestInterface(unittest.TestCase):
def __init__(self, name, harness):
super(TestInterface, self).__init__(name)
self.harness = harness
def setUp(self):
self.harness.start()
def tearDown(self):
self.harness.stop()
def test_CloseWhileUp(self):
self.harness.interface.flags |= InterfaceHarness.IFF_UP
self.harness.char_dev.close()
self.harness.start()
def test_UpDown(self):
self.harness.interface.flags |= InterfaceHarness.IFF_UP
self.assertEquals(InterfaceHarness.IFF_UP,
self.harness.interface.flags & InterfaceHarness.IFF_UP)
self.harness.interface.flags &= ~InterfaceHarness.IFF_UP
self.assertEquals(0,
self.harness.interface.flags & InterfaceHarness.IFF_UP)
def test_NetmaskAFFix(self):
self.harness.interface.addIfAddr(local = self.harness.addr.sa_local,
dst = self.harness.addr.sa_dst,
mask = SockaddrIn(af = 0, addr = self.harness.addr.mask))
for addr in self.harness.interface.getAddrs(socket.AF_INET):
if addr[1] == self.harness.addr.sa_mask:
return;
self.fail()
def test_Address(self):
self.harness.interface.addIfAddr(local = self.harness.addr.sa_local,
dst = self.harness.addr.sa_dst,
mask = self.harness.addr.sa_mask)
for addr in self.harness.interface.getAddrs(socket.AF_INET):
if (addr[0] == self.harness.addr.sa_local and
addr[1] == self.harness.addr.sa_mask and
addr[2] == self.harness.addr.sa_dst):
return
self.fail()
def test_Address6(self):
def compare(expected, actual):
return (expected or SockaddrIn6(af = 0, addr = None)) == actual
self.harness.interface.addIfAddr6(local = self.harness.addr6.sa_local,
dst = self.harness.addr6.sa_dst,
mask = self.harness.addr6.sa_mask)
for addr in self.harness.interface.getAddrs(socket.AF_INET6):
if (compare(addr[0], self.harness.addr6.sa_local) and
compare(addr[1], self.harness.addr6.sa_mask) and
compare(addr[2], self.harness.addr6.sa_dst)):
return
self.fail()
class TestTunInterface(TestInterface):
def __init__(self, name):
super(TestTunInterface, self).__init__(name, TunHarness())
def test_Flags(self):
self.assertEquals(InterfaceHarness.IFF_POINTOPOINT |
InterfaceHarness.IFF_RUNNING |
InterfaceHarness.IFF_SIMPLEX |
InterfaceHarness.IFF_MULTICAST,
self.harness.interface.flags)
class TestTapInterface(TestInterface):
def __init__(self, name):
super(TestTapInterface, self).__init__(name, TapHarness())
def test_Flags(self):
self.assertEquals(InterfaceHarness.IFF_BROADCAST |
InterfaceHarness.IFF_RUNNING |
InterfaceHarness.IFF_SIMPLEX |
InterfaceHarness.IFF_MULTICAST,
self.harness.interface.flags)
def test_SetLladdr(self):
addr = SockaddrDl(name = '', addr = '\x11\x22\x33\x44\x55\x66', type = 0)
self.harness.interface.lladdr = addr
self.assertEquals(addr.addr, self.harness.interface.lladdr.addr)

View File

@ -1,218 +0,0 @@
# 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.
import functools
import socket
import struct
from unittest import TestCase
from tuntap.packet import IPv4Packet, IPv6Packet, UDPPacket
from tuntap.packet_codec import TapPacketCodec
from tuntap.packet_reader import SelectPacketSource
class TestIO(TestCase):
def __init__(self, name, af, listenAddress, codec):
super(TestIO, self).__init__(name)
self._codec = codec(af, listenAddress);
def __str__(self):
return '%s [%s]' % (super(TestIO, self).__str__(), str(self._codec))
def setUp(self):
super(TestIO, self).setUp()
self._codec.start()
def tearDown(self):
self._codec.stop()
super(TestIO, self).tearDown()
class TestIp(TestIO):
def __init__(self, name, codec):
super(TestIp, self).__init__(name, socket.AF_INET, None, codec)
def test_Send(self):
payload = 'knock, knock!'
port = 12345
self._codec.sendUDP(payload, (self._codec.addr.remote, port))
self._codec.expectPacket(
{ 'version': 4,
'src': socket.inet_pton(self._codec.af, self._codec.addr.local),
'dst': socket.inet_pton(self._codec.af, self._codec.addr.remote),
'proto': IPv4Packet.PROTO_UDP,
'payload': { 'dst': port,
'payload': payload } })
self.assertTrue(self._codec.runPacket())
def test_Recv(self):
srcport = 23456
payload = 'who\'s there?'
packet = IPv4Packet(proto = IPv4Packet.PROTO_UDP,
src = socket.inet_pton(self._codec.af, self._codec.addr.remote),
dst = socket.inet_pton(self._codec.af, self._codec.addr.local),
payload = UDPPacket(src = srcport,
dst = self._codec.UDPPort,
payload = payload))
self._codec.sendPacket(packet.encode())
self._codec.expectUDP(payload)
self.assertTrue(self._codec.runUDP())
class TestIp6(TestIO):
def __init__(self, name, codec):
super(TestIp6, self).__init__(name, socket.AF_INET6, None, codec)
def test_Send(self):
payload = 'knock, knock!'
port = 12345
self._codec.sendUDP(payload, (self._codec.addr.remote, port))
self._codec.expectPacket(
{ 'version': 6,
'src': socket.inet_pton(self._codec.af, self._codec.addr.local),
'dst': socket.inet_pton(self._codec.af, self._codec.addr.remote),
'proto': IPv6Packet.PROTO_UDP,
'payload': { 'dst': port,
'payload': payload } })
self.assertTrue(self._codec.runPacket())
def test_Recv(self):
srcport = 23456
payload = 'who\'s there?'
packet = IPv6Packet(proto = IPv6Packet.PROTO_UDP,
src = socket.inet_pton(self._codec.af, self._codec.addr.remote),
dst = socket.inet_pton(self._codec.af, self._codec.addr.local),
payload = UDPPacket(src = srcport,
dst = self._codec.UDPPort,
payload = payload))
self._codec.sendPacket(packet.encode())
self._codec.expectUDP(payload)
self.assertTrue(self._codec.runUDP())
class TestMulticast(TestIO):
MULTICAST_GROUP = '224.1.2.3'
def __init__(self, name, codec):
super(TestMulticast, self).__init__(name, socket.AF_INET, TestMulticast.MULTICAST_GROUP,
codec)
def setUp(self):
super(TestMulticast, self).setUp()
mreq = struct.pack('4s4s',
socket.inet_pton(self._codec.af, TestMulticast.MULTICAST_GROUP),
socket.inet_pton(self._codec.af, self._codec.addr.local))
self._codec._recvSock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
self._codec._sendSock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 1)
self._codec._sendSock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF,
socket.inet_pton(self._codec.af, self._codec.addr.local))
def test_Send(self):
payload = 'knock, knock!'
port = 12345
self._codec.sendUDP(payload, (TestMulticast.MULTICAST_GROUP, port))
self._codec.expectPacket(
{ 'version': 4,
'src': socket.inet_pton(self._codec.af, self._codec.addr.local),
'dst': socket.inet_pton(self._codec.af, TestMulticast.MULTICAST_GROUP),
'proto': IPv4Packet.PROTO_UDP,
'payload': { 'dst': port,
'payload': payload } })
self.assertTrue(self._codec.runPacket())
def test_Recv(self):
srcport = 23456
payload = 'who\'s there?'
packet = IPv4Packet(proto = IPv4Packet.PROTO_UDP,
src = socket.inet_pton(self._codec.af, self._codec.addr.remote),
dst = socket.inet_pton(self._codec.af, TestMulticast.MULTICAST_GROUP),
payload = UDPPacket(src = srcport,
dst = self._codec.UDPPort,
payload = payload))
self._codec.sendPacket(packet.encode())
self._codec.expectUDP(payload)
self.assertTrue(self._codec.runUDP())
class TestMulticast6(TestIO):
MULTICAST_GROUP = 'ff05::114'
def __init__(self, name, codec):
super(TestMulticast6, self).__init__(name, socket.AF_INET6, TestMulticast6.MULTICAST_GROUP,
codec)
def setUp(self):
super(TestMulticast6, self).setUp()
mreq = struct.pack('16sI',
socket.inet_pton(self._codec.af, TestMulticast6.MULTICAST_GROUP),
self._codec._harness.interface.index)
self._codec._recvSock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)
self._codec._sendSock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, 1)
self._codec._sendSock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF,
self._codec._harness.interface.index)
def test_Send(self):
payload = 'knock, knock!'
port = 12345
self._codec.sendUDP(payload, (TestMulticast6.MULTICAST_GROUP, port))
self._codec.expectPacket(
{ 'version': 6,
'dst': socket.inet_pton(self._codec.af, TestMulticast6.MULTICAST_GROUP),
'proto': IPv6Packet.PROTO_UDP,
'payload': { 'dst': port,
'payload': payload } })
self.assertTrue(self._codec.runPacket())
def test_Recv(self):
srcport = 23456
payload = 'who\'s there?'
packet = IPv6Packet(proto = IPv6Packet.PROTO_UDP,
src = socket.inet_pton(self._codec.af, self._codec.addr.remote),
dst = socket.inet_pton(self._codec.af, TestMulticast6.MULTICAST_GROUP),
payload = UDPPacket(src = srcport,
dst = self._codec.UDPPort,
payload = payload))
self._codec.sendPacket(packet.encode())
self._codec.expectUDP(payload)
self.assertTrue(self._codec.runUDP())
class TestTapLladdr(TestIp):
def __init__(self, name):
super(TestTapLladdr, self).__init__(name,
lambda af, addr: TapPacketCodec(af, addr,
SelectPacketSource))
def setUp(self):
super(TestTapLladdr, self).setUp()
# Swap out the link-level address with a different address.
lladdr = self._codec._harness.interface.lladdr
mac_addr = list(lladdr.addr)
mac_addr[5] = chr(ord(mac_addr[5]) ^ 0xff)
lladdr.addr = ''.join(mac_addr)
self._codec._harness.interface.lladdr = lladdr

View File

@ -1,96 +0,0 @@
# 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.
import errno
import socket
from tuntap.char_dev_harness import TunCharDevHarness, TapCharDevHarness
from tuntap.interface_harness import Address, InterfaceHarness
import tuntap.route
class TunTapHarness(object):
def __init__(self, name, newCharDevHarness, addr, addr6):
self._newCharDevHarness = newCharDevHarness
self.name = name
self.addr = addr
self.addr6 = addr6
def start(self):
self.char_dev = self._newCharDevHarness()
self.char_dev.open()
self.interface = InterfaceHarness(self.name, self.char_dev.unit)
def up(self):
self.interface.addIfAddr(local = self.addr.sa_local,
dst = self.addr.sa_dst,
mask = self.addr.sa_mask)
self.interface.addIfAddr6(local = self.addr6.sa_local,
dst = self.addr6.sa_dst,
mask = self.addr6.sa_mask)
# Lion automatically creates routes for IPv6 addresses, earlier versions don't.
try:
tuntap.route.addNet(dst = self.addr6.sa_remote,
netmask = self.addr6.sa_mask,
interface = self.interface.lladdr)
except IOError as e:
if e.errno != errno.EEXIST:
raise e
self.interface.flags |= InterfaceHarness.IFF_UP
def stop(self):
self.interface.flags &= ~InterfaceHarness.IFF_UP
self.char_dev.close()
class TunHarness(TunTapHarness):
def __init__(self,
addr = Address(af = socket.AF_INET,
local = '10.0.0.1',
remote = '10.0.0.2',
dst = '10.0.0.2',
mask = '255.255.255.255'),
addr6 = Address(af = socket.AF_INET6,
local = 'fd00::1',
remote = 'fd00::2',
dst = 'fd00::2',
mask = 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff')):
super(TunHarness, self).__init__('tun', TunCharDevHarness, addr, addr6)
class TapHarness(TunTapHarness):
def __init__(self,
addr = Address(af = socket.AF_INET,
local = '10.0.0.1',
remote = '10.0.0.2',
dst = '10.255.255.255',
mask = '255.0.0.0'),
addr6 = Address(af = socket.AF_INET6,
local = 'fd00::1',
remote = 'fd00::2',
dst = None,
mask = 'ffff:ffff:ffff:ffff::0')):
super(TapHarness, self).__init__('tap', TapCharDevHarness, addr, addr6)

View File

@ -1,40 +0,0 @@
# 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.
from unittest import TestCase
class TunTapTestCase(TestCase):
def __init__(self, name, harness):
super(TunTapTestCase, self).__init__(name)
self.harness = harness
def __str__(self):
return '%s [%s]' % (super(TunTapTestCase, self).__str__(),
self.harness.__class__.__name__)
def setUp(self):
self.harness.start()
self.harness.up()
def tearDown(self):
self.harness.stop()

View File

@ -1,83 +0,0 @@
# 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.
import argparse
import itertools
import re
import sys
import unittest
from tuntap.packet_codec import TunPacketCodec, TunAFPacketCodec, TapPacketCodec
from tuntap.packet_reader import BlockingPacketSource, SelectPacketSource
from tuntap.test_char_dev import TestTunCharDev, TestTapCharDev
from tuntap.test_interface import TestTunInterface, TestTapInterface
from tuntap.test_ip import TestIp, TestIp6, TestMulticast, TestMulticast6, TestTapLladdr
class FilteringTestSuite(unittest.TestSuite):
def __init__(self, filter):
super(FilteringTestSuite, self).__init__()
self._matcher = re.compile(filter or '.*')
def __iter__(self):
return itertools.ifilter(lambda test : self._matcher.search(str(test)),
super(FilteringTestSuite, self).__iter__())
def loadTestsFromTestCase(testCaseClass, *args, **kwargs):
testCaseNames = unittest.getTestCaseNames(testCaseClass, 'test_')
return unittest.TestSuite(map(lambda n : testCaseClass(n, *args, **kwargs), testCaseNames))
def main(argv):
# Parse the command line.
parser = argparse.ArgumentParser(description = 'Run tuntap unit tests.')
parser.add_argument('--tests', type = str, nargs = '?', default = None,
help = 'tests to run')
parser.add_argument('--verbosity', type = int, nargs = '?', default = 2,
help = 'verbosity level')
options = parser.parse_args(argv[1:])
# Gather tests and run them.
loader = unittest.TestLoader()
suite = FilteringTestSuite(options.tests)
suite.addTests(loadTestsFromTestCase(TestTunCharDev))
suite.addTests(loadTestsFromTestCase(TestTapCharDev))
suite.addTests(loadTestsFromTestCase(TestTunInterface))
suite.addTests(loadTestsFromTestCase(TestTapInterface))
codecs = (TunPacketCodec, TunAFPacketCodec, TapPacketCodec)
sources = (SelectPacketSource, BlockingPacketSource)
tests = (TestIp, TestIp6, TestMulticast, TestMulticast6)
for (test, codec, source) in [ (test, codec, source) for test in tests
for codec in codecs
for source in sources ]:
suite.addTests(loadTestsFromTestCase(test, lambda af, addr: codec(af, addr, source)))
suite.addTests(loadTestsFromTestCase(TestTapLladdr))
runner = unittest.TextTestRunner(stream = sys.stderr,
descriptions = True,
verbosity = options.verbosity)
runner.run(suite)
if __name__ == '__main__':
main(sys.argv)