-- -- (C) 2013-25 - ntop.org -- dirs = ntop.getDirs() package.path = dirs.installdir .. "/scripts/lua/modules/?.lua;" .. package.path require "lua_utils_get" require "lua_utils_gui" local format_utils = require "format_utils" local flow_data_preset = {} -- This table contains the mapping to the live and historical flows, -- if the column has to be used as key (except bytes/packets everything should be a key) -- and the formatter local columns = { asn = {filters = {live = "asnFilter", historical = {"src_asn", "dst_asn"}}}, src_asn = { live = "src_as", historical = "SRC_ASN", is_key = true, filters = {live = "asnSrcFilter", historical = "SRC_ASN"}, formatter = { funct = format_utils.formatASN, link = "/lua/as_overview.lua?asn=%s" } }, dst_asn = { live = "dst_as", historical = "DST_ASN", is_key = true, filters = {live = "asnDstFilter", historical = "DST_ASN"}, formatter = { funct = format_utils.formatASN, link = "/lua/as_overview.lua?asn=%s" } }, src_peer_asn = { live = "src_peer_as", historical = "SRC_PEER_ASN", is_key = true, formatter = { funct = format_utils.formatASN, link = "/lua/as_overview.lua?asn=%s" } }, dst_peer_asn = { live = "dst_peer_as", historical = "DST_PEER_ASN", is_key = true, formatter = { funct = format_utils.formatASN, link = "/lua/as_overview.lua?asn=%s" } }, in_device = { live = "device_ip", historical = "PROBE_IP", is_key = true, filters = {live = "deviceIpFilter", historical = "PROBE_IP"}, db_formatting_fun = "IPv4NumToString", formatter = { funct = getProbeName, link = "/lua/pro/enterprise/exporters.lua?%s", generateLinkParams = generateExporterLink } }, out_device = { live = "device_ip", historical = "PROBE_IP", is_key = true, db_formatting_fun = "IPv4NumToString", filters = {live = "deviceIpFilter", historical = "PROBE_IP"}, formatter = { funct = getProbeName, link = "/lua/pro/enterprise/exporters.lua?%s", generateLinkParams = generateExporterLink } }, device = { live = "device_ip", historical = "PROBE_IP", is_key = true, filters = {live = "deviceIpFilter", historical = "PROBE_IP"}, db_formatting_fun = "IPv4NumToString", formatter = { funct = getProbeName, link = "/lua/pro/enterprise/exporters.lua?%s", generateLinkParams = generateExporterLink } }, in_iface_index = { live = "in_index", historical = "INPUT_SNMP", is_key = true, formatter = { funct = format_portidx_name, column_dependent = "in_device", link = "/lua/pro/enterprise/exporter_details.lua?%s", generateLinkParams = generateExporterInterfaceLink } }, out_iface_index = { live = "out_index", historical = "OUTPUT_SNMP", is_key = true, formatter = { funct = format_portidx_name, column_dependent = "out_device", link = "/lua/pro/enterprise/exporter_details.lua?%s", generateLinkParams = generateExporterInterfaceLink } }, interface = { formatter = { funct = format_portidx_name, column_dependent = "device", link = "/lua/pro/enterprise/exporter_details.lua?%s", generateLinkParams = generateExporterInterfaceLink } }, bytes_sent = { live = "bytes_sent", historical = "SUM(SRC2DST_BYTES)", invert_with = "bytes_rcvd" }, bytes_rcvd = { live = "bytes_rcvd", historical = "SUM(DST2SRC_BYTES)", invert_with = "bytes_sent" }, total_bytes = { live = "bytes", historical = "SUM(TOTAL_BYTES)", }, as = { formatter = { funct = format_utils.formatASN, link = "/lua/as_overview.lua?asn=%s" } }, transit_as = { formatter = { funct = format_utils.formatASN, link = "/lua/as_overview.lua?asn=%s" } }, src_transit_as = { formatter = { funct = format_utils.formatASN, link = "/lua/as_overview.lua?asn=%s" } }, dst_transit_as = { formatter = { funct = format_utils.formatASN, link = "/lua/as_overview.lua?asn=%s" } }, src_peer_asn_1 = { formatter = { funct = format_utils.formatASN, link = "/lua/as_overview.lua?asn=%s" } }, dst_peer_asn_1 = { formatter = { funct = format_utils.formatASN, link = "/lua/as_overview.lua?asn=%s" } }, src_peer_asn_2 = { formatter = { funct = format_utils.formatASN, link = "/lua/as_overview.lua?asn=%s" } }, dst_peer_asn_2 = { formatter = { funct = format_utils.formatASN, link = "/lua/as_overview.lua?asn=%s" } }, ifid = {live = "ifid", historical = "INTERFACE_ID"}, first_seen = {historical = "FIRST_SEEN"}, last_seen = {historical = "LAST_SEEN"} } -- ########################################### -- @brief Given a list of ids in an array format, returns the -- name of the data in case of live flows or historical -- @param columns_id Array, containing a list of columns ids -- @param is_historical Boolean, true if historical ids are needed, false for live ones -- @return a list of matching ids for the requested type (live or historical) function flow_data_preset.retrieveColumns(columns_id, is_historical) local id_list = {} local data_type = "live" if is_historical then data_type = "historical" end for position, id in pairs(columns_id or {}) do if columns[id] then local column_info = columns[id] column_info["key"] = column_info[data_type] column_info["id"] = id id_list[position] = column_info end end return id_list end -- ########################################### -- @brief given a column_id returns the info about that column -- @param columns_id String, containing a list of columns ids -- @return a list of matching ids for the requested type (live or historical) function flow_data_preset.getColumn(column_id) local column_info = {} if columns[column_id] then column_info = columns[column_id] column_info["id"] = column_id end return column_info end -- ########################################### -- @brief Given a list of filters, returns a list of particular conditions for each filter if available -- @param where Array, containing a list of ids for the where -- @param available_filters List, containing a list filters, key is the key of the filter, value is the value -- @param is_historical Boolean, true if historical ids are needed, false for live ones -- @return a list of filters, key - value function flow_data_preset.convertFilters(where, available_filters, is_historical) local where_query = {} local data_type = "live" if (not available_filters) or (table.len(available_filters) == 0) then return where_query end if is_historical then data_type = "historical" end for _, key in pairs(where or {}) do if (columns[key] and columns[key]["filters"] and columns[key]["filters"][data_type]) then local filter = columns[key]["filters"][data_type] if data_type == "live" then where_query[filter] = available_filters[key] else -- Multiple filters only available in historical, see asn if type(filter) == "table" then for _, or_filter in pairs(filter or {}) do local new_filter = columns[or_filter] new_filter.filter_value = available_filters[key] new_filter.id = or_filter new_filter.key = new_filter["filters"][data_type] if not where_query[key] then where_query[key] = {} end where_query[key][#where_query[key] + 1] = new_filter end else where_query[filter] = columns[key] where_query[filter].filter_value = available_filters[key] end end end end -- Ifid filter is mandatory, add it in case it's missing, only in live data if not where_query["ifid"] and not where_query["INTERFACE_ID"] then local ifid = available_filters["ifid"] or interface.getId() -- Use current ifid if not is_historical then where_query["ifid"] = ifid else where_query["INTERFACE_ID"] = ifid end end if data_type == "live" then where_query["detailsLevel"] = "normal" end return where_query end -- ########################################### -- @brief Return the requested formatted data if a -- formatting function is available -- @param key String, key of the formatter to retrieve -- @param value String, data to format -- @return the formatted data function flow_data_preset.getFormattedDataAndLink(key, value) local formatted_value = value local link = nil -- If key is empty or no formatter available return the value if isEmptyString(key) or isEmptyString(formatted_value) then return formatted_value, link end if (not columns[key]) or (not columns[key]["formatter"]) or (not columns[key]["formatter"]["funct"]) then return formatted_value, link end -- Get the formatter function local formatter = columns[key]["formatter"]["funct"] local link_params_formatter = columns[key]["formatter"]["generateLinkParams"] -- See if there is some column from which is dependent, -- e.g. SNMP Interface needs the SNMP IP if columns[key]["formatter"]["column_dependent"] then -- TODO: Now it's limited to a single column, add multiple columns local dependent_values = split(value, "|") if (#dependent_values == 1) then -- Not found return value return formatted_value, link end formatted_value = formatter(dependent_values[1], dependent_values[2]) if link_params_formatter then local link_params = link_params_formatter(dependent_values[1], dependent_values[2]) link = string.format(columns[key]["formatter"]["link"], link_params) end else formatted_value = formatter(formatted_value) if link_params_formatter then local link_params = link_params_formatter(value) link = string.format(columns[key]["formatter"]["link"], link_params) end end if not link and columns[key]["formatter"]["link"] then link = string.format(columns[key]["formatter"]["link"], value) end return formatted_value, link end -- ########################################### return flow_data_preset