diff --git a/include/Flow.h b/include/Flow.h index f9aac0f88e..cfadabf875 100644 --- a/include/Flow.h +++ b/include/Flow.h @@ -65,10 +65,11 @@ class Flow : public GenericHashEntry { bool detection_completed, protocol_processed, cli2srv_direction, twh_over, twh_ok, dissect_next_http_packet, passVerdict, - check_tor, l7_protocol_guessed, flow_alerted, flow_dropped_counts_increased, + check_tor, l7_protocol_guessed, flow_dropped_counts_increased, good_low_flow_detected, good_ssl_hs, update_flow_port_stats, quota_exceeded, has_malicious_signature; u_int16_t diff_num_http_requests; + int64_t alert_rowid; #ifdef NTOPNG_PRO bool counted_in_aggregated_flow, status_counted_in_aggregated_flow; bool ingress2egress_direction; @@ -485,7 +486,8 @@ class Flow : public GenericHashEntry { && ((dst2src_tcp_flags & (TH_SYN | TH_ACK | TH_FIN)) == (TH_SYN | TH_ACK | TH_FIN))); } inline bool isTCPReset() const { return (!isTCPClosed() && ((src2dst_tcp_flags & TH_RST) || (dst2src_tcp_flags & TH_RST))); } - inline bool isFlowAlerted() { return(flow_alerted); } + inline bool isFlowAlerted() { return(alert_rowid >= 0); } + inline void setFlowAlerted(int64_t rowid) { alert_rowid = rowid; } inline void setVRFid(u_int32_t v) { vrfId = v; } inline void setFlowNwLatency(const struct timeval * const tv, bool client) { diff --git a/scripts/locales/en.lua b/scripts/locales/en.lua index ef02e12fa2..0d7678e371 100644 --- a/scripts/locales/en.lua +++ b/scripts/locales/en.lua @@ -770,6 +770,31 @@ local lang = { ["type_explorer"] = "Type Explorer", ["visual_explorer"] = "Visual Explorer", }, + ["flow_callbacks"] = { + ["application_detected"] = "Application Detected", + ["callback"] = "Flow Callback", + ["callback_config"] = "Configuration", + ["callback_enabled"] = "Enabled", + ["idle"] = "Idle", + ["no_callbacks_defined"] = "No callbacks found for \"%{cb}\"", + ["note_flow_application_detected"] = "\"Application Detected\" callbacks are executed right after the detection of the application. This occurs within the first 12 packets of a flow for packet interfaces or immediately after a flow has been received for ZMQ interfaces.", + ["note_flow_idle"] = "\"Idle\" callbacks are executed when a flow has terminated its activity and it has gone idle.", + ["note_flow_lifecycle"] = "Callbacks are executed at certain moments in the lifecycle of a flow.", + ["note_flow_periodic_update"] = "\"Periodic Update\" callbacks are executed periodically after a flow has been active for more than five minutes.", + ["note_flow_staus_changed"] = "\"Status Changed\" callbacks are executed every time there is a status change in the flow. For example when connection issues such as retransmissions are seen.", + ["notes"] = "NOTES", + ["periodic_update"] = "Periodic Update", + ["status_changed"] = "Status Changed", + }, + ["flow_callbacks_config"] = { + ["blacklisted"] = "Blacklisted Flow", + ["blacklisted_description"] = "Trigger an alert when at least one among the client and server is blacklisted", + ["mud"] = "MUD", + ["mud_description"] = "Handle host Manufacturer usage descriptions (MUD)", + ["no_input"] = "No configuration necessary", + ["score"] = "Flow Score", + ["score_description"] = "Evaluate the flow and compute its score", + }, ["flow_details"] = { ["acceptable_label"] = "Acceptable", ["actual_peak_throughput"] = "Actual / Peak Throughput", @@ -815,6 +840,7 @@ local lang = { ["existing_rules_note"] = "Existing rules can be found at the %{name} page.", ["first_last_flow_sequence"] = "First / Last Flow Sequence", ["flow_active_msg"] = "Flow is active.", + ["flow_alerted"] = "Flow Alerted", ["flow_blocked_by_bridge"] = "Flow blocked due to configured policies", ["flow_cannot_be_found_message"] = "This flow cannot be found.", ["flow_completed_msg"] = "Flow is closed.", diff --git a/scripts/lua/flow_details.lua b/scripts/lua/flow_details.lua index 26a8f28e2e..204a42b481 100644 --- a/scripts/lua/flow_details.lua +++ b/scripts/lua/flow_details.lua @@ -7,6 +7,7 @@ package.path = dirs.installdir .. "/scripts/lua/modules/?.lua;" .. package.path local shaper_utils require "lua_utils" +require "alert_utils" local format_utils = require "format_utils" local have_nedge = ntop.isnEdge() local NfConfig = nil @@ -951,22 +952,38 @@ else end -- ###################################### - - if interface.isPacketInterface() then - print(""..i18n("flow_details.flow_status").."") - for id, t in pairs(flow_consts.flow_status_types) do - if ntop.bitmapIsSet(flow["status_map"], id) then - print(getFlowStatus(id, flow2statusinfo(flow)).."
") + + if flow["flow.alerted"] then + local message = nil + + print(" "..i18n("flow_details.flow_alerted").."") + + if(flow["flow.alert_rowid"] ~= nil) then + -- Try to fetch the alert + local res = performAlertsQuery("SELECT *", "historical-flows", {row_id = flow["flow.alert_rowid"]}) + + if((res ~= nil) and (#res == 1)) then + message = formatAlertMessage(ifid, res[1], true --[[ skip peers, we are already showing the flow ]]) end end - print("\n") - if debug_score then - if(flow["score"] > 0) then - print(""..i18n("flow_details.flow_score")..""..flow["score"].."\n") - end + print(message) + print("\n") + end + + print(""..i18n("flow_details.flow_status").."") + for id, t in pairs(flow_consts.flow_status_types) do + if ntop.bitmapIsSet(flow["status_map"], id) then + print(getFlowStatus(id, flow2statusinfo(flow)).."
") end end + print("\n") + + if debug_score then + if(flow["score"] > 0) then + print(""..i18n("flow_details.flow_score")..""..flow["score"].."\n") + end + end if((flow.client_process == nil) and (flow.server_process == nil)) then print(""..i18n("flow_details.actual_peak_throughput").."") diff --git a/scripts/lua/modules/alert_utils.lua b/scripts/lua/modules/alert_utils.lua index 141d05fd82..df5f84f784 100644 --- a/scripts/lua/modules/alert_utils.lua +++ b/scripts/lua/modules/alert_utils.lua @@ -659,7 +659,7 @@ end -- ################################# -local function formatRawFlow(record, flow_json, skip_add_links) +local function formatRawFlow(record, flow_json, skip_add_links, skip_peers) require "flow_utils" local time_bounds local add_links = (not skip_add_links) @@ -681,7 +681,7 @@ local function formatRawFlow(record, flow_json, skip_add_links) local status_info = alert2statusinfo(decoded) -- active flow lookup - if not interface.isView() and status_info and status_info["ntopng.key"] and record["alert_tstamp"] then + if not interface.isView() and status_info and status_info["ntopng.key"] and record["alert_tstamp"] and (not skip_peers) then -- attempt a lookup on the active flows local active_flow = interface.findFlowByKey(status_info["ntopng.key"]) @@ -700,7 +700,12 @@ local function formatRawFlow(record, flow_json, skip_add_links) ["srv.ip"] = record["srv_addr"], ["srv.port"] = tonumber(record["srv_port"]), ["srv.blacklisted"] = tostring(record["srv_blacklisted"]) == "1", ["vlan"] = record["vlan_id"]} - flow = "["..i18n("flow")..": "..(getFlowLabel(flow, false, add_links, time_bounds, host_page) or "").."] " + + if skip_peers then + flow = "" + else + flow = "["..i18n("flow")..": "..(getFlowLabel(flow, false, add_links, time_bounds, host_page) or "").."] " + end local l4_proto_label = l4_proto_to_string(record["proto"] or 0) or "" @@ -2591,11 +2596,11 @@ end -- ################################# -function formatAlertMessage(ifid, alert) +function formatAlertMessage(ifid, alert, skip_peers) local msg if(alert.alert_entity == alertEntity("flow") or (alert.alert_entity == nil)) then - msg = formatRawFlow(alert, alert["alert_json"]) + msg = formatRawFlow(alert, alert["alert_json"], nil, skip_peers) else msg = alert["alert_json"] diff --git a/src/AlertsManager.cpp b/src/AlertsManager.cpp index aac6508a7e..4b715a5c47 100644 --- a/src/AlertsManager.cpp +++ b/src/AlertsManager.cpp @@ -564,8 +564,10 @@ int AlertsManager::storeFlowAlert(Flow *f) { if(stmt3) sqlite3_finalize(stmt3); m.unlock(__FILE__, __LINE__); - if((rc == 0) && (cur_rowid != (u_int64_t)-1)) + if((rc == 0) && (cur_rowid != (u_int64_t)-1)) { + f->setFlowAlerted(cur_rowid); notifyFlowAlert(cur_rowid); + } if(cli) cli->incTotalAlerts(alert_type); if(srv) srv->incTotalAlerts(alert_type); @@ -771,8 +773,10 @@ int AlertsManager::storeFlowAlert(Flow *f, AlertType alert_type, AlertLevel aler if(stmt3) sqlite3_finalize(stmt3); m.unlock(__FILE__, __LINE__); - if((rc == 0) && (cur_rowid != (u_int64_t)-1)) + if((rc == 0) && (cur_rowid != (u_int64_t)-1)) { + f->setFlowAlerted(cur_rowid); notifyFlowAlert(cur_rowid); + } if(cli) cli->incTotalAlerts(alert_type); if(srv) srv->incTotalAlerts(alert_type); diff --git a/src/Flow.cpp b/src/Flow.cpp index a028d298dd..31e3c0870d 100644 --- a/src/Flow.cpp +++ b/src/Flow.cpp @@ -45,9 +45,10 @@ Flow::Flow(NetworkInterface *_iface, srv2cli_last_packets = 0, srv2cli_last_bytes = 0, cli_host = srv_host = NULL, good_low_flow_detected = false, srv2cli_last_goodput_bytes = cli2srv_last_goodput_bytes = 0, good_ssl_hs = true, - flow_alerted = flow_dropped_counts_increased = false, vrfId = 0; + flow_dropped_counts_increased = false, vrfId = 0; alert_score = CONST_NO_SCORE_SET; + alert_rowid = -1; last_notified_status_map = Utils::bitmapSet(0, status_normal); purge_acknowledged_mark = detection_completed = update_flow_port_stats = false; ndpiDetectedProtocol = ndpiUnknownProtocol; @@ -354,7 +355,6 @@ void Flow::dumpFlowAlert() { if(do_dump) { iface->getAlertsManager()->storeFlowAlert(this); - flow_alerted = true; if(cli_host) cli_host->incNumAlertedFlows(); if(srv_host) srv_host->incNumAlertedFlows(); @@ -2033,7 +2033,11 @@ void Flow::lua(lua_State* vm, AddressTree * ptree, lua_push_bool_table_entry(vm, "flow.idle", idle()); lua_push_uint64_table_entry(vm, "flow.status", getFlowStatus(&status_map)); - lua_push_bool_table_entry(vm, "flow.alerted", flow_alerted); + + if(isFlowAlerted()) { + lua_push_bool_table_entry(vm, "flow.alerted", isFlowAlerted()); + lua_push_uint64_table_entry(vm, "flow.alert_rowid", alert_rowid); + } lua_push_uint64_table_entry(vm, "status_map", status_map); @@ -4108,7 +4112,7 @@ void Flow::postFlowSetIdle(time_t t) { if(srv_host) srv_host->incNumAnomalousFlows(false); } - if(flow_alerted) { + if(isFlowAlerted()) { if(cli_host) cli_host->decNumAlertedFlows(); if(srv_host) srv_host->decNumAlertedFlows(); }