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

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

Move transport layer modules messages definitions to standard library.

  • Property mode set to 100644
File size: 26.7 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 <ipc/net.h>
47#include <ipc/tl.h>
48#include <ipc/icmp.h>
49#include <sys/time.h>
50#include <sys/types.h>
51#include <byteorder.h>
52#include <errno.h>
53#include <err.h>
54
55#include <net/socket_codes.h>
56#include <net/ip_protocols.h>
57#include <net/inet.h>
58
59#include <net/modules.h>
60#include <packet_client.h>
61#include <packet_remote.h>
62#include <net_checksum.h>
63#include <net/icmp_api.h>
64#include <icmp_client.h>
65#include <net/icmp_codes.h>
66#include <net/icmp_common.h>
67#include <icmp_interface.h>
68#include <il_interface.h>
69#include <ip_client.h>
70#include <ip_interface.h>
71#include <net_interface.h>
72#include <tl_interface.h>
73#include <tl_local.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_initialize(async_client_conn_t client_connection){
452 ERROR_DECLARE;
453
454 measured_string_t names[] = {{str_dup("ICMP_ERROR_REPORTING"), 20}, {str_dup("ICMP_ECHO_REPLYING"), 18}};
455 measured_string_ref configuration;
456 size_t count = sizeof(names) / sizeof(measured_string_t);
457 char * data;
458
459 fibril_rwlock_initialize(&icmp_globals.lock);
460 fibril_rwlock_write_lock(&icmp_globals.lock);
461 icmp_replies_initialize(&icmp_globals.replies);
462 icmp_echo_data_initialize(&icmp_globals.echo_data);
463 icmp_globals.ip_phone = ip_bind_service(SERVICE_IP, IPPROTO_ICMP, SERVICE_ICMP, client_connection);
464 if(icmp_globals.ip_phone < 0){
465 return icmp_globals.ip_phone;
466 }
467 ERROR_PROPAGATE(ip_packet_size_req(icmp_globals.ip_phone, -1, &icmp_globals.packet_dimension));
468 icmp_globals.packet_dimension.prefix += ICMP_HEADER_SIZE;
469 icmp_globals.packet_dimension.content -= ICMP_HEADER_SIZE;
470 // get configuration
471 icmp_globals.error_reporting = NET_DEFAULT_ICMP_ERROR_REPORTING;
472 icmp_globals.echo_replying = NET_DEFAULT_ICMP_ECHO_REPLYING;
473 configuration = &names[0];
474 ERROR_PROPAGATE(net_get_conf_req(icmp_globals.net_phone, &configuration, count, &data));
475 if(configuration){
476 if(configuration[0].value){
477 icmp_globals.error_reporting = (configuration[0].value[0] == 'y');
478 }
479 if(configuration[1].value){
480 icmp_globals.echo_replying = (configuration[1].value[0] == 'y');
481 }
482 net_free_settings(configuration, data);
483 }
484 fibril_rwlock_write_unlock(&icmp_globals.lock);
485 return EOK;
486}
487
488int icmp_received_msg(device_id_t device_id, packet_t packet, services_t receiver, services_t error){
489 ERROR_DECLARE;
490
491 if(ERROR_OCCURRED(icmp_process_packet(packet, error))){
492 return icmp_release_and_return(packet, ERROR_CODE);
493 }
494
495 return EOK;
496}
497
498int icmp_process_packet(packet_t packet, services_t error){
499 ERROR_DECLARE;
500
501 size_t length;
502 uint8_t * src;
503 int addrlen;
504 int result;
505 void * data;
506 icmp_header_ref header;
507 icmp_type_t type;
508 icmp_code_t code;
509
510 if(error){
511 switch(error){
512 case SERVICE_ICMP:
513 // process error
514 result = icmp_client_process_packet(packet, &type, &code, NULL, NULL);
515 if(result < 0){
516 return result;
517 }
518 length = (size_t) result;
519 // remove the error header
520 ERROR_PROPAGATE(packet_trim(packet, length, 0));
521 break;
522 default:
523 return ENOTSUP;
524 }
525 }
526 // get rid of the ip header
527 length = ip_client_header_length(packet);
528 ERROR_PROPAGATE(packet_trim(packet, length, 0));
529
530 length = packet_get_data_length(packet);
531 if(length <= 0){
532 return EINVAL;
533 }
534 if(length < ICMP_HEADER_SIZE){
535 return EINVAL;
536 }
537 data = packet_get_data(packet);
538 if(! data){
539 return EINVAL;
540 }
541 // get icmp header
542 header = (icmp_header_ref) data;
543 // checksum
544 if(header->checksum){
545 while(ICMP_CHECKSUM(header, length) != IP_CHECKSUM_ZERO){
546 // set the original message type on error notification
547 // type swap observed in Qemu
548 if(error){
549 switch(header->type){
550 case ICMP_ECHOREPLY:
551 header->type = ICMP_ECHO;
552 continue;
553 }
554 }
555 return EINVAL;
556 }
557 }
558 switch(header->type){
559 case ICMP_ECHOREPLY:
560 if(error){
561 return icmp_process_echo_reply(packet, header, type, code);
562 }else{
563 return icmp_process_echo_reply(packet, header, ICMP_ECHO, 0);
564 }
565 case ICMP_ECHO:
566 if(error){
567 return icmp_process_echo_reply(packet, header, type, code);
568 // do not send a reply if disabled
569 }else if(icmp_globals.echo_replying){
570 addrlen = packet_get_addr(packet, &src, NULL);
571 if((addrlen > 0)
572 // set both addresses to the source one (avoids the source address deletion before setting the destination one)
573 && (packet_set_addr(packet, src, src, (size_t) addrlen) == EOK)){
574 // send the reply
575 icmp_send_packet(ICMP_ECHOREPLY, 0, packet, header, 0, 0, 0, 0);
576 return EOK;
577 }else{
578 return EINVAL;
579 }
580 }else{
581 return EPERM;
582 }
583 case ICMP_DEST_UNREACH:
584 case ICMP_SOURCE_QUENCH:
585 case ICMP_REDIRECT:
586 case ICMP_ALTERNATE_ADDR:
587 case ICMP_ROUTER_ADV:
588 case ICMP_ROUTER_SOL:
589 case ICMP_TIME_EXCEEDED:
590 case ICMP_PARAMETERPROB:
591 case ICMP_CONVERSION_ERROR:
592 case ICMP_REDIRECT_MOBILE:
593 case ICMP_SKIP:
594 case ICMP_PHOTURIS:
595 ip_received_error_msg(icmp_globals.ip_phone, -1, packet, SERVICE_IP, SERVICE_ICMP);
596 return EOK;
597 default:
598 return ENOTSUP;
599 }
600}
601
602int icmp_process_echo_reply(packet_t packet, icmp_header_ref header, icmp_type_t type, icmp_code_t code){
603 int reply_key;
604 icmp_reply_ref reply;
605
606 // compute the reply key
607 reply_key = ICMP_GET_REPLY_KEY(header->un.echo.identifier, header->un.echo.sequence_number);
608 pq_release_remote(icmp_globals.net_phone, packet_get_id(packet));
609 // lock the globals
610 fibril_rwlock_write_lock(&icmp_globals.lock);
611 // find the pending reply
612 reply = icmp_replies_find(&icmp_globals.replies, reply_key);
613 if(reply){
614 // set the result
615 reply->result = type;
616 // notify the waiting fibril
617 fibril_condvar_signal(&reply->condvar);
618 }
619 fibril_rwlock_write_unlock(&icmp_globals.lock);
620 return EOK;
621}
622
623int icmp_message_standalone(ipc_callid_t callid, ipc_call_t * call, ipc_call_t * answer, int * answer_count){
624 ERROR_DECLARE;
625
626 packet_t packet;
627
628 *answer_count = 0;
629 switch(IPC_GET_METHOD(*call)){
630 case NET_TL_RECEIVED:
631 if(! ERROR_OCCURRED(packet_translate_remote(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){
632 ERROR_CODE = icmp_received_msg(IPC_GET_DEVICE(call), packet, SERVICE_ICMP, IPC_GET_ERROR(call));
633 }
634 return ERROR_CODE;
635 case NET_ICMP_INIT:
636 return icmp_process_client_messages(callid, * call);
637 default:
638 return icmp_process_message(call);
639 }
640 return ENOTSUP;
641}
642
643int icmp_process_client_messages(ipc_callid_t callid, ipc_call_t call){
644 ERROR_DECLARE;
645
646 bool keep_on_going = true;
647// fibril_rwlock_t lock;
648 ipc_call_t answer;
649 int answer_count;
650 size_t length;
651 struct sockaddr * addr;
652 ipc_callid_t data_callid;
653 icmp_echo_ref echo_data;
654 int res;
655
656 /*
657 * Accept the connection
658 * - Answer the first NET_ICMP_INIT call.
659 */
660 res = EOK;
661 answer_count = 0;
662
663// fibril_rwlock_initialize(&lock);
664
665 echo_data = (icmp_echo_ref) malloc(sizeof(*echo_data));
666 if(! echo_data){
667 return ENOMEM;
668 }
669
670 // assign a new identifier
671 fibril_rwlock_write_lock(&icmp_globals.lock);
672 res = icmp_bind_free_id(echo_data);
673 fibril_rwlock_write_unlock(&icmp_globals.lock);
674 if(res < 0){
675 free(echo_data);
676 return res;
677 }
678
679 while(keep_on_going){
680
681 // answer the call
682 answer_call(callid, res, &answer, answer_count);
683
684 // refresh data
685 refresh_answer(&answer, &answer_count);
686
687 // get the next call
688 callid = async_get_call(&call);
689
690 // process the call
691 switch(IPC_GET_METHOD(call)){
692 case IPC_M_PHONE_HUNGUP:
693 keep_on_going = false;
694 res = EHANGUP;
695 break;
696 case NET_ICMP_ECHO:
697// fibril_rwlock_write_lock(&lock);
698 if(! async_data_write_receive(&data_callid, &length)){
699 res = EINVAL;
700 }else{
701 addr = malloc(length);
702 if(! addr){
703 res = ENOMEM;
704 }else{
705 if(! ERROR_OCCURRED(async_data_write_finalize(data_callid, addr, length))){
706 fibril_rwlock_write_lock(&icmp_globals.lock);
707 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);
708 fibril_rwlock_write_unlock(&icmp_globals.lock);
709 free(addr);
710 if(echo_data->sequence_number < UINT16_MAX){
711 ++ echo_data->sequence_number;
712 }else{
713 echo_data->sequence_number = 0;
714 }
715 }else{
716 res = ERROR_CODE;
717 }
718 }
719 }
720// fibril_rwlock_write_unlock(&lock);
721 break;
722 default:
723 res = icmp_process_message(&call);
724 }
725 }
726
727 // release the identifier
728 fibril_rwlock_write_lock(&icmp_globals.lock);
729 icmp_echo_data_exclude(&icmp_globals.echo_data, echo_data->identifier);
730 fibril_rwlock_write_unlock(&icmp_globals.lock);
731 return res;
732}
733
734int icmp_process_message(ipc_call_t * call){
735 ERROR_DECLARE;
736
737 packet_t packet;
738
739 switch(IPC_GET_METHOD(*call)){
740 case NET_ICMP_DEST_UNREACH:
741 if(! ERROR_OCCURRED(packet_translate_remote(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){
742 ERROR_CODE = icmp_destination_unreachable_msg(0, ICMP_GET_CODE(call), ICMP_GET_MTU(call), packet);
743 }
744 return ERROR_CODE;
745 case NET_ICMP_SOURCE_QUENCH:
746 if(! ERROR_OCCURRED(packet_translate_remote(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){
747 ERROR_CODE = icmp_source_quench_msg(0, packet);
748 }
749 return ERROR_CODE;
750 case NET_ICMP_TIME_EXCEEDED:
751 if(! ERROR_OCCURRED(packet_translate_remote(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){
752 ERROR_CODE = icmp_time_exceeded_msg(0, ICMP_GET_CODE(call), packet);
753 }
754 return ERROR_CODE;
755 case NET_ICMP_PARAMETERPROB:
756 if(! ERROR_OCCURRED(packet_translate_remote(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){
757 ERROR_CODE = icmp_parameter_problem_msg(0, ICMP_GET_CODE(call), ICMP_GET_POINTER(call), packet);
758 }
759 return ERROR_CODE;
760 default:
761 return ENOTSUP;
762 }
763}
764
765int icmp_release_and_return(packet_t packet, int result){
766 pq_release_remote(icmp_globals.net_phone, packet_get_id(packet));
767 return result;
768}
769
770int icmp_bind_free_id(icmp_echo_ref echo_data){
771 icmp_param_t index;
772
773 if(! echo_data){
774 return EBADMEM;
775 }
776 // from the last used one
777 index = icmp_globals.last_used_id;
778 do{
779 ++ index;
780 // til the range end
781 if(index >= ICMP_FREE_IDS_END){
782 // start from the range beginning
783 index = ICMP_FREE_IDS_START - 1;
784 do{
785 ++ index;
786 // til the last used one
787 if(index >= icmp_globals.last_used_id){
788 // none found
789 return ENOTCONN;
790 }
791 }while(icmp_echo_data_find(&icmp_globals.echo_data, index) != NULL);
792 // found, break immediately
793 break;
794 }
795 }while(icmp_echo_data_find(&icmp_globals.echo_data, index) != NULL);
796 echo_data->identifier = index;
797 echo_data->sequence_number = 0;
798 return icmp_echo_data_add(&icmp_globals.echo_data, index, echo_data);
799}
800
801/** Default thread for new connections.
802 *
803 * @param[in] iid The initial message identifier.
804 * @param[in] icall The initial message call structure.
805 *
806 */
807static void tl_client_connection(ipc_callid_t iid, ipc_call_t * icall)
808{
809 /*
810 * Accept the connection
811 * - Answer the first IPC_M_CONNECT_ME_TO call.
812 */
813 ipc_answer_0(iid, EOK);
814
815 while(true) {
816 ipc_call_t answer;
817 int answer_count;
818
819 /* Clear the answer structure */
820 refresh_answer(&answer, &answer_count);
821
822 /* Fetch the next message */
823 ipc_call_t call;
824 ipc_callid_t callid = async_get_call(&call);
825
826 /* Process the message */
827 int res = tl_module_message_standalone(callid, &call, &answer,
828 &answer_count);
829
830 /* End if said to either by the message or the processing result */
831 if ((IPC_GET_METHOD(call) == IPC_M_PHONE_HUNGUP) || (res == EHANGUP))
832 return;
833
834 /* Answer the message */
835 answer_call(callid, res, &answer, answer_count);
836 }
837}
838
839/** Starts the module.
840 *
841 * @param argc The count of the command line arguments. Ignored parameter.
842 * @param argv The command line parameters. Ignored parameter.
843 *
844 * @returns EOK on success.
845 * @returns Other error codes as defined for each specific module start function.
846 *
847 */
848int main(int argc, char *argv[])
849{
850 ERROR_DECLARE;
851
852 /* Start the module */
853 if (ERROR_OCCURRED(tl_module_start_standalone(tl_client_connection)))
854 return ERROR_CODE;
855
856 return EOK;
857}
858
859/** @}
860 */
Note: See TracBrowser for help on using the repository browser.