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

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

improve cstyle and comments

  • Property mode set to 100644
File size: 20.1 KB
RevLine 
[21580dd]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
[edc5a985]30 * @{
[21580dd]31 */
32
33/** @file
[edc5a985]34 * ICMP module implementation.
[21580dd]35 */
36
37#include <async.h>
38#include <atomic.h>
39#include <fibril.h>
40#include <fibril_synch.h>
41#include <stdint.h>
[19f857a]42#include <str.h>
[21580dd]43#include <ipc/ipc.h>
44#include <ipc/services.h>
[514ee46]45#include <ipc/net.h>
[8e3a65c]46#include <ipc/tl.h>
[753bca3]47#include <ipc/icmp.h>
[1a0fb3f8]48#include <sys/time.h>
[21580dd]49#include <sys/types.h>
[2687bdb]50#include <byteorder.h>
[e98b1d5]51#include <errno.h>
[f1938c6]52#include <adt/hash_table.h>
[21580dd]53
[058edb6]54#include <net/socket_codes.h>
[fe5d3c1b]55#include <net/ip_protocols.h>
[e4554d4]56#include <net/inet.h>
[c7a8442]57#include <net/modules.h>
[edc5a985]58#include <net/icmp_api.h>
59#include <net/icmp_codes.h>
60#include <net/icmp_common.h>
61
[0a866eeb]62#include <packet_client.h>
[14f1db0]63#include <packet_remote.h>
[849ed54]64#include <net_checksum.h>
65#include <icmp_client.h>
[f1938c6]66#include <icmp_remote.h>
[797b704]67#include <il_remote.h>
[849ed54]68#include <ip_client.h>
69#include <ip_interface.h>
70#include <net_interface.h>
[014dd57b]71#include <tl_remote.h>
72#include <tl_skel.h>
[849ed54]73#include <icmp_header.h>
[21580dd]74
[f1938c6]75/** ICMP module name */
[014dd57b]76#define NAME "icmp"
[849ed54]77
[f1938c6]78/** Number of replies hash table keys */
79#define REPLY_KEYS 2
[21580dd]80
[f1938c6]81/** Number of replies hash table buckets */
82#define REPLY_BUCKETS 1024
[21580dd]83
[f1938c6]84/**
85 * Original datagram length in bytes transfered to the error
86 * notification message.
[21580dd]87 */
[f1938c6]88#define ICMP_KEEP_LENGTH 8
[21580dd]89
[f1938c6]90/** Compute the ICMP datagram checksum.
91 *
92 * @param[in,out] header ICMP datagram header.
93 * @param[in] length Total datagram length.
94 *
95 * @return Computed checksum.
[edc5a985]96 *
[21580dd]97 */
[edc5a985]98#define ICMP_CHECKSUM(header, length) \
99 htons(ip_checksum((uint8_t *) (header), (length)))
[21580dd]100
[edc5a985]101/** An echo request datagrams pattern. */
[f1938c6]102#define ICMP_ECHO_TEXT "ICMP hello from HelenOS."
[21580dd]103
[f1938c6]104/** ICMP reply data. */
105typedef struct {
106 /** Hash table link */
107 link_t link;
108
109 /** Reply identification and sequence */
110 icmp_param_t id;
111 icmp_param_t sequence;
112
113 /** Reply signaling */
114 fibril_condvar_t condvar;
115
116 /** Reply result */
117 int result;
118} icmp_reply_t;
119
120/** Global data */
121static int phone_net = -1;
122static int phone_ip = -1;
123static bool error_reporting = true;
124static bool echo_replying = true;
[ffaba00]125static packet_dimension_t icmp_dimension;
[f1938c6]126
127/** ICMP client identification counter */
128static atomic_t icmp_client;
129
[ffaba00]130/** ICMP identifier and sequence number (client-specific) */
[f1938c6]131static fibril_local icmp_param_t icmp_id;
132static fibril_local icmp_param_t icmp_seq;
133
134/** Reply hash table */
135static fibril_mutex_t reply_lock;
136static hash_table_t replies;
137
138static hash_index_t replies_hash(unsigned long key[])
139{
140 /*
141 * ICMP identifier and sequence numbers
142 * are 16-bit values.
143 */
[ffaba00]144 hash_index_t index = ((key[0] & 0xffff) << 16) | (key[1] & 0xffff);
145 return (index % REPLY_BUCKETS);
[f1938c6]146}
[21580dd]147
[f1938c6]148static int replies_compare(unsigned long key[], hash_count_t keys, link_t *item)
149{
150 icmp_reply_t *reply =
151 hash_table_get_instance(item, icmp_reply_t, link);
152
153 if (keys == 1)
154 return (reply->id == key[0]);
155 else
156 return ((reply->id == key[0]) && (reply->sequence == key[1]));
157}
[21580dd]158
[f1938c6]159static void replies_remove_callback(link_t *item)
160{
161}
[21580dd]162
[f1938c6]163static hash_table_operations_t reply_ops = {
164 .hash = replies_hash,
165 .compare = replies_compare,
166 .remove_callback = replies_remove_callback
167};
[21580dd]168
[f1938c6]169/** Release the packet and return the result.
170 *
171 * @param[in] packet Packet queue to be released.
[edc5a985]172 *
[21580dd]173 */
[f1938c6]174static void icmp_release(packet_t *packet)
[edc5a985]175{
[f1938c6]176 pq_release_remote(phone_net, packet_get_id(packet));
[edc5a985]177}
[21580dd]178
[f1938c6]179/** Send the ICMP message.
[edc5a985]180 *
[f1938c6]181 * Set the message type and code and compute the checksum.
[edc5a985]182 * Error messages are sent only if allowed in the configuration.
[f1938c6]183 * Release the packet on errors.
184 *
185 * @param[in] type Message type.
186 * @param[in] code Message code.
187 * @param[in] packet Message packet to be sent.
188 * @param[in] header ICMP header.
189 * @param[in] error Error service to be announced. Should be
190 * SERVICE_ICMP or zero.
191 * @param[in] ttl Time to live.
192 * @param[in] tos Type of service.
193 * @param[in] dont_fragment Disable fragmentation.
194 *
195 * @return EOK on success.
196 * @return EPERM if the error message is not allowed.
197 *
[21580dd]198 */
[f1938c6]199static int icmp_send_packet(icmp_type_t type, icmp_code_t code,
200 packet_t *packet, icmp_header_t *header, services_t error, ip_ttl_t ttl,
201 ip_tos_t tos, bool dont_fragment)
[edc5a985]202{
[fb04cba8]203 /* Do not send an error if disabled */
[f1938c6]204 if ((error) && (!error_reporting)) {
205 icmp_release(packet);
206 return EPERM;
207 }
208
[edc5a985]209 header->type = type;
210 header->code = code;
[ffaba00]211
212 /*
213 * The checksum needs to be calculated
214 * with a virtual checksum field set to
215 * zero.
216 */
[edc5a985]217 header->checksum = 0;
218 header->checksum = ICMP_CHECKSUM(header,
219 packet_get_data_length(packet));
[d8f95529]220
[f1938c6]221 int rc = ip_client_prepare_packet(packet, IPPROTO_ICMP, ttl, tos,
[d8f95529]222 dont_fragment, 0);
[f1938c6]223 if (rc != EOK) {
224 icmp_release(packet);
225 return rc;
226 }
227
228 return ip_send_msg(phone_ip, -1, packet, SERVICE_ICMP, error);
[edc5a985]229}
[21580dd]230
[f1938c6]231/** Prepare the ICMP error packet.
[edc5a985]232 *
[f1938c6]233 * Truncate the original packet if longer than ICMP_KEEP_LENGTH bytes.
234 * Prefix and return the ICMP header.
235 *
236 * @param[in,out] packet Original packet.
[edc5a985]237 *
[1bfd3d3]238 * @return The prefixed ICMP header.
239 * @return NULL on errors.
[f1938c6]240 *
[21580dd]241 */
[46d4d9f]242static icmp_header_t *icmp_prepare_packet(packet_t *packet)
[edc5a985]243{
[f1938c6]244 size_t total_length = packet_get_data_length(packet);
[edc5a985]245 if (total_length <= 0)
246 return NULL;
[f1938c6]247
248 size_t header_length = ip_client_header_length(packet);
[edc5a985]249 if (header_length <= 0)
250 return NULL;
[f1938c6]251
[fb04cba8]252 /* Truncate if longer than 64 bits (without the IP header) */
[edc5a985]253 if ((total_length > header_length + ICMP_KEEP_LENGTH) &&
254 (packet_trim(packet, 0,
[f1938c6]255 total_length - header_length - ICMP_KEEP_LENGTH) != EOK))
[edc5a985]256 return NULL;
[f1938c6]257
258 icmp_header_t *header = PACKET_PREFIX(packet, icmp_header_t);
[edc5a985]259 if (!header)
260 return NULL;
[f1938c6]261
[edc5a985]262 bzero(header, sizeof(*header));
263 return header;
[21580dd]264}
265
[f1938c6]266/** Request an echo message.
267 *
268 * Send a packet with specified parameters to the target host
269 * and wait for the reply upto the given timeout.
270 * Block the caller until the reply or the timeout occurs.
271 *
272 * @param[in] id Message identifier.
273 * @param[in] sequence Message sequence parameter.
274 * @param[in] size Message data length in bytes.
275 * @param[in] timeout Timeout in miliseconds.
276 * @param[in] ttl Time to live.
277 * @param[in] tos Type of service.
278 * @param[in] dont_fragment Disable fragmentation.
279 * @param[in] addr Target host address.
280 * @param[in] addrlen Torget host address length.
281 *
282 * @return ICMP_ECHO on success.
283 * @return ETIMEOUT if the reply has not arrived before the
284 * timeout.
285 * @return ICMP type of the received error notification.
286 * @return EINVAL if the addrlen parameter is less or equal to
287 * zero.
288 * @return ENOMEM if there is not enough memory left.
289 *
[edc5a985]290 */
[fb04cba8]291static int icmp_echo(icmp_param_t id, icmp_param_t sequence, size_t size,
[f1938c6]292 mseconds_t timeout, ip_ttl_t ttl, ip_tos_t tos, bool dont_fragment,
293 const struct sockaddr *addr, socklen_t addrlen)
[edc5a985]294{
295 if (addrlen <= 0)
[21580dd]296 return EINVAL;
[f1938c6]297
298 size_t length = (size_t) addrlen;
299
300 packet_t *packet = packet_get_4_remote(phone_net, size,
301 icmp_dimension.addr_len, ICMP_HEADER_SIZE + icmp_dimension.prefix,
302 icmp_dimension.suffix);
[edc5a985]303 if (!packet)
[aadf01e]304 return ENOMEM;
[f1938c6]305
[fb04cba8]306 /* Prepare the requesting packet, set the destination address. */
[f1938c6]307 int rc = packet_set_addr(packet, NULL, (const uint8_t *) addr, length);
308 if (rc != EOK) {
309 icmp_release(packet);
310 return rc;
311 }
312
[fb04cba8]313 /* Allocate space in the packet */
[f1938c6]314 uint8_t *data = (uint8_t *) packet_suffix(packet, size);
315 if (!data) {
316 icmp_release(packet);
317 return ENOMEM;
318 }
319
[fb04cba8]320 /* Fill the data */
[21580dd]321 length = 0;
[edc5a985]322 while (size > length + sizeof(ICMP_ECHO_TEXT)) {
[aadf01e]323 memcpy(data + length, ICMP_ECHO_TEXT, sizeof(ICMP_ECHO_TEXT));
324 length += sizeof(ICMP_ECHO_TEXT);
[21580dd]325 }
[aadf01e]326 memcpy(data + length, ICMP_ECHO_TEXT, size - length);
[f1938c6]327
[fb04cba8]328 /* Prefix the header */
[f1938c6]329 icmp_header_t *header = PACKET_PREFIX(packet, icmp_header_t);
330 if (!header) {
331 icmp_release(packet);
332 return ENOMEM;
333 }
334
[ffaba00]335 bzero(header, sizeof(icmp_header_t));
[21580dd]336 header->un.echo.identifier = id;
337 header->un.echo.sequence_number = sequence;
[f1938c6]338
[fb04cba8]339 /* Prepare the reply structure */
[f1938c6]340 icmp_reply_t *reply = malloc(sizeof(icmp_reply_t));
341 if (!reply) {
342 icmp_release(packet);
343 return ENOMEM;
[21580dd]344 }
[f1938c6]345
346 reply->id = id;
347 reply->sequence = sequence;
348 fibril_condvar_initialize(&reply->condvar);
349
350 /* Add the reply to the replies hash table */
351 fibril_mutex_lock(&reply_lock);
352
353 unsigned long key[REPLY_KEYS] = {id, sequence};
354 hash_table_insert(&replies, key, &reply->link);
355
[fb04cba8]356 /* Send the request */
[edc5a985]357 icmp_send_packet(ICMP_ECHO, 0, packet, header, 0, ttl, tos,
358 dont_fragment);
[f1938c6]359
[fb04cba8]360 /* Wait for the reply. Timeout in microseconds. */
[f1938c6]361 rc = fibril_condvar_wait_timeout(&reply->condvar, &reply_lock,
[d8f95529]362 timeout * 1000);
363 if (rc == EOK)
364 rc = reply->result;
[f1938c6]365
366 /* Remove the reply from the replies hash table */
367 hash_table_remove(&replies, key, REPLY_KEYS);
368
369 fibril_mutex_unlock(&reply_lock);
370
371 free(reply);
372
[d8f95529]373 return rc;
[21580dd]374}
375
[f1938c6]376static int icmp_destination_unreachable(icmp_code_t code, icmp_param_t mtu,
377 packet_t *packet)
[edc5a985]378{
[f1938c6]379 icmp_header_t *header = icmp_prepare_packet(packet);
380 if (!header) {
381 icmp_release(packet);
382 return ENOMEM;
383 }
384
[edc5a985]385 if (mtu)
[21580dd]386 header->un.frag.mtu = mtu;
[f1938c6]387
[edc5a985]388 return icmp_send_packet(ICMP_DEST_UNREACH, code, packet, header,
[f1938c6]389 SERVICE_ICMP, 0, 0, false);
[21580dd]390}
391
[f1938c6]392static int icmp_source_quench(packet_t *packet)
[edc5a985]393{
[f1938c6]394 icmp_header_t *header = icmp_prepare_packet(packet);
395 if (!header) {
396 icmp_release(packet);
397 return ENOMEM;
398 }
399
[edc5a985]400 return icmp_send_packet(ICMP_SOURCE_QUENCH, 0, packet, header,
[f1938c6]401 SERVICE_ICMP, 0, 0, false);
[21580dd]402}
403
[f1938c6]404static int icmp_time_exceeded(icmp_code_t code, packet_t *packet)
[edc5a985]405{
[f1938c6]406 icmp_header_t *header = icmp_prepare_packet(packet);
407 if (!header) {
408 icmp_release(packet);
409 return ENOMEM;
410 }
411
[edc5a985]412 return icmp_send_packet(ICMP_TIME_EXCEEDED, code, packet, header,
[f1938c6]413 SERVICE_ICMP, 0, 0, false);
[21580dd]414}
415
[f1938c6]416static int icmp_parameter_problem(icmp_code_t code, icmp_param_t pointer,
417 packet_t *packet)
[edc5a985]418{
[f1938c6]419 icmp_header_t *header = icmp_prepare_packet(packet);
420 if (!header) {
421 icmp_release(packet);
422 return ENOMEM;
423 }
424
[edc5a985]425 header->un.param.pointer = pointer;
426 return icmp_send_packet(ICMP_PARAMETERPROB, code, packet, header,
[f1938c6]427 SERVICE_ICMP, 0, 0, false);
[21580dd]428}
429
[f1938c6]430/** Try to set the pending reply result as the received message type.
[edc5a985]431 *
432 * If the reply data is not present, the reply timed out and the other fibril
[f1938c6]433 * is already awake. The packet is released.
434 *
435 * @param[in] packet The received reply message.
436 * @param[in] header The ICMP message header.
437 * @param[in] type The received reply message type.
438 * @param[in] code The received reply message code.
[edc5a985]439 *
440 */
[f1938c6]441static void icmp_process_echo_reply(packet_t *packet, icmp_header_t *header,
[edc5a985]442 icmp_type_t type, icmp_code_t code)
443{
[f1938c6]444 unsigned long key[REPLY_KEYS] =
445 {header->un.echo.identifier, header->un.echo.sequence_number};
[ffaba00]446
447 /* The packet is no longer needed */
[f1938c6]448 icmp_release(packet);
449
[fb04cba8]450 /* Find the pending reply */
[f1938c6]451 fibril_mutex_lock(&reply_lock);
452
453 link_t *link = hash_table_find(&replies, key);
454 if (link != NULL) {
455 icmp_reply_t *reply =
456 hash_table_get_instance(link, icmp_reply_t, link);
457
[edc5a985]458 reply->result = type;
459 fibril_condvar_signal(&reply->condvar);
460 }
[f1938c6]461
462 fibril_mutex_unlock(&reply_lock);
[21580dd]463}
464
[f1938c6]465/** Process the received ICMP packet.
466 *
467 * Notify the destination socket application.
468 *
469 * @param[in,out] packet Received packet.
470 * @param[in] error Packet error reporting service to prefix
471 * the received packet.
472 *
473 * @return EOK on success.
474 * @return EINVAL if the packet is not valid.
475 * @return EINVAL if the stored packet address is not the an_addr_t.
476 * @return EINVAL if the packet does not contain any data.
477 * @return NO_DATA if the packet content is shorter than the user
478 * datagram header.
479 * @return ENOMEM if there is not enough memory left.
480 * @return EADDRNOTAVAIL if the destination socket does not exist.
481 * @return Other error codes as defined for the
482 * ip_client_process_packet() function.
483 *
[edc5a985]484 */
[46d4d9f]485static int icmp_process_packet(packet_t *packet, services_t error)
[edc5a985]486{
[aadf01e]487 icmp_type_t type;
488 icmp_code_t code;
[d8f95529]489 int rc;
[4765152]490
[edc5a985]491 switch (error) {
492 case SERVICE_NONE:
493 break;
494 case SERVICE_ICMP:
[fb04cba8]495 /* Process error */
[f1938c6]496 rc = icmp_client_process_packet(packet, &type, &code, NULL, NULL);
497 if (rc < 0)
498 return rc;
499
[fb04cba8]500 /* Remove the error header */
[f1938c6]501 rc = packet_trim(packet, (size_t) rc, 0);
[d8f95529]502 if (rc != EOK)
503 return rc;
[f1938c6]504
[edc5a985]505 break;
506 default:
507 return ENOTSUP;
[21580dd]508 }
[f1938c6]509
[fb04cba8]510 /* Get rid of the IP header */
[f1938c6]511 size_t length = ip_client_header_length(packet);
[d8f95529]512 rc = packet_trim(packet, length, 0);
513 if (rc != EOK)
514 return rc;
[f1938c6]515
[aadf01e]516 length = packet_get_data_length(packet);
[edc5a985]517 if (length <= 0)
[aadf01e]518 return EINVAL;
[f1938c6]519
[edc5a985]520 if (length < ICMP_HEADER_SIZE)
[aadf01e]521 return EINVAL;
[f1938c6]522
523 void *data = packet_get_data(packet);
[edc5a985]524 if (!data)
[aadf01e]525 return EINVAL;
[f1938c6]526
[fb04cba8]527 /* Get ICMP header */
[f1938c6]528 icmp_header_t *header = (icmp_header_t *) data;
529
[edc5a985]530 if (header->checksum) {
531 while (ICMP_CHECKSUM(header, length) != IP_CHECKSUM_ZERO) {
[fb04cba8]532 /*
533 * Set the original message type on error notification.
534 * Type swap observed in Qemu.
535 */
[edc5a985]536 if (error) {
537 switch (header->type) {
538 case ICMP_ECHOREPLY:
539 header->type = ICMP_ECHO;
540 continue;
[21580dd]541 }
542 }
[f1938c6]543
[21580dd]544 return EINVAL;
545 }
546 }
[f1938c6]547
[edc5a985]548 switch (header->type) {
549 case ICMP_ECHOREPLY:
[d8f95529]550 if (error)
[edc5a985]551 icmp_process_echo_reply(packet, header, type, code);
552 else
553 icmp_process_echo_reply(packet, header, ICMP_ECHO, 0);
[f1938c6]554
[edc5a985]555 return EOK;
[f1938c6]556
[edc5a985]557 case ICMP_ECHO:
558 if (error) {
559 icmp_process_echo_reply(packet, header, type, code);
[21580dd]560 return EOK;
[edc5a985]561 }
562
[fb04cba8]563 /* Do not send a reply if disabled */
[f1938c6]564 if (echo_replying) {
565 uint8_t *src;
566 int addrlen = packet_get_addr(packet, &src, NULL);
567
[fb04cba8]568 /*
[f1938c6]569 * Set both addresses to the source one (avoid the
[fb04cba8]570 * source address deletion before setting the
571 * destination one).
572 */
[edc5a985]573 if ((addrlen > 0) && (packet_set_addr(packet, src, src,
574 (size_t) addrlen) == EOK)) {
[fb04cba8]575 /* Send the reply */
[edc5a985]576 icmp_send_packet(ICMP_ECHOREPLY, 0, packet,
577 header, 0, 0, 0, 0);
578 return EOK;
579 }
[f1938c6]580
[edc5a985]581 return EINVAL;
582 }
[f1938c6]583
[edc5a985]584 return EPERM;
[f1938c6]585
[edc5a985]586 case ICMP_DEST_UNREACH:
587 case ICMP_SOURCE_QUENCH:
588 case ICMP_REDIRECT:
589 case ICMP_ALTERNATE_ADDR:
590 case ICMP_ROUTER_ADV:
591 case ICMP_ROUTER_SOL:
592 case ICMP_TIME_EXCEEDED:
593 case ICMP_PARAMETERPROB:
594 case ICMP_CONVERSION_ERROR:
595 case ICMP_REDIRECT_MOBILE:
596 case ICMP_SKIP:
597 case ICMP_PHOTURIS:
[f1938c6]598 ip_received_error_msg(phone_ip, -1, packet,
[edc5a985]599 SERVICE_IP, SERVICE_ICMP);
600 return EOK;
[f1938c6]601
[edc5a985]602 default:
603 return ENOTSUP;
[21580dd]604 }
605}
606
[014dd57b]607/** Process IPC messages from the IP module
608 *
609 * @param[in] iid Message identifier.
610 * @param[in,out] icall Message parameters.
611 *
612 */
613static void icmp_receiver(ipc_callid_t iid, ipc_call_t *icall)
614{
[f1938c6]615 bool loop = true;
[014dd57b]616 packet_t *packet;
617 int rc;
618
[f1938c6]619 while (loop) {
[014dd57b]620 switch (IPC_GET_IMETHOD(*icall)) {
621 case NET_TL_RECEIVED:
[f1938c6]622 rc = packet_translate_remote(phone_net, &packet,
[014dd57b]623 IPC_GET_PACKET(*icall));
[f1938c6]624 if (rc == EOK) {
625 rc = icmp_process_packet(packet, IPC_GET_ERROR(*icall));
626 if (rc != EOK)
627 icmp_release(packet);
628 }
[014dd57b]629
630 ipc_answer_0(iid, (sysarg_t) rc);
631 break;
[f1938c6]632 case IPC_M_PHONE_HUNGUP:
633 loop = false;
634 continue;
[014dd57b]635 default:
636 ipc_answer_0(iid, (sysarg_t) ENOTSUP);
637 }
638
639 iid = async_get_call(icall);
640 }
641}
642
643/** Initialize the ICMP module.
644 *
645 * @param[in] net_phone Network module phone.
646 *
647 * @return EOK on success.
648 * @return ENOMEM if there is not enough memory left.
649 *
650 */
651int tl_initialize(int net_phone)
652{
653 measured_string_t names[] = {
654 {
655 (uint8_t *) "ICMP_ERROR_REPORTING",
656 20
657 },
658 {
659 (uint8_t *) "ICMP_ECHO_REPLYING",
660 18
661 }
662 };
663 measured_string_t *configuration;
664 size_t count = sizeof(names) / sizeof(measured_string_t);
665 uint8_t *data;
666
[f1938c6]667 if (!hash_table_create(&replies, REPLY_BUCKETS, REPLY_KEYS, &reply_ops))
668 return ENOMEM;
[014dd57b]669
[f1938c6]670 fibril_mutex_initialize(&reply_lock);
671 atomic_set(&icmp_client, 0);
[014dd57b]672
[f1938c6]673 phone_net = net_phone;
674 phone_ip = ip_bind_service(SERVICE_IP, IPPROTO_ICMP, SERVICE_ICMP,
675 icmp_receiver);
676 if (phone_ip < 0)
677 return phone_ip;
[014dd57b]678
[f1938c6]679 int rc = ip_packet_size_req(phone_ip, -1, &icmp_dimension);
680 if (rc != EOK)
[014dd57b]681 return rc;
682
[f1938c6]683 icmp_dimension.prefix += ICMP_HEADER_SIZE;
684 icmp_dimension.content -= ICMP_HEADER_SIZE;
[014dd57b]685
686 /* Get configuration */
687 configuration = &names[0];
[f1938c6]688 rc = net_get_conf_req(phone_net, &configuration, count, &data);
689 if (rc != EOK)
[014dd57b]690 return rc;
691
692 if (configuration) {
[f1938c6]693 if (configuration[0].value)
694 error_reporting = (configuration[0].value[0] == 'y');
695
696 if (configuration[1].value)
697 echo_replying = (configuration[1].value[0] == 'y');
698
[014dd57b]699 net_free_settings(configuration, data);
700 }
701
702 return EOK;
703}
704
[f1938c6]705/** Per-connection initialization
[ffaba00]706 *
707 * Initialize client-specific global variables.
[f1938c6]708 *
709 */
710void tl_connection(void)
711{
712 icmp_id = (icmp_param_t) atomic_postinc(&icmp_client);
713 icmp_seq = 1;
714}
715
716/** Process the ICMP message.
717 *
718 * @param[in] callid Message identifier.
719 * @param[in] call Message parameters.
720 * @param[out] answer Answer.
721 * @param[out] count Number of arguments of the answer.
722 *
723 * @return EOK on success.
724 * @return ENOTSUP if the message is not known.
725 * @return Other error codes as defined for the packet_translate()
726 * function.
727 * @return Other error codes as defined for the
728 * icmp_destination_unreachable() function.
729 * @return Other error codes as defined for the
730 * icmp_source_quench() function.
731 * @return Other error codes as defined for the
732 * icmp_time_exceeded() function.
733 * @return Other error codes as defined for the
734 * icmp_parameter_problem() function.
735 *
736 * @see icmp_remote.h
737 * @see IS_NET_ICMP_MESSAGE()
738 *
[edc5a985]739 */
[f1938c6]740int tl_message(ipc_callid_t callid, ipc_call_t *call,
741 ipc_call_t *answer, size_t *count)
[edc5a985]742{
[f1938c6]743 struct sockaddr *addr;
744 size_t size;
[46d4d9f]745 packet_t *packet;
[d8f95529]746 int rc;
[f1938c6]747
748 *count = 0;
749
[228e490]750 switch (IPC_GET_IMETHOD(*call)) {
[f1938c6]751 case NET_ICMP_ECHO:
752 rc = async_data_write_accept((void **) &addr, false, 0, 0, 0, &size);
753 if (rc != EOK)
754 return rc;
755
756 rc = icmp_echo(icmp_id, icmp_seq, ICMP_GET_SIZE(*call),
757 ICMP_GET_TIMEOUT(*call), ICMP_GET_TTL(*call),
758 ICMP_GET_TOS(*call), ICMP_GET_DONT_FRAGMENT(*call),
759 addr, (socklen_t) size);
760
761 free(addr);
762 icmp_seq++;
763 return rc;
764
[edc5a985]765 case NET_ICMP_DEST_UNREACH:
[f1938c6]766 rc = packet_translate_remote(phone_net, &packet,
[774e6d1a]767 IPC_GET_PACKET(*call));
[d8f95529]768 if (rc != EOK)
769 return rc;
[f1938c6]770
771 return icmp_destination_unreachable(ICMP_GET_CODE(*call),
772 ICMP_GET_MTU(*call), packet);
773
[edc5a985]774 case NET_ICMP_SOURCE_QUENCH:
[f1938c6]775 rc = packet_translate_remote(phone_net, &packet,
[774e6d1a]776 IPC_GET_PACKET(*call));
[d8f95529]777 if (rc != EOK)
778 return rc;
[f1938c6]779
780 return icmp_source_quench(packet);
781
[edc5a985]782 case NET_ICMP_TIME_EXCEEDED:
[f1938c6]783 rc = packet_translate_remote(phone_net, &packet,
[774e6d1a]784 IPC_GET_PACKET(*call));
[d8f95529]785 if (rc != EOK)
786 return rc;
[f1938c6]787
788 return icmp_time_exceeded(ICMP_GET_CODE(*call), packet);
789
[edc5a985]790 case NET_ICMP_PARAMETERPROB:
[f1938c6]791 rc = packet_translate_remote(phone_net, &packet,
[774e6d1a]792 IPC_GET_PACKET(*call));
[d8f95529]793 if (rc != EOK)
794 return rc;
[edc5a985]795
[f1938c6]796 return icmp_parameter_problem(ICMP_GET_CODE(*call),
797 ICMP_GET_POINTER(*call), packet);
[21580dd]798 }
[849ed54]799
[014dd57b]800 return ENOTSUP;
[849ed54]801}
802
803int main(int argc, char *argv[])
804{
805 /* Start the module */
[014dd57b]806 return tl_module_start(SERVICE_ICMP);
[849ed54]807}
808
[21580dd]809/** @}
810 */
Note: See TracBrowser for help on using the repository browser.