63,6 → 63,9 |
#include "../../include/ip_client.h" |
#include "../../include/ip_interface.h" |
#include "../../include/tl_interface.h" |
#include "../../include/icmp_codes.h" |
#include "../../include/icmp_interface.h" |
#include "../../include/icmp_client.h" |
#include "../../structures/measured_strings.h" |
#include "../../structures/module_map.h" |
#include "../../structures/packet/packet_client.h" |
178,16 → 181,16 |
*/ |
int ip_netif_initialize( ip_netif_ref ip_netif ); |
|
int ip_send_route( packet_t packet, ip_netif_ref netif, ip_route_ref route, in_addr_t * src, in_addr_t dest ); |
int ip_send_route( packet_t packet, ip_netif_ref netif, ip_route_ref route, in_addr_t * src, in_addr_t dest, services_t error ); |
int ip_prepare_packet( in_addr_t * source, in_addr_t dest, packet_t packet, measured_string_ref destination ); |
|
packet_t ip_split_packet( packet_t packet, size_t prefix, size_t content, size_t suffix, size_t addr_len ); |
packet_t ip_split_packet( packet_t packet, size_t prefix, size_t content, size_t suffix, size_t addr_len, services_t error ); |
int ip_fragment_packet( packet_t packet, size_t length, size_t prefix, size_t suffix, size_t addr_len ); |
int ip_fragment_packet_data( packet_t packet, packet_t new_packet, ip_header_ref header, ip_header_ref new_header, size_t length, void * src, void * dest, size_t address_length ); |
ip_header_ref ip_create_middle_header( packet_t packet, ip_header_ref last ); |
void ip_create_last_header( ip_header_ref last, ip_header_ref first ); |
|
in_addr_t * ip_netif_addr( ip_netif_ref netif ); |
in_addr_t * ip_netif_address( ip_netif_ref netif ); |
ip_route_ref ip_find_route( in_addr_t destination ); |
ip_route_ref ip_netif_find_route( ip_netif_ref netif, in_addr_t destination ); |
|
205,7 → 208,7 |
|
int ip_process_packet( device_id_t device_id, packet_t packet ); |
in_addr_t ip_get_destination( ip_header_ref header ); |
int ip_deliver_local( device_id_t device_id, packet_t packet, ip_header_ref header ); |
int ip_deliver_local( device_id_t device_id, packet_t packet, ip_header_ref header, services_t error ); |
|
/** Computes the ip header checksum. |
* To compute the checksum of a new packet, the checksum header field must be zero. |
218,6 → 221,12 |
*/ |
uint16_t ip_checksum( uint8_t * data, size_t length ); |
|
int ip_prepare_icmp_and_get_phone( services_t error, packet_t packet, ip_header_ref header ); |
int ip_get_icmp_phone( void ); |
int ip_prepare_icmp( packet_t packet, ip_header_ref header ); |
|
static int release_and_return( packet_t packet, int result ); |
|
uint16_t ip_checksum( uint8_t * data, size_t length ){ |
uint16_t checksum; |
|
518,7 → 527,7 |
return EOK; |
} |
|
int ip_send_msg( int il_phone, device_id_t device_id, packet_t packet, services_t sender ){ |
int ip_send_msg( int il_phone, device_id_t device_id, packet_t packet, services_t sender, services_t error ){ |
ERROR_DECLARE; |
|
int length; |
526,22 → 535,20 |
ip_route_ref route; |
in_addr_t * dest; |
in_addr_t * src; |
int phone; |
|
// addresses in the host byte order |
// should be the next hop address or the target destination address |
length = packet_get_addr( packet, NULL, ( uint8_t ** ) & dest ); |
if( length < 0 ){ |
pq_release( ip_globals.net_phone, packet_get_id( packet )); |
return length; |
return release_and_return( packet, length ); |
} |
// TODO IPv6 |
if( length != IP_ADDR ){ |
pq_release( ip_globals.net_phone, packet_get_id( packet )); |
return EINVAL; |
return release_and_return( packet, EINVAL ); |
} |
fibril_rwlock_read_lock( & ip_globals.netifs_lock ); |
// device specified? |
// dest.s_addr = ntohl( dest.s_addr ); |
if( device_id > 0 ){ |
netif = ip_netifs_find( & ip_globals.netifs, device_id ); |
route = ip_netif_find_route( netif, * dest ); |
552,23 → 559,35 |
} |
if( !( netif && route )){ |
fibril_rwlock_read_unlock( & ip_globals.netifs_lock ); |
pq_release( ip_globals.net_phone, packet_get_id( packet )); |
phone = ip_prepare_icmp_and_get_phone( error, packet, NULL ); |
if( phone >= 0 ){ |
// unreachable ICMP if no routing |
icmp_destination_unreachable_msg( phone, ICMP_HOST_UNREACH, 0, packet ); |
} |
return ENOENT; |
} |
if( error ){ |
// do not send for broadcast, anycast packets or network broadcast |
if(( ! dest->s_addr ) |
|| ( !( ~ dest->s_addr )) |
|| ( !( ~(( dest->s_addr & ( ~ route->netmask.s_addr )) | route->netmask.s_addr ))) |
|| ( !( dest->s_addr & ( ~ route->netmask.s_addr )))){ |
return release_and_return( packet, EINVAL ); |
} |
} |
// to me? |
if( route->address.s_addr == dest->s_addr ){ |
// TODO loopback deliver |
fibril_rwlock_read_unlock( & ip_globals.netifs_lock ); |
return ip_deliver_local( -1, packet, ( ip_header_ref ) packet_get_data( packet )); |
return ip_deliver_local( -1, packet, ( ip_header_ref ) packet_get_data( packet ), error ); |
} |
|
src = ip_netif_addr( netif ); |
src = ip_netif_address( netif ); |
if( ! src ){ |
fibril_rwlock_read_unlock( & ip_globals.netifs_lock ); |
pq_release( ip_globals.net_phone, packet_get_id( packet )); |
return ENOENT; |
return release_and_return( packet, ENOENT ); |
} |
if( ERROR_OCCURRED( ip_send_route( packet, netif, route, src, * dest ))){ |
if( ERROR_OCCURRED( ip_send_route( packet, netif, route, src, * dest, error ))){ |
pq_release( ip_globals.net_phone, packet_get_id( packet )); |
} |
fibril_rwlock_read_unlock( & ip_globals.netifs_lock ); |
575,7 → 594,7 |
return ERROR_CODE; |
} |
|
in_addr_t * ip_netif_addr( ip_netif_ref netif ){ |
in_addr_t * ip_netif_address( ip_netif_ref netif ){ |
ip_route_ref route; |
|
route = ip_routes_get_index( & netif->routes, 0 ); |
582,12 → 601,13 |
return route ? & route->address : NULL; |
} |
|
int ip_send_route( packet_t packet, ip_netif_ref netif, ip_route_ref route, in_addr_t * src, in_addr_t dest ){ |
int ip_send_route( packet_t packet, ip_netif_ref netif, ip_route_ref route, in_addr_t * src, in_addr_t dest, services_t error ){ |
ERROR_DECLARE; |
|
measured_string_t destination; |
measured_string_ref translation; |
char * data; |
int phone; |
|
// get destination hardware address |
if( netif->arp ){ |
597,12 → 617,16 |
sleep( 1 ); |
ERROR_PROPAGATE( arp_translate_req( netif->arp->phone, netif->device_id, SERVICE_IP, & destination, & translation, & data )); |
} |
// TODO unreachable |
if( ! translation ) return EINVAL; |
if( ! translation->value ){ |
// TODO unreachable |
free( translation ); |
free( data ); |
if( !( translation && translation->value )){ |
if( translation ){ |
free( translation ); |
free( data ); |
} |
phone = ip_prepare_icmp_and_get_phone( error, packet, NULL ); |
if( phone >= 0 ){ |
// unreachable ICMP if no routing |
icmp_destination_unreachable_msg( phone, ICMP_HOST_UNREACH, 0, packet ); |
} |
return EINVAL; |
} |
}else translation = NULL; |
609,7 → 633,7 |
if( ERROR_OCCURRED( ip_prepare_packet( src, dest, packet, translation ))){ |
pq_release( ip_globals.net_phone, packet_get_id( packet )); |
}else{ |
packet = ip_split_packet( packet, netif->prefix, netif->content, netif->suffix, netif->addr_len ); |
packet = ip_split_packet( packet, netif->prefix, netif->content, netif->suffix, netif->addr_len, error ); |
if( packet ){ |
nil_send_msg( netif->phone, netif->device_id, packet, SERVICE_IP ); |
} |
699,12 → 723,15 |
return ip_register( IL_GET_PROTO( call ), IL_GET_SERVICE( call ), IPC_GET_PHONE( call ), NULL ); |
case NET_IL_SEND: |
ERROR_PROPAGATE( packet_translate( ip_globals.net_phone, & packet, IPC_GET_PACKET( call ))); |
return ip_send_msg( 0, IPC_GET_DEVICE( call ), packet, 0 ); |
return ip_send_msg( 0, IPC_GET_DEVICE( call ), packet, 0, IPC_GET_ERROR( call )); |
case NET_IL_DEVICE_STATE: |
return ip_device_state_message( IPC_GET_DEVICE( call ), IPC_GET_STATE( call )); |
case NET_IL_RECEIVED: |
ERROR_PROPAGATE( packet_translate( ip_globals.net_phone, & packet, IPC_GET_PACKET( call ))); |
return ip_receive_message( IPC_GET_DEVICE( call ), packet ); |
case NET_IP_RECEIVED_ERROR: |
ERROR_PROPAGATE( packet_translate( ip_globals.net_phone, & packet, IPC_GET_PACKET( call ))); |
return ip_received_error_msg( 0, IPC_GET_DEVICE( call ), packet, IPC_GET_TARGET( call ), IPC_GET_ERROR( call )); |
case NET_IP_ADD_ROUTE: |
return ip_add_route_req( 0, IPC_GET_DEVICE( call ), IP_GET_ADDRESS( call ), IP_GET_NETMASK( call ), IP_GET_GATEWAY( call )); |
case NET_IP_SET_GATEWAY: |
831,10 → 858,12 |
return EOK; |
} |
|
packet_t ip_split_packet( packet_t packet, size_t prefix, size_t content, size_t suffix, size_t addr_len ){ |
packet_t ip_split_packet( packet_t packet, size_t prefix, size_t content, size_t suffix, size_t addr_len, services_t error ){ |
size_t length; |
packet_t next; |
packet_t new_packet; |
int result; |
int phone; |
|
next = packet; |
// check all packets |
842,12 → 871,23 |
length = packet_get_data_length( next ); |
// too long? |
if( length > content ){ |
if( ip_fragment_packet( next, content, prefix, suffix, addr_len ) != EOK ){ |
result = ip_fragment_packet( next, content, prefix, suffix, addr_len ); |
if( result != EOK ){ |
new_packet = pq_detach( next ); |
if( next == packet ){ |
// the new first packet of the queue |
packet = new_packet; |
} |
pq_release( ip_globals.net_phone, packet_get_id( next )); |
// fragmentation needed? |
if( result == EPERM ){ |
phone = ip_prepare_icmp_and_get_phone( error, next, NULL ); |
if( phone >= 0 ){ |
// fragmentation necessary ICMP |
icmp_destination_unreachable_msg( phone, ICMP_FRAG_NEEDED, content, next ); |
} |
}else{ |
pq_release( ip_globals.net_phone, packet_get_id( next )); |
} |
next = new_packet; |
continue; |
} |
876,7 → 916,6 |
if( ! header ) return EINVAL; |
// fragmentation forbidden? |
if( header->flags & IPFLAG_DONT_FRAGMENT ){ |
// TODO fragmentation necessary ICMP |
return EPERM; |
} |
// create the last fragment |
885,21 → 924,18 |
// allocate as much as originally |
last_header = ( ip_header_ref ) packet_suffix( new_packet, IP_HEADER_LENGTH( header )); |
if( ! last_header ){ |
pq_release( ip_globals.net_phone, packet_get_id( new_packet )); |
return ENOMEM; |
return release_and_return( packet, ENOMEM ); |
} |
ip_create_last_header( last_header, header ); |
// trim the unused space |
if( ERROR_OCCURRED( packet_trim( new_packet, 0, IP_HEADER_LENGTH( header ) - IP_HEADER_LENGTH( last_header )))){ |
pq_release( ip_globals.net_phone, packet_get_id( new_packet )); |
return ERROR_CODE; |
return release_and_return( packet, ERROR_CODE ); |
} |
// biggest multiple of 8 lower than content |
// TODO even fragmentation? |
length = length & ( ~ 0x7 );// ( content / 8 ) * 8 |
if( ERROR_OCCURRED( ip_fragment_packet_data( packet, new_packet, header, last_header, (( IP_TOTAL_LENGTH( header ) - length ) % ( length - IP_HEADER_LENGTH( last_header ))), src, dest, address_length ))){ |
pq_release( ip_globals.net_phone, packet_get_id( new_packet )); |
return ERROR_CODE; |
return release_and_return( packet, ERROR_CODE ); |
} |
// mark the first as fragmented |
header->flags |= IPFLAG_MORE_FRAGMENTS; |
909,17 → 945,14 |
if( ! new_packet ) return ENOMEM; |
middle_header = ip_create_middle_header( new_packet, last_header ); |
if( ! middle_header ){ |
pq_release( ip_globals.net_phone, packet_get_id( new_packet )); |
return ENOMEM; |
return release_and_return( packet, ENOMEM ); |
} |
if( ERROR_OCCURRED( ip_fragment_packet_data( packet, new_packet, header, middle_header, length - IP_HEADER_LENGTH( middle_header ), src, dest, address_length ))){ |
pq_release( ip_globals.net_phone, packet_get_id( new_packet )); |
return ERROR_CODE; |
return release_and_return( packet, ERROR_CODE ); |
} |
} |
// finish the first fragment |
header->header_checksum = IP_HEADER_CHECKSUM( header ); |
printf( "ok\n" ); |
return EOK; |
} |
|
990,9 → 1023,7 |
|
do{ |
next = pq_detach( packet ); |
if( ip_process_packet( device_id, packet ) != EOK ){ |
pq_release( ip_globals.net_phone, packet_get_id( packet )); |
} |
ip_process_packet( device_id, packet ); |
packet = next; |
}while( packet ); |
return EOK; |
1004,42 → 1035,102 |
ip_header_ref header; |
in_addr_t dest; |
ip_route_ref route; |
int phone; |
|
header = ( ip_header_ref ) packet_get_data( packet ); |
if( ! header ) return ENOMEM; |
if( ! header ){ |
return release_and_return( packet, ENOMEM ); |
} |
// checksum |
if(( header->header_checksum ) && ( IP_HEADER_CHECKSUM( header ))){ |
// TODO checksum error ICMP? |
return release_and_return( packet, EINVAL ); |
} |
if( header->ttl <= 1 ){ |
phone = ip_prepare_icmp_and_get_phone( 0, packet, header ); |
if( phone >= 0 ){ |
// ttl oxceeded ICMP |
icmp_time_exceeded_msg( phone, ICMP_EXC_TTL, packet ); |
} |
return EINVAL; |
} |
// TODO ttl oxceeded ICMP? |
if( !( -- header->ttl )) return EINVAL; |
// process ipopt and get destination |
dest = ip_get_destination( header ); |
ERROR_PROPAGATE( packet_set_addr( packet, NULL, ( uint8_t * ) & dest.s_addr, IP_ADDR )); |
route = ip_find_route( dest ); |
// TODO unreachable ICMP? |
if( ! route ) return ENOENT; |
if( ! route ){ |
phone = ip_prepare_icmp_and_get_phone( 0, packet, header ); |
if( phone >= 0 ){ |
// unreachable ICMP |
icmp_destination_unreachable_msg( phone, ICMP_HOST_UNREACH, 0, packet ); |
} |
return ENOENT; |
} |
if( route->address.s_addr == dest.s_addr ){ |
// local delivery |
return ip_deliver_local( device_id, packet, header ); |
return ip_deliver_local( device_id, packet, header, 0 ); |
}else{ |
// only if routing enabled |
if( route->netif->routing ){ |
return ip_send_route( packet, route->netif, route, NULL, dest ); |
} |
else |
{ |
// TODO icmp unreachable? |
-- header->ttl; |
return ip_send_route( packet, route->netif, route, NULL, dest, 0 ); |
}else{ |
phone = ip_prepare_icmp_and_get_phone( 0, packet, header ); |
if( phone >= 0 ){ |
// unreachable ICMP if no routing |
icmp_destination_unreachable_msg( phone, ICMP_HOST_UNREACH, 0, packet ); |
} |
return ENOENT; |
} |
} |
} |
|
int ip_deliver_local( device_id_t device_id, packet_t packet, ip_header_ref header ){ |
int ip_received_error_msg( int ip_phone, device_id_t device_id, packet_t packet, services_t target, services_t error ){ |
uint8_t * data; |
int offset; |
icmp_type_t type; |
icmp_code_t code; |
ip_netif_ref netif; |
measured_string_t address; |
ip_route_ref route; |
ip_header_ref header; |
|
switch( error ){ |
case SERVICE_ICMP: |
offset = icmp_client_process_packet( packet, & type, & code, NULL, NULL ); |
if( offset < 0 ){ |
return release_and_return( packet, ENOMEM ); |
} |
data = packet_get_data( packet ); |
header = ( ip_header_ref ) data + offset; |
// destination host unreachable? |
if(( type == ICMP_DEST_UNREACH ) && ( code == ICMP_HOST_UNREACH )){ |
fibril_rwlock_read_lock( & ip_globals.netifs_lock ); |
netif = ip_netifs_find( & ip_globals.netifs, device_id ); |
if( netif && netif->arp ){ |
route = ip_routes_get_index( & netif->routes, 0 ); |
// from the same network? |
if( route && (( route->address.s_addr & route->netmask.s_addr ) == ( header->destination_address & route->netmask.s_addr ))){ |
// clear the ARP mapping if any |
address.value = ( char * ) & header->destination_address; |
address.length = CONVERT_SIZE( uint8_t, char, sizeof( header->destination_address )); |
arp_clear_address_req( netif->arp->phone, netif->device_id, SERVICE_IP, & address ); |
} |
} |
fibril_rwlock_read_unlock( & ip_globals.netifs_lock ); |
} |
break; |
default: |
return release_and_return( packet, ENOTSUP ); |
} |
return ip_deliver_local( device_id, packet, header, error ); |
} |
|
int ip_deliver_local( device_id_t device_id, packet_t packet, ip_header_ref header, services_t error ){ |
ERROR_DECLARE; |
|
ip_proto_ref proto; |
int phone; |
|
if(( header->flags & IPFLAG_MORE_FRAGMENTS ) || header->fragment_offset ){ |
// TODO fragmented |
1050,12 → 1141,17 |
proto = ip_protos_find( & ip_globals.protos, header->protocol ); |
if( ! proto ){ |
fibril_rwlock_read_unlock( & ip_globals.protos_lock ); |
phone = ip_prepare_icmp_and_get_phone( error, packet, header ); |
if( phone >= 0 ){ |
// unreachable ICMP |
icmp_destination_unreachable_msg( phone, ICMP_PROT_UNREACH, 0, packet ); |
} |
return ENOENT; |
} |
if( proto->received_msg ){ |
ERROR_CODE = proto->received_msg( device_id, packet, SERVICE_IP ); |
ERROR_CODE = proto->received_msg( device_id, packet, proto->service, error ); |
}else{ |
ERROR_CODE = tl_received_msg( proto->phone, device_id, packet, proto->service ); |
ERROR_CODE = tl_received_msg( proto->phone, device_id, packet, proto->service, error ); |
} |
fibril_rwlock_read_unlock( & ip_globals.protos_lock ); |
return ERROR_CODE; |
1066,9 → 1162,55 |
in_addr_t destination; |
|
// TODO search set ipopt route? |
destination.s_addr = header->destination_address; //ntohl( header->destination_address ); |
destination.s_addr = header->destination_address; |
return destination; |
} |
|
int ip_prepare_icmp( packet_t packet, ip_header_ref header ){ |
packet_t next; |
|
// detach the first packet and release the others |
next = pq_detach( packet ); |
if( next ){ |
pq_release( ip_globals.net_phone, packet_get_id( next )); |
} |
if( ! header ){ |
if( packet_get_data_length( packet ) <= sizeof( ip_header_t )) return ENOMEM; |
// get header |
header = ( ip_header_ref ) packet_get_data( packet ); |
if( ! header ) return EINVAL; |
} |
// only for the first fragment |
if( header->fragment_offset ) return EINVAL; |
// set the destination address |
return packet_set_addr( packet, NULL, ( uint8_t * ) & header->source_address, sizeof( header->source_address )); |
} |
|
int ip_get_icmp_phone( void ){ |
ip_proto_ref proto; |
int phone; |
|
fibril_rwlock_read_lock( & ip_globals.protos_lock ); |
proto = ip_protos_find( & ip_globals.protos, IPPROTO_ICMP ); |
phone = proto ? proto->phone : ENOENT; |
fibril_rwlock_read_unlock( & ip_globals.protos_lock ); |
return phone; |
} |
|
int ip_prepare_icmp_and_get_phone( services_t error, packet_t packet, ip_header_ref header ){ |
int phone; |
|
phone = ip_get_icmp_phone(); |
if( error || ( phone < 0 ) || ip_prepare_icmp( packet, header )){ |
return release_and_return( packet, EINVAL ); |
} |
return phone; |
} |
|
static int release_and_return( packet_t packet, int result ){ |
pq_release( ip_globals.net_phone, packet_get_id( packet )); |
return result; |
} |
|
/** @} |
*/ |