Browse Source

Import upstream version 0.9.8

Christoph Biedl 16 years ago
commit
12845c26e9
34 changed files with 15753 additions and 0 deletions
  1. 238 0
      ChangeLog
  2. 146 0
      LICENSE
  3. 59 0
      Makefile.in
  4. 67 0
      README
  5. 104 0
      TODO
  6. 1 0
      aclocal.m4
  7. 100 0
      closefrom.c
  8. 279 0
      collector.pl
  9. 169 0
      common.h
  10. 163 0
      config.h.in
  11. 9163 0
      configure
  12. 170 0
      configure.ac
  13. 89 0
      convtime.c
  14. 50 0
      convtime.h
  15. 81 0
      daemon.c
  16. 251 0
      install-sh
  17. 59 0
      log.c
  18. 33 0
      log.h
  19. 40 0
      mkinstalldirs
  20. 168 0
      netflow1.c
  21. 170 0
      netflow5.c
  22. 377 0
      netflow9.c
  23. 95 0
      softflowctl.8
  24. 114 0
      softflowctl.c
  25. 402 0
      softflowd.8
  26. 1934 0
      softflowd.c
  27. 210 0
      softflowd.h
  28. 68 0
      softflowd.init
  29. 50 0
      softflowd.spec
  30. 10 0
      softflowd.sysconfig
  31. 68 0
      strlcat.c
  32. 65 0
      strlcpy.c
  33. 677 0
      sys-tree.h
  34. 83 0
      treetype.h

+ 238 - 0
ChangeLog

@@ -0,0 +1,238 @@
+20061101
+ - (djm) Collect licenses into LICENSE file
+ - (djm) malloc(x*y) -> calloc(x, y)
+ - (djm) Sync sys-tree.h
+ - (djm) Release 0.9.8
+
+20060315
+ - (djm) Add "send-template" softflowctl command to resend a NetFlow 9
+   template record immediately
+
+20060315
+ - (djm) Fix DLT_RAW support, from jhanna AT shaw.ca 
+ - (djm) Support ${DESTDIR} in Makefile install target, from
+   ssnodgra AT pheran.com
+ - (djm) Encode ICMP type and code into port numbers (apparently this is
+   what Cisco exporters do), patch from ssnodgra AT pheran.com slightly
+   tweaked by me
+ - (djm) Crank version number to 0.9.8
+ - (djm) Add RPM packaging files from ssnodgra AT pheran.com
+
+20060214
+ - (djm) Add missing getopt() bit for flowtrack mode
+
+20060211
+ - (djm) Add option to ignore port and protocol information from flows,
+   allowing flows from the same IP addresses to be automatically
+   coalesced
+
+20060126
+ - (djm) Correctly expire quiescent flows when they hit maximum_lifetime; 
+   bug noticed and patch tested by andreas.brillisauer AT hetzner.de
+
+20051206
+ - (djm) Make sure installation directories exist, spotted by
+   alshu AT tut.by
+
+20051118
+ - (djm) Some extra paranoia and verbosity on malloc failures
+ - (djm) Support Linux "cooked socket" datalink type, from Tony Lewis 
+   gnutered AT yahoo.com.au
+
+20051001
+ - (djm) Fix typo, from rbreathe AT brookes.ac.uk
+
+20050505
+ - (djm) Fix time printing bug in debug mode
+ - (djm) Fix reversed NetFlow v.9 first_switched and last_switched times
+
+20050505
+ - (djm) Fix bug in sequence number generation. Reported by 
+   b.ghita AT jack.see.plymouth.ac.uk and mwlucas AT blackhelicopters.org
+ - (djm) Report pcap stats in statistics display
+
+20050114
+ - (djm) Release 0.9.7
+
+20050110
+ - (djm) Fix endianness problem in NetFlow v.9 port number export. Found and 
+   fixed by paolo.lucente AT ic.cnr.it
+ - (djm) Add option to set hoplimit/TTL in support of multicast export support
+ - (djm) Document multicast export
+
+20041109
+ - (djm) Test for struct ip6_ext in autoconf and define a replacement if 
+   missing, some systems lack it
+
+20040930
+ - (djm) Increase caplen a little for IPv6
+ - (djm) Remove unused debugging code from NetFlow v.9 support
+ - (djm) Add a timeout to cluster expiry expiry events, so we get more flows 
+    per packet. Default is to check for expiries every 60s
+ - (djm) Allow timouts to be disabled (by setting them to 0)
+ - (djm) Include IP_PROTOCOL_VERSION field in NetFlow v.9 packets
+ - (djm) Don't bother tracking IPv6 flows if NetFlow export version 
+   doesn't support it
+ - (djm) Don't crank up pcap snaplen unless we are interested in IPv6 either
+ - (djm) Unbreak v6 flow export
+ - (djm) Unbreak compilation on non-OpenBSD
+ - (djm) Update README with recent changes (NetFlow v.9, v6 flows)
+ - (djm) Release 0.9.6
+
+20040929
+ - (djm) Improve IPv6 code: track flowlables bidirectionally (but don't key on 
+   them for now), print addresses:port tuples unambiguously and apply correct 
+   timeout for ICMPv6 flows
+ - (djm) Remove NetFlow v.1 types from NetFlow v.5 code
+ - (djm) NetFlow v.9 support
+ 
+20040913
+ - (djm) Split out netflow send functions into separate files
+ - (djm) Switch to a table of netflow exporter functions in preparation for 
+   additional export protocols
+ - (djm) Collect netflow export target information together in a struct, in 
+   preparation for more export protocols and support for multiple export targets
+ - (djm) Optimise the datalink_check function, by caching the last datalink type
+   used.
+
+20040909
+ - (djm) Implement IPv6 flow tracking. Currently no export functionality.
+ - (djm) Portability fixes for Linux, add closefrom()
+ - (djm) Use strlcat/strlcpy instead of strn* functions
+ - (djm) Comment out dump_packet (uncomment when debugging)
+
+20040909
+ - (djm) inline is unnecessary
+ - (djm) Rework datalink processing, in preparation of IPv6 support
+ - (djm) Next step in preparation of IPv6 support: make flow structure and
+   lookup function support both IPv4 and IPv6 addresses (v6 addrs aren't yet
+   used)
+ - (djm) Another step on the road: factor out transport-layer protocol parsing
+   from IPv4 parsing code
+ - (djm) Be more careful about putting flows into canonical format
+ - (djm) Prepare for IPv6 packet to flow conversion routine
+
+20040901
+ - (djm) Fix a tiny, stupid bug that prevents flow export
+ - (djm) Release version 0.9.2
+
+20040827
+ - (djm) NetFlow v.5 supports 30 flows per packet
+ - (djm) Use struct sockaddr in arguments (not sockaddr_storage), properly 
+   check length
+ - (djm) Mention NetFlow v.5 support in manpage
+ - (djm) Release version 0.9.1
+
+20040716
+ - (djm) Fix collector.pl when no address family specified on commandline
+   spotted by pgennai AT netstarnetworks.com
+
+20040710
+ - (djm) Tidy up code: remove some debugging gunk, kill a global
+ - (djm) Add support for NetFlow v.5 export format to softflowd
+ - (djm) Add support for NetFlow v.5 export format to collector.pl
+
+20040430
+ - (djm) Release version 0.9
+
+20040417
+ - (djm) Fix invalid packet bug
+
+20040417
+ - (djm) Eliminate periodic expiry scans, wait in poll() only until the next 
+   scheduled expiry event
+ - (djm) Separate timeout for ICMP traffic, generic timeout is too long
+
+20040416
+ - (djm) A bunch of changes necessary to support building on Solaris 9 (though
+   the resultant binary doesn't seem to work properly):
+        - Use getaddrinfo instead of inet_aton to parse host/port for export
+        - Use setreuid if setresuid isn't around (ditto for gid)
+        - Add replacement daemon() function from OpenBSD
+        - Provide our own logit() function, because Solaris syslog() doesn't 
+          support LOG_PERROR
+        - A heap of configure and common.h additions and fixes
+ - (djm) Fix busted preprocessor
+ - (djm) Support "[host]:port" syntax to specify numeric IPv6 export target
+ - (djm) Fix connect() for IPv6 export targets
+ - (djm) IPv6 listen support for collector.pl
+ - (djm) Allow v4 operation of collector.pl if v6 modules aren't present
+ - (djm) More flow export fixes
+ - (djm) Tidy manpage and mention v6 export syntax
+ - (djm) Unbreak Solaris, pass socklen around instead of using sa_len
+ - (djm) Unbreak "make install"
+
+20040415
+ - (djm) Clear socket errors before UDP send; from pfflowd
+ - (djm) Print flow start and finish times in collector.pl
+ - (djm) Linux needs grp.h for setgroups()
+ - (djm) Never endprotoent()
+ - (djm) Use autoconf to detect various things; in preparation of more 
+   portability
+ - (djm) Detect int and define standard int types in configure
+
+20031111
+ - (djm) Remove -Werror from CFLAGS, it causes problems in released software
+
+20031109
+ - (djm) Give compile-time choice over flow and expiry event tree type
+   default is splay tree for flows and red-black tree for expiry events
+   (limited benchmarking indicates that this is the fastest)
+ - (djm) Lock the BPF device to prevent changes should the unpriv child
+   be compromised (only OpenBSD supports this ioctl for now)
+
+20031001
+ - (djm) Realloc audit
+ - (djm) Chroot to /var/empty and drop privileges on daemonisation
+ - (djm) More things TODO
+
+20030620
+ - (djm) Fixup collector timestamp printing
+ - (djm) Rework TODO, add section on planned fragment handling
+ - (djm) Add "strip" target to Makefile
+ - (djm) Add "help" keyword to recognised softflowctl responses
+ - (djm) Fix fragment handling bug: we would try to look into fragmented
+   payload on later fragments. This could cause random tcp/udp  port numbers
+   to be recorded.
+ - (djm) Fix malicious fragment handling bug: deliberately tiny fragments 
+   (e.g. http://citeseer.nj.nec.com/ptacek98insertion.html) would be ignored
+   and would not create flow entries.
+ - (djm) Count fragments that we have seen
+ - (djm) Release version 0.8.1
+
+20030307
+ - (djm) Add basic perl netflow collector
+
+20021110
+ - (djm) Refactor, dramatically shrink mainloop
+
+20021105
+ - (djm) Don't exit on failure to lookup net/mask. From Alejandro Roman 
+   <aroman@uyr.com.ar>
+
+20021031
+ - (djm) Add some examples to the manpage
+
+20021030
+ - (djm) New user-friendly time specification code from OpenSSH/Kevin Steves
+ - (djm) Always use local sys-tree.h (for older OpenBSD's)
+
+20021029
+ - (djm) Multiple fixes and improvements from Octavian Cerna <tavy@ylabs.com>
+    - softflowd.c (connsock): Fix arguments to `connect': addr is a pointer, 
+	not a structure.
+      (flow_update_expiry): Properly compute the flow lifetime.
+      (send_netflow_v1): Count the leftover packet.
+	Send flow_start, flow_finish and uptime_ms as Cisco equipment 
+	does: milliseconds since system boot.
+      (timeval_sub_ms): New function.
+      (main): Changed POLL_WAIT to be (EXPIRY_WAIT/2) as stated in the 
+	comment above `poll': twice per recheck.
+	`poll' takes the last argument in milliseconds.
+	Initialize system_boot_time as the time at the start of capture
+	(fixme: how does this affect reading from capture files?)
+
+20021024
+ - (djm) Release softflowd-0.7.1
+
+$Id: ChangeLog,v 1.87 2006/11/02 06:36:16 djm Exp $

+ 146 - 0
LICENSE

@@ -0,0 +1,146 @@
+Original code is licensed under the following BSD-style license:
+
+/*
+ * Copyright 2002-2006 Damien Miller <djm@mindrot.org> 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.
+ */
+
+closefrom.c:
+
+/*
+ * Copyright (c) 2004 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+convtime.c:
+
+/*
+ * Copyright (c) 2001 Kevin Steves.  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.
+ */
+
+daemon.c:
+
+/*-
+ * Copyright (c) 1990, 1993
+ *	The Regents of the University of California.  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.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ */
+
+strlcat.c, strlcpy.c:
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+sys-tree.h:
+
+/*
+ * Copyright 2002 Niels Provos <provos@citi.umich.edu>
+ * 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.
+ */
+

+ 59 - 0
Makefile.in

@@ -0,0 +1,59 @@
+# $Id: Makefile.in,v 1.9 2006/03/14 22:55:41 djm Exp $
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+bindir=@bindir@
+sbindir=@sbindir@
+libexecdir=@libexecdir@
+datadir=@datadir@
+mandir=@mandir@
+sysconfdir=@sysconfdir@
+srcdir=@srcdir@
+top_srcdir=@top_srcdir@
+VPATH=@srcdir@
+CC=@CC@
+LDFLAGS=@LDFLAGS@
+CFLAGS=@CFLAGS@
+CPPFLAGS=-I$(srcdir) @CPPFLAGS@
+LIBS=@LIBS@
+EXEEXT=@EXEEXT@
+INSTALL=@INSTALL@
+
+#CFLAGS+=-DFLOW_RB		# Use red-black tree for flows
+CFLAGS+=-DFLOW_SPLAY		# Use splay tree for flows
+CFLAGS+=-DEXPIRY_RB		# Use red-black tree for expiry events
+#CFLAGS+=-DEXPIRY_SPLAY		# Use splay tree for expiry events
+
+TARGETS=softflowd${EXEEXT} softflowctl${EXEEXT}
+
+COMMON=convtime.o strlcpy.o strlcat.o closefrom.o daemon.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)
+
+softflowctl${EXEEXT}: softflowctl.o $(COMMON)
+	$(CC) $(LDFLAGS) -o $@ softflowctl.o $(COMMON) $(LIBS)
+
+clean:
+	rm -f $(TARGETS) *.o core *.core
+
+realclean: clean
+	rm -rf autom4te.cache Makefile config.log config.status
+
+distclean: realclean
+	rm -f config.h* configure
+
+strip:
+	strip $(TARGETS)
+
+install:
+	[ -d $(DESTDIR)$(sbindir) ] || \
+	    $(srcdir)/mkinstalldirs $(DESTDIR)$(sbindir)
+	[ -d $(DESTDIR)$(mandir)/man8 ] || \
+	    $(srcdir)/mkinstalldirs $(DESTDIR)$(mandir)/man8
+	$(INSTALL) -m 0755 -s softflowd $(DESTDIR)$(sbindir)/softflowd
+	$(INSTALL) -m 0755 -s softflowctl $(DESTDIR)$(sbindir)/softflowctl
+	$(INSTALL) -m 0644 softflowd.8 $(DESTDIR)$(mandir)/man8/softflowd.8
+	$(INSTALL) -m 0644 softflowctl.8 $(DESTDIR)$(mandir)/man8/softflowctl.8

+ 67 - 0
README

@@ -0,0 +1,67 @@
+Welcome to softflowd, a flow-based network monitor.
+
+Introduction
+------------
+
+softflowd listens promiscuously on a network interface and semi-statefully
+tracks network flows. These flows can be reported using NetFlow version 1, 5 
+or 9 datagrams. softflowd is fully IPv6 capable: it can track IPv6 flows and 
+export to IPv6 hosts.
+
+More details about softflowd's function and usage may be found in the
+supplied manpages, which you can view prior to installation using
+
+/usr/bin/nroff -c -mandoc softflowd.8 | less
+/usr/bin/nroff -c -mandoc softflowctl.8 | less
+
+If you are in need of a NetFlow collector, you may be interested in 
+softflowd's companion project "flowd" (http://www.mindrot.org/flowd.html). 
+flowd is a NetFlow collector that is maintained in parallel with
+softflowd and includes a few handy features, such as the ability
+to filter flows it receives as well as Perl and Python APIs to its
+storage format. NB. You don't have to use flowd: any NetFlow compatible 
+collector should work with softflowd. An example Perl collector is included 
+for testing purposes as collector.pl, but it doesn't yet support NetFlow v.9
+
+Installing
+----------
+
+Building softflowd should be as simple as typing:
+
+./configure
+make
+make install
+
+Unfortunately some systems like to make life complicated. Things work
+fine on the systems that I develop and test on (OpenBSD and Linux).
+There is peliminary support for Solaris 9 (i.e. it compiled), but no
+testing on this platform has been performed.
+
+Licensing
+---------
+
+Softflowd is licensed under a two-term BSD license (see the source
+files for details). The code in sys-tree.h is Copyright Niels Provos
+<provos@citi.umich.edu> and comes straight from OpenBSD CVS, convtime.c
+comes is Copyright Kevin Steves and comes from OpenSSH (misc.c). Both
+of these files are licensed under two-term BSD licenses too. strlcpy.c,
+strlcat.c and closefrom.c also come from OpenBSD CVS and are Copyright
+Todd C. Miller. Please refer to the LICENSE file for full details.
+
+Reporting Bugs
+--------------
+
+Please report bugs in softflowd to http://bugzilla.mindrot.org/ If you
+find a security bug, please report it directly by email. If you have any
+feedback or questions, please email me:
+
+Contributing
+------------
+
+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.
+
+Damien Miller <djm@mindrot.org>
+
+$Id: README,v 1.7 2006/11/02 06:34:18 djm Exp $

+ 104 - 0
TODO

@@ -0,0 +1,104 @@
+Things yet to do:
+
+softflowd
+---------
+
+ - Use strtonum()
+
+ Flow tracking engine
+  - Verify checksums (maybe. perhaps bad for accounting, good for flow tracking)
+  - Fragment processing
+    - We don't handle fragments right
+      - This shouldn't be too hard or too memory intensive. We just need to 
+	keep a tree of fragment entries. Each entry would need to contain 
+	enough information to reconstruct the flow (source/dest addr, etc), 
+	but also fragment related info: IP id, list of fragment offsets. etc.
+      - When we receive a new fragment, we add an entry to this tree (keyed 
+	by source IP, protocol, IP id)
+      - Each new fragment matched in the tree gets its offset added to the 
+	list, until all fragments have been seen
+      - Must be careful, as later fragments may arrive before inital one
+      - When does accounting occur? 
+	- Upon receipt of inital fragment? (and thus for ever frag thereafter)
+	- When we have seen all fragments? (what if we don't?)
+      - Must limit size of tree
+      - Must have fragment timeout (what happens then, apart from removal?)
+   - Timeouts
+    - Timeout for unanswered TCP connection
+    - Ditto orphaned connections (one packet in one direction only)
+    - Track ICMP generated by TCP/UDP session (painful, probably unecessary)
+    - More datalink types
+    - Improve fast-expiry of TCP session by tracking FIN sequence numbers
+  - Multiple interface support
+    - Requires some way to avoid duplicate recording of flows (IP ID)
+  - Track IPsec SPIs
+  - Track ToS / DSCP
+  - Make counters 64 bits
+    - We can report these directly for NetFlow v.9
+    - For older NetFlow, report by sending multiple flows until counter < 2^32
+
+ Misc features
+  - Ability to open more than one interface (maybe)
+  - Ability to read more than one pcap file (maybe)
+  - Fork for ctlsock actions? (don't block mainloop)
+  - Remote control over network (requires SSL)
+
+ Performance
+  - Profile and see where the hot spots are
+  - Fast "new flow" test using a bloom filter
+  - See if we can reduce per-packet overhead more
+    - Cost of expiry remove and re-add per packet
+  - Stop run-time malloc (maybe)
+    - Preallocate a pool of expiry events and flow entries
+      - keep a queue, pick/push first from head
+
+ Exporter features
+  - sflow support (www.sflow.org)
+    - Needs XDR encoding
+  - Ability to export to multiple hosts
+    - 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
+
+ Statistics code
+  - Collect more statistics (maybe)
+    - Advanced packet analysis: store hash of packet payload, keep 
+      statistics on traffic similarity
+       - Bloom filter?
+    - Option to record histograms of
+      - Flow lifetime and size, packet size
+    - Flow bandwidth
+    - Per well-known-port
+      - How to do this quicky? Memory efficiently?
+    - Per IP address/range
+      - How to do this quicky? Memory efficiently?
+    - Moving averages
+  - Track traffic over lifetime of flow
+    - Maintain linked list traffic counts, keyed by time interval
+    - E.g. key by (now / 300)
+      - Or (now - start_time) / 300 (better)
+    - When new packet comes in:
+      - If timestamp of HEAD of list == (now / xxx), then counter += octets
+      - Otherwise create new traffic counter at HEAD and update it
+        - Then trim tail if the list length is too big
+    - Maybe store "hunks" of data, rather than individual counts in the 
+      list, as storing a single int is a huge waste of space
+    - Maybe a rrdtool-like heirarchy of timespans
+      - 300 seconds (5 minutes)       (2400 bytes)
+      - 360 1-minute blocks (6 hours) (2880 bytes)
+      - 288 10-minute blocks (2 days) (2304 bytes)
+      - 336 1-hour blocks (2 weeks)   (2688 bytes)
+        - Total 10kb worst-case per-flow (scary, probably overkill)
+
+softflowctl
+-----------
+
+  - Extend interface
+    - Query for specific flows (e.g. by address)
+      - Do this in softflowd or softflowctl?
+    - Expire/delete specific flows (maybe)
+  - Runtime respecify timeouts
+  - Real-time binary dump of flowtable (shm/mmap fd pass?)
+    - ntop like view
+    - Spiffy GUI (separate tool)
+

+ 1 - 0
aclocal.m4

@@ -0,0 +1 @@
+dnl Nothing here

+ 100 - 0
closefrom.c

@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2004 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "common.h"
+
+#ifndef HAVE_CLOSEFROM
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stddef.h>
+#ifdef HAVE_DIRENT_H
+# include <dirent.h>
+# define NAMLEN(dirent) strlen((dirent)->d_name)
+#else
+# define dirent direct
+# define NAMLEN(dirent) (dirent)->d_namlen
+# ifdef HAVE_SYS_NDIR_H
+#  include <sys/ndir.h>
+# endif
+# ifdef HAVE_SYS_DIR_H
+#  include <sys/dir.h>
+# endif
+# ifdef HAVE_NDIR_H
+#  include <ndir.h>
+# endif
+#endif
+
+#ifndef OPEN_MAX
+# define OPEN_MAX	256
+#endif
+
+RCSID("$Id: closefrom.c,v 1.1 2004/09/10 09:08:08 djm Exp $");
+
+#ifndef lint
+static const char rcsid[] = "$Sudo: closefrom.c,v 1.6 2004/06/01 20:51:56 millert Exp $";
+#endif /* lint */
+
+/*
+ * Close all file descriptors greater than or equal to lowfd.
+ */
+void
+closefrom(int lowfd)
+{
+    long fd, maxfd;
+#if defined(HAVE_DIRFD) && defined(HAVE_PROC_PID)
+    char fdpath[PATH_MAX], *endp;
+    struct dirent *dent;
+    DIR *dirp;
+    int len;
+
+    /* Check for a /proc/$$/fd directory. */
+    len = snprintf(fdpath, sizeof(fdpath), "/proc/%ld/fd", (long)getpid());
+    if (len != -1 && len <= sizeof(fdpath) && (dirp = opendir(fdpath))) {
+	while ((dent = readdir(dirp)) != NULL) {
+	    fd = strtol(dent->d_name, &endp, 10);
+	    if (dent->d_name != endp && *endp == '\0' &&
+		fd >= 0 && fd < INT_MAX && fd >= lowfd && fd != dirfd(dirp))
+		(void) close((int) fd);
+	}
+	(void) closedir(dirp);
+    } else
+#endif
+    {
+	/*
+	 * Fall back on sysconf() or getdtablesize().  We avoid checking
+	 * resource limits since it is possible to open a file descriptor
+	 * and then drop the rlimit such that it is below the open fd.
+	 */
+#ifdef HAVE_SYSCONF
+	maxfd = sysconf(_SC_OPEN_MAX);
+#else
+	maxfd = getdtablesize();
+#endif /* HAVE_SYSCONF */
+	if (maxfd < 0)
+	    maxfd = OPEN_MAX;
+
+	for (fd = lowfd; fd < maxfd; fd++)
+	    (void) close((int) fd);
+    }
+}
+
+#endif /* HAVE_CLOSEFROM */
+

+ 279 - 0
collector.pl

@@ -0,0 +1,279 @@
+#!/usr/bin/perl -w
+
+# This is a Cisco NetFlow datagram collector
+
+# Netflow protocol reference:
+# http://www.cisco.com/univercd/cc/td/doc/product/rtrmgmt/nfc/nfc_3_0/nfc_ug/nfcform.htm
+
+# XXX Doesn't support NetFlow 9
+
+my $af;
+
+BEGIN {
+	use strict;
+	use warnings;
+	use IO qw(Socket);
+	use Socket;
+	use Carp;
+	use POSIX qw(strftime);
+	use Getopt::Long;
+	eval "use IO::Socket::INET6;";
+	eval "use Socket6;";
+}
+
+############################################################################
+
+sub timestamp()
+{
+	return strftime "%Y-%m-%dT%H:%M:%S", localtime;
+}
+
+sub fuptime($)
+{
+	my $t = shift;
+	my $r = "";
+	my $tmp;
+	
+	# Milliseconds
+	$tmp = $t % 1000;
+	$r = sprintf ".%03u%s", $tmp, $r;
+
+	# Seconds
+	$t = int($t / 1000);
+	$tmp = $t % 60;
+	$r = "${tmp}s${r}";
+
+	# Minutes
+	$t = int($t / 60);
+	$tmp = $t % 60;
+	$r = "${tmp}m${r}" if $tmp;
+
+	# Hours
+	$t = int($t / 60);
+	$tmp = $t % 24;
+	$r = "${tmp}h${r}" if $tmp;
+
+	# Days
+	$t = int($t / 24);
+	$tmp = $t % 7;
+	$r = "${tmp}d${r}" if $tmp;
+
+	# Weeks
+	$t = int($t / 7);
+	$tmp = $t % 52;
+	$r = "${tmp}w${r}" if $tmp;
+
+	# Years
+	$t = int($t / 52);
+	$r = "${tmp}y${r}" if $tmp;
+
+	return $r;
+}
+
+sub do_listen($$)
+{
+	my $port = shift
+		or confess "No UDP port specified";
+
+        my $socket;
+
+	if ($af == 4) {
+		$socket = IO::Socket::INET->new(Proto=>'udp', LocalPort=>$port)
+			or croak "Couldn't open UDP socket: $!";
+	} elsif ($af == 6) {
+		$socket = IO::Socket::INET6->new(Proto=>'udp', LocalPort=>$port)
+			or croak "Couldn't open UDP socket: $!";
+ 	} else {
+		croak "Unsupported AF";
+	}
+
+	return $socket;
+}
+
+sub process_nf_v1($$)
+{
+	my $sender = shift;
+	my $pkt = shift;
+	my %header;
+	my %flow;
+	my $sender_s;
+
+	%header = qw();
+
+	$sender_s = inet_ntoa($sender) if $af == 4;
+	$sender_s = inet_ntop(AF_INET6, $sender) if $af == 6;
+
+	($header{ver}, $header{flows}, $header{uptime}, $header{secs}, 
+	 $header{nsecs}) = unpack("nnNNN", $pkt);
+
+	if (length($pkt) < (16 + (48 * $header{flows}))) {
+		printf STDERR timestamp()." Short Netflow v.1 packet: %d < %d\n",
+		    length($pkt), 16 + (48 * $header{flows});
+		return;
+	}
+
+	printf timestamp() . " HEADER v.%u (%u flow%s)\n", $header{ver},
+	    $header{flows}, $header{flows} == 1 ? "" : "s";
+
+	for(my $i = 0; $i < $header{flows}; $i++) {
+		my $off = 16 + (48 * $i);
+		my $ptr = substr($pkt, $off, 52);
+
+		%flow = qw();
+
+		(my $src1, my $src2, my $src3, my $src4,
+		 my $dst1, my $dst2, my $dst3, my $dst4, 
+		 my $nxt1, my $nxt2, my $nxt3, my $nxt4, 
+		 $flow{in_ndx}, $flow{out_ndx}, $flow{pkts}, $flow{bytes}, 
+		 $flow{start}, $flow{finish}, $flow{src_port}, $flow{dst_port}, 
+		 my $pad1, $flow{protocol}, $flow{tos}, $flow{tcp_flags}) =
+		    unpack("CCCCCCCCCCCCnnNNNNnnnCCC", $ptr);
+
+		$flow{src} = sprintf "%u.%u.%u.%u", $src1, $src2, $src3, $src4;
+		$flow{dst} = sprintf "%u.%u.%u.%u", $dst1, $dst2, $dst3, $dst4;
+		$flow{nxt} = sprintf "%u.%u.%u.%u", $nxt1, $nxt2, $nxt3, $nxt4;
+
+		printf timestamp() . " " .
+		    "from %s started %s finish %s proto %u %s:%u > %s:%u %u " . 
+		    "packets %u octets\n",
+		    $sender_s,
+		    fuptime($flow{start}), fuptime($flow{finish}), 
+		    $flow{protocol}, 
+		    $flow{src}, $flow{src_port}, $flow{dst}, $flow{dst_port}, 
+		    $flow{pkts}, $flow{bytes};
+	}
+}
+
+sub process_nf_v5($$)
+{
+	my $sender = shift;
+	my $pkt = shift;
+	my %header;
+	my %flow;
+	my $sender_s;
+
+	%header = qw();
+
+	$sender_s = inet_ntoa($sender) if $af == 4;
+	$sender_s = inet_ntop(AF_INET6, $sender) if $af == 6;
+
+	($header{ver}, $header{flows}, $header{uptime}, $header{secs}, 
+	 $header{nsecs}, $header{flow_seq}, ) = unpack("nnNNNN", $pkt);
+
+	if (length($pkt) < (24 + (48 * $header{flows}))) {
+		printf STDERR timestamp()." Short Netflow v.1 packet: %d < %d\n",
+		    length($pkt), 24 + (48 * $header{flows});
+		return;
+	}
+
+	printf timestamp() . " HEADER v.%u (%u flow%s) seq %u\n", $header{ver},
+	    $header{flows}, $header{flows} == 1 ? "" : "s", $header{flow_seq};
+
+	for(my $i = 0; $i < $header{flows}; $i++) {
+		my $off = 24 + (48 * $i);
+		my $ptr = substr($pkt, $off, 52);
+
+		%flow = qw();
+
+		(my $src1, my $src2, my $src3, my $src4,
+		 my $dst1, my $dst2, my $dst3, my $dst4, 
+		 my $nxt1, my $nxt2, my $nxt3, my $nxt4, 
+		 $flow{in_ndx}, $flow{out_ndx}, $flow{pkts}, $flow{bytes}, 
+		 $flow{start}, $flow{finish}, $flow{src_port}, $flow{dst_port}, 
+		 my $pad1, $flow{tcp_flags}, $flow{protocol}, $flow{tos},
+		 $flow{src_as}, $flow{dst_as},
+		 $flow{src_mask}, $flow{dst_mask}) =
+		    unpack("CCCCCCCCCCCCnnNNNNnnCCCCnnCC", $ptr);
+
+		$flow{src} = sprintf "%u.%u.%u.%u", $src1, $src2, $src3, $src4;
+		$flow{dst} = sprintf "%u.%u.%u.%u", $dst1, $dst2, $dst3, $dst4;
+		$flow{nxt} = sprintf "%u.%u.%u.%u", $nxt1, $nxt2, $nxt3, $nxt4;
+
+		printf timestamp() . " " .
+		    "from %s started %s finish %s proto %u %s:%u > %s:%u %u " . 
+		    "packets %u octets\n",
+		    $sender_s,
+		    fuptime($flow{start}), fuptime($flow{finish}), 
+		    $flow{protocol}, 
+		    $flow{src}, $flow{src_port}, $flow{dst}, $flow{dst_port}, 
+		    $flow{pkts}, $flow{bytes};
+	}
+}
+
+############################################################################
+
+# Commandline options
+my $debug = 0;
+my $af4 = 0;
+my $af6 = 0;
+my $port;
+
+#		Long option		Short option
+GetOptions(	'debug+' => \$debug,	'd+' => \$debug,
+					'4+' => \$af4,
+					'6+' => \$af6,
+		'port=i' => \$port,	'p=i' => \$port);
+
+# Unbuffer output
+$| = 1;
+
+die "The -4 and -6 are mutually exclusive\n" if $af4 && $af6;
+die "You must specify a port (collector.pl -p XXX).\n" unless $port;
+
+$af4 = $af = 4 if $af4 || (!$af4 && !$af6);
+$af6 = $af = 6 if $af6;
+
+# These modules aren't standard everywhere, so load them only if necessary
+
+# Main loop - receive and process a packet
+for (;;) {
+	my $socket;
+	my $from;
+	my $payload;
+	my $ver;
+	my $failcount = 0;
+	my $netflow;
+	my $junk;
+	my $sender;
+
+	# Open the listening port if we haven't already
+	$socket = do_listen($port, $af) unless defined $socket;
+
+	# Fetch a packet
+	$from = $socket->recv($payload, 8192, 0);
+	
+	($junk, $sender) = unpack_sockaddr_in($from) if $af4;
+	($junk, $sender) = unpack_sockaddr_in6($from) if $af6;
+
+	# Reopen listening socket on error
+	if (!defined $from) {
+		$socket->close;
+		undef $socket;
+
+		$failcount++;
+		die "Couldn't recv: $!\n" if ($failcount > 5);
+		next; # Socket will be reopened at start of loop
+	}
+	
+	if (length($payload) < 16) {
+		printf STDERR timestamp()." Short packet recevied: %d < 16\n",
+		    length($payload);
+		next;
+	}
+
+	# The version is always the first 16 bits of the packet
+	($ver) = unpack("n", $payload);
+
+	if	($ver == 1)	{ process_nf_v1($sender, $payload); }
+	elsif	($ver == 5)	{ process_nf_v5($sender, $payload); }
+	else {
+		printf STDERR timestamp()." Unsupported netflow version %d\n",
+		    $ver;
+		next;
+	}
+	
+	undef $payload;
+	next;	
+}
+
+exit 0;

+ 169 - 0
common.h

@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2002 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 _SFD_COMMON_H
+#define _SFD_COMMON_H
+
+#include "config.h"
+
+#define _BSD_SOURCE /* Needed for BSD-style struct ip,tcp,udp on Linux */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/un.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <arpa/inet.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <netdb.h>
+#include <limits.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+
+#if defined(HAVE_NET_BPF_H)
+#include <net/bpf.h>
+#elif defined(HAVE_PCAP_BPF_H)
+#include <pcap-bpf.h>
+#endif
+#if defined(HAVE_INTTYPES_H)
+#include <inttypes.h>
+#endif
+
+/* The name of the program */
+#define PROGNAME		"softflowd"
+
+/* The name of the program */
+#define PROGVER			"0.9.8"
+
+/* Default pidfile */
+#define DEFAULT_PIDFILE		"/var/run/" PROGNAME ".pid"
+
+/* Default control socket */
+#define DEFAULT_CTLSOCK		"/var/run/" PROGNAME ".ctl"
+
+#define RCSID(msg) \
+	static /**/const char *const flowd_rcsid[] =		\
+	    { (const char *)flowd_rcsid, "\100(#)" msg }	\
+
+#ifndef IP_OFFMASK
+# define IP_OFFMASK		0x1fff	/* mask for fragmenting bits */
+#endif
+#ifndef IPV6_VERSION
+#define IPV6_VERSION		0x60
+#endif
+#ifndef IPV6_VERSION_MASK
+#define IPV6_VERSION_MASK	0xf0
+#endif
+#ifndef IPV6_FLOWINFO_MASK
+#define IPV6_FLOWINFO_MASK	ntohl(0x0fffffff)
+#endif
+#ifndef IPV6_FLOWLABEL_MASK
+#define IPV6_FLOWLABEL_MASK	ntohl(0x000fffff)
+#endif
+
+#ifndef _PATH_DEVNULL
+# define _PATH_DEVNULL		"/dev/null"
+#endif
+
+#ifndef MIN
+# define MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+#ifndef MAX
+# define MAX(a,b) (((a)>(b))?(a):(b))
+#endif
+#ifndef offsetof
+# define offsetof(type, member) ((size_t) &((type *)0)->member)
+#endif
+
+#if defined(__GNUC__)
+# ifndef __dead
+#  define __dead                __attribute__((__noreturn__))
+# endif
+# ifndef __packed
+#  define __packed              __attribute__((__packed__))
+# endif
+#endif
+
+#if !defined(HAVE_INT8_T) && defined(OUR_CFG_INT8_T)
+typedef OUR_CFG_INT8_T int8_t;
+#endif
+#if !defined(HAVE_INT16_T) && defined(OUR_CFG_INT16_T)
+typedef OUR_CFG_INT16_T int16_t;
+#endif
+#if !defined(HAVE_INT32_T) && defined(OUR_CFG_INT32_T)
+typedef OUR_CFG_INT32_T int32_t;
+#endif
+#if !defined(HAVE_INT64_T) && defined(OUR_CFG_INT64_T)
+typedef OUR_CFG_INT64_T int64_t;
+#endif
+#if !defined(HAVE_U_INT8_T) && defined(OUR_CFG_U_INT8_T)
+typedef OUR_CFG_U_INT8_T u_int8_t;
+#endif
+#if !defined(HAVE_U_INT16_T) && defined(OUR_CFG_U_INT16_T)
+typedef OUR_CFG_U_INT16_T u_int16_t;
+#endif
+#if !defined(HAVE_U_INT32_T) && defined(OUR_CFG_U_INT32_T)
+typedef OUR_CFG_U_INT32_T u_int32_t;
+#endif
+#if !defined(HAVE_U_INT64_T) && defined(OUR_CFG_U_INT64_T)
+typedef OUR_CFG_U_INT64_T u_int64_t;
+#endif
+
+#ifndef HAVE_STRLCPY
+size_t strlcpy(char *dst, const char *src, size_t siz);
+#endif
+#ifndef HAVE_STRLCAT
+size_t strlcat(char *dst, const char *src, size_t siz);
+#endif
+#ifndef HAVE_CLOSEFROM
+void closefrom(int lowfd);
+#endif
+
+#ifndef HAVE_STRUCT_IP6_EXT
+struct ip6_ext {
+	u_int8_t ip6e_nxt;
+	u_int8_t ip6e_len;
+} __packed;
+#endif
+
+#endif /* _SFD_COMMON_H */

+ 163 - 0
config.h.in

@@ -0,0 +1,163 @@
+/* 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
+ 9163 - 0
configure


+ 170 - 0
configure.ac

@@ -0,0 +1,170 @@
+# $Id: configure.ac,v 1.5 2004/11/08 23:03:28 djm Exp $
+#
+# Copyright (c) 2004 Damien Miller
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+AC_INIT
+AC_CONFIG_SRCDIR([softflowd.c])
+
+AC_CONFIG_HEADER(config.h)
+AC_PROG_CC
+AC_PROG_INSTALL
+
+# Optional verbose warnings for gcc, see below
+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" 
+
+# Process flag arguments early, so they are available for tests later
+AC_ARG_ENABLE(gcc-warnings,
+	[  --enable-gcc-warnings   Enable verbose warnings (only for gcc)],
+	[ if test "x$enableval" = "xyes" ; then CFLAGS="$CFLAGS $WFLAGS"; fi ]
+)
+AC_ARG_WITH(cflags,
+	[  --with-cflags           Specify additional compiler flags],
+	[ if test "x$withval" != "xno" ; then CFLAGS="$CFLAGS $withval"; fi ]	
+)
+AC_ARG_WITH(cppflags,
+	[  --with-cppflags         Specify additional preprocessor flags] ,
+	[ if test "x$withval" != "xno"; then CPPFLAGS="$CPPFLAGS $withval"; fi ]
+)
+AC_ARG_WITH(ldflags,
+	[  --with-ldflags          Specify additional linker flags],
+	[ if test "x$withval" != "xno" ; then LDFLAGS="$LDFLAGS $withval"; fi ]	
+)
+AC_ARG_WITH(libs,
+	[  --with-libs             Specify additional libraries to link with],
+	[ if test "x$withval" != "xno" ; then LIBS="$LIBS $withval"; fi ]	
+)
+
+AC_CHECK_HEADERS(net/bpf.h pcap.h pcap-bpf.h)
+
+dnl AC_CHECK_HEADERS(netinet/in_systm.h netinet/tcp.h netinet/udp.h)
+dnl 
+dnl # This ugliness is because of autoconf's stupid default include list
+dnl AC_CHECK_HEADERS([netinet/ip.h], 
+dnl 	[AC_DEFINE([HAVE_HAVE_NETINET_IP_H], 1, [has netinet/ip.h])], [], 
+dnl 	[
+dnl #include <sys/types.h>
+dnl #include <netinet/in.h>
+dnl #if HAVE_NETINET_IN_SYSTM_H
+dnl #include <netinet/in_systm.h>
+dnl #endif
+dnl 	])
+
+AC_CHECK_MEMBER([struct sockaddr.sa_len], 
+	[AC_DEFINE([SOCK_HAS_LEN], 1, [struct sockaddr contains length])], ,
+	[#include <sys/types.h>
+	 #include <sys/socket.h>])
+
+AC_CHECK_MEMBER(struct ip6_ext.ip6e_nxt,
+	[AC_DEFINE([HAVE_STRUCT_IP6_EXT], 1, [struct ip6_ext.ip6e_nxt exists])],
+	[],
+	[
+		#include <sys/types.h>
+		#include <sys/socket.h>
+		#include <netinet/in.h>
+		#include <netinet/ip6.h>
+	])
+
+AC_SEARCH_LIBS(daemon, bsd)
+AC_SEARCH_LIBS(gethostbyname, nsl)
+AC_SEARCH_LIBS(socket, socket)
+AC_CHECK_LIB(pcap, pcap_open_live)
+
+AC_CHECK_FUNCS(closefrom daemon setresuid setreuid setresgid setgid strlcpy strlcat)
+
+AC_CHECK_TYPES([u_int64_t, int64_t, uint64_t, u_int32_t, int32_t, uint32_t])
+AC_CHECK_TYPES([u_int16_t, int16_t, uint16_t, u_int8_t, int8_t, uint8_t])
+AC_CHECK_SIZEOF(char, 1)
+AC_CHECK_SIZEOF(short int, 2)
+AC_CHECK_SIZEOF(int, 4)
+AC_CHECK_SIZEOF(long int, 4)
+AC_CHECK_SIZEOF(long long int, 8)
+
+if test "x$ac_cv_type_uint8_t" = "xyes" ; then
+	AC_DEFINE([OUR_CFG_U_INT8_T], [uint8_t], [8-bit unsigned int])
+elif test "x$ac_cv_sizeof_char" = "x1" ; then
+	AC_DEFINE([OUR_CFG_U_INT8_T], [unsigned char], [8-bit unsigned int])
+else
+	AC_MSG_ERROR([No 8-bit unsigned int type found])
+fi
+if test "x$ac_cv_sizeof_char" = "x1" ; then
+	AC_DEFINE([OUR_CFG_INT8_T], [signed char], [8-bit signed int])
+else
+	AC_MSG_ERROR([No 8-bit signed int type found])
+fi
+
+if test "x$ac_cv_type_uint16_t" = "xyes" ; then
+	AC_DEFINE([OUR_CFG_U_INT16_T], [uint16_t], [16-bit unsigned int])
+elif test "x$ac_cv_sizeof_short_int" = "x2" ; then
+	AC_DEFINE([OUR_CFG_U_INT16_T], [unsigned short int], [16-bit unsigned int])
+else
+	AC_MSG_ERROR([No 16-bit unsigned int type found])
+fi
+if test "x$ac_cv_sizeof_short_int" = "x2" ; then
+	AC_DEFINE([OUR_CFG_INT16_T], [short int], [16-bit signed int])
+else
+	AC_MSG_ERROR([No 16-bit signed int type found])
+fi
+
+if test "x$ac_cv_type_uint32_t" = "xyes" ; then
+	AC_DEFINE([OUR_CFG_U_INT32_T], [uint32_t], [32-bit unsigned int])
+elif test "x$ac_cv_sizeof_int" = "x4" ; then
+	AC_DEFINE([OUR_CFG_U_INT32_T], [unsigned int], [32-bit unsigned int])
+else
+	AC_MSG_ERROR([No 32-bit unsigned int type found])
+fi
+if test "x$ac_cv_sizeof_int" = "x4" ; then
+	AC_DEFINE([OUR_CFG_INT32_T], [int], [32-bit signed int])
+else
+	AC_MSG_ERROR([No 32-bit signed int type found])
+fi
+
+if test "x$ac_cv_type_uint64_t" = "xyes" ; then
+	AC_DEFINE([OUR_CFG_U_INT64_T], [uint64_t], [64-bit unsigned int])
+elif test "x$ac_cv_sizeof_long_int" = "x8" ; then
+	AC_DEFINE([OUR_CFG_U_INT64_T], [unsigned long int], [64-bit unsigned int])
+elif test "x$ac_cv_sizeof_long_long_int" = "x8" ; then
+	AC_DEFINE([OUR_CFG_U_INT64_T], [unsigned long long int], [64-bit unsigned int])
+else
+	AC_MSG_ERROR([No 64-bit unsigned int type found])
+fi
+if test "x$ac_cv_sizeof_long_int" = "x8" ; then
+	AC_DEFINE([OUR_CFG_INT64_T], [long int], [64-bit signed int])
+elif test "x$ac_cv_sizeof_long_long_int" = "x8" ; then
+	AC_DEFINE([OUR_CFG_INT64_T], [long long int], [64-bit signed int])
+else
+	AC_MSG_ERROR([No 64-bit signed int type found])
+fi
+
+if test "x$ac_cv_header_pcap_bpf_h" != "xyes" && \
+   test "x$ac_cv_header_net_bpf_h" != "xyes" ; then
+	AC_MSG_ERROR([No BPF header found])
+fi
+if test "x$ac_cv_header_pcap_h" != "xyes" ; then
+	AC_MSG_ERROR([No pcap.h header found])
+fi
+if test "x$ac_cv_lib_pcap_pcap_open_live" != "xyes" ; then
+	AC_MSG_ERROR([libpcap not found])
+fi
+
+
+
+AC_EXEEXT
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
+

+ 89 - 0
convtime.c

@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2001 Kevin Steves.  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 "convtime.h"
+
+RCSID("$Id: convtime.c,v 1.2 2004/09/10 09:08:08 djm Exp $");
+
+#define SECONDS		1
+#define MINUTES		(SECONDS * 60)
+#define HOURS		(MINUTES * 60)
+#define DAYS		(HOURS * 24)
+#define WEEKS		(DAYS * 7)
+
+long int
+convtime(const char *s)
+{
+	long total, secs;
+	const char *p;
+	char *endp;
+
+	errno = 0;
+	total = 0;
+	p = s;
+
+	if (p == NULL || *p == '\0')
+		return -1;
+
+	while (*p) {
+		secs = strtol(p, &endp, 10);
+		if (p == endp ||
+		    (errno == ERANGE && (secs == LONG_MIN || secs == LONG_MAX)) ||
+		    secs < 0)
+			return -1;
+
+		switch (*endp++) {
+		case '\0':
+			endp--;
+		case 's':
+		case 'S':
+			break;
+		case 'm':
+		case 'M':
+			secs *= MINUTES;
+			break;
+		case 'h':
+		case 'H':
+			secs *= HOURS;
+			break;
+		case 'd':
+		case 'D':
+			secs *= DAYS;
+			break;
+		case 'w':
+		case 'W':
+			secs *= WEEKS;
+			break;
+		default:
+			return -1;
+		}
+		total += secs;
+		if (total < 0)
+			return -1;
+		p = endp;
+	}
+
+	return total;
+}

+ 50 - 0
convtime.h

@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2001 Kevin Steves.  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 _SFD_CONVTIME_H
+
+/*
+ * Convert a time string into seconds; format is
+ * a sequence of:
+ *      time[qualifier]
+ *
+ * Valid time qualifiers are:
+ *      <none>  seconds
+ *      s|S     seconds
+ *      m|M     minutes
+ *      h|H     hours
+ *      d|D     days
+ *      w|W     weeks
+ *
+ * Examples:
+ *      90m     90 minutes
+ *      1h30m   90 minutes
+ *      2d      2 days
+ *      1w      1 week
+ *
+ * Return -1 if time string is invalid.
+ */
+long int convtime(const char *s);
+
+#endif /* _SFD_CONVTIME_H */

+ 81 - 0
daemon.c

@@ -0,0 +1,81 @@
+/* OPENBSD ORIGINAL: lib/libc/gen/daemon.c */
+
+/*-
+ * Copyright (c) 1990, 1993
+ *	The Regents of the University of California.  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.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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"
+
+#ifndef HAVE_DAEMON
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char rcsid[] = "$OpenBSD: daemon.c,v 1.5 2003/07/15 17:32:41 deraadt Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+int
+daemon(int nochdir, int noclose)
+{
+	int fd;
+
+	switch (fork()) {
+	case -1:
+		return (-1);
+	case 0:
+#ifdef HAVE_CYGWIN
+		register_9x_service();
+#endif
+		break;
+	default:
+#ifdef HAVE_CYGWIN
+		/*
+		 * This sleep avoids a race condition which kills the
+		 * child process if parent is started by a NT/W2K service.
+		 */
+		sleep(1);
+#endif
+		_exit(0);
+	}
+
+	if (setsid() == -1)
+		return (-1);
+
+	if (!nochdir)
+		(void)chdir("/");
+
+	if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
+		(void)dup2(fd, STDIN_FILENO);
+		(void)dup2(fd, STDOUT_FILENO);
+		(void)dup2(fd, STDERR_FILENO);
+		if (fd > 2)
+			(void)close (fd);
+	}
+	return (0);
+}
+
+#endif /* !HAVE_DAEMON */
+

+ 251 - 0
install-sh

@@ -0,0 +1,251 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission.  M.I.T. makes no representations about the
+# suitability of this software for any purpose.  It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.  It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+	-c) instcmd="$cpprog"
+	    shift
+	    continue;;
+
+	-d) dir_arg=true
+	    shift
+	    continue;;
+
+	-m) chmodcmd="$chmodprog $2"
+	    shift
+	    shift
+	    continue;;
+
+	-o) chowncmd="$chownprog $2"
+	    shift
+	    shift
+	    continue;;
+
+	-g) chgrpcmd="$chgrpprog $2"
+	    shift
+	    shift
+	    continue;;
+
+	-s) stripcmd="$stripprog"
+	    shift
+	    continue;;
+
+	-t=*) transformarg=`echo $1 | sed 's/-t=//'`
+	    shift
+	    continue;;
+
+	-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+	    shift
+	    continue;;
+
+	*)  if [ x"$src" = x ]
+	    then
+		src=$1
+	    else
+		# this colon is to work around a 386BSD /bin/sh bug
+		:
+		dst=$1
+	    fi
+	    shift
+	    continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+	echo "install:	no input file specified"
+	exit 1
+else
+	true
+fi
+
+if [ x"$dir_arg" != x ]; then
+	dst=$src
+	src=""
+	
+	if [ -d $dst ]; then
+		instcmd=:
+		chmodcmd=""
+	else
+		instcmd=mkdir
+	fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+	if [ -f $src -o -d $src ]
+	then
+		true
+	else
+		echo "install:  $src does not exist"
+		exit 1
+	fi
+	
+	if [ x"$dst" = x ]
+	then
+		echo "install:	no destination specified"
+		exit 1
+	else
+		true
+	fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+	if [ -d $dst ]
+	then
+		dst="$dst"/`basename $src`
+	else
+		true
+	fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+#  this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='	
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+	pathcomp="${pathcomp}${1}"
+	shift
+
+	if [ ! -d "${pathcomp}" ] ;
+	then
+		$mkdirprog "${pathcomp}"
+	else
+		true
+	fi
+
+	pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+	$doit $instcmd $dst &&
+
+	if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+	if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+	if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+	if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+	if [ x"$transformarg" = x ]
+	then
+		dstfile=`basename $dst`
+	else
+		dstfile=`basename $dst $transformbasename |
+			sed $transformarg`$transformbasename
+	fi
+
+# don't allow the sed command to completely eliminate the filename
+
+	if [ x"$dstfile" = x ]
+	then
+		dstfile=`basename $dst`
+	else
+		true
+	fi
+
+# Make a temp file name in the proper directory.
+
+	dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+	$doit $instcmd $src $dsttmp &&
+
+	trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing.  If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+	if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+	if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+	if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+	if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+	$doit $rmcmd -f $dstdir/$dstfile &&
+	$doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0

+ 59 - 0
log.c

@@ -0,0 +1,59 @@
+/*
+ * Copyright 2004 Damien Miller <djm@mindrot.org> 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.
+ */
+
+/* $Id: log.c,v 1.2 2004/09/10 09:08:08 djm Exp $ */
+
+#include "common.h"
+#include "log.h"
+#include <stdarg.h>
+
+RCSID("$Id: log.c,v 1.2 2004/09/10 09:08:08 djm Exp $");
+
+static int logstderr = 0;
+
+void
+loginit(const char *ident, int to_stderr)
+{
+	if (to_stderr)
+		logstderr = 1;
+	else
+		openlog(PROGNAME, LOG_PID, LOG_DAEMON);
+}
+
+void
+logit(int level, const char *fmt,...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+
+	if (logstderr) {
+		vfprintf(stderr, fmt, args);
+		fputs("\n", stderr);
+	} else
+		vsyslog(level, fmt, args);
+
+	va_end(args);
+}
+

+ 33 - 0
log.h

@@ -0,0 +1,33 @@
+/*
+ * Copyright 2004 Damien Miller <djm@mindrot.org> 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.
+ */
+
+/* $Id: log.h,v 1.1 2004/04/16 01:51:51 djm Exp $ */
+
+#ifndef _LOG_H
+#define _LOG_H
+
+void loginit(const char *ident, int to_stderr);
+void logit(int level, const char *fmt,...);
+
+#endif /* _LOG_H */

+ 40 - 0
mkinstalldirs

@@ -0,0 +1,40 @@
+#! /bin/sh
+# mkinstalldirs --- make directory hierarchy
+# Author: Noah Friedman <friedman@prep.ai.mit.edu>
+# Created: 1993-05-16
+# Public domain
+
+# $Id: mkinstalldirs,v 1.1 2005/12/22 02:23:41 djm Exp $
+
+errstatus=0
+
+for file
+do
+   set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
+   shift
+
+   pathcomp=
+   for d
+   do
+     pathcomp="$pathcomp$d"
+     case "$pathcomp" in
+       -* ) pathcomp=./$pathcomp ;;
+     esac
+
+     if test ! -d "$pathcomp"; then
+	echo "mkdir $pathcomp"
+
+	mkdir "$pathcomp" || lasterr=$?
+
+	if test ! -d "$pathcomp"; then
+	  errstatus=$lasterr
+	fi
+     fi
+
+     pathcomp="$pathcomp/"
+   done
+done
+
+exit $errstatus
+
+# mkinstalldirs ends here

+ 168 - 0
netflow1.c

@@ -0,0 +1,168 @@
+/*
+ * Copyright 2002 Damien Miller <djm@mindrot.org> 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.
+ */
+
+/* $Id: netflow1.c,v 1.2 2005/05/05 03:31:42 djm Exp $ */
+
+#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 $");
+
+/*
+ * This is the Cisco Netflow(tm) version 1 packet format
+ * Based on:
+ * http://www.cisco.com/univercd/cc/td/doc/product/rtrmgmt/nfc/nfc_3_0/nfc_ug/nfcform.htm
+ */
+struct NF1_HEADER {
+	u_int16_t version, flows;
+	u_int32_t uptime_ms, time_sec, time_nanosec;
+};
+struct NF1_FLOW {
+	u_int32_t src_ip, dest_ip, nexthop_ip;
+	u_int16_t if_index_in, if_index_out;
+	u_int32_t flow_packets, flow_octets;
+	u_int32_t flow_start, flow_finish;
+	u_int16_t src_port, dest_port;
+	u_int16_t pad1;
+	u_int8_t protocol, tos, tcp_flags;
+	u_int8_t pad2, pad3, pad4;
+	u_int32_t reserved1;
+#if 0
+ 	u_int8_t reserved2; /* XXX: no longer used */
+#endif
+};
+/* Maximum of 24 flows per packet */
+#define NF1_MAXFLOWS		24
+#define NF1_MAXPACKET_SIZE	(sizeof(struct NF1_HEADER) + \
+				 (NF1_MAXFLOWS * sizeof(struct NF1_FLOW)))
+
+/*
+ * Given an array of expired flows, send netflow v1 report packets
+ * Returns number of packets sent or -1 on error
+ */
+int
+send_netflow_v1(struct FLOW **flows, int num_flows, int nfsock,
+    u_int64_t *flows_exported, struct timeval *system_boot_time, 
+    int verbose_flag)
+{
+	struct timeval now;
+	u_int32_t uptime_ms;
+	u_int8_t packet[NF1_MAXPACKET_SIZE];	/* Maximum allowed packet size (24 flows) */
+	struct NF1_HEADER *hdr = NULL;
+	struct NF1_FLOW *flw = NULL;
+	int i, j, offset, num_packets, err;
+	socklen_t errsz;
+	
+	gettimeofday(&now, NULL);
+	uptime_ms = timeval_sub_ms(&now, system_boot_time);
+
+	hdr = (struct NF1_HEADER *)packet;
+	for(num_packets = offset = j = i = 0; i < num_flows; i++) {
+		if (j >= NF1_MAXFLOWS - 1) {
+			if (verbose_flag)
+				logit(LOG_DEBUG, "Sending flow packet len = %d", offset);
+			hdr->flows = htons(hdr->flows);
+			errsz = sizeof(err);
+			getsockopt(nfsock, SOL_SOCKET, SO_ERROR,
+			    &err, &errsz); /* Clear ICMP errors */
+			if (send(nfsock, packet, (size_t)offset, 0) == -1)
+				return (-1);
+			*flows_exported += j;
+			j = 0;
+			num_packets++;
+		}
+		if (j == 0) {
+			memset(&packet, '\0', sizeof(packet));
+			hdr->version = htons(1);
+			hdr->flows = 0; /* Filled in as we go */
+			hdr->uptime_ms = htonl(uptime_ms);
+			hdr->time_sec = htonl(now.tv_sec);
+			hdr->time_nanosec = htonl(now.tv_usec * 1000);
+			offset = sizeof(*hdr);
+		}		
+		flw = (struct NF1_FLOW *)(packet + offset);
+		
+		/* NetFlow v.1 doesn't do IPv6 */
+		if (flows[i]->af != AF_INET)
+			continue;
+		if (flows[i]->octets[0] > 0) {
+			flw->src_ip = flows[i]->addr[0].v4.s_addr;
+			flw->dest_ip = flows[i]->addr[1].v4.s_addr;
+			flw->src_port = flows[i]->port[0];
+			flw->dest_port = flows[i]->port[1];
+			flw->flow_packets = htonl(flows[i]->packets[0]);
+			flw->flow_octets = htonl(flows[i]->octets[0]);
+			flw->flow_start =
+			    htonl(timeval_sub_ms(&flows[i]->flow_start,
+			    system_boot_time));
+			flw->flow_finish = 
+			    htonl(timeval_sub_ms(&flows[i]->flow_last,
+			    system_boot_time));
+			flw->protocol = flows[i]->protocol;
+			flw->tcp_flags = flows[i]->tcp_flags[0];
+			offset += sizeof(*flw);
+			j++;
+			hdr->flows++;
+		}
+		flw = (struct NF1_FLOW *)(packet + offset);
+
+		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;
+			flw->src_port = flows[i]->port[1];
+			flw->dest_port = flows[i]->port[0];
+			flw->flow_packets = htonl(flows[i]->packets[1]);
+			flw->flow_octets = htonl(flows[i]->octets[1]);
+			flw->flow_start =
+			    htonl(timeval_sub_ms(&flows[i]->flow_start,
+			    system_boot_time));
+			flw->flow_finish =
+			    htonl(timeval_sub_ms(&flows[i]->flow_last,
+			    system_boot_time));
+			flw->protocol = flows[i]->protocol;
+			flw->tcp_flags = flows[i]->tcp_flags[1];
+			offset += sizeof(*flw);
+			j++;
+			hdr->flows++;
+		}
+	}
+
+	/* Send any leftovers */
+	if (j != 0) {
+		if (verbose_flag)
+			logit(LOG_DEBUG, "Sending flow packet len = %d", offset);
+		hdr->flows = htons(hdr->flows);
+		errsz = sizeof(err);
+		getsockopt(nfsock, SOL_SOCKET, SO_ERROR,
+		    &err, &errsz); /* Clear ICMP errors */
+		if (send(nfsock, packet, (size_t)offset, 0) == -1)
+			return (-1);
+		num_packets++;
+	}
+
+	*flows_exported += j;
+	return (num_packets);
+}

+ 170 - 0
netflow5.c

@@ -0,0 +1,170 @@
+/*
+ * Copyright 2002 Damien Miller <djm@mindrot.org> 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.
+ */
+
+/* $Id: netflow5.c,v 1.3 2005/05/05 03:31:42 djm Exp $ */
+
+#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 $");
+
+/*
+ * This is the Cisco Netflow(tm) version 5 packet format
+ * Based on:
+ * http://www.cisco.com/univercd/cc/td/doc/product/rtrmgmt/nfc/nfc_3_0/nfc_ug/nfcform.htm
+ */
+struct NF5_HEADER {
+	u_int16_t version, flows;
+	u_int32_t uptime_ms, time_sec, time_nanosec, flow_sequence;
+	u_int8_t engine_type, engine_id, reserved1, reserved2;
+};
+struct NF5_FLOW {
+	u_int32_t src_ip, dest_ip, nexthop_ip;
+	u_int16_t if_index_in, if_index_out;
+	u_int32_t flow_packets, flow_octets;
+	u_int32_t flow_start, flow_finish;
+	u_int16_t src_port, dest_port;
+	u_int8_t pad1;
+	u_int8_t tcp_flags, protocol, tos;
+	u_int16_t src_as, dest_as;
+	u_int8_t src_mask, dst_mask;
+	u_int16_t pad2;
+};
+#define NF5_MAXFLOWS		30
+#define NF5_MAXPACKET_SIZE	(sizeof(struct NF5_HEADER) + \
+				 (NF5_MAXFLOWS * sizeof(struct NF5_FLOW)))
+
+/*
+ * Given an array of expired flows, send netflow v5 report packets
+ * Returns number of packets sent or -1 on error
+ */
+int
+send_netflow_v5(struct FLOW **flows, int num_flows, int nfsock,
+    u_int64_t *flows_exported, struct timeval *system_boot_time,
+    int verbose_flag)
+{
+	struct timeval now;
+	u_int32_t uptime_ms;
+	u_int8_t packet[NF5_MAXPACKET_SIZE];	/* Maximum allowed packet size (24 flows) */
+	struct NF5_HEADER *hdr = NULL;
+	struct NF5_FLOW *flw = NULL;
+	int i, j, offset, num_packets, err;
+	socklen_t errsz;
+	
+	gettimeofday(&now, NULL);
+	uptime_ms = timeval_sub_ms(&now, system_boot_time);
+
+	hdr = (struct NF5_HEADER *)packet;
+	for (num_packets = offset = j = i = 0; i < num_flows; i++) {
+		if (j >= NF5_MAXFLOWS - 1) {
+			if (verbose_flag)
+				logit(LOG_DEBUG, "Sending flow packet len = %d", offset);
+			hdr->flows = htons(hdr->flows);
+			errsz = sizeof(err);
+			getsockopt(nfsock, SOL_SOCKET, SO_ERROR,
+			    &err, &errsz); /* Clear ICMP errors */
+			if (send(nfsock, packet, (size_t)offset, 0) == -1)
+				return (-1);
+			*flows_exported += j;
+			j = 0;
+			num_packets++;
+		}
+		if (j == 0) {
+			memset(&packet, '\0', sizeof(packet));
+			hdr->version = htons(5);
+			hdr->flows = 0; /* Filled in as we go */
+			hdr->uptime_ms = htonl(uptime_ms);
+			hdr->time_sec = htonl(now.tv_sec);
+			hdr->time_nanosec = htonl(now.tv_usec * 1000);
+			hdr->flow_sequence = htonl(*flows_exported);
+			/* Other fields are left zero */
+			offset = sizeof(*hdr);
+		}		
+		flw = (struct NF5_FLOW *)(packet + offset);
+
+		/* NetFlow v.5 doesn't do IPv6 */
+		if (flows[i]->af != AF_INET)
+			continue;
+		if (flows[i]->octets[0] > 0) {
+			flw->src_ip = flows[i]->addr[0].v4.s_addr;
+			flw->dest_ip = flows[i]->addr[1].v4.s_addr;
+			flw->src_port = flows[i]->port[0];
+			flw->dest_port = flows[i]->port[1];
+			flw->flow_packets = htonl(flows[i]->packets[0]);
+			flw->flow_octets = htonl(flows[i]->octets[0]);
+			flw->flow_start =
+			    htonl(timeval_sub_ms(&flows[i]->flow_start,
+			    system_boot_time));
+			flw->flow_finish =
+			    htonl(timeval_sub_ms(&flows[i]->flow_last,
+			    system_boot_time));
+			flw->tcp_flags = flows[i]->tcp_flags[0];
+			flw->protocol = flows[i]->protocol;
+			offset += sizeof(*flw);
+			j++;
+			hdr->flows++;
+		}
+		flw = (struct NF5_FLOW *)(packet + offset);
+
+		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;
+			flw->src_port = flows[i]->port[1];
+			flw->dest_port = flows[i]->port[0];
+			flw->flow_packets = htonl(flows[i]->packets[1]);
+			flw->flow_octets = htonl(flows[i]->octets[1]);
+			flw->flow_start =
+			    htonl(timeval_sub_ms(&flows[i]->flow_start,
+			    system_boot_time));
+			flw->flow_finish =
+			    htonl(timeval_sub_ms(&flows[i]->flow_last,
+			    system_boot_time));
+			flw->tcp_flags = flows[i]->tcp_flags[1];
+			flw->protocol = flows[i]->protocol;
+			offset += sizeof(*flw);
+			j++;
+			hdr->flows++;
+		}
+	}
+
+	/* Send any leftovers */
+	if (j != 0) {
+		if (verbose_flag)
+			logit(LOG_DEBUG, "Sending v5 flow packet len = %d",
+			    offset);
+		hdr->flows = htons(hdr->flows);
+		errsz = sizeof(err);
+		getsockopt(nfsock, SOL_SOCKET, SO_ERROR,
+		    &err, &errsz); /* Clear ICMP errors */
+		if (send(nfsock, packet, (size_t)offset, 0) == -1)
+			return (-1);
+		num_packets++;
+	}
+
+	*flows_exported += j;
+	return (num_packets);
+}
+

+ 377 - 0
netflow9.c

@@ -0,0 +1,377 @@
+/*
+ * Copyright 2002 Damien Miller <djm@mindrot.org> 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.
+ */
+
+/* $Id: netflow9.c,v 1.9 2006/03/16 08:23:13 djm Exp $ */
+
+#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 $");
+
+/* Netflow v.9 */
+struct NF9_HEADER {
+	u_int16_t version, flows;
+	u_int32_t uptime_ms, time_sec;
+	u_int32_t package_sequence, source_id;
+} __packed;
+struct NF9_FLOWSET_HEADER_COMMON {
+	u_int16_t flowset_id, length;
+} __packed;
+struct NF9_TEMPLATE_FLOWSET_HEADER {
+	struct NF9_FLOWSET_HEADER_COMMON c;
+	u_int16_t template_id, count;
+} __packed;
+struct NF9_TEMPLATE_FLOWSET_RECORD {
+	u_int16_t type, length;
+} __packed;
+struct NF9_DATA_FLOWSET_HEADER {
+	struct NF9_FLOWSET_HEADER_COMMON c;
+} __packed;
+#define NF9_TEMPLATE_FLOWSET_ID		0
+#define NF9_OPTIONS_FLOWSET_ID		1
+#define NF9_MIN_RECORD_FLOWSET_ID	256
+
+/* Flowset record types the we care about */
+#define NF9_IN_BYTES			1
+#define NF9_IN_PACKETS			2
+/* ... */
+#define NF9_IN_PROTOCOL			4
+/* ... */
+#define NF9_TCP_FLAGS			6
+#define NF9_L4_SRC_PORT			7
+#define NF9_IPV4_SRC_ADDR		8
+/* ... */
+#define NF9_L4_DST_PORT			11
+#define NF9_IPV4_DST_ADDR		12
+/* ... */
+#define NF9_LAST_SWITCHED		21
+#define NF9_FIRST_SWITCHED		22
+/* ... */
+#define NF9_IPV6_SRC_ADDR		27
+#define NF9_IPV6_DST_ADDR		28
+/* ... */
+#define NF9_IP_PROTOCOL_VERSION		60
+
+/* Stuff pertaining to the templates that softflowd uses */
+#define NF9_SOFTFLOWD_TEMPLATE_NRECORDS	11
+struct NF9_SOFTFLOWD_TEMPLATE {
+	struct NF9_TEMPLATE_FLOWSET_HEADER h;
+	struct NF9_TEMPLATE_FLOWSET_RECORD r[NF9_SOFTFLOWD_TEMPLATE_NRECORDS];
+} __packed;
+
+/* softflowd data flowset types */
+struct NF9_SOFTFLOWD_DATA_COMMON {
+	u_int32_t first_switched, last_switched;
+	u_int32_t bytes, packets;
+	u_int16_t src_port, dst_port;
+	u_int8_t protocol, tcp_flags, ipproto;
+} __packed;
+
+struct NF9_SOFTFLOWD_DATA_V4 {
+	u_int32_t src_addr, dst_addr;
+	struct NF9_SOFTFLOWD_DATA_COMMON c;
+} __packed;
+
+struct NF9_SOFTFLOWD_DATA_V6 {
+	u_int8_t src_addr[16], dst_addr[16];
+	struct NF9_SOFTFLOWD_DATA_COMMON c;
+} __packed;
+
+/* Local data: templates and counters */
+#define NF9_SOFTFLOWD_MAX_PACKET_SIZE	512
+#define NF9_SOFTFLOWD_V4_TEMPLATE_ID	1024
+#define NF9_SOFTFLOWD_V6_TEMPLATE_ID	2048
+
+#define NF9_DEFAULT_TEMPLATE_INTERVAL	16
+
+static struct NF9_SOFTFLOWD_TEMPLATE v4_template;
+static struct NF9_SOFTFLOWD_TEMPLATE v6_template;
+static int nf9_pkts_until_template = -1;
+
+static void
+nf9_init_template(void)
+{
+	bzero(&v4_template, sizeof(v4_template));
+	v4_template.h.c.flowset_id = htons(0);
+	v4_template.h.c.length = htons(sizeof(v4_template));
+	v4_template.h.template_id = htons(NF9_SOFTFLOWD_V4_TEMPLATE_ID);
+	v4_template.h.count = htons(NF9_SOFTFLOWD_TEMPLATE_NRECORDS);
+	v4_template.r[0].type = htons(NF9_IPV4_SRC_ADDR);
+	v4_template.r[0].length = htons(4);
+	v4_template.r[1].type = htons(NF9_IPV4_DST_ADDR);
+	v4_template.r[1].length = htons(4);
+	v4_template.r[2].type = htons(NF9_LAST_SWITCHED);
+	v4_template.r[2].length = htons(4);
+	v4_template.r[3].type = htons(NF9_FIRST_SWITCHED);
+	v4_template.r[3].length = htons(4);
+	v4_template.r[4].type = htons(NF9_IN_BYTES);
+	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[10].length = htons(1);
+
+	bzero(&v6_template, sizeof(v6_template));
+	v6_template.h.c.flowset_id = htons(0);
+	v6_template.h.c.length = htons(sizeof(v6_template));
+	v6_template.h.template_id = htons(NF9_SOFTFLOWD_V6_TEMPLATE_ID);
+	v6_template.h.count = htons(NF9_SOFTFLOWD_TEMPLATE_NRECORDS);
+	v6_template.r[0].type = htons(NF9_IPV6_SRC_ADDR);
+	v6_template.r[0].length = htons(16);
+	v6_template.r[1].type = htons(NF9_IPV6_DST_ADDR);
+	v6_template.r[1].length = htons(16);
+	v6_template.r[2].type = htons(NF9_LAST_SWITCHED);
+	v6_template.r[2].length = htons(4);
+	v6_template.r[3].type = htons(NF9_FIRST_SWITCHED);
+	v6_template.r[3].length = htons(4);
+	v6_template.r[4].type = htons(NF9_IN_BYTES);
+	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);
+	v6_template.r[10].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)
+{
+	union {
+		struct NF9_SOFTFLOWD_DATA_V4 d4;
+		struct NF9_SOFTFLOWD_DATA_V6 d6;
+	} d[2];
+	struct NF9_SOFTFLOWD_DATA_COMMON *dc[2];
+	u_int freclen, ret_len, nflows;
+
+	bzero(d, sizeof(d));
+	*len_used = nflows = ret_len = 0;
+	switch (flow->af) {
+	case AF_INET:
+		freclen = sizeof(struct NF9_SOFTFLOWD_DATA_V4);
+		memcpy(&d[0].d4.src_addr, &flow->addr[0].v4, 4);
+		memcpy(&d[0].d4.dst_addr, &flow->addr[1].v4, 4);
+		memcpy(&d[1].d4.src_addr, &flow->addr[1].v4, 4);
+		memcpy(&d[1].d4.dst_addr, &flow->addr[0].v4, 4);
+		dc[0] = &d[0].d4.c;
+		dc[1] = &d[1].d4.c;
+		dc[0]->ipproto = dc[1]->ipproto = 4;
+		break;
+	case AF_INET6:
+		freclen = sizeof(struct NF9_SOFTFLOWD_DATA_V6);
+		memcpy(&d[0].d6.src_addr, &flow->addr[0].v6, 16);
+		memcpy(&d[0].d6.dst_addr, &flow->addr[1].v6, 16);
+		memcpy(&d[1].d6.src_addr, &flow->addr[1].v6, 16);
+		memcpy(&d[1].d6.dst_addr, &flow->addr[0].v6, 16);
+		dc[0] = &d[0].d6.c;
+		dc[1] = &d[1].d6.c;
+		dc[0]->ipproto = dc[1]->ipproto = 6;
+		break;
+	default:
+		return (-1);
+	}
+
+	dc[0]->first_switched = dc[1]->first_switched = 
+	    htonl(timeval_sub_ms(&flow->flow_start, system_boot_time));
+	dc[0]->last_switched = dc[1]->last_switched = 
+	    htonl(timeval_sub_ms(&flow->flow_last, system_boot_time));
+	dc[0]->bytes = htonl(flow->octets[0]);
+	dc[1]->bytes = htonl(flow->octets[1]);
+	dc[0]->packets = htonl(flow->packets[0]);
+	dc[1]->packets = htonl(flow->packets[1]);
+	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;
+	dc[0]->tcp_flags = flow->tcp_flags[0];
+	dc[1]->tcp_flags = flow->tcp_flags[1];
+
+	if (flow->octets[0] > 0) {
+		if (ret_len + freclen > len)
+			return (-1);
+		memcpy(packet + ret_len, &d[0], freclen);
+		ret_len += freclen;
+		nflows++;
+	}
+	if (flow->octets[1] > 0) {
+		if (ret_len + freclen > len)
+			return (-1);
+		memcpy(packet + ret_len, &d[1], freclen);
+		ret_len += freclen;
+		nflows++;
+	}
+
+	*len_used = ret_len;
+	return (nflows);
+}
+
+/*
+ * Given an array of expired flows, send netflow v9 report packets
+ * Returns number of packets sent or -1 on error
+ */
+int
+send_netflow_v9(struct FLOW **flows, int num_flows, int nfsock,
+    u_int64_t *flows_exported, struct timeval *system_boot_time,
+    int verbose_flag)
+{
+	struct NF9_HEADER *nf9;
+	struct NF9_DATA_FLOWSET_HEADER *dh;
+	struct timeval now;
+	u_int offset, last_af, i, j, num_packets, inc, last_valid;
+	socklen_t errsz;
+	int err, r;
+	u_char packet[NF9_SOFTFLOWD_MAX_PACKET_SIZE];
+
+	gettimeofday(&now, NULL);
+
+	if (nf9_pkts_until_template == -1) {
+		nf9_init_template();
+		nf9_pkts_until_template = 0;
+	}		
+
+	last_valid = num_packets = 0;
+	for (j = 0; j < num_flows;) {
+		bzero(packet, sizeof(packet));
+		nf9 = (struct NF9_HEADER *)packet;
+
+		nf9->version = htons(9);
+		nf9->flows = 0; /* Filled as we go, htons at end */
+		nf9->uptime_ms = htonl(timeval_sub_ms(&now, system_boot_time));
+		nf9->time_sec = htonl(time(NULL));
+		nf9->package_sequence = htonl(*flows_exported + j);
+		nf9->source_id = 0;
+		offset = sizeof(*nf9);
+
+		/* Refresh template headers if we need to */
+		if (nf9_pkts_until_template <= 0) {
+			memcpy(packet + offset, &v4_template,
+			    sizeof(v4_template));
+			offset += sizeof(v4_template);
+			memcpy(packet + offset, &v6_template,
+			    sizeof(v6_template));
+			offset += sizeof(v6_template);
+			nf9_pkts_until_template = NF9_DEFAULT_TEMPLATE_INTERVAL;
+		}
+
+		dh = NULL;
+		last_af = 0;
+		for (i = 0; i + j < num_flows; i++) {
+			if (dh == NULL || flows[i + j]->af != last_af) {
+				if (dh != NULL) {
+					if (offset % 4 != 0) {
+						/* Pad to multiple of 4 */
+						dh->c.length += 4 - (offset % 4);
+						offset += 4 - (offset % 4);
+					}
+					/* Finalise last header */
+					dh->c.length = htons(dh->c.length);
+				}
+				if (offset + sizeof(*dh) > sizeof(packet)) {
+					/* Mark header is finished */
+					dh = NULL;
+					break;
+				}
+				dh = (struct NF9_DATA_FLOWSET_HEADER *)
+				    (packet + offset);
+				dh->c.flowset_id =
+				    (flows[i + j]->af == AF_INET) ?
+				    v4_template.h.template_id : 
+				    v6_template.h.template_id;
+				last_af = flows[i + j]->af;
+				last_valid = offset;
+				dh->c.length = sizeof(*dh); /* Filled as we go */
+				offset += sizeof(*dh);
+			}
+
+			r = nf_flow_to_flowset(flows[i + j], packet + offset,
+			    sizeof(packet) - offset, system_boot_time, &inc);
+			if (r <= 0) {
+				/* yank off data header, if we had to go back */
+				if (last_valid)
+					offset = last_valid;
+				break;
+			}
+			offset += inc;
+			dh->c.length += inc;
+			nf9->flows += r;
+			last_valid = 0; /* Don't clobber this header now */
+			if (verbose_flag) {
+				logit(LOG_DEBUG, "Flow %d/%d: "
+				    "r %d offset %d type %04x len %d(0x%04x) "
+				    "flows %d", r, i, j, offset, 
+				    dh->c.flowset_id, dh->c.length, 
+				    dh->c.length, nf9->flows);
+			}
+		}
+		/* Don't finish header if it has already been done */
+		if (dh != NULL) {
+			if (offset % 4 != 0) {
+				/* Pad to multiple of 4 */
+				dh->c.length += 4 - (offset % 4);
+				offset += 4 - (offset % 4);
+			}
+			/* Finalise last header */
+			dh->c.length = htons(dh->c.length);
+		}
+		nf9->flows = htons(nf9->flows);
+
+		if (verbose_flag)
+			logit(LOG_DEBUG, "Sending flow packet len = %d", offset);
+		errsz = sizeof(err);
+		/* Clear ICMP errors */
+		getsockopt(nfsock, SOL_SOCKET, SO_ERROR, &err, &errsz); 
+		if (send(nfsock, packet, (size_t)offset, 0) == -1)
+			return (-1);
+		num_packets++;
+		nf9_pkts_until_template--;
+
+		j += i;
+	}
+
+	*flows_exported += j;
+	return (num_packets);
+}
+
+void
+netflow9_resend_template(void)
+{
+	if (nf9_pkts_until_template > 0)
+		nf9_pkts_until_template = 0;
+}

+ 95 - 0
softflowctl.8

@@ -0,0 +1,95 @@
+.\" $Id: softflowctl.8,v 1.2 2006/03/16 08:24:19 djm Exp $
+.\"
+.\" Copyright (c) 2002 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.
+.\"
+.Dd October 18, 2002
+.Dt SOFTFLOWCTL 8
+.Os
+.Sh NAME
+.Nm softflowctl
+.Nd Remote control program for softflowd
+.Sh SYNOPSIS
+.Nm softflowctl
+.Op Fl h
+.Op Fl c Ar ctl_sock
+.Ar command
+.Sh DESCRIPTION
+.Nm
+is a remote control program used to control a running
+.Xr softflowd 8 
+daemon.
+.Pp
+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 
+.Pa /var/run/softflowd.ctl
+.It Fl h
+Displays commandline usage information.
+.El
+.Pp
+.Sh COMMANDS
+.Bl -tag -width Ds
+.It Pa shutdown
+Ask
+.Xr softflowd 8
+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.
+.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.
+.It Pa statistics
+Return statistics collected by 
+.Xr softflowd 8
+on expired flows.
+.It Pa debug+
+Increase the debugging level of 
+.Xr softflowd 8
+.It Pa debug-
+Decrease the debugging level.
+.It Pa stop-gather
+Stops network data collection by
+.Xr softflowd 8 .
+.It Pa start-gather
+Resumes network data collection.
+.It Pa dump-flows
+Return information on all tracked flows.
+.It Pa timeouts
+Print information on flow timeout parameters.
+.It Pa send-template
+Resend a NetFlow v.9 template record before the next flow export.
+Has no effect for other flow export versions.
+.El
+.Sh AUTHORS
+Damien Miller <djm@mindrot.org>
+.Sh SEE ALSO
+.Xr softflowd 8

+ 114 - 0
softflowctl.c

@@ -0,0 +1,114 @@
+/*
+ * Copyright 2002 Damien Miller <djm@mindrot.org> 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"
+
+RCSID("$Id: softflowctl.c,v 1.6 2004/09/10 09:20:56 djm Exp $");
+
+static void
+usage(void)
+{
+	fprintf(stderr, "Usage: [-c ctlsock] softflowctl [command]\n");
+}
+
+int
+main(int argc, char **argv)
+{
+	const char *ctlsock_path;
+	char buf[8192], *command;
+	struct sockaddr_un ctl;
+	socklen_t ctllen;
+	int ctlsock, ch;
+	FILE *ctlf;
+	extern char *optarg;
+	extern int optind;
+
+	ctlsock_path = DEFAULT_CTLSOCK;
+	while ((ch = getopt(argc, argv, "hc:")) != -1) {
+		switch (ch) {
+		case 'h':
+			usage();
+			return (0);
+		case 'c':
+			ctlsock_path = optarg;
+			break;
+		default:
+			fprintf(stderr, "Invalid commandline option.\n");
+			usage();
+			exit(1);
+		}
+	}
+	
+	/* Accept only one argument */
+	if (optind != argc - 1) {
+		usage();
+		exit(1);
+	}
+	command = argv[optind];
+
+	memset(&ctl, '\0', sizeof(ctl));
+	if (strlcpy(ctl.sun_path, ctlsock_path, sizeof(ctl.sun_path)) >= 
+	    sizeof(ctl.sun_path)) {
+		fprintf(stderr, "Control socket path too long.\n");
+		exit(1);
+	}
+	ctl.sun_path[sizeof(ctl.sun_path) - 1] = '\0';
+	ctl.sun_family = AF_UNIX;
+	ctllen = offsetof(struct sockaddr_un, sun_path) +
+            strlen(ctlsock_path) + 1;
+#ifdef SOCK_HAS_LEN 
+	ctl.sun_len = ctllen;
+#endif
+	if ((ctlsock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+		fprintf(stderr, "ctl socket() error: %s\n", 
+		    strerror(errno));
+		exit(1);
+	}
+	if (connect(ctlsock, (struct sockaddr*)&ctl, sizeof(ctl)) == -1) {
+		fprintf(stderr, "ctl connect(\"%s\") error: %s\n",
+		    ctl.sun_path, strerror(errno));
+		exit(1);
+	}
+	
+	if ((ctlf = fdopen(ctlsock, "r+")) == NULL) {
+		fprintf(stderr, "fdopen: %s\n", strerror(errno));
+		exit(1);
+	}
+	setlinebuf(ctlf);
+	
+	/* Send command */
+	if (fprintf(ctlf, "%s\n", command) < 0) {
+		fprintf(stderr, "write: %s\n", strerror(errno));
+		exit(1);
+	}
+
+	/* Write out reply */
+	while((fgets(buf, sizeof(buf), ctlf)) != NULL)
+		fputs(buf, stdout);
+
+	fclose(ctlf);
+	close(ctlsock);
+
+	exit(0);
+}

+ 402 - 0
softflowd.8

@@ -0,0 +1,402 @@
+.\" $Id: softflowd.8,v 1.17 2006/02/11 11:27:38 djm Exp $
+.\"
+.\" Copyright (c) 2002 Damien Miller.  All rights reserved.
+.\" Portions Copyright (c) 2001 Kevin Steves.  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.
+.\"
+.Dd October 14, 2002
+.Dt SOFTFLOWD 8
+.Os
+.Sh NAME
+.Nm softflowd
+.Nd Traffic flow monitoring
+.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 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
+.Sh DESCRIPTION
+.Nm
+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. 
+.Pp
+The intended use of
+.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 
+information.
+However, too few statistics are collected to make this
+mode really useful for anything other than debugging.
+.Pp
+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 
+.Xr tcpdump 8 .
+Traffic may be filtered with an optional
+.Xr bpf 4
+program, specified on the command-line as
+.Ar bpf_prog .
+.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). 
+.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 
+.Ar max_flows
+(default: 8192).
+In this last case, flows are expired oldest-first. 
+.Pp
+Upon expiry, the flow information is accumulated into statistics which may
+be viewed using 
+.Xr softflowctl 8 .
+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 
+export format.
+These records are sent to the specified
+.Ar host
+and
+.Ar port .
+The host may represent a unicast host or a multicast group.
+.Pp
+The command-line options are as follows:
+.Bl -tag -width Ds
+.It Fl n Ar host:port
+Specify the 
+.Ar host
+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 
+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 
+.Xr services 5
+or a numeric port.
+.It Fl i Ar interface
+Specify a network interface on which to listen for traffic.
+Either the 
+.Fl i
+or the
+.Fl r 
+options must be specified.
+.It Fl r Ar pcap_file
+Specify that
+.Nm
+should read from a 
+.Xr pcap 3
+packet capture (such as one created with the 
+.Fl w
+option of 
+.Xr tcpdump 8 )
+file rather than a network interface. 
+.Nm
+processes the whole capture file and only expires flows when 
+.Ar max_flows
+is exceeded. In this mode, 
+.Nm
+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 
+.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
+.It Fl m Ar max_flows
+Specifies 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
+small amount as  expiry processing happens less frequently than traffic
+collection.
+The default is 8192 flows, which corresponds to slightly less
+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 
+.Sx Timeouts
+section for the valid timeout names and their meanings.
+The 
+.Ar time
+parameter may be specified using one of the formats explained in the 
+.Sx Time Formats
+section below.
+.It Fl d
+Specify that 
+.Nm
+should not fork and daemonise itself.
+.It Fl 6
+Forces
+.Nm
+To track IPv6 flows even if the NetFlow export protocol does not support
+reporting them.
+This is useful for debugging and statistics gathering only.
+.It Fl D
+Places
+.Nm
+in a debugging mode.
+This implies the 
+.Fl d
+and
+.Fl 6
+flags and turns on additional debugging output.
+.It Fl h
+Displays commandline usage information.
+.It Fl L Ar hoplimit
+Sets 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
+.Nm
+should be used to define a flow.
+.Ar track_level
+may be one of:
+.Dq full
+(track everything in the flow, the default),
+.Dq proto
+(track source and destination addresses and protocol), or
+.Dq ip
+(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 
+and CPU load that
+.Nm
+will place on the system at the cost of some detail.
+.El
+.Pp
+Any further commandline arguments will be concatenated together and 
+applied as a 
+.Xr bpf 4
+packet filter.
+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 
+option) and their meanings are:
+.Bl -tag -width Ds
+.It Ar general
+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 
+sent by one or both endpoints.
+.It Ar tcp.fin
+This timeout is applied to a TCP connection when a FIN packet has been
+sent by both endpoints.
+.It Ar udp
+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
+.Ar maxlife
+seconds.
+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 
+.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. 
+.Xr softflowctl 8
+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:
+.Sm off
+.Ar time Op Ar qualifier ,
+.Sm on
+where
+.Ar time
+is a positive integer value and
+.Ar qualifier
+is one of the following:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Cm <none>
+seconds
+.It Cm s | Cm S
+seconds
+.It Cm m | Cm M
+minutes
+.It Cm h | Cm H
+hours
+.It Cm d | Cm D
+days
+.It Cm w | Cm W
+weeks
+.El
+.Pp
+Each member of the sequence is added together to calculate
+the total time value.
+.Pp
+Time format examples:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It 600
+600 seconds (10 minutes)
+.It 10m
+10 minutes
+.It 1h30m
+1 hour 30 minutes (90 minutes)
+.El
+.Ss Run-time Control
+.Pp
+A daemonised
+.Nm
+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 
+.Dv SIGTERM
+or 
+.DV SIGINT
+.Nm
+will cause 
+.Nm
+to exit, after expiring all flows (and thus sending flow export packets 
+if 
+.Fl -n 
+was specified on the commandline).
+If you do not want to export flows upon shutdown, clear them first with
+.Xr softflowctl 8 .
+
+.Sh EXAMPLES
+.Bl -tag -width Ds
+.It softflowd -i fxp0
+This commandlie 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
+.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 
+.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 
+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
+and pid file.
+Similar commandlines 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
+.Nm
+is in daemon mode.
+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 
+.Fl c
+command-line option.
+.El
+.Sh BUGS
+Currently
+.Nm
+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 
+fragmented packets, but will not record TCP or UDP port information.
+.Sh AUTHORS
+Damien Miller <djm@mindrot.org>
+.Sh SEE ALSO
+.Xr softflowctl 8 ,
+.Xr tcpdump 8 ,
+.Xr pcap 3 ,
+.Xr bpf 4
+.Bd -literal
+http://www.cisco.com/univercd/cc/td/doc/product/rtrmgmt/nfc/nfc_3_0/nfc_ug/nfcform.htm

File diff suppressed because it is too large
+ 1934 - 0
softflowd.c


+ 210 - 0
softflowd.h

@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2002 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 _SOFTFLOWD_H
+#define _SOFTFLOWD_H
+
+#include "common.h"
+#include "sys-tree.h"
+#include "treetype.h"
+
+/* User to setuid to and directory to chroot to when we drop privs */
+#ifndef PRIVDROP_USER
+# define PRIVDROP_USER		"nobody"
+#endif
+
+#define PRIVDROP_CHROOT_DIR	"/var/empty"
+
+/*
+ * Capture length for libpcap: Must fit the link layer header, plus 
+ * a maximally sized ip/ipv6 header and most of a TCP header
+ */
+#define LIBPCAP_SNAPLEN_V4		96
+#define LIBPCAP_SNAPLEN_V6		160
+
+/*
+ * Timeouts
+ */
+#define DEFAULT_TCP_TIMEOUT		3600
+#define DEFAULT_TCP_RST_TIMEOUT		120
+#define DEFAULT_TCP_FIN_TIMEOUT		300
+#define DEFAULT_UDP_TIMEOUT		300
+#define DEFAULT_ICMP_TIMEOUT		300
+#define DEFAULT_GENERAL_TIMEOUT		3600
+#define DEFAULT_MAXIMUM_LIFETIME	(3600*24*7)
+#define DEFAULT_EXPIRY_INTERVAL		60
+
+/*
+ * Default maximum number of flow to track simultaneously 
+ * 8192 corresponds to just under 1Mb of flow data
+ */
+#define DEFAULT_MAX_FLOWS	8192
+
+/* Store a couple of statistics, maybe more in the future */
+struct STATISTIC {
+	double min, mean, max;
+};
+
+/* Flow tracking levels */
+#define TRACK_FULL		1	/* src/dst/addr/port/proto 5-tuple */
+#define TRACK_IP_PROTO		2	/* src/dst/proto 3-tuple */
+#define TRACK_IP_ONLY		3	/* src/dst tuple */
+
+/*
+ * This structure is the root of the flow tracking system.
+ * It holds the root of the tree of active flows and the head of the
+ * tree of expiry events. It also collects miscellaneous statistics
+ */
+struct FLOWTRACK {
+	/* The flows and their expiry events */
+	FLOW_HEAD(FLOWS, FLOW) flows;		/* Top of flow tree */
+	EXPIRY_HEAD(EXPIRIES, EXPIRY) expiries;	/* Top of expiries tree */
+
+	unsigned int num_flows;			/* # of active flows */
+	u_int64_t next_flow_seq;		/* Next flow ID */
+
+	/* Stuff related to flow export */
+	struct timeval system_boot_time;	/* SysUptime */
+	int track_level;			/* See TRACK_* above */
+
+	/* Flow timeouts */
+	int tcp_timeout;			/* Open TCP connections */
+	int tcp_rst_timeout;			/* TCP flows after RST */
+	int tcp_fin_timeout;			/* TCP flows after bidi FIN */
+	int udp_timeout;			/* UDP flows */
+	int icmp_timeout;			/* ICMP flows */
+	int general_timeout;			/* Everything else */
+	int maximum_lifetime;			/* Maximum life for flows */
+	int expiry_interval;			/* Interval between expiries */ 
+
+	/* Statistics */
+	u_int64_t total_packets;		/* # of good packets */
+	u_int64_t frag_packets;			/* # of fragmented packets */
+	u_int64_t non_ip_packets;		/* # of not-IP packets */
+	u_int64_t bad_packets;			/* # of bad packets */
+	u_int64_t flows_expired;		/* # expired */
+	u_int64_t flows_exported;		/* # of flows sent */
+	u_int64_t flows_dropped;		/* # of flows dropped */
+	u_int64_t flows_force_expired;		/* # of flows forced out */
+	u_int64_t packets_sent;			/* # netflow packets sent */
+	struct STATISTIC duration;		/* Flow duration */
+	struct STATISTIC octets;		/* Bytes (bidir) */
+	struct STATISTIC packets;		/* Packets (bidir) */
+
+	/* Per protocol statistics */
+	u_int64_t flows_pp[256];
+	u_int64_t octets_pp[256];
+	u_int64_t packets_pp[256];
+	struct STATISTIC duration_pp[256];
+
+	/* Timeout statistics */
+	u_int64_t expired_general;
+	u_int64_t expired_tcp;
+	u_int64_t expired_tcp_rst;
+	u_int64_t expired_tcp_fin;
+	u_int64_t expired_udp;
+	u_int64_t expired_icmp;
+	u_int64_t expired_maxlife;
+	u_int64_t expired_overbytes;
+	u_int64_t expired_maxflows;
+	u_int64_t expired_flush;
+};
+
+/*
+ * This structure is an entry in the tree of flows that we are 
+ * currently tracking. 
+ *
+ * Because flows are matched _bi-directionally_, they must be stored in
+ * a canonical format: the numerically lowest address and port number must
+ * be stored in the first address and port array slot respectively.
+ */
+struct FLOW {
+	/* Housekeeping */
+	struct EXPIRY *expiry;			/* Pointer to expiry record */
+	FLOW_ENTRY(FLOW) trp;			/* Tree pointer */
+
+	/* Flow identity (all are in network byte order) */
+	int af;					/* Address family of flow */
+	u_int32_t ip6_flowlabel[2];		/* IPv6 Flowlabel */
+	union {
+		struct in_addr v4;
+		struct in6_addr v6;
+	} addr[2];				/* Endpoint addresses */
+	u_int16_t port[2];			/* Endpoint ports */
+	u_int8_t tcp_flags[2];			/* Cumulative OR of flags */
+	u_int8_t protocol;			/* Protocol */
+
+	/* Per-flow statistics (all in _host_ byte order) */
+	u_int64_t flow_seq;			/* Flow ID */
+	struct timeval flow_start;		/* Time of creation */
+	struct timeval flow_last;		/* Time of last traffic */
+
+	/* Per-endpoint statistics (all in _host_ byte order) */
+	u_int32_t octets[2];			/* Octets so far */
+	u_int32_t packets[2];			/* Packets so far */
+};
+
+/*
+ * This is an entry in the tree of expiry events. The tree is used to 
+ * avoid traversion the whole tree of active flows looking for ones to
+ * expire. "expires_at" is the time at which the flow should be discarded,
+ * or zero if it is scheduled for immediate disposal. 
+ *
+ * When a flow which hasn't been scheduled for immediate expiry registers 
+ * traffic, it is deleted from its current position in the tree and 
+ * re-inserted (subject to its updated timeout).
+ *
+ * Expiry scans operate by starting at the head of the tree and expiring
+ * each entry with expires_at < now
+ * 
+ */
+struct EXPIRY {
+	EXPIRY_ENTRY(EXPIRY) trp;		/* Tree pointer */
+	struct FLOW *flow;			/* pointer to flow */
+
+	u_int32_t expires_at;			/* time_t */
+	enum { 
+		R_GENERAL, R_TCP, R_TCP_RST, R_TCP_FIN, R_UDP, R_ICMP, 
+		R_MAXLIFE, R_OVERBYTES, R_OVERFLOWS, R_FLUSH
+	} reason;
+};
+
+/* Prototype for functions shared from softflowd.c */
+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, 
+    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,
+    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,
+    int verbose_flag);
+
+/* Force a resend of the flow template */
+void netflow9_resend_template(void);
+
+#endif /* _SOFTFLOWD_H */

+ 68 - 0
softflowd.init

@@ -0,0 +1,68 @@
+#!/bin/bash
+#
+# softflowd	Starts softflowd NetFlow probe
+# $Id: softflowd.init,v 1.1 2006/03/14 23:15:41 djm Exp $
+#
+# chkconfig: 2345 95 02
+# description: Starts and stops the softflowd Netflow probe
+
+# Source function library.
+. /etc/init.d/functions
+
+SOFTFLOW_CONF=/etc/sysconfig/softflowd
+SOFTFLOW_LOCK=/var/lock/subsys/softflowd
+SOFTFLOW_PROG=/usr/sbin/softflowd
+SOFTFLOW_OPTS="-i eth0"
+
+# Source config
+if [ -f $SOFTFLOW_CONF ]; then
+    . $SOFTFLOW_CONF
+fi
+
+[ -x $SOFTFLOW_PROG ] || exit 0
+
+RETVAL=0
+
+start() {
+    echo -n $"Starting softflowd: "
+    daemon $SOFTFLOW_PROG $SOFTFLOW_OPTS
+    RETVAL=$?
+    echo
+    [ $RETVAL -eq 0 ] && touch $SOFTFLOW_LOCK
+    return $RETVAL
+}
+stop() {
+    echo -n $"Shutting down softflowd: "
+    killproc $SOFTFLOW_PROG
+    RETVAL=$?
+    echo
+    [ $RETVAL -eq 0 ] && rm -f $SOFTFLOW_LOCK
+    return $RETVAL
+}
+restart() {
+    stop
+    start
+}
+
+case "$1" in
+    start)
+	start
+	;;
+    stop)
+	stop
+	;;
+    status)
+	status $SOFTFLOW_PROG
+	;;
+    restart|reload)
+	restart
+	;;
+    condrestart)
+	[ -f $SOFTFLOW_LOCK ] && restart || :
+	;;
+    *)
+	echo $"Usage: $0 {start|stop|status|restart|condrestart}"
+	exit 1
+esac
+
+exit $?

+ 50 - 0
softflowd.spec

@@ -0,0 +1,50 @@
+# $Id: softflowd.spec,v 1.1 2006/03/14 23:15:41 djm Exp $
+
+# 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:]')
+
+Name: softflowd
+Summary: Network traffic analyser capable of Cisco NetFlow data export
+Version: 0.9.8
+Release: 1.%{_RHTAG}
+Source: softflowd-%{version}.tar.gz
+Group: System/Utilities
+License: BSD
+BuildRoot: %{_tmppath}/%{name}-root
+URL: http://www.mindrot.org/softflowd.html
+Vendor: mindrot.org
+
+%description
+softflowd is a software implementation of a flow-based network traffic
+monitor.  softflowd 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.
+The intended use of softflowd is as a software implementation of Cisco’s
+NetFlow traffic account system.  softflowd supports data export using
+versions 1, 5 or 9 of the NetFlow protocol.
+
+%prep
+%setup
+
+%build
+%configure
+make
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make install DESTDIR=$RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d
+mkdir -p $RPM_BUILD_ROOT/etc/sysconfig
+cp %_sourcedir/softflowd.init $RPM_BUILD_ROOT/etc/rc.d/init.d/softflowd
+cp %_sourcedir/softflowd.sysconfig $RPM_BUILD_ROOT/etc/sysconfig/softflowd
+
+%files
+%defattr(-,root,root)
+/usr/sbin/*
+/usr/share/man/*
+%attr(0755,root,root) /etc/rc.d/init.d/softflowd
+%config(noreplace) %attr(0644,root,root) /etc/sysconfig/softflowd
+%doc ChangeLog README TODO
+
+%clean
+rm -rf $RPM_BUILD_ROOT

+ 10 - 0
softflowd.sysconfig

@@ -0,0 +1,10 @@
+# Config file for softflowd startup
+
+# Location of softflowd binary
+#SOFTFLOW_PROG=/usr/sbin/softflowd
+
+# Options passed to the softflowd program
+# Default - not very useful
+#SOFTFLOW_OPTS="-i eth0"
+# Example NetFlow v5 export from traffic on eth1
+#SOFTFLOW_OPTS="-v 5 -i eth1 -n hostname:2055"

+ 68 - 0
strlcat.c

@@ -0,0 +1,68 @@
+/* OPENBSD ORIGINAL: lib/libc/string/strlcat.c */
+
+/*	$OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $	*/
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "common.h"
+#ifndef HAVE_STRLCAT
+
+RCSID("$Id: strlcat.c,v 1.1 2004/09/10 09:08:08 djm Exp $");
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left).  At most siz-1 characters
+ * will be copied.  Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(src) + MIN(siz, strlen(initial dst)).
+ * If retval >= siz, truncation occurred.
+ */
+size_t
+strlcat(char *dst, const char *src, size_t siz)
+{
+	register char *d = dst;
+	register const char *s = src;
+	register size_t n = siz;
+	size_t dlen;
+
+	/* Find the end of dst and adjust bytes left but don't go past end */
+	while (n-- != 0 && *d != '\0')
+		d++;
+	dlen = d - dst;
+	n = siz - dlen;
+
+	if (n == 0)
+		return(dlen + strlen(s));
+	while (*s != '\0') {
+		if (n != 1) {
+			*d++ = *s;
+			n--;
+		}
+		s++;
+	}
+	*d = '\0';
+
+	return(dlen + (s - src));	/* count does not include NUL */
+}
+
+#endif /* !HAVE_STRLCAT */

+ 65 - 0
strlcpy.c

@@ -0,0 +1,65 @@
+/* OPENBSD ORIGINAL: lib/libc/string/strlcpy.c */
+
+/*	$OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $	*/
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "common.h"
+
+#ifndef HAVE_STRLCPY
+
+RCSID("$Id: strlcpy.c,v 1.1 2004/09/10 09:08:08 djm Exp $");
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Copy src to string dst of size siz.  At most siz-1 characters
+ * will be copied.  Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+strlcpy(char *dst, const char *src, size_t siz)
+{
+	register char *d = dst;
+	register const char *s = src;
+	register size_t n = siz;
+
+	/* Copy as many bytes as will fit */
+	if (n != 0 && --n != 0) {
+		do {
+			if ((*d++ = *s++) == 0)
+				break;
+		} while (--n != 0);
+	}
+
+	/* Not enough room in dst, add NUL and traverse rest of src */
+	if (n == 0) {
+		if (siz != 0)
+			*d = '\0';		/* NUL-terminate dst */
+		while (*s++)
+			;
+	}
+
+	return(s - src - 1);	/* count does not include NUL */
+}
+
+#endif /* !HAVE_STRLCPY */

+ 677 - 0
sys-tree.h

@@ -0,0 +1,677 @@
+/*	$OpenBSD: tree.h,v 1.9 2004/11/24 18:10:42 tdeval Exp $	*/
+/*
+ * Copyright 2002 Niels Provos <provos@citi.umich.edu>
+ * 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	_SYS_TREE_H_
+#define	_SYS_TREE_H_
+
+/*
+ * This file defines data structures for different types of trees:
+ * splay trees and red-black trees.
+ *
+ * A splay tree is a self-organizing data structure.  Every operation
+ * on the tree causes a splay to happen.  The splay moves the requested
+ * node to the root of the tree and partly rebalances it.
+ *
+ * This has the benefit that request locality causes faster lookups as
+ * the requested nodes move to the top of the tree.  On the other hand,
+ * every lookup causes memory writes.
+ *
+ * The Balance Theorem bounds the total access time for m operations
+ * and n inserts on an initially empty tree as O((m + n)lg n).  The
+ * amortized cost for a sequence of m accesses to a splay tree is O(lg n);
+ *
+ * A red-black tree is a binary search tree with the node color as an
+ * extra attribute.  It fulfills a set of conditions:
+ *	- every search path from the root to a leaf consists of the
+ *	  same number of black nodes,
+ *	- each red node (except for the root) has a black parent,
+ *	- each leaf node is black.
+ *
+ * Every operation on a red-black tree is bounded as O(lg n).
+ * The maximum height of a red-black tree is 2lg (n+1).
+ */
+
+#define SPLAY_HEAD(name, type)						\
+struct name {								\
+	struct type *sph_root; /* root of the tree */			\
+}
+
+#define SPLAY_INITIALIZER(root)						\
+	{ NULL }
+
+#define SPLAY_INIT(root) do {						\
+	(root)->sph_root = NULL;					\
+} while (0)
+
+#define SPLAY_ENTRY(type)						\
+struct {								\
+	struct type *spe_left; /* left element */			\
+	struct type *spe_right; /* right element */			\
+}
+
+#define SPLAY_LEFT(elm, field)		(elm)->field.spe_left
+#define SPLAY_RIGHT(elm, field)		(elm)->field.spe_right
+#define SPLAY_ROOT(head)		(head)->sph_root
+#define SPLAY_EMPTY(head)		(SPLAY_ROOT(head) == NULL)
+
+/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
+#define SPLAY_ROTATE_RIGHT(head, tmp, field) do {			\
+	SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field);	\
+	SPLAY_RIGHT(tmp, field) = (head)->sph_root;			\
+	(head)->sph_root = tmp;						\
+} while (0)
+	
+#define SPLAY_ROTATE_LEFT(head, tmp, field) do {			\
+	SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field);	\
+	SPLAY_LEFT(tmp, field) = (head)->sph_root;			\
+	(head)->sph_root = tmp;						\
+} while (0)
+
+#define SPLAY_LINKLEFT(head, tmp, field) do {				\
+	SPLAY_LEFT(tmp, field) = (head)->sph_root;			\
+	tmp = (head)->sph_root;						\
+	(head)->sph_root = SPLAY_LEFT((head)->sph_root, field);		\
+} while (0)
+
+#define SPLAY_LINKRIGHT(head, tmp, field) do {				\
+	SPLAY_RIGHT(tmp, field) = (head)->sph_root;			\
+	tmp = (head)->sph_root;						\
+	(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);	\
+} while (0)
+
+#define SPLAY_ASSEMBLE(head, node, left, right, field) do {		\
+	SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field);	\
+	SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
+	SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field);	\
+	SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field);	\
+} while (0)
+
+/* Generates prototypes and inline functions */
+
+#define SPLAY_PROTOTYPE(name, type, field, cmp)				\
+void name##_SPLAY(struct name *, struct type *);			\
+void name##_SPLAY_MINMAX(struct name *, int);				\
+struct type *name##_SPLAY_INSERT(struct name *, struct type *);		\
+struct type *name##_SPLAY_REMOVE(struct name *, struct type *);		\
+									\
+/* Finds the node with the same key as elm */				\
+static __inline struct type *						\
+name##_SPLAY_FIND(struct name *head, struct type *elm)			\
+{									\
+	if (SPLAY_EMPTY(head))						\
+		return(NULL);						\
+	name##_SPLAY(head, elm);					\
+	if ((cmp)(elm, (head)->sph_root) == 0)				\
+		return (head->sph_root);				\
+	return (NULL);							\
+}									\
+									\
+static __inline struct type *						\
+name##_SPLAY_NEXT(struct name *head, struct type *elm)			\
+{									\
+	name##_SPLAY(head, elm);					\
+	if (SPLAY_RIGHT(elm, field) != NULL) {				\
+		elm = SPLAY_RIGHT(elm, field);				\
+		while (SPLAY_LEFT(elm, field) != NULL) {		\
+			elm = SPLAY_LEFT(elm, field);			\
+		}							\
+	} else								\
+		elm = NULL;						\
+	return (elm);							\
+}									\
+									\
+static __inline struct type *						\
+name##_SPLAY_MIN_MAX(struct name *head, int val)			\
+{									\
+	name##_SPLAY_MINMAX(head, val);					\
+        return (SPLAY_ROOT(head));					\
+}
+
+/* Main splay operation.
+ * Moves node close to the key of elm to top
+ */
+#define SPLAY_GENERATE(name, type, field, cmp)				\
+struct type *								\
+name##_SPLAY_INSERT(struct name *head, struct type *elm)		\
+{									\
+    if (SPLAY_EMPTY(head)) {						\
+	    SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL;	\
+    } else {								\
+	    int __comp;							\
+	    name##_SPLAY(head, elm);					\
+	    __comp = (cmp)(elm, (head)->sph_root);			\
+	    if(__comp < 0) {						\
+		    SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
+		    SPLAY_RIGHT(elm, field) = (head)->sph_root;		\
+		    SPLAY_LEFT((head)->sph_root, field) = NULL;		\
+	    } else if (__comp > 0) {					\
+		    SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
+		    SPLAY_LEFT(elm, field) = (head)->sph_root;		\
+		    SPLAY_RIGHT((head)->sph_root, field) = NULL;	\
+	    } else							\
+		    return ((head)->sph_root);				\
+    }									\
+    (head)->sph_root = (elm);						\
+    return (NULL);							\
+}									\
+									\
+struct type *								\
+name##_SPLAY_REMOVE(struct name *head, struct type *elm)		\
+{									\
+	struct type *__tmp;						\
+	if (SPLAY_EMPTY(head))						\
+		return (NULL);						\
+	name##_SPLAY(head, elm);					\
+	if ((cmp)(elm, (head)->sph_root) == 0) {			\
+		if (SPLAY_LEFT((head)->sph_root, field) == NULL) {	\
+			(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
+		} else {						\
+			__tmp = SPLAY_RIGHT((head)->sph_root, field);	\
+			(head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
+			name##_SPLAY(head, elm);			\
+			SPLAY_RIGHT((head)->sph_root, field) = __tmp;	\
+		}							\
+		return (elm);						\
+	}								\
+	return (NULL);							\
+}									\
+									\
+void									\
+name##_SPLAY(struct name *head, struct type *elm)			\
+{									\
+	struct type __node, *__left, *__right, *__tmp;			\
+	int __comp;							\
+\
+	SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
+	__left = __right = &__node;					\
+\
+	while ((__comp = (cmp)(elm, (head)->sph_root))) {		\
+		if (__comp < 0) {					\
+			__tmp = SPLAY_LEFT((head)->sph_root, field);	\
+			if (__tmp == NULL)				\
+				break;					\
+			if ((cmp)(elm, __tmp) < 0){			\
+				SPLAY_ROTATE_RIGHT(head, __tmp, field);	\
+				if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
+					break;				\
+			}						\
+			SPLAY_LINKLEFT(head, __right, field);		\
+		} else if (__comp > 0) {				\
+			__tmp = SPLAY_RIGHT((head)->sph_root, field);	\
+			if (__tmp == NULL)				\
+				break;					\
+			if ((cmp)(elm, __tmp) > 0){			\
+				SPLAY_ROTATE_LEFT(head, __tmp, field);	\
+				if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
+					break;				\
+			}						\
+			SPLAY_LINKRIGHT(head, __left, field);		\
+		}							\
+	}								\
+	SPLAY_ASSEMBLE(head, &__node, __left, __right, field);		\
+}									\
+									\
+/* Splay with either the minimum or the maximum element			\
+ * Used to find minimum or maximum element in tree.			\
+ */									\
+void name##_SPLAY_MINMAX(struct name *head, int __comp) \
+{									\
+	struct type __node, *__left, *__right, *__tmp;			\
+\
+	SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
+	__left = __right = &__node;					\
+\
+	while (1) {							\
+		if (__comp < 0) {					\
+			__tmp = SPLAY_LEFT((head)->sph_root, field);	\
+			if (__tmp == NULL)				\
+				break;					\
+			if (__comp < 0){				\
+				SPLAY_ROTATE_RIGHT(head, __tmp, field);	\
+				if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
+					break;				\
+			}						\
+			SPLAY_LINKLEFT(head, __right, field);		\
+		} else if (__comp > 0) {				\
+			__tmp = SPLAY_RIGHT((head)->sph_root, field);	\
+			if (__tmp == NULL)				\
+				break;					\
+			if (__comp > 0) {				\
+				SPLAY_ROTATE_LEFT(head, __tmp, field);	\
+				if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
+					break;				\
+			}						\
+			SPLAY_LINKRIGHT(head, __left, field);		\
+		}							\
+	}								\
+	SPLAY_ASSEMBLE(head, &__node, __left, __right, field);		\
+}
+
+#define SPLAY_NEGINF	-1
+#define SPLAY_INF	1
+
+#define SPLAY_INSERT(name, x, y)	name##_SPLAY_INSERT(x, y)
+#define SPLAY_REMOVE(name, x, y)	name##_SPLAY_REMOVE(x, y)
+#define SPLAY_FIND(name, x, y)		name##_SPLAY_FIND(x, y)
+#define SPLAY_NEXT(name, x, y)		name##_SPLAY_NEXT(x, y)
+#define SPLAY_MIN(name, x)		(SPLAY_EMPTY(x) ? NULL	\
+					: name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
+#define SPLAY_MAX(name, x)		(SPLAY_EMPTY(x) ? NULL	\
+					: name##_SPLAY_MIN_MAX(x, SPLAY_INF))
+
+#define SPLAY_FOREACH(x, name, head)					\
+	for ((x) = SPLAY_MIN(name, head);				\
+	     (x) != NULL;						\
+	     (x) = SPLAY_NEXT(name, head, x))
+
+/* Macros that define a red-black tree */
+#define RB_HEAD(name, type)						\
+struct name {								\
+	struct type *rbh_root; /* root of the tree */			\
+}
+
+#define RB_INITIALIZER(root)						\
+	{ NULL }
+
+#define RB_INIT(root) do {						\
+	(root)->rbh_root = NULL;					\
+} while (0)
+
+#define RB_BLACK	0
+#define RB_RED		1
+#define RB_ENTRY(type)							\
+struct {								\
+	struct type *rbe_left;		/* left element */		\
+	struct type *rbe_right;		/* right element */		\
+	struct type *rbe_parent;	/* parent element */		\
+	int rbe_color;			/* node color */		\
+}
+
+#define RB_LEFT(elm, field)		(elm)->field.rbe_left
+#define RB_RIGHT(elm, field)		(elm)->field.rbe_right
+#define RB_PARENT(elm, field)		(elm)->field.rbe_parent
+#define RB_COLOR(elm, field)		(elm)->field.rbe_color
+#define RB_ROOT(head)			(head)->rbh_root
+#define RB_EMPTY(head)			(RB_ROOT(head) == NULL)
+
+#define RB_SET(elm, parent, field) do {					\
+	RB_PARENT(elm, field) = parent;					\
+	RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL;		\
+	RB_COLOR(elm, field) = RB_RED;					\
+} while (0)
+
+#define RB_SET_BLACKRED(black, red, field) do {				\
+	RB_COLOR(black, field) = RB_BLACK;				\
+	RB_COLOR(red, field) = RB_RED;					\
+} while (0)
+
+#ifndef RB_AUGMENT
+#define RB_AUGMENT(x)
+#endif
+
+#define RB_ROTATE_LEFT(head, elm, tmp, field) do {			\
+	(tmp) = RB_RIGHT(elm, field);					\
+	if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) {		\
+		RB_PARENT(RB_LEFT(tmp, field), field) = (elm);		\
+	}								\
+	RB_AUGMENT(elm);						\
+	if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) {		\
+		if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))	\
+			RB_LEFT(RB_PARENT(elm, field), field) = (tmp);	\
+		else							\
+			RB_RIGHT(RB_PARENT(elm, field), field) = (tmp);	\
+	} else								\
+		(head)->rbh_root = (tmp);				\
+	RB_LEFT(tmp, field) = (elm);					\
+	RB_PARENT(elm, field) = (tmp);					\
+	RB_AUGMENT(tmp);						\
+	if ((RB_PARENT(tmp, field)))					\
+		RB_AUGMENT(RB_PARENT(tmp, field));			\
+} while (0)
+
+#define RB_ROTATE_RIGHT(head, elm, tmp, field) do {			\
+	(tmp) = RB_LEFT(elm, field);					\
+	if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) {		\
+		RB_PARENT(RB_RIGHT(tmp, field), field) = (elm);		\
+	}								\
+	RB_AUGMENT(elm);						\
+	if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) {		\
+		if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))	\
+			RB_LEFT(RB_PARENT(elm, field), field) = (tmp);	\
+		else							\
+			RB_RIGHT(RB_PARENT(elm, field), field) = (tmp);	\
+	} else								\
+		(head)->rbh_root = (tmp);				\
+	RB_RIGHT(tmp, field) = (elm);					\
+	RB_PARENT(elm, field) = (tmp);					\
+	RB_AUGMENT(tmp);						\
+	if ((RB_PARENT(tmp, field)))					\
+		RB_AUGMENT(RB_PARENT(tmp, field));			\
+} while (0)
+
+/* Generates prototypes and inline functions */
+#define RB_PROTOTYPE(name, type, field, cmp)				\
+void name##_RB_INSERT_COLOR(struct name *, struct type *);	\
+void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
+struct type *name##_RB_REMOVE(struct name *, struct type *);		\
+struct type *name##_RB_INSERT(struct name *, struct type *);		\
+struct type *name##_RB_FIND(struct name *, struct type *);		\
+struct type *name##_RB_NEXT(struct type *);				\
+struct type *name##_RB_MINMAX(struct name *, int);			\
+									\
+
+/* Main rb operation.
+ * Moves node close to the key of elm to top
+ */
+#define RB_GENERATE(name, type, field, cmp)				\
+void									\
+name##_RB_INSERT_COLOR(struct name *head, struct type *elm)		\
+{									\
+	struct type *parent, *gparent, *tmp;				\
+	while ((parent = RB_PARENT(elm, field)) &&			\
+	    RB_COLOR(parent, field) == RB_RED) {			\
+		gparent = RB_PARENT(parent, field);			\
+		if (parent == RB_LEFT(gparent, field)) {		\
+			tmp = RB_RIGHT(gparent, field);			\
+			if (tmp && RB_COLOR(tmp, field) == RB_RED) {	\
+				RB_COLOR(tmp, field) = RB_BLACK;	\
+				RB_SET_BLACKRED(parent, gparent, field);\
+				elm = gparent;				\
+				continue;				\
+			}						\
+			if (RB_RIGHT(parent, field) == elm) {		\
+				RB_ROTATE_LEFT(head, parent, tmp, field);\
+				tmp = parent;				\
+				parent = elm;				\
+				elm = tmp;				\
+			}						\
+			RB_SET_BLACKRED(parent, gparent, field);	\
+			RB_ROTATE_RIGHT(head, gparent, tmp, field);	\
+		} else {						\
+			tmp = RB_LEFT(gparent, field);			\
+			if (tmp && RB_COLOR(tmp, field) == RB_RED) {	\
+				RB_COLOR(tmp, field) = RB_BLACK;	\
+				RB_SET_BLACKRED(parent, gparent, field);\
+				elm = gparent;				\
+				continue;				\
+			}						\
+			if (RB_LEFT(parent, field) == elm) {		\
+				RB_ROTATE_RIGHT(head, parent, tmp, field);\
+				tmp = parent;				\
+				parent = elm;				\
+				elm = tmp;				\
+			}						\
+			RB_SET_BLACKRED(parent, gparent, field);	\
+			RB_ROTATE_LEFT(head, gparent, tmp, field);	\
+		}							\
+	}								\
+	RB_COLOR(head->rbh_root, field) = RB_BLACK;			\
+}									\
+									\
+void									\
+name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
+{									\
+	struct type *tmp;						\
+	while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) &&	\
+	    elm != RB_ROOT(head)) {					\
+		if (RB_LEFT(parent, field) == elm) {			\
+			tmp = RB_RIGHT(parent, field);			\
+			if (RB_COLOR(tmp, field) == RB_RED) {		\
+				RB_SET_BLACKRED(tmp, parent, field);	\
+				RB_ROTATE_LEFT(head, parent, tmp, field);\
+				tmp = RB_RIGHT(parent, field);		\
+			}						\
+			if ((RB_LEFT(tmp, field) == NULL ||		\
+			    RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
+			    (RB_RIGHT(tmp, field) == NULL ||		\
+			    RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
+				RB_COLOR(tmp, field) = RB_RED;		\
+				elm = parent;				\
+				parent = RB_PARENT(elm, field);		\
+			} else {					\
+				if (RB_RIGHT(tmp, field) == NULL ||	\
+				    RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\
+					struct type *oleft;		\
+					if ((oleft = RB_LEFT(tmp, field)))\
+						RB_COLOR(oleft, field) = RB_BLACK;\
+					RB_COLOR(tmp, field) = RB_RED;	\
+					RB_ROTATE_RIGHT(head, tmp, oleft, field);\
+					tmp = RB_RIGHT(parent, field);	\
+				}					\
+				RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
+				RB_COLOR(parent, field) = RB_BLACK;	\
+				if (RB_RIGHT(tmp, field))		\
+					RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\
+				RB_ROTATE_LEFT(head, parent, tmp, field);\
+				elm = RB_ROOT(head);			\
+				break;					\
+			}						\
+		} else {						\
+			tmp = RB_LEFT(parent, field);			\
+			if (RB_COLOR(tmp, field) == RB_RED) {		\
+				RB_SET_BLACKRED(tmp, parent, field);	\
+				RB_ROTATE_RIGHT(head, parent, tmp, field);\
+				tmp = RB_LEFT(parent, field);		\
+			}						\
+			if ((RB_LEFT(tmp, field) == NULL ||		\
+			    RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
+			    (RB_RIGHT(tmp, field) == NULL ||		\
+			    RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
+				RB_COLOR(tmp, field) = RB_RED;		\
+				elm = parent;				\
+				parent = RB_PARENT(elm, field);		\
+			} else {					\
+				if (RB_LEFT(tmp, field) == NULL ||	\
+				    RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\
+					struct type *oright;		\
+					if ((oright = RB_RIGHT(tmp, field)))\
+						RB_COLOR(oright, field) = RB_BLACK;\
+					RB_COLOR(tmp, field) = RB_RED;	\
+					RB_ROTATE_LEFT(head, tmp, oright, field);\
+					tmp = RB_LEFT(parent, field);	\
+				}					\
+				RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
+				RB_COLOR(parent, field) = RB_BLACK;	\
+				if (RB_LEFT(tmp, field))		\
+					RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\
+				RB_ROTATE_RIGHT(head, parent, tmp, field);\
+				elm = RB_ROOT(head);			\
+				break;					\
+			}						\
+		}							\
+	}								\
+	if (elm)							\
+		RB_COLOR(elm, field) = RB_BLACK;			\
+}									\
+									\
+struct type *								\
+name##_RB_REMOVE(struct name *head, struct type *elm)			\
+{									\
+	struct type *child, *parent, *old = elm;			\
+	int color;							\
+	if (RB_LEFT(elm, field) == NULL)				\
+		child = RB_RIGHT(elm, field);				\
+	else if (RB_RIGHT(elm, field) == NULL)				\
+		child = RB_LEFT(elm, field);				\
+	else {								\
+		struct type *left;					\
+		elm = RB_RIGHT(elm, field);				\
+		while ((left = RB_LEFT(elm, field)))			\
+			elm = left;					\
+		child = RB_RIGHT(elm, field);				\
+		parent = RB_PARENT(elm, field);				\
+		color = RB_COLOR(elm, field);				\
+		if (child)						\
+			RB_PARENT(child, field) = parent;		\
+		if (parent) {						\
+			if (RB_LEFT(parent, field) == elm)		\
+				RB_LEFT(parent, field) = child;		\
+			else						\
+				RB_RIGHT(parent, field) = child;	\
+			RB_AUGMENT(parent);				\
+		} else							\
+			RB_ROOT(head) = child;				\
+		if (RB_PARENT(elm, field) == old)			\
+			parent = elm;					\
+		(elm)->field = (old)->field;				\
+		if (RB_PARENT(old, field)) {				\
+			if (RB_LEFT(RB_PARENT(old, field), field) == old)\
+				RB_LEFT(RB_PARENT(old, field), field) = elm;\
+			else						\
+				RB_RIGHT(RB_PARENT(old, field), field) = elm;\
+			RB_AUGMENT(RB_PARENT(old, field));		\
+		} else							\
+			RB_ROOT(head) = elm;				\
+		RB_PARENT(RB_LEFT(old, field), field) = elm;		\
+		if (RB_RIGHT(old, field))				\
+			RB_PARENT(RB_RIGHT(old, field), field) = elm;	\
+		if (parent) {						\
+			left = parent;					\
+			do {						\
+				RB_AUGMENT(left);			\
+			} while ((left = RB_PARENT(left, field)));	\
+		}							\
+		goto color;						\
+	}								\
+	parent = RB_PARENT(elm, field);					\
+	color = RB_COLOR(elm, field);					\
+	if (child)							\
+		RB_PARENT(child, field) = parent;			\
+	if (parent) {							\
+		if (RB_LEFT(parent, field) == elm)			\
+			RB_LEFT(parent, field) = child;			\
+		else							\
+			RB_RIGHT(parent, field) = child;		\
+		RB_AUGMENT(parent);					\
+	} else								\
+		RB_ROOT(head) = child;					\
+color:									\
+	if (color == RB_BLACK)						\
+		name##_RB_REMOVE_COLOR(head, parent, child);		\
+	return (old);							\
+}									\
+									\
+/* Inserts a node into the RB tree */					\
+struct type *								\
+name##_RB_INSERT(struct name *head, struct type *elm)			\
+{									\
+	struct type *tmp;						\
+	struct type *parent = NULL;					\
+	int comp = 0;							\
+	tmp = RB_ROOT(head);						\
+	while (tmp) {							\
+		parent = tmp;						\
+		comp = (cmp)(elm, parent);				\
+		if (comp < 0)						\
+			tmp = RB_LEFT(tmp, field);			\
+		else if (comp > 0)					\
+			tmp = RB_RIGHT(tmp, field);			\
+		else							\
+			return (tmp);					\
+	}								\
+	RB_SET(elm, parent, field);					\
+	if (parent != NULL) {						\
+		if (comp < 0)						\
+			RB_LEFT(parent, field) = elm;			\
+		else							\
+			RB_RIGHT(parent, field) = elm;			\
+		RB_AUGMENT(parent);					\
+	} else								\
+		RB_ROOT(head) = elm;					\
+	name##_RB_INSERT_COLOR(head, elm);				\
+	return (NULL);							\
+}									\
+									\
+/* Finds the node with the same key as elm */				\
+struct type *								\
+name##_RB_FIND(struct name *head, struct type *elm)			\
+{									\
+	struct type *tmp = RB_ROOT(head);				\
+	int comp;							\
+	while (tmp) {							\
+		comp = cmp(elm, tmp);					\
+		if (comp < 0)						\
+			tmp = RB_LEFT(tmp, field);			\
+		else if (comp > 0)					\
+			tmp = RB_RIGHT(tmp, field);			\
+		else							\
+			return (tmp);					\
+	}								\
+	return (NULL);							\
+}									\
+									\
+struct type *								\
+name##_RB_NEXT(struct type *elm)					\
+{									\
+	if (RB_RIGHT(elm, field)) {					\
+		elm = RB_RIGHT(elm, field);				\
+		while (RB_LEFT(elm, field))				\
+			elm = RB_LEFT(elm, field);			\
+	} else {							\
+		if (RB_PARENT(elm, field) &&				\
+		    (elm == RB_LEFT(RB_PARENT(elm, field), field)))	\
+			elm = RB_PARENT(elm, field);			\
+		else {							\
+			while (RB_PARENT(elm, field) &&			\
+			    (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\
+				elm = RB_PARENT(elm, field);		\
+			elm = RB_PARENT(elm, field);			\
+		}							\
+	}								\
+	return (elm);							\
+}									\
+									\
+struct type *								\
+name##_RB_MINMAX(struct name *head, int val)				\
+{									\
+	struct type *tmp = RB_ROOT(head);				\
+	struct type *parent = NULL;					\
+	while (tmp) {							\
+		parent = tmp;						\
+		if (val < 0)						\
+			tmp = RB_LEFT(tmp, field);			\
+		else							\
+			tmp = RB_RIGHT(tmp, field);			\
+	}								\
+	return (parent);						\
+}
+
+#define RB_NEGINF	-1
+#define RB_INF	1
+
+#define RB_INSERT(name, x, y)	name##_RB_INSERT(x, y)
+#define RB_REMOVE(name, x, y)	name##_RB_REMOVE(x, y)
+#define RB_FIND(name, x, y)	name##_RB_FIND(x, y)
+#define RB_NEXT(name, x, y)	name##_RB_NEXT(y)
+#define RB_MIN(name, x)		name##_RB_MINMAX(x, RB_NEGINF)
+#define RB_MAX(name, x)		name##_RB_MINMAX(x, RB_INF)
+
+#define RB_FOREACH(x, name, head)					\
+	for ((x) = RB_MIN(name, head);					\
+	     (x) != NULL;						\
+	     (x) = name##_RB_NEXT(x))
+
+#endif	/* _SYS_TREE_H_ */

+ 83 - 0
treetype.h

@@ -0,0 +1,83 @@
+/*
+ * Copyright 2002 Damien Miller <djm@mindrot.org> 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.
+ */
+
+/* $Id: treetype.h,v 1.1 2003/11/09 00:38:08 djm Exp $ */
+
+/* Select our tree types for various data structures */
+
+#if defined(FLOW_RB)
+#define FLOW_HEAD	RB_HEAD
+#define FLOW_ENTRY	RB_ENTRY
+#define FLOW_PROTOTYPE  RB_PROTOTYPE
+#define FLOW_GENERATE	RB_GENERATE
+#define FLOW_INSERT	RB_INSERT
+#define FLOW_FIND	RB_FIND
+#define FLOW_REMOVE	RB_REMOVE
+#define FLOW_FOREACH	RB_FOREACH
+#define FLOW_MIN	RB_MIN
+#define FLOW_NEXT	RB_NEXT
+#define FLOW_INIT	RB_INIT
+#elif defined(FLOW_SPLAY)
+#define FLOW_HEAD	SPLAY_HEAD
+#define FLOW_ENTRY	SPLAY_ENTRY
+#define FLOW_PROTOTYPE  SPLAY_PROTOTYPE
+#define FLOW_GENERATE	SPLAY_GENERATE
+#define FLOW_INSERT	SPLAY_INSERT
+#define FLOW_FIND	SPLAY_FIND
+#define FLOW_REMOVE	SPLAY_REMOVE
+#define FLOW_FOREACH	SPLAY_FOREACH
+#define FLOW_MIN	SPLAY_MIN
+#define FLOW_NEXT	SPLAY_NEXT
+#define FLOW_INIT	SPLAY_INIT
+#else
+#error No flow tree type defined
+#endif
+
+#if defined(EXPIRY_RB)
+#define EXPIRY_HEAD	RB_HEAD
+#define EXPIRY_ENTRY	RB_ENTRY
+#define EXPIRY_PROTOTYPE  RB_PROTOTYPE
+#define EXPIRY_GENERATE	RB_GENERATE
+#define EXPIRY_INSERT	RB_INSERT
+#define EXPIRY_FIND	RB_FIND
+#define EXPIRY_REMOVE	RB_REMOVE
+#define EXPIRY_FOREACH	RB_FOREACH
+#define EXPIRY_MIN	RB_MIN
+#define EXPIRY_NEXT	RB_NEXT
+#define EXPIRY_INIT	RB_INIT
+#elif defined(EXPIRY_SPLAY)
+#define EXPIRY_HEAD	SPLAY_HEAD
+#define EXPIRY_ENTRY	SPLAY_ENTRY
+#define EXPIRY_PROTOTYPE  SPLAY_PROTOTYPE
+#define EXPIRY_GENERATE	SPLAY_GENERATE
+#define EXPIRY_INSERT	SPLAY_INSERT
+#define EXPIRY_FIND	SPLAY_FIND
+#define EXPIRY_REMOVE	SPLAY_REMOVE
+#define EXPIRY_FOREACH	SPLAY_FOREACH
+#define EXPIRY_MIN	SPLAY_MIN
+#define EXPIRY_NEXT	SPLAY_NEXT
+#define EXPIRY_INIT	SPLAY_INIT
+#else
+#error No expiry tree type defined
+#endif