~martin-decky/helenos/rcu

« back to all changes in this revision

Viewing changes to uspace/srv/net/tl/icmp/icmp.c

  • Committer: Martin Decky
  • Date: 2012-04-30 09:56:41 UTC
  • mfrom: (1460.2.24 main-clone)
  • Revision ID: martin@decky.cz-20120430095641-knbfd26jq7hagfzx
mergeĀ mainlineĀ changes

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
 */
36
 
 
37
 
#include <async.h>
38
 
#include <atomic.h>
39
 
#include <fibril.h>
40
 
#include <fibril_synch.h>
41
 
#include <stdint.h>
42
 
#include <str.h>
43
 
#include <ipc/services.h>
44
 
#include <ipc/net.h>
45
 
#include <ipc/tl.h>
46
 
#include <ipc/icmp.h>
47
 
#include <sys/time.h>
48
 
#include <sys/types.h>
49
 
#include <byteorder.h>
50
 
#include <errno.h>
51
 
#include <adt/hash_table.h>
52
 
 
53
 
#include <net/socket_codes.h>
54
 
#include <net/ip_protocols.h>
55
 
#include <net/inet.h>
56
 
#include <net/modules.h>
57
 
#include <net/icmp_api.h>
58
 
#include <net/icmp_codes.h>
59
 
#include <net/icmp_common.h>
60
 
 
61
 
#include <packet_client.h>
62
 
#include <packet_remote.h>
63
 
#include <net_checksum.h>
64
 
#include <icmp_client.h>
65
 
#include <icmp_remote.h>
66
 
#include <il_remote.h>
67
 
#include <ip_client.h>
68
 
#include <ip_interface.h>
69
 
#include <net_interface.h>
70
 
#include <tl_remote.h>
71
 
#include <tl_skel.h>
72
 
#include <icmp_header.h>
73
 
 
74
 
/** ICMP module name */
75
 
#define NAME  "icmp"
76
 
 
77
 
/** Number of replies hash table keys */
78
 
#define REPLY_KEYS  2
79
 
 
80
 
/** Number of replies hash table buckets */
81
 
#define REPLY_BUCKETS  1024
82
 
 
83
 
/**
84
 
 * Original datagram length in bytes transfered to the error
85
 
 * notification message.
86
 
 */
87
 
#define ICMP_KEEP_LENGTH  8
88
 
 
89
 
/** Compute the ICMP datagram checksum.
90
 
 *
91
 
 * @param[in,out] header ICMP datagram header.
92
 
 * @param[in]     length Total datagram length.
93
 
 *
94
 
 * @return Computed checksum.
95
 
 *
96
 
 */
97
 
#define ICMP_CHECKSUM(header, length) \
98
 
        htons(ip_checksum((uint8_t *) (header), (length)))
99
 
 
100
 
/** An echo request datagrams pattern. */
101
 
#define ICMP_ECHO_TEXT  "ICMP hello from HelenOS."
102
 
 
103
 
/** ICMP reply data. */
104
 
typedef struct {
105
 
        /** Hash table link */
106
 
        link_t link;
107
 
        
108
 
        /** Reply identification and sequence */
109
 
        icmp_param_t id;
110
 
        icmp_param_t sequence;
111
 
        
112
 
        /** Reply signaling */
113
 
        fibril_condvar_t condvar;
114
 
        
115
 
        /** Reply result */
116
 
        int result;
117
 
} icmp_reply_t;
118
 
 
119
 
/** Global data */
120
 
static async_sess_t *net_sess = NULL;
121
 
static async_sess_t *ip_sess = NULL;
122
 
static bool error_reporting = true;
123
 
static bool echo_replying = true;
124
 
static packet_dimension_t icmp_dimension;
125
 
 
126
 
/** ICMP client identification counter */
127
 
static atomic_t icmp_client;
128
 
 
129
 
/** ICMP identifier and sequence number (client-specific) */
130
 
static fibril_local icmp_param_t icmp_id;
131
 
static fibril_local icmp_param_t icmp_seq;
132
 
 
133
 
/** Reply hash table */
134
 
static fibril_mutex_t reply_lock;
135
 
static hash_table_t replies;
136
 
 
137
 
static hash_index_t replies_hash(unsigned long key[])
138
 
{
139
 
        /*
140
 
         * ICMP identifier and sequence numbers
141
 
         * are 16-bit values.
142
 
         */
143
 
        hash_index_t index = ((key[0] & 0xffff) << 16) | (key[1] & 0xffff);
144
 
        return (index % REPLY_BUCKETS);
145
 
}
146
 
 
147
 
static int replies_compare(unsigned long key[], hash_count_t keys, link_t *item)
148
 
{
149
 
        icmp_reply_t *reply =
150
 
            hash_table_get_instance(item, icmp_reply_t, link);
151
 
        
152
 
        if (keys == 1)
153
 
                return (reply->id == key[0]);
154
 
        else
155
 
                return ((reply->id == key[0]) && (reply->sequence == key[1]));
156
 
}
157
 
 
158
 
static void replies_remove_callback(link_t *item)
159
 
{
160
 
}
161
 
 
162
 
static hash_table_operations_t reply_ops = {
163
 
        .hash = replies_hash,
164
 
        .compare = replies_compare,
165
 
        .remove_callback = replies_remove_callback
166
 
};
167
 
 
168
 
/** Release the packet and return the result.
169
 
 *
170
 
 * @param[in] packet Packet queue to be released.
171
 
 *
172
 
 */
173
 
static void icmp_release(packet_t *packet)
174
 
{
175
 
        pq_release_remote(net_sess, packet_get_id(packet));
176
 
}
177
 
 
178
 
/** Send the ICMP message.
179
 
 *
180
 
 * Set the message type and code and compute the checksum.
181
 
 * Error messages are sent only if allowed in the configuration.
182
 
 * Release the packet on errors.
183
 
 *
184
 
 * @param[in] type          Message type.
185
 
 * @param[in] code          Message code.
186
 
 * @param[in] packet        Message packet to be sent.
187
 
 * @param[in] header        ICMP header.
188
 
 * @param[in] error         Error service to be announced. Should be
189
 
 *                          SERVICE_ICMP or zero.
190
 
 * @param[in] ttl           Time to live.
191
 
 * @param[in] tos           Type of service.
192
 
 * @param[in] dont_fragment Disable fragmentation.
193
 
 *
194
 
 * @return EOK on success.
195
 
 * @return EPERM if the error message is not allowed.
196
 
 *
197
 
 */
198
 
static int icmp_send_packet(icmp_type_t type, icmp_code_t code,
199
 
    packet_t *packet, icmp_header_t *header, services_t error, ip_ttl_t ttl,
200
 
    ip_tos_t tos, bool dont_fragment)
201
 
{
202
 
        /* Do not send an error if disabled */
203
 
        if ((error) && (!error_reporting)) {
204
 
                icmp_release(packet);
205
 
                return EPERM;
206
 
        }
207
 
        
208
 
        header->type = type;
209
 
        header->code = code;
210
 
        
211
 
        /*
212
 
         * The checksum needs to be calculated
213
 
         * with a virtual checksum field set to
214
 
         * zero.
215
 
         */
216
 
        header->checksum = 0;
217
 
        header->checksum = ICMP_CHECKSUM(header,
218
 
            packet_get_data_length(packet));
219
 
        
220
 
        int rc = ip_client_prepare_packet(packet, IPPROTO_ICMP, ttl, tos,
221
 
            dont_fragment, 0);
222
 
        if (rc != EOK) {
223
 
                icmp_release(packet);
224
 
                return rc;
225
 
        }
226
 
        
227
 
        return ip_send_msg(ip_sess, -1, packet, SERVICE_ICMP, error);
228
 
}
229
 
 
230
 
/** Prepare the ICMP error packet.
231
 
 *
232
 
 * Truncate the original packet if longer than ICMP_KEEP_LENGTH bytes.
233
 
 * Prefix and return the ICMP header.
234
 
 *
235
 
 * @param[in,out] packet Original packet.
236
 
 *
237
 
 * @return The prefixed ICMP header.
238
 
 * @return NULL on errors.
239
 
 *
240
 
 */
241
 
static icmp_header_t *icmp_prepare_packet(packet_t *packet)
242
 
{
243
 
        size_t total_length = packet_get_data_length(packet);
244
 
        if (total_length <= 0)
245
 
                return NULL;
246
 
        
247
 
        size_t header_length = ip_client_header_length(packet);
248
 
        if (header_length <= 0)
249
 
                return NULL;
250
 
        
251
 
        /* Truncate if longer than 64 bits (without the IP header) */
252
 
        if ((total_length > header_length + ICMP_KEEP_LENGTH) &&
253
 
            (packet_trim(packet, 0,
254
 
            total_length - header_length - ICMP_KEEP_LENGTH) != EOK))
255
 
                return NULL;
256
 
        
257
 
        icmp_header_t *header = PACKET_PREFIX(packet, icmp_header_t);
258
 
        if (!header)
259
 
                return NULL;
260
 
        
261
 
        bzero(header, sizeof(*header));
262
 
        return header;
263
 
}
264
 
 
265
 
/** Request an echo message.
266
 
 *
267
 
 * Send a packet with specified parameters to the target host
268
 
 * and wait for the reply upto the given timeout.
269
 
 * Block the caller until the reply or the timeout occurs.
270
 
 *
271
 
 * @param[in] id            Message identifier.
272
 
 * @param[in] sequence      Message sequence parameter.
273
 
 * @param[in] size          Message data length in bytes.
274
 
 * @param[in] timeout       Timeout in miliseconds.
275
 
 * @param[in] ttl           Time to live.
276
 
 * @param[in] tos           Type of service.
277
 
 * @param[in] dont_fragment Disable fragmentation.
278
 
 * @param[in] addr          Target host address.
279
 
 * @param[in] addrlen       Torget host address length.
280
 
 *
281
 
 * @return ICMP_ECHO on success.
282
 
 * @return ETIMEOUT if the reply has not arrived before the
283
 
 *         timeout.
284
 
 * @return ICMP type of the received error notification.
285
 
 * @return EINVAL if the addrlen parameter is less or equal to
286
 
 *         zero.
287
 
 * @return ENOMEM if there is not enough memory left.
288
 
 *
289
 
 */
290
 
static int icmp_echo(icmp_param_t id, icmp_param_t sequence, size_t size,
291
 
    mseconds_t timeout, ip_ttl_t ttl, ip_tos_t tos, bool dont_fragment,
292
 
    const struct sockaddr *addr, socklen_t addrlen)
293
 
{
294
 
        if (addrlen <= 0)
295
 
                return EINVAL;
296
 
        
297
 
        size_t length = (size_t) addrlen;
298
 
        
299
 
        packet_t *packet = packet_get_4_remote(net_sess, size,
300
 
            icmp_dimension.addr_len, ICMP_HEADER_SIZE + icmp_dimension.prefix,
301
 
            icmp_dimension.suffix);
302
 
        if (!packet)
303
 
                return ENOMEM;
304
 
        
305
 
        /* Prepare the requesting packet, set the destination address. */
306
 
        int rc = packet_set_addr(packet, NULL, (const uint8_t *) addr, length);
307
 
        if (rc != EOK) {
308
 
                icmp_release(packet);
309
 
                return rc;
310
 
        }
311
 
        
312
 
        /* Allocate space in the packet */
313
 
        uint8_t *data = (uint8_t *) packet_suffix(packet, size);
314
 
        if (!data) {
315
 
                icmp_release(packet);
316
 
                return ENOMEM;
317
 
        }
318
 
        
319
 
        /* Fill the data */
320
 
        length = 0;
321
 
        while (size > length + sizeof(ICMP_ECHO_TEXT)) {
322
 
                memcpy(data + length, ICMP_ECHO_TEXT, sizeof(ICMP_ECHO_TEXT));
323
 
                length += sizeof(ICMP_ECHO_TEXT);
324
 
        }
325
 
        memcpy(data + length, ICMP_ECHO_TEXT, size - length);
326
 
        
327
 
        /* Prefix the header */
328
 
        icmp_header_t *header = PACKET_PREFIX(packet, icmp_header_t);
329
 
        if (!header) {
330
 
                icmp_release(packet);
331
 
                return ENOMEM;
332
 
        }
333
 
        
334
 
        bzero(header, sizeof(icmp_header_t));
335
 
        header->un.echo.identifier = id;
336
 
        header->un.echo.sequence_number = sequence;
337
 
        
338
 
        /* Prepare the reply structure */
339
 
        icmp_reply_t *reply = malloc(sizeof(icmp_reply_t));
340
 
        if (!reply) {
341
 
                icmp_release(packet);
342
 
                return ENOMEM;
343
 
        }
344
 
        
345
 
        reply->id = id;
346
 
        reply->sequence = sequence;
347
 
        fibril_condvar_initialize(&reply->condvar);
348
 
        
349
 
        /* Add the reply to the replies hash table */
350
 
        fibril_mutex_lock(&reply_lock);
351
 
        
352
 
        unsigned long key[REPLY_KEYS] = {id, sequence};
353
 
        hash_table_insert(&replies, key, &reply->link);
354
 
        
355
 
        /* Send the request */
356
 
        icmp_send_packet(ICMP_ECHO, 0, packet, header, 0, ttl, tos,
357
 
            dont_fragment);
358
 
        
359
 
        /* Wait for the reply. Timeout in microseconds. */
360
 
        rc = fibril_condvar_wait_timeout(&reply->condvar, &reply_lock,
361
 
            timeout * 1000);
362
 
        if (rc == EOK)
363
 
                rc = reply->result;
364
 
        
365
 
        /* Remove the reply from the replies hash table */
366
 
        hash_table_remove(&replies, key, REPLY_KEYS);
367
 
        
368
 
        fibril_mutex_unlock(&reply_lock);
369
 
        
370
 
        free(reply);
371
 
        
372
 
        return rc;
373
 
}
374
 
 
375
 
static int icmp_destination_unreachable(icmp_code_t code, icmp_param_t mtu,
376
 
    packet_t *packet)
377
 
{
378
 
        icmp_header_t *header = icmp_prepare_packet(packet);
379
 
        if (!header) {
380
 
                icmp_release(packet);
381
 
                return ENOMEM;
382
 
        }
383
 
        
384
 
        if (mtu)
385
 
                header->un.frag.mtu = mtu;
386
 
        
387
 
        return icmp_send_packet(ICMP_DEST_UNREACH, code, packet, header,
388
 
            SERVICE_ICMP, 0, 0, false);
389
 
}
390
 
 
391
 
static int icmp_source_quench(packet_t *packet)
392
 
{
393
 
        icmp_header_t *header = icmp_prepare_packet(packet);
394
 
        if (!header) {
395
 
                icmp_release(packet);
396
 
                return ENOMEM;
397
 
        }
398
 
        
399
 
        return icmp_send_packet(ICMP_SOURCE_QUENCH, 0, packet, header,
400
 
            SERVICE_ICMP, 0, 0, false);
401
 
}
402
 
 
403
 
static int icmp_time_exceeded(icmp_code_t code, packet_t *packet)
404
 
{
405
 
        icmp_header_t *header = icmp_prepare_packet(packet);
406
 
        if (!header) {
407
 
                icmp_release(packet);
408
 
                return ENOMEM;
409
 
        }
410
 
        
411
 
        return icmp_send_packet(ICMP_TIME_EXCEEDED, code, packet, header,
412
 
            SERVICE_ICMP, 0, 0, false);
413
 
}
414
 
 
415
 
static int icmp_parameter_problem(icmp_code_t code, icmp_param_t pointer,
416
 
    packet_t *packet)
417
 
{
418
 
        icmp_header_t *header = icmp_prepare_packet(packet);
419
 
        if (!header) {
420
 
                icmp_release(packet);
421
 
                return ENOMEM;
422
 
        }
423
 
        
424
 
        header->un.param.pointer = pointer;
425
 
        return icmp_send_packet(ICMP_PARAMETERPROB, code, packet, header,
426
 
            SERVICE_ICMP, 0, 0, false);
427
 
}
428
 
 
429
 
/** Try to set the pending reply result as the received message type.
430
 
 *
431
 
 * If the reply data is not present, the reply timed out and the other fibril
432
 
 * is already awake. The packet is released.
433
 
 *
434
 
 * @param[in] packet The received reply message.
435
 
 * @param[in] header The ICMP message header.
436
 
 * @param[in] type   The received reply message type.
437
 
 * @param[in] code   The received reply message code.
438
 
 *
439
 
 */
440
 
static void icmp_process_echo_reply(packet_t *packet, icmp_header_t *header,
441
 
    icmp_type_t type, icmp_code_t code)
442
 
{
443
 
        unsigned long key[REPLY_KEYS] =
444
 
            {header->un.echo.identifier, header->un.echo.sequence_number};
445
 
        
446
 
        /* The packet is no longer needed */
447
 
        icmp_release(packet);
448
 
        
449
 
        /* Find the pending reply */
450
 
        fibril_mutex_lock(&reply_lock);
451
 
        
452
 
        link_t *link = hash_table_find(&replies, key);
453
 
        if (link != NULL) {
454
 
                icmp_reply_t *reply =
455
 
                   hash_table_get_instance(link, icmp_reply_t, link);
456
 
                
457
 
                reply->result = type;
458
 
                fibril_condvar_signal(&reply->condvar);
459
 
        }
460
 
        
461
 
        fibril_mutex_unlock(&reply_lock);
462
 
}
463
 
 
464
 
/** Process the received ICMP packet.
465
 
 *
466
 
 * Notify the destination socket application.
467
 
 *
468
 
 * @param[in,out] packet Received packet.
469
 
 * @param[in]     error  Packet error reporting service to prefix
470
 
 *                       the received packet.
471
 
 *
472
 
 * @return EOK on success.
473
 
 * @return EINVAL if the packet is not valid.
474
 
 * @return EINVAL if the stored packet address is not the an_addr_t.
475
 
 * @return EINVAL if the packet does not contain any data.
476
 
 * @return NO_DATA if the packet content is shorter than the user
477
 
 *         datagram header.
478
 
 * @return ENOMEM if there is not enough memory left.
479
 
 * @return EADDRNOTAVAIL if the destination socket does not exist.
480
 
 * @return Other error codes as defined for the
481
 
 *         ip_client_process_packet() function.
482
 
 *
483
 
 */
484
 
static int icmp_process_packet(packet_t *packet, services_t error)
485
 
{
486
 
        icmp_type_t type;
487
 
        icmp_code_t code;
488
 
        int rc;
489
 
        
490
 
        switch (error) {
491
 
        case SERVICE_NONE:
492
 
                break;
493
 
        case SERVICE_ICMP:
494
 
                /* Process error */
495
 
                rc = icmp_client_process_packet(packet, &type, &code, NULL, NULL);
496
 
                if (rc < 0)
497
 
                        return rc;
498
 
                
499
 
                /* Remove the error header */
500
 
                rc = packet_trim(packet, (size_t) rc, 0);
501
 
                if (rc != EOK)
502
 
                        return rc;
503
 
                
504
 
                break;
505
 
        default:
506
 
                return ENOTSUP;
507
 
        }
508
 
        
509
 
        /* Get rid of the IP header */
510
 
        size_t length = ip_client_header_length(packet);
511
 
        rc = packet_trim(packet, length, 0);
512
 
        if (rc != EOK)
513
 
                return rc;
514
 
        
515
 
        length = packet_get_data_length(packet);
516
 
        if (length <= 0)
517
 
                return EINVAL;
518
 
        
519
 
        if (length < ICMP_HEADER_SIZE)
520
 
                return EINVAL;
521
 
        
522
 
        void *data = packet_get_data(packet);
523
 
        if (!data)
524
 
                return EINVAL;
525
 
        
526
 
        /* Get ICMP header */
527
 
        icmp_header_t *header = (icmp_header_t *) data;
528
 
        
529
 
        if (header->checksum) {
530
 
                while (ICMP_CHECKSUM(header, length) != IP_CHECKSUM_ZERO) {
531
 
                        /*
532
 
                         * Set the original message type on error notification.
533
 
                         * Type swap observed in Qemu.
534
 
                         */
535
 
                        if (error) {
536
 
                                switch (header->type) {
537
 
                                case ICMP_ECHOREPLY:
538
 
                                        header->type = ICMP_ECHO;
539
 
                                        continue;
540
 
                                }
541
 
                        }
542
 
                        
543
 
                        return EINVAL;
544
 
                }
545
 
        }
546
 
        
547
 
        switch (header->type) {
548
 
        case ICMP_ECHOREPLY:
549
 
                if (error)
550
 
                        icmp_process_echo_reply(packet, header, type, code);
551
 
                else
552
 
                        icmp_process_echo_reply(packet, header, ICMP_ECHO, 0);
553
 
                
554
 
                return EOK;
555
 
        
556
 
        case ICMP_ECHO:
557
 
                if (error) {
558
 
                        icmp_process_echo_reply(packet, header, type, code);
559
 
                        return EOK;
560
 
                }
561
 
                
562
 
                /* Do not send a reply if disabled */
563
 
                if (echo_replying) {
564
 
                        uint8_t *src;
565
 
                        int addrlen = packet_get_addr(packet, &src, NULL);
566
 
                        
567
 
                        /*
568
 
                         * Set both addresses to the source one (avoid the
569
 
                         * source address deletion before setting the
570
 
                         * destination one).
571
 
                         */
572
 
                        if ((addrlen > 0) && (packet_set_addr(packet, src, src,
573
 
                            (size_t) addrlen) == EOK)) {
574
 
                                /* Send the reply */
575
 
                                icmp_send_packet(ICMP_ECHOREPLY, 0, packet,
576
 
                                    header, 0, 0, 0, 0);
577
 
                                return EOK;
578
 
                        }
579
 
                        
580
 
                        return EINVAL;
581
 
                }
582
 
                
583
 
                return EPERM;
584
 
        
585
 
        case ICMP_DEST_UNREACH:
586
 
        case ICMP_SOURCE_QUENCH:
587
 
        case ICMP_REDIRECT:
588
 
        case ICMP_ALTERNATE_ADDR:
589
 
        case ICMP_ROUTER_ADV:
590
 
        case ICMP_ROUTER_SOL:
591
 
        case ICMP_TIME_EXCEEDED:
592
 
        case ICMP_PARAMETERPROB:
593
 
        case ICMP_CONVERSION_ERROR:
594
 
        case ICMP_REDIRECT_MOBILE:
595
 
        case ICMP_SKIP:
596
 
        case ICMP_PHOTURIS:
597
 
                ip_received_error_msg(ip_sess, -1, packet,
598
 
                    SERVICE_IP, SERVICE_ICMP);
599
 
                return EOK;
600
 
        
601
 
        default:
602
 
                return ENOTSUP;
603
 
        }
604
 
}
605
 
 
606
 
/** Process IPC messages from the IP module
607
 
 *
608
 
 * @param[in]     iid   Message identifier.
609
 
 * @param[in,out] icall Message parameters.
610
 
 * @param[in]     arg   Local argument.
611
 
 *
612
 
 */
613
 
static void icmp_receiver(ipc_callid_t iid, ipc_call_t *icall, void *arg)
614
 
{
615
 
        packet_t *packet;
616
 
        int rc;
617
 
        
618
 
        while (true) {
619
 
                if (!IPC_GET_IMETHOD(*icall))
620
 
                        break;
621
 
                
622
 
                switch (IPC_GET_IMETHOD(*icall)) {
623
 
                case NET_TL_RECEIVED:
624
 
                        rc = packet_translate_remote(net_sess, &packet,
625
 
                            IPC_GET_PACKET(*icall));
626
 
                        if (rc == EOK) {
627
 
                                rc = icmp_process_packet(packet, IPC_GET_ERROR(*icall));
628
 
                                if (rc != EOK)
629
 
                                        icmp_release(packet);
630
 
                        }
631
 
                        
632
 
                        async_answer_0(iid, (sysarg_t) rc);
633
 
                        break;
634
 
                default:
635
 
                        async_answer_0(iid, (sysarg_t) ENOTSUP);
636
 
                }
637
 
                
638
 
                iid = async_get_call(icall);
639
 
        }
640
 
}
641
 
 
642
 
/** Initialize the ICMP module.
643
 
 *
644
 
 * @param[in] sess Network module session.
645
 
 *
646
 
 * @return EOK on success.
647
 
 * @return ENOMEM if there is not enough memory left.
648
 
 *
649
 
 */
650
 
int tl_initialize(async_sess_t *sess)
651
 
{
652
 
        measured_string_t names[] = {
653
 
                {
654
 
                        (uint8_t *) "ICMP_ERROR_REPORTING",
655
 
                        20
656
 
                },
657
 
                {
658
 
                        (uint8_t *) "ICMP_ECHO_REPLYING",
659
 
                        18
660
 
                }
661
 
        };
662
 
        measured_string_t *configuration;
663
 
        size_t count = sizeof(names) / sizeof(measured_string_t);
664
 
        uint8_t *data;
665
 
        
666
 
        if (!hash_table_create(&replies, REPLY_BUCKETS, REPLY_KEYS, &reply_ops))
667
 
                return ENOMEM;
668
 
        
669
 
        fibril_mutex_initialize(&reply_lock);
670
 
        atomic_set(&icmp_client, 0);
671
 
        
672
 
        net_sess = sess;
673
 
        ip_sess = ip_bind_service(SERVICE_IP, IPPROTO_ICMP, SERVICE_ICMP,
674
 
            icmp_receiver);
675
 
        if (ip_sess == NULL)
676
 
                return ENOENT;
677
 
        
678
 
        int rc = ip_packet_size_req(ip_sess, -1, &icmp_dimension);
679
 
        if (rc != EOK)
680
 
                return rc;
681
 
        
682
 
        icmp_dimension.prefix += ICMP_HEADER_SIZE;
683
 
        icmp_dimension.content -= ICMP_HEADER_SIZE;
684
 
        
685
 
        /* Get configuration */
686
 
        configuration = &names[0];
687
 
        rc = net_get_conf_req(net_sess, &configuration, count, &data);
688
 
        if (rc != EOK)
689
 
                return rc;
690
 
        
691
 
        if (configuration) {
692
 
                if (configuration[0].value)
693
 
                        error_reporting = (configuration[0].value[0] == 'y');
694
 
                
695
 
                if (configuration[1].value)
696
 
                        echo_replying = (configuration[1].value[0] == 'y');
697
 
                
698
 
                net_free_settings(configuration, data);
699
 
        }
700
 
        
701
 
        return EOK;
702
 
}
703
 
 
704
 
/** Per-connection initialization
705
 
 *
706
 
 * Initialize client-specific global variables.
707
 
 *
708
 
 */
709
 
void tl_connection(void)
710
 
{
711
 
        icmp_id = (icmp_param_t) atomic_postinc(&icmp_client);
712
 
        icmp_seq = 1;
713
 
}
714
 
 
715
 
/** Process the ICMP message.
716
 
 *
717
 
 * @param[in]  callid Message identifier.
718
 
 * @param[in]  call   Message parameters.
719
 
 * @param[out] answer Answer.
720
 
 * @param[out] count  Number of arguments of the answer.
721
 
 *
722
 
 * @return EOK on success.
723
 
 * @return ENOTSUP if the message is not known.
724
 
 * @return Other error codes as defined for the packet_translate()
725
 
 *         function.
726
 
 * @return Other error codes as defined for the
727
 
 *         icmp_destination_unreachable() function.
728
 
 * @return Other error codes as defined for the
729
 
 *         icmp_source_quench() function.
730
 
 * @return Other error codes as defined for the
731
 
 *         icmp_time_exceeded() function.
732
 
 * @return Other error codes as defined for the
733
 
 *         icmp_parameter_problem() function.
734
 
 *
735
 
 * @see icmp_remote.h
736
 
 * @see IS_NET_ICMP_MESSAGE()
737
 
 *
738
 
 */
739
 
int tl_message(ipc_callid_t callid, ipc_call_t *call,
740
 
    ipc_call_t *answer, size_t *count)
741
 
{
742
 
        struct sockaddr *addr;
743
 
        size_t size;
744
 
        packet_t *packet;
745
 
        int rc;
746
 
        
747
 
        *count = 0;
748
 
        
749
 
        switch (IPC_GET_IMETHOD(*call)) {
750
 
        case NET_ICMP_ECHO:
751
 
                rc = async_data_write_accept((void **) &addr, false, 0, 0, 0, &size);
752
 
                if (rc != EOK)
753
 
                        return rc;
754
 
                
755
 
                rc = icmp_echo(icmp_id, icmp_seq, ICMP_GET_SIZE(*call),
756
 
                    ICMP_GET_TIMEOUT(*call), ICMP_GET_TTL(*call),
757
 
                    ICMP_GET_TOS(*call), ICMP_GET_DONT_FRAGMENT(*call),
758
 
                    addr, (socklen_t) size);
759
 
                
760
 
                free(addr);
761
 
                icmp_seq++;
762
 
                return rc;
763
 
        
764
 
        case NET_ICMP_DEST_UNREACH:
765
 
                rc = packet_translate_remote(net_sess, &packet,
766
 
                    IPC_GET_PACKET(*call));
767
 
                if (rc != EOK)
768
 
                        return rc;
769
 
                
770
 
                return icmp_destination_unreachable(ICMP_GET_CODE(*call),
771
 
                    ICMP_GET_MTU(*call), packet);
772
 
        
773
 
        case NET_ICMP_SOURCE_QUENCH:
774
 
                rc = packet_translate_remote(net_sess, &packet,
775
 
                    IPC_GET_PACKET(*call));
776
 
                if (rc != EOK)
777
 
                        return rc;
778
 
                
779
 
                return icmp_source_quench(packet);
780
 
        
781
 
        case NET_ICMP_TIME_EXCEEDED:
782
 
                rc = packet_translate_remote(net_sess, &packet,
783
 
                    IPC_GET_PACKET(*call));
784
 
                if (rc != EOK)
785
 
                        return rc;
786
 
                
787
 
                return icmp_time_exceeded(ICMP_GET_CODE(*call), packet);
788
 
        
789
 
        case NET_ICMP_PARAMETERPROB:
790
 
                rc = packet_translate_remote(net_sess, &packet,
791
 
                    IPC_GET_PACKET(*call));
792
 
                if (rc != EOK)
793
 
                        return rc;
794
 
                
795
 
                return icmp_parameter_problem(ICMP_GET_CODE(*call),
796
 
                    ICMP_GET_POINTER(*call), packet);
797
 
        }
798
 
        
799
 
        return ENOTSUP;
800
 
}
801
 
 
802
 
int main(int argc, char *argv[])
803
 
{
804
 
        /* Start the module */
805
 
        return tl_module_start(SERVICE_ICMP);
806
 
}
807
 
 
808
 
/** @}
809
 
 */