mirror of
https://github.com/ntop/ntopng.git
synced 2026-05-06 03:45:26 +00:00
723 lines
21 KiB
C++
723 lines
21 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.
|
|
*
|
|
xo */
|
|
|
|
#include "ntop_includes.h"
|
|
|
|
#ifdef __APPLE__
|
|
#include <uuid/uuid.h>
|
|
#endif
|
|
|
|
//#define DEBUG_POLLING
|
|
|
|
#ifndef HAVE_NEDGE
|
|
|
|
/* **************************************************** */
|
|
|
|
PcapInterface::PcapInterface(const char *name, u_int8_t ifIdx,
|
|
bool _delete_pcap_when_done)
|
|
: NetworkInterface(name) {
|
|
char pcap_error_buffer[PCAP_ERRBUF_SIZE];
|
|
struct stat buf;
|
|
|
|
if(trace_new_delete) ntop->getTrace()->traceEvent(TRACE_NORMAL, "[new] %s", __FILE__);
|
|
delete_pcap_when_done = _delete_pcap_when_done;
|
|
memset(pcap_handle, 0, sizeof(pcap_handle));
|
|
memset(pcap_ifaces, 0, sizeof(pcap_ifaces));
|
|
memset(ifname_indexes, 0, sizeof(ifname_indexes));
|
|
num_ifaces = 0, pcap_list = NULL;
|
|
memset(&last_pcap_stat, 0, sizeof(last_pcap_stat));
|
|
emulate_traffic_directions = false;
|
|
read_pkts_from_pcap_dump = read_pkts_from_pcap_dump_done = false,
|
|
read_from_stdin_pipe = false;
|
|
|
|
firstPktTS.tv_sec = 0;
|
|
pcap_path = NULL;
|
|
iface_datalink[0] = DLT_EN10MB; /* default */
|
|
|
|
if ((stat(name, &buf) == 0) || (name[0] == '-') ||
|
|
!strncmp(name, "stdin", 5)) {
|
|
/*
|
|
The file exists so we need to check if it's a
|
|
text file or a pcap file
|
|
*/
|
|
|
|
if (strcmp(name, "-") == 0 || !strncmp(name, "stdin", 5)) {
|
|
/* stdin */
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Reading packets from stdin...");
|
|
pcap_error_buffer[0] = '\0';
|
|
pcap_handle[0] = pcap_fopen_offline(stdin, pcap_error_buffer);
|
|
iface_datalink[0] = pcap_datalink(pcap_handle[0]);
|
|
read_pkts_from_pcap_dump = false;
|
|
is_traffic_mirrored = true;
|
|
emulate_traffic_directions = true;
|
|
read_from_stdin_pipe = true;
|
|
num_ifaces = 1;
|
|
} else if ((pcap_handle[0] = pcap_open_offline(ifname, pcap_error_buffer)) != NULL) {
|
|
char *slash = strrchr(ifname, '/');
|
|
|
|
pcap_path = strdup(ifname);
|
|
if (slash) {
|
|
char *old = ifname;
|
|
ifname = strdup(&slash[1]);
|
|
free(old);
|
|
}
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Reading packets from pcap file %s...", ifname);
|
|
read_pkts_from_pcap_dump = true,
|
|
purge_idle_flows_hosts = ntop->getPrefs()->purgeHostsFlowsOnPcapFiles();
|
|
iface_datalink[0] = pcap_datalink(pcap_handle[0]);
|
|
num_ifaces = 1;
|
|
} else {
|
|
/* Trying to open a playlist */
|
|
if ((pcap_list = fopen(name, "r")) != NULL) {
|
|
read_pkts_from_pcap_dump = true;
|
|
} else {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Unable to open file %s", name);
|
|
exit(0);
|
|
}
|
|
}
|
|
} else {
|
|
char dev_names[64], *dev;
|
|
|
|
snprintf(dev_names, sizeof(dev_names), "%s", ifname);
|
|
dev = strtok(dev_names, ",");
|
|
|
|
while(dev != NULL) {
|
|
if(num_ifaces == MAX_NUM_PCAP_INTERFACES) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Too many interfaces (%d) in %s: skipping", num_ifaces, ifname);
|
|
break;
|
|
}
|
|
|
|
pcap_handle[num_ifaces] = pcap_open_live(dev, ntop->getGlobals()->getSnaplen(dev),
|
|
ntop->getPrefs()->use_promiscuous(),
|
|
1000 /* 1 sec */, pcap_error_buffer);
|
|
|
|
if(pcap_handle[num_ifaces] != NULL) {
|
|
iface_datalink[num_ifaces] = pcap_datalink(pcap_handle[num_ifaces]);
|
|
ifname_indexes[num_ifaces] = if_nametoindex(dev);
|
|
pcap_ifaces[num_ifaces] = strdup(dev);
|
|
|
|
if(pcap_ifaces[num_ifaces] == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Not enough memory");
|
|
break;
|
|
}
|
|
|
|
/* This is necessay as with multiple comma separated interfaces we need to take the max MTU */
|
|
ifMTU = ndpi_max(ifMTU, Utils::getIfMTU(pcap_ifaces[num_ifaces]));
|
|
|
|
pcap_path = NULL;
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Reading packets from %s [ifId: %d]",
|
|
dev, ifIdx);
|
|
read_pkts_from_pcap_dump = false;
|
|
|
|
Utils::readMac(dev, ifMac);
|
|
|
|
#ifndef WIN32
|
|
if (pcap_setdirection(pcap_handle[num_ifaces], ntop->getPrefs()->getCaptureDirection()) != 0)
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Unable to set packet capture direction");
|
|
|
|
if(!isTrafficMirrored()) {
|
|
if (Utils::readInterfaceStats(dev, &prev_stats_in, &prev_stats_out))
|
|
emulate_traffic_directions = true;
|
|
}
|
|
#endif
|
|
|
|
num_ifaces++;
|
|
} else
|
|
throw errno;
|
|
|
|
dev = strtok(NULL, ",");
|
|
} /* while */
|
|
}
|
|
|
|
set_datalink(iface_datalink[0]);
|
|
|
|
if (read_pkts_from_pcap_dump) {
|
|
/* Used to cleanup data during next ntopng startup */
|
|
char id_str[8];
|
|
snprintf(id_str, sizeof(id_str), "%d", get_id());
|
|
|
|
ntop->getRedis()->hashSet(PCAP_DUMP_INTERFACES_DELETE_HASH, id_str, get_name());
|
|
}
|
|
|
|
if (ntop->getPrefs()->are_ixia_timestamps_enabled())
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING,
|
|
"Hardware timestamps are supported only on PF_RING capture interfaces");
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
PcapInterface::~PcapInterface() {
|
|
for(u_int8_t i=0; i < get_num_ifaces(); i++) {
|
|
if (pcap_handle[i]) {
|
|
pcap_close(pcap_handle[i]);
|
|
pcap_handle[i] = NULL;
|
|
}
|
|
|
|
if (pcap_ifaces[i])
|
|
free(pcap_ifaces[i]);
|
|
}
|
|
|
|
if (pcap_path != NULL) {
|
|
if (delete_pcap_when_done) unlink(pcap_path);
|
|
free(pcap_path);
|
|
}
|
|
|
|
if (getIfType() == interface_type_PCAP_DUMP) {
|
|
/* Cleanup any possible leftover file */
|
|
cleanupPcapDumpDir();
|
|
}
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void PcapInterface::cleanupPcapDumpDir() {
|
|
char base_dir[MAX_PATH];
|
|
|
|
if (snprintf(base_dir, sizeof(base_dir), "%s/%d", ntop->get_working_dir(),
|
|
get_id()) < (int)sizeof(base_dir)) {
|
|
ntop->fixPath(base_dir);
|
|
Utils::remove_recursively(base_dir);
|
|
}
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
/*
|
|
Account flow traffic in the network interface
|
|
*/
|
|
static bool idle_flow_account(GenericHashEntry *h, void *user_data, bool *matched) {
|
|
Flow *f = (Flow*)h;
|
|
|
|
f->accountFlowTraffic();
|
|
|
|
return(false);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
static void *packetPollLoop(void *ptr) {
|
|
PcapInterface *iface = (PcapInterface *)ptr;
|
|
FILE *pcap_list = iface->get_pcap_list();
|
|
int fds[MAX_NUM_PCAP_INTERFACES] = { -1 };
|
|
|
|
/* Wait until the initialization completes */
|
|
while (iface->isStartingUp()) sleep(1);
|
|
|
|
/* Test Script (Pre Analysis) */
|
|
if (ntop->getPrefs()->get_test_pre_script_path()) {
|
|
const char *test_pre_script_path =
|
|
ntop->getPrefs()->get_test_pre_script_path();
|
|
|
|
/* Wait for the HTTP server to be able to serve requests
|
|
* from the pre script, if any */
|
|
while (!ntop->get_HTTPserver()->accepts_requests() &&
|
|
!ntop->getGlobals()->isShutdown())
|
|
sleep(1);
|
|
|
|
/* Execute as Bash script */
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Running Pre Script '%s'",
|
|
test_pre_script_path);
|
|
Utils::exec(test_pre_script_path);
|
|
|
|
/* Allow check configs to be re-read */
|
|
sleep(ntop->getPrefs()->get_housekeeping_frequency()*2);
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Processing pcap file");
|
|
}
|
|
|
|
do {
|
|
if (pcap_list != NULL) {
|
|
char path[256], *fname;
|
|
pcap_t *file_pcap_handle;
|
|
|
|
while ((fname = fgets(path, sizeof(path), pcap_list)) != NULL) {
|
|
char pcap_error_buffer[PCAP_ERRBUF_SIZE];
|
|
int l = (int)strlen(path) - 1;
|
|
|
|
if ((l <= 1) || (path[0] == '#')) continue;
|
|
path[l--] = '\0';
|
|
|
|
/* Remove trailer white spaces */
|
|
while ((l > 0) && (path[l] == ' ')) path[l--] = '\0';
|
|
|
|
while (l > 0) {
|
|
if (!isascii(path[l--])) {
|
|
/* This looks like a bad file */
|
|
fname = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fname != NULL) {
|
|
if ((file_pcap_handle = pcap_open_offline(path, pcap_error_buffer)) == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR,
|
|
"Unable to open file '%s': %s", path,
|
|
pcap_error_buffer);
|
|
} else {
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Reading packets from pcap file %s", path);
|
|
iface->set_pcap_handle(file_pcap_handle, 0);
|
|
break;
|
|
}
|
|
} else
|
|
break;
|
|
}
|
|
|
|
if (fname == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "No more pcap files to read");
|
|
fclose(pcap_list);
|
|
break;
|
|
} else
|
|
iface->set_datalink(pcap_datalink(file_pcap_handle));
|
|
}
|
|
|
|
/* Wait until the interface is active */
|
|
while (iface->idle()) {
|
|
iface->purgeIdle(time(NULL));
|
|
sleep(1);
|
|
}
|
|
|
|
#ifndef WIN32
|
|
for(u_int8_t i=0; i < iface->get_num_ifaces(); i++) {
|
|
pcap_t *pd = iface->get_pcap_handle(i);
|
|
|
|
fds[i] = pcap_get_selectable_fd(pd);
|
|
|
|
#if defined(__APPLE__) || defined(__FreeBSD__)
|
|
if(!iface->read_from_pcap_dump() && !iface->read_from_stdin()) {
|
|
char pcap_error_buffer[PCAP_ERRBUF_SIZE];
|
|
|
|
if(pcap_setnonblock(pd, 1, pcap_error_buffer))
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR,
|
|
"Unable to enable non blocking mode on %s: %s",
|
|
iface->getPcapIfaceName(i),
|
|
pcap_error_buffer);
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
while (iface->isRunning() && (!ntop->getGlobals()->isShutdown())) {
|
|
int max_fd = 0;
|
|
#ifndef WIN32
|
|
fd_set rset;
|
|
struct timeval tv;
|
|
bool do_break = false;
|
|
#ifdef DEBUG_POLLING
|
|
bool found = false;
|
|
#endif
|
|
|
|
FD_ZERO(&rset);
|
|
|
|
for(u_int8_t i=0; i < iface->get_num_ifaces(); i++) {
|
|
FD_SET(fds[i], &rset);
|
|
|
|
if(fds[i] > max_fd) max_fd = fds[i];
|
|
}
|
|
|
|
#if defined(__APPLE__) || defined(__FreeBSD__)
|
|
/*
|
|
On some, but not all, platforms, if a packet buffer timeout was specified,
|
|
the wait will terminate after the packet buffer timeout expires; applications
|
|
should be prepared for this, as it happens on some platforms, but should not
|
|
rely on it, as it does not happen on other platforms. Note that the wait might,
|
|
or might not, terminate even if no packets are available; applications should
|
|
be prepared for this to happen, but must not rely on it happening.
|
|
|
|
A handle can be put into ``non-blocking mode'', so that those routines will,
|
|
rather than blocking, return an indication that no packets are available to read.
|
|
Call pcap_setnonblock() to put a handle into non-blocking mode or to take it out
|
|
of non-blocking mode; call pcap_getnonblock() to determine whether a
|
|
handle is in non-blocking mode.
|
|
*/
|
|
tv.tv_sec = 0, tv.tv_usec = 10000 /* it must be < pcap_open_live() timeout */;
|
|
#else
|
|
tv.tv_sec = 1, tv.tv_usec = 0;
|
|
#endif
|
|
|
|
if(select(max_fd + 1, &rset, NULL, NULL, &tv) == 0) {
|
|
#ifdef DEBUG_POLLING
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "No packet to process");
|
|
#endif
|
|
|
|
for(u_int8_t i=0; i < iface->get_num_ifaces(); i++) {
|
|
if(!iface->read_from_stdin() &&
|
|
!Utils::nwInterfaceExists(iface->getPcapIfaceName(i))) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING,
|
|
"Network Interface %s (id %d) disappeared (is it is down ?)",
|
|
iface->getPcapIfaceName(i), i);
|
|
iface->reopen(i); /* Try to reopen the interface that disappeared */
|
|
}
|
|
|
|
iface->purgeIdle(time(NULL));
|
|
} /* for */
|
|
|
|
#if !(defined(__APPLE__) || defined(__FreeBSD__))
|
|
continue;
|
|
#endif
|
|
}
|
|
|
|
if(iface->idle())
|
|
continue;
|
|
|
|
for(u_int8_t i=0; i < iface->get_num_ifaces(); i++) {
|
|
#if !(defined(__APPLE__) || defined(__FreeBSD__))
|
|
if(FD_ISSET(fds[i], &rset))
|
|
#endif
|
|
{
|
|
#ifdef DEBUG_POLLING
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "processNextPacket(%d)", i);
|
|
#endif
|
|
|
|
if(iface->processNextPacket(iface->get_pcap_handle(i),
|
|
iface->get_ifindex(i),
|
|
iface->get_ifdatalink(i)) == false) {
|
|
do_break = true;
|
|
break;
|
|
}
|
|
#ifdef DEBUG_POLLING
|
|
found = true;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_POLLING
|
|
if(!found)
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "**** NO packet");
|
|
#endif
|
|
if(do_break)
|
|
break;
|
|
#else
|
|
if(iface->processNextPacket(iface->get_pcap_handle(0),
|
|
iface->get_ifindex(0),
|
|
iface->get_ifdatalink(0)) == false)
|
|
break;
|
|
#endif
|
|
} /* while */
|
|
|
|
} while (pcap_list != NULL);
|
|
|
|
if(iface->read_from_pcap_dump()) {
|
|
FlowHash *fh = iface->get_flows_hash();
|
|
u_int32_t begin_slot = 0;
|
|
|
|
iface->set_read_from_pcap_dump_done();
|
|
|
|
fh->walk(&begin_slot, true /* walk_all */, idle_flow_account, NULL /* user_data */);
|
|
}
|
|
|
|
/* Do two full scans to make sure all stats are updated */
|
|
for (int i = 0; i < 2; i++)
|
|
iface->purgeIdle(time(NULL), false, true /* Full scan */);
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Terminated packet polling for %s",
|
|
iface->get_description());
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void PcapInterface::startPacketPolling() {
|
|
pthread_create(&pollLoop, NULL, packetPollLoop, (void *)this);
|
|
pollLoopCreated = true;
|
|
NetworkInterface::startPacketPolling();
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
u_int32_t PcapInterface::getNumDroppedPackets() {
|
|
#ifndef WIN32
|
|
u_int32_t tot = 0;
|
|
|
|
for(u_int8_t i=0; i < get_num_ifaces(); i++) {
|
|
/* It seems this leads to crashes on Windows */
|
|
struct pcap_stat pcapStat;
|
|
|
|
if(pcap_handle[i] && (pcap_stats(pcap_handle[i], &pcapStat) >= 0)) {
|
|
tot += pcapStat.ps_drop;
|
|
#ifdef DEBUG_POLLING
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "[id: %u][pkts: %u][drops: %u]", i, pcapStat.ps_recv, pcapStat.ps_drop);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return(tot);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
bool PcapInterface::set_packet_filter(char *filter) {
|
|
for(u_int8_t i=0; i < get_num_ifaces(); i++) {
|
|
struct bpf_program fcode;
|
|
struct in_addr netmask;
|
|
int rc;
|
|
|
|
if (!pcap_handle[i]) return (false);
|
|
|
|
netmask.s_addr = htonl(0xFFFFFF00);
|
|
|
|
rc = pcap_compile(pcap_handle[i], &fcode, filter, 1, netmask.s_addr);
|
|
|
|
if (rc < 0) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Unable to compile %s filter %s. Filter ignored.",
|
|
ifname, filter);
|
|
return (false);
|
|
}
|
|
|
|
rc = pcap_setfilter(pcap_handle[i], &fcode);
|
|
|
|
pcap_freecode(&fcode);
|
|
|
|
if (rc < 0) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Unable to set on %s filter %s. Filter ignored.",
|
|
ifname, filter);
|
|
return (false);
|
|
}
|
|
} /* for */
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Packet capture filter on %s set to \"%s\"",
|
|
ifname, filter);
|
|
|
|
/* can't get consistent stats while bpf is set */
|
|
emulate_traffic_directions = false;
|
|
|
|
return (true);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
/* Computes the counter delta handling wrapping on 32 bit platforms */
|
|
static u_int64_t getCounterInc(u_int64_t old_v, u_int64_t new_v) {
|
|
/* Assume wrapping only occurs on 32 bit platforms (e.g. armv7l raspbian) */
|
|
const u_int32_t max_val = (u_int32_t)-1;
|
|
|
|
if (new_v >= old_v)
|
|
return (new_v - old_v);
|
|
else {
|
|
/* Counter wrapped */
|
|
if (max_val >= old_v)
|
|
return ((max_val - old_v) + new_v);
|
|
else
|
|
/* this should never occur */
|
|
return (new_v);
|
|
}
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
/* This method is only executed by the periodic script second.lua
|
|
* Note: this is required as libpcap does not provide packets/bytes
|
|
* statistics per direction. Make sure ethStats are not increased
|
|
* by the packet processing function when this is in place. */
|
|
void PcapInterface::updateDirectionStats() {
|
|
if(emulate_traffic_directions) {
|
|
ProtoStats current_stats_in, current_stats_out;
|
|
bool ret = true;
|
|
|
|
for(u_int8_t i=0; i < get_num_ifaces(); i++) {
|
|
if (pcap_ifaces[i]) {
|
|
ret &= Utils::readInterfaceStats(pcap_ifaces[i], ¤t_stats_in, ¤t_stats_out);
|
|
}
|
|
}
|
|
|
|
if(ret) {
|
|
pcap_direction_t capture_dir = ntop->getPrefs()->getCaptureDirection();
|
|
|
|
/* grsec check, the new ntopng user may not able to read the stats anymore */
|
|
if ((prev_stats_in.getPkts() || prev_stats_out.getPkts())
|
|
&& (!(current_stats_in.getPkts() || current_stats_out.getPkts()))) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING,
|
|
"Cannot read interface stats after user "
|
|
"change (grsec kernel hardening in place?)");
|
|
emulate_traffic_directions = false;
|
|
} else {
|
|
if ((capture_dir == PCAP_D_INOUT) || (capture_dir == PCAP_D_IN)) {
|
|
ethStats.incNumPackets(true, getCounterInc(prev_stats_in.getPkts(), current_stats_in.getPkts()));
|
|
ethStats.incNumBytes(true, getCounterInc(prev_stats_in.getBytes(), current_stats_in.getBytes()));
|
|
}
|
|
|
|
if ((capture_dir == PCAP_D_INOUT) || (capture_dir == PCAP_D_OUT)) {
|
|
ethStats.incNumPackets(false, getCounterInc(prev_stats_out.getPkts(), current_stats_out.getPkts()));
|
|
ethStats.incNumBytes(false, getCounterInc(prev_stats_out.getBytes(), current_stats_out.getBytes()));
|
|
}
|
|
|
|
prev_stats_in = current_stats_in, prev_stats_out = current_stats_out;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
bool PcapInterface::reproducePcapOriginalSpeed() const {
|
|
return (read_pkts_from_pcap_dump
|
|
&& ntop->getPrefs()->reproduceOriginalSpeed());
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
bool PcapInterface::reopen(u_int8_t iface_id) {
|
|
pcap_close(pcap_handle[iface_id]);
|
|
|
|
Utils::gainWriteCapabilities();
|
|
|
|
while(!ntop->getGlobals()->isShutdown()) {
|
|
char pcap_error_buffer[PCAP_ERRBUF_SIZE];
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "Trying to open %s", pcap_ifaces[iface_id]);
|
|
|
|
pcap_handle[iface_id] = pcap_open_live(pcap_ifaces[iface_id], ntop->getGlobals()->getSnaplen(pcap_ifaces[iface_id]),
|
|
ntop->getPrefs()->use_promiscuous(),
|
|
1000 /* 1 sec */, pcap_error_buffer);
|
|
|
|
if(pcap_handle[iface_id]) {
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Interface %s is back", pcap_ifaces[iface_id]);
|
|
Utils::dropWriteCapabilities();
|
|
return(true);
|
|
} else
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "Unable to open %s: %s",
|
|
pcap_ifaces[iface_id], pcap_error_buffer);
|
|
|
|
sleep(1);
|
|
}
|
|
|
|
Utils::dropWriteCapabilities();
|
|
|
|
return(false);
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void PcapInterface::sendTermination() {
|
|
for(u_int8_t i=0; i < get_num_ifaces(); i++) {
|
|
if(pcap_handle[i])
|
|
pcap_breakloop(pcap_handle[i]);
|
|
}
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
#ifdef TRACE
|
|
static u_int32_t num_pkts = 0;
|
|
#endif
|
|
|
|
bool PcapInterface::processNextPacket(pcap_t *pd, int32_t if_index, int datalink_type) {
|
|
const u_char *pkt;
|
|
struct pcap_pkthdr *hdr;
|
|
int rc;
|
|
|
|
if ((rc = pcap_next_ex(pd, &hdr, &pkt)) > 0) {
|
|
if(ntop->getPrefs()->doReforgeTimestamps())
|
|
gettimeofday(&hdr->ts, NULL);
|
|
|
|
if (reproducePcapOriginalSpeed()) {
|
|
struct timeval now;
|
|
|
|
gettimeofday(&now, NULL);
|
|
|
|
if (firstPktTS.tv_sec == 0) {
|
|
startTS = now;
|
|
firstPktTS = hdr->ts;
|
|
} else {
|
|
u_int32_t packetTimeDelta = Utils::msTimevalDiff(&hdr->ts, &firstPktTS);
|
|
u_int32_t fromStartTimeDelta = Utils::msTimevalDiff(&now, &startTS);
|
|
|
|
if (packetTimeDelta > fromStartTimeDelta) {
|
|
u_int32_t sleepMs = packetTimeDelta - fromStartTimeDelta;
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG, "Sleeping %.3f sec",
|
|
((float)(sleepMs)) / 1000);
|
|
|
|
_usleep(sleepMs * 1000);
|
|
|
|
/* Recompute after sleep */
|
|
gettimeofday(&now, NULL);
|
|
}
|
|
}
|
|
|
|
hdr->ts = now;
|
|
}
|
|
|
|
if((pkt != NULL) && (hdr->caplen > 0)) {
|
|
u_int16_t p;
|
|
Host *srcHost = NULL, *dstHost = NULL;
|
|
Flow *flow = NULL;
|
|
|
|
#ifdef WIN32
|
|
/*
|
|
For some unknown reason, on Windows winpcap
|
|
gets crazy with specific packets and so ntopng
|
|
crashes. Copying the packet memory onto a local buffer
|
|
prevents that, as specified in
|
|
https://github.com/ntop/ntopng/issues/194
|
|
*/
|
|
u_char pkt_copy[1600];
|
|
struct pcap_pkthdr hdr_copy;
|
|
|
|
memcpy(&hdr_copy, hdr, sizeof(hdr_copy));
|
|
hdr_copy.len = min(hdr->len, sizeof(pkt_copy) - 1);
|
|
hdr_copy.caplen = min(hdr_copy.len, hdr_copy.caplen);
|
|
memcpy(pkt_copy, pkt, hdr_copy.len);
|
|
dissectPacket(if_index,
|
|
DUMMY_BRIDGE_INTERFACE_ID, datalink_type,
|
|
true /* ingress - TODO: see if we pass the real
|
|
packet direction */
|
|
,
|
|
NULL, &hdr_copy, (const u_char *)pkt_copy, &p,
|
|
&srcHost, &dstHost, &flow);
|
|
#else
|
|
hdr->caplen = min_val(hdr->caplen, getMTU());
|
|
|
|
dissectPacket(if_index,
|
|
DUMMY_BRIDGE_INTERFACE_ID, datalink_type,
|
|
true /* ingress - TODO: see if we pass the real
|
|
packet direction */
|
|
,
|
|
NULL, hdr, pkt, &p, &srcHost, &dstHost, &flow);
|
|
#endif
|
|
} else {
|
|
incStats(true /* ingressPacket */, hdr->ts.tv_sec,
|
|
0, NDPI_PROTOCOL_UNKNOWN,
|
|
NDPI_PROTOCOL_CATEGORY_UNSPECIFIED,
|
|
0, hdr->len, 1);
|
|
}
|
|
} else if (rc < 0) {
|
|
if (read_from_pcap_dump())
|
|
return(false);
|
|
} else {
|
|
/* No packet received before the timeout */
|
|
purgeIdle(time(NULL));
|
|
}
|
|
|
|
#ifdef TRACE
|
|
if(++num_pkts != ethStats.getNumPackets())
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Received %u / processed %u", num_pkts, ethStats.getNumPackets());
|
|
#endif
|
|
|
|
return(true);
|
|
}
|
|
|
|
#endif
|