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

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

Get rid of the ERROR_CODE madness in icmp.

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