Subversion Repositories HelenOS

Rev

Rev 4720 | Rev 4726 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
4499 mejdrech 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
4701 mejdrech 34
 *  UDP module implementation.
35
 *  @see udp.h
4499 mejdrech 36
 */
37
 
38
#include <async.h>
4700 mejdrech 39
#include <fibril_sync.h>
4579 mejdrech 40
#include <malloc.h>
4499 mejdrech 41
 
42
#include <ipc/ipc.h>
43
#include <ipc/services.h>
44
 
45
#include "../../err.h"
46
#include "../../messages.h"
47
#include "../../modules.h"
4589 mejdrech 48
 
49
#include "../../structures/dynamic_fifo.h"
4499 mejdrech 50
#include "../../structures/packet/packet_client.h"
51
 
4722 mejdrech 52
#include "../../include/crc.h"
4579 mejdrech 53
#include "../../include/in.h"
4720 mejdrech 54
#include "../../include/in6.h"
4579 mejdrech 55
#include "../../include/inet.h"
4499 mejdrech 56
#include "../../include/ip_client.h"
57
#include "../../include/ip_interface.h"
58
#include "../../include/ip_protocols.h"
4707 mejdrech 59
#include "../../include/icmp_client.h"
60
#include "../../include/icmp_interface.h"
4722 mejdrech 61
#include "../../include/net_interface.h"
4713 mejdrech 62
#include "../../include/socket_codes.h"
4579 mejdrech 63
#include "../../include/socket_errno.h"
4499 mejdrech 64
 
4579 mejdrech 65
#include "../../socket/socket_core.h"
66
#include "../../socket/socket_messages.h"
67
 
4499 mejdrech 68
#include "../tl_messages.h"
69
 
70
#include "udp.h"
4579 mejdrech 71
#include "udp_header.h"
4499 mejdrech 72
#include "udp_module.h"
73
 
4722 mejdrech 74
/** Default UDP checksum computing.
75
 */
76
#define NET_DEFAULT_UDP_CHECKSUM_COMPUTING  true
77
 
4701 mejdrech 78
/** Maximum UDP fragment size.
79
 */
4589 mejdrech 80
#define MAX_UDP_FRAGMENT_SIZE   65535
81
 
4701 mejdrech 82
/** Free ports pool start.
83
 */
4700 mejdrech 84
#define UDP_FREE_PORTS_START    1025
4701 mejdrech 85
 
86
/** Free ports pool end.
87
 */
4700 mejdrech 88
#define UDP_FREE_PORTS_END      65535
89
 
4707 mejdrech 90
/** Processes the received UDP packet queue.
4701 mejdrech 91
 *  Is used as an entry point from the underlying IP module.
4707 mejdrech 92
 *  Notifies the destination socket application.
93
 *  Releases the packet on error or send an ICMP error notification..
4701 mejdrech 94
 *  @param device_id The device identifier. Ignored parameter.
4707 mejdrech 95
 *  @param packet The received packet queue. Input/output parameter.
4701 mejdrech 96
 *  @param receiver The target service. Ignored parameter.
4707 mejdrech 97
 *  @param error The packet error reporting service. Prefixes the received packet. Input parameter.
4701 mejdrech 98
 *  @returns EOK on success.
99
 *  @returns EINVAL if the packet is not valid.
100
 *  @returns EINVAL if the stored packet address is not the an_addr_t.
101
 *  @returns EINVAL if the packet does not contain any data.
102
 *  @returns NO_DATA if the packet content is shorter than the user datagram header.
103
 *  @returns ENOMEM if there is not enough memory left.
104
 *  @returns EADDRNOTAVAIL if the destination socket does not exist.
105
 *  @returns Other error codes as defined for the ip_client_process_packet() function.
106
 */
4707 mejdrech 107
int udp_received_msg( device_id_t device_id, packet_t packet, services_t receiver, services_t error );
4701 mejdrech 108
 
4707 mejdrech 109
/** Releases the packet and returns the result.
110
 *  @param packet The packet queue to be released. Input parameter.
111
 *  @param result The result to be returned. Input parameter.
112
 *  @return The result parameter.
113
 */
4713 mejdrech 114
int udp_release_and_return( packet_t packet, int result );
4707 mejdrech 115
 
116
/** Sends the port unreachable ICMP notification.
117
 *  Sends the first packet and releases all the others.
118
 *  Releases the packet queu on error.
119
 *  @param packet The packet to be send. Input parameter.
120
 *  @param error The packet error reporting service. Prefixes the received packet. Input parameter.
121
 */
122
void    udp_send_icmp_port_unreachable( packet_t packet, services_t error );
123
 
4701 mejdrech 124
/** @name Socket messages processing functions
125
 */
126
/*@{*/
127
 
128
/** Processes the socket client messages.
129
 *  Runs until the client module disconnects.
130
 *  @param callid The message identifier. Input parameter.
131
 *  @param call The message parameters. Input parameter.
132
 *  @returns EOK on success.
133
 *  @see socket.h
134
 */
4713 mejdrech 135
int udp_process_client_messages( ipc_callid_t callid, ipc_call_t call );
4701 mejdrech 136
 
137
/** Sends data from the socket to the remote address.
138
 *  Binds the socket to a free port if not already connected/bound.
139
 *  Handles the NET_SOCKET_SENDTO message.
4720 mejdrech 140
 *  Supports AF_INET and AF_INET6 address families.
4701 mejdrech 141
 *  @param local_sockets The application local sockets. Input/output parameter.
142
 *  @param socket_id Socket identifier. Input parameter.
143
 *  @param addr The destination address. Input parameter.
144
 *  @param addrlen The address length. Input parameter.
145
 *  @param fragments The number of data fragments. Input parameter.
146
 *  @param flags Various send flags. Input parameter.
147
 *  @returns EOK on success.
148
 *  @returns EAFNOTSUPPORT if the address family is not supported.
149
 *  @returns ENOTSOCK if the socket is not found.
150
 *  @returns EINVAL if the address is invalid.
151
 *  @returns ENOTCONN if the sending socket is not and cannot be bound.
152
 *  @returns ENOMEM if there is not enough memory left.
153
 *  @returns Other error codes as defined for the socket_read_packet_data() function.
154
 *  @returns Other error codes as defined for the ip_client_prepare_packet() function.
155
 *  @returns Other error codes as defined for the ip_send_msg() function.
156
 */
4720 mejdrech 157
int udp_sendto_message( socket_cores_ref local_sockets, int socket_id, const struct sockaddr * addr, socklen_t addrlen, int fragments, int flags );
4701 mejdrech 158
 
159
/** Receives data to the socket.
160
 *  Handles the NET_SOCKET_RECVFROM message.
4720 mejdrech 161
 *  Replies the source address as well.
4701 mejdrech 162
 *  @param local_sockets The application local sockets. Input parameter.
163
 *  @param socket_id Socket identifier. Input parameter.
164
 *  @param flags Various receive flags. Input parameter.
4720 mejdrech 165
 *  @param addrlen The source address length. Output parameter.
4701 mejdrech 166
 *  @returns The number of bytes received.
167
 *  @returns ENOTSOCK if the socket is not found.
168
 *  @returns NO_DATA if there are no received packets or data.
169
 *  @returns ENOMEM if there is not enough memory left.
170
 *  @returns EINVAL if the received address is not an IP address.
171
 *  @returns Other error codes as defined for the packet_translate() function.
4722 mejdrech 172
 *  @returns Other error codes as defined for the data_reply() function.
4701 mejdrech 173
 */
4720 mejdrech 174
int udp_recvfrom_message( socket_cores_ref local_sockets, int socket_id, int flags, size_t * addrlen );
4701 mejdrech 175
 
176
/*@}*/
177
 
178
/** Receives data from the socket into a packet.
179
 *  @param packet The new created packet. Output parameter.
180
 *  @param prefix Reserved packet data prefix length. Input parameter.
4720 mejdrech 181
 *  @param addr The destination address. Input parameter.
182
 *  @param addrlen The address length. Input parameter.
4701 mejdrech 183
 *  @returns Number of bytes received.
184
 *  @returns EINVAL if the client does not send data.
185
 *  @returns ENOMEM if there is not enough memory left.
186
 *  @returns Other error codes as defined for the ipc_data_read_finalize() function.
187
 */
4720 mejdrech 188
int socket_read_packet_data( packet_ref packet, size_t prefix, const struct sockaddr * addr, socklen_t addrlen );
4701 mejdrech 189
 
4720 mejdrech 190
/** Sets the address port.
191
 *  Supports AF_INET and AF_INET6 address families.
192
 *  @param addr The address to be updated. Input/output parameter.
193
 *  @param addrlen The address length. Input parameter.
194
 *  @param port The port to be set. Input parameter.
195
 *  @returns EOK on success.
196
 *  @returns EINVAL if the address length does not match the address family.
197
 *  @returns EAFNOSUPPORT if the address family is not supported.
198
 */
199
int udp_set_address_port( struct sockaddr * addr, int addrlen, uint16_t port );
200
 
4701 mejdrech 201
/** UDP global data.
202
 */
4499 mejdrech 203
udp_globals_t   udp_globals;
204
 
205
int udp_initialize( async_client_conn_t client_connection ){
4579 mejdrech 206
    ERROR_DECLARE;
4499 mejdrech 207
 
4722 mejdrech 208
    measured_string_t   names[] = {{ "UDP_CHECKSUM_COMPUTING", 22 }};
209
    measured_string_ref configuration;
210
    size_t              count = sizeof( names ) / sizeof( measured_string_t );
211
    char *              data;
212
 
4700 mejdrech 213
    fibril_rwlock_initialize( & udp_globals.lock );
214
    fibril_rwlock_write_lock( & udp_globals.lock );
4707 mejdrech 215
    udp_globals.icmp_phone = icmp_connect_module( SERVICE_ICMP );
216
    if( udp_globals.icmp_phone < 0 ){
217
        return udp_globals.icmp_phone;
218
    }
4499 mejdrech 219
    udp_globals.ip_phone = ip_bind_service( SERVICE_IP, IPPROTO_UDP, SERVICE_UDP, client_connection, udp_received_msg );
4579 mejdrech 220
    if( udp_globals.ip_phone < 0 ){
221
        return udp_globals.ip_phone;
222
    }
223
    ERROR_PROPAGATE( ip_packet_size_req( udp_globals.ip_phone, -1, & udp_globals.addr_len, & udp_globals.prefix, & udp_globals.content, & udp_globals.suffix ));
224
    ERROR_PROPAGATE( socket_ports_initialize( & udp_globals.sockets ));
225
    udp_globals.prefix += sizeof( udp_header_t );
226
    udp_globals.content -= sizeof( udp_header_t );
4700 mejdrech 227
    udp_globals.last_used_port = UDP_FREE_PORTS_START - 1;
4722 mejdrech 228
    // get configuration
229
    udp_globals.checksum_computing = NET_DEFAULT_UDP_CHECKSUM_COMPUTING;
230
    configuration = & names[ 0 ];
231
    ERROR_PROPAGATE( net_get_conf_req( udp_globals.net_phone, & configuration, count, & data ));
232
    if( configuration ){
233
        if( configuration[ 0 ].value ){
234
            udp_globals.checksum_computing = ( configuration[ 0 ].value[ 0 ] == 'y' );
235
        }
236
        net_free_settings( configuration, data );
237
    }
4700 mejdrech 238
    fibril_rwlock_write_unlock( & udp_globals.lock );
4579 mejdrech 239
    return EOK;
4499 mejdrech 240
}
241
 
4707 mejdrech 242
int udp_received_msg( device_id_t device_id, packet_t packet, services_t receiver, services_t error ){
4589 mejdrech 243
    ERROR_DECLARE;
244
 
4708 mejdrech 245
    size_t          length;
246
    size_t          offset;
247
    int             result;
4707 mejdrech 248
    uint8_t *       data;
4589 mejdrech 249
    udp_header_ref  header;
250
    socket_core_ref *   socket;
251
    packet_t        next_packet;
4708 mejdrech 252
    size_t          total_length;
4722 mejdrech 253
    uint32_t        checksum;
4589 mejdrech 254
    int             fragments;
255
    packet_t        tmp_packet;
4707 mejdrech 256
    icmp_type_t     type;
257
    icmp_code_t     code;
4722 mejdrech 258
    ip_pseudo_header_ref    ip_header;
259
    struct sockaddr *       src;
260
    struct sockaddr *       dest;
4589 mejdrech 261
 
4707 mejdrech 262
    if( error ){
263
        switch( error ){
264
            case SERVICE_ICMP:
265
                // process error
266
                // TODO remove debug dump
267
                // length = icmp_client_header_length( packet );
4708 mejdrech 268
                result = icmp_client_process_packet( packet, & type, & code, NULL, NULL );
269
                if( result < 0 ){
4713 mejdrech 270
                    return udp_release_and_return( packet, result );
4707 mejdrech 271
                }
272
                printf( "ICMP error %d (%d) in packet %d\n", type, code, packet_get_id( packet ) );
4708 mejdrech 273
                length = ( size_t ) result;
4707 mejdrech 274
                if( ERROR_OCCURRED( packet_trim( packet, length, 0 ))){
4713 mejdrech 275
                    return udp_release_and_return( packet, ERROR_CODE );
4707 mejdrech 276
                }
277
                break;
278
            default:
4713 mejdrech 279
                return udp_release_and_return( packet, ENOTSUP );
4707 mejdrech 280
        }
281
    }
4701 mejdrech 282
    // TODO process received ipopts?
4708 mejdrech 283
    result = ip_client_process_packet( packet, NULL, NULL, NULL, NULL, NULL );
284
    if( result < 0 ){
4713 mejdrech 285
        return udp_release_and_return( packet, result );
4707 mejdrech 286
    }
4708 mejdrech 287
    offset = ( size_t ) result;
4589 mejdrech 288
 
289
    length = packet_get_data_length( packet );
4707 mejdrech 290
    if( length <= 0 ){
4713 mejdrech 291
        return udp_release_and_return( packet, EINVAL );
4707 mejdrech 292
    }
293
    if( length < sizeof( udp_header_t ) + offset ){
4713 mejdrech 294
        return udp_release_and_return( packet, NO_DATA );
4707 mejdrech 295
    }
4499 mejdrech 296
    data = packet_get_data( packet );
4707 mejdrech 297
    if( ! data ){
4713 mejdrech 298
        return udp_release_and_return( packet, NO_DATA );
4707 mejdrech 299
    }
4589 mejdrech 300
    // get udp header
4707 mejdrech 301
    header = ( udp_header_ref )( data + offset );
4589 mejdrech 302
    // find the destination socket
303
    socket = socket_ports_find( & udp_globals.sockets, ntohs( header->dest ));
4707 mejdrech 304
    if( ! socket ){
305
        udp_send_icmp_port_unreachable( packet, error );
306
        return EADDRNOTAVAIL;
307
    }
308
    // trim after successful processing to be able to send an ICMP error message!
309
    ERROR_PROPAGATE( packet_trim( packet, offset, 0 ));
4589 mejdrech 310
    // count the received packet fragments
311
    next_packet = packet;
312
    fragments = 0;
313
    total_length = ntohs( header->len );
4722 mejdrech 314
    // compute header checksum if set
315
    if( header->check && ( ! error )){
316
        result = packet_get_addr( packet, ( uint8_t ** ) & src, ( uint8_t ** ) & dest );
317
        if( result <= 0 ){
318
            return udp_release_and_return( packet, result );
319
        }
320
        if( ERROR_OCCURRED( ip_client_get_pseudo_header( IPPROTO_UDP, src, result, dest, result, total_length, & ip_header, & length ))){
321
            return udp_release_and_return( packet, ERROR_CODE );
322
        }else{
323
            checksum = compute_checksum( 0, ip_header, length );
324
            // the udp header checksum will be added with the first fragment later
325
            free( ip_header );
326
        }
327
    }else{
328
        header->check = 0;
329
        checksum = 0;
330
    }
4589 mejdrech 331
    do{
332
        ++ fragments;
4722 mejdrech 333
        length = packet_get_data_length( next_packet );
4708 mejdrech 334
        if( length <= 0 ){
4713 mejdrech 335
            return udp_release_and_return( packet, NO_DATA );
4707 mejdrech 336
        }
4589 mejdrech 337
        if( total_length < length ){
4707 mejdrech 338
            if( ERROR_OCCURRED( packet_trim( next_packet, 0, length - total_length ))){
4713 mejdrech 339
                return udp_release_and_return( packet, ERROR_CODE );
4707 mejdrech 340
            }
4722 mejdrech 341
            // add partial checksum if set
342
            if( header->check ){
343
                checksum = compute_checksum( checksum, packet_get_data( packet ), packet_get_data_length( packet ));
344
            }
4589 mejdrech 345
            // relese the rest of the packet fragments
346
            tmp_packet = pq_next( next_packet );
347
            while( tmp_packet ){
348
                next_packet = pq_detach( tmp_packet );
349
                pq_release( udp_globals.net_phone, packet_get_id( tmp_packet ));
350
                tmp_packet = next_packet;
351
            }
4722 mejdrech 352
            // exit the loop
4589 mejdrech 353
            break;
354
        }
355
        total_length -= length;
4722 mejdrech 356
        // add partial checksum if set
357
        if( header->check ){
358
            checksum = compute_checksum( checksum, packet_get_data( packet ), packet_get_data_length( packet ));
4589 mejdrech 359
        }
360
    }while(( next_packet = pq_next( next_packet )) && ( total_length > 0 ));
4722 mejdrech 361
    // check checksum
362
    if( header->check ){
363
        if( flip_checksum( compact_checksum( checksum ))){
364
            // TODO checksum error ICMP?
365
            // TODO remove debug dump
366
            printf("udp check failed %x => %x\n", header->check, flip_checksum( compact_checksum( checksum )));
367
            return udp_release_and_return( packet, EINVAL );
368
        }
369
    }
4589 mejdrech 370
    // queue the received packet
4707 mejdrech 371
    if( ERROR_OCCURRED( dyn_fifo_push( &( ** socket ).received, packet_get_id( packet ), SOCKET_MAX_RECEIVED_SIZE ))){
4713 mejdrech 372
        return udp_release_and_return( packet, ERROR_CODE );
4707 mejdrech 373
    }
4499 mejdrech 374
 
4589 mejdrech 375
    // notify the destination socket
4708 mejdrech 376
    async_msg_2(( ** socket ).phone, NET_SOCKET_RECEIVED, ( ipcarg_t ) ( ** socket ).socket_id, ( ipcarg_t ) fragments );
4499 mejdrech 377
    return EOK;
378
}
379
 
380
int udp_message( ipc_callid_t callid, ipc_call_t * call, ipc_call_t * answer, int * answer_count ){
4505 mejdrech 381
    ERROR_DECLARE;
382
 
383
    packet_t    packet;
384
 
4499 mejdrech 385
    * answer_count = 0;
386
    switch( IPC_GET_METHOD( * call )){
387
        case NET_TL_RECEIVED:
4700 mejdrech 388
            fibril_rwlock_read_lock( & udp_globals.lock );
389
            if( ! ERROR_OCCURRED( packet_translate( udp_globals.net_phone, & packet, IPC_GET_PACKET( call )))){
4707 mejdrech 390
                ERROR_CODE = udp_received_msg( IPC_GET_DEVICE( call ), packet, SERVICE_UDP, IPC_GET_ERROR( call ));
4700 mejdrech 391
            }
392
            fibril_rwlock_read_unlock( & udp_globals.lock );
393
            return ERROR_CODE;
4579 mejdrech 394
        case IPC_M_CONNECT_TO_ME:
4713 mejdrech 395
            return udp_process_client_messages( callid, * call );
4499 mejdrech 396
    }
397
    return ENOTSUP;
398
}
399
 
4713 mejdrech 400
int udp_process_client_messages( ipc_callid_t callid, ipc_call_t call ){
4579 mejdrech 401
    int                     res;
402
    bool                    keep_on_going = true;
403
    socket_cores_t          local_sockets;
4701 mejdrech 404
    int                     app_phone = IPC_GET_PHONE( & call );
4720 mejdrech 405
    struct sockaddr *       addr;
4579 mejdrech 406
    size_t                  addrlen;
4700 mejdrech 407
    fibril_rwlock_t         lock;
4701 mejdrech 408
    ipc_call_t              answer;
409
    int                     answer_count;
4579 mejdrech 410
 
411
    /*
412
     * Accept the connection
413
     *  - Answer the first IPC_M_CONNECT_ME_TO call.
414
     */
415
    ipc_answer_0( callid, EOK );
416
 
417
    socket_cores_initialize( & local_sockets );
4700 mejdrech 418
    fibril_rwlock_initialize( & lock );
4579 mejdrech 419
 
420
    while( keep_on_going ){
421
        // refresh data
4713 mejdrech 422
        refresh_answer( & answer, & answer_count );
4579 mejdrech 423
 
4701 mejdrech 424
        callid = async_get_call( & call );
4589 mejdrech 425
//      printf( "message %d\n", IPC_GET_METHOD( * call ));
4579 mejdrech 426
 
4701 mejdrech 427
        switch( IPC_GET_METHOD( call )){
4579 mejdrech 428
            case IPC_M_PHONE_HUNGUP:
429
                keep_on_going = false;
430
                res = EOK;
431
                break;
432
            case NET_SOCKET:
4700 mejdrech 433
                fibril_rwlock_write_lock( & lock );
4589 mejdrech 434
                res = socket_create( & local_sockets, app_phone, SOCKET_SET_SOCKET_ID( answer ));
4700 mejdrech 435
                fibril_rwlock_write_unlock( & lock );
4589 mejdrech 436
                * SOCKET_SET_HEADER_SIZE( answer ) = sizeof( udp_header_t );
437
                * SOCKET_SET_DATA_FRAGMENT_SIZE( answer ) = MAX_UDP_FRAGMENT_SIZE;
4701 mejdrech 438
                answer_count = 3;
4579 mejdrech 439
                break;
440
            case NET_SOCKET_BIND:
4722 mejdrech 441
                res = data_receive(( void ** ) & addr, & addrlen );
4713 mejdrech 442
                if( res == EOK ){
443
                    fibril_rwlock_write_lock( & lock );
444
                    fibril_rwlock_write_lock( & udp_globals.lock );
445
                    res = socket_bind( & local_sockets, & udp_globals.sockets, SOCKET_GET_SOCKET_ID( call ), addr, addrlen, UDP_FREE_PORTS_START, UDP_FREE_PORTS_END, udp_globals.last_used_port );
446
                    fibril_rwlock_write_unlock( & udp_globals.lock );
447
                    fibril_rwlock_write_unlock( & lock );
448
                    free( addr );
4579 mejdrech 449
                }
4713 mejdrech 450
                break;
4579 mejdrech 451
            case NET_SOCKET_SENDTO:
4722 mejdrech 452
                res = data_receive(( void ** ) & addr, & addrlen );
4713 mejdrech 453
                if( res == EOK ){
454
                    fibril_rwlock_read_lock( & lock );
455
                    fibril_rwlock_read_lock( & udp_globals.lock );
456
                    res = udp_sendto_message( & local_sockets, SOCKET_GET_SOCKET_ID( call ), addr, addrlen, SOCKET_GET_DATA_FRAGMENTS( call ), SOCKET_GET_FLAGS( call ));
457
                    fibril_rwlock_read_unlock( & udp_globals.lock );
458
                    fibril_rwlock_read_unlock( & lock );
459
                    free( addr );
4579 mejdrech 460
                }
461
                break;
462
            case NET_SOCKET_RECVFROM:
4700 mejdrech 463
                fibril_rwlock_read_lock( & lock );
464
                fibril_rwlock_read_lock( & udp_globals.lock );
4720 mejdrech 465
                res = udp_recvfrom_message( & local_sockets, SOCKET_GET_SOCKET_ID( call ), SOCKET_GET_FLAGS( call ), & addrlen );
4700 mejdrech 466
                fibril_rwlock_read_unlock( & udp_globals.lock );
467
                fibril_rwlock_read_unlock( & lock );
4589 mejdrech 468
                if( res > 0 ){
469
                    * SOCKET_SET_READ_DATA_LENGTH( answer ) = res;
4720 mejdrech 470
                    * SOCKET_SET_ADDRESS_LENGTH( answer ) = addrlen;
4701 mejdrech 471
                    answer_count = 2;
4589 mejdrech 472
                    res = EOK;
473
                }
4579 mejdrech 474
                break;
475
            case NET_SOCKET_CLOSE:
4700 mejdrech 476
                fibril_rwlock_write_lock( & lock );
477
                fibril_rwlock_write_lock( & udp_globals.lock );
4579 mejdrech 478
                res = socket_destroy( udp_globals.net_phone, SOCKET_GET_SOCKET_ID( call ), & local_sockets, & udp_globals.sockets );
4700 mejdrech 479
                fibril_rwlock_write_unlock( & udp_globals.lock );
480
                fibril_rwlock_write_unlock( & lock );
4579 mejdrech 481
                break;
482
            case NET_SOCKET_GETSOCKOPT:
483
            case NET_SOCKET_SETSOCKOPT:
484
            default:
485
                res = ENOTSUP;
486
                break;
487
        }
488
 
4589 mejdrech 489
//      printf( "res = %d\n", res );
4579 mejdrech 490
 
4713 mejdrech 491
        answer_call( callid, res, & answer, answer_count );
4579 mejdrech 492
    }
493
 
494
    socket_cores_destroy( & local_sockets );
495
 
496
    return EOK;
497
}
498
 
4720 mejdrech 499
int udp_sendto_message( socket_cores_ref local_sockets, int socket_id, const struct sockaddr * addr, socklen_t addrlen, int fragments, int flags ){
4579 mejdrech 500
    ERROR_DECLARE;
501
 
502
    socket_core_ref         socket;
503
    struct sockaddr_in *    address_in;
4720 mejdrech 504
    struct sockaddr_in6 *   address_in6;
4579 mejdrech 505
    packet_t                packet;
4589 mejdrech 506
    packet_t                next_packet;
4579 mejdrech 507
    udp_header_ref          header;
4589 mejdrech 508
    int                     index;
4708 mejdrech 509
    size_t                  total_length;
510
    int                     result;
4720 mejdrech 511
    uint16_t                dest_port;
4722 mejdrech 512
    uint32_t                checksum;
513
    ip_pseudo_header_ref    ip_header;
514
    size_t                  headerlen;
515
    device_id_t             device_id;
4579 mejdrech 516
 
517
    if( addrlen < sizeof( struct sockaddr )) return EINVAL;
4720 mejdrech 518
    switch( addr->sa_family ){
4579 mejdrech 519
        case AF_INET:
520
            if( addrlen != sizeof( struct sockaddr_in )) return EINVAL;
521
            address_in = ( struct sockaddr_in * ) addr;
4720 mejdrech 522
            dest_port = address_in->sin_port;
523
            break;
524
        case AF_INET6:
525
            if( addrlen != sizeof( struct sockaddr_in6 )) return EINVAL;
526
            address_in6 = ( struct sockaddr_in6 * ) addr;
527
            dest_port = address_in6->sin6_port;
528
            break;
529
        default:
530
            return EAFNOSUPPORT;
531
    }
4589 mejdrech 532
 
4720 mejdrech 533
    socket = socket_cores_find( local_sockets, socket_id );
534
    if( ! socket ) return ENOTSOCK;
4589 mejdrech 535
 
4720 mejdrech 536
    // bind the socket to a random free port if not bound
537
    while( socket->port <= 0 ){
538
        // try to find a free port
539
        fibril_rwlock_read_unlock( & udp_globals.lock );
540
        fibril_rwlock_write_lock( & udp_globals.lock );
541
        if( socket->port <= 0 ){
542
            ERROR_PROPAGATE( socket_bind_free_port( & udp_globals.sockets, socket, UDP_FREE_PORTS_START, UDP_FREE_PORTS_END, udp_globals.last_used_port ));
543
            // set the next port as the search starting port number
544
            udp_globals.last_used_port = socket->port;
545
        }
546
        fibril_rwlock_write_unlock( & udp_globals.lock );
547
        fibril_rwlock_read_lock( & udp_globals.lock );
4579 mejdrech 548
    }
4720 mejdrech 549
 
550
    // TODO do not ask all the time
4722 mejdrech 551
    ERROR_PROPAGATE( ip_packet_size_req( udp_globals.ip_phone, socket->device_id, & udp_globals.addr_len, & udp_globals.prefix, & udp_globals.content, & udp_globals.suffix ));
4720 mejdrech 552
 
553
    // read the first packet fragment
554
    result = socket_read_packet_data( & packet, sizeof( udp_header_t ), addr, addrlen );
555
    if( result < 0 ) return result;
556
    total_length = ( size_t ) result;
4722 mejdrech 557
    if( udp_globals.checksum_computing ){
558
        checksum = compute_checksum( 0, packet_get_data( packet ), packet_get_data_length( packet ));
559
    }else{
560
        checksum = 0;
561
    }
4720 mejdrech 562
    // prefix the udp header
563
    header = PACKET_PREFIX( packet, udp_header_t );
564
    if( ! header ){
4722 mejdrech 565
        return udp_release_and_return( packet, ENOMEM );
4720 mejdrech 566
    }
4722 mejdrech 567
    bzero( header, sizeof( * header ));
4720 mejdrech 568
    // read the rest of the packet fragments
569
    for( index = 1; index < fragments; ++ index ){
570
        result = socket_read_packet_data( & next_packet, 0, addr, addrlen );
571
        if( result < 0 ){
572
            return udp_release_and_return( packet, result );
573
        }
574
        packet = pq_add( packet, next_packet, index, 0 );
575
        total_length += ( size_t ) result;
4722 mejdrech 576
        if( udp_globals.checksum_computing ){
577
            checksum = compute_checksum( checksum, packet_get_data( next_packet ), packet_get_data_length( next_packet ));
578
        }
4720 mejdrech 579
    }
580
    // set the udp header
581
    header->source = htons( socket->port );
582
    header->dest = htons( dest_port );
583
    header->len = htons( total_length + sizeof( udp_header_t ));
584
    header->check = 0;
4722 mejdrech 585
    if( udp_globals.checksum_computing ){
586
        if( ERROR_OCCURRED( ip_get_route_req( udp_globals.ip_phone, IPPROTO_UDP, addr, addrlen, & device_id, & ip_header, & headerlen ))){
587
            return udp_release_and_return( packet, ERROR_CODE );
588
        }
589
        if( ERROR_OCCURRED( ip_client_set_pseudo_header_data_length( ip_header, headerlen, total_length + sizeof( udp_header_t )))){
590
            free( ip_header );
591
            return udp_release_and_return( packet, ERROR_CODE );
592
        }
593
/*//        TODO remove debug dump:
594
        uint8_t *   data;
595
        data = ip_header;
596
        printf( "ip_header:\tlength\t= %d\n\tdata\t= %.2hhX %.2hhX %.2hhX %.2hhX:%.2hhX %.2hhX %.2hhX %.2hhX:%.2hhX %.2hhX %.2hhX %.2hhX\n", headerlen, data[ 0 ], data[ 1 ], data[ 2 ], data[ 3 ], data[ 4 ], data[ 5 ], data[ 6 ], data[ 7 ], data[ 8 ], data[ 9 ], data[ 10 ], data[ 11 ] );
597
*/      checksum = compute_checksum( checksum, ip_header, headerlen );
598
        checksum = compute_checksum( checksum, ( uint8_t * ) header, sizeof( * header ));
599
        header->check = htons( flip_checksum( compact_checksum( checksum )));
600
        free( ip_header );
601
    }else{
602
        device_id = socket->device_id;
603
    }
4720 mejdrech 604
    // prepare the first packet fragment
605
    if( ERROR_OCCURRED( ip_client_prepare_packet( packet, IPPROTO_UDP, 0, 0, 0, 0 ))){
4722 mejdrech 606
        return udp_release_and_return( packet, ERROR_CODE );
4720 mejdrech 607
    }
608
    // send the packet
4722 mejdrech 609
    return ip_send_msg( udp_globals.ip_phone, device_id, packet, SERVICE_UDP, 0 );
4579 mejdrech 610
}
611
 
4720 mejdrech 612
int udp_recvfrom_message( socket_cores_ref local_sockets, int socket_id, int flags, size_t * addrlen ){
4579 mejdrech 613
    ERROR_DECLARE;
614
 
4589 mejdrech 615
    socket_core_ref socket;
616
    int             packet_id;
617
    packet_t        packet;
618
    udp_header_ref  header;
4720 mejdrech 619
    struct sockaddr *   addr;
4708 mejdrech 620
    size_t          length;
4589 mejdrech 621
    packet_t        next_packet;
4708 mejdrech 622
    uint8_t *       data;
623
    size_t          fragments;
624
    size_t *        lengths;
4720 mejdrech 625
    size_t          index;
4708 mejdrech 626
    int             result;
4589 mejdrech 627
 
628
    // find the socket
629
    socket = socket_cores_find( local_sockets, socket_id );
630
    if( ! socket ) return ENOTSOCK;
631
    // get the next received packet
632
    packet_id = dyn_fifo_value( & socket->received );
4701 mejdrech 633
    if( packet_id < 0 ) return NO_DATA;
4589 mejdrech 634
    ERROR_PROPAGATE( packet_translate( udp_globals.net_phone, & packet, packet_id ));
635
    // get udp header
636
    data = packet_get_data( packet );
637
    if( ! data ){
638
        pq_release( udp_globals.net_phone, packet_id );
639
        return NO_DATA;
640
    }
641
    header = ( udp_header_ref ) data;
4720 mejdrech 642
 
643
    // set the source address port
644
    result = packet_get_addr( packet, ( uint8_t ** ) & addr, NULL );
645
    if( ERROR_OCCURRED( udp_set_address_port( addr, result, ntohs( header->source )))){
4589 mejdrech 646
        pq_release( udp_globals.net_phone, packet_id );
4720 mejdrech 647
        return ERROR_CODE;
4589 mejdrech 648
    }
4720 mejdrech 649
    * addrlen = ( size_t ) result;
4589 mejdrech 650
    // send the source address
4722 mejdrech 651
    ERROR_PROPAGATE( data_reply( addr, * addrlen ));
4720 mejdrech 652
 
4589 mejdrech 653
    next_packet = pq_next( packet );
654
    if( ! next_packet ){
655
        // write all if only one fragment
4722 mejdrech 656
        ERROR_PROPAGATE( data_reply( data + sizeof( udp_header_t ), packet_get_data_length( packet ) - sizeof( udp_header_t )));
4589 mejdrech 657
        // store the total length
658
        length = packet_get_data_length( packet ) - sizeof( udp_header_t );
659
    }else{
660
        // count the packet fragments
661
        fragments = 1;
662
        next_packet = pq_next( packet );
663
        while(( next_packet = pq_next( next_packet ))){
664
            ++ fragments;
665
        }
666
        // compute and store the fragment lengths
4708 mejdrech 667
        lengths = ( size_t * ) malloc( sizeof( size_t ) * fragments + sizeof( size_t ));
4589 mejdrech 668
        if( ! lengths ) return ENOMEM;
669
        lengths[ 0 ] = packet_get_data_length( packet ) - sizeof( udp_header_t );
670
        lengths[ fragments ] = lengths[ 0 ];
671
        next_packet = pq_next( packet );
672
        for( index = 1; index < fragments; ++ index ){
673
            lengths[ index ] = packet_get_data_length( next_packet );
674
            lengths[ fragments ] += lengths[ index ];
675
            next_packet = pq_next( packet );
676
        }while( next_packet );
677
        // write the fragment lengths
4722 mejdrech 678
        ERROR_PROPAGATE( data_reply( lengths, sizeof( int ) * ( fragments + 1 )));
4589 mejdrech 679
        // write the first fragment
4722 mejdrech 680
        ERROR_PROPAGATE( data_reply( data + sizeof( udp_header_t ), lengths[ 0 ] ));
4589 mejdrech 681
        next_packet = pq_next( packet );
682
        // write the rest of the fragments
683
        for( index = 1; index < fragments; ++ index ){
4722 mejdrech 684
            ERROR_PROPAGATE( data_reply( packet_get_data( next_packet ), lengths[ index ] ));
4589 mejdrech 685
            next_packet = pq_next( packet );
686
        }while( next_packet );
687
        // store the total length
688
        length = lengths[ fragments ];
689
        free( lengths );
690
    }
691
    // release the packet
692
    dyn_fifo_pop( & socket->received );
693
    pq_release( udp_globals.net_phone, packet_get_id( packet ));
694
    // return the total length
4708 mejdrech 695
    return ( int ) length;
4589 mejdrech 696
}
697
 
4720 mejdrech 698
int socket_read_packet_data( packet_ref packet, size_t prefix, const struct sockaddr * addr, socklen_t addrlen ){
4589 mejdrech 699
    ERROR_DECLARE;
700
 
701
    ipc_callid_t    callid;
702
    size_t          length;
703
    void *          data;
704
 
705
    // get the data length
706
    if( ! ipc_data_write_receive( & callid, & length )) return EINVAL;
707
    // get a new packet
708
    * packet = packet_get_4( udp_globals.net_phone, length, udp_globals.addr_len, prefix + udp_globals.prefix, udp_globals.suffix );
709
    if( ! packet ) return ENOMEM;
710
    // allocate space in the packet
711
    data = packet_suffix( * packet, length );
712
    if( ! data ){
4722 mejdrech 713
        return udp_release_and_return( * packet, ENOMEM );
4589 mejdrech 714
    }
715
    // read the data into the packet
716
    if( ERROR_OCCURRED( ipc_data_write_finalize( callid, data, length ))
717
    // set the packet destination address
4720 mejdrech 718
    || ERROR_OCCURRED( packet_set_addr( * packet, NULL, ( uint8_t * ) addr, addrlen ))){
4722 mejdrech 719
        return udp_release_and_return( * packet, ERROR_CODE );
4589 mejdrech 720
    }
4708 mejdrech 721
    return ( int ) length;
4589 mejdrech 722
}
723
 
4713 mejdrech 724
int udp_release_and_return( packet_t packet, int result ){
4707 mejdrech 725
    pq_release( udp_globals.net_phone, packet_get_id( packet ));
726
    return result;
727
}
728
 
729
void udp_send_icmp_port_unreachable( packet_t packet, services_t error ){
730
    packet_t    next;
731
    uint8_t *   src;
732
    int         length;
733
 
734
    // detach the first packet and release the others
735
    next = pq_detach( packet );
736
    if( next ){
737
        pq_release( udp_globals.net_phone, packet_get_id( next ));
738
    }
739
    length = packet_get_addr( packet, & src, NULL );
740
    if(( length > 0 )
741
    && ( ! error )
742
    && ( udp_globals.icmp_phone >= 0 )
743
    // set both addresses to the source one (avoids the source address deletion before setting the destination one)
4708 mejdrech 744
    && ( packet_set_addr( packet, src, src, ( size_t ) length ) == EOK )){
4707 mejdrech 745
        icmp_destination_unreachable_msg( udp_globals.icmp_phone, ICMP_PORT_UNREACH, 0, packet );
746
    }else{
4713 mejdrech 747
        udp_release_and_return( packet, EINVAL );
4707 mejdrech 748
    }
749
}
750
 
4720 mejdrech 751
int udp_set_address_port( struct sockaddr * addr, int addrlen, uint16_t port ){
752
    struct sockaddr_in *    address_in;
753
    struct sockaddr_in6 *   address_in6;
754
    size_t                  length;
755
 
756
    if( addrlen < 0 ) return EINVAL;
757
    length = ( size_t ) addrlen;
758
    if( length < sizeof( struct sockaddr )) return EINVAL;
759
    switch( addr->sa_family ){
760
        case AF_INET:
761
            if( length != sizeof( struct sockaddr_in )) return EINVAL;
762
            address_in = ( struct sockaddr_in * ) addr;
763
            address_in->sin_port = port;
764
            return EOK;
765
        case AF_INET6:
766
            if( length != sizeof( struct sockaddr_in6 )) return EINVAL;
767
            address_in6 = ( struct sockaddr_in6 * ) addr;
768
            address_in6->sin6_port = port;
769
            return EOK;
770
        default:
771
            return EAFNOSUPPORT;
772
    }
773
}
774
 
4499 mejdrech 775
/** @}
776
 */