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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 60ab6c3 was b48ebd19, checked in by Lukas Mejdrech <lukasmejdrech@…>, 15 years ago
  • debug prints in debug mode only
  • Property mode set to 100644
File size: 26.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
30 * @{
31 */
32
33/** @file
34 * ICMP module implementation.
35 * @see icmp.h
36 */
37
38#include <async.h>
39#include <atomic.h>
40#include <fibril.h>
41#include <fibril_synch.h>
42#include <stdint.h>
[b48ebd19]43#include <string.h>
[21580dd]44
45#include <ipc/ipc.h>
46#include <ipc/services.h>
47
[1a0fb3f8]48#include <sys/time.h>
[21580dd]49#include <sys/types.h>
50
51#include "../../err.h"
52#include "../../messages.h"
53#include "../../modules.h"
54
55#include "../../structures/packet/packet_client.h"
56
57#include "../../include/byteorder.h"
58#include "../../include/checksum.h"
59#include "../../include/icmp_api.h"
60#include "../../include/icmp_client.h"
61#include "../../include/icmp_codes.h"
62#include "../../include/icmp_common.h"
63#include "../../include/icmp_interface.h"
64#include "../../include/il_interface.h"
65#include "../../include/inet.h"
66#include "../../include/ip_client.h"
67#include "../../include/ip_interface.h"
68#include "../../include/ip_protocols.h"
69#include "../../include/net_interface.h"
70#include "../../include/socket_codes.h"
71#include "../../include/socket_errno.h"
72
73#include "../../tl/tl_messages.h"
74
75#include "icmp.h"
76#include "icmp_header.h"
77#include "icmp_messages.h"
78#include "icmp_module.h"
79
80/** Default ICMP error reporting.
81 */
82#define NET_DEFAULT_ICMP_ERROR_REPORTING true
83
84/** Default ICMP echo replying.
85 */
86#define NET_DEFAULT_ICMP_ECHO_REPLYING true
87
88/** Original datagram length in bytes transfered to the error notification message.
89 */
90#define ICMP_KEEP_LENGTH 8
91
92/** Free identifier numbers pool start.
93 */
94#define ICMP_FREE_IDS_START 1
95
96/** Free identifier numbers pool end.
97 */
98#define ICMP_FREE_IDS_END MAX_UINT16
99
100/** Computes the ICMP datagram checksum.
101 * @param[in,out] header The ICMP datagram header.
102 * @param[in] length The total datagram length.
103 * @returns The computed checksum.
104 */
[aadf01e]105#define ICMP_CHECKSUM(header, length) htons(ip_checksum((uint8_t *) (header), (length)))
[21580dd]106
107/** An echo request datagrams pattern.
108 */
109#define ICMP_ECHO_TEXT "Hello from HelenOS."
110
111/** Computes an ICMP reply data key.
112 * @param[in] id The message identifier.
113 * @param[in] sequence The message sequence number.
114 * @returns The computed ICMP reply data key.
115 */
[aadf01e]116#define ICMP_GET_REPLY_KEY(id, sequence) (((id) << 16) | (sequence &0xFFFF))
[21580dd]117
118/** Processes the received ICMP packet.
119 * Is used as an entry point from the underlying IP module.
120 * Releases the packet on error.
121 * @param device_id The device identifier. Ignored parameter.
122 * @param[in,out] packet The received packet.
123 * @param receiver The target service. Ignored parameter.
124 * @param[in] error The packet error reporting service. Prefixes the received packet.
125 * @returns EOK on success.
126 * @returns Other error codes as defined for the icmp_process_packet() function.
127 */
[aadf01e]128int icmp_received_msg(device_id_t device_id, packet_t packet, services_t receiver, services_t error);
[21580dd]129
130/** Processes the received ICMP packet.
131 * Notifies the destination socket application.
132 * @param[in,out] packet The received packet.
133 * @param[in] error The packet error reporting service. Prefixes the received packet.
134 * @returns EOK on success.
135 * @returns EINVAL if the packet is not valid.
136 * @returns EINVAL if the stored packet address is not the an_addr_t.
137 * @returns EINVAL if the packet does not contain any data.
138 * @returns NO_DATA if the packet content is shorter than the user datagram header.
139 * @returns ENOMEM if there is not enough memory left.
140 * @returns EADDRNOTAVAIL if the destination socket does not exist.
141 * @returns Other error codes as defined for the ip_client_process_packet() function.
142 */
[aadf01e]143int icmp_process_packet(packet_t packet, services_t error);
[21580dd]144
145/** Processes the client messages.
146 * Remembers the assigned identifier and sequence numbers.
147 * Runs until the client module disconnects.
148 * @param[in] callid The message identifier.
149 * @param[in] call The message parameters.
150 * @returns EOK.
151 * @see icmp_interface.h
152 * @see icmp_api.h
153 */
[aadf01e]154int icmp_process_client_messages(ipc_callid_t callid, ipc_call_t call);
[21580dd]155
156/** Processes the generic client messages.
157 * @param[in] call The message parameters.
158 * @returns EOK on success.
159 * @returns ENOTSUP if the message is not known.
160 * @returns Other error codes as defined for the packet_translate() function.
161 * @returns Other error codes as defined for the icmp_destination_unreachable_msg() function.
162 * @returns Other error codes as defined for the icmp_source_quench_msg() function.
163 * @returns Other error codes as defined for the icmp_time_exceeded_msg() function.
164 * @returns Other error codes as defined for the icmp_parameter_problem_msg() function.
165 * @see icmp_interface.h
166 */
[aadf01e]167int icmp_process_message(ipc_call_t * call);
[21580dd]168
169/** Releases the packet and returns the result.
170 * @param[in] packet The packet queue to be released.
171 * @param[in] result The result to be returned.
172 * @returns The result parameter.
173 */
[aadf01e]174int icmp_release_and_return(packet_t packet, int result);
[21580dd]175
176/** Requests an echo message.
177 * Sends a packet with specified parameters to the target host and waits for the reply upto the given timeout.
[b5cbff4]178 * Blocks the caller until the reply or the timeout occurs.
[21580dd]179 * @param[in] id The message identifier.
180 * @param[in] sequence The message sequence parameter.
181 * @param[in] size The message data length in bytes.
182 * @param[in] timeout The timeout in miliseconds.
183 * @param[in] ttl The time to live.
184 * @param[in] tos The type of service.
185 * @param[in] dont_fragment The value indicating whether the datagram must not be fragmented. Is used as a MTU discovery.
186 * @param[in] addr The target host address.
187 * @param[in] addrlen The torget host address length.
188 * @returns ICMP_ECHO on success.
189 * @returns ETIMEOUT if the reply has not arrived before the timeout.
190 * @returns ICMP type of the received error notification.
191 * @returns EINVAL if the addrlen parameter is less or equal to zero (<=0).
192 * @returns ENOMEM if there is not enough memory left.
193 * @returns EPARTY if there was an internal error.
194 */
[aadf01e]195int icmp_echo(icmp_param_t id, icmp_param_t sequence, size_t size, mseconds_t timeout, ip_ttl_t ttl, ip_tos_t tos, int dont_fragment, const struct sockaddr * addr, socklen_t addrlen);
[21580dd]196
197/** Prepares the ICMP error packet.
198 * Truncates the original packet if longer than ICMP_KEEP_LENGTH bytes.
199 * Prefixes and returns the ICMP header.
200 * @param[in,out] packet The original packet.
201 * @returns The prefixed ICMP header.
202 * @returns NULL on errors.
203 */
[aadf01e]204icmp_header_ref icmp_prepare_packet(packet_t packet);
[21580dd]205
206/** Sends the ICMP message.
207 * Sets the message type and code and computes the checksum.
208 * Error messages are sent only if allowed in the configuration.
209 * Releases the packet on errors.
210 * @param[in] type The message type.
211 * @param[in] code The message code.
212 * @param[in] packet The message packet to be sent.
213 * @param[in] header The ICMP header.
214 * @param[in] error The error service to be announced. Should be SERVICE_ICMP or zero (0).
215 * @param[in] ttl The time to live.
216 * @param[in] tos The type of service.
217 * @param[in] dont_fragment The value indicating whether the datagram must not be fragmented. Is used as a MTU discovery.
218 * @returns EOK on success.
219 * @returns EPERM if the error message is not allowed.
220 */
[aadf01e]221int icmp_send_packet(icmp_type_t type, icmp_code_t code, packet_t packet, icmp_header_ref header, services_t error, ip_ttl_t ttl, ip_tos_t tos, int dont_fragment);
[21580dd]222
223/** Tries to set the pending reply result as the received message type.
[b5cbff4]224 * If the reply data is not present, the reply timed out and the other fibril
225 * is already awake.
[21580dd]226 * Releases the packet.
227 * @param[in] packet The received reply message.
228 * @param[in] header The ICMP message header.
229 * @param[in] type The received reply message type.
230 * @param[in] code The received reply message code.
231 * @returns EOK.
232 */
[aadf01e]233int icmp_process_echo_reply(packet_t packet, icmp_header_ref header, icmp_type_t type, icmp_code_t code);
[21580dd]234
235/** Assigns a new identifier for the connection.
236 * Fills the echo data parameter with the assigned values.
237 * @param[in,out] echo_data The echo data to be bound.
238 * @returns Index of the inserted echo data.
239 * @returns EBADMEM if the echo_data parameter is NULL.
240 * @returns ENOTCONN if no free identifier have been found.
241 */
[aadf01e]242int icmp_bind_free_id(icmp_echo_ref echo_data);
[21580dd]243
244/** ICMP global data.
245 */
246icmp_globals_t icmp_globals;
247
[aadf01e]248INT_MAP_IMPLEMENT(icmp_replies, icmp_reply_t);
[21580dd]249
[aadf01e]250INT_MAP_IMPLEMENT(icmp_echo_data, icmp_echo_t);
[21580dd]251
[aadf01e]252int icmp_echo_msg(int icmp_phone, size_t size, mseconds_t timeout, ip_ttl_t ttl, ip_tos_t tos, int dont_fragment, const struct sockaddr * addr, socklen_t addrlen){
253 icmp_echo_ref echo_data;
254 int res;
[21580dd]255
[aadf01e]256 fibril_rwlock_write_lock(&icmp_globals.lock);
[21580dd]257 // use the phone as the echo data index
[aadf01e]258 echo_data = icmp_echo_data_find(&icmp_globals.echo_data, icmp_phone);
259 if(! echo_data){
[21580dd]260 res = ENOENT;
261 }else{
[aadf01e]262 res = icmp_echo(echo_data->identifier, echo_data->sequence_number, size, timeout, ttl, tos, dont_fragment, addr, addrlen);
263 if(echo_data->sequence_number < MAX_UINT16){
[21580dd]264 ++ echo_data->sequence_number;
265 }else{
266 echo_data->sequence_number = 0;
267 }
268 }
[aadf01e]269 fibril_rwlock_write_unlock(&icmp_globals.lock);
[21580dd]270 return res;
271}
272
[aadf01e]273int icmp_echo(icmp_param_t id, icmp_param_t sequence, size_t size, mseconds_t timeout, ip_ttl_t ttl, ip_tos_t tos, int dont_fragment, const struct sockaddr * addr, socklen_t addrlen){
[21580dd]274 ERROR_DECLARE;
275
[aadf01e]276 icmp_header_ref header;
277 packet_t packet;
278 size_t length;
279 uint8_t * data;
280 icmp_reply_ref reply;
281 int reply_key;
282 int result;
283 int index;
[21580dd]284
[aadf01e]285 if(addrlen <= 0){
[21580dd]286 return EINVAL;
287 }
[aadf01e]288 length = (size_t) addrlen;
[21580dd]289 // TODO do not ask all the time
[aadf01e]290 ERROR_PROPAGATE(ip_packet_size_req(icmp_globals.ip_phone, -1, &icmp_globals.packet_dimension));
291 packet = packet_get_4(icmp_globals.net_phone, size, icmp_globals.packet_dimension.addr_len, ICMP_HEADER_SIZE + icmp_globals.packet_dimension.prefix, icmp_globals.packet_dimension.suffix);
292 if(! packet){
293 return ENOMEM;
294 }
[21580dd]295
296 // prepare the requesting packet
297 // set the destination address
[aadf01e]298 if(ERROR_OCCURRED(packet_set_addr(packet, NULL, (const uint8_t *) addr, length))){
299 return icmp_release_and_return(packet, ERROR_CODE);
[21580dd]300 }
301 // allocate space in the packet
[aadf01e]302 data = (uint8_t *) packet_suffix(packet, size);
303 if(! data){
304 return icmp_release_and_return(packet, ENOMEM);
[21580dd]305 }
306 // fill the data
307 length = 0;
[aadf01e]308 while(size > length + sizeof(ICMP_ECHO_TEXT)){
309 memcpy(data + length, ICMP_ECHO_TEXT, sizeof(ICMP_ECHO_TEXT));
310 length += sizeof(ICMP_ECHO_TEXT);
[21580dd]311 }
[aadf01e]312 memcpy(data + length, ICMP_ECHO_TEXT, size - length);
[21580dd]313 // prefix the header
[aadf01e]314 header = PACKET_PREFIX(packet, icmp_header_t);
315 if(! header){
316 return icmp_release_and_return(packet, ENOMEM);
[21580dd]317 }
[aadf01e]318 bzero(header, sizeof(*header));
[21580dd]319 header->un.echo.identifier = id;
320 header->un.echo.sequence_number = sequence;
321
[536ded4]322 // prepare the reply structure
[aadf01e]323 reply = malloc(sizeof(*reply));
324 if(! reply){
325 return icmp_release_and_return(packet, ENOMEM);
[21580dd]326 }
[aadf01e]327 fibril_mutex_initialize(&reply->mutex);
328 fibril_mutex_lock(&reply->mutex);
329 fibril_condvar_initialize(&reply->condvar);
330 reply_key = ICMP_GET_REPLY_KEY(header->un.echo.identifier, header->un.echo.sequence_number);
331 index = icmp_replies_add(&icmp_globals.replies, reply_key, reply);
332 if(index < 0){
333 free(reply);
334 return icmp_release_and_return(packet, index);
[21580dd]335 }
336
[b5cbff4]337 // unlock the globals so that we can wait for the reply
[aadf01e]338 fibril_rwlock_write_unlock(&icmp_globals.lock);
[21580dd]339
340 // send the request
[aadf01e]341 icmp_send_packet(ICMP_ECHO, 0, packet, header, 0, ttl, tos, dont_fragment);
[21580dd]342
[b5cbff4]343 // wait for the reply
[536ded4]344 // timeout in microseconds
[aadf01e]345 if(ERROR_OCCURRED(fibril_condvar_wait_timeout(&reply->condvar, &reply->mutex, timeout * 1000))){
[536ded4]346 result = ERROR_CODE;
347 }else{
348 // read the result
349 result = reply->result;
350 }
[21580dd]351
[b5cbff4]352 // drop the reply mutex before locking the globals again
[936835e]353 fibril_mutex_unlock(&reply->mutex);
354 fibril_rwlock_write_lock(&icmp_globals.lock);
[b5cbff4]355
[21580dd]356 // destroy the reply structure
[aadf01e]357 icmp_replies_exclude_index(&icmp_globals.replies, index);
[21580dd]358 return result;
359}
360
[aadf01e]361int icmp_destination_unreachable_msg(int icmp_phone, icmp_code_t code, icmp_param_t mtu, packet_t packet){
362 icmp_header_ref header;
[21580dd]363
[aadf01e]364 header = icmp_prepare_packet(packet);
365 if(! header){
366 return icmp_release_and_return(packet, ENOMEM);
[21580dd]367 }
[aadf01e]368 if(mtu){
[21580dd]369 header->un.frag.mtu = mtu;
370 }
[aadf01e]371 return icmp_send_packet(ICMP_DEST_UNREACH, code, packet, header, SERVICE_ICMP, 0, 0, 0);
[21580dd]372}
373
[aadf01e]374int icmp_source_quench_msg(int icmp_phone, packet_t packet){
375 icmp_header_ref header;
[21580dd]376
[aadf01e]377 header = icmp_prepare_packet(packet);
378 if(! header){
379 return icmp_release_and_return(packet, ENOMEM);
[21580dd]380 }
[aadf01e]381 return icmp_send_packet(ICMP_SOURCE_QUENCH, 0, packet, header, SERVICE_ICMP, 0, 0, 0);
[21580dd]382}
383
[aadf01e]384int icmp_time_exceeded_msg(int icmp_phone, icmp_code_t code, packet_t packet){
385 icmp_header_ref header;
[21580dd]386
[aadf01e]387 header = icmp_prepare_packet(packet);
388 if(! header){
389 return icmp_release_and_return(packet, ENOMEM);
[21580dd]390 }
[aadf01e]391 return icmp_send_packet(ICMP_TIME_EXCEEDED, code, packet, header, SERVICE_ICMP, 0, 0, 0);
[21580dd]392}
393
[aadf01e]394int icmp_parameter_problem_msg(int icmp_phone, icmp_code_t code, icmp_param_t pointer, packet_t packet){
395 icmp_header_ref header;
[21580dd]396
[aadf01e]397 header = icmp_prepare_packet(packet);
398 if(! header){
399 return icmp_release_and_return(packet, ENOMEM);
[21580dd]400 }
401 header->un.param.pointer = pointer;
[aadf01e]402 return icmp_send_packet(ICMP_PARAMETERPROB, code, packet, header, SERVICE_ICMP, 0, 0, 0);
[21580dd]403}
404
[aadf01e]405icmp_header_ref icmp_prepare_packet(packet_t packet){
406 icmp_header_ref header;
407 size_t header_length;
408 size_t total_length;
[21580dd]409
[aadf01e]410 total_length = packet_get_data_length(packet);
411 if(total_length <= 0){
412 return NULL;
413 }
414 header_length = ip_client_header_length(packet);
415 if(header_length <= 0){
416 return NULL;
417 }
[21580dd]418 // truncate if longer than 64 bits (without the IP header)
[aadf01e]419 if((total_length > header_length + ICMP_KEEP_LENGTH)
420 && (packet_trim(packet, 0, total_length - header_length - ICMP_KEEP_LENGTH) != EOK)){
[21580dd]421 return NULL;
422 }
[aadf01e]423 header = PACKET_PREFIX(packet, icmp_header_t);
424 if(! header){
425 return NULL;
426 }
427 bzero(header, sizeof(*header));
[21580dd]428 return header;
429}
430
[aadf01e]431int icmp_send_packet(icmp_type_t type, icmp_code_t code, packet_t packet, icmp_header_ref header, services_t error, ip_ttl_t ttl, ip_tos_t tos, int dont_fragment){
[21580dd]432 ERROR_DECLARE;
433
434 // do not send an error if disabled
[aadf01e]435 if(error && (! icmp_globals.error_reporting)){
436 return icmp_release_and_return(packet, EPERM);
[21580dd]437 }
438 header->type = type;
439 header->code = code;
440 header->checksum = 0;
[aadf01e]441 header->checksum = ICMP_CHECKSUM(header, packet_get_data_length(packet));
442 if(ERROR_OCCURRED(ip_client_prepare_packet(packet, IPPROTO_ICMP, ttl, tos, dont_fragment, 0))){
443 return icmp_release_and_return(packet, ERROR_CODE);
[21580dd]444 }
[aadf01e]445 return ip_send_msg(icmp_globals.ip_phone, -1, packet, SERVICE_ICMP, error);
[21580dd]446}
447
[aadf01e]448int icmp_connect_module(services_t service, suseconds_t timeout){
449 icmp_echo_ref echo_data;
450 icmp_param_t id;
451 int index;
[21580dd]452
[aadf01e]453 echo_data = (icmp_echo_ref) malloc(sizeof(*echo_data));
454 if(! echo_data){
455 return ENOMEM;
456 }
[21580dd]457 // assign a new identifier
[aadf01e]458 fibril_rwlock_write_lock(&icmp_globals.lock);
459 index = icmp_bind_free_id(echo_data);
460 if(index < 0){
461 free(echo_data);
462 fibril_rwlock_write_unlock(&icmp_globals.lock);
[21580dd]463 return index;
464 }else{
465 id = echo_data->identifier;
[aadf01e]466 fibril_rwlock_write_unlock(&icmp_globals.lock);
[21580dd]467 // return the echo data identifier as the ICMP phone
468 return id;
469 }
470}
471
[aadf01e]472int icmp_initialize(async_client_conn_t client_connection){
[21580dd]473 ERROR_DECLARE;
474
[aadf01e]475 measured_string_t names[] = {{str_dup("ICMP_ERROR_REPORTING"), 20}, {str_dup("ICMP_ECHO_REPLYING"), 18}};
476 measured_string_ref configuration;
477 size_t count = sizeof(names) / sizeof(measured_string_t);
478 char * data;
479
480 fibril_rwlock_initialize(&icmp_globals.lock);
481 fibril_rwlock_write_lock(&icmp_globals.lock);
482 icmp_replies_initialize(&icmp_globals.replies);
483 icmp_echo_data_initialize(&icmp_globals.echo_data);
484 icmp_globals.ip_phone = ip_bind_service(SERVICE_IP, IPPROTO_ICMP, SERVICE_ICMP, client_connection, icmp_received_msg);
485 if(icmp_globals.ip_phone < 0){
[21580dd]486 return icmp_globals.ip_phone;
487 }
[aadf01e]488 ERROR_PROPAGATE(ip_packet_size_req(icmp_globals.ip_phone, -1, &icmp_globals.packet_dimension));
[91478aa]489 icmp_globals.packet_dimension.prefix += ICMP_HEADER_SIZE;
490 icmp_globals.packet_dimension.content -= ICMP_HEADER_SIZE;
[21580dd]491 // get configuration
492 icmp_globals.error_reporting = NET_DEFAULT_ICMP_ERROR_REPORTING;
493 icmp_globals.echo_replying = NET_DEFAULT_ICMP_ECHO_REPLYING;
[aadf01e]494 configuration = &names[0];
495 ERROR_PROPAGATE(net_get_conf_req(icmp_globals.net_phone, &configuration, count, &data));
496 if(configuration){
497 if(configuration[0].value){
498 icmp_globals.error_reporting = (configuration[0].value[0] == 'y');
[21580dd]499 }
[aadf01e]500 if(configuration[1].value){
501 icmp_globals.echo_replying = (configuration[1].value[0] == 'y');
[21580dd]502 }
[aadf01e]503 net_free_settings(configuration, data);
[21580dd]504 }
[aadf01e]505 fibril_rwlock_write_unlock(&icmp_globals.lock);
[21580dd]506 return EOK;
507}
508
[aadf01e]509int icmp_received_msg(device_id_t device_id, packet_t packet, services_t receiver, services_t error){
[21580dd]510 ERROR_DECLARE;
511
[aadf01e]512 if(ERROR_OCCURRED(icmp_process_packet(packet, error))){
513 return icmp_release_and_return(packet, ERROR_CODE);
[21580dd]514 }
515
516 return EOK;
517}
518
[aadf01e]519int icmp_process_packet(packet_t packet, services_t error){
[21580dd]520 ERROR_DECLARE;
521
[aadf01e]522 size_t length;
523 uint8_t * src;
524 int addrlen;
525 int result;
526 void * data;
527 icmp_header_ref header;
528 icmp_type_t type;
529 icmp_code_t code;
530
531 if(error){
532 switch(error){
[21580dd]533 case SERVICE_ICMP:
534 // process error
[aadf01e]535 result = icmp_client_process_packet(packet, &type, &code, NULL, NULL);
536 if(result < 0){
537 return result;
538 }
539 length = (size_t) result;
[21580dd]540 // remove the error header
[aadf01e]541 ERROR_PROPAGATE(packet_trim(packet, length, 0));
[21580dd]542 break;
543 default:
544 return ENOTSUP;
545 }
546 }
547 // get rid of the ip header
[aadf01e]548 length = ip_client_header_length(packet);
549 ERROR_PROPAGATE(packet_trim(packet, length, 0));
550
551 length = packet_get_data_length(packet);
552 if(length <= 0){
553 return EINVAL;
554 }
555 if(length < ICMP_HEADER_SIZE){
556 return EINVAL;
557 }
558 data = packet_get_data(packet);
559 if(! data){
560 return EINVAL;
561 }
[21580dd]562 // get icmp header
[aadf01e]563 header = (icmp_header_ref) data;
[21580dd]564 // checksum
[aadf01e]565 if(header->checksum){
566 while(ICMP_CHECKSUM(header, length) != IP_CHECKSUM_ZERO){
[21580dd]567 // set the original message type on error notification
568 // type swap observed in Qemu
[aadf01e]569 if(error){
570 switch(header->type){
[21580dd]571 case ICMP_ECHOREPLY:
572 header->type = ICMP_ECHO;
573 continue;
574 }
575 }
576 return EINVAL;
577 }
578 }
[aadf01e]579 switch(header->type){
[21580dd]580 case ICMP_ECHOREPLY:
[aadf01e]581 if(error){
582 return icmp_process_echo_reply(packet, header, type, code);
[21580dd]583 }else{
[aadf01e]584 return icmp_process_echo_reply(packet, header, ICMP_ECHO, 0);
[21580dd]585 }
586 case ICMP_ECHO:
[aadf01e]587 if(error){
588 return icmp_process_echo_reply(packet, header, type, code);
[21580dd]589 // do not send a reply if disabled
[aadf01e]590 }else if(icmp_globals.echo_replying){
591 addrlen = packet_get_addr(packet, &src, NULL);
592 if((addrlen > 0)
[21580dd]593 // set both addresses to the source one (avoids the source address deletion before setting the destination one)
[aadf01e]594 && (packet_set_addr(packet, src, src, (size_t) addrlen) == EOK)){
[21580dd]595 // send the reply
[aadf01e]596 icmp_send_packet(ICMP_ECHOREPLY, 0, packet, header, 0, 0, 0, 0);
[21580dd]597 return EOK;
598 }else{
599 return EINVAL;
600 }
601 }else{
602 return EPERM;
603 }
604 case ICMP_DEST_UNREACH:
605 case ICMP_SOURCE_QUENCH:
606 case ICMP_REDIRECT:
607 case ICMP_ALTERNATE_ADDR:
608 case ICMP_ROUTER_ADV:
609 case ICMP_ROUTER_SOL:
610 case ICMP_TIME_EXCEEDED:
611 case ICMP_PARAMETERPROB:
612 case ICMP_CONVERSION_ERROR:
613 case ICMP_REDIRECT_MOBILE:
614 case ICMP_SKIP:
615 case ICMP_PHOTURIS:
[aadf01e]616 ip_received_error_msg(icmp_globals.ip_phone, -1, packet, SERVICE_IP, SERVICE_ICMP);
[21580dd]617 return EOK;
618 default:
619 return ENOTSUP;
620 }
621}
622
[aadf01e]623int icmp_process_echo_reply(packet_t packet, icmp_header_ref header, icmp_type_t type, icmp_code_t code){
624 int reply_key;
625 icmp_reply_ref reply;
[21580dd]626
627 // compute the reply key
[aadf01e]628 reply_key = ICMP_GET_REPLY_KEY(header->un.echo.identifier, header->un.echo.sequence_number);
629 pq_release(icmp_globals.net_phone, packet_get_id(packet));
[21580dd]630 // lock the globals
[aadf01e]631 fibril_rwlock_write_lock(&icmp_globals.lock);
[21580dd]632 // find the pending reply
[aadf01e]633 reply = icmp_replies_find(&icmp_globals.replies, reply_key);
634 if(reply){
[21580dd]635 // set the result
636 reply->result = type;
[b5cbff4]637 // notify the waiting fibril
[aadf01e]638 fibril_condvar_signal(&reply->condvar);
[21580dd]639 }
[936835e]640 fibril_rwlock_write_unlock(&icmp_globals.lock);
[21580dd]641 return EOK;
642}
643
[aadf01e]644int icmp_message(ipc_callid_t callid, ipc_call_t * call, ipc_call_t * answer, int * answer_count){
[21580dd]645 ERROR_DECLARE;
646
[aadf01e]647 packet_t packet;
[21580dd]648
[aadf01e]649 *answer_count = 0;
650 switch(IPC_GET_METHOD(*call)){
[21580dd]651 case NET_TL_RECEIVED:
[aadf01e]652 if(! ERROR_OCCURRED(packet_translate(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){
653 ERROR_CODE = icmp_received_msg(IPC_GET_DEVICE(call), packet, SERVICE_ICMP, IPC_GET_ERROR(call));
[21580dd]654 }
655 return ERROR_CODE;
656 case NET_ICMP_INIT:
[aadf01e]657 return icmp_process_client_messages(callid, * call);
[21580dd]658 default:
[aadf01e]659 return icmp_process_message(call);
[21580dd]660 }
661 return ENOTSUP;
662}
663
[aadf01e]664int icmp_process_client_messages(ipc_callid_t callid, ipc_call_t call){
[21580dd]665 ERROR_DECLARE;
666
[aadf01e]667 bool keep_on_going = true;
[21580dd]668// fibril_rwlock_t lock;
[aadf01e]669 ipc_call_t answer;
670 int answer_count;
671 size_t length;
672 struct sockaddr * addr;
673 ipc_callid_t data_callid;
674 icmp_echo_ref echo_data;
675 int res;
[21580dd]676
677 /*
678 * Accept the connection
679 * - Answer the first NET_ICMP_INIT call.
680 */
[2e99277]681 res = EOK;
682 answer_count = 0;
[21580dd]683
[aadf01e]684// fibril_rwlock_initialize(&lock);
[21580dd]685
[aadf01e]686 echo_data = (icmp_echo_ref) malloc(sizeof(*echo_data));
687 if(! echo_data){
688 return ENOMEM;
689 }
[2e99277]690
[21580dd]691 // assign a new identifier
[aadf01e]692 fibril_rwlock_write_lock(&icmp_globals.lock);
693 res = icmp_bind_free_id(echo_data);
694 fibril_rwlock_write_unlock(&icmp_globals.lock);
695 if(res < 0){
696 free(echo_data);
[2e99277]697 return res;
[21580dd]698 }
699
[aadf01e]700 while(keep_on_going){
[2e99277]701
702 // answer the call
[aadf01e]703 answer_call(callid, res, &answer, answer_count);
[2e99277]704
705 // refresh data
[aadf01e]706 refresh_answer(&answer, &answer_count);
[21580dd]707
[2e99277]708 // get the next call
[aadf01e]709 callid = async_get_call(&call);
[21580dd]710
[2e99277]711 // process the call
[aadf01e]712 switch(IPC_GET_METHOD(call)){
[21580dd]713 case IPC_M_PHONE_HUNGUP:
714 keep_on_going = false;
[2e99277]715 res = EHANGUP;
[21580dd]716 break;
717 case NET_ICMP_ECHO:
[aadf01e]718// fibril_rwlock_write_lock(&lock);
719 if(! async_data_write_receive(&data_callid, &length)){
[2e99277]720 res = EINVAL;
[21580dd]721 }else{
[aadf01e]722 addr = malloc(length);
723 if(! addr){
[2e99277]724 res = ENOMEM;
[21580dd]725 }else{
[aadf01e]726 if(! ERROR_OCCURRED(async_data_write_finalize(data_callid, addr, length))){
727 fibril_rwlock_write_lock(&icmp_globals.lock);
728 res = icmp_echo(echo_data->identifier, echo_data->sequence_number, ICMP_GET_SIZE(call), ICMP_GET_TIMEOUT(call), ICMP_GET_TTL(call), ICMP_GET_TOS(call), ICMP_GET_DONT_FRAGMENT(call), addr, (socklen_t) length);
729 fibril_rwlock_write_unlock(&icmp_globals.lock);
730 free(addr);
731 if(echo_data->sequence_number < MAX_UINT16){
[21580dd]732 ++ echo_data->sequence_number;
733 }else{
734 echo_data->sequence_number = 0;
735 }
[2e99277]736 }else{
737 res = ERROR_CODE;
[21580dd]738 }
739 }
740 }
[aadf01e]741// fibril_rwlock_write_unlock(&lock);
[21580dd]742 break;
743 default:
[aadf01e]744 res = icmp_process_message(&call);
[21580dd]745 }
746 }
747
748 // release the identifier
[aadf01e]749 fibril_rwlock_write_lock(&icmp_globals.lock);
750 icmp_echo_data_exclude(&icmp_globals.echo_data, echo_data->identifier);
751 fibril_rwlock_write_unlock(&icmp_globals.lock);
[2e99277]752 return res;
[21580dd]753}
754
[aadf01e]755int icmp_process_message(ipc_call_t * call){
[21580dd]756 ERROR_DECLARE;
757
[aadf01e]758 packet_t packet;
[21580dd]759
[aadf01e]760 switch(IPC_GET_METHOD(*call)){
[21580dd]761 case NET_ICMP_DEST_UNREACH:
[aadf01e]762 if(! ERROR_OCCURRED(packet_translate(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){
763 ERROR_CODE = icmp_destination_unreachable_msg(0, ICMP_GET_CODE(call), ICMP_GET_MTU(call), packet);
[21580dd]764 }
765 return ERROR_CODE;
766 case NET_ICMP_SOURCE_QUENCH:
[aadf01e]767 if(! ERROR_OCCURRED(packet_translate(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){
768 ERROR_CODE = icmp_source_quench_msg(0, packet);
[21580dd]769 }
770 return ERROR_CODE;
771 case NET_ICMP_TIME_EXCEEDED:
[aadf01e]772 if(! ERROR_OCCURRED(packet_translate(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){
773 ERROR_CODE = icmp_time_exceeded_msg(0, ICMP_GET_CODE(call), packet);
[21580dd]774 }
775 return ERROR_CODE;
776 case NET_ICMP_PARAMETERPROB:
[aadf01e]777 if(! ERROR_OCCURRED(packet_translate(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){
778 ERROR_CODE = icmp_parameter_problem_msg(0, ICMP_GET_CODE(call), ICMP_GET_POINTER(call), packet);
[21580dd]779 }
780 return ERROR_CODE;
781 default:
782 return ENOTSUP;
783 }
784}
785
[aadf01e]786int icmp_release_and_return(packet_t packet, int result){
787 pq_release(icmp_globals.net_phone, packet_get_id(packet));
[21580dd]788 return result;
789}
790
[aadf01e]791int icmp_bind_free_id(icmp_echo_ref echo_data){
792 icmp_param_t index;
[21580dd]793
[aadf01e]794 if(! echo_data){
795 return EBADMEM;
796 }
[21580dd]797 // from the last used one
798 index = icmp_globals.last_used_id;
799 do{
800 ++ index;
801 // til the range end
[aadf01e]802 if(index >= ICMP_FREE_IDS_END){
[21580dd]803 // start from the range beginning
804 index = ICMP_FREE_IDS_START - 1;
805 do{
806 ++ index;
807 // til the last used one
[aadf01e]808 if(index >= icmp_globals.last_used_id){
[21580dd]809 // none found
810 return ENOTCONN;
811 }
[aadf01e]812 }while(icmp_echo_data_find(&icmp_globals.echo_data, index) != NULL);
[21580dd]813 // found, break immediately
814 break;
815 }
[aadf01e]816 }while(icmp_echo_data_find(&icmp_globals.echo_data, index) != NULL);
[21580dd]817 echo_data->identifier = index;
818 echo_data->sequence_number = 0;
[aadf01e]819 return icmp_echo_data_add(&icmp_globals.echo_data, index, echo_data);
[21580dd]820}
821
822/** @}
823 */
Note: See TracBrowser for help on using the repository browser.