Subversion Repositories HelenOS

Rev

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