Subversion Repositories HelenOS

Rev

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