Second night's commit.

We're a bit closer to something sane, now. We can wrap, but not unwrap,
packets.

Asymmetric encryption is *big*. encrypted text with a 4096-bit RSA
public key is 512 bytes. We can't fragment yet. Fortunately, this
isn't an infinite regress once we *can* fragment.

Performance is still a big question mark, of course.

There may still be endianness issues hanging around.

The eid<->rloc map is almost certainly far, far too slow to be of
any use in the real world.
main
Nick Thomas 10 years ago
parent c41a33e8b2
commit 15a200eb0c

1
.gitignore vendored

@ -1,3 +1,4 @@
*.o
pass-1/wrapper
pass-1/unwrapper
pass-1/example-keys

@ -22,3 +22,7 @@ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
This product includes software developed by the OpenSSL Project for use in the
OpenSSL Toolkit (http://www.openssl.org/)]

@ -1,12 +1,15 @@
#!/usr/bin/make -f
CFLAGS := $(CFLAGS) -Wall -Werror --std=c99
CFLAGS=-Wall -Werror --std=gnu99
LDFLAGS=-ljson-c -lpcap
LDFLAGS := $(LDFLAGS) -ljson-c -lssl -lcrypto
.PHONY: clean
all: wrapper unwrapper
rlocs.o: util.o
wrapper: util.o rlocs.o
clean:
rm -f wrapper unwrapper
rm -f wrapper unwrapper *.o

@ -21,7 +21,7 @@ running on. It then drops a bgpfeeder-suitable file on disc that will redirect
all the EIDs in the rloc registry to its IP address, and waits for packets.
Every time a packet comes in, wrapper reads it, then constructs and emits a
corresponding wrapped packet. Probably using pcap to start.
corresponding wrapped packet.
Don't bother implementing RLOC pools for ICMP yet.
@ -29,9 +29,8 @@ unwrapper
=========
This component is fed a private key, and told which RLOC(s) to listen on. It
dumps a bgpfeeder-compatible file to disc directing those RLOCs to it, then
(again, probably using pcap) waits for wrapped packets to come to it. As it
receives them, it decrypts them with the private key, then constructs the
unwrapped equivalent and forwards it on.
waits for wrapped packets to come to it. As it receives them, it decrypts them
with the private key, then constructs the unwrapped equivalent and forwards it.
bgpfeeder

@ -1,19 +1,79 @@
{
"type": "rloc-registry",
"eid_rloc_map": {
"172.16.10.0/24" : "172.16.10.2",
"172.16.11.0/24" : "172.16.10.2",
"172.16.12.0/24" : "172.16.12.2",
"fc00:1::/64" : "fc00::2",
"fc00:2::/64" : "fc00:2::2"
},
"eid_rloc_map": [
{ "family":"ipv4", "network":"172.16.10.0", "netmask":24, "rloc":"172.16.10.2"},
{ "family":"ipv4", "network":"172.16.11.0", "netmask":24, "rloc":"172.16.10.2"},
{ "family":"ipv4", "network":"172.16.12.0", "netmask":24, "rloc":"172.16.12.2"},
{ "family":"ipv6", "network":"fc00:1::", "netmask":64, "rloc":"fc00::2" },
{ "family":"ipv6", "network":"fc00:2::", "netmask":64, "rloc":"fc00:2::2" }
],
"keys": {
"172.16.10.2":"public key for 172.16.10.2",
"172.16.12.2":"public key for 172.16.12.2",
"fc00::2":"public key for fc00::2",
"fc00:2::2":"public key for fc00:2::2"
"172.16.10.2":
"-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0FxctMUibDqkVhChIvJg
6GhzKzaMEKza+9MhO1H0VEnvgMsvZ6S15iSxHVxiO5KpjwYAakW4ELLK7We+exsH
zOSAioh1Po3uxTLga7PrEvLkv7hI/2vxWPYk2j2j++WHKXjyNoAKpdC5nrauRPT2
DAlbRcRu9gnhLIIDu/HCLLxT1qQgWOaVZBJVxxcPYwrBPSjDAvKn5GKbN8wh4k1h
WIkedhHIjzM7mYQfXTaJttP2+9Gco5sOntKVldy2nGWWtaPDtlMmmCdclgrBpxT9
EPEZ+H9ApPWHAMEgVvLn9nKPNJ/06jPFEcA5ZEtu6N+lPjkjRU9JfuMPt/jz3otY
1iA/Utx1tQQqEzote5V5zxHnBcYtqtftf7PKANVfPnLQxQ9msKmrcBb0bNwn3KdX
pavbsPp5W4WGFalt499u3CGmd6AQ9/tZfafTEsAPCpNQrfZ4v0AVIiT6ZuQQ1Vip
q14pHlpN3xepxwHZtCJTyU7kHaWKvImFZRY6dg0dRkvWl2bO6xJsYYXTaYHre+Sx
ivtEZcM7VPEZlQyBlwTYYHmnllBlnXMJpx20p26Sy4iPX6hzn9sT3UQHE6NAlfON
He5Cw+4ma5DA2jQvvIBfYA1ipVfGKD8LyrPrxuD9qErYpPBP23EdJwT0ZL+jKjyT
t39smrDydkWUQWYPiiUKKM0CAwEAAQ==
-----END PUBLIC KEY-----
",
"172.16.12.2":
"-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvYljSQd/jHZtSTc6Z2E1
6D37s/MDGmWux/0ulRLsnGRnIj/cd8b3wV2AKbucjEOZ70MH5mU3szz/eZwD5z3A
hU+2ok9GV3vL1COg692RRV9ydizxc/8PduYyojeBiOgwBQvCS0PM6IcEDbNvgtfY
yzcnMf56Iadqukp5ovCZU/xl4zPMNwtVonMoeOgpdPBd9POBAhXI5zvZDEIF+PM3
Lfuy+ybn5alJKDQwWE2tjWnNWx3e34JKTulN7xC9Cw4xw+zmoG5GYua5RtM7AYsJ
cCBmF4V3XhpPnxgGhO5S3tKWyke317FjNBqbG1Gj30ADYliHZI1KEQbaXCHxB8lQ
bII6OEz9liz4QNSDd2iTInA7yv2djOIdEoPCgtY7oXQZi5Ch8q2M9sAVGAyyF2sR
2SlUiP0fuH2S5CviI/5KKTHMA8HNg2rH6gPeTDiLDluw+oL3tIy3XEmhUJS3ZSzG
PhN1So0n3XwsnAUWvm33ujTtvAGTuUwBKsG8nIb3iwr7Vh8O617yuOl7ab68Dxym
fJZZE5wzjzYbNxup8E9VSfHKSCUHptg9cB/1kF5HoshZCZwA62eAtQgaamr64NrT
Uuyz1Ng3KTiD1cObXeirB88yyYd1W21deRfGaGZlWpJTAMjy/bXjR6ZAA+xTXllO
QFqgpMLJRNxWj7UUOWm3xjECAwEAAQ==
-----END PUBLIC KEY-----
",
"fc00::2":
"-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnQhQ9S190q/+t0/QqplD
+aRHLrVm8oWFHKqfaE11zZ+7adse4NFgzEWMRN2bUIl47W/VuTHn3NtsF3myYjtX
n9bXW/fibVj1xFXk5qpsHvC1xgdUTP3msqiEXigHIgJKgaTCDOaYykfBaFKNP334
Dz0mZ4xTmMwmp/9MEiaj8ieFWBTJ5+QFJZCwYUCNGI3hWaXrmlNoKBIj4vfpnFv1
MUSCcvc3yl4+KBnIqq0rNfQqlvwNl1NfI2P80eGKW6urQSIjxqb966pOilVePoj8
iY8LUtT4WknCacM1oi9rVE0sjtZxeLUs5EuToFn6O2L576SJvNg9GvGA4YmqPTkS
fkxunarY+I7frdZyTWg/Bgr5rWXP7QJ5VY11VbsTo7vGblWQ6MqYH00owGuKXFqg
7Fl+dr+Ko3uLLnWXF92f4xvP5//0YmRADDGNu1Elgr7NxEoIe7iWFuaga2tV3BPf
//3nneHhPv3d4OYnImMQLih+OF25hfYFI8V1ef5dpKXuSNQ01nxIexRHTd0sQnTS
Agq3TFQzB5U54k554plWUjPvHFvUDmCyAdTAEn2/0xJQZ3X1RqFZfzjW+rcARjnt
lxtMZOyE1S1kAg0HS0uEGLYrIp0K+moEKhm3Upvv3ad3FP1695tJUBec0KH8/PbX
XlTdlmviw5Fo15QnPOLcsW8CAwEAAQ==
-----END PUBLIC KEY-----",
"fc00:2::2":
"-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAt/rGbJ46DIciiRHaknkV
pQ0ulKtZl5UApOaKTP8SxuEoDDrUZ7w3FnItPYkDh41RisVf7hBAKe+BwCRxodo0
++MeMcbrWG16rT/2AVi7V5te936L3O/WiF/BFgWXgJ1+bgXlZlIsJWLqMT5ozcaM
XRIosgoYMkvvS4tBYLwQIx3ov+PVfZELOmqtoesv5e70Xg0iXNVb57wTnsnBPHo0
u4Pw1oSsqDNM4oblDwXtfJEUMcBA+q12L5lqBEnXGlFQgeViCFnpaR03oc0btn0S
PZtAgZ88WPdnf+jAeSeEnVvTmhQtNxmnZXtEy/T0emD+163/lpcr7vIK/iFXvZsx
Bf2xdRs7EU/6M+YaE8rhI/uTNgMpR2RGAIyQn8aGCydGf/FCxXMBg99mk5R0h2cD
BkGyVbmFzXrsoitw5Foz/JC5svvoR/z16w/+lIbRRx2OMSaMtoRqYO6At2GC30TZ
DSDQ5nNOo4hmySZnrEWq+t4BJjJ1wu7eAJpsCVXkyRZUtNt6c98VaAuJQv7FHW/O
75XaEHewVAAGTmQEjI/cTY72bHkbtE3vpi0aK8Wc7K4g7slUld0HUsh8xszIhdxu
8Y+yuP72oOpicPK8LCDyI28V0HCz44bo1TDrDUC0YZ1fDr8Pk2A+jrlt4/yMcgkn
PBcctUqN/b2cIfCA8UDAMZ0CAwEAAQ==
-----END PUBLIC KEY-----
"
}

@ -0,0 +1,425 @@
/* Registry dependencies */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
/* We use RSA public-key encryption to begin with. We expect the config file
* to give us PEM-encoded data.
*/
#include <openssl/ssl.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/bio.h>
#include <openssl/rand.h>
#include <json/json_tokener.h>
#include <json/linkhash.h>
#include <arpa/inet.h>
#include "util.h"
#include "rlocs.h"
#define JSON_TYPE "rloc-registry"
void rlocs_init(void)
{
uint32_t seed;
SSL_load_error_strings(); /* readable error messages */
SSL_library_init(); /* initialize library */
// set the random seed to something good
if ( RAND_bytes( (unsigned char*)&seed, sizeof( uint32_t ) ) != 1 ) {
warn( "Failed to initialize random seed" );
exit(1);
}
srand( seed );
return;
}
int rlocs_rloc_from_json(struct rlocs* reg, const char* key, json_object* val )
{
ssize_t pem_data_size;
const char* pem_data;
struct rloc *rloc;
RSA *rsa_key, *result;
BIO *bp;
if ( !json_object_is_type( val, json_type_string ) ) {
warn( "Non-string value in 'keys' object" );
return 0;
}
if ( reg->num_entries >= MAX_RLOCS ) {
warn( "Too many RLOCs in config-file!" );
return 0;
}
rloc = &reg->entries[reg->num_entries];
if ( strchr( key, ':' ) == NULL ) { /* IPv4 */
rloc->family = AF_INET;
} else { /* IPv6 */
rloc->family = AF_INET6;
}
if ( inet_pton( rloc->family, key, (void*)&rloc->addr ) != 1 ) {
warn( "Couldn't parse %s as an IP address", key );
return 0;
}
// Next, we need to read and store the PEM-encoded RSA public key
pem_data = json_object_get_string( val );
pem_data_size = json_object_get_string_len( val );
rsa_key = RSA_new();
bp = BIO_new_mem_buf( (void*)pem_data, pem_data_size );
result = PEM_read_bio_RSA_PUBKEY( bp, &rsa_key, NULL, NULL );
BIO_vfree( bp );
if ( result == NULL ) {
RSA_free( rsa_key );
warn( "Couldn't parse key for %s as an RSA public key", key );
return 0;
}
rloc->key = rsa_key;
reg->num_entries++;
return 1;
}
int rlocs_eid_from_json( struct rlocs *reg, json_object *map)
{
json_object *entry;
const char *str;
int mask;
if ( !json_object_is_type( map, json_type_object ) ) {
warn( "eid_rloc_maps array in config contains non-object" );
return 0;
}
/* Initial processing of the netmask entry */
entry = json_object_object_get( map, "netmask" );
if ( !json_object_is_type( entry, json_type_int ) ) {
warn( "No netmask specified" );
return 0;
}
mask = json_object_get_int( entry );
if ( mask < 0 ) {
warn( "Bad netmask specified (%i)", mask );
return 0;
}
/* Work out the family and finish off processing */
entry = json_object_object_get( map, "family" );
if ( !json_object_is_type( entry, json_type_string ) ) {
warn( "No ip address family specified" );
return 0;
}
str = json_object_get_string( entry );
if ( strcmp( "ipv4", str ) == 0 ) {
struct ip4_eid_map_entry *eid_mapping;
struct in_addr addr;
if ( reg->num_ip4_map_entries >= MAX_EID_MAPPINGS ) {
warn( "Limit of %i IPv4 EID mappings reached!", MAX_EID_MAPPINGS );
return 0;
}
if ( mask > 32 ) {
warn( "Bad netmask specified for IPv4 (%i)", mask );
return 0;
}
eid_mapping = &reg->ip4_mappings[reg->num_ip4_map_entries];
eid_mapping->mask = (unsigned int) mask;
// parse our network address
entry = json_object_object_get( map, "network" );
if (!json_object_is_type( entry, json_type_string ) ) {
warn( "No network specified" );
return 0;
}
str = json_object_get_string( entry );
if ( inet_pton( AF_INET, str, &eid_mapping->network ) != 1 ) {
warn( "Invalid network specified: %s", str );
return 0;
}
// Remember, in_addrs are stored in network byte order
eid_mapping->broadcast.s_addr =
htonl( ntohl( eid_mapping->network.s_addr ) | (( 1 << (32 - mask )) -1 ) );
// Get a pointer to the rloc these eids are mapped to
entry = json_object_object_get( map, "rloc" );
if ( !json_object_is_type( entry, json_type_string ) ) {
warn( "no rloc specified" );
return 0;
}
str = json_object_get_string( entry );
if ( inet_pton( AF_INET, str, &addr ) != 1 ) {
warn( "Invalid rloc specified: %s", str );
return 0;
}
eid_mapping->rloc = rloc_find_by_address( reg, &addr, NULL );
if ( eid_mapping->rloc == NULL ) {
warn( "Unknown rloc: %s", str );
return 0;
}
// success!
reg->num_ip4_map_entries++;
} else if ( strcmp( "ipv6", str ) == 0 ) {
struct ip6_eid_map_entry *eid_mapping;
struct in6_addr addr;
if ( reg->num_ip6_map_entries >= MAX_EID_MAPPINGS ) {
warn( "Limit of %i IPv6 EID mappings reached!", MAX_EID_MAPPINGS );
return 0;
}
if ( mask > 128 ) {
warn( "Bad netmask specified for IPv6 (%i)", mask );
return 0;
}
eid_mapping = &reg->ip6_mappings[reg->num_ip6_map_entries];
eid_mapping->mask = (unsigned int) mask;
// parse our network address
entry = json_object_object_get( map, "network" );
if (!json_object_is_type( entry, json_type_string ) ) {
warn( "No network specified" );
return 0;
}
str = json_object_get_string( entry );
if ( inet_pton( AF_INET6, str, &eid_mapping->network ) != 1 ) {
warn( "Invalid network specified: %s", str );
return 0;
}
warn( "setting eid_mapping->network with %s", str );
memcpy( &eid_mapping->broadcast, &eid_mapping->network, sizeof( struct in6_addr ) );
// FIXME: calculate ipv6 broadcast here. ipv6 is broken until we do
warn( "IPv6 broadcast not calculated yet" );
// Get a pointer to the rloc these eids are mapped to
entry = json_object_object_get( map, "rloc" );
if ( !json_object_is_type( entry, json_type_string ) ) {
warn( "no rloc specified" );
return 0;
}
str = json_object_get_string( entry );
if ( inet_pton( AF_INET6, str, &addr ) != 1 ) {
warn( "Invalid rloc specified: %s", str );
return 0;
}
eid_mapping->rloc = rloc_find_by_address( reg, NULL, &addr );
if ( eid_mapping->rloc == NULL ) {
warn( "Unknown rloc: %s", str );
return 0;
}
// success!
reg->num_ip6_map_entries++;
} else {
warn( "Unknown family specified: %s", str );
return 0;
}
return 1;
}
struct rlocs* rlocs_new( char* filename )
{
struct rlocs* result;
struct stat filedata;
json_object* config;
ssize_t have_read = 0;
char* json_text;
int fd;
fd = open( filename, O_RDONLY );
if ( errno < 0 ) {
warn( "Error %s (%i) opening %s", strerror(errno), errno, filename );
return NULL;
}
fstat( fd, &filedata );
if ( errno < 0 ) {
warn( "Error %s (%i) getting size of %s", strerror(errno), errno, filename );
return NULL;
}
/* Make sure we're null-terminated */
json_text = xmalloc( filedata.st_size + 1 );
while ( have_read < filedata.st_size ) {
ssize_t bytes = read( fd, json_text + have_read, filedata.st_size - have_read );
if ( bytes < 0 ) {
warn( "Error %s (%i) reading %s", strerror(errno), errno, filename ) ;
free( json_text );
close( fd );
return NULL;
}
have_read += bytes;
//EOF
if ( bytes == 0 ) {
warn( "short read of %s: %zu instead of %zu", filename, have_read, filedata.st_size );
filedata.st_size = have_read;
}
}
//info( "Current config:\n %s", json_text );
close( fd );
config = json_tokener_parse( json_text );
free( json_text );
if ( !json_object_is_type( config, json_type_object ) ) {
warn( "Config does not parse to a JSON object" );
return NULL;
}
json_object* type_field = json_object_object_get( config, "type" );
const char* type_data = json_object_get_string( type_field );
if ( strcmp( JSON_TYPE, type_data ) != 0 ) {
warn( "Expected type to be %s, not %s", JSON_TYPE, type_data );
return NULL;
}
json_object * keys_field = json_object_object_get( config, "keys" );
if ( !json_object_is_type( keys_field, json_type_object ) ) {
warn( "keys field is not present in config" );
return NULL;
}
result = xmalloc( sizeof( struct rlocs ) );
result->config = config;
/* populate our array of rloc objects */
json_object_object_foreach( keys_field, key, val ) {
if ( !rlocs_rloc_from_json( result, key, val ) ) {
warn( "Failed to process rloc %s", key );
goto fail;
}
}
json_object * maps_field = json_object_object_get( config, "eid_rloc_map" );
if ( !json_object_is_type( maps_field, json_type_array ) ) {
warn( "Couldn't find eid_rloc_map field in config" );
goto fail;
}
int i, len = json_object_array_length( maps_field );
for ( i = 0; i < len ; i++ ) {
json_object *map_field = json_object_array_get_idx( maps_field, i );
if ( !rlocs_eid_from_json( result, map_field ) ) {
warn( "Failed to process eid map %i", i );
goto fail;
}
}
return result;
fail:
rlocs_free( result ); // deallocate any struct rloc we already created
return NULL;
}
// FIXME: these really do need to be as fast as possible. This is a 2-minute effort.
struct rloc *rloc_find_for_ipv4( struct rlocs *reg, struct in_addr *eid )
{
struct ip4_eid_map_entry *current;
uint32_t host_eid = ntohl( eid->s_addr );
int i;
for ( i = 0 ; i < reg->num_ip4_map_entries ; i++ ) {
current = &reg->ip4_mappings[i];
uint32_t host_start = ntohl( current->network.s_addr );
uint32_t host_end = ntohl( current->broadcast.s_addr );
if ( host_start <= host_eid && host_end >= host_eid ) {
break;
}
}
return i < reg->num_ip4_map_entries ? current->rloc : NULL;
}
struct rloc *rloc_find_for_ipv6( struct rlocs *reg, struct in6_addr *eid )
{
warn( "Searching for IPv6 RLOCs not done yet" );
return NULL;
}
struct rloc *rloc_find_by_address( struct rlocs *reg, struct in_addr *ipv4, struct in6_addr *ipv6 )
{
struct rloc *current;
int i, cmp;
for( i = 0 ; i < reg->num_entries; i++ ) {
current = &reg->entries[i];
cmp = 1;
switch( current->family ) {
case AF_INET:
if ( ipv4 != NULL ) {
cmp = memcmp( &current->addr, ipv4, sizeof( struct in_addr ) );
}
break;
case AF_INET6:
if ( ipv6 != NULL ) {
cmp = memcmp( &current->addr, ipv6, sizeof( struct in6_addr ) );
}
break;
}
if ( cmp == 0 ) {
break;
}
}
return i < reg->num_entries ? current : NULL;
}
ssize_t rloc_encrypt( struct rloc *rloc, unsigned char *data, size_t data_len, unsigned char *dest, size_t dest_len )
{
return RSA_public_encrypt( data_len, data, dest, rloc->key, RSA_PKCS1_OAEP_PADDING );
}
void rlocs_free( struct rlocs* registry )
{
int i;
for( i = 0; i < registry->num_entries; i++ ) {
if ( registry->entries[i].key != NULL ) {
RSA_free( registry->entries[i].key );
}
}
// No need to do json_object_put() here.
free( registry );
return;
}

@ -0,0 +1,65 @@
#ifndef _RLOCS_H_
#define _RLOCS_H_
#include <json/json_object.h>
#include <netinet/in.h>
#include <openssl/rsa.h>
// For now. We can dynamically allocate later.
#define MAX_RLOCS 64
#define MAX_EID_MAPPINGS 256
struct rloc {
short family;
union {
struct in_addr ip4;
struct in6_addr ip6;
} addr;
RSA* key;
};
struct ip4_eid_map_entry {
struct in_addr network;
struct in_addr broadcast;
unsigned int mask;
struct rloc *rloc;
};
struct ip6_eid_map_entry {
struct in6_addr network;
struct in6_addr broadcast;
unsigned int mask;
struct rloc *rloc;
};
struct rlocs {
json_object* config;
size_t num_entries;
struct rloc entries[MAX_RLOCS];
size_t num_ip4_map_entries;
struct ip4_eid_map_entry ip4_mappings[MAX_EID_MAPPINGS];
size_t num_ip6_map_entries;
struct ip6_eid_map_entry ip6_mappings[MAX_EID_MAPPINGS];
};
void rlocs_init(void);
struct rlocs *rlocs_new( char *filename );
struct rloc *rloc_find_for_ipv4( struct rlocs *reg, struct in_addr *eid );
struct rloc *rloc_find_for_ipv6( struct rlocs *reg, struct in6_addr *eid );
struct rloc *rloc_find_by_address( struct rlocs *reg, struct in_addr *ipv4, struct in6_addr *ipv6 );
/* Returns -1 on error, or number of bytes written */
ssize_t rloc_encrypt( struct rloc *rloc, unsigned char *data, size_t data_len, unsigned char *dest, size_t dest_len );
void rlocs_free( struct rlocs *registry );
#endif

@ -0,0 +1,123 @@
#include "util.h"
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
void* xmalloc( size_t bytes )
{
void* result = malloc( bytes );
if ( bytes > 0 && result == NULL ) {
warn( "Couldn't allocate memory, exiting!" );
exit(2);
}
memset( result, 0, bytes );
return result;
}
int create_tun( const char* name )
{
int fd, err;
struct ifreq ifr;
if ( ( fd = open( "/dev/net/tun", O_RDWR ) ) < 0 ) {
warn( "Error %s opening tun to create %s", strerror(errno), name );
return -1;
}
memset( &ifr, 0, sizeof( struct ifreq ) );
ifr.ifr_flags = IFF_TUN | IFF_NO_PI | IFF_UP;
strncpy( ifr.ifr_name, name, IFNAMSIZ );
if ( (err = ioctl( fd, TUNSETIFF, (void*) &ifr ) ) < 0 ) {
warn( "Error creating tun device %s: %s", name, strerror(errno) );
close( fd );
return -1;
}
return fd;
}
// shamelessly copied from:
// http://www.roman10.net/how-to-calculate-iptcpudp-checksumpart-2-implementation/
unsigned short compute_checksum(unsigned short *addr, unsigned int count) {
unsigned long sum = 0;
while (count > 1) {
sum += * addr++;
count -= 2;
}
//if any bytes left, pad the bytes and add
if(count > 0) {
sum += ((*addr)&htons(0xFF00));
}
//Fold sum to 16 bits: add carrier to result
while (sum>>16) {
sum = (sum & 0xffff) + (sum >> 16);
}
//one's complement
sum = ~sum;
return ((unsigned short)sum);
}
void compute_ip_checksum(struct iphdr* pkt)
{
pkt->check = 0x0000;
pkt->check = compute_checksum( (unsigned short*) pkt, pkt->ihl * 4 );
}
int link_set_up( char *link_name, int state )
{
int fd = socket( PF_INET, SOCK_DGRAM, IPPROTO_IP );
struct ifreq ifr;
memset( &ifr, 0, sizeof( struct ifreq ) );
strncpy( ifr.ifr_name, link_name, IFNAMSIZ );
if ( ioctl( fd, SIOCGIFFLAGS, (void*)&ifr ) < 0 ) {
warn( "Failed to read interface flags for %s: %s", link_name, strerror(errno) );
return 0;
}
if ( state ) {
ifr.ifr_flags |= IFF_UP;
} else {
ifr.ifr_flags = ( ifr.ifr_flags & ~IFF_UP );
}
if ( ioctl( fd, SIOCSIFFLAGS, (void*)&ifr ) < 0 ) {
warn( "Failed to set link state for interface %s to %i: %s", link_name, state, strerror(errno) );
return 0;
}
// ioctl(fd, SIOCGIFFLAGS, {ifr_name="wrapper", ifr_flags=IFF_POINTOPOINT|IFF_NOARP|IFF_MULTICAST}) = 0
// ioctl(fd, SIOCSIFFLAGS, {ifr_name="wrapper", ifr_flags=IFF_UP|IFF_POINTOPOINT|IFF_RUNNING|IFF_NOARP|IFF_MULTICAST}) = 0
return 1;
}

@ -0,0 +1,22 @@
#ifndef _UTIL_H_
#define _UTIL_H
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <netinet/ip.h>
#define info(msg, ...) { fprintf( stdout, msg, ##__VA_ARGS__ ) ; fprintf( stdout, "\n" ); }
#define warn(msg, ...) { fprintf( stderr, msg, ##__VA_ARGS__ ) ; fprintf( stderr, "\n" ); }
#define IPPROTO_HIDE_EID 0xfd
void* xmalloc( size_t bytes );
int create_tun( const char* name );
void compute_ip_checksum( struct iphdr* pkt );
int link_set_up( char *link_name, int state );
#endif

@ -1,124 +1,168 @@
// Will become util.h or so
#include "util.h"
#include "rlocs.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define info(msg, ...) { fprintf( stdout, msg, ##__VA_ARGS__ ) ; fprintf( stdout, "\n" ); }
#define warn(msg, ...) { fprintf( stderr, msg, ##__VA_ARGS__ ) ; fprintf( stderr, "\n" ); }
void* xmalloc( size_t bytes )
{
void* result = malloc( bytes );
if ( bytes > 0 && result == NULL ) {
warn( "Couldn't allocate memory, exiting!" );
exit(2);
}
memset( result, 0, bytes );
return result;
}
// We use a TUN device right now so we don't have to care about layer 2 headers
// or complicated, hard scaling stuff. This isn't likely to scale very well.
// dependencies for reading the rloc registry; will become rlocs.h/c or so
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <unistd.h>
#include <json/json_object.h>
#include <json/json_tokener.h>
#include <errno.h>
#include <fcntl.h>
// For now
typedef struct rloc_registry {
json_object* config;
} rloc_registry;
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
// We use writev() to send the packet, so we don't have to copy the
// unencrypted part.
#include <sys/uio.h>
rloc_registry* rlocs_get( char* filename )
typedef struct wrapper {
struct rlocs *rlocs;
int listen_if;
int output_if;
int same_if;
} wrapper;
struct recv_pkt {
union {
#ifdef __USE_BSD
struct ip ip;
#else
struct iphdr ip;
#endif
struct ip6_hdr ip6;
} hdr;
char payload[IP_MAXPACKET]; /* payload is this - header size, but OK */
};
// It's all our code that uses this. 12 is more than we probably need to
// construct a wrapped packet - just being careful.
//
// initial usage:
// 0 - wrapping ip header, including enc_size
// 1 - encrypted portion of payload, in scratch.
// 2 - unencrypted portion of payload, in recv_pkt
#define MAX_IOVS 12
struct rsp_data {
int count;
struct iovec iovs[MAX_IOVS];
unsigned char scratch[IP_MAXPACKET]; // somewhere easy to put results
};
int wrap_ipv4_packet(struct rlocs* reg, struct recv_pkt* pkt, struct rsp_data* out)
{
rloc_registry* result = NULL;
struct stat filedata;
json_object* config;
ssize_t have_read = 0;
char* json_text;
int fd;
fd = open( filename, O_RDONLY );
if ( errno < 0 ) {
warn( "Error %s (%i) opening %s", strerror(errno), errno, filename );
return NULL;
out->count = 3;
assert( out->count < MAX_IOVS );
unsigned char *scratch = &out->scratch[0];
// iovec 0: wrapping header
struct iphdr* wrap_hdr = (struct iphdr*) scratch;
unsigned int wrap_hdr_size = sizeof( struct iphdr );
scratch += wrap_hdr_size;
memset( wrap_hdr, 0, wrap_hdr_size );
wrap_hdr->version = 0x04;
wrap_hdr->ihl = wrap_hdr_size / 4;
wrap_hdr->ttl = IPDEFTTL;
wrap_hdr->protocol = IPPROTO_HIDE_EID;
out->iovs[0].iov_base = wrap_hdr;
out->iovs[0].iov_len = wrap_hdr_size;
// TODO: id, still needs filling now.
// We need to know source and destination rlocs to construct the packet
struct rloc* s_rloc;
struct rloc* d_rloc;
struct in_addr tmp;
// TODO: check endianness of saddr/daddr
tmp.s_addr = pkt->hdr.ip.saddr;
if ( ( s_rloc = rloc_find_for_ipv4( reg, &tmp ) ) == NULL ) {
warn( "Couldn't find source rloc, dropping packet" );
// TODO: fallback behaviour here?
return 0;
}
fstat( fd, &filedata );
if ( errno < 0 ) {
warn( "Error %s (%i) getting size of %s", strerror(errno), errno, filename );
return NULL;
tmp.s_addr = pkt->hdr.ip.daddr;
if ( ( d_rloc = rloc_find_for_ipv4( reg, &tmp ) ) == NULL ) {
warn( "Couldn't find destination rloc, dropping packet" );
// TODO: fallback behaviour here?
return 0;
}
/* Make sure we're null-terminated */
json_text = xmalloc( filedata.st_size + 1 );
wrap_hdr->saddr = s_rloc->addr.ip4.s_addr;
wrap_hdr->daddr = d_rloc->addr.ip4.s_addr;
// iovec 1: encrypted part.
// FIXME: Need to inspect the protocol field and gobble up the TCP/UDP/etc
// header as well, for decent anonymity. TCP/UDP ports are an obvious way
// to perform a correlation attack.
// RSA pubkey encryption with 4096-bit keys gobbles up at least 512 bytes
// of space, so we make sure to use it.
ssize_t enc_size;
size_t orig_data_size = ntohs( pkt->hdr.ip.tot_len );
size_t bytes_to_encrypt;
while ( have_read < filedata.st_size ) {
ssize_t bytes = read( fd, json_text + have_read, filedata.st_size - have_read );
if ( bytes < 0 ) {
warn( "Error %s (%i) reading %s", strerror(errno), errno, filename ) ;
free( json_text );
close( fd );
return NULL;
}
if ( orig_data_size > 512 ) {
bytes_to_encrypt = pkt->hdr.ip.ihl * 4;
} else {
bytes_to_encrypt = orig_data_size;
}
have_read += bytes;
off_t enc_max_len = IP_MAXPACKET - wrap_hdr_size - orig_data_size - bytes_to_encrypt;
// We use two bytes to store the size of the encrypted blob
unsigned short *pkt_enc_size = (unsigned short *) scratch;
scratch += 2;
//EOF
if ( bytes == 0 ) {
warn( "short read of %s: %zu instead of %zu", filename, have_read, filedata.st_size );
filedata.st_size = have_read;
}
enc_size = rloc_encrypt( d_rloc, (unsigned char *)&pkt->hdr, bytes_to_encrypt, scratch, enc_max_len - 2 );
if ( enc_size < 0 ) {
warn( "failed to encrypt, dropping packet" );
return 0;
}
warn( json_text );
close( fd );
config = json_tokener_parse( json_text );
free( json_text );
// TODO: Check for config-file errors here, ja?
result = xmalloc( sizeof( struct rloc_registry ) );
result->config = config;
*pkt_enc_size = htons( enc_size );
enc_size += 2;
scratch = (unsigned char*) pkt_enc_size;
// TODO: process the config into an easy-to-look-up format
warn( "Encrypted size: 2 + %zu", enc_size - 2);
out->iovs[1].iov_base = scratch;
out->iovs[1].iov_len = enc_size;
scratch += enc_size;
// iovec 2: unencrypted remains
if ( bytes_to_encrypt == orig_data_size ) {
out->count = 2;
out->iovs[2].iov_base = NULL;
out->iovs[2].iov_len = 0;
} else {
out->iovs[2].iov_base = (char *) pkt + bytes_to_encrypt;
out->iovs[2].iov_len = ntohs( pkt->hdr.ip.tot_len ) - bytes_to_encrypt;
}
wrap_hdr->tot_len = htons( wrap_hdr_size + enc_size + out->iovs[2].iov_len );
compute_ip_checksum( wrap_hdr );
info( "Finished building return packet" );
return result;
return 1;
}
void rlocs_free(rloc_registry* rlocs )
int wrap_ipv6_packet(struct rlocs *reg, struct recv_pkt* pkt, struct rsp_data* out)
{
// No need to do json_object_put() here.
free( rlocs );
return;
}
#include <pcap.h>
struct ip6_hdr wrap_hdr;
memset( &wrap_hdr, 0, sizeof( struct iphdr ) );
typedef struct wrapper {
rloc_registry* rlocs;
pcap_t* listen_if;
pcap_t* output_if;
char** us_rlocs;
} wrapper;
void wrapper_handle_packet( u_char *user, const struct pcap_pkthdr *h, const u_char *bytes)
{
//wrapper *wrap = ( wrapper* ) user;
info( "Received packet \\o/" );
return;
return -1;
}
@ -128,61 +172,138 @@ void wrapper_handle_packet( u_char *user, const struct pcap_pkthdr *h, const u_c
*/
int main(int argc, char** argv)
{
char errbuf[PCAP_ERRBUF_SIZE];
int result;
wrapper wrap;
if ( argc < 5 ) {
warn( "Usage: %s <rloc database> <listen_ifname> <output_ifname> <us-rlocs>+", argv[0] );
char addr1[128], addr2[128];
int i;
if ( argc < 4 ) {
warn( "Usage: %s <rloc database> <listen_ifname> <output_ifname>", argv[0] );
return 1;
}
memset( &wrap, 0, sizeof( wrapper ) );
rlocs_init();
wrap.rlocs = rlocs_get( argv[1] );
wrap.rlocs = rlocs_new( argv[1] );
if ( wrap.rlocs == NULL ) {
warn( "Failed to get config from %s", argv[1] );
return 1;
}
wrap.listen_if = pcap_create( argv[2], errbuf );
if ( wrap.listen_if == NULL ) {
warn( "Error opening %s for listening: %s", argv[2], errbuf );
rlocs_free( wrap.rlocs );
return 1;
info( "Configuration has parsed as:" );
for( i = 0 ; i < wrap.rlocs->num_entries; i++ ) {
struct rloc *current = &wrap.rlocs->entries[i];
inet_ntop( current->family, &current->addr, &addr1[0], 128 );
info( "RLOC %i: family %i, address %s", i, current->family, &addr1[0] );
}
wrap.output_if = pcap_create( argv[3], errbuf );
if ( wrap.output_if == NULL ) {
warn( "Error opening %s for forwarding: %s", argv[3], errbuf );
pcap_close( wrap.listen_if );
for ( i = 0 ; i < wrap.rlocs->num_ip4_map_entries ; i++ ) {
struct ip4_eid_map_entry *current = &wrap.rlocs->ip4_mappings[i];
inet_ntop( AF_INET, &current->network, &addr1[0], 128 );
inet_ntop( AF_INET, &current->rloc->addr, &addr2[0], 128 );
info( "IPv4 EID mapping %i: %s/%u => %s", i, addr1, current->mask, addr2 );
}
for ( i = 0 ; i < wrap.rlocs->num_ip6_map_entries ; i++ ) {
struct ip6_eid_map_entry *current = &wrap.rlocs->ip6_mappings[i];
inet_ntop( AF_INET6, &current->network, &addr1[0], 128 );
inet_ntop( AF_INET6, &current->rloc->addr, &addr2[0], 128 );
info( "IPv6 EID mapping %i: %s/%u => %s", i, addr1, current->mask, addr2 );
}
// TODO: We can scale the tun architecture by using multiqueue and having
// a bunch of workers, rather than this noddy scheme. If we don't jump
// directly to something saner, anyway...
wrap.listen_if = create_tun( argv[2] );
if ( wrap.listen_if == -1 ) {
warn( "Error opening %s for listening", argv[2] );
rlocs_free( wrap.rlocs );
return 1;
}
link_set_up( argv[3], 1 );
pcap_set_snaplen( wrap.listen_if, 65535 );
pcap_set_promisc( wrap.listen_if, 1 );
pcap_set_timeout( wrap.listen_if, 5 ); /* TODO: needs tuning */
/* TODO: Make buffer size tuneable */
if ( strcmp( argv[2], argv[3] ) == 0 ) {
wrap.same_if = 1;
wrap.output_if = wrap.listen_if;
} else {
wrap.same_if = 0;
wrap.output_if = create_tun( argv[3] );
if ( wrap.output_if == -1 ) {
warn( "Error opening %s for outputting", argv[3] );
rlocs_free( wrap.rlocs );
close( wrap.listen_if );
return 1;
}
link_set_up( argv[3], 1 );
}
warn( "TODO: Write BGP interventions to file" );
info( "Entering main loop" );
result = pcap_activate( wrap.listen_if );
if ( result != 0 ) {
warn( "Failed to activate %s for listening", argv[2] );
pcap_perror( wrap.listen_if, "pcap error was" );
goto done;
}
info( "Processing packets" );
struct recv_pkt recv_pkt;
struct rsp_data to_send;
ssize_t count;
int result;
memset( &recv_pkt, 0, sizeof( struct recv_pkt ) );
memset( &to_send, 0, sizeof( struct rsp_data ) );
pcap_loop( wrap.listen_if, -1, wrapper_handle_packet, (u_char*) &wrap );
while(1) {
// TODO: this isn't zero-copy. Not even close
if ( ( count = read( wrap.listen_if, &recv_pkt, sizeof( struct recv_pkt ) ) ) < 0 ) {
warn( "Failed to get a packet (%s)", strerror( errno ) );
break;
}
info( "Got a packet \\o/. %zu bytes", count );
switch( recv_pkt.hdr.ip.version ) {
case 0x04 :
result = wrap_ipv4_packet( wrap.rlocs, &recv_pkt, &to_send );
break;
case 0x06 :
result = wrap_ipv6_packet( wrap.rlocs, &recv_pkt, &to_send );
break;
default:
warn( "Unknown IP version: %i", recv_pkt.hdr.ip.version );
}
// We can't send the unwrapped one - it'll just be returned to us
// forever, given our bgp interventions, unless the router is clever.
// TODO: make fallback-to-unwrapped a configurable option?
if ( !result ) {
warn( "Failed to construct a wrapped version of received packet, dropping." );
continue;
}
// no failure, but nothing to forward.
if ( to_send.count == 0 ) {
continue;
}
// TODO: Drop the packet if we would fragment. A real implementation
// will need to fragment or inform the source, of course.
// docs say this should never block and should always write everything -
// trust that for now.
if ( ( count = writev( wrap.output_if, to_send.iovs, to_send.count ) ) < 0 ) {
warn( "Error writing wrapped packet to output: %s", strerror(errno) );
}
}
info( "Finished, cleaning up" );
done:
rlocs_free( wrap.rlocs );
pcap_close( wrap.listen_if );
pcap_close( wrap.output_if );
close( wrap.listen_if );
if ( !wrap.same_if ) {
close( wrap.output_if );
}
return 0;
}
Loading…
Cancel
Save