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

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