diff --git a/.gitignore b/.gitignore index d0a8e07..04e72fc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.o pass-1/wrapper pass-1/unwrapper +pass-1/hide-eid pass-1/example-keys diff --git a/pass-1/Makefile b/pass-1/Makefile index dd781f5..45b18c4 100644 --- a/pass-1/Makefile +++ b/pass-1/Makefile @@ -5,13 +5,15 @@ LDFLAGS := $(LDFLAGS) -ljson-c -lssl -lcrypto .PHONY: clean -all: wrapper unwrapper +all: wrapper unwrapper hide-eid rlocs.o: util.o packet.o: util.o wrapper: util.o rlocs.o packet.o +unwrapper: util.o rlocs.o packet.o +hide-eid: util.o rlocs.o packet.o clean: - rm -f wrapper unwrapper *.o + rm -f hide-eid wrapper unwrapper *.o diff --git a/pass-1/hide-eid.c b/pass-1/hide-eid.c new file mode 100644 index 0000000..57e5e4a --- /dev/null +++ b/pass-1/hide-eid.c @@ -0,0 +1,82 @@ +#include "util.h" +#include "rlocs.h" +#include "packet.h" + +#include +#include + +#include + +/* + * Entry point. Expects an invocation like: + * hide-eid + * + * hide-eid wraps packets that come to it unwrapped, and unwraps packets that + * come to it wrapped. This makes it handy for all-in-one boxes + */ +int main(int argc, char** argv) +{ + struct session session; + struct recv_pkt recv_pkt; + struct rsp_data to_send; + ssize_t count; + int result = 0; + + if ( argc < 4 ) { + warn( "Usage: %s ", argv[0] ); + return 1; + } + + rlocs_init(); + if ( !session_setup( &session, argv[1], argv[2], argv[3] ) ) { + warn( "Failed to set up session, exiting" ); + return 1; + } + + memset( &recv_pkt, 0, sizeof( struct recv_pkt ) ); + memset( &to_send, 0, sizeof( struct rsp_data ) ); + + warn( "TODO: Write BGP interventions to file" ); + + info( "Processing packets" ); + while(1) { + if ( ( count = read( session.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 ); + } + warn( "TODO: select wrap / unwrap functionality based on destination IP" ); + + if ( !result ) { + warn( "Failed to process received packet, dropping." ); + continue; + } + + // no failure, but nothing to forward. + if ( to_send.count == 0 ) { + continue; + } + + if ( ( count = writev( session.output_if, to_send.iovs, to_send.count ) ) < 0 ) { + warn( "Error writing processed packet to output: %s", strerror(errno) ); + } + + } + + info( "Finished, cleaning up" ); + session_teardown( &session ); + + return 0; +} \ No newline at end of file diff --git a/pass-1/packet.c b/pass-1/packet.c index fae2e40..57bcf8a 100644 --- a/pass-1/packet.c +++ b/pass-1/packet.c @@ -1,3 +1,8 @@ +#include +#include + +#include "util.h" +#include "rlocs.h" #include "packet.h" // shamelessly copied from: @@ -33,3 +38,130 @@ void compute_ip_checksum(struct iphdr* pkt) pkt->check = 0x0000; pkt->check = compute_checksum( (unsigned short*) pkt, pkt->ihl * 4 ); } + + +int wrap_ipv4_packet(struct rlocs* reg, struct recv_pkt* pkt, struct rsp_data* out) +{ + 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; + } + + 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; + } + + 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; + + if ( orig_data_size > 512 ) { + bytes_to_encrypt = pkt->hdr.ip.ihl * 4; + } else { + bytes_to_encrypt = orig_data_size; + } + + 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; + + 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; + } + + *pkt_enc_size = htons( enc_size ); + enc_size += 2; + scratch = (unsigned char*) pkt_enc_size; + + + + 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 1; +} + +int wrap_ipv6_packet(struct rlocs *reg, struct recv_pkt* pkt, struct rsp_data* out) +{ + warn( "STUB: wrap_ipv6_packet" ); + return 0; +} + + + +int unwrap_ipv4_packet(struct rlocs* reg, struct recv_pkt* pkt, struct rsp_data* out) +{ + warn( "STUB: unwrap_ipv4_packet" ); + return 0; +} + +int unwrap_ipv6_packet(struct rlocs *reg, struct recv_pkt* pkt, struct rsp_data* out) +{ + warn( "STUB: unwrap_ipv6_packet" ); + + return 0; +} diff --git a/pass-1/packet.h b/pass-1/packet.h index 2eb8cbd..824594b 100644 --- a/pass-1/packet.h +++ b/pass-1/packet.h @@ -5,6 +5,9 @@ #include #include +#define IPPROTO_HIDE_EID 0xfd + + struct packet { union { #ifdef __USE_BSD @@ -37,4 +40,9 @@ struct rsp_data { void compute_ip_checksum( struct iphdr* pkt ); +int wrap_ipv4_packet(struct rlocs* reg, struct recv_pkt* pkt, struct rsp_data* out); +int wrap_ipv6_packet(struct rlocs* reg, struct recv_pkt* pkt, struct rsp_data* out); + +int unwrap_ipv4_packet(struct rlocs* reg, struct recv_pkt* pkt, struct rsp_data* out); +int unwrap_ipv6_packet(struct rlocs* reg, struct recv_pkt* pkt, struct rsp_data* out); #endif \ No newline at end of file diff --git a/pass-1/unwrapper.c b/pass-1/unwrapper.c index 79bb97a..23ab90c 100644 --- a/pass-1/unwrapper.c +++ b/pass-1/unwrapper.c @@ -1,7 +1,85 @@ +#include "util.h" +#include "rlocs.h" +#include +#include +#include "packet.h" + +#include + +/* + * Entry point. Expects an invocation like: + * unwrapper + */ int main(int argc, char** argv) { + struct session unwrap; + struct recv_pkt recv_pkt; + struct rsp_data to_send; + ssize_t count; + int result; + if ( argc < 4 ) { + warn( "Usage: %s ", argv[0] ); + return 1; + } + + rlocs_init(); + if ( !session_setup( &unwrap, argv[1], argv[2], argv[3] ) ) { + warn( "Failed to set up session, exiting" ); + return 1; + } + + memset( &recv_pkt, 0, sizeof( struct recv_pkt ) ); + memset( &to_send, 0, sizeof( struct rsp_data ) ); + + warn( "TODO: Write BGP interventions to file" ); + + info( "Processing packets" ); + while(1) { + // TODO: this isn't zero-copy. Not even close + if ( ( count = read( unwrap.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 = unwrap_ipv4_packet( unwrap.rlocs, &recv_pkt, &to_send ); + break; + case 0x06 : + result = unwrap_ipv6_packet( unwrap.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 unwrap received packet, dropping." ); + continue; + } + + // no failure, but nothing to forward. + if ( to_send.count == 0 ) { + continue; + } + + // docs say this should never block and should always write everything - + // trust that for now. + if ( ( count = writev( unwrap.output_if, to_send.iovs, to_send.count ) ) < 0 ) { + warn( "Error writing unwrapped packet to output: %s", strerror(errno) ); + } + + } + + info( "Finished, cleaning up" ); + session_teardown( &unwrap ); + return 0; } \ No newline at end of file diff --git a/pass-1/util.c b/pass-1/util.c index af51f56..6a0d108 100644 --- a/pass-1/util.c +++ b/pass-1/util.c @@ -1,4 +1,5 @@ #include "util.h" +#include "rlocs.h" #include #include @@ -79,4 +80,64 @@ int link_set_up( char *link_name, int state ) return 1; +} + + +int session_setup( struct session *session, char *config_file, char *listen_if, char *output_if ) +{ + memset( &session, 0, sizeof( struct session ) ); + session->listen_if = -1; + session->output_if = -1; + + session->rlocs = rlocs_new( config_file ); + + if ( session->rlocs == NULL ) { + warn( "Failed to get config from %s", config_file ); + return 0; + } + + rlocs_debug_output( session->rlocs ); + + // 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... + + session->listen_if = create_tun( listen_if ); + if ( session->listen_if == -1 ) { + warn( "Error opening %s for listening", listen_if ); + rlocs_free( session->rlocs ); + return 0; + } + link_set_up( listen_if, 1 ); + + + if ( strcmp( listen_if, output_if ) == 0 ) { + session->same_if = 1; + session->output_if = session->listen_if; + } else { + session->same_if = 0; + session->output_if = create_tun( output_if ); + + if ( session->output_if == -1 ) { + warn( "Error opening %s for outputting", output_if ); + rlocs_free( session->rlocs ); + close( session->listen_if ); + return 0; + } + link_set_up( output_if, 1 ); + } + + return 1; +} + +void session_teardown( struct session *session ) +{ + rlocs_free( session->rlocs ); + if ( session->listen_if >= 0 ) { + close( session->listen_if ); + } + + if ( session->output_if >= 0 && !session->same_if ) { + close( session->output_if ); + } } \ No newline at end of file diff --git a/pass-1/util.h b/pass-1/util.h index 1ca2ee7..4627e3d 100644 --- a/pass-1/util.h +++ b/pass-1/util.h @@ -5,16 +5,26 @@ #include #include -#include - #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 ); int link_set_up( char *link_name, int state ); +/* Our programs use this common struct to take advantage of common init code */ +struct session { + struct rlocs *rlocs; + int listen_if; + int output_if; + int same_if; +}; + +int session_setup( struct session *session, char *config_file, char *listen_if, char *output_if ); +void session_teardown( struct session *session ); + + + #endif \ No newline at end of file diff --git a/pass-1/wrapper.c b/pass-1/wrapper.c index 9949967..f3a1f22 100644 --- a/pass-1/wrapper.c +++ b/pass-1/wrapper.c @@ -1,7 +1,6 @@ #include "util.h" #include "rlocs.h" -#include #include #include @@ -11,192 +10,36 @@ // unencrypted part. #include -typedef struct wrapper { - struct rlocs *rlocs; - int listen_if; - int output_if; - int same_if; -} wrapper; - - -int wrap_ipv4_packet(struct rlocs* reg, struct recv_pkt* pkt, struct rsp_data* out) -{ - 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; - } - - 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; - } - - 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; - - if ( orig_data_size > 512 ) { - bytes_to_encrypt = pkt->hdr.ip.ihl * 4; - } else { - bytes_to_encrypt = orig_data_size; - } - - 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; - - 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; - } - - *pkt_enc_size = htons( enc_size ); - enc_size += 2; - scratch = (unsigned char*) pkt_enc_size; - - - - 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 1; -} - -int wrap_ipv6_packet(struct rlocs *reg, struct recv_pkt* pkt, struct rsp_data* out) -{ - struct ip6_hdr wrap_hdr; - memset( &wrap_hdr, 0, sizeof( struct iphdr ) ); - - return -1; -} - /* * Entry point. Expects an invocation like: - * wrapper + * wrapper */ int main(int argc, char** argv) { - wrapper wrap; + struct session wrap; + struct recv_pkt recv_pkt; + struct rsp_data to_send; + ssize_t count; + int result; if ( argc < 4 ) { warn( "Usage: %s ", argv[0] ); return 1; } - memset( &wrap, 0, sizeof( wrapper ) ); - rlocs_init(); - - wrap.rlocs = rlocs_new( argv[1] ); - - if ( wrap.rlocs == NULL ) { - warn( "Failed to get config from %s", argv[1] ); + if ( !session_setup( &wrap, argv[1], argv[2], argv[3] ) ) { + warn( "Failed to set up session, exiting" ); return 1; } - rlocs_debug_output( wrap.rlocs ); - - // 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 ); - - - 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( "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 ) ); + warn( "TODO: Write BGP interventions to file" ); + + info( "Processing packets" ); while(1) { // TODO: this isn't zero-copy. Not even close if ( ( count = read( wrap.listen_if, &recv_pkt, sizeof( struct recv_pkt ) ) ) < 0 ) { @@ -242,13 +85,7 @@ int main(int argc, char** argv) } info( "Finished, cleaning up" ); - - - rlocs_free( wrap.rlocs ); - close( wrap.listen_if ); - if ( !wrap.same_if ) { - close( wrap.output_if ); - } + session_teardown( &wrap ); return 0; } \ No newline at end of file