Subversion Repositories HelenOS

Rev

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