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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 46ae62c was d8f95529, checked in by Jakub Jermar <jakub@…>, 15 years ago

Get rid of the ERROR_CODE madness in icmp.

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