mirror of
https://github.com/ntop/ntopng.git
synced 2026-05-06 12:15:15 +00:00
7428 lines
227 KiB
C++
7428 lines
227 KiB
C++
/*
|
|
*
|
|
* (C) 2013-19 - ntop.org
|
|
*
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
*/
|
|
|
|
#include "ntop_includes.h"
|
|
|
|
#ifdef __APPLE__
|
|
#include <uuid/uuid.h>
|
|
#endif
|
|
|
|
/* Lua.cpp */
|
|
extern int ntop_lua_cli_print(lua_State* vm);
|
|
extern int ntop_lua_check(lua_State* vm, const char* func, int pos, int expected_type);
|
|
|
|
static bool help_printed = false;
|
|
|
|
/* **************************************************** */
|
|
|
|
/* Method used for collateral activities */
|
|
NetworkInterface::NetworkInterface() {
|
|
init();
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
NetworkInterface::NetworkInterface(const char *name,
|
|
const char *custom_interface_type) {
|
|
NDPI_PROTOCOL_BITMASK all;
|
|
char _ifname[MAX_INTERFACE_NAME_LEN], buf[MAX_INTERFACE_NAME_LEN];
|
|
/* We need to do it as isView() is not yet initialized */
|
|
char pcap_error_buffer[PCAP_ERRBUF_SIZE];
|
|
|
|
init();
|
|
customIftype = custom_interface_type, flowHashingMode = flowhashing_none, tsExporter = NULL;
|
|
|
|
#ifdef WIN32
|
|
if(name == NULL) name = "1"; /* First available interface */
|
|
#endif
|
|
|
|
scalingFactor = 1;
|
|
if(strcmp(name, "-") == 0) name = "stdin";
|
|
if(strcmp(name, "-") == 0) name = "stdin";
|
|
|
|
if(ntop->getRedis())
|
|
id = Utils::ifname2id(name);
|
|
else
|
|
id = -1;
|
|
|
|
purge_idle_flows_hosts = true;
|
|
|
|
if(name == NULL) {
|
|
if(!help_printed)
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "No capture interface specified");
|
|
|
|
printAvailableInterfaces(false, 0, NULL, 0);
|
|
|
|
name = pcap_lookupdev(pcap_error_buffer);
|
|
|
|
if(name == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR,
|
|
"Unable to locate default interface (%s)\n",
|
|
pcap_error_buffer);
|
|
exit(0);
|
|
}
|
|
} else {
|
|
if(isNumber(name)) {
|
|
/* We need to convert this numeric index into an interface name */
|
|
int id = atoi(name);
|
|
|
|
_ifname[0] = '\0';
|
|
printAvailableInterfaces(false, id, _ifname, sizeof(_ifname));
|
|
|
|
if(_ifname[0] == '\0') {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Unable to locate interface Id %d", id);
|
|
printAvailableInterfaces(false, 0, NULL, 0);
|
|
exit(0);
|
|
}
|
|
|
|
name = _ifname;
|
|
}
|
|
}
|
|
|
|
db = NULL;
|
|
ifname = strdup(name);
|
|
if(custom_interface_type) {
|
|
ifDescription = strdup(name);
|
|
} else
|
|
ifDescription = strdup(Utils::getInterfaceDescription(ifname, buf, sizeof(buf)));
|
|
|
|
#ifdef NTOPNG_PRO
|
|
aggregated_flows_hash = NULL;
|
|
#endif
|
|
|
|
if(strchr(name, ':')
|
|
|| strchr(name, '@')
|
|
|| (!strcmp(name, "dummy"))
|
|
|| strchr(name, '/') /* file path */
|
|
|| strstr(name, ".pcap") /* pcap */
|
|
|| (strncmp(name, "lo", 2) == 0)
|
|
#if !defined(__APPLE__) && !defined(WIN32)
|
|
|| (Utils::readIPv4((char*)name) == 0)
|
|
#endif
|
|
)
|
|
; /* Don't setup MDNS on ZC or RSS interfaces */
|
|
else {
|
|
ipv4_network = ipv4_network_mask = 0;
|
|
if(pcap_lookupnet(ifname, &ipv4_network, &ipv4_network_mask, pcap_error_buffer) == -1) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Unable to read IPv4 address of %s: %s",
|
|
ifname, pcap_error_buffer);
|
|
} else {
|
|
try {
|
|
discovery = new NetworkDiscovery(this);
|
|
} catch (...) {
|
|
discovery = NULL;
|
|
}
|
|
|
|
if (discovery) {
|
|
try {
|
|
mdns = new MDNS(this);
|
|
}
|
|
catch (...) {
|
|
mdns = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(id >= 0) {
|
|
ndpi_port_range d_port[MAX_DEFAULT_PORTS];
|
|
u_int16_t no_master[2] = { NDPI_PROTOCOL_NO_MASTER_PROTO, NDPI_PROTOCOL_NO_MASTER_PROTO };
|
|
|
|
num_hashes = max_val(4096, ntop->getPrefs()->get_max_num_flows()/4);
|
|
flows_hash = new FlowHash(this, num_hashes, ntop->getPrefs()->get_max_num_flows());
|
|
|
|
num_hashes = max_val(4096, ntop->getPrefs()->get_max_num_hosts() / 4);
|
|
hosts_hash = new HostHash(this, num_hashes, ntop->getPrefs()->get_max_num_hosts());
|
|
/* The number of ASes cannot be greater than the number of hosts */
|
|
ases_hash = new AutonomousSystemHash(this, num_hashes,
|
|
ntop->getPrefs()->get_max_num_hosts());
|
|
|
|
countries_hash = new CountriesHash(this, num_hashes,
|
|
ntop->getPrefs()->get_max_num_hosts());
|
|
|
|
vlans_hash = new VlanHash(this, num_hashes,
|
|
max_val(ntop->getPrefs()->get_max_num_hosts() / 2,
|
|
(u_int16_t)-1));
|
|
|
|
macs_hash = new MacHash(this, num_hashes, ntop->getPrefs()->get_max_num_hosts());
|
|
|
|
if(ntop->getPrefs()->is_arp_matrix_generation_enabled())
|
|
arp_hash_matrix = new (std::nothrow) ArpStatsHashMatrix(this, num_hashes,
|
|
(ntop->getPrefs()->get_max_num_hosts() ^ 2) / 2);
|
|
else
|
|
arp_hash_matrix = NULL;
|
|
|
|
// init global detection structure
|
|
ndpi_struct = ndpi_init_detection_module();
|
|
if(ndpi_struct == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Unable to initialize nDPI");
|
|
exit(-1);
|
|
}
|
|
|
|
if(ntop->getCustomnDPIProtos() != NULL)
|
|
ndpi_load_protocols_file(ndpi_struct, ntop->getCustomnDPIProtos());
|
|
|
|
ndpi_set_detection_preferences(ndpi_struct, ndpi_pref_http_dont_dissect_response, 1);
|
|
ndpi_set_detection_preferences(ndpi_struct, ndpi_pref_dns_dont_dissect_response, 1);
|
|
ndpi_set_detection_preferences(ndpi_struct, ndpi_pref_enable_category_substring_match, 1);
|
|
|
|
memset(d_port, 0, sizeof(d_port));
|
|
ndpi_set_proto_defaults(ndpi_struct, NDPI_PROTOCOL_UNRATED, NTOPNG_NDPI_OS_PROTO_ID,
|
|
0, no_master, no_master, (char*)"Operating System",
|
|
NDPI_PROTOCOL_CATEGORY_SYSTEM_OS, d_port, d_port);
|
|
|
|
// enable all protocols
|
|
NDPI_BITMASK_SET_ALL(all);
|
|
ndpi_set_protocol_detection_bitmask2(ndpi_struct, &all);
|
|
|
|
last_pkt_rcvd = last_pkt_rcvd_remote = 0, pollLoopCreated = false,
|
|
bridge_interface = false;
|
|
next_idle_flow_purge = next_idle_host_purge = 0;
|
|
cpu_affinity = -1 /* no affinity */,
|
|
has_vlan_packets = has_ebpf_events = has_mac_addresses = false;
|
|
arp_requests = arp_replies = 0;
|
|
|
|
running = false,
|
|
inline_interface = false, db = NULL;
|
|
|
|
checkIdle();
|
|
ifSpeed = Utils::getMaxIfSpeed(name);
|
|
ifMTU = Utils::getIfMTU(name), mtuWarningShown = false;
|
|
} else /* id < 0 */ {
|
|
flows_hash = NULL, hosts_hash = NULL;
|
|
macs_hash = NULL, ases_hash = NULL, vlans_hash = NULL;
|
|
countries_hash = NULL, arp_hash_matrix = NULL;
|
|
ndpi_struct = NULL, db = NULL, ifSpeed = 0;
|
|
}
|
|
|
|
networkStats = NULL;
|
|
|
|
#ifdef NTOPNG_PRO
|
|
policer = NULL; /* possibly instantiated by subclass PacketBridge */
|
|
#ifndef HAVE_NEDGE
|
|
flow_profiles = ntop->getPro()->has_valid_license() ? new FlowProfiles(id) : NULL;
|
|
if(flow_profiles) flow_profiles->loadProfiles();
|
|
shadow_flow_profiles = NULL;
|
|
#endif
|
|
|
|
/* Lazy, instantiated on demand */
|
|
custom_app_stats = NULL;
|
|
flow_interfaces_stats = NULL;
|
|
#endif
|
|
|
|
loadScalingFactorPrefs();
|
|
loadPacketsDropsAlertPrefs();
|
|
reloadDhcpRanges();
|
|
|
|
statsManager = NULL, alertsManager = NULL;
|
|
|
|
host_pools = new HostPools(this);
|
|
bcast_domains = new BroadcastDomains(this);
|
|
|
|
#ifdef __linux__
|
|
/*
|
|
A bit aggressive but as people usually
|
|
ignore warnings let's be proactive
|
|
*/
|
|
if(ifname
|
|
&& (!isView())
|
|
&& (!strstr(ifname, ":"))
|
|
&& (!strstr(ifname, ".pcap"))
|
|
&& strcmp(ifname, "dummy")
|
|
&& strcmp(ifname, "any")
|
|
&& strcmp(ifname, "virbr")
|
|
&& strcmp(ifname, "wlan")
|
|
&& strncmp(ifname, "lo", 2)
|
|
) {
|
|
char buf[64], ifaces[128], *tmp, *iface;
|
|
|
|
snprintf(ifaces, sizeof(ifaces), "%s", ifname);
|
|
iface = strtok_r(ifaces, ",", &tmp);
|
|
|
|
while(iface != NULL) {
|
|
snprintf(buf, sizeof(buf), "ethtool -K %s gro off gso off tso off 2>/dev/null", iface);
|
|
system(buf);
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "Executing %s", buf);
|
|
iface = strtok_r(NULL, ",", &tmp);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
is_loopback = (strncmp(ifname, "lo", 2) == 0) ? true : false;
|
|
|
|
reloadHideFromTop(false);
|
|
updateTrafficMirrored();
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::init() {
|
|
ifname = NULL, flows_hash = NULL, hosts_hash = NULL,
|
|
bridge_lan_interface_id = bridge_wan_interface_id = 0, ndpi_struct = NULL,
|
|
inline_interface = false,
|
|
has_vlan_packets = false, has_ebpf_events = false,
|
|
last_pkt_rcvd = last_pkt_rcvd_remote = 0,
|
|
next_idle_flow_purge = next_idle_host_purge = 0,
|
|
running = false, customIftype = NULL, is_dynamic_interface = false,
|
|
is_loopback = is_traffic_mirrored = false;
|
|
numVirtualInterfaces = 0, flowHashing = NULL,
|
|
pcap_datalink_type = 0, mtuWarningShown = false,
|
|
purge_idle_flows_hosts = true, id = (u_int8_t)-1,
|
|
last_remote_pps = 0, last_remote_bps = 0,
|
|
has_vlan_packets = false,
|
|
cpu_affinity = -1 /* no affinity */,
|
|
inline_interface = false, running = false, interfaceStats = NULL,
|
|
has_too_many_hosts = has_too_many_flows = too_many_drops = false,
|
|
slow_stats_update = false,
|
|
numL2Devices = 0, numHosts = 0, numLocalHosts = 0,
|
|
checkpointPktCount = checkpointBytesCount = checkpointPktDropCount = 0,
|
|
pollLoopCreated = false, bridge_interface = false,
|
|
mdns = NULL, discovery = NULL, ifDescription = NULL,
|
|
flowHashingMode = flowhashing_none;
|
|
macs_hash = NULL, ases_hash = NULL, countries_hash = NULL, vlans_hash = NULL,
|
|
arp_hash_matrix = NULL;
|
|
|
|
numSubInterfaces = 0;
|
|
memset(subInterfaces, 0, sizeof(subInterfaces));
|
|
reload_custom_categories = reload_hosts_blacklist = false;
|
|
reload_hosts_bcast_domain = false;
|
|
hosts_bcast_domain_last_update = 0;
|
|
|
|
ip_addresses = "", networkStats = NULL,
|
|
pcap_datalink_type = 0, cpu_affinity = -1;
|
|
hide_from_top = hide_from_top_shadow = NULL;
|
|
|
|
gettimeofday(&last_frequent_reset, NULL);
|
|
frequentMacs = new FrequentTrafficItems(5);
|
|
frequentProtocols = new FrequentTrafficItems(5);
|
|
num_live_captures = 0;
|
|
memset(live_captures, 0, sizeof(live_captures));
|
|
|
|
db = NULL;
|
|
#ifdef NTOPNG_PRO
|
|
custom_app_stats = NULL;
|
|
aggregated_flows_hash = NULL, flow_interfaces_stats = NULL;
|
|
policer = NULL;
|
|
#endif
|
|
statsManager = NULL, alertsManager = NULL, ifSpeed = 0;
|
|
host_pools = NULL;
|
|
bcast_domains = NULL;
|
|
checkIdle();
|
|
ifMTU = CONST_DEFAULT_MAX_PACKET_SIZE, mtuWarningShown = false;
|
|
#ifdef NTOPNG_PRO
|
|
#ifndef HAVE_NEDGE
|
|
flow_profiles = shadow_flow_profiles = NULL;
|
|
#endif
|
|
|
|
#endif
|
|
|
|
dhcp_ranges = dhcp_ranges_shadow = NULL;
|
|
|
|
ts_ring = NULL;
|
|
|
|
if(ntop->getPrefs()) {
|
|
if(TimeseriesRing::isRingEnabled(ntop->getPrefs()))
|
|
ts_ring = new TimeseriesRing(this);
|
|
}
|
|
|
|
#ifdef HAVE_EBPF
|
|
if(bridge_interface
|
|
|| is_dynamic_interface
|
|
|| is_traffic_mirrored
|
|
|| isView())
|
|
;
|
|
else {
|
|
ebpfEvents = (eBPFevent**)calloc(sizeof(eBPFevent*), EBPF_QUEUE_LEN);
|
|
next_insert_idx = next_remove_idx = 0;
|
|
}
|
|
#endif
|
|
|
|
PROFILING_INIT();
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
#ifdef NTOPNG_PRO
|
|
|
|
void NetworkInterface::initL7Policer() {
|
|
/* Instantiate the policer */
|
|
policer = new L7Policer(this);
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
void NetworkInterface::aggregatePartialFlow(Flow *flow) {
|
|
if(flow && aggregated_flows_hash) {
|
|
AggregatedFlow *aggregatedFlow = aggregated_flows_hash->find(flow);
|
|
|
|
if(aggregatedFlow == NULL) {
|
|
if(!aggregated_flows_hash->hasEmptyRoom()) {
|
|
/* There is no more room in the hash table */
|
|
} else if((!ntop->getPrefs()->is_aggregated_flows_export_limit_enabled())
|
|
|| (aggregated_flows_hash->getNumEntries() < ntop->getPrefs()->get_max_num_aggregated_flows_per_export())) {
|
|
#ifdef AGGREGATED_FLOW_DEBUG
|
|
char buf[256];
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "AggregatedFlow not found [%s]. Creating it.",
|
|
flow->print(buf, sizeof(buf)));
|
|
#endif
|
|
|
|
try {
|
|
aggregatedFlow = new AggregatedFlow(this, flow);
|
|
|
|
if(aggregated_flows_hash->add(aggregatedFlow) == false) {
|
|
/* Too many flows, should never happen */
|
|
delete aggregatedFlow;
|
|
return;
|
|
} else {
|
|
#ifdef AGGREGATED_FLOW_DEBUG
|
|
char buf[256];
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"New AggregatedFlow successfully created and added "
|
|
"to the hash table [%s]",
|
|
aggregatedFlow->print(buf, sizeof(buf)));
|
|
#endif
|
|
}
|
|
} catch(std::bad_alloc& ba) {
|
|
return; /* Not enough memory */
|
|
}
|
|
} else {
|
|
/* The maximum number of aggregates has been reached. Add here the logic to handle
|
|
this case. For example, make the maximum number adaptive depending on the number of hosts,
|
|
or keep only the top-X aggregates. */
|
|
|
|
#ifdef AGGREGATED_FLOW_DEBUG
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Maximum reached [maximum: %d]", ntop->getPrefs()->get_max_num_aggregated_flows_per_export());
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if(aggregatedFlow) {
|
|
aggregatedFlow->sumFlowStats(flow,
|
|
/* nextFlowAggregation will be decremented by one after the current periodic
|
|
flows walk (this method is called in the periodic flows walk)
|
|
|
|
Therefore, we can check nextFlowAggregation minus one to determine whether
|
|
a cleanup of the aggregated flows hash table is going to be performed
|
|
after this walk on the (normal, non-aggregated) flows table.
|
|
*/
|
|
((getIfType() == interface_type_DUMMY) || (nextFlowAggregation - 1 == 0)));
|
|
|
|
#ifdef AGGREGATED_FLOW_DEBUG
|
|
char buf[256];
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Stats updated for AggregatedFlow [%s]",
|
|
aggregatedFlow->print(buf, sizeof(buf)));
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Aggregated Flows hash table [num items: %i]",
|
|
aggregated_flows_hash->getCurrentSize());
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::checkAggregationMode() {
|
|
if(!customIftype) {
|
|
char rsp[32];
|
|
|
|
if((!ntop->getRedis()->get((char*)CONST_RUNTIME_PREFS_IFACE_FLOW_COLLECTION, rsp, sizeof(rsp)))
|
|
&& (rsp[0] != '\0')) {
|
|
if(getIfType() == interface_type_ZMQ) { /* ZMQ interface */
|
|
if(!strcmp(rsp, DISAGGREGATION_PROBE_IP)) flowHashingMode = flowhashing_probe_ip;
|
|
else if(!strcmp(rsp, DISAGGREGATION_IFACE_ID)) flowHashingMode = flowhashing_iface_idx;
|
|
else if(!strcmp(rsp, DISAGGREGATION_INGRESS_IFACE_ID)) flowHashingMode = flowhashing_ingress_iface_idx;
|
|
else if(!strcmp(rsp, DISAGGREGATION_INGRESS_VRF_ID)) flowHashingMode = flowhashing_vrfid;
|
|
else if(!strcmp(rsp, DISAGGREGATION_VLAN)) flowHashingMode = flowhashing_vlan;
|
|
else if(strcmp(rsp, DISAGGREGATION_NONE))
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR,
|
|
"Unknown aggregation value for interface %s [rsp: %s]",
|
|
get_type(), rsp);
|
|
} else { /* non-ZMQ interface */
|
|
if(!strcmp(rsp, DISAGGREGATION_VLAN)) flowHashingMode = flowhashing_vlan;
|
|
else if(strcmp(rsp, DISAGGREGATION_NONE))
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR,
|
|
"Unknown aggregation value for interface %s [rsp: %s]",
|
|
get_type(), rsp);
|
|
|
|
}
|
|
}
|
|
|
|
/* Populate ignored interfaces */
|
|
rsp[0] = '\0';
|
|
if((!ntop->getRedis()->get((char*)CONST_RUNTIME_PREFS_IGNORED_INTERFACES, rsp, sizeof(rsp)))
|
|
&& (rsp[0] != '\0')) {
|
|
char *token;
|
|
char *rest = rsp;
|
|
|
|
while((token = strtok_r(rest, ",", &rest)))
|
|
flowHashingIgnoredInterfaces.insert(atoi(token));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::loadScalingFactorPrefs() {
|
|
if(ntop->getRedis() != NULL) {
|
|
char rkey[128], rsp[16];
|
|
|
|
snprintf(rkey, sizeof(rkey), CONST_IFACE_SCALING_FACTOR_PREFS, id);
|
|
|
|
if((ntop->getRedis()->get(rkey, rsp, sizeof(rsp)) == 0) && (rsp[0] != '\0'))
|
|
scalingFactor = atol(rsp);
|
|
|
|
if(scalingFactor == 0) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "INTERNAL ERROR: scalingFactor can't be 0!");
|
|
scalingFactor = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::loadPacketsDropsAlertPrefs() {
|
|
packet_drops_alert_perc = CONST_DEFAULT_PACKETS_DROP_PERCENTAGE_ALERT;
|
|
|
|
if(ntop->getRedis() != NULL) {
|
|
char rkey[128], rsp[8];
|
|
|
|
snprintf(rkey, sizeof(rkey), CONST_IFACE_PACKET_DROPS_ALERT_PREFS, id);
|
|
|
|
if((ntop->getRedis()->get(rkey, rsp, sizeof(rsp)) == 0) && (rsp[0] != '\0'))
|
|
packet_drops_alert_perc = atoi(rsp);
|
|
}
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::updateTrafficMirrored() {
|
|
char key[CONST_MAX_LEN_REDIS_KEY], rsp[2] = { 0 };
|
|
bool is_mirrored = CONST_DEFAULT_MIRRORED_TRAFFIC;
|
|
|
|
if(!ntop->getRedis()) return;
|
|
|
|
snprintf(key, sizeof(key), CONST_MIRRORED_TRAFFIC_PREFS, get_id());
|
|
if((ntop->getRedis()->get(key, rsp, sizeof(rsp)) == 0) && (rsp[0] != '\0')) {
|
|
if(rsp[0] == '1')
|
|
is_mirrored = true;
|
|
else if(rsp[0] == '0')
|
|
is_mirrored = false;
|
|
}
|
|
|
|
// ntop->getTrace()->traceEvent(TRACE_NORMAL, "Updating mirrored traffic [ifid: %i][rsp: %s][actual_value: %d]", get_id(), rsp, is_mirrored ? 1 : 0);
|
|
|
|
is_traffic_mirrored = is_mirrored;
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
bool NetworkInterface::checkIdle() {
|
|
is_idle = false;
|
|
|
|
if(ifname != NULL) {
|
|
char rkey[128], rsp[16];
|
|
|
|
snprintf(rkey, sizeof(rkey), "ntopng.prefs.%s_not_idle", ifname);
|
|
if((ntop->getRedis()->get(rkey, rsp, sizeof(rsp)) == 0) && (rsp[0] != '\0')) {
|
|
int val = atoi(rsp);
|
|
|
|
if(val == 0) is_idle = true;
|
|
}
|
|
}
|
|
|
|
return(is_idle);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::deleteDataStructures() {
|
|
if(flows_hash) { delete(flows_hash); flows_hash = NULL; }
|
|
if(hosts_hash) { delete(hosts_hash); hosts_hash = NULL; }
|
|
if(ases_hash) { delete(ases_hash); ases_hash = NULL; }
|
|
if(countries_hash) { delete(countries_hash); countries_hash = NULL; }
|
|
if(vlans_hash) { delete(vlans_hash); vlans_hash = NULL; }
|
|
if(macs_hash) { delete(macs_hash); macs_hash = NULL; }
|
|
if(arp_hash_matrix) { delete(arp_hash_matrix); arp_hash_matrix = NULL; }
|
|
|
|
#ifdef NTOPNG_PRO
|
|
if(aggregated_flows_hash) {
|
|
aggregated_flows_hash->cleanup();
|
|
delete(aggregated_flows_hash);
|
|
aggregated_flows_hash = NULL;
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_EBPF
|
|
if(ebpfEvents) {
|
|
for(u_int16_t i=0; i<EBPF_QUEUE_LEN; i++)
|
|
if(ebpfEvents[i])
|
|
free(ebpfEvents[i]);
|
|
|
|
free(ebpfEvents);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
NetworkInterface::~NetworkInterface() {
|
|
#ifdef PROFILING
|
|
u_int64_t n = ethStats.getNumIngressPackets();
|
|
if (n > 0) {
|
|
for (u_int i = 0; i < PROFILING_NUM_SECTIONS; i++) {
|
|
if (PROFILING_SECTION_LABEL(i) != NULL)
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "[PROFILING] Section #%d '%s': AVG %llu ticks",
|
|
i, PROFILING_SECTION_LABEL(i), PROFILING_SECTION_AVG(i, n));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if(getNumPackets() > 0) {
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Flushing host contacts for interface %s",
|
|
get_name());
|
|
cleanup();
|
|
}
|
|
|
|
deleteDataStructures();
|
|
|
|
if(db) {
|
|
/* note: keep this after deleteDataStructures to flush aggregated flows */
|
|
db->shutdown();
|
|
delete db;
|
|
}
|
|
if(host_pools) delete host_pools; /* note: this requires ndpi_struct */
|
|
if(bcast_domains) delete bcast_domains;
|
|
if(ifDescription) free(ifDescription);
|
|
if(discovery) delete discovery;
|
|
if(statsManager) delete statsManager;
|
|
if(alertsManager) delete alertsManager;
|
|
if(networkStats) delete []networkStats;
|
|
if(interfaceStats) delete interfaceStats;
|
|
|
|
if(flowHashing) {
|
|
FlowHashing *current, *tmp;
|
|
|
|
HASH_ITER(hh, flowHashing, current, tmp) {
|
|
/* Interfaces are deleted by the main termination function */
|
|
HASH_DEL(flowHashing, current);
|
|
free(current);
|
|
}
|
|
|
|
flowHashing = NULL;
|
|
}
|
|
|
|
if(ndpi_struct) {
|
|
ndpi_exit_detection_module(ndpi_struct);
|
|
ndpi_struct = NULL;
|
|
}
|
|
|
|
delete frequentProtocols;
|
|
delete frequentMacs;
|
|
|
|
#ifdef NTOPNG_PRO
|
|
if(policer) delete(policer);
|
|
#ifndef HAVE_NEDGE
|
|
if(flow_profiles) delete(flow_profiles);
|
|
if(shadow_flow_profiles) delete(shadow_flow_profiles);
|
|
#endif
|
|
if(custom_app_stats) delete custom_app_stats;
|
|
if(flow_interfaces_stats) delete flow_interfaces_stats;
|
|
#endif
|
|
if(hide_from_top) delete(hide_from_top);
|
|
if(hide_from_top_shadow) delete(hide_from_top_shadow);
|
|
if(tsExporter) delete tsExporter;
|
|
if(ts_ring) delete ts_ring;
|
|
if(mdns) delete mdns; /* Leave it at the end so the mdns resolved has time to initialize */
|
|
if(dhcp_ranges) delete[] dhcp_ranges;
|
|
if(dhcp_ranges_shadow) delete[] dhcp_ranges_shadow;
|
|
|
|
if(ifname) free(ifname);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
int NetworkInterface::dumpFlow(time_t when, Flow *f) {
|
|
int rc = -1;
|
|
|
|
#ifndef HAVE_NEDGE
|
|
char *json;
|
|
bool es_flow = ntop->getPrefs()->do_dump_flows_on_es() ||
|
|
ntop->getPrefs()->do_dump_flows_on_ls();
|
|
|
|
if(!db)
|
|
return(-1);
|
|
|
|
json = f->serialize(es_flow);
|
|
|
|
if(json) {
|
|
rc = db->dumpFlow(when, f, json);
|
|
free(json);
|
|
} else
|
|
rc = -1;
|
|
#endif
|
|
|
|
return(rc);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
#ifdef NTOPNG_PRO
|
|
|
|
void NetworkInterface::dumpAggregatedFlow(time_t when, AggregatedFlow *f, bool is_top_aggregated_flow, bool is_top_cli, bool is_top_srv) {
|
|
if(db
|
|
&& f && (f->get_packets() > 0)
|
|
&& ntop->getPrefs()->is_enterprise_edition()) {
|
|
#ifdef DUMP_AGGREGATED_FLOW_DEBUG
|
|
char buf[256];
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Going to dump AggregatedFlow to database [%s][is_top: %u]",
|
|
f->print(buf, sizeof(buf)), is_top_aggregated_flow ? 1 : 0);
|
|
#endif
|
|
|
|
if(!ntop->getPrefs()->is_tiny_flows_export_enabled() && f->isTiny()) {
|
|
#ifdef DUMP_AGGREGATED_FLOW_DEBUG
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Skipping tiny aggregated flow [flow: %s]",
|
|
f->print(buf, sizeof(buf)));
|
|
#endif
|
|
} else {
|
|
db->dumpAggregatedFlow(when, f, is_top_aggregated_flow, is_top_cli, is_top_srv);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::flushFlowDump() {
|
|
if(db) db->flush();
|
|
}
|
|
|
|
#endif
|
|
|
|
/* **************************************************** */
|
|
|
|
static bool local_hosts_2_redis_walker(GenericHashEntry *h, void *user_data, bool *matched) {
|
|
Host *host = (Host*)h;
|
|
|
|
if(host && (host->isLocalHost() || host->isSystemHost())) {
|
|
host->serialize2redis();
|
|
*matched = true;
|
|
}
|
|
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
int NetworkInterface::dumpLocalHosts2redis(bool disable_purge) {
|
|
int rc;
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
if(disable_purge) disablePurge(false /* on hosts */);
|
|
rc = walker(&begin_slot, walk_all, walker_hosts,
|
|
local_hosts_2_redis_walker, NULL) ? 0 : -1;
|
|
if(disable_purge) enablePurge(false /* on hosts */);
|
|
|
|
#ifdef NTOPNG_PRO
|
|
if(getHostPools()) getHostPools()->dumpToRedis();
|
|
#endif
|
|
|
|
return(rc);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
u_int32_t NetworkInterface::getHostsHashSize() {
|
|
return(hosts_hash->getNumEntries());
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
u_int32_t NetworkInterface::getASesHashSize() {
|
|
return(ases_hash->getNumEntries());
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
u_int32_t NetworkInterface::getCountriesHashSize() {
|
|
return(countries_hash->getNumEntries());
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
u_int32_t NetworkInterface::getVLANsHashSize() {
|
|
return(vlans_hash->getNumEntries());
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
u_int32_t NetworkInterface::getFlowsHashSize() {
|
|
return(flows_hash->getNumEntries());
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
u_int32_t NetworkInterface::getMacsHashSize() {
|
|
return(macs_hash->getNumEntries());
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
u_int32_t NetworkInterface::getArpHashMatrixSize() {
|
|
return(arp_hash_matrix ? arp_hash_matrix->getNumEntries() : 0);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
bool NetworkInterface::walker(u_int32_t *begin_slot,
|
|
bool walk_all,
|
|
WalkerType wtype,
|
|
bool (*walker)(GenericHashEntry *h, void *user_data, bool *matched),
|
|
void *user_data) {
|
|
bool ret = false;
|
|
|
|
switch(wtype) {
|
|
case walker_hosts:
|
|
ret = hosts_hash->walk(begin_slot, walk_all, walker, user_data);
|
|
break;
|
|
|
|
case walker_flows:
|
|
ret = flows_hash->walk(begin_slot, walk_all, walker, user_data);
|
|
break;
|
|
|
|
case walker_macs:
|
|
ret = macs_hash->walk(begin_slot, walk_all, walker, user_data);
|
|
break;
|
|
|
|
case walker_ases:
|
|
ret = ases_hash->walk(begin_slot, walk_all, walker, user_data);
|
|
break;
|
|
|
|
case walker_countries:
|
|
ret = countries_hash->walk(begin_slot, walk_all, walker, user_data);
|
|
break;
|
|
|
|
case walker_vlans:
|
|
ret = vlans_hash->walk(begin_slot, walk_all, walker, user_data);
|
|
break;
|
|
}
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
Flow* NetworkInterface::getFlow(Mac *srcMac, Mac *dstMac,
|
|
u_int16_t vlan_id, u_int32_t deviceIP,
|
|
u_int16_t inIndex, u_int16_t outIndex,
|
|
const ICMPinfo * const icmp_info,
|
|
IpAddress *src_ip, IpAddress *dst_ip,
|
|
u_int16_t src_port, u_int16_t dst_port,
|
|
u_int8_t l4_proto,
|
|
bool *src2dst_direction,
|
|
time_t first_seen, time_t last_seen,
|
|
u_int32_t rawsize,
|
|
bool *new_flow, bool create_if_missing) {
|
|
Flow *ret;
|
|
Mac *primary_mac;
|
|
Host *srcHost = NULL, *dstHost = NULL;
|
|
|
|
if(vlan_id != 0)
|
|
setSeenVlanTaggedPackets();
|
|
|
|
if((srcMac && Utils::macHash(srcMac->get_mac()) != 0)
|
|
|| (dstMac && Utils::macHash(dstMac->get_mac()) != 0))
|
|
setSeenMacAddresses();
|
|
|
|
PROFILING_SECTION_ENTER("NetworkInterface::getFlow: flows_hash->find", 5);
|
|
ret = flows_hash->find(src_ip, dst_ip, src_port, dst_port,
|
|
vlan_id, l4_proto, icmp_info, src2dst_direction);
|
|
PROFILING_SECTION_EXIT(5);
|
|
|
|
if(ret == NULL) {
|
|
if(!create_if_missing)
|
|
return(NULL);
|
|
|
|
*new_flow = true;
|
|
|
|
try {
|
|
PROFILING_SECTION_ENTER("NetworkInterface::getFlow: new Flow", 6);
|
|
ret = new Flow(this, vlan_id, l4_proto,
|
|
srcMac, src_ip, src_port,
|
|
dstMac, dst_ip, dst_port,
|
|
icmp_info,
|
|
first_seen, last_seen);
|
|
PROFILING_SECTION_EXIT(6);
|
|
} catch(std::bad_alloc& ba) {
|
|
static bool oom_warning_sent = false;
|
|
|
|
if(!oom_warning_sent) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Not enough memory");
|
|
oom_warning_sent = true;
|
|
}
|
|
|
|
has_too_many_flows = true;
|
|
return(NULL);
|
|
}
|
|
|
|
if(flows_hash->add(ret)) {
|
|
*src2dst_direction = true;
|
|
} else {
|
|
delete ret;
|
|
// ntop->getTrace()->traceEvent(TRACE_WARNING, "Too many flows");
|
|
has_too_many_flows = true;
|
|
return(NULL);
|
|
}
|
|
} else {
|
|
*new_flow = false;
|
|
has_too_many_flows = false;
|
|
}
|
|
|
|
if(srcMac) {
|
|
if((srcHost = (*src2dst_direction) ? ret->get_cli_host() : ret->get_srv_host())) {
|
|
if((!srcMac->isSpecialMac()) && (primary_mac = srcHost->getMac()) && primary_mac != srcMac) {
|
|
#ifdef MAC_DEBUG
|
|
char buf[32], bufm1[32], bufm2[32];
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Detected mac address [%s] [host: %s][primary mac: %s]",
|
|
Utils::formatMac(srcMac->get_mac(), bufm1, sizeof(bufm1)),
|
|
srcHost->get_ip()->print(buf, sizeof(buf)),
|
|
Utils::formatMac(primary_mac->get_mac(), bufm2, sizeof(bufm2)));
|
|
#endif
|
|
|
|
if(srcHost->getMac()->isSpecialMac()) {
|
|
if(getIfType() == interface_type_NETFILTER) {
|
|
/*
|
|
This is the first *reply* packet of a flow so we need to increment it
|
|
with the initial packet that was missed as NetFilter did not report
|
|
the (destination) MAC. From now on, all flow peers are known
|
|
*/
|
|
|
|
/* NOTE: in nEdge, stats are updated into Flow::update_hosts_stats */
|
|
#ifndef HAVE_NEDGE
|
|
if(ret->get_packets_cli2srv() == 1 /* first packet */)
|
|
srcMac->incRcvdStats(getTimeLastPktRcvd(), 1, ret->get_bytes_cli2srv() /* size of the last packet */);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
srcHost->set_mac(srcMac);
|
|
srcHost->updateHostPool(true /* Inline */);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(dstMac) {
|
|
if((dstHost = (*src2dst_direction) ? ret->get_srv_host() : ret->get_cli_host())) {
|
|
if((!dstMac->isSpecialMac()) && (primary_mac = dstHost->getMac()) && primary_mac != dstMac) {
|
|
#ifdef MAC_DEBUG
|
|
char buf[32], bufm1[32], bufm2[32];
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Detected mac address [%s] [host: %s][primary mac: %s]",
|
|
Utils::formatMac(dstMac->get_mac(), bufm1, sizeof(bufm1)),
|
|
dstHost->get_ip()->print(buf, sizeof(buf)),
|
|
Utils::formatMac(primary_mac->get_mac(), bufm2, sizeof(bufm2)));
|
|
#endif
|
|
dstHost->set_mac(dstMac);
|
|
dstHost->updateHostPool(true /* Inline */);
|
|
}
|
|
}
|
|
}
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
NetworkInterface* NetworkInterface::getSubInterface(u_int32_t criteria, bool parser_interface) {
|
|
#ifndef HAVE_NEDGE
|
|
FlowHashing *h = NULL;
|
|
|
|
HASH_FIND_INT(flowHashing, &criteria, h);
|
|
|
|
if(h == NULL) {
|
|
/* Interface not found */
|
|
|
|
if(numVirtualInterfaces < MAX_NUM_VIRTUAL_INTERFACES) {
|
|
if((h = (FlowHashing*)malloc(sizeof(FlowHashing))) != NULL) {
|
|
char buf[64], buf1[48];
|
|
const char *vIface_type;
|
|
|
|
h->criteria = criteria;
|
|
|
|
switch(flowHashingMode) {
|
|
case flowhashing_vlan:
|
|
vIface_type = CONST_INTERFACE_TYPE_VLAN;
|
|
snprintf(buf, sizeof(buf), "%s [VLAN Id: %u]", ifname, criteria);
|
|
// snprintf(buf, sizeof(buf), "VLAN Id %u", criteria);
|
|
break;
|
|
|
|
case flowhashing_probe_ip:
|
|
vIface_type = CONST_INTERFACE_TYPE_FLOW;
|
|
snprintf(buf, sizeof(buf), "%s [Probe IP: %s]", ifname, Utils::intoaV4(criteria, buf1, sizeof(buf1)));
|
|
// snprintf(buf, sizeof(buf), "Probe IP %s", Utils::intoaV4(criteria, buf1, sizeof(buf1)));
|
|
break;
|
|
|
|
case flowhashing_iface_idx:
|
|
case flowhashing_ingress_iface_idx:
|
|
vIface_type = CONST_INTERFACE_TYPE_FLOW;
|
|
snprintf(buf, sizeof(buf), "%s [If Idx: %u]", ifname, criteria);
|
|
// snprintf(buf, sizeof(buf), "If Idx %u", criteria);
|
|
break;
|
|
|
|
case flowhashing_vrfid:
|
|
vIface_type = CONST_INTERFACE_TYPE_FLOW;
|
|
snprintf(buf, sizeof(buf), "%s [VRF Id: %u]", ifname, criteria);
|
|
// snprintf(buf, sizeof(buf), "VRF Id %u", criteria);
|
|
break;
|
|
|
|
default:
|
|
free(h);
|
|
return(NULL);
|
|
break;
|
|
}
|
|
|
|
if(parser_interface)
|
|
h->iface = new ParserInterface(buf, vIface_type);
|
|
else
|
|
h->iface = new NetworkInterface(buf, vIface_type);
|
|
|
|
if(h->iface) {
|
|
if (ntop->registerInterface(h->iface))
|
|
ntop->initInterface(h->iface);
|
|
h->iface->allocateNetworkStats();
|
|
h->iface->setDynamicInterface();
|
|
HASH_ADD_INT(flowHashing, criteria, h);
|
|
numVirtualInterfaces++;
|
|
ntop->getRedis()->set(CONST_STR_RELOAD_LISTS, (const char * const)"1");
|
|
}
|
|
} else
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Not enough memory");
|
|
}
|
|
}
|
|
|
|
if(h) return(h->iface);
|
|
#endif
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::processFlow(ZMQ_Flow *zflow) {
|
|
bool src2dst_direction, new_flow;
|
|
Flow *flow;
|
|
ndpi_protocol p;
|
|
time_t now = time(NULL);
|
|
Mac *srcMac = NULL, *dstMac = NULL;
|
|
IpAddress srcIP, dstIP;
|
|
|
|
memset(&p, 0, sizeof(p));
|
|
|
|
if(last_pkt_rcvd_remote > 0) {
|
|
int drift = now - last_pkt_rcvd_remote;
|
|
|
|
if(drift >= 0)
|
|
zflow->core.last_switched += drift, zflow->core.first_switched += drift;
|
|
else {
|
|
u_int32_t d = (u_int32_t)-drift;
|
|
|
|
if(d < zflow->core.last_switched) zflow->core.last_switched += drift;
|
|
if(d < zflow->core.first_switched) zflow->core.first_switched += drift;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"[first=%u][last=%u][duration: %u][drift: %d][now: %u][remote: %u]",
|
|
zflow->core.first_switched, zflow->core.last_switched,
|
|
zflow->core.last_switched-zflow->core.first_switched, drift,
|
|
now, last_pkt_rcvd_remote);
|
|
#endif
|
|
} else {
|
|
/* Old nProbe */
|
|
|
|
if((time_t)zflow->core.last_switched > (time_t)last_pkt_rcvd_remote)
|
|
last_pkt_rcvd_remote = zflow->core.last_switched;
|
|
|
|
#ifdef DEBUG
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "[first=%u][last=%u][duration: %u]",
|
|
zflow->core.first_switched, zflow->core.last_switched,
|
|
zflow->core.last_switched- zflow->core.first_switched);
|
|
#endif
|
|
}
|
|
|
|
if((!isDynamicInterface()) && (flowHashingMode != flowhashing_none)) {
|
|
NetworkInterface *vIface = NULL, *vIfaceEgress = NULL;
|
|
|
|
switch(flowHashingMode) {
|
|
case flowhashing_probe_ip:
|
|
vIface = getSubInterface((u_int32_t)zflow->core.deviceIP, true);
|
|
break;
|
|
|
|
case flowhashing_iface_idx:
|
|
if(flowHashingIgnoredInterfaces.find((u_int32_t)zflow->core.outIndex) == flowHashingIgnoredInterfaces.end())
|
|
vIfaceEgress = getSubInterface((u_int32_t)zflow->core.outIndex, true);
|
|
/* No break HERE, want to get two interfaces, one for the ingress
|
|
and one for the egress. */
|
|
|
|
case flowhashing_ingress_iface_idx:
|
|
if(flowHashingIgnoredInterfaces.find((u_int32_t)zflow->core.inIndex) == flowHashingIgnoredInterfaces.end())
|
|
vIface = getSubInterface((u_int32_t)zflow->core.inIndex, true);
|
|
break;
|
|
|
|
case flowhashing_vrfid:
|
|
vIface = getSubInterface((u_int32_t)zflow->core.vrfId, true);
|
|
break;
|
|
|
|
case flowhashing_vlan:
|
|
vIface = getSubInterface((u_int32_t)zflow->core.vlan_id, true);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if(vIface) vIface->processFlow(zflow);
|
|
if(vIfaceEgress) vIfaceEgress->processFlow(zflow);
|
|
|
|
return;
|
|
}
|
|
|
|
if(!ntop->getPrefs()->do_ignore_macs()) {
|
|
srcMac = getMac((u_int8_t*)zflow->core.src_mac, true);
|
|
dstMac = getMac((u_int8_t*)zflow->core.dst_mac, true);
|
|
}
|
|
|
|
srcIP.set(&zflow->core.src_ip), dstIP.set(&zflow->core.dst_ip);
|
|
|
|
/* Updating Flow */
|
|
flow = getFlow(srcMac, dstMac,
|
|
zflow->core.vlan_id,
|
|
zflow->core.deviceIP,
|
|
zflow->core.inIndex, zflow->core.outIndex,
|
|
NULL /* ICMPinfo */,
|
|
&srcIP, &dstIP,
|
|
zflow->core.src_port, zflow->core.dst_port,
|
|
zflow->core.l4_proto, &src2dst_direction,
|
|
zflow->core.first_switched,
|
|
zflow->core.last_switched,
|
|
0, &new_flow, true);
|
|
|
|
if(flow == NULL)
|
|
return;
|
|
|
|
if(zflow->core.absolute_packet_octet_counters) {
|
|
/* Ajdust bytes and packets counters if the zflow update contains absolute values.
|
|
|
|
As flows may arrive out of sequence, special care is needed to avoid counting absolute values multiple times.
|
|
|
|
http://www.cisco.com/c/en/us/td/docs/security/asa/special/netflow/guide/asa_netflow.html#pgfId-1331296
|
|
Different events in the life of a flow may be issued in separate NetFlow packets and may arrive out-of-order
|
|
at the collector. For example, the packet containing a flow teardown event may reach the collector before the packet
|
|
containing a flow creation event. As a result, it is important that collector applications use the Event Time field
|
|
to correlate events.
|
|
*/
|
|
|
|
u_int64_t in_cur_pkts = src2dst_direction ? flow->get_packets_cli2srv() : flow->get_packets_srv2cli(),
|
|
in_cur_bytes = src2dst_direction ? flow->get_bytes_cli2srv() : flow->get_bytes_srv2cli();
|
|
u_int64_t out_cur_pkts = src2dst_direction ? flow->get_packets_srv2cli() : flow->get_packets_cli2srv(),
|
|
out_cur_bytes = src2dst_direction ? flow->get_bytes_srv2cli() : flow->get_bytes_cli2srv();
|
|
bool out_of_sequence = false;
|
|
|
|
if(zflow->core.in_pkts) {
|
|
if(zflow->core.in_pkts >= in_cur_pkts) zflow->core.in_pkts -= in_cur_pkts;
|
|
else zflow->core.in_pkts = 0, out_of_sequence = true;
|
|
}
|
|
|
|
if(zflow->core.in_bytes) {
|
|
if(zflow->core.in_bytes >= in_cur_bytes) zflow->core.in_bytes -= in_cur_bytes;
|
|
else zflow->core.in_bytes = 0, out_of_sequence = true;
|
|
}
|
|
|
|
if(zflow->core.out_pkts) {
|
|
if(zflow->core.out_pkts >= out_cur_pkts) zflow->core.out_pkts -= out_cur_pkts;
|
|
else zflow->core.out_pkts = 0, out_of_sequence = true;
|
|
}
|
|
|
|
if(zflow->core.out_bytes) {
|
|
if(zflow->core.out_bytes >= out_cur_bytes) zflow->core.out_bytes -= out_cur_bytes;
|
|
else zflow->core.out_bytes = 0, out_of_sequence = true;
|
|
}
|
|
|
|
if(out_of_sequence) {
|
|
#ifdef ABSOLUTE_COUNTERS_DEBUG
|
|
char flowbuf[265];
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING,
|
|
"A flow received an update with absolute values smaller than the current values. "
|
|
"[in_bytes: %u][in_cur_bytes: %u][out_bytes: %u][out_cur_bytes: %u]"
|
|
"[in_pkts: %u][in_cur_pkts: %u][out_pkts: %u][out_cur_pkts: %u]\n"
|
|
"%s",
|
|
zflow->core.in_bytes, in_cur_bytes, zflow->core.out_bytes, out_cur_bytes,
|
|
zflow->core.in_pkts, in_cur_pkts, zflow->core.out_pkts, out_cur_pkts,
|
|
flow->print(flowbuf, sizeof(flowbuf)));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if(zflow->core.tcp.clientNwLatency.tv_sec || zflow->core.tcp.clientNwLatency.tv_usec)
|
|
flow->setFlowNwLatency(&zflow->core.tcp.clientNwLatency, src2dst_direction);
|
|
|
|
if(zflow->core.tcp.serverNwLatency.tv_sec || zflow->core.tcp.serverNwLatency.tv_usec)
|
|
flow->setFlowNwLatency(&zflow->core.tcp.serverNwLatency, !src2dst_direction);
|
|
|
|
flow->setRtt();
|
|
|
|
if(src2dst_direction)
|
|
flow->setFlowApplLatency(zflow->core.tcp.applLatencyMsec);
|
|
|
|
/* Update flow device stats */
|
|
if(!flow->setFlowDevice(zflow->core.deviceIP,
|
|
src2dst_direction ? zflow->core.inIndex : zflow->core.outIndex,
|
|
src2dst_direction ? zflow->core.outIndex : zflow->core.inIndex)) {
|
|
static bool flow_device_already_set = false;
|
|
if(!flow_device_already_set) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "A flow has been seen from multiple exporters or from "
|
|
"multiple IN/OUT interfaces. Check exporters configuration.");
|
|
flow_device_already_set = true;
|
|
}
|
|
}
|
|
|
|
#ifdef MAC_DEBUG
|
|
char bufm1[32], bufm2[32];
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Processing Flow [src mac: %s][dst mac: %s][src2dst: %i]",
|
|
Utils::formatMac(srcMac->get_mac(), bufm1, sizeof(bufm1)),
|
|
Utils::formatMac(dstMac->get_mac(), bufm2, sizeof(bufm2)),
|
|
(src2dst_direction) ? 1 : 0);
|
|
#endif
|
|
|
|
/* Update Mac stats
|
|
Note: do not use src2dst_direction to inc the stats as
|
|
in_bytes/in_pkts and out_bytes/out_pkts are already relative to the current
|
|
source mac (srcMac) and destination mac (dstMac)
|
|
*/
|
|
if(likely(srcMac != NULL)) {
|
|
srcMac->incSentStats(getTimeLastPktRcvd(), zflow->core.pkt_sampling_rate * zflow->core.in_pkts,
|
|
zflow->core.pkt_sampling_rate * zflow->core.in_bytes);
|
|
srcMac->incRcvdStats(getTimeLastPktRcvd(), zflow->core.pkt_sampling_rate * zflow->core.out_pkts,
|
|
zflow->core.pkt_sampling_rate * zflow->core.out_bytes);
|
|
|
|
srcMac->setSourceMac();
|
|
}
|
|
|
|
if(likely(dstMac != NULL)) {
|
|
dstMac->incSentStats(getTimeLastPktRcvd(), zflow->core.pkt_sampling_rate * zflow->core.out_pkts,
|
|
zflow->core.pkt_sampling_rate * zflow->core.out_bytes);
|
|
dstMac->incRcvdStats(getTimeLastPktRcvd(), zflow->core.pkt_sampling_rate * zflow->core.in_pkts,
|
|
zflow->core.pkt_sampling_rate * zflow->core.in_bytes);
|
|
}
|
|
|
|
if(zflow->core.l4_proto == IPPROTO_TCP) {
|
|
if(zflow->core.tcp.client_tcp_flags || zflow->core.tcp.server_tcp_flags) {
|
|
/* There's a breadown between client and server TCP flags */
|
|
if(zflow->core.tcp.client_tcp_flags)
|
|
flow->setTcpFlags(zflow->core.tcp.client_tcp_flags, src2dst_direction);
|
|
if(zflow->core.tcp.server_tcp_flags)
|
|
flow->setTcpFlags(zflow->core.tcp.server_tcp_flags, !src2dst_direction);
|
|
} else if(zflow->core.tcp.tcp_flags)
|
|
/* TCP flags are cumulated client + server */
|
|
flow->setTcpFlags(zflow->core.tcp.tcp_flags, src2dst_direction);
|
|
|
|
flow->incTcpBadStats(true,
|
|
zflow->core.tcp.ooo_in_pkts, zflow->core.tcp.retr_in_pkts,
|
|
zflow->core.tcp.lost_in_pkts);
|
|
flow->incTcpBadStats(false,
|
|
zflow->core.tcp.ooo_out_pkts, zflow->core.tcp.retr_out_pkts,
|
|
zflow->core.tcp.lost_out_pkts);
|
|
}
|
|
|
|
#ifdef NTOPNG_PRO
|
|
if(zflow->core.deviceIP) {
|
|
// if(ntop->getPrefs()->is_flow_device_port_rrd_creation_enabled() && ntop->getPro()->has_valid_license()) {
|
|
if(!flow_interfaces_stats)
|
|
flow_interfaces_stats = new FlowInterfacesStats();
|
|
|
|
if(flow_interfaces_stats) {
|
|
flow_interfaces_stats->incStats(now, zflow->core.deviceIP, zflow->core.inIndex,
|
|
zflow->core.out_bytes, zflow->core.in_bytes);
|
|
/* If the SNMP device is actually an host with an SNMP agent, then traffic can enter and leave it
|
|
from the same interface (think to a management interface). For this reason it is important to check
|
|
the outIndex and increase its counters only if it is different from inIndex to avoid double counting. */
|
|
if(zflow->core.outIndex != zflow->core.inIndex)
|
|
flow_interfaces_stats->incStats(now, zflow->core.deviceIP, zflow->core.outIndex,
|
|
zflow->core.in_bytes, zflow->core.out_bytes);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
flow->addFlowStats(src2dst_direction,
|
|
zflow->core.pkt_sampling_rate*zflow->core.in_pkts,
|
|
zflow->core.pkt_sampling_rate*zflow->core.in_bytes, 0,
|
|
zflow->core.pkt_sampling_rate*zflow->core.out_pkts,
|
|
zflow->core.pkt_sampling_rate*zflow->core.out_bytes, 0,
|
|
zflow->core.last_switched);
|
|
p.app_protocol = zflow->core.l7_proto.app_protocol, p.master_protocol = zflow->core.l7_proto.master_protocol;
|
|
p.category = NDPI_PROTOCOL_CATEGORY_UNSPECIFIED;
|
|
flow->setDetectedProtocol(p, true);
|
|
flow->setJSONInfo(json_object_to_json_string(zflow->additional_fields));
|
|
|
|
flow->updateInterfaceLocalStats(src2dst_direction,
|
|
zflow->core.pkt_sampling_rate*(zflow->core.in_pkts+zflow->core.out_pkts),
|
|
zflow->core.pkt_sampling_rate*(zflow->core.in_bytes+zflow->core.out_bytes));
|
|
|
|
if(zflow->dns_query && zflow->dns_query[0] != '\0') flow->setDNSQuery(zflow->dns_query);
|
|
if(zflow->http_url && zflow->http_url[0] != '\0') flow->setHTTPURL(zflow->http_url);
|
|
if(zflow->http_site && zflow->http_site[0] != '\0') flow->setServerName(zflow->http_site);
|
|
if(zflow->ssl_server_name && zflow->ssl_server_name[0] != '\0') flow->setServerName(zflow->ssl_server_name);
|
|
if(zflow->bittorrent_hash && zflow->bittorrent_hash[0] != '\0') flow->setBTHash(zflow->bittorrent_hash);
|
|
if(zflow->core.vrfId) flow->setVRFid(zflow->core.vrfId);
|
|
#ifdef NTOPNG_PRO
|
|
if(zflow->custom_app.pen) {
|
|
flow->setCustomApp(zflow->custom_app);
|
|
|
|
if(custom_app_stats || (custom_app_stats = new(std::nothrow) CustomAppStats(this))) {
|
|
custom_app_stats->incStats(zflow->custom_app.remapped_app_id,
|
|
zflow->core.pkt_sampling_rate * (zflow->core.in_bytes + zflow->core.out_bytes));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// NOTE: fill the category only after the server name is set
|
|
flow->fillZmqFlowCategory();
|
|
|
|
/* Do not put incStats before guessing the flow protocol */
|
|
incStats(true /* ingressPacket */,
|
|
now, srcIP.isIPv4() ? ETHERTYPE_IP : ETHERTYPE_IPV6,
|
|
flow->get_detected_protocol().app_protocol,
|
|
zflow->core.pkt_sampling_rate*(zflow->core.in_bytes + zflow->core.out_bytes),
|
|
zflow->core.pkt_sampling_rate*(zflow->core.in_pkts + zflow->core.out_pkts),
|
|
24 /* 8 Preamble + 4 CRC + 12 IFG */ + 14 /* Ethernet header */);
|
|
|
|
|
|
/* purge is actually performed at most one time every FLOW_PURGE_FREQUENCY */
|
|
// purgeIdle(zflow->core.last_switched);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
bool NetworkInterface::processPacket(u_int32_t bridge_iface_idx,
|
|
bool ingressPacket,
|
|
const struct bpf_timeval *when,
|
|
const u_int64_t packet_time,
|
|
struct ndpi_ethhdr *eth,
|
|
u_int16_t vlan_id,
|
|
struct ndpi_iphdr *iph,
|
|
struct ndpi_ipv6hdr *ip6,
|
|
u_int16_t ipsize,
|
|
u_int32_t rawsize,
|
|
const struct pcap_pkthdr *h,
|
|
const u_char *packet,
|
|
u_int16_t *ndpiProtocol,
|
|
Host **srcHost, Host **dstHost,
|
|
Flow **hostFlow) {
|
|
bool src2dst_direction, is_sent_packet = false; /* FIX */
|
|
u_int8_t l4_proto;
|
|
Flow *flow;
|
|
Mac *srcMac = NULL, *dstMac = NULL;
|
|
IpAddress src_ip, dst_ip;
|
|
ICMPinfo icmp_info;
|
|
u_int16_t src_port = 0, dst_port = 0, payload_len = 0;
|
|
struct ndpi_tcphdr *tcph = NULL;
|
|
struct ndpi_udphdr *udph = NULL;
|
|
struct sctphdr *sctph = NULL;
|
|
u_int16_t l4_packet_len;
|
|
u_int8_t *l4, tcp_flags = 0, *payload = NULL;
|
|
u_int8_t *ip;
|
|
bool is_fragment = false, new_flow;
|
|
bool pass_verdict = true;
|
|
*hostFlow = NULL;
|
|
|
|
/* VLAN disaggregation */
|
|
if((!isDynamicInterface()) && (flowHashingMode == flowhashing_vlan) && (vlan_id > 0)) {
|
|
NetworkInterface *vIface;
|
|
|
|
if((vIface = getSubInterface((u_int32_t)vlan_id, false)) != NULL) {
|
|
bool ret;
|
|
|
|
vIface->setTimeLastPktRcvd(h->ts.tv_sec);
|
|
ret = vIface->processPacket(bridge_iface_idx,
|
|
ingressPacket, when, packet_time,
|
|
eth, vlan_id,
|
|
iph, ip6, ipsize, rawsize,
|
|
h, packet, ndpiProtocol,
|
|
srcHost, dstHost, hostFlow);
|
|
|
|
incStats(ingressPacket, when->tv_sec, ETHERTYPE_IP, NDPI_PROTOCOL_UNKNOWN,
|
|
rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */);
|
|
|
|
return(ret);
|
|
}
|
|
}
|
|
|
|
if((srcMac = getMac(eth->h_source, true))) {
|
|
/* NOTE: in nEdge, stats are updated into Flow::update_hosts_stats */
|
|
#ifndef HAVE_NEDGE
|
|
srcMac->incSentStats(getTimeLastPktRcvd(), 1, rawsize);
|
|
#endif
|
|
srcMac->setSeenIface(bridge_iface_idx);
|
|
|
|
#ifdef NTOPNG_PRO
|
|
u_int16_t mac_pool = 0;
|
|
char bufMac[24];
|
|
char *mac_str;
|
|
|
|
/* When captive portal is disabled, use the auto_assigned_pool_id as the default MAC pool */
|
|
if(host_pools
|
|
&& (ntop->getPrefs()->get_auto_assigned_pool_id() != NO_HOST_POOL_ID)
|
|
&& (!ntop->getPrefs()->isCaptivePortalEnabled())
|
|
&& (srcMac->locate() == located_on_lan_interface)) {
|
|
if(!host_pools->findMacPool(srcMac->get_mac(), &mac_pool) || (mac_pool == NO_HOST_POOL_ID)) {
|
|
mac_str = Utils::formatMac(srcMac->get_mac(), bufMac, sizeof(bufMac));
|
|
host_pools->addToPool(mac_str, ntop->getPrefs()->get_auto_assigned_pool_id(), 0);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if((dstMac = getMac(eth->h_dest, true))) {
|
|
/* NOTE: in nEdge, stats are updated into Flow::update_hosts_stats */
|
|
#ifndef HAVE_NEDGE
|
|
dstMac->incRcvdStats(getTimeLastPktRcvd(), 1, rawsize);
|
|
#endif
|
|
}
|
|
|
|
if(iph != NULL) {
|
|
/* IPv4 */
|
|
if(ipsize < 20) {
|
|
incStats(ingressPacket,
|
|
when->tv_sec, ETHERTYPE_IP, NDPI_PROTOCOL_UNKNOWN,
|
|
rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */);
|
|
return(pass_verdict);
|
|
}
|
|
|
|
if(((iph->ihl * 4) > ipsize) || (ipsize < ntohs(iph->tot_len))
|
|
|| (iph->frag_off & htons(0x1FFF /* IP_OFFSET */)) != 0)
|
|
is_fragment = true;
|
|
|
|
l4_packet_len = ntohs(iph->tot_len) - (iph->ihl * 4);
|
|
l4_proto = iph->protocol;
|
|
l4 = ((u_int8_t *) iph + iph->ihl * 4);
|
|
ip = (u_int8_t*)iph;
|
|
} else {
|
|
/* IPv6 */
|
|
u_int ipv6_shift = sizeof(const struct ndpi_ipv6hdr);
|
|
|
|
if(ipsize < sizeof(const struct ndpi_ipv6hdr)) {
|
|
incStats(ingressPacket,
|
|
when->tv_sec, ETHERTYPE_IPV6, NDPI_PROTOCOL_UNKNOWN, rawsize,
|
|
1, 24 /* 8 Preamble + 4 CRC + 12 IFG */);
|
|
return(pass_verdict);
|
|
}
|
|
|
|
l4_packet_len = ntohs(ip6->ip6_hdr.ip6_un1_plen);
|
|
l4_proto = ip6->ip6_hdr.ip6_un1_nxt;
|
|
|
|
if((l4_proto == 0x3C /* IPv6 destination option */) ||
|
|
(l4_proto == 0x0 /* Hop-by-hop option */)) {
|
|
u_int8_t *options = (u_int8_t*)ip6 + ipv6_shift;
|
|
|
|
l4_proto = options[0];
|
|
ipv6_shift += 8 * (options[1] + 1);
|
|
|
|
if(ipsize < ipv6_shift) {
|
|
incStats(ingressPacket,
|
|
when->tv_sec, ETHERTYPE_IPV6, NDPI_PROTOCOL_UNKNOWN,
|
|
rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */);
|
|
return(pass_verdict);
|
|
}
|
|
}
|
|
|
|
l4 = (u_int8_t*)ip6 + ipv6_shift;
|
|
ip = (u_int8_t*)ip6;
|
|
}
|
|
|
|
if(l4_proto == IPPROTO_TCP) {
|
|
if(l4_packet_len >= sizeof(struct ndpi_tcphdr)) {
|
|
u_int tcp_len;
|
|
|
|
/* TCP */
|
|
tcph = (struct ndpi_tcphdr *)l4;
|
|
src_port = tcph->source, dst_port = tcph->dest;
|
|
tcp_flags = l4[13];
|
|
tcp_len = min_val(4*tcph->doff, l4_packet_len);
|
|
payload = &l4[tcp_len];
|
|
payload_len = max_val(0, l4_packet_len-4*tcph->doff);
|
|
// TODO: check if payload should be set to NULL when payload_len == 0
|
|
} else {
|
|
/* Packet too short: this is a faked packet */
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "Invalid TCP packet received [%u bytes long]", l4_packet_len);
|
|
incStats(ingressPacket, when->tv_sec, iph ? ETHERTYPE_IP : ETHERTYPE_IPV6,
|
|
NDPI_PROTOCOL_UNKNOWN, rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */);
|
|
return(pass_verdict);
|
|
}
|
|
} else if(l4_proto == IPPROTO_UDP) {
|
|
if(l4_packet_len >= sizeof(struct ndpi_udphdr)) {
|
|
/* UDP */
|
|
udph = (struct ndpi_udphdr *)l4;
|
|
src_port = udph->source, dst_port = udph->dest;
|
|
payload = &l4[sizeof(struct ndpi_udphdr)];
|
|
payload_len = max_val(0, l4_packet_len-sizeof(struct ndpi_udphdr));
|
|
} else {
|
|
/* Packet too short: this is a faked packet */
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "Invalid UDP packet received [%u bytes long]", l4_packet_len);
|
|
incStats(ingressPacket, when->tv_sec, iph ? ETHERTYPE_IP : ETHERTYPE_IPV6, NDPI_PROTOCOL_UNKNOWN,
|
|
rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */);
|
|
return(pass_verdict);
|
|
}
|
|
} else if(l4_proto == IPPROTO_SCTP) {
|
|
if(l4_packet_len >= sizeof(struct sctphdr)) {
|
|
/* SCTP */
|
|
sctph = (struct sctphdr *)l4;
|
|
src_port = sctph->sport, dst_port = sctph->dport;
|
|
|
|
payload = &l4[sizeof(struct sctphdr)];
|
|
payload_len = max_val(0, l4_packet_len - sizeof(struct sctphdr));
|
|
} else {
|
|
/* Packet too short: this is a faked packet */
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "Invalid SCTP packet received [%u bytes long]", l4_packet_len);
|
|
incStats(ingressPacket, when->tv_sec, iph ? ETHERTYPE_IP : ETHERTYPE_IPV6, NDPI_PROTOCOL_UNKNOWN,
|
|
rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */);
|
|
return(pass_verdict);
|
|
}
|
|
} else if (l4_proto == IPPROTO_ICMP) {
|
|
icmp_info.dissectICMP(l4_packet_len, l4);
|
|
} else {
|
|
/* non TCP/UDP protocols */
|
|
}
|
|
|
|
if(iph != NULL)
|
|
src_ip.set(iph->saddr), dst_ip.set(iph->daddr);
|
|
else
|
|
src_ip.set(&ip6->ip6_src), dst_ip.set(&ip6->ip6_dst);
|
|
|
|
#if defined(WIN32) && defined(DEMO_WIN32)
|
|
if(this->ethStats.getNumPackets() > MAX_NUM_PACKETS) {
|
|
static bool showMsg = false;
|
|
|
|
if(!showMsg) {
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "-----------------------------------------------------------");
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "WARNING: this demo application is a limited ntopng version able to");
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "capture up to %d packets. If you are interested", MAX_NUM_PACKETS);
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "in the full version please have a look at the ntop");
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "home page http://www.ntop.org/.");
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "-----------------------------------------------------------");
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "");
|
|
showMsg = true;
|
|
}
|
|
|
|
return(pass_verdict);
|
|
}
|
|
#endif
|
|
|
|
PROFILING_SECTION_ENTER("NetworkInterface::processPacket: getFlow", 1);
|
|
/* Updating Flow */
|
|
flow = getFlow(srcMac, dstMac, vlan_id, 0, 0, 0,
|
|
l4_proto == IPPROTO_ICMP ? &icmp_info : NULL,
|
|
&src_ip, &dst_ip, src_port, dst_port,
|
|
l4_proto, &src2dst_direction, last_pkt_rcvd, last_pkt_rcvd, rawsize, &new_flow, true);
|
|
PROFILING_SECTION_EXIT(1);
|
|
|
|
if(flow == NULL) {
|
|
incStats(ingressPacket, when->tv_sec, iph ? ETHERTYPE_IP : ETHERTYPE_IPV6, NDPI_PROTOCOL_UNKNOWN,
|
|
rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */);
|
|
return(pass_verdict);
|
|
} else {
|
|
#ifdef HAVE_NEDGE
|
|
if(new_flow)
|
|
flow->setIngress2EgressDirection(ingressPacket);
|
|
#endif
|
|
*srcHost = src2dst_direction ? flow->get_cli_host() : flow->get_srv_host();
|
|
*dstHost = src2dst_direction ? flow->get_srv_host() : flow->get_cli_host();
|
|
*hostFlow = flow;
|
|
|
|
switch(l4_proto) {
|
|
case IPPROTO_TCP:
|
|
flow->updateTcpFlags(when, tcp_flags, src2dst_direction);
|
|
flow->updateTcpSeqNum(when, ntohl(tcph->seq), ntohl(tcph->ack_seq), ntohs(tcph->window),
|
|
tcp_flags, l4_packet_len - (4 * tcph->doff),
|
|
src2dst_direction);
|
|
break;
|
|
|
|
case IPPROTO_ICMP:
|
|
case IPPROTO_ICMPV6:
|
|
if(l4_packet_len > 2) {
|
|
u_int8_t icmp_type = l4[0];
|
|
u_int8_t icmp_code = l4[1];
|
|
|
|
if((flow->get_cli_host() && flow->get_cli_host()->isLocalHost())
|
|
&& (flow->get_srv_host() && flow->get_srv_host()->isLocalHost())) {
|
|
/* Set correct direction in localhost ping */
|
|
if((icmp_type == ICMP_ECHO /* ICMP Echo [RFC792] */)
|
|
|| (icmp_type == ICMP6_ECHO_REQUEST /* 128 - ICMPV6 Echo Request [RFC4443] */))
|
|
src2dst_direction = true;
|
|
else if((icmp_type == ICMP_ECHOREPLY /* ICMP Echo Reply [RFC792] */)
|
|
|| (icmp_type == ICMP6_ECHO_REPLY /* 129 - ICMPV6 Echo Reply [RFC4443] */))
|
|
src2dst_direction = false;
|
|
}
|
|
|
|
#if 0
|
|
if(((icmp_type == ND_NEIGHBOR_ADVERT) || (icmp_type == ND_NEIGHBOR_SOLICIT))
|
|
&& l4_packet_len >= 24) {
|
|
/*
|
|
Neighbor Solicitation and Neighbor Advertisement
|
|
have the Target Address at offset 8.
|
|
|
|
https://tools.ietf.org/html/rfc2461#section-4.1
|
|
*/
|
|
Host * target_address_h;
|
|
IpAddress target_address;
|
|
|
|
target_address.set((ndpi_in6_addr*)&l4[8]);
|
|
|
|
char buf[64];
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "->> %s", target_address.print(buf, sizeof(buf)));
|
|
|
|
if(target_address.isNonEmptyUnicastAddress()
|
|
&& (target_address_h = getHost(&target_address, vlan_id))
|
|
&& (!target_address_h->isBroadcastDomainHost()))
|
|
target_address_h->setBroadcastDomainHost();
|
|
}
|
|
#endif
|
|
|
|
flow->setICMP(src2dst_direction, icmp_type, icmp_code, l4);
|
|
if(l4_proto == IPPROTO_ICMP)
|
|
icmp_v4.incStats(icmp_type, icmp_code, is_sent_packet, NULL);
|
|
else
|
|
icmp_v6.incStats(icmp_type, icmp_code, is_sent_packet, NULL);
|
|
|
|
if(l4_proto == IPPROTO_ICMP) {
|
|
ndpi_protocol icmp_proto = flow->get_detected_protocol();
|
|
|
|
if(icmp_proto.category == NDPI_PROTOCOL_CATEGORY_UNSPECIFIED) {
|
|
ndpi_fill_ip_protocol_category(ndpi_struct,
|
|
((struct ndpi_iphdr*)ip)->saddr, ((struct ndpi_iphdr*)ip)->daddr, &icmp_proto);
|
|
flow->setDetectedProtocol(icmp_proto, false);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
#ifndef HAVE_NEDGE
|
|
#ifdef __OpenBSD__
|
|
struct timeval tv_ts;
|
|
tv_ts.tv_sec = h->ts.tv_sec;
|
|
tv_ts.tv_usec = h->ts.tv_usec;
|
|
flow->incStats(src2dst_direction, rawsize, payload, payload_len, l4_proto, &tv_ts);
|
|
#else
|
|
PROFILING_SECTION_ENTER("NetworkInterface::processPacket: flow->incStats", 2);
|
|
flow->incStats(src2dst_direction, rawsize, payload, payload_len, l4_proto, &h->ts);
|
|
PROFILING_SECTION_EXIT(2);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
/* Protocol Detection */
|
|
flow->updateInterfaceLocalStats(src2dst_direction, 1, rawsize);
|
|
|
|
if(!flow->isDetectionCompleted()) {
|
|
if(!is_fragment) {
|
|
struct ndpi_flow_struct *ndpi_flow = flow->get_ndpi_flow();
|
|
struct ndpi_id_struct *cli = (struct ndpi_id_struct*)flow->get_cli_id();
|
|
struct ndpi_id_struct *srv = (struct ndpi_id_struct*)flow->get_srv_id();
|
|
|
|
if(flow->get_packets() >= NDPI_MIN_NUM_PACKETS) {
|
|
flow->setDetectedProtocol(ndpi_detection_giveup(ndpi_struct, ndpi_flow, 1), false);
|
|
} else
|
|
flow->setDetectedProtocol(ndpi_detection_process_packet(ndpi_struct, ndpi_flow,
|
|
ip, ipsize, (u_int32_t)packet_time,
|
|
cli, srv), false);
|
|
} else {
|
|
// FIX - only handle unfragmented packets
|
|
// ntop->getTrace()->traceEvent(TRACE_WARNING, "IP fragments are not handled yet!");
|
|
}
|
|
}
|
|
|
|
if(flow->isDetectionCompleted()
|
|
&& (!isSampledTraffic())
|
|
&& flow->get_cli_host() && flow->get_srv_host()) {
|
|
struct ndpi_flow_struct *ndpi_flow;
|
|
|
|
switch(ndpi_get_lower_proto(flow->get_detected_protocol())) {
|
|
case NDPI_PROTOCOL_DHCP:
|
|
{
|
|
Mac *mac = (*srcHost)->getMac(), *payload_cli_mac;
|
|
|
|
if(mac && (payload_len > 240)) {
|
|
struct dhcp_packet *dhcpp = (struct dhcp_packet*)payload;
|
|
|
|
if(dhcpp->msgType == 0x01) /* Request */
|
|
;//mac->setDhcpHost();
|
|
else if(dhcpp->msgType == 0x02) /* Reply */
|
|
checkMacIPAssociation(false, dhcpp->chaddr, dhcpp->yiaddr);
|
|
|
|
for(int i = 240; i<payload_len; ) {
|
|
u_int8_t id = payload[i], len = payload[i+1];
|
|
|
|
if(len == 0)
|
|
break;
|
|
|
|
#ifdef DHCP_DEBUG
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "[DHCP] [id=%u][len=%u]", id, len);
|
|
#endif
|
|
|
|
if(id == 12 /* Host Name */) {
|
|
char name[64], buf[24], *client_mac, key[64];
|
|
int j;
|
|
|
|
j = ndpi_min(len, sizeof(name)-1);
|
|
strncpy((char*)name, (char*)&payload[i+2], j);
|
|
name[j] = '\0';
|
|
|
|
client_mac = Utils::formatMac(&payload[28], buf, sizeof(buf));
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "[DHCP] %s = '%s'", client_mac, name);
|
|
|
|
snprintf(key, sizeof(key), DHCP_CACHE, get_id());
|
|
ntop->getRedis()->hashSet(key, client_mac, name);
|
|
|
|
if((payload_cli_mac = getMac(&payload[28], false)))
|
|
payload_cli_mac->inlineSetDHCPName(name);
|
|
|
|
#ifdef DHCP_DEBUG
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "[DHCP] %s = '%s'", client_mac, name);
|
|
#endif
|
|
} else if(id == 55 /* Parameters List (Fingerprint) */) {
|
|
char fingerprint[64], buf[32];
|
|
u_int idx, offset = 0;
|
|
|
|
len = ndpi_min(len, sizeof(buf)/2);
|
|
|
|
for(idx=0; idx<len; idx++) {
|
|
snprintf((char*)&fingerprint[offset], sizeof(fingerprint)-offset-1, "%02X", payload[i+2+idx] & 0xFF);
|
|
offset += 2;
|
|
}
|
|
|
|
#ifdef DHCP_DEBUG
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "%s = %s", mac->print(buf, sizeof(buf)),fingerprint);
|
|
#endif
|
|
mac->inlineSetFingerprint((char*)flow->get_ndpi_flow()->protos.dhcp.fingerprint);
|
|
} else if(id == 0xFF)
|
|
break; /* End of options */
|
|
|
|
i += len + 2;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NDPI_PROTOCOL_DHCPV6:
|
|
{
|
|
Mac *src_mac = (*srcHost)->getMac();
|
|
Mac *dst_mac = (*dstHost)->getMac();
|
|
|
|
if(src_mac && dst_mac
|
|
&& (payload_len > 20)
|
|
&& dst_mac->isMulticast())
|
|
;//src_mac->setDhcpHost();
|
|
}
|
|
break;
|
|
|
|
case NDPI_PROTOCOL_NETBIOS:
|
|
if(*srcHost) {
|
|
if(! (*srcHost)->is_label_set()) {
|
|
char name[64];
|
|
|
|
if(((payload[2] & 0x80) /* NetBIOS Response */ || ((payload[2] & 0x78) == 0x28 /* NetBIOS Registration */))
|
|
&& (ndpi_netbios_name_interpret((char*)&payload[12], name, sizeof(name)) > 0)
|
|
&& (!strstr(name, "__MSBROWSE__"))
|
|
) {
|
|
|
|
if(name[0] == '*') {
|
|
int limit = min(payload_len-57, (int)sizeof(name)-1);
|
|
int i = 0;
|
|
|
|
while((i<limit) && (payload[57+i] != 0x20) && isprint(payload[57+i])) {
|
|
name[i] = payload[57+i];
|
|
i++;
|
|
}
|
|
|
|
if((i<limit) && (payload[57+i] != 0x00 /* Not a Workstation/Redirector */))
|
|
name[0] = '\0'; /* ignore */
|
|
else
|
|
name[i] = '\0';
|
|
}
|
|
#if 0
|
|
char buf[32];
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Setting hostname from NetBios [raw=0x%x opcode=0x%x response=0x%x]: ip=%s -> '%s'",
|
|
payload[2], (payload[2] & 0x78) >> 3, (payload[2] & 0x80) >> 7,
|
|
(*srcHost)->get_ip()->print(buf, sizeof(buf)), name);
|
|
#endif
|
|
if(name[0])
|
|
(*srcHost)->set_host_label(name, true);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NDPI_PROTOCOL_BITTORRENT:
|
|
if((flow->getBitTorrentHash() == NULL)
|
|
&& (l4_proto == IPPROTO_UDP)
|
|
&& (flow->get_packets() < 8))
|
|
flow->dissectBittorrent((char*)payload, payload_len);
|
|
break;
|
|
|
|
case NDPI_PROTOCOL_HTTP:
|
|
if(payload_len > 0)
|
|
flow->dissectHTTP(src2dst_direction, (char*)payload, payload_len);
|
|
break;
|
|
|
|
case NDPI_PROTOCOL_SSDP:
|
|
if(payload_len > 0)
|
|
flow->dissectSSDP(src2dst_direction, (char*)payload, payload_len);
|
|
break;
|
|
|
|
case NDPI_PROTOCOL_DNS:
|
|
ndpi_flow = flow->get_ndpi_flow();
|
|
|
|
/*
|
|
DNS-over-TCP flows may carry zero-payload TCP segments
|
|
e.g., during three-way-handshake, or when acknowledging.
|
|
Make sure only non-zero-payload segments are processed.
|
|
*/
|
|
if((payload_len > 0) && payload) {
|
|
/*
|
|
DNS-over-TCP has a 2-bytes field with DNS payload length
|
|
at the beginning. See RFC1035 section 4.2.2. TCP usage.
|
|
*/
|
|
u_int8_t dns_offset = ((l4_proto == IPPROTO_TCP) && (payload_len > 1)) ? 2 : 0;
|
|
struct ndpi_dns_packet_header *header = (struct ndpi_dns_packet_header*)(payload + dns_offset);
|
|
u_int16_t dns_flags = ntohs(header->flags);
|
|
bool is_query = (dns_flags & 0x8000) ? 0 : 1;
|
|
|
|
if(flow->get_cli_host() && flow->get_srv_host()) {
|
|
Host *client = src2dst_direction ? flow->get_cli_host() : flow->get_srv_host();
|
|
Host *server = src2dst_direction ? flow->get_srv_host() : flow->get_cli_host();
|
|
|
|
/*
|
|
Inside the DNS packet it is possible to have multiple queries
|
|
and mix query types. In general this is not a practice followed
|
|
by applications.
|
|
*/
|
|
|
|
if(is_query) {
|
|
u_int16_t query_type = ndpi_flow ? ndpi_flow->protos.dns.query_type : 0;
|
|
|
|
client->incNumDNSQueriesSent(query_type), server->incNumDNSQueriesRcvd(query_type);
|
|
} else {
|
|
u_int8_t ret_code = (dns_flags & 0x000F);
|
|
|
|
client->incNumDNSResponsesSent(ret_code), server->incNumDNSResponsesRcvd(ret_code);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(ndpi_flow) {
|
|
struct ndpi_id_struct *cli = (struct ndpi_id_struct*)flow->get_cli_id();
|
|
struct ndpi_id_struct *srv = (struct ndpi_id_struct*)flow->get_srv_id();
|
|
|
|
memset(&ndpi_flow->detected_protocol_stack,
|
|
0, sizeof(ndpi_flow->detected_protocol_stack));
|
|
|
|
ndpi_detection_process_packet(ndpi_struct, ndpi_flow,
|
|
ip, ipsize, (u_int32_t)packet_time,
|
|
src2dst_direction ? cli : srv,
|
|
src2dst_direction ? srv : cli);
|
|
|
|
/*
|
|
We reset the nDPI flow so that it can decode new packets
|
|
of the same flow (e.g. the DNS response)
|
|
*/
|
|
ndpi_flow->detected_protocol_stack[0] = NDPI_PROTOCOL_UNKNOWN;
|
|
}
|
|
break;
|
|
|
|
case NDPI_PROTOCOL_MDNS:
|
|
#ifdef MDNS_TEST
|
|
extern void _dissectMDNS(u_char *buf, u_int buf_len, char *out, u_int out_len);
|
|
char outbuf[1024];
|
|
|
|
_dissectMDNS(payload, payload_len, outbuf, sizeof(outbuf));
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "%s", outbuf);
|
|
#endif
|
|
flow->dissectMDNS(payload, payload_len);
|
|
|
|
if(discovery && iph)
|
|
discovery->queueMDNSRespomse(iph->saddr, payload, payload_len);
|
|
break;
|
|
|
|
case NDPI_PROTOCOL_DROPBOX:
|
|
if((src_port == dst_port) && (dst_port == htons(17500)))
|
|
flow->get_cli_host()->dissectDropbox((const char *)payload, payload_len);
|
|
break;
|
|
|
|
case NDPI_PROTOCOL_SSL:
|
|
if(payload_len > 0)
|
|
flow->dissectSSL((char *)payload, payload_len);
|
|
break;
|
|
}
|
|
|
|
flow->processDetectedProtocol();
|
|
|
|
#ifdef HAVE_NEDGE
|
|
if(is_bridge_interface()) {
|
|
struct tm now;
|
|
time_t t_now = time(NULL);
|
|
localtime_r(&t_now, &now);
|
|
pass_verdict = flow->checkPassVerdict(&now);
|
|
|
|
if(pass_verdict) {
|
|
TrafficShaper *shaper_ingress, *shaper_egress;
|
|
char buf[64];
|
|
|
|
flow->getFlowShapers(src2dst_direction, &shaper_ingress, &shaper_egress);
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG, "[%s] %u / %u ",
|
|
flow->get_detected_protocol_name(buf, sizeof(buf)),
|
|
shaper_ingress, shaper_egress);
|
|
pass_verdict = passShaperPacket(shaper_ingress, shaper_egress, (struct pcap_pkthdr*)h);
|
|
} else {
|
|
flow->incFlowDroppedCounters();
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if 0
|
|
if(new_flow)
|
|
flow->updateCommunityIdFlowHash();
|
|
#endif
|
|
|
|
PROFILING_SECTION_ENTER("NetworkInterface::processPacket: incStats", 4);
|
|
incStats(ingressPacket, when->tv_sec, iph ? ETHERTYPE_IP : ETHERTYPE_IPV6,
|
|
flow->get_detected_protocol().app_protocol,
|
|
rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */);
|
|
PROFILING_SECTION_EXIT(4);
|
|
|
|
return(pass_verdict);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::purgeIdle(time_t when) {
|
|
if(purge_idle_flows_hosts) {
|
|
u_int n, m;
|
|
|
|
last_pkt_rcvd = when;
|
|
|
|
if((n = purgeIdleFlows()) > 0)
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG, "Purged %u/%u idle flows on %s",
|
|
n, getNumFlows(), ifname);
|
|
|
|
if((m = purgeIdleHostsMacsASesVlans()) > 0)
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG, "Purged %u/%u idle hosts/macs on %s",
|
|
m, getNumHosts()+getNumMacs(), ifname);
|
|
}
|
|
|
|
if(flowHashing) {
|
|
FlowHashing *current, *tmp;
|
|
|
|
HASH_ITER(hh, flowHashing, current, tmp) {
|
|
if(current->iface)
|
|
current->iface->purgeIdle(when);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
#ifdef HAVE_EBPF
|
|
|
|
#ifdef EBPF_DEBUG
|
|
|
|
static void IPV4Handler(Flow *f, eBPFevent *e) {
|
|
struct ipv4_kernel_data *event = &e->event.v4;
|
|
char buf1[32], buf2[32];
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"[%s][IPv4][%s][pid/tid: %u/%u (%s), uid/gid: %u/%u][father pid/tid: %u/%u (%s), uid/gid: %u/%u][addr: %s:%u <-> %s:%u][latency: %.2f msec]\n",
|
|
e->ifname, (event->net.proto == IPPROTO_TCP) ? "TCP" : "UDP",
|
|
e->proc.pid, e->proc.tid,
|
|
e->proc.full_task_path ? e->proc.full_task_path : e->proc.task,
|
|
e->proc.uid, e->proc.gid,
|
|
e->father.pid, e->father.tid,
|
|
e->father.full_task_path ? e->father.full_task_path : e->father.task,
|
|
e->father.uid, e->father.gid,
|
|
Utils::intoaV4(htonl(event->saddr), buf1, sizeof(buf1)), event->net.sport,
|
|
Utils::intoaV4(htonl(event->daddr), buf2, sizeof(buf2)), event->net.dport,
|
|
((float)event->net.latency_usec)/(float)1000);
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
static void IPV6Handler(Flow *f, eBPFevent *e) {
|
|
struct ipv6_kernel_data *event = &e->event.v6;
|
|
char buf1[32], buf2[32];
|
|
struct ndpi_in6_addr saddr, daddr;
|
|
|
|
memcpy(&saddr, &event->saddr, sizeof(saddr));
|
|
memcpy(&daddr, &event->daddr, sizeof(daddr));
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"[%s][IPv6][%s][pid/tid: %u/%u (%s), uid/gid: %u/%u][father pid/tid: %u/%u (%s), uid/gid: %u/%u][addr: %s:%u <-> %s:%u][latency: %.2f msec]\n",
|
|
e->ifname, (event->net.proto == IPPROTO_TCP) ? "TCP" : "UDP",
|
|
e->proc.pid, e->proc.tid,
|
|
e->proc.full_task_path ? e->proc.full_task_path : e->proc.task,
|
|
e->proc.uid, e->proc.gid,
|
|
e->father.pid, e->father.tid,
|
|
e->father.full_task_path ? e->father.full_task_path : e->father.task,
|
|
e->father.uid, e->father.gid,
|
|
Utils::intoaV6(saddr, 128, buf1, sizeof(buf1)),
|
|
event->net.sport,
|
|
Utils::intoaV6(daddr, 128, buf2, sizeof(buf2)),
|
|
event->net.dport, ((float)event->net.latency_usec)/(float)1000);
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
/* **************************************************** */
|
|
|
|
bool NetworkInterface::dissectPacket(u_int32_t bridge_iface_idx,
|
|
bool ingressPacket,
|
|
u_int8_t *sender_mac,
|
|
const struct pcap_pkthdr *h,
|
|
const u_char *packet,
|
|
u_int16_t *ndpiProtocol,
|
|
Host **srcHost, Host **dstHost,
|
|
Flow **flow) {
|
|
struct ndpi_ethhdr *ethernet, dummy_ethernet;
|
|
u_int64_t time;
|
|
u_int16_t eth_type, ip_offset, vlan_id = 0, eth_offset = 0;
|
|
u_int32_t null_type;
|
|
int pcap_datalink_type = get_datalink();
|
|
bool pass_verdict = true;
|
|
u_int32_t rawsize = h->len * scalingFactor;
|
|
*flow = NULL;
|
|
|
|
/* Note summy ethernet is always 0 unless sender_mac is set (Netfilter only) */
|
|
memset(&dummy_ethernet, 0, sizeof(dummy_ethernet));
|
|
|
|
pollQueuedeBPFEvents();
|
|
reloadCustomCategories();
|
|
bcast_domains->inlineReloadBroadcastDomains();
|
|
|
|
/* Netfilter interfaces don't report MAC addresses on packets */
|
|
if(getIfType() == interface_type_NETFILTER)
|
|
rawsize += sizeof(struct ndpi_ethhdr);
|
|
|
|
if(h->len > ifMTU) {
|
|
if(!mtuWarningShown) {
|
|
#ifdef __linux__
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Invalid packet received [len: %u][max len: %u].", h->len, ifMTU);
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "If you have TSO/GRO enabled, please disable it");
|
|
if (strchr(ifname, ':') == NULL) /* print ethtool command for standard interfaces only */
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Use sudo ethtool -K %s gro off gso off tso off", ifname);
|
|
#endif
|
|
mtuWarningShown = true;
|
|
}
|
|
}
|
|
|
|
setTimeLastPktRcvd(h->ts.tv_sec);
|
|
purgeIdle(h->ts.tv_sec);
|
|
|
|
time = ((uint64_t) h->ts.tv_sec) * 1000 + h->ts.tv_usec / 1000;
|
|
|
|
datalink_check:
|
|
if(pcap_datalink_type == DLT_NULL) {
|
|
memcpy(&null_type, &packet[eth_offset], sizeof(u_int32_t));
|
|
|
|
switch(null_type) {
|
|
case BSD_AF_INET:
|
|
eth_type = ETHERTYPE_IP;
|
|
break;
|
|
case BSD_AF_INET6_BSD:
|
|
case BSD_AF_INET6_FREEBSD:
|
|
case BSD_AF_INET6_DARWIN:
|
|
eth_type = ETHERTYPE_IPV6;
|
|
break;
|
|
default:
|
|
incStats(ingressPacket, h->ts.tv_sec, 0, NDPI_PROTOCOL_UNKNOWN, rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */);
|
|
goto dissect_packet_end; /* Any other non IP protocol */
|
|
}
|
|
|
|
ethernet = (struct ndpi_ethhdr *)&dummy_ethernet;
|
|
if(sender_mac) memcpy(&dummy_ethernet.h_source, sender_mac, 6);
|
|
ip_offset = 4 + eth_offset;
|
|
} else if(pcap_datalink_type == DLT_EN10MB) {
|
|
ethernet = (struct ndpi_ethhdr *)&packet[eth_offset];
|
|
ip_offset = sizeof(struct ndpi_ethhdr) + eth_offset;
|
|
eth_type = ntohs(ethernet->h_proto);
|
|
} else if(pcap_datalink_type == 113 /* Linux Cooked Capture */) {
|
|
ethernet = (struct ndpi_ethhdr *)&dummy_ethernet;
|
|
if(sender_mac) memcpy(&dummy_ethernet.h_source, sender_mac, 6);
|
|
eth_type = (packet[eth_offset+14] << 8) + packet[eth_offset+15];
|
|
ip_offset = 16 + eth_offset;
|
|
#ifdef DLT_RAW
|
|
} else if(pcap_datalink_type == DLT_RAW /* Linux TUN/TAP device in TUN mode; Raw IP capture */
|
|
|| pcap_datalink_type == 14 /* raw IP DLT_RAW on OpenBSD captures */) {
|
|
switch((packet[eth_offset] & 0xf0) >> 4) {
|
|
case 4:
|
|
eth_type = ETHERTYPE_IP;
|
|
break;
|
|
case 6:
|
|
eth_type = ETHERTYPE_IPV6;
|
|
break;
|
|
default:
|
|
incStats(ingressPacket, h->ts.tv_sec, 0, NDPI_PROTOCOL_UNKNOWN, rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */);
|
|
goto dissect_packet_end; /* Unknown IP protocol version */
|
|
}
|
|
|
|
if(sender_mac) memcpy(&dummy_ethernet.h_source, sender_mac, 6);
|
|
ethernet = (struct ndpi_ethhdr *)&dummy_ethernet;
|
|
ip_offset = eth_offset;
|
|
#endif /* DLT_RAW */
|
|
} else if(pcap_datalink_type == DLT_IPV4) {
|
|
eth_type = ETHERTYPE_IP;
|
|
if(sender_mac) memcpy(&dummy_ethernet.h_source, sender_mac, 6);
|
|
ethernet = (struct ndpi_ethhdr *)&dummy_ethernet;
|
|
ip_offset = 0;
|
|
} else {
|
|
incStats(ingressPacket, h->ts.tv_sec, 0, NDPI_PROTOCOL_UNKNOWN, rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */);
|
|
goto dissect_packet_end;
|
|
}
|
|
|
|
while(true) {
|
|
if(eth_type == 0x8100 /* VLAN */) {
|
|
Ether80211q *qType = (Ether80211q*)&packet[ip_offset];
|
|
|
|
vlan_id = ntohs(qType->vlanId) & 0xFFF;
|
|
eth_type = (packet[ip_offset+2] << 8) + packet[ip_offset+3];
|
|
ip_offset += 4;
|
|
} else if(eth_type == 0x8847 /* MPLS */) {
|
|
u_int8_t bos; /* bottom_of_stack */
|
|
|
|
bos = (((u_int8_t)packet[ip_offset+2]) & 0x1), ip_offset += 4;
|
|
if(bos) {
|
|
eth_type = ETHERTYPE_IP;
|
|
break;
|
|
}
|
|
} else
|
|
break;
|
|
}
|
|
|
|
decode_packet_eth:
|
|
switch(eth_type) {
|
|
case ETHERTYPE_PPOE:
|
|
ip_offset += 6 /* PPPoE */;
|
|
/* Now we need to skip the PPP header */
|
|
if(packet[ip_offset] == 0x0)
|
|
eth_type = packet[ip_offset+1], ip_offset += 2; /* 2 Byte protocol */
|
|
else
|
|
eth_type = packet[ip_offset], ip_offset += 1; /* 1 Byte protocol */
|
|
|
|
switch(eth_type) {
|
|
case 0x21:
|
|
eth_type = ETHERTYPE_IP;
|
|
break;
|
|
|
|
case 0x57:
|
|
eth_type = ETHERTYPE_IPV6;
|
|
break;
|
|
|
|
default:
|
|
incStats(ingressPacket, h->ts.tv_sec, ETHERTYPE_IP,
|
|
NDPI_PROTOCOL_UNKNOWN, rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */);
|
|
goto dissect_packet_end;
|
|
}
|
|
goto decode_packet_eth;
|
|
break;
|
|
|
|
case ETHERTYPE_IP:
|
|
if(h->caplen >= ip_offset + sizeof(struct ndpi_iphdr)) {
|
|
u_int16_t frag_off;
|
|
struct ndpi_iphdr *iph = (struct ndpi_iphdr *)&packet[ip_offset];
|
|
u_short ip_len = ((u_short)iph->ihl * 4);
|
|
struct ndpi_ipv6hdr *ip6 = NULL;
|
|
|
|
if(iph->version != 4) {
|
|
/* This is not IPv4 */
|
|
incStats(ingressPacket, h->ts.tv_sec, ETHERTYPE_IP,
|
|
NDPI_PROTOCOL_UNKNOWN, rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */);
|
|
goto dissect_packet_end;
|
|
} else
|
|
frag_off = ntohs(iph->frag_off);
|
|
|
|
if(ntop->getGlobals()->decode_tunnels() && (iph->protocol == IPPROTO_GRE)
|
|
&& ((frag_off & 0x3FFF /* IP_MF | IP_OFFSET */ ) == 0)
|
|
&& h->caplen >= ip_offset + ip_len + sizeof(struct grev1_header)) {
|
|
struct grev1_header gre;
|
|
u_int offset = ip_offset + ip_len + sizeof(struct grev1_header);
|
|
|
|
memcpy(&gre, &packet[ip_offset+ip_len], sizeof(struct grev1_header));
|
|
gre.flags_and_version = ntohs(gre.flags_and_version);
|
|
gre.proto = ntohs(gre.proto);
|
|
|
|
if(gre.flags_and_version & (GRE_HEADER_CHECKSUM | GRE_HEADER_ROUTING)) offset += 4;
|
|
if(gre.flags_and_version & GRE_HEADER_KEY) offset += 4;
|
|
if(gre.flags_and_version & GRE_HEADER_SEQ_NUM) offset += 4;
|
|
|
|
if(h->caplen >= offset) {
|
|
if(gre.proto == 0x6558 /* Transparent Ethernet Bridging */) {
|
|
eth_offset = offset;
|
|
goto datalink_check;
|
|
} else if(gre.proto == ETHERTYPE_IP) {
|
|
ip_offset = offset;
|
|
goto decode_packet_eth;
|
|
} else if(gre.proto == ETHERTYPE_IPV6) {
|
|
eth_type = ETHERTYPE_IPV6;
|
|
ip_offset = offset;
|
|
goto decode_packet_eth;
|
|
}
|
|
}
|
|
|
|
/* ERSPAN Type 2 has an 8-byte header
|
|
https://tools.ietf.org/html/draft-foschiano-erspan-00 */
|
|
if(h->caplen >= offset + sizeof(struct ndpi_ethhdr) + 8) {
|
|
if(gre.proto == ETH_P_ERSPAN) {
|
|
offset += 8 /* ERSPAN Type 2 header */;
|
|
eth_offset = offset;
|
|
ethernet = (struct ndpi_ethhdr *)&packet[eth_offset];
|
|
ip_offset = eth_offset + sizeof(struct ndpi_ethhdr);
|
|
eth_type = ntohs(ethernet->h_proto);
|
|
goto decode_packet_eth;
|
|
} else if(gre.proto == ETH_P_ERSPAN2) {
|
|
; /* TODO: support ERSPAN Type 3 */
|
|
} else {
|
|
/* Unknown encapsulation */
|
|
}
|
|
}
|
|
|
|
} else if(ntop->getGlobals()->decode_tunnels() && (iph->protocol == IPPROTO_UDP)
|
|
&& ((frag_off & 0x3FFF /* IP_MF | IP_OFFSET */ ) == 0)) {
|
|
struct ndpi_udphdr *udp = (struct ndpi_udphdr *)&packet[ip_offset+ip_len];
|
|
u_int16_t sport = ntohs(udp->source), dport = ntohs(udp->dest);
|
|
|
|
if((sport == GTP_U_V1_PORT) || (dport == GTP_U_V1_PORT)) {
|
|
/* Check if it's GTPv1 */
|
|
u_int offset = (u_int)(ip_offset+ip_len+sizeof(struct ndpi_udphdr));
|
|
u_int8_t flags = packet[offset];
|
|
u_int8_t message_type = packet[offset+1];
|
|
|
|
if((((flags & 0xE0) >> 5) == 1 /* GTPv1 */) && (message_type == 0xFF /* T-PDU */)) {
|
|
ip_offset = ip_offset+ip_len+sizeof(struct ndpi_udphdr)+8 /* GTPv1 header len */;
|
|
|
|
if(flags & 0x04) ip_offset += 1; /* next_ext_header is present */
|
|
if(flags & 0x02) ip_offset += 4; /* sequence_number is present (it also includes next_ext_header and pdu_number) */
|
|
if(flags & 0x01) ip_offset += 1; /* pdu_number is present */
|
|
|
|
iph = (struct ndpi_iphdr *) &packet[ip_offset];
|
|
|
|
if(iph->version != 4) {
|
|
/* FIX - Add IPv6 support */
|
|
incStats(ingressPacket, h->ts.tv_sec, ETHERTYPE_IPV6,
|
|
NDPI_PROTOCOL_UNKNOWN, rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */);
|
|
goto dissect_packet_end;
|
|
}
|
|
}
|
|
} else if((sport == TZSP_PORT) || (dport == TZSP_PORT)) {
|
|
/* https://en.wikipedia.org/wiki/TZSP */
|
|
u_int offset = ip_offset+ip_len+sizeof(struct ndpi_udphdr);
|
|
u_int8_t version = packet[offset];
|
|
u_int8_t type = packet[offset+1];
|
|
u_int16_t encapsulates = ntohs(*((u_int16_t*)&packet[offset+2]));
|
|
|
|
if((version == 1) && (type == 0) && (encapsulates == 1)) {
|
|
u_int8_t stop = 0;
|
|
|
|
offset += 4;
|
|
|
|
while((!stop) && (offset < h->caplen)) {
|
|
u_int8_t tag_type = packet[offset];
|
|
u_int8_t tag_len;
|
|
|
|
switch(tag_type) {
|
|
case 0: /* PADDING Tag */
|
|
tag_len = 1;
|
|
break;
|
|
case 1: /* END Tag */
|
|
tag_len = 1, stop = 1;
|
|
break;
|
|
default:
|
|
tag_len = packet[offset+1];
|
|
break;
|
|
}
|
|
|
|
offset += tag_len;
|
|
|
|
if(offset >= h->caplen) {
|
|
incStats(ingressPacket, h->ts.tv_sec, ETHERTYPE_IPV6, NDPI_PROTOCOL_UNKNOWN,
|
|
rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */);
|
|
goto dissect_packet_end;
|
|
} else {
|
|
eth_offset = offset;
|
|
goto datalink_check;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if((sport == CAPWAP_DATA_PORT) || (dport == CAPWAP_DATA_PORT)) {
|
|
/*
|
|
Control And Provisioning of Wireless Access Points
|
|
|
|
https://www.rfc-editor.org/rfc/rfc5415.txt
|
|
|
|
CAPWAP Header - variable length (5 MSB of byte 2 of header)
|
|
IEEE 802.11 Data Flags - 24 bytes
|
|
Logical-Link Control - 8 bytes
|
|
|
|
Total = CAPWAP_header_length + 24 + 8
|
|
*/
|
|
u_short eth_type;
|
|
ip_offset = ip_offset+ip_len+sizeof(struct ndpi_udphdr);
|
|
u_int8_t capwap_header_len = ((*(u_int8_t*)&packet[ip_offset+1])>>3)*4;
|
|
ip_offset = ip_offset+capwap_header_len+24+8;
|
|
|
|
if(ip_offset >= h->len) {
|
|
incStats(ingressPacket, h->ts.tv_sec, 0, NDPI_PROTOCOL_UNKNOWN,
|
|
rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */);
|
|
goto dissect_packet_end;
|
|
}
|
|
eth_type = ntohs(*(u_int16_t*)&packet[ip_offset-2]);
|
|
|
|
switch(eth_type) {
|
|
case ETHERTYPE_IP:
|
|
iph = (struct ndpi_iphdr *) &packet[ip_offset];
|
|
break;
|
|
case ETHERTYPE_IPV6:
|
|
iph = NULL;
|
|
ip6 = (struct ndpi_ipv6hdr*)&packet[ip_offset];
|
|
break;
|
|
default:
|
|
incStats(ingressPacket, h->ts.tv_sec, 0, NDPI_PROTOCOL_UNKNOWN,
|
|
rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */);
|
|
goto dissect_packet_end;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(vlan_id && ntop->getPrefs()->do_ignore_vlans())
|
|
vlan_id = 0;
|
|
if((vlan_id == 0) && ntop->getPrefs()->do_simulate_vlans())
|
|
vlan_id = (ip6 ? ip6->ip6_src.u6_addr.u6_addr8[15] +
|
|
ip6->ip6_dst.u6_addr.u6_addr8[15] : iph->saddr + iph->daddr) % 0xFF;
|
|
|
|
if(ntop->getPrefs()->do_ignore_macs())
|
|
ethernet = &dummy_ethernet;
|
|
|
|
try {
|
|
PROFILING_SECTION_ENTER("NetworkInterface::dissectPacket: processPacket", 0);
|
|
pass_verdict = processPacket(bridge_iface_idx,
|
|
ingressPacket, &h->ts, time,
|
|
ethernet,
|
|
vlan_id, iph,
|
|
ip6, h->caplen - ip_offset, rawsize,
|
|
h, packet, ndpiProtocol, srcHost, dstHost, flow);
|
|
PROFILING_SECTION_EXIT(0);
|
|
} catch(std::bad_alloc& ba) {
|
|
static bool oom_warning_sent = false;
|
|
|
|
if(!oom_warning_sent) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Not enough memory");
|
|
oom_warning_sent = true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ETHERTYPE_IPV6:
|
|
if(h->caplen >= ip_offset + sizeof(struct ndpi_ipv6hdr)) {
|
|
struct ndpi_iphdr *iph = NULL;
|
|
struct ndpi_ipv6hdr *ip6 = (struct ndpi_ipv6hdr*)&packet[ip_offset];
|
|
|
|
if((ntohl(ip6->ip6_hdr.ip6_un1_flow) & 0xF0000000) != 0x60000000) {
|
|
/* This is not IPv6 */
|
|
incStats(ingressPacket, h->ts.tv_sec, ETHERTYPE_IPV6, NDPI_PROTOCOL_UNKNOWN,
|
|
rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */);
|
|
goto dissect_packet_end;
|
|
} else {
|
|
u_int ipv6_shift = sizeof(const struct ndpi_ipv6hdr);
|
|
u_int8_t l4_proto = ip6->ip6_hdr.ip6_un1_nxt;
|
|
|
|
if(l4_proto == 0x3C /* IPv6 destination option */) {
|
|
u_int8_t *options = (u_int8_t*)ip6 + ipv6_shift;
|
|
l4_proto = options[0];
|
|
ipv6_shift = 8 * (options[1] + 1);
|
|
}
|
|
|
|
if(ntop->getGlobals()->decode_tunnels() && (l4_proto == IPPROTO_GRE)
|
|
&& h->caplen >= ip_offset + ipv6_shift + sizeof(struct grev1_header)) {
|
|
struct grev1_header gre;
|
|
u_int offset = ip_offset + ipv6_shift + sizeof(struct grev1_header);
|
|
|
|
memcpy(&gre, &packet[ip_offset + ipv6_shift], sizeof(struct grev1_header));
|
|
gre.flags_and_version = ntohs(gre.flags_and_version);
|
|
gre.proto = ntohs(gre.proto);
|
|
|
|
if(gre.flags_and_version & (GRE_HEADER_CHECKSUM | GRE_HEADER_ROUTING)) offset += 4;
|
|
if(gre.flags_and_version & GRE_HEADER_KEY) offset += 4;
|
|
if(gre.flags_and_version & GRE_HEADER_SEQ_NUM) offset += 4;
|
|
|
|
if(h->caplen >= offset) {
|
|
if(gre.proto == ETHERTYPE_IP) {
|
|
eth_type = ETHERTYPE_IP;
|
|
ip_offset = offset;
|
|
goto decode_packet_eth;
|
|
} else if(gre.proto == ETHERTYPE_IPV6) {
|
|
ip_offset = offset;
|
|
goto decode_packet_eth;
|
|
}
|
|
}
|
|
|
|
if(h->caplen >= offset + sizeof(struct ndpi_ethhdr) + 8 /* ERSPAN Type 2 header */) {
|
|
if(gre.proto == ETH_P_ERSPAN) {
|
|
offset += 8;
|
|
eth_offset = offset;
|
|
ethernet = (struct ndpi_ethhdr *)&packet[eth_offset];
|
|
ip_offset = eth_offset + sizeof(struct ndpi_ethhdr);
|
|
eth_type = ntohs(ethernet->h_proto);
|
|
goto decode_packet_eth;
|
|
} else if(gre.proto == ETH_P_ERSPAN2) {
|
|
; /* TODO: support ERSPAN Type 3 */
|
|
} else {
|
|
/* Unknown encapsulation */
|
|
}
|
|
}
|
|
|
|
} else if(ntop->getGlobals()->decode_tunnels() && (l4_proto == IPPROTO_UDP)) {
|
|
// ip_offset += ipv6_shift;
|
|
if((ip_offset + ipv6_shift) >= h->len) {
|
|
incStats(ingressPacket, h->ts.tv_sec, ETHERTYPE_IPV6, NDPI_PROTOCOL_UNKNOWN,
|
|
rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */);
|
|
goto dissect_packet_end;
|
|
}
|
|
|
|
struct ndpi_udphdr *udp = (struct ndpi_udphdr *)&packet[ip_offset + ipv6_shift];
|
|
u_int16_t sport = udp->source, dport = udp->dest;
|
|
|
|
if((sport == CAPWAP_DATA_PORT) || (dport == CAPWAP_DATA_PORT)) {
|
|
/*
|
|
Control And Provisioning of Wireless Access Points
|
|
|
|
https://www.rfc-editor.org/rfc/rfc5415.txt
|
|
|
|
CAPWAP Header - variable length (5 MSB of byte 2 of header)
|
|
IEEE 802.11 Data Flags - 24 bytes
|
|
Logical-Link Control - 8 bytes
|
|
|
|
Total = CAPWAP_header_length + 24 + 8
|
|
*/
|
|
|
|
u_short eth_type;
|
|
ip_offset = ip_offset+ipv6_shift+sizeof(struct ndpi_udphdr);
|
|
u_int8_t capwap_header_len = ((*(u_int8_t*)&packet[ip_offset+1])>>3)*4;
|
|
ip_offset = ip_offset+capwap_header_len+24+8;
|
|
|
|
if(ip_offset >= h->len) {
|
|
incStats(ingressPacket, h->ts.tv_sec, 0, NDPI_PROTOCOL_UNKNOWN,
|
|
rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */);
|
|
goto dissect_packet_end;
|
|
}
|
|
eth_type = ntohs(*(u_int16_t*)&packet[ip_offset-2]);
|
|
|
|
switch(eth_type) {
|
|
case ETHERTYPE_IP:
|
|
iph = (struct ndpi_iphdr *) &packet[ip_offset];
|
|
ip6 = NULL;
|
|
break;
|
|
case ETHERTYPE_IPV6:
|
|
ip6 = (struct ndpi_ipv6hdr*)&packet[ip_offset];
|
|
break;
|
|
default:
|
|
incStats(ingressPacket, h->ts.tv_sec, 0, NDPI_PROTOCOL_UNKNOWN,
|
|
rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */);
|
|
goto dissect_packet_end;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(vlan_id && ntop->getPrefs()->do_ignore_vlans())
|
|
vlan_id = 0;
|
|
if((vlan_id == 0) && ntop->getPrefs()->do_simulate_vlans())
|
|
vlan_id = (ip6 ? ip6->ip6_src.u6_addr.u6_addr8[15] + ip6->ip6_dst.u6_addr.u6_addr8[15] : iph->saddr + iph->daddr) % 0xFF;
|
|
|
|
if(ntop->getPrefs()->do_ignore_macs())
|
|
ethernet = &dummy_ethernet;
|
|
|
|
try {
|
|
PROFILING_SECTION_ENTER("NetworkInterface::dissectPacket: processPacket", 0);
|
|
pass_verdict = processPacket(bridge_iface_idx,
|
|
ingressPacket, &h->ts, time,
|
|
ethernet,
|
|
vlan_id,
|
|
iph, ip6, h->len - ip_offset, rawsize,
|
|
h, packet, ndpiProtocol, srcHost, dstHost, flow);
|
|
PROFILING_SECTION_EXIT(0);
|
|
} catch(std::bad_alloc& ba) {
|
|
static bool oom_warning_sent = false;
|
|
|
|
if(!oom_warning_sent) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Not enough memory");
|
|
oom_warning_sent = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default: /* No IPv4 nor IPv6 */
|
|
if(ntop->getPrefs()->do_ignore_macs())
|
|
ethernet = &dummy_ethernet;
|
|
|
|
Mac *srcMac = getMac(ethernet->h_source, true);
|
|
Mac *dstMac = getMac(ethernet->h_dest, true);
|
|
|
|
/* NOTE: in nEdge, stats are updated into Flow::update_hosts_stats */
|
|
#ifndef HAVE_NEDGE
|
|
if(srcMac) srcMac->incSentStats(h->ts.tv_sec, 1, rawsize);
|
|
if(dstMac) dstMac->incRcvdStats(h->ts.tv_sec, 1, rawsize);
|
|
#endif
|
|
|
|
if(srcMac && dstMac && (!srcMac->isNull() || !dstMac->isNull())) {
|
|
setSeenMacAddresses();
|
|
srcMac->setSourceMac();
|
|
|
|
if((eth_type == ETHERTYPE_ARP) && (h->caplen >= (sizeof(arp_header)+sizeof(struct ndpi_ethhdr)))) {
|
|
struct arp_header *arpp = (struct arp_header*)&packet[ip_offset];
|
|
u_int16_t arp_opcode = ntohs(arpp->ar_op);
|
|
bool src2dst_element = false;
|
|
ArpStatsMatrixElement* e;
|
|
|
|
u_int32_t src = ntohl(arpp->arp_spa);
|
|
u_int32_t dst = ntohl(arpp->arp_tpa);
|
|
u_int32_t net = src & dst;
|
|
u_int32_t diff;
|
|
IpAddress cur_bcast_domain;
|
|
|
|
if(src > dst) {
|
|
u_int32_t r = src;
|
|
src = dst;
|
|
dst = r;
|
|
}
|
|
|
|
diff = dst - src;
|
|
|
|
if(diff && (src & 0xFFFF0000) != 0xA9FE0000 && (dst & 0xFFFF0000) != 0xA9FE0000) {
|
|
u_int32_t cur_mask;
|
|
u_int8_t cur_cidr;
|
|
|
|
for(cur_mask = 0xFFFFFFF0, cur_cidr = 28; cur_mask > 0x00000000; cur_mask <<= 1, cur_cidr--) {
|
|
if((diff & cur_mask) == 0) { /* diff < cur_mask */
|
|
net &= cur_mask;
|
|
|
|
if((src & cur_mask) != (dst & cur_mask)) {
|
|
cur_mask <<= 1, cur_cidr -= 1;
|
|
net = src & cur_mask;
|
|
}
|
|
|
|
cur_bcast_domain.set(htonl(net));
|
|
|
|
if(!checkBroadcastDomainTooLarge(cur_mask, vlan_id, srcMac, dstMac, src, dst)
|
|
&& !bcast_domains->isLocalBroadcastDomain(&cur_bcast_domain, cur_cidr, true /* Inline call */)) {
|
|
bcast_domains->inlineAddAddress(&cur_bcast_domain, cur_cidr);
|
|
|
|
#ifdef BROADCAST_DOMAINS_DEBUG
|
|
char buf1[32], buf2[32], buf3[32];
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "%s <-> %s [%s - %u]",
|
|
Utils::intoaV4(src, buf1, sizeof(buf1)),
|
|
Utils::intoaV4(dst, buf2, sizeof(buf2)),
|
|
Utils::intoaV4(net, buf3, sizeof(buf3)),
|
|
cur_cidr);
|
|
#endif
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
e = getArpHashMatrixElement(srcMac->get_mac(), dstMac->get_mac(), &src2dst_element);
|
|
|
|
if(arp_opcode == 0x1 /* ARP request */) {
|
|
arp_requests++;
|
|
srcMac->incSentArpRequests();
|
|
dstMac->incRcvdArpRequests();
|
|
|
|
if(e) e->incArpRequests(src2dst_element);
|
|
} else if(arp_opcode == 0x2 /* ARP reply */) {
|
|
arp_replies++;
|
|
srcMac->incSentArpReplies();
|
|
dstMac->incRcvdArpReplies();
|
|
if(e) e->incArpReplies(src2dst_element);
|
|
|
|
checkMacIPAssociation(true, arpp->arp_sha, arpp->arp_spa);
|
|
checkMacIPAssociation(true, arpp->arp_tha, arpp->arp_tpa);
|
|
}
|
|
}
|
|
}
|
|
|
|
incStats(ingressPacket, h->ts.tv_sec, eth_type, NDPI_PROTOCOL_UNKNOWN,
|
|
rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */);
|
|
break;
|
|
}
|
|
|
|
dissect_packet_end:
|
|
|
|
/* Live packet dump to mongoose */
|
|
if(num_live_captures > 0)
|
|
deliverLiveCapture(h, packet, *flow);
|
|
|
|
return(pass_verdict);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::pollQueuedeBPFEvents() {
|
|
#ifdef HAVE_EBPF
|
|
if(ebpfEvents) {
|
|
eBPFevent *event;
|
|
|
|
if(dequeueeBPFEvent(&event)) {
|
|
Flow *flow = NULL;
|
|
IpAddress src, dst;
|
|
bool src2dst_direction, new_flow;
|
|
u_int16_t proto, sport, dport;
|
|
|
|
if(event->ip_version == 4) {
|
|
src.set(event->event.v4.saddr), dst.set(event->event.v4.daddr),
|
|
sport = event->sport, dport = event->dport,
|
|
proto = event->proto;
|
|
} else {
|
|
src.set((struct ndpi_in6_addr*)&event->event.v6.saddr),
|
|
dst.set((struct ndpi_in6_addr*)&event->event.v6.daddr),
|
|
sport = event->sport, dport = event->dport,
|
|
proto = event->proto;
|
|
}
|
|
|
|
sport = htons(sport), dport = htons(dport);
|
|
|
|
flow = getFlow(NULL /* srcMac */, NULL /* dstMac */,
|
|
0 /* vlan_id */,
|
|
0 /* deviceIP */,
|
|
0 /* inIndex */, 1 /* outIndex */,
|
|
NULL /* ICMPinfo */,
|
|
&src, &dst,
|
|
sport, dport,
|
|
proto,
|
|
&src2dst_direction,
|
|
0, 0, 0, &new_flow,
|
|
true /* create_if_missing */);
|
|
|
|
|
|
if(flow) flow->setProcessInfo(event, src2dst_direction ? event->sent_packet : !event->sent_packet);
|
|
|
|
#ifdef EBPF_DEBUG
|
|
// ntop->getTrace()->traceEvent(TRACE_NORMAL, "[new flow: %u][src2dst_direction: %u]", new_flow ? 1 : 0, src2dst_direction ? 1 : 0);
|
|
|
|
if(event->ip_version == 4)
|
|
IPV4Handler(flow, event);
|
|
else
|
|
IPV6Handler(flow, event);
|
|
#endif
|
|
|
|
ebpf_free_event(event);
|
|
free(event);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::reloadCustomCategories() {
|
|
if(customCategoriesReloadRequested()) {
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG, "Going to reload categories [iface: %s]", get_name());
|
|
ndpi_enable_loaded_categories(ndpi_struct);
|
|
reload_custom_categories = false;
|
|
reload_hosts_blacklist = true;
|
|
}
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::startPacketPolling() {
|
|
if((cpu_affinity != -1) && (ntop->getNumCPUs() > 1)) {
|
|
if(Utils::setThreadAffinity(pollLoop, cpu_affinity))
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Couldn't set affinity of interface %s to core %d",
|
|
get_name(), cpu_affinity);
|
|
else
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Setting affinity of interface %s to core %d",
|
|
get_name(), cpu_affinity);
|
|
}
|
|
|
|
#ifdef __linux__
|
|
pthread_setname_np(pollLoop, get_name());
|
|
#endif
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Started packet polling on interface %s [id: %u]...",
|
|
get_name(), get_id());
|
|
|
|
running = true;
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::shutdown() {
|
|
running = false;
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::cleanup() {
|
|
next_idle_flow_purge = next_idle_host_purge = 0;
|
|
cpu_affinity = -1,
|
|
has_vlan_packets = false, has_ebpf_events = false, has_mac_addresses = false;
|
|
running = false, inline_interface = false;
|
|
|
|
getStats()->cleanup();
|
|
flows_hash->cleanup();
|
|
hosts_hash->cleanup();
|
|
ases_hash->cleanup();
|
|
countries_hash->cleanup();
|
|
vlans_hash->cleanup();
|
|
macs_hash->cleanup();
|
|
if(arp_hash_matrix) arp_hash_matrix->cleanup();
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Cleanup interface %s", get_name());
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::findFlowHosts(u_int16_t vlanId,
|
|
Mac *src_mac, IpAddress *_src_ip, Host **src,
|
|
Mac *dst_mac, IpAddress *_dst_ip, Host **dst) {
|
|
int16_t local_network_id;
|
|
|
|
PROFILING_SECTION_ENTER("NetworkInterface::findFlowHosts: hosts_hash->get", 8);
|
|
/* Do not look on sub interfaces, Flows are always created in the same interface of its hosts */
|
|
(*src) = hosts_hash->get(vlanId, _src_ip);
|
|
PROFILING_SECTION_EXIT(8);
|
|
|
|
if((*src) == NULL) {
|
|
if(!hosts_hash->hasEmptyRoom()) {
|
|
*src = *dst = NULL;
|
|
has_too_many_hosts = true;
|
|
return;
|
|
}
|
|
|
|
if(_src_ip && (_src_ip->isLocalHost(&local_network_id) || _src_ip->isLocalInterfaceAddress())) {
|
|
PROFILING_SECTION_ENTER("NetworkInterface::findFlowHosts: new LocalHost", 9);
|
|
(*src) = new LocalHost(this, src_mac, vlanId, _src_ip);
|
|
PROFILING_SECTION_EXIT(9);
|
|
} else {
|
|
PROFILING_SECTION_ENTER("NetworkInterface::findFlowHosts: new RemoteHost", 10);
|
|
(*src) = new RemoteHost(this, src_mac, vlanId, _src_ip);
|
|
PROFILING_SECTION_EXIT(10);
|
|
}
|
|
|
|
if(!hosts_hash->add(*src)) {
|
|
//ntop->getTrace()->traceEvent(TRACE_WARNING, "Too many hosts in interface %s", ifname);
|
|
delete *src;
|
|
*src = *dst = NULL;
|
|
has_too_many_hosts = true;
|
|
return;
|
|
}
|
|
|
|
(*src)->postHashAdd();
|
|
has_too_many_hosts = false;
|
|
|
|
}
|
|
|
|
/* ***************************** */
|
|
|
|
PROFILING_SECTION_ENTER("NetworkInterface::findFlowHosts: hosts_hash->get", 8);
|
|
(*dst) = hosts_hash->get(vlanId, _dst_ip);
|
|
PROFILING_SECTION_EXIT(8);
|
|
|
|
if((*dst) == NULL) {
|
|
if(!hosts_hash->hasEmptyRoom()) {
|
|
*dst = NULL;
|
|
has_too_many_hosts = true;
|
|
return;
|
|
}
|
|
|
|
if(_dst_ip
|
|
&& (_dst_ip->isLocalHost(&local_network_id)
|
|
|| _dst_ip->isLocalInterfaceAddress())) {
|
|
PROFILING_SECTION_ENTER("NetworkInterface::findFlowHosts: new LocalHost", 9);
|
|
(*dst) = new LocalHost(this, dst_mac, vlanId, _dst_ip);
|
|
PROFILING_SECTION_EXIT(9);
|
|
} else {
|
|
PROFILING_SECTION_ENTER("NetworkInterface::findFlowHosts: new RemoteHost", 10);
|
|
(*dst) = new RemoteHost(this, dst_mac, vlanId, _dst_ip);
|
|
PROFILING_SECTION_EXIT(10);
|
|
}
|
|
|
|
if(!hosts_hash->add(*dst)) {
|
|
// ntop->getTrace()->traceEvent(TRACE_WARNING, "Too many hosts in interface %s", ifname);
|
|
delete *dst;
|
|
*dst = NULL;
|
|
has_too_many_hosts = true;
|
|
return;
|
|
}
|
|
|
|
(*dst)->postHashAdd();
|
|
has_too_many_hosts = false;
|
|
|
|
}
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
struct ndpiStatsRetrieverData {
|
|
nDPIStats *stats;
|
|
Host *host;
|
|
};
|
|
|
|
static bool flow_sum_protos(GenericHashEntry *flow, void *user_data, bool *matched) {
|
|
ndpiStatsRetrieverData *retriever = (ndpiStatsRetrieverData*)user_data;
|
|
nDPIStats *stats = retriever->stats;
|
|
Flow *f = (Flow*)flow;
|
|
|
|
if(retriever->host
|
|
&& (retriever->host != f->get_cli_host())
|
|
&& (retriever->host != f->get_srv_host()))
|
|
return(false); /* false = keep on walking */
|
|
|
|
f->sumStats(stats);
|
|
*matched = true;
|
|
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::getnDPIStats(nDPIStats *stats, AddressTree *allowed_hosts,
|
|
const char *host_ip, u_int16_t vlan_id) {
|
|
ndpiStatsRetrieverData retriever;
|
|
Host *h = NULL;
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
if(host_ip)
|
|
h = findHostByIP(allowed_hosts, (char *)host_ip, vlan_id);
|
|
|
|
retriever.stats = stats;
|
|
retriever.host = h;
|
|
walker(&begin_slot, walk_all, walker_flows, flow_sum_protos, (void*)&retriever);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
static bool flow_update_hosts_stats(GenericHashEntry *node,
|
|
void *user_data, bool *matched) {
|
|
Flow *flow = (Flow*)node;
|
|
struct timeval *tv = (struct timeval*)user_data;
|
|
bool dump_alert = ((time(NULL) - tv->tv_sec) < ntop->getPrefs()->get_housekeeping_frequency()) ? true : false;
|
|
|
|
if(ntop->getGlobals()->isShutdownRequested() && !ntop->getPrefs()->flushFlowsOnShutdown())
|
|
return(true); /* true = stop walking */
|
|
|
|
flow->update_hosts_stats(tv, dump_alert);
|
|
*matched = true;
|
|
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
static bool update_hosts_stats(GenericHashEntry *node, void *user_data, bool *matched) {
|
|
Host *host = (Host*)node;
|
|
struct timeval *tv = (struct timeval*)user_data;
|
|
|
|
host->updateStats(tv);
|
|
*matched = true;
|
|
|
|
/*
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Updated: %s [%d]",
|
|
((StringHost*)node)->host_key(),
|
|
host->getThptTrend());
|
|
*/
|
|
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
static bool update_ases_stats(GenericHashEntry *node, void *user_data, bool *matched) {
|
|
AutonomousSystem *as = (AutonomousSystem*)node;
|
|
struct timeval *tv = (struct timeval*)user_data;
|
|
|
|
as->updateStats(tv);
|
|
*matched = true;
|
|
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
static bool update_vlans_stats(GenericHashEntry *node, void *user_data, bool *matched) {
|
|
Vlan *vl = (Vlan*)node;
|
|
struct timeval *tv = (struct timeval*)user_data;
|
|
|
|
vl->updateStats(tv);
|
|
*matched = true;
|
|
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
static bool update_macs_stats(GenericHashEntry *node, void *user_data, bool *matched) {
|
|
Mac *mac = (Mac*)node;
|
|
struct timeval *tv = (struct timeval*)user_data;
|
|
|
|
mac->updateStats(tv);
|
|
*matched = true;
|
|
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
// #define PERIODIC_STATS_UPDATE_DEBUG_TIMING
|
|
|
|
void NetworkInterface::periodicStatsUpdate() {
|
|
struct timeval tv;
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
#ifdef PERIODIC_STATS_UPDATE_DEBUG_TIMING
|
|
struct timeval tdebug;
|
|
#endif
|
|
|
|
if(isView()) return;
|
|
|
|
if(!read_from_pcap_dump())
|
|
gettimeofday(&tv, NULL);
|
|
else
|
|
tv.tv_sec = last_pkt_rcvd, tv.tv_usec = 0;
|
|
|
|
#ifdef NTOPNG_PRO
|
|
if(getHostPools()) getHostPools()->checkPoolsStatsReset();
|
|
#endif
|
|
|
|
#ifdef PERIODIC_STATS_UPDATE_DEBUG_TIMING
|
|
gettimeofday(&tdebug, NULL);
|
|
#endif
|
|
|
|
flows_hash->walk(&begin_slot, walk_all, flow_update_hosts_stats, (void*)&tv);
|
|
topItemsCommit(&tv);
|
|
|
|
#ifdef PERIODIC_STATS_UPDATE_DEBUG_TIMING
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "flows_hash->walk took %d seconds", time(NULL) - tdebug.tv_sec);
|
|
gettimeofday(&tdebug, NULL);
|
|
#endif
|
|
|
|
if(ntop->getGlobals()->isShutdownRequested())
|
|
return;
|
|
|
|
// if drop alerts enabled and have some significant packets
|
|
if((packet_drops_alert_perc > 0) && (getNumPacketsSinceReset() > 100)) {
|
|
float drop_perc = getNumPacketDropsSinceReset() * 100.f
|
|
/ (getNumPacketDropsSinceReset() + getNumPacketsSinceReset());
|
|
too_many_drops = (drop_perc >= packet_drops_alert_perc) ? true : false;
|
|
} else
|
|
too_many_drops = false;
|
|
|
|
#ifdef NTOPNG_PRO
|
|
if(aggregated_flows_hash) {
|
|
if((getIfType() == interface_type_DUMMY) || (--nextFlowAggregation == 0)) {
|
|
/* Start over */
|
|
aggregated_flows_hash->cleanup();
|
|
nextFlowAggregation = FLOW_AGGREGATION_DURATION;
|
|
|
|
#ifdef AGGREGATED_FLOW_DEBUG
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Aggregated flows exported. "
|
|
"Aggregated flows hash cleared. [num_items: %i]",
|
|
aggregated_flows_hash->getCurrentSize());
|
|
#endif
|
|
} else
|
|
#ifdef AGGREGATED_FLOW_DEBUG
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Aggregation in %i housekeeping cycles [housekeeping frequency: %i] [inter-aggregation housekeeping cycles: %i] ",
|
|
nextFlowAggregation, ntop->getPrefs()->get_housekeeping_frequency(), FLOW_AGGREGATION_DURATION,
|
|
aggregated_flows_hash->getCurrentSize());
|
|
#endif
|
|
;
|
|
}
|
|
#endif
|
|
|
|
#ifdef PERIODIC_STATS_UPDATE_DEBUG_TIMING
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "flows aggregation took %d seconds", time(NULL) - tdebug.tv_sec);
|
|
gettimeofday(&tdebug, NULL);
|
|
#endif
|
|
|
|
checkReloadHostsBroadcastDomain();
|
|
|
|
begin_slot = 0;
|
|
hosts_hash->walk(&begin_slot, walk_all, update_hosts_stats, (void*)&tv);
|
|
|
|
#ifdef PERIODIC_STATS_UPDATE_DEBUG_TIMING
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "hosts_hash->walk took %d seconds", time(NULL) - tdebug.tv_sec);
|
|
gettimeofday(&tdebug, NULL);
|
|
#endif
|
|
|
|
begin_slot = 0;
|
|
ases_hash->walk(&begin_slot, walk_all, update_ases_stats, (void*)&tv);
|
|
|
|
if(hasSeenVlanTaggedPackets()) {
|
|
begin_slot = 0;
|
|
vlans_hash->walk(&begin_slot, walk_all, update_vlans_stats, (void*)&tv);
|
|
}
|
|
|
|
begin_slot = 0;
|
|
macs_hash->walk(&begin_slot, walk_all, update_macs_stats, (void*)&tv);
|
|
|
|
#ifdef PERIODIC_STATS_UPDATE_DEBUG_TIMING
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "asn/macs/vlan->walk took %d seconds", time(NULL) - tdebug.tv_sec);
|
|
gettimeofday(&tdebug, NULL);
|
|
#endif
|
|
|
|
if(ntop->getGlobals()->isShutdownRequested())
|
|
return;
|
|
|
|
if(db) {
|
|
db->updateStats(&tv);
|
|
db->flush();
|
|
}
|
|
|
|
#ifdef PERIODIC_STATS_UPDATE_DEBUG_TIMING
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "MySQL dump took %d seconds", time(NULL) - tdebug.tv_sec);
|
|
gettimeofday(&tdebug, NULL);
|
|
#endif
|
|
|
|
#ifdef NTOPNG_PRO
|
|
if(host_pools)
|
|
host_pools->updateStats(&tv);
|
|
#endif
|
|
|
|
if(!ts_ring && TimeseriesRing::isRingEnabled(ntop->getPrefs()))
|
|
ts_ring = new TimeseriesRing(this);
|
|
|
|
if(ts_ring && ts_ring->isTimeToInsert()) {
|
|
NetworkInterfaceTsPoint *pt = new NetworkInterfaceTsPoint();
|
|
makeTsPoint(pt);
|
|
|
|
/* Ownership of the point is passed to the ring */
|
|
ts_ring->insert(pt, tv.tv_sec);
|
|
}
|
|
|
|
#ifdef PERIODIC_STATS_UPDATE_DEBUG_TIMING
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Timeseries update took %d seconds", time(NULL) - tdebug.tv_sec);
|
|
gettimeofday(&tdebug, NULL);
|
|
#endif
|
|
|
|
if((!read_from_pcap_dump()) &&
|
|
(time(NULL) - tv.tv_sec) > ntop->getPrefs()->get_housekeeping_frequency())
|
|
slow_stats_update = true;
|
|
else
|
|
slow_stats_update = false;
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
struct update_host_pool_l7policy {
|
|
bool update_pool_id;
|
|
bool update_l7policy;
|
|
};
|
|
|
|
static bool update_host_host_pool_l7policy(GenericHashEntry *node, void *user_data, bool *matched) {
|
|
Host *h = (Host*)node;
|
|
update_host_pool_l7policy *up = (update_host_pool_l7policy*)user_data;
|
|
#ifdef HOST_POOLS_DEBUG
|
|
char buf[128];
|
|
u_int16_t cur_pool_id = h->get_host_pool();
|
|
#endif
|
|
|
|
*matched = true;
|
|
|
|
if(up->update_pool_id)
|
|
h->updateHostPool(false /* Not inline with traffic processing */);
|
|
|
|
#ifdef NTOPNG_PRO
|
|
if(up->update_l7policy)
|
|
h->resetBlockedTrafficStatus();
|
|
#endif
|
|
|
|
#ifdef HOST_POOLS_DEBUG
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Going to refresh pool for %s "
|
|
"[refresh pool id: %i] "
|
|
"[refresh l7policy: %i] "
|
|
"[host pool id before refresh: %i] "
|
|
"[host pool id after refresh: %i] ",
|
|
h->get_ip()->print(buf, sizeof(buf)),
|
|
up->update_pool_id ? 1 : 0,
|
|
up->update_l7policy ? 1 : 0,
|
|
cur_pool_id,
|
|
h->get_host_pool());
|
|
#endif
|
|
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
static bool update_l2_device_host_pool(GenericHashEntry *node, void *user_data, bool *matched) {
|
|
Mac *m = (Mac*)node;
|
|
|
|
#ifdef HOST_POOLS_DEBUG
|
|
u_int16_t cur_pool_id = m->get_host_pool();
|
|
#endif
|
|
|
|
*matched = true;
|
|
m->updateHostPool(false /* Not inline with traffic processing */);
|
|
|
|
#ifdef HOST_POOLS_DEBUG
|
|
char buf[24];
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Going to refresh pool for %s "
|
|
"[host pool id before refresh: %i] "
|
|
"[host pool id after refresh: %i] ",
|
|
Utils::formatMac(m->get_mac(), buf, sizeof(buf)),
|
|
cur_pool_id,
|
|
m->get_host_pool());
|
|
#endif
|
|
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::refreshHostPools() {
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
if(isView()) return;
|
|
|
|
struct update_host_pool_l7policy update_host;
|
|
update_host.update_pool_id = true;
|
|
update_host.update_l7policy = false;
|
|
|
|
#ifdef NTOPNG_PRO
|
|
if(is_bridge_interface() && getL7Policer()) {
|
|
/*
|
|
Every pool is associated with a set of L7 rules
|
|
so a refresh must be triggered to seal this association
|
|
*/
|
|
|
|
getL7Policer()->refreshL7Rules();
|
|
|
|
/*
|
|
Must refresh host l7policies as a change in the host pool id
|
|
may determine an l7policy change for that host
|
|
*/
|
|
update_host.update_l7policy = true;
|
|
}
|
|
#endif
|
|
|
|
hosts_hash->walk(&begin_slot, walk_all, update_host_host_pool_l7policy, &update_host);
|
|
begin_slot = 0;
|
|
macs_hash->walk(&begin_slot, walk_all, update_l2_device_host_pool, NULL);
|
|
|
|
#ifdef HAVE_NEDGE
|
|
if(update_host.update_l7policy)
|
|
updateFlowsL7Policy();
|
|
#endif
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
#ifdef HAVE_NEDGE
|
|
|
|
static bool update_flow_l7_policy(GenericHashEntry *node, void *user_data, bool *matched) {
|
|
Flow *f = (Flow*)node;
|
|
|
|
*matched = true;
|
|
f->updateFlowShapers();
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::updateHostsL7Policy(u_int16_t host_pool_id) {
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
if(isView()) return;
|
|
|
|
struct update_host_pool_l7policy update_host;
|
|
update_host.update_pool_id = false;
|
|
update_host.update_l7policy = true;
|
|
|
|
/* Pool id didn't change here so there's no need to walk on the macs
|
|
as policies are set on the hosts */
|
|
hosts_hash->walk(&begin_slot, walk_all,
|
|
update_host_host_pool_l7policy, &update_host);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::updateFlowsL7Policy() {
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
if(isView()) return;
|
|
|
|
flows_hash->walk(&begin_slot, walk_all, update_flow_l7_policy, NULL);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
struct resetPoolsStatsData {
|
|
struct tm *now;
|
|
u_int16_t pool_filter;
|
|
};
|
|
|
|
static bool flow_recheck_quota_walker(GenericHashEntry *flow, void *user_data, bool *matched) {
|
|
Flow *f = (Flow*)flow;
|
|
struct tm *now = ((struct resetPoolsStatsData*)user_data)->now;
|
|
|
|
*matched = true;
|
|
f->recheckQuota(now);
|
|
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
static bool host_reset_quotas(GenericHashEntry *host, void *user_data, bool *matched) {
|
|
Host *h = (Host*)host;
|
|
u_int16_t pool_filter = ((struct resetPoolsStatsData*)user_data)->pool_filter;
|
|
|
|
if((pool_filter == (u_int16_t)-1) || (h->get_host_pool() == pool_filter)) {
|
|
*matched = true;
|
|
h->resetQuotaStats();
|
|
h->resetBlockedTrafficStatus();
|
|
}
|
|
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
#endif
|
|
|
|
/* **************************************************** */
|
|
|
|
#ifdef NTOPNG_PRO
|
|
|
|
void NetworkInterface::resetPoolsStats(u_int16_t pool_filter) {
|
|
struct tm now;
|
|
time_t t_now = time(NULL);
|
|
localtime_r(&t_now, &now);
|
|
|
|
if(host_pools) {
|
|
disablePurge(true);
|
|
|
|
host_pools->resetPoolsStats(pool_filter);
|
|
|
|
#ifdef HAVE_NEDGE
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
struct resetPoolsStatsData data;
|
|
|
|
data.pool_filter = pool_filter;
|
|
data.now = &now;
|
|
|
|
walker(&begin_slot, walk_all, walker_hosts, host_reset_quotas, &data);
|
|
begin_slot = 0;
|
|
walker(&begin_slot, walk_all, walker_flows, flow_recheck_quota_walker, &data);
|
|
#endif
|
|
|
|
enablePurge(true);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
/* **************************************************** */
|
|
|
|
struct host_find_info {
|
|
char *host_to_find;
|
|
u_int16_t vlan_id;
|
|
Host *h;
|
|
};
|
|
|
|
/* **************************************************** */
|
|
|
|
struct as_find_info {
|
|
u_int32_t asn;
|
|
AutonomousSystem *as;
|
|
};
|
|
|
|
/* **************************************************** */
|
|
|
|
struct vlan_find_info {
|
|
u_int16_t vlan_id;
|
|
Vlan *vl;
|
|
};
|
|
|
|
/* **************************************************** */
|
|
|
|
struct mac_find_info {
|
|
u_int8_t mac[6];
|
|
u_int16_t vlan_id;
|
|
Mac *m;
|
|
DeviceType dtype;
|
|
};
|
|
|
|
/* **************************************************** */
|
|
|
|
struct arp_stats_element_find_info {
|
|
u_int8_t src_mac[6];
|
|
u_int8_t dst_mac[6];
|
|
ArpStatsMatrixElement *elem;
|
|
};
|
|
|
|
/* **************************************************** */
|
|
|
|
static bool find_host_by_name(GenericHashEntry *h, void *user_data, bool *matched) {
|
|
struct host_find_info *info = (struct host_find_info*)user_data;
|
|
Host *host = (Host*)h;
|
|
char ip_buf[32], name_buf[96];
|
|
name_buf[0] = '\0';
|
|
|
|
|
|
#ifdef DEBUG
|
|
char buf[64];
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "[%s][%s][%s]",
|
|
host->get_ip() ? host->get_ip()->print(buf, sizeof(buf)) : "",
|
|
host->get_name(), info->host_to_find);
|
|
#endif
|
|
|
|
if((info->h == NULL) && (host->get_vlan_id() == info->vlan_id)) {
|
|
host->get_name(name_buf, sizeof(name_buf), false);
|
|
|
|
if(strlen(name_buf) == 0 && host->get_ip()) {
|
|
char *ipaddr = host->get_ip()->print(ip_buf, sizeof(ip_buf));
|
|
int rc = ntop->getRedis()->getAddress(ipaddr, name_buf, sizeof(name_buf),
|
|
false /* Don't resolve it if not known */);
|
|
|
|
if(rc == 0 /* found */ && strcmp(ipaddr, name_buf))
|
|
host->setResolvedName(name_buf);
|
|
else
|
|
name_buf[0] = '\0';
|
|
}
|
|
|
|
if(!strcmp(name_buf, info->host_to_find)) {
|
|
info->h = host;
|
|
*matched = true;
|
|
return(true); /* found */
|
|
}
|
|
}
|
|
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
static bool find_mac_by_name(GenericHashEntry *h, void *user_data, bool *matched) {
|
|
struct mac_find_info *info = (struct mac_find_info*)user_data;
|
|
Mac *m = (Mac*)h;
|
|
|
|
if((info->m == NULL) && (!memcmp(info->mac, m->get_mac(), 6))) {
|
|
info->m = m;
|
|
*matched = true;
|
|
|
|
return(true); /* found */
|
|
}
|
|
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
static bool find_as_by_asn(GenericHashEntry *he, void *user_data, bool *matched) {
|
|
struct as_find_info *info = (struct as_find_info*)user_data;
|
|
AutonomousSystem *as = (AutonomousSystem*)he;
|
|
|
|
if((info->as == NULL) && info->asn == as->get_asn()) {
|
|
info->as = as;
|
|
*matched = true;
|
|
return(true); /* found */
|
|
}
|
|
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
static bool find_vlan_by_vlan_id(GenericHashEntry *he, void *user_data, bool *matched) {
|
|
struct vlan_find_info *info = (struct vlan_find_info*)user_data;
|
|
Vlan *vl = (Vlan*)he;
|
|
|
|
if((info->vl == NULL) && info->vlan_id == vl->get_vlan_id()) {
|
|
info->vl = vl;
|
|
*matched = true;
|
|
return(true); /* found */
|
|
}
|
|
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
bool NetworkInterface::restoreHost(char *host_ip, u_int16_t vlan_id) {
|
|
Host *h;
|
|
int16_t local_network_id;
|
|
IpAddress ipa;
|
|
|
|
ipa.set(host_ip);
|
|
|
|
if(ipa.isLocalHost(&local_network_id))
|
|
h = new LocalHost(this, host_ip, vlan_id);
|
|
else
|
|
h = new RemoteHost(this, host_ip, vlan_id);
|
|
|
|
if(!h) return(false);
|
|
|
|
if(!hosts_hash->add(h)) {
|
|
//ntop->getTrace()->traceEvent(TRACE_WARNING, "Too many hosts in interface %s", ifname);
|
|
delete h;
|
|
return(false);
|
|
}
|
|
|
|
h->postHashAdd();
|
|
return(true);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
Host* NetworkInterface::getHost(char *host_ip, u_int16_t vlan_id) {
|
|
struct in_addr a4;
|
|
struct in6_addr a6;
|
|
Host *h = NULL;
|
|
|
|
if(!host_ip) return(NULL);
|
|
|
|
/* Check if address is invalid */
|
|
if((inet_pton(AF_INET, (const char*)host_ip, &a4) == 0)
|
|
&& (inet_pton(AF_INET6, (const char*)host_ip, &a6) == 0)) {
|
|
/* Looks like a symbolic name */
|
|
struct host_find_info info;
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
info.host_to_find = host_ip, info.vlan_id = vlan_id;
|
|
walker(&begin_slot, walk_all, walker_hosts, find_host_by_name, (void*)&info);
|
|
|
|
h = info.h;
|
|
} else {
|
|
IpAddress *ip = new IpAddress();
|
|
|
|
if(ip) {
|
|
ip->set(host_ip);
|
|
|
|
h = hosts_hash->get(vlan_id, ip);
|
|
|
|
delete ip;
|
|
}
|
|
}
|
|
|
|
return(h);
|
|
}
|
|
|
|
|
|
/* **************************************************** */
|
|
|
|
Host* NetworkInterface::getHost(IpAddress * const host_ip, u_int16_t vlan_id) const {
|
|
return(hosts_hash->get(vlan_id, host_ip));
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
#ifdef NTOPNG_PRO
|
|
|
|
#ifndef HAVE_NEDGE
|
|
static bool update_flow_profile(GenericHashEntry *h, void *user_data, bool *matched) {
|
|
Flow *flow = (Flow*)h;
|
|
|
|
flow->updateProfile();
|
|
*matched = true;
|
|
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::updateFlowProfiles() {
|
|
if(isView()) return;
|
|
|
|
if(ntop->getPro()->has_valid_license()) {
|
|
FlowProfiles *newP;
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
if(shadow_flow_profiles) {
|
|
delete shadow_flow_profiles;
|
|
shadow_flow_profiles = NULL;
|
|
}
|
|
|
|
flow_profiles->dumpCounters();
|
|
shadow_flow_profiles = flow_profiles, newP = new FlowProfiles(id);
|
|
|
|
newP->loadProfiles(); /* and reload */
|
|
flow_profiles = newP; /* Overwrite the current profiles */
|
|
|
|
flows_hash->walk(&begin_slot, walk_all, update_flow_profile, NULL);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#endif
|
|
|
|
/* **************************************************** */
|
|
|
|
bool NetworkInterface::getHostInfo(lua_State* vm,
|
|
AddressTree *allowed_hosts,
|
|
char *host_ip, u_int16_t vlan_id) {
|
|
Host *h;
|
|
bool ret;
|
|
|
|
disablePurge(false);
|
|
|
|
h = findHostByIP(allowed_hosts, host_ip, vlan_id);
|
|
|
|
if(h) {
|
|
h->lua(vm, allowed_hosts, true, true, true, false);
|
|
ret = true;
|
|
} else
|
|
ret = false;
|
|
|
|
enablePurge(false);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::checkReloadHostsBroadcastDomain() {
|
|
time_t bcast_domains_last_update = bcast_domains->getLastUpdate();
|
|
|
|
if(hosts_bcast_domain_last_update < bcast_domains_last_update)
|
|
reload_hosts_bcast_domain = true,
|
|
hosts_bcast_domain_last_update = bcast_domains_last_update;
|
|
else if(reload_hosts_bcast_domain)
|
|
reload_hosts_bcast_domain = false;
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
bool NetworkInterface::checkPointHostCounters(lua_State* vm, u_int8_t checkpoint_id,
|
|
char *host_ip, u_int16_t vlan_id,
|
|
DetailsLevel details_level) {
|
|
Host *h;
|
|
bool ret = false;
|
|
|
|
if(host_ip && (h = getHost(host_ip, vlan_id)))
|
|
ret = h->checkpoint(vm, this, checkpoint_id, details_level);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
bool NetworkInterface::checkPointNetworkCounters(lua_State* vm, u_int8_t checkpoint_id,
|
|
u_int8_t network_id,
|
|
DetailsLevel details_level) {
|
|
NetworkStats *stats = getNetworkStats(network_id);
|
|
|
|
if (stats == NULL)
|
|
return false;
|
|
|
|
return stats->checkpoint(vm, this, checkpoint_id, details_level);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
bool NetworkInterface::checkPointHostTalker(lua_State* vm, char *host_ip, u_int16_t vlan_id, bool saveCheckpoint) {
|
|
Host *h;
|
|
bool ret = false;
|
|
|
|
if(host_ip && (h = getHost(host_ip, vlan_id))) {
|
|
h->checkPointHostTalker(vm, saveCheckpoint);
|
|
ret = true;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
bool NetworkInterface::serializeCheckpoint(json_object *my_object, DetailsLevel details_level) {
|
|
json_object *inner;
|
|
|
|
if((inner = json_object_new_object()) == NULL) return false;
|
|
|
|
json_object_object_add(my_object, "seen.last", json_object_new_int64(getTimeLastPktRcvd()));
|
|
json_object_object_add(my_object, "ndpiStats", ndpiStats.getJSONObjectForCheckpoint(this));
|
|
json_object_object_add(my_object, "local_hosts", json_object_new_int64(getNumLocalHosts()));
|
|
json_object_object_add(inner, "bytes", json_object_new_int64(getNumBytes()));
|
|
json_object_object_add(inner, "packets", json_object_new_int64(getNumPackets()));
|
|
json_object_object_add(my_object, "stats", inner);
|
|
|
|
return true;
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
Host* NetworkInterface::findHostByIP(AddressTree *allowed_hosts,
|
|
char *host_ip, u_int16_t vlan_id) {
|
|
if(host_ip != NULL) {
|
|
Host *h = getHost(host_ip, vlan_id);
|
|
|
|
if(h && h->match(allowed_hosts))
|
|
return(h);
|
|
}
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
struct flowHostRetrieveList {
|
|
Flow *flow;
|
|
/* Value */
|
|
Host *hostValue;
|
|
Mac *macValue;
|
|
Vlan *vlanValue;
|
|
AutonomousSystem *asValue;
|
|
Country *countryVal;
|
|
u_int64_t numericValue;
|
|
char *stringValue;
|
|
IpAddress *ipValue;
|
|
};
|
|
|
|
struct flowHostRetriever {
|
|
/* Search criteria */
|
|
AddressTree *allowed_hosts;
|
|
Host *host;
|
|
u_int8_t *mac, bridge_iface_idx;
|
|
char *manufacturer;
|
|
bool sourceMacsOnly, dhcpHostsOnly;
|
|
char *country;
|
|
int ndpi_proto; /* Not used in flow_search_walker */
|
|
TrafficType traffic_type; /* Not used in flow_search_walker */
|
|
sortField sorter;
|
|
TcpFlowStateFilter tcp_flow_state_filter;
|
|
LocationPolicy location; /* Not used in flow_search_walker */
|
|
u_int8_t ipVersionFilter; /* Not used in flow_search_walker */
|
|
bool filteredHosts; /* Not used in flow_search_walker */
|
|
bool blacklistedHosts; /* Not used in flow_search_walker */
|
|
bool anomalousOnly; /* Not used in flow_search_walker */
|
|
bool dhcpOnly; /* Not used in flow_search_walker */
|
|
bool hideTopHidden; /* Not used in flow_search_walker */
|
|
const AddressTree * cidr_filter; /* Not used in flow_search_walker */
|
|
u_int16_t vlan_id;
|
|
char *osFilter;
|
|
u_int32_t asnFilter;
|
|
u_int32_t uidFilter;
|
|
u_int32_t pidFilter;
|
|
int16_t networkFilter;
|
|
u_int16_t poolFilter;
|
|
u_int8_t devtypeFilter;
|
|
u_int8_t locationFilter;
|
|
|
|
/* Return values */
|
|
u_int32_t maxNumEntries, actNumEntries;
|
|
struct flowHostRetrieveList *elems;
|
|
|
|
/* Paginator */
|
|
Paginator *pag;
|
|
};
|
|
|
|
/* **************************************************** */
|
|
|
|
static bool flow_matches(Flow *f, struct flowHostRetriever *retriever) {
|
|
int ndpi_proto, ndpi_cat;
|
|
u_int16_t port;
|
|
int16_t local_network_id;
|
|
u_int16_t vlan_id = 0, pool_filter;
|
|
u_int8_t ip_version;
|
|
u_int8_t *mac_filter;
|
|
LocationPolicy client_policy;
|
|
LocationPolicy server_policy;
|
|
TcpFlowStateFilter tcp_flow_state_filter;
|
|
bool unicast, unidirectional, alerted_flows;
|
|
u_int32_t asn_filter;
|
|
u_int32_t uid_filter;
|
|
u_int32_t pid_filter;
|
|
u_int32_t deviceIP;
|
|
u_int16_t inIndex, outIndex;
|
|
#ifdef HAVE_NEDGE
|
|
bool filtered_flows;
|
|
#endif
|
|
|
|
if(f && (!f->idle())) {
|
|
if(retriever->host
|
|
&& (retriever->host != f->get_cli_host())
|
|
&& (retriever->host != f->get_srv_host()))
|
|
return(false);
|
|
|
|
if(retriever->pag
|
|
&& retriever->pag->l7protoFilter(&ndpi_proto)
|
|
&& ((ndpi_proto == NDPI_PROTOCOL_UNKNOWN
|
|
&& (f->get_detected_protocol().app_protocol != ndpi_proto
|
|
|| f->get_detected_protocol().master_protocol != ndpi_proto))
|
|
||
|
|
(ndpi_proto != NDPI_PROTOCOL_UNKNOWN
|
|
&& (f->get_detected_protocol().app_protocol != ndpi_proto
|
|
&& f->get_detected_protocol().master_protocol != ndpi_proto))))
|
|
return(false);
|
|
|
|
if(retriever->pag
|
|
&& retriever->pag->l7categoryFilter(&ndpi_cat)
|
|
&& f->get_protocol_category() != ndpi_cat)
|
|
return(false);
|
|
|
|
if(retriever->pag
|
|
&& retriever->pag->tcpFlowStateFilter(&tcp_flow_state_filter)
|
|
&& ((f->get_protocol() != IPPROTO_TCP)
|
|
|| (tcp_flow_state_filter == tcp_flow_state_established && !f->isTCPEstablished())
|
|
|| (tcp_flow_state_filter == tcp_flow_state_connecting && !f->isTCPConnecting())
|
|
|| (tcp_flow_state_filter == tcp_flow_state_closed && !f->isTCPClosed())
|
|
|| (tcp_flow_state_filter == tcp_flow_state_reset && !f->isTCPReset())))
|
|
return(false);
|
|
|
|
if(retriever->pag
|
|
&& retriever->pag->ipVersion(&ip_version)
|
|
&& (((ip_version == 4) && (f->get_cli_host() && !f->get_cli_host()->get_ip()->isIPv4()))
|
|
|| ((ip_version == 6) && (f->get_cli_host() && !f->get_cli_host()->get_ip()->isIPv6()))))
|
|
return(false);
|
|
|
|
if(retriever->pag
|
|
&& retriever->pag->deviceIpFilter(&deviceIP)) {
|
|
if(f->getFlowDeviceIp() != deviceIP
|
|
|| (retriever->pag->inIndexFilter(&inIndex) && f->getFlowDeviceInIndex() != inIndex)
|
|
|| (retriever->pag->outIndexFilter(&outIndex) && f->getFlowDeviceOutIndex() != outIndex))
|
|
return(false);
|
|
}
|
|
|
|
if(retriever->pag
|
|
&& retriever->pag->asnFilter(&asn_filter)
|
|
&& f->get_cli_host() && f->get_srv_host()
|
|
&& f->get_cli_host()->get_asn() != asn_filter
|
|
&& f->get_srv_host()->get_asn() != asn_filter)
|
|
return(false);
|
|
|
|
if(retriever->pag
|
|
&& retriever->pag->uidFilter(&uid_filter)
|
|
&& f->get_uid(true /* client uid */) != uid_filter
|
|
&& f->get_uid(false /* server uid */) != uid_filter)
|
|
return(false);
|
|
|
|
if(retriever->pag
|
|
&& retriever->pag->pidFilter(&pid_filter)
|
|
&& f->get_pid(true /* client pid */) != pid_filter
|
|
&& f->get_pid(false /* server pid */) != pid_filter)
|
|
return(false);
|
|
|
|
if(retriever->pag
|
|
&& retriever->pag->portFilter(&port)
|
|
&& f->get_cli_port() != port
|
|
&& f->get_srv_port() != port)
|
|
return(false);
|
|
|
|
if(retriever->pag
|
|
&& retriever->pag->localNetworkFilter(&local_network_id)
|
|
&& f->get_cli_host() && f->get_srv_host()
|
|
&& f->get_cli_host()->get_local_network_id() != local_network_id
|
|
&& f->get_srv_host()->get_local_network_id() != local_network_id)
|
|
return(false);
|
|
|
|
if(retriever->pag
|
|
&& retriever->pag->vlanIdFilter(&vlan_id)
|
|
&& f->get_vlan_id() != vlan_id)
|
|
return(false);
|
|
|
|
if(retriever->pag
|
|
&& retriever->pag->clientMode(&client_policy)
|
|
&& f->get_cli_host()
|
|
&& (((client_policy == location_local_only) && (!f->get_cli_host()->isLocalHost()))
|
|
|| ((client_policy == location_remote_only) && (f->get_cli_host()->isLocalHost()))))
|
|
return(false);
|
|
|
|
if(retriever->pag
|
|
&& retriever->pag->serverMode(&server_policy)
|
|
&& (((server_policy == location_local_only) && (!f->get_srv_host()->isLocalHost()))
|
|
|| ((server_policy == location_remote_only) && (f->get_srv_host()->isLocalHost()))))
|
|
return(false);
|
|
|
|
if(retriever->pag
|
|
&& retriever->pag->alertedFlows(&alerted_flows)
|
|
&& ((alerted_flows && f->getFlowStatus() == status_normal)
|
|
|| (!alerted_flows && f->getFlowStatus() != status_normal)))
|
|
return(false);
|
|
|
|
#ifdef HAVE_NEDGE
|
|
if(retriever->pag
|
|
&& retriever->pag->filteredFlows(&filtered_flows)
|
|
&& ((filtered_flows && f->isPassVerdict())
|
|
|| (!filtered_flows && !f->isPassVerdict())))
|
|
return(false);
|
|
#endif
|
|
|
|
if(retriever->pag
|
|
&& retriever->pag->unidirectionalTraffic(&unidirectional)
|
|
&& ((unidirectional && (f->get_packets() > 0) && (f->get_packets_cli2srv() > 0) && (f->get_packets_srv2cli() > 0))
|
|
|| (!unidirectional && (f->get_packets() > 0) && ((f->get_packets_cli2srv() == 0) || (f->get_packets_srv2cli() == 0)))))
|
|
return(false);
|
|
|
|
/* Unicast: at least one between client and server is unicast address */
|
|
if(retriever->pag
|
|
&& retriever->pag->unicastTraffic(&unicast)
|
|
&& ((unicast && ((f->get_cli_host() && (f->get_cli_host()->get_ip()->isMulticastAddress() || f->get_cli_host()->get_ip()->isBroadcastAddress()))
|
|
|| (f->get_srv_host() && (f->get_srv_host()->get_ip()->isMulticastAddress() || f->get_srv_host()->get_ip()->isBroadcastAddress()))))
|
|
|| (!unicast && ((f->get_cli_host() && (!f->get_cli_host()->get_ip()->isMulticastAddress() && !f->get_cli_host()->get_ip()->isBroadcastAddress()))
|
|
&& (f->get_srv_host() && (!f->get_srv_host()->get_ip()->isMulticastAddress() && !f->get_srv_host()->get_ip()->isBroadcastAddress()))))))
|
|
return(false);
|
|
|
|
/* Pool filter */
|
|
if(retriever->pag
|
|
&& retriever->pag->poolFilter(&pool_filter)
|
|
&& !((f->get_cli_host() && f->get_cli_host()->get_host_pool() == pool_filter)
|
|
|| (f->get_srv_host() && f->get_srv_host()->get_host_pool() == pool_filter)))
|
|
return(false);
|
|
|
|
/* Mac filter - NOTE: must stay below the vlan_id filter */
|
|
if(retriever->pag
|
|
&& retriever->pag->macFilter(&mac_filter)
|
|
&& !((f->get_cli_host() && f->get_cli_host()->getMac() && f->get_cli_host()->getMac()->equal(mac_filter))
|
|
|| (f->get_srv_host() && f->get_srv_host()->getMac() && f->get_srv_host()->getMac()->equal(mac_filter))))
|
|
return(false);
|
|
|
|
if(f->match(retriever->allowed_hosts))
|
|
return(true); /* match */
|
|
}
|
|
|
|
return(false);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
static bool flow_search_walker(GenericHashEntry *h, void *user_data, bool *matched) {
|
|
struct flowHostRetriever *retriever = (struct flowHostRetriever*)user_data;
|
|
Flow *f = (Flow*)h;
|
|
char *flow_info;
|
|
|
|
if(retriever->actNumEntries >= retriever->maxNumEntries)
|
|
return(true); /* Limit reached - stop iterating */
|
|
|
|
if(flow_matches(f, retriever)) {
|
|
retriever->elems[retriever->actNumEntries].flow = f;
|
|
|
|
switch(retriever->sorter) {
|
|
case column_client:
|
|
retriever->elems[retriever->actNumEntries++].hostValue = f->get_cli_host();
|
|
break;
|
|
case column_server:
|
|
retriever->elems[retriever->actNumEntries++].hostValue = f->get_srv_host();
|
|
break;
|
|
case column_vlan:
|
|
retriever->elems[retriever->actNumEntries++].numericValue = f->get_vlan_id();
|
|
break;
|
|
case column_proto_l4:
|
|
retriever->elems[retriever->actNumEntries++].numericValue = f->get_protocol();
|
|
break;
|
|
case column_ndpi:
|
|
retriever->elems[retriever->actNumEntries++].numericValue = f->get_detected_protocol().app_protocol;
|
|
break;
|
|
case column_duration:
|
|
retriever->elems[retriever->actNumEntries++].numericValue = f->get_duration();
|
|
break;
|
|
case column_thpt:
|
|
retriever->elems[retriever->actNumEntries++].numericValue = f->get_bytes_thpt();
|
|
break;
|
|
case column_bytes:
|
|
retriever->elems[retriever->actNumEntries++].numericValue = f->get_bytes();
|
|
break;
|
|
case column_info:
|
|
flow_info = f->getFlowInfo();
|
|
retriever->elems[retriever->actNumEntries++].stringValue = flow_info ? flow_info : (char*)"";
|
|
break;
|
|
default:
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Internal error: column %d not handled", retriever->sorter);
|
|
break;
|
|
}
|
|
|
|
*matched = true;
|
|
}
|
|
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
static bool host_search_walker(GenericHashEntry *he, void *user_data, bool *matched) {
|
|
char buf[64];
|
|
u_int8_t network_prefix = 0;
|
|
IpAddress *ip_addr = NULL;
|
|
struct flowHostRetriever *r = (struct flowHostRetriever*)user_data;
|
|
Host *h = (Host*)he;
|
|
|
|
if(r->actNumEntries >= r->maxNumEntries)
|
|
return(true); /* Limit reached */
|
|
|
|
if(!h || h->idle() || !h->match(r->allowed_hosts))
|
|
return(false);
|
|
|
|
if((r->location == location_local_only && !h->isLocalHost()) ||
|
|
(r->location == location_remote_only && h->isLocalHost()) ||
|
|
(r->location == location_broadcast_domain_only && !h->isBroadcastDomainHost()) ||
|
|
((r->vlan_id != ((u_int16_t)-1)) && (r->vlan_id != h->get_vlan_id())) ||
|
|
((r->ndpi_proto != -1) && (h->get_ndpi_stats()->getProtoBytes(r->ndpi_proto) == 0)) ||
|
|
((r->asnFilter != (u_int32_t)-1) && (r->asnFilter != h->get_asn())) ||
|
|
((r->networkFilter != -2) && (r->networkFilter != h->get_local_network_id())) ||
|
|
(r->mac && ((!h->getMac()) || (!h->getMac()->equal(r->mac)))) ||
|
|
((r->poolFilter != (u_int16_t)-1) && (r->poolFilter != h->get_host_pool())) ||
|
|
(r->country && strlen(r->country) && strcmp(h->get_country(buf, sizeof(buf)), r->country)) ||
|
|
(r->osFilter && strlen(r->osFilter) && strcmp(h->get_os(buf, sizeof(buf)), r->osFilter)) ||
|
|
(r->blacklistedHosts && !h->isBlacklisted()) ||
|
|
(r->anomalousOnly && !h->hasAnomalies()) ||
|
|
(r->dhcpOnly && !h->isDhcpHost()) ||
|
|
(r->cidr_filter && !h->match(r->cidr_filter)) ||
|
|
(r->hideTopHidden && h->isHiddenFromTop()) ||
|
|
(r->traffic_type == traffic_type_one_way && !h->isOneWayTraffic()) ||
|
|
(r->traffic_type == traffic_type_bidirectional && h->isOneWayTraffic()) ||
|
|
(r->dhcpHostsOnly && (!h->isDhcpHost())) ||
|
|
#ifdef NTOPNG_PRO
|
|
(r->filteredHosts && !h->hasBlockedTraffic()) ||
|
|
#endif
|
|
(r->ipVersionFilter && (((r->ipVersionFilter == 4) && (!h->get_ip()->isIPv4()))
|
|
|| ((r->ipVersionFilter == 6) && (!h->get_ip()->isIPv6())))))
|
|
return(false); /* false = keep on walking */
|
|
|
|
r->elems[r->actNumEntries].hostValue = h;
|
|
|
|
switch(r->sorter) {
|
|
case column_ip:
|
|
r->elems[r->actNumEntries++].hostValue = h; /* hostValue was already set */
|
|
break;
|
|
|
|
case column_alerts:
|
|
r->elems[r->actNumEntries++].numericValue = h->getNumAlerts();
|
|
break;
|
|
|
|
case column_name:
|
|
r->elems[r->actNumEntries++].stringValue = strdup(h->get_visual_name(buf, sizeof(buf)));
|
|
break;
|
|
|
|
case column_country:
|
|
r->elems[r->actNumEntries++].stringValue = strdup(h->get_country(buf, sizeof(buf)));
|
|
break;
|
|
|
|
case column_os:
|
|
r->elems[r->actNumEntries++].stringValue = strdup(h->get_os(buf, sizeof(buf)));
|
|
break;
|
|
|
|
case column_vlan:
|
|
r->elems[r->actNumEntries++].numericValue = h->get_vlan_id();
|
|
break;
|
|
|
|
case column_since:
|
|
r->elems[r->actNumEntries++].numericValue = h->get_first_seen();
|
|
break;
|
|
|
|
case column_asn:
|
|
r->elems[r->actNumEntries++].numericValue = h->get_asn();
|
|
break;
|
|
|
|
case column_thpt:
|
|
r->elems[r->actNumEntries++].numericValue = h->getBytesThpt();
|
|
break;
|
|
|
|
case column_num_flows:
|
|
r->elems[r->actNumEntries++].numericValue = h->getNumActiveFlows();
|
|
break;
|
|
|
|
case column_num_dropped_flows:
|
|
r->elems[r->actNumEntries++].numericValue = h->getNumDroppedFlows();
|
|
break;
|
|
|
|
case column_traffic:
|
|
r->elems[r->actNumEntries++].numericValue = h->getNumBytes();
|
|
break;
|
|
|
|
case column_local_network_id:
|
|
r->elems[r->actNumEntries++].numericValue = h->get_local_network_id();
|
|
break;
|
|
|
|
case column_local_network:
|
|
ntop->getLocalNetworkIp(h->get_local_network_id(), &ip_addr, &network_prefix);
|
|
r->elems[r->actNumEntries].ipValue = ip_addr;
|
|
r->elems[r->actNumEntries++].numericValue = network_prefix;
|
|
break;
|
|
|
|
case column_mac:
|
|
r->elems[r->actNumEntries++].numericValue = Utils::macaddr_int(h->get_mac());
|
|
break;
|
|
|
|
case column_pool_id:
|
|
r->elems[r->actNumEntries++].numericValue = h->get_host_pool();
|
|
break;
|
|
|
|
/* Criteria */
|
|
case column_traffic_sent: r->elems[r->actNumEntries++].numericValue = h->getNumBytesSent(); break;
|
|
case column_traffic_rcvd: r->elems[r->actNumEntries++].numericValue = h->getNumBytesRcvd(); break;
|
|
case column_traffic_unknown: r->elems[r->actNumEntries++].numericValue = h->get_ndpi_stats()->getProtoBytes(NDPI_PROTOCOL_UNKNOWN); break;
|
|
case column_num_flows_as_client: r->elems[r->actNumEntries++].numericValue = h->getNumOutgoingFlows(); break;
|
|
case column_num_flows_as_server: r->elems[r->actNumEntries++].numericValue = h->getNumIncomingFlows(); break;
|
|
case column_total_num_anomalous_flows_as_client: r->elems[r->actNumEntries++].numericValue = h->getTotalNumAnomalousOutgoingFlows(); break;
|
|
case column_total_num_anomalous_flows_as_server: r->elems[r->actNumEntries++].numericValue = h->getTotalNumAnomalousIncomingFlows(); break;
|
|
case column_total_alerts: r->elems[r->actNumEntries++].numericValue = h->getTotalAlerts(); break;
|
|
|
|
default:
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Internal error: column %d not handled", r->sorter);
|
|
break;
|
|
}
|
|
|
|
*matched = true;
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
static bool mac_search_walker(GenericHashEntry *he, void *user_data, bool *matched) {
|
|
struct flowHostRetriever *r = (struct flowHostRetriever*)user_data;
|
|
Mac *m = (Mac*)he;
|
|
u_int16_t pool_value;
|
|
bool pool_found;
|
|
|
|
if(r->actNumEntries >= r->maxNumEntries)
|
|
return(true); /* Limit reached */
|
|
|
|
if(!m
|
|
|| m->idle()
|
|
|| (r->sourceMacsOnly && !m->isSourceMac())
|
|
|| ((r->devtypeFilter != (u_int8_t)-1) && (m->getDeviceType() != r->devtypeFilter))
|
|
|| ((r->locationFilter != (u_int8_t)-1) && (m->locate() != r->locationFilter))
|
|
|| ((r->poolFilter != (u_int16_t)-1) && (
|
|
(((pool_found = m->getInterface()->getHostPools()->findMacPool(m, &pool_value)) == false /* unassigned */) && r->poolFilter != 0)
|
|
|| ((pool_found == true) && (pool_value != r->poolFilter))))
|
|
|| (r->manufacturer && strcmp(r->manufacturer, m->get_manufacturer() ? m->get_manufacturer() : "") != 0))
|
|
return(false); /* false = keep on walking */
|
|
|
|
r->elems[r->actNumEntries].macValue = m;
|
|
|
|
switch(r->sorter) {
|
|
case column_mac:
|
|
r->elems[r->actNumEntries++].numericValue = Utils::macaddr_int(m->get_mac());
|
|
break;
|
|
|
|
case column_since:
|
|
r->elems[r->actNumEntries++].numericValue = m->get_first_seen();
|
|
break;
|
|
|
|
case column_thpt:
|
|
r->elems[r->actNumEntries++].numericValue = m->getBytesThpt();
|
|
break;
|
|
|
|
case column_traffic:
|
|
r->elems[r->actNumEntries++].numericValue = m->getNumBytes();
|
|
break;
|
|
|
|
case column_num_hosts:
|
|
r->elems[r->actNumEntries++].numericValue = m->getNumHosts();
|
|
break;
|
|
|
|
case column_manufacturer:
|
|
r->elems[r->actNumEntries++].stringValue = m->get_manufacturer() ? (char*)m->get_manufacturer() : (char*)"zzz";
|
|
break;
|
|
|
|
case column_device_type:
|
|
r->elems[r->actNumEntries++].numericValue = m->getDeviceType();
|
|
break;
|
|
|
|
case column_arp_total:
|
|
r->elems[r->actNumEntries++].numericValue = m->getNumSentArp() + m->getNumRcvdArp();
|
|
break;
|
|
|
|
case column_arp_sent:
|
|
r->elems[r->actNumEntries++].numericValue = m->getNumSentArp();
|
|
break;
|
|
|
|
case column_arp_rcvd:
|
|
r->elems[r->actNumEntries++].numericValue = m->getNumRcvdArp();
|
|
break;
|
|
|
|
default:
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Internal error: column %d not handled", r->sorter);
|
|
break;
|
|
}
|
|
|
|
*matched = true;
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
static bool as_search_walker(GenericHashEntry *he, void *user_data, bool *matched) {
|
|
struct flowHostRetriever *r = (struct flowHostRetriever*)user_data;
|
|
AutonomousSystem *as = (AutonomousSystem*)he;
|
|
|
|
if(r->actNumEntries >= r->maxNumEntries)
|
|
return(true); /* Limit reached */
|
|
|
|
if(!as || as->idle())
|
|
return(false); /* false = keep on walking */
|
|
|
|
r->elems[r->actNumEntries].asValue = as;
|
|
|
|
switch(r->sorter) {
|
|
|
|
case column_asn:
|
|
r->elems[r->actNumEntries++].numericValue = as->get_asn();
|
|
break;
|
|
|
|
case column_asname:
|
|
r->elems[r->actNumEntries++].stringValue = as->get_asname() ? as->get_asname() : (char*)"zzz";
|
|
break;
|
|
|
|
case column_since:
|
|
r->elems[r->actNumEntries++].numericValue = as->get_first_seen();
|
|
break;
|
|
|
|
case column_thpt:
|
|
r->elems[r->actNumEntries++].numericValue = as->getBytesThpt();
|
|
break;
|
|
|
|
case column_traffic:
|
|
r->elems[r->actNumEntries++].numericValue = as->getNumBytes();
|
|
break;
|
|
|
|
case column_num_hosts:
|
|
r->elems[r->actNumEntries++].numericValue = as->getNumHosts();
|
|
break;
|
|
|
|
default:
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Internal error: column %d not handled", r->sorter);
|
|
break;
|
|
}
|
|
|
|
*matched = true;
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
static bool country_search_walker(GenericHashEntry *he, void *user_data, bool *matched) {
|
|
struct flowHostRetriever *r = (struct flowHostRetriever*)user_data;
|
|
Country *country = (Country*)he;
|
|
|
|
if(r->actNumEntries >= r->maxNumEntries)
|
|
return(true); /* Limit reached */
|
|
|
|
if(!country || country->idle())
|
|
return(false); /* false = keep on walking */
|
|
|
|
r->elems[r->actNumEntries].countryVal = country;
|
|
|
|
/* Note: we don't have throughput information into the countries */
|
|
switch(r->sorter) {
|
|
|
|
case column_country:
|
|
r->elems[r->actNumEntries++].stringValue = country->get_country_name();
|
|
break;
|
|
|
|
case column_since:
|
|
r->elems[r->actNumEntries++].numericValue = country->get_first_seen();
|
|
break;
|
|
|
|
case column_num_hosts:
|
|
r->elems[r->actNumEntries++].numericValue = country->getNumHosts();
|
|
break;
|
|
|
|
default:
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Internal error: column %d not handled", r->sorter);
|
|
break;
|
|
}
|
|
|
|
*matched = true;
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
static bool vlan_search_walker(GenericHashEntry *he, void *user_data, bool *matched) {
|
|
struct flowHostRetriever *r = (struct flowHostRetriever*)user_data;
|
|
Vlan *vl = (Vlan*)he;
|
|
|
|
if(r->actNumEntries >= r->maxNumEntries)
|
|
return(true); /* Limit reached */
|
|
|
|
if(!vl || vl->idle())
|
|
return(false); /* false = keep on walking */
|
|
|
|
r->elems[r->actNumEntries].vlanValue = vl;
|
|
|
|
switch(r->sorter) {
|
|
|
|
case column_vlan:
|
|
r->elems[r->actNumEntries++].numericValue = vl->get_vlan_id();
|
|
break;
|
|
|
|
case column_since:
|
|
r->elems[r->actNumEntries++].numericValue = vl->get_first_seen();
|
|
break;
|
|
|
|
case column_thpt:
|
|
r->elems[r->actNumEntries++].numericValue = vl->getBytesThpt();
|
|
break;
|
|
|
|
case column_traffic:
|
|
r->elems[r->actNumEntries++].numericValue = vl->getNumBytes();
|
|
break;
|
|
|
|
case column_num_hosts:
|
|
r->elems[r->actNumEntries++].numericValue = vl->getNumHosts();
|
|
break;
|
|
|
|
default:
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Internal error: column %d not handled", r->sorter);
|
|
break;
|
|
}
|
|
|
|
*matched = true;
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
int hostSorter(const void *_a, const void *_b) {
|
|
struct flowHostRetrieveList *a = (struct flowHostRetrieveList*)_a;
|
|
struct flowHostRetrieveList *b = (struct flowHostRetrieveList*)_b;
|
|
|
|
return(a->hostValue->get_ip()->compare(b->hostValue->get_ip()));
|
|
}
|
|
|
|
int ipNetworkSorter(const void *_a, const void *_b) {
|
|
struct flowHostRetrieveList *a = (struct flowHostRetrieveList*)_a;
|
|
struct flowHostRetrieveList *b = (struct flowHostRetrieveList*)_b;
|
|
int rv;
|
|
|
|
if(!a || !b || !a->ipValue || !b->ipValue)
|
|
return(true);
|
|
|
|
/* Compare network address first */
|
|
rv = a->ipValue->compare(b->ipValue);
|
|
if(rv != 0) return rv;
|
|
|
|
/* If the address matches, compare netmasks */
|
|
if(a->numericValue < b->numericValue) return(-1);
|
|
else if(a->numericValue > b->numericValue) return(1);
|
|
else return(0);
|
|
}
|
|
|
|
int numericSorter(const void *_a, const void *_b) {
|
|
struct flowHostRetrieveList *a = (struct flowHostRetrieveList*)_a;
|
|
struct flowHostRetrieveList *b = (struct flowHostRetrieveList*)_b;
|
|
|
|
if(a->numericValue < b->numericValue) return(-1);
|
|
else if(a->numericValue > b->numericValue) return(1);
|
|
else return(0);
|
|
}
|
|
|
|
int stringSorter(const void *_a, const void *_b) {
|
|
struct flowHostRetrieveList *a = (struct flowHostRetrieveList*)_a;
|
|
struct flowHostRetrieveList *b = (struct flowHostRetrieveList*)_b;
|
|
|
|
return(strcmp(a->stringValue, b->stringValue));
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::disablePurge(bool on_flows) {
|
|
if(!isView()) {
|
|
if(on_flows)
|
|
flows_hash->disablePurge();
|
|
else {
|
|
hosts_hash->disablePurge();
|
|
ases_hash->disablePurge();
|
|
countries_hash->disablePurge();
|
|
vlans_hash->disablePurge();
|
|
macs_hash->disablePurge();
|
|
}
|
|
} else {
|
|
for(u_int8_t s = 0; s<numSubInterfaces; s++) {
|
|
if(on_flows)
|
|
subInterfaces[s]->get_flows_hash()->disablePurge();
|
|
else {
|
|
subInterfaces[s]->get_hosts_hash()->disablePurge();
|
|
subInterfaces[s]->get_ases_hash()->disablePurge();
|
|
subInterfaces[s]->get_countries_hash()->disablePurge();
|
|
subInterfaces[s]->get_vlans_hash()->disablePurge();
|
|
subInterfaces[s]->get_macs_hash()->disablePurge();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::enablePurge(bool on_flows) {
|
|
if(!isView()) {
|
|
if(on_flows)
|
|
flows_hash->enablePurge();
|
|
else {
|
|
hosts_hash->enablePurge();
|
|
ases_hash->enablePurge();
|
|
countries_hash->enablePurge();
|
|
vlans_hash->enablePurge();
|
|
macs_hash->enablePurge();
|
|
}
|
|
} else {
|
|
for(u_int8_t s = 0; s<numSubInterfaces; s++) {
|
|
if(on_flows)
|
|
subInterfaces[s]->get_flows_hash()->enablePurge();
|
|
else {
|
|
subInterfaces[s]->get_hosts_hash()->enablePurge();
|
|
subInterfaces[s]->get_ases_hash()->enablePurge();
|
|
subInterfaces[s]->get_countries_hash()->enablePurge();
|
|
subInterfaces[s]->get_vlans_hash()->enablePurge();
|
|
subInterfaces[s]->get_macs_hash()->enablePurge();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
int NetworkInterface::sortFlows(u_int32_t *begin_slot,
|
|
bool walk_all,
|
|
struct flowHostRetriever *retriever,
|
|
AddressTree *allowed_hosts,
|
|
Host *host,
|
|
Paginator *p,
|
|
const char *sortColumn) {
|
|
int (*sorter)(const void *_a, const void *_b);
|
|
|
|
if(retriever == NULL)
|
|
return -1;
|
|
|
|
retriever->pag = p;
|
|
retriever->host = host, retriever->location = location_all;
|
|
retriever->ndpi_proto = -1;
|
|
retriever->actNumEntries = 0, retriever->maxNumEntries = getFlowsHashSize(), retriever->allowed_hosts = allowed_hosts;
|
|
retriever->elems = (struct flowHostRetrieveList*)calloc(sizeof(struct flowHostRetrieveList), retriever->maxNumEntries);
|
|
|
|
if(retriever->elems == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Out of memory :-(");
|
|
return(-1);
|
|
}
|
|
|
|
if(!strcmp(sortColumn, "column_client")) retriever->sorter = column_client, sorter = hostSorter;
|
|
else if(!strcmp(sortColumn, "column_vlan")) retriever->sorter = column_vlan, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_server")) retriever->sorter = column_server, sorter = hostSorter;
|
|
else if(!strcmp(sortColumn, "column_proto_l4")) retriever->sorter = column_proto_l4, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_ndpi")) retriever->sorter = column_ndpi, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_duration")) retriever->sorter = column_duration, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_thpt")) retriever->sorter = column_thpt, sorter = numericSorter;
|
|
else if((!strcmp(sortColumn, "column_bytes")) || (!strcmp(sortColumn, "column_") /* default */)) retriever->sorter = column_bytes, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_info")) retriever->sorter = column_info, sorter = stringSorter;
|
|
else {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Unknown sort column %s", sortColumn);
|
|
retriever->sorter = column_bytes, sorter = numericSorter;
|
|
}
|
|
|
|
// make sure the caller has disabled the purge!!
|
|
walker(begin_slot, walk_all, walker_flows, flow_search_walker, (void*)retriever);
|
|
|
|
qsort(retriever->elems, retriever->actNumEntries, sizeof(struct flowHostRetrieveList), sorter);
|
|
|
|
return(retriever->actNumEntries);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
int NetworkInterface::getFlows(lua_State* vm,
|
|
AddressTree *allowed_hosts,
|
|
Host *host,
|
|
Paginator *p) {
|
|
struct flowHostRetriever retriever;
|
|
char sortColumn[32];
|
|
DetailsLevel highDetails;
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
if(p == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Unable to return results with a NULL paginator");
|
|
return(-1);
|
|
}
|
|
|
|
LocationPolicy client_mode = location_all;
|
|
LocationPolicy server_mode = location_all;
|
|
p->clientMode(&client_mode);
|
|
p->serverMode(&server_mode);
|
|
bool local_hosts = ((client_mode == location_local_only) && (server_mode == location_local_only));
|
|
|
|
snprintf(sortColumn, sizeof(sortColumn), "%s", p->sortColumn());
|
|
if(! p->getDetailsLevel(&highDetails))
|
|
highDetails = p->detailedResults() ? details_high : (local_hosts || (p && p->maxHits() != CONST_MAX_NUM_HITS)) ? details_high : details_normal;
|
|
|
|
disablePurge(true);
|
|
|
|
if(sortFlows(&begin_slot, walk_all, &retriever, allowed_hosts, host, p, sortColumn) < 0) {
|
|
enablePurge(true);
|
|
return -1;
|
|
}
|
|
|
|
lua_newtable(vm);
|
|
lua_push_uint64_table_entry(vm, "numFlows", retriever.actNumEntries);
|
|
|
|
lua_newtable(vm);
|
|
|
|
if(p->a2zSortOrder()) {
|
|
for(int i=p->toSkip(), num=0; i<(int)retriever.actNumEntries; i++) {
|
|
lua_newtable(vm);
|
|
|
|
retriever.elems[i].flow->lua(vm, allowed_hosts, highDetails, true);
|
|
|
|
lua_pushinteger(vm, num + 1);
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
|
|
if(++num >= (int)p->maxHits()) break;
|
|
}
|
|
} else {
|
|
for(int i=(retriever.actNumEntries-1-p->toSkip()), num=0; i>=0; i--) {
|
|
lua_newtable(vm);
|
|
|
|
retriever.elems[i].flow->lua(vm, allowed_hosts, highDetails, true);
|
|
|
|
lua_pushinteger(vm, num + 1);
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
|
|
if(++num >= (int)p->maxHits()) break;
|
|
}
|
|
}
|
|
|
|
lua_pushstring(vm, "flows");
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
|
|
enablePurge(true);
|
|
|
|
if(retriever.elems) free(retriever.elems);
|
|
|
|
return(retriever.actNumEntries);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
int NetworkInterface::getFlowsGroup(lua_State* vm,
|
|
AddressTree *allowed_hosts,
|
|
Paginator *p,
|
|
const char *groupColumn) {
|
|
struct flowHostRetriever retriever;
|
|
FlowGrouper *gper;
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
if(p == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Unable to return results with a NULL paginator");
|
|
return(-1);
|
|
}
|
|
|
|
disablePurge(true);
|
|
|
|
if(sortFlows(&begin_slot, walk_all, &retriever, allowed_hosts, NULL, p, groupColumn) < 0) {
|
|
enablePurge(true);
|
|
return -1;
|
|
}
|
|
|
|
// build a new grouper that will help in aggregating stats
|
|
if((gper = new(std::nothrow) FlowGrouper(retriever.sorter)) == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR,
|
|
"Unable to allocate memory for a Grouper.");
|
|
enablePurge(true);
|
|
return -1;
|
|
}
|
|
|
|
lua_newtable(vm);
|
|
|
|
for(int i=0; i<(int)retriever.actNumEntries; i++) {
|
|
Flow *flow = retriever.elems[i].flow;
|
|
|
|
if(flow) {
|
|
if(gper->inGroup(flow) == false) {
|
|
if(gper->getNumEntries() > 0)
|
|
gper->lua(vm);
|
|
gper->newGroup(flow);
|
|
}
|
|
|
|
gper->incStats(flow);
|
|
}
|
|
}
|
|
|
|
if(gper->getNumEntries() > 0)
|
|
gper->lua(vm);
|
|
|
|
delete gper;
|
|
enablePurge(true);
|
|
|
|
if(retriever.elems) free(retriever.elems);
|
|
|
|
return(retriever.actNumEntries);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
static bool flow_drop_walker(GenericHashEntry *h, void *user_data, bool *matched) {
|
|
struct flowHostRetriever *retriever = (struct flowHostRetriever*)user_data;
|
|
Flow *f = (Flow*)h;
|
|
|
|
if(flow_matches(f, retriever)) {
|
|
f->setDropVerdict();
|
|
*matched = true;
|
|
}
|
|
|
|
return(false); /* Keep on walking */
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
int NetworkInterface::dropFlowsTraffic(AddressTree *allowed_hosts, Paginator *p) {
|
|
struct flowHostRetriever retriever;
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
memset(&retriever, 0, sizeof(retriever));
|
|
|
|
retriever.allowed_hosts = allowed_hosts;
|
|
retriever.pag = p;
|
|
|
|
disablePurge(true);
|
|
|
|
walker(&begin_slot, walk_all, walker_flows, flow_drop_walker, (void*)&retriever);
|
|
|
|
enablePurge(true);
|
|
|
|
return(0);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
int NetworkInterface::sortHosts(u_int32_t *begin_slot,
|
|
bool walk_all,
|
|
struct flowHostRetriever *retriever,
|
|
u_int8_t bridge_iface_idx,
|
|
AddressTree *allowed_hosts,
|
|
bool host_details,
|
|
LocationPolicy location,
|
|
char *countryFilter, char *mac_filter,
|
|
u_int16_t vlan_id, char *osFilter,
|
|
u_int32_t asnFilter, int16_t networkFilter,
|
|
u_int16_t pool_filter, bool filtered_hosts,
|
|
bool blacklisted_hosts, bool hide_top_hidden,
|
|
bool anomalousOnly, bool dhcpOnly,
|
|
const AddressTree * const cidr_filter,
|
|
u_int8_t ipver_filter, int proto_filter,
|
|
TrafficType traffic_type_filter,
|
|
char *sortColumn) {
|
|
u_int32_t maxHits;
|
|
u_int8_t macAddr[6];
|
|
int (*sorter)(const void *_a, const void *_b);
|
|
|
|
if(retriever == NULL)
|
|
return -1;
|
|
|
|
maxHits = getHostsHashSize();
|
|
if((maxHits > CONST_MAX_NUM_HITS) || (maxHits == 0))
|
|
maxHits = CONST_MAX_NUM_HITS;
|
|
|
|
memset(retriever, 0, sizeof(struct flowHostRetriever));
|
|
|
|
if(mac_filter) {
|
|
Utils::parseMac(macAddr, mac_filter);
|
|
retriever->mac = macAddr;
|
|
} else {
|
|
retriever->mac = NULL;
|
|
}
|
|
|
|
retriever->allowed_hosts = allowed_hosts, retriever->location = location,
|
|
retriever->country = countryFilter, retriever->vlan_id = vlan_id,
|
|
retriever->osFilter = osFilter, retriever->asnFilter = asnFilter,
|
|
retriever->networkFilter = networkFilter, retriever->actNumEntries = 0,
|
|
retriever->poolFilter = pool_filter, retriever->bridge_iface_idx = 0,
|
|
retriever->ipVersionFilter = ipver_filter,
|
|
retriever->filteredHosts = filtered_hosts,
|
|
retriever->blacklistedHosts = blacklisted_hosts,
|
|
retriever->anomalousOnly = anomalousOnly,
|
|
retriever->dhcpOnly = dhcpOnly,
|
|
retriever->cidr_filter = cidr_filter,
|
|
retriever->hideTopHidden = hide_top_hidden,
|
|
retriever->ndpi_proto = proto_filter,
|
|
retriever->traffic_type = traffic_type_filter,
|
|
retriever->maxNumEntries = maxHits;
|
|
retriever->elems = (struct flowHostRetrieveList*)calloc(sizeof(struct flowHostRetrieveList), retriever->maxNumEntries);
|
|
|
|
if(retriever->elems == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Out of memory :-(");
|
|
return(-1);
|
|
}
|
|
|
|
if((!strcmp(sortColumn, "column_ip")) || (!strcmp(sortColumn, "column_"))) retriever->sorter = column_ip, sorter = hostSorter;
|
|
else if(!strcmp(sortColumn, "column_vlan")) retriever->sorter = column_vlan, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_alerts")) retriever->sorter = column_alerts, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_name")) retriever->sorter = column_name, sorter = stringSorter;
|
|
else if(!strcmp(sortColumn, "column_country")) retriever->sorter = column_country, sorter = stringSorter;
|
|
else if(!strcmp(sortColumn, "column_os")) retriever->sorter = column_os, sorter = stringSorter;
|
|
else if(!strcmp(sortColumn, "column_since")) retriever->sorter = column_since, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_asn")) retriever->sorter = column_asn, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_thpt")) retriever->sorter = column_thpt, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_num_flows")) retriever->sorter = column_num_flows, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_num_dropped_flows")) retriever->sorter = column_num_dropped_flows, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_traffic")) retriever->sorter = column_traffic, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_local_network_id")) retriever->sorter = column_local_network_id, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_local_network")) retriever->sorter = column_local_network, sorter = ipNetworkSorter;
|
|
else if(!strcmp(sortColumn, "column_mac")) retriever->sorter = column_mac, sorter = numericSorter;
|
|
/* criteria (datatype sortField in ntop_typedefs.h / see also host_search_walker:NetworkInterface.cpp) */
|
|
else if(!strcmp(sortColumn, "column_traffic_sent")) retriever->sorter = column_traffic_sent, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_traffic_rcvd")) retriever->sorter = column_traffic_rcvd, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_traffic_unknown")) retriever->sorter = column_traffic_unknown, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_num_flows_as_client")) retriever->sorter = column_num_flows_as_client, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_num_flows_as_server")) retriever->sorter = column_num_flows_as_server, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_total_num_anomalous_flows_as_client")) retriever->sorter = column_total_num_anomalous_flows_as_client, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_total_num_anomalous_flows_as_server")) retriever->sorter = column_total_num_anomalous_flows_as_server, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_total_alerts")) retriever->sorter = column_total_alerts, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_pool_id")) retriever->sorter = column_pool_id, sorter = numericSorter;
|
|
else {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Unknown sort column %s", sortColumn);
|
|
retriever->sorter = column_traffic, sorter = numericSorter;
|
|
}
|
|
|
|
// make sure the caller has disabled the purge!!
|
|
walker(begin_slot, walk_all, walker_hosts, host_search_walker, (void*)retriever);
|
|
|
|
qsort(retriever->elems, retriever->actNumEntries, sizeof(struct flowHostRetrieveList), sorter);
|
|
|
|
return(retriever->actNumEntries);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
int NetworkInterface::sortMacs(u_int32_t *begin_slot,
|
|
bool walk_all,
|
|
struct flowHostRetriever *retriever,
|
|
u_int8_t bridge_iface_idx,
|
|
bool sourceMacsOnly,
|
|
const char *manufacturer,
|
|
char *sortColumn, u_int16_t pool_filter,
|
|
u_int8_t devtype_filter, u_int8_t location_filter) {
|
|
u_int32_t maxHits;
|
|
int (*sorter)(const void *_a, const void *_b);
|
|
|
|
if(retriever == NULL)
|
|
return -1;
|
|
|
|
maxHits = getMacsHashSize();
|
|
if((maxHits > CONST_MAX_NUM_HITS) || (maxHits == 0))
|
|
maxHits = CONST_MAX_NUM_HITS;
|
|
|
|
retriever->sourceMacsOnly = sourceMacsOnly,
|
|
retriever->actNumEntries = 0,
|
|
retriever->poolFilter = pool_filter,
|
|
retriever->manufacturer = (char *)manufacturer,
|
|
retriever->maxNumEntries = maxHits,
|
|
retriever->devtypeFilter = devtype_filter,
|
|
retriever->locationFilter = location_filter,
|
|
retriever->ndpi_proto = -1,
|
|
retriever->elems = (struct flowHostRetrieveList*)calloc(sizeof(struct flowHostRetrieveList), retriever->maxNumEntries);
|
|
|
|
if(retriever->elems == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Out of memory :-(");
|
|
return(-1);
|
|
}
|
|
|
|
if((!strcmp(sortColumn, "column_mac")) || (!strcmp(sortColumn, "column_"))) retriever->sorter = column_mac, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_since")) retriever->sorter = column_since, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_thpt")) retriever->sorter = column_thpt, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_traffic")) retriever->sorter = column_traffic, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_hosts")) retriever->sorter = column_num_hosts, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_manufacturer")) retriever->sorter = column_manufacturer, sorter = stringSorter;
|
|
else if(!strcmp(sortColumn, "column_device_type")) retriever->sorter = column_device_type, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_arp_total")) retriever->sorter = column_arp_total, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_arp_sent")) retriever->sorter = column_arp_sent, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_arp_rcvd")) retriever->sorter = column_arp_rcvd, sorter = numericSorter;
|
|
else ntop->getTrace()->traceEvent(TRACE_WARNING, "Unknown sort column %s", sortColumn), sorter = numericSorter;
|
|
|
|
// make sure the caller has disabled the purge!!
|
|
walker(begin_slot, walk_all, walker_macs, mac_search_walker, (void*)retriever);
|
|
|
|
qsort(retriever->elems, retriever->actNumEntries, sizeof(struct flowHostRetrieveList), sorter);
|
|
|
|
return(retriever->actNumEntries);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
int NetworkInterface::sortASes(struct flowHostRetriever *retriever, char *sortColumn) {
|
|
u_int32_t maxHits;
|
|
int (*sorter)(const void *_a, const void *_b);
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
if(retriever == NULL)
|
|
return -1;
|
|
|
|
maxHits = getASesHashSize();
|
|
if((maxHits > CONST_MAX_NUM_HITS) || (maxHits == 0))
|
|
maxHits = CONST_MAX_NUM_HITS;
|
|
|
|
retriever->actNumEntries = 0,
|
|
retriever->maxNumEntries = maxHits,
|
|
retriever->elems = (struct flowHostRetrieveList*)calloc(sizeof(struct flowHostRetrieveList), retriever->maxNumEntries);
|
|
|
|
if(retriever->elems == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Out of memory :-(");
|
|
return(-1);
|
|
}
|
|
|
|
if((!strcmp(sortColumn, "column_asn")) || (!strcmp(sortColumn, "column_"))) retriever->sorter = column_asn, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_asname")) retriever->sorter = column_asname, sorter = stringSorter;
|
|
else if(!strcmp(sortColumn, "column_since")) retriever->sorter = column_since, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_thpt")) retriever->sorter = column_thpt, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_traffic")) retriever->sorter = column_traffic, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_hosts")) retriever->sorter = column_num_hosts, sorter = numericSorter;
|
|
else ntop->getTrace()->traceEvent(TRACE_WARNING, "Unknown sort column %s", sortColumn), sorter = numericSorter;
|
|
|
|
// make sure the caller has disabled the purge!!
|
|
walker(&begin_slot, walk_all, walker_ases, as_search_walker, (void*)retriever);
|
|
|
|
qsort(retriever->elems, retriever->actNumEntries, sizeof(struct flowHostRetrieveList), sorter);
|
|
|
|
return(retriever->actNumEntries);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
int NetworkInterface::sortCountries(struct flowHostRetriever *retriever,
|
|
char *sortColumn) {
|
|
u_int32_t maxHits;
|
|
int (*sorter)(const void *_a, const void *_b);
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
if(retriever == NULL)
|
|
return -1;
|
|
|
|
maxHits = getCountriesHashSize();
|
|
if((maxHits > CONST_MAX_NUM_HITS) || (maxHits == 0))
|
|
maxHits = CONST_MAX_NUM_HITS;
|
|
|
|
retriever->actNumEntries = 0,
|
|
retriever->maxNumEntries = maxHits,
|
|
retriever->elems = (struct flowHostRetrieveList*)calloc(sizeof(struct flowHostRetrieveList), retriever->maxNumEntries);
|
|
|
|
if(retriever->elems == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Out of memory :-(");
|
|
return(-1);
|
|
}
|
|
|
|
if((!strcmp(sortColumn, "column_country")) || (!strcmp(sortColumn, "column_"))) retriever->sorter = column_country, sorter = stringSorter;
|
|
else if(!strcmp(sortColumn, "column_since")) retriever->sorter = column_since, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_hosts")) retriever->sorter = column_num_hosts, sorter = numericSorter;
|
|
else ntop->getTrace()->traceEvent(TRACE_WARNING, "Unknown sort column %s", sortColumn), sorter = numericSorter;
|
|
|
|
// make sure the caller has disabled the purge!!
|
|
walker(&begin_slot, walk_all, walker_countries, country_search_walker, (void*)retriever);
|
|
|
|
qsort(retriever->elems, retriever->actNumEntries, sizeof(struct flowHostRetrieveList), sorter);
|
|
|
|
return(retriever->actNumEntries);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
int NetworkInterface::sortVLANs(struct flowHostRetriever *retriever, char *sortColumn) {
|
|
u_int32_t maxHits;
|
|
int (*sorter)(const void *_a, const void *_b);
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
if(retriever == NULL)
|
|
return -1;
|
|
|
|
maxHits = getVLANsHashSize();
|
|
if((maxHits > CONST_MAX_NUM_HITS) || (maxHits == 0))
|
|
maxHits = CONST_MAX_NUM_HITS;
|
|
|
|
retriever->actNumEntries = 0,
|
|
retriever->maxNumEntries = maxHits,
|
|
retriever->elems = (struct flowHostRetrieveList*)calloc(sizeof(struct flowHostRetrieveList), retriever->maxNumEntries);
|
|
|
|
if(retriever->elems == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Out of memory :-(");
|
|
return(-1);
|
|
}
|
|
|
|
if((!strcmp(sortColumn, "column_vlan")) || (!strcmp(sortColumn, "column_"))) retriever->sorter = column_vlan, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_since")) retriever->sorter = column_since, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_thpt")) retriever->sorter = column_thpt, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_traffic")) retriever->sorter = column_traffic, sorter = numericSorter;
|
|
else if(!strcmp(sortColumn, "column_hosts")) retriever->sorter = column_num_hosts, sorter = numericSorter;
|
|
else ntop->getTrace()->traceEvent(TRACE_WARNING, "Unknown sort column %s", sortColumn), sorter = numericSorter;
|
|
|
|
// make sure the caller has disabled the purge!!
|
|
walker(&begin_slot, walk_all, walker_vlans, vlan_search_walker, (void*)retriever);
|
|
|
|
qsort(retriever->elems, retriever->actNumEntries, sizeof(struct flowHostRetrieveList), sorter);
|
|
|
|
return(retriever->actNumEntries);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
int NetworkInterface::getActiveHostsList(lua_State* vm,
|
|
u_int32_t *begin_slot,
|
|
bool walk_all,
|
|
u_int8_t bridge_iface_idx,
|
|
AddressTree *allowed_hosts,
|
|
bool host_details, LocationPolicy location,
|
|
char *countryFilter, char *mac_filter,
|
|
u_int16_t vlan_id, char *osFilter,
|
|
u_int32_t asnFilter, int16_t networkFilter,
|
|
u_int16_t pool_filter, bool filtered_hosts,
|
|
bool blacklisted_hosts, bool hide_top_hidden,
|
|
u_int8_t ipver_filter, int proto_filter,
|
|
TrafficType traffic_type_filter, bool tsLua,
|
|
bool anomalousOnly, bool dhcpOnly,
|
|
const AddressTree * const cidr_filter,
|
|
char *sortColumn, u_int32_t maxHits,
|
|
u_int32_t toSkip, bool a2zSortOrder) {
|
|
struct flowHostRetriever retriever;
|
|
|
|
disablePurge(false);
|
|
|
|
#if DEBUG
|
|
if(!walk_all)
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "[BEGIN] %s(begin_slot=%u, walk_all=%u)",
|
|
__FUNCTION__, *begin_slot, walk_all);
|
|
#endif
|
|
|
|
if(sortHosts(begin_slot, walk_all,
|
|
&retriever, bridge_iface_idx,
|
|
allowed_hosts, host_details, location,
|
|
countryFilter, mac_filter, vlan_id, osFilter,
|
|
asnFilter, networkFilter, pool_filter, filtered_hosts, blacklisted_hosts, hide_top_hidden,
|
|
anomalousOnly, dhcpOnly,
|
|
cidr_filter,
|
|
ipver_filter, proto_filter,
|
|
traffic_type_filter,
|
|
sortColumn) < 0) {
|
|
enablePurge(false);
|
|
return -1;
|
|
}
|
|
|
|
#if DEBUG
|
|
if(!walk_all)
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "[END] %s(end_slot=%u, numHosts=%u)",
|
|
__FUNCTION__, *begin_slot, retriever.actNumEntries);
|
|
#endif
|
|
|
|
lua_newtable(vm);
|
|
lua_push_uint64_table_entry(vm, "numHosts", retriever.actNumEntries);
|
|
lua_push_uint64_table_entry(vm, "nextSlot", *begin_slot);
|
|
|
|
lua_newtable(vm);
|
|
|
|
if(a2zSortOrder) {
|
|
for(int i = toSkip, num=0; i<(int)retriever.actNumEntries && num < (int)maxHits; i++, num++) {
|
|
Host *h = retriever.elems[i].hostValue;
|
|
|
|
if(!tsLua)
|
|
h->lua(vm, NULL /* Already checked */, host_details, false, false, true);
|
|
else
|
|
h->tsLua(vm);
|
|
}
|
|
} else {
|
|
for(int i = (retriever.actNumEntries-1-toSkip), num=0; i >= 0 && num < (int)maxHits; i--, num++) {
|
|
Host *h = retriever.elems[i].hostValue;
|
|
|
|
if(!tsLua)
|
|
h->lua(vm, NULL /* Already checked */, host_details, false, false, true);
|
|
else
|
|
h->tsLua(vm);
|
|
}
|
|
}
|
|
|
|
lua_pushstring(vm, "hosts");
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
|
|
|
|
enablePurge(false);
|
|
|
|
// it's up to us to clean sorted data
|
|
// make sure first to free elements in case a string sorter has been used
|
|
if(retriever.sorter == column_name
|
|
|| retriever.sorter == column_country
|
|
|| retriever.sorter == column_os) {
|
|
for(u_int i=0; i<retriever.maxNumEntries; i++)
|
|
if(retriever.elems[i].stringValue)
|
|
free(retriever.elems[i].stringValue);
|
|
} else if(retriever.sorter == column_local_network)
|
|
for(u_int i=0; i<retriever.maxNumEntries; i++)
|
|
if(retriever.elems[i].ipValue)
|
|
delete retriever.elems[i].ipValue;
|
|
|
|
// finally free the elements regardless of the sorted kind
|
|
if(retriever.elems) free(retriever.elems);
|
|
|
|
return(retriever.actNumEntries);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
struct hosts_get_macs_retriever {
|
|
lua_State *vm;
|
|
int idx;
|
|
};
|
|
|
|
static bool hosts_get_macs(GenericHashEntry *he, void *user_data, bool *matched) {
|
|
struct hosts_get_macs_retriever *r = (struct hosts_get_macs_retriever *) user_data;
|
|
Host *host = (Host *)he;
|
|
Mac *mac = host->getMac();
|
|
char mac_buf[32], *mac_ptr;
|
|
char ip_buf[64];
|
|
|
|
if(mac && !mac->isSpecialMac() && host->get_ip()) {
|
|
mac_ptr = Utils::formatMac(mac->get_mac(), mac_buf, sizeof(mac_buf));
|
|
lua_getfield(r->vm, r->idx, mac_ptr);
|
|
|
|
if(lua_type(r->vm, -1) == LUA_TTABLE) {
|
|
lua_getfield(r->vm, -1, "ip");
|
|
|
|
if(lua_type(r->vm, -1) == LUA_TNIL) {
|
|
/* First assignment - create table */
|
|
lua_pop(r->vm, 1);
|
|
lua_pushstring(r->vm, "ip");
|
|
lua_newtable(r->vm);
|
|
lua_settable(r->vm, -3);
|
|
lua_getfield(r->vm, -1, "ip");
|
|
}
|
|
|
|
if(lua_type(r->vm, -1) == LUA_TTABLE) {
|
|
/* Add the ip address to the table */
|
|
lua_push_uint64_table_entry(r->vm, host->get_hostkey(ip_buf, sizeof(ip_buf)), host->get_ip()->isIPv4() ? 4 : 6);
|
|
}
|
|
|
|
lua_pop(r->vm, 1);
|
|
}
|
|
|
|
lua_pop(r->vm, 1);
|
|
*matched = true;
|
|
}
|
|
|
|
/* keep on iterating */
|
|
return (false);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
int NetworkInterface::getMacsIpAddresses(lua_State *vm, int idx) {
|
|
struct hosts_get_macs_retriever retriever;
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
retriever.vm = vm;
|
|
retriever.idx = idx;
|
|
|
|
walker(&begin_slot, walk_all, walker_hosts, hosts_get_macs, (void*)&retriever);
|
|
return 0;
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
int NetworkInterface::getActiveHostsGroup(lua_State* vm,
|
|
u_int32_t *begin_slot,
|
|
bool walk_all,
|
|
AddressTree *allowed_hosts,
|
|
bool host_details, LocationPolicy location,
|
|
char *countryFilter,
|
|
u_int16_t vlan_id, char *osFilter,
|
|
u_int32_t asnFilter, int16_t networkFilter,
|
|
u_int16_t pool_filter, bool filtered_hosts,
|
|
u_int8_t ipver_filter,
|
|
char *groupColumn) {
|
|
struct flowHostRetriever retriever;
|
|
Grouper *gper;
|
|
|
|
disablePurge(false);
|
|
|
|
// sort hosts according to the grouping criterion
|
|
if(sortHosts(begin_slot, walk_all,
|
|
&retriever, 0 /* bridge_iface_idx TODO */,
|
|
allowed_hosts, host_details, location,
|
|
countryFilter, NULL /* Mac */, vlan_id,
|
|
osFilter, asnFilter, networkFilter, pool_filter,
|
|
filtered_hosts, false /* no blacklisted hosts filter */, false, false, false,
|
|
NULL, /* no cidr filter */
|
|
ipver_filter, -1 /* no protocol filter */,
|
|
traffic_type_all /* no traffic type filter */,
|
|
groupColumn) < 0 ) {
|
|
enablePurge(false);
|
|
return -1;
|
|
}
|
|
|
|
// build a new grouper that will help in aggregating stats
|
|
if((gper = new(std::nothrow) Grouper(retriever.sorter)) == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR,
|
|
"Unable to allocate memory for a Grouper.");
|
|
enablePurge(false);
|
|
return -1;
|
|
}
|
|
|
|
lua_newtable(vm);
|
|
|
|
for(int i = 0; i < (int)retriever.actNumEntries; i++) {
|
|
Host *h = retriever.elems[i].hostValue;
|
|
|
|
if(h) {
|
|
if(gper->inGroup(h) == false) {
|
|
if(gper->getNumEntries() > 0)
|
|
gper->lua(vm);
|
|
gper->newGroup(h);
|
|
}
|
|
|
|
gper->incStats(h);
|
|
}
|
|
}
|
|
|
|
if(gper->getNumEntries() > 0)
|
|
gper->lua(vm);
|
|
|
|
delete gper;
|
|
gper = NULL;
|
|
|
|
enablePurge(false);
|
|
|
|
// it's up to us to clean sorted data
|
|
// make sure first to free elements in case a string sorter has been used
|
|
if((retriever.sorter == column_name)
|
|
|| (retriever.sorter == column_country)
|
|
|| (retriever.sorter == column_os)) {
|
|
for(u_int i=0; i<retriever.maxNumEntries; i++)
|
|
if(retriever.elems[i].stringValue)
|
|
free(retriever.elems[i].stringValue);
|
|
} else if(retriever.sorter == column_local_network)
|
|
for(u_int i=0; i<retriever.maxNumEntries; i++)
|
|
if(retriever.elems[i].ipValue)
|
|
delete retriever.elems[i].ipValue;
|
|
|
|
// finally free the elements regardless of the sorted kind
|
|
if(retriever.elems) free(retriever.elems);
|
|
|
|
return(retriever.actNumEntries);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
static bool flow_stats_walker(GenericHashEntry *h, void *user_data, bool *matched) {
|
|
struct active_flow_stats *stats = (struct active_flow_stats*)user_data;
|
|
Flow *flow = (Flow*)h;
|
|
|
|
stats->num_flows++,
|
|
stats->ndpi_bytes[flow->get_detected_protocol().app_protocol] += (u_int32_t)flow->get_bytes(),
|
|
stats->breeds_bytes[flow->get_protocol_breed()] += (u_int32_t)flow->get_bytes();
|
|
|
|
*matched = true;
|
|
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::getFlowsStats(lua_State* vm) {
|
|
struct active_flow_stats stats;
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
memset(&stats, 0, sizeof(stats));
|
|
walker(&begin_slot, walk_all, walker_flows, flow_stats_walker, (void*)&stats);
|
|
|
|
lua_newtable(vm);
|
|
lua_push_uint64_table_entry(vm, "num_flows", stats.num_flows);
|
|
|
|
lua_newtable(vm);
|
|
for(int i=0; i<NDPI_MAX_SUPPORTED_PROTOCOLS+NDPI_MAX_NUM_CUSTOM_PROTOCOLS; i++) {
|
|
if(stats.ndpi_bytes[i] > 0)
|
|
lua_push_uint64_table_entry(vm,
|
|
ndpi_get_proto_name(get_ndpi_struct(), i),
|
|
stats.ndpi_bytes[i]);
|
|
}
|
|
|
|
lua_pushstring(vm, "protos");
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
|
|
lua_newtable(vm);
|
|
for(int i=0; i<NUM_BREEDS; i++) {
|
|
if(stats.breeds_bytes[i] > 0)
|
|
lua_push_uint64_table_entry(vm,
|
|
ndpi_get_proto_breed_name(get_ndpi_struct(),
|
|
(ndpi_protocol_breed_t)i),
|
|
stats.breeds_bytes[i]);
|
|
}
|
|
|
|
lua_pushstring(vm, "breeds");
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
}
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::getNetworksStats(lua_State* vm) {
|
|
NetworkStats *network_stats;
|
|
u_int8_t num_local_networks = ntop->getNumLocalNetworks();
|
|
|
|
lua_newtable(vm);
|
|
for(u_int8_t network_id = 0; network_id < num_local_networks; network_id++) {
|
|
network_stats = getNetworkStats(network_id);
|
|
// do not add stats of networks that have not generated any traffic
|
|
if(!network_stats || !network_stats->trafficSeen())
|
|
continue;
|
|
lua_newtable(vm);
|
|
network_stats->lua(vm);
|
|
lua_push_int32_table_entry(vm, "network_id", network_id);
|
|
lua_pushstring(vm, ntop->getLocalNetworkName(network_id));
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
}
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
u_int NetworkInterface::purgeIdleFlows() {
|
|
u_int n = 0;
|
|
time_t last_packet_time = getTimeLastPktRcvd();
|
|
|
|
pollQueuedeBPFEvents();
|
|
reloadCustomCategories();
|
|
bcast_domains->inlineReloadBroadcastDomains();
|
|
|
|
if(!purge_idle_flows_hosts) return(0);
|
|
|
|
if(next_idle_flow_purge == 0) {
|
|
next_idle_flow_purge = last_packet_time + FLOW_PURGE_FREQUENCY;
|
|
return(0);
|
|
} else if(last_packet_time < next_idle_flow_purge)
|
|
return(0); /* Too early */
|
|
else {
|
|
/* Time to purge flows */
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_INFO,
|
|
"Purging idle flows [ifname: %s] [ifid: %i] [current size: %i]",
|
|
ifname, id, flows_hash->getCurrentSize());
|
|
n = flows_hash->purgeIdle();
|
|
|
|
next_idle_flow_purge = last_packet_time + FLOW_PURGE_FREQUENCY;
|
|
return(n);
|
|
}
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
u_int64_t NetworkInterface::getNumPackets() {
|
|
return(ethStats.getNumPackets());
|
|
};
|
|
|
|
/* **************************************************** */
|
|
|
|
u_int64_t NetworkInterface::getNumBytes() {
|
|
return(ethStats.getNumBytes());
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
u_int32_t NetworkInterface::getNumPacketDrops() {
|
|
return(!isDynamicInterface() ? getNumDroppedPackets() : 0);
|
|
};
|
|
|
|
/* **************************************************** */
|
|
|
|
u_int NetworkInterface::getNumFlows() {
|
|
return(flows_hash ? flows_hash->getNumEntries() : 0);
|
|
};
|
|
|
|
/* **************************************************** */
|
|
|
|
u_int NetworkInterface::getNumL2Devices() {
|
|
return(numL2Devices);
|
|
};
|
|
|
|
/* **************************************************** */
|
|
|
|
u_int NetworkInterface::getNumHosts() {
|
|
return(numHosts);
|
|
};
|
|
|
|
/* **************************************************** */
|
|
|
|
u_int NetworkInterface::getNumLocalHosts() {
|
|
return(numLocalHosts);
|
|
};
|
|
|
|
/* **************************************************** */
|
|
|
|
u_int NetworkInterface::getNumHTTPHosts() {
|
|
return(hosts_hash ? hosts_hash->getNumHTTPEntries() : 0);
|
|
};
|
|
|
|
/* **************************************************** */
|
|
|
|
u_int NetworkInterface::getNumMacs() {
|
|
return(macs_hash ? macs_hash->getNumEntries() : 0);
|
|
};
|
|
|
|
/* **************************************************** */
|
|
|
|
u_int NetworkInterface::getNumArpStatsMatrixElements() {
|
|
return(arp_hash_matrix ? arp_hash_matrix->getNumEntries() : 0);
|
|
};
|
|
|
|
/* **************************************************** */
|
|
|
|
u_int NetworkInterface::purgeIdleHostsMacsASesVlans() {
|
|
time_t last_packet_time = getTimeLastPktRcvd();
|
|
|
|
if(!purge_idle_flows_hosts) return(0);
|
|
|
|
if(next_idle_host_purge == 0) {
|
|
next_idle_host_purge = last_packet_time + HOST_PURGE_FREQUENCY;
|
|
return(0);
|
|
} else if(last_packet_time < next_idle_host_purge)
|
|
return(0); /* Too early */
|
|
else {
|
|
/* Time to purge hosts */
|
|
u_int n;
|
|
|
|
// ntop->getTrace()->traceEvent(TRACE_INFO, "Purging idle hosts");
|
|
n = hosts_hash->purgeIdle()
|
|
+ macs_hash->purgeIdle()
|
|
+ ases_hash->purgeIdle()
|
|
+ countries_hash->purgeIdle()
|
|
+ vlans_hash->purgeIdle();
|
|
|
|
if(arp_hash_matrix)
|
|
n += arp_hash_matrix->purgeIdle();
|
|
|
|
next_idle_host_purge = last_packet_time + HOST_PURGE_FREQUENCY;
|
|
return(n);
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void NetworkInterface::setnDPIProtocolCategory(u_int16_t protoId, ndpi_protocol_category_t protoCategory) {
|
|
ndpi_set_proto_category(ndpi_struct, protoId, protoCategory);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
static bool guess_all_ndpi_protocols_walker(GenericHashEntry *node, void *user_data, bool *matched) {
|
|
Flow *flow = (Flow*)node;
|
|
NetworkInterface *iface = (NetworkInterface*)user_data;
|
|
|
|
if(!flow->isDetectionCompleted() && iface->get_ndpi_struct() && flow->get_ndpi_flow())
|
|
flow->setDetectedProtocol(ndpi_detection_giveup(iface->get_ndpi_struct(), flow->get_ndpi_flow(), 1), true);
|
|
|
|
return(false /* keep walking */);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void NetworkInterface::guessAllnDPIProtocols() {
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
walker(&begin_slot, walk_all, walker_flows,
|
|
guess_all_ndpi_protocols_walker, this);
|
|
}
|
|
/* *************************************** */
|
|
|
|
void NetworkInterface::guessAllBroadcastDomainHosts() {
|
|
bcast_domains->inlineReloadBroadcastDomains(true);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::getnDPIProtocols(lua_State *vm, ndpi_protocol_category_t filter, bool skip_critical) {
|
|
int i;
|
|
u_int num_supported_protocols = ndpi_get_ndpi_num_supported_protocols(ndpi_struct);
|
|
ndpi_proto_defaults_t* proto_defaults = ndpi_get_proto_defaults(ndpi_struct);
|
|
|
|
lua_newtable(vm);
|
|
|
|
for(i=0; i<(int)num_supported_protocols; i++) {
|
|
char buf[8];
|
|
|
|
if((((u_int8_t)filter == (u_int8_t)-1)
|
|
|| proto_defaults[i].protoCategory == filter) &&
|
|
(!skip_critical || !Utils::isCriticalNetworkProtocol(i))) {
|
|
snprintf(buf, sizeof(buf), "%d", i);
|
|
if(!proto_defaults[i].protoName)
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "NULL protoname for index %d!!", i);
|
|
else
|
|
lua_push_str_table_entry(vm, proto_defaults[i].protoName, buf);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
#define NUM_TCP_STATES 4
|
|
/*
|
|
0 = RST
|
|
1 = SYN
|
|
2 = Established
|
|
3 = FIN
|
|
*/
|
|
|
|
static bool num_flows_state_walker(GenericHashEntry *node, void *user_data, bool *matched) {
|
|
Flow *flow = (Flow*)node;
|
|
u_int32_t *num_flows = (u_int32_t*)user_data;
|
|
|
|
if(flow->get_protocol() == IPPROTO_TCP) {
|
|
if(flow->isTCPEstablished())
|
|
num_flows[2]++;
|
|
else if(flow->isTCPConnecting())
|
|
num_flows[1]++;
|
|
else if(flow->isTCPReset())
|
|
num_flows[0]++;
|
|
else if(flow->isTCPClosed())
|
|
num_flows[3]++;
|
|
}
|
|
|
|
*matched = true;
|
|
|
|
return(false /* keep walking */);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
static bool num_flows_walker(GenericHashEntry *node, void *user_data, bool *matched) {
|
|
Flow *flow = (Flow*)node;
|
|
u_int32_t *num_flows = (u_int32_t*)user_data;
|
|
|
|
num_flows[flow->get_detected_protocol().app_protocol]++;
|
|
*matched = true;
|
|
|
|
return(false /* keep walking */);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void NetworkInterface::getFlowsStatus(lua_State *vm) {
|
|
u_int32_t num_flows[NUM_TCP_STATES] = { 0 };
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
walker(&begin_slot, walk_all, walker_flows, num_flows_state_walker, num_flows);
|
|
|
|
lua_push_uint64_table_entry(vm, "RST", num_flows[0]);
|
|
lua_push_uint64_table_entry(vm, "SYN", num_flows[1]);
|
|
lua_push_uint64_table_entry(vm, "Established", num_flows[2]);
|
|
lua_push_uint64_table_entry(vm, "FIN", num_flows[3]);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void NetworkInterface::getnDPIFlowsCount(lua_State *vm) {
|
|
u_int32_t *num_flows;
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
u_int num_supported_protocols = ndpi_get_ndpi_num_supported_protocols(ndpi_struct);
|
|
ndpi_proto_defaults_t* proto_defaults = ndpi_get_proto_defaults(ndpi_struct);
|
|
|
|
num_flows = (u_int32_t*)calloc(num_supported_protocols, sizeof(u_int32_t));
|
|
|
|
if(num_flows) {
|
|
walker(&begin_slot, walk_all, walker_flows, num_flows_walker, num_flows);
|
|
|
|
for(int i=0; i<(int)num_supported_protocols; i++) {
|
|
if(num_flows[i] > 0)
|
|
lua_push_uint64_table_entry(vm, proto_defaults[i].protoName,
|
|
num_flows[i]);
|
|
}
|
|
|
|
free(num_flows);
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void NetworkInterface::sumStats(TcpFlowStats *_tcpFlowStats,
|
|
EthStats *_ethStats,
|
|
LocalTrafficStats *_localStats,
|
|
nDPIStats *_ndpiStats,
|
|
PacketStats *_pktStats,
|
|
TcpPacketStats *_tcpPacketStats) {
|
|
tcpFlowStats.sum(_tcpFlowStats), ethStats.sum(_ethStats), localStats.sum(_localStats),
|
|
ndpiStats.sum(_ndpiStats), pktStats.sum(_pktStats), tcpPacketStats.sum(_tcpPacketStats);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void NetworkInterface::lua(lua_State *vm) {
|
|
TcpFlowStats _tcpFlowStats;
|
|
EthStats _ethStats;
|
|
LocalTrafficStats _localStats;
|
|
nDPIStats _ndpiStats;
|
|
PacketStats _pktStats;
|
|
TcpPacketStats _tcpPacketStats;
|
|
|
|
lua_newtable(vm);
|
|
|
|
lua_push_str_table_entry(vm, "name", get_name());
|
|
lua_push_str_table_entry(vm, "description", get_description());
|
|
lua_push_uint64_table_entry(vm, "scalingFactor", scalingFactor);
|
|
lua_push_uint64_table_entry(vm, "id", id);
|
|
if(customIftype) lua_push_str_table_entry(vm, "customIftype", (char*)customIftype);
|
|
lua_push_bool_table_entry(vm, "isView", isView()); /* View interface */
|
|
lua_push_bool_table_entry(vm, "isDynamic", isDynamicInterface()); /* An runtime-instantiated interface */
|
|
lua_push_uint64_table_entry(vm, "seen.last", getTimeLastPktRcvd());
|
|
lua_push_bool_table_entry(vm, "inline", get_inline_interface());
|
|
lua_push_bool_table_entry(vm, "vlan", hasSeenVlanTaggedPackets());
|
|
lua_push_bool_table_entry(vm, "has_macs", hasSeenMacAddresses());
|
|
lua_push_bool_table_entry(vm, "has_traffic_directions", (areTrafficDirectionsSupported() && (!is_traffic_mirrored)));
|
|
|
|
lua_newtable(vm);
|
|
lua_push_uint64_table_entry(vm, "packets", getNumPackets());
|
|
lua_push_uint64_table_entry(vm, "bytes", getNumBytes());
|
|
lua_push_uint64_table_entry(vm, "flows", getNumFlows());
|
|
lua_push_uint64_table_entry(vm, "hosts", getNumHosts());
|
|
lua_push_uint64_table_entry(vm, "local_hosts", getNumLocalHosts());
|
|
lua_push_uint64_table_entry(vm, "http_hosts", getNumHTTPHosts());
|
|
lua_push_uint64_table_entry(vm, "drops", getNumPacketDrops());
|
|
lua_push_uint64_table_entry(vm, "devices", getNumL2Devices());
|
|
lua_push_uint64_table_entry(vm, "current_macs", getNumMacs());
|
|
lua_push_uint64_table_entry(vm, "num_live_captures", num_live_captures);
|
|
|
|
if(db) db->lua(vm, false /* Overall */);
|
|
|
|
lua_pushstring(vm, "stats");
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
|
|
lua_newtable(vm);
|
|
lua_push_uint64_table_entry(vm, "packets", getNumPacketsSinceReset());
|
|
lua_push_uint64_table_entry(vm, "bytes", getNumBytesSinceReset());
|
|
lua_push_uint64_table_entry(vm, "drops", getNumPacketDropsSinceReset());
|
|
|
|
if(db) db->lua(vm, true /* Since last checkpoint */);
|
|
|
|
lua_pushstring(vm, "stats_since_reset");
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
|
|
lua_push_uint64_table_entry(vm, "remote_pps", last_remote_pps);
|
|
lua_push_uint64_table_entry(vm, "remote_bps", last_remote_bps);
|
|
icmp_v4.lua(true, vm);
|
|
icmp_v6.lua(false, vm);
|
|
lua_push_uint64_table_entry(vm, "arp.requests", arp_requests);
|
|
lua_push_uint64_table_entry(vm, "arp.replies", arp_replies);
|
|
lua_push_str_table_entry(vm, "type", (char*)get_type());
|
|
lua_push_uint64_table_entry(vm, "speed", ifSpeed);
|
|
lua_push_uint64_table_entry(vm, "mtu", ifMTU);
|
|
lua_push_uint64_table_entry(vm, "alertLevel", alertLevel);
|
|
lua_push_str_table_entry(vm, "ip_addresses", (char*)getLocalIPAddresses());
|
|
bcast_domains->lua(vm);
|
|
|
|
/* Anomalies */
|
|
lua_newtable(vm);
|
|
if(has_too_many_flows) lua_push_bool_table_entry(vm, "too_many_flows", true);
|
|
if(has_too_many_hosts) lua_push_bool_table_entry(vm, "too_many_hosts", true);
|
|
if(too_many_drops) lua_push_bool_table_entry(vm, "too_many_drops", true);
|
|
if(slow_stats_update) lua_push_bool_table_entry(vm, "slow_stats_update", true);
|
|
lua_pushstring(vm, "anomalies");
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
|
|
sumStats(&_tcpFlowStats, &_ethStats, &_localStats,
|
|
&_ndpiStats, &_pktStats, &_tcpPacketStats);
|
|
|
|
for(u_int8_t s = 0; s<numSubInterfaces; s++)
|
|
subInterfaces[s]->sumStats(&_tcpFlowStats, &_ethStats,
|
|
&_localStats, &_ndpiStats, &_pktStats, &_tcpPacketStats);
|
|
|
|
_tcpFlowStats.lua(vm, "tcpFlowStats");
|
|
_ethStats.lua(vm);
|
|
_localStats.lua(vm);
|
|
_ndpiStats.lua(this, vm, true);
|
|
_pktStats.lua(vm, "pktSizeDistribution");
|
|
_tcpPacketStats.lua(vm, "tcpPacketStats");
|
|
|
|
if(!isView()) {
|
|
#ifdef NTOPNG_PRO
|
|
#ifndef HAVE_NEDGE
|
|
if(flow_profiles) flow_profiles->lua(vm);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
if(host_pools)
|
|
host_pools->lua(vm);
|
|
|
|
#ifdef NTOPNG_PRO
|
|
if(custom_app_stats)
|
|
custom_app_stats->lua(vm);
|
|
#endif
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::runHousekeepingTasks() {
|
|
/* NOTE NOTE NOTE
|
|
|
|
This task runs asynchronously with respect to ntopng
|
|
so if you need to allocate memory you must LOCK
|
|
|
|
Example HTTPStats::updateHTTPHostRequest() is called
|
|
by both this function and the main thread
|
|
*/
|
|
|
|
periodicStatsUpdate();
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::runShutdownTasks() {
|
|
/* NOTE NOTE NOTE
|
|
This task runs asynchronously with respect to the datapath
|
|
*/
|
|
|
|
if (ntop->getPrefs()->flushFlowsOnShutdown()) {
|
|
/* Setting all flows as "ready to purge" (see isReadyToPurge) and dump them to the DB */
|
|
periodicStatsUpdate();
|
|
|
|
#ifdef NTOPNG_PRO
|
|
flushFlowDump();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
Mac* NetworkInterface::getMac(u_int8_t _mac[6], bool createIfNotPresent) {
|
|
Mac *ret = NULL;
|
|
|
|
if(_mac == NULL) return(NULL);
|
|
|
|
ret = macs_hash->get(_mac);
|
|
|
|
if((ret == NULL) && createIfNotPresent) {
|
|
try {
|
|
if((ret = new Mac(this, _mac)) != NULL) {
|
|
if(!macs_hash->add(ret)) {
|
|
delete ret;
|
|
return(NULL);
|
|
}
|
|
}
|
|
} catch(std::bad_alloc& ba) {
|
|
static bool oom_warning_sent = false;
|
|
|
|
if(!oom_warning_sent) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Not enough memory");
|
|
oom_warning_sent = true;
|
|
}
|
|
|
|
return(NULL);
|
|
}
|
|
}
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
ArpStatsMatrixElement* NetworkInterface::getArpHashMatrixElement(const u_int8_t _src_mac[6],
|
|
const u_int8_t _dst_mac[6],
|
|
bool * const src2dst){
|
|
ArpStatsMatrixElement *ret = NULL;
|
|
|
|
if(_src_mac == NULL || _dst_mac == NULL || arp_hash_matrix == NULL)
|
|
return NULL;
|
|
|
|
ret = arp_hash_matrix->get(_src_mac, _dst_mac, src2dst);
|
|
|
|
if(ret == NULL) {
|
|
try{
|
|
if((ret = new ArpStatsMatrixElement(this, _src_mac, _dst_mac, src2dst)) != NULL)
|
|
if(!arp_hash_matrix->add(ret)){
|
|
delete ret;
|
|
ret = NULL;
|
|
}
|
|
} catch(std::bad_alloc& ba) {
|
|
static bool oom_warning_sent = false;
|
|
|
|
if(!oom_warning_sent) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Not enough memory");
|
|
oom_warning_sent = true;
|
|
}
|
|
return(NULL);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
bool NetworkInterface::getArpStatsMatrixInfo(lua_State* vm){
|
|
if(getNumArpStatsMatrixElements() > 0) {
|
|
lua_newtable(vm);
|
|
arp_hash_matrix->lua(vm);
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
Vlan* NetworkInterface::getVlan(u_int16_t vlanId,
|
|
bool createIfNotPresent) {
|
|
Vlan *ret = NULL;
|
|
|
|
if(!isView())
|
|
ret = vlans_hash->get(vlanId);
|
|
else {
|
|
for(u_int8_t s = 0; s<numSubInterfaces; s++) {
|
|
if((ret = subInterfaces[s]->get_vlans_hash()->get(vlanId)) != NULL)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if((ret == NULL) && createIfNotPresent) {
|
|
try {
|
|
if((ret = new Vlan(this, vlanId)) != NULL) {
|
|
if(!vlans_hash->add(ret)) {
|
|
delete ret;
|
|
return(NULL);
|
|
}
|
|
}
|
|
} catch(std::bad_alloc& ba) {
|
|
static bool oom_warning_sent = false;
|
|
|
|
if(!oom_warning_sent) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Not enough memory");
|
|
oom_warning_sent = true;
|
|
}
|
|
|
|
return(NULL);
|
|
}
|
|
}
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
AutonomousSystem* NetworkInterface::getAS(IpAddress *ipa,
|
|
bool createIfNotPresent) {
|
|
AutonomousSystem *ret = NULL;
|
|
|
|
if(ipa == NULL) return(NULL);
|
|
|
|
if(!isView())
|
|
ret = ases_hash->get(ipa);
|
|
else {
|
|
for(u_int8_t s = 0; s<numSubInterfaces; s++) {
|
|
if((ret = subInterfaces[s]->get_ases_hash()->get(ipa)) != NULL)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if((ret == NULL) && createIfNotPresent) {
|
|
try {
|
|
if((ret = new AutonomousSystem(this, ipa)) != NULL) {
|
|
if(!ases_hash->add(ret)) {
|
|
delete ret;
|
|
return(NULL);
|
|
}
|
|
}
|
|
} catch(std::bad_alloc& ba) {
|
|
static bool oom_warning_sent = false;
|
|
|
|
if(!oom_warning_sent) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Not enough memory");
|
|
oom_warning_sent = true;
|
|
}
|
|
|
|
return(NULL);
|
|
}
|
|
}
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
Country* NetworkInterface::getCountry(const char *country_name,
|
|
bool createIfNotPresent) {
|
|
Country *ret = NULL;
|
|
|
|
if(!country_name || !country_name[0]) return(NULL);
|
|
|
|
if(!isView())
|
|
ret = countries_hash->get(country_name);
|
|
else {
|
|
for(u_int8_t s = 0; s<numSubInterfaces; s++) {
|
|
if((ret = subInterfaces[s]->get_countries_hash()->get(country_name)) != NULL)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if((ret == NULL) && createIfNotPresent) {
|
|
try {
|
|
if((ret = new Country(this, country_name)) != NULL) {
|
|
if(!countries_hash->add(ret)) {
|
|
delete ret;
|
|
return(NULL);
|
|
}
|
|
}
|
|
} catch(std::bad_alloc& ba) {
|
|
static bool oom_warning_sent = false;
|
|
|
|
if(!oom_warning_sent) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Not enough memory");
|
|
oom_warning_sent = true;
|
|
}
|
|
|
|
return(NULL);
|
|
}
|
|
}
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
Flow* NetworkInterface::findFlowByKey(u_int32_t key,
|
|
AddressTree *allowed_hosts) {
|
|
Flow *f = NULL;
|
|
|
|
f = (Flow*)(flows_hash->findByKey(key));
|
|
|
|
if(f && (!f->match(allowed_hosts))) f = NULL;
|
|
|
|
return(f);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
Flow* NetworkInterface::findFlowByTuple(u_int16_t vlan_id,
|
|
IpAddress *src_ip, IpAddress *dst_ip,
|
|
u_int16_t src_port, u_int16_t dst_port,
|
|
u_int8_t l4_proto,
|
|
AddressTree *allowed_hosts) const {
|
|
bool src2dst;
|
|
Flow *f = NULL;
|
|
|
|
f = (Flow*)flows_hash->find(src_ip, dst_ip, src_port, dst_port, vlan_id, l4_proto, NULL, &src2dst);
|
|
|
|
if(f && (!f->match(allowed_hosts))) f = NULL;
|
|
|
|
return(f);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
struct search_host_info {
|
|
lua_State *vm;
|
|
char *host_name_or_ip;
|
|
u_int num_matches;
|
|
AddressTree *allowed_hosts;
|
|
};
|
|
|
|
/* **************************************************** */
|
|
|
|
static bool hosts_search_walker(GenericHashEntry *h, void *user_data, bool *matched) {
|
|
Host *host = (Host*)h;
|
|
struct search_host_info *info = (struct search_host_info*)user_data;
|
|
|
|
if(host->addIfMatching(info->vm, info->allowed_hosts, info->host_name_or_ip)) {
|
|
info->num_matches++;
|
|
*matched = true;
|
|
}
|
|
|
|
/* Stop after CONST_MAX_NUM_FIND_HITS matches */
|
|
return((info->num_matches > CONST_MAX_NUM_FIND_HITS) ? true /* stop */ : false /* keep walking */);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
struct search_mac_info {
|
|
lua_State *vm;
|
|
u_int8_t *mac;
|
|
u_int num_matches;
|
|
};
|
|
|
|
/* **************************************************** */
|
|
|
|
static bool macs_search_walker(GenericHashEntry *h, void *user_data, bool *matched) {
|
|
Host *host = (Host*)h;
|
|
struct search_mac_info *info = (struct search_mac_info*)user_data;
|
|
|
|
if(host->addIfMatching(info->vm, info->mac)) {
|
|
info->num_matches++;
|
|
*matched = true;
|
|
}
|
|
|
|
/* Stop after CONST_MAX_NUM_FIND_HITS matches */
|
|
return((info->num_matches > CONST_MAX_NUM_FIND_HITS) ? true /* stop */ : false /* keep walking */);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
bool NetworkInterface::findHostsByMac(lua_State* vm, u_int8_t *mac) {
|
|
struct search_mac_info info;
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
info.vm = vm, info.mac = mac, info.num_matches = 0;
|
|
|
|
lua_newtable(vm);
|
|
walker(&begin_slot, walk_all, walker_hosts, macs_search_walker, (void*)&info);
|
|
return(info.num_matches > 0);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
bool NetworkInterface::findHostsByName(lua_State* vm,
|
|
AddressTree *allowed_hosts,
|
|
char *key) {
|
|
struct search_host_info info;
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
info.vm = vm, info.host_name_or_ip = key, info.num_matches = 0,
|
|
info.allowed_hosts = allowed_hosts;
|
|
|
|
lua_newtable(vm);
|
|
walker(&begin_slot, walk_all, walker_hosts, hosts_search_walker, (void*)&info);
|
|
return(info.num_matches > 0);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
u_int NetworkInterface::printAvailableInterfaces(bool printHelp, int idx,
|
|
char *ifname, u_int ifname_len) {
|
|
char ebuf[256];
|
|
int numInterfaces = 0;
|
|
pcap_if_t *devpointer;
|
|
|
|
if(printHelp && help_printed)
|
|
return(0);
|
|
|
|
ebuf[0] = '\0';
|
|
|
|
if(pcap_findalldevs(&devpointer, ebuf) < 0) {
|
|
;
|
|
} else {
|
|
if(ifname == NULL) {
|
|
if(printHelp)
|
|
printf("Available interfaces (-i <interface index>):\n");
|
|
else if(!help_printed)
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Available interfaces (-i <interface index>):");
|
|
}
|
|
|
|
for(int i = 0; devpointer != NULL; i++) {
|
|
if(Utils::validInterface(devpointer->description)) {
|
|
numInterfaces++;
|
|
|
|
if(ifname == NULL) {
|
|
if(printHelp) {
|
|
#ifdef WIN32
|
|
printf(" %d. %s\n"
|
|
"\t%s\n", numInterfaces,
|
|
devpointer->description ? devpointer->description : "",
|
|
devpointer->name);
|
|
#else
|
|
printf(" %d. %s\n", numInterfaces, devpointer->name);
|
|
#endif
|
|
} else if(!help_printed)
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "%d. %s (%s)\n",
|
|
numInterfaces, devpointer->name,
|
|
devpointer->description ? devpointer->description : devpointer->name);
|
|
} else if(numInterfaces == idx) {
|
|
snprintf(ifname, ifname_len, "%s", devpointer->name);
|
|
break;
|
|
}
|
|
}
|
|
|
|
devpointer = devpointer->next;
|
|
} /* for */
|
|
|
|
pcap_freealldevs(devpointer);
|
|
} /* else */
|
|
|
|
if(numInterfaces == 0) {
|
|
#ifdef WIN32
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "No interfaces available! This application cannot work");
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Make sure that winpcap is installed properly,");
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "that you have administrative rights,");
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "and that you have network interfaces installed.");
|
|
#else
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "No interfaces available: are you superuser?");
|
|
#endif
|
|
}
|
|
|
|
help_printed = true;
|
|
|
|
return(numInterfaces);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
bool NetworkInterface::isNumber(const char *str) {
|
|
while(*str) {
|
|
if(!isdigit(*str))
|
|
return(false);
|
|
|
|
str++;
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
struct proc_name_flows {
|
|
lua_State* vm;
|
|
char *proc_name;
|
|
};
|
|
|
|
static bool proc_name_finder_walker(GenericHashEntry *node, void *user_data, bool *matched) {
|
|
Flow *f = (Flow*)node;
|
|
struct proc_name_flows *info = (struct proc_name_flows*)user_data;
|
|
char *name = f->get_proc_name(true);
|
|
|
|
if(name && (strcmp(name, info->proc_name) == 0)) {
|
|
f->lua(info->vm, NULL, details_normal /* Minimum details */, false);
|
|
lua_pushinteger(info->vm, f->key()); // Key
|
|
lua_insert(info->vm, -2);
|
|
lua_settable(info->vm, -3);
|
|
} else {
|
|
name = f->get_proc_name(false);
|
|
|
|
if(name && (strcmp(name, info->proc_name) == 0)) {
|
|
f->lua(info->vm, NULL, details_normal /* Minimum details */, false);
|
|
lua_pushinteger(info->vm, f->key()); // Key
|
|
lua_insert(info->vm, -2);
|
|
lua_settable(info->vm, -3);
|
|
}
|
|
}
|
|
*matched = true;
|
|
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void NetworkInterface::findProcNameFlows(lua_State *vm, char *proc_name) {
|
|
struct proc_name_flows u;
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
u.vm = vm, u.proc_name = proc_name;
|
|
|
|
lua_newtable(vm);
|
|
walker(&begin_slot, walk_all, walker_flows, proc_name_finder_walker, &u);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
struct pid_flows {
|
|
lua_State* vm;
|
|
u_int32_t pid;
|
|
};
|
|
|
|
static bool pidfinder_walker(GenericHashEntry *node, void *pid_data, bool *matched) {
|
|
Flow *f = (Flow*)node;
|
|
struct pid_flows *info = (struct pid_flows*)pid_data;
|
|
|
|
if((f->getPid(true) == info->pid) || (f->getPid(false) == info->pid)) {
|
|
f->lua(info->vm, NULL, details_normal /* Minimum details */, false);
|
|
lua_pushinteger(info->vm, f->key()); // Key
|
|
lua_insert(info->vm, -2);
|
|
lua_settable(info->vm, -3);
|
|
*matched = true;
|
|
}
|
|
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
void NetworkInterface::findPidFlows(lua_State *vm, u_int32_t pid) {
|
|
struct pid_flows u;
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
u.vm = vm, u.pid = pid;
|
|
|
|
lua_newtable(vm);
|
|
walker(&begin_slot, walk_all, walker_flows, pidfinder_walker, &u);
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
struct virtual_host_valk_info {
|
|
lua_State *vm;
|
|
char *key;
|
|
u_int32_t num;
|
|
};
|
|
|
|
/* **************************************** */
|
|
|
|
static bool virtual_http_hosts_walker(GenericHashEntry *node, void *data, bool *matched) {
|
|
Host *h = (Host*)node;
|
|
struct virtual_host_valk_info *info = (struct virtual_host_valk_info*)data;
|
|
HTTPstats *s = h->getHTTPstats();
|
|
|
|
if(s) {
|
|
info->num += s->luaVirtualHosts(info->vm, info->key, h);
|
|
*matched = true;
|
|
}
|
|
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
void NetworkInterface::listHTTPHosts(lua_State *vm, char *key) {
|
|
struct virtual_host_valk_info info;
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
lua_newtable(vm);
|
|
|
|
info.vm = vm, info.key = key, info.num = 0;
|
|
walker(&begin_slot, walk_all, walker_hosts, virtual_http_hosts_walker, &info);
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
void NetworkInterface::addAllAvailableInterfaces() {
|
|
char ebuf[256] = { '\0' };
|
|
pcap_if_t *devpointer;
|
|
|
|
if(pcap_findalldevs(&devpointer, ebuf) < 0) {
|
|
;
|
|
} else {
|
|
for(int i = 0; devpointer != 0; i++) {
|
|
if(Utils::validInterface(devpointer->description)
|
|
&& (strncmp(devpointer->name, "virbr", 5) != 0) /* Ignore virtual interfaces */
|
|
&& Utils::isInterfaceUp(devpointer->name)
|
|
) {
|
|
ntop->getPrefs()->add_network_interface(devpointer->name,
|
|
devpointer->description);
|
|
} else
|
|
ntop->getTrace()->traceEvent(TRACE_INFO,
|
|
"Interface [%s][%s] not valid or down: discarded",
|
|
devpointer->name, devpointer->description);
|
|
|
|
devpointer = devpointer->next;
|
|
} /* for */
|
|
pcap_freealldevs(devpointer);
|
|
}
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
#ifdef NTOPNG_PRO
|
|
void NetworkInterface::refreshL7Rules() {
|
|
if(ntop->getPro()->has_valid_license() && policer)
|
|
policer->refreshL7Rules();
|
|
}
|
|
#endif
|
|
|
|
/* **************************************** */
|
|
|
|
#ifdef NTOPNG_PRO
|
|
void NetworkInterface::refreshShapers() {
|
|
if(ntop->getPro()->has_valid_license() && policer)
|
|
policer->refreshShapers();
|
|
}
|
|
#endif
|
|
|
|
/* **************************************** */
|
|
|
|
void NetworkInterface::addInterfaceAddress(char * const addr) {
|
|
if(ip_addresses.size() == 0)
|
|
ip_addresses = addr;
|
|
else {
|
|
string s = addr;
|
|
|
|
ip_addresses = ip_addresses + "," + s;
|
|
}
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
void NetworkInterface::addInterfaceNetwork(char * const net) {
|
|
interface_networks.addAddress(net);
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
bool NetworkInterface::isInterfaceNetwork(const IpAddress * const ipa, int network_bits) const {
|
|
return interface_networks.match(ipa, network_bits);
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
void NetworkInterface::allocateNetworkStats() {
|
|
u_int8_t numNetworks = ntop->getNumLocalNetworks();
|
|
|
|
try {
|
|
networkStats = new NetworkStats[numNetworks];
|
|
statsManager = new StatsManager(id, STATS_MANAGER_STORE_NAME);
|
|
alertsManager = new AlertsManager(id, ALERTS_MANAGER_STORE_NAME);
|
|
} catch(std::bad_alloc& ba) {
|
|
static bool oom_warning_sent = false;
|
|
|
|
if(!oom_warning_sent) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Not enough memory");
|
|
oom_warning_sent = true;
|
|
}
|
|
}
|
|
|
|
if(alertsManager)
|
|
alertLevel = alertsManager->getNumAlerts(true);
|
|
else
|
|
alertLevel = 0;
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
NetworkStats* NetworkInterface::getNetworkStats(u_int8_t networkId) {
|
|
if((networkStats == NULL) || (networkId >= ntop->getNumLocalNetworks()))
|
|
return(NULL);
|
|
else
|
|
return(&networkStats[networkId]);
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
void NetworkInterface::checkPointCounters(bool drops_only) {
|
|
if(!drops_only) {
|
|
checkpointPktCount = getNumPackets(),
|
|
checkpointBytesCount = getNumBytes();
|
|
}
|
|
checkpointPktDropCount = getNumPacketDrops();
|
|
|
|
if(db) db->checkPointCounters(drops_only);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
u_int64_t NetworkInterface::getCheckPointNumPackets() {
|
|
return(checkpointPktCount);
|
|
};
|
|
|
|
/* **************************************************** */
|
|
|
|
u_int64_t NetworkInterface::getCheckPointNumBytes() {
|
|
return(checkpointBytesCount);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
u_int32_t NetworkInterface::getCheckPointNumPacketDrops() {
|
|
return(checkpointPktDropCount);
|
|
};
|
|
|
|
/* **************************************** */
|
|
|
|
void NetworkInterface::processInterfaceStats(sFlowInterfaceStats *stats) {
|
|
if(interfaceStats == NULL)
|
|
interfaceStats = new InterfaceStatsHash(NUM_IFACE_STATS_HASH);
|
|
|
|
if(interfaceStats) {
|
|
char a[64];
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "[%s][ifIndex=%u]",
|
|
Utils::intoaV4(stats->deviceIP, a, sizeof(a)),
|
|
stats->ifIndex);
|
|
|
|
interfaceStats->set(stats->deviceIP, stats->ifIndex, stats);
|
|
}
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
ndpi_protocol_category_t NetworkInterface::get_ndpi_proto_category(u_int protoid) {
|
|
ndpi_protocol proto;
|
|
|
|
proto.app_protocol = NDPI_PROTOCOL_UNKNOWN;
|
|
proto.master_protocol = protoid;
|
|
proto.category = NDPI_PROTOCOL_CATEGORY_UNSPECIFIED;
|
|
return get_ndpi_proto_category(proto);
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
static bool host_reload_hide_from_top(GenericHashEntry *host, void *user_data, bool *matched) {
|
|
Host *h = (Host*)host;
|
|
|
|
h->reloadHideFromTop();
|
|
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
void NetworkInterface::reloadHideFromTop(bool refreshHosts) {
|
|
char kname[64];
|
|
char **networks = NULL;
|
|
VlanAddressTree *new_tree;
|
|
|
|
if(!ntop->getRedis()) return;
|
|
|
|
|
|
if ((new_tree = new VlanAddressTree) == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Not enough memory");
|
|
return;
|
|
}
|
|
|
|
snprintf(kname, sizeof(kname), CONST_IFACE_HIDE_FROM_TOP_PREFS, id);
|
|
|
|
int num_nets = ntop->getRedis()->smembers(kname, &networks);
|
|
char *at;
|
|
u_int16_t vlan_id;
|
|
|
|
for(int i=0; i<num_nets; i++) {
|
|
char *net = networks[i];
|
|
if(!net) continue;
|
|
|
|
if((at = strchr(net, '@'))) {
|
|
vlan_id = atoi(at + 1);
|
|
*at = '\0';
|
|
} else
|
|
vlan_id = 0;
|
|
|
|
new_tree->addAddress(vlan_id, net, 1);
|
|
free(net);
|
|
}
|
|
|
|
if(networks) free(networks);
|
|
|
|
if(hide_from_top_shadow) delete(hide_from_top_shadow);
|
|
hide_from_top_shadow = hide_from_top;
|
|
hide_from_top = new_tree;
|
|
|
|
if(refreshHosts) {
|
|
/* Reload existing hosts */
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
walker(&begin_slot, walk_all, walker_hosts, host_reload_hide_from_top, NULL);
|
|
}
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
bool NetworkInterface::isHiddenFromTop(Host *host) {
|
|
VlanAddressTree *vlan_addrtree = hide_from_top;
|
|
|
|
if(!vlan_addrtree) return false;
|
|
|
|
return(host->get_ip()->findAddress(vlan_addrtree->getAddressTree(host->getVlanId())));
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
int NetworkInterface::getActiveMacList(lua_State* vm,
|
|
u_int32_t *begin_slot,
|
|
bool walk_all,
|
|
u_int8_t bridge_iface_idx,
|
|
bool sourceMacsOnly,
|
|
const char *manufacturer,
|
|
char *sortColumn, u_int32_t maxHits,
|
|
u_int32_t toSkip, bool a2zSortOrder,
|
|
u_int16_t pool_filter, u_int8_t devtype_filter,
|
|
u_int8_t location_filter) {
|
|
struct flowHostRetriever retriever;
|
|
bool show_details = true;
|
|
|
|
disablePurge(false);
|
|
|
|
if(sortMacs(begin_slot, walk_all,
|
|
&retriever, bridge_iface_idx, sourceMacsOnly,
|
|
manufacturer, sortColumn,
|
|
pool_filter, devtype_filter, location_filter) < 0) {
|
|
enablePurge(false);
|
|
return -1;
|
|
}
|
|
|
|
lua_newtable(vm);
|
|
lua_push_uint64_table_entry(vm, "numMacs", retriever.actNumEntries);
|
|
lua_push_uint64_table_entry(vm, "nextSlot", *begin_slot);
|
|
|
|
lua_newtable(vm);
|
|
|
|
if(a2zSortOrder) {
|
|
for(int i = toSkip, num=0; i<(int)retriever.actNumEntries && num < (int)maxHits; i++, num++) {
|
|
Mac *m = retriever.elems[i].macValue;
|
|
|
|
m->lua(vm, show_details, false);
|
|
lua_rawseti(vm, -2, num + 1); /* Must use integer keys to preserve and iterate inorder with ipairs */
|
|
}
|
|
} else {
|
|
for(int i = (retriever.actNumEntries-1-toSkip), num=0; i >= 0 && num < (int)maxHits; i--, num++) {
|
|
Mac *m = retriever.elems[i].macValue;
|
|
|
|
m->lua(vm, show_details, false);
|
|
lua_rawseti(vm, -2, num + 1);
|
|
}
|
|
}
|
|
|
|
lua_pushstring(vm, "macs");
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
|
|
enablePurge(false);
|
|
|
|
// finally free the elements regardless of the sorted kind
|
|
if(retriever.elems) free(retriever.elems);
|
|
|
|
return(retriever.actNumEntries);
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
int NetworkInterface::getActiveASList(lua_State* vm, const Paginator *p) {
|
|
struct flowHostRetriever retriever;
|
|
DetailsLevel details_level;
|
|
|
|
if(!p)
|
|
return -1;
|
|
|
|
disablePurge(false);
|
|
|
|
if(sortASes(&retriever, p->sortColumn()) < 0) {
|
|
enablePurge(false);
|
|
return -1;
|
|
}
|
|
|
|
if(!p->getDetailsLevel(&details_level))
|
|
details_level = details_normal;
|
|
|
|
lua_newtable(vm);
|
|
lua_push_uint64_table_entry(vm, "numASes", retriever.actNumEntries);
|
|
|
|
lua_newtable(vm);
|
|
|
|
if(p->a2zSortOrder()) {
|
|
for(int i = p->toSkip(), num = 0; i < (int)retriever.actNumEntries && num < (int)p->maxHits(); i++, num++) {
|
|
AutonomousSystem *as = retriever.elems[i].asValue;
|
|
|
|
as->lua(vm, details_level, false);
|
|
lua_rawseti(vm, -2, num + 1); /* Must use integer keys to preserve and iterate inorder with ipairs */
|
|
}
|
|
} else {
|
|
for(int i = (retriever.actNumEntries - 1 - p->toSkip()), num = 0; i >= 0 && num < (int)p->maxHits(); i--, num++) {
|
|
AutonomousSystem *as = retriever.elems[i].asValue;
|
|
|
|
as->lua(vm, details_level, false);
|
|
lua_rawseti(vm, -2, num + 1);
|
|
}
|
|
}
|
|
|
|
lua_pushstring(vm, "ASes");
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
|
|
enablePurge(false);
|
|
|
|
// finally free the elements regardless of the sorted kind
|
|
if(retriever.elems) free(retriever.elems);
|
|
|
|
return(retriever.actNumEntries);
|
|
}
|
|
|
|
|
|
/* **************************************** */
|
|
|
|
int NetworkInterface::getActiveCountriesList(lua_State* vm, const Paginator *p) {
|
|
struct flowHostRetriever retriever;
|
|
DetailsLevel details_level;
|
|
|
|
if(!p)
|
|
return -1;
|
|
|
|
disablePurge(false);
|
|
|
|
if(sortCountries(&retriever, p->sortColumn()) < 0) {
|
|
enablePurge(false);
|
|
return -1;
|
|
}
|
|
|
|
if(!p->getDetailsLevel(&details_level))
|
|
details_level = details_normal;
|
|
|
|
lua_newtable(vm);
|
|
lua_push_uint64_table_entry(vm, "numCountries", retriever.actNumEntries);
|
|
|
|
lua_newtable(vm);
|
|
|
|
if(p->a2zSortOrder()) {
|
|
for(int i = p->toSkip(), num = 0; i < (int)retriever.actNumEntries && num < (int)p->maxHits(); i++, num++) {
|
|
Country *country = retriever.elems[i].countryVal;
|
|
|
|
country->lua(vm, details_level, false);
|
|
lua_rawseti(vm, -2, num + 1); /* Must use integer keys to preserve and iterate inorder with ipairs */
|
|
}
|
|
} else {
|
|
for(int i = (retriever.actNumEntries - 1 - p->toSkip()), num = 0; i >= 0 && num < (int)p->maxHits(); i--, num++) {
|
|
Country *country = retriever.elems[i].countryVal;
|
|
|
|
country->lua(vm, details_level, false);
|
|
lua_rawseti(vm, -2, num + 1);
|
|
}
|
|
}
|
|
|
|
lua_pushstring(vm, "Countries");
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
|
|
enablePurge(false);
|
|
|
|
// finally free the elements regardless of the sorted kind
|
|
if(retriever.elems) free(retriever.elems);
|
|
|
|
return(retriever.actNumEntries);
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
int NetworkInterface::getActiveVLANList(lua_State* vm,
|
|
char *sortColumn, u_int32_t maxHits,
|
|
u_int32_t toSkip, bool a2zSortOrder,
|
|
DetailsLevel details_level) {
|
|
struct flowHostRetriever retriever;
|
|
|
|
if(! hasSeenVlanTaggedPackets()) {
|
|
/* VLAN statistics are calculated only if VLAN tagged traffic has been seen */
|
|
lua_pushnil(vm);
|
|
return 0;
|
|
}
|
|
|
|
disablePurge(false);
|
|
|
|
if(sortVLANs(&retriever, sortColumn) < 0) {
|
|
enablePurge(false);
|
|
return -1;
|
|
}
|
|
|
|
lua_newtable(vm);
|
|
lua_push_uint64_table_entry(vm, "numVLANs", retriever.actNumEntries);
|
|
|
|
lua_newtable(vm);
|
|
|
|
if(a2zSortOrder) {
|
|
for(int i = toSkip, num = 0; i<(int)retriever.actNumEntries && num < (int)maxHits; i++, num++) {
|
|
Vlan *vl = retriever.elems[i].vlanValue;
|
|
|
|
vl->lua(vm, details_level, false);
|
|
lua_rawseti(vm, -2, num + 1); /* Must use integer keys to preserve and iterate inorder with ipairs */
|
|
}
|
|
} else {
|
|
for(int i = (retriever.actNumEntries-1-toSkip), num = 0; i >= 0 && num < (int)maxHits; i--, num++) {
|
|
Vlan *vl = retriever.elems[i].vlanValue;
|
|
|
|
vl->lua(vm, details_level, false);
|
|
lua_rawseti(vm, -2, num + 1);
|
|
}
|
|
}
|
|
|
|
lua_pushstring(vm, "VLANs");
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
|
|
enablePurge(false);
|
|
|
|
// finally free the elements regardless of the sorted kind
|
|
if(retriever.elems) free(retriever.elems);
|
|
|
|
return(retriever.actNumEntries);
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
int NetworkInterface::getActiveMacManufacturers(lua_State* vm,
|
|
u_int8_t bridge_iface_idx,
|
|
bool sourceMacsOnly,
|
|
u_int32_t maxHits,
|
|
u_int8_t devtype_filter, u_int8_t location_filter) {
|
|
struct flowHostRetriever retriever;
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
disablePurge(false);
|
|
|
|
if(sortMacs(&begin_slot, walk_all,
|
|
&retriever, bridge_iface_idx, sourceMacsOnly,
|
|
NULL, (char*)"column_manufacturer",
|
|
(u_int16_t)-1, devtype_filter, location_filter) < 0) {
|
|
enablePurge(false);
|
|
return -1;
|
|
}
|
|
|
|
lua_newtable(vm);
|
|
|
|
const char *cur_manuf = NULL;
|
|
u_int32_t cur_count = 0;
|
|
int k = 0;
|
|
|
|
for(int i = 0; i<(int)retriever.actNumEntries && k < (int)maxHits; i++) {
|
|
Mac *m = retriever.elems[i].macValue;
|
|
|
|
const char *manufacturer = m->get_manufacturer();
|
|
if(manufacturer != NULL) {
|
|
if(!cur_manuf || (strcmp(cur_manuf, manufacturer) != 0)) {
|
|
if(cur_manuf != NULL)
|
|
lua_push_int32_table_entry(vm, cur_manuf, cur_count);
|
|
|
|
cur_manuf = manufacturer;
|
|
cur_count = 1;
|
|
k++;
|
|
} else {
|
|
cur_count++;
|
|
}
|
|
}
|
|
}
|
|
if(cur_manuf != NULL)
|
|
lua_push_int32_table_entry(vm, cur_manuf, cur_count);
|
|
|
|
enablePurge(false);
|
|
|
|
// finally free the elements regardless of the sorted kind
|
|
if(retriever.elems) free(retriever.elems);
|
|
|
|
return(retriever.actNumEntries);
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
int NetworkInterface::getActiveDeviceTypes(lua_State* vm,
|
|
u_int8_t bridge_iface_idx,
|
|
bool sourceMacsOnly,
|
|
u_int32_t maxHits,
|
|
const char *manufacturer, u_int8_t location_filter) {
|
|
struct flowHostRetriever retriever;
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
disablePurge(false);
|
|
|
|
if(sortMacs(&begin_slot, walk_all,
|
|
&retriever, bridge_iface_idx, sourceMacsOnly,
|
|
manufacturer, (char*)"column_device_type",
|
|
(u_int16_t)-1, (u_int8_t)-1, location_filter) < 0) {
|
|
enablePurge(false);
|
|
return -1;
|
|
}
|
|
|
|
lua_newtable(vm);
|
|
|
|
u_int8_t cur_devtype = 0;
|
|
u_int32_t cur_count = 0;
|
|
int k = 0;
|
|
|
|
for(int i = 0; i<(int)retriever.actNumEntries && k < (int)maxHits; i++) {
|
|
Mac *m = retriever.elems[i].macValue;
|
|
|
|
if(m->getDeviceType() != cur_devtype) {
|
|
if(cur_count) {
|
|
lua_pushinteger(vm, cur_devtype);
|
|
lua_pushinteger(vm, cur_count);
|
|
lua_settable(vm, -3);
|
|
}
|
|
|
|
cur_devtype = m->getDeviceType();
|
|
cur_count = 1;
|
|
k++;
|
|
} else {
|
|
cur_count++;
|
|
}
|
|
}
|
|
|
|
if(cur_count) {
|
|
lua_pushinteger(vm, cur_devtype);
|
|
lua_pushinteger(vm, cur_count);
|
|
lua_settable(vm, -3);
|
|
}
|
|
|
|
enablePurge(false);
|
|
|
|
// finally free the elements regardless of the sorted kind
|
|
if(retriever.elems) free(retriever.elems);
|
|
|
|
return(retriever.actNumEntries);
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
bool NetworkInterface::getMacInfo(lua_State* vm, char *mac) {
|
|
struct mac_find_info info;
|
|
bool ret;
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
Utils::parseMac(info.mac, mac);
|
|
|
|
disablePurge(false);
|
|
|
|
walker(&begin_slot, walk_all, walker_macs, find_mac_by_name, (void*)&info);
|
|
|
|
if(info.m) {
|
|
info.m->lua(vm, true, false);
|
|
ret = true;
|
|
} else
|
|
ret = false;
|
|
|
|
enablePurge(false);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
bool NetworkInterface::resetMacStats(lua_State* vm, char *mac, bool delete_data) {
|
|
struct mac_find_info info;
|
|
bool ret;
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
Utils::parseMac(info.mac, mac);
|
|
|
|
disablePurge(false);
|
|
|
|
walker(&begin_slot, walk_all, walker_macs, find_mac_by_name, (void*)&info);
|
|
|
|
if(info.m) {
|
|
if(delete_data)
|
|
info.m->requestDataReset();
|
|
else
|
|
info.m->requestStatsReset();
|
|
ret = true;
|
|
} else
|
|
ret = false;
|
|
|
|
enablePurge(false);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
bool NetworkInterface::setMacOperatingSystem(lua_State* vm, char *strmac, OperatingSystem os) {
|
|
u_int8_t mac[6];
|
|
Mac *m;
|
|
|
|
Utils::parseMac(mac, strmac);
|
|
|
|
if((m = getMac(mac, false /* Don't create if missing */))) {
|
|
m->setOperatingSystem(os);
|
|
return(true);
|
|
} else
|
|
return(false);
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
bool NetworkInterface::setMacDeviceType(char *strmac,
|
|
DeviceType dtype, bool alwaysOverwrite) {
|
|
u_int8_t mac[6];
|
|
Mac *m;
|
|
DeviceType oldtype;
|
|
|
|
Utils::parseMac(mac, strmac);
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "setMacDeviceType(%s) = %d", strmac, (int)dtype);
|
|
|
|
if((m = getMac(mac, false /* Don't create if missing */))) {
|
|
oldtype = m->getDeviceType();
|
|
|
|
if(alwaysOverwrite || (oldtype == device_unknown)) {
|
|
m->forceDeviceType(dtype);
|
|
|
|
if(alwaysOverwrite && (oldtype != device_unknown) && (oldtype != dtype))
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "Device %s type changed from %d to %d\n",
|
|
strmac, oldtype, dtype);
|
|
}
|
|
return(true);
|
|
} else
|
|
return(false);
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
bool NetworkInterface::getASInfo(lua_State* vm, u_int32_t asn) {
|
|
struct as_find_info info;
|
|
bool ret;
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
info.asn = asn;
|
|
|
|
disablePurge(false);
|
|
|
|
walker(&begin_slot, walk_all, walker_ases, find_as_by_asn, (void*)&info);
|
|
|
|
if(info.as) {
|
|
info.as->lua(vm, details_higher, false);
|
|
ret = true;
|
|
} else
|
|
ret = false;
|
|
|
|
enablePurge(false);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
bool NetworkInterface::getVLANInfo(lua_State* vm, u_int16_t vlan_id) {
|
|
struct vlan_find_info info;
|
|
bool ret;
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
info.vlan_id = vlan_id;
|
|
|
|
disablePurge(false);
|
|
|
|
walker(&begin_slot, walk_all, walker_vlans, find_vlan_by_vlan_id, (void*)&info);
|
|
|
|
if(info.vl) {
|
|
info.vl->lua(vm, details_higher, false);
|
|
ret = true;
|
|
} else
|
|
ret = false;
|
|
|
|
enablePurge(false);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
static bool host_reload_alert_prefs(GenericHashEntry *host, void *user_data, bool *matched) {
|
|
bool full_refresh = (user_data != NULL) ? true : false;
|
|
Host *h = (Host*)host;
|
|
|
|
h->refreshHostAlertPrefs();
|
|
*matched = true;
|
|
|
|
if(full_refresh)
|
|
h->loadAlertsCounter();
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
void NetworkInterface::refreshHostsAlertPrefs(bool full_refresh) {
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
/* Read the new configuration */
|
|
ntop->getPrefs()->refreshHostsAlertsPrefs();
|
|
|
|
disablePurge(false);
|
|
|
|
/* Update the hosts */
|
|
walker(&begin_slot, walk_all, walker_hosts,
|
|
host_reload_alert_prefs, (void *)full_refresh);
|
|
|
|
enablePurge(false);
|
|
};
|
|
|
|
/* **************************************** */
|
|
|
|
int NetworkInterface::updateHostTrafficPolicy(AddressTree* allowed_networks,
|
|
char *host_ip, u_int16_t host_vlan) {
|
|
Host *h;
|
|
int rv;
|
|
disablePurge(false);
|
|
|
|
if((h = findHostByIP(allowed_networks, host_ip, host_vlan)) != NULL) {
|
|
h->updateHostTrafficPolicy(host_ip);
|
|
rv = CONST_LUA_OK;
|
|
} else
|
|
rv = CONST_LUA_ERROR;
|
|
|
|
enablePurge(false);
|
|
return rv;
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void NetworkInterface::topItemsCommit(const struct timeval *tv) {
|
|
float tdiff_msec = ((float)(tv->tv_sec-last_frequent_reset.tv_sec)*1000)+((tv->tv_usec-last_frequent_reset.tv_usec)/(float)1000);
|
|
|
|
frequentProtocols->reset(tdiff_msec);
|
|
frequentMacs->reset(tdiff_msec);
|
|
|
|
last_frequent_reset = *tv;
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void NetworkInterface::topProtocolsAdd(u_int16_t pool_id, u_int16_t protocol, u_int32_t bytes) {
|
|
if((bytes > 0) && (pool_id != 0)) {
|
|
// frequentProtocols->addPoolProtocol(pool_id, proto->master_protocol, bytes);
|
|
frequentProtocols->addPoolProtocol(pool_id, protocol, bytes);
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void NetworkInterface::topMacsAdd(Mac *mac, u_int16_t protocol, u_int32_t bytes) {
|
|
if((bytes > 0) && (! mac->isSpecialMac()) && (mac->locate() == located_on_lan_interface)) {
|
|
// frequentProtocols->addPoolProtocol(pool_id, proto->master_protocol, bytes);
|
|
frequentMacs->addMacProtocol(mac->get_mac(), protocol, bytes);
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
#ifdef HAVE_NINDEX
|
|
NIndexFlowDB* NetworkInterface::getNindex() {
|
|
return(ntop->getPrefs()->do_dump_flows_on_nindex() ? (NIndexFlowDB*)db : NULL);
|
|
}
|
|
#endif
|
|
|
|
/* *************************************** */
|
|
|
|
void NetworkInterface::checkMacIPAssociation(bool triggerEvent, u_char *_mac, u_int32_t ipv4) {
|
|
if(!ntop->getPrefs()->are_ip_reassignment_alerts_enabled())
|
|
return;
|
|
|
|
u_int64_t mac = Utils::mac2int(_mac);
|
|
|
|
if((ipv4 != 0) && (mac != 0) && (mac != 0xFFFFFFFFFFFF)) {
|
|
std::map<u_int32_t, u_int64_t>::iterator it;
|
|
|
|
if(!triggerEvent)
|
|
ip_mac[ipv4] = mac;
|
|
else {
|
|
if((it = ip_mac.find(ipv4)) != ip_mac.end()) {
|
|
/* Found entry */
|
|
if(it->second != mac) {
|
|
char oldmac[32], newmac[32], ipbuf[32], *ipa;
|
|
u_char tmp[6];
|
|
json_object *jobject;
|
|
|
|
Utils::int2mac(it->second, tmp);
|
|
Utils::formatMac(tmp, oldmac, sizeof(oldmac));
|
|
Utils::formatMac(_mac, newmac, sizeof(newmac));
|
|
ipa = Utils::intoaV4(ntohl(ipv4), ipbuf, sizeof(ipbuf));
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "IP %s: modified MAC association %s -> %s",
|
|
ipa, oldmac, newmac);
|
|
|
|
if((jobject = json_object_new_object()) != NULL) {
|
|
json_object_object_add(jobject, "ifname", json_object_new_string(get_name()));
|
|
json_object_object_add(jobject, "ifid", json_object_new_int(id));
|
|
json_object_object_add(jobject, "ip", json_object_new_string(ipa));
|
|
json_object_object_add(jobject, "old_mac", json_object_new_string(oldmac));
|
|
json_object_object_add(jobject, "new_mac", json_object_new_string(newmac));
|
|
|
|
ntop->getRedis()->rpush(CONST_ALERT_MAC_IP_QUEUE, (char *)json_object_to_json_string(jobject), 0 /* No trim */);
|
|
|
|
/* Free Memory */
|
|
json_object_put(jobject);
|
|
} else
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "json_object_new_object: Not enough memory");
|
|
|
|
ip_mac[ipv4] = mac;
|
|
}
|
|
} else
|
|
ip_mac[ipv4] = mac;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
bool NetworkInterface::checkBroadcastDomainTooLarge(u_int32_t bcast_mask, u_int16_t vlan_id, const Mac * const src_mac, const Mac * const dst_mac, u_int32_t spa, u_int32_t tpa) const {
|
|
if(bcast_mask < 0xFFFF0000) {
|
|
if(!ntop->getPrefs()->are_alerts_disabled()) {
|
|
json_object *jobject;
|
|
char buf[32];
|
|
|
|
if((jobject = json_object_new_object()) != NULL) {
|
|
json_object_object_add(jobject, "ifname", json_object_new_string(get_name()));
|
|
json_object_object_add(jobject, "ifid", json_object_new_int(id));
|
|
json_object_object_add(jobject, "vlan_id", json_object_new_int(vlan_id));
|
|
json_object_object_add(jobject, "src_mac", json_object_new_string(Utils::formatMac(src_mac->get_mac(), buf, sizeof(buf))));
|
|
json_object_object_add(jobject, "dst_mac", json_object_new_string(Utils::formatMac(dst_mac->get_mac(), buf, sizeof(buf))));
|
|
json_object_object_add(jobject, "spa", json_object_new_string(Utils::intoaV4(spa, buf, sizeof(buf))));
|
|
json_object_object_add(jobject, "tpa", json_object_new_string(Utils::intoaV4(tpa, buf, sizeof(buf))));
|
|
|
|
ntop->getRedis()->rpush(CONST_ALERT_BCAST_DOMAIN_TOO_LARGE_QUEUE, (char *)json_object_to_json_string(jobject), 0 /* No trim */);
|
|
|
|
/* Free Memory */
|
|
json_object_put(jobject);
|
|
}
|
|
}
|
|
|
|
return true; /* At most a /16 broadcast domain is acceptable */
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
/*
|
|
Put here all the code that is executed when the NIC initialization
|
|
is succesful
|
|
*/
|
|
void NetworkInterface::finishInitialization(u_int8_t num_defined_interfaces) {
|
|
if(!isView()) {
|
|
#if defined(NTOPNG_PRO) && defined(HAVE_NINDEX)
|
|
if(ntop->getPrefs()->do_dump_flows_on_nindex()) {
|
|
if(num_defined_interfaces + 1 >= NINDEX_MAX_NUM_INTERFACES) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR,
|
|
"nIndex cannot be enabled for %s.", get_name());
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR,
|
|
"The maximum number of interfaces that can be used with nIndex is %d.",
|
|
NINDEX_MAX_NUM_INTERFACES);
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR,
|
|
"Interface will continue to work without nIndex support.");
|
|
} else {
|
|
db = new NIndexFlowDB(this);
|
|
goto enable_aggregation;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if(db == NULL) {
|
|
if(ntop->getPrefs()->do_dump_flows_on_mysql()
|
|
|| ntop->getPrefs()->do_read_flows_from_nprobe_mysql()) {
|
|
#ifdef NTOPNG_PRO
|
|
if(ntop->getPrefs()->is_enterprise_edition()
|
|
&& !ntop->getPrefs()->do_read_flows_from_nprobe_mysql()) {
|
|
#ifdef HAVE_MYSQL
|
|
db = new BatchedMySQLDB(this);
|
|
#endif
|
|
|
|
#if defined(NTOPNG_PRO) && defined(HAVE_NINDEX)
|
|
enable_aggregation:
|
|
#endif
|
|
aggregated_flows_hash = new AggregatedFlowHash(this, num_hashes,
|
|
ntop->getPrefs()->get_max_num_flows());
|
|
|
|
ntop->getPrefs()->enable_flow_aggregation();
|
|
nextFlowAggregation = FLOW_AGGREGATION_DURATION;
|
|
} else
|
|
aggregated_flows_hash = NULL;
|
|
#endif
|
|
|
|
#ifdef HAVE_MYSQL
|
|
if(db == NULL)
|
|
db = new (std::nothrow) MySQLDB(this);
|
|
#endif
|
|
|
|
if(!db) throw "Not enough memory";
|
|
}
|
|
#ifndef HAVE_NEDGE
|
|
else if (ntop->getPrefs()->do_dump_flows_on_es())
|
|
db = new ElasticSearch(this);
|
|
else if (ntop->getPrefs()->do_dump_flows_on_ls())
|
|
db = new Logstash(this);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
bool NetworkInterface::registerLiveCapture(struct ntopngLuaContext * const luactx, int *id) {
|
|
bool ret = false;
|
|
|
|
*id = -1;
|
|
active_captures_lock.lock(__FILE__, __LINE__);
|
|
|
|
if(num_live_captures < MAX_NUM_PCAP_CAPTURES) {
|
|
for(int i=0; i<MAX_NUM_PCAP_CAPTURES; i++) {
|
|
if(live_captures[i] == NULL) {
|
|
live_captures[i] = luactx, num_live_captures++;
|
|
ret = true, *id = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
active_captures_lock.unlock(__FILE__, __LINE__);
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
bool NetworkInterface::deregisterLiveCapture(struct ntopngLuaContext * const luactx) {
|
|
bool ret = false;
|
|
|
|
active_captures_lock.lock(__FILE__, __LINE__);
|
|
|
|
for(int i=0; i<MAX_NUM_PCAP_CAPTURES; i++) {
|
|
if(live_captures[i] == luactx) {
|
|
struct ntopngLuaContext *c = (struct ntopngLuaContext *)live_captures[i];
|
|
|
|
c->live_capture.stopped = true;
|
|
live_captures[i] = NULL, num_live_captures--;
|
|
ret = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
active_captures_lock.unlock(__FILE__, __LINE__);
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
bool NetworkInterface::matchLiveCapture(struct ntopngLuaContext * const luactx,
|
|
const struct pcap_pkthdr * const h,
|
|
const u_char * const packet,
|
|
Flow * const f) {
|
|
if(!luactx->live_capture.matching_host /* No host filter set */
|
|
|| (f && (luactx->live_capture.matching_host == f->get_cli_host()
|
|
|| luactx->live_capture.matching_host == f->get_srv_host()))) {
|
|
if(luactx->live_capture.bpfFilterSet) {
|
|
if(!bpf_filter(luactx->live_capture.fcode.bf_insns,
|
|
(const u_char*)packet, h->caplen, h->caplen)) {
|
|
return(false);
|
|
}
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void NetworkInterface::deliverLiveCapture(const struct pcap_pkthdr * const h,
|
|
const u_char * const packet, Flow * const f) {
|
|
int res;
|
|
|
|
for(u_int i=0, num_found = 0; (i<MAX_NUM_PCAP_CAPTURES)
|
|
&& (num_found < num_live_captures); i++) {
|
|
if(live_captures[i] != NULL) {
|
|
struct ntopngLuaContext *c = (struct ntopngLuaContext *)live_captures[i];
|
|
bool http_client_disconnected = false;
|
|
|
|
num_found++;
|
|
|
|
if(c->live_capture.capture_until < h->ts.tv_sec || c->live_capture.stopped)
|
|
http_client_disconnected = true;
|
|
|
|
/* The header is always sent even when there is never a match with matchLiveCapture,
|
|
as otherwise some browsers may end up in hangning. Hanging has been
|
|
verified with Safari Version 12.0 (13606.2.11)
|
|
but not with Chrome Version 68.0.3440.106 (Official Build) (64-bit) */
|
|
if(!http_client_disconnected
|
|
&& c->conn
|
|
&& !c->live_capture.pcaphdr_sent) {
|
|
struct pcap_file_header pcaphdr;
|
|
|
|
Utils::init_pcap_header(&pcaphdr, this);
|
|
|
|
if((res = mg_write_async(c->conn, &pcaphdr, sizeof(pcaphdr))) < (int)sizeof(pcaphdr))
|
|
http_client_disconnected = true;
|
|
|
|
c->live_capture.pcaphdr_sent = true;
|
|
}
|
|
|
|
if(!http_client_disconnected
|
|
&& c->conn
|
|
&& matchLiveCapture(c, h, packet, f)) {
|
|
struct pcap_disk_pkthdr pkthdr; /* Cannot use h as the format on disk differs */
|
|
|
|
pkthdr.ts.tv_sec = h->ts.tv_sec, pkthdr.ts.tv_usec = h->ts.tv_usec,
|
|
pkthdr.caplen = h->caplen, pkthdr.len = h->len;
|
|
|
|
if(
|
|
((res = mg_write_async(c->conn, &pkthdr, sizeof(pkthdr))) < (int)sizeof(pkthdr))
|
|
|| ((res = mg_write_async(c->conn, packet, h->caplen)) < (int)h->caplen)
|
|
)
|
|
http_client_disconnected = true;
|
|
else {
|
|
c->live_capture.num_captured_packets++;
|
|
|
|
if((c->live_capture.capture_max_pkts != 0)
|
|
&& (c->live_capture.num_captured_packets == c->live_capture.capture_max_pkts))
|
|
http_client_disconnected = true;
|
|
}
|
|
}
|
|
|
|
if(http_client_disconnected)
|
|
deregisterLiveCapture(c); /* (*) */
|
|
}
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void NetworkInterface::dumpLiveCaptures(lua_State* vm) {
|
|
/* Administrative privileges checked by the caller */
|
|
|
|
active_captures_lock.lock(__FILE__, __LINE__);
|
|
|
|
lua_newtable(vm);
|
|
|
|
for(int i = 0, capture_id = 0; i < MAX_NUM_PCAP_CAPTURES; i++) {
|
|
if(live_captures[i] != NULL
|
|
&& !live_captures[i]->live_capture.stopped) {
|
|
lua_newtable(vm);
|
|
|
|
lua_push_uint64_table_entry(vm, "id", i);
|
|
lua_push_uint64_table_entry(vm, "capture_until",
|
|
live_captures[i]->live_capture.capture_until);
|
|
lua_push_uint64_table_entry(vm, "capture_max_pkts",
|
|
live_captures[i]->live_capture.capture_max_pkts);
|
|
lua_push_uint64_table_entry(vm, "num_captured_packets",
|
|
live_captures[i]->live_capture.num_captured_packets);
|
|
|
|
if(live_captures[i]->live_capture.matching_host != NULL) {
|
|
Host *h = (Host*)live_captures[i]->live_capture.matching_host;
|
|
char buf[64];
|
|
|
|
lua_push_str_table_entry(vm, "host",
|
|
h->get_ip()->print(buf, sizeof(buf)));
|
|
}
|
|
|
|
lua_pushinteger(vm, ++capture_id);
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
}
|
|
}
|
|
|
|
|
|
active_captures_lock.unlock(__FILE__, __LINE__);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
bool NetworkInterface::stopLiveCapture(int capture_id) {
|
|
/* Administrative privileges checked by the caller */
|
|
|
|
bool rc = false;
|
|
|
|
if((capture_id >= 0) && (capture_id < MAX_NUM_PCAP_CAPTURES)) {
|
|
active_captures_lock.lock(__FILE__, __LINE__);
|
|
|
|
if(live_captures[capture_id] != NULL) {
|
|
struct ntopngLuaContext *c = (struct ntopngLuaContext *)live_captures[capture_id];
|
|
|
|
c->live_capture.stopped = true, rc = true;
|
|
if(c->live_capture.bpfFilterSet)
|
|
pcap_freecode(&c->live_capture.fcode);
|
|
/* live_captures[capture_id] = NULL; */ /* <-- not necessary as mongoose will clean it */
|
|
}
|
|
|
|
active_captures_lock.unlock(__FILE__, __LINE__);
|
|
}
|
|
|
|
return(rc);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void NetworkInterface::makeTsPoint(NetworkInterfaceTsPoint *pt) {
|
|
/* unused */
|
|
TcpFlowStats _tcpFlowStats;
|
|
EthStats _ethStats;
|
|
|
|
sumStats(&_tcpFlowStats, &_ethStats, &pt->local_stats,
|
|
&pt->ndpi, &pt->packetStats, &pt->tcpPacketStats);
|
|
|
|
for(u_int8_t s = 0; s<numSubInterfaces; s++)
|
|
subInterfaces[s]->sumStats(&_tcpFlowStats, &_ethStats,
|
|
&pt->local_stats, &pt->ndpi, &pt->packetStats, &pt->tcpPacketStats);
|
|
|
|
pt->hosts = getNumHosts();
|
|
pt->local_hosts = getNumLocalHosts();
|
|
pt->devices = getNumL2Devices();
|
|
pt->flows = getNumFlows();
|
|
pt->http_hosts = getNumHTTPHosts();
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void NetworkInterface::tsLua(lua_State* vm) {
|
|
if(!ts_ring || !TimeseriesRing::isRingEnabled(ntop->getPrefs())) {
|
|
/* Use real time data */
|
|
NetworkInterfaceTsPoint pt;
|
|
|
|
makeTsPoint(&pt);
|
|
TimeseriesRing::luaSinglePoint(vm, this, &pt);
|
|
} else
|
|
ts_ring->lua(vm);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
static bool host_reload_blacklist(GenericHashEntry *host, void *user_data, bool *matched) {
|
|
Host *h = (Host*)host;
|
|
|
|
h->reloadHostBlacklist();
|
|
*matched = true;
|
|
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void NetworkInterface::reloadHostsBlacklist() {
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
|
|
/* Update the hosts */
|
|
walker(&begin_slot, walk_all, walker_hosts, host_reload_blacklist, NULL);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
static bool local_hosts_2_dropbox_walker(GenericHashEntry *h, void *user_data, bool *matched) {
|
|
Host *host = (Host*)h;
|
|
|
|
if(host && (host->getNumDropboxPeers() > 0)) {
|
|
lua_State *vm = (lua_State*)user_data;
|
|
|
|
host->dumpDropbox(vm);
|
|
*matched = true;
|
|
}
|
|
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
int NetworkInterface::dumpDropboxHosts(lua_State *vm) {
|
|
int rc;
|
|
u_int32_t begin_slot = 0;
|
|
|
|
lua_newtable(vm);
|
|
|
|
disablePurge(false /* on hosts */);
|
|
rc = walker(&begin_slot, true /* walk_all */, walker_hosts,
|
|
local_hosts_2_dropbox_walker, vm) ? 0 : -1;
|
|
enablePurge(false /* on hosts */);
|
|
|
|
return(rc);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
static bool host_reload_dhcp_host(GenericHashEntry *host, void *user_data, bool *matched) {
|
|
Host *h = (Host*)host;
|
|
|
|
h->reloadDhcpHost();
|
|
*matched = true;
|
|
|
|
return(false); /* false = keep on walking */
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void NetworkInterface::reloadDhcpRanges() {
|
|
char redis_key[CONST_MAX_LEN_REDIS_KEY], *rsp = NULL;
|
|
dhcp_range *new_ranges = NULL;
|
|
u_int num_ranges = 0;
|
|
u_int len;
|
|
|
|
if(!ntop->getRedis())
|
|
return;
|
|
|
|
snprintf(redis_key, sizeof(redis_key), IFACE_DHCP_RANGE_KEY, get_id());
|
|
|
|
if((rsp = (char*)malloc(CONST_MAX_LEN_REDIS_VALUE))
|
|
&& !ntop->getRedis()->get(redis_key, rsp, CONST_MAX_LEN_REDIS_VALUE)
|
|
&& (len = strlen(rsp))) {
|
|
u_int i;
|
|
num_ranges = 1;
|
|
|
|
for(i=0; i<len; i++) {
|
|
if(rsp[i] == ',')
|
|
num_ranges++;
|
|
}
|
|
|
|
// +1 for final zero IP, which is used to indicate array termination
|
|
new_ranges = new dhcp_range[num_ranges+1];
|
|
|
|
if(new_ranges) {
|
|
char *cur_pos = rsp;
|
|
|
|
/* E.g. 192.168.1.2-192.168.1.150,10.0.0.50-10.0.0.60 */
|
|
for(i=0; i<num_ranges; i++) {
|
|
char *end = strchr(cur_pos, ',');
|
|
char *delim = strchr(cur_pos, '-');
|
|
|
|
if(!end)
|
|
end = cur_pos + strlen(cur_pos);
|
|
|
|
if(delim) {
|
|
*delim = 0;
|
|
*end = 0;
|
|
|
|
new_ranges[i].first_ip.set(cur_pos);
|
|
new_ranges[i].last_ip.set(delim+1);
|
|
}
|
|
|
|
cur_pos = end + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(dhcp_ranges_shadow)
|
|
delete[] (dhcp_ranges_shadow);
|
|
|
|
dhcp_ranges_shadow = dhcp_ranges;
|
|
dhcp_ranges = new_ranges;
|
|
|
|
if(rsp)
|
|
free(rsp);
|
|
|
|
/* Reload existing hosts */
|
|
u_int32_t begin_slot = 0;
|
|
bool walk_all = true;
|
|
walker(&begin_slot, walk_all, walker_hosts, host_reload_dhcp_host, NULL);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
bool NetworkInterface::isInDhcpRange(IpAddress *ip) {
|
|
// Important: cache it as it may change
|
|
dhcp_range *ranges = dhcp_ranges;
|
|
|
|
if(!ranges)
|
|
return(false);
|
|
|
|
while(!ranges->last_ip.isEmpty()) {
|
|
if((ranges->first_ip.compare(ip) <= 0) &&
|
|
(ranges->last_ip.compare(ip) >= 0))
|
|
return true;
|
|
|
|
ranges++;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
bool NetworkInterface::isLocalBroadcastDomainHost(Host * const h, bool isInlineCall) {
|
|
IpAddress *i = h->get_ip();
|
|
|
|
return(bcast_domains->isLocalBroadcastDomainHost(h, isInlineCall)
|
|
|| (ntop->getLoadInterfaceAddresses() && i->match(ntop->getLoadInterfaceAddresses())));
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
#ifdef HAVE_EBPF
|
|
|
|
bool NetworkInterface::enqueueeBPFEvent(eBPFevent *event) {
|
|
#ifdef EBPF_DEBUG
|
|
// ntop->getTrace()->traceEvent(TRACE_ERROR, "[%s] %s(%d/%d)", ifname, __FUNCTION__, next_insert_idx, next_remove_idx);
|
|
#endif
|
|
|
|
if(ebpfEvents[next_insert_idx] != (eBPFevent*)NULL)
|
|
return(false);
|
|
|
|
ebpf_preprocess_event(event, true /* speak with docker */);
|
|
|
|
ebpfEvents[next_insert_idx] = event;
|
|
next_insert_idx = (next_insert_idx + 1) % EBPF_QUEUE_LEN;
|
|
return(true);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
bool NetworkInterface::dequeueeBPFEvent(eBPFevent **event) {
|
|
if(ebpfEvents[next_remove_idx] == (eBPFevent*)NULL) {
|
|
*event = NULL;
|
|
return(false);
|
|
}
|
|
|
|
#ifdef EBPF_DEBUG
|
|
// ntop->getTrace()->traceEvent(TRACE_ERROR, "[%s] %s(%d/%d)", ifname, __FUNCTION__, next_insert_idx, next_remove_idx);
|
|
#endif
|
|
|
|
*event = ebpfEvents[next_remove_idx];
|
|
ebpfEvents[next_remove_idx] = NULL;
|
|
next_remove_idx = (next_remove_idx + 1) % EBPF_QUEUE_LEN;
|
|
return(true);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void NetworkInterface::delivereBPFEvent(eBPFevent *event) {
|
|
eBPFevent *tmp;
|
|
|
|
if(ebpfEvents == NULL)
|
|
return; /* No events */
|
|
else if((event->ifname[0] != '\0') && strcmp(event->ifname, ifname))
|
|
return; /* Not for this interface */
|
|
|
|
if((tmp = (eBPFevent*)malloc(sizeof(eBPFevent))) != NULL) {
|
|
memcpy(tmp, event, sizeof(eBPFevent));
|
|
|
|
// ntop->getTrace()->traceEvent(TRACE_ERROR, "%s()", __FUNCTION__);
|
|
|
|
if(!enqueueeBPFEvent(tmp))
|
|
free(tmp); /* Not enough space */
|
|
else if(!hasSeenEBPFEvents())
|
|
setSeenEBPFEvents();
|
|
}
|
|
}
|
|
|
|
#endif // HAVE_EBPF
|