/** * Validates if the packet header is valid IPv4 header. This is wrapped around * by parse_ipv4 function. * @param buf Pointer to buffer containing packet header to verify for being ipv4 header * @param packet_len Length of packet passed in the buffer * @return bool True if the packet header is a valid IPv4 header, else False * @see bool parse_ipv4( buffer *buf ) */ static bool valid_ipv4_packet_header( buffer *buf, uint32_t packet_len ) { if ( ( size_t ) packet_len < sizeof( ipv4_header_t ) ) { debug( "Too short IPv4 packet ( length = %u ).", packet_len ); return false; } if ( packet_info( buf )->l3_data.ipv4->version != IPVERSION ) { debug( "Unsupported IP version ( version = %#x ).", packet_info( buf )->l3_data.ipv4->version ); return false; } size_t hdr_len = ( size_t ) packet_info( buf )->l3_data.ipv4->ihl * 4; if ( hdr_len < sizeof( ipv4_header_t ) ) { debug( "Too short IPv4 header length field value ( length = %u ).", hdr_len ); return false; } if ( verify_checksum( ( uint16_t * ) packet_info( buf )->l3_data.ipv4, ( uint32_t ) hdr_len ) != 0 ) { debug( "Corrupted IPv4 header ( checksum verification error )." ); return false; } return true; }
/** * Extracts fragment offset and IP packet length of IPv4 packet and validates * if packet's length is valid. This is wrapped around by parse_ipv4 function. * @param buf Pointer to buffer containing IPv4 packet header * @param packet_len Length of packet passed in the buffer * @return bool True if the packet length is valid IPv4 packet length, else False * @see bool parse_ipv4( buffer *buf ) */ static bool valid_ipv4_packet_more_fragments( buffer *buf, uint32_t packet_len ) { uint16_t ip_len = ntohs( packet_info( buf )->l3_data.ipv4->tot_len ); uint32_t frag_offset = ntohs( packet_info( buf )->l3_data.ipv4->frag_off ); frag_offset = ( frag_offset & IP_OFFMASK ) << 3; if ( ( frag_offset > 0 ) && ( ip_len == ( uint16_t ) sizeof( ipv4_header_t ) ) ) { debug( "Too short IPv4 fragment ( fragment offset = %u ).", frag_offset ); return false; } if ( ( frag_offset + ip_len ) > IP_MAXPACKET ) { debug( "Too large IPv4 packet ( total length = %u, fragment offset = %u ).", ip_len, frag_offset ); return false; } if ( ip_len > packet_len ) { debug( "Packet length is shorter than the total length header field value " "( total length = %u, packet length = %u ).", ip_len, packet_len ); return false; } return true; }
/** * Extracts source and destination address of IPv4 packet and validates them. * This is wrapped around by parse_ipv4 function. * @param buf Pointer to buffer containing IPv4 packet header * @return bool True if source and destination address of IPv4 packet header are valid, else False * @see bool parse_ipv4( buffer *buf ) */ static bool valid_ipv4_packet_ip_address( buffer *buf ) { char addr[ 16 ]; uint32_t ip_sa = ntohl( packet_info( buf )->l3_data.ipv4->saddr ); struct in_addr in; if ( IPV4_IS_CLASSDE( ip_sa ) || IPV4_IS_LOOPBACK( ip_sa ) || IPV4_IS_LIMITEDBC( ip_sa ) ) { in.s_addr = packet_info( buf )->l3_data.ipv4->saddr; memset( addr, 0, sizeof( addr ) ); inet_ntop( AF_INET, &in, addr, sizeof( addr ) ); debug( "Invalid source IPv4 address ( source address = %s ).", addr ); return false; } uint32_t ip_da = ntohl( packet_info( buf )->l3_data.ipv4->daddr ); if ( IPV4_IS_LOOPBACK( ip_da ) ) { in.s_addr = packet_info( buf )->l3_data.ipv4->daddr; memset( addr, 0, sizeof( addr ) ); inet_ntop( AF_INET, &in, addr, sizeof( addr ) ); debug( "Destination IPv4 address must not be a loopback address ( destination address = %s ).", addr ); return false; } if ( ip_sa == ip_da ) { in.s_addr = packet_info( buf )->l3_data.ipv4->saddr; memset( addr, 0, sizeof( addr ) ); inet_ntop( AF_INET, &in, addr, sizeof( addr ) ); debug( "Destination address and source address must not be the same ( address = %s ).", addr ); return false; } return true; }
static buffer * setup_dummy_snap_packet() { buffer *snap_buffer = setup_dummy_ether_packet( sizeof( ether_header_t ) + sizeof( snap_header_t ) ); packet_info( snap_buffer )->l2_data.eth->type = htons( sizeof( ether_header_t ) + sizeof( snap_header_t ) - ETH_PREPADLEN ); packet_info( snap_buffer )->vtag = ( void * ) ( packet_info( snap_buffer )->l2_data.eth + 1 ); memcpy( ( char * ) packet_info( snap_buffer )->vtag, sntp_data, sizeof( snap_header_t ) ); return snap_buffer; }
static buffer * setup_dummy_vlan_packet() { buffer *vlan_buffer = setup_dummy_ether_packet( sizeof( ether_header_t ) + sizeof( vlantag_header_t ) ); packet_info( vlan_buffer )->l2_data.eth->type = htons( sizeof( ether_header_t ) + sizeof( vlantag_header_t ) - ETH_PREPADLEN ); packet_info( vlan_buffer )->l2_data.eth->type = htons( ETH_ETHTYPE_TPID ); packet_info( vlan_buffer )->vtag = ( void * ) ( packet_info( vlan_buffer )->l2_data.eth + 1 ); packet_info( vlan_buffer )->vtag->type = htons( ETH_ETHTYPE_IPV4 ); return vlan_buffer; }
void test_parse_ether_not_llc_header_succeeds() { buffer *buffer = setup_dummy_snap_packet( ); ( ( snap_header_t * ) packet_info( buffer )->vtag )->llc[ 0 ] = 0xFF; ( ( snap_header_t * ) packet_info( buffer )->vtag )->llc[ 1 ] = 0xFF; assert_int_equal( parse_ether( buffer ), true ); assert_int_equal( packet_info( buffer )->ethtype, ETH_ETHTYPE_UKNOWN ); free_buffer( buffer ); }
static buffer * setup_dummy_ether_packet( size_t length ) { buffer *ether_buffer = alloc_buffer_with_length( length ); alloc_packet( ether_buffer ); append_back_buffer( ether_buffer, length ); packet_info( ether_buffer )->l2_data.eth = ether_buffer->data; memcpy( ( char * ) packet_info( ether_buffer )->l2_data.eth->macda, macda, ETH_ADDRLEN ); memcpy( ( char * ) packet_info( ether_buffer )->l2_data.eth->macsa, macsa, ETH_ADDRLEN ); packet_info( ether_buffer )->l2_data.eth->type = htons( ETH_ETHTYPE_ARP ); return ether_buffer; }
void network_throttle::tick() { double time_now = get_time_seconds(); if (!m_any_packet_yet) m_start_time = time_now; // starting now network_time_seconds current_sample_time_slot = time_to_slot( time_now ); // T=13.7 --> 13 (for 1-second smallwindow) network_time_seconds last_sample_time_slot = time_to_slot( m_last_sample_time ); // moving to next position, and filling gaps // !! during this loop the m_last_sample_time and last_sample_time_slot mean the variable moved in +1 // TODO optimize when moving few slots at once while ( (!m_any_packet_yet) || (last_sample_time_slot < current_sample_time_slot)) { _dbg3("Moving counter buffer by 1 second " << last_sample_time_slot << " < " << current_sample_time_slot << " (last time " << m_last_sample_time<<")"); // rotate buffer for (size_t i=m_history.size()-1; i>=1; --i) m_history[i] = m_history[i-1]; m_history[0] = packet_info(); if (! m_any_packet_yet) { m_last_sample_time = time_now; } m_last_sample_time += 1; last_sample_time_slot = time_to_slot( m_last_sample_time ); // increase and recalculate time, time slot m_any_packet_yet=true; } m_last_sample_time = time_now; // the real exact last time }
void test_parse_ether_fails_if_type_is_trancated_payload_ieee8023() { buffer *buffer = setup_dummy_snap_packet( ); packet_info( buffer )->l2_data.eth->type = htons( sizeof( ether_header_t ) + sizeof( snap_header_t ) ); assert_int_equal( parse_ether( buffer ), false ); free_buffer( buffer ); }
void test_parse_ether_fails_if_llc_is_unsupported_llc_snap() { buffer *buffer = setup_dummy_snap_packet( ); ( ( snap_header_t * ) packet_info( buffer )->vtag )->llc[ 2 ] = 1; assert_int_equal( parse_ether( buffer ), false ); free_buffer( buffer ); }
void test_parse_ether_fails_if_mac_address_is_wrong_mac() { buffer *buffer = setup_dummy_ether_packet( sizeof( ether_header_t ) ); packet_info( buffer )->l2_data.eth->macsa[ 0 ] = 0xff; assert_int_equal( parse_ether( buffer ), false ); free_buffer( buffer ); }
void test_parse_ether_fails_if_type_is_trancated_vlan() { buffer *buffer = setup_dummy_ether_packet(sizeof( ether_header_t ) ); packet_info( buffer )->l2_data.eth->type = htons( ETH_ETHTYPE_TPID ); assert_int_equal( parse_ether( buffer ), false ); free_buffer( buffer ); }
void test_parse_ether_fails_if_ether_data_is_NULL() { buffer *buffer = setup_dummy_vlan_packet( ); packet_info( buffer )->l2_data.eth = NULL; expect_assert_failure( parse_ether( buffer ) ); free_buffer( buffer ); }
static buffer * setup_dummy_ether_ipv4_packet() { buffer *ipv4_buffer = setup_dummy_ether_packet( sizeof( ether_header_t ) + sizeof( ipv4_header_t ) + ipv4_padding_size, ETH_ETHTYPE_IPV4 ); ipv4_header_t *ipv4 = packet_info( ipv4_buffer )->l3_data.ipv4; ipv4->version = IPVERSION; ipv4->ihl = sizeof( ipv4_header_t ) / 4; ipv4->tot_len = htons( sizeof( ipv4_header_t ) ); ipv4->ttl = 0; ipv4->check = 0; ipv4->protocol = IPPROTO_UDP; ipv4->saddr = htonl( 0xC0A80067 ); ipv4->daddr = htonl( 0xC0A80036 ); ipv4->frag_off = htons( 0 ); ipv4->check = get_checksum( ( uint16_t * ) packet_info( ipv4_buffer )->l3_data.ipv4, sizeof( ipv4_header_t ) ); free( ipv4_buffer->user_data ); ipv4_buffer->user_data = NULL; remove_front_buffer( ipv4_buffer, ETH_PREPADLEN ); return ipv4_buffer; }
/** * Parses the IPv4 packet for being valid IPv4 packet. * @param buf Pointer to buffer containing IPv4 packet * @return bool True if the buffer contains valid IPv4 packet, else False */ bool parse_ipv4( buffer *buf ) { assert( buf != NULL ); assert( packet_info( buf )->l3_data.ipv4 != NULL ); uint32_t packet_len = ( uint32_t ) buf->length - ( uint32_t ) ( ( char * ) ( packet_info( buf )->l3_data.l3 ) - ( char * ) ( buf->data ) ); if ( !valid_ipv4_packet_header( buf, packet_len ) ) { debug( "Invalid IPv4 header." ); return false; } if ( !valid_ipv4_packet_more_fragments( buf, packet_len ) ) { debug( "Invalid fragmented IPv4 packet." ); return false; } if ( !valid_ipv4_packet_ip_address( buf ) ) { debug( "IPv4 packet with an invalid address." ); return false; } uint32_t hdr_len = ( uint32_t ) packet_info( buf )->l3_data.ipv4->ihl * 4; switch ( packet_info( buf )->l3_data.ipv4->protocol ) { case IPPROTO_AH: if ( ( hdr_len + 8 ) > packet_len ) { debug( "Too short IPv4 packet with an authentication header ( length = %u ).", packet_len ); return false; } packet_info( buf )->ipproto = *( uint8_t * ) ( ( char * ) buf->data + hdr_len ); hdr_len += *( uint8_t * ) ( ( char * ) buf->data + hdr_len + 1 ); break; default: packet_info( buf )->ipproto = packet_info( buf )->l3_data.ipv4->protocol; break; } packet_info( buf )->l4_data.l4 = ( char * ) packet_info( buf )->l3_data.l3 + hdr_len; return true; }
static buffer * setup_dummy_ether_arp_packet() { buffer *arp_buffer = setup_dummy_ether_packet( sizeof( ether_header_t ) + sizeof( arp_header_t ) + arp_padding_size, ETH_ETHTYPE_ARP ); arp_header_t *arp = packet_info( arp_buffer )->l3_data.arp; arp->ar_hrd = htons( ARPHRD_ETHER ); arp->ar_pro = htons( ETH_ETHTYPE_IPV4 ); arp->ar_hln = ETH_ADDRLEN; arp->ar_pln = IPV4_ADDRLEN; arp->ar_op = htons( ARPOP_REPLY ); free( arp_buffer->user_data ); arp_buffer->user_data = NULL; remove_front_buffer( arp_buffer, ETH_PREPADLEN ); return arp_buffer; }
static buffer * setup_dummy_ether_packet( size_t length, uint16_t type ) { buffer *buf = alloc_buffer_with_length( length ); alloc_packet( buf ); append_back_buffer( buf, length ); packet_info( buf )->l2_data.eth = buf->data; packet_info( buf )->ethtype = type; ether_header_t *ether = packet_info( buf )->l2_data.eth; ether->type = htons( type ); memcpy( ( char * ) ether->macda, macda, ETH_ADDRLEN ); memcpy( ( char * ) ether->macsa, macsa, ETH_ADDRLEN ); packet_info( buf )->l3_data.l3 = ( char * ) packet_info( buf )->l2_data.l2 + sizeof( ether_header_t ); vlantag_header_t *vtag = ( vlantag_header_t * ) ( ( void * ) ( ether + 1 ) ); packet_info( buf )->vtag = vtag; return buf; }
/** * This function is to find if Ethernet header contained in passed buffer is * valid Ethernet header or not. * @param buf Buffer containing header to verify for being valid Ethernet header * @return bool True if buffer contains valid Ethernet header, else False */ bool parse_ether( buffer *buf ) { assert( buf != NULL ); assert( packet_info( buf )->l2_data.eth != NULL ); size_t frame_length = buf->length - ( size_t ) ( ( char * ) ( packet_info( buf )->l2_data.l2 ) - ( char * ) buf->data ) - ETH_PREPADLEN; ether_header_t *eth = packet_info( buf )->l2_data.eth; size_t ether_length = sizeof( ether_header_t ) - ETH_PREPADLEN; if ( frame_length < ether_length ) { debug( "Frame length is shorter than the length of an Ethernet header ( frame length = %u ).", frame_length ); return false; } debug( "Parsing an Ethernet frame " "( source mac = %02x:%02x:%02x:%02x:%02x:%02x, " "destination mac = %02x:%02x:%02x:%02x:%02x:%02x ).", eth->macsa[ 0 ], eth->macsa[ 1 ], eth->macsa[ 2 ], eth->macsa[ 3 ], eth->macsa[ 4 ], eth->macsa[ 5 ], eth->macda[ 0 ], eth->macda[ 1 ], eth->macda[ 2 ], eth->macda[ 3 ], eth->macda[ 4 ], eth->macda[ 5 ] ); if ( eth->macsa[ 0 ] & 0x01 ) { debug( "Source MAC address is multicast or broadcast." ); return false; } uint16_t type = ntohs( eth->type ); vlantag_header_t *vlan_tag = ( void * ) ( eth + 1 ); uint8_t next_vlan_tags = 0; while ( type == ETH_ETHTYPE_TPID ) { ether_length += sizeof( vlantag_header_t ); if ( frame_length < ether_length ) { debug( "Too short 802.1Q frame ( frame length = %u ).", frame_length ); return false; } type = ntohs( vlan_tag->type ); vlan_tag++; next_vlan_tags++; debug( "802.1Q header found ( type = %#x, # of headers = %u ).", type, next_vlan_tags ); } if ( type <= ETH_ETHTYPE_8023 ) { snap_header_t *snap = ( snap_header_t * ) vlan_tag; // check frame length first if ( snap->llc[ 0 ] == 0xaa && snap->llc[ 1 ] == 0xaa ) { // LLC header with SNAP ether_length += sizeof( snap_header_t ); } else { // LLC header without SNAP ether_length += offsetof( snap_header_t, oui ); } if ( frame_length < ether_length ) { debug( "Too short LLC/SNAP frame ( minimun frame length = %u, frame length = %u ).", ether_length, frame_length ); return false; } if ( frame_length < ( size_t ) type ) { debug( "Frame length is shorter than the length header field value " "( frame length = %u, length field value = %u ).", frame_length, type ); return false; } // check header field values if ( snap->llc[ 0 ] == 0xaa && snap->llc[ 1 ] == 0xaa ) { // LLC header with SNAP if ( snap->llc[ 2 ] == 0x03 || snap->llc[ 2 ] == 0xf3 || snap->llc[ 2 ] == 0xe3 || snap->llc[ 2 ] == 0xbf || snap->llc[ 2 ] == 0xaf ) { type = ntohs( snap->type ); } else { debug( "Unexpected SNAP frame ( length = %u, llc = 0x%02x%02x%02x, oui = 0x%02x%02x%02x, type = %u ).", type, snap->llc[ 0 ], snap->llc[ 1 ], snap->llc[ 2 ], snap->oui[ 0 ], snap->oui[ 1 ], snap->oui[ 2 ], ntohs( snap->type ) ); return false; } } else { // LLC header without SNAP debug( "Unhandled 802.3 frame ( length = %u, llc = 0x%02x%02x%02x ).", type, snap->llc[ 0 ], snap->llc[ 1 ], snap->llc[ 2 ] ); type = ETH_ETHTYPE_UKNOWN; } } packet_info( buf )->nvtags = next_vlan_tags; if ( next_vlan_tags > 0 ) { packet_info( buf )->vtag = ( void * ) ( eth + 1 ); debug( "802.1Q header found ( tci = %#x, type = %#x ).", ntohs( packet_info( buf )->vtag->tci ), ntohs( packet_info( buf )->vtag->type ) ); } else { packet_info( buf )->vtag = NULL; debug( "No 802.1Q header found." ); } debug( "Ethernet type = %#x.", type ); packet_info( buf )->ethtype = type; packet_info( buf )->l3_data.l3 = ( char * ) packet_info( buf )->l2_data.l2 + ETH_PREPADLEN + ether_length; return true; }
static void handle_packet_in( uint64_t datapath_id, uint32_t transaction_id, uint32_t buffer_id, uint16_t total_len, uint16_t in_port, uint8_t reason, const buffer *data, void *user_data ) { assert( in_port != 0 ); assert( data != NULL ); assert( user_data != NULL ); routing_switch *routing_switch = user_data; debug( "Packet-In received ( datapath_id = %#" PRIx64 ", transaction_id = %#lx, " "buffer_id = %#lx, total_len = %u, in_port = %u, reason = %#x, " "data_len = %u ).", datapath_id, transaction_id, buffer_id, total_len, in_port, reason, data->length ); const port_info *port = lookup_outbound_port( routing_switch->switches, datapath_id, in_port ); const uint8_t *src = packet_info( data )->l2_data.eth->macsa; const uint8_t *dst = packet_info( data )->l2_data.eth->macda; if ( in_port <= OFPP_MAX || in_port == OFPP_LOCAL ) { if ( port == NULL && !lookup_fdb( routing_switch->fdb, src, &datapath_id, &in_port ) ) { debug( "Ignoring Packet-In from switch-to-switch link" ); return; } } else { error( "Packet-In from invalid port ( in_port = %#u ).", in_port ); return; } if ( !update_fdb( routing_switch->fdb, src, datapath_id, in_port ) ) { return; } buffer *original_packet = duplicate_buffer( data ); uint16_t out_port; uint64_t out_datapath_id; if ( lookup_fdb( routing_switch->fdb, dst, &out_datapath_id, &out_port ) ) { // Host is located, so resolve path and send flowmod if ( ( datapath_id == out_datapath_id ) && ( in_port == out_port ) ) { // in and out are same free_buffer( original_packet ); return; } // Ask path resolver service to lookup a path // resolve_path_replied() will be called later resolve_path_replied_params *param = xmalloc( sizeof( *param ) ); param->routing_switch = routing_switch; param->original_packet = original_packet; resolve_path( datapath_id, in_port, out_datapath_id, out_port, param, resolve_path_replied ); } else { // Host's location is unknown, so flood packet flood_packet( datapath_id, in_port, original_packet, routing_switch->switches ); } }
/* * The MAC destination address. * * @return [Trema::Mac] macda MAC destination address. */ static VALUE packet_in_macda( VALUE self ) { packet_in *cpacket_in = get_packet_in( self ); VALUE macda = ULL2NUM( mac_to_uint64( packet_info( cpacket_in->data )->l2_data.eth->macda ) ); return rb_funcall( rb_eval_string( "Trema::Mac" ), rb_intern( "new" ), 1, macda ); }