Browse Source

Merge upstream version 0.9.8+hg20101101.b35a000870cc

Christoph Biedl 14 years ago
parent
commit
d2e72bbf0f
29 changed files with 682 additions and 9607 deletions
  1. 2 0
      .cvsignore
  2. 19 0
      .hgtags
  3. 41 1
      ChangeLog
  4. 5 4
      Makefile.in
  5. 4 1
      README
  6. 3 0
      TODO
  7. 1 1
      closefrom.c
  8. 0 163
      config.h.in
  9. 0 9163
      configure
  10. 2 2
      configure.ac
  11. 1 1
      convtime.c
  12. 166 0
      freelist.c
  13. 58 0
      freelist.h
  14. 3 3
      log.c
  15. 1 1
      log.h
  16. 1 1
      mkinstalldirs
  17. 8 5
      netflow1.c
  18. 6 3
      netflow5.c
  19. 37 23
      netflow9.c
  20. 19 14
      softflowctl.8
  21. 1 1
      softflowctl.c
  22. 136 118
      softflowd.8
  23. 152 92
      softflowd.c
  24. 11 5
      softflowd.h
  25. 1 1
      softflowd.init
  26. 1 1
      softflowd.spec
  27. 1 1
      strlcat.c
  28. 1 1
      strlcpy.c
  29. 1 1
      treetype.h

+ 2 - 0
.cvsignore

@@ -0,0 +1,2 @@
+softflowd
+softflowctl

+ 19 - 0
.hgtags

@@ -0,0 +1,19 @@
+0e7b1c83c22d691f0c2dc6a964d1334250f9be95 PRE_EXPIRY_RB_CONVERT
+0f4d543cfadd3f34162eb5e2785c057c548cfd22 SOFTFLOWD_0_3
+4680effcb255c612bc63f0ddedbea72f92151b59 SOFTFLOWD_0_9
+51c8e6737def4f6dd6de2692c98a8fda0d646a54 SOFTFLOWD_0_6
+7e447d5b8c8a7430bfaa234b45a06045b4ec04ba SOFTFLOWD_0_1
+7eaa764bbb3dbf71d5b329e61aed810bf7107f4a SOFTFLOWD_0_9_2
+8779cbdad42aaf29f981e2f9a7b617df8d2ac090 START
+9a9e2cd7e3b5dd05b63927acad9d0ca9bf39ce26 SOFTFLOWD_0_9_7
+9dae7a93ec5a71f921ea9ab8a46ad43942d28fc3 SOFTFLOWD_0_8
+b0c4bca0d46803f21e656a8c486e8a6ccb4fa676 SOFTFLOWD_0_2
+b7c24dd071091cbc74391e02799205c081edf8f1 SOFTFLOWD_0_8_1
+bcbc1910e8a68a3351d3bad37ca55558ff66d17c BEFORE_TREE_CHANGES
+bf9ccfda73346e315d37415d32a7c69653d4a245 SOFTFLOWD_0_5
+c025775ad959e68161ce6cf18a9ce9ffe35a92aa SOFTFLOWD_0_9_1
+ca5de0007bccbf42748d02630db01bb8a8961a92 SOFTFLOWD_0_7
+de48250d22aa20026d89fc715fff3bd27dcb0db1 PRE_BIDI
+eb6dc8d8b9c3166594cfa4bf5fd2c73c4a131820 SOFTFLOWD_0_7_1
+f81d538391377dc23346ddda4bd2fb0520daf6ea SOFTFLOWD_0_9_6
+ffda749c4623fcc78ea4a7ead6da6eeca64188aa SOFTFLOWD_0_9_8

+ 41 - 1
ChangeLog

@@ -3,6 +3,45 @@
    vs our template flowset. Patch from stephen AT sfnelson.org.
    https://bugzilla.mindrot.org/show_bug.cgi?id=1760
 
+20091001
+ - (djm) Lots of manpage tweaks from Tamas TEVESZ, ice AT extreme.hu
+ - (djm) Support manual specification of an interface index to be used
+   as the input and output interface of all flows generated. Patch from
+   kempf AT rpi.edu
+ - (djm) One more manpage tweak from Tamas TEVESZ.
+ - (djm) Display softflowd start time in "softflowctl statistics" display.
+   Suggestion from Tamas TEVESZ.
+
+20080515
+ - (djm) Fix typo in manpage for PID file location; patch from
+    ice AT extreme.hu
+ - (djm) Make privsep directory compile-time configurable; patch from
+    ice AT extreme.hu
+
+20070901
+ - (djm) Implement a very simple freelist allocator for flows and expiry
+   events
+
+20070831
+ - (djm) Move max_flows into struct FLOWTRACK
+
+20070726
+ - (djm) Add flow_get/flow_put and expiry_get/expiry_put functions to 
+   allocate and deallocate flows and expiry events, instead of calling
+   malloc/free directly. Right now these functions just call malloc/free
+   anyway, but they will soon be used to implemented pooled flow/expiry
+   allocations.
+
+20070725
+ - (djm) KNF
+ - (djm) Correctly exit from mainloop on signal - patch from Florian Weimer
+ - (djm) openlog with LOG_NDELAY so socket is connected before privdrop - 
+   patch from Florian Weimer
+
+20061102
+ - (djm) Document -v option and close Ed in manpage; from Nino Jogun
+   nino80 AT gmail.com
+
 20061101
  - (djm) Collect licenses into LICENSE file
  - (djm) malloc(x*y) -> calloc(x, y)
@@ -240,4 +279,5 @@
 20021024
  - (djm) Release softflowd-0.7.1
 
-$Id: ChangeLog,v 1.87 2006/11/02 06:36:16 djm Exp $
+$Id$
+

+ 5 - 4
Makefile.in

@@ -1,4 +1,4 @@
-# $Id: Makefile.in,v 1.9 2006/03/14 22:55:41 djm Exp $
+# $Id$
 
 prefix=@prefix@
 exec_prefix=@exec_prefix@
@@ -27,11 +27,12 @@ CFLAGS+=-DEXPIRY_RB		# Use red-black tree for expiry events
 TARGETS=softflowd${EXEEXT} softflowctl${EXEEXT}
 
 COMMON=convtime.o strlcpy.o strlcat.o closefrom.o daemon.o
+SOFTFLOWD=softflowd.o log.o netflow1.o netflow5.o netflow9.o freelist.o
 
 all: $(TARGETS)
 
-softflowd${EXEEXT}: log.o softflowd.o netflow1.o netflow5.o netflow9.o $(COMMON)
-	$(CC) $(LDFLAGS) -o $@ log.o netflow1.o netflow5.o netflow9.o softflowd.o $(COMMON) $(LIBS)
+softflowd${EXEEXT}: ${SOFTFLOWD} $(COMMON)
+	$(CC) $(LDFLAGS) -o $@ ${SOFTFLOWD} $(COMMON) $(LIBS)
 
 softflowctl${EXEEXT}: softflowctl.o $(COMMON)
 	$(CC) $(LDFLAGS) -o $@ softflowctl.o $(COMMON) $(LIBS)
@@ -43,7 +44,7 @@ realclean: clean
 	rm -rf autom4te.cache Makefile config.log config.status
 
 distclean: realclean
-	rm -f config.h
+	rm -f config.h* configure
 
 strip:
 	strip $(TARGETS)

+ 4 - 1
README

@@ -62,6 +62,9 @@ Softflowd has an extensive TODO list of interesting features, large and
 small, that are waiting to be implemented. If you are interested in
 helping, please contact me.
 
+The latest source code may be obtained from Google Code:
+http://code.google.com/p/softflowd/
+
 Damien Miller <djm@mindrot.org>
 
-$Id: README,v 1.7 2006/11/02 06:34:18 djm Exp $
+$Id$

+ 3 - 0
TODO

@@ -6,6 +6,8 @@ softflowd
  - Use strtonum()
 
  Flow tracking engine
+  - Calculate hash over flow and use it as a key to avoid lots of
+    cache-trashing comparisons
   - Verify checksums (maybe. perhaps bad for accounting, good for flow tracking)
   - Fragment processing
     - We don't handle fragments right
@@ -59,6 +61,7 @@ softflowd
     - Partly done, just need to keep a list of targets instead of a single one
   - Ability to directly write to file (maybe. If so, reuse flowd store code)
   - NetFlow v.9 field selection
+  - Get AS numbers from bgpd and fill in to Netflow packets
 
  Statistics code
   - Collect more statistics (maybe)

+ 1 - 1
closefrom.c

@@ -46,7 +46,7 @@
 # define OPEN_MAX	256
 #endif
 
-RCSID("$Id: closefrom.c,v 1.1 2004/09/10 09:08:08 djm Exp $");
+RCSID("$Id$");
 
 #ifndef lint
 static const char rcsid[] = "$Sudo: closefrom.c,v 1.6 2004/06/01 20:51:56 millert Exp $";

+ 0 - 163
config.h.in

@@ -1,163 +0,0 @@
-/* config.h.in.  Generated from configure.ac by autoheader.  */
-
-/* Define to 1 if you have the `closefrom' function. */
-#undef HAVE_CLOSEFROM
-
-/* Define to 1 if you have the `daemon' function. */
-#undef HAVE_DAEMON
-
-/* Define to 1 if the system has the type `int16_t'. */
-#undef HAVE_INT16_T
-
-/* Define to 1 if the system has the type `int32_t'. */
-#undef HAVE_INT32_T
-
-/* Define to 1 if the system has the type `int64_t'. */
-#undef HAVE_INT64_T
-
-/* Define to 1 if the system has the type `int8_t'. */
-#undef HAVE_INT8_T
-
-/* Define to 1 if you have the <inttypes.h> header file. */
-#undef HAVE_INTTYPES_H
-
-/* Define to 1 if you have the `pcap' library (-lpcap). */
-#undef HAVE_LIBPCAP
-
-/* Define to 1 if you have the <memory.h> header file. */
-#undef HAVE_MEMORY_H
-
-/* Define to 1 if you have the <net/bpf.h> header file. */
-#undef HAVE_NET_BPF_H
-
-/* Define to 1 if you have the <pcap-bpf.h> header file. */
-#undef HAVE_PCAP_BPF_H
-
-/* Define to 1 if you have the <pcap.h> header file. */
-#undef HAVE_PCAP_H
-
-/* Define to 1 if you have the `setgid' function. */
-#undef HAVE_SETGID
-
-/* Define to 1 if you have the `setresgid' function. */
-#undef HAVE_SETRESGID
-
-/* Define to 1 if you have the `setresuid' function. */
-#undef HAVE_SETRESUID
-
-/* Define to 1 if you have the `setreuid' function. */
-#undef HAVE_SETREUID
-
-/* Define to 1 if you have the <stdint.h> header file. */
-#undef HAVE_STDINT_H
-
-/* Define to 1 if you have the <stdlib.h> header file. */
-#undef HAVE_STDLIB_H
-
-/* Define to 1 if you have the <strings.h> header file. */
-#undef HAVE_STRINGS_H
-
-/* Define to 1 if you have the <string.h> header file. */
-#undef HAVE_STRING_H
-
-/* Define to 1 if you have the `strlcat' function. */
-#undef HAVE_STRLCAT
-
-/* Define to 1 if you have the `strlcpy' function. */
-#undef HAVE_STRLCPY
-
-/* struct ip6_ext.ip6e_nxt exists */
-#undef HAVE_STRUCT_IP6_EXT
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-#undef HAVE_SYS_STAT_H
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-#undef HAVE_SYS_TYPES_H
-
-/* Define to 1 if the system has the type `uint16_t'. */
-#undef HAVE_UINT16_T
-
-/* Define to 1 if the system has the type `uint32_t'. */
-#undef HAVE_UINT32_T
-
-/* Define to 1 if the system has the type `uint64_t'. */
-#undef HAVE_UINT64_T
-
-/* Define to 1 if the system has the type `uint8_t'. */
-#undef HAVE_UINT8_T
-
-/* Define to 1 if you have the <unistd.h> header file. */
-#undef HAVE_UNISTD_H
-
-/* Define to 1 if the system has the type `u_int16_t'. */
-#undef HAVE_U_INT16_T
-
-/* Define to 1 if the system has the type `u_int32_t'. */
-#undef HAVE_U_INT32_T
-
-/* Define to 1 if the system has the type `u_int64_t'. */
-#undef HAVE_U_INT64_T
-
-/* Define to 1 if the system has the type `u_int8_t'. */
-#undef HAVE_U_INT8_T
-
-/* 16-bit signed int */
-#undef OUR_CFG_INT16_T
-
-/* 32-bit signed int */
-#undef OUR_CFG_INT32_T
-
-/* 64-bit signed int */
-#undef OUR_CFG_INT64_T
-
-/* 8-bit signed int */
-#undef OUR_CFG_INT8_T
-
-/* 16-bit unsigned int */
-#undef OUR_CFG_U_INT16_T
-
-/* 32-bit unsigned int */
-#undef OUR_CFG_U_INT32_T
-
-/* 64-bit unsigned int */
-#undef OUR_CFG_U_INT64_T
-
-/* 8-bit unsigned int */
-#undef OUR_CFG_U_INT8_T
-
-/* Define to the address where bug reports for this package should be sent. */
-#undef PACKAGE_BUGREPORT
-
-/* Define to the full name of this package. */
-#undef PACKAGE_NAME
-
-/* Define to the full name and version of this package. */
-#undef PACKAGE_STRING
-
-/* Define to the one symbol short name of this package. */
-#undef PACKAGE_TARNAME
-
-/* Define to the version of this package. */
-#undef PACKAGE_VERSION
-
-/* The size of `char', as computed by sizeof. */
-#undef SIZEOF_CHAR
-
-/* The size of `int', as computed by sizeof. */
-#undef SIZEOF_INT
-
-/* The size of `long int', as computed by sizeof. */
-#undef SIZEOF_LONG_INT
-
-/* The size of `long long int', as computed by sizeof. */
-#undef SIZEOF_LONG_LONG_INT
-
-/* The size of `short int', as computed by sizeof. */
-#undef SIZEOF_SHORT_INT
-
-/* struct sockaddr contains length */
-#undef SOCK_HAS_LEN
-
-/* Define to 1 if you have the ANSI C header files. */
-#undef STDC_HEADERS

File diff suppressed because it is too large
+ 0 - 9163
configure


+ 2 - 2
configure.ac

@@ -1,4 +1,4 @@
-# $Id: configure.ac,v 1.5 2004/11/08 23:03:28 djm Exp $
+# $Id$
 #
 # Copyright (c) 2004 Damien Miller
 #
@@ -26,7 +26,7 @@ WFLAGS="-Wall -Waggregate-return -Wcast-align -Wcast-qual"
 WFLAGS="$WFLAGS -Wmissing-declarations -Wmissing-prototypes"
 WFLAGS="$WFLAGS -Wno-conversion -Wpointer-arith -Wshadow"
 WFLAGS="$WFLAGS -Wuninitialized -Wcast-align -Wcast-qual"
-WFLAGS="$WFLAGS -WformatC=2 -Wformat-nonliteral -Wwrite-strings" 
+WFLAGS="$WFLAGS -Wformat=2 -Wformat-nonliteral -Wwrite-strings" 
 
 # Process flag arguments early, so they are available for tests later
 AC_ARG_ENABLE(gcc-warnings,

+ 1 - 1
convtime.c

@@ -25,7 +25,7 @@
 #include "common.h"
 #include "convtime.h"
 
-RCSID("$Id: convtime.c,v 1.2 2004/09/10 09:08:08 djm Exp $");
+RCSID("$Id$");
 
 #define SECONDS		1
 #define MINUTES		(SECONDS * 60)

+ 166 - 0
freelist.c

@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2007 Damien Miller.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "common.h"
+#include "freelist.h"
+#include "log.h"
+
+#define FREELIST_MAX_ALLOC	0x1000000
+#define FREELIST_ALLOC_ALIGN	16
+#define FREELIST_INITIAL_ALLOC	16
+
+#ifndef roundup
+#define roundup(x, y) ((((x) + (y) - 1)/(y))*(y))
+#endif /* roundup */
+
+#undef FREELIST_DEBUG
+#ifdef FREELIST_DEBUG
+# define FLOGIT(a) logit a
+#else
+# define FLOGIT(a)
+#endif
+
+void
+freelist_init(struct freelist *fl, size_t allocsz)
+{
+	FLOGIT((LOG_DEBUG, "%s: %s(%p, %zu)", __func__, __func__, fl, allocsz));
+	bzero(fl, sizeof(fl));
+	fl->allocsz = roundup(allocsz, FREELIST_ALLOC_ALIGN);
+	fl->free_entries = NULL;
+}
+
+static int
+freelist_grow(struct freelist *fl)
+{
+	size_t i, oldnalloc, need;
+	void *p;
+
+	FLOGIT((LOG_DEBUG, "%s: %s(%p)", __func__, __func__, fl));
+	FLOGIT((LOG_DEBUG, "%s: nalloc = %zu", __func__, fl->nalloc));
+
+	/* Sanity check */
+	if (fl->nalloc > FREELIST_MAX_ALLOC)
+		return -1;
+
+	oldnalloc = fl->nalloc;
+	if (fl->nalloc == 0)
+		fl->nalloc = FREELIST_INITIAL_ALLOC;
+	else
+		fl->nalloc <<= 1;
+	if (fl->nalloc > FREELIST_MAX_ALLOC)
+		fl->nalloc = FREELIST_MAX_ALLOC;
+	FLOGIT((LOG_DEBUG, "%s: nalloc now %zu", __func__, fl->nalloc));
+
+	/* Check for integer overflow */
+	if (SIZE_MAX / fl->nalloc < fl->allocsz ||
+	    SIZE_MAX / fl->nalloc < sizeof(*fl->free_entries)) {
+		FLOGIT((LOG_DEBUG, "%s: integer overflow", __func__));
+ resize_fail:
+		fl->nalloc = oldnalloc;
+		return -1;
+	}
+
+	/* Allocate freelist - max size of nalloc */
+	need = fl->nalloc * sizeof(*fl->free_entries);
+	if ((p = realloc(fl->free_entries, need)) == NULL) {
+		FLOGIT((LOG_DEBUG, "%s: realloc(%zu) failed", __func__, need));
+		goto resize_fail;
+	}
+
+	/* Allocate the entries */
+	fl->free_entries = p;
+	need = (fl->nalloc - oldnalloc) * fl->allocsz;
+	if ((p = malloc(need)) == NULL) {
+		FLOGIT((LOG_DEBUG, "%s: malloc(%zu) failed", __func__, need));
+		goto resize_fail;
+	}
+	/*
+	 * XXX store these malloc ranges in a tree or list, so we can
+	 * validate them in _get/_put. Check that r_low <= addr < r_high, and
+	 * (addr - r_low) % fl->allocsz == 0
+	 */
+
+	fl->navail = fl->nalloc - oldnalloc;
+	for (i = 0; i < fl->navail; i++)
+		fl->free_entries[i] = (u_char *)p + (i * fl->allocsz);
+	for (i = fl->navail; i < fl->nalloc; i++)
+		fl->free_entries[i] = NULL;
+
+	FLOGIT((LOG_DEBUG, "%s: done, navail = %zu", __func__, fl->navail));
+	return 0;
+}
+
+void *
+freelist_get(struct freelist *fl)
+{
+	void *r;
+
+	FLOGIT((LOG_DEBUG, "%s: %s(%p)", __func__, __func__, fl));
+	FLOGIT((LOG_DEBUG, "%s: navail = %zu", __func__, fl->navail));
+
+	if (fl->navail == 0) {
+		if (freelist_grow(fl) == -1)
+			return NULL;
+	}
+
+	/* Sanity check */
+	if (fl->navail == 0 || fl->navail > FREELIST_MAX_ALLOC ||
+	    fl->free_entries[fl->navail - 1] == NULL) {
+		logit(LOG_ERR, "%s: invalid navail", __func__);
+		raise(SIGSEGV);
+	}
+
+	fl->navail--;
+	r = fl->free_entries[fl->navail];
+	fl->free_entries[fl->navail] = NULL;
+
+	FLOGIT((LOG_DEBUG, "%s: done, navail = %zu", __func__, fl->navail));
+	return r;
+}
+
+void
+freelist_put(struct freelist *fl, void *p)
+{
+	FLOGIT((LOG_DEBUG, "%s: %s(%p, %zu)", __func__, __func__, fl, p));
+	FLOGIT((LOG_DEBUG, "%s: navail = %zu", __func__, fl->navail));
+	FLOGIT((LOG_DEBUG, "%s: nalloc = %zu", __func__, fl->navail));
+
+	/* Sanity check */
+	if (fl->navail >= fl->nalloc) {
+		logit(LOG_ERR, "%s: freelist navail >= nalloc", __func__);
+		raise(SIGSEGV);
+	}
+	if (fl->free_entries[fl->navail] != NULL) {
+		logit(LOG_ERR, "%s: free_entries[%lu] != NULL",
+		    __func__, (unsigned long)fl->navail);
+		raise(SIGSEGV);
+	}
+
+	fl->free_entries[fl->navail] = p;
+	fl->navail++;
+
+	FLOGIT((LOG_DEBUG, "%s: done, navail = %zu", __func__, fl->navail));
+}
+
+

+ 58 - 0
freelist.h

@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2007 Damien Miller.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _FREELIST_H
+#define _FREELIST_H
+
+#include "common.h"
+
+/* Simple freelist of fixed-sized allocations */
+struct freelist {
+	size_t allocsz;
+	size_t nalloc;
+	size_t navail;
+	void **free_entries;
+};
+
+/*
+ * Initialise a freelist.
+ * allocsz is the size of the individual allocations
+ */
+void freelist_init(struct freelist *freelist, size_t allocsz);
+
+/*
+ * Get an entry from a freelist.
+ * Will allocate new entries if necessary
+ * Returns pointer to allocated memory or NULL on failure.
+ */
+void *freelist_get(struct freelist *freelist);
+
+/*
+ * Returns an entry to the freelist.
+ * p must be a pointer to an allocation from the freelist.
+ */
+void freelist_put(struct freelist *freelist, void *p);
+
+#endif /* FREELIST_H */
+

+ 3 - 3
log.c

@@ -22,13 +22,13 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-/* $Id: log.c,v 1.2 2004/09/10 09:08:08 djm Exp $ */
+/* $Id$ */
 
 #include "common.h"
 #include "log.h"
 #include <stdarg.h>
 
-RCSID("$Id: log.c,v 1.2 2004/09/10 09:08:08 djm Exp $");
+RCSID("$Id$");
 
 static int logstderr = 0;
 
@@ -38,7 +38,7 @@ loginit(const char *ident, int to_stderr)
 	if (to_stderr)
 		logstderr = 1;
 	else
-		openlog(PROGNAME, LOG_PID | LOG_NDELAY, LOG_DAEMON);
+		openlog(PROGNAME, LOG_PID|LOG_NDELAY, LOG_DAEMON);
 }
 
 void

+ 1 - 1
log.h

@@ -22,7 +22,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-/* $Id: log.h,v 1.1 2004/04/16 01:51:51 djm Exp $ */
+/* $Id$ */
 
 #ifndef _LOG_H
 #define _LOG_H

+ 1 - 1
mkinstalldirs

@@ -4,7 +4,7 @@
 # Created: 1993-05-16
 # Public domain
 
-# $Id: mkinstalldirs,v 1.1 2005/12/22 02:23:41 djm Exp $
+# $Id$
 
 errstatus=0
 

+ 8 - 5
netflow1.c

@@ -22,14 +22,14 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-/* $Id: netflow1.c,v 1.2 2005/05/05 03:31:42 djm Exp $ */
+/* $Id$ */
 
 #include "common.h"
 #include "log.h"
 #include "treetype.h"
 #include "softflowd.h"
 
-RCSID("$Id: netflow1.c,v 1.2 2005/05/05 03:31:42 djm Exp $");
+RCSID("$Id$");
 
 /*
  * This is the Cisco Netflow(tm) version 1 packet format
@@ -64,7 +64,7 @@ struct NF1_FLOW {
  * Returns number of packets sent or -1 on error
  */
 int
-send_netflow_v1(struct FLOW **flows, int num_flows, int nfsock,
+send_netflow_v1(struct FLOW **flows, int num_flows, int nfsock, u_int16_t ifidx,
     u_int64_t *flows_exported, struct timeval *system_boot_time, 
     int verbose_flag)
 {
@@ -103,8 +103,10 @@ send_netflow_v1(struct FLOW **flows, int num_flows, int nfsock,
 			hdr->time_nanosec = htonl(now.tv_usec * 1000);
 			offset = sizeof(*hdr);
 		}		
+
 		flw = (struct NF1_FLOW *)(packet + offset);
-		
+		flw->if_index_in = flw->if_index_out = htons(ifidx);
+
 		/* NetFlow v.1 doesn't do IPv6 */
 		if (flows[i]->af != AF_INET)
 			continue;
@@ -127,8 +129,9 @@ send_netflow_v1(struct FLOW **flows, int num_flows, int nfsock,
 			j++;
 			hdr->flows++;
 		}
-		flw = (struct NF1_FLOW *)(packet + offset);
 
+		flw = (struct NF1_FLOW *)(packet + offset);
+		flw->if_index_in = flw->if_index_out = htons(ifidx);
 		if (flows[i]->octets[1] > 0) {
 			flw->src_ip = flows[i]->addr[1].v4.s_addr;
 			flw->dest_ip = flows[i]->addr[0].v4.s_addr;

+ 6 - 3
netflow5.c

@@ -22,14 +22,14 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-/* $Id: netflow5.c,v 1.3 2005/05/05 03:31:42 djm Exp $ */
+/* $Id$ */
 
 #include "common.h"
 #include "log.h"
 #include "treetype.h"
 #include "softflowd.h"
 
-RCSID("$Id: netflow5.c,v 1.3 2005/05/05 03:31:42 djm Exp $");
+RCSID("$Id$");
 
 /*
  * This is the Cisco Netflow(tm) version 5 packet format
@@ -62,7 +62,7 @@ struct NF5_FLOW {
  * Returns number of packets sent or -1 on error
  */
 int
-send_netflow_v5(struct FLOW **flows, int num_flows, int nfsock,
+send_netflow_v5(struct FLOW **flows, int num_flows, int nfsock, u_int16_t ifidx,
     u_int64_t *flows_exported, struct timeval *system_boot_time,
     int verbose_flag)
 {
@@ -104,6 +104,7 @@ send_netflow_v5(struct FLOW **flows, int num_flows, int nfsock,
 			offset = sizeof(*hdr);
 		}		
 		flw = (struct NF5_FLOW *)(packet + offset);
+		flw->if_index_in = flw->if_index_out = htons(ifidx);
 
 		/* NetFlow v.5 doesn't do IPv6 */
 		if (flows[i]->af != AF_INET)
@@ -127,7 +128,9 @@ send_netflow_v5(struct FLOW **flows, int num_flows, int nfsock,
 			j++;
 			hdr->flows++;
 		}
+
 		flw = (struct NF5_FLOW *)(packet + offset);
+		flw->if_index_in = flw->if_index_out = htons(ifidx);
 
 		if (flows[i]->octets[1] > 0) {
 			flw->src_ip = flows[i]->addr[1].v4.s_addr;

+ 37 - 23
netflow9.c

@@ -22,14 +22,14 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-/* $Id: netflow9.c,v 1.9 2006/03/16 08:23:13 djm Exp $ */
+/* $Id$ */
 
 #include "common.h"
 #include "log.h"
 #include "treetype.h"
 #include "softflowd.h"
 
-RCSID("$Id: netflow9.c,v 1.9 2006/03/16 08:23:13 djm Exp $");
+RCSID("$Id$");
 
 /* Netflow v.9 */
 struct NF9_HEADER {
@@ -64,9 +64,12 @@ struct NF9_DATA_FLOWSET_HEADER {
 #define NF9_L4_SRC_PORT			7
 #define NF9_IPV4_SRC_ADDR		8
 /* ... */
+#define NF9_IF_INDEX_IN			10
 #define NF9_L4_DST_PORT			11
 #define NF9_IPV4_DST_ADDR		12
 /* ... */
+#define NF9_IF_INDEX_OUT		14
+/* ... */
 #define NF9_LAST_SWITCHED		21
 #define NF9_FIRST_SWITCHED		22
 /* ... */
@@ -86,6 +89,7 @@ struct NF9_SOFTFLOWD_TEMPLATE {
 struct NF9_SOFTFLOWD_DATA_COMMON {
 	u_int32_t last_switched, first_switched;
 	u_int32_t bytes, packets;
+	u_int32_t if_index_in, if_index_out;
 	u_int16_t src_port, dst_port;
 	u_int8_t protocol, tcp_flags, ipproto;
 } __packed;
@@ -131,16 +135,20 @@ nf9_init_template(void)
 	v4_template.r[4].length = htons(4);
 	v4_template.r[5].type = htons(NF9_IN_PACKETS);
 	v4_template.r[5].length = htons(4);
-	v4_template.r[6].type = htons(NF9_L4_SRC_PORT);
-	v4_template.r[6].length = htons(2);
-	v4_template.r[7].type = htons(NF9_L4_DST_PORT);
-	v4_template.r[7].length = htons(2);
-	v4_template.r[8].type = htons(NF9_IN_PROTOCOL);
-	v4_template.r[8].length = htons(1);
-	v4_template.r[9].type = htons(NF9_TCP_FLAGS);
-	v4_template.r[9].length = htons(1);
-	v4_template.r[10].type = htons(NF9_IP_PROTOCOL_VERSION);
+	v4_template.r[6].type = htons(NF9_IF_INDEX_IN);
+	v4_template.r[6].length = htons(4);
+	v4_template.r[7].type = htons(NF9_IF_INDEX_OUT);
+	v4_template.r[7].length = htons(4);
+	v4_template.r[8].type = htons(NF9_L4_SRC_PORT);
+	v4_template.r[8].length = htons(2);
+	v4_template.r[9].type = htons(NF9_L4_DST_PORT);
+	v4_template.r[9].length = htons(2);
+	v4_template.r[10].type = htons(NF9_IN_PROTOCOL);
 	v4_template.r[10].length = htons(1);
+	v4_template.r[11].type = htons(NF9_TCP_FLAGS);
+	v4_template.r[11].length = htons(1);
+	v4_template.r[12].type = htons(NF9_IP_PROTOCOL_VERSION);
+	v4_template.r[12].length = htons(1);
 
 	bzero(&v6_template, sizeof(v6_template));
 	v6_template.h.c.flowset_id = htons(0);
@@ -159,21 +167,25 @@ nf9_init_template(void)
 	v6_template.r[4].length = htons(4);
 	v6_template.r[5].type = htons(NF9_IN_PACKETS);
 	v6_template.r[5].length = htons(4);
-	v6_template.r[6].type = htons(NF9_L4_SRC_PORT);
-	v6_template.r[6].length = htons(2);
-	v6_template.r[7].type = htons(NF9_L4_DST_PORT);
-	v6_template.r[7].length = htons(2);
-	v6_template.r[8].type = htons(NF9_IN_PROTOCOL);
-	v6_template.r[8].length = htons(1);
-	v6_template.r[9].type = htons(NF9_TCP_FLAGS);
-	v6_template.r[9].length = htons(1);
-	v6_template.r[10].type = htons(NF9_IP_PROTOCOL_VERSION);
+	v4_template.r[6].type = htons(NF9_IF_INDEX_IN);
+	v4_template.r[6].length = htons(4);
+	v4_template.r[7].type = htons(NF9_IF_INDEX_OUT);
+	v4_template.r[7].length = htons(4);
+	v6_template.r[8].type = htons(NF9_L4_SRC_PORT);
+	v6_template.r[8].length = htons(2);
+	v6_template.r[9].type = htons(NF9_L4_DST_PORT);
+	v6_template.r[9].length = htons(2);
+	v6_template.r[10].type = htons(NF9_IN_PROTOCOL);
 	v6_template.r[10].length = htons(1);
+	v6_template.r[11].type = htons(NF9_TCP_FLAGS);
+	v6_template.r[11].length = htons(1);
+	v6_template.r[12].type = htons(NF9_IP_PROTOCOL_VERSION);
+	v6_template.r[12].length = htons(1);
 }
 
 static int
 nf_flow_to_flowset(const struct FLOW *flow, u_char *packet, u_int len,
-    const struct timeval *system_boot_time, u_int *len_used)
+    u_int16_t ifidx, const struct timeval *system_boot_time, u_int *len_used)
 {
 	union {
 		struct NF9_SOFTFLOWD_DATA_V4 d4;
@@ -217,6 +229,8 @@ nf_flow_to_flowset(const struct FLOW *flow, u_char *packet, u_int len,
 	dc[1]->bytes = htonl(flow->octets[1]);
 	dc[0]->packets = htonl(flow->packets[0]);
 	dc[1]->packets = htonl(flow->packets[1]);
+	dc[0]->if_index_in = dc[0]->if_index_out = htonl(ifidx);
+	dc[1]->if_index_in = dc[1]->if_index_out = htonl(ifidx);
 	dc[0]->src_port = dc[1]->dst_port = flow->port[0];
 	dc[1]->src_port = dc[0]->dst_port = flow->port[1];
 	dc[0]->protocol = dc[1]->protocol = flow->protocol;
@@ -247,7 +261,7 @@ nf_flow_to_flowset(const struct FLOW *flow, u_char *packet, u_int len,
  * Returns number of packets sent or -1 on error
  */
 int
-send_netflow_v9(struct FLOW **flows, int num_flows, int nfsock,
+send_netflow_v9(struct FLOW **flows, int num_flows, int nfsock, u_int16_t ifidx,
     u_int64_t *flows_exported, struct timeval *system_boot_time,
     int verbose_flag)
 {
@@ -321,7 +335,7 @@ send_netflow_v9(struct FLOW **flows, int num_flows, int nfsock,
 			}
 
 			r = nf_flow_to_flowset(flows[i + j], packet + offset,
-			    sizeof(packet) - offset, system_boot_time, &inc);
+			    sizeof(packet) - offset, ifidx, system_boot_time, &inc);
 			if (r <= 0) {
 				/* yank off data header, if we had to go back */
 				if (last_valid)

+ 19 - 14
softflowctl.8

@@ -1,4 +1,4 @@
-.\" $Id: softflowctl.8,v 1.2 2006/03/16 08:24:19 djm Exp $
+.\" $Id$
 .\"
 .\" Copyright (c) 2002 Damien Miller.  All rights reserved.
 .\"
@@ -36,17 +36,17 @@
 .Sh DESCRIPTION
 .Nm
 is a remote control program used to control a running
-.Xr softflowd 8 
+.Xr softflowd 8
 daemon.
 .Pp
-The command-line options are as follows:
+The command line options are as follows:
 .Bl -tag -width Ds
 .It Fl c Ar ctlsock
 Specify an alternate location for the remote control socket.
-Default is 
+Default is
 .Pa /var/run/softflowd.ctl
 .It Fl h
-Displays commandline usage information.
+Display command line usage information.
 .El
 .Pp
 .Sh COMMANDS
@@ -54,25 +54,27 @@ Displays commandline usage information.
 .It Pa shutdown
 Ask
 .Xr softflowd 8
-to gracefully exit. This is equivalent to sending it a 
-.Dv SIGTERM 
-or 
+to gracefully exit.
+This is equivalent to sending it a
+.Dv SIGTERM
+or
 .Dv SIGINT .
 .It Pa exit
 Ask
 .Xr softflowd 8
-to immediately exit. No flow expiry processing or data export is performed.
+to immediately exit.
+No flow expiry processing or data export is performed.
 .It Pa expire-all
 Immediately expire all tracked flows.
 .It Pa delete-all
-Immediately delete all tracked flows. No flow expiry processing or data 
-export is performed.
+Immediately delete all tracked flows.
+No flow expiry processing or data export is performed.
 .It Pa statistics
-Return statistics collected by 
+Return statistics collected by
 .Xr softflowd 8
 on expired flows.
 .It Pa debug+
-Increase the debugging level of 
+Increase the debugging level of
 .Xr softflowd 8
 .It Pa debug-
 Decrease the debugging level.
@@ -89,7 +91,10 @@ Print information on flow timeout parameters.
 Resend a NetFlow v.9 template record before the next flow export.
 Has no effect for other flow export versions.
 .El
+.Sh BUGS
+All times are unconditionally displayed in UTC, regardless of the system
+timezone.
 .Sh AUTHORS
-Damien Miller <djm@mindrot.org>
+.An Damien Miller Aq djm@mindrot.org
 .Sh SEE ALSO
 .Xr softflowd 8

+ 1 - 1
softflowctl.c

@@ -24,7 +24,7 @@
 
 #include "common.h"
 
-RCSID("$Id: softflowctl.c,v 1.6 2004/09/10 09:20:56 djm Exp $");
+RCSID("$Id$");
 
 static void
 usage(void)

+ 136 - 118
softflowd.8

@@ -1,4 +1,4 @@
-.\" $Id: softflowd.8,v 1.17 2006/02/11 11:27:38 djm Exp $
+.\" $Id$
 .\"
 .\" Copyright (c) 2002 Damien Miller.  All rights reserved.
 .\" Portions Copyright (c) 2001 Kevin Steves.  All rights reserved.
@@ -32,33 +32,40 @@
 .Sh SYNOPSIS
 .Nm softflowd
 .Op Fl 6dDh
-.Op Fl i Ar interface
-.Op Fl r Ar pcap_file
-.Op Fl t Ar timeout_name=seconds
+.Op Fl L Ar hoplimit
+.Op Fl T Ar track_level
+.Op Fl c Ar ctl_sock
+.Ek
+.Oo Fl i\ \&
+.Sm off
+.Oo Ar if_ndx : Oc
+.Ar interface
+.Sm on
+.Oc
+.Bk words
 .Op Fl m Ar max_flows
 .Op Fl n Ar host:port
 .Op Fl p Ar pidfile
-.Op Fl c Ar ctl_sock
-.Op Fl L Ar hoplimit
-.Op Fl T Ar track_level
-.Op bpf_program
+.Op Fl r Ar pcap_file
+.Op Fl t Ar timeout_name=seconds
+.Op Fl v Ar netflow_version
+.Op bpf_expression
 .Sh DESCRIPTION
 .Nm
-is a software implementation of a flow-based network traffic monitor. 
+is a software implementation of a flow-based network traffic monitor.
 .Nm
-reads network traffic and gathers information about active traffic flows. 
-A "traffic flow" is 
-communication between two IP addresses or (if the overlying protocol is 
-TCP or UDP) address/port tuples. 
+reads network traffic and gathers information about active traffic flows.
+A "traffic flow" is communication between two IP addresses or (if the
+overlying protocol is TCP or UDP) address/port tuples.
 .Pp
 The intended use of
-.Nm 
+.Nm
 is as a software implementation of Cisco's NetFlow(tm) traffic account
 system.
-.Nm 
-supports data export using versions 1, 5 or 9 of the NetFlow protocol. 
-.Nm 
-can also run in statistics-only mode, where it just collects summary 
+.Nm
+supports data export using versions 1, 5 or 9 of the NetFlow protocol.
+.Nm
+can also run in statistics-only mode, where it just collects summary
 information.
 However, too few statistics are collected to make this
 mode really useful for anything other than debugging.
@@ -66,33 +73,33 @@ mode really useful for anything other than debugging.
 Network traffic may be obtained by listening on a promiscuous network
 interface or by reading stored
 .Xr pcap 3
-files, such as those written by 
+files, such as those written by
 .Xr tcpdump 8 .
 Traffic may be filtered with an optional
 .Xr bpf 4
 program, specified on the command-line as
-.Ar bpf_prog .
+.Ar bpf_expression .
 .Nm
-is IPv6 capable and will track IPv6 flows if the NetFlow export protocol 
-supports it (currently only NetFlow v.9 possesses an IPv6 export capability). 
+is IPv6 capable and will track IPv6 flows if the NetFlow export protocol
+supports it (currently only NetFlow v.9 possesses an IPv6 export capability).
 .Pp
 .Nm
 tries to track only active traffic flows.
 When the
-flow has been quiescent for a period of time it is expired automatically. 
-Flows may also be expired early if they approach their traffic counts 
-exceed 2Gb or if the number of flows being tracked exceeds 
+flow has been quiescent for a period of time it is expired automatically.
+Flows may also be expired early if they approach their traffic counts
+exceed 2 Gib or if the number of flows being tracked exceeds
 .Ar max_flows
 (default: 8192).
-In this last case, flows are expired oldest-first. 
+In this last case, flows are expired oldest-first.
 .Pp
 Upon expiry, the flow information is accumulated into statistics which may
-be viewed using 
+be viewed using
 .Xr softflowctl 8 .
-If the 
+If the
 .Fl n
-option has been specified the flow informaion is formatted in a UDP datagram 
-which is compatible with versions 1, 5 or 9 of Cisco's NetFlow(tm) accounting 
+option has been specified the flow information is formatted in a UDP datagram
+which is compatible with versions 1, 5 or 9 of Cisco's NetFlow(tm) accounting
 export format.
 These records are sent to the specified
 .Ar host
@@ -103,52 +110,58 @@ The host may represent a unicast host or a multicast group.
 The command-line options are as follows:
 .Bl -tag -width Ds
 .It Fl n Ar host:port
-Specify the 
+Specify the
 .Ar host
-and 
+and
 .Ar port
 that the accounting datagrams are to be sent to.
-The host may be specified using a hostname or using a numeric IPv4 or 
+The host may be specified using a hostname or using a numeric IPv4 or
 IPv6 address.
 Numeric IPv6 addresses should be encosed in square brackets to avoid ambiguity
 between the address and the port.
-The destination port may be a portname listed in 
+The destination port may be a portname listed in
 .Xr services 5
 or a numeric port.
-.It Fl i Ar interface
+.It Fl i Xo
+.Sm off
+.Oo Ar if_ndx : Oc
+.Ar interface
+.Sm on
+.Xc
 Specify a network interface on which to listen for traffic.
-Either the 
+Either the
 .Fl i
 or the
-.Fl r 
+.Fl r
 options must be specified.
 .It Fl r Ar pcap_file
 Specify that
 .Nm
-should read from a 
+should read from a
 .Xr pcap 3
-packet capture (such as one created with the 
+packet capture file (such as one created with the
 .Fl w
-option of 
+option of
 .Xr tcpdump 8 )
-file rather than a network interface. 
+file rather than a network interface.
 .Nm
-processes the whole capture file and only expires flows when 
+processes the whole capture file and only expires flows when
 .Ar max_flows
-is exceeded. In this mode, 
+is exceeded.
+In this mode,
 .Nm
-will not fork and will automatically print summary statistics before 
+will not fork and will automatically print summary statistics before
 exiting.
 .It Fl p Ar pidfile
-Specify an alternate location to store the process-id when in daemon mode.
-Default is 
+Specify an alternate location to store the process ID when in daemon mode.
+Default is
 .Pa /var/run/softflowd.pid
 .It Fl c Ar ctlsock
 Specify an alternate location for the remote control socket in daemon mode.
-Default is 
-.Pa /var/run/softflowd.pid
+Default is
+.Pa /var/run/softflowd.ctl
 .It Fl m Ar max_flows
-Specifies the maximum number of flow to concurrently track.
+Specify the maximum number of flow to concurrently track.
 If this limit is exceeded, the flows which have least recently seen traffic
 are forcibly expired.
 In practice, the actual maximum may briefly exceed this limit by a
@@ -159,22 +172,22 @@ than 800k of working data.
 .It Fl t Ar timeout_name=time
 Set the timeout names
 .Ar timeout_name
-to 
-.Ar time 
-Refer to the 
+to
+.Ar time
+Refer to the
 .Sx Timeouts
 section for the valid timeout names and their meanings.
-The 
+The
 .Ar time
-parameter may be specified using one of the formats explained in the 
+parameter may be specified using one of the formats explained in the
 .Sx Time Formats
 section below.
 .It Fl d
-Specify that 
+Specify that
 .Nm
 should not fork and daemonise itself.
 .It Fl 6
-Forces
+Force
 .Nm
 To track IPv6 flows even if the NetFlow export protocol does not support
 reporting them.
@@ -183,22 +196,22 @@ This is useful for debugging and statistics gathering only.
 Places
 .Nm
 in a debugging mode.
-This implies the 
+This implies the
 .Fl d
 and
 .Fl 6
 flags and turns on additional debugging output.
 .It Fl h
-Displays commandline usage information.
+Display command-line usage information.
 .It Fl L Ar hoplimit
-Sets the IPv4 TTL or the IPv6 hop limit to
+Set the IPv4 TTL or the IPv6 hop limit to
 .Ar hoplimit .
 .Nm
 will use the default system TTL when exporting flows to a unicast host.
 When exporting to a multicast group, the default TTL will be 1
 (i.e. link-local).
 .It Fl T Ar track_level
-Specifies what flow elements
+Specify which flow elements
 .Nm
 should be used to define a flow.
 .Ar track_level
@@ -211,36 +224,41 @@ may be one of:
 (only track source and destination addresses).
 Selecting either of the latter options will produce flows with less information
 in them (e.g. TCP/UDP ports will not be recorded).
-This will cause flows to be consolidated, reducing the quantity of output 
+This will cause flows to be consolidated, reducing the quantity of output
 and CPU load that
 .Nm
-will place on the system at the cost of some detail.
+will place on the system at the cost of some detail being lost.
+.It Fl v Ar netflow_version
+Specify which version of the NetFlow(tm) protocol
+.Nm
+should use for export of the flow data.
+Supported versions are 1, 5 and 9.
+Default is version 5.
 .El
 .Pp
-Any further commandline arguments will be concatenated together and 
-applied as a 
+Any further command-line arguments will be concatenated together and
+applied as a
 .Xr bpf 4
 packet filter.
-This filter will cause 
+This filter will cause
 .Nm
 to ignore the specified traffic.
 .Ss Timeouts
 .Pp
 .Nm
 will expire quiescent flows after user-configurable periods.
-The exact 
-timeout used depends on the nature of the flow.
-The various timeouts that may be set from the command-line (using the 
-.Fl t 
+The exact timeout used depends on the nature of the flow.
+The various timeouts that may be set from the command-line (using the
+.Fl t
 option) and their meanings are:
 .Bl -tag -width Ds
 .It Ar general
-This is the general timeout applied to all traffic unless overridden by 
+This is the general timeout applied to all traffic unless overridden by
 one of the other timeouts.
 .It Ar tcp
 This is the general TCP timeout, applied to open TCP connections.
 .It Ar tcp.rst
-This timeout is applied to a TCP connection when a RST packet has been 
+This timeout is applied to a TCP connection when a RST packet has been
 sent by one or both endpoints.
 .It Ar tcp.fin
 This timeout is applied to a TCP connection when a FIN packet has been
@@ -249,31 +267,30 @@ sent by both endpoints.
 This is the general UDP timeout, applied to all UDP connections.
 .It Ar maxlife
 This is the maximum lifetime that a flow may exist for.
-All flows 
-are forcibly expired when they pass
+All flows are forcibly expired when they pass
 .Ar maxlife
 seconds.
-To disable this feature, specify a 
+To disable this feature, specify a
 .Ar maxlife
 of 0.
 .It Ar expint
 Specify the interval between expiry checks.
 Increase this to group more flows into a NetFlow packet.
-To disable this feature, specify a 
+To disable this feature, specify a
 .Ar expint
 of 0.
 .El
 .Pp
 Flows may also be expired if there are not enough flow entries to hold them
-or if their traffic exceeds 2Gb in either direction. 
+or if their traffic exceeds 2 Gib in either direction.
 .Xr softflowctl 8
-may be used to print information on the average lifetimes of flows and 
+may be used to print information on the average lifetimes of flows and
 the reasons for their expiry.
 .Ss Time Formats
 .Pp
 .Nm
-command-line arguments that specify time
-may be expressed using a sequence of the form:
+command-line arguments that specify time may be expressed using a sequence
+of the form:
 .Sm off
 .Ar time Op Ar qualifier ,
 .Sm on
@@ -298,8 +315,7 @@ days
 weeks
 .El
 .Pp
-Each member of the sequence is added together to calculate
-the total time value.
+Each member of the sequence is added together to calculate the total time value.
 .Pp
 Time format examples:
 .Pp
@@ -315,83 +331,85 @@ Time format examples:
 .Pp
 A daemonised
 .Nm
-instance may be controlled using the 
+instance may be controlled using the
 .Xr softflowctl 8
 command.
 This interface allows one to shut down the daemon, force expiry of
 all tracked flows and extract debugging and summary data.
-Also, upon 
-receipt of a 
+Also, upon receipt of a
 .Dv SIGTERM
-or 
-.Dv SIGINT
+or
+.DV SIGINT
 .Nm
-will cause 
+will cause
 .Nm
-to exit, after expiring all flows (and thus sending flow export packets 
-if 
-.Fl -n 
-was specified on the commandline).
+to exit, after expiring all flows (and thus sending flow export packets
+if
+.Fl n
+was specified on the command-line).
 If you do not want to export flows upon shutdown, clear them first with
-.Xr softflowctl 8 .
+.Xr softflowctl 8
+or use
+.Xr softflowctl 8 's
+.Dq exit
+command.
 .Sh EXAMPLES
 .Bl -tag -width Ds
 .It softflowd -i fxp0
-This commandlie will cause 
+This command-line will cause
 .Nm
-to listen on interface \fBfxp0\fP and
-to run in statistics gathering mode only (i.e no NetFlow data export).
-.It softflowd -i fxp0 -n10.1.0.2:4432
-This commandlie will cause
+to listen on interface fxp0 and to run in statistics gathering mode
+only (i.e. no NetFlow data export).
+.It softflowd -i fxp0 -n 10.1.0.2:4432
+This command-line will cause
 .Nm
-to listen on interface \fBfxp0\fP and
-to export NetFlow v.5 datagrams on flow expiry to a flow collector running
-on \fB10.1.0.2\fP port \fB4432\fP.
-.It softflowd -v 5 -i fxp0 -n10.1.0.2:4432 -m 65536 -t udp=1m30s
-This commandline increases the number of concurrent flows that 
+to listen on interface fxp0 and to export NetFlow v.5 datagrams on flow
+expiry to a flow collector running on 10.1.0.2 port 4432.
+.It softflowd -v 5 -i fxp0 -n 10.1.0.2:4432 -m 65536 -t udp=1m30s
+This command-line increases the number of concurrent flows that
 .Nm
-will track to \fB65536\fP and increases the timeout for UDP flows to 
-90 seconds.
-.It softflowd -v 9 -i fxp0 -n224.0.1.20:4432 -L 64
-This commandline will export NetFlow v.9 flows to the multicast group 
+will track to 65536 and increases the timeout for UDP flows to 90 seconds.
+.It softflowd -v 9 -i fxp0 -n 224.0.1.20:4432 -L 64
+This command-line will export NetFlow v.9 flows to the multicast group
 224.0.1.20.
 The export datagrams will have their TTL set to 64, so multicast receivers
 can be many hops away.
 .It softflowd -i fxp0 -p /var/run/sfd.pid.fxp0 -c /var/run/sfd.ctl.fxp0
-This commandline specifies alternate locations for the control socket
+This command-line specifies alternate locations for the control socket
 and pid file.
-Similar commandlines are useful when running multiple 
-instances of 
+Similar command-lines are useful when running multiple
+instances of
 .Nm
 on a single machine.
 .El
 .Sh FILES
 .Bl -tag -width Ds
 .It Pa /var/run/softflowd.pid
-This file stores the process-id when
+This file stores the process ID when
 .Nm
 is in daemon mode.
-This location may be overridden using the 
+This location may be overridden using the
 .Fl p
 command-line option.
 .It Pa /var/run/softflowd.ctl
-This is the remote control socket. 
-.Nm 
-listens on this socket for commands from 
-.Xr softflowctl 8 . This location may be overridden using the 
+This is the remote control socket.
+.Nm
+listens on this socket for commands from
+.Xr softflowctl 8 .
+This location may be overridden using the
 .Fl c
 command-line option.
 .El
 .Sh BUGS
 Currently
 .Nm
-does not handle maliciously fragmented packets properly, i.e. packets 
+does not handle maliciously fragmented packets properly, i.e. packets
 fragemented such that the UDP or TCP header does not fit into the first
 fragment.
-It will product correct traffic counts when presented with maliciously 
+It will product correct traffic counts when presented with maliciously
 fragmented packets, but will not record TCP or UDP port information.
 .Sh AUTHORS
-Damien Miller <djm@mindrot.org>
+.An Damien Miller Aq djm@mindrot.org
 .Sh SEE ALSO
 .Xr softflowctl 8 ,
 .Xr tcpdump 8 ,

+ 152 - 92
softflowd.c

@@ -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 volatile 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;
-	while (!graceful_shutdown_request) {
+
+	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;
 			}
 		}

+ 11 - 5
softflowd.h

@@ -27,6 +27,7 @@
 
 #include "common.h"
 #include "sys-tree.h"
+#include "freelist.h"
 #include "treetype.h"
 
 /* User to setuid to and directory to chroot to when we drop privs */
@@ -34,8 +35,9 @@
 # define PRIVDROP_USER		"nobody"
 #endif
 
-#define PRIVDROP_CHROOT_DIR	"/var/run/softflowd/chroot"
-
+#ifndef PRIVDROP_CHROOT_DIR
+# define PRIVDROP_CHROOT_DIR	"/var/empty"
+#endif
 /*
  * Capture length for libpcap: Must fit the link layer header, plus 
  * a maximally sized ip/ipv6 header and most of a TCP header
@@ -81,7 +83,11 @@ struct FLOWTRACK {
 	FLOW_HEAD(FLOWS, FLOW) flows;		/* Top of flow tree */
 	EXPIRY_HEAD(EXPIRIES, EXPIRY) expiries;	/* Top of expiries tree */
 
+	struct freelist flow_freelist;		/* Freelist for flows */
+	struct freelist expiry_freelist;	/* Freelist for expiry events */
+
 	unsigned int num_flows;			/* # of active flows */
+	unsigned int max_flows;			/* Max # of active flows */
 	u_int64_t next_flow_seq;		/* Next flow ID */
 
 	/* Stuff related to flow export */
@@ -195,13 +201,13 @@ u_int32_t timeval_sub_ms(const struct timeval *t1, const struct timeval *t2);
 
 /* Prototypes for functions to send NetFlow packets, from netflow*.c */
 int send_netflow_v1(struct FLOW **flows, int num_flows, int nfsock,
-    u_int64_t *flows_exported, struct timeval *system_boot_time, 
+    u_int16_t ifidx, u_int64_t *flows_exported, struct timeval *system_boot_time, 
     int verbose_flag);
 int send_netflow_v5(struct FLOW **flows, int num_flows, int nfsock,
-    u_int64_t *flows_exported, struct timeval *system_boot_time,
+    u_int16_t ifidx, u_int64_t *flows_exported, struct timeval *system_boot_time,
     int verbose_flag);
 int send_netflow_v9(struct FLOW **flows, int num_flows, int nfsock,
-    u_int64_t *flows_exported, struct timeval *system_boot_time,
+    u_int16_t ifidx, u_int64_t *flows_exported, struct timeval *system_boot_time,
     int verbose_flag);
 
 /* Force a resend of the flow template */

+ 1 - 1
softflowd.init

@@ -1,7 +1,7 @@
 #!/bin/bash
 #
 # softflowd	Starts softflowd NetFlow probe
-# $Id: softflowd.init,v 1.1 2006/03/14 23:15:41 djm Exp $
+# $Id$
 #
 # chkconfig: 2345 95 02
 # description: Starts and stops the softflowd Netflow probe

+ 1 - 1
softflowd.spec

@@ -1,4 +1,4 @@
-# $Id: softflowd.spec,v 1.1 2006/03/14 23:15:41 djm Exp $
+# $Id$
 
 # Figure out release tag (e.g. rhel3, fc1) based on redhat-release file
 %global _RHTAG %(sed 's/(.*)//;s/ [A-Z]* //;s/[a-z ]*//g' /etc/redhat-release | tr '[:upper:]' '[:lower:]')

+ 1 - 1
strlcat.c

@@ -21,7 +21,7 @@
 #include "common.h"
 #ifndef HAVE_STRLCAT
 
-RCSID("$Id: strlcat.c,v 1.1 2004/09/10 09:08:08 djm Exp $");
+RCSID("$Id$");
 
 #if defined(LIBC_SCCS) && !defined(lint)
 static char *rcsid = "$OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $";

+ 1 - 1
strlcpy.c

@@ -22,7 +22,7 @@
 
 #ifndef HAVE_STRLCPY
 
-RCSID("$Id: strlcpy.c,v 1.1 2004/09/10 09:08:08 djm Exp $");
+RCSID("$Id$");
 
 #if defined(LIBC_SCCS) && !defined(lint)
 static char *rcsid = "$OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $";

+ 1 - 1
treetype.h

@@ -22,7 +22,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-/* $Id: treetype.h,v 1.1 2003/11/09 00:38:08 djm Exp $ */
+/* $Id$ */
 
 /* Select our tree types for various data structures */