mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-24 18:25:20 +00:00
Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
d92da40bff | |||
5750cf6b72 | |||
7015017686 | |||
12b297a712 | |||
d290306bb3 | |||
60ac1b77c5 | |||
e514fe2bff | |||
148619f0ba | |||
17778a36ba | |||
df28cd88b8 | |||
942cc0ca21 | |||
010616e3ae | |||
1505e8dd50 | |||
5901972958 | |||
bbcd76ecd0 | |||
3de76fcab1 | |||
d496304bbf | |||
719dd2870d | |||
2f00ae4fd7 | |||
5e71e07f59 | |||
40e4f39181 | |||
6e217dfcb0 |
16
BUILDING.txt
16
BUILDING.txt
@ -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.
|
||||
|
@ -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
|
||||
|
17
Makefile.mac
17
Makefile.mac
@ -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:
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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>
|
BIN
ext/bin/tap-mac/tap.kext/Contents/MacOS/tap
Executable file
BIN
ext/bin/tap-mac/tap.kext/Contents/MacOS/tap
Executable file
Binary file not shown.
@ -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
|
@ -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.
|
@ -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
|
@ -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
|
||||
|
@ -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>.
|
||||
|
@ -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
|
@ -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.
|
||||
|
@ -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.}
|
@ -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¤cy_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 |
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -1 +0,0 @@
|
||||
<pkg-contents spec="1.12"/>
|
@ -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>
|
@ -1 +0,0 @@
|
||||
<pkg-contents spec="1.12"/>
|
@ -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>
|
@ -1 +0,0 @@
|
||||
<pkg-contents spec="1.12"/>
|
@ -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>
|
@ -1 +0,0 @@
|
||||
<pkg-contents spec="1.12"/>
|
@ -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>
|
@ -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>
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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__ */
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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__ */
|
||||
|
@ -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.
|
@ -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)
|
@ -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
|
@ -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
|
@ -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
|
@ -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))
|
@ -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
|
||||
|
@ -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))
|
@ -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])
|
||||
|
||||
|
@ -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)
|
@ -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)
|
@ -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
|
@ -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)
|
@ -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()
|
@ -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)
|
@ -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
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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)));
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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++];
|
||||
|
@ -1,5 +1,4 @@
|
||||
OBJS=\
|
||||
ext/kissdb/kissdb.o \
|
||||
ext/lz4/lz4hc.o \
|
||||
ext/lz4/lz4.o \
|
||||
node/C25519.o \
|
||||
|
89
selftest.cpp
89
selftest.cpp
@ -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
12
tap-mac/tuntap/Makefile
Normal file
@ -0,0 +1,12 @@
|
||||
TUNTAP_VERSION = 20131028
|
||||
BASE=
|
||||
|
||||
all: tap.kext
|
||||
|
||||
clean:
|
||||
cd src/tap && make -f Makefile clean
|
||||
|
||||
tap.kext:
|
||||
cd src/tap && make TUNTAP_VERSION=$(TUNTAP_VERSION) -f Makefile all
|
||||
|
||||
.PHONY: test
|
18
tap-mac/tuntap/README.zerotier-build
Normal file
18
tap-mac/tuntap/README.zerotier-build
Normal file
@ -0,0 +1,18 @@
|
||||
Building the tap for both x86_64 and i386 requires an older version of the
|
||||
Xcode tools than what now ships for Mavericks (10.9). The newer version
|
||||
does not support creating i386 kernel images.
|
||||
|
||||
These can be obtained from:
|
||||
|
||||
https://developer.apple.com/downloads
|
||||
|
||||
It requires a bit of a dance to unpack the package and obtain an unpacked
|
||||
tree, but once it's there you can change the line in tap/Makefile and
|
||||
build for both architectures.
|
||||
|
||||
This will go on until i386 is thoroughly legacy, at which point we'll
|
||||
probably start just supporting x86_64. But that might be a while. We want
|
||||
to support old Macs through their entire useful life.
|
||||
|
||||
Since this build is irritating, a pre-built copy is packaged in ext/ and
|
||||
is installed by 'make install'. So users shouldn't have to build this.
|
@ -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
|
||||
|
@ -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
|
@ -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 */
|
Reference in New Issue
Block a user