Subversion Repositories HelenOS

Compare Revisions

No changes between revisions

Ignore whitespace Rev 4725 → Rev 4726

/branches/network/uspace/srv/net/tl/tcp/Makefile
38,6 → 38,7
SOURCES = \
$(NAME).c \
$(NAME)_module.c \
$(NET_BASE)crc.c \
$(NET_BASE)module.c \
$(NET_BASE)modules.c \
$(NET_BASE)il/ip/ip_client.c \
47,6 → 48,7
$(NET_BASE)tl/icmp/icmp_client.c \
$(NET_BASE)tl/icmp/icmp_common.c \
$(NET_BASE)tl/icmp/icmp_remote.c \
$(NET_BASE)tl/tl_common.c \
$(STRUCTURES)dynamic_fifo.c \
$(STRUCTURES)measured_strings.c \
$(STRUCTURES)packet/packet.c \
/branches/network/uspace/srv/net/tl/udp/udp.c
65,6 → 65,7
#include "../../socket/socket_core.h"
#include "../../socket/socket_messages.h"
 
#include "../tl_common.h"
#include "../tl_messages.h"
 
#include "udp.h"
113,14 → 114,6
*/
int udp_release_and_return( packet_t packet, int result );
 
/** Sends the port unreachable ICMP notification.
* Sends the first packet and releases all the others.
* Releases the packet queu on error.
* @param packet The packet to be send. Input parameter.
* @param error The packet error reporting service. Prefixes the received packet. Input parameter.
*/
void udp_send_icmp_port_unreachable( packet_t packet, services_t error );
 
/** @name Socket messages processing functions
*/
/*@{*/
143,6 → 136,7
* @param addr The destination address. Input parameter.
* @param addrlen The address length. Input parameter.
* @param fragments The number of data fragments. Input parameter.
* @param data_fragment_size The data fragment size in bytes. Input parameter.
* @param flags Various send flags. Input parameter.
* @returns EOK on success.
* @returns EAFNOTSUPPORT if the address family is not supported.
154,7 → 148,7
* @returns Other error codes as defined for the ip_client_prepare_packet() function.
* @returns Other error codes as defined for the ip_send_msg() function.
*/
int udp_sendto_message( socket_cores_ref local_sockets, int socket_id, const struct sockaddr * addr, socklen_t addrlen, int fragments, int flags );
int udp_sendto_message( socket_cores_ref local_sockets, int socket_id, const struct sockaddr * addr, socklen_t addrlen, int fragments, size_t data_fragment_size, int flags );
 
/** Receives data to the socket.
* Handles the NET_SOCKET_RECVFROM message.
175,29 → 169,6
 
/*@}*/
 
/** Receives data from the socket into a packet.
* @param packet The new created packet. Output parameter.
* @param prefix Reserved packet data prefix length. Input parameter.
* @param addr The destination address. Input parameter.
* @param addrlen The address length. Input parameter.
* @returns Number of bytes received.
* @returns EINVAL if the client does not send data.
* @returns ENOMEM if there is not enough memory left.
* @returns Other error codes as defined for the ipc_data_read_finalize() function.
*/
int socket_read_packet_data( packet_ref packet, size_t prefix, const struct sockaddr * addr, socklen_t addrlen );
 
/** Sets the address port.
* Supports AF_INET and AF_INET6 address families.
* @param addr The address to be updated. Input/output parameter.
* @param addrlen The address length. Input parameter.
* @param port The port to be set. Input parameter.
* @returns EOK on success.
* @returns EINVAL if the address length does not match the address family.
* @returns EAFNOSUPPORT if the address family is not supported.
*/
int udp_set_address_port( struct sockaddr * addr, int addrlen, uint16_t port );
 
/** UDP global data.
*/
udp_globals_t udp_globals;
220,10 → 191,10
if( udp_globals.ip_phone < 0 ){
return udp_globals.ip_phone;
}
ERROR_PROPAGATE( ip_packet_size_req( udp_globals.ip_phone, -1, & udp_globals.addr_len, & udp_globals.prefix, & udp_globals.content, & udp_globals.suffix ));
ERROR_PROPAGATE( ip_packet_size_req( udp_globals.ip_phone, -1, & udp_globals.packet_dimension.addr_len, & udp_globals.packet_dimension.prefix, & udp_globals.packet_dimension.content, & udp_globals.packet_dimension.suffix ));
ERROR_PROPAGATE( socket_ports_initialize( & udp_globals.sockets ));
udp_globals.prefix += sizeof( udp_header_t );
udp_globals.content -= sizeof( udp_header_t );
udp_globals.packet_dimension.prefix += sizeof( udp_header_t );
udp_globals.packet_dimension.content -= sizeof( udp_header_t );
udp_globals.last_used_port = UDP_FREE_PORTS_START - 1;
// get configuration
udp_globals.checksum_computing = NET_DEFAULT_UDP_CHECKSUM_COMPUTING;
302,7 → 273,7
// find the destination socket
socket = socket_ports_find( & udp_globals.sockets, ntohs( header->dest ));
if( ! socket ){
udp_send_icmp_port_unreachable( packet, error );
tl_send_icmp_port_unreachable( udp_globals.net_phone, udp_globals.icmp_phone, packet, error );
return EADDRNOTAVAIL;
}
// trim after successful processing to be able to send an ICMP error message!
373,7 → 344,7
}
 
// notify the destination socket
async_msg_2(( ** socket ).phone, NET_SOCKET_RECEIVED, ( ipcarg_t ) ( ** socket ).socket_id, ( ipcarg_t ) fragments );
async_msg_5(( ** socket ).phone, NET_SOCKET_RECEIVED, ( ipcarg_t ) ( ** socket ).socket_id, 0, 0, 0, ( ipcarg_t ) fragments );
return EOK;
}
 
404,7 → 375,7
int app_phone = IPC_GET_PHONE( & call );
struct sockaddr * addr;
size_t addrlen;
fibril_rwlock_t lock;
// fibril_rwlock_t lock;
ipc_call_t answer;
int answer_count;
 
414,8 → 385,10
*/
ipc_answer_0( callid, EOK );
 
// The client connection is only in one fibril and therefore no additional locks are needed.
 
socket_cores_initialize( & local_sockets );
fibril_rwlock_initialize( & lock );
// fibril_rwlock_initialize( & lock );
 
while( keep_on_going ){
// refresh data
430,21 → 403,21
res = EOK;
break;
case NET_SOCKET:
fibril_rwlock_write_lock( & lock );
res = socket_create( & local_sockets, app_phone, SOCKET_SET_SOCKET_ID( answer ));
fibril_rwlock_write_unlock( & lock );
// fibril_rwlock_write_lock( & lock );
res = socket_create( & local_sockets, app_phone, NULL, SOCKET_SET_SOCKET_ID( answer ));
// fibril_rwlock_write_unlock( & lock );
* SOCKET_SET_DATA_FRAGMENT_SIZE( answer ) = MAX_UDP_FRAGMENT_SIZE;
* SOCKET_SET_HEADER_SIZE( answer ) = sizeof( udp_header_t );
* SOCKET_SET_DATA_FRAGMENT_SIZE( answer ) = MAX_UDP_FRAGMENT_SIZE;
answer_count = 3;
break;
case NET_SOCKET_BIND:
res = data_receive(( void ** ) & addr, & addrlen );
if( res == EOK ){
fibril_rwlock_write_lock( & lock );
// fibril_rwlock_write_lock( & lock );
fibril_rwlock_write_lock( & udp_globals.lock );
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 );
fibril_rwlock_write_unlock( & udp_globals.lock );
fibril_rwlock_write_unlock( & lock );
// fibril_rwlock_write_unlock( & lock );
free( addr );
}
break;
451,20 → 424,20
case NET_SOCKET_SENDTO:
res = data_receive(( void ** ) & addr, & addrlen );
if( res == EOK ){
fibril_rwlock_read_lock( & lock );
// fibril_rwlock_read_lock( & lock );
fibril_rwlock_read_lock( & udp_globals.lock );
res = udp_sendto_message( & local_sockets, SOCKET_GET_SOCKET_ID( call ), addr, addrlen, SOCKET_GET_DATA_FRAGMENTS( call ), SOCKET_GET_FLAGS( call ));
res = udp_sendto_message( & local_sockets, SOCKET_GET_SOCKET_ID( call ), addr, addrlen, SOCKET_GET_DATA_FRAGMENTS( call ), SOCKET_GET_DATA_FRAGMENT_SIZE( call ), SOCKET_GET_FLAGS( call ));
fibril_rwlock_read_unlock( & udp_globals.lock );
fibril_rwlock_read_unlock( & lock );
// fibril_rwlock_read_unlock( & lock );
free( addr );
}
break;
case NET_SOCKET_RECVFROM:
fibril_rwlock_read_lock( & lock );
// fibril_rwlock_read_lock( & lock );
fibril_rwlock_read_lock( & udp_globals.lock );
res = udp_recvfrom_message( & local_sockets, SOCKET_GET_SOCKET_ID( call ), SOCKET_GET_FLAGS( call ), & addrlen );
fibril_rwlock_read_unlock( & udp_globals.lock );
fibril_rwlock_read_unlock( & lock );
// fibril_rwlock_read_unlock( & lock );
if( res > 0 ){
* SOCKET_SET_READ_DATA_LENGTH( answer ) = res;
* SOCKET_SET_ADDRESS_LENGTH( answer ) = addrlen;
473,11 → 446,11
}
break;
case NET_SOCKET_CLOSE:
fibril_rwlock_write_lock( & lock );
// fibril_rwlock_write_lock( & lock );
fibril_rwlock_write_lock( & udp_globals.lock );
res = socket_destroy( udp_globals.net_phone, SOCKET_GET_SOCKET_ID( call ), & local_sockets, & udp_globals.sockets );
fibril_rwlock_write_unlock( & udp_globals.lock );
fibril_rwlock_write_unlock( & lock );
// fibril_rwlock_write_unlock( & lock );
break;
case NET_SOCKET_GETSOCKOPT:
case NET_SOCKET_SETSOCKOPT:
491,17 → 464,16
answer_call( callid, res, & answer, answer_count );
}
 
// TODO call socket_destroy() on all bound!
socket_cores_destroy( & local_sockets );
 
return EOK;
}
 
int udp_sendto_message( socket_cores_ref local_sockets, int socket_id, const struct sockaddr * addr, socklen_t addrlen, int fragments, int flags ){
int udp_sendto_message( socket_cores_ref local_sockets, int socket_id, const struct sockaddr * addr, socklen_t addrlen, int fragments, size_t data_fragment_size, int flags ){
ERROR_DECLARE;
 
socket_core_ref socket;
struct sockaddr_in * address_in;
struct sockaddr_in6 * address_in6;
packet_t packet;
packet_t next_packet;
udp_header_ref header;
514,21 → 486,7
size_t headerlen;
device_id_t device_id;
 
if( addrlen < sizeof( struct sockaddr )) return EINVAL;
switch( addr->sa_family ){
case AF_INET:
if( addrlen != sizeof( struct sockaddr_in )) return EINVAL;
address_in = ( struct sockaddr_in * ) addr;
dest_port = address_in->sin_port;
break;
case AF_INET6:
if( addrlen != sizeof( struct sockaddr_in6 )) return EINVAL;
address_in6 = ( struct sockaddr_in6 * ) addr;
dest_port = address_in6->sin6_port;
break;
default:
return EAFNOSUPPORT;
}
ERROR_PROPAGATE( tl_get_address_port( addr, addrlen, & dest_port ));
 
socket = socket_cores_find( local_sockets, socket_id );
if( ! socket ) return ENOTSOCK;
548,10 → 506,10
}
 
// TODO do not ask all the time
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 ));
ERROR_PROPAGATE( ip_packet_size_req( udp_globals.ip_phone, -1, & udp_globals.packet_dimension.addr_len, & udp_globals.packet_dimension.prefix, & udp_globals.packet_dimension.content, & udp_globals.packet_dimension.suffix ));
 
// read the first packet fragment
result = socket_read_packet_data( & packet, sizeof( udp_header_t ), addr, addrlen );
result = tl_socket_read_packet_data( udp_globals.net_phone, & packet, sizeof( udp_header_t ), & udp_globals.packet_dimension, addr, addrlen );
if( result < 0 ) return result;
total_length = ( size_t ) result;
if( udp_globals.checksum_computing ){
567,7 → 525,7
bzero( header, sizeof( * header ));
// read the rest of the packet fragments
for( index = 1; index < fragments; ++ index ){
result = socket_read_packet_data( & next_packet, 0, addr, addrlen );
result = tl_socket_read_packet_data( udp_globals.net_phone, & next_packet, 0, & udp_globals.packet_dimension, addr, addrlen );
if( result < 0 ){
return udp_release_and_return( packet, result );
}
599,7 → 557,7
header->check = htons( flip_checksum( compact_checksum( checksum )));
free( ip_header );
}else{
device_id = socket->device_id;
device_id = -1;
}
// prepare the first packet fragment
if( ERROR_OCCURRED( ip_client_prepare_packet( packet, IPPROTO_UDP, 0, 0, 0, 0 ))){
642,7 → 600,7
 
// set the source address port
result = packet_get_addr( packet, ( uint8_t ** ) & addr, NULL );
if( ERROR_OCCURRED( udp_set_address_port( addr, result, ntohs( header->source )))){
if( ERROR_OCCURRED( tl_set_address_port( addr, result, ntohs( header->source )))){
pq_release( udp_globals.net_phone, packet_id );
return ERROR_CODE;
}
695,82 → 653,10
return ( int ) length;
}
 
int socket_read_packet_data( packet_ref packet, size_t prefix, const struct sockaddr * addr, socklen_t addrlen ){
ERROR_DECLARE;
 
ipc_callid_t callid;
size_t length;
void * data;
 
// get the data length
if( ! ipc_data_write_receive( & callid, & length )) return EINVAL;
// get a new packet
* packet = packet_get_4( udp_globals.net_phone, length, udp_globals.addr_len, prefix + udp_globals.prefix, udp_globals.suffix );
if( ! packet ) return ENOMEM;
// allocate space in the packet
data = packet_suffix( * packet, length );
if( ! data ){
return udp_release_and_return( * packet, ENOMEM );
}
// read the data into the packet
if( ERROR_OCCURRED( ipc_data_write_finalize( callid, data, length ))
// set the packet destination address
|| ERROR_OCCURRED( packet_set_addr( * packet, NULL, ( uint8_t * ) addr, addrlen ))){
return udp_release_and_return( * packet, ERROR_CODE );
}
return ( int ) length;
}
 
int udp_release_and_return( packet_t packet, int result ){
pq_release( udp_globals.net_phone, packet_get_id( packet ));
return result;
}
 
void udp_send_icmp_port_unreachable( packet_t packet, services_t error ){
packet_t next;
uint8_t * src;
int length;
 
// detach the first packet and release the others
next = pq_detach( packet );
if( next ){
pq_release( udp_globals.net_phone, packet_get_id( next ));
}
length = packet_get_addr( packet, & src, NULL );
if(( length > 0 )
&& ( ! error )
&& ( udp_globals.icmp_phone >= 0 )
// set both addresses to the source one (avoids the source address deletion before setting the destination one)
&& ( packet_set_addr( packet, src, src, ( size_t ) length ) == EOK )){
icmp_destination_unreachable_msg( udp_globals.icmp_phone, ICMP_PORT_UNREACH, 0, packet );
}else{
udp_release_and_return( packet, EINVAL );
}
}
 
int udp_set_address_port( struct sockaddr * addr, int addrlen, uint16_t port ){
struct sockaddr_in * address_in;
struct sockaddr_in6 * address_in6;
size_t length;
 
if( addrlen < 0 ) return EINVAL;
length = ( size_t ) addrlen;
if( length < sizeof( struct sockaddr )) return EINVAL;
switch( addr->sa_family ){
case AF_INET:
if( length != sizeof( struct sockaddr_in )) return EINVAL;
address_in = ( struct sockaddr_in * ) addr;
address_in->sin_port = port;
return EOK;
case AF_INET6:
if( length != sizeof( struct sockaddr_in6 )) return EINVAL;
address_in6 = ( struct sockaddr_in6 * ) addr;
address_in6->sin6_port = port;
return EOK;
default:
return EAFNOSUPPORT;
}
}
 
/** @}
*/
/branches/network/uspace/srv/net/tl/udp/Makefile
48,6 → 48,7
$(NET_BASE)tl/icmp/icmp_client.c \
$(NET_BASE)tl/icmp/icmp_common.c \
$(NET_BASE)tl/icmp/icmp_remote.c \
$(NET_BASE)tl/tl_common.c \
$(STRUCTURES)dynamic_fifo.c \
$(STRUCTURES)measured_strings.c \
$(STRUCTURES)packet/packet.c \
/branches/network/uspace/srv/net/tl/udp/udp.h
41,6 → 41,8
 
#include "../../socket/socket_core.h"
 
#include "../tl_common.h"
 
/** Type definition of the UDP global data.
* @see udp_globals
*/
58,18 → 60,9
/** ICMP module phone.
*/
int icmp_phone;
/** Reserved packet prefix length.
/** Packet dimension.
*/
size_t prefix;
/** Maximal packet content length.
*/
size_t content;
/** Reserved packet suffix length.
*/
size_t suffix;
/** Packet address length.
*/
size_t addr_len;
packet_dimension_t packet_dimension;
/** Indicates whether UDP checksum computing is enabled.
*/
int checksum_computing;
/branches/network/uspace/srv/net/tl/tl_common.c
0,0 → 1,154
/*
* Copyright (c) 2008 Lukas Mejdrech
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
 
/** @addtogroup net_tl
* @{
*/
 
/** @file
* Transport layer common functions implementation.
* @see tl_common.h
*/
 
#include <async.h>
#include <ipc/services.h>
 
#include "../err.h"
 
#include "../structures/packet/packet.h"
#include "../structures/packet/packet_client.h"
 
#include "../include/icmp_interface.h"
#include "../include/in.h"
#include "../include/in6.h"
#include "../include/inet.h"
#include "../include/socket_codes.h"
#include "../include/socket_errno.h"
 
#include "tl_common.h"
 
int tl_get_address_port( const struct sockaddr * addr, int addrlen, uint16_t * port ){
const struct sockaddr_in * address_in;
const struct sockaddr_in6 * address_in6;
 
if( addrlen < sizeof( struct sockaddr )) return EINVAL;
switch( addr->sa_family ){
case AF_INET:
if( addrlen != sizeof( struct sockaddr_in )) return EINVAL;
address_in = ( struct sockaddr_in * ) addr;
* port = address_in->sin_port;
break;
case AF_INET6:
if( addrlen != sizeof( struct sockaddr_in6 )) return EINVAL;
address_in6 = ( struct sockaddr_in6 * ) addr;
* port = address_in6->sin6_port;
break;
default:
return EAFNOSUPPORT;
}
return EOK;
}
 
 
int tl_set_address_port( struct sockaddr * addr, int addrlen, uint16_t port ){
struct sockaddr_in * address_in;
struct sockaddr_in6 * address_in6;
size_t length;
 
if( addrlen < 0 ) return EINVAL;
length = ( size_t ) addrlen;
if( length < sizeof( struct sockaddr )) return EINVAL;
switch( addr->sa_family ){
case AF_INET:
if( length != sizeof( struct sockaddr_in )) return EINVAL;
address_in = ( struct sockaddr_in * ) addr;
address_in->sin_port = port;
return EOK;
case AF_INET6:
if( length != sizeof( struct sockaddr_in6 )) return EINVAL;
address_in6 = ( struct sockaddr_in6 * ) addr;
address_in6->sin6_port = port;
return EOK;
default:
return EAFNOSUPPORT;
}
}
 
void tl_send_icmp_port_unreachable( int packet_phone, int icmp_phone, packet_t packet, services_t error ){
packet_t next;
uint8_t * src;
int length;
 
// detach the first packet and release the others
next = pq_detach( packet );
if( next ){
pq_release( packet_phone, packet_get_id( next ));
}
length = packet_get_addr( packet, & src, NULL );
if(( length > 0 )
&& ( ! error )
&& ( icmp_phone >= 0 )
// set both addresses to the source one (avoids the source address deletion before setting the destination one)
&& ( packet_set_addr( packet, src, src, ( size_t ) length ) == EOK )){
icmp_destination_unreachable_msg( icmp_phone, ICMP_PORT_UNREACH, 0, packet );
}else{
pq_release( packet_phone, packet_get_id( packet ));
}
}
 
int tl_socket_read_packet_data( int packet_phone, packet_ref packet, size_t prefix, const packet_dimension_ref dimension, const struct sockaddr * addr, socklen_t addrlen ){
ERROR_DECLARE;
 
ipc_callid_t callid;
size_t length;
void * data;
 
if( ! dimension ) return EINVAL;
// get the data length
if( ! ipc_data_write_receive( & callid, & length )) return EINVAL;
// get a new packet
* packet = packet_get_4( packet_phone, length, dimension->addr_len, prefix + dimension->prefix, dimension->suffix );
if( ! packet ) return ENOMEM;
// allocate space in the packet
data = packet_suffix( * packet, length );
if( ! data ){
pq_release( packet_phone, packet_get_id( * packet ));
return ENOMEM;
}
// read the data into the packet
if( ERROR_OCCURRED( ipc_data_write_finalize( callid, data, length ))
// set the packet destination address
|| ERROR_OCCURRED( packet_set_addr( * packet, NULL, ( uint8_t * ) addr, addrlen ))){
pq_release( packet_phone, packet_get_id( * packet ));
return ERROR_CODE;
}
return ( int ) length;
}
 
/** @}
*/
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
/branches/network/uspace/srv/net/tl/tl_common.h
0,0 → 1,122
/*
* Copyright (c) 2008 Lukas Mejdrech
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
 
/** @addtogroup net_tl
* @{
*/
 
/** @file
* Transport layer common functions.
*/
 
#ifndef __NET_TL_COMMON_H__
#define __NET_TL_COMMON_H__
 
#include "../structures/packet/packet.h"
 
#include "../include/inet.h"
#include "../include/socket_codes.h"
 
/** Type definition of the packet dimension.
* @see packet_dimension
*/
typedef struct packet_dimension packet_dimension_t;
 
/** Type definition of the packet dimension pointer.
* @see packet_dimension
*/
typedef packet_dimension_t * packet_dimension_ref;
 
/** Packet dimension.
*/
struct packet_dimension{
/** Reserved packet prefix length.
*/
size_t prefix;
/** Maximal packet content length.
*/
size_t content;
/** Reserved packet suffix length.
*/
size_t suffix;
/** Maximal packet address length.
*/
size_t addr_len;
};
 
/** Gets the address port.
* Supports AF_INET and AF_INET6 address families.
* @param addr The address to be updated. Input/output parameter.
* @param addrlen The address length. Input parameter.
* @param port The set port. Output parameter.
* @returns EOK on success.
* @returns EINVAL if the address length does not match the address family.
* @returns EAFNOSUPPORT if the address family is not supported.
*/
int tl_get_address_port( const struct sockaddr * addr, int addrlen, uint16_t * port );
 
/** Sets the address port.
* Supports AF_INET and AF_INET6 address families.
* @param addr The address to be updated. Input/output parameter.
* @param addrlen The address length. Input parameter.
* @param port The port to be set. Input parameter.
* @returns EOK on success.
* @returns EINVAL if the address length does not match the address family.
* @returns EAFNOSUPPORT if the address family is not supported.
*/
int tl_set_address_port( struct sockaddr * addr, int addrlen, uint16_t port );
 
/** Sends the port unreachable ICMP notification.
* Sends the first packet and releases all the others.
* Releases the packet queu on error.
* @param packet_phone The packet server module phone. Input parameter.
* @param icmp_phone The ICMP module phone. Input parameter.
* @param packet The packet to be send. Input parameter.
* @param error The packet error reporting service. Prefixes the received packet. Input parameter.
*/
void tl_send_icmp_port_unreachable( int packet_phone, int icmp_phone, packet_t packet, services_t error );
 
/** Receives data from the socket into a packet.
* @param packet_phone The packet server module phone. Input parameter.
* @param packet The new created packet. Output parameter.
* @param prefix Reserved packet data prefix length. Input parameter.
* @param dimension The packet dimension. Input parameter.
* @param addr The destination address. Input parameter.
* @param addrlen The address length. Input parameter.
* @returns Number of bytes received.
* @returns EINVAL if the client does not send data.
* @returns ENOMEM if there is not enough memory left.
* @returns Other error codes as defined for the ipc_data_read_finalize() function.
*/
int tl_socket_read_packet_data( int packet_phone, packet_ref packet, size_t prefix, const packet_dimension_ref dimension, const struct sockaddr * addr, socklen_t addrlen );
 
#endif
 
/** @}
*/
 
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
/branches/network/uspace/srv/net/app/print_error.c
130,6 → 130,9
case NO_DATA:
fprintf( output, "No data (%d) error", error_code );
break;
case EINPROGRESS:
fprintf( output, "Another operation in progress (%d) error", error_code );
break;
default:
fprintf( output, "Other (%d) error", error_code );
}
/branches/network/uspace/srv/net/include/socket_errno.h
51,9 → 51,9
////#define EINVAL (-10022)
////#define EMFILE (-10024)
//#define EWOULDBLOCK (-10035)
/* If any API function is called while a blocking function is in progress.
/** An API function is called while another blocking function is in progress.
*/
//#define EINPROGRESS (-10036)
#define EINPROGRESS (-10036)
//#define EALREADY (-10037)
 
/** The socket identifier is not valid.
/branches/network/uspace/srv/net/include/socket.h
114,6 → 114,7
* @param socket_id Socket identifier. Input parameter.
* @returns EOK on success.
* @returns ENOTSOCK if the socket is not found.
* @returns EINPROGRESS if there is another blocking function in progress.
* @returns Other error codes as defined for the NET_SOCKET_CLOSE message.
*/
int closesocket( int socket_id );
/branches/network/uspace/srv/net/socket/socket_core.c
135,7 → 135,7
return EOK;
}
 
int socket_create( socket_cores_ref local_sockets, int app_phone, int * socket_id ){
int socket_create( socket_cores_ref local_sockets, int app_phone, void * specific_data, int * socket_id ){
ERROR_DECLARE;
 
socket_core_ref socket;
147,8 → 147,7
// initialize
socket->phone = app_phone;
socket->port = -1;
socket->device_id = -1;
socket->peer_addr = NULL;
socket->specific_data = specific_data;
if( ERROR_OCCURRED( dyn_fifo_initialize( & socket->received, SOCKET_INITIAL_RECEIVED_SIZE ))){
free( socket );
return ERROR_CODE;
/branches/network/uspace/srv/net/socket/socket_messages.h
73,22 → 73,21
#define SOCKET_SET_READ_DATA_LENGTH( call ) ( int * ) & IPC_GET_ARG1( call )
#define SOCKET_GET_READ_DATA_LENGTH( call ) ( int ) IPC_GET_ARG1( call )
 
#define SOCKET_SET_HEADER_SIZE( call ) ( int * ) & IPC_GET_ARG2( call )
#define SOCKET_GET_HEADER_SIZE( call ) ( int ) IPC_GET_ARG2( call )
#define SOCKET_GET_BACKLOG( call ) ( int ) IPC_GET_ARG2( call )
#define SOCKET_GET_OPT_LEVEL( call ) ( int ) IPC_GET_ARG2( call )
#define SOCKET_SET_DATA_FRAGMENTS( call ) ( int * ) & IPC_GET_ARG2( call )
#define SOCKET_GET_DATA_FRAGMENTS( call ) ( int ) IPC_GET_ARG2( call )
#define SOCKET_SET_ADDRESS_LENGTH( call ) ( socklen_t * ) & IPC_GET_ARG2( call )
#define SOCKET_GET_ADDRESS_LENGTH( call ) ( socklen_t ) IPC_GET_ARG2( call )
#define SOCKET_GET_DATA_FRAGMENT_SIZE( call ) ( size_t ) IPC_GET_ARG2( call )
#define SOCKET_SET_DATA_FRAGMENT_SIZE( call ) ( size_t * ) & IPC_GET_ARG2( call )
 
#define SOCKET_SET_DATA_FRAGMENT_SIZE( call ) ( size_t * ) & IPC_GET_ARG3( call )
#define SOCKET_GET_DATA_FRAGMENT_SIZE( call ) ( size_t ) IPC_GET_ARG3( call )
#define SOCKET_SET_HEADER_SIZE( call ) ( int * ) & IPC_GET_ARG3( call )
#define SOCKET_GET_HEADER_SIZE( call ) ( int ) IPC_GET_ARG3( call )
 
#define SOCKET_GET_FLAGS( call ) ( int ) IPC_GET_ARG4( call )
 
#define SOCKET_GET_OPT_NAME( call ) ( int ) IPC_GET_ARG4( call )
 
#define SOCKET_GET_DATA_FRAGMENTS( call ) ( int ) IPC_GET_ARG5( call )
 
/*@}*/
 
#endif
/branches/network/uspace/srv/net/socket/socket_core.h
59,11 → 59,9
int socket_id;
int phone;
int port;
int data_fragment_size;
device_id_t device_id;
struct sockaddr_in * peer_addr;
dyn_fifo_t received;
dyn_fifo_t accepted;
void * specific_data;
};
 
INT_MAP_DECLARE( socket_cores, socket_core_t );
72,7 → 70,7
 
int socket_bind( socket_cores_ref local_sockets, socket_ports_ref global_sockets, int socket_id, void * addr, size_t addrlen, int free_ports_start, int free_ports_end, int last_used_port );
int socket_bind_free_port( socket_ports_ref global_sockets, socket_core_ref socket, int free_ports_start, int free_ports_end, int last_used_port );
int socket_create( socket_cores_ref local_sockets, int app_phone, int * socket_id );
int socket_create( socket_cores_ref local_sockets, int app_phone, void * specific_data, int * socket_id );
int socket_destroy( int packet_phone, int socket_id, socket_cores_ref local_sockets, socket_ports_ref global_sockets );
 
#endif
/branches/network/uspace/srv/net/socket/socket_client.c
81,6 → 81,7
typedef socket_t * socket_ref;
 
/** Socket specific data.
* Each socket lock locks only its structure part and any number of them may be locked simultaneously.
*/
struct socket{
/** Socket identifier.
97,16 → 98,23
*/
size_t header_size;
/** Packet data fragment size.
* Sending and receiving optimalization.
* Sending optimalization.
*/
size_t data_fragment_size;
/** Sending safety lock.
* Locks the header_size and data_fragment_size attributes.
*/
fibril_rwlock_t sending_lock;
/** Received packets queue.
*/
dyn_fifo_t received;
/** Received packets safety lock.
* Used for receiving and receive notifications.
* Locks the received attribute.
*/
fibril_mutex_t receive_lock;
/** Received packets signaling.
* Signaled upon receive notification.
*/
fibril_condvar_t receive_signal;
/** Waiting sockets queue.
113,11 → 121,18
*/
dyn_fifo_t accepted;
/** Waiting sockets safety lock.
* Used for accepting and accept notifications.
* Locks the accepted attribute.
*/
fibril_mutex_t accept_lock;
/** Waiting sockets signaling.
* Signaled upon accept notification.
*/
fibril_condvar_t accept_signal;
/** The number of blocked functions called.
* Used while waiting for the received packets or accepted sockets.
*/
int blocked;
};
 
/** Sockets map.
128,7 → 143,7
 
/** Socket client library global data.
*/
static struct{
static struct socket_client_globals {
/** TCP module phone.
*/
int tcp_phone;
138,7 → 153,26
/** Active sockets.
*/
sockets_ref sockets;
} socket_globals = { -1, -1, NULL };
/** Safety lock.
* Write lock is used only for adding or removing sockets.
* When locked for writing, no other socket locks need to be locked.
* When locked for reading, any other socket locks may be locked.
* No socket lock may be locked if this lock is unlocked.
*/
fibril_rwlock_t lock;
} socket_globals = {
.tcp_phone = -1,
.udp_phone = -1,
.sockets = NULL,
.lock = {
.readers = 0,
.writers = 0,
.waiters = {
.prev = & socket_globals.lock.waiters,
.next = & socket_globals.lock.waiters
}
}
};
 
INT_MAP_IMPLEMENT( sockets, socket_t );
 
261,6 → 295,7
fibril_condvar_initialize( & socket->receive_signal );
fibril_mutex_initialize( & socket->accept_lock );
fibril_condvar_initialize( & socket->accept_signal );
fibril_rwlock_initialize( & socket->sending_lock );
}
 
void socket_connection( ipc_callid_t iid, ipc_call_t * icall ){
275,62 → 310,72
 
callid = async_get_call( & call );
switch( IPC_GET_METHOD( call )){
// TODO remember the data_fragment_size
case NET_SOCKET_RECEIVED:
fibril_rwlock_read_lock( & socket_globals.lock );
// find the socket
socket = sockets_find( socket_get_sockets(), SOCKET_GET_SOCKET_ID( call ));
if( ! socket ){
ERROR_CODE = ENOTSOCK;
break;
}else{
fibril_mutex_lock( & socket->receive_lock );
// push the number of received packet fragments
if( ! ERROR_OCCURRED( dyn_fifo_push( & socket->received, SOCKET_GET_DATA_FRAGMENTS( call ), SOCKET_MAX_RECEIVED_SIZE ))){
// signal the received packet
fibril_condvar_signal( & socket->receive_signal );
}
fibril_mutex_unlock( & socket->receive_lock );
}
fibril_mutex_lock( & socket->receive_lock );
// push the number of received packet fragments
if( ! ERROR_OCCURRED( dyn_fifo_push( & socket->received, SOCKET_GET_DATA_FRAGMENTS( call ), SOCKET_MAX_RECEIVED_SIZE ))){
// signal the received packet
fibril_condvar_signal( & socket->receive_signal );
}
fibril_mutex_unlock( & socket->receive_lock );
fibril_rwlock_read_unlock( & socket_globals.lock );
break;
case NET_SOCKET_ACCEPTED:
fibril_rwlock_read_lock( & socket_globals.lock );
// find the socket
socket = sockets_find( socket_get_sockets(), SOCKET_GET_SOCKET_ID( call ));
if( ! socket ){
ERROR_CODE = ENOTSOCK;
break;
}
// create a new scoket
new_socket = ( socket_ref ) malloc( sizeof( socket_t ));
if( ! new_socket ){
ERROR_CODE = ENOMEM;
break;
}
socket_initialize( new_socket, SOCKET_GET_SOCKET_ID( call ), socket->phone, socket->service );
ERROR_CODE = sockets_add( socket_get_sockets(), new_socket->socket_id, new_socket );
if( ERROR_CODE < 0 ){
free( new_socket );
}else{
// push the new socket identifier
fibril_mutex_lock( & socket->accept_lock );
if( ERROR_OCCURRED( dyn_fifo_push( & socket->accepted, new_socket->socket_id, SOCKET_MAX_ACCEPTED_SIZE ))){
sockets_exclude( socket_get_sockets(), new_socket->socket_id );
free( new_socket );
// create a new scoket
new_socket = ( socket_ref ) malloc( sizeof( socket_t ));
if( ! new_socket ){
ERROR_CODE = ENOMEM;
}else{
// signal the accepted socket
fibril_condvar_signal( & socket->accept_signal );
socket_initialize( new_socket, SOCKET_GET_SOCKET_ID( call ), socket->phone, socket->service );
ERROR_CODE = sockets_add( socket_get_sockets(), new_socket->socket_id, new_socket );
if( ERROR_CODE < 0 ){
free( new_socket );
}else{
// push the new socket identifier
fibril_mutex_lock( & socket->accept_lock );
if( ERROR_OCCURRED( dyn_fifo_push( & socket->accepted, new_socket->socket_id, SOCKET_MAX_ACCEPTED_SIZE ))){
sockets_exclude( socket_get_sockets(), new_socket->socket_id );
free( new_socket );
}else{
// signal the accepted socket
fibril_condvar_signal( & socket->accept_signal );
}
fibril_mutex_unlock( & socket->accept_lock );
ERROR_CODE = EOK;
}
}
fibril_mutex_unlock( & socket->accept_lock );
ERROR_CODE = EOK;
}
fibril_rwlock_read_unlock( & socket_globals.lock );
break;
// TODO obsolete?
case NET_SOCKET_DATA_FRAGMENT_SIZE:
fibril_rwlock_read_lock( & socket_globals.lock );
// find the socket
socket = sockets_find( socket_get_sockets(), SOCKET_GET_SOCKET_ID( call ));
if( ! socket ){
ERROR_CODE = ENOTSOCK;
break;
}else{
fibril_rwlock_write_lock( & socket->sending_lock );
// set the data fragment size
socket->data_fragment_size = SOCKET_GET_DATA_FRAGMENT_SIZE( call );
fibril_rwlock_write_unlock( & socket->sending_lock );
ERROR_CODE = EOK;
}
// set the data fragment size
socket->data_fragment_size = SOCKET_GET_DATA_FRAGMENT_SIZE( call );
ERROR_CODE = EOK;
fibril_rwlock_read_unlock( & socket_globals.lock );
break;
default:
ERROR_CODE = ENOTSUP;
395,7 → 440,7
return ERROR_CODE;
}
// request a new socket
if( ERROR_OCCURRED(( int ) async_req_3_3( phone, NET_SOCKET, 0, 0, service, ( ipcarg_t * ) & socket_id, ( ipcarg_t * ) & socket->header_size, ( ipcarg_t * ) & socket->data_fragment_size ))){
if( ERROR_OCCURRED(( int ) async_req_3_3( phone, NET_SOCKET, 0, 0, service, ( ipcarg_t * ) & socket_id, ( ipcarg_t * ) & socket->data_fragment_size, ( ipcarg_t * ) & socket->header_size ))){
dyn_fifo_destroy( & socket->received );
dyn_fifo_destroy( & socket->accepted );
free( socket );
404,7 → 449,9
// finish the new socket initialization
socket_initialize( socket, socket_id, phone, service );
// store the new socket
fibril_rwlock_write_lock( & socket_globals.lock );
ERROR_CODE = sockets_add( socket_get_sockets(), socket_id, socket );
fibril_rwlock_write_unlock( & socket_globals.lock );
if( ERROR_CODE < 0 ){
dyn_fifo_destroy( & socket->received );
dyn_fifo_destroy( & socket->accepted );
423,13 → 470,19
 
if( ! data ) return EBADMEM;
if( ! datalength ) return NO_DATA;
 
fibril_rwlock_read_lock( & socket_globals.lock );
// find the socket
socket = sockets_find( socket_get_sockets(), socket_id );
if( ! socket ) return ENOTSOCK;
if( ! socket ){
fibril_rwlock_read_unlock( & socket_globals.lock );
return ENOTSOCK;
}
// request the message
message_id = async_send_3( socket->phone, message, ( ipcarg_t ) socket->socket_id, arg2, socket->service, NULL );
// send the address
ipc_data_write_start( socket->phone, data, datalength );
fibril_rwlock_read_unlock( & socket_globals.lock );
async_wait_for( message_id, & result );
return ( int ) result;
}
441,14 → 494,21
}
 
int listen( int socket_id, int backlog ){
socket_ref socket;
socket_ref socket;
int result;
 
if( backlog <= 0 ) return EINVAL;
fibril_rwlock_read_lock( & socket_globals.lock );
// find the socket
socket = sockets_find( socket_get_sockets(), socket_id );
if( ! socket ) return ENOTSOCK;
if( ! socket ){
fibril_rwlock_read_unlock( & socket_globals.lock );
return ENOTSOCK;
}
// request listen backlog change
return ( int ) async_req_3_0( socket->phone, NET_SOCKET_LISTEN, ( ipcarg_t ) socket->socket_id, ( ipcarg_t ) backlog, socket->service );
result = ( int ) async_req_3_0( socket->phone, NET_SOCKET_LISTEN, ( ipcarg_t ) socket->socket_id, ( ipcarg_t ) backlog, socket->service );
fibril_rwlock_read_unlock( & socket_globals.lock );
return result;
}
 
int accept( int socket_id, struct sockaddr * cliaddr, socklen_t * addrlen ){
457,18 → 517,28
int result;
 
if(( ! cliaddr ) || ( ! addrlen )) return EBADMEM;
 
fibril_rwlock_read_lock( & socket_globals.lock );
// find the socket
socket = sockets_find( socket_get_sockets(), socket_id );
if( ! socket ) return ENOTSOCK;
if( ! socket ){
fibril_rwlock_read_unlock( & socket_globals.lock );
return ENOTSOCK;
}
fibril_mutex_lock( & socket->accept_lock );
// wait for an accepted socket
++ socket->blocked;
while( dyn_fifo_value( & socket->accepted ) <= 0 ){
fibril_rwlock_read_unlock( & socket_globals.lock );
fibril_condvar_wait( & socket->accept_signal, & socket->accept_lock );
fibril_rwlock_read_lock( & socket_globals.lock );
}
-- socket->blocked;
// request accept
message_id = async_send_3( socket->phone, NET_SOCKET_ACCEPT, ( ipcarg_t ) socket->socket_id, ( ipcarg_t ) dyn_fifo_value( & socket->accepted ), socket->service, NULL );
// read address
ipc_data_read_start( socket->phone, cliaddr, * addrlen );
fibril_rwlock_read_unlock( & socket_globals.lock );
async_wait_for( message_id, ( ipcarg_t * ) & result );
if( result > 0 ){
// dequeue the accepted apcket if successful
488,12 → 558,21
 
socket_ref socket;
 
fibril_rwlock_write_unlock( & socket_globals.lock );
socket = sockets_find( socket_get_sockets(), socket_id );
if( ! socket ) return ENOTSOCK;
if( ! socket ){
fibril_rwlock_write_unlock( & socket_globals.lock );
return ENOTSOCK;
}
if( socket->blocked ){
fibril_rwlock_write_unlock( & socket_globals.lock );
return EINPROGRESS;
}
// request close
ERROR_PROPAGATE(( int ) async_req_3_0( socket->phone, NET_SOCKET_CLOSE, ( ipcarg_t ) socket->socket_id, 0, socket->service ));
// free the socket structure
socket_destroy( socket );
fibril_rwlock_write_unlock( & socket_globals.lock );
return EOK;
}
 
529,14 → 608,19
 
if( ! data ) return EBADMEM;
if( ! datalength ) return NO_DATA;
fibril_rwlock_read_lock( & socket_globals.lock );
// find socket
socket = sockets_find( socket_get_sockets(), socket_id );
if( ! socket ) return ENOTSOCK;
if( ! socket ){
fibril_rwlock_read_unlock( & socket_globals.lock );
return ENOTSOCK;
}
fibril_rwlock_read_lock( & socket->sending_lock );
// compute data fragment count
fragments = ( datalength + socket->header_size ) / socket->data_fragment_size;
if(( datalength + socket->header_size ) % socket->data_fragment_size ) ++ fragments;
// request send
message_id = async_send_4( socket->phone, message, ( ipcarg_t ) socket->socket_id, fragments, socket->service, ( ipcarg_t ) flags, NULL );
message_id = async_send_5( socket->phone, message, ( ipcarg_t ) socket->socket_id, socket->data_fragment_size, socket->service, ( ipcarg_t ) flags, fragments, NULL );
// send the address if given
if(( ! toaddr ) || ( ipc_data_write_start( socket->phone, toaddr, addrlen ) == EOK )){
if( fragments == 1 ){
555,6 → 639,8
ipc_data_write_start( socket->phone, data, ( datalength + socket->header_size ) % socket->data_fragment_size );
}
}
fibril_rwlock_read_unlock( & socket->sending_lock );
fibril_rwlock_read_unlock( & socket_globals.lock );
async_wait_for( message_id, & result );
return ( int ) result;
}
583,14 → 669,22
if( ! data ) return EBADMEM;
if( ! datalength ) return NO_DATA;
if( fromaddr && ( ! addrlen )) return EINVAL;
fibril_rwlock_read_lock( & socket_globals.lock );
// find the socket
socket = sockets_find( socket_get_sockets(), socket_id );
if( ! socket ) return ENOTSOCK;
if( ! socket ){
fibril_rwlock_read_unlock( & socket_globals.lock );
return ENOTSOCK;
}
fibril_mutex_lock( & socket->receive_lock );
// wait for a received packet
++ socket->blocked;
while(( result = dyn_fifo_value( & socket->received )) <= 0 ){
fibril_rwlock_read_unlock( & socket_globals.lock );
fibril_condvar_wait( & socket->receive_signal, & socket->receive_lock );
fibril_rwlock_read_lock( & socket_globals.lock );
}
-- socket->blocked;
fragments = ( size_t ) result;
// prepare lengths if more fragments
if( fragments > 1 ){
597,6 → 691,7
lengths = ( size_t * ) malloc( sizeof( size_t ) * fragments + sizeof( size_t ));
if( ! lengths ){
fibril_mutex_unlock( & socket->receive_lock );
fibril_rwlock_read_unlock( & socket_globals.lock );
return ENOMEM;
}
// request packet data
635,6 → 730,7
if( fromaddr && addrlen ) * addrlen = SOCKET_GET_ADDRESS_LENGTH( answer );
}
fibril_mutex_unlock( & socket->receive_lock );
fibril_rwlock_read_unlock( & socket_globals.lock );
return result;
}
 
645,9 → 741,13
 
if( !( value && optlen )) return EBADMEM;
if( !( * optlen )) return NO_DATA;
fibril_rwlock_read_lock( & socket_globals.lock );
// find the socket
socket = sockets_find( socket_get_sockets(), socket_id );
if( ! socket ) return ENOTSOCK;
if( ! socket ){
fibril_rwlock_read_unlock( & socket_globals.lock );
return ENOTSOCK;
}
// request option value
message_id = async_send_3( socket->phone, NET_SOCKET_GETSOCKOPT, ( ipcarg_t ) socket->socket_id, ( ipcarg_t ) optname, socket->service, NULL );
// read the length
655,6 → 755,7
// read the value
ipc_data_read_start( socket->phone, value, * optlen );
}
fibril_rwlock_read_unlock( & socket_globals.lock );
async_wait_for( message_id, & result );
return ( int ) result;
}