diff options
Diffstat (limited to '')
77 files changed, 1831 insertions, 740 deletions
@@ -1,3 +1,33 @@ +7.0.5 -- 2024-04-23 + +Security #6905: base64: off-by-three overflow in DecodeBase64() (7.0.x backport)(CVE 2024-32664) +Security #6901: http2: timeout logging headers (7.0.x backport)(CVE 2024-32663) +Security #6893: http2: oom on copying compressed headers (7.0.x backport)(CVE 2024-32663) +Security #6677: ip-defrag: packet can be considered complete even with holes (7.0.x backport)(CVE 2024-32867) +Security #6673: ip defrag: final overlapping packet can lead to "hole" in re-assembled data (7.0.x backport)(CVE 2024-32867) +Security #6672: ip defrag: re-assembly error in bsd policy (7.0.x backport)(CVE 2024-32867) +Bug #6970: streaming buffer: heap overflows in StreamingBufferAppend()/StreamingBufferAppendNoTrack() (7.0.x backport) +Bug #6966: improve handling of content encoding: gzip but request_body not actually compressed (7.0.x backport) +Bug #6958: Assert: BUG_ON(id <= 0 || id > (int)thread_store.threads_size); (7.0.x backport) +Bug #6949: detect/http.response_body: false positive because not enforcing direction to_client (7.0.x backport) +Bug #6945: defrag: reassembled packet can have wrong datatype (7.0.x backport) +Bug #6923: jsonbuilder: serializes Rust f64 NaNs to an invalid literal (7.0.x backport) +Bug #6919: pcre2 compile warning (7.0.x backport) +Bug #6907: Fix stats key (7.0.x backport) +Bug #6890: detect: slowdown in rule parsing (7.0.x backport) +Bug #6884: rust: clippy 1.77 warning (7.0.x backport) +Bug #6882: Detect: ipopts keyword misfires (7.0.x backport) +Bug #6872: dpdk: fix compatibility issues for ice cards (7.0.x backport) +Bug #6863: BUG_ON triggered from TmThreadsInjectFlowById (7.0.x backport) +Bug #6859: fast_pattern specification in base64_data shouldn't be allowed (7.0.x backport) +Bug #6727: stream: stream.drop-invalid drops valid traffic (7.0.x backport) +Bug #6679: datasets: discard datasets that hit the memcap while loading correctly (7.0.x backport) +Bug #6313: napatech: display HBA deprecation notice only once +Optimization #6880: conf: quadratic complexity in yaml loader (7.0.x backport) +Feature #6947: pcap: datalink type 229 not (yet) supported in module PcapFile (7.0.x backport) +Feature #6696: dpdk: power saving mode (7.0.x backport) +Documentation #6912: manpages: use consistant date based on release and/or git commits (7.0.x backport) + 7.0.4 -- 2024-03-19 Security #6868: eve: excessive ssh long banner logging (7.0.x backport)(CVE 2024-28870) diff --git a/Makefile.in b/Makefile.in index ef89168..910bd55 100644 --- a/Makefile.in +++ b/Makefile.in @@ -317,6 +317,7 @@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ POW_LIB = @POW_LIB@ RANLIB = @RANLIB@ +RELEASE_DATE = @RELEASE_DATE@ RUSTC = @RUSTC@ RUSTUP_HOME_PATH = @RUSTUP_HOME_PATH@ RUST_FEATURES = @RUST_FEATURES@ @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.71 for suricata 7.0.4. +# Generated by GNU Autoconf 2.71 for suricata 7.0.5. # # # Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation, @@ -682,8 +682,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='suricata' PACKAGE_TARNAME='suricata' -PACKAGE_VERSION='7.0.4' -PACKAGE_STRING='suricata 7.0.4' +PACKAGE_VERSION='7.0.5' +PACKAGE_STRING='suricata 7.0.5' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -745,6 +745,7 @@ e_logcertsdir e_logfilesdir e_rundir e_logdir +RELEASE_DATE HAVE_GIT_CMD HAS_FUZZLDFLAGS_FALSE HAS_FUZZLDFLAGS_TRUE @@ -1661,7 +1662,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 suricata 7.0.4 to adapt to many kinds of systems. +\`configure' configures suricata 7.0.5 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1732,7 +1733,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of suricata 7.0.4:";; + short | recursive ) echo "Configuration of suricata 7.0.5:";; esac cat <<\_ACEOF @@ -1975,7 +1976,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -suricata configure 7.0.4 +suricata configure 7.0.5 generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. @@ -2567,7 +2568,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 suricata $as_me 7.0.4, which was +It was created by suricata $as_me 7.0.5, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw @@ -4062,7 +4063,7 @@ fi # Define the identity of the package. PACKAGE='suricata' - VERSION='7.0.4' + VERSION='7.0.5' printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h @@ -22946,7 +22947,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 suricata $as_me 7.0.4, which was +This file was extended by suricata $as_me 7.0.5, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -23014,7 +23015,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -suricata config.status 7.0.4 +suricata config.status 7.0.5 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" @@ -24917,19 +24918,19 @@ fi fi pkg_failed=no -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for htp >= 0.5.47" >&5 -printf %s "checking for htp >= 0.5.47... " >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for htp >= 0.5.48" >&5 +printf %s "checking for htp >= 0.5.48... " >&6; } if test -n "$LIBHTPMINVERSION_CFLAGS"; then pkg_cv_LIBHTPMINVERSION_CFLAGS="$LIBHTPMINVERSION_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"htp >= 0.5.47\""; } >&5 - ($PKG_CONFIG --exists --print-errors "htp >= 0.5.47") 2>&5 + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"htp >= 0.5.48\""; } >&5 + ($PKG_CONFIG --exists --print-errors "htp >= 0.5.48") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - pkg_cv_LIBHTPMINVERSION_CFLAGS=`$PKG_CONFIG --cflags "htp >= 0.5.47" 2>/dev/null` + pkg_cv_LIBHTPMINVERSION_CFLAGS=`$PKG_CONFIG --cflags "htp >= 0.5.48" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes @@ -24941,12 +24942,12 @@ if test -n "$LIBHTPMINVERSION_LIBS"; then pkg_cv_LIBHTPMINVERSION_LIBS="$LIBHTPMINVERSION_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"htp >= 0.5.47\""; } >&5 - ($PKG_CONFIG --exists --print-errors "htp >= 0.5.47") 2>&5 + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"htp >= 0.5.48\""; } >&5 + ($PKG_CONFIG --exists --print-errors "htp >= 0.5.48") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - pkg_cv_LIBHTPMINVERSION_LIBS=`$PKG_CONFIG --libs "htp >= 0.5.47" 2>/dev/null` + pkg_cv_LIBHTPMINVERSION_LIBS=`$PKG_CONFIG --libs "htp >= 0.5.48" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes @@ -24967,9 +24968,9 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - LIBHTPMINVERSION_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "htp >= 0.5.47" 2>&1` + LIBHTPMINVERSION_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "htp >= 0.5.48" 2>&1` else - LIBHTPMINVERSION_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "htp >= 0.5.47" 2>&1` + LIBHTPMINVERSION_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "htp >= 0.5.48" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBHTPMINVERSION_PKG_ERRORS" >&5 @@ -25060,7 +25061,7 @@ printf "%s\n" "yes" >&6; } fi if test "$libhtp_devver_found" = "no"; then echo - echo " ERROR! libhtp was found but it is neither >= 0.5.47, nor the dev 0.5.X" + echo " ERROR! libhtp was found but it is neither >= 0.5.48, nor the dev 0.5.X" echo exit 1 fi @@ -34295,14 +34296,8 @@ else fi -# get revision - if test -f ./revision; then - REVISION=`cat ./revision` - -printf "%s\n" "#define REVISION ${REVISION}" >>confdefs.h - - else - # Extract the first word of "git", so it can be a program name with args. +# get git revision and last commit date + # Extract the first word of "git", so it can be a program name with args. set dummy git; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } @@ -34348,18 +34343,33 @@ printf "%s\n" "no" >&6; } fi - if test "$HAVE_GIT_CMD" != "no"; then - if test -d .git ; then - REVISION=`git rev-parse --short HEAD` - DATE=`git log -1 --date=short --pretty=format:%cd` - REVISION="$REVISION $DATE" + if test "$HAVE_GIT_CMD" != "no"; then + if test -e .git ; then + REVISION=`git rev-parse --short HEAD` + LAST_COMMIT_DATE=`git log -1 --date=short --pretty=format:%cd` + REVISION="$REVISION $LAST_COMMIT_DATE" printf "%s\n" "#define REVISION ${REVISION}" >>confdefs.h - fi fi fi +# Get the release date. If LAST_COMMIT_DATE was set in the previous +# step, use it, otherwise parse it from the ChangeLog. + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for release date" >&5 +printf %s "checking for release date... " >&6; } + if test "x$LAST_COMMIT_DATE" != "x"; then + RELEASE_DATE=$LAST_COMMIT_DATE + else + RELEASE_DATE=`awk '/^[0-9\.]+ -- [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/ { print $3; exit }' $srcdir/ChangeLog` + if test "x$RELEASE_DATE" = "x"; then + as_fn_error $? "Failed to determine release date" "$LINENO" 5 + fi + fi + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${RELEASE_DATE}" >&5 +printf "%s\n" "${RELEASE_DATE}" >&6; } + + # get MAJOR_MINOR version for embedding in configuration file. MAJOR_MINOR=`expr "${PACKAGE_VERSION}" : "\([0-9]\+\.[0-9]\+\).*"` @@ -35272,7 +35282,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 suricata $as_me 7.0.4, which was +This file was extended by suricata $as_me 7.0.5, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -35340,7 +35350,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -suricata config.status 7.0.4 +suricata config.status 7.0.5 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 72f333e..c46ff40 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ - AC_INIT([suricata],[7.0.4]) + AC_INIT([suricata],[7.0.5]) m4_ifndef([AM_SILENT_RULES], [m4_define([AM_SILENT_RULES],[])])AM_SILENT_RULES([yes]) AC_CONFIG_HEADERS([src/autoconf.h]) AC_CONFIG_SRCDIR([src/suricata.c]) @@ -1575,12 +1575,12 @@ echo exit 1 fi - PKG_CHECK_MODULES(LIBHTPMINVERSION, [htp >= 0.5.47],[libhtp_minver_found="yes"],[libhtp_minver_found="no"]) + PKG_CHECK_MODULES(LIBHTPMINVERSION, [htp >= 0.5.48],[libhtp_minver_found="yes"],[libhtp_minver_found="no"]) if test "$libhtp_minver_found" = "no"; then PKG_CHECK_MODULES(LIBHTPDEVVERSION, [htp = 0.5.X],[libhtp_devver_found="yes"],[libhtp_devver_found="no"]) if test "$libhtp_devver_found" = "no"; then echo - echo " ERROR! libhtp was found but it is neither >= 0.5.47, nor the dev 0.5.X" + echo " ERROR! libhtp was found but it is neither >= 0.5.48, nor the dev 0.5.X" echo exit 1 fi @@ -2480,21 +2480,30 @@ return 0; AM_CONDITIONAL([HAS_FUZZLDFLAGS], [test "x$has_sanitizefuzzer" = "xyes"]) -# get revision - if test -f ./revision; then - REVISION=`cat ./revision` - AC_DEFINE_UNQUOTED([REVISION],[${REVISION}],[Git revision]) +# get git revision and last commit date + AC_PATH_PROG(HAVE_GIT_CMD, git, "no") + if test "$HAVE_GIT_CMD" != "no"; then + if [ test -e .git ]; then + REVISION=`git rev-parse --short HEAD` + LAST_COMMIT_DATE=`git log -1 --date=short --pretty=format:%cd` + REVISION="$REVISION $LAST_COMMIT_DATE" + AC_DEFINE_UNQUOTED([REVISION],[${REVISION}],[Git revision]) + fi + fi + +# Get the release date. If LAST_COMMIT_DATE was set in the previous +# step, use it, otherwise parse it from the ChangeLog. + AC_MSG_CHECKING([for release date]) + if test "x$LAST_COMMIT_DATE" != "x"; then + RELEASE_DATE=$LAST_COMMIT_DATE else - AC_PATH_PROG(HAVE_GIT_CMD, git, "no") - if test "$HAVE_GIT_CMD" != "no"; then - if [ test -d .git ]; then - REVISION=`git rev-parse --short HEAD` - DATE=`git log -1 --date=short --pretty=format:%cd` - REVISION="$REVISION $DATE" - AC_DEFINE_UNQUOTED([REVISION],[${REVISION}],[Git revision]) - fi + RELEASE_DATE=`awk '/^[[0-9\.]]+ -- [[0-9]][[0-9]][[0-9]][[0-9]]-[[0-9]][[0-9]]-[[0-9]][[0-9]]/ { print $3; exit }' $srcdir/ChangeLog` + if test "x$RELEASE_DATE" = "x"; then + AC_MSG_ERROR([Failed to determine release date]) fi fi + AC_MSG_RESULT([${RELEASE_DATE}]) + AC_SUBST(RELEASE_DATE) # get MAJOR_MINOR version for embedding in configuration file. MAJOR_MINOR=`expr "${PACKAGE_VERSION}" : "\([[0-9]]\+\.[[0-9]]\+\).*"` diff --git a/contrib/Makefile.in b/contrib/Makefile.in index a94fe1f..2efb9c9 100644 --- a/contrib/Makefile.in +++ b/contrib/Makefile.in @@ -288,6 +288,7 @@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ POW_LIB = @POW_LIB@ RANLIB = @RANLIB@ +RELEASE_DATE = @RELEASE_DATE@ RUSTC = @RUSTC@ RUSTUP_HOME_PATH = @RUSTUP_HOME_PATH@ RUST_FEATURES = @RUST_FEATURES@ diff --git a/contrib/file_processor/Action/Makefile.in b/contrib/file_processor/Action/Makefile.in index b2b3f8c..2755fb0 100644 --- a/contrib/file_processor/Action/Makefile.in +++ b/contrib/file_processor/Action/Makefile.in @@ -230,6 +230,7 @@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ POW_LIB = @POW_LIB@ RANLIB = @RANLIB@ +RELEASE_DATE = @RELEASE_DATE@ RUSTC = @RUSTC@ RUSTUP_HOME_PATH = @RUSTUP_HOME_PATH@ RUST_FEATURES = @RUST_FEATURES@ diff --git a/contrib/file_processor/Makefile.in b/contrib/file_processor/Makefile.in index fd42c12..fc85aff 100644 --- a/contrib/file_processor/Makefile.in +++ b/contrib/file_processor/Makefile.in @@ -288,6 +288,7 @@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ POW_LIB = @POW_LIB@ RANLIB = @RANLIB@ +RELEASE_DATE = @RELEASE_DATE@ RUSTC = @RUSTC@ RUSTUP_HOME_PATH = @RUSTUP_HOME_PATH@ RUST_FEATURES = @RUST_FEATURES@ diff --git a/contrib/file_processor/Processor/Makefile.in b/contrib/file_processor/Processor/Makefile.in index 7ed0063..9da38d0 100644 --- a/contrib/file_processor/Processor/Makefile.in +++ b/contrib/file_processor/Processor/Makefile.in @@ -230,6 +230,7 @@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ POW_LIB = @POW_LIB@ RANLIB = @RANLIB@ +RELEASE_DATE = @RELEASE_DATE@ RUSTC = @RUSTC@ RUSTUP_HOME_PATH = @RUSTUP_HOME_PATH@ RUST_FEATURES = @RUST_FEATURES@ diff --git a/doc/Makefile.in b/doc/Makefile.in index 0bec752..b44a221 100644 --- a/doc/Makefile.in +++ b/doc/Makefile.in @@ -320,6 +320,7 @@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ POW_LIB = @POW_LIB@ RANLIB = @RANLIB@ +RELEASE_DATE = @RELEASE_DATE@ RUSTC = @RUSTC@ RUSTUP_HOME_PATH = @RUSTUP_HOME_PATH@ RUST_FEATURES = @RUST_FEATURES@ diff --git a/doc/userguide/Makefile.am b/doc/userguide/Makefile.am index bd15792..8ffede5 100644 --- a/doc/userguide/Makefile.am +++ b/doc/userguide/Makefile.am @@ -74,6 +74,7 @@ userguide.pdf: _build/latex/Suricata.pdf pdf: userguide.pdf _build/man: manpages/suricata.rst manpages/suricatasc.rst manpages/suricatactl.rst manpages/suricatactl-filestore.rst + RELEASE_DATE=$(RELEASE_DATE) \ sysconfdir=$(sysconfdir) \ localstatedir=$(localstatedir) \ version=$(PACKAGE_VERSION) \ diff --git a/doc/userguide/Makefile.in b/doc/userguide/Makefile.in index 2d0a204..2884e25 100644 --- a/doc/userguide/Makefile.in +++ b/doc/userguide/Makefile.in @@ -262,6 +262,7 @@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ POW_LIB = @POW_LIB@ RANLIB = @RANLIB@ +RELEASE_DATE = @RELEASE_DATE@ RUSTC = @RUSTC@ RUSTUP_HOME_PATH = @RUSTUP_HOME_PATH@ RUST_FEATURES = @RUST_FEATURES@ @@ -640,6 +641,7 @@ uninstall-man: uninstall-man1 @SPHINX_BUILD_TRUE@pdf: userguide.pdf @SPHINX_BUILD_TRUE@_build/man: manpages/suricata.rst manpages/suricatasc.rst manpages/suricatactl.rst manpages/suricatactl-filestore.rst +@SPHINX_BUILD_TRUE@ RELEASE_DATE=$(RELEASE_DATE) \ @SPHINX_BUILD_TRUE@ sysconfdir=$(sysconfdir) \ @SPHINX_BUILD_TRUE@ localstatedir=$(localstatedir) \ @SPHINX_BUILD_TRUE@ version=$(PACKAGE_VERSION) \ diff --git a/doc/userguide/capture-hardware/dpdk.rst b/doc/userguide/capture-hardware/dpdk.rst index 1b9ecae..6be7278 100644 --- a/doc/userguide/capture-hardware/dpdk.rst +++ b/doc/userguide/capture-hardware/dpdk.rst @@ -146,3 +146,41 @@ management and worker CPU set. - worker-cpu-set: cpu: [ 2,4,6,8 ] ... + +Interrupt (power-saving) mode +----------------------------- + +The DPDK is traditionally recognized for its polling mode operation. +In this mode, CPU cores are continuously querying for packets from +the Network Interface Card (NIC). While this approach offers benefits like +reduced latency and improved performance, it might not be the most efficient +in scenarios with sporadic or low traffic. +The constant polling can lead to unnecessary CPU consumption. +To address this, DPDK offers an `interrupt` mode. + +The obvious advantage that interrupt mode brings is power efficiency. +So far in our tests, we haven't observed a decrease in performance. Suricata's +performance has actually seen a slight improvement. +The (IPS runmode) users should be aware that interrupts can +introduce non-deterministic latency. However, the latency should never be +higher than in other (e.g. AF_PACKET/AF_XDP/...) capture methods. + +Interrupt mode in DPDK can be configured on a per-interface basis. +This allows for a hybrid setup where some workers operate in polling mode, +while others utilize the interrupt mode. +The configuration for the interrupt mode can be found and modified in the +DPDK section of the suricata.yaml file. + +Below is a sample configuration that demonstrates how to enable the interrupt mode for a specific interface: + +:: + + ... + dpdk: + eal-params: + proc-type: primary + + interfaces: + - interface: 0000:3b:00.0 + interrupt-mode: true + threads: 4 diff --git a/doc/userguide/conf.py b/doc/userguide/conf.py index d043a28..959744e 100644 --- a/doc/userguide/conf.py +++ b/doc/userguide/conf.py @@ -19,6 +19,10 @@ import re import subprocess import datetime +# Set 'today'. This will be used as the man page date. If an empty +# string todays date will be used. +today = os.environ.get('RELEASE_DATE', '') + on_rtd = os.environ.get('READTHEDOCS', None) == 'True' # If extensions (or modules to document with autodoc) are in another directory, @@ -67,7 +71,7 @@ try: version = os.environ.get('version', None) if not version: version = re.search( - "AC_INIT\(\[suricata\],\s*\[(.*)?\]\)", + r"AC_INIT\(\[suricata\],\s*\[(.*)?\]\)", open("../../configure.ac").read()).groups()[0] if not version: version = "unknown" diff --git a/doc/userguide/rules/base64-keywords.rst b/doc/userguide/rules/base64-keywords.rst index 7daf0c2..190fdb5 100644 --- a/doc/userguide/rules/base64-keywords.rst +++ b/doc/userguide/rules/base64-keywords.rst @@ -62,3 +62,5 @@ Example:: alert http any any -> any any (msg:"Example"; content:"somestring"; http_uri; \ base64_decode:bytes 8, offset 1, relative; \ base64_data; content:"test"; sid:10001; rev:1;) + +.. note:: ``fast_pattern`` is ineffective with ``base64_data`` diff --git a/doc/userguide/suricata.1 b/doc/userguide/suricata.1 index 9564f6a..423e397 100644 --- a/doc/userguide/suricata.1 +++ b/doc/userguide/suricata.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "SURICATA" "1" "Mar 19, 2024" "7.0.4" "Suricata" +.TH "SURICATA" "1" "2024-04-23" "7.0.5" "Suricata" .SH NAME suricata \- Suricata .SH SYNOPSIS diff --git a/doc/userguide/suricatactl-filestore.1 b/doc/userguide/suricatactl-filestore.1 index 40688f8..4ec5a9f 100644 --- a/doc/userguide/suricatactl-filestore.1 +++ b/doc/userguide/suricatactl-filestore.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "SURICATACTL-FILESTORE" "1" "Mar 19, 2024" "7.0.4" "Suricata" +.TH "SURICATACTL-FILESTORE" "1" "2024-04-23" "7.0.5" "Suricata" .SH NAME suricatactl-filestore \- Perform actions on filestore .SH SYNOPSIS diff --git a/doc/userguide/suricatactl.1 b/doc/userguide/suricatactl.1 index 90b0801..a0a1725 100644 --- a/doc/userguide/suricatactl.1 +++ b/doc/userguide/suricatactl.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "SURICATACTL" "1" "Mar 19, 2024" "7.0.4" "Suricata" +.TH "SURICATACTL" "1" "2024-04-23" "7.0.5" "Suricata" .SH NAME suricatactl \- Suricata Control .SH SYNOPSIS diff --git a/doc/userguide/suricatasc.1 b/doc/userguide/suricatasc.1 index 40a4bc5..4f54787 100644 --- a/doc/userguide/suricatasc.1 +++ b/doc/userguide/suricatasc.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "SURICATASC" "1" "Mar 19, 2024" "7.0.4" "Suricata" +.TH "SURICATASC" "1" "2024-04-23" "7.0.5" "Suricata" .SH NAME suricatasc \- Tool to interact via unix socket .SH SYNOPSIS diff --git a/doc/userguide/userguide.pdf b/doc/userguide/userguide.pdf Binary files differindex 2ff1757..4b38931 100644 --- a/doc/userguide/userguide.pdf +++ b/doc/userguide/userguide.pdf diff --git a/ebpf/Makefile.in b/ebpf/Makefile.in index 41b736b..6269cb8 100644 --- a/ebpf/Makefile.in +++ b/ebpf/Makefile.in @@ -230,6 +230,7 @@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ POW_LIB = @POW_LIB@ RANLIB = @RANLIB@ +RELEASE_DATE = @RELEASE_DATE@ RUSTC = @RUSTC@ RUSTUP_HOME_PATH = @RUSTUP_HOME_PATH@ RUST_FEATURES = @RUST_FEATURES@ diff --git a/etc/Makefile.in b/etc/Makefile.in index 4eede76..05e1827 100644 --- a/etc/Makefile.in +++ b/etc/Makefile.in @@ -262,6 +262,7 @@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ POW_LIB = @POW_LIB@ RANLIB = @RANLIB@ +RELEASE_DATE = @RELEASE_DATE@ RUSTC = @RUSTC@ RUSTUP_HOME_PATH = @RUSTUP_HOME_PATH@ RUST_FEATURES = @RUST_FEATURES@ diff --git a/python/Makefile.in b/python/Makefile.in index d1e7f1d..f1e2bdd 100644 --- a/python/Makefile.in +++ b/python/Makefile.in @@ -242,6 +242,7 @@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ POW_LIB = @POW_LIB@ RANLIB = @RANLIB@ +RELEASE_DATE = @RELEASE_DATE@ RUSTC = @RUSTC@ RUSTUP_HOME_PATH = @RUSTUP_HOME_PATH@ RUST_FEATURES = @RUST_FEATURES@ diff --git a/qa/Makefile.in b/qa/Makefile.in index be4a02b..0e54fa2 100644 --- a/qa/Makefile.in +++ b/qa/Makefile.in @@ -288,6 +288,7 @@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ POW_LIB = @POW_LIB@ RANLIB = @RANLIB@ +RELEASE_DATE = @RELEASE_DATE@ RUSTC = @RUSTC@ RUSTUP_HOME_PATH = @RUSTUP_HOME_PATH@ RUST_FEATURES = @RUST_FEATURES@ diff --git a/qa/coccinelle/Makefile.in b/qa/coccinelle/Makefile.in index da64830..6f27b0f 100644 --- a/qa/coccinelle/Makefile.in +++ b/qa/coccinelle/Makefile.in @@ -230,6 +230,7 @@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ POW_LIB = @POW_LIB@ RANLIB = @RANLIB@ +RELEASE_DATE = @RELEASE_DATE@ RUSTC = @RUSTC@ RUSTUP_HOME_PATH = @RUSTUP_HOME_PATH@ RUST_FEATURES = @RUST_FEATURES@ diff --git a/rules/Makefile.in b/rules/Makefile.in index a461b14..6f51ced 100644 --- a/rules/Makefile.in +++ b/rules/Makefile.in @@ -261,6 +261,7 @@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ POW_LIB = @POW_LIB@ RANLIB = @RANLIB@ +RELEASE_DATE = @RELEASE_DATE@ RUSTC = @RUSTC@ RUSTUP_HOME_PATH = @RUSTUP_HOME_PATH@ RUST_FEATURES = @RUST_FEATURES@ diff --git a/rust/Cargo.toml.in b/rust/Cargo.toml.in index 0bac7e1..347391b 100644 --- a/rust/Cargo.toml.in +++ b/rust/Cargo.toml.in @@ -2,6 +2,7 @@ name = "suricata" version = "@PACKAGE_VERSION@" edition = "2021" +rust-version = "1.63.0" [workspace] members = [".", "./derive"] diff --git a/rust/Makefile.in b/rust/Makefile.in index 40e6b76..dc54872 100644 --- a/rust/Makefile.in +++ b/rust/Makefile.in @@ -234,6 +234,7 @@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ POW_LIB = @POW_LIB@ RANLIB = @RANLIB@ +RELEASE_DATE = @RELEASE_DATE@ RUSTC = @RUSTC@ RUSTUP_HOME_PATH = @RUSTUP_HOME_PATH@ RUST_FEATURES = @RUST_FEATURES@ $(am__append_1) $(am__append_2) \ diff --git a/rust/derive/Cargo.toml b/rust/derive/Cargo.toml index c697f68..4a749d7 100644 --- a/rust/derive/Cargo.toml +++ b/rust/derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "suricata-derive" -version = "7.0.4" +version = "7.0.5" edition = "2021" [lib] diff --git a/rust/src/asn1/mod.rs b/rust/src/asn1/mod.rs index 4b77b0c..7496d44 100644 --- a/rust/src/asn1/mod.rs +++ b/rust/src/asn1/mod.rs @@ -33,7 +33,7 @@ pub struct Asn1<'a>(Vec<BerObject<'a>>); enum Asn1DecodeError { InvalidKeywordParameter, MaxFrames, - BerError(Err<der_parser::error::BerError>), + BerError, } /// Enumeration of Asn1 checks @@ -282,8 +282,8 @@ impl From<std::num::TryFromIntError> for Asn1DecodeError { } impl From<Err<der_parser::error::BerError>> for Asn1DecodeError { - fn from(e: Err<der_parser::error::BerError>) -> Asn1DecodeError { - Asn1DecodeError::BerError(e) + fn from(_e: Err<der_parser::error::BerError>) -> Asn1DecodeError { + Asn1DecodeError::BerError } } diff --git a/rust/src/dhcp/logger.rs b/rust/src/dhcp/logger.rs index 3c86b1b..a9d8d98 100644 --- a/rust/src/dhcp/logger.rs +++ b/rust/src/dhcp/logger.rs @@ -89,7 +89,7 @@ impl DHCPLogger { js.set_uint("id", header.txid as u64)?; js.set_string("client_mac", - &format_addr_hex(&header.clienthw.to_vec()))?; + &format_addr_hex(&header.clienthw))?; js.set_string("assigned_ip", &dns_print_addr(&header.yourip))?; if self.extended { diff --git a/rust/src/http2/detect.rs b/rust/src/http2/detect.rs index 52b4119..67933b6 100644 --- a/rust/src/http2/detect.rs +++ b/rust/src/http2/detect.rs @@ -23,6 +23,7 @@ use crate::core::Direction; use crate::detect::uint::{detect_match_uint, DetectUintData}; use std::ffi::CStr; use std::str::FromStr; +use std::rc::Rc; fn http2_tx_has_frametype( tx: &mut HTTP2Transaction, direction: Direction, value: u8, @@ -404,7 +405,7 @@ fn http2_frames_get_header_firstvalue<'a>( for frame in frames { if let Some(blocks) = http2_header_blocks(frame) { for block in blocks.iter() { - if block.name == name.as_bytes() { + if block.name.as_ref() == name.as_bytes() { return Ok(&block.value); } } @@ -428,7 +429,7 @@ pub fn http2_frames_get_header_value_vec( for frame in frames { if let Some(blocks) = http2_header_blocks(frame) { for block in blocks.iter() { - if block.name == name.as_bytes() { + if block.name.as_ref() == name.as_bytes() { if found == 0 { vec.extend_from_slice(&block.value); found = 1; @@ -465,7 +466,7 @@ fn http2_frames_get_header_value<'a>( for frame in frames { if let Some(blocks) = http2_header_blocks(frame) { for block in blocks.iter() { - if block.name == name.as_bytes() { + if block.name.as_ref() == name.as_bytes() { if found == 0 { single = Ok(&block.value); found = 1; @@ -920,8 +921,8 @@ fn http2_tx_set_header(state: &mut HTTP2State, name: &[u8], input: &[u8]) { }; let mut blocks = Vec::new(); let b = parser::HTTP2FrameHeaderBlock { - name: name.to_vec(), - value: input.to_vec(), + name: Rc::new(name.to_vec()), + value: Rc::new(input.to_vec()), error: parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess, sizeupdate: 0, }; @@ -1063,15 +1064,15 @@ mod tests { }; let mut blocks = Vec::new(); let b = parser::HTTP2FrameHeaderBlock { - name: "Host".as_bytes().to_vec(), - value: "abc.com".as_bytes().to_vec(), + name: "Host".as_bytes().to_vec().into(), + value: "abc.com".as_bytes().to_vec().into(), error: parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess, sizeupdate: 0, }; blocks.push(b); let b2 = parser::HTTP2FrameHeaderBlock { - name: "Host".as_bytes().to_vec(), - value: "efg.net".as_bytes().to_vec(), + name: "Host".as_bytes().to_vec().into(), + value: "efg.net".as_bytes().to_vec().into(), error: parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess, sizeupdate: 0, }; diff --git a/rust/src/http2/http2.rs b/rust/src/http2/http2.rs index b62ccb9..23aaf26 100644 --- a/rust/src/http2/http2.rs +++ b/rust/src/http2/http2.rs @@ -208,7 +208,7 @@ impl HTTP2Transaction { let mut authority = None; let mut host = None; for block in blocks { - if block.name == b"content-encoding" { + if block.name.as_ref() == b"content-encoding" { self.decoder.http2_encoding_fromvec(&block.value, dir); } else if block.name.eq_ignore_ascii_case(b":authority") { authority = Some(&block.value); diff --git a/rust/src/http2/logger.rs b/rust/src/http2/logger.rs index d25f852..a117a54 100644 --- a/rust/src/http2/logger.rs +++ b/rust/src/http2/logger.rs @@ -19,7 +19,8 @@ use super::http2::{HTTP2Frame, HTTP2FrameTypeData, HTTP2Transaction}; use super::parser; use crate::jsonbuilder::{JsonBuilder, JsonError}; use std; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; +use std::rc::Rc; #[derive(Hash, PartialEq, Eq, Debug)] enum HeaderName { @@ -35,10 +36,20 @@ fn log_http2_headers<'a>( blocks: &'a [parser::HTTP2FrameHeaderBlock], js: &mut JsonBuilder, common: &mut HashMap<HeaderName, &'a Vec<u8>>, ) -> Result<(), JsonError> { + let mut logged_headers = HashSet::new(); for block in blocks { - js.start_object()?; + // delay js.start_object() because we skip suplicate headers match block.error { parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess => { + if Rc::strong_count(&block.name) > 2 { + // more than one reference in headers table + current headers + let ptr = Rc::as_ptr(&block.name) as usize; + if !logged_headers.insert(ptr) { + // only log once + continue; + } + } + js.start_object()?; js.set_string_from_bytes("name", &block.name)?; js.set_string_from_bytes("value", &block.value)?; if let Ok(name) = std::str::from_utf8(&block.name) { @@ -66,9 +77,11 @@ fn log_http2_headers<'a>( } } parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate => { + js.start_object()?; js.set_uint("table_size_update", block.sizeupdate)?; } _ => { + js.start_object()?; js.set_string("error", &block.error.to_string())?; } } diff --git a/rust/src/http2/parser.rs b/rust/src/http2/parser.rs index adabeb2..1a46437 100644 --- a/rust/src/http2/parser.rs +++ b/rust/src/http2/parser.rs @@ -30,6 +30,7 @@ use nom7::sequence::tuple; use nom7::{Err, IResult}; use std::fmt; use std::str::FromStr; +use std::rc::Rc; #[repr(u8)] #[derive(Clone, Copy, PartialEq, Eq, FromPrimitive, Debug)] @@ -295,8 +296,8 @@ fn http2_frame_header_static(n: u64, dyn_headers: &HTTP2DynTable) -> Option<HTTP }; if !name.is_empty() { return Some(HTTP2FrameHeaderBlock { - name: name.as_bytes().to_vec(), - value: value.as_bytes().to_vec(), + name: Rc::new(name.as_bytes().to_vec()), + value: Rc::new(value.as_bytes().to_vec()), error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess, sizeupdate: 0, }); @@ -304,23 +305,23 @@ fn http2_frame_header_static(n: u64, dyn_headers: &HTTP2DynTable) -> Option<HTTP //use dynamic table if n == 0 { return Some(HTTP2FrameHeaderBlock { - name: Vec::new(), - value: Vec::new(), + name: Rc::new(Vec::new()), + value: Rc::new(Vec::new()), error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeIndex0, sizeupdate: 0, }); } else if dyn_headers.table.len() + HTTP2_STATIC_HEADERS_NUMBER < n as usize { return Some(HTTP2FrameHeaderBlock { - name: Vec::new(), - value: Vec::new(), + name: Rc::new(Vec::new()), + value: Rc::new(Vec::new()), error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeNotIndexed, sizeupdate: 0, }); } else { let indyn = dyn_headers.table.len() - (n as usize - HTTP2_STATIC_HEADERS_NUMBER); let headcopy = HTTP2FrameHeaderBlock { - name: dyn_headers.table[indyn].name.to_vec(), - value: dyn_headers.table[indyn].value.to_vec(), + name: dyn_headers.table[indyn].name.clone(), + value: dyn_headers.table[indyn].value.clone(), error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess, sizeupdate: 0, }; @@ -348,8 +349,10 @@ impl fmt::Display for HTTP2HeaderDecodeStatus { #[derive(Clone, Debug)] pub struct HTTP2FrameHeaderBlock { - pub name: Vec<u8>, - pub value: Vec<u8>, + // Use Rc reference counted so that indexed headers do not get copied. + // Otherwise, this leads to quadratic complexity in memory occupation. + pub name: Rc<Vec<u8>>, + pub value: Rc<Vec<u8>>, pub error: HTTP2HeaderDecodeStatus, pub sizeupdate: u64, } @@ -391,7 +394,7 @@ fn http2_parse_headers_block_literal_common<'a>( ) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> { let (i3, name, error) = if index == 0 { match http2_parse_headers_block_string(input) { - Ok((r, n)) => Ok((r, n, HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess)), + Ok((r, n)) => Ok((r, Rc::new(n), HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess)), Err(e) => Err(e), } } else { @@ -403,7 +406,7 @@ fn http2_parse_headers_block_literal_common<'a>( )), None => Ok(( input, - Vec::new(), + Rc::new(Vec::new()), HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeNotIndexed, )), } @@ -413,7 +416,7 @@ fn http2_parse_headers_block_literal_common<'a>( i4, HTTP2FrameHeaderBlock { name, - value, + value: Rc::new(value), error, sizeupdate: 0, }, @@ -435,8 +438,8 @@ fn http2_parse_headers_block_literal_incindex<'a>( match r { Ok((r, head)) => { let headcopy = HTTP2FrameHeaderBlock { - name: head.name.to_vec(), - value: head.value.to_vec(), + name: head.name.clone(), + value: head.value.clone(), error: head.error, sizeupdate: 0, }; @@ -556,8 +559,8 @@ fn http2_parse_headers_block_dynamic_size<'a>( return Ok(( i3, HTTP2FrameHeaderBlock { - name: Vec::new(), - value: Vec::new(), + name: Rc::new(Vec::new()), + value: Rc::new(Vec::new()), error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate, sizeupdate: maxsize2, }, @@ -614,8 +617,8 @@ fn http2_parse_headers_blocks<'a>( // if we error from http2_parse_var_uint, we keep the first parsed headers if err.code == ErrorKind::LengthValue { blocks.push(HTTP2FrameHeaderBlock { - name: Vec::new(), - value: Vec::new(), + name: Rc::new(Vec::new()), + value: Rc::new(Vec::new()), error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeIntegerOverflow, sizeupdate: 0, }); @@ -765,8 +768,8 @@ mod tests { match r0 { Ok((remainder, hd)) => { // Check the first message. - assert_eq!(hd.name, ":method".as_bytes().to_vec()); - assert_eq!(hd.value, "GET".as_bytes().to_vec()); + assert_eq!(hd.name, ":method".as_bytes().to_vec().into()); + assert_eq!(hd.value, "GET".as_bytes().to_vec().into()); // And we should have no bytes left. assert_eq!(remainder.len(), 0); } @@ -782,8 +785,8 @@ mod tests { match r1 { Ok((remainder, hd)) => { // Check the first message. - assert_eq!(hd.name, "accept".as_bytes().to_vec()); - assert_eq!(hd.value, "*/*".as_bytes().to_vec()); + assert_eq!(hd.name, "accept".as_bytes().to_vec().into()); + assert_eq!(hd.value, "*/*".as_bytes().to_vec().into()); // And we should have no bytes left. assert_eq!(remainder.len(), 0); assert_eq!(dynh.table.len(), 1); @@ -802,8 +805,8 @@ mod tests { match result { Ok((remainder, hd)) => { // Check the first message. - assert_eq!(hd.name, ":authority".as_bytes().to_vec()); - assert_eq!(hd.value, "localhost:3000".as_bytes().to_vec()); + assert_eq!(hd.name, ":authority".as_bytes().to_vec().into()); + assert_eq!(hd.value, "localhost:3000".as_bytes().to_vec().into()); // And we should have no bytes left. assert_eq!(remainder.len(), 0); assert_eq!(dynh.table.len(), 2); @@ -820,8 +823,8 @@ mod tests { match r3 { Ok((remainder, hd)) => { // same as before - assert_eq!(hd.name, ":authority".as_bytes().to_vec()); - assert_eq!(hd.value, "localhost:3000".as_bytes().to_vec()); + assert_eq!(hd.name, ":authority".as_bytes().to_vec().into()); + assert_eq!(hd.value, "localhost:3000".as_bytes().to_vec().into()); // And we should have no bytes left. assert_eq!(remainder.len(), 0); assert_eq!(dynh.table.len(), 2); @@ -856,8 +859,8 @@ mod tests { match r2 { Ok((remainder, hd)) => { // Check the first message. - assert_eq!(hd.name, ":path".as_bytes().to_vec()); - assert_eq!(hd.value, "/doc/manual/html/index.html".as_bytes().to_vec()); + assert_eq!(hd.name, ":path".as_bytes().to_vec().into()); + assert_eq!(hd.value, "/doc/manual/html/index.html".as_bytes().to_vec().into()); // And we should have no bytes left. assert_eq!(remainder.len(), 0); assert_eq!(dynh.table.len(), 2); diff --git a/rust/src/jsonbuilder.rs b/rust/src/jsonbuilder.rs index 7264be5..8b42966 100644 --- a/rust/src/jsonbuilder.rs +++ b/rust/src/jsonbuilder.rs @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 Open Information Security Foundation +/* Copyright (C) 2020-2024 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -435,7 +435,7 @@ impl JsonBuilder { return Err(JsonError::InvalidState); } } - self.push_str(&val.to_string())?; + self.push_float(val)?; Ok(self) } @@ -650,7 +650,7 @@ impl JsonBuilder { self.push('"')?; self.push_str(key)?; self.push_str("\":")?; - self.push_str(&val.to_string())?; + self.push_float(val)?; Ok(self) } @@ -681,6 +681,15 @@ impl JsonBuilder { self.buf.capacity() } + fn push_float(&mut self, val: f64) -> Result<(), JsonError> { + if val.is_nan() || val.is_infinite() { + self.push_str("null")?; + } else { + self.push_str(&val.to_string())?; + } + Ok(()) + } + /// Encode a string into the buffer, escaping as needed. /// /// The string is encoded into an intermediate vector as its faster @@ -1339,6 +1348,48 @@ mod test { jb.close().unwrap(); assert_eq!(jb.buf, r#"[1.1,2.2]"#); } + + #[test] + fn test_set_nan() { + let mut jb = JsonBuilder::try_new_object().unwrap(); + jb.set_float("nan", f64::NAN).unwrap(); + jb.close().unwrap(); + assert_eq!(jb.buf, r#"{"nan":null}"#); + } + + #[test] + fn test_append_nan() { + let mut jb = JsonBuilder::try_new_array().unwrap(); + jb.append_float(f64::NAN).unwrap(); + jb.close().unwrap(); + assert_eq!(jb.buf, r#"[null]"#); + } + + #[test] + fn test_set_inf() { + let mut jb = JsonBuilder::try_new_object().unwrap(); + jb.set_float("inf", f64::INFINITY).unwrap(); + jb.close().unwrap(); + assert_eq!(jb.buf, r#"{"inf":null}"#); + + let mut jb = JsonBuilder::try_new_object().unwrap(); + jb.set_float("inf", f64::NEG_INFINITY).unwrap(); + jb.close().unwrap(); + assert_eq!(jb.buf, r#"{"inf":null}"#); + } + + #[test] + fn test_append_inf() { + let mut jb = JsonBuilder::try_new_array().unwrap(); + jb.append_float(f64::INFINITY).unwrap(); + jb.close().unwrap(); + assert_eq!(jb.buf, r#"[null]"#); + + let mut jb = JsonBuilder::try_new_array().unwrap(); + jb.append_float(f64::NEG_INFINITY).unwrap(); + jb.close().unwrap(); + assert_eq!(jb.buf, r#"[null]"#); + } } // Escape table as seen in serde-json (MIT/Apache license) diff --git a/rust/src/mqtt/mqtt.rs b/rust/src/mqtt/mqtt.rs index 8260251..35f3c6d 100644 --- a/rust/src/mqtt/mqtt.rs +++ b/rust/src/mqtt/mqtt.rs @@ -749,8 +749,7 @@ export_state_data_get!(rs_mqtt_get_state_data, MQTTState); #[no_mangle] pub unsafe extern "C" fn rs_mqtt_register_parser(cfg_max_msg_len: u32) { let default_port = CString::new("[1883]").unwrap(); - let max_msg_len = &mut MAX_MSG_LEN; - *max_msg_len = cfg_max_msg_len; + MAX_MSG_LEN = cfg_max_msg_len; let parser = RustParser { name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char, default_port: default_port.as_ptr(), diff --git a/rust/src/smb/smb.rs b/rust/src/smb/smb.rs index d6b0a56..45d2cdc 100644 --- a/rust/src/smb/smb.rs +++ b/rust/src/smb/smb.rs @@ -1045,7 +1045,7 @@ impl SMBState { pub fn get_service_for_guid(&self, guid: &[u8]) -> (&'static str, bool) { - let (name, is_dcerpc) = match self.guid2name_map.get(&guid.to_vec()) { + let (name, is_dcerpc) = match self.guid2name_map.get(guid) { Some(n) => { let mut s = n.as_slice(); // skip leading \ if we have it diff --git a/rust/src/smb/smb1.rs b/rust/src/smb/smb1.rs index 9d7d47e..3a04c72 100644 --- a/rust/src/smb/smb1.rs +++ b/rust/src/smb/smb1.rs @@ -482,7 +482,7 @@ fn smb1_request_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, and state.ssn2vec_map.insert(name_key, name_val); let tx_hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX); - let tx = state.new_create_tx(&cr.file_name.to_vec(), + let tx = state.new_create_tx(&cr.file_name, cr.disposition, del, dir, tx_hdr); tx.vercmd.set_smb1_cmd(command); SCLogDebug!("TS CREATE TX {} created", tx.id); diff --git a/src/Makefile.in b/src/Makefile.in index 4f15923..229aaef 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -1233,6 +1233,7 @@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ POW_LIB = @POW_LIB@ RANLIB = @RANLIB@ +RELEASE_DATE = @RELEASE_DATE@ RUSTC = @RUSTC@ RUSTUP_HOME_PATH = @RUSTUP_HOME_PATH@ RUST_FEATURES = @RUST_FEATURES@ diff --git a/src/autoconf.h b/src/autoconf.h index 336c550..457a903 100644 --- a/src/autoconf.h +++ b/src/autoconf.h @@ -696,7 +696,7 @@ #define PACKAGE_NAME "suricata" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "suricata 7.0.4" +#define PACKAGE_STRING "suricata 7.0.5" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "suricata" @@ -705,7 +705,7 @@ #define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "7.0.4" +#define PACKAGE_VERSION "7.0.5" /* Pcre code unit width is 8 bits */ #define PCRE2_CODE_UNIT_WIDTH 8 @@ -723,7 +723,7 @@ /* #undef PROFILING */ /* Git revision */ -#define REVISION d8bad3b1a 2024-03-19 +#define REVISION c4cf7b09f 2024-04-23 /* Define to 1 if all of the C90 standard headers exist (not just the ones required in a freestanding environment). This macro is provided for @@ -743,7 +743,7 @@ /* #undef UNITTESTS */ /* Version number of package */ -#define VERSION "7.0.4" +#define VERSION "7.0.5" /* Enable Windows WinDivert support for inline IDP */ /* #undef WINDIVERT */ diff --git a/src/conf-yaml-loader.c b/src/conf-yaml-loader.c index ea64563..463eb2e 100644 --- a/src/conf-yaml-loader.c +++ b/src/conf-yaml-loader.c @@ -394,8 +394,19 @@ static int ConfYamlParse(yaml_parser_t *parser, ConfNode *parent, int inseq, int if (inseq) { char sequence_node_name[DEFAULT_NAME_LEN]; snprintf(sequence_node_name, DEFAULT_NAME_LEN, "%d", seq_idx++); - ConfNode *seq_node = ConfNodeLookupChild(node, - sequence_node_name); + ConfNode *seq_node = NULL; + if (was_empty < 0) { + // initialize was_empty + if (TAILQ_EMPTY(&node->head)) { + was_empty = 1; + } else { + was_empty = 0; + } + } + // we only check if the node's list was not empty at first + if (was_empty == 0) { + seq_node = ConfNodeLookupChild(node, sequence_node_name); + } if (seq_node != NULL) { /* The sequence node has already been set, probably * from the command line. Remove it so it gets diff --git a/src/datasets.c b/src/datasets.c index d89ed8d..01ef5bb 100644 --- a/src/datasets.c +++ b/src/datasets.c @@ -746,6 +746,11 @@ Dataset *DatasetGet(const char *name, enum DatasetTypes type, const char *save, break; } + if (set->hash && SC_ATOMIC_GET(set->hash->memcap_reached)) { + SCLogError("dataset too large for set memcap"); + goto out_err; + } + SCLogDebug("set %p/%s type %u save %s load %s", set, set->name, set->type, set->save, set->load); diff --git a/src/decode-ipv4.h b/src/decode-ipv4.h index d247fa9..a825007 100644 --- a/src/decode-ipv4.h +++ b/src/decode-ipv4.h @@ -154,20 +154,18 @@ typedef struct IPV4Hdr_ memset(&p->ip4vars, 0x00, sizeof(p->ip4vars)); \ } while (0) -enum IPV4OptionFlags { - IPV4_OPT_FLAG_EOL = 0, - IPV4_OPT_FLAG_NOP, - IPV4_OPT_FLAG_RR, - IPV4_OPT_FLAG_TS, - IPV4_OPT_FLAG_QS, - IPV4_OPT_FLAG_LSRR, - IPV4_OPT_FLAG_SSRR, - IPV4_OPT_FLAG_SID, - IPV4_OPT_FLAG_SEC, - IPV4_OPT_FLAG_CIPSO, - IPV4_OPT_FLAG_RTRALT, - IPV4_OPT_FLAG_ESEC, -}; +#define IPV4_OPT_FLAG_EOL BIT_U16(1) +#define IPV4_OPT_FLAG_NOP BIT_U16(2) +#define IPV4_OPT_FLAG_RR BIT_U16(3) +#define IPV4_OPT_FLAG_TS BIT_U16(4) +#define IPV4_OPT_FLAG_QS BIT_U16(5) +#define IPV4_OPT_FLAG_LSRR BIT_U16(6) +#define IPV4_OPT_FLAG_SSRR BIT_U16(7) +#define IPV4_OPT_FLAG_SID BIT_U16(8) +#define IPV4_OPT_FLAG_SEC BIT_U16(9) +#define IPV4_OPT_FLAG_CIPSO BIT_U16(10) +#define IPV4_OPT_FLAG_RTRALT BIT_U16(11) +#define IPV4_OPT_FLAG_ESEC BIT_U16(12) /* helper structure with parsed ipv4 info */ typedef struct IPV4Vars_ diff --git a/src/decode.c b/src/decode.c index 5cdeeea..13c6541 100644 --- a/src/decode.c +++ b/src/decode.c @@ -408,7 +408,6 @@ Packet *PacketDefragPktSetup(Packet *parent, const uint8_t *pkt, uint32_t len, u } p->recursion_level = parent->recursion_level; /* NOT incremented */ p->ts = parent->ts; - p->datalink = DLT_RAW; p->tenant_id = parent->tenant_id; /* tell new packet it's part of a tunnel */ SET_TUNNEL_PKT(p); diff --git a/src/decode.h b/src/decode.h index dedfbb0..4516d37 100644 --- a/src/decode.h +++ b/src/decode.h @@ -974,6 +974,7 @@ void DecodeUnregisterCounters(void); * Libpcap on at least OpenBSD returns 101 as datalink type for RAW pcaps though. */ #define LINKTYPE_RAW2 101 #define LINKTYPE_IPV4 228 +#define LINKTYPE_IPV6 229 #define LINKTYPE_GRE_OVER_IP 778 #define LINKTYPE_CISCO_HDLC DLT_C_HDLC #define PPP_OVER_GRE 11 diff --git a/src/defrag.c b/src/defrag.c index 71cf420..4596d72 100644 --- a/src/defrag.c +++ b/src/defrag.c @@ -104,26 +104,6 @@ static DefragContext *defrag_context; RB_GENERATE(IP_FRAGMENTS, Frag_, rb, DefragRbFragCompare); /** - * Utility/debugging function to dump the frags associated with a - * tracker. Only enable when unit tests are enabled. - */ -#if 0 -#ifdef UNITTESTS -static void -DumpFrags(DefragTracker *tracker) -{ - Frag *frag; - - printf("Dumping frags for packet: ID=%d\n", tracker->id); - TAILQ_FOREACH(frag, &tracker->frags, next) { - printf("-> Frag: frag_offset=%d, frag_len=%d, data_len=%d, ltrim=%d, skip=%d\n", frag->offset, frag->len, frag->data_len, frag->ltrim, frag->skip); - PrintRawDataFp(stdout, frag->pkt, frag->len); - } -} -#endif /* UNITTESTS */ -#endif - -/** * \brief Reset a frag for reuse in a pool. */ static void @@ -266,7 +246,7 @@ Defrag4Reassemble(ThreadVars *tv, DefragTracker *tracker, Packet *p) } /* Check that we have all the data. Relies on the fact that - * fragments are inserted if frag_offset order. */ + * fragments are inserted in frag_offset order. */ Frag *frag = NULL; size_t len = 0; RB_FOREACH_FROM(frag, IP_FRAGMENTS, first) { @@ -276,7 +256,8 @@ Defrag4Reassemble(ThreadVars *tv, DefragTracker *tracker, Packet *p) goto done; } else { - len += frag->data_len; + /* Update the packet length to the largest known data offset. */ + len = MAX(len, frag->offset + frag->data_len); } } @@ -288,17 +269,27 @@ Defrag4Reassemble(ThreadVars *tv, DefragTracker *tracker, Packet *p) } PKT_SET_SRC(rp, PKT_SRC_DEFRAG); rp->flags |= PKT_REBUILT_FRAGMENT; - rp->recursion_level = p->recursion_level; + rp->datalink = tracker->datalink; int fragmentable_offset = 0; uint16_t fragmentable_len = 0; uint16_t hlen = 0; int ip_hdr_offset = 0; + /* Assume more frags. */ + uint16_t prev_offset = 0; + bool more_frags = 1; + RB_FOREACH(frag, IP_FRAGMENTS, &tracker->fragment_tree) { SCLogDebug("frag %p, data_len %u, offset %u, pcap_cnt %"PRIu64, frag, frag->data_len, frag->offset, frag->pcap_cnt); + /* Previous fragment has no more fragments, and this packet + * doesn't overlap. We're done. */ + if (!more_frags && frag->offset > prev_offset) { + break; + } + if (frag->skip) continue; if (frag->ltrim >= frag->data_len) @@ -339,9 +330,16 @@ Defrag4Reassemble(ThreadVars *tv, DefragTracker *tracker, Packet *p) fragmentable_len = frag->offset + frag->data_len; } - if (!frag->more_frags) { - break; - } + /* Even if this fragment is flagged as having no more + * fragments, still continue. The next fragment may have the + * same offset with data that is preferred. + * + * For example, DefragBsdFragmentAfterNoMfIpv{4,6}Test + * + * This is due to not all fragments being completely trimmed, + * but relying on the copy ordering. */ + more_frags = frag->more_frags; + prev_offset = frag->offset; } SCLogDebug("ip_hdr_offset %u, hlen %" PRIu16 ", fragmentable_len %" PRIu16, ip_hdr_offset, hlen, @@ -417,7 +415,7 @@ Defrag6Reassemble(ThreadVars *tv, DefragTracker *tracker, Packet *p) goto done; } else { - len += frag->data_len; + len = MAX(len, frag->offset + frag->data_len); } } } @@ -430,13 +428,23 @@ Defrag6Reassemble(ThreadVars *tv, DefragTracker *tracker, Packet *p) goto error_remove_tracker; } PKT_SET_SRC(rp, PKT_SRC_DEFRAG); + rp->flags |= PKT_REBUILT_FRAGMENT; + rp->datalink = tracker->datalink; uint16_t unfragmentable_len = 0; int fragmentable_offset = 0; uint16_t fragmentable_len = 0; int ip_hdr_offset = 0; uint8_t next_hdr = 0; + + /* Assume more frags. */ + uint16_t prev_offset = 0; + bool more_frags = 1; + RB_FOREACH(frag, IP_FRAGMENTS, &tracker->fragment_tree) { + if (!more_frags && frag->offset > prev_offset) { + break; + } if (frag->skip) continue; if (frag->data_len - frag->ltrim <= 0) @@ -481,9 +489,16 @@ Defrag6Reassemble(ThreadVars *tv, DefragTracker *tracker, Packet *p) fragmentable_len = frag->offset + frag->data_len; } - if (!frag->more_frags) { - break; - } + /* Even if this fragment is flagged as having no more + * fragments, still continue. The next fragment may have the + * same offset with data that is preferred. + * + * For example, DefragBsdFragmentAfterNoMfIpv{4,6}Test + * + * This is due to not all fragments being completely trimmed, + * but relying on the copy ordering. */ + more_frags = frag->more_frags; + prev_offset = frag->offset; } rp->ip6h = (IPV6Hdr *)(GET_PKT_DATA(rp) + ip_hdr_offset); @@ -660,16 +675,45 @@ DefragInsertFrag(ThreadVars *tv, DecodeThreadVars *dtv, DefragTracker *tracker, switch (tracker->policy) { case DEFRAG_POLICY_BSD: if (frag_offset < prev->offset + prev->data_len) { - if (frag_offset >= prev->offset) { - ltrim = prev->offset + prev->data_len - frag_offset; + if (prev->offset <= frag_offset) { + /* We prefer the data from the previous + * fragment, so trim off the data in the new + * fragment that exists in the previous + * fragment. */ + uint16_t prev_end = prev->offset + prev->data_len; + if (prev_end > frag_end) { + /* Just skip. */ + /* TODO: Set overlap flag. */ + goto done; + } + ltrim = prev_end - frag_offset; + + if ((next != NULL) && (frag_end > next->offset)) { + next->ltrim = frag_end - next->offset; + } + + goto insert; } + + /* If the end of this fragment overlaps the start + * of the previous fragment, then trim up the + * start of previous fragment so this fragment is + * used. + * + * See: + * DefragBsdSubsequentOverlapsStartOfOriginal. + */ + if (frag_offset <= prev->offset && frag_end > prev->offset + prev->ltrim) { + uint16_t prev_ltrim = frag_end - prev->offset; + if (prev_ltrim > prev->ltrim) { + prev->ltrim = prev_ltrim; + } + } + if ((next != NULL) && (frag_end > next->offset)) { next->ltrim = frag_end - next->offset; } - if ((frag_offset < prev->offset) && - (frag_end >= prev->offset + prev->data_len)) { - prev->skip = 1; - } + goto insert; } break; @@ -861,6 +905,9 @@ DefragInsertFrag(ThreadVars *tv, DecodeThreadVars *dtv, DefragTracker *tracker, #ifdef DEBUG new->pcap_cnt = pcap_cnt; #endif + if (frag_offset == 0) { + tracker->datalink = p->datalink; + } IP_FRAGMENTS_RB_INSERT(&tracker->fragment_tree, new); @@ -1093,8 +1140,8 @@ void DefragDestroy(void) * Allocate a test packet. Nothing to fancy, just a simple IP packet * with some payload of no particular protocol. */ -static Packet *BuildTestPacket(uint8_t proto, uint16_t id, uint16_t off, int mf, - const char content, int content_len) +static Packet *BuildIpv4TestPacket( + uint8_t proto, uint16_t id, uint16_t off, int mf, const char content, int content_len) { Packet *p = NULL; int hlen = 20; @@ -1167,8 +1214,79 @@ error: return NULL; } -static Packet *IPV6BuildTestPacket(uint8_t proto, uint32_t id, uint16_t off, - int mf, const char content, int content_len) +/** + * Allocate a test packet, much like BuildIpv4TestPacket, but with + * the full content provided by the caller. + */ +static Packet *BuildIpv4TestPacketWithContent( + uint8_t proto, uint16_t id, uint16_t off, int mf, const uint8_t *content, int content_len) +{ + Packet *p = NULL; + int hlen = 20; + int ttl = 64; + IPV4Hdr ip4h; + + p = SCCalloc(1, sizeof(*p) + default_packet_size); + if (unlikely(p == NULL)) + return NULL; + + PacketInit(p); + + struct timeval tval; + gettimeofday(&tval, NULL); + p->ts = SCTIME_FROM_TIMEVAL(&tval); + ip4h.ip_verhl = 4 << 4; + ip4h.ip_verhl |= hlen >> 2; + ip4h.ip_len = htons(hlen + content_len); + ip4h.ip_id = htons(id); + if (mf) + ip4h.ip_off = htons(IP_MF | off); + else + ip4h.ip_off = htons(off); + ip4h.ip_ttl = ttl; + ip4h.ip_proto = proto; + + ip4h.s_ip_src.s_addr = 0x01010101; /* 1.1.1.1 */ + ip4h.s_ip_dst.s_addr = 0x02020202; /* 2.2.2.2 */ + + /* copy content_len crap, we need full length */ + PacketCopyData(p, (uint8_t *)&ip4h, sizeof(ip4h)); + p->ip4h = (IPV4Hdr *)GET_PKT_DATA(p); + SET_IPV4_SRC_ADDR(p, &p->src); + SET_IPV4_DST_ADDR(p, &p->dst); + + PacketCopyDataOffset(p, hlen, content, content_len); + SET_PKT_LEN(p, hlen + content_len); + + p->ip4h->ip_csum = IPV4Checksum((uint16_t *)GET_PKT_DATA(p), hlen, 0); + + /* Self test. */ + if (IPV4_GET_VER(p) != 4) + goto error; + if (IPV4_GET_HLEN(p) != hlen) + goto error; + if (IPV4_GET_IPLEN(p) != hlen + content_len) + goto error; + if (IPV4_GET_IPID(p) != id) + goto error; + if (IPV4_GET_IPOFFSET(p) != off) + goto error; + if (IPV4_GET_MF(p) != mf) + goto error; + if (IPV4_GET_IPTTL(p) != ttl) + goto error; + if (IPV4_GET_IPPROTO(p) != proto) + goto error; + + return p; +error: + if (p != NULL) + SCFree(p); + return NULL; +} + +static Packet *BuildIpv6TestPacket( + uint8_t proto, uint32_t id, uint16_t off, int mf, const uint8_t content, int content_len) { Packet *p = NULL; uint8_t *pcontent; @@ -1238,6 +1356,71 @@ error: return NULL; } +static Packet *BuildIpv6TestPacketWithContent( + uint8_t proto, uint32_t id, uint16_t off, int mf, const uint8_t *content, int content_len) +{ + Packet *p = NULL; + IPV6Hdr ip6h; + + p = SCCalloc(1, sizeof(*p) + default_packet_size); + if (unlikely(p == NULL)) + return NULL; + + PacketInit(p); + + struct timeval tval; + gettimeofday(&tval, NULL); + p->ts = SCTIME_FROM_TIMEVAL(&tval); + + ip6h.s_ip6_nxt = 44; + ip6h.s_ip6_hlim = 2; + + /* Source and dest address - very bogus addresses. */ + ip6h.s_ip6_src[0] = 0x01010101; + ip6h.s_ip6_src[1] = 0x01010101; + ip6h.s_ip6_src[2] = 0x01010101; + ip6h.s_ip6_src[3] = 0x01010101; + ip6h.s_ip6_dst[0] = 0x02020202; + ip6h.s_ip6_dst[1] = 0x02020202; + ip6h.s_ip6_dst[2] = 0x02020202; + ip6h.s_ip6_dst[3] = 0x02020202; + + /* copy content_len crap, we need full length */ + PacketCopyData(p, (uint8_t *)&ip6h, sizeof(IPV6Hdr)); + + p->ip6h = (IPV6Hdr *)GET_PKT_DATA(p); + IPV6_SET_RAW_VER(p->ip6h, 6); + /* Fragmentation header. */ + IPV6FragHdr *fh = (IPV6FragHdr *)(GET_PKT_DATA(p) + sizeof(IPV6Hdr)); + fh->ip6fh_nxt = proto; + fh->ip6fh_ident = htonl(id); + fh->ip6fh_offlg = htons((off << 3) | mf); + + DecodeIPV6FragHeader(p, (uint8_t *)fh, 8, 8 + content_len, 0); + + PacketCopyDataOffset(p, sizeof(IPV6Hdr) + sizeof(IPV6FragHdr), content, content_len); + SET_PKT_LEN(p, sizeof(IPV6Hdr) + sizeof(IPV6FragHdr) + content_len); + + p->ip6h->s_ip6_plen = htons(sizeof(IPV6FragHdr) + content_len); + + SET_IPV6_SRC_ADDR(p, &p->src); + SET_IPV6_DST_ADDR(p, &p->dst); + + /* Self test. */ + if (IPV6_GET_VER(p) != 6) + goto error; + if (IPV6_GET_NH(p) != 44) + goto error; + if (IPV6_GET_PLEN(p) != sizeof(IPV6FragHdr) + content_len) + goto error; + + return p; +error: + if (p != NULL) + SCFree(p); + return NULL; +} + /** * Test the simplest possible re-assembly scenario. All packet in * order and no overlaps. @@ -1251,11 +1434,11 @@ static int DefragInOrderSimpleTest(void) DefragInit(); - p1 = BuildTestPacket(IPPROTO_ICMP, id, 0, 1, 'A', 8); + p1 = BuildIpv4TestPacket(IPPROTO_ICMP, id, 0, 1, 'A', 8); FAIL_IF_NULL(p1); - p2 = BuildTestPacket(IPPROTO_ICMP, id, 1, 1, 'B', 8); + p2 = BuildIpv4TestPacket(IPPROTO_ICMP, id, 1, 1, 'B', 8); FAIL_IF_NULL(p2); - p3 = BuildTestPacket(IPPROTO_ICMP, id, 2, 0, 'C', 3); + p3 = BuildIpv4TestPacket(IPPROTO_ICMP, id, 2, 0, 'C', 3); FAIL_IF_NULL(p3); FAIL_IF(Defrag(NULL, NULL, p1) != NULL); @@ -1303,11 +1486,11 @@ static int DefragReverseSimpleTest(void) DefragInit(); - p1 = BuildTestPacket(IPPROTO_ICMP, id, 0, 1, 'A', 8); + p1 = BuildIpv4TestPacket(IPPROTO_ICMP, id, 0, 1, 'A', 8); FAIL_IF_NULL(p1); - p2 = BuildTestPacket(IPPROTO_ICMP, id, 1, 1, 'B', 8); + p2 = BuildIpv4TestPacket(IPPROTO_ICMP, id, 1, 1, 'B', 8); FAIL_IF_NULL(p2); - p3 = BuildTestPacket(IPPROTO_ICMP, id, 2, 0, 'C', 3); + p3 = BuildIpv4TestPacket(IPPROTO_ICMP, id, 2, 0, 'C', 3); FAIL_IF_NULL(p3); FAIL_IF(Defrag(NULL, NULL, p3) != NULL); @@ -1347,7 +1530,7 @@ static int DefragReverseSimpleTest(void) * Test the simplest possible re-assembly scenario. All packet in * order and no overlaps. */ -static int IPV6DefragInOrderSimpleTest(void) +static int DefragInOrderSimpleIpv6Test(void) { Packet *p1 = NULL, *p2 = NULL, *p3 = NULL; Packet *reassembled = NULL; @@ -1356,11 +1539,11 @@ static int IPV6DefragInOrderSimpleTest(void) DefragInit(); - p1 = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 0, 1, 'A', 8); + p1 = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 0, 1, 'A', 8); FAIL_IF_NULL(p1); - p2 = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 1, 1, 'B', 8); + p2 = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 1, 1, 'B', 8); FAIL_IF_NULL(p2); - p3 = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 2, 0, 'C', 3); + p3 = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 2, 0, 'C', 3); FAIL_IF_NULL(p3); FAIL_IF(Defrag(NULL, NULL, p1) != NULL); @@ -1394,7 +1577,7 @@ static int IPV6DefragInOrderSimpleTest(void) PASS; } -static int IPV6DefragReverseSimpleTest(void) +static int DefragReverseSimpleIpv6Test(void) { DefragContext *dc = NULL; Packet *p1 = NULL, *p2 = NULL, *p3 = NULL; @@ -1407,11 +1590,11 @@ static int IPV6DefragReverseSimpleTest(void) dc = DefragContextNew(); FAIL_IF_NULL(dc); - p1 = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 0, 1, 'A', 8); + p1 = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 0, 1, 'A', 8); FAIL_IF_NULL(p1); - p2 = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 1, 1, 'B', 8); + p2 = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 1, 1, 'B', 8); FAIL_IF_NULL(p2); - p3 = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 2, 0, 'C', 3); + p3 = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 2, 0, 'C', 3); FAIL_IF_NULL(p3); FAIL_IF(Defrag(NULL, NULL, p3) != NULL); @@ -1444,8 +1627,7 @@ static int IPV6DefragReverseSimpleTest(void) PASS; } -static int DefragDoSturgesNovakTest(int policy, u_char *expected, - size_t expected_len) +static int DefragDoSturgesNovakTest(int policy, uint8_t *expected, size_t expected_len) { int i; @@ -1463,60 +1645,60 @@ static int DefragDoSturgesNovakTest(int policy, u_char *expected, * Original fragments. */ - /* A*24 at 0. */ - packets[0] = BuildTestPacket(IPPROTO_ICMP, id, 0, 1, 'A', 24); + /* <1> A*24 at 0. */ + packets[0] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 0, 1, 'A', 24); - /* B*15 at 32. */ - packets[1] = BuildTestPacket(IPPROTO_ICMP, id, 32 >> 3, 1, 'B', 16); + /* <2> B*16 at 32. */ + packets[1] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 32 >> 3, 1, 'B', 16); - /* C*24 at 48. */ - packets[2] = BuildTestPacket(IPPROTO_ICMP, id, 48 >> 3, 1, 'C', 24); + /* <3> C*24 at 48. */ + packets[2] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 48 >> 3, 1, 'C', 24); - /* D*8 at 80. */ - packets[3] = BuildTestPacket(IPPROTO_ICMP, id, 80 >> 3, 1, 'D', 8); + /* <3_1> D*8 at 80. */ + packets[3] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 80 >> 3, 1, 'D', 8); - /* E*16 at 104. */ - packets[4] = BuildTestPacket(IPPROTO_ICMP, id, 104 >> 3, 1, 'E', 16); + /* <3_2> E*16 at 104. */ + packets[4] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 104 >> 3, 1, 'E', 16); - /* F*24 at 120. */ - packets[5] = BuildTestPacket(IPPROTO_ICMP, id, 120 >> 3, 1, 'F', 24); + /* <3_3> F*24 at 120. */ + packets[5] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 120 >> 3, 1, 'F', 24); - /* G*16 at 144. */ - packets[6] = BuildTestPacket(IPPROTO_ICMP, id, 144 >> 3, 1, 'G', 16); + /* <3_4> G*16 at 144. */ + packets[6] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 144 >> 3, 1, 'G', 16); - /* H*16 at 160. */ - packets[7] = BuildTestPacket(IPPROTO_ICMP, id, 160 >> 3, 1, 'H', 16); + /* <3_5> H*16 at 160. */ + packets[7] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 160 >> 3, 1, 'H', 16); - /* I*8 at 176. */ - packets[8] = BuildTestPacket(IPPROTO_ICMP, id, 176 >> 3, 1, 'I', 8); + /* <3_6> I*8 at 176. */ + packets[8] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 176 >> 3, 1, 'I', 8); /* * Overlapping subsequent fragments. */ - /* J*32 at 8. */ - packets[9] = BuildTestPacket(IPPROTO_ICMP, id, 8 >> 3, 1, 'J', 32); + /* <4> J*32 at 8. */ + packets[9] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 8 >> 3, 1, 'J', 32); - /* K*24 at 48. */ - packets[10] = BuildTestPacket(IPPROTO_ICMP, id, 48 >> 3, 1, 'K', 24); + /* <5> K*24 at 48. */ + packets[10] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 48 >> 3, 1, 'K', 24); - /* L*24 at 72. */ - packets[11] = BuildTestPacket(IPPROTO_ICMP, id, 72 >> 3, 1, 'L', 24); + /* <6> L*24 at 72. */ + packets[11] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 72 >> 3, 1, 'L', 24); - /* M*24 at 96. */ - packets[12] = BuildTestPacket(IPPROTO_ICMP, id, 96 >> 3, 1, 'M', 24); + /* <7> M*24 at 96. */ + packets[12] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 96 >> 3, 1, 'M', 24); - /* N*8 at 128. */ - packets[13] = BuildTestPacket(IPPROTO_ICMP, id, 128 >> 3, 1, 'N', 8); + /* <8> N*8 at 128. */ + packets[13] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 128 >> 3, 1, 'N', 8); - /* O*8 at 152. */ - packets[14] = BuildTestPacket(IPPROTO_ICMP, id, 152 >> 3, 1, 'O', 8); + /* <9> O*8 at 152. */ + packets[14] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 152 >> 3, 1, 'O', 8); - /* P*8 at 160. */ - packets[15] = BuildTestPacket(IPPROTO_ICMP, id, 160 >> 3, 1, 'P', 8); + /* <10> P*8 at 160. */ + packets[15] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 160 >> 3, 1, 'P', 8); - /* Q*16 at 176. */ - packets[16] = BuildTestPacket(IPPROTO_ICMP, id, 176 >> 3, 0, 'Q', 16); + /* <11> Q*16 at 176. */ + packets[16] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 176 >> 3, 0, 'Q', 16); default_policy = policy; @@ -1542,8 +1724,15 @@ static int DefragDoSturgesNovakTest(int policy, u_char *expected, FAIL_IF(IPV4_GET_HLEN(reassembled) != 20); FAIL_IF(IPV4_GET_IPLEN(reassembled) != 20 + 192); - - FAIL_IF(memcmp(GET_PKT_DATA(reassembled) + 20, expected, expected_len) != 0); + FAIL_IF(expected_len != 192); + + if (memcmp(expected, GET_PKT_DATA(reassembled) + 20, expected_len) != 0) { + printf("Expected:\n"); + PrintRawDataFp(stdout, expected, expected_len); + printf("Got:\n"); + PrintRawDataFp(stdout, GET_PKT_DATA(reassembled) + 20, GET_PKT_LEN(reassembled) - 20); + FAIL; + } SCFree(reassembled); /* Make sure all frags were returned back to the pool. */ @@ -1556,8 +1745,7 @@ static int DefragDoSturgesNovakTest(int policy, u_char *expected, PASS; } -static int IPV6DefragDoSturgesNovakTest(int policy, u_char *expected, - size_t expected_len) +static int DefragDoSturgesNovakIpv6Test(int policy, uint8_t *expected, size_t expected_len) { int i; @@ -1575,60 +1763,60 @@ static int IPV6DefragDoSturgesNovakTest(int policy, u_char *expected, * Original fragments. */ - /* A*24 at 0. */ - packets[0] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 0, 1, 'A', 24); + /* <1> A*24 at 0. */ + packets[0] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 0, 1, 'A', 24); - /* B*15 at 32. */ - packets[1] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 32 >> 3, 1, 'B', 16); + /* <2> B*16 at 32. */ + packets[1] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 32 >> 3, 1, 'B', 16); - /* C*24 at 48. */ - packets[2] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 48 >> 3, 1, 'C', 24); + /* <3> C*24 at 48. */ + packets[2] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 48 >> 3, 1, 'C', 24); - /* D*8 at 80. */ - packets[3] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 80 >> 3, 1, 'D', 8); + /* <3_1> D*8 at 80. */ + packets[3] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 80 >> 3, 1, 'D', 8); - /* E*16 at 104. */ - packets[4] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 104 >> 3, 1, 'E', 16); + /* <3_2> E*16 at 104. */ + packets[4] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 104 >> 3, 1, 'E', 16); - /* F*24 at 120. */ - packets[5] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 120 >> 3, 1, 'F', 24); + /* <3_3> F*24 at 120. */ + packets[5] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 120 >> 3, 1, 'F', 24); - /* G*16 at 144. */ - packets[6] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 144 >> 3, 1, 'G', 16); + /* <3_4> G*16 at 144. */ + packets[6] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 144 >> 3, 1, 'G', 16); - /* H*16 at 160. */ - packets[7] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 160 >> 3, 1, 'H', 16); + /* <3_5> H*16 at 160. */ + packets[7] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 160 >> 3, 1, 'H', 16); - /* I*8 at 176. */ - packets[8] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 176 >> 3, 1, 'I', 8); + /* <3_6> I*8 at 176. */ + packets[8] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 176 >> 3, 1, 'I', 8); /* * Overlapping subsequent fragments. */ - /* J*32 at 8. */ - packets[9] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 8 >> 3, 1, 'J', 32); + /* <4> J*32 at 8. */ + packets[9] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 8 >> 3, 1, 'J', 32); - /* K*24 at 48. */ - packets[10] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 48 >> 3, 1, 'K', 24); + /* <5> K*24 at 48. */ + packets[10] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 48 >> 3, 1, 'K', 24); - /* L*24 at 72. */ - packets[11] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 72 >> 3, 1, 'L', 24); + /* <6> L*24 at 72. */ + packets[11] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 72 >> 3, 1, 'L', 24); - /* M*24 at 96. */ - packets[12] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 96 >> 3, 1, 'M', 24); + /* <7> M*24 at 96. */ + packets[12] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 96 >> 3, 1, 'M', 24); - /* N*8 at 128. */ - packets[13] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 128 >> 3, 1, 'N', 8); + /* <8> N*8 at 128. */ + packets[13] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 128 >> 3, 1, 'N', 8); - /* O*8 at 152. */ - packets[14] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 152 >> 3, 1, 'O', 8); + /* <9> O*8 at 152. */ + packets[14] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 152 >> 3, 1, 'O', 8); - /* P*8 at 160. */ - packets[15] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 160 >> 3, 1, 'P', 8); + /* <10> P*8 at 160. */ + packets[15] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 160 >> 3, 1, 'P', 8); - /* Q*16 at 176. */ - packets[16] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 176 >> 3, 0, 'Q', 16); + /* <11> Q*16 at 176. */ + packets[16] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 176 >> 3, 0, 'Q', 16); default_policy = policy; @@ -1667,35 +1855,61 @@ static int IPV6DefragDoSturgesNovakTest(int policy, u_char *expected, PASS; } +/* Define data that matches the naming "Target-Based Fragmentation + * Reassembly". + * + * For example, the data refers to a fragment of data as <1>, or <3_6> + * and uses these to diagram the input fragments and the resulting + * policies. We build test cases for the papers scenario but assign + * specific values to each segment. + */ +#define D_1 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A' +#define D_2 'B', 'B', 'B', 'B', 'B', 'B', 'B', 'B' +#define D_3 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C' +#define D_3_1 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D' +#define D_3_2 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E' +#define D_3_3 'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F' +#define D_3_4 'G', 'G', 'G', 'G', 'G', 'G', 'G', 'G' +#define D_3_5 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H' +#define D_3_6 'I', 'I', 'I', 'I', 'I', 'I', 'I', 'I' +#define D_4 'J', 'J', 'J', 'J', 'J', 'J', 'J', 'J' +#define D_5 'K', 'K', 'K', 'K', 'K', 'K', 'K', 'K' +#define D_6 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L' +#define D_7 'M', 'M', 'M', 'M', 'M', 'M', 'M', 'M' +#define D_8 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N' +#define D_9 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O' +#define D_10 'P', 'P', 'P', 'P', 'P', 'P', 'P', 'P' +#define D_11 'Q', 'Q', 'Q', 'Q', 'Q', 'Q', 'Q', 'Q' + static int DefragSturgesNovakBsdTest(void) { /* Expected data. */ - u_char expected[] = { - "AAAAAAAA" - "AAAAAAAA" - "AAAAAAAA" - "JJJJJJJJ" - "JJJJJJJJ" - "BBBBBBBB" - "CCCCCCCC" - "CCCCCCCC" - "CCCCCCCC" - "LLLLLLLL" - "LLLLLLLL" - "LLLLLLLL" - "MMMMMMMM" - "MMMMMMMM" - "MMMMMMMM" - "FFFFFFFF" - "FFFFFFFF" - "FFFFFFFF" - "GGGGGGGG" - "GGGGGGGG" - "HHHHHHHH" - "HHHHHHHH" - "IIIIIIII" - "QQQQQQQQ" + uint8_t expected[] = { + D_1, + D_1, + D_1, + D_4, + D_4, + D_2, + D_3, + D_3, + D_3, + D_6, + D_6, + D_6, + D_7, + D_7, + D_7, + D_3_3, + D_3_3, + D_3_3, + D_3_4, + D_3_4, + D_3_5, + D_3_5, + D_3_6, + D_11, }; FAIL_IF_NOT(DefragDoSturgesNovakTest(DEFRAG_POLICY_BSD, expected, @@ -1703,69 +1917,68 @@ DefragSturgesNovakBsdTest(void) PASS; } -static int IPV6DefragSturgesNovakBsdTest(void) +static int DefragSturgesNovakBsdIpv6Test(void) { /* Expected data. */ - u_char expected[] = { - "AAAAAAAA" - "AAAAAAAA" - "AAAAAAAA" - "JJJJJJJJ" - "JJJJJJJJ" - "BBBBBBBB" - "CCCCCCCC" - "CCCCCCCC" - "CCCCCCCC" - "LLLLLLLL" - "LLLLLLLL" - "LLLLLLLL" - "MMMMMMMM" - "MMMMMMMM" - "MMMMMMMM" - "FFFFFFFF" - "FFFFFFFF" - "FFFFFFFF" - "GGGGGGGG" - "GGGGGGGG" - "HHHHHHHH" - "HHHHHHHH" - "IIIIIIII" - "QQQQQQQQ" + uint8_t expected[] = { + D_1, + D_1, + D_1, + D_4, + D_4, + D_2, + D_3, + D_3, + D_3, + D_6, + D_6, + D_6, + D_7, + D_7, + D_7, + D_3_3, + D_3_3, + D_3_3, + D_3_4, + D_3_4, + D_3_5, + D_3_5, + D_3_6, + D_11, }; - FAIL_IF_NOT(IPV6DefragDoSturgesNovakTest(DEFRAG_POLICY_BSD, expected, - sizeof(expected))); + FAIL_IF_NOT(DefragDoSturgesNovakIpv6Test(DEFRAG_POLICY_BSD, expected, sizeof(expected))); PASS; } static int DefragSturgesNovakLinuxIpv4Test(void) { /* Expected data. */ - u_char expected[] = { - "AAAAAAAA" - "AAAAAAAA" - "AAAAAAAA" - "JJJJJJJJ" - "JJJJJJJJ" - "BBBBBBBB" - "KKKKKKKK" - "KKKKKKKK" - "KKKKKKKK" - "LLLLLLLL" - "LLLLLLLL" - "LLLLLLLL" - "MMMMMMMM" - "MMMMMMMM" - "MMMMMMMM" - "FFFFFFFF" - "FFFFFFFF" - "FFFFFFFF" - "GGGGGGGG" - "GGGGGGGG" - "PPPPPPPP" - "HHHHHHHH" - "QQQQQQQQ" - "QQQQQQQQ" + uint8_t expected[] = { + D_1, + D_1, + D_1, + D_4, + D_4, + D_2, + D_5, + D_5, + D_5, + D_6, + D_6, + D_6, + D_7, + D_7, + D_7, + D_3_3, + D_3_3, + D_3_3, + D_3_4, + D_3_4, + D_10, + D_3_5, + D_11, + D_11, }; FAIL_IF_NOT(DefragDoSturgesNovakTest(DEFRAG_POLICY_LINUX, expected, @@ -1773,69 +1986,68 @@ static int DefragSturgesNovakLinuxIpv4Test(void) PASS; } -static int IPV6DefragSturgesNovakLinuxTest(void) +static int DefragSturgesNovakLinuxIpv6Test(void) { /* Expected data. */ - u_char expected[] = { - "AAAAAAAA" - "AAAAAAAA" - "AAAAAAAA" - "JJJJJJJJ" - "JJJJJJJJ" - "BBBBBBBB" - "KKKKKKKK" - "KKKKKKKK" - "KKKKKKKK" - "LLLLLLLL" - "LLLLLLLL" - "LLLLLLLL" - "MMMMMMMM" - "MMMMMMMM" - "MMMMMMMM" - "FFFFFFFF" - "FFFFFFFF" - "FFFFFFFF" - "GGGGGGGG" - "GGGGGGGG" - "PPPPPPPP" - "HHHHHHHH" - "QQQQQQQQ" - "QQQQQQQQ" + uint8_t expected[] = { + D_1, + D_1, + D_1, + D_4, + D_4, + D_2, + D_5, + D_5, + D_5, + D_6, + D_6, + D_6, + D_7, + D_7, + D_7, + D_3_3, + D_3_3, + D_3_3, + D_3_4, + D_3_4, + D_10, + D_3_5, + D_11, + D_11, }; - FAIL_IF_NOT(IPV6DefragDoSturgesNovakTest(DEFRAG_POLICY_LINUX, expected, - sizeof(expected))); + FAIL_IF_NOT(DefragDoSturgesNovakIpv6Test(DEFRAG_POLICY_LINUX, expected, sizeof(expected))); PASS; } static int DefragSturgesNovakWindowsIpv4Test(void) { /* Expected data. */ - u_char expected[] = { - "AAAAAAAA" - "AAAAAAAA" - "AAAAAAAA" - "JJJJJJJJ" - "BBBBBBBB" - "BBBBBBBB" - "CCCCCCCC" - "CCCCCCCC" - "CCCCCCCC" - "LLLLLLLL" - "LLLLLLLL" - "LLLLLLLL" - "MMMMMMMM" - "EEEEEEEE" - "EEEEEEEE" - "FFFFFFFF" - "FFFFFFFF" - "FFFFFFFF" - "GGGGGGGG" - "GGGGGGGG" - "HHHHHHHH" - "HHHHHHHH" - "IIIIIIII" - "QQQQQQQQ" + uint8_t expected[] = { + D_1, + D_1, + D_1, + D_4, + D_2, + D_2, + D_3, + D_3, + D_3, + D_6, + D_6, + D_6, + D_7, + D_3_2, + D_3_2, + D_3_3, + D_3_3, + D_3_3, + D_3_4, + D_3_4, + D_3_5, + D_3_5, + D_3_6, + D_11, }; FAIL_IF_NOT(DefragDoSturgesNovakTest(DEFRAG_POLICY_WINDOWS, expected, @@ -1843,69 +2055,68 @@ static int DefragSturgesNovakWindowsIpv4Test(void) PASS; } -static int IPV6DefragSturgesNovakWindowsTest(void) +static int DefragSturgesNovakWindowsIpv6Test(void) { /* Expected data. */ - u_char expected[] = { - "AAAAAAAA" - "AAAAAAAA" - "AAAAAAAA" - "JJJJJJJJ" - "BBBBBBBB" - "BBBBBBBB" - "CCCCCCCC" - "CCCCCCCC" - "CCCCCCCC" - "LLLLLLLL" - "LLLLLLLL" - "LLLLLLLL" - "MMMMMMMM" - "EEEEEEEE" - "EEEEEEEE" - "FFFFFFFF" - "FFFFFFFF" - "FFFFFFFF" - "GGGGGGGG" - "GGGGGGGG" - "HHHHHHHH" - "HHHHHHHH" - "IIIIIIII" - "QQQQQQQQ" + uint8_t expected[] = { + D_1, + D_1, + D_1, + D_4, + D_2, + D_2, + D_3, + D_3, + D_3, + D_6, + D_6, + D_6, + D_7, + D_3_2, + D_3_2, + D_3_3, + D_3_3, + D_3_3, + D_3_4, + D_3_4, + D_3_5, + D_3_5, + D_3_6, + D_11, }; - FAIL_IF_NOT(IPV6DefragDoSturgesNovakTest(DEFRAG_POLICY_WINDOWS, expected, - sizeof(expected))); + FAIL_IF_NOT(DefragDoSturgesNovakIpv6Test(DEFRAG_POLICY_WINDOWS, expected, sizeof(expected))); PASS; } static int DefragSturgesNovakSolarisTest(void) { /* Expected data. */ - u_char expected[] = { - "AAAAAAAA" - "AAAAAAAA" - "AAAAAAAA" - "JJJJJJJJ" - "BBBBBBBB" - "BBBBBBBB" - "CCCCCCCC" - "CCCCCCCC" - "CCCCCCCC" - "LLLLLLLL" - "LLLLLLLL" - "LLLLLLLL" - "MMMMMMMM" - "MMMMMMMM" - "MMMMMMMM" - "FFFFFFFF" - "FFFFFFFF" - "FFFFFFFF" - "GGGGGGGG" - "GGGGGGGG" - "HHHHHHHH" - "HHHHHHHH" - "IIIIIIII" - "QQQQQQQQ" + uint8_t expected[] = { + D_1, + D_1, + D_1, + D_4, + D_2, + D_2, + D_3, + D_3, + D_3, + D_6, + D_6, + D_6, + D_7, + D_7, + D_7, + D_3_3, + D_3_3, + D_3_3, + D_3_4, + D_3_4, + D_3_5, + D_3_5, + D_3_6, + D_11, }; FAIL_IF_NOT(DefragDoSturgesNovakTest(DEFRAG_POLICY_SOLARIS, expected, @@ -1913,69 +2124,68 @@ static int DefragSturgesNovakSolarisTest(void) PASS; } -static int IPV6DefragSturgesNovakSolarisTest(void) +static int DefragSturgesNovakSolarisIpv6Test(void) { /* Expected data. */ - u_char expected[] = { - "AAAAAAAA" - "AAAAAAAA" - "AAAAAAAA" - "JJJJJJJJ" - "BBBBBBBB" - "BBBBBBBB" - "CCCCCCCC" - "CCCCCCCC" - "CCCCCCCC" - "LLLLLLLL" - "LLLLLLLL" - "LLLLLLLL" - "MMMMMMMM" - "MMMMMMMM" - "MMMMMMMM" - "FFFFFFFF" - "FFFFFFFF" - "FFFFFFFF" - "GGGGGGGG" - "GGGGGGGG" - "HHHHHHHH" - "HHHHHHHH" - "IIIIIIII" - "QQQQQQQQ" + uint8_t expected[] = { + D_1, + D_1, + D_1, + D_4, + D_2, + D_2, + D_3, + D_3, + D_3, + D_6, + D_6, + D_6, + D_7, + D_7, + D_7, + D_3_3, + D_3_3, + D_3_3, + D_3_4, + D_3_4, + D_3_5, + D_3_5, + D_3_6, + D_11, }; - FAIL_IF_NOT(IPV6DefragDoSturgesNovakTest(DEFRAG_POLICY_SOLARIS, expected, - sizeof(expected))); + FAIL_IF_NOT(DefragDoSturgesNovakIpv6Test(DEFRAG_POLICY_SOLARIS, expected, sizeof(expected))); PASS; } static int DefragSturgesNovakFirstTest(void) { /* Expected data. */ - u_char expected[] = { - "AAAAAAAA" - "AAAAAAAA" - "AAAAAAAA" - "JJJJJJJJ" - "BBBBBBBB" - "BBBBBBBB" - "CCCCCCCC" - "CCCCCCCC" - "CCCCCCCC" - "LLLLLLLL" - "DDDDDDDD" - "LLLLLLLL" - "MMMMMMMM" - "EEEEEEEE" - "EEEEEEEE" - "FFFFFFFF" - "FFFFFFFF" - "FFFFFFFF" - "GGGGGGGG" - "GGGGGGGG" - "HHHHHHHH" - "HHHHHHHH" - "IIIIIIII" - "QQQQQQQQ" + uint8_t expected[] = { + D_1, + D_1, + D_1, + D_4, + D_2, + D_2, + D_3, + D_3, + D_3, + D_6, + D_3_1, + D_6, + D_7, + D_3_2, + D_3_2, + D_3_3, + D_3_3, + D_3_3, + D_3_4, + D_3_4, + D_3_5, + D_3_5, + D_3_6, + D_11, }; FAIL_IF_NOT(DefragDoSturgesNovakTest(DEFRAG_POLICY_FIRST, expected, @@ -1983,69 +2193,68 @@ static int DefragSturgesNovakFirstTest(void) PASS; } -static int IPV6DefragSturgesNovakFirstTest(void) +static int DefragSturgesNovakFirstIpv6Test(void) { /* Expected data. */ - u_char expected[] = { - "AAAAAAAA" - "AAAAAAAA" - "AAAAAAAA" - "JJJJJJJJ" - "BBBBBBBB" - "BBBBBBBB" - "CCCCCCCC" - "CCCCCCCC" - "CCCCCCCC" - "LLLLLLLL" - "DDDDDDDD" - "LLLLLLLL" - "MMMMMMMM" - "EEEEEEEE" - "EEEEEEEE" - "FFFFFFFF" - "FFFFFFFF" - "FFFFFFFF" - "GGGGGGGG" - "GGGGGGGG" - "HHHHHHHH" - "HHHHHHHH" - "IIIIIIII" - "QQQQQQQQ" + uint8_t expected[] = { + D_1, + D_1, + D_1, + D_4, + D_2, + D_2, + D_3, + D_3, + D_3, + D_6, + D_3_1, + D_6, + D_7, + D_3_2, + D_3_2, + D_3_3, + D_3_3, + D_3_3, + D_3_4, + D_3_4, + D_3_5, + D_3_5, + D_3_6, + D_11, }; - return IPV6DefragDoSturgesNovakTest(DEFRAG_POLICY_FIRST, expected, - sizeof(expected)); + return DefragDoSturgesNovakIpv6Test(DEFRAG_POLICY_FIRST, expected, sizeof(expected)); } static int DefragSturgesNovakLastTest(void) { /* Expected data. */ - u_char expected[] = { - "AAAAAAAA" - "JJJJJJJJ" - "JJJJJJJJ" - "JJJJJJJJ" - "JJJJJJJJ" - "BBBBBBBB" - "KKKKKKKK" - "KKKKKKKK" - "KKKKKKKK" - "LLLLLLLL" - "LLLLLLLL" - "LLLLLLLL" - "MMMMMMMM" - "MMMMMMMM" - "MMMMMMMM" - "FFFFFFFF" - "NNNNNNNN" - "FFFFFFFF" - "GGGGGGGG" - "OOOOOOOO" - "PPPPPPPP" - "HHHHHHHH" - "QQQQQQQQ" - "QQQQQQQQ" + uint8_t expected[] = { + D_1, + D_4, + D_4, + D_4, + D_4, + D_2, + D_5, + D_5, + D_5, + D_6, + D_6, + D_6, + D_7, + D_7, + D_7, + D_3_3, + D_8, + D_3_3, + D_3_4, + D_9, + D_10, + D_3_5, + D_11, + D_11, }; FAIL_IF_NOT(DefragDoSturgesNovakTest(DEFRAG_POLICY_LAST, expected, @@ -2053,38 +2262,37 @@ DefragSturgesNovakLastTest(void) PASS; } -static int IPV6DefragSturgesNovakLastTest(void) +static int DefragSturgesNovakLastIpv6Test(void) { /* Expected data. */ - u_char expected[] = { - "AAAAAAAA" - "JJJJJJJJ" - "JJJJJJJJ" - "JJJJJJJJ" - "JJJJJJJJ" - "BBBBBBBB" - "KKKKKKKK" - "KKKKKKKK" - "KKKKKKKK" - "LLLLLLLL" - "LLLLLLLL" - "LLLLLLLL" - "MMMMMMMM" - "MMMMMMMM" - "MMMMMMMM" - "FFFFFFFF" - "NNNNNNNN" - "FFFFFFFF" - "GGGGGGGG" - "OOOOOOOO" - "PPPPPPPP" - "HHHHHHHH" - "QQQQQQQQ" - "QQQQQQQQ" + uint8_t expected[] = { + D_1, + D_4, + D_4, + D_4, + D_4, + D_2, + D_5, + D_5, + D_5, + D_6, + D_6, + D_6, + D_7, + D_7, + D_7, + D_3_3, + D_8, + D_3_3, + D_3_4, + D_9, + D_10, + D_3_5, + D_11, + D_11, }; - FAIL_IF_NOT(IPV6DefragDoSturgesNovakTest(DEFRAG_POLICY_LAST, expected, - sizeof(expected))); + FAIL_IF_NOT(DefragDoSturgesNovakIpv6Test(DEFRAG_POLICY_LAST, expected, sizeof(expected))); PASS; } @@ -2099,7 +2307,7 @@ static int DefragTimeoutTest(void) /* Load in 16 packets. */ for (i = 0; i < 16; i++) { - Packet *p = BuildTestPacket(IPPROTO_ICMP,i, 0, 1, 'A' + i, 16); + Packet *p = BuildIpv4TestPacket(IPPROTO_ICMP, i, 0, 1, 'A' + i, 16); FAIL_IF_NULL(p); Packet *tp = Defrag(NULL, NULL, p); @@ -2109,7 +2317,7 @@ static int DefragTimeoutTest(void) /* Build a new packet but push the timestamp out by our timeout. * This should force our previous fragments to be timed out. */ - Packet *p = BuildTestPacket(IPPROTO_ICMP, 99, 0, 1, 'A' + i, 16); + Packet *p = BuildIpv4TestPacket(IPPROTO_ICMP, 99, 0, 1, 'A' + i, 16); FAIL_IF_NULL(p); p->ts = SCTIME_ADD_SECS(p->ts, defrag_context->timeout + 1); @@ -2134,7 +2342,7 @@ static int DefragTimeoutTest(void) * fail. The fix was simple, but this unit test is just to make sure * its not introduced. */ -static int DefragIPv4NoDataTest(void) +static int DefragNoDataIpv4Test(void) { DefragContext *dc = NULL; Packet *p = NULL; @@ -2146,7 +2354,7 @@ static int DefragIPv4NoDataTest(void) FAIL_IF_NULL(dc); /* This packet has an offset > 0, more frags set to 0 and no data. */ - p = BuildTestPacket(IPPROTO_ICMP, id, 1, 0, 'A', 0); + p = BuildIpv4TestPacket(IPPROTO_ICMP, id, 1, 0, 'A', 0); FAIL_IF_NULL(p); /* We do not expect a packet returned. */ @@ -2163,7 +2371,7 @@ static int DefragIPv4NoDataTest(void) PASS; } -static int DefragIPv4TooLargeTest(void) +static int DefragTooLargeIpv4Test(void) { DefragContext *dc = NULL; Packet *p = NULL; @@ -2175,7 +2383,7 @@ static int DefragIPv4TooLargeTest(void) /* Create a fragment that would extend past the max allowable size * for an IPv4 packet. */ - p = BuildTestPacket(IPPROTO_ICMP, 1, 8183, 0, 'A', 71); + p = BuildIpv4TestPacket(IPPROTO_ICMP, 1, 8183, 0, 'A', 71); FAIL_IF_NULL(p); /* We do not expect a packet returned. */ @@ -2206,9 +2414,9 @@ static int DefragVlanTest(void) DefragInit(); - p1 = BuildTestPacket(IPPROTO_ICMP, 1, 0, 1, 'A', 8); + p1 = BuildIpv4TestPacket(IPPROTO_ICMP, 1, 0, 1, 'A', 8); FAIL_IF_NULL(p1); - p2 = BuildTestPacket(IPPROTO_ICMP, 1, 1, 0, 'B', 8); + p2 = BuildIpv4TestPacket(IPPROTO_ICMP, 1, 1, 0, 'B', 8); FAIL_IF_NULL(p2); /* With no VLAN IDs set, packets should re-assemble. */ @@ -2238,9 +2446,9 @@ static int DefragVlanQinQTest(void) DefragInit(); - p1 = BuildTestPacket(IPPROTO_ICMP, 1, 0, 1, 'A', 8); + p1 = BuildIpv4TestPacket(IPPROTO_ICMP, 1, 0, 1, 'A', 8); FAIL_IF_NULL(p1); - p2 = BuildTestPacket(IPPROTO_ICMP, 1, 1, 0, 'B', 8); + p2 = BuildIpv4TestPacket(IPPROTO_ICMP, 1, 1, 0, 'B', 8); FAIL_IF_NULL(p2); /* With no VLAN IDs set, packets should re-assemble. */ @@ -2272,9 +2480,9 @@ static int DefragVlanQinQinQTest(void) DefragInit(); - Packet *p1 = BuildTestPacket(IPPROTO_ICMP, 1, 0, 1, 'A', 8); + Packet *p1 = BuildIpv4TestPacket(IPPROTO_ICMP, 1, 0, 1, 'A', 8); FAIL_IF_NULL(p1); - Packet *p2 = BuildTestPacket(IPPROTO_ICMP, 1, 1, 0, 'B', 8); + Packet *p2 = BuildIpv4TestPacket(IPPROTO_ICMP, 1, 1, 0, 'B', 8); FAIL_IF_NULL(p2); /* With no VLAN IDs set, packets should re-assemble. */ @@ -2308,7 +2516,7 @@ static int DefragTrackerReuseTest(void) /* Build a packet, its not a fragment but shouldn't matter for * this test. */ - p1 = BuildTestPacket(IPPROTO_ICMP, id, 0, 0, 'A', 8); + p1 = BuildIpv4TestPacket(IPPROTO_ICMP, id, 0, 0, 'A', 8); FAIL_IF_NULL(p1); /* Get a tracker. It shouldn't look like its already in use. */ @@ -2355,9 +2563,9 @@ static int DefragMfIpv4Test(void) DefragInit(); - Packet *p1 = BuildTestPacket(IPPROTO_ICMP, ip_id, 2, 1, 'C', 8); - Packet *p2 = BuildTestPacket(IPPROTO_ICMP, ip_id, 0, 1, 'A', 8); - Packet *p3 = BuildTestPacket(IPPROTO_ICMP, ip_id, 1, 0, 'B', 8); + Packet *p1 = BuildIpv4TestPacket(IPPROTO_ICMP, ip_id, 2, 1, 'C', 8); + Packet *p2 = BuildIpv4TestPacket(IPPROTO_ICMP, ip_id, 0, 1, 'A', 8); + Packet *p3 = BuildIpv4TestPacket(IPPROTO_ICMP, ip_id, 1, 0, 'B', 8); FAIL_IF(p1 == NULL || p2 == NULL || p3 == NULL); p = Defrag(NULL, NULL, p1); @@ -2374,6 +2582,10 @@ static int DefragMfIpv4Test(void) * fragments should be in the re-assembled packet. */ FAIL_IF(IPV4_GET_IPLEN(p) != 36); + /* Verify the payload of the IPv4 packet. */ + uint8_t expected_payload[] = "AAAAAAAABBBBBBBB"; + FAIL_IF(memcmp(GET_PKT_DATA(p) + sizeof(IPV4Hdr), expected_payload, sizeof(expected_payload))); + SCFree(p1); SCFree(p2); SCFree(p3); @@ -2398,9 +2610,9 @@ static int DefragMfIpv6Test(void) DefragInit(); - Packet *p1 = IPV6BuildTestPacket(IPPROTO_ICMPV6, ip_id, 2, 1, 'C', 8); - Packet *p2 = IPV6BuildTestPacket(IPPROTO_ICMPV6, ip_id, 0, 1, 'A', 8); - Packet *p3 = IPV6BuildTestPacket(IPPROTO_ICMPV6, ip_id, 1, 0, 'B', 8); + Packet *p1 = BuildIpv6TestPacket(IPPROTO_ICMPV6, ip_id, 2, 1, 'C', 8); + Packet *p2 = BuildIpv6TestPacket(IPPROTO_ICMPV6, ip_id, 0, 1, 'A', 8); + Packet *p3 = BuildIpv6TestPacket(IPPROTO_ICMPV6, ip_id, 1, 0, 'B', 8); FAIL_IF(p1 == NULL || p2 == NULL || p3 == NULL); p = Defrag(NULL, NULL, p1); @@ -2417,6 +2629,10 @@ static int DefragMfIpv6Test(void) * of 2 fragments, so 16. */ FAIL_IF(IPV6_GET_PLEN(p) != 16); + /* Verify the payload of the IPv4 packet. */ + uint8_t expected_payload[] = "AAAAAAAABBBBBBBB"; + FAIL_IF(memcmp(GET_PKT_DATA(p) + sizeof(IPV6Hdr), expected_payload, sizeof(expected_payload))); + SCFree(p1); SCFree(p2); SCFree(p3); @@ -2436,11 +2652,11 @@ static int DefragTestBadProto(void) DefragInit(); - p1 = BuildTestPacket(IPPROTO_ICMP, id, 0, 1, 'A', 8); + p1 = BuildIpv4TestPacket(IPPROTO_ICMP, id, 0, 1, 'A', 8); FAIL_IF_NULL(p1); - p2 = BuildTestPacket(IPPROTO_UDP, id, 1, 1, 'B', 8); + p2 = BuildIpv4TestPacket(IPPROTO_UDP, id, 1, 1, 'B', 8); FAIL_IF_NULL(p2); - p3 = BuildTestPacket(IPPROTO_ICMP, id, 2, 0, 'C', 3); + p3 = BuildIpv4TestPacket(IPPROTO_ICMP, id, 2, 0, 'C', 3); FAIL_IF_NULL(p3); FAIL_IF_NOT_NULL(Defrag(NULL, NULL, p1)); @@ -2461,19 +2677,19 @@ static int DefragTestBadProto(void) */ static int DefragTestJeremyLinux(void) { - char expected[] = "AAAAAAAA" - "AAAAAAAA" - "AAAAAAAA" - "CCCCCCCC" - "CCCCCCCC" - "CCCCCCCC" - "CCCCCCCC" - "CCCCCCCC" - "CCCCCCCC" - "BBBBBBBB" - "BBBBBBBB" - "DDDDDDDD" - "DDDDDD"; + uint8_t expected[] = "AAAAAAAA" + "AAAAAAAA" + "AAAAAAAA" + "CCCCCCCC" + "CCCCCCCC" + "CCCCCCCC" + "CCCCCCCC" + "CCCCCCCC" + "CCCCCCCC" + "BBBBBBBB" + "BBBBBBBB" + "DDDDDDDD" + "DDDDDD"; DefragInit(); default_policy = DEFRAG_POLICY_LINUX; @@ -2482,10 +2698,10 @@ static int DefragTestJeremyLinux(void) Packet *packets[4]; int i = 0; - packets[0] = BuildTestPacket(IPPROTO_ICMP, id, 0, 1, 'A', 24); - packets[1] = BuildTestPacket(IPPROTO_ICMP, id, 40 >> 3, 1, 'B', 48); - packets[2] = BuildTestPacket(IPPROTO_ICMP, id, 24 >> 3, 1, 'C', 48); - packets[3] = BuildTestPacket(IPPROTO_ICMP, id, 88 >> 3, 0, 'D', 14); + packets[0] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 0, 1, 'A', 24); + packets[1] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 40 >> 3, 1, 'B', 48); + packets[2] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 24 >> 3, 1, 'C', 48); + packets[3] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 88 >> 3, 0, 'D', 14); Packet *r = Defrag(NULL, NULL, packets[0]); FAIL_IF_NOT_NULL(r); @@ -2510,6 +2726,401 @@ static int DefragTestJeremyLinux(void) PASS; } +/** + * | 0 | 8 | 16 | 24 | 32 | + * |----------|----------|----------|----------|----------| + * | AAAAAAAA | AAAAAAAA | + * | | BBBBBBBB | BBBBBBBB | | | + * | | | CCCCCCCC | CCCCCCCC | | + * | DDDDDDDD | | | | | + * + * | DDDDDDDD | BBBBBBBB | BBBBBBBB | CCCCCCCC | AAAAAAAA | + */ +static int DefragBsdFragmentAfterNoMfIpv4Test(void) +{ + DefragInit(); + default_policy = DEFRAG_POLICY_BSD; + Packet *packets[4]; + + packets[0] = BuildIpv4TestPacket(IPPROTO_ICMP, 0x96, 24 >> 3, 0, 'A', 16); + packets[1] = BuildIpv4TestPacket(IPPROTO_ICMP, 0x96, 8 >> 3, 1, 'B', 16); + packets[2] = BuildIpv4TestPacket(IPPROTO_ICMP, 0x96, 16 >> 3, 1, 'C', 16); + packets[3] = BuildIpv4TestPacket(IPPROTO_ICMP, 0x96, 0, 1, 'D', 8); + + Packet *r = Defrag(NULL, NULL, packets[0]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[1]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[2]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[3]); + FAIL_IF_NULL(r); + + // clang-format off + uint8_t expected[] = { + 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', + 'B', 'B', 'B', 'B', 'B', 'B', 'B', 'B', + 'B', 'B', 'B', 'B', 'B', 'B', 'B', 'B', + 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + }; + // clang-format on + + if (memcmp(expected, GET_PKT_DATA(r) + 20, sizeof(expected)) != 0) { + printf("Expected:\n"); + PrintRawDataFp(stdout, expected, sizeof(expected)); + printf("Got:\n"); + PrintRawDataFp(stdout, GET_PKT_DATA(r) + 20, GET_PKT_LEN(r) - 20); + FAIL; + } + + DefragDestroy(); + PASS; +} + +static int DefragBsdFragmentAfterNoMfIpv6Test(void) +{ + DefragInit(); + default_policy = DEFRAG_POLICY_BSD; + Packet *packets[4]; + + packets[0] = BuildIpv6TestPacket(IPPROTO_ICMP, 0x96, 24 >> 3, 0, 'A', 16); + packets[1] = BuildIpv6TestPacket(IPPROTO_ICMP, 0x96, 8 >> 3, 1, 'B', 16); + packets[2] = BuildIpv6TestPacket(IPPROTO_ICMP, 0x96, 16 >> 3, 1, 'C', 16); + packets[3] = BuildIpv6TestPacket(IPPROTO_ICMP, 0x96, 0, 1, 'D', 8); + + Packet *r = Defrag(NULL, NULL, packets[0]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[1]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[2]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[3]); + FAIL_IF_NULL(r); + + // clang-format off + uint8_t expected[] = { + 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', + 'B', 'B', 'B', 'B', 'B', 'B', 'B', 'B', + 'B', 'B', 'B', 'B', 'B', 'B', 'B', 'B', + 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + }; + // clang-format on + + if (memcmp(expected, GET_PKT_DATA(r) + 40, sizeof(expected)) != 0) { + printf("Expected:\n"); + PrintRawDataFp(stdout, expected, sizeof(expected)); + printf("Got:\n"); + PrintRawDataFp(stdout, GET_PKT_DATA(r) + 40, GET_PKT_LEN(r) - 40); + FAIL; + } + + DefragDestroy(); + PASS; +} + +static int DefragBsdSubsequentOverlapsStartOfOriginalIpv4Test_2(void) +{ + DefragInit(); + default_policy = DEFRAG_POLICY_BSD; + Packet *packets[4]; + + /* Packet 1: off=16, mf=1 */ + packets[0] = BuildIpv4TestPacketWithContent( + IPPROTO_ICMP, 6, 16 >> 3, 1, (uint8_t *)"AABBCCDDAABBDDCC", 16); + + /* Packet 2: off=8, mf=1 */ + packets[1] = BuildIpv4TestPacketWithContent( + IPPROTO_ICMP, 6, 8 >> 3, 1, (uint8_t *)"AACCBBDDAACCDDBB", 16); + + /* Packet 3: off=0, mf=1: IP and ICMP header. */ + packets[2] = BuildIpv4TestPacketWithContent(IPPROTO_ICMP, 6, 0, 1, (uint8_t *)"ZZZZZZZZ", 8); + + /* Packet 4: off=8, mf=1 */ + packets[3] = + BuildIpv4TestPacketWithContent(IPPROTO_ICMP, 6, 32 >> 3, 0, (uint8_t *)"DDCCBBAA", 8); + + Packet *r = Defrag(NULL, NULL, packets[0]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[1]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[2]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[3]); + FAIL_IF_NULL(r); + + // clang-format off + const uint8_t expected[] = { + // AACCBBDD + // AACCDDBB + // AABBDDCC + // DDCCBBAA + 'A', 'A', 'C', 'C', 'B', 'B', 'D', 'D', + 'A', 'A', 'C', 'C', 'D', 'D', 'B', 'B', + 'A', 'A', 'B', 'B', 'D', 'D', 'C', 'C', + 'D', 'D', 'C', 'C', 'B', 'B', 'A', 'A', + }; + // clang-format on + + FAIL_IF(memcmp(expected, GET_PKT_DATA(r) + 20 + 8, sizeof(expected)) != 0); + + DefragDestroy(); + PASS; +} + +static int DefragBsdSubsequentOverlapsStartOfOriginalIpv6Test_2(void) +{ + DefragInit(); + default_policy = DEFRAG_POLICY_BSD; + Packet *packets[4]; + + /* Packet 1: off=16, mf=1 */ + packets[0] = BuildIpv6TestPacketWithContent( + IPPROTO_ICMP, 6, 16 >> 3, 1, (uint8_t *)"AABBCCDDAABBDDCC", 16); + + /* Packet 2: off=8, mf=1 */ + packets[1] = BuildIpv6TestPacketWithContent( + IPPROTO_ICMP, 6, 8 >> 3, 1, (uint8_t *)"AACCBBDDAACCDDBB", 16); + + /* Packet 3: off=0, mf=1: IP and ICMP header. */ + packets[2] = BuildIpv6TestPacketWithContent(IPPROTO_ICMP, 6, 0, 1, (uint8_t *)"ZZZZZZZZ", 8); + + /* Packet 4: off=8, mf=1 */ + packets[3] = + BuildIpv6TestPacketWithContent(IPPROTO_ICMP, 6, 32 >> 3, 0, (uint8_t *)"DDCCBBAA", 8); + + Packet *r = Defrag(NULL, NULL, packets[0]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[1]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[2]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[3]); + FAIL_IF_NULL(r); + + // clang-format off + const uint8_t expected[] = { + // AACCBBDD + // AACCDDBB + // AABBDDCC + // DDCCBBAA + 'A', 'A', 'C', 'C', 'B', 'B', 'D', 'D', + 'A', 'A', 'C', 'C', 'D', 'D', 'B', 'B', + 'A', 'A', 'B', 'B', 'D', 'D', 'C', 'C', + 'D', 'D', 'C', 'C', 'B', 'B', 'A', 'A', + }; + // clang-format on + + FAIL_IF(memcmp(expected, GET_PKT_DATA(r) + 40 + 8, sizeof(expected)) != 0); + + DefragDestroy(); + PASS; +} + +/** + * #### Input + * + * | 96 (0) | 104 (8) | 112 (16) | 120 (24) | + * |----------|----------|----------|----------| + * | | EEEEEEEE | EEEEEEEE | EEEEEEEE | + * | MMMMMMMM | MMMMMMMM | MMMMMMMM | | + * + * #### Expected Output + * + * | MMMMMMMM | MMMMMMMM | MMMMMMMM | EEEEEEEE | + */ +static int DefragBsdSubsequentOverlapsStartOfOriginalIpv4Test(void) +{ + DefragInit(); + default_policy = DEFRAG_POLICY_BSD; + Packet *packets[2]; + + packets[0] = BuildIpv4TestPacket(IPPROTO_ICMP, 1, 8 >> 3, 0, 'E', 24); + packets[1] = BuildIpv4TestPacket(IPPROTO_ICMP, 1, 0, 1, 'M', 24); + + Packet *r = Defrag(NULL, NULL, packets[0]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[1]); + FAIL_IF_NULL(r); + + // clang-format off + const uint8_t expected[] = { + 'M', 'M', 'M', 'M', 'M', 'M', 'M', 'M', + 'M', 'M', 'M', 'M', 'M', 'M', 'M', 'M', + 'M', 'M', 'M', 'M', 'M', 'M', 'M', 'M', + 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', + }; + // clang-format on + + if (memcmp(expected, GET_PKT_DATA(r) + 20, sizeof(expected)) != 0) { + printf("Expected:\n"); + PrintRawDataFp(stdout, expected, sizeof(expected)); + printf("Got:\n"); + PrintRawDataFp(stdout, GET_PKT_DATA(r) + 20, GET_PKT_LEN(r) - 20); + FAIL; + } + + PASS; +} + +static int DefragBsdSubsequentOverlapsStartOfOriginalIpv6Test(void) +{ + DefragInit(); + default_policy = DEFRAG_POLICY_BSD; + Packet *packets[2]; + + packets[0] = BuildIpv6TestPacket(IPPROTO_ICMP, 1, 8 >> 3, 0, 'E', 24); + packets[1] = BuildIpv6TestPacket(IPPROTO_ICMP, 1, 0, 1, 'M', 24); + + Packet *r = Defrag(NULL, NULL, packets[0]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[1]); + FAIL_IF_NULL(r); + + // clang-format off + const uint8_t expected[] = { + 'M', 'M', 'M', 'M', 'M', 'M', 'M', 'M', + 'M', 'M', 'M', 'M', 'M', 'M', 'M', 'M', + 'M', 'M', 'M', 'M', 'M', 'M', 'M', 'M', + 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', + }; + // clang-format on + + if (memcmp(expected, GET_PKT_DATA(r) + 40, sizeof(expected)) != 0) { + printf("Expected:\n"); + PrintRawDataFp(stdout, expected, sizeof(expected)); + printf("Got:\n"); + PrintRawDataFp(stdout, GET_PKT_DATA(r) + 40, GET_PKT_LEN(r) - 40); + FAIL; + } + + PASS; +} + +/** + * Reassembly should fail. + * + * |0 |8 |16 |24 |32 |40 |48 | + * |========|========|========|========|========|========|========| + * | | |AABBCCDD|AABBDDCC| | | | + * | | | | | |AACCBBDD| | + * | |AACCDDBB|AADDBBCC| | | | | + * |ZZZZZZZZ| | | | | | | + * | | | | | | |DDCCBBAA| + */ +static int DefragBsdMissingFragmentIpv4Test(void) +{ + DefragInit(); + default_policy = DEFRAG_POLICY_BSD; + Packet *packets[5]; + + packets[0] = BuildIpv4TestPacketWithContent( + IPPROTO_ICMP, 189, 16 >> 3, 1, (uint8_t *)"AABBCCDDAABBDDCC", 16); + + packets[1] = + BuildIpv4TestPacketWithContent(IPPROTO_ICMP, 189, 40 >> 3, 1, (uint8_t *)"AACCBBDD", 8); + + packets[2] = BuildIpv4TestPacketWithContent( + IPPROTO_ICMP, 189, 8 >> 3, 1, (uint8_t *)"AACCDDBBAADDBBCC", 16); + + /* ICMP header. */ + packets[3] = BuildIpv4TestPacketWithContent(IPPROTO_ICMP, 189, 0, 1, (uint8_t *)"ZZZZZZZZ", 8); + + packets[4] = + BuildIpv4TestPacketWithContent(IPPROTO_ICMP, 189, 48 >> 3, 0, (uint8_t *)"DDCCBBAA", 8); + + Packet *r = Defrag(NULL, NULL, packets[0]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[1]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[2]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[3]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[4]); + FAIL_IF_NOT_NULL(r); + +#if 0 + PrintRawDataFp(stdout, GET_PKT_DATA(r) + 20, GET_PKT_LEN(r) - 20); +#endif + + for (int i = 0; i < 5; i++) { + SCFree(packets[i]); + } + + DefragDestroy(); + + PASS; +} + +static int DefragBsdMissingFragmentIpv6Test(void) +{ + DefragInit(); + default_policy = DEFRAG_POLICY_BSD; + Packet *packets[5]; + + packets[0] = BuildIpv6TestPacketWithContent( + IPPROTO_ICMP, 189, 16 >> 3, 1, (uint8_t *)"AABBCCDDAABBDDCC", 16); + + packets[1] = + BuildIpv6TestPacketWithContent(IPPROTO_ICMP, 189, 40 >> 3, 1, (uint8_t *)"AACCBBDD", 8); + + packets[2] = BuildIpv6TestPacketWithContent( + IPPROTO_ICMP, 189, 8 >> 3, 1, (uint8_t *)"AACCDDBBAADDBBCC", 16); + + /* ICMP header. */ + packets[3] = BuildIpv6TestPacketWithContent(IPPROTO_ICMP, 189, 0, 1, (uint8_t *)"ZZZZZZZZ", 8); + + packets[4] = + BuildIpv6TestPacketWithContent(IPPROTO_ICMP, 189, 48 >> 3, 0, (uint8_t *)"DDCCBBAA", 8); + + Packet *r = Defrag(NULL, NULL, packets[0]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[1]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[2]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[3]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[4]); + FAIL_IF_NOT_NULL(r); + +#if 0 + PrintRawDataFp(stdout, GET_PKT_DATA(r) + 40, GET_PKT_LEN(r) - 40); +#endif + + for (int i = 0; i < 5; i++) { + SCFree(packets[i]); + } + + DefragDestroy(); + + PASS; +} + #endif /* UNITTESTS */ void DefragRegisterTests(void) @@ -2527,23 +3138,17 @@ void DefragRegisterTests(void) UtRegisterTest("DefragSturgesNovakFirstTest", DefragSturgesNovakFirstTest); UtRegisterTest("DefragSturgesNovakLastTest", DefragSturgesNovakLastTest); - UtRegisterTest("DefragIPv4NoDataTest", DefragIPv4NoDataTest); - UtRegisterTest("DefragIPv4TooLargeTest", DefragIPv4TooLargeTest); - - UtRegisterTest("IPV6DefragInOrderSimpleTest", IPV6DefragInOrderSimpleTest); - UtRegisterTest("IPV6DefragReverseSimpleTest", IPV6DefragReverseSimpleTest); - UtRegisterTest("IPV6DefragSturgesNovakBsdTest", - IPV6DefragSturgesNovakBsdTest); - UtRegisterTest("IPV6DefragSturgesNovakLinuxTest", - IPV6DefragSturgesNovakLinuxTest); - UtRegisterTest("IPV6DefragSturgesNovakWindowsTest", - IPV6DefragSturgesNovakWindowsTest); - UtRegisterTest("IPV6DefragSturgesNovakSolarisTest", - IPV6DefragSturgesNovakSolarisTest); - UtRegisterTest("IPV6DefragSturgesNovakFirstTest", - IPV6DefragSturgesNovakFirstTest); - UtRegisterTest("IPV6DefragSturgesNovakLastTest", - IPV6DefragSturgesNovakLastTest); + UtRegisterTest("DefragNoDataIpv4Test", DefragNoDataIpv4Test); + UtRegisterTest("DefragTooLargeIpv4Test", DefragTooLargeIpv4Test); + + UtRegisterTest("DefragInOrderSimpleIpv6Test", DefragInOrderSimpleIpv6Test); + UtRegisterTest("DefragReverseSimpleIpv6Test", DefragReverseSimpleIpv6Test); + UtRegisterTest("DefragSturgesNovakBsdIpv6Test", DefragSturgesNovakBsdIpv6Test); + UtRegisterTest("DefragSturgesNovakLinuxIpv6Test", DefragSturgesNovakLinuxIpv6Test); + UtRegisterTest("DefragSturgesNovakWindowsIpv6Test", DefragSturgesNovakWindowsIpv6Test); + UtRegisterTest("DefragSturgesNovakSolarisIpv6Test", DefragSturgesNovakSolarisIpv6Test); + UtRegisterTest("DefragSturgesNovakFirstIpv6Test", DefragSturgesNovakFirstIpv6Test); + UtRegisterTest("DefragSturgesNovakLastIpv6Test", DefragSturgesNovakLastIpv6Test); UtRegisterTest("DefragVlanTest", DefragVlanTest); UtRegisterTest("DefragVlanQinQTest", DefragVlanQinQTest); @@ -2555,5 +3160,16 @@ void DefragRegisterTests(void) UtRegisterTest("DefragTestBadProto", DefragTestBadProto); UtRegisterTest("DefragTestJeremyLinux", DefragTestJeremyLinux); + + UtRegisterTest("DefragBsdFragmentAfterNoMfIpv4Test", DefragBsdFragmentAfterNoMfIpv4Test); + UtRegisterTest("DefragBsdFragmentAfterNoMfIpv6Test", DefragBsdFragmentAfterNoMfIpv6Test); + UtRegisterTest("DefragBsdSubsequentOverlapsStartOfOriginalIpv4Test", + DefragBsdSubsequentOverlapsStartOfOriginalIpv4Test); + UtRegisterTest("DefragBsdSubsequentOverlapsStartOfOriginalIpv6Test", + DefragBsdSubsequentOverlapsStartOfOriginalIpv6Test); + UtRegisterTest("DefragBsdSubsequentOverlapsStartOfOriginalIpv4Test_2", DefragBsdSubsequentOverlapsStartOfOriginalIpv4Test_2); + UtRegisterTest("DefragBsdSubsequentOverlapsStartOfOriginalIpv6Test_2", DefragBsdSubsequentOverlapsStartOfOriginalIpv6Test_2); + UtRegisterTest("DefragBsdMissingFragmentIpv4Test", DefragBsdMissingFragmentIpv4Test); + UtRegisterTest("DefragBsdMissingFragmentIpv6Test", DefragBsdMissingFragmentIpv6Test); #endif /* UNITTESTS */ } diff --git a/src/defrag.h b/src/defrag.h index 11e6a61..93fe872 100644 --- a/src/defrag.h +++ b/src/defrag.h @@ -106,6 +106,7 @@ typedef struct DefragTracker_ { Address src_addr; /**< Source address for this tracker. */ Address dst_addr; /**< Destination address for this tracker. */ + int datalink; /**< datalink for reassembled packet, set by first fragment */ SCTime_t timeout; /**< When this tracker will timeout. */ uint32_t host_timeout; /**< Host timeout, statically assigned from the yaml */ diff --git a/src/detect-dataset.c b/src/detect-dataset.c index 3d29646..69eaf81 100644 --- a/src/detect-dataset.c +++ b/src/detect-dataset.c @@ -407,10 +407,6 @@ int DetectDatasetSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawst SCLogError("failed to set up dataset '%s'.", name); return -1; } - if (set->hash && SC_ATOMIC_GET(set->hash->memcap_reached)) { - SCLogError("dataset too large for set memcap"); - return -1; - } cd = SCCalloc(1, sizeof(DetectDatasetData)); if (unlikely(cd == NULL)) diff --git a/src/detect-fast-pattern.c b/src/detect-fast-pattern.c index b82f327..ef6007a 100644 --- a/src/detect-fast-pattern.c +++ b/src/detect-fast-pattern.c @@ -274,6 +274,9 @@ static int DetectFastPatternSetup(DetectEngineCtx *de_ctx, Signature *s, const c } } } + if (SigMatchListSMBelongsTo(s, pm) == DETECT_SM_LIST_BASE64_DATA) { + SCLogInfo("fast_pattern is ineffective with base64_data"); + } cd->flags |= DETECT_CONTENT_FAST_PATTERN; return 0; } diff --git a/src/detect-http-server-body.c b/src/detect-http-server-body.c index 98f0ec5..28833a8 100644 --- a/src/detect-http-server-body.c +++ b/src/detect-http-server-body.c @@ -124,6 +124,9 @@ static int DetectHttpServerBodySetupSticky(DetectEngineCtx *de_ctx, Signature *s return -1; if (DetectSignatureSetAppProto(s, ALPROTO_HTTP) < 0) return -1; + // file data is on both directions, but we only take the one to client here + s->flags |= SIG_FLAG_TOCLIENT; + s->flags &= ~SIG_FLAG_TOSERVER; return 0; } diff --git a/src/detect-ipopts.c b/src/detect-ipopts.c index 105751c..01b4712 100644 --- a/src/detect-ipopts.c +++ b/src/detect-ipopts.c @@ -25,23 +25,13 @@ #include "suricata-common.h" #include "suricata.h" -#include "decode.h" #include "detect.h" #include "detect-parse.h" -#include "flow-var.h" -#include "decode-events.h" - -#include "util-debug.h" - #include "detect-ipopts.h" #include "util-unittest.h" -#define PARSE_REGEX "\\S[A-z]" - -static DetectParseRegex parse_regex; - static int DetectIpOptsMatch (DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *); static int DetectIpOptsSetup (DetectEngineCtx *, Signature *, const char *); @@ -64,7 +54,6 @@ void DetectIpOptsRegister (void) #ifdef UNITTESTS sigmatch_table[DETECT_IPOPTS].RegisterTests = IpOptsRegisterTests; #endif - DetectSetupParseRegexes(PARSE_REGEX, &parse_regex); } /** @@ -173,11 +162,7 @@ static int DetectIpOptsMatch (DetectEngineThreadCtx *det_ctx, Packet *p, if (!de || !PKT_IS_IPV4(p) || PKT_IS_PSEUDOPKT(p)) return 0; - if (p->ip4vars.opts_set & de->ipopt) { - return 1; - } - - return 0; + return (p->ip4vars.opts_set & de->ipopt) == de->ipopt; } /** @@ -191,42 +176,30 @@ static int DetectIpOptsMatch (DetectEngineThreadCtx *det_ctx, Packet *p, */ static DetectIpOptsData *DetectIpOptsParse (const char *rawstr) { - int i; - DetectIpOptsData *de = NULL; - int found = 0; - - pcre2_match_data *match = NULL; - int ret = DetectParsePcreExec(&parse_regex, &match, rawstr, 0, 0); - if (ret < 1) { - SCLogError("pcre_exec parse error, ret %" PRId32 ", string %s", ret, rawstr); - goto error; - } + if (rawstr == NULL || strlen(rawstr) == 0) + return NULL; + int i; + bool found = false; for(i = 0; ipopts[i].ipopt_name != NULL; i++) { if((strcasecmp(ipopts[i].ipopt_name,rawstr)) == 0) { - found = 1; + found = true; break; } } - if(found == 0) - goto error; + if (!found) { + SCLogError("unknown IP option specified \"%s\"", rawstr); + return NULL; + } - de = SCMalloc(sizeof(DetectIpOptsData)); + DetectIpOptsData *de = SCMalloc(sizeof(DetectIpOptsData)); if (unlikely(de == NULL)) - goto error; + return NULL; de->ipopt = ipopts[i].code; - pcre2_match_data_free(match); return de; - -error: - if (match) { - pcre2_match_data_free(match); - } - if (de) SCFree(de); - return NULL; } /** @@ -242,10 +215,8 @@ error: */ static int DetectIpOptsSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) { - DetectIpOptsData *de = NULL; SigMatch *sm = NULL; - - de = DetectIpOptsParse(rawstr); + DetectIpOptsData *de = DetectIpOptsParse(rawstr); if (de == NULL) goto error; @@ -275,8 +246,9 @@ error: */ void DetectIpOptsFree(DetectEngineCtx *de_ctx, void *de_ptr) { - DetectIpOptsData *de = (DetectIpOptsData *)de_ptr; - if(de) SCFree(de); + if (de_ptr) { + SCFree(de_ptr); + } } /* @@ -381,6 +353,20 @@ static int IpOptsTestParse04 (void) } /** + * \test IpOptsTestParse05 tests the NULL and empty string + */ +static int IpOptsTestParse05(void) +{ + DetectIpOptsData *de = DetectIpOptsParse(""); + FAIL_IF_NOT_NULL(de); + + de = DetectIpOptsParse(NULL); + FAIL_IF_NOT_NULL(de); + + PASS; +} + +/** * \brief this function registers unit tests for IpOpts */ void IpOptsRegisterTests(void) @@ -389,5 +375,6 @@ void IpOptsRegisterTests(void) UtRegisterTest("IpOptsTestParse02", IpOptsTestParse02); UtRegisterTest("IpOptsTestParse03", IpOptsTestParse03); UtRegisterTest("IpOptsTestParse04", IpOptsTestParse04); + UtRegisterTest("IpOptsTestParse05", IpOptsTestParse05); } #endif /* UNITTESTS */ diff --git a/src/detect-parse.c b/src/detect-parse.c index c3232b9..5dee7e6 100644 --- a/src/detect-parse.c +++ b/src/detect-parse.c @@ -2701,7 +2701,7 @@ int DetectParsePcreExec(DetectParseRegex *parse_regex, pcre2_match_data **match, *match = pcre2_match_data_create_from_pattern(parse_regex->regex, NULL); if (*match) return pcre2_match(parse_regex->regex, (PCRE2_SPTR8)str, strlen(str), options, start_offset, - *match, NULL); + *match, parse_regex->context); return -1; } @@ -2761,8 +2761,16 @@ bool DetectSetupParseRegexesOpts(const char *parse_str, DetectParseRegex *detect parse_str, en, errbuffer); return false; } - detect_parse->match = pcre2_match_data_create_from_pattern(detect_parse->regex, NULL); + detect_parse->context = pcre2_match_context_create(NULL); + if (detect_parse->context == NULL) { + SCLogError("pcre2 could not create match context"); + pcre2_code_free(detect_parse->regex); + detect_parse->regex = NULL; + return false; + } + pcre2_set_match_limit(detect_parse->context, SC_MATCH_LIMIT_DEFAULT); + pcre2_set_recursion_limit(detect_parse->context, SC_MATCH_LIMIT_RECURSION_DEFAULT); DetectParseRegexAddToFreeList(detect_parse); return true; diff --git a/src/flow-timeout.c b/src/flow-timeout.c index e5d2794..6efa382 100644 --- a/src/flow-timeout.c +++ b/src/flow-timeout.c @@ -341,14 +341,20 @@ int FlowForceReassemblyNeedReassembly(Flow *f) * * The function requires flow to be locked beforehand. * + * Normally, the first thread_id value should be used. This is when the flow is + * created on seeing the first packet to the server; when the flow's reversed + * flag is set, choose the second thread_id (to client/source). + * * \param f Pointer to the flow. * * \retval 0 This flow doesn't need any reassembly processing; 1 otherwise. */ void FlowForceReassemblyForFlow(Flow *f) { - const int thread_id = (int)f->thread_id[0]; - TmThreadsInjectFlowById(f, thread_id); + // Choose the thread_id based on whether the flow has been + // reversed. + int idx = f->flags & FLOW_DIR_REVERSED ? 1 : 0; + TmThreadsInjectFlowById(f, (const int)f->thread_id[idx]); } /** @@ -291,6 +291,8 @@ void FlowSwap(Flow *f) FlowSwapFlags(f); FlowSwapFileFlags(f); + SWAP_VARS(FlowThreadId, f->thread_id[0], f->thread_id[1]); + if (f->proto == IPPROTO_TCP) { TcpStreamFlowSwap(f); } diff --git a/src/output-json-stats.c b/src/output-json-stats.c index 33f98af..7cc8807 100644 --- a/src/output-json-stats.c +++ b/src/output-json-stats.c @@ -265,23 +265,30 @@ json_t *StatsToJSON(const StatsTable *st, uint8_t flags) uint32_t x; for (x = 0; x < st->ntstats; x++) { uint32_t offset = x * st->nstats; - - // Stats for for this thread. - json_t *thread = json_object(); - if (unlikely(thread == NULL)) { - json_decref(js_stats); - json_decref(threads); - return NULL; - } + const char *tm_name = NULL; + json_t *thread = NULL; /* for each counter */ for (u = offset; u < (offset + st->nstats); u++) { if (st->tstats[u].name == NULL) continue; - // Seems this holds, but assert in debug builds. - DEBUG_VALIDATE_BUG_ON( - strcmp(st->tstats[offset].tm_name, st->tstats[u].tm_name) != 0); + DEBUG_VALIDATE_BUG_ON(st->tstats[u].tm_name == NULL); + + if (tm_name == NULL) { + // First time we see a set tm_name. Remember it + // and allocate the stats object for this thread. + tm_name = st->tstats[u].tm_name; + thread = json_object(); + if (unlikely(thread == NULL)) { + json_decref(js_stats); + json_decref(threads); + return NULL; + } + } else { + DEBUG_VALIDATE_BUG_ON(strcmp(tm_name, st->tstats[u].tm_name) != 0); + DEBUG_VALIDATE_BUG_ON(thread == NULL); + } json_t *js_type = NULL; const char *stat_name = st->tstats[u].short_name; @@ -303,7 +310,10 @@ json_t *StatsToJSON(const StatsTable *st, uint8_t flags) } } } - json_object_set_new(threads, st->tstats[offset].tm_name, thread); + if (tm_name != NULL) { + DEBUG_VALIDATE_BUG_ON(thread == NULL); + json_object_set_new(threads, tm_name, thread); + } } json_object_set_new(js_stats, "threads", threads); } diff --git a/src/runmode-af-packet.c b/src/runmode-af-packet.c index b8ad0bf..742d968 100644 --- a/src/runmode-af-packet.c +++ b/src/runmode-af-packet.c @@ -90,7 +90,6 @@ static int AFPRunModeIsIPS(void) SCLogError("Problem with config file"); return 0; } - const char *copymodestr = NULL; if_root = ConfFindDeviceConfig(af_packet_node, live_dev); if (if_root == NULL) { @@ -101,7 +100,10 @@ static int AFPRunModeIsIPS(void) if_root = if_default; } - if (ConfGetChildValueWithDefault(if_root, if_default, "copy-mode", ©modestr) == 1) { + const char *copymodestr = NULL; + const char *copyifacestr = NULL; + if (ConfGetChildValueWithDefault(if_root, if_default, "copy-mode", ©modestr) == 1 && + ConfGetChildValue(if_root, "copy-iface", ©ifacestr) == 1) { if (strcmp(copymodestr, "ips") == 0) { has_ips = 1; } else { diff --git a/src/runmode-dpdk.c b/src/runmode-dpdk.c index 1a240aa..fb49d6e 100644 --- a/src/runmode-dpdk.c +++ b/src/runmode-dpdk.c @@ -54,9 +54,12 @@ #define RSS_HKEY_LEN 40 // General purpose RSS key for symmetric bidirectional flow distribution -uint8_t rss_hkey[] = { 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, - 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, - 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A }; +uint8_t rss_hkey[] = { + 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, + 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, + 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, // 40 + 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, // 52 +}; // Calculates the closest multiple of y from x #define ROUNDUP(x, y) ((((x) + ((y)-1)) / (y)) * (y)) @@ -111,6 +114,7 @@ static void *ParseDpdkConfigAndConfigureDevice(const char *iface); static void DPDKDerefConfig(void *conf); #define DPDK_CONFIG_DEFAULT_THREADS "auto" +#define DPDK_CONFIG_DEFAULT_INTERRUPT_MODE false #define DPDK_CONFIG_DEFAULT_MEMPOOL_SIZE 65535 #define DPDK_CONFIG_DEFAULT_MEMPOOL_CACHE_SIZE "auto" #define DPDK_CONFIG_DEFAULT_RX_DESCRIPTORS 1024 @@ -126,6 +130,7 @@ static void DPDKDerefConfig(void *conf); DPDKIfaceConfigAttributes dpdk_yaml = { .threads = "threads", + .irq_mode = "interrupt-mode", .promisc = "promisc", .multicast = "multicast", .checksum_checks = "checksum-checks", @@ -434,6 +439,15 @@ static int ConfigSetThreads(DPDKIfaceConfig *iconf, const char *entry_str) SCReturnInt(0); } +static bool ConfigSetInterruptMode(DPDKIfaceConfig *iconf, bool enable) +{ + SCEnter(); + if (enable) + iconf->flags |= DPDK_IRQ_MODE; + + SCReturnBool(true); +} + static int ConfigSetRxQueues(DPDKIfaceConfig *iconf, uint16_t nb_queues) { SCEnter(); @@ -704,6 +718,17 @@ static int ConfigLoad(DPDKIfaceConfig *iconf, const char *iface) if (retval < 0) SCReturnInt(retval); + bool irq_enable; + retval = ConfGetChildValueBoolWithDefault(if_root, if_default, dpdk_yaml.irq_mode, &entry_bool); + if (retval != 1) { + irq_enable = DPDK_CONFIG_DEFAULT_INTERRUPT_MODE; + } else { + irq_enable = entry_bool ? true : false; + } + retval = ConfigSetInterruptMode(iconf, irq_enable); + if (retval != true) + SCReturnInt(-EINVAL); + // currently only mapping "1 thread == 1 RX (and 1 TX queue in IPS mode)" is supported retval = ConfigSetRxQueues(iconf, (uint16_t)iconf->threads); if (retval < 0) @@ -846,7 +871,7 @@ static void DeviceSetPMDSpecificRSS(struct rte_eth_rss_conf *rss_conf, const cha if (strcmp(driver_name, "net_i40e") == 0) i40eDeviceSetRSSConf(rss_conf); if (strcmp(driver_name, "net_ice") == 0) - iceDeviceSetRSSHashFunction(&rss_conf->rss_hf); + iceDeviceSetRSSConf(rss_conf); if (strcmp(driver_name, "net_ixgbe") == 0) ixgbeDeviceSetRSSHashFunction(&rss_conf->rss_hf); if (strcmp(driver_name, "net_e1000_igb") == 0) @@ -1115,6 +1140,11 @@ static void DeviceInitPortConf(const DPDKIfaceConfig *iconf, }, }; + SCLogConfig("%s: interrupt mode is %s", iconf->iface, + iconf->flags & DPDK_IRQ_MODE ? "enabled" : "disabled"); + if (iconf->flags & DPDK_IRQ_MODE) + port_conf->intr_conf.rxq = 1; + // configure RX offloads if (dev_info->rx_offload_capa & RTE_ETH_RX_OFFLOAD_RSS_HASH) { if (iconf->nb_rx_queues > 1) { @@ -1616,7 +1646,9 @@ static int DPDKRunModeIsIPS(void) } const char *copymodestr = NULL; - if (ConfGetChildValueWithDefault(if_root, if_default, "copy-mode", ©modestr) == 1) { + const char *copyifacestr = NULL; + if (ConfGetChildValueWithDefault(if_root, if_default, "copy-mode", ©modestr) == 1 && + ConfGetChildValue(if_root, "copy-iface", ©ifacestr) == 1) { if (strcmp(copymodestr, "ips") == 0) { has_ips = true; } else { diff --git a/src/runmode-dpdk.h b/src/runmode-dpdk.h index a00327b..152c1d6 100644 --- a/src/runmode-dpdk.h +++ b/src/runmode-dpdk.h @@ -25,6 +25,7 @@ typedef struct DPDKIfaceConfigAttributes_ { const char *threads; + const char *irq_mode; const char *promisc; const char *multicast; const char *checksum_checks; diff --git a/src/runmode-netmap.c b/src/runmode-netmap.c index e207cf0..947b381 100644 --- a/src/runmode-netmap.c +++ b/src/runmode-netmap.c @@ -81,7 +81,6 @@ static int NetmapRunModeIsIPS(void) SCLogError("Problem with config file"); return 0; } - const char *copymodestr = NULL; if_root = ConfNodeLookupKeyValue(netmap_node, "interface", live_dev); if (if_root == NULL) { @@ -92,7 +91,10 @@ static int NetmapRunModeIsIPS(void) if_root = if_default; } - if (ConfGetChildValueWithDefault(if_root, if_default, "copy-mode", ©modestr) == 1) { + const char *copymodestr = NULL; + const char *copyifacestr = NULL; + if (ConfGetChildValueWithDefault(if_root, if_default, "copy-mode", ©modestr) == 1 && + ConfGetChildValue(if_root, "copy-iface", ©ifacestr) == 1) { if (strcmp(copymodestr, "ips") == 0) { has_ips = 1; } else { diff --git a/src/runmodes.c b/src/runmodes.c index 348adfa..d4b57df 100644 --- a/src/runmodes.c +++ b/src/runmodes.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2022 Open Information Security Foundation +/* Copyright (C) 2007-2024 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -70,6 +70,7 @@ #include "counters.h" #include "suricata-plugin.h" +#include "util-device.h" int debuglog_enabled = 0; int threading_set_cpu_affinity = FALSE; @@ -408,6 +409,14 @@ void RunModeEngineIsIPS(int capture_mode, const char *runmode, const char *captu if (mode->RunModeIsIPSEnabled != NULL) { mode->RunModeIsIPSEnabled(); + + if (EngineModeIsIPS()) { + extern uint16_t g_livedev_mask; + if (g_livedev_mask != 0 && LiveGetDeviceCount() > 0) { + SCLogWarning("disabling livedev.use-for-tracking with IPS mode. See ticket #6726."); + g_livedev_mask = 0; + } + } } } diff --git a/src/source-dpdk.c b/src/source-dpdk.c index cf26af5..6a7833e 100644 --- a/src/source-dpdk.c +++ b/src/source-dpdk.c @@ -93,6 +93,13 @@ TmEcode NoDPDKSupportExit(ThreadVars *tv, const void *initdata, void **data) #define BURST_SIZE 32 static struct timeval machine_start_time = { 0, 0 }; +// interrupt mode constants +#define MIN_ZERO_POLL_COUNT 10U +#define MIN_ZERO_POLL_COUNT_TO_SLEEP 10U +#define MINIMUM_SLEEP_TIME_US 1U +#define STANDARD_SLEEP_TIME_US 100U +#define MAX_EPOLL_TIMEOUT_MS 500U +static rte_spinlock_t intr_lock[RTE_MAX_ETHPORTS]; /** * \brief Structure to hold thread specific variables. @@ -104,6 +111,7 @@ typedef struct DPDKThreadVars_ { TmSlot *slot; LiveDevice *livedev; ChecksumValidationMode checksum_mode; + bool intr_enabled; /* references to packet and drop counters */ uint16_t capture_dpdk_packets; uint16_t capture_dpdk_rx_errs; @@ -142,6 +150,40 @@ static uint64_t CyclesToSeconds(uint64_t cycles); static void DPDKFreeMbufArray(struct rte_mbuf **mbuf_array, uint16_t mbuf_cnt, uint16_t offset); static uint64_t DPDKGetSeconds(void); +static bool InterruptsRXEnable(uint16_t port_id, uint16_t queue_id) +{ + uint32_t event_data = port_id << UINT16_WIDTH | queue_id; + int32_t ret = rte_eth_dev_rx_intr_ctl_q(port_id, queue_id, RTE_EPOLL_PER_THREAD, + RTE_INTR_EVENT_ADD, (void *)((uintptr_t)event_data)); + + if (ret != 0) { + SCLogError("%s-Q%d: failed to enable interrupt mode: %s", DPDKGetPortNameByPortID(port_id), + queue_id, rte_strerror(-ret)); + return false; + } + return true; +} + +static inline uint32_t InterruptsSleepHeuristic(uint32_t no_pkt_polls_count) +{ + if (no_pkt_polls_count < MIN_ZERO_POLL_COUNT_TO_SLEEP) + return MINIMUM_SLEEP_TIME_US; + + return STANDARD_SLEEP_TIME_US; +} + +static inline void InterruptsTurnOnOff(uint16_t port_id, uint16_t queue_id, bool on) +{ + rte_spinlock_lock(&(intr_lock[port_id])); + + if (on) + rte_eth_dev_rx_intr_enable(port_id, queue_id); + else + rte_eth_dev_rx_intr_disable(port_id, queue_id); + + rte_spinlock_unlock(&(intr_lock[port_id])); +} + static void DPDKFreeMbufArray(struct rte_mbuf **mbuf_array, uint16_t mbuf_cnt, uint16_t offset) { for (int i = offset; i < mbuf_cnt; i++) { @@ -377,6 +419,11 @@ static TmEcode ReceiveDPDKLoop(ThreadVars *tv, void *data, void *slot) rte_eth_stats_reset(ptv->port_id); rte_eth_xstats_reset(ptv->port_id); + + uint32_t pwd_zero_rx_packet_polls_count = 0; + if (ptv->intr_enabled && !InterruptsRXEnable(ptv->port_id, ptv->queue_id)) + SCReturnInt(TM_ECODE_FAILED); + while (1) { if (unlikely(suricata_ctl_flags != 0)) { SCLogDebug("Stopping Suricata!"); @@ -398,7 +445,27 @@ static TmEcode ReceiveDPDKLoop(ThreadVars *tv, void *data, void *slot) TmThreadsCaptureHandleTimeout(tv, NULL); last_timeout_msec = msecs; } - continue; + + if (!ptv->intr_enabled) + continue; + + pwd_zero_rx_packet_polls_count++; + if (pwd_zero_rx_packet_polls_count <= MIN_ZERO_POLL_COUNT) + continue; + + uint32_t pwd_idle_hint = InterruptsSleepHeuristic(pwd_zero_rx_packet_polls_count); + + if (pwd_idle_hint < STANDARD_SLEEP_TIME_US) { + rte_delay_us(pwd_idle_hint); + } else { + InterruptsTurnOnOff(ptv->port_id, ptv->queue_id, true); + struct rte_epoll_event event; + rte_epoll_wait(RTE_EPOLL_PER_THREAD, &event, 1, MAX_EPOLL_TIMEOUT_MS); + InterruptsTurnOnOff(ptv->port_id, ptv->queue_id, false); + continue; + } + } else if (ptv->intr_enabled && pwd_zero_rx_packet_polls_count) { + pwd_zero_rx_packet_polls_count = 0; } ptv->pkts += (uint64_t)nb_rx; @@ -522,6 +589,7 @@ static TmEcode ReceiveDPDKThreadInit(ThreadVars *tv, const void *initdata, void ptv->checksum_mode = dpdk_config->checksum_mode; ptv->threads = dpdk_config->threads; + ptv->intr_enabled = (dpdk_config->flags & DPDK_IRQ_MODE) ? true : false; ptv->port_id = dpdk_config->port_id; ptv->out_port_id = dpdk_config->out_port_id; ptv->port_socket_id = dpdk_config->socket_id; @@ -569,6 +637,9 @@ static TmEcode ReceiveDPDKThreadInit(ThreadVars *tv, const void *initdata, void "%s: unable to determine NIC's NUMA node, degraded performance can be expected", dpdk_config->iface); } + if (ptv->intr_enabled) { + rte_spinlock_init(&intr_lock[ptv->port_id]); + } } *data = (void *)ptv; diff --git a/src/source-dpdk.h b/src/source-dpdk.h index 3fdb63c..b962d86 100644 --- a/src/source-dpdk.h +++ b/src/source-dpdk.h @@ -38,6 +38,7 @@ typedef enum { DPDK_COPY_MODE_NONE, DPDK_COPY_MODE_TAP, DPDK_COPY_MODE_IPS } Dpd // General flags #define DPDK_PROMISC (1 << 0) /**< Promiscuous mode */ #define DPDK_MULTICAST (1 << 1) /**< Enable multicast packets */ +#define DPDK_IRQ_MODE (1 << 2) /**< Interrupt mode */ // Offloads #define DPDK_RX_CHECKSUM_OFFLOAD (1 << 4) /**< Enable chsum offload */ diff --git a/src/source-pcap-file-helper.c b/src/source-pcap-file-helper.c index 936b65f..4984a44 100644 --- a/src/source-pcap-file-helper.c +++ b/src/source-pcap-file-helper.c @@ -251,6 +251,7 @@ TmEcode ValidateLinkType(int datalink, DecoderFunc *DecoderFn) *DecoderFn = DecodePPP; break; case LINKTYPE_IPV4: + case LINKTYPE_IPV6: case LINKTYPE_RAW: case LINKTYPE_RAW2: case LINKTYPE_GRE_OVER_IP: diff --git a/src/tests/detect-http-client-body.c b/src/tests/detect-http-client-body.c index c87d667..bbeb4d3 100644 --- a/src/tests/detect-http-client-body.c +++ b/src/tests/detect-http-client-body.c @@ -157,6 +157,7 @@ static int RunTest (struct TestSteps *steps, const char *sig, const char *yaml) int i = 0; while (b->input != NULL) { SCLogDebug("chunk %p %d", b, i); + (void)i; Packet *p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); FAIL_IF_NULL(p); p->flow = &f; diff --git a/src/tests/detect-http-server-body.c b/src/tests/detect-http-server-body.c index 29340fb..d9723d4 100644 --- a/src/tests/detect-http-server-body.c +++ b/src/tests/detect-http-server-body.c @@ -119,6 +119,7 @@ static int RunTest(struct TestSteps *steps, const char *sig, const char *yaml) int i = 0; while (b->input != NULL) { SCLogDebug("chunk %p %d", b, i); + (void)i; Packet *p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); FAIL_IF_NULL(p); p->flow = &f; diff --git a/src/tests/output-json-stats.c b/src/tests/output-json-stats.c index ac1336e..332a819 100644 --- a/src/tests/output-json-stats.c +++ b/src/tests/output-json-stats.c @@ -23,7 +23,7 @@ static int OutputJsonStatsTest01(void) { - StatsRecord global_records[] = { { 0 }, { 0 } }; + StatsRecord total_records[] = { { 0 }, { 0 } }; StatsRecord thread_records[2]; thread_records[0].name = "capture.kernel_packets"; thread_records[0].short_name = "kernel_packets"; @@ -36,7 +36,7 @@ static int OutputJsonStatsTest01(void) StatsTable table = { .nstats = 2, - .stats = &global_records[0], + .stats = &total_records[0], .ntstats = 1, .tstats = &thread_records[0], }; @@ -64,7 +64,72 @@ static int OutputJsonStatsTest01(void) return cmp_result == 0; } +static int OutputJsonStatsTest02(void) +{ + StatsRecord total_records[4] = { 0 }; + StatsRecord thread_records[8] = { 0 }; + + // Totals + total_records[0].name = "tcp.syn"; + total_records[0].short_name = "syn"; + total_records[0].tm_name = NULL; + total_records[0].value = 1234; + + // Worker + // thread_records[0] is a global counter + thread_records[1].name = "capture.kernel_packets"; + thread_records[1].short_name = "kernel_packets"; + thread_records[1].tm_name = "W#01-bond0.30"; + thread_records[1].value = 42; + thread_records[2].name = "capture.kernel_drops"; + thread_records[2].short_name = "kernel_drops"; + thread_records[2].tm_name = "W#01-bond0.30"; + thread_records[2].value = 4711; + // thread_records[3] is a FM specific counter + + // Flow manager + // thread_records[4] is a global counter + // thread_records[5] is a worker specific counter + // thread_records[6] is a worker specific counter + thread_records[7].name = "flow.mgr.full_hash_passes"; + thread_records[7].short_name = "full_hash_passes"; + thread_records[7].tm_name = "FM#01"; + thread_records[7].value = 10; + + StatsTable table = { + .nstats = 4, + .stats = &total_records[0], + .ntstats = 2, + .tstats = &thread_records[0], + }; + + json_t *r = StatsToJSON(&table, JSON_STATS_TOTALS | JSON_STATS_THREADS); + if (!r) + return 0; + + // Remove variable content + json_object_del(r, "uptime"); + + char *serialized = json_dumps(r, 0); + + // Cheesy comparison + const char *expected = "{\"tcp\": {\"syn\": 1234}, \"threads\": {\"W#01-bond0.30\": " + "{\"capture\": {\"kernel_packets\": " + "42, \"kernel_drops\": 4711}}, \"FM#01\": {\"flow\": {\"mgr\": " + "{\"full_hash_passes\": 10}}}}}"; + + int cmp_result = strcmp(expected, serialized); + if (cmp_result != 0) + printf("unexpected result\nexpected=%s\ngot=%s\n", expected, serialized); + + free(serialized); + json_decref(r); + + return cmp_result == 0; +} + void OutputJsonStatsRegisterTests(void) { UtRegisterTest("OutputJsonStatsTest01", OutputJsonStatsTest01); + UtRegisterTest("OutputJsonStatsTest02", OutputJsonStatsTest02); } diff --git a/src/util-base64.c b/src/util-base64.c index 4a4a5d1..d973f0e 100644 --- a/src/util-base64.c +++ b/src/util-base64.c @@ -156,6 +156,8 @@ Base64Ecode DecodeBase64(uint8_t *dest, uint32_t dest_size, const uint8_t *src, ecode = BASE64_ECODE_BUF; break; } + if (dest_size - *decoded_bytes < ASCII_BLOCK) + return BASE64_ECODE_BUF; /* Decode base-64 block into ascii block and move pointer */ DecodeBase64Block(dptr, b64); @@ -183,7 +185,7 @@ Base64Ecode DecodeBase64(uint8_t *dest, uint32_t dest_size, const uint8_t *src, /* if the destination size is not at least 3 Bytes long, it'll give a dynamic * buffer overflow while decoding, so, return and let the caller take care of the * remaining bytes to be decoded which should always be < 4 at this stage */ - if (dest_size - *decoded_bytes < 3) + if (dest_size - *decoded_bytes < ASCII_BLOCK) return BASE64_ECODE_BUF; *decoded_bytes += numDecoded_blk; DecodeBase64Block(dptr, b64); @@ -193,6 +195,8 @@ Base64Ecode DecodeBase64(uint8_t *dest, uint32_t dest_size, const uint8_t *src, /* Finish remaining b64 bytes by padding */ if (valid && bbidx > 0 && (mode != BASE64_MODE_RFC2045)) { /* Decode remaining */ + if (dest_size - *decoded_bytes < ASCII_BLOCK) + return BASE64_ECODE_BUF; *decoded_bytes += ASCII_BLOCK - (B64_BLOCK - bbidx); DecodeBase64Block(dptr, b64); } diff --git a/src/util-dpdk-ice.c b/src/util-dpdk-ice.c index 36f4481..4b714d8 100644 --- a/src/util-dpdk-ice.c +++ b/src/util-dpdk-ice.c @@ -35,7 +35,7 @@ #ifdef HAVE_DPDK -void iceDeviceSetRSSHashFunction(uint64_t *rss_hf) +static void iceDeviceSetRSSHashFunction(uint64_t *rss_hf) { #if RTE_VERSION < RTE_VERSION_NUM(20, 0, 0, 0) *rss_hf = RTE_ETH_RSS_FRAG_IPV4 | RTE_ETH_RSS_NONFRAG_IPV4_OTHER | RTE_ETH_RSS_FRAG_IPV6 | @@ -46,6 +46,16 @@ void iceDeviceSetRSSHashFunction(uint64_t *rss_hf) #endif } +void iceDeviceSetRSSConf(struct rte_eth_rss_conf *rss_conf) +{ + iceDeviceSetRSSHashFunction(&rss_conf->rss_hf); +#if RTE_VERSION < RTE_VERSION_NUM(23, 11, 0, 0) + rss_conf->rss_key_len = 40; +#else + rss_conf->rss_key_len = 52; +#endif +} + #endif /* HAVE_DPDK */ /** * @} diff --git a/src/util-dpdk-ice.h b/src/util-dpdk-ice.h index cdc2185..d535fa0 100644 --- a/src/util-dpdk-ice.h +++ b/src/util-dpdk-ice.h @@ -28,7 +28,9 @@ #ifdef HAVE_DPDK -void iceDeviceSetRSSHashFunction(uint64_t *rss_conf); +#include "util-dpdk.h" + +void iceDeviceSetRSSConf(struct rte_eth_rss_conf *rss_conf); #endif /* HAVE_DPDK */ diff --git a/src/util-host-info.c b/src/util-host-info.c index c0dd93b..282b2fb 100644 --- a/src/util-host-info.c +++ b/src/util-host-info.c @@ -44,7 +44,6 @@ int SCKernelVersionIsAtLeast(int major, int minor) PCRE2_SIZE eo; int ret; int kmajor, kminor; - PCRE2_UCHAR **list; /* get local version */ if (uname(&kuname) != 0) { @@ -79,25 +78,36 @@ int SCKernelVersionIsAtLeast(int major, int minor) goto error; } - pcre2_substring_list_get(version_regex_match, &list, NULL); + char majorstr[32]; + size_t pcre2len = sizeof(majorstr); + ret = pcre2_substring_copy_bynumber( + version_regex_match, 1, (PCRE2_UCHAR8 *)majorstr, &pcre2len); + if (ret < 0) { + SCLogError("pcre2_substring_copy_bynumber failed"); + goto error; + } - bool err = false; - if (StringParseInt32(&kmajor, 10, 0, (const char *)list[1]) < 0) { - SCLogError("Invalid value for kmajor: '%s'", list[1]); - err = true; + char minorstr[32]; + pcre2len = sizeof(majorstr); + ret = pcre2_substring_copy_bynumber( + version_regex_match, 2, (PCRE2_UCHAR8 *)minorstr, &pcre2len); + if (ret < 0) { + SCLogError("pcre2_substring_copy_bynumber failed"); + goto error; } - if (StringParseInt32(&kminor, 10, 0, (const char *)list[2]) < 0) { - SCLogError("Invalid value for kminor: '%s'", list[2]); - err = true; + + if (StringParseInt32(&kmajor, 10, 0, (const char *)majorstr) < 0) { + SCLogError("Invalid value for kmajor: '%s'", minorstr); + goto error; + } + if (StringParseInt32(&kminor, 10, 0, (const char *)minorstr) < 0) { + SCLogError("Invalid value for kminor: '%s'", minorstr); + goto error; } - pcre2_substring_list_free((PCRE2_SPTR *)list); pcre2_match_data_free(version_regex_match); pcre2_code_free(version_regex); - if (err) - goto error; - if (kmajor > major) return 1; if (kmajor == major && kminor >= minor) diff --git a/src/util-streaming-buffer.c b/src/util-streaming-buffer.c index 6ff4f43..204ef2e 100644 --- a/src/util-streaming-buffer.c +++ b/src/util-streaming-buffer.c @@ -1064,7 +1064,15 @@ void StreamingBufferSlideToOffset( DEBUG_VALIDATE_BUG_ON(sb->region.stream_offset < offset); } -#define DATA_FITS(sb, len) ((sb)->region.buf_offset + (len) <= (sb)->region.buf_size) +static int DataFits(const StreamingBuffer *sb, const uint32_t len) +{ + uint64_t buf_offset64 = sb->region.buf_offset; + uint64_t len64 = len; + if (len64 + buf_offset64 > UINT32_MAX) { + return -1; + } + return sb->region.buf_offset + len <= sb->region.buf_size; +} int StreamingBufferAppend(StreamingBuffer *sb, const StreamingBufferConfig *cfg, StreamingBufferSegment *seg, const uint8_t *data, uint32_t data_len) @@ -1076,7 +1084,11 @@ int StreamingBufferAppend(StreamingBuffer *sb, const StreamingBufferConfig *cfg, return -1; } - if (!DATA_FITS(sb, data_len)) { + int r = DataFits(sb, data_len); + if (r < 0) { + DEBUG_VALIDATE_BUG_ON(1); + return -1; + } else if (r == 0) { if (sb->region.buf_size == 0) { if (GrowToSize(sb, cfg, data_len) != SC_OK) return -1; @@ -1085,7 +1097,7 @@ int StreamingBufferAppend(StreamingBuffer *sb, const StreamingBufferConfig *cfg, return -1; } } - DEBUG_VALIDATE_BUG_ON(!DATA_FITS(sb, data_len)); + DEBUG_VALIDATE_BUG_ON(DataFits(sb, data_len) != 1); memcpy(sb->region.buf + sb->region.buf_offset, data, data_len); seg->stream_offset = sb->region.stream_offset + sb->region.buf_offset; @@ -1111,7 +1123,11 @@ int StreamingBufferAppendNoTrack(StreamingBuffer *sb, const StreamingBufferConfi return -1; } - if (!DATA_FITS(sb, data_len)) { + int r = DataFits(sb, data_len); + if (r < 0) { + DEBUG_VALIDATE_BUG_ON(1); + return -1; + } else if (r == 0) { if (sb->region.buf_size == 0) { if (GrowToSize(sb, cfg, data_len) != SC_OK) return -1; @@ -1120,7 +1136,7 @@ int StreamingBufferAppendNoTrack(StreamingBuffer *sb, const StreamingBufferConfi return -1; } } - DEBUG_VALIDATE_BUG_ON(!DATA_FITS(sb, data_len)); + DEBUG_VALIDATE_BUG_ON(DataFits(sb, data_len) != 1); memcpy(sb->region.buf + sb->region.buf_offset, data, data_len); uint32_t rel_offset = sb->region.buf_offset; @@ -1133,7 +1149,15 @@ int StreamingBufferAppendNoTrack(StreamingBuffer *sb, const StreamingBufferConfi } } -#define DATA_FITS_AT_OFFSET(region, len, offset) ((offset) + (len) <= (region)->buf_size) +static int DataFitsAtOffset( + const StreamingBufferRegion *region, const uint32_t len, const uint32_t offset) +{ + const uint64_t offset64 = offset; + const uint64_t len64 = len; + if (offset64 + len64 > UINT32_MAX) + return -1; + return (offset + len <= region->buf_size); +} #if defined(DEBUG) || defined(DEBUG_VALIDATION) static void Validate(const StreamingBuffer *sb) @@ -1477,8 +1501,6 @@ static StreamingBufferRegion *BufferInsertAtRegion(StreamingBuffer *sb, int StreamingBufferInsertAt(StreamingBuffer *sb, const StreamingBufferConfig *cfg, StreamingBufferSegment *seg, const uint8_t *data, uint32_t data_len, uint64_t offset) { - int r; - DEBUG_VALIDATE_BUG_ON(seg == NULL); DEBUG_VALIDATE_BUG_ON(offset < sb->region.stream_offset); if (offset < sb->region.stream_offset) { @@ -1496,11 +1518,15 @@ int StreamingBufferInsertAt(StreamingBuffer *sb, const StreamingBufferConfig *cf region == &sb->region ? "main" : "aux", region); uint32_t rel_offset = offset - region->stream_offset; - if (!DATA_FITS_AT_OFFSET(region, data_len, rel_offset)) { + int r = DataFitsAtOffset(region, data_len, rel_offset); + if (r < 0) { + DEBUG_VALIDATE_BUG_ON(1); + return SC_ELIMIT; + } else if (r == 0) { if ((r = GrowToSize(sb, cfg, (rel_offset + data_len))) != SC_OK) return r; } - DEBUG_VALIDATE_BUG_ON(!DATA_FITS_AT_OFFSET(region, data_len, rel_offset)); + DEBUG_VALIDATE_BUG_ON(DataFitsAtOffset(region, data_len, rel_offset) != 1); SCLogDebug("offset %" PRIu64 " data_len %u, rel_offset %u into region offset %" PRIu64 ", buf_offset %u, buf_size %u", @@ -2320,6 +2346,22 @@ static int StreamingBufferTest10(void) PASS; } +static int StreamingBufferTest11(void) +{ + StreamingBufferConfig cfg = { 24, 1, STREAMING_BUFFER_REGION_GAP_DEFAULT, NULL, NULL, NULL }; + StreamingBuffer *sb = StreamingBufferInit(&cfg); + FAIL_IF(sb == NULL); + + StreamingBufferSegment seg1; + FAIL_IF(StreamingBufferAppend(sb, &cfg, &seg1, (const uint8_t *)"ABCDEFGH", 8) != 0); + StreamingBufferSegment seg2; + unsigned int data_len = 0xffffffff; + FAIL_IF(StreamingBufferAppend(sb, &cfg, &seg2, (const uint8_t *)"unused", data_len) != -1); + FAIL_IF(StreamingBufferInsertAt( + sb, &cfg, &seg2, (const uint8_t *)"abcdefghij", data_len, 100000) != SC_ELIMIT); + StreamingBufferFree(sb, &cfg); + PASS; +} #endif void StreamingBufferRegisterTests(void) @@ -2333,5 +2375,6 @@ void StreamingBufferRegisterTests(void) UtRegisterTest("StreamingBufferTest08", StreamingBufferTest08); UtRegisterTest("StreamingBufferTest09", StreamingBufferTest09); UtRegisterTest("StreamingBufferTest10", StreamingBufferTest10); + UtRegisterTest("StreamingBufferTest11 Bug 6903", StreamingBufferTest11); #endif } diff --git a/suricata-update/CHANGELOG.md b/suricata-update/CHANGELOG.md index 03310ca..6ae15ec 100644 --- a/suricata-update/CHANGELOG.md +++ b/suricata-update/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 1.3.3 - 2024-04-16 +- Add missing engine provided rules: + https://redmine.openinfosecfoundation.org/issues/6932 + ## 1.3.2 - 2024-03-14 - Fix copying of file hash lists which was broken in the dataset fix as part of ticket #6833: diff --git a/suricata-update/Makefile b/suricata-update/Makefile index e0ceb8f..7149893 100644 --- a/suricata-update/Makefile +++ b/suricata-update/Makefile @@ -253,10 +253,10 @@ OTOOL64 = PACKAGE = suricata PACKAGE_BUGREPORT = PACKAGE_NAME = suricata -PACKAGE_STRING = suricata 7.0.4 +PACKAGE_STRING = suricata 7.0.5 PACKAGE_TARNAME = suricata PACKAGE_URL = -PACKAGE_VERSION = 7.0.4 +PACKAGE_VERSION = 7.0.5 PATH_SEPARATOR = : PCAP_CFLAGS = -I/usr/include PCAP_LIBS = -lpcap @@ -265,6 +265,7 @@ PKG_CONFIG_LIBDIR = PKG_CONFIG_PATH = POW_LIB = RANLIB = ranlib +RELEASE_DATE = 2024-04-23 RUSTC = /usr/bin/rustc RUSTUP_HOME_PATH = no RUST_FEATURES = @@ -280,7 +281,7 @@ SHELL = /bin/bash SPHINX_BUILD = /usr/bin/sphinx-build STRIP = strip SURICATA_UPDATE_DIR = suricata-update -VERSION = 7.0.4 +VERSION = 7.0.5 abs_builddir = /builds/dev/suricata/suricata-update abs_srcdir = /builds/dev/suricata/suricata-update abs_top_builddir = /builds/dev/suricata diff --git a/suricata-update/Makefile.in b/suricata-update/Makefile.in index 1273803..4b2cc7a 100644 --- a/suricata-update/Makefile.in +++ b/suricata-update/Makefile.in @@ -265,6 +265,7 @@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ POW_LIB = @POW_LIB@ RANLIB = @RANLIB@ +RELEASE_DATE = @RELEASE_DATE@ RUSTC = @RUSTC@ RUSTUP_HOME_PATH = @RUSTUP_HOME_PATH@ RUST_FEATURES = @RUST_FEATURES@ diff --git a/suricata-update/suricata/update/main.py b/suricata-update/suricata/update/main.py index 18af7a8..7814518 100644 --- a/suricata-update/suricata/update/main.py +++ b/suricata-update/suricata/update/main.py @@ -334,14 +334,19 @@ def load_dist_rules(files): "dnp3-events.rules", "dns-events.rules", "files.rules", + "http2-events.rules", "http-events.rules", "ipsec-events.rules", "kerberos-events.rules", "modbus-events.rules", + "mqtt-events.rules", "nfs-events.rules", "ntp-events.rules", + "quic-events.rules", + "rfb-events.rules", "smb-events.rules", "smtp-events.rules", + "ssh-events.rules", "stream-events.rules", "tls-events.rules", ] diff --git a/suricata-update/suricata/update/version.py b/suricata-update/suricata/update/version.py index 75d1205..d190ec4 100644 --- a/suricata-update/suricata/update/version.py +++ b/suricata-update/suricata/update/version.py @@ -4,4 +4,4 @@ # Alpha: 1.0.0a1 # Development: 1.0.0dev0 # Release candidate: 1.0.0rc1 -version = "1.3.2" +version = "1.3.3" diff --git a/suricata.yaml.in b/suricata.yaml.in index b141c08..3ead5f5 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -753,6 +753,7 @@ dpdk: # - auto takes all cores # in IPS mode it is required to specify the number of cores and the numbers on both interfaces must match threads: auto + # interrupt-mode: false # true to switch to interrupt mode promisc: true # promiscuous mode - capture all packets multicast: true # enables also detection on multicast packets checksum-checks: true # if Suricata should validate checksums |