mirror of
https://github.com/ntop/ntopng.git
synced 2026-05-06 12:15:15 +00:00
1482 lines
45 KiB
C++
1482 lines
45 KiB
C++
/*
|
|
*
|
|
* (C) 2013-21 - 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, char *ipAddress, u_int16_t _vlanId) : GenericHashEntry(_iface),
|
|
AlertableEntity(_iface, alert_entity_host) {
|
|
ip.set(ipAddress);
|
|
initialize(NULL, _vlanId, true);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
Host::Host(NetworkInterface *_iface, Mac *_mac,
|
|
u_int16_t _vlanId, IpAddress *_ip) : GenericHashEntry(_iface), AlertableEntity(_iface, alert_entity_host) {
|
|
ip.set(_ip);
|
|
|
|
#ifdef BROADCAST_DEBUG
|
|
char buf[32];
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Setting %s [broadcast: %u]",
|
|
ip.print(buf, sizeof(buf)), ip.isBroadcastAddress() ? 1 : 0);
|
|
#endif
|
|
|
|
initialize(_mac, _vlanId, true);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
Host::~Host() {
|
|
if((getUses() > 0)
|
|
/* View hosts are not in sync with viewed flows so during shutdown it can be normal */
|
|
&& (!iface->isView() || !ntop->getGlobals()->isShutdownRequested()))
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Internal error: num_uses=%u", getUses());
|
|
|
|
// ntop->getTrace()->traceEvent(TRACE_NORMAL, "Deleting %s (%s)", k, localHost ? "local": "remote");
|
|
|
|
if(mac) mac->decUses();
|
|
if(as) as->decUses();
|
|
if(country) country->decUses();
|
|
if(vlan) vlan->decUses();
|
|
|
|
#ifdef NTOPNG_PRO
|
|
if(host_traffic_shapers) {
|
|
for(int i = 0; i < NUM_TRAFFIC_SHAPERS; i++) {
|
|
if(host_traffic_shapers[i])
|
|
delete host_traffic_shapers[i];
|
|
}
|
|
|
|
free(host_traffic_shapers);
|
|
}
|
|
|
|
#endif
|
|
|
|
freeHostNames();
|
|
|
|
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(stats) delete stats;
|
|
if(stats_shadow) delete stats_shadow;
|
|
|
|
/*
|
|
Pool counters are updated both in and outside the datapath.
|
|
So decPoolNumHosts must stay in the destructor to preserve counters
|
|
consistency (no thread outside the datapath will change the last pool id)
|
|
*/
|
|
iface->decPoolNumHosts(get_host_pool(), false /* Host is deleted offline */);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::updateSynAlertsCounter(time_t when, bool syn_sent) {
|
|
AlertCounter *counter = syn_sent ? syn_flood_attacker_alert : syn_flood_victim_alert;
|
|
|
|
counter->inc(when, this);
|
|
|
|
if(syn_sent)
|
|
syn_sent_last_min++;
|
|
else
|
|
syn_recvd_last_min++;
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::updateSynAckAlertsCounter(time_t when, bool synack_sent) {
|
|
if(synack_sent)
|
|
synack_sent_last_min++;
|
|
else
|
|
synack_recvd_last_min++;
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::housekeepAlerts(ScriptPeriodicity p) {
|
|
switch(p) {
|
|
case minute_script:
|
|
flow_flood_attacker_alert->reset_hits(),
|
|
flow_flood_victim_alert->reset_hits(),
|
|
syn_flood_attacker_alert->reset_hits(),
|
|
syn_flood_victim_alert->reset_hits();
|
|
syn_sent_last_min = synack_recvd_last_min = 0;
|
|
syn_recvd_last_min = synack_sent_last_min = 0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::initialize(Mac *_mac, u_int16_t _vlanId, bool init_all) {
|
|
char buf[64];
|
|
|
|
stats = NULL; /* it will be instantiated by specialized classes */
|
|
stats_shadow = NULL;
|
|
data_delete_requested = false, stats_reset_requested = false, name_reset_requested = false;
|
|
last_stats_reset = ntop->getLastStatsReset(); /* assume fresh stats, may be changed by deserialize */
|
|
os = os_unknown;
|
|
prefs_loaded = false;
|
|
host_services_bitmap = 0;
|
|
mud_pref = mud_recording_default;
|
|
|
|
// readStats(); - Commented as if put here it's too early and the key is not yet set
|
|
|
|
#ifdef NTOPNG_PRO
|
|
host_traffic_shapers = NULL;
|
|
has_blocking_quota = has_blocking_shaper = false;
|
|
#endif
|
|
|
|
if((mac = _mac))
|
|
mac->incUses();
|
|
|
|
if((vlan = iface->getVlan(_vlanId, true, true /* Inline call */)) != NULL)
|
|
vlan->incUses();
|
|
|
|
num_resolve_attempts = 0, ssdpLocation = NULL;
|
|
num_active_flows_as_client = num_active_flows_as_server = 0;
|
|
active_alerted_flows = 0;
|
|
|
|
nextResolveAttempt = 0;
|
|
vlan_id = _vlanId % MAX_NUM_VLAN,
|
|
memset(&names, 0, sizeof(names));
|
|
asn = 0, asname = NULL;
|
|
as = NULL, country = NULL;
|
|
reloadHostBlacklist();
|
|
is_dhcp_host = false;
|
|
is_in_broadcast_domain = false;
|
|
|
|
PROFILING_SUB_SECTION_ENTER(iface, "Host::initialize: new AlertCounter", 17);
|
|
syn_flood_attacker_alert = new (std::nothrow) AlertCounter();
|
|
syn_flood_victim_alert = new (std::nothrow) AlertCounter();
|
|
flow_flood_attacker_alert = new (std::nothrow) AlertCounter();
|
|
flow_flood_victim_alert = new (std::nothrow) AlertCounter();
|
|
syn_sent_last_min = synack_recvd_last_min = 0;
|
|
syn_recvd_last_min = synack_sent_last_min = 0;
|
|
PROFILING_SUB_SECTION_EXIT(iface, 17);
|
|
|
|
if(init_all && ip.getVersion() /* IP is set */) {
|
|
char country_name[64];
|
|
|
|
if((as = iface->getAS(&ip, true /* Create if missing */, true /* Inline call */)) != NULL) {
|
|
as->incUses();
|
|
asn = as->get_asn();
|
|
asname = as->get_asname();
|
|
}
|
|
|
|
get_country(country_name, sizeof(country_name));
|
|
|
|
if((country = iface->getCountry(country_name, true /* Create if missing */, true /* Inline call */ )) != NULL)
|
|
country->incUses();
|
|
}
|
|
|
|
reloadHideFromTop();
|
|
reloadDhcpHost();
|
|
setEntityValue(get_hostkey(buf, sizeof(buf), true));
|
|
|
|
is_in_broadcast_domain = iface->isLocalBroadcastDomainHost(this, true /* Inline call */);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
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::updateHostPool(bool isInlineCall, bool firstUpdate) {
|
|
if(!iface)
|
|
return;
|
|
|
|
if(!firstUpdate) iface->decPoolNumHosts(get_host_pool(), isInlineCall);
|
|
host_pool_id = iface->getHostPool(this);
|
|
iface->incPoolNumHosts(get_host_pool(), isInlineCall);
|
|
|
|
#ifdef NTOPNG_PRO
|
|
if(iface && iface->is_bridge_interface()) {
|
|
HostPools *hp = iface->getHostPools();
|
|
|
|
if(hp && hp->enforceQuotasPerPoolMember(get_host_pool())) {
|
|
/* must allocate a structure to keep track of used quotas */
|
|
stats->allocateQuotaEnforcementStats();
|
|
} 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 */
|
|
stats->deleteQuotaEnforcementStats();
|
|
}
|
|
|
|
if(hp && hp->enforceShapersPerPoolMember(get_host_pool())) {
|
|
/* Align with global traffic shapers */
|
|
iface->getL7Policer()->cloneShapers(&host_traffic_shapers);
|
|
|
|
#ifdef HOST_POOLS_DEBUG
|
|
char buf[128];
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Cloned shapers 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::set_mac(Mac *_mac) {
|
|
if((mac != _mac) && (_mac != NULL)) {
|
|
if(mac) mac->decUses();
|
|
mac = _mac;
|
|
mac->incUses();
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
bool Host::hasAnomalies() const {
|
|
time_t now = time(0);
|
|
|
|
return stats->hasAnomalies(now);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::lua_get_anomalies(lua_State* vm) const {
|
|
if(!vm)
|
|
return;
|
|
|
|
if(hasAnomalies()) {
|
|
time_t now = time(0);
|
|
lua_newtable(vm);
|
|
|
|
stats->luaAnomalies(vm, now);
|
|
|
|
lua_pushstring(vm, "anomalies");
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::luaStrTableEntryLocked(lua_State * const vm, const char * const entry_name, const char * const entry_value) {
|
|
/* Perform access to const entry values using a lock as entry value can change for example during a data reset */
|
|
if(entry_name) {
|
|
m.lock(__FILE__, __LINE__);
|
|
|
|
if(entry_value)
|
|
lua_push_str_table_entry(vm, entry_name, entry_value);
|
|
|
|
m.unlock(__FILE__, __LINE__);
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::lua_get_names(lua_State * const vm, char * const buf, ssize_t buf_size) {
|
|
Mac * cur_mac = getMac();
|
|
|
|
lua_newtable(vm);
|
|
|
|
getMDNSName(buf, buf_size);
|
|
if(buf[0]) lua_push_str_table_entry(vm, "mdns", buf);
|
|
|
|
getMDNSTXTName(buf, buf_size);
|
|
if(buf[0]) lua_push_str_table_entry(vm, "mdns_txt", buf);
|
|
|
|
getResolvedName(buf, buf_size);
|
|
if(buf[0]) lua_push_str_table_entry(vm, "resolved", buf);
|
|
|
|
getNetbiosName(buf, buf_size);
|
|
if(buf[0]) lua_push_str_table_entry(vm, "netbios", buf);
|
|
|
|
if(isBroadcastDomainHost() && cur_mac) {
|
|
cur_mac->getDHCPName(buf, buf_size);
|
|
if(buf[0]) lua_push_str_table_entry(vm, "dhcp", buf);
|
|
}
|
|
|
|
lua_pushstring(vm, "names");
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
void Host::lua_get_ip(lua_State *vm) const {
|
|
char ip_buf[64];
|
|
|
|
lua_push_str_table_entry(vm, "ip", ip.print(ip_buf, sizeof(ip_buf)));
|
|
lua_push_uint64_table_entry(vm, "vlan", get_vlan_id());
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
void Host::lua_get_localhost_info(lua_State *vm) const {
|
|
lua_pushboolean(vm, isLocalHost());
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
void Host::lua_get_mac(lua_State *vm) const {
|
|
char buf[64];
|
|
/* Cache macs as they can be swapped/updated */
|
|
Mac *cur_mac = getMac();
|
|
|
|
lua_push_str_table_entry(vm, "mac", Utils::formatMac(cur_mac ? cur_mac->get_mac() : NULL, buf, sizeof(buf)));
|
|
lua_push_uint64_table_entry(vm, "devtype", getDeviceType());
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
void Host::lua_get_bytes(lua_State *vm) const {
|
|
lua_push_uint64_table_entry(vm, "bytes.sent", getNumBytesSent());
|
|
lua_push_uint64_table_entry(vm, "bytes.rcvd", getNumBytesRcvd());
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
void Host::lua_get_app_bytes(lua_State *vm, u_int app_id) const {
|
|
lua_pushinteger(vm, get_ndpi_stats()->getProtoBytes(app_id));
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
void Host::lua_get_cat_bytes(lua_State *vm, ndpi_protocol_category_t category_id) const {
|
|
lua_pushinteger(vm, get_ndpi_stats()->getCategoryBytes(category_id));
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
void Host::lua_get_packets(lua_State *vm) const {
|
|
lua_push_uint64_table_entry(vm, "packets.sent", getNumPktsSent());
|
|
lua_push_uint64_table_entry(vm, "packets.rcvd", getNumPktsRcvd());
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
void Host::lua_get_as(lua_State *vm) const {
|
|
const char *asn_n = get_asname();
|
|
|
|
lua_push_uint64_table_entry(vm, "asn", get_asn());
|
|
lua_push_str_table_entry(vm, "asname", asn_n ? asn_n : (char*)"");
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
void Host::lua_get_host_pool(lua_State *vm) const {
|
|
lua_push_uint64_table_entry(vm, "host_pool_id", get_host_pool());
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
void Host::lua_get_score(lua_State *vm) {
|
|
lua_push_uint64_table_entry(vm, "score", score.get());
|
|
lua_push_uint64_table_entry(vm, "score.as_client", score.getClient());
|
|
lua_push_uint64_table_entry(vm, "score.as_server", score.getServer());
|
|
lua_push_uint64_table_entry(vm, "score.total", score.get());
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
void Host::lua_get_score_breakdown(lua_State *vm) {
|
|
score.lua_breakdown(vm);
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
void Host::lua_get_os(lua_State *vm) {
|
|
char buf[64];
|
|
|
|
lua_push_int32_table_entry(vm, "os", getOS());
|
|
lua_push_str_table_entry(vm, "os_detail", getOSDetail(buf, sizeof(buf)));
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
void Host::lua_get_min_info(lua_State *vm) const {
|
|
lua_push_bool_table_entry(vm, "localhost", isLocalHost());
|
|
lua_push_bool_table_entry(vm, "systemhost", isSystemHost());
|
|
lua_push_bool_table_entry(vm, "privatehost", isPrivateHost());
|
|
lua_push_bool_table_entry(vm, "broadcast_domain_host", isBroadcastDomainHost());
|
|
lua_push_bool_table_entry(vm, "dhcpHost", isDhcpHost());
|
|
lua_push_bool_table_entry(vm, "is_blacklisted", isBlacklisted());
|
|
lua_push_bool_table_entry(vm, "is_broadcast", ip.isBroadcastAddress());
|
|
lua_push_bool_table_entry(vm, "is_multicast", ip.isMulticastAddress());
|
|
lua_push_int32_table_entry(vm, "host_services_bitmap", host_services_bitmap);
|
|
|
|
#ifdef HAVE_NEDGE
|
|
lua_push_bool_table_entry(vm, "childSafe", isChildSafe());
|
|
lua_push_bool_table_entry(vm, "has_blocking_quota", has_blocking_quota);
|
|
lua_push_bool_table_entry(vm, "has_blocking_shaper", has_blocking_shaper);
|
|
lua_push_bool_table_entry(vm, "drop_all_host_traffic", dropAllTraffic());
|
|
#endif
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
void Host::lua_get_geoloc(lua_State *vm) {
|
|
char *continent = NULL, *country_name = NULL, *city = NULL;
|
|
float latitude = 0, longitude = 0;
|
|
|
|
ntop->getGeolocation()->getInfo(&ip, &continent, &country_name, &city, &latitude, &longitude);
|
|
lua_push_str_table_entry(vm, "continent", continent ? continent : (char*)"");
|
|
lua_push_str_table_entry(vm, "country", country_name ? country_name : (char*)"");
|
|
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*)"");
|
|
ntop->getGeolocation()->freeInfo(&continent, &country_name, &city);
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
void Host::lua_get_syn_flood(lua_State *vm) const {
|
|
u_int16_t hits;
|
|
|
|
if((hits = syn_flood_victim_alert->hits()))
|
|
lua_push_uint64_table_entry(vm, "hits.syn_flood_victim", hits);
|
|
if((hits = syn_flood_attacker_alert->hits()))
|
|
lua_push_uint64_table_entry(vm, "hits.syn_flood_attacker", hits);
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
void Host::lua_get_flow_flood(lua_State *vm) const {
|
|
u_int16_t hits;
|
|
|
|
if((hits = flow_flood_victim_alert->hits()))
|
|
lua_push_uint64_table_entry(vm, "hits.flow_flood_victim", hits);
|
|
if((hits = flow_flood_attacker_alert->hits()))
|
|
lua_push_uint64_table_entry(vm, "hits.flow_flood_attacker", hits);
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
void Host::lua_get_services(lua_State *vm) const {
|
|
if(host_services_bitmap == 0) return;
|
|
|
|
lua_newtable(vm);
|
|
|
|
if(isDhcpServer()) lua_push_bool_table_entry(vm, "dhcp", true);
|
|
if(isDnsServer()) lua_push_bool_table_entry(vm, "dns", true);
|
|
if(isSmtpServer()) lua_push_bool_table_entry(vm, "smtp", true);
|
|
if(isNtpServer()) lua_push_bool_table_entry(vm, "ntp", true);
|
|
|
|
lua_pushstring(vm, "services");
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
void Host::lua_get_syn_scan(lua_State *vm) const {
|
|
u_int32_t hits;
|
|
|
|
hits = 0;
|
|
if (syn_sent_last_min > synack_recvd_last_min)
|
|
hits = syn_sent_last_min - synack_recvd_last_min;
|
|
if(hits)
|
|
lua_push_uint64_table_entry(vm, "hits.syn_scan_attacker", hits);
|
|
|
|
hits = 0;
|
|
if (syn_recvd_last_min > synack_sent_last_min)
|
|
hits = syn_recvd_last_min - synack_sent_last_min;
|
|
if(hits)
|
|
lua_push_uint64_table_entry(vm, "hits.syn_scan_victim", hits);
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
void Host::lua_get_time(lua_State* vm) const {
|
|
lua_push_uint64_table_entry(vm, "seen.first", get_first_seen());
|
|
lua_push_uint64_table_entry(vm, "seen.last", get_last_seen());
|
|
lua_push_uint64_table_entry(vm, "duration", get_duration());
|
|
lua_push_uint64_table_entry(vm, "total_activity_time", stats->getTotalActivityTime());
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
void Host::lua_get_num_alerts(lua_State* vm) const {
|
|
lua_newtable(vm);
|
|
lua_push_uint64_table_entry(vm, "min", getNumEngagedAlerts(minute_script));
|
|
lua_push_uint64_table_entry(vm, "5mins", getNumEngagedAlerts(five_minute_script));
|
|
lua_push_uint64_table_entry(vm, "hour", getNumEngagedAlerts(hour_script));
|
|
lua_push_uint64_table_entry(vm, "day", getNumEngagedAlerts(day_script));
|
|
lua_pushstring(vm, "num_triggered_alerts");
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
|
|
lua_push_uint64_table_entry(vm, "num_alerts", getNumEngagedAlerts());
|
|
lua_push_uint64_table_entry(vm, "active_alerted_flows", getNumAlertedFlows());
|
|
lua_push_uint64_table_entry(vm, "total_alerts", stats->getTotalAlerts());
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
void Host::lua_get_num_total_flows(lua_State* vm) const {
|
|
lua_push_uint64_table_entry(vm, "total_flows.as_client", getTotalNumFlowsAsClient());
|
|
lua_push_uint64_table_entry(vm, "total_flows.as_server", getTotalNumFlowsAsServer());
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
void Host::lua_get_num_flows(lua_State* vm) const {
|
|
lua_push_uint64_table_entry(vm, "active_flows.as_client", getNumOutgoingFlows());
|
|
lua_push_uint64_table_entry(vm, "active_flows.as_server", getNumIncomingFlows());
|
|
lua_push_uint64_table_entry(vm, "alerted_flows.as_server", getTotalNumAlertedIncomingFlows());
|
|
lua_push_uint64_table_entry(vm, "alerted_flows.as_client", getTotalNumAlertedOutgoingFlows());
|
|
lua_push_uint64_table_entry(vm, "unreachable_flows.as_server", getTotalNumUnreachableIncomingFlows());
|
|
lua_push_uint64_table_entry(vm, "unreachable_flows.as_client", getTotalNumUnreachableOutgoingFlows());
|
|
lua_push_uint64_table_entry(vm, "host_unreachable_flows.as_server", getTotalNumHostUnreachableIncomingFlows());
|
|
lua_push_uint64_table_entry(vm, "host_unreachable_flows.as_client", getTotalNumHostUnreachableOutgoingFlows());
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
void Host::lua_get_num_contacts(lua_State* vm) {
|
|
lua_push_uint64_table_entry(vm, "contacts.as_client", getNumActiveContactsAsClient());
|
|
lua_push_uint64_table_entry(vm, "contacts.as_server", getNumActiveContactsAsServer());
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
void Host::lua_get_num_http_hosts(lua_State* vm) {
|
|
lua_push_uint64_table_entry(vm, "active_http_hosts", getActiveHTTPHosts());
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
void Host::lua_get_fingerprints(lua_State* vm) {
|
|
fingerprints.ja3.lua("ja3_fingerprint", vm);
|
|
fingerprints.hassh.lua("hassh_fingerprint", vm);
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
void Host::lua_get_visual_name(lua_State* vm) {
|
|
char buf[64];
|
|
lua_pushstring(vm, get_visual_name(buf, sizeof(buf)));
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
void Host::lua(lua_State* vm, AddressTree *ptree,
|
|
bool host_details, bool verbose,
|
|
bool returnHost, bool asListElement) {
|
|
char buf[64], buf_id[64], *host_id = buf_id;
|
|
char ip_buf[64], *ipaddr = NULL;
|
|
bool mask_host = Utils::maskHost(isLocalHost());
|
|
|
|
|
|
if((ptree && (!match(ptree))) || mask_host)
|
|
return;
|
|
|
|
lua_newtable(vm);
|
|
|
|
lua_push_str_table_entry(vm, "ip", (ipaddr = printMask(ip_buf, sizeof(ip_buf))));
|
|
lua_push_uint64_table_entry(vm, "vlan", vlan_id);
|
|
lua_push_bool_table_entry(vm, "hiddenFromTop", isHiddenFromTop());
|
|
|
|
lua_push_uint64_table_entry(vm, "ipkey", ip.key());
|
|
lua_push_str_table_entry(vm, "tskey", get_tskey(buf_id, sizeof(buf_id)));
|
|
|
|
lua_push_str_table_entry(vm, "name", get_visual_name(buf, sizeof(buf)));
|
|
|
|
lua_get_min_info(vm);
|
|
lua_get_mac(vm);
|
|
|
|
lua_get_num_alerts(vm);
|
|
lua_get_score(vm);
|
|
|
|
lua_get_as(vm);
|
|
lua_get_os(vm);
|
|
lua_get_host_pool(vm);
|
|
|
|
stats->lua(vm, mask_host, Utils::bool2DetailsLevel(verbose, host_details));
|
|
|
|
lua_get_num_flows(vm);
|
|
lua_get_num_contacts(vm);
|
|
lua_get_num_http_hosts(vm);
|
|
|
|
luaDNS(vm, verbose);
|
|
luaTCP(vm);
|
|
luaICMP(vm, get_ip()->isIPv4(), false);
|
|
|
|
if(host_details) {
|
|
lua_get_score_breakdown(vm);
|
|
|
|
/*
|
|
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
|
|
*/
|
|
get_name(buf, sizeof(buf), false);
|
|
if(strlen(buf) == 0 || strcmp(buf, 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 */);
|
|
}
|
|
|
|
luaStrTableEntryLocked(vm, "ssdp", ssdpLocation); /* locked to protect against data-reset changes */
|
|
|
|
/* ifid is useful for example for view interfaces to detemine
|
|
the actual, original interface the host is associated to. */
|
|
lua_push_uint64_table_entry(vm, "ifid", iface->get_id());
|
|
if(!mask_host)
|
|
luaStrTableEntryLocked(vm, "info", names.mdns_info); /* locked to protect against data-reset changes */
|
|
|
|
lua_get_names(vm, buf, sizeof(buf));
|
|
|
|
lua_get_geoloc(vm);
|
|
|
|
lua_get_syn_flood(vm);
|
|
lua_get_flow_flood(vm);
|
|
lua_get_syn_scan(vm);
|
|
lua_get_services(vm);
|
|
}
|
|
|
|
lua_get_time(vm);
|
|
|
|
lua_get_fingerprints(vm);
|
|
|
|
if(verbose) {
|
|
if(hasAnomalies()) lua_get_anomalies(vm);
|
|
}
|
|
|
|
if(!returnHost)
|
|
host_id = get_hostkey(buf_id, sizeof(buf_id));
|
|
|
|
if(asListElement) {
|
|
lua_pushstring(vm, host_id);
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
}
|
|
}
|
|
|
|
/* ***************************************** */
|
|
|
|
char* Host::get_name(char *buf, u_int buf_len, bool force_resolution_if_not_found) {
|
|
char *addr = NULL, name_buf[96];
|
|
int rc = -1;
|
|
time_t now = time(NULL);
|
|
bool skip_resolution = false;
|
|
|
|
if(nextResolveAttempt
|
|
&& ((num_resolve_attempts > 1) || (nextResolveAttempt > now) || (nextResolveAttempt == (time_t)-1))) {
|
|
skip_resolution = true;
|
|
} else
|
|
nextResolveAttempt = ntop->getPrefs()->is_dns_resolution_enabled() ? now + MIN_HOST_RESOLUTION_FREQUENCY : (time_t)-1;
|
|
|
|
num_resolve_attempts++;
|
|
|
|
getResolvedName(name_buf, sizeof(name_buf));
|
|
if(name_buf[0])
|
|
goto out;
|
|
|
|
/* Most relevant names goes first */
|
|
if(isBroadcastDomainHost()) {
|
|
Mac *cur_mac = getMac(); /* Cache it as it can change */
|
|
if (cur_mac) {
|
|
cur_mac->getDHCPName(name_buf, sizeof(name_buf));
|
|
if(strlen(name_buf))
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
getMDNSTXTName(name_buf, sizeof(name_buf));
|
|
if(name_buf[0])
|
|
goto out;
|
|
|
|
getMDNSName(name_buf, sizeof(name_buf));
|
|
if(name_buf[0])
|
|
goto out;
|
|
|
|
getMDNSInfo(name_buf, sizeof(name_buf));
|
|
if(name_buf[0])
|
|
goto out;
|
|
|
|
getNetbiosName(name_buf, sizeof(name_buf));
|
|
if(name_buf[0])
|
|
goto out;
|
|
|
|
if(!skip_resolution) {
|
|
addr = ip.print(buf, buf_len);
|
|
rc = ntop->getRedis()->getAddress(addr, name_buf, sizeof(name_buf),
|
|
force_resolution_if_not_found);
|
|
}
|
|
|
|
if(rc == 0 && strcmp(addr, name_buf))
|
|
setResolvedName(name_buf);
|
|
else
|
|
addr = ip.print(name_buf, sizeof(name_buf));
|
|
|
|
out:
|
|
snprintf(buf, buf_len, "%s", name_buf);
|
|
return(buf);
|
|
}
|
|
|
|
/* ***************************************** */
|
|
|
|
/* Retrieve the host label. This should only be used to store persistent
|
|
* information from C. In lua use hostinfo2label instead. */
|
|
char * Host::get_host_label(char * const buf, ssize_t buf_len) {
|
|
char redis_key[CONST_MAX_LEN_REDIS_KEY];
|
|
char ip_buf[64];
|
|
|
|
/* Try to get a label first */
|
|
snprintf(redis_key, sizeof(redis_key), HOST_LABEL_NAMES_KEY, ip.print(ip_buf, sizeof(ip_buf)));
|
|
if(ntop->getRedis()->get(redis_key, buf, buf_len) != 0) {
|
|
|
|
/* Not found, use the internal names instead */
|
|
get_name(buf, buf_len, false /* don't resolve */);
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
/* ***************************************** */
|
|
|
|
char * Host::getResolvedName(char * const buf, ssize_t buf_len) {
|
|
if(buf && buf_len) {
|
|
m.lock(__FILE__, __LINE__);
|
|
snprintf(buf, buf_len, "%s", names.resolved ? names.resolved : "");
|
|
m.unlock(__FILE__, __LINE__);
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
/* ***************************************** */
|
|
|
|
char * Host::getMDNSName(char * const buf, ssize_t buf_len) {
|
|
if(buf && buf_len) {
|
|
m.lock(__FILE__, __LINE__);
|
|
snprintf(buf, buf_len, "%s", names.mdns ? names.mdns : "");
|
|
m.unlock(__FILE__, __LINE__);
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
|
|
/* ***************************************** */
|
|
|
|
char * Host::getMDNSTXTName(char * const buf, ssize_t buf_len) {
|
|
if(buf && buf_len) {
|
|
m.lock(__FILE__, __LINE__);
|
|
snprintf(buf, buf_len, "%s", names.mdns_txt ? names.mdns_txt : "");
|
|
m.unlock(__FILE__, __LINE__);
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
/* ***************************************** */
|
|
|
|
char * Host::getMDNSInfo(char * const buf, ssize_t buf_len) {
|
|
if(buf && buf_len) {
|
|
m.lock(__FILE__, __LINE__);
|
|
snprintf(buf, buf_len, "%s", names.mdns_info ? names.mdns_info : "");
|
|
m.unlock(__FILE__, __LINE__);
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
/* ***************************************** */
|
|
|
|
char * Host::getNetbiosName(char * const buf, ssize_t buf_len) {
|
|
if(buf && buf_len) {
|
|
m.lock(__FILE__, __LINE__);
|
|
snprintf(buf, buf_len, "%s", names.netbios ? names.netbios : "");
|
|
m.unlock(__FILE__, __LINE__);
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
/* ***************************************** */
|
|
|
|
const char * Host::getOSDetail(char * const buf, ssize_t buf_len) {
|
|
if(buf && buf_len)
|
|
buf[0] = '\0';
|
|
|
|
return buf;
|
|
}
|
|
|
|
/* ***************************************** */
|
|
|
|
bool Host::is_hash_entry_state_idle_transition_ready() const {
|
|
u_int32_t max_idle;
|
|
|
|
/*
|
|
Idle transition should only be allowed if host has NO alerts engaged.
|
|
This is to always keep in-memory hosts with ongoing issues.
|
|
|
|
- For hosts that actively generate traffic, this is achieved automatically (active hosts
|
|
stay in memory).
|
|
- For hosts that stop generating traffic, this is NOT achieved automatically (inactive hosts
|
|
become candidates for purging).
|
|
|
|
However, it is not desirable to keep inactive hosts in memory for an unlimited amount of time,
|
|
even if they have ongoing inssues, as this could cause OOMs or make ntopng vulnerable to certain attacks.
|
|
|
|
For this reason, when an host has ongoing issues, a different (larger) maximum idleness is used to:
|
|
- Keep it in memory for a longer time
|
|
- Avoid keeping inactive hosts in memory for an indefinite time
|
|
*/
|
|
|
|
if(getNumEngagedAlerts() > 0)
|
|
max_idle = ntop->getPrefs()->get_alerted_host_max_idle();
|
|
else
|
|
max_idle = ntop->getPrefs()->get_host_max_idle(isLocalHost());
|
|
|
|
bool res = (getUses() == 0)
|
|
&& is_active_entry_now_idle(max_idle);
|
|
|
|
#if DEBUG_HOST_IDLE_TRANSITION
|
|
char buf[64];
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Idle check [%s][local: %u][get_host_max_idle: %u][last seen: %u][ready: %u]",
|
|
ip.print(buf, sizeof(buf)), isLocalHost(), ntop->getPrefs()->get_host_max_idle(isLocalHost()), last_seen, res ? 1 : 0);
|
|
#endif
|
|
|
|
return res;
|
|
};
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::periodic_stats_update(const struct timeval *tv) {
|
|
Mac *cur_mac = getMac();
|
|
|
|
checkReloadPrefs();
|
|
checkNameReset();
|
|
checkDataReset();
|
|
checkStatsReset();
|
|
checkBroadcastDomain();
|
|
|
|
/* OS detection */
|
|
if((os == os_unknown) && cur_mac && cur_mac->getFingerprint())
|
|
os = Utils::getOSFromFingerprint(cur_mac->getFingerprint(), cur_mac->get_manufacturer(), cur_mac->getDeviceType());
|
|
|
|
stats->updateStats(tv);
|
|
|
|
GenericHashEntry::periodic_stats_update(tv);
|
|
|
|
custom_periodic_stats_update(tv);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::incStats(u_int32_t when, u_int8_t l4_proto,
|
|
u_int ndpi_proto, ndpi_protocol_category_t ndpi_category,
|
|
custom_app_t custom_app,
|
|
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,
|
|
bool peer_is_unicast) {
|
|
if(sent_bytes || rcvd_bytes) {
|
|
stats->incStats(when, l4_proto, ndpi_proto, ndpi_category, custom_app,
|
|
sent_packets, sent_bytes, sent_goodput_bytes, rcvd_packets,
|
|
rcvd_bytes, rcvd_goodput_bytes, peer_is_unicast);
|
|
|
|
updateSeen();
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::serialize(json_object *my_object, DetailsLevel details_level) {
|
|
char buf[96];
|
|
Mac *m = mac;
|
|
|
|
stats->getJSONObject(my_object, details_level);
|
|
|
|
json_object_object_add(my_object, "ip", ip.getJSONObject());
|
|
if(vlan_id != 0) json_object_object_add(my_object, "vlan_id", json_object_new_int(vlan_id));
|
|
json_object_object_add(my_object, "mac_address", json_object_new_string(Utils::formatMac(m ? m->get_mac() : NULL, buf, sizeof(buf))));
|
|
json_object_object_add(my_object, "ifid", json_object_new_int(iface->get_id()));
|
|
|
|
if(details_level >= details_high) {
|
|
GenericHashEntry::getJSONObject(my_object, details_level);
|
|
json_object_object_add(my_object, "last_stats_reset", json_object_new_int64(last_stats_reset));
|
|
json_object_object_add(my_object, "asn", json_object_new_int(asn));
|
|
|
|
get_name(buf, sizeof(buf), false);
|
|
if(strlen(buf)) json_object_object_add(my_object, "symbolic_name", json_object_new_string(buf));
|
|
if(asname) json_object_object_add(my_object, "asname", json_object_new_string(asname ? asname : (char*)""));
|
|
json_object_object_add(my_object, "os_id", json_object_new_int(getOS()));
|
|
|
|
json_object_object_add(my_object, "localHost", json_object_new_boolean(isLocalHost()));
|
|
json_object_object_add(my_object, "systemHost", json_object_new_boolean(isSystemHost()));
|
|
json_object_object_add(my_object, "broadcastDomainHost", json_object_new_boolean(isBroadcastDomainHost()));
|
|
json_object_object_add(my_object, "is_blacklisted", json_object_new_boolean(isBlacklisted()));
|
|
json_object_object_add(my_object, "host_services_bitmap", json_object_new_int(host_services_bitmap));
|
|
|
|
/* Generic Host */
|
|
json_object_object_add(my_object, "num_alerts", json_object_new_int(getNumEngagedAlerts()));
|
|
}
|
|
|
|
/* 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());
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
char* Host::get_visual_name(char *buf, u_int buf_len) {
|
|
bool mask_host = Utils::maskHost(isLocalHost());
|
|
char buf2[64];
|
|
char ipbuf[64];
|
|
char *sym_name;
|
|
|
|
if(! mask_host) {
|
|
sym_name = 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);
|
|
buf[buf_len-1] = '\0';
|
|
}
|
|
} 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;
|
|
Mac *m = mac; /* Need to cache them as they can be swapped/updated */
|
|
|
|
if(!match(ptree)) return(false);
|
|
keybuf_ptr = get_hostkey(keybuf, sizeof(keybuf));
|
|
|
|
if(strcasestr((ipbuf_ptr = Utils::formatMac(m ? m->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::addIfMatching(lua_State* vm, u_int8_t *_mac) {
|
|
if(mac && mac->equal(_mac)) {
|
|
char keybuf[64], ipbuf[32];
|
|
|
|
lua_push_str_table_entry(vm,
|
|
get_string_key(ipbuf, sizeof(ipbuf)),
|
|
get_hostkey(keybuf, sizeof(keybuf)));
|
|
return(true);
|
|
}
|
|
|
|
return(false);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::incNumFlows(time_t t, bool as_client) {
|
|
AlertCounter *counter;
|
|
|
|
if(as_client) {
|
|
counter = flow_flood_attacker_alert;
|
|
num_active_flows_as_client++;
|
|
} else {
|
|
counter = flow_flood_victim_alert;
|
|
num_active_flows_as_server++;
|
|
}
|
|
|
|
counter->inc(t, this);
|
|
stats->incNumFlows(as_client);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::decNumFlows(time_t t, bool as_client) {
|
|
if(as_client)
|
|
num_active_flows_as_client--;
|
|
else
|
|
num_active_flows_as_server--;
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
// TODO NTOPNG_PRO -> HAVE_NEDGE
|
|
#ifdef NTOPNG_PRO
|
|
|
|
TrafficShaper* Host::get_shaper(ndpi_protocol ndpiProtocol, bool isIngress) {
|
|
HostPools *hp;
|
|
TrafficShaper *ts = NULL, **shapers = NULL;
|
|
u_int8_t shaper_id = DEFAULT_SHAPER_ID;
|
|
L7Policer *policer;
|
|
L7PolicySource_t policy_source;
|
|
|
|
if(!(policer = iface->getL7Policer())) return NULL;
|
|
if(!(hp = iface->getHostPools())) return policer->getShaper(PASS_ALL_SHAPER_ID);
|
|
|
|
// Avoid setting drop verdicts for wan hosts policy
|
|
if(getMac() && (getMac()->locate() != located_on_lan_interface)) {
|
|
return policer->getShaper(DEFAULT_SHAPER_ID);
|
|
}
|
|
|
|
shaper_id = policer->getShaperIdForPool(get_host_pool(), ndpiProtocol, isIngress, &policy_source);
|
|
|
|
#ifdef SHAPER_DEBUG
|
|
{
|
|
char buf[64], buf1[64];
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "[%s] [%s@%u][ndpiProtocol=%d/%s] => [shaper_id=%d]",
|
|
isIngress ? "INGRESS" : "EGRESS",
|
|
ip.print(buf, sizeof(buf)), vlan_id,
|
|
ndpiProtocol.app_protocol,
|
|
ndpi_protocol2name(iface->get_ndpi_struct(), ndpiProtocol, buf1, sizeof(buf1)),
|
|
shaper_id);
|
|
}
|
|
#endif
|
|
|
|
if(hp->enforceShapersPerPoolMember(get_host_pool())
|
|
&& (shapers = host_traffic_shapers)
|
|
&& shaper_id >= 0 && shaper_id < NUM_TRAFFIC_SHAPERS) {
|
|
ts = shapers[shaper_id];
|
|
|
|
#ifdef SHAPER_DEBUG
|
|
char buf[64], bufs[64];
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "[%s@%u] PER-HOST Traffic shaper: %s",
|
|
ip.print(buf, sizeof(buf)), vlan_id,
|
|
ts->print(bufs, sizeof(bufs)));
|
|
#endif
|
|
|
|
} else {
|
|
ts = policer->getShaper(shaper_id);
|
|
|
|
#ifdef SHAPER_DEBUG
|
|
char buf[64];
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "[%s@%u] SHARED Traffic Shaper",
|
|
ip.print(buf, sizeof(buf)), vlan_id);
|
|
#endif
|
|
|
|
}
|
|
|
|
/* Update blocking status */
|
|
if(ts && ts->shaping_enabled() && ts->get_max_rate_kbit_sec() == 0)
|
|
has_blocking_shaper = true;
|
|
else
|
|
has_blocking_shaper = false;
|
|
|
|
return ts;
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
bool Host::checkQuota(ndpi_protocol ndpiProtocol, L7PolicySource_t *quota_source, const struct tm *now) {
|
|
bool is_above;
|
|
L7Policer *policer;
|
|
|
|
if((policer = iface->getL7Policer()) == NULL)
|
|
return false;
|
|
|
|
is_above = policer->checkQuota(get_host_pool(), stats->getQuotaEnforcementStats(), ndpiProtocol, quota_source, now);
|
|
|
|
#ifdef SHAPER_DEBUG
|
|
char buf[128], protobuf[32];
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "[QUOTA (%s)] [%s@%u] => %s %s",
|
|
ndpi_protocol2name(iface->get_ndpi_struct(), ndpiProtocol, protobuf, sizeof(protobuf)),
|
|
ip.print(buf, sizeof(buf)), vlan_id,
|
|
is_above ? (char*)"EXCEEDED" : (char*)"ok",
|
|
stats->getQuotaEnforcementStats() ? "[QUOTAS enforced per pool member]" : "");
|
|
#endif
|
|
|
|
has_blocking_quota |= is_above;
|
|
return is_above;
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::luaUsedQuotas(lua_State* vm) {
|
|
HostPoolStats *quota_stats = stats->getQuotaEnforcementStats();
|
|
|
|
if(quota_stats)
|
|
quota_stats->lua(vm, iface);
|
|
else
|
|
lua_newtable(vm);
|
|
}
|
|
#endif
|
|
|
|
/* *************************************** */
|
|
|
|
/* 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::reloadHostBlacklist() {
|
|
ip.reloadBlacklist(iface->get_ndpi_struct());
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::offlineSetMDNSInfo(char * const str) {
|
|
char *cur_info;
|
|
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(names.mdns_info || !str)
|
|
return; /* Already set */
|
|
|
|
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';
|
|
|
|
if((cur_info = strdup(str))) {
|
|
for(i = 0; cur_info[i] != '\0'; i++) {
|
|
if(!isascii(cur_info[i]))
|
|
cur_info[i] = ' ';
|
|
}
|
|
|
|
/* Time to set the actual info */
|
|
names.mdns_info = cur_info;
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::offlineSetSSDPLocation(const char * const url) {
|
|
if(!ssdpLocation && url && (ssdpLocation = strdup(url)))
|
|
;
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::offlineSetMDNSName(const char * const mdns_n) {
|
|
if(!names.mdns && mdns_n && (names.mdns = strdup(mdns_n)))
|
|
;
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::offlineSetMDNSTXTName(const char * const mdns_n_txt) {
|
|
if(!names.mdns_txt && mdns_n_txt && (names.mdns_txt = strdup(mdns_n_txt)))
|
|
;
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::offlineSetNetbiosName(const char * const netbios_n) {
|
|
if(!names.netbios && netbios_n && (names.netbios = strdup(netbios_n)))
|
|
;
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::setResolvedName(const char * const resolved_name) {
|
|
/* This is NOT set inline, so we must lock. */
|
|
if(resolved_name) {
|
|
m.lock(__FILE__, __LINE__);
|
|
if(!names.resolved && (names.resolved = strdup(resolved_name)))
|
|
;
|
|
m.unlock(__FILE__, __LINE__);
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
char* Host::get_country(char *buf, u_int buf_len) {
|
|
char *continent = NULL, *country_name = NULL, *city = NULL;
|
|
float latitude = 0, longitude = 0;
|
|
|
|
ntop->getGeolocation()->getInfo(&ip, &continent, &country_name, &city, &latitude, &longitude);
|
|
|
|
if(country_name)
|
|
snprintf(buf, buf_len, "%s", country_name);
|
|
else
|
|
buf[0] = '\0';
|
|
|
|
ntop->getGeolocation()->freeInfo(&continent, &country_name, &city);
|
|
|
|
return(buf);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
char* Host::get_city(char *buf, u_int buf_len) {
|
|
char *continent = NULL, *country_name = NULL, *city = NULL;
|
|
float latitude = 0, longitude = 0;
|
|
|
|
ntop->getGeolocation()->getInfo(&ip, &continent, &country_name, &city, &latitude, &longitude);
|
|
|
|
if(city) {
|
|
snprintf(buf, buf_len, "%s", city);
|
|
} else
|
|
buf[0] = '\0';
|
|
|
|
ntop->getGeolocation()->freeInfo(&continent, &country_name, &city);
|
|
|
|
return(buf);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::get_geocoordinates(float *latitude, float *longitude) {
|
|
char *continent = NULL, *country_name = NULL, *city = NULL;
|
|
|
|
*latitude = 0, *longitude = 0;
|
|
ntop->getGeolocation()->getInfo(&ip, &continent, &country_name, &city, latitude, longitude);
|
|
ntop->getGeolocation()->freeInfo(&continent, &country_name, &city);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::serialize_geocoordinates(ndpi_serializer *s, const char *prefix) {
|
|
char *continent = NULL, *country = NULL, *city = NULL, buf[64];
|
|
float latitude = 0, longitude = 0;
|
|
|
|
ntop->getGeolocation()->getInfo(&ip, &continent, &country, &city, &latitude, &longitude);
|
|
|
|
if(city) {
|
|
snprintf(buf, sizeof(buf), "%s_city_name", prefix);
|
|
ndpi_serialize_string_string(s, buf, city);
|
|
}
|
|
|
|
if(country) {
|
|
snprintf(buf, sizeof(buf), "%s_country_name", prefix);
|
|
ndpi_serialize_string_string(s, buf, country);
|
|
}
|
|
|
|
if(continent) {
|
|
snprintf(buf, sizeof(buf), "%s_continent_name", prefix);
|
|
ndpi_serialize_string_string(s, buf, continent);
|
|
}
|
|
|
|
if(longitude) {
|
|
snprintf(buf, sizeof(buf), "%s_location_lon", prefix);
|
|
ndpi_serialize_string_float(s, buf, longitude, "%.f");
|
|
}
|
|
|
|
if(latitude) {
|
|
snprintf(buf, sizeof(buf), "%s_location_lat", prefix);
|
|
ndpi_serialize_string_float(s, buf, latitude, "%.f");
|
|
}
|
|
|
|
ntop->getGeolocation()->freeInfo(&continent, &country, &city);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
bool Host::isOneWayTraffic() const {
|
|
/* When both directions are at zero, it means no periodic update has visited the host yet,
|
|
so nothing can be said about its traffic directions. One way is only returned when
|
|
exactly one direction is greater than zero. */
|
|
return stats->getNumBytes() && !(stats->getNumBytesRcvd() && stats->getNumBytesSent());
|
|
};
|
|
|
|
/* *************************************** */
|
|
|
|
bool Host::isTwoWaysTraffic() const {
|
|
return stats->getNumBytesRcvd() && stats->getNumBytesSent();
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
DeviceProtoStatus Host::getDeviceAllowedProtocolStatus(ndpi_protocol proto, bool as_client) {
|
|
if(getMac() && !getMac()->isSpecialMac()
|
|
#ifdef HAVE_NEDGE
|
|
/* On nEdge the concept of device protocol policies is only applied to unassigned devices on LAN */
|
|
&& (getMac()->locate() == located_on_lan_interface)
|
|
#endif
|
|
)
|
|
return ntop->getDeviceAllowedProtocolStatus(getMac()->getDeviceType(), proto, get_host_pool(), as_client);
|
|
|
|
return device_proto_allowed;
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
bool Host::statsResetRequested() {
|
|
return(stats_reset_requested || (last_stats_reset < ntop->getLastStatsReset()));
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::checkStatsReset() {
|
|
if(stats_shadow) {
|
|
delete stats_shadow;
|
|
stats_shadow = NULL;
|
|
}
|
|
|
|
if(statsResetRequested()) {
|
|
HostStats *new_stats = allocateStats();
|
|
stats_shadow = stats;
|
|
stats = new_stats;
|
|
stats_shadow->resetTopSitesData();
|
|
|
|
/* Reset internal state */
|
|
#ifdef NTOPNG_PRO
|
|
has_blocking_quota = false;
|
|
#endif
|
|
|
|
last_stats_reset = ntop->getLastStatsReset();
|
|
stats_reset_requested = false;
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::checkBroadcastDomain() {
|
|
if(iface->reloadHostsBroadcastDomain())
|
|
is_in_broadcast_domain = iface->isLocalBroadcastDomainHost(this, false /* Non-inline call */);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
u_int16_t Host::incScoreValue(u_int16_t score_incr, ScoreCategory score_category, bool as_client) {
|
|
return score.incValue(score_incr, score_category, as_client);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
u_int16_t Host::decScoreValue(u_int16_t score_decr, ScoreCategory score_category, bool as_client) {
|
|
return score.decValue(score_decr, score_category, as_client);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::freeHostNames() {
|
|
if(ssdpLocation) { free(ssdpLocation); ssdpLocation = NULL; }
|
|
if(names.mdns) { free(names.mdns); names.mdns = NULL; }
|
|
if(names.mdns_info){ free(names.mdns_info); names.mdns_info = NULL; }
|
|
if(names.mdns_txt) { free(names.mdns_txt); names.mdns_txt = NULL; }
|
|
if(names.resolved) { free(names.resolved); names.resolved = NULL; }
|
|
if(names.netbios) { free(names.netbios); names.netbios = NULL; }
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::resetHostNames() {
|
|
m.lock(__FILE__, __LINE__);
|
|
freeHostNames();
|
|
m.unlock(__FILE__, __LINE__);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::checkNameReset() {
|
|
if(name_reset_requested) {
|
|
resetHostNames();
|
|
name_reset_requested = false;
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::deleteHostData() {
|
|
resetHostNames();
|
|
first_seen = last_seen;
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Host::checkDataReset() {
|
|
if(data_delete_requested) {
|
|
deleteHostData();
|
|
data_delete_requested = false;
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
char* Host::get_mac_based_tskey(Mac *mac, char *buf, size_t bufsize) {
|
|
char *k = mac->print(buf, bufsize);
|
|
|
|
/* NOTE: it is important to differentiate between v4 and v6 for macs */
|
|
strncat(buf, get_ip()->isIPv4() ? "_v4" : "_v6", bufsize);
|
|
|
|
return(k);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
char* Host::get_tskey(char *buf, size_t bufsize) {
|
|
char *k;
|
|
Mac *cur_mac = getMac(); /* Cache macs as they can be swapped/updated */
|
|
|
|
if(serializeByMac())
|
|
k = get_mac_based_tskey(cur_mac, buf, bufsize);
|
|
else
|
|
k = get_hostkey(buf, bufsize);
|
|
|
|
return(k);
|
|
}
|