mirror of
https://github.com/ntop/ntopng.git
synced 2026-05-20 00:57:00 +00:00
875 lines
24 KiB
C++
875 lines
24 KiB
C++
/*
|
|
*
|
|
* (C) 2015-20 - 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"
|
|
|
|
/* #define HOST_POOLS_DEBUG 1 */
|
|
|
|
/* *************************************** */
|
|
|
|
HostPools::HostPools(NetworkInterface *_iface) {
|
|
tree = tree_shadow = NULL;
|
|
#ifdef NTOPNG_PRO
|
|
children_safe = forge_global_dns = NULL;
|
|
routing_policy_id = NULL;
|
|
|
|
if((children_safe = (bool*)calloc(MAX_NUM_HOST_POOLS, sizeof(bool))) == NULL)
|
|
throw 1;
|
|
|
|
if((forge_global_dns = (bool*)calloc(MAX_NUM_HOST_POOLS, sizeof(bool))) == NULL)
|
|
throw 1;
|
|
|
|
if((routing_policy_id = (u_int8_t*)calloc(MAX_NUM_HOST_POOLS, sizeof(u_int8_t))) == NULL)
|
|
throw 1;
|
|
|
|
for(int i = 0; i < MAX_NUM_HOST_POOLS; i++) routing_policy_id[i] = DEFAULT_ROUTING_TABLE_ID;
|
|
|
|
if((volatile_members = (volatile_members_t**)calloc(MAX_NUM_HOST_POOLS, sizeof(volatile_members_t*))) == NULL
|
|
|| (volatile_members_lock = new Mutex*[MAX_NUM_HOST_POOLS]) == NULL
|
|
|| (pool_shaper = (u_int16_t*)calloc(MAX_NUM_HOST_POOLS, sizeof(u_int16_t))) == NULL
|
|
|| (schedule_bitmap = (u_int32_t*)calloc(MAX_NUM_HOST_POOLS, sizeof(u_int32_t))) == NULL
|
|
|| (enforce_quotas_per_pool_member = (bool*)calloc(MAX_NUM_HOST_POOLS, sizeof(bool))) == NULL
|
|
|| (enforce_shapers_per_pool_member = (bool*)calloc(MAX_NUM_HOST_POOLS, sizeof(bool))) == NULL)
|
|
throw 1;
|
|
|
|
for(int i = 0; i < MAX_NUM_HOST_POOLS; i++) {
|
|
if((volatile_members_lock[i] = new Mutex()) == NULL)
|
|
throw 2;
|
|
}
|
|
#endif
|
|
|
|
stats = stats_shadow = NULL;
|
|
|
|
if((num_active_hosts_inline = (int32_t*)calloc(sizeof(int32_t), MAX_NUM_HOST_POOLS)) == NULL
|
|
|| (num_active_hosts_offline = (int32_t*)calloc(sizeof(int32_t), MAX_NUM_HOST_POOLS)) == NULL
|
|
|| (num_active_l2_devices_inline = (int32_t*)calloc(sizeof(int32_t), MAX_NUM_HOST_POOLS)) == NULL
|
|
|| (num_active_l2_devices_offline = (int32_t*)calloc(sizeof(int32_t), MAX_NUM_HOST_POOLS)) == NULL)
|
|
throw 1;
|
|
|
|
latest_swap = 0;
|
|
if((swap_lock = new Mutex()) == NULL)
|
|
throw 3;
|
|
|
|
if(_iface)
|
|
iface = _iface;
|
|
|
|
reloadPools();
|
|
|
|
loadFromRedis();
|
|
|
|
max_num_pools = MAX_NUM_HOST_POOLS;
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "Host Pools Available: %u", MAX_NUM_HOST_POOLS);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void HostPools::deleteStats(HostPoolStats ***hps) {
|
|
if(hps) {
|
|
if(*hps) {
|
|
for(int i = 0; i < MAX_NUM_HOST_POOLS; i++)
|
|
if((*hps)[i])
|
|
delete (*hps)[i];
|
|
delete [] *hps;
|
|
*hps = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
HostPools::~HostPools() {
|
|
if(num_active_hosts_inline)
|
|
free(num_active_hosts_inline);
|
|
if(num_active_hosts_offline)
|
|
free(num_active_hosts_offline);
|
|
if(num_active_l2_devices_inline)
|
|
free(num_active_l2_devices_inline);
|
|
if(num_active_l2_devices_offline)
|
|
free(num_active_l2_devices_offline);
|
|
|
|
if(tree_shadow) delete tree_shadow;
|
|
if(tree) delete tree;
|
|
if(swap_lock) delete swap_lock;
|
|
|
|
dumpToRedis();
|
|
if(stats) deleteStats(&stats);
|
|
if(stats_shadow) deleteStats(&stats_shadow);
|
|
|
|
#ifdef NTOPNG_PRO
|
|
if(children_safe) free(children_safe);
|
|
if(forge_global_dns) free(forge_global_dns);
|
|
if(routing_policy_id) free(routing_policy_id);
|
|
if(pool_shaper)
|
|
free(pool_shaper);
|
|
if(schedule_bitmap)
|
|
free(schedule_bitmap);
|
|
if(enforce_quotas_per_pool_member)
|
|
free(enforce_quotas_per_pool_member);
|
|
if(enforce_shapers_per_pool_member)
|
|
free(enforce_shapers_per_pool_member);
|
|
if(volatile_members_lock) {
|
|
for(int i = 0; i < MAX_NUM_HOST_POOLS; i++) {
|
|
if(volatile_members_lock[i])
|
|
delete volatile_members_lock[i];
|
|
}
|
|
|
|
delete []volatile_members_lock;
|
|
}
|
|
|
|
if(volatile_members) {
|
|
for(int pool_id = 0; pool_id < MAX_NUM_HOST_POOLS; pool_id++) {
|
|
volatile_members_t *current, *tmp;
|
|
|
|
HASH_ITER(hh, volatile_members[pool_id], current, tmp) {
|
|
HASH_DEL(volatile_members[pool_id], current);
|
|
free(current->host_or_mac);
|
|
free(current);
|
|
}
|
|
|
|
if(volatile_members[pool_id])
|
|
free(volatile_members[pool_id]);
|
|
}
|
|
|
|
free(volatile_members);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void HostPools::swap(VlanAddressTree *new_trees, HostPoolStats **new_stats) {
|
|
swap_lock->lock(__FILE__, __LINE__);
|
|
|
|
while(time(NULL) - latest_swap < 1) {
|
|
swap_lock->unlock(__FILE__, __LINE__);
|
|
sleep(1); /* Force at least 1 sec. time between consecutive swaps */
|
|
swap_lock->lock(__FILE__, __LINE__);
|
|
}
|
|
|
|
/* Swap statistics */
|
|
if(new_stats) {
|
|
if(stats) {
|
|
if(stats_shadow) deleteStats(&stats_shadow);
|
|
stats_shadow = stats;
|
|
}
|
|
|
|
stats = new_stats;
|
|
}
|
|
|
|
/* Swap address trees */
|
|
if(new_trees) {
|
|
if(tree) {
|
|
if(tree_shadow) delete tree_shadow;
|
|
tree_shadow = tree;
|
|
}
|
|
|
|
tree = new_trees;
|
|
}
|
|
|
|
latest_swap = time(NULL);
|
|
swap_lock->unlock(__FILE__, __LINE__);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void HostPools::dumpToRedis() {
|
|
char key[128];
|
|
char buf[32];
|
|
Redis *redis = ntop->getRedis();
|
|
|
|
if((!redis) || (! stats) || (! iface)) return;
|
|
|
|
snprintf(key, sizeof(key), HOST_POOL_SERIALIZED_KEY, iface->get_id());
|
|
|
|
for(int i = 0; i<MAX_NUM_HOST_POOLS; i++) {
|
|
if(stats[i] && !stats[i]->needsReset()) {
|
|
snprintf(buf, sizeof(buf), "%d", i);
|
|
char *value = stats[i]->serialize(iface);
|
|
|
|
if(value) {
|
|
redis->hashSet(key, buf, value);
|
|
free(value);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_NEDGE
|
|
// Save the deadline time for quota expiration, assuming quota is reset at midnight
|
|
snprintf(buf, sizeof(buf), "%u",
|
|
Utils::roundTime(time(0), 86400, ntop->get_time_offset()) - 86400);
|
|
redis->hashSet(key, (char *)"deadline", buf);
|
|
#endif
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void HostPools::loadFromRedis() {
|
|
char key[CONST_MAX_LEN_REDIS_KEY], buf[32], *value;
|
|
json_object *obj;
|
|
enum json_tokener_error jerr = json_tokener_success;
|
|
Redis *redis = ntop->getRedis();
|
|
|
|
snprintf(key, sizeof(key), HOST_POOL_SERIALIZED_KEY, iface->get_id());
|
|
|
|
if((!redis) || (!stats) || (!iface)) return;
|
|
|
|
#ifdef HAVE_NEDGE
|
|
time_t deadline = 0;
|
|
|
|
if(redis->hashGet(key, (char *)"deadline", buf, sizeof(buf)) == 0) {
|
|
sscanf(buf, "%lu", &deadline);
|
|
|
|
if(time(0) > deadline)
|
|
return; /* Expired */
|
|
}
|
|
#endif
|
|
|
|
if((value = (char *) malloc(POOL_MAX_SERIALIZED_LEN)) == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Unable to allocate memory to deserialize %s", key);
|
|
return;
|
|
}
|
|
|
|
for(int i = 0; i<MAX_NUM_HOST_POOLS; i++) {
|
|
if(stats[i]) {
|
|
snprintf(buf, sizeof(buf), "%d", i);
|
|
if(redis->hashGet(key, buf, value, POOL_MAX_SERIALIZED_LEN) == 0) {
|
|
if((obj = json_tokener_parse_verbose(value, &jerr)) == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "JSON Parse error [%s] key: %s: %s",
|
|
json_tokener_error_desc(jerr),
|
|
key, value);
|
|
} else {
|
|
stats[i]->deserialize(iface, obj);
|
|
json_object_put(obj);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
free(value);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void HostPools::luaStats(lua_State *vm) {
|
|
if(vm) {
|
|
lua_newtable(vm);
|
|
|
|
for(int i = 0; i < MAX_NUM_HOST_POOLS; i++)
|
|
luaStats(vm, i);
|
|
}
|
|
};
|
|
|
|
/* *************************************** */
|
|
|
|
void HostPools::luaStats(lua_State *vm, u_int16_t pool_id) {
|
|
HostPoolStats *hps;
|
|
|
|
if(vm) {
|
|
if(stats && pool_id < MAX_NUM_HOST_POOLS) {
|
|
if((hps = stats[pool_id])) {
|
|
/* Must use the assigned hps as stats can be swapped
|
|
and accesses such as stats[pool_id] could yield a NULL value */
|
|
hps->lua(vm, iface);
|
|
lua_push_uint64_table_entry(vm, "num_hosts", getNumPoolHosts(pool_id));
|
|
lua_rawseti(vm, -2, pool_id);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/* *************************************** */
|
|
|
|
void HostPools::updateStats(struct timeval *tv) {
|
|
HostPoolStats *hps;
|
|
|
|
if(stats && tv) {
|
|
for(int i = 0; i < MAX_NUM_HOST_POOLS; i++)
|
|
if((hps = stats[i]))
|
|
hps->updateStats(tv); /* Use hps, stats[i] can become NULL after a swap */
|
|
}
|
|
};
|
|
|
|
/* *************************************** */
|
|
|
|
void HostPools::incPoolStats(u_int32_t when, u_int16_t host_pool_id, u_int16_t ndpi_proto,
|
|
ndpi_protocol_category_t category_id, u_int64_t sent_packets, u_int64_t sent_bytes,
|
|
u_int64_t rcvd_packets, u_int64_t rcvd_bytes) {
|
|
HostPoolStats *hps = getPoolStats(host_pool_id);
|
|
|
|
if(!hps) return;
|
|
|
|
/* Important to use the assigned hps as a swap can make stats[host_pool_id] NULL */
|
|
hps->incStats(when, ndpi_proto, sent_packets, sent_bytes, rcvd_packets, rcvd_bytes);
|
|
hps->incCategoryStats(when, category_id, sent_bytes, rcvd_bytes);
|
|
};
|
|
|
|
/* *************************************** */
|
|
|
|
#ifdef NTOPNG_PRO
|
|
|
|
void HostPools::reloadVolatileMembers(VlanAddressTree *_trees) {
|
|
volatile_members_t *current, *tmp;
|
|
char *at, *member;
|
|
bool rc;
|
|
u_int16_t vlan_id;
|
|
|
|
if(!_trees)
|
|
return;
|
|
|
|
for(int pool_id = 0; pool_id < MAX_NUM_HOST_POOLS; pool_id++) {
|
|
|
|
if(!volatile_members[pool_id])
|
|
continue;
|
|
|
|
volatile_members_lock[pool_id]->lock(__FILE__, __LINE__);
|
|
|
|
if(stats && stats[pool_id]) { /* The pool exists */
|
|
HASH_ITER(hh, volatile_members[pool_id], current, tmp) {
|
|
member = strdup(current->host_or_mac);
|
|
|
|
if((at = strchr(member, '@'))) {
|
|
vlan_id = atoi(at + 1);
|
|
*at = '\0';
|
|
} else
|
|
vlan_id = 0;
|
|
|
|
if(!(rc = _trees->addAddress(vlan_id, member, pool_id))
|
|
#ifdef HOST_POOLS_DEBUG
|
|
|| true
|
|
#endif
|
|
)
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"%s VOLATILE tree node for %s [vlan %i] [host pool: %i]",
|
|
rc ? "Successfully added" : "Unable to add",
|
|
member, vlan_id,
|
|
pool_id);
|
|
|
|
|
|
free(member);
|
|
}
|
|
} else { /* The pool no longer exists */
|
|
HASH_ITER(hh, volatile_members[pool_id], current, tmp) {
|
|
HASH_DEL(volatile_members[pool_id], current);
|
|
free(current->host_or_mac);
|
|
free(current);
|
|
}
|
|
}
|
|
|
|
volatile_members_lock[pool_id]->unlock(__FILE__, __LINE__);
|
|
}
|
|
|
|
#ifdef HAVE_NEDGE
|
|
/* Note: we must re-evaluate the active flows as a captive portal host may be blocked now */
|
|
if(iface && (iface->getIfType() == interface_type_NETFILTER))
|
|
((NetfilterInterface *) iface)->setPolicyChanged();
|
|
#endif
|
|
};
|
|
|
|
/* *************************************** */
|
|
|
|
void HostPools::addVolatileMember(char *host_or_mac, u_int16_t host_pool_id, time_t lifetime) {
|
|
volatile_members_t *m;
|
|
|
|
if(!host_or_mac || host_pool_id >= MAX_NUM_HOST_POOLS)
|
|
return;
|
|
|
|
volatile_members_lock[host_pool_id]->lock(__FILE__, __LINE__);
|
|
|
|
HASH_FIND_STR(volatile_members[host_pool_id], host_or_mac, m);
|
|
|
|
if(m == NULL) {
|
|
m = (volatile_members_t*)calloc(1, sizeof(volatile_members_t));
|
|
m->host_or_mac = strdup(host_or_mac);
|
|
HASH_ADD_STR(volatile_members[host_pool_id], host_or_mac, m);
|
|
}
|
|
m->lifetime = time(NULL) + lifetime;
|
|
|
|
#ifdef HOST_POOLS_DEBUG
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Adding %s VOLATILE MEMBER to the hash table [host pool: %i] [lifetime: %i]",
|
|
host_or_mac, host_pool_id, lifetime);
|
|
#endif
|
|
|
|
volatile_members_lock[host_pool_id]->unlock(__FILE__, __LINE__);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void HostPools::incPoolNumDroppedFlows(u_int16_t pool_id) {
|
|
HostPoolStats *hps = getPoolStats(pool_id);
|
|
|
|
if(!hps) return;
|
|
|
|
hps->incNumDroppedFlows();
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void HostPools::resetPoolsStats(u_int16_t pool_filter) {
|
|
HostPoolStats *hps;
|
|
|
|
if(stats) {
|
|
if(pool_filter != (u_int16_t)-1) {
|
|
if((hps = getPoolStats(pool_filter)))
|
|
hps->resetStats();
|
|
} else {
|
|
for(int i = 0; i < MAX_NUM_HOST_POOLS; i++) {
|
|
if((hps = stats[i])) {
|
|
/* Must use the assigned hps as stats can be swapped
|
|
and accesses such as stats[i] could yield a NULL value */
|
|
hps->resetStats();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void HostPools::checkPoolsStatsReset() {
|
|
HostPoolStats *hps;
|
|
|
|
if(stats) {
|
|
for(int i = 0; i < MAX_NUM_HOST_POOLS; i++) {
|
|
if((hps = stats[i])) {
|
|
/* Must use the assigned hps as stats can be swapped
|
|
and accesses such as stats[i] could yield a NULL value */
|
|
hps->checkStatsReset();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void HostPools::luaVolatileMembers(lua_State *vm) {
|
|
volatile_members_t *current, *tmp;
|
|
int i;
|
|
time_t now = time(NULL);
|
|
|
|
if(!vm)
|
|
return;
|
|
|
|
lua_newtable(vm);
|
|
|
|
for(int pool_id = 0; pool_id < MAX_NUM_HOST_POOLS; pool_id++) {
|
|
|
|
if(!volatile_members[pool_id])
|
|
continue;
|
|
|
|
volatile_members_lock[pool_id]->lock(__FILE__, __LINE__);
|
|
|
|
if(stats && stats[pool_id]) { /* The pool exists */
|
|
lua_newtable(vm);
|
|
|
|
i = 0;
|
|
HASH_ITER(hh, volatile_members[pool_id], current, tmp) {
|
|
lua_newtable(vm);
|
|
|
|
lua_push_str_table_entry(vm, "member", current->host_or_mac);
|
|
lua_push_float_table_entry(vm, "residual_lifetime", current->lifetime - now);
|
|
lua_push_bool_table_entry(vm, "expired", current->lifetime - now < 0);
|
|
|
|
lua_rawseti(vm, -2, ++i);
|
|
}
|
|
|
|
lua_rawseti(vm, -2, pool_id);
|
|
}
|
|
|
|
volatile_members_lock[pool_id]->unlock(__FILE__, __LINE__);
|
|
}
|
|
};
|
|
|
|
/* *************************************** */
|
|
|
|
void HostPools::addToPool(char *host_or_mac,
|
|
u_int16_t user_pool_id,
|
|
int32_t lifetime_secs) {
|
|
char key[128], pool_buf[16];
|
|
|
|
#ifdef HOST_POOLS_DEBUG
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Adding %s as %s host pool member [pool id: %i]",
|
|
host_or_mac,
|
|
lifetime_secs <= 0 ? "PERMANENT" : "VOLATILE",
|
|
user_pool_id);
|
|
#endif
|
|
|
|
if(lifetime_secs > 0)
|
|
addVolatileMember(host_or_mac, user_pool_id, (u_int32_t)lifetime_secs);
|
|
|
|
else {
|
|
snprintf(pool_buf, sizeof(pool_buf), "%u", user_pool_id);
|
|
snprintf(key, sizeof(key), HOST_POOL_MEMBERS_KEY, iface->get_id(), pool_buf);
|
|
ntop->getRedis()->sadd(key, host_or_mac); /* New member added */
|
|
}
|
|
|
|
reloadPools();
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void HostPools::purgeExpiredVolatileMembers() {
|
|
volatile_members_t *current, *tmp;
|
|
bool purged = false;
|
|
time_t now = time(NULL);
|
|
|
|
for(int pool_id = 0; pool_id < MAX_NUM_HOST_POOLS; pool_id++) {
|
|
volatile_members_lock[pool_id]->lock(__FILE__, __LINE__);
|
|
|
|
HASH_ITER(hh, volatile_members[pool_id], current, tmp) {
|
|
#ifdef HOST_POOLS_DEBUG
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Checking VOLATILE MEMBER %s [pool id: %i] for expiration...",
|
|
current->host_or_mac,
|
|
pool_id);
|
|
#endif
|
|
|
|
if(current->lifetime < now) {
|
|
purged = true;
|
|
|
|
#ifdef HOST_POOLS_DEBUG
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Purging expired VOLATILE MEMBER %s [pool id: %i]",
|
|
current->host_or_mac,
|
|
pool_id);
|
|
#endif
|
|
|
|
HASH_DEL(volatile_members[pool_id], current);
|
|
free(current->host_or_mac);
|
|
free(current);
|
|
}
|
|
}
|
|
|
|
volatile_members_lock[pool_id]->unlock(__FILE__, __LINE__);
|
|
}
|
|
|
|
if(purged)
|
|
reloadPools();
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void HostPools::removeVolatileMemberFromPool(char *host_or_mac, u_int16_t user_pool_id) {
|
|
volatile_members_t *m;
|
|
bool purged = false;
|
|
|
|
if(user_pool_id == NO_HOST_POOL_ID || user_pool_id >= MAX_NUM_HOST_POOLS || !host_or_mac)
|
|
return;
|
|
|
|
volatile_members_lock[user_pool_id]->lock(__FILE__, __LINE__);
|
|
|
|
HASH_FIND_STR(volatile_members[user_pool_id], host_or_mac, m);
|
|
if(m) {
|
|
HASH_DEL(volatile_members[user_pool_id], m);
|
|
free(m->host_or_mac);
|
|
free(m);
|
|
purged = true;
|
|
}
|
|
|
|
volatile_members_lock[user_pool_id]->unlock(__FILE__, __LINE__);
|
|
|
|
if(purged)
|
|
reloadPools();
|
|
}
|
|
|
|
#endif
|
|
|
|
/* *************************************** */
|
|
|
|
void HostPools::lua(lua_State *vm) {
|
|
u_int32_t hosts = 0, l2_devices = 0;
|
|
u_int32_t cur_hosts = 0, cur_l2 = 0;
|
|
u_int32_t active_pools = 0;
|
|
char buf[8];
|
|
|
|
lua_newtable(vm);
|
|
|
|
for(int i = 0; i < MAX_NUM_HOST_POOLS; i++) {
|
|
if((cur_hosts = getNumPoolHosts(i))) hosts += cur_hosts;
|
|
if((cur_l2 = getNumPoolL2Devices(i))) l2_devices += cur_l2;
|
|
|
|
if(cur_hosts || cur_l2) {
|
|
lua_newtable(vm);
|
|
lua_push_uint64_table_entry(vm, "num_hosts", cur_hosts);
|
|
lua_push_uint64_table_entry(vm, "num_l2_devices", cur_l2);
|
|
snprintf(buf, sizeof(buf), "%d", i);
|
|
|
|
lua_pushstring(vm, buf);
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
|
|
active_pools++;
|
|
}
|
|
}
|
|
|
|
lua_pushstring(vm, "num_members_per_pool");
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
|
|
lua_newtable(vm);
|
|
lua_push_uint64_table_entry(vm, "num_hosts", hosts);
|
|
lua_push_uint64_table_entry(vm, "num_l2_devices", l2_devices);
|
|
lua_push_uint64_table_entry(vm, "num_active_pools", active_pools);
|
|
|
|
lua_pushstring(vm, "num_members");
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void HostPools::reloadPools() {
|
|
char kname[CONST_MAX_LEN_REDIS_KEY];
|
|
char **pools, **pool_members, *at, *member;
|
|
int num_pools, num_members, actual_num_members;
|
|
u_int16_t _pool_id, vlan_id;
|
|
VlanAddressTree *new_tree;
|
|
HostPoolStats **new_stats;
|
|
Redis *redis = ntop->getRedis();
|
|
|
|
if(!iface || (iface->get_id() == -1))
|
|
return;
|
|
|
|
new_tree = new VlanAddressTree;
|
|
new_stats = new HostPoolStats*[MAX_NUM_HOST_POOLS];
|
|
for(u_int32_t i = 0; i < MAX_NUM_HOST_POOLS; i++)
|
|
new_stats[i] = NULL;
|
|
|
|
snprintf(kname, sizeof(kname), HOST_POOL_IDS_KEY, iface->get_id());
|
|
|
|
/* Always allocate default pool stats */
|
|
if(stats && stats[0]) /* Duplicate existing statistics */
|
|
new_stats[0] = new HostPoolStats(*stats[0]);
|
|
else /* Brand new statistics */
|
|
new_stats[0] = new HostPoolStats(iface);
|
|
|
|
/* Keys are pool ids */
|
|
num_pools = redis->smembers(kname, &pools);
|
|
|
|
for(int i = 0; i < num_pools; i++) {
|
|
if(!pools[i])
|
|
continue;
|
|
|
|
_pool_id = (u_int16_t)atoi(pools[i]);
|
|
if(_pool_id >= MAX_NUM_HOST_POOLS) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Ignoring pool [pool id: %2d]. "
|
|
"Maximum number of host pools for this license is %u, inclusive of the Not Assigned pool.",
|
|
_pool_id, MAX_NUM_HOST_POOLS);
|
|
|
|
free(pools[i]);
|
|
continue;
|
|
}
|
|
|
|
if(_pool_id != 0) { /* Pool id 0 stats already updated */
|
|
if(stats && stats[_pool_id]) /* Duplicate existing statistics */
|
|
new_stats[_pool_id] = new HostPoolStats(*stats[_pool_id]);
|
|
else /* Brand new statistics */
|
|
new_stats[_pool_id] = new HostPoolStats(iface);
|
|
}
|
|
|
|
snprintf(kname, sizeof(kname), HOST_POOL_DETAILS_KEY, iface->get_id(), _pool_id);
|
|
|
|
#ifdef NTOPNG_PRO
|
|
char rsp[16] = { 0 };
|
|
|
|
children_safe[_pool_id] = ((redis->hashGet(kname, (char*)CONST_CHILDREN_SAFE, rsp, sizeof(rsp)) != -1)
|
|
&& (!strcmp(rsp, "true")));
|
|
|
|
forge_global_dns[_pool_id] = ((redis->hashGet(kname, (char*)CONST_FORGE_GLOBAL_DNS, rsp, sizeof(rsp)) != -1)
|
|
&& (!strcmp(rsp, "true")));
|
|
|
|
routing_policy_id[_pool_id] = (redis->hashGet(kname, (char*)CONST_ROUTING_POLICY_ID, rsp, sizeof(rsp)) != -1) ? atoi(rsp) : DEFAULT_ROUTING_TABLE_ID;
|
|
pool_shaper[_pool_id] = (redis->hashGet(kname, (char*)CONST_POOL_SHAPER_ID, rsp, sizeof(rsp)) != -1) ? atoi(rsp) : DEFAULT_SHAPER_ID;
|
|
schedule_bitmap[_pool_id] = (redis->hashGet(kname, (char*)CONST_SCHEDULE_BITMAP, rsp, sizeof(rsp)) != -1) ? strtol(rsp, NULL, 16) : DEFAULT_TIME_SCHEDULE;
|
|
|
|
enforce_quotas_per_pool_member[_pool_id] = ((redis->hashGet(kname, (char*)CONST_ENFORCE_QUOTAS_PER_POOL_MEMBER, rsp, sizeof(rsp)) != -1)
|
|
&& (!strcmp(rsp, "true")));;
|
|
enforce_shapers_per_pool_member[_pool_id] = ((redis->hashGet(kname, (char*)CONST_ENFORCE_SHAPERS_PER_POOL_MEMBER, rsp, sizeof(rsp)) != -1)
|
|
&& (!strcmp(rsp, "true")));;
|
|
|
|
#ifdef HOST_POOLS_DEBUG
|
|
redis->hashGet(kname, (char*)"name", rsp, sizeof(rsp));
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Loading pool [iteration: %u][pool_id: %u][name: %s]"
|
|
"[children_safe: %i]"
|
|
"[forge_global_dns: %i]"
|
|
"[pool_shaper: %i]"
|
|
"[schedule_bitmap: %i]"
|
|
"[enforce_quotas_per_pool_member: %i]"
|
|
"[enforce_shapers_per_pool_member: %i]",
|
|
i, _pool_id,
|
|
rsp, children_safe[_pool_id], forge_global_dns[_pool_id],
|
|
pool_shaper[_pool_id], schedule_bitmap[_pool_id],
|
|
enforce_quotas_per_pool_member[_pool_id],
|
|
enforce_shapers_per_pool_member[_pool_id]);
|
|
#endif
|
|
|
|
#endif /* NTOPNG_PRO */
|
|
|
|
snprintf(kname, sizeof(kname), HOST_POOL_MEMBERS_KEY, iface->get_id(), pools[i]);
|
|
|
|
/* Pool members are the elements of the list */
|
|
if((num_members = redis->smembers(kname, &pool_members)) > 0) {
|
|
// NOTE: the auto-assigned host_pool must not be limited as it receives devices assigments automatically
|
|
actual_num_members = min_val((u_int32_t)num_members, ((_pool_id == ntop->getPrefs()->get_auto_assigned_pool_id()) ? MAX_NUM_INTERFACE_HOSTS : MAX_NUM_POOL_MEMBERS));
|
|
|
|
if(actual_num_members < num_members) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Too many members [pool id: %2d][pool members: %d]. "
|
|
"Maximum number of pool members for this license is %u, so %u pool members will be ignored.",
|
|
_pool_id, num_members, actual_num_members, num_members - actual_num_members, actual_num_members);
|
|
}
|
|
|
|
for(int k = 0; k < actual_num_members; k++) {
|
|
member = pool_members[k];
|
|
|
|
if(!member) continue;
|
|
|
|
if((at = strchr(member, '@'))) {
|
|
vlan_id = atoi(at + 1);
|
|
*at = '\0';
|
|
} else
|
|
vlan_id = 0;
|
|
|
|
bool rc;
|
|
|
|
if(!(rc = new_tree->addAddress(vlan_id, member, _pool_id))
|
|
#ifdef HOST_POOLS_DEBUG
|
|
|| true
|
|
#endif
|
|
)
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"%s tree node for %s [vlan %i] [host pool: %s]",
|
|
rc ? "Successfully added" : "Unable to add",
|
|
member, vlan_id,
|
|
pools[i]);
|
|
|
|
free(member);
|
|
}
|
|
|
|
free(pool_members);
|
|
}
|
|
|
|
free(pools[i]);
|
|
}
|
|
|
|
if(pools) free(pools);
|
|
|
|
#ifdef NTOPNG_PRO
|
|
if(ntop->getPrefs()->isCaptivePortalEnabled())
|
|
reloadVolatileMembers(new_tree /* Reload only on the new */);
|
|
#endif
|
|
swap(new_tree, new_stats);
|
|
|
|
iface->refreshHostPools();
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
bool HostPools::findMacPool(const u_int8_t * const mac, u_int16_t *found_pool) {
|
|
VlanAddressTree *cur_tree; /* must use this as tree can be swapped */
|
|
int16_t ret;
|
|
|
|
if(!tree || !(cur_tree = tree))
|
|
return(false);
|
|
|
|
ret = cur_tree->findMac(0, mac);
|
|
|
|
if(ret != -1) {
|
|
*found_pool = (u_int16_t)ret;
|
|
return(true);
|
|
}
|
|
|
|
return(false);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
bool HostPools::findMacPool(Mac *mac, u_int16_t *found_pool) {
|
|
if(mac->isSpecialMac())
|
|
return(false);
|
|
|
|
return findMacPool(mac->get_mac(), found_pool);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
bool HostPools::findIpPool(IpAddress *ip, u_int16_t vlan_id, u_int16_t *found_pool, patricia_node_t **found_node) {
|
|
VlanAddressTree *cur_tree; /* must use this as tree can be swapped */
|
|
#ifdef HOST_POOLS_DEBUG
|
|
char buf[128];
|
|
#endif
|
|
|
|
if(!tree || !(cur_tree = tree))
|
|
return(false);
|
|
|
|
*found_node = (patricia_node_t*)ip->findAddress(cur_tree->getAddressTree(vlan_id));
|
|
|
|
if(*found_node) {
|
|
#ifdef HOST_POOLS_DEBUG
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Found pool for %s [pool id: %i]",
|
|
ip->print(buf, sizeof(buf)), (*found_node)->user_data);
|
|
#endif
|
|
*found_pool = (*found_node)->user_data;
|
|
return(true);
|
|
}
|
|
|
|
return(false);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
u_int16_t HostPools::getPool(Host *h) {
|
|
u_int16_t pool_id;
|
|
patricia_node_t *node;
|
|
bool found = false;
|
|
|
|
if(h) {
|
|
if(h->getMac())
|
|
found = findMacPool(h->getMac(), &pool_id);
|
|
|
|
if(!found && h->get_ip()) {
|
|
found = findIpPool(h->get_ip(), h->get_vlan_id(), &pool_id, &node);
|
|
}
|
|
}
|
|
|
|
if(!found)
|
|
return NO_HOST_POOL_ID;
|
|
|
|
return pool_id;
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
u_int16_t HostPools::getPool(Mac *m) {
|
|
u_int16_t pool_id;
|
|
bool found = false;
|
|
|
|
if(m)
|
|
found = findMacPool(m, &pool_id);
|
|
|
|
if(!found)
|
|
return NO_HOST_POOL_ID;
|
|
|
|
return pool_id;
|
|
}
|