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

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

Move socket_codes.h from libsocket to libc.

Remove stuff that was probably inappropriatelly taken from a Linux kernel
header.

Remove unneeded AF and PF codes.

Start assigning AF and PF codes sequentially. (These are not standardized.)

  • Property mode set to 100644
File size: 27.3 KB
Line 
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>
43#include <str.h>
44#include <ipc/ipc.h>
45#include <ipc/services.h>
46#include <sys/time.h>
47#include <sys/types.h>
48#include <errno.h>
49#include <err.h>
50
51#include <net/socket_codes.h>
52
53#include <net_messages.h>
54#include <net_modules.h>
55#include <packet/packet_client.h>
56#include <packet_remote.h>
57#include <net_byteorder.h>
58#include <net_checksum.h>
59#include <icmp_api.h>
60#include <icmp_client.h>
61#include <icmp_codes.h>
62#include <icmp_common.h>
63#include <icmp_interface.h>
64#include <il_interface.h>
65#include <inet.h>
66#include <ip_client.h>
67#include <ip_interface.h>
68#include <ip_protocols.h>
69#include <net_interface.h>
70#include <tl_messages.h>
71#include <tl_interface.h>
72#include <tl_local.h>
73#include <icmp_messages.h>
74#include <icmp_header.h>
75
76#include "icmp.h"
77#include "icmp_module.h"
78
79/** ICMP module name.
80 */
81#define NAME "ICMP protocol"
82
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 */
101#define ICMP_FREE_IDS_END UINT16_MAX
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 */
108#define ICMP_CHECKSUM(header, length) htons(ip_checksum((uint8_t *) (header), (length)))
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 */
119#define ICMP_GET_REPLY_KEY(id, sequence) (((id) << 16) | (sequence &0xFFFF))
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 */
131int icmp_received_msg(device_id_t device_id, packet_t packet, services_t receiver, services_t error);
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 */
146int icmp_process_packet(packet_t packet, services_t error);
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 */
157int icmp_process_client_messages(ipc_callid_t callid, ipc_call_t call);
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 */
170int icmp_process_message(ipc_call_t * call);
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 */
177int icmp_release_and_return(packet_t packet, int result);
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.
181 * Blocks the caller until the reply or the timeout occurs.
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 */
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);
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 */
207icmp_header_ref icmp_prepare_packet(packet_t packet);
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 */
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);
225
226/** Tries to set the pending reply result as the received message type.
227 * If the reply data is not present, the reply timed out and the other fibril
228 * is already awake.
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 */
236int icmp_process_echo_reply(packet_t packet, icmp_header_ref header, icmp_type_t type, icmp_code_t code);
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 */
245int icmp_bind_free_id(icmp_echo_ref echo_data);
246
247/** ICMP global data.
248 */
249icmp_globals_t icmp_globals;
250
251INT_MAP_IMPLEMENT(icmp_replies, icmp_reply_t);
252
253INT_MAP_IMPLEMENT(icmp_echo_data, icmp_echo_t);
254
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;
258
259 fibril_rwlock_write_lock(&icmp_globals.lock);
260 // use the phone as the echo data index
261 echo_data = icmp_echo_data_find(&icmp_globals.echo_data, icmp_phone);
262 if(! echo_data){
263 res = ENOENT;
264 }else{
265 res = icmp_echo(echo_data->identifier, echo_data->sequence_number, size, timeout, ttl, tos, dont_fragment, addr, addrlen);
266 if(echo_data->sequence_number < UINT16_MAX){
267 ++ echo_data->sequence_number;
268 }else{
269 echo_data->sequence_number = 0;
270 }
271 }
272 fibril_rwlock_write_unlock(&icmp_globals.lock);
273 return res;
274}
275
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){
277 ERROR_DECLARE;
278
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;
287
288 if(addrlen <= 0){
289 return EINVAL;
290 }
291 length = (size_t) addrlen;
292 // TODO do not ask all the time
293 ERROR_PROPAGATE(ip_packet_size_req(icmp_globals.ip_phone, -1, &icmp_globals.packet_dimension));
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);
295 if(! packet){
296 return ENOMEM;
297 }
298
299 // prepare the requesting packet
300 // set the destination address
301 if(ERROR_OCCURRED(packet_set_addr(packet, NULL, (const uint8_t *) addr, length))){
302 return icmp_release_and_return(packet, ERROR_CODE);
303 }
304 // allocate space in the packet
305 data = (uint8_t *) packet_suffix(packet, size);
306 if(! data){
307 return icmp_release_and_return(packet, ENOMEM);
308 }
309 // fill the data
310 length = 0;
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);
314 }
315 memcpy(data + length, ICMP_ECHO_TEXT, size - length);
316 // prefix the header
317 header = PACKET_PREFIX(packet, icmp_header_t);
318 if(! header){
319 return icmp_release_and_return(packet, ENOMEM);
320 }
321 bzero(header, sizeof(*header));
322 header->un.echo.identifier = id;
323 header->un.echo.sequence_number = sequence;
324
325 // prepare the reply structure
326 reply = malloc(sizeof(*reply));
327 if(! reply){
328 return icmp_release_and_return(packet, ENOMEM);
329 }
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);
338 }
339
340 // unlock the globals so that we can wait for the reply
341 fibril_rwlock_write_unlock(&icmp_globals.lock);
342
343 // send the request
344 icmp_send_packet(ICMP_ECHO, 0, packet, header, 0, ttl, tos, dont_fragment);
345
346 // wait for the reply
347 // timeout in microseconds
348 if(ERROR_OCCURRED(fibril_condvar_wait_timeout(&reply->condvar, &reply->mutex, timeout * 1000))){
349 result = ERROR_CODE;
350 }else{
351 // read the result
352 result = reply->result;
353 }
354
355 // drop the reply mutex before locking the globals again
356 fibril_mutex_unlock(&reply->mutex);
357 fibril_rwlock_write_lock(&icmp_globals.lock);
358
359 // destroy the reply structure
360 icmp_replies_exclude_index(&icmp_globals.replies, index);
361 return result;
362}
363
364int icmp_destination_unreachable_msg(int icmp_phone, icmp_code_t code, icmp_param_t mtu, packet_t packet){
365 icmp_header_ref header;
366
367 header = icmp_prepare_packet(packet);
368 if(! header){
369 return icmp_release_and_return(packet, ENOMEM);
370 }
371 if(mtu){
372 header->un.frag.mtu = mtu;
373 }
374 return icmp_send_packet(ICMP_DEST_UNREACH, code, packet, header, SERVICE_ICMP, 0, 0, 0);
375}
376
377int icmp_source_quench_msg(int icmp_phone, packet_t packet){
378 icmp_header_ref header;
379
380 header = icmp_prepare_packet(packet);
381 if(! header){
382 return icmp_release_and_return(packet, ENOMEM);
383 }
384 return icmp_send_packet(ICMP_SOURCE_QUENCH, 0, packet, header, SERVICE_ICMP, 0, 0, 0);
385}
386
387int icmp_time_exceeded_msg(int icmp_phone, icmp_code_t code, packet_t packet){
388 icmp_header_ref header;
389
390 header = icmp_prepare_packet(packet);
391 if(! header){
392 return icmp_release_and_return(packet, ENOMEM);
393 }
394 return icmp_send_packet(ICMP_TIME_EXCEEDED, code, packet, header, SERVICE_ICMP, 0, 0, 0);
395}
396
397int icmp_parameter_problem_msg(int icmp_phone, icmp_code_t code, icmp_param_t pointer, packet_t packet){
398 icmp_header_ref header;
399
400 header = icmp_prepare_packet(packet);
401 if(! header){
402 return icmp_release_and_return(packet, ENOMEM);
403 }
404 header->un.param.pointer = pointer;
405 return icmp_send_packet(ICMP_PARAMETERPROB, code, packet, header, SERVICE_ICMP, 0, 0, 0);
406}
407
408icmp_header_ref icmp_prepare_packet(packet_t packet){
409 icmp_header_ref header;
410 size_t header_length;
411 size_t total_length;
412
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 }
421 // truncate if longer than 64 bits (without the IP header)
422 if((total_length > header_length + ICMP_KEEP_LENGTH)
423 && (packet_trim(packet, 0, total_length - header_length - ICMP_KEEP_LENGTH) != EOK)){
424 return NULL;
425 }
426 header = PACKET_PREFIX(packet, icmp_header_t);
427 if(! header){
428 return NULL;
429 }
430 bzero(header, sizeof(*header));
431 return header;
432}
433
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){
435 ERROR_DECLARE;
436
437 // do not send an error if disabled
438 if(error && (! icmp_globals.error_reporting)){
439 return icmp_release_and_return(packet, EPERM);
440 }
441 header->type = type;
442 header->code = code;
443 header->checksum = 0;
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);
447 }
448 return ip_send_msg(icmp_globals.ip_phone, -1, packet, SERVICE_ICMP, error);
449}
450
451int icmp_connect_module(services_t service, suseconds_t timeout){
452 icmp_echo_ref echo_data;
453 icmp_param_t id;
454 int index;
455
456 echo_data = (icmp_echo_ref) malloc(sizeof(*echo_data));
457 if(! echo_data){
458 return ENOMEM;
459 }
460 // assign a new identifier
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);
466 return index;
467 }else{
468 id = echo_data->identifier;
469 fibril_rwlock_write_unlock(&icmp_globals.lock);
470 // return the echo data identifier as the ICMP phone
471 return id;
472 }
473}
474
475int icmp_initialize(async_client_conn_t client_connection){
476 ERROR_DECLARE;
477
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){
489 return icmp_globals.ip_phone;
490 }
491 ERROR_PROPAGATE(ip_packet_size_req(icmp_globals.ip_phone, -1, &icmp_globals.packet_dimension));
492 icmp_globals.packet_dimension.prefix += ICMP_HEADER_SIZE;
493 icmp_globals.packet_dimension.content -= ICMP_HEADER_SIZE;
494 // get configuration
495 icmp_globals.error_reporting = NET_DEFAULT_ICMP_ERROR_REPORTING;
496 icmp_globals.echo_replying = NET_DEFAULT_ICMP_ECHO_REPLYING;
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');
502 }
503 if(configuration[1].value){
504 icmp_globals.echo_replying = (configuration[1].value[0] == 'y');
505 }
506 net_free_settings(configuration, data);
507 }
508 fibril_rwlock_write_unlock(&icmp_globals.lock);
509 return EOK;
510}
511
512int icmp_received_msg(device_id_t device_id, packet_t packet, services_t receiver, services_t error){
513 ERROR_DECLARE;
514
515 if(ERROR_OCCURRED(icmp_process_packet(packet, error))){
516 return icmp_release_and_return(packet, ERROR_CODE);
517 }
518
519 return EOK;
520}
521
522int icmp_process_packet(packet_t packet, services_t error){
523 ERROR_DECLARE;
524
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){
536 case SERVICE_ICMP:
537 // process error
538 result = icmp_client_process_packet(packet, &type, &code, NULL, NULL);
539 if(result < 0){
540 return result;
541 }
542 length = (size_t) result;
543 // remove the error header
544 ERROR_PROPAGATE(packet_trim(packet, length, 0));
545 break;
546 default:
547 return ENOTSUP;
548 }
549 }
550 // get rid of the ip header
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 }
565 // get icmp header
566 header = (icmp_header_ref) data;
567 // checksum
568 if(header->checksum){
569 while(ICMP_CHECKSUM(header, length) != IP_CHECKSUM_ZERO){
570 // set the original message type on error notification
571 // type swap observed in Qemu
572 if(error){
573 switch(header->type){
574 case ICMP_ECHOREPLY:
575 header->type = ICMP_ECHO;
576 continue;
577 }
578 }
579 return EINVAL;
580 }
581 }
582 switch(header->type){
583 case ICMP_ECHOREPLY:
584 if(error){
585 return icmp_process_echo_reply(packet, header, type, code);
586 }else{
587 return icmp_process_echo_reply(packet, header, ICMP_ECHO, 0);
588 }
589 case ICMP_ECHO:
590 if(error){
591 return icmp_process_echo_reply(packet, header, type, code);
592 // do not send a reply if disabled
593 }else if(icmp_globals.echo_replying){
594 addrlen = packet_get_addr(packet, &src, NULL);
595 if((addrlen > 0)
596 // set both addresses to the source one (avoids the source address deletion before setting the destination one)
597 && (packet_set_addr(packet, src, src, (size_t) addrlen) == EOK)){
598 // send the reply
599 icmp_send_packet(ICMP_ECHOREPLY, 0, packet, header, 0, 0, 0, 0);
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:
619 ip_received_error_msg(icmp_globals.ip_phone, -1, packet, SERVICE_IP, SERVICE_ICMP);
620 return EOK;
621 default:
622 return ENOTSUP;
623 }
624}
625
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;
629
630 // compute the reply key
631 reply_key = ICMP_GET_REPLY_KEY(header->un.echo.identifier, header->un.echo.sequence_number);
632 pq_release_remote(icmp_globals.net_phone, packet_get_id(packet));
633 // lock the globals
634 fibril_rwlock_write_lock(&icmp_globals.lock);
635 // find the pending reply
636 reply = icmp_replies_find(&icmp_globals.replies, reply_key);
637 if(reply){
638 // set the result
639 reply->result = type;
640 // notify the waiting fibril
641 fibril_condvar_signal(&reply->condvar);
642 }
643 fibril_rwlock_write_unlock(&icmp_globals.lock);
644 return EOK;
645}
646
647int icmp_message_standalone(ipc_callid_t callid, ipc_call_t * call, ipc_call_t * answer, int * answer_count){
648 ERROR_DECLARE;
649
650 packet_t packet;
651
652 *answer_count = 0;
653 switch(IPC_GET_METHOD(*call)){
654 case NET_TL_RECEIVED:
655 if(! ERROR_OCCURRED(packet_translate_remote(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){
656 ERROR_CODE = icmp_received_msg(IPC_GET_DEVICE(call), packet, SERVICE_ICMP, IPC_GET_ERROR(call));
657 }
658 return ERROR_CODE;
659 case NET_ICMP_INIT:
660 return icmp_process_client_messages(callid, * call);
661 default:
662 return icmp_process_message(call);
663 }
664 return ENOTSUP;
665}
666
667int icmp_process_client_messages(ipc_callid_t callid, ipc_call_t call){
668 ERROR_DECLARE;
669
670 bool keep_on_going = true;
671// fibril_rwlock_t lock;
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;
679
680 /*
681 * Accept the connection
682 * - Answer the first NET_ICMP_INIT call.
683 */
684 res = EOK;
685 answer_count = 0;
686
687// fibril_rwlock_initialize(&lock);
688
689 echo_data = (icmp_echo_ref) malloc(sizeof(*echo_data));
690 if(! echo_data){
691 return ENOMEM;
692 }
693
694 // assign a new identifier
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);
700 return res;
701 }
702
703 while(keep_on_going){
704
705 // answer the call
706 answer_call(callid, res, &answer, answer_count);
707
708 // refresh data
709 refresh_answer(&answer, &answer_count);
710
711 // get the next call
712 callid = async_get_call(&call);
713
714 // process the call
715 switch(IPC_GET_METHOD(call)){
716 case IPC_M_PHONE_HUNGUP:
717 keep_on_going = false;
718 res = EHANGUP;
719 break;
720 case NET_ICMP_ECHO:
721// fibril_rwlock_write_lock(&lock);
722 if(! async_data_write_receive(&data_callid, &length)){
723 res = EINVAL;
724 }else{
725 addr = malloc(length);
726 if(! addr){
727 res = ENOMEM;
728 }else{
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);
734 if(echo_data->sequence_number < UINT16_MAX){
735 ++ echo_data->sequence_number;
736 }else{
737 echo_data->sequence_number = 0;
738 }
739 }else{
740 res = ERROR_CODE;
741 }
742 }
743 }
744// fibril_rwlock_write_unlock(&lock);
745 break;
746 default:
747 res = icmp_process_message(&call);
748 }
749 }
750
751 // release the identifier
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);
755 return res;
756}
757
758int icmp_process_message(ipc_call_t * call){
759 ERROR_DECLARE;
760
761 packet_t packet;
762
763 switch(IPC_GET_METHOD(*call)){
764 case NET_ICMP_DEST_UNREACH:
765 if(! ERROR_OCCURRED(packet_translate_remote(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){
766 ERROR_CODE = icmp_destination_unreachable_msg(0, ICMP_GET_CODE(call), ICMP_GET_MTU(call), packet);
767 }
768 return ERROR_CODE;
769 case NET_ICMP_SOURCE_QUENCH:
770 if(! ERROR_OCCURRED(packet_translate_remote(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){
771 ERROR_CODE = icmp_source_quench_msg(0, packet);
772 }
773 return ERROR_CODE;
774 case NET_ICMP_TIME_EXCEEDED:
775 if(! ERROR_OCCURRED(packet_translate_remote(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){
776 ERROR_CODE = icmp_time_exceeded_msg(0, ICMP_GET_CODE(call), packet);
777 }
778 return ERROR_CODE;
779 case NET_ICMP_PARAMETERPROB:
780 if(! ERROR_OCCURRED(packet_translate_remote(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){
781 ERROR_CODE = icmp_parameter_problem_msg(0, ICMP_GET_CODE(call), ICMP_GET_POINTER(call), packet);
782 }
783 return ERROR_CODE;
784 default:
785 return ENOTSUP;
786 }
787}
788
789int icmp_release_and_return(packet_t packet, int result){
790 pq_release_remote(icmp_globals.net_phone, packet_get_id(packet));
791 return result;
792}
793
794int icmp_bind_free_id(icmp_echo_ref echo_data){
795 icmp_param_t index;
796
797 if(! echo_data){
798 return EBADMEM;
799 }
800 // from the last used one
801 index = icmp_globals.last_used_id;
802 do{
803 ++ index;
804 // til the range end
805 if(index >= ICMP_FREE_IDS_END){
806 // start from the range beginning
807 index = ICMP_FREE_IDS_START - 1;
808 do{
809 ++ index;
810 // til the last used one
811 if(index >= icmp_globals.last_used_id){
812 // none found
813 return ENOTCONN;
814 }
815 }while(icmp_echo_data_find(&icmp_globals.echo_data, index) != NULL);
816 // found, break immediately
817 break;
818 }
819 }while(icmp_echo_data_find(&icmp_globals.echo_data, index) != NULL);
820 echo_data->identifier = index;
821 echo_data->sequence_number = 0;
822 return icmp_echo_data_add(&icmp_globals.echo_data, index, echo_data);
823}
824
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 */
851 int res = tl_module_message_standalone(callid, &call, &answer,
852 &answer_count);
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 */
877 if (ERROR_OCCURRED(tl_module_start_standalone(tl_client_connection)))
878 return ERROR_CODE;
879
880 return EOK;
881}
882
883/** @}
884 */
Note: See TracBrowser for help on using the repository browser.