|
@@ -22,8 +22,6 @@
|
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
*/
|
|
|
|
|
|
-/* $Id$ */
|
|
|
-
|
|
|
/*
|
|
|
* This is software implementation of Cisco's NetFlow(tm) traffic
|
|
|
* reporting system. It operates by listening (via libpcap) on a
|
|
@@ -53,8 +51,6 @@
|
|
|
#include "log.h"
|
|
|
#include <pcap.h>
|
|
|
|
|
|
-RCSID("$Id$");
|
|
|
-
|
|
|
/* Global variables */
|
|
|
static int verbose_flag = 0; /* Debugging flag */
|
|
|
static u_int16_t if_index = 0; /* "manual" interface index */
|
|
@@ -99,7 +95,7 @@ static const struct DATALINK lt[] = {
|
|
|
|
|
|
/* Netflow send functions */
|
|
|
typedef int (netflow_send_func_t)(struct FLOW **, int, int, u_int16_t,
|
|
|
- u_int64_t *, struct timeval *, int);
|
|
|
+ u_int64_t *, struct timeval *, int, struct OPTION *);
|
|
|
struct NETFLOW_SENDER {
|
|
|
int version;
|
|
|
netflow_send_func_t *func;
|
|
@@ -270,7 +266,7 @@ format_flow(struct FLOW *flow)
|
|
|
snprintf(ftime, sizeof(ftime), "%s",
|
|
|
format_time(flow->flow_last.tv_sec));
|
|
|
|
|
|
- snprintf(buf, sizeof(buf), "seq:%llu [%s]:%hu <> [%s]:%hu proto:%u "
|
|
|
+ snprintf(buf, sizeof(buf), "seq:%"PRIu64" [%s]:%hu <> [%s]:%hu proto:%u "
|
|
|
"octets>:%u packets>:%u octets<:%u packets<:%u "
|
|
|
"start:%s.%03ld finish:%s.%03ld tcp>:%02x tcp<:%02x "
|
|
|
"flowlabel>:%08x flowlabel<:%08x ",
|
|
@@ -298,7 +294,7 @@ format_flow_brief(struct FLOW *flow)
|
|
|
inet_ntop(flow->af, &flow->addr[1], addr2, sizeof(addr2));
|
|
|
|
|
|
snprintf(buf, sizeof(buf),
|
|
|
- "seq:%llu [%s]:%hu <> [%s]:%hu proto:%u",
|
|
|
+ "seq:%"PRIu64" [%s]:%hu <> [%s]:%hu proto:%u",
|
|
|
flow->flow_seq,
|
|
|
addr1, ntohs(flow->port[0]), addr2, ntohs(flow->port[1]),
|
|
|
(int)flow->protocol);
|
|
@@ -811,7 +807,7 @@ check_expired(struct FLOWTRACK *ft, struct NETFLOW_TARGET *target, int ex)
|
|
|
|
|
|
if (verbose_flag)
|
|
|
logit(LOG_DEBUG,
|
|
|
- "Queuing flow seq:%llu (%p) for expiry "
|
|
|
+ "Queuing flow seq:%"PRIu64" (%p) for expiry "
|
|
|
"reason %d", expiry->flow->flow_seq,
|
|
|
expiry->flow, expiry->reason);
|
|
|
|
|
@@ -851,7 +847,7 @@ check_expired(struct FLOWTRACK *ft, struct NETFLOW_TARGET *target, int ex)
|
|
|
if (target != NULL && target->fd != -1) {
|
|
|
r = target->dialect->func(expired_flows, num_expired,
|
|
|
target->fd, if_index, &ft->flows_exported,
|
|
|
- &ft->system_boot_time, verbose_flag);
|
|
|
+ &ft->system_boot_time, verbose_flag, &ft->option);
|
|
|
if (verbose_flag)
|
|
|
logit(LOG_DEBUG, "sent %d netflow packets", r);
|
|
|
if (r > 0) {
|
|
@@ -976,13 +972,16 @@ statistics(struct FLOWTRACK *ft, FILE *out, pcap_t *pcap)
|
|
|
struct pcap_stat ps;
|
|
|
|
|
|
fprintf(out, "Number of active flows: %d\n", ft->num_flows);
|
|
|
- fprintf(out, "Packets processed: %llu\n", ft->total_packets);
|
|
|
- fprintf(out, "Fragments: %llu\n", ft->frag_packets);
|
|
|
- fprintf(out, "Ignored packets: %llu (%llu non-IP, %llu too short)\n",
|
|
|
+ fprintf(out, "Packets processed: %"PRIu64"\n", ft->total_packets);
|
|
|
+ if (ft->non_sampled_packets)
|
|
|
+ fprintf(out, "Packets non-sampled: %"PRIu64"\n",
|
|
|
+ ft->non_sampled_packets);
|
|
|
+ fprintf(out, "Fragments: %"PRIu64"\n", ft->frag_packets);
|
|
|
+ fprintf(out, "Ignored packets: %"PRIu64" (%"PRIu64" non-IP, %"PRIu64" too short)\n",
|
|
|
ft->non_ip_packets + ft->bad_packets, ft->non_ip_packets, ft->bad_packets);
|
|
|
- fprintf(out, "Flows expired: %llu (%llu forced)\n",
|
|
|
+ fprintf(out, "Flows expired: %"PRIu64" (%"PRIu64" forced)\n",
|
|
|
ft->flows_expired, ft->flows_force_expired);
|
|
|
- fprintf(out, "Flows exported: %llu in %llu packets (%llu failures)\n",
|
|
|
+ fprintf(out, "Flows exported: %"PRIu64" in %"PRIu64" packets (%"PRIu64" failures)\n",
|
|
|
ft->flows_exported, ft->packets_sent, ft->flows_dropped);
|
|
|
|
|
|
if (pcap_stats(pcap, &ps) == 0) {
|
|
@@ -1007,16 +1006,16 @@ 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,
|
|
|
+ fprintf(out, " tcp = %9"PRIu64" tcp.rst = %9"PRIu64" "
|
|
|
+ "tcp.fin = %9"PRIu64"\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,
|
|
|
+ fprintf(out, " udp = %9"PRIu64" icmp = %9"PRIu64" "
|
|
|
+ "general = %9"PRIu64"\n", ft->expired_udp, ft->expired_icmp,
|
|
|
ft->expired_general);
|
|
|
- fprintf(out, " maxlife = %9llu\n", ft->expired_maxlife);
|
|
|
- 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, " maxlife = %9"PRIu64"\n", ft->expired_maxlife);
|
|
|
+ fprintf(out, "over 2 GiB = %9"PRIu64"\n", ft->expired_overbytes);
|
|
|
+ fprintf(out, " maxflows = %9"PRIu64"\n", ft->expired_maxflows);
|
|
|
+ fprintf(out, " flushed = %9"PRIu64"\n", ft->expired_flush);
|
|
|
|
|
|
fprintf(out, "\n");
|
|
|
|
|
@@ -1027,7 +1026,7 @@ statistics(struct FLOWTRACK *ft, FILE *out, pcap_t *pcap)
|
|
|
pe = getprotobynumber(i);
|
|
|
snprintf(proto, sizeof(proto), "%s (%d)",
|
|
|
pe != NULL ? pe->p_name : "Unknown", i);
|
|
|
- fprintf(out, " %17s: %14llu %12llu %8.2fs "
|
|
|
+ fprintf(out, " %17s: %14"PRIu64" %12"PRIu64" %8.2fs "
|
|
|
"%10.2fs\n", proto,
|
|
|
ft->octets_pp[i],
|
|
|
ft->packets_pp[i],
|
|
@@ -1052,12 +1051,12 @@ dump_flows(struct FLOWTRACK *ft, FILE *out)
|
|
|
fprintf(out, "ACTIVE %s\n", format_flow(expiry->flow));
|
|
|
if ((long int) expiry->expires_at - now < 0) {
|
|
|
fprintf(out,
|
|
|
- "EXPIRY EVENT for flow %llu now%s\n",
|
|
|
+ "EXPIRY EVENT for flow %"PRIu64" now%s\n",
|
|
|
expiry->flow->flow_seq,
|
|
|
expiry->expires_at == 0 ? " (FORCED)": "");
|
|
|
} else {
|
|
|
fprintf(out,
|
|
|
- "EXPIRY EVENT for flow %llu in %ld seconds\n",
|
|
|
+ "EXPIRY EVENT for flow %"PRIu64" in %ld seconds\n",
|
|
|
expiry->flow->flow_seq,
|
|
|
(long int) expiry->expires_at - now);
|
|
|
}
|
|
@@ -1128,6 +1127,13 @@ flow_cb(u_char *user_data, const struct pcap_pkthdr* phdr,
|
|
|
struct CB_CTXT *cb_ctxt = (struct CB_CTXT *)user_data;
|
|
|
struct timeval tv;
|
|
|
|
|
|
+ if (cb_ctxt->ft->option.sample &&
|
|
|
+ (cb_ctxt->ft->total_packets +
|
|
|
+ cb_ctxt->ft->non_sampled_packets) %
|
|
|
+ cb_ctxt->ft->option.sample > 0) {
|
|
|
+ cb_ctxt->ft->non_sampled_packets++;
|
|
|
+ return;
|
|
|
+ }
|
|
|
s = datalink_check(cb_ctxt->linktype, pkt, phdr->caplen, &af);
|
|
|
if (s < 0 || (!cb_ctxt->want_v6 && af == AF_INET6)) {
|
|
|
cb_ctxt->ft->non_ip_packets++;
|
|
@@ -1496,6 +1502,7 @@ usage(void)
|
|
|
" NetFlow export protocol supports it\n"
|
|
|
" -d Don't daemonise (run in foreground)\n"
|
|
|
" -D Debug mode: foreground + verbosity + track v6 flows\n"
|
|
|
+" -s sampling_rate Specify periodical sampling rate (denominator)\n"
|
|
|
" -h Display this help\n"
|
|
|
"\n"
|
|
|
"Valid timeout names and default values:\n"
|
|
@@ -1707,7 +1714,7 @@ main(int argc, char **argv)
|
|
|
dontfork_flag = 0;
|
|
|
always_v6 = 0;
|
|
|
|
|
|
- while ((ch = getopt(argc, argv, "6hdDL:T:i:r:f:t:n:m:p:c:v:")) != -1) {
|
|
|
+ while ((ch = getopt(argc, argv, "6hdDL:T:i:r:f:t:n:m:p:c:v:s:")) != -1) {
|
|
|
switch (ch) {
|
|
|
case '6':
|
|
|
always_v6 = 1;
|
|
@@ -1807,6 +1814,12 @@ main(int argc, char **argv)
|
|
|
}
|
|
|
target.dialect = &nf[i];
|
|
|
break;
|
|
|
+ case 's':
|
|
|
+ flowtrack.option.sample = atoi(optarg);
|
|
|
+ if (flowtrack.option.sample < 2) {
|
|
|
+ flowtrack.option.sample = 0;
|
|
|
+ }
|
|
|
+ break;
|
|
|
default:
|
|
|
fprintf(stderr, "Invalid commandline option.\n");
|
|
|
usage();
|
|
@@ -1847,7 +1860,7 @@ main(int argc, char **argv)
|
|
|
} else {
|
|
|
FILE *pidfile;
|
|
|
|
|
|
- daemon(0, 0);
|
|
|
+ r = daemon(0, 0);
|
|
|
loginit(PROGNAME, 0);
|
|
|
|
|
|
if ((pidfile = fopen(pidfile_path, "w")) == NULL) {
|
|
@@ -1924,7 +1937,7 @@ main(int argc, char **argv)
|
|
|
logit(LOG_ERR, "Exiting on pcap_dispatch: %s",
|
|
|
pcap_geterr(pcap));
|
|
|
break;
|
|
|
- } else if (r == 0) {
|
|
|
+ } else if (r == 0 && capfile != NULL) {
|
|
|
logit(LOG_NOTICE, "Shutting down after "
|
|
|
"pcap EOF");
|
|
|
graceful_shutdown_request = 1;
|