Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/srv/net/tl/icmp/icmp.c

    rffa2c8ef r8e3a65c  
    2828
    2929/** @addtogroup icmp
    30  * @{
     30 *  @{
    3131 */
    3232
    3333/** @file
    34  * ICMP module implementation.
     34 *  ICMP module implementation.
     35 *  @see icmp.h
    3536 */
    3637
     
    4142#include <stdint.h>
    4243#include <str.h>
     44#include <ipc/ipc.h>
    4345#include <ipc/services.h>
    4446#include <ipc/net.h>
     
    4951#include <byteorder.h>
    5052#include <errno.h>
    51 #include <adt/hash_table.h>
     53#include <err.h>
    5254
    5355#include <net/socket_codes.h>
    5456#include <net/ip_protocols.h>
    5557#include <net/inet.h>
     58
    5659#include <net/modules.h>
    57 #include <net/icmp_api.h>
    58 #include <net/icmp_codes.h>
    59 #include <net/icmp_common.h>
    60 
    6160#include <packet_client.h>
    6261#include <packet_remote.h>
    6362#include <net_checksum.h>
     63#include <net/icmp_api.h>
    6464#include <icmp_client.h>
    65 #include <icmp_remote.h>
    66 #include <il_remote.h>
     65#include <net/icmp_codes.h>
     66#include <net/icmp_common.h>
     67#include <icmp_interface.h>
     68#include <il_interface.h>
    6769#include <ip_client.h>
    6870#include <ip_interface.h>
    6971#include <net_interface.h>
    70 #include <tl_remote.h>
    71 #include <tl_skel.h>
     72#include <tl_interface.h>
     73#include <tl_local.h>
    7274#include <icmp_header.h>
    7375
    74 /** ICMP module name */
    75 #define NAME  "icmp"
    76 
    77 /** Number of replies hash table keys */
    78 #define REPLY_KEYS  2
    79 
    80 /** Number of replies hash table buckets */
    81 #define REPLY_BUCKETS  1024
    82 
    83 /**
    84  * Original datagram length in bytes transfered to the error
    85  * notification message.
    86  */
    87 #define ICMP_KEEP_LENGTH  8
    88 
    89 /** Compute the ICMP datagram checksum.
     76#include "icmp.h"
     77#include "icmp_module.h"
     78
     79/** ICMP module name.
     80 */
     81#define NAME    "ICMP protocol"
     82
     83/** Default ICMP error reporting.
     84 */
     85#define NET_DEFAULT_ICMP_ERROR_REPORTING        true
     86
     87/** Default ICMP echo replying.
     88 */
     89#define NET_DEFAULT_ICMP_ECHO_REPLYING          true
     90
     91/** Original datagram length in bytes transfered to the error notification message.
     92 */
     93#define ICMP_KEEP_LENGTH        8
     94
     95/** Free identifier numbers pool start.
     96 */
     97#define ICMP_FREE_IDS_START     1
     98
     99/** Free identifier numbers pool end.
     100 */
     101#define ICMP_FREE_IDS_END       UINT16_MAX
     102
     103/** Computes the ICMP datagram checksum.
     104 *  @param[in,out] header The ICMP datagram header.
     105 *  @param[in] length The total datagram length.
     106 *  @returns The computed checksum.
     107 */
     108#define ICMP_CHECKSUM(header, length)           htons(ip_checksum((uint8_t *) (header), (length)))
     109
     110/** An echo request datagrams pattern.
     111 */
     112#define ICMP_ECHO_TEXT                                  "Hello from HelenOS."
     113
     114/** Computes an ICMP reply data key.
     115 *  @param[in] id The message identifier.
     116 *  @param[in] sequence The message sequence number.
     117 *  @returns The computed ICMP reply data key.
     118 */
     119#define ICMP_GET_REPLY_KEY(id, sequence)        (((id) << 16) | (sequence &0xFFFF))
     120
     121/** Processes the received ICMP packet.
     122 *  Is used as an entry point from the underlying IP module.
     123 *  Releases the packet on error.
     124 *  @param device_id The device identifier. Ignored parameter.
     125 *  @param[in,out] packet The received packet.
     126 *  @param receiver The target service. Ignored parameter.
     127 *  @param[in] error The packet error reporting service. Prefixes the received packet.
     128 *  @returns EOK on success.
     129 *  @returns Other error codes as defined for the icmp_process_packet() function.
     130 */
     131int icmp_received_msg(device_id_t device_id, packet_t packet, services_t receiver, services_t error);
     132
     133/** Processes the received ICMP packet.
     134 *  Notifies the destination socket application.
     135 *  @param[in,out] packet The received packet.
     136 *  @param[in] error The packet error reporting service. Prefixes the received packet.
     137 *  @returns EOK on success.
     138 *  @returns EINVAL if the packet is not valid.
     139 *  @returns EINVAL if the stored packet address is not the an_addr_t.
     140 *  @returns EINVAL if the packet does not contain any data.
     141 *  @returns NO_DATA if the packet content is shorter than the user datagram header.
     142 *  @returns ENOMEM if there is not enough memory left.
     143 *  @returns EADDRNOTAVAIL if the destination socket does not exist.
     144 *  @returns Other error codes as defined for the ip_client_process_packet() function.
     145 */
     146int icmp_process_packet(packet_t packet, services_t error);
     147
     148/** Processes the client messages.
     149 *  Remembers the assigned identifier and sequence numbers.
     150 *  Runs until the client module disconnects.
     151 *  @param[in] callid The message identifier.
     152 *  @param[in] call The message parameters.
     153 *  @returns EOK.
     154 *  @see icmp_interface.h
     155 *  @see icmp_api.h
     156 */
     157int icmp_process_client_messages(ipc_callid_t callid, ipc_call_t call);
     158
     159/** Processes the generic client messages.
     160 *  @param[in] call The message parameters.
     161 *  @returns EOK on success.
     162 *  @returns ENOTSUP if the message is not known.
     163 *  @returns Other error codes as defined for the packet_translate() function.
     164 *  @returns Other error codes as defined for the icmp_destination_unreachable_msg() function.
     165 *  @returns Other error codes as defined for the icmp_source_quench_msg() function.
     166 *  @returns Other error codes as defined for the icmp_time_exceeded_msg() function.
     167 *  @returns Other error codes as defined for the icmp_parameter_problem_msg() function.
     168 *  @see icmp_interface.h
     169 */
     170int icmp_process_message(ipc_call_t * call);
     171
     172/** Releases the packet and returns the result.
     173 *  @param[in] packet The packet queue to be released.
     174 *  @param[in] result The result to be returned.
     175 *  @returns The result parameter.
     176 */
     177int icmp_release_and_return(packet_t packet, int result);
     178
     179/** Requests an echo message.
     180 *  Sends a packet with specified parameters to the target host and waits for the reply upto the given timeout.
     181 *  Blocks the caller until the reply or the timeout occurs.
     182 *  @param[in] id The message identifier.
     183 *  @param[in] sequence The message sequence parameter.
     184 *  @param[in] size The message data length in bytes.
     185 *  @param[in] timeout The timeout in miliseconds.
     186 *  @param[in] ttl The time to live.
     187 *  @param[in] tos The type of service.
     188 *  @param[in] dont_fragment The value indicating whether the datagram must not be fragmented. Is used as a MTU discovery.
     189 *  @param[in] addr The target host address.
     190 *  @param[in] addrlen The torget host address length.
     191 *  @returns ICMP_ECHO on success.
     192 *  @returns ETIMEOUT if the reply has not arrived before the timeout.
     193 *  @returns ICMP type of the received error notification.
     194 *  @returns EINVAL if the addrlen parameter is less or equal to zero (<=0).
     195 *  @returns ENOMEM if there is not enough memory left.
     196 *  @returns EPARTY if there was an internal error.
     197 */
     198int 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);
     199
     200/** Prepares the ICMP error packet.
     201 *  Truncates the original packet if longer than ICMP_KEEP_LENGTH bytes.
     202 *  Prefixes and returns the ICMP header.
     203 *  @param[in,out] packet The original packet.
     204 *  @returns The prefixed ICMP header.
     205 *  @returns NULL on errors.
     206 */
     207icmp_header_ref icmp_prepare_packet(packet_t packet);
     208
     209/** Sends the ICMP message.
     210 *  Sets the message type and code and computes the checksum.
     211 *  Error messages are sent only if allowed in the configuration.
     212 *  Releases the packet on errors.
     213 *  @param[in] type The message type.
     214 *  @param[in] code The message code.
     215 *  @param[in] packet The message packet to be sent.
     216 *  @param[in] header The ICMP header.
     217 *  @param[in] error The error service to be announced. Should be SERVICE_ICMP or zero (0).
     218 *  @param[in] ttl The time to live.
     219 *  @param[in] tos The type of service.
     220 *  @param[in] dont_fragment The value indicating whether the datagram must not be fragmented. Is used as a MTU discovery.
     221 *  @returns EOK on success.
     222 *  @returns EPERM if the error message is not allowed.
     223 */
     224int 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);
     225
     226/** Tries to set the pending reply result as the received message type.
     227 *  If the reply data is not present, the reply timed out and the other fibril
     228 *  is already awake.
     229 *  Releases the packet.
     230 *  @param[in] packet The received reply message.
     231 *  @param[in] header The ICMP message header.
     232 *  @param[in] type The received reply message type.
     233 *  @param[in] code The received reply message code.
     234 *  @returns EOK.
     235 */
     236int icmp_process_echo_reply(packet_t packet, icmp_header_ref header, icmp_type_t type, icmp_code_t code);
     237
     238/** Assigns a new identifier for the connection.
     239 *  Fills the echo data parameter with the assigned values.
     240 *  @param[in,out] echo_data The echo data to be bound.
     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 */
     245int icmp_bind_free_id(icmp_echo_ref echo_data);
     246
     247/** ICMP global data.
     248 */
     249icmp_globals_t  icmp_globals;
     250
     251INT_MAP_IMPLEMENT(icmp_replies, icmp_reply_t);
     252
     253INT_MAP_IMPLEMENT(icmp_echo_data, icmp_echo_t);
     254
     255int 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){
     256        icmp_echo_ref echo_data;
     257        int res;
     258
     259        fibril_rwlock_write_lock(&icmp_globals.lock);
     260        // use the phone as the echo data index
     261        echo_data = icmp_echo_data_find(&icmp_globals.echo_data, icmp_phone);
     262        if(! echo_data){
     263                res = ENOENT;
     264        }else{
     265                res = icmp_echo(echo_data->identifier, echo_data->sequence_number, size, timeout, ttl, tos, dont_fragment, addr, addrlen);
     266                if(echo_data->sequence_number < UINT16_MAX){
     267                        ++ echo_data->sequence_number;
     268                }else{
     269                        echo_data->sequence_number = 0;
     270                }
     271        }
     272        fibril_rwlock_write_unlock(&icmp_globals.lock);
     273        return res;
     274}
     275
     276int 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){
     277        ERROR_DECLARE;
     278
     279        icmp_header_ref header;
     280        packet_t packet;
     281        size_t length;
     282        uint8_t * data;
     283        icmp_reply_ref reply;
     284        int reply_key;
     285        int result;
     286        int index;
     287
     288        if(addrlen <= 0){
     289                return EINVAL;
     290        }
     291        length = (size_t) addrlen;
     292        // TODO do not ask all the time
     293        ERROR_PROPAGATE(ip_packet_size_req(icmp_globals.ip_phone, -1, &icmp_globals.packet_dimension));
     294        packet = packet_get_4_remote(icmp_globals.net_phone, size, icmp_globals.packet_dimension.addr_len, ICMP_HEADER_SIZE + icmp_globals.packet_dimension.prefix, icmp_globals.packet_dimension.suffix);
     295        if(! packet){
     296                return ENOMEM;
     297        }
     298
     299        // prepare the requesting packet
     300        // set the destination address
     301        if(ERROR_OCCURRED(packet_set_addr(packet, NULL, (const uint8_t *) addr, length))){
     302                return icmp_release_and_return(packet, ERROR_CODE);
     303        }
     304        // allocate space in the packet
     305        data = (uint8_t *) packet_suffix(packet, size);
     306        if(! data){
     307                return icmp_release_and_return(packet, ENOMEM);
     308        }
     309        // fill the data
     310        length = 0;
     311        while(size > length + sizeof(ICMP_ECHO_TEXT)){
     312                memcpy(data + length, ICMP_ECHO_TEXT, sizeof(ICMP_ECHO_TEXT));
     313                length += sizeof(ICMP_ECHO_TEXT);
     314        }
     315        memcpy(data + length, ICMP_ECHO_TEXT, size - length);
     316        // prefix the header
     317        header = PACKET_PREFIX(packet, icmp_header_t);
     318        if(! header){
     319                return icmp_release_and_return(packet, ENOMEM);
     320        }
     321        bzero(header, sizeof(*header));
     322        header->un.echo.identifier = id;
     323        header->un.echo.sequence_number = sequence;
     324
     325        // prepare the reply structure
     326        reply = malloc(sizeof(*reply));
     327        if(! reply){
     328                return icmp_release_and_return(packet, ENOMEM);
     329        }
     330        fibril_mutex_initialize(&reply->mutex);
     331        fibril_mutex_lock(&reply->mutex);
     332        fibril_condvar_initialize(&reply->condvar);
     333        reply_key = ICMP_GET_REPLY_KEY(header->un.echo.identifier, header->un.echo.sequence_number);
     334        index = icmp_replies_add(&icmp_globals.replies, reply_key, reply);
     335        if(index < 0){
     336                free(reply);
     337                return icmp_release_and_return(packet, index);
     338        }
     339
     340        // unlock the globals so that we can wait for the reply
     341        fibril_rwlock_write_unlock(&icmp_globals.lock);
     342
     343        // send the request
     344        icmp_send_packet(ICMP_ECHO, 0, packet, header, 0, ttl, tos, dont_fragment);
     345
     346        // wait for the reply
     347        // timeout in microseconds
     348        if(ERROR_OCCURRED(fibril_condvar_wait_timeout(&reply->condvar, &reply->mutex, timeout * 1000))){
     349                result = ERROR_CODE;
     350        }else{
     351                // read the result
     352                result = reply->result;
     353        }
     354
     355        // drop the reply mutex before locking the globals again
     356        fibril_mutex_unlock(&reply->mutex);
     357        fibril_rwlock_write_lock(&icmp_globals.lock);
     358
     359        // destroy the reply structure
     360        icmp_replies_exclude_index(&icmp_globals.replies, index);
     361        return result;
     362}
     363
     364int icmp_destination_unreachable_msg(int icmp_phone, icmp_code_t code, icmp_param_t mtu, packet_t packet){
     365        icmp_header_ref header;
     366
     367        header = icmp_prepare_packet(packet);
     368        if(! header){
     369                return icmp_release_and_return(packet, ENOMEM);
     370        }
     371        if(mtu){
     372                header->un.frag.mtu = mtu;
     373        }
     374        return icmp_send_packet(ICMP_DEST_UNREACH, code, packet, header, SERVICE_ICMP, 0, 0, 0);
     375}
     376
     377int icmp_source_quench_msg(int icmp_phone, packet_t packet){
     378        icmp_header_ref header;
     379
     380        header = icmp_prepare_packet(packet);
     381        if(! header){
     382                return icmp_release_and_return(packet, ENOMEM);
     383        }
     384        return icmp_send_packet(ICMP_SOURCE_QUENCH, 0, packet, header, SERVICE_ICMP, 0, 0, 0);
     385}
     386
     387int icmp_time_exceeded_msg(int icmp_phone, icmp_code_t code, packet_t packet){
     388        icmp_header_ref header;
     389
     390        header = icmp_prepare_packet(packet);
     391        if(! header){
     392                return icmp_release_and_return(packet, ENOMEM);
     393        }
     394        return icmp_send_packet(ICMP_TIME_EXCEEDED, code, packet, header, SERVICE_ICMP, 0, 0, 0);
     395}
     396
     397int icmp_parameter_problem_msg(int icmp_phone, icmp_code_t code, icmp_param_t pointer, packet_t packet){
     398        icmp_header_ref header;
     399
     400        header = icmp_prepare_packet(packet);
     401        if(! header){
     402                return icmp_release_and_return(packet, ENOMEM);
     403        }
     404        header->un.param.pointer = pointer;
     405        return icmp_send_packet(ICMP_PARAMETERPROB, code, packet, header, SERVICE_ICMP, 0, 0, 0);
     406}
     407
     408icmp_header_ref icmp_prepare_packet(packet_t packet){
     409        icmp_header_ref header;
     410        size_t header_length;
     411        size_t total_length;
     412
     413        total_length = packet_get_data_length(packet);
     414        if(total_length <= 0){
     415                return NULL;
     416        }
     417        header_length = ip_client_header_length(packet);
     418        if(header_length <= 0){
     419                return NULL;
     420        }
     421        // truncate if longer than 64 bits (without the IP header)
     422        if((total_length > header_length + ICMP_KEEP_LENGTH)
     423                && (packet_trim(packet, 0, total_length - header_length - ICMP_KEEP_LENGTH) != EOK)){
     424                return NULL;
     425        }
     426        header = PACKET_PREFIX(packet, icmp_header_t);
     427        if(! header){
     428                return NULL;
     429        }
     430        bzero(header, sizeof(*header));
     431        return header;
     432}
     433
     434int 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){
     435        ERROR_DECLARE;
     436
     437        // do not send an error if disabled
     438        if(error && (! icmp_globals.error_reporting)){
     439                return icmp_release_and_return(packet, EPERM);
     440        }
     441        header->type = type;
     442        header->code = code;
     443        header->checksum = 0;
     444        header->checksum = ICMP_CHECKSUM(header, packet_get_data_length(packet));
     445        if(ERROR_OCCURRED(ip_client_prepare_packet(packet, IPPROTO_ICMP, ttl, tos, dont_fragment, 0))){
     446                return icmp_release_and_return(packet, ERROR_CODE);
     447        }
     448        return ip_send_msg(icmp_globals.ip_phone, -1, packet, SERVICE_ICMP, error);
     449}
     450
     451int icmp_initialize(async_client_conn_t client_connection){
     452        ERROR_DECLARE;
     453
     454        measured_string_t names[] = {{str_dup("ICMP_ERROR_REPORTING"), 20}, {str_dup("ICMP_ECHO_REPLYING"), 18}};
     455        measured_string_ref configuration;
     456        size_t count = sizeof(names) / sizeof(measured_string_t);
     457        char * data;
     458
     459        fibril_rwlock_initialize(&icmp_globals.lock);
     460        fibril_rwlock_write_lock(&icmp_globals.lock);
     461        icmp_replies_initialize(&icmp_globals.replies);
     462        icmp_echo_data_initialize(&icmp_globals.echo_data);
     463        icmp_globals.ip_phone = ip_bind_service(SERVICE_IP, IPPROTO_ICMP, SERVICE_ICMP, client_connection);
     464        if(icmp_globals.ip_phone < 0){
     465                return icmp_globals.ip_phone;
     466        }
     467        ERROR_PROPAGATE(ip_packet_size_req(icmp_globals.ip_phone, -1, &icmp_globals.packet_dimension));
     468        icmp_globals.packet_dimension.prefix += ICMP_HEADER_SIZE;
     469        icmp_globals.packet_dimension.content -= ICMP_HEADER_SIZE;
     470        // get configuration
     471        icmp_globals.error_reporting = NET_DEFAULT_ICMP_ERROR_REPORTING;
     472        icmp_globals.echo_replying = NET_DEFAULT_ICMP_ECHO_REPLYING;
     473        configuration = &names[0];
     474        ERROR_PROPAGATE(net_get_conf_req(icmp_globals.net_phone, &configuration, count, &data));
     475        if(configuration){
     476                if(configuration[0].value){
     477                        icmp_globals.error_reporting = (configuration[0].value[0] == 'y');
     478                }
     479                if(configuration[1].value){
     480                        icmp_globals.echo_replying = (configuration[1].value[0] == 'y');
     481                }
     482                net_free_settings(configuration, data);
     483        }
     484        fibril_rwlock_write_unlock(&icmp_globals.lock);
     485        return EOK;
     486}
     487
     488int icmp_received_msg(device_id_t device_id, packet_t packet, services_t receiver, services_t error){
     489        ERROR_DECLARE;
     490
     491        if(ERROR_OCCURRED(icmp_process_packet(packet, error))){
     492                return icmp_release_and_return(packet, ERROR_CODE);
     493        }
     494
     495        return EOK;
     496}
     497
     498int icmp_process_packet(packet_t packet, services_t error){
     499        ERROR_DECLARE;
     500
     501        size_t length;
     502        uint8_t * src;
     503        int addrlen;
     504        int result;
     505        void * data;
     506        icmp_header_ref header;
     507        icmp_type_t type;
     508        icmp_code_t code;
     509
     510        if(error){
     511                switch(error){
     512                        case SERVICE_ICMP:
     513                                // process error
     514                                result = icmp_client_process_packet(packet, &type, &code, NULL, NULL);
     515                                if(result < 0){
     516                                        return result;
     517                                }
     518                                length = (size_t) result;
     519                                // remove the error header
     520                                ERROR_PROPAGATE(packet_trim(packet, length, 0));
     521                                break;
     522                        default:
     523                                return ENOTSUP;
     524                }
     525        }
     526        // get rid of the ip header
     527        length = ip_client_header_length(packet);
     528        ERROR_PROPAGATE(packet_trim(packet, length, 0));
     529
     530        length = packet_get_data_length(packet);
     531        if(length <= 0){
     532                return EINVAL;
     533        }
     534        if(length < ICMP_HEADER_SIZE){
     535                return EINVAL;
     536        }
     537        data = packet_get_data(packet);
     538        if(! data){
     539                return EINVAL;
     540        }
     541        // get icmp header
     542        header = (icmp_header_ref) data;
     543        // checksum
     544        if(header->checksum){
     545                while(ICMP_CHECKSUM(header, length) != IP_CHECKSUM_ZERO){
     546                        // set the original message type on error notification
     547                        // type swap observed in Qemu
     548                        if(error){
     549                                switch(header->type){
     550                                        case ICMP_ECHOREPLY:
     551                                                header->type = ICMP_ECHO;
     552                                                continue;
     553                                }
     554                        }
     555                        return EINVAL;
     556                }
     557        }
     558        switch(header->type){
     559                case ICMP_ECHOREPLY:
     560                        if(error){
     561                                return icmp_process_echo_reply(packet, header, type, code);
     562                        }else{
     563                                return icmp_process_echo_reply(packet, header, ICMP_ECHO, 0);
     564                        }
     565                case ICMP_ECHO:
     566                        if(error){
     567                                return icmp_process_echo_reply(packet, header, type, code);
     568                        // do not send a reply if disabled
     569                        }else if(icmp_globals.echo_replying){
     570                                addrlen = packet_get_addr(packet, &src, NULL);
     571                                if((addrlen > 0)
     572                                // set both addresses to the source one (avoids the source address deletion before setting the destination one)
     573                                        && (packet_set_addr(packet, src, src, (size_t) addrlen) == EOK)){
     574                                        // send the reply
     575                                        icmp_send_packet(ICMP_ECHOREPLY, 0, packet, header, 0, 0, 0, 0);
     576                                        return EOK;
     577                                }else{
     578                                        return EINVAL;
     579                                }
     580                        }else{
     581                                return EPERM;
     582                        }
     583                case ICMP_DEST_UNREACH:
     584                case ICMP_SOURCE_QUENCH:
     585                case ICMP_REDIRECT:
     586                case ICMP_ALTERNATE_ADDR:
     587                case ICMP_ROUTER_ADV:
     588                case ICMP_ROUTER_SOL:
     589                case ICMP_TIME_EXCEEDED:
     590                case ICMP_PARAMETERPROB:
     591                case ICMP_CONVERSION_ERROR:
     592                case ICMP_REDIRECT_MOBILE:
     593                case ICMP_SKIP:
     594                case ICMP_PHOTURIS:
     595                        ip_received_error_msg(icmp_globals.ip_phone, -1, packet, SERVICE_IP, SERVICE_ICMP);
     596                        return EOK;
     597                default:
     598                        return ENOTSUP;
     599        }
     600}
     601
     602int icmp_process_echo_reply(packet_t packet, icmp_header_ref header, icmp_type_t type, icmp_code_t code){
     603        int reply_key;
     604        icmp_reply_ref reply;
     605
     606        // compute the reply key
     607        reply_key = ICMP_GET_REPLY_KEY(header->un.echo.identifier, header->un.echo.sequence_number);
     608        pq_release_remote(icmp_globals.net_phone, packet_get_id(packet));
     609        // lock the globals
     610        fibril_rwlock_write_lock(&icmp_globals.lock);
     611        // find the pending reply
     612        reply = icmp_replies_find(&icmp_globals.replies, reply_key);
     613        if(reply){
     614                // set the result
     615                reply->result = type;
     616                // notify the waiting fibril
     617                fibril_condvar_signal(&reply->condvar);
     618        }
     619        fibril_rwlock_write_unlock(&icmp_globals.lock);
     620        return EOK;
     621}
     622
     623int icmp_message_standalone(ipc_callid_t callid, ipc_call_t * call, ipc_call_t * answer, int * answer_count){
     624        ERROR_DECLARE;
     625
     626        packet_t packet;
     627
     628        *answer_count = 0;
     629        switch(IPC_GET_METHOD(*call)){
     630                case NET_TL_RECEIVED:
     631                        if(! ERROR_OCCURRED(packet_translate_remote(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){
     632                                ERROR_CODE = icmp_received_msg(IPC_GET_DEVICE(call), packet, SERVICE_ICMP, IPC_GET_ERROR(call));
     633                        }
     634                        return ERROR_CODE;
     635                case NET_ICMP_INIT:
     636                        return icmp_process_client_messages(callid, * call);
     637                default:
     638                        return icmp_process_message(call);
     639        }
     640        return ENOTSUP;
     641}
     642
     643int icmp_process_client_messages(ipc_callid_t callid, ipc_call_t call){
     644        ERROR_DECLARE;
     645
     646        bool keep_on_going = true;
     647//      fibril_rwlock_t                 lock;
     648        ipc_call_t answer;
     649        int answer_count;
     650        size_t length;
     651        struct sockaddr * addr;
     652        ipc_callid_t data_callid;
     653        icmp_echo_ref echo_data;
     654        int res;
     655
     656        /*
     657         * Accept the connection
     658         *  - Answer the first NET_ICMP_INIT call.
     659         */
     660        res = EOK;
     661        answer_count = 0;
     662
     663//      fibril_rwlock_initialize(&lock);
     664
     665        echo_data = (icmp_echo_ref) malloc(sizeof(*echo_data));
     666        if(! echo_data){
     667                return ENOMEM;
     668        }
     669
     670        // assign a new identifier
     671        fibril_rwlock_write_lock(&icmp_globals.lock);
     672        res = icmp_bind_free_id(echo_data);
     673        fibril_rwlock_write_unlock(&icmp_globals.lock);
     674        if(res < 0){
     675                free(echo_data);
     676                return res;
     677        }
     678
     679        while(keep_on_going){
     680
     681                // answer the call
     682                answer_call(callid, res, &answer, answer_count);
     683
     684                // refresh data
     685                refresh_answer(&answer, &answer_count);
     686
     687                // get the next call
     688                callid = async_get_call(&call);
     689
     690                // process the call
     691                switch(IPC_GET_METHOD(call)){
     692                        case IPC_M_PHONE_HUNGUP:
     693                                keep_on_going = false;
     694                                res = EHANGUP;
     695                                break;
     696                        case NET_ICMP_ECHO:
     697//                              fibril_rwlock_write_lock(&lock);
     698                                if(! async_data_write_receive(&data_callid, &length)){
     699                                        res = EINVAL;
     700                                }else{
     701                                        addr = malloc(length);
     702                                        if(! addr){
     703                                                res = ENOMEM;
     704                                        }else{
     705                                                if(! ERROR_OCCURRED(async_data_write_finalize(data_callid, addr, length))){
     706                                                        fibril_rwlock_write_lock(&icmp_globals.lock);
     707                                                        res = 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);
     708                                                        fibril_rwlock_write_unlock(&icmp_globals.lock);
     709                                                        free(addr);
     710                                                        if(echo_data->sequence_number < UINT16_MAX){
     711                                                                ++ echo_data->sequence_number;
     712                                                        }else{
     713                                                                echo_data->sequence_number = 0;
     714                                                        }
     715                                                }else{
     716                                                        res = ERROR_CODE;
     717                                                }
     718                                        }
     719                                }
     720//                              fibril_rwlock_write_unlock(&lock);
     721                                break;
     722                        default:
     723                                res = icmp_process_message(&call);
     724                }
     725        }
     726
     727        // release the identifier
     728        fibril_rwlock_write_lock(&icmp_globals.lock);
     729        icmp_echo_data_exclude(&icmp_globals.echo_data, echo_data->identifier);
     730        fibril_rwlock_write_unlock(&icmp_globals.lock);
     731        return res;
     732}
     733
     734int icmp_process_message(ipc_call_t * call){
     735        ERROR_DECLARE;
     736
     737        packet_t packet;
     738
     739        switch(IPC_GET_METHOD(*call)){
     740                case NET_ICMP_DEST_UNREACH:
     741                        if(! ERROR_OCCURRED(packet_translate_remote(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){
     742                                ERROR_CODE = icmp_destination_unreachable_msg(0, ICMP_GET_CODE(call), ICMP_GET_MTU(call), packet);
     743                        }
     744                        return ERROR_CODE;
     745                case NET_ICMP_SOURCE_QUENCH:
     746                        if(! ERROR_OCCURRED(packet_translate_remote(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){
     747                                ERROR_CODE = icmp_source_quench_msg(0, packet);
     748                        }
     749                        return ERROR_CODE;
     750                case NET_ICMP_TIME_EXCEEDED:
     751                        if(! ERROR_OCCURRED(packet_translate_remote(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){
     752                                ERROR_CODE = icmp_time_exceeded_msg(0, ICMP_GET_CODE(call), packet);
     753                        }
     754                        return ERROR_CODE;
     755                case NET_ICMP_PARAMETERPROB:
     756                        if(! ERROR_OCCURRED(packet_translate_remote(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){
     757                                ERROR_CODE = icmp_parameter_problem_msg(0, ICMP_GET_CODE(call), ICMP_GET_POINTER(call), packet);
     758                        }
     759                        return ERROR_CODE;
     760                default:
     761                        return ENOTSUP;
     762        }
     763}
     764
     765int icmp_release_and_return(packet_t packet, int result){
     766        pq_release_remote(icmp_globals.net_phone, packet_get_id(packet));
     767        return result;
     768}
     769
     770int icmp_bind_free_id(icmp_echo_ref echo_data){
     771        icmp_param_t index;
     772
     773        if(! echo_data){
     774                return EBADMEM;
     775        }
     776        // from the last used one
     777        index = icmp_globals.last_used_id;
     778        do{
     779                ++ index;
     780                // til the range end
     781                if(index >= ICMP_FREE_IDS_END){
     782                        // start from the range beginning
     783                        index = ICMP_FREE_IDS_START - 1;
     784                        do{
     785                                ++ index;
     786                                // til the last used one
     787                                if(index >= icmp_globals.last_used_id){
     788                                        // none found
     789                                        return ENOTCONN;
     790                                }
     791                        }while(icmp_echo_data_find(&icmp_globals.echo_data, index) != NULL);
     792                        // found, break immediately
     793                        break;
     794                }
     795        }while(icmp_echo_data_find(&icmp_globals.echo_data, index) != NULL);
     796        echo_data->identifier = index;
     797        echo_data->sequence_number = 0;
     798        return icmp_echo_data_add(&icmp_globals.echo_data, index, echo_data);
     799}
     800
     801/** Default thread for new connections.
    90802 *
    91  * @param[in,out] header ICMP datagram header.
    92  * @param[in]     length Total datagram length.
     803 *  @param[in] iid The initial message identifier.
     804 *  @param[in] icall The initial message call structure.
    93805 *
    94  * @return Computed checksum.
    95  *
    96  */
    97 #define ICMP_CHECKSUM(header, length) \
    98         htons(ip_checksum((uint8_t *) (header), (length)))
    99 
    100 /** An echo request datagrams pattern. */
    101 #define ICMP_ECHO_TEXT  "ICMP hello from HelenOS."
    102 
    103 /** ICMP reply data. */
    104 typedef struct {
    105         /** Hash table link */
    106         link_t link;
    107        
    108         /** Reply identification and sequence */
    109         icmp_param_t id;
    110         icmp_param_t sequence;
    111        
    112         /** Reply signaling */
    113         fibril_condvar_t condvar;
    114        
    115         /** Reply result */
    116         int result;
    117 } icmp_reply_t;
    118 
    119 /** Global data */
    120 static int phone_net = -1;
    121 static int phone_ip = -1;
    122 static bool error_reporting = true;
    123 static bool echo_replying = true;
    124 static packet_dimension_t icmp_dimension;
    125 
    126 /** ICMP client identification counter */
    127 static atomic_t icmp_client;
    128 
    129 /** ICMP identifier and sequence number (client-specific) */
    130 static fibril_local icmp_param_t icmp_id;
    131 static fibril_local icmp_param_t icmp_seq;
    132 
    133 /** Reply hash table */
    134 static fibril_mutex_t reply_lock;
    135 static hash_table_t replies;
    136 
    137 static hash_index_t replies_hash(unsigned long key[])
     806 */
     807static void tl_client_connection(ipc_callid_t iid, ipc_call_t * icall)
    138808{
    139809        /*
    140          * ICMP identifier and sequence numbers
    141          * are 16-bit values.
     810         * Accept the connection
     811         *  - Answer the first IPC_M_CONNECT_ME_TO call.
    142812         */
    143         hash_index_t index = ((key[0] & 0xffff) << 16) | (key[1] & 0xffff);
    144         return (index % REPLY_BUCKETS);
    145 }
    146 
    147 static int replies_compare(unsigned long key[], hash_count_t keys, link_t *item)
     813        ipc_answer_0(iid, EOK);
     814       
     815        while(true) {
     816                ipc_call_t answer;
     817                int answer_count;
     818               
     819                /* Clear the answer structure */
     820                refresh_answer(&answer, &answer_count);
     821               
     822                /* Fetch the next message */
     823                ipc_call_t call;
     824                ipc_callid_t callid = async_get_call(&call);
     825               
     826                /* Process the message */
     827                int res = tl_module_message_standalone(callid, &call, &answer,
     828                    &answer_count);
     829               
     830                /* End if said to either by the message or the processing result */
     831                if ((IPC_GET_METHOD(call) == IPC_M_PHONE_HUNGUP) || (res == EHANGUP))
     832                        return;
     833               
     834                /* Answer the message */
     835                answer_call(callid, res, &answer, answer_count);
     836        }
     837}
     838
     839/** Starts the module.
     840 *
     841 *  @param argc The count of the command line arguments. Ignored parameter.
     842 *  @param argv The command line parameters. Ignored parameter.
     843 *
     844 *  @returns EOK on success.
     845 *  @returns Other error codes as defined for each specific module start function.
     846 *
     847 */
     848int main(int argc, char *argv[])
    148849{
    149         icmp_reply_t *reply =
    150             hash_table_get_instance(item, icmp_reply_t, link);
     850        ERROR_DECLARE;
    151851       
    152         if (keys == 1)
    153                 return (reply->id == key[0]);
    154         else
    155                 return ((reply->id == key[0]) && (reply->sequence == key[1]));
    156 }
    157 
    158 static void replies_remove_callback(link_t *item)
    159 {
    160 }
    161 
    162 static hash_table_operations_t reply_ops = {
    163         .hash = replies_hash,
    164         .compare = replies_compare,
    165         .remove_callback = replies_remove_callback
    166 };
    167 
    168 /** Release the packet and return the result.
    169  *
    170  * @param[in] packet Packet queue to be released.
    171  *
    172  */
    173 static void icmp_release(packet_t *packet)
    174 {
    175         pq_release_remote(phone_net, packet_get_id(packet));
    176 }
    177 
    178 /** Send the ICMP message.
    179  *
    180  * Set the message type and code and compute the checksum.
    181  * Error messages are sent only if allowed in the configuration.
    182  * Release the packet on errors.
    183  *
    184  * @param[in] type          Message type.
    185  * @param[in] code          Message code.
    186  * @param[in] packet        Message packet to be sent.
    187  * @param[in] header        ICMP header.
    188  * @param[in] error         Error service to be announced. Should be
    189  *                          SERVICE_ICMP or zero.
    190  * @param[in] ttl           Time to live.
    191  * @param[in] tos           Type of service.
    192  * @param[in] dont_fragment Disable fragmentation.
    193  *
    194  * @return EOK on success.
    195  * @return EPERM if the error message is not allowed.
    196  *
    197  */
    198 static int icmp_send_packet(icmp_type_t type, icmp_code_t code,
    199     packet_t *packet, icmp_header_t *header, services_t error, ip_ttl_t ttl,
    200     ip_tos_t tos, bool dont_fragment)
    201 {
    202         /* Do not send an error if disabled */
    203         if ((error) && (!error_reporting)) {
    204                 icmp_release(packet);
    205                 return EPERM;
    206         }
    207        
    208         header->type = type;
    209         header->code = code;
    210        
    211         /*
    212          * The checksum needs to be calculated
    213          * with a virtual checksum field set to
    214          * zero.
    215          */
    216         header->checksum = 0;
    217         header->checksum = ICMP_CHECKSUM(header,
    218             packet_get_data_length(packet));
    219        
    220         int rc = ip_client_prepare_packet(packet, IPPROTO_ICMP, ttl, tos,
    221             dont_fragment, 0);
    222         if (rc != EOK) {
    223                 icmp_release(packet);
    224                 return rc;
    225         }
    226        
    227         return ip_send_msg(phone_ip, -1, packet, SERVICE_ICMP, error);
    228 }
    229 
    230 /** Prepare the ICMP error packet.
    231  *
    232  * Truncate the original packet if longer than ICMP_KEEP_LENGTH bytes.
    233  * Prefix and return the ICMP header.
    234  *
    235  * @param[in,out] packet Original packet.
    236  *
    237  * @return The prefixed ICMP header.
    238  * @return NULL on errors.
    239  *
    240  */
    241 static icmp_header_t *icmp_prepare_packet(packet_t *packet)
    242 {
    243         size_t total_length = packet_get_data_length(packet);
    244         if (total_length <= 0)
    245                 return NULL;
    246        
    247         size_t header_length = ip_client_header_length(packet);
    248         if (header_length <= 0)
    249                 return NULL;
    250        
    251         /* Truncate if longer than 64 bits (without the IP header) */
    252         if ((total_length > header_length + ICMP_KEEP_LENGTH) &&
    253             (packet_trim(packet, 0,
    254             total_length - header_length - ICMP_KEEP_LENGTH) != EOK))
    255                 return NULL;
    256        
    257         icmp_header_t *header = PACKET_PREFIX(packet, icmp_header_t);
    258         if (!header)
    259                 return NULL;
    260        
    261         bzero(header, sizeof(*header));
    262         return header;
    263 }
    264 
    265 /** Request an echo message.
    266  *
    267  * Send a packet with specified parameters to the target host
    268  * and wait for the reply upto the given timeout.
    269  * Block the caller until the reply or the timeout occurs.
    270  *
    271  * @param[in] id            Message identifier.
    272  * @param[in] sequence      Message sequence parameter.
    273  * @param[in] size          Message data length in bytes.
    274  * @param[in] timeout       Timeout in miliseconds.
    275  * @param[in] ttl           Time to live.
    276  * @param[in] tos           Type of service.
    277  * @param[in] dont_fragment Disable fragmentation.
    278  * @param[in] addr          Target host address.
    279  * @param[in] addrlen       Torget host address length.
    280  *
    281  * @return ICMP_ECHO on success.
    282  * @return ETIMEOUT if the reply has not arrived before the
    283  *         timeout.
    284  * @return ICMP type of the received error notification.
    285  * @return EINVAL if the addrlen parameter is less or equal to
    286  *         zero.
    287  * @return ENOMEM if there is not enough memory left.
    288  *
    289  */
    290 static int icmp_echo(icmp_param_t id, icmp_param_t sequence, size_t size,
    291     mseconds_t timeout, ip_ttl_t ttl, ip_tos_t tos, bool dont_fragment,
    292     const struct sockaddr *addr, socklen_t addrlen)
    293 {
    294         if (addrlen <= 0)
    295                 return EINVAL;
    296        
    297         size_t length = (size_t) addrlen;
    298        
    299         packet_t *packet = packet_get_4_remote(phone_net, size,
    300             icmp_dimension.addr_len, ICMP_HEADER_SIZE + icmp_dimension.prefix,
    301             icmp_dimension.suffix);
    302         if (!packet)
    303                 return ENOMEM;
    304        
    305         /* Prepare the requesting packet, set the destination address. */
    306         int rc = packet_set_addr(packet, NULL, (const uint8_t *) addr, length);
    307         if (rc != EOK) {
    308                 icmp_release(packet);
    309                 return rc;
    310         }
    311        
    312         /* Allocate space in the packet */
    313         uint8_t *data = (uint8_t *) packet_suffix(packet, size);
    314         if (!data) {
    315                 icmp_release(packet);
    316                 return ENOMEM;
    317         }
    318        
    319         /* Fill the data */
    320         length = 0;
    321         while (size > length + sizeof(ICMP_ECHO_TEXT)) {
    322                 memcpy(data + length, ICMP_ECHO_TEXT, sizeof(ICMP_ECHO_TEXT));
    323                 length += sizeof(ICMP_ECHO_TEXT);
    324         }
    325         memcpy(data + length, ICMP_ECHO_TEXT, size - length);
    326        
    327         /* Prefix the header */
    328         icmp_header_t *header = PACKET_PREFIX(packet, icmp_header_t);
    329         if (!header) {
    330                 icmp_release(packet);
    331                 return ENOMEM;
    332         }
    333        
    334         bzero(header, sizeof(icmp_header_t));
    335         header->un.echo.identifier = id;
    336         header->un.echo.sequence_number = sequence;
    337        
    338         /* Prepare the reply structure */
    339         icmp_reply_t *reply = malloc(sizeof(icmp_reply_t));
    340         if (!reply) {
    341                 icmp_release(packet);
    342                 return ENOMEM;
    343         }
    344        
    345         reply->id = id;
    346         reply->sequence = sequence;
    347         fibril_condvar_initialize(&reply->condvar);
    348        
    349         /* Add the reply to the replies hash table */
    350         fibril_mutex_lock(&reply_lock);
    351        
    352         unsigned long key[REPLY_KEYS] = {id, sequence};
    353         hash_table_insert(&replies, key, &reply->link);
    354        
    355         /* Send the request */
    356         icmp_send_packet(ICMP_ECHO, 0, packet, header, 0, ttl, tos,
    357             dont_fragment);
    358        
    359         /* Wait for the reply. Timeout in microseconds. */
    360         rc = fibril_condvar_wait_timeout(&reply->condvar, &reply_lock,
    361             timeout * 1000);
    362         if (rc == EOK)
    363                 rc = reply->result;
    364        
    365         /* Remove the reply from the replies hash table */
    366         hash_table_remove(&replies, key, REPLY_KEYS);
    367        
    368         fibril_mutex_unlock(&reply_lock);
    369        
    370         free(reply);
    371        
    372         return rc;
    373 }
    374 
    375 static int icmp_destination_unreachable(icmp_code_t code, icmp_param_t mtu,
    376     packet_t *packet)
    377 {
    378         icmp_header_t *header = icmp_prepare_packet(packet);
    379         if (!header) {
    380                 icmp_release(packet);
    381                 return ENOMEM;
    382         }
    383        
    384         if (mtu)
    385                 header->un.frag.mtu = mtu;
    386        
    387         return icmp_send_packet(ICMP_DEST_UNREACH, code, packet, header,
    388             SERVICE_ICMP, 0, 0, false);
    389 }
    390 
    391 static int icmp_source_quench(packet_t *packet)
    392 {
    393         icmp_header_t *header = icmp_prepare_packet(packet);
    394         if (!header) {
    395                 icmp_release(packet);
    396                 return ENOMEM;
    397         }
    398        
    399         return icmp_send_packet(ICMP_SOURCE_QUENCH, 0, packet, header,
    400             SERVICE_ICMP, 0, 0, false);
    401 }
    402 
    403 static int icmp_time_exceeded(icmp_code_t code, packet_t *packet)
    404 {
    405         icmp_header_t *header = icmp_prepare_packet(packet);
    406         if (!header) {
    407                 icmp_release(packet);
    408                 return ENOMEM;
    409         }
    410        
    411         return icmp_send_packet(ICMP_TIME_EXCEEDED, code, packet, header,
    412             SERVICE_ICMP, 0, 0, false);
    413 }
    414 
    415 static int icmp_parameter_problem(icmp_code_t code, icmp_param_t pointer,
    416     packet_t *packet)
    417 {
    418         icmp_header_t *header = icmp_prepare_packet(packet);
    419         if (!header) {
    420                 icmp_release(packet);
    421                 return ENOMEM;
    422         }
    423        
    424         header->un.param.pointer = pointer;
    425         return icmp_send_packet(ICMP_PARAMETERPROB, code, packet, header,
    426             SERVICE_ICMP, 0, 0, false);
    427 }
    428 
    429 /** Try to set the pending reply result as the received message type.
    430  *
    431  * If the reply data is not present, the reply timed out and the other fibril
    432  * is already awake. The packet is released.
    433  *
    434  * @param[in] packet The received reply message.
    435  * @param[in] header The ICMP message header.
    436  * @param[in] type   The received reply message type.
    437  * @param[in] code   The received reply message code.
    438  *
    439  */
    440 static void icmp_process_echo_reply(packet_t *packet, icmp_header_t *header,
    441     icmp_type_t type, icmp_code_t code)
    442 {
    443         unsigned long key[REPLY_KEYS] =
    444             {header->un.echo.identifier, header->un.echo.sequence_number};
    445        
    446         /* The packet is no longer needed */
    447         icmp_release(packet);
    448        
    449         /* Find the pending reply */
    450         fibril_mutex_lock(&reply_lock);
    451        
    452         link_t *link = hash_table_find(&replies, key);
    453         if (link != NULL) {
    454                 icmp_reply_t *reply =
    455                    hash_table_get_instance(link, icmp_reply_t, link);
    456                
    457                 reply->result = type;
    458                 fibril_condvar_signal(&reply->condvar);
    459         }
    460        
    461         fibril_mutex_unlock(&reply_lock);
    462 }
    463 
    464 /** Process the received ICMP packet.
    465  *
    466  * Notify the destination socket application.
    467  *
    468  * @param[in,out] packet Received packet.
    469  * @param[in]     error  Packet error reporting service to prefix
    470  *                       the received packet.
    471  *
    472  * @return EOK on success.
    473  * @return EINVAL if the packet is not valid.
    474  * @return EINVAL if the stored packet address is not the an_addr_t.
    475  * @return EINVAL if the packet does not contain any data.
    476  * @return NO_DATA if the packet content is shorter than the user
    477  *         datagram header.
    478  * @return ENOMEM if there is not enough memory left.
    479  * @return EADDRNOTAVAIL if the destination socket does not exist.
    480  * @return Other error codes as defined for the
    481  *         ip_client_process_packet() function.
    482  *
    483  */
    484 static int icmp_process_packet(packet_t *packet, services_t error)
    485 {
    486         icmp_type_t type;
    487         icmp_code_t code;
    488         int rc;
    489        
    490         switch (error) {
    491         case SERVICE_NONE:
    492                 break;
    493         case SERVICE_ICMP:
    494                 /* Process error */
    495                 rc = icmp_client_process_packet(packet, &type, &code, NULL, NULL);
    496                 if (rc < 0)
    497                         return rc;
    498                
    499                 /* Remove the error header */
    500                 rc = packet_trim(packet, (size_t) rc, 0);
    501                 if (rc != EOK)
    502                         return rc;
    503                
    504                 break;
    505         default:
    506                 return ENOTSUP;
    507         }
    508        
    509         /* Get rid of the IP header */
    510         size_t length = ip_client_header_length(packet);
    511         rc = packet_trim(packet, length, 0);
    512         if (rc != EOK)
    513                 return rc;
    514        
    515         length = packet_get_data_length(packet);
    516         if (length <= 0)
    517                 return EINVAL;
    518        
    519         if (length < ICMP_HEADER_SIZE)
    520                 return EINVAL;
    521        
    522         void *data = packet_get_data(packet);
    523         if (!data)
    524                 return EINVAL;
    525        
    526         /* Get ICMP header */
    527         icmp_header_t *header = (icmp_header_t *) data;
    528        
    529         if (header->checksum) {
    530                 while (ICMP_CHECKSUM(header, length) != IP_CHECKSUM_ZERO) {
    531                         /*
    532                          * Set the original message type on error notification.
    533                          * Type swap observed in Qemu.
    534                          */
    535                         if (error) {
    536                                 switch (header->type) {
    537                                 case ICMP_ECHOREPLY:
    538                                         header->type = ICMP_ECHO;
    539                                         continue;
    540                                 }
    541                         }
    542                        
    543                         return EINVAL;
    544                 }
    545         }
    546        
    547         switch (header->type) {
    548         case ICMP_ECHOREPLY:
    549                 if (error)
    550                         icmp_process_echo_reply(packet, header, type, code);
    551                 else
    552                         icmp_process_echo_reply(packet, header, ICMP_ECHO, 0);
    553                
    554                 return EOK;
    555        
    556         case ICMP_ECHO:
    557                 if (error) {
    558                         icmp_process_echo_reply(packet, header, type, code);
    559                         return EOK;
    560                 }
    561                
    562                 /* Do not send a reply if disabled */
    563                 if (echo_replying) {
    564                         uint8_t *src;
    565                         int addrlen = packet_get_addr(packet, &src, NULL);
    566                        
    567                         /*
    568                          * Set both addresses to the source one (avoid the
    569                          * source address deletion before setting the
    570                          * destination one).
    571                          */
    572                         if ((addrlen > 0) && (packet_set_addr(packet, src, src,
    573                             (size_t) addrlen) == EOK)) {
    574                                 /* Send the reply */
    575                                 icmp_send_packet(ICMP_ECHOREPLY, 0, packet,
    576                                     header, 0, 0, 0, 0);
    577                                 return EOK;
    578                         }
    579                        
    580                         return EINVAL;
    581                 }
    582                
    583                 return EPERM;
    584        
    585         case ICMP_DEST_UNREACH:
    586         case ICMP_SOURCE_QUENCH:
    587         case ICMP_REDIRECT:
    588         case ICMP_ALTERNATE_ADDR:
    589         case ICMP_ROUTER_ADV:
    590         case ICMP_ROUTER_SOL:
    591         case ICMP_TIME_EXCEEDED:
    592         case ICMP_PARAMETERPROB:
    593         case ICMP_CONVERSION_ERROR:
    594         case ICMP_REDIRECT_MOBILE:
    595         case ICMP_SKIP:
    596         case ICMP_PHOTURIS:
    597                 ip_received_error_msg(phone_ip, -1, packet,
    598                     SERVICE_IP, SERVICE_ICMP);
    599                 return EOK;
    600        
    601         default:
    602                 return ENOTSUP;
    603         }
    604 }
    605 
    606 /** Process IPC messages from the IP module
    607  *
    608  * @param[in]     iid   Message identifier.
    609  * @param[in,out] icall Message parameters.
    610  *
    611  */
    612 static void icmp_receiver(ipc_callid_t iid, ipc_call_t *icall)
    613 {
    614         bool loop = true;
    615         packet_t *packet;
    616         int rc;
    617        
    618         while (loop) {
    619                 switch (IPC_GET_IMETHOD(*icall)) {
    620                 case NET_TL_RECEIVED:
    621                         rc = packet_translate_remote(phone_net, &packet,
    622                             IPC_GET_PACKET(*icall));
    623                         if (rc == EOK) {
    624                                 rc = icmp_process_packet(packet, IPC_GET_ERROR(*icall));
    625                                 if (rc != EOK)
    626                                         icmp_release(packet);
    627                         }
    628                        
    629                         async_answer_0(iid, (sysarg_t) rc);
    630                         break;
    631                 case IPC_M_PHONE_HUNGUP:
    632                         loop = false;
    633                         continue;
    634                 default:
    635                         async_answer_0(iid, (sysarg_t) ENOTSUP);
    636                 }
    637                
    638                 iid = async_get_call(icall);
    639         }
    640 }
    641 
    642 /** Initialize the ICMP module.
    643  *
    644  * @param[in] net_phone Network module phone.
    645  *
    646  * @return EOK on success.
    647  * @return ENOMEM if there is not enough memory left.
    648  *
    649  */
    650 int tl_initialize(int net_phone)
    651 {
    652         measured_string_t names[] = {
    653                 {
    654                         (uint8_t *) "ICMP_ERROR_REPORTING",
    655                         20
    656                 },
    657                 {
    658                         (uint8_t *) "ICMP_ECHO_REPLYING",
    659                         18
    660                 }
    661         };
    662         measured_string_t *configuration;
    663         size_t count = sizeof(names) / sizeof(measured_string_t);
    664         uint8_t *data;
    665        
    666         if (!hash_table_create(&replies, REPLY_BUCKETS, REPLY_KEYS, &reply_ops))
    667                 return ENOMEM;
    668        
    669         fibril_mutex_initialize(&reply_lock);
    670         atomic_set(&icmp_client, 0);
    671        
    672         phone_net = net_phone;
    673         phone_ip = ip_bind_service(SERVICE_IP, IPPROTO_ICMP, SERVICE_ICMP,
    674             icmp_receiver);
    675         if (phone_ip < 0)
    676                 return phone_ip;
    677        
    678         int rc = ip_packet_size_req(phone_ip, -1, &icmp_dimension);
    679         if (rc != EOK)
    680                 return rc;
    681        
    682         icmp_dimension.prefix += ICMP_HEADER_SIZE;
    683         icmp_dimension.content -= ICMP_HEADER_SIZE;
    684        
    685         /* Get configuration */
    686         configuration = &names[0];
    687         rc = net_get_conf_req(phone_net, &configuration, count, &data);
    688         if (rc != EOK)
    689                 return rc;
    690        
    691         if (configuration) {
    692                 if (configuration[0].value)
    693                         error_reporting = (configuration[0].value[0] == 'y');
    694                
    695                 if (configuration[1].value)
    696                         echo_replying = (configuration[1].value[0] == 'y');
    697                
    698                 net_free_settings(configuration, data);
    699         }
     852        /* Start the module */
     853        if (ERROR_OCCURRED(tl_module_start_standalone(tl_client_connection)))
     854                return ERROR_CODE;
    700855       
    701856        return EOK;
    702857}
    703858
    704 /** Per-connection initialization
    705  *
    706  * Initialize client-specific global variables.
    707  *
    708  */
    709 void tl_connection(void)
    710 {
    711         icmp_id = (icmp_param_t) atomic_postinc(&icmp_client);
    712         icmp_seq = 1;
    713 }
    714 
    715 /** Process the ICMP message.
    716  *
    717  * @param[in]  callid Message identifier.
    718  * @param[in]  call   Message parameters.
    719  * @param[out] answer Answer.
    720  * @param[out] count  Number of arguments of the answer.
    721  *
    722  * @return EOK on success.
    723  * @return ENOTSUP if the message is not known.
    724  * @return Other error codes as defined for the packet_translate()
    725  *         function.
    726  * @return Other error codes as defined for the
    727  *         icmp_destination_unreachable() function.
    728  * @return Other error codes as defined for the
    729  *         icmp_source_quench() function.
    730  * @return Other error codes as defined for the
    731  *         icmp_time_exceeded() function.
    732  * @return Other error codes as defined for the
    733  *         icmp_parameter_problem() function.
    734  *
    735  * @see icmp_remote.h
    736  * @see IS_NET_ICMP_MESSAGE()
    737  *
    738  */
    739 int tl_message(ipc_callid_t callid, ipc_call_t *call,
    740     ipc_call_t *answer, size_t *count)
    741 {
    742         struct sockaddr *addr;
    743         size_t size;
    744         packet_t *packet;
    745         int rc;
    746        
    747         *count = 0;
    748        
    749         switch (IPC_GET_IMETHOD(*call)) {
    750         case NET_ICMP_ECHO:
    751                 rc = async_data_write_accept((void **) &addr, false, 0, 0, 0, &size);
    752                 if (rc != EOK)
    753                         return rc;
    754                
    755                 rc = icmp_echo(icmp_id, icmp_seq, ICMP_GET_SIZE(*call),
    756                     ICMP_GET_TIMEOUT(*call), ICMP_GET_TTL(*call),
    757                     ICMP_GET_TOS(*call), ICMP_GET_DONT_FRAGMENT(*call),
    758                     addr, (socklen_t) size);
    759                
    760                 free(addr);
    761                 icmp_seq++;
    762                 return rc;
    763        
    764         case NET_ICMP_DEST_UNREACH:
    765                 rc = packet_translate_remote(phone_net, &packet,
    766                     IPC_GET_PACKET(*call));
    767                 if (rc != EOK)
    768                         return rc;
    769                
    770                 return icmp_destination_unreachable(ICMP_GET_CODE(*call),
    771                     ICMP_GET_MTU(*call), packet);
    772        
    773         case NET_ICMP_SOURCE_QUENCH:
    774                 rc = packet_translate_remote(phone_net, &packet,
    775                     IPC_GET_PACKET(*call));
    776                 if (rc != EOK)
    777                         return rc;
    778                
    779                 return icmp_source_quench(packet);
    780        
    781         case NET_ICMP_TIME_EXCEEDED:
    782                 rc = packet_translate_remote(phone_net, &packet,
    783                     IPC_GET_PACKET(*call));
    784                 if (rc != EOK)
    785                         return rc;
    786                
    787                 return icmp_time_exceeded(ICMP_GET_CODE(*call), packet);
    788        
    789         case NET_ICMP_PARAMETERPROB:
    790                 rc = packet_translate_remote(phone_net, &packet,
    791                     IPC_GET_PACKET(*call));
    792                 if (rc != EOK)
    793                         return rc;
    794                
    795                 return icmp_parameter_problem(ICMP_GET_CODE(*call),
    796                     ICMP_GET_POINTER(*call), packet);
    797         }
    798        
    799         return ENOTSUP;
    800 }
    801 
    802 int main(int argc, char *argv[])
    803 {
    804         /* Start the module */
    805         return tl_module_start(SERVICE_ICMP);
    806 }
    807 
    808859/** @}
    809860 */
Note: See TracChangeset for help on using the changeset viewer.