Compare commits

...

22 Commits
0.6.0 ... 0.6.2

Author SHA1 Message Date
d92da40bff VERSION 0.6.2: Mac users should 'sudo make install-mac-tap' again.
This version fixes a recurrent gremlin in the tap driver for Mac. If you are
having this issue, you should reinstall the tap.

If you're already running ZT1, shut it down (sudo killall zerotier-one) and
then do:

sudo kextunload /Library/Application\ Support/ZeroTier/One/tap.kext

This should unload the old version. Then type 'sudo make install-mac-tap' in
the ZT1 source home directory and the new version will be installed. ZT1 will
load the module again when it next starts.

In addition to a fix, I am now distributing tap binaries and it is no longer
built in the default Makefile. This is because Apple's in the midst of some
changes that have made building it somewhat difficult.

Another note for Mavericks users:

The first time you use ZT1, you will get a popup about unsigned kernel
extensions. This will vanish once we're out of beta and have signing keys
and signed drivers.

Other changes in this version:

 * Minor improvement to Utils::getSecureRandom
 * Bug fixes and a small change to certificates of membership for private
   networks, which now appear to be working very well!
 * Stubbed out messages for auto-update, which will be done in-band via
   the ZT1 protocol. Not implemented yet.
2013-10-28 16:32:17 -04:00
5750cf6b72 New cthulhu.zerotier.com supernode IP address. 2013-10-28 16:24:55 -04:00
7015017686 Make Makefile for Mac use clang options instead of old GCC options, and fix a nasty but obvious bug I introduced into Utils::getSecureRandom. 2013-10-28 15:53:40 -04:00
12b297a712 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. 2013-10-28 15:18:06 -04:00
d290306bb3 Update binary build of tap. 2013-10-28 13:23:56 -04:00
60ac1b77c5 Fix for GitHub issue #25 2013-10-28 13:22:23 -04:00
e514fe2bff Change install-mac-tap rule. 2013-10-28 09:34:36 -04:00
148619f0ba Make tap build on OSX 10.9, though not in a way that is easy for users. Instead package binaries. 2013-10-28 09:33:32 -04:00
17778a36ba Clean up secure random, add packet definitions for update distribution facility. 2013-10-27 07:26:50 -04:00
df28cd88b8 docs: we no longer use MS loopback 2013-10-26 07:39:07 -04:00
942cc0ca21 Certificate of membership works now... had to fix multicast propagation so COM is pushed with multicast, which makes tremendous sense in retrospect. 2013-10-25 14:51:55 -04:00
010616e3ae Add some more TRACE output for certs. 2013-10-25 13:43:04 -04:00
1505e8dd50 Fix netconf init and identity transfer. 2013-10-25 13:04:58 -04:00
5901972958 More tying up of certificate of membership stuff in the client. 2013-10-24 16:57:26 -04:00
bbcd76ecd0 Netconf updates -- actually issue COM, and log attempts to access networks in NetworkActivity using the new authenticated flag in the new DB schema. 2013-10-24 16:19:53 -04:00
3de76fcab1 Make network autoconf a little more frequent to tighten up expiration times. 2013-10-21 16:11:29 -04:00
d496304bbf Put back rest of selftest. 2013-10-21 15:59:22 -04:00
719dd2870d Self-test for certificate of membership. 2013-10-21 15:47:33 -04:00
2f00ae4fd7 Version 0.6.1: minor bug fix, DBM removal
This version removes the peer DBM present in earlier releases. It is not necessary for
regular clients and has been a source of problems.

There is a long-term identity cache that can be enabled by making a directory called
"iddb.d" in the home folder and restarting ZT1. This is probably something only our
supernodes would need, since regular nodes can easily WHOIS peers they've forgotten
about.

On shutdown, the peer database is dumped to disk. It's then restored on startup.
Peers that have not been used in a while are cleaned out, so this keeps this data
set small.

A DBM may re-appear later if it's needed, but for now it was YAGNI.
2013-10-21 14:22:02 -04:00
5e71e07f59 Add persistent identity caching for use on supernodes. Activate by just making an iddb.d directory in the ZeroTier home folder. Also clean up some obsolete cruft from makefiles. 2013-10-21 14:12:00 -04:00
40e4f39181 Peers are now dumped on shutdown in a persistence cache and reloaded on startup, which is good enough for clients right now. Supernodes will get something else for long-term authoritative identity caching. 2013-10-21 11:15:47 -04:00
6e217dfcb0 Get rid of DBM, which technically is a case of YAGNI. Supernodes will need a way to save identities, but that can be a different feature. Regular clients do not really need a permanent cache (yet). When/if we do need one we can do it then. Until then it only caused problems. 2013-10-21 10:29:44 -04:00
100 changed files with 640 additions and 4645 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

@ -2,12 +2,11 @@ CC=gcc
CXX=g++
INCLUDES=
ARCH=$(shell uname -m)
DEFS=-DZT_ARCH="$(ARCH)" -DZT_OSNAME="linux" -DZT_TRACE
DEFS=
LIBS=
# Uncomment for a release optimized build
CFLAGS=-Wall -O3 -fno-unroll-loops -fstack-protector -pthread $(INCLUDES) -DNDEBUG $(DEFS)
CFLAGS=-Wall -O3 -fno-unroll-loops -fvisibility=hidden -fstack-protector -pthread $(INCLUDES) -DNDEBUG $(DEFS)
STRIP=strip --strip-all
# Uncomment for a debug build

View File

@ -1,12 +1,12 @@
CC=gcc
CXX=g++
CC=clang
CXX=clang++
INCLUDES=
DEFS=-DZT_ARCH="x86_combined" -DZT_OSNAME="mac" -DZT_TRACE
DEFS=
LIBS=-lm
# Uncomment for a release optimized universal binary build
CFLAGS=-arch i386 -arch x86_64 -Wall -O3 -ftree-vectorize -fstack-protector -pthread -mmacosx-version-min=10.6 -DNDEBUG $(INCLUDES) $(DEFS)
CFLAGS=-arch i386 -arch x86_64 -Wall -O4 -pthread -mmacosx-version-min=10.6 -DNDEBUG $(INCLUDES) $(DEFS)
STRIP=strip
# Uncomment for a debug build
@ -17,7 +17,7 @@ CXXFLAGS=$(CFLAGS) -fno-rtti
include objects.mk
all: one cli mac-tap
all: one cli
one: $(OBJS)
$(CXX) $(CXXFLAGS) -o zerotier-one main.cpp $(OBJS) $(LIBS)
@ -35,18 +35,15 @@ idtool: $(OBJS)
$(CXX) $(CXXFLAGS) -o zerotier-idtool idtool.cpp $(OBJS) $(LIBS)
$(STRIP) zerotier-idtool
mac-tap: FORCE
cd mac-tap/tuntap ; make tap.kext
install-mac-tap: FORCE
mkdir -p /Library/Application\ Support/ZeroTier/One
rm -rf /Library/Application\ Support/ZeroTier/One/tap.kext
cp -R mac-tap/tuntap/tap.kext /Library/Application\ Support/ZeroTier/One
cp -R ext/bin/tap-mac//tap.kext /Library/Application\ Support/ZeroTier/One
chown -R root:wheel /Library/Application\ Support/ZeroTier/One/tap.kext
clean:
rm -rf *.dSYM
rm -f $(OBJS) zerotier-*
cd mac-tap/tuntap ; make clean
cd tap-mac/tuntap ; make clean
FORCE:

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

@ -3,5 +3,5 @@ fair game to redistribute. It's packaged with OpenVPN and several
other things and also distributed in source code form as an example
program by Microsoft.
It's used to automatically install the Microsoft Loopback Adapter
or remove it when no longer needed.
It's called by zerotier-one.exe to automagically install and remove
instances of the tap device.

View File

@ -3,21 +3,21 @@
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>@@CFBUNDLEDEVELOPMENTREGION@@</string>
<string>English</string>
<key>CFBundleExecutable</key>
<string>@@CFBUNDLEEXECUTABLE@@</string>
<string>tap</string>
<key>CFBundleIdentifier</key>
<string>@@CFBUNDLEIDENTIFIER@@</string>
<string>com.zerotier.tap</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>@@CFBUNDLEEXECUTABLE@@</string>
<string>tap</string>
<key>CFBundlePackageType</key>
<string>@@CFBUNDLEPACKAGETYPE@@</string>
<string>KEXT</string>
<key>CFBundleShortVersionString</key>
<string>@@CFBUNDLEVERSION@@</string>
<string>20131028</string>
<key>CFBundleSignature</key>
<string>@@CFBUNDLESIGNATURE@@</string>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>OSBundleLibraries</key>

Binary file not shown.

View File

@ -1,70 +0,0 @@
kissdb
======
(Keep It) Simple Stupid Database
KISSDB is about the simplest key/value store you'll ever see, anywhere.
It's written in plain vanilla C using only the standard string and FILE
I/O functions, and should port to just about anything with a disk or
something that acts like one.
It stores keys and values of fixed length in a stupid-simple file format
based on fixed-size hash tables. If a hash collision occurrs, a new "page"
of hash table is appended to the database. The format is append-only.
There is no delete. Puts that replace an existing value, however, will not
grow the file as they will overwrite the existing entry.
Hash table size is a space/speed trade-off parameter. Larger hash tables
will reduce collisions and speed things up a bit, at the expense of memory
and disk space. A good size is usually about 1/2 the average number of
entries you expect.
Features:
* Tiny, compiles to ~4k on an x86_64 Linux system
* Small memory footprint (only caches hash tables)
* Very space-efficient (on disk) if small hash tables are used
* Makes a decent effort to be robust on power loss
* Pretty respectably fast, especially given its simplicity
* 64-bit, file size limit is 2^64 bytes
* Ports to anything with a C compiler and stdlib/stdio
* Public domain
Limitations:
* Fixed-size keys and values, must recreate and copy to change any init size parameter
* Add/update only, no delete
* Iteration is supported but key order is undefined
* No search for subsets of keys/values
* No indexes
* No transactions
* No special recovery features if a database gets corrupted
* No built-in thread-safety (guard it with a mutex in MT code)
* No built-in caching of data (only hash tables are cached for lookup speed)
* No endian-awareness (currently), so big-endian DBs won't read on little-endian machines
Alternative key/value stores and embedded databases:
* [MDB](http://symas.com/mdb/) uses mmap() and is very fast (not quite as tiny/simple/portable)
* [CDB](http://cr.yp.to/cdb.html) is also minimal and fast, probably the closest thing to this (but has a 4gb size limit)
* [Kyoto Cabinet](http://fallabs.com/kyotocabinet/) is very fast, full-featured, and modern (license required for commercial use)
* [SQLite](http://www.sqlite.org/) gives you a complete embedded SQL server (public domain, very mature, much larger)
* Others include GDBM, NDBM, Berkeley DB, etc. Use your Googles. :)
KISSDB is good if you want space-efficient relatively fast write-once/read-many storage
of keys mapped to values. It's not a good choice if you need searches, indexes, delete,
structured storage, or widely varying key/value sizes. It's also probably not a good
choice if you need a long-lived database for critical data, since it lacks recovery
features and is brittle if its internals are modified. It would be better for a cache
of data that can be restored or "re-learned," such as keys, Bitcoin transactions, nodes
on a peer-to-peer network, log analysis results, rendered web pages, session cookies,
auth tokens, etc.
KISSDB is in the public domain. One reason it was written was the
poverty of simple key/value databases with wide open licensing. Even old
ones like GDBM have GPL, not LGPL, licenses.
See comments in kissdb.h for documentation. Makefile can be used to build
a test program on systems with gcc.
Author: Adam Ierymenko / ZeroTier Networks LLC

View File

@ -1,60 +0,0 @@
-----
KISSDB file format (version 2)
Author: Adam Ierymenko <adam.ierymenko@zerotier.com>
-----
In keeping with the goal of minimalism the file format is very simple, the
sort of thing that would be given as an example in an introductory course in
data structures. It's a basic hash table that adds additional pages of hash
table entries on collision.
It consists of a 28 byte header followed by a series of hash tables and data.
All integer values are stored in the native word order of the target
architecture (in the future the code might be fixed to make everything
little-endian if anyone cares about that).
The header consists of the following fields:
[0-3] magic numbers: (ASCII) 'K', 'd', 'B', KISSDB_VERSION (currently 2)
[4-11] 64-bit hash table size in entries
[12-19] 64-bit key size in bytes
[20-27] 64-bit value size in bytes
Hash tables are arrays of [hash table size + 1] 64-bit integers. The extra
entry, if nonzero, is the offset in the file of the next hash table, forming
a linked list of hash tables across the file.
Immediately following the header, the first hash table will be written when
the first key/value is added. The algorithm for adding new entries is as
follows:
(1) The key is hashed using a 64-bit variant of the DJB2 hash function, and
this is taken modulo hash table size to get a bucket number.
(2) Hash tables are checked in order, starting with the first hash table,
until a zero (empty) bucket is found. If one is found, skip to step (4).
(3) If no empty buckets are found in any hash table, a new table is appended
to the file and the final pointer in the previous hash table is set to
its offset. (In the code the update of the next hash table pointer in
the previous hash table happens last, after the whole write is complete,
to avoid corruption on power loss.)
(4) The key and value are appended, in order with no additional meta-data,
to the database file. Before appending the offset in the file stream
where they will be stored is saved. After appending, this offset is
written to the empty hash table bucket we chose in steps 2/3. Hash table
updates happen last to avoid corruption if the write does not complete.
Lookup of a key/value pair occurs as follows:
(1) The key is hashed and taken modulo hash table size to get a bucket
number.
(2) If this bucket's entry in the hash table is nonzero, the key at the
offset specified by this bucket is compared to the key being looked up.
If they are equal, the value is read and returned.
(3) If the keys are not equal, the next hash table is checked and step (2)
is repeated. If an empty bucket is encountered or if we run out of hash
tables, the key was not found.
To update an existing value, its location is looked up and the value portion
of the entry is rewritten.

View File

@ -1,447 +0,0 @@
/* (Keep It) Simple Stupid Database
*
* Written by Adam Ierymenko <adam.ierymenko@zerotier.com>
* KISSDB is in the public domain and is distributed with NO WARRANTY. */
/* Compile with KISSDB_TEST to build as a test program. */
/* Note: big-endian systems will need changes to implement byte swapping
* on hash table file I/O. Or you could just use it as-is if you don't care
* that your database files will be unreadable on little-endian systems. */
#define _FILE_OFFSET_BITS 64
#include "kissdb.h"
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#ifdef _WIN32
#define fseeko _fseeki64
#define ftello _ftelli64
#endif
#define KISSDB_HEADER_SIZE ((sizeof(uint64_t) * 3) + 4)
/* djb2 hash function */
static uint64_t KISSDB_hash(const void *b,unsigned long len)
{
unsigned long i;
uint64_t hash = 5381;
for(i=0;i<len;++i)
hash = ((hash << 5) + hash) + (uint64_t)(((const uint8_t *)b)[i]);
return hash;
}
int KISSDB_open(
KISSDB *db,
const char *path,
int mode,
unsigned long hash_table_size,
unsigned long key_size,
unsigned long value_size)
{
uint64_t tmp;
uint8_t tmp2[4];
uint64_t *httmp;
uint64_t *hash_tables_rea;
#ifdef _WIN32
db->f = (FILE *)0;
fopen_s(&db->f,path,((mode == KISSDB_OPEN_MODE_RWREPLACE) ? "w+b" : (((mode == KISSDB_OPEN_MODE_RDWR)||(mode == KISSDB_OPEN_MODE_RWCREAT)) ? "r+b" : "rb")));
#else
db->f = fopen(path,((mode == KISSDB_OPEN_MODE_RWREPLACE) ? "w+b" : (((mode == KISSDB_OPEN_MODE_RDWR)||(mode == KISSDB_OPEN_MODE_RWCREAT)) ? "r+b" : "rb")));
#endif
if (!db->f) {
if (mode == KISSDB_OPEN_MODE_RWCREAT) {
#ifdef _WIN32
db->f = (FILE *)0;
fopen_s(&db->f,path,"w+b");
#else
db->f = fopen(path,"w+b");
#endif
}
if (!db->f)
return KISSDB_ERROR_IO;
}
if (fseeko(db->f,0,SEEK_END)) {
fclose(db->f);
return KISSDB_ERROR_IO;
}
if (ftello(db->f) < KISSDB_HEADER_SIZE) {
/* write header if not already present */
if ((hash_table_size)&&(key_size)&&(value_size)) {
if (fseeko(db->f,0,SEEK_SET)) { fclose(db->f); return KISSDB_ERROR_IO; }
tmp2[0] = 'K'; tmp2[1] = 'd'; tmp2[2] = 'B'; tmp2[3] = KISSDB_VERSION;
if (fwrite(tmp2,4,1,db->f) != 1) { fclose(db->f); return KISSDB_ERROR_IO; }
tmp = hash_table_size;
if (fwrite(&tmp,sizeof(uint64_t),1,db->f) != 1) { fclose(db->f); return KISSDB_ERROR_IO; }
tmp = key_size;
if (fwrite(&tmp,sizeof(uint64_t),1,db->f) != 1) { fclose(db->f); return KISSDB_ERROR_IO; }
tmp = value_size;
if (fwrite(&tmp,sizeof(uint64_t),1,db->f) != 1) { fclose(db->f); return KISSDB_ERROR_IO; }
fflush(db->f);
} else {
fclose(db->f);
return KISSDB_ERROR_INVALID_PARAMETERS;
}
} else {
if (fseeko(db->f,0,SEEK_SET)) { fclose(db->f); return KISSDB_ERROR_IO; }
if (fread(tmp2,4,1,db->f) != 1) { fclose(db->f); return KISSDB_ERROR_IO; }
if ((tmp2[0] != 'K')||(tmp2[1] != 'd')||(tmp2[2] != 'B')||(tmp2[3] != KISSDB_VERSION)) {
fclose(db->f);
return KISSDB_ERROR_CORRUPT_DBFILE;
}
if (fread(&tmp,sizeof(uint64_t),1,db->f) != 1) { fclose(db->f); return KISSDB_ERROR_IO; }
if (!tmp) {
fclose(db->f);
return KISSDB_ERROR_CORRUPT_DBFILE;
}
hash_table_size = (unsigned long)tmp;
if (fread(&tmp,sizeof(uint64_t),1,db->f) != 1) { fclose(db->f); return KISSDB_ERROR_IO; }
if (!tmp) {
fclose(db->f);
return KISSDB_ERROR_CORRUPT_DBFILE;
}
key_size = (unsigned long)tmp;
if (fread(&tmp,sizeof(uint64_t),1,db->f) != 1) { fclose(db->f); return KISSDB_ERROR_IO; }
if (!tmp) {
fclose(db->f);
return KISSDB_ERROR_CORRUPT_DBFILE;
}
value_size = (unsigned long)tmp;
}
db->hash_table_size = hash_table_size;
db->key_size = key_size;
db->value_size = value_size;
db->hash_table_size_bytes = sizeof(uint64_t) * (hash_table_size + 1); /* [hash_table_size] == next table */
httmp = malloc(db->hash_table_size_bytes);
if (!httmp) {
fclose(db->f);
return KISSDB_ERROR_MALLOC;
}
db->num_hash_tables = 0;
db->hash_tables = (uint64_t *)0;
while (fread(httmp,db->hash_table_size_bytes,1,db->f) == 1) {
hash_tables_rea = realloc(db->hash_tables,db->hash_table_size_bytes * (db->num_hash_tables + 1));
if (!hash_tables_rea) {
KISSDB_close(db);
free(httmp);
return KISSDB_ERROR_MALLOC;
}
db->hash_tables = hash_tables_rea;
memcpy(((uint8_t *)db->hash_tables) + (db->hash_table_size_bytes * db->num_hash_tables),httmp,db->hash_table_size_bytes);
++db->num_hash_tables;
if (httmp[db->hash_table_size]) {
if (fseeko(db->f,httmp[db->hash_table_size],SEEK_SET)) {
KISSDB_close(db);
free(httmp);
return KISSDB_ERROR_IO;
}
} else break;
}
free(httmp);
return 0;
}
void KISSDB_close(KISSDB *db)
{
if (db->hash_tables)
free(db->hash_tables);
if (db->f)
fclose(db->f);
memset(db,0,sizeof(KISSDB));
}
int KISSDB_get(KISSDB *db,const void *key,void *vbuf)
{
uint8_t tmp[4096];
const uint8_t *kptr;
unsigned long klen,i;
uint64_t hash = KISSDB_hash(key,db->key_size) % (uint64_t)db->hash_table_size;
uint64_t offset;
uint64_t *cur_hash_table;
long n;
cur_hash_table = db->hash_tables;
for(i=0;i<db->num_hash_tables;++i) {
offset = cur_hash_table[hash];
if (offset) {
if (fseeko(db->f,offset,SEEK_SET))
return KISSDB_ERROR_IO;
kptr = (const uint8_t *)key;
klen = db->key_size;
while (klen) {
n = (long)fread(tmp,1,(klen > sizeof(tmp)) ? sizeof(tmp) : klen,db->f);
if (n > 0) {
if (memcmp(kptr,tmp,n))
goto get_no_match_next_hash_table;
kptr += n;
klen -= (unsigned long)n;
} else return 1; /* not found */
}
if (fread(vbuf,db->value_size,1,db->f) == 1)
return 0; /* success */
else return KISSDB_ERROR_IO;
} else return 1; /* not found */
get_no_match_next_hash_table:
cur_hash_table += db->hash_table_size + 1;
}
return 1; /* not found */
}
int KISSDB_put(KISSDB *db,const void *key,const void *value)
{
uint8_t tmp[4096];
const uint8_t *kptr;
unsigned long klen,i;
uint64_t hash = KISSDB_hash(key,db->key_size) % (uint64_t)db->hash_table_size;
uint64_t offset;
uint64_t htoffset,lasthtoffset;
uint64_t endoffset;
uint64_t *cur_hash_table;
uint64_t *hash_tables_rea;
long n;
lasthtoffset = htoffset = KISSDB_HEADER_SIZE;
cur_hash_table = db->hash_tables;
for(i=0;i<db->num_hash_tables;++i) {
offset = cur_hash_table[hash];
if (offset) {
/* rewrite if already exists */
if (fseeko(db->f,offset,SEEK_SET))
return KISSDB_ERROR_IO;
kptr = (const uint8_t *)key;
klen = db->key_size;
while (klen) {
n = (long)fread(tmp,1,(klen > sizeof(tmp)) ? sizeof(tmp) : klen,db->f);
if (n > 0) {
if (memcmp(kptr,tmp,n))
goto put_no_match_next_hash_table;
kptr += n;
klen -= (unsigned long)n;
}
}
if (fwrite(value,db->value_size,1,db->f) == 1) {
fflush(db->f);
return 0; /* success */
} else return KISSDB_ERROR_IO;
} else {
/* add if an empty hash table slot is discovered */
if (fseeko(db->f,0,SEEK_END))
return KISSDB_ERROR_IO;
endoffset = ftello(db->f);
if (fwrite(key,db->key_size,1,db->f) != 1)
return KISSDB_ERROR_IO;
if (fwrite(value,db->value_size,1,db->f) != 1)
return KISSDB_ERROR_IO;
if (fseeko(db->f,htoffset + (sizeof(uint64_t) * hash),SEEK_SET))
return KISSDB_ERROR_IO;
if (fwrite(&endoffset,sizeof(uint64_t),1,db->f) != 1)
return KISSDB_ERROR_IO;
cur_hash_table[hash] = endoffset;
fflush(db->f);
return 0; /* success */
}
put_no_match_next_hash_table:
lasthtoffset = htoffset;
htoffset = cur_hash_table[db->hash_table_size];
cur_hash_table += (db->hash_table_size + 1);
}
/* if no existing slots, add a new page of hash table entries */
if (fseeko(db->f,0,SEEK_END))
return KISSDB_ERROR_IO;
endoffset = ftello(db->f);
hash_tables_rea = realloc(db->hash_tables,db->hash_table_size_bytes * (db->num_hash_tables + 1));
if (!hash_tables_rea)
return KISSDB_ERROR_MALLOC;
db->hash_tables = hash_tables_rea;
cur_hash_table = &(db->hash_tables[(db->hash_table_size + 1) * db->num_hash_tables]);
memset(cur_hash_table,0,db->hash_table_size_bytes);
cur_hash_table[hash] = endoffset + db->hash_table_size_bytes; /* where new entry will go */
if (fwrite(cur_hash_table,db->hash_table_size_bytes,1,db->f) != 1)
return KISSDB_ERROR_IO;
if (fwrite(key,db->key_size,1,db->f) != 1)
return KISSDB_ERROR_IO;
if (fwrite(value,db->value_size,1,db->f) != 1)
return KISSDB_ERROR_IO;
if (db->num_hash_tables) {
if (fseeko(db->f,lasthtoffset + (sizeof(uint64_t) * db->hash_table_size),SEEK_SET))
return KISSDB_ERROR_IO;
if (fwrite(&endoffset,sizeof(uint64_t),1,db->f) != 1)
return KISSDB_ERROR_IO;
db->hash_tables[((db->hash_table_size + 1) * (db->num_hash_tables - 1)) + db->hash_table_size] = endoffset;
}
++db->num_hash_tables;
fflush(db->f);
return 0; /* success */
}
void KISSDB_Iterator_init(KISSDB *db,KISSDB_Iterator *dbi)
{
dbi->db = db;
dbi->h_no = 0;
dbi->h_idx = 0;
}
int KISSDB_Iterator_next(KISSDB_Iterator *dbi,void *kbuf,void *vbuf)
{
uint64_t offset;
if ((dbi->h_no < dbi->db->num_hash_tables)&&(dbi->h_idx < dbi->db->hash_table_size)) {
while (!(offset = dbi->db->hash_tables[((dbi->db->hash_table_size + 1) * dbi->h_no) + dbi->h_idx])) {
if (++dbi->h_idx >= dbi->db->hash_table_size) {
dbi->h_idx = 0;
if (++dbi->h_no >= dbi->db->num_hash_tables)
return 0;
}
}
if (fseeko(dbi->db->f,offset,SEEK_SET))
return KISSDB_ERROR_IO;
if (fread(kbuf,dbi->db->key_size,1,dbi->db->f) != 1)
return KISSDB_ERROR_IO;
if (fread(vbuf,dbi->db->value_size,1,dbi->db->f) != 1)
return KISSDB_ERROR_IO;
if (++dbi->h_idx >= dbi->db->hash_table_size) {
dbi->h_idx = 0;
++dbi->h_no;
}
return 1;
}
return 0;
}
#ifdef KISSDB_TEST
#include <inttypes.h>
int main(int argc,char **argv)
{
uint64_t i,j;
uint64_t v[8];
KISSDB db;
KISSDB_Iterator dbi;
char got_all_values[10000];
int q;
printf("Opening new empty database test.db...\n");
if (KISSDB_open(&db,"test.db",KISSDB_OPEN_MODE_RWREPLACE,1024,8,sizeof(v))) {
printf("KISSDB_open failed\n");
return 1;
}
printf("Adding and then re-getting 10000 64-byte values...\n");
for(i=0;i<10000;++i) {
for(j=0;j<8;++j)
v[j] = i;
if (KISSDB_put(&db,&i,v)) {
printf("KISSDB_put failed (%"PRIu64")\n",i);
return 1;
}
memset(v,0,sizeof(v));
if ((q = KISSDB_get(&db,&i,v))) {
printf("KISSDB_get (1) failed (%"PRIu64") (%d)\n",i,q);
return 1;
}
for(j=0;j<8;++j) {
if (v[j] != i) {
printf("KISSDB_get (1) failed, bad data (%"PRIu64")\n",i);
return 1;
}
}
}
printf("Getting 10000 64-byte values...\n");
for(i=0;i<10000;++i) {
if ((q = KISSDB_get(&db,&i,v))) {
printf("KISSDB_get (2) failed (%"PRIu64") (%d)\n",i,q);
return 1;
}
for(j=0;j<8;++j) {
if (v[j] != i) {
printf("KISSDB_get (2) failed, bad data (%"PRIu64")\n",i);
return 1;
}
}
}
printf("Closing and re-opening database in read-only mode...\n");
KISSDB_close(&db);
if (KISSDB_open(&db,"test.db",KISSDB_OPEN_MODE_RDONLY,1024,8,sizeof(v))) {
printf("KISSDB_open failed\n");
return 1;
}
printf("Getting 10000 64-byte values...\n");
for(i=0;i<10000;++i) {
if ((q = KISSDB_get(&db,&i,v))) {
printf("KISSDB_get (3) failed (%"PRIu64") (%d)\n",i,q);
return 1;
}
for(j=0;j<8;++j) {
if (v[j] != i) {
printf("KISSDB_get (3) failed, bad data (%"PRIu64")\n",i);
return 1;
}
}
}
printf("Iterator test...\n");
KISSDB_Iterator_init(&db,&dbi);
i = 0xdeadbeef;
memset(got_all_values,0,sizeof(got_all_values));
while (KISSDB_Iterator_next(&dbi,&i,&v) > 0) {
if (i < 10000)
got_all_values[i] = 1;
else {
printf("KISSDB_Iterator_next failed, bad data (%"PRIu64")\n",i);
return 1;
}
}
for(i=0;i<10000;++i) {
if (!got_all_values[i]) {
printf("KISSDB_Iterator failed, missing value index %"PRIu64"\n",i);
return 1;
}
}
KISSDB_close(&db);
printf("All tests OK!\n");
return 0;
}
#endif

View File

@ -1,171 +0,0 @@
/* (Keep It) Simple Stupid Database
*
* Written by Adam Ierymenko <adam.ierymenko@zerotier.com>
* KISSDB is in the public domain and is distributed with NO WARRANTY. */
#ifndef ___KISSDB_H
#define ___KISSDB_H
#include <stdio.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Version: 2
*
* This is the file format identifier, and changes any time the file
* format changes. The code version will be this dot something, and can
* be seen in tags in the git repository.
*/
#define KISSDB_VERSION 2
/**
* KISSDB database state
*
* These fields can be read by a user, e.g. to look up key_size and
* value_size, but should never be changed.
*/
typedef struct {
unsigned long hash_table_size;
unsigned long key_size;
unsigned long value_size;
unsigned long hash_table_size_bytes;
unsigned long num_hash_tables;
uint64_t *hash_tables;
FILE *f;
} KISSDB;
/**
* I/O error or file not found
*/
#define KISSDB_ERROR_IO -1
/**
* Out of memory
*/
#define KISSDB_ERROR_MALLOC -2
/**
* Invalid paramters (e.g. missing _size paramters on init to create database)
*/
#define KISSDB_ERROR_INVALID_PARAMETERS -3
/**
* Database file appears corrupt
*/
#define KISSDB_ERROR_CORRUPT_DBFILE -4
/**
* Open mode: read only
*/
#define KISSDB_OPEN_MODE_RDONLY 1
/**
* Open mode: read/write
*/
#define KISSDB_OPEN_MODE_RDWR 2
/**
* Open mode: read/write, create if doesn't exist
*/
#define KISSDB_OPEN_MODE_RWCREAT 3
/**
* Open mode: truncate database, open for reading and writing
*/
#define KISSDB_OPEN_MODE_RWREPLACE 4
/**
* Open database
*
* The three _size parameters must be specified if the database could
* be created or re-created. Otherwise an error will occur. If the
* database already exists, these parameters are ignored and are read
* from the database. You can check the struture afterwords to see what
* they were.
*
* @param db Database struct
* @param path Path to file
* @param mode One of the KISSDB_OPEN_MODE constants
* @param hash_table_size Size of hash table in 64-bit entries (must be >0)
* @param key_size Size of keys in bytes
* @param value_size Size of values in bytes
* @return 0 on success, nonzero on error
*/
extern int KISSDB_open(
KISSDB *db,
const char *path,
int mode,
unsigned long hash_table_size,
unsigned long key_size,
unsigned long value_size);
/**
* Close database
*
* @param db Database struct
*/
extern void KISSDB_close(KISSDB *db);
/**
* Get an entry
*
* @param db Database struct
* @param key Key (key_size bytes)
* @param vbuf Value buffer (value_size bytes capacity)
* @return -1 on I/O error, 0 on success, 1 on not found
*/
extern int KISSDB_get(KISSDB *db,const void *key,void *vbuf);
/**
* Put an entry (overwriting it if it already exists)
*
* In the already-exists case the size of the database file does not
* change.
*
* @param db Database struct
* @param key Key (key_size bytes)
* @param value Value (value_size bytes)
* @return -1 on I/O error, 0 on success
*/
extern int KISSDB_put(KISSDB *db,const void *key,const void *value);
/**
* Cursor used for iterating over all entries in database
*/
typedef struct {
KISSDB *db;
unsigned long h_no;
unsigned long h_idx;
} KISSDB_Iterator;
/**
* Initialize an iterator
*
* @param db Database struct
* @param i Iterator to initialize
*/
extern void KISSDB_Iterator_init(KISSDB *db,KISSDB_Iterator *dbi);
/**
* Get the next entry
*
* The order of entries returned by iterator is undefined. It depends on
* how keys hash.
*
* @param Database iterator
* @param kbuf Buffer to fill with next key (key_size bytes)
* @param vbuf Buffer to fill with next value (value_size bytes)
* @return 0 if there are no more entries, negative on error, positive if an kbuf/vbuf have been filled
*/
extern int KISSDB_Iterator_next(KISSDB_Iterator *dbi,void *kbuf,void *vbuf);
#ifdef __cplusplus
}
#endif
#endif

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 +0,0 @@
# Lets have a version, at last!
TUNTAP_VERSION = 20111101
# BASE install directory
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
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
}<7D>}}}}

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

@ -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)

View File

@ -1,7 +1,7 @@
all:
gcc -O6 -c ../ext/lz4/lz4hc.c ../ext/lz4/lz4.c
g++ -DZT_OSNAME="linux" -DZT_ARCH="x86_64" -I/usr/include/mysql -I../ext/bin/libcrypto/include -O -pthread -o netconf.service netconf.cpp ../node/Utils.cpp ../node/Identity.cpp ../node/Salsa20.cpp ../node/C25519.cpp ../node/SHA512.cpp lz4.o lz4hc.o -lmysqlpp
g++ -DZT_OSNAME="linux" -DZT_ARCH="x86_64" -I/usr/include/mysql -I../ext/bin/libcrypto/include -O -pthread -o netconf-test netconf-test.cpp ../node/Utils.cpp ../node/Identity.cpp ../node/Salsa20.cpp ../node/Logger.cpp ../node/Service.cpp ../node/C25519.cpp ../node/SHA512.cpp lz4.o lz4hc.o
g++ -DZT_OSNAME="linux" -DZT_ARCH="x86_64" -I/usr/include/mysql -I../ext/bin/libcrypto/include -O -pthread -o netconf.service netconf.cpp ../node/Utils.cpp ../node/Identity.cpp ../node/Salsa20.cpp ../node/C25519.cpp ../node/SHA512.cpp ../node/CertificateOfMembership.cpp lz4.o lz4hc.o -lmysqlpp
# g++ -DZT_OSNAME="linux" -DZT_ARCH="x86_64" -I/usr/include/mysql -I../ext/bin/libcrypto/include -O -pthread -o netconf-test netconf-test.cpp ../node/Utils.cpp ../node/Identity.cpp ../node/Salsa20.cpp ../node/Logger.cpp ../node/Service.cpp ../node/C25519.cpp ../node/SHA512.cpp lz4.o lz4hc.o
clean:
rm -f *.o netconf.service netconf-test

View File

@ -69,11 +69,13 @@
#include <mysql++/mysql++.h>
#include "../node/Constants.hpp"
#include "../node/Dictionary.hpp"
#include "../node/Identity.hpp"
#include "../node/Utils.hpp"
#include "../node/Mutex.hpp"
#include "../node/NetworkConfig.hpp"
#include "../node/CertificateOfMembership.hpp"
using namespace ZeroTier;
using namespace mysqlpp;
@ -116,6 +118,7 @@ int main(int argc,char **argv)
}
char buf[131072],buf2[131072];
Identity signingIdentity;
std::string dictBuf;
try {
@ -131,6 +134,19 @@ int main(int argc,char **argv)
return -1;
}
// Send ready message to tell parent that the service is up, and to
// solicit netconf-init.
{
Dictionary response;
response["type"] = "ready";
std::string respm = response.toString();
uint32_t respml = (uint32_t)htonl((uint32_t)respm.length());
stdoutWriteLock.lock();
write(STDOUT_FILENO,&respml,4);
write(STDOUT_FILENO,respm.data(),respm.length());
stdoutWriteLock.unlock();
}
for(;;) {
for(int l=0;l<4;) {
int n = (int)read(STDIN_FILENO,buf + l,4 - l);
@ -195,7 +211,21 @@ int main(int argc,char **argv)
try {
const std::string &reqType = request.get("type");
if (reqType == "netconf-request") { // NETWORK_CONFIG_REQUEST packet
if (reqType == "netconf-init") { // initialization to set things like netconf's identity
Identity netconfId(request.get("netconfId"));
if ((netconfId)&&(netconfId.hasPrivate())) {
signingIdentity = netconfId;
fprintf(stderr,"got netconf signing identity: %s\n",signingIdentity.toString(false).c_str());
} else {
fprintf(stderr,"netconfId invalid or lacks private key\n");
return -1;
}
} else if (reqType == "netconf-request") { // NETWORK_CONFIG_REQUEST packet
if (!signingIdentity) {
fprintf(stderr,"no signing identity; missing netconf-init?\n");
return -1;
}
// Deserialize querying peer identity and network ID
Identity peerIdentity(request.get("peerId"));
uint64_t nwid = strtoull(request.get("nwid").c_str(),(char **)0,16);
@ -224,7 +254,7 @@ int main(int argc,char **argv)
}
} else {
q = dbCon->query();
q << "INSERT INTO Node (id,creationTime,lastSeen,identity) VALUES (" << peerIdentity.address().toInt() << "," << Utils::now() << ",0," << quote << peerIdentity.toString(false) << ")";
q << "INSERT INTO Node (id,creationTime,identity) VALUES (" << peerIdentity.address().toInt() << "," << Utils::now() << "," << quote << peerIdentity.toString(false) << ")";
if (!q.exec()) {
fprintf(stderr,"error inserting Node row for peer %s, aborting netconf request\n",peerIdentity.address().toString().c_str());
continue;
@ -233,13 +263,6 @@ int main(int argc,char **argv)
}
}
// Update lastSeen for Node, which is always updated on a netconf request
{
Query q = dbCon->query();
q << "UPDATE Node SET lastSeen = " << Utils::now() << " WHERE id = " << peerIdentity.address().toInt();
q.exec();
}
// Look up core network information
bool isOpen = false;
unsigned int multicastPrefixBits = 0;
@ -278,11 +301,13 @@ int main(int argc,char **argv)
write(STDOUT_FILENO,&respml,4);
write(STDOUT_FILENO,respm.data(),respm.length());
stdoutWriteLock.unlock();
continue; // ABORT, wait for next request
}
}
// Check membership if this is a closed network
bool authenticated = true;
if (!isOpen) {
Query q = dbCon->query();
q << "SELECT Node_id FROM NetworkNodes WHERE Network_id = " << nwid << " AND Node_id = " << peerIdentity.address().toInt();
@ -301,10 +326,28 @@ int main(int argc,char **argv)
write(STDOUT_FILENO,&respml,4);
write(STDOUT_FILENO,respm.data(),respm.length());
stdoutWriteLock.unlock();
continue; // ABORT, wait for next request
authenticated = false;
}
}
// Update most recent activity entry for this peer, also indicating
// whether authentication was successful.
{
if (fromAddr.length()) {
Query q = dbCon->query();
q << "INSERT INTO NetworkActivity (Network_id,Node_id,lastActivityTime,authenticated,lastActivityFrom) VALUES (" << nwid << "," << peerIdentity.address().toInt() << "," << Utils::now() << "," << (authenticated ? 1 : 0) << "," << quote << fromAddr << ") ON DUPLICATE KEY UPDATE lastActivityTime = VALUES(lastActivityTime),authenticated = VALUES(authenticated),lastActivityFrom = VALUES(lastActivityFrom)";
q.exec();
} else {
Query q = dbCon->query();
q << "INSERT INTO NetworkActivity (Network_id,Node_id,lastActivityTime,authenticated) VALUES (" << nwid << "," << peerIdentity.address().toInt() << "," << Utils::now() << "," << (authenticated ? 1 : 0) << ") ON DUPLICATE KEY UPDATE lastActivityTime = VALUES(lastActivityTime),authenticated = VALUES(authenticated)";
q.exec();
}
}
if (!authenticated)
continue; // ABORT, wait for next request
// Get list of etherTypes in comma-delimited hex format
std::string etherTypeWhitelist;
{
@ -401,19 +444,6 @@ int main(int argc,char **argv)
}
}
// Update activity table for this network to indicate peer's participation
{
if (fromAddr.length()) {
Query q = dbCon->query();
q << "INSERT INTO NetworkActivity (Network_id,Node_id,lastActivityTime,lastActivityFrom) VALUES (" << nwid << "," << peerIdentity.address().toInt() << "," << Utils::now() << "," << quote << fromAddr << ") ON DUPLICATE KEY UPDATE lastActivityTime = VALUES(lastActivityTime),lastActivityFrom = VALUES(lastActivityFrom)";
q.exec();
} else {
Query q = dbCon->query();
q << "INSERT INTO NetworkActivity (Network_id,Node_id,lastActivityTime) VALUES (" << nwid << "," << peerIdentity.address().toInt() << "," << Utils::now() << ") ON DUPLICATE KEY UPDATE lastActivityTime = VALUES(lastActivityTime)";
q.exec();
}
}
// Assemble response dictionary to send to peer
Dictionary netconf;
sprintf(buf,"%.16llx",(unsigned long long)nwid);
@ -448,6 +478,11 @@ int main(int argc,char **argv)
netconf[ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC] = ipv4Static;
if (ipv6Static.length())
netconf[ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC] = ipv6Static;
if ((!isOpen)&&(authenticated)) {
CertificateOfMembership com(Utils::now(),ZT_NETWORK_AUTOCONF_DELAY * 3,nwid,peerIdentity.address());
com.sign(signingIdentity);
netconf[ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP] = com.toString();
}
// Send netconf as service bus response
{

View File

@ -86,6 +86,9 @@ void CertificateOfMembership::fromString(const char *s)
_signedBy.zero();
memset(_signature.data,0,_signature.size());
if (!*s)
return;
unsigned int colonAt = 0;
while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt;

View File

@ -265,6 +265,7 @@ public:
*
* Invalid strings will result in invalid or undefined certificate
* contents. These will subsequently fail validation and comparison.
* Empty strings will result in an empty certificate.
*
* @param s String to deserialize
*/
@ -367,6 +368,24 @@ public:
return (p - startAt);
}
inline bool operator==(const CertificateOfMembership &c) const
throw()
{
if (_signedBy != c._signedBy)
return false;
// We have to compare in depth manually since == only compares id
if (_qualifiers.size() != c._qualifiers.size())
return false;
for(unsigned long i=0;i<_qualifiers.size();++i) {
const _Qualifier &a = _qualifiers[i];
const _Qualifier &b = c._qualifiers[i];
if ((a.id != b.id)||(a.value != b.value)||(a.maxDelta != b.maxDelta))
return false;
}
return (_signature == c._signature);
}
inline bool operator!=(const CertificateOfMembership &c) const throw() { return (!(*this == c)); }
private:
struct _Qualifier
{

View File

@ -99,24 +99,6 @@
#ifndef __BYTE_ORDER
error_no_byte_order_defined;
#endif
#ifndef ZT_OSNAME
#ifdef __WINDOWS__
#define ZT_OSNAME "windows"
#else
no ZT_OSNAME defined;
#endif
#endif
#ifndef ZT_ARCH
#ifdef __WINDOWS__
#ifdef _WIN64
#define ZT_ARCH "x64"
#else
#define ZT_ARCH "x86"
#endif
#else
error_no_ZT_ARCH_defined;
#endif
#endif
/**
* Length of a ZeroTier address in bytes
@ -208,6 +190,11 @@ error_no_ZT_ARCH_defined;
*/
#define ZT_DB_CLEAN_PERIOD 300000
/**
* How long to remember peers in RAM if they haven't been used
*/
#define ZT_PEER_IN_MEMORY_EXPIRATION 600000
/**
* Delay between WHOIS retries in ms
*/
@ -293,7 +280,7 @@ error_no_ZT_ARCH_defined;
/**
* Delay between requests for updated network autoconf information
*/
#define ZT_NETWORK_AUTOCONF_DELAY 120000
#define ZT_NETWORK_AUTOCONF_DELAY 60000
/**
* Delay in core loop between checks of network autoconf newness

View File

@ -55,7 +55,7 @@ static inline std::map< Identity,std::vector<InetAddress> > _mkSupernodeMap()
addrs.clear();
if (!id.fromString("8acf059fe3:0:482f6ee5dfe902319b419de5bdc765209c0ecda38c4d6e4fcf0d33658398b4527dcd22f93112fb9befd02fd78bf7261b333fc105d192a623ca9e50fc60b374a5"))
throw std::runtime_error("invalid identity in Defaults");
addrs.push_back(InetAddress("198.199.73.93",ZT_DEFAULT_UDP_PORT));
addrs.push_back(InetAddress("162.243.77.111",ZT_DEFAULT_UDP_PORT));
sn[id] = addrs;
// nyarlathotep.zerotier.com - San Francisco, California, USA

View File

@ -533,6 +533,7 @@ void EthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const
*((uint16_t *)(putBuf + 12)) = htons((uint16_t)etherType);
memcpy(putBuf + 14,data,len);
len += 14;
int n = ::write(_fd,putBuf,len);
if (n <= 0) {
LOG("error writing packet to Ethernet tap device: %s",strerror(errno));
@ -658,7 +659,8 @@ void EthernetTap::threadMain()
{
fd_set readfds,nullfds;
MAC to,from;
char getBuf[4096 + 14];
int n,nfds,r;
char getBuf[8194];
Buffer<4096> data;
// Wait for a moment after startup -- wait for Network to finish
@ -667,8 +669,9 @@ void EthernetTap::threadMain()
FD_ZERO(&readfds);
FD_ZERO(&nullfds);
int nfds = (int)std::max(_shutdownSignalPipe[0],_fd) + 1;
nfds = (int)std::max(_shutdownSignalPipe[0],_fd) + 1;
r = 0;
for(;;) {
FD_SET(_shutdownSignalPipe[0],&readfds);
FD_SET(_fd,&readfds);
@ -678,20 +681,31 @@ void EthernetTap::threadMain()
break;
if (FD_ISSET(_fd,&readfds)) {
int n = (int)::read(_fd,getBuf,_mtu + 14);
if (n > 14) {
for(int i=0;i<6;++i)
to.data[i] = (unsigned char)getBuf[i];
for(int i=0;i<6;++i)
from.data[i] = (unsigned char)getBuf[i + 6];
data.copyFrom(getBuf + 14,(unsigned int)n - 14);
_handler(_arg,from,to,ntohs(((const uint16_t *)getBuf)[6]),data);
} else if (n < 0) {
n = (int)::read(_fd,getBuf + r,sizeof(getBuf) - r);
if (n < 0) {
if ((errno != EINTR)&&(errno != ETIMEDOUT)) {
TRACE("unexpected error reading from tap: %s",strerror(errno));
break;
}
} else {
// Some tap drivers like to send the ethernet frame and the
// payload in two chunks, so handle that by accumulating
// data until we have at least a frame.
r += n;
if (r > 14) {
if (r > (_mtu + 14)) // sanity check for weird TAP behavior on some platforms
r = _mtu + 14;
for(int i=0;i<6;++i)
to.data[i] = (unsigned char)getBuf[i];
for(int i=0;i<6;++i)
from.data[i] = (unsigned char)getBuf[i + 6];
unsigned int etherType = ntohs(((const uint16_t *)getBuf)[6]);
if (etherType != 0x8100) { // VLAN tagged frames are not supported!
data.copyFrom(getBuf + 14,(unsigned int)r - 14);
_handler(_arg,from,to,etherType,data);
}
r = 0;
}
}
}
}

View File

@ -256,13 +256,15 @@ public:
p += ZT_ADDRESS_LENGTH;
if (b[p++] != IDENTITY_TYPE_C25519)
throw std::invalid_argument("Identity: deserialize(): unsupported identity type");
throw std::invalid_argument("unsupported identity type");
memcpy(_publicKey.data,b.field(p,_publicKey.size()),_publicKey.size());
p += _publicKey.size();
unsigned int privateKeyLength = b[p++];
if ((privateKeyLength)&&(privateKeyLength == ZT_C25519_PRIVATE_KEY_LEN)) {
unsigned int privateKeyLength = (unsigned int)b[p++];
if (privateKeyLength) {
if (privateKeyLength != ZT_C25519_PRIVATE_KEY_LEN)
throw std::invalid_argument("invalid private key");
_privateKey = new C25519::Private();
memcpy(_privateKey->data,b.field(p,ZT_C25519_PRIVATE_KEY_LEN),ZT_C25519_PRIVATE_KEY_LEN);
p += ZT_C25519_PRIVATE_KEY_LEN;

View File

@ -37,7 +37,7 @@
#include "Packet.hpp"
#include "Buffer.hpp"
#define ZT_NETWORK_CERT_WRITE_BUF_SIZE 524288
#define ZT_NETWORK_CERT_WRITE_BUF_SIZE 131072
namespace ZeroTier {
@ -142,6 +142,9 @@ void Network::requestConfiguration()
void Network::addMembershipCertificate(const CertificateOfMembership &cert)
{
if (!cert) // sanity check
return;
Mutex::Lock _l(_lock);
// We go ahead and accept certs provisionally even if _isOpen is true, since
@ -149,8 +152,10 @@ void Network::addMembershipCertificate(const CertificateOfMembership &cert)
// These will be purged on clean() for open networks eventually.
CertificateOfMembership &old = _membershipCertificates[cert.issuedTo()];
if (cert.timestamp() >= old.timestamp())
if (cert.timestamp() >= old.timestamp()) {
TRACE("got new certificate for %s on network %.16llx",cert.issuedTo().toString().c_str(),cert.networkId());
old = cert;
}
}
bool Network::isAllowed(const Address &peer) const
@ -221,13 +226,16 @@ void Network::_CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned
void Network::_pushMembershipCertificate(const Address &peer,bool force,uint64_t now)
{
uint64_t timestampMaxDelta = _config->com().timestampMaxDelta();
if (!timestampMaxDelta)
uint64_t pushTimeout = _config->com().timestampMaxDelta() / 2;
if (!pushTimeout)
return; // still waiting on my own cert
if (pushTimeout > 1000)
pushTimeout -= 1000;
uint64_t &lastPushed = _lastPushedMembershipCertificate[peer];
if ((force)||((now - lastPushed) > (timestampMaxDelta / 2))) {
if ((force)||((now - lastPushed) > pushTimeout)) {
lastPushed = now;
TRACE("pushing membership cert for %.16llx to %s",(unsigned long long)_id,peer.toString().c_str());
Packet outp(peer,_r->identity.address(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE);
_config->com().serialize(outp);
@ -324,6 +332,7 @@ void Network::_dumpMulticastCerts()
if (!mcdb)
return;
if (fwrite("ZTMCD0",6,1,mcdb) != 1) {
fclose(mcdb);
Utils::rm(mcdbPath);
return;
}

View File

@ -203,34 +203,10 @@ public:
inline void pushMembershipCertificate(const Address &peer,bool force,uint64_t now)
{
Mutex::Lock _l(_lock);
if ((_config)&&(!_config->isOpen()))
if ((_config)&&(!_config->isOpen())&&(_config->com()))
_pushMembershipCertificate(peer,force,now);
}
/**
* Push membership certificate to a packed zero-terminated array of addresses
*
* This pushes to all peers in peers[] (length must be a multiple of 5) until
* len is reached or a null address is encountered.
*
* @param peers Packed array of 5-byte big-endian addresses
* @param len Length of peers[] in total, MUST be a multiple of 5
* @param force If true, push even if we've already done so within required time frame
* @param now Current time
*/
inline void pushMembershipCertificate(const void *peers,unsigned int len,bool force,uint64_t now)
{
Mutex::Lock _l(_lock);
if ((_config)&&(!_config->isOpen())) {
for(unsigned int i=0;i<len;i+=ZT_ADDRESS_LENGTH) {
Address a((char *)peers + i,ZT_ADDRESS_LENGTH);
if (a)
_pushMembershipCertificate(a,force,now);
else break;
}
}
}
/**
* @param peer Peer address to check
* @return True if peer is allowed to communicate on this network
@ -320,6 +296,8 @@ public:
}
/**
* Get current network config or return NULL
*
* @return Network configuration -- may be NULL
*/
inline SharedPtr<NetworkConfig> config2() const

View File

@ -128,6 +128,8 @@ void NetworkConfig::_fromDictionary(const Dictionary &d)
if (params.size() >= 3)
_multicastRates[MulticastGroup(i->first)] = MulticastRate(Utils::hexStrToUInt(params[0].c_str()),Utils::hexStrToUInt(params[1].c_str()),Utils::hexStrToUInt(params[2].c_str()));
}
_com.fromString(d.get(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP,std::string()));
}
} // namespace ZeroTier

View File

@ -235,7 +235,13 @@ static void _netconfServiceMessageHandler(void *renv,Service &svc,const Dictiona
try {
//TRACE("from netconf:\n%s",msg.toString().c_str());
const std::string &type = msg.get("type");
if (type == "netconf-response") {
if (type == "ready") {
LOG("received 'ready' from netconf.service, sending netconf-init with identity information...");
Dictionary initMessage;
initMessage["type"] = "netconf-init";
initMessage["netconfId"] = _r->identity.toString(true);
_r->netconfService->send(initMessage);
} else if (type == "netconf-response") {
uint64_t inRePacketId = strtoull(msg.get("requestId").c_str(),(char **)0,16);
uint64_t nwid = strtoull(msg.get("nwid").c_str(),(char **)0,16);
Address peerAddress(msg.get("peer").c_str());
@ -380,6 +386,7 @@ Node::ReasonForTermination Node::run()
// Clean up some obsolete files if present -- this will be removed later
Utils::rm((_r->homePath + ZT_PATH_SEPARATOR_S + "status"));
Utils::rm((_r->homePath + ZT_PATH_SEPARATOR_S + "thisdeviceismine"));
Utils::rm((_r->homePath + ZT_PATH_SEPARATOR_S + "peer.db"));
// Make sure networks.d exists
#ifdef __WINDOWS__
@ -407,7 +414,7 @@ Node::ReasonForTermination Node::run()
_r->mc = new Multicaster();
_r->sw = new Switch(_r);
_r->demarc = new Demarc(_r);
_r->topology = new Topology(_r,(_r->homePath + ZT_PATH_SEPARATOR_S + "peer.db").c_str());
_r->topology = new Topology(_r,Utils::fileExists((_r->homePath + ZT_PATH_SEPARATOR_S + "iddb.d").c_str()));
_r->sysEnv = new SysEnv(_r);
try {
_r->nc = new NodeConfig(_r,configAuthToken.c_str(),impl->controlPort);
@ -441,8 +448,12 @@ Node::ReasonForTermination Node::run()
try {
std::string netconfServicePath(_r->homePath + ZT_PATH_SEPARATOR_S + "services.d" + ZT_PATH_SEPARATOR_S + "netconf.service");
if (Utils::fileExists(netconfServicePath.c_str())) {
LOG("netconf.d/netconfi.service appears to exist, starting...");
LOG("netconf.d/netconf.service appears to exist, starting...");
_r->netconfService = new Service(_r,"netconf",netconfServicePath.c_str(),&_netconfServiceMessageHandler,_r);
Dictionary initMessage;
initMessage["type"] = "netconf-init";
initMessage["netconfId"] = _r->identity.toString(true);
_r->netconfService->send(initMessage);
}
} catch ( ... ) {
LOG("unexpected exception attempting to start services");

View File

@ -353,7 +353,9 @@ bool NodeConfig::decodeControlMessagePacket(const void *key,const void *data,uns
void NodeConfig::_CBcontrolPacketHandler(UdpSocket *sock,void *arg,const InetAddress &remoteAddr,const void *data,unsigned int len)
{
NodeConfig *nc = (NodeConfig *)arg;
#ifdef ZT_TRACE
const RuntimeEnvironment *_r = nc->_r;
#endif
try {
unsigned long convId = 0;

View File

@ -205,6 +205,8 @@
#define ZT_PROTO_VERB_MULTICAST_FRAME_LEN_FRAME_LEN 2
#define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME_LEN + ZT_PROTO_VERB_MULTICAST_FRAME_LEN_FRAME_LEN)
#define ZT_PROTO_VERB_MULTICAST_FRAME_FLAGS_HAS_MEMBERSHIP_CERTIFICATE 0x01
#define ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID (ZT_PACKET_IDX_PAYLOAD)
#define ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN (ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID + 8)
#define ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT (ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN + 2)
@ -507,6 +509,11 @@ public:
* [... end of signed portion ...]
* <[2] 16-bit length of signature>
* <[...] signature (currently Ed25519/SHA-512, 96 bytes in length)>
* [<[...] network membership certificate (optional)>]
*
* Flags:
* 0x01 - Multicast frame includes network membership certificate
* for original sender for this network.
*
* When a multicast frame is received:
*
@ -559,7 +566,7 @@ public:
*/
VERB_MULTICAST_LIKE = 9,
/* Network member certificate:
/* Network member certificate replication/push:
* <[...] serialized certificate of membership>
* [ ... additional certificates may follow ...]
*
@ -591,6 +598,9 @@ public:
*
* ERROR response payload:
* <[8] 64-bit network ID>
*
* Support is optional. Nodes should return UNSUPPORTED_OPERATION if
* not supported or enabled.
*/
VERB_NETWORK_CONFIG_REQUEST = 11,
@ -605,7 +615,52 @@ public:
* It does not generate an OK or ERROR message, and is treated only as
* a hint to refresh now.
*/
VERB_NETWORK_CONFIG_REFRESH = 12
VERB_NETWORK_CONFIG_REFRESH = 12,
/* Request information about a shared file:
* <[1] flags, currently unused and must be 0>
* <[2] 16-bit length of filename>
* <[...] name of file being requested>
*
* OK response payload (indicates that we have and will share):
* <[1] flags, currently unused and must be 0>
* <[2] 16-bit length of filename>
* <[...] name of file being requested>
* <[64] full length SHA-512 hash of file contents>
* <[4] 32-bit length of file in bytes>
* <[5] Signing ZeroTier One identity address>
* <[2] 16-bit length of signature of SHA-512 hash>
* <[...] signature of SHA-512 hash>
*
* ERROR response payload:
* <[2] 16-bit length of filename>
* <[...] name of file being requested>
*
* Support is optional. Nodes should return UNSUPPORTED_OPERATION if
* not supported or enabled.
*/
VERB_FILE_INFO_REQUEST = 13,
/* Request a piece of a shared file
* <[16] first 16 bytes of SHA-512 of file being requested>
* <[4] 32-bit index of desired chunk>
* <[2] 16-bit length of desired chunk>
*
* OK response payload:
* <[16] first 16 bytes of SHA-512 of file being requested>
* <[4] 32-bit index of desired chunk>
* <[2] 16-bit length of desired chunk>
* <[...] the chunk>
*
* ERROR response payload:
* <[16] first 16 bytes of SHA-512 of file being requested>
* <[4] 32-bit index of desired chunk>
* <[2] 16-bit length of desired chunk>
*
* Support is optional. Nodes should return UNSUPPORTED_OPERATION if
* not supported or enabled.
*/
VERB_FILE_BLOCK_REQUEST = 14
};
/**

View File

@ -189,16 +189,16 @@ bool PacketDecoder::_doHELLO(const RuntimeEnvironment *_r)
return true;
}
// Do we already have this peer?
SharedPtr<Peer> peer(_r->topology->getPeer(id.address()));
if (peer) {
// Check to make sure this isn't a colliding identity (different key,
// but same address). The odds are spectacularly low but it could happen.
// Could also be a sign of someone doing something nasty.
if (peer->identity() != id) {
// Sorry, someone beat you to that address. What are the odds?
// Well actually they're around two in 2^40. You should play
// the lottery.
unsigned char key[ZT_PEER_SECRET_KEY_LENGTH];
if (_r->identity.agree(id,key,ZT_PEER_SECRET_KEY_LENGTH)) {
TRACE("rejected HELLO from %s(%s): address already claimed",source().toString().c_str(),_remoteAddress.toString().c_str());
Packet outp(source(),_r->identity.address(),Packet::VERB_ERROR);
outp.append((unsigned char)Packet::VERB_HELLO);
outp.append(packetId());
@ -209,7 +209,26 @@ bool PacketDecoder::_doHELLO(const RuntimeEnvironment *_r)
return true;
} // else continue and send OK since we already know thee...
} else {
// Learn a new peer
// If we don't have a peer record on file, check the identity cache (if
// we have one) to see if we have a cached identity. Then check that for
// collision before adding a new peer.
Identity alreadyHaveCachedId(_r->topology->getIdentity(id.address()));
if ((alreadyHaveCachedId)&&(id != alreadyHaveCachedId)) {
unsigned char key[ZT_PEER_SECRET_KEY_LENGTH];
if (_r->identity.agree(id,key,ZT_PEER_SECRET_KEY_LENGTH)) {
TRACE("rejected HELLO from %s(%s): address already claimed",source().toString().c_str(),_remoteAddress.toString().c_str());
Packet outp(source(),_r->identity.address(),Packet::VERB_ERROR);
outp.append((unsigned char)Packet::VERB_HELLO);
outp.append(packetId());
outp.append((unsigned char)Packet::ERROR_IDENTITY_COLLISION);
outp.armor(key,true);
_r->demarc->send(_localPort,_remoteAddress,outp.data(),outp.size(),-1);
}
return true;
}
// Learn a new peer if it's new. This also adds it to the identity
// cache if that's enabled.
peer = _r->topology->addPeer(SharedPtr<Peer>(new Peer(_r->identity,id)));
}
@ -286,12 +305,12 @@ bool PacketDecoder::_doOK(const RuntimeEnvironment *_r,const SharedPtr<Peer> &pe
bool PacketDecoder::_doWHOIS(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer)
{
if (payloadLength() == ZT_ADDRESS_LENGTH) {
SharedPtr<Peer> p(_r->topology->getPeer(Address(payload(),ZT_ADDRESS_LENGTH)));
if (p) {
Identity id(_r->topology->getIdentity(Address(payload(),ZT_ADDRESS_LENGTH)));
if (id) {
Packet outp(source(),_r->identity.address(),Packet::VERB_OK);
outp.append((unsigned char)Packet::VERB_WHOIS);
outp.append(packetId());
p->identity().serialize(outp,false);
id.serialize(outp,false);
outp.armor(peer->key(),true);
_r->demarc->send(_localPort,_remoteAddress,outp.data(),outp.size(),-1);
//TRACE("sent WHOIS response to %s for %s",source().toString().c_str(),Address(payload(),ZT_ADDRESS_LENGTH).toString().c_str());
@ -417,7 +436,7 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared
unsigned char *const bloom = field(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PROPAGATION_BLOOM,ZT_PROTO_VERB_MULTICAST_FRAME_LEN_PROPAGATION_BLOOM);
// These fields don't -- they're signed by the original sender
// const unsigned int flags = (*this)[ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FLAGS];
const unsigned int flags = (*this)[ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FLAGS];
const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_NETWORK_ID);
const uint16_t bloomNonce = at<uint16_t>(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PROPAGATION_BLOOM_NONCE);
const unsigned int prefixBits = (*this)[ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PROPAGATION_PREFIX_BITS];
@ -431,6 +450,27 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared
const unsigned int signatureLen = at<uint16_t>(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME + frameLen);
const unsigned char *const signature = field(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME + frameLen + 2,signatureLen);
SharedPtr<Network> network(_r->nc->network(nwid));
// Grab, verify, and learn certificate if any -- provided we are a member of this network
// Note: we can do this before verification of the actual packet, since the certificate
// has its own separate signature.
if (((flags & ZT_PROTO_VERB_MULTICAST_FRAME_FLAGS_HAS_MEMBERSHIP_CERTIFICATE))&&(network)) {
CertificateOfMembership originCom(*this,ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME + frameLen + 2 + signatureLen);
Address signedBy(originCom.signedBy());
if ((originCom.networkId() == nwid)&&(signedBy == network->controller())) {
SharedPtr<Peer> signingPeer(_r->topology->getPeer(signedBy));
if (!signingPeer) {
// Technically this shouldn't happen, but handle it anyway...
_r->sw->requestWhois(signedBy);
_step = DECODE_WAITING_FOR_MULTICAST_FRAME_ORIGINAL_SENDER_LOOKUP; // causes processing to come back here
return false;
} else if (originCom.verify(signingPeer->identity())) {
network->addMembershipCertificate(originCom);
}
}
}
// Check multicast signature to verify original sender
const unsigned int signedPartLen = (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME - ZT_PROTO_VERB_MULTICAST_FRAME_IDX__START_OF_SIGNED_PORTION) + frameLen;
if (!originPeer->identity().verify(field(ZT_PROTO_VERB_MULTICAST_FRAME_IDX__START_OF_SIGNED_PORTION,signedPartLen),signedPartLen,signature,signatureLen)) {
@ -471,7 +511,6 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared
#endif
unsigned int maxDepth = ZT_MULTICAST_GLOBAL_MAX_DEPTH;
SharedPtr<Network> network(_r->nc->network(nwid));
if ((origin == _r->identity.address())||(_r->mc->deduplicate(nwid,guid))) {
// Ordinary nodes will drop duplicates. Supernodes keep propagating
@ -641,12 +680,6 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared
while (newFifoPtr != newFifoEnd)
*(newFifoPtr++) = (unsigned char)0;
// If we're forwarding a packet within a private network that we are
// a member of, also propagate our cert if needed. This propagates
// it to everyone including people who will receive this multicast.
if (network)
network->pushMembershipCertificate(newFifo,sizeof(newFifo),false,Utils::now());
// First element in newFifo[] is next hop
Address nextHop(newFifo,ZT_ADDRESS_LENGTH);
if ((!nextHop)&&(!_r->topology->amSupernode())) {
@ -828,8 +861,10 @@ bool PacketDecoder::_doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *_r,const
while ((ptr + sizeof(uint64_t)) <= size()) {
uint64_t nwid = at<uint64_t>(ptr); ptr += sizeof(uint64_t);
SharedPtr<Network> nw(_r->nc->network(nwid));
if ((nw)&&(source() == nw->controller())) // only respond to requests from controller
if ((nw)&&(source() == nw->controller())) { // only respond to requests from controller
TRACE("NETWORK_CONFIG_REFRESH from %s, refreshing network %.16llx",source().toString().c_str(),nwid);
nw->requestConfiguration();
}
}
} catch (std::exception &exc) {
TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),exc.what());

View File

@ -34,12 +34,13 @@ Peer::Peer() :
_id(),
_ipv4p(),
_ipv6p(),
_lastUsed(0),
_lastUnicastFrame(0),
_lastMulticastFrame(0),
_lastAnnouncedTo(0),
_vMajor(0),
_vMinor(0),
_vRevision(0),
_dirty(false)
_vRevision(0)
{
}
@ -48,12 +49,13 @@ Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity)
_id(peerIdentity),
_ipv4p(),
_ipv6p(),
_lastUsed(0),
_lastUnicastFrame(0),
_lastMulticastFrame(0),
_lastAnnouncedTo(0),
_vMajor(0),
_vMinor(0),
_vRevision(0),
_dirty(true)
_vRevision(0)
{
if (!myIdentity.agree(peerIdentity,_key,ZT_PEER_SECRET_KEY_LENGTH))
throw std::runtime_error("new peer identity key agreement failed");
@ -72,16 +74,12 @@ void Peer::onReceive(const RuntimeEnvironment *_r,Demarc::Port localPort,const I
_lastAnnouncedTo = now;
_r->sw->announceMulticastGroups(SharedPtr<Peer>(this));
}
_dirty = true;
}
if (verb == Packet::VERB_FRAME) {
_lastUnicastFrame = now;
_dirty = true;
} else if (verb == Packet::VERB_MULTICAST_FRAME) {
_lastMulticastFrame = now;
_dirty = true;
}
}
@ -90,7 +88,6 @@ bool Peer::send(const RuntimeEnvironment *_r,const void *data,unsigned int len,u
if ((_ipv6p.isActive(now))||((!(_ipv4p.addr))&&(_ipv6p.addr))) {
if (_r->demarc->send(_ipv6p.localPort,_ipv6p.addr,data,len,-1)) {
_ipv6p.lastSend = now;
_dirty = true;
return true;
}
}
@ -98,7 +95,6 @@ bool Peer::send(const RuntimeEnvironment *_r,const void *data,unsigned int len,u
if (_ipv4p.addr) {
if (_r->demarc->send(_ipv4p.localPort,_ipv4p.addr,data,len,-1)) {
_ipv4p.lastSend = now;
_dirty = true;
return true;
}
}
@ -112,14 +108,12 @@ bool Peer::sendFirewallOpener(const RuntimeEnvironment *_r,uint64_t now)
if (_ipv4p.addr) {
if (_r->demarc->send(_ipv4p.localPort,_ipv4p.addr,"\0",1,ZT_FIREWALL_OPENER_HOPS)) {
_ipv4p.lastFirewallOpener = now;
_dirty = true;
sent = true;
}
}
if (_ipv6p.addr) {
if (_r->demarc->send(_ipv6p.localPort,_ipv6p.addr,"\0",1,ZT_FIREWALL_OPENER_HOPS)) {
_ipv6p.lastFirewallOpener = now;
_dirty = true;
sent = true;
}
}
@ -132,14 +126,12 @@ bool Peer::sendPing(const RuntimeEnvironment *_r,uint64_t now)
if (_ipv4p.addr) {
if (_r->sw->sendHELLO(SharedPtr<Peer>(this),_ipv4p.localPort,_ipv4p.addr)) {
_ipv4p.lastSend = now;
_dirty = true;
sent = true;
}
}
if (_ipv6p.addr) {
if (_r->sw->sendHELLO(SharedPtr<Peer>(this),_ipv6p.localPort,_ipv6p.addr)) {
_ipv6p.lastSend = now;
_dirty = true;
sent = true;
}
}
@ -151,11 +143,9 @@ void Peer::setPathAddress(const InetAddress &addr,bool fixed)
if (addr.isV4()) {
_ipv4p.addr = addr;
_ipv4p.fixed = fixed;
_dirty = true;
} else if (addr.isV6()) {
_ipv6p.addr = addr;
_ipv6p.fixed = fixed;
_dirty = true;
}
}
@ -165,15 +155,12 @@ void Peer::clearFixedFlag(InetAddress::AddressType t)
case InetAddress::TYPE_NULL:
_ipv4p.fixed = false;
_ipv6p.fixed = false;
_dirty = true;
break;
case InetAddress::TYPE_IPV4:
_ipv4p.fixed = false;
_dirty = true;
break;
case InetAddress::TYPE_IPV6:
_ipv6p.fixed = false;
_dirty = true;
break;
}
}

View File

@ -48,24 +48,6 @@
#include "NonCopyable.hpp"
#include "Mutex.hpp"
/**
* Max length of serialized peer record
*/
#define ZT_PEER_MAX_SERIALIZED_LENGTH ( \
ZT_PEER_SECRET_KEY_LENGTH + \
ZT_IDENTITY_MAX_BINARY_SERIALIZED_LENGTH + \
( ( \
(sizeof(uint64_t) * 4) + \
sizeof(uint16_t) + \
1 + \
sizeof(uint16_t) + \
16 + \
1 \
) * 2) + \
(sizeof(uint64_t) * 3) + \
(sizeof(uint16_t) * 3) \
)
namespace ZeroTier {
/**
@ -99,6 +81,16 @@ public:
Peer(const Identity &myIdentity,const Identity &peerIdentity)
throw(std::runtime_error);
/**
* @return Time peer record was last used in any way
*/
inline uint64_t lastUsed() const throw() { return _lastUsed; }
/**
* @param now New time of last use
*/
inline void setLastUsed(uint64_t now) throw() { _lastUsed = now; }
/**
* @return This peer's ZT address (short for identity().address())
*/
@ -254,10 +246,8 @@ public:
{
if (addr == _ipv4p.addr) {
_ipv4p.latency = latency;
_dirty = true;
} else if (addr == _ipv6p.addr) {
_ipv6p.latency = latency;
_dirty = true;
}
}
@ -357,33 +347,15 @@ public:
return std::string("?");
}
/**
* Get and reset dirty flag
*
* @return Previous value of dirty flag before reset
*/
inline bool getAndResetDirty()
throw()
{
bool d = _dirty;
_dirty = false;
return d;
}
/**
* @return Current value of dirty flag
*/
inline bool dirty() const throw() { return _dirty; }
template<unsigned int C>
inline void serialize(Buffer<C> &b)
throw(std::out_of_range)
{
b.append((unsigned char)3); // version
b.append((unsigned char)4); // version
b.append(_key,sizeof(_key));
_id.serialize(b,false);
_ipv4p.serialize(b);
_ipv6p.serialize(b);
b.append(_lastUsed);
b.append(_lastUnicastFrame);
b.append(_lastMulticastFrame);
b.append(_lastAnnouncedTo);
@ -394,17 +366,17 @@ public:
template<unsigned int C>
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
throw(std::out_of_range,std::invalid_argument)
{
unsigned int p = startAt;
if (b[p++] != 3)
if (b[p++] != 4)
throw std::invalid_argument("Peer: deserialize(): version mismatch");
memcpy(_key,b.field(p,sizeof(_key)),sizeof(_key)); p += sizeof(_key);
p += _id.deserialize(b,p);
p += _ipv4p.deserialize(b,p);
p += _ipv6p.deserialize(b,p);
_lastUsed = b.template at<uint64_t>(p); p += sizeof(uint64_t);
_lastUnicastFrame = b.template at<uint64_t>(p); p += sizeof(uint64_t);
_lastMulticastFrame = b.template at<uint64_t>(p); p += sizeof(uint64_t);
_lastAnnouncedTo = b.template at<uint64_t>(p); p += sizeof(uint64_t);
@ -412,8 +384,6 @@ public:
_vMinor = b.template at<uint16_t>(p); p += sizeof(uint16_t);
_vRevision = b.template at<uint16_t>(p); p += sizeof(uint16_t);
_dirty = false;
return (p - startAt);
}
@ -538,14 +508,12 @@ private:
WanPath _ipv4p;
WanPath _ipv6p;
uint64_t _lastUsed;
uint64_t _lastUnicastFrame;
uint64_t _lastMulticastFrame;
uint64_t _lastAnnouncedTo;
unsigned int _vMajor,_vMinor,_vRevision;
// Fields below this line are not persisted with serialize() ---------------
bool _dirty;
AtomicCounter __refCount;
};

View File

@ -117,7 +117,6 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
unsigned char *const fifoEnd = fifo + sizeof(fifo);
const unsigned int signedPartLen = (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME - ZT_PROTO_VERB_MULTICAST_FRAME_IDX__START_OF_SIGNED_PORTION) + data.size();
const SharedPtr<Peer> supernode(_r->topology->getBestSupernode());
uint64_t now = Utils::now();
for(unsigned int prefix=0,np=((unsigned int)2 << (nconf->multicastPrefixBits() - 1));prefix<np;++prefix) {
memset(bloom,0,sizeof(bloom));
@ -134,17 +133,11 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
else continue;
}
// If network is not open, make sure all recipients have our membership
// certificate if we haven't sent it recently. As the multicast goes
// further down the line, peers beyond the first batch will ask us for
// our membership certificate if they need it.
network->pushMembershipCertificate(fifo,sizeof(fifo),false,now);
Packet outp(firstHop,_r->identity.address(),Packet::VERB_MULTICAST_FRAME);
outp.append((uint16_t)0);
outp.append(fifo + ZT_ADDRESS_LENGTH,ZT_PROTO_VERB_MULTICAST_FRAME_LEN_PROPAGATION_FIFO); // remainder of fifo is loaded into packet
outp.append(bloom,ZT_PROTO_VERB_MULTICAST_FRAME_LEN_PROPAGATION_BLOOM);
outp.append((unsigned char)0);
outp.append((nconf->com()) ? (unsigned char)ZT_PROTO_VERB_MULTICAST_FRAME_FLAGS_HAS_MEMBERSHIP_CERTIFICATE : (unsigned char)0);
outp.append(network->id());
outp.append(bloomNonce);
outp.append((unsigned char)nconf->multicastPrefixBits());
@ -164,6 +157,9 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
outp.append((uint16_t)sig.size());
outp.append(sig.data,sig.size());
if (nconf->com())
nconf->com().serialize(outp);
outp.compress();
send(outp,true);
}
@ -194,8 +190,6 @@ void Switch::send(const Packet &packet,bool encrypt)
return;
}
//TRACE(">> %.16llx %s -> %s (size: %u) (enc: %s)",(unsigned long long)packet.packetId(),packet.source().toString().c_str(),packet.destination().toString().c_str(),packet.size(),(encrypt ? "yes" : "no"));
if (!_trySend(packet,encrypt)) {
Mutex::Lock _l(_txQueue_m);
_txQueue.insert(std::pair< Address,TXQueueEntry >(packet.destination(),TXQueueEntry(Utils::now(),packet,encrypt)));

View File

@ -31,34 +31,23 @@
#include "NodeConfig.hpp"
#include "CMWC4096.hpp"
#define ZT_PEER_WRITE_BUF_SIZE 131072
namespace ZeroTier {
#define ZT_KISSDB_HASH_TABLE_SIZE 32768
#define ZT_KISSDB_KEY_SIZE ZT_ADDRESS_LENGTH
#define ZT_KISSDB_VALUE_SIZE ZT_PEER_MAX_SERIALIZED_LENGTH
Topology::Topology(const RuntimeEnvironment *renv,const char *dbpath) :
Topology::Topology(const RuntimeEnvironment *renv,bool enablePermanentIdCaching) :
_r(renv),
_amSupernode(false)
{
if (KISSDB_open(&_dbm,dbpath,KISSDB_OPEN_MODE_RWCREAT,ZT_KISSDB_HASH_TABLE_SIZE,ZT_KISSDB_KEY_SIZE,ZT_KISSDB_VALUE_SIZE)) {
if (KISSDB_open(&_dbm,dbpath,KISSDB_OPEN_MODE_RWREPLACE,ZT_KISSDB_HASH_TABLE_SIZE,ZT_KISSDB_KEY_SIZE,ZT_KISSDB_VALUE_SIZE))
throw std::runtime_error("unable to open peer database (rw/create)");
}
if ((_dbm.key_size != ZT_KISSDB_KEY_SIZE)||(_dbm.value_size != ZT_KISSDB_VALUE_SIZE)||(_dbm.hash_table_size != ZT_KISSDB_HASH_TABLE_SIZE)) {
KISSDB_close(&_dbm);
if (KISSDB_open(&_dbm,dbpath,KISSDB_OPEN_MODE_RWREPLACE,ZT_KISSDB_HASH_TABLE_SIZE,ZT_KISSDB_KEY_SIZE,ZT_KISSDB_VALUE_SIZE))
throw std::runtime_error("unable to open peer database (recreate)");
}
Utils::lockDownFile(dbpath,false); // node.db caches secrets
if (enablePermanentIdCaching)
_idCacheBase = (_r->homePath + ZT_PATH_SEPARATOR_S + "iddb.d");
_loadPeers();
}
Topology::~Topology()
{
// Flush last changes to disk
clean();
_dumpPeers();
}
void Topology::setSupernodes(const std::map< Identity,std::vector<InetAddress> > &sn)
@ -68,6 +57,7 @@ void Topology::setSupernodes(const std::map< Identity,std::vector<InetAddress> >
_supernodes = sn;
_supernodeAddresses.clear();
_supernodePeers.clear();
uint64_t now = Utils::now();
for(std::map< Identity,std::vector<InetAddress> >::const_iterator i(sn.begin());i!=sn.end();++i) {
if (i->first != _r->identity) {
@ -76,6 +66,7 @@ void Topology::setSupernodes(const std::map< Identity,std::vector<InetAddress> >
p = addPeer(SharedPtr<Peer>(new Peer(_r->identity,i->first)));
for(std::vector<InetAddress>::const_iterator j(i->second.begin());j!=i->second.end();++j)
p->setPathAddress(*j,true);
p->setLastUsed(now);
_supernodePeers.push_back(p);
}
_supernodeAddresses.insert(i->first.address());
@ -90,27 +81,12 @@ SharedPtr<Peer> Topology::addPeer(const SharedPtr<Peer> &peer)
TRACE("BUG: addNewPeer() caught and ignored attempt to add peer for self");
throw std::logic_error("cannot add peer for self");
}
SharedPtr<Peer> actualPeer;
{
Mutex::Lock _l(_activePeers_m);
actualPeer = _activePeers.insert(std::pair< Address,SharedPtr<Peer> >(peer->address(),peer)).first->second;
}
uint64_t atmp[ZT_ADDRESS_LENGTH];
actualPeer->address().copyTo(atmp,ZT_ADDRESS_LENGTH);
Buffer<ZT_PEER_MAX_SERIALIZED_LENGTH> b;
actualPeer->serialize(b);
b.zeroUnused();
_dbm_m.lock();
if (KISSDB_put(&_dbm,atmp,b.data())) {
TRACE("error writing %s to peerdb",actualPeer->address().toString().c_str());
} else actualPeer->getAndResetDirty();
_dbm_m.unlock();
return actualPeer;
uint64_t now = Utils::now();
Mutex::Lock _l(_activePeers_m);
SharedPtr<Peer> p(_activePeers.insert(std::pair< Address,SharedPtr<Peer> >(peer->address(),peer)).first->second);
p->setLastUsed(now);
saveIdentity(p->identity());
return p;
}
SharedPtr<Peer> Topology::getPeer(const Address &zta)
@ -119,37 +95,42 @@ SharedPtr<Peer> Topology::getPeer(const Address &zta)
TRACE("BUG: ignored attempt to getPeer() for self, returned NULL");
return SharedPtr<Peer>();
}
{
Mutex::Lock _l(_activePeers_m);
std::map< Address,SharedPtr<Peer> >::const_iterator ap(_activePeers.find(zta));
if ((ap != _activePeers.end())&&(ap->second))
return ap->second;
uint64_t now = Utils::now();
Mutex::Lock _l(_activePeers_m);
std::map< Address,SharedPtr<Peer> >::const_iterator ap(_activePeers.find(zta));
if ((ap != _activePeers.end())&&(ap->second)) {
ap->second->setLastUsed(now);
return ap->second;
}
unsigned char ztatmp[ZT_ADDRESS_LENGTH];
zta.copyTo(ztatmp,ZT_ADDRESS_LENGTH);
Buffer<ZT_KISSDB_VALUE_SIZE> b(ZT_KISSDB_VALUE_SIZE);
_dbm_m.lock();
if (!KISSDB_get(&_dbm,ztatmp,b.data())) {
_dbm_m.unlock();
SharedPtr<Peer> p(new Peer());
try {
p->deserialize(b,0);
Mutex::Lock _l(_activePeers_m);
_activePeers[zta] = p;
return p;
} catch ( ... ) {
TRACE("unexpected exception deserializing peer %s from peerdb",zta.toString().c_str());
return SharedPtr<Peer>();
}
} else _dbm_m.unlock();
return SharedPtr<Peer>();
}
Identity Topology::getIdentity(const Address &zta)
{
SharedPtr<Peer> p(getPeer(zta));
if (p)
return p->identity();
if (_idCacheBase.length()) {
std::string idcPath(_idCacheBase + ZT_PATH_SEPARATOR_S + zta.toString());
std::string ids;
if (Utils::readFile(idcPath.c_str(),ids)) {
try {
return Identity(ids);
} catch ( ... ) {} // ignore invalid IDs
}
}
return Identity();
}
void Topology::saveIdentity(const Identity &id)
{
if ((id)&&(_idCacheBase.length())) {
std::string idcPath(_idCacheBase + ZT_PATH_SEPARATOR_S + id.address().toString());
if (!Utils::fileExists(idcPath.c_str()))
Utils::writeFile(idcPath.c_str(),id.toString(false));
}
}
SharedPtr<Peer> Topology::getBestSupernode(const Address *avoid,unsigned int avoidCount,bool strictAvoid) const
{
SharedPtr<Peer> bestSupernode;
@ -183,8 +164,11 @@ skip_and_try_next_supernode:
++sn;
}
if ((bestSupernode)||(strictAvoid))
if (bestSupernode) {
bestSupernode->setLastUsed(now);
return bestSupernode;
} else if (strictAvoid)
return SharedPtr<Peer>();
for(std::vector< SharedPtr<Peer> >::const_iterator sn=_supernodePeers.begin();sn!=_supernodePeers.end();++sn) {
if ((*sn)->hasActiveDirectPath(now)) {
@ -203,36 +187,110 @@ skip_and_try_next_supernode:
}
if (bestSupernode)
return bestSupernode;
return _supernodePeers[_r->prng->next32() % _supernodePeers.size()];
bestSupernode->setLastUsed(now);
return bestSupernode;
}
void Topology::clean()
{
TRACE("cleaning caches and flushing modified peers to disk...");
uint64_t now = Utils::now();
Mutex::Lock _l(_activePeers_m);
Mutex::Lock _l2(_supernodes_m);
for(std::map< Address,SharedPtr<Peer> >::iterator p(_activePeers.begin());p!=_activePeers.end();) {
if (((now - p->second->lastUsed()) >= ZT_PEER_IN_MEMORY_EXPIRATION)&&(!_supernodeAddresses.count(p->second->address())))
_activePeers.erase(p++);
else ++p;
}
}
void Topology::_dumpPeers()
{
Buffer<ZT_PEER_WRITE_BUF_SIZE> buf;
std::string pdpath(_r->homePath + ZT_PATH_SEPARATOR_S + "peers.persist");
Mutex::Lock _l(_activePeers_m);
FILE *pd = fopen(pdpath.c_str(),"wb");
if (!pd)
return;
if (fwrite("ZTPD0",5,1,pd) != 1) {
fclose(pd);
Utils::rm(pdpath);
return;
}
for(std::map< Address,SharedPtr<Peer> >::iterator p(_activePeers.begin());p!=_activePeers.end();++p) {
if (p->second->getAndResetDirty()) {
try {
uint64_t atmp[ZT_ADDRESS_LENGTH];
p->second->identity().address().copyTo(atmp,ZT_ADDRESS_LENGTH);
Buffer<ZT_PEER_MAX_SERIALIZED_LENGTH> b;
p->second->serialize(b);
b.zeroUnused();
_dbm_m.lock();
if (KISSDB_put(&_dbm,atmp,b.data())) {
TRACE("error writing %s to peer.db",p->second->identity().address().toString().c_str());
try {
p->second->serialize(buf);
if (buf.size() >= (ZT_PEER_WRITE_BUF_SIZE / 2)) {
if (fwrite(buf.data(),buf.size(),1,pd) != 1) {
fclose(pd);
Utils::rm(pdpath);
return;
}
_dbm_m.unlock();
} catch ( ... ) {
TRACE("unexpected exception flushing %s to peer.db",p->second->identity().address().toString().c_str());
buf.clear();
}
} catch ( ... ) {
fclose(pd);
Utils::rm(pdpath);
return;
}
}
if (buf.size()) {
if (fwrite(buf.data(),buf.size(),1,pd) != 1) {
fclose(pd);
Utils::rm(pdpath);
return;
}
}
fclose(pd);
Utils::lockDownFile(pdpath.c_str(),false);
}
void Topology::_loadPeers()
{
Buffer<ZT_PEER_WRITE_BUF_SIZE> buf;
std::string pdpath(_r->homePath + ZT_PATH_SEPARATOR_S + "peers.persist");
Mutex::Lock _l(_activePeers_m);
_activePeers.clear();
FILE *pd = fopen(pdpath.c_str(),"rb");
if (!pd)
return;
try {
char magic[5];
if ((fread(magic,5,1,pd) == 1)&&(!memcmp("ZTPD0",magic,5))) {
long rlen = 0;
do {
long rlen = (long)fread(buf.data() + buf.size(),1,ZT_PEER_WRITE_BUF_SIZE - buf.size(),pd);
if (rlen < 0) rlen = 0;
buf.setSize(buf.size() + (unsigned int)rlen);
unsigned int ptr = 0;
while ((ptr < (ZT_PEER_WRITE_BUF_SIZE / 2))&&(ptr < buf.size())) {
SharedPtr<Peer> p(new Peer());
ptr += p->deserialize(buf,ptr);
_activePeers[p->address()] = p;
saveIdentity(p->identity());
}
if (ptr) {
memmove(buf.data(),buf.data() + ptr,buf.size() - ptr);
buf.setSize(buf.size() - ptr);
}
} while (rlen > 0);
fclose(pd);
} else {
fclose(pd);
Utils::rm(pdpath);
}
} catch ( ... ) {
// Membership cert dump file invalid. We'll re-learn them off the net.
_activePeers.clear();
fclose(pd);
Utils::rm(pdpath);
}
}
} // namespace ZeroTier

View File

@ -43,8 +43,6 @@
#include "InetAddress.hpp"
#include "Utils.hpp"
#include "../ext/kissdb/kissdb.h"
namespace ZeroTier {
class RuntimeEnvironment;
@ -55,7 +53,7 @@ class RuntimeEnvironment;
class Topology
{
public:
Topology(const RuntimeEnvironment *renv,const char *dbpath);
Topology(const RuntimeEnvironment *renv,bool enablePermanentIdCaching);
~Topology();
/**
@ -84,6 +82,25 @@ public:
*/
SharedPtr<Peer> getPeer(const Address &zta);
/**
* Get an identity if cached or available in a peer record
*
* @param zta ZeroTier address
* @return Identity or NULL-identity if not found
*/
Identity getIdentity(const Address &zta);
/**
* Save identity in permanent store, or do nothing if disabled
*
* This is called automatically by addPeer(), so it should not need to be
* called manually anywhere else. The private part of the identity, if
* present, is NOT cached by this.
*
* @param id Identity to save
*/
void saveIdentity(const Identity &id);
/**
* @return Current network supernodes
*/
@ -273,6 +290,11 @@ public:
private:
const RuntimeEnvironment *const _r;
void _dumpPeers();
void _loadPeers();
std::string _idCacheBase; // empty if identity caching disabled
std::map< Address,SharedPtr<Peer> > _activePeers;
Mutex _activePeers_m;
@ -283,9 +305,6 @@ private:
// Set to true if my identity is in _supernodes
volatile bool _amSupernode;
KISSDB _dbm;
Mutex _dbm_m;
};
} // namespace ZeroTier

View File

@ -186,31 +186,42 @@ unsigned int Utils::unhex(const char *hex,unsigned int hexlen,void *buf,unsigned
void Utils::getSecureRandom(void *buf,unsigned int bytes)
{
static Mutex randomLock;
static char randbuf[32768];
static char randbuf[16384];
static unsigned int randptr = sizeof(randbuf);
#ifdef __WINDOWS__
static Salsa20 s20;
volatile bool s20Initialized = false;
#endif
static bool randInitialized = false;
Mutex::Lock _l(randomLock);
// A Salsa20 instance is used to mangle whatever our base
// random source happens to be.
if (!randInitialized) {
randInitialized = true;
memset(randbuf,0,sizeof(randbuf));
char s20key[33];
uint64_t s20iv = now();
Utils::snprintf(s20key,sizeof(s20key),"%.16llx%.16llx",(unsigned long long)now(),(unsigned long long)((void *)&s20iv));
s20.init(s20key,256,&s20iv,8);
}
for(unsigned int i=0;i<bytes;++i) {
if (randptr >= sizeof(randbuf)) {
#ifdef __UNIX_LIKE__
int fd = ::open("/dev/urandom",O_RDONLY);
if (fd < 0) {
fprintf(stderr,"FATAL ERROR: unable to open /dev/urandom: %s"ZT_EOL_S,strerror(errno));
exit(-1);
{
int fd = ::open("/dev/urandom",O_RDONLY);
if (fd < 0) {
fprintf(stderr,"FATAL ERROR: unable to open /dev/urandom: %s"ZT_EOL_S,strerror(errno));
exit(-1);
}
if ((int)::read(fd,randbuf,sizeof(randbuf)) != (int)sizeof(randbuf)) {
fprintf(stderr,"FATAL ERROR: unable to read from /dev/urandom"ZT_EOL_S);
exit(-1);
}
::close(fd);
}
if ((int)::read(fd,randbuf,sizeof(randbuf)) != (int)sizeof(randbuf)) {
fprintf(stderr,"FATAL ERROR: unable to read from /dev/urandom"ZT_EOL_S);
exit(-1);
}
::close(fd);
#else
#ifdef __WINDOWS__
if (!s20Initialized) {
s20Initialized = true;
{
char ktmp[32];
char ivtmp[8];
for(int i=0;i<32;++i) ktmp[i] = (char)rand();
@ -218,17 +229,17 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
double now = Utils::nowf();
memcpy(ktmp,&now,sizeof(now));
DWORD tmp = GetCurrentProcessId();
memcpy(ktmp + sizeof(double),&tmp,sizeof(tmp));
memcpy(ktmp + sizeof(now),&tmp,sizeof(tmp));
tmp = GetTickCount();
memcpy(ktmp + sizeof(double) + sizeof(DWORD),&tmp,sizeof(tmp));
s20.init(ktmp,256,ivtmp);
for(int i=0;i<sizeof(randbuf);++i) randbuf[i] = (char)rand();
memcpy(ktmp + sizeof(now) + sizeof(DWORD),&tmp,sizeof(tmp));
Salsa20 s20tmp(ktmp,256,ivtmp,8);
s20tmp.encrypt(randbuf,randbuf,sizeof(randbuf));
}
s20.encrypt(randbuf,randbuf,sizeof(randbuf));
#else
no getSecureRandom() implementation;
#endif
#endif
s20.encrypt(randbuf,randbuf,sizeof(randbuf));
randptr = 0;
}
((char *)buf)[i] = randbuf[randptr++];

View File

@ -1,5 +1,4 @@
OBJS=\
ext/kissdb/kissdb.o \
ext/lz4/lz4hc.o \
ext/lz4/lz4.o \
node/C25519.o \

View File

@ -51,6 +51,7 @@
#include "node/SHA512.hpp"
#include "node/C25519.hpp"
#include "node/Poly1305.hpp"
#include "node/CertificateOfMembership.hpp"
#ifdef __WINDOWS__
#include <tchar.h>
@ -67,6 +68,11 @@ static int testCrypto()
unsigned char buf1[16384];
unsigned char buf2[sizeof(buf1)],buf3[sizeof(buf1)];
for(int i=0;i<3;++i) {
Utils::getSecureRandom(buf1,64);
std::cout << "[crypto] getSecureRandom: " << Utils::hex(buf1,64) << std::endl;
}
std::cout << "[crypto] Testing SHA-512... "; std::cout.flush();
SHA512::hash(buf1,sha512TV0Input,strlen(sha512TV0Input));
if (memcmp(buf1,sha512TV0Digest,64)) {
@ -119,17 +125,22 @@ static int testCrypto()
std::cout << "PASS" << std::endl;
std::cout << "[crypto] Testing C25519 ECC key agreement... "; std::cout.flush();
for(unsigned int i=0;i<50;++i) {
for(unsigned int i=0;i<100;++i) {
memset(buf1,64,sizeof(buf1));
memset(buf2,64,sizeof(buf2));
memset(buf3,64,sizeof(buf3));
C25519::Pair p1 = C25519::generate();
C25519::Pair p2 = C25519::generate();
C25519::Pair p3 = C25519::generate();
C25519::agree(p1,p2.pub,buf1,64);
C25519::agree(p2,p1.pub,buf2,64);
C25519::agree(p3,p1.pub,buf3,64);
// p1<>p2 should equal p1<>p2
if (memcmp(buf1,buf2,64)) {
std::cout << "FAIL (1)" << std::endl;
return -1;
}
// p2<>p1 should not equal p3<>p1
if (!memcmp(buf2,buf3,64)) {
std::cout << "FAIL (2)" << std::endl;
return -1;
@ -307,6 +318,81 @@ static int testIdentity()
return 0;
}
static int testCertificate()
{
Identity authority;
std::cout << "[certificate] Generating identity to act as authority... "; std::cout.flush();
authority.generate();
std::cout << authority.address().toString() << std::endl;
Identity idA,idB;
std::cout << "[certificate] Generating identities A and B... "; std::cout.flush();
idA.generate();
idB.generate();
std::cout << idA.address().toString() << ", " << idB.address().toString() << std::endl;
std::cout << "[certificate] Generating certificates A and B...";
CertificateOfMembership cA(10000,100,1,idA.address());
CertificateOfMembership cB(10099,100,1,idB.address());
std::cout << std::endl;
std::cout << "[certificate] Signing certificates A and B with authority...";
cA.sign(authority);
cB.sign(authority);
std::cout << std::endl;
//std::cout << "[certificate] A: " << cA.toString() << std::endl;
//std::cout << "[certificate] B: " << cB.toString() << std::endl;
std::cout << "[certificate] A agrees with B and B with A... ";
if (cA.agreesWith(cB))
std::cout << "yes, ";
else {
std::cout << "FAIL" << std::endl;
return -1;
}
if (cB.agreesWith(cA))
std::cout << "yes." << std::endl;
else {
std::cout << "FAIL" << std::endl;
return -1;
}
std::cout << "[certificate] Testing string serialization... ";
CertificateOfMembership copyA(cA.toString());
CertificateOfMembership copyB(cB.toString());
if (copyA != cA) {
std::cout << "FAIL" << std::endl;
return -1;
}
if (copyB != cB) {
std::cout << "FAIL" << std::endl;
return -1;
}
std::cout << "PASS" << std::endl;
std::cout << "[certificate] Generating two certificates that should not agree...";
cA = CertificateOfMembership(10000,100,1,idA.address());
cB = CertificateOfMembership(10101,100,1,idB.address());
std::cout << std::endl;
std::cout << "[certificate] A agrees with B and B with A... ";
if (!cA.agreesWith(cB))
std::cout << "no, ";
else {
std::cout << "FAIL" << std::endl;
return -1;
}
if (!cB.agreesWith(cA))
std::cout << "no." << std::endl;
else {
std::cout << "FAIL" << std::endl;
return -1;
}
return 0;
}
static int testPacket()
{
unsigned char salsaKey[32],hmacKey[32];
@ -480,6 +566,7 @@ int main(int argc,char **argv)
r |= testPacket();
r |= testOther();
r |= testIdentity();
r |= testCertificate();
if (r)
std::cout << std::endl << "SOMETHING FAILED!" << std::endl;

12
tap-mac/tuntap/Makefile Normal file
View File

@ -0,0 +1,12 @@
TUNTAP_VERSION = 20131028
BASE=
all: tap.kext
clean:
cd src/tap && make -f Makefile clean
tap.kext:
cd src/tap && make TUNTAP_VERSION=$(TUNTAP_VERSION) -f Makefile all
.PHONY: test

View File

@ -0,0 +1,18 @@
Building the tap for both x86_64 and i386 requires an older version of the
Xcode tools than what now ships for Mavericks (10.9). The newer version
does not support creating i386 kernel images.
These can be obtained from:
https://developer.apple.com/downloads
It requires a bit of a dance to unpack the package and obtain an unpacked
tree, but once it's there you can change the line in tap/Makefile and
build for both architectures.
This will go on until i386 is thoroughly legacy, at which point we'll
probably start just supporting x86_64. But that might be a while. We want
to support old Macs through their entire useful life.
Since this build is irritating, a pre-built copy is packaged in ext/ and
is installed by 'make install'. So users shouldn't have to build this.

View File

@ -19,7 +19,7 @@ BUNDLE_SIGNATURE = ????
BUNDLE_PACKAGETYPE = KEXT
BUNDLE_VERSION = $(TAP_KEXT_VERSION)
INCLUDE = -I.. -I/System/Library/Frameworks/Kernel.framework/Headers
INCLUDE = -I.. -I/System/Library/Frameworks/Kernel.framework/Headers -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk/System/Library/Frameworks/Kernel.framework/Headers
CFLAGS = -Wall -mkernel -force_cpusubtype_ALL \
-fno-builtin -fno-stack-protector -arch i386 -arch x86_64 \
-DKERNEL -D__APPLE__ -DKERNEL_PRIVATE -DTUNTAP_VERSION=\"$(TUNTAP_VERSION)\" \
@ -27,8 +27,10 @@ CFLAGS = -Wall -mkernel -force_cpusubtype_ALL \
CCFLAGS = $(CFLAGS)
LDFLAGS = -Wall -mkernel -nostdlib -r -lcc_kext -arch i386 -arch x86_64 -Xlinker -kext
CCP = $(shell if [ -f /usr/bin/llvm-g++ ]; then echo llvm-g++; else echo g++; fi)
CC = gcc
#CCP = g++
#CC = gcc
CCP = $(HOME)/Code/llvm-g++-Xcode4.6.2/bin/llvm-g++
CC = $(HOME)/Code/llvm-g++-Xcode4.6.2/bin/llvm-gcc
all: $(KMOD_BIN) bundle

View File

@ -34,7 +34,6 @@
#define TAP_IF_COUNT 16 /* max number of tap interfaces */
// ZeroTier default MTU, see Constants.hpp
#define TAP_MTU 2800
#define TAP_LLADDR tap_lladdr

View File

@ -648,7 +648,9 @@ tuntap_interface::cdev_write(uio_t uio, int ioflag)
mb = first;
while (uio_resid(uio) > 0) {
/* copy a chunk. enforce mtu (don't know if this is correct behaviour) */
chunk_len = min(ifnet_mtu(ifp), min(uio_resid(uio), mlen));
// ... evidently not :) -- Adam Ierymenko <adam.ierymenko@zerotier.com>
//chunk_len = min(ifnet_mtu(ifp), min(uio_resid(uio), mlen));
chunk_len = min(uio_resid(uio),mlen);
error = uiomove((caddr_t) mbuf_data(mb), chunk_len, uio);
if (error) {
log(LOG_ERR, "tuntap: could not copy data from userspace: %d\n", error);
@ -664,7 +666,9 @@ tuntap_interface::cdev_write(uio_t uio, int ioflag)
copied += chunk_len;
/* if done, break the loop */
if (uio_resid(uio) <= 0 || copied >= ifnet_mtu(ifp))
//if (uio_resid(uio) <= 0 || copied >= ifnet_mtu(ifp))
// break;
if (uio_resid(uio) <= 0)
break;
/* allocate a new mbuf if the current is filled */

View File

@ -41,6 +41,6 @@
/**
* Revision: 16-bit (0-65535)
*/
#define ZEROTIER_ONE_VERSION_REVISION 0
#define ZEROTIER_ONE_VERSION_REVISION 2
#endif