ntopng/src/AddressTree.cpp

671 lines
18 KiB
C++

/*
*
* (C) 2013-25 - 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"
/* **************************************** */
static void free_ptree_data(void *data) {
if (data) free(data);
}
/* **************************************** */
AddressTree::AddressTree(bool handleIPv6, ndpi_void_fn_t data_free_func) {
if(trace_new_delete) ntop->getTrace()->traceEvent(TRACE_NORMAL, "[new] %s", __FILE__);
if (data_free_func)
free_func = data_free_func;
else
free_func = free_ptree_data;
init(handleIPv6);
}
/* **************************************** */
AddressTree::AddressTree(const AddressTree &at, ndpi_void_fn_t data_free_func) {
if (data_free_func)
free_func = data_free_func;
else
free_func = free_ptree_data;
ptree_v4 = ndpi_patricia_clone(at.ptree_v4);
if (at.ptree_v6)
ptree_v6 = ndpi_patricia_clone(at.ptree_v6);
else
ptree_v6 = NULL;
macs = at.macs;
numAddresses = at.numAddresses;
numAddressesIPv4 = at.numAddressesIPv4;
numAddressesIPv6 = at.numAddressesIPv6;
}
/* **************************************** */
void AddressTree::init(bool handleIPv6) {
numAddresses = numAddressesIPv4 = numAddressesIPv6 = 0;
ptree_v4 = ndpi_patricia_new(32), macs.clear();
if (handleIPv6)
ptree_v6 = ndpi_patricia_new(128);
else
ptree_v6 = NULL;
}
/* **************************************** */
AddressTree::~AddressTree() {
if(trace_new_delete) ntop->getTrace()->traceEvent(TRACE_NORMAL, "[delete] %s", __FILE__);
cleanup();
}
/* ******************************************* */
ndpi_patricia_node_t *AddressTree::addAddress(const IpAddress *const ipa) {
if (!ipa) return NULL;
bool is_v4 = ipa->isIPv4();
if ((!is_v4) && (!ptree_v6))
return (NULL);
else {
ndpi_patricia_tree_t *cur_ptree = is_v4 ? ptree_v4 : ptree_v6;
int cur_family = is_v4 ? AF_INET : AF_INET6;
int cur_bits = is_v4 ? 32 : 128;
void *cur_addr = is_v4 ? (void *)&ipa->getIP()->ipType.ipv4
: (void *)&ipa->getIP()->ipType.ipv6;
ndpi_patricia_node_t *res;
updateLock.wrlock(__FILE__, __LINE__);
res = Utils::ptree_match(cur_ptree, cur_family, cur_addr, cur_bits);
if (!res) {
res = Utils::add_to_ptree(cur_ptree, cur_family, cur_addr, cur_bits);
if (res) {
numAddresses++;
if (is_v4)
numAddressesIPv4++;
else
numAddressesIPv6++;
}
}
updateLock.unlock(__FILE__, __LINE__);
return res;
}
}
/* ******************************************* */
typedef struct {
int cur_bitlen;
vector<ndpi_prefix_t *> larger_bitlens;
} compact_tree_t;
/* ******************************************* */
static void compact_tree_funct(ndpi_patricia_node_t *node, void *data,
void *user_data) {
ndpi_prefix_t *prefix;
compact_tree_t *compact = (compact_tree_t *)user_data;
if (!node || !(prefix = ndpi_patricia_get_node_prefix(node))) return;
if (prefix->bitlen > compact->cur_bitlen)
compact->larger_bitlens.push_back(prefix);
}
/* **************************************************** */
ndpi_patricia_node_t *AddressTree::addAddress(const IpAddress *const ipa,
int network_bits,
bool compact_after_add) {
if (!ipa) return NULL;
bool is_v4 = ipa->isIPv4();
if ((!is_v4) && (!ptree_v6))
return (NULL);
else {
ndpi_patricia_node_t *res;
ndpi_patricia_tree_t *cur_ptree = is_v4 ? ptree_v4 : ptree_v6;
int cur_family = is_v4 ? AF_INET : AF_INET6;
int cur_bits = network_bits;
if (network_bits < 0)
network_bits = 0;
else if (is_v4 && network_bits > 32)
network_bits = 32;
else if (!is_v4 && network_bits > 128)
network_bits = 128;
void *cur_addr = is_v4 ? (void *)&ipa->getIP()->ipType.ipv4
: (void *)&ipa->getIP()->ipType.ipv6;
updateLock.wrlock(__FILE__, __LINE__);
res = Utils::ptree_match(cur_ptree, cur_family, cur_addr, cur_bits);
if (!res) {
res = Utils::add_to_ptree(cur_ptree, cur_family, cur_addr, cur_bits);
if (compact_after_add && res) {
compact_tree_t compact;
compact.cur_bitlen = network_bits;
/* navigate this subtree */
ndpi_patricia_walk_inorder(res, compact_tree_funct, &compact);
for (std::vector<ndpi_prefix_t *>::const_iterator it =
compact.larger_bitlens.begin();
it != compact.larger_bitlens.end(); ++it)
removePrefix(is_v4, *it);
}
}
updateLock.unlock(__FILE__, __LINE__);
return res;
}
}
/* ******************************************* */
bool AddressTree::addAddressAndData(const char *_what, void *user_data,
bool fail_if_already_present) {
ndpi_patricia_node_t *node;
bool ret;
if (fail_if_already_present && match((char *) _what)) {
return false;
}
updateLock.wrlock(__FILE__, __LINE__);
node = Utils::ptree_add_rule(strchr(_what, '.') ? ptree_v4 : ptree_v6, _what);
if (node) {
ndpi_patricia_set_node_data(node, user_data), ret = true;
numAddresses++;
} else
ret = false;
updateLock.unlock(__FILE__, __LINE__);
return(ret);
}
/* ******************************************* */
bool AddressTree::addAddress(const char *_what, const int64_t user_data) {
u_int32_t _mac[6];
int64_t id = (user_data == -1) ? numAddresses : user_data;
bool ret = true;
if (sscanf(_what, "%02X:%02X:%02X:%02X:%02X:%02X", &_mac[0], &_mac[1],
&_mac[2], &_mac[3], &_mac[4], &_mac[5]) == 6) {
u_int8_t mac[6];
u_int64_t mac_num;
for (int i = 0; i < 6; i++) mac[i] = (u_int8_t)_mac[i];
mac_num = Utils::mac2int(mac);
macs[mac_num] = id;
} else {
ndpi_patricia_node_t *node;
updateLock.wrlock(__FILE__, __LINE__);
node = Utils::ptree_add_rule(strchr(_what, '.') ? ptree_v4 : ptree_v6, _what);
if (node) {
ndpi_patricia_set_node_u64(node, id);
numAddresses++;
ret = true;
} else
ret = false;
updateLock.unlock(__FILE__, __LINE__);
}
return(ret);
}
/* ******************************************* */
/* Format: 131.114.21.0/24,10.0.0.0/255.0.0.0
* Return true if all provided addresses are added successfully, false if none
* or partial */
bool AddressTree::addAddresses(const char *rule, const int64_t user_data) {
char *tmp, *net;
char *_rule = strdup(rule);
bool rc;
if (!_rule) return false;
net = strtok_r(_rule, ",", &tmp);
rc = true;
while (net != NULL) {
if (!addAddress(net, user_data)) rc = false;
net = strtok_r(NULL, ",", &tmp);
}
free(_rule);
return rc;
}
/* ******************************************* */
// TODO match MAC
ndpi_patricia_node_t *AddressTree::matchAndGetNode(const char *addr) {
ndpi_patricia_node_t *node = NULL;
char addr_cpy[48];
IpAddress address;
char *net_prefix;
int bits;
strncpy(addr_cpy, addr, sizeof(addr_cpy) - 1);
net_prefix = strchr(addr_cpy, '/');
if (net_prefix) {
*net_prefix = '\0';
address.set(addr_cpy);
bits = atoi(net_prefix + 1);
} else {
address.set(addr);
bits = address.isIPv4() ? 32 : 128;
}
updateLock.rdlock(__FILE__, __LINE__);
if (address.isIPv4())
node = Utils::ptree_match(ptree_v4, AF_INET, &address.getIP()->ipType.ipv4,
bits);
else
node = Utils::ptree_match(ptree_v6, AF_INET6,
(void *)&address.getIP()->ipType.ipv6, bits);
updateLock.unlock(__FILE__, __LINE__);
return node;
}
/* ******************************************* */
void *AddressTree::matchAndGetData(const char *addr) {
ndpi_patricia_node_t *node = matchAndGetNode(addr);
if (node)
return ndpi_patricia_get_node_data(node);
else
return NULL;
}
/* ******************************************* */
bool AddressTree::match(char *addr) { return !!matchAndGetNode(addr); }
/* ******************************************* */
ndpi_patricia_node_t* AddressTree::match(IpAddress *ipa, int network_bits) {
ndpi_patricia_node_t *rc;
if (!ipa) return (NULL);
bool is_v4 = ipa->isIPv4();
if (!is_v4 && !ptree_v6) return (NULL);
updateLock.rdlock(__FILE__, __LINE__);
if (is_v4)
rc = Utils::ptree_match(ptree_v4, AF_INET, &ipa->getIP()->ipType.ipv4,
network_bits);
else
rc = Utils::ptree_match(ptree_v6, AF_INET6, &ipa->getIP()->ipType.ipv6,
network_bits);
updateLock.unlock(__FILE__, __LINE__);
return(rc);
}
/* ******************************************* */
void *AddressTree::matchAndGetData(IpAddress *ipa) {
ndpi_patricia_node_t *node = match(ipa, ipa->isIPv4() ? 32 : 128);
if (node)
return ndpi_patricia_get_node_data(node);
else
return NULL;
}
/* ******************************************* */
/*
NOTE: this does NOT accept a char* address! Use AddressTree::find() instead.
*/
int64_t AddressTree::findAddress(int family, void *addr,
u_int8_t *network_mask_bits) {
ndpi_patricia_tree_t *p;
int bits;
ndpi_patricia_node_t *node;
if (family == AF_INET)
p = ptree_v4, bits = 32;
else if (family == AF_INET6)
p = ptree_v6, bits = 128;
else
return (-1);
if (p == NULL) return (-1);
node = Utils::ptree_match(p, family, addr, bits);
if (node == NULL)
return (-1);
else {
if (network_mask_bits)
*network_mask_bits = ndpi_patricia_get_node_bits(node);
return (ndpi_patricia_get_node_u64(node));
}
}
/* ******************************************* */
int64_t AddressTree::findMac(const u_int8_t addr[]) {
std::map<u_int64_t, int64_t>::iterator it;
u_int64_t mac_num = Utils::mac2int((u_int8_t *)addr);
it = macs.find(mac_num);
if (it != macs.end()) return (it->second);
return (-1);
}
/* **************************************************** */
/* Generic find with IPv4/IPv6/Mac */
int64_t AddressTree::find(const char *addr, u_int8_t *network_mask_bits) {
u_int8_t mac[6];
u_int32_t _mac[6];
if (strchr(addr, '.')) {
/* IPv4 */
struct in_addr addr4;
if (inet_pton(AF_INET, addr, &addr4) != 1) return (-1);
return (findAddress(AF_INET, &addr4, network_mask_bits));
} else if (sscanf(addr, "%02X:%02X:%02X:%02X:%02X:%02X", &_mac[0], &_mac[1],
&_mac[2], &_mac[3], &_mac[4], &_mac[5]) == 6) {
/* MAC address */
for (int i = 0; i < 6; i++) mac[i] = _mac[i];
return (findMac(mac));
} else {
/* IPv6 */
struct in6_addr addr6;
if (inet_pton(AF_INET6, addr, &addr6) != 1) return (-1);
return (findAddress(AF_INET6, &addr6, network_mask_bits));
}
}
/* **************************************************** */
static void address_tree_dump_funct(ndpi_patricia_node_t *node, void *data,
void *user_data) {
char address[128];
ndpi_prefix_t *prefix;
if (!node || !(prefix = ndpi_patricia_get_node_prefix(node))) return;
if (!Utils::ptree_prefix_print(prefix, address, sizeof(address))) return;
if (user_data)
lua_push_uint64_table_entry((lua_State *)user_data, address,
ndpi_patricia_get_node_u64(node));
else
ntop->getTrace()->traceEvent(TRACE_NORMAL, "[AddressTree] %s", address);
}
/* **************************************************** */
static void address_tree_serialize_funct(ndpi_patricia_node_t *node, void *data,
void *user_data) {
char address[128];
ndpi_prefix_t *prefix;
if (!node || !(prefix = ndpi_patricia_get_node_prefix(node))) return;
if (!Utils::ptree_prefix_print(prefix, address, sizeof(address))) return;
if (user_data)
ndpi_serialize_string_uint64((ndpi_serializer*)user_data, address, ndpi_patricia_get_node_u64(node));
else
ntop->getTrace()->traceEvent(TRACE_NORMAL, "[AddressTree] %s", address);
}
/* **************************************************** */
void AddressTree::getAddresses(lua_State *vm) {
std::map<u_int64_t, int64_t>::const_iterator it;
updateLock.rdlock(__FILE__, __LINE__);
ndpi_patricia_walk_tree_inorder(ptree_v4, address_tree_dump_funct, vm);
if (ptree_v6)
ndpi_patricia_walk_tree_inorder(ptree_v6, address_tree_dump_funct, vm);
for (it = macs.begin(); it != macs.end(); ++it) {
char key[32], val[8];
u_int8_t *mac = (u_int8_t *)&it->first;
snprintf(key, sizeof(key), "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1],
mac[2], mac[3], mac[4], mac[5]);
snprintf(val, sizeof(val), "%llu", (long long unsigned int)it->second);
lua_push_str_table_entry(vm, key, val);
}
updateLock.unlock(__FILE__, __LINE__);
}
/* **************************************************** */
void AddressTree::removePrefix(bool isV4, ndpi_prefix_t *prefix) {
if (removePrefix(getTree(isV4), prefix)) {
numAddresses--;
if (isV4)
numAddressesIPv4--;
else
numAddressesIPv6--;
}
}
/* **************************************************** */
bool AddressTree::removePrefix(ndpi_patricia_tree_t *ptree,
ndpi_prefix_t *prefix) {
if (!ptree || !prefix) return false;
ndpi_patricia_node_t *candidate = ndpi_patricia_search_exact(ptree, prefix);
if (!candidate) return false;
ndpi_patricia_remove(ptree, candidate);
return true;
}
/* **************************************************** */
void AddressTree::walk(ndpi_patricia_tree_t *ptree, ndpi_void_fn3_t func,
void *const user_data) {
if (ptree) ndpi_patricia_walk_tree_inorder(ptree, func, user_data);
}
/* **************************************************** */
void AddressTree::walk(ndpi_void_fn3_t func, void *const user_data) {
updateLock.rdlock(__FILE__, __LINE__);
walk(ptree_v4, func, user_data);
walk(ptree_v6, func, user_data);
updateLock.unlock(__FILE__, __LINE__);
}
/* **************************************************** */
void AddressTree::dump() {
std::map<u_int64_t, int64_t>::iterator it;
updateLock.rdlock(__FILE__, __LINE__);
ndpi_patricia_walk_tree_inorder(ptree_v4, address_tree_dump_funct, NULL);
if (ptree_v6)
ndpi_patricia_walk_tree_inorder(ptree_v6, address_tree_dump_funct, NULL);
for (it = macs.begin(); it != macs.end(); ++it) {
char key[32];
u_int8_t *mac = (u_int8_t *)&it->first;
snprintf(key, sizeof(key), "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1],
mac[2], mac[3], mac[4], mac[5]);
ntop->getTrace()->traceEvent(TRACE_NORMAL, "[AddressTree] %s", key);
}
updateLock.unlock(__FILE__, __LINE__);
}
/* **************************************************** */
void AddressTree::cleanup(ndpi_void_fn_t _free_func) {
if (ptree_v4) {
ndpi_patricia_destroy(ptree_v4, _free_func);
ptree_v4 = NULL;
}
if (ptree_v6) {
ndpi_patricia_destroy(ptree_v6, _free_func);
ptree_v6 = NULL;
}
macs.clear();
}
/* **************************************************** */
void AddressTree::cleanup() { cleanup(free_func); }
/* **************************************************** */
char* AddressTree::serialize(char *buf, u_int buf_len) {
ndpi_serializer serializer;
u_int32_t buflen;
char *json;
if(ndpi_init_serializer(&serializer, ndpi_serialization_format_json) < 0)
return(NULL);
updateLock.rdlock(__FILE__, __LINE__);
ndpi_serialize_start_of_block(&serializer, "ip");
ndpi_patricia_walk_tree_inorder(ptree_v4, address_tree_serialize_funct, &serializer);
if (ptree_v6)
ndpi_patricia_walk_tree_inorder(ptree_v6, address_tree_serialize_funct, &serializer);
ndpi_serialize_end_of_block(&serializer);
ndpi_serialize_start_of_list(&serializer, "mac");
for (std::map<u_int64_t, int64_t>::iterator it = macs.begin(); it != macs.end(); ++it) {
char key[32], val[8];
u_int8_t *mac = (u_int8_t *)&it->first;
snprintf(key, sizeof(key), "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1],
mac[2], mac[3], mac[4], mac[5]);
snprintf(val, sizeof(val), "%llu", (long long unsigned int)it->second);
ndpi_serialize_string_string(&serializer, "mac", val);
}
ndpi_serialize_end_of_list(&serializer);
updateLock.unlock(__FILE__, __LINE__);
json = ndpi_serializer_get_buffer(&serializer, &buflen);
buf_len = ndpi_min(buf_len-1, buflen);
strncpy(buf, json, buf_len);
buf[buf_len] = '\0';
ndpi_term_serializer(&serializer);
return(buf);
}
/* **************************************************** */
bool AddressTree:: deserialize(char *json) {
json_object *o, *obj;
enum json_tokener_error jerr = json_tokener_success;
if (!json) return(false);
if((o = json_tokener_parse_verbose(json, &jerr)) == NULL)
return(false);
cleanup();
if(json_object_object_get_ex(o, "ip", &obj)) {
json_object_object_foreach(obj, key, val) {
ntop->getTrace()->traceEvent(TRACE_INFO, "Network: %s, Value: %d\n", key, json_object_get_int(val));
addAddress(key, json_object_get_int(val));
}
}
if(json_object_object_get_ex(o, "mac", &obj)) {
int array_len = json_object_array_length(obj);
if (array_len > 0) {
for (int i = 0; i < array_len; i++) {
json_object *mac_item = json_object_array_get_idx(obj, i);
addAddress(json_object_get_string(mac_item), -1);
}
}
}
json_object_put(o);
return(true);
}