#include #include "session.h" #include "tcp_utils.h" #include "tcp_reassembly.h" #define EX_KEY_MAX_LEN 64 struct ex_schema { char key[EX_KEY_MAX_LEN]; session_ex_free_cb *free_cb; void *args; }; struct ex_manager { struct ex_schema schemas[EX_DATA_MAX_COUNT]; uint8_t count; }; static struct ex_manager g_ex_manager = {0}; /****************************************************************************** * session set/get ******************************************************************************/ void session_init(struct session *sess) { memset(sess, 0, sizeof(struct session)); } void session_set_id(struct session *sess, uint64_t id) { sess->id = id; } uint64_t session_get_id(const struct session *sess) { return sess->id; } void session_set_tuple(struct session *sess, const struct tuple6 *tuple) { memcpy(&sess->tuple, tuple, sizeof(struct tuple6)); } const struct tuple6 *session_get_tuple(const struct session *sess) { return &sess->tuple; } void session_set_tuple_dir(struct session *sess, enum session_dir dir) { sess->tuple_dir = dir; } enum session_dir session_get_tuple_dir(const struct session *sess) { return sess->tuple_dir; } void session_set_cur_dir(struct session *sess, enum session_dir dir) { sess->cur_dir = dir; } enum session_dir session_get_cur_dir(const struct session *sess) { return sess->cur_dir; } void session_set_state(struct session *sess, enum session_state state) { sess->state = state; } enum session_state session_get_state(const struct session *sess) { return sess->state; } void session_set_type(struct session *sess, enum session_type type) { sess->type = type; } enum session_type session_get_type(const struct session *sess) { return sess->type; } void session_set_dup_traffic(struct session *sess) { sess->dup = 1; } int session_has_dup_traffic(const struct session *sess) { return sess->dup; } void session_set_closing_reason(struct session *sess, enum closing_reason reason) { sess->reason = reason; } enum closing_reason session_get_closing_reason(const struct session *sess) { return sess->reason; } void session_inc_metric(struct session *sess, enum session_metric_index idx, uint64_t val) { sess->metrics[idx] += val; } void session_set_metric(struct session *sess, enum session_metric_index idx, uint64_t val) { sess->metrics[idx] = val; } uint64_t session_get_metric(const struct session *sess, enum session_metric_index idx) { return sess->metrics[idx]; } void session_set_timestamp(struct session *sess, enum session_timestamp_index idx, uint64_t timestamp) { sess->timestamps[idx] = timestamp; } uint64_t session_get_timestamp(const struct session *sess, enum session_timestamp_index idx) { return sess->timestamps[idx]; } void session_set_packet(struct session *sess, enum session_packet_index idx, const struct packet *pkt) { if (idx == SESSION_PACKET_CURRENT) { sess->packets[idx] = pkt; } else { if (sess->packets[idx]) { return; } sess->packets[idx] = packet_dup(pkt); } } void session_clean_packet(struct session *sess, enum session_packet_index idx) { if (idx == SESSION_PACKET_CURRENT) { sess->packets[idx] = NULL; } else { packet_free((struct packet *)sess->packets[idx]); sess->packets[idx] = NULL; } } const struct packet *session_get_packet(const struct session *sess, enum session_packet_index idx) { return sess->packets[idx]; } void session_set_user_data(struct session *sess, void *user_data) { sess->user_data = user_data; } void *session_get_user_data(const struct session *sess) { return sess->user_data; } struct tcp_segment *session_get_tcp_segment(struct session *sess) { struct tcp_half *half = NULL; if (session_get_cur_dir(sess) == SESSION_DIR_C2S) { half = &sess->tcp_pcb.c2s; } else { 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; } } void session_free_tcp_segment(struct session *sess, struct tcp_segment *seg) { if (seg == NULL) { return; } struct tcp_half *half = NULL; if (session_get_cur_dir(sess) == SESSION_DIR_C2S) { 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); } } /****************************************************************************** * session ex data ******************************************************************************/ /* * the exdata prodoced by user, and comsumed by same user. * so, the exdata is not shared by different user. * otherwise, the exdata need dup by refer count, and free by refer count. * * if key exist, not allow update, return original index. */ uint8_t session_get_ex_new_index(const char *key, session_ex_free_cb *free_cb, void *args) { if (g_ex_manager.count >= EX_DATA_MAX_COUNT) { abort(); return EX_DATA_MAX_COUNT; } for (uint8_t i = 0; i < g_ex_manager.count; i++) { if (strcmp(g_ex_manager.schemas[i].key, key) == 0) { return i; } } uint8_t idx = g_ex_manager.count; g_ex_manager.count++; struct ex_schema *schema = &g_ex_manager.schemas[idx]; strncpy(schema->key, key, EX_KEY_MAX_LEN); schema->free_cb = free_cb; schema->args = args; return idx; } /* * Support update ex_data. * * if key exist: run free_cb free old value, then set new value. * if not run free_cb, old value will be memory leak. * if not allow update, new value will be memory leak. * if key not exist: set new value. */ void session_set_ex_data(struct session *sess, uint8_t idx, void *val) { if (idx >= g_ex_manager.count) { assert(0); return; } session_free_ex_data(sess, idx); sess->ex_data[idx] = val; } void *session_get0_ex_data(const struct session *sess, uint8_t idx) { if (idx >= g_ex_manager.count) { assert(0); return NULL; } return sess->ex_data[idx]; } /* * after set ex_data, the owner of ex_data is session, so user should not free it directly. * if user want to free ex_data, should use session_free_ex_data. */ void session_free_ex_data(struct session *sess, uint8_t idx) { if (idx >= g_ex_manager.count) { assert(0); return; } struct ex_schema *schema = &g_ex_manager.schemas[idx]; if (schema->free_cb != NULL && sess->ex_data[idx] != NULL) { printf("free ex_data, idx: %d, key: %s, val: %p\n", idx, schema->key, sess->ex_data[idx]); schema->free_cb(sess, idx, sess->ex_data[idx], schema->args); } sess->ex_data[idx] = NULL; } void session_free_all_ex_data(struct session *sess) { if (sess) { for (uint8_t i = 0; i < g_ex_manager.count; i++) { session_free_ex_data(sess, i); } } } /****************************************************************************** * 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); } }