This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
stellar-stellar/infra/monitor/enforcer/show_session_enforcer.c
2024-11-07 18:30:58 +08:00

819 lines
30 KiB
C

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <assert.h>
#include "stellar/session.h"
#include "stellar/monitor.h"
#include "session_manager/session_manager_rte.h"
#include "monitor/monitor_utils.h"
#include "monitor/monitor_rpc.h"
#include "sds/sds.h"
#include "session_manager/session_internal.h"
// temp add
extern struct session_manager_rte *session_manager_get_runtime(struct session_manager *sess_mgr, uint16_t thread_id);
#define SHOW_SESSION_BRIEF_LIMIT_DEFAULT 10
#define SHOW_SESSION_BRIEF_LIMIT_MAX 1000
#define SHOW_SESSION_BRIEF_SCAN_COUNT_DEFAULT 100
/* show session brief */
struct stm_show_session_brief_opt
{
#define SESSION_SCAN_SNET (1 << 25)
#define SESSION_SCAN_DNET (1 << 26)
#define SESSION_SCAN_ID (1 << 27)
#define SESSION_SCAN_CURSOR (1 << 28)
#define SESSION_SCAN_COUNT (1 << 29)
#define SESSION_SCAN_LIMIT (1 << 30)
struct session_scan_opts scan_opt;
uint32_t limit;
};
/* show session detail <id> */
struct stm_show_session_detail_opt
{
uint64_t sess_id;
int thread_idx; // todo, not used now
};
struct show_session_brief_result
{
uint64_t sid;
int thread_index;
enum session_state state;
enum session_type protocol;
time_t create_time_in_sec;
time_t last_pkt_time_in_sec;
uint8_t flow_dir;
struct tuple6 addr;
} __attribute__((packed));
struct show_session_brief_array
{
size_t array_num;
struct show_session_brief_result array[0]; /* Continuous memory, array_num * sizeof(struct show_session_brief_result) */
} __attribute__((packed));
struct show_session_detail_result
{
struct show_session_brief_result sess_brief;
unsigned long long sess_stat[MAX_FLOW_TYPE][MAX_STAT];
enum session_direction direction; // in or out
unsigned char application; // L7 appid
// todo, other info
} __attribute__((packed));
static void stm_session_brief_cli_args_set_default(struct stm_show_session_brief_opt *show_opt)
{
memset(show_opt, 0, sizeof(struct stm_show_session_brief_opt));
show_opt->limit = SHOW_SESSION_BRIEF_LIMIT_DEFAULT;
show_opt->scan_opt.cursor = 0;
show_opt->scan_opt.count = SHOW_SESSION_BRIEF_SCAN_COUNT_DEFAULT;
}
static uint32_t stm_session_brief_cli_args_get_flags(const char *para_name)
{
if (NULL == para_name)
{
return 0;
}
if (strncasecmp(para_name, "id", 2) == 0)
{
return SESSION_SCAN_ID;
}
else if (strncasecmp(para_name, "cursor", 6) == 0)
{
return SESSION_SCAN_CURSOR;
}
else if (strncasecmp(para_name, "count", 5) == 0)
{
return SESSION_SCAN_COUNT;
}
else if (strncasecmp(para_name, "state", 5) == 0)
{
return SESSION_SCAN_STATE;
}
else if (strncasecmp(para_name, "sip", 3) == 0)
{
return SESSION_SCAN_SIP;
}
else if (strncasecmp(para_name, "dip", 3) == 0)
{
return SESSION_SCAN_DIP;
}
else if (strncasecmp(para_name, "snet", 4) == 0)
{
return SESSION_SCAN_SNET;
}
else if (strncasecmp(para_name, "dnet", 4) == 0)
{
return SESSION_SCAN_DNET;
}
else if (strncasecmp(para_name, "sport", 5) == 0)
{
return SESSION_SCAN_SPORT;
}
else if (strncasecmp(para_name, "dport", 5) == 0)
{
return SESSION_SCAN_DPORT;
}
else if (strncasecmp(para_name, "protocol", 8) == 0)
{
return SESSION_SCAN_TYPE;
}
else if (strncasecmp(para_name, "limit", 5) == 0)
{
return SESSION_SCAN_LIMIT;
}
else if (strncasecmp(para_name, "create-time-in", strlen("create-time-in")) == 0)
{
return SESSION_SCAN_CREATE_TIME;
}
else if (strncasecmp(para_name, "last-pkt-time-in", strlen("last-pkt-time-in")) == 0)
{
return SESSION_SCAN_LASPKT_TIME;
}
return 0;
}
static enum session_state show_session_state_pton(const char *state_str)
{
if (strncasecmp(state_str, "opening", strlen("opening")) == 0)
{
return SESSION_STATE_OPENING;
}
else if (strncasecmp(state_str, "active", strlen("active")) == 0)
{
return SESSION_STATE_ACTIVE;
}
else if (strncasecmp(state_str, "closing", strlen("closing")) == 0)
{
return SESSION_STATE_CLOSING;
}
return MAX_STATE;
}
static uint32_t show_session_ipaddr_ntop(const char *ip_string, struct session_scan_opts *scan_opt, uint32_t flag)
{
uint32_t addr_family;
if (SESSION_SCAN_SIP == flag)
{
addr_family = stm_inet_pton(ip_string, &scan_opt->src_addr[0].v4, &scan_opt->src_addr[0].v6);
}
else
{
addr_family = stm_inet_pton(ip_string, &scan_opt->dst_addr[0].v4, &scan_opt->dst_addr[0].v6);
}
if (addr_family == 0)
{
return 0;
}
if (AF_INET == addr_family)
{
if (SESSION_SCAN_SIP == flag)
{
scan_opt->src_addr[1].v4 = scan_opt->src_addr[0].v4;
}
else
{
scan_opt->dst_addr[1].v4 = scan_opt->dst_addr[0].v4;
}
}
else
{
if (SESSION_SCAN_SIP == flag)
{
scan_opt->src_addr[1].v6 = scan_opt->src_addr[0].v6;
}
else
{
scan_opt->dst_addr[1].v6 = scan_opt->dst_addr[0].v6;
}
}
return addr_family;
}
static sds show_session_cli_args_sanity_check(const struct stm_show_session_brief_opt *brief_opt)
{
uint32_t flags = brief_opt->scan_opt.flags;
sds ss = sdsempty();
if ((flags & SESSION_SCAN_SIP) && (flags & SESSION_SCAN_SNET))
{
ss = sdscatprintf(ss, "error: the 'sip' and 'snet' options conflict!");
return ss;
}
if ((flags & SESSION_SCAN_DIP) && (flags & SESSION_SCAN_DNET))
{
ss = sdscatprintf(ss, "error: the 'dip' and 'dnet' options conflict!");
return ss;
}
return ss;
}
static int show_session_is_help(int argc, char *argv[])
{
for (int i = 0; i < argc; i++)
{
if (strncasecmp(argv[i], "help", 4) == 0)
{
return 1;
}
if (strncasecmp(argv[i], "--help", 6) == 0)
{
return 1;
}
if (strncasecmp(argv[i], "-h", 2) == 0)
{
return 1;
}
}
return 0;
}
static struct monitor_reply *show_session_brief_usage(void)
{
sds ss = sdsempty();
ss = sdscatprintf(ss, "Usage: show session brief [options]\n");
ss = sdscatprintf(ss, "Options:\n");
ss = sdscatprintf(ss, " -h, --help help\tdisplay usage information and exit\n");
ss = sdscatprintf(ss, " cursor <cursor>\n");
ss = sdscatprintf(ss, " count <count>\n");
ss = sdscatprintf(ss, " state <opening|active|closing>\n");
ss = sdscatprintf(ss, " protocol <tcp|udp>\n");
ss = sdscatprintf(ss, " sip <source ip>\n");
ss = sdscatprintf(ss, " dip <destination ip>\n");
ss = sdscatprintf(ss, " sport <source port>\n");
ss = sdscatprintf(ss, " dport <destination port>\n");
ss = sdscatprintf(ss, " snet <source network/netmask>\texample 192.168.1.0/24\n");
ss = sdscatprintf(ss, " dnet <destination network/netmask>\texample 1234::abcd/48\n");
ss = sdscatprintf(ss, " create-time-in <last-N-[seconds|minutes|hours|days]>\texample last-1-hours\n");
ss = sdscatprintf(ss, " last-pkt-time-in <last-N-[seconds|minutes|hours|days]>\texample last-7-days\n");
ss = sdscatprintf(ss, " limit <limit>\n");
struct monitor_reply *reply = monitor_reply_new_string("%s", ss);
sdsfree(ss);
return reply;
}
static struct monitor_reply *show_session_detail_usage(void)
{
sds ss = sdsempty();
ss = sdscatprintf(ss, "Usage: show session detial [options]\n");
ss = sdscatprintf(ss, "Options:\n");
ss = sdscatprintf(ss, " -h, --help help\tdisplay usage information and exit\n");
ss = sdscatprintf(ss, " id <session id>\n");
struct monitor_reply *reply = monitor_reply_new_string("%s", ss);
sdsfree(ss);
return reply;
}
/*
"show session ..." command args parser
return:
NULL: success, brief_opt is filled
not NULL: error message
*/
static struct monitor_reply *show_session_brief_cli_args_parse(int argc, char *argv[], struct stm_show_session_brief_opt *brief_opt)
{
uint32_t ipaddr_family = 0, history_ipaddr_family = 0;
sds ss = NULL;
uint32_t flag;
struct monitor_reply *error_reply = NULL;
const char *opt_name, *opt_value;
stm_session_brief_cli_args_set_default(brief_opt);
int i = 3; // skip "show session brief"
while (i < argc)
{
ipaddr_family = 0;
opt_name = argv[i];
flag = stm_session_brief_cli_args_get_flags(opt_name);
if (i + 1 >= argc)
{
error_reply = monitor_reply_new_error("option %s requires an argument\n", opt_name);
return error_reply;
}
opt_value = argv[++i];
switch (flag)
{
case SESSION_SCAN_TYPE:
{
if (strncasecmp(opt_value, "tcp", 3) == 0)
{
brief_opt->scan_opt.type = SESSION_TYPE_TCP;
}
else if (strncasecmp(opt_value, "udp", 3) == 0)
{
brief_opt->scan_opt.type = SESSION_TYPE_UDP;
}
else
{
error_reply = monitor_reply_new_error("unsupported protocol type: %s. should be <tcp|udp>\n", opt_value);
goto error_exit;
}
}
break;
case SESSION_SCAN_STATE:
{
enum session_state tmp_state = show_session_state_pton(opt_value);
if (tmp_state == MAX_STATE)
{
error_reply = monitor_reply_new_error("unrecognized session state: %s. should be <opening|active|closing>\n", opt_value);
goto error_exit;
}
brief_opt->scan_opt.state = tmp_state;
}
break;
// case SESSION_SCAN_ID:
// if (stm_string_isdigit(opt_value) == 0)
// {
// *error_reply = monitor_reply_new_error("invalid session id: %s. should be integer in range: <1 - UINT64_MAX>\n", opt_value);
// goto error_exit;
// }
// show_opt->detail_opt.sess_id = strtoull(opt_value, NULL, 10);
// break;
case SESSION_SCAN_SIP:
ipaddr_family = show_session_ipaddr_ntop(opt_value, &brief_opt->scan_opt, SESSION_SCAN_SIP);
if (ipaddr_family == 0)
{
error_reply = monitor_reply_new_error("invalid sip address: %s\n", opt_value);
goto error_exit;
}
brief_opt->scan_opt.addr_family = ipaddr_family;
break;
case SESSION_SCAN_DIP:
ipaddr_family = show_session_ipaddr_ntop(opt_value, &brief_opt->scan_opt, SESSION_SCAN_DIP);
if (ipaddr_family == 0)
{
error_reply = monitor_reply_new_error("invalid dip address: %s\n", opt_value);
goto error_exit;
}
brief_opt->scan_opt.addr_family = ipaddr_family;
break;
case SESSION_SCAN_SNET:
{
uint32_t ipv4addr, ipv4mask;
struct in6_addr ipv6addr, ipv6mask;
ipaddr_family = stm_ip_cidr_pton(opt_value, &ipv4addr, &ipv4mask, &ipv6addr, &ipv6mask);
if (ipaddr_family == 0)
{
error_reply = monitor_reply_new_error("invalid snet CIDR address: %s\n", opt_value);
goto error_exit;
}
if (AF_INET == ipaddr_family)
{
uint32_t ipv4_range[2];
stm_ipv4_cidr_to_range(ipv4addr, ipv4mask, ipv4_range);
brief_opt->scan_opt.src_addr[0].v4.s_addr = ipv4_range[0];
brief_opt->scan_opt.src_addr[1].v4.s_addr = ipv4_range[1];
}
else
{
struct in6_addr ipv6_range[2];
stm_ipv6_cidr_to_range(&ipv6addr, &ipv6mask, ipv6_range);
brief_opt->scan_opt.src_addr[0].v6 = ipv6_range[0];
brief_opt->scan_opt.src_addr[1].v6 = ipv6_range[1];
}
brief_opt->scan_opt.addr_family = ipaddr_family;
flag = SESSION_SCAN_SIP;
}
break;
case SESSION_SCAN_DNET:
{
uint32_t ipv4addr, ipv4mask;
struct in6_addr ipv6addr, ipv6mask;
ipaddr_family = stm_ip_cidr_pton(opt_value, &ipv4addr, &ipv4mask, &ipv6addr, &ipv6mask);
if (ipaddr_family == 0)
{
error_reply = monitor_reply_new_error("invalid dnet CIDR address: %s\n", opt_value);
goto error_exit;
}
if (AF_INET == ipaddr_family)
{
uint32_t ipv4_range[2];
stm_ipv4_cidr_to_range(ipv4addr, ipv4mask, ipv4_range);
brief_opt->scan_opt.dst_addr[0].v4.s_addr = ipv4_range[0];
brief_opt->scan_opt.dst_addr[1].v4.s_addr = ipv4_range[1];
}
else
{
struct in6_addr ipv6_range[2];
stm_ipv6_cidr_to_range(&ipv6addr, &ipv6mask, ipv6_range);
brief_opt->scan_opt.dst_addr[0].v6 = ipv6_range[0];
brief_opt->scan_opt.dst_addr[1].v6 = ipv6_range[1];
}
brief_opt->scan_opt.addr_family = ipaddr_family;
flag = SESSION_SCAN_DIP;
}
break;
case SESSION_SCAN_SPORT:
{
if (stm_string_isdigit(opt_value) == 0)
{
error_reply = monitor_reply_new_error("illegal sport: %s. should be integer\n", opt_value);
goto error_exit;
}
int tmp_val = atoi(opt_value);
if (tmp_val <= 0 || tmp_val > 65535)
{
error_reply = monitor_reply_new_error("illegal sport: %s. should be <1-65535>\n", opt_value);
goto error_exit;
}
brief_opt->scan_opt.src_port = htons((unsigned short)tmp_val);
}
break;
case SESSION_SCAN_DPORT:
{
if (stm_string_isdigit(opt_value) == 0)
{
error_reply = monitor_reply_new_error("illegal sport: %s. should be integer\n", opt_value);
goto error_exit;
}
int tmp_val = atoi(opt_value);
if (tmp_val <= 0 || tmp_val > 65535)
{
error_reply = monitor_reply_new_error("illegal sport: %s. should be <1-65535>\n", opt_value);
goto error_exit;
}
brief_opt->scan_opt.dst_port = htons((unsigned short)tmp_val);
}
break;
case SESSION_SCAN_CURSOR:
brief_opt->scan_opt.cursor = strtoul(opt_value, NULL, 10);
break;
case SESSION_SCAN_COUNT:
brief_opt->scan_opt.count = strtoul(opt_value, NULL, 10);
if (brief_opt->scan_opt.count == 0)
{
error_reply = monitor_reply_new_error("illegal count: %s. should be <1 - UINT32_MAX>\n", opt_value);
goto error_exit;
}
break;
case SESSION_SCAN_LIMIT:
brief_opt->limit = strtoul(opt_value, NULL, 10);
if (brief_opt->limit == 0 || brief_opt->limit > SHOW_SESSION_BRIEF_LIMIT_MAX)
{
error_reply = monitor_reply_new_error("illegal limit: %s. should be <1 - %u>\n", opt_value, SHOW_SESSION_BRIEF_LIMIT_MAX);
goto error_exit;
}
break;
case SESSION_SCAN_CREATE_TIME:
{
time_t tmp_range[2] = {0, 0};
if (stm_time_range_pton(opt_value, time(NULL), tmp_range) < 0)
{
error_reply = monitor_reply_new_error("invalid create-time-in: %s. \r\nsyntax: <last-N-[seconds|minutes|hours|days]>\n", opt_value);
goto error_exit;
}
brief_opt->scan_opt.create_time_ms[0] = tmp_range[0] * 1000; // second to ms
brief_opt->scan_opt.create_time_ms[1] = tmp_range[1] * 1000; // second to ms
}
break;
case SESSION_SCAN_LASPKT_TIME:
{
time_t tmp_range[2] = {0, 0};
if (stm_time_range_pton(opt_value, time(NULL), tmp_range) < 0)
{
error_reply = monitor_reply_new_error("invalid last-pkt-time-in: %s. \r\nsyntax: <last-N-[seconds|minutes|hours|days]>\n", opt_value);
goto error_exit;
}
brief_opt->scan_opt.laspkt_time_ms[0] = tmp_range[0] * 1000; // second to ms
brief_opt->scan_opt.laspkt_time_ms[1] = tmp_range[1] * 1000; // second to ms
}
break;
default:
error_reply = monitor_reply_new_error("unrecognized params: %s \n", opt_name);
return error_reply;
}
if ((history_ipaddr_family != 0) && (ipaddr_family != 0) && (history_ipaddr_family != ipaddr_family))
{
error_reply = monitor_reply_new_error("contradictory ip version, expression rejects all sessions!\n");
goto error_exit;
}
history_ipaddr_family = ipaddr_family;
i++; // to next option
brief_opt->scan_opt.flags |= flag;
}
ss = show_session_cli_args_sanity_check(brief_opt);
if (ss && sdslen(ss) > 0)
{
error_reply = monitor_reply_new_error("%s\n", ss);
sdsfree(ss);
goto error_exit;
}
sdsfree(ss);
error_reply = NULL;
return NULL;
error_exit:
return error_reply;
}
static void get_single_session_brief(struct session *sess, int thread_id, struct show_session_brief_result *brief)
{
brief->thread_index = thread_id;
brief->state = session_get_current_state(sess);
brief->protocol = session_get_type(sess);
session_is_symmetric(sess, &brief->flow_dir);
brief->create_time_in_sec = session_get_timestamp(sess, SESSION_TIMESTAMP_START) / 1000; // ms to sec
brief->last_pkt_time_in_sec = session_get_timestamp(sess, SESSION_TIMESTAMP_LAST) / 1000; // ms to sec
const struct tuple6 *tp6 = session_get_tuple6(sess);
memcpy(&brief->addr, tp6, sizeof(struct tuple6));
}
struct iovec show_session_brief_on_worker_thread_rpc_cb(int thread_idx, struct iovec request, void *args)
{
struct iovec response = {};
assert(request.iov_len == sizeof(struct stm_show_session_brief_opt));
struct stm_show_session_brief_opt *show_brief_opt = (struct stm_show_session_brief_opt *)request.iov_base;
uint64_t session_id_array[SHOW_SESSION_BRIEF_LIMIT_MAX];
uint64_t session_id_array_count = SHOW_SESSION_BRIEF_LIMIT_MAX;
struct session_manager *sess_mgr = stellar_module_get_session_manager((struct stellar_module_manager *)args);
struct session_manager_rte *sess_mgr_rt = session_manager_get_runtime(sess_mgr, thread_idx);
session_id_array_count = session_manager_rte_scan_session(sess_mgr_rt, &show_brief_opt->scan_opt, session_id_array, show_brief_opt->limit);
if (session_id_array_count == 0)
{
// no session match the filter params, but no error! still need to build a empty reply!
// go on !!!
response.iov_base = NULL;
response.iov_len = 0;
return response;
}
struct show_session_brief_result *brief_result_array = (struct show_session_brief_result *)calloc(session_id_array_count, sizeof(struct show_session_brief_result));
for (uint32_t i = 0; i < session_id_array_count; i++)
{
struct session *sess = session_manager_rte_lookup_session_by_id(sess_mgr_rt, session_id_array[i]);
assert(sess != NULL);
get_single_session_brief(sess, thread_idx, &brief_result_array[i]);
brief_result_array[i].sid = session_id_array[i];
}
response.iov_base = brief_result_array;
response.iov_len = session_id_array_count;
return response;
}
static sds session_brief_to_readable(const struct show_session_brief_result *sess_brief)
{
char time_buf[64];
char addr_buf[256];
sds ss = sdsempty();
strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", localtime(&sess_brief->create_time_in_sec));
ss = sdscatprintf(ss, "%-6d %-21lu %-5s %-8s %-20s %s", sess_brief->thread_index, sess_brief->sid,
(sess_brief->protocol == SESSION_TYPE_TCP) ? "tcp" : "udp",
stm_session_state_ntop(sess_brief->state), time_buf,
stm_get0_readable_session_addr(&sess_brief->addr, addr_buf, sizeof(addr_buf)));
return ss;
}
static struct monitor_reply *show_session_brief_cmd_cb(struct stellar_monitor *monitor, int argc, char *argv[], void *arg)
{
struct stm_show_session_brief_opt brief_opt = {};
if (show_session_is_help(argc, argv))
{
return show_session_brief_usage();
}
struct monitor_reply *error_reply = show_session_brief_cli_args_parse(argc, argv, &brief_opt);
if (error_reply != NULL)
{
return error_reply;
}
struct monitor_reply *cmd_reply;
struct stellar_module_manager *mod_mgr = (struct stellar_module_manager *)arg;
int thread_num = stellar_module_manager_get_max_thread_num(mod_mgr);
struct iovec request;
request.iov_base = (void *)&brief_opt;
request.iov_len = sizeof(struct stm_show_session_brief_opt);
struct iovec response[thread_num];
memset(response, 0, sizeof(response));
size_t tot_sess_id_num = 0;
sds ss = sdsempty();
ss = sdscatprintf(ss, "%-6s %-21s %-5s %-8s %-20s %s\r\n", "thread", "session-id", "proto", "state", "create-time", "tuple4(sip:sport-dip:dport)");
ss = sdscatprintf(ss, "--------------------------------------------------------------------------------------------\r\n");
for (int i = 0; i < thread_num; i++)
{
response[i] = monitor_worker_thread_rpc(monitor, i, request, show_session_brief_on_worker_thread_rpc_cb, mod_mgr);
if (response[i].iov_base == NULL || response[i].iov_len == 0) // empty result
{
continue;
}
struct show_session_brief_result *brief_res_array = (struct show_session_brief_result *)(response[i].iov_base);
tot_sess_id_num += response[i].iov_len; // session_id_array_count
for (size_t j = 0; j < response[i].iov_len; j++)
{
ss = sdscatprintf(ss, "%s\n", session_brief_to_readable(&brief_res_array[j]));
}
if (tot_sess_id_num >= brief_opt.limit)
{
break;
}
}
if (tot_sess_id_num == 0)
{
cmd_reply = monitor_reply_new_string("No session found");
goto empty_result;
}
cmd_reply = monitor_reply_new_string("%s", ss);
empty_result:
sdsfree(ss);
for (int i = 0; i < thread_num; i++)
{
if (response[i].iov_base)
{
free(response[i].iov_base);
}
}
return cmd_reply;
}
/*
todo: add thread id,
fast patch, avoid traversing all worker threads
*/
static struct monitor_reply *show_session_detail_cli_args_parse(int argc, char *argv[], struct stm_show_session_detail_opt *detail_opt)
{
if (argc < 4)
{
return monitor_reply_new_error("missing session id\n");
}
if (argc > 5)
{
return monitor_reply_new_error("too many arguments\n");
}
if (strncasecmp(argv[3], "id", 2) != 0)
{
return monitor_reply_new_error("missing session id\n");
}
if (stm_string_isdigit(argv[4]) == 0)
{
return monitor_reply_new_error("invalid session id: %s. should be integer in range: <1 - UINT64_MAX>\n", argv[4]);
}
detail_opt->sess_id = strtoull(argv[4], NULL, 10);
return NULL;
}
struct iovec show_session_detail_on_worker_thread_rpc_cb(int thread_idx, struct iovec request, void *args)
{
struct iovec response = {};
assert(request.iov_len == sizeof(struct stm_show_session_detail_opt));
struct stm_show_session_detail_opt *detail_opt = (struct stm_show_session_detail_opt *)request.iov_base;
struct session_manager *sess_mgr = stellar_module_get_session_manager((struct stellar_module_manager *)args);
struct session_manager_rte *sess_mgr_rt = session_manager_get_runtime(sess_mgr, thread_idx);
struct session *sess = session_manager_rte_lookup_session_by_id(sess_mgr_rt, detail_opt->sess_id);
if (NULL == sess)
{
return response;
}
struct show_session_detail_result *detail_res = (struct show_session_detail_result *)calloc(1, sizeof(struct show_session_detail_result));
get_single_session_brief(sess, thread_idx, &detail_res->sess_brief);
detail_res->sess_brief.sid = detail_opt->sess_id;
detail_res->direction = session_get_direction(sess);
// todo, get some exact stat, not all
memcpy(detail_res->sess_stat, sess->stats, sizeof(detail_res->sess_stat));
// todo, get application info
response.iov_base = detail_res;
response.iov_len = sizeof(struct show_session_detail_result);
return response;
}
static sds session_detail_to_readable(const struct show_session_detail_result *sess_detail)
{
char addr_buf[256];
sds ss = sdsempty();
char time_buf[64];
#define SHOW_SESSION_DETAIL_NAME_FORMAT "%-30s"
#define SHOW_SESSION_DETAIL_VALUE_FORMAT "%llu"
const char *flow_str[MAX_FLOW_TYPE] = {"C2S flow", "S2C flow"};
ss = sdscatprintf(ss, "%-15s: %lu\r\n", "session-id", sess_detail->sess_brief.sid);
ss = sdscatprintf(ss, "%-15s: %d\r\n", "thread", sess_detail->sess_brief.thread_index);
ss = sdscatprintf(ss, "%-15s: %s\r\n", "state", stm_session_state_ntop(sess_detail->sess_brief.state));
ss = sdscatprintf(ss, "%-15s: %s\r\n", "protocol", (sess_detail->sess_brief.protocol == SESSION_TYPE_TCP) ? "tcp" : "udp");
strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", localtime(&sess_detail->sess_brief.create_time_in_sec));
ss = sdscatprintf(ss, "%-15s: %s\r\n", "create-time", time_buf);
strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", localtime(&sess_detail->sess_brief.last_pkt_time_in_sec));
ss = sdscatprintf(ss, "%-15s: %s\r\n", "last-pkt-time", time_buf);
ss = sdscatprintf(ss, "%-15s: %s\r\n", "tuple4", stm_get0_readable_session_addr(&sess_detail->sess_brief.addr, addr_buf, sizeof(addr_buf)));
ss = sdscatprintf(ss, "%-15s: %s\r\n", "symmetric", stm_session_flow_dir_ntop(sess_detail->sess_brief.flow_dir));
ss = sdscatprintf(ss, "%-15s: %s\r\n", "direction", sess_detail->direction == SESSION_DIRECTION_INBOUND ? "INBOUND" : "OUTBOUND");
ss = sdscatprintf(ss, "statistics:\r\n");
char printf_format[256];
snprintf(printf_format, sizeof(printf_format), "\t%s : %s\r\n", SHOW_SESSION_DETAIL_NAME_FORMAT, SHOW_SESSION_DETAIL_VALUE_FORMAT);
for (int flow = 0; flow < MAX_FLOW_TYPE; flow++)
{
ss = sdscatprintf(ss, " %s:\r\n", flow_str[flow]);
ss = sdscatprintf(ss, printf_format, "raw_packets_received", sess_detail->sess_stat[flow][STAT_RAW_PACKETS_RECEIVED]);
ss = sdscatprintf(ss, printf_format, "raw_bytes_received", sess_detail->sess_stat[flow][STAT_RAW_BYTES_RECEIVED]);
ss = sdscatprintf(ss, printf_format, "duplicate_packets_bypass", sess_detail->sess_stat[flow][STAT_DUPLICATE_PACKETS_BYPASS]);
ss = sdscatprintf(ss, printf_format, "injected_packets", sess_detail->sess_stat[flow][STAT_INJECTED_PACKETS_SUCCESS]);
ss = sdscatprintf(ss, printf_format, "injected_bytes", sess_detail->sess_stat[flow][STAT_INJECTED_BYTES_SUCCESS]);
if (SESSION_TYPE_TCP == sess_detail->sess_brief.protocol)
{
ss = sdscatprintf(ss, printf_format, "tcp_segments_retransmit", sess_detail->sess_stat[flow][STAT_TCP_SEGMENTS_RETRANSMIT]);
ss = sdscatprintf(ss, printf_format, "tcp_payloads_retransmit", sess_detail->sess_stat[flow][STAT_TCP_PAYLOADS_RETRANSMIT]);
ss = sdscatprintf(ss, printf_format, "tcp_segments_reordered", sess_detail->sess_stat[flow][STAT_TCP_SEGMENTS_REORDERED]);
ss = sdscatprintf(ss, printf_format, "tcp_payloads_reordered", sess_detail->sess_stat[flow][STAT_TCP_PAYLOADS_REORDERED]);
}
}
// todo:
ss = sdscatprintf(ss, "\r\n \033[31mtodo: add security policy rule id, HTTP URL, Server FQDN, etc... \033[0m");
return ss;
}
static struct monitor_reply *show_session_detail_cmd_cb(struct stellar_monitor *stm, int argc, char *argv[], void *arg)
{
struct stm_show_session_detail_opt detail_opt = {};
detail_opt.thread_idx = -1;
if (show_session_is_help(argc, argv))
{
return show_session_detail_usage();
}
struct monitor_reply *error_reply = show_session_detail_cli_args_parse(argc, argv, &detail_opt);
if (error_reply != NULL)
{
return error_reply;
}
struct monitor_reply *cmd_reply;
struct stellar_module_manager *mod_mgr = (struct stellar_module_manager *)arg;
int thread_num = stellar_module_manager_get_max_thread_num(mod_mgr);
struct iovec request;
request.iov_base = (void *)&detail_opt;
request.iov_len = sizeof(struct stm_show_session_detail_opt);
struct iovec response[thread_num];
memset(response, 0, sizeof(response));
size_t tot_sess_id_num = 0;
sds ss = sdsempty();
for (int i = 0; i < thread_num; i++)
{
if (detail_opt.thread_idx != -1 && detail_opt.thread_idx != i)
{
continue;
}
response[i] = monitor_worker_thread_rpc(stm, i, request, show_session_detail_on_worker_thread_rpc_cb, mod_mgr);
if (response[i].iov_base == NULL || response[i].iov_len == 0) // empty result
{
continue;
}
struct show_session_detail_result *detail_res = (struct show_session_detail_result *)(response[i].iov_base);
ss = sdscatprintf(ss, "%s\n", session_detail_to_readable(detail_res));
tot_sess_id_num++;
break;
}
if (tot_sess_id_num == 0)
{
cmd_reply = monitor_reply_new_string("No session found by id %lu", detail_opt.sess_id);
goto empty_result;
}
cmd_reply = monitor_reply_new_string("%s", ss);
empty_result:
sdsfree(ss);
for (int i = 0; i < thread_num; i++)
{
if (response[i].iov_base)
{
free(response[i].iov_base);
}
}
return cmd_reply;
}
int show_session_enforcer_init(struct stellar_module_manager *mod_mgr, struct stellar_monitor *stm)
{
monitor_register_cmd(stm, "show session brief", show_session_brief_cmd_cb, "readonly",
"[sip | dip | sport | dport | protocol | state | cursor | count | limit | create-time-in | last-pkt-time-in ]",
"Show session brief information", (void *)mod_mgr);
monitor_register_cmd(stm, "show session detail", show_session_detail_cmd_cb, "readonly",
"id <id>",
"Show session verbose information", (void *)mod_mgr);
return 0;
}