|
@@ -22,24 +22,25 @@
|
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
*/
|
|
|
|
|
|
-/* $Id: softflowd.c,v 1.93 2006/11/02 06:23:29 djm Exp $ */
|
|
|
+/* $Id$ */
|
|
|
|
|
|
/*
|
|
|
- * This is software implementation of Cisco's NetFlow(tm) traffic
|
|
|
- * reporting system. It operates by listening (via libpcap) on a
|
|
|
- * promiscuous interface and tracking traffic flows.
|
|
|
+ * This is software implementation of Cisco's NetFlow(tm) traffic
|
|
|
+ * reporting system. It operates by listening (via libpcap) on a
|
|
|
+ * promiscuous interface and tracking traffic flows.
|
|
|
*
|
|
|
- * Traffic flows are recorded by source/destination/protocol IP address or, in the
|
|
|
- * case of TCP and UDP, by src_addr:src_port/dest_addr:dest_port/protocol
|
|
|
+ * Traffic flows are recorded by source/destination/protocol
|
|
|
+ * IP address or, in the case of TCP and UDP, by
|
|
|
+ * src_addr:src_port/dest_addr:dest_port/protocol
|
|
|
*
|
|
|
- * Flows expire automatically after a period of inactivity (default: 1 hour)
|
|
|
- * They may also be evicted (in order of age) in situations where there are
|
|
|
- * more flows than slots available.
|
|
|
+ * Flows expire automatically after a period of inactivity (default: 1
|
|
|
+ * hour) They may also be evicted (in order of age) in situations where
|
|
|
+ * there are more flows than slots available.
|
|
|
*
|
|
|
- * Netflow version 1 compatible packets are sent to a specified target
|
|
|
- * host upon flow expiry.
|
|
|
+ * Netflow compatible packets are sent to a specified target host upon
|
|
|
+ * flow expiry.
|
|
|
*
|
|
|
- * As this implementation watches traffic promiscuously, it is likely to
|
|
|
+ * As this implementation watches traffic promiscuously, it is likely to
|
|
|
* place significant load on hosts or gateways on which it is installed.
|
|
|
*/
|
|
|
|
|
@@ -48,16 +49,18 @@
|
|
|
#include "convtime.h"
|
|
|
#include "softflowd.h"
|
|
|
#include "treetype.h"
|
|
|
+#include "freelist.h"
|
|
|
#include "log.h"
|
|
|
#include <pcap.h>
|
|
|
|
|
|
-RCSID("$Id: softflowd.c,v 1.93 2006/11/02 06:23:29 djm Exp $");
|
|
|
+RCSID("$Id$");
|
|
|
|
|
|
/* Global variables */
|
|
|
static int verbose_flag = 0; /* Debugging flag */
|
|
|
+static u_int16_t if_index = 0; /* "manual" interface index */
|
|
|
|
|
|
/* Signal handler flags */
|
|
|
-static int graceful_shutdown_request = 0;
|
|
|
+static volatile sig_atomic_t graceful_shutdown_request = 0;
|
|
|
|
|
|
/* Context for libpcap callback functions */
|
|
|
struct CB_CTXT {
|
|
@@ -95,8 +98,8 @@ static const struct DATALINK lt[] = {
|
|
|
};
|
|
|
|
|
|
/* Netflow send functions */
|
|
|
-typedef int (netflow_send_func_t)(struct FLOW **, int, int, u_int64_t *,
|
|
|
- struct timeval *, int);
|
|
|
+typedef int (netflow_send_func_t)(struct FLOW **, int, int, u_int16_t,
|
|
|
+ u_int64_t *, struct timeval *, int);
|
|
|
struct NETFLOW_SENDER {
|
|
|
int version;
|
|
|
netflow_send_func_t *func;
|
|
@@ -126,7 +129,8 @@ static void sighand_graceful_shutdown(int signum)
|
|
|
static void sighand_other(int signum)
|
|
|
{
|
|
|
/* XXX: this may not be completely safe */
|
|
|
- logit(LOG_WARNING, "Exiting immediately on unexpected signal %d", signum);
|
|
|
+ logit(LOG_WARNING, "Exiting immediately on unexpected signal %d",
|
|
|
+ signum);
|
|
|
_exit(0);
|
|
|
}
|
|
|
|
|
@@ -194,6 +198,30 @@ expiry_compare(struct EXPIRY *a, struct EXPIRY *b)
|
|
|
EXPIRY_PROTOTYPE(EXPIRIES, EXPIRY, trp, expiry_compare);
|
|
|
EXPIRY_GENERATE(EXPIRIES, EXPIRY, trp, expiry_compare);
|
|
|
|
|
|
+static struct FLOW *
|
|
|
+flow_get(struct FLOWTRACK *ft)
|
|
|
+{
|
|
|
+ return freelist_get(&ft->flow_freelist);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+flow_put(struct FLOWTRACK *ft, struct FLOW *flow)
|
|
|
+{
|
|
|
+ return freelist_put(&ft->flow_freelist, flow);
|
|
|
+}
|
|
|
+
|
|
|
+static struct EXPIRY *
|
|
|
+expiry_get(struct FLOWTRACK *ft)
|
|
|
+{
|
|
|
+ return freelist_get(&ft->expiry_freelist);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+expiry_put(struct FLOWTRACK *ft, struct EXPIRY *expiry)
|
|
|
+{
|
|
|
+ return freelist_put(&ft->expiry_freelist, expiry);
|
|
|
+}
|
|
|
+
|
|
|
#if 0
|
|
|
/* Dump a packet */
|
|
|
static void
|
|
@@ -218,9 +246,9 @@ static const char *
|
|
|
format_time(time_t t)
|
|
|
{
|
|
|
struct tm *tm;
|
|
|
- static char buf[20];
|
|
|
+ static char buf[32];
|
|
|
|
|
|
- tm = localtime(&t);
|
|
|
+ tm = gmtime(&t);
|
|
|
strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", tm);
|
|
|
|
|
|
return (buf);
|
|
@@ -231,7 +259,7 @@ format_time(time_t t)
|
|
|
static const char *
|
|
|
format_flow(struct FLOW *flow)
|
|
|
{
|
|
|
- char addr1[64], addr2[64], stime[20], ftime[20];
|
|
|
+ char addr1[64], addr2[64], stime[32], ftime[32];
|
|
|
static char buf[1024];
|
|
|
|
|
|
inet_ntop(flow->af, &flow->addr[0], addr1, sizeof(addr1));
|
|
@@ -288,9 +316,9 @@ transport_to_flowrec(struct FLOW *flow, const u_int8_t *pkt,
|
|
|
const struct icmp *icmp = (const struct icmp *)pkt;
|
|
|
|
|
|
/*
|
|
|
- * XXX to keep flow in proper canonical format, it may be necessary
|
|
|
- * to swap the array slots based on the order of the port numbers
|
|
|
- * does this matter in practice??? I don't think so - return flows will
|
|
|
+ * XXX to keep flow in proper canonical format, it may be necessary to
|
|
|
+ * swap the array slots based on the order of the port numbers does
|
|
|
+ * this matter in practice??? I don't think so - return flows will
|
|
|
* always match, because of their symmetrical addr/ports
|
|
|
*/
|
|
|
|
|
@@ -427,7 +455,7 @@ flow_update_expiry(struct FLOWTRACK *ft, struct FLOW *flow)
|
|
|
{
|
|
|
EXPIRY_REMOVE(EXPIRIES, &ft->expiries, flow->expiry);
|
|
|
|
|
|
- /* Flows over 2Gb traffic */
|
|
|
+ /* Flows over 2 GiB traffic */
|
|
|
if (flow->octets[0] > (1U << 31) || flow->octets[1] > (1U << 31)) {
|
|
|
flow->expiry->expires_at = 0;
|
|
|
flow->expiry->reason = R_OVERBYTES;
|
|
@@ -564,8 +592,8 @@ process_packet(struct FLOWTRACK *ft, const u_int8_t *pkt, int af,
|
|
|
/* If a matching flow does not exist, create and insert one */
|
|
|
if ((flow = FLOW_FIND(FLOWS, &ft->flows, &tmp)) == NULL) {
|
|
|
/* Allocate and fill in the flow */
|
|
|
- if ((flow = malloc(sizeof(*flow))) == NULL) {
|
|
|
- logit(LOG_ERR, "process_packet: flow malloc(%u) fail",
|
|
|
+ if ((flow = flow_get(ft)) == NULL) {
|
|
|
+ logit(LOG_ERR, "process_packet: flow_get failed",
|
|
|
sizeof(*flow));
|
|
|
return (PP_MALLOC_FAIL);
|
|
|
}
|
|
@@ -576,8 +604,8 @@ process_packet(struct FLOWTRACK *ft, const u_int8_t *pkt, int af,
|
|
|
FLOW_INSERT(FLOWS, &ft->flows, flow);
|
|
|
|
|
|
/* Allocate and fill in the associated expiry event */
|
|
|
- if ((flow->expiry = malloc(sizeof(*flow->expiry))) == NULL) {
|
|
|
- logit(LOG_ERR, "process_packet: expiry malloc(%u) fail",
|
|
|
+ if ((flow->expiry = expiry_get(ft)) == NULL) {
|
|
|
+ logit(LOG_ERR, "process_packet: expiry_get failed",
|
|
|
sizeof(*flow->expiry));
|
|
|
return (PP_MALLOC_FAIL);
|
|
|
}
|
|
@@ -589,7 +617,8 @@ process_packet(struct FLOWTRACK *ft, const u_int8_t *pkt, int af,
|
|
|
|
|
|
ft->num_flows++;
|
|
|
if (verbose_flag)
|
|
|
- logit(LOG_DEBUG, "ADD FLOW %s", format_flow_brief(flow));
|
|
|
+ logit(LOG_DEBUG, "ADD FLOW %s",
|
|
|
+ format_flow_brief(flow));
|
|
|
} else {
|
|
|
/* Update flow statistics */
|
|
|
flow->packets[0] += tmp.packets[0];
|
|
@@ -807,20 +836,21 @@ check_expired(struct FLOWTRACK *ft, struct NETFLOW_TARGET *target, int ex)
|
|
|
FLOW_REMOVE(FLOWS, &ft->flows, expiry->flow);
|
|
|
EXPIRY_REMOVE(EXPIRIES, &ft->expiries, expiry);
|
|
|
expiry->flow->expiry = NULL;
|
|
|
- free(expiry);
|
|
|
+ expiry_put(ft, expiry);
|
|
|
|
|
|
ft->num_flows--;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (verbose_flag)
|
|
|
- logit(LOG_DEBUG, "Finished scan %d flow(s) to be evicted", num_expired);
|
|
|
+ logit(LOG_DEBUG, "Finished scan %d flow(s) to be evicted",
|
|
|
+ num_expired);
|
|
|
|
|
|
/* Processing for expired flows */
|
|
|
if (num_expired > 0) {
|
|
|
if (target != NULL && target->fd != -1) {
|
|
|
r = target->dialect->func(expired_flows, num_expired,
|
|
|
- target->fd, &ft->flows_exported,
|
|
|
+ target->fd, if_index, &ft->flows_exported,
|
|
|
&ft->system_boot_time, verbose_flag);
|
|
|
if (verbose_flag)
|
|
|
logit(LOG_DEBUG, "sent %d netflow packets", r);
|
|
@@ -838,8 +868,7 @@ check_expired(struct FLOWTRACK *ft, struct NETFLOW_TARGET *target, int ex)
|
|
|
expired_flows[i]);
|
|
|
}
|
|
|
update_statistics(ft, expired_flows[i]);
|
|
|
-
|
|
|
- free(expired_flows[i]);
|
|
|
+ flow_put(ft, expired_flows[i]);
|
|
|
}
|
|
|
|
|
|
free(expired_flows);
|
|
@@ -923,10 +952,10 @@ delete_all_flows(struct FLOWTRACK *ft)
|
|
|
FLOW_REMOVE(FLOWS, &ft->flows, flow);
|
|
|
|
|
|
EXPIRY_REMOVE(EXPIRIES, &ft->expiries, flow->expiry);
|
|
|
- free(flow->expiry);
|
|
|
+ expiry_put(ft, flow->expiry);
|
|
|
|
|
|
ft->num_flows--;
|
|
|
- free(flow);
|
|
|
+ flow_put(ft, flow);
|
|
|
i++;
|
|
|
}
|
|
|
|
|
@@ -978,26 +1007,28 @@ statistics(struct FLOWTRACK *ft, FILE *out, pcap_t *pcap)
|
|
|
|
|
|
fprintf(out, "\n");
|
|
|
fprintf(out, "Expired flow reasons:\n");
|
|
|
- fprintf(out, " tcp = %9llu tcp.rst = %9llu tcp.fin = %9llu\n",
|
|
|
- ft->expired_tcp, ft->expired_tcp_rst, ft->expired_tcp_fin);
|
|
|
- fprintf(out, " udp = %9llu icmp = %9llu general = %9llu\n",
|
|
|
- ft->expired_udp, ft->expired_icmp, ft->expired_general);
|
|
|
+ fprintf(out, " tcp = %9llu tcp.rst = %9llu "
|
|
|
+ "tcp.fin = %9llu\n", ft->expired_tcp, ft->expired_tcp_rst,
|
|
|
+ ft->expired_tcp_fin);
|
|
|
+ fprintf(out, " udp = %9llu icmp = %9llu "
|
|
|
+ "general = %9llu\n", ft->expired_udp, ft->expired_icmp,
|
|
|
+ ft->expired_general);
|
|
|
fprintf(out, " maxlife = %9llu\n", ft->expired_maxlife);
|
|
|
- fprintf(out, " over 2Gb = %9llu\n", ft->expired_overbytes);
|
|
|
+ fprintf(out, "over 2 GiB = %9llu\n", ft->expired_overbytes);
|
|
|
fprintf(out, " maxflows = %9llu\n", ft->expired_maxflows);
|
|
|
fprintf(out, " flushed = %9llu\n", ft->expired_flush);
|
|
|
|
|
|
fprintf(out, "\n");
|
|
|
|
|
|
- fprintf(out, "Per-protocol statistics: Octets Packets Avg Life Max Life\n");
|
|
|
+ fprintf(out, "Per-protocol statistics: Octets "
|
|
|
+ "Packets Avg Life Max Life\n");
|
|
|
for(i = 0; i < 256; i++) {
|
|
|
if (ft->packets_pp[i]) {
|
|
|
pe = getprotobynumber(i);
|
|
|
snprintf(proto, sizeof(proto), "%s (%d)",
|
|
|
pe != NULL ? pe->p_name : "Unknown", i);
|
|
|
- fprintf(out,
|
|
|
- " %17s: %14llu %12llu %8.2fs %10.2fs\n",
|
|
|
- proto,
|
|
|
+ fprintf(out, " %17s: %14llu %12llu %8.2fs "
|
|
|
+ "%10.2fs\n", proto,
|
|
|
ft->octets_pp[i],
|
|
|
ft->packets_pp[i],
|
|
|
ft->duration_pp[i].mean,
|
|
@@ -1164,7 +1195,8 @@ accept_control(int lsock, struct NETFLOW_TARGET *target, struct FLOWTRACK *ft,
|
|
|
fprintf(ctlf, "\tsend-template\n");
|
|
|
ret = 0;
|
|
|
} else if (strcmp(buf, "shutdown") == 0) {
|
|
|
- fprintf(ctlf, "softflowd[%u]: Shutting down gracefully...\n", getpid());
|
|
|
+ fprintf(ctlf, "softflowd[%u]: Shutting down gracefully...\n",
|
|
|
+ getpid());
|
|
|
graceful_shutdown_request = 1;
|
|
|
ret = 1;
|
|
|
} else if (strcmp(buf, "exit") == 0) {
|
|
@@ -1186,8 +1218,9 @@ accept_control(int lsock, struct NETFLOW_TARGET *target, struct FLOWTRACK *ft,
|
|
|
delete_all_flows(ft));
|
|
|
ret = 0;
|
|
|
} else if (strcmp(buf, "statistics") == 0) {
|
|
|
- fprintf(ctlf, "softflowd[%u]: Accumulated statistics:\n",
|
|
|
- getpid());
|
|
|
+ fprintf(ctlf, "softflowd[%u]: Accumulated statistics "
|
|
|
+ "since %s UTC:\n", getpid(),
|
|
|
+ format_time(ft->system_boot_time.tv_sec));
|
|
|
statistics(ft, ctlf, pcap);
|
|
|
ret = 0;
|
|
|
} else if (strcmp(buf, "debug+") == 0) {
|
|
@@ -1395,6 +1428,11 @@ init_flowtrack(struct FLOWTRACK *ft)
|
|
|
FLOW_INIT(&ft->flows);
|
|
|
EXPIRY_INIT(&ft->expiries);
|
|
|
|
|
|
+ freelist_init(&ft->flow_freelist, sizeof(struct FLOW));
|
|
|
+ freelist_init(&ft->expiry_freelist, sizeof(struct EXPIRY));
|
|
|
+
|
|
|
+ ft->max_flows = DEFAULT_MAX_FLOWS;
|
|
|
+
|
|
|
ft->track_level = TRACK_FULL;
|
|
|
|
|
|
ft->tcp_timeout = DEFAULT_TCP_TIMEOUT;
|
|
@@ -1439,34 +1477,42 @@ argv_join(int argc, char **argv)
|
|
|
static void
|
|
|
usage(void)
|
|
|
{
|
|
|
- fprintf(stderr, "Usage: %s [options] [bpf_program]\n", PROGNAME);
|
|
|
- fprintf(stderr, "This is %s version %s. Valid commandline options:\n", PROGNAME, PROGVER);
|
|
|
- fprintf(stderr, " -i interface Specify interface to listen on\n");
|
|
|
- fprintf(stderr, " -r pcap_file Specify packet capture file to read\n");
|
|
|
- fprintf(stderr, " -t timeout=time Specify named timeout\n");
|
|
|
- fprintf(stderr, " -m max_flows Specify maximum number of flows to track (default %d)\n", DEFAULT_MAX_FLOWS);
|
|
|
- fprintf(stderr, " -n host:port Send Cisco NetFlow(tm)-compatible packets to host:port\n");
|
|
|
- fprintf(stderr, " -p pidfile Record pid in specified file (default: %s)\n", DEFAULT_PIDFILE);
|
|
|
- fprintf(stderr, " -c pidfile Location of control socket (default: %s)\n", DEFAULT_CTLSOCK);
|
|
|
- fprintf(stderr, " -v 1|5|9 NetFlow export packet version\n");
|
|
|
- fprintf(stderr, " -L hoplimit Set TTL/hoplimit for export datagrams\n");
|
|
|
- fprintf(stderr, " -T full|proto|ip Set flow tracking level (default: full)\n");
|
|
|
- fprintf(stderr, " -6 Track IPv6 flows, regardless of whether selected \n"
|
|
|
- " NetFlow export protocol supports it\n");
|
|
|
- fprintf(stderr, " -d Don't daemonise\n");
|
|
|
- fprintf(stderr, " -D Debug mode: don't daemonise + verbosity + track v6 flows\n");
|
|
|
- fprintf(stderr, " -h Display this help\n");
|
|
|
- fprintf(stderr, "\n");
|
|
|
- fprintf(stderr, "Valid timeout names and default values:\n");
|
|
|
- fprintf(stderr, " tcp (default %6d)", DEFAULT_TCP_TIMEOUT);
|
|
|
- fprintf(stderr, " tcp.rst (default %6d)", DEFAULT_TCP_RST_TIMEOUT);
|
|
|
- fprintf(stderr, " tcp.fin (default %6d)\n", DEFAULT_TCP_FIN_TIMEOUT);
|
|
|
- fprintf(stderr, " udp (default %6d)", DEFAULT_UDP_TIMEOUT);
|
|
|
- fprintf(stderr, " icmp (default %6d)", DEFAULT_ICMP_TIMEOUT);
|
|
|
- fprintf(stderr, " general (default %6d)\n", DEFAULT_GENERAL_TIMEOUT);
|
|
|
- fprintf(stderr, " maxlife (default %6d)", DEFAULT_MAXIMUM_LIFETIME);
|
|
|
- fprintf(stderr, " expint (default %6d)\n", DEFAULT_EXPIRY_INTERVAL);
|
|
|
- fprintf(stderr, "\n");
|
|
|
+ fprintf(stderr,
|
|
|
+"Usage: %s [options] [bpf_program]\n"
|
|
|
+"This is %s version %s. Valid commandline options:\n"
|
|
|
+" -i [idx:]interface Specify interface to listen on\n"
|
|
|
+" -r pcap_file Specify packet capture file to read\n"
|
|
|
+" -t timeout=time Specify named timeout\n"
|
|
|
+" -m max_flows Specify maximum number of flows to track (default %d)\n"
|
|
|
+" -n host:port Send Cisco NetFlow(tm)-compatible packets to host:port\n"
|
|
|
+" -p pidfile Record pid in specified file\n"
|
|
|
+" (default: %s)\n"
|
|
|
+" -c pidfile Location of control socket\n"
|
|
|
+" (default: %s)\n"
|
|
|
+" -v 1|5|9 NetFlow export packet version\n"
|
|
|
+" -L hoplimit Set TTL/hoplimit for export datagrams\n"
|
|
|
+" -T full|proto|ip Set flow tracking level (default: full)\n"
|
|
|
+" -6 Track IPv6 flows, regardless of whether selected \n"
|
|
|
+" NetFlow export protocol supports it\n"
|
|
|
+" -d Don't daemonise (run in foreground)\n"
|
|
|
+" -D Debug mode: foreground + verbosity + track v6 flows\n"
|
|
|
+" -h Display this help\n"
|
|
|
+"\n"
|
|
|
+"Valid timeout names and default values:\n"
|
|
|
+" tcp (default %6d)"
|
|
|
+" tcp.rst (default %6d)"
|
|
|
+" tcp.fin (default %6d)\n"
|
|
|
+" udp (default %6d)"
|
|
|
+" icmp (default %6d)"
|
|
|
+" general (default %6d)\n"
|
|
|
+" maxlife (default %6d)"
|
|
|
+" expint (default %6d)\n"
|
|
|
+"\n" ,
|
|
|
+ PROGNAME, PROGNAME, PROGVER, DEFAULT_MAX_FLOWS, DEFAULT_PIDFILE,
|
|
|
+ DEFAULT_CTLSOCK, DEFAULT_TCP_TIMEOUT, DEFAULT_TCP_RST_TIMEOUT,
|
|
|
+ DEFAULT_TCP_FIN_TIMEOUT, DEFAULT_UDP_TIMEOUT, DEFAULT_ICMP_TIMEOUT,
|
|
|
+ DEFAULT_GENERAL_TIMEOUT, DEFAULT_MAXIMUM_LIFETIME,
|
|
|
+ DEFAULT_EXPIRY_INTERVAL);
|
|
|
}
|
|
|
|
|
|
static void
|
|
@@ -1633,8 +1679,8 @@ main(int argc, char **argv)
|
|
|
const char *pidfile_path, *ctlsock_path;
|
|
|
extern char *optarg;
|
|
|
extern int optind;
|
|
|
- int ch, dontfork_flag, linktype, ctlsock, i, r, err, always_v6;
|
|
|
- int max_flows, stop_collection_flag, exit_request, hoplimit;
|
|
|
+ int ch, dontfork_flag, linktype, ctlsock, i, err, always_v6, r;
|
|
|
+ int stop_collection_flag, exit_request, hoplimit;
|
|
|
pcap_t *pcap = NULL;
|
|
|
struct sockaddr_storage dest;
|
|
|
struct FLOWTRACK flowtrack;
|
|
@@ -1648,6 +1694,7 @@ main(int argc, char **argv)
|
|
|
init_flowtrack(&flowtrack);
|
|
|
|
|
|
memset(&dest, '\0', sizeof(dest));
|
|
|
+ dest_len = 0;
|
|
|
memset(&target, '\0', sizeof(target));
|
|
|
target.fd = -1;
|
|
|
target.dialect = &nf[0];
|
|
@@ -1655,11 +1702,11 @@ main(int argc, char **argv)
|
|
|
bpf_prog = NULL;
|
|
|
ctlsock = -1;
|
|
|
dev = capfile = NULL;
|
|
|
- max_flows = DEFAULT_MAX_FLOWS;
|
|
|
pidfile_path = DEFAULT_PIDFILE;
|
|
|
ctlsock_path = DEFAULT_CTLSOCK;
|
|
|
dontfork_flag = 0;
|
|
|
always_v6 = 0;
|
|
|
+
|
|
|
while ((ch = getopt(argc, argv, "6hdDL:T:i:r:f:t:n:m:p:c:v:")) != -1) {
|
|
|
switch (ch) {
|
|
|
case '6':
|
|
@@ -1677,15 +1724,23 @@ main(int argc, char **argv)
|
|
|
break;
|
|
|
case 'i':
|
|
|
if (capfile != NULL || dev != NULL) {
|
|
|
- fprintf(stderr, "Packet source already specified.\n\n");
|
|
|
+ fprintf(stderr, "Packet source already "
|
|
|
+ "specified.\n\n");
|
|
|
usage();
|
|
|
exit(1);
|
|
|
}
|
|
|
- dev = optarg;
|
|
|
+ dev = strsep(&optarg, ":");
|
|
|
+ if (optarg != NULL) {
|
|
|
+ if_index = (u_int16_t) atoi(dev);
|
|
|
+ dev = optarg;
|
|
|
+ }
|
|
|
+ if (verbose_flag)
|
|
|
+ fprintf(stderr, "Using %s (idx: %d)\n", dev, if_index);
|
|
|
break;
|
|
|
case 'r':
|
|
|
if (capfile != NULL || dev != NULL) {
|
|
|
- fprintf(stderr, "Packet source already specified.\n\n");
|
|
|
+ fprintf(stderr, "Packet source already "
|
|
|
+ "specified.\n\n");
|
|
|
usage();
|
|
|
exit(1);
|
|
|
}
|
|
@@ -1705,7 +1760,8 @@ main(int argc, char **argv)
|
|
|
else if (strcasecmp(optarg, "ip") == 0)
|
|
|
flowtrack.track_level = TRACK_IP_ONLY;
|
|
|
else {
|
|
|
- fprintf(stderr, "Unknown flow tracking level\n");
|
|
|
+ fprintf(stderr, "Unknown flow tracking "
|
|
|
+ "level\n");
|
|
|
usage();
|
|
|
exit(1);
|
|
|
}
|
|
@@ -1719,7 +1775,7 @@ main(int argc, char **argv)
|
|
|
}
|
|
|
break;
|
|
|
case 'm':
|
|
|
- if ((max_flows = atoi(optarg)) < 0) {
|
|
|
+ if ((flowtrack.max_flows = atoi(optarg)) < 0) {
|
|
|
fprintf(stderr, "Invalid maximum flows\n\n");
|
|
|
usage();
|
|
|
exit(1);
|
|
@@ -1824,12 +1880,12 @@ main(int argc, char **argv)
|
|
|
cb_ctxt.ft = &flowtrack;
|
|
|
cb_ctxt.linktype = linktype;
|
|
|
cb_ctxt.want_v6 = target.dialect->v6_capable || always_v6;
|
|
|
- for(;;) {
|
|
|
+
|
|
|
+ for (r = 0; graceful_shutdown_request == 0; r = 0) {
|
|
|
/*
|
|
|
* Silly libpcap's timeout function doesn't work, so we
|
|
|
* do it here (only if we are reading live)
|
|
|
*/
|
|
|
- r = 0;
|
|
|
if (capfile == NULL) {
|
|
|
memset(pl, '\0', sizeof(pl));
|
|
|
|
|
@@ -1862,13 +1918,15 @@ main(int argc, char **argv)
|
|
|
/* If we have data, run it through libpcap */
|
|
|
if (!stop_collection_flag &&
|
|
|
(capfile != NULL || pl[0].revents != 0)) {
|
|
|
- r = pcap_dispatch(pcap, max_flows, flow_cb, (void*)&cb_ctxt);
|
|
|
+ r = pcap_dispatch(pcap, flowtrack.max_flows, flow_cb,
|
|
|
+ (void*)&cb_ctxt);
|
|
|
if (r == -1) {
|
|
|
logit(LOG_ERR, "Exiting on pcap_dispatch: %s",
|
|
|
pcap_geterr(pcap));
|
|
|
break;
|
|
|
} else if (r == 0) {
|
|
|
- logit(LOG_NOTICE, "Shutting down after pcap EOF");
|
|
|
+ logit(LOG_NOTICE, "Shutting down after "
|
|
|
+ "pcap EOF");
|
|
|
graceful_shutdown_request = 1;
|
|
|
break;
|
|
|
}
|
|
@@ -1886,7 +1944,7 @@ main(int argc, char **argv)
|
|
|
* or whenever we have exceeded the maximum number of active
|
|
|
* flows
|
|
|
*/
|
|
|
- if (flowtrack.num_flows > max_flows ||
|
|
|
+ if (flowtrack.num_flows > flowtrack.max_flows ||
|
|
|
next_expire(&flowtrack) == 0) {
|
|
|
expiry_check:
|
|
|
/*
|
|
@@ -1895,15 +1953,17 @@ expiry_check:
|
|
|
* expire flows when the flow table is full.
|
|
|
*/
|
|
|
if (check_expired(&flowtrack, &target,
|
|
|
- capfile == NULL ? CE_EXPIRE_NORMAL : CE_EXPIRE_FORCED) < 0)
|
|
|
+ capfile == NULL ? CE_EXPIRE_NORMAL :
|
|
|
+ CE_EXPIRE_FORCED) < 0)
|
|
|
logit(LOG_WARNING, "Unable to export flows");
|
|
|
|
|
|
/*
|
|
|
* If we are over max_flows, force-expire the oldest
|
|
|
* out first and immediately reprocess to evict them
|
|
|
*/
|
|
|
- if (flowtrack.num_flows > max_flows) {
|
|
|
- force_expire(&flowtrack, flowtrack.num_flows - max_flows);
|
|
|
+ if (flowtrack.num_flows > flowtrack.max_flows) {
|
|
|
+ force_expire(&flowtrack,
|
|
|
+ flowtrack.num_flows - flowtrack.max_flows);
|
|
|
goto expiry_check;
|
|
|
}
|
|
|
}
|