diff options
31 files changed, 1313 insertions, 407 deletions
@@ -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 960e244..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: */ @@ -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,7 +177,11 @@ int main(int argc, char *argv[]) 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) { @@ -158,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)); @@ -176,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 42389aa..33480af 100644 --- a/src/extra/checksum.c +++ b/src/extra/checksum.c @@ -17,6 +17,7 @@ #include <netinet/ip6.h> #include <netinet/tcp.h> +#include <linux/netfilter/nfnetlink_queue.h> #include <libnetfilter_queue/libnetfilter_queue.h> #include "internal.h" @@ -62,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 c03f23f..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 + * 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 + * 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 - * - * Sets the \b transport_header field in \b pktb - * - * Level 4 helper functions need this to be set. + * \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) } /** + * \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 + * + * @{ + */ + +/** * nfq_ip_set_checksum - set IPv4 checksum * \param iph: Pointer to the IPv4 header - * - * \note Call to this function if you modified the IPv4 header to update the - * checksum. + * \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,16 +127,20 @@ void nfq_ip_set_checksum(struct iphdr *iph) } /** + * @} + */ + +/** * nfq_ip_mangle - mangle IPv4 packet buffer * \param pktb: Pointer to user-space network packet buffer - * \param dataoff: Offset to layer 4 header + * \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 *pktb, unsigned int dataoff, @@ -117,21 +154,19 @@ int nfq_ip_mangle(struct pkt_buff *pktb, unsigned int dataoff, return 0; /* fix IP hdr checksum information */ - iph->tot_len = htons(pktb->tail - pktb->network_header); + 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 + * 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 - * - * This function returns the number of bytes written (excluding the - * string-terminating NUL) *assuming sufficient room in the buffer*. - * Read the snprintf manpage for more information about this strange behaviour. + * \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 f685b3b..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,6 +24,17 @@ /** * \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 + * * @{ */ @@ -36,7 +48,7 @@ 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 IPv6 header. */ if (pktlen < sizeof(struct ip6_hdr)) @@ -67,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; } @@ -87,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. */ @@ -107,16 +128,47 @@ 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). diff --git a/src/extra/pktbuff.c b/src/extra/pktbuff.c index c4f3da3..40d2250 100644 --- a/src/extra/pktbuff.c +++ b/src/extra/pktbuff.c @@ -23,12 +23,58 @@ /** * \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. Currently supported families are @@ -38,7 +84,12 @@ * \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 Pointer to a new userspace packet buffer or NULL on failure. * \par Errors @@ -62,37 +113,44 @@ 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: - case AF_INET6: - 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: - case ETH_P_IPV6: - pktb->network_header = pktb->data + ETH_HLEN; - break; - default: - /* This protocol is unsupported. */ - errno = EPROTONOSUPPORT; - free(pktb); - return NULL; - } - break; - } - } return pktb; } @@ -142,21 +200,37 @@ void pktb_free(struct pkt_buff *pktb) * \n * 1. Functions to get values of members of opaque __struct pktbuff__, described * below - * \n + * * 2. Internal functions, described in Module __Internal functions__ * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <libmnl/libmnl.h> +#include <libnetfilter_queue/pktbuff.h> +\endmanonly + * * @{ */ /** - * \defgroup uselessfns Internal functions + * \defgroup do_not_use Internal functions * - * \warning Do not use these functions. Instead, always use the mangle + * 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 + * * @{ */ @@ -192,7 +266,6 @@ void pktb_pull(struct pkt_buff *pktb, unsigned int len) EXPORT_SYMBOL void pktb_put(struct pkt_buff *pktb, unsigned int len) { - pktb->tail += len; pktb->len += len; } @@ -205,7 +278,6 @@ EXPORT_SYMBOL void pktb_trim(struct pkt_buff *pktb, unsigned int len) { pktb->len = len; - pktb->tail = pktb->head + len; } /** @@ -271,26 +343,25 @@ uint8_t *pktb_transport_header(struct pkt_buff *pktb) * @} */ -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; @@ -299,8 +370,10 @@ static int enlarge_pkt(struct pkt_buff *pkt, unsigned int extra) /** * pktb_mangle - adjust contents of a packet * \param pktb Pointer to userspace packet buffer - * \param dataoff Offset to layer 4 header. Specify zero to access layer 3 (IP) - * header (layer 2 for family \b AF_BRIDGE) + * \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 @@ -310,13 +383,13 @@ static int enlarge_pkt(struct pkt_buff *pkt, unsigned int extra) * 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() or nfq_udp_mangle_ipv4(). IPv6 versions are planned. + 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 *pktb, - unsigned int dataoff, + int dataoff, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, @@ -334,7 +407,7 @@ int pktb_mangle(struct pkt_buff *pktb, /* move post-replacement */ memmove(data + match_offset + rep_len, data + match_offset + match_len, - pktb->tail - (pktb->network_header + dataoff + + pktb_tail(pktb) - (pktb->network_header + dataoff + match_offset + match_len)); /* insert data from buffer */ @@ -366,5 +439,17 @@ bool pktb_mangled(const struct pkt_buff *pktb) } /** + * pktb_head_size - get number of bytes needed for a packet buffer + * (control part only) + * \return size of struct pkt_buff + */ + +EXPORT_SYMBOL +size_t pktb_head_size(void) +{ + 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 cd14825..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> @@ -44,11 +45,11 @@ * libnetfilter_queue homepage is: * https://netfilter.org/projects/libnetfilter_queue/ * - * \section deps Dependencies + <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 features Main Features + * <h1>Main Features</h1> * - receiving queued packets from the kernel nfnetlink_queue subsystem * - issuing verdicts and possibly reinjecting altered packets to the kernel * nfnetlink_queue subsystem @@ -70,16 +71,16 @@ * When a queue is full, packets that should have been enqueued are dropped by * kernel instead of being enqueued. * - * \section git Git Tree + * <h1>Git Tree</h1> * The current development version of libnetfilter_queue can be accessed at * https://git.netfilter.org/libnetfilter_queue. * - * \section privs 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 Using libnetfilter_queue - * + * <h1>Using libnetfilter_queue</h1> + * * 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. @@ -87,7 +88,14 @@ * \verbatim gcc -g3 -ggdb -Wall -lmnl -lnetfilter_queue -o nf-queue nf-queue.c \endverbatim - * The doxygen documentation \link LibrarySetup \endlink is Deprecated and + *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. * @@ -96,7 +104,7 @@ gcc -g3 -ggdb -Wall -lmnl -lnetfilter_queue -o nf-queue nf-queue.c * 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. @@ -105,7 +113,7 @@ gcc -g3 -ggdb -Wall -lmnl -lnetfilter_queue -o nf-queue nf-queue.c * 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: * @@ -119,6 +127,9 @@ gcc -g3 -ggdb -Wall -lmnl -lnetfilter_queue -o nf-queue nf-queue.c * (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 */ @@ -147,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) @@ -238,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"); @@ -270,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: * @@ -287,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 + * * @{ */ @@ -318,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() @@ -387,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. @@ -409,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 */ @@ -437,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 + * * @{ */ @@ -446,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) @@ -513,10 +542,10 @@ 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 @@ -651,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: * @@ -830,14 +859,14 @@ 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); @@ -867,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 @@ -886,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. @@ -920,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. @@ -944,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 + * * @{ */ @@ -1065,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 @@ -1081,10 +1119,10 @@ 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(); @@ -1101,7 +1139,7 @@ uint32_t nfq_get_physoutdev(struct nfq_data *nfad) * 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); @@ -1130,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, @@ -1150,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, @@ -1170,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 @@ -1184,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 * @@ -1211,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 @@ -1233,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 @@ -1277,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 * @@ -1315,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 cbf49a6..39fd12d 100644 --- a/src/nlmsg.c +++ b/src/nlmsg.c @@ -27,6 +27,16 @@ /** * \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 + * * @{ */ @@ -139,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 + * * @{ */ @@ -205,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 + * * @{ */ @@ -225,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: @@ -253,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; @@ -261,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) @@ -275,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> |