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

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

streamline and create a common skeleton for the transport layer
(in the line of the previous updates to the lower layers)

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