Backport radsniff and additional required library code from master
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sat, 26 Apr 2014 12:33:21 +0000 (13:33 +0100)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Fri, 2 May 2014 13:57:39 +0000 (14:57 +0100)
18 files changed:
Make.inc.in
configure
configure.ac
doc/ChangeLog
src/include/event.h
src/include/libradius.h
src/include/pcap.h [new file with mode: 0644]
src/include/radsniff.h
src/lib/all.mk
src/lib/event.c
src/lib/pcap.c [new file with mode: 0644]
src/lib/radius.c
src/lib/tcp.c
src/lib/valuepair.c
src/main/collectd.c [new file with mode: 0644]
src/main/radsniff.c
src/main/radsniff.mk.in
src/main/tls_listen.c

index 1fc11fa..cee6d1d 100644 (file)
@@ -47,13 +47,12 @@ MAKEFLAGS   = @FR_MAKEFLAGS@
 
 CC             = @CC@
 RANLIB         = @RANLIB@
-
 INCLUDE                = -I${top_srcdir} -I${top_srcdir}/src \
                  -include ${top_srcdir}/src/freeradius-devel/autoconf.h \
                  -include ${top_srcdir}/src/freeradius-devel/build.h \
-                 -include ${top_srcdir}/src/freeradius-devel/features.h
-
-CFLAGS         = $(IMACROS) $(INCLUDE) -std=c99 -fno-strict-aliasing @CFLAGS@
+                 -include ${top_srcdir}/src/freeradius-devel/features.h \
+                 -include ${top_srcdir}/src/freeradius-devel/radpaths.h
+CFLAGS         = $(INCLUDE) -std=c99 -fno-strict-aliasing @CFLAGS@
 CPPFLAGS       = @CPPFLAGS@
 LIBPREFIX      = @LIBPREFIX@
 EXEEXT         = @EXEEXT@
@@ -69,16 +68,37 @@ INSTALL_SCRIPT      = ${INSTALL_PROGRAM}
 INSTALLSTRIP   = @INSTALLSTRIP@
 DARWIN_CFLAGS  = @DARWIN_CFLAGS@
 
+#
+#  Linker arguments for libraries searched for by the main
+#  configure script.
+#
+TALLOC_LIBS     = @TALLOC_LIBS@
+TALLOC_LDFLAGS  = @TALLOC_LDFLAGS@
+
+OPENSSL_LIBS    = @OPENSSL_LIBS@
+OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@
+
+PCAP_LIBS      = @PCAP_LIBS@
+PCAP_LDFLAGS    = @PCAP_LDFLAGS@
+
+COLLECTDC_LIBS = @COLLECTDC_LIBS@
+COLLECTDC_LDFLAGS = @COLLECTDC_LDFLAGS@
+
 LCRYPT         = @CRYPTLIB@
-LIBS           = @LIBS@
-LDFLAGS                = @LDFLAGS@
+
+#
+#  OpenSSL libs (if used) must be linked everywhere in order for
+#  the server to work properly on on all platforms.
+#
+LIBS           = $(OPENSSL_LIBS) $(TALLOC_LIBS) @LIBS@
+LDFLAGS                = $(OPENSSL_LDFLAGS) $(TALLOC_LDFLAGS) @LDFLAGS@
 
 LOGDIR         = ${logdir}
 RADDBDIR       = ${raddbdir}
 RUNDIR         = ${localstatedir}/run/radiusd
 SBINDIR                = ${sbindir}
 RADIR          = ${radacctdir}
-LIBRADIUS      = $(top_builddir)/src/lib/$(LIBPREFIX)freeradius-radius.la -ltalloc
+LIBRADIUS      = $(top_builddir)/src/lib/$(LIBPREFIX)freeradius-radius.la $(TALLOC_LIBS)
 
 USE_SHARED_LIBS = @USE_SHARED_LIBS@
 bm_shared_libs  = @USE_SHARED_LIBS@
@@ -86,18 +106,6 @@ USE_STATIC_LIBS = @USE_STATIC_LIBS@
 bm_static_libs  = @USE_STATIC_LIBS@
 
 STATIC_MODULES = @STATIC_MODULES@
-
-OPENSSL_LIBS   = @OPENSSL_LIBS@
-OPENSSL_INCLUDE = @OPENSSL_INCLUDE@
-
-#
-#  If the system has OpenSSL, use it's version of MD4/MD5/SHA1, instead of
-#  using ours.
-#
-ifneq "$(OPENSSL_INCLUDE)" ""
-CFLAGS         +=  -DWITH_OPENSSL_MD4 -DWITH_OPENSSL_MD5
-endif
-
 LIBREADLINE    = @LIBREADLINE@
 
 #
@@ -106,15 +114,23 @@ LIBREADLINE       = @LIBREADLINE@
 RADIUSD_VERSION_STRING = @RADIUSD_VERSION_STRING@
 
 #
-#  This allows dlopen to do runtime checks for version mistmatches
+#  This allows dlopen to do runtime checks for version mismatches
 #  between what it was originally linked with, and the library it's
 #  actually loading.
 #
-#LDFLAGS += -release=$(RADIUSD_VERSION_STRING)
-
 MODULES                = @MODULES@
 HOSTINFO       = @HOSTINFO@
 
+#
+#  If the system has OpenSSL, use it's version of MD4/MD5/SHA1, instead of
+#  using ours.
+#
+ifeq "$(WITH_OPENSSL)" "yes"
+CFLAGS         +=  -DWITH_OPENSSL_MD4 -DWITH_OPENSSL_MD5
+endif
+
+OPENSSL_LIBS   = @OPENSSL_LIBS@
+
 ifneq ($(WITH_OPENSSL_MD5),)
 LIBRADIUS_WITH_OPENSSL = 1
 CFLAGS += -DWITH_OPENSSL_MD5
index e129b9e..706e1a2 100755 (executable)
--- a/configure
+++ b/configure
@@ -637,10 +637,15 @@ REGEX_PCRE
 REGEX
 CRYPTLIB
 LIBPREFIX
+COLLECTDC_LDFLAGS
+COLLECTDC_LIBS
+PCAP_LDFLAGS
 PCAP_LIBS
+OPENSSL_LDFLAGS
 OPENSSL_LIBS
-OPENSSL_INCLUDE
 LIBREADLINE
+TALLOC_LDFLAGS
+TALLOC_LIBS
 DIRNAME
 LOCATE
 AUTOHEADER
@@ -750,15 +755,17 @@ with_shared_libs
 with_modules
 with_experimental_modules
 with_udpfromto
-with_openssl
-with_openssl_includes
-with_openssl_libraries
 with_rlm_FOO_lib_dir
 with_rlm_FOO_include_dir
-with_talloc_include_dir
+with_openssl
+with_openssl_lib_dir
+with_openssl_include_dir
 with_talloc_lib_dir
-with_pcap_include_dir
+with_talloc_include_dir
 with_pcap_lib_dir
+with_pcap_include_dir
+with_collectdclient_lib_dir
+with_collectdclient_include_dir
 with_execinfo_lib_dir
 with_execinfo_include_dir
 with_pcre_lib_dir
@@ -1409,23 +1416,46 @@ Optional Packages:
   --with-vmps             compile in VMPS support. (default=yes)
   --with-dhcp             compile in DHCP support. (default=yes)
   --with-static-modules=QUOTED-MODULE-LIST
-  --with-shared-libs               build dynamic libraries and link against them. (default=yes)
+  --with-shared-libs      build dynamic libraries and link against them.
+                          (default=yes)
   --with-modules=QUOTED-MODULE-LIST
-  --with-experimental-modules      use experimental and unstable modules. (default=no, unless --enable-developer=yes)
-  --with-udpfromto                 compile in UDPFROMTO support. (default=yes)
-  --with-openssl                   use OpenSSL. (default=yes)
-  --with-openssl-includes=DIR      directory to look for OpenSSL include files in
-  --with-openssl-libraries=DIR     directory to look for OpenSSL library files in
-  --with-rlm-FOO-lib-dir=DIR       directory to look for library files used by module FOO in
-  --with-rlm-FOO-include-dir=DIR   directory to look for include files used by module FOO in
-  --with-talloc-include-dir=DIR    directory to look for talloc include files in
-  --with-talloc-lib-dir=DIR        directory to look for talloc library files in
-  --with-pcap-include-dir=DIR      directory to look for pcap include files in
-  --with-pcap-lib-dir=DIR          directory to look for pcap library files in
-  --with-execinfo-lib-dir=DIR      directory to look for execinfo library files in
-  --with-execinfo-include-dir=DIR  directory to look for execinfo include files in
-  --with-pcre-lib-dir=DIR          directory to look for pcre library files in
-  --with-pcre-include-dir=DIR      directory to look for PCRE include files in
+  --with-experimental-modules
+                          use experimental and unstable modules. (default=no,
+                          unless --enable-developer=yes)
+  --with-udpfromto        compile in UDPFROMTO support. (default=yes)
+  --with-rlm-FOO-lib-dir=DIR
+                          directory in which to look for library files used by
+                          module FOO
+  --with-rlm-FOO-include-dir=DIR
+                          directory in which to look for include files used by
+                          module FOO
+  --with-openssl          use OpenSSL. (default=yes)
+  --with-openssl-lib-dir=DIR
+                          directory to look for OpenSSL library files
+  --with-openssl-include-dir=DIR
+                          directory to look for OpenSSL include files
+  --with-talloc-lib-dir=DIR
+                          directory in which to look for talloc library files
+  --with-talloc-include-dir=DIR
+                          directory in which to look for talloc include files
+  --with-pcap-lib-dir=DIR directory in which to look for pcap library files
+  --with-pcap-include-dir=DIR
+                          directory in which to look for pcap include files
+  --with-collectdclient-lib-dir=DIR
+                          directory in which to look for collectdclient
+                          library files
+  --with-collectdclient-include-dir=DIR
+                          directory in which to look for collectdclient
+                          include files
+  --with-execinfo-lib-dir=DIR
+                          directory in which to look for execinfo library
+                          files
+  --with-execinfo-include-dir=DIR
+                          directory in which to look for execinfo include
+                          files
+  --with-pcre-lib-dir=DIR directory in which to look for pcre library files
+  --with-pcre-include-dir=DIR
+                          directory in which to look for pcre include files
 
 Some influential environment variables:
   CC          C compiler command
@@ -1843,60 +1873,6 @@ fi
 
 } # ac_fn_c_try_link
 
-# ac_fn_c_check_type LINENO TYPE VAR INCLUDES
-# -------------------------------------------
-# Tests whether TYPE exists after having included INCLUDES, setting cache
-# variable VAR accordingly.
-ac_fn_c_check_type ()
-{
-  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
-$as_echo_n "checking for $2... " >&6; }
-if eval \${$3+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  eval "$3=no"
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-$4
-int
-main ()
-{
-if (sizeof ($2))
-        return 0;
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-$4
-int
-main ()
-{
-if (sizeof (($2)))
-           return 0;
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-
-else
-  eval "$3=yes"
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-eval ac_res=\$$3
-              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-$as_echo "$ac_res" >&6; }
-  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
-
-} # ac_fn_c_check_type
-
 # ac_fn_c_check_func LINENO FUNC VAR
 # ----------------------------------
 # Tests whether FUNC exists, setting the cache variable VAR accordingly
@@ -1964,6 +1940,60 @@ $as_echo "$ac_res" >&6; }
 
 } # ac_fn_c_check_func
 
+# ac_fn_c_check_type LINENO TYPE VAR INCLUDES
+# -------------------------------------------
+# Tests whether TYPE exists after having included INCLUDES, setting cache
+# variable VAR accordingly.
+ac_fn_c_check_type ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  eval "$3=no"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+if (sizeof ($2))
+        return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+if (sizeof (($2)))
+           return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+  eval "$3=yes"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_type
+
 # ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES
 # ---------------------------------------------
 # Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR
@@ -2569,6 +2599,8 @@ test -n "$target_alias" &&
 
 case "$host" in
   *-darwin*)
+                                LDFLAGS="-L/usr/local/lib -L/usr/lib"
+
                     { $as_echo "$as_me:${as_lineno-$LINENO}: checking if cc is apple llvm" >&5
 $as_echo_n "checking if cc is apple llvm... " >&6; }
     if ! $CC --version 2>&1 | grep -I 'Apple LLVM' > /dev/null; then
@@ -2659,7 +2691,6 @@ $as_echo "$as_me: determining OSX SDK path" >&6;}
 $as_echo "$osx_sdk_path" >&6; }
 
                                         export CFLAGS="$CFLAGS --sysroot=$osx_sdk_path"
-        export CPPFLAGS="$CPPFLAGS --sysroot=$osx_sdk_path"
         export LDFLAGS="$LDFLAGS -L$osx_sdk_path/usr/lib/"
         DARWIN_CFLAGS="--sysroot=$osx_sdk_path"
 
@@ -5444,63 +5475,63 @@ $as_echo "#define WITH_UDPFROMTO /**/" >>confdefs.h
 
 fi
 
-WITH_OPENSSL=yes
 
-# Check whether --with-openssl was given.
-if test "${with_openssl+set}" = set; then :
-  withval=$with_openssl;  case "$withval" in
-  no)
-    WITH_OPENSSL=no
-    ;;
+
+# Check whether --with-rlm-FOO-lib-dir was given.
+if test "${with_rlm_FOO_lib_dir+set}" = set; then :
+  withval=$with_rlm_FOO_lib_dir;  case "$withval" in
   *)
-    WITH_OPENSSL=yes
     ;;
   esac
 
 fi
 
 
-OPENSSL_INCLUDE_DIR=
 
-# Check whether --with-openssl-includes was given.
-if test "${with_openssl_includes+set}" = set; then :
-  withval=$with_openssl_includes;  case "$withval" in
-  *) OPENSSL_INCLUDE_DIR="$withval"
+# Check whether --with-rlm-FOO-include-dir was given.
+if test "${with_rlm_FOO_include_dir+set}" = set; then :
+  withval=$with_rlm_FOO_include_dir;  case "$withval" in
+  *)
     ;;
   esac
 
 fi
 
 
-OPENSSL_LIB_DIR=
+WITH_OPENSSL=yes
 
-# Check whether --with-openssl-libraries was given.
-if test "${with_openssl_libraries+set}" = set; then :
-  withval=$with_openssl_libraries;  case "$withval" in
-  *) OPENSSL_LIB_DIR="$withval"
+# Check whether --with-openssl was given.
+if test "${with_openssl+set}" = set; then :
+  withval=$with_openssl;  case "$withval" in
+  no)
+    WITH_OPENSSL=no
+    ;;
+  *)
+    WITH_OPENSSL=yes
     ;;
   esac
 
 fi
 
 
+openssl_lib_dir=
 
-
-# Check whether --with-rlm-FOO-lib-dir was given.
-if test "${with_rlm_FOO_lib_dir+set}" = set; then :
-  withval=$with_rlm_FOO_lib_dir;  case "$withval" in
-  *)
+# Check whether --with-openssl-lib-dir was given.
+if test "${with_openssl_lib_dir+set}" = set; then :
+  withval=$with_openssl_lib_dir;  case "$withval" in
+  *) openssl_lib_dir="$withval"
     ;;
   esac
 
 fi
 
 
+openssl_include_dir=
 
-# Check whether --with-rlm-FOO-include-dir was given.
-if test "${with_rlm_FOO_include_dir+set}" = set; then :
-  withval=$with_rlm_FOO_include_dir;  case "$withval" in
-  *)
+# Check whether --with-openssl-include-dir was given.
+if test "${with_openssl_include_dir+set}" = set; then :
+  withval=$with_openssl_include_dir;  case "$withval" in
+  *) openssl_include_dir="$withval"
     ;;
   esac
 
 
 
 
-old_CFLAGS=$CFLAGS
-if test "x$WITH_THREADS" = "xyes"; then
-  if test $ac_cv_prog_suncc = "yes"; then
-    CFLAGS="$CFLAGS -mt"
-  fi
+talloc_lib_dir=
 
-  for ac_header in pthread.h
-do :
-  ac_fn_c_check_header_mongrel "$LINENO" "pthread.h" "ac_cv_header_pthread_h" "$ac_includes_default"
-if test "x$ac_cv_header_pthread_h" = xyes; then :
-  cat >>confdefs.h <<_ACEOF
-#define HAVE_PTHREAD_H 1
-_ACEOF
+# Check whether --with-talloc-lib-dir was given.
+if test "${with_talloc_lib_dir+set}" = set; then :
+  withval=$with_talloc_lib_dir; case "$withval" in
+    no)
+      as_fn_error $? "Need talloc-lib-dir" "$LINENO" 5
+      ;;
+    yes)
+      ;;
+    *)
+      talloc_lib_dir="$withval"
+      ;;
+  esac
+fi
 
-else
-   WITH_THREADS="no"
+
+talloc_include_dir=
+
+# Check whether --with-talloc-include-dir was given.
+if test "${with_talloc_include_dir+set}" = set; then :
+  withval=$with_talloc_include_dir; case "$withval" in
+    no)
+      as_fn_error $? "Need talloc-include-dir" "$LINENO" 5
+      ;;
+    yes)
+      ;;
+    *)
+      talloc_include_dir="$withval"
+      ;;
+  esac
 fi
 
-done
 
+smart_try_dir="$talloc_lib_dir"
 
-                { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5
-$as_echo_n "checking for pthread_create in -lpthread... " >&6; }
-if ${ac_cv_lib_pthread_pthread_create+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  ac_check_lib_save_LIBS=$LIBS
-LIBS="-lpthread  $LIBS"
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
 
-/* Override any GCC internal prototype to avoid an error.
-   Use char because int might match the return type of a GCC
-   builtin and then its argument prototype would still apply.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-char pthread_create ();
+sm_lib_safe=`echo "talloc" | sed 'y%./+-%__p_%'`
+sm_func_safe=`echo "_talloc" | sed 'y%./+-%__p_%'`
+
+old_LIBS="$LIBS"
+old_CPPFLAGS="$CPPFLAGS"
+smart_lib=
+smart_ldflags=
+smart_lib_dir=
+
+if test "x$smart_try_dir" != "x"; then
+  for try in $smart_try_dir; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _talloc in -ltalloc in $try" >&5
+$as_echo_n "checking for _talloc in -ltalloc in $try... " >&6; }
+    LIBS="-ltalloc $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char _talloc();
 int
 main ()
 {
-return pthread_create ();
+_talloc()
   ;
   return 0;
 }
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
-  ac_cv_lib_pthread_pthread_create=yes
+
+                smart_lib="-ltalloc"
+                smart_ldflags="-L$try -Wl,-rpath,$try"
+                { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                break
+
 else
-  ac_cv_lib_pthread_pthread_create=no
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
 fi
 rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
+  done
+  LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create" >&5
-$as_echo "$ac_cv_lib_pthread_pthread_create" >&6; }
-if test "x$ac_cv_lib_pthread_pthread_create" = xyes; then :
 
-      CFLAGS="$CFLAGS -D_REENTRANT -D_POSIX_PTHREAD_SEMANTICS"
-      LIBS="-lpthread $LIBS"
-
-else
-
-      { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lc_r" >&5
-$as_echo_n "checking for pthread_create in -lc_r... " >&6; }
-if ${ac_cv_lib_c_r_pthread_create+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  ac_check_lib_save_LIBS=$LIBS
-LIBS="-lc_r  $LIBS"
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-
-/* Override any GCC internal prototype to avoid an error.
-   Use char because int might match the return type of a GCC
+if test "x$smart_lib" = "x"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _talloc in -ltalloc" >&5
+$as_echo_n "checking for _talloc in -ltalloc... " >&6; }
+  LIBS="-ltalloc $old_LIBS"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char _talloc();
+int
+main ()
+{
+_talloc()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+               smart_lib="-ltalloc"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  LIBS="$old_LIBS"
+fi
+
+if test "x$smart_lib" = "x"; then
+
+
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file=libtalloc${libltdl_cv_shlibext}
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                        base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+                exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
+
+
+
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file=libtalloc.a
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                        base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+                exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
+
+
+  for try in $smart_lib_dir /usr/local/lib /opt/lib; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _talloc in -ltalloc in $try" >&5
+$as_echo_n "checking for _talloc in -ltalloc in $try... " >&6; }
+    LIBS="-ltalloc $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char _talloc();
+int
+main ()
+{
+_talloc()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+                 smart_lib="-ltalloc"
+                 smart_ldflags="-L$try -Wl,-rpath,$try"
+                 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                 break
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  done
+  LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_lib" != "x"; then
+  eval "ac_cv_lib_${sm_lib_safe}_${sm_func_safe}=yes"
+  LIBS="$smart_ldflags $smart_lib $old_LIBS"
+  SMART_LIBS="$smart_ldflags $smart_lib $SMART_LIBS"
+fi
+
+if test "x$ac_cv_lib_talloc__talloc" != "xyes"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: talloc library not found. Use --with-talloc-lib-dir=<path>." >&5
+$as_echo "$as_me: WARNING: talloc library not found. Use --with-talloc-lib-dir=<path>." >&2;}
+  as_fn_error $? "FreeRADIUS requires libtalloc" "$LINENO" 5
+fi
+
+TALLOC_LIBS="${smart_lib}"
+TALLOC_LDFLAGS="${smart_ldflags}"
+
+
+LIBS="$old_LIBS"
+
+old_CFLAGS=$CFLAGS
+if test "x$WITH_THREADS" = "xyes"; then
+  if test $ac_cv_prog_suncc = "yes"; then
+    CFLAGS="$CFLAGS -mt"
+  fi
+
+  for ac_header in pthread.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "pthread.h" "ac_cv_header_pthread_h" "$ac_includes_default"
+if test "x$ac_cv_header_pthread_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_PTHREAD_H 1
+_ACEOF
+
+else
+
+      WITH_THREADS="no"
+      fail=pthread.h
+
+fi
+
+done
+
+
+                { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5
+$as_echo_n "checking for pthread_create in -lpthread... " >&6; }
+if ${ac_cv_lib_pthread_pthread_create+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpthread  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pthread_create ();
+int
+main ()
+{
+return pthread_create ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_pthread_pthread_create=yes
+else
+  ac_cv_lib_pthread_pthread_create=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create" >&5
+$as_echo "$ac_cv_lib_pthread_pthread_create" >&6; }
+if test "x$ac_cv_lib_pthread_pthread_create" = xyes; then :
+
+      CFLAGS="$CFLAGS -D_REENTRANT -D_POSIX_PTHREAD_SEMANTICS"
+      LIBS="-lpthread $LIBS"
+
+else
+
+                              { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lc_r" >&5
+$as_echo_n "checking for pthread_create in -lc_r... " >&6; }
+if ${ac_cv_lib_c_r_pthread_create+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lc_r  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
    builtin and then its argument prototype would still apply.  */
 #ifdef __cplusplus
 extern "C"
@@ -5942,7 +6197,10 @@ $as_echo "$ac_cv_lib_c_r_pthread_create" >&6; }
 if test "x$ac_cv_lib_c_r_pthread_create" = xyes; then :
    CFLAGS="$CFLAGS -pthread -D_THREAD_SAFE"
 else
-   WITH_THREADS="no"
+
+          WITH_THREADS="no"
+          fail=-lpthread
+
 
 fi
 
@@ -5950,6 +6208,17 @@ fi
 
 fi
 
+
+  if test "x$WITH_THREADS" != "xyes"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: silently not building with thread support." >&5
+$as_echo "$as_me: WARNING: silently not building with thread support." >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: FAILURE: thread support requires: $fail." >&5
+$as_echo "$as_me: WARNING: FAILURE: thread support requires: $fail." >&2;}
+  else
+
+$as_echo "#define WITH_THREADS 1" >>confdefs.h
+
+  fi
 fi
 
 if test "x$WITH_THREADS" != "xyes"; then
@@ -6019,12 +6288,6 @@ fi
 
 fi
 
-if test "x$WITH_THREADS" = "xyes"; then
-
-$as_echo "#define WITH_THREADS 1" >>confdefs.h
-
-fi
-
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
 $as_echo_n "checking for dlopen in -ldl... " >&6; }
 if ${ac_cv_lib_dl_dlopen+:} false; then :
@@ -6254,6 +6517,40 @@ _ACEOF
 fi
 
 
+pcap_lib_dir=
+
+# Check whether --with-pcap-lib-dir was given.
+if test "${with_pcap_lib_dir+set}" = set; then :
+  withval=$with_pcap_lib_dir; case "$withval" in
+    no)
+      as_fn_error $? "Need pcap-lib-dir" "$LINENO" 5
+      ;;
+    yes)
+      ;;
+    *)
+      pcap_lib_dir="$withval"
+      ;;
+  esac
+fi
+
+
+pcap_include_dir=
+
+# Check whether --with-pcap-include-dir was given.
+if test "${with_pcap_include_dir+set}" = set; then :
+  withval=$with_pcap_include_dir; case "$withval" in
+    no)
+      as_fn_error $? "Need pcap-include-dir" "$LINENO" 5
+      ;;
+    yes)
+      ;;
+    *)
+      pcap_include_dir="$withval"
+      ;;
+  esac
+fi
+
+
 smart_try_dir="$pcap_lib_dir"
 
 
@@ -6261,14 +6558,17 @@ sm_lib_safe=`echo "pcap" | sed 'y%./+-%__p_%'`
 sm_func_safe=`echo "pcap_open_live" | sed 'y%./+-%__p_%'`
 
 old_LIBS="$LIBS"
+old_CPPFLAGS="$CPPFLAGS"
 smart_lib=
+smart_ldflags=
 smart_lib_dir=
 
 if test "x$smart_try_dir" != "x"; then
   for try in $smart_try_dir; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pcap_open_live in -lpcap in $try" >&5
 $as_echo_n "checking for pcap_open_live in -lpcap in $try... " >&6; }
-    LIBS="-L$try -lpcap $old_LIBS -Wl,-rpath,$try"
+    LIBS="-lpcap $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 extern char pcap_open_live();
@@ -6282,7 +6582,8 @@ pcap_open_live()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-                smart_lib="-L$try -lpcap -Wl,-rpath,$try"
+                smart_lib="-lpcap"
+                smart_ldflags="-L$try -Wl,-rpath,$try"
                 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
                 break
@@ -6295,6 +6596,7 @@ rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
   done
   LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_lib" = "x"; then
@@ -6386,7 +6688,8 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
   for try in $smart_lib_dir /usr/local/lib /opt/lib; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pcap_open_live in -lpcap in $try" >&5
 $as_echo_n "checking for pcap_open_live in -lpcap in $try... " >&6; }
-    LIBS="-L$try -lpcap $old_LIBS -Wl,-rpath,$try"
+    LIBS="-lpcap $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 extern char pcap_open_live();
@@ -6400,7 +6703,8 @@ pcap_open_live()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-                 smart_lib="-L$try -lpcap -Wl,-rpath,$try"
+                 smart_lib="-lpcap"
+                 smart_ldflags="-L$try -Wl,-rpath,$try"
                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
                  break
@@ -6413,29 +6717,266 @@ rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
   done
   LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_lib" != "x"; then
   eval "ac_cv_lib_${sm_lib_safe}_${sm_func_safe}=yes"
-  LIBS="$smart_lib $old_LIBS"
-  SMART_LIBS="$smart_lib $SMART_LIBS"
+  LIBS="$smart_ldflags $smart_lib $old_LIBS"
+  SMART_LIBS="$smart_ldflags $smart_lib $SMART_LIBS"
 fi
 
 if test "x$ac_cv_lib_pcap_pcap_open_live" != "xyes"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: pcap library not found. Use --with-pcap-lib-dir=<path>." >&5
-$as_echo "$as_me: WARNING: pcap library not found. Use --with-pcap-lib-dir=<path>." >&2;}
-  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: pcap library not found, silently disabling the RADIUS sniffer and ARP listener." >&5
-$as_echo "$as_me: WARNING: pcap library not found, silently disabling the RADIUS sniffer and ARP listener." >&2;}
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: pcap library not found, silently disabling the RADIUS sniffer, and ARP listener.  Use --with-pcap-lib-dir=<path>." >&5
+$as_echo "$as_me: WARNING: pcap library not found, silently disabling the RADIUS sniffer, and ARP listener.  Use --with-pcap-lib-dir=<path>." >&2;}
 else
-  PCAP_LIBS="${smart_lib}"
-  LIBS=$old_LIBS
 
 $as_echo "#define HAVE_LIBPCAP 1" >>confdefs.h
 
-fi
 
+  for ac_func in \
+    pcap_fopen_offline \
+    pcap_dump_fopen \
+    pcap_create \
+    pcap_activate
 
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a readline compatible library" >&5
+do :
+  as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+  PCAP_LIBS="${smart_lib}"
+  PCAP_LDFLAGS="${smart_ldflags}"
+fi
+LIBS="${old_LIBS}"
+
+collectdclient_lib_dir=
+
+# Check whether --with-collectdclient-lib-dir was given.
+if test "${with_collectdclient_lib_dir+set}" = set; then :
+  withval=$with_collectdclient_lib_dir; case "$withval" in
+    no)
+      as_fn_error $? "Need collectdclient-lib-dir" "$LINENO" 5
+      ;;
+    yes)
+      ;;
+    *)
+      collectdclient_lib_dir="$withval"
+      ;;
+  esac
+fi
+
+
+collectdclient_include_dir=
+
+# Check whether --with-collectdclient-include-dir was given.
+if test "${with_collectdclient_include_dir+set}" = set; then :
+  withval=$with_collectdclient_include_dir; case "$withval" in
+    no)
+      as_fn_error $? "Need collectdclient-include-dir" "$LINENO" 5
+      ;;
+    yes)
+      ;;
+    *)
+      collectdclient_include_dir="$withval"
+      ;;
+  esac
+fi
+
+
+smart_try_dir="$collectdclient_lib_dir"
+
+
+sm_lib_safe=`echo "collectdclient" | sed 'y%./+-%__p_%'`
+sm_func_safe=`echo "lcc_connect" | sed 'y%./+-%__p_%'`
+
+old_LIBS="$LIBS"
+old_CPPFLAGS="$CPPFLAGS"
+smart_lib=
+smart_ldflags=
+smart_lib_dir=
+
+if test "x$smart_try_dir" != "x"; then
+  for try in $smart_try_dir; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lcc_connect in -lcollectdclient in $try" >&5
+$as_echo_n "checking for lcc_connect in -lcollectdclient in $try... " >&6; }
+    LIBS="-lcollectdclient $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char lcc_connect();
+int
+main ()
+{
+lcc_connect()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+                smart_lib="-lcollectdclient"
+                smart_ldflags="-L$try -Wl,-rpath,$try"
+                { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                break
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  done
+  LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_lib" = "x"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lcc_connect in -lcollectdclient" >&5
+$as_echo_n "checking for lcc_connect in -lcollectdclient... " >&6; }
+  LIBS="-lcollectdclient $old_LIBS"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char lcc_connect();
+int
+main ()
+{
+lcc_connect()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+               smart_lib="-lcollectdclient"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  LIBS="$old_LIBS"
+fi
+
+if test "x$smart_lib" = "x"; then
+
+
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file=libcollectdclient${libltdl_cv_shlibext}
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                        base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+                exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
+
+
+
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file=libcollectdclient.a
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                        base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+                exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
+
+
+  for try in $smart_lib_dir /usr/local/lib /opt/lib; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lcc_connect in -lcollectdclient in $try" >&5
+$as_echo_n "checking for lcc_connect in -lcollectdclient in $try... " >&6; }
+    LIBS="-lcollectdclient $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char lcc_connect();
+int
+main ()
+{
+lcc_connect()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+                 smart_lib="-lcollectdclient"
+                 smart_ldflags="-L$try -Wl,-rpath,$try"
+                 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                 break
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  done
+  LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_lib" != "x"; then
+  eval "ac_cv_lib_${sm_lib_safe}_${sm_func_safe}=yes"
+  LIBS="$smart_ldflags $smart_lib $old_LIBS"
+  SMART_LIBS="$smart_ldflags $smart_lib $SMART_LIBS"
+fi
+
+if test "x$ac_cv_lib_collectdclient_lcc_connect" != "xyes"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: collectdclient library not found. Use --with-collectdclient-lib-dir=<path>." >&5
+$as_echo "$as_me: WARNING: collectdclient library not found. Use --with-collectdclient-lib-dir=<path>." >&2;}
+else
+  COLLECTDC_LIBS="${smart_lib}"
+  COLLECTDC_LDFLAGS="${smart_ldflags}"
+fi
+LIBS="${old_LIBS}"
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a readline compatible library" >&5
 $as_echo_n "checking for a readline compatible library... " >&6; }
 if ${vl_cv_lib_readline+:} false; then :
   $as_echo_n "(cached) " >&6
@@ -6565,65 +7106,216 @@ done
 
 
 
-case "$host" in
-  *-interix*)
-    CFLAGS="$CFLAGS -D_ALL_SOURCE"
-    ;;
-  *-darwin*)
-    CFLAGS="$CFLAGS -DDARWIN"
-    LIBS="-framework DirectoryService $LIBS"
-    ;;
-esac
+smart_try_dir="$talloc_include_dir"
 
-ac_header_dirent=no
-for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do
-  as_ac_Header=`$as_echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh`
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_hdr that defines DIR" >&5
-$as_echo_n "checking for $ac_hdr that defines DIR... " >&6; }
-if eval \${$as_ac_Header+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+
+ac_safe=`echo "talloc.h" | sed 'y%./+-%__pm%'`
+old_CPPFLAGS="$CPPFLAGS"
+smart_include=
+smart_include_dir=
+
+if test "x$smart_try_dir" != "x"; then
+  for try in $smart_try_dir; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for talloc.h in $try" >&5
+$as_echo_n "checking for talloc.h in $try... " >&6; }
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-#include <sys/types.h>
-#include <$ac_hdr>
 
+                   #include <talloc.h>
 int
 main ()
 {
-if ((DIR *) 0)
-return 0;
+int a = 1;
   ;
   return 0;
 }
 _ACEOF
 if ac_fn_c_try_compile "$LINENO"; then :
-  eval "$as_ac_Header=yes"
+
+                    smart_include="-isystem $try"
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                    break
+
 else
-  eval "$as_ac_Header=no"
+
+                    smart_include=
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+  CPPFLAGS="$old_CPPFLAGS"
 fi
-eval ac_res=\$$as_ac_Header
-              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-$as_echo "$ac_res" >&6; }
-if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
-  cat >>confdefs.h <<_ACEOF
-#define `$as_echo "HAVE_$ac_hdr" | $as_tr_cpp` 1
-_ACEOF
 
-ac_header_dirent=$ac_hdr; break
-fi
+if test "x$smart_include" = "x"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for talloc.h" >&5
+$as_echo_n "checking for talloc.h... " >&6; }
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
 
-done
-# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix.
-if test $ac_header_dirent = dirent.h; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5
-$as_echo_n "checking for library containing opendir... " >&6; }
-if ${ac_cv_search_opendir+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  ac_func_search_save_LIBS=$LIBS
+                 #include <talloc.h>
+int
+main ()
+{
+int a = 1;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+                  smart_include=" "
+                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                  break
+
+else
+
+                  smart_include=
+                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+if test "x$smart_include" = "x"; then
+
+
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file=talloc.h
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                        base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+                exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_include_dir=\"\$smart_include_dir $DIRS\""
+
+  for try in $smart_include_dir /usr/local/include /opt/include; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for talloc.h in $try" >&5
+$as_echo_n "checking for talloc.h in $try... " >&6; }
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                   #include <talloc.h>
+int
+main ()
+{
+int a = 1;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+                    smart_include="-isystem $try"
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                    break
+
+else
+
+                    smart_include=
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+  CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_include" != "x"; then
+  eval "ac_cv_header_$ac_safe=yes"
+  CPPFLAGS="$smart_include $old_CPPFLAGS"
+  SMART_CPPFLAGS="$smart_include $SMART_CPPFLAGS"
+fi
+
+if test "x$ac_cv_header_talloc_h" != "xyes"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: talloc headers not found. Use --with-talloc-include-dir=<path>." >&5
+$as_echo "$as_me: WARNING: talloc headers not found. Use --with-talloc-include-dir=<path>." >&2;}
+  as_fn_error $? "FreeRADIUS requires libtalloc" "$LINENO" 5
+fi
+
+case "$host" in
+  *-interix*)
+    CFLAGS="$CFLAGS -D_ALL_SOURCE"
+    ;;
+  *-darwin*)
+    CFLAGS="$CFLAGS -DDARWIN"
+    LIBS="-framework DirectoryService $LIBS"
+    ;;
+esac
+
+ac_header_dirent=no
+for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do
+  as_ac_Header=`$as_echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_hdr that defines DIR" >&5
+$as_echo_n "checking for $ac_hdr that defines DIR... " >&6; }
+if eval \${$as_ac_Header+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <$ac_hdr>
+
+int
+main ()
+{
+if ((DIR *) 0)
+return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  eval "$as_ac_Header=yes"
+else
+  eval "$as_ac_Header=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$as_ac_Header
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_hdr" | $as_tr_cpp` 1
+_ACEOF
+
+ac_header_dirent=$ac_hdr; break
+fi
+
+done
+# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix.
+if test $ac_header_dirent = dirent.h; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5
+$as_echo_n "checking for library containing opendir... " >&6; }
+if ${ac_cv_search_opendir+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_func_search_save_LIBS=$LIBS
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -6927,7 +7619,6 @@ for ac_header in \
   unistd.h \
   crypt.h \
   errno.h \
-  features.h \
   resource.h \
   sys/resource.h \
   getopt.h \
@@ -6962,7 +7653,8 @@ for ac_header in \
   stddef.h \
   fnmatch.h \
   sia.h \
-  siad.h
+  siad.h \
+  features.h
 
 do :
   as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
@@ -6980,9 +7672,10 @@ done
 for ac_header in net/if.h
 do :
   ac_fn_c_check_header_compile "$LINENO" "net/if.h" "ac_cv_header_net_if_h" "
-  #ifdef HAVE_SYS_SOCKET_H
-  #  include <sys/socket.h>
-  #endif
+    #ifdef HAVE_SYS_SOCKET_H
+    #  include <sys/socket.h>
+    #endif
+
 
 "
 if test "x$ac_cv_header_net_if_h" = xyes; then :
@@ -7010,279 +7703,408 @@ $as_echo "#define OSFSIA /**/" >>confdefs.h
 fi
 
 if test "x$WITH_OPENSSL" = xyes; then
-  old_LIBS=$LIBS
-  old_LDFLAGS="$LDFLAGS"
+  OLD_LIBS="$LIBS"
 
-  OPENSSL_INCLUDE="-DNO_OPENSSL"
-  OPENSSL_LIBS=
-  if test "x$OPENSSL_LIB_DIR" != "x"; then
-    LDFLAGS="-L$OPENSSL_LIB_DIR $LDFLAGS"
-  fi
+        CFLAGS="$CFLAGS -DOPENSSL_NO_KRB5"
 
-        { $as_echo "$as_me:${as_lineno-$LINENO}: checking for DH_new in -lcrypto" >&5
-$as_echo_n "checking for DH_new in -lcrypto... " >&6; }
-if ${ac_cv_lib_crypto_DH_new+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  ac_check_lib_save_LIBS=$LIBS
-LIBS="-lcrypto  $LIBS"
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
+        smart_try_dir="$openssl_lib_dir"
 
-/* Override any GCC internal prototype to avoid an error.
-   Use char because int might match the return type of a GCC
-   builtin and then its argument prototype would still apply.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-char DH_new ();
+
+sm_lib_safe=`echo "crypto" | sed 'y%./+-%__p_%'`
+sm_func_safe=`echo "DH_new" | sed 'y%./+-%__p_%'`
+
+old_LIBS="$LIBS"
+old_CPPFLAGS="$CPPFLAGS"
+smart_lib=
+smart_ldflags=
+smart_lib_dir=
+
+if test "x$smart_try_dir" != "x"; then
+  for try in $smart_try_dir; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for DH_new in -lcrypto in $try" >&5
+$as_echo_n "checking for DH_new in -lcrypto in $try... " >&6; }
+    LIBS="-lcrypto $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char DH_new();
 int
 main ()
 {
-return DH_new ();
+DH_new()
   ;
   return 0;
 }
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
-  ac_cv_lib_crypto_DH_new=yes
+
+                smart_lib="-lcrypto"
+                smart_ldflags="-L$try -Wl,-rpath,$try"
+                { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                break
+
 else
-  ac_cv_lib_crypto_DH_new=no
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
 fi
 rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
+  done
+  LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_crypto_DH_new" >&5
-$as_echo "$ac_cv_lib_crypto_DH_new" >&6; }
-if test "x$ac_cv_lib_crypto_DH_new" = xyes; then :
-
-      LIBS="-lcrypto $LIBS"
-
-$as_echo "#define HAVE_LIBCRYPTO 1" >>confdefs.h
 
-      { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_new in -lssl" >&5
-$as_echo_n "checking for SSL_new in -lssl... " >&6; }
-if ${ac_cv_lib_ssl_SSL_new+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  ac_check_lib_save_LIBS=$LIBS
-LIBS="-lssl  $LIBS"
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+if test "x$smart_lib" = "x"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for DH_new in -lcrypto" >&5
+$as_echo_n "checking for DH_new in -lcrypto... " >&6; }
+  LIBS="-lcrypto $old_LIBS"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-
-/* Override any GCC internal prototype to avoid an error.
-   Use char because int might match the return type of a GCC
-   builtin and then its argument prototype would still apply.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-char SSL_new ();
+extern char DH_new();
 int
 main ()
 {
-return SSL_new ();
+DH_new()
   ;
   return 0;
 }
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
-  ac_cv_lib_ssl_SSL_new=yes
+
+               smart_lib="-lcrypto"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
 else
-  ac_cv_lib_ssl_SSL_new=no
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
 fi
 rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
+  LIBS="$old_LIBS"
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssl_SSL_new" >&5
-$as_echo "$ac_cv_lib_ssl_SSL_new" >&6; }
-if test "x$ac_cv_lib_ssl_SSL_new" = xyes; then :
-
 
-$as_echo "#define HAVE_LIBSSL 1" >>confdefs.h
+if test "x$smart_lib" = "x"; then
 
-          if test "x$OPENSSL_LIB_DIR" != "x"; then
-            OPENSSL_LIBS="-L$OPENSSL_LIB_DIR"
-          fi
-          OPENSSL_LIBS="$OPENSSL_LIBS -lcrypto -lssl -lcrypto"
 
-else
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file=libcrypto${libltdl_cv_shlibext}
 
-          { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error $? "failed linking to libssl
-See \`config.log' for more details" "$LINENO" 5; }
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                        base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
 
+    dir=`${DIRNAME} $x 2>/dev/null`
+                exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
 
+                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
 fi
 
-
-fi
+eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
-        old_CPPFLAGS=$CPPFLAGS
-  old_CFLAGS=$CFLAGS
-  if test "x$OPENSSL_INCLUDE_DIR" != "x"; then
-    CPPFLAGS="-isystem $OPENSSL_INCLUDE_DIR $CPPFLAGS"
-    CFLAGS="-isystem $OPENSSL_INCLUDE_DIR $CFLAGS"
-  fi
 
-        CPPFLAGS="$CPPFLAGS -DOPENSSL_NO_KRB5"
-  for ac_header in \
-    openssl/ssl.h \
-    openssl/crypto.h \
-    openssl/err.h \
-    openssl/evp.h \
-    openssl/md5.h \
-    openssl/md4.h \
-    openssl/sha.h \
-    openssl/ocsp.h \
-    openssl/engine.h
-do :
-  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
-ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
-if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
-  cat >>confdefs.h <<_ACEOF
-#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
-_ACEOF
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file=libcrypto.a
 
-else
-
-      { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error $? "failed locating OpenSSL headers
-See \`config.log' for more details" "$LINENO" 5; }
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                        base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
 
+    dir=`${DIRNAME} $x 2>/dev/null`
+                exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
 
+                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
 fi
 
-done
+eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OpenSSL version >= 0.9.7" >&5
-$as_echo_n "checking for OpenSSL version >= 0.9.7... " >&6; }
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+  for try in $smart_lib_dir /usr/local/lib /opt/lib; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for DH_new in -lcrypto in $try" >&5
+$as_echo_n "checking for DH_new in -lcrypto in $try... " >&6; }
+    LIBS="-lcrypto $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-#include <openssl/crypto.h>
-     #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
-     yes
-     #endif
-
+extern char DH_new();
+int
+main ()
+{
+DH_new()
+  ;
+  return 0;
+}
 _ACEOF
-if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
-  $EGREP "yes" >/dev/null 2>&1; then :
+if ac_fn_c_try_link "$LINENO"; then :
 
-      { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+                 smart_lib="-lcrypto"
+                 smart_ldflags="-L$try -Wl,-rpath,$try"
+                 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
+                 break
 
 else
-
-      { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
-      { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error $? "OpenSSL version too old
-See \`config.log' for more details" "$LINENO" 5; }
-
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  done
+  LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
+fi
 
+if test "x$smart_lib" != "x"; then
+  eval "ac_cv_lib_${sm_lib_safe}_${sm_func_safe}=yes"
+  LIBS="$smart_ldflags $smart_lib $old_LIBS"
+  SMART_LIBS="$smart_ldflags $smart_lib $SMART_LIBS"
 fi
-rm -f conftest*
 
+  if test "x$ac_cv_lib_crypto_DH_new" = "xyes"; then
 
-  if test "x$OPENSSL_INCLUDE_DIR" != "x"; then
-    OPENSSL_INCLUDE="-isystem $OPENSSL_INCLUDE_DIR -DOPENSSL_NO_KRB5"
-  else
-    OPENSSL_INCLUDE="-DOPENSSL_NO_KRB5"
-  fi
+$as_echo "#define HAVE_LIBCRYPTO 1" >>confdefs.h
 
-        { $as_echo "$as_me:${as_lineno-$LINENO}: checking OpenSSL library and header version consistency" >&5
-$as_echo_n "checking OpenSSL library and header version consistency... " >&6; }
-  if test "$cross_compiling" = yes; then :
-  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error $? "cannot run test program while cross compiling
-See \`config.log' for more details" "$LINENO" 5; }
-else
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
+    OPENSSL_LIBS="$smart_lib"
+    OPENSSL_LDFLAGS="$smart_ldflags"
 
-        #include <stdio.h>
-        #include <openssl/opensslv.h>
-        #include <openssl/crypto.h>
 
+
+sm_lib_safe=`echo "ssl" | sed 'y%./+-%__p_%'`
+sm_func_safe=`echo "SSL_new" | sed 'y%./+-%__p_%'`
+
+old_LIBS="$LIBS"
+old_CPPFLAGS="$CPPFLAGS"
+smart_lib=
+smart_ldflags=
+smart_lib_dir=
+
+if test "x$smart_try_dir" != "x"; then
+  for try in $smart_try_dir; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_new in -lssl in $try" >&5
+$as_echo_n "checking for SSL_new in -lssl in $try... " >&6; }
+    LIBS="-lssl $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char SSL_new();
 int
 main ()
 {
+SSL_new()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
 
-        if (SSLeay() == OPENSSL_VERSION_NUMBER) {
-          return 0;
-        } else {
-          printf("library: %lx header: %lx... ", (unsigned long) SSLeay(), (unsigned long) OPENSSL_VERSION_NUMBER);
-          return 1;
-        }
+                smart_lib="-lssl"
+                smart_ldflags="-L$try -Wl,-rpath,$try"
+                { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                break
 
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  done
+  LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
+fi
 
+if test "x$smart_lib" = "x"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_new in -lssl" >&5
+$as_echo_n "checking for SSL_new in -lssl... " >&6; }
+  LIBS="-lssl $old_LIBS"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char SSL_new();
+int
+main ()
+{
+SSL_new()
   ;
   return 0;
 }
 _ACEOF
-if ac_fn_c_try_run "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"; then :
 
-      { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lssl"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
-
-      { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
-      { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error $? "OpenSSL library version does not match header version
-See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  LIBS="$old_LIBS"
+fi
 
+if test "x$smart_lib" = "x"; then
 
-fi
-rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
-  conftest.$ac_objext conftest.beam conftest.$ac_ext
+
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file=libssl${libltdl_cv_shlibext}
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                        base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+                exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
 fi
 
+eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
-  if test "x$OPENSSL_LIBS" = x; then
-    LIBS=$old_LIBS
-    LDFLAGS="$old_LDFLAGS"
-  fi
-  if test "x$OPENSSL_INCLUDE" = x; then
-    CPPFLAGS=$old_CPPFLAGS
-    CFLAGS=$old_CFLAGS
-  fi
+
+
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file=libssl.a
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                        base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+                exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
 fi
 
+eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
+
 
+  for try in $smart_lib_dir /usr/local/lib /opt/lib; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_new in -lssl in $try" >&5
+$as_echo_n "checking for SSL_new in -lssl in $try... " >&6; }
+    LIBS="-lssl $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char SSL_new();
+int
+main ()
+{
+SSL_new()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
 
-export OPENSSL_LIBS
+                 smart_lib="-lssl"
+                 smart_ldflags="-L$try -Wl,-rpath,$try"
+                 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                 break
 
-if test "x$PCAP_LIBS" = x; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: skipping test for pcap.h." >&5
-$as_echo "$as_me: skipping test for pcap.h." >&6;}
 else
-  smart_try_dir="$pcap_include_dir"
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  done
+  LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_lib" != "x"; then
+  eval "ac_cv_lib_${sm_lib_safe}_${sm_func_safe}=yes"
+  LIBS="$smart_ldflags $smart_lib $old_LIBS"
+  SMART_LIBS="$smart_ldflags $smart_lib $SMART_LIBS"
+fi
+
+    if test "x$ac_cv_lib_ssl_SSL_new" != "xyes"; then
+      { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed linking to libssl. Use --with-openssl-lib-dir=<path>, or --with-openssl=no (builds without OpenSSL)
+See \`config.log' for more details" "$LINENO" 5; }
+    else
 
+$as_echo "#define HAVE_LIBSSL 1" >>confdefs.h
 
-ac_safe=`echo "pcap.h" | sed 'y%./+-%__pm%'`
-old_CFLAGS="$CFLAGS"
+      OPENSSL_LIBS="$OPENSSL_LIBS $smart_lib"
+
+      if test "$OPENSSL_LDFLAGS" != "$smart_ldflags"; then
+        { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "\"inconsistent LDFLAGS between -lssl '$smart_ldflags' and -lcrypto '$OPENSSL_LDFLAGS'\"
+See \`config.log' for more details" "$LINENO" 5; }
+      fi
+    fi
+  else
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed linking to libcrypto. Use --with-openssl-lib-dir=<path>, or --with-openssl=no (builds without OpenSSL)
+See \`config.log' for more details" "$LINENO" 5; }
+  fi
+
+  smart_try_dir="$openssl_include_dir"
+
+
+ac_safe=`echo "openssl/ssl.h" | sed 'y%./+-%__pm%'`
+old_CPPFLAGS="$CPPFLAGS"
 smart_include=
 smart_include_dir=
 
 if test "x$smart_try_dir" != "x"; then
   for try in $smart_try_dir; do
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pcap.h in $try" >&5
-$as_echo_n "checking for pcap.h in $try... " >&6; }
-    CFLAGS="$old_CFLAGS -isystem $try"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for openssl/ssl.h in $try" >&5
+$as_echo_n "checking for openssl/ssl.h in $try... " >&6; }
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
-                   #include <pcap.h>
+                   #include <openssl/ssl.h>
 int
 main ()
 {
@@ -7307,16 +8129,16 @@ $as_echo "no" >&6; }
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
   done
-  CFLAGS="$old_CFLAGS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_include" = "x"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pcap.h" >&5
-$as_echo_n "checking for pcap.h... " >&6; }
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for openssl/ssl.h" >&5
+$as_echo_n "checking for openssl/ssl.h... " >&6; }
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
-                 #include <pcap.h>
+                 #include <openssl/ssl.h>
 int
 main ()
 {
@@ -7347,7 +8169,7 @@ if test "x$smart_include" = "x"; then
 
 if test "x$LOCATE" != "x"; then
         DIRS=
-  file=pcap.h
+  file=openssl/ssl.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
                                         base=`echo $x | sed "s%/${file}%%"`
@@ -7371,13 +8193,13 @@ fi
 eval "smart_include_dir=\"\$smart_include_dir $DIRS\""
 
   for try in $smart_include_dir /usr/local/include /opt/include; do
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pcap.h in $try" >&5
-$as_echo_n "checking for pcap.h in $try... " >&6; }
-    CFLAGS="$old_CFLAGS -isystem $try"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for openssl/ssl.h in $try" >&5
+$as_echo_n "checking for openssl/ssl.h in $try... " >&6; }
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
-                   #include <pcap.h>
+                   #include <openssl/ssl.h>
 int
 main ()
 {
@@ -7402,426 +8224,771 @@ $as_echo "no" >&6; }
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
   done
-  CFLAGS="$old_CFLAGS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_include" != "x"; then
   eval "ac_cv_header_$ac_safe=yes"
-  CFLAGS="$old_CFLAGS $smart_include"
-  SMART_CFLAGS="$SMART_CFLAGS $smart_include"
+  CPPFLAGS="$smart_include $old_CPPFLAGS"
+  SMART_CPPFLAGS="$smart_include $SMART_CPPFLAGS"
 fi
 
-  if test "x$ac_cv_header_pcap_h" != "xyes"; then
-    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: pcap headers not found. Use --with-pcap-include-dir=<path>." >&5
-$as_echo "$as_me: WARNING: pcap headers not found. Use --with-pcap-include-dir=<path>." >&2;}
-    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: pcap.h not found, silently disabling the RADIUS sniffer, and ARP listener." >&5
-$as_echo "$as_me: WARNING: pcap.h not found, silently disabling the RADIUS sniffer, and ARP listener." >&2;}
-  else
+  if test "x$ac_cv_header_openssl_ssl_h" = "xyes"; then
 
-$as_echo "#define HAVE_PCAP_H 1" >>confdefs.h
+$as_echo "#define HAVE_OPENSSL_SSL_H 1" >>confdefs.h
 
 
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pcap_fopen_offline in -lpcap" >&5
-$as_echo_n "checking for pcap_fopen_offline in -lpcap... " >&6; }
-if ${ac_cv_lib_pcap_pcap_fopen_offline+:} false; then :
-  $as_echo_n "(cached) " >&6
+    for ac_header in \
+      openssl/crypto.h \
+      openssl/err.h \
+      openssl/evp.h \
+      openssl/md5.h \
+      openssl/md4.h \
+      openssl/sha.h \
+      openssl/ocsp.h \
+      openssl/engine.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
 else
-  ac_check_lib_save_LIBS=$LIBS
-LIBS="-lpcap  $LIBS"
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+
+        { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed locating OpenSSL headers. Use --with-openssl-include-dir=<path>, or --with-openssl=no (builds without OpenSSL)
+See \`config.log' for more details" "$LINENO" 5; }
+
+
+fi
+
+done
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OpenSSL version >= 0.9.7" >&5
+$as_echo_n "checking for OpenSSL version >= 0.9.7... " >&6; }
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
+#include <openssl/crypto.h>
+       #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
+       yes
+       #endif
 
-/* Override any GCC internal prototype to avoid an error.
-   Use char because int might match the return type of a GCC
-   builtin and then its argument prototype would still apply.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-char pcap_fopen_offline ();
-int
-main ()
-{
-return pcap_fopen_offline ();
-  ;
-  return 0;
-}
 _ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
-  ac_cv_lib_pcap_pcap_fopen_offline=yes
-else
-  ac_cv_lib_pcap_pcap_fopen_offline=no
-fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pcap_pcap_fopen_offline" >&5
-$as_echo "$ac_cv_lib_pcap_pcap_fopen_offline" >&6; }
-if test "x$ac_cv_lib_pcap_pcap_fopen_offline" = xyes; then :
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "yes" >/dev/null 2>&1; then :
 
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
 
-$as_echo "#define HAVE_PCAP_FOPEN_OFFLINE 1" >>confdefs.h
+else
 
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+        { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "OpenSSL version too old
+See \`config.log' for more details" "$LINENO" 5; }
 
 
 fi
+rm -f conftest*
 
 
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pcap_dump_fopen in -lpcap" >&5
-$as_echo_n "checking for pcap_dump_fopen in -lpcap... " >&6; }
-if ${ac_cv_lib_pcap_pcap_dump_fopen+:} false; then :
-  $as_echo_n "(cached) " >&6
+                        old_CPPFLAGS="$CPPFLAGS"
+    CPPFLAGS="$OPENSSL_LDFLAGS $CPPFLAGS"
+
+                { $as_echo "$as_me:${as_lineno-$LINENO}: checking OpenSSL library and header version consistency" >&5
+$as_echo_n "checking OpenSSL library and header version consistency... " >&6; }
+    if test "$cross_compiling" = yes; then :
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run test program while cross compiling
+See \`config.log' for more details" "$LINENO" 5; }
 else
-  ac_check_lib_save_LIBS=$LIBS
-LIBS="-lpcap  $LIBS"
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
-/* Override any GCC internal prototype to avoid an error.
-   Use char because int might match the return type of a GCC
-   builtin and then its argument prototype would still apply.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-char pcap_dump_fopen ();
+          #include <stdio.h>
+          #include <openssl/opensslv.h>
+          #include <openssl/crypto.h>
+
 int
 main ()
 {
-return pcap_dump_fopen ();
+
+          printf("library: %lx header: %lx... ", (unsigned long) SSLeay(), (unsigned long) OPENSSL_VERSION_NUMBER);
+          if (SSLeay() == OPENSSL_VERSION_NUMBER) {
+            return 0;
+          } else {
+            return 1;
+          }
+
+
   ;
   return 0;
 }
 _ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
-  ac_cv_lib_pcap_pcap_dump_fopen=yes
-else
-  ac_cv_lib_pcap_pcap_dump_fopen=no
-fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pcap_pcap_dump_fopen" >&5
-$as_echo "$ac_cv_lib_pcap_pcap_dump_fopen" >&6; }
-if test "x$ac_cv_lib_pcap_pcap_dump_fopen" = xyes; then :
+if ac_fn_c_try_run "$LINENO"; then :
 
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
 
-$as_echo "#define HAVE_PCAP_DUMP_FOPEN 1" >>confdefs.h
+else
 
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+        { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "OpenSSL library version does not match header version
+See \`config.log' for more details" "$LINENO" 5; }
 
 
 fi
-
-  fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
 fi
 
+    CPPFLAGS="$old_CPPFLAGS"
+  fi
 
+  LIBS="$OLD_LIBS"
 
-ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default"
-if test "x$ac_cv_type_off_t" = xyes; then :
-
-else
-
-cat >>confdefs.h <<_ACEOF
-#define off_t long int
-_ACEOF
 
+  export OPENSSL_LIBS OPENSSL_LDFLAGS
 fi
 
-ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default"
-if test "x$ac_cv_type_pid_t" = xyes; then :
-
+if test "x$PCAP_LIBS" = x; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: skipping test for pcap.h." >&5
+$as_echo "$as_me: skipping test for pcap.h." >&6;}
 else
+        smart_try_dir="$pcap_include_dir"
 
-cat >>confdefs.h <<_ACEOF
-#define pid_t int
-_ACEOF
-
-fi
 
-ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default"
-if test "x$ac_cv_type_size_t" = xyes; then :
+ac_safe=`echo "pcap.h" | sed 'y%./+-%__pm%'`
+old_CPPFLAGS="$CPPFLAGS"
+smart_include=
+smart_include_dir=
 
-else
+if test "x$smart_try_dir" != "x"; then
+  for try in $smart_try_dir; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pcap.h in $try" >&5
+$as_echo_n "checking for pcap.h in $try... " >&6; }
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
 
-cat >>confdefs.h <<_ACEOF
-#define size_t unsigned int
+                   #include <pcap.h>
+int
+main ()
+{
+int a = 1;
+  ;
+  return 0;
+}
 _ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
 
-fi
+                    smart_include="-isystem $try"
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                    break
 
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for uid_t in sys/types.h" >&5
-$as_echo_n "checking for uid_t in sys/types.h... " >&6; }
-if ${ac_cv_type_uid_t+:} false; then :
-  $as_echo_n "(cached) " >&6
 else
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-#include <sys/types.h>
 
-_ACEOF
-if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
-  $EGREP "uid_t" >/dev/null 2>&1; then :
-  ac_cv_type_uid_t=yes
-else
-  ac_cv_type_uid_t=no
-fi
-rm -f conftest*
+                    smart_include=
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
 
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uid_t" >&5
-$as_echo "$ac_cv_type_uid_t" >&6; }
-if test $ac_cv_type_uid_t = no; then
-
-$as_echo "#define uid_t int" >>confdefs.h
-
-
-$as_echo "#define gid_t int" >>confdefs.h
-
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
-
-
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for socklen_t" >&5
-$as_echo_n "checking for socklen_t... " >&6; }
-if ${ac_cv_type_socklen_t+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-   ac_cv_type_socklen_t=no
-      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+if test "x$smart_include" = "x"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pcap.h" >&5
+$as_echo_n "checking for pcap.h... " >&6; }
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-#ifdef HAVE_SYS_TYPES_H
-    #  include <sys/types.h>
-    #endif
-
-    #ifdef HAVE_SYS_SOCKET_H
-    #  include <sys/socket.h>
-    #endif
 
+                 #include <pcap.h>
 int
 main ()
 {
-socklen_t foo
+int a = 1;
   ;
   return 0;
 }
 _ACEOF
 if ac_fn_c_try_compile "$LINENO"; then :
-  ac_cv_type_socklen_t=yes
+
+                  smart_include=" "
+                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                  break
+
+else
+
+                  smart_include=
+                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
 
+if test "x$smart_include" = "x"; then
 
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_socklen_t" >&5
-$as_echo "$ac_cv_type_socklen_t" >&6; }
 
-  if test "$ac_cv_type_socklen_t" != "yes"; then
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file=pcap.h
 
-$as_echo "#define socklen_t int" >>confdefs.h
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                        base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
 
-  fi
+    dir=`${DIRNAME} $x 2>/dev/null`
+                exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
 
+                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
 
+eval "smart_include_dir=\"\$smart_include_dir $DIRS\""
 
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint8_t" >&5
-$as_echo_n "checking for uint8_t... " >&6; }
-if ${ac_cv_type_uint8_t+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-   ac_cv_type_uint8_t=no
-      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+  for try in $smart_include_dir /usr/local/include /opt/include; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pcap.h in $try" >&5
+$as_echo_n "checking for pcap.h in $try... " >&6; }
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-#ifdef HAVE_INTTYPES_H
-    #  include <inttypes.h>
-    #endif
-
-    #ifdef HAVE_STDINT_H
-    #  include <stdint.h>
-    #endif
 
+                   #include <pcap.h>
 int
 main ()
 {
-uint8_t foo
+int a = 1;
   ;
   return 0;
 }
 _ACEOF
 if ac_fn_c_try_compile "$LINENO"; then :
-  ac_cv_type_uint8_t=yes
+
+                    smart_include="-isystem $try"
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                    break
+
+else
+
+                    smart_include=
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+  CPPFLAGS="$old_CPPFLAGS"
+fi
 
-
+if test "x$smart_include" != "x"; then
+  eval "ac_cv_header_$ac_safe=yes"
+  CPPFLAGS="$smart_include $old_CPPFLAGS"
+  SMART_CPPFLAGS="$smart_include $SMART_CPPFLAGS"
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uint8_t" >&5
-$as_echo "$ac_cv_type_uint8_t" >&6; }
 
-  if test "$ac_cv_type_uint8_t" != "yes"; then
+  if test "x$ac_cv_header_pcap_h" == "xyes"; then
 
-$as_echo "#define uint8_t unsigned char" >>confdefs.h
+$as_echo "#define HAVE_PCAP_H 1" >>confdefs.h
 
-  fi
 
 
+  else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: pcap headers not found, silently disabling the RADIUS sniffer, and ARP listener. Use --with-pcap-include-dir=<path>." >&5
+$as_echo "$as_me: WARNING: pcap headers not found, silently disabling the RADIUS sniffer, and ARP listener. Use --with-pcap-include-dir=<path>." >&2;}
+  fi
+fi
 
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint16_t" >&5
-$as_echo_n "checking for uint16_t... " >&6; }
-if ${ac_cv_type_uint16_t+:} false; then :
-  $as_echo_n "(cached) " >&6
+if test "x$COLLECTDC_LIBS" = x; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: skipping test for collectd/client.h." >&5
+$as_echo "$as_me: skipping test for collectd/client.h." >&6;}
 else
-   ac_cv_type_uint16_t=no
-      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-#ifdef HAVE_INTTYPES_H
-    #  include <inttypes.h>
-    #endif
+        smart_try_dir="$collectdclient_include_dir"
 
-    #ifdef HAVE_STDINT_H
-    #  include <stdint.h>
-    #endif
 
+ac_safe=`echo "collectd/client.h" | sed 'y%./+-%__pm%'`
+old_CPPFLAGS="$CPPFLAGS"
+smart_include=
+smart_include_dir=
+
+if test "x$smart_try_dir" != "x"; then
+  for try in $smart_try_dir; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for collectd/client.h in $try" >&5
+$as_echo_n "checking for collectd/client.h in $try... " >&6; }
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                   #include <collectd/client.h>
 int
 main ()
 {
-uint16_t foo
+int a = 1;
   ;
   return 0;
 }
 _ACEOF
 if ac_fn_c_try_compile "$LINENO"; then :
-  ac_cv_type_uint16_t=yes
+
+                    smart_include="-isystem $try"
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                    break
+
+else
+
+                    smart_include=
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+  CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_include" = "x"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for collectd/client.h" >&5
+$as_echo_n "checking for collectd/client.h... " >&6; }
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                 #include <collectd/client.h>
+int
+main ()
+{
+int a = 1;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+                  smart_include=" "
+                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                  break
+
+else
 
+                  smart_include=
+                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
 
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uint16_t" >&5
-$as_echo "$ac_cv_type_uint16_t" >&6; }
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
 
-  if test "$ac_cv_type_uint16_t" != "yes"; then
+if test "x$smart_include" = "x"; then
 
-$as_echo "#define uint16_t unsigned short" >>confdefs.h
 
-  fi
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file=collectd/client.h
 
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                        base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
 
+    dir=`${DIRNAME} $x 2>/dev/null`
+                exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
 
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint32_t" >&5
-$as_echo_n "checking for uint32_t... " >&6; }
-if ${ac_cv_type_uint32_t+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-   ac_cv_type_uint32_t=no
-      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-#ifdef HAVE_INTTYPES_H
-    #  include <inttypes.h>
-    #endif
+                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
 
-    #ifdef HAVE_STDINT_H
-    #  include <stdint.h>
-    #endif
+eval "smart_include_dir=\"\$smart_include_dir $DIRS\""
+
+  for try in $smart_include_dir /usr/local/include /opt/include; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for collectd/client.h in $try" >&5
+$as_echo_n "checking for collectd/client.h in $try... " >&6; }
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
 
+                   #include <collectd/client.h>
 int
 main ()
 {
-uint32_t foo
+int a = 1;
   ;
   return 0;
 }
 _ACEOF
 if ac_fn_c_try_compile "$LINENO"; then :
-  ac_cv_type_uint32_t=yes
+
+                    smart_include="-isystem $try"
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                    break
+
+else
+
+                    smart_include=
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+  CPPFLAGS="$old_CPPFLAGS"
+fi
 
-
+if test "x$smart_include" != "x"; then
+  eval "ac_cv_header_$ac_safe=yes"
+  CPPFLAGS="$smart_include $old_CPPFLAGS"
+  SMART_CPPFLAGS="$smart_include $SMART_CPPFLAGS"
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uint32_t" >&5
-$as_echo "$ac_cv_type_uint32_t" >&6; }
 
-  if test "$ac_cv_type_uint32_t" != "yes"; then
+  if test "x$ac_cv_header_collectd_client_h" == "xyes"; then
+
+$as_echo "#define HAVE_COLLECTDC_H 1" >>confdefs.h
 
-$as_echo "#define uint32_t unsigned int" >>confdefs.h
 
+
+  else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: collectdclient headers not found. Use --with-collectdclient-include-dir=<path>." >&5
+$as_echo "$as_me: WARNING: collectdclient headers not found. Use --with-collectdclient-include-dir=<path>." >&2;}
   fi
+fi
 
 
+ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default"
+if test "x$ac_cv_type_off_t" = xyes; then :
 
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint64_t" >&5
-$as_echo_n "checking for uint64_t... " >&6; }
-if ${ac_cv_type_uint64_t+:} false; then :
+else
+
+cat >>confdefs.h <<_ACEOF
+#define off_t long int
+_ACEOF
+
+fi
+
+ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default"
+if test "x$ac_cv_type_pid_t" = xyes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+#define pid_t int
+_ACEOF
+
+fi
+
+ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default"
+if test "x$ac_cv_type_size_t" = xyes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+#define size_t unsigned int
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for uid_t in sys/types.h" >&5
+$as_echo_n "checking for uid_t in sys/types.h... " >&6; }
+if ${ac_cv_type_uid_t+:} false; then :
   $as_echo_n "(cached) " >&6
 else
-   ac_cv_type_uint64_t=no
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "uid_t" >/dev/null 2>&1; then :
+  ac_cv_type_uid_t=yes
+else
+  ac_cv_type_uid_t=no
+fi
+rm -f conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uid_t" >&5
+$as_echo "$ac_cv_type_uid_t" >&6; }
+if test $ac_cv_type_uid_t = no; then
+
+$as_echo "#define uid_t int" >>confdefs.h
+
+
+$as_echo "#define gid_t int" >>confdefs.h
+
+fi
+
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for socklen_t" >&5
+$as_echo_n "checking for socklen_t... " >&6; }
+if ${ac_cv_type_socklen_t+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+   ac_cv_type_socklen_t=no
       cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-#ifdef HAVE_INTTYPES_H
-    #  include <inttypes.h>
+#ifdef HAVE_SYS_TYPES_H
+    #  include <sys/types.h>
     #endif
 
-    #ifdef HAVE_STDINT_H
-    #  include <stdint.h>
+    #ifdef HAVE_SYS_SOCKET_H
+    #  include <sys/socket.h>
     #endif
 
 int
 main ()
 {
-uint64_t foo
+socklen_t foo
   ;
   return 0;
 }
 _ACEOF
 if ac_fn_c_try_compile "$LINENO"; then :
-  ac_cv_type_uint64_t=yes
+  ac_cv_type_socklen_t=yes
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 
 
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uint64_t" >&5
-$as_echo "$ac_cv_type_uint64_t" >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_socklen_t" >&5
+$as_echo "$ac_cv_type_socklen_t" >&6; }
 
-  if test "$ac_cv_type_uint64_t" != "yes"; then
+  if test "$ac_cv_type_socklen_t" != "yes"; then
 
-$as_echo "#define uint64_t unsigned long long" >>confdefs.h
+$as_echo "#define socklen_t int" >>confdefs.h
 
   fi
 
 
 
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sig_t" >&5
-$as_echo_n "checking for sig_t... " >&6; }
-if ${ac_cv_type_sig_t+:} false; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint8_t" >&5
+$as_echo_n "checking for uint8_t... " >&6; }
+if ${ac_cv_type_uint8_t+:} false; then :
   $as_echo_n "(cached) " >&6
 else
-   ac_cv_type_sig_t=no
+   ac_cv_type_uint8_t=no
       cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-#ifdef HAVE_SIGNAL_H
-    #  include <signal.h>
+#ifdef HAVE_INTTYPES_H
+    #  include <inttypes.h>
+    #endif
+
+    #ifdef HAVE_STDINT_H
+    #  include <stdint.h>
     #endif
 
 int
 main ()
 {
-sig_t foo
+uint8_t foo
   ;
   return 0;
 }
 _ACEOF
 if ac_fn_c_try_compile "$LINENO"; then :
-  ac_cv_type_sig_t=yes
+  ac_cv_type_uint8_t=yes
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 
 
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_sig_t" >&5
-$as_echo "$ac_cv_type_sig_t" >&6; }
-
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uint8_t" >&5
+$as_echo "$ac_cv_type_uint8_t" >&6; }
+
+  if test "$ac_cv_type_uint8_t" != "yes"; then
+
+$as_echo "#define uint8_t unsigned char" >>confdefs.h
+
+  fi
+
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint16_t" >&5
+$as_echo_n "checking for uint16_t... " >&6; }
+if ${ac_cv_type_uint16_t+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+   ac_cv_type_uint16_t=no
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef HAVE_INTTYPES_H
+    #  include <inttypes.h>
+    #endif
+
+    #ifdef HAVE_STDINT_H
+    #  include <stdint.h>
+    #endif
+
+int
+main ()
+{
+uint16_t foo
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_type_uint16_t=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uint16_t" >&5
+$as_echo "$ac_cv_type_uint16_t" >&6; }
+
+  if test "$ac_cv_type_uint16_t" != "yes"; then
+
+$as_echo "#define uint16_t unsigned short" >>confdefs.h
+
+  fi
+
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint32_t" >&5
+$as_echo_n "checking for uint32_t... " >&6; }
+if ${ac_cv_type_uint32_t+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+   ac_cv_type_uint32_t=no
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef HAVE_INTTYPES_H
+    #  include <inttypes.h>
+    #endif
+
+    #ifdef HAVE_STDINT_H
+    #  include <stdint.h>
+    #endif
+
+int
+main ()
+{
+uint32_t foo
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_type_uint32_t=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uint32_t" >&5
+$as_echo "$ac_cv_type_uint32_t" >&6; }
+
+  if test "$ac_cv_type_uint32_t" != "yes"; then
+
+$as_echo "#define uint32_t unsigned int" >>confdefs.h
+
+  fi
+
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint64_t" >&5
+$as_echo_n "checking for uint64_t... " >&6; }
+if ${ac_cv_type_uint64_t+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+   ac_cv_type_uint64_t=no
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef HAVE_INTTYPES_H
+    #  include <inttypes.h>
+    #endif
+
+    #ifdef HAVE_STDINT_H
+    #  include <stdint.h>
+    #endif
+
+int
+main ()
+{
+uint64_t foo
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_type_uint64_t=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uint64_t" >&5
+$as_echo "$ac_cv_type_uint64_t" >&6; }
+
+  if test "$ac_cv_type_uint64_t" != "yes"; then
+
+$as_echo "#define uint64_t unsigned long long" >>confdefs.h
+
+  fi
+
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sig_t" >&5
+$as_echo_n "checking for sig_t... " >&6; }
+if ${ac_cv_type_sig_t+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+   ac_cv_type_sig_t=no
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef HAVE_SIGNAL_H
+    #  include <signal.h>
+    #endif
+
+int
+main ()
+{
+sig_t foo
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_type_sig_t=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_sig_t" >&5
+$as_echo "$ac_cv_type_sig_t" >&6; }
+
   if test "$ac_cv_type_sig_t" != "yes"; then
 
 $as_echo "#define sig_t void(*sig_t)(int)" >>confdefs.h
@@ -8247,528 +9414,251 @@ if test "x$developer" = "xyes"; then
 $as_echo "$as_me: Setting additional developer CFLAGS" >&6;}
 
 
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the compiler flag \"-Wdocumentation\"" >&5
-$as_echo_n "checking for the compiler flag \"-Wdocumentation\"... " >&6; }
-if ${ax_cv_cc_wdocumentation_flag+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-
-
-    CFLAGS_SAVED=$CFLAGS
-    CFLAGS="$CFLAGS -Werror -Wdocumentation"
-
-    ac_ext=c
-ac_cpp='$CPP $CPPFLAGS'
-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_c_compiler_gnu
-
-    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-
-int
-main ()
-{
-return 0;
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-  ax_cv_cc_wdocumentation_flag="yes"
-else
-  ax_cv_cc_wdocumentation_flag="no"
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-    ac_ext=c
-ac_cpp='$CPP $CPPFLAGS'
-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_c_compiler_gnu
-
-
-    CFLAGS="$CFLAGS_SAVED"
-
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cc_wdocumentation_flag" >&5
-$as_echo "$ax_cv_cc_wdocumentation_flag" >&6; }
-
-  if test "x$ax_cv_cc_wdocumentation_flag" = "xyes"; then
-    devflags="-Wdocumentation"
-  fi
-
-  if test "x$GCC" = "xyes"; then
-    devflags="$devflags -Wshadow -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wnested-externs -W -Wredundant-decls -Wundef -Wformat-y2k -Wno-format-extra-args -Wno-format-zero-length -Wno-cast-align -Wformat-nonliteral -Wformat-security -Wformat=2 -DWITH_VERIFY_PTR=1"
-    INSTALLSTRIP=""
-  fi
-
-  { $as_echo "$as_me:${as_lineno-$LINENO}: Developer CFLAGS are \"$devflags\"" >&5
-$as_echo "$as_me: Developer CFLAGS are \"$devflags\"" >&6;}
-
-  CFLAGS="$CFLAGS $devflags"
-        if test "x$EXPERIMENTAL" != "xno"; then
-    { $as_echo "$as_me:${as_lineno-$LINENO}: is developer build, enabling experimental modules implicitly, disable with --without-experimental-modules" >&5
-$as_echo "$as_me: is developer build, enabling experimental modules implicitly, disable with --without-experimental-modules" >&6;}
-    EXPERIMENTAL=yes
-  fi
-else
-  devflags=""
-  CFLAGS="$CFLAGS -DNDEBUG"
-  INSTALLSTRIP=""
-fi
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if building with -DNDEBUG" >&5
-$as_echo_n "checking if building with -DNDEBUG... " >&6; }
-if echo "$CFLAGS" | grep '\-DNDEBUG' > /dev/null; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-
-$as_echo "#define WITH_NDEBUG 1" >>confdefs.h
-
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-fi
-
-export EXPERIMENTAL
-
-if test -d $srcdir/.git -a "x$GIT" = "xyes"; then
-  RADIUSD_VERSION_COMMIT=`git log --pretty=format:'%h' -n 1`
-
-cat >>confdefs.h <<_ACEOF
-#define RADIUSD_VERSION_COMMIT ${RADIUSD_VERSION_COMMIT}
-_ACEOF
-
-fi
-
-
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __thread support in compiler" >&5
-$as_echo_n "checking for __thread support in compiler... " >&6; }
-  if test "$cross_compiling" = yes; then :
-  have_tls=no
-else
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-
-        static __thread int val;
-        int main(int argc, char **argv) {
-          val = 0;
-          return val;
-        }
-
-
-_ACEOF
-if ac_fn_c_try_run "$LINENO"; then :
-  have_tls=yes
-else
-  have_tls=no
-fi
-rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
-  conftest.$ac_objext conftest.beam conftest.$ac_ext
-fi
-
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_tls" >&5
-$as_echo "$have_tls" >&6; }
-  if test "x$have_tls" = "xyes"; then
-
-$as_echo "#define HAVE_THREAD_TLS 1" >>confdefs.h
-
-  fi
-
-
-
-talloc_include_dir=
-
-# Check whether --with-talloc-include-dir was given.
-if test "${with_talloc_include_dir+set}" = set; then :
-  withval=$with_talloc_include_dir; case "$withval" in
-    no)
-      as_fn_error $? "Need talloc-include-dir" "$LINENO" 5
-      ;;
-    yes)
-      ;;
-    *)
-      talloc_include_dir="$withval"
-      ;;
-  esac
-fi
-
-
-talloc_lib_dir=
-
-# Check whether --with-talloc-lib-dir was given.
-if test "${with_talloc_lib_dir+set}" = set; then :
-  withval=$with_talloc_lib_dir; case "$withval" in
-    no)
-      as_fn_error $? "Need talloc-lib-dir" "$LINENO" 5
-      ;;
-    yes)
-      ;;
-    *)
-      talloc_lib_dir="$withval"
-      ;;
-  esac
-fi
-
-
-pcap_include_dir=
-
-# Check whether --with-pcap-include-dir was given.
-if test "${with_pcap_include_dir+set}" = set; then :
-  withval=$with_pcap_include_dir; case "$withval" in
-    no)
-      as_fn_error $? "Need pcap-include-dir" "$LINENO" 5
-      ;;
-    yes)
-      ;;
-    *)
-      pcap_include_dir="$withval"
-      ;;
-  esac
-fi
-
-
-pcap_lib_dir=
-
-# Check whether --with-pcap-lib-dir was given.
-if test "${with_pcap_lib_dir+set}" = set; then :
-  withval=$with_pcap_lib_dir; case "$withval" in
-    no)
-      as_fn_error $? "Need pcap-lib-dir" "$LINENO" 5
-      ;;
-    yes)
-      ;;
-    *)
-      pcap_lib_dir="$withval"
-      ;;
-  esac
-fi
-
-
-smart_try_dir="$talloc_include_dir"
-
-
-ac_safe=`echo "talloc.h" | sed 'y%./+-%__pm%'`
-old_CFLAGS="$CFLAGS"
-smart_include=
-smart_include_dir=
-
-if test "x$smart_try_dir" != "x"; then
-  for try in $smart_try_dir; do
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for talloc.h in $try" >&5
-$as_echo_n "checking for talloc.h in $try... " >&6; }
-    CFLAGS="$old_CFLAGS -isystem $try"
-    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-
-                   #include <talloc.h>
-int
-main ()
-{
-int a = 1;
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-
-                    smart_include="-isystem $try"
-                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-                    break
-
-else
-
-                    smart_include=
-                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-  done
-  CFLAGS="$old_CFLAGS"
-fi
-
-if test "x$smart_include" = "x"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for talloc.h" >&5
-$as_echo_n "checking for talloc.h... " >&6; }
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-
-                 #include <talloc.h>
-int
-main ()
-{
-int a = 1;
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-
-                  smart_include=" "
-                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-                  break
-
-else
-
-                  smart_include=
-                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-
-if test "x$smart_include" = "x"; then
-
-
-if test "x$LOCATE" != "x"; then
-        DIRS=
-  file=talloc.h
-
-  for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
-    if test "x$x" = "x$base"; then
-      continue;
-    fi
-
-    dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
-    if test "x$exclude" != "x"; then
-      continue
-    fi
-
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
-    if test "x$already" = "x"; then
-      DIRS="$DIRS $dir"
-    fi
-  done
-fi
-
-eval "smart_include_dir=\"\$smart_include_dir $DIRS\""
-
-  for try in $smart_include_dir /usr/local/include /opt/include; do
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for talloc.h in $try" >&5
-$as_echo_n "checking for talloc.h in $try... " >&6; }
-    CFLAGS="$old_CFLAGS -isystem $try"
-    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-
-                   #include <talloc.h>
-int
-main ()
-{
-int a = 1;
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-
-                    smart_include="-isystem $try"
-                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-                    break
-
-else
-
-                    smart_include=
-                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-  done
-  CFLAGS="$old_CFLAGS"
-fi
-
-if test "x$smart_include" != "x"; then
-  eval "ac_cv_header_$ac_safe=yes"
-  CFLAGS="$old_CFLAGS $smart_include"
-  SMART_CFLAGS="$SMART_CFLAGS $smart_include"
-fi
-
-if test "x$ac_cv_header_talloc_h" != "xyes"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: talloc headers not found. Use --with-talloc-include-dir=<path>." >&5
-$as_echo "$as_me: WARNING: talloc headers not found. Use --with-talloc-include-dir=<path>." >&2;}
-  as_fn_error $? "FreeRADIUS requires libtalloc" "$LINENO" 5
-fi
-
-smart_try_dir="$talloc_lib_dir"
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the compiler flag \"-Wdocumentation\"" >&5
+$as_echo_n "checking for the compiler flag \"-Wdocumentation\"... " >&6; }
+if ${ax_cv_cc_wdocumentation_flag+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
 
 
-sm_lib_safe=`echo "talloc" | sed 'y%./+-%__p_%'`
-sm_func_safe=`echo "_talloc" | sed 'y%./+-%__p_%'`
+    CFLAGS_SAVED=$CFLAGS
+    CFLAGS="$CFLAGS -Werror -Wdocumentation"
 
-old_LIBS="$LIBS"
-smart_lib=
-smart_lib_dir=
+    ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
-if test "x$smart_try_dir" != "x"; then
-  for try in $smart_try_dir; do
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _talloc in -ltalloc in $try" >&5
-$as_echo_n "checking for _talloc in -ltalloc in $try... " >&6; }
-    LIBS="-L$try -ltalloc $old_LIBS -Wl,-rpath,$try"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-extern char _talloc();
+
 int
 main ()
 {
-_talloc()
+return 0;
   ;
   return 0;
 }
 _ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"; then :
+  ax_cv_cc_wdocumentation_flag="yes"
+else
+  ax_cv_cc_wdocumentation_flag="no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
-                smart_lib="-L$try -ltalloc -Wl,-rpath,$try"
-                { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+
+    CFLAGS="$CFLAGS_SAVED"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cc_wdocumentation_flag" >&5
+$as_echo "$ax_cv_cc_wdocumentation_flag" >&6; }
+
+  if test "x$ax_cv_cc_wdocumentation_flag" = "xyes"; then
+    devflags="-Wdocumentation"
+  fi
+
+  if test "x$GCC" = "xyes"; then
+    devflags="$devflags -Wshadow -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wnested-externs -W -Wredundant-decls -Wundef -Wformat-y2k -Wno-format-extra-args -Wno-format-zero-length -Wno-cast-align -Wformat-nonliteral -Wformat-security -Wformat=2 -DWITH_VERIFY_PTR=1"
+    INSTALLSTRIP=""
+  fi
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: Developer CFLAGS are \"$devflags\"" >&5
+$as_echo "$as_me: Developer CFLAGS are \"$devflags\"" >&6;}
+
+  CFLAGS="$CFLAGS $devflags"
+        if test "x$EXPERIMENTAL" != "xno"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: is developer build, enabling experimental modules implicitly, disable with --without-experimental-modules" >&5
+$as_echo "$as_me: is developer build, enabling experimental modules implicitly, disable with --without-experimental-modules" >&6;}
+    EXPERIMENTAL=yes
+  fi
+else
+  devflags=""
+  CFLAGS="$CFLAGS -DNDEBUG"
+  INSTALLSTRIP=""
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if building with -DNDEBUG" >&5
+$as_echo_n "checking if building with -DNDEBUG... " >&6; }
+if echo "$CFLAGS" | grep '\-DNDEBUG' > /dev/null; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
-                break
+
+$as_echo "#define WITH_NDEBUG 1" >>confdefs.h
 
 else
   { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
 fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext conftest.$ac_ext
-  done
-  LIBS="$old_LIBS"
+
+export EXPERIMENTAL
+
+if test -d $srcdir/.git -a "x$GIT" = "xyes"; then
+  RADIUSD_VERSION_COMMIT=`git log --pretty=format:'%h' -n 1`
+
+cat >>confdefs.h <<_ACEOF
+#define RADIUSD_VERSION_COMMIT ${RADIUSD_VERSION_COMMIT}
+_ACEOF
+
 fi
 
-if test "x$smart_lib" = "x"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _talloc in -ltalloc" >&5
-$as_echo_n "checking for _talloc in -ltalloc... " >&6; }
-  LIBS="-ltalloc $old_LIBS"
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __thread support in compiler" >&5
+$as_echo_n "checking for __thread support in compiler... " >&6; }
+  if test "$cross_compiling" = yes; then :
+  have_tls=no
+else
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-extern char _talloc();
-int
-main ()
-{
-_talloc()
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-ltalloc"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
+        static __thread int val;
+        int main(int argc, char **argv) {
+          val = 0;
+          return val;
+        }
 
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+  have_tls=yes
 else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+  have_tls=no
 fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext conftest.$ac_ext
-  LIBS="$old_LIBS"
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
 fi
 
-if test "x$smart_lib" = "x"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_tls" >&5
+$as_echo "$have_tls" >&6; }
+  if test "x$have_tls" = "xyes"; then
 
+$as_echo "#define TLS_STORAGE_CLASS __thread" >>confdefs.h
 
-if test "x$LOCATE" != "x"; then
-        DIRS=
-  file=libtalloc${libltdl_cv_shlibext}
+  fi
 
-  for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
-    if test "x$x" = "x$base"; then
-      continue;
-    fi
+  if test "x$have_tls" = "xno"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __declspec(thread) support in compiler" >&5
+$as_echo_n "checking for __declspec(thread) support in compiler... " >&6; }
+    if test "$cross_compiling" = yes; then :
+  have_tls=no
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
 
-    dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
-    if test "x$exclude" != "x"; then
-      continue
-    fi
+          static _Thread_local int val;
+          int main(int argc, char **argv) {
+            val = 0;
+            return val;
+          }
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
-    if test "x$already" = "x"; then
-      DIRS="$DIRS $dir"
-    fi
-  done
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+  have_tls=yes
+else
+  have_tls=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
 fi
 
-eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_tls" >&5
+$as_echo "$have_tls" >&6; }
+    if test "x$have_tls" = "xyes"; then
 
+$as_echo "#define TLS_STORAGE_CLASS __declspec(thread)" >>confdefs.h
 
+    fi
+  fi
+  if test "x$have_tls" = "xno"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _Thread_local support in compiler" >&5
+$as_echo_n "checking for _Thread_local support in compiler... " >&6; }
+    if test "$cross_compiling" = yes; then :
+  have_tls=no
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
 
-if test "x$LOCATE" != "x"; then
-        DIRS=
-  file=libtalloc.a
+          static _Thread_local int val;
+          int main(int argc, char **argv) {
+            val = 0;
+            return val;
+          }
 
-  for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
-    if test "x$x" = "x$base"; then
-      continue;
-    fi
 
-    dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
-    if test "x$exclude" != "x"; then
-      continue
-    fi
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+  have_tls=yes
+else
+  have_tls=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_tls" >&5
+$as_echo "$have_tls" >&6; }
+    if test "x$have_tls" = "xyes"; then
+
+$as_echo "#define TLS_STORAGE_CLASS _Thread_local" >>confdefs.h
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
-    if test "x$already" = "x"; then
-      DIRS="$DIRS $dir"
     fi
-  done
-fi
+  fi
 
-eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
-  for try in $smart_lib_dir /usr/local/lib /opt/lib; do
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _talloc in -ltalloc in $try" >&5
-$as_echo_n "checking for _talloc in -ltalloc in $try... " >&6; }
-    LIBS="-L$try -ltalloc $old_LIBS -Wl,-rpath,$try"
-    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for talloc_set_memlimit in -ltalloc" >&5
+$as_echo_n "checking for talloc_set_memlimit in -ltalloc... " >&6; }
+if ${ac_cv_lib_talloc_talloc_set_memlimit+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ltalloc  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-extern char _talloc();
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char talloc_set_memlimit ();
 int
 main ()
 {
-_talloc()
+return talloc_set_memlimit ();
   ;
   return 0;
 }
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
-
-                 smart_lib="-L$try -ltalloc -Wl,-rpath,$try"
-                 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-                 break
-
+  ac_cv_lib_talloc_talloc_set_memlimit=yes
 else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+  ac_cv_lib_talloc_talloc_set_memlimit=no
 fi
 rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
-  done
-  LIBS="$old_LIBS"
+LIBS=$ac_check_lib_save_LIBS
 fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_talloc_talloc_set_memlimit" >&5
+$as_echo "$ac_cv_lib_talloc_talloc_set_memlimit" >&6; }
+if test "x$ac_cv_lib_talloc_talloc_set_memlimit" = xyes; then :
+
+
+$as_echo "#define HAVE_TALLOC_SET_MEMLIMIT 1" >>confdefs.h
+
 
-if test "x$smart_lib" != "x"; then
-  eval "ac_cv_lib_${sm_lib_safe}_${sm_func_safe}=yes"
-  LIBS="$smart_lib $old_LIBS"
-  SMART_LIBS="$smart_lib $SMART_LIBS"
-fi
 
-if test "x$ac_cv_lib_talloc__talloc" != "xyes"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: talloc library not found. Use --with-talloc-lib-dir=<path>." >&5
-$as_echo "$as_me: WARNING: talloc library not found. Use --with-talloc-lib-dir=<path>." >&2;}
-  as_fn_error $? "FreeRADIUS requires libtalloc" "$LINENO" 5
 fi
 
+
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for crypt in -lcrypt" >&5
 $as_echo_n "checking for crypt in -lcrypt... " >&6; }
 if ${ac_cv_lib_crypt_crypt+:} false; then :
@@ -8892,7 +9782,7 @@ execinfo_include_dir=
 if test "${with_execinfo_include_dir+set}" = set; then :
   withval=$with_execinfo_include_dir;  case "$withval" in
     no)
-       as_fn_error $? "Need execinfo-include-dir" "$LINENO" 5
+        as_fn_error $? "Need execinfo-include-dir" "$LINENO" 5
        ;;
     yes)
        ;;
@@ -8908,7 +9798,7 @@ smart_try_dir=$execinfo_include_dir
 
 
 ac_safe=`echo "execinfo.h" | sed 'y%./+-%__pm%'`
-old_CFLAGS="$CFLAGS"
+old_CPPFLAGS="$CPPFLAGS"
 smart_include=
 smart_include_dir=
 
@@ -8916,7 +9806,7 @@ if test "x$smart_try_dir" != "x"; then
   for try in $smart_try_dir; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for execinfo.h in $try" >&5
 $as_echo_n "checking for execinfo.h in $try... " >&6; }
-    CFLAGS="$old_CFLAGS -isystem $try"
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -8945,7 +9835,7 @@ $as_echo "no" >&6; }
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
   done
-  CFLAGS="$old_CFLAGS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_include" = "x"; then
@@ -9011,7 +9901,7 @@ eval "smart_include_dir=\"\$smart_include_dir $DIRS\""
   for try in $smart_include_dir /usr/local/include /opt/include; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for execinfo.h in $try" >&5
 $as_echo_n "checking for execinfo.h in $try... " >&6; }
-    CFLAGS="$old_CFLAGS -isystem $try"
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -9040,13 +9930,13 @@ $as_echo "no" >&6; }
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
   done
-  CFLAGS="$old_CFLAGS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_include" != "x"; then
   eval "ac_cv_header_$ac_safe=yes"
-  CFLAGS="$old_CFLAGS $smart_include"
-  SMART_CFLAGS="$SMART_CFLAGS $smart_include"
+  CPPFLAGS="$smart_include $old_CPPFLAGS"
+  SMART_CPPFLAGS="$smart_include $SMART_CPPFLAGS"
 fi
 
 if test "x$ac_cv_header_execinfo_h" = "xyes"; then
@@ -9057,14 +9947,17 @@ sm_lib_safe=`echo "execinfo" | sed 'y%./+-%__p_%'`
 sm_func_safe=`echo "backtrace_symbols" | sed 'y%./+-%__p_%'`
 
 old_LIBS="$LIBS"
+old_CPPFLAGS="$CPPFLAGS"
 smart_lib=
+smart_ldflags=
 smart_lib_dir=
 
 if test "x$smart_try_dir" != "x"; then
   for try in $smart_try_dir; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for backtrace_symbols in -lexecinfo in $try" >&5
 $as_echo_n "checking for backtrace_symbols in -lexecinfo in $try... " >&6; }
-    LIBS="-L$try -lexecinfo $old_LIBS -Wl,-rpath,$try"
+    LIBS="-lexecinfo $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 extern char backtrace_symbols();
@@ -9078,7 +9971,8 @@ backtrace_symbols()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-                smart_lib="-L$try -lexecinfo -Wl,-rpath,$try"
+                smart_lib="-lexecinfo"
+                smart_ldflags="-L$try -Wl,-rpath,$try"
                 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
                 break
@@ -9091,6 +9985,7 @@ rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
   done
   LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_lib" = "x"; then
@@ -9182,7 +10077,8 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
   for try in $smart_lib_dir /usr/local/lib /opt/lib; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for backtrace_symbols in -lexecinfo in $try" >&5
 $as_echo_n "checking for backtrace_symbols in -lexecinfo in $try... " >&6; }
-    LIBS="-L$try -lexecinfo $old_LIBS -Wl,-rpath,$try"
+    LIBS="-lexecinfo $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 extern char backtrace_symbols();
@@ -9196,7 +10092,8 @@ backtrace_symbols()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-                 smart_lib="-L$try -lexecinfo -Wl,-rpath,$try"
+                 smart_lib="-lexecinfo"
+                 smart_ldflags="-L$try -Wl,-rpath,$try"
                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
                  break
@@ -9209,12 +10106,13 @@ rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
   done
   LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_lib" != "x"; then
   eval "ac_cv_lib_${sm_lib_safe}_${sm_func_safe}=yes"
-  LIBS="$smart_lib $old_LIBS"
-  SMART_LIBS="$smart_lib $SMART_LIBS"
+  LIBS="$smart_ldflags $smart_lib $old_LIBS"
+  SMART_LIBS="$smart_ldflags $smart_lib $SMART_LIBS"
 fi
 
   if test "x$ac_cv_lib_execinfo_backtrace_symbols" != "xyes"; then
@@ -9239,13 +10137,12 @@ if ac_fn_c_try_link "$LINENO"; then :
 
         { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
-        ac_cv_lib_execinfo_backtrace_symbols=yes
+        ac_cv_lib_execinfo_backtrace_symbols="yes"
 
 else
 
         { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
-        ac_cv_lib_execinfo_backtrace_symbols=no
 
 
 fi
@@ -9266,14 +10163,14 @@ pcre_lib_dir=
 if test "${with_pcre_lib_dir+set}" = set; then :
   withval=$with_pcre_lib_dir;  case "$withval" in
     no)
-        as_fn_error $? "Need pcre-lib-dir" "$LINENO" 5
+       as_fn_error $? "Need pcre-lib-dir" "$LINENO" 5
        ;;
     yes)
        ;;
     *)
        pcre_lib_dir="$withval"
        ;;
-    esac
+  esac
 
 fi
 
@@ -9291,7 +10188,7 @@ if test "${with_pcre_include_dir+set}" = set; then :
     *)
        pcre_include_dir="$withval"
        ;;
-    esac
+  esac
 
 fi
 
@@ -9304,7 +10201,7 @@ smart_try_dir=$pcre_include_dir
 
 
 ac_safe=`echo "pcreposix.h" | sed 'y%./+-%__pm%'`
-old_CFLAGS="$CFLAGS"
+old_CPPFLAGS="$CPPFLAGS"
 smart_include=
 smart_include_dir=
 
@@ -9312,7 +10209,7 @@ if test "x$smart_try_dir" != "x"; then
   for try in $smart_try_dir; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pcreposix.h in $try" >&5
 $as_echo_n "checking for pcreposix.h in $try... " >&6; }
-    CFLAGS="$old_CFLAGS -isystem $try"
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -9341,7 +10238,7 @@ $as_echo "no" >&6; }
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
   done
-  CFLAGS="$old_CFLAGS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_include" = "x"; then
@@ -9407,7 +10304,7 @@ eval "smart_include_dir=\"\$smart_include_dir $DIRS\""
   for try in $smart_include_dir /usr/local/include /opt/include; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pcreposix.h in $try" >&5
 $as_echo_n "checking for pcreposix.h in $try... " >&6; }
-    CFLAGS="$old_CFLAGS -isystem $try"
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -9436,13 +10333,13 @@ $as_echo "no" >&6; }
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
   done
-  CFLAGS="$old_CFLAGS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_include" != "x"; then
   eval "ac_cv_header_$ac_safe=yes"
-  CFLAGS="$old_CFLAGS $smart_include"
-  SMART_CFLAGS="$SMART_CFLAGS $smart_include"
+  CPPFLAGS="$smart_include $old_CPPFLAGS"
+  SMART_CPPFLAGS="$smart_include $SMART_CPPFLAGS"
 fi
 
 if test "x$ac_cv_header_pcreposix_h" = "xyes"; then
@@ -9462,7 +10359,7 @@ else
 
 
 ac_safe=`echo "regex.h" | sed 'y%./+-%__pm%'`
-old_CFLAGS="$CFLAGS"
+old_CPPFLAGS="$CPPFLAGS"
 smart_include=
 smart_include_dir=
 
@@ -9470,7 +10367,7 @@ if test "x$smart_try_dir" != "x"; then
   for try in $smart_try_dir; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for regex.h in $try" >&5
 $as_echo_n "checking for regex.h in $try... " >&6; }
-    CFLAGS="$old_CFLAGS -isystem $try"
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -9499,7 +10396,7 @@ $as_echo "no" >&6; }
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
   done
-  CFLAGS="$old_CFLAGS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_include" = "x"; then
@@ -9565,7 +10462,7 @@ eval "smart_include_dir=\"\$smart_include_dir $DIRS\""
   for try in $smart_include_dir /usr/local/include /opt/include; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for regex.h in $try" >&5
 $as_echo_n "checking for regex.h in $try... " >&6; }
-    CFLAGS="$old_CFLAGS -isystem $try"
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -9594,13 +10491,13 @@ $as_echo "no" >&6; }
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
   done
-  CFLAGS="$old_CFLAGS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_include" != "x"; then
   eval "ac_cv_header_$ac_safe=yes"
-  CFLAGS="$old_CFLAGS $smart_include"
-  SMART_CFLAGS="$SMART_CFLAGS $smart_include"
+  CPPFLAGS="$smart_include $old_CPPFLAGS"
+  SMART_CPPFLAGS="$smart_include $SMART_CPPFLAGS"
 fi
 
   if test "x$ac_cv_header_regex_h" = "xyes"; then
index fa87b10..b33baf2 100644 (file)
@@ -115,45 +115,6 @@ dnl #
 AC_CANONICAL_SYSTEM
 
 dnl #
-dnl #  As of OSX 10.9 (Mavericks), /usr is no longer populated with the
-dnl #  standard set of headers and libraries, instead were meant to use
-dnl #  one of the SDKs which contains system headers and libraries for
-dnl #  different versions of OSX and iOS.
-dnl #
-case "$host" in
-  *-darwin*)
-    dnl #
-    dnl #  The version of GCC apple ships with Mavericks works out of the
-    dnl #  box, and presumably selects the highest version SDK for OSX.
-    dnl #
-    AC_MSG_CHECKING([if cc is apple llvm])
-    if ! $CC --version 2>&1 | grep -I 'Apple LLVM' > /dev/null; then
-      AC_MSG_RESULT(no)
-      AC_CHECK_PROG(SW_VERS, sw_vers, yes, no)
-      AC_CHECK_PROG(XCODEBUILD, xcodebuild, yes, no)
-
-      if test "x$SW_VERS" = "xyes" && test "x$XCODEBUILD" = "xyes"; then
-        AC_MSG_NOTICE([determining OSX SDK path])
-        osx_sdk_path=$(xcodebuild -version -sdk macosx$(sw_vers -productVersion | egrep -o '^[[0-9]]+\.[[0-9]]+') Path)
-        AC_MSG_RESULT([$osx_sdk_path])
-
-        dnl #
-        dnl #  We need to export these, else the child configure scripts all fail
-        dnl #  their compiler checks.
-        dnl #
-        export CFLAGS="$CFLAGS --sysroot=$osx_sdk_path"
-        export CPPFLAGS="$CPPFLAGS --sysroot=$osx_sdk_path"
-        export LDFLAGS="$LDFLAGS -L$osx_sdk_path/usr/lib/"
-        DARWIN_CFLAGS="--sysroot=$osx_sdk_path"
-       AC_SUBST(DARWIN_CFLAGS)
-      fi
-    else
-      AC_MSG_RESULT(yes)
-    fi
-    ;;
-esac
-
-dnl #
 dnl #  Check for GNU cc
 dnl #
 AC_PROG_CC
@@ -488,7 +449,8 @@ AC_ARG_WITH(static_modules,
 
 USE_SHARED_LIBS=yes
 AC_ARG_WITH(shared-libs,
-[  --with-shared-libs               build dynamic libraries and link against them. (default=yes)],
+[AS_HELP_STRING([--with-shared-libs ],
+[build dynamic libraries and link against them. (default=yes)])],
 [ case "$withval" in
   no)
     USE_SHARED_LIBS=no
@@ -510,7 +472,8 @@ dnl #  extra argument: --with-experimental-modules
 dnl #
 EXPERIMENTAL=
 AC_ARG_WITH(experimental-modules,
-[  --with-experimental-modules      use experimental and unstable modules. (default=no, unless --enable-developer=yes) ],
+[AS_HELP_STRING([--with-experimental-modules],
+[use experimental and unstable modules. (default=no, unless --enable-developer=yes)])],
 [ case "$withval" in
   yes)
     EXPERIMENTAL=yes
@@ -527,7 +490,7 @@ dnl #  extra argument: --with-udpfromto
 dnl #
 WITH_UDPFROMTO=yes
 AC_ARG_WITH(udpfromto,
-[  --with-udpfromto                 compile in UDPFROMTO support. (default=yes)],
+[  --with-udpfromto        compile in UDPFROMTO support. (default=yes)],
 [ case "$withval" in
   yes)
     WITH_UDPFROMTO=yes
@@ -542,69 +505,73 @@ if test "x$WITH_UDPFROMTO" = "xyes"; then
 fi
 
 dnl #
-dnl #  extra argument: --with-openssl
+dnl #  These next two arguments don't actually do anything.  They're
+dnl #  place holders so that the top-level configure script can tell
+dnl #  the user how to configure lower-level modules
 dnl #
-WITH_OPENSSL=yes
-AC_ARG_WITH(openssl,
-[  --with-openssl                   use OpenSSL. (default=yes)],
+
+dnl #
+dnl #  extra argument: --with-rlm-FOO-lib-dir
+dnl #
+AC_ARG_WITH(rlm-FOO-lib-dir,
+[AS_HELP_STRING([--with-rlm-FOO-lib-dir=DIR],
+[directory in which to look for library files used by module FOO])],
 [ case "$withval" in
-  no)
-    WITH_OPENSSL=no
-    ;;
   *)
-    WITH_OPENSSL=yes
     ;;
   esac ]
 )
 
 dnl #
-dnl #  extra argument: --with-openssl-includes=dir
+dnl #  extra argument: --with-rlm-FOO-include-dir
 dnl #
-OPENSSL_INCLUDE_DIR=
-AC_ARG_WITH(openssl-includes,
-[  --with-openssl-includes=DIR      directory to look for OpenSSL include files in],
+AC_ARG_WITH(rlm-FOO-include-dir,
+[AS_HELP_STRING([--with-rlm-FOO-include-dir=DIR],
+[directory in which to look for include files used by module FOO])],
 [ case "$withval" in
-  *) OPENSSL_INCLUDE_DIR="$withval"
+  *)
     ;;
   esac ]
 )
 
 dnl #
-dnl #  extra argument: --with-openssl-libraries=dir
+dnl #  extra argument: --with-openssl
 dnl #
-OPENSSL_LIB_DIR=
-AC_ARG_WITH(openssl-libraries,
-[  --with-openssl-libraries=DIR     directory to look for OpenSSL library files in],
+WITH_OPENSSL=yes
+AC_ARG_WITH(openssl,
+[  --with-openssl          use OpenSSL. (default=yes)],
 [ case "$withval" in
-  *) OPENSSL_LIB_DIR="$withval"
+  no)
+    WITH_OPENSSL=no
+    ;;
+  *)
+    WITH_OPENSSL=yes
     ;;
   esac ]
 )
 
 dnl #
-dnl #  These next two arguments don't actually do anything.  They're
-dnl #  place holders so that the top-level configure script can tell
-dnl #  the user how to configure lower-level modules
-dnl #
-
-dnl #
-dnl #  extra argument: --with-rlm-FOO-lib-dir
+dnl #  extra argument: --with-openssl-lib-dir=dir
 dnl #
-AC_ARG_WITH(rlm-FOO-lib-dir,
-[  --with-rlm-FOO-lib-dir=DIR       directory to look for library files used by module FOO in],
+openssl_lib_dir=
+AC_ARG_WITH(openssl-lib-dir,
+[AS_HELP_STRING([--with-openssl-lib-dir=DIR],
+[directory to look for OpenSSL library files])],
 [ case "$withval" in
-  *)
+  *) openssl_lib_dir="$withval"
     ;;
   esac ]
 )
 
 dnl #
-dnl #  extra argument: --with-rlm-FOO-include-dir
+dnl #  extra argument: --with-openssl-includes=dir
 dnl #
-AC_ARG_WITH(rlm-FOO-include-dir,
-[  --with-rlm-FOO-include-dir=DIR   directory to look for include files used by module FOO in],
+openssl_include_dir=
+AC_ARG_WITH(openssl-include-dir,
+[AS_HELP_STRING([--with-openssl-include-dir=DIR],
+[directory to look for OpenSSL include files])],
 [ case "$withval" in
-  *)
+  *) openssl_include_dir="$withval"
     ;;
   esac ]
 )
@@ -650,6 +617,52 @@ dnl #  2. Checks for libraries
 dnl #
 dnl #############################################################
 
+dnl Check for talloc
+dnl extra argument: --with-talloc-lib-dir=DIR
+talloc_lib_dir=
+AC_ARG_WITH(talloc-lib-dir,
+  [AS_HELP_STRING([--with-talloc-lib-dir=DIR],
+  [directory in which to look for talloc library files])],
+  [case "$withval" in
+    no)
+      AC_MSG_ERROR([Need talloc-lib-dir])
+      ;;
+    yes)
+      ;;
+    *)
+      talloc_lib_dir="$withval"
+      ;;
+  esac])
+
+dnl extra argument: --with-talloc-include-dir=DIR
+talloc_include_dir=
+AC_ARG_WITH(talloc-include-dir,
+  [AS_HELP_STRING([--with-talloc-include-dir=DIR],
+  [directory in which to look for talloc include files])],
+  [case "$withval" in
+    no)
+      AC_MSG_ERROR([Need talloc-include-dir])
+      ;;
+    yes)
+      ;;
+    *)
+      talloc_include_dir="$withval"
+      ;;
+  esac])
+
+smart_try_dir="$talloc_lib_dir"
+FR_SMART_CHECK_LIB(talloc, _talloc)
+if test "x$ac_cv_lib_talloc__talloc" != "xyes"; then
+  AC_MSG_WARN([talloc library not found. Use --with-talloc-lib-dir=<path>.])
+  AC_MSG_ERROR([FreeRADIUS requires libtalloc])
+fi
+
+TALLOC_LIBS="${smart_lib}"
+TALLOC_LDFLAGS="${smart_ldflags}"
+AC_SUBST(TALLOC_LIBS)
+AC_SUBST(TALLOC_LDFLAGS)
+LIBS="$old_LIBS"
+
 dnl #
 dnl #  If using pthreads, check for -lpthread (posix) or -lc_r (*BSD)
 dnl #
@@ -659,7 +672,11 @@ if test "x$WITH_THREADS" = "xyes"; then
     CFLAGS="$CFLAGS -mt"
   fi
 
-  AC_CHECK_HEADERS(pthread.h, [], [ WITH_THREADS="no" ])
+  AC_CHECK_HEADERS(pthread.h, [],
+    [
+      WITH_THREADS="no"
+      fail=[pthread.h]
+    ])
 
   dnl #
   dnl #  pthread stuff is usually in -lpthread
@@ -674,12 +691,26 @@ if test "x$WITH_THREADS" = "xyes"; then
       LIBS="-lpthread $LIBS"
     ],
     [
+      dnl #
+      dnl # -pthread is not a typo, it's a GCC option which sets additional flags required
+      dnl # for multithreading with the pthreads library.
+      dnl #
       AC_CHECK_LIB(c_r, pthread_create,
         [ CFLAGS="$CFLAGS -pthread -D_THREAD_SAFE" ],
-        [ WITH_THREADS="no" ]
+        [
+          WITH_THREADS="no"
+          fail=[-lpthread]
+        ]
       )
     ]
   )
+
+  if test "x$WITH_THREADS" != "xyes"; then
+    AC_MSG_WARN([silently not building with thread support.])
+    AC_MSG_WARN([FAILURE: thread support requires: $fail.])
+  else
+    AC_DEFINE(WITH_THREADS, [1], [define if you want thread support])
+  fi
 fi
 
 dnl #
@@ -706,10 +737,6 @@ else
   )
 fi
 
-if test "x$WITH_THREADS" = "xyes"; then
-  AC_DEFINE(WITH_THREADS, [1], [define if you want thread support])
-fi
-
 dnl #
 dnl #  Check if we need -lsocket
 dnl #
@@ -736,18 +763,103 @@ AC_CHECK_LIB(ws2_32, htonl)
 dnl #
 dnl #  Check the pcap library for the RADIUS sniffer.
 dnl #
+dnl extra argument: --with-pcap-lib-dir=DIR
+pcap_lib_dir=
+AC_ARG_WITH(pcap-lib-dir,
+  [AS_HELP_STRING([--with-pcap-lib-dir=DIR],
+  [directory in which to look for pcap library files])],
+  [case "$withval" in
+    no)
+      AC_MSG_ERROR([Need pcap-lib-dir])
+      ;;
+    yes)
+      ;;
+    *)
+      pcap_lib_dir="$withval"
+      ;;
+  esac])
+
+dnl extra argument: --with-pcap-include-dir=DIR
+pcap_include_dir=
+AC_ARG_WITH(pcap-include-dir,
+  [AS_HELP_STRING([--with-pcap-include-dir=DIR],
+  [directory in which to look for pcap include files])],
+  [case "$withval" in
+    no)
+      AC_MSG_ERROR([Need pcap-include-dir])
+      ;;
+    yes)
+      ;;
+    *)
+      pcap_include_dir="$withval"
+      ;;
+  esac])
+
 smart_try_dir="$pcap_lib_dir"
 FR_SMART_CHECK_LIB(pcap, pcap_open_live)
 if test "x$ac_cv_lib_pcap_pcap_open_live" != "xyes"; then
-  AC_MSG_WARN([pcap library not found. Use --with-pcap-lib-dir=<path>.])
-  AC_MSG_WARN([pcap library not found, silently disabling the RADIUS sniffer and ARP listener.])
+  AC_MSG_WARN([pcap library not found, silently disabling the RADIUS sniffer, and ARP listener.  Use --with-pcap-lib-dir=<path>.])
 else
-  PCAP_LIBS="${smart_lib}"
-  LIBS=$old_LIBS
   AC_DEFINE(HAVE_LIBPCAP, 1,
     [Define to 1 if you have the `pcap' library (-lpcap).]
   )
+
+  AC_CHECK_FUNCS(\
+    pcap_fopen_offline \
+    pcap_dump_fopen \
+    pcap_create \
+    pcap_activate
+  )
+
+  PCAP_LIBS="${smart_lib}"
+  PCAP_LDFLAGS="${smart_ldflags}"
 fi
+dnl Set by FR_SMART_CHECK_LIB
+LIBS="${old_LIBS}"
+
+dnl Check for collectdclient
+dnl extra argument: --with-collectdclient-lib-dir=DIR
+collectdclient_lib_dir=
+AC_ARG_WITH(collectdclient-lib-dir,
+  [AS_HELP_STRING([--with-collectdclient-lib-dir=DIR],
+  [directory in which to look for collectdclient library files])],
+  [case "$withval" in
+    no)
+      AC_MSG_ERROR([Need collectdclient-lib-dir])
+      ;;
+    yes)
+      ;;
+    *)
+      collectdclient_lib_dir="$withval"
+      ;;
+  esac])
+
+dnl extra argument: --with-collectdclient-include-dir=DIR
+collectdclient_include_dir=
+AC_ARG_WITH(collectdclient-include-dir,
+  [AS_HELP_STRING([--with-collectdclient-include-dir=DIR],
+  [directory in which to look for collectdclient include files])],
+  [case "$withval" in
+    no)
+      AC_MSG_ERROR([Need collectdclient-include-dir])
+      ;;
+    yes)
+      ;;
+    *)
+      collectdclient_include_dir="$withval"
+      ;;
+  esac])
+
+smart_try_dir="$collectdclient_lib_dir"
+FR_SMART_CHECK_LIB(collectdclient, lcc_connect)
+if test "x$ac_cv_lib_collectdclient_lcc_connect" != "xyes"; then
+  AC_MSG_WARN([collectdclient library not found. Use --with-collectdclient-lib-dir=<path>.])
+else
+  COLLECTDC_LIBS="${smart_lib}"
+  COLLECTDC_LDFLAGS="${smart_ldflags}"
+fi
+dnl Set by FR_SMART_CHECKLIB
+LIBS="${old_LIBS}"
 
 VL_LIB_READLINE
 
@@ -758,10 +870,19 @@ dnl #
 dnl #############################################################
 
 dnl #
+dnl # Check for talloc header files
+dnl #
+smart_try_dir="$talloc_include_dir"
+FR_SMART_CHECK_INCLUDE([talloc.h])
+if test "x$ac_cv_header_talloc_h" != "xyes"; then
+  AC_MSG_WARN([talloc headers not found. Use --with-talloc-include-dir=<path>.])
+  AC_MSG_ERROR([FreeRADIUS requires libtalloc])
+fi
+
+dnl #
 dnl #  Interix requires us to set -D_ALL_SOURCE, otherwise
 dnl #  getopt will be #included, but won't link.  <sigh>
 dnl #
-dnl #
 case "$host" in
   *-interix*)
     CFLAGS="$CFLAGS -D_ALL_SOURCE"
@@ -782,7 +903,6 @@ AC_CHECK_HEADERS( \
   unistd.h \
   crypt.h \
   errno.h \
-  features.h \
   resource.h \
   sys/resource.h \
   getopt.h \
@@ -817,18 +937,20 @@ AC_CHECK_HEADERS( \
   stddef.h \
   fnmatch.h \
   sia.h \
-  siad.h
+  siad.h \
+  features.h
 )
 
 dnl #
 dnl #  FreeBSD requires sys/socket.h before net/if.h
 dnl #
 AC_CHECK_HEADERS(net/if.h, [], [],
-[
-  #ifdef HAVE_SYS_SOCKET_H
-  #  include <sys/socket.h>
-  #endif
-])
+  [
+    #ifdef HAVE_SYS_SOCKET_H
+    #  include <sys/socket.h>
+    #endif
+  ]
+)
 
 dnl #
 dnl #  other checks which require headers
@@ -847,132 +969,118 @@ dnl #
 dnl #  Were we told to use OpenSSL, if we were and we find an error, call AC_MSG_FAILURE and exit
 dnl #
 if test "x$WITH_OPENSSL" = xyes; then
-  old_LIBS=$LIBS
-  old_LDFLAGS="$LDFLAGS"
-
-  OPENSSL_INCLUDE="-DNO_OPENSSL"
-  OPENSSL_LIBS=
-  if test "x$OPENSSL_LIB_DIR" != "x"; then
-    LDFLAGS="-L$OPENSSL_LIB_DIR $LDFLAGS"
-  fi
+  OLD_LIBS="$LIBS"
 
   dnl #
-  dnl #  Check we can link to libssl
+  dnl #  Apparently OpenSSL will attempt to build with kerberos if we don't pass this?!
   dnl #
-  AC_CHECK_LIB(crypto, DH_new,
-    [
-      LIBS="-lcrypto $LIBS"
-      AC_DEFINE(HAVE_LIBCRYPTO, 1, [Define to 1 if you have the `crypto' library (-lcrypto).])
-      AC_CHECK_LIB(ssl, SSL_new,
-        [
-          AC_DEFINE(HAVE_LIBSSL, 1, [Define to 1 if you have the `ssl' library (-lssl).])
-          if test "x$OPENSSL_LIB_DIR" != "x"; then
-            OPENSSL_LIBS="-L$OPENSSL_LIB_DIR"
-          fi
-          OPENSSL_LIBS="$OPENSSL_LIBS -lcrypto -lssl -lcrypto"
-        ],
-        [
-          AC_MSG_FAILURE([failed linking to libssl])
-        ]
-      )
-    ],
-    []
-  )
+  CFLAGS="$CFLAGS -DOPENSSL_NO_KRB5"
 
   dnl #
-  dnl #  Check we can find required headers
+  dnl #  Check we can link to libcrypto and libssl
   dnl #
-  old_CPPFLAGS=$CPPFLAGS
-  old_CFLAGS=$CFLAGS
-  if test "x$OPENSSL_INCLUDE_DIR" != "x"; then
-    CPPFLAGS="-isystem $OPENSSL_INCLUDE_DIR $CPPFLAGS"
-    CFLAGS="-isystem $OPENSSL_INCLUDE_DIR $CFLAGS"
+  smart_try_dir="$openssl_lib_dir"
+  FR_SMART_CHECK_LIB(crypto, DH_new)
+  if test "x$ac_cv_lib_crypto_DH_new" = "xyes"; then
+    AC_DEFINE(HAVE_LIBCRYPTO, 1, [Define to 1 if you have the `crypto' library (-lcrypto).])
+    OPENSSL_LIBS="$smart_lib"
+    OPENSSL_LDFLAGS="$smart_ldflags"
+
+    FR_SMART_CHECK_LIB(ssl, SSL_new)
+    if test "x$ac_cv_lib_ssl_SSL_new" != "xyes"; then
+      AC_MSG_FAILURE([failed linking to libssl. Use --with-openssl-lib-dir=<path>, or --with-openssl=no (builds without OpenSSL)])
+    else
+      AC_DEFINE(HAVE_LIBSSL, 1, [Define to 1 if you have the `ssl' library (-lssl).])
+      OPENSSL_LIBS="$OPENSSL_LIBS $smart_lib"
+
+      if test "$OPENSSL_LDFLAGS" != "$smart_ldflags"; then
+        AC_MSG_FAILURE(["inconsistent LDFLAGS between -lssl '$smart_ldflags' and -lcrypto '$OPENSSL_LDFLAGS'"])
+      fi
+    fi
+  else
+    AC_MSG_FAILURE([failed linking to libcrypto. Use --with-openssl-lib-dir=<path>, or --with-openssl=no (builds without OpenSSL)])
   fi
 
-  dnl #
-  dnl #  Stupid RedHat shit
-  dnl #
-  CPPFLAGS="$CPPFLAGS -DOPENSSL_NO_KRB5"
-  AC_CHECK_HEADERS( \
-    openssl/ssl.h \
-    openssl/crypto.h \
-    openssl/err.h \
-    openssl/evp.h \
-    openssl/md5.h \
-    openssl/md4.h \
-    openssl/sha.h \
-    openssl/ocsp.h \
-    openssl/engine.h,
-    [],
-    [
-      AC_MSG_FAILURE([failed locating OpenSSL headers])
-    ]
-  )
+  smart_try_dir="$openssl_include_dir"
+  FR_SMART_CHECK_INCLUDE(openssl/ssl.h)
+  if test "x$ac_cv_header_openssl_ssl_h" = "xyes"; then
+    AC_DEFINE(HAVE_OPENSSL_SSL_H, 1, [Define to 1 if you have the <openssl/ssl.h> header file.])
+
+    AC_CHECK_HEADERS( \
+      openssl/crypto.h \
+      openssl/err.h \
+      openssl/evp.h \
+      openssl/md5.h \
+      openssl/md4.h \
+      openssl/sha.h \
+      openssl/ocsp.h \
+      openssl/engine.h,
+      [],
+      [
+        AC_MSG_FAILURE([failed locating OpenSSL headers. Use --with-openssl-include-dir=<path>, or --with-openssl=no (builds without OpenSSL)])
+      ]
+    )
 
-  AC_MSG_CHECKING([for OpenSSL version >= 0.9.7])
-  AC_EGREP_CPP(yes,
-    [#include <openssl/crypto.h>
-     #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
-     yes
-     #endif
-    ],
-    [
-      AC_MSG_RESULT(yes)
-    ],
-    [
-      AC_MSG_RESULT(no)
-      AC_MSG_FAILURE([OpenSSL version too old])
-    ]
-  )
+    AC_MSG_CHECKING([for OpenSSL version >= 0.9.7])
+    AC_EGREP_CPP(yes,
+      [#include <openssl/crypto.h>
+       #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
+       yes
+       #endif
+      ],
+      [
+        AC_MSG_RESULT(yes)
+      ],
+      [
+        AC_MSG_RESULT(no)
+        AC_MSG_FAILURE([OpenSSL version too old])
+      ]
+    )
 
-  if test "x$OPENSSL_INCLUDE_DIR" != "x"; then
-    OPENSSL_INCLUDE="-isystem $OPENSSL_INCLUDE_DIR -DOPENSSL_NO_KRB5"
-  else
-    OPENSSL_INCLUDE="-DOPENSSL_NO_KRB5"
-  fi
+    dnl #
+    dnl #  CPPFLAGS are passed to the compiler first, so we use
+    dnl #  them to ensure things like --sysroot don't override the
+    dnl #  library location we discovered previously.
+    dnl #
+    old_CPPFLAGS="$CPPFLAGS"
+    CPPFLAGS="$OPENSSL_LDFLAGS $CPPFLAGS"
 
-  dnl #
-  dnl #  Now check that the header versions match the library
-  dnl #
-  AC_MSG_CHECKING([OpenSSL library and header version consistency])
-  AC_RUN_IFELSE(
-    [AC_LANG_PROGRAM(
-      [[
-        #include <stdio.h>
-        #include <openssl/opensslv.h>
-        #include <openssl/crypto.h>
-      ]],
-      [[
-        if (SSLeay() == OPENSSL_VERSION_NUMBER) {
-          return 0;
-        } else {
+    dnl #
+    dnl #  Now check that the header versions match the library
+    dnl #
+    AC_MSG_CHECKING([OpenSSL library and header version consistency])
+    AC_RUN_IFELSE(
+      [AC_LANG_PROGRAM(
+        [[
+          #include <stdio.h>
+          #include <openssl/opensslv.h>
+          #include <openssl/crypto.h>
+        ]],
+        [[
           printf("library: %lx header: %lx... ", (unsigned long) SSLeay(), (unsigned long) OPENSSL_VERSION_NUMBER);
-          return 1;
-        }
-      ]]
-    )],
-    [
-      AC_MSG_RESULT(yes)
-    ],
-    [
-      AC_MSG_RESULT(no)
-      AC_MSG_FAILURE([OpenSSL library version does not match header version])
-    ]
-  )
-
-  if test "x$OPENSSL_LIBS" = x; then
-    LIBS=$old_LIBS
-    LDFLAGS="$old_LDFLAGS"
-  fi
-  if test "x$OPENSSL_INCLUDE" = x; then
-    CPPFLAGS=$old_CPPFLAGS
-    CFLAGS=$old_CFLAGS
+          if (SSLeay() == OPENSSL_VERSION_NUMBER) {
+            return 0;
+          } else {
+            return 1;
+          }
+        ]]
+      )],
+      [
+        AC_MSG_RESULT(yes)
+      ],
+      [
+        AC_MSG_RESULT(no)
+        AC_MSG_FAILURE([OpenSSL library version does not match header version])
+      ]
+    )
+    CPPFLAGS="$old_CPPFLAGS"
   fi
-fi
 
-AC_SUBST(OPENSSL_INCLUDE)
-AC_SUBST(OPENSSL_LIBS)
-export OPENSSL_LIBS
+  LIBS="$OLD_LIBS"
+  AC_SUBST(OPENSSL_LIBS)
+  AC_SUBST(OPENSSL_LDFLAGS)
+  export OPENSSL_LIBS OPENSSL_LDFLAGS
+fi
 
 dnl #
 dnl #  Check the pcap includes for the RADIUS sniffer.
@@ -980,28 +1088,37 @@ dnl #
 if test "x$PCAP_LIBS" = x; then
   AC_MSG_NOTICE([skipping test for pcap.h.])
 else
+  dnl #
+  dnl # Check for pcap header files
+  dnl #
   smart_try_dir="$pcap_include_dir"
   FR_SMART_CHECK_INCLUDE([pcap.h])
-  if test "x$ac_cv_header_pcap_h" != "xyes"; then
-    AC_MSG_WARN([pcap headers not found. Use --with-pcap-include-dir=<path>.])
-    AC_MSG_WARN([pcap.h not found, silently disabling the RADIUS sniffer, and ARP listener.])
-  else
+  if test "x$ac_cv_header_pcap_h" == "xyes"; then
     AC_DEFINE(HAVE_PCAP_H, 1, [Define to 1 if you have the <pcap.h> header file.])
+    AC_SUBST(PCAP_LIBS)
+    AC_SUBST(PCAP_LDFLAGS)
+  else
+    AC_MSG_WARN([pcap headers not found, silently disabling the RADIUS sniffer, and ARP listener. Use --with-pcap-include-dir=<path>.])
+  fi
+fi
 
-    AC_CHECK_LIB(pcap, pcap_fopen_offline,
-      [
-        AC_DEFINE(HAVE_PCAP_FOPEN_OFFLINE, 1, [Define to 1 if you have the function pcap_fopen_offline.])
-      ]
-    )
-
-    AC_CHECK_LIB(pcap, pcap_dump_fopen,
-      [
-        AC_DEFINE(HAVE_PCAP_DUMP_FOPEN, 1, [Define to 1 if you have the function pcap_dump_fopen.])
-      ]
-    )
+dnl Check for collectd-client
+if test "x$COLLECTDC_LIBS" = x; then
+  AC_MSG_NOTICE([skipping test for collectd/client.h.])
+else
+  dnl #
+  dnl # Check for collectd-client header files
+  dnl #
+  smart_try_dir="$collectdclient_include_dir"
+  FR_SMART_CHECK_INCLUDE([collectd/client.h])
+  if test "x$ac_cv_header_collectd_client_h" == "xyes"; then
+    AC_DEFINE(HAVE_COLLECTDC_H, 1, [Define to 1 if you have the `collectdclient' library (-lcollectdclient).])
+    AC_SUBST(COLLECTDC_LIBS)
+    AC_SUBST(COLLECTDC_LDFLAGS)
+  else
+    AC_MSG_WARN([collectdclient headers not found. Use --with-collectdclient-include-dir=<path>.])
   fi
 fi
-AC_SUBST(PCAP_LIBS)
 
 dnl #############################################################
 dnl #
@@ -1323,86 +1440,15 @@ dnl #  7. Checks for library functions
 dnl #
 dnl #############################################################
 
-dnl Check for talloc
-dnl extra argument: --with-talloc-include-dir=DIR
-talloc_include_dir=
-AC_ARG_WITH(talloc-include-dir,
-  [  --with-talloc-include-dir=DIR    directory to look for talloc include files in],
-  [case "$withval" in
-    no)
-      AC_MSG_ERROR([Need talloc-include-dir])
-      ;;
-    yes)
-      ;;
-    *)
-      talloc_include_dir="$withval"
-      ;;
-  esac])
-
-dnl Check for talloc
-dnl extra argument: --with-talloc-lib-dir=DIR
-talloc_lib_dir=
-AC_ARG_WITH(talloc-lib-dir,
-  [  --with-talloc-lib-dir=DIR        directory to look for talloc library files in],
-  [case "$withval" in
-    no)
-      AC_MSG_ERROR([Need talloc-lib-dir])
-      ;;
-    yes)
-      ;;
-    *)
-      talloc_lib_dir="$withval"
-      ;;
-  esac])
-
-dnl Check for pcap
-dnl extra argument: --with-pcap-include-dir=DIR
-pcap_include_dir=
-AC_ARG_WITH(pcap-include-dir,
-  [  --with-pcap-include-dir=DIR      directory to look for pcap include files in],
-  [case "$withval" in
-    no)
-      AC_MSG_ERROR([Need pcap-include-dir])
-      ;;
-    yes)
-      ;;
-    *)
-      pcap_include_dir="$withval"
-      ;;
-  esac])
-
-dnl Check for pcap
-dnl extra argument: --with-pcap-lib-dir=DIR
-pcap_lib_dir=
-AC_ARG_WITH(pcap-lib-dir,
-  [  --with-pcap-lib-dir=DIR          directory to look for pcap library files in],
-  [case "$withval" in
-    no)
-      AC_MSG_ERROR([Need pcap-lib-dir])
-      ;;
-    yes)
-      ;;
-    *)
-      pcap_lib_dir="$withval"
-      ;;
-  esac])
-
 dnl #
-dnl # Check for talloc header files
+dnl # Check for talloc_set_memlimit
+dnl # This was only included in version 2.0.8
 dnl #
-smart_try_dir="$talloc_include_dir"
-FR_SMART_CHECK_INCLUDE([talloc.h])
-if test "x$ac_cv_header_talloc_h" != "xyes"; then
-  AC_MSG_WARN([talloc headers not found. Use --with-talloc-include-dir=<path>.])
-  AC_MSG_ERROR([FreeRADIUS requires libtalloc])
-fi
-
-smart_try_dir="$talloc_lib_dir"
-FR_SMART_CHECK_LIB(talloc, _talloc)
-if test "x$ac_cv_lib_talloc__talloc" != "xyes"; then
-  AC_MSG_WARN([talloc library not found. Use --with-talloc-lib-dir=<path>.])
-  AC_MSG_ERROR([FreeRADIUS requires libtalloc])
-fi
+AC_CHECK_LIB(talloc, talloc_set_memlimit,
+  [
+    AC_DEFINE(HAVE_TALLOC_SET_MEMLIMIT, 1, [Define to 1 if you have the function talloc_set_memlimit.])
+  ]
+)
 
 dnl #
 dnl # Check for libcrypt
@@ -1431,7 +1477,8 @@ dnl #
 dnl extra argument: --with-execinfo-lib-dir
 execinfo_lib_dir=
 AC_ARG_WITH(execinfo-lib-dir,
-[  --with-execinfo-lib-dir=DIR      directory to look for execinfo library files in],
+[AS_HELP_STRING([--with-execinfo-lib-dir=DIR],
+[directory in which to look for execinfo library files])],
 [ case "$withval" in
     no)
         AC_MSG_ERROR([Need execinfo-lib-dir])
@@ -1447,10 +1494,11 @@ AC_ARG_WITH(execinfo-lib-dir,
 dnl extra argument: --with-execinfo-include-dir
 execinfo_include_dir=
 AC_ARG_WITH(execinfo-include-dir,
-[  --with-execinfo-include-dir=DIR  directory to look for execinfo include files in],
+[AS_HELP_STRING([--with-execinfo-include-dir=DIR],
+[directory in which to look for execinfo include files])],
 [ case "$withval" in
     no)
-       AC_MSG_ERROR(Need execinfo-include-dir)
+        AC_MSG_ERROR([Need execinfo-include-dir])
        ;;
     yes)
        ;;
@@ -1480,11 +1528,10 @@ if test "x$ac_cv_header_execinfo_h" = "xyes"; then
         backtrace_symbols(&sym, sizeof(sym)) ],
       [
         AC_MSG_RESULT(yes)
-        ac_cv_lib_execinfo_backtrace_symbols=yes
+        ac_cv_lib_execinfo_backtrace_symbols="yes"
       ],
       [
         AC_MSG_RESULT(no)
-        ac_cv_lib_execinfo_backtrace_symbols=no
       ]
     )
   fi
@@ -1503,24 +1550,26 @@ dnl #
 dnl extra argument: --with-pcre-lib-dir
 pcre_lib_dir=
 AC_ARG_WITH(pcre-lib-dir,
-  [  --with-pcre-lib-dir=DIR          directory to look for pcre library files in],
-  [ case "$withval" in
+[AS_HELP_STRING([--with-pcre-lib-dir=DIR],
+[directory in which to look for pcre library files])],
+[ case "$withval" in
     no)
-        AC_MSG_ERROR([Need pcre-lib-dir])
+       AC_MSG_ERROR(Need pcre-lib-dir)
        ;;
     yes)
        ;;
     *)
        pcre_lib_dir="$withval"
        ;;
-    esac ]
+  esac ]
 )
 
 dnl extra argument: --with-pcre-include--dir
 pcre_include_dir=
 AC_ARG_WITH(pcre-include-dir,
-  [  --with-pcre-include-dir=DIR      directory to look for PCRE include files in],
-  [ case "$withval" in
+[AS_HELP_STRING([--with-pcre-include-dir=DIR],
+[directory in which to look for pcre include files])],
+[ case "$withval" in
     no)
        AC_MSG_ERROR(Need pcre-include-dir)
        ;;
@@ -1529,7 +1578,7 @@ AC_ARG_WITH(pcre-include-dir,
     *)
        pcre_include_dir="$withval"
        ;;
-    esac ]
+  esac ]
 )
 
 REGEX=no
index 7d1c17f..996c14b 100644 (file)
@@ -48,6 +48,8 @@ FreeRADIUS 3.0.3 Fri 21 Mar 2014 08:30:00 EDT urgency=medium
          reply:FreeRADIUS-Response-Delay = 1
        * Allow tag and array references anywhere attributes
          are allowed in "unlang".
+       * many enhancements to radsniff, including output 
+         to collectd, ipv6 support and packet loss statistics.
 
        Bug fixes
        * Fix xlat expression %{attribute[n]} so that it actually
@@ -74,7 +76,7 @@ FreeRADIUS 3.0.3 Fri 21 Mar 2014 08:30:00 EDT urgency=medium
          to double-check your Perl scripts if they use "\" characters.
          See mods-available/perl for documentation.
        * Don't re-run "authorize" if a home server fails to respond.
-       * "0x" isn't printed in front of octets types, for xlat
+       * Don't append "0x" to hex output of octets types, for xlat
          expansions.  This is the same as v2, and makes it easier
          to concatenate multiple attributes of type "octets"
        * FreeBSD fixes for execinfo linking.
index 1e24cb7..a29c956 100644 (file)
@@ -39,6 +39,7 @@ typedef void (*fr_event_fd_handler_t)(fr_event_list_t *el, int sock, void *ctx);
 
 fr_event_list_t *fr_event_list_create(TALLOC_CTX *ctx, fr_event_status_t status);
 
+int fr_event_list_num_fds(fr_event_list_t *el);
 int fr_event_list_num_elements(fr_event_list_t *el);
 
 int fr_event_insert(fr_event_list_t *el,
@@ -55,6 +56,7 @@ int fr_event_fd_insert(fr_event_list_t *el, int type, int fd,
 int fr_event_fd_delete(fr_event_list_t *el, int type, int fd);
 int fr_event_loop(fr_event_list_t *el);
 void fr_event_loop_exit(fr_event_list_t *el, int code);
+bool fr_event_loop_exiting(fr_event_list_t *el);
 
 #ifdef __cplusplus
 }
index 52fae96..9a460a5 100644 (file)
@@ -508,7 +508,7 @@ void fr_hmac_sha1(uint8_t const *text, size_t text_len, uint8_t const *key, size
 
 /* radius.c */
 int            rad_send(RADIUS_PACKET *, RADIUS_PACKET const *, char const *secret);
-int            rad_packet_ok(RADIUS_PACKET *packet, int flags);
+bool           rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason);
 RADIUS_PACKET  *rad_recv(int fd, int flags);
 ssize_t rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, int *src_port,
                        int *code);
@@ -545,6 +545,12 @@ int                rad_attr_ok(RADIUS_PACKET const *packet, RADIUS_PACKET const *original,
 int            rad_tlv_ok(uint8_t const *data, size_t length,
                           size_t dv_type, size_t dv_length);
 
+ssize_t                data2vp(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
+                       char const *secret,
+                       DICT_ATTR const *da, uint8_t const *start,
+                       size_t const attrlen, size_t const packetlen,
+                       VALUE_PAIR **pvp);
+
 ssize_t                rad_attr2vp(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
                            char const *secret,
                            uint8_t const *data, size_t length,
@@ -603,6 +609,7 @@ void                pairreplace(VALUE_PAIR **first, VALUE_PAIR *add);
 int8_t         paircmp_value(VALUE_PAIR const *a, VALUE_PAIR const *b);
 int8_t         paircmp_op(VALUE_PAIR const *a, FR_TOKEN op, VALUE_PAIR const *b);
 int8_t         paircmp(VALUE_PAIR *a, VALUE_PAIR *b);
+int8_t         pairlistcmp(VALUE_PAIR *a, VALUE_PAIR *b);
 
 typedef int8_t (*fr_pair_cmp_t)(VALUE_PAIR const *a, VALUE_PAIR const *b);
 int8_t         attrcmp(VALUE_PAIR const *a, VALUE_PAIR const *b);
diff --git a/src/include/pcap.h b/src/include/pcap.h
new file mode 100644 (file)
index 0000000..005b481
--- /dev/null
@@ -0,0 +1,187 @@
+#ifndef FR_PCAP_H
+#define FR_PCAP_H
+/*
+ *   This program is is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License, version 2 if the
+ *   License as published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ * @file include/pcap.h
+ * @brief Prototypes and constants for PCAP functions.
+ *
+ * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @copyright 2013 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ */
+#include <freeradius-devel/libradius.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <pcap.h>
+
+/*
+ *     Length of a DEC/Intel/Xerox or 802.3 Ethernet header.
+ *     Note that some compilers may pad "struct ether_header" to
+ *     a multiple of 4 *bytes, for example, so "sizeof (struct
+ *     ether_header)" may not give the right answer.
+ *
+ *     6 Byte SRC, 6 Byte DST, 2 Byte Ether type, 4 Byte CVID, 4 Byte SVID
+ */
+#define ETHER_HDRLEN   22
+#define IP_HDRLEN      60
+
+/*
+ *     RADIUS packet length.
+ *     RFC 2865, Section 3., subsection 'length' says:
+ *     " ... and maximum length is 4096."
+ */
+#define MAX_RADIUS_LEN 4096
+#define MIN_RADIUS_LEN 20
+#define SNAPLEN ETHER_HDRLEN + IP_HDRLEN + sizeof(struct udp_header) + MAX_RADIUS_LEN
+#define PCAP_BUFFER_DEFAULT (10000)
+/*
+ *     It's unclear why this differs between platforms
+ */
+#ifndef __linux__
+#  define PCAP_NONBLOCK_TIMEOUT (0)
+#else
+#  define PCAP_NONBLOCK_TIMEOUT (-1)
+#endif
+
+#ifndef BIOCIMMEDIATE
+#define BIOCIMMEDIATE (2147762800)
+#endif
+
+/*
+ *     Older versions of libpcap don't define this
+ */
+#ifndef PCAP_NETMASK_UNKNOWN
+#  define PCAP_NETMASK_UNKNOWN 0
+#endif
+
+/*
+ *     The number of bytes in an ethernet (MAC) address.
+ */
+#define ETHER_ADDR_LEN         6
+
+/*
+ *     Structure of a DEC/Intel/Xerox or 802.3 Ethernet header.
+ */
+struct  ethernet_header {
+       uint8_t         ether_dst[ETHER_ADDR_LEN];
+       uint8_t         ether_src[ETHER_ADDR_LEN];
+       uint16_t        ether_type;
+};
+
+/*
+ *     Structure of an internet header, naked of options.
+ */
+
+#define IP_V(ip)       (((ip)->ip_vhl & 0xf0) >> 4)
+#define IP_HL(ip)       ((ip)->ip_vhl & 0x0f)
+
+#define        I_DF            0x4000          //!< Dont fragment flag.
+#define IP_MF          0x2000          //!< More fragments flag.
+#define IP_OFFMASK     0x1fff          //!< Mask for fragmenting bits.
+
+typedef struct ip_header {
+       uint8_t         ip_vhl;         //!< Header length, version.
+
+       uint8_t         ip_tos;         //!< Type of service.
+       uint16_t        ip_len;         //!< Total length.
+       uint16_t        ip_id;          //!< identification.
+       uint16_t        ip_off;         //!< Fragment offset field.
+
+       uint8_t         ip_ttl;         //!< Time To Live.
+       uint8_t         ip_p;           //!< Protocol.
+       uint16_t        ip_sum;         //!< Checksum.
+       struct in_addr  ip_src, ip_dst; //!< Src and Dst address
+} ip_header_t;
+
+typedef struct ip_header6 {
+       uint32_t        ip_vtcfl;       //!< Version, traffic class, flow label.
+       uint16_t        ip_len;         //!< Payload length
+
+       uint8_t         ip_next;        //!< Next header (protocol)
+       uint8_t         ip_hopl;        //!< IP Hop Limit
+
+       struct in6_addr ip_src, ip_dst; //!< Src and Dst address
+} ip_header6_t;
+
+/*
+ *     UDP protocol header.
+ *     Per RFC 768, September, 1981.
+ */
+typedef struct udp_header {
+       uint16_t        src;            //!< Source port.
+       uint16_t        dst;            //!< Destination port.
+       uint16_t        len;            //!< UDP length.
+       uint16_t        checksum;       //!< UDP checksum.
+} udp_header_t;
+
+typedef struct radius_packet_t {
+       uint8_t         code;
+       uint8_t         id;
+       uint8_t         length[2];
+       uint8_t         vector[AUTH_VECTOR_LEN];
+       uint8_t         data[1];
+} radius_packet_t;
+
+#define AUTH_HDR_LEN 20
+
+typedef enum {
+       PCAP_INVALID = 0,
+       PCAP_INTERFACE_IN,
+       PCAP_FILE_IN,
+       PCAP_STDIO_IN,
+       PCAP_INTERFACE_OUT,
+       PCAP_FILE_OUT,
+       PCAP_STDIO_OUT
+} fr_pcap_type_t;
+
+extern const FR_NAME_NUMBER pcap_types[];
+
+/*
+ *     Internal pcap structures
+ */
+typedef struct fr_pcap fr_pcap_t;
+struct fr_pcap {
+       char                    errbuf[PCAP_ERRBUF_SIZE];       //!< Last error on this interface.
+       fr_pcap_type_t          type;                           //!< What type of handle this is.
+       char                    *name;                          //!< Name of file or interface.
+       bool                    promiscuous;                    //!< Whether the interface is in promiscuous mode.
+                                                               //!< Only valid for live capture handles.
+       int                     buffer_pkts;                    //!< How big to make the PCAP ring buffer.
+                                                               //!< Actual buffer size is SNAPLEN * buffer.
+                                                               //!< Only valid for live capture handles.
+
+       pcap_t                  *handle;                        //!< libpcap handle.
+       pcap_dumper_t           *dumper;                        //!< libpcap dumper handle.
+
+       int                     link_type;                      //!< Link layer type.
+
+       int                     fd;                             //!< Selectable file descriptor we feed to select.
+       struct pcap_stat        pstats;                         //!< The last set of pcap stats for this handle.
+
+       fr_pcap_t               *next;                          //!< Next handle in collection.
+};
+
+
+fr_pcap_t *fr_pcap_init(TALLOC_CTX *ctx, char const *name, fr_pcap_type_t type);
+int fr_pcap_open(fr_pcap_t *handle);
+int fr_pcap_apply_filter(fr_pcap_t *handle, char const *expression);
+char *fr_pcap_device_names(TALLOC_CTX *ctx, fr_pcap_t *handle, char c);
+ssize_t fr_pcap_link_layer_offset(uint8_t const *data, size_t len, int link_type);
+uint16_t fr_udp_checksum(uint8_t const *data, uint16_t len, uint16_t checksum,
+                        struct in_addr const src_addr, struct in_addr const dst_addr);
+#endif
+
index 0f3f999..62a7b02 100644 (file)
@@ -1,24 +1,26 @@
 /*
- *  radsniff.h Structures and defines for the RADIUS sniffer.
+ *   This program is is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License, version 2 if the
+ *   License as published by the Free Software Foundation.
  *
- *  Version:    $Id$
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
  *
- *  This program is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU General Public License
- *  as published by the Free Software Foundation; either version 2
- *  of the License, or (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ * @file radsniff.h
+ * @brief Structures and prototypes for the RADIUS sniffer.
  *
- *  Copyright 2006  The FreeRADIUS server project
- *  Copyright 2006  Nicolas Baradakis <nicolas.baradakis@cegetel.net>
+ * @copyright 2013 Arran Cudbard-Bell <arran.cudbardb@freeradius.org>
+ * @copyright 2006 The FreeRADIUS server project
+ * @copyright 2006 Nicolas Baradakis <nicolas.baradakis@cegetel.net>
  */
 
 RCSIDH(radsniff_h, "$Id$")
@@ -26,74 +28,285 @@ RCSIDH(radsniff_h, "$Id$")
 #include <sys/types.h>
 #include <netinet/in.h>
 
+#include <pcap/pcap.h>
+#include <freeradius-devel/libradius.h>
+#include <freeradius-devel/pcap.h>
+#include <freeradius-devel/event.h>
+
+#ifdef HAVE_COLLECTDC_H
+#  include <collectd/client.h>
+#endif
+
+#define RS_DEFAULT_PREFIX      "radsniff"      //!< Default instance
+#define RS_DEFAULT_SECRET      "testing123"    //!< Default secret
+#define RS_DEFAULT_TIMEOUT     5200            //!< Standard timeout of 5s + 300ms to cover network latency
+#define RS_FORCE_YIELD         1000            //!< Service another descriptor every X number of packets
+#define RS_RETRANSMIT_MAX      5               //!< Maximum number of times we expect to see a packet retransmitted
+#define RS_MAX_ATTRS           50              //!< Maximum number of attributes we can filter on.
+#define RS_SOCKET_REOPEN_DELAY  5000           //!< How long we delay re-opening a collectd socket.
+
 /*
- *     The number of bytes in an ethernet (MAC) address.
+ *     Logging macros
  */
-#define ETHER_ADDR_LEN         6
+#undef DEBUG2
+#define DEBUG2(fmt, ...)       if (fr_debug_flag > 2) fprintf(fr_log_fp , fmt "\n", ## __VA_ARGS__)
+#undef DEBUG
+#define DEBUG(fmt, ...)                if (fr_debug_flag > 1) fprintf(fr_log_fp , fmt "\n", ## __VA_ARGS__)
+#undef INFO
+#define INFO(fmt, ...)         if (fr_debug_flag > 0) fprintf(fr_log_fp , fmt "\n", ## __VA_ARGS__)
 
-/*
- *     Structure of a DEC/Intel/Xerox or 802.3 Ethernet header.
+#define ERROR(fmt, ...)                fr_perror("radsniff: " fmt, ## __VA_ARGS__)
+
+#define RIDEBUG_ENABLED()      (conf->print_packet && (fr_debug_flag > 0))
+#define RDEBUG_ENABLED()       (conf->print_packet && (fr_debug_flag > 1))
+#define RDEBUG_ENABLED2()      (conf->print_packet && (fr_debug_flag > 2))
+
+#define REDEBUG(fmt, ...)      if (conf->print_packet) fprintf(fr_log_fp , "%s (%" PRIu64 ")  " fmt ": %s\n", timestr, count, ## __VA_ARGS__,  fr_strerror())
+#define RIDEBUG(fmt, ...)      if (conf->print_packet && (fr_debug_flag > 0)) fprintf(fr_log_fp , "%s (%" PRIu64 ") " fmt "\n", timestr, count, ## __VA_ARGS__)
+#define RDEBUG(fmt, ...)       if (conf->print_packet && (fr_debug_flag > 1)) fprintf(fr_log_fp , "%s (%" PRIu64 ") " fmt "\n", timestr, count, ## __VA_ARGS__)
+#define RDEBUG2(fmt, ...)      if (conf->print_packet && (fr_debug_flag > 2)) fprintf(fr_log_fp , "%s (%" PRIu64 ") " fmt "\n", timestr, count, ## __VA_ARGS__)
+
+typedef enum {
+       RS_NORMAL       = 0x01,
+       RS_UNLINKED     = 0x02,
+       RS_RTX          = 0x04,
+       RS_REUSED       = 0x08,
+       RS_ERROR        = 0x10,
+       RS_LOST         = 0x20
+} rs_status_t;
+
+typedef void (*rs_packet_logger_t)(uint64_t count, rs_status_t status, fr_pcap_t *handle, RADIUS_PACKET *packet,
+                                  struct timeval *elapsed, struct timeval *latency, bool response, bool body);
+typedef enum {
+#ifdef HAVE_COLLECTDC_H
+       RS_STATS_OUT_COLLECTD = 1,
+#endif
+       RS_STATS_OUT_STDIO
+} stats_out_t;
+
+typedef struct rs rs_t;
+
+#ifdef HAVE_COLLECTDC_H
+typedef struct rs_stats_tmpl rs_stats_tmpl_t;
+typedef struct rs_stats_value_tmpl rs_stats_value_tmpl_t;
+#endif
+
+typedef struct rs_counters {
+       uint64_t type[PW_CODE_MAX];
+} rs_counters_t;
+
+/** Stats for a single interval
+ *
+ * And interval is defined as the time between a call to the stats output function.
  */
-struct  ethernet_header {
-       uint8_t ethernet_dhost[ETHER_ADDR_LEN];
-       uint8_t ethernet_shost[ETHER_ADDR_LEN];
-       uint16_t        ethernet_type;
-};
+typedef struct rs_latency {
+       int                     intervals;                      //!< Number of stats intervals.
 
-/*
- *     Length of a DEC/Intel/Xerox or 802.3 Ethernet header.
- *     Note that some compilers may pad "struct ether_header" to
- *     a multiple of 4 *bytes, for example, so "sizeof (struct
- *     ether_header)" may not give the right answer.
+       double                  latency_smoothed;               //!< Smoothed moving average.
+       uint64_t                latency_smoothed_count;         //!< Number of CMA datapoints processed.
+
+       struct {
+               uint64_t                received_total;         //!< Total received over interval.
+               uint64_t                linked_total;           //!< Total request/response pairs over interval.
+               uint64_t                unlinked_total;         //!< Total unlinked over interval.
+               uint64_t                reused_total;           //!< Total reused over interval.
+               uint64_t                lost_total;             //!< Total packets definitely lost in this interval.
+               uint64_t                rt_total[RS_RETRANSMIT_MAX + 1];        //!< Number of RTX until complete
+                                                                               //!< over interval.
+
+
+               double                  received;               //!< Number of this type of packet we've received.
+               double                  linked;                 //!< Number of request/response pairs
+               double                  unlinked;               //!< Response with no request.
+               double                  reused;                 //!< ID re-used too quickly.
+               double                  lost;                   //!< Never got a response to a request.
+               double                  rt[RS_RETRANSMIT_MAX + 1];      //!< Number of times we saw the same
+                                                                       //!< request packet.
+
+               long double             latency_total;          //!< Total latency between requests/responses in the
+                                                               //!< interval.
+               double                  latency_average;        //!< Average latency (this iteration).
+
+               double                  latency_high;           //!< Latency high water mark.
+               double                  latency_low;            //!< Latency low water mark.
+       } interval;
+} rs_latency_t;
+
+typedef struct rs_malformed {
+       uint64_t                min_length_packet;
+       uint64_t                min_length_field;
+       uint64_t                min_length_mimatch;
+       uint64_t                header_overflow;
+       uint64_t                invalid_attribute;
+       uint64_t                attribute_too_short;
+       uint64_t                attribute_overflow;
+       uint64_t                ma_invalid_length;
+       uint64_t                attribute_underflow;
+       uint64_t                too_many_attributes;
+       uint64_t                ma_missing;
+} rs_malformed_t;
+
+/** One set of statistics
+ *
  */
-#define ETHER_HDRLEN           14
+typedef struct rs_stats {
+       int                     intervals;              //!< Number of stats intervals.
 
-/*
- *     Structure of an internet header, naked of options.
+       rs_latency_t            exchange[PW_CODE_MAX];  //!< We end up allocating ~16K, but memory is cheap so
+                                                       //!< what the hell.  This is required because instances of
+                                                       //!< FreeRADIUS delay Access-Rejects, which would artificially
+                                                       //!< increase latency stats for Access-Requests.
+
+       struct timeval          quiet;                  //!< We may need to 'mute' the stats if libpcap starts
+                                                       //!< dropping packets, or we run out of memory.
+} rs_stats_t;
+
+/** Wrapper for RADIUS_PACKET
+ *
+ * Allows an event to be associated with a request packet.  This is required because we need to disarm
+ * the event timer when a response is received, so we don't erroneously log the response as lost.
+ */
+typedef struct rs_request {
+       uint64_t                id;                     //!< Monotonically increasing packet counter.
+       fr_event_t              *event;                 //!< Event created when we received the original request.
+
+       struct timeval          when;                   //!< Time when the packet was received, or next time an event
+                                                       //!< is scheduled.
+       fr_pcap_t               *in;                    //!< PCAP handle the original request was received on.
+       RADIUS_PACKET           *packet;                //!< The original packet.
+       RADIUS_PACKET           *expect;                //!< Request/response.
+       RADIUS_PACKET           *linked;                //!< The subsequent response or forwarded request the packet
+                                                       //!< was linked against.
+
+       uint64_t                rt_req;                 //!< Number of times we saw the same request packet.
+       uint64_t                rt_rsp;                 //!< Number of times we saw a retransmitted response
+                                                       //!< packet.
+       rs_latency_t            *stats_req;             //!< Latency entry for the request type.
+       rs_latency_t            *stats_rsp;             //!< Latency entry for the request type.
+
+       bool                    silent_cleanup;         //!< Cleanup was forced before normal expiry period,
+                                                       //!< ignore stats about packet loss.
+
+       VALUE_PAIR              *link_vps;              //!< VALUE_PAIRs used to link retransmissions.
+
+       bool                    in_request_tree;        //!< Whether the request is currently in the request tree.
+       bool                    in_link_tree;           //!< Whether the request is currently in the linked tree.
+} rs_request_t;
+
+/** Statistic write/print event
+ *
+ */
+typedef struct rs_event {
+       fr_event_list_t         *list;                  //!< The event list.
+
+       fr_pcap_t               *in;                    //!< PCAP handle event occurred on.
+       fr_pcap_t               *out;                   //!< Where to write output.
+
+       rs_stats_t              *stats;                 //!< Where to write stats.
+} rs_event_t;
+
+/** FD data which gets passed to callbacks
+ *
+ */
+typedef struct rs_update {
+       fr_event_list_t         *list;                  //!< List to insert new event into.
+
+       fr_pcap_t               *in;                    //!< Linked list of PCAP handles to check for drops.
+       rs_stats_t              *stats;                 //!< Stats to process.
+} rs_update_t;
+
+
+struct rs {
+       bool                    from_file;              //!< Were reading pcap data from files.
+       bool                    from_dev;               //!< Were reading pcap data from devices.
+       bool                    from_stdin;             //!< Were reading pcap data from stdin.
+       bool                    to_file;                //!< Were writing pcap data to files.
+       bool                    to_stdout;              //!< Were writing pcap data to stdout.
+
+       bool                    daemonize;              //!< Daemonize and write PID out to file.
+       char const              *pidfile;               //!< File to write PID to.
+
+       bool                    from_auto;              //!< From list was auto-generated.
+       bool                    promiscuous;            //!< Capture in promiscuous mode.
+       bool                    print_packet;           //!< Print packet info, disabled with -W
+       bool                    decode_attrs;           //!< Whether we should decode attributes in the request
+                                                       //!< and response.
+
+       char const              *radius_secret;         //!< Secret to decode encrypted attributes.
+
+       char                    *pcap_filter;           //!< PCAP filter string applied to live capture devices.
+
+       char                    *list_attributes;       //!< Raw attribute filter string.
+       DICT_ATTR const         *list_da[RS_MAX_ATTRS]; //!< Output CSV with these attribute values.
+       int                     list_da_num;
+
+       char                    *link_attributes;       //!< Names of DICT_ATTRs to use for rtx.
+       DICT_ATTR const         *link_da[RS_MAX_ATTRS]; //!< DICT_ATTRs to link on.
+       int                     link_da_num;            //!< Number of rtx DICT_ATTRs.
+
+       char const              *filter_request;        //!< Raw request filter string.
+       char const              *filter_response;       //!< Raw response filter string.
+
+       VALUE_PAIR              *filter_request_vps;    //!< Sorted filter vps.
+       VALUE_PAIR              *filter_response_vps;   //!< Sorted filter vps.
+       PW_CODE                 filter_request_code;    //!< Filter request packets by code.
+       PW_CODE                 filter_response_code;   //!< Filter response packets by code.
+
+       rs_status_t             event_flags;            //!< Events we log and capture on.
+       rs_packet_logger_t      logger;                 //!< Packet logger
+
+       int                     buffer_pkts;            //!< Size of the ring buffer to setup for live capture.
+       uint64_t                limit;                  //!< Maximum number of packets to capture
+
+       struct {
+               int                     interval;               //!< Time between stats updates in seconds.
+               stats_out_t             out;                    //!< Where to write stats.
+               int                     timeout;                //!< Maximum length of time we wait for a response.
+
+#ifdef HAVE_COLLECTDC_H
+               char const              *collectd;              //!< Collectd server/port/unixsocket
+               char const              *prefix;                //!< Prefix collectd stats with this value.
+               lcc_connection_t        *handle;                //!< Collectd client handle.
+               rs_stats_tmpl_t         *tmpl;                  //!< The stats templates we created on startup.
+#endif
+       } stats;
+};
+
+#ifdef HAVE_COLLECTDC_H
+
+/** Callback for processing stats values.
+ *
  */
-struct ip_header {
-       uint8_t ip_vhl;  /* header length, version */
-#define IP_V(ip)       (((ip)->ip_vhl & 0xf0) >> 4)
-#define IP_HL(ip)       ((ip)->ip_vhl & 0x0f)
-       uint8_t ip_tos;  /* type of service */
-       uint16_t       ip_len;   /* total length */
-       uint16_t       ip_id;     /* identification */
-       uint16_t       ip_off;   /* fragment offset field */
-#define I_DF 0x4000                /* dont fragment flag */
-#define IP_MF 0x2000               /* more fragments flag */
-#define IP_OFFMASK 0x1fff             /* mask for fragmenting bits */
-       uint8_t ip_ttl;  /* time to live */
-       uint8_t ip_p;      /* protocol */
-       uint16_t       ip_sum;   /* checksum */
-       struct in_addr  ip_src,ip_dst;  /* source and dest address */
+typedef void (*rs_stats_cb_t)(rs_t *conf, rs_stats_value_tmpl_t *tmpl);
+struct rs_stats_value_tmpl {
+       void                    *src;                   //!< Pointer to source field in struct. Must be set by
+                                                       //!< stats_collectdc_init caller.
+       int                     type;                   //!< Stats type.
+       rs_stats_cb_t           cb;                     //!< Callback used to process stats
+       void                    *dst;                   //!< Pointer to dst field in value struct. Must be set
+                                                       //!< by stats_collectdc_init caller.
 };
 
-/*
- *     UDP protocol header.
- *     Per RFC 768, September, 1981.
+/** Stats templates
+ *
+ * This gets processed to turn radsniff stats structures into collectd lcc_value_list_t structures.
  */
-struct udp_header {
-       uint16_t       udp_sport;              /* source port */
-       uint16_t       udp_dport;              /* destination port */
-       uint16_t       udp_ulen;                /* udp length */
-       uint16_t       udp_sum;          /* udp checksum */
+struct rs_stats_tmpl
+{
+       rs_stats_value_tmpl_t   *value_tmpl;            //!< Value template
+       void                    *stats;                 //!< Struct containing the raw stats to process
+       lcc_value_list_t        *value;                 //!< Collectd stats struct to populate
+
+       rs_stats_tmpl_t         *next;                  //!< Next...
 };
 
 /*
- *     RADIUS packet length.
- *     RFC 2865, Section 3., subsection 'length' says:
- *     " ... and maximum length is 4096."
+ *     collectd.c - Registration and processing functions
  */
-#define MAX_RADIUS_LEN 4096
-#define MIN_RADIUS_LEN 20
-#define SNAPLEN (sizeof(struct ethernet_header) + sizeof(struct ip_header) + sizeof(struct udp_header) + MAX_RADIUS_LEN)
-
-typedef struct radius_packet_t {
-       uint8_t       code;
-       uint8_t       id;
-       uint8_t       length[2];
-       uint8_t       vector[AUTH_VECTOR_LEN];
-       uint8_t       data[1];
-} radius_packet_t;
-
-#define AUTH_HDR_LEN 20
+rs_stats_tmpl_t *rs_stats_collectd_init_latency(TALLOC_CTX *ctx, rs_stats_tmpl_t **out, rs_t *conf,
+                                               char const *type, rs_latency_t *stats, PW_CODE code);
+void rs_stats_collectd_do_stats(rs_t *conf, rs_stats_tmpl_t *tmpls, struct timeval *now);
+int rs_stats_collectd_open(rs_t *conf);
+int rs_stats_collectd_close(rs_t *conf);
+
+#endif
index e1408c5..b215cb1 100644 (file)
@@ -5,15 +5,14 @@
 #
 TARGET         := libfreeradius-radius.a
 
-SOURCES                := cbuff.c cursor.c debug.c dict.c filters.c hash.c hmac.c \
-                          hmacsha1.c isaac.c log.c misc.c missing.c md4.c \
-                          md5.c print.c radius.c rbtree.c sha1.c snprintf.c \
-                          strlcat.c strlcpy.c token.c udpfromto.c \
-                          valuepair.c fifo.c packet.c event.c getaddrinfo.c \
-                          heap.c tcp.c base64.c version.c
+SOURCES                := cbuff.c cursor.c debug.c dict.c filters.c hash.c hmac.c hmacsha1.c \
+                  isaac.c log.c  misc.c missing.c md4.c md5.c pcap.c print.c radius.c rbtree.c \
+                  sha1.c snprintf.c strlcat.c strlcpy.c token.c udpfromto.c valuepair.c fifo.c \
+                  packet.c event.c getaddrinfo.c heap.c tcp.c base64.c version.c
 
 SRC_CFLAGS     := -D_LIBRADIUS -I$(top_builddir)/src
 
 # System libraries discovered by our top level configure script, links things
 # like pthread and the regexp libraries.
-TGT_LDLIBS     := $(LIBS)
+TGT_LDLIBS     := $(LIBS) $(PCAP_LIBS)
+TGT_LDFLAGS    := $(LDFLAGS) $(PCAP_LDFLAGS)
index af55d56..14d7e85 100644 (file)
@@ -124,6 +124,13 @@ fr_event_list_t *fr_event_list_create(TALLOC_CTX *ctx, fr_event_status_t status)
        return el;
 }
 
+int fr_event_list_num_fds(fr_event_list_t *el)
+{
+       if (!el) return 0;
+
+       return el->num_readers;
+}
+
 int fr_event_list_num_elements(fr_event_list_t *el)
 {
        if (!el) return 0;
@@ -376,6 +383,11 @@ void fr_event_loop_exit(fr_event_list_t *el, int code)
        el->exit = code;
 }
 
+bool fr_event_loop_exiting(fr_event_list_t *el)
+{
+       return (el->exit != 0);
+}
+
 int fr_event_loop(fr_event_list_t *el)
 {
        int i, rcode, maxfd = 0;
diff --git a/src/lib/pcap.c b/src/lib/pcap.c
new file mode 100644 (file)
index 0000000..c64ae1a
--- /dev/null
@@ -0,0 +1,475 @@
+/*
+ *   This program is is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License, version 2 if the
+ *   License as published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ * @file pcap.c
+ * @brief Wrappers around libpcap functions
+ *
+ * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @copyright 2013 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ */
+#ifdef HAVE_LIBPCAP
+
+#include <sys/ioctl.h>
+#include <pcap/pcap.h>
+#include <freeradius-devel/pcap.h>
+
+const FR_NAME_NUMBER pcap_types[] = {
+       { "interface",  PCAP_INTERFACE_IN },
+       { "file",       PCAP_FILE_IN },
+       { "stdio",      PCAP_STDIO_IN },
+       { "interface",  PCAP_INTERFACE_OUT },
+       { "file",       PCAP_FILE_OUT },
+       { "stdio",      PCAP_STDIO_OUT },
+
+       { NULL, 0}
+};
+
+/** Talloc destructor to free pcap resources associated with a handle.
+ *
+ * @param pcap to free.
+ * @return 0
+ */
+static int _free_pcap(fr_pcap_t *pcap) {
+       switch (pcap->type) {
+               case PCAP_INTERFACE_IN:
+               case PCAP_INTERFACE_OUT:
+               case PCAP_FILE_IN:
+               case PCAP_STDIO_IN:
+                       if (pcap->handle) {
+                               pcap_close(pcap->handle);
+
+                               if (pcap->fd > 0) {
+                                       close(pcap->fd);
+                               }
+                       }
+
+                       break;
+
+               case PCAP_FILE_OUT:
+               case PCAP_STDIO_OUT:
+                       if (pcap->dumper) {
+                               pcap_dump_flush(pcap->dumper);
+                               pcap_dump_close(pcap->dumper);
+                       }
+
+                       break;
+               case PCAP_INVALID:
+                       break;
+       }
+
+       return 0;
+}
+
+/** Initialise a pcap handle abstraction
+ *
+ * @param ctx talloc TALLOC_CTX to allocate handle in.
+ * @param name of interface or file to open.
+ * @param type of handle to initialise.
+ * @return new handle or NULL on error.
+ */
+fr_pcap_t *fr_pcap_init(TALLOC_CTX *ctx, char const *name, fr_pcap_type_t type)
+{
+       fr_pcap_t *this = talloc_zero(ctx, fr_pcap_t);
+       if (!this) {
+               return NULL;
+       }
+
+       talloc_set_destructor(this, _free_pcap);
+       this->name = talloc_typed_strdup(this, name);
+       this->type = type;
+       this->link_type = -1;
+
+       return this;
+}
+
+/** Open a PCAP handle abstraction
+ *
+ * This opens interfaces for capture or injection, or files/streams for reading/writing.
+ * @param pcap created with fr_pcap_init.
+ * @return 0 on success, -1 on error.
+ */
+int fr_pcap_open(fr_pcap_t *pcap)
+{
+       switch (pcap->type) {
+       case PCAP_INTERFACE_OUT:
+       case PCAP_INTERFACE_IN:
+       {
+#if defined(HAVE_PCAP_CREATE) && defined(HAVE_PCAP_ACTIVATE)
+               pcap->handle = pcap_create(pcap->name, pcap->errbuf);
+               if (!pcap->handle) {
+                       fr_strerror_printf("%s", pcap->errbuf);
+                       return -1;
+               }
+               if (pcap_set_snaplen(pcap->handle, SNAPLEN) != 0) {
+               create_error:
+                       fr_strerror_printf("%s", pcap_geterr(pcap->handle));
+                       pcap_close(pcap->handle);
+                       pcap->handle = NULL;
+                       return -1;
+               }
+               if (pcap_set_timeout(pcap->handle, PCAP_NONBLOCK_TIMEOUT) != 0) {
+                       goto create_error;
+               }
+               if (pcap_set_promisc(pcap->handle, pcap->promiscuous) != 0) {
+                       goto create_error;
+               }
+
+               if (pcap_set_buffer_size(pcap->handle, SNAPLEN *
+                                        (pcap->buffer_pkts ? pcap->buffer_pkts : PCAP_BUFFER_DEFAULT)) != 0) {
+                       goto create_error;
+               }
+               if (pcap_activate(pcap->handle) != 0) {
+                       goto create_error;
+               }
+#else
+               /*
+                *      Alternative functions for libpcap < 1.0
+                */
+               pcap->handle = pcap_open_live(pcap->name, SNAPLEN, pcap->promiscuous, PCAP_NONBLOCK_TIMEOUT,
+                                             pcap->errbuf);
+               if (!pcap->handle) {
+                       fr_strerror_printf("%s", pcap->errbuf);
+                       return -1;
+               }
+#endif
+               /*
+                *      Despite accepting an errbuff, pcap_setnonblock doesn't seem to write
+                *      error message there in newer versions.
+                */
+               if (pcap_setnonblock(pcap->handle, true, pcap->errbuf) != 0) {
+                       fr_strerror_printf("%s", *pcap->errbuf != '\0' ?
+                                          pcap->errbuf : pcap_geterr(pcap->handle));
+                       pcap_close(pcap->handle);
+                       pcap->handle = NULL;
+                       return -1;
+               }
+
+               pcap->fd = pcap_get_selectable_fd(pcap->handle);
+               pcap->link_type = pcap_datalink(pcap->handle);
+#ifndef __linux__
+               {
+                       int value = 1;
+                       if (ioctl(pcap->fd, BIOCIMMEDIATE, &value) < 0) {
+                               fr_strerror_printf("Failed setting BIOCIMMEDIATE: %s", fr_syserror(errno));
+                       }
+               }
+#endif
+       }
+               break;
+
+       case PCAP_FILE_IN:
+               pcap->handle = pcap_open_offline(pcap->name, pcap->errbuf);
+               if (!pcap->handle) {
+                       fr_strerror_printf("%s", pcap->errbuf);
+
+                       return -1;
+               }
+               pcap->fd = pcap_get_selectable_fd(pcap->handle);
+               pcap->link_type = pcap_datalink(pcap->handle);
+               break;
+
+       case PCAP_FILE_OUT:
+               if (pcap->link_type < 0) {
+                       pcap->link_type = DLT_EN10MB;
+               }
+               pcap->handle = pcap_open_dead(pcap->link_type, SNAPLEN);
+               if (!pcap->handle) {
+                       fr_strerror_printf("Unknown error occurred opening dead PCAP handle");
+
+                       return -1;
+               }
+               pcap->dumper = pcap_dump_open(pcap->handle, pcap->name);
+               if (!pcap->dumper) {
+                       fr_strerror_printf("%s", pcap_geterr(pcap->handle));
+
+                       return -1;
+               }
+               break;
+
+#ifdef HAVE_PCAP_FOPEN_OFFLINE
+       case PCAP_STDIO_IN:
+               pcap->handle = pcap_fopen_offline(stdin, pcap->errbuf);
+               if (!pcap->handle) {
+                       fr_strerror_printf("%s", pcap->errbuf);
+
+                       return -1;
+               }
+               pcap->fd = pcap_get_selectable_fd(pcap->handle);
+               pcap->link_type = pcap_datalink(pcap->handle);
+               break;
+#else
+       case PCAP_STDIO_IN:
+               fr_strerror_printf("This version of libpcap does not support reading pcap data from streams");
+
+               return -1;
+#endif
+#ifdef HAVE_PCAP_DUMP_FOPEN
+       case PCAP_STDIO_OUT:
+               pcap->handle = pcap_open_dead(DLT_EN10MB, SNAPLEN);
+               pcap->dumper = pcap_dump_fopen(pcap->handle, stdout);
+               if (!pcap->dumper) {
+                       fr_strerror_printf("%s", pcap_geterr(pcap->handle));
+
+                       return -1;
+               }
+               break;
+#else
+       case PCAP_STDIO_OUT:
+               fr_strerror_printf("This version of libpcap does not support writing pcap data to streams");
+
+               return -1;
+#endif
+               case PCAP_INVALID:
+       default:
+               fr_assert(0);
+               fr_strerror_printf("Bad handle type (%i)", pcap->type);
+               return -1;
+       }
+
+       return 0;
+}
+
+/** Apply capture filter to an interface
+ *
+ * @param pcap handle to apply filter to.
+ * @param expression PCAP expression to use as a filter.
+ * @return 0 on success, 1 can't apply to interface, -1 on error.
+ */
+int fr_pcap_apply_filter(fr_pcap_t *pcap, char const *expression)
+{
+       bpf_u_int32 mask = 0;                           /* Our netmask */
+       bpf_u_int32 net = 0;                            /* Our IP */
+       struct bpf_program fp;
+
+       /*
+        *      nflog devices are in the set of devices selected by default.
+        *      Unfortunately there's a bug in all released version of libpcap (as of 2/1/2014)
+        *      which triggers an abort if pcap_setfilter is called on an nflog interface.
+        *
+        *      See here:
+        *      https://github.com/the-tcpdump-group/libpcap/commit/676cf8a61ed240d0a86d471ef419f45ba35dba80
+        */
+#ifdef DLT_NFLOG
+       if (pcap->link_type == DLT_NFLOG) {
+               fr_strerror_printf("NFLOG link-layer type filtering not implemented");
+
+               return 1;
+       }
+#endif
+
+       if (pcap->type == PCAP_INTERFACE_IN) {
+               if (pcap_lookupnet(pcap->name, &net, &mask, pcap->errbuf) < 0) {
+                       fr_strerror_printf("Failed getting IP for interface \"%s\", using defaults: %s",
+                                          pcap->name, pcap->errbuf);
+               }
+       }
+
+       if (pcap_compile(pcap->handle, &fp, expression, 0, net) < 0) {
+               fr_strerror_printf("%s", pcap_geterr(pcap->handle));
+
+               return -1;
+       }
+
+       if (pcap_setfilter(pcap->handle, &fp) < 0) {
+               fr_strerror_printf("%s", pcap_geterr(pcap->handle));
+
+               return -1;
+       }
+
+       return 0;
+}
+
+char *fr_pcap_device_names(TALLOC_CTX *ctx, fr_pcap_t *pcap, char c)
+{
+       fr_pcap_t *pcap_p;
+       char *buff, *p;
+       size_t len = 0, left = 0, wrote;
+
+       if (!pcap) {
+               goto null;
+       }
+
+       for (pcap_p = pcap;
+            pcap_p;
+            pcap_p = pcap_p->next) {
+               len += talloc_array_length(pcap_p->name);       // Talloc array length includes the \0
+       }
+
+       if (!len) {
+               null:
+               return talloc_zero_array(ctx, char, 1);
+       }
+
+       left = len + 1;
+       buff = p = talloc_zero_array(ctx, char, left);
+       for (pcap_p = pcap;
+            pcap_p;
+            pcap_p = pcap_p->next) {
+               wrote = snprintf(p, left, "%s%c", pcap_p->name, c);
+               left -= wrote;
+               p += wrote;
+       }
+       buff[len - 1] = '\0';
+
+       return buff;
+}
+
+/** Returns the length of the link layer header
+ *
+ * Libpcap does not include a decoding function to skip the L2 header, but it does
+ * at least inform us of the type.
+ *
+ * Unfortunately some headers are of variable length (like ethernet), so additional
+ * decoding logic is required.
+ *
+ * @note No header data is returned, this is only meant to be used to determine how
+ * data to consume before attempting to parse the IP header.
+ *
+ * @param data start of PCAP data.
+ * @param len caplen.
+ * @param link_type value returned from pcap_linktype.
+ * @return the length of the header, or -1 on error.
+ */
+ssize_t fr_pcap_link_layer_offset(uint8_t const *data, size_t len, int link_type)
+{
+       uint8_t const *p = data;
+
+       switch (link_type) {
+       case DLT_RAW:
+               break;
+
+       case DLT_NULL:
+       case DLT_LOOP:
+               p += 4;
+               if (((size_t)(p - data)) > len) {
+                       goto ood;
+               }
+               break;
+
+       case DLT_EN10MB:
+               {
+                       uint16_t ether_type;    /* Ethernet type */
+                       int i;
+
+                       p += 12;                /* SRC/DST Mac-Addresses */
+                       if (((size_t)(p - data)) > len) {
+                               goto ood;
+                       }
+
+                       for (i = 0; i < 3; i++) {
+                               ether_type = ntohs(*((uint16_t const *) p));
+                               switch (ether_type) {
+                               /*
+                                *      There are a number of devices out there which
+                                *      double tag with 0x8100 *sigh*
+                                */
+                               case 0x8100:    /* CVLAN */
+                               case 0x9100:    /* SVLAN */
+                               case 0x9200:    /* SVLAN */
+                               case 0x9300:    /* SVLAN */
+                                       p += 4;
+                                       if (((size_t)(p - data)) > len) {
+                                               goto ood;
+                                       }
+                                       break;
+
+                               default:
+                                       p += 2;
+                                       if (((size_t)(p - data)) > len) {
+                                               goto ood;
+                                       }
+                                       goto done;
+                               }
+                       }
+                       fr_strerror_printf("Exceeded maximum level of VLAN tag nesting (2)");
+                       return -1;
+               }
+
+       case DLT_LINUX_SLL:
+               p += 16;
+               if (((size_t)(p - data)) > len) {
+                       goto ood;
+               }
+               break;
+
+       case DLT_PFLOG:
+               p += 28;
+               if (((size_t)(p - data)) > len) {
+                       goto ood;
+               }
+               break;
+
+       default:
+               fr_strerror_printf("Unsupported link layer type %i", link_type);
+       }
+
+       done:
+       return p - data;
+
+       ood:
+       fr_strerror_printf("Out of data, needed %zu bytes, have %zu bytes", (size_t)(p - data), len);
+
+       return -1;
+}
+
+/** Calculate UDP checksum
+ *
+ * Zero out UDP checksum in UDP header before calling fr_udp_checksum to get 'expected' checksum.
+ *
+ * @param data Pointer to the start of the UDP header
+ * @param len value of udp length field in host byte order. Must be validated to make
+ *       sure it won't overrun data buffer.
+ * @param checksum current checksum, leave as 0 to just enable validation.
+ * @param src_addr in network byte order.
+ * @param dst_addr in network byte order.
+ * @return 0 if the checksum is correct, else another number.
+ */
+uint16_t fr_udp_checksum(uint8_t const *data, uint16_t len, uint16_t checksum,
+                        struct in_addr const src_addr, struct in_addr const dst_addr)
+{
+       uint64_t sum = 0;       /* using 64bits avoids overflow check */
+       uint16_t const *p = (uint16_t const *)data;
+
+       uint16_t const *ip_src = (void const *) &src_addr.s_addr;
+       uint16_t const *ip_dst = (void const *) &dst_addr.s_addr;
+       uint16_t i;
+
+       sum += *(ip_src++);
+       sum += *ip_src;
+       sum += *(ip_dst++);
+       sum += *ip_dst;
+
+       sum += htons(IPPROTO_UDP);
+       sum += htons(len);
+
+       for (i = len; i > 1; i -= 2) {
+               sum += *p++;
+       }
+
+       if (i) {
+               sum += (0xff & *(uint8_t const *)p) << 8;
+       }
+
+       sum -= checksum;
+
+       while (sum >> 16) {
+               sum = (sum & 0xffff) + (sum >> 16);
+       }
+
+       return ((uint16_t) ~sum);
+}
+#endif
index f59f187..ed42521 100644 (file)
@@ -852,19 +852,20 @@ static ssize_t vp2data_any(RADIUS_PACKET const *packet,
        case PW_TYPE_IPV6PREFIX:
        case PW_TYPE_IPV4PREFIX:
        case PW_TYPE_ABINARY:
+       case PW_TYPE_ETHERNET:  /* just in case */
                data = (uint8_t const *) &vp->data;
                break;
 
        case PW_TYPE_BYTE:
                len = 1;        /* just in case */
-               array[0] = vp->vp_integer & 0xff;
+               array[0] = vp->vp_byte;
                data = array;
                break;
 
        case PW_TYPE_SHORT:
                len = 2;        /* just in case */
-               array[0] = (vp->vp_integer >> 8) & 0xff;
-               array[1] = vp->vp_integer & 0xff;
+               array[0] = (vp->vp_short >> 8) & 0xff;
+               array[1] = vp->vp_short & 0xff;
                data = array;
                break;
 
@@ -902,8 +903,7 @@ static ssize_t vp2data_any(RADIUS_PACKET const *packet,
        }
 
        default:                /* unknown type: ignore it */
-               fr_strerror_printf("ERROR: Unknown attribute type %d",
-                                  vp->da->type);
+               fr_strerror_printf("ERROR: Unknown attribute type %d", vp->da->type);
                return -1;
        }
 
@@ -1741,7 +1741,7 @@ int rad_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
        case PW_CODE_AUTHENTICATION_REJECT:
        case PW_CODE_ACCESS_CHALLENGE:
                if (!original) {
-                       fr_strerror_printf("ERROR: Cannot sign response packet without a request packet.");
+                       fr_strerror_printf("ERROR: Cannot sign response packet without a request packet");
                        return -1;
                }
                break;
@@ -1900,7 +1900,7 @@ int rad_sign(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
         *      It wasn't assigned an Id, this is bad!
         */
        if (packet->id < 0) {
-               fr_strerror_printf("ERROR: RADIUS packets must be assigned an Id.");
+               fr_strerror_printf("ERROR: RADIUS packets must be assigned an Id");
                return -1;
        }
 
@@ -1929,7 +1929,6 @@ int rad_sign(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
                case PW_CODE_DISCONNECT_NAK:
                case PW_CODE_COA_REQUEST:
                case PW_CODE_COA_ACK:
-               case PW_CODE_COA_NAK:
                        memset(hdr->vector, 0, AUTH_VECTOR_LEN);
                        break;
 
@@ -1938,7 +1937,7 @@ int rad_sign(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
                case PW_CODE_AUTHENTICATION_REJECT:
                case PW_CODE_ACCESS_CHALLENGE:
                        if (!original) {
-                               fr_strerror_printf("ERROR: Cannot sign response packet without a request packet.");
+                               fr_strerror_printf("ERROR: Cannot sign response packet without a request packet");
                                return -1;
                        }
                        memcpy(hdr->vector, original->vector,
@@ -2287,13 +2286,17 @@ int rad_tlv_ok(uint8_t const *data, size_t length,
 }
 
 
-/**
- * @brief See if the data pointed to by PTR is a valid RADIUS packet.
+/** See if the data pointed to by PTR is a valid RADIUS packet.
+ *
+ * Packet is not 'const * const' because we may update data_len, if there's more data
+ * in the UDP packet than in the RADIUS packet.
  *
- *     packet is not 'const * const' because we may update data_len,
- *     if there's more data in the UDP packet than in the RADIUS packet.
+ * @param packet to check
+ * @param flags to control decoding
+ * @param reason if not NULL, will have the failure reason written to where it points.
+ * @return bool, true on success, false on failure.
  */
-int rad_packet_ok(RADIUS_PACKET *packet, int flags)
+bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason)
 {
        uint8_t                 *attr;
        size_t                  totallen;
@@ -2303,6 +2306,7 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
        bool                    require_ma = false;
        bool                    seen_ma = false;
        int                     num_attributes;
+       decode_fail_t           failure = DECODE_FAIL_NONE;
 
        /*
         *      Check for packets smaller than the packet header.
@@ -2317,7 +2321,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)),
                                     packet->data_len, AUTH_HDR_LEN);
-               return 0;
+               failure = DECODE_FAIL_MIN_LENGTH_PACKET;
+               goto finish;
        }
 
 
@@ -2340,7 +2345,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)),
                           hdr->code);
-               return 0;
+               failure = DECODE_FAIL_UNKNOWN_PACKET_CODE;
+               goto finish;
        }
 
        /*
@@ -2371,7 +2377,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)),
                                     totallen, AUTH_HDR_LEN);
-               return 0;
+               failure = DECODE_FAIL_MIN_LENGTH_FIELD;
+               goto finish;
        }
 
        /*
@@ -2403,7 +2410,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)),
                                     packet->data_len, totallen);
-               return 0;
+               failure = DECODE_FAIL_MIN_LENGTH_MISMATCH;
+               goto finish;
        }
 
        /*
@@ -2447,7 +2455,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                   inet_ntop(packet->src_ipaddr.af,
                                             &packet->src_ipaddr.ipaddr,
                                             host_ipaddr, sizeof(host_ipaddr)));
-                       return 0;
+                       failure = DECODE_FAIL_HEADER_OVERFLOW;
+                       goto finish;
                }
 
                /*
@@ -2458,7 +2467,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                   inet_ntop(packet->src_ipaddr.af,
                                             &packet->src_ipaddr.ipaddr,
                                             host_ipaddr, sizeof(host_ipaddr)));
-                       return 0;
+                       failure = DECODE_FAIL_INVALID_ATTRIBUTE;
+                       goto finish;
                }
 
                /*
@@ -2471,7 +2481,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                             &packet->src_ipaddr.ipaddr,
                                             host_ipaddr, sizeof(host_ipaddr)),
                                   attr[0]);
-                       return 0;
+                       failure = DECODE_FAIL_ATTRIBUTE_TOO_SHORT;
+                       goto finish;
                }
 
                /*
@@ -2484,7 +2495,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                             &packet->src_ipaddr.ipaddr,
                                             host_ipaddr, sizeof(host_ipaddr)),
                                           attr[0]);
-                       return 0;
+                       failure = DECODE_FAIL_ATTRIBUTE_OVERFLOW;
+                       goto finish;
                }
 
                /*
@@ -2509,7 +2521,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                                     &packet->src_ipaddr.ipaddr,
                                                     host_ipaddr, sizeof(host_ipaddr)),
                                           attr[1] - 2);
-                               return 0;
+                               failure = DECODE_FAIL_MA_INVALID_LENGTH;
+                               goto finish;
                        }
                        seen_ma = true;
                        break;
@@ -2536,7 +2549,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                           inet_ntop(packet->src_ipaddr.af,
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)));
-               return 0;
+               failure = DECODE_FAIL_ATTRIBUTE_UNDERFLOW;
+               goto finish;
        }
 
        /*
@@ -2551,7 +2565,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)),
                           num_attributes, fr_max_attributes);
-               return 0;
+               failure = DECODE_FAIL_TOO_MANY_ATTRIBUTES;
+               goto finish;
        }
 
        /*
@@ -2565,12 +2580,13 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
         *      Similarly, Status-Server packets MUST contain
         *      Message-Authenticator attributes.
         */
-       if (require_ma && ! seen_ma) {
+       if (require_ma && !seen_ma) {
                fr_strerror_printf("WARNING: Insecure packet from host %s:  Packet does not contain required Message-Authenticator attribute",
                           inet_ntop(packet->src_ipaddr.af,
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)));
-               return 0;
+               failure = DECODE_FAIL_MA_MISSING;
+               goto finish;
        }
 
        /*
@@ -2580,7 +2596,13 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
        packet->id = hdr->id;
        memcpy(packet->vector, hdr->vector, AUTH_VECTOR_LEN);
 
-       return 1;
+
+       finish:
+
+       if (reason) {
+               *reason = failure;
+       }
+       return (failure == DECODE_FAIL_NONE);
 }
 
 
@@ -2629,7 +2651,7 @@ RADIUS_PACKET *rad_recv(int fd, int flags)
         *      packet.
         */
        if (packet->data_len > MAX_PACKET_LEN) {
-               fr_strerror_printf("Discarding packet: Larger than RFC limitation of 4096 bytes.");
+               fr_strerror_printf("Discarding packet: Larger than RFC limitation of 4096 bytes");
                /* packet->data is NULL */
                rad_free(&packet);
                return NULL;
@@ -2642,7 +2664,7 @@ RADIUS_PACKET *rad_recv(int fd, int flags)
         *      packet->data == NULL
         */
        if ((packet->data_len == 0) || !packet->data) {
-               fr_strerror_printf("Empty packet: Socket is not ready.");
+               fr_strerror_printf("Empty packet: Socket is not ready");
                rad_free(&packet);
                return NULL;
        }
@@ -2650,7 +2672,7 @@ RADIUS_PACKET *rad_recv(int fd, int flags)
        /*
         *      See if it's a well-formed RADIUS packet.
         */
-       if (!rad_packet_ok(packet, flags)) {
+       if (!rad_packet_ok(packet, flags, NULL)) {
                rad_free(&packet);
                return NULL;
        }
@@ -2763,7 +2785,7 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                        case PW_CODE_COA_ACK:
                        case PW_CODE_COA_NAK:
                                if (!original) {
-                                       fr_strerror_printf("ERROR: Cannot validate Message-Authenticator in response packet without a request packet.");
+                                       fr_strerror_printf("ERROR: Cannot validate Message-Authenticator in response packet without a request packet");
                                        return -1;
                                }
                                memcpy(packet->data + 4, original->vector, AUTH_VECTOR_LEN);
@@ -2878,13 +2900,6 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
 }
 
 
-static ssize_t data2vp(RADIUS_PACKET *packet,
-                      RADIUS_PACKET const *original,
-                      char const *secret,
-                      DICT_ATTR const *da, uint8_t const *start,
-                      size_t const attrlen, size_t const packetlen,
-                      VALUE_PAIR **pvp);
-
 /**
  * @brief convert a "concatenated" attribute to one long VP.
  */
@@ -3366,12 +3381,12 @@ static ssize_t data2vp_vsas(RADIUS_PACKET *packet,
  *
  * @return -1 on error, or "length".
  */
-static ssize_t data2vp(RADIUS_PACKET *packet,
-                      RADIUS_PACKET const *original,
-                      char const *secret,
-                      DICT_ATTR const *da, uint8_t const *start,
-                      size_t const attrlen, size_t const packetlen,
-                      VALUE_PAIR **pvp)
+ssize_t data2vp(RADIUS_PACKET *packet,
+               RADIUS_PACKET const *original,
+               char const *secret,
+               DICT_ATTR const *da, uint8_t const *start,
+               size_t const attrlen, size_t const packetlen,
+               VALUE_PAIR **pvp)
 {
        int tag = 0;
        size_t datalen;
@@ -3762,11 +3777,11 @@ static ssize_t data2vp(RADIUS_PACKET *packet,
                break;
 
        case PW_TYPE_BYTE:
-               vp->vp_integer = data[0];
+               vp->vp_byte = data[0];
                break;
 
        case PW_TYPE_SHORT:
-               vp->vp_integer = (data[0] << 8) | data[1];
+               vp->vp_short = (data[0] << 8) | data[1];
                break;
 
        case PW_TYPE_INTEGER:
@@ -3948,7 +3963,7 @@ ssize_t rad_vp2data(uint8_t const **out, VALUE_PAIR const *vp)
 
                ret = fr_thread_local_set(rad_vp2data_buff, buffer);
                if (ret != 0) {
-                       fr_strerror_printf("Failed setting up TLS for rad_vp2data buffer: %s", fr_syserror(errno));
+                       fr_strerror_printf("Failed setting up TLS for rad_vp2data buffer: %s", strerror(errno));
                        free(buffer);
                        return -1;
                }
@@ -4676,6 +4691,8 @@ void rad_free(RADIUS_PACKET **radius_packet_ptr)
        if (!radius_packet_ptr || !*radius_packet_ptr) return;
        radius_packet = *radius_packet_ptr;
 
+       VERIFY_PACKET(radius_packet);
+
        pairfree(&radius_packet->vps);
 
        talloc_free(radius_packet);
index a2c5331..426d92a 100644 (file)
@@ -226,7 +226,7 @@ int fr_tcp_read_packet(RADIUS_PACKET *packet, int flags)
        /*
         *      See if it's a well-formed RADIUS packet.
         */
-       if (!rad_packet_ok(packet, flags)) {
+       if (!rad_packet_ok(packet, flags, NULL)) {
                return -1;
        }
 
index e092195..77dfdbf 100644 (file)
@@ -2752,6 +2752,60 @@ int8_t paircmp(VALUE_PAIR *a, VALUE_PAIR *b)
        return paircmp_op(b, a->op, a);
 }
 
+/** Determine equality of two lists
+ *
+ * This is useful for comparing lists of attributes inserted into a binary tree.
+ *
+ * @param a first list of VALUE_PAIRs.
+ * @param b second list of VALUE_PAIRs.
+ * @return -1 if a < b, 0 if the two lists are equal, 1 if a > b, -2 on error.
+ */
+int8_t pairlistcmp(VALUE_PAIR *a, VALUE_PAIR *b)
+{
+       vp_cursor_t a_cursor, b_cursor;
+       VALUE_PAIR *a_p, *b_p;
+       int ret;
+
+       for (a_p = fr_cursor_init(&a_cursor, &a), b_p = fr_cursor_init(&b_cursor, &b);
+            a_p && b_p;
+            a_p = fr_cursor_next(&a_cursor), b_p = fr_cursor_next(&b_cursor)) {
+               /* Same VP, no point doing expensive checks */
+               if (a_p == b_p) {
+                       continue;
+               }
+
+               if (a_p->da < b_p->da) {
+                       return -1;
+               }
+               if (a_p->da > b_p->da) {
+                       return 1;
+               }
+
+               if (a_p->tag < b_p->tag) {
+                       return -1;
+               }
+               if (a_p->tag > b_p->tag) {
+                       return 1;
+               }
+
+               ret = paircmp_value(a_p, b_p);
+               if (ret != 0) {
+                       fr_assert(ret >= -1);   /* Comparison error */
+                       return ret;
+               }
+       }
+
+       if (!a_p && !b_p) {
+               return 0;
+       }
+
+       if (!a_p) {
+               return -1;
+       }
+
+       /* if(!b_p) */
+       return 1;
+}
 
 /** Set the type of the VALUE_PAIR value buffer to match it's DICT_ATTR
  *
@@ -2816,7 +2870,7 @@ void pairmemsteal(VALUE_PAIR *vp, uint8_t const *src)
 
        vp->vp_octets = talloc_steal(vp, src);
        vp->type = VT_DATA;
-       vp->length = talloc_array_length(vp->vp_octets);
+       vp->length = talloc_array_length(vp->vp_strvalue);
        pairtypeset(vp);
 }
 
diff --git a/src/main/collectd.c b/src/main/collectd.c
new file mode 100644 (file)
index 0000000..1310e04
--- /dev/null
@@ -0,0 +1,380 @@
+/*
+ *   This program is is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License, version 2 if the
+ *   License as published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ * @file collectd.c
+ * @brief Helper functions to enabled radsniff to talk to collectd
+ *
+ * @copyright 2013 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ */
+#ifdef HAVE_COLLECTDC_H
+#include <assert.h>
+#include <ctype.h>
+
+#include <collectd/client.h>
+#include <freeradius-devel/radsniff.h>
+
+/** Copy a 64bit unsigned integer into a double
+ *
+ */
+/*
+static void _copy_uint64_to_double(UNUSED rs_t *conf, rs_stats_value_tmpl_t *tmpl)
+{
+       assert(tmpl->src);
+       assert(tmpl->dst);
+
+       *((double *) tmpl->dst) = *((uint64_t *) tmpl->src);
+}
+*/
+
+/*
+static void _copy_uint64_to_uint64(UNUSED rs_t *conf, rs_stats_value_tmpl_t *tmpl)
+{
+       assert(tmpl->src);
+       assert(tmpl->dst);
+
+       *((uint64_t *) tmpl->dst) = *((uint64_t *) tmpl->src);
+}
+*/
+
+static void _copy_double_to_double(UNUSED rs_t *conf, rs_stats_value_tmpl_t *tmpl)
+{
+       assert(tmpl->src);
+       assert(tmpl->dst);
+
+       *((double *) tmpl->dst) = *((double*) tmpl->src);
+}
+
+
+/** Allocates a stats template which describes a single guage/counter
+ *
+ * This is just intended to simplify allocating a fairly complex memory structure
+ * src and dst pointers must be set
+ *
+ * @param ctx Context to allocate collectd struct in.
+ * @param conf Radsniff configuration.
+ * @param plugin_instance usually the type of packet (in our case).
+ * @param type string, the name of a collection of stats e.g. exchange
+ * @param type_instance the name of the counter/guage within the collection e.g. latency.
+ * @param stats structure to derive statistics from.
+ * @param values Value templates used to populate lcc_value_list.
+ * @return a new rs_stats_tmpl_t on success or NULL on failure.
+ */
+static rs_stats_tmpl_t *rs_stats_collectd_init(TALLOC_CTX *ctx, rs_t *conf,
+                                              char const *plugin_instance,
+                                              char const *type, char const *type_instance,
+                                              void *stats,
+                                              rs_stats_value_tmpl_t const *values)
+{
+       static char hostname[255];
+       static char fqdn[LCC_NAME_LEN];
+
+       size_t len;
+       int i;
+       char *p;
+
+       rs_stats_tmpl_t *tmpl;
+       lcc_value_list_t *value;
+
+       assert(conf);
+       assert(type);
+       assert(type_instance);
+
+       for (len = 0; values[len].src; len++) {} ;
+       assert(len > 0);
+
+       /*
+        *      Initialise hostname once so we don't call gethostname every time
+        */
+       if (*fqdn == '\0') {
+               int ret;
+               struct addrinfo hints, *info = NULL;
+
+               if (gethostname(hostname, sizeof(hostname)) < 0) {
+                       ERROR("Error getting hostname: %s", fr_syserror(errno));
+
+                       return NULL;
+               }
+
+               memset(&hints, 0, sizeof hints);
+               hints.ai_family = AF_UNSPEC; /*either IPV4 or IPV6*/
+               hints.ai_socktype = SOCK_STREAM;
+               hints.ai_flags = AI_CANONNAME;
+
+               if ((ret = getaddrinfo(hostname, "radius", &hints, &info)) != 0) {
+                       ERROR("Error getting hostname: %s", gai_strerror(ret));
+                       return NULL;
+               }
+
+               strlcpy(fqdn, info->ai_canonname, sizeof(fqdn));
+
+               freeaddrinfo(info);
+       }
+
+       tmpl = talloc_zero(ctx, rs_stats_tmpl_t);
+       if (!tmpl) {
+               return NULL;
+       }
+
+       tmpl->value_tmpl = talloc_zero_array(tmpl, rs_stats_value_tmpl_t, len);
+       if (!tmpl->value_tmpl) {
+               goto error;
+       }
+
+       tmpl->stats = stats;
+
+       value = talloc_zero(tmpl, lcc_value_list_t);
+       if (!value) {
+               goto error;
+       }
+       tmpl->value = value;
+
+       value->interval = conf->stats.interval;
+       value->values_len = len;
+
+       value->values_types = talloc_zero_array(value, int, len);
+       if (!value->values_types) {
+               goto error;
+       }
+
+       value->values = talloc_zero_array(value, value_t, len);
+       if (!value->values) {
+               goto error;
+       }
+
+       for (i = 0; i < (int) len; i++) {
+               assert(values[i].src);
+               assert(values[i].cb);
+
+               tmpl->value_tmpl[i] = values[i];
+               switch (tmpl->value_tmpl[i].type) {
+                       case LCC_TYPE_COUNTER:
+                               tmpl->value_tmpl[i].dst = &value->values[i].counter;
+                               break;
+
+                       case LCC_TYPE_GAUGE:
+                               tmpl->value_tmpl[i].dst = &value->values[i].gauge;
+                               break;
+
+                       case LCC_TYPE_DERIVE:
+                               tmpl->value_tmpl[i].dst = &value->values[i].derive;
+                               break;
+
+                       case LCC_TYPE_ABSOLUTE:
+                               tmpl->value_tmpl[i].dst = &value->values[i].absolute;
+                               break;
+                       default:
+                               assert(0);
+               }
+               value->values_types[i] = tmpl->value_tmpl[i].type;
+       }
+
+       /*
+        *      These should be OK as is
+        */
+       strlcpy(value->identifier.host, fqdn, sizeof(value->identifier.host));
+
+       /*
+        *      Plugin is ASCII only and no '/'
+        */
+       fr_print_string(conf->stats.prefix, strlen(conf->stats.prefix),
+                       value->identifier.plugin, sizeof(value->identifier.plugin));
+       for (p = value->identifier.plugin; *p; ++p) {
+               if ((*p == '-') || (*p == '/'))*p = '_';
+       }
+
+       /*
+        *      Plugin instance is ASCII only (assuming printable only) and no '/'
+        */
+       fr_print_string(plugin_instance, strlen(plugin_instance),
+                       value->identifier.plugin_instance, sizeof(value->identifier.plugin_instance));
+       for (p = value->identifier.plugin_instance; *p; ++p) {
+               if ((*p == '-') || (*p == '/')) *p = '_';
+       }
+
+       /*
+        *      Type is ASCII only (assuming printable only) and no '/' or '-'
+        */
+       fr_print_string(type, strlen(type),
+                       value->identifier.type, sizeof(value->identifier.type));
+       for (p = value->identifier.type; *p; ++p) {
+               if ((*p == '-') || (*p == '/')) *p = '_';
+       }
+
+       fr_print_string(type_instance, strlen(type_instance),
+                       value->identifier.type_instance, sizeof(value->identifier.type_instance));
+       for (p = value->identifier.type_instance; *p; ++p) {
+               if ((*p == '-') || (*p == '/')) *p = '_';
+       }
+
+
+       return tmpl;
+
+       error:
+       talloc_free(tmpl);
+       return NULL;
+}
+
+
+/** Setup stats templates for latency
+ *
+ */
+rs_stats_tmpl_t *rs_stats_collectd_init_latency(TALLOC_CTX *ctx, rs_stats_tmpl_t **out, rs_t *conf,
+                                               char const *type, rs_latency_t *stats, PW_CODE code)
+{
+       rs_stats_tmpl_t **tmpl, *last;
+       char *p;
+       char buffer[LCC_NAME_LEN];
+       tmpl = out;
+
+       rs_stats_value_tmpl_t rtx[(RS_RETRANSMIT_MAX + 1) + 1 + 1];     // RTX bins + 0 bin + lost + NULL
+       int i;
+
+       /* not static so were thread safe */
+       rs_stats_value_tmpl_t const _packet_count[] = {
+               { &stats->interval.received, LCC_TYPE_GAUGE,  _copy_double_to_double, NULL },
+               { &stats->interval.linked, LCC_TYPE_GAUGE,  _copy_double_to_double, NULL },
+               { &stats->interval.unlinked, LCC_TYPE_GAUGE,  _copy_double_to_double, NULL },
+               { &stats->interval.reused, LCC_TYPE_GAUGE,  _copy_double_to_double, NULL },
+               { NULL, 0, NULL, NULL }
+       };
+
+       rs_stats_value_tmpl_t const _latency[] = {
+               { &stats->latency_smoothed, LCC_TYPE_GAUGE, _copy_double_to_double, NULL },
+               { &stats->interval.latency_average, LCC_TYPE_GAUGE, _copy_double_to_double, NULL },
+               { &stats->interval.latency_high, LCC_TYPE_GAUGE, _copy_double_to_double, NULL },
+               { &stats->interval.latency_low, LCC_TYPE_GAUGE, _copy_double_to_double, NULL },
+               { NULL, 0, NULL, NULL }
+       };
+
+#define INIT_STATS(_ti, _v) do {\
+               strlcpy(buffer, fr_packet_codes[code], sizeof(buffer)); \
+               for (p = buffer; *p; ++p) *p = tolower(*p);\
+               last = *tmpl = rs_stats_collectd_init(ctx, conf, type, _ti, buffer, stats, _v);\
+               if (!*tmpl) {\
+                       TALLOC_FREE(*out);\
+                       return NULL;\
+               }\
+               tmpl = &(*tmpl)->next;\
+               ctx = *tmpl;\
+               } while (0)
+
+
+       INIT_STATS("radius_count", _packet_count);
+       INIT_STATS("radius_latency", _latency);
+
+       for (i = 0; i < (RS_RETRANSMIT_MAX + 1); i++) {
+               rtx[i].src = &stats->interval.rt[i];
+               rtx[i].type = LCC_TYPE_GAUGE;
+               rtx[i].cb = _copy_double_to_double;
+               rtx[i].dst = NULL;
+       }
+
+       rtx[i].src = &stats->interval.lost;
+       rtx[i].type = LCC_TYPE_GAUGE;
+       rtx[i].cb = _copy_double_to_double;
+       rtx[i].dst = NULL;
+
+       memset(&rtx[++i], 0, sizeof(rs_stats_value_tmpl_t));
+
+       INIT_STATS("radius_rtx", rtx);
+
+       return last;
+}
+
+/** Refresh and send the stats to the collectd server
+ *
+ */
+void rs_stats_collectd_do_stats(rs_t *conf, rs_stats_tmpl_t *tmpls, struct timeval *now)
+{
+       rs_stats_tmpl_t *tmpl = tmpls;
+       char identifier[6 * LCC_NAME_LEN];
+       int i;
+
+       while (tmpl) {
+               /*
+                *      Refresh the value of whatever were sending
+                */
+               for (i = 0; i < (int) tmpl->value->values_len; i++) {
+                       tmpl->value_tmpl[i].cb(conf, &tmpl->value_tmpl[i]);
+               }
+
+               tmpl->value->time = now->tv_sec;
+
+               lcc_identifier_to_string(conf->stats.handle, identifier, sizeof(identifier), &tmpl->value->identifier);
+
+               if (lcc_putval(conf->stats.handle, tmpl->value) < 0) {
+                       char const *error;
+
+                       error = lcc_strerror(conf->stats.handle);
+                       ERROR("Failed PUTVAL \"%s\" interval=%i %" PRIu64 " : %s",
+                             identifier,
+                             (int) tmpl->value->interval,
+                             (uint64_t) tmpl->value->time,
+                             error ? error : "unknown error");
+               }
+
+               tmpl = tmpl->next;
+       }
+}
+
+/** Connect to a collectd server for stats output
+ *
+ * @param[in,out] conf radsniff configuration, we write the generated handle here.
+ * @return 0 on success -1 on failure.
+ */
+int rs_stats_collectd_open(rs_t *conf)
+{
+       assert(conf->stats.collectd);
+
+       /*
+        *      Tear down stale connections gracefully.
+        */
+       rs_stats_collectd_close(conf);
+
+       /*
+        *      There's no way to get the error from the connection handle
+        *      because it's freed on failure, before lcc returns.
+        */
+       if (lcc_connect(conf->stats.collectd, &conf->stats.handle) < 0) {
+               ERROR("Failed opening connection to collectd: %s", fr_syserror(errno));
+               return -1;
+       }
+       DEBUG2("Connected to \"%s\"", conf->stats.collectd);
+
+       assert(conf->stats.handle);
+       return 0;
+}
+
+/** Close connection
+ *
+ * @param[in,out] conf radsniff configuration.
+ * @return 0 on success -1 on failure.
+ */
+int rs_stats_collectd_close(rs_t *conf)
+{
+       assert(conf->stats.collectd);
+
+       int ret = 0;
+
+       if (conf->stats.handle) {
+               ret = lcc_disconnect(conf->stats.handle);
+               conf->stats.handle = NULL;
+       }
+
+       return ret;
+}
+#endif
index ecd0fd9..caae277 100644 (file)
@@ -1,60 +1,56 @@
 /*
- *  radsniff.c Display the RADIUS traffic on the network.
+ *   This program is is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License, version 2 if the
+ *   License as published by the Free Software Foundation.
  *
- *  Version:    $Id$
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
  *
- *  This program is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU General Public License
- *  as published by the Free Software Foundation; either version 2
- *  of the License, or (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ * @file radsniff.c
+ * @brief Capture, filter, and generate statistics for RADIUS traffic
  *
- *  Copyright 2006  The FreeRADIUS server project
- *  Copyright 2006  Nicolas Baradakis <nicolas.baradakis@cegetel.net>
+ * @copyright 2013 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @copyright 2006 The FreeRADIUS server project
+ * @copyright 2006 Nicolas Baradakis <nicolas.baradakis@cegetel.net>
  */
 
 RCSID("$Id$")
 
 #define _LIBRADIUS 1
+#include <assert.h>
+#include <time.h>
+#include <math.h>
 #include <freeradius-devel/libradius.h>
-
-#include <pcap.h>
+#include <freeradius-devel/event.h>
 
 #include <freeradius-devel/radpaths.h>
 #include <freeradius-devel/conf.h>
+#include <freeradius-devel/pcap.h>
 #include <freeradius-devel/radsniff.h>
 
-static char const *radius_secret = "testing123";
-static VALUE_PAIR *filter_vps = NULL;
-
-static bool do_sort = false;
-static bool to_stdout = false;
-static FILE *log_dst;
-
-#ifndef PCAP_NETMASK_UNKNOWN
-#  define PCAP_NETMASK_UNKNOWN 0
+#ifdef HAVE_COLLECTDC_H
+#  include <collectd/client.h>
 #endif
 
-#undef DEBUG1
-#define DEBUG1 if (fr_debug_flag > 2) fprintf
-#undef DEBUG
-#define DEBUG if (fr_debug_flag > 1) fprintf
-#undef INFO
-#define INFO if (fr_debug_flag > 0) fprintf
-
+static rs_t *conf;
 struct timeval start_pcap = {0, 0};
-static rbtree_t *filter_tree = NULL;
+static char timestr[50];
+
 static rbtree_t *request_tree = NULL;
-static pcap_dumper_t *out = NULL;
-static RADIUS_PACKET *nullpacket = NULL;
+static rbtree_t *link_tree = NULL;
+static fr_event_list_t *events;
+static bool cleanup;
+
+static int self_pipe[2] = {-1, -1};            //!< Signals from sig handlers
 
 typedef int (*rbcmp)(void const *, void const *);
 
@@ -64,90 +60,107 @@ static char const *radsniff_version = "radsniff version " RADIUSD_VERSION_STRING
 #endif
 ", built on " __DATE__ " at " __TIME__;
 
-static int filter_packet(RADIUS_PACKET *packet)
+static int rs_useful_codes[] = {
+       PW_CODE_AUTHENTICATION_REQUEST,         //!< RFC2865 - Authentication request
+       PW_CODE_AUTHENTICATION_ACK,             //!< RFC2865 - Access-Accept
+       PW_CODE_AUTHENTICATION_REJECT,          //!< RFC2865 - Access-Reject
+       PW_CODE_ACCOUNTING_REQUEST,             //!< RFC2866 - Accounting-Request
+       PW_CODE_ACCOUNTING_RESPONSE,            //!< RFC2866 - Accounting-Response
+       PW_CODE_ACCESS_CHALLENGE,               //!< RFC2865 - Access-Challenge
+       PW_CODE_STATUS_SERVER,                  //!< RFC2865/RFC5997 - Status Server (request)
+       PW_CODE_STATUS_CLIENT,                  //!< RFC2865/RFC5997 - Status Server (response)
+       PW_CODE_DISCONNECT_REQUEST,             //!< RFC3575/RFC5176 - Disconnect-Request
+       PW_CODE_DISCONNECT_ACK,                 //!< RFC3575/RFC5176 - Disconnect-Ack (positive)
+       PW_CODE_DISCONNECT_NAK,                 //!< RFC3575/RFC5176 - Disconnect-Nak (not willing to perform)
+       PW_CODE_COA_REQUEST,                    //!< RFC3575/RFC5176 - CoA-Request
+       PW_CODE_COA_ACK,                        //!< RFC3575/RFC5176 - CoA-Ack (positive)
+       PW_CODE_COA_NAK,                        //!< RFC3575/RFC5176 - CoA-Nak (not willing to perform)
+};
+
+const FR_NAME_NUMBER rs_events[] = {
+       { "received",   RS_NORMAL       },
+       { "norsp",      RS_LOST         },
+       { "rtx",        RS_RTX          },
+       { "noreq",      RS_UNLINKED     },
+       { "reused",     RS_REUSED       },
+       { "error",      RS_ERROR        },
+       {  NULL , -1 }
+};
+
+static void NEVER_RETURNS usage(int status);
+
+/** Fork and kill the parent process, writing out our PID
+ *
+ * @param pidfile the PID file to write our PID to
+ */
+static void rs_daemonize(char const *pidfile)
 {
-       vp_cursor_t cursor, check_cursor;
-       VALUE_PAIR *check_item;
-       VALUE_PAIR *vp;
-       unsigned int pass, fail;
-       int compare;
+       FILE *fp;
+       pid_t pid, sid;
 
-       pass = fail = 0;
-       for (vp = fr_cursor_init(&cursor, &packet->vps);
-            vp;
-            vp = fr_cursor_next(&cursor)) {
-               for (check_item = fr_cursor_init(&check_cursor, &filter_vps);
-                    check_item;
-                    check_item = fr_cursor_next(&check_cursor))
-                       if ((check_item->da == vp->da)
-                        && (check_item->op != T_OP_SET)) {
-                               compare = paircmp(check_item, vp);
-                               if (compare == 1)
-                                       pass++;
-                               else
-                                       fail++;
-                       }
+       pid = fork();
+       if (pid < 0) {
+               exit(EXIT_FAILURE);
        }
 
-       if (fail == 0 && pass != 0) {
-               /*
-                *      Cache authentication requests, as the replies
-                *      may not match the RADIUS filter.
-                */
-               if ((packet->code == PW_CODE_AUTHENTICATION_REQUEST) ||
-                   (packet->code == PW_CODE_ACCOUNTING_REQUEST)) {
-                       rbtree_deletebydata(filter_tree, packet);
-
-                       if (!rbtree_insert(filter_tree, packet)) {
-                       oom:
-                               fprintf(stderr, "radsniff: Out of memory\n");
-                               exit(1);
-                       }
-               }
-               return 0;       /* matched */
+       /*
+        *      Kill the parent...
+        */
+       if (pid > 0) {
+               close(self_pipe[0]);
+               close(self_pipe[1]);
+               exit(EXIT_SUCCESS);
        }
 
        /*
-        *      Don't create erroneous matches.
+        *      Continue as the child.
         */
-       if ((packet->code == PW_CODE_AUTHENTICATION_REQUEST) ||
-           (packet->code == PW_CODE_ACCOUNTING_REQUEST)) {
-               rbtree_deletebydata(filter_tree, packet);
-               return 1;
+
+       /* Create a new SID for the child process */
+       sid = setsid();
+       if (sid < 0) {
+               exit(EXIT_FAILURE);
        }
 
        /*
-        *      Else see if a previous Access-Request
-        *      matched.  If so, also print out the
-        *      matching accept, reject, or challenge.
+        *      Change the current working directory. This prevents the current
+        *      directory from being locked; hence not being able to remove it.
         */
-       if ((packet->code == PW_CODE_AUTHENTICATION_ACK) ||
-           (packet->code == PW_CODE_AUTHENTICATION_REJECT) ||
-           (packet->code == PW_CODE_ACCESS_CHALLENGE) ||
-           (packet->code == PW_CODE_ACCOUNTING_RESPONSE)) {
-               RADIUS_PACKET *reply;
+       if ((chdir("/")) < 0) {
+               exit(EXIT_FAILURE);
+       }
 
-               /*
-                *      This swaps the various fields.
-                */
-               reply = rad_alloc_reply(NULL, packet);
-               if (!reply) goto oom;
+       /*
+        *      And write it AFTER we've forked, so that we write the
+        *      correct PID.
+        */
+       fp = fopen(pidfile, "w");
+       if (fp != NULL) {
+               fprintf(fp, "%d\n", (int) sid);
+               fclose(fp);
+       } else {
+               ERROR("Failed creating PID file %s: %s", pidfile, fr_syserror(errno));
+               exit(EXIT_FAILURE);
+       }
 
-               compare = 1;
-               if (rbtree_finddata(filter_tree, reply)) {
-                       compare = 0;
+       /*
+        *      Close stdout and stderr if they've not been redirected.
+        */
+       if (isatty(fileno(stdout))) {
+               if (!freopen("/dev/null", "w", stdout)) {
+                       exit(EXIT_FAILURE);
                }
-
-               rad_free(&reply);
-               return compare;
        }
 
-       return 1;
+       if (isatty(fileno(stderr))) {
+               if (!freopen("/dev/null", "w", stderr)) {
+                       exit(EXIT_FAILURE);
+               }
+       }
 }
 
 #define USEC 1000000
-static void tv_sub(struct timeval const *end, struct timeval const *start,
-                  struct timeval *elapsed)
+static void rs_tv_sub(struct timeval const *end, struct timeval const *start, struct timeval *elapsed)
 {
        elapsed->tv_sec = end->tv_sec - start->tv_sec;
        if (elapsed->tv_sec > 0) {
@@ -165,235 +178,1635 @@ static void tv_sub(struct timeval const *end, struct timeval const *start,
        }
 }
 
-static void got_packet(UNUSED uint8_t *args, struct pcap_pkthdr const*header, uint8_t const *data)
+static void rs_tv_add_ms(struct timeval const *start, unsigned long interval, struct timeval *result) {
+    result->tv_sec = start->tv_sec + (interval / 1000);
+    result->tv_usec = start->tv_usec + ((interval % 1000) * 1000);
+
+    if (result->tv_usec > USEC) {
+       result->tv_usec -= USEC;
+       result->tv_sec++;
+    }
+}
+
+static void rs_time_print(char *out, size_t len, struct timeval const *t)
+{
+       size_t ret;
+       struct timeval now;
+       uint32_t usec;
+
+       if (!t) {
+               gettimeofday(&now, NULL);
+               t = &now;
+       }
+
+       ret = strftime(out, len, "%Y-%m-%d %H:%M:%S", localtime(&t->tv_sec));
+       if (ret >= len) {
+               return;
+       }
+
+       usec = t->tv_usec;
+
+       if (usec) {
+               while (usec < 100000) usec *= 10;
+               snprintf(out + ret, len - ret, ".%i", usec);
+       } else {
+               snprintf(out + ret, len - ret, ".000000");
+       }
+}
+
+static void rs_packet_print_null(UNUSED uint64_t count, UNUSED rs_status_t status, UNUSED fr_pcap_t *handle,
+                                UNUSED RADIUS_PACKET *packet, UNUSED struct timeval *elapsed,
+                                UNUSED struct timeval *latency, UNUSED bool response, UNUSED bool body)
+{
+       return;
+}
+
+static size_t rs_prints_csv(char *out, size_t outlen, char const *in, size_t inlen)
+{
+       char const      *start = out;
+       uint8_t const   *str = (uint8_t const *) in;
+
+       if (!in) {
+               if (outlen) {
+                       *out = '\0';
+               }
+
+               return 0;
+       }
+
+       if (inlen == 0) {
+               inlen = strlen(in);
+       }
+
+       while ((inlen > 0) && (outlen > 2)) {
+               /*
+                *      Escape double quotes with... MORE DOUBLE QUOTES!
+                */
+               if (*str == '"') {
+                       *out++ = '"';
+                       outlen--;
+               }
+
+               /*
+                *      Safe chars which require no escaping
+                */
+               if ((*str == '\r') || (*str == '\n') || ((*str >= '\x20') && (*str <= '\x7E'))) {
+                       *out++ = *str++;
+                       outlen--;
+                       inlen--;
+
+                       continue;
+               }
+
+               /*
+                *      Everything else is dropped
+                */
+               str++;
+               inlen--;
+       }
+       *out = '\0';
+
+       return out - start;
+}
+
+static void rs_packet_print_csv_header(void)
+{
+       char buffer[2048];
+       char *p = buffer;
+       int i;
+
+       ssize_t len, s = sizeof(buffer);
+
+       len = strlcpy(p, "\"Status\",\"Count\",\"Time\",\"Latency\",\"Type\",\"Interface\","
+                     "\"Src IP\",\"Src Port\",\"Dst IP\",\"Dst Port\",\"ID\",", s);
+       p += len;
+       s -= len;
+
+       if (s <= 0) return;
+
+       for (i = 0; i < conf->list_da_num; i++) {
+               char const *in;
+
+               *p++ = '"';
+               s -= 1;
+               if (s <= 0) return;
+
+               for (in = conf->list_da[i]->name; *in; in++) {
+                       *p++ = *in;
+                       s -= len;
+                       if (s <= 0) return;
+               }
+
+               *p++ = '"';
+               s -= 1;
+               if (s <= 0) return;
+               *p++ = ',';
+               s -= 1;
+               if (s <= 0) return;
+       }
+
+       *--p = '\0';
+
+       fprintf(stdout , "%s\n", buffer);
+}
+
+static void rs_packet_print_csv(uint64_t count, rs_status_t status, fr_pcap_t *handle, RADIUS_PACKET *packet,
+                               UNUSED struct timeval *elapsed, struct timeval *latency, UNUSED bool response,
+                               bool body)
+{
+       char const *status_str;
+       char buffer[2048];
+       char *p = buffer;
+
+       char src[INET6_ADDRSTRLEN];
+       char dst[INET6_ADDRSTRLEN];
+
+       ssize_t len, s = sizeof(buffer);
+
+       inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, src, sizeof(src));
+       inet_ntop(packet->dst_ipaddr.af, &packet->dst_ipaddr.ipaddr, dst, sizeof(dst));
+
+       status_str = fr_int2str(rs_events, status, NULL);
+       assert(status_str);
+
+       len = snprintf(p, s, "%s,%" PRIu64 ",%s,", status_str, count, timestr);
+       p += len;
+       s -= len;
+
+       if (s <= 0) return;
+
+       if (latency) {
+               len = snprintf(p, s, "%u.%03u,",
+                              (unsigned int) latency->tv_sec, ((unsigned int) latency->tv_usec / 1000));
+               p += len;
+               s -= len;
+       } else {
+               *p = ',';
+               p += 1;
+               s -= 1;
+       }
+
+       if (s <= 0) return;
+
+       /* Status, Type, Interface, Src, Src port, Dst, Dst port, ID */
+       if (is_radius_code(packet->code)) {
+               len = snprintf(p, s, "%s,%s,%s,%i,%s,%i,%i,", fr_packet_codes[packet->code], handle->name,
+                              src, packet->src_port, dst, packet->dst_port, packet->id);
+       } else {
+               len = snprintf(p, s, "%i,%s,%s,%i,%s,%i,%i,", packet->code, handle->name,
+                              src, packet->src_port, dst, packet->dst_port, packet->id);
+       }
+       p += len;
+       s -= len;
+
+       if (s <= 0) return;
+
+       if (body) {
+               int i;
+               VALUE_PAIR *vp;
+
+               for (i = 0; i < conf->list_da_num; i++) {
+                       vp = pairfind_da(packet->vps, conf->list_da[i], TAG_ANY);
+                       if (vp && (vp->length > 0)) {
+                               if (conf->list_da[i]->type == PW_TYPE_STRING) {
+                                       *p++ = '"';
+                                       s--;
+                                       if (s <= 0) return;
+
+                                       len = rs_prints_csv(p, s, vp->vp_strvalue, vp->length);
+                                       p += len;
+                                       s -= len;
+                                       if (s <= 0) return;
+
+                                       *p++ = '"';
+                                       s--;
+                                       if (s <= 0) return;
+                               } else {
+                                       len = vp_prints_value(p, s, vp, 0);
+                                       p += len;
+                                       s -= len;
+                                       if (s <= 0) return;
+                               }
+                       }
+
+                       *p++ = ',';
+                       s -= 1;
+                       if (s <= 0) return;
+               }
+       } else {
+               s -= conf->list_da_num;
+               if (s <= 0) return;
+
+               memset(p, ',', conf->list_da_num);
+               p += conf->list_da_num;
+       }
+
+       *--p = '\0';
+       fprintf(stdout , "%s\n", buffer);
+}
+
+static void rs_packet_print_fancy(uint64_t count, rs_status_t status, fr_pcap_t *handle, RADIUS_PACKET *packet,
+                                 struct timeval *elapsed, struct timeval *latency, bool response, bool body)
+{
+       char buffer[2048];
+       char *p = buffer;
+
+       char src[INET6_ADDRSTRLEN];
+       char dst[INET6_ADDRSTRLEN];
+
+       ssize_t len, s = sizeof(buffer);
+
+       inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, src, sizeof(src));
+       inet_ntop(packet->dst_ipaddr.af, &packet->dst_ipaddr.ipaddr, dst, sizeof(dst));
+
+       /* Only print out status str if something's not right */
+       if (status != RS_NORMAL) {
+               char const *status_str;
+
+               status_str = fr_int2str(rs_events, status, NULL);
+               assert(status_str);
+
+               len = snprintf(p, s, "** %s ** ", status_str);
+               p += len;
+               s -= len;
+               if (s <= 0) return;
+       }
+
+       if (is_radius_code(packet->code)) {
+               len = snprintf(p, s, "%s Id %i %s:%s:%d %s %s:%i ",
+                              fr_packet_codes[packet->code],
+                              packet->id,
+                              handle->name,
+                              response ? dst : src,
+                              response ? packet->dst_port : packet->src_port,
+                              response ? "<-" : "->",
+                              response ? src : dst ,
+                              response ? packet->src_port : packet->dst_port);
+       } else {
+               len = snprintf(p, s, "%i Id %i %s:%s:%i %s %s:%i ",
+                              packet->code,
+                              packet->id,
+                              handle->name,
+                              response ? dst : src,
+                              response ? packet->dst_port : packet->src_port,
+                              response ? "<-" : "->",
+                              response ? src : dst ,
+                              response ? packet->src_port : packet->dst_port);
+       }
+       p += len;
+       s -= len;
+       if (s <= 0) return;
+
+       if (elapsed) {
+               len = snprintf(p, s, "+%u.%03u ",
+                              (unsigned int) elapsed->tv_sec, ((unsigned int) elapsed->tv_usec / 1000));
+               p += len;
+               s -= len;
+               if (s <= 0) return;
+       }
+
+       if (latency) {
+               len = snprintf(p, s, "+%u.%03u ",
+                              (unsigned int) latency->tv_sec, ((unsigned int) latency->tv_usec / 1000));
+               p += len;
+               s -= len;
+               if (s <= 0) return;
+       }
+
+       *--p = '\0';
+
+       RIDEBUG("%s", buffer);
+
+       if (body) {
+               /*
+                *      Print out verbose HEX output
+                */
+               if (conf->print_packet && (fr_debug_flag > 3)) {
+                       rad_print_hex(packet);
+               }
+
+               if (conf->print_packet && (fr_debug_flag > 1) && packet->vps) {
+                       pairsort(&packet->vps, attrtagcmp);
+                       vp_printlist(fr_log_fp, packet->vps);
+               }
+       }
+}
+
+static void rs_stats_print(rs_latency_t *stats, PW_CODE code)
+{
+       int i;
+       bool have_rt = false;
+
+       for (i = 0; i <= RS_RETRANSMIT_MAX; i++) {
+               if (stats->interval.rt[i]) {
+                       have_rt = true;
+               }
+       }
+
+       if (!stats->interval.received && !have_rt && !stats->interval.reused) {
+               return;
+       }
+
+       if (stats->interval.received || stats->interval.linked) {
+               INFO("%s counters:", fr_packet_codes[code]);
+               if (stats->interval.received > 0) {
+                       INFO("\tTotal     : %.3lf/s" , stats->interval.received);
+               }
+       }
+
+       if (stats->interval.linked > 0) {
+               INFO("\tLinked    : %.3lf/s", stats->interval.linked);
+               INFO("\tUnlinked  : %.3lf/s", stats->interval.unlinked);
+               INFO("%s latency:", fr_packet_codes[code]);
+               INFO("\tHigh      : %.3lfms", stats->interval.latency_high);
+               INFO("\tLow       : %.3lfms", stats->interval.latency_low);
+               INFO("\tAverage   : %.3lfms", stats->interval.latency_average);
+               INFO("\tMA        : %.3lfms", stats->latency_smoothed);
+       }
+
+       if (have_rt || stats->interval.lost || stats->interval.reused) {
+               INFO("%s retransmits & loss:",  fr_packet_codes[code]);
+
+               if (stats->interval.lost) {
+                       INFO("\tLost      : %.3lf/s", stats->interval.lost);
+               }
+
+               if (stats->interval.reused) {
+                       INFO("\tID Reused : %.3lf/s", stats->interval.reused);
+               }
+
+               for (i = 0; i <= RS_RETRANSMIT_MAX; i++) {
+                       if (!stats->interval.rt[i]) {
+                               continue;
+                       }
+
+                       if (i != RS_RETRANSMIT_MAX) {
+                               INFO("\tRT (%i)    : %.3lf/s", i, stats->interval.rt[i]);
+                       } else {
+                               INFO("\tRT (%i+)   : %.3lf/s", i, stats->interval.rt[i]);
+                       }
+               }
+       }
+}
+
+/** Query libpcap to see if it dropped any packets
+ *
+ * We need to check to see if libpcap dropped any packets and if it did, we need to stop stats output for long
+ * enough for inaccurate statistics to be cleared out.
+ *
+ * @param in pcap handle to check.
+ * @param interval time between checks (used for debug output)
+ * @return 0, no drops, -1 we couldn't check, -2 dropped because of buffer exhaustion, -3 dropped because of NIC.
+ */
+static int rs_check_pcap_drop(fr_pcap_t *in, int interval) {
+       int ret = 0;
+       struct pcap_stat pstats;
+
+       if (pcap_stats(in->handle, &pstats) != 0) {
+               ERROR("%s failed retrieving pcap stats: %s", in->name, pcap_geterr(in->handle));
+               return -1;
+       }
+
+       INFO("\t%s%*s: %.3lf/s", in->name, (int) (10 - strlen(in->name)), "",
+            ((double) (pstats.ps_recv - in->pstats.ps_recv)) / interval);
+
+       if (pstats.ps_drop - in->pstats.ps_drop > 0) {
+               ERROR("%s dropped %i packets: Buffer exhaustion", in->name, pstats.ps_drop - in->pstats.ps_drop);
+               ret = -2;
+       }
+
+       if (pstats.ps_ifdrop - in->pstats.ps_ifdrop > 0) {
+               ERROR("%s dropped %i packets: Interface", in->name, pstats.ps_ifdrop - in->pstats.ps_ifdrop);
+               ret = -3;
+       }
+
+       in->pstats = pstats;
+
+       return ret;
+}
+
+/** Update smoothed average
+ *
+ */
+static void rs_stats_process_latency(rs_latency_t *stats)
+{
+       /*
+        *      If we didn't link any packets during this interval, we don't have a value to return.
+        *      returning 0 is misleading as it would be like saying the latency had dropped to 0.
+        *      We instead set NaN which libcollectd converts to a 'U' or unknown value.
+        *
+        *      This will cause gaps in graphs, but is completely legitimate as we are missing data.
+        *      This is unfortunately an effect of being just a passive observer.
+        */
+       if (stats->interval.linked_total == 0) {
+               double unk = strtod("NAN()", (char **) NULL);
+
+               stats->interval.latency_average = unk;
+               stats->interval.latency_high = unk;
+               stats->interval.latency_low = unk;
+
+               /*
+                *      We've not yet been able to determine latency, so latency_smoothed is also NaN
+                */
+               if (stats->latency_smoothed_count == 0) {
+                       stats->latency_smoothed = unk;
+               }
+               return;
+       }
+
+       if (stats->interval.linked_total && stats->interval.latency_total) {
+               stats->interval.latency_average = (stats->interval.latency_total / stats->interval.linked_total);
+       }
+
+       if (isnan(stats->latency_smoothed)) {
+               stats->latency_smoothed = 0;
+       }
+       if (stats->interval.latency_average > 0) {
+               stats->latency_smoothed_count++;
+               stats->latency_smoothed += ((stats->interval.latency_average - stats->latency_smoothed) /
+                                      ((stats->latency_smoothed_count < 100) ? stats->latency_smoothed_count : 100));
+       }
+}
+
+static void rs_stats_process_counters(rs_latency_t *stats)
+{
+       int i;
+
+       stats->interval.received = ((long double) stats->interval.received_total) / conf->stats.interval;
+       stats->interval.linked = ((long double) stats->interval.linked_total) / conf->stats.interval;
+       stats->interval.unlinked = ((long double) stats->interval.unlinked_total) / conf->stats.interval;
+       stats->interval.reused = ((long double) stats->interval.reused_total) / conf->stats.interval;
+       stats->interval.lost = ((long double) stats->interval.lost_total) / conf->stats.interval;
+
+       for (i = 0; i < RS_RETRANSMIT_MAX; i++) {
+               stats->interval.rt[i] = ((long double) stats->interval.rt_total[i]) / conf->stats.interval;
+       }
+}
+
+/** Process stats for a single interval
+ *
+ */
+static void rs_stats_process(void *ctx)
 {
+       size_t i;
+       size_t rs_codes_len = (sizeof(rs_useful_codes) / sizeof(*rs_useful_codes));
+       fr_pcap_t               *in_p;
+       rs_update_t             *this = ctx;
+       rs_stats_t              *stats = this->stats;
+       struct timeval          now;
+
+       gettimeofday(&now, NULL);
+
+       stats->intervals++;
 
-       static int count = 1;                   /* Packets seen */
+       INFO("######### Stats Iteration %i #########", stats->intervals);
 
        /*
-        *  Define pointers for packet's attributes
+        *      Verify that none of the pcap handles have dropped packets.
         */
-       const struct ip_header *ip;             /* The IP header */
-       const struct udp_header *udp;           /* The UDP header */
-       const uint8_t *payload;                 /* Packet payload */
+       INFO("Interface capture rate:");
+       for (in_p = this->in;
+            in_p;
+            in_p = in_p->next) {
+               if (rs_check_pcap_drop(in_p, conf->stats.interval) < 0) {
+                       ERROR("Muting stats for the next %i milliseconds", conf->stats.timeout);
+
+                       rs_tv_add_ms(&now, conf->stats.timeout, &stats->quiet);
+                       goto clear;
+               }
+       }
+
+       if ((stats->quiet.tv_sec + (stats->quiet.tv_usec / 1000000.0)) -
+           (now.tv_sec + (now.tv_usec / 1000000.0)) > 0) {
+               INFO("Stats muted because of warmup, or previous error");
+               goto clear;
+       }
 
        /*
-        *  And define the size of the structures we're using
+        *      Latency stats need a bit more work to calculate the SMA.
+        *
+        *      No further work is required for codes.
         */
-       int size_ethernet = sizeof(struct ethernet_header);
-       int size_ip = sizeof(struct ip_header);
-       int size_udp = sizeof(struct udp_header);
+       for (i = 0; i < rs_codes_len; i++) {
+               rs_stats_process_latency(&stats->exchange[rs_useful_codes[i]]);
+               rs_stats_process_counters(&stats->exchange[rs_useful_codes[i]]);
+               if (fr_debug_flag > 0) {
+                       rs_stats_print(&stats->exchange[rs_useful_codes[i]], rs_useful_codes[i]);
+               }
+       }
 
+#ifdef HAVE_COLLECTDC_H
        /*
-        *  For FreeRADIUS
+        *      Update stats in collectd using the complex structures we
+        *      initialised earlier.
         */
-       RADIUS_PACKET *packet, *original;
-       struct timeval elapsed;
+       if ((conf->stats.out == RS_STATS_OUT_COLLECTD) && conf->stats.handle) {
+               rs_stats_collectd_do_stats(conf, conf->stats.tmpl, &now);
+       }
+#endif
 
+       clear:
        /*
-        * Define our packet's attributes
+        *      Rinse and repeat...
         */
+       for (i = 0; i < rs_codes_len; i++) {
+               memset(&stats->exchange[rs_useful_codes[i]].interval, 0,
+                      sizeof(stats->exchange[rs_useful_codes[i]].interval));
+       }
 
-       if ((data[0] == 2) && (data[1] == 0) &&
-           (data[2] == 0) && (data[3] == 0)) {
-               ip = (struct ip_header const *) (data + 4);
+       {
+               static fr_event_t *event;
 
-       } else {
-               ip = (struct ip_header const *)(data + size_ethernet);
+               now.tv_sec += conf->stats.interval;
+               now.tv_usec = 0;
+
+               if (!fr_event_insert(this->list, rs_stats_process, ctx, &now, &event)) {
+                       ERROR("Failed inserting stats interval event");
+               }
        }
+}
 
-       udp = (struct udp_header const *)(((uint8_t const *) ip) + size_ip);
-       payload = (uint8_t const *)(((uint8_t const *) udp) + size_udp);
 
-       packet = rad_alloc(NULL, 0);
-       if (!packet) {
-               fprintf(stderr, "Out of memory\n");
-               return;
+/** Update latency statistics for request/response and forwarded packets
+ *
+ */
+static void rs_stats_update_latency(rs_latency_t *stats, struct timeval *latency)
+{
+       double lint;
+
+       stats->interval.linked_total++;
+       /* More useful is this in milliseconds */
+       lint = (latency->tv_sec + (latency->tv_usec / 1000000.0)) * 1000;
+       if (lint > stats->interval.latency_high) {
+               stats->interval.latency_high = lint;
        }
+       if (!stats->interval.latency_low || (lint < stats->interval.latency_low)) {
+               stats->interval.latency_low = lint;
+       }
+       stats->interval.latency_total += lint;
 
-       packet->src_ipaddr.af = AF_INET;
-       packet->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr;
-       packet->src_port = ntohs(udp->udp_sport);
-       packet->dst_ipaddr.af = AF_INET;
-       packet->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr;
-       packet->dst_port = ntohs(udp->udp_dport);
+}
 
-       memcpy(&packet->data, &payload, sizeof(packet->data));
-       packet->data_len = header->len - (payload - data);
+/** Copy a subset of attributes from one list into the other
+ *
+ * Should be O(n) if all the attributes exist.  List must be pre-sorted.
+ */
+static int rs_get_pairs(TALLOC_CTX *ctx, VALUE_PAIR **out, VALUE_PAIR *vps, DICT_ATTR const *da[], int num)
+{
+       vp_cursor_t list_cursor, out_cursor;
+       VALUE_PAIR *match, *last_match, *copy;
+       uint64_t count = 0;
+       int i;
+
+       last_match = vps;
+
+       fr_cursor_init(&list_cursor, &last_match);
+       fr_cursor_init(&out_cursor, out);
+       for (i = 0; i < num; i++) {
+               match = fr_cursor_next_by_da(&list_cursor, da[i], TAG_ANY);
+               if (!match) {
+                       fr_cursor_init(&list_cursor, &last_match);
+                       continue;
+               }
 
-       if (!rad_packet_ok(packet, 0)) {
-               DEBUG(log_dst, "Packet: %s\n", fr_strerror());
+               do {
+                       copy = paircopyvp(ctx, match);
+                       if (!copy) {
+                               pairfree(out);
+                               return -1;
+                       }
+                       fr_cursor_insert(&out_cursor, copy);
+                       last_match = match;
 
-               DEBUG(log_dst, "  From     %s:%d\n", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
-               DEBUG(log_dst, "  To:      %s:%d\n", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
-               DEBUG(log_dst, "  Type:    %s\n", fr_packet_codes[packet->code]);
+                       count++;
+               } while ((match = fr_cursor_next_by_da(&list_cursor, da[i], TAG_ANY)));
+       }
 
-               rad_free(&packet);
-               return;
+       return count;
+}
+
+static int _request_free(rs_request_t *request)
+{
+       /*
+        *      If were attempting to cleanup the request, and it's no longer in the request_tree
+        *      something has gone very badly wrong.
+        */
+       if (request->in_request_tree) {
+               assert(rbtree_deletebydata(request_tree, request));
        }
 
-       switch (packet->code) {
-       case PW_CODE_COA_REQUEST:
-               /* we need a 16 x 0 byte vector for decrypting encrypted VSAs */
-               original = nullpacket;
-               break;
-       case PW_CODE_AUTHENTICATION_ACK:
-               /* look for a matching request and use it for decoding */
-               original = rbtree_finddata(request_tree, packet);
-               break;
-       case PW_CODE_AUTHENTICATION_REQUEST:
-               /* save the request for later matching */
-               original = rad_alloc_reply(NULL, packet);
-               if (original) { /* just ignore allocation failures */
-                       rbtree_deletebydata(request_tree, original);
-                       rbtree_insert(request_tree, original);
-               }
-               /* fallthrough */
-       default:
-               /* don't attempt to decode any encrypted attributes */
-               original = NULL;
+       if (request->in_link_tree) {
+               assert(rbtree_deletebydata(link_tree, request));
+       }
+
+       if (request->event) {
+               assert(fr_event_delete(events, &request->event));
+       }
+
+       rad_free(&request->packet);
+       rad_free(&request->expect);
+       rad_free(&request->linked);
+
+       return 0;
+}
+
+static void rs_packet_cleanup(rs_request_t *request)
+{
+
+       RADIUS_PACKET *packet = request->packet;
+       uint64_t count = request->id;
+
+       assert(request->stats_req);
+       assert(!request->rt_rsp || request->stats_rsp);
+       assert(packet);
+
+       /*
+        *      Don't pollute stats or print spurious messages as radsniff closes.
+        */
+       if (cleanup) {
+               talloc_free(request);
+               return;
+       }
+
+       if (RIDEBUG_ENABLED()) {
+               rs_time_print(timestr, sizeof(timestr), &request->when);
+       }
+
+       /*
+        *      Were at packet cleanup time which is when the packet was received + timeout
+        *      and it's not been linked with a forwarded packet or a response.
+        *
+        *      We now count it as lost.
+        */
+       if (!request->silent_cleanup) {
+               if (!request->linked) {
+                       request->stats_req->interval.lost_total++;
+
+                       if (conf->event_flags & RS_LOST) {
+                               /* @fixme We should use flags in the request to indicate whether it's been dumped
+                                * to a PCAP file or logged yet, this simplifies the body logging logic */
+                               conf->logger(request->id, RS_LOST, request->in, packet, NULL, NULL, false,
+                                            conf->filter_response_vps || !(conf->event_flags & RS_NORMAL));
+                       }
+               }
+
+               if (request->in->type == PCAP_INTERFACE_IN) {
+                       RDEBUG("Cleaning up request packet ID %i", request->expect->id);
+               }
+       }
+
+       /*
+        *      Now the request is done, we can update the retransmission stats
+        */
+       if (request->rt_req > RS_RETRANSMIT_MAX) {
+               request->stats_req->interval.rt_total[RS_RETRANSMIT_MAX]++;
+       } else {
+               request->stats_req->interval.rt_total[request->rt_req]++;
+       }
+
+       if (request->rt_rsp) {
+               if (request->rt_rsp > RS_RETRANSMIT_MAX) {
+                       request->stats_rsp->interval.rt_total[RS_RETRANSMIT_MAX]++;
+               } else {
+                       request->stats_rsp->interval.rt_total[request->rt_rsp]++;
+               }
+       }
+
+       talloc_free(request);
+}
+
+static void _rs_event(void *ctx)
+{
+       rs_request_t *request = talloc_get_type_abort(ctx, rs_request_t);
+       request->event = NULL;
+       rs_packet_cleanup(request);
+}
+
+/** Wrapper around fr_packet_cmp to strip off the outer request struct
+ *
+ */
+static int rs_packet_cmp(rs_request_t const *a, rs_request_t const *b)
+{
+       return fr_packet_cmp(a->expect, b->expect);
+}
+
+/* This is the same as immediately scheduling the cleanup event */
+#define RS_CLEANUP_NOW(_x, _s)\
+       {\
+               _x->silent_cleanup = _s;\
+               _x->when = header->ts;\
+               rs_packet_cleanup(_x);\
+               _x = NULL;\
+       } while (0)
+
+static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkthdr const *header, uint8_t const *data)
+{
+       rs_stats_t              *stats = event->stats;
+       struct timeval          elapsed = {0, 0};
+       struct timeval          latency;
+
+       /*
+        *      Pointers into the packet data we just received
+        */
+       ssize_t len;
+       uint8_t const           *p = data;
+
+       ip_header_t const       *ip = NULL;             /* The IP header */
+       ip_header6_t const      *ip6 = NULL;            /* The IPv6 header */
+       udp_header_t const      *udp;                   /* The UDP header */
+       uint8_t                 version;                /* IP header version */
+       bool                    response;               /* Was it a response code */
+
+       decode_fail_t           reason;                 /* Why we failed decoding the packet */
+       static uint64_t         captured = 0;
+
+       rs_status_t             status = RS_NORMAL;     /* Any special conditions (RTX, Unlinked, ID-Reused) */
+       RADIUS_PACKET           *current;               /* Current packet were processing */
+       rs_request_t            *original = NULL;
+
+       rs_request_t            search;
+
+       memset(&search, 0, sizeof(search));
+
+       if (!start_pcap.tv_sec) {
+               start_pcap = header->ts;
+       }
+
+       if (RIDEBUG_ENABLED()) {
+               rs_time_print(timestr, sizeof(timestr), &header->ts);
+       }
+
+       len = fr_pcap_link_layer_offset(data, header->caplen, event->in->link_type);
+       if (len < 0) {
+               REDEBUG("Failed determining link layer header offset");
+               return;
+       }
+       p += len;
+
+       version = (p[0] & 0xf0) >> 4;
+       switch (version) {
+       case 4:
+               ip = (ip_header_t const *)p;
+               len = (0x0f & ip->ip_vhl) * 4;  /* ip_hl specifies length in 32bit words */
+               p += len;
+               break;
+
+       case 6:
+               ip6 = (ip_header6_t const *)p;
+               p += sizeof(ip_header6_t);
+
+               break;
+
+       default:
+               REDEBUG("IP version invalid %i", version);
+               return;
+       }
+
+       /*
+        *      End of variable length bits, do basic check now to see if packet looks long enough
+        */
+       len = (p - data) + sizeof(udp_header_t) + (sizeof(radius_packet_t) - 1);        /* length value */
+       if ((size_t) len > header->caplen) {
+               REDEBUG("Packet too small, we require at least %zu bytes, captured %i bytes",
+                       (size_t) len, header->caplen);
+               return;
+       }
+
+       /*
+        *      UDP header validation.
+        */
+       udp = (udp_header_t const *)p;
+       {
+               uint16_t udp_len;
+               ssize_t diff;
+
+               udp_len = ntohs(udp->len);
+               diff = udp_len - (header->caplen - (p - data));
+               /* Truncated data */
+               if (diff > 0) {
+                       REDEBUG("Packet too small by %zi bytes, UDP header + Payload should be %hu bytes",
+                               diff, udp_len);
+                       return;
+               }
+               /* Trailing data */
+               else if (diff < 0) {
+                       REDEBUG("Packet too big by %zi bytes, UDP header + Payload should be %hu bytes",
+                               diff * -1, udp_len);
+                       return;
+               }
+       }
+       if (version == 4) {
+               uint16_t expected;
+
+               expected = fr_udp_checksum((uint8_t const *) udp, ntohs(udp->len), udp->checksum,
+                                          ip->ip_src, ip->ip_dst);
+               if (udp->checksum != expected) {
+                       REDEBUG("UDP checksum invalid, packet: 0x%04hx calculated: 0x%04hx",
+                               ntohs(udp->checksum), ntohs(expected));
+                       /* Not a fatal error */
+               }
+       }
+       p += sizeof(udp_header_t);
+
+       /*
+        *      With artificial talloc memory limits there's a good chance we can
+        *      recover once some requests timeout, so make an effort to deal
+        *      with allocation failures gracefully.
+        */
+       current = rad_alloc(conf, 0);
+       if (!current) {
+               REDEBUG("Failed allocating memory to hold decoded packet");
+               rs_tv_add_ms(&header->ts, conf->stats.timeout, &stats->quiet);
+               return;
+       }
+
+       current->timestamp = header->ts;
+       current->data_len = header->caplen - (p - data);
+       memcpy(&current->data, &p, sizeof(current->data));
+
+       /*
+        *      Populate IP/UDP fields from PCAP data
+        */
+       if (ip) {
+               current->src_ipaddr.af = AF_INET;
+               current->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr;
+
+               current->dst_ipaddr.af = AF_INET;
+               current->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr;
+       } else {
+               current->src_ipaddr.af = AF_INET6;
+               memcpy(&current->src_ipaddr.ipaddr.ip6addr.s6_addr, &ip6->ip_src.s6_addr,
+                      sizeof(current->src_ipaddr.ipaddr.ip6addr.s6_addr));
+
+               current->dst_ipaddr.af = AF_INET6;
+               memcpy(&current->dst_ipaddr.ipaddr.ip6addr.s6_addr, &ip6->ip_dst.s6_addr,
+                      sizeof(current->dst_ipaddr.ipaddr.ip6addr.s6_addr));
+       }
+
+       current->src_port = ntohs(udp->src);
+       current->dst_port = ntohs(udp->dst);
+
+       if (!rad_packet_ok(current, 0, &reason)) {
+               REDEBUG("%s", fr_strerror());
+               if (conf->event_flags & RS_ERROR) {
+                       conf->logger(count, RS_ERROR, event->in, current, &elapsed, NULL, false, false);
+               }
+               rad_free(&current);
+
+               return;
+       }
+
+       switch (current->code) {
+       case PW_CODE_ACCOUNTING_RESPONSE:
+       case PW_CODE_AUTHENTICATION_REJECT:
+       case PW_CODE_AUTHENTICATION_ACK:
+       case PW_CODE_COA_NAK:
+       case PW_CODE_COA_ACK:
+       case PW_CODE_DISCONNECT_NAK:
+       case PW_CODE_DISCONNECT_ACK:
+       case PW_CODE_STATUS_CLIENT:
+       {
+               /* look for a matching request and use it for decoding */
+               search.expect = current;
+               original = rbtree_finddata(request_tree, &search);
+
+               /*
+                *      Verify this code is allowed
+                */
+               if (conf->filter_response_code && (conf->filter_response_code != current->code)) {
+                       drop_response:
+                       RDEBUG2("Response dropped by filter");
+                       rad_free(&current);
+
+                       /* We now need to cleanup the original request too */
+                       if (original) {
+                               RS_CLEANUP_NOW(original, true);
+                       }
+                       return;
+               }
+
+               /*
+                *      Only decode attributes if we want to print them or filter on them
+                *      rad_packet_ok does checks to verify the packet is actually valid.
+                */
+               if (conf->decode_attrs) {
+                       int ret;
+                       FILE *log_fp = fr_log_fp;
+
+                       fr_log_fp = NULL;
+                       ret = rad_decode(current, original ? original->expect : NULL, conf->radius_secret);
+                       fr_log_fp = log_fp;
+                       if (ret != 0) {
+                               rad_free(&current);
+                               REDEBUG("Failed decoding");
+                               return;
+                       }
+               }
+
+               /*
+                *      Check if we've managed to link it to a request
+                */
+               if (original) {
+                       /*
+                        *      Now verify the packet passes the attribute filter
+                        */
+                       if (conf->filter_response_vps) {
+                               pairsort(&current->vps, attrtagcmp);
+                               if (!pairvalidate_relaxed(conf->filter_response_vps, current->vps)) {
+                                       goto drop_response;
+                               }
+                       }
+
+                       /*
+                        *      Is this a retransmission?
+                        */
+                       if (original->linked) {
+                               status = RS_RTX;
+                               original->rt_rsp++;
+
+                               rad_free(&original->linked);
+                               fr_event_delete(event->list, &original->event);
+                       /*
+                        *      ...nope it's the first response to a request.
+                        */
+                       } else {
+                               original->stats_rsp = &stats->exchange[current->code];
+                       }
+
+                       /*
+                        *      Insert a callback to remove the request and response
+                        *      from the tree after the timeout period.
+                        *      The delay is so we can detect retransmissions.
+                        */
+                       original->linked = talloc_steal(original, current);
+                       rs_tv_add_ms(&header->ts, conf->stats.timeout, &original->when);
+                       if (!fr_event_insert(event->list, _rs_event, original, &original->when,
+                                            &original->event)) {
+                               REDEBUG("Failed inserting new event");
+                               /*
+                                *      Delete the original request/event, it's no longer valid
+                                *      for statistics.
+                                */
+                               talloc_free(original);
+                               return;
+                       }
+               /*
+                *      No request seen, or request was dropped by attribute filter
+                */
+               } else {
+                       /*
+                        *      If conf->filter_request_vps are set assume the original request was dropped,
+                        *      the alternative is maintaining another 'filter', but that adds
+                        *      complexity, reduces max capture rate, and is generally a PITA.
+                        */
+                       if (conf->filter_request) {
+                               rad_free(&current);
+                               RDEBUG2("Original request dropped by filter");
+                               return;
+                       }
+
+                       status = RS_UNLINKED;
+                       stats->exchange[current->code].interval.unlinked_total++;
+               }
+
+               response = true;
+               break;
+       }
+
+       case PW_CODE_ACCOUNTING_REQUEST:
+       case PW_CODE_AUTHENTICATION_REQUEST:
+       case PW_CODE_COA_REQUEST:
+       case PW_CODE_DISCONNECT_REQUEST:
+       case PW_CODE_STATUS_SERVER:
+       {
+               /*
+                *      Verify this code is allowed
+                */
+               if (conf->filter_request_code && (conf->filter_request_code != current->code)) {
+                       drop_request:
+
+                       RDEBUG2("Request dropped by filter");
+                       rad_free(&current);
+
+                       return;
+               }
+
+               /*
+                *      Only decode attributes if we want to print them or filter on them
+                *      rad_packet_ok does checks to verify the packet is actually valid.
+                */
+               if (conf->decode_attrs) {
+                       int ret;
+                       FILE *log_fp = fr_log_fp;
+
+                       fr_log_fp = NULL;
+                       ret = rad_decode(current, NULL, conf->radius_secret);
+                       fr_log_fp = log_fp;
+
+                       if (ret != 0) {
+                               rad_free(&current);
+                               REDEBUG("Failed decoding");
+                               return;
+                       }
+
+                       pairsort(&current->vps, attrtagcmp);
+               }
+
+               /*
+                *      Save the request for later matching
+                */
+               search.expect = rad_alloc_reply(current, current);
+               if (!search.expect) {
+                       REDEBUG("Failed allocating memory to hold expected reply");
+                       rs_tv_add_ms(&header->ts, conf->stats.timeout, &stats->quiet);
+                       rad_free(&current);
+                       return;
+               }
+               search.expect->code = current->code;
+
+               if ((conf->link_da_num > 0) && current->vps) {
+                       int ret;
+                       ret = rs_get_pairs(current, &search.link_vps, current->vps, conf->link_da,
+                                          conf->link_da_num);
+                       if (ret < 0) {
+                               ERROR("Failed extracting RTX linking pairs from request");
+                               rad_free(&current);
+                               return;
+                       }
+               }
+
+               /*
+                *      If we have linking attributes set, attempt to find a request in the linking tree.
+                */
+               if (search.link_vps) {
+                       rs_request_t *tuple;
+
+                       original = rbtree_finddata(link_tree, &search);
+                       tuple = rbtree_finddata(request_tree, &search);
+
+                       /*
+                        *      If the packet we matched using attributes is not the same
+                        *      as the packet in the request tree, then we need to clean up
+                        *      the packet in the request tree.
+                        */
+                       if (tuple && (original != tuple)) {
+                               RS_CLEANUP_NOW(tuple, true);
+                       }
+               /*
+                *      Detect duplicates using the normal 5-tuple of src/dst ips/ports id
+                */
+               } else {
+                       original = rbtree_finddata(request_tree, &search);
+                       if (original && memcmp(original->expect->vector, current->vector,
+                           sizeof(original->expect->vector)) != 0) {
+                               /*
+                                *      ID reused before the request timed out (which may be an issue)...
+                                */
+                               if (!original->linked) {
+                                       status = RS_REUSED;
+                                       stats->exchange[current->code].interval.reused_total++;
+                                       /* Occurs regularly downstream of proxy servers (so don't complain) */
+                                       RS_CLEANUP_NOW(original, true);
+                               /*
+                                *      ...and before we saw a response (which may be a bigger issue).
+                                */
+                               } else {
+                                       RS_CLEANUP_NOW(original, false);
+                               }
+                               /* else it's a proper RTX with the same src/dst id authenticator/nonce */
+                       }
+               }
+
+               /*
+                *      Now verify the packet passes the attribute filter
+                */
+               if (conf->filter_request_vps) {
+                       if (!pairvalidate_relaxed(conf->filter_request_vps, current->vps)) {
+                               goto drop_request;
+                       }
+               }
+
+               /*
+                *      Is this a retransmission?
+                */
+               if (original) {
+                       status = RS_RTX;
+                       original->rt_req++;
+
+                       rad_free(&original->packet);
+
+                       /* We may of seen the response, but it may of been lost upstream */
+                       rad_free(&original->linked);
+
+                       original->packet = talloc_steal(original, current);
+
+                       /* Request may need to be reinserted as the 5 tuple of the response may of changed */
+                       if (rs_packet_cmp(original, &search) != 0) {
+                               rbtree_deletebydata(request_tree, original);
+                       }
+
+                       rad_free(&original->expect);
+                       original->expect = talloc_steal(original, search.expect);
+
+                       /* Disarm the timer for the cleanup event for the original request */
+                       fr_event_delete(event->list, &original->event);
+               /*
+                *      ...nope it's a new request.
+                */
+               } else {
+                       original = talloc_zero(conf, rs_request_t);
+                       talloc_set_destructor(original, _request_free);
+
+                       original->id = count;
+                       original->in = event->in;
+                       original->stats_req = &stats->exchange[current->code];
+
+                       original->packet = talloc_steal(original, current);
+                       original->expect = talloc_steal(original, search.expect);
+
+                       if (search.link_vps) {
+                               original->link_vps = pairsteal(original, search.link_vps);
+
+                               /* We should never have conflicts */
+                               assert(rbtree_insert(link_tree, original));
+                               original->in_link_tree = true;
+                       }
+
+                       /*
+                        *      Special case for when were filtering by response,
+                        *      we never count any requests as lost, because we
+                        *      don't know what the response to that request would
+                        *      of been.
+                        */
+                       if (conf->filter_response_vps) {
+                               original->silent_cleanup = true;
+                       }
+               }
+
+               if (!original->in_request_tree) {
+                       /* We should never have conflicts */
+                       assert(rbtree_insert(request_tree, original));
+                       original->in_request_tree = true;
+               }
+
+               /*
+                *      Insert a callback to remove the request from the tree
+                */
+               original->packet->timestamp = header->ts;
+               rs_tv_add_ms(&header->ts, conf->stats.timeout, &original->when);
+               if (!fr_event_insert(event->list, _rs_event, original,
+                                    &original->when, &original->event)) {
+                       REDEBUG("Failed inserting new event");
+
+                       talloc_free(original);
+                       return;
+               }
+               response = false;
+               break;
+       }
+
+       default:
+               REDEBUG("Unsupported code %i", current->code);
+               rad_free(&current);
+
+               return;
+       }
+
+       if (event->out) {
+               pcap_dump((void *) (event->out->dumper), header, data);
+       }
+
+       rs_tv_sub(&header->ts, &start_pcap, &elapsed);
+
+       /*
+        *      Increase received count
+        */
+       stats->exchange[current->code].interval.received_total++;
+
+       /*
+        *      It's a linked response
+        */
+       if (original && original->linked) {
+               rs_tv_sub(&current->timestamp, &original->packet->timestamp, &latency);
+
+               /*
+                *      Update stats for both the request and response types.
+                *
+                *      This isn't useful for things like Access-Requests, but will be useful for
+                *      CoA and Disconnect Messages, as we get the average latency across both
+                *      response types.
+                *
+                *      It also justifies allocating PW_CODE_MAX instances of rs_latency_t.
+                */
+               rs_stats_update_latency(&stats->exchange[current->code], &latency);
+               rs_stats_update_latency(&stats->exchange[original->expect->code], &latency);
+
+               /*
+                *      Were filtering on response, now print out the full data from the request
+                */
+               if (conf->filter_response && RIDEBUG_ENABLED() && (conf->event_flags & RS_NORMAL)) {
+                       rs_time_print(timestr, sizeof(timestr), &original->packet->timestamp);
+                       rs_tv_sub(&original->packet->timestamp, &start_pcap, &elapsed);
+                       conf->logger(original->id, RS_NORMAL, original->in, original->packet, &elapsed, NULL, false, true);
+                       rs_tv_sub(&header->ts, &start_pcap, &elapsed);
+                       rs_time_print(timestr, sizeof(timestr), &header->ts);
+               }
+
+               if (conf->event_flags & status) {
+                       conf->logger(count, status, event->in, current, &elapsed, &latency, response, true);
+               }
+       /*
+        *      It's the original request
+        *
+        *      If were filtering on responses we can only indicate we received it on response, or timeout.
+        */
+       } else if (!conf->filter_response && (conf->event_flags & status)) {
+               conf->logger(original ? original->id : count, status, event->in,
+                            current, &elapsed, NULL, response, true);
+       }
+
+       fflush(fr_log_fp);
+
+       /*
+        *      If it's a unlinked response, we need to free it explicitly, as it will
+        *      not be done by the event queue.
+        */
+       if (response && !original) {
+               rad_free(&current);
+       }
+
+       captured++;
+       /*
+        *      We've hit our capture limit, break out of the event loop
+        */
+       if ((conf->limit > 0) && (captured >= conf->limit)) {
+               INFO("Captured %" PRIu64 " packets, exiting...", captured);
+               fr_event_loop_exit(events, 1);
+       }
+}
+
+static void rs_got_packet(UNUSED fr_event_list_t *el, int fd, void *ctx)
+{
+       static uint64_t count = 0;      /* Packets seen */
+       rs_event_t      *event = ctx;
+       pcap_t          *handle = event->in->handle;
+
+       int i;
+       int ret;
+       const uint8_t *data;
+       struct pcap_pkthdr *header;
+
+       /*
+        *      Consume entire capture, interleaving not currently possible
+        */
+       if ((event->in->type == PCAP_FILE_IN) || (event->in->type == PCAP_STDIO_IN)) {
+               while (!fr_event_loop_exiting(el)) {
+                       struct timeval now;
+
+                       ret = pcap_next_ex(handle, &header, &data);
+                       if (ret == 0) {
+                               /* No more packets available at this time */
+                               return;
+                       }
+                       if (ret == -2) {
+                               DEBUG("Done reading packets (%s)", event->in->name);
+                               fr_event_fd_delete(events, 0, fd);
+
+                               if (fr_event_list_num_fds(events) == 0) {
+                                       fr_event_loop_exit(events, 1);
+                               }
+
+                               return;
+                       }
+                       if (ret < 0) {
+                               ERROR("Error requesting next packet, got (%i): %s", ret, pcap_geterr(handle));
+                               return;
+                       }
+
+                       do {
+                               now = header->ts;
+                       } while (fr_event_run(el, &now) == 1);
+                       count++;
+
+                       rs_packet_process(count, event, header, data);
+               }
+               return;
+       }
+
+       /*
+        *      Consume multiple packets from the capture buffer.
+        *      We occasionally need to yield to allow events to run.
+        */
+       for (i = 0; i < RS_FORCE_YIELD; i++) {
+               ret = pcap_next_ex(handle, &header, &data);
+               if (ret == 0) {
+                       /* No more packets available at this time */
+                       return;
+               }
+               if (ret < 0) {
+                       ERROR("Error requesting next packet, got (%i): %s", ret, pcap_geterr(handle));
+                       return;
+               }
+
+               count++;
+               rs_packet_process(count, event, header, data);
+       }
+}
+
+static void _rs_event_status(struct timeval *wake)
+{
+       if (wake && ((wake->tv_sec != 0) || (wake->tv_usec >= 100000))) {
+               DEBUG2("Waking up in %d.%01u seconds.", (int) wake->tv_sec, (unsigned int) wake->tv_usec / 100000);
+
+               if (RIDEBUG_ENABLED()) {
+                       rs_time_print(timestr, sizeof(timestr), wake);
+               }
+       }
+}
+
+/** Compare requests using packet info and lists of attributes
+ *
+ */
+static int rs_rtx_cmp(rs_request_t const *a, rs_request_t const *b)
+{
+       int rcode;
+
+       assert(a->link_vps);
+       assert(b->link_vps);
+
+       rcode = (int) a->expect->code - (int) b->expect->code;
+       if (rcode != 0) return rcode;
+
+       rcode = a->expect->sockfd - b->expect->sockfd;
+       if (rcode != 0) return rcode;
+
+       rcode = fr_ipaddr_cmp(&a->expect->src_ipaddr, &b->expect->src_ipaddr);
+       if (rcode != 0) return rcode;
+
+       rcode = fr_ipaddr_cmp(&a->expect->dst_ipaddr, &b->expect->dst_ipaddr);
+       if (rcode != 0) return rcode;
+
+       return pairlistcmp(a->link_vps, b->link_vps);
+}
+
+static int rs_build_dict_list(DICT_ATTR const **out, size_t len, char *list)
+{
+       size_t i = 0;
+       char *p, *tok;
+
+       p = list;
+       while ((tok = strsep(&p, "\t ,")) != NULL) {
+               DICT_ATTR const *da;
+               if ((*tok == '\t') || (*tok == ' ') || (*tok == '\0')) {
+                       continue;
+               }
+
+               if (i == len) {
+                       ERROR("Too many attributes, maximum allowed is %zu", len);
+                       return -1;
+               }
+
+               da = dict_attrbyname(tok);
+               if (!da) {
+                       ERROR("Error parsing attribute name \"%s\"", tok);
+                       return -1;
+               }
+
+               out[i] = da;
+               i++;
+       }
+
+       return i;
+}
+
+static int rs_build_filter(VALUE_PAIR **out, char const *filter)
+{
+       vp_cursor_t cursor;
+       VALUE_PAIR *vp;
+       FR_TOKEN code;
+
+       code = userparse(conf, filter, out);
+       if (code == T_OP_INVALID) {
+               ERROR("Invalid RADIUS filter \"%s\" (%s)", filter, fr_strerror());
+               return -1;
+       }
+
+       if (!*out) {
+               ERROR("Empty RADIUS filter '%s'", filter);
+               return -1;
        }
 
-       /*
-        *  Decode the data without bothering to check the signatures.
-        */
-       if (rad_decode(packet, original, radius_secret) != 0) {
-               rad_free(&packet);
-               fr_perror("decode");
-               return;
+       for (vp = fr_cursor_init(&cursor, out);
+            vp;
+            vp = fr_cursor_next(&cursor)) {
+               /*
+                *      xlat expansion isn't support here
+                */
+               if (vp->type == VT_XLAT) {
+                       vp->type = VT_DATA;
+                       vp->vp_strvalue = vp->value.xlat;
+               }
        }
 
        /*
-        *  We've seen a successful reply to this, so delete it now
+        *      This allows efficient list comparisons later
         */
-       if (original)
-               rbtree_deletebydata(request_tree, original);
+       pairsort(out, attrtagcmp);
 
-       if (filter_vps && filter_packet(packet)) {
-               rad_free(&packet);
-               DEBUG(log_dst, "Packet number %d doesn't match\n", count++);
-               return;
-       }
+       return 0;
+}
 
-       if (out) {
-               pcap_dump((void *) out, header, data);
-               goto check_filter;
-       }
+static int rs_build_flags(int *flags, FR_NAME_NUMBER const *map, char *list)
+{
+       size_t i = 0;
+       char *p, *tok;
 
-       INFO(log_dst, "%s Id %d\t", fr_packet_codes[packet->code], packet->id);
+       p = list;
+       while ((tok = strsep(&p, "\t ,")) != NULL) {
+               int flag;
 
-       /*
-        *  Print the RADIUS packet
-        */
-       INFO(log_dst, "%s:%d -> ", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
-       INFO(log_dst, "%s:%d", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
+               if ((*tok == '\t') || (*tok == ' ') || (*tok == '\0')) {
+                       continue;
+               }
 
-       DEBUG1(log_dst, "\t(%d packets)", count++);
+               *flags |= flag = fr_str2int(map, tok, -1);
+               if (flag < 0) {
+                       ERROR("Invalid flag \"%s\"", tok);
+                       return -1;
+               }
 
-       if (!start_pcap.tv_sec) {
-               start_pcap = header->ts;
+               i++;
        }
 
-       tv_sub(&header->ts, &start_pcap, &elapsed);
+       return i;
+}
 
-       INFO(log_dst, "\t+%u.%03u", (unsigned int) elapsed.tv_sec,
-              (unsigned int) elapsed.tv_usec / 1000);
+/** Callback for when the request is removed from the request tree
+ *
+ * @param request being removed.
+ */
+static void _unmark_request(void *request)
+{
+       rs_request_t *this = request;
+       this->in_request_tree = false;
+}
 
-       if (fr_debug_flag > 1) {
-               DEBUG(log_dst, "\n");
-               if (packet->vps) {
-                       if (do_sort) {
-                               pairsort(&packet->vps, attrtagcmp);
-                       }
-                       vp_printlist(log_dst, packet->vps);
-                       pairfree(&packet->vps);
-               }
+/** Callback for when the request is removed from the link tree
+ *
+ * @param request being removed.
+ */
+static void _unmark_link(void *request)
+{
+       rs_request_t *this = request;
+       this->in_link_tree = false;
+}
+
+/** Write the last signal to the signal pipe
+ *
+ * @param sig raised
+ */
+static void rs_signal_self(int sig)
+{
+       if (write(self_pipe[1], &sig, sizeof(sig)) < 0) {
+               ERROR("Failed writing signal %s to pipe: %s", strsignal(sig), fr_syserror(errno));
+               exit(EXIT_FAILURE);
        }
+}
 
-       INFO(log_dst, "\n");
+#ifdef HAVE_COLLECTDC_H
+/** Re-open the collectd socket
+ *
+ */
+static void rs_collectd_reopen(void *ctx)
+{
+       fr_event_list_t *list = ctx;
+       static fr_event_t *event;
+       struct timeval now, when;
 
-       if (!to_stdout && (fr_debug_flag > 4)) {
-               rad_print_hex(packet);
+       if (rs_stats_collectd_open(conf) == 0) {
+               DEBUG2("Stats output socket (re)opened");
+               return;
        }
 
-       fflush(log_dst);
+       ERROR("Will attempt to re-establish connection in %i ms", RS_SOCKET_REOPEN_DELAY);
 
- check_filter:
-       /*
-        *  If we're doing filtering, Access-Requests are cached in the
-        *  filter tree.
-        */
-       if (!filter_vps ||
-           ((packet->code != PW_CODE_AUTHENTICATION_REQUEST) &&
-            (packet->code != PW_CODE_ACCOUNTING_REQUEST))) {
-               rad_free(&packet);
+       gettimeofday(&now, NULL);
+       rs_tv_add_ms(&now, RS_SOCKET_REOPEN_DELAY, &when);
+       if (!fr_event_insert(list, rs_collectd_reopen, list, &when, &event)) {
+               ERROR("Failed inserting re-open event");
+               assert(0);
        }
 }
+#endif
 
-
-/** Wrapper function to allow rad_free to be called as an rbtree destructor callback
+/** Read the last signal from the signal pipe
  *
- * @param packet to free.
  */
-static void _rb_rad_free(void *packet)
+static void rs_signal_action(UNUSED fr_event_list_t *list, int fd, UNUSED void *ctx)
 {
-       rad_free((RADIUS_PACKET **) &packet);
+       int sig;
+       ssize_t ret;
+
+       ret = read(fd, &sig, sizeof(sig));
+       if (ret < 0) {
+               ERROR("Failed reading signal from pipe: %s", fr_syserror(errno));
+               exit(EXIT_FAILURE);
+       }
+
+       if (ret != sizeof(sig)) {
+               ERROR("Failed reading signal from pipe: "
+                     "Expected signal to be %zu bytes but only read %zu byes", sizeof(sig), ret);
+               exit(EXIT_FAILURE);
+       }
+
+       switch (sig) {
+#ifdef HAVE_COLLECTDC_H
+       case SIGPIPE:
+               rs_collectd_reopen(list);
+               break;
+#endif
+
+       case SIGINT:
+       case SIGTERM:
+       case SIGQUIT:
+               DEBUG2("Signalling event loop to exit");
+               fr_event_loop_exit(events, 1);
+               break;
+
+       default:
+               ERROR("Unhandled signal %s", strsignal(sig));
+               exit(EXIT_FAILURE);
+       }
 }
 
+
 static void NEVER_RETURNS usage(int status)
 {
        FILE *output = status ? stderr : stdout;
-       fprintf(output, "Usage: radsniff [options]\n");
+       fprintf(output, "Usage: radsniff [options][stats options] -- [pcap files]\n");
        fprintf(output, "options:\n");
-       fprintf(output, "  -c <count>      Number of packets to capture.\n");
-       fprintf(output, "  -d <directory>  Set dictionary directory.\n");
-       fprintf(output, "  -F              Filter PCAP file from stdin to stdout.\n");
-       fprintf(output, "  -f <filter>     PCAP filter (default is 'udp port <port> or <port + 1> or 3799')\n");
-       fprintf(output, "  -h              This help message.\n");
-       fprintf(output, "  -i <interface>  Capture packets from interface (defaults to any if supported).\n");
-       fprintf(output, "  -I <file>       Read packets from file (overrides input of -F).\n");
-       fprintf(output, "  -p <port>       Filter packets by port (default is 1812).\n");
-       fprintf(output, "  -q              Print less debugging information.\n");
-       fprintf(output, "  -r <filter>     RADIUS attribute filter.\n");
-       fprintf(output, "  -s <secret>     RADIUS secret.\n");
-       fprintf(output, "  -S              Sort attributes in the packet (useful for diffing responses).\n");
-       fprintf(output, "  -v              Show program version information.\n");
-       fprintf(output, "  -w <file>       Write output packets to file (overrides output of -F).\n");
-       fprintf(output, "  -x              Print more debugging information (defaults to -xx).\n");
+       fprintf(output, "  -a                    List all interfaces available for capture.\n");
+       fprintf(output, "  -c <count>            Number of packets to capture.\n");
+       fprintf(output, "  -d <directory>        Set dictionary directory.\n");
+       fprintf(stderr, "  -d <raddb>            Set configuration directory (defaults to " RADDBDIR ").\n");
+       fprintf(stderr, "  -D <dictdir>          Set main dictionary directory (defaults to " DICTDIR ").\n");
+       fprintf(output, "  -e <event>[,<event>]  Only log requests with these event flags.\n");
+       fprintf(output, "                        Event may be one of the following:\n");
+       fprintf(output, "                        - received - a request or response.\n");
+       fprintf(output, "                        - norsp    - seen for a request.\n");
+       fprintf(output, "                        - rtx      - of a request that we've seen before.\n");
+       fprintf(output, "                        - noreq    - could be matched with the response.\n");
+       fprintf(output, "                        - reused   - ID too soon.\n");
+       fprintf(output, "                        - error    - decoding the packet.\n");
+       fprintf(output, "  -f <filter>           PCAP filter (default is 'udp port <port> or <port + 1> or 3799')\n");
+       fprintf(output, "  -h                    This help message.\n");
+       fprintf(output, "  -i <interface>        Capture packets from interface (defaults to all if supported).\n");
+       fprintf(output, "  -I <file>             Read packets from file (overrides input of -F).\n");
+       fprintf(output, "  -l <attr>[,<attr>]    Output packet sig and a list of attributes.\n");
+       fprintf(output, "  -L <attr>[,<attr>]    Detect retransmissions using these attributes to link requests.\n");
+       fprintf(output, "  -m                    Don't put interface(s) into promiscuous mode.\n");
+       fprintf(output, "  -p <port>             Filter packets by port (default is 1812).\n");
+       fprintf(output, "  -P <pidfile>          Daemonize and write out <pidfile>.\n");
+       fprintf(output, "  -q                    Print less debugging information.\n");
+       fprintf(output, "  -r <filter>           RADIUS attribute request filter.\n");
+       fprintf(output, "  -R <filter>           RADIUS attribute response filter.\n");
+       fprintf(output, "  -s <secret>           RADIUS secret.\n");
+       fprintf(output, "  -S                    Write PCAP data to stdout.\n");
+       fprintf(output, "  -v                    Show program version information.\n");
+       fprintf(output, "  -w <file>             Write output packets to file.\n");
+       fprintf(output, "  -x                    Print more debugging information (defaults to -xx).\n");
+       fprintf(output, "stats options:\n");
+       fprintf(output, "  -W <interval>         Periodically write out statistics every <interval> seconds.\n");
+       fprintf(output, "  -T <timeout>          How many milliseconds before the request is counted as lost "
+               "(defaults to %i).\n", RS_DEFAULT_TIMEOUT);
+#ifdef HAVE_COLLECTDC_H
+       fprintf(output, "  -N <prefix>           The instance name passed to the collectd plugin.\n");
+       fprintf(output, "  -O <server>           Write statistics to this collectd server.\n");
+#endif
        exit(status);
 }
 
 int main(int argc, char *argv[])
 {
-       char const *from_dev = NULL;                    /* Capture from device */
-       char const *from_file = NULL;                   /* Read from pcap file */
-       bool from_stdin = false;                        /* Read from stdin */
-
-       pcap_t *in = NULL;                              /* PCAP input handle */
+       fr_pcap_t *in = NULL, *in_p;
+       fr_pcap_t **in_head = &in;
+       fr_pcap_t *out = NULL;
 
-       int limit = -1;                                 /* How many packets to sniff */
+       int ret = 1;                                    /* Exit status */
 
        char errbuf[PCAP_ERRBUF_SIZE];                  /* Error buffer */
-
-       char *to_file = NULL;                           /* PCAP output file */
-
-       char *pcap_filter = NULL;                       /* PCAP filter string */
-       char *radius_filter = NULL;
        int port = 1812;
 
-       struct bpf_program fp;                          /* Holds compiled filter */
-       bpf_u_int32 ip_mask = PCAP_NETMASK_UNKNOWN;     /* Device Subnet mask */
-       bpf_u_int32 ip_addr = 0;                        /* Device IP */
-
        char buffer[1024];
 
        int opt;
-       FR_TOKEN parsecode;
-       char const *radius_dir = RADIUS_DIR;
+       char const *radius_dir = RADDBDIR;
        char const *dict_dir = DICTDIR;
 
-       fr_debug_flag = 2;
-       log_dst = stdout;
+       rs_stats_t stats;
+
+       fr_debug_flag = 1;
+       fr_log_fp = stdout;
 
        /*
         *      Useful if using radsniff as a long running stats daemon
@@ -407,68 +1820,209 @@ int main(int argc, char *argv[])
 
        talloc_set_log_stderr();
 
+       conf = talloc_zero(NULL, rs_t);
+       if (!fr_assert(conf)) {
+               exit (1);
+       }
+
+       /*
+        *  We don't really want probes taking down machines
+        */
+#ifdef HAVE_TALLOC_SET_MEMLIMIT
+       /*
+        *      @fixme causes hang in talloc steal
+        */
+        //talloc_set_memlimit(conf, 524288000);                /* 50 MB */
+#endif
+
+       /*
+        *      Set some defaults
+        */
+       conf->print_packet = true;
+       conf->limit = 0;
+       conf->promiscuous = true;
+#ifdef HAVE_COLLECTDC_H
+       conf->stats.prefix = RS_DEFAULT_PREFIX;
+#endif
+       conf->radius_secret = RS_DEFAULT_SECRET;
+       conf->logger = rs_packet_print_null;
+
+#ifdef HAVE_COLLECTDC_H
+       conf->stats.prefix = RS_DEFAULT_PREFIX;
+#endif
+
        /*
         *  Get options
         */
-       while ((opt = getopt(argc, argv, "c:d:D:Ff:hi:I:p:qr:s:Svw:xX")) != EOF) {
+       while ((opt = getopt(argc, argv, "ab:c:d:D:e:Ff:hi:I:l:L:mp:P:qr:R:s:Svw:xXW:T:P:N:O:")) != EOF) {
                switch (opt) {
+               case 'a':
+                       {
+                               pcap_if_t *all_devices = NULL;
+                               pcap_if_t *dev_p;
+
+                               if (pcap_findalldevs(&all_devices, errbuf) < 0) {
+                                       ERROR("Error getting available capture devices: %s", errbuf);
+                                       goto finish;
+                               }
+
+                               int i = 1;
+                               for (dev_p = all_devices;
+                                    dev_p;
+                                    dev_p = dev_p->next) {
+                                       INFO("%i.%s", i++, dev_p->name);
+                               }
+                               ret = 0;
+                               goto finish;
+                       }
+
+               /* super secret option */
+               case 'b':
+                       conf->buffer_pkts = atoi(optarg);
+                       if (conf->buffer_pkts == 0) {
+                               ERROR("Invalid buffer length \"%s\"", optarg);
+                               usage(1);
+                       }
+                       break;
+
                case 'c':
-                       limit = atoi(optarg);
-                       if (limit <= 0) {
-                               fprintf(stderr, "radsniff: Invalid number of packets \"%s\"\n", optarg);
-                               exit(1);
+                       conf->limit = atoi(optarg);
+                       if (conf->limit == 0) {
+                               ERROR("Invalid number of packets \"%s\"", optarg);
+                               usage(1);
                        }
                        break;
+
                case 'd':
                        radius_dir = optarg;
                        break;
+
                case 'D':
                        dict_dir = optarg;
                        break;
-               case 'F':
-                       from_stdin = true;
-                       to_stdout = true;
+
+               case 'e':
+                       if (rs_build_flags((int *) &conf->event_flags, rs_events, optarg) < 0) {
+                               usage(64);
+                       }
                        break;
+
                case 'f':
-                       pcap_filter = optarg;
+                       conf->pcap_filter = optarg;
                        break;
+
                case 'h':
                        usage(0);
                        break;
+
                case 'i':
-                       from_dev = optarg;
+                       *in_head = fr_pcap_init(conf, optarg, PCAP_INTERFACE_IN);
+                       if (!*in_head) {
+                               goto finish;
+                       }
+                       in_head = &(*in_head)->next;
+                       conf->from_dev = true;
                        break;
+
                case 'I':
-                       from_file = optarg;
+                       *in_head = fr_pcap_init(conf, optarg, PCAP_FILE_IN);
+                       if (!*in_head) {
+                               goto finish;
+                       }
+                       in_head = &(*in_head)->next;
+                       conf->from_file = true;
+                       break;
+
+               case 'l':
+                       conf->list_attributes = optarg;
+                       break;
+
+               case 'L':
+                       conf->link_attributes = optarg;
+                       break;
+
+               case 'm':
+                       conf->promiscuous = false;
                        break;
+
                case 'p':
                        port = atoi(optarg);
                        break;
+
+               case 'P':
+                       conf->daemonize = true;
+                       conf->pidfile = optarg;
+                       break;
+
                case 'q':
                        if (fr_debug_flag > 0) {
                                fr_debug_flag--;
                        }
                        break;
+
                case 'r':
-                       radius_filter = optarg;
+                       conf->filter_request = optarg;
                        break;
+
+               case 'R':
+                       conf->filter_response = optarg;
+                       break;
+
                case 's':
-                       radius_secret = optarg;
+                       conf->radius_secret = optarg;
                        break;
+
                case 'S':
-                       do_sort = true;
+                       conf->to_stdout = true;
                        break;
+
                case 'v':
-                       INFO(log_dst, "%s %s\n", radsniff_version, pcap_lib_version());
-                       exit(0);
+#ifdef HAVE_COLLECTDC_H
+                       INFO("%s, %s, collectdclient version %s", radsniff_version, pcap_lib_version(),
+                            lcc_version_string());
+#else
+                       INFO("%s %s", radsniff_version, pcap_lib_version());
+#endif
+                       exit(EXIT_SUCCESS);
                        break;
+
                case 'w':
-                       to_file = optarg;
+                       out = fr_pcap_init(conf, optarg, PCAP_FILE_OUT);
+                       conf->to_file = true;
                        break;
+
                case 'x':
                case 'X':
                        fr_debug_flag++;
                        break;
+
+               case 'W':
+                       conf->stats.interval = atoi(optarg);
+                       conf->print_packet = false;
+                       if (conf->stats.interval <= 0) {
+                               ERROR("Stats interval must be > 0");
+                               usage(64);
+                       }
+                       break;
+
+               case 'T':
+                       conf->stats.timeout = atoi(optarg);
+                       if (conf->stats.timeout <= 0) {
+                               ERROR("Timeout value must be > 0");
+                               usage(64);
+                       }
+                       break;
+
+#ifdef HAVE_COLLECTDC_H
+               case 'N':
+                       conf->stats.prefix = optarg;
+                       break;
+
+               case 'O':
+                       conf->stats.collectd = optarg;
+                       conf->stats.out = RS_STATS_OUT_COLLECTD;
+                       break;
+#endif
                default:
                        usage(64);
                }
@@ -479,196 +2033,476 @@ int main(int argc, char *argv[])
         */
        if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) {
                fr_perror("radsniff");
-               exit(1);
+               exit(EXIT_FAILURE);
+       }
+
+       /* Useful for file globbing */
+       while (optind < argc) {
+               *in_head = fr_pcap_init(conf, argv[optind], PCAP_FILE_IN);
+               if (!*in_head) {
+                       goto finish;
+               }
+               in_head = &(*in_head)->next;
+               conf->from_file = true;
+               optind++;
+       }
+
+       /* Is stdin not a tty? If so it's probably a pipe */
+       if (!isatty(fileno(stdin))) {
+               conf->from_stdin = true;
        }
 
        /* What's the point in specifying -F ?! */
-       if (from_stdin && from_file && to_file) {
+       if (conf->from_stdin && conf->from_file && conf->to_file) {
                usage(64);
        }
 
        /* Can't read from both... */
-       if (from_file && from_dev) {
+       if (conf->from_file && conf->from_dev) {
                usage(64);
        }
 
        /* Reading from file overrides stdin */
-       if (from_stdin && (from_file || from_dev)) {
-               from_stdin = false;
+       if (conf->from_stdin && (conf->from_file || conf->from_dev)) {
+               conf->from_stdin = false;
        }
 
        /* Writing to file overrides stdout */
-       if (to_file && to_stdout) {
-               to_stdout = false;
+       if (conf->to_file && conf->to_stdout) {
+               conf->to_stdout = false;
+       }
+
+       if (conf->to_stdout) {
+               out = fr_pcap_init(conf, "stdout", PCAP_STDIO_OUT);
+               if (!out) {
+                       goto finish;
+               }
+       }
+
+       if (conf->from_stdin) {
+               *in_head = fr_pcap_init(conf, "stdin", PCAP_STDIO_IN);
+               if (!*in_head) {
+                       goto finish;
+               }
+               in_head = &(*in_head)->next;
+       }
+
+       if (conf->stats.interval && !conf->stats.out) {
+               conf->stats.out = RS_STATS_OUT_STDIO;
+       }
+
+       if (conf->stats.timeout == 0) {
+               conf->stats.timeout = RS_DEFAULT_TIMEOUT;
        }
 
        /*
-        *  If were writing pcap data stdout we *really* don't want to send
-        *  logging there as well.
+        *      If were writing pcap data, or CSV to stdout we *really* don't want to send
+        *      logging there as well.
         */
-       log_dst = to_stdout ? stderr : stdout;
+       if (conf->to_stdout || conf->list_attributes) {
+               fr_log_fp = stderr;
+       }
+
+       if (conf->list_attributes) {
+               conf->logger = rs_packet_print_csv;
+       } else if (fr_debug_flag > 0) {
+               conf->logger = rs_packet_print_fancy;
+       }
 
 #if !defined(HAVE_PCAP_FOPEN_OFFLINE) || !defined(HAVE_PCAP_DUMP_FOPEN)
-       if (from_stdin || to_stdout) {
-               fprintf(stderr, "radsniff: PCAP streams not supported.\n");
-               exit(64);
+       if (conf->from_stdin || conf->to_stdout) {
+               ERROR("PCAP streams not supported");
+               goto finish;
        }
 #endif
 
-       if (!pcap_filter) {
-               pcap_filter = buffer;
+       if (!conf->pcap_filter) {
                snprintf(buffer, sizeof(buffer), "udp port %d or %d or %d",
                         port, port + 1, 3799);
+               conf->pcap_filter = buffer;
        }
 
-       /*
-        *  There are times when we don't need the dictionaries.
-        */
-       if (!to_stdout) {
-               if (dict_init(dict_dir, RADIUS_DICTIONARY) < 0) {
-                       fr_perror("radsniff");
-                       exit(64);
+       if (dict_init(dict_dir, RADIUS_DICTIONARY) < 0) {
+               fr_perror("radsniff");
+               ret = 64;
+               goto finish;
+       }
+
+       if (dict_read(radius_dir, RADIUS_DICTIONARY) == -1) {
+               fr_perror("radsniff");
+               ret = 64;
+               goto finish;
+       }
+
+       fr_strerror();  /* Clear out any non-fatal errors */
+
+       if (conf->list_attributes) {
+               conf->list_da_num = rs_build_dict_list(conf->list_da, sizeof(conf->list_da) / sizeof(*conf->list_da),
+                                                      conf->list_attributes);
+               if (conf->list_da_num < 0) {
+                       usage(64);
+               }
+               rs_packet_print_csv_header();
+       }
+
+       if (conf->link_attributes) {
+               conf->link_da_num = rs_build_dict_list(conf->link_da, sizeof(conf->link_da) / sizeof(*conf->link_da),
+                                                      conf->link_attributes);
+               if (conf->link_da_num < 0) {
+                       usage(64);
                }
 
-               if (dict_read(radius_dir, RADIUS_DICTIONARY) == -1) {
-                       fr_perror("radsniff");
-                       exit(64);
+               link_tree = rbtree_create((rbcmp) rs_rtx_cmp, _unmark_link, 0);
+               if (!link_tree) {
+                       ERROR("Failed creating RTX tree");
+                       goto finish;
                }
        }
 
-       if (radius_filter) {
-               parsecode = userparse(NULL, radius_filter, &filter_vps);
-               if (parsecode == T_OP_INVALID) {
-                       fprintf(stderr, "radsniff: Invalid RADIUS filter \"%s\" (%s)\n", radius_filter, fr_strerror());
-                       exit(64);
+       if (conf->filter_request) {
+               vp_cursor_t cursor;
+               VALUE_PAIR *type;
+
+               if (rs_build_filter(&conf->filter_request_vps, conf->filter_request) < 0) {
+                       usage(64);
+               }
+
+               fr_cursor_init(&cursor, &conf->filter_request_vps);
+               type = fr_cursor_next_by_num(&cursor, PW_PACKET_TYPE, 0, TAG_ANY);
+               if (type) {
+                       fr_cursor_remove(&cursor);
+                       conf->filter_request_code = type->vp_integer;
+                       talloc_free(type);
                }
+       }
 
-               if (!filter_vps) {
-                       fprintf(stderr, "radsniff: Empty RADIUS filter \"%s\"\n", radius_filter);
-                       exit(64);
+       if (conf->filter_response) {
+               vp_cursor_t cursor;
+               VALUE_PAIR *type;
+
+               if (rs_build_filter(&conf->filter_response_vps, conf->filter_response) < 0) {
+                       usage(64);
                }
 
-               filter_tree = rbtree_create((rbcmp) fr_packet_cmp, _rb_rad_free, 0);
-               if (!filter_tree) {
-                       fprintf(stderr, "radsniff: Failed creating filter tree\n");
-                       exit(1);
+               fr_cursor_init(&cursor, &conf->filter_response_vps);
+               type = fr_cursor_next_by_num(&cursor, PW_PACKET_TYPE, 0, TAG_ANY);
+               if (type) {
+                       fr_cursor_remove(&cursor);
+                       conf->filter_response_code = type->vp_integer;
+                       talloc_free(type);
                }
        }
 
        /*
-        *  Setup the request tree
+        *      Default to logging and capturing all events
         */
-       request_tree = rbtree_create((rbcmp) fr_packet_cmp, _rb_rad_free, 0);
-       if (!request_tree) {
-               fprintf(stderr, "radsniff: Failed creating request tree\n");
-               exit(1);
+       if (conf->event_flags == 0) {
+               DEBUG("Logging all events");
+               memset(&conf->event_flags, 0xff, sizeof(conf->event_flags));
+       }
+
+       /*
+        *      If we need to list attributes, link requests using attributes, filter attributes
+        *      or print the packet contents, we need to decode the attributes.
+        *
+        *      But, if were just logging requests, or graphing packets, we don't need to decode
+        *      attributes.
+        */
+       if (conf->list_da_num || conf->link_da_num || conf->filter_response_vps || conf->filter_request_vps ||
+           conf->print_packet) {
+               conf->decode_attrs = true;
        }
 
        /*
-        *  Allocate a null packet for decrypting attributes in CoA requests
+        *      Setup the request tree
         */
-       nullpacket = rad_alloc(NULL, 0);
-       if (!nullpacket) {
-               fprintf(stderr, "radsniff: Out of memory\n");
-               exit(1);
+       request_tree = rbtree_create((rbcmp) rs_packet_cmp, _unmark_request, 0);
+       if (!request_tree) {
+               ERROR("Failed creating request tree");
+               goto finish;
        }
 
        /*
-        *  Get the default capture device
+        *      Get the default capture device
         */
-       if (!from_stdin && !from_file && !from_dev) {
-               from_dev = pcap_lookupdev(errbuf);
-               if (!from_dev) {
-                       fprintf(stderr, "radsniff: Failed discovering default interface (%s)\n", errbuf);
-                       exit(1);
+       if (!conf->from_stdin && !conf->from_file && !conf->from_dev) {
+               pcap_if_t *all_devices;                 /* List of all devices libpcap can listen on */
+               pcap_if_t *dev_p;
+
+               if (pcap_findalldevs(&all_devices, errbuf) < 0) {
+                       ERROR("Error getting available capture devices: %s", errbuf);
+                       goto finish;
+               }
+
+               if (!all_devices) {
+                       ERROR("No capture files specified and no live interfaces available");
+                       ret = 64;
+                       goto finish;
                }
 
-               INFO(log_dst, "Capturing from interface \"%s\"\n", from_dev);
+               for (dev_p = all_devices;
+                    dev_p;
+                    dev_p = dev_p->next) {
+                       /* Don't use the any devices, it's horribly broken */
+                       if (!strcmp(dev_p->name, "any")) continue;
+                       *in_head = fr_pcap_init(conf, dev_p->name, PCAP_INTERFACE_IN);
+                       in_head = &(*in_head)->next;
+               }
+               conf->from_auto = true;
+               conf->from_dev = true;
+               INFO("Defaulting to capture on all interfaces");
        }
 
        /*
-        *  Print captures values which will be used
+        *      Print captures values which will be used
         */
        if (fr_debug_flag > 2) {
-                               DEBUG1(log_dst, "Sniffing with options:\n");
-               if (from_dev)   DEBUG1(log_dst, "  Device                   : [%s]\n", from_dev);
-               if (limit > 0)  DEBUG1(log_dst, "  Capture limit (packets)  : [%d]\n", limit);
-                               DEBUG1(log_dst, "  PCAP filter              : [%s]\n", pcap_filter);
-                               DEBUG1(log_dst, "  RADIUS secret            : [%s]\n", radius_secret);
-               if (filter_vps){DEBUG1(log_dst, "  RADIUS filter            :\n");
-                       vp_printlist(log_dst, filter_vps);
+               DEBUG2("Sniffing with options:");
+               if (conf->from_dev)     {
+                       char *buff = fr_pcap_device_names(conf, in, ' ');
+                       DEBUG2("  Device(s)               : [%s]", buff);
+                       talloc_free(buff);
+               }
+               if (conf->to_file || conf->to_stdout) {
+                       DEBUG2("  Writing to              : [%s]", out->name);
+               }
+               if (conf->limit > 0)    {
+                       DEBUG2("  Capture limit (packets) : [%" PRIu64 "]", conf->limit);
+               }
+                       DEBUG2("  PCAP filter             : [%s]", conf->pcap_filter);
+                       DEBUG2("  RADIUS secret           : [%s]", conf->radius_secret);
+
+               if (conf->filter_request_code) {
+                       DEBUG2("  RADIUS request code     : [%s]", fr_packet_codes[conf->filter_request_code]);
+               }
+
+               if (conf->filter_request_vps){
+                       DEBUG2("  RADIUS request filter   :");
+                       vp_printlist(fr_log_fp, conf->filter_request_vps);
+               }
+
+               if (conf->filter_response_code) {
+                       DEBUG2("  RADIUS response code    : [%s]", fr_packet_codes[conf->filter_response_code]);
+               }
+
+               if (conf->filter_response_vps){
+                       DEBUG2("  RADIUS response filter  :");
+                       vp_printlist(fr_log_fp, conf->filter_response_vps);
                }
        }
 
        /*
-        *  Figure out whether were doing a reading from a file, doing a live
-        *  capture or reading from stdin.
+        *      Setup collectd templates
         */
-       if (from_file) {
-               in = pcap_open_offline(from_file, errbuf);
-#ifdef HAVE_PCAP_FOPEN_OFFLINE
-       } else if (from_stdin) {
-               in = pcap_fopen_offline(stdin, errbuf);
-#endif
-       } else if (from_dev) {
-               pcap_lookupnet(from_dev, &ip_addr, &ip_mask, errbuf);
-               in = pcap_open_live(from_dev, 65536, 1, 1, errbuf);
-       } else {
-               fprintf(stderr, "radsniff: No capture devices available\n");
+#ifdef HAVE_COLLECTDC_H
+       if (conf->stats.out == RS_STATS_OUT_COLLECTD) {
+               size_t i;
+               rs_stats_tmpl_t *tmpl, **next;
+
+               if (rs_stats_collectd_open(conf) < 0) {
+                       exit(EXIT_FAILURE);
+               }
+
+               next = &conf->stats.tmpl;
+
+               for (i = 0; i < (sizeof(rs_useful_codes) / sizeof(*rs_useful_codes)); i++) {
+                       tmpl = rs_stats_collectd_init_latency(conf, next, conf, "exchanged",
+                                                             &stats.exchange[rs_useful_codes[i]],
+                                                             rs_useful_codes[i]);
+                       if (!tmpl) {
+                               ERROR("Error allocating memory for stats template");
+                               goto finish;
+                       }
+                       next = &(tmpl->next);
+               }
        }
+#endif
 
-       if (!in) {
-               fprintf(stderr, "radsniff: Failed opening input (%s)\n", errbuf);
-               exit(1);
+       /*
+        *      This actually opens the capture interfaces/files (we just allocated the memory earlier)
+        */
+       {
+               fr_pcap_t *tmp;
+               fr_pcap_t **tmp_p = &tmp;
+
+               for (in_p = in;
+                    in_p;
+                    in_p = in_p->next) {
+                       in_p->promiscuous = conf->promiscuous;
+                       in_p->buffer_pkts = conf->buffer_pkts;
+                       if (fr_pcap_open(in_p) < 0) {
+                               ERROR("Failed opening pcap handle (%s): %s", in_p->name, fr_strerror());
+                               if (conf->from_auto || (in_p->type == PCAP_FILE_IN)) {
+                                       continue;
+                               }
+
+                               goto finish;
+                       }
+
+                       if (conf->pcap_filter) {
+                               if (fr_pcap_apply_filter(in_p, conf->pcap_filter) < 0) {
+                                       ERROR("Failed applying filter");
+                                       goto finish;
+                               }
+                       }
+
+                       *tmp_p = in_p;
+                       tmp_p = &(in_p->next);
+               }
+               *tmp_p = NULL;
+               in = tmp;
+
+               if (!in) {
+                       ERROR("No PCAP sources available");
+                       exit(EXIT_FAILURE);
+               }
+
+               /* Clear any irrelevant errors */
+               fr_strerror();
        }
 
-       if (to_file) {
-               out = pcap_dump_open(in, to_file);
-               if (!out) {
-                       fprintf(stderr, "radsniff: Failed opening output file (%s)\n", pcap_geterr(in));
-                       exit(1);
+       /*
+        *      Open our output interface (if we have one);
+        */
+       if (out) {
+               out->link_type = -1;    /* Infer output link type from input */
+
+               for (in_p = in;
+                    in_p;
+                    in_p = in_p->next) {
+                       if (out->link_type < 0) {
+                               out->link_type = in_p->link_type;
+                               continue;
+                       }
+
+                       if (out->link_type != in_p->link_type) {
+                               ERROR("Asked to write to output file, but inputs do not have the same link type");
+                               ret = 64;
+                               goto finish;
+                       }
                }
-#ifdef HAVE_PCAP_DUMP_FOPEN
-       } else if (to_stdout) {
-               out = pcap_dump_fopen(in, stdout);
-               if (!out) {
-                       fprintf(stderr, "radsniff: Failed opening stdout (%s)\n", pcap_geterr(in));
-                       exit(1);
+
+               assert(out->link_type > 0);
+
+               if (fr_pcap_open(out) < 0) {
+                       ERROR("Failed opening pcap output (%s): %s", out->name, fr_strerror());
+                       goto finish;
                }
-#endif
        }
 
        /*
-        *  Apply the rules
+        *      Setup and enter the main event loop. Who needs libev when you can roll your own...
         */
-       if (pcap_compile(in, &fp, pcap_filter, 0, ip_mask) < 0) {
-               fprintf(stderr, "radsniff: Failed compiling PCAP filter (%s)\n", pcap_geterr(in));
-               exit(1);
+        {
+               struct timeval now;
+               rs_update_t update;
+
+               char *buff;
+
+               memset(&stats, 0, sizeof(stats));
+               memset(&update, 0, sizeof(update));
+
+               events = fr_event_list_create(conf, _rs_event_status);
+               if (!events) {
+                       ERROR();
+                       goto finish;
+               }
+
+               /*
+                *  Initialise the signal handler pipe
+                */
+               if (pipe(self_pipe) < 0) {
+                       ERROR("Couldn't open signal pipe: %s", fr_syserror(errno));
+                       exit(EXIT_FAILURE);
+               }
+
+               if (!fr_event_fd_insert(events, 0, self_pipe[0], rs_signal_action, events)) {
+                       ERROR("Failed inserting signal pipe descriptor: %s", fr_strerror());
+                       goto finish;
+               }
+
+               /*
+                *  Now add fd's for each of the pcap sessions we opened
+                */
+               for (in_p = in;
+                    in_p;
+                    in_p = in_p->next) {
+                       rs_event_t *event;
+
+                       event = talloc_zero(events, rs_event_t);
+                       event->list = events;
+                       event->in = in_p;
+                       event->out = out;
+                       event->stats = &stats;
+
+                       if (!fr_event_fd_insert(events, 0, in_p->fd, rs_got_packet, event)) {
+                               ERROR("Failed inserting file descriptor");
+                               goto finish;
+                       }
+               }
+
+               buff = fr_pcap_device_names(conf, in, ' ');
+               DEBUG("Sniffing on (%s)", buff);
+               talloc_free(buff);
+
+               gettimeofday(&now, NULL);
+
+               /*
+                *  Insert our stats processor
+                */
+               if (conf->stats.interval) {
+                       static fr_event_t *event;
+
+                       update.list = events;
+                       update.stats = &stats;
+                       update.in = in;
+
+                       now.tv_sec += conf->stats.interval;
+                       now.tv_usec = 0;
+                       if (!fr_event_insert(events, rs_stats_process, (void *) &update, &now, &event)) {
+                               ERROR("Failed inserting stats event");
+                       }
+
+                       INFO("Muting stats for the next %i milliseconds (warmup)", conf->stats.timeout);
+                       rs_tv_add_ms(&now, conf->stats.timeout, &stats.quiet);
+               }
        }
 
-       if (pcap_setfilter(in, &fp) < 0) {
-               fprintf(stderr, "radsniff: Failed applying PCAP filter (%s)\n", pcap_geterr(in));
-               exit(1);
+
+       /*
+        *      Do this as late as possible so we can return an error code if something went wrong.
+        */
+       if (conf->daemonize) {
+               rs_daemonize(conf->pidfile);
        }
 
        /*
-        *  Enter the main capture loop...
+        *      Setup signal handlers so we always exit gracefully, ensuring output buffers are always
+        *      flushed.
         */
-       pcap_loop(in, limit, got_packet, NULL);
+       fr_set_signal(SIGPIPE, rs_signal_self);
+       fr_set_signal(SIGINT, rs_signal_self);
+       fr_set_signal(SIGTERM, rs_signal_self);
+#ifdef SIGQUIT
+       fr_set_signal(SIGQUIT, rs_signal_self);
+#endif
+
+       fr_event_loop(events);  /* Enter the main event loop */
+
+       DEBUG("Done sniffing");
+
+       finish:
+
+       cleanup = true;
 
        /*
-        *  ...were done capturing.
+        *      Free all the things! This also closes all the sockets and file descriptors
         */
-       pcap_close(in);
-       if (out) {
-               pcap_dump_close(out);
-       }
+       talloc_free(conf);
 
-       if (filter_tree) {
-               rbtree_free(filter_tree);
+       if (conf->daemonize) {
+               unlink(conf->pidfile);
        }
 
-       INFO(log_dst, "Done sniffing\n");
-
-       return 0;
+       return ret;
 }
index 03b9abd..3de7ebc 100644 (file)
@@ -6,7 +6,8 @@ else
 TARGET         :=
 endif
 
-SOURCES                := radsniff.c
+SOURCES                := radsniff.c collectd.c
 
 TGT_PREREQS    := libfreeradius-radius.a
-TGT_LDLIBS     := $(LIBS) $(PCAP_LIBS)
+TGT_LDLIBS     := $(LIBS) $(PCAP_LIBS) $(COLLECTDC_LIBS)
+TGT_LDFLAGS     := $(LDFLAGS) $(PCAP_LDFLAGS) $(COLLECTDC_LDFLAGS)
index df77af8..a6d8a2e 100644 (file)
@@ -295,7 +295,7 @@ app:
        packet->vps = NULL;
        PTHREAD_MUTEX_UNLOCK(&sock->mutex);
 
-       if (!rad_packet_ok(packet, 0)) {
+       if (!rad_packet_ok(packet, 0, NULL)) {
                RDEBUG("Received bad packet: %s", fr_strerror());
                DEBUG("Closing TLS socket from client");
                PTHREAD_MUTEX_LOCK(&sock->mutex);