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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 936835e was 936835e, checked in by Lukas Mejdrech <lukasmejdrech@…>, 16 years ago

Merge mainline changes, revision 311

  • Property mode set to 100644
File size: 26.0 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/** 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 occurs.
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 is not present, the reply timed out and the other fibril
224 * is already awake.
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;
280 int reply_key;
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.packet_dimension));
290 packet = packet_get_4(icmp_globals.net_phone, size, icmp_globals.packet_dimension.addr_len, ICMP_HEADER_SIZE + icmp_globals.packet_dimension.prefix, icmp_globals.packet_dimension.suffix);
291 if(! packet){
292 return ENOMEM;
293 }
294
295 // prepare the requesting packet
296 // set the destination address
297 if(ERROR_OCCURRED(packet_set_addr(packet, NULL, (const uint8_t *) addr, length))){
298 return icmp_release_and_return(packet, ERROR_CODE);
299 }
300 // allocate space in the packet
301 data = (uint8_t *) packet_suffix(packet, size);
302 if(! data){
303 return icmp_release_and_return(packet, ENOMEM);
304 }
305 // fill the data
306 length = 0;
307 while(size > length + sizeof(ICMP_ECHO_TEXT)){
308 memcpy(data + length, ICMP_ECHO_TEXT, sizeof(ICMP_ECHO_TEXT));
309 length += sizeof(ICMP_ECHO_TEXT);
310 }
311 memcpy(data + length, ICMP_ECHO_TEXT, size - length);
312 // prefix the header
313 header = PACKET_PREFIX(packet, icmp_header_t);
314 if(! header){
315 return icmp_release_and_return(packet, ENOMEM);
316 }
317 bzero(header, sizeof(*header));
318 header->un.echo.identifier = id;
319 header->un.echo.sequence_number = sequence;
320
321 // prepare the reply structure
322 reply = malloc(sizeof(*reply));
323 if(! reply){
324 return icmp_release_and_return(packet, ENOMEM);
325 }
326 fibril_mutex_initialize(&reply->mutex);
327 fibril_mutex_lock(&reply->mutex);
328 fibril_condvar_initialize(&reply->condvar);
329 reply_key = ICMP_GET_REPLY_KEY(header->un.echo.identifier, header->un.echo.sequence_number);
330 index = icmp_replies_add(&icmp_globals.replies, reply_key, reply);
331 if(index < 0){
332 free(reply);
333 return icmp_release_and_return(packet, index);
334 }
335
336 // unlock the globals so that we can wait for the reply
337 fibril_rwlock_write_unlock(&icmp_globals.lock);
338
339 // send the request
340 icmp_send_packet(ICMP_ECHO, 0, packet, header, 0, ttl, tos, dont_fragment);
341
342 // wait for the reply
343 // timeout in microseconds
344 if(ERROR_OCCURRED(fibril_condvar_wait_timeout(&reply->condvar, &reply->mutex, timeout * 1000))){
345 result = ERROR_CODE;
346 }else{
347 // read the result
348 result = reply->result;
349 }
350
351 // drop the reply mutex before locking the globals again
352 fibril_mutex_unlock(&reply->mutex);
353 fibril_rwlock_write_lock(&icmp_globals.lock);
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){
411 return NULL;
412 }
413 header_length = ip_client_header_length(packet);
414 if(header_length <= 0){
415 return NULL;
416 }
417 // truncate if longer than 64 bits (without the IP header)
418 if((total_length > header_length + ICMP_KEEP_LENGTH)
419 && (packet_trim(packet, 0, total_length - header_length - ICMP_KEEP_LENGTH) != EOK)){
420 return NULL;
421 }
422 header = PACKET_PREFIX(packet, icmp_header_t);
423 if(! header){
424 return NULL;
425 }
426 bzero(header, sizeof(*header));
427 return header;
428}
429
430int 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){
431 ERROR_DECLARE;
432
433 // do not send an error if disabled
434 if(error && (! icmp_globals.error_reporting)){
435 return icmp_release_and_return(packet, EPERM);
436 }
437 header->type = type;
438 header->code = code;
439 header->checksum = 0;
440 header->checksum = ICMP_CHECKSUM(header, packet_get_data_length(packet));
441 if(ERROR_OCCURRED(ip_client_prepare_packet(packet, IPPROTO_ICMP, ttl, tos, dont_fragment, 0))){
442 return icmp_release_and_return(packet, ERROR_CODE);
443 }
444 return ip_send_msg(icmp_globals.ip_phone, -1, packet, SERVICE_ICMP, error);
445}
446
447int icmp_connect_module(services_t service, suseconds_t timeout){
448 icmp_echo_ref echo_data;
449 icmp_param_t id;
450 int index;
451
452 echo_data = (icmp_echo_ref) malloc(sizeof(*echo_data));
453 if(! echo_data){
454 return ENOMEM;
455 }
456 // assign a new identifier
457 fibril_rwlock_write_lock(&icmp_globals.lock);
458 index = icmp_bind_free_id(echo_data);
459 if(index < 0){
460 free(echo_data);
461 fibril_rwlock_write_unlock(&icmp_globals.lock);
462 return index;
463 }else{
464 id = echo_data->identifier;
465 fibril_rwlock_write_unlock(&icmp_globals.lock);
466 // return the echo data identifier as the ICMP phone
467 return id;
468 }
469}
470
471int icmp_initialize(async_client_conn_t client_connection){
472 ERROR_DECLARE;
473
474 measured_string_t names[] = {{str_dup("ICMP_ERROR_REPORTING"), 20}, {str_dup("ICMP_ECHO_REPLYING"), 18}};
475 measured_string_ref configuration;
476 size_t count = sizeof(names) / sizeof(measured_string_t);
477 char * data;
478
479 fibril_rwlock_initialize(&icmp_globals.lock);
480 fibril_rwlock_write_lock(&icmp_globals.lock);
481 icmp_replies_initialize(&icmp_globals.replies);
482 icmp_echo_data_initialize(&icmp_globals.echo_data);
483 icmp_globals.ip_phone = ip_bind_service(SERVICE_IP, IPPROTO_ICMP, SERVICE_ICMP, client_connection, icmp_received_msg);
484 if(icmp_globals.ip_phone < 0){
485 return icmp_globals.ip_phone;
486 }
487 ERROR_PROPAGATE(ip_packet_size_req(icmp_globals.ip_phone, -1, &icmp_globals.packet_dimension));
488 icmp_globals.packet_dimension.prefix += ICMP_HEADER_SIZE;
489 icmp_globals.packet_dimension.content -= ICMP_HEADER_SIZE;
490 // get configuration
491 icmp_globals.error_reporting = NET_DEFAULT_ICMP_ERROR_REPORTING;
492 icmp_globals.echo_replying = NET_DEFAULT_ICMP_ECHO_REPLYING;
493 configuration = &names[0];
494 ERROR_PROPAGATE(net_get_conf_req(icmp_globals.net_phone, &configuration, count, &data));
495 if(configuration){
496 if(configuration[0].value){
497 icmp_globals.error_reporting = (configuration[0].value[0] == 'y');
498 }
499 if(configuration[1].value){
500 icmp_globals.echo_replying = (configuration[1].value[0] == 'y');
501 }
502 net_free_settings(configuration, data);
503 }
504 fibril_rwlock_write_unlock(&icmp_globals.lock);
505 return EOK;
506}
507
508int icmp_received_msg(device_id_t device_id, packet_t packet, services_t receiver, services_t error){
509 ERROR_DECLARE;
510
511 if(ERROR_OCCURRED(icmp_process_packet(packet, error))){
512 return icmp_release_and_return(packet, ERROR_CODE);
513 }
514
515 return EOK;
516}
517
518int icmp_process_packet(packet_t packet, services_t error){
519 ERROR_DECLARE;
520
521 size_t length;
522 uint8_t * src;
523 int addrlen;
524 int result;
525 void * data;
526 icmp_header_ref header;
527 icmp_type_t type;
528 icmp_code_t code;
529
530 if(error){
531 switch(error){
532 case SERVICE_ICMP:
533 // process error
534 result = icmp_client_process_packet(packet, &type, &code, NULL, NULL);
535 if(result < 0){
536 return result;
537 }
538 length = (size_t) result;
539 // remove the error header
540 ERROR_PROPAGATE(packet_trim(packet, length, 0));
541 break;
542 default:
543 return ENOTSUP;
544 }
545 }
546 // get rid of the ip header
547 length = ip_client_header_length(packet);
548 ERROR_PROPAGATE(packet_trim(packet, length, 0));
549
550 length = packet_get_data_length(packet);
551 if(length <= 0){
552 return EINVAL;
553 }
554 if(length < ICMP_HEADER_SIZE){
555 return EINVAL;
556 }
557 data = packet_get_data(packet);
558 if(! data){
559 return EINVAL;
560 }
561 // get icmp header
562 header = (icmp_header_ref) data;
563 // checksum
564 if(header->checksum){
565 while(ICMP_CHECKSUM(header, length) != IP_CHECKSUM_ZERO){
566 // set the original message type on error notification
567 // type swap observed in Qemu
568 if(error){
569 switch(header->type){
570 case ICMP_ECHOREPLY:
571 header->type = ICMP_ECHO;
572 continue;
573 }
574 }
575 return EINVAL;
576 }
577 }
578 switch(header->type){
579 case ICMP_ECHOREPLY:
580 if(error){
581 return icmp_process_echo_reply(packet, header, type, code);
582 }else{
583 return icmp_process_echo_reply(packet, header, ICMP_ECHO, 0);
584 }
585 case ICMP_ECHO:
586 if(error){
587 return icmp_process_echo_reply(packet, header, type, code);
588 // do not send a reply if disabled
589 }else if(icmp_globals.echo_replying){
590 addrlen = packet_get_addr(packet, &src, NULL);
591 if((addrlen > 0)
592 // set both addresses to the source one (avoids the source address deletion before setting the destination one)
593 && (packet_set_addr(packet, src, src, (size_t) addrlen) == EOK)){
594 // send the reply
595 icmp_send_packet(ICMP_ECHOREPLY, 0, packet, header, 0, 0, 0, 0);
596 return EOK;
597 }else{
598 return EINVAL;
599 }
600 }else{
601 return EPERM;
602 }
603 case ICMP_DEST_UNREACH:
604 case ICMP_SOURCE_QUENCH:
605 case ICMP_REDIRECT:
606 case ICMP_ALTERNATE_ADDR:
607 case ICMP_ROUTER_ADV:
608 case ICMP_ROUTER_SOL:
609 case ICMP_TIME_EXCEEDED:
610 case ICMP_PARAMETERPROB:
611 case ICMP_CONVERSION_ERROR:
612 case ICMP_REDIRECT_MOBILE:
613 case ICMP_SKIP:
614 case ICMP_PHOTURIS:
615 ip_received_error_msg(icmp_globals.ip_phone, -1, packet, SERVICE_IP, SERVICE_ICMP);
616 return EOK;
617 default:
618 return ENOTSUP;
619 }
620}
621
622int icmp_process_echo_reply(packet_t packet, icmp_header_ref header, icmp_type_t type, icmp_code_t code){
623 int reply_key;
624 icmp_reply_ref reply;
625
626 // compute the reply key
627 reply_key = ICMP_GET_REPLY_KEY(header->un.echo.identifier, header->un.echo.sequence_number);
628 pq_release(icmp_globals.net_phone, packet_get_id(packet));
629 // lock the globals
630 fibril_rwlock_write_lock(&icmp_globals.lock);
631 // find the pending reply
632 reply = icmp_replies_find(&icmp_globals.replies, reply_key);
633 if(reply){
634 // set the result
635 reply->result = type;
636 // notify the waiting fibril
637 fibril_condvar_signal(&reply->condvar);
638 }
639 fibril_rwlock_write_unlock(&icmp_globals.lock);
640 return EOK;
641}
642
643int icmp_message(ipc_callid_t callid, ipc_call_t * call, ipc_call_t * answer, int * answer_count){
644 ERROR_DECLARE;
645
646 packet_t packet;
647
648 *answer_count = 0;
649 switch(IPC_GET_METHOD(*call)){
650 case NET_TL_RECEIVED:
651 if(! ERROR_OCCURRED(packet_translate(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){
652 ERROR_CODE = icmp_received_msg(IPC_GET_DEVICE(call), packet, SERVICE_ICMP, IPC_GET_ERROR(call));
653 }
654 return ERROR_CODE;
655 case NET_ICMP_INIT:
656 return icmp_process_client_messages(callid, * call);
657 default:
658 return icmp_process_message(call);
659 }
660 return ENOTSUP;
661}
662
663int icmp_process_client_messages(ipc_callid_t callid, ipc_call_t call){
664 ERROR_DECLARE;
665
666 bool keep_on_going = true;
667// fibril_rwlock_t lock;
668 ipc_call_t answer;
669 int answer_count;
670 size_t length;
671 struct sockaddr * addr;
672 ipc_callid_t data_callid;
673 icmp_echo_ref echo_data;
674 int res;
675
676 /*
677 * Accept the connection
678 * - Answer the first NET_ICMP_INIT call.
679 */
680 res = EOK;
681 answer_count = 0;
682
683// fibril_rwlock_initialize(&lock);
684
685 echo_data = (icmp_echo_ref) malloc(sizeof(*echo_data));
686 if(! echo_data){
687 return ENOMEM;
688 }
689
690 // assign a new identifier
691 fibril_rwlock_write_lock(&icmp_globals.lock);
692 res = icmp_bind_free_id(echo_data);
693 fibril_rwlock_write_unlock(&icmp_globals.lock);
694 if(res < 0){
695 free(echo_data);
696 return res;
697 }
698
699 while(keep_on_going){
700
701 // answer the call
702 answer_call(callid, res, &answer, answer_count);
703
704 // refresh data
705 refresh_answer(&answer, &answer_count);
706
707 // get the next call
708 callid = async_get_call(&call);
709
710 // process the call
711 switch(IPC_GET_METHOD(call)){
712 case IPC_M_PHONE_HUNGUP:
713 keep_on_going = false;
714 res = EHANGUP;
715 break;
716 case NET_ICMP_ECHO:
717// fibril_rwlock_write_lock(&lock);
718 if(! async_data_write_receive(&data_callid, &length)){
719 res = EINVAL;
720 }else{
721 addr = malloc(length);
722 if(! addr){
723 res = ENOMEM;
724 }else{
725 if(! ERROR_OCCURRED(async_data_write_finalize(data_callid, addr, length))){
726 fibril_rwlock_write_lock(&icmp_globals.lock);
727 res = icmp_echo(echo_data->identifier, echo_data->sequence_number, ICMP_GET_SIZE(call), ICMP_GET_TIMEOUT(call), ICMP_GET_TTL(call), ICMP_GET_TOS(call), ICMP_GET_DONT_FRAGMENT(call), addr, (socklen_t) length);
728 fibril_rwlock_write_unlock(&icmp_globals.lock);
729 free(addr);
730 if(echo_data->sequence_number < MAX_UINT16){
731 ++ echo_data->sequence_number;
732 }else{
733 echo_data->sequence_number = 0;
734 }
735 }else{
736 res = ERROR_CODE;
737 }
738 }
739 }
740// fibril_rwlock_write_unlock(&lock);
741 break;
742 default:
743 res = icmp_process_message(&call);
744 }
745 }
746
747 // release the identifier
748 fibril_rwlock_write_lock(&icmp_globals.lock);
749 icmp_echo_data_exclude(&icmp_globals.echo_data, echo_data->identifier);
750 fibril_rwlock_write_unlock(&icmp_globals.lock);
751 return res;
752}
753
754int icmp_process_message(ipc_call_t * call){
755 ERROR_DECLARE;
756
757 packet_t packet;
758
759 switch(IPC_GET_METHOD(*call)){
760 case NET_ICMP_DEST_UNREACH:
761 if(! ERROR_OCCURRED(packet_translate(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){
762 ERROR_CODE = icmp_destination_unreachable_msg(0, ICMP_GET_CODE(call), ICMP_GET_MTU(call), packet);
763 }
764 return ERROR_CODE;
765 case NET_ICMP_SOURCE_QUENCH:
766 if(! ERROR_OCCURRED(packet_translate(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){
767 ERROR_CODE = icmp_source_quench_msg(0, packet);
768 }
769 return ERROR_CODE;
770 case NET_ICMP_TIME_EXCEEDED:
771 if(! ERROR_OCCURRED(packet_translate(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){
772 ERROR_CODE = icmp_time_exceeded_msg(0, ICMP_GET_CODE(call), packet);
773 }
774 return ERROR_CODE;
775 case NET_ICMP_PARAMETERPROB:
776 if(! ERROR_OCCURRED(packet_translate(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){
777 ERROR_CODE = icmp_parameter_problem_msg(0, ICMP_GET_CODE(call), ICMP_GET_POINTER(call), packet);
778 }
779 return ERROR_CODE;
780 default:
781 return ENOTSUP;
782 }
783}
784
785int icmp_release_and_return(packet_t packet, int result){
786 pq_release(icmp_globals.net_phone, packet_get_id(packet));
787 return result;
788}
789
790int icmp_bind_free_id(icmp_echo_ref echo_data){
791 icmp_param_t index;
792
793 if(! echo_data){
794 return EBADMEM;
795 }
796 // from the last used one
797 index = icmp_globals.last_used_id;
798 do{
799 ++ index;
800 // til the range end
801 if(index >= ICMP_FREE_IDS_END){
802 // start from the range beginning
803 index = ICMP_FREE_IDS_START - 1;
804 do{
805 ++ index;
806 // til the last used one
807 if(index >= icmp_globals.last_used_id){
808 // none found
809 return ENOTCONN;
810 }
811 }while(icmp_echo_data_find(&icmp_globals.echo_data, index) != NULL);
812 // found, break immediately
813 break;
814 }
815 }while(icmp_echo_data_find(&icmp_globals.echo_data, index) != NULL);
816 echo_data->identifier = index;
817 echo_data->sequence_number = 0;
818 return icmp_echo_data_add(&icmp_globals.echo_data, index, echo_data);
819}
820
821/** @}
822 */
Note: See TracBrowser for help on using the repository browser.