Browse Source

Merge upstream version 4.4.0

Christoph Biedl 2 years ago
parent
commit
350140adde
100 changed files with 2003 additions and 10176 deletions
  1. 8 4
      Makefile.in
  2. 7 9050
      aclocal.m4
  3. 61 103
      configure
  4. 48 94
      configure.ac
  5. 14 0
      docs/CHANGELOG
  6. 3 0
      docs/CREDIT
  7. 6 3
      docs/Makefile.in
  8. 1 1
      docs/TODO
  9. 6 3
      lib/Makefile.in
  10. 24 28
      lib/strlcat.c
  11. 19 24
      lib/strlcpy.c
  12. 1 1
      lib/strlcpy.h
  13. 6 3
      libopts/Makefile.in
  14. 588 0
      m4/libopts.m4
  15. 41 0
      m4/stdnoreturn.m4
  16. 6 3
      scripts/Makefile.in
  17. 6 3
      src/Makefile.in
  18. 1 1
      src/bridge.c
  19. 1 1
      src/bridge.h
  20. 1 1
      src/common.h
  21. 6 3
      src/common/Makefile.in
  22. 1 1
      src/common/cache.c
  23. 1 1
      src/common/cache.h
  24. 5 7
      src/common/cidr.c
  25. 1 1
      src/common/cidr.h
  26. 1 1
      src/common/err.c
  27. 1 1
      src/common/err.h
  28. 1 1
      src/common/fakepcap.c
  29. 1 1
      src/common/fakepcap.h
  30. 1 1
      src/common/fakepcapnav.c
  31. 1 1
      src/common/fakepcapnav.h
  32. 35 92
      src/common/flows.c
  33. 1 1
      src/common/flows.h
  34. 348 212
      src/common/get.c
  35. 19 2
      src/common/get.h
  36. 1 1
      src/common/git_version.c
  37. 1 1
      src/common/interface.c
  38. 1 1
      src/common/interface.h
  39. 1 1
      src/common/list.c
  40. 1 1
      src/common/list.h
  41. 1 1
      src/common/mac.c
  42. 1 1
      src/common/mac.h
  43. 1 1
      src/common/pcap_dlt.h
  44. 1 1
      src/common/sendpacket.c
  45. 1 1
      src/common/sendpacket.h
  46. 1 1
      src/common/services.c
  47. 1 1
      src/common/services.h
  48. 1 1
      src/common/tcpdump.c
  49. 1 1
      src/common/tcpdump.h
  50. 1 1
      src/common/timer.c
  51. 1 1
      src/common/timer.h
  52. 1 1
      src/common/utils.c
  53. 1 3
      src/common/utils.h
  54. 1 1
      src/common/xX.c
  55. 1 1
      src/common/xX.h
  56. 3 3
      src/config.h.in
  57. 6 3
      src/defines.h
  58. 6 3
      src/defines.h.in
  59. 6 3
      src/fragroute/Makefile.in
  60. 1 1
      src/fragroute/fragroute.c
  61. 1 1
      src/fragroute/fragroute.h
  62. 1 1
      src/replay.c
  63. 1 1
      src/replay.h
  64. 37 37
      src/send_packets.c
  65. 1 1
      src/send_packets.h
  66. 1 1
      src/signal_handler.c
  67. 1 1
      src/signal_handler.h
  68. 1 1
      src/sleep.c
  69. 1 1
      src/sleep.h
  70. 24 4
      src/tcpbridge.1
  71. 1 1
      src/tcpbridge.c
  72. 1 1
      src/tcpbridge.h
  73. 215 184
      src/tcpbridge_opts.c
  74. 4 4
      src/tcpbridge_opts.def
  75. 34 32
      src/tcpbridge_opts.h
  76. 1 1
      src/tcpcapinfo.1
  77. 1 1
      src/tcpcapinfo.c
  78. 1 1
      src/tcpcapinfo_opts.c
  79. 2 2
      src/tcpcapinfo_opts.def
  80. 6 3
      src/tcpedit/Makefile.in
  81. 19 24
      src/tcpedit/checksum.c
  82. 1 1
      src/tcpedit/checksum.h
  83. 1 1
      src/tcpedit/dlt.c
  84. 1 1
      src/tcpedit/dlt.h
  85. 5 44
      src/tcpedit/edit_packet.c
  86. 10 1
      src/tcpedit/edit_packet.h
  87. 6 2
      src/tcpedit/fuzzing.c
  88. 1 1
      src/tcpedit/parse_args.c
  89. 1 1
      src/tcpedit/parse_args.h
  90. 1 1
      src/tcpedit/plugins.h
  91. 251 123
      src/tcpedit/plugins/dlt_en10mb/en10mb.c
  92. 6 2
      src/tcpedit/plugins/dlt_en10mb/en10mb.h
  93. 1 1
      src/tcpedit/plugins/dlt_en10mb/en10mb_api.c
  94. 1 1
      src/tcpedit/plugins/dlt_en10mb/en10mb_api.h
  95. 27 1
      src/tcpedit/plugins/dlt_en10mb/en10mb_opts.def
  96. 8 1
      src/tcpedit/plugins/dlt_en10mb/en10mb_types.h
  97. 8 4
      src/tcpedit/plugins/dlt_hdlc/hdlc.c
  98. 6 2
      src/tcpedit/plugins/dlt_hdlc/hdlc.h
  99. 1 1
      src/tcpedit/plugins/dlt_hdlc/hdlc_api.c
  100. 0 0
      src/tcpedit/plugins/dlt_hdlc/hdlc_api.h

+ 8 - 4
Makefile.in

@@ -90,9 +90,12 @@ host_triplet = @host@
 target_triplet = @target@
 subdir = .
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/libopts/m4/libopts.m4 \
-	$(top_srcdir)/libopts/m4/stdnoreturn.m4 \
-	$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
+am__aclocal_m4_deps = $(top_srcdir)/m4/libopts.m4 \
+	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+	$(top_srcdir)/m4/lt~obsolete.m4 \
+	$(top_srcdir)/m4/stdnoreturn.m4 $(top_srcdir)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \
@@ -165,7 +168,8 @@ am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/doxygen.cfg.in \
 	$(top_srcdir)/config/install-sh $(top_srcdir)/config/ltmain.sh \
 	$(top_srcdir)/config/missing INSTALL config/ar-lib \
 	config/compile config/config.guess config/config.sub \
-	config/install-sh config/ltmain.sh config/missing
+	config/depcomp config/install-sh config/ltmain.sh \
+	config/missing
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 distdir = $(PACKAGE)-$(VERSION)
 top_distdir = $(distdir)

File diff suppressed because it is too large
+ 7 - 9050
aclocal.m4


+ 61 - 103
configure

@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for tcpreplay 4.3.4.
+# Generated by GNU Autoconf 2.69 for tcpreplay 4.4.0.
 #
 # Report bugs to <https://github.com/appneta/tcpreplay/issues>.
 #
@@ -590,8 +590,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='tcpreplay'
 PACKAGE_TARNAME='tcpreplay'
-PACKAGE_VERSION='4.3.4'
-PACKAGE_STRING='tcpreplay 4.3.4'
+PACKAGE_VERSION='4.4.0'
+PACKAGE_STRING='tcpreplay 4.4.0'
 PACKAGE_BUGREPORT='https://github.com/appneta/tcpreplay/issues'
 PACKAGE_URL='http://tcpreplay.sourceforge.net/'
 
@@ -833,7 +833,7 @@ ac_user_opts='
 enable_option_checking
 enable_maintainer_mode
 with_dmalloc
-with_macosx_sdk
+with_macos_sdk
 with_pfring_lib
 enable_silent_rules
 enable_dependency_tracking
@@ -1445,7 +1445,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures tcpreplay 4.3.4 to adapt to many kinds of systems.
+\`configure' configures tcpreplay 4.4.0 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1517,7 +1517,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of tcpreplay 4.3.4:";;
+     short | recursive ) echo "Configuration of tcpreplay 4.4.0:";;
    esac
   cat <<\_ACEOF
 
@@ -1576,10 +1576,7 @@ Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
   --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
   --with-dmalloc          use dmalloc, as in http://www.dmalloc.com
-  --with-macosx-sdk       Use a specific SDK for building.
-
-                          Usage:     --with-macosx-sdk=<version>
-                          e. g.: --with-macosx-sdk=10.8
+  --with-macos-sdk=VER    Specify the macOS SDK version to use.
 
   --with-pfring-lib       Use a specific PF_RING static library when using
                           PF_RING libpcap.
@@ -1692,7 +1689,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-tcpreplay configure 4.3.4
+tcpreplay configure 4.4.0
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2652,7 +2649,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by tcpreplay $as_me 4.3.4, which was
+It was created by tcpreplay $as_me 4.4.0, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -3221,17 +3218,15 @@ fi
 
 
 
-# Check whether --with-macosx-sdk was given.
-if test "${with_macosx_sdk+set}" = set; then :
-  withval=$with_macosx_sdk;
+# Check whether --with-macos-sdk was given.
+if test "${with_macos_sdk+set}" = set; then :
+  withval=$with_macos_sdk;
 fi
 
 
-
-
 case "$host_os" in
     darwin*) # Mac OS X or iOS
-        # If no --with-macosx-sdk option is given, look for one
+        # If no --with-macos-sdk option is given, look for the latestq SDK
 
         # The intent is that for "most" Mac-based developers, a suitable
         # SDK will be found automatically without any configure options.
@@ -3242,22 +3237,48 @@ case "$host_os" in
         # To find a list of available version run `xcodebuild -showsdks`
 
         MULTIARCH=${host_cpu}-${host_os}
+        unset MACOSX_SDK_PATH
+
         { $as_echo "$as_me:${as_lineno-$LINENO}: checking what macOS compiler to use" >&5
 $as_echo_n "checking what macOS compiler to use... " >&6; }
 
-        for _macosx_sdk in $with_macosx_sdk 10.8 10.9 10.10 10.11 10.12 10.13 10.14 10.15 10.16 10.17 10.18 10.19 10.20 11.0 11.1 11.2 11.3 11.4 11.5; do
-            MACOSX_SDK_PATH=$(xcrun --sdk macosx${_macosx_sdk} --show-sdk-path 2> /dev/null)
-            if test -d "$MACOSX_SDK_PATH"; then
-                with_macosx_sdk="${_macosx_sdk}"
-                break
-            else
-                MACOSX_SDK_PATH=$(xcode-select -print-path)/Platforms/MacOSX.platform/Developer/SDKs/MacOSX${_macosx_sdk}.sdk
-                if test -d "$MACOSX_SDK_PATH"; then
-                    with_macosx_sdk="${_macosx_sdk}"
-                    break
+        if test -n "$with_macos_sdk"; then
+            MACOSX_SDK_PATH=$(xcrun --sdk macosx${with_macos_sdk} --show-sdk-path 2> /dev/null)
+            if test -z "$MACOSX_SDK_PATH" ; then
+                as_fn_error $? "could not find SDK ${with_macos_sdk} for macos-${MULTIARCH}" "$LINENO" 5
+            fi
+        else
+            MACOSX_SDK_PATH=$(xcrun --show-sdk-path 2> /dev/null)
+            if test -z "$MACOSX_SDK_PATH" ; then
+                                for _macos_sdk_major in $(seq 15 -1 10); do
+                    for _macos_sdk_minor in $(seq 20 -1 0); do
+                        _macos_sdk_version=$_macos_sdk_major.$_macos_sdk_minor
+                        MACOSX_SDK_PATH=$(xcrun --sdk macosx${_macos_sdk_version} --show-sdk-path 2> /dev/null)
+                        if test -d "$_macos_sdk_path" ; then
+                            break 2
+                        fi
+                    done
+                done
+
+                                if test -z "$MACOSX_SDK_PATH"; then
+                    for _macos_sdk_major in $(seq 15 -1 10); do
+                        for _macos_sdk_minor in $(seq 20 -1 0); do
+                            _macos_sdk_version=$_macos_sdk_major.$_macos_sdk_minor
+                            MACOSX_SDK_PATH=$(xcode-select -print-path)/Platforms/MacOSX.platform/Developer/SDKs/MacOSX${_macos_sdk_version}.sdk
+                            if test -d "$MACOSX_SDK_PATH"; then
+                                break 2
+                            fi
+
+                            MACOSX_SDK_PATH=$(xcode-select -print-path)/SDKs/MacOSX${_macos_sdk_version}.sdk
+                            if test -d "$MACOSX_SDK_PATH"; then
+                                break 2
+                            fi
+                        done
+                    done
                 fi
             fi
-        done
+        fi
+
         if test -d "$MACOSX_SDK_PATH"; then
             CC="$(xcrun -find clang) -m64 -isysroot $MACOSX_SDK_PATH"
             INSTALL_NAME_TOOL=$(xcrun -find install_name_tool)
@@ -3266,8 +3287,8 @@ $as_echo_n "checking what macOS compiler to use... " >&6; }
             STRIP=$(xcrun -find strip)
             LIBTOOL=$(xcrun -find libtool)
             RANLIB=$(xcrun -find ranlib)
-            { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
-$as_echo "$CC" >&6; }
+            { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${MACOSX_SDK_PATH}" >&5
+$as_echo "${MACOSX_SDK_PATH}" >&6; }
         else
             { $as_echo "$as_me:${as_lineno-$LINENO}: result: legacy" >&5
 $as_echo "legacy" >&6; }
@@ -3795,7 +3816,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='tcpreplay'
- VERSION='4.3.4'
+ VERSION='4.4.0'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -9342,6 +9363,10 @@ _lt_linker_boilerplate=`cat conftest.err`
 $RM -r conftest*
 
 
+## CAVEAT EMPTOR:
+## There is no encapsulation within the following macros, do not change
+## the running order or otherwise move them around unless you know exactly
+## what you are doing...
 if test -n "$compiler"; then
 
 lt_prog_compiler_no_builtin_flag=
@@ -19291,7 +19316,7 @@ fi
 
 done
 
-for ac_header in sys/io.h architecture/i386/pio.h sched.h
+for ac_header in sys/io.h architecture/i386/pio.h sched.h fts.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"
@@ -22555,7 +22580,7 @@ if test "$cross_compiling" = yes; then
         NETMAP_SEARCH_DIRS=$trynetmapdir
     fi
 else
-    NETMAP_SEARCH_DIRS="$trynetmapdir /opt/netmap /usr/src/netmap-release /usr/src/netmap /usr/local/src/netmap-release /usr/local/src/netmap /usr/include"
+    NETMAP_SEARCH_DIRS="$trynetmapdir /opt/netmap /usr/src/netmap-release /usr/src/netmap /usr/local/src/netmap-release /usr/local/src/netmap /usr/include /usr/local/include"
 fi
 
 for testdir in $NETMAP_SEARCH_DIRS; do
@@ -23237,73 +23262,6 @@ fi
 fi # checking pcapnav version
 
 
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for requires strict byte alignment" >&5
-$as_echo_n "checking for requires strict byte alignment... " >&6; }
-if ${unaligned_cv_fail+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  case "$host_cpu" in
-
-        # XXX: should also check that they don't do weird things (like on arm)
-        alpha*|arm*|hp*|mips*|sparc*|ia64)
-                unaligned_cv_fail=yes
-                ;;
-
-        *)
-        cat >conftest.c <<EOF
-        #include <sys/types.h>
-        #include <sys/wait.h>
-        #include <stdio.h>
-        unsigned char a[5] = { 1, 2, 3, 4, 5 };
-        main() {
-            unsigned int i;
-            pid_t pid;
-            int status;
-            /* avoid "core dumped" message */
-            pid = fork();
-            if (pid <  0)
-                exit(2);
-            if (pid > 0) {
-                /* parent */
-                pid = waitpid(pid, &status, 0);
-                if (pid < 0)
-                        exit(3);
-                exit(!WIFEXITED(status));
-            }
-            /* child */
-            i = *(unsigned int *)&a[1];
-            printf("%d\n", i);
-            exit(0);
-        }
-EOF
-        ${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS \
-            conftest.c $LIBS >/dev/null 2>&1
-        if test ! -x conftest ; then
-                        unaligned_cv_fail=yes
-        else
-            ./conftest >conftest.out
-            if test ! -s conftest.out ; then
-                unaligned_cv_fail=yes
-            else
-                unaligned_cv_fail=no
-            fi
-        fi
-        rm -f conftest* core core.conftest
-        ;;
-
-        esac
-
-fi
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $unaligned_cv_fail" >&5
-$as_echo "$unaligned_cv_fail" >&6; }
-if test $unaligned_cv_fail = yes ; then
-
-$as_echo "#define FORCE_ALIGN 1" >>confdefs.h
-
-fi
-
-
 tcpdump_path=no
 
 # Check whether --with-tcpdump was given.
@@ -25839,7 +25797,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by tcpreplay $as_me 4.3.4, which was
+This file was extended by tcpreplay $as_me 4.4.0, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -25906,7 +25864,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-tcpreplay config.status 4.3.4
+tcpreplay config.status 4.4.0
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 

+ 48 - 94
configure.ac

@@ -4,7 +4,7 @@ dnl $Id$
 AC_PREREQ([2.69])
 
 dnl Set version info here!
-AC_INIT([tcpreplay],[4.3.4],[https://github.com/appneta/tcpreplay/issues],[tcpreplay],[http://tcpreplay.sourceforge.net/])
+AC_INIT([tcpreplay],[4.4.0],[https://github.com/appneta/tcpreplay/issues],[tcpreplay],[http://tcpreplay.sourceforge.net/])
 AC_CONFIG_SRCDIR([src/tcpreplay.c])
 AC_CONFIG_HEADERS([src/config.h])
 AC_CONFIG_AUX_DIR(config)
@@ -44,27 +44,18 @@ fi
 AC_SUBST(CROSS_ARCH)
 AC_SUBST(CROSS_LD)
 
-dnl ============================================
-dnl MacOSX build and runtime environment options
-dnl ============================================
-
-AC_ARG_WITH(macosx-sdk,
-    AS_HELP_STRING([--with-macosx-sdk],
-        [Use a specific SDK for building.])
-    [
-                          Usage:     --with-macosx-sdk=<version>
-                          e. g.: --with-macosx-sdk=10.8
-    ],
-,)
-
 
 dnl ===========================
 dnl Check OS X SDK and compiler
 dnl ===========================
+AC_ARG_WITH([macos-sdk],
+            [AS_HELP_STRING([--with-macos-sdk=VER],
+                            [Specify the macOS SDK version to use.])]
+)
 
 case "$host_os" in
     darwin*) # Mac OS X or iOS
-        # If no --with-macosx-sdk option is given, look for one
+        # If no --with-macos-sdk option is given, look for the latestq SDK
 
         # The intent is that for "most" Mac-based developers, a suitable
         # SDK will be found automatically without any configure options.
@@ -75,21 +66,49 @@ case "$host_os" in
         # To find a list of available version run `xcodebuild -showsdks`
 
         MULTIARCH=${host_cpu}-${host_os}
+        unset MACOSX_SDK_PATH
+
         AC_MSG_CHECKING([what macOS compiler to use])
 
-        for _macosx_sdk in $with_macosx_sdk 10.8 10.9 10.10 10.11 10.12 10.13 10.14 10.15 10.16 10.17 10.18 10.19 10.20 11.0 11.1 11.2 11.3 11.4 11.5; do
-            MACOSX_SDK_PATH=$(xcrun --sdk macosx${_macosx_sdk} --show-sdk-path 2> /dev/null)
-            if test -d "$MACOSX_SDK_PATH"; then
-                with_macosx_sdk="${_macosx_sdk}"
-                break
-            else
-                MACOSX_SDK_PATH=$(xcode-select -print-path)/Platforms/MacOSX.platform/Developer/SDKs/MacOSX${_macosx_sdk}.sdk
-                if test -d "$MACOSX_SDK_PATH"; then
-                    with_macosx_sdk="${_macosx_sdk}"
-                    break
+        if test -n "$with_macos_sdk"; then
+            MACOSX_SDK_PATH=$(xcrun --sdk macosx${with_macos_sdk} --show-sdk-path 2> /dev/null)
+            if test -z "$MACOSX_SDK_PATH" ; then
+                AC_MSG_ERROR([could not find SDK ${with_macos_sdk} for macos-${MULTIARCH}])
+            fi
+        else
+            MACOSX_SDK_PATH=$(xcrun --show-sdk-path 2> /dev/null)
+            if test -z "$MACOSX_SDK_PATH" ; then
+                dnl could not find a default SDK - search likely SDK versions
+                for _macos_sdk_major in $(seq 15 -1 10); do
+                    for _macos_sdk_minor in $(seq 20 -1 0); do
+                        _macos_sdk_version=$_macos_sdk_major.$_macos_sdk_minor
+                        MACOSX_SDK_PATH=$(xcrun --sdk macosx${_macos_sdk_version} --show-sdk-path 2> /dev/null)
+                        if test -d "$_macos_sdk_path" ; then
+                            break 2
+                        fi
+                    done
+                done
+
+                dnl could not find a default SDK - search disk for the latest SDK
+                if test -z "$MACOSX_SDK_PATH"; then
+                    for _macos_sdk_major in $(seq 15 -1 10); do
+                        for _macos_sdk_minor in $(seq 20 -1 0); do
+                            _macos_sdk_version=$_macos_sdk_major.$_macos_sdk_minor
+                            MACOSX_SDK_PATH=$(xcode-select -print-path)/Platforms/MacOSX.platform/Developer/SDKs/MacOSX${_macos_sdk_version}.sdk
+                            if test -d "$MACOSX_SDK_PATH"; then
+                                break 2
+                            fi
+
+                            MACOSX_SDK_PATH=$(xcode-select -print-path)/SDKs/MacOSX${_macos_sdk_version}.sdk
+                            if test -d "$MACOSX_SDK_PATH"; then
+                                break 2
+                            fi
+                        done
+                    done
                 fi
             fi
-        done
+        fi
+
         if test -d "$MACOSX_SDK_PATH"; then
             CC="$(xcrun -find clang) -m64 -isysroot $MACOSX_SDK_PATH"
             INSTALL_NAME_TOOL=$(xcrun -find install_name_tool)
@@ -98,7 +117,7 @@ case "$host_os" in
             STRIP=$(xcrun -find strip)
             LIBTOOL=$(xcrun -find libtool)
             RANLIB=$(xcrun -find ranlib)
-            AC_MSG_RESULT([$CC])
+            AC_MSG_RESULT([${MACOSX_SDK_PATH}])
         else
             AC_MSG_RESULT([legacy])
         fi
@@ -333,7 +352,7 @@ AC_CHECK_HEADERS([fcntl.h stddef.h sys/socket.h  arpa/inet.h sys/time.h])
 AC_CHECK_HEADERS([signal.h string.h strings.h sys/types.h stdint.h sys/select.h])
 AC_CHECK_HEADERS([netinet/in.h netinet/in_systm.h poll.h sys/poll.h unistd.h sys/param.h])
 AC_CHECK_HEADERS([inttypes.h libintl.h sys/file.h sys/ioctl.h sys/systeminfo.h])
-AC_CHECK_HEADERS([sys/io.h architecture/i386/pio.h sched.h])
+AC_CHECK_HEADERS([sys/io.h architecture/i386/pio.h sched.h fts.h])
 AC_HEADER_STDBOOL
 
 dnl OpenBSD has special requirements
@@ -1266,7 +1285,7 @@ if test "$cross_compiling" = yes; then
         NETMAP_SEARCH_DIRS=$trynetmapdir
     fi
 else
-    NETMAP_SEARCH_DIRS="$trynetmapdir /opt/netmap /usr/src/netmap-release /usr/src/netmap /usr/local/src/netmap-release /usr/local/src/netmap /usr/include"
+    NETMAP_SEARCH_DIRS="$trynetmapdir /opt/netmap /usr/src/netmap-release /usr/src/netmap /usr/local/src/netmap-release /usr/local/src/netmap /usr/include /usr/local/include"
 fi
 
 for testdir in $NETMAP_SEARCH_DIRS; do
@@ -1680,71 +1699,6 @@ fi
 
 fi # checking pcapnav version
 
-
-dnl (shamelessly ripped off from libpcap)
-dnl Checks to see if unaligned memory accesses fail
-dnl
-dnl     FORCE_ALIGN (DEFINED)
-dnl
-AC_MSG_CHECKING(for requires strict byte alignment)
-AC_CACHE_VAL(unaligned_cv_fail,
-        [case "$host_cpu" in
-
-        # XXX: should also check that they don't do weird things (like on arm)
-        alpha*|arm*|hp*|mips*|sparc*|ia64)
-                unaligned_cv_fail=yes
-                ;;
-
-        *)
-        cat >conftest.c <<EOF
-        #include <sys/types.h>
-        #include <sys/wait.h>
-        #include <stdio.h>
-        unsigned char a[[5]] = { 1, 2, 3, 4, 5 };
-        main() {
-            unsigned int i;
-            pid_t pid;
-            int status;
-            /* avoid "core dumped" message */
-            pid = fork();
-            if (pid <  0)
-                exit(2);
-            if (pid > 0) {
-                /* parent */
-                pid = waitpid(pid, &status, 0);
-                if (pid < 0)
-                        exit(3);
-                exit(!WIFEXITED(status));
-            }
-            /* child */
-            i = *(unsigned int *)&a[[1]];
-            printf("%d\n", i);
-            exit(0);
-        }
-EOF
-        ${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS \
-            conftest.c $LIBS >/dev/null 2>&1
-        if test ! -x conftest ; then
-            dnl failed to compile for some reason
-            unaligned_cv_fail=yes
-        else
-            ./conftest >conftest.out
-            if test ! -s conftest.out ; then
-                unaligned_cv_fail=yes
-            else
-                unaligned_cv_fail=no
-            fi
-        fi
-        rm -f conftest* core core.conftest
-        ;;
-
-        esac
-    ])
-AC_MSG_RESULT($unaligned_cv_fail)
-if test $unaligned_cv_fail = yes ; then
-    AC_DEFINE([FORCE_ALIGN], [1], [Are we strictly aligned?])
-fi
-
 dnl ##################################################
 dnl # Check for tcpdump.
 dnl ##################################################

+ 14 - 0
docs/CHANGELOG

@@ -1,3 +1,17 @@
+06/19/2021 Version 4.4.0
+    - remove obsolete FORCE_ALIGN support to fix macOS 11 compile (#695)
+    - add a security policy document (#689)
+    - ability to specify directory of pcap files (#682)
+    - incorrect PPS rate for long-running sessions (#679)
+    - option --skipbroadcast not working (#677)
+    - revert #630 to fix --multiplier issues (#674)
+    - gcc 9.3 compiler warnings (#670)
+    - installed netmap not automatically detected (#669)
+    - latest macOS SDK selected by default (#668)
+    - heap-buffer-overflow with flow_decode() (#665)
+    - add feature VLAN Q-in-Q (#625)
+    - add feature update Ethernet MAC on multicast IP (#563)
+
 04/01/2021 Version 4.3.4
     - ASAN reports memory leaks while running tests (#662)
     - local libopts compiler warnings (#658)

+ 3 - 0
docs/CREDIT

@@ -104,3 +104,6 @@ Dave Craig <GitHub @davecraig>
 
 Vincent Bernat <GitHub @vincentbernat>
     - tcprewrite: fix DLT name for DLT_C_JNPR_ETHER in documentation
+
+Halver <GigHub @Halver>
+    - specify directories as files

+ 6 - 3
docs/Makefile.in

@@ -90,9 +90,12 @@ host_triplet = @host@
 target_triplet = @target@
 subdir = docs
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/libopts/m4/libopts.m4 \
-	$(top_srcdir)/libopts/m4/stdnoreturn.m4 \
-	$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
+am__aclocal_m4_deps = $(top_srcdir)/m4/libopts.m4 \
+	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+	$(top_srcdir)/m4/lt~obsolete.m4 \
+	$(top_srcdir)/m4/stdnoreturn.m4 $(top_srcdir)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)

+ 1 - 1
docs/TODO

@@ -76,7 +76,7 @@ TCPREWRITE:
     - others non-vanilla types?
     + Add tags?  Remove tags?  Change tags?
     - Tag only one side of the connection
-    - Support Q-in-Q tags:
+    + Support Q-in-Q tags:
           http://www.informit.com/articles/article.asp?p=101367&rl=1
     - Cisco's ISL trunking?
 

+ 6 - 3
lib/Makefile.in

@@ -92,9 +92,12 @@ host_triplet = @host@
 target_triplet = @target@
 subdir = lib
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/libopts/m4/libopts.m4 \
-	$(top_srcdir)/libopts/m4/stdnoreturn.m4 \
-	$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
+am__aclocal_m4_deps = $(top_srcdir)/m4/libopts.m4 \
+	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+	$(top_srcdir)/m4/lt~obsolete.m4 \
+	$(top_srcdir)/m4/stdnoreturn.m4 $(top_srcdir)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \

+ 24 - 28
lib/strlcat.c

@@ -1,7 +1,7 @@
-/*	$OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $	*/
+/*	$OpenBSD: strlcat.c,v 1.19 2019/01/25 00:19:25 millert Exp $	*/
 
 /*
- * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * Copyright (c) 1998, 2015 Todd C. Miller <millert@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -16,44 +16,40 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#if defined(LIBC_SCCS) && !defined(lint)
-static char *rcsid = "$OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $";
-#endif /* LIBC_SCCS and not lint */
-
 #include <sys/types.h>
 #include <string.h>
 
 /*
- * Appends src to string dst of size siz (unlike strncat, siz is the
- * full size of dst, not space left).  At most siz-1 characters
- * will be copied.  Always NUL terminates (unless siz <= strlen(dst)).
- * Returns strlen(src) + MIN(siz, strlen(initial dst)).
- * If retval >= siz, truncation occurred.
+ * Appends src to string dst of size dsize (unlike strncat, dsize is the
+ * full size of dst, not space left).  At most dsize-1 characters
+ * will be copied.  Always NUL terminates (unless dsize <= strlen(dst)).
+ * Returns strlen(src) + MIN(dsize, strlen(initial dst)).
+ * If retval >= dsize, truncation occurred.
  */
 size_t
-strlcat(char *dst, const char *src, size_t siz)
+strlcat(char *dst, const char *src, size_t dsize)
 {
-	register char *d = dst;
-	register const char *s = src;
-	register size_t n = siz;
+	const char *odst = dst;
+	const char *osrc = src;
+	size_t n = dsize;
 	size_t dlen;
 
-	/* Find the end of dst and adjust bytes left but don't go past end */
-	while (n-- != 0 && *d != '\0')
-		d++;
-	dlen = d - dst;
-	n = siz - dlen;
+	/* Find the end of dst and adjust bytes left but don't go past end. */
+	while (n-- != 0 && *dst != '\0')
+		dst++;
+	dlen = dst - odst;
+	n = dsize - dlen;
 
-	if (n == 0)
-		return(dlen + strlen(s));
-	while (*s != '\0') {
-		if (n != 1) {
-			*d++ = *s;
+	if (n-- == 0)
+		return(dlen + strlen(src));
+	while (*src != '\0') {
+		if (n != 0) {
+			*dst++ = *src;
 			n--;
 		}
-		s++;
+		src++;
 	}
-	*d = '\0';
+	*dst = '\0';
 
-	return(dlen + (s - src));	/* count does not include NUL */
+	return(dlen + (src - osrc));	/* count does not include NUL */
 }

+ 19 - 24
lib/strlcpy.c

@@ -1,7 +1,7 @@
-/*	$OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $	*/
+/*	$OpenBSD: strlcpy.c,v 1.16 2019/01/25 00:19:25 millert Exp $	*/
 
 /*
- * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * Copyright (c) 1998, 2015 Todd C. Miller <millert@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -16,40 +16,35 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#if defined(LIBC_SCCS) && !defined(lint)
-static char *rcsid = "$OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $";
-#endif /* LIBC_SCCS and not lint */
-
 #include <sys/types.h>
 #include <string.h>
 
 /*
- * Copy src to string dst of size siz.  At most siz-1 characters
- * will be copied.  Always NUL terminates (unless siz == 0).
- * Returns strlen(src); if retval >= siz, truncation occurred.
+ * Copy string src to buffer dst of size dsize.  At most dsize-1
+ * chars will be copied.  Always NUL terminates (unless dsize == 0).
+ * Returns strlen(src); if retval >= dsize, truncation occurred.
  */
 size_t
-strlcpy(char *dst, const char *src, size_t siz)
+strlcpy(char *dst, const char *src, size_t dsize)
 {
-	register char *d = dst;
-	register const char *s = src;
-	register size_t n = siz;
+	const char *osrc = src;
+	size_t nleft = dsize;
 
-	/* Copy as many bytes as will fit */
-	if (n != 0 && --n != 0) {
-		do {
-			if ((*d++ = *s++) == 0)
+	/* Copy as many bytes as will fit. */
+	if (nleft != 0) {
+		while (--nleft != 0) {
+			if ((*dst++ = *src++) == '\0')
 				break;
-		} while (--n != 0);
+		}
 	}
 
-	/* Not enough room in dst, add NUL and traverse rest of src */
-	if (n == 0) {
-		if (siz != 0)
-			*d = '\0';		/* NUL-terminate dst */
-		while (*s++)
+	/* Not enough room in dst, add NUL and traverse rest of src. */
+	if (nleft == 0) {
+		if (dsize != 0)
+			*dst = '\0';		/* NUL-terminate dst */
+		while (*src++)
 			;
 	}
 
-	return(s - src - 1);	/* count does not include NUL */
+	return(src - osrc - 1);	/* count does not include NUL */
 }

+ 1 - 1
lib/strlcpy.h

@@ -1,6 +1,6 @@
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 6 - 3
libopts/Makefile.in

@@ -91,9 +91,12 @@ host_triplet = @host@
 target_triplet = @target@
 subdir = libopts
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/libopts/m4/libopts.m4 \
-	$(top_srcdir)/libopts/m4/stdnoreturn.m4 \
-	$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
+am__aclocal_m4_deps = $(top_srcdir)/m4/libopts.m4 \
+	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+	$(top_srcdir)/m4/lt~obsolete.m4 \
+	$(top_srcdir)/m4/stdnoreturn.m4 $(top_srcdir)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)

+ 588 - 0
m4/libopts.m4

@@ -0,0 +1,588 @@
+dnl  -*- buffer-read-only: t -*- vi: set ro:
+dnl
+dnl DO NOT EDIT THIS FILE   (libopts.m4)
+dnl
+dnl It has been AutoGen-ed
+dnl From the definitions    libopts.def
+dnl and the template file   conftest.tpl
+dnl
+dnl do always before generated macros:
+dnl
+AC_DEFUN([INVOKE_LIBOPTS_MACROS_FIRST],[
+  AC_HEADER_DIRENT
+
+  # =================
+  # AC_CHECK_HEADERS
+  # =================
+  AC_CHECK_HEADERS([ \
+      sys/mman.h    sys/param.h   sys/poll.h    sys/procset.h \
+      sys/select.h  sys/socket.h  sys/stropts.h sys/time.h \
+      sys/un.h      sys/wait.h    dlfcn.h       errno.h \
+      fcntl.h       libgen.h      libintl.h     memory.h \
+      netinet/in.h  setjmp.h      stdbool.h     sysexits.h \
+      unistd.h      utime.h])
+
+  AC_CHECK_HEADERS([stdarg.h     varargs.h],
+      [lo_have_arg_hdr=true;break],
+      [lo_have_arg_hdr=false])
+
+  AC_CHECK_HEADERS([string.h     strings.h],
+      [lo_have_str_hdr=true;break],
+      [lo_have_str_hdr=false])
+
+  AC_CHECK_HEADERS([limits.h     sys/limits.h  values.h],
+      [lo_have_lim_hdr=true;break],
+      [lo_have_lim_hdr=false])
+
+  AC_CHECK_HEADERS([inttypes.h   stdint.h],
+      [lo_have_typ_hdr=true;break],
+      [lo_have_typ_hdr=false])
+  gl_STDNORETURN_H
+
+  # ----------------------------------------------------------------------
+  # check for various programs used during the build.
+  # On OS/X, "wchar.h" needs "runetype.h" to work properly.
+  # ----------------------------------------------------------------------
+  AC_CHECK_HEADERS([runetype.h wchar.h], [], [],[
+  AC_INCLUDES_DEFAULT
+  #if HAVE_RUNETYPE_H
+  # include <runetype.h>
+  #endif
+  ])
+
+  AC_ARG_ENABLE([nls],
+  AS_HELP_STRING([--disable-nls],[disable nls support in libopts]))
+  AS_IF([test "x$enable_nls" != "xno" && \
+  test "X${ac_cv_header_libintl_h}" = Xyes], [
+  AC_DEFINE([ENABLE_NLS],[1],[nls support in libopts])])
+
+  # --------------------------------------------
+  # Verify certain entries from AC_CHECK_HEADERS
+  # --------------------------------------------
+  [${lo_have_arg_hdr} || \
+    ]AC_MSG_ERROR([you must have stdarg.h or varargs.h on your system])[
+
+  ${lo_have_str_hdr} || \
+    ]AC_MSG_ERROR([you must have string.h or strings.h on your system])[
+
+  ${lo_have_lim_hdr} || \
+    ]AC_MSG_ERROR(
+      [you must have one of limits.h, sys/limits.h or values.h])[
+
+  ${lo_have_typ_hdr} || \
+    ]AC_MSG_ERROR([you must have inttypes.h or stdint.h on your system])[
+
+  for f in sys_types sys_param sys_stat string errno stdlib memory setjmp
+  do eval as_ac_var=\${ac_cv_header_${f}_h}
+     test "X${as_ac_var}" = Xyes || {
+       ]AC_MSG_ERROR([you must have ${f}.h on your system])[
+     }
+  done
+  test "X${ac_cv_header_inttypes_h-no}" = Xyes || \
+    echo '#include <stdint.h>' > inttypes.h]
+
+  # ----------------------------------------------------------------------
+  # Checks for typedefs
+  # ----------------------------------------------------------------------
+  AC_CHECK_TYPES(wchar_t)
+  AC_CHECK_TYPES(wint_t, [], [], [
+    AC_INCLUDES_DEFAULT
+    #if HAVE_RUNETYPE_H
+    # include <runetype.h>
+    #endif
+    #if HAVE_WCHAR_H
+    # include <wchar.h>
+    #endif
+  ])
+  AC_CHECK_TYPES([int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t,
+  intptr_t, uintptr_t, uint_t, pid_t, size_t, ptrdiff_t])
+  AC_CHECK_SIZEOF(char *, 8)
+  AC_CHECK_SIZEOF(int,    4)
+  AC_CHECK_SIZEOF(long,   8)
+  AC_CHECK_SIZEOF(short,  2)
+
+  # ------------
+  # AC_CHECK_LIB
+  # ------------
+  AC_CHECK_LIB(gen, pathfind)
+  AC_CHECK_LIB(intl,gettext)
+  AC_FUNC_VPRINTF
+  AC_FUNC_FORK
+  AC_CHECK_FUNCS([mmap canonicalize_file_name snprintf strdup strchr \
+                 strrchr strsignal fchmod fstat chmod])
+  AC_PROG_SED
+  [while :
+  do
+      POSIX_SHELL=`which bash`
+      test -x "$POSIX_SHELL" && break
+      POSIX_SHELL=`which dash`
+      test -x "$POSIX_SHELL" && break
+      POSIX_SHELL=/usr/xpg4/bin/sh
+      test -x "$POSIX_SHELL" && break
+      POSIX_SHELL=`/bin/sh -c '
+          exec 2>/dev/null
+          if ! true ; then exit 1 ; fi
+          echo /bin/sh'`
+      test -x "$POSIX_SHELL" && break
+      ]AC_MSG_ERROR([cannot locate a working POSIX shell])[
+  done]
+  AC_DEFINE_UNQUOTED([POSIX_SHELL], ["${POSIX_SHELL}"],
+           [define to a working POSIX compliant shell])
+  AC_SUBST([POSIX_SHELL])
+])
+
+dnl
+dnl @synopsis  INVOKE_LIBOPTS_MACROS
+dnl
+dnl  This macro will invoke the AutoConf macros specified in libopts.def
+dnl  that have not been disabled with "omit-invocation".
+dnl
+AC_DEFUN([LIBOPTS_WITH_REGEX_HEADER],[
+  AC_ARG_WITH([regex-header],
+    AS_HELP_STRING([--with-regex-header], [a reg expr header is specified]),
+    [libopts_cv_with_regex_header=${with_regex_header}],
+    AC_CACHE_CHECK([whether a reg expr header is specified], libopts_cv_with_regex_header,
+      libopts_cv_with_regex_header=no)
+  ) # end of AC_ARG_WITH
+  if test "X${libopts_cv_with_regex_header}" != Xno
+  then
+    AC_DEFINE_UNQUOTED([REGEX_HEADER],[<${libopts_cv_with_regex_header}>])
+  else
+    AC_DEFINE([REGEX_HEADER],[<regex.h>],[name of regex header file])
+  fi
+
+]) # end of AC_DEFUN of LIBOPTS_WITH_REGEX_HEADER
+
+
+AC_DEFUN([LIBOPTS_WITHLIB_REGEX],[
+  AC_ARG_WITH([libregex],
+    AS_HELP_STRING([--with-libregex], [libregex installation prefix]),
+    [libopts_cv_with_libregex_root=${with_libregex}],
+    AC_CACHE_CHECK([whether with-libregex was specified], libopts_cv_with_libregex_root,
+      libopts_cv_with_libregex_root=no)
+  ) # end of AC_ARG_WITH libregex
+
+  if test "${with_libregex+set}" = set && \
+     test "X${withval}" = Xno
+  then ## disabled by request
+    libopts_cv_with_libregex_root=no
+    libopts_cv_with_libregex_cflags=no
+    libopts_cv_with_libregex_libs=no
+  else
+
+  AC_ARG_WITH([libregex-cflags],
+    AS_HELP_STRING([--with-libregex-cflags], [libregex compile flags]),
+    [libopts_cv_with_libregex_cflags=${with_libregex_cflags}],
+    AC_CACHE_CHECK([whether with-libregex-cflags was specified], libopts_cv_with_libregex_cflags,
+      libopts_cv_with_libregex_cflags=no)
+  ) # end of AC_ARG_WITH libregex-cflags
+
+  AC_ARG_WITH([libregex-libs],
+    AS_HELP_STRING([--with-libregex-libs], [libregex link command arguments]),
+    [libopts_cv_with_libregex_libs=${with_libregex_libs}],
+    AC_CACHE_CHECK([whether with-libregex-libs was specified], libopts_cv_with_libregex_libs,
+      libopts_cv_with_libregex_libs=no)
+  ) # end of AC_ARG_WITH libregex-libs
+
+  case "X${libopts_cv_with_libregex_cflags}" in
+  Xyes|Xno|X )
+    case "X${libopts_cv_with_libregex_root}" in
+    Xyes|Xno|X ) libopts_cv_with_libregex_cflags=no ;;
+    * ) libopts_cv_with_libregex_cflags=-I${libopts_cv_with_libregex_root}/include ;;
+    esac
+  esac
+  case "X${libopts_cv_with_libregex_libs}" in
+  Xyes|Xno|X )
+    case "X${libopts_cv_with_libregex_root}" in
+    Xyes|Xno|X ) libopts_cv_with_libregex_libs=no ;;
+    * )        libopts_cv_with_libregex_libs="-L${libopts_cv_with_libregex_root}/lib -lregex" ;;
+    esac
+  esac
+  libopts_save_CPPFLAGS="${CPPFLAGS}"
+  libopts_save_LIBS="${LIBS}"
+  case "X${libopts_cv_with_libregex_cflags}" in
+  Xyes|Xno|X )
+    libopts_cv_with_libregex_cflags="" ;;
+  * ) CPPFLAGS="${CPPFLAGS} ${libopts_cv_with_libregex_cflags}" ;;
+  esac
+  case "X${libopts_cv_with_libregex_libs}" in
+  Xyes|Xno|X )
+    libopts_cv_with_libregex_libs="" ;;
+  * )
+    LIBS="${LIBS} ${libopts_cv_with_libregex_libs}" ;;
+  esac
+  LIBREGEX_CFLAGS=""
+  LIBREGEX_LIBS=""
+  AC_MSG_CHECKING([whether libregex functions properly])
+  AC_CACHE_VAL([libopts_cv_with_libregex],[
+  AC_RUN_IFELSE([AC_LANG_SOURCE([@%:@include <stdio.h>
+@%:@include <stdlib.h>
+@%:@include <sys/types.h>
+@%:@include REGEX_HEADER
+static regex_t re;
+void comp_re(char const * pzPat) {
+  int res = regcomp( &re, pzPat, REG_EXTENDED|REG_ICASE|REG_NEWLINE );
+  if (res == 0) return;
+  exit( res ); }
+int main() {
+  regmatch_t m@<:@2@:>@;
+  comp_re( "^.*\@S|@"   );
+  comp_re( "()|no.*" );
+  comp_re( "."       );
+  if (regexec( &re, "X", 2, m, 0 ) != 0)  return 1;
+  if ((m@<:@0@:>@.rm_so != 0) || (m@<:@0@:>@.rm_eo != 1)) {
+    fputs( "error: regex -->.<-- did not match\n", stderr );
+    return 1;
+  }
+  return 0; }])],
+    [libopts_cv_with_libregex=yes], [libopts_cv_with_libregex=no],
+    [libopts_cv_with_libregex=no]) # end of AC_RUN_IFELSE 
+  ]) # end of AC_CACHE_VAL for libopts_cv_with_libregex
+  fi ## disabled by request
+  AC_MSG_RESULT([${libopts_cv_with_libregex}])
+  if test "X${libopts_cv_with_libregex}" != Xno
+  then
+    AC_DEFINE([WITH_LIBREGEX],[1],
+        [Define this if a working libregex can be found])
+  else
+    CPPFLAGS="${libopts_save_CPPFLAGS}"
+    LIBS="${libopts_save_LIBS}"
+    libopts_cv_with_libregex_root=no
+libopts_cv_with_libregex_cflags=no
+libopts_cv_with_libregex_libs=no
+libopts_cv_with_libregex=no
+  fi
+
+]) # end of AC_DEFUN of LIBOPTS_WITHLIB_REGEX
+
+
+AC_DEFUN([LIBOPTS_RUN_PATHFIND],[
+  AC_MSG_CHECKING([whether pathfind(3) works])
+  AC_CACHE_VAL([libopts_cv_run_pathfind],[
+  AC_RUN_IFELSE([AC_LANG_SOURCE([@%:@include <string.h>
+@%:@include <stdlib.h>
+int main (int argc, char ** argv) {
+   char * pz = pathfind( getenv( "PATH" ), "sh", "x" );
+   return (pz == 0) ? 1 : 0;
+}])],
+    [libopts_cv_run_pathfind=yes],[libopts_cv_run_pathfind=no],[libopts_cv_run_pathfind=no]
+  ) # end of RUN_IFELSE
+  ]) # end of AC_CACHE_VAL for libopts_cv_run_pathfind
+  AC_MSG_RESULT([${libopts_cv_run_pathfind}])
+  if test "X${libopts_cv_run_pathfind}" != Xno
+  then
+    AC_DEFINE([HAVE_PATHFIND],[1],
+        [Define this if pathfind(3) works])
+  fi
+
+]) # end of AC_DEFUN of LIBOPTS_RUN_PATHFIND
+
+
+AC_DEFUN([LIBOPTS_TEST_DEV_ZERO],[
+  AC_MSG_CHECKING([whether /dev/zero is readable device])
+  AC_CACHE_VAL([libopts_cv_test_dev_zero],[
+    libopts_cv_test_dev_zero=`exec 2> /dev/null
+dzero=\`ls -lL /dev/zero | egrep ^c......r\`
+test -z "${dzero}" && exit 1
+echo ${dzero}`
+    if test $? -ne 0 || test -z "$libopts_cv_test_dev_zero"
+    then libopts_cv_test_dev_zero=no
+    fi
+  ]) # end of CACHE_VAL of libopts_cv_test_dev_zero
+  AC_MSG_RESULT([${libopts_cv_test_dev_zero}])
+  if test "X${libopts_cv_test_dev_zero}" != Xno
+  then
+    AC_DEFINE([HAVE_DEV_ZERO],[1],
+        [Define this if /dev/zero is readable device])
+  fi
+
+]) # end of AC_DEFUN of LIBOPTS_TEST_DEV_ZERO
+
+
+AC_DEFUN([LIBOPTS_RUN_REALPATH],[
+  AC_MSG_CHECKING([whether we have a functional realpath(3C)])
+  AC_CACHE_VAL([libopts_cv_run_realpath],[
+  AC_RUN_IFELSE([AC_LANG_SOURCE([@%:@include <limits.h>
+@%:@include <stdlib.h>
+int main (int argc, char ** argv) {
+@%:@ifndef PATH_MAX
+choke me!!
+@%:@else
+   char zPath@<:@PATH_MAX+1@:>@;
+@%:@endif
+   char *pz = realpath(argv@<:@0@:>@, zPath);
+   return (pz == zPath) ? 0 : 1;
+}])],
+    [libopts_cv_run_realpath=yes],[libopts_cv_run_realpath=no],[libopts_cv_run_realpath=no]
+  ) # end of RUN_IFELSE
+  ]) # end of AC_CACHE_VAL for libopts_cv_run_realpath
+  AC_MSG_RESULT([${libopts_cv_run_realpath}])
+  if test "X${libopts_cv_run_realpath}" != Xno
+  then
+    AC_DEFINE([HAVE_REALPATH],[1],
+        [Define this if we have a functional realpath(3C)])
+  fi
+
+]) # end of AC_DEFUN of LIBOPTS_RUN_REALPATH
+
+
+AC_DEFUN([LIBOPTS_RUN_STRFTIME],[
+  AC_MSG_CHECKING([whether strftime() works])
+  AC_CACHE_VAL([libopts_cv_run_strftime],[
+  AC_RUN_IFELSE([AC_LANG_SOURCE([@%:@include <time.h>
+@%:@include <string.h>
+char t_buf@<:@ 64 @:>@;
+int main() {
+  static char const z@<:@@:>@ = "Thursday Aug 28 240";
+  struct tm tm;
+  tm.tm_sec   = 36;  /* seconds after the minute @<:@0, 61@:>@  */
+  tm.tm_min   = 44;  /* minutes after the hour @<:@0, 59@:>@ */
+  tm.tm_hour  = 12;  /* hour since midnight @<:@0, 23@:>@ */
+  tm.tm_mday  = 28;  /* day of the month @<:@1, 31@:>@ */
+  tm.tm_mon   =  7;  /* months since January @<:@0, 11@:>@ */
+  tm.tm_year  = 86;  /* years since 1900 */
+  tm.tm_wday  =  4;  /* days since Sunday @<:@0, 6@:>@ */
+  tm.tm_yday  = 239; /* days since January 1 @<:@0, 365@:>@ */
+  tm.tm_isdst =  1;  /* flag for daylight savings time */
+  strftime( t_buf, sizeof( t_buf ), "%A %b %d %j", &tm );
+  return (strcmp( t_buf, z ) != 0); }])],
+    [libopts_cv_run_strftime=yes],[libopts_cv_run_strftime=no],[libopts_cv_run_strftime=no]
+  ) # end of RUN_IFELSE
+  ]) # end of AC_CACHE_VAL for libopts_cv_run_strftime
+  AC_MSG_RESULT([${libopts_cv_run_strftime}])
+  if test "X${libopts_cv_run_strftime}" != Xno
+  then
+    AC_DEFINE([HAVE_STRFTIME],[1],
+        [Define this if strftime() works])
+  fi
+
+]) # end of AC_DEFUN of LIBOPTS_RUN_STRFTIME
+
+
+AC_DEFUN([LIBOPTS_RUN_FOPEN_BINARY],[
+  AC_MSG_CHECKING([whether fopen accepts "b" mode])
+  AC_CACHE_VAL([libopts_cv_run_fopen_binary],[
+  AC_RUN_IFELSE([AC_LANG_SOURCE([@%:@include <stdio.h>
+int main (int argc, char ** argv) {
+FILE * fp = fopen("conftest.@S|@ac_ext", "rb");
+return (fp == NULL) ? 1 : fclose(fp); }])],
+    [libopts_cv_run_fopen_binary=yes],[libopts_cv_run_fopen_binary=no],[libopts_cv_run_fopen_binary=no]
+  ) # end of RUN_IFELSE
+  ]) # end of AC_CACHE_VAL for libopts_cv_run_fopen_binary
+  AC_MSG_RESULT([${libopts_cv_run_fopen_binary}])
+  if test "X${libopts_cv_run_fopen_binary}" != Xno
+  then
+    AC_DEFINE([FOPEN_BINARY_FLAG],"b",
+	[fopen(3) accepts a 'b' in the mode flag])
+  else
+    AC_DEFINE([FOPEN_BINARY_FLAG],"",
+	[fopen(3) accepts a 'b' in the mode flag])
+  fi
+
+]) # end of AC_DEFUN of LIBOPTS_RUN_FOPEN_BINARY
+
+
+AC_DEFUN([LIBOPTS_RUN_FOPEN_TEXT],[
+  AC_MSG_CHECKING([whether fopen accepts "t" mode])
+  AC_CACHE_VAL([libopts_cv_run_fopen_text],[
+  AC_RUN_IFELSE([AC_LANG_SOURCE([@%:@include <stdio.h>
+int main (int argc, char ** argv) {
+FILE * fp = fopen("conftest.@S|@ac_ext", "rt");
+return (fp == NULL) ? 1 : fclose(fp); }])],
+    [libopts_cv_run_fopen_text=yes],[libopts_cv_run_fopen_text=no],[libopts_cv_run_fopen_text=no]
+  ) # end of RUN_IFELSE
+  ]) # end of AC_CACHE_VAL for libopts_cv_run_fopen_text
+  AC_MSG_RESULT([${libopts_cv_run_fopen_text}])
+  if test "X${libopts_cv_run_fopen_text}" != Xno
+  then
+    AC_DEFINE([FOPEN_TEXT_FLAG],"t",
+	[fopen(3) accepts a 't' in the mode flag])
+  else
+    AC_DEFINE([FOPEN_TEXT_FLAG],"",
+	[fopen(3) accepts a 't' in the mode flag])
+  fi
+
+]) # end of AC_DEFUN of LIBOPTS_RUN_FOPEN_TEXT
+
+
+AC_DEFUN([LIBOPTS_DISABLE_OPTIONAL_ARGS],[
+  AC_ARG_ENABLE([optional-args],
+    AS_HELP_STRING([--disable-optional-args], [not wanting optional option args]),
+    [libopts_cv_enable_optional_args=${enable_optional_args}],
+    AC_CACHE_CHECK([whether not wanting optional option args], libopts_cv_enable_optional_args,
+      libopts_cv_enable_optional_args=yes)
+  ) # end of AC_ARG_ENABLE
+  if test "X${libopts_cv_enable_optional_args}" = Xno
+  then
+    AC_DEFINE([NO_OPTIONAL_OPT_ARGS], [1],
+          [Define this if optional arguments are disallowed])
+  fi
+
+]) # end of AC_DEFUN of LIBOPTS_DISABLE_OPTIONAL_ARGS
+
+
+AC_DEFUN([INVOKE_LIBOPTS_MACROS],[
+  AC_REQUIRE([INVOKE_LIBOPTS_MACROS_FIRST])
+  # Check to see if a reg expr header is specified.
+  LIBOPTS_WITH_REGEX_HEADER
+
+  # Check to see if a working libregex can be found.
+  LIBOPTS_WITHLIB_REGEX
+
+  # Check to see if pathfind(3) works.
+  LIBOPTS_RUN_PATHFIND
+
+  # Check to see if /dev/zero is readable device.
+  LIBOPTS_TEST_DEV_ZERO
+
+  # Check to see if we have a functional realpath(3C).
+  LIBOPTS_RUN_REALPATH
+
+  # Check to see if strftime() works.
+  LIBOPTS_RUN_STRFTIME
+
+  # Check to see if fopen accepts "b" mode.
+  LIBOPTS_RUN_FOPEN_BINARY
+
+  # Check to see if fopen accepts "t" mode.
+  LIBOPTS_RUN_FOPEN_TEXT
+
+  # Check to see if not wanting optional option args.
+  LIBOPTS_DISABLE_OPTIONAL_ARGS
+
+]) # end AC_DEFUN of INVOKE_LIBOPTS_MACROS
+
+dnl @synopsis  LIBOPTS_CHECK
+dnl
+dnl If autoopts-config works, add the linking information to LIBS.
+dnl Otherwise, add ``libopts-${ao_rev}'' to SUBDIRS and run all
+dnl the config tests that the library needs.  Invoke the
+dnl "INVOKE_LIBOPTS_MACROS" macro iff we are building libopts.
+dnl
+dnl  This file is part of AutoGen.
+dnl  AutoGen Copyright (C) 1992-2015 by Bruce Korb - all rights reserved
+dnl
+dnl  AutoGen is free software: you can redistribute it and/or modify it
+dnl  under the terms of the GNU General Public License as published by the
+dnl  Free Software Foundation, either version 3 of the License, or
+dnl  (at your option) any later version.
+dnl
+dnl  AutoGen is distributed in the hope that it will be useful, but
+dnl  WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+dnl  See the GNU General Public License for more details.
+dnl
+dnl  You should have received a copy of the GNU General Public License along
+dnl  with this program.  If not, see <http://www.gnu.org/licenses/>.
+dnl
+dnl Default to system libopts
+dnl
+AC_DEFUN([LIBOPTS_CHECK_COMMON],[
+  AC_REQUIRE([INVOKE_LIBOPTS_MACROS_FIRST])
+  [NEED_LIBOPTS_DIR='']
+  m4_pushdef([AO_Libopts_Dir],
+	    [ifelse($1, , [libopts], [$1])])
+  AC_ARG_ENABLE([local-libopts],
+    AS_HELP_STRING([--enable-local-libopts],[Use the supplied libopts tearoff code]),[
+    if test x$enableval = xyes ; then
+       AC_MSG_NOTICE([Using supplied libopts tearoff])
+       LIBOPTS_CFLAGS='-I$(top_srcdir)/AO_Libopts_Dir'
+       NEED_LIBOPTS_DIR=true
+       LIBOPTS_LDADD='$(top_builddir)/AO_Libopts_Dir/libopts.la'
+    fi])
+
+  AC_ARG_ENABLE([libopts-install],
+    AS_HELP_STRING([--enable-libopts-install],[Install libopts with client installation]))
+  AM_CONDITIONAL([INSTALL_LIBOPTS],[test "X${enable_libopts_install}" = Xyes])
+
+  [if test -z "${NEED_LIBOPTS_DIR}" ; then]
+     AC_MSG_CHECKING([whether autoopts-config can be found])
+     AC_ARG_WITH([autoopts-config],
+        AS_HELP_STRING([--with-autoopts-config],[specify the config-info script]),
+        [lo_cv_with_autoopts_config=${with_autoopts_config}],
+        AC_CACHE_CHECK([whether autoopts-config is specified],
+             [lo_cv_with_autoopts_config],
+             [if autoopts-config --help 2>/dev/null 1>&2
+        then lo_cv_with_autoopts_config=autoopts-config
+        elif libopts-config --help 2>/dev/null 1>&2
+        then lo_cv_with_autoopts_config=libopts-config
+        else lo_cv_with_autoopts_config=no ; fi])
+     ) # end of AC_ARG_WITH
+
+     AC_CACHE_VAL([lo_cv_test_autoopts],[
+        if test -z "${lo_cv_with_autoopts_config}" \
+                -o X"${lo_cv_with_autoopts_config}" = Xno
+        then
+           if autoopts-config --help 2>/dev/null 1>&2
+           then lo_cv_with_autoopts_config=autoopts-config
+           elif libopts-config --help 2>/dev/null 1>&2
+           then lo_cv_with_autoopts_config=libopts-config
+           else lo_cv_with_autoopts_config=false ; fi
+        fi
+        lo_cv_test_autoopts=`
+            ${lo_cv_with_autoopts_config} --libs` 2> /dev/null
+        if test $? -ne 0 -o -z "${lo_cv_test_autoopts}"
+        then lo_cv_test_autoopts=no ; fi
+     ]) # end of CACHE_VAL
+     AC_MSG_RESULT([${lo_cv_test_autoopts}])
+
+     [if test "X${lo_cv_test_autoopts}" != Xno
+     then
+        LIBOPTS_LDADD="${lo_cv_test_autoopts}"
+        LIBOPTS_CFLAGS="`${lo_cv_with_autoopts_config} --cflags`"
+     else
+        LIBOPTS_LDADD='$(top_builddir)/]AO_Libopts_Dir[/libopts.la'
+        LIBOPTS_CFLAGS='-I$(top_srcdir)/]AO_Libopts_Dir['
+        NEED_LIBOPTS_DIR=true
+     fi
+  fi # end of if test -z "${NEED_LIBOPTS_DIR}"
+  if test -n "${LIBOPTS_BUILD_BLOCKED}" ; then
+    NEED_LIBOPTS_DIR=''
+  fi]
+  AM_CONDITIONAL([NEED_LIBOPTS], [test -n "${NEED_LIBOPTS_DIR}"])
+  AC_SUBST(LIBOPTS_LDADD)
+  AC_SUBST(LIBOPTS_CFLAGS)
+  AC_SUBST(LIBOPTS_DIR, AO_Libopts_Dir)
+  m4_popdef([AO_Libopts_Dir])
+[# end of AC_DEFUN of LIBOPTS_CHECK_COMMON]
+])
+dnl
+dnl AC_CONFIG_FILES conditionalization requires using AM_COND_IF, however
+dnl AM_COND_IF is new to Automake 1.11.  To use it on new Automake without
+dnl requiring same, a fallback implementation for older Automake is provided.
+dnl Note that disabling of AC_CONFIG_FILES requires Automake 1.11, this code
+dnl is correct only in terms of m4sh generated script.
+dnl
+m4_ifndef([AM_COND_IF],
+  [AC_DEFUN([AM_COND_IF], [
+    if test -z "$$1_TRUE"; then :
+      m4_n([$2])[]dnl
+      m4_ifval([$3],[
+    else
+      $3
+    ])dnl
+    fi[]dnl
+  ])dnl
+])
+dnl
+AC_DEFUN([LIBOPTS_CHECK_NOBUILD], [
+  m4_pushdef([AO_Libopts_Dir],
+	      [ifelse($1, , [libopts], [$1])])
+  LIBOPTS_BUILD_BLOCKED=true
+  LIBOPTS_CHECK_COMMON(AO_Libopts_Dir)
+  m4_popdef([AO_Libopts_Dir])dnl
+# end of AC_DEFUN of LIBOPTS_CHECK_NOBUILD
+])
+dnl
+AC_DEFUN([LIBOPTS_CHECK], [
+  m4_pushdef([AO_Libopts_Dir],
+	      [ifelse($1, , [libopts], [$1])])
+  LIBOPTS_BUILD_BLOCKED=''
+  LIBOPTS_CHECK_COMMON(AO_Libopts_Dir)
+  AM_COND_IF([NEED_LIBOPTS], [
+    INVOKE_LIBOPTS_MACROS
+  ])
+  AC_CONFIG_FILES(AO_Libopts_Dir/Makefile)
+  m4_popdef([AO_Libopts_Dir])dnl
+# end of AC_DEFUN of LIBOPTS_CHECK
+])

+ 41 - 0
m4/stdnoreturn.m4

@@ -0,0 +1,41 @@
+# Check for stdnoreturn.h that conforms to C11.
+
+dnl Copyright 2012-2016 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+# Prepare for substituting <stdnoreturn.h> if it is not supported.
+
+AC_DEFUN([gl_STDNORETURN_H],
+[
+  AC_CACHE_CHECK([for working stdnoreturn.h],
+    [gl_cv_header_working_stdnoreturn_h],
+    [AC_COMPILE_IFELSE(
+       [AC_LANG_PROGRAM(
+          [[#include <stdlib.h>
+            #include <stdnoreturn.h>
+            /* Do not check for 'noreturn' after the return type.
+               C11 allows it, but it's rarely done that way
+               and circa-2012 bleeding-edge GCC rejects it when given
+               -Werror=old-style-declaration.  */
+            noreturn void foo1 (void) { exit (0); }
+            _Noreturn void foo2 (void) { exit (0); }
+            int testit (int argc, char **argv) {
+              if (argc & 1)
+                return 0;
+              (argv[0][0] ? foo1 : foo2) ();
+            }
+          ]])],
+       [gl_cv_header_working_stdnoreturn_h=yes],
+       [gl_cv_header_working_stdnoreturn_h=no])])
+
+  if test $gl_cv_header_working_stdnoreturn_h = yes; then
+    STDNORETURN_H=''
+  else
+    STDNORETURN_H='stdnoreturn.h'
+  fi
+
+  AC_SUBST([STDNORETURN_H])
+  AM_CONDITIONAL([GL_GENERATE_STDNORETURN_H], [test -n "$STDNORETURN_H"])
+])

+ 6 - 3
scripts/Makefile.in

@@ -90,9 +90,12 @@ host_triplet = @host@
 target_triplet = @target@
 subdir = scripts
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/libopts/m4/libopts.m4 \
-	$(top_srcdir)/libopts/m4/stdnoreturn.m4 \
-	$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
+am__aclocal_m4_deps = $(top_srcdir)/m4/libopts.m4 \
+	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+	$(top_srcdir)/m4/lt~obsolete.m4 \
+	$(top_srcdir)/m4/stdnoreturn.m4 $(top_srcdir)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)

+ 6 - 3
src/Makefile.in

@@ -99,9 +99,12 @@ bin_PROGRAMS = tcpreplay$(EXEEXT) tcpprep$(EXEEXT) tcprewrite$(EXEEXT) \
 @COMPILE_TCPLIVEPLAY_TRUE@am__append_4 = tcpliveplay.1
 subdir = src
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/libopts/m4/libopts.m4 \
-	$(top_srcdir)/libopts/m4/stdnoreturn.m4 \
-	$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
+am__aclocal_m4_deps = $(top_srcdir)/m4/libopts.m4 \
+	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+	$(top_srcdir)/m4/lt~obsolete.m4 \
+	$(top_srcdir)/m4/stdnoreturn.m4 $(top_srcdir)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \

+ 1 - 1
src/bridge.c

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/bridge.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/common.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 6 - 3
src/common/Makefile.in

@@ -94,9 +94,12 @@ target_triplet = @target@
 @COMPILE_NETMAP_TRUE@am__append_2 = netmap.c
 subdir = src/common
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/libopts/m4/libopts.m4 \
-	$(top_srcdir)/libopts/m4/stdnoreturn.m4 \
-	$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
+am__aclocal_m4_deps = $(top_srcdir)/m4/libopts.m4 \
+	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+	$(top_srcdir)/m4/lt~obsolete.m4 \
+	$(top_srcdir)/m4/stdnoreturn.m4 $(top_srcdir)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \

+ 1 - 1
src/common/cache.c

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/common/cache.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 5 - 7
src/common/cidr.c

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 
@@ -110,20 +110,18 @@ u_char *
 ip2cidr(const unsigned long ip, const int masklen)
 {
     u_char *network;
-    char mask[3];
+    char mask[32];
 
     network = (u_char *)safe_malloc(20);
 
     strlcpy((char *)network, (char *)get_addr2name4(ip, RESOLVE), 20);
 
     strcat((char *)network, "/");
-    if (masklen < 10) {
-        snprintf(mask, sizeof(mask), "%d", masklen);
+    snprintf(mask, sizeof(mask), "%d", masklen);
+    if (masklen < 10)
         strncat((char *)network, mask, 1);
-    } else {
-        snprintf(mask, sizeof(mask), "%d", masklen);
+    else
         strncat((char *)network, mask, 2);
-    }
 
     return (network);
 }

+ 1 - 1
src/common/cidr.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/common/err.c

@@ -7,7 +7,7 @@
  *
  * Copyright (c) 2001-2010 Aaron Turner.
  *
- * Copyright (c) 2013-2018 Fred Klassen - AppNeta
+ * Copyright (c) 2013-2022 Fred Klassen - AppNeta
  *
  * Copyright (c) 2000 Dug Song <dugsong@monkey.org>
  *

+ 1 - 1
src/common/err.h

@@ -7,7 +7,7 @@
  *
  * Copyright (c) 2001-2010 Aaron Turner.
  *
- * Copyright (c) 2013-2018 Fred Klassen - AppNeta
+ * Copyright (c) 2013-2022 Fred Klassen - AppNeta
  *
  * Copyright (c) 2000 Dug Song <dugsong@monkey.org>
  *

+ 1 - 1
src/common/fakepcap.c

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/common/fakepcap.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/common/fakepcapnav.c

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/common/fakepcapnav.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 35 - 92
src/common/flows.c

@@ -1,6 +1,6 @@
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 
@@ -23,6 +23,10 @@
 #include <string.h>
 #include "../../lib/sll.h"
 
+#define JUNIPER_FLAG_NO_L2          0x02     /* L2 header */
+#define JUNIPER_FLAG_EXT            0x80     /* Juniper extensions present */
+#define JUNIPER_PCAP_MAGIC          "MGC"
+
 /* 5-tuple plus VLAN ID */
 typedef struct flow_entry_data {
     union {
@@ -157,23 +161,25 @@ static inline flow_entry_type_t hash_put_data(flow_hash_table_t *fht, const uint
 flow_entry_type_t flow_decode(flow_hash_table_t *fht, const struct pcap_pkthdr *pkthdr,
         const u_char *pktdata, const int datalink, const int expiry)
 {
+    uint32_t pkt_len = pkthdr->caplen;
+    const u_char *packet = pktdata;
+    uint32_t _U_ vlan_offset;
+    uint32_t _U_ l2offset;
     uint16_t ether_type = 0;
     ipv4_hdr_t *ip_hdr = NULL;
     ipv6_hdr_t *ip6_hdr = NULL;
     tcp_hdr_t *tcp_hdr;
     udp_hdr_t *udp_hdr;
     icmpv4_hdr_t *icmp_hdr;
-    hdlc_hdr_t *hdlc_hdr;
-    sll_hdr_t *sll_hdr;
-    struct tcpr_pppserial_hdr *ppp;
     flow_entry_data_t entry;
-    uint32_t l2_len = 0;
-    int ip_len;
+    uint32_t l2len = 0;
     uint8_t protocol;
     uint32_t hash;
+    int ip_len;
+    int res;
 
     assert(fht);
-    assert(pktdata);
+    assert(packet);
 
     /*
      * extract the 5-tuple and populate the entry data
@@ -181,90 +187,27 @@ flow_entry_type_t flow_decode(flow_hash_table_t *fht, const struct pcap_pkthdr *
 
     memset(&entry, 0, sizeof(entry));
 
-    switch (datalink) {
-    case DLT_LINUX_SLL:
-        l2_len = 16;
-        if (pkthdr->caplen < l2_len)
-            return FLOW_ENTRY_INVALID;
-
-        sll_hdr = (sll_hdr_t *)pktdata;
-        ether_type = sll_hdr->sll_protocol;
-        break;
-
-    case DLT_PPP_SERIAL:
-        l2_len = 4;
-        if (pkthdr->caplen < l2_len)
-            return FLOW_ENTRY_INVALID;
-
-        ppp = (struct tcpr_pppserial_hdr *)pktdata;
-        if (ntohs(ppp->protocol) == 0x0021)
-            ether_type = htons(ETHERTYPE_IP);
-        else
-            ether_type = ppp->protocol;
-        break;
-
-    case DLT_C_HDLC:
-        l2_len = 4;
-        if (pkthdr->caplen < l2_len)
-            return FLOW_ENTRY_INVALID;
-
-        hdlc_hdr = (hdlc_hdr_t *)pktdata;
-        ether_type = hdlc_hdr->protocol;
-        break;
-
-    case DLT_RAW:
-        if ((pktdata[0] >> 4) == 4)
-            ether_type = ETHERTYPE_IP;
-        else if ((pktdata[0] >> 4) == 6)
-            ether_type = ETHERTYPE_IP6;
-        break;
-
-    case DLT_JUNIPER_ETHER:
-        if (pkthdr->caplen < 5)
-            return FLOW_ENTRY_INVALID;
-
-        if (memcmp(pktdata, "MGC", 3))
-            warnx("No Magic Number found: %s (0x%x)",
-                 pcap_datalink_val_to_description(datalink), datalink);
-
-        if ((pktdata[3] & 0x80) == 0x80) {
-            l2_len = ntohs(*((uint16_t*)&pktdata[4]));
-            l2_len += 6;
-        } else {
-            l2_len = 4; /* no header extensions */
-        }
+    res = get_l2len_protocol(packet,
+                             pkt_len,
+                             datalink,
+                             &ether_type,
+                             &l2len,
+                             &l2offset,
+                             &vlan_offset);
 
-        /* fallthrough */
-    case DLT_EN10MB:
-        /* l2_len will be zero if we did not fall through */
-        if (pkthdr->caplen < l2_len + sizeof(eth_hdr_t))
-            return FLOW_ENTRY_INVALID;
-
-        ether_type = ntohs(((eth_hdr_t*)(pktdata + l2_len))->ether_type);
-        l2_len += sizeof(eth_hdr_t);
-
-        while (ether_type == ETHERTYPE_VLAN) {
-            if (pkthdr->caplen < l2_len + sizeof(vlan_hdr_t))
-                    return FLOW_ENTRY_INVALID;
-
-            vlan_hdr_t *vlan_hdr = (vlan_hdr_t *)(pktdata + l2_len);
-            entry.vlan = vlan_hdr->vlan_tci & htons(0xfff);
-            ether_type = ntohs(vlan_hdr->vlan_tpid);
-            l2_len += 4;
-        }
-        break;
-
-    default:
+    if (res == -1) {
         warnx("Unable to process unsupported DLT type: %s (0x%x)",
-             pcap_datalink_val_to_description(datalink), datalink);
+              pcap_datalink_val_to_description(datalink), datalink);
         return FLOW_ENTRY_INVALID;
     }
 
+    assert(l2len > 0);
+
     if (ether_type == ETHERTYPE_IP) {
-        if (pkthdr->caplen < l2_len + sizeof(ipv4_hdr_t))
+        if (pkt_len < l2len + sizeof(ipv4_hdr_t))
                 return FLOW_ENTRY_INVALID;
 
-        ip_hdr = (ipv4_hdr_t *)(pktdata + l2_len);
+        ip_hdr = (ipv4_hdr_t *)(packet + l2len);
 
         if (ip_hdr->ip_v != 4)
             return FLOW_ENTRY_NON_IP;
@@ -274,13 +217,13 @@ flow_entry_type_t flow_decode(flow_hash_table_t *fht, const struct pcap_pkthdr *
         entry.src_ip.in = ip_hdr->ip_src;
         entry.dst_ip.in = ip_hdr->ip_dst;
     } else if (ether_type == ETHERTYPE_IP6) {
-        if (pkthdr->caplen < l2_len + sizeof(ipv6_hdr_t))
+        if (pkt_len < l2len + sizeof(ipv6_hdr_t))
                 return FLOW_ENTRY_INVALID;
 
-        if ((pktdata[0] >> 4) != 6)
+        if ((packet[0] >> 4) != 6)
             return FLOW_ENTRY_NON_IP;
 
-        ip6_hdr = (ipv6_hdr_t *)(pktdata + l2_len);
+        ip6_hdr = (ipv6_hdr_t *)(packet + l2len);
         ip_len = sizeof(*ip6_hdr);
         protocol = ip6_hdr->ip_nh;
 
@@ -299,26 +242,26 @@ flow_entry_type_t flow_decode(flow_hash_table_t *fht, const struct pcap_pkthdr *
 
     switch (protocol) {
     case IPPROTO_UDP:
-        if (pkthdr->caplen < (l2_len + ip_len + sizeof(udp_hdr_t)))
+        if (pkt_len < (l2len + ip_len + sizeof(udp_hdr_t)))
             return FLOW_ENTRY_INVALID;
-        udp_hdr = (udp_hdr_t*)(pktdata + ip_len + l2_len);
+        udp_hdr = (udp_hdr_t*)(packet + ip_len + l2len);
         entry.src_port = udp_hdr->uh_sport;
         entry.dst_port = udp_hdr->uh_dport;
         break;
 
     case IPPROTO_TCP:
-        if (pkthdr->caplen < (l2_len + ip_len + sizeof(tcp_hdr_t)))
+        if (pkt_len < (l2len + ip_len + sizeof(tcp_hdr_t)))
             return FLOW_ENTRY_INVALID;
-        tcp_hdr = (tcp_hdr_t*)(pktdata + ip_len + l2_len);
+        tcp_hdr = (tcp_hdr_t*)(packet + ip_len + l2len);
         entry.src_port = tcp_hdr->th_sport;
         entry.dst_port = tcp_hdr->th_dport;
         break;
 
     case IPPROTO_ICMP:
     case IPPROTO_ICMPV6:
-        if (pkthdr->caplen < (l2_len + ip_len + sizeof(icmpv4_hdr_t)))
+        if (pkt_len < (l2len + ip_len + sizeof(icmpv4_hdr_t)))
             return FLOW_ENTRY_INVALID;
-        icmp_hdr = (icmpv4_hdr_t*)(pktdata + ip_len + l2_len);
+        icmp_hdr = (icmpv4_hdr_t*)(packet + ip_len + l2len);
         entry.src_port = icmp_hdr->icmp_type;
         entry.dst_port = icmp_hdr->icmp_code;
         break;

+ 1 - 1
src/common/flows.h

@@ -1,6 +1,6 @@
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 348 - 212
src/common/get.c

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 
@@ -71,111 +71,318 @@ get_pcap_version(void)
 #endif
 }
 
+/*
+ * Advance L2 protocol and L2 length past any MPLS labels.
+ * e.g. https://www.cloudshark.org/captures/20f210391b21
+ *
+ * If EoMPLS is detected, also advance L2 offset to point to the
+ * encapsulated L2.
+ * e.g. https://www.cloudshark.org/captures/b15412060b3d
+ *
+ * pktdata:       pointer to the raw packet
+ * datalen:       number of bytes captured in the packet
+ * next_protocol: reference to the next L2 protocol to be examined and possibly updated
+ * l2len:         reference to the length of the L2 header discovered so far
+ * l2offset:      reference to the offset to the start of the L2 header - typically 0
+ *
+ * return 0 on success, -1 on failure
+ */
+int parse_mpls(const u_char *pktdata,
+               const uint32_t datalen,
+               uint16_t *next_protocol,
+               uint32_t *l2len,
+               uint32_t *l2offset)
+{
+    struct tcpr_mpls_label *mpls_label;
+    int len_remaining = (int)datalen;
+    u_char first_nibble;
+    eth_hdr_t *eth_hdr;
+    bool bos = false;
+    uint32_t label;
+    int len;
+
+    assert(next_protocol);
+    assert(l2len);
+    assert(l2offset);
+
+    len = (int)*l2len;
+
+    /* move over MPLS labels until we get to the last one */
+    while (!bos) {
+        if (len_remaining < (int)sizeof(*mpls_label))
+            return -1;
+
+        mpls_label = (struct tcpr_mpls_label*)(pktdata + len);
+        len += sizeof(*mpls_label);
+        len_remaining -= sizeof(*mpls_label);
+        bos = (ntohl(mpls_label->entry) & MPLS_LS_S_MASK) != 0;
+        label = ntohl(mpls_label->entry) >> MPLS_LS_LABEL_SHIFT;
+        if (label == MPLS_LABEL_GACH) {
+            /* Generic Associated Channel Header */
+            warn("GACH MPLS label not supported at this time");
+            return -1;
+        }
+    }
 
+    if (len_remaining < 4)
+        return -1;
 
-/**
- * returns the L2 protocol (IP, ARP, etc)
- * or 0 for error
+    first_nibble = *((u_char *)(mpls_label + 1)) >> 4;
+    switch(first_nibble) {
+    case 4:
+        *next_protocol = ETHERTYPE_IP;
+        break;
+    case 6:
+        *next_protocol = ETHERTYPE_IP6;
+        break;
+    case 0:
+        /* EoMPLS - jump over PW Ethernet Control Word and handle
+         * inner Ethernet header
+         */
+        len += 4;
+        len_remaining -= 4;
+        if (len_remaining < (int)sizeof(*eth_hdr))
+            return -1;
+
+        *l2offset = len;
+        eth_hdr = (eth_hdr_t*)(pktdata + len);
+        len += sizeof(*eth_hdr);
+        *next_protocol = ntohs(eth_hdr->ether_type);
+        break;
+    default:
+        /* suspect Generic Associated Channel Header */
+        return -1;
+    }
+
+    *l2len = (uint32_t)len;
+    return 0;
+}
+
+/*
+ * Advance L2 protocol and L2 length past any VLAN tags.
+ * e.g. https://www.cloudshark.org/captures/e4fa464563d2
+ *
+ * pktdata:       pointer to the raw packet
+ * datalen:       number of bytes captured in the packet
+ * next_protocol: reference to the next L2 protocol to be examined and possibly updated
+ * l2len:         reference to the length of the L2 header discovered so far
+ *
+ * return 0 on success, -1 on failure
+ */
+int parse_vlan(const u_char *pktdata,
+               const uint32_t datalen,
+               uint16_t *next_protocol,
+               uint32_t *l2len)
+{
+    vlan_hdr_t *vlan_hdr;
+    if ((size_t)datalen < *l2len + sizeof(*vlan_hdr))
+        return -1;
+
+    vlan_hdr = (vlan_hdr_t*)(pktdata + *l2len);
+    *next_protocol = ntohs(vlan_hdr->vlan_tpid);
+    *l2len += sizeof(vlan_hdr_t);
+
+    return 0;
+}
+
+/*
+ * Loop through all non-protocol L2 headers while updating key variables
+ *
+ * pktdata:       pointer to the raw packet
+ * datalen:       number of bytes captured in the packet
+ * next_protocol: reference to the next L2 protocol to be examined and possibly updated
+ * l2len:         reference to the length of the L2 header discovered so far
+ * l2offset:      reference to the offset to the start of the L2 header - typically 0
+ * vlan_offset: reference to the offset to the start of the VLAN headers, if any
+ *
+ * return 0 on success, -1 on failure
+ */
+static int parse_metadata(const u_char *pktdata,
+                          const uint32_t datalen,
+                          uint16_t *next_protocol,
+                          uint32_t *l2len,
+                          uint32_t *l2offset,
+                          uint32_t *vlan_offset)
+{
+    bool done = false;
+    int res = 0;
+    while (!done && res == 0) {
+        switch (*next_protocol) {
+        case ETHERTYPE_VLAN:
+        case ETHERTYPE_Q_IN_Q:
+        case ETHERTYPE_8021QINQ:
+            if (*vlan_offset == 0)
+                *vlan_offset = *l2len;
+
+            res = parse_vlan(pktdata, datalen, next_protocol, l2len);
+            break;
+        case ETHERTYPE_MPLS:
+        case ETHERTYPE_MPLS_MULTI:
+            res = parse_mpls(pktdata, datalen, next_protocol, l2len, l2offset);
+            break;
+        default:
+            done = true;
+        }
+    }
+
+    return res;
+}
+
+/*
+ * Parse raw packet and get the L3 protocol and L2 length. In cases where the
+ * L2 header is not at the beginning of the packet
+ * (e.g. DLT_JUNIPER_ETHER or EoMPLS), report the offset to the start of the
+ * L2 header
+ *
+ * pktdata:     pointer to the raw packet
+ * datalen:     number of bytes captured in the packet
+ * datalink:    data link type of the packet
+ * protocol:    reference to the L3 protocol as discovered in the L2 header
+ * l2len:       reference to the total length of the L2 header
+ * l2offset:    reference to the offset to the start of the L2 header (typically 0)
+ * vlan_offset: reference to the offset to the start of the VLAN headers, if any
+ *
+ * return 0 on success, -1 on failure
  */
-uint16_t
-get_l2protocol(const u_char *pktdata, const uint32_t datalen, const int datalink)
+int get_l2len_protocol(const u_char *pktdata,
+                       const uint32_t datalen,
+                       const int datalink,
+                       uint16_t *protocol,
+                       uint32_t *l2len,
+                       uint32_t *l2offset,
+                       uint32_t *vlan_offset)
 {
-    uint16_t eth_hdr_offset = 0;
+    assert(protocol);
+    assert(l2len);
+    assert(l2offset);
+    assert(vlan_offset);
 
     if (!pktdata || !datalen) {
-        errx(-1, "invalid l2 parameters: pktdata=0x%p len=%d",
-                pktdata, datalen);
-        return 0;
+        errx(-1, "get_l2len_protocol: invalid L2 parameters: pktdata=0x%p len=%d",
+             pktdata,
+             datalen);
+        return -1;
     }
 
+    *protocol = 0;
+    *l2len = 0;
+    *l2offset = 0;
+    *vlan_offset = 0;
+
     switch (datalink) {
     case DLT_RAW:
-        if (datalen < 1)
-            return 0;
+        if (datalen == 0)
+            return -1;
+
         if ((pktdata[0] >> 4) == 4)
-            return ETHERTYPE_IP;
+            *protocol = ETHERTYPE_IP;
         else if ((pktdata[0] >> 4) == 6)
-            return ETHERTYPE_IP6;
+            *protocol = ETHERTYPE_IP6;
         break;
-
     case DLT_JUNIPER_ETHER:
         if (datalen < 4)
-            return 0;
+            return -1;
 
         if (memcmp(pktdata, JUNIPER_PCAP_MAGIC, 3)) {
             warnx("No Magic Number found during protocol lookup: %s (0x%x)",
-                 pcap_datalink_val_to_description(datalink), datalink);
-            return 0;
+                  pcap_datalink_val_to_description(datalink),
+                  datalink);
+            return -1;
         }
 
         if ((pktdata[3] & JUNIPER_FLAG_EXT) == JUNIPER_FLAG_EXT) {
             if (datalen < 6)
-                return 0;  /* datalen too short */
+                return -1;
 
-            eth_hdr_offset = ntohs(*((uint16_t*)&pktdata[4]));
-            eth_hdr_offset += 6; /* MGC + flags + ext_total_len */
+            *l2offset = ntohs(*((uint16_t*)&pktdata[4]));
+            *l2offset += 6; /* MGC + flags + ext_total_len */
         } else {
-            eth_hdr_offset = 4; /* MGC + flags (no header extensions) */
+            *l2offset = 4; /* MGC + flags (no header extensions) */
         }
+
         if ((pktdata[3] & JUNIPER_FLAG_NO_L2) == JUNIPER_FLAG_NO_L2) {
-             /* no L2 header present - eth_hdr_offset is actually IP offset */
-            uint32_t ip_hdr_offset = eth_hdr_offset;
+            /* no L2 header present - *l2offset is actually IP offset */
+            uint32_t ip_hdr_offset = *l2offset;
             if (datalen < ip_hdr_offset + 1)
-                return 0;
+                return -1;
+
             if ((pktdata[ip_hdr_offset] >> 4) == 4)
-                return ETHERTYPE_IP;
+                *protocol = ETHERTYPE_IP;
             else if ((pktdata[ip_hdr_offset] >> 4) == 6)
-                return ETHERTYPE_IP6;
-            else
-                return 0;
-        }
-        /* fall through */
-    case DLT_EN10MB:
-        if ((size_t)datalen >= (sizeof(eth_hdr_t) + eth_hdr_offset)) {
-            eth_hdr_t *eth_hdr = (eth_hdr_t *)(pktdata + eth_hdr_offset);
-            uint16_t ether_type = ntohs(eth_hdr->ether_type);
-            uint16_t l2_len = sizeof(*eth_hdr) + eth_hdr_offset;
-            while (ether_type == ETHERTYPE_VLAN) {
-                if (datalen < l2_len + sizeof(vlan_hdr_t))
-                     return 0;
-
-                 vlan_hdr_t *vlan_hdr = (vlan_hdr_t*)(pktdata + l2_len);
-                 ether_type = ntohs(vlan_hdr->vlan_tpid);
-                 l2_len += sizeof(vlan_hdr_t);
-            }
+                *protocol = ETHERTYPE_IP6;
 
-            return ether_type; /* yes, return it in host byte order */
+            return 0;
         }
-        break;
 
-    case DLT_PPP_SERIAL:
-        if ((size_t)datalen >= sizeof(struct tcpr_pppserial_hdr)) {
-            struct tcpr_pppserial_hdr *ppp = (struct tcpr_pppserial_hdr *)pktdata;
-            if (ntohs(ppp->protocol) == 0x0021)
-                return htons(ETHERTYPE_IP);
+        /* fall through */
+    case DLT_EN10MB:
+    {
+        eth_hdr_t *eth_hdr = (eth_hdr_t*)(pktdata + *l2offset);
+        uint32_t l2_net_off = sizeof(*eth_hdr) + *l2offset;
+        uint16_t ether_type = ntohs(eth_hdr->ether_type);
+
+        if (datalen <= l2_net_off)
+            return -1;
+
+        if (parse_metadata(pktdata,
+                           datalen,
+                           &ether_type,
+                           &l2_net_off,
+                           l2offset,
+                           vlan_offset))
+            return -1;
+
+        if (datalen < l2_net_off)
+            return -1;
+
+        *l2len = l2_net_off;
+        if (ether_type > 1500) {
+            /* Ethernet II frame - return in host order */
+            *protocol = ether_type;
+        } else {
+            /* 803.3 frame */
+            if ((pktdata[l2_net_off] >> 4) == 4)
+                *protocol = ETHERTYPE_IP;
+            else if ((pktdata[l2_net_off] >> 4) == 6)
+                *protocol = ETHERTYPE_IP6;
             else
-                return ppp->protocol;
+                /* unsupported 802.3 protocol */
+                return -1;
         }
         break;
+    }
+    case DLT_PPP_SERIAL:
+        if ((size_t)datalen < sizeof(struct tcpr_pppserial_hdr))
+            return -1;
+
+        struct tcpr_pppserial_hdr *ppp = (struct tcpr_pppserial_hdr*)pktdata;
+        *l2len = sizeof(*ppp);
+        if (ntohs(ppp->protocol) == 0x0021)
+            *protocol = ETHERTYPE_IP;
+        else
+            *protocol = ntohs(ppp->protocol);
 
-    case DLT_C_HDLC:
-        if ((size_t)datalen >= sizeof(hdlc_hdr_t)) {
-            hdlc_hdr_t *hdlc_hdr = (hdlc_hdr_t *)pktdata;
-            return hdlc_hdr->protocol;
-        }
         break;
+    case DLT_C_HDLC:
+        if (datalen < CISCO_HDLC_LEN)
+            return -1;
 
-    case DLT_LINUX_SLL:
-        if ((size_t)datalen >= sizeof(sll_hdr_t)) {
-            sll_hdr_t *sll_hdr = (sll_hdr_t *)pktdata;
-            return sll_hdr->sll_protocol;
-        }
+        hdlc_hdr_t *hdlc_hdr = (hdlc_hdr_t*)pktdata;
+        *l2len = sizeof(*hdlc_hdr);
+        *protocol = ntohs(hdlc_hdr->protocol);
         break;
+    case DLT_LINUX_SLL:
+        if (datalen < SLL_HDR_LEN)
+            return -1;
 
+        sll_hdr_t *sll_hdr = (sll_hdr_t*)pktdata;
+        *l2len = sizeof(*sll_hdr);
+        *protocol = ntohs(sll_hdr->sll_protocol);
+        break;
     default:
-        errx(-1, "Unable to process unsupported DLT type: %s (0x%x)", 
-             pcap_datalink_val_to_description(datalink), datalink);
-
+        errx(-1, "Unable to process unsupported DLT type: %s (0x%x)",
+             pcap_datalink_val_to_description(datalink),
+             datalink);
     }
 
     return 0;
@@ -187,93 +394,23 @@ get_l2protocol(const u_char *pktdata, const uint32_t datalen, const int datalink
 int
 get_l2len(const u_char *pktdata, const int datalen, const int datalink)
 {
-    int l2_len = 0;
-
-    assert(pktdata);
-    assert(datalen);
-
-    switch (datalink) {
-    case DLT_RAW:
-        /* pktdata IS the ip header! */
-        break;
-
-    case DLT_JUNIPER_ETHER:
-        if (datalen < 4) {
-            l2_len = -1;
-            break;
-        }
-
-        if (memcmp(pktdata, JUNIPER_PCAP_MAGIC, 3)) {
-            warnx("No Magic Number found during L2 lookup: %s (0x%x)",
-                  pcap_datalink_val_to_description(datalink), datalink);
-            l2_len = -1;
-            break;
-        }
-
-        if ((pktdata[3] & JUNIPER_FLAG_NO_L2) == JUNIPER_FLAG_NO_L2) {
-            /* no L2 header present */
-            l2_len = 0;
-            break;
-        }
-
-        if ((pktdata[3] & JUNIPER_FLAG_EXT) == JUNIPER_FLAG_EXT) {
-            if (datalen < 6) {
-                /* datalen too short */
-                l2_len = -1;
-                break;
-            }
-            l2_len = ntohs(*((uint16_t*)&pktdata[4]));
-            l2_len += 6;        /* MGC + flags + ext_total_len */
-        } else {
-            l2_len = 4;         /* MGC + flags */
-        }
-        /* fall through */
-    case DLT_EN10MB:
-        if ((size_t)datalen >= sizeof(eth_hdr_t) + l2_len) {
-            uint16_t ether_type = ntohs(((eth_hdr_t*)(pktdata + l2_len))->ether_type);
-
-            l2_len += sizeof(eth_hdr_t);
-            while (ether_type == ETHERTYPE_VLAN) {
-                if ((size_t)datalen < sizeof(vlan_hdr_t) + l2_len) {
-                    l2_len = -1;
-                    break;
-                }
-                vlan_hdr_t *vlan_hdr = (vlan_hdr_t *)(pktdata + l2_len);
-                ether_type = ntohs(vlan_hdr->vlan_tpid);
-                l2_len += 4;
-            }
-        }
-
-        if (datalen < l2_len)
-            l2_len = -1;
-
-        break;
-
-    case DLT_PPP_SERIAL:
-        if (datalen >= 4) {
-            l2_len = 4;
-        }
-        break;
-
-    case DLT_C_HDLC:
-        if (datalen >= CISCO_HDLC_LEN) {
-            l2_len = CISCO_HDLC_LEN;
-        }
-        break;
-
-    case DLT_LINUX_SLL:
-        if (datalen >= SLL_HDR_LEN) {
-            l2_len = SLL_HDR_LEN;
-        }
-        break;
-
-    default:
-        errx(-1, "Unable to process unsupported DLT type: %s (0x%x)", 
-             pcap_datalink_val_to_description(datalink), datalink);
-        return -1; /* we shouldn't get here */
-    }
+    uint16_t _U_ protocol;
+    uint32_t _U_ l2offset;
+    uint32_t _U_ vlan_offset;
+    uint32_t l2len = 0;
+
+    int res = get_l2len_protocol(pktdata,
+                                 datalen,
+                                 datalink,
+                                 &protocol,
+                                 &l2len,
+                                 &l2offset,
+                                 &vlan_offset);
+
+    if (res == -1)
+        return 0;
 
-    return l2_len;
+    return l2len;
 }
 
 /**
@@ -289,50 +426,45 @@ get_l2len(const u_char *pktdata, const int datalen, const int datalink)
 const u_char *
 get_ipv4(const u_char *pktdata, int datalen, int datalink, u_char **newbuff)
 {
+    const u_char *packet = pktdata;
     const u_char *ip_hdr = NULL;
-    int l2_len = 0;
+    ssize_t pkt_len = datalen;
+    uint32_t _U_ vlan_offset;
+    uint32_t l2offset;
     uint16_t proto;
+    uint32_t l2len;
+    int res;
 
-    assert(pktdata);
-    assert(datalen);
+    assert(packet);
+    assert(pkt_len);
     assert(*newbuff);
 
-    l2_len = get_l2len(pktdata, datalen, datalink);
+    res = get_l2len_protocol(packet,
+                             pkt_len,
+                             datalink,
+                             &proto,
+                             &l2len,
+                             &l2offset,
+                             &vlan_offset);
 
-    /* sanity... datalen must be > l2_len + IP header len*/
-    if (l2_len < 0 || l2_len + TCPR_IPV4_H > datalen) {
+    /* sanity... pkt_len must be > l2len + IP header len*/
+    if (res == -1 || l2len + TCPR_IPV4_H > pkt_len) {
         dbg(1, "get_ipv4(): Layer 2 len > total packet len, hence no IP header");
         return NULL;
     }
 
-    proto = get_l2protocol(pktdata, datalen, datalink);
-
     if (proto != ETHERTYPE_IP)
         return NULL;
 
-#ifdef FORCE_ALIGN
-    /*
-     * copy layer 3 and up to our temp packet buffer
-     * for now on, we have to edit the packetbuff because
-     * just before we send the packet, we copy the packetbuff 
-     * back onto the pkt.data + l2len buffer
-     * we do all this work to prevent byte alignment issues
-     */
-    if (l2_len % sizeof(long)) {
-        memcpy(*newbuff, (pktdata + l2_len), (datalen - l2_len));
-        ip_hdr = *newbuff;
-    } else {
+    packet += l2offset;
+    l2len -= l2offset;
+    pkt_len -= l2offset;
 
-        /* we don't have to do a memcpy if l2_len lands on a boundary */
-        ip_hdr = (pktdata + l2_len);
-    }
-#else
     /*
      * on non-strict byte align systems, don't need to memcpy(), 
      * just point to l2len bytes into the existing buffer
      */
-    ip_hdr = (pktdata + l2_len);
-#endif
+    ip_hdr = (packet + l2len);
 
     return ip_hdr;
 }
@@ -351,50 +483,45 @@ get_ipv4(const u_char *pktdata, int datalen, int datalink, u_char **newbuff)
 const u_char *
 get_ipv6(const u_char *pktdata, int datalen, int datalink, u_char **newbuff)
 {
+    const u_char *packet = pktdata;
     const u_char *ip6_hdr = NULL;
-    int l2_len = 0;
+    ssize_t pkt_len = datalen;
+    uint32_t _U_ vlan_offset;
+    uint32_t l2offset;
     uint16_t proto;
+    uint32_t l2len;
+    int res;
 
-    assert(pktdata);
-    assert(datalen);
+    assert(packet);
+    assert(pkt_len);
     assert(*newbuff);
 
-    l2_len = get_l2len(pktdata, datalen, datalink);
+    res = get_l2len_protocol(packet,
+                             pkt_len,
+                             datalink,
+                             &proto,
+                             &l2len,
+                             &l2offset,
+                             &vlan_offset);
 
-    /* sanity... datalen must be > l2_len + IP header len*/
-    if (l2_len < 0 || l2_len + TCPR_IPV6_H > datalen) {
+    /* sanity... pkt_len must be > l2len + IP header len*/
+    if (res == -1 || l2len + TCPR_IPV6_H > pkt_len) {
         dbg(1, "get_ipv6(): Layer 2 len > total packet len, hence no IPv6 header");
         return NULL;
     }
 
-    proto = get_l2protocol(pktdata, datalen, datalink);
-
     if (proto != ETHERTYPE_IP6)
         return NULL;
 
-#ifdef FORCE_ALIGN
-    /*
-     * copy layer 3 and up to our temp packet buffer
-     * for now on, we have to edit the packetbuff because
-     * just before we send the packet, we copy the packetbuff
-     * back onto the pkt.data + l2len buffer
-     * we do all this work to prevent byte alignment issues
-     */
-    if (l2_len % sizeof(long)) {
-        memcpy(*newbuff, (pktdata + l2_len), (datalen - l2_len));
-        ip6_hdr = *newbuff;
-    } else {
+    packet += l2offset;
+    l2len -= l2offset;
+    pkt_len -= l2offset;
 
-        /* we don't have to do a memcpy if l2_len lands on a boundary */
-        ip6_hdr = (pktdata + l2_len);
-    }
-#else
     /*
      * on non-strict byte align systems, don't need to memcpy(),
      * just point to l2len bytes into the existing buffer
      */
-    ip6_hdr = (pktdata + l2_len);
-#endif
+    ip6_hdr = (packet + l2len);
 
     return ip6_hdr;
 }
@@ -428,8 +555,9 @@ void *
 get_layer4_v6(const ipv6_hdr_t *ip6_hdr, const int l3len)
 {
     struct tcpr_ipv6_ext_hdr_base *next, *exthdr;
-    uint8_t proto;
+    bool done = false;
     uint32_t maxlen;
+    uint8_t proto;
     int min_len;
 
     assert(ip6_hdr);
@@ -442,14 +570,14 @@ get_layer4_v6(const ipv6_hdr_t *ip6_hdr, const int l3len)
     next = (struct tcpr_ipv6_ext_hdr_base *)((u_char *)ip6_hdr + TCPR_IPV6_H);
     proto = ip6_hdr->ip_nh;
 
-    while (1) {
+    while (!done) {
         dbgx(3, "Processing proto: 0x%hx", (uint16_t)proto);
 
         switch (proto) {
         /* recurse due to v6-in-v6, need to recast next as an IPv6 Header */
         case TCPR_IPV6_NH_IPV6:
             dbg(3, "recursing due to v6-in-v6");
-            return get_layer4_v6((ipv6_hdr_t *)next, l3len - min_len);
+            next = get_layer4_v6((ipv6_hdr_t *)next, l3len - min_len);
             break;
 
         /* loop again */
@@ -460,8 +588,10 @@ get_layer4_v6(const ipv6_hdr_t *ip6_hdr, const int l3len)
             dbgx(3, "Going deeper due to extension header 0x%02X", proto);
             maxlen = l3len - (int)((u_char *)ip6_hdr - (u_char *)next);
             exthdr = get_ipv6_next(next, maxlen);
-            if (exthdr == NULL)
-                return next;
+            if (exthdr == NULL) {
+                done = true;
+                break;
+            }
             proto = exthdr->ip_nh;
             next = exthdr;
             break;
@@ -471,7 +601,8 @@ get_layer4_v6(const ipv6_hdr_t *ip6_hdr, const int l3len)
          */
         case TCPR_IPV6_NH_FRAGMENT:
         case TCPR_IPV6_NH_ESP:
-            return NULL;
+            next = NULL;
+            done = true;
             break;
 
         /*
@@ -481,14 +612,19 @@ get_layer4_v6(const ipv6_hdr_t *ip6_hdr, const int l3len)
             if (proto != ip6_hdr->ip_nh) {
                 dbgx(3, "Returning byte offset of this ext header: %u", 
                         IPV6_EXTLEN_TO_BYTES(next->ip_len));
-                return (void *)((u_char *)next + IPV6_EXTLEN_TO_BYTES(next->ip_len));
+                next =  (void *)((u_char *)next + IPV6_EXTLEN_TO_BYTES(next->ip_len));
             } else {
                 dbgx(3, "%s", "Returning end of IPv6 Header");
-                return next;
             }
-            break;
+
+            done = true;
         } /* switch */
     } /* while */
+
+    if (!next || (u_char*)next > (u_char*)ip6_hdr + l3len)
+        return NULL;
+
+    return next;
 }
 
 

+ 19 - 2
src/common/get.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 
@@ -26,9 +26,26 @@
 #include "common.h"
 
 
+int parse_mpls(const u_char *pktdata,
+               const uint32_t datalen,
+               uint16_t *protocol,
+               uint32_t *l2len,
+               uint32_t *l2offset);
+
+int parse_vlan(const u_char *pktdata,
+               const uint32_t datalen,
+               uint16_t *next_protocol,
+               uint32_t *l2len);
+
 int get_l2len(const u_char *pktdata, const int datalen, const int datalink);
 
-u_int16_t get_l2protocol(const u_char *pktdata, const uint32_t datalen, const int datalink);
+int get_l2len_protocol(const u_char *pktdata,
+                       const uint32_t datalen,
+                       const int datalink,
+                       uint16_t *protocol,
+                       uint32_t *l2len,
+                       uint32_t *l2offset,
+                       uint32_t *vlan_offset);
 
 void *get_layer4_v4(const ipv4_hdr_t *ip_hdr, const int l3len);
 void *get_layer4_v6(const ipv6_hdr_t *ip_hdr, const int l3len);

+ 1 - 1
src/common/git_version.c

@@ -1,4 +1,4 @@
-const char GIT_Version[] = "git:v4.3.4";
+const char GIT_Version[] = "git:v4.4.0";
 const char *git_version(void) {
     return GIT_Version;
 }

+ 1 - 1
src/common/interface.c

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/common/interface.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/common/list.c

@@ -1,6 +1,6 @@
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/common/list.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/common/mac.c

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/common/mac.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/common/pcap_dlt.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/common/sendpacket.c

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/common/sendpacket.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/common/services.c

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/common/services.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/common/tcpdump.c

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/common/tcpdump.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/common/timer.c

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/common/timer.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/common/utils.c

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 3
src/common/utils.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 
@@ -36,8 +36,6 @@ typedef struct {
     struct timeval end_time;
     struct timeval pkt_ts_delta;
     struct timeval last_print;
-    struct timeval first_packet_sent_wall_time;
-    struct timeval first_packet_pcap_timestamp;
     COUNTER flow_non_flow_packets;
     COUNTER flows;
     COUNTER flows_unique;

+ 1 - 1
src/common/xX.c

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/common/xX.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 3 - 3
src/config.h.in

@@ -54,9 +54,6 @@
 /* fopen(3) accepts a 't' in the mode flag */
 #undef FOPEN_TEXT_FLAG
 
-/* Are we strictly aligned? */
-#undef FORCE_ALIGN
-
 /* Force using BPF for sending packet */
 #undef FORCE_INJECT_BPF
 
@@ -160,6 +157,9 @@
 /* Define to 1 if you have the `fstat' function. */
 #undef HAVE_FSTAT
 
+/* Define to 1 if you have the <fts.h> header file. */
+#undef HAVE_FTS_H
+
 /* Define to 1 if you have the `gethostbyname' function. */
 #undef HAVE_GETHOSTBYNAME
 

+ 6 - 3
src/defines.h

@@ -100,8 +100,7 @@ typedef struct tcpr_udp_hdr udp_hdr_t;
 typedef struct tcpr_icmpv4_hdr icmpv4_hdr_t;
 typedef struct tcpr_icmpv6_hdr icmpv6_hdr_t;
 typedef struct tcpr_ethernet_hdr eth_hdr_t;
-typedef struct tcpr_802_1q_tag vlan_hdr_t;
-typedef struct tcpr_802_1q_hdr vlan_pkt_hdr_t;
+typedef struct tcpr_802_1q_hdr vlan_hdr_t;
 typedef struct sll_header sll_hdr_t;
 typedef struct tcpr_arp_hdr arp_hdr_t;
 typedef struct tcpr_dnsv4_hdr dnsv4_hdr_t;
@@ -211,7 +210,11 @@ typedef enum tcpprep_mode_e {
     CLIENT_MODE     /* second pass through in client (router) mode */
 } tcpprep_mode_t;
 
-#define BROADCAST_MAC "\xFF\xFF\xFF\xFF\xFF\xFF"
+#define BROADCAST_MAC           "\xff\xff\xff\xff\xff\xff"
+#define IPV4_MULTICAST_MAC      "\x01\x00\x5e\x00\x00\x00"
+#define IPV6_MULTICAST_MAC      "\x33\x33\x00\x00\x00\x00"
+#define IPV4_VRRP               "\x00\x00\x50\x00\x01\x00"
+#define IPV6_VRRP               "\x00\x00\x50\x00\x02\x00"
 
 /* MAC macros for printf */
 #define MAC_FORMAT "%02X:%02X:%02X:%02X:%02X:%02X"

+ 6 - 3
src/defines.h.in

@@ -100,8 +100,7 @@ typedef struct tcpr_udp_hdr udp_hdr_t;
 typedef struct tcpr_icmpv4_hdr icmpv4_hdr_t;
 typedef struct tcpr_icmpv6_hdr icmpv6_hdr_t;
 typedef struct tcpr_ethernet_hdr eth_hdr_t;
-typedef struct tcpr_802_1q_tag vlan_hdr_t;
-typedef struct tcpr_802_1q_hdr vlan_pkt_hdr_t;
+typedef struct tcpr_802_1q_hdr vlan_hdr_t;
 typedef struct sll_header sll_hdr_t;
 typedef struct tcpr_arp_hdr arp_hdr_t;
 typedef struct tcpr_dnsv4_hdr dnsv4_hdr_t;
@@ -211,7 +210,11 @@ typedef enum tcpprep_mode_e {
     CLIENT_MODE     /* second pass through in client (router) mode */
 } tcpprep_mode_t;
 
-#define BROADCAST_MAC "\xFF\xFF\xFF\xFF\xFF\xFF"
+#define BROADCAST_MAC           "\xff\xff\xff\xff\xff\xff"
+#define IPV4_MULTICAST_MAC      "\x01\x00\x5e\x00\x00\x00"
+#define IPV6_MULTICAST_MAC      "\x33\x33\x00\x00\x00\x00"
+#define IPV4_VRRP               "\x00\x00\x50\x00\x01\x00"
+#define IPV6_VRRP               "\x00\x00\x50\x00\x02\x00"
 
 /* MAC macros for printf */
 #define MAC_FORMAT "%02X:%02X:%02X:%02X:%02X:%02X"

+ 6 - 3
src/fragroute/Makefile.in

@@ -92,9 +92,12 @@ host_triplet = @host@
 target_triplet = @target@
 subdir = src/fragroute
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/libopts/m4/libopts.m4 \
-	$(top_srcdir)/libopts/m4/stdnoreturn.m4 \
-	$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
+am__aclocal_m4_deps = $(top_srcdir)/m4/libopts.m4 \
+	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+	$(top_srcdir)/m4/lt~obsolete.m4 \
+	$(top_srcdir)/m4/stdnoreturn.m4 $(top_srcdir)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \

+ 1 - 1
src/fragroute/fragroute.c

@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2001 Dug Song <dugsong@monkey.org>
  * Copyright (c) 2007-2008 Aaron Turner.
- * Copyright (c) 2013-2018 Fred Klassen - AppNeta
+ * Copyright (c) 2013-2022 Fred Klassen - AppNeta
  * $Id$
  */
 

+ 1 - 1
src/fragroute/fragroute.h

@@ -3,7 +3,7 @@
 /*
  *   Copyright (c) 2001 Dug Song <dugsong@monkey.org>
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/replay.c

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/replay.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 37 - 37
src/send_packets.c

@@ -3,7 +3,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 
@@ -90,36 +90,48 @@ static inline int
 fast_edit_packet(struct pcap_pkthdr *pkthdr, u_char **pktdata,
         COUNTER iteration, bool cached, int datalink)
 {
-    uint16_t ether_type;
+    uint32_t pkt_len = pkthdr->caplen;
+    u_char *packet = *pktdata;
     ipv4_hdr_t *ip_hdr = NULL;
     ipv6_hdr_t *ip6_hdr = NULL;
     uint32_t src_ip, dst_ip;
     uint32_t src_ip_orig, dst_ip_orig;
-    int l2_len;
-    u_char *packet = *pktdata;
+    uint32_t _U_ vlan_offset;
+    uint16_t ether_type;
+    uint32_t l2offset;
+    uint32_t l2len;
+    int res;
 
-    l2_len = get_l2len(packet, pkthdr->caplen, datalink);
-    if (l2_len < 0)
-        return -1;
+    res = get_l2len_protocol(packet,
+                             pkt_len,
+                             datalink,
+                             &ether_type,
+                             &l2len,
+                             &l2offset,
+                             &vlan_offset);
+
+    if (res < 0)
+        return res;
+
+    assert(l2len > 0);
 
-    ether_type = get_l2protocol(packet, pkthdr->caplen, datalink);
     switch (ether_type) {
     case ETHERTYPE_IP:
-        if (pkthdr->caplen < (bpf_u_int32)(l2_len + sizeof(ipv4_hdr_t))) {
+        if (pkt_len < (bpf_u_int32)(l2len + sizeof(ipv4_hdr_t))) {
             dbgx(1, "IP packet too short for Unique IP feature: %u", pkthdr->caplen);
             return -1;
         }
-        ip_hdr = (ipv4_hdr_t *)(packet + l2_len);
+        ip_hdr = (ipv4_hdr_t *)(packet + l2len);
         src_ip_orig = src_ip = ntohl(ip_hdr->ip_src.s_addr);
         dst_ip_orig = dst_ip = ntohl(ip_hdr->ip_dst.s_addr);
         break;
 
     case ETHERTYPE_IP6:
-        if (pkthdr->caplen < (bpf_u_int32)(l2_len + sizeof(ipv6_hdr_t))) {
+        if (pkt_len < (bpf_u_int32)(l2len + sizeof(ipv6_hdr_t))) {
             dbgx(1, "IP6 packet too short for Unique IP feature: %u", pkthdr->caplen);
             return -1;
         }
-        ip6_hdr = (ipv6_hdr_t *)(packet + l2_len);
+        ip6_hdr = (ipv6_hdr_t *)(packet + l2len);
         src_ip_orig = src_ip = ntohl(ip6_hdr->ip_src.__u6_addr.__u6_addr32[3]);
         dst_ip_orig = dst_ip = ntohl(ip6_hdr->ip_dst.__u6_addr.__u6_addr32[3]);
         break;
@@ -339,7 +351,6 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx)
 
     ctx->skip_packets = 0;
     timerclear(&last_pkt_ts);
-    timerclear(&stats->first_packet_sent_wall_time);
     if (options->limit_time > 0)
         end_us = TIMEVAL_TO_MICROSEC(&stats->start_time) +
             SEC_TO_MICROSEC(options->limit_time);
@@ -424,23 +435,18 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx)
             ctx->skip_packets = 0;
 
             if (options->speed.mode == speed_multiplier) {
-                if(!timerisset(&stats->first_packet_sent_wall_time)) {
-                    /* We're sending the first packet, so we have an absolute time reference. */
-                    TIMEVAL_SET(&stats->first_packet_sent_wall_time, &now);
-                    TIMEVAL_SET(&stats->first_packet_pcap_timestamp, &pkthdr.ts);
-                }
-
                 if (!timerisset(&last_pkt_ts)) {
                     TIMEVAL_SET(&last_pkt_ts, &pkthdr.ts);
                 } else if (timercmp(&pkthdr.ts, &last_pkt_ts, >)) {
-                    /* pkt_ts_delta is the packet time stamp difference since the first packet */
-                    timersub(&pkthdr.ts, &stats->first_packet_pcap_timestamp, &stats->pkt_ts_delta);
-
-                    /* time_delta is the wall time difference since sending the first packet */
-                    timersub(&now, &stats->first_packet_sent_wall_time, &stats->time_delta);
+                    struct timeval delta;
 
+                    timersub(&pkthdr.ts, &last_pkt_ts, &delta);
+                    timeradd(&stats->pkt_ts_delta, &delta, &stats->pkt_ts_delta);
                     TIMEVAL_SET(&last_pkt_ts, &pkthdr.ts);
                 }
+
+                if (!timerisset(&stats->time_delta))
+                    TIMEVAL_SET(&stats->pkt_ts_delta, &stats->pkt_ts_delta);
             }
 
             if (!top_speed) {
@@ -590,7 +596,6 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t *
 
     ctx->skip_packets = 0;
     timerclear(&last_pkt_ts);
-    timerclear(&stats->first_packet_sent_wall_time);
     if (options->limit_time > 0)
         end_us = TIMEVAL_TO_MICROSEC(&stats->start_time) +
             SEC_TO_MICROSEC(options->limit_time);
@@ -706,23 +711,18 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t *
             ctx->skip_packets = 0;
 
             if (options->speed.mode == speed_multiplier) {
-                if(!timerisset(&stats->first_packet_sent_wall_time)) {
-                    /* We're sending the first packet, so we have an absolute time reference. */
-                    TIMEVAL_SET(&stats->first_packet_sent_wall_time, &now);
-                    TIMEVAL_SET(&stats->first_packet_pcap_timestamp, &pkthdr_ptr->ts);
-                }
-
                 if (!timerisset(&last_pkt_ts)) {
                     TIMEVAL_SET(&last_pkt_ts, &pkthdr_ptr->ts);
                 } else if (timercmp(&pkthdr_ptr->ts, &last_pkt_ts, >)) {
-                    /* pkt_ts_delta is the packet time stamp difference since the first packet */
-                    timersub(&pkthdr_ptr->ts, &stats->first_packet_pcap_timestamp, &stats->pkt_ts_delta);
-
-                    /* time_delta is the wall time difference since sending the first packet */
-                    timersub(&now, &stats->first_packet_sent_wall_time, &stats->time_delta);
+                    struct timeval delta;
 
+                    timersub(&pkthdr_ptr->ts, &last_pkt_ts, &delta);
+                    timeradd(&stats->pkt_ts_delta, &delta, &stats->pkt_ts_delta);
                     TIMEVAL_SET(&last_pkt_ts, &pkthdr_ptr->ts);
                 }
+
+                if (!timerisset(&stats->time_delta))
+                    TIMEVAL_SET(&stats->pkt_ts_delta, &stats->pkt_ts_delta);
             }
 
             if (!top_speed) {
@@ -1069,7 +1069,7 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timeval *pkt_ts_delta,
              if ((pkts_sent < COUNTER_OVERFLOW_RISK))
                  next_tx_us = (pkts_sent * 1000000) * (60 * 60) / pph;
              else
-                 next_tx_us = (pkts_sent * 1000000) / pph / (60 * 60);
+                 next_tx_us = ((pkts_sent * 1000000) / pph) * (60 * 60);
 
              if (next_tx_us > tx_us)
                  NANOSEC_TO_TIMESPEC((next_tx_us - tx_us) * 1000, &ctx->nap);

+ 1 - 1
src/send_packets.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/signal_handler.c

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/signal_handler.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/sleep.c

@@ -1,6 +1,6 @@
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it
  *   and/or modify it under the terms of the GNU General Public License as

+ 1 - 1
src/sleep.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 24 - 4
src/tcpbridge.1

@@ -10,7 +10,7 @@
 .ds B-Font B
 .ds I-Font I
 .ds R-Font R
-.TH tcpbridge 1 "01 May 2021" "tcpbridge" "User Commands"
+.TH tcpbridge 1 "29 Jan 2022" "tcpbridge" "User Commands"
 .\"
 .\" DO NOT EDIT THIS FILE (in-mem file)
 .\"
@@ -446,7 +446,14 @@ Allows you to rewrite ethernet frames to add a 802.1q header to standard 802.3
 ethernet headers or remove the 802.1q VLAN tag information.
 .sp 1
 \fBadd\fP
-Rewrites the existing 802.3 ethernet header as an 802.1q VLAN header
+Adds an 802.1q VLAN header to the existing 802.3 ethernet header. If
+a VLAN header already exists, a new VLAN header is added outside of the
+existing header.
+.sp
+Note that you will be allowed to run this option multiple times to create
+more than 2 VLAN headers, however those packets will be valid. At most
+you should have 2 X 802.1q VLAN tags, or outer an 802.1ad and an inner 802.1q
+VLAN tag.
 .sp 1
 \fBdel\fP
 Rewrites the existing 802.1q VLAN header as an 802.3 ethernet header
@@ -502,6 +509,19 @@ in the range  0 through 7
 .in -4
 .sp
 .TP
+.NOP \f\*[B-Font]\-\-enet\-vlan\-proto\f[]=\f\*[I-Font]string\f[]
+Specify VLAN tag protocol 802.1q or 802.1ad.
+This option may appear up to 1 times.
+.sp
+Allows you to specify the protocol of the added VLAN tags.
+.sp 1
+\fB802.1q\fP
+Specifies that 802.1q VLAN headers are to be added. This is the default.
+.sp 1
+\fB802.1ad\fP
+Specifies that 802.1ad Q-in-Q VLAN headers are to be added. To make valid packets,
+input packets must already have 802.1q VLAN headers.
+.TP
 .NOP \f\*[B-Font]\-\-hdlc\-control\f[]=\f\*[I-Font]number\f[]
 Specify HDLC control value.
 This option may appear up to 1 times.
@@ -763,13 +783,13 @@ libopts had an internal operational error.  Please report
 it to autogen-users@lists.sourceforge.net.  Thank you.
 .PP
 .SH "AUTHORS"
-Copyright 2013-2018 Fred Klassen \- AppNeta
+Copyright 2013-2022 Fred Klassen \- AppNeta
 Copyright 2000-2012 Aaron Turner
 For support please use the tcpreplay-users@lists.sourceforge.net mailing list.
 The latest version of this software is always available from:
 http://tcpreplay.appneta.com/
 .SH "COPYRIGHT"
-Copyright (C) 2000-2018 Aaron Turner and Fred Klassen all rights reserved.
+Copyright (C) 2000-2022 Aaron Turner and Fred Klassen all rights reserved.
 This program is released under the terms of the GNU General Public License, version 3 or later.
 .SH "BUGS"
 Please send bug reports to: tcpreplay-users@lists.sourceforge.net

+ 1 - 1
src/tcpbridge.c

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/tcpbridge.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 215 - 184
src/tcpbridge_opts.c

@@ -19,7 +19,7 @@
  * The tcpbridge program is copyrighted and licensed
  * under the following terms:
  *
- *  Copyright (C) 2000-2018 Aaron Turner and Fred Klassen, all rights reserved.
+ *  Copyright (C) 2000-2022 Aaron Turner and Fred Klassen, all rights reserved.
  *  This is free software. It is licensed for use, modification and
  *  redistribution under the terms of the GNU General Public License,
  *  version 3 or later <http://gnu.org/licenses/gpl.html>
@@ -76,9 +76,9 @@ extern tcpbridge_opt_t options;
 /**
  *  static const strings for tcpbridge options
  */
-static char const tcpbridge_opt_strs[4517] =
+static char const tcpbridge_opt_strs[4593] =
 /*     0 */ "tcpbridge (tcpbridge)\n"
-            "Copyright (C) 2000-2018 Aaron Turner and Fred Klassen, all rights reserved.\n"
+            "Copyright (C) 2000-2022 Aaron Turner and Fred Klassen, all rights reserved.\n"
             "This is free software. It is licensed for use, modification and\n"
             "redistribution under the terms of the GNU General Public License,\n"
             "version 3 or later <http://gnu.org/licenses/gpl.html>\n\0"
@@ -182,79 +182,82 @@ static char const tcpbridge_opt_strs[4517] =
 /*  2667 */ "Specify the ethernet 802.1q VLAN priority\0"
 /*  2709 */ "ENET_VLAN_PRI\0"
 /*  2723 */ "enet-vlan-pri\0"
-/*  2737 */ "Specify HDLC control value\0"
-/*  2764 */ "HDLC_CONTROL\0"
-/*  2777 */ "hdlc-control\0"
-/*  2790 */ "Specify HDLC address\0"
-/*  2811 */ "HDLC_ADDRESS\0"
-/*  2824 */ "hdlc-address\0"
-/*  2837 */ "Set output file DLT type\0"
-/*  2862 */ "USER_DLT\0"
-/*  2871 */ "user-dlt\0"
-/*  2880 */ "Rewrite Data-Link layer with user specified data\0"
-/*  2929 */ "USER_DLINK\0"
-/*  2940 */ "user-dlink\0"
-/*  2951 */ "Enable debugging output\0"
-/*  2975 */ "DBUG\0"
-/*  2980 */ "dbug\0"
-/*  2985 */ "Primary interface (listen in uni-directional mode)\0"
-/*  3036 */ "INTF1\0"
-/*  3042 */ "intf1\0"
-/*  3048 */ "Secondary interface (send in uni-directional mode)\0"
-/*  3099 */ "INTF2\0"
-/*  3105 */ "intf2\0"
-/*  3111 */ "Send and receive in only one direction\0"
-/*  3150 */ "UNIDIR\0"
-/*  3157 */ "unidir\0"
-/*  3164 */ "List available network interfaces and exit\0"
-/*  3207 */ "LISTNICS\0"
-/*  3216 */ "listnics\0"
-/*  3225 */ "Limit the number of packets to send\0"
-/*  3261 */ "LIMIT\0"
-/*  3267 */ "limit\0"
-/*  3273 */ "MAC addresses of local NIC's\0"
-/*  3302 */ "MAC\0"
-/*  3306 */ "mac\0"
-/*  3310 */ "Include only packets matching rule\0"
-/*  3345 */ "INCLUDE\0"
-/*  3353 */ "include\0"
-/*  3361 */ "Exclude any packet matching this rule\0"
-/*  3399 */ "EXCLUDE\0"
-/*  3407 */ "exclude\0"
-/*  3415 */ "Print the PID of tcpbridge at startup\0"
-/*  3453 */ "PID\0"
-/*  3457 */ "pid\0"
-/*  3461 */ "Print decoded packets via tcpdump to STDOUT\0"
-/*  3505 */ "VERBOSE\0"
-/*  3513 */ "verbose\0"
-/*  3521 */ "Arguments passed to tcpdump decoder\0"
-/*  3557 */ "DECODE\0"
-/*  3564 */ "decode\0"
-/*  3571 */ "Print version information\0"
-/*  3597 */ "VERSION\0"
-/*  3605 */ "version\0"
-/*  3613 */ "Display less usage information and exit\0"
-/*  3653 */ "LESS_HELP\0"
-/*  3663 */ "less-help\0"
-/*  3673 */ "display extended usage information and exit\0"
-/*  3717 */ "help\0"
-/*  3722 */ "extended usage information passed thru pager\0"
-/*  3767 */ "more-help\0"
-/*  3777 */ "save the option state to a config file\0"
-/*  3816 */ "save-opts\0"
-/*  3826 */ "load options from a config file\0"
-/*  3858 */ "LOAD_OPTS\0"
-/*  3868 */ "no-load-opts\0"
-/*  3881 */ "no\0"
-/*  3884 */ "TCPBRIDGE\0"
-/*  3894 */ "tcpbridge (tcpbridge) - Bridge network traffic across two interfaces\n"
+/*  2737 */ "Specify VLAN tag protocol 802.1q or 802.1ad\0"
+/*  2781 */ "ENET_VLAN_PROTO\0"
+/*  2797 */ "enet-vlan-proto\0"
+/*  2813 */ "Specify HDLC control value\0"
+/*  2840 */ "HDLC_CONTROL\0"
+/*  2853 */ "hdlc-control\0"
+/*  2866 */ "Specify HDLC address\0"
+/*  2887 */ "HDLC_ADDRESS\0"
+/*  2900 */ "hdlc-address\0"
+/*  2913 */ "Set output file DLT type\0"
+/*  2938 */ "USER_DLT\0"
+/*  2947 */ "user-dlt\0"
+/*  2956 */ "Rewrite Data-Link layer with user specified data\0"
+/*  3005 */ "USER_DLINK\0"
+/*  3016 */ "user-dlink\0"
+/*  3027 */ "Enable debugging output\0"
+/*  3051 */ "DBUG\0"
+/*  3056 */ "dbug\0"
+/*  3061 */ "Primary interface (listen in uni-directional mode)\0"
+/*  3112 */ "INTF1\0"
+/*  3118 */ "intf1\0"
+/*  3124 */ "Secondary interface (send in uni-directional mode)\0"
+/*  3175 */ "INTF2\0"
+/*  3181 */ "intf2\0"
+/*  3187 */ "Send and receive in only one direction\0"
+/*  3226 */ "UNIDIR\0"
+/*  3233 */ "unidir\0"
+/*  3240 */ "List available network interfaces and exit\0"
+/*  3283 */ "LISTNICS\0"
+/*  3292 */ "listnics\0"
+/*  3301 */ "Limit the number of packets to send\0"
+/*  3337 */ "LIMIT\0"
+/*  3343 */ "limit\0"
+/*  3349 */ "MAC addresses of local NIC's\0"
+/*  3378 */ "MAC\0"
+/*  3382 */ "mac\0"
+/*  3386 */ "Include only packets matching rule\0"
+/*  3421 */ "INCLUDE\0"
+/*  3429 */ "include\0"
+/*  3437 */ "Exclude any packet matching this rule\0"
+/*  3475 */ "EXCLUDE\0"
+/*  3483 */ "exclude\0"
+/*  3491 */ "Print the PID of tcpbridge at startup\0"
+/*  3529 */ "PID\0"
+/*  3533 */ "pid\0"
+/*  3537 */ "Print decoded packets via tcpdump to STDOUT\0"
+/*  3581 */ "VERBOSE\0"
+/*  3589 */ "verbose\0"
+/*  3597 */ "Arguments passed to tcpdump decoder\0"
+/*  3633 */ "DECODE\0"
+/*  3640 */ "decode\0"
+/*  3647 */ "Print version information\0"
+/*  3673 */ "VERSION\0"
+/*  3681 */ "version\0"
+/*  3689 */ "Display less usage information and exit\0"
+/*  3729 */ "LESS_HELP\0"
+/*  3739 */ "less-help\0"
+/*  3749 */ "display extended usage information and exit\0"
+/*  3793 */ "help\0"
+/*  3798 */ "extended usage information passed thru pager\0"
+/*  3843 */ "more-help\0"
+/*  3853 */ "save the option state to a config file\0"
+/*  3892 */ "save-opts\0"
+/*  3902 */ "load options from a config file\0"
+/*  3934 */ "LOAD_OPTS\0"
+/*  3944 */ "no-load-opts\0"
+/*  3957 */ "no\0"
+/*  3960 */ "TCPBRIDGE\0"
+/*  3970 */ "tcpbridge (tcpbridge) - Bridge network traffic across two interfaces\n"
             "Usage:  %s [ -<flag> [<val>] | --<name>[{=| }<val>] ]...\n\0"
-/*  4021 */ "$$/\0"
-/*  4025 */ ".tcpbridgerc\0"
-/*  4038 */ "tcpreplay-users@lists.sourceforge.net\0"
-/*  4076 */ "tcpbridge is a tool for selectively bridging network traffic across two\n"
+/*  4097 */ "$$/\0"
+/*  4101 */ ".tcpbridgerc\0"
+/*  4114 */ "tcpreplay-users@lists.sourceforge.net\0"
+/*  4152 */ "tcpbridge is a tool for selectively bridging network traffic across two\n"
             "interfaces and optionally modifying the packets in between\n\0"
-/*  4208 */ "The basic operation of tcpbridge is to be a network bridge between two\n"
+/*  4284 */ "The basic operation of tcpbridge is to be a network bridge between two\n"
             "subnets.  All packets received on one interface are sent via the other.\n\n"
             "Optionally, packets can be edited in a variety of ways according to your\n"
             "needs.\n\n"
@@ -715,14 +718,27 @@ static int const aEnet_Vlan_PriMustList[] = {
         | OPTST_SET_ARGTYPE(OPARG_TYPE_NUMERIC))
 
 /**
+ *  enet-vlan-proto option description:
+ */
+/** Descriptive text for the enet-vlan-proto option */
+#define ENET_VLAN_PROTO_DESC      (tcpbridge_opt_strs+2737)
+/** Upper-cased name for the enet-vlan-proto option */
+#define ENET_VLAN_PROTO_NAME      (tcpbridge_opt_strs+2781)
+/** Name string for the enet-vlan-proto option */
+#define ENET_VLAN_PROTO_name      (tcpbridge_opt_strs+2797)
+/** Compiled in flag settings for the enet-vlan-proto option */
+#define ENET_VLAN_PROTO_FLAGS     (OPTST_DISABLED \
+        | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/**
  *  hdlc-control option description:
  */
 /** Descriptive text for the hdlc-control option */
-#define HDLC_CONTROL_DESC      (tcpbridge_opt_strs+2737)
+#define HDLC_CONTROL_DESC      (tcpbridge_opt_strs+2813)
 /** Upper-cased name for the hdlc-control option */
-#define HDLC_CONTROL_NAME      (tcpbridge_opt_strs+2764)
+#define HDLC_CONTROL_NAME      (tcpbridge_opt_strs+2840)
 /** Name string for the hdlc-control option */
-#define HDLC_CONTROL_name      (tcpbridge_opt_strs+2777)
+#define HDLC_CONTROL_name      (tcpbridge_opt_strs+2853)
 /** Compiled in flag settings for the hdlc-control option */
 #define HDLC_CONTROL_FLAGS     (OPTST_DISABLED \
         | OPTST_SET_ARGTYPE(OPARG_TYPE_NUMERIC))
@@ -731,11 +747,11 @@ static int const aEnet_Vlan_PriMustList[] = {
  *  hdlc-address option description:
  */
 /** Descriptive text for the hdlc-address option */
-#define HDLC_ADDRESS_DESC      (tcpbridge_opt_strs+2790)
+#define HDLC_ADDRESS_DESC      (tcpbridge_opt_strs+2866)
 /** Upper-cased name for the hdlc-address option */
-#define HDLC_ADDRESS_NAME      (tcpbridge_opt_strs+2811)
+#define HDLC_ADDRESS_NAME      (tcpbridge_opt_strs+2887)
 /** Name string for the hdlc-address option */
-#define HDLC_ADDRESS_name      (tcpbridge_opt_strs+2824)
+#define HDLC_ADDRESS_name      (tcpbridge_opt_strs+2900)
 /** Compiled in flag settings for the hdlc-address option */
 #define HDLC_ADDRESS_FLAGS     (OPTST_DISABLED \
         | OPTST_SET_ARGTYPE(OPARG_TYPE_NUMERIC))
@@ -744,11 +760,11 @@ static int const aEnet_Vlan_PriMustList[] = {
  *  user-dlt option description:
  */
 /** Descriptive text for the user-dlt option */
-#define USER_DLT_DESC      (tcpbridge_opt_strs+2837)
+#define USER_DLT_DESC      (tcpbridge_opt_strs+2913)
 /** Upper-cased name for the user-dlt option */
-#define USER_DLT_NAME      (tcpbridge_opt_strs+2862)
+#define USER_DLT_NAME      (tcpbridge_opt_strs+2938)
 /** Name string for the user-dlt option */
-#define USER_DLT_name      (tcpbridge_opt_strs+2871)
+#define USER_DLT_name      (tcpbridge_opt_strs+2947)
 /** Compiled in flag settings for the user-dlt option */
 #define USER_DLT_FLAGS     (OPTST_DISABLED \
         | OPTST_SET_ARGTYPE(OPARG_TYPE_NUMERIC))
@@ -757,11 +773,11 @@ static int const aEnet_Vlan_PriMustList[] = {
  *  user-dlink option description:
  */
 /** Descriptive text for the user-dlink option */
-#define USER_DLINK_DESC      (tcpbridge_opt_strs+2880)
+#define USER_DLINK_DESC      (tcpbridge_opt_strs+2956)
 /** Upper-cased name for the user-dlink option */
-#define USER_DLINK_NAME      (tcpbridge_opt_strs+2929)
+#define USER_DLINK_NAME      (tcpbridge_opt_strs+3005)
 /** Name string for the user-dlink option */
-#define USER_DLINK_name      (tcpbridge_opt_strs+2940)
+#define USER_DLINK_name      (tcpbridge_opt_strs+3016)
 /** Compiled in flag settings for the user-dlink option */
 #define USER_DLINK_FLAGS     (OPTST_DISABLED | OPTST_STACKED \
         | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
@@ -771,11 +787,11 @@ static int const aEnet_Vlan_PriMustList[] = {
  */
 #ifdef DEBUG
 /** Descriptive text for the dbug option */
-#define DBUG_DESC      (tcpbridge_opt_strs+2951)
+#define DBUG_DESC      (tcpbridge_opt_strs+3027)
 /** Upper-cased name for the dbug option */
-#define DBUG_NAME      (tcpbridge_opt_strs+2975)
+#define DBUG_NAME      (tcpbridge_opt_strs+3051)
 /** Name string for the dbug option */
-#define DBUG_name      (tcpbridge_opt_strs+2980)
+#define DBUG_name      (tcpbridge_opt_strs+3056)
 /** The compiled in default value for the dbug option argument */
 #define DBUG_DFT_ARG   ((char const*)0)
 /** Compiled in flag settings for the dbug option */
@@ -794,11 +810,11 @@ static int const aEnet_Vlan_PriMustList[] = {
  *  intf1 option description:
  */
 /** Descriptive text for the intf1 option */
-#define INTF1_DESC      (tcpbridge_opt_strs+2985)
+#define INTF1_DESC      (tcpbridge_opt_strs+3061)
 /** Upper-cased name for the intf1 option */
-#define INTF1_NAME      (tcpbridge_opt_strs+3036)
+#define INTF1_NAME      (tcpbridge_opt_strs+3112)
 /** Name string for the intf1 option */
-#define INTF1_name      (tcpbridge_opt_strs+3042)
+#define INTF1_name      (tcpbridge_opt_strs+3118)
 /** Compiled in flag settings for the intf1 option */
 #define INTF1_FLAGS     (OPTST_DISABLED | OPTST_MUST_SET \
         | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
@@ -807,11 +823,11 @@ static int const aEnet_Vlan_PriMustList[] = {
  *  intf2 option description:
  */
 /** Descriptive text for the intf2 option */
-#define INTF2_DESC      (tcpbridge_opt_strs+3048)
+#define INTF2_DESC      (tcpbridge_opt_strs+3124)
 /** Upper-cased name for the intf2 option */
-#define INTF2_NAME      (tcpbridge_opt_strs+3099)
+#define INTF2_NAME      (tcpbridge_opt_strs+3175)
 /** Name string for the intf2 option */
-#define INTF2_name      (tcpbridge_opt_strs+3105)
+#define INTF2_name      (tcpbridge_opt_strs+3181)
 /** Compiled in flag settings for the intf2 option */
 #define INTF2_FLAGS     (OPTST_DISABLED \
         | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
@@ -820,11 +836,11 @@ static int const aEnet_Vlan_PriMustList[] = {
  *  unidir option description:
  */
 /** Descriptive text for the unidir option */
-#define UNIDIR_DESC      (tcpbridge_opt_strs+3111)
+#define UNIDIR_DESC      (tcpbridge_opt_strs+3187)
 /** Upper-cased name for the unidir option */
-#define UNIDIR_NAME      (tcpbridge_opt_strs+3150)
+#define UNIDIR_NAME      (tcpbridge_opt_strs+3226)
 /** Name string for the unidir option */
-#define UNIDIR_name      (tcpbridge_opt_strs+3157)
+#define UNIDIR_name      (tcpbridge_opt_strs+3233)
 /** Compiled in flag settings for the unidir option */
 #define UNIDIR_FLAGS     (OPTST_DISABLED)
 
@@ -833,11 +849,11 @@ static int const aEnet_Vlan_PriMustList[] = {
  */
 #ifdef ENABLE_PCAP_FINDALLDEVS
 /** Descriptive text for the listnics option */
-#define LISTNICS_DESC      (tcpbridge_opt_strs+3164)
+#define LISTNICS_DESC      (tcpbridge_opt_strs+3240)
 /** Upper-cased name for the listnics option */
-#define LISTNICS_NAME      (tcpbridge_opt_strs+3207)
+#define LISTNICS_NAME      (tcpbridge_opt_strs+3283)
 /** Name string for the listnics option */
-#define LISTNICS_name      (tcpbridge_opt_strs+3216)
+#define LISTNICS_name      (tcpbridge_opt_strs+3292)
 /** Compiled in flag settings for the listnics option */
 #define LISTNICS_FLAGS     (OPTST_DISABLED | OPTST_IMM)
 
@@ -852,11 +868,11 @@ static int const aEnet_Vlan_PriMustList[] = {
  *  limit option description:
  */
 /** Descriptive text for the limit option */
-#define LIMIT_DESC      (tcpbridge_opt_strs+3225)
+#define LIMIT_DESC      (tcpbridge_opt_strs+3301)
 /** Upper-cased name for the limit option */
-#define LIMIT_NAME      (tcpbridge_opt_strs+3261)
+#define LIMIT_NAME      (tcpbridge_opt_strs+3337)
 /** Name string for the limit option */
-#define LIMIT_name      (tcpbridge_opt_strs+3267)
+#define LIMIT_name      (tcpbridge_opt_strs+3343)
 /** The compiled in default value for the limit option argument */
 #define LIMIT_DFT_ARG   ((char const*)-1)
 /** Compiled in flag settings for the limit option */
@@ -867,11 +883,11 @@ static int const aEnet_Vlan_PriMustList[] = {
  *  mac option description:
  */
 /** Descriptive text for the mac option */
-#define MAC_DESC      (tcpbridge_opt_strs+3273)
+#define MAC_DESC      (tcpbridge_opt_strs+3349)
 /** Upper-cased name for the mac option */
-#define MAC_NAME      (tcpbridge_opt_strs+3302)
+#define MAC_NAME      (tcpbridge_opt_strs+3378)
 /** Name string for the mac option */
-#define MAC_name      (tcpbridge_opt_strs+3306)
+#define MAC_name      (tcpbridge_opt_strs+3382)
 /** Compiled in flag settings for the mac option */
 #define MAC_FLAGS     (OPTST_DISABLED | OPTST_STACKED \
         | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
@@ -881,11 +897,11 @@ static int const aEnet_Vlan_PriMustList[] = {
  *  "Must also have options" and "Incompatible options":
  */
 /** Descriptive text for the include option */
-#define INCLUDE_DESC      (tcpbridge_opt_strs+3310)
+#define INCLUDE_DESC      (tcpbridge_opt_strs+3386)
 /** Upper-cased name for the include option */
-#define INCLUDE_NAME      (tcpbridge_opt_strs+3345)
+#define INCLUDE_NAME      (tcpbridge_opt_strs+3421)
 /** Name string for the include option */
-#define INCLUDE_name      (tcpbridge_opt_strs+3353)
+#define INCLUDE_name      (tcpbridge_opt_strs+3429)
 /** Other options that appear in conjunction with the include option */
 static int const aIncludeCantList[] = {
     INDEX_OPT_EXCLUDE, NO_EQUIVALENT };
@@ -898,11 +914,11 @@ static int const aIncludeCantList[] = {
  *  "Must also have options" and "Incompatible options":
  */
 /** Descriptive text for the exclude option */
-#define EXCLUDE_DESC      (tcpbridge_opt_strs+3361)
+#define EXCLUDE_DESC      (tcpbridge_opt_strs+3437)
 /** Upper-cased name for the exclude option */
-#define EXCLUDE_NAME      (tcpbridge_opt_strs+3399)
+#define EXCLUDE_NAME      (tcpbridge_opt_strs+3475)
 /** Name string for the exclude option */
-#define EXCLUDE_name      (tcpbridge_opt_strs+3407)
+#define EXCLUDE_name      (tcpbridge_opt_strs+3483)
 /** Other options that appear in conjunction with the exclude option */
 static int const aExcludeCantList[] = {
     INDEX_OPT_INCLUDE, NO_EQUIVALENT };
@@ -914,11 +930,11 @@ static int const aExcludeCantList[] = {
  *  pid option description:
  */
 /** Descriptive text for the pid option */
-#define PID_DESC      (tcpbridge_opt_strs+3415)
+#define PID_DESC      (tcpbridge_opt_strs+3491)
 /** Upper-cased name for the pid option */
-#define PID_NAME      (tcpbridge_opt_strs+3453)
+#define PID_NAME      (tcpbridge_opt_strs+3529)
 /** Name string for the pid option */
-#define PID_name      (tcpbridge_opt_strs+3457)
+#define PID_name      (tcpbridge_opt_strs+3533)
 /** Compiled in flag settings for the pid option */
 #define PID_FLAGS     (OPTST_DISABLED)
 
@@ -927,11 +943,11 @@ static int const aExcludeCantList[] = {
  */
 #ifdef ENABLE_VERBOSE
 /** Descriptive text for the verbose option */
-#define VERBOSE_DESC      (tcpbridge_opt_strs+3461)
+#define VERBOSE_DESC      (tcpbridge_opt_strs+3537)
 /** Upper-cased name for the verbose option */
-#define VERBOSE_NAME      (tcpbridge_opt_strs+3505)
+#define VERBOSE_NAME      (tcpbridge_opt_strs+3581)
 /** Name string for the verbose option */
-#define VERBOSE_name      (tcpbridge_opt_strs+3513)
+#define VERBOSE_name      (tcpbridge_opt_strs+3589)
 /** Compiled in flag settings for the verbose option */
 #define VERBOSE_FLAGS     (OPTST_DISABLED | OPTST_IMM)
 
@@ -948,11 +964,11 @@ static int const aExcludeCantList[] = {
  */
 #ifdef ENABLE_VERBOSE
 /** Descriptive text for the decode option */
-#define DECODE_DESC      (tcpbridge_opt_strs+3521)
+#define DECODE_DESC      (tcpbridge_opt_strs+3597)
 /** Upper-cased name for the decode option */
-#define DECODE_NAME      (tcpbridge_opt_strs+3557)
+#define DECODE_NAME      (tcpbridge_opt_strs+3633)
 /** Name string for the decode option */
-#define DECODE_name      (tcpbridge_opt_strs+3564)
+#define DECODE_name      (tcpbridge_opt_strs+3640)
 /** Other options that are required by the decode option */
 static int const aDecodeMustList[] = {
     INDEX_OPT_VERBOSE, NO_EQUIVALENT };
@@ -972,11 +988,11 @@ static int const aDecodeMustList[] = {
  *  version option description:
  */
 /** Descriptive text for the version option */
-#define VERSION_DESC      (tcpbridge_opt_strs+3571)
+#define VERSION_DESC      (tcpbridge_opt_strs+3647)
 /** Upper-cased name for the version option */
-#define VERSION_NAME      (tcpbridge_opt_strs+3597)
+#define VERSION_NAME      (tcpbridge_opt_strs+3673)
 /** Name string for the version option */
-#define VERSION_name      (tcpbridge_opt_strs+3605)
+#define VERSION_name      (tcpbridge_opt_strs+3681)
 /** Compiled in flag settings for the version option */
 #define VERSION_FLAGS     (OPTST_DISABLED)
 
@@ -984,34 +1000,34 @@ static int const aDecodeMustList[] = {
  *  less-help option description:
  */
 /** Descriptive text for the less-help option */
-#define LESS_HELP_DESC      (tcpbridge_opt_strs+3613)
+#define LESS_HELP_DESC      (tcpbridge_opt_strs+3689)
 /** Upper-cased name for the less-help option */
-#define LESS_HELP_NAME      (tcpbridge_opt_strs+3653)
+#define LESS_HELP_NAME      (tcpbridge_opt_strs+3729)
 /** Name string for the less-help option */
-#define LESS_HELP_name      (tcpbridge_opt_strs+3663)
+#define LESS_HELP_name      (tcpbridge_opt_strs+3739)
 /** Compiled in flag settings for the less-help option */
 #define LESS_HELP_FLAGS     (OPTST_DISABLED | OPTST_IMM)
 
 /*
  *  Help/More_Help option descriptions:
  */
-#define HELP_DESC       (tcpbridge_opt_strs+3673)
-#define HELP_name       (tcpbridge_opt_strs+3717)
+#define HELP_DESC       (tcpbridge_opt_strs+3749)
+#define HELP_name       (tcpbridge_opt_strs+3793)
 #ifdef HAVE_WORKING_FORK
-#define MORE_HELP_DESC  (tcpbridge_opt_strs+3722)
-#define MORE_HELP_name  (tcpbridge_opt_strs+3767)
+#define MORE_HELP_DESC  (tcpbridge_opt_strs+3798)
+#define MORE_HELP_name  (tcpbridge_opt_strs+3843)
 #define MORE_HELP_FLAGS (OPTST_IMM | OPTST_NO_INIT)
 #else
 #define MORE_HELP_DESC  HELP_DESC
 #define MORE_HELP_name  HELP_name
 #define MORE_HELP_FLAGS (OPTST_OMITTED | OPTST_NO_INIT)
 #endif
-#define SAVE_OPTS_DESC  (tcpbridge_opt_strs+3777)
-#define SAVE_OPTS_name  (tcpbridge_opt_strs+3816)
-#define LOAD_OPTS_DESC     (tcpbridge_opt_strs+3826)
-#define LOAD_OPTS_NAME     (tcpbridge_opt_strs+3858)
-#define NO_LOAD_OPTS_name  (tcpbridge_opt_strs+3868)
-#define LOAD_OPTS_pfx      (tcpbridge_opt_strs+3881)
+#define SAVE_OPTS_DESC  (tcpbridge_opt_strs+3853)
+#define SAVE_OPTS_name  (tcpbridge_opt_strs+3892)
+#define LOAD_OPTS_DESC     (tcpbridge_opt_strs+3902)
+#define LOAD_OPTS_NAME     (tcpbridge_opt_strs+3934)
+#define NO_LOAD_OPTS_name  (tcpbridge_opt_strs+3944)
+#define LOAD_OPTS_pfx      (tcpbridge_opt_strs+3957)
 #define LOAD_OPTS_name     (NO_LOAD_OPTS_name + 3)
 /**
  *  Declare option callback procedures
@@ -1421,8 +1437,20 @@ static tOptDesc optDesc[OPTION_CT] = {
      /* desc, NAME, name */ ENET_VLAN_PRI_DESC, ENET_VLAN_PRI_NAME, ENET_VLAN_PRI_name,
      /* disablement strs */ NULL, NULL },
 
-  {  /* entry idx, value */ 31, VALUE_OPT_HDLC_CONTROL,
-     /* equiv idx, value */ 31, VALUE_OPT_HDLC_CONTROL,
+  {  /* entry idx, value */ 31, VALUE_OPT_ENET_VLAN_PROTO,
+     /* equiv idx, value */ 31, VALUE_OPT_ENET_VLAN_PROTO,
+     /* equivalenced to  */ NO_EQUIVALENT,
+     /* min, max, act ct */ 0, 1, 0,
+     /* opt state flags  */ ENET_VLAN_PROTO_FLAGS, 0,
+     /* last opt argumnt */ { NULL }, /* --enet-vlan-proto */
+     /* arg list/cookie  */ NULL,
+     /* must/cannot opts */ NULL, NULL,
+     /* option proc      */ NULL,
+     /* desc, NAME, name */ ENET_VLAN_PROTO_DESC, ENET_VLAN_PROTO_NAME, ENET_VLAN_PROTO_name,
+     /* disablement strs */ NULL, NULL },
+
+  {  /* entry idx, value */ 32, VALUE_OPT_HDLC_CONTROL,
+     /* equiv idx, value */ 32, VALUE_OPT_HDLC_CONTROL,
      /* equivalenced to  */ NO_EQUIVALENT,
      /* min, max, act ct */ 0, 1, 0,
      /* opt state flags  */ HDLC_CONTROL_FLAGS, 0,
@@ -1433,8 +1461,8 @@ static tOptDesc optDesc[OPTION_CT] = {
      /* desc, NAME, name */ HDLC_CONTROL_DESC, HDLC_CONTROL_NAME, HDLC_CONTROL_name,
      /* disablement strs */ NULL, NULL },
 
-  {  /* entry idx, value */ 32, VALUE_OPT_HDLC_ADDRESS,
-     /* equiv idx, value */ 32, VALUE_OPT_HDLC_ADDRESS,
+  {  /* entry idx, value */ 33, VALUE_OPT_HDLC_ADDRESS,
+     /* equiv idx, value */ 33, VALUE_OPT_HDLC_ADDRESS,
      /* equivalenced to  */ NO_EQUIVALENT,
      /* min, max, act ct */ 0, 1, 0,
      /* opt state flags  */ HDLC_ADDRESS_FLAGS, 0,
@@ -1445,8 +1473,8 @@ static tOptDesc optDesc[OPTION_CT] = {
      /* desc, NAME, name */ HDLC_ADDRESS_DESC, HDLC_ADDRESS_NAME, HDLC_ADDRESS_name,
      /* disablement strs */ NULL, NULL },
 
-  {  /* entry idx, value */ 33, VALUE_OPT_USER_DLT,
-     /* equiv idx, value */ 33, VALUE_OPT_USER_DLT,
+  {  /* entry idx, value */ 34, VALUE_OPT_USER_DLT,
+     /* equiv idx, value */ 34, VALUE_OPT_USER_DLT,
      /* equivalenced to  */ NO_EQUIVALENT,
      /* min, max, act ct */ 0, 1, 0,
      /* opt state flags  */ USER_DLT_FLAGS, 0,
@@ -1457,8 +1485,8 @@ static tOptDesc optDesc[OPTION_CT] = {
      /* desc, NAME, name */ USER_DLT_DESC, USER_DLT_NAME, USER_DLT_name,
      /* disablement strs */ NULL, NULL },
 
-  {  /* entry idx, value */ 34, VALUE_OPT_USER_DLINK,
-     /* equiv idx, value */ 34, VALUE_OPT_USER_DLINK,
+  {  /* entry idx, value */ 35, VALUE_OPT_USER_DLINK,
+     /* equiv idx, value */ 35, VALUE_OPT_USER_DLINK,
      /* equivalenced to  */ NO_EQUIVALENT,
      /* min, max, act ct */ 0, 2, 0,
      /* opt state flags  */ USER_DLINK_FLAGS, 0,
@@ -1469,8 +1497,8 @@ static tOptDesc optDesc[OPTION_CT] = {
      /* desc, NAME, name */ USER_DLINK_DESC, USER_DLINK_NAME, USER_DLINK_name,
      /* disablement strs */ NULL, NULL },
 
-  {  /* entry idx, value */ 35, VALUE_OPT_DBUG,
-     /* equiv idx, value */ 35, VALUE_OPT_DBUG,
+  {  /* entry idx, value */ 36, VALUE_OPT_DBUG,
+     /* equiv idx, value */ 36, VALUE_OPT_DBUG,
      /* equivalenced to  */ NO_EQUIVALENT,
      /* min, max, act ct */ 0, 1, 0,
      /* opt state flags  */ DBUG_FLAGS, 0,
@@ -1481,8 +1509,8 @@ static tOptDesc optDesc[OPTION_CT] = {
      /* desc, NAME, name */ DBUG_DESC, DBUG_NAME, DBUG_name,
      /* disablement strs */ NULL, NULL },
 
-  {  /* entry idx, value */ 36, VALUE_OPT_INTF1,
-     /* equiv idx, value */ 36, VALUE_OPT_INTF1,
+  {  /* entry idx, value */ 37, VALUE_OPT_INTF1,
+     /* equiv idx, value */ 37, VALUE_OPT_INTF1,
      /* equivalenced to  */ NO_EQUIVALENT,
      /* min, max, act ct */ 1, 1, 0,
      /* opt state flags  */ INTF1_FLAGS, 0,
@@ -1493,8 +1521,8 @@ static tOptDesc optDesc[OPTION_CT] = {
      /* desc, NAME, name */ INTF1_DESC, INTF1_NAME, INTF1_name,
      /* disablement strs */ NULL, NULL },
 
-  {  /* entry idx, value */ 37, VALUE_OPT_INTF2,
-     /* equiv idx, value */ 37, VALUE_OPT_INTF2,
+  {  /* entry idx, value */ 38, VALUE_OPT_INTF2,
+     /* equiv idx, value */ 38, VALUE_OPT_INTF2,
      /* equivalenced to  */ NO_EQUIVALENT,
      /* min, max, act ct */ 0, 1, 0,
      /* opt state flags  */ INTF2_FLAGS, 0,
@@ -1505,8 +1533,8 @@ static tOptDesc optDesc[OPTION_CT] = {
      /* desc, NAME, name */ INTF2_DESC, INTF2_NAME, INTF2_name,
      /* disablement strs */ NULL, NULL },
 
-  {  /* entry idx, value */ 38, VALUE_OPT_UNIDIR,
-     /* equiv idx, value */ 38, VALUE_OPT_UNIDIR,
+  {  /* entry idx, value */ 39, VALUE_OPT_UNIDIR,
+     /* equiv idx, value */ 39, VALUE_OPT_UNIDIR,
      /* equivalenced to  */ NO_EQUIVALENT,
      /* min, max, act ct */ 0, 1, 0,
      /* opt state flags  */ UNIDIR_FLAGS, 0,
@@ -1517,8 +1545,8 @@ static tOptDesc optDesc[OPTION_CT] = {
      /* desc, NAME, name */ UNIDIR_DESC, UNIDIR_NAME, UNIDIR_name,
      /* disablement strs */ NULL, NULL },
 
-  {  /* entry idx, value */ 39, VALUE_OPT_LISTNICS,
-     /* equiv idx, value */ 39, VALUE_OPT_LISTNICS,
+  {  /* entry idx, value */ 40, VALUE_OPT_LISTNICS,
+     /* equiv idx, value */ 40, VALUE_OPT_LISTNICS,
      /* equivalenced to  */ NO_EQUIVALENT,
      /* min, max, act ct */ 0, 1, 0,
      /* opt state flags  */ LISTNICS_FLAGS, 0,
@@ -1529,8 +1557,8 @@ static tOptDesc optDesc[OPTION_CT] = {
      /* desc, NAME, name */ LISTNICS_DESC, LISTNICS_NAME, LISTNICS_name,
      /* disablement strs */ NULL, NULL },
 
-  {  /* entry idx, value */ 40, VALUE_OPT_LIMIT,
-     /* equiv idx, value */ 40, VALUE_OPT_LIMIT,
+  {  /* entry idx, value */ 41, VALUE_OPT_LIMIT,
+     /* equiv idx, value */ 41, VALUE_OPT_LIMIT,
      /* equivalenced to  */ NO_EQUIVALENT,
      /* min, max, act ct */ 0, 1, 0,
      /* opt state flags  */ LIMIT_FLAGS, 0,
@@ -1541,8 +1569,8 @@ static tOptDesc optDesc[OPTION_CT] = {
      /* desc, NAME, name */ LIMIT_DESC, LIMIT_NAME, LIMIT_name,
      /* disablement strs */ NULL, NULL },
 
-  {  /* entry idx, value */ 41, VALUE_OPT_MAC,
-     /* equiv idx, value */ 41, VALUE_OPT_MAC,
+  {  /* entry idx, value */ 42, VALUE_OPT_MAC,
+     /* equiv idx, value */ 42, VALUE_OPT_MAC,
      /* equivalenced to  */ NO_EQUIVALENT,
      /* min, max, act ct */ 0, 2, 0,
      /* opt state flags  */ MAC_FLAGS, 0,
@@ -1553,8 +1581,8 @@ static tOptDesc optDesc[OPTION_CT] = {
      /* desc, NAME, name */ MAC_DESC, MAC_NAME, MAC_name,
      /* disablement strs */ NULL, NULL },
 
-  {  /* entry idx, value */ 42, VALUE_OPT_INCLUDE,
-     /* equiv idx, value */ 42, VALUE_OPT_INCLUDE,
+  {  /* entry idx, value */ 43, VALUE_OPT_INCLUDE,
+     /* equiv idx, value */ 43, VALUE_OPT_INCLUDE,
      /* equivalenced to  */ NO_EQUIVALENT,
      /* min, max, act ct */ 0, 1, 0,
      /* opt state flags  */ INCLUDE_FLAGS, 0,
@@ -1565,8 +1593,8 @@ static tOptDesc optDesc[OPTION_CT] = {
      /* desc, NAME, name */ INCLUDE_DESC, INCLUDE_NAME, INCLUDE_name,
      /* disablement strs */ NULL, NULL },
 
-  {  /* entry idx, value */ 43, VALUE_OPT_EXCLUDE,
-     /* equiv idx, value */ 43, VALUE_OPT_EXCLUDE,
+  {  /* entry idx, value */ 44, VALUE_OPT_EXCLUDE,
+     /* equiv idx, value */ 44, VALUE_OPT_EXCLUDE,
      /* equivalenced to  */ NO_EQUIVALENT,
      /* min, max, act ct */ 0, 1, 0,
      /* opt state flags  */ EXCLUDE_FLAGS, 0,
@@ -1577,8 +1605,8 @@ static tOptDesc optDesc[OPTION_CT] = {
      /* desc, NAME, name */ EXCLUDE_DESC, EXCLUDE_NAME, EXCLUDE_name,
      /* disablement strs */ NULL, NULL },
 
-  {  /* entry idx, value */ 44, VALUE_OPT_PID,
-     /* equiv idx, value */ 44, VALUE_OPT_PID,
+  {  /* entry idx, value */ 45, VALUE_OPT_PID,
+     /* equiv idx, value */ 45, VALUE_OPT_PID,
      /* equivalenced to  */ NO_EQUIVALENT,
      /* min, max, act ct */ 0, 1, 0,
      /* opt state flags  */ PID_FLAGS, 0,
@@ -1589,8 +1617,8 @@ static tOptDesc optDesc[OPTION_CT] = {
      /* desc, NAME, name */ PID_DESC, PID_NAME, PID_name,
      /* disablement strs */ NULL, NULL },
 
-  {  /* entry idx, value */ 45, VALUE_OPT_VERBOSE,
-     /* equiv idx, value */ 45, VALUE_OPT_VERBOSE,
+  {  /* entry idx, value */ 46, VALUE_OPT_VERBOSE,
+     /* equiv idx, value */ 46, VALUE_OPT_VERBOSE,
      /* equivalenced to  */ NO_EQUIVALENT,
      /* min, max, act ct */ 0, 1, 0,
      /* opt state flags  */ VERBOSE_FLAGS, 0,
@@ -1601,8 +1629,8 @@ static tOptDesc optDesc[OPTION_CT] = {
      /* desc, NAME, name */ VERBOSE_DESC, VERBOSE_NAME, VERBOSE_name,
      /* disablement strs */ NULL, NULL },
 
-  {  /* entry idx, value */ 46, VALUE_OPT_DECODE,
-     /* equiv idx, value */ 46, VALUE_OPT_DECODE,
+  {  /* entry idx, value */ 47, VALUE_OPT_DECODE,
+     /* equiv idx, value */ 47, VALUE_OPT_DECODE,
      /* equivalenced to  */ NO_EQUIVALENT,
      /* min, max, act ct */ 0, 1, 0,
      /* opt state flags  */ DECODE_FLAGS, 0,
@@ -1613,8 +1641,8 @@ static tOptDesc optDesc[OPTION_CT] = {
      /* desc, NAME, name */ DECODE_DESC, DECODE_NAME, DECODE_name,
      /* disablement strs */ NULL, NULL },
 
-  {  /* entry idx, value */ 47, VALUE_OPT_VERSION,
-     /* equiv idx, value */ 47, VALUE_OPT_VERSION,
+  {  /* entry idx, value */ 48, VALUE_OPT_VERSION,
+     /* equiv idx, value */ 48, VALUE_OPT_VERSION,
      /* equivalenced to  */ NO_EQUIVALENT,
      /* min, max, act ct */ 0, 1, 0,
      /* opt state flags  */ VERSION_FLAGS, 0,
@@ -1625,8 +1653,8 @@ static tOptDesc optDesc[OPTION_CT] = {
      /* desc, NAME, name */ VERSION_DESC, VERSION_NAME, VERSION_name,
      /* disablement strs */ NULL, NULL },
 
-  {  /* entry idx, value */ 48, VALUE_OPT_LESS_HELP,
-     /* equiv idx, value */ 48, VALUE_OPT_LESS_HELP,
+  {  /* entry idx, value */ 49, VALUE_OPT_LESS_HELP,
+     /* equiv idx, value */ 49, VALUE_OPT_LESS_HELP,
      /* equivalenced to  */ NO_EQUIVALENT,
      /* min, max, act ct */ 0, 1, 0,
      /* opt state flags  */ LESS_HELP_FLAGS, 0,
@@ -1692,21 +1720,21 @@ tOptDesc * const tcpedit_tcpedit_optDesc_p = optDesc + 0;
 
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 /** Reference to the upper cased version of tcpbridge. */
-#define zPROGNAME       (tcpbridge_opt_strs+3884)
+#define zPROGNAME       (tcpbridge_opt_strs+3960)
 /** Reference to the title line for tcpbridge usage. */
-#define zUsageTitle     (tcpbridge_opt_strs+3894)
+#define zUsageTitle     (tcpbridge_opt_strs+3970)
 /** tcpbridge configuration file name. */
-#define zRcName         (tcpbridge_opt_strs+4025)
+#define zRcName         (tcpbridge_opt_strs+4101)
 /** Directories to search for tcpbridge config files. */
 static char const * const apzHomeList[2] = {
-    tcpbridge_opt_strs+4021,
+    tcpbridge_opt_strs+4097,
     NULL };
 /** The tcpbridge program bug email address. */
-#define zBugsAddr       (tcpbridge_opt_strs+4038)
+#define zBugsAddr       (tcpbridge_opt_strs+4114)
 /** Clarification/explanation of what tcpbridge does. */
-#define zExplain        (tcpbridge_opt_strs+4076)
+#define zExplain        (tcpbridge_opt_strs+4152)
 /** Extra detail explaining what tcpbridge does. */
-#define zDetail         (tcpbridge_opt_strs+4208)
+#define zDetail         (tcpbridge_opt_strs+4284)
 /** The full version string for tcpbridge. */
 #define zFullVersion    (NULL)
 /* extracted from optcode.tlib near line 364 */
@@ -2408,7 +2436,7 @@ doOptVersion(tOptions* pOptions, tOptDesc* pOptDesc)
     fprintf(stderr, " (debug)");
 #endif
     fprintf(stderr, "\n");
-    fprintf(stderr, "Copyright 2013-2018 by Fred Klassen <tcpreplay at appneta dot com> - AppNeta\n");
+    fprintf(stderr, "Copyright 2013-2022 by Fred Klassen <tcpreplay at appneta dot com> - AppNeta\n");
     fprintf(stderr, "Copyright 2000-2012 by Aaron Turner <aturner at synfin dot net>\n");
     fprintf(stderr, "The entire Tcpreplay Suite is licensed under the GPLv3\n");
 #ifdef HAVE_LIBDNET
@@ -2527,7 +2555,7 @@ tOptions tcpbridgeOptions = {
       NO_EQUIVALENT, /* '-#' option index */
       10 /* index of default opt */
     },
-    53 /* full option count */, 49 /* user option count */,
+    54 /* full option count */, 50 /* user option count */,
     tcpbridge_full_usage, tcpbridge_short_usage,
     NULL, NULL,
     PKGDATADIR, tcpbridge_packager_info
@@ -2663,7 +2691,7 @@ static void bogus_function(void) {
    */
   /* referenced via tcpbridgeOptions.pzCopyright */
   puts(_("tcpbridge (tcpbridge)\n\
-Copyright (C) 2000-2018 Aaron Turner and Fred Klassen, all rights reserved.\n\
+Copyright (C) 2000-2022 Aaron Turner and Fred Klassen, all rights reserved.\n\
 This is free software. It is licensed for use, modification and\n\
 redistribution under the terms of the GNU General Public License,\n\
 version 3 or later <http://gnu.org/licenses/gpl.html>\n"));
@@ -2774,6 +2802,9 @@ with this program.  If not, see <http://www.gnu.org/licenses/>.\n"));
   puts(_("Specify the ethernet 802.1q VLAN priority"));
 
   /* referenced via tcpbridgeOptions.pOptDesc->pzText */
+  puts(_("Specify VLAN tag protocol 802.1q or 802.1ad"));
+
+  /* referenced via tcpbridgeOptions.pOptDesc->pzText */
   puts(_("Specify HDLC control value"));
 
   /* referenced via tcpbridgeOptions.pOptDesc->pzText */

+ 4 - 4
src/tcpbridge_opts.def

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it
  *   and/or modify it under the terms of the GNU General Public License as
@@ -23,12 +23,12 @@ autogen definitions options;
 
 
 copyright = {
-    date        = "2000-2018";
+    date        = "2000-2022";
     owner       = "Aaron Turner and Fred Klassen";
     eaddr       = "tcpreplay-users@lists.sourceforge.net";
     type        = gpl;
     author      = <<- EOText
-Copyright 2013-2018 Fred Klassen - AppNeta
+Copyright 2013-2022 Fred Klassen - AppNeta
 
 Copyright 2000-2012 Aaron Turner
 
@@ -366,7 +366,7 @@ flag = {
     fprintf(stderr, " (debug)");
 #endif
     fprintf(stderr, "\n");
-    fprintf(stderr, "Copyright 2013-2018 by Fred Klassen <tcpreplay at appneta dot com> - AppNeta\n");
+    fprintf(stderr, "Copyright 2013-2022 by Fred Klassen <tcpreplay at appneta dot com> - AppNeta\n");
     fprintf(stderr, "Copyright 2000-2012 by Aaron Turner <aturner at synfin dot net>\n");
     fprintf(stderr, "The entire Tcpreplay Suite is licensed under the GPLv3\n");
 #ifdef HAVE_LIBDNET

+ 34 - 32
src/tcpbridge_opts.h

@@ -19,7 +19,7 @@
  * The tcpbridge program is copyrighted and licensed
  * under the following terms:
  *
- *  Copyright (C) 2000-2018 Aaron Turner and Fred Klassen, all rights reserved.
+ *  Copyright (C) 2000-2022 Aaron Turner and Fred Klassen, all rights reserved.
  *  This is free software. It is licensed for use, modification and
  *  redistribution under the terms of the GNU General Public License,
  *  version 3 or later <http://gnu.org/licenses/gpl.html>
@@ -96,31 +96,32 @@ typedef enum {
     INDEX_OPT_ENET_VLAN_TAG             = 28,
     INDEX_OPT_ENET_VLAN_CFI             = 29,
     INDEX_OPT_ENET_VLAN_PRI             = 30,
-    INDEX_OPT_HDLC_CONTROL              = 31,
-    INDEX_OPT_HDLC_ADDRESS              = 32,
-    INDEX_OPT_USER_DLT                  = 33,
-    INDEX_OPT_USER_DLINK                = 34,
-    INDEX_OPT_DBUG                      = 35,
-    INDEX_OPT_INTF1                     = 36,
-    INDEX_OPT_INTF2                     = 37,
-    INDEX_OPT_UNIDIR                    = 38,
-    INDEX_OPT_LISTNICS                  = 39,
-    INDEX_OPT_LIMIT                     = 40,
-    INDEX_OPT_MAC                       = 41,
-    INDEX_OPT_INCLUDE                   = 42,
-    INDEX_OPT_EXCLUDE                   = 43,
-    INDEX_OPT_PID                       = 44,
-    INDEX_OPT_VERBOSE                   = 45,
-    INDEX_OPT_DECODE                    = 46,
-    INDEX_OPT_VERSION                   = 47,
-    INDEX_OPT_LESS_HELP                 = 48,
-    INDEX_OPT_HELP                      = 49,
-    INDEX_OPT_MORE_HELP                 = 50,
-    INDEX_OPT_SAVE_OPTS                 = 51,
-    INDEX_OPT_LOAD_OPTS                 = 52
+    INDEX_OPT_ENET_VLAN_PROTO           = 31,
+    INDEX_OPT_HDLC_CONTROL              = 32,
+    INDEX_OPT_HDLC_ADDRESS              = 33,
+    INDEX_OPT_USER_DLT                  = 34,
+    INDEX_OPT_USER_DLINK                = 35,
+    INDEX_OPT_DBUG                      = 36,
+    INDEX_OPT_INTF1                     = 37,
+    INDEX_OPT_INTF2                     = 38,
+    INDEX_OPT_UNIDIR                    = 39,
+    INDEX_OPT_LISTNICS                  = 40,
+    INDEX_OPT_LIMIT                     = 41,
+    INDEX_OPT_MAC                       = 42,
+    INDEX_OPT_INCLUDE                   = 43,
+    INDEX_OPT_EXCLUDE                   = 44,
+    INDEX_OPT_PID                       = 45,
+    INDEX_OPT_VERBOSE                   = 46,
+    INDEX_OPT_DECODE                    = 47,
+    INDEX_OPT_VERSION                   = 48,
+    INDEX_OPT_LESS_HELP                 = 49,
+    INDEX_OPT_HELP                      = 50,
+    INDEX_OPT_MORE_HELP                 = 51,
+    INDEX_OPT_SAVE_OPTS                 = 52,
+    INDEX_OPT_LOAD_OPTS                 = 53
 } teOptIndex;
 /** count of all options for tcpbridge */
-#define OPTION_CT    53
+#define OPTION_CT    54
 
 /**
  *  Interface defines for all options.  Replace "n" with the UPPER_CASED
@@ -228,16 +229,17 @@ typedef enum {
 #define VALUE_OPT_ENET_VLAN_PRI  0x1013
 
 #define OPT_VALUE_ENET_VLAN_PRI  (DESC(ENET_VLAN_PRI).optArg.argInt)
-#define VALUE_OPT_HDLC_CONTROL   0x1014
+#define VALUE_OPT_ENET_VLAN_PROTO 0x1014
+#define VALUE_OPT_HDLC_CONTROL   0x1015
 
 #define OPT_VALUE_HDLC_CONTROL   (DESC(HDLC_CONTROL).optArg.argInt)
-#define VALUE_OPT_HDLC_ADDRESS   0x1015
+#define VALUE_OPT_HDLC_ADDRESS   0x1016
 
 #define OPT_VALUE_HDLC_ADDRESS   (DESC(HDLC_ADDRESS).optArg.argInt)
-#define VALUE_OPT_USER_DLT       0x1016
+#define VALUE_OPT_USER_DLT       0x1017
 
 #define OPT_VALUE_USER_DLT       (DESC(USER_DLT).optArg.argInt)
-#define VALUE_OPT_USER_DLINK     0x1017
+#define VALUE_OPT_USER_DLINK     0x1018
 #define VALUE_OPT_DBUG           'd'
 #ifdef DEBUG
 #define OPT_VALUE_DBUG           (DESC(DBUG).optArg.argInt)
@@ -245,7 +247,7 @@ typedef enum {
 #define VALUE_OPT_INTF1          'i'
 #define VALUE_OPT_INTF2          'I'
 #define VALUE_OPT_UNIDIR         'u'
-#define VALUE_OPT_LISTNICS       0x1018
+#define VALUE_OPT_LISTNICS       0x1019
 #define VALUE_OPT_LIMIT          'L'
 
 #define OPT_VALUE_LIMIT          (DESC(LIMIT).optArg.argInt)
@@ -256,7 +258,7 @@ typedef enum {
 #define VALUE_OPT_VERBOSE        'v'
 #ifdef ENABLE_VERBOSE
 #define SET_OPT_VERBOSE   STMTS( \
-        DESC(VERBOSE).optActualIndex = 45; \
+        DESC(VERBOSE).optActualIndex = 46; \
         DESC(VERBOSE).optActualValue = VALUE_OPT_VERBOSE; \
         DESC(VERBOSE).fOptState &= OPTST_PERSISTENT_MASK; \
         DESC(VERBOSE).fOptState |= OPTST_SET )
@@ -269,9 +271,9 @@ typedef enum {
 /** option flag (value) for more-help-value option */
 #define VALUE_OPT_MORE_HELP     '!'
 /** option flag (value) for save-opts-value option */
-#define VALUE_OPT_SAVE_OPTS     0x1019
+#define VALUE_OPT_SAVE_OPTS     0x101A
 /** option flag (value) for load-opts-value option */
-#define VALUE_OPT_LOAD_OPTS     0x101A
+#define VALUE_OPT_LOAD_OPTS     0x101B
 #define SET_OPT_SAVE_OPTS(a)   STMTS( \
         DESC(SAVE_OPTS).fOptState &= OPTST_PERSISTENT_MASK; \
         DESC(SAVE_OPTS).fOptState |= OPTST_SET; \

+ 1 - 1
src/tcpcapinfo.1

@@ -10,7 +10,7 @@
 .ds B-Font B
 .ds I-Font I
 .ds R-Font R
-.TH tcpcapinfo 1 "01 May 2021" "Tcpreplay Suite" "User Commands"
+.TH tcpcapinfo 1 "29 Jan 2022" "Tcpreplay Suite" "User Commands"
 .\"
 .\" DO NOT EDIT THIS FILE (in-mem file)
 .\"

+ 1 - 1
src/tcpcapinfo.c

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2012 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/tcpcapinfo_opts.c

@@ -345,7 +345,7 @@ doOptVersion(tOptions* pOptions, tOptDesc* pOptDesc)
     fprintf(stderr, " (debug)");
 #endif
     fprintf(stderr, "\n");
-    fprintf(stderr, "Copyright 2013-2018 by Fred Klassen <tcpreplay at appneta dot com> - AppNeta\n");
+    fprintf(stderr, "Copyright 2013-2022 by Fred Klassen <tcpreplay at appneta dot com> - AppNeta\n");
     fprintf(stderr, "Copyright 2000-2010 by Aaron Turner <aturner at synfin dot net>\n");
     fprintf(stderr, "The entire Tcpreplay Suite is licensed under the GPLv3\n");
     exit(0);

+ 2 - 2
src/tcpcapinfo_opts.def

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 
@@ -102,7 +102,7 @@ flag = {
     fprintf(stderr, " (debug)");
 #endif
     fprintf(stderr, "\n");
-    fprintf(stderr, "Copyright 2013-2018 by Fred Klassen <tcpreplay at appneta dot com> - AppNeta\n");
+    fprintf(stderr, "Copyright 2013-2022 by Fred Klassen <tcpreplay at appneta dot com> - AppNeta\n");
     fprintf(stderr, "Copyright 2000-2010 by Aaron Turner <aturner at synfin dot net>\n");
     fprintf(stderr, "The entire Tcpreplay Suite is licensed under the GPLv3\n");
     exit(0);

+ 6 - 3
src/tcpedit/Makefile.in

@@ -185,9 +185,12 @@ host_triplet = @host@
 target_triplet = @target@
 subdir = src/tcpedit
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/libopts/m4/libopts.m4 \
-	$(top_srcdir)/libopts/m4/stdnoreturn.m4 \
-	$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
+am__aclocal_m4_deps = $(top_srcdir)/m4/libopts.m4 \
+	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+	$(top_srcdir)/m4/lt~obsolete.m4 \
+	$(top_srcdir)/m4/stdnoreturn.m4 $(top_srcdir)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \

+ 19 - 24
src/tcpedit/checksum.c

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it
  *   and/or modify it under the terms of the GNU General Public License as
@@ -34,7 +34,7 @@ static int do_checksum_math(uint16_t *, int);
  * Returns -1 on error and 0 on success, 1 on warn
  */
 int
-do_checksum(tcpedit_t *tcpedit, uint8_t *data, int proto, int payload_len) {
+do_checksum(tcpedit_t *tcpedit, uint8_t *data, int proto, int len) {
     ipv4_hdr_t *ipv4;
     ipv6_hdr_t *ipv6;
     tcp_hdr_t *tcp;
@@ -50,25 +50,20 @@ do_checksum(tcpedit_t *tcpedit, uint8_t *data, int proto, int payload_len) {
     ipv6 = NULL;
     assert(data);
 
-    if (!data || payload_len < (int)sizeof(*ipv4) || payload_len > 0xffff) {
-        tcpedit_setwarn(tcpedit, "%s", "Unable to checksum packet with no L3+ data");
-        return TCPEDIT_WARN;
+    if (!data || len <= 0) {
+        tcpedit_seterr(tcpedit, "%s", "length of data must be > 0");
+        return TCPEDIT_ERROR;
     }
 
     ipv4 = (ipv4_hdr_t *)data;
     if (ipv4->ip_v == 6) {
-        if (payload_len < (int)sizeof(*ipv6)) {
-            tcpedit_setwarn(tcpedit, "%s", "Unable to checksum IPv6 packet with insufficient data");
-            return TCPEDIT_WARN;
-        }
-
         ipv6 = (ipv6_hdr_t *)data;
         ipv4 = NULL;
 
-        proto = get_ipv6_l4proto(ipv6, payload_len + sizeof(ipv6_hdr_t));
+        proto = get_ipv6_l4proto(ipv6, len + sizeof(ipv6_hdr_t));
         dbgx(3, "layer4 proto is 0x%hx", (uint16_t)proto);
 
-        layer = (u_char*)get_layer4_v6(ipv6, payload_len + sizeof(ipv6_hdr_t));
+        layer = (u_char*)get_layer4_v6(ipv6, len + sizeof(ipv6_hdr_t));
         if (!layer) {
             tcpedit_setwarn(tcpedit, "%s", "Packet to short for checksum");
             return TCPEDIT_WARN;
@@ -77,14 +72,14 @@ do_checksum(tcpedit_t *tcpedit, uint8_t *data, int proto, int payload_len) {
         ip_hl = layer - (u_char*)data;
         dbgx(3, "ip_hl proto is 0x%d", ip_hl);
 
-        payload_len -= (ip_hl - TCPR_IPV6_H);
+        len -= (ip_hl - TCPR_IPV6_H);
     } else {
         ip_hl = ipv4->ip_hl << 2;
     }
 
     switch (proto) {
         case IPPROTO_TCP:
-            if (payload_len < (int)sizeof(tcp_hdr_t)) {
+            if (len < (int)sizeof(tcp_hdr_t)) {
                 tcpedit_setwarn(tcpedit, "%s", "Unable to checksum TCP with insufficient L4 data");
                 return TCPEDIT_WARN;
             }
@@ -104,13 +99,13 @@ do_checksum(tcpedit_t *tcpedit, uint8_t *data, int proto, int payload_len) {
             } else {
                 sum = do_checksum_math((uint16_t *)&ipv4->ip_src, 8);
             }
-            sum += ntohs(IPPROTO_TCP + payload_len);
-            sum += do_checksum_math((uint16_t *)tcp, payload_len);
+            sum += ntohs(IPPROTO_TCP + len);
+            sum += do_checksum_math((uint16_t *)tcp, len);
             tcp->th_sum = CHECKSUM_CARRY(sum);
             break;
 
         case IPPROTO_UDP:
-            if (payload_len < (int)sizeof(udp_hdr_t)) {
+            if (len < (int)sizeof(udp_hdr_t)) {
                 tcpedit_setwarn(tcpedit, "%s", "Unable to checksum UDP with insufficient L4 data");
                 return TCPEDIT_WARN;
             }
@@ -124,13 +119,13 @@ do_checksum(tcpedit_t *tcpedit, uint8_t *data, int proto, int payload_len) {
             } else {
                 sum = do_checksum_math((uint16_t *)&ipv4->ip_src, 8);
             }
-            sum += ntohs(IPPROTO_UDP + payload_len);
-            sum += do_checksum_math((uint16_t *)udp, payload_len);
+            sum += ntohs(IPPROTO_UDP + len);
+            sum += do_checksum_math((uint16_t *)udp, len);
             udp->uh_sum = CHECKSUM_CARRY(sum);
             break;
 
         case IPPROTO_ICMP:
-            if (payload_len < (int)sizeof(icmpv4_hdr_t)) {
+            if (len < (int)sizeof(icmpv4_hdr_t)) {
                 tcpedit_setwarn(tcpedit, "%s", "Unable to checksum ICMP with insufficient L4 data");
                 return TCPEDIT_WARN;
             }
@@ -140,12 +135,12 @@ do_checksum(tcpedit_t *tcpedit, uint8_t *data, int proto, int payload_len) {
                 sum = do_checksum_math((uint16_t *)&ipv6->ip_src, 32);
                 icmp->icmp_sum = CHECKSUM_CARRY(sum);
             }
-            sum += do_checksum_math((uint16_t *)icmp, payload_len);
+            sum += do_checksum_math((uint16_t *)icmp, len);
             icmp->icmp_sum = CHECKSUM_CARRY(sum);
             break;
 
         case IPPROTO_ICMP6:
-            if (payload_len < (int)sizeof(icmpv6_hdr_t)) {
+            if (len < (int)sizeof(icmpv6_hdr_t)) {
                 tcpedit_setwarn(tcpedit, "%s", "Unable to checksum ICMP6 with insufficient L4 data");
                 return TCPEDIT_WARN;
             }
@@ -154,8 +149,8 @@ do_checksum(tcpedit_t *tcpedit, uint8_t *data, int proto, int payload_len) {
             if (ipv6 != NULL) {
                 sum = do_checksum_math((u_int16_t *)&ipv6->ip_src, 32);
             }
-            sum += ntohs(IPPROTO_ICMP6 + payload_len);
-            sum += do_checksum_math((u_int16_t *)icmp6, payload_len);
+            sum += ntohs(IPPROTO_ICMP6 + len);
+            sum += do_checksum_math((u_int16_t *)icmp6, len);
             icmp6->icmp_sum = CHECKSUM_CARRY(sum);
             break;
 

+ 1 - 1
src/tcpedit/checksum.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/tcpedit/dlt.c

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/tcpedit/dlt.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 5 - 44
src/tcpedit/edit_packet.c

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 
@@ -953,9 +953,6 @@ randomize_iparp(tcpedit_t *tcpedit, struct pcap_pkthdr *pkthdr,
 {
     arp_hdr_t *arp_hdr ;
     int l2len;
-#ifdef FORCE_ALIGN
-    uint32_t iptemp;
-#endif
 
     assert(tcpedit);
     assert(pkthdr);
@@ -982,30 +979,12 @@ randomize_iparp(tcpedit_t *tcpedit, struct pcap_pkthdr *pkthdr,
         u_char *add_hdr = ((u_char *)arp_hdr) + sizeof(arp_hdr_t) +
                 arp_hdr->ar_hln;
 
-#ifdef FORCE_ALIGN
-        /* copy IP to a temporary buffer for processing */
-        memcpy(&iptemp, add_hdr, sizeof(uint32_t));
-        ip = &iptemp;
-#else
         ip = (uint32_t *)add_hdr;
-#endif
         *ip = randomize_ipv4_addr(tcpedit, *ip);
-#ifdef FORCE_ALIGN
-        memcpy(add_hdr, &iptemp, sizeof(uint32_t));
-#endif
 
         add_hdr += arp_hdr->ar_pln + arp_hdr->ar_hln;
-#ifdef FORCE_ALIGN
-        /* copy IP2 to a temporary buffer for processing */
-        memcpy(&iptemp, add_hdr, sizeof(uint32_t));
-        ip = &iptemp;
-#else
         ip = (uint32_t *)add_hdr;
-#endif
         *ip = randomize_ipv4_addr(tcpedit, *ip);
-#ifdef FORCE_ALIGN
-        memcpy(add_hdr, &iptemp, sizeof(uint32_t));
-#endif
     }
 
     return 1; /* yes we changed the packet */
@@ -1027,9 +1006,6 @@ rewrite_iparp(tcpedit_t *tcpedit, arp_hdr_t *arp_hdr, int cache_mode)
     uint32_t newip = 0;
     tcpr_cidrmap_t *cidrmap1 = NULL, *cidrmap2 = NULL;
     int didsrc = 0, diddst = 0, loop = 1;
-#ifdef FORCE_ALIGN
-    uint32_t iptemp;
-#endif
 
     assert(tcpedit);
     assert(arp_hdr);
@@ -1061,14 +1037,7 @@ rewrite_iparp(tcpedit_t *tcpedit, arp_hdr_t *arp_hdr, int cache_mode)
         add_hdr += sizeof(arp_hdr_t) + arp_hdr->ar_hln;
         ip1 = (uint32_t *)add_hdr;
         add_hdr += arp_hdr->ar_pln + arp_hdr->ar_hln;
-#ifdef FORCE_ALIGN
-        /* copy IP2 to a temporary buffer for processing */
-        memcpy(&iptemp, add_hdr, sizeof(uint32_t));
-        ip2 = &iptemp;
-#else
         ip2 = (uint32_t *)add_hdr;
-#endif
-        
 
         /* loop through the cidrmap to rewrite */
         do {
@@ -1099,31 +1068,24 @@ rewrite_iparp(tcpedit_t *tcpedit, arp_hdr_t *arp_hdr, int cache_mode)
                 }
             }
 
-#ifdef FORCE_ALIGN
-            /* copy temporary IP to IP2 location in buffer */
-            memcpy(add_hdr, &iptemp, sizeof(uint32_t));
-#endif
-
             /*
              * loop while we haven't modified both src/dst AND
              * at least one of the cidr maps have a next pointer
              */
             if ((! (diddst && didsrc)) &&
                 (! ((cidrmap1->next == NULL) && (cidrmap2->next == NULL)))) {
-                
+
                 /* increment our ptr's if possible */
                 if (cidrmap1->next != NULL)
                     cidrmap1 = cidrmap1->next;
-                
+
                 if (cidrmap2->next != NULL)
                     cidrmap2 = cidrmap2->next;
-                
             } else {
                 loop = 0;
             }
 
         } while (loop);
-        
     } else {
         warn("ARP packet isn't for IPv4!  Can't rewrite IP's");
     }
@@ -1140,8 +1102,8 @@ is_unicast_ipv4(tcpedit_t *tcpedit, uint32_t ip)
 {
     assert(tcpedit);
    
-    /* multicast/broadcast is 224.0.0.0 or greater */
-    if (ntohl(ip) > 3758096384)
+    /* multicast/broadcast is 224.0.0.0 to 239.255.255.255 */
+    if ((ntohl(ip) & 0xf0000000) == 0xe0000000)
         return 0;
         
     return 1;
@@ -1161,4 +1123,3 @@ is_multicast_ipv6(tcpedit_t *tcpedit, struct tcpr_in6_addr *addr)
 
     return 0;
 }
-

+ 10 - 1
src/tcpedit/edit_packet.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 
@@ -61,6 +61,15 @@ int rewrite_ipv4_ttl(tcpedit_t *tcpedit, ipv4_hdr_t *ip_hdr);
 
 int rewrite_ipv6_hlim(tcpedit_t *tcpedit, ipv6_hdr_t *ip6_hdr);
 
+int extract_ipv4_multicast_mac(tcpedit_t *tcpedit,
+                               uint32_t ip,
+                               u_char *mac[ETHER_ADDR_LEN]);
+
+int extract_ipv6_multicast_mac(tcpedit_t *tcpedit,
+                               struct tcpr_in6_addr *ip6,
+                               u_char *mac[ETHER_ADDR_LEN]);
+
+
 #define BROADCAST_IP 4294967295
 
 #endif

+ 6 - 2
src/tcpedit/fuzzing.c

@@ -105,7 +105,7 @@ fuzzing(tcpedit_t *tcpedit, struct pcap_pkthdr *pkthdr,
     caplen = pkthdr->caplen;
     plugin = tcpedit->dlt_ctx->encoder;
     l2len = plugin->plugin_l2len(ctx, packet, caplen);
-    l2proto = ntohs(ctx->proto);
+    l2proto = ntohs(plugin->plugin_proto(ctx, packet, caplen));
     if (l2len == -1 || caplen < l2len)
         goto done;
 
@@ -307,7 +307,11 @@ fuzzing(tcpedit_t *tcpedit, struct pcap_pkthdr *pkthdr,
     }
 
     /* in cases where 'l3data' is a working buffer, copy it back to '*pkthdr' */
-    plugin->plugin_merge_layer3(ctx, packet, caplen, l3data);
+    plugin->plugin_merge_layer3(ctx,
+                                packet,
+                                caplen,
+                                (l2proto == ETHERTYPE_IP) ? l4data : NULL,
+                                (l2proto == ETHERTYPE_IPV6) ? l4data : NULL);
 
 done:
     return packet_changed;

+ 1 - 1
src/tcpedit/parse_args.c

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/tcpedit/parse_args.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/tcpedit/plugins.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 251 - 123
src/tcpedit/plugins/dlt_en10mb/en10mb.c

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 
@@ -124,6 +124,8 @@ dlt_en10mb_init(tcpeditdlt_t *ctx)
     config->vlan_pri = 255;
     config->vlan_cfi = 255;
     
+    /* VLAN protocol = 802.1q */
+    config->vlan_proto = ETHERTYPE_VLAN;
     
     return TCPEDIT_OK; /* success */
 }
@@ -341,7 +343,7 @@ dlt_en10mb_parse_opts(tcpeditdlt_t *ctx)
      * Validate 802.1q vlan args and populate tcpedit->vlan_record
      */
     if (HAVE_OPT(ENET_VLAN)) {
-        if (strcmp(OPT_ARG(ENET_VLAN), "add") == 0) { // add or change
+        if (strcmp(OPT_ARG(ENET_VLAN), "add") == 0) { /* add or change */
             config->vlan = TCPEDIT_VLAN_ADD;
         } else if (strcmp(OPT_ARG(ENET_VLAN), "del") == 0) {
             config->vlan = TCPEDIT_VLAN_DEL;
@@ -373,6 +375,18 @@ dlt_en10mb_parse_opts(tcpeditdlt_t *ctx)
             if (HAVE_OPT(ENET_VLAN_CFI))
                 config->vlan_cfi = OPT_VALUE_ENET_VLAN_CFI;
             }
+
+            if (HAVE_OPT(ENET_VLAN_PROTO)) {
+                if (strcasecmp(OPT_ARG(ENET_VLAN_PROTO), "802.1q") == 0) {
+                    config->vlan_proto = ETHERTYPE_VLAN;
+                } else if (strcasecmp(OPT_ARG(ENET_VLAN_PROTO), "802.1ad") == 0) {
+                    config->vlan_proto = ETHERTYPE_Q_IN_Q;
+                } else {
+                    tcpedit_seterr(ctx->tcpedit, "VLAN protocol \"%s\" is invalid",
+                                   OPT_ARG(ENET_VLAN_PROTO));
+                    return TCPEDIT_ERROR;
+                }
+            }
         }
     }
 
@@ -387,50 +401,80 @@ dlt_en10mb_parse_opts(tcpeditdlt_t *ctx)
 int 
 dlt_en10mb_decode(tcpeditdlt_t *ctx, const u_char *packet, const int pktlen)
 {
-    struct tcpr_ethernet_hdr *eth = NULL;
-    struct tcpr_802_1q_hdr *vlan = NULL;
-    en10mb_extra_t *extra = NULL;
+    struct tcpr_ethernet_hdr *eth_hdr;
+    uint32_t pkt_len = pktlen;
+    en10mb_extra_t *extra;
+    uint32_t vlan_offset;
+    uint16_t protcol;
+    uint32_t l2offset;
+    uint32_t l2len;
+    int res;
     
     assert(ctx);
     assert(packet);
-    if (pktlen < TCPR_802_3_H)
+
+    extra = (en10mb_extra_t *)ctx->decoded_extra;
+    if (ctx->decoded_extra_size < sizeof(*extra)) {
+        tcpedit_seterr(ctx->tcpedit, "Decode extra size too short! %d < %u",
+                       ctx->decoded_extra_size, sizeof(*extra));
         return TCPEDIT_ERROR;
+    }
 
-    /* get our src & dst address */
-    eth = (struct tcpr_ethernet_hdr *)packet;
-    memcpy(&(ctx->dstaddr.ethernet), eth, ETHER_ADDR_LEN);
-    memcpy(&(ctx->srcaddr.ethernet), &(eth->ether_shost), ETHER_ADDR_LEN);
+    res = get_l2len_protocol(packet,
+                             pkt_len,
+                             dlt_value,
+                             &protcol,
+                             &l2len,
+                             &l2offset,
+                             &vlan_offset);
+    if (res == -1)
+        return TCPEDIT_ERROR;
 
-    extra = (en10mb_extra_t *)ctx->decoded_extra;
-    if (ctx->decoded_extra_size < sizeof(*extra))
+    if (pkt_len < TCPR_802_3_H + l2offset)
         return TCPEDIT_ERROR;
 
-    extra->vlan = 0;
-    
-    /* get the L3 protocol type  & L2 len*/
-    switch (ntohs(eth->ether_type)) {
-        case ETHERTYPE_VLAN:
-            if (pktlen < TCPR_802_1Q_H)
-                    return TCPEDIT_ERROR;
+    eth_hdr = (struct tcpr_ethernet_hdr*)(packet + l2offset);
+    protcol = ntohs(eth_hdr->ether_type);
+    memcpy(&(ctx->dstaddr.ethernet), eth_hdr, ETHER_ADDR_LEN);
+    memcpy(&(ctx->srcaddr.ethernet), &(eth_hdr->ether_shost), ETHER_ADDR_LEN);
+    ctx->proto_vlan_tag = ntohs(eth_hdr->ether_type);
+
+    if (vlan_offset != 0) {
+        if (vlan_offset == l2offset + sizeof(*eth_hdr)) {
+            vlan_hdr_t *vlan_hdr;
+            vlan_hdr = (vlan_hdr_t*)(packet + vlan_offset);
+            if (pkt_len < vlan_offset + sizeof(*vlan_hdr)) {
+                tcpedit_seterr(ctx->tcpedit,
+                               "Frame is too short for VLAN headers! %d < %d",
+                               pktlen,
+                               vlan_offset + sizeof(*vlan_hdr));
+                return TCPEDIT_ERROR;
+            }
 
-            vlan = (struct tcpr_802_1q_hdr *)packet;
-            ctx->proto = vlan->vlan_len;
-            
-            /* Get VLAN tag info */
             extra->vlan = 1;
-            /* must use these mask values, rather then what's in the tcpr.h since it assumes you're shifting */
-            extra->vlan_tag = vlan->vlan_priority_c_vid & 0x0FFF;
-            extra->vlan_pri = vlan->vlan_priority_c_vid & 0xE000;
-            extra->vlan_cfi = vlan->vlan_priority_c_vid & 0x1000;
-            ctx->l2len = TCPR_802_1Q_H;
-            break;
-        
-        /* we don't properly handle SNAP encoding */
-        default:
-            ctx->proto = eth->ether_type;
-            ctx->l2len = TCPR_802_3_H;
+            extra->vlan_offset = vlan_offset;
+            extra->vlan_proto = ntohs(vlan_hdr->vlan_tpid);
+            extra->vlan_tag = htons(vlan_hdr->vlan_tci) & TCPR_802_1Q_VIDMASK;
+            extra->vlan_pri = htons(vlan_hdr->vlan_tci) & TCPR_802_1Q_PRIMASK;
+            extra->vlan_cfi = htons(vlan_hdr->vlan_tci) & TCPR_802_1Q_CFIMASK;
+
+        } else {
+            /* VLAN headers cannot be after MPLS. There are other protocols such
+             * Cisco Metatdata that could be before VLAN, but we don't support
+             * them yet.
+             */
+            return TCPEDIT_ERROR;
+        }
+    } else {
+        extra->vlan = 0;
+        extra->vlan_offset = l2offset + sizeof(*eth_hdr);
+        extra->vlan_proto = protcol;
     }
 
+    ctx->proto = ntohs(protcol);
+    ctx->l2offset = l2offset;
+    ctx->l2len = l2len;
+
     return TCPEDIT_OK; /* success */
 }
 
@@ -441,18 +485,18 @@ dlt_en10mb_decode(tcpeditdlt_t *ctx, const u_char *packet, const int pktlen)
 int 
 dlt_en10mb_encode(tcpeditdlt_t *ctx, u_char *packet, int pktlen, tcpr_dir_t dir)
 {
-    tcpeditdlt_plugin_t *plugin = NULL;
-    struct tcpr_ethernet_hdr *eth = NULL;
-    struct tcpr_802_1q_hdr *vlan = NULL;
-    en10mb_config_t *config = NULL;
-    en10mb_extra_t *extra = NULL;
-    
+    struct tcpr_802_1q_hdr *vlan_hdr;
+    struct tcpr_ethernet_hdr *eth;
+    tcpeditdlt_plugin_t *plugin;
+    en10mb_config_t *config;
+    en10mb_extra_t *extra;
     int newl2len = 0;
+    int oldl2len = 0;
 
     assert(ctx);
     assert(packet);
 
-    if (pktlen < TCPR_802_1Q_H) {
+    if (pktlen < TCPR_802_3_H) {
         tcpedit_seterr(ctx->tcpedit, 
                 "Unable to process packet #" COUNTER_SPEC " since it is less then 14 bytes.", 
                 ctx->tcpedit->runtime.packetnum);
@@ -473,24 +517,35 @@ dlt_en10mb_encode(tcpeditdlt_t *ctx, u_char *packet, int pktlen, tcpr_dir_t dir)
 
     /* figure out the new layer2 length, first for the case: ethernet -> ethernet? */
     if (ctx->decoder->dlt == dlt_value) {
-        if ((ctx->l2len == TCPR_802_1Q_H && config->vlan == TCPEDIT_VLAN_OFF) ||
-            (config->vlan == TCPEDIT_VLAN_ADD)) {
-            newl2len = TCPR_802_1Q_H;
-        } else if ((ctx->l2len == TCPR_802_3_H && config->vlan == TCPEDIT_VLAN_OFF) ||
-            (config->vlan == TCPEDIT_VLAN_DEL)) {
-            newl2len = TCPR_802_3_H;
+        switch (config->vlan) {
+        case TCPEDIT_VLAN_ADD:
+            oldl2len = extra->vlan_offset;
+            newl2len = extra->vlan_offset + sizeof(vlan_hdr_t);
+            break;
+        case TCPEDIT_VLAN_DEL:
+            if (extra->vlan) {
+                oldl2len = extra->vlan_offset + sizeof(vlan_hdr_t);
+                newl2len = extra->vlan_offset;
+            }
+            break;
+        case TCPEDIT_VLAN_OFF:
+            if (extra->vlan) {
+                oldl2len = extra->vlan_offset;
+                newl2len = extra->vlan_offset;
+            }
+            break;
         }
-    } 
-    
+    }
+
     /* newl2len for some other DLT -> ethernet */
-    else {
-        /* if add a vlan then 18, else 14 bytes */
-        newl2len = config->vlan == TCPEDIT_VLAN_ADD ? TCPR_802_1Q_H : TCPR_802_3_H;
+    else if (config->vlan == TCPEDIT_VLAN_ADD) {
+        /* if add a vlan then 18, */
+        newl2len = TCPR_802_1Q_H;
     }
 
-    if (pktlen < newl2len) {
+    if (pktlen < newl2len || pktlen + newl2len - ctx->l2len > MAXPACKET) {
         tcpedit_seterr(ctx->tcpedit,
-                "Unable to process packet #" COUNTER_SPEC " since its new length less then %d bytes.",
+                "Unable to process packet #" COUNTER_SPEC " since its new length is %d bytes.",
                 ctx->tcpedit->runtime.packetnum, newl2len);
         return TCPEDIT_ERROR;
     }
@@ -503,19 +558,22 @@ dlt_en10mb_encode(tcpeditdlt_t *ctx, u_char *packet, int pktlen, tcpr_dir_t dir)
     }
 
     /* Make space for our new L2 header */
-    if (newl2len != ctx->l2len) {
-        if (pktlen + (newl2len - ctx->l2len) > MAXPACKET)
-            errx(-1, "New frame too big, new length %d exceeds %d",
+    if (newl2len > 0 && newl2len != oldl2len) {
+        if (pktlen + (newl2len - oldl2len) > MAXPACKET) {
+            tcpedit_seterr(ctx->tcpedit, "New frame too big, new length %d exceeds %d",
                     pktlen + (newl2len - ctx->l2len), MAXPACKET);
+            return TCPEDIT_ERROR;
+        }
 
-        memmove(packet + newl2len, packet + ctx->l2len, pktlen - ctx->l2len);
+        memmove(packet + newl2len, packet + oldl2len, pktlen - oldl2len);
     }
 
     /* update the total packet length */
-    pktlen += newl2len - ctx->l2len;
+    pktlen += newl2len - oldl2len;
+    ctx->l2len += newl2len - oldl2len;
     
-    /* always set the src & dst address as the first 12 bytes */
-    eth = (struct tcpr_ethernet_hdr *)packet;
+    /* set the src & dst address as the first 12 bytes */
+    eth = (struct tcpr_ethernet_hdr *)(packet + ctx->l2offset);
     
     if (dir == TCPR_DIR_C2S) {
         /* copy user supplied SRC MAC if provided or from original packet */
@@ -529,6 +587,7 @@ dlt_en10mb_encode(tcpeditdlt_t *ctx, u_char *packet, int pktlen, tcpr_dir_t dir)
                 memcpy(eth->ether_shost, ctx->srcaddr.ethernet, ETHER_ADDR_LEN);                
             }
         } else if (ctx->addr_type == ETHERNET) {
+            extra->src_modified = memcmp(eth->ether_shost, ctx->srcaddr.ethernet, ETHER_ADDR_LEN);
             memcpy(eth->ether_shost, ctx->srcaddr.ethernet, ETHER_ADDR_LEN);
         } else {
             tcpedit_seterr(ctx->tcpedit, "%s", "Please provide a source address");
@@ -545,6 +604,7 @@ dlt_en10mb_encode(tcpeditdlt_t *ctx, u_char *packet, int pktlen, tcpr_dir_t dir)
                 memcpy(eth->ether_dhost, ctx->dstaddr.ethernet, ETHER_ADDR_LEN);
             }
         } else if (ctx->addr_type == ETHERNET) {
+            extra->dst_modified = memcmp(eth->ether_dhost, ctx->dstaddr.ethernet, ETHER_ADDR_LEN);
             memcpy(eth->ether_dhost, ctx->dstaddr.ethernet, ETHER_ADDR_LEN);
         } else {
             tcpedit_seterr(ctx->tcpedit, "%s", "Please provide a destination address");
@@ -562,21 +622,20 @@ dlt_en10mb_encode(tcpeditdlt_t *ctx, u_char *packet, int pktlen, tcpr_dir_t dir)
                 memcpy(eth->ether_shost, ctx->srcaddr.ethernet, ETHER_ADDR_LEN);
             }
         } else if (ctx->addr_type == ETHERNET) {
-            memcpy(eth->ether_shost, ctx->srcaddr.ethernet, ETHER_ADDR_LEN);            
+            memcpy(eth->ether_shost, ctx->srcaddr.ethernet, ETHER_ADDR_LEN);
         } else {
             tcpedit_seterr(ctx->tcpedit, "%s", "Please provide a source address");
             return TCPEDIT_ERROR;
         }
 
-        
-        /* copy user supplied DMAC MAC if provided or from original packet */        
+        /* copy user supplied DMAC MAC if provided or from original packet */
         if (config->mac_mask & TCPEDIT_MAC_MASK_DMAC2) {
             if ((ctx->addr_type == ETHERNET && 
                 ((ctx->skip_broadcast && is_unicast_ethernet(ctx, ctx->dstaddr.ethernet)) || !ctx->skip_broadcast))
                 || ctx->addr_type != ETHERNET) {
                 memcpy(eth->ether_dhost, config->intf2_dmac, ETHER_ADDR_LEN);
             } else {
-                memcpy(eth->ether_dhost, ctx->dstaddr.ethernet, ETHER_ADDR_LEN);                
+                memcpy(eth->ether_dhost, ctx->dstaddr.ethernet, ETHER_ADDR_LEN);
             }
         } else if (ctx->addr_type == ETHERNET) {
             memcpy(eth->ether_dhost, ctx->dstaddr.ethernet, ETHER_ADDR_LEN);
@@ -623,48 +682,48 @@ dlt_en10mb_encode(tcpeditdlt_t *ctx, u_char *packet, int pktlen, tcpr_dir_t dir)
       }
     }
 
-    if (newl2len == TCPR_802_3_H) {
-        /* all we need for 802.3 is the proto */
-        eth->ether_type = ctx->proto;
-        
-    } else if (newl2len == TCPR_802_1Q_H) {
-        /* VLAN tags need a bit more */
-        vlan = (struct tcpr_802_1q_hdr *)packet;
-        vlan->vlan_len = ctx->proto;
-        vlan->vlan_tpi = htons(ETHERTYPE_VLAN);
-        
+    if (config->vlan == TCPEDIT_VLAN_ADD
+            || (config->vlan == TCPEDIT_VLAN_OFF && extra->vlan)) {
+        vlan_hdr = (struct tcpr_802_1q_hdr *)(packet + extra->vlan_offset);
+        if (config->vlan == TCPEDIT_VLAN_ADD) {
+            struct tcpr_ethernet_hdr *eth_hdr;
+            eth_hdr = (struct tcpr_ethernet_hdr*)(packet + ctx->l2offset);
+            eth_hdr->ether_type = htons(config->vlan_proto);
+            vlan_hdr->vlan_tpid = htons(ctx->proto_vlan_tag);
+        }
+
         /* are we changing VLAN info? */
         if (config->vlan_tag < 65535) {
-            vlan->vlan_priority_c_vid = 
+            vlan_hdr->vlan_tci =
                 htons((uint16_t)config->vlan_tag & TCPR_802_1Q_VIDMASK);
         } else if (extra->vlan) {
-            vlan->vlan_priority_c_vid = extra->vlan_tag;
+            vlan_hdr->vlan_tci = htons(extra->vlan_tag);
         } else {
             tcpedit_seterr(ctx->tcpedit, "%s", "Non-VLAN tagged packet requires --enet-vlan-tag");
             return TCPEDIT_ERROR;
         }
         
         if (config->vlan_pri < 255) {
-            vlan->vlan_priority_c_vid += htons((uint16_t)config->vlan_pri << 13);
+            vlan_hdr->vlan_tci += htons((uint16_t)config->vlan_pri << 13);
         } else if (extra->vlan) {
-            vlan->vlan_priority_c_vid += extra->vlan_pri;
+            vlan_hdr->vlan_tci += htons(extra->vlan_pri);
         } else {
             tcpedit_seterr(ctx->tcpedit, "%s", "Non-VLAN tagged packet requires --enet-vlan-pri");
             return TCPEDIT_ERROR;
         }
-            
+
         if (config->vlan_cfi < 255) {
-            vlan->vlan_priority_c_vid += htons((uint16_t)config->vlan_cfi << 12);
+            vlan_hdr->vlan_tci += htons((uint16_t)config->vlan_cfi << 12);
         } else if (extra->vlan) {
-            vlan->vlan_priority_c_vid += extra->vlan_cfi;
+            vlan_hdr->vlan_tci += htons(extra->vlan_cfi);
         } else {
             tcpedit_seterr(ctx->tcpedit, "%s", "Non-VLAN tagged packet requires --enet-vlan-cfi");
-            return TCPEDIT_ERROR;            
-        }        
-        
-    } else {
-        tcpedit_seterr(ctx->tcpedit, "Unsupported new layer 2 length: %d", newl2len);
-        return TCPEDIT_ERROR;
+            return TCPEDIT_ERROR;
+        }
+
+    } else if (config->vlan == TCPEDIT_VLAN_DEL && newl2len > 0) {
+        /* all we need for 802.3 is the proto */
+        eth->ether_type = htons(extra->vlan_proto);
     }
 
     return pktlen;
@@ -677,30 +736,31 @@ dlt_en10mb_encode(tcpeditdlt_t *ctx, u_char *packet, int pktlen, tcpr_dir_t dir)
 int 
 dlt_en10mb_proto(tcpeditdlt_t *ctx, const u_char *packet, const int pktlen)
 {
-    struct tcpr_ethernet_hdr *eth = NULL;
-    struct tcpr_802_1q_hdr *vlan = NULL;
-    
+    uint32_t _U_ vlan_offset;
+    uint16_t ether_type;
+    uint32_t _U_ l2offset;
+    uint32_t _U_ l2len;
+    int res;
+
     assert(ctx);
     assert(packet);
-    if (pktlen < (int) sizeof(*eth)) {
+    if (pktlen < (int) sizeof(struct tcpr_ethernet_hdr)) {
         tcpedit_seterr(ctx->tcpedit, "Ethernet packet length too short: %d",
                 pktlen);
         return TCPEDIT_ERROR;
     }
-    
-    eth = (struct tcpr_ethernet_hdr *)packet;
-    switch (ntohs(eth->ether_type)) {
-        case ETHERTYPE_VLAN:
-            vlan = (struct tcpr_802_1q_hdr *)packet;
-            return vlan->vlan_len;
-            break;
-        
-        default:
-            return eth->ether_type;
-            break;
-    }
 
-    return TCPEDIT_ERROR;
+    res = get_l2len_protocol(packet,
+                             pktlen,
+                             dlt_value,
+                             &ether_type,
+                             &l2len,
+                             &l2offset,
+                             &vlan_offset);
+    if (res == -1)
+        return TCPEDIT_ERROR;
+
+    return htons(ether_type);
 }
 
 /*
@@ -721,24 +781,100 @@ dlt_en10mb_get_layer3(tcpeditdlt_t *ctx, u_char *packet, const int pktlen)
 }
 
 /*
+ * Modify MAC address if IPv4 address is Multicast (#563)
+ * ip: 32-bit IP address in network order
+ * mac: pointer to packet ethernet source or destination address (6 bytes)
+ */
+static void dlt_en10mb_ipv4_multicast_mac_update(const uint32_t ip,
+                                                 uint8_t mac[])
+{
+    /* only modify multicast packets */
+    if ((ntohl(ip) & 0xf0000000) != 0xe0000000)
+        return;
+
+    mac[2] = (ntohl(ip) & 0x7fffff);
+    mac[0] =0x01;
+    mac[1] =0x0;
+    mac[2] =0x5e;
+}
+
+/*
+ * Modify MAC address if IPv4 address is Multicast (#563)
+ * ip6: 128-bit IPv6 address in network order
+ * mac: pointer to packet ethernet source or destination address (6 bytes)
+ */
+static void dlt_en10mb_ipv6_multicast_mac_update(const struct tcpr_in6_addr *ip6,
+                                                 uint8_t mac[])
+{
+    /* only modify multicast packets */
+    if (ip6->tcpr_s6_addr[0] == 0xff)
+        return;
+
+    mac[0] = 0x33;
+    mac[1] = 0x33;
+    mac[2] = ip6->tcpr_s6_addr[12];
+    mac[3] = ip6->tcpr_s6_addr[13];
+    mac[4] = ip6->tcpr_s6_addr[14];
+    mac[5] = ip6->tcpr_s6_addr[15];
+}
+
+/*
  * function merges the packet (containing L2 and old L3) with the l3data buffer
  * containing the new l3 data.  Note, if L2 % 4 == 0, then they're pointing to the
  * same buffer, otherwise there was a memcpy involved on strictly aligned architectures
  * like SPARC
  */
 u_char *
-dlt_en10mb_merge_layer3(tcpeditdlt_t *ctx, u_char *packet, const int pktlen, u_char *l3data)
+dlt_en10mb_merge_layer3(tcpeditdlt_t *ctx,
+                        u_char *packet,
+                        const int pktlen,
+                        u_char *ipv4_data,
+                        u_char *ipv6_data)
 {
+    en10mb_extra_t *extra;
+    struct tcpr_ethernet_hdr *eth;
+
     int l2len;
+
     assert(ctx);
     assert(packet);
-    assert(l3data);
     
     l2len = dlt_en10mb_l2len(ctx, packet, pktlen);
     if (l2len == -1 || pktlen < l2len)
         return NULL;
-    
-    return tcpedit_dlt_l3data_merge(ctx, packet, pktlen, l3data, l2len);
+
+    assert(ctx->decoded_extra_size == sizeof(*extra));
+    extra = (en10mb_extra_t *)ctx->decoded_extra;
+    eth = (struct tcpr_ethernet_hdr *)(packet + ctx->l2offset);
+    assert(eth);
+    /* modify source/destination MAC if source/destination IP is multicast */
+    if (ipv4_data) {
+        if ((size_t)pktlen >= sizeof(*eth) + sizeof(struct tcpr_ipv4_hdr)) {
+            struct tcpr_ipv4_hdr *ip_hdr = (struct tcpr_ipv4_hdr*)ipv4_data;
+            if (!extra->src_modified)
+                dlt_en10mb_ipv4_multicast_mac_update(ip_hdr->ip_src.s_addr,
+                                                     eth->ether_shost);
+
+            if (!extra->dst_modified)
+                dlt_en10mb_ipv4_multicast_mac_update(ip_hdr->ip_dst.s_addr,
+                                                     eth->ether_dhost);
+        }
+        return tcpedit_dlt_l3data_merge(ctx, packet, pktlen, ipv4_data, l2len);
+    } else if (ipv6_data) {
+        if ((size_t)pktlen >= sizeof(*eth) + sizeof(struct tcpr_ipv6_hdr)) {
+            struct tcpr_ipv6_hdr *ip6_hdr = (struct tcpr_ipv6_hdr*)ipv6_data;
+            if (!extra->src_modified)
+                dlt_en10mb_ipv6_multicast_mac_update(&ip6_hdr->ip_src,
+                                                     eth->ether_shost);
+
+            if (!extra->dst_modified)
+                dlt_en10mb_ipv6_multicast_mac_update(&ip6_hdr->ip_dst,
+                                                     eth->ether_dhost);
+        }
+        return tcpedit_dlt_l3data_merge(ctx, packet, pktlen, ipv6_data, l2len);
+    }
+
+    return NULL;
 }
 
 /*
@@ -778,31 +914,23 @@ int
 dlt_en10mb_l2len(tcpeditdlt_t *ctx, const u_char *packet, const int pktlen)
 {
     int l2len;
-    uint16_t ether_type;
     
     assert(ctx);
     assert(packet);
 
-    l2len = sizeof(eth_hdr_t);
-    if (pktlen < l2len)
-        return -1;
-
-    ether_type = ntohs(((eth_hdr_t*)packet)->ether_type);
-    while (ether_type == ETHERTYPE_VLAN) {
-        if (pktlen < l2len + (int)sizeof(vlan_hdr_t))
-             return -1;
-
-         vlan_hdr_t *vlan_hdr = (vlan_hdr_t*)(packet + l2len);
-         ether_type = ntohs(vlan_hdr->vlan_tpid);
-         l2len += 4;
+    if (pktlen < (int)sizeof(eth_hdr_t)) {
+        tcpedit_seterr(ctx->tcpedit, "dlt_en10mb_l2len: pktlen=%u is less than size of Ethernet header",
+                       pktlen);
+        return TCPEDIT_ERROR;
     }
 
+    l2len = get_l2len(packet, pktlen, dlt_value);
     if (l2len > 0) {
         if (pktlen < l2len) {
             /* can happen if fuzzing is enabled */
             tcpedit_seterr(ctx->tcpedit, "dlt_en10mb_l2len: pktlen=%u is less than l2len=%u",
                     pktlen, l2len);
-            return -1;
+            return TCPEDIT_ERROR;
         }
 
         return l2len;

+ 6 - 2
src/tcpedit/plugins/dlt_en10mb/en10mb.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 
@@ -42,7 +42,11 @@ int dlt_en10mb_decode(tcpeditdlt_t *ctx, const u_char *packet, const int pktlen)
 int dlt_en10mb_encode(tcpeditdlt_t *ctx, u_char *packet, int pktlen, tcpr_dir_t dir);
 int dlt_en10mb_proto(tcpeditdlt_t *ctx, const u_char *packet, const int pktlen);
 u_char *dlt_en10mb_get_layer3(tcpeditdlt_t *ctx, u_char *packet, const int pktlen);
-u_char *dlt_en10mb_merge_layer3(tcpeditdlt_t *ctx, u_char *packet, const int pktlen, u_char *l3data);
+u_char *dlt_en10mb_merge_layer3(tcpeditdlt_t *ctx,
+                                u_char *packet,
+                                const int pktlen,
+                                u_char *ipv4_data,
+                                u_char *ipv6_data);
 int dlt_en10mb_l2len(tcpeditdlt_t *ctx, const u_char *packet, const int pktlen);
 u_char *dlt_en10mb_get_mac(tcpeditdlt_t *ctx, tcpeditdlt_mac_type_t mac, const u_char *packet, const int pktlen);
 

+ 1 - 1
src/tcpedit/plugins/dlt_en10mb/en10mb_api.c

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 1 - 1
src/tcpedit/plugins/dlt_en10mb/en10mb_api.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 27 - 1
src/tcpedit/plugins/dlt_en10mb/en10mb_opts.def

@@ -93,7 +93,14 @@ ethernet headers or remove the 802.1q VLAN tag information.
 @table @bullet
 @item
 @var{add}
-Rewrites the existing 802.3 ethernet header as an 802.1q VLAN header
+Adds an 802.1q VLAN header to the existing 802.3 ethernet header. If
+a VLAN header already exists, a new VLAN header is added outside of the
+existing header.
+
+Note that you will be allowed to run this option multiple times to create
+more than 2 VLAN headers, however those packets will be valid. At most
+you should have 2 X 802.1q VLAN tags, or outer an 802.1ad and an inner 802.1q
+VLAN tag.
 @item
 @var{del}
 Rewrites the existing 802.1q VLAN header as an 802.3 ethernet header
@@ -129,3 +136,22 @@ flag = {
     arg-range   = "0->7"; /* one byte */
     doc         = "";
 };
+
+flag = {
+    name        = enet-vlan-proto;
+    max         = 1;
+    flag-must   = enet-vlan;
+    descrip     = "Specify VLAN tag protocol 802.1q or 802.1ad";
+    arg-type    = string;
+    doc         = <<- EOText
+Allows you to specify the protocol of the added VLAN tags.
+@table @bullet
+@item
+@var{802.1q}
+Specifies that 802.1q VLAN headers are to be added. This is the default.
+@item
+@var{802.1ad}
+Specifies that 802.1ad Q-in-Q VLAN headers are to be added. To make valid packets,
+input packets must already have 802.1q VLAN headers.
+EOText;
+};

+ 8 - 1
src/tcpedit/plugins/dlt_en10mb/en10mb_types.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 
@@ -30,9 +30,13 @@ extern "C" {
 typedef struct {
     int vlan; /* set to 1 for vlan_ fields being filled out */
     
+    u_int32_t vlan_offset;
     u_int16_t vlan_tag;
     u_int16_t vlan_pri;
     u_int16_t vlan_cfi;
+    u_int16_t vlan_proto;
+    bool src_modified;
+    bool dst_modified;
 } en10mb_extra_t;
 
 typedef enum {
@@ -84,6 +88,9 @@ typedef struct {
     u_int16_t vlan_tag;
     u_int8_t  vlan_pri;
     u_int8_t  vlan_cfi;
+
+    /* 802.1Q/802.1ad VLAN Q-in-Q - 0 means 802.1Q */
+    u_int16_t vlan_proto;
 } en10mb_config_t;
 
 

+ 8 - 4
src/tcpedit/plugins/dlt_hdlc/hdlc.c

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 
@@ -339,18 +339,22 @@ dlt_hdlc_get_layer3(tcpeditdlt_t *ctx, u_char *packet, const int pktlen)
  * like SPARC
  */
 u_char *
-dlt_hdlc_merge_layer3(tcpeditdlt_t *ctx, u_char *packet, const int pktlen, u_char *l3data)
+dlt_hdlc_merge_layer3(tcpeditdlt_t *ctx,
+                      u_char *packet,
+                      const int pktlen,
+                      u_char *ipv4_data,
+                      u_char *ipv6_data)
 {
     int l2len;
     assert(ctx);
     assert(packet);
-    assert(l3data);
+    assert(ipv4_data || ipv6_data);
     
     l2len = dlt_hdlc_l2len(ctx, packet, pktlen);
     if (l2len == -1 || pktlen < l2len)
         return NULL;
     
-    return tcpedit_dlt_l3data_merge(ctx, packet, pktlen, l3data, l2len);
+    return tcpedit_dlt_l3data_merge(ctx, packet, pktlen, ipv4_data ?: ipv6_data, l2len);
 }
 
 /* 

+ 6 - 2
src/tcpedit/plugins/dlt_hdlc/hdlc.h

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 
@@ -33,7 +33,11 @@ int dlt_hdlc_decode(tcpeditdlt_t *ctx, const u_char *packet, const int pktlen);
 int dlt_hdlc_encode(tcpeditdlt_t *ctx, u_char *packet, int pktlen, tcpr_dir_t dir);
 int dlt_hdlc_proto(tcpeditdlt_t *ctx, const u_char *packet, const int pktlen);
 u_char *dlt_hdlc_get_layer3(tcpeditdlt_t *ctx, u_char *packet, const int pktlen);
-u_char *dlt_hdlc_merge_layer3(tcpeditdlt_t *ctx, u_char *packet, const int pktlen, u_char *l3data);
+u_char *dlt_hdlc_merge_layer3(tcpeditdlt_t *ctx,
+                              u_char *packet,
+                              const int pktlen,
+                              u_char *ipv4_data,
+                              u_char *ipv6_data);
 tcpeditdlt_l2addr_type_t dlt_hdlc_l2addr_type(void);
 int dlt_hdlc_l2len(tcpeditdlt_t *ctx, const u_char *packet, const int pktlen);
 u_char *dlt_hdlc_get_mac(tcpeditdlt_t *ctx, tcpeditdlt_mac_type_t mac, const u_char *packet, const int pktlen);

+ 1 - 1
src/tcpedit/plugins/dlt_hdlc/hdlc_api.c

@@ -2,7 +2,7 @@
 
 /*
  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
- *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
+ *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  *
  *   The Tcpreplay Suite of tools is free software: you can redistribute it 
  *   and/or modify it under the terms of the GNU General Public License as 

+ 0 - 0
src/tcpedit/plugins/dlt_hdlc/hdlc_api.h


Some files were not shown because too many files changed in this diff