First pass at fragmenting

main
Nick Thomas 2013-08-09 03:11:15 +01:00
parent 1acaa03799
commit cfd1b2f957
11 changed files with 405 additions and 359 deletions

2
.gitignore vendored
View File

@ -1,6 +1,4 @@
*.o
pass-1/wrapper
pass-1/unwrapper
pass-1/hide-eid
pass-1/example-keys
README.html

View File

@ -4,15 +4,13 @@ LDFLAGS := $(LDFLAGS) -ljson-c -lssl -lcrypto
.PHONY: clean
all: wrapper unwrapper hide-eid
all: 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 hide-eid wrapper unwrapper *.o
rm -f hide-eid *.o

View File

@ -14,31 +14,34 @@ each RLOC. Both wrapper and unwrapper need a copy of this file to work. We also
include a tiny library for reading it.
wrapper
=======
This component only wraps packets. It reads the contents of the rloc-registry,
outputs a bgpfeeder file that will redirect eid ranges to it, if honoured, opens
a tun device, and waits for packets to be sent to it. Upon receiving a packet,
it encrypts the start - including IP, TCP, UDP, etc headers, prepends a new IP
header that will route it to the destination RLOC, and outputs the new packet.
hide-eid
========
This program acts as an xTR (wraps and unwraps packets). It reads the
contents of the rloc-registry, outputs a bgpfeeder file that will redirect eid
ranges to it, if honoured, opens a tun device, and waits for packets to be sent
to it.
Upon receiving a packet, it encrypts the start - including IP, TCP, UDP, etc
headers - prepends a new IP header that will route it to the destination RLOC,
and outputs the new packet.
If it doesn't recognise an EID, or can't encrypt the packet, it is dropped.
unwrapper
=========
The unwrapper is in operation to the wrapper, but is also given a list of RLOCs
that is has private keys for. When a packet comes in, it tries to decrypt the
encrypted portion, reassemble the original packet and forward it on. If it
doesn't have the private key, or other problems arise, the packet is dropped.
When it receives an already-wrapped packet, it tries to decrypt and reassemble it,
and forward it on. It needs the private key for that, and if it doesn't have it,
or other problems occur, the packet is dropped.
We also handle ICMP and fragmentation, in case the packet we receive is too big,
according to the schemes in RFC6380. We keep track of the discovered path MTU for
each of the other xTRs by parsing incoming ICMP messages.
For wrapped packets that are too large to fit down the discovered path, if the DF
bit is set in the unwrapped packet, we return an ICMP packet informing the source
of this. Otherwise, we fragment the nuwrapped packet according to standard IP
rules, encrypt the two IP packets that result, and let the destination EID be
responsible for reassembling it. This removes a responsibility from hide-eid
at the destination, which won't have to keep track of encrypted fragments.
hide-eid
========
Most people will be more interested in hide-eid, which operates as a combined
wrapper and unwrapper. Like those programs, it opens a tun device, but it can
recognise whether a packet forwarded to it has been wrapped or not, and perform
the opposite operation before forwarding the packet on. This makes it useful
if you want to both receive and send traffic with a minimum of fuss - which is
most people, I guess.
bgpfeeder
=========
@ -68,11 +71,10 @@ number as the wrapping IP header's payload length. It then writes the length of
the encrypted portion of the total payload as the first 2 bytes of the payload,
followed by the constructed blob.
The unwrapper receives the packet, reads the IP header, then reads the next two
bytes. It takes the first n bytes of the remainder of the payload and decrypts
it, then prepends that to the remaining bytes. What it ends up with should be a
valid IP packet. It then alters the packet's TTL according to the wrapping
packet's TTL field, and forwards it for further routing.
The constructed blob currently consists of 16 bytes of PRNG data used as the IV,
the encrypted data (using the symmetric aes256gcm authenticating cipher) and 16
bytes of generated tag data.

View File

@ -7,6 +7,81 @@
#include <sys/uio.h>
int process_icmp_rloc_update( struct rlocs *reg, struct packet *packet )
{
uint16_t hdr_len = packet->hdr.ip.ihl * 4;
uint16_t inner_ip_hdr_offset = hdr_len + sizeof( struct icmphdr );
if ( ntohs( packet->hdr.ip.tot_len ) < inner_ip_hdr_offset + sizeof( struct iphdr ) ) {
debug( "Truncated ICMP packet is unidentifiable" );
return 0;
}
struct rloc *s_rloc;
struct rloc *d_rloc;
struct icmphdr *icmp = (struct icmphdr *) packet->payload + ( packet->hdr.ip.ihl * 4 );
if ( icmp->type != ICMP_DEST_UNREACH && icmp->code != ICMP_FRAG_NEEDED ) {
return 0; // It may be going elsewhere
}
// Be careful with this - some of payload may be past allocated memory
struct packet *inner_ip = (struct packet *) ((char *) packet + inner_ip_hdr_offset );
// Not much we can do with this case right now
if ( inner_ip->hdr.ip.protocol != IPPROTO_HIDE_EID ) {
debug( "ICMP Too Big response to an unwrapped packet. Peculiar." );
return -1;
}
if ( !rlocs_find_two_ipv4( reg, &s_rloc, (struct in_addr *)&inner_ip->hdr.ip.saddr, &d_rloc, (struct in_addr *)&inner_ip->hdr.ip.daddr ) ) {
return 0;
}
// All we're interested in is setting path mtu
uint16_t new_mtu = ntohs( icmp->un.frag.mtu );
rlocs_set_path_mtu( reg, s_rloc, d_rloc, new_mtu );
debug( "Set MTU for %s <-> %s to %u", s_rloc->presentation, d_rloc->presentation, new_mtu );
return 1;
}
int process_ipv4_packet( struct rlocs *reg, struct packet *packet, struct rsp_data *frag1, struct rsp_data *frag2 )
{
int result, updated;
switch ( packet->hdr.ip.protocol ) {
case IPPROTO_HIDE_EID:
// doesn't need fragmenting, or if it does, tough
result = unwrap_ipv4_packet( reg, packet, frag1 );
break;
case IPPROTO_ICMP:
if ( ( updated = process_icmp_rloc_update( reg, packet ) ) != 0 ) {
result = 0;
break;
}
// intentional fallthrough for ICMP not addressed to us.
// -1 (error) and 1 ( dealt with ) don't need to respond.
default:
result = wrap_ipv4_packet( reg, packet, frag1, frag2 );
break;
}
return result;
}
int process_packet( struct rlocs *reg, struct packet *packet, struct rsp_data *frag1, struct rsp_data *frag2 )
{
if ( packet->hdr.ip.version == 0x04 ) {
return process_ipv4_packet( reg, packet, frag1, frag2 );
}
warn( "Can't process IP protocol version %i yet; dropping packet", packet->hdr.ip.version );
return 0;
}
/*
* Entry point. Expects an invocation like:
* hide-eid <filename of rloc database> <listen_ifname> <output_ifname>
@ -17,10 +92,11 @@
int main(int argc, char** argv)
{
struct session session;
struct recv_pkt recv_pkt;
struct rsp_data to_send;
struct packet recv_pkt;
struct rsp_data frag1;
struct rsp_data frag2;
ssize_t count;
int result = 0;
if ( argc < 4 ) {
warn( "Usage: %s <rloc database> <listen_ifname> <output_ifname> [ <rloc> <keyfile> ]n", argv[0] );
@ -41,55 +117,41 @@ int main(int argc, char** argv)
}
}
memset( &recv_pkt, 0, sizeof( struct recv_pkt ) );
memset( &to_send, 0, sizeof( struct rsp_data ) );
memset( &recv_pkt, 0, sizeof( struct packet ) );
memset( &frag1, 0, sizeof( struct rsp_data ) );
memset( &frag2, 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 ) {
info( "Listening for packets" );
while( 1 ) {
int rsp_count;
if ( ( count = read( session.listen_if, &recv_pkt, sizeof( struct packet ) ) ) < 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 :
if ( recv_pkt.hdr.ip.protocol == IPPROTO_HIDE_EID ) {
result = unwrap_ipv4_packet( session.rlocs, &recv_pkt, &to_send );
} else {
result = wrap_ipv4_packet( session.rlocs, &recv_pkt, &to_send );
}
break;
case 0x06 :
/* TODO: ip6.protocol doesn't exist. And we're not wrapping
* IPv6 packets yet anyway.
if ( recv_pkt.hdr.ip6.protocol == IPPROTO_HIDE_EID ) {
result = unwrap_ipv6_packet( wrap.rlocs, &recv_pkt, &to_send );
} else {
result = wrap_ipv6_packet( wrap.rlocs, &recv_pkt, &to_send );
}
*/
warn( "TODO: wrap/unwrap IPv6 packets" );
break;
default:
warn( "Unknown IP version: %i", recv_pkt.hdr.ip.version );
}
if ( !result ) {
warn( "Failed to process received packet, dropping." );
continue;
}
// no failure, but nothing to forward.
if ( to_send.count == 0 ) {
continue;
if ( count == 0 ) {
warn( "Got EOF" );
break;
}
if ( ( count = writev( session.output_if, to_send.iovs, to_send.count ) ) < 0 ) {
warn( "Error writing processed packet to output: %s", strerror(errno) );
if ( ( rsp_count = process_packet( session.rlocs, &recv_pkt, &frag1, &frag2 ) ) < 0 ) {
debug( "Error processing packet, dropping" );
}
if ( rsp_count > 0 ) {
if ( ( count = writev( session.output_if, frag1.iovs, frag1.count ) ) < 0 ) {
debug( "Error writing processed packet to output: %s", strerror(errno) );
}
}
if ( rsp_count == 2 ) {
if ( ( count = writev( session.output_if, frag2.iovs, frag2.count ) ) < 0 ) {
debug( "Error writing second processed packet to output: %s", strerror(errno) );
}
}
}

View File

@ -33,24 +33,34 @@ unsigned short compute_checksum(unsigned short *addr, unsigned int count) {
}
void compute_ip_checksum(struct iphdr* pkt)
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)
int build_wrapped_ipv4_packet(struct rlocs *reg, struct rloc * s_rloc, struct rloc *d_rloc, struct packet *pkt, struct rsp_data *out)
{
out->count = 3;
assert( out->count < MAX_IOVS );
struct iphdr *wrap_hdr = (struct iphdr *) out->scratch;
uint16_t wrap_hdr_size = sizeof( struct iphdr );
unsigned char *scratch = &out->scratch[0];
ssize_t enc_size;
size_t orig_data_size = ntohs( pkt->hdr.ip.tot_len );
size_t bytes_to_encrypt = orig_data_size > 512 ? 512 : orig_data_size;
// iovec 0: wrapping header
struct iphdr* wrap_hdr = (struct iphdr*) scratch;
unsigned int wrap_hdr_size = sizeof( struct iphdr );
scratch += wrap_hdr_size;
debug( "Wrapping an IPv4 packet" );
debug( "wrap_hdr_size: %u, orig_data_size: %zu, bytes_to_encrypt: %zu", wrap_hdr_size, orig_data_size, bytes_to_encrypt );
// Areas in scratch we'll be using later
// We use two bytes to store the size of the encrypted blob
uint16_t *pkt_enc_size = (uint16_t *) (out->scratch + wrap_hdr_size );
unsigned char * pkt_enc_data = out->scratch + wrap_hdr_size + 2;
// Keep track of the total size of the data in out as we go
uint16_t out_len = 0;
out->count = 0;
memset( wrap_hdr, 0, wrap_hdr_size );
@ -58,96 +68,171 @@ int wrap_ipv4_packet(struct rlocs* reg, struct recv_pkt* pkt, struct rsp_data* o
wrap_hdr->ihl = wrap_hdr_size / 4;
wrap_hdr->ttl = IPDEFTTL;
wrap_hdr->protocol = IPPROTO_HIDE_EID;
wrap_hdr->frag_off = htons( 0x4000 ); // DF bit set
wrap_hdr->frag_off = htons( IP_DF ); // DF bit set
wrap_hdr->saddr = s_rloc->addr.ip4.s_addr;
wrap_hdr->daddr = d_rloc->addr.ip4.s_addr;
// FIXME: Do we need to set an ID ?
// iovec 0: encapsulating IP header.
out->iovs[0].iov_base = wrap_hdr;
out->iovs[0].iov_len = wrap_hdr_size;
out_len += wrap_hdr_size;
out->count++;
// 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;
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 = 512; // No point wasting bytes on padding
} 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;
// Encrypt the first 512 or so bytes of the data. FIXME: introspect and
// calculate exactly how many bytes for TCP, UDP, etc. to do as little work
// as we can get away with, here. fragments > 0 don't need encrypting at all
enc_size = rlocs_encrypt(
reg, s_rloc, d_rloc,
(unsigned char *)&pkt->hdr, bytes_to_encrypt, scratch, enc_max_len - 2
(unsigned char *)&pkt->hdr, bytes_to_encrypt,
pkt_enc_data, IP_MAXPACKET - wrap_hdr_size - 2
);
if ( enc_size < 0 ) {
warn( "failed to encrypt, dropping packet" );
debug( "Failed to encrypt, dropping packet" );
return 0;
}
debug( "enc_size: %li", enc_size );
*pkt_enc_size = htons( enc_size );
enc_size += 2;
scratch = (unsigned char*) pkt_enc_size;
warn( "Encrypted size: 2 + %zu", enc_size - 2);
// iovec 1: encrypted portion of encpasulated packet
out->iovs[1].iov_base = pkt_enc_size;
out->iovs[1].iov_len = enc_size + 2;
out_len += enc_size + 2;
out->count++;
out->iovs[1].iov_base = scratch;
out->iovs[1].iov_len = enc_size;
scratch += enc_size;
debug( "iovs[0]: %p, %zu", out->iovs[0].iov_base, out->iovs[0].iov_len );
debug( "iovs[1]: %p, %zu", out->iovs[1].iov_base, out->iovs[1].iov_len );
// 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;
// iovec 2: unencrypted remains of encapsulated packet, if present
if ( bytes_to_encrypt < orig_data_size ) {
out->iovs[2].iov_base = ((char *) pkt) + bytes_to_encrypt;
out->iovs[2].iov_len = orig_data_size - bytes_to_encrypt;
out_len += orig_data_size - bytes_to_encrypt;
out->count++;
debug( "iovs[2]: %p, %zu", out->iovs[2].iov_base, out->iovs[2].iov_len );
}
wrap_hdr->tot_len = htons( wrap_hdr_size + enc_size + out->iovs[2].iov_len );
wrap_hdr->tot_len = htons( out_len );
compute_ip_checksum( wrap_hdr );
info( "Finished wrapping IPv4 packet" );
debug( "Finished wrapping IPv4 packet" );
return 1;
}
int wrap_ipv6_packet(struct rlocs *reg, struct recv_pkt* pkt, struct rsp_data* out)
void build_icmp_too_big( uint16_t max_mtu, struct in_addr *rloc_src, struct packet *pkt, struct rsp_data *out )
{
struct iphdr *ip = (struct iphdr*) out->scratch;
struct icmphdr *icmp = (struct icmphdr *) ( out->scratch + sizeof( struct iphdr ) );
uint16_t icmp_size = sizeof( struct icmphdr ) + ( pkt->hdr.ip.ihl * 4 ) + 8;
debug( "Building ICMP Too Big packet" );
memset( out->scratch, 0, sizeof( struct iphdr ) + sizeof( struct icmphdr ) );
ip->version = 4;
ip->ihl = sizeof( struct iphdr ) / 4;
ip->ttl = IPDEFTTL;
ip->protocol = IPPROTO_ICMP;
ip->saddr = rloc_src->s_addr;
ip->daddr = pkt->hdr.ip.saddr;
icmp->type = ICMP_DEST_UNREACH;
icmp->code = ICMP_FRAG_NEEDED;
icmp->un.frag.mtu = htons( max_mtu );
out->iovs[0].iov_base = out->scratch;
out->iovs[0].iov_len = sizeof( struct iphdr ) + icmp_size;
memcpy( ((char *)icmp) + sizeof( struct icmphdr ), pkt, icmp_size - sizeof( struct icmphdr ) );
//out->iovs[1].iov_base = pkt;
//out->iovs[1].iov_len = ( pkt->hdr.ip.ihl * 4 ) + 8;
ip->tot_len = htons( out->iovs[0].iov_len );
icmp->checksum = compute_checksum( (unsigned short *)icmp, icmp_size );
compute_ip_checksum( ip );
out->count = 1;
return;
}
int wrap_ipv4_packet( struct rlocs *reg, struct packet *pkt, struct rsp_data *frag1, struct rsp_data *frag2 )
{
struct rloc *s_rloc, *d_rloc;
if ( ( s_rloc = rloc_find_for_ipv4( reg, (struct in_addr *)&pkt->hdr.ip.saddr ) ) == NULL ) {
debug( "Couldn't find source RLOC for (TODO), dropping packet" );
return 0;
}
if ( ( d_rloc = rloc_find_for_ipv4( reg, (struct in_addr *)&pkt->hdr.ip.daddr ) ) == NULL ) {
debug( "Couldn't find destination RLOC for (TODO), dropping packet" );
return 0;
}
uint16_t max_size = rlocs_get_path_mtu( reg, s_rloc, d_rloc );
uint16_t pkt_tot_len = ntohs( pkt->hdr.ip.tot_len );
uint16_t pkt_hdr_len = pkt->hdr.ip.ihl * 4;
int num_packets = 1;
// fragmentation is needed.
if ( pkt_tot_len > max_size - WRAP_OVERHEAD ) {
debug( "Packet needs fragmenting" );
// DF bit set, so return ICMP Too Big
if ( ntohs( pkt->hdr.ip.frag_off ) & IP_DF ) {
build_icmp_too_big( max_size, &s_rloc->addr.ip4, pkt, frag1 );
return 1;
}
num_packets = 2;
// good enough, it's getting encrypted and only needs to be unique for
// a short period of time
uint16_t frag_id = (uint16_t) rand();
// Must be an 8-byte offset
uint16_t frag_off = ( pkt_tot_len - pkt_hdr_len ) / 2;
frag_off += frag_off%8;
uint16_t frag2_size = pkt_tot_len - pkt_hdr_len - frag_off;
if ( pkt_hdr_len > sizeof( struct iphdr ) ) {
warn( "FIXME: options specified with IP header are not handled correctly during fragmentation yet" );
}
// wrap_ipv4_packet only touches scratch upto IP_MAXPACKET. We allocate
// double that.
struct packet *pkt2 = (struct packet *) frag2->scratch + IP_MAXPACKET;
pkt->hdr.ip.tot_len = htons( pkt_hdr_len + frag_off );
pkt->hdr.ip.id = htons( frag_id );
pkt->hdr.ip.frag_off = htons( 0 | IP_MF );
memcpy( pkt2, pkt, pkt_hdr_len );
pkt2->hdr.ip.tot_len = htons( pkt_tot_len - frag_off );
pkt2->hdr.ip.frag_off = htons( frag_off / 8 );
memcpy( ((char *)pkt2) + pkt_hdr_len, ((char*)pkt)+pkt_hdr_len, frag2_size );
if ( !build_wrapped_ipv4_packet( reg, s_rloc, d_rloc, pkt2, frag2 ) ) {
debug( "Couldn't wrap packet 2 of 2 ");
return 0;
}
}
if ( !build_wrapped_ipv4_packet( reg, s_rloc, d_rloc, pkt, frag1 ) ) {
debug( "Couldn't wrap packet 1 of %i", num_packets );
return 0;
}
return num_packets;
}
int wrap_ipv6_packet(struct rlocs *reg, struct packet *pkt, struct rsp_data *out)
{
warn( "STUB: wrap_ipv6_packet" );
return 0;
@ -155,7 +240,7 @@ int wrap_ipv6_packet(struct rlocs *reg, struct recv_pkt* pkt, struct rsp_data* o
int unwrap_ipv4_packet(struct rlocs* reg, struct recv_pkt* pkt, struct rsp_data* out)
int unwrap_ipv4_packet(struct rlocs* reg, struct packet *pkt, struct rsp_data *out)
{
out->count = 2;
assert( out->count < MAX_IOVS );
@ -222,7 +307,7 @@ int unwrap_ipv4_packet(struct rlocs* reg, struct recv_pkt* pkt, struct rsp_data*
return 1;
}
int unwrap_ipv6_packet(struct rlocs *reg, struct recv_pkt* pkt, struct rsp_data* out)
int unwrap_ipv6_packet(struct rlocs *reg, struct packet *pkt, struct rsp_data *out)
{
warn( "STUB: unwrap_ipv6_packet" );

View File

@ -4,26 +4,29 @@
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/ip_icmp.h>
#define IPPROTO_HIDE_EID 99
// IP header + IV + tag + block cipher max overhead
#define WRAP_OVERHEAD 20 + 16 + 16 + 16 + 2
#ifndef IP_DF
#define IP_DF 0x4000 /* dont fragment flag */
#endif
#ifndef IP_MF
#define IP_MF 0x2000 /* more fragments flag */
#endif
struct packet {
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 */
char payload[IP_MAXPACKET]; /* payload can be this - header size, but OK */
};
// wrapper.c expects this name
#define recv_pkt packet
// It's all our code that uses this. 12 is much more than we need to
// construct a wrapped packet at the moment.
//
@ -35,14 +38,23 @@ struct packet {
struct rsp_data {
int count;
struct iovec iovs[MAX_IOVS];
unsigned char scratch[IP_MAXPACKET]; // somewhere easy to put results
unsigned char scratch[IP_MAXPACKET * 2]; // somewhere easy to put results
};
// If we need more fragments than this, I am a sad person
#define MAX_PACKET_FRAGMENTS 4
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 wrap_ipv4_packet( struct rlocs *reg, struct packet *pkt, struct rsp_data *frag1, struct rsp_data *frag2 );
int wrap_ipv6_packet(struct rlocs* reg, struct packet *pkt, struct rsp_data *out);
int unwrap_ipv4_packet(struct rlocs* reg, struct packet *pkt, struct rsp_data *out);
int unwrap_ipv6_packet(struct rlocs* reg, struct packet *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

View File

@ -87,6 +87,7 @@ int rlocs_rloc_from_json(struct rlocs* reg, const char* key, json_object* val )
warn( "Couldn't parse %s as an IP address", key );
return 0;
}
strncpy( rloc->presentation, key, 128 );
// Next, we need to read and store the PEM-encoded RSA public key
pem_data = json_object_get_string( val );
@ -371,7 +372,7 @@ struct rlocs* rlocs_new( char* filename )
goto fail;
}
int i, len = json_object_array_length( maps_field );
int i, j, 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 );
@ -380,6 +381,13 @@ struct rlocs* rlocs_new( char* filename )
goto fail;
}
}
// Set our default path mtu for all peers
for ( i = 0 ; i < MAX_RLOCS ; i++ ) {
for( j = 0 ; j < MAX_RLOCS ; j++ ) {
result->peer_contexts[i][j].path_mtu = DEFAULT_PATH_MTU;
}
}
return result;
@ -444,6 +452,31 @@ struct rloc *rloc_find_by_address( struct rlocs *reg, struct in_addr *ipv4, stru
return i < reg->num_entries ? current : NULL;
}
int rlocs_find_two_ipv4(
struct rlocs *reg,
struct rloc **s_rloc_ptr, struct in_addr *s_rloc_addr,
struct rloc **d_rloc_ptr, struct in_addr *d_rloc_addr
)
{
struct rloc *s_rloc = *s_rloc_ptr;
struct rloc *d_rloc = *d_rloc_ptr;
char addr_str[128];
if ( ( s_rloc = rloc_find_by_address( reg, s_rloc_addr, NULL ) ) == NULL ) {
inet_ntop( AF_INET, s_rloc_addr, &addr_str[0], 128 );
debug( "Couldn't find source rloc (%s) in DB", addr_str );
return 0;
}
if ( ( d_rloc = rloc_find_by_address( reg, d_rloc_addr, NULL ) ) == NULL ) {
inet_ntop( AF_INET, d_rloc_addr, &addr_str[0], 128 );
debug( "Couldn't find destination rloc (%s) in DB", addr_str );
return 0;
}
return 1;
}
/* Replaces the public key in the rloc struct with a private key so we can
* unwrap, as well as wrap, packets.
*/
@ -474,9 +507,9 @@ fail:
return 0;
}
int rlocs_update_key_context(struct rlocs *reg, struct rloc *x, struct rloc *y)
int rlocs_update_peer_context(struct rlocs *reg, struct rloc *x, struct rloc *y)
{
struct key_context *entry = &reg->key_contexts[x->context_id][y->context_id];
struct peer_context *entry = &reg->peer_contexts[x->context_id][y->context_id];
unsigned char secret[1024]; // Should be enough buffer space
size_t secret_len = 1024;
@ -525,6 +558,7 @@ int rlocs_update_key_context(struct rlocs *reg, struct rloc *x, struct rloc *y)
* decrypt packet data. */
EVP_CIPHER_CTX_init( &entry->ctx );
entry->path_mtu = DEFAULT_PATH_MTU;
entry->in_use = 1;
return 1;
@ -539,11 +573,16 @@ fail:
}
static inline struct peer_context *rlocs_get_peer_ctx( struct rlocs *reg, struct rloc *x, struct rloc *y )
{
return &reg->peer_contexts[x->context_id][y->context_id];
}
ssize_t rlocs_encrypt( struct rlocs *reg, struct rloc *x, struct rloc *y, unsigned char *data, size_t data_len, unsigned char *dest, size_t dest_len )
{
struct key_context *entry = &reg->key_contexts[x->context_id][y->context_id];
struct peer_context *entry = rlocs_get_peer_ctx( reg, x, y );
if ( !entry->in_use && !rlocs_update_key_context( reg, x, y ) ) {
if ( !entry->in_use && !rlocs_update_peer_context( reg, x, y ) ) {
warn( "Couldn't build an encryption context for these rlocs" );
return -1;
}
@ -611,9 +650,9 @@ fail:
ssize_t rlocs_decrypt( struct rlocs *reg, struct rloc *x, struct rloc *y, unsigned char *data, size_t data_len, unsigned char *dest, size_t dest_len )
{
struct key_context *entry = &reg->key_contexts[x->context_id][y->context_id];
struct peer_context *entry = rlocs_get_peer_ctx( reg, x, y );
if ( !entry->in_use && !rlocs_update_key_context( reg, x, y ) ) {
if ( !entry->in_use && !rlocs_update_peer_context( reg, x, y ) ) {
warn( "Couldn't build a decryption context for these rlocs" );
return -1;
}
@ -700,6 +739,20 @@ void rlocs_debug_output( struct rlocs *reg )
return;
}
unsigned short rlocs_get_path_mtu( struct rlocs *reg, struct rloc *x, struct rloc *y )
{
struct peer_context *entry = rlocs_get_peer_ctx( reg, x, y );
return entry->path_mtu;
}
void rlocs_set_path_mtu( struct rlocs *reg, struct rloc *x, struct rloc *y, unsigned short new_mtu )
{
struct peer_context *entry = rlocs_get_peer_ctx( reg, x, y );
entry->path_mtu = new_mtu;
return;
}
void rlocs_free( struct rlocs* registry )
{
@ -710,6 +763,9 @@ void rlocs_free( struct rlocs* registry )
EVP_PKEY_free( registry->entries[i].key );
}
}
// TODO: free our ctxes and other peer_context items
// No need to do json_object_put() here.
free( registry );
return;

View File

@ -13,11 +13,15 @@
#define MAX_RLOCS 64
#define MAX_EID_MAPPINGS 256
// Just a guess, but 20-byte IP header, 16-byte IV, 16-byte tag, 16 bytes for symmetric block padding
#define DEFAULT_PATH_MTU 1500 - ( 20 + 16 + 16 + 16 )
struct key_context {
struct peer_context {
int in_use;
char secret[SHA256_DIGEST_LENGTH];
EVP_CIPHER_CTX ctx;
unsigned int path_mtu;
/* Probably don't need these
struct rloc *rloc_x;
@ -35,6 +39,7 @@ struct rloc {
EVP_PKEY *key;
// We use this to index our rloc for shared keys
int context_id;
char presentation[128];
};
@ -72,7 +77,7 @@ struct rlocs {
* Half of the allocated memory goes unused, but we can worry about dynamic
* allocation at the same time as MAX_RLOCS and MAX_EID_MAPPINGS
*/
struct key_context key_contexts[MAX_RLOCS][MAX_RLOCS];
struct peer_context peer_contexts[MAX_RLOCS][MAX_RLOCS];
};
@ -84,6 +89,12 @@ 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 );
int rlocs_find_two_ipv4(
struct rlocs *reg,
struct rloc **s_rloc_ptr, struct in_addr *s_rloc_addr,
struct rloc **d_rloc_ptr, struct in_addr *d_rloc_addr
);
int rloc_add_private_key( struct rloc *rloc, char *filename );
void rlocs_debug_output( struct rlocs *reg );
@ -92,6 +103,9 @@ void rlocs_debug_output( struct rlocs *reg );
ssize_t rlocs_encrypt( struct rlocs *reg, struct rloc *x, struct rloc *y, unsigned char *data, size_t data_len, unsigned char *dest, size_t dest_len );
ssize_t rlocs_decrypt( struct rlocs *reg, struct rloc *x, struct rloc *y, unsigned char *data, size_t data_len, unsigned char *dest, size_t dest_len );
unsigned short rlocs_get_path_mtu( struct rlocs *reg, struct rloc *x, struct rloc *y );
void rlocs_set_path_mtu( struct rlocs *reg, struct rloc *x, struct rloc *y, unsigned short new_mtu );
void rlocs_free( struct rlocs *registry );
#endif

View File

@ -1,91 +0,0 @@
#include "util.h"
#include "rlocs.h"
#include <unistd.h>
#include <errno.h>
#include "packet.h"
#include <sys/uio.h>
/*
* Entry point. Expects an invocation like:
* unwrapper <filename of rloc database> <listen_ifname> <output_ifname>
*/
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 < 6 ) {
warn( "Usage: %s <rloc database> <listen_ifname> <output_ifname> <rloc> <keyfile> [<rloc> <keyfile>]n", 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;
}
if ( !session_upgrade_rlocs( &unwrap, argc - 4, argv + 4 ) ) {
warn( "Failed to upgrade rlocs for session, exiting" );
session_teardown( &unwrap );
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;
}

View File

@ -9,6 +9,7 @@
#include <openssl/sha.h>
#define debug(msg, ...) { fprintf( stderr, msg, ##__VA_ARGS__ ) ; fprintf( stdout, "\n" ); }
#define info(msg, ...) { fprintf( stdout, msg, ##__VA_ARGS__ ) ; fprintf( stdout, "\n" ); }
#define warn(msg, ...) { fprintf( stderr, msg, ##__VA_ARGS__ ) ; fprintf( stderr, "\n" ); }

View File

@ -1,91 +0,0 @@
#include "util.h"
#include "rlocs.h"
#include <unistd.h>
#include <errno.h>
#include "packet.h"
// We use writev() to send the packet, so we don't have to copy the
// unencrypted part.
#include <sys/uio.h>
/*
* Entry point. Expects an invocation like:
* wrapper <filename of rloc database> <listen_ifname> <output_ifname>
*/
int main(int argc, char** argv)
{
struct session wrap;
struct recv_pkt recv_pkt;
struct rsp_data to_send;
ssize_t count;
int result;
if ( argc < 6 ) {
warn( "Usage: %s <rloc database> <listen_ifname> <output_ifname> <rloc> <keyfile> [<rloc> <keyfile>]n", argv[0] );
return 1;
}
rlocs_init();
if ( !session_setup( &wrap, 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( 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" );
session_teardown( &wrap );
return 0;
}