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

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

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

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