diff --git a/src/session/session.cpp b/src/session/session.cpp index 23747e7..0b7f7c3 100644 --- a/src/session/session.cpp +++ b/src/session/session.cpp @@ -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_pcb *pcb = &sess->tcp_pcb; - if (pcb->order_seg.data != NULL && pcb->order_seg.len > 0) - { - return &pcb->order_seg; - } - + struct tcp_half *half = NULL; if (session_get_cur_dir(sess) == SESSION_DIR_C2S) { - return tcp_reassembly_pop(pcb->c2s_assembler); + half = &sess->tcp_pcb.c2s; } 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; } - 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; - sess->tcp_pcb.order_seg.len = 0; + half = &sess->tcp_pcb.c2s; + } + else + { + half = &sess->tcp_pcb.s2c; + } + + if (seg == &half->order) + { + half->order.data = NULL; + half->order.len = 0; return; } else { + half->nr_seg_released++; 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 ******************************************************************************/ @@ -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); + } +} diff --git a/src/session/session.h b/src/session/session.h index 924a7ee..953ed65 100644 --- a/src/session/session.h +++ b/src/session/session.h @@ -75,35 +75,32 @@ enum session_packet_index MAX_PACKETS, }; -enum tcp_state +struct tcp_half { - TCP_SYN_RCVD = 1 << 0, - TCP_SYN_ACK_RCVD = 1 << 1, + struct tcp_reassembly *assembler; + struct tcp_segment order; - TCP_C2S_FIN_RCVD = 1 << 2, - TCP_S2C_FIN_RCVD = 1 << 3, + uint64_t nr_seg_received; + uint64_t nr_seg_expired; + uint64_t nr_seg_overlap; //(retransmission) + uint64_t nr_seg_no_space; - TCP_C2S_RST_RCVD = 1 << 4, - TCP_S2C_RST_RCVD = 1 << 5, + uint64_t nr_seg_inorder; + uint64_t nr_seg_reorded; - TCP_C2S_UNVERIFIED_RST_RCVD = 1 << 6, - TCP_S2C_UNVERIFIED_RST_RCVD = 1 << 7, + uint64_t nr_seg_buffered; + uint64_t nr_seg_released; + + uint32_t seq; + uint32_t ack; + uint8_t flags; }; // the TCP protocol control block struct tcp_pcb { - struct tcp_reassembly *c2s_assembler; - struct tcp_reassembly *s2c_assembler; - - 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 tcp_half c2s; + struct tcp_half s2c; }; 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); 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 ******************************************************************************/ @@ -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_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 } #endif diff --git a/src/session/session_manager.cpp b/src/session/session_manager.cpp index c5b72f2..3345001 100644 --- a/src/session/session_manager.cpp +++ b/src/session/session_manager.cpp @@ -15,25 +15,7 @@ struct session_manager { - // max session number - 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_manager_options opts; struct session_pool *sess_pool; struct session_table *tcp_sess_table; @@ -58,6 +40,16 @@ int check_options(const struct session_manager_options *opts) 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) { 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) { - tcp_reassembly_free(pcb->c2s_assembler); - tcp_reassembly_free(pcb->s2c_assembler); + tcp_reassembly_free(pcb->c2s.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) { - pcb->c2s_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) + pcb->c2s.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) { tcp_pcb_clean(pcb); return -1; @@ -134,100 +126,69 @@ static int tcp_pcb_init(struct tcp_pcb *pcb, uint64_t max_timeout, uint64_t max_ 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_reassembly *assembler; 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); - uint32_t rcv_nxt; - /* - * https://www.rfc-editor.org/rfc/rfc5961#section-3.2 - * - * 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; - } + half->flags |= flags; + half->seq = tcp_hdr_get_seq(hdr); + half->ack = tcp_hdr_get_ack(hdr); 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) { - // TODO add metric (expire) + half->nr_seg_expired++; + half->nr_seg_released++; tcp_segment_free(seg); } if (tcp_layer->pld_len) { - rcv_nxt = tcp_reassembly_get_recv_next(assembler); - if (seq == rcv_nxt) + half->nr_seg_received++; + uint32_t rcv_nxt = tcp_reassembly_get_recv_next(half->assembler); + if (half->seq == rcv_nxt) { - pcb->order_seg.data = tcp_layer->pld_ptr; - pcb->order_seg.len = tcp_layer->pld_len; - tcp_reassembly_inc_recv_next(assembler, tcp_layer->pld_len); + half->nr_seg_inorder++; + half->order.data = tcp_layer->pld_ptr; + 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) + 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: - // TODO add metric (assembler full) + half->nr_seg_no_space++; tcp_segment_free(seg); break; case 0: - // TODO add metric (assembler push success) + half->nr_seg_buffered++; break; case 1: - // TODO add metric (assembler push success, overlap) + half->nr_seg_buffered++; + half->nr_seg_overlap++; break; default: assert(0); 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) { 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_bytes += packet_get_len(pkt); @@ -372,7 +333,7 @@ static int session_manager_self_protection(struct session_manager *mgr, struct s } break; 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_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 - 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); 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_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); session_pool_push(mgr->sess_pool, sess); 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); session_update(sess, next_state, pkt, key, dir, now); session_transition_log(sess, SESSION_STATE_INIT, next_state, TCP_SYN); 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_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) { // 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); 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_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); 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; enum session_dir dir = identify_direction_by_history(sess, key); 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_RST) ? TCP_RST : NONE; inputs |= tcp_layer->pld_len ? TCP_DATA : NONE; + + // update state enum session_state curr_state = session_get_state(sess); 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 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)); } - 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)); } } - uint16_t sub_state = sess->tcp_pcb.sub_state; - + // update timeout uint64_t timeout = 0; switch (next_state) { case SESSION_STATE_OPENING: 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 { - timeout = mgr->tcp_data_timeout; + timeout = mgr->opts.tcp_data_timeout; } break; case SESSION_STATE_ACTIVE: - timeout = mgr->tcp_data_timeout; + timeout = mgr->opts.tcp_data_timeout; break; case SESSION_STATE_CLOSING: 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) { - 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 { - timeout = mgr->tcp_data_timeout; + timeout = mgr->opts.tcp_data_timeout; } break; case SESSION_STATE_DISCARD: - timeout = mgr->tcp_discard_timeout; + timeout = mgr->opts.tcp_discard_timeout; break; default: 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_transition_log(sess, curr_state, next_state, UDP_DATA); 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; } @@ -733,26 +704,9 @@ struct session_manager *session_manager_new(struct session_manager_options *opts { 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 - mgr->tcp_reassembly_max_timeout = opts->tcp_reassembly_max_timeout; - mgr->tcp_reassembly_max_segments = opts->tcp_reassembly_max_segments; + memcpy(&mgr->opts, opts, sizeof(struct session_manager_options)); + // duplicated packet filter struct duplicated_packet_filter_options duplicated_packet_filter_opts = { .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, }; - 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->udp_sess_table = session_table_new(); 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)) { case SESSION_TYPE_TCP: - timeout = mgr->tcp_data_timeout; + timeout = mgr->opts.tcp_data_timeout; break; case SESSION_TYPE_UDP: - timeout = mgr->udp_data_timeout; + timeout = mgr->opts.udp_data_timeout; break; default: assert(0);