diff --git a/include/FlowStats.h b/include/FlowStats.h index 097e8943b785..92e92ff173bc 100644 --- a/include/FlowStats.h +++ b/include/FlowStats.h @@ -36,6 +36,7 @@ class FlowStats { u_int32_t dscps[64]; // 64 values available for dscp u_int32_t host_pools[UNLIMITED_NUM_HOST_POOLS]; std::map talking_hosts; + std::map wlan_ssid; public: FlowStats(); @@ -46,6 +47,7 @@ class FlowStats { u_int8_t dscp_srv2cli, Flow *flow); void updateTalkingHosts(Flow *f); + void updateWLANSSID(Flow *f); void lua(lua_State *vm); diff --git a/include/Paginator.h b/include/Paginator.h index ecfc97452520..86ba7702d63c 100644 --- a/include/Paginator.h +++ b/include/Paginator.h @@ -30,7 +30,7 @@ class Paginator { bool a2z_sort_order; bool detailed_results /* deprecated, use DetailsLevel instead */; char *sort_column, *country_filter, *host_filter, *client_filter, - *server_filter; + *server_filter, *wlan_ssid_filter; char *container_filter, *pod_filter; char *traffic_profile_filter; char *username_filter, *pidname_filter; @@ -85,6 +85,14 @@ class Paginator { return false; } + inline bool wlanSSIDFilter(char **f) const { + if (wlan_ssid_filter) { + (*f) = wlan_ssid_filter; + return true; + } + return false; + } + inline bool hostFilter(char **f) const { if (host_filter) { (*f) = host_filter; diff --git a/scripts/locales/en.lua b/scripts/locales/en.lua index fb13fd2f66da..f275da71498f 100644 --- a/scripts/locales/en.lua +++ b/scripts/locales/en.lua @@ -742,6 +742,7 @@ local lang = { ["traffic_type"] = "Traffic Type", ["type"] = "ICMP Type", ["udp"] = "UDP", + ["wlan_ssid"] = "WLAN SSID", ["udp_processes"] = "UDP Processes", ["unable_to_copy_to_clickboard"] = "Unable to copy to clipboard", ["undo"] = "Undo", @@ -2147,6 +2148,7 @@ local lang = { ["top_servers"] = "Top Servers", ["traffic_direction"] = "Traffic Direction", ["vlan_id"] = "VLAN", + ["wlan_ssid"] = "WLAN SSID", ["where_note"] = "Use <field> <operator> <value> conditions, where <operator> could be <, >, =, !=. AND/OR operators are also allowed to combine conditions. Please use quotes for string fields (e.g. INFO='www.ntop.org')", ["all"] = { ["host_pool"] = "All Host Pools", @@ -2158,6 +2160,7 @@ local lang = { ["output_snmp"] = "All Interfaces", ["probe_ip"] = "All Exporters", ["vlan_id"] = "All VLANs", + ["wlan_ssid"] = "All WLAN SSID", }, ["tags"] = { ["acknowledged"] = "Acknowledged", diff --git a/scripts/lua/flow_details.lua b/scripts/lua/flow_details.lua index 628fa1f71403..423a3d26aa12 100644 --- a/scripts/lua/flow_details.lua +++ b/scripts/lua/flow_details.lua @@ -1865,6 +1865,19 @@ else "\n") end + if flow.wlan then + if flow.wlan.ssid then + print("" .. getFlowKey('WLAN_SSID') .. "") + print("" .. flow.wlan["ssid"] .. "") + print("\n") + end + if flow.wlan.wtp_mac_address then + print("" .. getFlowKey('WTP_MAC_ADDRESS') .. "") + print("" .. flow.wlan["wtp_mac_address"] .. "") + print("\n") + end + end + if (flow["moreinfo.json"] ~= nil) then local flow_field_value_maps = require "flow_field_value_maps" local info, pos, err = json.decode(flow["moreinfo.json"], 1, nil) @@ -2010,19 +2023,6 @@ else end end - if flow.wlan then - if flow.wlan.ssid then - print("" .. getFlowKey('WLAN_SSID') .. "") - print("" .. flow.wlan["ssid"] .. "") - print("\n") - end - if flow.wlan.wtp_mac_address then - print("" .. getFlowKey('WTP_MAC_ADDRESS') .. "") - print("" .. flow.wlan["wtp_mac_address"] .. "") - print("\n") - end - end - if (flow.flow_payload ~= nil) then local idx diff --git a/scripts/lua/modules/flow_utils.lua b/scripts/lua/modules/flow_utils.lua index f0be3b7124c8..9af3f76b2cd8 100644 --- a/scripts/lua/modules/flow_utils.lua +++ b/scripts/lua/modules/flow_utils.lua @@ -166,6 +166,7 @@ function getFlowsFilter() local icmp_type = _GET["icmp_type"] local icmp_code = _GET["icmp_cod"] local dscp_filter = _GET["dscp"] + local wlan_ssid_filter = _GET["wlan_ssid"] local host_pool = _GET["host_pool_id"] local flow_status = _GET["flow_status"] local flow_status_severity = _GET["flow_status_severity"] @@ -370,6 +371,10 @@ function getFlowsFilter() pageinfo["dscpFilter"] = tonumber(dscp_filter) end + if not isEmptyString(wlan_ssid_filter) then + pageinfo["wlanSSIDFilter"] = wlan_ssid_filter + end + if not isEmptyString(talking_with) then pageinfo["talkingWith"] = talking_with end diff --git a/scripts/lua/modules/historical_flow_details_formatter.lua b/scripts/lua/modules/historical_flow_details_formatter.lua index 9ec1638c6e2b..38cfb7698024 100644 --- a/scripts/lua/modules/historical_flow_details_formatter.lua +++ b/scripts/lua/modules/historical_flow_details_formatter.lua @@ -15,533 +15,564 @@ local historical_flow_details_formatter = {} -- ############################################### local function empty_port(port) - return port == '0' + return port == '0' end -- ############################################### local function empty_ip(ip) - return ip == '0.0.0.0' + return ip == '0.0.0.0' end -- ############################################### -- This function format info regarding pre/post nat ips and ports local function format_pre_post_nat_info(flow, info) - local tmp = {} - local nat_values = {} - - -- Checking empty values - -- Checking IPs - if (not isEmptyString(info["PRE_NAT_IPV4_SRC_ADDR"]) and not empty_ip(info["PRE_NAT_IPV4_SRC_ADDR"])) then - nat_values.pre_nat_src_ip = info["PRE_NAT_IPV4_SRC_ADDR"] - end - if (not isEmptyString(info["POST_NAT_IPV4_SRC_ADDR"]) and not empty_ip(info["POST_NAT_IPV4_SRC_ADDR"])) then - nat_values.post_nat_src_ip = info["POST_NAT_IPV4_SRC_ADDR"] - end - if (not isEmptyString(info["PRE_NAT_IPV4_DST_ADDR"]) and not empty_ip(info["PRE_NAT_IPV4_DST_ADDR"])) then - nat_values.pre_nat_dst_ip = info["PRE_NAT_IPV4_DST_ADDR"] - end - if (not isEmptyString(info["POST_NAT_IPV4_DST_ADDR"]) and not empty_ip(info["POST_NAT_IPV4_DST_ADDR"])) then - nat_values.post_nat_dst_ip = info["POST_NAT_IPV4_DST_ADDR"] - end - -- Checking ports - if (not isEmptyString(info["PRE_NAT_SRC_PORT"]) and not empty_port(info["PRE_NAT_SRC_PORT"])) then - nat_values.pre_nat_src_port = info["PRE_NAT_SRC_PORT"] - end - if (not isEmptyString(info["POST_NAT_SRC_PORT"]) and not empty_port(info["POST_NAT_SRC_PORT"])) then - nat_values.post_nat_src_port = info["POST_NAT_SRC_PORT"] - end - if (not isEmptyString(info["PRE_NAT_DST_PORT"]) and not empty_port(info["PRE_NAT_DST_PORT"])) then - nat_values.pre_nat_dst_port = info["PRE_NAT_DST_PORT"] - end - if (not isEmptyString(info["POST_NAT_DST_PORT"]) and not empty_port(info["POST_NAT_DST_PORT"])) then - nat_values.post_nat_dst_port = info["POST_NAT_DST_PORT"] - end - - -- No Post-NAT values - if not nat_values.post_nat_dst_port and - not nat_values.post_nat_src_port and - not nat_values.post_nat_dst_ip and - not nat_values.post_nat_src_ip then - return flow - end - - -- Substituting empty values - if not nat_values.post_nat_src_ip then - nat_values.post_nat_src_ip = nat_values.pre_nat_src_ip - end - - if not nat_values.post_nat_dst_ip then - nat_values.post_nat_dst_ip = nat_values.pre_nat_dst_ip - end - - if not nat_values.post_nat_src_port then - nat_values.post_nat_src_port = nat_values.pre_nat_src_port - end - - if not nat_values.post_nat_dst_port then - nat_values.post_nat_dst_port = nat_values.pre_nat_dst_port - end - - -- Format all info - local pre_nat_flow = nat_values.pre_nat_src_ip .. ":" .. nat_values.pre_nat_src_port .. - ' ' .. nat_values.post_nat_src_ip .. ":" .. nat_values.post_nat_src_port - local post_nat_flow = nat_values.post_nat_src_ip .. ":" .. nat_values.post_nat_src_port .. - ' ' .. nat_values.post_nat_dst_ip .. ":" .. nat_values.post_nat_dst_port - flow[#flow + 1] = { - name = i18n('db_explorer.pre_nat_info'), - values = {pre_nat_flow} - } - flow[#flow + 1] = { - name = i18n('db_explorer.post_nat_info'), - values = {post_nat_flow} - } - - return flow + local tmp = {} + local nat_values = {} + + -- Checking empty values + -- Checking IPs + if (not isEmptyString(info["PRE_NAT_IPV4_SRC_ADDR"]) and not empty_ip(info["PRE_NAT_IPV4_SRC_ADDR"])) then + nat_values.pre_nat_src_ip = info["PRE_NAT_IPV4_SRC_ADDR"] + end + if (not isEmptyString(info["POST_NAT_IPV4_SRC_ADDR"]) and not empty_ip(info["POST_NAT_IPV4_SRC_ADDR"])) then + nat_values.post_nat_src_ip = info["POST_NAT_IPV4_SRC_ADDR"] + end + if (not isEmptyString(info["PRE_NAT_IPV4_DST_ADDR"]) and not empty_ip(info["PRE_NAT_IPV4_DST_ADDR"])) then + nat_values.pre_nat_dst_ip = info["PRE_NAT_IPV4_DST_ADDR"] + end + if (not isEmptyString(info["POST_NAT_IPV4_DST_ADDR"]) and not empty_ip(info["POST_NAT_IPV4_DST_ADDR"])) then + nat_values.post_nat_dst_ip = info["POST_NAT_IPV4_DST_ADDR"] + end + -- Checking ports + if (not isEmptyString(info["PRE_NAT_SRC_PORT"]) and not empty_port(info["PRE_NAT_SRC_PORT"])) then + nat_values.pre_nat_src_port = info["PRE_NAT_SRC_PORT"] + end + if (not isEmptyString(info["POST_NAT_SRC_PORT"]) and not empty_port(info["POST_NAT_SRC_PORT"])) then + nat_values.post_nat_src_port = info["POST_NAT_SRC_PORT"] + end + if (not isEmptyString(info["PRE_NAT_DST_PORT"]) and not empty_port(info["PRE_NAT_DST_PORT"])) then + nat_values.pre_nat_dst_port = info["PRE_NAT_DST_PORT"] + end + if (not isEmptyString(info["POST_NAT_DST_PORT"]) and not empty_port(info["POST_NAT_DST_PORT"])) then + nat_values.post_nat_dst_port = info["POST_NAT_DST_PORT"] + end + + -- No Post-NAT values + if not nat_values.post_nat_dst_port and not nat_values.post_nat_src_port and not nat_values.post_nat_dst_ip and + not nat_values.post_nat_src_ip then + return flow + end + + -- Substituting empty values + if not nat_values.post_nat_src_ip then + nat_values.post_nat_src_ip = nat_values.pre_nat_src_ip + end + + if not nat_values.post_nat_dst_ip then + nat_values.post_nat_dst_ip = nat_values.pre_nat_dst_ip + end + + if not nat_values.post_nat_src_port then + nat_values.post_nat_src_port = nat_values.pre_nat_src_port + end + + if not nat_values.post_nat_dst_port then + nat_values.post_nat_dst_port = nat_values.pre_nat_dst_port + end + + -- Format all info + local pre_nat_flow = nat_values.pre_nat_src_ip .. ":" .. nat_values.pre_nat_src_port .. + ' ' .. nat_values.post_nat_src_ip .. ":" .. + nat_values.post_nat_src_port + local post_nat_flow = nat_values.post_nat_src_ip .. ":" .. nat_values.post_nat_src_port .. + ' ' .. nat_values.post_nat_dst_ip .. ":" .. + nat_values.post_nat_dst_port + flow[#flow + 1] = { + name = i18n('db_explorer.pre_nat_info'), + values = {pre_nat_flow} + } + flow[#flow + 1] = { + name = i18n('db_explorer.post_nat_info'), + values = {post_nat_flow} + } + + return flow end -- ############################################### local function format_historical_flow_label(flow) - local historical_flow_utils = require "historical_flow_utils" + local historical_flow_utils = require "historical_flow_utils" - return { - name = i18n("flow_details.flow_peers_client_server"), - values = {historical_flow_utils.getHistoricalFlowLabel(flow, true)} - } + return { + name = i18n("flow_details.flow_peers_client_server"), + values = {historical_flow_utils.getHistoricalFlowLabel(flow, true)} + } end -- ############################################### local function format_historical_protocol_label(flow) - local historical_flow_utils = require "historical_flow_utils" + local historical_flow_utils = require "historical_flow_utils" - return { - name = i18n("protocol") .. " / " .. i18n("application"), - values = {historical_flow_utils.getHistoricalProtocolLabel(flow, true)} - } + return { + name = i18n("protocol") .. " / " .. i18n("application"), + values = {historical_flow_utils.getHistoricalProtocolLabel(flow, true)} + } end -- ############################################### local function format_historical_last_first_seen(flow, info) - return { - name = i18n("db_explorer.date_time"), - values = { - [1] = info.first_seen.time, - [2] = info.last_seen - } - } + return { + name = i18n("db_explorer.date_time"), + values = { + [1] = info.first_seen.time, + [2] = info.last_seen + } + } end -- ############################################### local function format_historical_total_traffic(flow) - return { - name = i18n("db_explorer.traffic_info"), - values = {formatPackets(flow['PACKETS']) .. ' / ' .. bytesToSize(flow['TOTAL_BYTES'])} - } + return { + name = i18n("db_explorer.traffic_info"), + values = {formatPackets(flow['PACKETS']) .. ' / ' .. bytesToSize(flow['TOTAL_BYTES'])} + } end -- ############################################### local function format_historical_client_server_bytes(flow) - return { - name = "", - values = { - [1] = i18n("client") .. " " .. i18n("server") .. ": " .. - bytesToSize(flow['SRC2DST_BYTES']), - [2] = i18n("client") .. " " .. i18n("server") .. ": " .. - bytesToSize(flow['DST2SRC_BYTES']) - } - } + return { + name = "", + values = { + [1] = i18n("client") .. " " .. i18n("server") .. ": " .. + bytesToSize(flow['SRC2DST_BYTES']), + [2] = i18n("client") .. " " .. i18n("server") .. ": " .. + bytesToSize(flow['DST2SRC_BYTES']) + } + } end -- ############################################### local function format_historical_bytes_progress_bar(flow, info) - local cli2srv = round(((flow["SRC2DST_BYTES"] or 0) * 100) / flow["TOTAL_BYTES"], 0) - - return { - name = "", - values = {'
' .. - (info.cli_ip.label or '') .. '
' .. '
' .. (info.srv_ip.label or '') .. '
'} - } + local cli2srv = round(((flow["SRC2DST_BYTES"] or 0) * 100) / flow["TOTAL_BYTES"], 0) + + return { + name = "", + values = {'
' .. + (info.cli_ip.label or '') .. '
' .. '
' .. (info.srv_ip.label or '') .. '
'} + } +end + +-- ############################################### + +local function format_historical_wlan_ssid(flow, info) + return { + name = i18n("flow_fields_description.wlan_ssid"), + values = {info.wlan_ssid.label} + } +end + +-- ############################################### + +local function format_historical_wtp_mac_address(flow, info) + return { + name = i18n("flow_fields_description.wtp_mac_address"), + values = {info.apn_mac.label} + } end -- ############################################### local function format_historical_tos(flow) - return { - name = i18n("db_explorer.tos"), - values = { - [1] = dscp_consts.dscp_descr(flow['SRC2DST_DSCP']), - [2] = dscp_consts.dscp_descr(flow['DST2SRC_DSCP']) - } - } + return { + name = i18n("db_explorer.tos"), + values = { + [1] = dscp_consts.dscp_descr(flow['SRC2DST_DSCP']), + [2] = dscp_consts.dscp_descr(flow['DST2SRC_DSCP']) + } + } end -- ############################################### local function format_historical_tcp_flags(flow, info) - return { - name = i18n("tcp_flags"), - values = { - [1] = i18n("client") .. " " .. i18n("server") .. ": " .. - info.src2dst_tcp_flags.label, - [2] = i18n("client") .. " " .. i18n("server") .. ": " .. - info.dst2src_tcp_flags.label - } - } + return { + name = i18n("tcp_flags"), + values = { + [1] = i18n("client") .. " " .. i18n("server") .. ": " .. + info.src2dst_tcp_flags.label, + [2] = i18n("client") .. " " .. i18n("server") .. ": " .. + info.dst2src_tcp_flags.label + } + } end -- ############################################### local function format_historical_host_pool(flow, info) - return { - name = i18n("details.host_pool"), - values = { - [1] = i18n("client") .. " " .. i18n("pools.pool") .. ": " .. info.cli_host_pool_id.label, - [2] = i18n("server") .. " " .. i18n("pools.pool") .. ": " .. info.srv_host_pool_id.label - } - } + return { + name = i18n("details.host_pool"), + values = { + [1] = i18n("client") .. " " .. i18n("pools.pool") .. ": " .. info.cli_host_pool_id.label, + [2] = i18n("server") .. " " .. i18n("pools.pool") .. ": " .. info.srv_host_pool_id.label + } + } end -- a############################################### local function format_historical_issue_description(alert_id, score, title, msg, info, alert_scores, add_remediation) - local alert_consts = require "alert_consts" - local alert_entities = require "alert_entities" - - if not alert_id or alert_id == "0" then - return nil - end - - if alert_scores and alert_scores[alert_id] then - score = alert_scores[alert_id] - end - - -- If alert risk is 0 then it comes from ntonpg, else nDPI - local alert_risk = ntop.getFlowAlertRisk(tonumber(alert_id)) - local alert_src - - if (tonumber(alert_risk) == 0) then - alert_src = "ntopng" - alert_risk = alert_id - else - alert_src = "nDPI" - end - - local alert_source = " " .. alert_src .. "" - - local severity_id = map_score_to_severity(score) - local severity = alert_consts.alertSeverityById(severity_id) - local remediation = flow_risk_utils.get_remediation_documentation_link(tostring(alert_risk), alert_src) - - local html = "" .. (msg or "") .. alert_source .. "" .. '' .. score .. '' - - if (add_remediation) then - html = html .. "" .. info .. " " .. remediation .."" - else - html = html .. "" .. info .."" - end - - -- Add Mitre info - local alert_key = alert_consts.getAlertType(alert_id, alert_entities.flow.entity_id) - - if alert_key then - local mitre_info = alert_consts.getAlertMitreInfo(alert_key) - - if mitre_info and mitre_info.mitre_id then - local keys = split(mitre_info.mitre_id, "%.") - local url = "https://attack.mitre.org/techniques/"..keys[1]:gsub("%%", "").."/" - - if keys[2] ~= nil then - url = url .. keys[2]:gsub("%%", "") .. "/" - end - - html = html .. ''..mitre_info.mitre_id.."" - - if(mitre_info.mitre_tactic.i18n_label) then - html = html .. '
' .. i18n(mitre_info.mitre_tactic.i18n_label) .. "" - end - else - html = html .. " " - end - else - html = html .. " " - end - - return html + local alert_consts = require "alert_consts" + local alert_entities = require "alert_entities" + + if not alert_id or alert_id == "0" then + return nil + end + + if alert_scores and alert_scores[alert_id] then + score = alert_scores[alert_id] + end + + -- If alert risk is 0 then it comes from ntonpg, else nDPI + local alert_risk = ntop.getFlowAlertRisk(tonumber(alert_id)) + local alert_src + + if (tonumber(alert_risk) == 0) then + alert_src = "ntopng" + alert_risk = alert_id + else + alert_src = "nDPI" + end + + local alert_source = " " .. alert_src .. "" + + local severity_id = map_score_to_severity(score) + local severity = alert_consts.alertSeverityById(severity_id) + local remediation = flow_risk_utils.get_remediation_documentation_link(tostring(alert_risk), alert_src) + + local html = "" .. (msg or "") .. alert_source .. "" .. '' .. score .. '' + + if (add_remediation) then + html = html .. "" .. info .. " " .. remediation .. "" + else + html = html .. "" .. info .. "" + end + + -- Add Mitre info + local alert_key = alert_consts.getAlertType(alert_id, alert_entities.flow.entity_id) + + if alert_key then + local mitre_info = alert_consts.getAlertMitreInfo(alert_key) + + if mitre_info and mitre_info.mitre_id then + local keys = split(mitre_info.mitre_id, "%.") + local url = "https://attack.mitre.org/techniques/" .. keys[1]:gsub("%%", "") .. "/" + + if keys[2] ~= nil then + url = url .. keys[2]:gsub("%%", "") .. "/" + end + + html = html .. '' .. mitre_info.mitre_id .. "" + + if (mitre_info.mitre_tactic.i18n_label) then + html = html .. '
' .. i18n(mitre_info.mitre_tactic.i18n_label) .. "" + end + else + html = html .. " " + end + else + html = html .. " " + end + + return html end -- ############################################### local function format_historical_issues(flow_details, flow) - local alert_store_utils = require "alert_store_utils" - local alert_entities = require "alert_entities" - local alert_consts = require "alert_consts" - local format_utils = require "format_utils" - local alert_store_instances = alert_store_utils.all_instances_factory() - local alert_utils = require "alert_utils" - local alert_json = json.decode(flow["ALERT_JSON"] or '') or {} - local details = "" - local alert - - local alert_store_instance = alert_store_instances[alert_entities["flow"].alert_store_name] - - if alert_store_instance then - local alerts, _ = alert_store_instance:select_request(nil, "*") - if alerts and #alerts >= 1 then - alert = alerts[1] - details = alert_utils.formatFlowAlertMessage(interface.getId(), alert, alert_json, false, true) - end - end - - local alert_json = json.decode(flow["ALERT_JSON"] or '') or {} - local alert_scores = alert_json.alert_score - local alert_consts = require "alert_consts" - local alert_label = i18n("flow_details.normal") - local alert_id = tonumber(flow["STATUS"] or 0) - local main_alert_score = ntop.getFlowAlertScore(tonumber(alert_id)) - - -- Check if there is a custom score - if alert_scores and alert_scores[tostring(alert_id)] then - main_alert_score = alert_scores[tostring(alert_id)] - end - local severity_id = map_score_to_severity(main_alert_score) - local severity = alert_consts.alertSeverityById(severity_id) - - flow_details[#flow_details + 1] = { - name = i18n('total_flow_score'), - values = {'' .. format_utils.formatValue(tonumber(flow["SCORE"])) .. '', ''} - } - - local html = "" - - -- No status set - if (alert_id ~= 0) then - - alert_label = alert_consts.alertTypeLabel(alert_id, true) - - html = "\n" - html = html .. "\n" - html = html .. format_historical_issue_description(tostring(alert_id), tonumber(main_alert_score), i18n("issues_score"), alert_label, details, alert_scores, true) - end - - local alert_utils = require "alert_utils" - local _, other_issues = alert_utils.format_other_alerts(flow['ALERTS_MAP'], flow['STATUS'], alert_json, false, nil, true) - - if table.len(other_issues) > 0 then - for _, issue in pairs(other_issues or {}) do - local msg, info - local pieces = string.split(issue.msg, "%[") - - if(pieces ~= nil) then - msg = pieces[1] - info = string.gsub(pieces[2], "%]", "") - else - msg = issue.msg - info = "" - end - html = html .. format_historical_issue_description(tostring(issue.alert_id), tonumber(issue.score), '', msg, info, alert_scores, true) - end - end - - flow_details[#flow_details + 1] = { name = i18n('total_flow_score'), values = { html } } - - - return flow_details + local alert_store_utils = require "alert_store_utils" + local alert_entities = require "alert_entities" + local alert_consts = require "alert_consts" + local format_utils = require "format_utils" + local alert_store_instances = alert_store_utils.all_instances_factory() + local alert_utils = require "alert_utils" + local alert_json = json.decode(flow["ALERT_JSON"] or '') or {} + local details = "" + local alert + + local alert_store_instance = alert_store_instances[alert_entities["flow"].alert_store_name] + + if alert_store_instance then + local alerts, _ = alert_store_instance:select_request(nil, "*") + if alerts and #alerts >= 1 then + alert = alerts[1] + details = alert_utils.formatFlowAlertMessage(interface.getId(), alert, alert_json, false, true) + end + end + + local alert_json = json.decode(flow["ALERT_JSON"] or '') or {} + local alert_scores = alert_json.alert_score + local alert_consts = require "alert_consts" + local alert_label = i18n("flow_details.normal") + local alert_id = tonumber(flow["STATUS"] or 0) + local main_alert_score = ntop.getFlowAlertScore(tonumber(alert_id)) + + -- Check if there is a custom score + if alert_scores and alert_scores[tostring(alert_id)] then + main_alert_score = alert_scores[tostring(alert_id)] + end + local severity_id = map_score_to_severity(main_alert_score) + local severity = alert_consts.alertSeverityById(severity_id) + + flow_details[#flow_details + 1] = { + name = i18n('total_flow_score'), + values = {'' .. format_utils.formatValue(tonumber(flow["SCORE"])) .. + '', ''} + } + + local html = "" + + -- No status set + if (alert_id ~= 0) then + + alert_label = alert_consts.alertTypeLabel(alert_id, true) + + html = "
" .. i18n("description") .. "" .. i18n("score") .. "".. i18n("info") .. " / ".. i18n("remediation").. "".. i18n("mitre_id") .. "
\n" + html = + html .. "\n" + html = html .. + format_historical_issue_description(tostring(alert_id), tonumber(main_alert_score), + i18n("issues_score"), alert_label, details, alert_scores, true) + end + + local alert_utils = require "alert_utils" + local _, other_issues = alert_utils.format_other_alerts(flow['ALERTS_MAP'], flow['STATUS'], alert_json, false, nil, + true) + + if table.len(other_issues) > 0 then + for _, issue in pairs(other_issues or {}) do + local msg, info + local pieces = string.split(issue.msg, "%[") + + if (pieces ~= nil) then + msg = pieces[1] + info = string.gsub(pieces[2], "%]", "") + else + msg = issue.msg + info = "" + end + html = html .. + format_historical_issue_description(tostring(issue.alert_id), tonumber(issue.score), '', msg, + info, alert_scores, true) + end + end + + flow_details[#flow_details + 1] = { + name = i18n('total_flow_score'), + values = {html} + } + + return flow_details end -- ############################################### local function format_tcp_connection_states(info) - local conn_states = {} - conn_states[#conn_states + 1] = string.format("%s: %s (%s)", i18n("flow_fields_description.major_connection_state"), - i18n(string.format("flow_fields_description.major_connection_states.%s", info.major_connection_state.value)), - i18n(string.format("flow_fields_description.minor_connection_states_info.%u", info.minor_connection_state.value))) - conn_states[#conn_states + 1] = string.format("%s: %s (%s)", i18n("flow_fields_description.minor_connection_state"), - i18n(string.format("flow_fields_description.minor_connection_states.%s", info.minor_connection_state.value)), - i18n(string.format("flow_fields_description.minor_connection_states_info.%u", info.minor_connection_state.value))) - return conn_states + local conn_states = {} + conn_states[#conn_states + 1] = string.format("%s: %s (%s)", i18n("flow_fields_description.major_connection_state"), + i18n(string.format("flow_fields_description.major_connection_states.%s", info.major_connection_state.value)), + i18n(string.format("flow_fields_description.minor_connection_states_info.%u", info.minor_connection_state.value))) + conn_states[#conn_states + 1] = string.format("%s: %s (%s)", i18n("flow_fields_description.minor_connection_state"), + i18n(string.format("flow_fields_description.minor_connection_states.%s", info.minor_connection_state.value)), + i18n(string.format("flow_fields_description.minor_connection_states_info.%u", info.minor_connection_state.value))) + return conn_states end -- ############################################### local function format_historical_community_id(flow) - return { - name = i18n("db_explorer.community_id"), - values = {flow["COMMUNITY_ID"]} - } + return { + name = i18n("db_explorer.community_id"), + values = {flow["COMMUNITY_ID"]} + } end -- ############################################### local function add_info_field(flow) - local alert_json = json.decode(flow["ALERT_JSON"] or '') or {} - local proto_details = {} - local add_info = true - if table.len(alert_json) >= 1 then - for proto, info in pairs(alert_json["proto"] or {}) do - if proto == "tls" then - add_info = isEmptyString(info.client_requested_server_name) - break - elseif proto == "dns" then - add_info = isEmptyString(info.last_query) - break - elseif proto == "http" then - add_info = isEmptyString(info.last_url) - break - elseif proto == "icmp" then - -- Alwais add for icmp - break - end - end - end - - return add_info + local alert_json = json.decode(flow["ALERT_JSON"] or '') or {} + local proto_details = {} + local add_info = true + if table.len(alert_json) >= 1 then + for proto, info in pairs(alert_json["proto"] or {}) do + if proto == "tls" then + add_info = isEmptyString(info.client_requested_server_name) + break + elseif proto == "dns" then + add_info = isEmptyString(info.last_query) + break + elseif proto == "http" then + add_info = isEmptyString(info.last_url) + break + elseif proto == "icmp" then + -- Alwais add for icmp + break + end + end + end + + return add_info end -- ############################################### local function format_historical_info(flow) - local historical_flow_utils = require "historical_flow_utils" - local info_field = historical_flow_utils.get_historical_url(flow["INFO"], "info", flow["INFO"], true, flow["INFO"], - true) - - - return { - name = i18n("db_explorer.info"), - values = {info_field} - } + local historical_flow_utils = require "historical_flow_utils" + local info_field = historical_flow_utils.get_historical_url(flow["INFO"], "info", flow["INFO"], true, flow["INFO"], + true) + + return { + name = i18n("db_explorer.info"), + values = {info_field} + } end -- ############################################### local function format_historical_probe(flow_details, flow, info) - local historical_flow_utils = require "historical_flow_utils" - local format_utils = require "format_utils" - - local alias = getFlowDevAlias(info["probe_ip"]["value"], true) - local name - - if alias == info["probe_ip"]["value"] then - name = format_name_value(info["probe_ip"]["value"], info["probe_ip"]["label"], true) - else - name = alias - end - - local info_field = { - device_ip = historical_flow_utils.get_historical_url(name, "probe_ip", info["probe_ip"]["value"], true, - info["probe_ip"]["title"]) - } - - if (flow["INPUT_SNMP"]) and (tonumber(flow["INPUT_SNMP"]) ~= 0) then - info_field["input_interface"] = historical_flow_utils.get_historical_url( - format_utils.formatSNMPInterface(flow["PROBE_IP"], flow["INPUT_SNMP"]), "input_snmp", - info["input_snmp"]["value"], true, info["input_snmp"]["title"]) - end - - if (flow["OUTPUT_SNMP"]) and (tonumber(flow["OUTPUT_SNMP"]) ~= 0) then - info_field["output_interface"] = historical_flow_utils.get_historical_url( - format_utils.formatSNMPInterface(flow["PROBE_IP"], flow["OUTPUT_SNMP"]), "output_snmp", - info["output_snmp"]["value"], true, info["output_snmp"]["title"]) - end - - if table.len(info_field) > 1 then - flow_details[#flow_details + 1] = { - name = i18n("details.flow_snmp_localization"), - values = { "" } - } - for field, value in pairs(info_field) do - flow_details[#flow_details + 1] = { - name = "", - values = { i18n(field), value } - } - end - end - - return flow_details + local historical_flow_utils = require "historical_flow_utils" + local format_utils = require "format_utils" + + local alias = getFlowDevAlias(info["probe_ip"]["value"], true) + local name + + if alias == info["probe_ip"]["value"] then + name = format_name_value(info["probe_ip"]["value"], info["probe_ip"]["label"], true) + else + name = alias + end + + local info_field = { + device_ip = historical_flow_utils.get_historical_url(name, "probe_ip", info["probe_ip"]["value"], true, + info["probe_ip"]["title"]) + } + + if (flow["INPUT_SNMP"]) and (tonumber(flow["INPUT_SNMP"]) ~= 0) then + info_field["input_interface"] = historical_flow_utils.get_historical_url( + format_utils.formatSNMPInterface(flow["PROBE_IP"], flow["INPUT_SNMP"]), "input_snmp", + info["input_snmp"]["value"], true, info["input_snmp"]["title"]) + end + + if (flow["OUTPUT_SNMP"]) and (tonumber(flow["OUTPUT_SNMP"]) ~= 0) then + info_field["output_interface"] = historical_flow_utils.get_historical_url( + format_utils.formatSNMPInterface(flow["PROBE_IP"], flow["OUTPUT_SNMP"]), "output_snmp", + info["output_snmp"]["value"], true, info["output_snmp"]["title"]) + end + + if table.len(info_field) > 1 then + flow_details[#flow_details + 1] = { + name = i18n("details.flow_snmp_localization"), + values = {""} + } + for field, value in pairs(info_field) do + flow_details[#flow_details + 1] = { + name = "", + values = {i18n(field), value} + } + end + end + + return flow_details end -- ############################################### local function format_historical_latency(flow, value, cli_or_srv) - return { - name = i18n("db_explorer." .. cli_or_srv .. "_latency"), - values = {(tonumber(flow[value]) / 1000) .. " msec"} - } + return { + name = i18n("db_explorer." .. cli_or_srv .. "_latency"), + values = {(tonumber(flow[value]) / 1000) .. " msec"} + } end -- ############################################### local function format_historical_obs_point(flow) - return { - name = i18n("db_explorer.observation_point"), - values = {getObsPointAlias(flow["OBSERVATION_POINT_ID"], true, true)} - } + return { + name = i18n("db_explorer.observation_point"), + values = {getObsPointAlias(flow["OBSERVATION_POINT_ID"], true, true)} + } end -- ############################################### local function format_historical_proto_info(flow_details, proto_info) - local info = format_proto_info(flow_details, proto_info) - return info + local info = format_proto_info(flow_details, proto_info) + return info end -- ############################################### local function format_historical_flow_traffic_stats(rowspan, cli2srv_retr, srv2cli_retr, cli2srv_ooo, srv2cli_ooo, - cli2srv_lost, srv2cli_lost) - - local flow_details = {} - - if rowspan > 0 then - flow_details[#flow_details + 1] = { - name = i18n("flow_details.tcp_packet_analysis"), - values = { "",i18n("client") .. " " .. i18n("server") .. - " / " .. i18n("client") .. " " .. i18n("server") } - } - - if ((cli2srv_retr and (tonumber(cli2srv_retr) > 0)) or (srv2cli_retr and (tonumber(srv2cli_retr) > 0))) then - flow_details[#flow_details + 1] = { - name = "", - values = { i18n("details.retransmissions"), formatPackets(cli2srv_retr) .." / " ..formatPackets(srv2cli_retr) } - } - end - if ((cli2srv_ooo and (tonumber(cli2srv_ooo) > 0)) or (srv2cli_ooo and (tonumber(srv2cli_ooo) > 0))) then - - flow_details[#flow_details + 1] = { - name = "", - values = { i18n("details.out_of_order"), formatPackets(cli2srv_ooo) .." / " ..formatPackets(srv2cli_ooo) } - } - end - if ((cli2srv_ooo and (tonumber(cli2srv_ooo) > 0)) or (srv2cli_ooo and (tonumber(srv2cli_ooo) > 0))) then - flow_details[#flow_details + 1] = { - name = "", - values = { i18n("details.lost"), formatPackets(cli2srv_lost) .." / " ..formatPackets(srv2cli_lost) } - } - end - end - - return flow_details + cli2srv_lost, srv2cli_lost) + + local flow_details = {} + + if rowspan > 0 then + flow_details[#flow_details + 1] = { + name = i18n("flow_details.tcp_packet_analysis"), + values = {"", + i18n("client") .. " " .. i18n("server") .. " / " .. + i18n("client") .. " " .. i18n("server")} + } + + if ((cli2srv_retr and (tonumber(cli2srv_retr) > 0)) or (srv2cli_retr and (tonumber(srv2cli_retr) > 0))) then + flow_details[#flow_details + 1] = { + name = "", + values = {i18n("details.retransmissions"), + formatPackets(cli2srv_retr) .. " / " .. formatPackets(srv2cli_retr)} + } + end + if ((cli2srv_ooo and (tonumber(cli2srv_ooo) > 0)) or (srv2cli_ooo and (tonumber(srv2cli_ooo) > 0))) then + + flow_details[#flow_details + 1] = { + name = "", + values = {i18n("details.out_of_order"), + formatPackets(cli2srv_ooo) .. " / " .. formatPackets(srv2cli_ooo)} + } + end + if ((cli2srv_ooo and (tonumber(cli2srv_ooo) > 0)) or (srv2cli_ooo and (tonumber(srv2cli_ooo) > 0))) then + flow_details[#flow_details + 1] = { + name = "", + values = {i18n("details.lost"), formatPackets(cli2srv_lost) .. " / " .. formatPackets(srv2cli_lost)} + } + end + end + + return flow_details end local function format_historical_flow_rtt(client_nw_latency, server_nw_latency) - local rtt = client_nw_latency + server_nw_latency - local cli2srv = round(client_nw_latency, 3) - local srv2cli = round(server_nw_latency, 3) - local values = - '
' .. - cli2srv .. ' ms (client)
' .. '
' .. srv2cli .. ' ms (server)
' - return { - name = i18n("flow_details.rtt_breakdown"), - values = {values} - } + local rtt = client_nw_latency + server_nw_latency + local cli2srv = round(client_nw_latency, 3) + local srv2cli = round(server_nw_latency, 3) + local values = + '
' .. + cli2srv .. ' ms (client)
' .. '
' .. srv2cli .. ' ms (server)
' + return { + name = i18n("flow_details.rtt_breakdown"), + values = {values} + } end @@ -549,117 +580,125 @@ end -- This function format the historical flow details page function historical_flow_details_formatter.formatHistoricalFlowDetails(flow) - local historical_flow_utils = require "historical_flow_utils" - local flow_details = {} - - if flow then - local info = historical_flow_utils.format_clickhouse_record(flow) - flow_details[#flow_details + 1] = format_historical_flow_label(flow) - flow_details[#flow_details + 1] = format_historical_protocol_label(flow) - flow_details[#flow_details + 1] = format_historical_last_first_seen(flow, info) - flow_details[#flow_details + 1] = format_historical_total_traffic(flow) - flow_details[#flow_details + 1] = format_historical_client_server_bytes(flow) - flow_details[#flow_details + 1] = format_historical_bytes_progress_bar(flow, info) - - if ((tonumber(flow["SERVER_NW_LATENCY_US"]) > 0) or (tonumber(flow["CLIENT_NW_LATENCY_US"]) > 0)) then - flow_details[#flow_details + 1] = format_historical_flow_rtt(tonumber(flow["SERVER_NW_LATENCY_US"]), - tonumber(flow["CLIENT_NW_LATENCY_US"])) - end - - if (info['dst2src_dscp']) and (info['src2dst_dscp']) then - flow_details[#flow_details + 1] = format_historical_tos(flow) - end - - if (info["l4proto"]) and (info["l4proto"]["label"] == 'TCP') then - flow_details[#flow_details + 1] = format_historical_tcp_flags(flow, info) - - if (info["major_connection_state"] ~= 0 and info["minor_connection_state"] ~= 0) then - local conn_states = format_tcp_connection_states(info) - - for _, state in pairs(conn_states or {}) do - flow_details[#flow_details + 1] = { - name = '', -- Empty label - values = {state} - } - end - - end - - end - - if (info["cli_host_pool_id"]) and (info["cli_host_pool_id"]["value"] ~= '0') and - (info["srv_host_pool_id"]["value"] ~= '0') then - flow_details[#flow_details + 1] = format_historical_host_pool(flow, info) - end - - if (info["score"]) and (info["score"]["value"] ~= 0) then - flow_details = format_historical_issues(flow_details, flow) - end - - if (info['COMMUNITY_ID']) and (not isEmptyString(info['COMMUNITY_ID'])) then - flow_details[#flow_details + 1] = format_historical_community_id(flow) - end - - if (info['info']) and (not isEmptyString(info['info']["title"])) then - if add_info_field(flow) then - flow_details[#flow_details + 1] = format_historical_info(flow) - end - end - - flow_details = format_pre_post_nat_info(flow_details, flow, info) - - if (flow["PROBE_IP"] and not isEmptyString(flow['PROBE_IP']) and (flow['PROBE_IP'] ~= '0.0.0.0')) then - flow_details = format_historical_probe(flow_details, flow, info) - end - - if tonumber(flow["CLIENT_NW_LATENCY_US"]) ~= 0 then - flow_details[#flow_details + 1] = format_historical_latency(flow, "CLIENT_NW_LATENCY_US", "cli") - end - - if tonumber(flow["SERVER_NW_LATENCY_US"]) ~= 0 then - flow_details[#flow_details + 1] = format_historical_latency(flow, "SERVER_NW_LATENCY_US", "srv") - end - local alert_json = json.decode(flow["ALERT_JSON"] or '') or {} - - if (alert_json["traffic_stats"] and table.len(alert_json["traffic_stats"]) > 0) then - local rowspan = 1; - - if (alert_json["traffic_stats"]["cli2srv_retransmissions"] ~= 0 or - alert_json["traffic_stats"]["srv2cli_retransmissions"] ~= 0) then - rowspan = rowspan + 1 - end - - if (alert_json["traffic_stats"]["cli2srv_out_of_order"] ~= 0 or - alert_json["traffic_stats"]["srv2cli_out_of_order"] ~= 0) then - rowspan = rowspan + 1 - end - - if (alert_json["traffic_stats"]["cli2srv_lost"] ~= 0 or alert_json["traffic_stats"]["srv2cli_lost"] ~= 0) then - rowspan = rowspan + 1 - end - flow_details = table.merge(flow_details,format_historical_flow_traffic_stats(rowspan, - alert_json["traffic_stats"]["cli2srv_retransmissions"], - alert_json["traffic_stats"]["srv2cli_retransmissions"], - alert_json["traffic_stats"]["cli2srv_out_of_order"], - alert_json["traffic_stats"]["srv2cli_out_of_order"], alert_json["traffic_stats"]["cli2srv_lost"], - alert_json["traffic_stats"]["srv2cli_lost"])) - end - - if tonumber(flow["OBSERVATION_POINT_ID"]) ~= 0 then - flow_details[#flow_details + 1] = format_historical_obs_point(flow) - end - - if table.len(alert_json["proto"]) > 0 then - flow_details = format_historical_proto_info(flow_details, alert_json["proto"]) - - if (type(flow_details[#flow_details]['values']) == 'table') and - (table.len(flow_details[#flow_details]['values']) == 0) then - table.remove(flow_details, #flow_details) - end - end - end - - return flow_details + local historical_flow_utils = require "historical_flow_utils" + local flow_details = {} + + if flow then + local info = historical_flow_utils.format_clickhouse_record(flow) + flow_details[#flow_details + 1] = format_historical_flow_label(flow) + flow_details[#flow_details + 1] = format_historical_protocol_label(flow) + flow_details[#flow_details + 1] = format_historical_last_first_seen(flow, info) + flow_details[#flow_details + 1] = format_historical_total_traffic(flow) + flow_details[#flow_details + 1] = format_historical_client_server_bytes(flow) + flow_details[#flow_details + 1] = format_historical_bytes_progress_bar(flow, info) + + if ((tonumber(flow["SERVER_NW_LATENCY_US"]) > 0) or (tonumber(flow["CLIENT_NW_LATENCY_US"]) > 0)) then + flow_details[#flow_details + 1] = format_historical_flow_rtt(tonumber(flow["SERVER_NW_LATENCY_US"]), + tonumber(flow["CLIENT_NW_LATENCY_US"])) + end + + if (info['dst2src_dscp']) and (info['src2dst_dscp']) then + flow_details[#flow_details + 1] = format_historical_tos(flow) + end + + if (info["l4proto"]) and (info["l4proto"]["label"] == 'TCP') then + flow_details[#flow_details + 1] = format_historical_tcp_flags(flow, info) + + if (info["major_connection_state"] ~= 0 and info["minor_connection_state"] ~= 0) then + local conn_states = format_tcp_connection_states(info) + + for _, state in pairs(conn_states or {}) do + flow_details[#flow_details + 1] = { + name = '', -- Empty label + values = {state} + } + end + + end + + end + + if (info["cli_host_pool_id"]) and (info["cli_host_pool_id"]["value"] ~= '0') and + (info["srv_host_pool_id"]["value"] ~= '0') then + flow_details[#flow_details + 1] = format_historical_host_pool(flow, info) + end + + if (info["score"]) and (info["score"]["value"] ~= 0) then + flow_details = format_historical_issues(flow_details, flow) + end + + if (info['COMMUNITY_ID']) and (not isEmptyString(info['COMMUNITY_ID'])) then + flow_details[#flow_details + 1] = format_historical_community_id(flow) + end + + if (info['info']) and (not isEmptyString(info['info']["title"])) then + if add_info_field(flow) then + flow_details[#flow_details + 1] = format_historical_info(flow) + end + end + + flow_details = format_pre_post_nat_info(flow_details, flow, info) + + if (flow["PROBE_IP"] and not isEmptyString(flow['PROBE_IP']) and (flow['PROBE_IP'] ~= '0.0.0.0')) then + flow_details = format_historical_probe(flow_details, flow, info) + end + + if tonumber(flow["CLIENT_NW_LATENCY_US"]) ~= 0 then + flow_details[#flow_details + 1] = format_historical_latency(flow, "CLIENT_NW_LATENCY_US", "cli") + end + + if tonumber(flow["SERVER_NW_LATENCY_US"]) ~= 0 then + flow_details[#flow_details + 1] = format_historical_latency(flow, "SERVER_NW_LATENCY_US", "srv") + end + local alert_json = json.decode(flow["ALERT_JSON"] or '') or {} + + if (alert_json["traffic_stats"] and table.len(alert_json["traffic_stats"]) > 0) then + local rowspan = 1; + + if (alert_json["traffic_stats"]["cli2srv_retransmissions"] ~= 0 or + alert_json["traffic_stats"]["srv2cli_retransmissions"] ~= 0) then + rowspan = rowspan + 1 + end + + if (alert_json["traffic_stats"]["cli2srv_out_of_order"] ~= 0 or + alert_json["traffic_stats"]["srv2cli_out_of_order"] ~= 0) then + rowspan = rowspan + 1 + end + + if (alert_json["traffic_stats"]["cli2srv_lost"] ~= 0 or alert_json["traffic_stats"]["srv2cli_lost"] ~= 0) then + rowspan = rowspan + 1 + end + flow_details = table.merge(flow_details, + format_historical_flow_traffic_stats(rowspan, alert_json["traffic_stats"]["cli2srv_retransmissions"], + alert_json["traffic_stats"]["srv2cli_retransmissions"], + alert_json["traffic_stats"]["cli2srv_out_of_order"], + alert_json["traffic_stats"]["srv2cli_out_of_order"], alert_json["traffic_stats"]["cli2srv_lost"], + alert_json["traffic_stats"]["srv2cli_lost"])) + end + + if tonumber(flow["OBSERVATION_POINT_ID"]) ~= 0 then + flow_details[#flow_details + 1] = format_historical_obs_point(flow) + end + + if info.wlan_ssid then + flow_details[#flow_details + 1] = format_historical_wlan_ssid(flow, info) + end + + if info.apn_mac then + flow_details[#flow_details + 1] = format_historical_wtp_mac_address(flow, info) + end + + if table.len(alert_json["proto"]) > 0 then + flow_details = format_historical_proto_info(flow_details, alert_json["proto"]) + + if (type(flow_details[#flow_details]['values']) == 'table') and + (table.len(flow_details[#flow_details]['values']) == 0) then + table.remove(flow_details, #flow_details) + end + end + end + + return flow_details end return historical_flow_details_formatter diff --git a/scripts/lua/modules/tag_utils.lua b/scripts/lua/modules/tag_utils.lua index 0edf62797e68..f383f28e730c 100644 --- a/scripts/lua/modules/tag_utils.lua +++ b/scripts/lua/modules/tag_utils.lua @@ -615,7 +615,8 @@ tag_utils.defined_tags = { operators = {'eq', 'neq'} }, wlan_ssid = { - value_type = 'text', + type = tag_utils.input_types.select, + value_type = 'wlan_ssid', i18n_label = i18n('db_search.tags.wlan_ssid'), operators = {'eq', 'neq', 'in', 'nin'} }, @@ -945,6 +946,30 @@ function tag_utils.get_tag_info(id, entity, hide_exporters_name, restrict_filter } end + elseif tag.value_type == "wlan_ssid" then + local flows_stats = interface.getActiveFlowsStats() + filter.value_type = 'array' + filter.options = {} + local tmp_list = {} + + if table.len(flows_stats["wlan_ssid"]) > 0 then + local tmp_list = {} + for key, value in pairs(flows_stats["wlan_ssid"] or {}, asc) do + tmp_list[key] = { + key = "wlan_ssid", + value = key, + label = key + } + end + + for key, _ in pairsByKeys(tmp_list, asc) do + filter.options[#filter.options + 1] = { + value = key, + label = key, + } + end + end + elseif tag.value_type == "mitre_tactic" then filter.value_type = 'array' diff --git a/scripts/lua/rest/v2/get/flow/flow_filters.lua b/scripts/lua/rest/v2/get/flow/flow_filters.lua index 31a3bab6be42..cd0344ea3cf6 100644 --- a/scripts/lua/rest/v2/get/flow/flow_filters.lua +++ b/scripts/lua/rest/v2/get/flow/flow_filters.lua @@ -24,6 +24,7 @@ local flow_info = _GET["flow_info"] local flowstats = interface.getActiveFlowsStats(host, nil, false, talking_with, client, server, flow_info) local selected_ip = _GET["flowhosts_type"] + local rsp = {} if interface.isView() then @@ -457,6 +458,34 @@ if table.len(networks_stats) > 1 then } end +local wlan_ssid_filters = {{ + key = "wlan_ssid", + value = "", + label = i18n("all") +}} + +if table.len(flowstats["wlan_ssid"]) > 0 then + local tmp_list = {} + for key, value in pairs(flowstats["wlan_ssid"] or {}, asc) do + tmp_list[key] = { + key = "wlan_ssid", + value = key, + label = key + } + end + + for _, value in pairsByKeys(tmp_list, asc) do + wlan_ssid_filters[#wlan_ssid_filters + 1] = value + end + + rsp[#rsp + 1] = { + action = "wlan_ssid", + label = i18n("flow_fields_description.wlan_ssid"), + name = "wlan_ssid", + value = wlan_ssid_filters + } +end + if ntop.isPro() and interface.isPacketInterface() == false then local flowdevs = interface.getFlowDevices() or {} local devips = getProbesName(flowdevs) diff --git a/scripts/templates/report/default.json b/scripts/templates/report/default.json index 4bb222549191..f4aa31d699ca 100644 --- a/scripts/templates/report/default.json +++ b/scripts/templates/report/default.json @@ -30,6 +30,9 @@ }, { "name" : "host_pool" + }, + { + "name" : "wlan_ssid" } ], "components" : [ diff --git a/src/FlowStats.cpp b/src/FlowStats.cpp index 6fda0329a9db..da60b1e31864 100644 --- a/src/FlowStats.cpp +++ b/src/FlowStats.cpp @@ -229,6 +229,15 @@ void FlowStats::lua(lua_State *vm) { lua_pushstring(vm, "talking_with"); lua_insert(vm, -2); lua_settable(vm, -3); + + lua_newtable(vm); + + for (it2 = wlan_ssid.begin(); it2 != wlan_ssid.end(); it2++) + lua_push_uint32_table_entry(vm, it2->first.c_str(), it2->second); + + lua_pushstring(vm, "wlan_ssid"); + lua_insert(vm, -2); + lua_settable(vm, -3); } /* *************************************** */ @@ -249,6 +258,18 @@ void FlowStats::updateTalkingHosts(Flow *f) { /* *************************************** */ +void FlowStats::updateWLANSSID(Flow *f) { + char *wlan_ssid_string = f->getWLANSSID(); + if(wlan_ssid_string) { + std::pair::iterator, bool> ret; + ret = wlan_ssid.insert(std::pair( + wlan_ssid_string, 1)); + if (!ret.second) ret.first->second++; + } +} + +/* *************************************** */ + void FlowStats::resetStats() { memset(counters, 0, sizeof(counters)); memset(protocols, 0, sizeof(protocols)); @@ -256,6 +277,7 @@ void FlowStats::resetStats() { memset(dscps, 0, sizeof(dscps)); memset(host_pools, 0, sizeof(host_pools)); talking_hosts.clear(); + wlan_ssid.clear(); } /* *************************************** */ diff --git a/src/NetworkInterface.cpp b/src/NetworkInterface.cpp index d7d4168d37c3..bf262dc1ecfd 100644 --- a/src/NetworkInterface.cpp +++ b/src/NetworkInterface.cpp @@ -4845,6 +4845,7 @@ static bool flow_matches(Flow *f, struct flowHostRetriever *retriever) { u_int32_t asn_filter; char *username_filter; char *pidname_filter; + char *wlan_ssid_filter; u_int32_t deviceIP = 0; int32_t iface_index = -1; u_int32_t inIndex, outIndex; @@ -5121,6 +5122,10 @@ static bool flow_matches(Flow *f, struct flowHostRetriever *retriever) { return (false); } + if (retriever->pag && retriever->pag->wlanSSIDFilter(&wlan_ssid_filter) && + (!f->getWLANSSID() || strcmp(f->getWLANSSID(), wlan_ssid_filter))) + return (false); + if (retriever->pag && retriever->pag->dscpFilter(&dscp_filter) && f->getCli2SrvDSCP() != dscp_filter && f->getSrv2CliDSCP() != dscp_filter) @@ -6115,6 +6120,9 @@ static bool flow_sum_stats(GenericHashEntry *flow, void *user_data, /* Add this info only in case some filter host is requested */ stats->updateTalkingHosts(f); } + if(f->getWLANSSID()) { + stats->updateWLANSSID(f); + } retriever->totBytesSent += f->get_bytes_cli2srv(); retriever->totBytesRcvd += f->get_bytes_srv2cli(); retriever->totThpt += f->get_bytes_thpt(); diff --git a/src/Paginator.cpp b/src/Paginator.cpp index 861df1e9eaf7..25010414dab4 100644 --- a/src/Paginator.cpp +++ b/src/Paginator.cpp @@ -37,6 +37,7 @@ Paginator::Paginator() { traffic_profile_filter = NULL; username_filter = NULL; pidname_filter = NULL; + wlan_ssid_filter = NULL; /* bool */ a2z_sort_order = true; @@ -90,6 +91,7 @@ Paginator::Paginator() { Paginator::~Paginator() { if (sort_column) free(sort_column); if (country_filter) free(country_filter); + if (wlan_ssid_filter) free(wlan_ssid_filter); if (host_filter) free(host_filter); if (client_filter) free(client_filter); if (server_filter) free(server_filter); @@ -121,6 +123,8 @@ void Paginator::readOptions(lua_State* L, int index) { sort_column = strdup(lua_tostring(L, -1)); } else if (!strcmp(key, "deviceIpFilter")) { deviceIP = ntohl(inet_addr(lua_tostring(L, -1))); + } else if (!strcmp(key, "wlanSSIDFilter")) { + wlan_ssid_filter = strdup(lua_tostring(L, -1)); } else if (!strcmp(key, "countryFilter")) { if (country_filter) free(country_filter); country_filter = strdup(lua_tostring(L, -1));
" .. i18n("description") .. "" .. i18n("score") .. "" .. i18n("info") .. + " / " .. i18n("remediation") .. "" .. i18n("mitre_id") .. "