31,10 → 31,16 |
*/ |
|
/** @file |
* TCP module implementation. |
* @see tcp.h |
* \todo |
*/ |
|
#include <assert.h> |
#include <async.h> |
#include <fibril_sync.h> |
#include <malloc.h> |
//TODO remove stdio |
#include <stdio.h> |
|
#include <ipc/ipc.h> |
43,20 → 49,54 |
#include "../../err.h" |
#include "../../messages.h" |
#include "../../modules.h" |
|
#include "../../structures/dynamic_fifo.h" |
#include "../../structures/packet/packet_client.h" |
|
#include "../../include/checksum.h" |
#include "../../include/in.h" |
#include "../../include/in6.h" |
#include "../../include/inet.h" |
#include "../../include/ip_client.h" |
#include "../../include/ip_interface.h" |
#include "../../include/ip_protocols.h" |
#include "../../include/icmp_client.h" |
#include "../../include/icmp_interface.h" |
#include "../../include/net_interface.h" |
#include "../../include/socket_codes.h" |
#include "../../include/socket_errno.h" |
#include "../../include/tcp_codes.h" |
|
#include "../../socket/socket_core.h" |
#include "../../socket/socket_messages.h" |
|
#include "../tl_common.h" |
#include "../tl_messages.h" |
|
#include "tcp.h" |
#include "tcp_header.h" |
#include "tcp_module.h" |
|
/** The TCP window default value. |
*/ |
#define NET_DEFAULT_TCP_WINDOW 10240 |
|
/** \todo |
*/ |
#define NET_DEFAULT_TCP_INITIAL_TIMEOUT 3000000L |
|
/** \todo |
*/ |
#define NET_DEFAULT_TCP_TIME_WAIT_TIMEOUT 2000L |
|
/** The initial outgoing sequence number. |
*/ |
#define TCP_INITIAL_SEQUENCE_NUMBER 2999 |
|
/** Maximum TCP fragment size. |
*/ |
#define MAX_TCP_FRAGMENT_SIZE 65535 |
|
/** Free ports pool start. |
*/ |
#define TCP_FREE_PORTS_START 1025 |
65,17 → 105,119 |
*/ |
#define TCP_FREE_PORTS_END 65535 |
|
/** \todo |
*/ |
#define TCP_SYN_SENT_TIMEOUT 1000000L |
|
/** The maximum number of timeouts in a row before singaling connection lost. |
*/ |
#define TCP_MAX_TIMEOUTS 8 |
|
/** The number of acknowledgements before retransmit. |
*/ |
#define TCP_FAST_RETRANSMIT_COUNT 3 |
|
/** \todo |
*/ |
#define IS_IN_INTERVAL_OVERFLOW( lower, value, higher_equal ) (((( lower ) < ( value )) && ((( value ) <= ( higher_equal )) || (( higher_equal ) < ( lower )))) || ((( value ) <= ( higher_equal )) && (( higher_equal ) < ( lower )))) |
|
/** Type definition of the TCP timeout. |
* @see tcp_timeout |
*/ |
typedef struct tcp_timeout tcp_timeout_t; |
|
/** Type definition of the TCP timeout pointer. |
* @see tcp_timeout |
*/ |
typedef tcp_timeout_t * tcp_timeout_ref; |
|
/** TCP reply timeout data. |
* Used as a timeouting fibril argument. |
* @see tcp_timeout() |
*/ |
struct tcp_timeout{ |
/** TCP global data are going to be read only. |
*/ |
int globals_read_only; |
/** Socket port. |
*/ |
int port; |
/** Local sockets. |
*/ |
socket_cores_ref local_sockets; |
/** Socket identifier. |
*/ |
int socket_id; |
/** Socket state. |
*/ |
tcp_socket_state_t state; |
/** Sent packet sequence number. |
*/ |
int sequence_number; |
/** Timeout in microseconds. |
*/ |
suseconds_t timeout; |
/** Port map key. |
*/ |
char * key; |
/** Port map key length. |
*/ |
size_t key_length; |
}; |
|
/** Releases the packet and returns the result. |
* @param packet The packet queue to be released. Input parameter. |
* @param result The result to be returned. Input parameter. |
* @return The result parameter. |
*/ |
int tcp_release_and_return( packet_t packet, int result ); |
|
int tcp_get_packet_dimension( device_id_t device_id, packet_dimension_ref * packet_dimension ); |
void tcp_prepare_operation_header( socket_core_ref socket, tcp_socket_data_ref socket_data, tcp_header_ref header, int synchronize, int finalize ); |
int tcp_prepare_timeout( int ( * timeout_function )( void * tcp_timeout_t ), socket_core_ref socket, tcp_socket_data_ref socket_data, size_t sequence_number, tcp_socket_state_t state, suseconds_t timeout, int globals_read_only ); |
void tcp_free_socket_data( socket_core_ref socket ); |
int tcp_timeout( void * data ); |
int tcp_release_after_timeout( void * data ); |
int tcp_process_packet( device_id_t device_id, packet_t packet, services_t error ); |
int tcp_connect_core( socket_core_ref socket, socket_cores_ref local_sockets, struct sockaddr * addr, socklen_t addrlen ); |
int tcp_queue_prepare_packet( socket_core_ref socket, tcp_socket_data_ref socket_data, packet_t packet, size_t data_length ); |
int tcp_queue_packet( socket_core_ref socket, tcp_socket_data_ref socket_data, packet_t packet, size_t data_length ); |
packet_t tcp_get_packets_to_send( socket_core_ref socket, tcp_socket_data_ref socket_data ); |
void tcp_send_packets( device_id_t device_id, packet_t packet ); |
void tcp_process_acknowledgement( socket_core_ref socket, tcp_socket_data_ref socket_data, tcp_header_ref header ); |
packet_t tcp_send_prepare_packet( socket_core_ref socket, tcp_socket_data_ref socket_data, packet_t packet, size_t data_length, size_t sequence_number ); |
packet_t tcp_prepare_copy( socket_core_ref socket, tcp_socket_data_ref socket_data, packet_t packet, size_t data_length, size_t sequence_number ); |
void tcp_retransmit_packet( socket_core_ref socket, tcp_socket_data_ref socket_data, size_t sequence_number ); |
int tcp_create_notification_packet( packet_t * packet, socket_core_ref socket, tcp_socket_data_ref socket_data, int synchronize, int finalize ); |
void tcp_refresh_socket_data( tcp_socket_data_ref socket_data ); |
void tcp_initialize_socket_data( tcp_socket_data_ref socket_data ); |
int tcp_process_listen( socket_core_ref listening_socket, tcp_socket_data_ref listening_socket_data, tcp_header_ref header, packet_t packet, struct sockaddr * src, struct sockaddr * dest, size_t addrlen ); |
int tcp_process_syn_sent( socket_core_ref socket, tcp_socket_data_ref socket_data, tcp_header_ref header, packet_t packet ); |
int tcp_process_syn_received( socket_core_ref socket, tcp_socket_data_ref socket_data, tcp_header_ref header, packet_t packet ); |
int tcp_process_established( socket_core_ref socket, tcp_socket_data_ref socket_data, tcp_header_ref header, packet_t packet, int fragments, size_t total_length ); |
int tcp_queue_received_packet( socket_core_ref socket, tcp_socket_data_ref socket_data, packet_t packet, int fragments, size_t total_length ); |
|
int tcp_received_msg( device_id_t device_id, packet_t packet, services_t receiver, services_t error ); |
int tcp_process_client_messages( ipc_callid_t callid, ipc_call_t call ); |
int tcp_listen_message( socket_cores_ref local_sockets, int socket_id, int backlog ); |
int tcp_connect_message( socket_cores_ref local_sockets, int socket_id, struct sockaddr * addr, socklen_t addrlen ); |
int tcp_recvfrom_message( socket_cores_ref local_sockets, int socket_id, int flags, size_t * addrlen ); |
int tcp_send_message( socket_cores_ref local_sockets, int socket_id, int fragments, size_t data_fragment_size, int flags ); |
int tcp_accept_message( socket_cores_ref local_sockets, int socket_id, size_t * addrlen ); |
int tcp_close_message( socket_cores_ref local_sockets, int socket_id ); |
|
DEVICE_MAP_IMPLEMENT( packet_dimensions, packet_dimension_t ); |
|
/** TCP global data. |
*/ |
tcp_globals_t tcp_globals; |
|
int tcp_received_msg( device_id_t device_id, packet_t packet, services_t receiver, services_t error ); |
|
/** Initializes the module. |
*/ |
int tcp_initialize( async_client_conn_t client_connection ){ |
ERROR_DECLARE; |
|
assert( client_connection ); |
fibril_rwlock_initialize( & tcp_globals.lock ); |
fibril_rwlock_write_lock( & tcp_globals.lock ); |
tcp_globals.icmp_phone = icmp_connect_module( SERVICE_ICMP ); |
87,6 → 229,10 |
return tcp_globals.ip_phone; |
} |
ERROR_PROPAGATE( socket_ports_initialize( & tcp_globals.sockets )); |
if( ERROR_OCCURRED( packet_dimensions_initialize( & tcp_globals.dimensions ))){ |
socket_ports_destroy( & tcp_globals.sockets ); |
return ERROR_CODE; |
} |
tcp_globals.last_used_port = TCP_FREE_PORTS_START - 1; |
fibril_rwlock_write_unlock( & tcp_globals.lock ); |
return EOK; |
93,31 → 239,1727 |
} |
|
int tcp_received_msg( device_id_t device_id, packet_t packet, services_t receiver, services_t error ){ |
// TODO received |
// TODO remove debug dump: |
uint8_t * data; |
data = packet_get_data( packet ); |
printf( "Receiving packet:\n\tid\t= %d\n\tlength\t= %d\n\tdata\t= %.2hhX %.2hhX %.2hhX %.2hhX:%.2hhX %.2hhX %.2hhX %.2hhX:%.2hhX %.2hhX %.2hhX %.2hhX:%.2hhX %.2hhX %.2hhX %.2hhX:%.2hhX %.2hhX %.2hhX %.2hhX:%.2hhX %.2hhX\n\t\t%.2hhX %.2hhX:%.2hhX %.2hhX %.2hhX %.2hhX:%.2hhX %.2hhX %.2hhX %.2hhX:%.2hhX %.2hhX %.2hhX %.2hhX:%.2hhX %.2hhX %.2hhX %.2hhX:%.2hhX %.2hhX %.2hhX %.2hhX:%.2hhX %.2hhX %.2hhX %.2hhX:%.2hhX %.2hhX %.2hhX %.2hhX:%.2hhX %.2hhX %.2hhX %.2hhX:%.2hhX %.2hhX %.2hhX %.2hhX\n", packet_get_id( packet ), packet_get_data_length( packet ), data[ 0 ], data[ 1 ], data[ 2 ], data[ 3 ], data[ 4 ], data[ 5 ], data[ 6 ], data[ 7 ], data[ 8 ], data[ 9 ], data[ 10 ], data[ 11 ], data[ 12 ], data[ 13 ], data[ 14 ], data[ 15 ], data[ 16 ], data[ 17 ], data[ 18 ], data[ 19 ], data[ 20 ], data[ 21 ], data[ 22 ], data[ 23 ], data[ 24 ], data[ 25 ], data[ 26 ], data[ 27 ], data[ 28 ], data[ 29 ], data[ 30 ], data[ 31 ], data[ 32 ], data[ 33 ], data[ 34 ], data[ 35 ], data[ 36 ], data[ 37 ], data[ 38 ], data[ 39 ], data[ 40 ], data[ 41 ], data[ 42 ], data[ 43 ], data[ 44 ], data[ 45 ], data[ 46 ], data[ 47 ], data[ 48 ], data[ 49 ], data[ 50 ], data[ 51 ], data[ 52 ], data[ 53 ], data[ 54 ], data[ 55 ], data[ 56 ], data[ 57 ], data[ 58 ], data[ 59 ] ); |
ERROR_DECLARE; |
|
if( receiver != SERVICE_TCP ) return EREFUSED; |
fibril_rwlock_write_lock( & tcp_globals.lock ); |
if( ERROR_OCCURRED( tcp_process_packet( device_id, packet, error ))){ |
fibril_rwlock_write_unlock( & tcp_globals.lock ); |
} |
printf( "receive %d \n", ERROR_CODE ); |
|
return ERROR_CODE; |
} |
|
int tcp_process_packet( device_id_t device_id, packet_t packet, services_t error ){ |
ERROR_DECLARE; |
|
size_t length; |
size_t offset; |
int result; |
tcp_header_ref header; |
socket_core_ref socket; |
tcp_socket_data_ref socket_data; |
packet_t next_packet; |
size_t total_length; |
uint32_t checksum; |
int fragments; |
icmp_type_t type; |
icmp_code_t code; |
struct sockaddr * src; |
struct sockaddr * dest; |
size_t addrlen; |
|
printf( "p1 \n" ); |
if( error ){ |
switch( error ){ |
case SERVICE_ICMP: |
// process error |
result = icmp_client_process_packet( packet, & type, & code, NULL, NULL ); |
if( result < 0 ){ |
return tcp_release_and_return( packet, result ); |
} |
length = ( size_t ) result; |
if( ERROR_OCCURRED( packet_trim( packet, length, 0 ))){ |
return tcp_release_and_return( packet, ERROR_CODE ); |
} |
break; |
default: |
return tcp_release_and_return( packet, ENOTSUP ); |
} |
} |
|
// TODO process received ipopts? |
result = ip_client_process_packet( packet, NULL, NULL, NULL, NULL, NULL ); |
// printf("ip len %d\n", result ); |
if( result < 0 ){ |
return tcp_release_and_return( packet, result ); |
} |
offset = ( size_t ) result; |
|
length = packet_get_data_length( packet ); |
// printf("packet len %d\n", length ); |
if( length <= 0 ){ |
return tcp_release_and_return( packet, EINVAL ); |
} |
if( length < sizeof( tcp_header_t ) + offset ){ |
return tcp_release_and_return( packet, NO_DATA ); |
} |
|
// trim all but TCP header |
if( ERROR_OCCURRED( packet_trim( packet, offset, 0 ))){ |
return tcp_release_and_return( packet, ERROR_CODE ); |
} |
|
// get tcp header |
header = ( tcp_header_ref ) packet_get_data( packet ); |
if( ! header ){ |
return tcp_release_and_return( packet, NO_DATA ); |
} |
// printf( "header len %d, port %d \n", TCP_HEADER_LENGTH( header ), ntohs( header->destination_port )); |
|
result = packet_get_addr( packet, ( uint8_t ** ) & src, ( uint8_t ** ) & dest ); |
if( result <= 0 ){ |
return tcp_release_and_return( packet, result ); |
} |
addrlen = ( size_t ) result; |
|
if( ERROR_OCCURRED( tl_set_address_port( src, addrlen, ntohs( header->source_port )))){ |
return tcp_release_and_return( packet, ERROR_CODE ); |
} |
|
// find the destination socket |
socket = socket_port_find( & tcp_globals.sockets, ntohs( header->destination_port ), ( const char * ) src, addrlen ); |
if( ! socket ){ |
// printf("listening?\n"); |
// find the listening destination socket |
socket = socket_port_find( & tcp_globals.sockets, ntohs( header->destination_port ), SOCKET_MAP_KEY_LISTENING, 0 ); |
if( ! socket ){ |
if( tl_prepare_icmp_packet( tcp_globals.net_phone, tcp_globals.icmp_phone, packet, error ) == EOK ){ |
icmp_destination_unreachable_msg( tcp_globals.icmp_phone, ICMP_PORT_UNREACH, 0, packet ); |
} |
return EADDRNOTAVAIL; |
} |
} |
printf("socket id %d\n", socket->socket_id ); |
socket_data = ( tcp_socket_data_ref ) socket->specific_data; |
assert( socket_data ); |
|
// some data received, clear the timeout counter |
socket_data->timeout_count = 0; |
|
// count the received packet fragments |
next_packet = packet; |
fragments = 0; |
checksum = 0; |
total_length = 0; |
do{ |
++ fragments; |
length = packet_get_data_length( next_packet ); |
if( length <= 0 ){ |
return tcp_release_and_return( packet, NO_DATA ); |
} |
total_length += length; |
// add partial checksum if set |
if( ! error ){ |
checksum = compute_checksum( checksum, packet_get_data( packet ), packet_get_data_length( packet )); |
} |
}while(( next_packet = pq_next( next_packet ))); |
// printf( "fragments %d of %d bytes\n", fragments, total_length ); |
|
// printf("lock?\n"); |
fibril_rwlock_write_lock( socket_data->local_lock ); |
// printf("locked\n"); |
if( ! error ){ |
if( socket_data->state == TCP_SOCKET_LISTEN ){ |
if( socket_data->pseudo_header ){ |
free( socket_data->pseudo_header ); |
socket_data->pseudo_header = NULL; |
socket_data->headerlen = 0; |
} |
if( ERROR_OCCURRED( ip_client_get_pseudo_header( IPPROTO_TCP, src, addrlen, dest, addrlen, total_length, & socket_data->pseudo_header, & socket_data->headerlen ))){ |
fibril_rwlock_write_unlock( socket_data->local_lock ); |
return tcp_release_and_return( packet, ERROR_CODE ); |
} |
}else if( ERROR_OCCURRED( ip_client_set_pseudo_header_data_length( socket_data->pseudo_header, socket_data->headerlen, total_length ))){ |
fibril_rwlock_write_unlock( socket_data->local_lock ); |
return tcp_release_and_return( packet, ERROR_CODE ); |
} |
checksum = compute_checksum( checksum, socket_data->pseudo_header, socket_data->headerlen ); |
if( flip_checksum( compact_checksum( checksum ))){ |
printf( "checksum err %x -> %x\n", header->checksum, flip_checksum( compact_checksum( checksum ))); |
fibril_rwlock_write_unlock( socket_data->local_lock ); |
if( ! ERROR_OCCURRED( tl_prepare_icmp_packet( tcp_globals.net_phone, tcp_globals.icmp_phone, packet, error ))){ |
// checksum error ICMP |
icmp_parameter_problem_msg( tcp_globals.icmp_phone, ICMP_PARAM_POINTER, (( size_t ) (( void * ) & header->checksum )) - (( size_t ) (( void * ) header )), packet ); |
} |
return EINVAL; |
} |
} |
|
fibril_rwlock_read_unlock( & tcp_globals.lock ); |
|
// TODO error reporting/handling |
// printf( "st %d\n", socket_data->state ); |
switch( socket_data->state ){ |
case TCP_SOCKET_LISTEN: |
ERROR_CODE = tcp_process_listen( socket, socket_data, header, packet, src, dest, addrlen ); |
break; |
case TCP_SOCKET_SYN_RECEIVED: |
ERROR_CODE = tcp_process_syn_received( socket, socket_data, header, packet ); |
break; |
case TCP_SOCKET_SYN_SENT: |
ERROR_CODE = tcp_process_syn_sent( socket, socket_data, header, packet ); |
break; |
case TCP_SOCKET_FIN_WAIT_1: |
// ack changing the state to FIN_WAIT_2 gets processed later |
case TCP_SOCKET_FIN_WAIT_2: |
// fin changing state to LAST_ACK gets processed later |
case TCP_SOCKET_LAST_ACK: |
// ack releasing the socket get processed later |
case TCP_SOCKET_CLOSING: |
// ack releasing the socket gets processed later |
case TCP_SOCKET_ESTABLISHED: |
ERROR_CODE = tcp_process_established( socket, socket_data, header, packet, fragments, total_length ); |
break; |
default: |
pq_release( tcp_globals.net_phone, packet_get_id( packet )); |
} |
|
if( ERROR_CODE != EOK ){ |
printf( "process %d\n", ERROR_CODE ); |
fibril_rwlock_write_unlock( socket_data->local_lock ); |
} |
return EOK; |
} |
|
int tcp_process_established( socket_core_ref socket, tcp_socket_data_ref socket_data, tcp_header_ref header, packet_t packet, int fragments, size_t total_length ){ |
ERROR_DECLARE; |
|
packet_t next_packet; |
packet_t tmp_packet; |
uint32_t old_incoming; |
size_t order; |
uint32_t sequence_number; |
size_t length; |
size_t offset; |
uint32_t new_sequence_number; |
|
assert( socket ); |
assert( socket_data ); |
assert( socket->specific_data == socket_data ); |
assert( header ); |
assert( packet ); |
|
new_sequence_number = ntohl( header->sequence_number ); |
old_incoming = socket_data->next_incoming; |
|
if( header->finalize ){ |
socket_data->fin_incoming = new_sequence_number; |
} |
|
// printf( "pe %d < %d <= %d\n", new_sequence_number, socket_data->next_incoming, new_sequence_number + total_length ); |
// trim begining if containing expected data |
if( IS_IN_INTERVAL_OVERFLOW( new_sequence_number, socket_data->next_incoming, new_sequence_number + total_length )){ |
// get the acknowledged offset |
if( socket_data->next_incoming < new_sequence_number ){ |
offset = new_sequence_number - socket_data->next_incoming; |
}else{ |
offset = socket_data->next_incoming - new_sequence_number; |
} |
// printf( "offset %d\n", offset ); |
new_sequence_number += offset; |
total_length -= offset; |
length = packet_get_data_length( packet ); |
// trim the acknowledged data |
while( length <= offset ){ |
// release the acknowledged packets |
next_packet = pq_next( packet ); |
pq_release( tcp_globals.net_phone, packet_get_id( packet )); |
packet = next_packet; |
offset -= length; |
length = packet_get_data_length( packet ); |
} |
if(( offset > 0 ) |
&& ( ERROR_OCCURRED( packet_trim( packet, offset, 0 )))){ |
return tcp_release_and_return( packet, ERROR_CODE ); |
} |
assert( new_sequence_number == socket_data->next_incoming ); |
} |
|
// release if overflowing the window |
// if( IS_IN_INTERVAL_OVERFLOW( socket_data->next_incoming + socket_data->window, new_sequence_number, new_sequence_number + total_length )){ |
// return tcp_release_and_return( packet, EOVERFLOW ); |
// } |
|
/* |
// trim end if overflowing the window |
if( IS_IN_INTERVAL_OVERFLOW( new_sequence_number, socket_data->next_incoming + socket_data->window, new_sequence_number + total_length )){ |
// get the allowed data length |
if( socket_data->next_incoming + socket_data->window < new_sequence_number ){ |
offset = new_sequence_number - socket_data->next_incoming + socket_data->window; |
}else{ |
offset = socket_data->next_incoming + socket_data->window - new_sequence_number; |
} |
next_packet = packet; |
// trim the overflowing data |
while( next_packet && ( offset > 0 )){ |
length = packet_get_data_length( packet ); |
if( length <= offset ){ |
next_packet = pq_next( next_packet ); |
}else if( ERROR_OCCURRED( packet_trim( next_packet, 0, length - offset ))){ |
return tcp_release_and_return( packet, ERROR_CODE ); |
} |
offset -= length; |
total_length -= length - offset; |
} |
// release the overflowing packets |
next_packet = pq_next( next_packet ); |
if( next_packet ){ |
tmp_packet = next_packet; |
next_packet = pq_next( next_packet ); |
pq_insert_after( tmp_packet, next_packet ); |
pq_release( tcp_globals.net_phone, packet_get_id( tmp_packet )); |
} |
assert( new_sequence_number + total_length == socket_data->next_incoming + socket_data->window ); |
} |
*/ |
// the expected one arrived? |
if( new_sequence_number == socket_data->next_incoming ){ |
printf("expected\n"); |
// process acknowledgement |
tcp_process_acknowledgement( socket, socket_data, header ); |
|
// remove the header |
total_length -= TCP_HEADER_LENGTH( header ); |
if( ERROR_OCCURRED( packet_trim( packet, TCP_HEADER_LENGTH( header ), 0 ))){ |
return tcp_release_and_return( packet, ERROR_CODE ); |
} |
|
if( total_length ){ |
ERROR_PROPAGATE( tcp_queue_received_packet( socket, socket_data, packet, fragments, total_length )); |
}else{ |
total_length = 1; |
} |
socket_data->next_incoming = old_incoming + total_length; |
packet = socket_data->incoming; |
while( packet ){ |
if( ERROR_OCCURRED( pq_get_order( socket_data->incoming, & order, NULL ))){ |
// remove the corrupted packet |
next_packet = pq_detach( packet ); |
if( packet == socket_data->incoming ){ |
socket_data->incoming = next_packet; |
} |
pq_release( tcp_globals.net_phone, packet_get_id( packet )); |
packet = next_packet; |
continue; |
} |
sequence_number = ( uint32_t ) order; |
if( IS_IN_INTERVAL_OVERFLOW( sequence_number, old_incoming, socket_data->next_incoming )){ |
// move to the next |
packet = pq_next( packet ); |
// coninual data? |
}else if( IS_IN_INTERVAL_OVERFLOW( old_incoming, sequence_number, socket_data->next_incoming )){ |
// detach the packet |
next_packet = pq_detach( packet ); |
if( packet == socket_data->incoming ){ |
socket_data->incoming = next_packet; |
} |
// get data length |
length = packet_get_data_length( packet ); |
new_sequence_number = sequence_number + length; |
if( length <= 0 ){ |
// remove the empty packet |
pq_release( tcp_globals.net_phone, packet_get_id( packet )); |
packet = next_packet; |
continue; |
} |
// exactly following |
if( sequence_number == socket_data->next_incoming ){ |
// queue received data |
ERROR_PROPAGATE( tcp_queue_received_packet( socket, socket_data, packet, 1, packet_get_data_length( packet ))); |
socket_data->next_incoming = new_sequence_number; |
packet = next_packet; |
continue; |
// at least partly following data? |
}else if( IS_IN_INTERVAL_OVERFLOW( sequence_number, socket_data->next_incoming, new_sequence_number )){ |
if( socket_data->next_incoming < new_sequence_number ){ |
length = new_sequence_number - socket_data->next_incoming; |
}else{ |
length = socket_data->next_incoming - new_sequence_number; |
} |
if( ! ERROR_OCCURRED( packet_trim( packet, length, 0 ))){ |
// queue received data |
ERROR_PROPAGATE( tcp_queue_received_packet( socket, socket_data, packet, 1, packet_get_data_length( packet ))); |
socket_data->next_incoming = new_sequence_number; |
packet = next_packet; |
continue; |
} |
} |
// remove the duplicit or corrupted packet |
pq_release( tcp_globals.net_phone, packet_get_id( packet )); |
packet = next_packet; |
continue; |
}else{ |
break; |
} |
} |
}else if( IS_IN_INTERVAL( socket_data->next_incoming, new_sequence_number, socket_data->next_incoming + socket_data->window )){ |
printf("in window\n"); |
// process acknowledgement |
tcp_process_acknowledgement( socket, socket_data, header ); |
|
// remove the header |
total_length -= TCP_HEADER_LENGTH( header ); |
if( ERROR_OCCURRED( packet_trim( packet, TCP_HEADER_LENGTH( header ), 0 ))){ |
return tcp_release_and_return( packet, ERROR_CODE ); |
} |
|
next_packet = pq_detach( packet ); |
length = packet_get_data_length( packet ); |
tmp_packet = pq_add( socket_data->incoming, packet, new_sequence_number, length ); |
if( ! tmp_packet ){ |
// remove the corrupted packets |
pq_release( tcp_globals.net_phone, packet_get_id( packet )); |
pq_release( tcp_globals.net_phone, packet_get_id( next_packet )); |
}else{ |
socket_data->incoming = tmp_packet; |
while( next_packet ){ |
new_sequence_number += length; |
tmp_packet = pq_detach( next_packet ); |
length = packet_get_data_length( next_packet ); |
if( ERROR_OCCURRED( pq_set_order( next_packet, new_sequence_number, length )) |
|| ERROR_OCCURRED( pq_insert_after( packet, next_packet ))){ |
pq_release( tcp_globals.net_phone, packet_get_id( next_packet )); |
} |
next_packet = tmp_packet; |
} |
} |
}else{ |
printf("unexpected\n"); |
// release duplicite or restricted |
pq_release( tcp_globals.net_phone, packet_get_id( packet )); |
} |
|
// change state according to the acknowledging incoming fin |
if( IS_IN_INTERVAL_OVERFLOW( old_incoming, socket_data->fin_incoming, socket_data->next_incoming )){ |
switch( socket_data->state ){ |
case TCP_SOCKET_FIN_WAIT_1: |
case TCP_SOCKET_FIN_WAIT_2: |
case TCP_SOCKET_CLOSING: |
socket_data->state = TCP_SOCKET_CLOSING; |
break; |
//case TCP_ESTABLISHED: |
default: |
socket_data->state = TCP_SOCKET_CLOSE_WAIT; |
break; |
} |
} |
|
packet = tcp_get_packets_to_send( socket, socket_data ); |
if( ! packet ){ |
// create the notification packet |
ERROR_PROPAGATE( tcp_create_notification_packet( & packet, socket, socket_data, 0, 0 )); |
ERROR_PROPAGATE( tcp_queue_prepare_packet( socket, socket_data, packet, 1 )); |
packet = tcp_send_prepare_packet( socket, socket_data, packet, 1, socket_data->last_outgoing + 1 ); |
} |
fibril_rwlock_write_unlock( socket_data->local_lock ); |
// send the packet |
tcp_send_packets( socket_data->device_id, packet ); |
return EOK; |
} |
|
int tcp_queue_received_packet( socket_core_ref socket, tcp_socket_data_ref socket_data, packet_t packet, int fragments, size_t total_length ){ |
ERROR_DECLARE; |
|
assert( socket ); |
assert( socket_data ); |
assert( socket->specific_data == socket_data ); |
assert( packet ); |
assert( fragments >= 1 ); |
assert( socket_data->window > total_length ); |
|
// queue the received packet |
if( ERROR_OCCURRED( dyn_fifo_push( & socket->received, packet_get_id( packet ), SOCKET_MAX_RECEIVED_SIZE ))){ |
return tcp_release_and_return( packet, ERROR_CODE ); |
} |
|
// decrease the window size |
socket_data->window -= total_length; |
|
// notify the destination socket |
async_msg_5( socket->phone, NET_SOCKET_RECEIVED, ( ipcarg_t ) socket->socket_id, 0, 0, 0, ( ipcarg_t ) fragments ); |
return EOK; |
} |
|
int tcp_process_syn_sent( socket_core_ref socket, tcp_socket_data_ref socket_data, tcp_header_ref header, packet_t packet ){ |
ERROR_DECLARE; |
|
packet_t next_packet; |
|
assert( socket ); |
assert( socket_data ); |
assert( socket->specific_data == socket_data ); |
assert( header ); |
assert( packet ); |
|
if( header->synchronize ){ |
// process acknowledgement |
tcp_process_acknowledgement( socket, socket_data, header ); |
|
socket_data->next_incoming = ntohl( header->sequence_number ) + 1; |
// release additional packets |
next_packet = pq_detach( packet ); |
if( next_packet ){ |
pq_release( tcp_globals.net_phone, packet_get_id( next_packet )); |
} |
// trim if longer than the header |
if(( packet_get_data_length( packet ) > sizeof( * header )) |
&& ERROR_OCCURRED( packet_trim( packet, 0, packet_get_data_length( packet ) - sizeof( * header )))){ |
return tcp_release_and_return( packet, ERROR_CODE ); |
} |
tcp_prepare_operation_header( socket, socket_data, header, 0, 0 ); |
fibril_mutex_lock( & socket_data->operation.mutex ); |
socket_data->operation.result = tcp_queue_packet( socket, socket_data, packet, 1 ); |
if( socket_data->operation.result == EOK ){ |
socket_data->state = TCP_SOCKET_ESTABLISHED; |
packet = tcp_get_packets_to_send( socket, socket_data ); |
if( packet ){ |
fibril_rwlock_write_unlock( socket_data->local_lock ); |
// send the packet |
tcp_send_packets( socket_data->device_id, packet ); |
// signal the result |
fibril_condvar_signal( & socket_data->operation.condvar ); |
fibril_mutex_unlock( & socket_data->operation.mutex ); |
return EOK; |
} |
} |
fibril_mutex_unlock( & socket_data->operation.mutex ); |
} |
return tcp_release_and_return( packet, EINVAL ); |
} |
|
int tcp_process_listen( socket_core_ref listening_socket, tcp_socket_data_ref listening_socket_data, tcp_header_ref header, packet_t packet, struct sockaddr * src, struct sockaddr * dest, size_t addrlen ){ |
ERROR_DECLARE; |
|
packet_t next_packet; |
socket_core_ref socket; |
tcp_socket_data_ref socket_data; |
int socket_id; |
int listening_socket_id = listening_socket->socket_id; |
int listening_port = listening_socket->port; |
|
assert( listening_socket ); |
assert( listening_socket_data ); |
assert( listening_socket->specific_data == listening_socket_data ); |
assert( header ); |
assert( packet ); |
|
// printf( "syn %d\n", header->synchronize ); |
if( header->synchronize ){ |
socket_data = ( tcp_socket_data_ref ) malloc( sizeof( * socket_data )); |
if( ! socket_data ){ |
return tcp_release_and_return( packet, ENOMEM ); |
}else{ |
tcp_initialize_socket_data( socket_data ); |
socket_data->local_lock = listening_socket_data->local_lock; |
socket_data->local_sockets = listening_socket_data->local_sockets; |
socket_data->listening_socket_id = listening_socket->socket_id; |
|
socket_data->next_incoming = ntohl( header->sequence_number ); |
socket_data->treshold = socket_data->next_incoming + ntohs( header->window ); |
|
socket_data->addrlen = addrlen; |
socket_data->addr = malloc( socket_data->addrlen ); |
if( ! socket_data->addr ){ |
free( socket_data ); |
return tcp_release_and_return( packet, ENOMEM ); |
} |
memcpy( socket_data->addr, src, socket_data->addrlen ); |
|
socket_data->dest_port = ntohs( header->source_port ); |
if( ERROR_OCCURRED( tl_set_address_port( socket_data->addr, socket_data->addrlen, socket_data->dest_port ))){ |
free( socket_data->addr ); |
free( socket_data ); |
pq_release( tcp_globals.net_phone, packet_get_id( packet )); |
return ERROR_CODE; |
} |
|
// printf( "addr %p\n", socket_data->addr, socket_data->addrlen ); |
// create a socket |
if( ERROR_OCCURRED( socket_create( socket_data->local_sockets, listening_socket->phone, socket_data, & socket_id ))){ |
free( socket_data->addr ); |
free( socket_data ); |
return tcp_release_and_return( packet, ERROR_CODE ); |
} |
|
printf("new_sock %d\n", socket_id); |
socket_data->pseudo_header = listening_socket_data->pseudo_header; |
socket_data->headerlen = listening_socket_data->headerlen; |
listening_socket_data->pseudo_header = NULL; |
listening_socket_data->headerlen = 0; |
|
fibril_rwlock_write_unlock( socket_data->local_lock ); |
// printf("list lg\n"); |
fibril_rwlock_write_lock( & tcp_globals.lock ); |
// printf("list locked\n"); |
// find the destination socket |
listening_socket = socket_port_find( & tcp_globals.sockets, listening_port, SOCKET_MAP_KEY_LISTENING, 0 ); |
if(( ! listening_socket ) || ( listening_socket->socket_id != listening_socket_id )){ |
fibril_rwlock_write_unlock( & tcp_globals.lock ); |
// a shadow may remain until app hangs up |
return tcp_release_and_return( packet, EOK/*ENOTSOCK*/ ); |
} |
// printf("port %d\n", listening_socket->port ); |
listening_socket_data = ( tcp_socket_data_ref ) listening_socket->specific_data; |
assert( listening_socket_data ); |
|
// printf("list ll\n"); |
fibril_rwlock_write_lock( listening_socket_data->local_lock ); |
// printf("list locked\n"); |
|
socket = socket_cores_find( listening_socket_data->local_sockets, socket_id ); |
if( ! socket ){ |
// where is the socket?!? |
fibril_rwlock_write_unlock( & tcp_globals.lock ); |
return ENOTSOCK; |
} |
socket_data = ( tcp_socket_data_ref ) socket->specific_data; |
assert( socket_data ); |
|
// uint8_t * data = socket_data->addr; |
// printf( "addr %d of %x %x %x %x-%x %x %x %x-%x %x %x %x-%x %x %x %x\n", socket_data->addrlen, data[ 0 ], data[ 1 ], data[ 2 ], data[ 3 ], data[ 4 ], data[ 5 ], data[ 6 ], data[ 7 ], data[ 8 ], data[ 9 ], data[ 10 ], data[ 11 ], data[ 12 ], data[ 13 ], data[ 14 ], data[ 15 ] ); |
|
ERROR_CODE = socket_port_add( & tcp_globals.sockets, listening_port, socket, ( const char * ) socket_data->addr, socket_data->addrlen ); |
assert( socket == socket_port_find( & tcp_globals.sockets, listening_port, ( const char * ) socket_data->addr, socket_data->addrlen )); |
//ERROR_CODE = socket_bind_free_port( & tcp_globals.sockets, socket, TCP_FREE_PORTS_START, TCP_FREE_PORTS_END, tcp_globals.last_used_port ); |
//tcp_globals.last_used_port = socket->port; |
// printf("bound %d\n", socket->port ); |
fibril_rwlock_write_unlock( & tcp_globals.lock ); |
if( ERROR_CODE != EOK ){ |
socket_destroy( tcp_globals.net_phone, socket->socket_id, socket_data->local_sockets, & tcp_globals.sockets, tcp_free_socket_data ); |
return tcp_release_and_return( packet, ERROR_CODE ); |
} |
|
socket_data->state = TCP_SOCKET_LISTEN; |
socket_data->next_incoming = ntohl( header->sequence_number ) + 1; |
// release additional packets |
next_packet = pq_detach( packet ); |
if( next_packet ){ |
pq_release( tcp_globals.net_phone, packet_get_id( next_packet )); |
} |
// trim if longer than the header |
if(( packet_get_data_length( packet ) > sizeof( * header )) |
&& ERROR_OCCURRED( packet_trim( packet, 0, packet_get_data_length( packet ) - sizeof( * header )))){ |
socket_destroy( tcp_globals.net_phone, socket->socket_id, socket_data->local_sockets, & tcp_globals.sockets, tcp_free_socket_data ); |
return tcp_release_and_return( packet, ERROR_CODE ); |
} |
tcp_prepare_operation_header( socket, socket_data, header, 1, 0 ); |
if( ERROR_OCCURRED( tcp_queue_packet( socket, socket_data, packet, 1 ))){ |
socket_destroy( tcp_globals.net_phone, socket->socket_id, socket_data->local_sockets, & tcp_globals.sockets, tcp_free_socket_data ); |
return ERROR_CODE; |
} |
packet = tcp_get_packets_to_send( socket, socket_data ); |
// printf("send %d\n", packet_get_id( packet )); |
if( ! packet ){ |
socket_destroy( tcp_globals.net_phone, socket->socket_id, socket_data->local_sockets, & tcp_globals.sockets, tcp_free_socket_data ); |
return EINVAL; |
}else{ |
socket_data->state = TCP_SOCKET_SYN_RECEIVED; |
// printf("unlock\n"); |
fibril_rwlock_write_unlock( socket_data->local_lock ); |
// send the packet |
tcp_send_packets( socket_data->device_id, packet ); |
return EOK; |
} |
} |
} |
return tcp_release_and_return( packet, EINVAL ); |
} |
|
int tcp_process_syn_received( socket_core_ref socket, tcp_socket_data_ref socket_data, tcp_header_ref header, packet_t packet ){ |
ERROR_DECLARE; |
|
socket_core_ref listening_socket; |
tcp_socket_data_ref listening_socket_data; |
|
assert( socket ); |
assert( socket_data ); |
assert( socket->specific_data == socket_data ); |
assert( header ); |
assert( packet ); |
|
printf("syn_rec\n"); |
if( header->acknowledge ){ |
// process acknowledgement |
tcp_process_acknowledgement( socket, socket_data, header ); |
|
socket_data->next_incoming = ntohl( header->sequence_number );// + 1; |
pq_release( tcp_globals.net_phone, packet_get_id( packet )); |
socket_data->state = TCP_SOCKET_ESTABLISHED; |
listening_socket = socket_cores_find( socket_data->local_sockets, socket_data->listening_socket_id ); |
if( listening_socket ){ |
listening_socket_data = ( tcp_socket_data_ref ) listening_socket->specific_data; |
assert( listening_socket_data ); |
|
// queue the received packet |
if( ! ERROR_OCCURRED( dyn_fifo_push( & listening_socket->accepted, socket->socket_id, listening_socket_data->backlog ))){ |
// notify the destination socket |
async_msg_5( socket->phone, NET_SOCKET_ACCEPTED, ( ipcarg_t ) listening_socket->socket_id, 0, 0, 0, ( ipcarg_t ) socket->socket_id ); |
fibril_rwlock_write_unlock( socket_data->local_lock ); |
return EOK; |
} |
} |
// send FIN |
socket_data->state = TCP_SOCKET_FIN_WAIT_1; |
|
// create the notification packet |
ERROR_PROPAGATE( tcp_create_notification_packet( & packet, socket, socket_data, 0, 1 )); |
|
// send the packet |
ERROR_PROPAGATE( tcp_queue_packet( socket, socket_data, packet, 1 )); |
|
// flush packets |
packet = tcp_get_packets_to_send( socket, socket_data ); |
fibril_rwlock_write_unlock( socket_data->local_lock ); |
if( packet ){ |
// send the packet |
tcp_send_packets( socket_data->device_id, packet ); |
} |
return EOK; |
}else{ |
return tcp_release_and_return( packet, EINVAL ); |
} |
return EINVAL; |
} |
|
void tcp_process_acknowledgement( socket_core_ref socket, tcp_socket_data_ref socket_data, tcp_header_ref header ){ |
size_t number; |
size_t length; |
packet_t packet; |
packet_t next; |
packet_t acknowledged = NULL; |
packet_t first; |
uint32_t old; |
|
assert( socket ); |
assert( socket_data ); |
assert( socket->specific_data == socket_data ); |
assert( header ); |
|
if( header->acknowledge ){ |
number = ntohl( header->acknowledgement_number ); |
// if more data acknowledged |
if( number != socket_data->expected ){ |
old = socket_data->expected; |
if( IS_IN_INTERVAL_OVERFLOW( old, socket_data->fin_outgoing, number )){ |
switch( socket_data->state ){ |
case TCP_SOCKET_FIN_WAIT_1: |
socket_data->state = TCP_SOCKET_FIN_WAIT_2; |
break; |
case TCP_SOCKET_LAST_ACK: |
case TCP_SOCKET_CLOSING: |
// fin acknowledged - release the socket in another fibril |
tcp_prepare_timeout( tcp_release_after_timeout, socket, socket_data, 0, TCP_SOCKET_TIME_WAIT, NET_DEFAULT_TCP_TIME_WAIT_TIMEOUT, true ); |
break; |
default: |
break; |
} |
} |
// update the treshold if higher than set |
if( number + ntohs( header->window ) > socket_data->expected + socket_data->treshold ){ |
socket_data->treshold = number + ntohs( header->window ) - socket_data->expected; |
} |
// set new expected sequence number |
socket_data->expected = number; |
socket_data->expected_count = 1; |
packet = socket_data->outgoing; |
while( pq_get_order( packet, & number, & length ) == EOK ){ |
if( IS_IN_INTERVAL_OVERFLOW(( uint32_t ) old, ( uint32_t )( number + length ), ( uint32_t ) socket_data->expected )){ |
next = pq_detach( packet ); |
if( packet == socket_data->outgoing ){ |
socket_data->outgoing = next; |
} |
// add to acknowledged or release |
first = pq_add( acknowledged, packet, 0, 0 ); |
if( first ){ |
acknowledged = first; |
}else{ |
pq_release( tcp_globals.net_phone, packet_get_id( packet )); |
} |
packet = next; |
}else if( old < socket_data->expected ){ |
break; |
} |
} |
// release acknowledged |
if( acknowledged ){ |
pq_release( tcp_globals.net_phone, packet_get_id( acknowledged )); |
} |
return; |
// if the same as the previous time |
}else if( number == socket_data->expected ){ |
// increase the counter |
++ socket_data->expected_count; |
if( socket_data->expected_count == TCP_FAST_RETRANSMIT_COUNT ){ |
socket_data->expected_count = 1; |
// TODO retransmit lock |
//tcp_retransmit_packet( socket, socket_data, number ); |
} |
} |
} |
} |
|
int tcp_message( ipc_callid_t callid, ipc_call_t * call, ipc_call_t * answer, int * answer_count ){ |
ERROR_DECLARE; |
|
packet_t packet; |
|
assert( call ); |
assert( answer ); |
assert( answer_count ); |
|
* answer_count = 0; |
switch( IPC_GET_METHOD( * call )){ |
case IPC_M_PHONE_HUNGUP: |
return EOK; |
case NET_TL_RECEIVED: |
ERROR_PROPAGATE( packet_translate( tcp_globals.net_phone, & packet, IPC_GET_PACKET( call ))); |
return tcp_received_msg( IPC_GET_DEVICE( call ), packet, SERVICE_TCP, IPC_GET_ERROR( call )); |
//fibril_rwlock_read_lock( & tcp_globals.lock ); |
if( ! ERROR_OCCURRED( packet_translate( tcp_globals.net_phone, & packet, IPC_GET_PACKET( call )))){ |
ERROR_CODE = tcp_received_msg( IPC_GET_DEVICE( call ), packet, SERVICE_TCP, IPC_GET_ERROR( call )); |
} |
//fibril_rwlock_read_unlock( & tcp_globals.lock ); |
return ERROR_CODE; |
case IPC_M_CONNECT_TO_ME: |
return tcp_process_client_messages( callid, * call ); |
} |
return ENOTSUP; |
} |
|
void tcp_refresh_socket_data( tcp_socket_data_ref socket_data ){ |
assert( socket_data ); |
|
bzero( socket_data, sizeof( * socket_data )); |
socket_data->state = TCP_SOCKET_INITIAL; |
socket_data->device_id = -1; |
socket_data->window = NET_DEFAULT_TCP_WINDOW; |
socket_data->treshold = socket_data->window; |
socket_data->last_outgoing = TCP_INITIAL_SEQUENCE_NUMBER; |
socket_data->timeout = NET_DEFAULT_TCP_INITIAL_TIMEOUT; |
socket_data->acknowledged = socket_data->last_outgoing; |
socket_data->next_outgoing = socket_data->last_outgoing + 1; |
socket_data->expected = socket_data->next_outgoing; |
} |
|
void tcp_initialize_socket_data( tcp_socket_data_ref socket_data ){ |
assert( socket_data ); |
|
tcp_refresh_socket_data( socket_data ); |
fibril_mutex_initialize( & socket_data->operation.mutex ); |
fibril_condvar_initialize( & socket_data->operation.condvar ); |
} |
|
int tcp_process_client_messages( ipc_callid_t callid, ipc_call_t call ){ |
int res; |
bool keep_on_going = true; |
socket_cores_t local_sockets; |
int app_phone = IPC_GET_PHONE( & call ); |
struct sockaddr * addr; |
size_t addrlen; |
fibril_rwlock_t lock; |
ipc_call_t answer; |
int answer_count; |
tcp_socket_data_ref socket_data; |
socket_core_ref socket; |
|
/* |
* Accept the connection |
* - Answer the first IPC_M_CONNECT_ME_TO call. |
*/ |
ipc_answer_0( callid, EOK ); |
|
socket_cores_initialize( & local_sockets ); |
fibril_rwlock_initialize( & lock ); |
|
while( keep_on_going ){ |
// refresh data |
refresh_answer( & answer, & answer_count ); |
|
callid = async_get_call( & call ); |
// printf( "message %d\n", IPC_GET_METHOD( * call )); |
|
switch( IPC_GET_METHOD( call )){ |
case IPC_M_PHONE_HUNGUP: |
keep_on_going = false; |
res = EOK; |
break; |
case NET_SOCKET: |
socket_data = ( tcp_socket_data_ref ) malloc( sizeof( * socket_data )); |
if( ! socket_data ){ |
res = ENOMEM; |
}else{ |
tcp_initialize_socket_data( socket_data ); |
socket_data->local_lock = & lock; |
socket_data->local_sockets = & local_sockets; |
fibril_rwlock_write_lock( & lock ); |
res = socket_create( & local_sockets, app_phone, socket_data, SOCKET_SET_SOCKET_ID( answer )); |
fibril_rwlock_write_unlock( & lock ); |
if( res == EOK ){ |
* SOCKET_SET_HEADER_SIZE( answer ) = sizeof( tcp_header_t ); |
* SOCKET_SET_DATA_FRAGMENT_SIZE( answer ) = MAX_TCP_FRAGMENT_SIZE; |
answer_count = 3; |
}else{ |
free( socket_data ); |
} |
} |
break; |
case NET_SOCKET_BIND: |
res = data_receive(( void ** ) & addr, & addrlen ); |
if( res == EOK ){ |
fibril_rwlock_write_lock( & tcp_globals.lock ); |
fibril_rwlock_write_lock( & lock ); |
res = socket_bind( & local_sockets, & tcp_globals.sockets, SOCKET_GET_SOCKET_ID( call ), addr, addrlen, TCP_FREE_PORTS_START, TCP_FREE_PORTS_END, tcp_globals.last_used_port ); |
if( res == EOK ){ |
socket = socket_cores_find( & local_sockets, SOCKET_GET_SOCKET_ID( call )); |
if( socket ){ |
socket_data = ( tcp_socket_data_ref ) socket->specific_data; |
assert( socket_data ); |
socket_data->state = TCP_SOCKET_LISTEN; |
} |
} |
fibril_rwlock_write_unlock( & lock ); |
fibril_rwlock_write_unlock( & tcp_globals.lock ); |
free( addr ); |
} |
break; |
case NET_SOCKET_LISTEN: |
fibril_rwlock_read_lock( & tcp_globals.lock ); |
// fibril_rwlock_write_lock( & tcp_globals.lock ); |
fibril_rwlock_write_lock( & lock ); |
res = tcp_listen_message( & local_sockets, SOCKET_GET_SOCKET_ID( call ), SOCKET_GET_BACKLOG( call )); |
fibril_rwlock_write_unlock( & lock ); |
// fibril_rwlock_write_unlock( & tcp_globals.lock ); |
fibril_rwlock_read_unlock( & tcp_globals.lock ); |
break; |
case NET_SOCKET_CONNECT: |
res = data_receive(( void ** ) & addr, & addrlen ); |
if( res == EOK ){ |
// the global lock may released in the tcp_connect_message() function |
fibril_rwlock_write_lock( & tcp_globals.lock ); |
fibril_rwlock_write_lock( & lock ); |
res = tcp_connect_message( & local_sockets, SOCKET_GET_SOCKET_ID( call ), addr, addrlen ); |
if( res != EOK ){ |
fibril_rwlock_write_unlock( & lock ); |
fibril_rwlock_write_unlock( & tcp_globals.lock ); |
free( addr ); |
} |
} |
break; |
case NET_SOCKET_ACCEPT: |
fibril_rwlock_read_lock( & tcp_globals.lock ); |
fibril_rwlock_write_lock( & lock ); |
res = tcp_accept_message( & local_sockets, SOCKET_GET_SOCKET_ID( call ), & addrlen ); |
fibril_rwlock_write_unlock( & lock ); |
fibril_rwlock_read_unlock( & tcp_globals.lock ); |
if( res > 0 ){ |
* SOCKET_SET_SOCKET_ID( answer ) = res; |
* SOCKET_SET_ADDRESS_LENGTH( answer ) = addrlen; |
answer_count = 2; |
} |
break; |
case NET_SOCKET_SEND: |
fibril_rwlock_read_lock( & tcp_globals.lock ); |
fibril_rwlock_write_lock( & lock ); |
res = tcp_send_message( & local_sockets, SOCKET_GET_SOCKET_ID( call ), SOCKET_GET_DATA_FRAGMENTS( call ), SOCKET_GET_DATA_FRAGMENT_SIZE( call ), SOCKET_GET_FLAGS( call )); |
if( res != EOK ){ |
fibril_rwlock_write_unlock( & lock ); |
fibril_rwlock_read_unlock( & tcp_globals.lock ); |
} |
break; |
case NET_SOCKET_SENDTO: |
res = data_receive(( void ** ) & addr, & addrlen ); |
if( res == EOK ){ |
fibril_rwlock_read_lock( & tcp_globals.lock ); |
fibril_rwlock_write_lock( & lock ); |
res = tcp_send_message( & local_sockets, SOCKET_GET_SOCKET_ID( call ), SOCKET_GET_DATA_FRAGMENTS( call ), SOCKET_GET_DATA_FRAGMENT_SIZE( call ), SOCKET_GET_FLAGS( call )); |
if( res != EOK ){ |
fibril_rwlock_write_unlock( & lock ); |
fibril_rwlock_read_unlock( & tcp_globals.lock ); |
} |
free( addr ); |
} |
break; |
case NET_SOCKET_RECV: |
fibril_rwlock_read_lock( & tcp_globals.lock ); |
fibril_rwlock_write_lock( & lock ); |
res = tcp_recvfrom_message( & local_sockets, SOCKET_GET_SOCKET_ID( call ), SOCKET_GET_FLAGS( call ), NULL ); |
fibril_rwlock_write_unlock( & lock ); |
fibril_rwlock_read_unlock( & tcp_globals.lock ); |
if( res > 0 ){ |
* SOCKET_SET_READ_DATA_LENGTH( answer ) = res; |
answer_count = 1; |
res = EOK; |
} |
break; |
case NET_SOCKET_RECVFROM: |
fibril_rwlock_read_lock( & tcp_globals.lock ); |
fibril_rwlock_write_lock( & lock ); |
res = tcp_recvfrom_message( & local_sockets, SOCKET_GET_SOCKET_ID( call ), SOCKET_GET_FLAGS( call ), & addrlen ); |
fibril_rwlock_write_unlock( & lock ); |
fibril_rwlock_read_unlock( & tcp_globals.lock ); |
if( res > 0 ){ |
* SOCKET_SET_READ_DATA_LENGTH( answer ) = res; |
* SOCKET_SET_ADDRESS_LENGTH( answer ) = addrlen; |
answer_count = 2; |
res = EOK; |
} |
break; |
case NET_SOCKET_CLOSE: |
fibril_rwlock_write_lock( & tcp_globals.lock ); |
fibril_rwlock_write_lock( & lock ); |
res = tcp_close_message( & local_sockets, SOCKET_GET_SOCKET_ID( call )); |
if( res != EOK ){ |
fibril_rwlock_write_unlock( & lock ); |
fibril_rwlock_write_unlock( & tcp_globals.lock ); |
} |
break; |
case NET_SOCKET_GETSOCKOPT: |
case NET_SOCKET_SETSOCKOPT: |
default: |
res = ENOTSUP; |
break; |
} |
|
// printf( "res = %d\n", res ); |
|
answer_call( callid, res, & answer, answer_count ); |
} |
|
printf("release\n"); |
// release all local sockets |
socket_cores_release( tcp_globals.net_phone, & local_sockets, & tcp_globals.sockets, tcp_free_socket_data ); |
|
return EOK; |
} |
|
int tcp_timeout( void * data ){ |
tcp_timeout_ref timeout = data; |
int keep_write_lock = false; |
socket_core_ref socket; |
tcp_socket_data_ref socket_data; |
|
assert( timeout ); |
|
// sleep the given timeout |
async_usleep( timeout->timeout ); |
// lock the globals |
if( timeout->globals_read_only ){ |
fibril_rwlock_read_lock( & tcp_globals.lock ); |
}else{ |
fibril_rwlock_write_lock( & tcp_globals.lock ); |
} |
// find the pending operation socket |
socket = socket_port_find( & tcp_globals.sockets, timeout->port, timeout->key, timeout->key_length ); |
if( socket && ( socket->socket_id == timeout->socket_id )){ |
socket_data = ( tcp_socket_data_ref ) socket->specific_data; |
assert( socket_data ); |
if( socket_data->local_sockets == timeout->local_sockets ){ |
fibril_rwlock_write_lock( socket_data->local_lock ); |
if( timeout->sequence_number ){ |
// increase the timeout counter; |
++ socket_data->timeout_count; |
if( socket_data->timeout_count == TCP_MAX_TIMEOUTS ){ |
// TODO release as connection lost |
//tcp_refresh_socket_data( socket_data ); |
} |
// retransmit |
// TODO enable retransmit |
//tcp_retransmit_packet( socket, socket_data, timeout->sequence_number ); |
fibril_rwlock_write_unlock( socket_data->local_lock ); |
}else{ |
fibril_mutex_lock( & socket_data->operation.mutex ); |
// set the timeout operation result if state not changed |
if( socket_data->state == timeout->state ){ |
socket_data->operation.result = ETIMEOUT; |
// notify the main fibril |
fibril_condvar_signal( & socket_data->operation.condvar ); |
// keep the global write lock |
keep_write_lock = true; |
}else{ |
// operation is ok, do nothing |
// unlocking from now on, so the unlock order does not matter... |
fibril_rwlock_write_unlock( socket_data->local_lock ); |
} |
fibril_mutex_unlock( & socket_data->operation.mutex ); |
} |
} |
} |
// unlock only if no socket |
if( timeout->globals_read_only ){ |
fibril_rwlock_read_unlock( & tcp_globals.lock ); |
}else if( ! keep_write_lock ){ |
// release if not desired |
fibril_rwlock_write_unlock( & tcp_globals.lock ); |
} |
// release the timeout structure |
free( timeout ); |
return EOK; |
} |
|
int tcp_release_after_timeout( void * data ){ |
tcp_timeout_ref timeout = data; |
socket_core_ref socket; |
tcp_socket_data_ref socket_data; |
fibril_rwlock_t * local_lock; |
|
assert( timeout ); |
|
// sleep the given timeout |
async_usleep( timeout->timeout ); |
// lock the globals |
fibril_rwlock_write_lock( & tcp_globals.lock ); |
// find the pending operation socket |
socket = socket_port_find( & tcp_globals.sockets, timeout->port, timeout->key, timeout->key_length ); |
if( socket && ( socket->socket_id == timeout->socket_id )){ |
socket_data = ( tcp_socket_data_ref ) socket->specific_data; |
assert( socket_data ); |
if( socket_data->local_sockets == timeout->local_sockets ){ |
local_lock = socket_data->local_lock; |
fibril_rwlock_write_lock( local_lock ); |
socket_destroy( tcp_globals.net_phone, timeout->socket_id, timeout->local_sockets, & tcp_globals.sockets, tcp_free_socket_data ); |
fibril_rwlock_write_unlock( local_lock ); |
} |
} |
// unlock the globals |
fibril_rwlock_write_unlock( & tcp_globals.lock ); |
// release the timeout structure |
free( timeout ); |
return EOK; |
} |
|
void tcp_retransmit_packet( socket_core_ref socket, tcp_socket_data_ref socket_data, size_t sequence_number ){ |
packet_t packet; |
packet_t copy; |
size_t data_length; |
|
assert( socket ); |
assert( socket_data ); |
assert( socket->specific_data == socket_data ); |
|
// sent packet? |
packet = pq_find( socket_data->outgoing, sequence_number ); |
printf("retransmit %d\n", packet_get_id( packet )); |
if( packet ){ |
pq_get_order( packet, NULL, & data_length ); |
copy = tcp_prepare_copy( socket, socket_data, packet, data_length, sequence_number ); |
fibril_rwlock_write_unlock( socket_data->local_lock ); |
// printf( "r send %d\n", packet_get_id( packet )); |
if( copy ){ |
tcp_send_packets( socket_data->device_id, copy ); |
} |
}else{ |
fibril_rwlock_write_unlock( socket_data->local_lock ); |
} |
} |
|
int tcp_listen_message( socket_cores_ref local_sockets, int socket_id, int backlog ){ |
socket_core_ref socket; |
tcp_socket_data_ref socket_data; |
|
assert( local_sockets ); |
|
if( backlog < 0 ) return EINVAL; |
// find the socket |
socket = socket_cores_find( local_sockets, socket_id ); |
if( ! socket ) return ENOTSOCK; |
// get the socket specific data |
socket_data = ( tcp_socket_data_ref ) socket->specific_data; |
assert( socket_data ); |
// set the backlog |
socket_data->backlog = backlog; |
return EOK; |
} |
|
int tcp_connect_message( socket_cores_ref local_sockets, int socket_id, struct sockaddr * addr, socklen_t addrlen ){ |
ERROR_DECLARE; |
|
socket_core_ref socket; |
|
assert( local_sockets ); |
assert( addr ); |
assert( addrlen > 0 ); |
|
// find the socket |
socket = socket_cores_find( local_sockets, socket_id ); |
if( ! socket ) return ENOTSOCK; |
if( ERROR_OCCURRED( tcp_connect_core( socket, local_sockets, addr, addrlen ))){ |
tcp_free_socket_data( socket ); |
// unbind if bound |
if( socket->port > 0 ){ |
socket_ports_exclude( & tcp_globals.sockets, socket->port ); |
socket->port = 0; |
} |
} |
return ERROR_CODE; |
} |
|
int tcp_connect_core( socket_core_ref socket, socket_cores_ref local_sockets, struct sockaddr * addr, socklen_t addrlen ){ |
ERROR_DECLARE; |
|
tcp_socket_data_ref socket_data; |
packet_t packet; |
|
assert( socket ); |
assert( addr ); |
assert( addrlen > 0 ); |
|
// get the socket specific data |
socket_data = ( tcp_socket_data_ref ) socket->specific_data; |
assert( socket_data ); |
assert( socket->specific_data == socket_data ); |
if(( socket_data->state != TCP_SOCKET_INITIAL ) |
&& (( socket_data->state != TCP_SOCKET_LISTEN ) || ( socket->port <= 0 ))){ |
return EINVAL; |
} |
// get the destination port |
ERROR_PROPAGATE( tl_get_address_port( addr, addrlen, & socket_data->dest_port )); |
if( socket->port <= 0 ){ |
// try to find a free port |
ERROR_PROPAGATE( socket_bind_free_port( & tcp_globals.sockets, socket, TCP_FREE_PORTS_START, TCP_FREE_PORTS_END, tcp_globals.last_used_port )); |
// set the next port as the search starting port number |
tcp_globals.last_used_port = socket->port; |
} |
ERROR_PROPAGATE( ip_get_route_req( tcp_globals.ip_phone, IPPROTO_TCP, addr, addrlen, & socket_data->device_id, & socket_data->pseudo_header, & socket_data->headerlen )); |
|
// create the notification packet |
ERROR_PROPAGATE( tcp_create_notification_packet( & packet, socket, socket_data, 1, 0 )); |
|
// unlock the globals and wait for an operation |
fibril_rwlock_write_unlock( & tcp_globals.lock ); |
|
socket_data->addr = addr; |
socket_data->addrlen = addrlen; |
// send the packet |
if( ERROR_OCCURRED( tcp_queue_packet( socket, socket_data, packet, 1 )) |
|| ERROR_OCCURRED( tcp_prepare_timeout( tcp_timeout, socket, socket_data, 0, TCP_SOCKET_INITIAL, NET_DEFAULT_TCP_INITIAL_TIMEOUT, false ))){ |
socket_data->addr = NULL; |
socket_data->addrlen = 0; |
fibril_rwlock_write_lock( & tcp_globals.lock ); |
}else{ |
packet = tcp_get_packets_to_send( socket, socket_data ); |
if( packet ){ |
fibril_mutex_lock( & socket_data->operation.mutex ); |
fibril_rwlock_write_unlock( socket_data->local_lock ); |
// send the packet |
printf( "connecting %d\n", packet_get_id( packet )); |
tcp_send_packets( socket_data->device_id, packet ); |
// wait for a reply |
fibril_condvar_wait( & socket_data->operation.condvar, & socket_data->operation.mutex ); |
ERROR_CODE = socket_data->operation.result; |
if( ERROR_CODE != EOK ){ |
socket_data->addr = NULL; |
socket_data->addrlen = 0; |
} |
}else{ |
socket_data->addr = NULL; |
socket_data->addrlen = 0; |
ERROR_CODE = EINTR; |
} |
} |
|
fibril_mutex_unlock( & socket_data->operation.mutex ); |
|
// return the result |
return ERROR_CODE; |
} |
|
int tcp_queue_prepare_packet( socket_core_ref socket, tcp_socket_data_ref socket_data, packet_t packet, size_t data_length ){ |
ERROR_DECLARE; |
|
tcp_header_ref header; |
|
assert( socket ); |
assert( socket_data ); |
assert( socket->specific_data == socket_data ); |
|
// get tcp header |
header = ( tcp_header_ref ) packet_get_data( packet ); |
if( ! header ) return NO_DATA; |
header->destination_port = htons( socket_data->dest_port ); |
header->source_port = htons( socket->port ); |
header->sequence_number = htonl( socket_data->next_outgoing ); |
if( ERROR_OCCURRED( packet_set_addr( packet, NULL, ( uint8_t * ) socket_data->addr, socket_data->addrlen ))){ |
return tcp_release_and_return( packet, EINVAL ); |
} |
// remember the outgoing FIN |
if( header->finalize ){ |
socket_data->fin_outgoing = socket_data->next_outgoing; |
} |
return EOK; |
} |
|
int tcp_queue_packet( socket_core_ref socket, tcp_socket_data_ref socket_data, packet_t packet, size_t data_length ){ |
ERROR_DECLARE; |
packet_t first; |
|
assert( socket ); |
assert( socket_data ); |
assert( socket->specific_data == socket_data ); |
|
ERROR_PROPAGATE( tcp_queue_prepare_packet( socket, socket_data, packet, data_length )); |
|
first = pq_add( socket_data->outgoing, packet, socket_data->next_outgoing, data_length ); |
if( ! first ){ |
return tcp_release_and_return( packet, EINVAL ); |
} |
socket_data->outgoing = first; |
socket_data->next_outgoing += data_length; |
return EOK; |
} |
|
packet_t tcp_get_packets_to_send( socket_core_ref socket, tcp_socket_data_ref socket_data ){ |
ERROR_DECLARE; |
|
packet_t packet; |
packet_t copy; |
packet_t sending = NULL; |
packet_t previous = NULL; |
size_t data_length; |
|
assert( socket ); |
assert( socket_data ); |
assert( socket->specific_data == socket_data ); |
|
packet = pq_find( socket_data->outgoing, socket_data->last_outgoing + 1 ); |
while( packet ){ |
pq_get_order( packet, NULL, & data_length ); |
// send only if fits into the window |
// respecting the possible overflow |
if( IS_IN_INTERVAL_OVERFLOW(( uint32_t ) socket_data->last_outgoing, ( uint32_t )( socket_data->last_outgoing + data_length ), ( uint32_t )( socket_data->expected + socket_data->treshold ))){ |
copy = tcp_prepare_copy( socket, socket_data, packet, data_length, socket_data->last_outgoing + 1 ); |
if( ! copy ){ |
return sending; |
} |
if( ! sending ){ |
sending = copy; |
}else{ |
if( ERROR_OCCURRED( pq_insert_after( previous, copy ))){ |
pq_release( tcp_globals.net_phone, packet_get_id( copy )); |
return sending; |
} |
} |
previous = copy; |
packet = pq_next( packet ); |
// overflow occurred ? |
if(( ! packet ) && ( socket_data->last_outgoing > socket_data->next_outgoing )){ |
printf("gpts overflow\n"); |
// continue from the beginning |
packet = socket_data->outgoing; |
} |
socket_data->last_outgoing += data_length; |
}else{ |
break; |
} |
} |
return sending; |
} |
|
packet_t tcp_send_prepare_packet( socket_core_ref socket, tcp_socket_data_ref socket_data, packet_t packet, size_t data_length, size_t sequence_number ){ |
ERROR_DECLARE; |
|
tcp_header_ref header; |
uint32_t checksum; |
|
assert( socket ); |
assert( socket_data ); |
assert( socket->specific_data == socket_data ); |
|
// adjust the pseudo header |
if( ERROR_OCCURRED( ip_client_set_pseudo_header_data_length( socket_data->pseudo_header, socket_data->headerlen, packet_get_data_length( packet )))){ |
pq_release( tcp_globals.net_phone, packet_get_id( packet )); |
return NULL; |
} |
|
// get the header |
header = ( tcp_header_ref ) packet_get_data( packet ); |
if( ! header ){ |
pq_release( tcp_globals.net_phone, packet_get_id( packet )); |
return NULL; |
} |
assert( ntohl( header->sequence_number ) == sequence_number ); |
|
// adjust the header |
if( socket_data->next_incoming ){ |
header->acknowledgement_number = htonl( socket_data->next_incoming ); |
header->acknowledge = 1; |
} |
header->window = htons( socket_data->window ); |
|
// checksum |
header->checksum = 0; |
checksum = compute_checksum( 0, socket_data->pseudo_header, socket_data->headerlen ); |
checksum = compute_checksum( checksum, ( uint8_t * ) packet_get_data( packet ), packet_get_data_length( packet )); |
header->checksum = htons( flip_checksum( compact_checksum( checksum ))); |
// prepare the packet |
if( ERROR_OCCURRED( ip_client_prepare_packet( packet, IPPROTO_TCP, 0, 0, 0, 0 )) |
// prepare the timeout |
|| ERROR_OCCURRED( tcp_prepare_timeout( tcp_timeout, socket, socket_data, sequence_number, socket_data->state, socket_data->timeout, true ))){ |
pq_release( tcp_globals.net_phone, packet_get_id( packet )); |
return NULL; |
} |
return packet; |
} |
|
packet_t tcp_prepare_copy( socket_core_ref socket, tcp_socket_data_ref socket_data, packet_t packet, size_t data_length, size_t sequence_number ){ |
packet_t copy; |
|
assert( socket ); |
assert( socket_data ); |
assert( socket->specific_data == socket_data ); |
|
// make a copy of the packet |
copy = packet_get_copy( tcp_globals.net_phone, packet ); |
if( ! copy ) return NULL; |
|
return tcp_send_prepare_packet( socket, socket_data, copy, data_length, sequence_number ); |
} |
|
void tcp_send_packets( device_id_t device_id, packet_t packet ){ |
packet_t next; |
|
while( packet ){ |
next = pq_detach( packet ); |
ip_send_msg( tcp_globals.ip_phone, device_id, packet, SERVICE_TCP, 0 ); |
packet = next; |
} |
} |
|
int tcp_get_packet_dimension( device_id_t device_id, packet_dimension_ref * packet_dimension ){ |
ERROR_DECLARE; |
|
assert( packet_dimension ); |
|
* packet_dimension = packet_dimensions_find( & tcp_globals.dimensions, device_id ); |
if( ! * packet_dimension ){ |
// ask for and remember them if not found |
* packet_dimension = malloc( sizeof( ** packet_dimension )); |
if( ! * packet_dimension ) return ENOMEM; |
if( ERROR_OCCURRED( ip_packet_size_req( tcp_globals.ip_phone, device_id, & ( ** packet_dimension ).addr_len, & ( ** packet_dimension ).prefix, & ( ** packet_dimension ).content, & ( ** packet_dimension ).suffix ))){ |
free( * packet_dimension ); |
return ERROR_CODE; |
} |
ERROR_CODE = packet_dimensions_add( & tcp_globals.dimensions, device_id, * packet_dimension ); |
if( ERROR_CODE < 0 ){ |
free( * packet_dimension ); |
return ERROR_CODE; |
} |
} |
return EOK; |
} |
|
void tcp_prepare_operation_header( socket_core_ref socket, tcp_socket_data_ref socket_data, tcp_header_ref header, int synchronize, int finalize ){ |
assert( socket ); |
assert( socket_data ); |
assert( socket->specific_data == socket_data ); |
assert( header ); |
|
bzero( header, sizeof( * header )); |
header->source_port = htons( socket->port ); |
header->source_port = htons( socket_data->dest_port ); |
header->header_length = TCP_COMPUTE_HEADER_LENGTH( sizeof( * header )); |
header->synchronize = synchronize; |
header->finalize = finalize; |
} |
|
int tcp_prepare_timeout( int ( * timeout_function )( void * tcp_timeout_t ), socket_core_ref socket, tcp_socket_data_ref socket_data, size_t sequence_number, tcp_socket_state_t state, suseconds_t timeout, int globals_read_only ){ |
tcp_timeout_ref operation_timeout; |
fid_t fibril; |
|
assert( socket ); |
assert( socket_data ); |
assert( socket->specific_data == socket_data ); |
|
// prepare the timeout with key bundle structure |
operation_timeout = malloc( sizeof( * operation_timeout ) + socket->key_length + 1 ); |
if( ! operation_timeout ) return ENOMEM; |
bzero( operation_timeout, sizeof( * operation_timeout )); |
operation_timeout->globals_read_only = globals_read_only; |
operation_timeout->port = socket->port; |
operation_timeout->local_sockets = socket_data->local_sockets; |
operation_timeout->socket_id = socket->socket_id; |
operation_timeout->timeout = timeout; |
operation_timeout->sequence_number = sequence_number; |
operation_timeout->state = state; |
|
// copy the key |
operation_timeout->key = (( char * ) operation_timeout ) + sizeof( * operation_timeout ); |
operation_timeout->key_length = socket->key_length; |
memcpy( operation_timeout->key, socket->key, socket->key_length ); |
operation_timeout->key[ operation_timeout->key_length ] = '\0'; |
|
// prepare the timeouting thread |
fibril = fibril_create( timeout_function, operation_timeout ); |
if( ! fibril ){ |
free( operation_timeout ); |
return EPARTY; |
} |
// fibril_mutex_lock( & socket_data->operation.mutex ); |
// start the timeouting fibril |
fibril_add_ready( fibril ); |
//socket_data->state = state; |
return EOK; |
} |
|
int tcp_recvfrom_message( socket_cores_ref local_sockets, int socket_id, int flags, size_t * addrlen ){ |
ERROR_DECLARE; |
|
socket_core_ref socket; |
tcp_socket_data_ref socket_data; |
int packet_id; |
packet_t packet; |
size_t length; |
|
assert( local_sockets ); |
|
// find the socket |
socket = socket_cores_find( local_sockets, socket_id ); |
if( ! socket ) return ENOTSOCK; |
// get the socket specific data |
if( ! socket->specific_data ) return NO_DATA; |
socket_data = ( tcp_socket_data_ref ) socket->specific_data; |
|
// check state |
if(( socket_data->state != TCP_SOCKET_ESTABLISHED ) && ( socket_data->state != TCP_SOCKET_CLOSE_WAIT )){ |
return ENOTCONN; |
} |
|
// send the source address if desired |
if( addrlen ){ |
ERROR_PROPAGATE( data_reply( socket_data->addr, socket_data->addrlen )); |
* addrlen = socket_data->addrlen; |
} |
|
// get the next received packet |
packet_id = dyn_fifo_value( & socket->received ); |
if( packet_id < 0 ) return NO_DATA; |
ERROR_PROPAGATE( packet_translate( tcp_globals.net_phone, & packet, packet_id )); |
|
// reply the packets |
ERROR_PROPAGATE( socket_reply_packets( packet, & length )); |
|
// release the packet |
dyn_fifo_pop( & socket->received ); |
pq_release( tcp_globals.net_phone, packet_get_id( packet )); |
// return the total length |
return ( int ) length; |
} |
|
int tcp_send_message( socket_cores_ref local_sockets, int socket_id, int fragments, size_t data_fragment_size, int flags ){ |
ERROR_DECLARE; |
|
socket_core_ref socket; |
tcp_socket_data_ref socket_data; |
packet_dimension_ref packet_dimension; |
packet_t packet; |
size_t total_length; |
tcp_header_ref header; |
int index; |
int result; |
|
assert( local_sockets ); |
|
// find the socket |
socket = socket_cores_find( local_sockets, socket_id ); |
if( ! socket ) return ENOTSOCK; |
// get the socket specific data |
if( ! socket->specific_data ) return NO_DATA; |
socket_data = ( tcp_socket_data_ref ) socket->specific_data; |
|
// check state |
if(( socket_data->state != TCP_SOCKET_ESTABLISHED ) && ( socket_data->state != TCP_SOCKET_CLOSE_WAIT )){ |
return ENOTCONN; |
} |
|
ERROR_PROPAGATE( tcp_get_packet_dimension( socket_data->device_id, & packet_dimension )); |
|
// TODO return the device_id + data_fragment_size if different - the client should send it again |
// ( two messages are better than ip fragmentation ) |
|
for( index = 0; index < fragments; ++ index ){ |
// read the data fragment |
result = tl_socket_read_packet_data( tcp_globals.net_phone, & packet, sizeof( tcp_header_t ), packet_dimension, socket_data->addr, socket_data->addrlen ); |
if( result < 0 ) return result; |
total_length = ( size_t ) result; |
// prefix the tcp header |
header = PACKET_PREFIX( packet, tcp_header_t ); |
if( ! header ){ |
return tcp_release_and_return( packet, ENOMEM ); |
} |
tcp_prepare_operation_header( socket, socket_data, header, 0, 0 ); |
ERROR_PROPAGATE( tcp_queue_packet( socket, socket_data, packet, 0 )); |
} |
|
// flush packets |
packet = tcp_get_packets_to_send( socket, socket_data ); |
fibril_rwlock_write_unlock( socket_data->local_lock ); |
fibril_rwlock_read_unlock( & tcp_globals.lock ); |
if( packet ){ |
// send the packet |
tcp_send_packets( socket_data->device_id, packet ); |
} |
|
return EOK; |
} |
|
int tcp_close_message( socket_cores_ref local_sockets, int socket_id ){ |
ERROR_DECLARE; |
|
socket_core_ref socket; |
tcp_socket_data_ref socket_data; |
packet_t packet; |
|
// find the socket |
socket = socket_cores_find( local_sockets, socket_id ); |
if( ! socket ) return ENOTSOCK; |
// get the socket specific data |
socket_data = ( tcp_socket_data_ref ) socket->specific_data; |
assert( socket_data ); |
|
// check state |
switch( socket_data->state ){ |
case TCP_SOCKET_ESTABLISHED: |
socket_data->state = TCP_SOCKET_FIN_WAIT_1; |
break; |
case TCP_SOCKET_CLOSE_WAIT: |
socket_data->state = TCP_SOCKET_LAST_ACK; |
break; |
// case TCP_SOCKET_LISTEN: |
default: |
// just destroy |
if( ! ERROR_OCCURRED( socket_destroy( tcp_globals.net_phone, socket_id, local_sockets, & tcp_globals.sockets, tcp_free_socket_data ))){ |
fibril_rwlock_write_unlock( socket_data->local_lock ); |
fibril_rwlock_write_unlock( & tcp_globals.lock ); |
} |
return ERROR_CODE; |
} |
// send FIN |
// TODO should I wait to complete? |
|
// create the notification packet |
ERROR_PROPAGATE( tcp_create_notification_packet( & packet, socket, socket_data, 0, 1 )); |
|
// send the packet |
ERROR_PROPAGATE( tcp_queue_packet( socket, socket_data, packet, 1 )); |
|
// flush packets |
packet = tcp_get_packets_to_send( socket, socket_data ); |
fibril_rwlock_write_unlock( socket_data->local_lock ); |
fibril_rwlock_write_unlock( & tcp_globals.lock ); |
if( packet ){ |
// send the packet |
tcp_send_packets( socket_data->device_id, packet ); |
} |
return EOK; |
} |
|
int tcp_create_notification_packet( packet_t * packet, socket_core_ref socket, tcp_socket_data_ref socket_data, int synchronize, int finalize ){ |
ERROR_DECLARE; |
|
packet_dimension_ref packet_dimension; |
tcp_header_ref header; |
|
assert( packet ); |
|
// get the device packet dimension |
ERROR_PROPAGATE( tcp_get_packet_dimension( socket_data->device_id, & packet_dimension )); |
// get a new packet |
* packet = packet_get_4( tcp_globals.net_phone, sizeof( tcp_header_t ), packet_dimension->addr_len, packet_dimension->prefix, packet_dimension->suffix ); |
if( ! * packet ) return ENOMEM; |
// allocate space in the packet |
header = PACKET_SUFFIX( * packet, tcp_header_t ); |
if( ! header ){ |
tcp_release_and_return( * packet, ENOMEM ); |
} |
|
tcp_prepare_operation_header( socket, socket_data, header, synchronize, finalize ); |
return EOK; |
} |
|
int tcp_accept_message( socket_cores_ref local_sockets, int socket_id, size_t * addrlen ){ |
ERROR_DECLARE; |
|
socket_core_ref accepted; |
socket_core_ref socket; |
tcp_socket_data_ref socket_data; |
|
assert( local_sockets ); |
assert( addrlen ); |
|
// find the socket |
socket = socket_cores_find( local_sockets, socket_id ); |
if( ! socket ) return ENOTSOCK; |
// get the socket specific data |
socket_data = ( tcp_socket_data_ref ) socket->specific_data; |
assert( socket_data ); |
|
// check state |
if( socket_data->state != TCP_SOCKET_LISTEN ){ |
return EINVAL; |
} |
|
do{ |
socket_id = dyn_fifo_value( & socket->accepted ); |
if( socket_id < 0 ) return ENOTSOCK; |
|
accepted = socket_cores_find( local_sockets, socket_id ); |
if( ! accepted ) return ENOTSOCK; |
// get the socket specific data |
socket_data = ( tcp_socket_data_ref ) accepted->specific_data; |
assert( socket_data ); |
if( socket_data->state == TCP_SOCKET_ESTABLISHED ){ |
ERROR_PROPAGATE( data_reply( socket_data->addr, socket_data->addrlen )); |
* addrlen = socket_data->addrlen; |
} |
dyn_fifo_pop( & socket->accepted ); |
}while( socket_data->state != TCP_SOCKET_ESTABLISHED ); |
printf("ret accept %d\n", accepted->socket_id ); |
return accepted->socket_id; |
} |
|
void tcp_free_socket_data( socket_core_ref socket ){ |
tcp_socket_data_ref socket_data; |
|
assert( socket ); |
|
printf( "destroy_socket %d\n", socket->socket_id ); |
|
// get the socket specific data |
socket_data = ( tcp_socket_data_ref ) socket->specific_data; |
assert( socket_data ); |
//free the pseudo header |
if( socket_data->pseudo_header ){ |
if( socket_data->headerlen ){ |
printf("d pseudo\n"); |
free( socket_data->pseudo_header ); |
socket_data->headerlen = 0; |
} |
socket_data->pseudo_header = NULL; |
} |
socket_data->headerlen = 0; |
// free the address |
if( socket_data->addr ){ |
if( socket_data->addrlen ){ |
printf("d addr\n"); |
free( socket_data->addr ); |
socket_data->addrlen = 0; |
} |
socket_data->addr = NULL; |
} |
socket_data->addrlen = 0; |
} |
|
int tcp_release_and_return( packet_t packet, int result ){ |
pq_release( tcp_globals.net_phone, packet_get_id( packet )); |
return result; |
} |
|
/** @} |
*/ |