Subversion Repositories HelenOS

Rev

Rev 4715 | Rev 4720 | 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>
42
 
43
#include <ipc/ipc.h>
44
#include <ipc/services.h>
45
 
4715 mejdrech 46
#include <sys/types.h>
47
 
4707 mejdrech 48
#include "../../err.h"
49
#include "../../messages.h"
50
#include "../../modules.h"
51
 
52
#include "../../structures/packet/packet_client.h"
53
 
4715 mejdrech 54
#include "../../include/byteorder.h"
4707 mejdrech 55
#include "../../include/crc.h"
4715 mejdrech 56
#include "../../include/icmp_api.h"
57
#include "../../include/icmp_client.h"
4707 mejdrech 58
#include "../../include/icmp_codes.h"
4715 mejdrech 59
#include "../../include/icmp_common.h"
4707 mejdrech 60
#include "../../include/icmp_interface.h"
4715 mejdrech 61
#include "../../include/il_interface.h"
62
#include "../../include/inet.h"
4707 mejdrech 63
#include "../../include/ip_client.h"
64
#include "../../include/ip_interface.h"
65
#include "../../include/ip_protocols.h"
4715 mejdrech 66
#include "../../include/socket_codes.h"
67
#include "../../include/socket_errno.h"
4707 mejdrech 68
 
69
#include "../../tl/tl_messages.h"
70
 
71
#include "icmp.h"
72
#include "icmp_header.h"
73
#include "icmp_messages.h"
74
#include "icmp_module.h"
75
 
76
#define ICMP_KEEP_LENGTH    8
77
 
4715 mejdrech 78
#define ICMP_CHECKSUM( header, length )     htons( ip_checksum(( uint8_t * ) ( header ), ( length )))
4707 mejdrech 79
 
4715 mejdrech 80
#define ICMP_ECHO_TEXT                  "Hello from HelenOS."
81
 
82
#define ICMP_GET_LOCK_KEY( id, sequence )   ((( id ) << 16 ) | ( sequence & 0xFFFF ))
83
 
4707 mejdrech 84
/** Processes the received ICMP packet.
85
 *  Is used as an entry point from the underlying IP module.
86
 *  Releases the packet on error.
87
 *  @param device_id The device identifier. Ignored parameter.
88
 *  @param packet The received packet. Input/output parameter.
89
 *  @param receiver The target service. Ignored parameter.
90
 *  @param error The packet error reporting service. Prefixes the received packet. Input parameter.
91
 *  @returns EOK on success.
92
 *  @returns Other error codes as defined for the icmp_process_packet() function.
93
 */
94
int icmp_received_msg( device_id_t device_id, packet_t packet, services_t receiver, services_t error );
95
 
96
/** Processes the received ICMP packet.
97
 *  Notifies the destination socket application.
98
 *  @param packet The received packet. Input/output parameter.
4715 mejdrech 99
 *  @param error The packet error reporting service. Prefixes the received packet. Input parameter.
4707 mejdrech 100
 *  @returns EOK on success.
101
 *  @returns EINVAL if the packet is not valid.
102
 *  @returns EINVAL if the stored packet address is not the an_addr_t.
103
 *  @returns EINVAL if the packet does not contain any data.
104
 *  @returns NO_DATA if the packet content is shorter than the user datagram header.
105
 *  @returns ENOMEM if there is not enough memory left.
106
 *  @returns EADDRNOTAVAIL if the destination socket does not exist.
107
 *  @returns Other error codes as defined for the ip_client_process_packet() function.
108
 */
4715 mejdrech 109
int icmp_process_packet( packet_t packet, services_t error );
4707 mejdrech 110
 
4715 mejdrech 111
/** Processes the client messages.
112
 *  Remenbers the assigned identifier and sequence numbers.
113
 *  Runs until the client module disconnects.
114
 *  @param callid The message identifier. Input parameter.
115
 *  @param call The message parameters. Input parameter.
116
 *  @returns EOK on success.
117
 *  @see icmp_interface.h
118
 */
119
int icmp_process_client_messages( ipc_callid_t callid, ipc_call_t call );
120
 
121
/** Releases the packet and returns the result.
122
 *  @param packet The packet queue to be released. Input parameter.
123
 *  @param result The result to be returned. Input parameter.
124
 *  @return The result parameter.
125
 */
126
int icmp_release_and_return( packet_t packet, int result );
127
 
128
int icmp_echo( icmp_param_t id, icmp_param_t sequence, size_t size, suseconds_t timeout, ip_ttl_t ttl, ip_tos_t tos, int dont_fragment, const struct sockaddr * addr, socklen_t addrlen );
4707 mejdrech 129
icmp_header_ref icmp_prepare_packet( packet_t packet );
4715 mejdrech 130
int icmp_send_packet( icmp_type_t type, icmp_code_t code, packet_t packet, icmp_header_ref header, services_t error );
131
int process_echo_reply( packet_t packet, icmp_header_ref header, icmp_type_t type, icmp_code_t code );
4707 mejdrech 132
 
133
/** ICMP global data.
134
 */
135
icmp_globals_t  icmp_globals;
136
 
4715 mejdrech 137
INT_MAP_IMPLEMENT( time_locks, atomic_t );
138
 
139
GENERIC_FIELD_IMPLEMENT( echo_data, icmp_echo_t );
140
 
141
int icmp_echo_msg( int icmp_phone, size_t size, suseconds_t timeout, ip_ttl_t ttl, ip_tos_t tos, int dont_fragment, const struct sockaddr * addr, socklen_t addrlen ){
142
    icmp_echo_ref   echo_data;
143
    int             res;
144
 
145
    fibril_rwlock_write_lock( & icmp_globals.lock );
146
    // use the phone as the echo data index
147
    echo_data = echo_data_get_index( & icmp_globals.echo_data, icmp_phone );
148
    if( ! echo_data ){
149
        res = ENOENT;
150
    }else{
151
        res = icmp_echo( echo_data->id, echo_data->sequence, size, timeout, ttl, tos, dont_fragment, addr, addrlen );
152
        ++ echo_data->sequence;
153
    }
154
    fibril_rwlock_write_unlock( & icmp_globals.lock );
155
    return res;
4707 mejdrech 156
}
157
 
4715 mejdrech 158
int icmp_echo( icmp_param_t id, icmp_param_t sequence, size_t size, suseconds_t timeout, ip_ttl_t ttl, ip_tos_t tos, int dont_fragment, const struct sockaddr * addr, socklen_t addrlen ){
159
    ERROR_DECLARE;
160
 
161
    icmp_header_ref header;
162
    packet_t        packet;
163
    size_t          offset;
164
    uint8_t *       data;
165
    atomic_t *      lock;
166
    int             result;
167
    int             index;
168
    int             lock_key;
169
    struct sockaddr_in *    address_in;
170
    struct timeval  time_before;
171
    struct timeval  time_after;
172
 
173
    // TODO do not ask all the time
174
    ERROR_PROPAGATE( ip_packet_size_req( icmp_globals.ip_phone, -1, & icmp_globals.addr_len, & icmp_globals.prefix, & icmp_globals.content, & icmp_globals.suffix ));
175
    packet = packet_get_4( icmp_globals.net_phone, size, icmp_globals.addr_len, sizeof( icmp_header_t ) + icmp_globals.prefix, icmp_globals.suffix );
176
    if( ! packet ) return ENOMEM;
177
    // set the destination address
178
    if( addrlen < sizeof( struct sockaddr )){
179
        return icmp_release_and_return( packet, EINVAL );
180
    }
181
    switch( addr->sa_family ){
182
        case AF_INET:
183
            if( addrlen != sizeof( struct sockaddr_in )){
184
                return icmp_release_and_return( packet, EINVAL );
185
            }
186
            address_in = ( struct sockaddr_in * ) addr;
187
            if( ERROR_OCCURRED( packet_set_addr( packet, NULL, ( uint8_t * ) & address_in->sin_addr.s_addr, sizeof( address_in->sin_addr.s_addr )))){
188
                return icmp_release_and_return( packet, ERROR_CODE );
189
            }
190
            break;
191
        default:
192
            return icmp_release_and_return( packet, EAFNOSUPPORT );
193
    }
194
    // allocate space in the packet
195
    data = ( uint8_t * ) packet_suffix( packet, size );
196
    if( ! data ){
197
        return icmp_release_and_return( packet, ENOMEM );
198
    }
199
    offset = 0;
200
    while( size > offset + sizeof( ICMP_ECHO_TEXT )){
201
        memcpy( data + offset, ICMP_ECHO_TEXT, sizeof( ICMP_ECHO_TEXT ));
202
        offset += sizeof( ICMP_ECHO_TEXT );
203
    }
204
    memcpy( data + offset, ICMP_ECHO_TEXT, size - offset );
205
    header = icmp_prepare_packet( packet );
206
    if( ! header ){
207
        return icmp_release_and_return( packet, ENOMEM );
208
    }
209
    header->un.echo.id = id;
210
    header->un.echo.sequence = sequence;
211
    lock_key = ICMP_GET_LOCK_KEY( header->un.echo.id, header->un.echo.sequence );
212
    // create a locked fuxed
213
    lock = malloc( sizeof( * lock ));
214
    if( ! lock ){
215
        return icmp_release_and_return( packet, ENOMEM );
216
    }
217
    atomic_set( lock, 0 );
218
    index = time_locks_add( & icmp_globals.time_locks, lock_key, lock );
219
    if( index < 0 ){
220
        free( lock );
221
        return icmp_release_and_return( packet, index );
222
    }
223
    if( ERROR_OCCURRED( icmp_send_packet( ICMP_ECHO, 0, packet, header, 0 ))){
224
        free( lock );
225
        return icmp_release_and_return( packet, ERROR_CODE );
226
    }
227
    // unlock the global to allow unlocking and other fibrils to work
228
    // try to lock again - may be unlocked by the reply
229
    ERROR_PROPAGATE( gettimeofday( & time_before, NULL ));
230
    do{
231
        result = atomic_get( lock );
232
        if( result ){
233
            break;
234
        }else{
235
            fibril_rwlock_write_unlock( & icmp_globals.lock );
236
            // TODO does not yield?
237
            //printf( "y %d\n", fibril_yield());
238
            fibril_yield();
239
            fibril_rwlock_write_lock( & icmp_globals.lock );
240
            ERROR_PROPAGATE( gettimeofday( & time_after, NULL ));
241
        }
242
    }while( tv_sub( & time_after, & time_before ) <= timeout );
243
    if( ! result ){
244
        result = ELIMIT;
245
    }
246
    // destroy the lock
247
    time_locks_exclude_index( & icmp_globals.time_locks, index );
248
    return result;
249
}
250
 
4707 mejdrech 251
int icmp_destination_unreachable_msg( int icmp_phone, icmp_code_t code, icmp_param_t mtu, packet_t packet ){
252
    icmp_header_ref header;
253
 
254
    header = icmp_prepare_packet( packet );
255
    if( ! header ) return ENOMEM;
256
    if( mtu ){
257
        header->un.frag.mtu = mtu;
258
    }
4715 mejdrech 259
    return icmp_send_packet( ICMP_DEST_UNREACH, code, packet, header, SERVICE_ICMP );
4707 mejdrech 260
}
261
 
262
int icmp_source_quench_msg( int icmp_phone, packet_t packet ){
263
    icmp_header_ref header;
264
 
265
    header = icmp_prepare_packet( packet );
266
    if( ! header ) return ENOMEM;
4715 mejdrech 267
    return icmp_send_packet( ICMP_SOURCE_QUENCH, 0, packet, header, SERVICE_ICMP );
4707 mejdrech 268
}
269
 
270
int icmp_time_exceeded_msg( int icmp_phone, icmp_code_t code, packet_t packet ){
271
    icmp_header_ref header;
272
 
273
    header = icmp_prepare_packet( packet );
274
    if( ! header ) return ENOMEM;
4715 mejdrech 275
    return icmp_send_packet( ICMP_TIME_EXCEEDED, code, packet, header, SERVICE_ICMP );
4707 mejdrech 276
}
277
 
278
int icmp_parameter_problem_msg( int icmp_phone, icmp_code_t code, icmp_param_t pointer, packet_t packet ){
279
    icmp_header_ref header;
280
 
281
    header = icmp_prepare_packet( packet );
282
    if( ! header ) return ENOMEM;
283
    header->un.param.pointer = pointer;
4715 mejdrech 284
    return icmp_send_packet( ICMP_PARAMETERPROB, code, packet, header, SERVICE_ICMP );
4707 mejdrech 285
}
286
 
287
icmp_header_ref icmp_prepare_packet( packet_t packet ){
288
    icmp_header_ref header;
289
    int             header_length;
290
    size_t          total_length;
291
 
292
    total_length = packet_get_data_length( packet );
293
    if( total_length <= 0 ) return NULL;
294
    header_length = ip_client_header_length( packet );
295
    if( header_length <= 0 ) return NULL;
296
    // truncate if longer than 64 bits (without the IP header)
297
    if( total_length - header_length > ICMP_KEEP_LENGTH ){
298
        if( packet_trim( packet, 0, total_length - header_length - ICMP_KEEP_LENGTH ) != EOK ) return NULL;
299
    }
300
    header = PACKET_PREFIX( packet, icmp_header_t );
301
    if( ! header ){
302
        pq_release( icmp_globals.net_phone, packet_get_id( packet ));
303
        return NULL;
304
    }
305
    bzero( header, sizeof( * header ));
306
    return header;
307
}
308
 
4715 mejdrech 309
int icmp_send_packet( icmp_type_t type, icmp_code_t code, packet_t packet, icmp_header_ref header, services_t error ){
4707 mejdrech 310
    ERROR_DECLARE;
311
 
312
    header->type = type;
313
    header->code = code;
314
    header->checksum = 0;
4715 mejdrech 315
    header->checksum = ICMP_CHECKSUM( header, packet_get_data_length( packet ));
4707 mejdrech 316
    if( ERROR_OCCURRED( ip_client_prepare_packet( packet, IPPROTO_ICMP, 0, 0, 0, 0 ))){
317
        pq_release( icmp_globals.net_phone, packet_get_id( packet ));
318
        return ERROR_CODE;
319
    }
4715 mejdrech 320
    return ip_send_msg( icmp_globals.ip_phone, -1, packet, SERVICE_ICMP, error );
4707 mejdrech 321
}
322
 
323
int icmp_connect_module( services_t service ){
4715 mejdrech 324
    icmp_echo_ref   echo_data;
325
    int             index;
326
 
327
    echo_data = ( icmp_echo_ref ) malloc( sizeof( * echo_data ));
328
    if( ! echo_data ) return ENOMEM;
329
    // assign a new identifier
330
    fibril_rwlock_write_lock( & icmp_globals.lock );
331
    ++ icmp_globals.last_used_id;
332
    echo_data->id = icmp_globals.last_used_id;
333
    echo_data->sequence = 0;
334
    // remember the assigned echo data
335
    index = echo_data_add( & icmp_globals.echo_data, echo_data );
336
    if( index < 0 ){
337
        free( echo_data );
338
    }
339
    fibril_rwlock_write_unlock( & icmp_globals.lock );
340
    // return the echo data index as the ICMP phone
341
    return index;
4707 mejdrech 342
}
343
 
344
int icmp_initialize( async_client_conn_t client_connection ){
345
    ERROR_DECLARE;
346
 
347
    fibril_rwlock_initialize( & icmp_globals.lock );
348
    fibril_rwlock_write_lock( & icmp_globals.lock );
4715 mejdrech 349
    time_locks_initialize( & icmp_globals.time_locks );
350
    echo_data_initialize( & icmp_globals.echo_data );
4707 mejdrech 351
    icmp_globals.ip_phone = ip_bind_service( SERVICE_IP, IPPROTO_ICMP, SERVICE_ICMP, client_connection, icmp_received_msg );
352
    if( icmp_globals.ip_phone < 0 ){
353
        return icmp_globals.ip_phone;
354
    }
355
    ERROR_PROPAGATE( ip_packet_size_req( icmp_globals.ip_phone, -1, & icmp_globals.addr_len, & icmp_globals.prefix, & icmp_globals.content, & icmp_globals.suffix ));
356
    icmp_globals.prefix += sizeof( icmp_header_t );
357
    icmp_globals.content -= sizeof( icmp_header_t );
358
    fibril_rwlock_write_unlock( & icmp_globals.lock );
359
    return EOK;
360
}
361
 
362
int icmp_received_msg( device_id_t device_id, packet_t packet, services_t receiver, services_t error ){
363
    ERROR_DECLARE;
364
 
4715 mejdrech 365
    if( ERROR_OCCURRED( icmp_process_packet( packet, error ))){
4707 mejdrech 366
        pq_release( icmp_globals.net_phone, packet_get_id( packet ));
367
        return ERROR_CODE;
368
    }
369
 
370
    return EOK;
371
}
372
 
4715 mejdrech 373
int icmp_process_packet( packet_t packet, services_t error ){
374
    ERROR_DECLARE;
375
 
4708 mejdrech 376
    size_t          length;
4715 mejdrech 377
    uint8_t *       src;
378
    int             addrlen;
4708 mejdrech 379
    int             result;
4707 mejdrech 380
    void *          data;
381
    icmp_header_ref header;
4715 mejdrech 382
    icmp_type_t     type;
383
    icmp_code_t     code;
4707 mejdrech 384
 
4715 mejdrech 385
    if( error ){
386
        switch( error ){
387
            case SERVICE_ICMP:
388
                // process error
389
                // TODO remove debug dump
390
                // length = icmp_client_header_length( packet );
391
                result = icmp_client_process_packet( packet, & type, & code, NULL, NULL );
392
                if( result < 0 ) return result;
393
                length = ( size_t ) result;
394
                printf( "ICMP error %d (%d) in packet %d\n", type, code, packet_get_id( packet ) );
395
                ERROR_PROPAGATE( packet_trim( packet, length, 0 ));
396
                break;
397
            default:
398
                return ENOTSUP;
399
        }
400
    }
4707 mejdrech 401
    // get rid of the ip header
4708 mejdrech 402
    result = ip_client_process_packet( packet, NULL, NULL, NULL, NULL, NULL );
403
    if( result < 0 ) return result;
404
    packet_trim( packet, ( size_t ) result, 0 );
4707 mejdrech 405
 
406
    length = packet_get_data_length( packet );
407
    if( length <= 0 ) return EINVAL;
408
    if( length < sizeof( icmp_header_t )) return EINVAL;
409
    data = packet_get_data( packet );
410
    if( ! data ) return EINVAL;
411
    // get icmp header
412
    header = ( icmp_header_ref ) data;
413
    // checksum
4715 mejdrech 414
    if(( header->checksum ) && ( ICMP_CHECKSUM( header, length ))){
4707 mejdrech 415
        return EINVAL;
416
    }
4715 mejdrech 417
    switch( header->type ){
418
        case ICMP_ECHOREPLY:
419
            return process_echo_reply( packet, header, ICMP_ECHO, 0 );
420
        case ICMP_ECHO:
421
            if( error ){
422
                return process_echo_reply( packet, header, type, code );
423
            }else{
424
                addrlen = packet_get_addr( packet, & src, NULL );
425
                if(( addrlen > 0 )
426
                // set both addresses to the source one (avoids the source address deletion before setting the destination one)
427
                && ( packet_set_addr( packet, src, src, ( size_t ) addrlen ) == EOK )){
428
                    // send the reply
429
                    return icmp_send_packet( ICMP_ECHOREPLY, 0, packet, header, 0 );
430
                }else{
431
                    return icmp_release_and_return( packet, EINVAL );
432
                }
433
            }
434
        case ICMP_DEST_UNREACH:
435
        case ICMP_SOURCE_QUENCH:
436
        case ICMP_REDIRECT:
437
        case ICMP_ALTERNATE_ADDR:
438
        case ICMP_ROUTER_ADV:
439
        case ICMP_ROUTER_SOL:
440
        case ICMP_TIME_EXCEEDED:
441
        case ICMP_PARAMETERPROB:
442
        case ICMP_CONVERSION_ERROR:
443
        case ICMP_REDIRECT_MOBILE:
444
        case ICMP_SKIP:
445
        case ICMP_PHOTURIS:
446
            fibril_rwlock_read_lock( & icmp_globals.lock );
447
            ip_received_error_msg( icmp_globals.ip_phone, -1, packet, SERVICE_IP, SERVICE_ICMP );
448
            fibril_rwlock_read_unlock( & icmp_globals.lock );
449
            return EOK;
450
        default:
451
            return icmp_release_and_return( packet, ENOTSUP );
452
    }
453
}
454
 
455
int process_echo_reply( packet_t packet, icmp_header_ref header, icmp_type_t type, icmp_code_t code ){
456
    int             lock_key;
457
    atomic_t *      lock;
458
 
459
    lock_key = ICMP_GET_LOCK_KEY( header->un.echo.id, header->un.echo.sequence );
460
    fibril_rwlock_write_lock( & icmp_globals.lock );
461
    lock = time_locks_find( & icmp_globals.time_locks, lock_key );
462
    if( lock ){
463
        // unlock the lock for the waiting fibril
464
        atomic_set( lock, type );
465
    }
466
    pq_release( icmp_globals.net_phone, packet_get_id( packet ));
467
    fibril_rwlock_write_unlock( & icmp_globals.lock );
4707 mejdrech 468
    return EOK;
469
}
470
 
471
int icmp_message( ipc_callid_t callid, ipc_call_t * call, ipc_call_t * answer, int * answer_count ){
472
    ERROR_DECLARE;
473
 
4715 mejdrech 474
    packet_t            packet;
4707 mejdrech 475
 
476
    * answer_count = 0;
477
    switch( IPC_GET_METHOD( * call )){
478
        case NET_TL_RECEIVED:
479
            if( ! ERROR_OCCURRED( packet_translate( icmp_globals.net_phone, & packet, IPC_GET_PACKET( call )))){
480
                ERROR_CODE = icmp_received_msg( IPC_GET_DEVICE( call ), packet, SERVICE_ICMP, IPC_GET_ERROR( call ));
481
            }
482
            return ERROR_CODE;
4715 mejdrech 483
        case NET_ICMP_INIT:
484
            return icmp_process_client_messages( callid, * call );
4707 mejdrech 485
    }
486
    return ENOTSUP;
487
}
488
 
4715 mejdrech 489
int icmp_process_client_messages( ipc_callid_t callid, ipc_call_t call ){
490
    ERROR_DECLARE;
491
 
492
    bool                    keep_on_going = true;
493
    fibril_rwlock_t         lock;
494
    ipc_call_t              answer;
495
    int                     answer_count;
496
    packet_t                packet;
497
    size_t                  addrlen;
498
    struct sockaddr *       addr;
499
    icmp_param_t            id;
500
    icmp_param_t            sequence = 0;
501
    ipc_callid_t            data_callid;
502
 
503
    /*
504
     * Accept the connection
505
     *  - Answer the first NET_ICMP_INIT call.
506
     */
507
    ipc_answer_0( callid, EOK );
508
 
509
    fibril_rwlock_initialize( & lock );
510
 
511
    // assign a new identifier
512
    fibril_rwlock_write_lock( & icmp_globals.lock );
513
    ++ icmp_globals.last_used_id;
514
    id = icmp_globals.last_used_id;
515
    fibril_rwlock_write_unlock( & icmp_globals.lock );
516
 
517
    while( keep_on_going ){
518
        refresh_answer( & answer, & answer_count );
519
 
520
        callid = async_get_call( & call );
521
 
522
        switch( IPC_GET_METHOD( call )){
523
            case IPC_M_PHONE_HUNGUP:
524
                keep_on_going = false;
525
                ERROR_CODE = EOK;
526
                break;
527
            case NET_ICMP_ECHO:
528
                fibril_rwlock_write_lock( & lock );
529
                if( ! ipc_data_write_receive( & data_callid, & addrlen )){
530
                    ERROR_CODE = EINVAL;
531
                }else{
532
                    addr = malloc( addrlen );
533
                    if( ! addr ){
534
                        ERROR_CODE = ENOMEM;
535
                    }else{
536
                        if( ! ERROR_OCCURRED( ipc_data_write_finalize( data_callid, addr, addrlen ))){
537
                            fibril_rwlock_write_lock( & icmp_globals.lock );
538
                            ERROR_CODE = icmp_echo( id, sequence, ICMP_GET_SIZE( call ), ICMP_GET_TIMEOUT( call ), ICMP_GET_TTL( call ), ICMP_GET_TOS( call ), ICMP_GET_DONT_FRAGMENT( call ), addr, addrlen );
539
                            fibril_rwlock_write_unlock( & icmp_globals.lock );
540
                            free( addr );
541
                            ++ sequence;
542
                        }
543
                    }
544
                }
545
                fibril_rwlock_write_unlock( & lock );
546
                break;
547
            case NET_ICMP_DEST_UNREACH:
548
                if( ! ERROR_OCCURRED( packet_translate( icmp_globals.net_phone, & packet, IPC_GET_PACKET( & call )))){
549
                    ERROR_CODE = icmp_destination_unreachable_msg( 0, ICMP_GET_CODE( call ), ICMP_GET_MTU( call ), packet );
550
                }
551
                break;
552
            case NET_ICMP_SOURCE_QUENCH:
553
                if( ! ERROR_OCCURRED( packet_translate( icmp_globals.net_phone, & packet, IPC_GET_PACKET( & call )))){
554
                    ERROR_CODE = icmp_source_quench_msg( 0, packet );
555
                }
556
            case NET_ICMP_TIME_EXCEEDED:
557
                if( ! ERROR_OCCURRED( packet_translate( icmp_globals.net_phone, & packet, IPC_GET_PACKET( & call )))){
558
                    ERROR_CODE = icmp_time_exceeded_msg( 0, ICMP_GET_CODE( call ), packet );
559
                }
560
                break;
561
            case NET_ICMP_PARAMETERPROB:
562
                if( ! ERROR_OCCURRED( packet_translate( icmp_globals.net_phone, & packet, IPC_GET_PACKET( & call )))){
563
                    ERROR_CODE = icmp_parameter_problem_msg( 0, ICMP_GET_CODE( call ), ICMP_GET_POINTER( call ), packet );
564
                }
565
            default:
566
                ERROR_CODE = ENOTSUP;
567
        }
568
 
569
        answer_call( callid, ERROR_CODE, & answer, answer_count );
570
    }
571
 
572
    return EOK;
573
}
574
 
575
int icmp_release_and_return( packet_t packet, int result ){
576
    pq_release( icmp_globals.net_phone, packet_get_id( packet ));
577
    return result;
578
}
579
 
4707 mejdrech 580
/** @}
581
 */