#include #include "session_def.h" #include "session_utils.h" #include "session_manager.h" #define EX_KEY_MAX_LEN 64 struct session_exdata_schema { char key[EX_KEY_MAX_LEN]; session_ex_free_cb *free_cb; void *args; }; struct session_exdata_manager { struct session_exdata_schema schemas[EX_DATA_MAX_COUNT]; uint8_t count; }; static struct session_exdata_manager g_ex_manager = {}; /****************************************************************************** * 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_tuple6(struct session *sess, const struct tuple6 *tuple) { memcpy(&sess->tuple, tuple, sizeof(struct tuple6)); } const struct tuple6 *session_get_tuple6(const struct session *sess) { return &sess->tuple; } void session_set_tuple_direction(struct session *sess, enum flow_direction dir) { sess->tuple_dir = dir; } enum flow_direction session_get_tuple6_direction(const struct session *sess) { return sess->tuple_dir; } const char *session_get0_readable_addr(const struct session *sess) { return sess->tuple_str; } void session_set_direction(struct session *sess, enum session_direction dir) { sess->sess_dir = dir; } enum session_direction session_get_direction(const struct session *sess) { return sess->sess_dir; } void session_set_current_flow_direction(struct session *sess, enum flow_direction dir) { sess->flow_dir = dir; } enum flow_direction session_get_current_flow_direction(const struct session *sess) { return sess->flow_dir; } void session_set_current_state(struct session *sess, enum session_state state) { sess->state = state; } enum session_state session_get_current_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_duplicate_traffic(struct session *sess) { sess->dup = 1; } int session_has_duplicate_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_stat(struct session *sess, enum flow_direction dir, enum session_stat stat, uint64_t val) { sess->stats[dir][stat] += val; } uint64_t session_get_stat(const struct session *sess, enum flow_direction dir, enum session_stat stat) { return sess->stats[dir][stat]; } void session_set_timestamp(struct session *sess, enum session_timestamp type, uint64_t value) { sess->timestamps[type] = value; } uint64_t session_get_timestamp(const struct session *sess, enum session_timestamp type) { return sess->timestamps[type]; } void session_clear_sids(struct session *sess, enum flow_direction dir) { memset(&sess->sids[dir], 0, sizeof(struct sids)); } void session_set_sids(struct session *sess, enum flow_direction dir, const struct sids *sids) { sess->sids[dir] = *sids; } const struct sids *session_get_sids(const struct session *sess, enum flow_direction dir) { return &sess->sids[dir]; } void session_clear_route_ctx(struct session *sess, enum flow_direction dir) { memset(&sess->route_ctx[dir], 0, sizeof(struct route_ctx)); } void session_set_route_ctx(struct session *sess, enum flow_direction dir, const struct route_ctx *ctx) { sess->route_ctx[dir] = *ctx; } const struct route_ctx *session_get_route_ctx(const struct session *sess, enum flow_direction dir) { return &sess->route_ctx[dir]; } void session_set_first_packet(struct session *sess, enum flow_direction dir, const struct packet *pkt) { sess->first_pkt[dir] = pkt; } const struct packet *session_get_first_packet(const struct session *sess, enum flow_direction dir) { return sess->first_pkt[dir]; } void session_set_current_packet(struct session *sess, const struct packet *pkt) { sess->curr_pkt = pkt; } const struct packet *session_get0_current_packet(const struct session *sess) { return sess->curr_pkt; } int session_is_symmetric(const struct session *sess, unsigned char *flag) { int is_symmetric = 0; if (sess->first_pkt[FLOW_DIRECTION_C2S] && sess->first_pkt[FLOW_DIRECTION_S2C]) { if (flag) { *flag = (SESSION_SEEN_C2S_FLOW | SESSION_SEEN_S2C_FLOW); } is_symmetric = 1; } else if (sess->first_pkt[FLOW_DIRECTION_C2S]) { if (flag) { *flag = SESSION_SEEN_C2S_FLOW; } } else if (sess->first_pkt[FLOW_DIRECTION_S2C]) { if (flag) { *flag = SESSION_SEEN_S2C_FLOW; } } return is_symmetric; } 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) { enum flow_direction dir = session_get_current_flow_direction(sess); struct tcp_half *half = &sess->tcp_halfs[dir]; if (half->in_order.data != NULL && half->in_order.len > 0 && half->in_order_ref == 0) { half->in_order_ref++; return &half->in_order; } else { struct tcp_segment *seg = tcp_reassembly_pop(half->assembler); if (seg) { session_inc_stat(sess, dir, STAT_TCP_SEGMENTS_REORDERED, 1); session_inc_stat(sess, dir, STAT_TCP_PAYLOADS_REORDERED, seg->len); // TODO sess->mgr_stat->nr_tcp_seg_reorded++; } return seg; } } void session_free_tcp_segment(struct session *sess, struct tcp_segment *seg) { if (seg == NULL) { return; } enum flow_direction dir = session_get_current_flow_direction(sess); struct tcp_half *half = &sess->tcp_halfs[dir]; if (seg == &half->in_order) { half->in_order.data = NULL; half->in_order.len = 0; return; } else { session_inc_stat(sess, dir, STAT_TCP_SEGMENTS_RELEASED, 1); session_inc_stat(sess, dir, STAT_TCP_PAYLOADS_RELEASED, seg->len); sess->mgr_stat->nr_tcp_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 session_exdata_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 session_exdata_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_LRU_EVICTED: return "closing by lru evicted"; case CLOSING_BY_PORT_REUSE_EVICTED: return "closing by port reuse 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 *flow_direction_to_str(enum flow_direction dir) { switch (dir) { case FLOW_DIRECTION_C2S: return "C2S"; case FLOW_DIRECTION_S2C: return "S2C"; default: return "unknown"; } } static void tcp_flags_to_str(uint8_t flags, char *buffer, size_t len) { int used = 0; if (flags & TH_SYN) { used += snprintf(buffer + used, len - used, "SYN "); } if (flags & TH_ACK) { used += snprintf(buffer + used, len - used, "ACK "); } if (flags & TH_FIN) { used += snprintf(buffer + used, len - used, "FIN "); } if (flags & TH_RST) { used += snprintf(buffer + used, len - used, "RST "); } } int session_to_str(const struct session *sess, int bref, char *buff, int size) { memset(buff, 0, size); char flags[64] = {0}; int used = 0; if (bref) { used += snprintf(buff + used, size - used, "session id: %lu, addr: %s, type: %s, state: %s, dir: %s, c2s_rx_pkts: %lu, s2c_rx_pkts: %lu", session_get_id(sess), session_get0_readable_addr(sess), session_type_to_str(session_get_type(sess)), session_state_to_str(session_get_current_state(sess)), flow_direction_to_str(session_get_current_flow_direction(sess)), session_get_stat(sess, FLOW_DIRECTION_C2S, STAT_RAW_PACKETS_RECEIVED), session_get_stat(sess, FLOW_DIRECTION_S2C, STAT_RAW_PACKETS_RECEIVED)); } else { used += snprintf(buff + used, size - used, "{"); used += snprintf(buff + used, size - used, "\"id\":%" PRIu64 ",", session_get_id(sess)); used += snprintf(buff + used, size - used, "\"start_timestamp_ms\":%" PRIu64 ",", session_get_timestamp(sess, SESSION_TIMESTAMP_START)); used += snprintf(buff + used, size - used, "\"last_timestamp_ms\":%" PRIu64 ",", session_get_timestamp(sess, SESSION_TIMESTAMP_LAST)); used += snprintf(buff + used, size - used, "\"tuple\":\"%s\",", session_get0_readable_addr(sess)); used += snprintf(buff + used, size - used, "\"type\":\"%s\",", session_type_to_str(session_get_type(sess))); used += snprintf(buff + used, size - used, "\"state\":\"%s\",", session_state_to_str(session_get_current_state(sess))); used += snprintf(buff + used, size - used, "\"closing_reason\":\"%s\",", closing_reason_to_str(session_get_closing_reason(sess))); used += snprintf(buff + used, size - used, "\"duplicate_traffic\":%d,", session_has_duplicate_traffic(sess)); used += snprintf(buff + used, size - used, "\"current_packet\":\"%p\",", session_get0_current_packet(sess)); const char *str[] = {"c2s", "s2c"}; enum flow_direction dir[] = {FLOW_DIRECTION_C2S, FLOW_DIRECTION_S2C}; for (int i = 0; i < 2; i++) { // raw packets used += snprintf(buff + used, size - used, "\"%s_raw_packets_received\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_RAW_PACKETS_RECEIVED)); used += snprintf(buff + used, size - used, "\"%s_raw_bytes_received\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_RAW_BYTES_RECEIVED)); used += snprintf(buff + used, size - used, "\"%s_raw_packets_transmitted\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_RAW_PACKETS_TRANSMITTED)); used += snprintf(buff + used, size - used, "\"%s_raw_bytes_transmitted\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_RAW_BYTES_TRANSMITTED)); used += snprintf(buff + used, size - used, "\"%s_raw_packets_dropped\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_RAW_PACKETS_DROPPED)); used += snprintf(buff + used, size - used, "\"%s_raw_bytes_dropped\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_RAW_BYTES_DROPPED)); // duplicate packets used += snprintf(buff + used, size - used, "\"%s_duplicate_packets_bypass\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_DUPLICATE_PACKETS_BYPASS)); used += snprintf(buff + used, size - used, "\"%s_duplicate_bytes_bypass\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_DUPLICATE_BYTES_BYPASS)); // injected packets used += snprintf(buff + used, size - used, "\"%s_injected_packets_failed\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_INJECTED_PACKETS_FAILED)); used += snprintf(buff + used, size - used, "\"%s_injected_packets_success\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_INJECTED_PACKETS_SUCCESS)); used += snprintf(buff + used, size - used, "\"%s_injected_bytes_success\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_INJECTED_BYTES_SUCCESS)); // control packets used += snprintf(buff + used, size - used, "\"%s_control_packets_received\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_CONTROL_PACKETS_RECEIVED)); used += snprintf(buff + used, size - used, "\"%s_control_bytes_received\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_CONTROL_BYTES_RECEIVED)); used += snprintf(buff + used, size - used, "\"%s_control_packets_transmitted\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_CONTROL_PACKETS_TRANSMITTED)); used += snprintf(buff + used, size - used, "\"%s_control_bytes_transmitted\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_CONTROL_BYTES_TRANSMITTED)); used += snprintf(buff + used, size - used, "\"%s_control_packets_dropped\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_CONTROL_PACKETS_DROPPED)); used += snprintf(buff + used, size - used, "\"%s_control_bytes_dropped\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_CONTROL_BYTES_DROPPED)); if (session_get_type(sess) == SESSION_TYPE_TCP) { used += snprintf(buff + used, size - used, "\"%s_tcp_last_seq\":%u,", str[i], sess->tcp_halfs[dir[i]].seq); used += snprintf(buff + used, size - used, "\"%s_tcp_last_ack\":%u,", str[i], sess->tcp_halfs[dir[i]].ack); memset(flags, 0, sizeof(flags)); tcp_flags_to_str(sess->tcp_halfs[dir[i]].flags, flags, sizeof(flags)); used += snprintf(buff + used, size - used, "\"%s_tcp_flags\":\"%s\",", str[i], flags); used += snprintf(buff + used, size - used, "\"%s_tcp_segments_received\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_TCP_SEGMENTS_RECEIVED)); used += snprintf(buff + used, size - used, "\"%s_tcp_payloads_received\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_TCP_PAYLOADS_RECEIVED)); used += snprintf(buff + used, size - used, "\"%s_tcp_segments_expired\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_TCP_SEGMENTS_EXPIRED)); used += snprintf(buff + used, size - used, "\"%s_tcp_payloads_expired\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_TCP_PAYLOADS_EXPIRED)); used += snprintf(buff + used, size - used, "\"%s_tcp_segments_retransmit\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_TCP_SEGMENTS_RETRANSMIT)); used += snprintf(buff + used, size - used, "\"%s_tcp_payloads_retransmit\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_TCP_PAYLOADS_RETRANSMIT)); used += snprintf(buff + used, size - used, "\"%s_tcp_segments_overlap\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_TCP_SEGMENTS_OVERLAP)); used += snprintf(buff + used, size - used, "\"%s_tcp_payloads_overlap\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_TCP_PAYLOADS_OVERLAP)); used += snprintf(buff + used, size - used, "\"%s_tcp_segments_nospace\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_TCP_SEGMENTS_NOSPACE)); used += snprintf(buff + used, size - used, "\"%s_tcp_payloads_nospace\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_TCP_PAYLOADS_NOSPACE)); used += snprintf(buff + used, size - used, "\"%s_tcp_segments_inorder\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_TCP_SEGMENTS_INORDER)); used += snprintf(buff + used, size - used, "\"%s_tcp_payloads_inorder\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_TCP_PAYLOADS_INORDER)); used += snprintf(buff + used, size - used, "\"%s_tcp_segments_reordered\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_TCP_SEGMENTS_REORDERED)); used += snprintf(buff + used, size - used, "\"%s_tcp_payloads_reordered\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_TCP_PAYLOADS_REORDERED)); used += snprintf(buff + used, size - used, "\"%s_tcp_segments_buffered\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_TCP_SEGMENTS_BUFFERED)); used += snprintf(buff + used, size - used, "\"%s_tcp_payloads_buffered\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_TCP_PAYLOADS_BUFFERED)); used += snprintf(buff + used, size - used, "\"%s_tcp_segments_released\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_TCP_SEGMENTS_RELEASED)); used += snprintf(buff + used, size - used, "\"%s_tcp_payloads_released\":%" PRIu64 ",", str[i], session_get_stat(sess, dir[i], STAT_TCP_PAYLOADS_RELEASED)); } used += snprintf(buff + used, size - used, "\"%s_first_packet\":\"%p\"", str[i], session_get_first_packet(sess, dir[i])); if (i == 0) { used += snprintf(buff + used, size - used, ","); } } used += snprintf(buff + used, size - used, "}"); } return used; }