source: mainline/uspace/srv/net/tl/icmp/icmp.c@ 536ded4

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 536ded4 was 536ded4, checked in by Lukas Mejdrech <lukasmejdrech@…>, 15 years ago
  • use timeouting condvar for the ICMP echo
  • Property mode set to 100644
File size: 26.6 KB
RevLine 
[21580dd]1/*
2 * Copyright (c) 2008 Lukas Mejdrech
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup icmp
30 * @{
31 */
32
33/** @file
34 * ICMP module implementation.
35 * @see icmp.h
36 */
37
38#include <async.h>
39#include <atomic.h>
40#include <fibril.h>
41#include <fibril_synch.h>
42#include <stdint.h>
43
44#include <ipc/ipc.h>
45#include <ipc/services.h>
46
[1a0fb3f8]47#include <sys/time.h>
[21580dd]48#include <sys/types.h>
49
50#include "../../err.h"
51#include "../../messages.h"
52#include "../../modules.h"
53
54#include "../../structures/packet/packet_client.h"
55
56#include "../../include/byteorder.h"
57#include "../../include/checksum.h"
58#include "../../include/icmp_api.h"
59#include "../../include/icmp_client.h"
60#include "../../include/icmp_codes.h"
61#include "../../include/icmp_common.h"
62#include "../../include/icmp_interface.h"
63#include "../../include/il_interface.h"
64#include "../../include/inet.h"
65#include "../../include/ip_client.h"
66#include "../../include/ip_interface.h"
67#include "../../include/ip_protocols.h"
68#include "../../include/net_interface.h"
69#include "../../include/socket_codes.h"
70#include "../../include/socket_errno.h"
71
72#include "../../tl/tl_messages.h"
73
74#include "icmp.h"
75#include "icmp_header.h"
76#include "icmp_messages.h"
77#include "icmp_module.h"
78
79/** Default ICMP error reporting.
80 */
81#define NET_DEFAULT_ICMP_ERROR_REPORTING true
82
83/** Default ICMP echo replying.
84 */
85#define NET_DEFAULT_ICMP_ECHO_REPLYING true
86
87/** Original datagram length in bytes transfered to the error notification message.
88 */
89#define ICMP_KEEP_LENGTH 8
90
91/** Free identifier numbers pool start.
92 */
93#define ICMP_FREE_IDS_START 1
94
95/** Free identifier numbers pool end.
96 */
97#define ICMP_FREE_IDS_END MAX_UINT16
98
99/** Computes the ICMP datagram checksum.
100 * @param[in,out] header The ICMP datagram header.
101 * @param[in] length The total datagram length.
102 * @returns The computed checksum.
103 */
104#define ICMP_CHECKSUM( header, length ) htons( ip_checksum(( uint8_t * ) ( header ), ( length )))
105
106/** An echo request datagrams pattern.
107 */
108#define ICMP_ECHO_TEXT "Hello from HelenOS."
109
110/** Computes an ICMP reply data key.
111 * @param[in] id The message identifier.
112 * @param[in] sequence The message sequence number.
113 * @returns The computed ICMP reply data key.
114 */
115#define ICMP_GET_REPLY_KEY( id, sequence ) ((( id ) << 16 ) | ( sequence & 0xFFFF ))
116
117/** Processes the received ICMP packet.
118 * Is used as an entry point from the underlying IP module.
119 * Releases the packet on error.
120 * @param device_id The device identifier. Ignored parameter.
121 * @param[in,out] packet The received packet.
122 * @param receiver The target service. Ignored parameter.
123 * @param[in] error The packet error reporting service. Prefixes the received packet.
124 * @returns EOK on success.
125 * @returns Other error codes as defined for the icmp_process_packet() function.
126 */
127int icmp_received_msg( device_id_t device_id, packet_t packet, services_t receiver, services_t error );
128
129/** Processes the received ICMP packet.
130 * Notifies the destination socket application.
131 * @param[in,out] packet The received packet.
132 * @param[in] error The packet error reporting service. Prefixes the received packet.
133 * @returns EOK on success.
134 * @returns EINVAL if the packet is not valid.
135 * @returns EINVAL if the stored packet address is not the an_addr_t.
136 * @returns EINVAL if the packet does not contain any data.
137 * @returns NO_DATA if the packet content is shorter than the user datagram header.
138 * @returns ENOMEM if there is not enough memory left.
139 * @returns EADDRNOTAVAIL if the destination socket does not exist.
140 * @returns Other error codes as defined for the ip_client_process_packet() function.
141 */
142int icmp_process_packet( packet_t packet, services_t error );
143
144/** Processes the client messages.
145 * Remembers the assigned identifier and sequence numbers.
146 * Runs until the client module disconnects.
147 * @param[in] callid The message identifier.
148 * @param[in] call The message parameters.
149 * @returns EOK.
150 * @see icmp_interface.h
151 * @see icmp_api.h
152 */
153int icmp_process_client_messages( ipc_callid_t callid, ipc_call_t call );
154
155/** Processes the generic client messages.
156 * @param[in] call The message parameters.
157 * @returns EOK on success.
158 * @returns ENOTSUP if the message is not known.
159 * @returns Other error codes as defined for the packet_translate() function.
160 * @returns Other error codes as defined for the icmp_destination_unreachable_msg() function.
161 * @returns Other error codes as defined for the icmp_source_quench_msg() function.
162 * @returns Other error codes as defined for the icmp_time_exceeded_msg() function.
163 * @returns Other error codes as defined for the icmp_parameter_problem_msg() function.
164 * @see icmp_interface.h
165 */
166int icmp_process_message( ipc_call_t * call );
167
168/** Releases the packet and returns the result.
169 * @param[in] packet The packet queue to be released.
170 * @param[in] result The result to be returned.
171 * @returns The result parameter.
172 */
173int icmp_release_and_return( packet_t packet, int result );
174
175/** Requests an echo message.
176 * Sends a packet with specified parameters to the target host and waits for the reply upto the given timeout.
177 * Blocks the caller until the reply or the timeout occurres.
178 * @param[in] id The message identifier.
179 * @param[in] sequence The message sequence parameter.
180 * @param[in] size The message data length in bytes.
181 * @param[in] timeout The timeout in miliseconds.
182 * @param[in] ttl The time to live.
183 * @param[in] tos The type of service.
184 * @param[in] dont_fragment The value indicating whether the datagram must not be fragmented. Is used as a MTU discovery.
185 * @param[in] addr The target host address.
186 * @param[in] addrlen The torget host address length.
187 * @returns ICMP_ECHO on success.
188 * @returns ETIMEOUT if the reply has not arrived before the timeout.
189 * @returns ICMP type of the received error notification.
190 * @returns EINVAL if the addrlen parameter is less or equal to zero (<=0).
191 * @returns ENOMEM if there is not enough memory left.
192 * @returns EPARTY if there was an internal error.
193 */
194int 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 );
195
196/** Prepares the ICMP error packet.
197 * Truncates the original packet if longer than ICMP_KEEP_LENGTH bytes.
198 * Prefixes and returns the ICMP header.
199 * @param[in,out] packet The original packet.
200 * @returns The prefixed ICMP header.
201 * @returns NULL on errors.
202 */
203icmp_header_ref icmp_prepare_packet( packet_t packet );
204
205/** Sends the ICMP message.
206 * Sets the message type and code and computes the checksum.
207 * Error messages are sent only if allowed in the configuration.
208 * Releases the packet on errors.
209 * @param[in] type The message type.
210 * @param[in] code The message code.
211 * @param[in] packet The message packet to be sent.
212 * @param[in] header The ICMP header.
213 * @param[in] error The error service to be announced. Should be SERVICE_ICMP or zero (0).
214 * @param[in] ttl The time to live.
215 * @param[in] tos The type of service.
216 * @param[in] dont_fragment The value indicating whether the datagram must not be fragmented. Is used as a MTU discovery.
217 * @returns EOK on success.
218 * @returns EPERM if the error message is not allowed.
219 */
220int 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 );
221
222/** Tries to set the pending reply result as the received message type.
223 * If the reply data are still present, the reply timeouted and the parent fibril is awaken.
224 * The global lock is not released in this case to be reused by the parent fibril.
225 * Releases the packet.
226 * @param[in] packet The received reply message.
227 * @param[in] header The ICMP message header.
228 * @param[in] type The received reply message type.
229 * @param[in] code The received reply message code.
230 * @returns EOK.
231 */
232int icmp_process_echo_reply( packet_t packet, icmp_header_ref header, icmp_type_t type, icmp_code_t code );
233
234/** Assigns a new identifier for the connection.
235 * Fills the echo data parameter with the assigned values.
236 * @param[in,out] echo_data The echo data to be bound.
237 * @returns Index of the inserted echo data.
238 * @returns EBADMEM if the echo_data parameter is NULL.
239 * @returns ENOTCONN if no free identifier have been found.
240 */
241int icmp_bind_free_id( icmp_echo_ref echo_data );
242
243/** ICMP global data.
244 */
245icmp_globals_t icmp_globals;
246
247INT_MAP_IMPLEMENT( icmp_replies, icmp_reply_t );
248
249INT_MAP_IMPLEMENT( icmp_echo_data, icmp_echo_t );
250
251int 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 ){
252 icmp_echo_ref echo_data;
253 int res;
254
255 fibril_rwlock_write_lock( & icmp_globals.lock );
256 // use the phone as the echo data index
257 echo_data = icmp_echo_data_find( & icmp_globals.echo_data, icmp_phone );
258 if( ! echo_data ){
259 res = ENOENT;
260 }else{
261 res = icmp_echo( echo_data->identifier, echo_data->sequence_number, size, timeout, ttl, tos, dont_fragment, addr, addrlen );
262 if( echo_data->sequence_number < MAX_UINT16 ){
263 ++ echo_data->sequence_number;
264 }else{
265 echo_data->sequence_number = 0;
266 }
267 }
268 fibril_rwlock_write_unlock( & icmp_globals.lock );
269 return res;
270}
271
272int 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 ){
273 ERROR_DECLARE;
274
275 icmp_header_ref header;
276 packet_t packet;
277 size_t length;
278 uint8_t * data;
279 icmp_reply_ref reply;
[536ded4]280 int reply_key;
[21580dd]281 int result;
282 int index;
283
284 if( addrlen <= 0 ){
285 return EINVAL;
286 }
287 length = ( size_t ) addrlen;
288 // TODO do not ask all the time
289 ERROR_PROPAGATE( ip_packet_size_req( icmp_globals.ip_phone, -1, & icmp_globals.addr_len, & icmp_globals.prefix, & icmp_globals.content, & icmp_globals.suffix ));
[ede63e4]290 packet = packet_get_4( icmp_globals.net_phone, size, icmp_globals.addr_len, ICMP_HEADER_SIZE + icmp_globals.prefix, icmp_globals.suffix );
[21580dd]291 if( ! packet ) return ENOMEM;
292
293 // prepare the requesting packet
294 // set the destination address
295 if( ERROR_OCCURRED( packet_set_addr( packet, NULL, ( const uint8_t * ) addr, length ))){
296 return icmp_release_and_return( packet, ERROR_CODE );
297 }
298 // allocate space in the packet
299 data = ( uint8_t * ) packet_suffix( packet, size );
300 if( ! data ){
301 return icmp_release_and_return( packet, ENOMEM );
302 }
303 // fill the data
304 length = 0;
305 while( size > length + sizeof( ICMP_ECHO_TEXT )){
306 memcpy( data + length, ICMP_ECHO_TEXT, sizeof( ICMP_ECHO_TEXT ));
307 length += sizeof( ICMP_ECHO_TEXT );
308 }
309 memcpy( data + length, ICMP_ECHO_TEXT, size - length );
310 // prefix the header
311 header = PACKET_PREFIX( packet, icmp_header_t );
312 if( ! header ){
313 return icmp_release_and_return( packet, ENOMEM );
314 }
315 bzero( header, sizeof( * header ));
316 header->un.echo.identifier = id;
317 header->un.echo.sequence_number = sequence;
318
[536ded4]319 // prepare the reply structure
[21580dd]320 reply = malloc( sizeof( * reply ));
321 if( ! reply ){
322 return icmp_release_and_return( packet, ENOMEM );
323 }
324 fibril_mutex_initialize( & reply->mutex );
325 fibril_mutex_lock( & reply->mutex );
326 fibril_condvar_initialize( & reply->condvar );
[536ded4]327 reply_key = ICMP_GET_REPLY_KEY( header->un.echo.identifier, header->un.echo.sequence_number );
328 index = icmp_replies_add( & icmp_globals.replies, reply_key, reply );
[21580dd]329 if( index < 0 ){
330 free( reply );
331 return icmp_release_and_return( packet, index );
332 }
333
334 // unlock the globals and wait for a reply
335 fibril_rwlock_write_unlock( & icmp_globals.lock );
336
337 // send the request
338 icmp_send_packet( ICMP_ECHO, 0, packet, header, 0, ttl, tos, dont_fragment );
339
340 // wait for a reply
[536ded4]341 // timeout in microseconds
342 if( ERROR_OCCURRED( fibril_condvar_wait_timeout( & reply->condvar, & reply->mutex, timeout * 1000 ))){
343 result = ERROR_CODE;
[21580dd]344
[536ded4]345 // lock the globals again and clean up
346 fibril_rwlock_write_lock( & icmp_globals.lock );
347 }else{
348 // read the result
349 result = reply->result;
350
351 // release the reply structure
352 fibril_mutex_unlock( & reply->mutex );
353 }
[21580dd]354
355 // destroy the reply structure
356 icmp_replies_exclude_index( & icmp_globals.replies, index );
357 return result;
358}
359
360int icmp_destination_unreachable_msg( int icmp_phone, icmp_code_t code, icmp_param_t mtu, packet_t packet ){
361 icmp_header_ref header;
362
363 header = icmp_prepare_packet( packet );
364 if( ! header ){
365 return icmp_release_and_return( packet, ENOMEM );
366 }
367 if( mtu ){
368 header->un.frag.mtu = mtu;
369 }
370 return icmp_send_packet( ICMP_DEST_UNREACH, code, packet, header, SERVICE_ICMP, 0, 0, 0 );
371}
372
373int icmp_source_quench_msg( int icmp_phone, packet_t packet ){
374 icmp_header_ref header;
375
376 header = icmp_prepare_packet( packet );
377 if( ! header ){
378 return icmp_release_and_return( packet, ENOMEM );
379 }
380 return icmp_send_packet( ICMP_SOURCE_QUENCH, 0, packet, header, SERVICE_ICMP, 0, 0, 0 );
381}
382
383int icmp_time_exceeded_msg( int icmp_phone, icmp_code_t code, packet_t packet ){
384 icmp_header_ref header;
385
386 header = icmp_prepare_packet( packet );
387 if( ! header ){
388 return icmp_release_and_return( packet, ENOMEM );
389 }
390 return icmp_send_packet( ICMP_TIME_EXCEEDED, code, packet, header, SERVICE_ICMP, 0, 0, 0 );
391}
392
393int icmp_parameter_problem_msg( int icmp_phone, icmp_code_t code, icmp_param_t pointer, packet_t packet ){
394 icmp_header_ref header;
395
396 header = icmp_prepare_packet( packet );
397 if( ! header ){
398 return icmp_release_and_return( packet, ENOMEM );
399 }
400 header->un.param.pointer = pointer;
401 return icmp_send_packet( ICMP_PARAMETERPROB, code, packet, header, SERVICE_ICMP, 0, 0, 0 );
402}
403
404icmp_header_ref icmp_prepare_packet( packet_t packet ){
405 icmp_header_ref header;
406 size_t header_length;
407 size_t total_length;
408
409 total_length = packet_get_data_length( packet );
410 if( total_length <= 0 ) return NULL;
411 header_length = ip_client_header_length( packet );
412 if( header_length <= 0 ) return NULL;
413 // truncate if longer than 64 bits (without the IP header)
414 if(( total_length > header_length + ICMP_KEEP_LENGTH )
415 && ( packet_trim( packet, 0, total_length - header_length - ICMP_KEEP_LENGTH ) != EOK )){
416 return NULL;
417 }
418 header = PACKET_PREFIX( packet, icmp_header_t );
419 if( ! header ) return NULL;
420 bzero( header, sizeof( * header ));
421 return header;
422}
423
424int 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 ){
425 ERROR_DECLARE;
426
427 // do not send an error if disabled
428 if( error && ( ! icmp_globals.error_reporting )){
429 return icmp_release_and_return( packet, EPERM );
430 }
431 header->type = type;
432 header->code = code;
433 header->checksum = 0;
434 header->checksum = ICMP_CHECKSUM( header, packet_get_data_length( packet ));
435 if( ERROR_OCCURRED( ip_client_prepare_packet( packet, IPPROTO_ICMP, ttl, tos, dont_fragment, 0 ))){
436 return icmp_release_and_return( packet, ERROR_CODE );
437 }
438 return ip_send_msg( icmp_globals.ip_phone, -1, packet, SERVICE_ICMP, error );
439}
440
[1a0fb3f8]441int icmp_connect_module( services_t service, suseconds_t timeout ){
[21580dd]442 icmp_echo_ref echo_data;
443 icmp_param_t id;
444 int index;
445
446 echo_data = ( icmp_echo_ref ) malloc( sizeof( * echo_data ));
447 if( ! echo_data ) return ENOMEM;
448 // assign a new identifier
449 fibril_rwlock_write_lock( & icmp_globals.lock );
450 index = icmp_bind_free_id( echo_data );
451 if( index < 0 ){
452 free( echo_data );
453 fibril_rwlock_write_unlock( & icmp_globals.lock );
454 return index;
455 }else{
456 id = echo_data->identifier;
457 fibril_rwlock_write_unlock( & icmp_globals.lock );
458 // return the echo data identifier as the ICMP phone
459 return id;
460 }
461}
462
463int icmp_initialize( async_client_conn_t client_connection ){
464 ERROR_DECLARE;
465
466 measured_string_t names[] = {{ "ICMP_ERROR_REPORTING", 20 }, { "ICMP_ECHO_REPLYING", 18 }};
467 measured_string_ref configuration;
468 size_t count = sizeof( names ) / sizeof( measured_string_t );
469 char * data;
470
471 fibril_rwlock_initialize( & icmp_globals.lock );
472 fibril_rwlock_write_lock( & icmp_globals.lock );
473 icmp_replies_initialize( & icmp_globals.replies );
474 icmp_echo_data_initialize( & icmp_globals.echo_data );
475 icmp_globals.ip_phone = ip_bind_service( SERVICE_IP, IPPROTO_ICMP, SERVICE_ICMP, client_connection, icmp_received_msg );
476 if( icmp_globals.ip_phone < 0 ){
477 return icmp_globals.ip_phone;
478 }
479 ERROR_PROPAGATE( ip_packet_size_req( icmp_globals.ip_phone, -1, & icmp_globals.addr_len, & icmp_globals.prefix, & icmp_globals.content, & icmp_globals.suffix ));
[ede63e4]480 icmp_globals.prefix += ICMP_HEADER_SIZE;
481 icmp_globals.content -= ICMP_HEADER_SIZE;
[21580dd]482 // get configuration
483 icmp_globals.error_reporting = NET_DEFAULT_ICMP_ERROR_REPORTING;
484 icmp_globals.echo_replying = NET_DEFAULT_ICMP_ECHO_REPLYING;
485 configuration = & names[ 0 ];
486 ERROR_PROPAGATE( net_get_conf_req( icmp_globals.net_phone, & configuration, count, & data ));
487 if( configuration ){
488 if( configuration[ 0 ].value ){
489 icmp_globals.error_reporting = ( configuration[ 0 ].value[ 0 ] == 'y' );
490 }
491 if( configuration[ 1 ].value ){
492 icmp_globals.echo_replying = ( configuration[ 1 ].value[ 0 ] == 'y' );
493 }
494 net_free_settings( configuration, data );
495 }
496 fibril_rwlock_write_unlock( & icmp_globals.lock );
497 return EOK;
498}
499
500int icmp_received_msg( device_id_t device_id, packet_t packet, services_t receiver, services_t error ){
501 ERROR_DECLARE;
502
503 if( ERROR_OCCURRED( icmp_process_packet( packet, error ))){
504 return icmp_release_and_return( packet, ERROR_CODE );
505 }
506
507 return EOK;
508}
509
510int icmp_process_packet( packet_t packet, services_t error ){
511 ERROR_DECLARE;
512
513 size_t length;
514 uint8_t * src;
515 int addrlen;
516 int result;
517 void * data;
518 icmp_header_ref header;
519 icmp_type_t type;
520 icmp_code_t code;
521
522 if( error ){
523 switch( error ){
524 case SERVICE_ICMP:
525 // process error
526 result = icmp_client_process_packet( packet, & type, & code, NULL, NULL );
527 if( result < 0 ) return result;
528 length = ( size_t ) result;
529 // remove the error header
530 ERROR_PROPAGATE( packet_trim( packet, length, 0 ));
531 break;
532 default:
533 return ENOTSUP;
534 }
535 }
536 // get rid of the ip header
537 length = ip_client_header_length( packet );
538 ERROR_PROPAGATE( packet_trim( packet, length, 0 ));
539
540 length = packet_get_data_length( packet );
541 if( length <= 0 ) return EINVAL;
[ede63e4]542 if( length < ICMP_HEADER_SIZE) return EINVAL;
[21580dd]543 data = packet_get_data( packet );
544 if( ! data ) return EINVAL;
545 // get icmp header
546 header = ( icmp_header_ref ) data;
547 // checksum
548 if( header->checksum ){
[918e9910]549 while( ICMP_CHECKSUM( header, length ) != IP_CHECKSUM_ZERO ){
[21580dd]550 // set the original message type on error notification
551 // type swap observed in Qemu
552 if( error ){
553 switch( header->type ){
554 case ICMP_ECHOREPLY:
555 header->type = ICMP_ECHO;
556 continue;
557 }
558 }
559 return EINVAL;
560 }
561 }
562 switch( header->type ){
563 case ICMP_ECHOREPLY:
564 if( error ){
565 return icmp_process_echo_reply( packet, header, type, code );
566 }else{
567 return icmp_process_echo_reply( packet, header, ICMP_ECHO, 0 );
568 }
569 case ICMP_ECHO:
570 if( error ){
571 return icmp_process_echo_reply( packet, header, type, code );
572 // do not send a reply if disabled
573 }else if( icmp_globals.echo_replying ){
574 addrlen = packet_get_addr( packet, & src, NULL );
575 if(( addrlen > 0 )
576 // set both addresses to the source one (avoids the source address deletion before setting the destination one)
577 && ( packet_set_addr( packet, src, src, ( size_t ) addrlen ) == EOK )){
578 // send the reply
579 icmp_send_packet( ICMP_ECHOREPLY, 0, packet, header, 0, 0, 0, 0 );
580 return EOK;
581 }else{
582 return EINVAL;
583 }
584 }else{
585 return EPERM;
586 }
587 case ICMP_DEST_UNREACH:
588 case ICMP_SOURCE_QUENCH:
589 case ICMP_REDIRECT:
590 case ICMP_ALTERNATE_ADDR:
591 case ICMP_ROUTER_ADV:
592 case ICMP_ROUTER_SOL:
593 case ICMP_TIME_EXCEEDED:
594 case ICMP_PARAMETERPROB:
595 case ICMP_CONVERSION_ERROR:
596 case ICMP_REDIRECT_MOBILE:
597 case ICMP_SKIP:
598 case ICMP_PHOTURIS:
599 ip_received_error_msg( icmp_globals.ip_phone, -1, packet, SERVICE_IP, SERVICE_ICMP );
600 return EOK;
601 default:
602 return ENOTSUP;
603 }
604}
605
606int icmp_process_echo_reply( packet_t packet, icmp_header_ref header, icmp_type_t type, icmp_code_t code ){
607 int reply_key;
608 icmp_reply_ref reply;
609
610 // compute the reply key
611 reply_key = ICMP_GET_REPLY_KEY( header->un.echo.identifier, header->un.echo.sequence_number );
612 pq_release( icmp_globals.net_phone, packet_get_id( packet ));
613 // lock the globals
614 fibril_rwlock_write_lock( & icmp_globals.lock );
615 // find the pending reply
616 reply = icmp_replies_find( & icmp_globals.replies, reply_key );
617 if( reply ){
618 // set the result
619 reply->result = type;
620 // notify the main fibril
621 fibril_condvar_signal( & reply->condvar );
622 }else{
623 // unlock only if no reply
624 fibril_rwlock_write_unlock( & icmp_globals.lock );
625 }
626 return EOK;
627}
628
629int icmp_message( ipc_callid_t callid, ipc_call_t * call, ipc_call_t * answer, int * answer_count ){
630 ERROR_DECLARE;
631
632 packet_t packet;
633
634 * answer_count = 0;
635 switch( IPC_GET_METHOD( * call )){
636 case NET_TL_RECEIVED:
637 if( ! ERROR_OCCURRED( packet_translate( icmp_globals.net_phone, & packet, IPC_GET_PACKET( call )))){
638 ERROR_CODE = icmp_received_msg( IPC_GET_DEVICE( call ), packet, SERVICE_ICMP, IPC_GET_ERROR( call ));
639 }
640 return ERROR_CODE;
641 case NET_ICMP_INIT:
642 return icmp_process_client_messages( callid, * call );
643 default:
644 return icmp_process_message( call );
645 }
646 return ENOTSUP;
647}
648
649int icmp_process_client_messages( ipc_callid_t callid, ipc_call_t call ){
650 ERROR_DECLARE;
651
652 bool keep_on_going = true;
653// fibril_rwlock_t lock;
654 ipc_call_t answer;
655 int answer_count;
656 size_t length;
657 struct sockaddr * addr;
658 ipc_callid_t data_callid;
659 icmp_echo_ref echo_data;
660
661 /*
662 * Accept the connection
663 * - Answer the first NET_ICMP_INIT call.
664 */
665 ipc_answer_0( callid, EOK );
666
667// fibril_rwlock_initialize( & lock );
668
669 echo_data = ( icmp_echo_ref ) malloc( sizeof( * echo_data ));
670 if( ! echo_data ) return ENOMEM;
671 // assign a new identifier
672 fibril_rwlock_write_lock( & icmp_globals.lock );
673 ERROR_CODE = icmp_bind_free_id( echo_data );
674 fibril_rwlock_write_unlock( & icmp_globals.lock );
675 if( ERROR_CODE < 0 ){
676 free( echo_data );
677 return ERROR_CODE;
678 }
679
680 while( keep_on_going ){
681 refresh_answer( & answer, & answer_count );
682
683 callid = async_get_call( & call );
684
685 switch( IPC_GET_METHOD( call )){
686 case IPC_M_PHONE_HUNGUP:
687 keep_on_going = false;
688 ERROR_CODE = EOK;
689 break;
690 case NET_ICMP_ECHO:
691// fibril_rwlock_write_lock( & lock );
692 if( ! async_data_write_receive( & data_callid, & length )){
693 ERROR_CODE = EINVAL;
694 }else{
695 addr = malloc( length );
696 if( ! addr ){
697 ERROR_CODE = ENOMEM;
698 }else{
699 if( ! ERROR_OCCURRED( async_data_write_finalize( data_callid, addr, length ))){
700 fibril_rwlock_write_lock( & icmp_globals.lock );
701 ERROR_CODE = icmp_echo( echo_data->identifier, echo_data->sequence_number, ICMP_GET_SIZE( call ), ICMP_GET_TIMEOUT( call ), ICMP_GET_TTL( call ), ICMP_GET_TOS( call ), ICMP_GET_DONT_FRAGMENT( call ), addr, ( socklen_t ) length );
702 fibril_rwlock_write_unlock( & icmp_globals.lock );
703 free( addr );
704 if( echo_data->sequence_number < MAX_UINT16 ){
705 ++ echo_data->sequence_number;
706 }else{
707 echo_data->sequence_number = 0;
708 }
709 }
710 }
711 }
712// fibril_rwlock_write_unlock( & lock );
713 break;
714 default:
715 ERROR_CODE = icmp_process_message( & call );
716 }
717
718 answer_call( callid, ERROR_CODE, & answer, answer_count );
719 }
720
721 // release the identifier
722 fibril_rwlock_write_lock( & icmp_globals.lock );
723 icmp_echo_data_exclude( & icmp_globals.echo_data, echo_data->identifier );
724 fibril_rwlock_write_unlock( & icmp_globals.lock );
725 return EOK;
726}
727
728int icmp_process_message( ipc_call_t * call ){
729 ERROR_DECLARE;
730
731 packet_t packet;
732
733 switch( IPC_GET_METHOD( * call )){
734 case NET_ICMP_DEST_UNREACH:
735 if( ! ERROR_OCCURRED( packet_translate( icmp_globals.net_phone, & packet, IPC_GET_PACKET( call )))){
736 ERROR_CODE = icmp_destination_unreachable_msg( 0, ICMP_GET_CODE( call ), ICMP_GET_MTU( call ), packet );
737 }
738 return ERROR_CODE;
739 case NET_ICMP_SOURCE_QUENCH:
740 if( ! ERROR_OCCURRED( packet_translate( icmp_globals.net_phone, & packet, IPC_GET_PACKET( call )))){
741 ERROR_CODE = icmp_source_quench_msg( 0, packet );
742 }
743 return ERROR_CODE;
744 case NET_ICMP_TIME_EXCEEDED:
745 if( ! ERROR_OCCURRED( packet_translate( icmp_globals.net_phone, & packet, IPC_GET_PACKET( call )))){
746 ERROR_CODE = icmp_time_exceeded_msg( 0, ICMP_GET_CODE( call ), packet );
747 }
748 return ERROR_CODE;
749 case NET_ICMP_PARAMETERPROB:
750 if( ! ERROR_OCCURRED( packet_translate( icmp_globals.net_phone, & packet, IPC_GET_PACKET( call )))){
751 ERROR_CODE = icmp_parameter_problem_msg( 0, ICMP_GET_CODE( call ), ICMP_GET_POINTER( call ), packet );
752 }
753 return ERROR_CODE;
754 default:
755 return ENOTSUP;
756 }
757}
758
759int icmp_release_and_return( packet_t packet, int result ){
760 pq_release( icmp_globals.net_phone, packet_get_id( packet ));
761 return result;
762}
763
764int icmp_bind_free_id( icmp_echo_ref echo_data ){
765 icmp_param_t index;
766
767 if( ! echo_data ) return EBADMEM;
768 // from the last used one
769 index = icmp_globals.last_used_id;
770 do{
771 ++ index;
772 // til the range end
773 if( index >= ICMP_FREE_IDS_END ){
774 // start from the range beginning
775 index = ICMP_FREE_IDS_START - 1;
776 do{
777 ++ index;
778 // til the last used one
779 if( index >= icmp_globals.last_used_id ){
780 // none found
781 return ENOTCONN;
782 }
783 }while( icmp_echo_data_find( & icmp_globals.echo_data, index ) != NULL );
784 // found, break immediately
785 break;
786 }
787 }while( icmp_echo_data_find( & icmp_globals.echo_data, index ) != NULL );
788 echo_data->identifier = index;
789 echo_data->sequence_number = 0;
790 return icmp_echo_data_add( & icmp_globals.echo_data, index, echo_data );
791}
792
793/** @}
794 */
Note: See TracBrowser for help on using the repository browser.