Subversion Repositories HelenOS

Rev

Rev 4720 | Rev 4722 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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