From 20b5a4ac1148a35df12c84f9b2a4d38ddc6aee65 Mon Sep 17 00:00:00 2001 From: Luca Deri Date: Mon, 3 Nov 2025 23:41:27 +0100 Subject: [PATCH] Added support for %TCP_STATS_SRC_TO_DST %TCP_STATS_DST_TO_SRC (WiP) --- include/Flow.h | 13 +- include/ParsedFlow.h | 9 +- include/ntop_flow.h | 3 + scripts/locales/en.lua | 2 + scripts/lua/flow_details.lua | 2 + scripts/lua/modules/flow_consts.lua | 2 + scripts/lua/modules/historical_flow_utils.lua | 15 +- scripts/lua/modules/template.lua | 5 +- src/Flow.cpp | 62 ++++- src/ParsedFlow.cpp | 241 +++++++++--------- src/ParserInterface.cpp | 6 +- src/ZMQParserInterface.cpp | 11 +- tests/e2e | 2 +- 13 files changed, 240 insertions(+), 133 deletions(-) diff --git a/include/Flow.h b/include/Flow.h index 3f66d53a9f..df62d9b577 100644 --- a/include/Flow.h +++ b/include/Flow.h @@ -32,6 +32,11 @@ typedef struct { u_int64_t last, next; } TCPSeqNum; +typedef struct { + /* 0...254, 255 means more events */ + u_int8_t num_syn, num_rst, num_fin, num_zero_window; +} tcp_stats; + typedef struct { /* TCP stats */ TCPSeqNum tcp_seq_s2d, tcp_seq_d2s; @@ -212,6 +217,8 @@ private: } external_alert; char *tcp_fingerprint; + tcp_stats tcp_stats_src2dst, tcp_stats_dst2src; + bool trigger_immediate_periodic_update; /* needed to process external alerts */ time_t next_call_periodic_update; /* The time at which the periodic lua script on this flow shall be called */ @@ -417,7 +424,8 @@ private: void processHostName(char *host_name); #endif void updateMac(); - + void decodeTCPstats(u_int32_t v, tcp_stats *stats); + public: Flow(NetworkInterface *_iface, int32_t iface_idx, u_int16_t _vlanId, @@ -998,6 +1006,7 @@ public: bool asListElement); void lua_get_min_info(lua_State *vm); void lua_duration_info(lua_State *vm); + void lua_dump_tcp_stats(lua_State *vm, const tcp_stats *s, const char *label) const; void lua_snmp_info(lua_State *vm); void lua_device_protocol_allowed_info(lua_State *vm); void lua_get_flow_connection_state(lua_State *vm); @@ -1568,6 +1577,8 @@ public: inline void incNumProcessedPkts() { numFlowProcessedPkts++; } inline void setNumPktsMarker() { numPktsMarkerSet = numFlowProcessedPkts; } #endif + + void updateTCPStats(u_int32_t cli_stats, u_int32_t srv_stats); }; #endif /* _FLOW_H_ */ diff --git a/include/ParsedFlow.h b/include/ParsedFlow.h index c897a64838..b6aa3918f5 100644 --- a/include/ParsedFlow.h +++ b/include/ParsedFlow.h @@ -41,7 +41,8 @@ class ParsedFlow : public ParsedFlowCore, public ParsedeBPF { char *l7_json; char *smtp_rcp_to, *smtp_mail_from; u_int32_t src_ip_addr_pre_nat, dst_ip_addr_pre_nat, - src_ip_addr_post_nat, dst_ip_addr_post_nat; + src_ip_addr_post_nat, dst_ip_addr_post_nat; + u_int32_t tcp_stats_src_to_dst, tcp_stats_dst_to_src; u_int8_t tls_unsafe_cipher, flow_verdict; ndpi_os os_hint; u_int16_t tls_cipher; @@ -178,9 +179,11 @@ class ParsedFlow : public ParsedFlowCore, public ParsedeBPF { inline void setQoEDst2Src(u_int8_t t) { qoe.dst_to_src = t; } inline void setOSHint(ndpi_os t) { os_hint = t; } inline ndpi_os getOSHint() { return(os_hint); } - + inline void setTCPStats(u_int32_t value, bool cli2src) { + if(cli2src) tcp_stats_src_to_dst = value; else tcp_stats_dst_to_src = value; + } u_int32_t get_private_flow_id(); - + inline u_int32_t get_tcp_stats(bool cli2srv) { return(cli2srv ? tcp_stats_src_to_dst : tcp_stats_dst_to_src); } void print(); }; diff --git a/include/ntop_flow.h b/include/ntop_flow.h index ab2451fd87..fb76dd4aea 100644 --- a/include/ntop_flow.h +++ b/include/ntop_flow.h @@ -572,6 +572,9 @@ #define L7_DOMAIN_INFO NTOP_BASE_ID+591 #define TCP_FINGERPRINT NTOP_BASE_ID+597 #define NPROBE_SOURCE_ID NTOP_BASE_ID+600 /* Unique nProbe identifier */ +#define TCP_STATS_SRC_TO_DST NTOP_BASE_ID+601 +#define TCP_STATS_DST_TO_SRC NTOP_BASE_ID+602 + /* sFlow Counter Fields */ #define SFLOW_DEVICE_IP 0 diff --git a/scripts/locales/en.lua b/scripts/locales/en.lua index afe11e604c..530ed0aeb9 100644 --- a/scripts/locales/en.lua +++ b/scripts/locales/en.lua @@ -4199,6 +4199,8 @@ local lang = { ["ssdp_usn"] = "SSDP USN", ["suricata_app_proto"] = "Suricata Application Protocol", ["suricata_flow_id"] = "Suricata Flow ID", + ["tcp_stats_src_to_dst"] = "TCP Stats (client to server)", + ["tcp_stats_dst_to_src"] = "TCP Stats (server to client)", ["tcpAckNum"] = "TCP Ack Number", ["tcpSeqNum"] = "TCP Sequence Number", ["tcpUrgentPtr"] = "TCP Urgent Ptr", diff --git a/scripts/lua/flow_details.lua b/scripts/lua/flow_details.lua index 3b1e10c866..10e3870773 100644 --- a/scripts/lua/flow_details.lua +++ b/scripts/lua/flow_details.lua @@ -581,6 +581,8 @@ else getFlowLabel(flow, true, not ifstats.isViewed --[[ don't add hyperlinks, viewed interface don't have hosts --]], nil, nil, false --[[ add flags ]])) + -- tprint(flow["tcp_stats"]) + if (flow.periodic_flow) then print(" " .. i18n("periodic_flow") .. "") end diff --git a/scripts/lua/modules/flow_consts.lua b/scripts/lua/modules/flow_consts.lua index b2ee2a152a..fdadd2597b 100644 --- a/scripts/lua/modules/flow_consts.lua +++ b/scripts/lua/modules/flow_consts.lua @@ -191,6 +191,8 @@ flow_consts.flow_fields_description = { ["PAYLOAD_HASH"] = i18n("flow_fields_description.payload_hash"), ["SRC_AS_MAP"] = i18n("flow_fields_description.src_as_map"), ["DST_AS_MAP"] = i18n("flow_fields_description.dst_as_map"), + ["TCP_STATS_SRC_TO_DST"] = i18n("flow_fields_description.tcp_stats_src_to_dst"), + ["TCP_STATS_DST_TO_SRC"] = i18n("flow_fields_description.tcp_stats_dst_to_src"), -- BGP Update Listener ["SRC_AS_PATH_1"] = i18n("flow_fields_description.src_as_path_1"), diff --git a/scripts/lua/modules/historical_flow_utils.lua b/scripts/lua/modules/historical_flow_utils.lua index 854b5c15da..888a091995 100644 --- a/scripts/lua/modules/historical_flow_utils.lua +++ b/scripts/lua/modules/historical_flow_utils.lua @@ -171,6 +171,14 @@ end -- ##################################### +local function dt_format_tcp_stats(s) + -- TODO Implement + + return dt_format_generic(s) +end + +-- ##################################### + local function dt_format_info(info) if not isEmptyString(profile) then info = string.gsub(info, " ", "") @@ -1156,7 +1164,12 @@ local flow_columns = { ['DST_MAC'] = { tag = "srv_mac", dt_func = dt_format_mac, db_type = "Number", db_raw_type = "Uint64" }, ['COMMUNITY_ID'] = { tag = "community_id", format_func = format_flow_info, i18n = i18n("flow_fields_description.community_id"), order = 10, db_type = "String", db_raw_type = "String" }, ['CLIENT_FINGERPRINT'] = { tag = "cli_fingerprint", dt_func = dt_format_generic, order = 11, db_type = "String", db_raw_type = "String" }, - ['TCP_FINGERPRINT'] = { tag = "tcp_fingerprint", dt_func = dt_format_generic, order = 11, db_type = "String", db_raw_type = "String" }, + ['TCP_FINGERPRINT'] = { tag = "tcp_fingerprint", dt_func = dt_format_generic, order = 11, db_type = "String", db_raw_type = "String" }, + + + ['TCP_STATS_SRC_TO_DST'] = { tag = "tcp_stats_src_to_dst", dt_func = dt_format_tcp_stats, order = 11, db_type = "String", db_raw_type = "String" }, + ['TCP_STATS_SRC_TO_DST'] = { tag = "tcp_stats_src_to_dst", dt_func = dt_format_tcp_stats, order = 11, db_type = "String", db_raw_type = "String" }, + ['SRC_ASN'] = { tag = "cli_asn", simple_dt_func = simple_format_src_asn, db_type = "Number", db_raw_type = "Uint32" }, ['DST_ASN'] = { tag = "srv_asn", simple_dt_func = simple_format_dst_asn, db_type = "Number", db_raw_type = "Uint32" }, ['PROBE_IP'] = { tag = "probe_ip", dt_func = dt_format_probe, select_func = "IPv4NumToString", where_func = "IPv4StringToNum", db_type = "Number", db_raw_type = "Uint32" }, diff --git a/scripts/lua/modules/template.lua b/scripts/lua/modules/template.lua index 4ac6913233..be33031c0f 100644 --- a/scripts/lua/modules/template.lua +++ b/scripts/lua/modules/template.lua @@ -167,6 +167,9 @@ template = { ['FLOW_VERDICT'] = NTOP_BASE_ID+522, ['L7_RISK_SCORE'] = NTOP_BASE_ID+527, ['L7_INFO'] = NTOP_BASE_ID+539, + ['TCP_STATS_SRC_TO_DST'] = NTOP_BASE_ID+601, + ['TCP_STATS_DST_TO_SRC'] = NTOP_BASE_ID+602, + -- SIP ['SIP_CALL_ID'] = NTOP_BASE_ID+130, @@ -281,7 +284,7 @@ template = { ['DNS_TTL_ANSWER'] = NTOP_BASE_ID+352, ['DNS_RESPONSE'] = NTOP_BASE_ID+398, - -- TLS Protocol + -- TLS Protocol ['TLS_VERSION'] = NTOP_BASE_ID+495, ['TLS_CERT_NOT_BEFORE'] = NTOP_BASE_ID+496, ['TLS_CERT_AFTER'] = NTOP_BASE_ID+497, diff --git a/src/Flow.cpp b/src/Flow.cpp index c6d9bbfc70..7500151454 100644 --- a/src/Flow.cpp +++ b/src/Flow.cpp @@ -53,7 +53,9 @@ Flow::Flow(NetworkInterface *_iface, collected_qoe.src_to_dst = collected_qoe.dst_to_src = NTOP_QOE_UNKNOWN, has_collected_qoe = 0; tcp = NULL; c_mac_updated = s_mac_updated = false; - + memset(&tcp_stats_src2dst, 0, sizeof(tcp_stats_src2dst)); + memset(&tcp_stats_dst2src, 0, sizeof(tcp_stats_dst2src)); + #ifdef NTOPNG_PRO udp = NULL; #endif @@ -5294,9 +5296,9 @@ void Flow::updateTcpWindow(u_int16_t window, bool src2dst_direction) { /* The update depends on the direction of the flow */ if(window == 0) { if(src2dst_direction) - src2dst_tcp_zero_window = 1; + src2dst_tcp_zero_window = 1, tcp_stats_src2dst.num_zero_window++; else - dst2src_tcp_zero_window = 1; + dst2src_tcp_zero_window = 1, tcp_stats_dst2src.num_zero_window++; } } @@ -5435,6 +5437,18 @@ void Flow::updateTcpFlags(const struct bpf_timeval *when, u_int8_t flags, } else { /* Packet Interface */ + if((flags & TH_SYN) == TH_SYN) { + if(src2dst_direction) tcp_stats_src2dst.num_syn++; else tcp_stats_dst2src.num_syn++; + } + + if((flags & TH_FIN) == TH_FIN) { + if(src2dst_direction) tcp_stats_src2dst.num_fin++; else tcp_stats_dst2src.num_fin++; + } + + if((flags & TH_RST) == TH_RST) { + if(src2dst_direction) tcp_stats_src2dst.num_rst++; else tcp_stats_dst2src.num_rst++; + } + /* Update syn alerts counters. In case of cumulative flags, the AND is used as * possibly other flags can be present */ if(flags_3wh == TH_SYN) { @@ -7627,10 +7641,24 @@ void Flow::getSIPInfo(ndpi_serializer *serializer) const { /* ***************************************************** */ +void Flow::lua_dump_tcp_stats(lua_State *vm, const tcp_stats *s, const char *label) const { + lua_newtable(vm); + + lua_push_uint32_table_entry(vm, "num_syn", s->num_syn); + lua_push_uint32_table_entry(vm, "num_rst", s->num_rst); + lua_push_uint32_table_entry(vm, "num_fin", s->num_fin); + lua_push_uint32_table_entry(vm, "num_zero_window", s->num_zero_window); + + lua_pushstring(vm, label); + lua_insert(vm, -2); + lua_settable(vm, -3); +} + +/* ***************************************************** */ + void Flow::lua_get_tcp_info(lua_State *vm) const { if(get_protocol() == IPPROTO_TCP) { - lua_push_bool_table_entry( - vm, "tcp.seq_problems", + lua_push_bool_table_entry(vm, "tcp.seq_problems", (stats.get_cli2srv_tcp_retr() || stats.get_cli2srv_tcp_ooo() || stats.get_cli2srv_tcp_lost() || stats.get_cli2srv_tcp_keepalive() || stats.get_srv2cli_tcp_retr() || stats.get_srv2cli_tcp_ooo() || @@ -7672,6 +7700,14 @@ void Flow::lua_get_tcp_info(lua_State *vm) const { lua_push_bool_table_entry(vm, "tcp_connecting", isTCPConnecting()); lua_push_bool_table_entry(vm, "tcp_closed", isTCPClosed()); lua_push_bool_table_entry(vm, "tcp_reset", isTCPReset()); + + lua_newtable(vm); + lua_dump_tcp_stats(vm, &tcp_stats_src2dst, "src2dst"); + lua_dump_tcp_stats(vm, &tcp_stats_dst2src, "dst2src"); + lua_pushstring(vm, "tcp_stats"); + lua_insert(vm, -2); + lua_settable(vm, -3); + } } @@ -9273,3 +9309,19 @@ void Flow::updateMac() { } /* *************************************** */ + +void Flow::decodeTCPstats(u_int32_t v, tcp_stats *stats) { + v = ntohl(v); + + stats->num_syn = (v & 0xFF000000) >> 24; + stats->num_rst = (v & 0x00FF0000) >> 16; + stats->num_fin = (v & 0x0000FF00) >> 8; + stats->num_zero_window = v & 0x000000FF;; +} + +/* *************************************** */ + +void Flow::updateTCPStats(u_int32_t cli_stats, u_int32_t srv_stats) { + if(cli_stats) decodeTCPstats(cli_stats, &tcp_stats_src2dst); + if(srv_stats) decodeTCPstats(srv_stats, &tcp_stats_dst2src); +} diff --git a/src/ParsedFlow.cpp b/src/ParsedFlow.cpp index f22ac74e36..21b4f49fdc 100644 --- a/src/ParsedFlow.cpp +++ b/src/ParsedFlow.cpp @@ -59,6 +59,7 @@ ParsedFlow::ParsedFlow() : ParsedFlowCore(), ParsedeBPF() { l7_json = NULL; has_parsed_ebpf = false; memset(&qoe, 0, sizeof(qoe)); + tcp_stats_src_to_dst = tcp_stats_dst_to_src = 0; } /* *************************************** */ @@ -213,121 +214,125 @@ void ParsedFlow::fromLua(lua_State *L, int index) { int t = lua_type(L, -1); switch (t) { - case LUA_TSTRING: - if (!strcmp(key, "src_ip")) { - src_ip.set(lua_tostring(L, -1)); - } else if (!strcmp(key, "dst_ip")) { - dst_ip.set(lua_tostring(L, -1)); - } else if (!strcmp(key, "http_method")) { - http_method = ndpi_http_str2method(lua_tostring(L, -1), - strlen(lua_tostring(L, -1))); - } else if (!strcmp(key, "http_site")) { - if (http_site) free(http_site); - http_site = strdup(lua_tostring(L, -1)); - } else if (!strcmp(key, "http_user_agent")) { - if (http_user_agent) free(http_user_agent); - http_user_agent = strdup(lua_tostring(L, -1)); - } else if (!strcmp(key, "l7_info")) { - if (l7_info) free(l7_info); - l7_info = strdup(lua_tostring(L, -1)); - } else if (!strcmp(key, "http_url")) { - if (http_url) free(http_url); - http_url = strdup(lua_tostring(L, -1)); - } else if (!strcmp(key, "tls_server_name")) { - if (tls_server_name) free(tls_server_name); - tls_server_name = strdup(lua_tostring(L, -1)); - } else if (!strcmp(key, "dns_query")) { - if (dns_query) free(dns_query); - dns_query = strdup(lua_tostring(L, -1)); - } else if (!strcmp(key, "ja4c_hash")) { - if (ja4c_hash) free(ja4c_hash); - ja4c_hash = strdup(lua_tostring(L, -1)); - } else if (!strcmp(key, "tcp_fingerprint")) { - if (tcp_fingerprint) free(tcp_fingerprint); - tcp_fingerprint = strdup(lua_tostring(L, -1)); - } else if (!strcmp(key, "bittorrent_hash")) { - if (bittorrent_hash) free(bittorrent_hash); - bittorrent_hash = strdup(lua_tostring(L, -1)); - } else if (!strcmp(key, "external_alert")) { - if (external_alert) free(external_alert); - external_alert = strdup(lua_tostring(L, -1)); - } else if (!strcmp(key, "flow_risk_info")) { - if (flow_risk_info) free(flow_risk_info); - flow_risk_info = strdup(lua_tostring(L, -1)); - } else if (!strcmp(key, "first_switched_iso8601")) { - first_switched = Utils::str2epoch(lua_tostring(L, -1)); - } else if (!strcmp(key, "last_switched_iso8601")) { - last_switched = Utils::str2epoch(lua_tostring(L, -1)); - } else if (!strcmp(key, "l4_proto")) { - l4_proto = Utils::l4name2proto(lua_tostring(L, -1)); - } else if (!strcmp(key, "l7_json")) { - if (l7_json) free(l7_json); - l7_json = strdup(lua_tostring(L, -1)); - } else { - addAdditionalField(key, json_object_new_string(lua_tostring(L, -1))); - ntop->getTrace()->traceEvent(TRACE_DEBUG, - "Key '%s' (string) not supported", key); - } - break; + case LUA_TSTRING: + if (!strcmp(key, "src_ip")) { + src_ip.set(lua_tostring(L, -1)); + } else if (!strcmp(key, "dst_ip")) { + dst_ip.set(lua_tostring(L, -1)); + } else if (!strcmp(key, "http_method")) { + http_method = ndpi_http_str2method(lua_tostring(L, -1), + strlen(lua_tostring(L, -1))); + } else if (!strcmp(key, "http_site")) { + if (http_site) free(http_site); + http_site = strdup(lua_tostring(L, -1)); + } else if (!strcmp(key, "http_user_agent")) { + if (http_user_agent) free(http_user_agent); + http_user_agent = strdup(lua_tostring(L, -1)); + } else if (!strcmp(key, "l7_info")) { + if (l7_info) free(l7_info); + l7_info = strdup(lua_tostring(L, -1)); + } else if (!strcmp(key, "http_url")) { + if (http_url) free(http_url); + http_url = strdup(lua_tostring(L, -1)); + } else if (!strcmp(key, "tls_server_name")) { + if (tls_server_name) free(tls_server_name); + tls_server_name = strdup(lua_tostring(L, -1)); + } else if (!strcmp(key, "dns_query")) { + if (dns_query) free(dns_query); + dns_query = strdup(lua_tostring(L, -1)); + } else if (!strcmp(key, "ja4c_hash")) { + if (ja4c_hash) free(ja4c_hash); + ja4c_hash = strdup(lua_tostring(L, -1)); + } else if (!strcmp(key, "tcp_fingerprint")) { + if (tcp_fingerprint) free(tcp_fingerprint); + tcp_fingerprint = strdup(lua_tostring(L, -1)); + } else if (!strcmp(key, "bittorrent_hash")) { + if (bittorrent_hash) free(bittorrent_hash); + bittorrent_hash = strdup(lua_tostring(L, -1)); + } else if (!strcmp(key, "external_alert")) { + if (external_alert) free(external_alert); + external_alert = strdup(lua_tostring(L, -1)); + } else if (!strcmp(key, "flow_risk_info")) { + if (flow_risk_info) free(flow_risk_info); + flow_risk_info = strdup(lua_tostring(L, -1)); + } else if (!strcmp(key, "first_switched_iso8601")) { + first_switched = Utils::str2epoch(lua_tostring(L, -1)); + } else if (!strcmp(key, "last_switched_iso8601")) { + last_switched = Utils::str2epoch(lua_tostring(L, -1)); + } else if (!strcmp(key, "l4_proto")) { + l4_proto = Utils::l4name2proto(lua_tostring(L, -1)); + } else if (!strcmp(key, "l7_json")) { + if (l7_json) free(l7_json); + l7_json = strdup(lua_tostring(L, -1)); + } else { + addAdditionalField(key, json_object_new_string(lua_tostring(L, -1))); + ntop->getTrace()->traceEvent(TRACE_DEBUG, + "Key '%s' (string) not supported", key); + } + break; - case LUA_TNUMBER: - if (!strcmp(key, "vlan_id")) - vlan_id = lua_tonumber(L, -1); - else if (!strcmp(key, "version")) - version = htons(lua_tointeger(L, -1)); - else if (!strcmp(key, "src_port")) - src_port = htons(lua_tointeger(L, -1)); - else if (!strcmp(key, "dst_port")) - dst_port = htons(lua_tointeger(L, -1)); - else if (!strcmp(key, "l4_proto")) - l4_proto = lua_tonumber(L, -1); - else if (!strcmp(key, "tcp_flags")) - tcp.tcp_flags = htons(lua_tointeger(L, -1)); - else if (!strcmp(key, "direction")) - direction = htons(lua_tointeger(L, -1)); - else if (!strcmp(key, "first_switched")) - first_switched = lua_tonumber(L, -1); - else if (!strcmp(key, "last_switched")) - last_switched = lua_tonumber(L, -1); - else if (!strcmp(key, "in_pkts")) - in_pkts = lua_tonumber(L, -1); - else if (!strcmp(key, "in_bytes")) - in_bytes = lua_tonumber(L, -1); - else if (!strcmp(key, "out_pkts")) - out_pkts = lua_tonumber(L, -1); - else if (!strcmp(key, "out_bytes")) - out_bytes = lua_tonumber(L, -1); - else if (!strcmp(key, "master_protocol")) - l7_proto.proto.master_protocol = lua_tonumber(L, -1); - else if (!strcmp(key, "app_protocol")) - l7_proto.proto.app_protocol = lua_tonumber(L, -1); - else if (!strcmp(key, "confidence")) - confidence = (ndpi_confidence_t) lua_tonumber(L, -1); - else if (!strcmp(key, "dns_query_type")) - dns_query_type = lua_tonumber(L, -1); - else if (!strcmp(key, "dns_ret_code")) - dns_ret_code = lua_tonumber(L, -1); - else if (!strcmp(key, "dns_query_id")) - dns_query_id = lua_tonumber(L, -1); - else if (!strcmp(key, "http_ret_code")) - http_ret_code = lua_tonumber(L, -1); - else { - addAdditionalField(key, json_object_new_int64(lua_tonumber(L, -1))); - ntop->getTrace()->traceEvent(TRACE_DEBUG, - "Key '%s' (number) not supported", key); - } - break; + case LUA_TNUMBER: + if (!strcmp(key, "vlan_id")) + vlan_id = lua_tonumber(L, -1); + else if (!strcmp(key, "version")) + version = htons(lua_tointeger(L, -1)); + else if (!strcmp(key, "src_port")) + src_port = htons(lua_tointeger(L, -1)); + else if (!strcmp(key, "dst_port")) + dst_port = htons(lua_tointeger(L, -1)); + else if (!strcmp(key, "l4_proto")) + l4_proto = lua_tonumber(L, -1); + else if (!strcmp(key, "tcp_flags")) + tcp.tcp_flags = htons(lua_tointeger(L, -1)); + else if (!strcmp(key, "direction")) + direction = htons(lua_tointeger(L, -1)); + else if (!strcmp(key, "first_switched")) + first_switched = lua_tonumber(L, -1); + else if (!strcmp(key, "last_switched")) + last_switched = lua_tonumber(L, -1); + else if (!strcmp(key, "in_pkts")) + in_pkts = lua_tonumber(L, -1); + else if (!strcmp(key, "in_bytes")) + in_bytes = lua_tonumber(L, -1); + else if (!strcmp(key, "out_pkts")) + out_pkts = lua_tonumber(L, -1); + else if (!strcmp(key, "out_bytes")) + out_bytes = lua_tonumber(L, -1); + else if (!strcmp(key, "master_protocol")) + l7_proto.proto.master_protocol = lua_tonumber(L, -1); + else if (!strcmp(key, "app_protocol")) + l7_proto.proto.app_protocol = lua_tonumber(L, -1); + else if (!strcmp(key, "confidence")) + confidence = (ndpi_confidence_t) lua_tonumber(L, -1); + else if (!strcmp(key, "dns_query_type")) + dns_query_type = lua_tonumber(L, -1); + else if (!strcmp(key, "dns_ret_code")) + dns_ret_code = lua_tonumber(L, -1); + else if (!strcmp(key, "dns_query_id")) + dns_query_id = lua_tonumber(L, -1); + else if (!strcmp(key, "http_ret_code")) + http_ret_code = lua_tonumber(L, -1); + else if (!strcmp(key, "tcp_stats_src_to_dst")) + tcp_stats_src_to_dst = lua_tonumber(L, -1); + else if (!strcmp(key, "tcp_stats_dst_to_src")) + tcp_stats_dst_to_src = lua_tonumber(L, -1); + else { + addAdditionalField(key, json_object_new_int64(lua_tonumber(L, -1))); + ntop->getTrace()->traceEvent(TRACE_DEBUG, + "Key '%s' (number) not supported", key); + } + break; - case LUA_TBOOLEAN: - addAdditionalField(key, json_object_new_boolean(lua_toboolean(L, -1))); - ntop->getTrace()->traceEvent(TRACE_DEBUG, - "Key '%s' (boolean) not supported", key); - break; + case LUA_TBOOLEAN: + addAdditionalField(key, json_object_new_boolean(lua_toboolean(L, -1))); + ntop->getTrace()->traceEvent(TRACE_DEBUG, + "Key '%s' (boolean) not supported", key); + break; - default: - ntop->getTrace()->traceEvent(TRACE_ERROR, - "Internal error: type %d not handled", t); - break; + default: + ntop->getTrace()->traceEvent(TRACE_ERROR, + "Internal error: type %d not handled", t); + break; } lua_pop(L, 1); @@ -437,10 +442,10 @@ void ParsedFlow::print() { dst_ip.print(buf2, sizeof(buf2)); ntop->getTrace()->traceEvent(TRACE_NORMAL, "[src-ip: %s][src-port: %u][dst-ip: %s][dst-port: %u][first: %u][last: %u]", - src_ip.print(buf1, sizeof(buf1)), - ntohs(src_port), - dst_ip.print(buf2, sizeof(buf2)), - ntohs(dst_port), - first_switched, - last_switched); + src_ip.print(buf1, sizeof(buf1)), + ntohs(src_port), + dst_ip.print(buf2, sizeof(buf2)), + ntohs(dst_port), + first_switched, + last_switched); } diff --git a/src/ParserInterface.cpp b/src/ParserInterface.cpp index 434ee05099..4f97a5d46a 100644 --- a/src/ParserInterface.cpp +++ b/src/ParserInterface.cpp @@ -604,9 +604,9 @@ bool ParserInterface::processFlow(ParsedFlow *zflow) { if(flow->get_protocol() == IPPROTO_ICMP) flow->setICMPTypeCode(zflow->icmp_type_code); - if (flow->isDNS()) flow->updateDNS(zflow); + if (flow->isDNS()) flow->updateDNS(zflow); if (flow->isHTTP()) flow->updateHTTP(zflow); - if (flow->isTLS()) flow->updateTLS(zflow); + if (flow->isTLS()) flow->updateTLS(zflow); if (flow->isSMTP()) { if (zflow->getSMTPMailFrom()) @@ -652,6 +652,8 @@ bool ParserInterface::processFlow(ParsedFlow *zflow) { if (zflow->getBittorrentHash()) flow->setBittorrentHash(zflow->getBittorrentHash(), strlen(zflow->getBittorrentHash())); + flow->updateTCPStats(zflow->get_tcp_stats(true), zflow->get_tcp_stats(false)); + if (zflow->getRiskInfo()) { json_object *o, *obj; enum json_tokener_error jerr = json_tokener_success; diff --git a/src/ZMQParserInterface.cpp b/src/ZMQParserInterface.cpp index 225cfa4479..f9ced43c64 100644 --- a/src/ZMQParserInterface.cpp +++ b/src/ZMQParserInterface.cpp @@ -152,6 +152,8 @@ ZMQParserInterface::ZMQParserInterface(const char *endpoint, addMapping("UNIQUE_SOURCE_ID", UNIQUE_SOURCE_ID, NTOP_PEN); addMapping("NPROBE_SOURCE_ID", NPROBE_SOURCE_ID, NTOP_PEN); addMapping("TCP_FINGERPRINT", TCP_FINGERPRINT, NTOP_PEN); + addMapping("TCP_STATS_SRC_TO_DST", TCP_STATS_SRC_TO_DST, NTOP_PEN); + addMapping("TCP_STATS_DST_TO_SRC", TCP_STATS_DST_TO_SRC, NTOP_PEN); /* eBPF / Process */ addMapping("SRC_PROC_PID", SRC_PROC_PID, NTOP_PEN); @@ -1245,6 +1247,14 @@ bool ZMQParserInterface::parsePENNtopField(ParsedFlow *const flow, flow->setTCPFingerprint(value->string); break; + case TCP_STATS_SRC_TO_DST: + flow->setTCPStats(value->int_num, true); + break; + + case TCP_STATS_DST_TO_SRC: + flow->setTCPStats(value->int_num, false); + break; + case TLS_CIPHER: flow->setTLSCipher(value->int_num); break; @@ -2542,7 +2552,6 @@ u_int32_t ZMQParserInterface::parseTLVFlows(const char *payload, int payload_siz } while (ndpi_deserialize_get_item_type(&deserializer, &kt) != ndpi_serialization_unknown) { - rc = parseSingleTLVFlow(&deserializer, n); if (rc < 0) diff --git a/tests/e2e b/tests/e2e index 01c2e61d5a..0490fb54b2 160000 --- a/tests/e2e +++ b/tests/e2e @@ -1 +1 @@ -Subproject commit 01c2e61d5a0c7014bc1c43f1e2a8b03aec9e03f6 +Subproject commit 0490fb54b2ec6b5548bdc79f959002f0e15534b6