Subversion Repositories HelenOS

Rev

Rev 4719 | Rev 4721 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright (c) 2008 Lukas Mejdrech
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  *
  9.  * - Redistributions of source code must retain the above copyright
  10.  *   notice, this list of conditions and the following disclaimer.
  11.  * - Redistributions in binary form must reproduce the above copyright
  12.  *   notice, this list of conditions and the following disclaimer in the
  13.  *   documentation and/or other materials provided with the distribution.
  14.  * - The name of the author may not be used to endorse or promote products
  15.  *   derived from this software without specific prior written permission.
  16.  *
  17.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  18.  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  19.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  20.  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  21.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  22.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27.  */
  28.  
  29. /** @addtogroup icmp
  30.  *  @{
  31.  */
  32.  
  33. /** @file
  34.  *  ICMP module implementation.
  35.  *  @see icmp.h
  36.  */
  37.  
  38. #include <async.h>
  39. #include <atomic.h>
  40. #include <fibril.h>
  41. #include <fibril_sync.h>
  42.  
  43. #include <ipc/ipc.h>
  44. #include <ipc/services.h>
  45.  
  46. #include <sys/types.h>
  47.  
  48. #include "../../err.h"
  49. #include "../../messages.h"
  50. #include "../../modules.h"
  51.  
  52. #include "../../structures/packet/packet_client.h"
  53.  
  54. #include "../../include/byteorder.h"
  55. #include "../../include/crc.h"
  56. #include "../../include/icmp_api.h"
  57. #include "../../include/icmp_client.h"
  58. #include "../../include/icmp_codes.h"
  59. #include "../../include/icmp_common.h"
  60. #include "../../include/icmp_interface.h"
  61. #include "../../include/il_interface.h"
  62. #include "../../include/inet.h"
  63. #include "../../include/ip_client.h"
  64. #include "../../include/ip_interface.h"
  65. #include "../../include/ip_protocols.h"
  66. #include "../../include/net_interface.h"
  67. #include "../../include/socket_codes.h"
  68. #include "../../include/socket_errno.h"
  69.  
  70. #include "../../tl/tl_messages.h"
  71.  
  72. #include "icmp.h"
  73. #include "icmp_header.h"
  74. #include "icmp_messages.h"
  75. #include "icmp_module.h"
  76.  
  77. /** Original datagram length in bytes transfered to the error notification message.
  78.  */
  79. #define ICMP_KEEP_LENGTH    8
  80.  
  81. /** Computes the ICMP datagram checksum.
  82.  *  @param header The ICMP datagram header. Input/output parameter.
  83.  *  @param length The total datagram length. Input parameter.
  84.  *  @returns The computed checksum.
  85.  */
  86. #define ICMP_CHECKSUM( header, length )     htons( ip_checksum(( uint8_t * ) ( header ), ( length )))
  87.  
  88. /** An echo request datagrams pattern.
  89.  */
  90. #define ICMP_ECHO_TEXT                  "Hello from HelenOS."
  91.  
  92. /** Computes an ICMP reply data key.
  93.  *  @param id The message identifier. Input parameter.
  94.  *  @param sequence The message sequence number. Input parameter.
  95.  *  @returns The computed ICMP reply data key.
  96.  */
  97. #define ICMP_GET_REPLY_KEY( id, sequence )  ((( id ) << 16 ) | ( sequence & 0xFFFF ))
  98.  
  99. /** Type definition of the ICMP reply timeout.
  100.  *  @see icmp_reply_timeout
  101.  */
  102. typedef struct icmp_reply_timeout   icmp_reply_timeout_t;
  103.  
  104. /** Type definition of the ICMP reply timeout pointer.
  105.  *  @see icmp_reply_timeout
  106.  */
  107. typedef icmp_reply_timeout_t *  icmp_reply_timeout_ref;
  108.  
  109. /** Processes the received ICMP packet.
  110.  *  Is used as an entry point from the underlying IP module.
  111.  *  Releases the packet on error.
  112.  *  @param device_id The device identifier. Ignored parameter.
  113.  *  @param packet The received packet. Input/output parameter.
  114.  *  @param receiver The target service. Ignored parameter.
  115.  *  @param error The packet error reporting service. Prefixes the received packet. Input parameter.
  116.  *  @returns EOK on success.
  117.  *  @returns Other error codes as defined for the icmp_process_packet() function.
  118.  */
  119. int icmp_received_msg( device_id_t device_id, packet_t packet, services_t receiver, services_t error );
  120.  
  121. /** Processes the received ICMP packet.
  122.  *  Notifies the destination socket application.
  123.  *  @param packet The received packet. Input/output parameter.
  124.  *  @param error The packet error reporting service. Prefixes the received packet. Input parameter.
  125.  *  @returns EOK on success.
  126.  *  @returns EINVAL if the packet is not valid.
  127.  *  @returns EINVAL if the stored packet address is not the an_addr_t.
  128.  *  @returns EINVAL if the packet does not contain any data.
  129.  *  @returns NO_DATA if the packet content is shorter than the user datagram header.
  130.  *  @returns ENOMEM if there is not enough memory left.
  131.  *  @returns EADDRNOTAVAIL if the destination socket does not exist.
  132.  *  @returns Other error codes as defined for the ip_client_process_packet() function.
  133.  */
  134. int icmp_process_packet( packet_t packet, services_t error );
  135.  
  136. /** Processes the client messages.
  137.  *  Remembers the assigned identifier and sequence numbers.
  138.  *  Runs until the client module disconnects.
  139.  *  @param callid The message identifier. Input parameter.
  140.  *  @param call The message parameters. Input parameter.
  141.  *  @returns EOK.
  142.  *  @see icmp_interface.h
  143.  *  @see icmp_api.h
  144.  */
  145. int icmp_process_client_messages( ipc_callid_t callid, ipc_call_t call );
  146.  
  147. /** Processes the generic client messages.
  148.  *  @param call The message parameters. Input parameter.
  149.  *  @returns EOK on success.
  150.  *  @returns ENOTSUP if the message is not known.
  151.  *  @returns Other error codes as defined for the packet_translate() function.
  152.  *  @returns Other error codes as defined for the icmp_destination_unreachable_msg() function.
  153.  *  @returns Other error codes as defined for the icmp_source_quench_msg() function.
  154.  *  @returns Other error codes as defined for the icmp_time_exceeded_msg() function.
  155.  *  @returns Other error codes as defined for the icmp_parameter_problem_msg() function.
  156.  *  @see icmp_interface.h
  157.  */
  158. int icmp_process_message( ipc_call_t * call );
  159.  
  160. /** Releases the packet and returns the result.
  161.  *  @param packet The packet queue to be released. Input parameter.
  162.  *  @param result The result to be returned. Input parameter.
  163.  *  @returns The result parameter.
  164.  */
  165. int icmp_release_and_return( packet_t packet, int result );
  166.  
  167. /** Requests an echo message.
  168.  *  Sends a packet with specified parameters to the target host and waits for the reply upto the given timeout.
  169.  *  Blocks the caller until the reply or the timeout occurres.
  170.  *  @param id The message identifier. Input parameter.
  171.  *  @param sequence The message sequence parameter. Input parameter.
  172.  *  @param size The message data length in bytes. Input parameter.
  173.  *  @param timeout The timeout in miliseconds. Input parameter.
  174.  *  @param ttl The time to live. Input parameter.
  175.  *  @param tos The type of service. Input parameter.
  176.  *  @param dont_fragment The value indicating whether the datagram must not be fragmented. Is used as a MTU discovery. Input parameter.
  177.  *  @param addr The target host address. Input parameter.
  178.  *  @param addrlen The torget host address length. Input parameter.
  179.  *  @returns ICMP_ECHO on success.
  180.  *  @returns ETIMEOUT if the reply has not arrived before the timeout.
  181.  *  @returns ICMP type of the received error notification.
  182.  *  @returns EINVAL if the addrlen parameter is less or equal to zero (<=0).
  183.  *  @returns ENOMEM if there is not enough memory left.
  184.  *  @returns EPARTY if there was an internal error.
  185.  */
  186. int icmp_echo( icmp_param_t id, icmp_param_t sequence, size_t size, mseconds_t timeout, ip_ttl_t ttl, ip_tos_t tos, int dont_fragment, const struct sockaddr * addr, socklen_t addrlen );
  187.  
  188. /** Prepares the ICMP error packet.
  189.  *  Truncates the original packet if longer than ICMP_KEEP_LENGTH bytes.
  190.  *  Prefixes and returns the ICMP header.
  191.  *  @param packet The original packet. Input/output parameter.
  192.  *  @returns The prefixed ICMP header.
  193.  *  @returns NULL on errors.
  194.  */
  195. icmp_header_ref icmp_prepare_packet( packet_t packet );
  196.  
  197. /** Sends the ICMP message.
  198.  *  Sets the message type and code and computes the checksum.
  199.  *  Error messages are sent only if allowed in the configuration.
  200.  *  Releases the packet on errors.
  201.  *  @returns EOK on success.
  202.  *  @returns EPERM if the error message is not allowed.
  203.  */
  204. int icmp_send_packet( icmp_type_t type, icmp_code_t code, packet_t packet, icmp_header_ref header, services_t error );
  205.  
  206. /** Tries to set the pending reply result as the received message type.
  207.  *  If the reply data are still present, the reply timeouted and the parent fibril is awaken.
  208.  *  The global lock is not released in this case to be reused by the parent fibril.
  209.  *  Releases the packet.
  210.  *  @param packet The received reply message. Input parameter.
  211.  *  @param header The ICMP message header. Input parameter.
  212.  *  @param type The received reply message type. Input parameter.
  213.  *  @param code The received reply message code. Input parameter.
  214.  *  @returns EOK.
  215.  */
  216. int icmp_process_echo_reply( packet_t packet, icmp_header_ref header, icmp_type_t type, icmp_code_t code );
  217.  
  218. /** Tries to set the pending reply result as timeouted.
  219.  *  Sleeps the timeout period of time and then tries to obtain and set the pending reply result as timeouted and signals the reply result.
  220.  *  If the reply data are still present, the reply timeouted and the parent fibril is awaken.
  221.  *  The global lock is not released in this case to be reused by the parent fibril.
  222.  *  Should run in a searate fibril.
  223.  *  @param data The icmp_reply_timeout structure. Input parameter.
  224.  *  @returns EOK on success.
  225.  *  @returns EINVAL if the data parameter is NULL.
  226.  */
  227. int icmp_timeout_for_reply( void * data );
  228.  
  229. /** ICMP reply timeout data.
  230.  *  Used as a timeouting fibril argument.
  231.  *  @see icmp_timeout_for_reply()
  232.  */
  233. struct icmp_reply_timeout{
  234.     /** Reply data key.
  235.      */
  236.     int         reply_key;
  237.     /** Timeout in microseconds.
  238.      */
  239.     suseconds_t timeout;
  240. };
  241.  
  242. /** ICMP global data.
  243.  */
  244. icmp_globals_t  icmp_globals;
  245.  
  246. INT_MAP_IMPLEMENT( icmp_replies, icmp_reply_t );
  247.  
  248. GENERIC_FIELD_IMPLEMENT( icmp_echo_data, icmp_echo_t );
  249.  
  250. int icmp_echo_msg( int icmp_phone, size_t size, mseconds_t timeout, ip_ttl_t ttl, ip_tos_t tos, int dont_fragment, const struct sockaddr * addr, socklen_t addrlen ){
  251.     icmp_echo_ref   echo_data;
  252.     int             res;
  253.  
  254.     fibril_rwlock_write_lock( & icmp_globals.lock );
  255.     // use the phone as the echo data index
  256.     echo_data = icmp_echo_data_get_index( & icmp_globals.echo_data, icmp_phone );
  257.     if( ! echo_data ){
  258.         res = ENOENT;
  259.     }else{
  260.         res = icmp_echo( echo_data->id, echo_data->sequence, size, timeout, ttl, tos, dont_fragment, addr, addrlen );
  261.         ++ echo_data->sequence;
  262.     }
  263.     fibril_rwlock_write_unlock( & icmp_globals.lock );
  264.     return res;
  265. }
  266.  
  267. int icmp_timeout_for_reply( void * data ){
  268.     icmp_reply_ref          reply;
  269.     icmp_reply_timeout_ref  timeout = data;
  270.  
  271.     if( ! timeout ){
  272.         return EINVAL;
  273.     }
  274.     // sleep the given timeout
  275.     async_usleep( timeout->timeout );
  276.     // lock the globals
  277.     fibril_rwlock_write_lock( & icmp_globals.lock );
  278.     // find the pending reply
  279.     reply = icmp_replies_find( & icmp_globals.replies, timeout->reply_key );
  280.     if( reply ){
  281.         // set the timeout result
  282.         reply->result = ETIMEOUT;
  283.         // notify the main fibril
  284.         fibril_condvar_signal( & reply->condvar );
  285.     }else{
  286.         // unlock only if no reply
  287.         fibril_rwlock_write_unlock( & icmp_globals.lock );
  288.     }
  289.     // release the timeout structure
  290.     free( timeout );
  291.     return EOK;
  292. }
  293.  
  294. int icmp_echo( icmp_param_t id, icmp_param_t sequence, size_t size, mseconds_t timeout, ip_ttl_t ttl, ip_tos_t tos, int dont_fragment, const struct sockaddr * addr, socklen_t addrlen ){
  295.     ERROR_DECLARE;
  296.  
  297.     icmp_header_ref header;
  298.     packet_t        packet;
  299.     size_t          length;
  300.     uint8_t *       data;
  301.     icmp_reply_ref          reply;
  302.     icmp_reply_timeout_ref  reply_timeout;
  303.     int             result;
  304.     int             index;
  305.     fid_t           fibril;
  306.  
  307.     if( addrlen <= 0 ){
  308.         return EINVAL;
  309.     }
  310.     length = ( size_t ) addrlen;
  311.     // TODO do not ask all the time
  312.     ERROR_PROPAGATE( ip_packet_size_req( icmp_globals.ip_phone, -1, & icmp_globals.addr_len, & icmp_globals.prefix, & icmp_globals.content, & icmp_globals.suffix ));
  313.     packet = packet_get_4( icmp_globals.net_phone, size, icmp_globals.addr_len, sizeof( icmp_header_t ) + icmp_globals.prefix, icmp_globals.suffix );
  314.     if( ! packet ) return ENOMEM;
  315.  
  316.     // prepare the requesting packet
  317.     // set the destination address
  318.     if( ERROR_OCCURRED( packet_set_addr( packet, NULL, ( const uint8_t * ) addr, length ))){
  319.         return icmp_release_and_return( packet, ERROR_CODE );
  320.     }
  321.     // allocate space in the packet
  322.     data = ( uint8_t * ) packet_suffix( packet, size );
  323.     if( ! data ){
  324.         return icmp_release_and_return( packet, ENOMEM );
  325.     }
  326.     // fill the data
  327.     length = 0;
  328.     while( size > length + sizeof( ICMP_ECHO_TEXT )){
  329.         memcpy( data + length, ICMP_ECHO_TEXT, sizeof( ICMP_ECHO_TEXT ));
  330.         length += sizeof( ICMP_ECHO_TEXT );
  331.     }
  332.     memcpy( data + length, ICMP_ECHO_TEXT, size - length );
  333.     // prefix the header
  334.     header = PACKET_PREFIX( packet, icmp_header_t );
  335.     if( ! header ){
  336.         return icmp_release_and_return( packet, ENOMEM );
  337.     }
  338.     bzero( header, sizeof( * header ));
  339.     header->un.echo.id = id;
  340.     header->un.echo.sequence = sequence;
  341.  
  342.     // prepare the reply and the reply timeout structures
  343.     reply_timeout = malloc( sizeof( * reply_timeout ));
  344.     if( ! reply_timeout ){
  345.         return icmp_release_and_return( packet, ENOMEM );
  346.     }
  347.     reply = malloc( sizeof( * reply ));
  348.     if( ! reply ){
  349.         free( reply_timeout );
  350.         return icmp_release_and_return( packet, ENOMEM );
  351.     }
  352.     reply_timeout->reply_key = ICMP_GET_REPLY_KEY( header->un.echo.id, header->un.echo.sequence );
  353.     // timeout in microseconds
  354.     reply_timeout->timeout = timeout * 1000;
  355.     fibril_mutex_initialize( & reply->mutex );
  356.     fibril_mutex_lock( & reply->mutex );
  357.     fibril_condvar_initialize( & reply->condvar );
  358.     index = icmp_replies_add( & icmp_globals.replies, reply_timeout->reply_key, reply );
  359.     if( index < 0 ){
  360.         free( reply );
  361.         free( reply_timeout );
  362.         return icmp_release_and_return( packet, index );
  363.     }
  364.     // start the timeouting thread
  365.     fibril = fibril_create( icmp_timeout_for_reply, reply_timeout );
  366.     if( ! fibril ){
  367.         return EPARTY;
  368.     }
  369.     fibril_add_ready( fibril );
  370.  
  371.     // unlock the globals and wait for a reply
  372.     fibril_rwlock_write_unlock( & icmp_globals.lock );
  373.  
  374.     // send the request
  375.     icmp_send_packet( ICMP_ECHO, 0, packet, header, 0 );
  376.  
  377.     // wait for a reply
  378.     fibril_condvar_wait( & reply->condvar, & reply->mutex );
  379.     // read the result
  380.     result = reply->result;
  381.  
  382.     // destroy the reply structure
  383.     fibril_mutex_unlock( & reply->mutex );
  384.     icmp_replies_exclude_index( & icmp_globals.replies, index );
  385.     return result;
  386. }
  387.  
  388. int icmp_destination_unreachable_msg( int icmp_phone, icmp_code_t code, icmp_param_t mtu, packet_t packet ){
  389.     icmp_header_ref header;
  390.  
  391.     header = icmp_prepare_packet( packet );
  392.     if( ! header ){
  393.         return icmp_release_and_return( packet, ENOMEM );
  394.     }
  395.     if( mtu ){
  396.         header->un.frag.mtu = mtu;
  397.     }
  398.     return icmp_send_packet( ICMP_DEST_UNREACH, code, packet, header, SERVICE_ICMP );
  399. }
  400.  
  401. int icmp_source_quench_msg( int icmp_phone, packet_t packet ){
  402.     icmp_header_ref header;
  403.  
  404.     header = icmp_prepare_packet( packet );
  405.     if( ! header ){
  406.         return icmp_release_and_return( packet, ENOMEM );
  407.     }
  408.     return icmp_send_packet( ICMP_SOURCE_QUENCH, 0, packet, header, SERVICE_ICMP );
  409. }
  410.  
  411. int icmp_time_exceeded_msg( int icmp_phone, icmp_code_t code, packet_t packet ){
  412.     icmp_header_ref header;
  413.  
  414.     header = icmp_prepare_packet( packet );
  415.     if( ! header ){
  416.         return icmp_release_and_return( packet, ENOMEM );
  417.     }
  418.     return icmp_send_packet( ICMP_TIME_EXCEEDED, code, packet, header, SERVICE_ICMP );
  419. }
  420.  
  421. int icmp_parameter_problem_msg( int icmp_phone, icmp_code_t code, icmp_param_t pointer, packet_t packet ){
  422.     icmp_header_ref header;
  423.  
  424.     header = icmp_prepare_packet( packet );
  425.     if( ! header ){
  426.         return icmp_release_and_return( packet, ENOMEM );
  427.     }
  428.     header->un.param.pointer = pointer;
  429.     return icmp_send_packet( ICMP_PARAMETERPROB, code, packet, header, SERVICE_ICMP );
  430. }
  431.  
  432. icmp_header_ref icmp_prepare_packet( packet_t packet ){
  433.     icmp_header_ref header;
  434.     int             header_length;
  435.     size_t          total_length;
  436.  
  437.     total_length = packet_get_data_length( packet );
  438.     if( total_length <= 0 ) return NULL;
  439.     header_length = ip_client_header_length( packet );
  440.     if( header_length <= 0 ) return NULL;
  441.     // truncate if longer than 64 bits (without the IP header)
  442.     if(( total_length - header_length > ICMP_KEEP_LENGTH )
  443.     && ( packet_trim( packet, 0, total_length - header_length - ICMP_KEEP_LENGTH ) != EOK )){
  444.         return NULL;
  445.     }
  446.     header = PACKET_PREFIX( packet, icmp_header_t );
  447.     if( ! header ) return NULL;
  448.     bzero( header, sizeof( * header ));
  449.     return header;
  450. }
  451.  
  452. int icmp_send_packet( icmp_type_t type, icmp_code_t code, packet_t packet, icmp_header_ref header, services_t error ){
  453.     ERROR_DECLARE;
  454.  
  455.     // do not send an error if disabled
  456.     if( error && ( ! icmp_globals.error_reporting )){
  457.         return icmp_release_and_return( packet, EPERM );
  458.     }
  459.     header->type = type;
  460.     header->code = code;
  461.     header->checksum = 0;
  462.     header->checksum = ICMP_CHECKSUM( header, packet_get_data_length( packet ));
  463.     if( ERROR_OCCURRED( ip_client_prepare_packet( packet, IPPROTO_ICMP, 0, 0, 0, 0 ))){
  464.         return icmp_release_and_return( packet, ERROR_CODE );
  465.     }
  466.     return ip_send_msg( icmp_globals.ip_phone, -1, packet, SERVICE_ICMP, error );
  467. }
  468.  
  469. int icmp_connect_module( services_t service ){
  470.     icmp_echo_ref   echo_data;
  471.     int             index;
  472.  
  473.     echo_data = ( icmp_echo_ref ) malloc( sizeof( * echo_data ));
  474.     if( ! echo_data ) return ENOMEM;
  475.     // assign a new identifier
  476.     fibril_rwlock_write_lock( & icmp_globals.lock );
  477.     ++ icmp_globals.last_used_id;
  478.     echo_data->id = icmp_globals.last_used_id;
  479.     echo_data->sequence = 0;
  480.     // remember the assigned echo data
  481.     index = icmp_echo_data_add( & icmp_globals.echo_data, echo_data );
  482.     if( index < 0 ){
  483.         free( echo_data );
  484.     }
  485.     fibril_rwlock_write_unlock( & icmp_globals.lock );
  486.     // return the echo data index as the ICMP phone
  487.     return index;
  488. }
  489.  
  490. int icmp_initialize( async_client_conn_t client_connection ){
  491.     ERROR_DECLARE;
  492.  
  493.     measured_string_t   names[] = {{ "ICMP_ERROR_REPORTING", 20 }, { "ICMP_ECHO_REPLYING", 18 }};
  494.     measured_string_ref configuration;
  495.     size_t              count = sizeof( names ) / sizeof( measured_string_t );
  496.     char *              data;
  497.  
  498.     fibril_rwlock_initialize( & icmp_globals.lock );
  499.     fibril_rwlock_write_lock( & icmp_globals.lock );
  500.     icmp_replies_initialize( & icmp_globals.replies );
  501.     icmp_echo_data_initialize( & icmp_globals.echo_data );
  502.     icmp_globals.ip_phone = ip_bind_service( SERVICE_IP, IPPROTO_ICMP, SERVICE_ICMP, client_connection, icmp_received_msg );
  503.     if( icmp_globals.ip_phone < 0 ){
  504.         return icmp_globals.ip_phone;
  505.     }
  506.     ERROR_PROPAGATE( ip_packet_size_req( icmp_globals.ip_phone, -1, & icmp_globals.addr_len, & icmp_globals.prefix, & icmp_globals.content, & icmp_globals.suffix ));
  507.     icmp_globals.prefix += sizeof( icmp_header_t );
  508.     icmp_globals.content -= sizeof( icmp_header_t );
  509.     configuration = & names[ 0 ];
  510.     // get configuration
  511.     ERROR_PROPAGATE( net_get_conf_req( icmp_globals.net_phone, & configuration, count, & data ));
  512.     if( configuration ){
  513.         icmp_globals.error_reporting = configuration[ 0 ].value && ( configuration[ 0 ].value[ 0 ] == 'y' );
  514.         icmp_globals.echo_replying = configuration[ 1 ].value && ( configuration[ 1 ].value[ 0 ] == 'y' );
  515.         net_free_settings( configuration, data );
  516.     }
  517.     fibril_rwlock_write_unlock( & icmp_globals.lock );
  518.     return EOK;
  519. }
  520.  
  521. int icmp_received_msg( device_id_t device_id, packet_t packet, services_t receiver, services_t error ){
  522.     ERROR_DECLARE;
  523.  
  524.     if( ERROR_OCCURRED( icmp_process_packet( packet, error ))){
  525.         return icmp_release_and_return( packet, ERROR_CODE );
  526.     }
  527.  
  528.     return EOK;
  529. }
  530.  
  531. int icmp_process_packet( packet_t packet, services_t error ){
  532.     ERROR_DECLARE;
  533.  
  534.     size_t          length;
  535.     uint8_t *       src;
  536.     int             addrlen;
  537.     int             result;
  538.     void *          data;
  539.     icmp_header_ref header;
  540.     icmp_type_t     type;
  541.     icmp_code_t     code;
  542.  
  543.     if( error ){
  544.         switch( error ){
  545.             case SERVICE_ICMP:
  546.                 // process error
  547.                 // TODO remove debug dump
  548.                 // length = icmp_client_header_length( packet );
  549.                 result = icmp_client_process_packet( packet, & type, & code, NULL, NULL );
  550.                 if( result < 0 ) return result;
  551.                 length = ( size_t ) result;
  552.                 printf( "ICMP error %d (%d) in packet %d\n", type, code, packet_get_id( packet ) );
  553.                 ERROR_PROPAGATE( packet_trim( packet, length, 0 ));
  554.                 break;
  555.             default:
  556.                 return ENOTSUP;
  557.         }
  558.     }
  559.     // get rid of the ip header
  560.     result = ip_client_process_packet( packet, NULL, NULL, NULL, NULL, NULL );
  561.     if( result < 0 ) return result;
  562.     ERROR_PROPAGATE( packet_trim( packet, ( size_t ) result, 0 ));
  563.  
  564.     length = packet_get_data_length( packet );
  565.     if( length <= 0 ) return EINVAL;
  566.     if( length < sizeof( icmp_header_t )) return EINVAL;
  567.     data = packet_get_data( packet );
  568.     if( ! data ) return EINVAL;
  569.     // get icmp header
  570.     header = ( icmp_header_ref ) data;
  571.     // checksum
  572. /*  if(( header->checksum ) && ( ICMP_CHECKSUM( header, length ))){
  573.         // set the original message type on error notification
  574.         // type swap observed in Qemu
  575.         if( error ){
  576.             switch( header->type ){
  577.                 case ICMP_ECHOREPLY:
  578.                     header->type = ICMP_ECHO;
  579.                     break;
  580.             }
  581.         }
  582.         if( ICMP_CHECKSUM( header, length )){
  583.             return EINVAL;
  584.         }
  585.     }
  586. */  if( header->checksum ){
  587.         while( ICMP_CHECKSUM( header, length )){
  588.             // set the original message type on error notification
  589.             // type swap observed in Qemu
  590.             if( error ){
  591.                 switch( header->type ){
  592.                     case ICMP_ECHOREPLY:
  593.                         header->type = ICMP_ECHO;
  594.                         continue;
  595.                 }
  596.             }
  597.             return EINVAL;
  598.         }
  599.     }
  600.     switch( header->type ){
  601.         case ICMP_ECHOREPLY:
  602.             if( error ){
  603.                 return icmp_process_echo_reply( packet, header, type, code );
  604.             }else{
  605.                 return icmp_process_echo_reply( packet, header, ICMP_ECHO, 0 );
  606.             }
  607.         case ICMP_ECHO:
  608.             if( error ){
  609.                 return icmp_process_echo_reply( packet, header, type, code );
  610.             // do not send a reply if disabled
  611.             }else if( icmp_globals.echo_replying ){
  612.                 addrlen = packet_get_addr( packet, & src, NULL );
  613.                 if(( addrlen > 0 )
  614.                 // set both addresses to the source one (avoids the source address deletion before setting the destination one)
  615.                 && ( packet_set_addr( packet, src, src, ( size_t ) addrlen ) == EOK )){
  616.                     // send the reply
  617.                     icmp_send_packet( ICMP_ECHOREPLY, 0, packet, header, 0 );
  618.                     return EOK;
  619.                 }else{
  620.                     return EINVAL;
  621.                 }
  622.             }else{
  623.                 return EPERM;
  624.             }
  625.         case ICMP_DEST_UNREACH:
  626.         case ICMP_SOURCE_QUENCH:
  627.         case ICMP_REDIRECT:
  628.         case ICMP_ALTERNATE_ADDR:
  629.         case ICMP_ROUTER_ADV:
  630.         case ICMP_ROUTER_SOL:
  631.         case ICMP_TIME_EXCEEDED:
  632.         case ICMP_PARAMETERPROB:
  633.         case ICMP_CONVERSION_ERROR:
  634.         case ICMP_REDIRECT_MOBILE:
  635.         case ICMP_SKIP:
  636.         case ICMP_PHOTURIS:
  637.             ip_received_error_msg( icmp_globals.ip_phone, -1, packet, SERVICE_IP, SERVICE_ICMP );
  638.             return EOK;
  639.         default:
  640.             return ENOTSUP;
  641.     }
  642. }
  643.  
  644. int icmp_process_echo_reply( packet_t packet, icmp_header_ref header, icmp_type_t type, icmp_code_t code ){
  645.     int             reply_key;
  646.     icmp_reply_ref  reply;
  647.  
  648.     // compute the reply key
  649.     reply_key = ICMP_GET_REPLY_KEY( header->un.echo.id, header->un.echo.sequence );
  650.     pq_release( icmp_globals.net_phone, packet_get_id( packet ));
  651.     // lock the globals
  652.     fibril_rwlock_write_lock( & icmp_globals.lock );
  653.     // find the pending reply
  654.     reply = icmp_replies_find( & icmp_globals.replies, reply_key );
  655.     if( reply ){
  656.         // set the result
  657.         reply->result = type;
  658.         // notify the main fibril
  659.         fibril_condvar_signal( & reply->condvar );
  660.     }else{
  661.         // unlock only if no reply
  662.         fibril_rwlock_write_unlock( & icmp_globals.lock );
  663.     }
  664.     return EOK;
  665. }
  666.  
  667. int icmp_message( ipc_callid_t callid, ipc_call_t * call, ipc_call_t * answer, int * answer_count ){
  668.     ERROR_DECLARE;
  669.  
  670.     packet_t            packet;
  671.  
  672.     * answer_count = 0;
  673.     switch( IPC_GET_METHOD( * call )){
  674.         case NET_TL_RECEIVED:
  675.             if( ! ERROR_OCCURRED( packet_translate( icmp_globals.net_phone, & packet, IPC_GET_PACKET( call )))){
  676.                 ERROR_CODE = icmp_received_msg( IPC_GET_DEVICE( call ), packet, SERVICE_ICMP, IPC_GET_ERROR( call ));
  677.             }
  678.             return ERROR_CODE;
  679.         case NET_ICMP_INIT:
  680.             return icmp_process_client_messages( callid, * call );
  681.         default:
  682.             return icmp_process_message( call );
  683.     }
  684.     return ENOTSUP;
  685. }
  686.  
  687. int icmp_process_client_messages( ipc_callid_t callid, ipc_call_t call ){
  688.     ERROR_DECLARE;
  689.  
  690.     bool                    keep_on_going = true;
  691.     fibril_rwlock_t         lock;
  692.     ipc_call_t              answer;
  693.     int                     answer_count;
  694.     size_t                  length;
  695.     struct sockaddr *       addr;
  696.     icmp_param_t            id;
  697.     icmp_param_t            sequence = 0;
  698.     ipc_callid_t            data_callid;
  699.  
  700.     /*
  701.      * Accept the connection
  702.      *  - Answer the first NET_ICMP_INIT call.
  703.      */
  704.     ipc_answer_0( callid, EOK );
  705.  
  706.     fibril_rwlock_initialize( & lock );
  707.  
  708.     // assign a new identifier
  709.     fibril_rwlock_write_lock( & icmp_globals.lock );
  710.     ++ icmp_globals.last_used_id;
  711.     id = icmp_globals.last_used_id;
  712.     fibril_rwlock_write_unlock( & icmp_globals.lock );
  713.  
  714.     while( keep_on_going ){
  715.         refresh_answer( & answer, & answer_count );
  716.  
  717.         callid = async_get_call( & call );
  718.  
  719.         switch( IPC_GET_METHOD( call )){
  720.             case IPC_M_PHONE_HUNGUP:
  721.                 keep_on_going = false;
  722.                 ERROR_CODE = EOK;
  723.                 break;
  724.             case NET_ICMP_ECHO:
  725.                 fibril_rwlock_write_lock( & lock );
  726.                 if( ! ipc_data_write_receive( & data_callid, & length )){
  727.                     ERROR_CODE = EINVAL;
  728.                 }else{
  729.                     addr = malloc( length );
  730.                     if( ! addr ){
  731.                         ERROR_CODE = ENOMEM;
  732.                     }else{
  733.                         if( ! ERROR_OCCURRED( ipc_data_write_finalize( data_callid, addr, length ))){
  734.                             fibril_rwlock_write_lock( & icmp_globals.lock );
  735.                             ERROR_CODE = icmp_echo( id, sequence, ICMP_GET_SIZE( call ), ICMP_GET_TIMEOUT( call ), ICMP_GET_TTL( call ), ICMP_GET_TOS( call ), ICMP_GET_DONT_FRAGMENT( call ), addr, ( socklen_t ) length );
  736.                             fibril_rwlock_write_unlock( & icmp_globals.lock );
  737.                             free( addr );
  738.                             ++ sequence;
  739.                         }
  740.                     }
  741.                 }
  742.                 fibril_rwlock_write_unlock( & lock );
  743.                 break;
  744.             default:
  745.                 ERROR_CODE = icmp_process_message( & call );
  746.         }
  747.  
  748.         answer_call( callid, ERROR_CODE, & answer, answer_count );
  749.     }
  750.  
  751.     return EOK;
  752. }
  753.  
  754. int icmp_process_message( ipc_call_t * call ){
  755.     ERROR_DECLARE;
  756.  
  757.     packet_t    packet;
  758.  
  759.     switch( IPC_GET_METHOD( * call )){
  760.         case NET_ICMP_DEST_UNREACH:
  761.             if( ! ERROR_OCCURRED( packet_translate( icmp_globals.net_phone, & packet, IPC_GET_PACKET( call )))){
  762.                 ERROR_CODE = icmp_destination_unreachable_msg( 0, ICMP_GET_CODE( call ), ICMP_GET_MTU( call ), packet );
  763.             }
  764.             return ERROR_CODE;
  765.         case NET_ICMP_SOURCE_QUENCH:
  766.             if( ! ERROR_OCCURRED( packet_translate( icmp_globals.net_phone, & packet, IPC_GET_PACKET( call )))){
  767.                 ERROR_CODE = icmp_source_quench_msg( 0, packet );
  768.             }
  769.             return ERROR_CODE;
  770.         case NET_ICMP_TIME_EXCEEDED:
  771.             if( ! ERROR_OCCURRED( packet_translate( icmp_globals.net_phone, & packet, IPC_GET_PACKET( call )))){
  772.                 ERROR_CODE = icmp_time_exceeded_msg( 0, ICMP_GET_CODE( call ), packet );
  773.             }
  774.             return ERROR_CODE;
  775.         case NET_ICMP_PARAMETERPROB:
  776.             if( ! ERROR_OCCURRED( packet_translate( icmp_globals.net_phone, & packet, IPC_GET_PACKET( call )))){
  777.                 ERROR_CODE = icmp_parameter_problem_msg( 0, ICMP_GET_CODE( call ), ICMP_GET_POINTER( call ), packet );
  778.             }
  779.             return ERROR_CODE;
  780.         default:
  781.             return ENOTSUP;
  782.     }
  783. }
  784.  
  785. int icmp_release_and_return( packet_t packet, int result ){
  786.     pq_release( icmp_globals.net_phone, packet_get_id( packet ));
  787.     return result;
  788. }
  789.  
  790. /** @}
  791.  */
  792.