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

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