~martin-decky/helenos/rcu

« back to all changes in this revision

Viewing changes to uspace/srv/net/tl/udp/udp.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 udp
30
 
 * @{
31
 
 */
32
 
 
33
 
/** @file
34
 
 * UDP module implementation.
35
 
 * @see udp.h
36
 
 */
37
 
 
38
 
#include <async.h>
39
 
#include <fibril_synch.h>
40
 
#include <malloc.h>
41
 
#include <stdio.h>
42
 
#include <ipc/services.h>
43
 
#include <ipc/net.h>
44
 
#include <ipc/tl.h>
45
 
#include <ipc/socket.h>
46
 
#include <adt/dynamic_fifo.h>
47
 
#include <errno.h>
48
 
 
49
 
#include <net/socket_codes.h>
50
 
#include <net/ip_protocols.h>
51
 
#include <net/in.h>
52
 
#include <net/in6.h>
53
 
#include <net/inet.h>
54
 
#include <net/modules.h>
55
 
 
56
 
#include <packet_client.h>
57
 
#include <packet_remote.h>
58
 
#include <net_checksum.h>
59
 
#include <ip_client.h>
60
 
#include <ip_interface.h>
61
 
#include <icmp_client.h>
62
 
#include <icmp_remote.h>
63
 
#include <net_interface.h>
64
 
#include <socket_core.h>
65
 
#include <tl_common.h>
66
 
#include <tl_remote.h>
67
 
#include <tl_skel.h>
68
 
 
69
 
#include "udp.h"
70
 
#include "udp_header.h"
71
 
 
72
 
/** UDP module name. */
73
 
#define NAME  "udp"
74
 
 
75
 
/** Default UDP checksum computing. */
76
 
#define NET_DEFAULT_UDP_CHECKSUM_COMPUTING      true
77
 
 
78
 
/** Default UDP autobind when sending via unbound sockets. */
79
 
#define NET_DEFAULT_UDP_AUTOBINDING     true
80
 
 
81
 
/** Maximum UDP fragment size. */
82
 
#define MAX_UDP_FRAGMENT_SIZE           65535
83
 
 
84
 
/** Free ports pool start. */
85
 
#define UDP_FREE_PORTS_START            1025
86
 
 
87
 
/** Free ports pool end. */
88
 
#define UDP_FREE_PORTS_END              65535
89
 
 
90
 
/** UDP global data.  */
91
 
udp_globals_t udp_globals;
92
 
 
93
 
/** Releases the packet and returns the result.
94
 
 *
95
 
 * @param[in] packet    The packet queue to be released.
96
 
 * @param[in] result    The result to be returned.
97
 
 * @return              The result parameter.
98
 
 */
99
 
static int udp_release_and_return(packet_t *packet, int result)
100
 
{
101
 
        pq_release_remote(udp_globals.net_sess, packet_get_id(packet));
102
 
        return result;
103
 
}
104
 
 
105
 
/** Processes the received UDP packet queue.
106
 
 *
107
 
 * Notifies the destination socket application.
108
 
 * Releases the packet on error or sends an ICMP error notification.
109
 
 *
110
 
 * @param[in] device_id The receiving device identifier.
111
 
 * @param[in,out] packet The received packet queue.
112
 
 * @param[in] error     The packet error reporting service. Prefixes the
113
 
 *                      received packet.
114
 
 * @return              EOK on success.
115
 
 * @return              EINVAL if the packet is not valid.
116
 
 * @return              EINVAL if the stored packet address is not the
117
 
 *                      an_addr_t.
118
 
 * @return              EINVAL if the packet does not contain any data.
119
 
 * @return              NO_DATA if the packet content is shorter than the user
120
 
 *                      datagram header.
121
 
 * @return              ENOMEM if there is not enough memory left.
122
 
 * @return              EADDRNOTAVAIL if the destination socket does not exist.
123
 
 * @return              Other error codes as defined for the
124
 
 *                      ip_client_process_packet() function.
125
 
 */
126
 
static int udp_process_packet(nic_device_id_t device_id, packet_t *packet,
127
 
    services_t error)
128
 
{
129
 
        size_t length;
130
 
        size_t offset;
131
 
        int result;
132
 
        udp_header_t *header;
133
 
        socket_core_t *socket;
134
 
        packet_t *next_packet;
135
 
        size_t total_length;
136
 
        uint32_t checksum;
137
 
        int fragments;
138
 
        packet_t *tmp_packet;
139
 
        icmp_type_t type;
140
 
        icmp_code_t code;
141
 
        void *ip_header;
142
 
        struct sockaddr *src;
143
 
        struct sockaddr *dest;
144
 
        packet_dimension_t *packet_dimension;
145
 
        int rc;
146
 
 
147
 
        switch (error) {
148
 
        case SERVICE_NONE:
149
 
                break;
150
 
        case SERVICE_ICMP:
151
 
                /* Ignore error */
152
 
                // length = icmp_client_header_length(packet);
153
 
 
154
 
                /* Process error */
155
 
                result = icmp_client_process_packet(packet, &type,
156
 
                    &code, NULL, NULL);
157
 
                if (result < 0)
158
 
                        return udp_release_and_return(packet, result);
159
 
                length = (size_t) result;
160
 
                rc = packet_trim(packet, length, 0);
161
 
                if (rc != EOK)
162
 
                        return udp_release_and_return(packet, rc);
163
 
                break;
164
 
        default:
165
 
                return udp_release_and_return(packet, ENOTSUP);
166
 
        }
167
 
 
168
 
        /* TODO process received ipopts? */
169
 
        result = ip_client_process_packet(packet, NULL, NULL, NULL, NULL, NULL);
170
 
        if (result < 0)
171
 
                return udp_release_and_return(packet, result);
172
 
        offset = (size_t) result;
173
 
 
174
 
        length = packet_get_data_length(packet);
175
 
        if (length <= 0)
176
 
                return udp_release_and_return(packet, EINVAL);
177
 
        if (length < UDP_HEADER_SIZE + offset)
178
 
                return udp_release_and_return(packet, NO_DATA);
179
 
 
180
 
        /* Trim all but UDP header */
181
 
        rc = packet_trim(packet, offset, 0);
182
 
        if (rc != EOK)
183
 
                return udp_release_and_return(packet, rc);
184
 
 
185
 
        /* Get UDP header */
186
 
        header = (udp_header_t *) packet_get_data(packet);
187
 
        if (!header)
188
 
                return udp_release_and_return(packet, NO_DATA);
189
 
 
190
 
        /* Find the destination socket */
191
 
        socket = socket_port_find(&udp_globals.sockets,
192
 
            ntohs(header->destination_port), (uint8_t *) SOCKET_MAP_KEY_LISTENING, 0);
193
 
        if (!socket) {
194
 
                if (tl_prepare_icmp_packet(udp_globals.net_sess,
195
 
                    udp_globals.icmp_sess, packet, error) == EOK) {
196
 
                        icmp_destination_unreachable_msg(udp_globals.icmp_sess,
197
 
                            ICMP_PORT_UNREACH, 0, packet);
198
 
                }
199
 
                return EADDRNOTAVAIL;
200
 
        }
201
 
 
202
 
        /* Count the received packet fragments */
203
 
        next_packet = packet;
204
 
        fragments = 0;
205
 
        total_length = ntohs(header->total_length);
206
 
 
207
 
        /* Compute header checksum if set */
208
 
        if (header->checksum && !error) {
209
 
                result = packet_get_addr(packet, (uint8_t **) &src,
210
 
                    (uint8_t **) &dest);
211
 
                if (result <= 0)
212
 
                        return udp_release_and_return(packet, result);
213
 
                
214
 
                rc = ip_client_get_pseudo_header(IPPROTO_UDP, src, result, dest,
215
 
                    result, total_length, &ip_header, &length);
216
 
                if (rc != EOK) {
217
 
                        return udp_release_and_return(packet, rc);
218
 
                } else {
219
 
                        checksum = compute_checksum(0, ip_header, length);
220
 
                        /*
221
 
                         * The udp header checksum will be added with the first
222
 
                         * fragment later.
223
 
                         */
224
 
                        free(ip_header);
225
 
                }
226
 
        } else {
227
 
                header->checksum = 0;
228
 
                checksum = 0;
229
 
        }
230
 
 
231
 
        do {
232
 
                fragments++;
233
 
                length = packet_get_data_length(next_packet);
234
 
                if (length <= 0)
235
 
                        return udp_release_and_return(packet, NO_DATA);
236
 
 
237
 
                if (total_length < length) {
238
 
                        rc = packet_trim(next_packet, 0, length - total_length);
239
 
                        if (rc != EOK)
240
 
                                return udp_release_and_return(packet, rc);
241
 
 
242
 
                        /* Add partial checksum if set */
243
 
                        if (header->checksum) {
244
 
                                checksum = compute_checksum(checksum,
245
 
                                    packet_get_data(packet),
246
 
                                    packet_get_data_length(packet));
247
 
                        }
248
 
 
249
 
                        /* Relese the rest of the packet fragments */
250
 
                        tmp_packet = pq_next(next_packet);
251
 
                        while (tmp_packet) {
252
 
                                next_packet = pq_detach(tmp_packet);
253
 
                                pq_release_remote(udp_globals.net_sess,
254
 
                                    packet_get_id(tmp_packet));
255
 
                                tmp_packet = next_packet;
256
 
                        }
257
 
 
258
 
                        /* Exit the loop */
259
 
                        break;
260
 
                }
261
 
                total_length -= length;
262
 
 
263
 
                /* Add partial checksum if set */
264
 
                if (header->checksum) {
265
 
                        checksum = compute_checksum(checksum,
266
 
                            packet_get_data(packet),
267
 
                            packet_get_data_length(packet));
268
 
                }
269
 
 
270
 
        } while ((next_packet = pq_next(next_packet)) && (total_length > 0));
271
 
 
272
 
        /* Verify checksum */
273
 
        if (header->checksum) {
274
 
                if (flip_checksum(compact_checksum(checksum)) !=
275
 
                    IP_CHECKSUM_ZERO) {
276
 
                        if (tl_prepare_icmp_packet(udp_globals.net_sess,
277
 
                            udp_globals.icmp_sess, packet, error) == EOK) {
278
 
                                /* Checksum error ICMP */
279
 
                                icmp_parameter_problem_msg(
280
 
                                    udp_globals.icmp_sess, ICMP_PARAM_POINTER,
281
 
                                    ((size_t) ((void *) &header->checksum)) -
282
 
                                    ((size_t) ((void *) header)), packet);
283
 
                        }
284
 
                        return EINVAL;
285
 
                }
286
 
        }
287
 
 
288
 
        /* Queue the received packet */
289
 
        rc = dyn_fifo_push(&socket->received, packet_get_id(packet),
290
 
            SOCKET_MAX_RECEIVED_SIZE);
291
 
        if (rc != EOK)
292
 
                return udp_release_and_return(packet, rc);
293
 
                
294
 
        rc = tl_get_ip_packet_dimension(udp_globals.ip_sess,
295
 
            &udp_globals.dimensions, device_id, &packet_dimension);
296
 
        if (rc != EOK)
297
 
                return udp_release_and_return(packet, rc);
298
 
 
299
 
        /* Notify the destination socket */
300
 
        fibril_rwlock_write_unlock(&udp_globals.lock);
301
 
        
302
 
        async_exch_t *exch = async_exchange_begin(socket->sess);
303
 
        async_msg_5(exch, NET_SOCKET_RECEIVED, (sysarg_t) socket->socket_id,
304
 
            packet_dimension->content, 0, 0, (sysarg_t) fragments);
305
 
        async_exchange_end(exch);
306
 
 
307
 
        return EOK;
308
 
}
309
 
 
310
 
/** Processes the received UDP packet queue.
311
 
 *
312
 
 * Is used as an entry point from the underlying IP module.
313
 
 * Locks the global lock and calls udp_process_packet() function.
314
 
 *
315
 
 * @param[in] device_id The receiving device identifier.
316
 
 * @param[in,out] packet The received packet queue.
317
 
 * @param receiver      The target service. Ignored parameter.
318
 
 * @param[in] error     The packet error reporting service. Prefixes the
319
 
 *                      received packet.
320
 
 * @return              EOK on success.
321
 
 * @return              Other error codes as defined for the
322
 
 *                      udp_process_packet() function.
323
 
 */
324
 
static int udp_received_msg(nic_device_id_t device_id, packet_t *packet,
325
 
    services_t receiver, services_t error)
326
 
{
327
 
        int result;
328
 
 
329
 
        fibril_rwlock_write_lock(&udp_globals.lock);
330
 
        result = udp_process_packet(device_id, packet, error);
331
 
        if (result != EOK)
332
 
                fibril_rwlock_write_unlock(&udp_globals.lock);
333
 
 
334
 
        return result;
335
 
}
336
 
 
337
 
/** Process IPC messages from the IP module
338
 
 *
339
 
 * @param[in]     iid   Message identifier.
340
 
 * @param[in,out] icall Message parameters.
341
 
 * @param[in]     arg   Local argument.
342
 
 *
343
 
 */
344
 
static void udp_receiver(ipc_callid_t iid, ipc_call_t *icall, void *arg)
345
 
{
346
 
        packet_t *packet;
347
 
        int rc;
348
 
        
349
 
        while (true) {
350
 
                switch (IPC_GET_IMETHOD(*icall)) {
351
 
                case NET_TL_RECEIVED:
352
 
                        rc = packet_translate_remote(udp_globals.net_sess, &packet,
353
 
                            IPC_GET_PACKET(*icall));
354
 
                        if (rc == EOK)
355
 
                                rc = udp_received_msg(IPC_GET_DEVICE(*icall), packet,
356
 
                                    SERVICE_UDP, IPC_GET_ERROR(*icall));
357
 
                        
358
 
                        async_answer_0(iid, (sysarg_t) rc);
359
 
                        break;
360
 
                default:
361
 
                        async_answer_0(iid, (sysarg_t) ENOTSUP);
362
 
                }
363
 
                
364
 
                iid = async_get_call(icall);
365
 
        }
366
 
}
367
 
 
368
 
/** Initialize the UDP module.
369
 
 *
370
 
 * @param[in] sess Network module session.
371
 
 *
372
 
 * @return EOK on success.
373
 
 * @return ENOMEM if there is not enough memory left.
374
 
 *
375
 
 */
376
 
int tl_initialize(async_sess_t *sess)
377
 
{
378
 
        measured_string_t names[] = {
379
 
                {
380
 
                        (uint8_t *) "UDP_CHECKSUM_COMPUTING",
381
 
                        22
382
 
                },
383
 
                {
384
 
                        (uint8_t *) "UDP_AUTOBINDING",
385
 
                        15
386
 
                }
387
 
        };
388
 
        measured_string_t *configuration;
389
 
        size_t count = sizeof(names) / sizeof(measured_string_t);
390
 
        uint8_t *data;
391
 
        
392
 
        fibril_rwlock_initialize(&udp_globals.lock);
393
 
        fibril_rwlock_write_lock(&udp_globals.lock);
394
 
        
395
 
        udp_globals.net_sess = sess;
396
 
        udp_globals.icmp_sess = icmp_connect_module();
397
 
        
398
 
        udp_globals.ip_sess = ip_bind_service(SERVICE_IP, IPPROTO_UDP,
399
 
             SERVICE_UDP, udp_receiver);
400
 
        if (udp_globals.ip_sess == NULL) {
401
 
            fibril_rwlock_write_unlock(&udp_globals.lock);
402
 
            return ENOENT;
403
 
        }
404
 
        
405
 
        /* Read default packet dimensions */
406
 
        int rc = ip_packet_size_req(udp_globals.ip_sess, -1,
407
 
            &udp_globals.packet_dimension);
408
 
        if (rc != EOK) {
409
 
                fibril_rwlock_write_unlock(&udp_globals.lock);
410
 
                return rc;
411
 
        }
412
 
        
413
 
        rc = socket_ports_initialize(&udp_globals.sockets);
414
 
        if (rc != EOK) {
415
 
                fibril_rwlock_write_unlock(&udp_globals.lock);
416
 
                return rc;
417
 
        }
418
 
        
419
 
        rc = packet_dimensions_initialize(&udp_globals.dimensions);
420
 
        if (rc != EOK) {
421
 
                socket_ports_destroy(&udp_globals.sockets, free);
422
 
                fibril_rwlock_write_unlock(&udp_globals.lock);
423
 
                return rc;
424
 
        }
425
 
        
426
 
        udp_globals.packet_dimension.prefix += sizeof(udp_header_t);
427
 
        udp_globals.packet_dimension.content -= sizeof(udp_header_t);
428
 
        udp_globals.last_used_port = UDP_FREE_PORTS_START - 1;
429
 
 
430
 
        udp_globals.checksum_computing = NET_DEFAULT_UDP_CHECKSUM_COMPUTING;
431
 
        udp_globals.autobinding = NET_DEFAULT_UDP_AUTOBINDING;
432
 
 
433
 
        /* Get configuration */
434
 
        configuration = &names[0];
435
 
        rc = net_get_conf_req(udp_globals.net_sess, &configuration, count,
436
 
            &data);
437
 
        if (rc != EOK) {
438
 
                socket_ports_destroy(&udp_globals.sockets, free);
439
 
                fibril_rwlock_write_unlock(&udp_globals.lock);
440
 
                return rc;
441
 
        }
442
 
        
443
 
        if (configuration) {
444
 
                if (configuration[0].value)
445
 
                        udp_globals.checksum_computing =
446
 
                            (configuration[0].value[0] == 'y');
447
 
                
448
 
                if (configuration[1].value)
449
 
                        udp_globals.autobinding =
450
 
                            (configuration[1].value[0] == 'y');
451
 
 
452
 
                net_free_settings(configuration, data);
453
 
        }
454
 
 
455
 
        fibril_rwlock_write_unlock(&udp_globals.lock);
456
 
        return EOK;
457
 
}
458
 
 
459
 
/** Sends data from the socket to the remote address.
460
 
 *
461
 
 * Binds the socket to a free port if not already connected/bound.
462
 
 * Handles the NET_SOCKET_SENDTO message.
463
 
 * Supports AF_INET and AF_INET6 address families.
464
 
 *
465
 
 * @param[in,out] local_sockets The application local sockets.
466
 
 * @param[in] socket_id Socket identifier.
467
 
 * @param[in] addr      The destination address.
468
 
 * @param[in] addrlen   The address length.
469
 
 * @param[in] fragments The number of data fragments.
470
 
 * @param[out] data_fragment_size The data fragment size in bytes.
471
 
 * @param[in] flags     Various send flags.
472
 
 * @return              EOK on success.
473
 
 * @return              EAFNOTSUPPORT if the address family is not supported.
474
 
 * @return              ENOTSOCK if the socket is not found.
475
 
 * @return              EINVAL if the address is invalid.
476
 
 * @return              ENOTCONN if the sending socket is not and cannot be
477
 
 *                      bound.
478
 
 * @return              ENOMEM if there is not enough memory left.
479
 
 * @return              Other error codes as defined for the
480
 
 *                      socket_read_packet_data() function.
481
 
 * @return              Other error codes as defined for the
482
 
 *                      ip_client_prepare_packet() function.
483
 
 * @return              Other error codes as defined for the ip_send_msg()
484
 
 *                      function.
485
 
 */
486
 
static int udp_sendto_message(socket_cores_t *local_sockets, int socket_id,
487
 
    const struct sockaddr *addr, socklen_t addrlen, int fragments,
488
 
    size_t *data_fragment_size, int flags)
489
 
{
490
 
        socket_core_t *socket;
491
 
        packet_t *packet;
492
 
        packet_t *next_packet;
493
 
        udp_header_t *header;
494
 
        int index;
495
 
        size_t total_length;
496
 
        int result;
497
 
        uint16_t dest_port;
498
 
        uint32_t checksum;
499
 
        void *ip_header;
500
 
        size_t headerlen;
501
 
        nic_device_id_t device_id;
502
 
        packet_dimension_t *packet_dimension;
503
 
        size_t size;
504
 
        int rc;
505
 
 
506
 
        /* In case of error, do not update the data fragment size. */
507
 
        *data_fragment_size = 0;
508
 
        
509
 
        rc = tl_get_address_port(addr, addrlen, &dest_port);
510
 
        if (rc != EOK)
511
 
                return rc;
512
 
 
513
 
        socket = socket_cores_find(local_sockets, socket_id);
514
 
        if (!socket)
515
 
                return ENOTSOCK;
516
 
 
517
 
        if ((socket->port <= 0) && udp_globals.autobinding) {
518
 
                /* Bind the socket to a random free port if not bound */
519
 
                rc = socket_bind_free_port(&udp_globals.sockets, socket,
520
 
                    UDP_FREE_PORTS_START, UDP_FREE_PORTS_END,
521
 
                    udp_globals.last_used_port);
522
 
                if (rc != EOK)
523
 
                        return rc;
524
 
                /* Set the next port as the search starting port number */
525
 
                udp_globals.last_used_port = socket->port;
526
 
        }
527
 
 
528
 
        if (udp_globals.checksum_computing) {
529
 
                rc = ip_get_route_req(udp_globals.ip_sess, IPPROTO_UDP, addr,
530
 
                    addrlen, &device_id, &ip_header, &headerlen);
531
 
                if (rc != EOK)
532
 
                        return rc;
533
 
                /* Get the device packet dimension */
534
 
//              rc = tl_get_ip_packet_dimension(udp_globals.ip_sess,
535
 
//                  &udp_globals.dimensions, device_id, &packet_dimension);
536
 
//              if (rc != EOK)
537
 
//                      return rc;
538
 
        }
539
 
//      } else {
540
 
                /* Do not ask all the time */
541
 
                rc = ip_packet_size_req(udp_globals.ip_sess, -1,
542
 
                    &udp_globals.packet_dimension);
543
 
                if (rc != EOK)
544
 
                        return rc;
545
 
                packet_dimension = &udp_globals.packet_dimension;
546
 
//      }
547
 
 
548
 
        /*
549
 
         * Update the data fragment size based on what the lower layers can
550
 
         * handle without fragmentation, but not more than the maximum allowed
551
 
         * for UDP.
552
 
         */
553
 
        size = MAX_UDP_FRAGMENT_SIZE;
554
 
        if (packet_dimension->content < size)
555
 
            size = packet_dimension->content;
556
 
        *data_fragment_size = size;
557
 
 
558
 
        /* Read the first packet fragment */
559
 
        result = tl_socket_read_packet_data(udp_globals.net_sess, &packet,
560
 
            UDP_HEADER_SIZE, packet_dimension, addr, addrlen);
561
 
        if (result < 0)
562
 
                return result;
563
 
 
564
 
        total_length = (size_t) result;
565
 
        if (udp_globals.checksum_computing)
566
 
                checksum = compute_checksum(0, packet_get_data(packet),
567
 
                    packet_get_data_length(packet));
568
 
        else
569
 
                checksum = 0;
570
 
 
571
 
        /* Prefix the UDP header */
572
 
        header = PACKET_PREFIX(packet, udp_header_t);
573
 
        if (!header)
574
 
                return udp_release_and_return(packet, ENOMEM);
575
 
 
576
 
        bzero(header, sizeof(*header));
577
 
 
578
 
        /* Read the rest of the packet fragments */
579
 
        for (index = 1; index < fragments; index++) {
580
 
                result = tl_socket_read_packet_data(udp_globals.net_sess,
581
 
                    &next_packet, 0, packet_dimension, addr, addrlen);
582
 
                if (result < 0)
583
 
                        return udp_release_and_return(packet, result);
584
 
 
585
 
                rc = pq_add(&packet, next_packet, index, 0);
586
 
                if (rc != EOK)
587
 
                        return udp_release_and_return(packet, rc);
588
 
 
589
 
                total_length += (size_t) result;
590
 
                if (udp_globals.checksum_computing) {
591
 
                        checksum = compute_checksum(checksum,
592
 
                            packet_get_data(next_packet),
593
 
                            packet_get_data_length(next_packet));
594
 
                }
595
 
        }
596
 
 
597
 
        /* Set the UDP header */
598
 
        header->source_port = htons((socket->port > 0) ? socket->port : 0);
599
 
        header->destination_port = htons(dest_port);
600
 
        header->total_length = htons(total_length + sizeof(*header));
601
 
        header->checksum = 0;
602
 
 
603
 
        if (udp_globals.checksum_computing) {
604
 
                /* Update the pseudo header */
605
 
                rc = ip_client_set_pseudo_header_data_length(ip_header,
606
 
                    headerlen, total_length + UDP_HEADER_SIZE);
607
 
                if (rc != EOK) {
608
 
                        free(ip_header);
609
 
                        return udp_release_and_return(packet, rc);
610
 
                }
611
 
 
612
 
                /* Finish the checksum computation */
613
 
                checksum = compute_checksum(checksum, ip_header, headerlen);
614
 
                checksum = compute_checksum(checksum, (uint8_t *) header,
615
 
                    sizeof(*header));
616
 
                header->checksum =
617
 
                    htons(flip_checksum(compact_checksum(checksum)));
618
 
                free(ip_header);
619
 
        } else
620
 
                device_id = NIC_DEVICE_INVALID_ID;
621
 
 
622
 
        /* Prepare the first packet fragment */
623
 
        rc = ip_client_prepare_packet(packet, IPPROTO_UDP, 0, 0, 0, 0);
624
 
        if (rc != EOK)
625
 
                return udp_release_and_return(packet, rc);
626
 
 
627
 
        /* Release the UDP global lock on success. */
628
 
        fibril_rwlock_write_unlock(&udp_globals.lock);
629
 
 
630
 
        /* Send the packet */
631
 
        ip_send_msg(udp_globals.ip_sess, device_id, packet, SERVICE_UDP, 0);
632
 
 
633
 
        return EOK;
634
 
}
635
 
 
636
 
/** Receives data to the socket.
637
 
 *
638
 
 * Handles the NET_SOCKET_RECVFROM message.
639
 
 * Replies the source address as well.
640
 
 *
641
 
 * @param[in] local_sockets The application local sockets.
642
 
 * @param[in] socket_id Socket identifier.
643
 
 * @param[in] flags     Various receive flags.
644
 
 * @param[out] addrlen  The source address length.
645
 
 * @return              The number of bytes received.
646
 
 * @return              ENOTSOCK if the socket is not found.
647
 
 * @return              NO_DATA if there are no received packets or data.
648
 
 * @return              ENOMEM if there is not enough memory left.
649
 
 * @return              EINVAL if the received address is not an IP address.
650
 
 * @return              Other error codes as defined for the packet_translate()
651
 
 *                      function.
652
 
 * @return              Other error codes as defined for the data_reply()
653
 
 *                      function.
654
 
 */
655
 
static int udp_recvfrom_message(socket_cores_t *local_sockets, int socket_id,
656
 
    int flags, size_t *addrlen)
657
 
{
658
 
        socket_core_t *socket;
659
 
        int packet_id;
660
 
        packet_t *packet;
661
 
        udp_header_t *header;
662
 
        struct sockaddr *addr;
663
 
        size_t length;
664
 
        uint8_t *data;
665
 
        int result;
666
 
        int rc;
667
 
 
668
 
        /* Find the socket */
669
 
        socket = socket_cores_find(local_sockets, socket_id);
670
 
        if (!socket)
671
 
                return ENOTSOCK;
672
 
 
673
 
        /* Get the next received packet */
674
 
        packet_id = dyn_fifo_value(&socket->received);
675
 
        if (packet_id < 0)
676
 
                return NO_DATA;
677
 
        
678
 
        rc = packet_translate_remote(udp_globals.net_sess, &packet, packet_id);
679
 
        if (rc != EOK) {
680
 
                (void) dyn_fifo_pop(&socket->received);
681
 
                return rc;
682
 
        }
683
 
 
684
 
        /* Get UDP header */
685
 
        data = packet_get_data(packet);
686
 
        if (!data) {
687
 
                (void) dyn_fifo_pop(&socket->received);
688
 
                return udp_release_and_return(packet, NO_DATA);
689
 
        }
690
 
        header = (udp_header_t *) data;
691
 
 
692
 
        /* Set the source address port */
693
 
        result = packet_get_addr(packet, (uint8_t **) &addr, NULL);
694
 
        rc = tl_set_address_port(addr, result, ntohs(header->source_port));
695
 
        if (rc != EOK) {
696
 
                (void) dyn_fifo_pop(&socket->received);
697
 
                return udp_release_and_return(packet, rc);
698
 
        }
699
 
        *addrlen = (size_t) result;
700
 
 
701
 
        /* Send the source address */
702
 
        rc = data_reply(addr, *addrlen);
703
 
        switch (rc) {
704
 
        case EOK:
705
 
                break;
706
 
        case EOVERFLOW:
707
 
                return rc;
708
 
        default:
709
 
                (void) dyn_fifo_pop(&socket->received);
710
 
                return udp_release_and_return(packet, rc);
711
 
        }
712
 
 
713
 
        /* Trim the header */
714
 
        rc = packet_trim(packet, UDP_HEADER_SIZE, 0);
715
 
        if (rc != EOK) {
716
 
                (void) dyn_fifo_pop(&socket->received);
717
 
                return udp_release_and_return(packet, rc);
718
 
        }
719
 
 
720
 
        /* Reply the packets */
721
 
        rc = socket_reply_packets(packet, &length);
722
 
        switch (rc) {
723
 
        case EOK:
724
 
                break;
725
 
        case EOVERFLOW:
726
 
                return rc;
727
 
        default:
728
 
                (void) dyn_fifo_pop(&socket->received);
729
 
                return udp_release_and_return(packet, rc);
730
 
        }
731
 
 
732
 
        (void) dyn_fifo_pop(&socket->received);
733
 
 
734
 
        /* Release the packet and return the total length */
735
 
        return udp_release_and_return(packet, (int) length);
736
 
}
737
 
 
738
 
/** Process the socket client messages.
739
 
 *
740
 
 * Run until the client module disconnects.
741
 
 *
742
 
 * @see socket.h
743
 
 *
744
 
 * @param[in] sess   Callback session.
745
 
 * @param[in] callid Message identifier.
746
 
 * @param[in] call   Message parameters.
747
 
 *
748
 
 * @return EOK on success.
749
 
 *
750
 
 */
751
 
static int udp_process_client_messages(async_sess_t *sess, ipc_callid_t callid,
752
 
    ipc_call_t call)
753
 
{
754
 
        int res;
755
 
        socket_cores_t local_sockets;
756
 
        struct sockaddr *addr;
757
 
        int socket_id;
758
 
        size_t addrlen;
759
 
        size_t size;
760
 
        ipc_call_t answer;
761
 
        size_t answer_count;
762
 
        packet_dimension_t *packet_dimension;
763
 
 
764
 
        /*
765
 
         * Accept the connection
766
 
         *  - Answer the first IPC_M_CONNECT_TO_ME call.
767
 
         */
768
 
        res = EOK;
769
 
        answer_count = 0;
770
 
 
771
 
        /*
772
 
         * The client connection is only in one fibril and therefore no
773
 
         * additional locks are needed.
774
 
         */
775
 
 
776
 
        socket_cores_initialize(&local_sockets);
777
 
 
778
 
        while (true) {
779
 
 
780
 
                /* Answer the call */
781
 
                answer_call(callid, res, &answer, answer_count);
782
 
 
783
 
                /* Refresh data */
784
 
                refresh_answer(&answer, &answer_count);
785
 
 
786
 
                /* Get the next call */
787
 
                callid = async_get_call(&call);
788
 
 
789
 
                /* Process the call */
790
 
                if (!IPC_GET_IMETHOD(call)) {
791
 
                        res = EHANGUP;
792
 
                        break;
793
 
                }
794
 
                
795
 
                switch (IPC_GET_IMETHOD(call)) {
796
 
                case NET_SOCKET:
797
 
                        socket_id = SOCKET_GET_SOCKET_ID(call);
798
 
                        res = socket_create(&local_sockets, sess, NULL,
799
 
                            &socket_id);
800
 
                        SOCKET_SET_SOCKET_ID(answer, socket_id);
801
 
 
802
 
                        if (res != EOK)
803
 
                                break;
804
 
                        
805
 
                        size = MAX_UDP_FRAGMENT_SIZE;
806
 
                        if (tl_get_ip_packet_dimension(udp_globals.ip_sess,
807
 
                            &udp_globals.dimensions, NIC_DEVICE_INVALID_ID,
808
 
                            &packet_dimension) == EOK) {
809
 
                                if (packet_dimension->content < size)
810
 
                                        size = packet_dimension->content;
811
 
                        }
812
 
                        SOCKET_SET_DATA_FRAGMENT_SIZE(answer, size);
813
 
                        SOCKET_SET_HEADER_SIZE(answer, UDP_HEADER_SIZE);
814
 
                        answer_count = 3;
815
 
                        break;
816
 
 
817
 
                case NET_SOCKET_BIND:
818
 
                        res = async_data_write_accept((void **) &addr, false,
819
 
                            0, 0, 0, &addrlen);
820
 
                        if (res != EOK)
821
 
                                break;
822
 
                        fibril_rwlock_write_lock(&udp_globals.lock);
823
 
                        res = socket_bind(&local_sockets, &udp_globals.sockets,
824
 
                            SOCKET_GET_SOCKET_ID(call), addr, addrlen,
825
 
                            UDP_FREE_PORTS_START, UDP_FREE_PORTS_END,
826
 
                            udp_globals.last_used_port);
827
 
                        fibril_rwlock_write_unlock(&udp_globals.lock);
828
 
                        free(addr);
829
 
                        break;
830
 
 
831
 
                case NET_SOCKET_SENDTO:
832
 
                        res = async_data_write_accept((void **) &addr, false,
833
 
                            0, 0, 0, &addrlen);
834
 
                        if (res != EOK)
835
 
                                break;
836
 
 
837
 
                        fibril_rwlock_write_lock(&udp_globals.lock);
838
 
                        res = udp_sendto_message(&local_sockets,
839
 
                            SOCKET_GET_SOCKET_ID(call), addr, addrlen,
840
 
                            SOCKET_GET_DATA_FRAGMENTS(call), &size,
841
 
                            SOCKET_GET_FLAGS(call));
842
 
                        SOCKET_SET_DATA_FRAGMENT_SIZE(answer, size);
843
 
 
844
 
                        if (res != EOK)
845
 
                                fibril_rwlock_write_unlock(&udp_globals.lock);
846
 
                        else
847
 
                                answer_count = 2;
848
 
                        
849
 
                        free(addr);
850
 
                        break;
851
 
 
852
 
                case NET_SOCKET_RECVFROM:
853
 
                        fibril_rwlock_write_lock(&udp_globals.lock);
854
 
                        res = udp_recvfrom_message(&local_sockets,
855
 
                             SOCKET_GET_SOCKET_ID(call), SOCKET_GET_FLAGS(call),
856
 
                             &addrlen);
857
 
                        fibril_rwlock_write_unlock(&udp_globals.lock);
858
 
 
859
 
                        if (res <= 0)
860
 
                                break;
861
 
 
862
 
                        SOCKET_SET_READ_DATA_LENGTH(answer, res);
863
 
                        SOCKET_SET_ADDRESS_LENGTH(answer, addrlen);
864
 
                        answer_count = 3;
865
 
                        res = EOK;
866
 
                        break;
867
 
                        
868
 
                case NET_SOCKET_CLOSE:
869
 
                        fibril_rwlock_write_lock(&udp_globals.lock);
870
 
                        res = socket_destroy(udp_globals.net_sess,
871
 
                            SOCKET_GET_SOCKET_ID(call), &local_sockets,
872
 
                            &udp_globals.sockets, NULL);
873
 
                        fibril_rwlock_write_unlock(&udp_globals.lock);
874
 
                        break;
875
 
 
876
 
                case NET_SOCKET_GETSOCKOPT:
877
 
                case NET_SOCKET_SETSOCKOPT:
878
 
                default:
879
 
                        res = ENOTSUP;
880
 
                        break;
881
 
                }
882
 
        }
883
 
 
884
 
        /* Release the application session */
885
 
        async_hangup(sess);
886
 
 
887
 
        /* Release all local sockets */
888
 
        socket_cores_release(udp_globals.net_sess, &local_sockets,
889
 
            &udp_globals.sockets, NULL);
890
 
 
891
 
        return res;
892
 
}
893
 
 
894
 
/** Per-connection initialization
895
 
 *
896
 
 */
897
 
void tl_connection(void)
898
 
{
899
 
}
900
 
 
901
 
/** Processes the UDP message.
902
 
 *
903
 
 * @param[in] callid    The message identifier.
904
 
 * @param[in] call      The message parameters.
905
 
 * @param[out] answer   The message answer parameters.
906
 
 * @param[out] answer_count The last parameter for the actual answer in the
907
 
 *                      answer parameter.
908
 
 * @return              EOK on success.
909
 
 * @return              ENOTSUP if the message is not known.
910
 
 *
911
 
 * @see udp_interface.h
912
 
 * @see IS_NET_UDP_MESSAGE()
913
 
 */
914
 
int tl_message(ipc_callid_t callid, ipc_call_t *call,
915
 
    ipc_call_t *answer, size_t *answer_count)
916
 
{
917
 
        *answer_count = 0;
918
 
        
919
 
        async_sess_t *callback =
920
 
            async_callback_receive_start(EXCHANGE_SERIALIZE, call);
921
 
        if (callback)
922
 
                return udp_process_client_messages(callback, callid, *call);
923
 
        
924
 
        return ENOTSUP;
925
 
}
926
 
 
927
 
int main(int argc, char *argv[])
928
 
{
929
 
        /* Start the module */
930
 
        return tl_module_start(SERVICE_UDP);
931
 
}
932
 
 
933
 
/** @}
934
 
 */