summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore9
-rw-r--r--Makefile.am2
-rw-r--r--configure.ac54
-rw-r--r--doxygen.cfg.in184
-rw-r--r--doxygen/Makefile.am45
-rwxr-xr-xdoxygen/build_man.sh323
-rw-r--r--doxygen/doxygen.cfg.in27
-rw-r--r--examples/Makefile.am4
-rw-r--r--examples/nf-queue.c94
-rw-r--r--include/libnetfilter_queue/Makefile.am1
-rw-r--r--include/libnetfilter_queue/libnetfilter_queue.h13
-rw-r--r--include/libnetfilter_queue/libnetfilter_queue_icmp.h8
-rw-r--r--include/libnetfilter_queue/libnetfilter_queue_ipv4.h2
-rw-r--r--include/libnetfilter_queue/libnetfilter_queue_ipv6.h1
-rw-r--r--include/libnetfilter_queue/libnetfilter_queue_tcp.h3
-rw-r--r--include/libnetfilter_queue/libnetfilter_queue_udp.h3
-rw-r--r--include/libnetfilter_queue/linux_nfnetlink_queue.h6
-rw-r--r--include/libnetfilter_queue/pktbuff.h7
-rw-r--r--include/linux/netfilter/nfnetlink_queue.h4
-rw-r--r--src/Makefile.am5
-rw-r--r--src/extra/checksum.c29
-rw-r--r--src/extra/icmp.c57
-rw-r--r--src/extra/ipv4.c109
-rw-r--r--src/extra/ipv6.c95
-rw-r--r--src/extra/pktbuff.c318
-rw-r--r--src/extra/tcp.c129
-rw-r--r--src/extra/udp.c132
-rw-r--r--src/internal.h6
-rw-r--r--src/libnetfilter_queue.c231
-rw-r--r--src/nlmsg.c192
-rw-r--r--utils/nfqnl_test.c1
31 files changed, 1577 insertions, 517 deletions
diff --git a/.gitignore b/.gitignore
index a0fc139..ae3e740 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
.libs/
Makefile
Makefile.in
+.dirstamp
*.o
*.la
*.lo
@@ -12,6 +13,12 @@ Makefile.in
/config.*
/configure
/libtool
+/stamp-h1
-/doxygen.cfg
+/doxygen/doxygen.cfg
/libnetfilter_queue.pc
+
+/examples/nf-queue
+/doxygen/doxyfile.stamp
+/doxygen/html/
+/doxygen/man/
diff --git a/Makefile.am b/Makefile.am
index 6b4ef77..a5b347b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2,7 +2,7 @@ ACLOCAL_AMFLAGS = -I m4
EXTRA_DIST = $(man_MANS) include/linux
-SUBDIRS = src utils include examples
+SUBDIRS = src utils include examples doxygen
man_MANS = #nfnetlink_queue.3 nfnetlink_queue.7
diff --git a/configure.ac b/configure.ac
index 0c08459..7359fba 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,18 +1,34 @@
dnl Process this file with autoconf to create configure.
-AC_INIT([libnetfilter_queue], [1.0.3])
+AC_INIT([libnetfilter_queue], [1.0.5])
AC_CONFIG_AUX_DIR([build-aux])
AC_CANONICAL_HOST
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([-Wall foreign subdir-objects
- tar-pax no-dist-gzip dist-bzip2 1.6])
+ tar-pax no-dist-gzip dist-xz 1.6])
m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
dnl kernel style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+AC_ARG_ENABLE([html-doc],
+ AS_HELP_STRING([--enable-html-doc], [Enable html documentation]),
+ [], [enable_html_doc=no])
+AM_CONDITIONAL([BUILD_HTML], [test "$enable_html_doc" = yes])
+AS_IF([test "$enable_html_doc" = yes],
+ [AC_SUBST(GEN_HTML, YES)],
+ [AC_SUBST(GEN_HTML, NO)])
+
+AC_ARG_ENABLE([man-pages],
+ AS_HELP_STRING([--disable-man-pages], [Disable man page documentation]),
+ [], [enable_man_pages=yes])
+AM_CONDITIONAL([BUILD_MAN], [test "$enable_man_pages" = yes])
+AS_IF([test "$enable_man_pages" = yes],
+ [AC_SUBST(GEN_MAN, YES)],
+ [AC_SUBST(GEN_MAN, NO)])
+
AC_PROG_CC
AM_PROG_CC_C_O
AC_DISABLE_STATIC
@@ -29,9 +45,41 @@ dnl Dependencies
PKG_CHECK_MODULES([LIBNFNETLINK], [libnfnetlink >= 0.0.41])
PKG_CHECK_MODULES([LIBMNL], [libmnl >= 1.0.3])
+AS_IF([test "$enable_man_pages" = no -a "$enable_html_doc" = no],
+ [with_doxygen=no], [with_doxygen=yes])
+
+AS_IF([test "x$with_doxygen" != xno], [
+ AC_CHECK_PROGS([DOXYGEN], [doxygen], [""])
+ AC_CHECK_PROGS([DOT], [dot], [""])
+ AS_IF([test "x$DOT" != "x"],
+ [AC_SUBST(HAVE_DOT, YES)],
+ [AC_SUBST(HAVE_DOT, NO)])
+])
+
+AM_CONDITIONAL([HAVE_DOXYGEN], [test -n "$DOXYGEN"])
+AS_IF([test "x$DOXYGEN" = x], [
+ AS_IF([test "x$with_doxygen" != xno], [
+ dnl Only run doxygen Makefile if doxygen installed
+ AC_MSG_WARN([Doxygen not found - not building documentation])
+ enable_html_doc=no
+ enable_man_pages=no
+ ])
+], [
+ dnl Warn user if html docs will be missing diagrams
+ AS_IF([test "$enable_html_doc" = yes -a -z "$DOT"],
+ AC_MSG_WARN([Dot not found - install graphviz to get interactive diagrams in HTML]))
+])
+
dnl Output the makefiles
AC_CONFIG_FILES([Makefile src/Makefile utils/Makefile examples/Makefile
- libnetfilter_queue.pc doxygen.cfg
+ libnetfilter_queue.pc
include/Makefile include/libnetfilter_queue/Makefile
+ doxygen/Makefile doxygen/doxygen.cfg
include/linux/Makefile include/linux/netfilter/Makefile])
+
AC_OUTPUT
+
+echo "
+libnetfilter_queue configuration:
+man pages: ${enable_man_pages}
+html docs: ${enable_html_doc}"
diff --git a/doxygen.cfg.in b/doxygen.cfg.in
deleted file mode 100644
index 660fe60..0000000
--- a/doxygen.cfg.in
+++ /dev/null
@@ -1,184 +0,0 @@
-DOXYFILE_ENCODING = UTF-8
-PROJECT_NAME = @PACKAGE@
-PROJECT_NUMBER = @VERSION@
-OUTPUT_DIRECTORY = doxygen
-CREATE_SUBDIRS = NO
-OUTPUT_LANGUAGE = English
-BRIEF_MEMBER_DESC = YES
-REPEAT_BRIEF = YES
-ABBREVIATE_BRIEF =
-ALWAYS_DETAILED_SEC = NO
-INLINE_INHERITED_MEMB = NO
-FULL_PATH_NAMES = NO
-STRIP_FROM_PATH =
-STRIP_FROM_INC_PATH =
-SHORT_NAMES = NO
-JAVADOC_AUTOBRIEF = NO
-QT_AUTOBRIEF = NO
-MULTILINE_CPP_IS_BRIEF = NO
-INHERIT_DOCS = YES
-SEPARATE_MEMBER_PAGES = NO
-TAB_SIZE = 8
-ALIASES =
-OPTIMIZE_OUTPUT_FOR_C = YES
-OPTIMIZE_OUTPUT_JAVA = NO
-OPTIMIZE_FOR_FORTRAN = NO
-OPTIMIZE_OUTPUT_VHDL = NO
-BUILTIN_STL_SUPPORT = NO
-CPP_CLI_SUPPORT = NO
-SIP_SUPPORT = NO
-DISTRIBUTE_GROUP_DOC = NO
-SUBGROUPING = YES
-TYPEDEF_HIDES_STRUCT = NO
-EXTRACT_ALL = NO
-EXTRACT_PRIVATE = NO
-EXTRACT_STATIC = NO
-EXTRACT_LOCAL_CLASSES = YES
-EXTRACT_LOCAL_METHODS = NO
-EXTRACT_ANON_NSPACES = NO
-HIDE_UNDOC_MEMBERS = NO
-HIDE_UNDOC_CLASSES = NO
-HIDE_FRIEND_COMPOUNDS = NO
-HIDE_IN_BODY_DOCS = NO
-INTERNAL_DOCS = NO
-CASE_SENSE_NAMES = YES
-HIDE_SCOPE_NAMES = NO
-SHOW_INCLUDE_FILES = YES
-INLINE_INFO = YES
-SORT_MEMBER_DOCS = YES
-SORT_BRIEF_DOCS = NO
-SORT_GROUP_NAMES = NO
-SORT_BY_SCOPE_NAME = NO
-GENERATE_TODOLIST = YES
-GENERATE_TESTLIST = YES
-GENERATE_BUGLIST = YES
-GENERATE_DEPRECATEDLIST= YES
-ENABLED_SECTIONS =
-MAX_INITIALIZER_LINES = 30
-SHOW_USED_FILES = YES
-SHOW_DIRECTORIES = NO
-FILE_VERSION_FILTER =
-QUIET = NO
-WARNINGS = YES
-WARN_IF_UNDOCUMENTED = YES
-WARN_IF_DOC_ERROR = YES
-WARN_NO_PARAMDOC = NO
-WARN_FORMAT = "$file:$line: $text"
-WARN_LOGFILE =
-INPUT = .
-INPUT_ENCODING = UTF-8
-FILE_PATTERNS = *.c
-RECURSIVE = YES
-EXCLUDE =
-EXCLUDE_SYMLINKS = NO
-EXCLUDE_PATTERNS =
-EXCLUDE_SYMBOLS = EXPORT_SYMBOL
-EXAMPLE_PATH =
-EXAMPLE_PATTERNS =
-EXAMPLE_RECURSIVE = NO
-IMAGE_PATH =
-INPUT_FILTER = "sed 's/EXPORT_SYMBOL//g'"
-FILTER_PATTERNS =
-FILTER_SOURCE_FILES = NO
-SOURCE_BROWSER = YES
-INLINE_SOURCES = NO
-STRIP_CODE_COMMENTS = YES
-REFERENCED_BY_RELATION = NO
-REFERENCES_RELATION = NO
-REFERENCES_LINK_SOURCE = YES
-USE_HTAGS = NO
-VERBATIM_HEADERS = YES
-ALPHABETICAL_INDEX = NO
-COLS_IN_ALPHA_INDEX = 5
-IGNORE_PREFIX =
-GENERATE_HTML = YES
-HTML_OUTPUT = html
-HTML_FILE_EXTENSION = .html
-HTML_HEADER =
-HTML_STYLESHEET =
-HTML_ALIGN_MEMBERS = YES
-GENERATE_HTMLHELP = NO
-GENERATE_DOCSET = NO
-DOCSET_FEEDNAME = "Doxygen generated docs"
-DOCSET_BUNDLE_ID = org.doxygen.Project
-HTML_DYNAMIC_SECTIONS = NO
-CHM_FILE =
-HHC_LOCATION =
-GENERATE_CHI = NO
-BINARY_TOC = NO
-TOC_EXPAND = NO
-DISABLE_INDEX = NO
-ENUM_VALUES_PER_LINE = 4
-GENERATE_TREEVIEW = NO
-TREEVIEW_WIDTH = 250
-GENERATE_LATEX = NO
-LATEX_OUTPUT = latex
-LATEX_CMD_NAME = latex
-MAKEINDEX_CMD_NAME = makeindex
-COMPACT_LATEX = NO
-PAPER_TYPE = a4wide
-EXTRA_PACKAGES =
-LATEX_HEADER =
-PDF_HYPERLINKS = YES
-USE_PDFLATEX = YES
-LATEX_BATCHMODE = NO
-LATEX_HIDE_INDICES = NO
-GENERATE_RTF = NO
-RTF_OUTPUT = rtf
-COMPACT_RTF = NO
-RTF_HYPERLINKS = NO
-RTF_STYLESHEET_FILE =
-RTF_EXTENSIONS_FILE =
-GENERATE_MAN = YES
-MAN_OUTPUT = man
-MAN_EXTENSION = .3
-MAN_LINKS = NO
-GENERATE_XML = NO
-XML_OUTPUT = xml
-XML_SCHEMA =
-XML_DTD =
-XML_PROGRAMLISTING = YES
-GENERATE_AUTOGEN_DEF = NO
-GENERATE_PERLMOD = NO
-PERLMOD_LATEX = NO
-PERLMOD_PRETTY = YES
-PERLMOD_MAKEVAR_PREFIX =
-ENABLE_PREPROCESSING = YES
-MACRO_EXPANSION = NO
-EXPAND_ONLY_PREDEF = NO
-SEARCH_INCLUDES = YES
-INCLUDE_PATH =
-INCLUDE_FILE_PATTERNS =
-PREDEFINED =
-EXPAND_AS_DEFINED =
-SKIP_FUNCTION_MACROS = YES
-TAGFILES =
-GENERATE_TAGFILE =
-ALLEXTERNALS = NO
-EXTERNAL_GROUPS = YES
-PERL_PATH = /usr/bin/perl
-CLASS_DIAGRAMS = YES
-MSCGEN_PATH =
-HIDE_UNDOC_RELATIONS = YES
-HAVE_DOT = YES
-CLASS_GRAPH = YES
-COLLABORATION_GRAPH = YES
-GROUP_GRAPHS = YES
-UML_LOOK = NO
-TEMPLATE_RELATIONS = NO
-INCLUDE_GRAPH = YES
-INCLUDED_BY_GRAPH = YES
-CALL_GRAPH = NO
-CALLER_GRAPH = NO
-GRAPHICAL_HIERARCHY = YES
-DIRECTORY_GRAPH = YES
-DOT_IMAGE_FORMAT = png
-DOT_PATH =
-DOTFILE_DIRS =
-DOT_GRAPH_MAX_NODES = 50
-MAX_DOT_GRAPH_DEPTH = 0
-DOT_TRANSPARENT = YES
-DOT_MULTI_TARGETS = NO
-GENERATE_LEGEND = YES
-DOT_CLEANUP = YES
-SEARCHENGINE = NO
diff --git a/doxygen/Makefile.am b/doxygen/Makefile.am
new file mode 100644
index 0000000..68be963
--- /dev/null
+++ b/doxygen/Makefile.am
@@ -0,0 +1,45 @@
+if HAVE_DOXYGEN
+
+doc_srcs = $(top_srcdir)/src/libnetfilter_queue.c\
+ $(top_srcdir)/src/nlmsg.c\
+ $(top_srcdir)/src/extra/checksum.c\
+ $(top_srcdir)/src/extra/ipv4.c\
+ $(top_srcdir)/src/extra/pktbuff.c\
+ $(top_srcdir)/src/extra/ipv6.c\
+ $(top_srcdir)/src/extra/tcp.c\
+ $(top_srcdir)/src/extra/udp.c\
+ $(top_srcdir)/src/extra/icmp.c
+
+doxyfile.stamp: $(doc_srcs) Makefile build_man.sh
+ rm -rf html man
+ doxygen doxygen.cfg >/dev/null
+
+if BUILD_MAN
+ $(abs_top_srcdir)/doxygen/build_man.sh libnetfilter_queue libnetfilter_queue.c
+endif
+
+ touch doxyfile.stamp
+
+CLEANFILES = doxyfile.stamp
+
+all-local: doxyfile.stamp
+clean-local:
+ rm -rf man html
+install-data-local:
+if BUILD_MAN
+ mkdir -p $(DESTDIR)$(mandir)/man3
+ cp --no-dereference --preserve=links,mode,timestamps man/man3/*.3\
+ $(DESTDIR)$(mandir)/man3/
+endif
+if BUILD_HTML
+ mkdir -p $(DESTDIR)$(htmldir)
+ cp --no-dereference --preserve=links,mode,timestamps html/*\
+ $(DESTDIR)$(htmldir)
+endif
+
+# make distcheck needs uninstall-local
+uninstall-local:
+ rm -rf $(DESTDIR)$(mandir) man html doxyfile.stamp $(DESTDIR)$(htmldir)
+endif
+
+EXTRA_DIST = build_man.sh
diff --git a/doxygen/build_man.sh b/doxygen/build_man.sh
new file mode 100755
index 0000000..7eab8fa
--- /dev/null
+++ b/doxygen/build_man.sh
@@ -0,0 +1,323 @@
+#!/bin/sh
+[ -n "$BASH" ] || exec bash -p $0 $@
+
+# Script to process man pages output by doxygen.
+# We need to use bash for its associative array facility.
+# (`bash -p` prevents import of functions from the environment).
+# Args: none or 2 being man7 page name & relative path of source with \mainpage
+
+declare -A renamed_page
+
+main(){
+ set -e
+ pushd man/man3 >/dev/null; rm -f _*
+ count_real_pages
+ rename_real_pages
+ make_symlinks
+ post_process $@
+}
+
+count_real_pages(){
+ page_count=0
+ #
+ # Count "real" man pages (i.e. not generated by MAN_LINKS)
+ # MAN_LINKS pages are 1-liners starting .so
+ # Method: list files in descending order of size,
+ # looking for the first 1-liner
+ #
+ for i in $(ls -S)
+ do head -n1 $i | grep -E -q '^\.so' && break
+ page_count=$(($page_count + 1))
+ done
+ first_link=$(($page_count + 1))
+}
+
+rename_real_pages(){
+ for i in $(ls -S | head -n$page_count)
+ do for j in $(ls -S | tail -n+$first_link)
+ do grep -E -q $i$ $j && break
+ done
+ mv -f $i $j
+ renamed_page[$i]=$j
+ done
+}
+
+make_symlinks(){
+ for j in $(ls -S | tail -n+$first_link)
+ do ln -sf ${renamed_page[$(cat $j | cut -f2 -d/)]} $j
+ done
+}
+
+post_process(){
+ make_temp_files
+ #
+ # DIAGNOSTIC / DEVELOPMENT CODE
+ # set -x and restrict processing to keep_me: un-comment to activate
+ # Change keep_me as required
+ #
+ #keep_me=nfq_icmp_get_hdr.3;\
+ #do_diagnostics;\
+ #
+ # Work through the "real" man pages
+ for target in $(ls -S | head -n$page_count)
+ do mygrep "^\\.SH \"Function Documentation" $target
+ # Next file if this isn't a function page
+ [ $linnum -ne 0 ] || continue
+
+ del_modules
+ del_bogus_synopsis
+ fix_name_line
+ move_synopsis
+ del_empty_det_desc
+ del_def_at_lines
+ fix_double_blanks
+
+ # Fix rendering of verbatim "\n" (in code snippets)
+ sed -i 's/\\n/\\\\n/' $target
+
+ done
+
+ [ $# -ne 2 ] || make_man7 $@
+
+ remove_temp_files
+}
+
+make_man7(){
+ popd >/dev/null
+ target=$(grep -Ew INPUT doxygen.cfg | rev | cut -f1 -d' ' | rev)/$2
+ mypath=$(dirname $0)
+
+ # Build up temporary source in temp.c
+ # (doxygen only makes man pages from .c files).
+ mygrep \\\\mainpage $target
+ tail -n+$((linnum-1)) $target | head -n1 >temp.c
+ echo " * \\defgroup $1 $1 overview" >>temp.c
+ tail -n+$((linnum+1)) $target >$fileA
+ linnum=$(grep -En '\*/' $fileA | head -n1 | cut -d: -f1)
+ head -n$((linnum - 1)) $fileA >> temp.c
+
+ echo ' */' >> temp.c
+ cat >> temp.c <<////
+
+ /**
+ * @{
+ *
+ * $1 - DELETE_ME
+ */
+int $1(void)
+{
+ return 0;
+}
+/**
+ * @}
+ */
+////
+
+ # Create temporary doxygen config in fileC
+ cat /dev/null >$fileC
+ for i in \
+ PROJECT_NAME \
+ PROJECT_NUMBER \
+ ABBREVIATE_BRIEF \
+ FULL_PATH_NAMES \
+ TAB_SIZE \
+ OPTIMIZE_OUTPUT_FOR_C \
+ EXAMPLE_PATTERNS \
+ ALPHABETICAL_INDEX \
+ SEARCHENGINE \
+ GENERATE_LATEX \
+ ; do grep -Ew $i doxygen.cfg >>$fileC; done
+ cat >>$fileC <<////
+INPUT = temp.c
+GENERATE_HTML = NO
+GENERATE_MAN = YES
+MAN_EXTENSION = .7
+////
+
+ doxygen $fileC >/dev/null
+
+ # Remove SYNOPSIS line if there is one
+ target=man/man7/$1.7
+ mygrep "SH SYNOPSIS" $target
+ [ $linnum -eq 0 ] || delete_lines $linnum $((linnum+1))
+
+ # doxygen 1.8.9.1 and possibly newer run the first para into NAME
+ # (i.e. in this unusual group). There won't be a SYNOPSIS when this happens
+ if grep -Eq "overview$1" $target; then
+ head -n2 temp.c >$fileA
+ cat >>$fileA <<////
+ * \\manonly
+.PP
+.SH "Detailed Description"
+.PP
+\\endmanonly
+////
+ tail -n+3 temp.c >>$fileA
+ cat $fileA >temp.c
+ doxygen $fileC >/dev/null
+ fi
+
+ # Insert top-level "See also" of man7 page in all real man3 pages
+ for target in $(find man/man3 -type f)
+ do mygrep "Detailed Description" $target
+ [ $linnum -ne 0 ] || mygrep "Function Documentation" $target
+ [ $linnum -ne 0 ] || { echo "NO HEADER IN $target" >&2; continue; }
+ head -n$((linnum-1)) $target >$fileA
+ cat >>$fileA <<////
+.SH "See also"
+\\fB${1}\\fP(7)
+////
+ tail -n+$linnum $target >>$fileA
+ cp $fileA $target
+ done
+
+ rm temp.c
+}
+
+fix_double_blanks(){
+ linnum=1
+ #
+ # Older versions of man display a blank line on encountering "\fB\fP";
+ # newer versions of man do not.
+ # doxygen emits "\fB\fP" on seeing "\par" on a line by itself.
+ # "\par" gives us double-spacing in the web doc, which we want, but double-
+ # spacing looks odd in a man page so remove "\fB\fP".
+ #
+ while [ $linnum -ne 0 ]
+ do mygrep \\\\fB\\\\fP $target
+ [ $linnum -eq 0 ] || delete_lines $linnum $linnum
+ done
+}
+
+del_def_at_lines(){
+ linnum=1
+ while [ $linnum -ne 0 ]
+ do mygrep '^Definition at line (\\fB)?[[:digit:]]*(\\fP)? of file' $target
+ [ $linnum -eq 0 ] || delete_lines $(($linnum - 1)) $linnum
+ done
+}
+
+# Only invoked if you un-comment the 2 diagnostic / development lines above
+do_diagnostics(){
+ mv $keep_me xxx
+ rm *.3
+ mv xxx $keep_me
+ page_count=1
+ set -x
+}
+
+del_empty_det_desc(){
+ mygrep "^\\.SH \"Function Documentation" $target
+ i=$linnum
+ mygrep "^\\.SH \"Detailed Description" $target
+ [ $linnum -ne 0 ] || return 0
+ [ $(($i - $linnum)) -eq 3 ] || return 0
+ # A 1-line Detailed Description is also 3 lines long,
+ # but the 3rd line is not empty
+ i=$(($i -1))
+ [ $(tail -n+$i $target | head -n1 | wc -c) -le 2 ] || return 0
+ delete_lines $linnum $i
+}
+
+move_synopsis(){
+ mygrep "SH SYNOPSIS" $target
+ [ $linnum -ne 0 ] || return 0
+ i=$linnum
+ # If this is a doxygen-created synopsis, leave it.
+ # (We haven't inserted our own one in the source yet)
+ mygrep "^\\.SS \"Functions" $target
+ [ $i -gt $linnum ] || return 0
+
+ mygrep "^\\.SH \"Function Documentation" $target
+ j=$(($linnum - 1))
+ head -n$(($j - 1)) $target | tail -n$(($linnum - $i - 1)) >$fileC
+ delete_lines $i $j
+ mygrep "^\\.SS \"Functions" $target
+ head -n$(($linnum - 1)) $target >$fileA
+ tail -n+$(($linnum + 1)) $target >$fileB
+ cat $fileA $fileC $fileB >$target
+}
+
+fix_name_line(){
+ all_funcs=""
+
+ # Search a shortened version of the page in case there are .RI lines later
+ mygrep "^\\.SH \"Function Documentation" $target
+ head -n$linnum $target >$fileC
+
+ while :
+ do mygrep ^\\.RI $fileC
+ [ $linnum -ne 0 ] || break
+ # Discard this entry
+ tail -n+$(($linnum + 1)) $fileC >$fileB
+ cp $fileB $fileC
+
+ func=$(cat $fileG | cut -f2 -d\\ | cut -c3-)
+ [ -z "$all_funcs" ] && all_funcs=$func ||\
+ all_funcs="$all_funcs, $func"
+ done
+ # For now, assume name is at line 5
+ head -n4 $target >$fileA
+ desc=$(head -n5 $target | tail -n1 | cut -f3- -d" ")
+ tail -n+6 $target >$fileB
+ cat $fileA >$target
+ echo "$all_funcs \\- $desc" >>$target
+ cat $fileB >>$target
+}
+
+del_modules(){
+ mygrep "^\.SS \"Modules" $target
+ [ $linnum -ne 0 ] || return 0
+ i=$linnum
+ mygrep "^\\.SS \"Functions" $target
+ delete_lines $i $(($linnum - 1))
+}
+
+del_bogus_synopsis(){
+ mygrep "SH SYNOPSIS" $target
+ #
+ # doxygen 1.8.20 inserts its own SYNOPSIS line but there is no mention
+ # in the documentation or git log what to do with it.
+ # So get rid of it
+ #
+ [ $linnum -ne 0 ] || return 0
+ i=$linnum
+ # Look for the next one
+ tail -n+$(($i + 1)) $target >$fileC;\
+ mygrep "SH SYNOPSIS" $fileC
+ [ $linnum -ne 0 ] || return 0
+
+ mygrep "^\\.SS \"Functions" $target
+ delete_lines $i $(($linnum - 1))
+}
+
+# Delete lines $1 through $2 from $target
+delete_lines(){
+ head -n$(($1 - 1)) $target >$fileA
+ tail -n+$(($2 +1)) $target >$fileB
+ cat $fileA $fileB >$target
+}
+
+mygrep(){
+ set +e
+ grep -En "$1" $2 2>/dev/null >$fileH
+ [ $? -ne 0 ] && linnum=0 ||\
+ { head -n1 $fileH >$fileG; linnum=$(cat $fileG | cut -f1 -d:); }
+ set -e
+}
+
+make_temp_files(){
+ temps="A B C G H"
+ for i in $temps
+ do declare -g file$i=$(mktemp)
+ done
+}
+
+remove_temp_files(){
+ for i in $temps
+ do j=file$i
+ rm ${!j}
+ done
+}
+
+main $@
diff --git a/doxygen/doxygen.cfg.in b/doxygen/doxygen.cfg.in
new file mode 100644
index 0000000..97174ff
--- /dev/null
+++ b/doxygen/doxygen.cfg.in
@@ -0,0 +1,27 @@
+# Difference with default Doxyfile 1.8.20
+PROJECT_NAME = @PACKAGE@
+PROJECT_NUMBER = @VERSION@
+ABBREVIATE_BRIEF =
+FULL_PATH_NAMES = NO
+TAB_SIZE = 8
+OPTIMIZE_OUTPUT_FOR_C = YES
+INPUT = @abs_top_srcdir@/src
+FILE_PATTERNS = *.c
+RECURSIVE = YES
+EXCLUDE_SYMBOLS = EXPORT_SYMBOL \
+ tcp_word_hdr \
+ nfq_handle \
+ nfq_data \
+ nfq_q_handle \
+ tcp_flag_word
+EXAMPLE_PATTERNS =
+INPUT_FILTER = "sed 's/EXPORT_SYMBOL//g'"
+SOURCE_BROWSER = YES
+ALPHABETICAL_INDEX = NO
+SEARCHENGINE = NO
+GENERATE_LATEX = NO
+LATEX_CMD_NAME = latex
+GENERATE_MAN = @GEN_MAN@
+GENERATE_HTML = @GEN_HTML@
+MAN_LINKS = YES
+HAVE_DOT = @HAVE_DOT@
diff --git a/examples/Makefile.am b/examples/Makefile.am
index 1906697..97bb70c 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -3,5 +3,5 @@ include ${top_srcdir}/Make_global.am
check_PROGRAMS = nf-queue
nf_queue_SOURCES = nf-queue.c
-nf_queue_LDADD = ../src/libnetfilter_queue.la
-nf_queue_LDFLAGS = -dynamic -lmnl
+nf_queue_LDADD = ../src/libnetfilter_queue.la -lmnl
+nf_queue_LDFLAGS = -dynamic
diff --git a/examples/nf-queue.c b/examples/nf-queue.c
index c2bc6cc..1ae52e4 100644
--- a/examples/nf-queue.c
+++ b/examples/nf-queue.c
@@ -15,26 +15,11 @@
#include <libnetfilter_queue/libnetfilter_queue.h>
-/* only for NFQA_CT, not needed otherwise: */
+/* NFQA_CT requires CTA_* attributes defined in nfnetlink_conntrack.h */
#include <linux/netfilter/nfnetlink_conntrack.h>
static struct mnl_socket *nl;
-static struct nlmsghdr *
-nfq_hdr_put(char *buf, int type, uint32_t queue_num)
-{
- struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
- nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | type;
- nlh->nlmsg_flags = NLM_F_REQUEST;
-
- struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
- nfg->nfgen_family = AF_UNSPEC;
- nfg->version = NFNETLINK_V0;
- nfg->res_id = htons(queue_num);
-
- return nlh;
-}
-
static void
nfq_send_verdict(int queue_num, uint32_t id)
{
@@ -42,7 +27,7 @@ nfq_send_verdict(int queue_num, uint32_t id)
struct nlmsghdr *nlh;
struct nlattr *nest;
- nlh = nfq_hdr_put(buf, NFQNL_MSG_VERDICT, queue_num);
+ nlh = nfq_nlmsg_put(buf, NFQNL_MSG_VERDICT, queue_num);
nfq_nlmsg_verdict_put(nlh, id, NF_ACCEPT);
/* example to set the connmark. First, start NFQA_CT section: */
@@ -50,7 +35,7 @@ nfq_send_verdict(int queue_num, uint32_t id)
/* then, add the connmark attribute: */
mnl_attr_put_u32(nlh, CTA_MARK, htonl(42));
- /* more conntrack attributes, e.g. CTA_LABEL, could be set here */
+ /* more conntrack attributes, e.g. CTA_LABELS could be set here */
/* end conntrack section */
mnl_attr_nest_end(nlh, nest);
@@ -69,6 +54,9 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data)
struct nfgenmsg *nfg;
uint16_t plen;
+ /* Parse netlink message received from the kernel, the array of
+ * attributes is set up to store metadata and the actual packet.
+ */
if (nfq_nlmsg_parse(nlh, attr) < 0) {
perror("problems parsing");
return MNL_CB_ERROR;
@@ -81,13 +69,30 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data)
return MNL_CB_ERROR;
}
+ /* Access packet metadata, which provides unique packet ID, hook number
+ * and ethertype. See struct nfqnl_msg_packet_hdr for details.
+ */
ph = mnl_attr_get_payload(attr[NFQA_PACKET_HDR]);
+ /* Access actual packet data length. */
plen = mnl_attr_get_payload_len(attr[NFQA_PAYLOAD]);
+
+ /* Access actual packet data */
/* void *payload = mnl_attr_get_payload(attr[NFQA_PAYLOAD]); */
+ /* Fetch metadata flags, possible flags values are:
+ *
+ * - NFQA_SKB_CSUMNOTREADY:
+ * Kernel performed partial checksum validation, see CHECKSUM_PARTIAL.
+ * - NFQA_SKB_CSUM_NOTVERIFIED:
+ * Kernel already verified checksum.
+ * - NFQA_SKB_GSO:
+ * Not the original packet received from the wire. Kernel has
+ * aggregated several packets into one single packet via GSO.
+ */
skbinfo = attr[NFQA_SKB_INFO] ? ntohl(mnl_attr_get_u32(attr[NFQA_SKB_INFO])) : 0;
+ /* Kernel has truncated the packet, fetch original packet length. */
if (attr[NFQA_CAP_LEN]) {
uint32_t orig_len = ntohl(mnl_attr_get_u32(attr[NFQA_CAP_LEN]));
if (orig_len != plen)
@@ -101,6 +106,25 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data)
printf("packet received (id=%u hw=0x%04x hook=%u, payload len %u",
id, ntohs(ph->hw_protocol), ph->hook, plen);
+ /* Fetch ethernet destination address. */
+ if (attr[NFQA_HWADDR]) {
+ struct nfqnl_msg_packet_hw *hw = mnl_attr_get_payload(attr[NFQA_HWADDR]);
+ unsigned int hwlen = ntohs(hw->hw_addrlen);
+ const unsigned char *addr = hw->hw_addr;
+ unsigned int i;
+
+ printf(", hwaddr %02x", addr[0]);
+ for (i = 1; i < hwlen; i++) {
+ if (i >= sizeof(hw->hw_addr)) {
+ printf("[truncated]");
+ break;
+ }
+ printf(":%02x", (unsigned char)addr[i]);
+ }
+
+ printf(" len %u", hwlen);
+ }
+
/*
* ip/tcp checksums are not yet valid, e.g. due to GRO/GSO.
* The application should behave as if the checksums are correct.
@@ -132,6 +156,9 @@ int main(int argc, char *argv[])
}
queue_num = atoi(argv[1]);
+ /*
+ * Set up netlink socket to communicate with the netfilter subsystem.
+ */
nl = mnl_socket_open(NETLINK_NETFILTER);
if (nl == NULL) {
perror("mnl_socket_open");
@@ -150,24 +177,11 @@ int main(int argc, char *argv[])
exit(EXIT_FAILURE);
}
- /* PF_(UN)BIND is not needed with kernels 3.8 and later */
- nlh = nfq_hdr_put(buf, NFQNL_MSG_CONFIG, 0);
- nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, NFQNL_CFG_CMD_PF_UNBIND);
-
- if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- perror("mnl_socket_send");
- exit(EXIT_FAILURE);
- }
-
- nlh = nfq_hdr_put(buf, NFQNL_MSG_CONFIG, 0);
- nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, NFQNL_CFG_CMD_PF_BIND);
-
- if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- perror("mnl_socket_send");
- exit(EXIT_FAILURE);
- }
-
- nlh = nfq_hdr_put(buf, NFQNL_MSG_CONFIG, queue_num);
+ /* Configure the pipeline between kernel and userspace, build and send
+ * a netlink message to specify queue number to bind to. Your ruleset
+ * has to use this queue number to deliver packets to userspace.
+ */
+ nlh = nfq_nlmsg_put(buf, NFQNL_MSG_CONFIG, queue_num);
nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, NFQNL_CFG_CMD_BIND);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
@@ -175,7 +189,10 @@ int main(int argc, char *argv[])
exit(EXIT_FAILURE);
}
- nlh = nfq_hdr_put(buf, NFQNL_MSG_CONFIG, queue_num);
+ /* Build and send a netlink message to specify how many bytes are
+ * copied from kernel to userspace for this queue.
+ */
+ nlh = nfq_nlmsg_put(buf, NFQNL_MSG_CONFIG, queue_num);
nfq_nlmsg_cfg_put_params(nlh, NFQNL_COPY_PACKET, 0xffff);
mnl_attr_put_u32(nlh, NFQA_CFG_FLAGS, htonl(NFQA_CFG_F_GSO));
@@ -193,6 +210,9 @@ int main(int argc, char *argv[])
ret = 1;
mnl_socket_setsockopt(nl, NETLINK_NO_ENOBUFS, &ret, sizeof(int));
+ /* Loop forever on packets received from the kernel and run the
+ * callback handler.
+ */
for (;;) {
ret = mnl_socket_recvfrom(nl, buf, sizeof_buf);
if (ret == -1) {
diff --git a/include/libnetfilter_queue/Makefile.am b/include/libnetfilter_queue/Makefile.am
index 902fbf9..e436bab 100644
--- a/include/libnetfilter_queue/Makefile.am
+++ b/include/libnetfilter_queue/Makefile.am
@@ -1,5 +1,6 @@
pkginclude_HEADERS = libnetfilter_queue.h \
linux_nfnetlink_queue.h \
+ libnetfilter_queue_icmp.h \
libnetfilter_queue_ipv4.h \
libnetfilter_queue_ipv6.h \
libnetfilter_queue_tcp.h \
diff --git a/include/libnetfilter_queue/libnetfilter_queue.h b/include/libnetfilter_queue/libnetfilter_queue.h
index 2e38411..f7e68d8 100644
--- a/include/libnetfilter_queue/libnetfilter_queue.h
+++ b/include/libnetfilter_queue/libnetfilter_queue.h
@@ -3,9 +3,9 @@
* (C) 2005 by Harald Welte <laforge@gnumonks.org>
*
*
- * Changelog :
+ * Changelog :
* (2005/08/11) added parsing function (Eric Leblond <regit@inl.fr>)
- *
+ *
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*/
@@ -82,7 +82,7 @@ extern int nfq_set_verdict_batch2(struct nfq_q_handle *qh,
uint32_t mark);
extern __attribute__((deprecated))
-int nfq_set_verdict_mark(struct nfq_q_handle *qh,
+int nfq_set_verdict_mark(struct nfq_q_handle *qh,
uint32_t id,
uint32_t verdict,
uint32_t mark,
@@ -103,6 +103,7 @@ extern uint32_t nfq_get_indev(struct nfq_data *nfad);
extern uint32_t nfq_get_physindev(struct nfq_data *nfad);
extern uint32_t nfq_get_outdev(struct nfq_data *nfad);
extern uint32_t nfq_get_physoutdev(struct nfq_data *nfad);
+extern uint32_t nfq_get_skbinfo(struct nfq_data *nfad);
extern int nfq_get_uid(struct nfq_data *nfad, uint32_t *uid);
extern int nfq_get_gid(struct nfq_data *nfad, uint32_t *gid);
extern int nfq_get_secctx(struct nfq_data *nfad, unsigned char **secdata);
@@ -110,7 +111,7 @@ extern int nfq_get_secctx(struct nfq_data *nfad, unsigned char **secdata);
extern int nfq_get_indev_name(struct nlif_handle *nlif_handle,
struct nfq_data *nfad, char *name);
extern int nfq_get_physindev_name(struct nlif_handle *nlif_handle,
- struct nfq_data *nfad, char *name);
+ struct nfq_data *nfad, char *name);
extern int nfq_get_outdev_name(struct nlif_handle *nlif_handle,
struct nfq_data *nfad, char *name);
extern int nfq_get_physoutdev_name(struct nlif_handle *nlif_handle,
@@ -148,7 +149,9 @@ void nfq_nlmsg_verdict_put(struct nlmsghdr *nlh, int id, int verdict);
void nfq_nlmsg_verdict_put_mark(struct nlmsghdr *nlh, uint32_t mark);
void nfq_nlmsg_verdict_put_pkt(struct nlmsghdr *nlh, const void *pkt, uint32_t pktlen);
-int nfq_nlmsg_parse(const struct nlmsghdr *nlh, struct nlattr **pkt);
+int nfq_nlmsg_parse(const struct nlmsghdr *nlh, struct nlattr **attr);
+struct nlmsghdr *nfq_nlmsg_put(char *buf, int type, uint32_t queue_num);
+struct nlmsghdr *nfq_nlmsg_put2(char *buf, int type, uint32_t queue_num, uint16_t flags);
#ifdef __cplusplus
} /* extern "C" */
diff --git a/include/libnetfilter_queue/libnetfilter_queue_icmp.h b/include/libnetfilter_queue/libnetfilter_queue_icmp.h
new file mode 100644
index 0000000..9a8bd52
--- /dev/null
+++ b/include/libnetfilter_queue/libnetfilter_queue_icmp.h
@@ -0,0 +1,8 @@
+#ifndef _LIBNFQUEUE_ICMP_H_
+#define _LIBNFQUEUE_ICMP_H_
+
+struct pkt_buff;
+
+struct icmphdr *nfq_icmp_get_hdr(struct pkt_buff *pktb);
+
+#endif
diff --git a/include/libnetfilter_queue/libnetfilter_queue_ipv4.h b/include/libnetfilter_queue/libnetfilter_queue_ipv4.h
index e707f1f..17be93e 100644
--- a/include/libnetfilter_queue/libnetfilter_queue_ipv4.h
+++ b/include/libnetfilter_queue/libnetfilter_queue_ipv4.h
@@ -7,7 +7,7 @@ struct iphdr;
struct iphdr *nfq_ip_get_hdr(struct pkt_buff *pktb);
int nfq_ip_set_transport_header(struct pkt_buff *pktb, struct iphdr *iph);
void nfq_ip_set_checksum(struct iphdr *iph);
-int nfq_ip_mangle(struct pkt_buff *pkt, unsigned int dataoff, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, unsigned int rep_len);
+int nfq_ip_mangle(struct pkt_buff *pktb, unsigned int dataoff, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, unsigned int rep_len);
int nfq_ip_snprintf(char *buf, size_t size, const struct iphdr *iph);
#endif
diff --git a/include/libnetfilter_queue/libnetfilter_queue_ipv6.h b/include/libnetfilter_queue/libnetfilter_queue_ipv6.h
index 93452ce..c0a7d37 100644
--- a/include/libnetfilter_queue/libnetfilter_queue_ipv6.h
+++ b/include/libnetfilter_queue/libnetfilter_queue_ipv6.h
@@ -6,6 +6,7 @@ struct ip6_hdr;
struct ip6_hdr *nfq_ip6_get_hdr(struct pkt_buff *pktb);
int nfq_ip6_set_transport_header(struct pkt_buff *pktb, struct ip6_hdr *iph, uint8_t target);
+int nfq_ip6_mangle(struct pkt_buff *pktb, unsigned int dataoff,unsigned int match_offset, unsigned int match_len,const char *rep_buffer, unsigned int rep_len);
int nfq_ip6_snprintf(char *buf, size_t size, const struct ip6_hdr *ip6h);
#endif
diff --git a/include/libnetfilter_queue/libnetfilter_queue_tcp.h b/include/libnetfilter_queue/libnetfilter_queue_tcp.h
index c66dfb6..e1b9690 100644
--- a/include/libnetfilter_queue/libnetfilter_queue_tcp.h
+++ b/include/libnetfilter_queue/libnetfilter_queue_tcp.h
@@ -13,7 +13,8 @@ struct ip6_hdr;
void nfq_tcp_compute_checksum_ipv4(struct tcphdr *tcph, struct iphdr *iph);
void nfq_tcp_compute_checksum_ipv6(struct tcphdr *tcph, struct ip6_hdr *ip6h);
-int nfq_tcp_mangle_ipv4(struct pkt_buff *pkt, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, unsigned int rep_len);
+int nfq_tcp_mangle_ipv4(struct pkt_buff *pktb, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, unsigned int rep_len);
+int nfq_tcp_mangle_ipv6(struct pkt_buff *pktb, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, unsigned int rep_len);
int nfq_tcp_snprintf(char *buf, size_t size, const struct tcphdr *tcp);
diff --git a/include/libnetfilter_queue/libnetfilter_queue_udp.h b/include/libnetfilter_queue/libnetfilter_queue_udp.h
index f4b6c49..9d594f2 100644
--- a/include/libnetfilter_queue/libnetfilter_queue_udp.h
+++ b/include/libnetfilter_queue/libnetfilter_queue_udp.h
@@ -10,7 +10,8 @@ unsigned int nfq_udp_get_payload_len(struct udphdr *udph, struct pkt_buff *pktb)
void nfq_udp_compute_checksum_ipv4(struct udphdr *udph, struct iphdr *iph);
void nfq_udp_compute_checksum_ipv6(struct udphdr *udph, struct ip6_hdr *ip6h);
-int nfq_udp_mangle_ipv4(struct pkt_buff *pkt, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, unsigned int rep_len);
+int nfq_udp_mangle_ipv4(struct pkt_buff *pktb, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, unsigned int rep_len);
+int nfq_udp_mangle_ipv6(struct pkt_buff *pktb, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, unsigned int rep_len);
int nfq_udp_snprintf(char *buf, size_t size, const struct udphdr *udp);
diff --git a/include/libnetfilter_queue/linux_nfnetlink_queue.h b/include/libnetfilter_queue/linux_nfnetlink_queue.h
index 1975dfa..6844270 100644
--- a/include/libnetfilter_queue/linux_nfnetlink_queue.h
+++ b/include/libnetfilter_queue/linux_nfnetlink_queue.h
@@ -1,6 +1,8 @@
#ifndef _NFNETLINK_QUEUE_H
#define _NFNETLINK_QUEUE_H
+#warning "libnetfilter_queue/linux_nfnetlink_queue.h is deprecated, add #include <linux/netfilter/nfnetlink_queue.h> to your source code before #include <libnetfilter_queue/libnetfilter_queue.h>"
+
#ifndef aligned_u64
#define aligned_u64 unsigned long long __attribute__((aligned(8)))
#endif
@@ -46,11 +48,11 @@ enum nfqnl_attr_type {
NFQA_IFINDEX_PHYSOUTDEV, /* __u32 ifindex */
NFQA_HWADDR, /* nfqnl_msg_packet_hw */
NFQA_PAYLOAD, /* opaque data payload */
- NFQA_CT, /* nf_conntrack_netlink.h */
+ NFQA_CT, /* nfnetlink_conntrack.h */
NFQA_CT_INFO, /* enum ip_conntrack_info */
NFQA_CAP_LEN, /* __u32 length of captured packet */
NFQA_SKB_INFO, /* __u32 skb meta information */
- NFQA_EXP, /* nf_conntrack_netlink.h */
+ NFQA_EXP, /* nfnetlink_conntrack.h */
NFQA_UID, /* __u32 sk uid */
NFQA_GID, /* __u32 sk gid */
NFQA_SECCTX, /* security context string */
diff --git a/include/libnetfilter_queue/pktbuff.h b/include/libnetfilter_queue/pktbuff.h
index b15ee1e..d3588c7 100644
--- a/include/libnetfilter_queue/pktbuff.h
+++ b/include/libnetfilter_queue/pktbuff.h
@@ -1,11 +1,16 @@
#ifndef _PKTBUFF_H_
#define _PKTBUFF_H_
+#include <stdbool.h>
+
struct pkt_buff;
struct pkt_buff *pktb_alloc(int family, void *data, size_t len, size_t extra);
void pktb_free(struct pkt_buff *pktb);
+struct pkt_buff *pktb_setup_raw(void *pktb, int family, void *data, size_t len, size_t extra);
+size_t pktb_head_size(void);
+
uint8_t *pktb_data(struct pkt_buff *pktb);
uint32_t pktb_len(struct pkt_buff *pktb);
@@ -19,7 +24,7 @@ uint8_t *pktb_mac_header(struct pkt_buff *pktb);
uint8_t *pktb_network_header(struct pkt_buff *pktb);
uint8_t *pktb_transport_header(struct pkt_buff *pktb);
-int pktb_mangle(struct pkt_buff *pkt, unsigned int dataoff, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, unsigned int rep_len);
+int pktb_mangle(struct pkt_buff *pktb, int dataoff, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, unsigned int rep_len);
bool pktb_mangled(const struct pkt_buff *pktb);
diff --git a/include/linux/netfilter/nfnetlink_queue.h b/include/linux/netfilter/nfnetlink_queue.h
index 030672d..8e2e469 100644
--- a/include/linux/netfilter/nfnetlink_queue.h
+++ b/include/linux/netfilter/nfnetlink_queue.h
@@ -42,11 +42,11 @@ enum nfqnl_attr_type {
NFQA_IFINDEX_PHYSOUTDEV, /* __u32 ifindex */
NFQA_HWADDR, /* nfqnl_msg_packet_hw */
NFQA_PAYLOAD, /* opaque data payload */
- NFQA_CT, /* nf_conntrack_netlink.h */
+ NFQA_CT, /* nfnetlink_conntrack.h */
NFQA_CT_INFO, /* enum ip_conntrack_info */
NFQA_CAP_LEN, /* __u32 length of captured packet */
NFQA_SKB_INFO, /* __u32 skb meta information */
- NFQA_EXP, /* nf_conntrack_netlink.h */
+ NFQA_EXP, /* nfnetlink_conntrack.h */
NFQA_UID, /* __u32 sk uid */
NFQA_GID, /* __u32 sk gid */
NFQA_SECCTX,
diff --git a/src/Makefile.am b/src/Makefile.am
index 9fdccfb..079853e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -18,7 +18,7 @@
# set age to 0.
# </snippet>
#
-LIBVERSION=5:0:4
+LIBVERSION=6:0:5
include ${top_srcdir}/Make_global.am
@@ -26,11 +26,12 @@ lib_LTLIBRARIES = libnetfilter_queue.la
noinst_HEADERS = internal.h
-libnetfilter_queue_la_LDFLAGS = -Wc,-nostartfiles -lnfnetlink \
+libnetfilter_queue_la_LDFLAGS = -Wc,-nostartfiles \
-version-info $(LIBVERSION)
libnetfilter_queue_la_SOURCES = libnetfilter_queue.c \
nlmsg.c \
extra/checksum.c \
+ extra/icmp.c \
extra/ipv6.c \
extra/tcp.c \
extra/ipv4.c \
diff --git a/src/extra/checksum.c b/src/extra/checksum.c
index 4d52a99..33480af 100644
--- a/src/extra/checksum.c
+++ b/src/extra/checksum.c
@@ -11,11 +11,13 @@
#include <stdio.h>
#include <stdbool.h>
+#include <endian.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/tcp.h>
+#include <linux/netfilter/nfnetlink_queue.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#include "internal.h"
@@ -26,8 +28,13 @@ uint16_t nfq_checksum(uint32_t sum, uint16_t *buf, int size)
sum += *buf++;
size -= sizeof(uint16_t);
}
- if (size)
- sum += *(uint8_t *)buf;
+ if (size) {
+#if __BYTE_ORDER == __BIG_ENDIAN
+ sum += (uint16_t)*(uint8_t *)buf << 8;
+#else
+ sum += (uint16_t)*(uint8_t *)buf;
+#endif
+ }
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >>16);
@@ -56,25 +63,21 @@ uint16_t nfq_checksum_tcpudp_ipv6(struct ip6_hdr *ip6h, void *transport_hdr,
uint16_t protonum)
{
uint32_t sum = 0;
- uint32_t hdr_len = (uint32_t *)transport_hdr - (uint32_t *)ip6h;
- uint32_t len = ip6h->ip6_plen - hdr_len;
+ uint32_t hdr_len = (uint8_t *)transport_hdr - (uint8_t *)ip6h;
+ /* Allow for extra headers before the UDP header */
+ /* TODO: Deal with routing headers */
+ uint32_t len = ntohs(ip6h->ip6_plen) - (hdr_len - sizeof *ip6h);
uint8_t *payload = (uint8_t *)ip6h + hdr_len;
int i;
for (i=0; i<8; i++) {
- sum += (ip6h->ip6_src.s6_addr16[i] >> 16) & 0xFFFF;
- sum += (ip6h->ip6_src.s6_addr16[i]) & 0xFFFF;
+ sum += (ip6h->ip6_src.s6_addr16[i]);
}
for (i=0; i<8; i++) {
- sum += (ip6h->ip6_dst.s6_addr16[i] >> 16) & 0xFFFF;
- sum += (ip6h->ip6_dst.s6_addr16[i]) & 0xFFFF;
+ sum += (ip6h->ip6_dst.s6_addr16[i]);
}
sum += htons(protonum);
- sum += htons(ip6h->ip6_plen);
+ sum += htons(len);
return nfq_checksum(sum, (uint16_t *)payload, len);
}
-
-/**
- * @}
- */
diff --git a/src/extra/icmp.c b/src/extra/icmp.c
new file mode 100644
index 0000000..eaade7b
--- /dev/null
+++ b/src/extra/icmp.c
@@ -0,0 +1,57 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This code has been sponsored by Vyatta Inc. <http://www.vyatta.com>
+ */
+
+#include <stdio.h>
+#define _GNU_SOURCE
+#include <netinet/ip_icmp.h>
+
+#include <libnetfilter_queue/libnetfilter_queue_icmp.h>
+
+#include "internal.h"
+
+/**
+ * \defgroup icmp ICMP helper functions
+ *
+ * \manonly
+.SH SYNOPSIS
+.nf
+\fB
+#include <libmnl/libmnl.h>
+#include <libnetfilter_queue/libnetfilter_queue_icmp.h>
+\endmanonly
+ *
+ * @{
+ */
+
+/**
+ * nfq_icmp_get_hdr - get the ICMP header.
+ * \param pktb: pointer to user-space network packet buffer
+ * \returns validated pointer to the ICMP header or NULL if the ICMP header was
+ * not set or if a minimal length check fails.
+ * \note You have to call nfq_ip_set_transport_header() or
+ * nfq_ip6_set_transport_header() first to set the ICMP header.
+ */
+EXPORT_SYMBOL
+struct icmphdr *nfq_icmp_get_hdr(struct pkt_buff *pktb)
+{
+ if (pktb->transport_header == NULL)
+ return NULL;
+
+ /* No room for the ICMP header. */
+ if (pktb_tail(pktb) - pktb->transport_header < sizeof(struct icmphdr))
+ return NULL;
+
+ return (struct icmphdr *)pktb->transport_header;
+}
+
+/**
+ * @}
+ */
diff --git a/src/extra/ipv4.c b/src/extra/ipv4.c
index 3eb1054..58fb471 100644
--- a/src/extra/ipv4.c
+++ b/src/extra/ipv4.c
@@ -14,6 +14,7 @@
#include <arpa/inet.h>
#include <netinet/ip.h>
+#include <linux/netfilter/nfnetlink_queue.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <libnetfilter_queue/libnetfilter_queue_ipv4.h>
#include <libnetfilter_queue/pktbuff.h>
@@ -22,21 +23,34 @@
/**
* \defgroup ipv4 IPv4 helper functions
+ *
+ * \manonly
+.SH SYNOPSIS
+.nf
+\fB
+#include <libmnl/libmnl.h>
+#include <libnetfilter_queue/libnetfilter_queue_ipv4.h>
+\endmanonly
+ *
* @{
*/
/**
- * nfq_ip_get_hdr - get IPv4 header
- * \param pktb: pointer to network packet buffer
+ * nfq_ip_get_hdr - get the IPv4 header
+ * \param pktb: Pointer to user-space network packet buffer
+ * \returns validated pointer to the IPv4 header or NULL if IP is malformed or
+ * not version 4
*
- * This funcion returns NULL if the IPv4 is malformed or the protocol version
- * is not 4. On success, it returns a valid pointer to the IPv4 header.
+ * Many programs will not need to call this function. A possible use is to
+ * determine the layer 4 protocol. The validation is that the buffer is big
+ * enough for the declared lengths in the header, i.e. an extra check for packet
+ * truncation.
*/
EXPORT_SYMBOL
struct iphdr *nfq_ip_get_hdr(struct pkt_buff *pktb)
{
struct iphdr *iph;
- unsigned int pktlen = pktb->tail - pktb->network_header;
+ unsigned int pktlen = pktb_tail(pktb) - pktb->network_header;
/* Not enough room for IPv4 header. */
if (pktlen < sizeof(struct iphdr))
@@ -56,13 +70,14 @@ struct iphdr *nfq_ip_get_hdr(struct pkt_buff *pktb)
}
/**
- * nfq_ip_set_transport_header - set transport header
- * \param pktb: pointer to network packet buffer
- * \param iph: pointer to the IPv4 header
- *
- * Sets the \b transport_header field in \b pktb
- *
- * Level 4 helper functions need this to be set.
+ * nfq_ip_set_transport_header - set the \b transport_header field in \b pktb
+ * \param pktb: Pointer to user-space network packet buffer
+ * \param iph: Pointer to the IPv4 header
+ * \returns 0 on success or -1 if a minimal validation check fails
+ * \note
+ * Most programs should call __nfq_ip_set_transport_header__ as soon as
+ * possible, since most layer 4 helper functions assume the
+ * \b transport_header field is valid.
*/
EXPORT_SYMBOL
int nfq_ip_set_transport_header(struct pkt_buff *pktb, struct iphdr *iph)
@@ -78,11 +93,29 @@ int nfq_ip_set_transport_header(struct pkt_buff *pktb, struct iphdr *iph)
}
/**
- * nfq_ip_set_checksum - set IPv4 checksum
- * \param iph: pointer to the IPv4 header
+ * \defgroup ip_internals Internal IP functions
+ *
+ * Most user-space programs will never need these.
+ *
+ *
+ * \manonly
+.SH SYNOPSIS
+.nf
+\fB
+#include <libmnl/libmnl.h>
+#include <libnetfilter_queue/libnetfilter_queue_ipv4.h>
+\endmanonly
*
- * \note Call to this function if you modified the IPv4 header to update the
- * checksum.
+ * @{
+ */
+
+/**
+ * nfq_ip_set_checksum - set IPv4 checksum
+ * \param iph: Pointer to the IPv4 header
+ * \note
+ * nfq_ip_mangle() invokes this function.
+ * As long as developers always use the appropriate mangler for the layer being
+ * mangled, there is no need to call __nfq_ip_set_checksum__.
*/
EXPORT_SYMBOL
void nfq_ip_set_checksum(struct iphdr *iph)
@@ -94,44 +127,46 @@ void nfq_ip_set_checksum(struct iphdr *iph)
}
/**
+ * @}
+ */
+
+/**
* nfq_ip_mangle - mangle IPv4 packet buffer
- * \param pktb: pointer to network packet buffer
- * \param dataoff: offset to layer 4 header
- * \param match_offset: offset to content that you want to mangle
- * \param match_len: length of the existing content you want to mangle
- * \param rep_buffer: pointer to data you want to use to replace current content
- * \param rep_len: length of data you want to use to replace current content
+ * \param pktb: Pointer to user-space network packet buffer
+ * \param dataoff: Offset to layer 4 header, or zero to mangle IP header
+ * \param match_offset: Offset to content that you want to mangle
+ * \param match_len: Length of the existing content you want to mangle
+ * \param rep_buffer: Pointer to data you want to use to replace current content
+ * \param rep_len: Length of data you want to use to replace current content
* \returns 1 for success and 0 for failure. See pktb_mangle() for failure case
- * \note This function updates the IPv4 length and recalculates the IPv4
- * checksum (if necessary)
+ * \note This function updates the IPv4 length if necessary and recalculates the
+ * IPv4 checksum.
*/
EXPORT_SYMBOL
-int nfq_ip_mangle(struct pkt_buff *pkt, unsigned int dataoff,
+int nfq_ip_mangle(struct pkt_buff *pktb, unsigned int dataoff,
unsigned int match_offset, unsigned int match_len,
const char *rep_buffer, unsigned int rep_len)
{
- struct iphdr *iph = (struct iphdr *) pkt->network_header;
+ struct iphdr *iph = (struct iphdr *) pktb->network_header;
- if (!pktb_mangle(pkt, dataoff, match_offset, match_len,
- rep_buffer, rep_len))
+ if (!pktb_mangle(pktb, dataoff, match_offset, match_len, rep_buffer,
+ rep_len))
return 0;
/* fix IP hdr checksum information */
- iph->tot_len = htons(pkt->len);
+ iph->tot_len = htons(pktb_tail(pktb) - pktb->network_header);
nfq_ip_set_checksum(iph);
return 1;
}
/**
- * nfq_pkt_snprintf_ip - print IPv4 header into buffer in iptables LOG format
- * \param buf: pointer to buffer that will be used to print the header
- * \param size: size of the buffer (or remaining room in it)
- * \param ip: pointer to a valid IPv4 header
- *
- * This function returns the number of bytes that would have been written in
- * case that there is enough room in the buffer. Read snprintf manpage for more
- * information to know more about this strange behaviour.
+ * nfq_ip_snprintf - print IPv4 header into buffer in iptables LOG format
+ * \param buf: Pointer to buffer that will be used to print the header
+ * \param size: Size of the buffer (or remaining room in it)
+ * \param iph: Pointer to a valid IPv4 header
+ * \returns same as snprintf
+ * \sa **snprintf**(3)
*/
EXPORT_SYMBOL
int nfq_ip_snprintf(char *buf, size_t size, const struct iphdr *iph)
diff --git a/src/extra/ipv6.c b/src/extra/ipv6.c
index b599e91..fd8ebc4 100644
--- a/src/extra/ipv6.c
+++ b/src/extra/ipv6.c
@@ -15,6 +15,7 @@
#include <arpa/inet.h>
#include <netinet/ip6.h>
+#include <linux/netfilter/nfnetlink_queue.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <libnetfilter_queue/libnetfilter_queue_ipv6.h>
#include <libnetfilter_queue/pktbuff.h>
@@ -23,30 +24,40 @@
/**
* \defgroup ipv6 IPv6 helper functions
+ *
+ * \manonly
+.SH SYNOPSIS
+.nf
+\fB
+#include <arpa/inet.h>
+#include <linux/netfilter/nfnetlink_queue.h>
+#include <libnetfilter_queue/libnetfilter_queue.h>
+#include <libnetfilter_queue/libnetfilter_queue_ipv6.h>
+\endmanonly
+ *
* @{
*/
/**
* nfq_ip6_get_hdr - get IPv6 header
- * \param pktb: pointer to user-space network packet buffer
+ * \param pktb: Pointer to user-space network packet buffer
*
- * This funcion returns NULL if an invalid header is found. On sucess, it
- * returns a valid pointer to the header.
+ * \returns pointer to IPv6 header if a valid header found, else NULL.
*/
EXPORT_SYMBOL
struct ip6_hdr *nfq_ip6_get_hdr(struct pkt_buff *pktb)
{
struct ip6_hdr *ip6h;
- unsigned int pktlen = pktb->tail - pktb->network_header;
+ unsigned int pktlen = pktb_tail(pktb) - pktb->network_header;
- /* Not enough room for IPv4 header. */
+ /* Not enough room for IPv6 header. */
if (pktlen < sizeof(struct ip6_hdr))
return NULL;
ip6h = (struct ip6_hdr *)pktb->network_header;
/* Not IPv6 packet. */
- if (ip6h->ip6_flow != 0x60)
+ if ((*(uint8_t *)ip6h & 0xf0) != 0x60)
return NULL;
return ip6h;
@@ -54,12 +65,12 @@ struct ip6_hdr *nfq_ip6_get_hdr(struct pkt_buff *pktb)
/**
* nfq_ip6_set_transport_header - set transport header pointer for IPv6 packet
- * \param pktb: pointer to user-space network packet buffer
- * \param ip6h: pointer to IPv6 header
- * \param target: protocol number to find transport header (ie. IPPROTO_*)
+ * \param pktb: Pointer to user-space network packet buffer
+ * \param ip6h: Pointer to IPv6 header
+ * \param target: Protocol number to find transport header (ie. IPPROTO_*)
*
- * This function returns 1 if the protocol has been found and the transport
- * header has been set. Otherwise, it returns 0.
+ * \returns 1 if the protocol has been found and the transport
+ * header has been set, else 0.
*/
EXPORT_SYMBOL
int nfq_ip6_set_transport_header(struct pkt_buff *pktb, struct ip6_hdr *ip6h,
@@ -68,17 +79,26 @@ int nfq_ip6_set_transport_header(struct pkt_buff *pktb, struct ip6_hdr *ip6h,
uint8_t nexthdr = ip6h->ip6_nxt;
uint8_t *cur = (uint8_t *)ip6h + sizeof(struct ip6_hdr);
- while (nexthdr != target) {
+ while (nexthdr == IPPROTO_HOPOPTS ||
+ nexthdr == IPPROTO_ROUTING ||
+ nexthdr == IPPROTO_FRAGMENT ||
+ nexthdr == IPPROTO_AH ||
+ nexthdr == IPPROTO_NONE ||
+ nexthdr == IPPROTO_DSTOPTS) {
struct ip6_ext *ip6_ext;
uint32_t hdrlen;
+ /* Extension header was requested, we're done. */
+ if (nexthdr == target)
+ break;
+
/* No more extensions, we're done. */
if (nexthdr == IPPROTO_NONE) {
cur = NULL;
break;
}
/* No room for extension, bad packet. */
- if (pktb->tail - cur < sizeof(struct ip6_ext)) {
+ if (pktb_tail(pktb) - cur < sizeof(struct ip6_ext)) {
cur = NULL;
break;
}
@@ -88,16 +108,16 @@ int nfq_ip6_set_transport_header(struct pkt_buff *pktb, struct ip6_hdr *ip6h,
uint16_t *frag_off;
/* No room for full fragment header, bad packet. */
- if (pktb->tail - cur < sizeof(struct ip6_frag)) {
+ if (pktb_tail(pktb) - cur < sizeof(struct ip6_frag)) {
cur = NULL;
break;
}
- frag_off = (uint16_t *)cur +
- offsetof(struct ip6_frag, ip6f_offlg);
+ frag_off = (uint16_t *)(cur +
+ offsetof(struct ip6_frag, ip6f_offlg));
/* Fragment offset is only 13 bits long. */
- if (htons(*frag_off & ~0x7)) {
+ if (htons(*frag_off) & ~0x7) {
/* Not the first fragment, it does not contain
* any headers.
*/
@@ -108,20 +128,53 @@ int nfq_ip6_set_transport_header(struct pkt_buff *pktb, struct ip6_hdr *ip6h,
} else if (nexthdr == IPPROTO_AH)
hdrlen = (ip6_ext->ip6e_len + 2) << 2;
else
- hdrlen = ip6_ext->ip6e_len;
+ hdrlen = (ip6_ext->ip6e_len + 1) << 3;
nexthdr = ip6_ext->ip6e_nxt;
cur += hdrlen;
}
+ if (nexthdr != target)
+ cur = NULL;
pktb->transport_header = cur;
return cur ? 1 : 0;
}
/**
+ * nfq_ip6_mangle - mangle IPv6 packet buffer
+ * \param pktb: Pointer to user-space network packet buffer
+ * \param dataoff: Offset to layer 4 header
+ * \param match_offset: Offset to content that you want to mangle
+ * \param match_len: Length of the existing content you want to mangle
+ * \param rep_buffer: Pointer to data you want to use to replace current content
+ * \param rep_len: Length of data you want to use to replace current content
+ * \returns 1 for success and 0 for failure. See pktb_mangle() for failure case
+ * \note This function updates the IPv6 length (if necessary)
+ */
+EXPORT_SYMBOL
+int nfq_ip6_mangle(struct pkt_buff *pktb, unsigned int dataoff,
+ unsigned int match_offset, unsigned int match_len,
+ const char *rep_buffer, unsigned int rep_len)
+{
+ struct ip6_hdr *ip6h = (struct ip6_hdr *)pktb->network_header;
+
+ if (!pktb_mangle(pktb, dataoff, match_offset, match_len, rep_buffer,
+ rep_len))
+ return 0;
+
+ /* Fix IPv6 hdr length information */
+ ip6h->ip6_plen =
+ htons(pktb_tail(pktb) - pktb->network_header - sizeof *ip6h);
+
+ return 1;
+}
+
+/**
* nfq_ip6_snprintf - print IPv6 header into one buffer in iptables LOG format
- * \param buf: pointer to buffer that is used to print the object
- * \param size: size of the buffer (or remaining room in it).
- * \param ip6_hdr: pointer to a valid IPv6 header.
+ * \param buf: Pointer to buffer that is used to print the object
+ * \param size: Size of the buffer (or remaining room in it).
+ * \param ip6h: Pointer to a valid IPv6 header.
+ * \returns same as snprintf
+ * \sa **snprintf**(3)
*
*/
EXPORT_SYMBOL
diff --git a/src/extra/pktbuff.c b/src/extra/pktbuff.c
index 00af037..40d2250 100644
--- a/src/extra/pktbuff.c
+++ b/src/extra/pktbuff.c
@@ -9,6 +9,7 @@
* This code has been sponsored by Vyatta Inc. <http://www.vyatta.com>
*/
+#include <errno.h>
#include <stdlib.h>
#include <string.h> /* for memcpy */
#include <stdbool.h>
@@ -22,23 +23,81 @@
/**
* \defgroup pktbuff User-space network packet buffer
*
- * This library provides the user-space network packet buffer. This abstraction
- * is strongly inspired by Linux kernel network buffer, the so-called sk_buff.
+ * These functions provide the user-space network packet buffer.
+ * This abstraction is strongly inspired by Linux kernel network buffer,
+ * the so-called sk_buff.
+ *
+ * \manonly
+.SH SYNOPSIS
+.nf
+\fB
+#include <libmnl/libmnl.h>
+#include <libnetfilter_queue/pktbuff.h>
+\endmanonly
*
* @{
*/
+static int __pktb_setup(int family, struct pkt_buff *pktb)
+{
+ struct ethhdr *ethhdr;
+
+ switch (family) {
+ case AF_INET:
+ case AF_INET6:
+ pktb->network_header = pktb->data;
+ break;
+ case AF_BRIDGE:
+ ethhdr = (struct ethhdr *)pktb->data;
+ pktb->mac_header = pktb->data;
+
+ switch(ethhdr->h_proto) {
+ case ETH_P_IP:
+ case ETH_P_IPV6:
+ pktb->network_header = pktb->data + ETH_HLEN;
+ break;
+ default:
+ /* This protocol is unsupported. */
+ errno = EPROTONOSUPPORT;
+ return -1;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static void pktb_setup_metadata(struct pkt_buff *pktb, void *pkt_data,
+ size_t len, size_t extra)
+{
+ pktb->len = len;
+ pktb->data_len = len + extra;
+ pktb->data = pkt_data;
+}
+
/**
* pktb_alloc - allocate a new packet buffer
- * \param family Indicate what family, eg. AF_BRIDGE, AF_INET, AF_INET6, ...
+ * \param family Indicate what family. Currently supported families are
+ * AF_BRIDGE, AF_INET & AF_INET6.
* \param data Pointer to packet data
* \param len Packet length
* \param extra Extra memory in the tail to be allocated (for mangling)
*
* This function returns a packet buffer that contains the packet data and
- * some extra memory room in the tail (if requested).
+ * some extra memory room in the tail (if requested). This function copies
+ * the memory area provided as a pointer to packet data into the packet buffer
+ * structure.
+ *
+ * The extra length provides extra packet data room at the tail of the packet
+ * buffer in case you need to mangle it.
*
- * \return a pointer to a new queue handle or NULL on failure.
+ * \return Pointer to a new userspace packet buffer or NULL on failure.
+ * \par Errors
+ * __ENOMEM__ From __calloc__()
+ * \n
+ * __EPROTONOSUPPORT__ _family_ was __AF_BRIDGE__ and this is not an IP packet
+ * (v4 or v6)
+ * \sa __calloc__(3)
*/
EXPORT_SYMBOL
struct pkt_buff *pktb_alloc(int family, void *data, size_t len, size_t extra)
@@ -54,40 +113,54 @@ struct pkt_buff *pktb_alloc(int family, void *data, size_t len, size_t extra)
pkt_data = (uint8_t *)pktb + sizeof(struct pkt_buff);
memcpy(pkt_data, data, len);
- pktb->len = len;
- pktb->data_len = len + extra;
+ pktb_setup_metadata(pktb, pkt_data, len, extra);
- pktb->head = pkt_data;
- pktb->data = pkt_data;
- pktb->tail = pktb->head + len;
+ if (__pktb_setup(family, pktb) < 0) {
+ free(pktb);
+ return NULL;
+ }
- switch(family) {
- case AF_INET:
- pktb->network_header = pktb->data;
- break;
- case AF_BRIDGE: {
- struct ethhdr *ethhdr = (struct ethhdr *)pktb->data;
+ return pktb;
+}
- pktb->mac_header = pktb->data;
+/**
+ * pktb_setup_raw - set up a packet buffer from memory area
+ * \param pktb Pointer to memory of length pktb_head_size() bytes
+ * \param family Supported families are AF_BRIDGE, AF_INET & AF_INET6.
+ * \param data Pointer to packet data
+ * \param len Packet data length
+ * \param extra Extra memory available after packet data (for mangling).
+ *
+ * Use this function to set up a packet buffer from a memory area, minimum size
+ * of such memory area must be pktb_head_size(). This function attaches the
+ * packet data that is provided to the packet buffer (data is not copied). Use
+ * this function as an alternative to the pktb_alloc() interface for more
+ * control on memory management.
+ *
+ * \return Pointer to a new userspace packet buffer or NULL on failure.
+ * \par Errors
+ * __EPROTONOSUPPORT__ _family_ was __AF_BRIDGE__ and this is not an IP packet
+ * (v4 or v6)
+ */
+EXPORT_SYMBOL
+struct pkt_buff *pktb_setup_raw(void *pktb, int family, void *data,
+ size_t len, size_t extra)
+{
+ memset(pktb, 0, sizeof (struct pkt_buff));
+ pktb_setup_metadata(pktb, data, len, extra);
+ if (__pktb_setup(family, pktb) < 0)
+ pktb = NULL;
- switch(ethhdr->h_proto) {
- case ETH_P_IP:
- pktb->network_header = pktb->data + ETH_HLEN;
- break;
- default:
- /* This protocol is unsupported. */
- free(pktb);
- return NULL;
- }
- break;
- }
- }
return pktb;
}
/**
- * pktb_data - return pointer to the beginning of the packet buffer
- * \param pktb Pointer to packet buffer
+ * pktb_data - get pointer to network packet
+ * \param pktb Pointer to userspace packet buffer
+ * \return Pointer to start of network packet data within __pktb__
+ * \par
+ * It is appropriate to use _pktb_data_ as the second argument of
+ * nfq_nlmsg_verdict_put_pkt()
*/
EXPORT_SYMBOL
uint8_t *pktb_data(struct pkt_buff *pktb)
@@ -96,8 +169,12 @@ uint8_t *pktb_data(struct pkt_buff *pktb)
}
/**
- * pktb_len - return length of the packet buffer
- * \param pktb Pointer to packet buffer
+ * pktb_len - get length of packet buffer
+ * \param pktb Pointer to userspace packet buffer
+ * \return Length of packet contained within __pktb__
+ * \par
+ * It is appropriate to use _pktb_len_ as the third argument of
+ * nfq_nlmsg_verdict_put_pkt()
*/
EXPORT_SYMBOL
uint32_t pktb_len(struct pkt_buff *pktb)
@@ -107,7 +184,7 @@ uint32_t pktb_len(struct pkt_buff *pktb)
/**
* pktb_free - release packet buffer
- * \param pktb Pointer to packet buffer
+ * \param pktb Pointer to userspace packet buffer
*/
EXPORT_SYMBOL
void pktb_free(struct pkt_buff *pktb)
@@ -116,8 +193,51 @@ void pktb_free(struct pkt_buff *pktb)
}
/**
- * pktb_push - update pointer to the beginning of the packet buffer
- * \param pktb Pointer to packet buffer
+ * \defgroup otherfns Other functions
+ *
+ * The library provides a number of other functions which many user-space
+ * programs will never need. These divide into 2 groups:
+ * \n
+ * 1. Functions to get values of members of opaque __struct pktbuff__, described
+ * below
+ *
+ * 2. Internal functions, described in Module __Internal functions__
+ *
+ * \manonly
+.SH SYNOPSIS
+.nf
+\fB
+#include <libmnl/libmnl.h>
+#include <libnetfilter_queue/pktbuff.h>
+\endmanonly
+ *
+ * @{
+ */
+
+/**
+ * \defgroup do_not_use Internal functions
+ *
+ * Do not use these functions. Instead, always use the mangle
+ * function appropriate to the level at which you are working.
+ * \n
+ * pktb_mangle() uses all the below functions except _pktb_pull_, which is not
+ * used by anything.
+ *
+ * \manonly
+.SH SYNOPSIS
+.nf
+\fB
+#include <libmnl/libmnl.h>
+#include <libnetfilter_queue/pktbuff.h>
+\endmanonly
+ *
+ * @{
+ */
+
+/**
+ * pktb_push - decrement pointer to packet buffer
+ * \param pktb Pointer to userspace packet buffer
+ * \param len Number of bytes to subtract from packet start address
*/
EXPORT_SYMBOL
void pktb_push(struct pkt_buff *pktb, unsigned int len)
@@ -127,8 +247,9 @@ void pktb_push(struct pkt_buff *pktb, unsigned int len)
}
/**
- * pktb_pull - update pointer to the beginning of the packet buffer
- * \param pktb Pointer to packet buffer
+ * pktb_pull - increment pointer to packet buffer
+ * \param pktb Pointer to userspace packet buffer
+ * \param len Number of bytes to add to packet start address
*/
EXPORT_SYMBOL
void pktb_pull(struct pkt_buff *pktb, unsigned int len)
@@ -139,18 +260,19 @@ void pktb_pull(struct pkt_buff *pktb, unsigned int len)
/**
* pktb_put - add extra bytes to the tail of the packet buffer
- * \param pktb Pointer to packet buffer
+ * \param pktb Pointer to userspace packet buffer
+ * \param len Number of bytes to add to packet tail (and length)
*/
EXPORT_SYMBOL
void pktb_put(struct pkt_buff *pktb, unsigned int len)
{
- pktb->tail += len;
pktb->len += len;
}
/**
* pktb_trim - set new length for this packet buffer
- * \param pktb Pointer to packet buffer
+ * \param pktb Pointer to userspace packet buffer
+ * \param len New packet length (tail is adjusted to reflect this)
*/
EXPORT_SYMBOL
void pktb_trim(struct pkt_buff *pktb, unsigned int len)
@@ -159,8 +281,17 @@ void pktb_trim(struct pkt_buff *pktb, unsigned int len)
}
/**
- * pktb_tailroom - get room in bytes in the tail of the packet buffer
- * \param pktb Pointer to packet buffer
+ * @}
+ */
+
+/**
+ * pktb_tailroom - get room available for packet expansion
+ * \param pktb Pointer to userspace packet buffer
+ * \return room in bytes after the tail of the packet buffer
+ * \n
+ * This starts off as the __extra__ argument to pktb_alloc().
+ * Programmers should ensure this __extra__ argument is sufficient for any
+ * packet mangle, as packet buffers cannot be expanded dynamically.
*/
EXPORT_SYMBOL
unsigned int pktb_tailroom(struct pkt_buff *pktb)
@@ -169,8 +300,11 @@ unsigned int pktb_tailroom(struct pkt_buff *pktb)
}
/**
- * pktb_mac_header - return pointer to layer 2 header (if any)
- * \param pktb Pointer to packet buffer
+ * pktb_mac_header - get address of layer 2 header (if any)
+ * \param pktb Pointer to userspace packet buffer
+ * \return Pointer to MAC header or NULL if no such header present.
+ * \n
+ * Only packet buffers in family __AF_BRIDGE__ have a non-NULL MAC header.
*/
EXPORT_SYMBOL
uint8_t *pktb_mac_header(struct pkt_buff *pktb)
@@ -179,8 +313,10 @@ uint8_t *pktb_mac_header(struct pkt_buff *pktb)
}
/**
- * pktb_network_header - return pointer to layer 3 header
- * \param pktb Pointer to packet buffer
+ * pktb_network_header - get address of layer 3 header
+ * \param pktb Pointer to userspace packet buffer
+ * \return Pointer to layer 3 header or NULL if the packet buffer was created
+ * with an unsupported family
*/
EXPORT_SYMBOL
uint8_t *pktb_network_header(struct pkt_buff *pktb)
@@ -189,8 +325,13 @@ uint8_t *pktb_network_header(struct pkt_buff *pktb)
}
/**
- * pktb_transport_header - return pointer to layer 4 header (if any)
- * \param pktb Pointer to packet buffer
+ * pktb_transport_header - get address of layer 4 header (if known)
+ * \param pktb Pointer to userspace packet buffer
+ * \return Pointer to layer 4 header or NULL if not (yet) set
+ * \note
+ * Unlike the lower-level headers, it is the programmer's responsibility to
+ * create the level 4 (transport) header pointer by caling e.g.
+ * nfq_ip_set_transport_header()
*/
EXPORT_SYMBOL
uint8_t *pktb_transport_header(struct pkt_buff *pktb)
@@ -198,26 +339,29 @@ uint8_t *pktb_transport_header(struct pkt_buff *pktb)
return pktb->transport_header;
}
-static int pktb_expand_tail(struct pkt_buff *pkt, int extra)
+/**
+ * @}
+ */
+
+static int pktb_expand_tail(struct pkt_buff *pktb, int extra)
{
/* No room in packet, cannot mangle it. We don't support dynamic
* reallocation. Instead, increase the size of the extra room in
* the tail in pktb_alloc.
*/
- if (pkt->len + extra > pkt->data_len)
+ if (pktb->len + extra > pktb->data_len)
return 0;
- pkt->len += extra;
- pkt->tail = pkt->tail + extra;
+ pktb->len += extra;
return 1;
}
-static int enlarge_pkt(struct pkt_buff *pkt, unsigned int extra)
+static int enlarge_pkt(struct pkt_buff *pktb, unsigned int extra)
{
- if (pkt->len + extra > 65535)
+ if (pktb->len + extra > 65535)
return 0;
- if (!pktb_expand_tail(pkt, extra - pktb_tailroom(pkt)))
+ if (!pktb_expand_tail(pktb, extra - pktb_tailroom(pktb)))
return 0;
return 1;
@@ -225,20 +369,27 @@ static int enlarge_pkt(struct pkt_buff *pkt, unsigned int extra)
/**
* pktb_mangle - adjust contents of a packet
- * \param pkt Pointer to packet buffer
- * \param dataoff Offset to layer 4 header. Specify zero to access layer 3 (IP)
- * header
+ * \param pktb Pointer to userspace packet buffer
+ * \param dataoff Supplementary offset, usually offset from layer 3 (IP) header
+ * to the layer 4 (TCP or UDP) header. Specify zero to access the layer 3
+ * header. If \b pktb was created in family \b AF_BRIDGE, specify
+ * \b -ETH_HLEN (a negative offset) to access the layer 2 (MAC) header.
* \param match_offset Further offset to content that you want to mangle
* \param match_len Length of the existing content you want to mangle
* \param rep_buffer Pointer to data you want to use to replace current content
* \param rep_len Length of data you want to use to replace current content
* \returns 1 for success and 0 for failure. Failure will occur if the \b extra
- * argument to the pktb_alloc() call that created \b pkt is less than the excess
- * of \b rep_len over \b match_len
+ * argument to the pktb_alloc() call that created \b pktb is less than the
+ * excess of \b rep_len over \b match_len
+ \warning pktb_mangle does not update any checksums. Developers should use the
+ appropriate mangler for the protocol level: nfq_ip_mangle(),
+ nfq_tcp_mangle_ipv4(), nfq_udp_mangle_ipv4() or IPv6 variants.
+ \n
+ It is appropriate to use pktb_mangle to change the MAC header.
*/
EXPORT_SYMBOL
-int pktb_mangle(struct pkt_buff *pkt,
- unsigned int dataoff,
+int pktb_mangle(struct pkt_buff *pktb,
+ int dataoff,
unsigned int match_offset,
unsigned int match_len,
const char *rep_buffer,
@@ -247,39 +398,56 @@ int pktb_mangle(struct pkt_buff *pkt,
unsigned char *data;
if (rep_len > match_len &&
- rep_len - match_len > pktb_tailroom(pkt) &&
- !enlarge_pkt(pkt, rep_len - match_len))
+ rep_len - match_len > pktb_tailroom(pktb) &&
+ !enlarge_pkt(pktb, rep_len - match_len))
return 0;
- data = pkt->network_header + dataoff;
+ data = pktb->network_header + dataoff;
/* move post-replacement */
memmove(data + match_offset + rep_len,
data + match_offset + match_len,
- pkt->tail - (pkt->network_header + dataoff +
+ pktb_tail(pktb) - (pktb->network_header + dataoff +
match_offset + match_len));
/* insert data from buffer */
memcpy(data + match_offset, rep_buffer, rep_len);
- /* update pkt info */
+ /* update packet info */
if (rep_len > match_len)
- pktb_put(pkt, rep_len - match_len);
+ pktb_put(pktb, rep_len - match_len);
else
- pktb_trim(pkt, pkt->len + rep_len - match_len);
+ pktb_trim(pktb, pktb->len + rep_len - match_len);
- pkt->mangled = true;
+ pktb->mangled = true;
return 1;
}
/**
- * pktb_mangled - return true if packet has been mangled
- * \param pktb Pointer to packet buffer
+ * pktb_mangled - test whether packet has been mangled
+ * \param pktb Pointer to userspace packet buffer
+ * \return __true__ if packet has been mangled (modified), else __false__
+ * \par
+ * When assembling a verdict, it is not necessary to return the contents of
+ * un-modified packets. Use _pktb_mangled_ to decide whether packet contents
+ * need to be returned.
+ */
+EXPORT_SYMBOL
+bool pktb_mangled(const struct pkt_buff *pktb)
+{
+ return pktb->mangled;
+}
+
+/**
+ * pktb_head_size - get number of bytes needed for a packet buffer
+ * (control part only)
+ * \return size of struct pkt_buff
*/
+
EXPORT_SYMBOL
-bool pktb_mangled(const struct pkt_buff *pkt)
+size_t pktb_head_size(void)
{
- return pkt->mangled;
+ return sizeof(struct pkt_buff);
}
/**
diff --git a/src/extra/tcp.c b/src/extra/tcp.c
index 136d7ea..720afd2 100644
--- a/src/extra/tcp.c
+++ b/src/extra/tcp.c
@@ -18,27 +18,36 @@
#define _GNU_SOURCE
#include <netinet/tcp.h>
+#include <linux/netfilter/nfnetlink_queue.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <libnetfilter_queue/libnetfilter_queue_tcp.h>
#include <libnetfilter_queue/libnetfilter_queue_ipv4.h>
+#include <libnetfilter_queue/libnetfilter_queue_ipv6.h>
#include <libnetfilter_queue/pktbuff.h>
#include "internal.h"
/**
* \defgroup tcp TCP helper functions
+ *
+ * \manonly
+.SH SYNOPSIS
+.nf
+\fB
+#include <libmnl/libmnl.h>
+#include <libnetfilter_queue/libnetfilter_queue_tcp.h>
+\endmanonly
+ *
* @{
*/
/**
- * nfq_tcp_get - get the TCP header
+ * nfq_tcp_get_hdr - get the TCP header
* \param pktb: pointer to user-space network packet buffer
- *
- * This function returns NULL if an invalid TCP header is found. On success,
- * it returns the TCP header.
- *
- * \note You have to call nfq_ip_set_transport_header or
- * nfq_ip6_set_transport_header first to access the TCP header.
+ * \returns validated pointer to the TCP header or NULL if the TCP header was
+ * not set or if a minimal length check fails.
+ * \note You have to call nfq_ip_set_transport_header() or
+ * nfq_ip6_set_transport_header() first to set the TCP header.
*/
EXPORT_SYMBOL
struct tcphdr *nfq_tcp_get_hdr(struct pkt_buff *pktb)
@@ -47,7 +56,7 @@ struct tcphdr *nfq_tcp_get_hdr(struct pkt_buff *pktb)
return NULL;
/* No room for the TCP header. */
- if (pktb->tail - pktb->transport_header < sizeof(struct tcphdr))
+ if (pktb_tail(pktb) - pktb->transport_header < sizeof(struct tcphdr))
return NULL;
return (struct tcphdr *)pktb->transport_header;
@@ -57,6 +66,7 @@ struct tcphdr *nfq_tcp_get_hdr(struct pkt_buff *pktb)
* nfq_tcp_get_payload - get the TCP packet payload
* \param tcph: pointer to the TCP header
* \param pktb: pointer to user-space network packet buffer
+ * \returns Pointer to the TCP payload, or NULL if malformed TCP packet.
*/
EXPORT_SYMBOL
void *nfq_tcp_get_payload(struct tcphdr *tcph, struct pkt_buff *pktb)
@@ -68,7 +78,7 @@ void *nfq_tcp_get_payload(struct tcphdr *tcph, struct pkt_buff *pktb)
return NULL;
/* malformed TCP data offset. */
- if (pktb->transport_header + len > pktb->tail)
+ if (pktb->transport_header + len > pktb_tail(pktb))
return NULL;
return pktb->transport_header + len;
@@ -78,17 +88,42 @@ void *nfq_tcp_get_payload(struct tcphdr *tcph, struct pkt_buff *pktb)
* nfq_tcp_get_payload_len - get the tcp packet payload
* \param tcph: pointer to the TCP header
* \param pktb: pointer to user-space network packet buffer
+ * \returns Length of TCP payload (user data)
*/
EXPORT_SYMBOL
unsigned int nfq_tcp_get_payload_len(struct tcphdr *tcph, struct pkt_buff *pktb)
{
- return pktb->tail - pktb->transport_header;
+ return pktb_tail(pktb) - pktb->transport_header - (tcph->doff * 4);
}
/**
- * nfq_tcp_set_checksum_ipv4 - computes IPv4/TCP packet checksum
+ * \defgroup tcp_internals Internal TCP functions
+ *
+ * Most user-space programs will never need these.
+ *
+ *
+ * \manonly
+.SH SYNOPSIS
+.nf
+\fB
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/tcp.h>
+#include <libnetfilter_queue/libnetfilter_queue_tcp.h>
+\endmanonly
+ *
+ * @{
+ */
+
+/**
+ * nfq_tcp_compute_checksum_ipv4 - computes IPv4/TCP packet checksum
* \param tcph: pointer to the TCP header
* \param iph: pointer to the IPv4 header
+ * \note
+ * nfq_tcp_mangle_ipv4() invokes this function.
+ * As long as developers always use __nfq_tcp_mangle_ipv4__ when changing the
+ * content of a TCP message, there is no need to call
+ * __nfq_tcp_compute_checksum_ipv4__.
*/
EXPORT_SYMBOL
void nfq_tcp_compute_checksum_ipv4(struct tcphdr *tcph, struct iphdr *iph)
@@ -99,9 +134,14 @@ void nfq_tcp_compute_checksum_ipv4(struct tcphdr *tcph, struct iphdr *iph)
}
/**
- * nfq_tcp_set_checksum_ipv6 - computes IPv6/TCP packet checksum
+ * nfq_tcp_compute_checksum_ipv6 - computes IPv6/TCP packet checksum
* \param tcph: pointer to the TCP header
- * \param iph: pointer to the IPv6 header
+ * \param ip6h: pointer to the IPv6 header
+ * \note
+ * nfq_tcp_mangle_ipv6() invokes this function.
+ * As long as developers always use __nfq_tcp_mangle_ipv6__ when changing the
+ * content of a TCP message, there is no need to call
+ * __nfq_tcp_compute_checksum_ipv6__.
*/
EXPORT_SYMBOL
void nfq_tcp_compute_checksum_ipv6(struct tcphdr *tcph, struct ip6_hdr *ip6h)
@@ -111,6 +151,10 @@ void nfq_tcp_compute_checksum_ipv6(struct tcphdr *tcph, struct ip6_hdr *ip6h)
tcph->check = nfq_checksum_tcpudp_ipv6(ip6h, tcph, IPPROTO_TCP);
}
+/**
+ * @}
+ */
+
/*
* The union cast uses a gcc extension to avoid aliasing problems
* (union is compatible to any of its members)
@@ -128,7 +172,9 @@ union tcp_word_hdr {
* readable way
* \param buf: pointer to buffer that is used to print the object
* \param size: size of the buffer (or remaining room in it).
- * \param tcp: pointer to a valid tcp header.
+ * \param tcph: pointer to a valid tcp header.
+ * \returns Same as \b snprintf
+ * \sa __snprintf__(3)
*
*/
EXPORT_SYMBOL
@@ -183,21 +229,25 @@ int nfq_tcp_snprintf(char *buf, size_t size, const struct tcphdr *tcph)
* \param match_len: length of the existing content you want to mangle
* \param rep_buffer: pointer to data you want to use to replace current content
* \param rep_len: length of data you want to use to replace current content
- *
- * \note This function recalculates the IPv4 and TCP checksums for you.
+ * \returns 1 for success and 0 for failure. See pktb_mangle() for failure case
+ * \note This function updates the IPv4 length and recalculates the IPv4 & TCP
+ * checksums for you.
+ * \warning After changing the length of a TCP message, the application will
+ * need to mangle sequence numbers in both directions until another change
+ * puts them in sync again
*/
EXPORT_SYMBOL
-int nfq_tcp_mangle_ipv4(struct pkt_buff *pkt,
+int nfq_tcp_mangle_ipv4(struct pkt_buff *pktb,
unsigned int match_offset, unsigned int match_len,
const char *rep_buffer, unsigned int rep_len)
{
struct iphdr *iph;
struct tcphdr *tcph;
- iph = (struct iphdr *)pkt->network_header;
- tcph = (struct tcphdr *)(pkt->network_header + iph->ihl*4);
+ iph = (struct iphdr *)pktb->network_header;
+ tcph = (struct tcphdr *)(pktb->network_header + iph->ihl*4);
- if (!nfq_ip_mangle(pkt, iph->ihl*4 + tcph->doff*4,
+ if (!nfq_ip_mangle(pktb, iph->ihl*4 + tcph->doff*4,
match_offset, match_len, rep_buffer, rep_len))
return 0;
@@ -207,5 +257,44 @@ int nfq_tcp_mangle_ipv4(struct pkt_buff *pkt,
}
/**
+ * nfq_tcp_mangle_ipv6 - Mangle TCP/IPv6 packet buffer
+ * \param pktb: Pointer to network packet buffer
+ * \param match_offset: Offset from start of TCP data of content that you want
+ * to mangle
+ * \param match_len: Length of the existing content you want to mangle
+ * \param rep_buffer: Pointer to data you want to use to replace current content
+ * \param rep_len: Length of data you want to use to replace current content
+ * \returns 1 for success and 0 for failure. See pktb_mangle() for failure case
+ * \note This function updates the IPv6 length and recalculates the TCP
+ * checksum for you.
+ * \warning After changing the length of a TCP message, the application will
+ * need to mangle sequence numbers in both directions until another change
+ * puts them in sync again
+ */
+EXPORT_SYMBOL
+int nfq_tcp_mangle_ipv6(struct pkt_buff *pktb,
+ unsigned int match_offset, unsigned int match_len,
+ const char *rep_buffer, unsigned int rep_len)
+{
+ struct ip6_hdr *ip6h;
+ struct tcphdr *tcph;
+
+ ip6h = (struct ip6_hdr *)pktb->network_header;
+ tcph = (struct tcphdr *)(pktb->transport_header);
+ if (!tcph)
+ return 0;
+
+ if (!nfq_ip6_mangle(pktb,
+ pktb->transport_header - pktb->network_header +
+ tcph->doff * 4,
+ match_offset, match_len, rep_buffer, rep_len))
+ return 0;
+
+ nfq_tcp_compute_checksum_ipv6(tcph, ip6h);
+
+ return 1;
+}
+
+/**
* @}
*/
diff --git a/src/extra/udp.c b/src/extra/udp.c
index fed23e2..ede2196 100644
--- a/src/extra/udp.c
+++ b/src/extra/udp.c
@@ -17,24 +17,37 @@
#define _GNU_SOURCE
#include <netinet/udp.h>
+#include <linux/netfilter/nfnetlink_queue.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <libnetfilter_queue/libnetfilter_queue_udp.h>
#include <libnetfilter_queue/libnetfilter_queue_ipv4.h>
+#include <libnetfilter_queue/libnetfilter_queue_ipv6.h>
#include <libnetfilter_queue/pktbuff.h>
#include "internal.h"
/**
* \defgroup udp UDP helper functions
+ *
+ * \manonly
+.SH SYNOPSIS
+.nf
+\fB
+#include <libmnl/libmnl.h>
+#include <libnetfilter_queue/libnetfilter_queue_udp.h>
+\endmanonly
+ *
* @{
*/
/**
* nfq_udp_get_hdr - get the UDP header.
- * \param pktb: Pointer to network packet buffer
+ * \param pktb: Pointer to userspace network packet buffer
*
- * This function returns NULL if invalid UDP header is found. On success,
- * it returns the UDP header.
+ * \returns validated pointer to the UDP header or NULL if the UDP header was
+ * not set or if a minimal length check fails.
+ * \note You have to call nfq_ip_set_transport_header() or
+ * nfq_ip6_set_transport_header() first to set the UDP header.
*/
EXPORT_SYMBOL
struct udphdr *nfq_udp_get_hdr(struct pkt_buff *pktb)
@@ -43,7 +56,7 @@ struct udphdr *nfq_udp_get_hdr(struct pkt_buff *pktb)
return NULL;
/* No room for the UDP header. */
- if (pktb->tail - pktb->transport_header < sizeof(struct udphdr))
+ if (pktb_tail(pktb) - pktb->transport_header < sizeof(struct udphdr))
return NULL;
return (struct udphdr *)pktb->transport_header;
@@ -52,7 +65,8 @@ struct udphdr *nfq_udp_get_hdr(struct pkt_buff *pktb)
/**
* nfq_udp_get_payload - get the UDP packet payload.
* \param udph: Pointer to UDP header
- * \param pktb: Pointer to network packet buffer
+ * \param pktb: Pointer to userspace network packet buffer
+ * \returns Pointer to the UDP payload, or NULL if malformed UDP packet.
*/
EXPORT_SYMBOL
void *nfq_udp_get_payload(struct udphdr *udph, struct pkt_buff *pktb)
@@ -64,7 +78,7 @@ void *nfq_udp_get_payload(struct udphdr *udph, struct pkt_buff *pktb)
return NULL;
/* malformed UDP packet. */
- if (pktb->transport_header + len > pktb->tail)
+ if (pktb->transport_header + len > pktb_tail(pktb))
return NULL;
return pktb->transport_header + sizeof(struct udphdr);
@@ -73,23 +87,43 @@ void *nfq_udp_get_payload(struct udphdr *udph, struct pkt_buff *pktb)
/**
* nfq_udp_get_payload_len - get the udp packet payload.
* \param udph: Pointer to UDP header
- * \param pktb: Pointer to network packet buffer
+ * \param pktb: Pointer to userspace network packet buffer
+ * \returns Length of UDP payload (user data)
*/
EXPORT_SYMBOL
unsigned int nfq_udp_get_payload_len(struct udphdr *udph, struct pkt_buff *pktb)
{
- return pktb->tail - pktb->transport_header;
+ return pktb_tail(pktb) - pktb->transport_header - sizeof(struct udphdr);
}
/**
- * nfq_udp_set_checksum_ipv4 - computes a IPv4/TCP packet's segment
- * \param iphdrp: pointer to the ip header
- * \param ippayload: payload of the ip packet
+ * \defgroup udp_internals Internal UDP functions
*
- * \returns the checksum of the udp segment.
+ * Most user-space programs will never need these.
+ *
+ *
+ * \manonly
+.SH SYNOPSIS
+.nf
+\fB
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/udp.h>
+#include <libnetfilter_queue/libnetfilter_queue_udp.h>
+\endmanonly
*
- * \see nfq_pkt_compute_ip_checksum
- * \see nfq_pkt_compute_udp_checksum
+ * @{
+ */
+
+/**
+ * nfq_udp_compute_checksum_ipv4 - sets up the UDP checksum in a UDP/IPv4 packet
+ * \param udph: pointer to the UDP header
+ * \param iph: pointer to the IPv4 header
+ * \note
+ * nfq_udp_mangle_ipv4() invokes this function.
+ * As long as developers always use __nfq_udp_mangle_ipv4__ when changing the
+ * content of a UDP message, there is no need to call
+ * __nfq_udp_compute_checksum_ipv4__.
*/
EXPORT_SYMBOL
void nfq_udp_compute_checksum_ipv4(struct udphdr *udph, struct iphdr *iph)
@@ -100,14 +134,14 @@ void nfq_udp_compute_checksum_ipv4(struct udphdr *udph, struct iphdr *iph)
}
/**
- * nfq_udp_set_checksum_ipv6 - computes a IPv6/TCP packet's segment
- * \param iphdrp: pointer to the ip header
- * \param ippayload: payload of the ip packet
- *
- * \returns the checksum of the udp segment.
- *
- * \see nfq_pkt_compute_ip_checksum
- * \see nfq_pkt_compute_udp_checksum
+ * nfq_udp_compute_checksum_ipv6 - sets up the UDP checksum in a UDP/IPv6 packet
+ * \param udph: pointer to the UDP header
+ * \param ip6h: pointer to the IPv6 header
+ * \note
+ * nfq_udp_mangle_ipv6() invokes this function.
+ * As long as developers always use __nfq_udp_mangle_ipv6__ when changing the
+ * content of a UDP message, there is no need to call
+ * __nfq_udp_compute_checksum_ipv6__.
*/
EXPORT_SYMBOL
void nfq_udp_compute_checksum_ipv6(struct udphdr *udph, struct ip6_hdr *ip6h)
@@ -118,6 +152,10 @@ void nfq_udp_compute_checksum_ipv6(struct udphdr *udph, struct ip6_hdr *ip6h)
}
/**
+ * @}
+ */
+
+/**
* nfq_udp_mangle_ipv4 - Mangle UDP/IPv4 packet buffer
* \param pktb: Pointer to network packet buffer
* \param match_offset: Offset from start of UDP data of content that you want
@@ -130,19 +168,19 @@ void nfq_udp_compute_checksum_ipv6(struct udphdr *udph, struct ip6_hdr *ip6h)
* checksums for you.
*/
EXPORT_SYMBOL
-int nfq_udp_mangle_ipv4(struct pkt_buff *pkt,
+int nfq_udp_mangle_ipv4(struct pkt_buff *pktb,
unsigned int match_offset, unsigned int match_len,
const char *rep_buffer, unsigned int rep_len)
{
struct iphdr *iph;
struct udphdr *udph;
- iph = (struct iphdr *)pkt->network_header;
- udph = (struct udphdr *)(pkt->network_header + iph->ihl*4);
+ iph = (struct iphdr *)pktb->network_header;
+ udph = (struct udphdr *)(pktb->network_header + iph->ihl*4);
udph->len = htons(ntohs(udph->len) + rep_len - match_len);
- if (!nfq_ip_mangle(pkt, iph->ihl*4 + sizeof(struct udphdr),
+ if (!nfq_ip_mangle(pktb, iph->ihl*4 + sizeof(struct udphdr),
match_offset, match_len, rep_buffer, rep_len))
return 0;
@@ -152,11 +190,51 @@ int nfq_udp_mangle_ipv4(struct pkt_buff *pkt,
}
/**
+ * nfq_udp_mangle_ipv6 - Mangle UDP/IPv6 packet buffer
+ * \param pktb: Pointer to network packet buffer
+ * \param match_offset: Offset from start of UDP data of content that you want
+ * to mangle
+ * \param match_len: Length of the existing content you want to mangle
+ * \param rep_buffer: Pointer to data you want to use to replace current content
+ * \param rep_len: Length of data you want to use to replace current content
+ * \returns 1 for success and 0 for failure. See pktb_mangle() for failure case
+ * \note This function updates the IPv6 and UDP lengths and recalculates the UDP
+ * checksum for you.
+ */
+EXPORT_SYMBOL
+int nfq_udp_mangle_ipv6(struct pkt_buff *pktb,
+ unsigned int match_offset, unsigned int match_len,
+ const char *rep_buffer, unsigned int rep_len)
+{
+ struct ip6_hdr *ip6h;
+ struct udphdr *udph;
+
+ ip6h = (struct ip6_hdr *)pktb->network_header;
+ udph = (struct udphdr *)(pktb->transport_header);
+ if (!udph)
+ return 0;
+
+ udph->len = htons(ntohs(udph->len) + rep_len - match_len);
+
+ if (!nfq_ip6_mangle(pktb,
+ pktb->transport_header - pktb->network_header +
+ sizeof(struct udphdr),
+ match_offset, match_len, rep_buffer, rep_len))
+ return 0;
+
+ nfq_udp_compute_checksum_ipv6(udph, ip6h);
+
+ return 1;
+}
+
+/**
* nfq_pkt_snprintf_udp_hdr - print udp header into one buffer in a humnan
* readable way
* \param buf: pointer to buffer that is used to print the object
* \param size: size of the buffer (or remaining room in it).
- * \param udp: pointer to a valid udp header.
+ * \param udph: pointer to a valid udp header.
+ * \returns The number of characters notionally written (excluding trailing NUL)
+ * \sa __snprintf__(3)
*
*/
EXPORT_SYMBOL
diff --git a/src/internal.h b/src/internal.h
index d968325..ae849d6 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -23,9 +23,7 @@ struct pkt_buff {
uint8_t *network_header;
uint8_t *transport_header;
- uint8_t *head;
uint8_t *data;
- uint8_t *tail;
uint32_t len;
uint32_t data_len;
@@ -33,4 +31,8 @@ struct pkt_buff {
bool mangled;
};
+static inline uint8_t *pktb_tail(struct pkt_buff *pktb)
+{
+ return pktb->data + pktb->len;
+}
#endif
diff --git a/src/libnetfilter_queue.c b/src/libnetfilter_queue.c
index 7014878..bf67a19 100644
--- a/src/libnetfilter_queue.c
+++ b/src/libnetfilter_queue.c
@@ -4,7 +4,7 @@
* (C) 2005, 2008-2010 by Pablo Neira Ayuso <pablo@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
+ * it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation (or any later at your option)
*
* This program is distributed in the hope that it will be useful,
@@ -29,6 +29,7 @@
#include <errno.h>
#include <netinet/in.h>
#include <sys/socket.h>
+#include <linux/netfilter/nfnetlink_queue.h>
#include <libnfnetlink/libnfnetlink.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
@@ -39,53 +40,71 @@
*
* libnetfilter_queue is a userspace library providing an API to packets that
* have been queued by the kernel packet filter. It is is part of a system that
- * deprecates the old ip_queue / libipq mechanism.
+ * replaces the old ip_queue / libipq mechanism (withdrawn in kernel 3.5).
*
* libnetfilter_queue homepage is:
- * http://netfilter.org/projects/libnetfilter_queue/
+ * https://netfilter.org/projects/libnetfilter_queue/
*
- * \section Dependencies
- * libnetfilter_queue requires libnfnetlink and a kernel that includes the
- * nfnetlink_queue subsystem (i.e. 2.6.14 or later).
+ <h1>Dependencies</h1>
+ * libnetfilter_queue requires libmnl, libnfnetlink and a kernel that includes
+ * the Netfilter NFQUEUE over NFNETLINK interface (i.e. 2.6.14 or later).
*
- * \section Main Features
+ * <h1>Main Features</h1>
* - receiving queued packets from the kernel nfnetlink_queue subsystem
- * - issuing verdicts and/or reinjecting altered packets to the kernel
+ * - issuing verdicts and possibly reinjecting altered packets to the kernel
* nfnetlink_queue subsystem
*
- * The cinematic is the following: When an iptables rules with target NFQUEUE
- * matches, the kernel en-queued the packet in a chained list. It then format
- * a nfnetlink message and sends the information (packet data , packet id and
- * metadata) via a socket to the software connected to the queue. The software
- * can then read the message.
+ * The cinematic is the following: When an nft rule with action **queue**
+ * matches, the kernel terminates the current nft chain and enqueues the packet
+ * in a chained list. It then formats and sends an nfnetlink message containing
+ * the packet id and whatever information the userspace program configured to
+ * receive (packet data and/or metadata) via a socket to the userspace program.
*
- * To remove the packet from the queue, the userspace software must issue a
- * verdict asking kernel to accept or drop the packet. Userspace can also alter
- * the packet. Verdict can be done in asynchronous manner, as the only needed
- * information is the packet id.
+ * The userspace program must issue a verdict advising the kernel to **accept**
+ * or **drop** the packet. Either verdict takes the packet off the queue:
+ * **drop** discards the packet while
+ * **accept** passes it on to the next chain.
+ * Userspace can also alter packet contents or metadata (e.g. packet mark,
+ * contrack mark). Verdict can be done in asynchronous manner, as the only
+ * needed information is the packet id.
*
- * When a queue is full, packets that should have been en-queued are dropped by
- * kernel instead of being en-queued.
+ * When a queue is full, packets that should have been enqueued are dropped by
+ * kernel instead of being enqueued.
*
- * \section Git Tree
- * The current development version of libnetfilter_queue can be accessed
- * at https://git.netfilter.org/cgi-bin/gitweb.cgi?p=libnetfilter_queue.git;a=summary.
+ * <h1>Git Tree</h1>
+ * The current development version of libnetfilter_queue can be accessed at
+ * https://git.netfilter.org/libnetfilter_queue.
*
- * \section Privileges
+ * <h1>Privileges</h1>
* You need the CAP_NET_ADMIN capability in order to allow your application
* to receive from and to send packets to kernel-space.
*
- * \section Using libnetfilter_queue
- *
- * To write your own program using libnetfilter_queue, you should start by reading
- * the doxygen documentation (start by \link LibrarySetup \endlink page) and
- * nf-queue.c source file.
+ * <h1>Using libnetfilter_queue</h1>
*
- * Another source of information on libnetfilter_queue usage is the following
+ * To write your own program using libnetfilter_queue, you should start by
+ * reading (or, if feasible, compiling and stepping through with *gdb*)
+ * nf-queue.c source file.
+ * Simple compile line:
+ * \verbatim
+gcc -g3 -ggdb -Wall -lmnl -lnetfilter_queue -o nf-queue nf-queue.c
+\endverbatim
+ *The doxygen documentation
+ * \htmlonly
+<a class="el" href="group__LibrarySetup.html">LibrarySetup </a>
+\endhtmlonly
+ * \manonly
+\fBLibrarySetup\fP\
+\endmanonly
+ * is Deprecated and
+ * incompatible with non-deprecated functions. It is hoped to produce a
+ * corresponding non-deprecated (*Current*) topic soon.
+ *
+ * Somewhat outdated but possibly providing some insight into
+ * libnetfilter_queue usage is the following
* article:
* https://home.regit.org/netfilter-en/using-nfqueue-and-libnetfilter_queue/
*
- * \section errors ENOBUFS errors in recv()
+ * <h1>ENOBUFS errors in recv()</h1>
*
* recv() may return -1 and errno is set to ENOBUFS in case that your
* application is not fast enough to retrieve the packets from the kernel.
@@ -94,7 +113,7 @@
* you may hit it again sooner or later. The next section provides some hints
* on how to obtain the best performance for your application.
*
- * \section perf Performance
+ * <h1>Performance</h1>
* To improve your libnetfilter_queue application in terms of performance,
* you may consider the following tweaks:
*
@@ -108,6 +127,9 @@
* (it requires Linux kernel >= 2.6.31).
* - consider using fail-open option see nfq_set_queue_flags() (it requires
* Linux kernel >= 3.6)
+ * - make your application offload aware to avoid costly normalization on kernel
+ * side. See NFQA_CFG_F_GSO flag to nfq_set_queue_flags().
+ * Linux kernel >= 3.10.
* - increase queue max length with nfq_set_queue_maxlen() to resist to packets
* burst
*/
@@ -136,7 +158,7 @@ struct nfq_data {
EXPORT_SYMBOL int nfq_errno;
/***********************************************************************
- * low level stuff
+ * low level stuff
***********************************************************************/
static void del_qh(struct nfq_q_handle *qh)
@@ -227,12 +249,12 @@ struct nfnl_handle *nfq_nfnlh(struct nfq_handle *h)
*
* \defgroup Queue Queue handling [DEPRECATED]
*
- * Once libnetfilter_queue library has been initialised (See
+ * Once libnetfilter_queue library has been initialised (See
* \link LibrarySetup \endlink), it is possible to bind the program to a
* specific queue. This can be done by using nfq_create_queue().
*
* The queue can then be tuned via nfq_set_mode() or nfq_set_queue_maxlen().
- *
+ *
* Here's a little code snippet that create queue numbered 0:
* \verbatim
printf("binding this socket to queue '0'\n");
@@ -259,7 +281,7 @@ struct nfnl_handle *nfq_nfnlh(struct nfq_handle *h)
nfq_handle_packet(h, buf, rv);
}
\endverbatim
- * When the decision on a packet has been choosed, the verdict has to be given
+ * When the decision on a packet has been chosen, the verdict has to be given
* by calling nfq_set_verdict() or nfq_set_verdict2(). The verdict
* determines the destiny of the packet as follows:
*
@@ -276,8 +298,18 @@ struct nfnl_handle *nfq_nfnlh(struct nfq_handle *h)
* is to also set an nfmark using nfq_set_verdict2, and set up the nefilter
* rules to only queue a packet when the mark is not (yet) set.
*
- * Data and information about the packet can be fetch by using message parsing
+ * Data and information about the packet can be fetched by using message parsing
* functions (See \link Parsing \endlink).
+ *
+ * \manonly
+.SH SYNOPSIS
+.nf
+\fB
+#include <linux/netfilter.h>
+#include <linux/netfilter/nfnetlink_queue.h>
+#include <libnetfilter_queue/libnetfilter_queue.h>
+\endmanonly
+ *
* @{
*/
@@ -307,7 +339,7 @@ int nfq_fd(struct nfq_handle *h)
*
* Library initialisation is made in two steps.
*
- * First step is to call nfq_open() to open a NFQUEUE handler.
+ * First step is to call nfq_open() to open a NFQUEUE handler.
*
* Second step is to tell the kernel that userspace queueing is handle by
* NFQUEUE for the selected protocol. This is made by calling nfq_unbind_pf()
@@ -376,7 +408,7 @@ struct nfq_handle *nfq_open(void)
* \param nfnlh Netfilter netlink connection handle obtained by calling nfnl_open()
*
* This function obtains a netfilter queue connection handle using an existing
- * netlink connection. This function is used internally to implement
+ * netlink connection. This function is used internally to implement
* nfq_open(), and should typically not be called directly.
*
* \return a pointer to a new queue handle or NULL on failure.
@@ -398,7 +430,7 @@ struct nfq_handle *nfq_open_nfnl(struct nfnl_handle *nfnlh)
memset(h, 0, sizeof(*h));
h->nfnlh = nfnlh;
- h->nfnlssh = nfnl_subsys_open(h->nfnlh, NFNL_SUBSYS_QUEUE,
+ h->nfnlssh = nfnl_subsys_open(h->nfnlh, NFNL_SUBSYS_QUEUE,
NFQNL_MSG_MAX, 0);
if (!h->nfnlssh) {
/* FIXME: nfq_errno */
@@ -426,6 +458,14 @@ out_free:
* When the program has finished with libnetfilter_queue, it has to call
* the nfq_close() function to free all associated resources.
*
+ * \manonly
+.SH SYNOPSIS
+.nf
+\fB
+#include <linux/netfilter/nfnetlink_queue.h>
+#include <libnetfilter_queue/libnetfilter_queue.h>
+\endmanonly
+ *
* @{
*/
@@ -435,7 +475,7 @@ out_free:
*
* This function closes the nfqueue handler and free associated resources.
*
- * \return 0 on success, non-zero on failure.
+ * \return 0 on success, non-zero on failure.
*/
EXPORT_SYMBOL
int nfq_close(struct nfq_handle *h)
@@ -502,15 +542,15 @@ int nfq_unbind_pf(struct nfq_handle *h, uint16_t pf)
* \return a nfq_q_handle pointing to the newly created queue
*
* Creates a new queue handle, and returns it. The new queue is identified by
- * #num, and the callback specified by #cb will be called for each enqueued
- * packet. The #data argument will be passed unchanged to the callback. If
- * a queue entry with id #num already exists, this function will return failure
- * and the existing entry is unchanged.
+ * \b num, and the callback specified by \b cb will be called for each enqueued
+ * packet. The \b data argument will be passed unchanged to the callback. If
+ * a queue entry with id \b num already exists,
+ * this function will return failure and the existing entry is unchanged.
*
* The nfq_callback type is defined in libnetfilter_queue.h as:
* \verbatim
typedef int nfq_callback(struct nfq_q_handle *qh,
- struct nfgenmsg *nfmsg,
+ struct nfgenmsg *nfmsg,
struct nfq_data *nfad, void *data);
\endverbatim
*
@@ -640,7 +680,7 @@ int nfq_set_mode(struct nfq_q_handle *qh, uint8_t mode, uint32_t range)
* nfq_set_queue_flags - set flags (options) for the kernel queue
* \param qh Netfilter queue handle obtained by call to nfq_create_queue().
* \param mask specifies which flag bits to modify
- * \param flag bitmask of flags
+ * \param flags bitmask of flags
*
* Existing flags, that you may want to combine, are:
*
@@ -819,19 +859,19 @@ static int __set_verdict(struct nfq_q_handle *qh, uint32_t id,
*/
/**
- * nfq_set_verdict - issue a verdict on a packet
+ * nfq_set_verdict - issue a verdict on a packet
* \param qh Netfilter queue handle obtained by call to nfq_create_queue().
* \param id ID assigned to packet by netfilter.
* \param verdict verdict to return to netfilter (NF_ACCEPT, NF_DROP)
- * \param data_len number of bytes of data pointed to by #buf
+ * \param data_len number of bytes of data pointed to by \b buf
* \param buf the buffer that contains the packet data
*
- * Can be obtained by:
+ * Can be obtained by:
* \verbatim
int id;
struct nfqnl_msg_packet_hdr *ph = nfq_get_msg_packet_hdr(tb);
if (ph)
- id = ntohl(ph->packet_id);
+ id = ntohl(ph->packet_id);
\endverbatim
*
* Notifies netfilter of the userspace verdict for the given packet. Every
@@ -856,7 +896,7 @@ int nfq_set_verdict(struct nfq_q_handle *qh, uint32_t id,
* \param id ID assigned to packet by netfilter.
* \param verdict verdict to return to netfilter (NF_ACCEPT, NF_DROP)
* \param mark mark to put on packet
- * \param data_len number of bytes of data pointed to by #buf
+ * \param data_len number of bytes of data pointed to by \b buf
* \param buf the buffer that contains the packet data
*/
EXPORT_SYMBOL
@@ -875,7 +915,7 @@ int nfq_set_verdict2(struct nfq_q_handle *qh, uint32_t id,
* \param verdict verdict to return to netfilter (NF_ACCEPT, NF_DROP)
*
* Unlike nfq_set_verdict, the verdict is applied to all queued packets
- * whose packet id is smaller or equal to #id.
+ * whose packet id is smaller or equal to \b id.
*
* batch support was added in Linux 3.1.
* These functions will fail silently on older kernels.
@@ -909,7 +949,7 @@ int nfq_set_verdict_batch2(struct nfq_q_handle *qh, uint32_t id,
* \param id ID assigned to packet by netfilter.
* \param verdict verdict to return to netfilter (NF_ACCEPT, NF_DROP)
* \param mark the mark to put on the packet, in network byte order.
- * \param data_len number of bytes of data pointed to by #buf
+ * \param data_len number of bytes of data pointed to by \b buf
* \param buf the buffer that contains the packet data
*
* \return -1 on error; >= 0 otherwise.
@@ -933,11 +973,20 @@ int nfq_set_verdict_mark(struct nfq_q_handle *qh, uint32_t id,
/*************************************************************
- * Message parsing functions
+ * Message parsing functions
*************************************************************/
/**
* \defgroup Parsing Message parsing functions [DEPRECATED]
+ *
+ * \manonly
+.SH SYNOPSIS
+.nf
+\fB
+#include <linux/netfilter/nfnetlink_queue.h>
+#include <libnetfilter_queue/libnetfilter_queue.h>
+\endmanonly
+ *
* @{
*/
@@ -1054,7 +1103,7 @@ uint32_t nfq_get_outdev(struct nfq_data *nfad)
* The index of the physical device the queued packet will be sent out.
* If the returned index is 0, the packet is destined for localhost or the
* physical output interface is not yet known (ie. PREROUTING?).
- *
+ *
* \return The index of physical interface that the packet output will be routed out.
*/
EXPORT_SYMBOL
@@ -1070,34 +1119,34 @@ uint32_t nfq_get_physoutdev(struct nfq_data *nfad)
* \param nfad Netlink packet data handle passed to callback function
* \param name pointer to the buffer to receive the interface name;
* not more than \c IFNAMSIZ bytes will be copied to it.
- * \return -1 in case of error, >0 if it succeed.
+ * \return -1 in case of error, >0 if it succeed.
*
* To use a nlif_handle, You need first to call nlif_open() and to open
- * an handler. Don't forget to store the result as it will be used
+ * an handler. Don't forget to store the result as it will be used
* during all your program life:
* \verbatim
h = nlif_open();
- if (h == NULL) {
- perror("nlif_open");
- exit(EXIT_FAILURE);
- }
+ if (h == NULL) {
+ perror("nlif_open");
+ exit(EXIT_FAILURE);
+ }
\endverbatim
* Once the handler is open, you need to fetch the interface table at a
* whole via a call to nlif_query.
* \verbatim
- nlif_query(h);
+ nlif_query(h);
\endverbatim
* libnfnetlink is able to update the interface mapping when a new interface
* appears. To do so, you need to call nlif_catch() on the handler after each
* interface related event. The simplest way to get and treat event is to run
- * a select() or poll() against the nlif file descriptor. To get this file
+ * a select() or poll() against the nlif file descriptor. To get this file
* descriptor, you need to use nlif_fd:
* \verbatim
- if_fd = nlif_fd(h);
+ if_fd = nlif_fd(h);
\endverbatim
* Don't forget to close the handler when you don't need the feature anymore:
* \verbatim
- nlif_close(h);
+ nlif_close(h);
\endverbatim
*
*/
@@ -1119,7 +1168,7 @@ int nfq_get_indev_name(struct nlif_handle *nlif_handle,
*
* See nfq_get_indev_name() documentation for nlif_handle usage.
*
- * \return -1 in case of error, > 0 if it succeed.
+ * \return -1 in case of error, > 0 if it succeed.
*/
EXPORT_SYMBOL
int nfq_get_physindev_name(struct nlif_handle *nlif_handle,
@@ -1139,7 +1188,7 @@ int nfq_get_physindev_name(struct nlif_handle *nlif_handle,
*
* See nfq_get_indev_name() documentation for nlif_handle usage.
*
- * \return -1 in case of error, > 0 if it succeed.
+ * \return -1 in case of error, > 0 if it succeed.
*/
EXPORT_SYMBOL
int nfq_get_outdev_name(struct nlif_handle *nlif_handle,
@@ -1159,7 +1208,7 @@ int nfq_get_outdev_name(struct nlif_handle *nlif_handle,
*
* See nfq_get_indev_name() documentation for nlif_handle usage.
*
- * \return -1 in case of error, > 0 if it succeed.
+ * \return -1 in case of error, > 0 if it succeed.
*/
EXPORT_SYMBOL
@@ -1173,7 +1222,7 @@ int nfq_get_physoutdev_name(struct nlif_handle *nlif_handle,
/**
* nfq_get_packet_hw
*
- * get hardware address
+ * get hardware address
*
* \param nfad Netlink packet data handle passed to callback function
*
@@ -1200,8 +1249,40 @@ struct nfqnl_msg_packet_hw *nfq_get_packet_hw(struct nfq_data *nfad)
}
/**
+ * nfq_get_skbinfo - return the NFQA_SKB_INFO meta information
+ * \param nfad Netlink packet data handle passed to callback function
+ *
+ * This can be used to obtain extra information about a packet by testing
+ * the returned integer for any of the following bit flags:
+ *
+ * - NFQA_SKB_CSUMNOTREADY
+ * packet header checksums will be computed by hardware later on, i.e.
+ * tcp/ip checksums in the packet must not be validated, application
+ * should pretend they are correct.
+ * - NFQA_SKB_GSO
+ * packet is an aggregated super-packet. It exceeds device mtu and will
+ * be (re-)split on transmit by hardware.
+ * - NFQA_SKB_CSUM_NOTVERIFIED
+ * packet checksum was not yet verified by the kernel/hardware, for
+ * example because this is an incoming packet and the NIC does not
+ * perform checksum validation at hardware level.
+ *
+ * \return the skbinfo value
+ * \sa __nfq_set_queue_flags__(3)
+ */
+EXPORT_SYMBOL
+uint32_t nfq_get_skbinfo(struct nfq_data *nfad)
+{
+ if (!nfnl_attr_present(nfad->data, NFQA_SKB_INFO))
+ return 0;
+
+ return ntohl(nfnl_get_data(nfad->data, NFQA_SKB_INFO, uint32_t));
+}
+
+/**
* nfq_get_uid - get the UID of the user the packet belongs to
* \param nfad Netlink packet data handle passed to callback function
+ * \param uid Set to UID on return
*
* \warning If the NFQA_CFG_F_GSO flag is not set, then fragmented packets
* may be pushed into the queue. In this case, only one fragment will have the
@@ -1222,6 +1303,7 @@ int nfq_get_uid(struct nfq_data *nfad, uint32_t *uid)
/**
* nfq_get_gid - get the GID of the user the packet belongs to
* \param nfad Netlink packet data handle passed to callback function
+ * \param gid Set to GID on return
*
* \warning If the NFQA_CFG_F_GSO flag is not set, then fragmented packets
* may be pushed into the queue. In this case, only one fragment will have the
@@ -1266,7 +1348,7 @@ int nfq_get_secctx(struct nfq_data *nfad, unsigned char **secdata)
}
/**
- * nfq_get_payload - get payload
+ * nfq_get_payload - get payload
* \param nfad Netlink packet data handle passed to callback function
* \param data Pointer of pointer that will be pointed to the payload
*
@@ -1304,6 +1386,15 @@ do { \
/**
* \defgroup Printing Printing [DEPRECATED]
+ *
+ * \manonly
+.SH SYNOPSIS
+.nf
+\fB
+#include <linux/netfilter/nfnetlink_queue.h>
+#include <libnetfilter_queue/libnetfilter_queue.h>
+\endmanonly
+ *
* @{
*/
diff --git a/src/nlmsg.c b/src/nlmsg.c
index d06e6db..39fd12d 100644
--- a/src/nlmsg.c
+++ b/src/nlmsg.c
@@ -27,18 +27,56 @@
/**
* \defgroup nfq_verd Verdict helpers
+ *
+ * \manonly
+.SH SYNOPSIS
+.nf
+\fB
+#include <linux/netfilter.h>
+#include <linux/netfilter/nfnetlink_queue.h>
+#include <libnetfilter_queue/libnetfilter_queue.h>
+\endmanonly
+ *
* @{
*/
/**
- * nfq_nlmsg_verdict_put - Put a verdict into a Netlink header
+ * nfq_nlmsg_verdict_put - Put a verdict into a Netlink message
* \param nlh Pointer to netlink message
* \param id ID assigned to packet by netfilter
- * \param verdict verdict to return to netfilter (NF_ACCEPT, NF_DROP)
+ * \param verdict verdict to return to netfilter (see \b Verdicts below)
+ * \par Verdicts
+ * __NF_DROP__ Drop the packet. This is final.
+ * \n
+ * __NF_ACCEPT__ Accept the packet. Processing of the current base chain
+ * and any called chains terminates,
+ * but the packet may still be processed by subsequently invoked base chains.
+ * \n
+ * __NF_STOP__ Like __NF_ACCEPT__, but skip any further base chains using the
+ * current hook.
+ * \n
+ * __NF_REPEAT__ Like __NF_ACCEPT__, but re-queue this packet to the
+ * current base chain. One way to prevent a re-queueing loop is to
+ * also set a packet mark using nfq_nlmsg_verdict_put_mark() and have the
+ * program test for this mark in \c attr[NFQA_MARK]; or have the nefilter rules
+ * do this test.
+ * \n
+ * __NF_QUEUE_NR__(*new_queue*) Like __NF_ACCEPT__, but queue this packet to
+ * queue number *new_queue*. As with the command-line \b queue \b num verdict,
+ * if no process is listening to that queue then the packet is discarded; but
+ * again like with the command-line, one may OR in a flag to bypass *new_queue*
+ * if there is no listener, as in this snippet:
+ * \verbatim
+ nfq_nlmsg_verdict_put(nlh, id, NF_QUEUE_NR(new_queue) |
+ NF_VERDICT_FLAG_QUEUE_BYPASS);
+\endverbatim
*
- * See examples/nf-queue.c, line 46 for an example of how to use this function.
+ * See examples/nf-queue.c, line
+ * <a class="el" href="nf-queue_8c_source.html#l00046">46</a>
+ * for an example of how to use this function in context.
* The calling sequence is \b main --> \b mnl_cb_run --> \b queue_cb -->
* \b nfq_send_verdict --> \b nfq_nlmsg_verdict_put
+ * (\b cb being short for \b callback).
*/
EXPORT_SYMBOL
void nfq_nlmsg_verdict_put(struct nlmsghdr *nlh, int id, int verdict)
@@ -50,6 +88,15 @@ void nfq_nlmsg_verdict_put(struct nlmsghdr *nlh, int id, int verdict)
mnl_attr_put(nlh, NFQA_VERDICT_HDR, sizeof(vh), &vh);
}
+/**
+ * nfq_nlmsg_verdict_put_mark - Put a packet mark into a netlink message
+ * \param nlh Pointer to netlink message
+ * \param mark Value of mark to put
+ *
+ * The mark becomes part of the packet's metadata, and may be tested by the *nft
+ * primary expression* **meta mark**
+ * \sa __nft__(1)
+ */
EXPORT_SYMBOL
void nfq_nlmsg_verdict_put_mark(struct nlmsghdr *nlh, uint32_t mark)
{
@@ -57,6 +104,39 @@ void nfq_nlmsg_verdict_put_mark(struct nlmsghdr *nlh, uint32_t mark)
}
EXPORT_SYMBOL
+/**
+ * nfq_nlmsg_verdict_put_pkt - Put replacement packet content into a netlink
+ * message
+ * \param nlh Pointer to netlink message
+ * \param pkt Pointer to start of modified IP datagram
+ * \param plen Length of modified IP datagram
+ *
+ * There is only ever a need to return packet content if it has been modified.
+ * Usually one of the nfq_*_mangle_* functions does the modifying.
+ *
+ * This code snippet uses nfq_udp_mangle_ipv4. See nf-queue.c for
+ * context:
+ * \verbatim
+// main calls queue_cb (line 64) to process an enqueued packet:
+ // Extra variables
+ uint8_t *payload, *rep_data;
+ unsigned int match_offset, match_len, rep_len;
+
+ // The next line was commented-out (with payload void*)
+ payload = mnl_attr_get_payload(attr[NFQA_PAYLOAD]);
+ // Copy data to a packet buffer (allow 255 bytes for mangling).
+ pktb = pktb_alloc(AF_INET, payload, plen, 255);
+ // (decide that this packet needs mangling)
+ nfq_udp_mangle_ipv4(pktb, match_offset, match_len, rep_data, rep_len);
+ // nfq_udp_mangle_ipv4 updates packet length, no need to track locally
+
+ // Eventually nfq_send_verdict (line 39) gets called
+ // The received packet may or may not have been modified.
+ // Add this code before nfq_nlmsg_verdict_put call:
+ if (pktb_mangled(pktb))
+ nfq_nlmsg_verdict_put_pkt(nlh, pktb_data(pktb), pktb_len(pktb));
+\endverbatim
+ */
void nfq_nlmsg_verdict_put_pkt(struct nlmsghdr *nlh, const void *pkt,
uint32_t plen)
{
@@ -69,6 +149,15 @@ void nfq_nlmsg_verdict_put_pkt(struct nlmsghdr *nlh, const void *pkt,
/**
* \defgroup nfq_cfg Config helpers
+ *
+ * \manonly
+.SH SYNOPSIS
+.nf
+\fB
+#include <linux/netfilter/nfnetlink_queue.h>
+#include <libnetfilter_queue/libnetfilter_queue.h>
+\endmanonly
+ *
* @{
*/
@@ -135,6 +224,15 @@ void nfq_nlmsg_cfg_put_qmaxlen(struct nlmsghdr *nlh, uint32_t queue_maxlen)
/**
* \defgroup nlmsg Netlink message helper functions
+ *
+ * \manonly
+.SH SYNOPSIS
+.nf
+\fB
+#include <linux/netfilter/nfnetlink_queue.h>
+#include <libnetfilter_queue/libnetfilter_queue.h>
+\endmanonly
+ *
* @{
*/
@@ -155,7 +253,6 @@ static int nfq_pkt_parse_attr_cb(const struct nlattr *attr, void *data)
case NFQA_IFINDEX_PHYSOUTDEV:
case NFQA_CAP_LEN:
case NFQA_SKB_INFO:
- case NFQA_SECCTX:
case NFQA_UID:
case NFQA_GID:
case NFQA_CT_INFO:
@@ -183,6 +280,7 @@ static int nfq_pkt_parse_attr_cb(const struct nlattr *attr, void *data)
case NFQA_PAYLOAD:
case NFQA_CT:
case NFQA_EXP:
+ case NFQA_SECCTX:
break;
}
tb[type] = attr;
@@ -191,11 +289,9 @@ static int nfq_pkt_parse_attr_cb(const struct nlattr *attr, void *data)
/**
* nfq_nlmsg_parse - set packet attributes from netlink message
- * \param nlh netlink message that you want to read.
- * \param attr pointer to array of attributes to set.
- *
- * This function returns MNL_CB_ERROR if any error occurs, or MNL_CB_OK on
- * success.
+ * \param nlh Pointer to netlink message
+ * \param attr Pointer to array of attributes to set
+ * \returns MNL_CB_OK on success or MNL_CB_ERROR if any error occurs
*/
EXPORT_SYMBOL
int nfq_nlmsg_parse(const struct nlmsghdr *nlh, struct nlattr **attr)
@@ -205,5 +301,83 @@ int nfq_nlmsg_parse(const struct nlmsghdr *nlh, struct nlattr **attr)
}
/**
+ * nfq_nlmsg_put - Convert memory buffer into a Netlink buffer
+ * \param *buf Pointer to memory buffer
+ * \param type Either NFQNL_MSG_CONFIG or NFQNL_MSG_VERDICT
+ * \param queue_num Queue number
+ * \returns Pointer to netlink message
+ */
+EXPORT_SYMBOL
+struct nlmsghdr *nfq_nlmsg_put(char *buf, int type, uint32_t queue_num)
+{
+ return nfq_nlmsg_put2(buf, type, queue_num, 0);
+}
+
+/**
+ * nfq_nlmsg_put2 - Set up a netlink header with user-specified flags
+ * in a memory buffer
+ * \param *buf Pointer to memory buffer
+ * \param type One of NFQNL_MSG_CONFIG, NFQNL_MSG_VERDICT
+ * or NFQNL_MSG_VERDICT_BATCH
+ * \param queue_num Queue number
+ * \param flags additional NLM_F_xxx flags to put in message header. These are
+ * defined in /usr/include/linux/netlink.h. nfq_nlmsg_put2() always
+ * sets NLM_F_REQUEST
+ * \returns Pointer to netlink header
+ *
+ * For most applications, the only sensible flag will be NLM_F_ACK.
+ * Use it to get an explicit acknowledgment from the kernel, e.g.
+ * attempt to configure NFQA_CFG_F_SECCTX on a kernel not supporting
+ * CONFIG_NETWORK_SECMARK.
+ * \n
+ * The kernel always sends a message in response to a failed command.
+ * NLM_F_ACK instructs the kernel to also send a message in response
+ * to a successful command.
+ * \n
+ * This code snippet demonstrates reading these responses:
+ * \verbatim
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+
+ nlh = nfq_nlmsg_put2(buf, NFQNL_MSG_CONFIG, queue_num,
+ NLM_F_ACK);
+ mnl_attr_put_u32(nlh, NFQA_CFG_FLAGS, NFQA_CFG_F_SECCTX);
+ mnl_attr_put_u32(nlh, NFQA_CFG_MASK, NFQA_CFG_F_SECCTX);
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ perror("mnl_socket_send");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof buf);
+ if (ret == -1) {
+ perror("mnl_socket_recvfrom");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_cb_run(buf, ret, 0, portid, NULL, NULL);
+ if (ret == -1)
+ fprintf(stderr, "This kernel version does not allow to "
+ "retrieve security context.\n");
+\endverbatim
+ *
+ */
+
+EXPORT_SYMBOL
+struct nlmsghdr *nfq_nlmsg_put2(char *buf, int type, uint32_t queue_num,
+ uint16_t flags)
+{
+ struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | type;
+ nlh->nlmsg_flags = NLM_F_REQUEST | flags;
+
+ struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
+ nfg->nfgen_family = AF_UNSPEC;
+ nfg->version = NFNETLINK_V0;
+ nfg->res_id = htons(queue_num);
+
+ return nlh;
+}
+
+/**
* @}
*/
diff --git a/utils/nfqnl_test.c b/utils/nfqnl_test.c
index 5e76ffe..682f3d7 100644
--- a/utils/nfqnl_test.c
+++ b/utils/nfqnl_test.c
@@ -5,6 +5,7 @@
#include <netinet/in.h>
#include <linux/types.h>
#include <linux/netfilter.h> /* for NF_ACCEPT */
+#include <linux/netfilter/nfnetlink_queue.h>
#include <errno.h>
#include <libnetfilter_queue/libnetfilter_queue.h>