mirror of
https://github.com/ntop/ntopng.git
synced 2026-05-19 16:13:43 +00:00
1798 lines
61 KiB
C++
1798 lines
61 KiB
C++
/*
|
|
*
|
|
* (C) 2013-18 - 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"
|
|
|
|
/* *************************************** */
|
|
|
|
Host::Host(NetworkInterface *_iface) : GenericHost(_iface) {
|
|
initialize(NULL, 0, false);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
Host::Host(NetworkInterface *_iface, char *ipAddress, u_int16_t _vlanId) : GenericHost(_iface) {
|
|
ip.set(ipAddress);
|
|
initialize(NULL, _vlanId, true);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
Host::Host(NetworkInterface *_iface, u_int8_t _mac[6],
|
|
u_int16_t _vlanId, IpAddress *_ip) : GenericHost(_iface) {
|
|
ip.set(_ip);
|
|
initialize(_mac, _vlanId, true);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
Host::Host(NetworkInterface *_iface, u_int8_t _mac[6], u_int16_t _vlanId) : GenericHost(_iface) {
|
|
initialize(_mac, _vlanId, true);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
Host::~Host() {
|
|
if(num_uses > 0)
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Internal error: num_uses=%u", num_uses);
|
|
|
|
if(!ip.isEmpty()) dumpStats(false);
|
|
|
|
// ntop->getTrace()->traceEvent(TRACE_NORMAL, "Deleting %s (%s)", k, localHost ? "local": "remote");
|
|
|
|
serialize2redis(); /* possibly dumps counters and data to redis */
|
|
|
|
if(mac) mac->decUses();
|
|
if(as) as->decUses();
|
|
if(vlan) vlan->decUses();
|
|
#ifdef NTOPNG_PRO
|
|
if(sent_to_sketch) delete sent_to_sketch;
|
|
if(rcvd_from_sketch) delete rcvd_from_sketch;
|
|
if(quota_enforcement_stats) delete quota_enforcement_stats;
|
|
if(quota_enforcement_stats_shadow) delete quota_enforcement_stats_shadow;
|
|
|
|
if(l7Policy) free_ptree_l7_policy_data((void*)l7Policy);
|
|
if(l7PolicyShadow) free_ptree_l7_policy_data((void*)l7PolicyShadow);
|
|
#endif
|
|
if(icmp) delete icmp;
|
|
if(dns) delete dns;
|
|
if(http) delete http;
|
|
if(user_activities) delete user_activities;
|
|
if(ifa_stats) delete ifa_stats;
|
|
if(symbolic_name) free(symbolic_name);
|
|
if(continent) free(continent);
|
|
if(country) free(country);
|
|
if(city) free(city);
|
|
if(categoryStats) delete categoryStats;
|
|
if(syn_flood_attacker_alert) delete syn_flood_attacker_alert;
|
|
if(syn_flood_victim_alert) delete syn_flood_victim_alert;
|
|
if(flow_flood_attacker_alert) delete flow_flood_attacker_alert;
|
|
if(flow_flood_victim_alert) delete flow_flood_victim_alert;
|
|
if(m) delete m;
|
|
if(top_sites) delete top_sites;
|
|
if(old_sites) free(old_sites);
|
|
if(info) free(info);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::set_host_label(char *label_name, bool ignoreIfPresent) {
|
|
if(label_name) {
|
|
char buf[64], buf1[64], *host = ip.print(buf, sizeof(buf));
|
|
|
|
host_label_set = true;
|
|
|
|
if(ignoreIfPresent
|
|
&& (!ntop->getRedis()->hashGet((char*)HOST_LABEL_NAMES, host, buf1, (u_int)sizeof(buf1)) /* Found into redis */
|
|
&& (buf1[0] != '\0') /* Not empty */ ))
|
|
return;
|
|
else
|
|
ntop->getRedis()->hashSet((char*)HOST_LABEL_NAMES, host, label_name);
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::computeHostSerial() {
|
|
if(iface && Utils::dumpHostToDB(&ip, ntop->getPrefs()->get_dump_hosts_to_db_policy())) {
|
|
if(host_serial) {
|
|
char buf[64];
|
|
|
|
/* We need to reconfirm the id (e.g. after a day wrap) */
|
|
ntop->getRedis()->setHostId(iface, NULL, ip.print(buf, sizeof(buf)), host_serial);
|
|
} else
|
|
host_serial = ntop->getRedis()->addHostToDBDump(iface, &ip, NULL);
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::initialize(u_int8_t _mac[6], u_int16_t _vlanId, bool init_all) {
|
|
char key[64], redis_key[128], *k;
|
|
char buf[64], host[96];
|
|
|
|
#ifdef NTOPNG_PRO
|
|
sent_to_sketch = rcvd_from_sketch = NULL;
|
|
l7Policy = l7PolicyShadow = NULL;
|
|
has_blocking_quota = has_blocking_shaper = false;
|
|
quota_enforcement_stats = quota_enforcement_stats_shadow = NULL;
|
|
#endif
|
|
host_pool_id = NO_HOST_POOL_ID;
|
|
|
|
if(_mac == NULL)
|
|
mac = NULL;
|
|
else if((mac = iface->getMac(_mac, _vlanId, true)) != NULL)
|
|
mac->incUses();
|
|
|
|
if((vlan = iface->getVlan(_vlanId, true)) != NULL)
|
|
vlan->incUses();
|
|
|
|
num_alerts_detected = 0;
|
|
drop_all_host_traffic = false, dump_host_traffic = false, dhcpUpdated = false,
|
|
num_resolve_attempts = 0;
|
|
attacker_max_num_syn_per_sec = ntop->getPrefs()->get_attacker_max_num_syn_per_sec();
|
|
victim_max_num_syn_per_sec = ntop->getPrefs()->get_victim_max_num_syn_per_sec();
|
|
attacker_max_num_flows_per_sec = ntop->getPrefs()->get_attacker_max_num_flows_per_sec();
|
|
victim_max_num_flows_per_sec = ntop->getPrefs()->get_victim_max_num_flows_per_sec();
|
|
good_low_flow_detected = false;
|
|
networkStats = NULL, local_network_id = -1, nextResolveAttempt = 0, info = NULL;
|
|
syn_flood_attacker_alert = new AlertCounter(attacker_max_num_syn_per_sec, CONST_MAX_THRESHOLD_CROSS_DURATION);
|
|
syn_flood_victim_alert = new AlertCounter(victim_max_num_syn_per_sec, CONST_MAX_THRESHOLD_CROSS_DURATION);
|
|
flow_flood_attacker_alert = new AlertCounter(attacker_max_num_flows_per_sec, CONST_MAX_THRESHOLD_CROSS_DURATION);
|
|
flow_flood_victim_alert = new AlertCounter(victim_max_num_flows_per_sec, CONST_MAX_THRESHOLD_CROSS_DURATION);
|
|
host_label_set = false;
|
|
os[0] = '\0', trafficCategory[0] = '\0', blacklisted_host = false, blacklisted_alarm_emitted = false;
|
|
num_uses = 0, symbolic_name = NULL, vlan_id = _vlanId % MAX_NUM_VLAN,
|
|
total_num_flows_as_client = total_num_flows_as_server = 0,
|
|
num_active_flows_as_client = num_active_flows_as_server = 0;
|
|
trigger_host_alerts = false;
|
|
first_seen = last_seen = iface->getTimeLastPktRcvd();
|
|
nextSitesUpdate = 0;
|
|
if((m = new(std::nothrow) Mutex()) == NULL)
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Internal error: NULL mutex. Are you running out of memory?");
|
|
|
|
memset(&tcpPacketStats, 0, sizeof(tcpPacketStats));
|
|
continent = NULL, country = NULL, city = NULL;
|
|
asn = 0, asname = NULL;
|
|
as = NULL;
|
|
longitude = 0, latitude = 0;
|
|
k = ip.print(key, sizeof(key));
|
|
snprintf(redis_key, sizeof(redis_key), HOST_SERIALIZED_KEY, iface->get_id(), k, vlan_id);
|
|
dns = NULL, http = NULL, categoryStats = NULL, top_sites = NULL, old_sites = NULL,
|
|
user_activities = NULL, ifa_stats = NULL, icmp = NULL;
|
|
|
|
if(init_all) {
|
|
char *strIP = ip.print(buf, sizeof(buf));
|
|
|
|
snprintf(host, sizeof(host), "%s@%u", strIP, vlan_id);
|
|
|
|
updateLocal();
|
|
updateHostTrafficPolicy(host);
|
|
|
|
if(localHost) {
|
|
/* initialize this in any case to support runtime 'are_top_talkers_enabled' changes */
|
|
top_sites = new FrequentStringItems(HOST_SITES_TOP_NUMBER);
|
|
old_sites = strdup("{}");
|
|
|
|
readDHCPCache();
|
|
}
|
|
|
|
// ntop->getTrace()->traceEvent(TRACE_NORMAL, "Loading %s (%s)", k, localHost ? "local": "remote");
|
|
|
|
if(localHost || systemHost) {
|
|
dns = new DnsStats();
|
|
http = new HTTPstats(iface->get_hosts_hash());
|
|
}
|
|
|
|
if((localHost || systemHost)
|
|
&& ntop->getPrefs()->is_idle_local_host_cache_enabled()) {
|
|
char *json;
|
|
|
|
if((json = (char*)malloc(HOST_MAX_SERIALIZED_LEN * sizeof(char))) == NULL)
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR,
|
|
"Unable to allocate memory to deserialize %s", redis_key);
|
|
else if(!ntop->getRedis()->get(redis_key, json, HOST_MAX_SERIALIZED_LEN)){
|
|
bool shadow_localHost = localHost, shadow_systemHost = systemHost; /* Just in case */
|
|
/* Found saved copy of the host so let's start from the previous state */
|
|
// ntop->getTrace()->traceEvent(TRACE_NORMAL, "%s => %s", redis_key, json);
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "Deserializing %s", redis_key);
|
|
|
|
deserialize(json, redis_key);
|
|
localHost = shadow_localHost, systemHost = shadow_systemHost;
|
|
}
|
|
|
|
if(json) free(json);
|
|
}
|
|
|
|
if(localHost || systemHost
|
|
|| ntop->getPrefs()->is_dns_resolution_enabled_for_all_hosts()) {
|
|
char rsp[256];
|
|
|
|
if(ntop->getRedis()->getAddress(host, rsp, sizeof(rsp), true) == 0)
|
|
setName(rsp);
|
|
// else ntop->getRedis()->pushHostToResolve(host, false, localHost);
|
|
}
|
|
|
|
if(!(localHost || systemHost)) {
|
|
blacklisted_host = ntop->isBlacklistedIP(&ip);
|
|
|
|
if((!blacklisted_host) && ntop->getPrefs()->is_httpbl_enabled() && ip.isIPv4()) {
|
|
// http:bl only works for IPv4 addresses
|
|
if(ntop->getRedis()->getAddressTrafficFiltering(host, iface, trafficCategory,
|
|
sizeof(trafficCategory), true) == 0) {
|
|
if(strcmp(trafficCategory, NULL_BL)) {
|
|
blacklisted_host = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if((as = iface->getAS(&ip, true)) != NULL) {
|
|
as->incUses();
|
|
asn = as->get_asn();
|
|
asname = as->get_asname();
|
|
}
|
|
|
|
if(continent) { free(continent); continent = NULL; }
|
|
if(country) { free(country); country = NULL; }
|
|
if(city) { free(city); city = NULL; }
|
|
ntop->getGeolocation()->getInfo(&ip, &continent, &country, &city, &latitude, &longitude);
|
|
|
|
if(localHost || systemHost) {
|
|
#ifdef NTOPNG_PRO
|
|
sent_to_sketch = new CountMinSketch();
|
|
rcvd_from_sketch = new CountMinSketch();
|
|
#endif
|
|
readStats();
|
|
|
|
if(ntop->getPrefs()->is_flow_activity_enabled()) {
|
|
ifa_stats = new InterFlowActivityStats[IFA_STATS_PROTOS_N*INTER_FLOW_ACTIVITY_SLOTS];
|
|
user_activities = new UserActivityStats;
|
|
|
|
if(ifa_stats) memset(ifa_stats, 0, sizeof(InterFlowActivityStats)*IFA_STATS_PROTOS_N*INTER_FLOW_ACTIVITY_SLOTS);
|
|
if(user_activities) memset(user_activities, 0, sizeof(UserActivityStats));
|
|
}
|
|
}
|
|
}
|
|
|
|
refreshHostAlertPrefs();
|
|
|
|
if(!host_serial) computeHostSerial();
|
|
updateHostPool();
|
|
updateHostL7Policy();
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
bool Host::readDHCPCache() {
|
|
if(localHost && mac && (!dhcpUpdated)) {
|
|
/* Check DHCP cache */
|
|
char client_mac[24], buf[64], key[64];
|
|
|
|
dhcpUpdated = true;
|
|
|
|
if(!mac->isNull()) {
|
|
Utils::formatMac(mac->get_mac(), client_mac, sizeof(client_mac));
|
|
|
|
snprintf(key, sizeof(key), DHCP_CACHE, iface->get_id());
|
|
if(ntop->getRedis()->hashGet(key, client_mac, buf, sizeof(buf)) == 0) {
|
|
setName(buf);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
char* Host::get_hostkey(char *buf, u_int buf_len, bool force_vlan) {
|
|
char ipbuf[64];
|
|
char *key = ip.print(ipbuf, sizeof(ipbuf));
|
|
|
|
if((vlan_id > 0) || force_vlan)
|
|
snprintf(buf, buf_len, "%s@%u", key, vlan_id);
|
|
else
|
|
strncpy(buf, key, buf_len);
|
|
|
|
buf[buf_len-1] = '\0';
|
|
return buf;
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::updateHostTrafficPolicy(char *key) {
|
|
if(localHost || systemHost) {
|
|
char buf[64], *host;
|
|
|
|
if(key)
|
|
host = key;
|
|
else
|
|
host = get_hostkey(buf, sizeof(buf));
|
|
|
|
if(iface->isPacketInterface()) {
|
|
if((ntop->getRedis()->hashGet((char*)DROP_HOST_TRAFFIC, host, buf, sizeof(buf)) == -1)
|
|
|| (strcmp(buf, "true") != 0))
|
|
drop_all_host_traffic = false;
|
|
else
|
|
drop_all_host_traffic = true;
|
|
|
|
}
|
|
|
|
if((ntop->getRedis()->hashGet((char*)DUMP_HOST_TRAFFIC,
|
|
host, buf, sizeof(buf)) == -1)
|
|
|| (strcmp(buf, "true") != 0))
|
|
dump_host_traffic = false;
|
|
else
|
|
dump_host_traffic = true;
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::updateHostL7Policy() {
|
|
#ifdef NTOPNG_PRO
|
|
if(!iface->is_bridge_interface() && !iface->getL7Policer())
|
|
return;
|
|
|
|
if(ntop->getPro()->has_valid_license()) {
|
|
|
|
if(l7PolicyShadow) {
|
|
free_ptree_l7_policy_data((void*)l7PolicyShadow);
|
|
l7PolicyShadow = NULL;
|
|
}
|
|
|
|
l7PolicyShadow = l7Policy;
|
|
|
|
#ifdef SHAPER_DEBUG
|
|
{
|
|
char buf[64];
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Updating host policy %s",
|
|
ip.print(buf, sizeof(buf)));
|
|
}
|
|
#endif
|
|
|
|
l7Policy = getInterface()->getL7Policer()->getIpPolicy(host_pool_id);
|
|
resetBlockedTrafficStatus();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::updateHostPool() {
|
|
if(!iface)
|
|
return;
|
|
|
|
host_pool_id = iface->getHostPool(this);
|
|
|
|
#ifdef NTOPNG_PRO
|
|
HostPools *hp = iface->getHostPools();
|
|
|
|
if(hp && hp->enforceQuotasPerPoolMember(host_pool_id)) {
|
|
/* must allocate a structure to keep track of used quotas */
|
|
if(!quota_enforcement_stats) {
|
|
quota_enforcement_stats = new HostPoolStats();
|
|
|
|
#ifdef HOST_POOLS_DEBUG
|
|
char buf[128];
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Allocating quota stats for %s [quota_enforcement_stats: %p] [host pool: %i]",
|
|
ip.print(buf, sizeof(buf)), (void*)quota_enforcement_stats, host_pool_id);
|
|
#endif
|
|
|
|
}
|
|
} else { /* Free the structure that is no longer needed */
|
|
/* It is ensured by the caller that this method is called no more than 1 time per second.
|
|
Therefore, it is safe to delete a previously allocated shadow class */
|
|
if(quota_enforcement_stats_shadow) {
|
|
delete quota_enforcement_stats_shadow;
|
|
quota_enforcement_stats_shadow = NULL;
|
|
|
|
#ifdef HOST_POOLS_DEBUG
|
|
char buf[128];
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Freeing shadow pointer of longer quota stats for %s [host pool: %i]",
|
|
ip.print(buf, sizeof(buf)), host_pool_id);
|
|
#endif
|
|
|
|
}
|
|
if(quota_enforcement_stats) {
|
|
quota_enforcement_stats_shadow = quota_enforcement_stats;
|
|
quota_enforcement_stats = NULL;
|
|
|
|
#ifdef HOST_POOLS_DEBUG
|
|
char buf[128];
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Moving quota stats to the shadow pointer for %s [host pool: %i]",
|
|
ip.print(buf, sizeof(buf)), host_pool_id);
|
|
#endif
|
|
}
|
|
}
|
|
#endif /* NTOPNG_PRO */
|
|
|
|
#ifdef HOST_POOLS_DEBUG
|
|
char buf[128];
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Updating host pool for %s [host pool: %i]",
|
|
ip.print(buf, sizeof(buf)), host_pool_id);
|
|
#endif
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::updateLocal() {
|
|
localHost = ip.isLocalHost(&local_network_id);
|
|
|
|
if(local_network_id >= 0)
|
|
networkStats = getNetworkStats(local_network_id);
|
|
|
|
systemHost = localHost ? ip.isLocalInterfaceAddress() : false;
|
|
|
|
if(0) {
|
|
char buf[64];
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "%s is %s %s [%p]",
|
|
ip.print(buf, sizeof(buf)),
|
|
localHost ? "local" : "remote",
|
|
systemHost ? "systemHost" : "", this);
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::set_mac(char *m) {
|
|
u_int8_t mac_address[6];
|
|
u_int32_t _mac[6] = { 0 };
|
|
|
|
if((m == NULL) || (!strcmp(m, "00:00:00:00:00:00")))
|
|
return;
|
|
|
|
sscanf(m, "%02X:%02X:%02X:%02X:%02X:%02X",
|
|
&_mac[0], &_mac[1], &_mac[2], &_mac[3], &_mac[4], &_mac[5]);
|
|
|
|
mac_address[0] = _mac[0], mac_address[1] = _mac[1],
|
|
mac_address[2] = _mac[2], mac_address[3] = _mac[3],
|
|
mac_address[4] = _mac[4], mac_address[5] = _mac[5];
|
|
|
|
if(mac) mac->decUses();
|
|
|
|
if((mac = iface->getMac(mac_address, vlan_id, true)) != NULL)
|
|
mac->incUses();
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::lua(lua_State* vm, AddressTree *ptree,
|
|
bool host_details, bool verbose,
|
|
bool returnHost, bool asListElement,
|
|
bool exclude_deserialized_bytes) {
|
|
char buf[64], buf_id[64], ip_buf[64], *ipaddr = NULL, *local_net, *host_id = buf_id;
|
|
bool mask_host = Utils::maskHost(localHost);
|
|
|
|
if((ptree && (!match(ptree))) || mask_host)
|
|
return;
|
|
|
|
#if 0
|
|
if(1) {
|
|
char buf[64];
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "********* %s is %s %s [%p]",
|
|
ip.print(buf, sizeof(buf)),
|
|
localHost ? "local" : "remote",
|
|
systemHost ? "systemHost" : "", this);
|
|
}
|
|
#endif
|
|
|
|
lua_newtable(vm);
|
|
lua_push_str_table_entry(vm, "ip", (ipaddr = ip.printMask(ip_buf, sizeof(ip_buf), localHost)));
|
|
lua_push_int_table_entry(vm, "ipkey", ip.key());
|
|
|
|
lua_push_str_table_entry(vm, "mac", Utils::formatMac(mac ? mac->get_mac() : NULL, buf, sizeof(buf)));
|
|
lua_push_bool_table_entry(vm, "localhost", localHost);
|
|
|
|
lua_push_int_table_entry(vm, "bytes.sent",
|
|
sent.getNumBytes() - (exclude_deserialized_bytes ? sent.getNumDeserializedBytes() : 0));
|
|
lua_push_int_table_entry(vm, "bytes.rcvd",
|
|
rcvd.getNumBytes() - (exclude_deserialized_bytes ? rcvd.getNumDeserializedBytes() : 0));
|
|
|
|
lua_push_bool_table_entry(vm, "privatehost", isPrivateHost());
|
|
|
|
lua_push_int_table_entry(vm, "num_alerts", triggerAlerts() ? getNumAlerts() : 0);
|
|
|
|
lua_push_str_table_entry(vm, "name", get_visual_name(buf, sizeof(buf)));
|
|
lua_push_int32_table_entry(vm, "local_network_id", local_network_id);
|
|
|
|
local_net = ntop->getLocalNetworkName(local_network_id);
|
|
if(local_net == NULL)
|
|
lua_push_nil_table_entry(vm, "local_network_name");
|
|
else
|
|
lua_push_str_table_entry(vm, "local_network_name", local_net);
|
|
|
|
lua_push_bool_table_entry(vm, "systemhost", systemHost);
|
|
lua_push_bool_table_entry(vm, "is_blacklisted", blacklisted_host);
|
|
lua_push_bool_table_entry(vm, "childSafe", isChildSafe());
|
|
lua_push_int_table_entry(vm, "source_id", source_id);
|
|
lua_push_int_table_entry(vm, "asn", asn);
|
|
lua_push_int_table_entry(vm, "host_pool_id", host_pool_id);
|
|
lua_push_str_table_entry(vm, "asname", asname ? asname : (char*)"");
|
|
lua_push_str_table_entry(vm, "os", os);
|
|
|
|
lua_push_str_table_entry(vm, "continent", continent ? continent : (char*)"");
|
|
lua_push_str_table_entry(vm, "country", country ? country : (char*)"");
|
|
lua_push_int_table_entry(vm, "active_flows.as_client", num_active_flows_as_client);
|
|
lua_push_int_table_entry(vm, "active_flows.as_server", num_active_flows_as_server);
|
|
lua_push_int_table_entry(vm, "active_http_hosts", http ? http->get_num_virtual_hosts() : 0);
|
|
|
|
#ifdef NTOPNG_PRO
|
|
lua_push_bool_table_entry(vm, "has_blocking_quota", has_blocking_quota);
|
|
lua_push_bool_table_entry(vm, "has_blocking_shaper", has_blocking_shaper);
|
|
#endif
|
|
|
|
if(host_details) {
|
|
/*
|
|
This has been disabled as in case of an attack, most hosts do not have a name and we will waste
|
|
a lot of time doing activities that are not necessary
|
|
*/
|
|
if((symbolic_name == NULL) || (strcmp(symbolic_name, ipaddr) == 0)) {
|
|
/* We resolve immediately the IP address by queueing on the top of address queue */
|
|
|
|
ntop->getRedis()->pushHostToResolve(ipaddr, false, true /* Fake to resolve it ASAP */);
|
|
}
|
|
|
|
if(icmp)
|
|
icmp->lua(ip.isIPv4(), vm);
|
|
}
|
|
|
|
/* TCP stats */
|
|
if(host_details) {
|
|
lua_push_int_table_entry(vm, "tcp.packets.sent", tcp_sent.getNumPkts());
|
|
lua_push_int_table_entry(vm, "tcp.packets.rcvd", tcp_rcvd.getNumPkts());
|
|
|
|
lua_push_int_table_entry(vm, "tcp.bytes.sent", tcp_sent.getNumBytes());
|
|
lua_push_int_table_entry(vm, "tcp.bytes.rcvd", tcp_rcvd.getNumBytes());
|
|
|
|
lua_push_bool_table_entry(vm, "tcp.packets.seq_problems",
|
|
(tcpPacketStats.pktRetr
|
|
|| tcpPacketStats.pktOOO
|
|
|| tcpPacketStats.pktLost) ? true : false);
|
|
lua_push_int_table_entry(vm, "tcp.packets.retransmissions", tcpPacketStats.pktRetr);
|
|
lua_push_int_table_entry(vm, "tcp.packets.out_of_order", tcpPacketStats.pktOOO);
|
|
lua_push_int_table_entry(vm, "tcp.packets.lost", tcpPacketStats.pktLost);
|
|
|
|
} else {
|
|
/* Limit tcp information to anomalies when host_details aren't required */
|
|
if(tcpPacketStats.pktRetr > 0)
|
|
lua_push_int_table_entry(vm, "tcp.packets.retransmissions", tcpPacketStats.pktRetr);
|
|
if(tcpPacketStats.pktOOO > 0)
|
|
lua_push_int_table_entry(vm, "tcp.packets.out_of_order", tcpPacketStats.pktOOO);
|
|
if(tcpPacketStats.pktLost)
|
|
lua_push_int_table_entry(vm, "tcp.packets.lost", tcpPacketStats.pktLost);
|
|
}
|
|
|
|
if(host_details) {
|
|
lua_push_int_table_entry(vm, "total_activity_time", total_activity_time);
|
|
|
|
if(info) lua_push_str_table_entry(vm, "info", getInfo(buf, sizeof(buf)));
|
|
|
|
lua_push_float_table_entry(vm, "latitude", latitude);
|
|
lua_push_float_table_entry(vm, "longitude", longitude);
|
|
lua_push_str_table_entry(vm, "city", city ? city : (char*)"");
|
|
|
|
lua_push_int_table_entry(vm, "flows.as_client", total_num_flows_as_client);
|
|
lua_push_int_table_entry(vm, "flows.as_server", total_num_flows_as_server);
|
|
|
|
lua_push_int_table_entry(vm, "udp.packets.sent", udp_sent.getNumPkts());
|
|
lua_push_int_table_entry(vm, "udp.bytes.sent", udp_sent.getNumBytes());
|
|
lua_push_int_table_entry(vm, "udp.packets.rcvd", udp_rcvd.getNumPkts());
|
|
lua_push_int_table_entry(vm, "udp.bytes.rcvd", udp_rcvd.getNumBytes());
|
|
|
|
lua_push_int_table_entry(vm, "icmp.packets.sent", icmp_sent.getNumPkts());
|
|
lua_push_int_table_entry(vm, "icmp.bytes.sent", icmp_sent.getNumBytes());
|
|
lua_push_int_table_entry(vm, "icmp.packets.rcvd", icmp_rcvd.getNumPkts());
|
|
lua_push_int_table_entry(vm, "icmp.bytes.rcvd", icmp_rcvd.getNumBytes());
|
|
|
|
lua_push_int_table_entry(vm, "other_ip.packets.sent", other_ip_sent.getNumPkts());
|
|
lua_push_int_table_entry(vm, "other_ip.bytes.sent", other_ip_sent.getNumBytes());
|
|
lua_push_int_table_entry(vm, "other_ip.packets.rcvd", other_ip_rcvd.getNumPkts());
|
|
lua_push_int_table_entry(vm, "other_ip.bytes.rcvd", other_ip_rcvd.getNumBytes());
|
|
|
|
lua_push_bool_table_entry(vm, "drop_all_host_traffic", drop_all_host_traffic);
|
|
|
|
/* Host ingress/egress drops */
|
|
lua_push_int_table_entry(vm, "bridge.ingress_drops.bytes", ingress_drops.getNumBytes());
|
|
lua_push_int_table_entry(vm, "bridge.ingress_drops.packets", ingress_drops.getNumPkts());
|
|
lua_push_int_table_entry(vm, "bridge.egress_drops.bytes", egress_drops.getNumBytes());
|
|
lua_push_int_table_entry(vm, "bridge.egress_drops.packets", egress_drops.getNumPkts());
|
|
|
|
lua_push_int_table_entry(vm, "low_goodput_flows.as_client", low_goodput_client_flows);
|
|
lua_push_int_table_entry(vm, "low_goodput_flows.as_server", low_goodput_server_flows);
|
|
|
|
if((!mask_host) && top_sites && ntop->getPrefs()->are_top_talkers_enabled()) {
|
|
lua_push_str_table_entry(vm, "sites", top_sites->json());
|
|
lua_push_str_table_entry(vm, "sites.old", old_sites);
|
|
}
|
|
}
|
|
|
|
if(localHost) {
|
|
/* Criteria */
|
|
lua_newtable(vm);
|
|
|
|
lua_push_int_table_entry(vm, "upload", getNumBytesSent());
|
|
lua_push_int_table_entry(vm, "download", getNumBytesRcvd());
|
|
lua_push_int_table_entry(vm, "unknown", get_ndpi_stats()->getProtoBytes(NDPI_PROTOCOL_UNKNOWN));
|
|
lua_push_int_table_entry(vm, "incomingflows", getNumIncomingFlows());
|
|
lua_push_int_table_entry(vm, "outgoingflows", getNumOutgoingFlows());
|
|
|
|
lua_pushstring(vm, "criteria");
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
}
|
|
|
|
lua_push_int_table_entry(vm, "seen.first", first_seen);
|
|
lua_push_int_table_entry(vm, "seen.last", last_seen);
|
|
lua_push_int_table_entry(vm, "duration", get_duration());
|
|
|
|
// ntop->getTrace()->traceEvent(TRACE_NORMAL, "[pkts_thpt: %.2f] [pkts_thpt_trend: %d]", pkts_thpt,pkts_thpt_trend);
|
|
|
|
if(ntop->getPrefs()->is_httpbl_enabled())
|
|
lua_push_str_table_entry(vm, "httpbl", get_httpbl());
|
|
|
|
lua_push_bool_table_entry(vm, "dump_host_traffic", dump_host_traffic);
|
|
|
|
if(verbose) {
|
|
char *rsp = serialize();
|
|
|
|
if(categoryStats) categoryStats->lua(vm);
|
|
if(ndpiStats) ndpiStats->lua(iface, vm);
|
|
lua_push_str_table_entry(vm, "json", rsp);
|
|
free(rsp);
|
|
|
|
sent_stats.lua(vm, "pktStats.sent");
|
|
recv_stats.lua(vm, "pktStats.recv");
|
|
|
|
if(dns) dns->lua(vm);
|
|
if(http) http->lua(vm);
|
|
if(hasAnomalies()) luaAnomalies(vm);
|
|
}
|
|
|
|
if(!returnHost)
|
|
host_id = get_hostkey(buf_id, sizeof(buf_id));
|
|
|
|
((GenericTrafficElement*)this)->lua(vm, host_details);
|
|
|
|
if(asListElement) {
|
|
lua_pushstring(vm, host_id);
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
}
|
|
}
|
|
|
|
/* ***************************************** */
|
|
|
|
/*
|
|
As this method can be called from Lua, in order to avoid concurrency issues
|
|
we need to lock/unlock
|
|
*/
|
|
void Host::setName(char *name) {
|
|
if(m) m->lock(__FILE__, __LINE__);
|
|
if((symbolic_name == NULL) || (symbolic_name && strcmp(symbolic_name, name))) {
|
|
if(symbolic_name) free(symbolic_name);
|
|
symbolic_name = strdup(name);
|
|
}
|
|
|
|
if(m) m->unlock(__FILE__, __LINE__);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
bool Host::hasAnomalies() {
|
|
time_t now = time(0);
|
|
|
|
return syn_flood_victim_alert->isAboveThreshold(now)
|
|
|| syn_flood_attacker_alert->isAboveThreshold(now)
|
|
|| flow_flood_victim_alert->isAboveThreshold(now)
|
|
|| flow_flood_attacker_alert->isAboveThreshold(now);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::luaAnomalies(lua_State* vm) {
|
|
if(!vm)
|
|
return;
|
|
|
|
if(hasAnomalies()) {
|
|
time_t now = time(0);
|
|
lua_newtable(vm);
|
|
|
|
if(syn_flood_victim_alert->isAboveThreshold(now))
|
|
syn_flood_victim_alert->lua(vm, "syn_flood_victim");
|
|
if(syn_flood_attacker_alert->isAboveThreshold(now))
|
|
syn_flood_attacker_alert->lua(vm, "syn_flood_attacker");
|
|
if(flow_flood_victim_alert->isAboveThreshold(now))
|
|
flow_flood_victim_alert->lua(vm, "flows_flood_victim");
|
|
if(flow_flood_attacker_alert->isAboveThreshold(now))
|
|
flow_flood_attacker_alert->lua(vm, "flows_flood_attacker");
|
|
|
|
lua_pushstring(vm, "anomalies");
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
}
|
|
}
|
|
|
|
/* ***************************************** */
|
|
|
|
void Host::refreshHTTPBL() {
|
|
if(ip.isIPv4()
|
|
&& (!localHost)
|
|
&& (trafficCategory[0] == '\0')
|
|
&& ntop->get_httpbl()) {
|
|
char buf[128] = { 0 };
|
|
char* ip_addr = ip.print(buf, sizeof(buf));
|
|
|
|
ntop->get_httpbl()->findCategory(ip_addr, trafficCategory, sizeof(trafficCategory), false);
|
|
}
|
|
}
|
|
|
|
/* ***************************************** */
|
|
|
|
char* Host::get_name(char *buf, u_int buf_len, bool force_resolution_if_not_found) {
|
|
char *addr, redis_buf[64];
|
|
int rc;
|
|
time_t now = time(NULL);
|
|
|
|
if(nextResolveAttempt
|
|
&& ((num_resolve_attempts > 1) || (nextResolveAttempt > now) || (nextResolveAttempt == (time_t)-1))) {
|
|
return(symbolic_name);
|
|
} else
|
|
nextResolveAttempt = ntop->getPrefs()->is_dns_resolution_enabled() ? now + MIN_HOST_RESOLUTION_FREQUENCY : (time_t)-1;
|
|
|
|
num_resolve_attempts++;
|
|
addr = ip.print(buf, buf_len);
|
|
|
|
if((symbolic_name != NULL) && strcmp(symbolic_name, addr))
|
|
return(symbolic_name);
|
|
|
|
if(readDHCPCache() && symbolic_name) return(symbolic_name);
|
|
|
|
rc = ntop->getRedis()->getAddress(addr, redis_buf, sizeof(redis_buf),
|
|
force_resolution_if_not_found);
|
|
|
|
if(rc == 0)
|
|
setName(redis_buf);
|
|
else
|
|
setName(addr);
|
|
|
|
return(symbolic_name);
|
|
}
|
|
|
|
/* ***************************************** */
|
|
|
|
bool Host::idle() {
|
|
if((num_uses > 0) || (!iface->is_purge_idle_interface()))
|
|
return(false);
|
|
|
|
switch(ntop->getPrefs()->get_host_stickiness()) {
|
|
case location_none:
|
|
break;
|
|
|
|
case location_local_only:
|
|
if(localHost || systemHost) return(false);
|
|
break;
|
|
|
|
case location_remote_only:
|
|
if(!(localHost||systemHost)) return(false);
|
|
break;
|
|
|
|
case location_all:
|
|
return(false);
|
|
break;
|
|
}
|
|
|
|
return(isIdle(ntop->getPrefs()->get_host_max_idle(localHost)));
|
|
};
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::incStats(u_int32_t when, u_int8_t l4_proto, u_int ndpi_proto,
|
|
struct site_categories *category,
|
|
u_int64_t sent_packets, u_int64_t sent_bytes, u_int64_t sent_goodput_bytes,
|
|
u_int64_t rcvd_packets, u_int64_t rcvd_bytes, u_int64_t rcvd_goodput_bytes) {
|
|
|
|
if(sent_packets || rcvd_packets) {
|
|
((GenericHost*)this)->incStats(when, l4_proto, ndpi_proto, sent_packets, sent_bytes, sent_goodput_bytes,
|
|
rcvd_packets, rcvd_bytes, rcvd_goodput_bytes);
|
|
|
|
/* Paket stats sent_stats and rcvd_stats are incremented in Flow::incStats */
|
|
|
|
switch(l4_proto) {
|
|
case 0:
|
|
/* Unknown protocol */
|
|
break;
|
|
case IPPROTO_UDP:
|
|
udp_rcvd.incStats(rcvd_packets, rcvd_bytes),
|
|
udp_sent.incStats(sent_packets, sent_bytes);
|
|
break;
|
|
case IPPROTO_TCP:
|
|
tcp_rcvd.incStats(rcvd_packets, rcvd_bytes),
|
|
tcp_sent.incStats(sent_packets, sent_bytes);
|
|
break;
|
|
case IPPROTO_ICMP:
|
|
icmp_rcvd.incStats(rcvd_packets, rcvd_bytes),
|
|
icmp_sent.incStats(sent_packets, sent_bytes);
|
|
break;
|
|
default:
|
|
other_ip_rcvd.incStats(rcvd_packets, rcvd_bytes),
|
|
other_ip_sent.incStats(sent_packets, sent_bytes);
|
|
break;
|
|
}
|
|
|
|
if(as) {
|
|
as->incStats(when, ndpi_proto, sent_packets, sent_bytes, rcvd_packets, rcvd_bytes);
|
|
}
|
|
|
|
if(mac) {
|
|
mac->incSentStats(sent_packets, sent_bytes);
|
|
mac->incRcvdStats(rcvd_packets, rcvd_bytes);
|
|
}
|
|
|
|
if(category && localHost && ntop->get_flashstart()) {
|
|
if(categoryStats == NULL)
|
|
categoryStats = new CategoryStats();
|
|
|
|
if(categoryStats) {
|
|
for(int i=0; i <MAX_NUM_CATEGORIES; i++)
|
|
if(category->categories[i] == NTOP_UNKNOWN_CATEGORY_ID)
|
|
break;
|
|
else
|
|
categoryStats->incStats(category->categories[i],
|
|
sent_bytes+rcvd_bytes);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
char* Host::serialize() {
|
|
json_object *my_object = getJSONObject();
|
|
char *rsp = strdup(json_object_to_json_string(my_object));
|
|
|
|
/* Free memory */
|
|
json_object_put(my_object);
|
|
|
|
return(rsp);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::serialize2redis() {
|
|
if((localHost || systemHost)
|
|
&& (ntop->getPrefs()->is_idle_local_host_cache_enabled()
|
|
|| ntop->getPrefs()->is_active_local_host_cache_enabled())
|
|
&& (!ip.isEmpty())) {
|
|
char *json = serialize();
|
|
char host_key[128], key[128];
|
|
char *k = ip.print(host_key, sizeof(host_key));
|
|
|
|
snprintf(key, sizeof(key), HOST_SERIALIZED_KEY, iface->get_id(), k, vlan_id);
|
|
ntop->getRedis()->set(key, json, ntop->getPrefs()->get_local_host_cache_duration());
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "Dumping serialization %s", k);
|
|
//ntop->getTrace()->traceEvent(TRACE_NORMAL, "%s => %s", k, json);
|
|
free(json);
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
json_object* Host::getJSONObject() {
|
|
json_object *my_object;
|
|
char buf[32];
|
|
|
|
if((my_object = json_object_new_object()) == NULL) return(NULL);
|
|
|
|
json_object_object_add(my_object, "mac_address", json_object_new_string(Utils::formatMac(mac ? mac->get_mac() : NULL, buf, sizeof(buf))));
|
|
json_object_object_add(my_object, "seen.first", json_object_new_int64(first_seen));
|
|
json_object_object_add(my_object, "seen.last", json_object_new_int64(last_seen));
|
|
json_object_object_add(my_object, "asn", json_object_new_int(asn));
|
|
if(symbolic_name) json_object_object_add(my_object, "symbolic_name", json_object_new_string(symbolic_name));
|
|
if(continent) json_object_object_add(my_object, "continent", json_object_new_string(continent));
|
|
if(country) json_object_object_add(my_object, "country", json_object_new_string(country));
|
|
if(city) json_object_object_add(my_object, "city", json_object_new_string(city));
|
|
if(asname) json_object_object_add(my_object, "asname", json_object_new_string(asname ? asname : (char*)""));
|
|
if(strlen(os)) json_object_object_add(my_object, "os", json_object_new_string(os));
|
|
if(trafficCategory[0] != '\0') json_object_object_add(my_object, "trafficCategory", json_object_new_string(trafficCategory));
|
|
if(vlan_id != 0) json_object_object_add(my_object, "vlan_id", json_object_new_int(vlan_id));
|
|
if(latitude) json_object_object_add(my_object, "latitude", json_object_new_double(latitude));
|
|
if(longitude) json_object_object_add(my_object, "longitude", json_object_new_double(longitude));
|
|
json_object_object_add(my_object, "ip", ip.getJSONObject());
|
|
json_object_object_add(my_object, "localHost", json_object_new_boolean(localHost));
|
|
json_object_object_add(my_object, "systemHost", json_object_new_boolean(systemHost));
|
|
json_object_object_add(my_object, "is_blacklisted", json_object_new_boolean(blacklisted_host));
|
|
json_object_object_add(my_object, "tcp_sent", tcp_sent.getJSONObject());
|
|
json_object_object_add(my_object, "tcp_rcvd", tcp_rcvd.getJSONObject());
|
|
json_object_object_add(my_object, "udp_sent", udp_sent.getJSONObject());
|
|
json_object_object_add(my_object, "udp_rcvd", udp_rcvd.getJSONObject());
|
|
json_object_object_add(my_object, "icmp_sent", icmp_sent.getJSONObject());
|
|
json_object_object_add(my_object, "icmp_rcvd", icmp_rcvd.getJSONObject());
|
|
json_object_object_add(my_object, "other_ip_sent", other_ip_sent.getJSONObject());
|
|
json_object_object_add(my_object, "other_ip_rcvd", other_ip_rcvd.getJSONObject());
|
|
|
|
/* packet stats */
|
|
json_object_object_add(my_object, "pktStats.sent", sent_stats.getJSONObject());
|
|
json_object_object_add(my_object, "pktStats.recv", recv_stats.getJSONObject());
|
|
|
|
/* TCP packet stats (serialize only anomalies) */
|
|
if(tcpPacketStats.pktRetr) json_object_object_add(my_object,
|
|
"tcpPacketStats.pktRetr",
|
|
json_object_new_int(tcpPacketStats.pktRetr));
|
|
if(tcpPacketStats.pktOOO) json_object_object_add(my_object,
|
|
"tcpPacketStats.pktOOO",
|
|
json_object_new_int(tcpPacketStats.pktOOO));
|
|
if(tcpPacketStats.pktLost) json_object_object_add(my_object,
|
|
"tcpPacketStats.pktLost",
|
|
json_object_new_int(tcpPacketStats.pktLost));
|
|
|
|
/* throughput stats */
|
|
json_object_object_add(my_object, "throughput_bps", json_object_new_double(bytes_thpt));
|
|
json_object_object_add(my_object, "throughput_trend_bps", json_object_new_string(Utils::trend2str(bytes_thpt_trend)));
|
|
json_object_object_add(my_object, "throughput_pps", json_object_new_double(pkts_thpt));
|
|
json_object_object_add(my_object, "throughput_trend_pps", json_object_new_string(Utils::trend2str(pkts_thpt_trend)));
|
|
json_object_object_add(my_object, "flows.as_client", json_object_new_int(total_num_flows_as_client));
|
|
json_object_object_add(my_object, "flows.as_server", json_object_new_int(total_num_flows_as_server));
|
|
if(user_activities)
|
|
json_object_object_add(my_object, "userActivities", user_activities->getJSONObject());
|
|
|
|
/* Generic Host */
|
|
json_object_object_add(my_object, "num_alerts", json_object_new_int(triggerAlerts() ? getNumAlerts() : 0));
|
|
json_object_object_add(my_object, "sent", sent.getJSONObject());
|
|
json_object_object_add(my_object, "rcvd", rcvd.getJSONObject());
|
|
json_object_object_add(my_object, "ndpiStats", ndpiStats->getJSONObject(iface));
|
|
json_object_object_add(my_object, "total_activity_time", json_object_new_int(total_activity_time));
|
|
|
|
/* The value below is handled by reading dumps on disk as otherwise the string will be too long */
|
|
//json_object_object_add(my_object, "activityStats", activityStats.getJSONObject());
|
|
|
|
if(categoryStats) json_object_object_add(my_object, "categories", categoryStats->getJSONObject());
|
|
if(dns) json_object_object_add(my_object, "dns", dns->getJSONObject());
|
|
if(http) json_object_object_add(my_object, "http", http->getJSONObject());
|
|
|
|
return(my_object);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
char* Host::get_visual_name(char *buf, u_int buf_len, bool from_info) {
|
|
bool mask_host = Utils::maskHost(localHost);
|
|
char buf2[64];
|
|
char ipbuf[64];
|
|
char *sym_name;
|
|
|
|
if(! mask_host) {
|
|
sym_name = from_info ? info : get_name(buf2, sizeof(buf2), false);
|
|
|
|
if(sym_name && sym_name[0]) {
|
|
if(ip.isIPv6() && strcmp(ip.print(ipbuf, sizeof(ipbuf)), sym_name)) {
|
|
snprintf(buf, buf_len, "%s [IPv6]", sym_name);
|
|
} else
|
|
strncpy(buf, sym_name, buf_len);
|
|
} else
|
|
buf[0] = '\0';
|
|
} else
|
|
buf[0] = '\0';
|
|
|
|
return buf;
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
bool Host::addIfMatching(lua_State* vm, AddressTree *ptree, char *key) {
|
|
char keybuf[64] = { 0 }, *keybuf_ptr;
|
|
char ipbuf[64] = { 0 }, *ipbuf_ptr;
|
|
|
|
if(!match(ptree)) return(false);
|
|
keybuf_ptr = get_hostkey(keybuf, sizeof(keybuf));
|
|
|
|
if(strcasestr((ipbuf_ptr = Utils::formatMac(mac ? mac->get_mac() : NULL, ipbuf, sizeof(ipbuf))), key) /* Match by MAC */
|
|
|| strcasestr((ipbuf_ptr = keybuf_ptr), key) /* Match by hostkey */
|
|
|| strcasestr((ipbuf_ptr = get_visual_name(ipbuf, sizeof(ipbuf))), key)) { /* Match by name */
|
|
lua_push_str_table_entry(vm, keybuf_ptr, ipbuf_ptr);
|
|
return(true);
|
|
}
|
|
|
|
return(false);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
bool Host::deserialize(char *json_str, char *key) {
|
|
json_object *o, *obj;
|
|
enum json_tokener_error jerr = json_tokener_success;
|
|
|
|
if((o = json_tokener_parse_verbose(json_str, &jerr)) == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "JSON Parse error [%s] key: %s: %s",
|
|
json_tokener_error_desc(jerr),
|
|
key,
|
|
json_str);
|
|
return(false);
|
|
}
|
|
|
|
if(json_object_object_get_ex(o, "seen.first", &obj)) first_seen = json_object_get_int64(obj);
|
|
if(json_object_object_get_ex(o, "seen.last", &obj)) last_seen = json_object_get_int64(obj);
|
|
|
|
if(json_object_object_get_ex(o, "mac_address", &obj)) set_mac((char*)json_object_get_string(obj));
|
|
if(json_object_object_get_ex(o, "source_id", &obj)) source_id = json_object_get_int(obj);
|
|
|
|
if(json_object_object_get_ex(o, "symbolic_name", &obj)) { if(symbolic_name) free(symbolic_name); symbolic_name = strdup(json_object_get_string(obj)); }
|
|
if(json_object_object_get_ex(o, "country", &obj)) { if(country) free(country); country = strdup(json_object_get_string(obj)); }
|
|
if(json_object_object_get_ex(o, "continent", &obj)) { if(continent) free(continent); continent = strdup(json_object_get_string(obj)); }
|
|
if(json_object_object_get_ex(o, "city", &obj)) { if(city) free(city); city = strdup(json_object_get_string(obj)); }
|
|
if(json_object_object_get_ex(o, "os", &obj)) { snprintf(os, sizeof(os), "%s", json_object_get_string(obj)); }
|
|
if(json_object_object_get_ex(o, "trafficCategory", &obj)){ snprintf(trafficCategory, sizeof(trafficCategory), "%s", json_object_get_string(obj)); }
|
|
if(json_object_object_get_ex(o, "latitude", &obj)) latitude = (float)json_object_get_double(obj);
|
|
if(json_object_object_get_ex(o, "longitude", &obj)) longitude = (float)json_object_get_double(obj);
|
|
if(json_object_object_get_ex(o, "ip", &obj)) { ip.deserialize(obj); }
|
|
if(json_object_object_get_ex(o, "localHost", &obj)) localHost = (json_object_get_boolean(obj) ? true : false);
|
|
if(json_object_object_get_ex(o, "systemHost", &obj)) systemHost = (json_object_get_boolean(obj) ? true : false);
|
|
if(json_object_object_get_ex(o, "tcp_sent", &obj)) tcp_sent.deserialize(obj);
|
|
if(json_object_object_get_ex(o, "tcp_rcvd", &obj)) tcp_rcvd.deserialize(obj);
|
|
if(json_object_object_get_ex(o, "udp_sent", &obj)) udp_sent.deserialize(obj);
|
|
if(json_object_object_get_ex(o, "udp_rcvd", &obj)) udp_rcvd.deserialize(obj);
|
|
if(json_object_object_get_ex(o, "icmp_sent", &obj)) icmp_sent.deserialize(obj);
|
|
if(json_object_object_get_ex(o, "icmp_rcvd", &obj)) icmp_rcvd.deserialize(obj);
|
|
if(json_object_object_get_ex(o, "other_ip_sent", &obj)) other_ip_sent.deserialize(obj);
|
|
if(json_object_object_get_ex(o, "other_ip_rcvd", &obj)) other_ip_rcvd.deserialize(obj);
|
|
|
|
/* packet stats */
|
|
if(json_object_object_get_ex(o, "pktStats.sent", &obj)) sent_stats.deserialize(obj);
|
|
if(json_object_object_get_ex(o, "pktStats.recv", &obj)) recv_stats.deserialize(obj);
|
|
|
|
/* TCP packet stats */
|
|
if(json_object_object_get_ex(o, "tcpPacketStats.pktRetr", &obj)) tcpPacketStats.pktRetr = json_object_get_int(obj);
|
|
if(json_object_object_get_ex(o, "tcpPacketStats.pktOOO", &obj)) tcpPacketStats.pktOOO = json_object_get_int(obj);
|
|
if(json_object_object_get_ex(o, "tcpPacketStats.pktLost", &obj)) tcpPacketStats.pktLost = json_object_get_int(obj);
|
|
|
|
if(json_object_object_get_ex(o, "flows.as_client", &obj)) total_num_flows_as_client = json_object_get_int(obj);
|
|
if(json_object_object_get_ex(o, "flows.as_server", &obj)) total_num_flows_as_server = json_object_get_int(obj);
|
|
if(user_activities) if(json_object_object_get_ex(o, "userActivities", &obj)) user_activities->deserialize(obj);
|
|
|
|
if(json_object_object_get_ex(o, "is_blacklisted", &obj)) blacklisted_host = json_object_get_boolean(obj);
|
|
|
|
if(json_object_object_get_ex(o, "sent", &obj)) sent.deserialize(obj);
|
|
if(json_object_object_get_ex(o, "rcvd", &obj)) rcvd.deserialize(obj);
|
|
|
|
if(json_object_object_get_ex(o, "total_activity_time", &obj)) total_activity_time = json_object_get_int(obj);
|
|
|
|
if(json_object_object_get_ex(o, "dns", &obj)) {
|
|
if(dns) dns->deserialize(obj);
|
|
}
|
|
|
|
if(json_object_object_get_ex(o, "http", &obj)) {
|
|
if(http) http->deserialize(obj);
|
|
}
|
|
|
|
if(categoryStats) {
|
|
delete categoryStats;
|
|
categoryStats = NULL;
|
|
}
|
|
|
|
// deserialize categories only if flashstart is enabled for the current instance
|
|
if(json_object_object_get_ex(o, "categories", &obj) && ntop->get_flashstart()) {
|
|
categoryStats = new CategoryStats();
|
|
if(categoryStats) categoryStats->deserialize(obj);
|
|
}
|
|
|
|
if(ndpiStats) {
|
|
delete ndpiStats;
|
|
ndpiStats = NULL;
|
|
}
|
|
|
|
if(json_object_object_get_ex(o, "ndpiStats", &obj)) {
|
|
ndpiStats = new nDPIStats();
|
|
ndpiStats->deserialize(iface, obj);
|
|
}
|
|
|
|
/* We commented the line below to avoid strings too long */
|
|
#if 0
|
|
activityStats.reset();
|
|
if(json_object_object_get_ex(o, "activityStats", &obj)) activityStats.deserialize(obj);
|
|
#endif
|
|
|
|
computeHostSerial();
|
|
if(json_object_object_get_ex(o, "pktStats.sent", &obj)) sent_stats.deserialize(obj);
|
|
if(json_object_object_get_ex(o, "pktStats.recv", &obj)) recv_stats.deserialize(obj);
|
|
|
|
json_object_put(o);
|
|
|
|
/* We need to update too the stats for traffic */
|
|
last_update_time.tv_sec = (long)time(NULL), last_update_time.tv_usec = 0;
|
|
// Update bps throughput
|
|
bytes_thpt = 0, last_bytes = sent.getNumBytes()+rcvd.getNumBytes(),
|
|
bytes_thpt_trend = trend_unknown;
|
|
|
|
// Update pps throughput
|
|
pkts_thpt = 0, last_packets = sent.getNumPkts()+rcvd.getNumPkts(),
|
|
pkts_thpt_trend = trend_unknown;
|
|
|
|
return(true);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::updateSynFlags(time_t when, u_int8_t flags, Flow *f, bool syn_sent) {
|
|
AlertCounter *counter = syn_sent ? syn_flood_attacker_alert : syn_flood_victim_alert;
|
|
|
|
if(localHost && triggerAlerts())
|
|
counter->incHits(when);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::incNumFlows(bool as_client) {
|
|
AlertCounter *counter;
|
|
|
|
if(as_client) {
|
|
total_num_flows_as_client++, num_active_flows_as_client++;
|
|
counter = flow_flood_attacker_alert;
|
|
} else {
|
|
total_num_flows_as_server++, num_active_flows_as_server++;
|
|
counter = flow_flood_victim_alert;
|
|
}
|
|
|
|
if(localHost && triggerAlerts())
|
|
counter->incHits(time(0));
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::decNumFlows(bool as_client) {
|
|
if(as_client) {
|
|
if(num_active_flows_as_client)
|
|
num_active_flows_as_client--;
|
|
else
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Internal error: invalid counter value");
|
|
} else {
|
|
if(num_active_flows_as_server)
|
|
num_active_flows_as_server--;
|
|
else
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Internal error: invalid counter value");
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
#ifdef NTOPNG_PRO
|
|
|
|
u_int8_t Host::get_shaper_id(ndpi_protocol ndpiProtocol, bool isIngress) {
|
|
u_int8_t ret = DEFAULT_SHAPER_ID;
|
|
ShaperDirection_t *sd = NULL;
|
|
L7Policy_t *policy = l7Policy; /*
|
|
Cache value so that even if updateHostL7Policy()
|
|
runs in the meantime, we're consistent with the policer
|
|
*/
|
|
|
|
if(policy) {
|
|
int protocol = ndpiProtocol.app_protocol;
|
|
|
|
HASH_FIND_INT(policy->mapping_proto_shaper_id, &protocol, sd);
|
|
if(!sd) {
|
|
protocol = ndpiProtocol.master_protocol;
|
|
HASH_FIND_INT(policy->mapping_proto_shaper_id, &protocol, sd);
|
|
}
|
|
|
|
ret = isIngress ? policy->default_shapers.ingress : policy->default_shapers.egress;
|
|
|
|
if(sd) {
|
|
/* A protocol shaper has priority over the category shaper */
|
|
if(sd->protocol_shapers.enabled)
|
|
ret = isIngress ? sd->protocol_shapers.ingress : sd->protocol_shapers.egress;
|
|
else if(sd->category_shapers.enabled)
|
|
ret = isIngress ? sd->category_shapers.ingress : sd->category_shapers.egress;
|
|
}
|
|
}
|
|
|
|
#ifdef SHAPER_DEBUG
|
|
{
|
|
char buf[64], buf1[64];
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "[%s] [%s@%u][ndpiProtocol=%d/%s] => [policer=%p][shaper_id=%d]%s",
|
|
isIngress ? "INGRESS" : "EGRESS",
|
|
ip.print(buf, sizeof(buf)), vlan_id,
|
|
ndpiProtocol.app_protocol,
|
|
ndpi_protocol2name(iface->get_ndpi_struct(), ndpiProtocol, buf1, sizeof(buf1)),
|
|
policy ? policy : NULL, ret, sd ? "" : " [DEFAULT]");
|
|
}
|
|
#endif
|
|
|
|
/* Update blocking status */
|
|
if(!has_blocking_shaper && getInterface()->getL7Policer()) {
|
|
TrafficShaper *shaper = getInterface()->getL7Policer()->getShaper(ret);
|
|
if(shaper->shaping_enabled() && (shaper->get_max_rate_kbit_sec() == 0))
|
|
has_blocking_shaper = true;
|
|
}
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::get_quota(u_int16_t protocol, u_int64_t *bytes_quota, u_int32_t *secs_quota, bool *is_category) {
|
|
L7Policy_t *policy = l7Policy; /*
|
|
Cache value so that even if updateHostL7Policy()
|
|
runs in the meantime, we're consistent with the policer
|
|
*/
|
|
ShaperDirection_t *sd = NULL;
|
|
u_int64_t bytes = 0; /* Default: no quota */
|
|
u_int32_t secs = 0; /* Default: no quota */
|
|
bool category = false; /* Default: no category */
|
|
int protocol32 = (int)protocol; /* uthash macro HASH_FIND_INT requires an int */
|
|
|
|
if(policy) {
|
|
HASH_FIND_INT(policy->mapping_proto_shaper_id, &protocol32, sd);
|
|
|
|
if(sd) {
|
|
/* A protocol quota has priority over the category quota */
|
|
if(sd->protocol_shapers.enabled) {
|
|
bytes = sd->protocol_shapers.bytes_quota;
|
|
secs = sd->protocol_shapers.secs_quota;
|
|
category = false;
|
|
} else if(sd->category_shapers.enabled) {
|
|
bytes = sd->category_shapers.bytes_quota;
|
|
secs = sd->category_shapers.secs_quota;
|
|
category = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
*bytes_quota = bytes;
|
|
*secs_quota = secs;
|
|
*is_category = category;
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
bool Host::checkQuota(u_int16_t protocol, bool *is_category) {
|
|
u_int64_t bytes_quota, bytes;
|
|
u_int32_t secs_quota, secs;
|
|
ndpi_protocol_category_t category;
|
|
HostPools *pools = getInterface()->getHostPools();
|
|
bool is_above = false;
|
|
|
|
if(!pools || get_host_pool() == NO_HOST_POOL_ID) /* Enforce quotas only for custom pools */
|
|
return false;
|
|
|
|
get_quota(protocol, &bytes_quota, &secs_quota, is_category);
|
|
|
|
if((bytes_quota > 0) || (secs_quota > 0)) {
|
|
category = getInterface()->get_ndpi_proto_category(protocol);
|
|
|
|
if(!pools->enforceQuotasPerPoolMember(get_host_pool())) {
|
|
|
|
if((*is_category && pools->getCategoryStats(get_host_pool(), category, &bytes, &secs))
|
|
|| (!*is_category && pools->getProtoStats(get_host_pool(), protocol, &bytes, &secs))) {
|
|
if(((bytes_quota > 0) && (bytes >= bytes_quota))
|
|
|| ((secs_quota > 0) && (secs >= secs_quota)))
|
|
is_above = true;
|
|
}
|
|
|
|
} else if(quota_enforcement_stats) { /* Per pool member quota enforcement */
|
|
|
|
if(*is_category)
|
|
quota_enforcement_stats->getCategoryStats(category, &bytes, &secs);
|
|
else
|
|
quota_enforcement_stats->getProtoStats(protocol, &bytes, &secs);
|
|
|
|
if(((bytes_quota > 0) && (bytes >= bytes_quota))
|
|
|| ((secs_quota > 0) && (secs >= secs_quota)))
|
|
is_above = true;
|
|
|
|
}
|
|
|
|
#ifdef SHAPER_DEBUG
|
|
char buf[128];
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "[QUOTA (%s)] [%s@%u] [bytes: %ld/%lu][seconds: %d/%u] => %s %s",
|
|
ndpi_get_proto_name(iface->get_ndpi_struct(), protocol),
|
|
ip.print(buf, sizeof(buf)), vlan_id,
|
|
bytes, bytes_quota,
|
|
secs, secs_quota,
|
|
is_above ? (char*)"EXCEEDED" : (char*)"ok",
|
|
quota_enforcement_stats ? "[QUOTAS enforced per pool member]" : "");
|
|
#endif
|
|
}
|
|
|
|
has_blocking_quota |= is_above;
|
|
return is_above;
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::luaUsedQuotas(lua_State* vm) {
|
|
if(quota_enforcement_stats)
|
|
quota_enforcement_stats->lua(vm, iface);
|
|
else
|
|
lua_newtable(vm);
|
|
}
|
|
#endif
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::updateStats(struct timeval *tv) {
|
|
((GenericHost*)this)->updateStats(tv);
|
|
if(http) http->updateStats(tv);
|
|
|
|
if(!localHost) return;
|
|
|
|
if(top_sites && ntop->getPrefs()->are_top_talkers_enabled() && (tv->tv_sec >= nextSitesUpdate)) {
|
|
if(nextSitesUpdate > 0) {
|
|
if(old_sites)
|
|
free(old_sites);
|
|
old_sites = top_sites->json();
|
|
}
|
|
|
|
nextSitesUpdate = tv->tv_sec + HOST_SITES_REFRESH;
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
u_int32_t Host::getNumAlerts(bool from_alertsmanager) {
|
|
if(!from_alertsmanager)
|
|
return(num_alerts_detected);
|
|
|
|
num_alerts_detected = iface->getAlertsManager()->getNumHostAlerts(this, true);
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG,
|
|
"Refreshing alerts from alertsmanager [num: %i]",
|
|
num_alerts_detected);
|
|
|
|
return(num_alerts_detected);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::postHashAdd() {
|
|
loadAlertsCounter();
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::loadAlertsCounter() {
|
|
char buf[64], counters_key[64];
|
|
char rsp[16];
|
|
char *key = get_hostkey(buf, sizeof(buf), true /* force vlan */);
|
|
|
|
if(ntop->getPrefs()->are_alerts_disabled() || !isLocalHost()) {
|
|
num_alerts_detected = 0;
|
|
return;
|
|
}
|
|
|
|
snprintf(counters_key, sizeof(counters_key), CONST_HOSTS_ALERT_COUNTERS, iface->get_id());
|
|
|
|
if (ntop->getRedis()->hashGet(counters_key, key, rsp, sizeof(rsp)) == 0)
|
|
num_alerts_detected = atoi(rsp);
|
|
else
|
|
num_alerts_detected = 0;
|
|
|
|
#if 0
|
|
printf("%s: num_alerts_detected = %d\n", key, num_alerts_detected);
|
|
#endif
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::resetPeriodicStats() {
|
|
((GenericHost*)this)->resetPeriodicStats();
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::updateHTTPHostRequest(char *virtual_host_name, u_int32_t num_req,
|
|
u_int32_t bytes_sent, u_int32_t bytes_rcvd) {
|
|
if(http)
|
|
http->updateHTTPHostRequest(virtual_host_name, num_req, bytes_sent, bytes_rcvd);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::setDumpTrafficPolicy(bool new_policy) {
|
|
char buf[64], *host;
|
|
|
|
if(dump_host_traffic == new_policy)
|
|
return; /* Nothing to do */
|
|
else
|
|
dump_host_traffic = new_policy;
|
|
|
|
host = get_hostkey(buf, sizeof(buf), true);
|
|
|
|
ntop->getRedis()->hashSet((char*)DUMP_HOST_TRAFFIC, host,
|
|
(char*)(dump_host_traffic ? "true" : "false"));
|
|
};
|
|
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::refreshHostAlertPrefs() {
|
|
bool alerts_read = false;
|
|
|
|
if(!ntop->getPrefs()->are_alerts_disabled()
|
|
&& (localHost || systemHost)
|
|
&& (!ip.isEmpty())) {
|
|
char *key, ip_buf[48], rsp[64], rkey[128];
|
|
|
|
/* This value always contains vlan information */
|
|
key = get_hostkey(ip_buf, sizeof(ip_buf), true);
|
|
|
|
if(key) {
|
|
snprintf(rkey, sizeof(rkey), CONST_SUPPRESSED_ALERT_PREFS, getInterface()->get_id());
|
|
if(ntop->getRedis()->hashGet(rkey, key, rsp, sizeof(rsp)) == 0)
|
|
trigger_host_alerts = ((strcmp(rsp, "false") == 0) ? 0 : 1);
|
|
else
|
|
trigger_host_alerts = true;
|
|
|
|
alerts_read = true;
|
|
|
|
if(trigger_host_alerts) {
|
|
/* Defaults */
|
|
int flow_attacker_pref = ntop->getPrefs()->get_attacker_max_num_flows_per_sec();
|
|
int flow_victim_pref = ntop->getPrefs()->get_victim_max_num_flows_per_sec();
|
|
int syn_attacker_pref = ntop->getPrefs()->get_attacker_max_num_syn_per_sec();
|
|
int syn_victim_pref = ntop->getPrefs()->get_victim_max_num_syn_per_sec();
|
|
|
|
key = ip.print(ip_buf, sizeof(ip_buf));
|
|
snprintf(rkey, sizeof(rkey), CONST_HOST_ANOMALIES_THRESHOLD, key, vlan_id);
|
|
|
|
/* per-host values */
|
|
if((ntop->getRedis()->get(rkey, rsp, sizeof(rsp)) == 0) && (rsp[0] != '\0'))
|
|
/* Note: the order of the fields must match that of anomalies_config into alerts_utils.lua */
|
|
sscanf(rsp, "%i|%i|%i|%i", &flow_attacker_pref, &flow_victim_pref, &syn_attacker_pref, &syn_victim_pref);
|
|
|
|
/* Counter reload logic */
|
|
if((u_int32_t)flow_attacker_pref != attacker_max_num_flows_per_sec) {
|
|
attacker_max_num_flows_per_sec = flow_attacker_pref;
|
|
flow_flood_attacker_alert->resetThresholds(attacker_max_num_flows_per_sec, CONST_MAX_THRESHOLD_CROSS_DURATION);
|
|
#if 0
|
|
printf("%s: attacker_max_num_flows_per_sec = %d\n", key, attacker_max_num_flows_per_sec);
|
|
#endif
|
|
}
|
|
|
|
if((u_int32_t)flow_victim_pref != victim_max_num_flows_per_sec) {
|
|
victim_max_num_flows_per_sec = flow_victim_pref;
|
|
flow_flood_victim_alert->resetThresholds(victim_max_num_flows_per_sec, CONST_MAX_THRESHOLD_CROSS_DURATION);
|
|
#if 0
|
|
printf("%s: victim_max_num_flows_per_sec = %d\n", key, victim_max_num_flows_per_sec);
|
|
#endif
|
|
}
|
|
|
|
if((u_int32_t)syn_attacker_pref != attacker_max_num_syn_per_sec) {
|
|
attacker_max_num_syn_per_sec = syn_attacker_pref;
|
|
syn_flood_attacker_alert->resetThresholds(attacker_max_num_syn_per_sec, CONST_MAX_THRESHOLD_CROSS_DURATION);
|
|
|
|
#if 0
|
|
printf("%s: attacker_max_num_syn_per_sec = %d\n", key, attacker_max_num_syn_per_sec);
|
|
#endif
|
|
}
|
|
|
|
if((u_int32_t)syn_victim_pref != victim_max_num_syn_per_sec) {
|
|
victim_max_num_syn_per_sec = syn_victim_pref;
|
|
syn_flood_victim_alert->resetThresholds(victim_max_num_syn_per_sec, CONST_MAX_THRESHOLD_CROSS_DURATION);
|
|
|
|
#if 0
|
|
printf("%s: victim_max_num_syn_per_sec = %d\n", key, victim_max_num_syn_per_sec);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!alerts_read)
|
|
trigger_host_alerts = false;
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::incHitter(Host *peer, u_int64_t sent_bytes, u_int64_t rcvd_bytes) {
|
|
#ifdef NOTUSED // check for memory corruptions here!
|
|
#ifdef NTOPNG_PRO
|
|
if(sent_bytes) sent_to_sketch->update(peer->key(), sent_bytes);
|
|
if(rcvd_bytes) rcvd_from_sketch->update(peer->key(), rcvd_bytes);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::getPeerBytes(lua_State* vm, u_int32_t peer_key) {
|
|
#ifdef NOTUSED
|
|
lua_newtable(vm);
|
|
|
|
#ifdef NTOPNG_PRO
|
|
if(sent_to_sketch && rcvd_from_sketch) {
|
|
lua_push_int_table_entry(vm, "sent", sent_to_sketch->estimate(peer_key));
|
|
lua_push_int_table_entry(vm, "rcvd", rcvd_from_sketch->estimate(peer_key));
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
lua_push_int_table_entry(vm, "sent", 0);
|
|
lua_push_int_table_entry(vm, "rcvd", 0);
|
|
#endif
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::incLowGoodputFlows(bool asClient) {
|
|
bool alert = false;
|
|
|
|
if(asClient) {
|
|
if(++low_goodput_client_flows > HOST_LOW_GOODPUT_THRESHOLD) alert = true;
|
|
} else {
|
|
if(++low_goodput_server_flows > HOST_LOW_GOODPUT_THRESHOLD) alert = true;
|
|
}
|
|
|
|
/* TODO: decide if an alert should be sent in a future version */
|
|
if(alert && (!good_low_flow_detected)) {
|
|
#if 0
|
|
char alert_msg[1024], *c, c_buf[64];
|
|
|
|
c = get_ip()->print(c_buf, sizeof(c_buf));
|
|
|
|
snprintf(alert_msg, sizeof(alert_msg),
|
|
"Host <A HREF='%s/lua/host_details.lua?host=%s&ifid=%s'>%s</A> has %d low goodput active %s flows",
|
|
ntop->getPrefs()->get_http_prefix(),
|
|
c, iface->get_id(), get_name() ? get_name() : c,
|
|
HOST_LOW_GOODPUT_THRESHOLD, asClient ? "client" : "server");
|
|
|
|
// iface->getAlertsManager()->engageHostAlert(this,
|
|
// asClient ? (char*)"low_goodput_victim", (char*)"low_goodput_attacker",
|
|
// asClient ? alert_host_under_attack : alert_host_attacker,
|
|
// alert_level_error, msg);
|
|
#endif
|
|
good_low_flow_detected = true;
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::decLowGoodputFlows(bool asClient) {
|
|
bool alert = false;
|
|
|
|
if(asClient) {
|
|
if(--low_goodput_client_flows < HOST_LOW_GOODPUT_THRESHOLD) alert = true;
|
|
} else {
|
|
if(--low_goodput_server_flows < HOST_LOW_GOODPUT_THRESHOLD) alert = true;
|
|
}
|
|
|
|
if(alert && good_low_flow_detected) {
|
|
/* TODO: send end of alert
|
|
iface->getAlertsManager()->releaseHostAlert(this,
|
|
asClient ? (char*)"low_goodput_victim", (char*)"low_goodput_attacker",
|
|
asClient ? alert_host_under_attack : alert_host_attacker,
|
|
alert_level_error, msg);
|
|
*/
|
|
good_low_flow_detected = false;
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::incrVisitedWebSite(char *hostname) {
|
|
u_int ip4_0 = 0, ip4_1 = 0, ip4_2 = 0, ip4_3 = 0;
|
|
char *firstdot = NULL, *nextdot = NULL;
|
|
|
|
if(top_sites
|
|
&& ntop->getPrefs()->are_top_talkers_enabled()
|
|
&& (strstr(hostname, "in-addr.arpa") == NULL)
|
|
&& (sscanf(hostname, "%u.%u.%u.%u", &ip4_0, &ip4_1, &ip4_2, &ip4_3) != 4)
|
|
) {
|
|
|
|
firstdot = strchr(hostname, '.');
|
|
|
|
if(firstdot)
|
|
nextdot = strchr(&firstdot[1], '.');
|
|
|
|
top_sites->add(nextdot ? &firstdot[1] : hostname, 1);
|
|
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::incActivityBytes(UserActivityID id, u_int64_t upbytes, u_int64_t downbytes, u_int64_t bgbytes) {
|
|
if(user_activities) user_activities->incBytes(id, upbytes, downbytes, bgbytes);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
const UserActivityCounter* Host::getActivityBytes(UserActivityID id) {
|
|
return(user_activities ? user_activities->getBytes(id) : NULL);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::incIfaPackets(InterFlowActivityProtos proto, const Flow * flow, time_t when) {
|
|
if(!ifa_stats)
|
|
return;
|
|
else {
|
|
int k = -1;
|
|
float worst = 0.f;
|
|
int i, idx;
|
|
uint tbase = proto*INTER_FLOW_ACTIVITY_SLOTS;
|
|
|
|
for (i=0; (i < INTER_FLOW_ACTIVITY_SLOTS)
|
|
&& (ifa_stats[tbase+i].flow != flow); i++) {
|
|
float bad;
|
|
|
|
idx = tbase+i;
|
|
if(ifa_stats[idx].flow == NULL)
|
|
// empty slot
|
|
bad = 1.f;
|
|
else
|
|
// old value: estimate goodness
|
|
bad = (when - ifa_stats[idx].last) * 1.f / INTER_FLOW_ACTIVITY_MAX_INTERVAL - ifa_stats[idx].pkts / 100.f;
|
|
|
|
if(bad > worst) {
|
|
k = i;
|
|
worst = bad;
|
|
}
|
|
}
|
|
|
|
if(i < INTER_FLOW_ACTIVITY_SLOTS) {
|
|
idx = tbase+i;
|
|
|
|
if((when - ifa_stats[idx].last) <= INTER_FLOW_ACTIVITY_MAX_INTERVAL) {
|
|
// update slot
|
|
ifa_stats[idx].pkts += 1;
|
|
ifa_stats[idx].last = when;
|
|
k = -1;
|
|
} else {
|
|
// reset slot counters
|
|
k = i;
|
|
}
|
|
}
|
|
|
|
if(k != -1) {
|
|
u_int idx = tbase+k;
|
|
// allocate or reset slot
|
|
ifa_stats[idx].flow = flow, ifa_stats[idx].pkts = 1,
|
|
ifa_stats[idx].first = when, ifa_stats[idx].last = when;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::getIfaStats(InterFlowActivityProtos proto, time_t when,
|
|
int * count, u_int32_t * packets, time_t * max_diff) {
|
|
*count = 0, *max_diff = 0, *packets = 0;
|
|
|
|
if(ifa_stats) {
|
|
uint tbase = proto*INTER_FLOW_ACTIVITY_SLOTS;
|
|
|
|
for(int i=0; i < INTER_FLOW_ACTIVITY_SLOTS; i++) {
|
|
int idx = tbase+i;
|
|
bool timeok = (when - ifa_stats[idx].last) <= INTER_FLOW_ACTIVITY_MAX_INTERVAL;
|
|
bool continuity = (when - ifa_stats[idx].last) <= INTER_FLOW_ACTIVITY_MAX_CONTINUITY_INTERVAL;
|
|
|
|
if(continuity || timeok) {
|
|
if(timeok) {
|
|
*count += 1;
|
|
*max_diff = max(ifa_stats[idx].last - ifa_stats[idx].first, *max_diff);
|
|
}
|
|
|
|
// this is affected by activity continuity
|
|
*packets += ifa_stats[idx].pkts;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
/* Splits a string in the format hostip@vlanid: *buf=hostip, *vlan_id=vlanid */
|
|
void Host::splitHostVlan(const char *at_sign_str, char*buf, int bufsize, u_int16_t *vlan_id) {
|
|
int size;
|
|
const char *vlan_ptr = strchr(at_sign_str, '@');
|
|
|
|
if(vlan_ptr == NULL) {
|
|
vlan_ptr = at_sign_str + strlen(at_sign_str);
|
|
*vlan_id = 0;
|
|
} else {
|
|
*vlan_id = atoi(vlan_ptr + 1);
|
|
}
|
|
|
|
size = min(bufsize, (int)(vlan_ptr - at_sign_str + 1));
|
|
strncpy(buf, at_sign_str, size);
|
|
buf[size-1] = '\0';
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::setMDSNInfo(char *str) {
|
|
const char *tokens[] = {
|
|
"._http._tcp.local",
|
|
"._sftp-ssh._tcp.local",
|
|
"._smb._tcp.local",
|
|
"._device-info._tcp.local",
|
|
"._privet._tcp.local",
|
|
"._afpovertcp._tcp.local",
|
|
NULL
|
|
};
|
|
|
|
if(strstr(str, ".ip6.arpa")) return; /* Ignored for the time being */
|
|
|
|
for(int i=0; tokens[i] != NULL; i++) {
|
|
if(strstr(str, tokens[i])) {
|
|
str[strlen(str)-strlen(tokens[i])] = '\0';
|
|
setInfo(str);
|
|
|
|
for(i=0; info[i] != '\0'; i++) {
|
|
if(!isascii(info[i]))
|
|
info[i] = ' ';
|
|
}
|
|
|
|
set_host_label(info, true);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
bool Host::IsAllowedTrafficCategory(struct site_categories *category) {
|
|
#ifdef NTOPNG_PRO
|
|
if(!ntop->get_flashstart())
|
|
return(true);
|
|
|
|
L7Policy_t *policy = l7Policy; /*
|
|
Cache value so that even if updateHostL7Policy()
|
|
runs in the meantime, we're consistent with the policer
|
|
*/
|
|
|
|
if(policy) {
|
|
for(int i=0; i<MAX_NUM_CATEGORIES; i++) {
|
|
if(category->categories[i] == 0) break;
|
|
|
|
u_int8_t cat_id = category->categories[i];
|
|
|
|
if((cat_id < MAX_NUM_MAPPED_CATEGORIES) && /* Check if category id is valid */
|
|
(policy->blocked_categories[cat_id])) /* Check if the category id is blocked */
|
|
return(false);
|
|
}
|
|
}
|
|
|
|
return(true);
|
|
#else
|
|
return(true);
|
|
#endif
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::incICMP(u_int8_t icmp_type, u_int8_t icmp_code, bool sent, Host *peer) {
|
|
if(localHost) {
|
|
if(!icmp) icmp = new ICMPstats();
|
|
if(icmp) icmp->incStats(icmp_type, icmp_code, sent, peer);
|
|
}
|
|
}
|