/* * * (C) 2013-17 - 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 #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[64], buf[64]; bool isViewInterface = (strncmp(name, "view:", 5) == 0) ? 1 : 0; /* We need to do it as isView() is not yet initialized */ customIftype = custom_interface_type, flowHashingMode = flowhashing_none; init(); #ifdef WIN32 if(name == NULL) name = "1"; /* First available interface */ #endif scalingFactor = 1, remoteIfname = remoteIfIPaddr = remoteProbeIPaddr = remoteProbePublicIPaddr = NULL; 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) { char pcap_error_buffer[PCAP_ERRBUF_SIZE]; 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; } } pkt_dumper_tap = NULL, lastSecUpdate = 0; ifname = strdup(name); ifDescription = strdup(Utils::getInterfaceDescription(ifname, buf, sizeof(buf))); if(id >= 0) { u_int32_t num_hashes; 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()); 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()); // init global detection structure ndpi_struct = ndpi_init_detection_module(); if(ndpi_struct == NULL) { ntop->getTrace()->traceEvent(TRACE_ERROR, "Global structure initialization failed"); exit(-1); } if(ntop->getCustomnDPIProtos() != NULL) ndpi_load_protocols_file(ndpi_struct, ntop->getCustomnDPIProtos()); ndpi_struct->http_dont_dissect_response = 1; memset(d_port, 0, sizeof(d_port)); ndpi_set_proto_defaults(ndpi_struct, NDPI_PROTOCOL_UNRATED, NTOPNG_NDPI_OS_PROTO_ID, no_master, no_master, (char*)"Operating System", NDPI_PROTOCOL_CATEGORY_SYSTEM, 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_mac_addresses = false, pkt_dumper = NULL; arp_requests = arp_replies = 0; if(ntop->getPrefs()->are_taps_enabled()) pkt_dumper_tap = new PacketDumperTuntap(this); running = false, sprobe_interface = false, inline_interface = false, db = NULL; if((!isViewInterface) && (ntop->getPrefs()->do_dump_flows_on_mysql())) { #ifdef NTOPNG_PRO if(ntop->getPrefs()->is_enterprise_edition()){ db = new BatchedMySQLDB(this); ntop->getPrefs()->enable_flow_aggregation(); aggregated_flows_hash = new AggregatedFlowHash(this, num_hashes, ntop->getPrefs()->get_max_num_flows()); nextFlowAggregation = FLOW_AGGREGATION_DURATION; } else aggregated_flows_hash = NULL; #endif if(db == NULL) db = new MySQLDB(this); if(!db) throw "Not enough memory"; } checkIdle(); ifSpeed = Utils::getMaxIfSpeed(name); ifMTU = Utils::getIfMTU(name), mtuWarningShown = false; } else { #ifdef NTOPNG_PRO aggregated_flows_hash = NULL; #endif flows_hash = NULL, hosts_hash = NULL; macs_hash = NULL, ases_hash = NULL, vlans_hash = NULL; ndpi_struct = NULL, db = NULL, ifSpeed = 0; pkt_dumper = NULL, pkt_dumper_tap = NULL; } networkStats = NULL; #ifdef NTOPNG_PRO policer = NULL; /* possibly instantiated by subclass PacketBridge */ flow_profiles = ntop->getPro()->has_valid_license() ? new FlowProfiles(id) : NULL; if(flow_profiles) flow_profiles->loadProfiles(); shadow_flow_profiles = NULL; flow_interfaces_stats = NULL; /* Lazy, instantiated on demand */ #endif loadDumpPrefs(); loadScalingFactorPrefs(); if(((statsManager = new StatsManager(id, STATS_MANAGER_STORE_NAME)) == NULL) || ((alertsManager = new AlertsManager(id, ALERTS_MANAGER_STORE_NAME)) == NULL)) throw "Not enough memory"; if((host_pools = new HostPools(this)) == NULL) throw "Not enough memory"; alertLevel = alertsManager->getNumAlerts(true); #ifdef linux /* A bit aggressive but as people usually ignore warnings let's be proactive */ if(ifname && (!isViewInterface) && (!strstr(ifname, ":")) && (!strstr(ifname, ".pcap")) && strcmp(ifname, "dummy") && strcmp(ifname, "any") && 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", iface); system(buf); ntop->getTrace()->traceEvent(TRACE_NORMAL, "Executing %s", buf); iface = strtok_r(NULL, ",", &tmp); } } #endif } /* **************************************************** */ void NetworkInterface::init() { ifname = remoteIfname = remoteIfIPaddr = remoteProbeIPaddr = NULL, remoteProbePublicIPaddr = NULL, flows_hash = NULL, hosts_hash = NULL, ndpi_struct = NULL, zmq_initial_bytes = 0, zmq_initial_pkts = 0, sprobe_interface = inline_interface = false, has_vlan_packets = false, last_pkt_rcvd = last_pkt_rcvd_remote = 0, next_idle_flow_purge = next_idle_host_purge = 0, running = false, numSubInterfaces = 0, numVirtualInterfaces = 0, flowHashing = NULL, pcap_datalink_type = 0, mtuWarningShown = false, lastSecUpdate = 0, purge_idle_flows_hosts = true, id = (u_int8_t)-1, last_remote_pps = 0, last_remote_bps = 0, sprobe_interface = false, has_vlan_packets = false, pcap_datalink_type = 0, cpu_affinity = -1 /* no affinity */, inline_interface = false, running = false, interfaceStats = NULL, has_too_many_hosts = has_too_many_flows = false, pkt_dumper = NULL, numL2Devices = 0, numHosts = 0, numLocalHosts = 0, checkpointPktCount = checkpointBytesCount = checkpointPktDropCount = 0, pollLoopCreated = false, bridge_interface = false; if(ntop && ntop->getPrefs() && ntop->getPrefs()->are_taps_enabled()) pkt_dumper_tap = new PacketDumperTuntap(this); else pkt_dumper_tap = NULL; memset(subInterfaces, 0, sizeof(subInterfaces)); ip_addresses = "", networkStats = NULL, pcap_datalink_type = 0, cpu_affinity = -1, pkt_dumper = NULL; memset(lastMinuteTraffic, 0, sizeof(lastMinuteTraffic)); resetSecondTraffic(); L_user_scripts_inline = L_user_scripts_periodic = NULL; forceLuaInterpreterReload(); db = NULL; #ifdef NTOPNG_PRO aggregated_flows_hash = NULL; policer = NULL; #endif statsManager = NULL, alertsManager = NULL, ifSpeed = 0; host_pools = NULL; checkIdle(); dump_all_traffic = dump_to_disk = dump_unknown_traffic = dump_security_packets = dump_to_tap = false; dump_sampling_rate = CONST_DUMP_SAMPLING_RATE; dump_max_pkts_file = CONST_MAX_NUM_PACKETS_PER_DUMP; dump_max_duration = CONST_MAX_DUMP_DURATION; dump_max_files = CONST_MAX_DUMP; ifMTU = CONST_DEFAULT_MAX_PACKET_SIZE, mtuWarningShown = false; #ifdef NTOPNG_PRO flow_profiles = shadow_flow_profiles = NULL; #endif } /* **************************************************** */ #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) { #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 */ 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 */ } } aggregatedFlow->sumFlowStats(flow); #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)) == 0 && rsp[0] != '\0') { if(!strcmp(get_type(), CONST_INTERFACE_TYPE_ZMQ)) { /* ZMQ interface */ if(!strcmp(rsp, DISAGGREGATION_PROBE_IP)) flowHashingMode = flowhashing_probe_ip; 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); } } } } /* **************************************************** */ void NetworkInterface::loadDumpPrefs() { if(ntop->getRedis() != NULL) { updateDumpAllTrafficPolicy(); updateDumpTrafficDiskPolicy(); updateDumpTrafficTapPolicy(); updateDumpTrafficSamplingRate(); updateDumpTrafficMaxPktsPerFile(); updateDumpTrafficMaxSecPerFile(); updateDumpTrafficMaxFiles(); } } /* **************************************************** */ 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; } } } /* **************************************************** */ bool NetworkInterface::updateDumpTrafficTapPolicy(void) { bool retval = false; if(ifname != NULL) { char rkey[128], rsp[16]; snprintf(rkey, sizeof(rkey), "ntopng.prefs.%s.dump_tap", ifname); if(ntop->getRedis()->get(rkey, rsp, sizeof(rsp)) == 0) retval = !strncmp(rsp, "true", 5); else retval = false; } dump_to_tap = retval; return retval; } /* **************************************************** */ bool NetworkInterface::updateDumpAllTrafficPolicy(void) { bool retval = false; if(ifname != NULL) { char rkey[128], rsp[16]; snprintf(rkey, sizeof(rkey), "ntopng.prefs.%s.dump_all_traffic", ifname); if(ntop->getRedis()->get(rkey, rsp, sizeof(rsp)) == 0) retval = !strncmp(rsp, "true", 5); } dump_all_traffic = retval; return retval; } /* **************************************************** */ bool NetworkInterface::updateDumpTrafficDiskPolicy(void) { bool retval = false, retval_u = false, retval_s = false; if(ifname != NULL) { char rkey[128], rsp[16]; snprintf(rkey, sizeof(rkey), "ntopng.prefs.%s.dump_disk", ifname); if(ntop->getRedis()->get(rkey, rsp, sizeof(rsp)) == 0) retval = !strncmp(rsp, "true", 5); snprintf(rkey, sizeof(rkey), "ntopng.prefs.%s.dump_unknown_disk", ifname); if(ntop->getRedis()->get(rkey, rsp, sizeof(rsp)) == 0) retval_u = !strncmp(rsp, "true", 5); snprintf(rkey, sizeof(rkey), "ntopng.prefs.%s.dump_security_disk", ifname); if(ntop->getRedis()->get(rkey, rsp, sizeof(rsp)) == 0) retval_s = !strncmp(rsp, "true", 5); } dump_to_disk = retval; dump_unknown_traffic = retval_u; dump_security_packets = retval_s; return retval; } /* **************************************************** */ int NetworkInterface::updateDumpTrafficSamplingRate(void) { int retval = 1; if(ifname != NULL) { char rkey[128], rsp[16]; snprintf(rkey, sizeof(rkey), "ntopng.prefs.%s.dump_sampling_rate", ifname); if(ntop->getRedis()->get(rkey, rsp, sizeof(rsp)) == 0) retval = atoi(rsp); } dump_sampling_rate = retval; return retval; } /* **************************************************** */ int NetworkInterface::updateDumpTrafficMaxPktsPerFile(void) { int retval = 0; if(ifname != NULL) { char rkey[128], rsp[16]; snprintf(rkey, sizeof(rkey), "ntopng.prefs.%s.dump_max_pkts_file", ifname); if(ntop->getRedis()->get(rkey, rsp, sizeof(rsp)) == 0) retval = atoi(rsp); } retval = retval > 0 ? retval : CONST_MAX_NUM_PACKETS_PER_DUMP; dump_max_pkts_file = retval; return retval; } /* **************************************************** */ int NetworkInterface::updateDumpTrafficMaxSecPerFile(void) { int retval = 0; if(ifname != NULL) { char rkey[128], rsp[16]; snprintf(rkey, sizeof(rkey), "ntopng.prefs.%s.dump_max_sec_file", ifname); if(ntop->getRedis()->get(rkey, rsp, sizeof(rsp)) == 0) retval = atoi(rsp); } retval = retval > 0 ? retval : CONST_MAX_DUMP_DURATION; dump_max_duration = retval; return retval; } /* **************************************************** */ int NetworkInterface::updateDumpTrafficMaxFiles(void) { int retval = 0; if(ifname != NULL) { char rkey[128], rsp[16]; snprintf(rkey, sizeof(rkey), "ntopng.prefs.%s.dump_max_files", ifname); if(ntop->getRedis()->get(rkey, rsp, sizeof(rsp)) == 0) retval = atoi(rsp); } retval = retval > 0 ? retval : CONST_MAX_DUMP; dump_max_files = retval; return retval; } /* **************************************************** */ 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(vlans_hash) { delete(vlans_hash); vlans_hash = NULL; } if(macs_hash) { delete(macs_hash); macs_hash = NULL; } #ifdef NTOPNG_PRO if(aggregated_flows_hash) { delete(aggregated_flows_hash); aggregated_flows_hash = NULL; } #endif if(ndpi_struct) { ndpi_exit_detection_module(ndpi_struct); ndpi_struct = NULL; } if(ifname) { //ntop->getTrace()->traceEvent(TRACE_NORMAL, "Interface %s shutdown", ifname); free(ifname); ifname = NULL; } } /* **************************************************** */ NetworkInterface::~NetworkInterface() { if(getNumPackets() > 0) { ntop->getTrace()->traceEvent(TRACE_NORMAL, "Flushing host contacts for interface %s", get_name()); cleanup(); } if(host_pools) delete host_pools; /* note: this requires ndpi_struct */ deleteDataStructures(); if(ifDescription) free(ifDescription); if(remoteIfname) free(remoteIfname); if(remoteIfIPaddr) free(remoteIfIPaddr); if(remoteProbeIPaddr) free(remoteProbeIPaddr); if(remoteProbePublicIPaddr) free(remoteProbePublicIPaddr); if(db) delete db; if(statsManager) delete statsManager; if(alertsManager) delete alertsManager; if(networkStats) delete []networkStats; if(pkt_dumper) delete pkt_dumper; if(pkt_dumper_tap) delete pkt_dumper_tap; 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); } } ndpi_exit_detection_module(ndpi_struct); #ifdef NTOPNG_PRO if(policer) delete(policer); if(flow_profiles) delete(flow_profiles); if(shadow_flow_profiles) delete(shadow_flow_profiles); if(flow_interfaces_stats) delete flow_interfaces_stats; #endif termLuaInterpreter(); } /* **************************************************** */ int NetworkInterface::dumpFlow(time_t when, Flow *f) { ntop->getTrace()->traceEvent(TRACE_INFO, "Dumping flow."); if(ntop->getPrefs()->do_dump_flows_on_mysql()) { return(dumpDBFlow(when, f)); } else if(ntop->getPrefs()->do_dump_flows_on_es()){ return(dumpEsFlow(when, f)); }else if(ntop->getPrefs()->do_dump_flows_on_ls()){ return(dumpLsFlow(when,f)); }else { ntop->getTrace()->traceEvent(TRACE_WARNING, "Internal error"); return(-1); } } /* **************************************************** */ int NetworkInterface::dumpLsFlow(time_t when, Flow *f){ char *json = f->serialize(true); int rc; if(json && ntop->getLogstash()) { ntop->getTrace()->traceEvent(TRACE_INFO, "[LS] %s", json); rc = ntop->getLogstash()->sendToLS(json); free(json); } else rc = -1; return(rc); } int NetworkInterface::dumpEsFlow(time_t when, Flow *f) { char *json = f->serialize(true); int rc; if(json) { ntop->getTrace()->traceEvent(TRACE_INFO, "[ES] %s", json); rc = ntop->getElasticSearch()->sendToES(json); free(json); } else rc = -1; return(rc); } /* **************************************************** */ int NetworkInterface::dumpDBFlow(time_t when, Flow *f) { char *json = f->serialize(false); int rc; if(json) { rc = db->dumpFlow(when, f, json); free(json); } else rc = -1; return(rc); } /* **************************************************** */ #ifdef NTOPNG_PRO int NetworkInterface::dumpAggregatedFlow(AggregatedFlow *f) { if(ntop->getPrefs()->is_enterprise_edition() && db && f && (f->get_packets() > 0)) { #ifdef AGGREGATED_FLOW_DEBUG char buf[256]; ntop->getTrace()->traceEvent(TRACE_NORMAL, "Going to dump AggregatedFlow to database [%s]", f->print(buf, sizeof(buf))); #endif return(dynamic_cast(db)->dumpAggregatedFlow(f)); } return(-1); } #endif /* **************************************************** */ static bool local_hosts_2_redis_walker(GenericHashEntry *h, void *user_data) { Host *host = (Host*)h; if(host && (host->isLocalHost() || host->isSystemHost())) host->serialize2redis(); return(false); /* false = keep on walking */ } /* **************************************************** */ int NetworkInterface::dumpLocalHosts2redis(bool disable_purge) { int rc; if(disable_purge) disablePurge(false /* on hosts */); rc = walker(walker_hosts, local_hosts_2_redis_walker, NULL) ? 0 : -1; if(disable_purge) enablePurge(false /* on hosts */); return rc; } /* **************************************************** */ u_int32_t NetworkInterface::getHostsHashSize() { if(!isView()) return(hosts_hash->getNumEntries()); else { u_int32_t tot = 0; for(u_int8_t s = 0; sget_hosts_hash()->getNumEntries(); return(tot); } } /* **************************************************** */ u_int32_t NetworkInterface::getASesHashSize() { if(!isView()) return(ases_hash->getNumEntries()); else { u_int32_t tot = 0; for(u_int8_t s = 0; sget_ases_hash()->getNumEntries(); return(tot); } } /* **************************************************** */ u_int32_t NetworkInterface::getVLANsHashSize() { if(!isView()) return(vlans_hash->getNumEntries()); else { u_int32_t tot = 0; for(u_int8_t s = 0; sget_vlans_hash()->getNumEntries(); return(tot); } } /* **************************************************** */ u_int32_t NetworkInterface::getFlowsHashSize() { if(!isView()) return(flows_hash->getNumEntries()); else { u_int32_t tot = 0; for(u_int8_t s = 0; sget_flows_hash()->getNumEntries(); return(tot); } } /* **************************************************** */ u_int32_t NetworkInterface::getMacsHashSize() { if(!isView()) return(macs_hash->getNumEntries()); else { u_int32_t tot = 0; for(u_int8_t s = 0; sget_macs_hash()->getNumEntries(); return(tot); } } /* **************************************************** */ bool NetworkInterface::walker(WalkerType wtype, bool (*walker)(GenericHashEntry *h, void *user_data), void *user_data) { bool ret = false; switch(wtype) { case walker_hosts: if(!isView()) ret = hosts_hash->walk(walker, user_data); else { for(u_int8_t s = 0; sget_hosts_hash()->walk(walker, user_data); } break; case walker_flows: if(!isView()) ret = flows_hash->walk(walker, user_data); else { for(u_int8_t s = 0; sget_flows_hash()->walk(walker, user_data); } break; case walker_macs: if(!isView()) ret = macs_hash->walk(walker, user_data); else { for(u_int8_t s = 0; sget_macs_hash()->walk(walker, user_data); } break; case walker_ases: if(!isView()) ret = ases_hash->walk(walker, user_data); else { for(u_int8_t s = 0; sget_ases_hash()->walk(walker, user_data); } break; case walker_vlans: if(!isView()) ret = vlans_hash->walk(walker, user_data); else { for(u_int8_t s = 0; sget_vlans_hash()->walk(walker, user_data); } break; } return(ret); } /* **************************************************** */ Flow* NetworkInterface::getFlow(u_int8_t *src_eth, u_int8_t *dst_eth, u_int16_t vlan_id, u_int32_t deviceIP, u_int16_t inIndex, u_int16_t outIndex, 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, bool *new_flow) { Flow *ret; if(vlan_id != 0) setSeenVlanTaggedPackets(); if((src_eth && Utils::macHash(src_eth) != 0) || (dst_eth && Utils::macHash(dst_eth) != 0)) setSeenMacAddresses(); ret = flows_hash->find(src_eth, dst_eth, src_ip, dst_ip, src_port, dst_port, vlan_id, l4_proto, src2dst_direction); if(ret == NULL) { *new_flow = true; try { ret = new Flow(this, vlan_id, l4_proto, src_eth, src_ip, src_port, dst_eth, dst_ip, dst_port, first_seen, last_seen); } 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; return(ret); } 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; return(ret); } } /* **************************************************** */ NetworkInterface* NetworkInterface::getSubInterface(u_int32_t criteria) { 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 [vlanId: %u]", ifname, criteria); break; case flowhashing_probe_ip: vIface_type = CONST_INTERFACE_TYPE_FLOW; snprintf(buf, sizeof(buf), "%s [probeIP: %s]", ifname, Utils::intoaV4(criteria, buf1, sizeof(buf1))); break; case flowhashing_ingress_iface_idx: vIface_type = CONST_INTERFACE_TYPE_FLOW; snprintf(buf, sizeof(buf), "%s [ifIdx: %u]", ifname, criteria); break; case flowhashing_vrfid: vIface_type = CONST_INTERFACE_TYPE_FLOW; snprintf(buf, sizeof(buf), "%s [VRF Id: %u]", ifname, criteria); break; default: free(h); return(NULL); break; } if((h->iface = new NetworkInterface(buf, vIface_type)) != NULL) { HASH_ADD_INT(flowHashing, criteria, h); ntop->registerInterface(h->iface); numVirtualInterfaces++; } } else ntop->getTrace()->traceEvent(TRACE_WARNING, "Not enough memory"); } } if(h) return(h->iface); return(NULL); } /* **************************************************** */ void NetworkInterface::processFlow(ZMQ_Flow *zflow) { bool src2dst_direction, new_flow; Flow *flow; ndpi_protocol p; time_t now = time(NULL); if(last_pkt_rcvd_remote > 0) { int drift = now - last_pkt_rcvd_remote; if(drift >= 0) zflow->last_switched += drift, zflow->first_switched += drift; else { u_int32_t d = (u_int32_t)-drift; if(d < zflow->last_switched) zflow->last_switched += drift; if(d < zflow->first_switched) zflow->first_switched += drift; } #ifdef DEBUG ntop->getTrace()->traceEvent(TRACE_NORMAL, "[first=%u][last=%u][duration: %u][drift: %d][now: %u][remote: %u]", zflow->first_switched, zflow->last_switched, zflow->last_switched-zflow->first_switched, drift, now, last_pkt_rcvd_remote); #endif } else { /* Old nProbe */ if((time_t)zflow->last_switched > (time_t)last_pkt_rcvd_remote) last_pkt_rcvd_remote = zflow->last_switched; #ifdef DEBUG ntop->getTrace()->traceEvent(TRACE_NORMAL, "[first=%u][last=%u][duration: %u]", zflow->first_switched, zflow->last_switched, zflow->last_switched- zflow->first_switched); #endif } if(flowHashingMode != flowhashing_none) { NetworkInterface *vIface = NULL; switch(flowHashingMode) { case flowhashing_probe_ip: vIface = getSubInterface((u_int32_t)zflow->deviceIP); break; case flowhashing_ingress_iface_idx: vIface = getSubInterface((u_int32_t)zflow->inIndex); break; case flowhashing_vrfid: vIface = getSubInterface((u_int32_t)zflow->vrfId); break; case flowhashing_vlan: if(zflow->vlan_id) vIface = getSubInterface((u_int32_t)zflow->vlan_id); break; default: break; } if(vIface) { vIface->processFlow(zflow); return; } } /* Updating Flow */ flow = getFlow((u_int8_t*)zflow->src_mac, (u_int8_t*)zflow->dst_mac, zflow->vlan_id, zflow->deviceIP, zflow->inIndex, zflow->outIndex, &zflow->src_ip, &zflow->dst_ip, zflow->src_port, zflow->dst_port, zflow->l4_proto, &src2dst_direction, zflow->first_switched, zflow->last_switched, &new_flow); if(flow == NULL) return; if(zflow->l4_proto == IPPROTO_TCP) { struct timeval when; when.tv_sec = (long)now, when.tv_usec = 0; flow->updateTcpFlags((const struct bpf_timeval*)&when, zflow->tcp_flags, src2dst_direction); flow->incTcpBadStats(true, zflow->tcp.ooo_in_pkts, zflow->tcp.retr_in_pkts, zflow->tcp.lost_in_pkts); flow->incTcpBadStats(false, zflow->tcp.ooo_out_pkts, zflow->tcp.retr_out_pkts, zflow->tcp.lost_out_pkts); } #ifdef NTOPNG_PRO if(zflow->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(zflow->deviceIP, zflow->inIndex, zflow->out_bytes, zflow->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->outIndex != zflow->inIndex) flow_interfaces_stats->incStats(zflow->deviceIP, zflow->outIndex, zflow->in_bytes, zflow->out_bytes); } } #endif flow->addFlowStats(src2dst_direction, zflow->pkt_sampling_rate*zflow->in_pkts, zflow->pkt_sampling_rate*zflow->in_bytes, 0, zflow->pkt_sampling_rate*zflow->out_pkts, zflow->pkt_sampling_rate*zflow->out_bytes, 0, zflow->last_switched); p.app_protocol = zflow->l7_proto, p.master_protocol = NDPI_PROTOCOL_UNKNOWN; flow->setDetectedProtocol(p, true); flow->setJSONInfo(json_object_to_json_string(zflow->additional_fields)); flow->updateActivities(); flow->updateInterfaceLocalStats(src2dst_direction, zflow->pkt_sampling_rate*(zflow->in_pkts+zflow->out_pkts), zflow->pkt_sampling_rate*(zflow->in_bytes+zflow->out_bytes)); if(zflow->src_process.pid || zflow->dst_process.pid) { if(zflow->src_process.pid) flow->handle_process(&zflow->src_process, src2dst_direction ? true : false); if(zflow->dst_process.pid) flow->handle_process(&zflow->dst_process, src2dst_direction ? false : true); if(zflow->l7_proto == NDPI_PROTOCOL_UNKNOWN) flow->guessProtocol(); } if(zflow->dns_query) flow->setDNSQuery(zflow->dns_query); if(zflow->http_url) flow->setHTTPURL(zflow->http_url); if(zflow->http_site) flow->setServerName(zflow->http_site); if(zflow->ssl_server_name) flow->setServerName(zflow->ssl_server_name); if(zflow->bittorrent_hash) flow->setBTHash(zflow->bittorrent_hash); if(zflow->vrfId) flow->setVRFid(zflow->vrfId); /* Do not put incStats before guessing the flow protocol */ incStats(now, zflow->src_ip.isIPv4() ? ETHERTYPE_IP : ETHERTYPE_IPV6, flow->get_detected_protocol().app_protocol, zflow->pkt_sampling_rate*(zflow->in_bytes + zflow->out_bytes), zflow->pkt_sampling_rate*(zflow->in_pkts + zflow->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->last_switched); } /* **************************************************** */ void NetworkInterface::dumpPacketDisk(const struct pcap_pkthdr *h, const u_char *packet, dump_reason reason) { if(pkt_dumper == NULL) pkt_dumper = new PacketDumper(this); if(pkt_dumper) pkt_dumper->dumpPacket(h, packet, reason, getDumpTrafficSamplingRate(), getDumpTrafficMaxPktsPerFile(), getDumpTrafficMaxSecPerFile()); } /* **************************************************** */ void NetworkInterface::dumpPacketTap(const struct pcap_pkthdr *h, const u_char *packet, dump_reason reason) { if(pkt_dumper_tap) pkt_dumper_tap->writeTap((unsigned char *)packet, h->len, reason, getDumpTrafficSamplingRate()); } /* **************************************************** */ bool NetworkInterface::processPacket(u_int8_t bridge_iface_idx, const struct bpf_timeval *when, const u_int64_t 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; u_int8_t *eth_src = eth->h_source, *eth_dst = eth->h_dest; IpAddress src_ip, dst_ip; u_int16_t src_port = 0, dst_port = 0, payload_len = 0; struct ndpi_tcphdr *tcph = NULL; struct ndpi_udphdr *udph = 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; /* VLAN disaggregation */ if((flowHashingMode == flowhashing_vlan) && (vlan_id > 0)) { NetworkInterface *vIface; if((vIface = getSubInterface((u_int32_t)vlan_id)) != NULL) { bool ret; vIface->setTimeLastPktRcvd(h->ts.tv_sec); ret = vIface->processPacket(bridge_iface_idx, when, time, eth, vlan_id, iph, ip6, ipsize, rawsize, h, packet, ndpiProtocol, srcHost, dstHost, hostFlow); vIface->purgeIdle(h->ts.tv_sec); incStats(when->tv_sec, ETHERTYPE_IP, NDPI_PROTOCOL_UNKNOWN, rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */); return(ret); } } decode_ip: if(iph != NULL) { /* IPv4 */ if(ipsize < 20) { incStats(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(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_ctlun.ip6_un1.ip6_un1_plen); l4_proto = ip6->ip6_ctlun.ip6_un1.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(ipsize < ipv6_shift) { incStats(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(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(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_GRE) { struct grev1_header gre; int offset = sizeof(struct grev1_header); memcpy(&gre, l4, 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(gre.proto == ETHERTYPE_IP) { iph = (struct ndpi_iphdr*)(l4 + offset), ip6 = NULL; goto decode_ip; } else if(gre.proto == ETHERTYPE_IPV6) { iph = (struct ndpi_iphdr*)(l4 + offset), ip6 = NULL; goto decode_ip; } else { /* Unknown encapsulation */ } } 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 /* Updating Flow */ flow = getFlow(eth_src, eth_dst, vlan_id, 0, 0, 0, &src_ip, &dst_ip, src_port, dst_port, l4_proto, &src2dst_direction, last_pkt_rcvd, last_pkt_rcvd, &new_flow); if(flow == NULL) { incStats(when->tv_sec, iph ? ETHERTYPE_IP : ETHERTYPE_IPV6, NDPI_PROTOCOL_UNKNOWN, rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */); return(pass_verdict); } else { *srcHost = src2dst_direction ? flow->get_cli_host() : flow->get_srv_host(); *dstHost = src2dst_direction ? flow->get_srv_host() : flow->get_cli_host(); *hostFlow = flow; if(*srcHost) (*srcHost)->setInterfaceId(bridge_iface_idx); 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 == 8) || /* ICMP Echo [RFC792] */ (icmp_type == 128)) /* ICMPV6 Echo Request [RFC4443] */ src2dst_direction = true; else if((icmp_type == 0) || /* ICMP Echo Reply [RFC792] */ (icmp_type == 129)) /* ICMPV6 Echo Reply [RFC4443] */ src2dst_direction = false; } flow->setICMP(src2dst_direction, icmp_type, icmp_code); 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); } break; } #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 flow->incStats(src2dst_direction, rawsize, payload, payload_len, l4_proto, &h->ts); #endif } /* Protocol Detection */ flow->updateActivities(); flow->updateInterfaceLocalStats(src2dst_direction, 1, rawsize); if(!flow->isDetectionCompleted()) { if(isSampledTraffic()) flow->guessProtocol(); else { 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), false); else flow->setDetectedProtocol(ndpi_detection_process_packet(ndpi_struct, ndpi_flow, ip, ipsize, (u_int32_t)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: if(payload_len > 240) { for(int i = 240; igetTrace()->traceEvent(TRACE_INFO, "[DHCP] %s = '%s'", client_mac, name); snprintf(key, sizeof(key), DHCP_CACHE, get_id()); ntop->getRedis()->hashSet(key, client_mac, name); break; } else if(id == 0xFF) break; /* End of options */ i += len + 2; } } 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((igetTrace()->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_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) == 0x8000) ? false : true; 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)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; default: if(flow->isSSLProto()) flow->dissectSSL(payload, payload_len, when, src2dst_direction); } flow->processDetectedProtocol(); #if defined(NTOPNG_PRO) && !defined(WIN32) if(is_bridge_interface()) { pass_verdict = flow->isPassVerdict(); if(pass_verdict) { u_int8_t shaper_ingress, shaper_egress; char buf[64]; /* In case of uncategorized DNS requests we need to temporarily drop traffic until a decision is made */ if(ntop->getPrefs()->is_flashstart_enabled() && flow->isCategorizationOngoing() && (ndpi_get_lower_proto(flow->get_detected_protocol()) == NDPI_PROTOCOL_DNS)) { /* ntop->getTrace()->traceEvent(TRACE_WARNING, "*** DROPPING UNCATEGORIZED DNS ***"); */ pass_verdict = false; } else { 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); if(pass_verdict) { /* Update pools stats inline only for bridge interfaces! */ if(src2dst_direction) flow->update_pools_stats(when, 1, rawsize, /* sent-only */ 0, 0); else flow->update_pools_stats(when, 0, 0, 1, rawsize /* received-only */); } } } } #endif bool dump_if_unknown = dump_unknown_traffic && (!flow->isDetectionCompleted() || flow->get_detected_protocol().app_protocol == NDPI_PROTOCOL_UNKNOWN); if(dump_if_unknown || dump_all_traffic || dump_security_packets || flow->dumpFlowTraffic()) { if(dump_to_disk) dumpPacketDisk(h, packet, dump_if_unknown ? UNKNOWN : GUI); if(dump_to_tap) dumpPacketTap(h, packet, GUI); } } incStats(when->tv_sec, iph ? ETHERTYPE_IP : ETHERTYPE_IPV6, flow->get_detected_protocol().app_protocol, rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */); 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_INFO, "Purged %u/%u idle flows on %s", n, getNumFlows(), ifname); if((m = purgeIdleHostsMacsASesVlans()) > 0) ntop->getTrace()->traceEvent(TRACE_INFO, "Purged %u/%u idle hosts/macs on %s", n, getNumHosts()+getNumMacs(), ifname); } if(pkt_dumper) pkt_dumper->idle(when); updateSecondTraffic(when); } /* **************************************************** */ bool NetworkInterface::dissectPacket(u_int8_t bridge_iface_idx, 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; if(h->len > ifMTU) { if(!mtuWarningShown) { 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"); #ifdef linux 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); 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(h->ts.tv_sec, 0, NDPI_PROTOCOL_UNKNOWN, rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */); return(pass_verdict); /* Any other non IP protocol */ } memset(&dummy_ethernet, 0, sizeof(dummy_ethernet)); ethernet = (struct ndpi_ethhdr *)&dummy_ethernet; 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 */) { memset(&dummy_ethernet, 0, sizeof(dummy_ethernet)); ethernet = (struct ndpi_ethhdr *)&dummy_ethernet; eth_type = (packet[eth_offset+14] << 8) + packet[eth_offset+15]; ip_offset = 16 + eth_offset; incStats(h->ts.tv_sec, 0, NDPI_PROTOCOL_UNKNOWN, rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */); #ifdef DLT_RAW } else if(pcap_datalink_type == DLT_RAW /* Linux TUN/TAP device in TUN mode; Raw IP capture */) { switch((packet[eth_offset] & 0xf0) >> 4) { case 4: eth_type = ETHERTYPE_IP; break; case 6: eth_type = ETHERTYPE_IPV6; break; default: incStats(h->ts.tv_sec, 0, NDPI_PROTOCOL_UNKNOWN, rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */); return(pass_verdict); /* Unknown IP protocol version */ } memset(&dummy_ethernet, 0, sizeof(dummy_ethernet)); ethernet = (struct ndpi_ethhdr *)&dummy_ethernet; ip_offset = eth_offset; #endif /* DLT_RAW */ } else if(pcap_datalink_type == DLT_IPV4) { eth_type = ETHERTYPE_IP; memset(&dummy_ethernet, 0, sizeof(dummy_ethernet)); ethernet = (struct ndpi_ethhdr *)&dummy_ethernet; ip_offset = 0; } else { incStats(h->ts.tv_sec, 0, NDPI_PROTOCOL_UNKNOWN, rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */); return(pass_verdict); } 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: eth_type = ETHERTYPE_IP; ip_offset += 8; goto decode_packet_eth; break; case ETHERTYPE_IP: if(h->caplen >= ip_offset) { u_int16_t frag_off; struct ndpi_iphdr *iph = (struct ndpi_iphdr *) &packet[ip_offset]; struct ndpi_ipv6hdr *ip6 = NULL; if(iph->version != 4) { /* This is not IPv4 */ incStats(h->ts.tv_sec, ETHERTYPE_IP, NDPI_PROTOCOL_UNKNOWN, rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */); return(pass_verdict); } else frag_off = ntohs(iph->frag_off); if(ntop->getGlobals()->decode_tunnels() && (iph->protocol == IPPROTO_UDP) && ((frag_off & 0x3FFF /* IP_MF | IP_OFFSET */ ) == 0)) { u_short ip_len = ((u_short)iph->ihl * 4); 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(h->ts.tv_sec, ETHERTYPE_IPV6, NDPI_PROTOCOL_UNKNOWN, rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */); return(pass_verdict); } } } 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(h->ts.tv_sec, ETHERTYPE_IPV6, NDPI_PROTOCOL_UNKNOWN, rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */); return(pass_verdict); } 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(h->ts.tv_sec, 0, NDPI_PROTOCOL_UNKNOWN, rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */); return(pass_verdict); } 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(h->ts.tv_sec, 0, NDPI_PROTOCOL_UNKNOWN, rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */); return(pass_verdict); } } } if((vlan_id == 0) && ntop->getPrefs()->do_simulate_vlans()) vlan_id = (ip6 ? ip6->ip6_src.u6_addr.u6_addr8[15] : iph->saddr) & 0xFF; try { pass_verdict = processPacket(bridge_iface_idx, &h->ts, time, ethernet, vlan_id, iph, ip6, h->caplen - ip_offset, rawsize, h, packet, ndpiProtocol, srcHost, dstHost, flow); } 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) { struct ndpi_iphdr *iph = NULL; struct ndpi_ipv6hdr *ip6 = (struct ndpi_ipv6hdr*)&packet[ip_offset]; if((ntohl(ip6->ip6_ctlun.ip6_un1.ip6_un1_flow) & 0xF0000000) != 0x60000000) { /* This is not IPv6 */ incStats(h->ts.tv_sec, ETHERTYPE_IPV6, NDPI_PROTOCOL_UNKNOWN, rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */); return(pass_verdict); } else { u_int ipv6_shift = sizeof(const struct ndpi_ipv6hdr); u_int8_t l4_proto = ip6->ip6_ctlun.ip6_un1.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_UDP)) { // ip_offset += ipv6_shift; if((ip_offset + ipv6_shift) >= h->len) { incStats(h->ts.tv_sec, ETHERTYPE_IPV6, NDPI_PROTOCOL_UNKNOWN, rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */); return(pass_verdict); } 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(h->ts.tv_sec, 0, NDPI_PROTOCOL_UNKNOWN, rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */); return(pass_verdict); } 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(h->ts.tv_sec, 0, NDPI_PROTOCOL_UNKNOWN, rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */); return(pass_verdict); } } } if((vlan_id == 0) && ntop->getPrefs()->do_simulate_vlans()) vlan_id = (ip6 ? ip6->ip6_src.u6_addr.u6_addr8[15] : iph->saddr) & 0xFF; try { pass_verdict = processPacket(bridge_iface_idx, &h->ts, time, ethernet, vlan_id, iph, ip6, h->len - ip_offset, rawsize, h, packet, ndpiProtocol, srcHost, dstHost, flow); } 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 */ Mac *srcMac = getMac(ethernet->h_source, vlan_id, true); Mac *dstMac = getMac(ethernet->h_dest, vlan_id, true); if(srcMac) srcMac->incSentStats(1, rawsize); if(dstMac) dstMac->incRcvdStats(1, rawsize); if(srcMac && dstMac) { const u_int16_t arp_opcode_offset = ip_offset + 6; u_int16_t arp_opcode = 0; if((eth_type == ETHERTYPE_ARP) && (h->len > (u_int16_t)(arp_opcode_offset + 1))) arp_opcode = (packet[arp_opcode_offset] << 8) + packet[arp_opcode_offset + 1]; if(arp_opcode == 0x1 /* ARP request */) { arp_requests++; srcMac->incSentArpRequests(); dstMac->incRcvdArpRequests(); } else if(arp_opcode == 0x2 /* ARP reply */) { arp_replies++; srcMac->incSentArpReplies(); dstMac->incRcvdArpReplies(); } } incStats(h->ts.tv_sec, eth_type, NDPI_PROTOCOL_UNKNOWN, rawsize, 1, 24 /* 8 Preamble + 4 CRC + 12 IFG */); break; } purgeIdle(h->ts.tv_sec); return(pass_verdict); } /* **************************************************** */ void NetworkInterface::startPacketPolling() { if((cpu_affinity != -1) && (ntop->getNumCPUs() > 1)) { if(Utils::setThreadAffinity(pollLoop, cpu_affinity)) ntop->getTrace()->traceEvent(TRACE_WARNING, "Could not 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); } 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_mac_addresses = false; running = false, sprobe_interface = false, inline_interface = false; getStats()->cleanup(); flows_hash->cleanup(); hosts_hash->cleanup(); ases_hash->cleanup(); vlans_hash->cleanup(); macs_hash->cleanup(); ntop->getTrace()->traceEvent(TRACE_NORMAL, "Cleanup interface %s", get_name()); } /* **************************************************** */ void NetworkInterface::findFlowHosts(u_int16_t vlanId, u_int8_t src_mac[6], IpAddress *_src_ip, Host **src, u_int8_t dst_mac[6], IpAddress *_dst_ip, Host **dst) { /* Do not look on sub interfaces, Flows are always created in the same interface of its hosts */ (*src) = hosts_hash->get(vlanId, src_mac, _src_ip); if((*src) == NULL) { if(!hosts_hash->hasEmptyRoom()) { *src = *dst = NULL; has_too_many_hosts = true; return; } (*src) = new Host(this, src_mac, vlanId, _src_ip); 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; } /* ***************************** */ (*dst) = hosts_hash->get(vlanId, dst_mac, _dst_ip); if((*dst) == NULL) { if(!hosts_hash->hasEmptyRoom()) { *dst = NULL; has_too_many_hosts = true; return; } (*dst) = new Host(this, dst_mac, vlanId, _dst_ip); 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) { 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); 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; if(host_ip) h = findHostsByIP(allowed_hosts, (char *)host_ip, vlan_id); retriever.stats = stats; retriever.host = h; walker(walker_flows, flow_sum_protos, (void*)&retriever); } /* **************************************************** */ static bool flow_update_hosts_stats(GenericHashEntry *node, void *user_data) { Flow *flow = (Flow*)node; struct timeval *tv = (struct timeval*)user_data; flow->update_hosts_stats(tv); return(false); /* false = keep on walking */ } /* **************************************************** */ static bool update_hosts_stats(GenericHashEntry *node, void *user_data) { Host *host = (Host*)node; struct timeval *tv = (struct timeval*)user_data; host->updateStats(tv); /* 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) { AutonomousSystem *as = (AutonomousSystem*)node; struct timeval *tv = (struct timeval*)user_data; as->updateStats(tv); return(false); /* false = keep on walking */ } /* **************************************************** */ static bool update_vlans_stats(GenericHashEntry *node, void *user_data) { Vlan *vl = (Vlan*)node; struct timeval *tv = (struct timeval*)user_data; vl->updateStats(tv); return(false); /* false = keep on walking */ } /* **************************************************** */ static bool update_macs_stats(GenericHashEntry *node, void *user_data) { Mac *mac = (Mac*)node; struct timeval *tv = (struct timeval*)user_data; mac->updateStats(tv); return(false); /* false = keep on walking */ } /* **************************************************** */ void NetworkInterface::periodicStatsUpdate() { struct timeval tv; if(isView()) return; gettimeofday(&tv, NULL); flows_hash->walk(flow_update_hosts_stats, (void*)&tv); #ifdef NTOPNG_PRO if(aggregated_flows_hash) { if(--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 hosts_hash->walk(update_hosts_stats, (void*)&tv); ases_hash->walk(update_ases_stats, (void*)&tv); if(hasSeenVlanTaggedPackets()) vlans_hash->walk(update_vlans_stats, (void*)&tv); macs_hash->walk(update_macs_stats, (void*)&tv); if(ntop->getPrefs()->do_dump_flows_on_mysql()) { static_cast(db)->updateStats(&tv); db->flush(); } #ifdef NTOPNG_PRO if(host_pools) host_pools->updateStats(&tv); #endif } /* **************************************************** */ struct update_host_pool_l7policy { bool update_pool_id; bool update_l7policy; }; static bool update_host_host_pool_l7policy(GenericHashEntry *node, void *user_data) { 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 if(up->update_pool_id) h->updateHostPool(); if(up->update_l7policy) h->updateHostL7Policy(); #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 */ } /* **************************************************** */ void NetworkInterface::refreshHostPools() { 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(update_host_host_pool_l7policy, &update_host); #ifdef NTOPNG_PRO if(update_host.update_l7policy) updateFlowsL7Policy(); #endif } /* **************************************************** */ #ifdef NTOPNG_PRO /* **************************************************** */ static bool update_flow_l7_policy(GenericHashEntry *node, void *user_data) { Flow *f = (Flow*)node; f->updateFlowShapers(); f->updateProfile(); return(false); /* false = keep on walking */ } /* **************************************************** */ void NetworkInterface::updateHostsL7Policy(u_int16_t host_pool_id) { if(isView()) return; struct update_host_pool_l7policy update_host; update_host.update_pool_id = false; update_host.update_l7policy = true; hosts_hash->walk(update_host_host_pool_l7policy, &update_host); } /* **************************************************** */ void NetworkInterface::updateFlowsL7Policy() { if(isView()) return; flows_hash->walk(update_flow_l7_policy, NULL); } /* **************************************************** */ static bool flow_recheck_quota_walker(GenericHashEntry *flow, void *user_data) { Flow *f = (Flow*)flow; f->recheckQuota(); return(false); /* false = keep on walking */ } static bool host_reset_quotas(GenericHashEntry *host, void *user_data) { Host *h = (Host*)host; h->resetQuotaStats(); h->resetBlockedTrafficStatus(); return(false); /* false = keep on walking */ } void NetworkInterface::resetPoolsStats() { if(host_pools) { disablePurge(true); host_pools->resetPoolsStats(); walker(walker_hosts, host_reset_quotas, NULL); walker(walker_flows, flow_recheck_quota_walker, NULL); 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; }; /* **************************************************** */ static bool find_host_by_name(GenericHashEntry *h, void *user_data) { struct host_find_info *info = (struct host_find_info*)user_data; Host *host = (Host*)h; #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)) { if((host->get_name() == NULL) && host->get_ip()) { char ip_buf[32], name_buf[96]; 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 */) host->setName(name_buf); } if(host->get_name() && (!strcmp(host->get_name(), info->host_to_find))) { info->h = host; return(true); /* found */ } } return(false); /* false = keep on walking */ } /* **************************************************** */ static bool find_mac_by_name(GenericHashEntry *h, void *user_data) { struct mac_find_info *info = (struct mac_find_info*)user_data; Mac *m = (Mac*)h; if((info->m == NULL) && ((info->vlan_id == 0) || (m->get_vlan_id() == info->vlan_id)) && (!memcmp(info->mac, m->get_mac(), 6)) ) { info->m = m; return(true); /* found */ } return(false); /* false = keep on walking */ } /* **************************************************** */ static bool find_as_by_asn(GenericHashEntry *he, void *user_data) { 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; return(true); /* found */ } return(false); /* false = keep on walking */ } /* **************************************************** */ static bool find_vlan_by_vlan_id(GenericHashEntry *he, void *user_data) { 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; return(true); /* found */ } return(false); /* false = keep on walking */ } /* **************************************************** */ bool NetworkInterface::restoreHost(char *host_ip, u_int16_t vlan_id) { Host *h = new Host(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; memset(&info, 0, sizeof(info)); info.host_to_find = host_ip, info.vlan_id = vlan_id; walker(walker_hosts, find_host_by_name, (void*)&info); h = info.h; } else { IpAddress *ip = new IpAddress(); if(ip) { ip->set(host_ip); if(!isView()) h = hosts_hash->get(vlan_id, ip); else { for(u_int8_t s = 0; sget_hosts_hash()->get(vlan_id, ip); if(h) break; } } delete ip; } } return(h); } /* **************************************************** */ #ifdef NTOPNG_PRO static bool update_flow_profile(GenericHashEntry *h, void *user_data) { Flow *flow = (Flow*)h; flow->updateProfile(); return(false); /* false = keep on walking */ } /* **************************************************** */ void NetworkInterface::updateFlowProfiles() { if(isView()) return; if(ntop->getPro()->has_valid_license()) { FlowProfiles *newP; 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(update_flow_profile, NULL); } } #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 = findHostsByIP(allowed_hosts, host_ip, vlan_id); if(h) { h->lua(vm, allowed_hosts, true, true, true, false, false); ret = true; } else ret = false; enablePurge(false); return ret; } /* **************************************************** */ Host* NetworkInterface::findHostsByIP(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; 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, hostMacsOnly; char *country; int ndpi_proto; /* Not used in flow_search_walker */ sortField sorter; LocationPolicy location; /* Not used in flow_search_walker */ u_int8_t ipVersionFilter; /* Not used in flow_search_walker */ u_int16_t vlan_id; char *osFilter; u_int32_t asnFilter; int16_t networkFilter; u_int16_t poolFilter; /* Return values */ u_int32_t maxNumEntries, actNumEntries; struct flowHostRetrieveList *elems; /* Paginator */ Paginator *pag; }; /* **************************************************** */ static bool flow_search_walker(GenericHashEntry *h, void *user_data) { struct flowHostRetriever *retriever = (struct flowHostRetriever*)user_data; Flow *f = (Flow*)h; int ndpi_proto; u_int16_t port; int16_t local_network_id; u_int8_t ip_version; LocationPolicy client_policy; LocationPolicy server_policy; bool unicast, unidirectional, alerted_flows; if(retriever->actNumEntries >= retriever->maxNumEntries) return(true); /* Limit reached */ if(f && (!f->idle())) { if(retriever->host && (retriever->host != f->get_cli_host()) && (retriever->host != f->get_srv_host())) return(false); /* false = keep on walking */ 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); /* false = keep on walking */ 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); /* false = keep on walking */ if(retriever->pag && retriever->pag->portFilter(&port) && f->get_cli_port() != port && f->get_srv_port() != port) return(false); /* false = keep on walking */ 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); /* false = keep on walking */ 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); /* false = keep on walking */ 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); /* false = keep on walking */ if(retriever->pag && retriever->pag->alertedFlows(&alerted_flows) && ((alerted_flows && f->getFlowStatus() == status_normal) || (!alerted_flows && f->getFlowStatus() != status_normal))) return(false); /* false = keep on walking */ 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); /* false = keep on walking */ /* 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); /* false = keep on walking */ retriever->elems[retriever->actNumEntries].flow = f; if(f->match(retriever->allowed_hosts)) { 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: if(f->getDNSQuery()) retriever->elems[retriever->actNumEntries++].stringValue = f->getDNSQuery(); else if(f->getHTTPURL()) retriever->elems[retriever->actNumEntries++].stringValue = f->getHTTPURL(); else if(f->getSSLCertificate()) retriever->elems[retriever->actNumEntries++].stringValue = f->getSSLCertificate(); else retriever->elems[retriever->actNumEntries++].stringValue = (char*)""; break; default: ntop->getTrace()->traceEvent(TRACE_WARNING, "Internal error: column %d not handled", retriever->sorter); break; } } } return(false); /* false = keep on walking */ } /* **************************************************** */ static bool host_search_walker(GenericHashEntry *he, void *user_data) { char buf[64]; 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->vlan_id && (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->hostMacsOnly && h->getMac() && !h->getMac()->isSourceMac()) || (r->mac && (! h->getMac()->equal(r->vlan_id, r->mac))) || ((r->poolFilter != (u_int16_t)-1) && (r->poolFilter != h->get_host_pool())) || (r->country && strlen(r->country) && (!h->get_country() || strcmp(h->get_country(), r->country))) || (r->osFilter && strlen(r->osFilter) && (!h->get_os() || strcmp(h->get_os(), r->osFilter))) || (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_name(buf, sizeof(buf), false)); break; case column_country: r->elems[r->actNumEntries++].stringValue = strdup(h->get_country() ? h->get_country() : (char*)""); break; case column_os: r->elems[r->actNumEntries++].stringValue = strdup(h->get_os() ? h->get_os() : (char*)""); 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_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: r->elems[r->actNumEntries++].ipValue = ntop->getLocalNetworkIp(h->get_local_network_id()); 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_uploaders: r->elems[r->actNumEntries++].numericValue = h->getNumBytesSent(); break; case column_downloaders: r->elems[r->actNumEntries++].numericValue = h->getNumBytesRcvd(); break; case column_unknowers: r->elems[r->actNumEntries++].numericValue = h->get_ndpi_stats()->getProtoBytes(NDPI_PROTOCOL_UNKNOWN); break; case column_incomingflows: r->elems[r->actNumEntries++].numericValue = h->getNumIncomingFlows(); break; case column_outgoingflows: r->elems[r->actNumEntries++].numericValue = h->getNumOutgoingFlows(); break; default: ntop->getTrace()->traceEvent(TRACE_WARNING, "Internal error: column %d not handled", r->sorter); break; } return(false); /* false = keep on walking */ } /* **************************************************** */ static bool mac_search_walker(GenericHashEntry *he, void *user_data) { 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->vlan_id && (r->vlan_id != m->get_vlan_id()))) || (r->sourceMacsOnly && !m->isSourceMac()) || (r->hostMacsOnly && m->getNumHosts() == 0) || ((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_vlan: r->elems[r->actNumEntries++].numericValue = m->get_vlan_id(); 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_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; } return(false); /* false = keep on walking */ } /* **************************************************** */ static bool as_search_walker(GenericHashEntry *he, void *user_data) { 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; } return(false); /* false = keep on walking */ } /* **************************************************** */ static bool vlan_search_walker(GenericHashEntry *he, void *user_data) { 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; } 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 ipSorter(const void *_a, const void *_b) { struct flowHostRetrieveList *a = (struct flowHostRetrieveList*)_a; struct flowHostRetrieveList *b = (struct flowHostRetrieveList*)_b; if(!a || !b || !a->ipValue || !b->ipValue) return(true); return(a->ipValue->compare(b->ipValue)); } 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(); vlans_hash->disablePurge(); macs_hash->disablePurge(); } } else { for(u_int8_t s = 0; sget_flows_hash()->disablePurge(); else { subInterfaces[s]->get_hosts_hash()->disablePurge(); subInterfaces[s]->get_ases_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(); vlans_hash->enablePurge(); macs_hash->enablePurge(); } } else { for(u_int8_t s = 0; sget_flows_hash()->enablePurge(); else { subInterfaces[s]->get_hosts_hash()->enablePurge(); subInterfaces[s]->get_ases_hash()->enablePurge(); subInterfaces[s]->get_vlans_hash()->enablePurge(); subInterfaces[s]->get_macs_hash()->enablePurge(); } } } } /* **************************************************** */ #ifdef NOTUSED int NetworkInterface::getFlows(lua_State* vm, AddressTree *allowed_hosts, Host *host, int ndpi_proto, LocationPolicy location, char *sortColumn, u_int32_t maxHits, u_int32_t toSkip, bool a2zSortOrder) { struct flowHostRetriever retriever; int (*sorter)(const void *_a, const void *_b); DetailsLevel highDetails = (location == location_local_only || (maxHits != CONST_MAX_NUM_HITS)) ? details_high : details_normal; if((maxHits > CONST_MAX_NUM_HITS) || (maxHits == 0)) maxHits = CONST_MAX_NUM_HITS; retriever.pag = NULL; retriever.host = host, retriever.ndpi_proto = ndpi_proto, retriever.location = location; 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), sorter = numericSorter; /* ******************************* */ disablePurge(true); walker(walker_flows, flow_search_walker, (void*)&retriever); qsort(retriever.elems, retriever.actNumEntries, sizeof(struct flowHostRetrieveList), sorter); lua_newtable(vm); if(a2zSortOrder) { for(int i=toSkip, num=0; i<(int)retriever.actNumEntries; i++) { lua_newtable(vm); retriever.elems[i].flow->lua(vm, allowed_hosts, highDetails, true); lua_pushnumber(vm, num + 1); lua_insert(vm, -2); lua_settable(vm, -3); if(++num >= (int)maxHits) break; } } else { for(int i=(retriever.actNumEntries-1-toSkip), num=0; i>=0; i--) { lua_newtable(vm); retriever.elems[i].flow->lua(vm, allowed_hosts, highDetails, true); lua_pushnumber(vm, num + 1); lua_insert(vm, -2); lua_settable(vm, -3); if(++num >= (int)maxHits) break; } } enablePurge(true); free(retriever.elems); return(retriever.actNumEntries); } #endif /* **************************************************** */ int NetworkInterface::getFlows(lua_State* vm, AddressTree *allowed_hosts, Host *host, Paginator *p) { struct flowHostRetriever retriever; int (*sorter)(const void *_a, const void *_b); char sortColumn[32]; DetailsLevel highDetails; 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)); if (! p->getDetailsLevel(&highDetails)) highDetails = p->detailedResults() ? details_high : (local_hosts || (p && p->maxHits() != CONST_MAX_NUM_HITS)) ? details_high : details_normal; 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); } snprintf(sortColumn, sizeof(sortColumn), "%s", p->sortColumn()); 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; } /* ******************************* */ disablePurge(true); walker(walker_flows, flow_search_walker, (void*)&retriever); qsort(retriever.elems, retriever.actNumEntries, sizeof(struct flowHostRetrieveList), sorter); lua_newtable(vm); lua_push_int_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_pushnumber(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_pushnumber(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); free(retriever.elems); return(retriever.actNumEntries); } /* **************************************************** */ int NetworkInterface::getLatestActivityHostsList(lua_State* vm, AddressTree *allowed_hosts) { struct flowHostRetriever retriever; memset(&retriever, 0, sizeof(retriever)); // there's not even the need to use the retriever or to sort results here // we use the retriever just to leverage on the existing code. retriever.allowed_hosts = allowed_hosts, retriever.location = location_all; retriever.actNumEntries = 0, retriever.maxNumEntries = getHostsHashSize(); retriever.asnFilter = (u_int32_t)-1, retriever.poolFilter = (u_int16_t)-1; retriever.networkFilter = -2; retriever.ndpi_proto = -1; retriever.sorter = column_vlan; // just a placeholder, we don't care as we won't sort retriever.elems = (struct flowHostRetrieveList*)calloc(sizeof(struct flowHostRetrieveList), retriever.maxNumEntries); if(retriever.elems == NULL) { ntop->getTrace()->traceEvent(TRACE_WARNING, "Out of memory :-("); return(-1); } disablePurge(false); walker(walker_hosts, host_search_walker, (void*)&retriever); lua_newtable(vm); if(retriever.actNumEntries > 0) { for(int i=0; i<(int)retriever.actNumEntries; i++) { Host *h = retriever.elems[i].hostValue; if(i < CONST_MAX_NUM_HITS) h->lua(vm, NULL /* Already checked */, false /* host details */, false /* verbose */, false /* return host */, true /* as list element*/, true /* exclude deserialized bytes */); } } enablePurge(false); free(retriever.elems); return(retriever.actNumEntries); } /* **************************************************** */ int NetworkInterface::sortHosts(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, u_int8_t ipver_filter, int proto_filter, bool hostMacsOnly, char *sortColumn) { u_int32_t maxHits; u_int8_t macAddr[6]; int (*sorter)(const void *_a, const void *_b); if(retriever == NULL) return -1; if((!isPacketInterface()) || (pcap_datalink_type == DLT_NULL) || (pcap_datalink_type == DLT_RAW) || (!strcmp(ifname, "lo")) ) hostMacsOnly = false; 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->ndpi_proto = proto_filter; retriever->maxNumEntries = maxHits, retriever->hostMacsOnly = hostMacsOnly; 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_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 = ipSorter; 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_uploaders")) retriever->sorter = column_uploaders, sorter = numericSorter; else if(!strcmp(sortColumn, "column_downloaders")) retriever->sorter = column_downloaders, sorter = numericSorter; else if(!strcmp(sortColumn, "column_unknowers")) retriever->sorter = column_unknowers, sorter = numericSorter; else if(!strcmp(sortColumn, "column_incomingflows")) retriever->sorter = column_incomingflows, sorter = numericSorter; else if(!strcmp(sortColumn, "column_outgoingflows")) retriever->sorter = column_outgoingflows, 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(walker_hosts, host_search_walker, (void*)retriever); qsort(retriever->elems, retriever->actNumEntries, sizeof(struct flowHostRetrieveList), sorter); return(retriever->actNumEntries); } /* **************************************************** */ int NetworkInterface::sortMacs(struct flowHostRetriever *retriever, u_int8_t bridge_iface_idx, u_int16_t vlan_id, bool sourceMacsOnly, bool hostMacsOnly, const char *manufacturer, char *sortColumn, u_int16_t pool_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->vlan_id = vlan_id, retriever->sourceMacsOnly = sourceMacsOnly, retriever->hostMacsOnly = hostMacsOnly, retriever->actNumEntries = 0, retriever->poolFilter = pool_filter, retriever->manufacturer = (char *)manufacturer, retriever->maxNumEntries = maxHits, 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_vlan")) 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 if(!strcmp(sortColumn, "column_manufacturer")) retriever->sorter = column_manufacturer, sorter = stringSorter; 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(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); 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(walker_ases, as_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); 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(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_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, u_int8_t ipver_filter, int proto_filter, char *sortColumn, u_int32_t maxHits, u_int32_t toSkip, bool a2zSortOrder) { struct flowHostRetriever retriever; disablePurge(false); if(sortHosts(&retriever, bridge_iface_idx, allowed_hosts, host_details, location, countryFilter, mac_filter, vlan_id, osFilter, asnFilter, networkFilter, pool_filter, ipver_filter, proto_filter, false /* All MACs */, sortColumn) < 0) { enablePurge(false); return -1; } lua_newtable(vm); lua_push_int_table_entry(vm, "numHosts", retriever.actNumEntries); 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; h->lua(vm, NULL /* Already checked */, host_details, false, false, true, false); } } else { for(int i = (retriever.actNumEntries-1-toSkip), num=0; i >= 0 && num < (int)maxHits; i--, num++) { Host *h = retriever.elems[i].hostValue; h->lua(vm, NULL /* Already checked */, host_details, false, false, true, false); } } 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; igetTrace()->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; inum_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(); return(false); /* false = keep on walking */ } /* **************************************************** */ void NetworkInterface::getFlowsStats(lua_State* vm) { struct active_flow_stats stats; memset(&stats, 0, sizeof(stats)); walker(walker_flows, flow_stats_walker, (void*)&stats); lua_newtable(vm); lua_push_int_table_entry(vm, "num_flows", stats.num_flows); lua_newtable(vm); for(int i=0; i 0) lua_push_int_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 0) lua_push_int_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() { time_t last_packet_time = getTimeLastPktRcvd(); 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 */ u_int n; ntop->getTrace()->traceEvent(TRACE_INFO, "Purging idle flows [ifname: %s] [ifid: %i] [current size: %i]", ifname, id, flows_hash->getCurrentSize()); n = flows_hash->purgeIdle(); if(flowHashing) { FlowHashing *current, *tmp; HASH_ITER(hh, flowHashing, current, tmp) current->iface->purgeIdleFlows(); } next_idle_flow_purge = last_packet_time + FLOW_PURGE_FREQUENCY; return(n); } } /* **************************************************** */ u_int64_t NetworkInterface::getNumPackets() { u_int64_t tot = ethStats.getNumPackets(); for(u_int8_t s = 0; sgetNumPackets(); return(tot); }; /* **************************************************** */ u_int64_t NetworkInterface::getNumBytes() { u_int64_t tot = ethStats.getNumBytes(); for(u_int8_t s = 0; sgetNumBytes(); return(tot); } /* **************************************************** */ u_int32_t NetworkInterface::getNumPacketDrops() { u_int32_t tot = getNumDroppedPackets(); for(u_int8_t s = 0; sgetNumDroppedPackets(); return(tot); }; /* **************************************************** */ u_int NetworkInterface::getNumFlows() { u_int tot = flows_hash ? flows_hash->getNumEntries() : 0; for(u_int8_t s = 0; sgetNumFlows(); return(tot); }; /* **************************************************** */ u_int NetworkInterface::getNumHosts() { u_int tot = numHosts; for(u_int8_t s = 0; sgetNumHosts(); return(tot); }; /* **************************************************** */ u_int NetworkInterface::getNumLocalHosts() { u_int tot = numLocalHosts; for(u_int8_t s = 0; sgetNumLocalHosts(); return(tot); }; /* **************************************************** */ u_int NetworkInterface::getNumHTTPHosts() { u_int tot = hosts_hash ? hosts_hash->getNumHTTPEntries() : 0; for(u_int8_t s = 0; sgetNumHTTPHosts(); return(tot); }; /* **************************************************** */ u_int NetworkInterface::getNumMacs() { u_int tot = macs_hash ? macs_hash->getNumEntries() : 0; for(u_int8_t s = 0; sgetNumMacs(); return(tot); }; /* **************************************************** */ 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() + vlans_hash->purgeIdle(); if(flowHashing) { FlowHashing *current, *tmp; HASH_ITER(hh, flowHashing, current, tmp) current->iface->purgeIdleHostsMacsASesVlans(); } next_idle_host_purge = last_packet_time + HOST_PURGE_FREQUENCY; return(n); } } /* *************************************** */ void NetworkInterface::getnDPIProtocols(lua_State *vm) { int i; lua_newtable(vm); for(i=0; i<(int)ndpi_struct->ndpi_num_supported_protocols; i++) { char buf[8]; snprintf(buf, sizeof(buf), "%d", i); lua_push_str_table_entry(vm, ndpi_struct->proto_defaults[i].protoName, buf); } } /* **************************************************** */ void NetworkInterface::getnDPIProtocols(lua_State *vm, ndpi_protocol_category_t filter) { int i; lua_newtable(vm); for(i=0; i<(int)ndpi_struct->ndpi_num_supported_protocols; i++) { char buf[8]; if(ndpi_struct->proto_defaults[i].protoCategory == filter) { snprintf(buf, sizeof(buf), "%d", i); lua_push_str_table_entry(vm, ndpi_struct->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) { Flow *flow = (Flow*)node; u_int32_t *num_flows = (u_int32_t*)user_data; switch(flow->getFlowState()) { case flow_state_syn: num_flows[1]++; break; case flow_state_established: num_flows[2]++; break; case flow_state_rst: num_flows[0]++; break; case flow_state_fin: num_flows[3]++; break; default: /* UDP... */ break; } return(false /* keep walking */); } /* *************************************** */ static bool num_flows_walker(GenericHashEntry *node, void *user_data) { Flow *flow = (Flow*)node; u_int32_t *num_flows = (u_int32_t*)user_data; num_flows[flow->get_detected_protocol().app_protocol]++; return(false /* keep walking */); } /* *************************************** */ void NetworkInterface::getFlowsStatus(lua_State *vm) { u_int32_t num_flows[NUM_TCP_STATES] = { 0 }; walker(walker_flows, num_flows_state_walker, num_flows); lua_push_int_table_entry(vm, "RST", num_flows[0]); lua_push_int_table_entry(vm, "SYN", num_flows[1]); lua_push_int_table_entry(vm, "Established", num_flows[2]); lua_push_int_table_entry(vm, "FIN", num_flows[3]); } /* *************************************** */ void NetworkInterface::getnDPIFlowsCount(lua_State *vm) { u_int32_t *num_flows; num_flows = (u_int32_t*)calloc(ndpi_struct->ndpi_num_supported_protocols, sizeof(u_int32_t)); if(num_flows) { walker(walker_flows, num_flows_walker, num_flows); for(int i=0; i<(int)ndpi_struct->ndpi_num_supported_protocols; i++) { if(num_flows[i] > 0) lua_push_int_table_entry(vm, ndpi_struct->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_int_table_entry(vm, "scalingFactor", scalingFactor); lua_push_int_table_entry(vm, "id", id); lua_push_bool_table_entry(vm, "isView", isView()); /* View interface */ lua_push_int_table_entry(vm, "seen.last", getTimeLastPktRcvd()); lua_push_bool_table_entry(vm, "sprobe", get_sprobe_interface()); 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()); if(remoteIfname) lua_push_str_table_entry(vm, "remote.name", remoteIfname); if(remoteIfIPaddr) lua_push_str_table_entry(vm, "remote.if_addr", remoteIfIPaddr); if(remoteProbeIPaddr) lua_push_str_table_entry(vm, "probe.ip", remoteProbeIPaddr); if(remoteProbePublicIPaddr) lua_push_str_table_entry(vm, "probe.public_ip", remoteProbePublicIPaddr); lua_newtable(vm); lua_push_int_table_entry(vm, "packets", getNumPackets()); lua_push_int_table_entry(vm, "bytes", getNumBytes()); lua_push_int_table_entry(vm, "flows", getNumFlows()); lua_push_int_table_entry(vm, "hosts", getNumHosts()); lua_push_int_table_entry(vm, "local_hosts", getNumLocalHosts()); lua_push_int_table_entry(vm, "http_hosts", getNumHTTPHosts()); lua_push_int_table_entry(vm, "drops", getNumPacketDrops()); lua_push_int_table_entry(vm, "devices", numL2Devices); /* even if the counter is global, we put it here on every interface as we may decide to make an elasticsearch thread per interface. */ if(ntop->getPrefs()->do_dump_flows_on_es()) { ntop->getElasticSearch()->lua(vm, false /* Overall */); } else if(ntop->getPrefs()->do_dump_flows_on_mysql()) { if(db) db->lua(vm, false /* Overall */); }else if(ntop->getPrefs()->do_dump_flows_on_ls()){ ntop->getLogstash()->lua(vm, false /* Overall */); } lua_pushstring(vm, "stats"); lua_insert(vm, -2); lua_settable(vm, -3); lua_newtable(vm); lua_push_int_table_entry(vm, "packets", getNumPackets() - getCheckPointNumPackets()); lua_push_int_table_entry(vm, "bytes", getNumBytes() - getCheckPointNumBytes()); lua_push_int_table_entry(vm, "drops", getNumPacketDrops() - getCheckPointNumPacketDrops()); if(ntop->getPrefs()->do_dump_flows_on_es()) { ntop->getElasticSearch()->lua(vm, true /* Since last checkpoint */); } else if(ntop->getPrefs()->do_dump_flows_on_mysql()) { if(db) db->lua(vm, true /* Since last checkpoint */); }else if(ntop->getPrefs()->do_dump_flows_on_ls()){ ntop->getLogstash()->lua(vm, true /* Since last checkpoint */); } lua_pushstring(vm, "stats_since_reset"); lua_insert(vm, -2); lua_settable(vm, -3); lua_push_int_table_entry(vm, "remote_pps", last_remote_pps); lua_push_int_table_entry(vm, "remote_bps", last_remote_bps); icmp_v4.lua(true, vm); icmp_v6.lua(false, vm); lua_push_int_table_entry(vm, "arp.requests", arp_requests); lua_push_int_table_entry(vm, "arp.replies", arp_replies); lua_push_str_table_entry(vm, "type", (char*)get_type()); lua_push_int_table_entry(vm, "speed", ifSpeed); lua_push_int_table_entry(vm, "mtu", ifMTU); lua_push_int_table_entry(vm, "alertLevel", alertLevel); lua_push_str_table_entry(vm, "ip_addresses", (char*)getLocalIPAddresses()); /* 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); 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; ssumStats(&_tcpFlowStats, &_ethStats, &_localStats, &_ndpiStats, &_pktStats, &_tcpPacketStats); _tcpFlowStats.lua(vm, "tcpFlowStats"); _ethStats.lua(vm); _localStats.lua(vm); _ndpiStats.lua(this, vm); _pktStats.lua(vm, "pktSizeDistribution"); _tcpPacketStats.lua(vm, "tcpPacketStats"); if(!isView()) { if(pkt_dumper) pkt_dumper->lua(vm); #ifdef NTOPNG_PRO if(flow_profiles) flow_profiles->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(); } /* **************************************************** */ Mac* NetworkInterface::getMac(u_int8_t _mac[6], u_int16_t vlanId, bool createIfNotPresent) { Mac *ret = NULL; if(_mac == NULL) return(NULL); if(!isView()) ret = macs_hash->get(vlanId, _mac); else { for(u_int8_t s = 0; sget_macs_hash()->get(vlanId, _mac)) != NULL) break; } } if((ret == NULL) && createIfNotPresent) { try { if((ret = new Mac(this, _mac, vlanId)) != NULL) macs_hash->add(ret); } 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); } /* **************************************************** */ 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; sget_vlans_hash()->get(vlanId)) != NULL) break; } } if((ret == NULL) && createIfNotPresent) { try { if((ret = new Vlan(this, vlanId)) != NULL) vlans_hash->add(ret); } 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; sget_ases_hash()->get(ipa)) != NULL) break; } } if((ret == NULL) && createIfNotPresent) { try { if((ret = new AutonomousSystem(this, ipa)) != NULL) ases_hash->add(ret); } 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; if(!isView()) f = (Flow*)(flows_hash->findByKey(key)); else { for(u_int8_t s = 0; sget_flows_hash()->findByKey(key); if(f) break; } } 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) { 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++; /* Stop after CONST_MAX_NUM_FIND_HITS matches */ return((info->num_matches > CONST_MAX_NUM_FIND_HITS) ? true /* stop */ : false /* keep walking */); } /* **************************************************** */ bool NetworkInterface::findHostsByName(lua_State* vm, AddressTree *allowed_hosts, char *key) { struct search_host_info info; info.vm = vm, info.host_name_or_ip = key, info.num_matches = 0, info.allowed_hosts = allowed_hosts; lua_newtable(vm); walker(walker_hosts, hosts_search_walker, (void*)&info); return(info.num_matches > 0); } /* **************************************************** */ bool NetworkInterface::validInterface(char *name) { if(name && (strstr(name, "PPP") /* Avoid to use the PPP interface */ || strstr(name, "dialup") /* Avoid to use the dialup interface */ || strstr(name, "ICSHARE") /* Avoid to use the internet sharing interface */ || strstr(name, "NdisWan"))) { /* Avoid to use the internet sharing interface */ return(false); } return(true); } /* **************************************************** */ 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 ):\n"); else if(!help_printed) ntop->getTrace()->traceEvent(TRACE_NORMAL, "Available interfaces (-i ):"); } for(int i = 0; devpointer != NULL; i++) { if(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 correlator_host_info { lua_State* vm; Host *h; activity_bitmap x; }; static bool correlator_walker(GenericHashEntry *node, void *user_data) { Host *h = (Host*)node; struct correlator_host_info *info = (struct correlator_host_info*)user_data; if(h // && h->isLocalHost() /* Consider only local hosts */ && h->get_ip() && (h != info->h)) { char buf[32], *name = h->get_ip()->print(buf, sizeof(buf)); activity_bitmap y; double pearson; h->getActivityStats()->extractPoints(&y); pearson = Utils::pearsonValueCorrelation(&(info->x), &y); /* ntop->getTrace()->traceEvent(TRACE_WARNING, "%s: %f", name, pearson); */ lua_push_float_table_entry(info->vm, name, (float)pearson); } return(false); /* false = keep on walking */ } static bool similarity_walker(GenericHashEntry *node, void *user_data) { Host *h = (Host*)node; struct correlator_host_info *info = (struct correlator_host_info*)user_data; if(h // && h->isLocalHost() /* Consider only local hosts */ && h->get_ip() && (h != info->h)) { char buf[32], name[64]; if(h->get_vlan_id() == 0) { sprintf(name, "%s",h->get_ip()->print(buf, sizeof(buf))); } else { sprintf(name, "%s@%d",h->get_ip()->print(buf, sizeof(buf)), h->get_vlan_id()); } activity_bitmap y; double jaccard; h->getActivityStats()->extractPoints(&y); jaccard = Utils::JaccardSimilarity(&(info->x), &y); /* ntop->getTrace()->traceEvent(TRACE_WARNING, "%s: %f", name, pearson); */ lua_push_float_table_entry(info->vm, name, (float)jaccard); } return(false); /* false = keep on walking */ } /* **************************************************** */ bool NetworkInterface::correlateHostActivity(lua_State* vm, AddressTree *allowed_hosts, char *host_ip, u_int16_t vlan_id) { Host *h = getHost(host_ip, vlan_id); if(h) { struct correlator_host_info info; memset(&info, 0, sizeof(info)); info.vm = vm, info.h = h; h->getActivityStats()->extractPoints(&info.x); walker(walker_hosts, correlator_walker, &info); return(true); } else return(false); } /* **************************************************** */ bool NetworkInterface::similarHostActivity(lua_State* vm, AddressTree *allowed_hosts, char *host_ip, u_int16_t vlan_id) { Host *h = getHost(host_ip, vlan_id); if(h) { struct correlator_host_info info; memset(&info, 0, sizeof(info)); info.vm = vm, info.h = h; h->getActivityStats()->extractPoints(&info.x); walker(walker_hosts, similarity_walker, &info); return(true); } else return(false); } /* **************************************************** */ struct user_flows { lua_State* vm; char *username; }; static bool userfinder_walker(GenericHashEntry *node, void *user_data) { Flow *f = (Flow*)node; struct user_flows *info = (struct user_flows*)user_data; char *user = f->get_username(true); if(user == NULL) user = f->get_username(false); if(user && (strcmp(user, info->username) == 0)) { f->lua(info->vm, NULL, details_normal /* Minimum details */, false); lua_pushnumber(info->vm, f->key()); // Key lua_insert(info->vm, -2); lua_settable(info->vm, -3); } return(false); /* false = keep on walking */ } /* **************************************************** */ void NetworkInterface::findUserFlows(lua_State *vm, char *username) { struct user_flows u; u.vm = vm, u.username = username; walker(walker_flows, userfinder_walker, &u); } /* **************************************************** */ struct proc_name_flows { lua_State* vm; char *proc_name; }; static bool proc_name_finder_walker(GenericHashEntry *node, void *user_data) { 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_pushnumber(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_pushnumber(info->vm, f->key()); // Key lua_insert(info->vm, -2); lua_settable(info->vm, -3); } } return(false); /* false = keep on walking */ } void NetworkInterface::findProcNameFlows(lua_State *vm, char *proc_name) { struct proc_name_flows u; u.vm = vm, u.proc_name = proc_name; walker(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) { 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_pushnumber(info->vm, f->key()); // Key lua_insert(info->vm, -2); lua_settable(info->vm, -3); } return(false); /* false = keep on walking */ } /* **************************************** */ void NetworkInterface::findPidFlows(lua_State *vm, u_int32_t pid) { struct pid_flows u; u.vm = vm, u.pid = pid; walker(walker_flows, pidfinder_walker, &u); } /* **************************************** */ static bool father_pidfinder_walker(GenericHashEntry *node, void *father_pid_data) { Flow *f = (Flow*)node; struct pid_flows *info = (struct pid_flows*)father_pid_data; if((f->getFatherPid(true) == info->pid) || (f->getFatherPid(false) == info->pid)) { f->lua(info->vm, NULL, details_normal /* Minimum details */, false); lua_pushnumber(info->vm, f->key()); // Key lua_insert(info->vm, -2); lua_settable(info->vm, -3); } return(false); /* false = keep on walking */ } /* **************************************** */ void NetworkInterface::findFatherPidFlows(lua_State *vm, u_int32_t father_pid) { struct pid_flows u; u.vm = vm, u.pid = father_pid; walker(walker_flows, father_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) { 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); return(false); /* false = keep on walking */ } /* **************************************** */ void NetworkInterface::listHTTPHosts(lua_State *vm, char *key) { struct virtual_host_valk_info info; lua_newtable(vm); info.vm = vm, info.key = key, info.num = 0; walker(walker_hosts, virtual_http_hosts_walker, &info); } /* **************************************** */ bool NetworkInterface::isInterfaceUp(char *name) { #ifdef WIN32 return(true); #else struct ifreq ifr; int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); if(strlen(name) >= sizeof(ifr.ifr_name)) return(false); memset(&ifr, 0, sizeof(ifr)); strcpy(ifr.ifr_name, name); if(ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) { closesocket(sock); return(false); } closesocket(sock); return(!!(ifr.ifr_flags & IFF_UP)); #endif } /* **************************************** */ 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(validInterface(devpointer->description) && 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 *addr) { if(ip_addresses.size() == 0) ip_addresses = addr; else { string s = addr; ip_addresses = ip_addresses + "," + s; } } /* **************************************** */ void NetworkInterface::allocateNetworkStats() { u_int8_t numNetworks = ntop->getNumLocalNetworks(); try { networkStats = new NetworkStats[numNetworks]; } 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; } networkStats = NULL; } } /* **************************************** */ NetworkStats* NetworkInterface::getNetworkStats(u_int8_t networkId) { if((networkStats == NULL) || (networkId >= ntop->getNumLocalNetworks())) return(NULL); else return(&networkStats[networkId]); } /* **************************************** */ void NetworkInterface::updateSecondTraffic(time_t when) { u_int64_t bytes = ethStats.getNumBytes(); u_int16_t sec = when % 60; if(sec == 0) { /* Beginning of a new minute */ memcpy(lastMinuteTraffic, currentMinuteTraffic, sizeof(currentMinuteTraffic)); resetSecondTraffic(); } currentMinuteTraffic[sec] = max_val(0, bytes-lastSecTraffic); lastSecTraffic = bytes; }; /* **************************************** */ void NetworkInterface::checkPointCounters(bool drops_only) { if(!drops_only) { checkpointPktCount = getNumPackets(), checkpointBytesCount = getNumBytes(); } checkpointPktDropCount = getNumPacketDrops(); if(ntop->getPrefs()->do_dump_flows_on_es()) { ntop->getElasticSearch()->checkPointCounters(drops_only); } else if(ntop->getPrefs()->do_dump_flows_on_mysql()) { if(db) db->checkPointCounters(drops_only); }else if(ntop->getPrefs()->do_dump_flows_on_ls()){ ntop->getLogstash()->checkPointCounters(drops_only); } } /* **************************************************** */ u_int64_t NetworkInterface::getCheckPointNumPackets() { u_int64_t tot = checkpointPktCount; for(u_int8_t s = 0; sgetCheckPointNumPackets(); return(tot); }; /* **************************************************** */ u_int64_t NetworkInterface::getCheckPointNumBytes() { u_int64_t tot = checkpointBytesCount; for(u_int8_t s = 0; sgetCheckPointNumBytes(); return(tot); } /* **************************************************** */ u_int32_t NetworkInterface::getCheckPointNumPacketDrops() { u_int32_t tot = checkpointPktDropCount; for(u_int8_t s = 0; sgetCheckPointNumPacketDrops(); return(tot); }; /* **************************************** */ void NetworkInterface::setRemoteStats(char *name, char *address, u_int32_t speedMbit, char *remoteProbeAddress, char *remoteProbePublicAddress, u_int64_t remBytes, u_int64_t remPkts, u_int32_t remTime, u_int32_t last_pps, u_int32_t last_bps) { if(name) setRemoteIfname(name); if(address) setRemoteIfIPaddr(address); if(remoteProbeAddress) setRemoteProbeAddr(remoteProbeAddress); if(remoteProbePublicAddress) setRemoteProbePublicAddr(remoteProbePublicAddress); ifSpeed = speedMbit, last_pkt_rcvd = 0, last_pkt_rcvd_remote = remTime, last_remote_pps = last_pps, last_remote_bps = last_bps; if((zmq_initial_pkts == 0) /* ntopng has been restarted */ || (remBytes < zmq_initial_bytes) /* nProbe has been restarted */ ) { /* Start over */ zmq_initial_bytes = remBytes, zmq_initial_pkts = remPkts; } else { remBytes -= zmq_initial_bytes, remPkts -= zmq_initial_pkts; ntop->getTrace()->traceEvent(TRACE_INFO, "[%s][bytes=%u/%u (%d)][pkts=%u/%u (%d)]", ifname, remBytes, ethStats.getNumBytes(), remBytes-ethStats.getNumBytes(), remPkts, ethStats.getNumPackets(), remPkts-ethStats.getNumPackets()); /* * Don't override ethStats here, these stats are properly updated * inside NetworkInterface::processFlow for ZMQ interfaces. * Overriding values here may cause glitches and non-strictly-increasing counters * yielding negative rates. ethStats.setNumBytes(remBytes), ethStats.setNumPackets(remPkts); * */ } } /* **************************************** */ 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; return get_ndpi_proto_category(proto); } /* **************************************** */ static int lua_flow_get_ndpi_category(lua_State* vm) { Flow *f; lua_getglobal(vm, CONST_USER_SCRIPTS_FLOW); f = (Flow*)lua_touserdata(vm, lua_gettop(vm)); if(!f) return(CONST_LUA_ERROR); lua_pushstring(vm, ndpi_category_str(f->get_detected_protocol_category())); return(CONST_LUA_OK); } /* **************************************** */ static int lua_flow_get_ndpi_proto(lua_State* vm) { Flow *f; char buf[32]; lua_getglobal(vm, CONST_USER_SCRIPTS_FLOW); f = (Flow*)lua_touserdata(vm, lua_gettop(vm)); if(!f) return(CONST_LUA_ERROR); lua_pushstring(vm, f->get_detected_protocol_name(buf, sizeof(buf))); return(CONST_LUA_OK); } /* **************************************** */ static int lua_flow_get_ndpi_proto_id(lua_State* vm) { Flow *f; ndpi_protocol p; lua_getglobal(vm, CONST_USER_SCRIPTS_FLOW); f = (Flow*)lua_touserdata(vm, lua_gettop(vm)); if(!f) return(CONST_LUA_ERROR); else p = f->get_detected_protocol(); lua_pushnumber(vm, (p.app_protocol != NDPI_PROTOCOL_UNKNOWN) ? p.app_protocol : p.master_protocol); return(CONST_LUA_OK); } /* **************************************** */ static int lua_flow_get_first_seen(lua_State* vm) { Flow *f; lua_getglobal(vm, CONST_USER_SCRIPTS_FLOW); f = (Flow*)lua_touserdata(vm, lua_gettop(vm)); if(!f) return(CONST_LUA_ERROR); lua_pushnumber(vm, f->get_first_seen()); return(CONST_LUA_OK); } /* **************************************** */ static int lua_flow_get_last_seen(lua_State* vm) { Flow *f; lua_getglobal(vm, CONST_USER_SCRIPTS_FLOW); f = (Flow*)lua_touserdata(vm, lua_gettop(vm)); if(!f) return(CONST_LUA_ERROR); lua_pushnumber(vm, f->get_last_seen()); return(CONST_LUA_OK); } /* **************************************** */ static int lua_flow_get_server_name(lua_State* vm) { Flow *f; char buf[64]; const char *srv; lua_getglobal(vm, CONST_USER_SCRIPTS_FLOW); f = (Flow*)lua_touserdata(vm, lua_gettop(vm)); if(!f) return(CONST_LUA_ERROR); srv = f->getFlowServerInfo(); if(!srv && f->get_srv_host()) srv = f->get_srv_host()->get_name(buf, sizeof(buf), false); if(!srv) srv = ""; lua_pushstring(vm, srv); return(CONST_LUA_OK); } /* **************************************** */ static int lua_flow_get_http_url(lua_State* vm) { Flow *f; lua_getglobal(vm, CONST_USER_SCRIPTS_FLOW); f = (Flow*)lua_touserdata(vm, lua_gettop(vm)); if(!f) return(CONST_LUA_ERROR); lua_pushstring(vm, f->getHTTPURL()); return(CONST_LUA_OK); } /* **************************************** */ static int lua_flow_get_http_content_type(lua_State* vm) { Flow *f; lua_getglobal(vm, CONST_USER_SCRIPTS_FLOW); f = (Flow*)lua_touserdata(vm, lua_gettop(vm)); if(!f) return(CONST_LUA_ERROR); lua_pushstring(vm, f->getHTTPContentType()); return(CONST_LUA_OK); } /* **************************************** */ static int lua_flow_dump(lua_State* vm) { Flow *f; lua_getglobal(vm, CONST_USER_SCRIPTS_FLOW); f = (Flow*)lua_touserdata(vm, lua_gettop(vm)); if(!f) return(CONST_LUA_ERROR); f->lua(vm, NULL, details_high, false); return(CONST_LUA_OK); } /* ****************************************** */ /* These callbacks operate on the global flow of user scripts */ static const luaL_Reg flow_reg[] = { { "getNdpiCategory", lua_flow_get_ndpi_category }, { "getNdpiProto", lua_flow_get_ndpi_proto }, { "getNdpiProtoId", lua_flow_get_ndpi_proto_id }, { "getFirstSeen", lua_flow_get_first_seen }, { "getLastSeen", lua_flow_get_last_seen }, { "getServerName", lua_flow_get_server_name }, { "getHTTPUrl", lua_flow_get_http_url }, { "getHTTPContentType",lua_flow_get_http_content_type }, { "dump", lua_flow_dump }, { NULL, NULL } }; static const ntop_class_reg ntop_lua_reg[] = { { "flow", flow_reg }, {NULL, NULL} }; lua_State* NetworkInterface::initUserScriptsInterpreter(const char *lua_file, const char *context) { int i; char script_path[256]; lua_State *L; L = luaL_newstate(); if(!L) { ntop->getTrace()->traceEvent(TRACE_ERROR, "Unable to initialize lua interpreter"); return(NULL); } snprintf(script_path, sizeof(script_path), "%s/%s", ntop->getPrefs()->get_callbacks_dir(), lua_file); /* ******************************************** */ luaL_openlibs(L); /* Load base libraries */ Lua::luaRegisterInternalRegs(L); for(i=0; ntop_lua_reg[i].class_name != NULL; i++) Lua::luaRegister(L, &ntop_lua_reg[i]); lua_register(L, "print", ntop_lua_cli_print); lua_pushstring(L, context); lua_setglobal(L, CONST_USER_SCRIPTS_CONTEXT); if(luaL_loadfile(L, script_path) || lua_pcall(L, 0, 0, 0)) { ntop->getTrace()->traceEvent(TRACE_WARNING, "Cannot run lua file %s[%s]: %s", script_path, context, lua_tostring(L, -1)); lua_close(L); L = NULL; } else { ntop->getTrace()->traceEvent(TRACE_INFO, "Successfully interpreted %s[%s]", script_path, context); lua_pushlightuserdata(L, NULL); lua_setglobal(L, CONST_USER_SCRIPTS_FLOW); } return(L); } /* **************************************** */ void NetworkInterface::termLuaInterpreter() { if(L_user_scripts_inline) { lua_close(L_user_scripts_inline); L_user_scripts_inline = NULL; } if(L_user_scripts_periodic) { lua_close(L_user_scripts_periodic); L_user_scripts_periodic = NULL; } } /* **************************************** */ int NetworkInterface::luaEvalFlow(Flow *f, const LuaCallback cb) { int rc; lua_State *L = NULL; const char *luaFunction; UserScriptContext context; if(! ntop->getPrefs()->are_user_scripts_enabled()) return 0; switch(cb) { case callback_flow_create: context = user_script_context_inline, luaFunction = CONST_LUA_FLOW_CREATE; break; case callback_flow_delete: context = user_script_context_periodic, luaFunction = CONST_LUA_FLOW_DELETE; break; case callback_flow_update: context = user_script_context_periodic, luaFunction = CONST_LUA_FLOW_UPDATE; break; case callback_flow_proto_callback: context = user_script_context_inline, luaFunction = CONST_LUA_FLOW_NDPI_DETECT; break; default: ntop->getTrace()->traceEvent(TRACE_WARNING, "Invalid lua callback (%d)", cb); return(-1); } switch(context) { case user_script_context_inline: if (user_scripts_reload_inline) { if(L_user_scripts_inline) lua_close(L_user_scripts_inline); L_user_scripts_inline = initUserScriptsInterpreter(CONST_USER_SCRIPTS_LOADER, CONST_USER_SCRIPTS_CONTEXT_INLINE); user_scripts_reload_inline = false; } L = L_user_scripts_inline; break; case user_script_context_periodic: if (user_scripts_reload_periodic) { if(L_user_scripts_periodic) lua_close(L_user_scripts_periodic); L_user_scripts_periodic = initUserScriptsInterpreter(CONST_USER_SCRIPTS_LOADER, CONST_USER_SCRIPTS_CONTEXT_PERIODIC); user_scripts_reload_periodic = false; } L = L_user_scripts_periodic; break; } if(L == NULL) return(-2); lua_settop(L, 0); /* Reset stack */ lua_pushlightuserdata(L, f); lua_setglobal(L, CONST_USER_SCRIPTS_FLOW); lua_getglobal(L, luaFunction); /* function to be called */ if((rc = lua_pcall(L, 0 /* 0 parameters */, 0 /* no return values */, 0)) != 0) { ntop->getTrace()->traceEvent(TRACE_WARNING, "Error while executing %s [rc=%d][%s]", luaFunction, rc, lua_tostring(L, -1)); } return(rc); } /* **************************************** */ int NetworkInterface::getActiveMacList(lua_State* vm, u_int8_t bridge_iface_idx, u_int16_t vlan_id, bool sourceMacsOnly, bool hostMacsOnly, const char *manufacturer, char *sortColumn, u_int32_t maxHits, u_int32_t toSkip, bool a2zSortOrder, u_int16_t pool_filter) { struct flowHostRetriever retriever; bool show_details = true; disablePurge(false); if(sortMacs(&retriever, bridge_iface_idx, vlan_id, sourceMacsOnly, hostMacsOnly, manufacturer, sortColumn, pool_filter) < 0) { enablePurge(false); return -1; } lua_newtable(vm); lua_push_int_table_entry(vm, "numMacs", retriever.actNumEntries); 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, char *sortColumn, u_int32_t maxHits, u_int32_t toSkip, bool a2zSortOrder, DetailsLevel details_level) { struct flowHostRetriever retriever; disablePurge(false); if(sortASes(&retriever, sortColumn) < 0) { enablePurge(false); return -1; } lua_newtable(vm); lua_push_int_table_entry(vm, "numASes", retriever.actNumEntries); lua_newtable(vm); if(a2zSortOrder) { for(int i = toSkip, num=0; i<(int)retriever.actNumEntries && num < (int)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-toSkip), num=0; i >= 0 && num < (int)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::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_int_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, u_int16_t vlan_id, bool sourceMacsOnly, bool hostMacsOnly, u_int32_t maxHits) { struct flowHostRetriever retriever; disablePurge(false); if(sortMacs(&retriever, bridge_iface_idx, vlan_id, sourceMacsOnly, hostMacsOnly, NULL, (char*)"column_manufacturer", (u_int16_t)-1) < 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); } /* **************************************** */ bool NetworkInterface::getMacInfo(lua_State* vm, char *mac, u_int16_t vlan_id) { struct mac_find_info info; bool ret; memset(&info, 0, sizeof(info)); Utils::parseMac(info.mac, mac), info.vlan_id = vlan_id; disablePurge(false); walker(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::getASInfo(lua_State* vm, u_int32_t asn) { struct as_find_info info; bool ret; memset(&info, 0, sizeof(info)); info.asn = asn; disablePurge(false); walker(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; memset(&info, 0, sizeof(info)); info.vlan_id = vlan_id; disablePurge(false); walker(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 full_refresh = (user_data != NULL) ? true : false; Host *h = (Host*)host; h->refreshHostAlertPrefs(); if (full_refresh) h->loadAlertsCounter(); return(false); /* false = keep on walking */ } /* **************************************** */ void NetworkInterface::refreshHostsAlertPrefs(bool full_refresh) { /* Read the new configuration */ ntop->getPrefs()->refreshHostsAlertsPrefs(); disablePurge(false); /* Update the hosts */ walker(walker_hosts, host_reload_alert_prefs, (void *)full_refresh); enablePurge(false); }; /* **************************************** */ int NetworkInterface::resetPeriodicHostStats(AddressTree* allowed_networks, char *host_ip, u_int16_t host_vlan) { Host *h; int rv; disablePurge(false); if((h = findHostsByIP(allowed_networks, host_ip, host_vlan)) != NULL) { h->resetPeriodicStats(); rv = CONST_LUA_OK; } else rv = CONST_LUA_ERROR; enablePurge(false); return rv; } /* **************************************** */ int NetworkInterface::updateHostTrafficPolicy(AddressTree* allowed_networks, char *host_ip, u_int16_t host_vlan) { Host *h; int rv; disablePurge(false); if((h = findHostsByIP(allowed_networks, host_ip, host_vlan)) != NULL) { h->updateHostTrafficPolicy(host_ip); rv = CONST_LUA_OK; } else rv = CONST_LUA_ERROR; enablePurge(false); return rv; } /* **************************************** */ int NetworkInterface::setHostDumpTrafficPolicy(AddressTree* allowed_networks, char *host_ip, u_int16_t host_vlan, bool dump_traffic_to_disk) { Host *h; int rv; disablePurge(false); if((h = findHostsByIP(allowed_networks, host_ip, host_vlan)) != NULL) { h->setDumpTrafficPolicy(dump_traffic_to_disk); rv = CONST_LUA_OK; } else rv = CONST_LUA_ERROR; enablePurge(false); return rv; } /* **************************************** */ int NetworkInterface::getPeerBytes(AddressTree* allowed_networks, lua_State *vm, char *host_ip, u_int16_t host_vlan, u_int32_t peer_key) { Host *h; int rv; disablePurge(false); if((h = findHostsByIP(allowed_networks, host_ip, host_vlan)) != NULL) { h->getPeerBytes(vm, peer_key); rv = CONST_LUA_OK; } else rv = CONST_LUA_ERROR; enablePurge(false); return rv; } /* **************************************** */ int NetworkInterface::engageReleaseHostAlert(AddressTree* allowed_networks, char *host_ip, u_int16_t host_vlan, bool engage, AlertEngine alert_engine, char *engaged_alert_id, AlertType alert_type, AlertLevel alert_severity, const char *alert_json) { AlertsManager *am; int rv; if((am = getAlertsManager()) != NULL) { if(engage) rv = am->engageHostAlert(host_ip, host_vlan, alert_engine, engaged_alert_id, alert_type, alert_severity, alert_json); else rv = am->releaseHostAlert(host_ip, host_vlan, alert_engine, engaged_alert_id, alert_type, alert_severity, alert_json); } else rv = CONST_LUA_ERROR; return rv; }