2024-03-21 19:27:41 +08:00
# include <string.h>
2024-04-02 16:21:39 +08:00
# include "list.h"
2024-03-21 19:27:41 +08:00
# include "tcp_reassembly.h"
2024-03-27 17:11:38 +08:00
# include "interval_tree.h"
2024-03-21 19:27:41 +08:00
2024-04-02 16:21:39 +08:00
struct tcp_segment_private
2024-03-21 19:27:41 +08:00
{
2024-04-02 16:21:39 +08:00
uint64_t ts ;
2024-03-27 17:11:38 +08:00
uint64_t id ;
2024-04-02 16:21:39 +08:00
struct list_head lru ;
struct interval_tree_node node ;
struct tcp_segment seg ;
void * data ; // flexible array member
2024-03-21 19:27:41 +08:00
} ;
2024-03-25 17:30:48 +08:00
struct tcp_reassembly
{
2024-04-02 16:21:39 +08:00
uint64_t max_timeout ;
uint64_t max_seg_num ;
uint64_t cur_seg_num ;
uint64_t sum_seg_num ;
struct list_head list ;
struct rb_root_cached root ;
uint32_t recv_next ;
2024-03-25 17:30:48 +08:00
} ;
2024-04-02 16:21:39 +08:00
struct tcp_segment * tcp_segment_new ( uint32_t seq , const void * data , uint32_t len )
2024-03-29 16:32:16 +08:00
{
2024-04-02 16:21:39 +08:00
struct tcp_segment_private * p = ( struct tcp_segment_private * ) calloc ( 1 , sizeof ( struct tcp_segment_private ) + len ) ;
if ( ! p )
2024-03-29 16:32:16 +08:00
{
2024-04-02 16:21:39 +08:00
TCP_REASSEMBLY_LOG_ERROR ( " calloc failed " ) ;
2024-03-29 16:32:16 +08:00
return NULL ;
}
2024-03-25 17:30:48 +08:00
2024-04-02 16:21:39 +08:00
p - > node . start = seq ;
p - > node . last = ( uint64_t ) seq + ( uint64_t ) len - 1 ;
p - > data = ( char * ) p + sizeof ( struct tcp_segment_private ) ;
memcpy ( p - > data , data , len ) ;
2024-03-21 19:27:41 +08:00
2024-04-02 16:21:39 +08:00
p - > seg . len = len ;
p - > seg . data = p - > data ;
2024-03-21 19:27:41 +08:00
2024-04-02 16:21:39 +08:00
return & p - > seg ;
2024-03-21 19:27:41 +08:00
}
2024-04-02 16:21:39 +08:00
void tcp_segment_free ( struct tcp_segment * seg )
2024-03-21 19:27:41 +08:00
{
2024-04-02 16:21:39 +08:00
if ( seg )
2024-03-21 19:27:41 +08:00
{
2024-04-02 16:21:39 +08:00
struct tcp_segment_private * p = container_of ( seg , struct tcp_segment_private , seg ) ;
free ( p ) ;
2024-03-21 19:27:41 +08:00
}
}
2024-04-02 16:21:39 +08:00
struct tcp_reassembly * tcp_reassembly_new ( uint64_t max_timeout , uint64_t max_seg_num )
2024-03-21 19:27:41 +08:00
{
2024-04-02 16:21:39 +08:00
struct tcp_reassembly * assembler = ( struct tcp_reassembly * ) calloc ( 1 , sizeof ( struct tcp_reassembly ) ) ;
if ( ! assembler )
2024-03-21 19:27:41 +08:00
{
2024-04-02 16:21:39 +08:00
TCP_REASSEMBLY_LOG_ERROR ( " calloc failed " ) ;
return NULL ;
2024-03-21 19:27:41 +08:00
}
2024-03-25 17:30:48 +08:00
2024-04-02 16:21:39 +08:00
assembler - > max_timeout = max_timeout ;
assembler - > max_seg_num = max_seg_num ;
assembler - > cur_seg_num = 0 ;
assembler - > root = RB_ROOT_CACHED ;
INIT_LIST_HEAD ( & assembler - > list ) ;
return assembler ;
2024-03-21 19:27:41 +08:00
}
2024-04-02 16:21:39 +08:00
void tcp_reassembly_free ( struct tcp_reassembly * assembler )
2024-03-21 19:27:41 +08:00
{
2024-04-02 16:21:39 +08:00
if ( assembler )
2024-03-21 19:27:41 +08:00
{
2024-04-02 16:21:39 +08:00
while ( ! list_empty ( & assembler - > list ) )
2024-03-21 19:27:41 +08:00
{
2024-04-02 16:21:39 +08:00
struct tcp_segment_private * p = list_first_entry ( & assembler - > list , struct tcp_segment_private , lru ) ;
assembler - > cur_seg_num - - ;
list_del ( & p - > lru ) ;
interval_tree_remove ( & p - > node , & assembler - > root ) ;
free ( p ) ;
2024-03-21 19:27:41 +08:00
}
2024-04-02 16:21:39 +08:00
free ( assembler ) ;
2024-03-21 19:27:41 +08:00
}
}
2024-05-06 15:54:16 +08:00
/*
* return : 1 : push tcp segment success ( segment overlap )
* return : 0 : push tcp segment success
* return : - 1 : push tcp segment failed ( no space )
* return : - 2 : push tcp segment failed ( segment repeat )
*/
2024-04-02 16:21:39 +08:00
int tcp_reassembly_push ( struct tcp_reassembly * assembler , struct tcp_segment * seg , uint64_t now )
2024-03-21 19:27:41 +08:00
{
2024-04-17 17:53:42 +08:00
if ( assembler = = NULL )
{
return - 1 ;
}
2024-04-02 16:21:39 +08:00
if ( assembler - > cur_seg_num > = assembler - > max_seg_num )
2024-03-21 19:27:41 +08:00
{
2024-05-06 15:54:16 +08:00
TCP_REASSEMBLY_LOG_ERROR ( " assembler %p is full " , assembler ) ;
2024-04-02 16:21:39 +08:00
return - 1 ;
2024-03-21 19:27:41 +08:00
}
2024-04-02 16:21:39 +08:00
int ret = 0 ;
struct tcp_segment_private * p = container_of ( seg , struct tcp_segment_private , seg ) ;
2024-05-06 15:54:16 +08:00
struct interval_tree_node * node = interval_tree_iter_first ( & assembler - > root , p - > node . start , p - > node . last ) ;
if ( node )
2024-03-21 19:27:41 +08:00
{
2024-05-06 15:54:16 +08:00
do
{
struct tcp_segment_private * t = container_of ( node , struct tcp_segment_private , node ) ;
if ( t - > node . start = = p - > node . start & & t - > node . last = = p - > node . last )
{
TCP_REASSEMBLY_LOG_DEBUG ( " assembler %p push segment %p [%lu, %lu] failed, segment repeat " , assembler , seg , p - > node . start , p - > node . last ) ;
return - 2 ;
}
} while ( ( node = interval_tree_iter_next ( node , p - > node . start , p - > node . last ) ) ) ;
TCP_REASSEMBLY_LOG_DEBUG ( " assembler %p push segment %p [%lu, %lu], but segment overlap " , assembler , seg , p - > node . start , p - > node . last ) ;
2024-04-02 16:21:39 +08:00
ret = 1 ;
2024-03-21 19:27:41 +08:00
}
2024-05-06 15:54:16 +08:00
else
{
TCP_REASSEMBLY_LOG_DEBUG ( " assembler %p push segment %p [%lu, %lu] " , assembler , seg , p - > node . start , p - > node . last ) ;
}
2024-03-21 19:27:41 +08:00
2024-04-02 16:21:39 +08:00
p - > ts = now ;
p - > id = assembler - > sum_seg_num + + ;
list_add_tail ( & p - > lru , & assembler - > list ) ;
interval_tree_insert ( & p - > node , & assembler - > root ) ;
2024-03-21 19:27:41 +08:00
2024-04-02 16:21:39 +08:00
assembler - > cur_seg_num + + ;
2024-03-27 17:11:38 +08:00
2024-04-02 16:21:39 +08:00
return ret ;
2024-03-21 19:27:41 +08:00
}
2024-04-02 16:21:39 +08:00
struct tcp_segment * tcp_reassembly_pop ( struct tcp_reassembly * assembler )
2024-03-21 19:27:41 +08:00
{
2024-04-17 17:53:42 +08:00
if ( assembler = = NULL )
{
return NULL ;
}
2024-03-21 19:27:41 +08:00
2024-04-17 17:53:42 +08:00
struct interval_tree_node * node = interval_tree_iter_first ( & assembler - > root , assembler - > recv_next , assembler - > recv_next ) ;
2024-04-02 16:21:39 +08:00
if ( node = = NULL )
2024-03-21 19:27:41 +08:00
{
return NULL ;
}
2024-05-06 15:54:16 +08:00
uint64_t overlap = 0 ;
2024-04-02 16:21:39 +08:00
uint64_t min_id = UINT64_MAX ;
2024-04-11 19:44:02 +08:00
struct tcp_segment_private * oldest = NULL ;
2024-04-02 16:21:39 +08:00
while ( node )
2024-03-21 19:27:41 +08:00
{
2024-04-02 16:21:39 +08:00
struct tcp_segment_private * p = container_of ( node , struct tcp_segment_private , node ) ;
if ( p - > id < min_id )
2024-03-25 17:30:48 +08:00
{
2024-04-02 16:21:39 +08:00
min_id = p - > id ;
oldest = p ;
2024-03-25 17:30:48 +08:00
}
2024-04-02 16:21:39 +08:00
node = interval_tree_iter_next ( node , assembler - > recv_next , assembler - > recv_next ) ;
2024-03-25 17:30:48 +08:00
}
2024-04-02 16:21:39 +08:00
list_del ( & oldest - > lru ) ;
interval_tree_remove ( & oldest - > node , & assembler - > root ) ;
assembler - > cur_seg_num - - ;
if ( oldest - > node . start < assembler - > recv_next )
2024-03-25 17:30:48 +08:00
{
2024-04-02 16:21:39 +08:00
// trim overlap
2024-05-06 15:54:16 +08:00
overlap = assembler - > recv_next - oldest - > node . start ;
2024-04-02 16:21:39 +08:00
oldest - > seg . len - = overlap ;
oldest - > seg . data = ( char * ) oldest - > data + overlap ;
2024-03-25 17:30:48 +08:00
}
2024-05-06 15:54:16 +08:00
TCP_REASSEMBLY_LOG_DEBUG ( " assembler %p pop segment %p [%lu, %lu], trim overlap %lu " , assembler , & oldest - > seg , oldest - > node . start , oldest - > node . last , overlap ) ;
2024-04-02 16:21:39 +08:00
// update recv_next
2024-05-06 15:54:16 +08:00
assembler - > recv_next = uint32_add ( assembler - > recv_next , oldest - > seg . len ) ;
2024-03-21 19:27:41 +08:00
2024-04-02 16:21:39 +08:00
return & oldest - > seg ;
2024-03-21 19:27:41 +08:00
}
2024-04-02 16:21:39 +08:00
struct tcp_segment * tcp_reassembly_expire ( struct tcp_reassembly * assembler , uint64_t now )
2024-03-21 19:27:41 +08:00
{
2024-04-17 17:53:42 +08:00
if ( assembler = = NULL )
{
return NULL ;
}
2024-04-02 16:21:39 +08:00
if ( list_empty ( & assembler - > list ) )
2024-03-21 19:27:41 +08:00
{
2024-04-02 16:21:39 +08:00
return NULL ;
2024-03-21 19:27:41 +08:00
}
2024-04-02 16:21:39 +08:00
struct tcp_segment_private * p = list_first_entry ( & assembler - > list , struct tcp_segment_private , lru ) ;
if ( now - p - > ts > = assembler - > max_timeout )
2024-03-25 17:30:48 +08:00
{
2024-04-02 16:21:39 +08:00
assembler - > cur_seg_num - - ;
list_del ( & p - > lru ) ;
interval_tree_remove ( & p - > node , & assembler - > root ) ;
2024-05-06 15:54:16 +08:00
TCP_REASSEMBLY_LOG_DEBUG ( " assembler %p expire segment %p [%lu, %lu] " , assembler , & p - > seg , p - > node . start , p - > node . last ) ;
2024-04-02 16:21:39 +08:00
return & p - > seg ;
2024-03-25 17:30:48 +08:00
}
2024-04-02 16:21:39 +08:00
else
2024-03-21 19:27:41 +08:00
{
2024-04-02 16:21:39 +08:00
return NULL ;
2024-03-21 19:27:41 +08:00
}
}
2024-04-02 16:21:39 +08:00
void tcp_reassembly_inc_recv_next ( struct tcp_reassembly * assembler , uint32_t offset )
2024-03-21 19:27:41 +08:00
{
2024-04-17 17:53:42 +08:00
if ( assembler = = NULL )
{
return ;
}
2024-05-06 15:54:16 +08:00
assembler - > recv_next = uint32_add ( assembler - > recv_next , offset ) ;
TCP_REASSEMBLY_LOG_DEBUG ( " assembler %p inc recv_next %u to %lu " , assembler , offset , assembler - > recv_next ) ;
2024-03-25 17:30:48 +08:00
}
2024-04-02 16:21:39 +08:00
void tcp_reassembly_set_recv_next ( struct tcp_reassembly * assembler , uint32_t seq )
2024-03-25 17:30:48 +08:00
{
2024-04-17 17:53:42 +08:00
if ( assembler = = NULL )
{
return ;
}
2024-04-02 16:21:39 +08:00
assembler - > recv_next = seq ;
2024-05-06 15:54:16 +08:00
TCP_REASSEMBLY_LOG_DEBUG ( " assembler %p set recv_next %u " , assembler , seq ) ;
2024-03-25 17:30:48 +08:00
}
2024-04-02 16:21:39 +08:00
uint32_t tcp_reassembly_get_recv_next ( struct tcp_reassembly * assembler )
{
2024-04-17 17:53:42 +08:00
if ( assembler = = NULL )
{
return 0 ;
}
2024-04-02 16:21:39 +08:00
return assembler - > recv_next ;
}