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

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

Do not leak the 'names' measured string values.

  • Property mode set to 100644
File size: 25.8 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#include <err.h>
57
58#include <net/socket_codes.h>
59#include <net/ip_protocols.h>
60#include <net/inet.h>
61#include <net/modules.h>
62#include <net/icmp_api.h>
63#include <net/icmp_codes.h>
64#include <net/icmp_common.h>
65
66#include <packet_client.h>
67#include <packet_remote.h>
68#include <net_checksum.h>
69#include <icmp_client.h>
70#include <icmp_interface.h>
71#include <il_interface.h>
72#include <ip_client.h>
73#include <ip_interface.h>
74#include <net_interface.h>
75#include <tl_interface.h>
76#include <tl_local.h>
77#include <icmp_header.h>
78
79/** ICMP module name. */
80#define NAME "ICMP protocol"
81
82/** Default ICMP error reporting. */
83#define NET_DEFAULT_ICMP_ERROR_REPORTING true
84
85/** Default ICMP echo replying. */
86#define NET_DEFAULT_ICMP_ECHO_REPLYING true
87
88/** Original datagram length in bytes transfered to the error notification
89 * message.
90 */
91#define ICMP_KEEP_LENGTH 8
92
93/** Free identifier numbers pool start. */
94#define ICMP_FREE_IDS_START 1
95
96/** Free identifier numbers pool end. */
97#define ICMP_FREE_IDS_END UINT16_MAX
98
99/** Computes the ICMP datagram checksum.
100 *
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) \
106 htons(ip_checksum((uint8_t *) (header), (length)))
107
108/** An echo request datagrams pattern. */
109#define ICMP_ECHO_TEXT "Hello from HelenOS."
110
111/** Computes an ICMP reply data key.
112 *
113 * @param[in] id The message identifier.
114 * @param[in] sequence The message sequence number.
115 * @returns The computed ICMP reply data key.
116 */
117#define ICMP_GET_REPLY_KEY(id, sequence) \
118 (((id) << 16) | (sequence & 0xFFFF))
119
120
121/** ICMP global data. */
122icmp_globals_t icmp_globals;
123
124INT_MAP_IMPLEMENT(icmp_replies, icmp_reply_t);
125INT_MAP_IMPLEMENT(icmp_echo_data, icmp_echo_t);
126
127/** Releases the packet and returns the result.
128 *
129 * @param[in] packet The packet queue to be released.
130 * @param[in] result The result to be returned.
131 * @returns The result parameter.
132 */
133static int icmp_release_and_return(packet_t packet, int result)
134{
135 pq_release_remote(icmp_globals.net_phone, packet_get_id(packet));
136 return result;
137}
138
139/** Sends the ICMP message.
140 *
141 * Sets the message type and code and computes the checksum.
142 * Error messages are sent only if allowed in the configuration.
143 * Releases the packet on errors.
144 *
145 * @param[in] type The message type.
146 * @param[in] code The message code.
147 * @param[in] packet The message packet to be sent.
148 * @param[in] header The ICMP header.
149 * @param[in] error The error service to be announced. Should be
150 * SERVICE_ICMP or zero.
151 * @param[in] ttl The time to live.
152 * @param[in] tos The type of service.
153 * @param[in] dont_fragment The value indicating whether the datagram must not
154 * be fragmented. Is used as a MTU discovery.
155 * @returns EOK on success.
156 * @returns EPERM if the error message is not allowed.
157 */
158static int
159icmp_send_packet(icmp_type_t type, icmp_code_t code, packet_t packet,
160 icmp_header_ref header, services_t error, ip_ttl_t ttl, ip_tos_t tos,
161 int dont_fragment)
162{
163 ERROR_DECLARE;
164
165 // do not send an error if disabled
166 if (error && !icmp_globals.error_reporting)
167 return icmp_release_and_return(packet, EPERM);
168
169 header->type = type;
170 header->code = code;
171 header->checksum = 0;
172 header->checksum = ICMP_CHECKSUM(header,
173 packet_get_data_length(packet));
174 if (ERROR_OCCURRED(ip_client_prepare_packet(packet, IPPROTO_ICMP, ttl,
175 tos, dont_fragment, 0))) {
176 return icmp_release_and_return(packet, ERROR_CODE);
177 }
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 ERROR_DECLARE;
252
253 icmp_header_ref header;
254 packet_t packet;
255 size_t length;
256 uint8_t *data;
257 icmp_reply_ref reply;
258 int reply_key;
259 int result;
260 int index;
261
262 if (addrlen <= 0)
263 return EINVAL;
264
265 length = (size_t) addrlen;
266 // TODO do not ask all the time
267 ERROR_PROPAGATE(ip_packet_size_req(icmp_globals.ip_phone, -1,
268 &icmp_globals.packet_dimension));
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 if (ERROR_OCCURRED(packet_set_addr(packet, NULL, (const uint8_t *) addr,
280 length))) {
281 return icmp_release_and_return(packet, ERROR_CODE);
282 }
283
284 // allocate space in the packet
285 data = (uint8_t *) packet_suffix(packet, size);
286 if (!data)
287 return icmp_release_and_return(packet, ENOMEM);
288
289 // fill the data
290 length = 0;
291 while (size > length + sizeof(ICMP_ECHO_TEXT)) {
292 memcpy(data + length, ICMP_ECHO_TEXT, sizeof(ICMP_ECHO_TEXT));
293 length += sizeof(ICMP_ECHO_TEXT);
294 }
295 memcpy(data + length, ICMP_ECHO_TEXT, size - length);
296
297 // prefix the header
298 header = PACKET_PREFIX(packet, icmp_header_t);
299 if (!header)
300 return icmp_release_and_return(packet, ENOMEM);
301
302 bzero(header, sizeof(*header));
303 header->un.echo.identifier = id;
304 header->un.echo.sequence_number = sequence;
305
306 // prepare the reply structure
307 reply = malloc(sizeof(*reply));
308 if (!reply)
309 return icmp_release_and_return(packet, ENOMEM);
310
311 fibril_mutex_initialize(&reply->mutex);
312 fibril_mutex_lock(&reply->mutex);
313 fibril_condvar_initialize(&reply->condvar);
314 reply_key = ICMP_GET_REPLY_KEY(header->un.echo.identifier,
315 header->un.echo.sequence_number);
316 index = icmp_replies_add(&icmp_globals.replies, reply_key, reply);
317 if (index < 0) {
318 free(reply);
319 return icmp_release_and_return(packet, index);
320 }
321
322 // unlock the globals so that we can wait for the reply
323 fibril_rwlock_write_unlock(&icmp_globals.lock);
324
325 // send the request
326 icmp_send_packet(ICMP_ECHO, 0, packet, header, 0, ttl, tos,
327 dont_fragment);
328
329 // wait for the reply
330 // timeout in microseconds
331 if (ERROR_OCCURRED(fibril_condvar_wait_timeout(&reply->condvar,
332 &reply->mutex, timeout * 1000))) {
333 result = ERROR_CODE;
334 } else {
335 // read the result
336 result = reply->result;
337 }
338
339 // drop the reply mutex before locking the globals again
340 fibril_mutex_unlock(&reply->mutex);
341 fibril_rwlock_write_lock(&icmp_globals.lock);
342
343 // destroy the reply structure
344 icmp_replies_exclude_index(&icmp_globals.replies, index);
345
346 return result;
347}
348
349static int
350icmp_destination_unreachable_msg_local(int icmp_phone, icmp_code_t code,
351 icmp_param_t mtu, packet_t packet)
352{
353 icmp_header_ref header;
354
355 header = icmp_prepare_packet(packet);
356 if (!header)
357 return icmp_release_and_return(packet, ENOMEM);
358
359 if (mtu)
360 header->un.frag.mtu = mtu;
361
362 return icmp_send_packet(ICMP_DEST_UNREACH, code, packet, header,
363 SERVICE_ICMP, 0, 0, 0);
364}
365
366static int icmp_source_quench_msg_local(int icmp_phone, packet_t packet)
367{
368 icmp_header_ref header;
369
370 header = icmp_prepare_packet(packet);
371 if (!header)
372 return icmp_release_and_return(packet, ENOMEM);
373
374 return icmp_send_packet(ICMP_SOURCE_QUENCH, 0, packet, header,
375 SERVICE_ICMP, 0, 0, 0);
376}
377
378static int
379icmp_time_exceeded_msg_local(int icmp_phone, icmp_code_t code, packet_t packet)
380{
381 icmp_header_ref header;
382
383 header = icmp_prepare_packet(packet);
384 if (!header)
385 return icmp_release_and_return(packet, ENOMEM);
386
387 return icmp_send_packet(ICMP_TIME_EXCEEDED, code, packet, header,
388 SERVICE_ICMP, 0, 0, 0);
389}
390
391static int
392icmp_parameter_problem_msg_local(int icmp_phone, icmp_code_t code,
393 icmp_param_t pointer, packet_t packet)
394{
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,
403 SERVICE_ICMP, 0, 0, 0);
404}
405
406/** Initializes the ICMP module.
407 *
408 * @param[in] client_connection The client connection processing function. The
409 * module skeleton propagates its own one.
410 * @returns EOK on success.
411 * @returns ENOMEM if there is not enough memory left.
412 */
413int icmp_initialize(async_client_conn_t client_connection)
414{
415 ERROR_DECLARE;
416
417 measured_string_t names[] = {
418 {
419 (char *) "ICMP_ERROR_REPORTING",
420 20
421 },
422 {
423 (char *) "ICMP_ECHO_REPLYING",
424 18
425 }
426 };
427 measured_string_ref configuration;
428 size_t count = sizeof(names) / sizeof(measured_string_t);
429 char *data;
430
431 fibril_rwlock_initialize(&icmp_globals.lock);
432 fibril_rwlock_write_lock(&icmp_globals.lock);
433 icmp_replies_initialize(&icmp_globals.replies);
434 icmp_echo_data_initialize(&icmp_globals.echo_data);
435 icmp_globals.ip_phone = ip_bind_service(SERVICE_IP, IPPROTO_ICMP,
436 SERVICE_ICMP, client_connection);
437 if (icmp_globals.ip_phone < 0)
438 return icmp_globals.ip_phone;
439
440 ERROR_PROPAGATE(ip_packet_size_req(icmp_globals.ip_phone, -1,
441 &icmp_globals.packet_dimension));
442 icmp_globals.packet_dimension.prefix += ICMP_HEADER_SIZE;
443 icmp_globals.packet_dimension.content -= ICMP_HEADER_SIZE;
444
445 icmp_globals.error_reporting = NET_DEFAULT_ICMP_ERROR_REPORTING;
446 icmp_globals.echo_replying = NET_DEFAULT_ICMP_ECHO_REPLYING;
447
448 // get configuration
449 configuration = &names[0];
450 ERROR_PROPAGATE(net_get_conf_req(icmp_globals.net_phone, &configuration,
451 count, &data));
452 if (configuration) {
453 if (configuration[0].value) {
454 icmp_globals.error_reporting =
455 (configuration[0].value[0] == 'y');
456 }
457 if (configuration[1].value) {
458 icmp_globals.echo_replying =
459 (configuration[1].value[0] == 'y');
460 }
461 net_free_settings(configuration, data);
462 }
463
464 fibril_rwlock_write_unlock(&icmp_globals.lock);
465 return EOK;
466}
467
468/** Tries to set the pending reply result as the received message type.
469 *
470 * If the reply data is not present, the reply timed out and the other fibril
471 * is already awake.
472 * Releases the packet.
473 *
474 * @param[in] packet The received reply message.
475 * @param[in] header The ICMP message header.
476 * @param[in] type The received reply message type.
477 * @param[in] code The received reply message code.
478 */
479static void
480icmp_process_echo_reply(packet_t packet, icmp_header_ref header,
481 icmp_type_t type, icmp_code_t code)
482{
483 int reply_key;
484 icmp_reply_ref reply;
485
486 // compute the reply key
487 reply_key = ICMP_GET_REPLY_KEY(header->un.echo.identifier,
488 header->un.echo.sequence_number);
489 pq_release_remote(icmp_globals.net_phone, packet_get_id(packet));
490
491 fibril_rwlock_write_lock(&icmp_globals.lock);
492 // find the pending reply
493 reply = icmp_replies_find(&icmp_globals.replies, reply_key);
494 if (reply) {
495 reply->result = type;
496 fibril_condvar_signal(&reply->condvar);
497 }
498 fibril_rwlock_write_unlock(&icmp_globals.lock);
499}
500
501/** Processes the received ICMP packet.
502 *
503 * Notifies the destination socket application.
504 *
505 * @param[in,out] packet The received packet.
506 * @param[in] error The packet error reporting service. Prefixes the
507 * received packet.
508 * @returns EOK on success.
509 * @returns EINVAL if the packet is not valid.
510 * @returns EINVAL if the stored packet address is not the an_addr_t.
511 * @returns EINVAL if the packet does not contain any data.
512 * @returns NO_DATA if the packet content is shorter than the user
513 * datagram header.
514 * @returns ENOMEM if there is not enough memory left.
515 * @returns EADDRNOTAVAIL if the destination socket does not exist.
516 * @returns Other error codes as defined for the
517 * ip_client_process_packet() function.
518 */
519static int icmp_process_packet(packet_t packet, services_t error)
520{
521 ERROR_DECLARE;
522
523 size_t length;
524 uint8_t *src;
525 int addrlen;
526 int result;
527 void *data;
528 icmp_header_ref header;
529 icmp_type_t type;
530 icmp_code_t code;
531
532 switch (error) {
533 case SERVICE_NONE:
534 break;
535 case SERVICE_ICMP:
536 // process error
537 result = icmp_client_process_packet(packet, &type, &code, NULL,
538 NULL);
539 if (result < 0)
540 return result;
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
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
582 switch (header->type) {
583 case ICMP_ECHOREPLY:
584 if (error)
585 icmp_process_echo_reply(packet, header, type, code);
586 else
587 icmp_process_echo_reply(packet, header, ICMP_ECHO, 0);
588
589 return EOK;
590
591 case ICMP_ECHO:
592 if (error) {
593 icmp_process_echo_reply(packet, header, type, code);
594 return EOK;
595 }
596
597 // do not send a reply if disabled
598 if (icmp_globals.echo_replying) {
599 addrlen = packet_get_addr(packet, &src, NULL);
600
601 // set both addresses to the source one (avoids the
602 // source address deletion before setting the
603 // destination one)
604 if ((addrlen > 0) && (packet_set_addr(packet, src, src,
605 (size_t) addrlen) == EOK)) {
606 // send the reply
607 icmp_send_packet(ICMP_ECHOREPLY, 0, packet,
608 header, 0, 0, 0, 0);
609 return EOK;
610 }
611
612 return EINVAL;
613 }
614
615 return EPERM;
616
617 case ICMP_DEST_UNREACH:
618 case ICMP_SOURCE_QUENCH:
619 case ICMP_REDIRECT:
620 case ICMP_ALTERNATE_ADDR:
621 case ICMP_ROUTER_ADV:
622 case ICMP_ROUTER_SOL:
623 case ICMP_TIME_EXCEEDED:
624 case ICMP_PARAMETERPROB:
625 case ICMP_CONVERSION_ERROR:
626 case ICMP_REDIRECT_MOBILE:
627 case ICMP_SKIP:
628 case ICMP_PHOTURIS:
629 ip_received_error_msg(icmp_globals.ip_phone, -1, packet,
630 SERVICE_IP, SERVICE_ICMP);
631 return EOK;
632
633 default:
634 return ENOTSUP;
635 }
636}
637
638/** Processes the received ICMP packet.
639 *
640 * Is used as an entry point from the underlying IP module.
641 * Releases the packet on error.
642 *
643 * @param device_id The device identifier. Ignored parameter.
644 * @param[in,out] packet The received packet.
645 * @param receiver The target service. Ignored parameter.
646 * @param[in] error The packet error reporting service. Prefixes the
647 * received packet.
648 * @returns EOK on success.
649 * @returns Other error codes as defined for the
650 * icmp_process_packet() function.
651 */
652static int
653icmp_received_msg_local(device_id_t device_id, packet_t packet,
654 services_t receiver, services_t error)
655{
656 ERROR_DECLARE;
657
658 if (ERROR_OCCURRED(icmp_process_packet(packet, error)))
659 return icmp_release_and_return(packet, ERROR_CODE);
660
661 return EOK;
662}
663
664/** Processes the generic client messages.
665 *
666 * @param[in] call The message parameters.
667 * @returns EOK on success.
668 * @returns ENOTSUP if the message is not known.
669 * @returns Other error codes as defined for the packet_translate()
670 * function.
671 * @returns Other error codes as defined for the
672 * icmp_destination_unreachable_msg_local() function.
673 * @returns Other error codes as defined for the
674 * icmp_source_quench_msg_local() function.
675 * @returns Other error codes as defined for the
676 * icmp_time_exceeded_msg_local() function.
677 * @returns Other error codes as defined for the
678 * icmp_parameter_problem_msg_local() function.
679 *
680 * @see icmp_interface.h
681 */
682static int icmp_process_message(ipc_call_t *call)
683{
684 ERROR_DECLARE;
685
686 packet_t packet;
687
688 switch (IPC_GET_METHOD(*call)) {
689 case NET_ICMP_DEST_UNREACH:
690 if (ERROR_NONE(packet_translate_remote(icmp_globals.net_phone,
691 &packet, IPC_GET_PACKET(call)))) {
692 ERROR_CODE = icmp_destination_unreachable_msg_local(0,
693 ICMP_GET_CODE(call), ICMP_GET_MTU(call), packet);
694 }
695 return ERROR_CODE;
696 case NET_ICMP_SOURCE_QUENCH:
697 if (ERROR_NONE(packet_translate_remote(icmp_globals.net_phone,
698 &packet, IPC_GET_PACKET(call)))) {
699 ERROR_CODE = icmp_source_quench_msg_local(0, packet);
700 }
701 return ERROR_CODE;
702 case NET_ICMP_TIME_EXCEEDED:
703 if (ERROR_NONE(packet_translate_remote(icmp_globals.net_phone,
704 &packet, IPC_GET_PACKET(call)))) {
705 ERROR_CODE = icmp_time_exceeded_msg_local(0,
706 ICMP_GET_CODE(call), packet);
707 }
708 return ERROR_CODE;
709 case NET_ICMP_PARAMETERPROB:
710 if (ERROR_NONE(packet_translate_remote(icmp_globals.net_phone,
711 &packet, IPC_GET_PACKET(call)))) {
712 ERROR_CODE = icmp_parameter_problem_msg_local(0,
713 ICMP_GET_CODE(call), ICMP_GET_POINTER(call),
714 packet);
715 }
716 return ERROR_CODE;
717 default:
718 return ENOTSUP;
719 }
720}
721
722/** Assigns a new identifier for the connection.
723 *
724 * Fills the echo data parameter with the assigned values.
725 *
726 * @param[in,out] echo_data The echo data to be bound.
727 * @returns Index of the inserted echo data.
728 * @returns EBADMEM if the echo_data parameter is NULL.
729 * @returns ENOTCONN if no free identifier have been found.
730 */
731static int icmp_bind_free_id(icmp_echo_ref echo_data)
732{
733 icmp_param_t index;
734
735 if (!echo_data)
736 return EBADMEM;
737
738 // from the last used one
739 index = icmp_globals.last_used_id;
740 do {
741 index++;
742 // til the range end
743 if (index >= ICMP_FREE_IDS_END) {
744 // start from the range beginning
745 index = ICMP_FREE_IDS_START - 1;
746 do {
747 index++;
748 // til the last used one
749 if (index >= icmp_globals.last_used_id) {
750 // none found
751 return ENOTCONN;
752 }
753 } while(icmp_echo_data_find(&icmp_globals.echo_data,
754 index) != NULL);
755
756 // found, break immediately
757 break;
758 }
759 } while(icmp_echo_data_find(&icmp_globals.echo_data, index) != NULL);
760
761 echo_data->identifier = index;
762 echo_data->sequence_number = 0;
763
764 return icmp_echo_data_add(&icmp_globals.echo_data, index, echo_data);
765}
766
767/** Processes the client messages.
768 *
769 * Remembers the assigned identifier and sequence numbers.
770 * Runs until the client module disconnects.
771 *
772 * @param[in] callid The message identifier.
773 * @param[in] call The message parameters.
774 * @returns EOK.
775 *
776 * @see icmp_interface.h
777 * @see icmp_api.h
778 */
779static int icmp_process_client_messages(ipc_callid_t callid, ipc_call_t call)
780{
781 ERROR_DECLARE;
782
783 bool keep_on_going = true;
784 ipc_call_t answer;
785 int answer_count;
786 size_t length;
787 struct sockaddr *addr;
788 ipc_callid_t data_callid;
789 icmp_echo_ref echo_data;
790 int res;
791
792 /*
793 * Accept the connection
794 * - Answer the first NET_ICMP_INIT call.
795 */
796 res = EOK;
797 answer_count = 0;
798
799 echo_data = (icmp_echo_ref) malloc(sizeof(*echo_data));
800 if (!echo_data)
801 return ENOMEM;
802
803 // assign a new identifier
804 fibril_rwlock_write_lock(&icmp_globals.lock);
805 res = icmp_bind_free_id(echo_data);
806 fibril_rwlock_write_unlock(&icmp_globals.lock);
807 if (res < 0) {
808 free(echo_data);
809 return res;
810 }
811
812 while (keep_on_going) {
813 // answer the call
814 answer_call(callid, res, &answer, answer_count);
815
816 // refresh data
817 refresh_answer(&answer, &answer_count);
818
819 // get the next call
820 callid = async_get_call(&call);
821
822 // process the call
823 switch (IPC_GET_METHOD(call)) {
824 case IPC_M_PHONE_HUNGUP:
825 keep_on_going = false;
826 res = EHANGUP;
827 break;
828
829 case NET_ICMP_ECHO:
830 if (!async_data_write_receive(&data_callid, &length)) {
831 res = EINVAL;
832 break;
833 }
834
835 addr = malloc(length);
836 if (!addr) {
837 res = ENOMEM;
838 break;
839 }
840
841 if (ERROR_OCCURRED(async_data_write_finalize(
842 data_callid, addr, length))) {
843 free(addr);
844 res = ERROR_CODE;
845 break;
846 }
847
848 fibril_rwlock_write_lock(&icmp_globals.lock);
849 res = icmp_echo(echo_data->identifier,
850 echo_data->sequence_number, ICMP_GET_SIZE(call),
851 ICMP_GET_TIMEOUT(call), ICMP_GET_TTL(call),
852 ICMP_GET_TOS(call), ICMP_GET_DONT_FRAGMENT(call),
853 addr, (socklen_t) length);
854 fibril_rwlock_write_unlock(&icmp_globals.lock);
855
856 free(addr);
857
858 if (echo_data->sequence_number < UINT16_MAX)
859 echo_data->sequence_number++;
860 else
861 echo_data->sequence_number = 0;
862
863 break;
864
865 default:
866 res = icmp_process_message(&call);
867 }
868
869 }
870
871 // release the identifier
872 fibril_rwlock_write_lock(&icmp_globals.lock);
873 icmp_echo_data_exclude(&icmp_globals.echo_data, echo_data->identifier);
874 fibril_rwlock_write_unlock(&icmp_globals.lock);
875
876 return res;
877}
878
879/** Processes the ICMP message.
880 *
881 * @param[in] callid The message identifier.
882 * @param[in] call The message parameters.
883 * @param[out] answer The message answer parameters.
884 * @param[out] answer_count The last parameter for the actual answer in the
885 * answer parameter.
886 * @returns EOK on success.
887 * @returns ENOTSUP if the message is not known.
888 *
889 * @see icmp_interface.h
890 * @see IS_NET_ICMP_MESSAGE()
891 */
892int
893icmp_message_standalone(ipc_callid_t callid, ipc_call_t *call,
894 ipc_call_t *answer, int *answer_count)
895{
896 ERROR_DECLARE;
897
898 packet_t packet;
899
900 *answer_count = 0;
901 switch (IPC_GET_METHOD(*call)) {
902 case NET_TL_RECEIVED:
903 if (ERROR_NONE(packet_translate_remote(icmp_globals.net_phone,
904 &packet, IPC_GET_PACKET(call)))) {
905 ERROR_CODE =
906 icmp_received_msg_local(IPC_GET_DEVICE(call),
907 packet, SERVICE_ICMP, IPC_GET_ERROR(call));
908 }
909 return ERROR_CODE;
910
911 case NET_ICMP_INIT:
912 return icmp_process_client_messages(callid, * call);
913
914 default:
915 return icmp_process_message(call);
916 }
917
918 return ENOTSUP;
919}
920
921
922/** Default thread for new connections.
923 *
924 * @param[in] iid The initial message identifier.
925 * @param[in] icall The initial message call structure.
926 *
927 */
928static void tl_client_connection(ipc_callid_t iid, ipc_call_t *icall)
929{
930 /*
931 * Accept the connection
932 * - Answer the first IPC_M_CONNECT_ME_TO call.
933 */
934 ipc_answer_0(iid, EOK);
935
936 while (true) {
937 ipc_call_t answer;
938 int answer_count;
939
940 /* Clear the answer structure */
941 refresh_answer(&answer, &answer_count);
942
943 /* Fetch the next message */
944 ipc_call_t call;
945 ipc_callid_t callid = async_get_call(&call);
946
947 /* Process the message */
948 int res = tl_module_message_standalone(callid, &call, &answer,
949 &answer_count);
950
951 /*
952 * End if told to either by the message or the processing
953 * result.
954 */
955 if ((IPC_GET_METHOD(call) == IPC_M_PHONE_HUNGUP) ||
956 (res == EHANGUP))
957 return;
958
959 /* Answer the message */
960 answer_call(callid, res, &answer, answer_count);
961 }
962}
963
964/** Starts the module.
965 *
966 * @returns EOK on success.
967 * @returns Other error codes as defined for each specific module
968 * start function.
969 */
970int main(int argc, char *argv[])
971{
972 ERROR_DECLARE;
973
974 /* Start the module */
975 ERROR_PROPAGATE(tl_module_start_standalone(tl_client_connection));
976 return EOK;
977}
978
979/** @}
980 */
981
Note: See TracBrowser for help on using the repository browser.