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

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

Move ip_protocols.h from libsocket to libc.

Remove currently unused defines.

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