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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 851f33a was 9539be6, checked in by Martin Decky <martin@…>, 16 years ago

autotool now also detects values of UINT_MAX and friends

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