Add TCP reassembly metrics on session

This commit is contained in:
luwenpeng
2024-04-03 18:59:46 +08:00
parent e8e60cee6d
commit 151b6f8f1d
3 changed files with 279 additions and 269 deletions

View File

@@ -181,19 +181,28 @@ void *session_get_user_data(const struct session *sess)
struct tcp_segment *session_get_tcp_segment(struct session *sess) struct tcp_segment *session_get_tcp_segment(struct session *sess)
{ {
struct tcp_pcb *pcb = &sess->tcp_pcb; struct tcp_half *half = NULL;
if (pcb->order_seg.data != NULL && pcb->order_seg.len > 0)
{
return &pcb->order_seg;
}
if (session_get_cur_dir(sess) == SESSION_DIR_C2S) if (session_get_cur_dir(sess) == SESSION_DIR_C2S)
{ {
return tcp_reassembly_pop(pcb->c2s_assembler); half = &sess->tcp_pcb.c2s;
} }
else else
{ {
return tcp_reassembly_pop(pcb->s2c_assembler); half = &sess->tcp_pcb.s2c;
}
if (half->order.data != NULL && half->order.len > 0)
{
return &half->order;
}
else
{
struct tcp_segment *seg = tcp_reassembly_pop(half->assembler);
if (seg)
{
half->nr_seg_reorded++;
}
return seg;
} }
} }
@@ -204,117 +213,29 @@ void session_free_tcp_segment(struct session *sess, struct tcp_segment *seg)
return; return;
} }
if (seg == &sess->tcp_pcb.order_seg) struct tcp_half *half = NULL;
if (session_get_cur_dir(sess) == SESSION_DIR_C2S)
{ {
sess->tcp_pcb.order_seg.data = NULL; half = &sess->tcp_pcb.c2s;
sess->tcp_pcb.order_seg.len = 0; }
else
{
half = &sess->tcp_pcb.s2c;
}
if (seg == &half->order)
{
half->order.data = NULL;
half->order.len = 0;
return; return;
} }
else else
{ {
half->nr_seg_released++;
tcp_segment_free(seg); tcp_segment_free(seg);
} }
} }
/******************************************************************************
* to string
******************************************************************************/
const char *closing_reason_to_str(enum closing_reason reason)
{
switch (reason)
{
case CLOSING_BY_TIMEOUT:
return "closing by timeout";
case CLOSING_BY_EVICTED:
return "closing by evicted";
case CLOSING_BY_CLIENT_FIN:
return "closing by client FIN";
case CLOSING_BY_CLIENT_RST:
return "closing by client RST";
case CLOSING_BY_SERVER_FIN:
return "closing by server FIN";
case CLOSING_BY_SERVER_RST:
return "closing by server RST";
default:
return "unknown";
}
}
const char *session_state_to_str(enum session_state state)
{
switch (state)
{
case SESSION_STATE_INIT:
return "init";
case SESSION_STATE_OPENING:
return "opening";
case SESSION_STATE_ACTIVE:
return "active";
case SESSION_STATE_CLOSING:
return "closing";
case SESSION_STATE_DISCARD:
return "discard";
case SESSION_STATE_CLOSED:
return "closed";
default:
return "unknown";
}
}
const char *session_type_to_str(enum session_type type)
{
switch (type)
{
case SESSION_TYPE_TCP:
return "TCP";
case SESSION_TYPE_UDP:
return "UDP";
default:
return "unknown";
}
}
const char *session_dir_to_str(enum session_dir dir)
{
switch (dir)
{
case SESSION_DIR_C2S:
return "C2S";
case SESSION_DIR_S2C:
return "S2C";
default:
return "unknown";
}
}
void session_dump(struct session *sess)
{
char buffer[1024] = {0};
tuple6_to_str(session_get_tuple(sess), buffer, sizeof(buffer));
printf("session id : %" PRIu64 "\n", session_get_id(sess));
printf("session tuple : %s\n", buffer);
printf("session tuple dir : %s\n", session_dir_to_str(session_get_tuple_dir(sess)));
printf("session state : %s\n", session_state_to_str(session_get_state(sess)));
printf("session type : %s\n", session_type_to_str(session_get_type(sess)));
printf("session dup traffic : %d\n", session_has_dup_traffic(sess));
printf("session closing reason : %s\n", closing_reason_to_str(session_get_closing_reason(sess)));
printf("session C2S packets : %" PRIu64 "\n", session_get_metric(sess, SESSION_METRIC_C2S_PACKETS));
printf("session C2S bytes : %" PRIu64 "\n", session_get_metric(sess, SESSION_METRIC_C2S_BYTES));
printf("session S2C packets : %" PRIu64 "\n", session_get_metric(sess, SESSION_METRIC_S2C_PACKETS));
printf("session S2C bytes : %" PRIu64 "\n", session_get_metric(sess, SESSION_METRIC_S2C_BYTES));
printf("session new time : %" PRIu64 "\n", session_get_timestamp(sess, SESSION_TIMESTAMP_NEW));
printf("session last time : %" PRIu64 "\n", session_get_timestamp(sess, SESSION_TIMESTAMP_LAST));
printf("session current packet ptr : %p\n", (void *)session_get_packet(sess, SESSION_PACKET_CURRENT));
printf("session current packet dir : %s\n", session_dir_to_str(session_get_cur_dir(sess)));
printf("session ex data: \n");
for (uint8_t i = 0; i < g_ex_manager.count; i++)
{
printf(" ex_idx: %d, ex_key: %s, ex_data: %p\n", i, g_ex_manager.schemas[i].key, sess->ex_data[i]);
}
}
/****************************************************************************** /******************************************************************************
* session ex data * session ex data
******************************************************************************/ ******************************************************************************/
@@ -416,3 +337,141 @@ void session_free_all_ex_data(struct session *sess)
} }
} }
} }
/******************************************************************************
* to string
******************************************************************************/
const char *closing_reason_to_str(enum closing_reason reason)
{
switch (reason)
{
case CLOSING_BY_TIMEOUT:
return "closing by timeout";
case CLOSING_BY_EVICTED:
return "closing by evicted";
case CLOSING_BY_CLIENT_FIN:
return "closing by client FIN";
case CLOSING_BY_CLIENT_RST:
return "closing by client RST";
case CLOSING_BY_SERVER_FIN:
return "closing by server FIN";
case CLOSING_BY_SERVER_RST:
return "closing by server RST";
default:
return "unknown";
}
}
const char *session_state_to_str(enum session_state state)
{
switch (state)
{
case SESSION_STATE_INIT:
return "init";
case SESSION_STATE_OPENING:
return "opening";
case SESSION_STATE_ACTIVE:
return "active";
case SESSION_STATE_CLOSING:
return "closing";
case SESSION_STATE_DISCARD:
return "discard";
case SESSION_STATE_CLOSED:
return "closed";
default:
return "unknown";
}
}
const char *session_type_to_str(enum session_type type)
{
switch (type)
{
case SESSION_TYPE_TCP:
return "TCP";
case SESSION_TYPE_UDP:
return "UDP";
default:
return "unknown";
}
}
const char *session_dir_to_str(enum session_dir dir)
{
switch (dir)
{
case SESSION_DIR_C2S:
return "C2S";
case SESSION_DIR_S2C:
return "S2C";
default:
return "unknown";
}
}
void tcp_half_dump(const struct tcp_half *half)
{
char buffer[32] = {0};
int used = 0;
if (half->flags & TH_SYN)
{
used += snprintf(buffer + used, sizeof(buffer) - used, "SYN ");
}
if (half->flags & TH_ACK)
{
used += snprintf(buffer + used, sizeof(buffer) - used, "ACK ");
}
if (half->flags & TH_FIN)
{
used += snprintf(buffer + used, sizeof(buffer) - used, "FIN ");
}
if (half->flags & TH_RST)
{
used += snprintf(buffer + used, sizeof(buffer) - used, "RST ");
}
printf(" flags : %s\n", buffer);
printf(" nr_seg_received : %lu\n", half->nr_seg_received);
printf(" nr_seg_expired : %lu\n", half->nr_seg_expired);
printf(" nr_seg_overlap : %lu\n", half->nr_seg_overlap);
printf(" nr_seg_no_space : %lu\n", half->nr_seg_no_space);
printf(" nr_seg_inorder : %lu\n", half->nr_seg_inorder);
printf(" nr_seg_reorded : %lu\n", half->nr_seg_reorded);
printf(" nr_seg_buffered : %lu\n", half->nr_seg_buffered);
printf(" nr_seg_released : %lu\n", half->nr_seg_released);
}
void session_dump(struct session *sess)
{
char buffer[1024] = {0};
tuple6_to_str(session_get_tuple(sess), buffer, sizeof(buffer));
printf("session id : %" PRIu64 "\n", session_get_id(sess));
printf("session tuple : %s\n", buffer);
printf("session tuple dir : %s\n", session_dir_to_str(session_get_tuple_dir(sess)));
printf("session state : %s\n", session_state_to_str(session_get_state(sess)));
printf("session type : %s\n", session_type_to_str(session_get_type(sess)));
printf("session dup traffic : %d\n", session_has_dup_traffic(sess));
printf("session closing reason : %s\n", closing_reason_to_str(session_get_closing_reason(sess)));
printf("session C2S packets : %" PRIu64 "\n", session_get_metric(sess, SESSION_METRIC_C2S_PACKETS));
printf("session C2S bytes : %" PRIu64 "\n", session_get_metric(sess, SESSION_METRIC_C2S_BYTES));
printf("session S2C packets : %" PRIu64 "\n", session_get_metric(sess, SESSION_METRIC_S2C_PACKETS));
printf("session S2C bytes : %" PRIu64 "\n", session_get_metric(sess, SESSION_METRIC_S2C_BYTES));
printf("session new time : %" PRIu64 "\n", session_get_timestamp(sess, SESSION_TIMESTAMP_NEW));
printf("session last time : %" PRIu64 "\n", session_get_timestamp(sess, SESSION_TIMESTAMP_LAST));
printf("session current packet ptr : %p\n", (void *)session_get_packet(sess, SESSION_PACKET_CURRENT));
printf("session current packet dir : %s\n", session_dir_to_str(session_get_cur_dir(sess)));
printf("session ex data:\n");
for (uint8_t i = 0; i < g_ex_manager.count; i++)
{
printf(" ex_idx: %d, ex_key: %s, ex_data: %p\n", i, g_ex_manager.schemas[i].key, sess->ex_data[i]);
}
if (session_get_type(sess) == SESSION_TYPE_TCP)
{
printf("session TCP C2S:\n");
tcp_half_dump(&sess->tcp_pcb.c2s);
printf("session TCP S2C:\n");
tcp_half_dump(&sess->tcp_pcb.s2c);
}
}

View File

@@ -75,35 +75,32 @@ enum session_packet_index
MAX_PACKETS, MAX_PACKETS,
}; };
enum tcp_state struct tcp_half
{ {
TCP_SYN_RCVD = 1 << 0, struct tcp_reassembly *assembler;
TCP_SYN_ACK_RCVD = 1 << 1, struct tcp_segment order;
TCP_C2S_FIN_RCVD = 1 << 2, uint64_t nr_seg_received;
TCP_S2C_FIN_RCVD = 1 << 3, uint64_t nr_seg_expired;
uint64_t nr_seg_overlap; //(retransmission)
uint64_t nr_seg_no_space;
TCP_C2S_RST_RCVD = 1 << 4, uint64_t nr_seg_inorder;
TCP_S2C_RST_RCVD = 1 << 5, uint64_t nr_seg_reorded;
TCP_C2S_UNVERIFIED_RST_RCVD = 1 << 6, uint64_t nr_seg_buffered;
TCP_S2C_UNVERIFIED_RST_RCVD = 1 << 7, uint64_t nr_seg_released;
uint32_t seq;
uint32_t ack;
uint8_t flags;
}; };
// the TCP protocol control block // the TCP protocol control block
struct tcp_pcb struct tcp_pcb
{ {
struct tcp_reassembly *c2s_assembler; struct tcp_half c2s;
struct tcp_reassembly *s2c_assembler; struct tcp_half s2c;
struct tcp_segment order_seg; // order segment from raw packet
uint16_t sub_state; // tcp sub state
uint32_t c2s_seq;
uint32_t s2c_seq;
uint32_t c2s_ack;
uint32_t s2c_ack;
}; };
struct session struct session
@@ -181,16 +178,6 @@ void *session_get_user_data(const struct session *sess);
struct tcp_segment *session_get_tcp_segment(struct session *sess); struct tcp_segment *session_get_tcp_segment(struct session *sess);
void session_free_tcp_segment(struct session *sess, struct tcp_segment *seg); void session_free_tcp_segment(struct session *sess, struct tcp_segment *seg);
/******************************************************************************
* to string
******************************************************************************/
const char *closing_reason_to_str(enum closing_reason reason);
const char *session_state_to_str(enum session_state state);
const char *session_type_to_str(enum session_type type);
const char *session_dir_to_str(enum session_dir dir);
void session_dump(struct session *sess);
/****************************************************************************** /******************************************************************************
* session ex data * session ex data
******************************************************************************/ ******************************************************************************/
@@ -222,6 +209,16 @@ void *session_get0_ex_data(const struct session *sess, uint8_t idx);
void session_free_ex_data(struct session *sess, uint8_t idx); void session_free_ex_data(struct session *sess, uint8_t idx);
void session_free_all_ex_data(struct session *sess); void session_free_all_ex_data(struct session *sess);
/******************************************************************************
* to string
******************************************************************************/
const char *closing_reason_to_str(enum closing_reason reason);
const char *session_state_to_str(enum session_state state);
const char *session_type_to_str(enum session_type type);
const char *session_dir_to_str(enum session_dir dir);
void session_dump(struct session *sess);
#ifdef __cpluscplus #ifdef __cpluscplus
} }
#endif #endif

View File

@@ -15,25 +15,7 @@
struct session_manager struct session_manager
{ {
// max session number struct session_manager_options opts;
uint64_t max_tcp_session_num;
uint64_t max_udp_session_num;
// session overload
uint8_t tcp_overload_evict_old_sess; // 1: evict old session, 0: bypass new session
uint8_t udp_overload_evict_old_sess; // 1: evict old session, 0: bypass new session
// TCP timeout
uint64_t tcp_init_timeout; // range: [1, 60000]
uint64_t tcp_handshake_timeout; // range: [1, 60000]
uint64_t tcp_data_timeout; // range: [1, 15999999000]
uint64_t tcp_half_closed_timeout; // range: [1, 604800000]
uint64_t tcp_time_wait_timeout; // range: [1, 600000]
uint64_t tcp_discard_timeout; // range: [1, 15999999000]
uint64_t tcp_unverified_rst_timeout; // range: [1, 600000]
// UDP timeout
uint64_t udp_data_timeout; // range: [1, 15999999000]
// TCP reassembly
uint32_t tcp_reassembly_max_timeout; // range: [1, 60000] (ms)
uint32_t tcp_reassembly_max_segments; // range: [2, 32]
struct session_pool *sess_pool; struct session_pool *sess_pool;
struct session_table *tcp_sess_table; struct session_table *tcp_sess_table;
@@ -58,6 +40,16 @@ int check_options(const struct session_manager_options *opts)
return -1; return -1;
} }
if (opts->max_tcp_session_num < EVICTE_SESSION_BURST * 2)
{
SESSION_LOG_ERROR("invalid max_tcp_session_num: %lu, supported range: [%u, %lu]", opts->max_tcp_session_num, EVICTE_SESSION_BURST * 2, UINT64_MAX);
return -1;
}
if (opts->max_udp_session_num < EVICTE_SESSION_BURST * 2)
{
SESSION_LOG_ERROR("invalid max_udp_session_num: %lu, supported range: [%u, %lu]", opts->max_udp_session_num, EVICTE_SESSION_BURST * 2, UINT64_MAX);
return -1;
}
if (opts->tcp_init_timeout < 1 || opts->tcp_init_timeout > 60000) if (opts->tcp_init_timeout < 1 || opts->tcp_init_timeout > 60000)
{ {
SESSION_LOG_ERROR("invalid tcp_init_timeout: %lu, supported range: [1, 60000]", opts->tcp_init_timeout); SESSION_LOG_ERROR("invalid tcp_init_timeout: %lu, supported range: [1, 60000]", opts->tcp_init_timeout);
@@ -116,16 +108,16 @@ static void tcp_pcb_clean(struct tcp_pcb *pcb)
{ {
if (pcb) if (pcb)
{ {
tcp_reassembly_free(pcb->c2s_assembler); tcp_reassembly_free(pcb->c2s.assembler);
tcp_reassembly_free(pcb->s2c_assembler); tcp_reassembly_free(pcb->s2c.assembler);
} }
} }
static int tcp_pcb_init(struct tcp_pcb *pcb, uint64_t max_timeout, uint64_t max_seg_num) static int tcp_pcb_init(struct tcp_pcb *pcb, uint64_t max_timeout, uint64_t max_seg_num)
{ {
pcb->c2s_assembler = tcp_reassembly_new(max_timeout, max_seg_num); pcb->c2s.assembler = tcp_reassembly_new(max_timeout, max_seg_num);
pcb->s2c_assembler = tcp_reassembly_new(max_timeout, max_seg_num); pcb->s2c.assembler = tcp_reassembly_new(max_timeout, max_seg_num);
if (pcb->c2s_assembler == NULL || pcb->s2c_assembler == NULL) if (pcb->c2s.assembler == NULL || pcb->s2c.assembler == NULL)
{ {
tcp_pcb_clean(pcb); tcp_pcb_clean(pcb);
return -1; return -1;
@@ -134,100 +126,69 @@ static int tcp_pcb_init(struct tcp_pcb *pcb, uint64_t max_timeout, uint64_t max_
return 0; return 0;
} }
static void tcp_pcb_update(struct tcp_pcb *pcb, enum session_dir dir, const struct pkt_layer *tcp_layer, uint64_t now) static void tcp_half_update(struct tcp_half *half, const struct pkt_layer *tcp_layer, uint64_t now)
{ {
struct tcp_segment *seg; struct tcp_segment *seg;
struct tcp_reassembly *assembler;
struct tcphdr *hdr = (struct tcphdr *)tcp_layer->hdr_ptr; struct tcphdr *hdr = (struct tcphdr *)tcp_layer->hdr_ptr;
uint32_t seq = tcp_hdr_get_seq(hdr);
uint32_t ack = tcp_hdr_get_ack(hdr);
uint8_t flags = tcp_hdr_get_flags(hdr); uint8_t flags = tcp_hdr_get_flags(hdr);
uint32_t rcv_nxt;
/* half->flags |= flags;
* https://www.rfc-editor.org/rfc/rfc5961#section-3.2 half->seq = tcp_hdr_get_seq(hdr);
* half->ack = tcp_hdr_get_ack(hdr);
* If the RST bit is set and the sequence number exactly matches the
* next expected sequence number (RCV.NXT), then TCP MUST reset the
* connection.
*
* if fin is received, the expected sequence number should be increased by 1
*/
uint16_t expect = 0;
if (dir == SESSION_DIR_C2S)
{
pcb->c2s_seq = seq;
pcb->c2s_ack = ack;
assembler = pcb->c2s_assembler;
expect = pcb->s2c_ack;
expect += pcb->sub_state & TCP_S2C_FIN_RCVD ? 1 : 0;
pcb->sub_state |= (flags & TH_SYN) ? TCP_SYN_RCVD : 0;
pcb->sub_state |= (flags & TH_FIN) ? TCP_C2S_FIN_RCVD : 0;
pcb->sub_state |= ((flags & TH_RST) && (seq == expect)) ? TCP_C2S_RST_RCVD : 0;
pcb->sub_state |= ((flags & TH_RST) && (seq != expect)) ? TCP_C2S_UNVERIFIED_RST_RCVD : 0;
}
else
{
pcb->s2c_seq = seq;
pcb->s2c_ack = ack;
assembler = pcb->s2c_assembler;
expect = pcb->c2s_ack;
expect += pcb->sub_state & TCP_C2S_FIN_RCVD ? 1 : 0;
pcb->sub_state |= (flags & TH_SYN) ? TCP_SYN_ACK_RCVD : 0;
pcb->sub_state |= (flags & TH_FIN) ? TCP_S2C_FIN_RCVD : 0;
pcb->sub_state |= ((flags & TH_RST) && (seq == expect)) ? TCP_S2C_RST_RCVD : 0;
pcb->sub_state |= ((flags & TH_RST) && (seq != expect)) ? TCP_S2C_UNVERIFIED_RST_RCVD : 0;
}
if (flags & TH_SYN) if (flags & TH_SYN)
{ {
tcp_reassembly_set_recv_next(assembler, seq + 1); tcp_reassembly_set_recv_next(half->assembler, half->seq + 1);
} }
seg = tcp_reassembly_expire(assembler, now); seg = tcp_reassembly_expire(half->assembler, now);
if (seg) if (seg)
{ {
// TODO add metric (expire) half->nr_seg_expired++;
half->nr_seg_released++;
tcp_segment_free(seg); tcp_segment_free(seg);
} }
if (tcp_layer->pld_len) if (tcp_layer->pld_len)
{ {
rcv_nxt = tcp_reassembly_get_recv_next(assembler); half->nr_seg_received++;
if (seq == rcv_nxt) uint32_t rcv_nxt = tcp_reassembly_get_recv_next(half->assembler);
if (half->seq == rcv_nxt)
{ {
pcb->order_seg.data = tcp_layer->pld_ptr; half->nr_seg_inorder++;
pcb->order_seg.len = tcp_layer->pld_len; half->order.data = tcp_layer->pld_ptr;
tcp_reassembly_inc_recv_next(assembler, tcp_layer->pld_len); half->order.len = tcp_layer->pld_len;
tcp_reassembly_inc_recv_next(half->assembler, tcp_layer->pld_len);
} }
else if (before(seq, rcv_nxt)) else if (before(half->seq, rcv_nxt))
{ {
// TODO add metric (overlap) // TODO add metric (overlap)
half->nr_seg_overlap++;
} }
else if ((seg = tcp_segment_new(seq, tcp_layer->pld_ptr, tcp_layer->pld_len))) else if ((seg = tcp_segment_new(half->seq, tcp_layer->pld_ptr, tcp_layer->pld_len)))
{ {
switch (tcp_reassembly_push(assembler, seg, now)) switch (tcp_reassembly_push(half->assembler, seg, now))
{ {
case -1: case -1:
// TODO add metric (assembler full) half->nr_seg_no_space++;
tcp_segment_free(seg); tcp_segment_free(seg);
break; break;
case 0: case 0:
// TODO add metric (assembler push success) half->nr_seg_buffered++;
break; break;
case 1: case 1:
// TODO add metric (assembler push success, overlap) half->nr_seg_buffered++;
half->nr_seg_overlap++;
break; break;
default: default:
assert(0); assert(0);
break; break;
} }
} }
else
{
half->nr_seg_no_space++;
}
} }
} }
@@ -363,7 +324,7 @@ static int session_manager_self_protection(struct session_manager *mgr, struct s
switch (key->ip_proto) switch (key->ip_proto)
{ {
case IPPROTO_TCP: case IPPROTO_TCP:
if (stat->tcp_sess.nr_sess_used >= mgr->max_tcp_session_num) if (stat->tcp_sess.nr_sess_used >= mgr->opts.max_tcp_session_num)
{ {
stat->evc_pkt.nr_pkts++; stat->evc_pkt.nr_pkts++;
stat->evc_pkt.nr_bytes += packet_get_len(pkt); stat->evc_pkt.nr_bytes += packet_get_len(pkt);
@@ -372,7 +333,7 @@ static int session_manager_self_protection(struct session_manager *mgr, struct s
} }
break; break;
case IPPROTO_UDP: case IPPROTO_UDP:
if (stat->udp_sess.nr_sess_used >= mgr->max_udp_session_num) if (stat->udp_sess.nr_sess_used >= mgr->opts.max_udp_session_num)
{ {
stat->evc_pkt.nr_pkts++; stat->evc_pkt.nr_pkts++;
stat->evc_pkt.nr_bytes += packet_get_len(pkt); stat->evc_pkt.nr_bytes += packet_get_len(pkt);
@@ -559,7 +520,7 @@ static struct session *session_manager_new_tcp_session(struct session_manager *m
} }
// tcp table full evict old session // tcp table full evict old session
if (mgr->tcp_overload_evict_old_sess && mgr->stat.tcp_sess.nr_sess_used >= mgr->max_tcp_session_num - EVICTE_SESSION_BURST) if (mgr->opts.tcp_overload_evict_old_sess && mgr->stat.tcp_sess.nr_sess_used >= mgr->opts.max_tcp_session_num - EVICTE_SESSION_BURST)
{ {
struct session *evic_sess = session_table_find_lru(mgr->tcp_sess_table); struct session *evic_sess = session_table_find_lru(mgr->tcp_sess_table);
session_manager_evicte_session(mgr, evic_sess, now); session_manager_evicte_session(mgr, evic_sess, now);
@@ -575,20 +536,21 @@ static struct session *session_manager_new_tcp_session(struct session_manager *m
session_init(sess); session_init(sess);
session_set_id(sess, id_generator_alloc()); session_set_id(sess, id_generator_alloc());
if (tcp_pcb_init(&sess->tcp_pcb, mgr->tcp_reassembly_max_timeout, mgr->tcp_reassembly_max_segments) == -1) if (tcp_pcb_init(&sess->tcp_pcb, mgr->opts.tcp_reassembly_max_timeout, mgr->opts.tcp_reassembly_max_segments) == -1)
{ {
assert(0); assert(0);
session_pool_push(mgr->sess_pool, sess); session_pool_push(mgr->sess_pool, sess);
return NULL; return NULL;
} }
tcp_pcb_update(&sess->tcp_pcb, dir, tcp_layer, now); struct tcp_half *curr = (dir == SESSION_DIR_C2S) ? &sess->tcp_pcb.c2s : &sess->tcp_pcb.s2c;
tcp_half_update(curr, tcp_layer, now);
enum session_state next_state = session_transition_run(SESSION_STATE_INIT, TCP_SYN); enum session_state next_state = session_transition_run(SESSION_STATE_INIT, TCP_SYN);
session_update(sess, next_state, pkt, key, dir, now); session_update(sess, next_state, pkt, key, dir, now);
session_transition_log(sess, SESSION_STATE_INIT, next_state, TCP_SYN); session_transition_log(sess, SESSION_STATE_INIT, next_state, TCP_SYN);
session_stat_inc(&mgr->stat.tcp_sess, next_state); session_stat_inc(&mgr->stat.tcp_sess, next_state);
uint64_t timeout = (flags & TH_ACK) ? mgr->tcp_handshake_timeout : mgr->tcp_init_timeout; uint64_t timeout = (flags & TH_ACK) ? mgr->opts.tcp_handshake_timeout : mgr->opts.tcp_init_timeout;
session_timer_update(mgr->sess_timer, sess, now + timeout); session_timer_update(mgr->sess_timer, sess, now + timeout);
session_table_add(mgr->tcp_sess_table, key, sess); session_table_add(mgr->tcp_sess_table, key, sess);
@@ -601,7 +563,7 @@ static struct session *session_manager_new_tcp_session(struct session_manager *m
static struct session *session_manager_new_udp_session(struct session_manager *mgr, const struct packet *pkt, const struct tuple6 *key, uint64_t now) static struct session *session_manager_new_udp_session(struct session_manager *mgr, const struct packet *pkt, const struct tuple6 *key, uint64_t now)
{ {
// udp table full evict old session // udp table full evict old session
if (mgr->udp_overload_evict_old_sess && mgr->stat.udp_sess.nr_sess_used >= mgr->max_udp_session_num - EVICTE_SESSION_BURST) if (mgr->opts.udp_overload_evict_old_sess && mgr->stat.udp_sess.nr_sess_used >= mgr->opts.max_udp_session_num - EVICTE_SESSION_BURST)
{ {
struct session *evic_sess = session_table_find_lru(mgr->udp_sess_table); struct session *evic_sess = session_table_find_lru(mgr->udp_sess_table);
session_manager_evicte_session(mgr, evic_sess, now); session_manager_evicte_session(mgr, evic_sess, now);
@@ -623,7 +585,7 @@ static struct session *session_manager_new_udp_session(struct session_manager *m
session_transition_log(sess, SESSION_STATE_INIT, next_state, UDP_DATA); session_transition_log(sess, SESSION_STATE_INIT, next_state, UDP_DATA);
session_stat_inc(&mgr->stat.udp_sess, next_state); session_stat_inc(&mgr->stat.udp_sess, next_state);
session_timer_update(mgr->sess_timer, sess, now + mgr->udp_data_timeout); session_timer_update(mgr->sess_timer, sess, now + mgr->opts.udp_data_timeout);
session_table_add(mgr->udp_sess_table, key, sess); session_table_add(mgr->udp_sess_table, key, sess);
return sess; return sess;
@@ -635,65 +597,74 @@ static int session_manager_update_tcp_session(struct session_manager *mgr, struc
const struct tcphdr *hdr = (const struct tcphdr *)tcp_layer->hdr_ptr; const struct tcphdr *hdr = (const struct tcphdr *)tcp_layer->hdr_ptr;
enum session_dir dir = identify_direction_by_history(sess, key); enum session_dir dir = identify_direction_by_history(sess, key);
uint8_t flags = tcp_hdr_get_flags(hdr); uint8_t flags = tcp_hdr_get_flags(hdr);
int inputs = (flags & TH_SYN) ? TCP_SYN : NONE; int inputs = 0;
inputs |= (flags & TH_SYN) ? TCP_SYN : NONE;
inputs |= (flags & TH_FIN) ? TCP_FIN : NONE; inputs |= (flags & TH_FIN) ? TCP_FIN : NONE;
inputs |= (flags & TH_RST) ? TCP_RST : NONE; inputs |= (flags & TH_RST) ? TCP_RST : NONE;
inputs |= tcp_layer->pld_len ? TCP_DATA : NONE; inputs |= tcp_layer->pld_len ? TCP_DATA : NONE;
// update state
enum session_state curr_state = session_get_state(sess); enum session_state curr_state = session_get_state(sess);
enum session_state next_state = session_transition_run(curr_state, inputs); enum session_state next_state = session_transition_run(curr_state, inputs);
session_update(sess, next_state, pkt, key, dir, now);
session_transition_log(sess, curr_state, next_state, inputs);
session_stat_update(mgr, sess, curr_state, next_state);
tcp_pcb_update(&sess->tcp_pcb, dir, tcp_layer, now); // update session
session_update(sess, next_state, pkt, key, dir, now);
session_stat_update(mgr, sess, curr_state, next_state);
session_transition_log(sess, curr_state, next_state, inputs);
// update tcp pcb
struct tcp_half *curr = (dir == SESSION_DIR_C2S) ? &sess->tcp_pcb.c2s : &sess->tcp_pcb.s2c;
struct tcp_half *peer = (dir == SESSION_DIR_C2S) ? &sess->tcp_pcb.s2c : &sess->tcp_pcb.c2s;
tcp_half_update(curr, tcp_layer, now);
// set closing reason // set closing reason
if (next_state == SESSION_STATE_CLOSING && !session_get_closing_reason(sess)) if (next_state == SESSION_STATE_CLOSING && !session_get_closing_reason(sess))
{ {
if (tcp_hdr_get_fin_flag(hdr)) if (flags & TH_FIN)
{ {
session_set_closing_reason(sess, (dir == SESSION_DIR_C2S ? CLOSING_BY_CLIENT_FIN : CLOSING_BY_SERVER_FIN)); session_set_closing_reason(sess, (dir == SESSION_DIR_C2S ? CLOSING_BY_CLIENT_FIN : CLOSING_BY_SERVER_FIN));
} }
if (tcp_hdr_get_rst_flag(hdr)) if (flags & TH_RST)
{ {
session_set_closing_reason(sess, (dir == SESSION_DIR_C2S ? CLOSING_BY_CLIENT_RST : CLOSING_BY_SERVER_RST)); session_set_closing_reason(sess, (dir == SESSION_DIR_C2S ? CLOSING_BY_CLIENT_RST : CLOSING_BY_SERVER_RST));
} }
} }
uint16_t sub_state = sess->tcp_pcb.sub_state; // update timeout
uint64_t timeout = 0; uint64_t timeout = 0;
switch (next_state) switch (next_state)
{ {
case SESSION_STATE_OPENING: case SESSION_STATE_OPENING:
if (flags & TH_SYN) if (flags & TH_SYN)
{ {
timeout = (flags & TH_ACK) ? mgr->tcp_handshake_timeout : mgr->tcp_init_timeout; timeout = (flags & TH_ACK) ? mgr->opts.tcp_handshake_timeout : mgr->opts.tcp_init_timeout;
} }
else else
{ {
timeout = mgr->tcp_data_timeout; timeout = mgr->opts.tcp_data_timeout;
} }
break; break;
case SESSION_STATE_ACTIVE: case SESSION_STATE_ACTIVE:
timeout = mgr->tcp_data_timeout; timeout = mgr->opts.tcp_data_timeout;
break; break;
case SESSION_STATE_CLOSING: case SESSION_STATE_CLOSING:
if (flags & TH_FIN) if (flags & TH_FIN)
{ {
timeout = (sub_state & TCP_C2S_FIN_RCVD && sub_state & TCP_S2C_FIN_RCVD) ? mgr->tcp_time_wait_timeout : mgr->tcp_half_closed_timeout; timeout = (peer->flags & TH_FIN) ? mgr->opts.tcp_time_wait_timeout : mgr->opts.tcp_half_closed_timeout;
} }
else if (flags & TH_RST) else if (flags & TH_RST)
{ {
timeout = (sub_state & TCP_C2S_RST_RCVD || sub_state & TCP_S2C_RST_RCVD) ? mgr->tcp_time_wait_timeout : mgr->tcp_unverified_rst_timeout; // if fin is received, the expected sequence number should be increased by 1
uint32_t expected = (peer->flags & TH_FIN) ? peer->ack + 1 : peer->ack;
timeout = (expected == curr->seq) ? mgr->opts.tcp_time_wait_timeout : mgr->opts.tcp_unverified_rst_timeout;
} }
else else
{ {
timeout = mgr->tcp_data_timeout; timeout = mgr->opts.tcp_data_timeout;
} }
break; break;
case SESSION_STATE_DISCARD: case SESSION_STATE_DISCARD:
timeout = mgr->tcp_discard_timeout; timeout = mgr->opts.tcp_discard_timeout;
break; break;
default: default:
assert(0); assert(0);
@@ -712,7 +683,7 @@ static int session_manager_update_udp_session(struct session_manager *mgr, struc
session_update(sess, next_state, pkt, key, dir, now); session_update(sess, next_state, pkt, key, dir, now);
session_transition_log(sess, curr_state, next_state, UDP_DATA); session_transition_log(sess, curr_state, next_state, UDP_DATA);
session_stat_update(mgr, sess, curr_state, next_state); session_stat_update(mgr, sess, curr_state, next_state);
session_timer_update(mgr->sess_timer, sess, now + mgr->udp_data_timeout); session_timer_update(mgr->sess_timer, sess, now + mgr->opts.udp_data_timeout);
return 0; return 0;
} }
@@ -733,26 +704,9 @@ struct session_manager *session_manager_new(struct session_manager_options *opts
{ {
return NULL; return NULL;
} }
// max session number
mgr->max_tcp_session_num = (opts->max_tcp_session_num < EVICTE_SESSION_BURST * 2) ? EVICTE_SESSION_BURST * 2 : opts->max_tcp_session_num;
mgr->max_udp_session_num = (opts->max_udp_session_num < EVICTE_SESSION_BURST * 2) ? EVICTE_SESSION_BURST * 2 : opts->max_udp_session_num;
// session overload
mgr->stat.tcp_sess.nr_sess_init = 0;
mgr->tcp_overload_evict_old_sess = opts->tcp_overload_evict_old_sess;
mgr->udp_overload_evict_old_sess = opts->udp_overload_evict_old_sess;
// session timeout
mgr->tcp_init_timeout = opts->tcp_init_timeout;
mgr->tcp_handshake_timeout = opts->tcp_handshake_timeout;
mgr->tcp_data_timeout = opts->tcp_data_timeout;
mgr->tcp_half_closed_timeout = opts->tcp_half_closed_timeout;
mgr->tcp_time_wait_timeout = opts->tcp_time_wait_timeout;
mgr->tcp_discard_timeout = opts->tcp_discard_timeout;
mgr->tcp_unverified_rst_timeout = opts->tcp_unverified_rst_timeout;
mgr->udp_data_timeout = opts->udp_data_timeout;
// tcp reassembly memcpy(&mgr->opts, opts, sizeof(struct session_manager_options));
mgr->tcp_reassembly_max_timeout = opts->tcp_reassembly_max_timeout;
mgr->tcp_reassembly_max_segments = opts->tcp_reassembly_max_segments;
// duplicated packet filter // duplicated packet filter
struct duplicated_packet_filter_options duplicated_packet_filter_opts = { struct duplicated_packet_filter_options duplicated_packet_filter_opts = {
.enable = opts->duplicated_packet_filter_enable, .enable = opts->duplicated_packet_filter_enable,
@@ -768,7 +722,7 @@ struct session_manager *session_manager_new(struct session_manager_options *opts
.error_rate = opts->evicted_session_filter_error_rate, .error_rate = opts->evicted_session_filter_error_rate,
}; };
mgr->sess_pool = session_pool_new(mgr->max_tcp_session_num + mgr->max_udp_session_num); mgr->sess_pool = session_pool_new(mgr->opts.max_tcp_session_num + mgr->opts.max_udp_session_num);
mgr->tcp_sess_table = session_table_new(); mgr->tcp_sess_table = session_table_new();
mgr->udp_sess_table = session_table_new(); mgr->udp_sess_table = session_table_new();
mgr->sess_timer = session_timer_new(now); mgr->sess_timer = session_timer_new(now);
@@ -946,10 +900,10 @@ struct session *session_manager_get_expired_session(struct session_manager *mgr,
switch (session_get_type(sess)) switch (session_get_type(sess))
{ {
case SESSION_TYPE_TCP: case SESSION_TYPE_TCP:
timeout = mgr->tcp_data_timeout; timeout = mgr->opts.tcp_data_timeout;
break; break;
case SESSION_TYPE_UDP: case SESSION_TYPE_UDP:
timeout = mgr->udp_data_timeout; timeout = mgr->opts.udp_data_timeout;
break; break;
default: default:
assert(0); assert(0);