First pass at fragmenting

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

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) );
}
}
}