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

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

networking fixes

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