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-04-02 16:21:39 +08:00
|
|
|
// return: 1: success (seg overlap)
|
|
|
|
|
// return: 0: success
|
|
|
|
|
// return: -1: failed (no space)
|
|
|
|
|
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-02 16:21:39 +08:00
|
|
|
if (assembler->cur_seg_num >= assembler->max_seg_num)
|
2024-03-21 19:27:41 +08:00
|
|
|
{
|
2024-04-02 16:21:39 +08:00
|
|
|
TCP_REASSEMBLY_LOG_ERROR("assembler is full");
|
|
|
|
|
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);
|
|
|
|
|
if (interval_tree_iter_first(&assembler->root, p->node.start, p->node.last))
|
2024-03-21 19:27:41 +08:00
|
|
|
{
|
2024-04-02 16:21:39 +08:00
|
|
|
TCP_REASSEMBLY_LOG_DEBUG("seg overlap");
|
|
|
|
|
ret = 1;
|
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-02 16:21:39 +08:00
|
|
|
struct interval_tree_node *node;
|
2024-03-21 19:27:41 +08:00
|
|
|
|
2024-04-02 16:21:39 +08:00
|
|
|
node = interval_tree_iter_first(&assembler->root, assembler->recv_next, assembler->recv_next);
|
|
|
|
|
if (node == NULL)
|
2024-03-21 19:27:41 +08:00
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
uint64_t overlap = assembler->recv_next - oldest->node.start;
|
|
|
|
|
oldest->seg.len -= overlap;
|
|
|
|
|
oldest->seg.data = (char *)oldest->data + overlap;
|
2024-03-25 17:30:48 +08:00
|
|
|
}
|
|
|
|
|
|
2024-04-02 16:21:39 +08:00
|
|
|
// update recv_next
|
|
|
|
|
assembler->recv_next = oldest->node.last + 1;
|
|
|
|
|
if (assembler->recv_next > UINT32_MAX)
|
2024-03-25 17:30:48 +08:00
|
|
|
{
|
2024-04-02 16:21:39 +08:00
|
|
|
assembler->recv_next = assembler->recv_next % 4294967296;
|
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-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);
|
|
|
|
|
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-02 16:21:39 +08:00
|
|
|
assembler->recv_next += offset;
|
|
|
|
|
if (assembler->recv_next > UINT32_MAX)
|
2024-03-25 17:30:48 +08:00
|
|
|
{
|
2024-04-02 16:21:39 +08:00
|
|
|
assembler->recv_next = assembler->recv_next % 4294967296;
|
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-02 16:21:39 +08:00
|
|
|
assembler->recv_next = 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)
|
|
|
|
|
{
|
|
|
|
|
return assembler->recv_next;
|
|
|
|
|
}
|