Coverage Report

Created: 2026-07-25 06:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ntopng/src/LocalHostStats.cpp
Line
Count
Source
1
/*
2
 *
3
 * (C) 2013-26 - ntop.org
4
 *
5
 *
6
 * This program is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software Foundation,
18
 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
 *
20
 */
21
22
#include "ntop_includes.h"
23
24
/* *************************************** */
25
26
10.9k
LocalHostStats::LocalHostStats(Host* _host) : HostStats(_host) {
27
10.9k
  if (trace_new_delete)
28
0
    ntop->getTrace()->traceEvent(TRACE_NORMAL, "[new] %s", __FILE__);
29
30
10.9k
  top_sites = new (std::nothrow) MostVisitedList(HOST_SITES_TOP_NUMBER);
31
32
10.9k
  dns = new (std::nothrow) DnsStats();
33
10.9k
  http = new (std::nothrow) HTTPstats(_host);
34
10.9k
  icmp = new (std::nothrow) ICMPstats();
35
10.9k
  peers = new (std::nothrow)
36
10.9k
      PeerStats(MAX_DYNAMIC_STATS_VALUES /* 10 as default */);
37
38
10.9k
  init();
39
10.9k
}
40
41
/* *************************************** */
42
43
10.9k
LocalHostStats::LocalHostStats(LocalHostStats& s) : HostStats(s) {
44
10.9k
  top_sites = new (std::nothrow) MostVisitedList(HOST_SITES_TOP_NUMBER);
45
10.9k
  peers = new (std::nothrow)
46
10.9k
      PeerStats(MAX_DYNAMIC_STATS_VALUES /* 10 as default */);
47
10.9k
  dns = s.getDNSstats() ? new (std::nothrow) DnsStats(*s.getDNSstats()) : NULL;
48
10.9k
  http = NULL;
49
10.9k
  icmp = NULL;
50
51
10.9k
  init();
52
10.9k
}
53
54
/* *************************************** */
55
56
21.8k
LocalHostStats::~LocalHostStats() {
57
21.8k
  if (top_sites) delete top_sites;
58
21.8k
  if (dns) delete dns;
59
21.8k
  if (http) delete http;
60
21.8k
  if (icmp) delete icmp;
61
21.8k
  if (peers) delete (peers);
62
21.8k
}
63
64
/* *************************************** */
65
66
21.8k
void LocalHostStats::init() {
67
21.8k
  nextPeriodicUpdate = 0;
68
21.8k
  num_contacts_as_cli = num_contacts_as_srv = num_reset_flow = 0;
69
70
21.8k
  num_contacted_hosts_as_client.init(8);    /* 128 bytes */
71
21.8k
  num_host_contacts_as_server.init(8);      /* 128 bytes */
72
21.8k
  num_contacted_services_as_client.init(8); /* 128 bytes */
73
21.8k
  num_contacted_hosts.init(4);              /* 16 bytes  */
74
21.8k
  num_contacted_countries.init(4);          /* 16 bytes  */
75
21.8k
  contacts_as_cli.init(4);                  /* 16 bytes  */
76
21.8k
  contacts_as_srv.init(4);                  /* 16 bytes  */
77
78
21.8k
  num_dns_servers.init(5);
79
21.8k
  num_smtp_servers.init(5);
80
21.8k
  num_ntp_servers.init(5);
81
21.8k
  num_imap_servers.init(5);
82
21.8k
  num_pop_servers.init(5);
83
21.8k
  num_contacted_domain_names.init(4);
84
21.8k
}
85
86
/* *************************************** */
87
88
868
void LocalHostStats::incrVisitedWebSite(char* hostname) {
89
868
  u_int ip4_0 = 0, ip4_1 = 0, ip4_2 = 0, ip4_3 = 0;
90
868
  char *firstdot = NULL, *nextdot = NULL;
91
92
868
  if ((strstr(hostname, "in-addr.arpa") == NULL) &&
93
849
      (sscanf(hostname, "%u.%u.%u.%u", &ip4_0, &ip4_1, &ip4_2, &ip4_3) != 4)) {
94
848
    incContactedHosts(hostname);
95
96
    /* Top Sites update, done only if the preference is enabled */
97
848
    if (top_sites && ntop->getPrefs()->are_top_talkers_enabled()) {
98
0
      if (ntop->isATrackerHost(hostname)) {
99
0
        ntop->getTrace()->traceEvent(TRACE_INFO, "[TRACKER] %s", hostname);
100
0
        return; /* Ignore trackers */
101
0
      }
102
103
0
      firstdot = strchr(hostname, '.');
104
105
0
      if (firstdot) nextdot = strchr(&firstdot[1], '.');
106
107
0
      top_sites->incrVisitedData(nextdot ? &firstdot[1] : hostname, 1);
108
0
      host->getInterface()->incrVisitedWebSite(nextdot ? &firstdot[1]
109
0
                                                       : hostname);
110
0
    }
111
848
  }
112
868
}
113
114
/* *************************************** */
115
116
13.5k
void LocalHostStats::updateStats(const struct timeval* tv) {
117
13.5k
  HostStats::updateStats(tv);
118
119
13.5k
  if (dns) dns->updateStats(tv);
120
13.5k
  if (icmp) icmp->updateStats(tv);
121
13.5k
  if (http) http->updateStats(tv);
122
123
  /* 5 Min Update */
124
13.5k
  if (tv->tv_sec >= nextPeriodicUpdate) {
125
    /* visited sites update */
126
6.65k
    contacted_hosts.addObservation(getContactedHostsCardinality());
127
6.65k
    resetContactedHosts();
128
129
    /* countries contacts update */
130
6.65k
    resetCountriesContacts();
131
132
    /* Contacted peers update */
133
6.65k
    updateHostContacts();
134
135
6.65k
    if (ntop->getPrefs()->are_top_talkers_enabled()) {
136
0
      if (top_sites && host) {
137
0
        char additional_key_info[128], buf[100];
138
0
        if (!host->get_mac() && !host->get_ip()) return;
139
140
        /* String like `_1.1.1.1@2` */
141
0
        snprintf(additional_key_info, sizeof(additional_key_info), "_%s",
142
0
                 host->get_tskey(buf, sizeof(buf)));
143
144
0
        top_sites->saveOldData(
145
0
            host->getInterface()->get_id(), additional_key_info,
146
0
            (char*)HASHKEY_LOCAL_HOSTS_TOP_SITES_HOUR_KEYS_PUSHED);
147
0
      }
148
0
    }
149
150
6.65k
    nextPeriodicUpdate = tv->tv_sec + HOST_SITES_REFRESH;
151
6.65k
  }
152
13.5k
}
153
154
/* *************************************** */
155
156
6.65k
void LocalHostStats::updateHostContacts() {
157
6.65k
  num_contacts_as_cli = contacts_as_cli.getEstimate(),
158
6.65k
  num_contacts_as_srv = contacts_as_srv.getEstimate();
159
6.65k
  if (peers) {
160
6.65k
    peers->addElement(num_contacts_as_cli, true);
161
6.65k
    peers->addElement(num_contacts_as_srv, false);
162
6.65k
  }
163
6.65k
  contacts_as_cli.reset(), contacts_as_srv.reset();
164
6.65k
}
165
166
/* *************************************** */
167
168
void LocalHostStats::getJSONObject(json_object* my_object,
169
0
                                   DetailsLevel details_level) {
170
0
  HostStats::getJSONObject(my_object, details_level);
171
172
0
  if (dns) json_object_object_add(my_object, "dns", dns->getJSONObject());
173
0
  if (http) json_object_object_add(my_object, "http", http->getJSONObject());
174
175
  /* UDP stats */
176
0
  if (udp_sent_unicast)
177
0
    json_object_object_add(my_object, "udpBytesSent.unicast",
178
0
                           json_object_new_int64(udp_sent_unicast));
179
0
  if (udp_sent_non_unicast)
180
0
    json_object_object_add(my_object, "udpBytesSent.non_unicast",
181
0
                           json_object_new_int64(udp_sent_non_unicast));
182
183
0
  addRedisSitesKey();
184
0
}
185
186
/* *************************************** */
187
188
0
void LocalHostStats::luaHostBehaviour(lua_State* vm) {
189
0
  HostStats::luaHostBehaviour(vm);
190
191
0
  lua_newtable(vm);
192
193
0
  lua_push_uint32_table_entry(vm, "value", getContactedHostsCardinality());
194
0
  lua_push_bool_table_entry(vm, "anomaly", contacted_hosts.anomalyFound());
195
0
  lua_push_uint64_table_entry(vm, "lower_bound",
196
0
                              contacted_hosts.getLastLowerBound());
197
0
  lua_push_uint64_table_entry(vm, "upper_bound",
198
0
                              contacted_hosts.getLastUpperBound());
199
200
0
  lua_pushstring(vm, "contacted_hosts_behaviour");
201
0
  lua_insert(vm, -2);
202
0
  lua_settable(vm, -3);
203
0
}
204
205
/* *************************************** */
206
207
void LocalHostStats::lua(lua_State* vm, bool mask_host,
208
0
                         DetailsLevel details_level) {
209
0
  HostStats::lua(vm, mask_host, details_level);
210
211
0
  if ((!mask_host) && top_sites &&
212
0
      ntop->getPrefs()->are_top_talkers_enabled()) {
213
0
    top_sites->lua(vm, (char*)"sites", (char*)"sites.old");
214
0
  }
215
216
0
  luaHostBehaviour(vm);
217
218
0
  if (details_level >= details_high) {
219
0
    luaICMP(vm, host->get_ip()->isIPv4(), true);
220
0
    luaDNS(vm, true);
221
0
    luaHTTP(vm);
222
223
    /* Contacts */
224
0
    lua_newtable(vm);
225
226
0
    lua_push_int32_table_entry(vm, "num_contacted_hosts_as_client",
227
0
                               num_contacted_hosts_as_client.getEstimate());
228
0
    lua_push_int32_table_entry(vm, "num_host_contacts_as_server",
229
0
                               num_host_contacts_as_server.getEstimate());
230
0
    lua_push_int32_table_entry(vm, "num_contacted_services_as_client",
231
0
                               num_contacted_services_as_client.getEstimate());
232
233
0
    lua_pushstring(vm, "cardinality");
234
0
    lua_insert(vm, -2);
235
0
    lua_settable(vm, -3);
236
0
  }
237
0
}
238
239
/* *************************************** */
240
241
0
void LocalHostStats::luaPeers(lua_State* vm) {
242
0
  if (peers) {
243
0
    if (peers->getSlidingWinStatus()) {
244
0
      lua_newtable(vm);
245
246
0
      lua_push_int32_table_entry(vm, "contacted_peers_in_last_5mins_as_cli",
247
0
                                 num_contacts_as_cli);
248
0
      lua_push_int32_table_entry(vm, "contacted_peers_in_last_5mins_as_srv",
249
0
                                 num_contacts_as_srv);
250
0
      lua_push_int32_table_entry(vm, "sliding_avg_peers_as_client",
251
0
                                 peers->getCliSlidingEstimate());
252
0
      lua_push_int32_table_entry(vm, "sliding_avg_peers_as_server",
253
0
                                 peers->getSrvSlidingEstimate());
254
0
      lua_push_int32_table_entry(vm, "tot_avg_peers_as_client",
255
0
                                 peers->getCliTotEstimate());
256
0
      lua_push_int32_table_entry(vm, "tot_avg_peers_as_server",
257
0
                                 peers->getSrvTotEstimate());
258
259
0
      lua_pushstring(vm, "peers");
260
0
      lua_insert(vm, -2);
261
0
      lua_settable(vm, -3);
262
263
0
      return;
264
0
    }
265
0
  }
266
267
0
  lua_pushnil(vm);
268
0
}
269
270
/* *************************************** */
271
272
0
void LocalHostStats::lua_get_timeseries(lua_State* vm) {
273
0
  luaStats(vm, host->getInterface(), true /* host details */,
274
0
           true /* verbose */, true /* tsLua */);
275
276
0
  tcp_packet_stats_sent.lua(vm, "tcpPacketStats.sent");
277
0
  tcp_packet_stats_rcvd.lua(vm, "tcpPacketStats.rcvd");
278
279
0
  if (dns) dns->lua(vm, false /* NOT verbose */);
280
281
0
  if (icmp) {
282
0
    struct ts_icmp_stats icmp_s;
283
0
    icmp->getTsStats(&icmp_s);
284
285
0
    lua_push_uint64_table_entry(vm, "icmp.echo_pkts_sent",
286
0
                                icmp_s.echo_packets_sent);
287
0
    lua_push_uint64_table_entry(vm, "icmp.echo_pkts_rcvd",
288
0
                                icmp_s.echo_packets_rcvd);
289
0
    lua_push_uint64_table_entry(vm, "icmp.echo_reply_pkts_sent",
290
0
                                icmp_s.echo_reply_packets_sent);
291
0
    lua_push_uint64_table_entry(vm, "icmp.echo_reply_pkts_rcvd",
292
0
                                icmp_s.echo_reply_packets_rcvd);
293
0
  }
294
295
0
  luaHostBehaviour(vm);
296
0
}
297
298
/* *************************************** */
299
300
void LocalHostStats::incStats(time_t when, u_int8_t l4_proto, u_int ndpi_proto,
301
                              ndpi_protocol_category_t ndpi_category,
302
                              custom_app_t custom_app, u_int64_t sent_packets,
303
                              u_int64_t sent_bytes,
304
                              u_int64_t sent_goodput_bytes,
305
                              u_int64_t rcvd_packets, u_int64_t rcvd_bytes,
306
                              u_int64_t rcvd_goodput_bytes,
307
18.4k
                              bool peer_is_unicast) {
308
18.4k
  HostStats::incStats(when, l4_proto, ndpi_proto, ndpi_category, custom_app,
309
18.4k
                      sent_packets, sent_bytes, sent_goodput_bytes,
310
18.4k
                      rcvd_packets, rcvd_bytes, rcvd_goodput_bytes,
311
18.4k
                      peer_is_unicast);
312
313
18.4k
  if (l4_proto == IPPROTO_UDP) {
314
4.36k
    if (peer_is_unicast)
315
2.77k
      udp_sent_unicast += sent_bytes;
316
1.59k
    else
317
1.59k
      udp_sent_non_unicast += sent_bytes;
318
4.36k
  }
319
18.4k
}
320
321
/* *************************************** */
322
323
0
void LocalHostStats::removeRedisSitesKey() {
324
0
  char additional_key_info[128];
325
326
0
  if (!host->get_mac() && !host->get_ip()) return;
327
328
  /* String like `_1.1.1.1@2` */
329
0
  snprintf(additional_key_info, sizeof(additional_key_info), "%s_",
330
0
           host->get_tskey(additional_key_info, sizeof(additional_key_info)));
331
  /* Deserializing the info */
332
0
  top_sites->serializeDeserialize(
333
0
      host->getInterface()->get_id(), false, additional_key_info,
334
0
      (char*)HASHKEY_TOP_SITES_SERIALIZATION_KEY,
335
0
      (char*)HASHKEY_LOCAL_HOSTS_TOP_SITES_HOUR_KEYS_PUSHED,
336
0
      (char*)HASHKEY_LOCAL_HOSTS_TOP_SITES_DAY_KEYS_PUSHED);
337
0
}
338
339
/* *************************************** */
340
341
0
void LocalHostStats::addRedisSitesKey() {
342
0
  char additional_key_info[128];
343
344
0
  if (!host->get_mac() && !host->get_ip()) return;
345
346
  /* String like `_1.1.1.1@2` */
347
0
  snprintf(additional_key_info, sizeof(additional_key_info), "%s_",
348
0
           host->get_tskey(additional_key_info, sizeof(additional_key_info)));
349
  /* Serializing the info */
350
0
  top_sites->serializeDeserialize(
351
0
      host->getInterface()->get_id(), true, additional_key_info,
352
0
      (char*)HASHKEY_TOP_SITES_SERIALIZATION_KEY,
353
0
      (char*)HASHKEY_LOCAL_HOSTS_TOP_SITES_HOUR_KEYS_PUSHED,
354
0
      (char*)HASHKEY_LOCAL_HOSTS_TOP_SITES_DAY_KEYS_PUSHED);
355
0
}