Browse Source

Merge upstream version 1.0.0

Christoph Biedl 4 years ago
parent
commit
e2a06f6df2
37 changed files with 8690 additions and 8572 deletions
  1. 0 2
      .cvsignore
  2. 0 4
      .hg_archival.txt
  3. 0 19
      .hgtags
  4. 19 0
      .travis.yml
  5. 2 0
      AUTHORS
  6. 1 0
      COPYING
  7. 1751 0
      ChangeLog
  8. 368 0
      INSTALL
  9. 17 0
      Makefile.am
  10. 0 58
      Makefile.in
  11. 30 0
      NEWS
  12. 20 4
      README
  13. 1152 1
      aclocal.m4
  14. 51 18
      common.h
  15. 0 169
      config.h.in
  16. 0 5673
      configure
  17. 46 5
      configure.ac
  18. 2 1
      freelist.c
  19. 462 205
      install-sh
  20. 981 0
      ipfix.c
  21. 141 0
      ipfix.h
  22. 144 20
      mkinstalldirs
  23. 122 111
      netflow1.c
  24. 179 120
      netflow5.c
  25. 362 333
      netflow9.c
  26. 54 0
      netflow9.h
  27. 232 0
      ntopng.c
  28. 171 0
      psamp.c
  29. 46 0
      psamp.h
  30. 4 1
      softflowctl.8
  31. 3 1
      softflowctl.c
  32. BIN
      softflowctl.pdf
  33. 52 6
      softflowd.8
  34. 2090 1708
      softflowd.c
  35. 187 112
      softflowd.h
  36. BIN
      softflowd.pdf
  37. 1 1
      softflowd.spec

+ 0 - 2
.cvsignore

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

+ 0 - 4
.hg_archival.txt

@@ -1,4 +0,0 @@
-repo: a471267f42340e3926cc0d68c98ec64789cffcca
-node: c496d4d4934828f6b62ff38e1702891f75710b0a
-branch: default
-tag: softflowd-0.9.9

+ 0 - 19
.hgtags

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

+ 19 - 0
.travis.yml

@@ -0,0 +1,19 @@
+language: c
+
+os:
+  - linux
+  - osx
+
+compiler: gcc
+sudo: false
+addons:
+  apt:
+    packages:
+      libpcap-dev
+
+before_install:
+  - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update         ; fi
+  - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew install libpcap; fi
+  
+before_script: autoreconf -if
+script: ./configure && make

+ 2 - 0
AUTHORS

@@ -0,0 +1,2 @@
+Original creator: Damien Miller <djm@mindrot.org>
+Current maintainer: Hitoshi Irino <irino@sfc.wide.ad.jp>

+ 1 - 0
COPYING

@@ -0,0 +1 @@
+LICENSE

File diff suppressed because it is too large
+ 1751 - 0
ChangeLog


+ 368 - 0
INSTALL

@@ -0,0 +1,368 @@
+Installation Instructions
+*************************
+
+   Copyright (C) 1994-1996, 1999-2002, 2004-2016 Free Software
+Foundation, Inc.
+
+   Copying and distribution of this file, with or without modification,
+are permitted in any medium without royalty provided the copyright
+notice and this notice are preserved.  This file is offered as-is,
+without warranty of any kind.
+
+Basic Installation
+==================
+
+   Briefly, the shell command './configure && make && make install'
+should configure, build, and install this package.  The following
+more-detailed instructions are generic; see the 'README' file for
+instructions specific to this package.  Some packages provide this
+'INSTALL' file but do not implement all of the features documented
+below.  The lack of an optional feature in a given package is not
+necessarily a bug.  More recommendations for GNU packages can be found
+in *note Makefile Conventions: (standards)Makefile Conventions.
+
+   The 'configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a 'Makefile' in each directory of the package.
+It may also create one or more '.h' files containing system-dependent
+definitions.  Finally, it creates a shell script 'config.status' that
+you can run in the future to recreate the current configuration, and a
+file 'config.log' containing compiler output (useful mainly for
+debugging 'configure').
+
+   It can also use an optional file (typically called 'config.cache' and
+enabled with '--cache-file=config.cache' or simply '-C') that saves the
+results of its tests to speed up reconfiguring.  Caching is disabled by
+default to prevent problems with accidental use of stale cache files.
+
+   If you need to do unusual things to compile the package, please try
+to figure out how 'configure' could check whether to do them, and mail
+diffs or instructions to the address given in the 'README' so they can
+be considered for the next release.  If you are using the cache, and at
+some point 'config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+   The file 'configure.ac' (or 'configure.in') is used to create
+'configure' by a program called 'autoconf'.  You need 'configure.ac' if
+you want to change it or regenerate 'configure' using a newer version of
+'autoconf'.
+
+   The simplest way to compile this package is:
+
+  1. 'cd' to the directory containing the package's source code and type
+     './configure' to configure the package for your system.
+
+     Running 'configure' might take a while.  While running, it prints
+     some messages telling which features it is checking for.
+
+  2. Type 'make' to compile the package.
+
+  3. Optionally, type 'make check' to run any self-tests that come with
+     the package, generally using the just-built uninstalled binaries.
+
+  4. Type 'make install' to install the programs and any data files and
+     documentation.  When installing into a prefix owned by root, it is
+     recommended that the package be configured and built as a regular
+     user, and only the 'make install' phase executed with root
+     privileges.
+
+  5. Optionally, type 'make installcheck' to repeat any self-tests, but
+     this time using the binaries in their final installed location.
+     This target does not install anything.  Running this target as a
+     regular user, particularly if the prior 'make install' required
+     root privileges, verifies that the installation completed
+     correctly.
+
+  6. You can remove the program binaries and object files from the
+     source code directory by typing 'make clean'.  To also remove the
+     files that 'configure' created (so you can compile the package for
+     a different kind of computer), type 'make distclean'.  There is
+     also a 'make maintainer-clean' target, but that is intended mainly
+     for the package's developers.  If you use it, you may have to get
+     all sorts of other programs in order to regenerate files that came
+     with the distribution.
+
+  7. Often, you can also type 'make uninstall' to remove the installed
+     files again.  In practice, not all packages have tested that
+     uninstallation works correctly, even though it is required by the
+     GNU Coding Standards.
+
+  8. Some packages, particularly those that use Automake, provide 'make
+     distcheck', which can by used by developers to test that all other
+     targets like 'make install' and 'make uninstall' work correctly.
+     This target is generally not run by end users.
+
+Compilers and Options
+=====================
+
+   Some systems require unusual options for compilation or linking that
+the 'configure' script does not know about.  Run './configure --help'
+for details on some of the pertinent environment variables.
+
+   You can give 'configure' initial values for configuration parameters
+by setting variables in the command line or in the environment.  Here is
+an example:
+
+     ./configure CC=c99 CFLAGS=-g LIBS=-lposix
+
+   *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+   You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you can use GNU 'make'.  'cd' to the
+directory where you want the object files and executables to go and run
+the 'configure' script.  'configure' automatically checks for the source
+code in the directory that 'configure' is in and in '..'.  This is known
+as a "VPATH" build.
+
+   With a non-GNU 'make', it is safer to compile the package for one
+architecture at a time in the source code directory.  After you have
+installed the package for one architecture, use 'make distclean' before
+reconfiguring for another architecture.
+
+   On MacOS X 10.5 and later systems, you can create libraries and
+executables that work on multiple system types--known as "fat" or
+"universal" binaries--by specifying multiple '-arch' options to the
+compiler but only a single '-arch' option to the preprocessor.  Like
+this:
+
+     ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+                 CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+                 CPP="gcc -E" CXXCPP="g++ -E"
+
+   This is not guaranteed to produce working output in all cases, you
+may have to build one architecture at a time and combine the results
+using the 'lipo' tool if you have problems.
+
+Installation Names
+==================
+
+   By default, 'make install' installs the package's commands under
+'/usr/local/bin', include files under '/usr/local/include', etc.  You
+can specify an installation prefix other than '/usr/local' by giving
+'configure' the option '--prefix=PREFIX', where PREFIX must be an
+absolute file name.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+pass the option '--exec-prefix=PREFIX' to 'configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+   In addition, if you use an unusual directory layout you can give
+options like '--bindir=DIR' to specify different values for particular
+kinds of files.  Run 'configure --help' for a list of the directories
+you can set and what kinds of files go in them.  In general, the default
+for these options is expressed in terms of '${prefix}', so that
+specifying just '--prefix' will affect all of the other directory
+specifications that were not explicitly provided.
+
+   The most portable way to affect installation locations is to pass the
+correct locations to 'configure'; however, many packages provide one or
+both of the following shortcuts of passing variable assignments to the
+'make install' command line to change installation locations without
+having to reconfigure or recompile.
+
+   The first method involves providing an override variable for each
+affected directory.  For example, 'make install
+prefix=/alternate/directory' will choose an alternate location for all
+directory configuration variables that were expressed in terms of
+'${prefix}'.  Any directories that were specified during 'configure',
+but not in terms of '${prefix}', must each be overridden at install time
+for the entire installation to be relocated.  The approach of makefile
+variable overrides for each directory variable is required by the GNU
+Coding Standards, and ideally causes no recompilation.  However, some
+platforms have known limitations with the semantics of shared libraries
+that end up requiring recompilation when using this method, particularly
+noticeable in packages that use GNU Libtool.
+
+   The second method involves providing the 'DESTDIR' variable.  For
+example, 'make install DESTDIR=/alternate/directory' will prepend
+'/alternate/directory' before all installation names.  The approach of
+'DESTDIR' overrides is not required by the GNU Coding Standards, and
+does not work on platforms that have drive letters.  On the other hand,
+it does better at avoiding recompilation issues, and works well even
+when some directory options were not specified in terms of '${prefix}'
+at 'configure' time.
+
+Optional Features
+=================
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving 'configure' the
+option '--program-prefix=PREFIX' or '--program-suffix=SUFFIX'.
+
+   Some packages pay attention to '--enable-FEATURE' options to
+'configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to '--with-PACKAGE' options, where PACKAGE
+is something like 'gnu-as' or 'x' (for the X Window System).  The
+'README' should mention any '--enable-' and '--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, 'configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the 'configure' options '--x-includes=DIR' and
+'--x-libraries=DIR' to specify their locations.
+
+   Some packages offer the ability to configure how verbose the
+execution of 'make' will be.  For these packages, running './configure
+--enable-silent-rules' sets the default to minimal output, which can be
+overridden with 'make V=1'; while running './configure
+--disable-silent-rules' sets the default to verbose, which can be
+overridden with 'make V=0'.
+
+Particular systems
+==================
+
+   On HP-UX, the default C compiler is not ANSI C compatible.  If GNU CC
+is not installed, it is recommended to use the following options in
+order to use an ANSI C compiler:
+
+     ./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
+
+and if that doesn't work, install pre-built binaries of GCC for HP-UX.
+
+   HP-UX 'make' updates targets which have the same time stamps as their
+prerequisites, which makes it generally unusable when shipped generated
+files such as 'configure' are involved.  Use GNU 'make' instead.
+
+   On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
+parse its '<wchar.h>' header file.  The option '-nodtk' can be used as a
+workaround.  If GNU CC is not installed, it is therefore recommended to
+try
+
+     ./configure CC="cc"
+
+and if that doesn't work, try
+
+     ./configure CC="cc -nodtk"
+
+   On Solaris, don't put '/usr/ucb' early in your 'PATH'.  This
+directory contains several dysfunctional programs; working variants of
+these programs are available in '/usr/bin'.  So, if you need '/usr/ucb'
+in your 'PATH', put it _after_ '/usr/bin'.
+
+   On Haiku, software installed for all users goes in '/boot/common',
+not '/usr/local'.  It is recommended to use the following options:
+
+     ./configure --prefix=/boot/common
+
+Specifying the System Type
+==========================
+
+   There may be some features 'configure' cannot figure out
+automatically, but needs to determine by the type of machine the package
+will run on.  Usually, assuming the package is built to be run on the
+_same_ architectures, 'configure' can figure that out, but if it prints
+a message saying it cannot guess the machine type, give it the
+'--build=TYPE' option.  TYPE can either be a short name for the system
+type, such as 'sun4', or a canonical name which has the form:
+
+     CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+     OS
+     KERNEL-OS
+
+   See the file 'config.sub' for the possible values of each field.  If
+'config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+   If you are _building_ compiler tools for cross-compiling, you should
+use the option '--target=TYPE' to select the type of system they will
+produce code for.
+
+   If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with '--host=TYPE'.
+
+Sharing Defaults
+================
+
+   If you want to set default values for 'configure' scripts to share,
+you can create a site shell script called 'config.site' that gives
+default values for variables like 'CC', 'cache_file', and 'prefix'.
+'configure' looks for 'PREFIX/share/config.site' if it exists, then
+'PREFIX/etc/config.site' if it exists.  Or, you can set the
+'CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all 'configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+   Variables not defined in a site shell script can be set in the
+environment passed to 'configure'.  However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost.  In order to avoid this problem, you should set
+them in the 'configure' command line, using 'VAR=value'.  For example:
+
+     ./configure CC=/usr/local2/bin/gcc
+
+causes the specified 'gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+Unfortunately, this technique does not work for 'CONFIG_SHELL' due to an
+Autoconf limitation.  Until the limitation is lifted, you can use this
+workaround:
+
+     CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+'configure' Invocation
+======================
+
+   'configure' recognizes the following options to control how it
+operates.
+
+'--help'
+'-h'
+     Print a summary of all of the options to 'configure', and exit.
+
+'--help=short'
+'--help=recursive'
+     Print a summary of the options unique to this package's
+     'configure', and exit.  The 'short' variant lists options used only
+     in the top level, while the 'recursive' variant lists options also
+     present in any nested packages.
+
+'--version'
+'-V'
+     Print the version of Autoconf used to generate the 'configure'
+     script, and exit.
+
+'--cache-file=FILE'
+     Enable the cache: use and save the results of the tests in FILE,
+     traditionally 'config.cache'.  FILE defaults to '/dev/null' to
+     disable caching.
+
+'--config-cache'
+'-C'
+     Alias for '--cache-file=config.cache'.
+
+'--quiet'
+'--silent'
+'-q'
+     Do not print messages saying which checks are being made.  To
+     suppress all normal output, redirect it to '/dev/null' (any error
+     messages will still be shown).
+
+'--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     'configure' can determine that directory automatically.
+
+'--prefix=DIR'
+     Use DIR as the installation prefix.  *note Installation Names:: for
+     more details, including other options available for fine-tuning the
+     installation locations.
+
+'--no-create'
+'-n'
+     Run the configure checks, but stop before creating any output
+     files.
+
+'configure' also accepts some other, not widely useful, options.  Run
+'configure --help' for more details.

+ 17 - 0
Makefile.am

@@ -0,0 +1,17 @@
+bin_PROGRAMS = softflowd softflowctl
+COMMON = common.h convtime.h treetype.h sys-tree.h\
+  convtime.c strlcpy.c strlcat.c closefrom.c daemon.c
+if ENABLE_LEGACY
+  LEGACY_SOURCES = netflow9.c netflow1.c
+endif
+if ENABLE_NTOPNG
+  NTOPNG_SOURCES = ntopng.c
+endif
+softflowd_SOURCES = freelist.h log.h softflowd.h netflow9.h ipfix.h psamp.h\
+	freelist.c softflowd.c log.c netflow5.c ipfix.c psamp.c\
+  $(COMMON)
+EXTRA_softflowd_SOURCES = $(LEGACY_SOURCES) $(NTOPNG_SOURCES)
+softflowd_LDADD = $(LEGACY) $(NTOPNG)
+softflowd_DEPENDENCIES = $(LEGACY) $(NTOPNG)
+softflowctl_SOURCES = softflowctl.c $(COMMON)
+dist_man_MANS = softflowd.8 softflowctl.8

+ 0 - 58
Makefile.in

@@ -1,58 +0,0 @@
-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
-SOFTFLOWD=softflowd.o log.o netflow1.o netflow5.o netflow9.o freelist.o
-
-all: $(TARGETS)
-
-softflowd${EXEEXT}: ${SOFTFLOWD} $(COMMON)
-	$(CC) $(LDFLAGS) -o $@ ${SOFTFLOWD} $(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

+ 30 - 0
NEWS

@@ -0,0 +1,30 @@
+Fri Aug 16 2019
+Softflowd 1.0.0
+This release contains some new features.
+
+Details
+This release contains the following new features:
+Add support sending packet capture data using PSAMP format,
+Add function for sending to multiple destination with/without load-balance.
+Add function for injecting to ntopng directly.
+
+
+Mon Feb 13 2012
+Softflowd 0.9.9
+This release contains a number of bug fixes and some new features. It also welcomes Hitoshi Irino as a developer.
+
+Details
+This release contains the following new features:
+
+Add support for flow sampling, controlled by a new -s option (see softflowd(8) manpage for details)
+Add compile-time --chrootdir option to specify alternate chroot directory.
+Allow specification of input and output interface indices using a new -i option.
+Introduce a freelist allocator for expiry and flow objects, faster than malloc
+And the following bug fixes:
+
+Correctly exit from mainloop when a signal is received
+Fix logging from reduced-privileged child
+Many manpage typo, consistency and formatting fixes
+The softflow statistics command now displays the time at which softflowd was started
+Avoid spurious exit from mainloop while listening on "any" interface
+Correct broken IPv6 Netflow v.9 flows

+ 20 - 4
README

@@ -9,13 +9,18 @@ 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
+supplied PDF manpages. They were built with:
+
+man -t ./softflowd.8 | ps2pdf - softflowd.pdf
+man -t ./softflowctl.8 | ps2pdf - softflowctl.pdf
+
+You can view those pages 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). 
+softflowd's companion project "flowd" (http://www.mindrot.org/projects/flowd/). 
 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
@@ -28,6 +33,8 @@ Installing
 
 Building softflowd should be as simple as typing:
 
+autoconf
+autoreconf
 ./configure
 make
 make install
@@ -51,6 +58,10 @@ Todd C. Miller. Please refer to the LICENSE file for full details.
 Reporting Bugs
 --------------
 
+Please report bugs in softflowd (https://github.com/irino/softflowd/)
+to https://github.com/irino/softflowd/issues
+
+Following descriptions are historical information:
 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:
@@ -62,7 +73,12 @@ Softflowd has an extensive TODO list of interesting features, large and
 small, that are waiting to be implemented. If you are interested in
 helping, please contact me.
 
-The latest source code may be obtained from Google Code:
-http://code.google.com/p/softflowd/
+The latest source code may be obtained from Github:
+https://github.com/irino/softflowd/
+(This repository was forked from http://code.google.com/p/softflowd/)
 
+Original creator:
 Damien Miller <djm@mindrot.org>
+
+Current maintainer:
+Hitoshi Irino <irino@sfc.wide.ad.jp>

File diff suppressed because it is too large
+ 1152 - 1
aclocal.m4


+ 51 - 18
common.h

@@ -27,7 +27,8 @@
 
 #include "config.h"
 
-#define _BSD_SOURCE /* Needed for BSD-style struct ip,tcp,udp on Linux */
+#define _BSD_SOURCE             /* Needed for BSD-style struct ip,tcp,udp on Linux */
+#define _DEFAULT_SOURCE         /* It is recommended to use instead of _BSD_SOURCE on Linux */
 
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -45,6 +46,7 @@
 #include <netinet/tcp.h>
 #include <netinet/udp.h>
 #include <arpa/inet.h>
+#include <net/ethernet.h>
 
 #include <unistd.h>
 #include <stdlib.h>
@@ -57,8 +59,10 @@
 #include <signal.h>
 #include <stdio.h>
 #include <string.h>
+#include <strings.h>
 #include <syslog.h>
 #include <time.h>
+#include <netdb.h>
 
 #if defined(HAVE_NET_BPF_H)
 #include <net/bpf.h>
@@ -69,11 +73,17 @@
 #include <inttypes.h>
 #endif
 
+#if defined(HAVE_SYS_ENDIAN_H)
+#include <sys/endian.h>
+#elif defined(HAVE_ENDIAN_H)
+#include <endian.h>
+#endif
+
 /* The name of the program */
 #define PROGNAME		"softflowd"
 
 /* The name of the program */
-#define PROGVER			"0.9.9"
+#define PROGVER			"1.0.0"
 
 /* Default pidfile */
 #define DEFAULT_PIDFILE		"/var/run/" PROGNAME ".pid"
@@ -86,7 +96,7 @@
 	    { (const char *)flowd_rcsid, "\100(#)" msg }	\
 
 #ifndef IP_OFFMASK
-# define IP_OFFMASK		0x1fff	/* mask for fragmenting bits */
+#define IP_OFFMASK		0x1fff  /* mask for fragmenting bits */
 #endif
 #ifndef IPV6_VERSION
 #define IPV6_VERSION		0x60
@@ -102,26 +112,26 @@
 #endif
 
 #ifndef _PATH_DEVNULL
-# define _PATH_DEVNULL		"/dev/null"
+#define _PATH_DEVNULL		"/dev/null"
 #endif
 
 #ifndef MIN
-# define MIN(a,b) (((a)<(b))?(a):(b))
+#define MIN(a,b) (((a)<(b))?(a):(b))
 #endif
 #ifndef MAX
-# define MAX(a,b) (((a)>(b))?(a):(b))
+#define MAX(a,b) (((a)>(b))?(a):(b))
 #endif
 #ifndef offsetof
-# define offsetof(type, member) ((size_t) &((type *)0)->member)
+#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
+#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)
@@ -150,20 +160,43 @@ typedef OUR_CFG_U_INT64_T u_int64_t;
 #endif
 
 #ifndef HAVE_STRLCPY
-size_t strlcpy(char *dst, const char *src, size_t siz);
+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);
+size_t strlcat (char *dst, const char *src, size_t siz);
 #endif
 #ifndef HAVE_CLOSEFROM
-void closefrom(int lowfd);
+void closefrom (int lowfd);
 #endif
 
 #ifndef HAVE_STRUCT_IP6_EXT
 struct ip6_ext {
-	u_int8_t ip6e_nxt;
-	u_int8_t ip6e_len;
+  u_int8_t ip6e_nxt;
+  u_int8_t ip6e_len;
 } __packed;
 #endif
 
+
+/* following lines are copy from unistd.h in Linux for avoidance warnings in compilation */
+#if defined(HAVE_SETRESGID) && !defined(_GNU_SOURCE)
+extern int setresgid (__uid_t __ruid, __uid_t __euid, __uid_t __suid);
+#endif
+#if defined(HAVE_SETRESUID) && !defined(_GNU_SOURCE)
+extern int setresuid (__uid_t __ruid, __uid_t __euid, __uid_t __suid);
+#endif
+
+#if defined (HAVE_DECL_HTONLL) && !defined (HAVE_DECL_HTOBE64)
+#define htobe64     htonll
+#endif
+
+#ifndef ETH_ALEN
+// https://cdn.kernel.org/pub/linux/kernel/people/marcelo/linux-2.4/include/linux/if_ether.h
+#define ETH_ALEN	6		/* Octets in one ethernet addr	 */
+#endif /* ETH_ALEN */
+
+#ifdef __APPLE__
+#include <libkern/OSByteOrder.h>
+#define htobe64(x) OSSwapHostToBigInt64(x)
+#endif /* __APPLE__ */
+
 #endif /* _SFD_COMMON_H */

+ 0 - 169
config.h.in

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

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


+ 46 - 5
configure.ac

@@ -12,8 +12,9 @@
 # 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_INIT([softflowd], [1.0.0])
 AC_CONFIG_SRCDIR([softflowd.c])
+AM_INIT_AUTOMAKE
 
 AC_CONFIG_HEADER(config.h)
 AC_PROG_CC
@@ -31,6 +32,27 @@ 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_ENABLE(legacy,
+              AC_HELP_STRING([--enable-legacy],
+                             [enable legacy NetFlow implementation (default NO)]),
+              [legacy=yes],[legacy=no])
+AC_ARG_ENABLE(pthread,
+              AC_HELP_STRING([--enable-pthread], [enable pthread (default NO) (experimental, unstable)]),
+              [pthread=yes],[pthread=no])
+AC_ARG_ENABLE(ntopng,
+              AC_HELP_STRING([--enable-ntopng], [enable flow sending to ntopng with zeromq (default NO)]),
+              [ntopng=yes],[ntopng=no])
+AC_ARG_ENABLE(flow-spray,
+              AC_HELP_STRING([--enable-flow-spray],
+                             [enable spray as flow tree type(default is RB)]),
+              AC_DEFINE([FLOW_SPRAY], 1, [enable spray as flow tree type]),
+	      AC_DEFINE([FLOW_RB], 1, [enable RB(red-black) as flow tree type]))
+AC_ARG_ENABLE(expiry-spray,
+              AC_HELP_STRING([--enable-expiry-spray],
+                             [enable spray as expiry tree type (default is RB)]),
+              AC_DEFINE([EXPIRY_SPRAY], 1, [enable spray as flow tree type]),
+	      AC_DEFINE([EXPIRY_RB], 1, [enable RB(red-black) as flow tree type]))
+
 AC_ARG_WITH(cflags,
 	[  --with-cflags           Specify additional compiler flags],
 	[ if test "x$withval" != "xno" ; then CFLAGS="$CFLAGS $withval"; fi ]	
@@ -52,7 +74,8 @@ AC_ARG_WITH(chrootdir,
 	[ AC_DEFINE_UNQUOTED([PRIVDROP_CHROOT_DIR], ["${withval}"], [privdrop chroot directory]) ]	
 )
 
-AC_CHECK_HEADERS(net/bpf.h pcap.h pcap-bpf.h)
+AC_DEFINE([_BSD_SOURCE], [], [Define BSD SOURCE for Linux])
+AC_CHECK_HEADERS(net/bpf.h pcap.h pcap-bpf.h sys/endian.h endian.h)
 
 dnl AC_CHECK_HEADERS(netinet/in_systm.h netinet/tcp.h netinet/udp.h)
 dnl 
@@ -87,7 +110,8 @@ 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_FUNCS(closefrom daemon setresuid setreuid setresgid setgid strlcpy strlcat strsep)
+AC_CHECK_DECLS([htobe64, htonll])
 
 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])
@@ -97,6 +121,25 @@ AC_CHECK_SIZEOF(int, 4)
 AC_CHECK_SIZEOF(long int, 4)
 AC_CHECK_SIZEOF(long long int, 8)
 
+if test "x$legacy" = "xyes" ; then
+  AC_DEFINE([ENABLE_LEGACY], 1, [enable legacy NetFlow implementation])
+  LEGACY='netflow9.$(OBJEXT) netflow1.$(OBJEXT)'
+  AC_SUBST([LEGACY])
+fi
+AM_CONDITIONAL([ENABLE_LEGACY], [test x$legacy = xyes])
+if test "x$pthread" = "xyes" ; then
+  AC_DEFINE([ENABLE_PTHREAD], 1, [enable pthread])
+  AC_CHECK_LIB(pthread, pthread_create, [],[AC_MSG_ERROR([pthread.h not found])])
+  AC_CHECK_HEADERS(pthread.h, [],[AC_MSG_ERROR([pthread.h not found])])
+fi
+if test "x$ntopng" = "xyes" ; then
+  AC_DEFINE([ENABLE_NTOPNG], 1, [enable ntopng])
+   AC_CHECK_LIB(zmq, zmq_connect, [],[AC_MSG_ERROR([libzmq not found])])
+   AC_CHECK_HEADERS(zmq.h, [],[AC_MSG_ERROR([zmq.h not found])])
+  NTOPNG='ntopng.$(OBJEXT)'
+  AC_SUBST([NTOPNG])
+fi
+AM_CONDITIONAL([ENABLE_NTOPNG], [test x$ntopng = xyes])
 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
@@ -164,8 +207,6 @@ 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

+ 2 - 1
freelist.c

@@ -44,8 +44,9 @@
 void
 freelist_init(struct freelist *fl, size_t allocsz)
 {
+	size_t sizeof_fl = sizeof(fl);
 	FLOGIT((LOG_DEBUG, "%s: %s(%p, %zu)", __func__, __func__, fl, allocsz));
-	bzero(fl, sizeof(fl));
+	bzero(fl, sizeof_fl);
 	fl->allocsz = roundup(allocsz, FREELIST_ALLOC_ALIGN);
 	fl->free_entries = NULL;
 }

+ 462 - 205
install-sh

@@ -1,251 +1,508 @@
 #!/bin/sh
-#
 # install - install a program, script, or datafile
-# This comes from X11R5 (mit/util/scripts/install.sh).
+
+scriptversion=2014-09-12.12; # UTC
+
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #
-# Copyright 1991 by the Massachusetts Institute of Technology
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
 #
-# 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.
+#
+# FSF changes to this file are in the public domain.
 #
 # Calling this script install-sh is preferred over install.sh, to prevent
-# `make' implicit rules from creating a file called install from it
+# '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.
+# from scratch.
+
+tab='	'
+nl='
+'
+IFS=" $tab$nl"
 
+# Set DOITPROG to "echo" to test this script.
 
-# set DOITPROG to echo to test this script
+doit=${DOITPROG-}
+doit_exec=${doit:-exec}
 
-# Don't use :- since 4.3BSD and earlier shells don't like it.
-doit="${DOITPROG-}"
+# Put in absolute file names if you don't have them in your path;
+# or use environment vars.
 
+chgrpprog=${CHGRPPROG-chgrp}
+chmodprog=${CHMODPROG-chmod}
+chownprog=${CHOWNPROG-chown}
+cmpprog=${CMPPROG-cmp}
+cpprog=${CPPROG-cp}
+mkdirprog=${MKDIRPROG-mkdir}
+mvprog=${MVPROG-mv}
+rmprog=${RMPROG-rm}
+stripprog=${STRIPPROG-strip}
 
-# put in absolute paths if you don't have them in your path; or use env. vars.
+posix_mkdir=
 
-mvprog="${MVPROG-mv}"
-cpprog="${CPPROG-cp}"
-chmodprog="${CHMODPROG-chmod}"
-chownprog="${CHOWNPROG-chown}"
-chgrpprog="${CHGRPPROG-chgrp}"
-stripprog="${STRIPPROG-strip}"
-rmprog="${RMPROG-rm}"
-mkdirprog="${MKDIRPROG-mkdir}"
+# Desired mode of installed file.
+mode=0755
 
-transformbasename=""
-transform_arg=""
-instcmd="$mvprog"
-chmodcmd="$chmodprog 0755"
-chowncmd=""
-chgrpcmd=""
-stripcmd=""
+chgrpcmd=
+chmodcmd=$chmodprog
+chowncmd=
+mvcmd=$mvprog
 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
+stripcmd=
 
-if [ x"$src" = x ]
-then
-	echo "install:	no input file specified"
-	exit 1
-else
-	true
-fi
+src=
+dst=
+dir_arg=
+dst_arg=
 
-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
+copy_on_change=false
+is_target_a_directory=possibly
 
-## this sed command emulates the dirname command
-dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+usage="\
+Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
+   or: $0 [OPTION]... SRCFILES... DIRECTORY
+   or: $0 [OPTION]... -t DIRECTORY SRCFILES...
+   or: $0 [OPTION]... -d DIRECTORIES...
 
-# Make sure that the destination directory exists.
-#  this part is taken from Noah Friedman's mkinstalldirs script
+In the 1st form, copy SRCFILE to DSTFILE.
+In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
+In the 4th, create DIRECTORIES.
 
-# Skip lots of stat calls in the usual case.
-if [ ! -d "$dstdir" ]; then
-defaultIFS='	
-'
-IFS="${IFS-${defaultIFS}}"
+Options:
+     --help     display this help and exit.
+     --version  display version info and exit.
 
-oIFS="${IFS}"
-# Some sh's can't handle IFS=/ for some reason.
-IFS='%'
-set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
-IFS="${oIFS}"
+  -c            (ignored)
+  -C            install only if different (preserve the last data modification time)
+  -d            create directories instead of installing files.
+  -g GROUP      $chgrpprog installed files to GROUP.
+  -m MODE       $chmodprog installed files to MODE.
+  -o USER       $chownprog installed files to USER.
+  -s            $stripprog installed files.
+  -t DIRECTORY  install into DIRECTORY.
+  -T            report an error if DSTFILE is a directory.
 
-pathcomp=''
+Environment variables override the default commands:
+  CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
+  RMPROG STRIPPROG
+"
 
-while [ $# -ne 0 ] ; do
-	pathcomp="${pathcomp}${1}"
-	shift
+while test $# -ne 0; do
+  case $1 in
+    -c) ;;
 
-	if [ ! -d "${pathcomp}" ] ;
-	then
-		$mkdirprog "${pathcomp}"
-	else
-		true
-	fi
+    -C) copy_on_change=true;;
 
-	pathcomp="${pathcomp}/"
-done
-fi
+    -d) dir_arg=true;;
 
-if [ x"$dir_arg" != x ]
-then
-	$doit $instcmd $dst &&
+    -g) chgrpcmd="$chgrpprog $2"
+        shift;;
 
-	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
+    --help) echo "$usage"; exit $?;;
 
-# If we're going to rename the final executable, determine the name now.
+    -m) mode=$2
+        case $mode in
+          *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
+            echo "$0: invalid mode: $mode" >&2
+            exit 1;;
+        esac
+        shift;;
 
-	if [ x"$transformarg" = x ]
-	then
-		dstfile=`basename $dst`
-	else
-		dstfile=`basename $dst $transformbasename |
-			sed $transformarg`$transformbasename
-	fi
+    -o) chowncmd="$chownprog $2"
+        shift;;
 
-# don't allow the sed command to completely eliminate the filename
+    -s) stripcmd=$stripprog;;
 
-	if [ x"$dstfile" = x ]
-	then
-		dstfile=`basename $dst`
-	else
-		true
-	fi
+    -t)
+        is_target_a_directory=always
+        dst_arg=$2
+        # Protect names problematic for 'test' and other utilities.
+        case $dst_arg in
+          -* | [=\(\)!]) dst_arg=./$dst_arg;;
+        esac
+        shift;;
 
-# Make a temp file name in the proper directory.
+    -T) is_target_a_directory=never;;
 
-	dsttmp=$dstdir/#inst.$$#
+    --version) echo "$0 $scriptversion"; exit $?;;
 
-# Move or copy the file name to the temp name
+    --) shift
+        break;;
 
-	$doit $instcmd $src $dsttmp &&
+    -*) echo "$0: invalid option: $1" >&2
+        exit 1;;
 
-	trap "rm -f ${dsttmp}" 0 &&
+    *)  break;;
+  esac
+  shift
+done
 
-# and set any options; do chmod last to preserve setuid bits
+# We allow the use of options -d and -T together, by making -d
+# take the precedence; this is for compatibility with GNU install.
 
-# 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 test -n "$dir_arg"; then
+  if test -n "$dst_arg"; then
+    echo "$0: target directory not allowed when installing a directory." >&2
+    exit 1
+  fi
+fi
 
-	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 &&
+if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
+  # When -d is used, all remaining arguments are directories to create.
+  # When -t is used, the destination is already specified.
+  # Otherwise, the last argument is the destination.  Remove it from $@.
+  for arg
+  do
+    if test -n "$dst_arg"; then
+      # $@ is not empty: it contains at least $arg.
+      set fnord "$@" "$dst_arg"
+      shift # fnord
+    fi
+    shift # arg
+    dst_arg=$arg
+    # Protect names problematic for 'test' and other utilities.
+    case $dst_arg in
+      -* | [=\(\)!]) dst_arg=./$dst_arg;;
+    esac
+  done
+fi
 
-# Now rename the file to the real destination.
+if test $# -eq 0; then
+  if test -z "$dir_arg"; then
+    echo "$0: no input file specified." >&2
+    exit 1
+  fi
+  # It's OK to call 'install-sh -d' without argument.
+  # This can happen when creating conditional directories.
+  exit 0
+fi
+
+if test -z "$dir_arg"; then
+  if test $# -gt 1 || test "$is_target_a_directory" = always; then
+    if test ! -d "$dst_arg"; then
+      echo "$0: $dst_arg: Is not a directory." >&2
+      exit 1
+    fi
+  fi
+fi
 
-	$doit $rmcmd -f $dstdir/$dstfile &&
-	$doit $mvcmd $dsttmp $dstdir/$dstfile
+if test -z "$dir_arg"; then
+  do_exit='(exit $ret); exit $ret'
+  trap "ret=129; $do_exit" 1
+  trap "ret=130; $do_exit" 2
+  trap "ret=141; $do_exit" 13
+  trap "ret=143; $do_exit" 15
+
+  # Set umask so as not to create temps with too-generous modes.
+  # However, 'strip' requires both read and write access to temps.
+  case $mode in
+    # Optimize common cases.
+    *644) cp_umask=133;;
+    *755) cp_umask=22;;
+
+    *[0-7])
+      if test -z "$stripcmd"; then
+        u_plus_rw=
+      else
+        u_plus_rw='% 200'
+      fi
+      cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
+    *)
+      if test -z "$stripcmd"; then
+        u_plus_rw=
+      else
+        u_plus_rw=,u+rw
+      fi
+      cp_umask=$mode$u_plus_rw;;
+  esac
+fi
 
-fi &&
+for src
+do
+  # Protect names problematic for 'test' and other utilities.
+  case $src in
+    -* | [=\(\)!]) src=./$src;;
+  esac
+
+  if test -n "$dir_arg"; then
+    dst=$src
+    dstdir=$dst
+    test -d "$dstdir"
+    dstdir_status=$?
+  else
+
+    # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
+    # might cause directories to be created, which would be especially bad
+    # if $src (and thus $dsttmp) contains '*'.
+    if test ! -f "$src" && test ! -d "$src"; then
+      echo "$0: $src does not exist." >&2
+      exit 1
+    fi
+
+    if test -z "$dst_arg"; then
+      echo "$0: no destination specified." >&2
+      exit 1
+    fi
+    dst=$dst_arg
+
+    # If destination is a directory, append the input filename; won't work
+    # if double slashes aren't ignored.
+    if test -d "$dst"; then
+      if test "$is_target_a_directory" = never; then
+        echo "$0: $dst_arg: Is a directory" >&2
+        exit 1
+      fi
+      dstdir=$dst
+      dst=$dstdir/`basename "$src"`
+      dstdir_status=0
+    else
+      dstdir=`dirname "$dst"`
+      test -d "$dstdir"
+      dstdir_status=$?
+    fi
+  fi
+
+  obsolete_mkdir_used=false
+
+  if test $dstdir_status != 0; then
+    case $posix_mkdir in
+      '')
+        # Create intermediate dirs using mode 755 as modified by the umask.
+        # This is like FreeBSD 'install' as of 1997-10-28.
+        umask=`umask`
+        case $stripcmd.$umask in
+          # Optimize common cases.
+          *[2367][2367]) mkdir_umask=$umask;;
+          .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
+
+          *[0-7])
+            mkdir_umask=`expr $umask + 22 \
+              - $umask % 100 % 40 + $umask % 20 \
+              - $umask % 10 % 4 + $umask % 2
+            `;;
+          *) mkdir_umask=$umask,go-w;;
+        esac
+
+        # With -d, create the new directory with the user-specified mode.
+        # Otherwise, rely on $mkdir_umask.
+        if test -n "$dir_arg"; then
+          mkdir_mode=-m$mode
+        else
+          mkdir_mode=
+        fi
+
+        posix_mkdir=false
+        case $umask in
+          *[123567][0-7][0-7])
+            # POSIX mkdir -p sets u+wx bits regardless of umask, which
+            # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
+            ;;
+          *)
+            # $RANDOM is not portable (e.g. dash);  use it when possible to
+            # lower collision chance
+            tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+            trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0
+
+            # As "mkdir -p" follows symlinks and we work in /tmp possibly;  so
+            # create the $tmpdir first (and fail if unsuccessful) to make sure
+            # that nobody tries to guess the $tmpdir name.
+            if (umask $mkdir_umask &&
+                $mkdirprog $mkdir_mode "$tmpdir" &&
+                exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
+            then
+              if test -z "$dir_arg" || {
+                   # Check for POSIX incompatibilities with -m.
+                   # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+                   # other-writable bit of parent directory when it shouldn't.
+                   # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+                   test_tmpdir="$tmpdir/a"
+                   ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
+                   case $ls_ld_tmpdir in
+                     d????-?r-*) different_mode=700;;
+                     d????-?--*) different_mode=755;;
+                     *) false;;
+                   esac &&
+                   $mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
+                     ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
+                     test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+                   }
+                 }
+              then posix_mkdir=:
+              fi
+              rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
+            else
+              # Remove any dirs left behind by ancient mkdir implementations.
+              rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
+            fi
+            trap '' 0;;
+        esac;;
+    esac
 
+    if
+      $posix_mkdir && (
+        umask $mkdir_umask &&
+        $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+      )
+    then :
+    else
+
+      # The umask is ridiculous, or mkdir does not conform to POSIX,
+      # or it failed possibly due to a race condition.  Create the
+      # directory the slow way, step by step, checking for races as we go.
+
+      case $dstdir in
+        /*) prefix='/';;
+        [-=\(\)!]*) prefix='./';;
+        *)  prefix='';;
+      esac
+
+      oIFS=$IFS
+      IFS=/
+      set -f
+      set fnord $dstdir
+      shift
+      set +f
+      IFS=$oIFS
+
+      prefixes=
+
+      for d
+      do
+        test X"$d" = X && continue
+
+        prefix=$prefix$d
+        if test -d "$prefix"; then
+          prefixes=
+        else
+          if $posix_mkdir; then
+            (umask=$mkdir_umask &&
+             $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+            # Don't fail if two instances are running concurrently.
+            test -d "$prefix" || exit 1
+          else
+            case $prefix in
+              *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+              *) qprefix=$prefix;;
+            esac
+            prefixes="$prefixes '$qprefix'"
+          fi
+        fi
+        prefix=$prefix/
+      done
+
+      if test -n "$prefixes"; then
+        # Don't fail if two instances are running concurrently.
+        (umask $mkdir_umask &&
+         eval "\$doit_exec \$mkdirprog $prefixes") ||
+          test -d "$dstdir" || exit 1
+        obsolete_mkdir_used=true
+      fi
+    fi
+  fi
+
+  if test -n "$dir_arg"; then
+    { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
+    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
+    { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
+      test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
+  else
+
+    # Make a couple of temp file names in the proper directory.
+    dsttmp=$dstdir/_inst.$$_
+    rmtmp=$dstdir/_rm.$$_
+
+    # Trap to clean up those temp files at exit.
+    trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
+
+    # Copy the file name to the temp name.
+    (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
+
+    # 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 $cpprog $src $dsttmp" command.
+    #
+    { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
+    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
+    { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
+    { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
+
+    # If -C, don't bother to copy if it wouldn't change the file.
+    if $copy_on_change &&
+       old=`LC_ALL=C ls -dlL "$dst"     2>/dev/null` &&
+       new=`LC_ALL=C ls -dlL "$dsttmp"  2>/dev/null` &&
+       set -f &&
+       set X $old && old=:$2:$4:$5:$6 &&
+       set X $new && new=:$2:$4:$5:$6 &&
+       set +f &&
+       test "$old" = "$new" &&
+       $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
+    then
+      rm -f "$dsttmp"
+    else
+      # Rename the file to the real destination.
+      $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
+
+      # The rename failed, perhaps because mv can't rename something else
+      # to itself, or perhaps because mv is so ancient that it does not
+      # support -f.
+      {
+        # Now remove or move aside any old file at destination location.
+        # We try this two ways since rm can't unlink itself on some
+        # systems and the destination file might be busy for other
+        # reasons.  In this case, the final cleanup might fail but the new
+        # file should still install successfully.
+        {
+          test ! -f "$dst" ||
+          $doit $rmcmd -f "$dst" 2>/dev/null ||
+          { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+            { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
+          } ||
+          { echo "$0: cannot unlink or rename $dst" >&2
+            (exit 1); exit 1
+          }
+        } &&
+
+        # Now rename the file to the real destination.
+        $doit $mvcmd "$dsttmp" "$dst"
+      }
+    fi || exit 1
+
+    trap '' 0
+  fi
+done
 
-exit 0
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:

+ 981 - 0
ipfix.c

@@ -0,0 +1,981 @@
+/*
+ * Copyright 2002 Damien Miller <djm@mindrot.org> All rights reserved.
+ * Copyright 2012 Hitoshi Irino <irino@sfc.wide.ad.jp> 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 "log.h"
+#include "treetype.h"
+#include "softflowd.h"
+#include "netflow9.h"
+#include "ipfix.h"
+#include "psamp.h"
+
+const struct IPFIX_FIELD_SPECIFIER field_v4[] = {
+  {IPFIX_sourceIPv4Address, 4},
+  {IPFIX_destinationIPv4Address, 4}
+};
+
+const struct IPFIX_FIELD_SPECIFIER field_v6[] = {
+  {IPFIX_sourceIPv6Address, 16},
+  {IPFIX_destinationIPv6Address, 16}
+};
+
+const struct IPFIX_FIELD_SPECIFIER field_common[] = {
+  {IPFIX_octetDeltaCount, 4},
+  {IPFIX_packetDeltaCount, 4},
+  {IPFIX_ingressInterface, 4},
+  {IPFIX_egressInterface, 4}
+};
+
+const struct IPFIX_FIELD_SPECIFIER field_transport[] = {
+  {IPFIX_sourceTransportPort, 2},
+  {IPFIX_destinationTransportPort, 2},
+  {IPFIX_protocolIdentifier, 1},
+  {IPFIX_tcpControlBits, 1},
+  {IPFIX_ipVersion, 1},
+  {IPFIX_ipClassOfService, 1}
+};
+
+const struct IPFIX_FIELD_SPECIFIER field_icmp4[] = {
+  {IPFIX_icmpTypeCodeIPv4, 2},
+  {IPFIX_ipVersion, 1},
+  {IPFIX_ipClassOfService, 1}
+};
+
+const struct IPFIX_FIELD_SPECIFIER field_icmp6[] = {
+  {IPFIX_icmpTypeCodeIPv6, 2},
+  {IPFIX_ipVersion, 1},
+  {IPFIX_ipClassOfService, 1}
+};
+
+const struct IPFIX_FIELD_SPECIFIER field_vlan[] = {
+  {IPFIX_vlanId, 2},
+  {IPFIX_postVlanId, 2}
+};
+
+const struct IPFIX_FIELD_SPECIFIER field_ether[] = {
+  {IPFIX_sourceMacAddress, 6},
+  {IPFIX_postDestinationMacAddress, 6}
+};
+
+const struct IPFIX_FIELD_SPECIFIER field_timesec[] = {
+  {IPFIX_flowStartSeconds, 4},
+  {IPFIX_flowEndSeconds, 4}
+};
+
+const struct IPFIX_FIELD_SPECIFIER field_timemsec[] = {
+  {IPFIX_flowStartMilliSeconds, 8},
+  {IPFIX_flowEndMilliSeconds, 8}
+};
+
+const struct IPFIX_FIELD_SPECIFIER field_timeusec[] = {
+  {IPFIX_flowStartMicroSeconds, 8},
+  {IPFIX_flowEndMicroSeconds, 8}
+};
+
+const struct IPFIX_FIELD_SPECIFIER field_timensec[] = {
+  {IPFIX_flowStartNanoSeconds, 8},
+  {IPFIX_flowEndNanoSeconds, 8}
+};
+
+const struct IPFIX_FIELD_SPECIFIER field_timesysup[] = {
+  {IPFIX_flowStartSysUpTime, 4},
+  {IPFIX_flowEndSysUpTime, 4}
+};
+
+const struct IPFIX_FIELD_SPECIFIER field_bicommon[] = {
+  {IPFIX_octetDeltaCount, 4},
+  {IPFIX_packetDeltaCount, 4},
+  {IPFIX_ipClassOfService, 1}
+};
+
+const struct IPFIX_FIELD_SPECIFIER field_bitransport[] =
+  { {IPFIX_tcpControlBits, 1} };
+
+const struct IPFIX_FIELD_SPECIFIER field_biicmp4[] =
+  { {IPFIX_icmpTypeCodeIPv4, 2} };
+
+const struct IPFIX_FIELD_SPECIFIER field_biicmp6[] =
+  { {IPFIX_icmpTypeCodeIPv6, 2} };
+
+const struct IPFIX_FIELD_SPECIFIER field_scope[] =
+  { {IPFIX_meteringProcessId, 4} };
+
+const struct IPFIX_FIELD_SPECIFIER field_option[] = {
+  {IPFIX_systemInitTimeMilliseconds, 8},
+  {PSAMP_samplingPacketInterval, 4},
+  {PSAMP_samplingPacketSpace, 4},
+  {PSAMP_selectorAlgorithm, 2}
+};
+
+const struct IPFIX_FIELD_SPECIFIER field_nf9scope[] =
+  { {NFLOW9_OPTION_SCOPE_INTERFACE, 4} };
+
+const struct IPFIX_FIELD_SPECIFIER field_nf9option[] = {
+  {NFLOW9_SAMPLING_INTERVAL, 4},
+  {NFLOW9_SAMPLING_ALGORITHM, 1}
+};
+
+/* Stuff pertaining to the templates that softflowd uses */
+#define IPFIX_SOFTFLOWD_TEMPLATE_IPRECORDS          \
+    sizeof(field_v4) / sizeof(struct IPFIX_FIELD_SPECIFIER)
+#define IPFIX_SOFTFLOWD_TEMPLATE_TIMERECORDS        \
+    sizeof(field_timesysup) / sizeof(struct IPFIX_FIELD_SPECIFIER)
+#define IPFIX_SOFTFLOWD_TEMPLATE_COMMONRECORDS      \
+    sizeof(field_common) / sizeof(struct IPFIX_FIELD_SPECIFIER)
+#define IPFIX_SOFTFLOWD_TEMPLATE_TRANSPORTRECORDS   \
+    sizeof(field_transport) / sizeof(struct IPFIX_FIELD_SPECIFIER)
+#define IPFIX_SOFTFLOWD_TEMPLATE_ICMPRECORDS        \
+    sizeof(field_icmp4) / sizeof(struct IPFIX_FIELD_SPECIFIER)
+#define IPFIX_SOFTFLOWD_TEMPLATE_VLANRECORDS        \
+    sizeof(field_vlan) / sizeof(struct IPFIX_FIELD_SPECIFIER)
+#define IPFIX_SOFTFLOWD_TEMPLATE_ETHERRECORDS       \
+    sizeof(field_ether) / sizeof(struct IPFIX_FIELD_SPECIFIER)
+#define IPFIX_SOFTFLOWD_TEMPLATE_BICOMMONRECORDS    \
+    sizeof(field_bicommon) / sizeof(struct IPFIX_FIELD_SPECIFIER)
+#define IPFIX_SOFTFLOWD_TEMPLATE_BITRANSPORTRECORDS \
+    sizeof(field_bitransport) / sizeof(struct IPFIX_FIELD_SPECIFIER)
+#define IPFIX_SOFTFLOWD_TEMPLATE_BIICMPRECORDS      \
+    sizeof(field_biicmp4) / sizeof(struct IPFIX_FIELD_SPECIFIER)
+
+#define IPFIX_SOFTFLOWD_TEMPLATE_NRECORDS       \
+    IPFIX_SOFTFLOWD_TEMPLATE_IPRECORDS +        \
+    IPFIX_SOFTFLOWD_TEMPLATE_TIMERECORDS +      \
+    IPFIX_SOFTFLOWD_TEMPLATE_COMMONRECORDS +    \
+    IPFIX_SOFTFLOWD_TEMPLATE_TRANSPORTRECORDS + \
+    IPFIX_SOFTFLOWD_TEMPLATE_VLANRECORDS +      \
+    IPFIX_SOFTFLOWD_TEMPLATE_ETHERRECORDS
+
+#define IPFIX_SOFTFLOWD_TEMPLATE_BI_NRECORDS    \
+    IPFIX_SOFTFLOWD_TEMPLATE_BICOMMONRECORDS +  \
+    IPFIX_SOFTFLOWD_TEMPLATE_BITRANSPORTRECORDS
+
+struct IPFIX_SOFTFLOWD_TEMPLATE {
+  struct IPFIX_TEMPLATE_SET_HEADER h;
+  struct IPFIX_FIELD_SPECIFIER r[IPFIX_SOFTFLOWD_TEMPLATE_NRECORDS];
+  struct IPFIX_VENDOR_FIELD_SPECIFIER
+    v[IPFIX_SOFTFLOWD_TEMPLATE_BI_NRECORDS];
+  u_int16_t data_len, bi_count;
+} __packed;
+
+#define IPFIX_SOFTFLOWD_OPTION_TEMPLATE_SCOPE_RECORDS   \
+    sizeof(field_scope) / sizeof(struct IPFIX_FIELD_SPECIFIER)
+#define IPFIX_SOFTFLOWD_OPTION_TEMPLATE_NRECORDS        \
+    sizeof(field_option) / sizeof(struct IPFIX_FIELD_SPECIFIER)
+
+#define NFLOW9_SOFTFLOWD_OPTION_TEMPLATE_SCOPE_RECORDS  \
+    sizeof(field_nf9scope) / sizeof(struct IPFIX_FIELD_SPECIFIER)
+#define NFLOW9_SOFTFLOWD_OPTION_TEMPLATE_NRECORDS       \
+    sizeof(field_nf9option) / sizeof(struct IPFIX_FIELD_SPECIFIER)
+
+struct IPFIX_SOFTFLOWD_OPTION_TEMPLATE {
+  struct IPFIX_OPTION_TEMPLATE_SET_HEADER h;
+  struct IPFIX_FIELD_SPECIFIER
+    s[IPFIX_SOFTFLOWD_OPTION_TEMPLATE_SCOPE_RECORDS];
+  struct IPFIX_FIELD_SPECIFIER r[IPFIX_SOFTFLOWD_OPTION_TEMPLATE_NRECORDS];
+} __packed;
+
+/* softflowd data set */
+struct IPFIX_SOFTFLOWD_DATA_COMMON {
+  u_int32_t octetDeltaCount, packetDeltaCount;
+  u_int32_t ingressInterface, egressInterface;
+} __packed;
+
+struct IPFIX_SOFTFLOWD_DATA_TRANSPORT {
+  u_int16_t sourceTransportPort, destinationTransportPort;
+  u_int8_t protocolIdentifier, tcpControlBits, ipVersion, ipClassOfService;
+} __packed;
+
+struct IPFIX_SOFTFLOWD_DATA_ICMP {
+  u_int16_t icmpTypeCode;
+  u_int8_t ipVersion, ipClassOfService;
+} __packed;
+
+struct IPFIX_SOFTFLOWD_DATA_VLAN {
+  u_int16_t vlanId, postVlanId;
+} __packed;
+
+struct IPFIX_SOFTFLOWD_DATA_ETHER {
+  u_int8_t sourceMacAddress[6], destinationMacAddress[6];
+} __packed;
+
+struct IPFIX_SOFTFLOWD_DATA_BICOMMON {
+  u_int32_t octetDeltaCount, packetDeltaCount;
+  u_int8_t ipClassOfService;
+} __packed;
+
+struct IPFIX_SOFTFLOWD_DATA_BITRANSPORT {
+  u_int8_t tcpControlBits;
+} __packed;
+
+struct IPFIX_SOFTFLOWD_DATA_BIICMP {
+  u_int16_t icmpTypeCode;
+} __packed;
+
+union IPFIX_SOFTFLOWD_DATA_TIME {
+  struct {
+    u_int32_t start;
+    u_int32_t end;
+  } u32;
+  struct {
+    u_int64_t start;
+    u_int64_t end;
+  } u64;
+};
+
+struct IPFIX_SOFTFLOWD_DATA_V4ADDR {
+  u_int32_t sourceIPv4Address, destinationIPv4Address;
+} __packed;
+
+struct IPFIX_SOFTFLOWD_DATA_V6ADDR {
+  struct in6_addr sourceIPv6Address, destinationIPv6Address;
+} __packed;
+
+struct IPFIX_SOFTFLOWD_OPTION_DATA {
+  struct IPFIX_SET_HEADER c;
+  u_int32_t scope_pid;
+  u_int64_t systemInitTimeMilliseconds;
+  u_int32_t samplingInterval;
+  u_int32_t samplingSpace;
+  u_int16_t samplingAlgorithm;
+} __packed;
+
+struct NFLOW9_SOFTFLOWD_OPTION_DATA {
+  struct IPFIX_SET_HEADER c;
+  u_int32_t scope_ifidx;
+  u_int32_t samplingInterval;
+  u_int8_t samplingAlgorithm;
+} __packed;
+
+/* Local data: templates and counters */
+#define IPFIX_SOFTFLOWD_MAX_PACKET_SIZE     1428
+#define IPFIX_SOFTFLOWD_V4_TEMPLATE_ID      1024
+#define IPFIX_SOFTFLOWD_ICMPV4_TEMPLATE_ID  1025
+#define IPFIX_SOFTFLOWD_V6_TEMPLATE_ID      2048
+#define IPFIX_SOFTFLOWD_ICMPV6_TEMPLATE_ID  2049
+#define IPFIX_SOFTFLOWD_OPTION_TEMPLATE_ID  256
+
+#define IPFIX_DEFAULT_TEMPLATE_INTERVAL 16
+
+/* ... */
+#define IPFIX_OPTION_SCOPE_SYSTEM               1
+#define IPFIX_OPTION_SCOPE_INTERFACE            2
+#define IPFIX_OPTION_SCOPE_LINECARD             3
+#define IPFIX_OPTION_SCOPE_CACHE                4
+#define IPFIX_OPTION_SCOPE_TEMPLATE             5
+/* ... */
+#define IPFIX_SAMPLING_ALGORITHM_DETERMINISTIC  1
+#define IPFIX_SAMPLING_ALGORITHM_RANDOM         2
+/* ... */
+
+// prototype
+void memcpy_template (u_char * packet, u_int * offset,
+                      struct IPFIX_SOFTFLOWD_TEMPLATE *template,
+                      u_int8_t bi_flag);
+
+// variables
+enum { TMPLV4, TMPLICMPV4, TMPLV6, TMPLICMPV6, TMPLMAX };
+static struct IPFIX_SOFTFLOWD_TEMPLATE templates[TMPLMAX];
+static struct IPFIX_SOFTFLOWD_OPTION_TEMPLATE option_template;
+static struct IPFIX_SOFTFLOWD_OPTION_DATA option_data;
+static struct NFLOW9_SOFTFLOWD_OPTION_DATA nf9opt_data;
+
+static int ipfix_pkts_until_template = -1;
+
+int
+ipfix_init_fields (struct IPFIX_FIELD_SPECIFIER *dst,
+                   u_int * index,
+                   const struct IPFIX_FIELD_SPECIFIER *src,
+                   u_int field_number) {
+  int i, length = 0;
+  for (i = 0; i < field_number; i++) {
+    dst[*index + i].ie = htons (src[i].ie);
+    dst[*index + i].length = htons (src[i].length);
+    length += src[i].length;
+  }
+  *index += field_number;
+  return length;
+}
+
+void
+conv_unix_to_ntp (struct timeval tv, struct ntp_time_t *ntp) {
+  if (ntp != NULL) {
+    ntp->second = tv.tv_sec + 0x83AA7E80;
+    ntp->fraction =
+      (uint32_t) ((double) (tv.tv_usec + 1) * (double) (1LL << 32) * 1.0e-6);
+  }
+}
+
+struct timeval
+conv_ntp_to_unix (struct ntp_time_t ntp) {
+  struct timeval tv = {
+    ntp.second - 0x83AA7E80,    // the seconds from Jan 1, 1900 to Jan 1, 1970
+    (uint32_t) ((double) ntp.fraction * 1.0e6 / (double) (1LL << 32))
+  };
+  return tv;
+}
+
+static int
+ipfix_init_bifields (struct IPFIX_SOFTFLOWD_TEMPLATE *template,
+                     u_int * index,
+                     const struct IPFIX_FIELD_SPECIFIER *fields,
+                     u_int field_number) {
+  int i, length = 0;
+  for (i = 0; i < field_number; i++) {
+    template->v[*index + i].ie = htons (fields[i].ie | 0x8000);
+    template->v[*index + i].length = htons (fields[i].length);
+    template->v[*index + i].pen = htonl (REVERSE_PEN);
+    length += fields[i].length;
+  }
+  *index += field_number;
+  return length;
+}
+
+static int
+ipfix_init_template_time (struct FLOWTRACKPARAMETERS *param,
+                          struct IPFIX_SOFTFLOWD_TEMPLATE *template,
+                          u_int * index) {
+  int length = 0;
+  if (param->time_format == 's') {
+    length = ipfix_init_fields (template->r, index,
+                                field_timesec,
+                                IPFIX_SOFTFLOWD_TEMPLATE_TIMERECORDS);
+  } else if (param->time_format == 'm') {
+    length = ipfix_init_fields (template->r, index,
+                                field_timemsec,
+                                IPFIX_SOFTFLOWD_TEMPLATE_TIMERECORDS);
+  } else if (param->time_format == 'M') {
+    length = ipfix_init_fields (template->r, index,
+                                field_timeusec,
+                                IPFIX_SOFTFLOWD_TEMPLATE_TIMERECORDS);
+  } else if (param->time_format == 'n') {
+    length = ipfix_init_fields (template->r, index,
+                                field_timensec,
+                                IPFIX_SOFTFLOWD_TEMPLATE_TIMERECORDS);
+  } else {
+    length = ipfix_init_fields (template->r, index,
+                                field_timesysup,
+                                IPFIX_SOFTFLOWD_TEMPLATE_TIMERECORDS);
+  }
+  return length;
+}
+
+static void
+ipfix_init_template_unity (struct FLOWTRACKPARAMETERS *param,
+                           struct IPFIX_SOFTFLOWD_TEMPLATE *template,
+                           u_int template_id, u_int8_t v6_flag,
+                           u_int8_t icmp_flag, u_int8_t bi_flag,
+                           u_int16_t version) {
+  u_int index = 0, bi_index = 0, length = 0;
+  bzero (template, sizeof (*template));
+  template->h.c.set_id = htons (version == 10 ?
+                                IPFIX_TEMPLATE_SET_ID :
+                                NFLOW9_TEMPLATE_SET_ID);
+  template->h.r.template_id = htons (template_id);
+  if (v6_flag) {
+    length += ipfix_init_fields (template->r, &index,
+                                 field_v6,
+                                 IPFIX_SOFTFLOWD_TEMPLATE_IPRECORDS);
+  } else {
+    length += ipfix_init_fields (template->r, &index,
+                                 field_v4,
+                                 IPFIX_SOFTFLOWD_TEMPLATE_IPRECORDS);
+  }
+  length += ipfix_init_template_time (param, template, &index);
+  length += ipfix_init_fields (template->r, &index,
+                               field_common,
+                               IPFIX_SOFTFLOWD_TEMPLATE_COMMONRECORDS);
+  if (icmp_flag) {
+    if (v6_flag) {
+      length += ipfix_init_fields (template->r, &index,
+                                   field_icmp6,
+                                   IPFIX_SOFTFLOWD_TEMPLATE_ICMPRECORDS);
+    } else {
+      length += ipfix_init_fields (template->r, &index,
+                                   field_icmp4,
+                                   IPFIX_SOFTFLOWD_TEMPLATE_ICMPRECORDS);
+    }
+  } else {
+    length += ipfix_init_fields (template->r, &index,
+                                 field_transport,
+                                 IPFIX_SOFTFLOWD_TEMPLATE_TRANSPORTRECORDS);
+  }
+  if (param->track_level >= TRACK_FULL_VLAN) {
+    length += ipfix_init_fields (template->r, &index,
+                                 field_vlan,
+                                 IPFIX_SOFTFLOWD_TEMPLATE_VLANRECORDS);
+  }
+  if (param->track_level >= TRACK_FULL_VLAN_ETHER) {
+    length += ipfix_init_fields (template->r, &index,
+                                 field_ether,
+                                 IPFIX_SOFTFLOWD_TEMPLATE_ETHERRECORDS);
+  }
+  if (bi_flag) {
+    length +=
+      ipfix_init_bifields (template, &bi_index,
+                           field_bicommon,
+                           IPFIX_SOFTFLOWD_TEMPLATE_BICOMMONRECORDS);
+    if (icmp_flag) {
+      if (v6_flag) {
+        length +=
+          ipfix_init_bifields (template, &bi_index,
+                               field_biicmp6,
+                               IPFIX_SOFTFLOWD_TEMPLATE_BIICMPRECORDS);
+      } else {
+        length +=
+          ipfix_init_bifields (template, &bi_index,
+                               field_biicmp4,
+                               IPFIX_SOFTFLOWD_TEMPLATE_BIICMPRECORDS);
+      }
+    } else {
+      length +=
+        ipfix_init_bifields (template, &bi_index,
+                             field_bitransport,
+                             IPFIX_SOFTFLOWD_TEMPLATE_BITRANSPORTRECORDS);
+
+    }
+  }
+  template->bi_count = bi_index;
+  template->h.r.count = htons (index + bi_index);
+  template->h.c.length =
+    htons (sizeof (struct IPFIX_TEMPLATE_SET_HEADER) +
+           index * sizeof (struct IPFIX_FIELD_SPECIFIER) +
+           bi_index * sizeof (struct IPFIX_VENDOR_FIELD_SPECIFIER));
+  template->data_len = length;
+}
+
+static void
+ipfix_init_template (struct FLOWTRACKPARAMETERS *param,
+                     u_int8_t bi_flag, u_int16_t version) {
+  u_int8_t v6_flag = 0, icmp_flag = 0;
+  u_int16_t template_id = 0;
+  int i = 0;
+  for (i = 0; i < TMPLMAX; i++) {
+    switch (i) {
+    case TMPLV4:
+      v6_flag = 0;
+      icmp_flag = 0;
+      template_id = IPFIX_SOFTFLOWD_V4_TEMPLATE_ID;
+      break;
+    case TMPLICMPV4:
+      v6_flag = 0;
+      icmp_flag = 1;
+      template_id = IPFIX_SOFTFLOWD_ICMPV4_TEMPLATE_ID;
+      break;
+    case TMPLV6:
+      v6_flag = 1;
+      icmp_flag = 0;
+      template_id = IPFIX_SOFTFLOWD_V6_TEMPLATE_ID;
+      break;
+    case TMPLICMPV6:
+      v6_flag = 1;
+      icmp_flag = 1;
+      template_id = IPFIX_SOFTFLOWD_ICMPV6_TEMPLATE_ID;
+      break;
+    }
+    ipfix_init_template_unity (param, &templates[i],
+                               template_id, v6_flag,
+                               icmp_flag, bi_flag, version);
+  }
+}
+
+static void
+nflow9_init_option (u_int16_t ifidx, struct OPTION *option) {
+  u_int scope_index = 0, option_index = 0;
+  u_int16_t scope_len =
+    NFLOW9_SOFTFLOWD_OPTION_TEMPLATE_SCOPE_RECORDS *
+    sizeof (struct IPFIX_FIELD_SPECIFIER);
+  u_int16_t opt_len =
+    NFLOW9_SOFTFLOWD_OPTION_TEMPLATE_NRECORDS *
+    sizeof (struct IPFIX_FIELD_SPECIFIER);
+
+  bzero (&option_template, sizeof (option_template));
+  option_template.h.c.set_id = htons (NFLOW9_OPTION_TEMPLATE_SET_ID);
+  option_template.h.c.length =
+    htons (sizeof (option_template.h) + scope_len + opt_len);
+  option_template.h.u.n.template_id =
+    htons (IPFIX_SOFTFLOWD_OPTION_TEMPLATE_ID);
+  option_template.h.u.n.scope_length = htons (scope_len);
+  option_template.h.u.n.option_length = htons (opt_len);
+  ipfix_init_fields (option_template.s, &scope_index,
+                     field_nf9scope,
+                     NFLOW9_SOFTFLOWD_OPTION_TEMPLATE_SCOPE_RECORDS);
+  ipfix_init_fields (option_template.r, &option_index,
+                     field_nf9option,
+                     NFLOW9_SOFTFLOWD_OPTION_TEMPLATE_NRECORDS);
+  bzero (&nf9opt_data, sizeof (nf9opt_data));
+  nf9opt_data.c.set_id = htons (IPFIX_SOFTFLOWD_OPTION_TEMPLATE_ID);
+  nf9opt_data.c.length = htons (sizeof (nf9opt_data));
+  nf9opt_data.scope_ifidx = htonl (ifidx);
+  nf9opt_data.samplingInterval =
+    htonl (option->sample > 1 ? option->sample : 1);
+  nf9opt_data.samplingAlgorithm = NFLOW9_SAMPLING_ALGORITHM_DETERMINISTIC;
+}
+
+static void
+ipfix_init_option (struct timeval *system_boot_time, struct OPTION *option) {
+  u_int scope_index = 0, option_index = 0;
+  bzero (&option_template, sizeof (option_template));
+  option_template.h.c.set_id = htons (IPFIX_OPTION_TEMPLATE_SET_ID);
+  option_template.h.c.length = htons (sizeof (option_template));
+  option_template.h.u.i.r.template_id =
+    htons (IPFIX_SOFTFLOWD_OPTION_TEMPLATE_ID);
+  option_template.h.u.i.r.count =
+    htons (IPFIX_SOFTFLOWD_OPTION_TEMPLATE_SCOPE_RECORDS +
+           IPFIX_SOFTFLOWD_OPTION_TEMPLATE_NRECORDS);
+  option_template.h.u.i.scope_count =
+    htons (IPFIX_SOFTFLOWD_OPTION_TEMPLATE_SCOPE_RECORDS);
+
+  ipfix_init_fields (option_template.s, &scope_index,
+                     field_scope,
+                     IPFIX_SOFTFLOWD_OPTION_TEMPLATE_SCOPE_RECORDS);
+  ipfix_init_fields (option_template.r, &option_index, field_option,
+                     IPFIX_SOFTFLOWD_OPTION_TEMPLATE_NRECORDS);
+
+  bzero (&option_data, sizeof (option_data));
+  option_data.c.set_id = htons (IPFIX_SOFTFLOWD_OPTION_TEMPLATE_ID);
+  option_data.c.length = htons (sizeof (option_data));
+  option_data.scope_pid = htonl ((u_int32_t) option->meteringProcessId);
+#if defined(htobe64) || defined(HAVE_DECL_HTOBE64)
+  option_data.systemInitTimeMilliseconds =
+    htobe64 ((u_int64_t) system_boot_time->tv_sec * 1000 +
+             (u_int64_t) system_boot_time->tv_usec / 1000);
+#endif
+  option_data.samplingAlgorithm = htons (PSAMP_selectorAlgorithm_count);
+  option_data.samplingInterval = htonl (1);
+  option_data.samplingSpace =
+    htonl (option->sample > 0 ? option->sample - 1 : 0);
+}
+
+static int
+copy_data_time (union IPFIX_SOFTFLOWD_DATA_TIME *dt,
+                const struct FLOW *flow,
+                const struct timeval *system_boot_time,
+                struct FLOWTRACKPARAMETERS *param) {
+  int length = (param->time_format == 'm' || param->time_format == 'M'
+                || param->time_format == 'n') ? 16 : 8;
+  if (dt == NULL)
+    return -1;
+
+  switch (param->time_format) {
+    struct ntp_time_t ntptime;
+  case 's':
+    dt->u32.start = htonl (flow->flow_start.tv_sec);
+    dt->u32.end = htonl (flow->flow_last.tv_sec);
+    break;
+#if defined(htobe64) || defined(HAVE_DECL_HTOBE64)
+  case 'm':
+    dt->u64.start =
+      htobe64 ((u_int64_t) flow->flow_start.tv_sec * 1000 +
+               (u_int64_t) flow->flow_start.tv_usec / 1000);
+    dt->u64.end =
+      htobe64 ((u_int64_t) flow->flow_last.tv_sec * 1000 +
+               (u_int64_t) flow->flow_last.tv_usec / 1000);
+    break;
+  case 'M':
+  case 'n':
+    conv_unix_to_ntp ((struct timeval) flow->flow_start, &ntptime);
+    dt->u64.start =
+      htobe64 ((u_int64_t) ntptime.second << 32 | ntptime.fraction);
+    conv_unix_to_ntp ((struct timeval) flow->flow_last, &ntptime);
+    dt->u64.end =
+      htobe64 ((u_int64_t) ntptime.second << 32 | ntptime.fraction);
+    break;
+#endif
+  default:
+    dt->u32.start =
+      htonl (timeval_sub_ms (&flow->flow_start, system_boot_time));
+    dt->u32.end = htonl (timeval_sub_ms (&flow->flow_last, system_boot_time));
+    break;
+  }
+  return length;
+}
+
+static u_int
+ipfix_flow_to_template_index (const struct FLOW *flow) {
+  u_int index = 0;
+  if (flow->af == AF_INET) {
+    index = (flow->protocol == IPPROTO_ICMP) ? TMPLICMPV4 : TMPLV4;
+  } else if (flow->af == AF_INET6) {
+    index = (flow->protocol == IPPROTO_ICMPV6) ? TMPLICMPV6 : TMPLV6;
+  }
+  return index;
+}
+
+static int
+ipfix_flow_to_flowset (const struct FLOW *flow, u_char * packet,
+                       u_int len, u_int16_t ifidx,
+                       const struct timeval *system_boot_time,
+                       u_int * len_used,
+                       struct FLOWTRACKPARAMETERS *param, u_int8_t bi_flag) {
+  struct IPFIX_SOFTFLOWD_DATA_V4ADDR *d4[2] = { NULL, NULL };
+  struct IPFIX_SOFTFLOWD_DATA_V6ADDR *d6[2] = { NULL, NULL };
+  union IPFIX_SOFTFLOWD_DATA_TIME *dt[2] = { NULL, NULL };
+  struct IPFIX_SOFTFLOWD_DATA_COMMON *dc[2] = { NULL, NULL };
+  struct IPFIX_SOFTFLOWD_DATA_TRANSPORT *dtr[2] = { NULL, NULL };
+  struct IPFIX_SOFTFLOWD_DATA_ICMP *di[2] = { NULL, NULL };
+  struct IPFIX_SOFTFLOWD_DATA_VLAN *dv[2] = { NULL, NULL };
+  struct IPFIX_SOFTFLOWD_DATA_ETHER *de[2] = { NULL, NULL };
+  struct IPFIX_SOFTFLOWD_DATA_BICOMMON *dbc = NULL;
+  struct IPFIX_SOFTFLOWD_DATA_BITRANSPORT *dbtr = NULL;
+  struct IPFIX_SOFTFLOWD_DATA_BIICMP *dbi = NULL;
+
+  u_int freclen = 0, nflows = 0, offset = 0;
+  u_int frecnum = bi_flag ? 1 : 2;
+  u_int tmplindex = ipfix_flow_to_template_index (flow);
+  int i = 0;
+  freclen = templates[tmplindex].data_len;
+  if (len < freclen * frecnum)
+    return (-1);
+
+  for (i = 0; i < frecnum; i++) {
+    if (bi_flag == 0 && flow->octets[i] == 0)
+      continue;
+    nflows++;
+    if (flow->af == AF_INET) {
+      d4[i] = (struct IPFIX_SOFTFLOWD_DATA_V4ADDR *) &packet[offset];
+      memcpy (&d4[i]->sourceIPv4Address, &flow->addr[i].v4, 4);
+      memcpy (&d4[i]->destinationIPv4Address, &flow->addr[i ^ 1].v4, 4);
+      offset += sizeof (struct IPFIX_SOFTFLOWD_DATA_V4ADDR);
+    } else if (flow->af == AF_INET6) {
+      d6[i] = (struct IPFIX_SOFTFLOWD_DATA_V6ADDR *) &packet[offset];
+      memcpy (&d6[i]->sourceIPv6Address, &flow->addr[i].v6, 16);
+      memcpy (&d6[i]->destinationIPv6Address, &flow->addr[i ^ 1].v6, 16);
+      offset += sizeof (struct IPFIX_SOFTFLOWD_DATA_V6ADDR);
+    }
+
+    dt[i] = (union IPFIX_SOFTFLOWD_DATA_TIME *) &packet[offset];
+    offset += copy_data_time (dt[i], flow, system_boot_time, param);
+
+    dc[i] = (struct IPFIX_SOFTFLOWD_DATA_COMMON *) &packet[offset];
+    dc[i]->octetDeltaCount = htonl (flow->octets[i]);
+    dc[i]->packetDeltaCount = htonl (flow->packets[i]);
+    dc[i]->ingressInterface = dc[i]->egressInterface = htonl (ifidx);
+    offset += sizeof (struct IPFIX_SOFTFLOWD_DATA_COMMON);
+
+    if (flow->protocol != IPPROTO_ICMP && flow->protocol != IPPROTO_ICMPV6) {
+      dtr[i] = (struct IPFIX_SOFTFLOWD_DATA_TRANSPORT *) &packet[offset];
+      dtr[i]->sourceTransportPort = flow->port[i];
+      dtr[i]->destinationTransportPort = flow->port[i ^ 1];
+      dtr[i]->protocolIdentifier = flow->protocol;
+      dtr[i]->tcpControlBits = flow->tcp_flags[i];
+      dtr[i]->ipClassOfService = flow->tos[i];
+      dtr[i]->ipVersion = (flow->af == AF_INET) ? 4 : 6;
+      offset += sizeof (struct IPFIX_SOFTFLOWD_DATA_TRANSPORT);
+    } else {
+      di[i] = (struct IPFIX_SOFTFLOWD_DATA_ICMP *) &packet[offset];
+      di[i]->icmpTypeCode = flow->port[i ^ 1];
+      di[i]->ipClassOfService = flow->tos[i];
+      di[i]->ipVersion = (flow->af == AF_INET) ? 4 : 6;
+      offset += sizeof (struct IPFIX_SOFTFLOWD_DATA_ICMP);
+    }
+    if (param->track_level >= TRACK_FULL_VLAN) {
+      dv[i] = (struct IPFIX_SOFTFLOWD_DATA_VLAN *) &packet[offset];
+      dv[i]->vlanId = flow->vlanid[i];
+      dv[i]->postVlanId = flow->vlanid[i ^ 1];
+      offset += sizeof (struct IPFIX_SOFTFLOWD_DATA_VLAN);
+    }
+    if (param->track_level >= TRACK_FULL_VLAN_ETHER) {
+      de[i] = (struct IPFIX_SOFTFLOWD_DATA_ETHER *) &packet[offset];
+      memcpy (&de[i]->sourceMacAddress, &flow->ethermac[i], 6);
+      memcpy (&de[i]->destinationMacAddress, &flow->ethermac[i ^ 1], 6);
+      offset += sizeof (struct IPFIX_SOFTFLOWD_DATA_ETHER);
+    }
+    if (bi_flag && i == 0) {
+      dbc = (struct IPFIX_SOFTFLOWD_DATA_BICOMMON *) &packet[offset];
+      dbc->octetDeltaCount = htonl (flow->octets[1]);
+      dbc->packetDeltaCount = htonl (flow->packets[1]);
+      dbc->ipClassOfService = flow->tos[1];
+      offset += sizeof (struct IPFIX_SOFTFLOWD_DATA_BICOMMON);
+      if (flow->protocol != IPPROTO_ICMP && flow->protocol != IPPROTO_ICMPV6) {
+        dbtr = (struct IPFIX_SOFTFLOWD_DATA_BITRANSPORT *)
+          &packet[offset];
+        dbtr->tcpControlBits = flow->tcp_flags[1];
+        offset += sizeof (struct IPFIX_SOFTFLOWD_DATA_BITRANSPORT);
+      } else {
+        dbi = (struct IPFIX_SOFTFLOWD_DATA_BIICMP *)
+          &packet[offset];
+        dbi->icmpTypeCode = flow->port[1];
+        offset += sizeof (struct IPFIX_SOFTFLOWD_DATA_BIICMP);
+      }
+    }
+  }
+  *len_used = offset;
+  return (nflows);
+}
+
+static int
+valuate_icmp (struct FLOW *flow) {
+  if (flow == NULL)
+    return -1;
+  if (flow->af == AF_INET)
+    if (flow->protocol == IPPROTO_ICMP)
+      return 1;
+    else
+      return 0;
+  else if (flow->af == AF_INET6)
+    if (flow->protocol == IPPROTO_ICMPV6)
+      return 1;
+    else
+      return 0;
+  else
+    return -1;
+  return -1;
+}
+
+void
+ipfix_resend_template (void) {
+  if (ipfix_pkts_until_template > 0)
+    ipfix_pkts_until_template = 0;
+}
+
+void
+memcpy_template (u_char * packet, u_int * offset,
+                 struct IPFIX_SOFTFLOWD_TEMPLATE *template, u_int8_t bi_flag) 
+{
+  int size = ntohs (template->h.c.length) -
+    template->bi_count * sizeof (struct IPFIX_VENDOR_FIELD_SPECIFIER);
+  memcpy (packet + *offset, template, size);
+  *offset += size;
+  if (bi_flag) {
+    size = template->bi_count * sizeof (struct IPFIX_VENDOR_FIELD_SPECIFIER);
+    memcpy (packet + *offset, template->v, size);
+    *offset += size;
+  }
+}
+
+/*
+ * Given an array of expired flows, send ipfix report packets
+ * Returns number of packets sent or -1 on error
+ */
+static int
+send_ipfix_common (struct FLOW **flows, int num_flows,
+                   struct NETFLOW_TARGET *target,
+                   u_int16_t ifidx, struct FLOWTRACKPARAMETERS *param,
+                   int verbose_flag, u_int8_t bi_flag, u_int16_t version) {
+  struct IPFIX_HEADER *ipfix;
+  struct NFLOW9_HEADER *nf9;
+  struct IPFIX_SET_HEADER *dh;
+  struct timeval now;
+  u_int offset, last_af, i, j, num_packets, inc, last_valid, tmplindex;
+  int8_t icmp_flag, last_icmp_flag;
+  int r;
+  u_int records;
+  u_char packet[IPFIX_SOFTFLOWD_MAX_PACKET_SIZE];
+  struct timeval *system_boot_time = &param->system_boot_time;
+  u_int64_t *flows_exported = &param->flows_exported;
+  u_int64_t *records_sent = &param->records_sent;
+  struct OPTION *option = &param->option;
+
+  if (version != 9 && version != 10)
+    return (-1);
+  if (param->adjust_time)
+    now = param->last_packet_time;
+  else
+    gettimeofday (&now, NULL);
+
+  if (ipfix_pkts_until_template == -1) {
+    ipfix_init_template (param, bi_flag, version);
+    ipfix_pkts_until_template = 0;
+    if (option != NULL) {
+      if (version == 10) {
+        ipfix_init_option (system_boot_time, option);
+      } else {
+        nflow9_init_option (ifidx, option);
+      }
+    }
+  }
+
+  last_valid = num_packets = 0;
+  for (j = 0; j < num_flows;) {
+    bzero (packet, sizeof (packet));
+    if (version == 10) {
+      ipfix = (struct IPFIX_HEADER *) packet;
+      ipfix->version = htons (version);
+      ipfix->length = 0;        /* Filled as we go, htons at end */
+      if (param->adjust_time)
+        ipfix->export_time = htonl (now.tv_sec);
+      else
+        ipfix->export_time = htonl (time (NULL));
+      ipfix->od_id = 0;
+      offset = sizeof (*ipfix);
+    } else if (version == 9) {
+      nf9 = (struct NFLOW9_HEADER *) packet;
+      nf9->version = htons (version);
+      nf9->flows = 0;           /* Filled as we go, htons at end */
+      nf9->uptime_ms = htonl (timeval_sub_ms (&now, system_boot_time));
+      if (param->adjust_time)
+        nf9->export_time = htonl (now.tv_sec);
+      else
+        nf9->export_time = htonl (time (NULL));
+      nf9->od_id = 0;
+      offset = sizeof (*nf9);
+    }
+
+    /* Refresh template headers if we need to */
+    if (ipfix_pkts_until_template <= 0) {
+      for (i = 0; i < TMPLMAX; i++) {
+        memcpy_template (packet, &offset, &templates[i], bi_flag);
+      }
+      if (option != NULL) {
+        u_int16_t opt_tmpl_len = ntohs (option_template.h.c.length);
+        memcpy (packet + offset, &option_template, opt_tmpl_len);
+        offset += opt_tmpl_len;
+        if (version == 10) {
+          memcpy (packet + offset, &option_data, sizeof (option_data));
+          offset += sizeof (option_data);
+        } else if (version == 9) {
+          memcpy (packet + offset, &nf9opt_data, sizeof (nf9opt_data));
+          offset += sizeof (nf9opt_data);
+        }
+      }
+
+      ipfix_pkts_until_template = IPFIX_DEFAULT_TEMPLATE_INTERVAL;
+      if (target->is_loadbalance && target->num_destinations > 1) {
+        ipfix->length = htons (offset);
+        if (version == 10) {
+          ipfix->sequence =
+            htonl ((u_int32_t) (*records_sent & 0x00000000ffffffff));
+        } else if (version == 9) {
+          nf9->sequence =
+            htonl ((u_int32_t) (*records_sent & 0x00000000ffffffff));
+        }
+        if (send_multi_destinations
+            (target->num_destinations, target->destinations, 0, packet,
+             offset) < 0)
+          return (-1);
+        offset = version == 10 ? sizeof (*ipfix) : sizeof (*nf9);       // resest offset
+      }
+    }
+
+    dh = NULL;
+    last_af = 0;
+    last_icmp_flag = -1;
+    records = 0;
+    for (i = 0; i + j < num_flows; i++) {
+      icmp_flag = valuate_icmp (flows[i + j]);
+      if (dh == NULL || flows[i + j]->af != last_af ||
+          icmp_flag != last_icmp_flag) {
+        if (dh != NULL) {
+          if (offset % 4 != 0) {
+            /* Pad to multiple of 4 */
+            dh->length += 4 - (offset % 4);
+            offset += 4 - (offset % 4);
+          }
+          /* Finalise last header */
+          dh->length = htons (dh->length);
+        }
+        if (offset + sizeof (*dh) > sizeof (packet)) {
+          /* Mark header is finished */
+          dh = NULL;
+          break;
+        }
+        dh = (struct IPFIX_SET_HEADER *) (packet + offset);
+        tmplindex = ipfix_flow_to_template_index (flows[i + j]);
+        dh->set_id = templates[tmplindex].h.r.template_id;
+        last_af = flows[i + j]->af;
+        last_icmp_flag = icmp_flag;
+        last_valid = offset;
+        dh->length = sizeof (*dh);      /* Filled as we go */
+        offset += sizeof (*dh);
+      }
+      r = ipfix_flow_to_flowset (flows[i + j],
+                                 packet + offset,
+                                 sizeof (packet) - offset,
+                                 ifidx, system_boot_time,
+                                 &inc, param, bi_flag);
+      if (r <= 0) {
+        /* yank off data header, if we had to go back */
+        if (last_valid)
+          offset = last_valid;
+        break;
+      }
+      records += (u_int) r;
+      offset += inc;
+      dh->length += inc;
+      last_valid = 0;           /* Don't clobber this header now */
+      if (verbose_flag) {
+        logit (LOG_DEBUG, "Flow %d/%d: "
+               "r %d offset %d ie %04x len %d(0x%04x)",
+               r, i, j, offset, dh->set_id, dh->length, dh->length);
+      }
+    }
+    /* Don't finish header if it has already been done */
+    if (dh != NULL) {
+      if (offset % 4 != 0) {
+        /* Pad to multiple of 4 */
+        dh->length += 4 - (offset % 4);
+        offset += 4 - (offset % 4);
+      }
+      /* Finalise last header */
+      dh->length = htons (dh->length);
+    }
+    ipfix->length = htons (offset);
+    *records_sent += records;
+    if (version == 10) {
+      ipfix->sequence =
+        htonl ((u_int32_t) (*records_sent & 0x00000000ffffffff));
+    } else if (version == 9) {
+      nf9->sequence =
+        htonl ((u_int32_t) (*records_sent & 0x00000000ffffffff));
+    }
+
+    if (verbose_flag)
+      logit (LOG_DEBUG, "Sending flow packet len = %d", offset);
+    if (send_multi_destinations
+        (target->num_destinations, target->destinations,
+         target->is_loadbalance, packet, offset) < 0)
+      return (-1);
+    num_packets++;
+    ipfix_pkts_until_template--;
+
+    j += i;
+  }
+
+  *flows_exported += j;
+  param->packets_sent += num_packets;
+#ifdef ENABLE_PTHREAD
+  if (use_thread)
+    free (flows);
+#endif /* ENABLE_PTHREAD */
+  return (num_packets);
+}
+
+int
+send_nflow9 (struct SENDPARAMETER sp) {
+  return send_ipfix_common (sp.flows, sp.num_flows, sp.target, sp.ifidx,
+                            sp.param, sp.verbose_flag, 0, 9);
+}
+
+int
+send_ipfix (struct SENDPARAMETER sp) {
+  return send_ipfix_common (sp.flows, sp.num_flows, sp.target, sp.ifidx,
+                            sp.param, sp.verbose_flag, 0, 10);
+}
+
+int
+send_ipfix_bi (struct SENDPARAMETER sp) {
+  return send_ipfix_common (sp.flows, sp.num_flows, sp.target, sp.ifidx,
+                            sp.param, sp.verbose_flag, 1, 10);
+}

+ 141 - 0
ipfix.h

@@ -0,0 +1,141 @@
+/*
+ * Copyright 2019 Hitoshi Irino <irino@sfc.wide.ad.jp> 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 _IPFIX_H
+#define _IPFIX_H
+
+#include "softflowd.h"
+
+#define IPFIX_TEMPLATE_SET_ID           2
+#define IPFIX_OPTION_TEMPLATE_SET_ID    3
+#define IPFIX_MIN_RECORD_SET_ID         256
+
+/* Flowset record ies the we care about */
+#define IPFIX_octetDeltaCount           1
+#define IPFIX_packetDeltaCount          2
+/* ... */
+#define IPFIX_protocolIdentifier        4
+#define IPFIX_ipClassOfService          5
+/* ... */
+#define IPFIX_tcpControlBits            6
+#define IPFIX_sourceTransportPort       7
+#define IPFIX_sourceIPv4Address         8
+/* ... */
+#define IPFIX_ingressInterface          10
+#define IPFIX_destinationTransportPort  11
+#define IPFIX_destinationIPv4Address    12
+/* ... */
+#define IPFIX_egressInterface           14
+/* ... */
+#define IPFIX_flowEndSysUpTime          21
+#define IPFIX_flowStartSysUpTime        22
+/* ... */
+#define IPFIX_sourceIPv6Address         27
+#define IPFIX_destinationIPv6Address    28
+/* ... */
+#define IPFIX_icmpTypeCodeIPv4          32
+/* ... */
+#define IPFIX_sourceMacAddress          56
+#define IPFIX_postDestinationMacAddress 57
+#define IPFIX_vlanId                    58
+#define IPFIX_postVlanId                59
+
+#define IPFIX_ipVersion                     60
+/* ... */
+#define IPFIX_icmpTypeCodeIPv6              139
+/* ... */
+#define IPFIX_meteringProcessId             143
+/* ... */
+#define IPFIX_flowStartSeconds              150
+#define IPFIX_flowEndSeconds                151
+#define IPFIX_flowStartMilliSeconds         152
+#define IPFIX_flowEndMilliSeconds           153
+#define IPFIX_flowStartMicroSeconds         154
+#define IPFIX_flowEndMicroSeconds           155
+#define IPFIX_flowStartNanoSeconds          156
+#define IPFIX_flowEndNanoSeconds            157
+/* ... */
+#define IPFIX_systemInitTimeMilliseconds    160
+/* ... */
+
+
+#define IPFIX_SOFTFLOWD_MAX_PACKET_SIZE     1428
+struct IPFIX_HEADER {
+  u_int16_t version, length;
+  u_int32_t export_time;        /* in seconds */
+  u_int32_t sequence, od_id;
+} __packed;
+struct IPFIX_SET_HEADER {
+  u_int16_t set_id, length;
+} __packed;
+struct IPFIX_TEMPLATE_RECORD_HEADER {
+  u_int16_t template_id, count;
+} __packed;
+struct IPFIX_TEMPLATE_SET_HEADER {
+  struct IPFIX_SET_HEADER c;
+  struct IPFIX_TEMPLATE_RECORD_HEADER r;
+} __packed;
+
+struct IPFIX_FIELD_SPECIFIER {
+  u_int16_t ie, length;
+} __packed;
+
+struct IPFIX_OPTION_TEMPLATE_SET_HEADER {
+  struct IPFIX_SET_HEADER c;
+  union {
+    struct {
+      struct IPFIX_TEMPLATE_RECORD_HEADER r;
+      u_int16_t scope_count;
+    } i;
+    struct {
+      u_int16_t template_id;
+      u_int16_t scope_length;
+      u_int16_t option_length;
+    } n;
+  } u;
+} __packed;
+
+struct IPFIX_VENDOR_FIELD_SPECIFIER {
+  u_int16_t ie, length;
+  u_int32_t pen;
+} __packed;
+#define REVERSE_PEN 29305
+
+struct ntp_time_t {
+  uint32_t second;
+  uint32_t fraction;
+};
+
+/* Prototypes for functions to send NetFlow packets */
+int send_nflow9 (struct SENDPARAMETER sp);
+int send_ipfix (struct SENDPARAMETER sp);
+int send_ipfix_bi (struct SENDPARAMETER sp);
+/* Force a resend of the flow template */
+void ipfix_resend_template (void);
+int ipfix_init_fields (struct IPFIX_FIELD_SPECIFIER *dst, u_int * index,
+                       const struct IPFIX_FIELD_SPECIFIER *src,
+                       u_int field_number);
+void conv_unix_to_ntp (struct timeval tv, struct ntp_time_t *ntp);
+struct timeval conv_ntp_to_unix (struct ntp_time_t ntp);
+#endif /* _IPFIX_H */

+ 144 - 20
mkinstalldirs

@@ -1,38 +1,162 @@
 #! /bin/sh
 # mkinstalldirs --- make directory hierarchy
-# Author: Noah Friedman <friedman@prep.ai.mit.edu>
+
+scriptversion=2009-04-28.21; # UTC
+
+# Original author: Noah Friedman <friedman@prep.ai.mit.edu>
 # Created: 1993-05-16
-# Public domain
+# Public domain.
+#
+# This file is maintained in Automake, please report
+# bugs to <bug-automake@gnu.org> or send patches to
+# <automake-patches@gnu.org>.
 
+nl='
+'
+IFS=" ""	$nl"
 errstatus=0
+dirmode=
+
+usage="\
+Usage: mkinstalldirs [-h] [--help] [--version] [-m MODE] DIR ...
+
+Create each directory DIR (with mode MODE, if specified), including all
+leading file name components.
+
+Report bugs to <bug-automake@gnu.org>."
+
+# process command line arguments
+while test $# -gt 0 ; do
+  case $1 in
+    -h | --help | --h*)         # -h for help
+      echo "$usage"
+      exit $?
+      ;;
+    -m)                         # -m PERM arg
+      shift
+      test $# -eq 0 && { echo "$usage" 1>&2; exit 1; }
+      dirmode=$1
+      shift
+      ;;
+    --version)
+      echo "$0 $scriptversion"
+      exit $?
+      ;;
+    --)                         # stop option processing
+      shift
+      break
+      ;;
+    -*)                         # unknown option
+      echo "$usage" 1>&2
+      exit 1
+      ;;
+    *)                          # first non-opt arg
+      break
+      ;;
+  esac
+done
 
 for file
 do
-   set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
-   shift
+  if test -d "$file"; then
+    shift
+  else
+    break
+  fi
+done
+
+case $# in
+  0) exit 0 ;;
+esac
+
+# Solaris 8's mkdir -p isn't thread-safe.  If you mkdir -p a/b and
+# mkdir -p a/c at the same time, both will detect that a is missing,
+# one will create a, then the other will try to create a and die with
+# a "File exists" error.  This is a problem when calling mkinstalldirs
+# from a parallel make.  We use --version in the probe to restrict
+# ourselves to GNU mkdir, which is thread-safe.
+case $dirmode in
+  '')
+    if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then
+      echo "mkdir -p -- $*"
+      exec mkdir -p -- "$@"
+    else
+      # On NextStep and OpenStep, the 'mkdir' command does not
+      # recognize any option.  It will interpret all options as
+      # directories to create, and then abort because '.' already
+      # exists.
+      test -d ./-p && rmdir ./-p
+      test -d ./--version && rmdir ./--version
+    fi
+    ;;
+  *)
+    if mkdir -m "$dirmode" -p --version . >/dev/null 2>&1 &&
+       test ! -d ./--version; then
+      echo "mkdir -m $dirmode -p -- $*"
+      exec mkdir -m "$dirmode" -p -- "$@"
+    else
+      # Clean up after NextStep and OpenStep mkdir.
+      for d in ./-m ./-p ./--version "./$dirmode";
+      do
+        test -d $d && rmdir $d
+      done
+    fi
+    ;;
+esac
+
+for file
+do
+  case $file in
+    /*) pathcomp=/ ;;
+    *)  pathcomp= ;;
+  esac
+  oIFS=$IFS
+  IFS=/
+  set fnord $file
+  shift
+  IFS=$oIFS
+
+  for d
+  do
+    test "x$d" = x && continue
+
+    pathcomp=$pathcomp$d
+    case $pathcomp in
+      -*) pathcomp=./$pathcomp ;;
+    esac
 
-   pathcomp=
-   for d
-   do
-     pathcomp="$pathcomp$d"
-     case "$pathcomp" in
-       -* ) pathcomp=./$pathcomp ;;
-     esac
+    if test ! -d "$pathcomp"; then
+      echo "mkdir $pathcomp"
 
-     if test ! -d "$pathcomp"; then
-	echo "mkdir $pathcomp"
+      mkdir "$pathcomp" || lasterr=$?
 
-	mkdir "$pathcomp" || lasterr=$?
+      if test ! -d "$pathcomp"; then
+	errstatus=$lasterr
+      else
+	if test ! -z "$dirmode"; then
+	  echo "chmod $dirmode $pathcomp"
+	  lasterr=
+	  chmod "$dirmode" "$pathcomp" || lasterr=$?
 
-	if test ! -d "$pathcomp"; then
-	  errstatus=$lasterr
+	  if test ! -z "$lasterr"; then
+	    errstatus=$lasterr
+	  fi
 	fi
-     fi
+      fi
+    fi
 
-     pathcomp="$pathcomp/"
-   done
+    pathcomp=$pathcomp/
+  done
 done
 
 exit $errstatus
 
-# mkinstalldirs ends here
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:

+ 122 - 111
netflow1.c

@@ -1,3 +1,4 @@
+
 /*
  * Copyright 2002 Damien Miller <djm@mindrot.org> All rights reserved.
  *
@@ -21,33 +22,33 @@
  * (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 "log.h"
 #include "treetype.h"
 #include "softflowd.h"
 
+#ifdef ENABLE_LEGACY
 /*
  * This is the Cisco Netflow(tm) version 1 packet format
  * Based on:
  * http://www.cisco.com/en/US/products/sw/netmgtsw/ps1964/products_implementation_design_guide09186a00800d6a11.html 
  */
 struct NF1_HEADER {
-	u_int16_t version, flows;
-	u_int32_t uptime_ms, time_sec, time_nanosec;
+  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;
+  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 */
+  u_int8_t reserved2;           /* XXX: no longer used */
 #endif
 };
 /* Maximum of 24 flows per packet */
@@ -60,108 +61,118 @@ struct NF1_FLOW {
  * Returns number of packets sent or -1 on error
  */
 int
-send_netflow_v1(struct FLOW **flows, int num_flows, int nfsock, u_int16_t ifidx,
-    u_int64_t *flows_exported, struct timeval *system_boot_time, 
-    int verbose_flag, struct OPTION *option)
-{
-	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);
+send_netflow_v1 (struct SENDPARAMETER sp) {
+  struct FLOW **flows = sp.flows;
+  int num_flows = sp.num_flows;
+  u_int16_t ifidx = sp.ifidx;
+  struct FLOWTRACKPARAMETERS *param = sp.param;
+  int verbose_flag = sp.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;
+  struct timeval *system_boot_time = &param->system_boot_time;
+  u_int64_t *flows_exported = &param->flows_exported;
+
+  if (param->adjust_time)
+    now = param->last_packet_time;
+  else
+    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);
-		}		
+  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);
+      param->records_sent += hdr->flows;
+      hdr->flows = htons (hdr->flows);
+      if (send_multi_destinations
+          (sp.target->num_destinations, sp.target->destinations,
+           sp.target->is_loadbalance, packet, offset) < 0)
+        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);
-		flw->if_index_in = flw->if_index_out = htons(ifidx);
+    flw = (struct NF1_FLOW *) (packet + offset);
+    flw->if_index_in = flw->if_index_out = htons (ifidx);
 
-		/* NetFlow v.1 doesn't do IPv6 */
-		if (flows[i]->af != AF_INET)
-			continue;
-		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++;
-		}
+    /* 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];
+      flw->tos = flows[i]->tos[0];
+      offset += sizeof (*flw);
+      j++;
+      hdr->flows++;
+    }
 
-		flw = (struct NF1_FLOW *)(packet + offset);
-		flw->if_index_in = flw->if_index_out = htons(ifidx);
-		if (flows[i]->octets[1] > 0) {
-			flw->src_ip = flows[i]->addr[1].v4.s_addr;
-			flw->dest_ip = flows[i]->addr[0].v4.s_addr;
-			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++;
-		}
-	}
+    flw = (struct NF1_FLOW *) (packet + offset);
+    flw->if_index_in = flw->if_index_out = htons (ifidx);
+    if (flows[i]->octets[1] > 0) {
+      flw->src_ip = flows[i]->addr[1].v4.s_addr;
+      flw->dest_ip = flows[i]->addr[0].v4.s_addr;
+      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];
+      flw->tos = flows[i]->tos[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++;
-	}
+  /* Send any leftovers */
+  if (j != 0) {
+    if (verbose_flag)
+      logit (LOG_DEBUG, "Sending flow packet len = %d", offset);
+    param->records_sent += hdr->flows;
+    hdr->flows = htons (hdr->flows);
+    if (send_multi_destinations
+        (sp.target->num_destinations, sp.target->destinations,
+         sp.target->is_loadbalance, packet, offset) < 0)
+      return (-1);
+    num_packets++;
+  }
 
-	*flows_exported += j;
-	return (num_packets);
+  *flows_exported += j;
+  param->packets_sent += num_packets;
+#ifdef ENABLE_PTHREAD
+  if (use_thread)
+    free (sp.flows);
+#endif /* ENABLE_PTHREAD */
+  return (num_packets);
 }
+#endif /* ENABLE_LEGACY */

+ 179 - 120
netflow5.c

@@ -31,144 +31,203 @@
  * This is the Cisco Netflow(tm) version 5 packet format
  * Based on:
  * http://www.cisco.com/en/US/products/sw/netmgtsw/ps1964/products_implementation_design_guide09186a00800d6a11.html 
+ * https://www.cisco.com/c/en/us/td/docs/net_mgmt/netflow_collection_engine/3-6/user/guide/format.html#wp1007472
  */
 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;
-	u_int16_t sampling_interval;
+  u_int16_t version, flows;     // same as netflow v1
+  u_int32_t uptime_ms, time_sec, time_nanosec;  // same as netflow v1
+  u_int32_t flow_sequence;
+  u_int8_t engine_type, engine_id;
+  u_int16_t sampling_interval;
 };
 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;
+  u_int32_t src_ip, dest_ip, nexthop_ip;        // same as netflow v1
+  u_int16_t if_index_in, if_index_out;  // same as netflow v1
+  u_int32_t flow_packets, flow_octets;  // same as netflow v1
+  u_int32_t flow_start, flow_finish;    // same as netflow v1
+  u_int16_t src_port, dest_port;        // same as netflow v1
+  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;
 };
+struct NF1_FLOW_PROTO_TOS_TCPF {
+  u_int16_t pad1;
+  u_int8_t protocol, tos, tcp_flags;
+  u_int8_t pad2, pad3, pad4;
+  u_int32_t reserved1;
+};
+
 #define NF5_MAXFLOWS		30
 #define NF5_MAXPACKET_SIZE	(sizeof(struct NF5_HEADER) + \
 				 (NF5_MAXFLOWS * sizeof(struct NF5_FLOW)))
+#define NF1_HEADER_SIZE 16
+#define NF5_NF1_FLOW_COMMON_SIZE (sizeof(struct NF5_FLOW) - \
+                                  sizeof(struct NF1_FLOW_PROTO_TOS_TCPF))
+
+static void
+fill_netflow_v1_proto_tos_tcp (u_int8_t * pkt, u_int8_t proto, u_int8_t tos,
+                               u_int8_t tcpf) {
+  struct NF1_FLOW_PROTO_TOS_TCPF *flw =
+    (struct NF1_FLOW_PROTO_TOS_TCPF *) pkt;
+  memset (pkt, 0, sizeof (struct NF1_FLOW_PROTO_TOS_TCPF));
+  flw->protocol = proto;
+  flw->tos = tos;
+  flw->tcp_flags = tcpf;
+}
 
 /*
  * 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_int16_t ifidx,
-    u_int64_t *flows_exported, struct timeval *system_boot_time,
-    int verbose_flag, struct OPTION *option)
-{
-	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);
+static int
+send_netflow_v5_v1 (struct SENDPARAMETER sp, u_int16_t version) {
+  struct FLOW **flows = sp.flows;
+  int num_flows = sp.num_flows;
+  u_int16_t ifidx = sp.ifidx;
+  struct FLOWTRACKPARAMETERS *param = sp.param;
+  int verbose_flag = sp.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;
+  struct timeval *system_boot_time = &param->system_boot_time;
+  u_int64_t *flows_exported = &param->flows_exported;
+  struct OPTION *option = &param->option;
+
+  if (version != 5 && version != 1)
+    return (-1);
 
-	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);
-			if (option->sample > 0) {
-				hdr->sampling_interval =
-					htons(0x01 << 14 | option->sample & 0x3FFF);
-			}
-			/* Other fields are left zero */
-			offset = sizeof(*hdr);
-		}		
-		flw = (struct NF5_FLOW *)(packet + offset);
-		flw->if_index_in = flw->if_index_out = htons(ifidx);
+  if (param->adjust_time)
+    now = param->last_packet_time;
+  else
+    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);
+      param->records_sent += hdr->flows;
+      hdr->flows = htons (hdr->flows);
+      if (send_multi_destinations
+          (sp.target->num_destinations, sp.target->destinations,
+           sp.target->is_loadbalance, packet, offset) < 0)
+        return (-1);
+      *flows_exported += j;
+      j = 0;
+      num_packets++;
+    }
+    if (j == 0) {
+      memset (&packet, '\0', sizeof (packet));
+      hdr->version = htons (version);
+      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);
+      if (option->sample > 0) {
+        hdr->sampling_interval =
+          htons ((0x01 << 14) | (option->sample & 0x3FFF));
+      }
+      /* Other fields are left zero */
+      offset = sizeof (*hdr);
+      if (version == 1)
+        offset = NF1_HEADER_SIZE;
+    }
+    flw = (struct NF5_FLOW *) (packet + offset);
+    flw->if_index_in = flw->if_index_out = htons (ifidx);
 
-		/* NetFlow v.5 doesn't do IPv6 */
-		if (flows[i]->af != AF_INET)
-			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++;
-		}
+    /* 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;
+      flw->tos = flows[i]->tos[0];
+      if (version == 1) {
+        fill_netflow_v1_proto_tos_tcp (packet + offset +
+                                       NF5_NF1_FLOW_COMMON_SIZE,
+                                       flows[i]->protocol, flows[i]->tos[0],
+                                       flows[i]->tcp_flags[0]);
+      }
+      offset += sizeof (*flw);
+      j++;
+      hdr->flows++;
+    }
 
-		flw = (struct NF5_FLOW *)(packet + offset);
-		flw->if_index_in = flw->if_index_out = htons(ifidx);
+    flw = (struct NF5_FLOW *) (packet + offset);
+    flw->if_index_in = flw->if_index_out = htons (ifidx);
 
-		if (flows[i]->octets[1] > 0) {
-			flw->src_ip = flows[i]->addr[1].v4.s_addr;
-			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++;
-		}
-	}
+    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;
+      flw->tos = flows[i]->tos[1];
+      if (version == 1) {
+        fill_netflow_v1_proto_tos_tcp (packet + offset +
+                                       NF5_NF1_FLOW_COMMON_SIZE,
+                                       flows[i]->protocol, flows[i]->tos[1],
+                                       flows[i]->tcp_flags[1]);
+      }
+      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++;
-	}
+  /* Send any leftovers */
+  if (j != 0) {
+    if (verbose_flag)
+      logit (LOG_DEBUG, "Sending v5 flow packet len = %d", offset);
+    param->records_sent += hdr->flows;
+    hdr->flows = htons (hdr->flows);
+    if (send_multi_destinations
+        (sp.target->num_destinations, sp.target->destinations,
+         sp.target->is_loadbalance, packet, offset) < 0)
+      return (-1);
+    num_packets++;
+  }
 
-	*flows_exported += j;
-	return (num_packets);
+  *flows_exported += j;
+  param->packets_sent += num_packets;
+#ifdef ENABLE_PTHREAD
+  if (use_thread)
+    free (sp.flows);
+#endif /* ENABLE_PTHREAD */
+  return (num_packets);
 }
 
+int
+send_netflow_v5 (struct SENDPARAMETER sp) {
+  return send_netflow_v5_v1 (sp, 5);
+}
+
+#ifndef ENABLE_LEGACY
+int
+send_netflow_v1 (struct SENDPARAMETER sp) {
+  return send_netflow_v5_v1 (sp, 1);
+}
+#endif /* ENABLE_LEGACY */

+ 362 - 333
netflow9.c

@@ -26,39 +26,34 @@
 #include "log.h"
 #include "treetype.h"
 #include "softflowd.h"
+#include "netflow9.h"
 
 /* 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;
+  u_int16_t flowset_id, length;
 } __packed;
 struct NF9_TEMPLATE_FLOWSET_HEADER {
-	struct NF9_FLOWSET_HEADER_COMMON c;
-	u_int16_t template_id, count;
+  struct NF9_FLOWSET_HEADER_COMMON c;
+  u_int16_t template_id, count;
 } __packed;
 struct NF9_OPTION_TEMPLATE_FLOWSET_HEADER {
-	struct NF9_FLOWSET_HEADER_COMMON c;
-	u_int16_t template_id, scope_length, option_length;
+  struct NF9_FLOWSET_HEADER_COMMON c;
+  u_int16_t template_id, scope_length, option_length;
 } __packed;
 struct NF9_TEMPLATE_FLOWSET_RECORD {
-	u_int16_t type, length;
+  u_int16_t type, length;
 } __packed;
 struct NF9_DATA_FLOWSET_HEADER {
-	struct NF9_FLOWSET_HEADER_COMMON c;
+  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_PROTOCOL			4
+#define NF9_TOS				5
 /* ... */
 #define NF9_TCP_FLAGS			6
 #define NF9_L4_SRC_PORT			7
@@ -76,53 +71,57 @@ struct NF9_DATA_FLOWSET_HEADER {
 #define NF9_IPV6_SRC_ADDR		27
 #define NF9_IPV6_DST_ADDR		28
 /* ... */
-#define NF9_SAMPLING_INTERVAL           34
-#define NF9_SAMPLING_ALGORITHM          35
+#define NF9_ICMP_TYPE		        32
+/* ... */
+#define NF9_SRC_VLAN                    58
 /* ... */
 #define NF9_IP_PROTOCOL_VERSION		60
 
 /* Stuff pertaining to the templates that softflowd uses */
-#define NF9_SOFTFLOWD_TEMPLATE_NRECORDS	13
+#define NF9_SOFTFLOWD_TEMPLATE_NRECORDS	16
 struct NF9_SOFTFLOWD_TEMPLATE {
-	struct NF9_TEMPLATE_FLOWSET_HEADER h;
-	struct NF9_TEMPLATE_FLOWSET_RECORD r[NF9_SOFTFLOWD_TEMPLATE_NRECORDS];
+  struct NF9_TEMPLATE_FLOWSET_HEADER h;
+  struct NF9_TEMPLATE_FLOWSET_RECORD r[NF9_SOFTFLOWD_TEMPLATE_NRECORDS];
 } __packed;
 
 #define NF9_SOFTFLOWD_OPTION_TEMPLATE_SCOPE_RECORDS	1
 #define NF9_SOFTFLOWD_OPTION_TEMPLATE_NRECORDS	2
 struct NF9_SOFTFLOWD_OPTION_TEMPLATE {
-	struct NF9_OPTION_TEMPLATE_FLOWSET_HEADER h;
-	struct NF9_TEMPLATE_FLOWSET_RECORD s[NF9_SOFTFLOWD_OPTION_TEMPLATE_SCOPE_RECORDS];
-	struct NF9_TEMPLATE_FLOWSET_RECORD r[NF9_SOFTFLOWD_OPTION_TEMPLATE_NRECORDS];
+  struct NF9_OPTION_TEMPLATE_FLOWSET_HEADER h;
+  struct NF9_TEMPLATE_FLOWSET_RECORD
+    s[NF9_SOFTFLOWD_OPTION_TEMPLATE_SCOPE_RECORDS];
+  struct NF9_TEMPLATE_FLOWSET_RECORD
+    r[NF9_SOFTFLOWD_OPTION_TEMPLATE_NRECORDS];
 } __packed;
 
 /* softflowd data flowset types */
 struct NF9_SOFTFLOWD_DATA_COMMON {
-	u_int32_t last_switched, first_switched;
-	u_int32_t bytes, packets;
-	u_int32_t if_index_in, if_index_out;
-	u_int16_t src_port, dst_port;
-	u_int8_t protocol, tcp_flags, ipproto;
+  u_int32_t last_switched, first_switched;
+  u_int32_t bytes, packets;
+  u_int32_t if_index_in, if_index_out;
+  u_int16_t src_port, dst_port;
+  u_int8_t protocol, tcp_flags, ipproto, tos;
+  u_int16_t icmp_type, vlanid;
 } __packed;
 
 struct NF9_SOFTFLOWD_DATA_V4 {
-	u_int32_t src_addr, dst_addr;
-	struct NF9_SOFTFLOWD_DATA_COMMON c;
+  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;
+  u_int8_t src_addr[16], dst_addr[16];
+  struct NF9_SOFTFLOWD_DATA_COMMON c;
 } __packed;
 
 struct NF9_SOFTFLOWD_OPTION_DATA {
-	struct NF9_FLOWSET_HEADER_COMMON c;	
-	u_int32_t scope_ifidx;
-	u_int32_t sampling_interval;
-	u_int8_t sampling_algorithm;
-	u_int8_t padding[3];
+  struct NF9_FLOWSET_HEADER_COMMON c;
+  u_int32_t scope_ifidx;
+  u_int32_t sampling_interval;
+  u_int8_t sampling_algorithm;
+  u_int8_t padding[3];
 } __packed;
-	
+
 /* Local data: templates and counters */
 #define NF9_SOFTFLOWD_MAX_PACKET_SIZE	512
 #define NF9_SOFTFLOWD_V4_TEMPLATE_ID	1024
@@ -137,10 +136,6 @@ struct NF9_SOFTFLOWD_OPTION_DATA {
 #define NF9_OPTION_SCOPE_LINECARD  3
 #define NF9_OPTION_SCOPE_CACHE     4
 #define NF9_OPTION_SCOPE_TEMPLATE  5
-/* ... */
-#define NF9_SAMPLING_ALGORITHM_DETERMINISTIC 1
-#define NF9_SAMPLING_ALGORITHM_RANDOM        2
-/* ... */
 
 static struct NF9_SOFTFLOWD_TEMPLATE v4_template;
 static struct NF9_SOFTFLOWD_TEMPLATE v6_template;
@@ -149,310 +144,344 @@ static struct NF9_SOFTFLOWD_OPTION_DATA option_data;
 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(NF9_TEMPLATE_FLOWSET_ID);
-	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_IF_INDEX_IN);
-	v4_template.r[6].length = htons(4);
-	v4_template.r[7].type = htons(NF9_IF_INDEX_OUT);
-	v4_template.r[7].length = htons(4);
-	v4_template.r[8].type = htons(NF9_L4_SRC_PORT);
-	v4_template.r[8].length = htons(2);
-	v4_template.r[9].type = htons(NF9_L4_DST_PORT);
-	v4_template.r[9].length = htons(2);
-	v4_template.r[10].type = htons(NF9_IN_PROTOCOL);
-	v4_template.r[10].length = htons(1);
-	v4_template.r[11].type = htons(NF9_TCP_FLAGS);
-	v4_template.r[11].length = htons(1);
-	v4_template.r[12].type = htons(NF9_IP_PROTOCOL_VERSION);
-	v4_template.r[12].length = htons(1);
-
-	bzero(&v6_template, sizeof(v6_template));
-	v6_template.h.c.flowset_id = htons(NF9_TEMPLATE_FLOWSET_ID);
-	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_IF_INDEX_IN);
-	v6_template.r[6].length = htons(4);
-	v6_template.r[7].type = htons(NF9_IF_INDEX_OUT);
-	v6_template.r[7].length = htons(4);
-	v6_template.r[8].type = htons(NF9_L4_SRC_PORT);
-	v6_template.r[8].length = htons(2);
-	v6_template.r[9].type = htons(NF9_L4_DST_PORT);
-	v6_template.r[9].length = htons(2);
-	v6_template.r[10].type = htons(NF9_IN_PROTOCOL);
-	v6_template.r[10].length = htons(1);
-	v6_template.r[11].type = htons(NF9_TCP_FLAGS);
-	v6_template.r[11].length = htons(1);
-	v6_template.r[12].type = htons(NF9_IP_PROTOCOL_VERSION);
-	v6_template.r[12].length = htons(1);
+nf9_init_template (void) {
+  bzero (&v4_template, sizeof (v4_template));
+  v4_template.h.c.flowset_id = htons (NFLOW9_TEMPLATE_SET_ID);
+  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_IF_INDEX_IN);
+  v4_template.r[6].length = htons (4);
+  v4_template.r[7].type = htons (NF9_IF_INDEX_OUT);
+  v4_template.r[7].length = htons (4);
+  v4_template.r[8].type = htons (NF9_L4_SRC_PORT);
+  v4_template.r[8].length = htons (2);
+  v4_template.r[9].type = htons (NF9_L4_DST_PORT);
+  v4_template.r[9].length = htons (2);
+  v4_template.r[10].type = htons (NF9_PROTOCOL);
+  v4_template.r[10].length = htons (1);
+  v4_template.r[11].type = htons (NF9_TCP_FLAGS);
+  v4_template.r[11].length = htons (1);
+  v4_template.r[12].type = htons (NF9_IP_PROTOCOL_VERSION);
+  v4_template.r[12].length = htons (1);
+  v4_template.r[13].type = htons (NF9_TOS);
+  v4_template.r[13].length = htons (1);
+  v4_template.r[14].type = htons (NF9_ICMP_TYPE);
+  v4_template.r[14].length = htons (2);
+  v4_template.r[15].type = htons (NF9_SRC_VLAN);
+  v4_template.r[15].length = htons (2);
+  bzero (&v6_template, sizeof (v6_template));
+  v6_template.h.c.flowset_id = htons (NFLOW9_TEMPLATE_SET_ID);
+  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_IF_INDEX_IN);
+  v6_template.r[6].length = htons (4);
+  v6_template.r[7].type = htons (NF9_IF_INDEX_OUT);
+  v6_template.r[7].length = htons (4);
+  v6_template.r[8].type = htons (NF9_L4_SRC_PORT);
+  v6_template.r[8].length = htons (2);
+  v6_template.r[9].type = htons (NF9_L4_DST_PORT);
+  v6_template.r[9].length = htons (2);
+  v6_template.r[10].type = htons (NF9_PROTOCOL);
+  v6_template.r[10].length = htons (1);
+  v6_template.r[11].type = htons (NF9_TCP_FLAGS);
+  v6_template.r[11].length = htons (1);
+  v6_template.r[12].type = htons (NF9_IP_PROTOCOL_VERSION);
+  v6_template.r[12].length = htons (1);
+  v6_template.r[13].type = htons (NF9_TOS);
+  v6_template.r[13].length = htons (1);
+  v6_template.r[14].type = htons (NF9_ICMP_TYPE);
+  v6_template.r[14].length = htons (2);
+  v6_template.r[15].type = htons (NF9_SRC_VLAN);
+  v6_template.r[15].length = htons (2);
 }
 
 static void
-nf9_init_option(u_int16_t ifidx, struct OPTION *option) {
-	bzero(&option_template, sizeof(option_template));
-	option_template.h.c.flowset_id = htons(NF9_OPTIONS_FLOWSET_ID);
-	option_template.h.c.length = htons(sizeof(option_template));
-	option_template.h.template_id = htons(NF9_SOFTFLOWD_OPTION_TEMPLATE_ID);
-	option_template.h.scope_length = htons(sizeof(option_template.s));
-	option_template.h.option_length = htons(sizeof(option_template.r));
-	option_template.s[0].type = htons(NF9_OPTION_SCOPE_INTERFACE);
-	option_template.s[0].length = htons(sizeof(option_data.scope_ifidx));
-	option_template.r[0].type = htons(NF9_SAMPLING_INTERVAL);
-	option_template.r[0].length = htons(sizeof(option_data.sampling_interval));
-	option_template.r[1].type = htons(NF9_SAMPLING_ALGORITHM);
-	option_template.r[1].length = htons(sizeof(option_data.sampling_algorithm));
-
-	bzero(&option_data, sizeof(option_data));
-	option_data.c.flowset_id = htons(NF9_SOFTFLOWD_OPTION_TEMPLATE_ID);
-	option_data.c.length = htons(sizeof(option_data));
-	option_data.scope_ifidx = htonl(ifidx);
-	option_data.sampling_interval = htonl(option->sample);
-	option_data.sampling_algorithm = NF9_SAMPLING_ALGORITHM_DETERMINISTIC;
+nf9_init_option (u_int16_t ifidx, struct OPTION *option) {
+  bzero (&option_template, sizeof (option_template));
+  option_template.h.c.flowset_id = htons (NFLOW9_OPTION_TEMPLATE_SET_ID);
+  option_template.h.c.length = htons (sizeof (option_template));
+  option_template.h.template_id = htons (NF9_SOFTFLOWD_OPTION_TEMPLATE_ID);
+  option_template.h.scope_length = htons (sizeof (option_template.s));
+  option_template.h.option_length = htons (sizeof (option_template.r));
+  option_template.s[0].type = htons (NF9_OPTION_SCOPE_INTERFACE);
+  option_template.s[0].length = htons (sizeof (option_data.scope_ifidx));
+  option_template.r[0].type = htons (NFLOW9_SAMPLING_INTERVAL);
+  option_template.r[0].length =
+    htons (sizeof (option_data.sampling_interval));
+  option_template.r[1].type = htons (NFLOW9_SAMPLING_ALGORITHM);
+  option_template.r[1].length =
+    htons (sizeof (option_data.sampling_algorithm));
+
+  bzero (&option_data, sizeof (option_data));
+  option_data.c.flowset_id = htons (NF9_SOFTFLOWD_OPTION_TEMPLATE_ID);
+  option_data.c.length = htons (sizeof (option_data));
+  option_data.scope_ifidx = htonl (ifidx);
+  option_data.sampling_interval = htonl (option->sample);
+  option_data.sampling_algorithm = NFLOW9_SAMPLING_ALGORITHM_DETERMINISTIC;
 }
+
 static int
-nf_flow_to_flowset(const struct FLOW *flow, u_char *packet, u_int len,
-    u_int16_t ifidx, 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]->if_index_in = dc[0]->if_index_out = htonl(ifidx);
-	dc[1]->if_index_in = dc[1]->if_index_out = htonl(ifidx);
-	dc[0]->src_port = dc[1]->dst_port = flow->port[0];
-	dc[1]->src_port = dc[0]->dst_port = flow->port[1];
-	dc[0]->protocol = dc[1]->protocol = flow->protocol;
-	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);
+nf_flow_to_flowset (const struct FLOW *flow, u_char * packet, u_int len,
+                    u_int16_t ifidx, 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]->if_index_in = dc[0]->if_index_out = htonl (ifidx);
+  dc[1]->if_index_in = dc[1]->if_index_out = htonl (ifidx);
+  dc[0]->src_port = dc[1]->dst_port = flow->port[0];
+  dc[1]->src_port = dc[0]->dst_port = flow->port[1];
+  dc[0]->protocol = dc[1]->protocol = flow->protocol;
+  dc[0]->tcp_flags = flow->tcp_flags[0];
+  dc[1]->tcp_flags = flow->tcp_flags[1];
+  dc[0]->tos = flow->tos[0];
+  dc[1]->tos = flow->tos[1];
+  if (flow->protocol == IPPROTO_ICMP || flow->protocol == IPPROTO_ICMPV6) {
+    dc[0]->icmp_type = dc[0]->dst_port;
+    dc[1]->icmp_type = dc[1]->dst_port;
+  }
+  dc[0]->vlanid = dc[1]->vlanid = htons (flow->vlanid[0]);
+  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
  */
+#ifdef ENABLE_LEGACY
 int
-send_netflow_v9(struct FLOW **flows, int num_flows, int nfsock, u_int16_t ifidx,
-    u_int64_t *flows_exported, struct timeval *system_boot_time,
-    int verbose_flag, struct OPTION *option)
-{
-	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;
-		if (option != NULL && option->sample > 1){
-			nf9_init_option(ifidx, option);
-		}
-	}		
-
-	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);
-			if (option != NULL && option->sample > 1){
-				memcpy(packet + offset, &option_template,
-				       sizeof(option_template));
-				offset += sizeof(option_template);
-				memcpy(packet + offset, &option_data,
-				       sizeof(option_data));
-				offset += sizeof(option_data);
-			}
-
-			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, ifidx, 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);
+send_netflow_v9 (struct SENDPARAMETER sp) {
+  struct FLOW **flows = sp.flows;
+  int num_flows = sp.num_flows;
+  u_int16_t ifidx = sp.ifidx;
+  struct FLOWTRACKPARAMETERS *param = sp.param;
+  int verbose_flag = sp.verbose_flag;
+  struct NFLOW9_HEADER *nf9;
+  struct NF9_DATA_FLOWSET_HEADER *dh;
+  struct timeval now;
+  u_int offset, last_af, i, j, num_packets, inc, last_valid;
+  int r;
+  u_char packet[NF9_SOFTFLOWD_MAX_PACKET_SIZE];
+  struct timeval *system_boot_time = &param->system_boot_time;
+  u_int64_t *flows_exported = &param->flows_exported;
+  u_int64_t *packets_sent = &param->packets_sent;
+  struct OPTION *option = &param->option;
+
+  if (param->adjust_time)
+    now = param->last_packet_time;
+  else
+    gettimeofday (&now, NULL);
+
+  if (nf9_pkts_until_template == -1) {
+    nf9_init_template ();
+    nf9_pkts_until_template = 0;
+    if (option != NULL && option->sample > 1) {
+      nf9_init_option (ifidx, option);
+    }
+  }
+
+  last_valid = num_packets = 0;
+  for (j = 0; j < num_flows;) {
+    bzero (packet, sizeof (packet));
+    nf9 = (struct NFLOW9_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->export_time = htonl (time (NULL));
+    nf9->od_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);
+      nf9->flows++;
+      memcpy (packet + offset, &v6_template, sizeof (v6_template));
+      offset += sizeof (v6_template);
+      nf9->flows++;
+      if (option != NULL && option->sample > 1) {
+        memcpy (packet + offset, &option_template, sizeof (option_template));
+        offset += sizeof (option_template);
+        nf9->flows++;
+        memcpy (packet + offset, &option_data, sizeof (option_data));
+        offset += sizeof (option_data);
+        nf9->flows++;
+      }
+
+      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, ifidx,
+                              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);
+    }
+    param->records_sent += nf9->flows;
+    nf9->flows = htons (nf9->flows);
+    nf9->sequence = htonl ((u_int32_t)
+                           ((*packets_sent + num_packets +
+                             1) & 0x00000000ffffffff));
+
+    if (verbose_flag)
+      logit (LOG_DEBUG, "Sending flow packet len = %d", offset);
+    if (send_multi_destinations
+        (sp.target->num_destinations, sp.target->destinations,
+         sp.target->is_loadbalance, packet, offset) < 0)
+      return (-1);
+    num_packets++;
+    nf9_pkts_until_template--;
+
+    j += i;
+  }
+
+  *flows_exported += j;
+  param->packets_sent += num_packets;
+#ifdef ENABLE_PTHREAD
+  if (use_thread)
+    free (sp.flows);
+#endif /* ENABLE_PTHREAD */
+  return (num_packets);
 }
+#endif /* ENABLE_LEGACY */
 
 void
-netflow9_resend_template(void)
-{
-	if (nf9_pkts_until_template > 0)
-		nf9_pkts_until_template = 0;
+netflow9_resend_template (void) {
+  if (nf9_pkts_until_template > 0)
+    nf9_pkts_until_template = 0;
 }

+ 54 - 0
netflow9.h

@@ -0,0 +1,54 @@
+/*
+ * Copyright 2019 Hitoshi Irino <irino@sfc.wide.ad.jp> 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 _NETFLOW9_H
+#define _NETFLOW9_H
+
+#include "softflowd.h"
+
+#define NFLOW9_TEMPLATE_SET_ID          0
+#define NFLOW9_OPTION_TEMPLATE_SET_ID   1
+
+/* Information Elements */
+#define NFLOW9_SAMPLING_INTERVAL        34
+#define NFLOW9_SAMPLING_ALGORITHM       35
+
+#define NFLOW9_OPTION_SCOPE_INTERFACE           2
+#define NFLOW9_SAMPLING_ALGORITHM_DETERMINISTIC 1
+
+struct NFLOW9_HEADER {
+  u_int16_t version, flows;
+  u_int32_t uptime_ms;
+  u_int32_t export_time;        // in seconds
+  u_int32_t sequence, od_id;
+} __packed;
+
+#ifdef ENABLE_LEGACY
+/* Prototypes for functions to send NetFlow packets, from netflow*.c */
+int send_netflow_v9 (struct SENDPARAMETER sp);
+/* Force a resend of the flow template */
+void netflow9_resend_template (void);
+#endif /* ENABLE_LEGACY */
+
+#endif /* _NETFLOW9_H */

+ 232 - 0
ntopng.c

@@ -0,0 +1,232 @@
+/*
+ * Copyright 2018 Alastair D'Silva <alastair@d-silva.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"
+#include "log.h"
+#include "treetype.h"
+#include "softflowd.h"
+#include <stdbool.h>
+
+
+struct NTOPNG_MSG_HEADER {
+  char url[16];
+  u_int8_t version, source_id;
+  u_int16_t size;
+  u_int32_t msg_id;
+} __attribute__((packed));
+
+/*
+ * Connect to NTOPNG collector
+ */
+int
+connect_ntopng(const char *host, const char *port, struct ZMQ *zmq) {
+  void *context = zmq_ctx_new();
+  void *pub_socket = zmq_socket(context, ZMQ_PUB);
+  char connect_str[6 + NI_MAXHOST + 1 + NI_MAXSERV + 1];  /* "tcp://hostname:port" */
+
+  if (!context)
+    return errno;
+
+  if (!pub_socket) {
+    zmq_ctx_destroy(context);
+    return errno;
+  }
+
+  snprintf(connect_str, sizeof(connect_str), "tcp://%s:%s", host, port);
+  fprintf(stderr, "Connecting ZMQ socket '%s'\n", connect_str);
+  if (zmq_connect (pub_socket, connect_str)) {
+    zmq_close(pub_socket);
+    zmq_ctx_destroy(context);
+    return errno;
+  }
+
+  zmq->context = context;
+  zmq->socket = pub_socket;
+  return 0;
+}
+
+static int
+add_json_flow (struct SENDPARAMETER *sp, struct FLOW *flow, char *buf, size_t len)
+{
+  int size = snprintf(buf, len,
+    "{"
+      "\"7\": %d," /* src port */
+      "\"11\": %d," /* dst port */
+      "\"1\": %d," /* in octets */
+      "\"2\": %d," /* in packets */
+      "\"23\": %d," /* out octets */
+      "\"24\": %d," /* out packets */
+      "\"22\": %d," /* start timestamp */
+      "\"21\": %d," /* last timestamp */
+      "\"6\": %d," /* tcp flags */
+      "\"4\": %d", /* protocol */
+    flow->port[0],
+    flow->port[1],
+    flow->octets[1],
+    flow->packets[1],
+    flow->octets[0],
+    flow->packets[0],
+    timeval_sub_ms (&flow->flow_start, &sp->param->system_boot_time),
+    timeval_sub_ms (&flow->flow_last, &sp->param->system_boot_time),
+    flow->tcp_flags[0],
+    flow->protocol
+  );
+
+  if (size > (len - 1))
+    return size;
+
+  if (flow->af == AF_INET) {
+    char src[INET_ADDRSTRLEN];
+    char dst[INET_ADDRSTRLEN];
+
+    /* safe to ignore errors, neither error case can occur */
+    inet_ntop(AF_INET, &flow->addr[0].v4, src, sizeof(src));
+    inet_ntop(AF_INET, &flow->addr[1].v4, dst, sizeof(dst));
+
+    size += snprintf(buf + size, len - size,
+        ",\"8\":\"%s\"," /* ipv4 src addr */
+        "\"12\":\"%s\"", /* ipv4 dst addr */
+        src,
+        dst
+    );
+  } else {
+    char src[INET6_ADDRSTRLEN];
+    char dst[INET6_ADDRSTRLEN];
+
+    /* safe to ignore errors, neither error case can occur */
+    inet_ntop(AF_INET6, &flow->addr[0].v6, src, sizeof(src));
+    inet_ntop(AF_INET6, &flow->addr[1].v6, dst, sizeof(dst));
+
+    size += snprintf(buf + size, len - size,
+        ",\"27\":\"%s\"," /* ipv6 src addr */
+        "\"28\":\"%s\"", /* ipv6 dst addr */
+        src,
+        dst
+    );
+  }
+  if (size > (len - 1))
+    return size;
+
+  if (sp->param->track_level >= TRACK_FULL_VLAN) {
+    size += snprintf(buf + size, len - size,
+        ",\"58\":%d," /* vlan src */
+        "\"59\":%d", /* vlan dst */
+        flow->vlanid[0],
+        flow->vlanid[1]
+    );
+
+    if (size > (len - 1))
+      return size;
+  }
+
+  if (sp->param->track_level >= TRACK_FULL_VLAN_ETHER) {
+    size += snprintf(buf + size, len - size,
+        ",\"56\":\"%d:%d:%d:%d:%d:%d\"," /* ether mac src */
+        "\"57\":\"%d:%d:%d:%d:%d:%d\"", /* ether mac dst */
+        flow->ethermac[0][0],flow->ethermac[0][1],flow->ethermac[0][2],
+        flow->ethermac[0][3],flow->ethermac[0][4],flow->ethermac[0][5],
+        flow->ethermac[1][0],flow->ethermac[1][1],flow->ethermac[1][2],
+        flow->ethermac[1][3],flow->ethermac[1][4],flow->ethermac[1][5]
+    );
+  }
+
+  if (size > (len - 1))
+    return size;
+
+  size += snprintf(buf + size, len - size, "}");
+
+  return size;
+}
+
+#define MAX_JSON_SIZE 7168
+
+int
+send_ntopng_message (struct SENDPARAMETER *sp, int start_at_flow) {
+  struct NTOPNG_MSG_HEADER header;
+  static uint32_t msg_id = 0;
+  char json[MAX_JSON_SIZE];
+  int json_used = 0;
+  int flow = start_at_flow;
+  bool first = true;
+  int target = 0;
+
+  header.url[0] = 'f';
+  header.url[1] = 'l';
+  header.url[2] = 'o';
+  header.url[3] = 'w';
+  memset(header.url + 4, 0, sizeof(header.url) - 4);
+
+  header.version = 2;
+  header.msg_id = htonl(msg_id++);
+
+  json_used += snprintf(json + json_used, MAX_JSON_SIZE - json_used, "[");
+
+  while (flow < sp->num_flows) {
+    int size = 0;
+    if (first) {
+      first = false;
+    } else {
+      json_used += snprintf (json + json_used, MAX_JSON_SIZE - json_used, ",\n");
+    }
+
+    size = add_json_flow (sp, sp->flows[flow], json + json_used, MAX_JSON_SIZE - json_used);
+    if (size > (MAX_JSON_SIZE - json_used - 2 -2)) { /* space for "]\0" and next ",\n"*/
+      break;
+    }
+
+    json_used += size;
+    flow++;
+  }
+  json_used += snprintf (json + json_used, MAX_JSON_SIZE - json_used, "]");
+
+  header.size = htons(json_used);
+
+  for (target = 0; target < sp->target->num_destinations; target++) {
+    zmq_send(sp->target->destinations[target].zmq.socket, &header, sizeof(header), ZMQ_SNDMORE);
+    zmq_send(sp->target->destinations[target].zmq.socket, json, json_used, 0);
+  }
+
+  return flow;
+}
+
+int
+send_ntopng(struct SENDPARAMETER sp) {
+  int flow = 0;
+  int packets = 0;
+
+  while (flow < sp.num_flows) {
+    flow = send_ntopng_message(&sp, flow);
+    packets++;
+  }
+
+  sp.param->records_sent += flow;
+  sp.param->packets_sent += packets;
+
+#ifdef ENABLE_PTHREAD
+  if (use_thread)
+    free (sp.flows);
+#endif /* ENABLE_PTHREAD */
+
+  return packets;
+}

+ 171 - 0
psamp.c

@@ -0,0 +1,171 @@
+/*
+ * Copyright 2019 Hitoshi Irino <irino@sfc.wide.ad.jp> 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 "log.h"
+#include "ipfix.h"
+#include "psamp.h"
+#include <pcap.h>
+
+#define PSAMP_SOFTFLOWD_TEMPLATE_ID       3072
+#define PSAMP_SOFTFLOWD_TEMPLATE_NRECORDS 4
+
+#define PSAMP_DATALINKFRAME_SIZE IPFIX_SOFTFLOWD_MAX_PACKET_SIZE - \
+  sizeof(struct IPFIX_HEADER) - sizeof(struct IPFIX_SET_HEADER) - 8 - 8 -2
+
+const struct IPFIX_FIELD_SPECIFIER field_psamp[] = {
+  {PSAMP_selectionSequenceId, 8},
+  {PSAMP_observationTimeMicroseconds, 8},
+  {PSAMP_sectionExportedOctets, 2},
+  {PSAMP_dataLinkFrameSection, PSAMP_DATALINKFRAME_SIZE}
+};
+
+struct PSAMP_SOFTFLOWD_TEMPLATE {
+  struct IPFIX_TEMPLATE_SET_HEADER h;
+  struct IPFIX_FIELD_SPECIFIER r[PSAMP_SOFTFLOWD_TEMPLATE_NRECORDS];
+} __packed;
+struct PSAMP_SOFTFLOWD_TEMPLATE template;
+static int psamp_pkts_until_template = -1;
+
+static void
+psamp_init_template (struct PSAMP_SOFTFLOWD_TEMPLATE *template_p) {
+  u_int index = 0;
+  bzero (template_p, sizeof (*template_p));
+  template_p->h.c.set_id = htons (IPFIX_TEMPLATE_SET_ID);
+  template_p->h.c.length = htons (sizeof (struct PSAMP_SOFTFLOWD_TEMPLATE));
+  template_p->h.r.template_id = htons (PSAMP_SOFTFLOWD_TEMPLATE_ID);
+  template_p->h.r.count = htons (PSAMP_SOFTFLOWD_TEMPLATE_NRECORDS);
+  ipfix_init_fields (template_p->r, &index, field_psamp,
+                     PSAMP_SOFTFLOWD_TEMPLATE_NRECORDS);
+}
+
+int
+send_psamp (const u_char * pkt, int caplen, struct timeval tv,
+            struct NETFLOW_TARGET *target, uint64_t total_packets) {
+  u_char packet[IPFIX_SOFTFLOWD_MAX_PACKET_SIZE];
+  struct IPFIX_HEADER *ipfix = (struct IPFIX_HEADER *) packet;
+  struct IPFIX_SET_HEADER *dh;
+  u_int64_t *sequenceId;
+  struct ntp_time_t *ntptime;
+  u_int16_t *exportedOctets;
+  int offset = sizeof (struct IPFIX_HEADER);
+  int copysize =
+    caplen < PSAMP_DATALINKFRAME_SIZE ? caplen : PSAMP_DATALINKFRAME_SIZE;
+
+  ipfix->version = htons (NF_VERSION_IPFIX);    // PSAMP uses IPFIX
+  ipfix->export_time = htonl (tv.tv_sec);
+  ipfix->sequence = htonl ((u_int32_t) (total_packets & 0x00000000ffffffff));
+  ipfix->od_id = 0;
+
+  if (psamp_pkts_until_template == -1) {
+    psamp_init_template (&template);
+    psamp_pkts_until_template = 0;
+    memcpy (&packet[offset], &template, sizeof (template));
+    ipfix->length = htons (offset + sizeof (template));
+    if (send_multi_destinations
+        (target->num_destinations, target->destinations, 0, packet,
+         offset + sizeof (template)) < 0)
+      return (-1);
+  }
+
+  dh = (struct IPFIX_SET_HEADER *) &packet[offset];
+  dh->set_id = htons (PSAMP_SOFTFLOWD_TEMPLATE_ID);
+  dh->length =
+    htons (IPFIX_SOFTFLOWD_MAX_PACKET_SIZE - sizeof (struct IPFIX_HEADER));
+  offset += sizeof (struct IPFIX_SET_HEADER);
+
+  sequenceId = (u_int64_t *) & packet[offset];
+  *sequenceId = htobe64 (total_packets);
+  offset += sizeof (u_int64_t);
+
+  ntptime = (struct ntp_time_t *) &packet[offset];
+  conv_unix_to_ntp (tv, ntptime);
+  ntptime->second = htonl (ntptime->second);
+  ntptime->fraction = htonl (ntptime->fraction);
+  offset += sizeof (struct ntp_time_t);
+
+  exportedOctets = (u_int16_t *) & packet[offset];
+  *exportedOctets = htons (copysize);
+  offset += sizeof (u_int16_t);
+
+  memset (&packet[offset], 0, IPFIX_SOFTFLOWD_MAX_PACKET_SIZE - offset);
+  memcpy (&packet[offset], pkt, copysize);
+  ipfix->length = htons (IPFIX_SOFTFLOWD_MAX_PACKET_SIZE);
+  if (send_multi_destinations (target->num_destinations, target->destinations,
+                               target->is_loadbalance,
+                               packet, IPFIX_SOFTFLOWD_MAX_PACKET_SIZE) < 0)
+    return (-1);
+  return 1;
+}
+
+// This function only process softflowd orignate psamp data record.
+int
+recv_psamp (int rsock, struct CB_CTXT *cb_ctxt) {
+  char buf[IPFIX_SOFTFLOWD_MAX_PACKET_SIZE];
+  struct IPFIX_HEADER *ipfix = (struct IPFIX_HEADER *) buf;
+  struct ntp_time_t ntptime;
+  struct pcap_pkthdr pkthdr;
+  int recvsize = 0;
+  int offset = 0;
+  memset (buf, 0, sizeof (buf));
+  recvsize = recv (rsock, buf, sizeof (buf), 0);
+  if (recvsize < 0) {
+    perror ("recv");
+    return -1;
+  } else if (recvsize <
+             (sizeof (struct IPFIX_HEADER) +
+              sizeof (struct IPFIX_SET_HEADER) + sizeof (uint64_t) +
+              sizeof (struct ntp_time_t) + sizeof (uint16_t))) {
+    logit (LOG_DEBUG, "recv_psamp: recvsize: %d is shorter than needed size",
+           recvsize);
+    return -1;
+  }
+  offset = sizeof (struct IPFIX_HEADER);
+  struct IPFIX_SET_HEADER *sh = (struct IPFIX_SET_HEADER *) &buf[offset];
+  offset += sizeof (struct IPFIX_SET_HEADER);
+  if (ntohs (ipfix->version) != NF_VERSION_IPFIX)
+    return -1;
+  if (ntohs (sh->set_id) != PSAMP_SOFTFLOWD_TEMPLATE_ID)
+    return 0;                   // This function only process softflowd orignate psamp data record.
+
+  // disrgard selectionSequenceId
+  offset += sizeof (uint64_t);
+
+  // observationTimeMicroseconds
+  ntptime = *((struct ntp_time_t *) &buf[offset]);
+  offset += sizeof (struct ntp_time_t);
+  ntptime.second = ntohl (ntptime.second);
+  ntptime.fraction = ntohl (ntptime.fraction);
+  pkthdr.ts = conv_ntp_to_unix (ntptime);
+
+  //sectionExportedOctets
+  pkthdr.caplen = pkthdr.len = ntohs (*((uint16_t *) & buf[offset]));
+  offset += sizeof (uint16_t);
+  pkthdr.caplen =
+    pkthdr.caplen <
+    sizeof (buf) - offset ? pkthdr.caplen : sizeof (buf) - offset;
+
+  flow_cb ((u_char *) cb_ctxt, &pkthdr, (const u_char *) &buf[offset]);
+  return 1;
+}

+ 46 - 0
psamp.h

@@ -0,0 +1,46 @@
+/*
+ * Copyright 2019 Hitoshi Irino <irino@sfc.wide.ad.jp> 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 _PSAMP_H
+#define _PSAMP_H
+
+#include "common.h"
+#include "softflowd.h"
+
+#define PSAMP_selectionSequenceId         301
+#define PSAMP_selectorAlgorithm           304
+#define PSAMP_samplingPacketInterval      305
+#define PSAMP_samplingPacketSpace         306
+#define PSAMP_digestHashValue             326
+#define PSAMP_observationTimeMicroseconds 324
+#define PSAMP_dataLinkFrameSection        315
+#define PSAMP_sectionExportedOctets       410
+
+#define PSAMP_selectorAlgorithm_count   1
+
+
+int send_psamp (const u_char * pkt, int caplen, struct timeval tv,
+                struct NETFLOW_TARGET *target, uint64_t total_packets);
+int recv_psamp (int rsock, struct CB_CTXT *cb_ctxt);
+#endif /* _PSAMP_H */

+ 4 - 1
softflowctl.8

@@ -91,8 +91,11 @@ Has no effect for other flow export versions.
 .El
 .Sh BUGS
 All times are unconditionally displayed in UTC, regardless of the system
-timezone.
+timezone. 
+Please report bugs in softflowctl to 
+https://github.com/irino/softflowd/issues
 .Sh AUTHORS
 .An Damien Miller Aq djm@mindrot.org
+.An Hitoshi Irino (current maintainer) Aq irino@sfc.wide.ad.jp 
 .Sh SEE ALSO
 .Xr softflowd 8

+ 3 - 1
softflowctl.c

@@ -36,7 +36,9 @@ main(int argc, char **argv)
 	const char *ctlsock_path;
 	char buf[8192], *command;
 	struct sockaddr_un ctl;
+#ifdef SOCK_HAS_LEN 
 	socklen_t ctllen;
+#endif
 	int ctlsock, ch;
 	FILE *ctlf;
 	extern char *optarg;
@@ -73,9 +75,9 @@ main(int argc, char **argv)
 	}
 	ctl.sun_path[sizeof(ctl.sun_path) - 1] = '\0';
 	ctl.sun_family = AF_UNIX;
+#ifdef SOCK_HAS_LEN 
 	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) {

BIN
softflowctl.pdf


+ 52 - 6
softflowd.8

@@ -21,7 +21,7 @@
 .\" (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
+.Dd July 15, 2019
 .Dt SOFTFLOWD 8
 .Os
 .Sh NAME
@@ -29,7 +29,7 @@
 .Nd Traffic flow monitoring
 .Sh SYNOPSIS
 .Nm softflowd
-.Op Fl 6dDh
+.Op Fl 6dDhbal
 .Op Fl L Ar hoplimit
 .Op Fl T Ar track_level
 .Op Fl c Ar ctl_sock
@@ -47,7 +47,11 @@
 .Op Fl r Ar pcap_file
 .Op Fl t Ar timeout_name=seconds
 .Op Fl v Ar netflow_version
+.Op Fl P Ar transport_protocol
+.Op Fl A Ar time_format
 .Op Fl s Ar sampling_rate
+.Op Fl C Ar capture_length
+.Op Fl R Ar receive_port
 .Op bpf_expression
 .Sh DESCRIPTION
 .Nm
@@ -62,7 +66,7 @@ The intended use of
 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.
+supports data export using versions 1, 5, 9 or 10 (a.k.a. IPFIX) of the NetFlow protocol.
 .Nm
 can also run in statistics-only mode, where it just collects summary
 information.
@@ -116,11 +120,12 @@ and
 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
+Numeric IPv6 addresses should be enclosed 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.
+Comma can be used for specifying multiple destinations.
 .It Fl i Xo
 .Sm off
 .Oo Ar if_ndx : Oc
@@ -200,6 +205,12 @@ This implies the
 and
 .Fl 6
 flags and turns on additional debugging output.
+.It Fl b
+Bidirectional mode in IPFIX (-b work with -v 10)
+.It Fl a
+Adjusting time for reading pcap file (-a work with -r)
+.It Fl l
+Load balancing mode for multiple destinations which are specified with -n
 .It Fl h
 Display command-line usage information.
 .It Fl L Ar hoplimit
@@ -215,8 +226,12 @@ Specify which flow elements
 should be used to define a flow.
 .Ar track_level
 may be one of:
+.Dq ether
+(track everything including source and destination addresses, source and destination port, source and destination ethernet address, vlanid and protocol),
+.Dq vlan
+(track source and destination addresses, source and destination port, vlanid and protocol),
 .Dq full
-(track everything in the flow, the default),
+(track source and destination addresses, source and destination port and protocol in the flow, the default),
 .Dq proto
 (track source and destination addresses and protocol), or
 .Dq ip
@@ -231,10 +246,20 @@ will place on the system at the cost of some detail being lost.
 Specify which version of the NetFlow(tm) protocol
 .Nm
 should use for export of the flow data.
-Supported versions are 1, 5 and 9.
+Supported versions are 1, 5, 9, 10(IPFIX), and psamp.
 Default is version 5.
+.It Fl P Ar transport_protocol
+Specify transport layer protocol for exporting packets.
+Supported transport layer protocols are udp, tcp, and sctp.
+.It Fl A Ar time_format
+Specify absolute time format form exporting records.
+Supported time formats are sec, milli, micro, and nano.
 .It Fl s Ar sampling_rate
 Specify periodical sampling rate (denominator).
+.It Fl C Ar capture_length
+Specify length for packet capture (snaplen).
+.It Fl R Ar receive_port
+Specify port number for PSAMP receive mode.
 .El
 .Pp
 Any further command-line arguments will be concatenated together and
@@ -365,6 +390,20 @@ This command-line will cause
 .Nm
 to listen on interface fxp0 and to export NetFlow v.5 datagrams on flow
 expiry to a flow collector running on 10.1.0.2 port 4432.
+.It softflowd -i fxp0 -n 10.1.0.2:4432,10.1.0.3:4432
+This command-line will cause
+.Nm
+to listen on interface fxp0 and to export NetFlow v.5 datagrams on flow
+expiry to a flow collector running on 10.1.0.2 port 4432 and 10.1.0.3
+port 4432.
+.It softflowd -i fxp0 -l -n 10.1.0.2:4432,10.1.0.3:4432
+This command-line will cause
+.Nm
+to listen on interface fxp0 and to export NetFlow v.5 datagrams on flow
+expiry to a flow collector running on 10.1.0.2 port 4432 and 10.1.0.3
+port 4432 with load balncing mode. Odd netflow packets will be sent to
+10.1.0.2 port 4432 and even netflow packets will be sent to 10.1.0.3
+port 4432.
 .It softflowd -v 5 -i fxp0 -n 10.1.0.2:4432 -m 65536 -t udp=1m30s
 This command-line increases the number of concurrent flows that
 .Nm
@@ -408,8 +447,11 @@ 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.
+Please report bugs in softflowd 
+to https://github.com/irino/softflowd/issues
 .Sh AUTHORS
 .An Damien Miller Aq djm@mindrot.org
+.An Hitoshi Irino (current maintainer) Aq irino@sfc.wide.ad.jp 
 .Sh SEE ALSO
 .Xr softflowctl 8 ,
 .Xr tcpdump 8 ,
@@ -419,4 +461,8 @@ fragmented packets, but will not record TCP or UDP port information.
 http://www.ietf.org/rfc/rfc3954.txt
 .br
 http://www.cisco.com/en/US/products/sw/netmgtsw/ps1964/products_implementation_design_guide09186a00800d6a11.html
+http://www.ietf.org/rfc/rfc5101.txt
+.br
+http://www.ietf.org/rfc/rfc5103.txt
+.br
 .Ed

File diff suppressed because it is too large
+ 2090 - 1708
softflowd.c


+ 187 - 112
softflowd.h

@@ -24,19 +24,34 @@
 
 #ifndef _SOFTFLOWD_H
 #define _SOFTFLOWD_H
-
 #include "common.h"
 #include "sys-tree.h"
 #include "freelist.h"
 #include "treetype.h"
+#include <pcap.h>
+#ifdef ENABLE_PTHREAD
+#include <pthread.h>
+extern int use_thread;
+#endif /* ENABLE_PTHREAD */
+#ifdef ENABLE_NTOPNG
+#include <zmq.h>
+// The version field in NetFow and IPFIX headers is 16 bits unsiged int.
+// If the version number is over 0x7fff0000, it is unique number in softflowd.
+#define SOFTFLOWD_NF_VERSION_NTOPNG (0x7fff0001)
+#define SOFTFLOWD_NF_VERSION_NTOPNG_STRING "ntopng"
+struct ZMQ {
+  void *context;
+  void *socket;
+};
+#endif /* ENABLE_NTOPNG */
 
 /* User to setuid to and directory to chroot to when we drop privs */
 #ifndef PRIVDROP_USER
-# define PRIVDROP_USER		"nobody"
+#define PRIVDROP_USER		"nobody"
 #endif
 
 #ifndef PRIVDROP_CHROOT_DIR
-# define PRIVDROP_CHROOT_DIR	"/var/empty"
+#define PRIVDROP_CHROOT_DIR	"/var/empty"
 #endif
 /*
  * Capture length for libpcap: Must fit the link layer header, plus 
@@ -63,22 +78,92 @@
  */
 #define DEFAULT_MAX_FLOWS	8192
 
+#define NF_VERSION_IPFIX 10
+
 /* Store a couple of statistics, maybe more in the future */
 struct STATISTIC {
-	double min, mean, max;
+  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 */
+#define TRACK_FULL		1       /* src/dst/addr/port/proto/tos 6-tuple */
+#define TRACK_IP_PROTO_PORT	2       /* src/dst/addr/port/proto 5-tuple */
+#define TRACK_IP_PROTO		3       /* src/dst/proto 3-tuple */
+#define TRACK_IP_ONLY		4       /* src/dst tuple */
+#define TRACK_FULL_VLAN		5       /* src/dst/addr/port/proto/tos/vlanid 7-tuple */
+#define TRACK_FULL_VLAN_ETHER	6       /* src/dst/addr/port/proto/tos/vlanid/src-mac/dst-mac 9-tuple */
+
+#define SOFTFLOWD_MAX_DESTINATIONS 16
 
 /*
  * This structure contains optional information carried by Option Data
  * Record.
  */
 struct OPTION {
-	uint32_t sample;
+  uint32_t sample;
+  pid_t meteringProcessId;
+};
+
+struct FLOWTRACKPARAMETERS {
+  unsigned int num_flows;       /* # of active flows */
+  unsigned int max_flows;       /* Max # of active flows */
+  u_int64_t next_flow_seq;      /* Next flow ID */
+
+  /* Stuff related to flow export */
+  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 non_sampled_packets;        /* # of not sampled 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 */
+  u_int64_t records_sent;       /* # netflow records 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;
+
+  /* Optional information */
+  struct OPTION option;
+  char time_format;
+  u_int8_t bidirection;
+  u_int8_t adjust_time;
+  u_int8_t is_psamp;
+  struct timeval last_packet_time;
 };
 /*
  * This structure is the root of the flow tracking system.
@@ -86,66 +171,14 @@ struct OPTION {
  * 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 */
-
-	struct freelist flow_freelist;		/* Freelist for flows */
-	struct freelist expiry_freelist;	/* Freelist for expiry events */
-
-	unsigned int num_flows;			/* # of active flows */
-	unsigned int max_flows;			/* Max # of active flows */
-	u_int64_t next_flow_seq;		/* Next flow ID */
-
-	/* Stuff related to flow export */
-	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 non_sampled_packets;		/* # of not sampled 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;
-
-	/* Optional information */
-	struct OPTION option;
+  /* The flows and their expiry events */
+  FLOW_HEAD (FLOWS, FLOW) flows;        /* Top of flow tree */
+  EXPIRY_HEAD (EXPIRIES, EXPIRY) expiries;      /* Top of expiries tree */
+
+  struct freelist flow_freelist;        /* Freelist for flows */
+  struct freelist expiry_freelist;      /* Freelist for expiry events */
+
+  struct FLOWTRACKPARAMETERS param;
 };
 
 /*
@@ -157,29 +190,32 @@ struct FLOWTRACK {
  * 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 */
+  /* Housekeeping */
+  struct EXPIRY *expiry;        /* Pointer to expiry record */
+    FLOW_ENTRY (FLOW) trp;      /* Tree pointer */
+
+  /* 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 */
+
+  /* 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 tos[2];              /* Tos */
+  u_int16_t vlanid[2];          /* vlanid */
+  uint8_t ethermac[2][6];
+  u_int8_t protocol;            /* Protocol */
 };
 
 /*
@@ -197,31 +233,70 @@ struct FLOW {
  * 
  */
 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;
+  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;
+};
+
+struct DESTINATION {
+  char *arg;
+  int sock;
+  struct sockaddr_storage ss;
+  socklen_t sslen;
+  char hostname[NI_MAXHOST];
+  char servname[NI_MAXSERV];
+#ifdef ENABLE_NTOPNG
+  struct ZMQ zmq;
+#endif
+};
+
+/* Describes a location where we send NetFlow packets to */
+struct NETFLOW_TARGET {
+  int num_destinations;
+  struct DESTINATION destinations[SOFTFLOWD_MAX_DESTINATIONS];
+  const struct NETFLOW_SENDER *dialect;
+  u_int8_t is_loadbalance;
+};
+
+struct SENDPARAMETER {
+  struct FLOW **flows;
+  int num_flows;
+  struct NETFLOW_TARGET *target;
+  u_int16_t ifidx;
+  struct FLOWTRACKPARAMETERS *param;
+  int verbose_flag;
+};
+
+/* Context for libpcap callback functions */
+struct CB_CTXT {
+  struct FLOWTRACK *ft;
+  struct NETFLOW_TARGET *target;
+  int linktype;
+  int fatal;
+  int want_v6;
 };
 
 /* Prototype for functions shared from softflowd.c */
-u_int32_t timeval_sub_ms(const struct timeval *t1, const struct timeval *t2);
+u_int32_t timeval_sub_ms (const struct timeval *t1, const struct timeval *t2);
+int send_multi_destinations (int num_destinations,
+                             struct DESTINATION *destinations,
+                             u_int8_t is_loadbalnce, u_int8_t * packet,
+                             int size);
+void flow_cb (u_char * user_data, const struct pcap_pkthdr *phdr,
+              const u_char * pkt);
 
 /* Prototypes for functions to send NetFlow packets, from netflow*.c */
-int send_netflow_v1(struct FLOW **flows, int num_flows, int nfsock,
-    u_int16_t ifidx, u_int64_t *flows_exported, struct timeval *system_boot_time, 
-    int verbose_flag, struct OPTION *option);
-int send_netflow_v5(struct FLOW **flows, int num_flows, int nfsock,
-    u_int16_t ifidx, u_int64_t *flows_exported, struct timeval *system_boot_time,
-    int verbose_flag, struct OPTION *option);
-int send_netflow_v9(struct FLOW **flows, int num_flows, int nfsock,
-    u_int16_t ifidx, u_int64_t *flows_exported, struct timeval *system_boot_time,
-    int verbose_flag, struct OPTION *option);
-
-/* Force a resend of the flow template */
-void netflow9_resend_template(void);
+int send_netflow_v1 (struct SENDPARAMETER sp);
+int send_netflow_v5 (struct SENDPARAMETER sp);
+#ifdef ENABLE_NTOPNG
+/* Protypes for ntopng.c */
+int connect_ntopng (const char *host, const char *port, struct ZMQ *zmq);
+int send_ntopng (struct SENDPARAMETER sp);
+#endif /* ENABLE_NTOPNG */
 
 #endif /* _SOFTFLOWD_H */

BIN
softflowd.pdf


+ 1 - 1
softflowd.spec

@@ -3,7 +3,7 @@
 
 Name: softflowd
 Summary: Network traffic analyser capable of Cisco NetFlow data export
-Version: 0.9.9
+Version: 1.0.0
 Release: 1.%{_RHTAG}
 Source: softflowd-%{version}.tar.gz
 Group: System/Utilities