mirror of
https://github.com/ZeroTier/ZeroTierOne
synced 2025-08-14 10:37:33 -07:00
Upgrade LZ4, remove extraneous files, put tap-mac into ext/ to declutter root.
This commit is contained in:
parent
9455b1cc81
commit
6b8c90bffd
33 changed files with 815 additions and 1952 deletions
477
ext/lz4/bench.c
477
ext/lz4/bench.c
|
@ -1,477 +0,0 @@
|
|||
/*
|
||||
bench.c - Demo program to benchmark open-source compression algorithm
|
||||
Copyright (C) Yann Collet 2012
|
||||
GPL v2 License
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
You can contact the author at :
|
||||
- LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html
|
||||
- LZ4 source repository : http://code.google.com/p/lz4/
|
||||
*/
|
||||
|
||||
//**************************************
|
||||
// Compiler Options
|
||||
//**************************************
|
||||
// Disable some Visual warning messages
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#define _CRT_SECURE_NO_DEPRECATE // VS2005
|
||||
|
||||
// Unix Large Files support (>4GB)
|
||||
#if (defined(__sun__) && (!defined(__LP64__))) // Sun Solaris 32-bits requires specific definitions
|
||||
# define _LARGEFILE_SOURCE
|
||||
# define FILE_OFFSET_BITS=64
|
||||
#elif ! defined(__LP64__) // No point defining Large file for 64 bit
|
||||
# define _LARGEFILE64_SOURCE
|
||||
#endif
|
||||
|
||||
// S_ISREG & gettimeofday() are not supported by MSVC
|
||||
#if defined(_MSC_VER)
|
||||
# define S_ISREG(x) (((x) & S_IFMT) == S_IFREG)
|
||||
# define BMK_LEGACY_TIMER 1
|
||||
#endif
|
||||
|
||||
// GCC does not support _rotl outside of Windows
|
||||
#if !defined(_WIN32)
|
||||
# define _rotl(x,r) ((x << r) | (x >> (32 - r)))
|
||||
#endif
|
||||
|
||||
|
||||
//**************************************
|
||||
// Includes
|
||||
//**************************************
|
||||
#include <stdlib.h> // malloc
|
||||
#include <stdio.h> // fprintf, fopen, ftello64
|
||||
#include <sys/types.h> // stat64
|
||||
#include <sys/stat.h> // stat64
|
||||
|
||||
// Use ftime() if gettimeofday() is not available on your target
|
||||
#if defined(BMK_LEGACY_TIMER)
|
||||
# include <sys/timeb.h> // timeb, ftime
|
||||
#else
|
||||
# include <sys/time.h> // gettimeofday
|
||||
#endif
|
||||
|
||||
#include "lz4.h"
|
||||
#define COMPRESSOR0 LZ4_compress
|
||||
#include "lz4hc.h"
|
||||
#define COMPRESSOR1 LZ4_compressHC
|
||||
#define DEFAULTCOMPRESSOR LZ4_compress
|
||||
|
||||
|
||||
|
||||
//**************************************
|
||||
// Basic Types
|
||||
//**************************************
|
||||
#if defined(_MSC_VER) // Visual Studio does not support 'stdint' natively
|
||||
#define BYTE unsigned __int8
|
||||
#define U16 unsigned __int16
|
||||
#define U32 unsigned __int32
|
||||
#define S32 __int32
|
||||
#define U64 unsigned __int64
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#define BYTE uint8_t
|
||||
#define U16 uint16_t
|
||||
#define U32 uint32_t
|
||||
#define S32 int32_t
|
||||
#define U64 uint64_t
|
||||
#endif
|
||||
|
||||
|
||||
//**************************************
|
||||
// Constants
|
||||
//**************************************
|
||||
#define NBLOOPS 3
|
||||
#define TIMELOOP 2000
|
||||
|
||||
#define KNUTH 2654435761U
|
||||
#define MAX_MEM (1984<<20)
|
||||
#define DEFAULT_CHUNKSIZE (8<<20)
|
||||
|
||||
|
||||
//**************************************
|
||||
// Local structures
|
||||
//**************************************
|
||||
struct chunkParameters
|
||||
{
|
||||
U32 id;
|
||||
char* inputBuffer;
|
||||
char* outputBuffer;
|
||||
int inputSize;
|
||||
int outputSize;
|
||||
};
|
||||
|
||||
struct compressionParameters
|
||||
{
|
||||
int (*compressionFunction)(const char*, char*, int);
|
||||
int (*decompressionFunction)(const char*, char*, int);
|
||||
};
|
||||
|
||||
|
||||
//**************************************
|
||||
// MACRO
|
||||
//**************************************
|
||||
#define DISPLAY(...) fprintf(stderr, __VA_ARGS__)
|
||||
|
||||
|
||||
|
||||
//**************************************
|
||||
// Benchmark Parameters
|
||||
//**************************************
|
||||
static int chunkSize = DEFAULT_CHUNKSIZE;
|
||||
static int nbIterations = NBLOOPS;
|
||||
static int BMK_pause = 0;
|
||||
|
||||
void BMK_SetBlocksize(int bsize)
|
||||
{
|
||||
chunkSize = bsize;
|
||||
DISPLAY("-Using Block Size of %i KB-", chunkSize>>10);
|
||||
}
|
||||
|
||||
void BMK_SetNbIterations(int nbLoops)
|
||||
{
|
||||
nbIterations = nbLoops;
|
||||
DISPLAY("- %i iterations-", nbIterations);
|
||||
}
|
||||
|
||||
void BMK_SetPause()
|
||||
{
|
||||
BMK_pause = 1;
|
||||
}
|
||||
|
||||
//*********************************************************
|
||||
// Private functions
|
||||
//*********************************************************
|
||||
|
||||
#if defined(BMK_LEGACY_TIMER)
|
||||
|
||||
static int BMK_GetMilliStart()
|
||||
{
|
||||
// Based on Legacy ftime()
|
||||
// Rolls over every ~ 12.1 days (0x100000/24/60/60)
|
||||
// Use GetMilliSpan to correct for rollover
|
||||
struct timeb tb;
|
||||
int nCount;
|
||||
ftime( &tb );
|
||||
nCount = (int) (tb.millitm + (tb.time & 0xfffff) * 1000);
|
||||
return nCount;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int BMK_GetMilliStart()
|
||||
{
|
||||
// Based on newer gettimeofday()
|
||||
// Use GetMilliSpan to correct for rollover
|
||||
struct timeval tv;
|
||||
int nCount;
|
||||
gettimeofday(&tv, NULL);
|
||||
nCount = (int) (tv.tv_usec/1000 + (tv.tv_sec & 0xfffff) * 1000);
|
||||
return nCount;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static int BMK_GetMilliSpan( int nTimeStart )
|
||||
{
|
||||
int nSpan = BMK_GetMilliStart() - nTimeStart;
|
||||
if ( nSpan < 0 )
|
||||
nSpan += 0x100000 * 1000;
|
||||
return nSpan;
|
||||
}
|
||||
|
||||
|
||||
static U32 BMK_checksum_MMH3A (char* buff, U32 length)
|
||||
{
|
||||
const BYTE* data = (const BYTE*)buff;
|
||||
const int nblocks = length >> 2;
|
||||
|
||||
U32 h1 = KNUTH;
|
||||
U32 c1 = 0xcc9e2d51;
|
||||
U32 c2 = 0x1b873593;
|
||||
|
||||
const U32* blocks = (const U32*)(data + nblocks*4);
|
||||
int i;
|
||||
|
||||
for(i = -nblocks; i; i++)
|
||||
{
|
||||
U32 k1 = blocks[i];
|
||||
|
||||
k1 *= c1;
|
||||
k1 = _rotl(k1,15);
|
||||
k1 *= c2;
|
||||
|
||||
h1 ^= k1;
|
||||
h1 = _rotl(h1,13);
|
||||
h1 = h1*5+0xe6546b64;
|
||||
}
|
||||
|
||||
{
|
||||
const BYTE* tail = (const BYTE*)(data + nblocks*4);
|
||||
U32 k1 = 0;
|
||||
|
||||
switch(length & 3)
|
||||
{
|
||||
case 3: k1 ^= tail[2] << 16;
|
||||
case 2: k1 ^= tail[1] << 8;
|
||||
case 1: k1 ^= tail[0];
|
||||
k1 *= c1; k1 = _rotl(k1,15); k1 *= c2; h1 ^= k1;
|
||||
};
|
||||
}
|
||||
|
||||
h1 ^= length;
|
||||
h1 ^= h1 >> 16;
|
||||
h1 *= 0x85ebca6b;
|
||||
h1 ^= h1 >> 13;
|
||||
h1 *= 0xc2b2ae35;
|
||||
h1 ^= h1 >> 16;
|
||||
|
||||
return h1;
|
||||
}
|
||||
|
||||
|
||||
static size_t BMK_findMaxMem(U64 requiredMem)
|
||||
{
|
||||
size_t step = (64U<<20); // 64 MB
|
||||
BYTE* testmem=NULL;
|
||||
|
||||
requiredMem = (((requiredMem >> 25) + 1) << 26);
|
||||
if (requiredMem > MAX_MEM) requiredMem = MAX_MEM;
|
||||
|
||||
requiredMem += 2*step;
|
||||
while (!testmem)
|
||||
{
|
||||
requiredMem -= step;
|
||||
testmem = malloc ((size_t)requiredMem);
|
||||
}
|
||||
|
||||
free (testmem);
|
||||
return (size_t) (requiredMem - step);
|
||||
}
|
||||
|
||||
|
||||
static U64 BMK_GetFileSize(char* infilename)
|
||||
{
|
||||
int r;
|
||||
#if defined(_MSC_VER)
|
||||
struct _stat64 statbuf;
|
||||
r = _stat64(infilename, &statbuf);
|
||||
#else
|
||||
struct stat statbuf;
|
||||
r = stat(infilename, &statbuf);
|
||||
#endif
|
||||
if (r || !S_ISREG(statbuf.st_mode)) return 0; // No good...
|
||||
return (U64)statbuf.st_size;
|
||||
}
|
||||
|
||||
|
||||
//*********************************************************
|
||||
// Public function
|
||||
//*********************************************************
|
||||
|
||||
int BMK_benchFile(char** fileNamesTable, int nbFiles, int cLevel)
|
||||
{
|
||||
int fileIdx=0;
|
||||
FILE* fileIn;
|
||||
char* infilename;
|
||||
U64 largefilesize;
|
||||
size_t benchedsize;
|
||||
int nbChunks;
|
||||
int maxCChunkSize;
|
||||
size_t readSize;
|
||||
char* in_buff;
|
||||
char* out_buff; int out_buff_size;
|
||||
struct chunkParameters* chunkP;
|
||||
U32 crcc, crcd=0;
|
||||
struct compressionParameters compP;
|
||||
|
||||
U64 totals = 0;
|
||||
U64 totalz = 0;
|
||||
double totalc = 0.;
|
||||
double totald = 0.;
|
||||
|
||||
|
||||
// Init
|
||||
switch (cLevel)
|
||||
{
|
||||
#ifdef COMPRESSOR0
|
||||
case 0 : compP.compressionFunction = COMPRESSOR0; break;
|
||||
#endif
|
||||
#ifdef COMPRESSOR1
|
||||
case 1 : compP.compressionFunction = COMPRESSOR1; break;
|
||||
#endif
|
||||
default : compP.compressionFunction = DEFAULTCOMPRESSOR;
|
||||
}
|
||||
compP.decompressionFunction = LZ4_uncompress;
|
||||
|
||||
// Loop for each file
|
||||
while (fileIdx<nbFiles)
|
||||
{
|
||||
// Check file existence
|
||||
infilename = fileNamesTable[fileIdx++];
|
||||
fileIn = fopen( infilename, "rb" );
|
||||
if (fileIn==NULL)
|
||||
{
|
||||
DISPLAY( "Pb opening %s\n", infilename);
|
||||
return 11;
|
||||
}
|
||||
|
||||
// Memory allocation & restrictions
|
||||
largefilesize = BMK_GetFileSize(infilename);
|
||||
benchedsize = (size_t) BMK_findMaxMem(largefilesize) / 2;
|
||||
if ((U64)benchedsize > largefilesize) benchedsize = (size_t)largefilesize;
|
||||
if (benchedsize < largefilesize)
|
||||
{
|
||||
DISPLAY("Not enough memory for '%s' full size; testing %i MB only...\n", infilename, (int)(benchedsize>>20));
|
||||
}
|
||||
|
||||
// Alloc
|
||||
chunkP = (struct chunkParameters*) malloc(((benchedsize / chunkSize)+1) * sizeof(struct chunkParameters));
|
||||
in_buff = malloc((size_t )benchedsize);
|
||||
nbChunks = (int) (benchedsize / chunkSize) + 1;
|
||||
maxCChunkSize = LZ4_compressBound(chunkSize);
|
||||
out_buff_size = nbChunks * maxCChunkSize;
|
||||
out_buff = malloc((size_t )out_buff_size);
|
||||
|
||||
|
||||
if(!in_buff || !out_buff)
|
||||
{
|
||||
DISPLAY("\nError: not enough memory!\n");
|
||||
free(in_buff);
|
||||
free(out_buff);
|
||||
fclose(fileIn);
|
||||
return 12;
|
||||
}
|
||||
|
||||
// Init chunks data
|
||||
{
|
||||
int i;
|
||||
size_t remaining = benchedsize;
|
||||
char* in = in_buff;
|
||||
char* out = out_buff;
|
||||
for (i=0; i<nbChunks; i++)
|
||||
{
|
||||
chunkP[i].id = i;
|
||||
chunkP[i].inputBuffer = in; in += chunkSize;
|
||||
if ((int)remaining > chunkSize) { chunkP[i].inputSize = chunkSize; remaining -= chunkSize; } else { chunkP[i].inputSize = (int)remaining; remaining = 0; }
|
||||
chunkP[i].outputBuffer = out; out += maxCChunkSize;
|
||||
chunkP[i].outputSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill input buffer
|
||||
DISPLAY("Loading %s... \r", infilename);
|
||||
readSize = fread(in_buff, 1, benchedsize, fileIn);
|
||||
fclose(fileIn);
|
||||
|
||||
if(readSize != benchedsize)
|
||||
{
|
||||
DISPLAY("\nError: problem reading file '%s' !! \n", infilename);
|
||||
free(in_buff);
|
||||
free(out_buff);
|
||||
return 13;
|
||||
}
|
||||
|
||||
// Calculating input Checksum
|
||||
crcc = BMK_checksum_MMH3A(in_buff, (unsigned int)benchedsize);
|
||||
|
||||
|
||||
// Bench
|
||||
{
|
||||
int loopNb, nb_loops, chunkNb;
|
||||
size_t cSize=0;
|
||||
int milliTime;
|
||||
double fastestC = 100000000., fastestD = 100000000.;
|
||||
double ratio=0.;
|
||||
|
||||
DISPLAY("\r%79s\r", "");
|
||||
for (loopNb = 1; loopNb <= nbIterations; loopNb++)
|
||||
{
|
||||
// Compression
|
||||
DISPLAY("%1i-%-14.14s : %9i ->\r", loopNb, infilename, (int)benchedsize);
|
||||
{ size_t i; for (i=0; i<benchedsize; i++) out_buff[i]=(char)i; } // warmimg up memory
|
||||
|
||||
nb_loops = 0;
|
||||
milliTime = BMK_GetMilliStart();
|
||||
while(BMK_GetMilliStart() == milliTime);
|
||||
milliTime = BMK_GetMilliStart();
|
||||
while(BMK_GetMilliSpan(milliTime) < TIMELOOP)
|
||||
{
|
||||
for (chunkNb=0; chunkNb<nbChunks; chunkNb++)
|
||||
chunkP[chunkNb].outputSize = compP.compressionFunction(chunkP[chunkNb].inputBuffer, chunkP[chunkNb].outputBuffer, chunkP[chunkNb].inputSize);
|
||||
nb_loops++;
|
||||
}
|
||||
milliTime = BMK_GetMilliSpan(milliTime);
|
||||
|
||||
if ((double)milliTime < fastestC*nb_loops) fastestC = (double)milliTime/nb_loops;
|
||||
cSize=0; for (chunkNb=0; chunkNb<nbChunks; chunkNb++) cSize += chunkP[chunkNb].outputSize;
|
||||
ratio = (double)cSize/(double)benchedsize*100.;
|
||||
|
||||
DISPLAY("%1i-%-14.14s : %9i -> %9i (%5.2f%%),%7.1f MB/s\r", loopNb, infilename, (int)benchedsize, (int)cSize, ratio, (double)benchedsize / fastestC / 1000.);
|
||||
|
||||
// Decompression
|
||||
{ size_t i; for (i=0; i<benchedsize; i++) in_buff[i]=0; } // zeroing area, for CRC checking
|
||||
|
||||
nb_loops = 0;
|
||||
milliTime = BMK_GetMilliStart();
|
||||
while(BMK_GetMilliStart() == milliTime);
|
||||
milliTime = BMK_GetMilliStart();
|
||||
while(BMK_GetMilliSpan(milliTime) < TIMELOOP)
|
||||
{
|
||||
for (chunkNb=0; chunkNb<nbChunks; chunkNb++)
|
||||
chunkP[chunkNb].outputSize = LZ4_uncompress(chunkP[chunkNb].outputBuffer, chunkP[chunkNb].inputBuffer, chunkP[chunkNb].inputSize);
|
||||
//chunkP[chunkNb].inputSize = LZ4_uncompress_unknownOutputSize(chunkP[chunkNb].outputBuffer, chunkP[chunkNb].inputBuffer, chunkP[chunkNb].outputSize, chunkSize);
|
||||
nb_loops++;
|
||||
}
|
||||
milliTime = BMK_GetMilliSpan(milliTime);
|
||||
|
||||
if ((double)milliTime < fastestD*nb_loops) fastestD = (double)milliTime/nb_loops;
|
||||
DISPLAY("%1i-%-14.14s : %9i -> %9i (%5.2f%%),%7.1f MB/s ,%7.1f MB/s\r", loopNb, infilename, (int)benchedsize, (int)cSize, ratio, (double)benchedsize / fastestC / 1000., (double)benchedsize / fastestD / 1000.);
|
||||
|
||||
// CRC Checking
|
||||
crcd = BMK_checksum_MMH3A(in_buff, (unsigned int)benchedsize);
|
||||
if (crcc!=crcd) { DISPLAY("\n!!! WARNING !!! %14s : Invalid Checksum : %x != %x\n", infilename, (unsigned)crcc, (unsigned)crcd); break; }
|
||||
}
|
||||
|
||||
if (crcc==crcd)
|
||||
{
|
||||
if (ratio<100.)
|
||||
DISPLAY("%-16.16s : %9i -> %9i (%5.2f%%),%7.1f MB/s ,%7.1f MB/s\n", infilename, (int)benchedsize, (int)cSize, ratio, (double)benchedsize / fastestC / 1000., (double)benchedsize / fastestD / 1000.);
|
||||
else
|
||||
DISPLAY("%-16.16s : %9i -> %9i (%5.1f%%),%7.1f MB/s ,%7.1f MB/s \n", infilename, (int)benchedsize, (int)cSize, ratio, (double)benchedsize / fastestC / 1000., (double)benchedsize / fastestD / 1000.);
|
||||
}
|
||||
totals += benchedsize;
|
||||
totalz += cSize;
|
||||
totalc += fastestC;
|
||||
totald += fastestD;
|
||||
}
|
||||
|
||||
free(in_buff);
|
||||
free(out_buff);
|
||||
free(chunkP);
|
||||
}
|
||||
|
||||
if (nbFiles > 1)
|
||||
printf("%-16.16s :%10llu ->%10llu (%5.2f%%), %6.1f MB/s , %6.1f MB/s\n", " TOTAL", (long long unsigned int)totals, (long long unsigned int)totalz, (double)totalz/(double)totals*100., (double)totals/totalc/1000., (double)totals/totald/1000.);
|
||||
|
||||
if (BMK_pause) { printf("press enter...\n"); getchar(); }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
bench.h - Demo program to benchmark open-source compression algorithm
|
||||
Copyright (C) Yann Collet 2012
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
You can contact the author at :
|
||||
- LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html
|
||||
- LZ4 source repository : http://code.google.com/p/lz4/
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#if defined (__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
int BMK_benchFile(char** fileNamesTable, int nbFiles, int cLevel);
|
||||
|
||||
// Parameters
|
||||
void BMK_SetBlocksize(int bsize);
|
||||
void BMK_SetNbIterations(int nbLoops);
|
||||
void BMK_SetPause();
|
||||
|
||||
|
||||
|
||||
#if defined (__cplusplus)
|
||||
}
|
||||
#endif
|
227
ext/lz4/fuzzer.c
227
ext/lz4/fuzzer.c
|
@ -1,227 +0,0 @@
|
|||
/*
|
||||
fuzzer.c - Fuzzer test tool for LZ4
|
||||
Copyright (C) Andrew Mahone - Yann Collet 2012
|
||||
Original code by Andrew Mahone / Modified by Yann Collet
|
||||
GPL v2 License
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
You can contact the author at :
|
||||
- LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html
|
||||
- LZ4 source repository : http://code.google.com/p/lz4/
|
||||
*/
|
||||
|
||||
//**************************************
|
||||
// Remove Visual warning messages
|
||||
//**************************************
|
||||
#define _CRT_SECURE_NO_WARNINGS // fgets
|
||||
|
||||
|
||||
//**************************************
|
||||
// Includes
|
||||
//**************************************
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h> // fgets, sscanf
|
||||
#include <sys/timeb.h> // timeb
|
||||
#include "lz4.h"
|
||||
|
||||
|
||||
//**************************************
|
||||
// Constants
|
||||
//**************************************
|
||||
#define NB_ATTEMPTS (1<<18)
|
||||
#define LEN ((1<<15))
|
||||
#define SEQ_POW 2
|
||||
#define NUM_SEQ (1 << SEQ_POW)
|
||||
#define SEQ_MSK ((NUM_SEQ) - 1)
|
||||
#define MOD_SEQ(x) ((((x) >> 8) & 255) == 0)
|
||||
#define NEW_SEQ(x) ((((x) >> 10) %10) == 0)
|
||||
#define PAGE_SIZE 4096
|
||||
#define ROUND_PAGE(x) (((x) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))
|
||||
#define PRIME1 2654435761U
|
||||
#define PRIME2 2246822519U
|
||||
#define PRIME3 3266489917U
|
||||
|
||||
|
||||
|
||||
//*********************************************************
|
||||
// Functions
|
||||
//*********************************************************
|
||||
static int FUZ_GetMilliStart()
|
||||
{
|
||||
struct timeb tb;
|
||||
int nCount;
|
||||
ftime( &tb );
|
||||
nCount = (int) (tb.millitm + (tb.time & 0xfffff) * 1000);
|
||||
return nCount;
|
||||
}
|
||||
|
||||
static int FUZ_GetMilliSpan( int nTimeStart )
|
||||
{
|
||||
int nSpan = FUZ_GetMilliStart() - nTimeStart;
|
||||
if ( nSpan < 0 )
|
||||
nSpan += 0x100000 * 1000;
|
||||
return nSpan;
|
||||
}
|
||||
|
||||
|
||||
unsigned int FUZ_rand(unsigned int* src)
|
||||
{
|
||||
*src = ((*src) * PRIME1) + PRIME2;
|
||||
return *src;
|
||||
}
|
||||
|
||||
|
||||
int test_canary(unsigned char *buf) {
|
||||
int i;
|
||||
for (i = 0; i < 2048; i++)
|
||||
if (buf[i] != buf[i + 2048])
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int FUZ_SecurityTest()
|
||||
{
|
||||
char* output;
|
||||
char* input;
|
||||
int i, r;
|
||||
|
||||
printf("Starting security tests...");
|
||||
input = (char*) malloc (20<<20);
|
||||
output = (char*) malloc (20<<20);
|
||||
input[0] = 0x0F;
|
||||
input[1] = 0x00;
|
||||
input[2] = 0x00;
|
||||
for(i = 3; i < 16840000; i++)
|
||||
input[i] = 0xff;
|
||||
r = LZ4_uncompress(input, output, 20<<20);
|
||||
|
||||
free(input);
|
||||
free(output);
|
||||
printf(" Completed (r=%i)\n",r);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//int main(int argc, char *argv[]) {
|
||||
int main() {
|
||||
unsigned long long bytes = 0;
|
||||
unsigned long long cbytes = 0;
|
||||
unsigned char buf[LEN];
|
||||
unsigned char testOut[LEN+1];
|
||||
# define FUZ_max LZ4_COMPRESSBOUND(LEN)
|
||||
# define FUZ_avail ROUND_PAGE(FUZ_max)
|
||||
const int off_full = FUZ_avail - FUZ_max;
|
||||
unsigned char cbuf[FUZ_avail + PAGE_SIZE];
|
||||
unsigned int seed, cur_seq=PRIME3, seeds[NUM_SEQ], timestamp=FUZ_GetMilliStart();
|
||||
int i, j, k, ret, len;
|
||||
char userInput[30] = {0};
|
||||
|
||||
printf("starting LZ4 fuzzer\n");
|
||||
printf("Select an Initialisation number (default : random) : ");
|
||||
fflush(stdout);
|
||||
if ( fgets(userInput, sizeof userInput, stdin) )
|
||||
{
|
||||
if ( sscanf(userInput, "%d", &seed) == 1 ) {}
|
||||
else seed = FUZ_GetMilliSpan(timestamp);
|
||||
}
|
||||
printf("Seed = %u\n", seed);
|
||||
|
||||
FUZ_SecurityTest();
|
||||
|
||||
for (i = 0; i < 2048; i++)
|
||||
cbuf[FUZ_avail + i] = cbuf[FUZ_avail + 2048 + i] = FUZ_rand(&seed) >> 16;
|
||||
|
||||
for (i = 0; i < NB_ATTEMPTS; i++)
|
||||
{
|
||||
printf("\r%7i /%7i\r", i, NB_ATTEMPTS);
|
||||
|
||||
FUZ_rand(&seed);
|
||||
for (j = 0; j < NUM_SEQ; j++) {
|
||||
seeds[j] = FUZ_rand(&seed) << 8;
|
||||
seeds[j] ^= (FUZ_rand(&seed) >> 8) & 65535;
|
||||
}
|
||||
for (j = 0; j < LEN; j++) {
|
||||
k = FUZ_rand(&seed);
|
||||
if (j == 0 || NEW_SEQ(k))
|
||||
cur_seq = seeds[(FUZ_rand(&seed) >> 16) & SEQ_MSK];
|
||||
if (MOD_SEQ(k)) {
|
||||
k = (FUZ_rand(&seed) >> 16) & SEQ_MSK;
|
||||
seeds[k] = FUZ_rand(&seed) << 8;
|
||||
seeds[k] ^= (FUZ_rand(&seed) >> 8) & 65535;
|
||||
}
|
||||
buf[j] = FUZ_rand(&cur_seq) >> 16;
|
||||
}
|
||||
|
||||
// Test compression
|
||||
ret = LZ4_compress_limitedOutput((const char*)buf, (char*)&cbuf[off_full], LEN, FUZ_max);
|
||||
if (ret == 0) { printf("compression failed despite sufficient space: seed %u, len %d\n", seed, LEN); goto _output_error; }
|
||||
len = ret;
|
||||
|
||||
// Test decoding with output size being exactly what's necessary => must work
|
||||
ret = LZ4_uncompress((char*)&cbuf[off_full], (char*)testOut, LEN);
|
||||
if (ret<0) { printf("decompression failed despite correct space: seed %u, len %d\n", seed, LEN); goto _output_error; }
|
||||
|
||||
// Test decoding with one byte missing => must fail
|
||||
ret = LZ4_uncompress((char*)&cbuf[off_full], (char*)testOut, LEN-1);
|
||||
if (ret>=0) { printf("decompression should have failed, due to Output Size being too small : seed %u, len %d\n", seed, LEN); goto _output_error; }
|
||||
|
||||
// Test decoding with one byte too much => must fail
|
||||
ret = LZ4_uncompress((char*)&cbuf[off_full], (char*)testOut, LEN+1);
|
||||
if (ret>=0) { printf("decompression should have failed, due to Output Size being too large : seed %u, len %d\n", seed, LEN); goto _output_error; }
|
||||
|
||||
// Test decoding with enough output size => must work
|
||||
ret = LZ4_uncompress_unknownOutputSize((char*)&cbuf[off_full], (char*)testOut, len, LEN+1);
|
||||
if (ret<0) { printf("decompression failed despite sufficient space: seed %u, len %d\n", seed, LEN); goto _output_error; }
|
||||
|
||||
// Test decoding with output size being exactly what's necessary => must work
|
||||
ret = LZ4_uncompress_unknownOutputSize((char*)&cbuf[off_full], (char*)testOut, len, LEN);
|
||||
if (ret<0) { printf("decompression failed despite sufficient space: seed %u, len %d\n", seed, LEN); goto _output_error; }
|
||||
|
||||
// Test decoding with output size being one byte too short => must fail
|
||||
ret = LZ4_uncompress_unknownOutputSize((char*)&cbuf[off_full], (char*)testOut, len, LEN-1);
|
||||
if (ret>=0) { printf("decompression should have failed, due to Output Size being too small : seed %u, len %d\n", seed, LEN); goto _output_error; }
|
||||
|
||||
// Test decoding with input size being one byte too short => must fail
|
||||
ret = LZ4_uncompress_unknownOutputSize((char*)&cbuf[off_full], (char*)testOut, len-1, LEN);
|
||||
if (ret>=0) { printf("decompression should have failed, due to input size being too small : seed %u, len %d\n", seed, LEN); goto _output_error; }
|
||||
|
||||
// Test decoding with input size being one byte too large => must fail
|
||||
ret = LZ4_uncompress_unknownOutputSize((char*)&cbuf[off_full], (char*)testOut, len+1, LEN);
|
||||
if (ret>=0) { printf("decompression should have failed, due to input size being too large : seed %u, len %d\n", seed, LEN); goto _output_error; }
|
||||
|
||||
// Test compression with output size being exactly what's necessary (should work)
|
||||
ret = LZ4_compress_limitedOutput((const char*)buf, (char*)&cbuf[FUZ_avail-len], LEN, len);
|
||||
if (!test_canary(&cbuf[FUZ_avail])) { printf("compression overran output buffer: seed %u, len %d, olen %d\n", seed, LEN, len); goto _output_error; }
|
||||
if (ret == 0) { printf("compression failed despite sufficient space: seed %u, len %d\n", seed, LEN); goto _output_error; }
|
||||
|
||||
// Test compression with just one missing byte into output buffer => must fail
|
||||
ret = LZ4_compress_limitedOutput((const char*)buf, (char*)&cbuf[FUZ_avail-(len-1)], LEN, len-1);
|
||||
if (ret) { printf("compression overran output buffer: seed %u, len %d, olen %d => ret %d", seed, LEN, len-1, ret); goto _output_error; }
|
||||
if (!test_canary(&cbuf[FUZ_avail])) { printf("compression overran output buffer: seed %u, len %d, olen %d", seed, LEN, len-1); goto _output_error; }
|
||||
|
||||
bytes += LEN;
|
||||
cbytes += len;
|
||||
}
|
||||
|
||||
printf("all tests completed successfully \n");
|
||||
printf("compression ratio: %0.3f%%\n", (double)cbytes/bytes*100);
|
||||
getchar();
|
||||
return 0;
|
||||
|
||||
_output_error:
|
||||
getchar();
|
||||
return 1;
|
||||
}
|
932
ext/lz4/lz4.c
932
ext/lz4/lz4.c
File diff suppressed because it is too large
Load diff
145
ext/lz4/lz4.h
145
ext/lz4/lz4.h
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
LZ4 - Fast LZ compression algorithm
|
||||
Header File
|
||||
Copyright (C) 2011-2012, Yann Collet.
|
||||
Copyright (C) 2011-2013, Yann Collet.
|
||||
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -42,7 +42,7 @@ extern "C" {
|
|||
// Compiler Options
|
||||
//**************************************
|
||||
#if defined(_MSC_VER) && !defined(__cplusplus) // Visual Studio
|
||||
# define inline __inline // Visual is not C99, but supports some kind of inline
|
||||
# define inline __inline // Visual C is not C99, but supports some kind of inline
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -50,78 +50,155 @@ extern "C" {
|
|||
// Simple Functions
|
||||
//****************************
|
||||
|
||||
int LZ4_compress (const char* source, char* dest, int isize);
|
||||
int LZ4_uncompress (const char* source, char* dest, int osize);
|
||||
int LZ4_compress (const char* source, char* dest, int inputSize);
|
||||
int LZ4_decompress_safe (const char* source, char* dest, int inputSize, int maxOutputSize);
|
||||
|
||||
/*
|
||||
LZ4_compress() :
|
||||
Compresses 'isize' bytes from 'source' into 'dest'.
|
||||
Compresses 'inputSize' bytes from 'source' into 'dest'.
|
||||
Destination buffer must be already allocated,
|
||||
and must be sized to handle worst cases situations (input data not compressible)
|
||||
Worst case size evaluation is provided by function LZ4_compressBound()
|
||||
|
||||
isize : is the input size. Max supported value is ~1.9GB
|
||||
inputSize : Max supported value is LZ4_MAX_INPUT_VALUE
|
||||
return : the number of bytes written in buffer dest
|
||||
or 0 if the compression fails
|
||||
|
||||
|
||||
LZ4_uncompress() :
|
||||
osize : is the output size, therefore the original size
|
||||
return : the number of bytes read in the source buffer
|
||||
If the source stream is malformed, the function will stop decoding and return a negative result, indicating the byte position of the faulty instruction
|
||||
This function never writes outside of provided buffers, and never modifies input buffer.
|
||||
note : destination buffer must be already allocated.
|
||||
its size must be a minimum of 'osize' bytes.
|
||||
LZ4_decompress_safe() :
|
||||
maxOutputSize : is the size of the destination buffer (which must be already allocated)
|
||||
return : the number of bytes decoded in the destination buffer (necessarily <= maxOutputSize)
|
||||
If the source stream is detected malformed, the function will stop decoding and return a negative result.
|
||||
This function is protected against buffer overflow exploits (never writes outside of output buffer, and never reads outside of input buffer). Therefore, it is protected against malicious data packets
|
||||
*/
|
||||
|
||||
|
||||
//****************************
|
||||
// Advanced Functions
|
||||
//****************************
|
||||
|
||||
static inline int LZ4_compressBound(int isize) { return ((isize) + ((isize)/255) + 16); }
|
||||
#define LZ4_COMPRESSBOUND( isize) ((isize) + ((isize)/255) + 16)
|
||||
#define LZ4_MAX_INPUT_SIZE 0x7E000000 // 2 113 929 216 bytes
|
||||
#define LZ4_COMPRESSBOUND(isize) ((unsigned int)(isize) > (unsigned int)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)
|
||||
static inline int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); }
|
||||
|
||||
/*
|
||||
LZ4_compressBound() :
|
||||
Provides the maximum size that LZ4 may output in a "worst case" scenario (input data not compressible)
|
||||
primarily useful for memory allocation of output buffer.
|
||||
inline function is recommended for the general case,
|
||||
but macro is also provided when results need to be evaluated at compile time (such as table size allocation).
|
||||
inline function is recommended for the general case,
|
||||
macro is also provided when result needs to be evaluated at compilation (such as stack memory allocation).
|
||||
|
||||
isize : is the input size. Max supported value is ~1.9GB
|
||||
isize : is the input size. Max supported value is LZ4_MAX_INPUT_SIZE
|
||||
return : maximum output size in a "worst case" scenario
|
||||
note : this function is limited by "int" range (2^31-1)
|
||||
or 0, if input size is too large ( > LZ4_MAX_INPUT_SIZE)
|
||||
*/
|
||||
|
||||
|
||||
int LZ4_compress_limitedOutput (const char* source, char* dest, int isize, int maxOutputSize);
|
||||
int LZ4_compress_limitedOutput (const char* source, char* dest, int inputSize, int maxOutputSize);
|
||||
|
||||
/*
|
||||
LZ4_compress_limitedOutput() :
|
||||
Compress 'isize' bytes from 'source' into an output buffer 'dest' of maximum size 'maxOutputSize'.
|
||||
Compress 'inputSize' bytes from 'source' into an output buffer 'dest' of maximum size 'maxOutputSize'.
|
||||
If it cannot achieve it, compression will stop, and result of the function will be zero.
|
||||
This function never writes outside of provided output buffer.
|
||||
|
||||
isize : is the input size. Max supported value is ~1.9GB
|
||||
inputSize : Max supported value is LZ4_MAX_INPUT_VALUE
|
||||
maxOutputSize : is the size of the destination buffer (which must be already allocated)
|
||||
return : the number of bytes written in buffer 'dest'
|
||||
or 0 if the compression fails
|
||||
*/
|
||||
|
||||
|
||||
int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize);
|
||||
int LZ4_decompress_fast (const char* source, char* dest, int outputSize);
|
||||
|
||||
/*
|
||||
LZ4_uncompress_unknownOutputSize() :
|
||||
isize : is the input size, therefore the compressed size
|
||||
maxOutputSize : is the size of the destination buffer (which must be already allocated)
|
||||
return : the number of bytes decoded in the destination buffer (necessarily <= maxOutputSize)
|
||||
If the source stream is malformed, the function will stop decoding and return a negative result, indicating the byte position of the faulty instruction
|
||||
This function never writes beyond dest + maxOutputSize, and is therefore protected against malicious data packets
|
||||
note : Destination buffer must be already allocated.
|
||||
This version is slightly slower than LZ4_uncompress()
|
||||
LZ4_decompress_fast() :
|
||||
outputSize : is the original (uncompressed) size
|
||||
return : the number of bytes read from the source buffer (in other words, the compressed size)
|
||||
If the source stream is malformed, the function will stop decoding and return a negative result.
|
||||
note : This function is a bit faster than LZ4_decompress_safe()
|
||||
This function never writes outside of output buffers, but may read beyond input buffer in case of malicious data packet.
|
||||
Use this function preferably into a trusted environment (data to decode comes from a trusted source).
|
||||
Destination buffer must be already allocated. Its size must be a minimum of 'outputSize' bytes.
|
||||
*/
|
||||
|
||||
int LZ4_decompress_safe_partial (const char* source, char* dest, int inputSize, int targetOutputSize, int maxOutputSize);
|
||||
|
||||
/*
|
||||
LZ4_decompress_safe_partial() :
|
||||
This function decompress a compressed block of size 'inputSize' at position 'source'
|
||||
into output buffer 'dest' of size 'maxOutputSize'.
|
||||
The function tries to stop decompressing operation as soon as 'targetOutputSize' has been reached,
|
||||
reducing decompression time.
|
||||
return : the number of bytes decoded in the destination buffer (necessarily <= maxOutputSize)
|
||||
Note : this number can be < 'targetOutputSize' should the compressed block to decode be smaller.
|
||||
Always control how many bytes were decoded.
|
||||
If the source stream is detected malformed, the function will stop decoding and return a negative result.
|
||||
This function never writes outside of output buffer, and never reads outside of input buffer. It is therefore protected against malicious data packets
|
||||
*/
|
||||
|
||||
|
||||
//****************************
|
||||
// Stream Functions
|
||||
//****************************
|
||||
|
||||
void* LZ4_create (const char* inputBuffer);
|
||||
int LZ4_compress_continue (void* LZ4_Data, const char* source, char* dest, int inputSize);
|
||||
int LZ4_compress_limitedOutput_continue (void* LZ4_Data, const char* source, char* dest, int inputSize, int maxOutputSize);
|
||||
char* LZ4_slideInputBuffer (void* LZ4_Data);
|
||||
int LZ4_free (void* LZ4_Data);
|
||||
|
||||
/*
|
||||
These functions allow the compression of dependent blocks, where each block benefits from prior 64 KB within preceding blocks.
|
||||
In order to achieve this, it is necessary to start creating the LZ4 Data Structure, thanks to the function :
|
||||
|
||||
void* LZ4_create (const char* inputBuffer);
|
||||
The result of the function is the (void*) pointer on the LZ4 Data Structure.
|
||||
This pointer will be needed in all other functions.
|
||||
If the pointer returned is NULL, then the allocation has failed, and compression must be aborted.
|
||||
The only parameter 'const char* inputBuffer' must, obviously, point at the beginning of input buffer.
|
||||
The input buffer must be already allocated, and size at least 192KB.
|
||||
'inputBuffer' will also be the 'const char* source' of the first block.
|
||||
|
||||
All blocks are expected to lay next to each other within the input buffer, starting from 'inputBuffer'.
|
||||
To compress each block, use either LZ4_compress_continue() or LZ4_compress_limitedOutput_continue().
|
||||
Their behavior are identical to LZ4_compress() or LZ4_compress_limitedOutput(),
|
||||
but require the LZ4 Data Structure as their first argument, and check that each block starts right after the previous one.
|
||||
If next block does not begin immediately after the previous one, the compression will fail (return 0).
|
||||
|
||||
When it's no longer possible to lay the next block after the previous one (not enough space left into input buffer), a call to :
|
||||
char* LZ4_slideInputBuffer(void* LZ4_Data);
|
||||
must be performed. It will typically copy the latest 64KB of input at the beginning of input buffer.
|
||||
Note that, for this function to work properly, minimum size of an input buffer must be 192KB.
|
||||
==> The memory position where the next input data block must start is provided as the result of the function.
|
||||
|
||||
Compression can then resume, using LZ4_compress_continue() or LZ4_compress_limitedOutput_continue(), as usual.
|
||||
|
||||
When compression is completed, a call to LZ4_free() will release the memory used by the LZ4 Data Structure.
|
||||
*/
|
||||
|
||||
|
||||
int LZ4_decompress_safe_withPrefix64k (const char* source, char* dest, int inputSize, int maxOutputSize);
|
||||
int LZ4_decompress_fast_withPrefix64k (const char* source, char* dest, int outputSize);
|
||||
|
||||
/*
|
||||
*_withPrefix64k() :
|
||||
These decoding functions work the same as their "normal name" versions,
|
||||
but can use up to 64KB of data in front of 'char* dest'.
|
||||
These functions are necessary to decode inter-dependant blocks.
|
||||
*/
|
||||
|
||||
|
||||
//****************************
|
||||
// Obsolete Functions
|
||||
//****************************
|
||||
|
||||
static inline int LZ4_uncompress (const char* source, char* dest, int outputSize) { return LZ4_decompress_fast(source, dest, outputSize); }
|
||||
static inline int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize) { return LZ4_decompress_safe(source, dest, isize, maxOutputSize); }
|
||||
|
||||
/*
|
||||
These functions are deprecated and should no longer be used.
|
||||
They are provided here for compatibility with existing user programs.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#if defined (__cplusplus)
|
||||
}
|
||||
|
|
|
@ -1,121 +0,0 @@
|
|||
LZ4 Format Description
|
||||
Last revised: 2012-02-27
|
||||
Author : Y. Collet
|
||||
|
||||
|
||||
|
||||
This small specification intents to provide enough information
|
||||
to anyone willing to produce LZ4-compatible compressed streams
|
||||
using any programming language.
|
||||
|
||||
LZ4 is an LZ77-type compressor with a fixed, byte-oriented encoding.
|
||||
The most important design principle behind LZ4 is simplicity.
|
||||
It helps to create an easy to read and maintain source code.
|
||||
It also helps later on for optimisations, compactness, and speed.
|
||||
There is no entropy encoder backend nor framing layer.
|
||||
The latter is assumed to be handled by other parts of the system.
|
||||
|
||||
This document only describes the format,
|
||||
not how the LZ4 compressor nor decompressor actually work.
|
||||
The correctness of the decompressor should not depend
|
||||
on implementation details of the compressor, and vice versa.
|
||||
|
||||
|
||||
|
||||
-- Compressed stream format --
|
||||
|
||||
An LZ4 compressed stream is composed of sequences.
|
||||
Schematically, a sequence is a suite of literals, followed by a match copy.
|
||||
|
||||
Each sequence starts with a token.
|
||||
The token is a one byte value, separated into two 4-bits fields.
|
||||
Therefore each field ranges from 0 to 15.
|
||||
|
||||
|
||||
The first field uses the 4 high-bits of the token.
|
||||
It provides the length of literals to follow.
|
||||
(Note : a literal is a not-compressed byte).
|
||||
If the field value is 0, then there is no literal.
|
||||
If it is 15, then we need to add some more bytes to indicate the full length.
|
||||
Each additionnal byte then represent a value from 0 to 255,
|
||||
which is added to the previous value to produce a total length.
|
||||
When the byte value is 255, another byte is output.
|
||||
There can be any number of bytes following the token. There is no "size limit".
|
||||
(Sidenote this is why a not-compressible input stream is expanded by 0.4%).
|
||||
|
||||
Example 1 : A length of 48 will be represented as :
|
||||
- 15 : value for the 4-bits High field
|
||||
- 33 : (=48-15) remaining length to reach 48
|
||||
|
||||
Example 2 : A length of 280 will be represented as :
|
||||
- 15 : value for the 4-bits High field
|
||||
- 255 : following byte is maxed, since 280-15 >= 255
|
||||
- 10 : (=280 - 15 - 255) ) remaining length to reach 280
|
||||
|
||||
Example 3 : A length of 15 will be represented as :
|
||||
- 15 : value for the 4-bits High field
|
||||
- 0 : (=15-15) yes, the zero must be output
|
||||
|
||||
Following the token and optional length bytes, are the literals themselves.
|
||||
They are exactly as numerous as previously decoded (length of literals).
|
||||
It's possible that there are zero literal.
|
||||
|
||||
|
||||
Following the literals is the match copy operation.
|
||||
|
||||
It starts by the offset.
|
||||
This is a 2 bytes value, in little endian format :
|
||||
the lower byte is the first one in the stream.
|
||||
|
||||
The offset represents the position of the match to be copied from.
|
||||
1 means "current position - 1 byte".
|
||||
The maximum offset value is 65535, 65536 cannot be coded.
|
||||
Note that 0 is an invalid value, not used.
|
||||
|
||||
Then we need to extract the match length.
|
||||
For this, we use the second token field, the low 4-bits.
|
||||
Value, obviously, ranges from 0 to 15.
|
||||
However here, 0 means that the copy operation will be minimal.
|
||||
The minimum length of a match, called minmatch, is 4.
|
||||
As a consequence, a 0 value means 4 bytes, and a value of 15 means 19+ bytes.
|
||||
Similar to literal length, on reaching the highest possible value (15),
|
||||
we output additional bytes, one at a time, with values ranging from 0 to 255.
|
||||
They are added to total to provide the final match length.
|
||||
A 255 value means there is another byte to read and add.
|
||||
There is no limit to the number of optional bytes that can be output this way.
|
||||
(This points towards a maximum achievable compression ratio of ~250).
|
||||
|
||||
With the offset and the matchlength,
|
||||
the decoder can now proceed to copy the data from the already decoded buffer.
|
||||
On decoding the matchlength, we reach the end of the compressed sequence,
|
||||
and therefore start another one.
|
||||
|
||||
|
||||
-- Parsing restrictions --
|
||||
|
||||
There are specific parsing rules to respect in order to remain compatible
|
||||
with assumptions made by the decoder :
|
||||
1) The last 5 bytes are always literals
|
||||
2) The last match must start at least 12 bytes before end of stream
|
||||
Consequently, a file with less than 13 bytes cannot be compressed.
|
||||
These rules are in place to ensure that the decoder
|
||||
will never read beyond the input buffer, nor write beyond the output buffer.
|
||||
|
||||
Note that the last sequence is also incomplete,
|
||||
and stops right after literals.
|
||||
|
||||
|
||||
-- Additional notes --
|
||||
|
||||
There is no assumption nor limits to the way the compressor
|
||||
searches and selects matches within the source stream.
|
||||
It could be a fast scan, a multi-probe, a full search using BST,
|
||||
standard hash chains or MMC, well whatever.
|
||||
|
||||
Advanced parsing strategies can also be implemented, such as lazy match,
|
||||
or full optimal parsing.
|
||||
|
||||
All these trade-off offer distinctive speed/memory/compression advantages.
|
||||
Whatever the method used by the compressor, its result will be decodable
|
||||
by any LZ4 decoder if it follows the format specification described above.
|
||||
|
|
@ -1,402 +0,0 @@
|
|||
/*
|
||||
LZ4Demo - Demo CLI program using LZ4 compression
|
||||
Copyright (C) Yann Collet 2011-2012
|
||||
GPL v2 License
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
You can contact the author at :
|
||||
- LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html
|
||||
- LZ4 source repository : http://code.google.com/p/lz4/
|
||||
*/
|
||||
/*
|
||||
Note : this is *only* a demo program, an example to show how LZ4 can be used.
|
||||
It is not considered part of LZ4 compression library.
|
||||
The license of LZ4 is BSD.
|
||||
The license of the demo program is GPL.
|
||||
*/
|
||||
|
||||
//**************************************
|
||||
// Compiler Options
|
||||
//**************************************
|
||||
// Disable some Visual warning messages
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#define _CRT_SECURE_NO_DEPRECATE // VS2005
|
||||
|
||||
|
||||
//****************************
|
||||
// Includes
|
||||
//****************************
|
||||
#include <stdio.h> // fprintf, fopen, fread, _fileno(?)
|
||||
#include <stdlib.h> // malloc
|
||||
#include <string.h> // strcmp
|
||||
#include <time.h> // clock
|
||||
#ifdef _WIN32
|
||||
#include <io.h> // _setmode
|
||||
#include <fcntl.h> // _O_BINARY
|
||||
#endif
|
||||
#include "lz4.h"
|
||||
#include "lz4hc.h"
|
||||
#include "bench.h"
|
||||
|
||||
|
||||
//**************************************
|
||||
// Compiler-specific functions
|
||||
//**************************************
|
||||
#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
|
||||
|
||||
#if defined(_MSC_VER) // Visual Studio
|
||||
#define swap32 _byteswap_ulong
|
||||
#elif GCC_VERSION >= 403
|
||||
#define swap32 __builtin_bswap32
|
||||
#else
|
||||
static inline unsigned int swap32(unsigned int x) {
|
||||
return ((x << 24) & 0xff000000 ) |
|
||||
((x << 8) & 0x00ff0000 ) |
|
||||
((x >> 8) & 0x0000ff00 ) |
|
||||
((x >> 24) & 0x000000ff );
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
//****************************
|
||||
// Constants
|
||||
//****************************
|
||||
#define COMPRESSOR_NAME "Compression CLI using LZ4 algorithm"
|
||||
#define COMPRESSOR_VERSION ""
|
||||
#define COMPILED __DATE__
|
||||
#define AUTHOR "Yann Collet"
|
||||
#define EXTENSION ".lz4"
|
||||
#define WELCOME_MESSAGE "*** %s %s, by %s (%s) ***\n", COMPRESSOR_NAME, COMPRESSOR_VERSION, AUTHOR, COMPILED
|
||||
|
||||
#define CHUNKSIZE (8<<20) // 8 MB
|
||||
#define CACHELINE 64
|
||||
#define ARCHIVE_MAGICNUMBER 0x184C2102
|
||||
#define ARCHIVE_MAGICNUMBER_SIZE 4
|
||||
|
||||
|
||||
//**************************************
|
||||
// Architecture Macros
|
||||
//**************************************
|
||||
static const int one = 1;
|
||||
#define CPU_LITTLE_ENDIAN (*(char*)(&one))
|
||||
#define CPU_BIG_ENDIAN (!CPU_LITTLE_ENDIAN)
|
||||
#define LITTLE_ENDIAN32(i) if (CPU_BIG_ENDIAN) { i = swap32(i); }
|
||||
|
||||
|
||||
//**************************************
|
||||
// Macros
|
||||
//**************************************
|
||||
#define DISPLAY(...) fprintf(stderr, __VA_ARGS__)
|
||||
|
||||
|
||||
//****************************
|
||||
// Functions
|
||||
//****************************
|
||||
int usage(char* exename)
|
||||
{
|
||||
DISPLAY( "Usage :\n");
|
||||
DISPLAY( " %s [arg] input output\n", exename);
|
||||
DISPLAY( "Arguments :\n");
|
||||
DISPLAY( " -c0: Fast compression (default) \n");
|
||||
DISPLAY( " -c1: High compression \n");
|
||||
DISPLAY( " -d : decompression \n");
|
||||
DISPLAY( " -b#: Benchmark files, using # compression level\n");
|
||||
DISPLAY( " -t : check compressed file \n");
|
||||
DISPLAY( " -h : help (this text)\n");
|
||||
DISPLAY( "input : can be 'stdin' (pipe) or a filename\n");
|
||||
DISPLAY( "output : can be 'stdout'(pipe) or a filename or 'null'\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int badusage(char* exename)
|
||||
{
|
||||
DISPLAY("Wrong parameters\n");
|
||||
usage(exename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int get_fileHandle(char* input_filename, char* output_filename, FILE** pfinput, FILE** pfoutput)
|
||||
{
|
||||
char stdinmark[] = "stdin";
|
||||
char stdoutmark[] = "stdout";
|
||||
|
||||
if (!strcmp (input_filename, stdinmark)) {
|
||||
DISPLAY( "Using stdin for input\n");
|
||||
*pfinput = stdin;
|
||||
#ifdef _WIN32 // Need to set stdin/stdout to binary mode specifically for windows
|
||||
_setmode( _fileno( stdin ), _O_BINARY );
|
||||
#endif
|
||||
} else {
|
||||
*pfinput = fopen( input_filename, "rb" );
|
||||
}
|
||||
|
||||
if (!strcmp (output_filename, stdoutmark)) {
|
||||
DISPLAY( "Using stdout for output\n");
|
||||
*pfoutput = stdout;
|
||||
#ifdef _WIN32 // Need to set stdin/stdout to binary mode specifically for windows
|
||||
_setmode( _fileno( stdout ), _O_BINARY );
|
||||
#endif
|
||||
} else {
|
||||
*pfoutput = fopen( output_filename, "wb" );
|
||||
}
|
||||
|
||||
if ( *pfinput==0 ) { DISPLAY( "Pb opening %s\n", input_filename); return 2; }
|
||||
if ( *pfoutput==0) { DISPLAY( "Pb opening %s\n", output_filename); return 3; }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int compress_file(char* input_filename, char* output_filename, int compressionlevel)
|
||||
{
|
||||
int (*compressionFunction)(const char*, char*, int);
|
||||
unsigned long long filesize = 0;
|
||||
unsigned long long compressedfilesize = ARCHIVE_MAGICNUMBER_SIZE;
|
||||
unsigned int u32var;
|
||||
char* in_buff;
|
||||
char* out_buff;
|
||||
FILE* finput;
|
||||
FILE* foutput;
|
||||
int r;
|
||||
int displayLevel = (compressionlevel>0);
|
||||
clock_t start, end;
|
||||
size_t sizeCheck;
|
||||
|
||||
|
||||
// Init
|
||||
switch (compressionlevel)
|
||||
{
|
||||
case 0 : compressionFunction = LZ4_compress; break;
|
||||
case 1 : compressionFunction = LZ4_compressHC; break;
|
||||
default : compressionFunction = LZ4_compress;
|
||||
}
|
||||
start = clock();
|
||||
r = get_fileHandle(input_filename, output_filename, &finput, &foutput);
|
||||
if (r) return r;
|
||||
|
||||
// Allocate Memory
|
||||
in_buff = (char*)malloc(CHUNKSIZE);
|
||||
out_buff = (char*)malloc(LZ4_compressBound(CHUNKSIZE));
|
||||
if (!in_buff || !out_buff) { DISPLAY("Allocation error : not enough memory\n"); return 8; }
|
||||
|
||||
// Write Archive Header
|
||||
u32var = ARCHIVE_MAGICNUMBER;
|
||||
LITTLE_ENDIAN32(u32var);
|
||||
*(unsigned int*)out_buff = u32var;
|
||||
sizeCheck = fwrite(out_buff, 1, ARCHIVE_MAGICNUMBER_SIZE, foutput);
|
||||
if (sizeCheck!=ARCHIVE_MAGICNUMBER_SIZE) { DISPLAY("write error\n"); return 10; }
|
||||
|
||||
// Main Loop
|
||||
while (1)
|
||||
{
|
||||
int outSize;
|
||||
// Read Block
|
||||
int inSize = (int) fread(in_buff, (size_t)1, (size_t)CHUNKSIZE, finput);
|
||||
if( inSize<=0 ) break;
|
||||
filesize += inSize;
|
||||
if (displayLevel) DISPLAY("Read : %i MB \r", (int)(filesize>>20));
|
||||
|
||||
// Compress Block
|
||||
outSize = compressionFunction(in_buff, out_buff+4, inSize);
|
||||
compressedfilesize += outSize+4;
|
||||
if (displayLevel) DISPLAY("Read : %i MB ==> %.2f%%\r", (int)(filesize>>20), (double)compressedfilesize/filesize*100);
|
||||
|
||||
// Write Block
|
||||
LITTLE_ENDIAN32(outSize);
|
||||
* (unsigned int*) out_buff = outSize;
|
||||
LITTLE_ENDIAN32(outSize);
|
||||
sizeCheck = fwrite(out_buff, 1, outSize+4, foutput);
|
||||
if (sizeCheck!=(size_t)(outSize+4)) { DISPLAY("write error\n"); return 11; }
|
||||
}
|
||||
|
||||
// Status
|
||||
end = clock();
|
||||
DISPLAY( "Compressed %llu bytes into %llu bytes ==> %.2f%%\n",
|
||||
(unsigned long long) filesize, (unsigned long long) compressedfilesize, (double)compressedfilesize/filesize*100);
|
||||
{
|
||||
double seconds = (double)(end - start)/CLOCKS_PER_SEC;
|
||||
DISPLAY( "Done in %.2f s ==> %.2f MB/s\n", seconds, (double)filesize / seconds / 1024 / 1024);
|
||||
}
|
||||
|
||||
// Close & Free
|
||||
free(in_buff);
|
||||
free(out_buff);
|
||||
fclose(finput);
|
||||
fclose(foutput);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int decode_file(char* input_filename, char* output_filename)
|
||||
{
|
||||
unsigned long long filesize = 0;
|
||||
char* in_buff;
|
||||
char* out_buff;
|
||||
size_t uselessRet;
|
||||
int sinkint;
|
||||
unsigned int chunkSize;
|
||||
FILE* finput;
|
||||
FILE* foutput;
|
||||
clock_t start, end;
|
||||
int r;
|
||||
size_t sizeCheck;
|
||||
|
||||
|
||||
// Init
|
||||
start = clock();
|
||||
r = get_fileHandle(input_filename, output_filename, &finput, &foutput);
|
||||
if (r) return r;
|
||||
|
||||
// Allocate Memory
|
||||
in_buff = (char*)malloc(LZ4_compressBound(CHUNKSIZE));
|
||||
out_buff = (char*)malloc(CHUNKSIZE);
|
||||
if (!in_buff || !out_buff) { DISPLAY("Allocation error : not enough memory\n"); return 7; }
|
||||
|
||||
// Check Archive Header
|
||||
chunkSize = 0;
|
||||
uselessRet = fread(&chunkSize, 1, ARCHIVE_MAGICNUMBER_SIZE, finput);
|
||||
LITTLE_ENDIAN32(chunkSize);
|
||||
if (chunkSize != ARCHIVE_MAGICNUMBER) { DISPLAY("Unrecognized header : file cannot be decoded\n"); return 6; }
|
||||
|
||||
// Main Loop
|
||||
while (1)
|
||||
{
|
||||
// Block Size
|
||||
uselessRet = fread(&chunkSize, 1, 4, finput);
|
||||
if( uselessRet==0 ) break; // Nothing to read : file read is completed
|
||||
LITTLE_ENDIAN32(chunkSize);
|
||||
if (chunkSize == ARCHIVE_MAGICNUMBER)
|
||||
continue; // appended compressed stream
|
||||
|
||||
// Read Block
|
||||
uselessRet = fread(in_buff, 1, chunkSize, finput);
|
||||
|
||||
// Decode Block
|
||||
sinkint = LZ4_uncompress_unknownOutputSize(in_buff, out_buff, chunkSize, CHUNKSIZE);
|
||||
if (sinkint < 0) { DISPLAY("Decoding Failed ! Corrupted input !\n"); return 9; }
|
||||
filesize += sinkint;
|
||||
|
||||
// Write Block
|
||||
sizeCheck = fwrite(out_buff, 1, sinkint, foutput);
|
||||
if (sizeCheck != (size_t)sinkint) { DISPLAY("write error\n"); return 12; }
|
||||
}
|
||||
|
||||
// Status
|
||||
end = clock();
|
||||
DISPLAY( "Successfully decoded %llu bytes \n", (unsigned long long)filesize);
|
||||
{
|
||||
double seconds = (double)(end - start)/CLOCKS_PER_SEC;
|
||||
DISPLAY( "Done in %.2f s ==> %.2f MB/s\n", seconds, (double)filesize / seconds / 1024 / 1024);
|
||||
}
|
||||
|
||||
// Close & Free
|
||||
free(in_buff);
|
||||
free(out_buff);
|
||||
fclose(finput);
|
||||
fclose(foutput);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int i,
|
||||
cLevel=0,
|
||||
decode=0,
|
||||
bench=0,
|
||||
filenamesStart=2;
|
||||
char* exename=argv[0];
|
||||
char* input_filename=0;
|
||||
char* output_filename=0;
|
||||
#ifdef _WIN32
|
||||
char nulmark[] = "nul";
|
||||
#else
|
||||
char nulmark[] = "/dev/null";
|
||||
#endif
|
||||
char nullinput[] = "null";
|
||||
|
||||
// Welcome message
|
||||
DISPLAY( WELCOME_MESSAGE);
|
||||
|
||||
if (argc<2) { badusage(exename); return 1; }
|
||||
|
||||
for(i=1; i<argc; i++)
|
||||
{
|
||||
char* argument = argv[i];
|
||||
|
||||
if(!argument) continue; // Protection if argument empty
|
||||
|
||||
// Select command
|
||||
if (argument[0]=='-')
|
||||
{
|
||||
argument ++;
|
||||
|
||||
// Display help on usage
|
||||
if ( argument[0] =='h' ) { usage(exename); return 0; }
|
||||
|
||||
// Compression (default)
|
||||
if ( argument[0] =='c' ) { if (argument[1] >='0') cLevel=argument[1] - '0'; continue; }
|
||||
|
||||
// Decoding
|
||||
if ( argument[0] =='d' ) { decode=1; continue; }
|
||||
|
||||
// Bench
|
||||
if ( argument[0] =='b' ) { bench=1; if (argument[1] >= '0') cLevel=argument[1] - '0'; continue; }
|
||||
|
||||
// Modify Block Size (benchmark only)
|
||||
if ( argument[0] =='B' ) { int B = argument[1] - '0'; int S = 1 << (10 + 2*B); BMK_SetBlocksize(S); continue; }
|
||||
|
||||
// Modify Nb Iterations (benchmark only)
|
||||
if ( argument[0] =='i' ) { int iters = argument[1] - '0'; BMK_SetNbIterations(iters); continue; }
|
||||
|
||||
// Pause at the end (benchmark only)
|
||||
if ( argument[0] =='p' ) { BMK_SetPause(); continue; }
|
||||
|
||||
// Test
|
||||
if ( argument[0] =='t' ) { decode=1; output_filename=nulmark; continue; }
|
||||
}
|
||||
|
||||
// first provided filename is input
|
||||
if (!input_filename) { input_filename=argument; filenamesStart=i; continue; }
|
||||
|
||||
// second provided filename is output
|
||||
if (!output_filename)
|
||||
{
|
||||
output_filename=argument;
|
||||
if (!strcmp (output_filename, nullinput)) output_filename = nulmark;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// No input filename ==> Error
|
||||
if(!input_filename) { badusage(exename); return 1; }
|
||||
|
||||
if (bench) return BMK_benchFile(argv+filenamesStart, argc-filenamesStart, cLevel);
|
||||
|
||||
// No output filename ==> Error
|
||||
if (!output_filename) { badusage(exename); return 1; }
|
||||
|
||||
if (decode) return decode_file(input_filename, output_filename);
|
||||
|
||||
return compress_file(input_filename, output_filename, cLevel); // Compression is 'default' action
|
||||
|
||||
}
|
355
ext/lz4/lz4hc.c
355
ext/lz4/lz4hc.c
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
LZ4 HC - High Compression Mode of LZ4
|
||||
Copyright (C) 2011-2012, Yann Collet.
|
||||
Copyright (C) 2011-2013, Yann Collet.
|
||||
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -31,12 +31,24 @@
|
|||
- LZ4 source repository : http://code.google.com/p/lz4/
|
||||
*/
|
||||
|
||||
//**************************************
|
||||
// Memory routines
|
||||
//**************************************
|
||||
#include <stdlib.h> // calloc, free
|
||||
#define ALLOCATOR(s) calloc(1,s)
|
||||
#define FREEMEM free
|
||||
#include <string.h> // memset, memcpy
|
||||
#define MEM_INIT memset
|
||||
|
||||
|
||||
//**************************************
|
||||
// CPU Feature Detection
|
||||
//**************************************
|
||||
// 32 or 64 bits ?
|
||||
#if (defined(__x86_64__) || defined(__x86_64) || defined(__amd64__) || defined(__amd64) || defined(__ppc64__) || defined(_WIN64) || defined(__LP64__) || defined(_LP64) ) // Detects 64 bits mode
|
||||
#if (defined(__x86_64__) || defined(_M_X64) || defined(_WIN64) \
|
||||
|| defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__) \
|
||||
|| defined(__64BIT__) || defined(_LP64) || defined(__LP64__) \
|
||||
|| defined(__ia64) || defined(__itanium__) || defined(_M_IA64) ) // Detects 64 bits mode
|
||||
# define LZ4_ARCH64 1
|
||||
#else
|
||||
# define LZ4_ARCH64 0
|
||||
|
@ -52,7 +64,7 @@
|
|||
#elif (defined(__BIG_ENDIAN__) || defined(__BIG_ENDIAN) || defined(_BIG_ENDIAN)) && !(defined(__LITTLE_ENDIAN__) || defined(__LITTLE_ENDIAN) || defined(_LITTLE_ENDIAN))
|
||||
# define LZ4_BIG_ENDIAN 1
|
||||
#elif defined(__sparc) || defined(__sparc__) \
|
||||
|| defined(__ppc__) || defined(_POWER) || defined(__powerpc__) || defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC) || defined(PPC) || defined(__powerpc__) || defined(__powerpc) || defined(powerpc) \
|
||||
|| defined(__powerpc__) || defined(__ppc__) || defined(__PPC__) \
|
||||
|| defined(__hpux) || defined(__hppa) \
|
||||
|| defined(_MIPSEB) || defined(__s390__)
|
||||
# define LZ4_BIG_ENDIAN 1
|
||||
|
@ -76,78 +88,84 @@
|
|||
//**************************************
|
||||
// Compiler Options
|
||||
//**************************************
|
||||
#if __STDC_VERSION__ >= 199901L // C99
|
||||
#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L // C99
|
||||
/* "restrict" is a known keyword */
|
||||
#else
|
||||
# define restrict // Disable restrict
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define inline __inline // Visual is not C99, but supports some kind of inline
|
||||
# define forceinline __forceinline
|
||||
# include <intrin.h> // For Visual 2005
|
||||
# if LZ4_ARCH64 // 64-bit
|
||||
#ifdef _MSC_VER // Visual Studio
|
||||
# define FORCE_INLINE static __forceinline
|
||||
# include <intrin.h> // For Visual 2005
|
||||
# if LZ4_ARCH64 // 64-bits
|
||||
# pragma intrinsic(_BitScanForward64) // For Visual 2005
|
||||
# pragma intrinsic(_BitScanReverse64) // For Visual 2005
|
||||
# else
|
||||
# else // 32-bits
|
||||
# pragma intrinsic(_BitScanForward) // For Visual 2005
|
||||
# pragma intrinsic(_BitScanReverse) // For Visual 2005
|
||||
# endif
|
||||
# pragma warning(disable : 4127) // disable: C4127: conditional expression is constant
|
||||
# pragma warning(disable : 4701) // disable: C4701: potentially uninitialized local variable used
|
||||
#else
|
||||
# ifdef __GNUC__
|
||||
# define forceinline inline __attribute__((always_inline))
|
||||
# define FORCE_INLINE static inline __attribute__((always_inline))
|
||||
# else
|
||||
# define forceinline inline
|
||||
# define FORCE_INLINE static inline
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER // Visual Studio
|
||||
#define lz4_bswap16(x) _byteswap_ushort(x)
|
||||
# define lz4_bswap16(x) _byteswap_ushort(x)
|
||||
#else
|
||||
#define lz4_bswap16(x) ((unsigned short int) ((((x) >> 8) & 0xffu) | (((x) & 0xffu) << 8)))
|
||||
# define lz4_bswap16(x) ((unsigned short int) ((((x) >> 8) & 0xffu) | (((x) & 0xffu) << 8)))
|
||||
#endif
|
||||
|
||||
|
||||
//**************************************
|
||||
// Includes
|
||||
//**************************************
|
||||
#include <stdlib.h> // calloc, free
|
||||
#include <string.h> // memset, memcpy
|
||||
#include "lz4hc.h"
|
||||
|
||||
#define ALLOCATOR(s) calloc(1,s)
|
||||
#define FREEMEM free
|
||||
#define MEM_INIT memset
|
||||
#include "lz4.h"
|
||||
|
||||
|
||||
//**************************************
|
||||
// Basic Types
|
||||
//**************************************
|
||||
#if defined(_MSC_VER) // Visual Studio does not support 'stdint' natively
|
||||
#define BYTE unsigned __int8
|
||||
#define U16 unsigned __int16
|
||||
#define U32 unsigned __int32
|
||||
#define S32 __int32
|
||||
#define U64 unsigned __int64
|
||||
#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L // C99
|
||||
# include <stdint.h>
|
||||
typedef uint8_t BYTE;
|
||||
typedef uint16_t U16;
|
||||
typedef uint32_t U32;
|
||||
typedef int32_t S32;
|
||||
typedef uint64_t U64;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#define BYTE uint8_t
|
||||
#define U16 uint16_t
|
||||
#define U32 uint32_t
|
||||
#define S32 int32_t
|
||||
#define U64 uint64_t
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned short U16;
|
||||
typedef unsigned int U32;
|
||||
typedef signed int S32;
|
||||
typedef unsigned long long U64;
|
||||
#endif
|
||||
|
||||
#ifndef LZ4_FORCE_UNALIGNED_ACCESS
|
||||
#pragma pack(push, 1)
|
||||
#if defined(__GNUC__) && !defined(LZ4_FORCE_UNALIGNED_ACCESS)
|
||||
# define _PACKED __attribute__ ((packed))
|
||||
#else
|
||||
# define _PACKED
|
||||
#endif
|
||||
|
||||
typedef struct _U16_S { U16 v; } U16_S;
|
||||
typedef struct _U32_S { U32 v; } U32_S;
|
||||
typedef struct _U64_S { U64 v; } U64_S;
|
||||
#if !defined(LZ4_FORCE_UNALIGNED_ACCESS) && !defined(__GNUC__)
|
||||
# ifdef __IBMC__
|
||||
# pragma pack(1)
|
||||
# else
|
||||
# pragma pack(push, 1)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef LZ4_FORCE_UNALIGNED_ACCESS
|
||||
#pragma pack(pop)
|
||||
typedef struct _U16_S { U16 v; } _PACKED U16_S;
|
||||
typedef struct _U32_S { U32 v; } _PACKED U32_S;
|
||||
typedef struct _U64_S { U64 v; } _PACKED U64_S;
|
||||
|
||||
#if !defined(LZ4_FORCE_UNALIGNED_ACCESS) && !defined(__GNUC__)
|
||||
# pragma pack(pop)
|
||||
#endif
|
||||
|
||||
#define A64(x) (((U64_S *)(x))->v)
|
||||
|
@ -182,34 +200,40 @@ typedef struct _U64_S { U64 v; } U64_S;
|
|||
#define MINLENGTH (MFLIMIT+1)
|
||||
#define OPTIMAL_ML (int)((ML_MASK-1)+MINMATCH)
|
||||
|
||||
#define KB *(1U<<10)
|
||||
#define MB *(1U<<20)
|
||||
#define GB *(1U<<30)
|
||||
|
||||
|
||||
//**************************************
|
||||
// Architecture-specific macros
|
||||
//**************************************
|
||||
#if LZ4_ARCH64 // 64-bit
|
||||
#define STEPSIZE 8
|
||||
#define LZ4_COPYSTEP(s,d) A64(d) = A64(s); d+=8; s+=8;
|
||||
#define LZ4_COPYPACKET(s,d) LZ4_COPYSTEP(s,d)
|
||||
#define UARCH U64
|
||||
#define AARCH A64
|
||||
#define HTYPE U32
|
||||
#define INITBASE(b,s) const BYTE* const b = s
|
||||
#else // 32-bit
|
||||
#define STEPSIZE 4
|
||||
#define LZ4_COPYSTEP(s,d) A32(d) = A32(s); d+=4; s+=4;
|
||||
#define LZ4_COPYPACKET(s,d) LZ4_COPYSTEP(s,d); LZ4_COPYSTEP(s,d);
|
||||
#define UARCH U32
|
||||
#define AARCH A32
|
||||
#define HTYPE const BYTE*
|
||||
#define INITBASE(b,s) const int b = 0
|
||||
#if LZ4_ARCH64 // 64-bit
|
||||
# define STEPSIZE 8
|
||||
# define LZ4_COPYSTEP(s,d) A64(d) = A64(s); d+=8; s+=8;
|
||||
# define LZ4_COPYPACKET(s,d) LZ4_COPYSTEP(s,d)
|
||||
# define UARCH U64
|
||||
# define AARCH A64
|
||||
# define HTYPE U32
|
||||
# define INITBASE(b,s) const BYTE* const b = s
|
||||
#else // 32-bit
|
||||
# define STEPSIZE 4
|
||||
# define LZ4_COPYSTEP(s,d) A32(d) = A32(s); d+=4; s+=4;
|
||||
# define LZ4_COPYPACKET(s,d) LZ4_COPYSTEP(s,d); LZ4_COPYSTEP(s,d);
|
||||
# define UARCH U32
|
||||
# define AARCH A32
|
||||
//# define HTYPE const BYTE*
|
||||
//# define INITBASE(b,s) const int b = 0
|
||||
# define HTYPE U32
|
||||
# define INITBASE(b,s) const BYTE* const b = s
|
||||
#endif
|
||||
|
||||
#if defined(LZ4_BIG_ENDIAN)
|
||||
#define LZ4_READ_LITTLEENDIAN_16(d,s,p) { U16 v = A16(p); v = lz4_bswap16(v); d = (s) - v; }
|
||||
#define LZ4_WRITE_LITTLEENDIAN_16(p,i) { U16 v = (U16)(i); v = lz4_bswap16(v); A16(p) = v; p+=2; }
|
||||
#else // Little Endian
|
||||
#define LZ4_READ_LITTLEENDIAN_16(d,s,p) { d = (s) - A16(p); }
|
||||
#define LZ4_WRITE_LITTLEENDIAN_16(p,v) { A16(p) = v; p+=2; }
|
||||
# define LZ4_READ_LITTLEENDIAN_16(d,s,p) { U16 v = A16(p); v = lz4_bswap16(v); d = (s) - v; }
|
||||
# define LZ4_WRITE_LITTLEENDIAN_16(p,i) { U16 v = (U16)(i); v = lz4_bswap16(v); A16(p) = v; p+=2; }
|
||||
#else // Little Endian
|
||||
# define LZ4_READ_LITTLEENDIAN_16(d,s,p) { d = (s) - A16(p); }
|
||||
# define LZ4_WRITE_LITTLEENDIAN_16(p,v) { A16(p) = v; p+=2; }
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -218,7 +242,9 @@ typedef struct _U64_S { U64 v; } U64_S;
|
|||
//************************************************************
|
||||
typedef struct
|
||||
{
|
||||
const BYTE* inputBuffer;
|
||||
const BYTE* base;
|
||||
const BYTE* end;
|
||||
HTYPE hashTable[HASHTABLESIZE];
|
||||
U16 chainTable[MAXD];
|
||||
const BYTE* nextToUpdate;
|
||||
|
@ -230,11 +256,11 @@ typedef struct
|
|||
//**************************************
|
||||
#define LZ4_WILDCOPY(s,d,e) do { LZ4_COPYPACKET(s,d) } while (d<e);
|
||||
#define LZ4_BLINDCOPY(s,d,l) { BYTE* e=d+l; LZ4_WILDCOPY(s,d,e); d=e; }
|
||||
#define HASH_FUNCTION(i) (((i) * 2654435761U) >> ((MINMATCH*8)-HASH_LOG))
|
||||
#define HASH_VALUE(p) HASH_FUNCTION(A32(p))
|
||||
#define HASH_POINTER(p) (HashTable[HASH_VALUE(p)] + base)
|
||||
#define DELTANEXT(p) chainTable[(size_t)(p) & MAXD_MASK]
|
||||
#define GETNEXT(p) ((p) - (size_t)DELTANEXT(p))
|
||||
#define HASH_FUNCTION(i) (((i) * 2654435761U) >> ((MINMATCH*8)-HASH_LOG))
|
||||
#define HASH_VALUE(p) HASH_FUNCTION(A32(p))
|
||||
#define HASH_POINTER(p) (HashTable[HASH_VALUE(p)] + base)
|
||||
#define DELTANEXT(p) chainTable[(size_t)(p) & MAXD_MASK]
|
||||
#define GETNEXT(p) ((p) - (size_t)DELTANEXT(p))
|
||||
|
||||
|
||||
//**************************************
|
||||
|
@ -242,99 +268,98 @@ typedef struct
|
|||
//**************************************
|
||||
#if LZ4_ARCH64
|
||||
|
||||
inline static int LZ4_NbCommonBytes (register U64 val)
|
||||
FORCE_INLINE int LZ4_NbCommonBytes (register U64 val)
|
||||
{
|
||||
#if defined(LZ4_BIG_ENDIAN)
|
||||
#if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)
|
||||
# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)
|
||||
unsigned long r = 0;
|
||||
_BitScanReverse64( &r, val );
|
||||
return (int)(r>>3);
|
||||
#elif defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT)
|
||||
# elif defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT)
|
||||
return (__builtin_clzll(val) >> 3);
|
||||
#else
|
||||
# else
|
||||
int r;
|
||||
if (!(val>>32)) { r=4; } else { r=0; val>>=32; }
|
||||
if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }
|
||||
r += (!val);
|
||||
return r;
|
||||
#endif
|
||||
# endif
|
||||
#else
|
||||
#if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)
|
||||
# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)
|
||||
unsigned long r = 0;
|
||||
_BitScanForward64( &r, val );
|
||||
return (int)(r>>3);
|
||||
#elif defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT)
|
||||
# elif defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT)
|
||||
return (__builtin_ctzll(val) >> 3);
|
||||
#else
|
||||
# else
|
||||
static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 };
|
||||
return DeBruijnBytePos[((U64)((val & -val) * 0x0218A392CDABBD3F)) >> 58];
|
||||
#endif
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
inline static int LZ4_NbCommonBytes (register U32 val)
|
||||
FORCE_INLINE int LZ4_NbCommonBytes (register U32 val)
|
||||
{
|
||||
#if defined(LZ4_BIG_ENDIAN)
|
||||
#if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)
|
||||
# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)
|
||||
unsigned long r;
|
||||
_BitScanReverse( &r, val );
|
||||
return (int)(r>>3);
|
||||
#elif defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT)
|
||||
# elif defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT)
|
||||
return (__builtin_clz(val) >> 3);
|
||||
#else
|
||||
# else
|
||||
int r;
|
||||
if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; }
|
||||
r += (!val);
|
||||
return r;
|
||||
#endif
|
||||
# endif
|
||||
#else
|
||||
#if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)
|
||||
# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)
|
||||
unsigned long r;
|
||||
_BitScanForward( &r, val );
|
||||
return (int)(r>>3);
|
||||
#elif defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT)
|
||||
# elif defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 304) && !defined(LZ4_FORCE_SW_BITCOUNT)
|
||||
return (__builtin_ctz(val) >> 3);
|
||||
#else
|
||||
# else
|
||||
static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 };
|
||||
return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];
|
||||
#endif
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
inline static int LZ4HC_Init (LZ4HC_Data_Structure* hc4, const BYTE* base)
|
||||
FORCE_INLINE void LZ4_initHC (LZ4HC_Data_Structure* hc4, const BYTE* base)
|
||||
{
|
||||
MEM_INIT((void*)hc4->hashTable, 0, sizeof(hc4->hashTable));
|
||||
MEM_INIT(hc4->chainTable, 0xFF, sizeof(hc4->chainTable));
|
||||
hc4->nextToUpdate = base + LZ4_ARCH64;
|
||||
hc4->nextToUpdate = base + 1;
|
||||
hc4->base = base;
|
||||
return 1;
|
||||
hc4->inputBuffer = base;
|
||||
hc4->end = base;
|
||||
}
|
||||
|
||||
|
||||
inline static void* LZ4HC_Create (const BYTE* base)
|
||||
void* LZ4_createHC (const char* inputBuffer)
|
||||
{
|
||||
void* hc4 = ALLOCATOR(sizeof(LZ4HC_Data_Structure));
|
||||
|
||||
LZ4HC_Init ((LZ4HC_Data_Structure*)hc4, base);
|
||||
LZ4_initHC ((LZ4HC_Data_Structure*)hc4, (const BYTE*)inputBuffer);
|
||||
return hc4;
|
||||
}
|
||||
|
||||
|
||||
inline static int LZ4HC_Free (void** LZ4HC_Data)
|
||||
int LZ4_freeHC (void* LZ4HC_Data)
|
||||
{
|
||||
FREEMEM(*LZ4HC_Data);
|
||||
*LZ4HC_Data = NULL;
|
||||
return (1);
|
||||
FREEMEM(LZ4HC_Data);
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
// Update chains up to ip (excluded)
|
||||
forceinline static void LZ4HC_Insert (LZ4HC_Data_Structure* hc4, const BYTE* ip)
|
||||
FORCE_INLINE void LZ4HC_Insert (LZ4HC_Data_Structure* hc4, const BYTE* ip)
|
||||
{
|
||||
U16* chainTable = hc4->chainTable;
|
||||
HTYPE* HashTable = hc4->hashTable;
|
||||
|
@ -342,17 +367,37 @@ forceinline static void LZ4HC_Insert (LZ4HC_Data_Structure* hc4, const BYTE* ip)
|
|||
|
||||
while(hc4->nextToUpdate < ip)
|
||||
{
|
||||
const BYTE* p = hc4->nextToUpdate;
|
||||
const BYTE* const p = hc4->nextToUpdate;
|
||||
size_t delta = (p) - HASH_POINTER(p);
|
||||
if (delta>MAX_DISTANCE) delta = MAX_DISTANCE;
|
||||
DELTANEXT(p) = (U16)delta;
|
||||
HashTable[HASH_VALUE(p)] = (p) - base;
|
||||
HashTable[HASH_VALUE(p)] = (HTYPE)((p) - base);
|
||||
hc4->nextToUpdate++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
forceinline static size_t LZ4HC_CommonLength (const BYTE* p1, const BYTE* p2, const BYTE* const matchlimit)
|
||||
char* LZ4_slideInputBufferHC(void* LZ4HC_Data)
|
||||
{
|
||||
LZ4HC_Data_Structure* hc4 = (LZ4HC_Data_Structure*)LZ4HC_Data;
|
||||
U32 distance = (U32)(hc4->end - hc4->inputBuffer) - 64 KB;
|
||||
distance = (distance >> 16) << 16; // Must be a multiple of 64 KB
|
||||
LZ4HC_Insert(hc4, hc4->end - MINMATCH);
|
||||
memcpy((void*)(hc4->end - 64 KB - distance), (const void*)(hc4->end - 64 KB), 64 KB);
|
||||
hc4->nextToUpdate -= distance;
|
||||
hc4->base -= distance;
|
||||
if ((U32)(hc4->inputBuffer - hc4->base) > 1 GB + 64 KB) // Avoid overflow
|
||||
{
|
||||
int i;
|
||||
hc4->base += 1 GB;
|
||||
for (i=0; i<HASHTABLESIZE; i++) hc4->hashTable[i] -= 1 GB;
|
||||
}
|
||||
hc4->end -= distance;
|
||||
return (char*)(hc4->end);
|
||||
}
|
||||
|
||||
|
||||
FORCE_INLINE size_t LZ4HC_CommonLength (const BYTE* p1, const BYTE* p2, const BYTE* const matchlimit)
|
||||
{
|
||||
const BYTE* p1t = p1;
|
||||
|
||||
|
@ -370,7 +415,7 @@ forceinline static size_t LZ4HC_CommonLength (const BYTE* p1, const BYTE* p2, co
|
|||
}
|
||||
|
||||
|
||||
forceinline static int LZ4HC_InsertAndFindBestMatch (LZ4HC_Data_Structure* hc4, const BYTE* ip, const BYTE* const matchlimit, const BYTE** matchpos)
|
||||
FORCE_INLINE int LZ4HC_InsertAndFindBestMatch (LZ4HC_Data_Structure* hc4, const BYTE* ip, const BYTE* const matchlimit, const BYTE** matchpos)
|
||||
{
|
||||
U16* const chainTable = hc4->chainTable;
|
||||
HTYPE* const HashTable = hc4->hashTable;
|
||||
|
@ -378,7 +423,7 @@ forceinline static int LZ4HC_InsertAndFindBestMatch (LZ4HC_Data_Structure* hc4,
|
|||
INITBASE(base,hc4->base);
|
||||
int nbAttempts=MAX_NB_ATTEMPTS;
|
||||
size_t repl=0, ml=0;
|
||||
U16 delta;
|
||||
U16 delta=0; // useless assignment, to remove an uninitialization warning
|
||||
|
||||
// HC4 match finder
|
||||
LZ4HC_Insert(hc4, ip);
|
||||
|
@ -387,7 +432,7 @@ forceinline static int LZ4HC_InsertAndFindBestMatch (LZ4HC_Data_Structure* hc4,
|
|||
#define REPEAT_OPTIMIZATION
|
||||
#ifdef REPEAT_OPTIMIZATION
|
||||
// Detect repetitive sequences of length <= 4
|
||||
if (ref >= ip-4) // potential repetition
|
||||
if ((U32)(ip-ref) <= 4) // potential repetition
|
||||
{
|
||||
if (A32(ref) == A32(ip)) // confirmed
|
||||
{
|
||||
|
@ -399,7 +444,7 @@ forceinline static int LZ4HC_InsertAndFindBestMatch (LZ4HC_Data_Structure* hc4,
|
|||
}
|
||||
#endif
|
||||
|
||||
while ((ref >= ip-MAX_DISTANCE) && (nbAttempts))
|
||||
while (((U32)(ip-ref) <= MAX_DISTANCE) && (nbAttempts))
|
||||
{
|
||||
nbAttempts--;
|
||||
if (*(ref+ml) == *(ip+ml))
|
||||
|
@ -427,7 +472,7 @@ forceinline static int LZ4HC_InsertAndFindBestMatch (LZ4HC_Data_Structure* hc4,
|
|||
do
|
||||
{
|
||||
DELTANEXT(ptr) = delta;
|
||||
HashTable[HASH_VALUE(ptr)] = (ptr) - base; // Head of chain
|
||||
HashTable[HASH_VALUE(ptr)] = (HTYPE)((ptr) - base); // Head of chain
|
||||
ptr++;
|
||||
} while(ptr < end);
|
||||
hc4->nextToUpdate = end;
|
||||
|
@ -438,7 +483,7 @@ forceinline static int LZ4HC_InsertAndFindBestMatch (LZ4HC_Data_Structure* hc4,
|
|||
}
|
||||
|
||||
|
||||
forceinline static int LZ4HC_InsertAndGetWiderMatch (LZ4HC_Data_Structure* hc4, const BYTE* ip, const BYTE* startLimit, const BYTE* matchlimit, int longest, const BYTE** matchpos, const BYTE** startpos)
|
||||
FORCE_INLINE int LZ4HC_InsertAndGetWiderMatch (LZ4HC_Data_Structure* hc4, const BYTE* ip, const BYTE* startLimit, const BYTE* matchlimit, int longest, const BYTE** matchpos, const BYTE** startpos)
|
||||
{
|
||||
U16* const chainTable = hc4->chainTable;
|
||||
HTYPE* const HashTable = hc4->hashTable;
|
||||
|
@ -451,7 +496,7 @@ forceinline static int LZ4HC_InsertAndGetWiderMatch (LZ4HC_Data_Structure* hc4,
|
|||
LZ4HC_Insert(hc4, ip);
|
||||
ref = HASH_POINTER(ip);
|
||||
|
||||
while ((ref >= ip-MAX_DISTANCE) && (nbAttempts))
|
||||
while (((U32)(ip-ref) <= MAX_DISTANCE) && (nbAttempts))
|
||||
{
|
||||
nbAttempts--;
|
||||
if (*(startLimit + longest) == *(ref - delta + longest))
|
||||
|
@ -481,7 +526,7 @@ _endCount:
|
|||
const BYTE* ipt = ip + MINMATCH + LZ4HC_CommonLength(ip+MINMATCH, ref+MINMATCH, matchlimit);
|
||||
#endif
|
||||
|
||||
while ((startt>startLimit) && (reft > hc4->base) && (startt[-1] == reft[-1])) {startt--; reft--;}
|
||||
while ((startt>startLimit) && (reft > hc4->inputBuffer) && (startt[-1] == reft[-1])) {startt--; reft--;}
|
||||
|
||||
if ((ipt-startt) > longest)
|
||||
{
|
||||
|
@ -497,16 +542,26 @@ _endCount:
|
|||
}
|
||||
|
||||
|
||||
forceinline static int LZ4_encodeSequence(const BYTE** ip, BYTE** op, const BYTE** anchor, int ml, const BYTE* ref)
|
||||
typedef enum { noLimit = 0, limitedOutput = 1 } limitedOutput_directive;
|
||||
|
||||
FORCE_INLINE int LZ4HC_encodeSequence (
|
||||
const BYTE** ip,
|
||||
BYTE** op,
|
||||
const BYTE** anchor,
|
||||
int matchLength,
|
||||
const BYTE* ref,
|
||||
limitedOutput_directive limitedOutputBuffer,
|
||||
BYTE* oend)
|
||||
{
|
||||
int length, len;
|
||||
int length;
|
||||
BYTE* token;
|
||||
|
||||
// Encode Literal length
|
||||
length = (int)(*ip - *anchor);
|
||||
token = (*op)++;
|
||||
if (length>=(int)RUN_MASK) { *token=(RUN_MASK<<ML_BITS); len = length-RUN_MASK; for(; len > 254 ; len-=255) *(*op)++ = 255; *(*op)++ = (BYTE)len; }
|
||||
else *token = (length<<ML_BITS);
|
||||
if ((limitedOutputBuffer) && ((*op + length + (2 + 1 + LASTLITERALS) + (length>>8)) > oend)) return 1; // Check output limit
|
||||
if (length>=(int)RUN_MASK) { int len; *token=(RUN_MASK<<ML_BITS); len = length-RUN_MASK; for(; len > 254 ; len-=255) *(*op)++ = 255; *(*op)++ = (BYTE)len; }
|
||||
else *token = (BYTE)(length<<ML_BITS);
|
||||
|
||||
// Copy Literals
|
||||
LZ4_BLINDCOPY(*anchor, *op, length);
|
||||
|
@ -515,36 +570,39 @@ forceinline static int LZ4_encodeSequence(const BYTE** ip, BYTE** op, const BYTE
|
|||
LZ4_WRITE_LITTLEENDIAN_16(*op,(U16)(*ip-ref));
|
||||
|
||||
// Encode MatchLength
|
||||
len = (int)(ml-MINMATCH);
|
||||
if (len>=(int)ML_MASK) { *token+=ML_MASK; len-=ML_MASK; for(; len > 509 ; len-=510) { *(*op)++ = 255; *(*op)++ = 255; } if (len > 254) { len-=255; *(*op)++ = 255; } *(*op)++ = (BYTE)len; }
|
||||
else *token += len;
|
||||
length = (int)(matchLength-MINMATCH);
|
||||
if ((limitedOutputBuffer) && (*op + (1 + LASTLITERALS) + (length>>8) > oend)) return 1; // Check output limit
|
||||
if (length>=(int)ML_MASK) { *token+=ML_MASK; length-=ML_MASK; for(; length > 509 ; length-=510) { *(*op)++ = 255; *(*op)++ = 255; } if (length > 254) { length-=255; *(*op)++ = 255; } *(*op)++ = (BYTE)length; }
|
||||
else *token += (BYTE)(length);
|
||||
|
||||
// Prepare next loop
|
||||
*ip += ml;
|
||||
*ip += matchLength;
|
||||
*anchor = *ip;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//****************************
|
||||
// Compression CODE
|
||||
//****************************
|
||||
|
||||
int LZ4_compressHCCtx(LZ4HC_Data_Structure* ctx,
|
||||
static int LZ4HC_compress_generic (
|
||||
void* ctxvoid,
|
||||
const char* source,
|
||||
char* dest,
|
||||
int isize)
|
||||
{
|
||||
int inputSize,
|
||||
int maxOutputSize,
|
||||
limitedOutput_directive limit
|
||||
)
|
||||
{
|
||||
LZ4HC_Data_Structure* ctx = (LZ4HC_Data_Structure*) ctxvoid;
|
||||
const BYTE* ip = (const BYTE*) source;
|
||||
const BYTE* anchor = ip;
|
||||
const BYTE* const iend = ip + isize;
|
||||
const BYTE* const iend = ip + inputSize;
|
||||
const BYTE* const mflimit = iend - MFLIMIT;
|
||||
const BYTE* const matchlimit = (iend - LASTLITERALS);
|
||||
|
||||
BYTE* op = (BYTE*) dest;
|
||||
BYTE* const oend = op + maxOutputSize;
|
||||
|
||||
int ml, ml2, ml3, ml0;
|
||||
int ml, ml2, ml3, ml0;
|
||||
const BYTE* ref=NULL;
|
||||
const BYTE* start2=NULL;
|
||||
const BYTE* ref2=NULL;
|
||||
|
@ -553,6 +611,11 @@ int LZ4_compressHCCtx(LZ4HC_Data_Structure* ctx,
|
|||
const BYTE* start0;
|
||||
const BYTE* ref0;
|
||||
|
||||
|
||||
// Ensure blocks follow each other
|
||||
if (ip != ctx->end) return 0;
|
||||
ctx->end += inputSize;
|
||||
|
||||
ip++;
|
||||
|
||||
// Main Loop
|
||||
|
@ -573,7 +636,7 @@ _Search2:
|
|||
|
||||
if (ml2 == ml) // No better match
|
||||
{
|
||||
LZ4_encodeSequence(&ip, &op, &anchor, ml, ref);
|
||||
if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ref, limit, oend)) return 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -625,9 +688,9 @@ _Search3:
|
|||
// ip & ref are known; Now for ml
|
||||
if (start2 < ip+ml) ml = (int)(start2 - ip);
|
||||
// Now, encode 2 sequences
|
||||
LZ4_encodeSequence(&ip, &op, &anchor, ml, ref);
|
||||
if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ref, limit, oend)) return 0;
|
||||
ip = start2;
|
||||
LZ4_encodeSequence(&ip, &op, &anchor, ml2, ref2);
|
||||
if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml2, ref2, limit, oend)) return 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -649,7 +712,7 @@ _Search3:
|
|||
}
|
||||
}
|
||||
|
||||
LZ4_encodeSequence(&ip, &op, &anchor, ml, ref);
|
||||
if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ref, limit, oend)) return 0;
|
||||
ip = start3;
|
||||
ref = ref3;
|
||||
ml = ml3;
|
||||
|
@ -688,7 +751,7 @@ _Search3:
|
|||
ml = (int)(start2 - ip);
|
||||
}
|
||||
}
|
||||
LZ4_encodeSequence(&ip, &op, &anchor, ml, ref);
|
||||
if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ref, limit, oend)) return 0;
|
||||
|
||||
ip = start2;
|
||||
ref = ref2;
|
||||
|
@ -705,8 +768,9 @@ _Search3:
|
|||
// Encode Last Literals
|
||||
{
|
||||
int lastRun = (int)(iend - anchor);
|
||||
if ((limit) && (((char*)op - dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize)) return 0; // Check output limit
|
||||
if (lastRun>=(int)RUN_MASK) { *op++=(RUN_MASK<<ML_BITS); lastRun-=RUN_MASK; for(; lastRun > 254 ; lastRun-=255) *op++ = 255; *op++ = (BYTE) lastRun; }
|
||||
else *op++ = (lastRun<<ML_BITS);
|
||||
else *op++ = (BYTE)(lastRun<<ML_BITS);
|
||||
memcpy(op, anchor, iend - anchor);
|
||||
op += iend-anchor;
|
||||
}
|
||||
|
@ -716,15 +780,38 @@ _Search3:
|
|||
}
|
||||
|
||||
|
||||
int LZ4_compressHC(const char* source,
|
||||
char* dest,
|
||||
int isize)
|
||||
int LZ4_compressHC(const char* source, char* dest, int inputSize)
|
||||
{
|
||||
void* ctx = LZ4HC_Create((const BYTE*)source);
|
||||
int result = LZ4_compressHCCtx(ctx, source, dest, isize);
|
||||
LZ4HC_Free (&ctx);
|
||||
void* ctx = LZ4_createHC(source);
|
||||
int result;
|
||||
if (ctx==NULL) return 0;
|
||||
|
||||
result = LZ4HC_compress_generic (ctx, source, dest, inputSize, 0, noLimit);
|
||||
|
||||
LZ4_freeHC(ctx);
|
||||
return result;
|
||||
}
|
||||
|
||||
int LZ4_compressHC_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize)
|
||||
{
|
||||
return LZ4HC_compress_generic (LZ4HC_Data, source, dest, inputSize, 0, noLimit);
|
||||
}
|
||||
|
||||
|
||||
int LZ4_compressHC_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize)
|
||||
{
|
||||
void* ctx = LZ4_createHC(source);
|
||||
int result;
|
||||
if (ctx==NULL) return 0;
|
||||
|
||||
result = LZ4HC_compress_generic (ctx, source, dest, inputSize, maxOutputSize, limitedOutput);
|
||||
|
||||
LZ4_freeHC(ctx);
|
||||
return result;
|
||||
}
|
||||
|
||||
int LZ4_compressHC_limitedOutput_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int maxOutputSize)
|
||||
{
|
||||
return LZ4HC_compress_generic (LZ4HC_Data, source, dest, inputSize, maxOutputSize, limitedOutput);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
LZ4 HC - High Compression Mode of LZ4
|
||||
Header File
|
||||
Copyright (C) 2011-2012, Yann Collet.
|
||||
Copyright (C) 2011-2013, Yann Collet.
|
||||
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -39,19 +39,70 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
|
||||
int LZ4_compressHC (const char* source, char* dest, int isize);
|
||||
|
||||
int LZ4_compressHC (const char* source, char* dest, int inputSize);
|
||||
/*
|
||||
LZ4_compressHC :
|
||||
return : the number of bytes in compressed buffer dest
|
||||
note : destination buffer must be already allocated.
|
||||
To avoid any problem, size it to handle worst cases situations (input data not compressible)
|
||||
Worst case size evaluation is provided by function LZ4_compressBound() (see "lz4.h")
|
||||
return : the number of bytes in compressed buffer dest
|
||||
or 0 if compression fails.
|
||||
note : destination buffer must be already allocated.
|
||||
To avoid any problem, size it to handle worst cases situations (input data not compressible)
|
||||
Worst case size evaluation is provided by function LZ4_compressBound() (see "lz4.h")
|
||||
*/
|
||||
|
||||
int LZ4_compressHC_limitedOutput (const char* source, char* dest, int inputSize, int maxOutputSize);
|
||||
/*
|
||||
LZ4_compress_limitedOutput() :
|
||||
Compress 'inputSize' bytes from 'source' into an output buffer 'dest' of maximum size 'maxOutputSize'.
|
||||
If it cannot achieve it, compression will stop, and result of the function will be zero.
|
||||
This function never writes outside of provided output buffer.
|
||||
|
||||
inputSize : Max supported value is 1 GB
|
||||
maxOutputSize : is maximum allowed size into the destination buffer (which must be already allocated)
|
||||
return : the number of output bytes written in buffer 'dest'
|
||||
or 0 if compression fails.
|
||||
*/
|
||||
|
||||
|
||||
/* Note :
|
||||
Decompression functions are provided within regular LZ4 source code (see "lz4.h") (BSD license)
|
||||
Decompression functions are provided within LZ4 source code (see "lz4.h") (BSD license)
|
||||
*/
|
||||
|
||||
|
||||
/* Advanced Functions */
|
||||
|
||||
void* LZ4_createHC (const char* inputBuffer);
|
||||
int LZ4_compressHC_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize);
|
||||
int LZ4_compressHC_limitedOutput_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int maxOutputSize);
|
||||
char* LZ4_slideInputBufferHC (void* LZ4HC_Data);
|
||||
int LZ4_freeHC (void* LZ4HC_Data);
|
||||
|
||||
/*
|
||||
These functions allow the compression of dependent blocks, where each block benefits from prior 64 KB within preceding blocks.
|
||||
In order to achieve this, it is necessary to start creating the LZ4HC Data Structure, thanks to the function :
|
||||
|
||||
void* LZ4_createHC (const char* inputBuffer);
|
||||
The result of the function is the (void*) pointer on the LZ4HC Data Structure.
|
||||
This pointer will be needed in all other functions.
|
||||
If the pointer returned is NULL, then the allocation has failed, and compression must be aborted.
|
||||
The only parameter 'const char* inputBuffer' must, obviously, point at the beginning of input buffer.
|
||||
The input buffer must be already allocated, and size at least 192KB.
|
||||
'inputBuffer' will also be the 'const char* source' of the first block.
|
||||
|
||||
All blocks are expected to lay next to each other within the input buffer, starting from 'inputBuffer'.
|
||||
To compress each block, use either LZ4_compressHC_continue() or LZ4_compressHC_limitedOutput_continue().
|
||||
Their behavior are identical to LZ4_compressHC() or LZ4_compressHC_limitedOutput(),
|
||||
but require the LZ4HC Data Structure as their first argument, and check that each block starts right after the previous one.
|
||||
If next block does not begin immediately after the previous one, the compression will fail (return 0).
|
||||
|
||||
When it's no longer possible to lay the next block after the previous one (not enough space left into input buffer), a call to :
|
||||
char* LZ4_slideInputBufferHC(void* LZ4HC_Data);
|
||||
must be performed. It will typically copy the latest 64KB of input at the beginning of input buffer.
|
||||
Note that, for this function to work properly, minimum size of an input buffer must be 192KB.
|
||||
==> The memory position where the next input data block must start is provided as the result of the function.
|
||||
|
||||
Compression can then resume, using LZ4_compressHC_continue() or LZ4_compressHC_limitedOutput_continue(), as usual.
|
||||
|
||||
When compression is completed, a call to LZ4_freeHC() will release the memory used by the LZ4HC Data Structure.
|
||||
*/
|
||||
|
||||
|
||||
|
|
19
ext/tap-mac/README.txt
Normal file
19
ext/tap-mac/README.txt
Normal file
|
@ -0,0 +1,19 @@
|
|||
This is a hack of tuntaposx. It's here for two reasons:
|
||||
|
||||
1) There seem to be issues with large MTUs in the original tuntap code,
|
||||
so we set up our zt0 tap with the correct ZeroTier MTU as the default.
|
||||
|
||||
2) Lots of other mac products (VPNs, etc.) ship their own tap device
|
||||
drivers that like to conflict with one another. This gives us no
|
||||
choice but to play along. But we call our tap device zt0, which means
|
||||
it won't conflict with everyone else's tap0.
|
||||
|
||||
3) It's nice to call the device zt0, same as Linux, for consistency across
|
||||
*nix platforms. Mac does not seem to support interface renaming.
|
||||
|
||||
This will be placed in the ZeroTier home as a kext and is auto-loaded by the
|
||||
ZeroTier One binary if /dev/zt0 is not found. It can also be auto-updated.
|
||||
|
||||
See this page for the original:
|
||||
|
||||
http://tuntaposx.sourceforge.net
|
141
ext/tap-mac/tuntap/Changelog
Normal file
141
ext/tap-mac/tuntap/Changelog
Normal file
|
@ -0,0 +1,141 @@
|
|||
|
||||
November 1, 2011:
|
||||
o Make the netmask address family fix work without knowledge of the struct
|
||||
ifaddr definition. This fixes a crash on Lion, where the layout of the
|
||||
structure has been changed, but at the cost of the fix no longer working for
|
||||
IPv6. I think this is OK though, since mDNSResponder has been fixed on
|
||||
Leopard and beyond to no longer require the hack.
|
||||
o Proper multicast address checking for tun; multicast should now work reliably
|
||||
with IP and IPv6 on tun also.
|
||||
o A quite comprehensive test suite has been added that allows for quick release
|
||||
testing.
|
||||
o PPC support has been dropped due to XCode 4 no longer supporting PPC arch.
|
||||
|
||||
September 13, 2009:
|
||||
o Change linker options to produce 64 bit kext bundle for Snow Leopard.
|
||||
o Switch from kmem_alloc and friends to OSAlloc for memory allocation and
|
||||
avoid the delay() call. Respective symbols are not available on 64 bit
|
||||
kernels anymore.
|
||||
|
||||
September 5, 2009:
|
||||
o Initial Snow Leopard port. Thanks to various people contributing patches in
|
||||
the bugtracker. The new official version can only be compiled on Snow
|
||||
Leopard but has been tested to work on all Tiger, Leopard and Snow Leopard
|
||||
o Clean up unused locking code and switch to rwlocks even for simple mutexes,
|
||||
which avoids a symbol incompatibility for Tiger and Leopard.
|
||||
o Clean up compilation flags in the Makefiles.
|
||||
|
||||
July 4, 2008:
|
||||
o Adapt the former Leopard package to also be installable on Tiger systems.
|
||||
This obsoletes the Tiger version, both Leopard and Tiger are now supported
|
||||
by a single package.
|
||||
|
||||
June 7, 2008:
|
||||
o Protect the selwakeup() call by the lock. This fixes incorrect select()
|
||||
behaviour, thanks to Roland Wendelin for reporting this.
|
||||
o Fix tuntap_mbuf_queue::size initialization
|
||||
o Use a proper wait condition for synchronization when detaching the network
|
||||
interface. The old code would crash if the if_detached() handler was called
|
||||
from a different thread than unregister_interface().
|
||||
|
||||
January 21, 2008:
|
||||
o Work around an issue in the Darwin kernel. When unregistering an interface,
|
||||
addresses are not properly removed from the interface. This leads to
|
||||
crashes and other problems when reusing the interface. Introduce an ugly
|
||||
hack that tries to remove all interface addresses when shutting the
|
||||
interface down.
|
||||
o Fix a small mbuf leak that could occur when the output queue was full.
|
||||
Thanks to Oleg Dolgov for reporting this.
|
||||
|
||||
December 21, 2007:
|
||||
o Fix paths in the startup item postflight scripts
|
||||
o Check if_ioctl arguments more defensively after a report of a panic after
|
||||
receiving a NULL arg.
|
||||
|
||||
November 14, 2007:
|
||||
o I have done a complete rework of the installer package generation. The
|
||||
package is now edited in PackageMaker. The distribution package can still
|
||||
be built from the commandline though.
|
||||
o Fix incorrect permission & ownership of the installed files.
|
||||
|
||||
Oktober 11, 2007:
|
||||
o Fix the permissions of the postflight scripts. Installer packages should work
|
||||
again.
|
||||
o Drop the kmod and kmodc++ in the linker command, they seem to be unneeded
|
||||
with Leopard.
|
||||
|
||||
September 20, 2007:
|
||||
o Initial Leopard port, it's basically the latest Tiger version with some
|
||||
Leopard-related fixes and s/Tiger/Leopard/g
|
||||
o I have switched to a proper version management system (git) and could
|
||||
remove some of the CVS hacks subsequently.
|
||||
o The installation packages have been reworked a bit, they now install into
|
||||
/System/Extensions and /System/StartupItems directly by using
|
||||
DestinationPaths.
|
||||
|
||||
May 13, 2006:
|
||||
o This version is not stable, it may crash, sorry.
|
||||
o Universal binaries that run on ppc and intel macs.
|
||||
o Adds tap MAC address randomization
|
||||
o Redesigned locking.
|
||||
o Better multicast support
|
||||
o mDNSResponder workaround, so that the tap interfaces should get picked up
|
||||
now. Note that we are fixing ifconfig/kernel behaviour here.
|
||||
o All tapX and tunX devices are visible in /dev at all times, network
|
||||
interfaces still created dynamically, though.
|
||||
o Startup items moved to /Library/StartupItems
|
||||
|
||||
May 17, 2005:
|
||||
o Initial Tiger port. We now have KPI-style interfaces. I guess the Tiger
|
||||
version is little slower than the Panther version because of all the
|
||||
wrapping and hiding in the kernel.
|
||||
o The kernel extensions moved to /Library/Extensions. That is the place where
|
||||
non-Apple kexts are supposed to live.
|
||||
|
||||
April 21, 2005:
|
||||
o I added support in tun for AF prepending like some BSDs do. Thanks to Dennis
|
||||
kSchneider for mailing the initial patch. You can also set the value of
|
||||
AF_INET6 to be used.
|
||||
o I finally found that major bug causing crashes (especially on multiprocessor
|
||||
machines). It also caused a memory leak (lost mbufs), and might have caused
|
||||
performance/througput/data-loss problems. Everyone is recommended to upgrade.
|
||||
|
||||
April 6, 2005:
|
||||
o I rewrote the common part concerning the tun and tap initialization and
|
||||
cleanup. This should make the code more maintainable (less duplication).
|
||||
o The devices now reinitialize to the state they were started in when they
|
||||
are closed by an application. This concerns IP addresses for example.
|
||||
o I changed the package building system to use PackageMaker.app in batch
|
||||
mode. The packages also check for version 10.3 now, so nobody should be
|
||||
able to install tun/tap on 10.2 using installer packages. Furthermore I
|
||||
have sprinkled some warnings telling you not to use tun/tap on SMP machines
|
||||
over the installation process ;-)
|
||||
o Some minor locking fixes.
|
||||
|
||||
November 19, 2004:
|
||||
o Jamie Wood reported that the packet queue in the driver could be considered
|
||||
empty even if there were packets in it. This was probably caused by a
|
||||
synchronization problem that should be fixed now. People encountering
|
||||
timeouts etc. should try the new version.
|
||||
o I finally implemented support for changing the interface MTU. The driver
|
||||
enforces the MTU when writing packets to the character device now. However,
|
||||
packets coming from the kernel are not checked.
|
||||
|
||||
September 9, 2004:
|
||||
o Marcello Teodori told me that the tun driver wasn't working with openvpn.
|
||||
The problem was the fcntl call, fixed that. Should work now. Thanks
|
||||
Marcello!
|
||||
o changed the tun driver not to prepend the address family field before each
|
||||
and every packet (which is the behaviour of OpenBSD). As there is currently
|
||||
only IPv4 and IPv6 support there is no problem with the standard tun
|
||||
approach used on other OSes. This should make the driver much more
|
||||
compatible.
|
||||
o Did a script and makefile support so that the installer packages can now be
|
||||
built from the command prompt. Unfortunately this might break things
|
||||
someday as I am not using the 'official' way to build the packages
|
||||
o Cleaned up installer packages a little.
|
||||
|
||||
August 24, 2004:
|
||||
o initial version put online
|
||||
o basic tun/tap support, tap working with qemu
|
||||
|
12
ext/tap-mac/tuntap/Makefile
Normal file
12
ext/tap-mac/tuntap/Makefile
Normal file
|
@ -0,0 +1,12 @@
|
|||
TUNTAP_VERSION = 20131028
|
||||
BASE=
|
||||
|
||||
all: tap.kext
|
||||
|
||||
clean:
|
||||
cd src/tap && make -f Makefile clean
|
||||
|
||||
tap.kext:
|
||||
cd src/tap && make TUNTAP_VERSION=$(TUNTAP_VERSION) -f Makefile all
|
||||
|
||||
.PHONY: test
|
85
ext/tap-mac/tuntap/README
Normal file
85
ext/tap-mac/tuntap/README
Normal file
|
@ -0,0 +1,85 @@
|
|||
|
||||
tun/tap driver for Mac OS X
|
||||
===========================
|
||||
|
||||
This is an experimental IP tunnel/ethertap driver for Mac OS X/Darwin. It
|
||||
provides /dev/tunX and /dev/tapX devices. The maximum number of devices can be
|
||||
configured at compile time, it is currently set to 16. That should be enough in
|
||||
most cases.
|
||||
|
||||
The driver ships as two kernel extensions, one for tap and one for tun. They are
|
||||
located in /Library/Extensions and can also be loaded and unloaded by hand. If
|
||||
you install the startup item, the system will load them automatically at
|
||||
startup (tun and tap startup items get installed in /Library/StartupItems).
|
||||
|
||||
Operation & Programming notes
|
||||
=============================
|
||||
|
||||
tapX are ethertap devices which provide an interface to the kernel's ethernet
|
||||
layer. Packets can be read from and written to the /dev/tapX character devices
|
||||
one at a time (same name as the interface that shows up in ifconfig).
|
||||
|
||||
tunX are IP tunnel devices. These can be used to exchange IP packets with the
|
||||
kernel. You will get single packets for each read() and should write() packets
|
||||
one at a time to /dev/tunX.
|
||||
|
||||
There are some special ioctls with the tun devices that allow you to have them
|
||||
prepend the address family of the packet when reading it from /dev/tunX. Using
|
||||
this mode the driver also expects you put this 4-byte address family field
|
||||
(network byte order) in front of the packets you write to /dev/tunX.
|
||||
|
||||
Here are the ioctls to setup up address prepending mode (for convenience there
|
||||
also is a header called tun_ioctls.h in the source package that you can use)
|
||||
Set the int argument to one if you want to have AF prepending, use 0 if you want
|
||||
to switch it off.
|
||||
|
||||
#define TUNSIFHEAD _IOW('t', 96, int)
|
||||
#define TUNGIFHEAD _IOR('t', 97, int)
|
||||
|
||||
Prepending mode is off by default. Currently it is not recommended to switch the
|
||||
mode while packets are in flight on the device.
|
||||
|
||||
The character devices are always visible in the filesystem as /dev/tunX and
|
||||
/dev/tapX. The number of available character devices is a compile time constant
|
||||
and is currently fixed to 16. Each character devices is associated with a
|
||||
network interface of the same name. The network interfaces are only created when
|
||||
the corresponding character device is opened by a program and will be removed
|
||||
when the character device is closed.
|
||||
|
||||
The character devices currently provide a pretty minimal interface. Whole
|
||||
packets are read and written using a singe read/write call. File descriptors
|
||||
opened on the devices can also be select()ed and support O_NONBLOCK.
|
||||
Asynchronous i/o and some ioctls are currently unimplemented, but implementing
|
||||
them shouldn't be very hard. Do it yourself or contact me if you can't live
|
||||
without.
|
||||
|
||||
There is another limitation imposed by the Darwin 8 kernel. It concerns the
|
||||
poll() system call; Darwin currently does *not* support that for (character)
|
||||
devices. Use select() instead.
|
||||
|
||||
The interfaces can be configured using ifconfig, the tap devices also support
|
||||
setting the MAC address to be used. Both tun and tap should be ready for IPv6.
|
||||
Just setup addresses and routing as you would do with other interfaces.
|
||||
|
||||
Please contact me if you find any bugs or have suggestions.
|
||||
|
||||
Enjoy!
|
||||
|
||||
Mattias
|
||||
<mattias.nissler@gmx.de>
|
||||
|
||||
|
||||
Uninstalling
|
||||
============
|
||||
|
||||
The installer packages for OS X currently don't have support for uninstall as
|
||||
the installer doesn't provide it. Remove the following directories if you want
|
||||
to completely remove the files installed:
|
||||
|
||||
/Library/Extensions/tap.kext
|
||||
/Library/Extensions/tun.kext
|
||||
/Library/StartupItems/tap
|
||||
/Library/StartupItems/tun
|
||||
|
||||
Unload the the kernel extensions or reboot and you're done.
|
||||
|
18
ext/tap-mac/tuntap/README.zerotier-build
Normal file
18
ext/tap-mac/tuntap/README.zerotier-build
Normal file
|
@ -0,0 +1,18 @@
|
|||
Building the tap for both x86_64 and i386 requires an older version of the
|
||||
Xcode tools than what now ships for Mavericks (10.9). The newer version
|
||||
does not support creating i386 kernel images.
|
||||
|
||||
These can be obtained from:
|
||||
|
||||
https://developer.apple.com/downloads
|
||||
|
||||
It requires a bit of a dance to unpack the package and obtain an unpacked
|
||||
tree, but once it's there you can change the line in tap/Makefile and
|
||||
build for both architectures.
|
||||
|
||||
This will go on until i386 is thoroughly legacy, at which point we'll
|
||||
probably start just supporting x86_64. But that might be a while. We want
|
||||
to support old Macs through their entire useful life.
|
||||
|
||||
Since this build is irritating, a pre-built copy is packaged in ext/ and
|
||||
is installed by 'make install'. So users shouldn't have to build this.
|
201
ext/tap-mac/tuntap/src/lock.cc
Normal file
201
ext/tap-mac/tuntap/src/lock.cc
Normal file
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* ip tunnel/ethertap device for MacOSX.
|
||||
*
|
||||
* Locking implementation.
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "lock.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include <sys/syslog.h>
|
||||
#include <sys/proc.h>
|
||||
|
||||
}
|
||||
|
||||
#if 0
|
||||
#define dprintf(...) log(LOG_INFO, __VA_ARGS__)
|
||||
#else
|
||||
#define dprintf(...)
|
||||
#endif
|
||||
|
||||
/* class tt_lock */
|
||||
lck_grp_t *tt_lock::tt_lck_grp = NULL;
|
||||
|
||||
bool
|
||||
tt_lock::initialize()
|
||||
{
|
||||
/* init if necessary */
|
||||
if (tt_lck_grp == NULL) {
|
||||
dprintf("initing lock group\n");
|
||||
tt_lck_grp = lck_grp_alloc_init("tuntap locks", LCK_GRP_ATTR_NULL);
|
||||
|
||||
if (tt_lck_grp == NULL) {
|
||||
/* if something fails, the lock won't work */
|
||||
log(LOG_ERR, "tuntap: could not allocate locking group\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
tt_lock::shutdown()
|
||||
{
|
||||
/* free the locking group */
|
||||
if (tt_lck_grp != NULL) {
|
||||
dprintf("freeing lock group\n");
|
||||
lck_grp_free(tt_lck_grp);
|
||||
tt_lck_grp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* tt_mutex */
|
||||
tt_mutex::tt_mutex()
|
||||
{
|
||||
/* fail if locking group not initialized */
|
||||
if (tt_lck_grp == NULL)
|
||||
return;
|
||||
|
||||
/* allocate the lock */
|
||||
lck = lck_rw_alloc_init(tt_lck_grp, NULL);
|
||||
|
||||
if (lck == NULL)
|
||||
log(LOG_ERR, "tuntap: could not allocate mutex\n");
|
||||
}
|
||||
|
||||
tt_mutex::~tt_mutex()
|
||||
{
|
||||
/* if the lock doesn't exist, this will be a no-op */
|
||||
if (lck == NULL)
|
||||
return;
|
||||
|
||||
/* free the lock */
|
||||
lck_rw_free(lck, tt_lck_grp);
|
||||
}
|
||||
|
||||
void
|
||||
tt_mutex::lock()
|
||||
{
|
||||
if (lck != NULL)
|
||||
lck_rw_lock_exclusive(lck);
|
||||
}
|
||||
|
||||
void
|
||||
tt_mutex::unlock()
|
||||
{
|
||||
if (lck != NULL)
|
||||
lck_rw_unlock_exclusive(lck);
|
||||
}
|
||||
|
||||
void
|
||||
tt_mutex::sleep(void *cond)
|
||||
{
|
||||
if (lck != NULL)
|
||||
lck_rw_sleep(lck, LCK_SLEEP_DEFAULT, cond, THREAD_INTERRUPTIBLE);
|
||||
}
|
||||
|
||||
void
|
||||
tt_mutex::sleep(void *cond, uint64_t timeout)
|
||||
{
|
||||
if (lck != NULL)
|
||||
lck_rw_sleep_deadline(lck, LCK_SLEEP_DEFAULT, cond, THREAD_INTERRUPTIBLE, timeout);
|
||||
}
|
||||
|
||||
void
|
||||
tt_mutex::wakeup(void *cond)
|
||||
{
|
||||
if (lck != NULL)
|
||||
::wakeup(cond);
|
||||
}
|
||||
|
||||
/* tt_gate */
|
||||
tt_gate::tt_gate()
|
||||
: ticket_number(0),
|
||||
population(0)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
tt_gate::enter()
|
||||
{
|
||||
/* just try to grab the lock, increase the ticket number and the population */
|
||||
auto_lock l(&slock);
|
||||
ticket_number++;
|
||||
population++;
|
||||
}
|
||||
|
||||
void
|
||||
tt_gate::exit()
|
||||
{
|
||||
auto_lock l(&slock);
|
||||
ticket_number--;
|
||||
population--;
|
||||
}
|
||||
|
||||
bool
|
||||
tt_gate::is_anyone_in()
|
||||
{
|
||||
return population != 0;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
tt_gate::get_ticket_number()
|
||||
{
|
||||
return ticket_number;
|
||||
}
|
||||
|
||||
void
|
||||
tt_gate::lock()
|
||||
{
|
||||
slock.lock();
|
||||
}
|
||||
|
||||
void
|
||||
tt_gate::unlock()
|
||||
{
|
||||
slock.unlock();
|
||||
}
|
||||
|
||||
void
|
||||
tt_gate::sleep(void* cond)
|
||||
{
|
||||
slock.sleep(cond);
|
||||
}
|
||||
|
||||
void
|
||||
tt_gate::sleep(void* cond, uint64_t timeout)
|
||||
{
|
||||
slock.sleep(cond, timeout);
|
||||
}
|
||||
|
||||
void
|
||||
tt_gate::wakeup(void* cond)
|
||||
{
|
||||
slock.wakeup(cond);
|
||||
}
|
||||
|
160
ext/tap-mac/tuntap/src/lock.h
Normal file
160
ext/tap-mac/tuntap/src/lock.h
Normal file
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* ip tunnel/ethertap device for MacOSX.
|
||||
*
|
||||
* Locking is not as straightforward for Tiger. So declare our own locking class.
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __LOCK_H__
|
||||
#define __LOCK_H__
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include <kern/locks.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
}
|
||||
|
||||
/* our own locking class. declares the common interface of the locking primitives. */
|
||||
class tt_lock {
|
||||
|
||||
protected:
|
||||
/* locking group */
|
||||
static lck_grp_t *tt_lck_grp;
|
||||
|
||||
public:
|
||||
/* be virtual */
|
||||
virtual ~tt_lock() { };
|
||||
|
||||
/* static intialization (inits the locking group) */
|
||||
static bool initialize();
|
||||
static void shutdown();
|
||||
|
||||
/* locking */
|
||||
virtual void lock() = 0;
|
||||
virtual void unlock() = 0;
|
||||
|
||||
/* monitor primitives */
|
||||
virtual void sleep(void* cond) = 0;
|
||||
virtual void sleep(void* cond, uint64_t) = 0;
|
||||
virtual void wakeup(void* cond) = 0;
|
||||
};
|
||||
|
||||
/* simple mutex */
|
||||
class tt_mutex : public tt_lock {
|
||||
|
||||
private:
|
||||
/* underlying darwin lock */
|
||||
lck_rw_t *lck;
|
||||
|
||||
public:
|
||||
tt_mutex();
|
||||
virtual ~tt_mutex();
|
||||
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
/* monitor primitives */
|
||||
void sleep(void* cond);
|
||||
void sleep(void* cond, uint64_t);
|
||||
void wakeup(void* cond);
|
||||
};
|
||||
|
||||
/* A very special locking class that we use to track threads that enter and leave the character
|
||||
* device service functions. They call enter() before entering the actual service routinge and
|
||||
* exit() when done. enter() only permits them to pass when the gate isn't locked. Furthermore, the
|
||||
* gate assigns ticket numbers to everyone that passes the gate, so you can check whether more
|
||||
* threads came through. See tuntap_mgr::shutdown() for how we use that stuff.
|
||||
*/
|
||||
class tt_gate : public tt_lock {
|
||||
|
||||
private:
|
||||
/* synchronization lock */
|
||||
tt_mutex slock;
|
||||
/* ticket number */
|
||||
unsigned int ticket_number;
|
||||
/* count of threads that are in */
|
||||
unsigned int population;
|
||||
|
||||
public:
|
||||
/* construct a new gate */
|
||||
tt_gate();
|
||||
|
||||
/* enter - pass the gate */
|
||||
void enter();
|
||||
/* exit - pass the gate */
|
||||
void exit();
|
||||
|
||||
/* check whether anyone is in */
|
||||
bool is_anyone_in();
|
||||
/* gets the next ticket number */
|
||||
unsigned int get_ticket_number();
|
||||
|
||||
/* lock the gate */
|
||||
void lock();
|
||||
/* unlock the gate */
|
||||
void unlock();
|
||||
|
||||
/* monitor primitives */
|
||||
void sleep(void* cond);
|
||||
void sleep(void* cond, uint64_t);
|
||||
void wakeup(void* cond);
|
||||
};
|
||||
|
||||
/* auto_lock and auto_rwlock serve as automatic lock managers: Create an object, passing the
|
||||
* tt_[rw]lock you want to lock to have it grab the lock. When the object goes out of scope, the
|
||||
* destructor of the class will release the lock.
|
||||
*/
|
||||
class auto_lock {
|
||||
|
||||
protected:
|
||||
/* the lock we hold */
|
||||
tt_lock *l;
|
||||
|
||||
public:
|
||||
auto_lock(tt_lock *m)
|
||||
: l(m)
|
||||
{
|
||||
lock();
|
||||
}
|
||||
|
||||
~auto_lock()
|
||||
{
|
||||
unlock();
|
||||
}
|
||||
|
||||
void lock()
|
||||
{
|
||||
l->lock();
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
l->unlock();
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* __LOCK_H__ */
|
||||
|
76
ext/tap-mac/tuntap/src/mem.cc
Normal file
76
ext/tap-mac/tuntap/src/mem.cc
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* ip tunnel/ethertap device for MacOSX. Common functionality of tap_interface and tun_interface.
|
||||
*
|
||||
* Memory management implementation.
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "mem.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include <libkern/OSMalloc.h>
|
||||
|
||||
}
|
||||
|
||||
#if 0
|
||||
#define dprintf(...) log(LOG_INFO, __VA_ARGS__)
|
||||
#else
|
||||
#define dprintf(...)
|
||||
#endif
|
||||
|
||||
static int inited = 0;
|
||||
static OSMallocTag tag;
|
||||
|
||||
void
|
||||
mem_initialize(const char* name) {
|
||||
|
||||
if (!inited) {
|
||||
tag = OSMalloc_Tagalloc(name, OSMT_DEFAULT);
|
||||
inited = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mem_shutdown() {
|
||||
|
||||
if (inited) {
|
||||
OSMalloc_Tagfree(tag);
|
||||
inited = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void *
|
||||
mem_alloc(uint32_t size) {
|
||||
|
||||
return OSMalloc(size, tag);
|
||||
}
|
||||
|
||||
void
|
||||
mem_free(void *addr, uint32_t size) {
|
||||
|
||||
OSFree(addr, size, tag);
|
||||
}
|
||||
|
48
ext/tap-mac/tuntap/src/mem.h
Normal file
48
ext/tap-mac/tuntap/src/mem.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* ip tunnel/ethertap device for MacOSX. Common functionality of tap_interface and tun_interface.
|
||||
*
|
||||
* Memory management.
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __MEM_H__
|
||||
#define __MEM_H__
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
}
|
||||
|
||||
/* Memory manager initalization and shutdown */
|
||||
void mem_initialize(const char *name);
|
||||
void mem_shutdown();
|
||||
|
||||
/* Memory allocation functions */
|
||||
void *mem_alloc(uint32_t size);
|
||||
void mem_free(void *addr, uint32_t size);
|
||||
|
||||
#endif /* __MEM_H__ */
|
||||
|
36
ext/tap-mac/tuntap/src/tap/Info.plist
Normal file
36
ext/tap-mac/tuntap/src/tap/Info.plist
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>@@CFBUNDLEDEVELOPMENTREGION@@</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>@@CFBUNDLEEXECUTABLE@@</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>@@CFBUNDLEIDENTIFIER@@</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>@@CFBUNDLEEXECUTABLE@@</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>@@CFBUNDLEPACKAGETYPE@@</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>@@CFBUNDLEVERSION@@</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>@@CFBUNDLESIGNATURE@@</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>OSBundleLibraries</key>
|
||||
<dict>
|
||||
<key>com.apple.kpi.mach</key>
|
||||
<string>8.0</string>
|
||||
<key>com.apple.kpi.bsd</key>
|
||||
<string>8.0</string>
|
||||
<key>com.apple.kpi.libkern</key>
|
||||
<string>8.0</string>
|
||||
<key>com.apple.kpi.unsupported</key>
|
||||
<string>8.0</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
60
ext/tap-mac/tuntap/src/tap/Makefile
Normal file
60
ext/tap-mac/tuntap/src/tap/Makefile
Normal file
|
@ -0,0 +1,60 @@
|
|||
#
|
||||
# ethertap driver for MacOSX
|
||||
#
|
||||
# Makefile
|
||||
#
|
||||
# (c) 2004, 2005, 2006, 2007, 2008 Mattias Nissler
|
||||
#
|
||||
|
||||
OBJS = ../tuntap.o ../tuntap_mgr.o ../lock.o ../mem.o kmod.o tap.o
|
||||
KMOD_BIN = tap
|
||||
BUNDLE_DIR = ../..
|
||||
BUNDLE_NAME = tap.kext
|
||||
|
||||
TAP_KEXT_VERSION = $(TUNTAP_VERSION)
|
||||
|
||||
BUNDLE_REGION = English
|
||||
BUNDLE_IDENTIFIER = com.zerotier.tap
|
||||
BUNDLE_SIGNATURE = ????
|
||||
BUNDLE_PACKAGETYPE = KEXT
|
||||
BUNDLE_VERSION = $(TAP_KEXT_VERSION)
|
||||
|
||||
INCLUDE = -I.. -I/System/Library/Frameworks/Kernel.framework/Headers -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk/System/Library/Frameworks/Kernel.framework/Headers
|
||||
CFLAGS = -Wall -mkernel -force_cpusubtype_ALL \
|
||||
-fno-builtin -fno-stack-protector -arch i386 -arch x86_64 \
|
||||
-DKERNEL -D__APPLE__ -DKERNEL_PRIVATE -DTUNTAP_VERSION=\"$(TUNTAP_VERSION)\" \
|
||||
-DTAP_KEXT_VERSION=\"$(TAP_KEXT_VERSION)\"
|
||||
CCFLAGS = $(CFLAGS)
|
||||
LDFLAGS = -Wall -mkernel -nostdlib -r -lcc_kext -arch i386 -arch x86_64 -Xlinker -kext
|
||||
|
||||
#CCP = g++
|
||||
#CC = gcc
|
||||
CCP = $(HOME)/Code/llvm-g++-Xcode4.6.2/bin/llvm-g++
|
||||
CC = $(HOME)/Code/llvm-g++-Xcode4.6.2/bin/llvm-gcc
|
||||
|
||||
all: $(KMOD_BIN) bundle
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
|
||||
.cc.o:
|
||||
$(CCP) $(CCFLAGS) $(INCLUDE) -c $< -o $@
|
||||
|
||||
$(KMOD_BIN): $(OBJS)
|
||||
$(CCP) $(LDFLAGS) -o $(KMOD_BIN) $(OBJS)
|
||||
|
||||
bundle: $(KMOD_BIN)
|
||||
rm -rf $(BUNDLE_DIR)/$(BUNDLE_NAME)
|
||||
mkdir -p $(BUNDLE_DIR)/$(BUNDLE_NAME)/Contents/MacOS
|
||||
cp $(KMOD_BIN) $(BUNDLE_DIR)/$(BUNDLE_NAME)/Contents/MacOS
|
||||
sed -e "s/@@CFBUNDLEEXECUTABLE@@/$(KMOD_BIN)/" \
|
||||
-e "s/@@CFBUNDLEDEVELOPMENTREGION@@/$(BUNDLE_REGION)/" \
|
||||
-e "s/@@CFBUNDLEIDENTIFIER@@/$(BUNDLE_IDENTIFIER)/" \
|
||||
-e "s/@@CFBUNDLESIGNATURE@@/$(BUNDLE_SIGNATURE)/" \
|
||||
-e "s/@@CFBUNDLEPACKAGETYPE@@/$(BUNDLE_PACKAGETYPE)/" \
|
||||
-e "s/@@CFBUNDLEVERSION@@/$(BUNDLE_VERSION)/" \
|
||||
Info.plist > $(BUNDLE_DIR)/$(BUNDLE_NAME)/Contents/Info.plist
|
||||
|
||||
clean:
|
||||
-rm -f $(OBJS) $(KMOD_BIN)
|
||||
-rm -rf $(BUNDLE_DIR)/$(BUNDLE_NAME)
|
||||
|
93
ext/tap-mac/tuntap/src/tap/kmod.cc
Normal file
93
ext/tap-mac/tuntap/src/tap/kmod.cc
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* ethertap device for MacOSX.
|
||||
*
|
||||
* Kext definition (it is a mach kmod really...)
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "tap.h"
|
||||
#include "mem.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include <sys/param.h>
|
||||
|
||||
#include <mach/kmod.h>
|
||||
|
||||
static tap_manager *mgr;
|
||||
|
||||
/*
|
||||
* start function. called when the kext gets loaded.
|
||||
*/
|
||||
static kern_return_t tap_module_start(struct kmod_info *ki, void *data)
|
||||
{
|
||||
mem_initialize(TAP_FAMILY_NAME);
|
||||
|
||||
/* initialize locking */
|
||||
if (!tt_lock::initialize())
|
||||
return KMOD_RETURN_FAILURE;
|
||||
|
||||
/* create a tap manager that will handle the rest */
|
||||
mgr = new tap_manager();
|
||||
|
||||
if (mgr != NULL) {
|
||||
if (mgr->initialize(TAP_IF_COUNT, (char *) TAP_FAMILY_NAME))
|
||||
return KMOD_RETURN_SUCCESS;
|
||||
|
||||
delete mgr;
|
||||
mgr = NULL;
|
||||
/* clean up locking */
|
||||
tt_lock::shutdown();
|
||||
}
|
||||
|
||||
return KMOD_RETURN_FAILURE;
|
||||
}
|
||||
|
||||
/*
|
||||
* stop function. called when the kext should be unloaded. unloading can be prevented by
|
||||
* returning failure
|
||||
*/
|
||||
static kern_return_t tap_module_stop(struct kmod_info *ki, void *data)
|
||||
{
|
||||
if (mgr != NULL) {
|
||||
if (!mgr->shutdown())
|
||||
return KMOD_RETURN_FAILURE;
|
||||
|
||||
delete mgr;
|
||||
mgr = NULL;
|
||||
}
|
||||
|
||||
/* clean up locking */
|
||||
tt_lock::shutdown();
|
||||
|
||||
mem_shutdown();
|
||||
|
||||
return KMOD_RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
KMOD_DECL(tap, TAP_KEXT_VERSION)
|
||||
|
||||
}
|
||||
|
452
ext/tap-mac/tuntap/src/tap/tap.cc
Normal file
452
ext/tap-mac/tuntap/src/tap/tap.cc
Normal file
|
@ -0,0 +1,452 @@
|
|||
/*
|
||||
* ethertap device for macosx.
|
||||
*
|
||||
* tap_interface class definition
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "tap.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include <sys/systm.h>
|
||||
#include <sys/syslog.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/sockio.h>
|
||||
#include <sys/random.h>
|
||||
#include <sys/kern_event.h>
|
||||
|
||||
#include <net/if_types.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <net/if_dl.h>
|
||||
#include <net/if_media.h>
|
||||
#include <net/dlil.h>
|
||||
#include <net/ethernet.h>
|
||||
|
||||
}
|
||||
|
||||
#if 0
|
||||
#define dprintf(...) log(LOG_INFO, __VA_ARGS__)
|
||||
#else
|
||||
#define dprintf(...)
|
||||
#endif
|
||||
|
||||
// These declarations are missing in the Kernel.framework headers, put present in userspace :-/
|
||||
#pragma pack(4)
|
||||
struct ifmediareq {
|
||||
char ifm_name[IFNAMSIZ]; /* if name, e.g. "en0" */
|
||||
int ifm_current; /* current media options */
|
||||
int ifm_mask; /* don't care mask */
|
||||
int ifm_status; /* media status */
|
||||
int ifm_active; /* active options */
|
||||
int ifm_count; /* # entries in ifm_ulist array */
|
||||
int *ifm_ulist; /* media words */
|
||||
};
|
||||
|
||||
struct ifmediareq64 {
|
||||
char ifm_name[IFNAMSIZ]; /* if name, e.g. "en0" */
|
||||
int ifm_current; /* current media options */
|
||||
int ifm_mask; /* don't care mask */
|
||||
int ifm_status; /* media status */
|
||||
int ifm_active; /* active options */
|
||||
int ifm_count; /* # entries in ifm_ulist array */
|
||||
user64_addr_t ifmu_ulist __attribute__((aligned(8)));
|
||||
};
|
||||
|
||||
struct ifmediareq32 {
|
||||
char ifm_name[IFNAMSIZ]; /* if name, e.g. "en0" */
|
||||
int ifm_current; /* current media options */
|
||||
int ifm_mask; /* don't care mask */
|
||||
int ifm_status; /* media status */
|
||||
int ifm_active; /* active options */
|
||||
int ifm_count; /* # entries in ifm_ulist array */
|
||||
user32_addr_t ifmu_ulist; /* 32-bit pointer */
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
#define SIOCGIFMEDIA32 _IOWR('i', 56, struct ifmediareq32) /* get net media */
|
||||
#define SIOCGIFMEDIA64 _IOWR('i', 56, struct ifmediareq64) /* get net media (64-bit) */
|
||||
|
||||
|
||||
static unsigned char ETHER_BROADCAST_ADDR[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
|
||||
/* members */
|
||||
bool
|
||||
tap_interface::initialize(unsigned short major, unsigned short unit)
|
||||
{
|
||||
this->unit = unit;
|
||||
this->family_name = TAP_FAMILY_NAME;
|
||||
this->family = IFNET_FAMILY_ETHERNET;
|
||||
this->type = IFT_ETHER;
|
||||
bzero(unique_id, UIDLEN);
|
||||
snprintf(unique_id, UIDLEN, "%s%d", family_name, unit);
|
||||
|
||||
dprintf("tap: starting interface %s%d\n", TAP_FAMILY_NAME, unit);
|
||||
|
||||
/* register character device */
|
||||
if (!tuntap_interface::register_chardev(major))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
tap_interface::shutdown()
|
||||
{
|
||||
dprintf("tap: shutting down tap interface %s%d\n", TAP_FAMILY_NAME, unit);
|
||||
|
||||
unregister_chardev();
|
||||
}
|
||||
|
||||
int
|
||||
tap_interface::initialize_interface()
|
||||
{
|
||||
struct sockaddr_dl lladdr;
|
||||
lladdr.sdl_len = sizeof(lladdr);
|
||||
lladdr.sdl_family = AF_LINK;
|
||||
lladdr.sdl_alen = ETHER_ADDR_LEN;
|
||||
lladdr.sdl_nlen = lladdr.sdl_slen = 0;
|
||||
|
||||
/* generate a random MAC address */
|
||||
read_random(LLADDR(&lladdr), ETHER_ADDR_LEN);
|
||||
|
||||
/* clear multicast bit and set local assignment bit (see IEEE 802) */
|
||||
(LLADDR(&lladdr))[0] &= 0xfe;
|
||||
(LLADDR(&lladdr))[0] |= 0x02;
|
||||
|
||||
dprintf("tap: random tap address: %02x:%02x:%02x:%02x:%02x:%02x\n",
|
||||
(LLADDR(&lladdr))[0] & 0xff,
|
||||
(LLADDR(&lladdr))[1] & 0xff,
|
||||
(LLADDR(&lladdr))[2] & 0xff,
|
||||
(LLADDR(&lladdr))[3] & 0xff,
|
||||
(LLADDR(&lladdr))[4] & 0xff,
|
||||
(LLADDR(&lladdr))[5] & 0xff);
|
||||
|
||||
/* register interface */
|
||||
if (!tuntap_interface::register_interface(&lladdr, ETHER_BROADCAST_ADDR, ETHER_ADDR_LEN))
|
||||
return EIO;
|
||||
|
||||
/* Set link level address. Yes, we need to do that again. Darwin sucks. */
|
||||
errno_t err = ifnet_set_lladdr(ifp, LLADDR(&lladdr), ETHER_ADDR_LEN);
|
||||
if (err)
|
||||
dprintf("tap: failed to set lladdr on %s%d: %d\n", family_name, unit, err);
|
||||
|
||||
/* set mtu */
|
||||
ifnet_set_mtu(ifp, TAP_MTU);
|
||||
/* set header length */
|
||||
ifnet_set_hdrlen(ifp, sizeof(struct ether_header));
|
||||
/* add the broadcast flag */
|
||||
ifnet_set_flags(ifp, IFF_BROADCAST, IFF_BROADCAST);
|
||||
|
||||
/* we must call bpfattach(). Otherwise we deadlock BPF while unloading. Seems to be a bug in
|
||||
* the kernel, see bpfdetach() in net/bpf.c, it will return without releasing the lock if
|
||||
* the interface wasn't attached. I wonder what they were smoking while writing it ;-)
|
||||
*/
|
||||
bpfattach(ifp, DLT_EN10MB, ifnet_hdrlen(ifp));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
tap_interface::shutdown_interface()
|
||||
{
|
||||
dprintf("tap: shutting down network interface of device %s%d\n", TAP_FAMILY_NAME, unit);
|
||||
|
||||
/* detach all protocols */
|
||||
for (unsigned int i = 0; i < MAX_ATTACHED_PROTOS; i++) {
|
||||
if (attached_protos[i].used) {
|
||||
errno_t err = ifnet_detach_protocol(ifp, attached_protos[i].proto);
|
||||
if (err)
|
||||
log(LOG_WARNING, "tap: could not detach protocol %d from %s%d\n",
|
||||
attached_protos[i].proto, TAP_FAMILY_NAME, unit);
|
||||
}
|
||||
}
|
||||
|
||||
cleanup_interface();
|
||||
unregister_interface();
|
||||
}
|
||||
|
||||
errno_t
|
||||
tap_interface::if_ioctl(u_int32_t cmd, void *arg)
|
||||
{
|
||||
dprintf("tap: if_ioctl cmd: %d (%x)\n", cmd & 0xff, cmd);
|
||||
|
||||
switch (cmd) {
|
||||
case SIOCSIFLLADDR:
|
||||
{
|
||||
/* set ethernet address */
|
||||
struct sockaddr *ea = &(((struct ifreq *) arg)->ifr_addr);
|
||||
|
||||
dprintf("tap: SIOCSIFLLADDR family %d len %d\n",
|
||||
ea->sa_family, ea->sa_len);
|
||||
|
||||
/* check if it is really an ethernet address */
|
||||
if (ea->sa_family != AF_LINK || ea->sa_len != ETHER_ADDR_LEN)
|
||||
return EINVAL;
|
||||
|
||||
/* ok, copy */
|
||||
errno_t err = ifnet_set_lladdr(ifp, ea->sa_data, ETHER_ADDR_LEN);
|
||||
if (err) {
|
||||
dprintf("tap: failed to set lladdr on %s%d: %d\n",
|
||||
family_name, unit, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Generate a LINK_ON event. This necessary for configd to re-read
|
||||
* the interface data and refresh the MAC address. Not doing so
|
||||
* would result in the DHCP client using a stale MAC address...
|
||||
*/
|
||||
generate_link_event(KEV_DL_LINK_ON);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
case SIOCGIFMEDIA32:
|
||||
case SIOCGIFMEDIA64:
|
||||
{
|
||||
struct ifmediareq *ifmr = (struct ifmediareq*) arg;
|
||||
user_addr_t list = USER_ADDR_NULL;
|
||||
|
||||
ifmr->ifm_current = IFM_ETHER;
|
||||
ifmr->ifm_mask = 0;
|
||||
ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE;
|
||||
ifmr->ifm_active = IFM_ETHER;
|
||||
ifmr->ifm_count = 1;
|
||||
|
||||
if (cmd == SIOCGIFMEDIA64)
|
||||
list = ((struct ifmediareq64*) ifmr)->ifmu_ulist;
|
||||
else
|
||||
list = CAST_USER_ADDR_T(
|
||||
((struct ifmediareq32*) ifmr)->ifmu_ulist);
|
||||
|
||||
if (list != USER_ADDR_NULL)
|
||||
return copyout(&ifmr->ifm_current, list, sizeof(int));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
default:
|
||||
/* let our superclass handle it */
|
||||
return tuntap_interface::if_ioctl(cmd, arg);
|
||||
}
|
||||
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
errno_t
|
||||
tap_interface::if_demux(mbuf_t m, char *header, protocol_family_t *proto)
|
||||
{
|
||||
struct ether_header *eh = (struct ether_header *) header;
|
||||
unsigned char lladdr[ETHER_ADDR_LEN];
|
||||
|
||||
dprintf("tap: if_demux\n");
|
||||
|
||||
/* size check */
|
||||
if (mbuf_len(m) < sizeof(struct ether_header))
|
||||
return ENOENT;
|
||||
|
||||
/* catch broadcast and multicast (stolen from bsd/net/ether_if_module.c) */
|
||||
if (eh->ether_dhost[0] & 1) {
|
||||
if (memcmp(ETHER_BROADCAST_ADDR, eh->ether_dhost, ETHER_ADDR_LEN) == 0) {
|
||||
/* broadcast */
|
||||
dprintf("tap: broadcast packet.\n");
|
||||
mbuf_setflags_mask(m, MBUF_BCAST, MBUF_BCAST);
|
||||
} else {
|
||||
/* multicast */
|
||||
dprintf("tap: multicast packet.\n");
|
||||
mbuf_setflags_mask(m, MBUF_MCAST, MBUF_MCAST);
|
||||
}
|
||||
} else {
|
||||
/* check wether the packet has our address */
|
||||
ifnet_lladdr_copy_bytes(ifp, lladdr, ETHER_ADDR_LEN);
|
||||
if (memcmp(lladdr, eh->ether_dhost, ETHER_ADDR_LEN) != 0)
|
||||
mbuf_setflags_mask(m, MBUF_PROMISC, MBUF_PROMISC);
|
||||
}
|
||||
|
||||
/* find the protocol */
|
||||
for (unsigned int i = 0; i < MAX_ATTACHED_PROTOS; i++) {
|
||||
if (attached_protos[i].used && attached_protos[i].type == eh->ether_type) {
|
||||
*proto = attached_protos[i].proto;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
dprintf("tap: if_demux() failed to find proto.\n");
|
||||
|
||||
/* no matching proto found */
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
errno_t
|
||||
tap_interface::if_framer(mbuf_t *m, const struct sockaddr *dest, const char *dest_linkaddr,
|
||||
const char *frame_type)
|
||||
{
|
||||
struct ether_header *eh;
|
||||
mbuf_t nm = *m;
|
||||
errno_t err;
|
||||
|
||||
dprintf("tap: if_framer\n");
|
||||
|
||||
/* prepend the ethernet header */
|
||||
err = mbuf_prepend(&nm, sizeof (struct ether_header), MBUF_WAITOK);
|
||||
if (err) {
|
||||
dprintf("tap: could not prepend data to mbuf: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
*m = nm;
|
||||
|
||||
/* fill the header */
|
||||
eh = (struct ether_header *) mbuf_data(*m);
|
||||
memcpy(eh->ether_dhost, dest_linkaddr, ETHER_ADDR_LEN);
|
||||
ifnet_lladdr_copy_bytes(ifp, eh->ether_shost, ETHER_ADDR_LEN);
|
||||
eh->ether_type = *((u_int16_t *) frame_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
errno_t
|
||||
tap_interface::if_add_proto(protocol_family_t proto, const struct ifnet_demux_desc *desc,
|
||||
u_int32_t ndesc)
|
||||
{
|
||||
errno_t err;
|
||||
|
||||
dprintf("tap: if_add_proto proto %d\n", proto);
|
||||
|
||||
for (unsigned int i = 0; i < ndesc; i++) {
|
||||
/* try to add the protocol */
|
||||
err = add_one_proto(proto, desc[i]);
|
||||
if (err != 0) {
|
||||
/* if that fails, remove everything stored so far */
|
||||
if_del_proto(proto);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
errno_t
|
||||
tap_interface::if_del_proto(protocol_family_t proto)
|
||||
{
|
||||
dprintf("tap: if_del_proto proto %d\n", proto);
|
||||
|
||||
/* delete all matching entries in attached_protos */
|
||||
for (unsigned int i = 0; i < MAX_ATTACHED_PROTOS; i++) {
|
||||
if (attached_protos[i].proto == proto)
|
||||
attached_protos[i].used = false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
errno_t
|
||||
tap_interface::if_check_multi(const struct sockaddr *maddr)
|
||||
{
|
||||
dprintf("tap: if_check_multi family %d\n", maddr->sa_family);
|
||||
|
||||
/* see whether it is a ethernet address with the multicast bit set */
|
||||
if (maddr->sa_family == AF_LINK) {
|
||||
struct sockaddr_dl *dlmaddr = (struct sockaddr_dl *) maddr;
|
||||
if (LLADDR(dlmaddr)[0] & 0x01)
|
||||
return 0;
|
||||
else
|
||||
return EADDRNOTAVAIL;
|
||||
}
|
||||
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
errno_t
|
||||
tap_interface::add_one_proto(protocol_family_t proto, const struct ifnet_demux_desc &dd)
|
||||
{
|
||||
int free = -1;
|
||||
u_int16_t dt;
|
||||
|
||||
/* we only support DLIL_DESC_ETYPE2 */
|
||||
if (dd.type != DLIL_DESC_ETYPE2 || dd.datalen != 2) {
|
||||
log(LOG_WARNING, "tap: tap only supports DLIL_DESC_ETYPE2 protocols.\n");
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
dt = *((u_int16_t *) (dd.data));
|
||||
|
||||
/* see if the protocol is already registered */
|
||||
for (unsigned int i = 0; i < MAX_ATTACHED_PROTOS; i++) {
|
||||
if (attached_protos[i].used) {
|
||||
if (dt == attached_protos[i].type) {
|
||||
/* already registered */
|
||||
if (attached_protos[i].proto == proto) {
|
||||
/* matches the old entry */
|
||||
return 0;
|
||||
} else
|
||||
return EEXIST;
|
||||
}
|
||||
} else if (free == -1)
|
||||
free = i;
|
||||
}
|
||||
|
||||
/* did we find a free entry? */
|
||||
if (free == -1)
|
||||
/* is ENOBUFS correct? */
|
||||
return ENOBUFS;
|
||||
|
||||
/* ok, save information */
|
||||
attached_protos[free].used = true;
|
||||
attached_protos[free].type = dt;
|
||||
attached_protos[free].proto = proto;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This code is shamelessly stolen from if_bond.c */
|
||||
void
|
||||
tap_interface::generate_link_event(u_int32_t code)
|
||||
{
|
||||
struct {
|
||||
struct kern_event_msg header;
|
||||
u_int32_t unit;
|
||||
char if_name[IFNAMSIZ];
|
||||
} event;
|
||||
|
||||
bzero(&event, sizeof(event));
|
||||
event.header.total_size = sizeof(event);
|
||||
event.header.vendor_code = KEV_VENDOR_APPLE;
|
||||
event.header.kev_class = KEV_NETWORK_CLASS;
|
||||
event.header.kev_subclass = KEV_DL_SUBCLASS;
|
||||
event.header.event_code = code;
|
||||
event.header.event_data[0] = family;
|
||||
event.unit = (u_int32_t) unit;
|
||||
strncpy(event.if_name, ifnet_name(ifp), IFNAMSIZ);
|
||||
|
||||
ifnet_event(ifp, &event.header);
|
||||
}
|
||||
|
||||
/* tap_manager members */
|
||||
tuntap_interface *
|
||||
tap_manager::create_interface()
|
||||
{
|
||||
return new tap_interface();
|
||||
}
|
||||
|
103
ext/tap-mac/tuntap/src/tap/tap.h
Normal file
103
ext/tap-mac/tuntap/src/tap/tap.h
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* ethertap device for MacOSX.
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __TAP_H__
|
||||
#define __TAP_H__
|
||||
|
||||
#include "tuntap.h"
|
||||
|
||||
#define TAP_FAMILY_NAME ((char *) "zt")
|
||||
|
||||
#define TAP_IF_COUNT 16 /* max number of tap interfaces */
|
||||
|
||||
#define TAP_MTU 2800
|
||||
|
||||
#define TAP_LLADDR tap_lladdr
|
||||
|
||||
/* the mac address of our interfaces. note that the last byte will be replaced by the unit number */
|
||||
extern u_char tap_lladdr[];
|
||||
|
||||
/* tap manager */
|
||||
class tap_manager : public tuntap_manager {
|
||||
|
||||
protected:
|
||||
/* just define the interface creation method */
|
||||
virtual tuntap_interface *create_interface();
|
||||
|
||||
};
|
||||
|
||||
/* the tap network interface */
|
||||
class tap_interface : public tuntap_interface {
|
||||
|
||||
protected:
|
||||
/* maximum number of protocols that can be attached */
|
||||
static const unsigned int MAX_ATTACHED_PROTOS = 8;
|
||||
|
||||
/* information about attached protocols for demuxing is stored here */
|
||||
struct {
|
||||
/* whether this entry is used */
|
||||
bool used;
|
||||
/* type in the ethernet header */
|
||||
u_int16_t type;
|
||||
/* protocol passed to add_proto */
|
||||
protocol_family_t proto;
|
||||
} attached_protos[MAX_ATTACHED_PROTOS];
|
||||
|
||||
/* initializes the interface */
|
||||
virtual bool initialize(unsigned short major, unsigned short unit);
|
||||
|
||||
/* shuts the interface down */
|
||||
virtual void shutdown();
|
||||
|
||||
/* called when the character device is opened in order to intialize the network
|
||||
* interface.
|
||||
*/
|
||||
virtual int initialize_interface();
|
||||
/* called when the character device is closed to shutdown the network interface */
|
||||
virtual void shutdown_interface();
|
||||
|
||||
/* override interface routines */
|
||||
virtual errno_t if_ioctl(u_int32_t cmd, void *arg);
|
||||
virtual errno_t if_demux(mbuf_t m, char *header, protocol_family_t *proto);
|
||||
virtual errno_t if_framer(mbuf_t *m, const struct sockaddr *dest,
|
||||
const char *dest_linkaddr, const char *frame_type);
|
||||
virtual errno_t if_add_proto(protocol_family_t proto,
|
||||
const struct ifnet_demux_desc *ddesc, u_int32_t ndesc);
|
||||
virtual errno_t if_del_proto(protocol_family_t proto);
|
||||
virtual errno_t if_check_multi(const struct sockaddr *maddr);
|
||||
|
||||
/* if_add_proto helper */
|
||||
errno_t add_one_proto(protocol_family_t proto, const struct ifnet_demux_desc &dd);
|
||||
|
||||
/* generates a kernel event */
|
||||
void generate_link_event(u_int32_t code);
|
||||
|
||||
friend class tap_manager;
|
||||
};
|
||||
|
||||
#endif /* __TAP_H__ */
|
||||
|
966
ext/tap-mac/tuntap/src/tuntap.cc
Normal file
966
ext/tap-mac/tuntap/src/tuntap.cc
Normal file
|
@ -0,0 +1,966 @@
|
|||
/*
|
||||
* ip tunnel/ethertap device for MacOSX. Common functionality of tap_interface and tun_interface.
|
||||
*
|
||||
* tuntap_interface class definition
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "tuntap.h"
|
||||
|
||||
#if 0
|
||||
#define dprintf(...) log(LOG_INFO, __VA_ARGS__)
|
||||
#else
|
||||
#define dprintf(...)
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include <sys/conf.h>
|
||||
#include <sys/syslog.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/filio.h>
|
||||
#include <sys/sockio.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/kpi_socket.h>
|
||||
|
||||
#include <vm/vm_kern.h>
|
||||
|
||||
#include <net/if_types.h>
|
||||
#include <net/if_var.h>
|
||||
#include <net/if_dl.h>
|
||||
#include <net/if_arp.h>
|
||||
|
||||
#include <miscfs/devfs/devfs.h>
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
/* interface service functions that delegate to the appropriate tuntap_interface instance */
|
||||
errno_t
|
||||
tuntap_if_output(ifnet_t ifp, mbuf_t m)
|
||||
{
|
||||
if (ifp != NULL) {
|
||||
tuntap_interface *ttif = (tuntap_interface *) ifnet_softc(ifp);
|
||||
if (ttif != NULL)
|
||||
return ttif->if_output(m);
|
||||
}
|
||||
|
||||
if (m != NULL)
|
||||
mbuf_freem_list(m);
|
||||
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
errno_t
|
||||
tuntap_if_ioctl(ifnet_t ifp, long unsigned int cmd, void *arg)
|
||||
{
|
||||
if (ifp != NULL) {
|
||||
tuntap_interface *ttif = (tuntap_interface *) ifnet_softc(ifp);
|
||||
if (ttif != NULL)
|
||||
return ttif->if_ioctl(cmd, arg);
|
||||
}
|
||||
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
errno_t
|
||||
tuntap_if_set_bpf_tap(ifnet_t ifp, bpf_tap_mode mode, int (*cb)(ifnet_t, mbuf_t))
|
||||
{
|
||||
if (ifp != NULL) {
|
||||
tuntap_interface *ttif = (tuntap_interface *) ifnet_softc(ifp);
|
||||
if (ttif != NULL)
|
||||
return ttif->if_set_bpf_tap(mode, cb);
|
||||
}
|
||||
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
errno_t
|
||||
tuntap_if_demux(ifnet_t ifp, mbuf_t m, char *header, protocol_family_t *proto)
|
||||
{
|
||||
if (ifp != NULL) {
|
||||
tuntap_interface *ttif = (tuntap_interface *) ifnet_softc(ifp);
|
||||
if (ttif != NULL)
|
||||
return ttif->if_demux(m, header, proto);
|
||||
}
|
||||
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
errno_t
|
||||
tuntap_if_framer(ifnet_t ifp, mbuf_t *m, const struct sockaddr *dest, const char *dest_linkaddr,
|
||||
const char *frame_type)
|
||||
{
|
||||
if (ifp != NULL) {
|
||||
tuntap_interface *ttif = (tuntap_interface *) ifnet_softc(ifp);
|
||||
if (ttif != NULL)
|
||||
return ttif->if_framer(m, dest, dest_linkaddr, frame_type);
|
||||
}
|
||||
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
errno_t
|
||||
tuntap_if_add_proto(ifnet_t ifp, protocol_family_t proto, const struct ifnet_demux_desc *ddesc,
|
||||
u_int32_t ndesc)
|
||||
{
|
||||
if (ifp != NULL) {
|
||||
tuntap_interface *ttif = (tuntap_interface *) ifnet_softc(ifp);
|
||||
if (ttif != NULL)
|
||||
return ttif->if_add_proto(proto, ddesc, ndesc);
|
||||
}
|
||||
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
errno_t
|
||||
tuntap_if_del_proto(ifnet_t ifp, protocol_family_t proto)
|
||||
{
|
||||
if (ifp != NULL) {
|
||||
tuntap_interface *ttif = (tuntap_interface *) ifnet_softc(ifp);
|
||||
if (ttif != NULL)
|
||||
return ttif->if_del_proto(proto);
|
||||
}
|
||||
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
errno_t
|
||||
tuntap_if_check_multi(ifnet_t ifp, const struct sockaddr* maddr)
|
||||
{
|
||||
if (ifp != NULL)
|
||||
{
|
||||
tuntap_interface *ttif = (tuntap_interface *) ifnet_softc(ifp);
|
||||
if (ttif != NULL)
|
||||
return ttif->if_check_multi(maddr);
|
||||
}
|
||||
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
void
|
||||
tuntap_if_detached(ifnet_t ifp)
|
||||
{
|
||||
if (ifp != NULL) {
|
||||
tuntap_interface *ttif = (tuntap_interface *) ifnet_softc(ifp);
|
||||
if (ttif != NULL)
|
||||
ttif->if_detached();
|
||||
}
|
||||
}
|
||||
|
||||
errno_t
|
||||
tuntap_if_noop_output(ifnet_t, mbuf_t)
|
||||
{
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
errno_t
|
||||
tuntap_if_noop_demux(ifnet_t, mbuf_t, char*, protocol_family_t*)
|
||||
{
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
errno_t
|
||||
tuntap_if_noop_add_proto(ifnet_t, protocol_family_t, const struct ifnet_demux_desc*, u_int32_t)
|
||||
{
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
errno_t
|
||||
tuntap_if_noop_del_proto(ifnet_t, protocol_family_t)
|
||||
{
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
} /* extern "C" */
|
||||
|
||||
/* tuntap_mbuf_queue */
|
||||
tuntap_mbuf_queue::tuntap_mbuf_queue()
|
||||
{
|
||||
head = tail = NULL;
|
||||
size = 0;
|
||||
}
|
||||
|
||||
tuntap_mbuf_queue::~tuntap_mbuf_queue()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
bool
|
||||
tuntap_mbuf_queue::enqueue(mbuf_t mb)
|
||||
{
|
||||
if (size == QUEUE_SIZE)
|
||||
return false;
|
||||
|
||||
mbuf_setnextpkt(mb, NULL);
|
||||
|
||||
if (head == NULL)
|
||||
head = tail = mb;
|
||||
else {
|
||||
mbuf_setnextpkt(tail, mb);
|
||||
tail = mb;
|
||||
}
|
||||
size++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
mbuf_t
|
||||
tuntap_mbuf_queue::dequeue()
|
||||
{
|
||||
mbuf_t ret;
|
||||
|
||||
/* check wether there is a packet in the queue */
|
||||
if (head == NULL)
|
||||
return NULL;
|
||||
|
||||
/* fetch it */
|
||||
ret = head;
|
||||
head = mbuf_nextpkt(head);
|
||||
mbuf_setnextpkt(ret, NULL);
|
||||
size--;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
tuntap_mbuf_queue::clear()
|
||||
{
|
||||
/* free mbufs that are in the queue */
|
||||
if (head != NULL)
|
||||
mbuf_freem_list(head);
|
||||
|
||||
head = NULL;
|
||||
tail = NULL;
|
||||
size = 0;
|
||||
}
|
||||
|
||||
/* tuntap_interface members */
|
||||
tuntap_interface::tuntap_interface()
|
||||
{
|
||||
/* initialize the members */
|
||||
ifp = NULL;
|
||||
open = false;
|
||||
block_io = true;
|
||||
dev_handle = NULL;
|
||||
pid = 0;
|
||||
selthreadclear(&rsel);
|
||||
bpf_mode = BPF_MODE_DISABLED;
|
||||
bpf_callback = NULL;
|
||||
bzero(unique_id, UIDLEN);
|
||||
in_ioctl = false;
|
||||
}
|
||||
|
||||
tuntap_interface::~tuntap_interface()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
tuntap_interface::register_chardev(unsigned short major)
|
||||
{
|
||||
/* register character device */
|
||||
dev_handle = devfs_make_node(makedev(major, unit), DEVFS_CHAR, 0, 0, 0660, "%s%d",
|
||||
family_name, (int) unit);
|
||||
|
||||
if (dev_handle == NULL) {
|
||||
log(LOG_ERR, "tuntap: could not make /dev/%s%d\n", family_name, (int) unit);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
tuntap_interface::unregister_chardev()
|
||||
{
|
||||
dprintf("unregistering character device\n");
|
||||
|
||||
/* unregister character device */
|
||||
if (dev_handle != NULL)
|
||||
devfs_remove(dev_handle);
|
||||
dev_handle = NULL;
|
||||
}
|
||||
|
||||
bool
|
||||
tuntap_interface::register_interface(const struct sockaddr_dl* lladdr, void *bcaddr,
|
||||
u_int32_t bcaddrlen)
|
||||
{
|
||||
struct ifnet_init_params ip;
|
||||
errno_t err;
|
||||
|
||||
dprintf("register_interface\n");
|
||||
|
||||
/* initialize an initialization info struct */
|
||||
ip.uniqueid_len = UIDLEN;
|
||||
ip.uniqueid = unique_id;
|
||||
ip.name = family_name;
|
||||
ip.unit = unit;
|
||||
ip.family = family;
|
||||
ip.type = type;
|
||||
ip.output = tuntap_if_output;
|
||||
ip.demux = tuntap_if_demux;
|
||||
ip.add_proto = tuntap_if_add_proto;
|
||||
ip.del_proto = tuntap_if_del_proto;
|
||||
ip.check_multi = tuntap_if_check_multi;
|
||||
ip.framer = tuntap_if_framer;
|
||||
ip.softc = this;
|
||||
ip.ioctl = tuntap_if_ioctl;
|
||||
ip.set_bpf_tap = tuntap_if_set_bpf_tap;
|
||||
ip.detach = tuntap_if_detached;
|
||||
ip.event = NULL;
|
||||
ip.broadcast_addr = bcaddr;
|
||||
ip.broadcast_len = bcaddrlen;
|
||||
|
||||
dprintf("tuntap: tuntap_if_check_multi is at 0x%08x\n", (void*) tuntap_if_check_multi);
|
||||
|
||||
/* allocate the interface */
|
||||
err = ifnet_allocate(&ip, &ifp);
|
||||
if (err) {
|
||||
log(LOG_ERR, "tuntap: could not allocate interface for %s%d: %d\n", family_name,
|
||||
(int) unit, err);
|
||||
ifp = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* activate the interface */
|
||||
err = ifnet_attach(ifp, lladdr);
|
||||
if (err) {
|
||||
log(LOG_ERR, "tuntap: could not attach interface %s%d: %d\n", family_name,
|
||||
(int) unit, err);
|
||||
ifnet_release(ifp);
|
||||
ifp = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
dprintf("setting interface flags\n");
|
||||
|
||||
/* set interface flags */
|
||||
ifnet_set_flags(ifp, IFF_RUNNING | IFF_MULTICAST | IFF_SIMPLEX, (u_int16_t) ~0UL);
|
||||
|
||||
dprintf("flags: %x\n", ifnet_flags(ifp));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
tuntap_interface::unregister_interface()
|
||||
{
|
||||
errno_t err;
|
||||
|
||||
dprintf("unregistering network interface\n");
|
||||
|
||||
if (ifp != NULL) {
|
||||
interface_detached = false;
|
||||
|
||||
/* detach interface */
|
||||
err = ifnet_detach(ifp);
|
||||
if (err)
|
||||
log(LOG_ERR, "tuntap: error detaching interface %s%d: %d\n",
|
||||
family_name, unit, err);
|
||||
|
||||
dprintf("interface detaching\n");
|
||||
|
||||
/* Wait until the interface has completely been detached. */
|
||||
detach_lock.lock();
|
||||
while (!interface_detached)
|
||||
detach_lock.sleep(&interface_detached);
|
||||
detach_lock.unlock();
|
||||
|
||||
dprintf("interface detached\n");
|
||||
|
||||
/* release the interface */
|
||||
ifnet_release(ifp);
|
||||
|
||||
ifp = NULL;
|
||||
}
|
||||
|
||||
dprintf("network interface unregistered\n");
|
||||
}
|
||||
|
||||
void
|
||||
tuntap_interface::cleanup_interface()
|
||||
{
|
||||
errno_t err;
|
||||
ifaddr_t *addrs;
|
||||
ifaddr_t *a;
|
||||
struct ifreq ifr;
|
||||
|
||||
/* mark the interface down */
|
||||
ifnet_set_flags(ifp, 0, IFF_UP | IFF_RUNNING);
|
||||
|
||||
/* Unregister all interface addresses. This works around a deficiency in the Darwin kernel.
|
||||
* If we don't remove all IP addresses that are attached to the interface it can happen that
|
||||
* the IP code fails to clean them up itself. When the interface is recycled, the IP code
|
||||
* might then think some addresses are still attached to the interface...
|
||||
*/
|
||||
|
||||
err = ifnet_get_address_list(ifp, &addrs);
|
||||
if (!err) {
|
||||
|
||||
/* Execute a SIOCDIFADDR ioctl for each address. For technical reasons, we can only
|
||||
* do that with a socket of the appropriate family. So try to create a dummy socket.
|
||||
* I know this is a little expensive, but better than crashing...
|
||||
*
|
||||
* This really sucks.
|
||||
*/
|
||||
for (a = addrs; *a != NULL; a++) {
|
||||
/* initialize the request parameters */
|
||||
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d",
|
||||
ifnet_name(ifp), ifnet_unit(ifp));
|
||||
ifaddr_address(*a, &(ifr.ifr_addr), sizeof(ifr.ifr_addr));
|
||||
if (ifr.ifr_addr.sa_family != AF_INET)
|
||||
continue;
|
||||
|
||||
dprintf("trying to delete address of family %d\n", ifr.ifr_addr.sa_family);
|
||||
|
||||
do_sock_ioctl(ifr.ifr_addr.sa_family, SIOCDIFADDR, &ifr);
|
||||
}
|
||||
|
||||
/* release the address list */
|
||||
ifnet_free_address_list(addrs);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
tuntap_interface::idle()
|
||||
{
|
||||
return !(open);
|
||||
}
|
||||
|
||||
void
|
||||
tuntap_interface::notify_bpf(mbuf_t mb, bool out)
|
||||
{
|
||||
auto_lock l(&bpf_lock);
|
||||
|
||||
if ((out && bpf_mode == BPF_MODE_OUTPUT)
|
||||
|| (!out && bpf_mode == BPF_MODE_INPUT)
|
||||
|| (bpf_mode == BPF_MODE_INPUT_OUTPUT))
|
||||
(*bpf_callback)(ifp, mb);
|
||||
}
|
||||
|
||||
void
|
||||
tuntap_interface::do_sock_ioctl(sa_family_t af, unsigned long cmd, void* arg) {
|
||||
if (in_ioctl) {
|
||||
log(LOG_ERR, "tuntap: ioctl recursion detected, aborting.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
socket_t sock;
|
||||
errno_t err = sock_socket(af, SOCK_RAW, 0, NULL, NULL, &sock);
|
||||
if (err) {
|
||||
log(LOG_ERR, "tuntap: failed to create socket: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
in_ioctl = true;
|
||||
|
||||
/* issue the ioctl */
|
||||
err = sock_ioctl(sock, cmd, arg);
|
||||
if (err)
|
||||
log(LOG_ERR, "tuntap: socket ioctl %d failed: %d\n", cmd, err);
|
||||
|
||||
in_ioctl = false;
|
||||
|
||||
/* get rid of the socket */
|
||||
sock_close(sock);
|
||||
}
|
||||
|
||||
/* character device service methods */
|
||||
int
|
||||
tuntap_interface::cdev_open(int flags, int devtype, proc_t p)
|
||||
{
|
||||
dprintf("tuntap: cdev_open()\n");
|
||||
|
||||
/* grab the lock so that there can only be one thread inside */
|
||||
auto_lock l(&lock);
|
||||
|
||||
/* check wether it is already open */
|
||||
if (open)
|
||||
return EBUSY;
|
||||
|
||||
/* bring the network interface up */
|
||||
int error = initialize_interface();
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
open = true;
|
||||
pid = proc_pid(p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
tuntap_interface::cdev_close(int flags, int devtype, proc_t p)
|
||||
{
|
||||
dprintf("tuntap: cdev_close()\n");
|
||||
|
||||
auto_lock l(&lock);
|
||||
|
||||
if (open) {
|
||||
open = false;
|
||||
|
||||
/* shut down the network interface */
|
||||
shutdown_interface();
|
||||
|
||||
/* clear the queue */
|
||||
send_queue.clear();
|
||||
|
||||
/* wakeup the cdev thread and notify selects */
|
||||
wakeup(this);
|
||||
selwakeup(&rsel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return EBADF;
|
||||
}
|
||||
|
||||
int
|
||||
tuntap_interface::cdev_read(uio_t uio, int ioflag)
|
||||
{
|
||||
auto_lock l(&lock);
|
||||
|
||||
unsigned int nb = 0;
|
||||
int error;
|
||||
|
||||
dprintf("tuntap: cdev read\n");
|
||||
|
||||
if (!open || ifp == NULL || !(ifnet_flags(ifp) & IFF_UP))
|
||||
return EIO;
|
||||
|
||||
/* fetch a new mbuf from the queue if necessary */
|
||||
mbuf_t cur_mbuf = NULL;
|
||||
while (cur_mbuf == NULL) {
|
||||
dprintf("tuntap: fetching new mbuf\n");
|
||||
|
||||
cur_mbuf = send_queue.dequeue();
|
||||
if (cur_mbuf == NULL) {
|
||||
/* nothing in queue, block or return */
|
||||
if (!block_io) {
|
||||
dprintf("tuntap: aborting (nbio)\n");
|
||||
return EWOULDBLOCK;
|
||||
} else {
|
||||
/* block */
|
||||
dprintf("tuntap: waiting\n");
|
||||
/* release the lock while waiting */
|
||||
l.unlock();
|
||||
error = msleep(this, NULL, PZERO | PCATCH, "tuntap", NULL);
|
||||
|
||||
l.lock();
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* see whether the device was closed in the meantime */
|
||||
if (!open || ifp == NULL || !(ifnet_flags(ifp) & IFF_UP))
|
||||
return EIO;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* notify bpf */
|
||||
notify_bpf(cur_mbuf, true);
|
||||
|
||||
/* output what we have */
|
||||
do {
|
||||
dprintf("tuntap: got new mbuf: %p uio_resid: %d\n", cur_mbuf, uio_resid(uio));
|
||||
|
||||
/* now we have an mbuf */
|
||||
int chunk_len = min(mbuf_len(cur_mbuf), uio_resid(uio));
|
||||
error = uiomove((char *) mbuf_data(cur_mbuf), chunk_len, uio);
|
||||
if (error) {
|
||||
mbuf_freem(cur_mbuf);
|
||||
return error;
|
||||
}
|
||||
nb += chunk_len;
|
||||
|
||||
dprintf("tuntap: moved %d bytes to userspace uio_resid: %d\n", chunk_len,
|
||||
uio_resid(uio));
|
||||
|
||||
/* update cur_mbuf */
|
||||
cur_mbuf = mbuf_free(cur_mbuf);
|
||||
|
||||
} while (uio_resid(uio) > 0 && cur_mbuf != NULL);
|
||||
|
||||
/* update statistics */
|
||||
ifnet_stat_increment_out(ifp, 1, nb, 0);
|
||||
|
||||
/* still data left? forget about that ;-) */
|
||||
if (cur_mbuf != NULL)
|
||||
mbuf_freem(cur_mbuf);
|
||||
|
||||
dprintf("tuntap: read done\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
tuntap_interface::cdev_write(uio_t uio, int ioflag)
|
||||
{
|
||||
auto_lock l(&lock);
|
||||
|
||||
if (!open || ifp == NULL || !(ifnet_flags(ifp) & IFF_UP))
|
||||
return EIO;
|
||||
|
||||
dprintf("tuntap: cdev write. uio_resid: %d\n", uio_resid(uio));
|
||||
|
||||
/* pack the data into an mbuf chain */
|
||||
mbuf_t first, mb;
|
||||
|
||||
/* first we need an mbuf having a header */
|
||||
mbuf_gethdr(MBUF_WAITOK, MBUF_TYPE_DATA, &first);
|
||||
if (first == NULL) {
|
||||
log(LOG_ERR, "tuntap: could not get mbuf.\n");
|
||||
return ENOMEM;
|
||||
}
|
||||
mbuf_setlen(first, 0);
|
||||
|
||||
unsigned int mlen = mbuf_maxlen(first);
|
||||
unsigned int chunk_len;
|
||||
unsigned int copied = 0;
|
||||
int error;
|
||||
|
||||
/* stuff the data into the mbuf(s) */
|
||||
mb = first;
|
||||
while (uio_resid(uio) > 0) {
|
||||
/* copy a chunk. enforce mtu (don't know if this is correct behaviour) */
|
||||
// ... evidently not :) -- Adam Ierymenko <adam.ierymenko@zerotier.com>
|
||||
//chunk_len = min(ifnet_mtu(ifp), min(uio_resid(uio), mlen));
|
||||
chunk_len = min(uio_resid(uio),mlen);
|
||||
error = uiomove((caddr_t) mbuf_data(mb), chunk_len, uio);
|
||||
if (error) {
|
||||
log(LOG_ERR, "tuntap: could not copy data from userspace: %d\n", error);
|
||||
mbuf_freem(first);
|
||||
return error;
|
||||
}
|
||||
|
||||
dprintf("tuntap: copied %d bytes, uio_resid %d\n", chunk_len,
|
||||
uio_resid(uio));
|
||||
|
||||
mlen -= chunk_len;
|
||||
mbuf_setlen(mb, mbuf_len(mb) + chunk_len);
|
||||
copied += chunk_len;
|
||||
|
||||
/* if done, break the loop */
|
||||
//if (uio_resid(uio) <= 0 || copied >= ifnet_mtu(ifp))
|
||||
// break;
|
||||
if (uio_resid(uio) <= 0)
|
||||
break;
|
||||
|
||||
/* allocate a new mbuf if the current is filled */
|
||||
if (mlen == 0) {
|
||||
mbuf_t next;
|
||||
mbuf_get(MBUF_WAITOK, MBUF_TYPE_DATA, &next);
|
||||
if (next == NULL) {
|
||||
log(LOG_ERR, "tuntap: could not get mbuf.\n");
|
||||
mbuf_freem(first);
|
||||
return ENOMEM;
|
||||
}
|
||||
mbuf_setnext(mb, next);
|
||||
mb = next;
|
||||
mbuf_setlen(mb, 0);
|
||||
mlen = mbuf_maxlen(mb);
|
||||
}
|
||||
}
|
||||
|
||||
/* fill in header info */
|
||||
mbuf_pkthdr_setrcvif(first, ifp);
|
||||
mbuf_pkthdr_setlen(first, copied);
|
||||
mbuf_pkthdr_setheader(first, mbuf_data(first));
|
||||
mbuf_set_csum_performed(first, 0, 0);
|
||||
|
||||
/* update statistics */
|
||||
ifnet_stat_increment_in(ifp, 1, copied, 0);
|
||||
|
||||
dprintf("tuntap: mbuf chain constructed. first: %p mb: %p len: %d data: %p\n",
|
||||
first, mb, mbuf_len(first), mbuf_data(first));
|
||||
|
||||
/* notify bpf */
|
||||
notify_bpf(first, false);
|
||||
|
||||
/* need to adjust the data pointer to point directly behind the linklevel header. The header
|
||||
* itself is later accessed via m_pkthdr.header. Well, if something is ugly, here is it.
|
||||
*/
|
||||
mbuf_adj(first, ifnet_hdrlen(ifp));
|
||||
|
||||
/* pass the packet over to the network stack */
|
||||
error = ifnet_input(ifp, first, NULL);
|
||||
|
||||
if (error) {
|
||||
log(LOG_ERR, "tuntap: could not input packet into network stack.\n");
|
||||
mbuf_freem(first);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
tuntap_interface::cdev_ioctl(u_long cmd, caddr_t data, int fflag, proc_t p)
|
||||
{
|
||||
auto_lock l(&lock);
|
||||
|
||||
dprintf("tuntap: cdev ioctl: %d\n", (int) (cmd & 0xff));
|
||||
|
||||
switch (cmd) {
|
||||
case FIONBIO:
|
||||
/* set i/o mode */
|
||||
block_io = *((int *) data) ? false : true;
|
||||
return 0;
|
||||
case FIOASYNC:
|
||||
/* don't allow switching it on */
|
||||
if (*((int *) data))
|
||||
return ENOTTY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ENOTTY;
|
||||
}
|
||||
|
||||
int
|
||||
tuntap_interface::cdev_select(int which, void *wql, proc_t p)
|
||||
{
|
||||
auto_lock l(&lock);
|
||||
|
||||
int ret = 0;
|
||||
|
||||
dprintf("tuntap: select. which: %d\n", which);
|
||||
|
||||
switch (which) {
|
||||
case FREAD:
|
||||
/* check wether data is available */
|
||||
{
|
||||
if (!send_queue.empty())
|
||||
ret = 1;
|
||||
else {
|
||||
dprintf("tuntap: select: waiting\n");
|
||||
selrecord(p, &rsel, wql);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FWRITE:
|
||||
/* we are always writeable */
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* interface service methods */
|
||||
errno_t
|
||||
tuntap_interface::if_output(mbuf_t m)
|
||||
{
|
||||
mbuf_t pkt;
|
||||
|
||||
dprintf("tuntap: if output\n");
|
||||
|
||||
/* just to be sure */
|
||||
if (m == NULL)
|
||||
return 0;
|
||||
|
||||
if (!open || ifp == NULL || !(ifnet_flags(ifp) & IFF_UP)) {
|
||||
mbuf_freem_list(m);
|
||||
return EHOSTDOWN;
|
||||
}
|
||||
|
||||
/* check whether packet has a header */
|
||||
if ((mbuf_flags(m) & MBUF_PKTHDR) == 0) {
|
||||
log(LOG_ERR, "tuntap: packet to be output has no mbuf header.\n");
|
||||
mbuf_freem_list(m);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* put the packet(s) into the output queue */
|
||||
while (m != NULL) {
|
||||
/* keep pointer, iterate */
|
||||
pkt = m;
|
||||
m = mbuf_nextpkt(m);
|
||||
mbuf_setnextpkt(pkt, NULL);
|
||||
|
||||
auto_lock l(&lock);
|
||||
|
||||
if (!send_queue.enqueue(pkt)) {
|
||||
mbuf_freem(pkt);
|
||||
mbuf_freem_list(m);
|
||||
return ENOBUFS;
|
||||
}
|
||||
}
|
||||
|
||||
/* protect the wakeup calls with the lock, not sure they are safe. */
|
||||
{
|
||||
auto_lock l(&lock);
|
||||
|
||||
/* wakeup the cdev thread and notify selects */
|
||||
wakeup(this);
|
||||
selwakeup(&rsel);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
errno_t
|
||||
tuntap_interface::if_ioctl(u_int32_t cmd, void *arg)
|
||||
{
|
||||
dprintf("tuntap: if ioctl: %d\n", (int) (cmd & 0xff));
|
||||
|
||||
switch (cmd) {
|
||||
case SIOCSIFADDR:
|
||||
{
|
||||
dprintf("tuntap: if_ioctl: SIOCSIFADDR\n");
|
||||
|
||||
/* Unfortunately, ifconfig sets the address family field of an INET
|
||||
* netmask to zero, which makes early mDNSresponder versions ignore
|
||||
* the interface. Fix that here. This one is of the category "ugly
|
||||
* workaround". Dumb Darwin...
|
||||
*
|
||||
* Meanwhile, Apple has fixed mDNSResponder, and recent versions of
|
||||
* Leopard don't need this hack anymore. However, Tiger still has a
|
||||
* broken version so we leave the hack in for now.
|
||||
*
|
||||
* TODO: Revisit when dropping Tiger support.
|
||||
*
|
||||
* Btw. If you configure other network interfaces using ifconfig,
|
||||
* you run into the same problem. I still don't know how to make the
|
||||
* tap devices show up in the network configuration panel...
|
||||
*/
|
||||
ifaddr_t ifa = (ifaddr_t) arg;
|
||||
if (ifa == NULL)
|
||||
return 0;
|
||||
|
||||
sa_family_t af = ifaddr_address_family(ifa);
|
||||
if (af != AF_INET)
|
||||
return 0;
|
||||
|
||||
struct ifaliasreq ifra;
|
||||
int sa_size = sizeof(struct sockaddr);
|
||||
if (ifaddr_address(ifa, &ifra.ifra_addr, sa_size)
|
||||
|| ifaddr_dstaddress(ifa, &ifra.ifra_broadaddr, sa_size)
|
||||
|| ifaddr_netmask(ifa, &ifra.ifra_mask, sa_size)) {
|
||||
log(LOG_WARNING,
|
||||
"tuntap: failed to parse interface address.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check that the address family fields match. If not, issue another
|
||||
// SIOCAIFADDR to fix the entry.
|
||||
if (ifra.ifra_addr.sa_family != af
|
||||
|| ifra.ifra_broadaddr.sa_family != af
|
||||
|| ifra.ifra_mask.sa_family != af) {
|
||||
log(LOG_INFO, "tuntap: Fixing address family for %s%d\n",
|
||||
family_name, unit);
|
||||
|
||||
snprintf(ifra.ifra_name, sizeof(ifra.ifra_name), "%s%d",
|
||||
family_name, unit);
|
||||
ifra.ifra_addr.sa_family = af;
|
||||
ifra.ifra_broadaddr.sa_family = af;
|
||||
ifra.ifra_mask.sa_family = af;
|
||||
|
||||
do_sock_ioctl(af, SIOCAIFADDR, &ifra);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
case SIOCSIFFLAGS:
|
||||
return 0;
|
||||
|
||||
case SIOCGIFSTATUS:
|
||||
{
|
||||
struct ifstat *stat = (struct ifstat *) arg;
|
||||
int len;
|
||||
char *p;
|
||||
|
||||
if (stat == NULL)
|
||||
return EINVAL;
|
||||
|
||||
/* print status */
|
||||
len = strlen(stat->ascii);
|
||||
p = stat->ascii + len;
|
||||
if (open) {
|
||||
snprintf(p, IFSTATMAX - len, "\topen (pid %u)\n", pid);
|
||||
} else {
|
||||
snprintf(p, IFSTATMAX - len, "\tclosed\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
case SIOCSIFMTU:
|
||||
{
|
||||
struct ifreq *ifr = (struct ifreq *) arg;
|
||||
|
||||
if (ifr == NULL)
|
||||
return EINVAL;
|
||||
|
||||
ifnet_set_mtu(ifp, ifr->ifr_mtu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
case SIOCDIFADDR:
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
errno_t
|
||||
tuntap_interface::if_set_bpf_tap(bpf_tap_mode mode, int (*cb)(ifnet_t, mbuf_t))
|
||||
{
|
||||
dprintf("tuntap: mode %d\n", mode);
|
||||
|
||||
auto_lock l(&bpf_lock);
|
||||
|
||||
bpf_callback = cb;
|
||||
bpf_mode = mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
errno_t
|
||||
tuntap_interface::if_check_multi(const struct sockaddr *maddr)
|
||||
{
|
||||
dprintf("tuntap: if_check_multi\n");
|
||||
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
void
|
||||
tuntap_interface::if_detached()
|
||||
{
|
||||
dprintf("tuntap: if_detached\n");
|
||||
|
||||
/* wake unregister_interface() */
|
||||
detach_lock.lock();
|
||||
interface_detached = true;
|
||||
detach_lock.wakeup(&interface_detached);
|
||||
detach_lock.unlock();
|
||||
|
||||
dprintf("if_detached done\n");
|
||||
}
|
||||
|
301
ext/tap-mac/tuntap/src/tuntap.h
Normal file
301
ext/tap-mac/tuntap/src/tuntap.h
Normal file
|
@ -0,0 +1,301 @@
|
|||
/*
|
||||
* ip tunnel/ethertap device for MacOSX.
|
||||
*
|
||||
* The class tuntaptap_interface contains the common functionality of tuntap_interface and
|
||||
* tap_interface.
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __TUNTAP_H__
|
||||
#define __TUNTAP_H__
|
||||
|
||||
#include "util.h"
|
||||
#include "lock.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/kpi_mbuf.h>
|
||||
|
||||
#include <kern/locks.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/bpf.h>
|
||||
#include <net/kpi_interface.h>
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
errno_t tuntap_if_output(ifnet_t ifp, mbuf_t m);
|
||||
errno_t tuntap_if_ioctl(ifnet_t ifp, long unsigned int cmd, void *arg);
|
||||
errno_t tuntap_if_set_bpf_tap(ifnet_t ifp, bpf_tap_mode mode, int (*cb)(ifnet_t, mbuf_t));
|
||||
errno_t tuntap_if_demux(ifnet_t ifp, mbuf_t m, char *header, protocol_family_t *proto);
|
||||
errno_t tuntap_if_framer(ifnet_t ifp, mbuf_t *m, const struct sockaddr *dest,
|
||||
const char *dest_linkaddr, const char *frame_type);
|
||||
errno_t tuntap_if_add_proto(ifnet_t ifp, protocol_family_t proto,
|
||||
const struct ifnet_demux_desc *ddesc, u_int32_t ndesc);
|
||||
errno_t tuntap_if_del_proto(ifnet_t ifp, protocol_family_t proto);
|
||||
errno_t tuntap_if_check_multi(ifnet_t ifp, const struct sockaddr *maddr);
|
||||
void tuntap_if_detached(ifnet_t ifp);
|
||||
|
||||
}
|
||||
|
||||
/* forward declaration */
|
||||
class tuntap_interface;
|
||||
|
||||
/* both interface families have their manager object that will create, initialize, shutdown and
|
||||
* delete interfaces. This is (mostly) generic so it can be used both for tun and tap. The only
|
||||
* exception is the interface creation, therefore this class is abstract. tun and tap have their own
|
||||
* versions that simply fill in create_interface().
|
||||
*/
|
||||
class tuntap_manager {
|
||||
|
||||
protected:
|
||||
/* manager cdev gate */
|
||||
tt_gate cdev_gate;
|
||||
/* interface count */
|
||||
unsigned int count;
|
||||
/* an array holding all the interface instances */
|
||||
tuntap_interface **tuntaps;
|
||||
/* the major device number */
|
||||
int dev_major;
|
||||
/* family name */
|
||||
char *family;
|
||||
|
||||
/* wether static members are initialized */
|
||||
static bool statics_initialized;
|
||||
|
||||
/* major-to-manager-map */
|
||||
static const int MAX_CDEV = 256;
|
||||
static tuntap_manager *mgr_map[MAX_CDEV];
|
||||
|
||||
/* initializes static members */
|
||||
void initialize_statics();
|
||||
|
||||
public:
|
||||
/* sets major device number, allocates the interface table. */
|
||||
bool initialize(unsigned int count, char *family);
|
||||
|
||||
/* tries to shutdown the family. returns true if successful. the manager object may
|
||||
* not be deleted if this wasn't called successfully.
|
||||
*/
|
||||
bool shutdown();
|
||||
|
||||
/* the destructor deletes allocated memory and unregisters the character device
|
||||
* switch */
|
||||
virtual ~tuntap_manager();
|
||||
|
||||
/* here are the cdev routines for the class. They will figure out the manager object
|
||||
* and call the service methods declared below.
|
||||
*/
|
||||
static int cdev_open(dev_t dev, int flags, int devtype, proc_t p);
|
||||
static int cdev_close(dev_t dev, int flags, int devtype, proc_t p);
|
||||
static int cdev_read(dev_t dev, uio_t uio, int ioflag);
|
||||
static int cdev_write(dev_t dev, uio_t uio, int ioflag);
|
||||
static int cdev_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag,
|
||||
proc_t p);
|
||||
static int cdev_select(dev_t dev, int which, void *wql, proc_t p);
|
||||
|
||||
protected:
|
||||
/* Here are the actual service routines that will do the required things (creating
|
||||
* interfaces and such) and forward to the interface's implementation.
|
||||
*/
|
||||
int do_cdev_open(dev_t dev, int flags, int devtype, proc_t p);
|
||||
int do_cdev_close(dev_t dev, int flags, int devtype, proc_t p);
|
||||
int do_cdev_read(dev_t dev, uio_t uio, int ioflag);
|
||||
int do_cdev_write(dev_t dev, uio_t uio, int ioflag);
|
||||
int do_cdev_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, proc_t p);
|
||||
int do_cdev_select(dev_t dev, int which, void *wql, proc_t p);
|
||||
|
||||
/* abstract method that will create an interface. Implemented by tun and tap */
|
||||
virtual tuntap_interface *create_interface() = 0;
|
||||
|
||||
/* makes sure there is one idle interface available (if nothing fails */
|
||||
void ensure_idle_device();
|
||||
|
||||
};
|
||||
|
||||
/* a class implementing a mbuf packet queue. On Darwin 7 we had struct ifqueue, but that is now
|
||||
* internal to the kernel for Darwin 8. So lets have our own.
|
||||
*/
|
||||
class tuntap_mbuf_queue {
|
||||
|
||||
private:
|
||||
/* output end of the queue. dequeueing takes mbufs from here */
|
||||
mbuf_t head;
|
||||
/* input end. new mbufs are appended here. */
|
||||
mbuf_t tail;
|
||||
|
||||
/* size */
|
||||
unsigned int size;
|
||||
|
||||
/* maximum queue size */
|
||||
static const unsigned int QUEUE_SIZE = 128;
|
||||
|
||||
public:
|
||||
/* initialize new empty queue */
|
||||
tuntap_mbuf_queue();
|
||||
~tuntap_mbuf_queue();
|
||||
|
||||
/* is the queue full? */
|
||||
bool full() { return size == QUEUE_SIZE; }
|
||||
/* is it emtpy? */
|
||||
bool empty() { return size == 0; }
|
||||
|
||||
/* enqueue an mbuf. returns true if there was space left, so the mbuf could be
|
||||
* queued, false otherwise */
|
||||
bool enqueue(mbuf_t mb);
|
||||
|
||||
/* tries to dequeue the next mbuf. If the queue is empty, NULL is returned */
|
||||
mbuf_t dequeue();
|
||||
|
||||
/* makes the queue empty, discarding any queue packets */
|
||||
void clear();
|
||||
};
|
||||
|
||||
class tuntap_interface {
|
||||
|
||||
protected:
|
||||
/* interface number */
|
||||
unsigned int unit;
|
||||
/* family name */
|
||||
char *family_name;
|
||||
/* family identifier */
|
||||
ifnet_family_t family;
|
||||
/* interface type */
|
||||
u_int32_t type;
|
||||
/* id string */
|
||||
static const unsigned int UIDLEN = 20;
|
||||
char unique_id[UIDLEN];
|
||||
|
||||
/* synchronization */
|
||||
tt_mutex lock;
|
||||
tt_mutex bpf_lock;
|
||||
tt_mutex detach_lock;
|
||||
|
||||
/* the interface structure registered */
|
||||
ifnet_t ifp;
|
||||
/* whether the device has been opened */
|
||||
bool open;
|
||||
/* whether we are doing blocking i/o */
|
||||
bool block_io;
|
||||
/* whether the interface has properly been detached */
|
||||
bool interface_detached;
|
||||
/* handle to the devfs node for the character device */
|
||||
void *dev_handle;
|
||||
/* the pid of the process that opened the cdev, if any */
|
||||
pid_t pid;
|
||||
/* read select info */
|
||||
struct selinfo rsel;
|
||||
/* bpf mode, wether filtering is on or off */
|
||||
bpf_tap_mode bpf_mode;
|
||||
/* bpf callback. called when packet arrives/leaves */
|
||||
int (*bpf_callback)(ifnet_t, mbuf_t);
|
||||
/* pending packets queue (for output), must be accessed with the lock held */
|
||||
tuntap_mbuf_queue send_queue;
|
||||
/* whether an ioctl that we issued is currently being processed */
|
||||
bool in_ioctl;
|
||||
|
||||
/* protected constructor. initializes most of the members */
|
||||
tuntap_interface();
|
||||
virtual ~tuntap_interface();
|
||||
|
||||
/* initialize the device */
|
||||
virtual bool initialize(unsigned short major, unsigned short unit) = 0;
|
||||
|
||||
/* character device management */
|
||||
virtual bool register_chardev(unsigned short major);
|
||||
virtual void unregister_chardev();
|
||||
|
||||
/* network interface management */
|
||||
virtual bool register_interface(const struct sockaddr_dl *lladdr,
|
||||
void *bcaddr, u_int32_t bcaddrlen);
|
||||
virtual void unregister_interface();
|
||||
virtual void cleanup_interface();
|
||||
|
||||
/* called when the character device is opened in order to intialize the network
|
||||
* interface.
|
||||
*/
|
||||
virtual int initialize_interface() = 0;
|
||||
/* called when the character device is closed to shutdown the network interface */
|
||||
virtual void shutdown_interface() = 0;
|
||||
|
||||
/* check wether the interface is idle (so it can be brought down) */
|
||||
virtual bool idle();
|
||||
|
||||
/* shut it down */
|
||||
virtual void shutdown() = 0;
|
||||
|
||||
/* notifies BPF of a packet coming through */
|
||||
virtual void notify_bpf(mbuf_t mb, bool out);
|
||||
|
||||
/* executes a socket ioctl through a temporary socket */
|
||||
virtual void do_sock_ioctl(sa_family_t af, unsigned long cmd, void* arg);
|
||||
|
||||
/* character device service methods. Called by the manager */
|
||||
virtual int cdev_open(int flags, int devtype, proc_t p);
|
||||
virtual int cdev_close(int flags, int devtype, proc_t p);
|
||||
virtual int cdev_read(uio_t uio, int ioflag);
|
||||
virtual int cdev_write(uio_t uio, int ioflag);
|
||||
virtual int cdev_ioctl(u_long cmd, caddr_t data, int fflag, proc_t p);
|
||||
virtual int cdev_select(int which, void *wql, proc_t p);
|
||||
|
||||
/* interface functions. friends and implementation methods */
|
||||
friend errno_t tuntap_if_output(ifnet_t ifp, mbuf_t m);
|
||||
friend errno_t tuntap_if_ioctl(ifnet_t ifp, long unsigned int cmd, void *arg);
|
||||
friend errno_t tuntap_if_set_bpf_tap(ifnet_t ifp, bpf_tap_mode mode,
|
||||
int (*cb)(ifnet_t, mbuf_t));
|
||||
friend errno_t tuntap_if_demux(ifnet_t ifp, mbuf_t m, char *header,
|
||||
protocol_family_t *proto);
|
||||
friend errno_t tuntap_if_framer(ifnet_t ifp, mbuf_t *m, const struct sockaddr *dest,
|
||||
const char *dest_linkaddr, const char *frame_type);
|
||||
friend errno_t tuntap_if_add_proto(ifnet_t ifp, protocol_family_t proto,
|
||||
const struct ifnet_demux_desc *ddesc, u_int32_t ndesc);
|
||||
friend errno_t tuntap_if_del_proto(ifnet_t ifp, protocol_family_t proto);
|
||||
friend errno_t tuntap_if_check_multi(ifnet_t ifp, const struct sockaddr *maddr);
|
||||
friend void tuntap_if_detached(ifnet_t ifp);
|
||||
|
||||
virtual errno_t if_output(mbuf_t m);
|
||||
virtual errno_t if_ioctl(u_int32_t cmd, void *arg);
|
||||
virtual errno_t if_set_bpf_tap(bpf_tap_mode mode, int (*cb)(ifnet_t, mbuf_t));
|
||||
virtual errno_t if_demux(mbuf_t m, char *header, protocol_family_t *proto) = 0;
|
||||
virtual errno_t if_framer(mbuf_t *m, const struct sockaddr *dest,
|
||||
const char *dest_linkaddr, const char *frame_type) = 0;
|
||||
virtual errno_t if_add_proto(protocol_family_t proto,
|
||||
const struct ifnet_demux_desc *ddesc, u_int32_t ndesc) = 0;
|
||||
virtual errno_t if_del_proto(protocol_family_t proto) = 0;
|
||||
virtual errno_t if_check_multi(const struct sockaddr *maddr);
|
||||
virtual void if_detached();
|
||||
|
||||
/* tuntap_manager feeds us with cdev input, so it is our friend */
|
||||
friend class tuntap_manager;
|
||||
};
|
||||
|
||||
#endif /* __TUNTAP_H__ */
|
||||
|
372
ext/tap-mac/tuntap/src/tuntap_mgr.cc
Normal file
372
ext/tap-mac/tuntap/src/tuntap_mgr.cc
Normal file
|
@ -0,0 +1,372 @@
|
|||
/*
|
||||
* ip tunnel/ethertap device for MacOSX.
|
||||
*
|
||||
* tuntap_manager definition.
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "tuntap.h"
|
||||
#include "mem.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include <sys/conf.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/syslog.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <vm/vm_kern.h>
|
||||
|
||||
#include <miscfs/devfs/devfs.h>
|
||||
|
||||
}
|
||||
|
||||
#if 0
|
||||
#define dprintf(...) log(LOG_INFO, __VA_ARGS__)
|
||||
#else
|
||||
#define dprintf(...)
|
||||
#endif
|
||||
|
||||
/* cdevsw for tuntap_manager */
|
||||
static struct cdevsw mgr_cdevsw =
|
||||
{
|
||||
tuntap_manager::cdev_open,
|
||||
tuntap_manager::cdev_close,
|
||||
tuntap_manager::cdev_read,
|
||||
tuntap_manager::cdev_write,
|
||||
tuntap_manager::cdev_ioctl,
|
||||
eno_stop,
|
||||
eno_reset,
|
||||
NULL,
|
||||
tuntap_manager::cdev_select,
|
||||
eno_mmap,
|
||||
eno_strat,
|
||||
eno_getc,
|
||||
eno_putc,
|
||||
0
|
||||
};
|
||||
|
||||
/* tuntap_manager members */
|
||||
tuntap_manager *tuntap_manager::mgr_map[MAX_CDEV];
|
||||
|
||||
bool tuntap_manager::statics_initialized = false;
|
||||
|
||||
/* static initializer */
|
||||
void
|
||||
tuntap_manager::initialize_statics()
|
||||
{
|
||||
dprintf("initializing mgr_map\n");
|
||||
|
||||
/* initialize the major-to-manager map */
|
||||
for (int i = 0; i < MAX_CDEV; i++)
|
||||
mgr_map[i] = NULL;
|
||||
|
||||
statics_initialized = true;
|
||||
}
|
||||
|
||||
bool
|
||||
tuntap_manager::initialize(unsigned int count, char *family)
|
||||
{
|
||||
this->count = count;
|
||||
this->family = family;
|
||||
this->tuntaps = NULL;
|
||||
|
||||
if (!statics_initialized)
|
||||
initialize_statics();
|
||||
|
||||
/* make sure noone can access the character devices until we are done */
|
||||
auto_lock l(&cdev_gate);
|
||||
|
||||
/* register the switch for the tap character devices */
|
||||
dev_major = cdevsw_add(-1, &mgr_cdevsw);
|
||||
if (dev_major == -1) {
|
||||
log(LOG_ERR, "%s: could not register character device switch.\n", family);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* allocate memory for the interface instance table */
|
||||
tuntaps = (tuntap_interface **) mem_alloc(count * sizeof(tuntap_interface *));
|
||||
if (tuntaps == NULL)
|
||||
{
|
||||
log(LOG_ERR, "%s: no memory!\n", family);
|
||||
return false;
|
||||
}
|
||||
|
||||
bzero(tuntaps, count * sizeof(tuntap_interface *));
|
||||
|
||||
/* Create the interfaces. This will only add the character devices. The network devices will
|
||||
* be created upon open()ing the corresponding character devices.
|
||||
*/
|
||||
for (int i = 0; i < (int) count; i++)
|
||||
{
|
||||
tuntaps[i] = create_interface();
|
||||
|
||||
if (tuntaps[i] != NULL)
|
||||
{
|
||||
if (tuntaps[i]->initialize(dev_major, i))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* error here. current interface needs to be shut down */
|
||||
i++;
|
||||
}
|
||||
|
||||
/* something went wrong. clean up. */
|
||||
while (--i >= 0)
|
||||
{
|
||||
tuntaps[i]->shutdown();
|
||||
delete tuntaps[i];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* register the new family in the mgr switch */
|
||||
mgr_map[dev_major] = this;
|
||||
|
||||
log(LOG_INFO, "%s kernel extension version %s <mattias.nissler@gmx.de>\n",
|
||||
family, TUNTAP_VERSION);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
tuntap_manager::shutdown()
|
||||
{
|
||||
bool ok = true;
|
||||
|
||||
/* we halt the whole thing while we check whether we can shutdown */
|
||||
auto_lock l(&cdev_gate);
|
||||
|
||||
/* anyone in? */
|
||||
if (cdev_gate.is_anyone_in()) {
|
||||
dprintf("tuntap_mgr: won't shutdown, threads still behind the gate.");
|
||||
ok = false;
|
||||
} else {
|
||||
/* query the interfaces to see if shutting down is ok */
|
||||
if (tuntaps != NULL) {
|
||||
for (unsigned int i = 0; i < count; i++) {
|
||||
if (tuntaps[i] != NULL)
|
||||
ok &= tuntaps[i]->idle();
|
||||
}
|
||||
|
||||
/* if yes, do it now */
|
||||
if (ok) {
|
||||
for (unsigned int i = 0; i < count; i++) {
|
||||
if (tuntaps[i] != NULL) {
|
||||
tuntaps[i]->shutdown();
|
||||
delete tuntaps[i];
|
||||
tuntaps[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* unregister the character device switch */
|
||||
if (ok) {
|
||||
if (dev_major != -1 && cdevsw_remove(dev_major, &mgr_cdevsw) == -1) {
|
||||
log(LOG_WARNING,
|
||||
"%s: character device switch got lost. strange.\n", family);
|
||||
}
|
||||
mgr_map[dev_major] = NULL;
|
||||
dev_major = -1;
|
||||
|
||||
/* at this point there is still a chance that some thread hangs at the cdev_gate in
|
||||
* one of the cdev service functions. I can't imagine any way that would aviod this.
|
||||
* So lets unblock the gate such that they fail.
|
||||
*/
|
||||
unsigned int old_number;
|
||||
do {
|
||||
old_number = cdev_gate.get_ticket_number();
|
||||
|
||||
dprintf("tuntap_manager: waiting for other threads to give up.\n");
|
||||
|
||||
/* wait one second */
|
||||
cdev_gate.sleep(&cdev_gate, 1000000);
|
||||
|
||||
} while (cdev_gate.get_ticket_number() != old_number);
|
||||
|
||||
/* I hope it is safe to unload now. */
|
||||
|
||||
} else {
|
||||
log(LOG_WARNING, "%s: won't unload, at least one interface is busy.\n", family);
|
||||
}
|
||||
|
||||
dprintf("tuntap manager: shutdown %s\n", ok ? "ok" : "failed");
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
tuntap_manager::~tuntap_manager()
|
||||
{
|
||||
dprintf("freeing interface table\n");
|
||||
|
||||
/* free memory */
|
||||
if (tuntaps != NULL)
|
||||
mem_free(tuntaps, count * sizeof(tuntap_interface *));
|
||||
}
|
||||
|
||||
/* service method dispatchers */
|
||||
int
|
||||
tuntap_manager::cdev_open(dev_t dev, int flags, int devtype, proc_t p)
|
||||
{
|
||||
return (mgr_map[major(dev)] == NULL ? ENOENT
|
||||
: mgr_map[major(dev)]->do_cdev_open(dev, flags, devtype, p));
|
||||
}
|
||||
|
||||
int
|
||||
tuntap_manager::cdev_close(dev_t dev, int flags, int devtype, proc_t p)
|
||||
{
|
||||
return (mgr_map[major(dev)] == NULL ? EBADF
|
||||
: mgr_map[major(dev)]->do_cdev_close(dev, flags, devtype, p));
|
||||
}
|
||||
|
||||
int
|
||||
tuntap_manager::cdev_read(dev_t dev, uio_t uio, int ioflag)
|
||||
{
|
||||
return (mgr_map[major(dev)] == NULL ? EBADF
|
||||
: mgr_map[major(dev)]->do_cdev_read(dev, uio, ioflag));
|
||||
}
|
||||
|
||||
int
|
||||
tuntap_manager::cdev_write(dev_t dev, uio_t uio, int ioflag)
|
||||
{
|
||||
return (mgr_map[major(dev)] == NULL ? EBADF
|
||||
: mgr_map[major(dev)]->do_cdev_write(dev, uio, ioflag));
|
||||
}
|
||||
|
||||
int
|
||||
tuntap_manager::cdev_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, proc_t p)
|
||||
{
|
||||
return (mgr_map[major(dev)] == NULL ? EBADF
|
||||
: mgr_map[major(dev)]->do_cdev_ioctl(dev, cmd, data, fflag, p));
|
||||
}
|
||||
|
||||
int
|
||||
tuntap_manager::cdev_select(dev_t dev, int which, void *wql, proc_t p)
|
||||
{
|
||||
return (mgr_map[major(dev)] == NULL ? EBADF
|
||||
: mgr_map[major(dev)]->do_cdev_select(dev, which, wql, p));
|
||||
}
|
||||
|
||||
/* character device service methods */
|
||||
int
|
||||
tuntap_manager::do_cdev_open(dev_t dev, int flags, int devtype, proc_t p)
|
||||
{
|
||||
int dmin = minor(dev);
|
||||
int error = ENOENT;
|
||||
|
||||
cdev_gate.enter();
|
||||
|
||||
if (dmin < (int) count && dmin >= 0 && tuntaps[dmin] != NULL)
|
||||
error = tuntaps[dmin]->cdev_open(flags, devtype, p);
|
||||
|
||||
cdev_gate.exit();
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
tuntap_manager::do_cdev_close(dev_t dev, int flags, int devtype, proc_t p)
|
||||
{
|
||||
int dmin = minor(dev);
|
||||
int error = EBADF;
|
||||
|
||||
cdev_gate.enter();
|
||||
|
||||
if (dmin < (int) count && dmin >= 0 && tuntaps[dmin] != NULL)
|
||||
error = tuntaps[dmin]->cdev_close(flags, devtype, p);
|
||||
|
||||
cdev_gate.exit();
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
tuntap_manager::do_cdev_read(dev_t dev, uio_t uio, int ioflag)
|
||||
{
|
||||
int dmin = minor(dev);
|
||||
int error = EBADF;
|
||||
|
||||
cdev_gate.enter();
|
||||
|
||||
if (dmin < (int) count && dmin >= 0 && tuntaps[dmin] != NULL)
|
||||
error = tuntaps[dmin]->cdev_read(uio, ioflag);
|
||||
|
||||
cdev_gate.exit();
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
tuntap_manager::do_cdev_write(dev_t dev, uio_t uio, int ioflag)
|
||||
{
|
||||
int dmin = minor(dev);
|
||||
int error = EBADF;
|
||||
|
||||
cdev_gate.enter();
|
||||
|
||||
if (dmin < (int) count && dmin >= 0 && tuntaps[dmin] != NULL)
|
||||
error = tuntaps[dmin]->cdev_write(uio, ioflag);
|
||||
|
||||
cdev_gate.exit();
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
tuntap_manager::do_cdev_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, proc_t p)
|
||||
{
|
||||
int dmin = minor(dev);
|
||||
int error = EBADF;
|
||||
|
||||
cdev_gate.enter();
|
||||
|
||||
if (dmin < (int) count && dmin >= 0 && tuntaps[dmin] != NULL)
|
||||
error = tuntaps[dmin]->cdev_ioctl(cmd, data, fflag, p);
|
||||
|
||||
cdev_gate.exit();
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
tuntap_manager::do_cdev_select(dev_t dev, int which, void *wql, proc_t p)
|
||||
{
|
||||
int dmin = minor(dev);
|
||||
int error = EBADF;
|
||||
|
||||
cdev_gate.enter();
|
||||
|
||||
if (dmin < (int) count && dmin >= 0 && tuntaps[dmin] != NULL)
|
||||
error = tuntaps[dmin]->cdev_select(which, wql, p);
|
||||
|
||||
cdev_gate.exit();
|
||||
|
||||
return error;
|
||||
}
|
||||
|
46
ext/tap-mac/tuntap/src/util.h
Normal file
46
ext/tap-mac/tuntap/src/util.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* ip tunnel/ethertap device for MacOSX.
|
||||
*
|
||||
* Some utilities and misc stuff.
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __UTIL_H__
|
||||
#define __UTIL_H__
|
||||
|
||||
extern "C" {
|
||||
|
||||
/* In Darwin 8 (OS X Tiger) there is a problem with struct selinfo. It was made `private' to the
|
||||
* kernel, so its definition is not available from the headers in Kernel.framework. However, we need
|
||||
* to declare something :-(
|
||||
*/
|
||||
struct selinfo {
|
||||
char data[128]; /* should be enough... */
|
||||
};
|
||||
|
||||
} /* extern "C" */
|
||||
|
||||
#endif /* __UTIL_H__ */
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
|
||||
<plist version="0.9">
|
||||
<dict>
|
||||
<key>ethertap device kernel extension</key>
|
||||
<string>ethertap kernel extension</string>
|
||||
<key>Initializing tap devices</key>
|
||||
<string>Initializing tap devices</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
Description = "ethertap device kernel extension";
|
||||
Provides = ("ethertap");
|
||||
Requires = ("Network Configuration");
|
||||
OrderPreference = "None";
|
||||
}
|
33
ext/tap-mac/tuntap/startup_item/tap/tap
Executable file
33
ext/tap-mac/tuntap/startup_item/tap/tap
Executable file
|
@ -0,0 +1,33 @@
|
|||
#!/bin/sh
|
||||
|
||||
##
|
||||
# load the tap kext
|
||||
##
|
||||
|
||||
. /etc/rc.common
|
||||
|
||||
StartService ()
|
||||
{
|
||||
ConsoleMessage "Initializing tap devices"
|
||||
|
||||
if [ -d /Library/Extensions/tap.kext ]; then
|
||||
kextload /Library/Extensions/tap.kext
|
||||
fi
|
||||
}
|
||||
|
||||
StopService ()
|
||||
{
|
||||
if [ -d /Library/Extensions/tap.kext ]; then
|
||||
kextunload /Library/Extensions/tap.kext
|
||||
fi
|
||||
}
|
||||
|
||||
RestartService ()
|
||||
{
|
||||
if [ -d /Library/Extensions/tap.kext ]; then
|
||||
kextunload /Library/Extensions/tap.kext
|
||||
kextload /Library/Extensions/tap.kext
|
||||
fi
|
||||
}
|
||||
|
||||
RunService "$1"
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
|
||||
<plist version="0.9">
|
||||
<dict>
|
||||
<key>ip tunnel device kernel extension</key>
|
||||
<string>ip tunnel kernel extension</string>
|
||||
<key>Initializing tun devices</key>
|
||||
<string>Initializing tun devices</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
Description = "ip tunnel device kernel extension";
|
||||
Provides = ("tun");
|
||||
Requires = ("Network Configuration");
|
||||
OrderPreference = "None";
|
||||
}
|
33
ext/tap-mac/tuntap/startup_item/tun/tun
Executable file
33
ext/tap-mac/tuntap/startup_item/tun/tun
Executable file
|
@ -0,0 +1,33 @@
|
|||
#!/bin/sh
|
||||
|
||||
##
|
||||
# load the tun kext
|
||||
##
|
||||
|
||||
. /etc/rc.common
|
||||
|
||||
StartService ()
|
||||
{
|
||||
ConsoleMessage "Initializing tun devices"
|
||||
|
||||
if [ -d /Library/Extensions/tun.kext ]; then
|
||||
kextload /Library/Extensions/tun.kext
|
||||
fi
|
||||
}
|
||||
|
||||
StopService ()
|
||||
{
|
||||
if [ -d /Library/Extensions/tun.kext ]; then
|
||||
kextunload /Library/Extensions/tun.kext
|
||||
fi
|
||||
}
|
||||
|
||||
RestartService ()
|
||||
{
|
||||
if [ -d /Library/Extensions/tun.kext ]; then
|
||||
kextunload /Library/Extensions/tun.kext
|
||||
kextload /Library/Extensions/tun.kext
|
||||
fi
|
||||
}
|
||||
|
||||
RunService "$1"
|
Loading…
Add table
Add a link
Reference in a new issue