All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH libnetfilter_queue 0/1] Convert libnetfilter_queue to use entirely libmnl functions
@ 2024-02-13 21:07 Duncan Roe
  2024-02-13 21:07 ` [PATCH libnetfilter_queue 1/1] " Duncan Roe
  0 siblings, 1 reply; 39+ messages in thread
From: Duncan Roe @ 2024-02-13 21:07 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

This libnetfilter_queue does not use any libnfnetlink functions or headers.

The major revision of libnetfilter_queue.so will have to increase because
nfq_open_nfnl() is deleted.

I've never used `./autogen.sh distrib` so have left the libnfnetlink reference.
Can it be removed?

The library provides all libnfnetlink functions mentioned in documentation
or required to implement these functions:

 - nlif_open(), nlif_close(), nlif_fd(), nlif_query(), nlif_catch(),
    nlif_index2name() & nlif_get_ifflags()
 - (macro) __be64_to_cpu
 - nfnl_rcvbufsiz()

In the absence of feedback, I added these former libnfnetlink functions to
libnetfilter_queue and left libmnl unchanged.

As always, the documentation could still be improved. Next to do might be a
refresh of the "Performance" section on the Main page.

Cheers ... Duncan.

Duncan Roe (1):
  Convert libnetfilter_queue to use entirely libmnl functions

 Make_global.am                                |   2 +-
 configure.ac                                  |   1 -
 doxygen/Makefile.am                           |   5 +
 doxygen/build_man.sh                          |  44 +-
 doxygen/doxygen.cfg.in                        |   8 +-
 .../libnetfilter_queue/libnetfilter_queue.h   |  47 +-
 include/libnetfilter_queue/linux_list.h       | 185 +++++
 .../linux_nfnetlink_queue.h                   |   1 -
 libnetfilter_queue.pc.in                      |   1 -
 src/Makefile.am                               |   3 +-
 src/extra/pktbuff.c                           |  27 +-
 src/iftable.c                                 | 391 ++++++++++
 src/libnetfilter_queue.c                      | 696 ++++++++++--------
 src/nlmsg.c                                   |  46 +-
 14 files changed, 1102 insertions(+), 355 deletions(-)
 create mode 100644 include/libnetfilter_queue/linux_list.h
 create mode 100644 src/iftable.c

-- 
2.35.8


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 1/1] Convert libnetfilter_queue to use entirely libmnl functions
  2024-02-13 21:07 [PATCH libnetfilter_queue 0/1] Convert libnetfilter_queue to use entirely libmnl functions Duncan Roe
@ 2024-02-13 21:07 ` Duncan Roe
  2024-02-14 10:47   ` Pablo Neira Ayuso
  0 siblings, 1 reply; 39+ messages in thread
From: Duncan Roe @ 2024-02-13 21:07 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

And no libnfnetlink headers either.
Submitted as a single patch because the first change essentially broke
it until the job was nearly finished.

Summary of changes (sort-of cleaned up git log):

doc: pre-submission updates
-Main page: re-work introductory para
 - mention fail-open earlier
 - Add manonly section "Accessing source online "
 - Add link to examples/nf-queue.c to actually see source code
 - Add link to nfq6.c to actually see source code
 - Elaborate on Eric Leblond's blog
- Queue handling, Library setup, Message parsing functions,
  Printing: Add [nfnl API]
- Verdict helpers, Config helpers,
  Netlink message helper functions: Add [mnl API]
- linux_list.h: Add "alternative approach"
- pktbuff.c:
  - pktb_alloc: mention pktb_setup_raw
  - pktb_setup_raw: re-work and expand
- iftable.c:
 - Point to nfq6.c example of using only libmnl calls instead.
 - Document what to do on interrupted dump.
- libnetfilter_queue.c:
  - Main page (as above)
  - Delete nfq_set_queue_maxlen reference
  - Update nftables command-line queue num verdict to nft QUEUE STATEMENT
  - replace nf-queue.c example with nfq6 (easier to find)
  - re-work nfq_nlmsg_verdict_put_pkt

Use a cast in place of convoluted construct
I.e. when calling list_del() and list_add().
We have a list of struct ifindex_node but the fns want struct list_head
which is at the head of struct ifindex_node.
Also audit counter loops to count downwards (c/w 0 is faster).

Add list_empty() to linux_list.h
And translate kerneldoc comments to doxygen.
Use \note instead of Note:
Add \return to list_empty docn

doc: Cater for doxygen variants w.r.t. #define stmts

doc: Resolve most issues with man page generated from linux_list.h
build_man.sh has extra logic to extract documented macros into the
"Name" line.
doxygen.cfg.in excludes the list_head structure.
doxygen 1.10.0 has a bug which appends ".PP" to macro "Value:" headings.
Fixed after I reported it.

build: Shave some time off build
Modify function mygrep in build_man.sh to use pipes rather than the
temporary files. Saves ~20% elapsed time in a make with no compiles on my
system.

SYNOPSIS of linux_list.h nominates libnetfilter_queue/linux_list.h

doc: Get doxygen to document useful static inline functions
include/libnetfilter_queue/linux_list.h contains static inline list_add and
list_del which mnl programs may wish to use. Make a temporary copy of
linux_list.h with 'static' removed and get doxygen to process that.

Move nlif_*() usage description from libnetfilter_queue.c to iftable.c
Also in iftable.c:
 - Expand usage description to cover nlif_catch.
 - Add SYNOPSIS.
 - Fix some doc typos.
Also in libnetfilter_queue.c:
 - Standardise similar-looking \return lines.
 - Use "an nlif" instead of "a nlif" since nlif pronounciation will always
   start "enn" ("enn liff" or "enn ell eye eff").

doc: Change libnfnetlink reference to libnetfilter_queue
While being about it, clarify surrounding documentation a bit.

include: linux_nfnetlink_queue.h no longer needs
libnfnetlink/linux_nfnetlink.h

Conversion to not use libnfnetlink is complete
No sources include libnfnetlink.h
libnetfilter_queue.so builds
programs in examples and utils do not show libnfnetlink in ldd

Add iftable.c to provide nlif_* functions
Fix all doxygen warnings. Introduce some new doxygen content.
Convert to use libmnl.

Eliminate doxygen warnings from linux_list.h
The warnings concerned prefetch(), LIST_POISON1 & LIST_POISON2.
Remove all 3 macros since they do nothing useful in userspace programs.
Also take a few doxygen comment improvements from 6.6 Linux source.

Add linux_list.h to the doxygen system
Produce web and man pages for list_for_each_entry() &c.

Create include/libnetfilter_queue/linux_list.h
include/libnetfilter_queue/libnetfilter_queue.h includes linux_list.h.
Needed to write replacemants for the nlif_* functions,
or for dierct use by mnl-api programs.

Convert remaining nfq_* functions to use libmnl
Converted: nfq_set_verdict2(), nfq_set_verdict_batch2(),
	   nfq_set_verdict_mark(), nfq_get_nfmark() [again] &
	   nfq_get_skbinfo().

Eliminate calls to nfnl_build_nfa_iovec() & nfnl_sendiov

Clarify where nfqnl_msg_packet_hdr structure is defined
I.e. not in libnetfilter_queue.h

Convert nfq_fd()
Also update build of utils/nfqnl_test to ignore unresolved symbols
and document nfq_nfnlh().

Rename element `id` of struct nfq_q_handle to `queue_num`
This leaves `id` as always referring to packet id.
`queue_num` is also used in other sources.

Remove libnfnetlink from the build
Programs using nfnl-api functions must build with -lnfnetlink.
Programs using mnl-api functions no longer show libnfnetlink in ldd.

Incorporate nfnl_rcvbufsiz()

Remove `struct nfnl_subsys_handle *nfnlssh` from `struct nfq_handle`

Convert nfq_close()

Convert non-data verdict functions
Updated:
 include/libnetfilter_queue/libnetfilter_queue.h:
 - Fix name in 1st line
 - Change name of guardian #define to the conventional one
 - Flag line to be removed (when we can)
 src/libnetfilter_queue.c:
 - include libmnl.h
 - Use real Linux headers
 - __set_verdict() uses mnl_socket_sendto() if no packed data

Convert more message parsing functioms
Functions now using libmnl exclusively: nfq_get_msg_packet_hdr(),
nfq_get_nfmark(), nfq_get_timestamp(), nfq_get_indev(),
nfq_get_physindev(), nfq_get_outdev(), nfq_get_physoutdev(),
nfqnl_msg_packet_hw(), nfq_get_uid() & nfq_get_gid().

About to convert / redo all nfq_get_ functions
Functions now using libmnl exclusively: nfq_handle_packet(),
nfq_get_secctx() & nfq_get_payload().
The opaque struct nfq_data is now an array of struct nlattr instead of
struct nfattr.
The difference is: nlattr starts at 0 while nfattr starts at 1.

Delete nfq_open_nfnl() and __nfq_rcv_pkt()
nfq_open_nfnl was the last user of static __nfq_rcv_pkt and was slated
for removal so delete both of them.

Convert nfq_set_queue_flags() & nfq_set_queue_maxlen() to use libmnl

Introduce a static function to check response from using NLM_F_ACK
Code uses nfq_query() where it previously used nfnl_query().
nfq_query() takes 2 extra args being a buffer / buffer size pair which
the caller can also use for sending.
(nfq_query() used to call nfnl_catch() which declared that buffer).
THIS FUNCTION WILL BE REPLACED by nfq_socket_sendto() if it ever makes
it out of patchwork.

Convert nfq_set_mode(), nfq_bind_pf() & nfq_unbind_pf() to use libmnl
Also remove nfq_errno (incomplete project, never documented).
Main change is to static function __build_send_cfg_msg().

Convert nfq_open() to use libmnl

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 Make_global.am                                |   2 +-
 configure.ac                                  |   1 -
 doxygen/Makefile.am                           |   5 +
 doxygen/build_man.sh                          |  44 +-
 doxygen/doxygen.cfg.in                        |   8 +-
 .../libnetfilter_queue/libnetfilter_queue.h   |  47 +-
 include/libnetfilter_queue/linux_list.h       | 185 +++++
 .../linux_nfnetlink_queue.h                   |   1 -
 libnetfilter_queue.pc.in                      |   1 -
 src/Makefile.am                               |   3 +-
 src/extra/pktbuff.c                           |  27 +-
 src/iftable.c                                 | 391 ++++++++++
 src/libnetfilter_queue.c                      | 696 ++++++++++--------
 src/nlmsg.c                                   |  46 +-
 14 files changed, 1102 insertions(+), 355 deletions(-)
 create mode 100644 include/libnetfilter_queue/linux_list.h
 create mode 100644 src/iftable.c

diff --git a/Make_global.am b/Make_global.am
index 91da5da..4d8a58e 100644
--- a/Make_global.am
+++ b/Make_global.am
@@ -1,2 +1,2 @@
-AM_CPPFLAGS = -I${top_srcdir}/include ${LIBNFNETLINK_CFLAGS} ${LIBMNL_CFLAGS}
+AM_CPPFLAGS = -I${top_srcdir}/include ${LIBMNL_CFLAGS}
 AM_CFLAGS = -Wall ${GCC_FVISIBILITY_HIDDEN}
diff --git a/configure.ac b/configure.ac
index 7359fba..ba7b15f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -42,7 +42,6 @@ case "$host" in
 esac
 
 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],
diff --git a/doxygen/Makefile.am b/doxygen/Makefile.am
index 68be963..4934e8e 100644
--- a/doxygen/Makefile.am
+++ b/doxygen/Makefile.am
@@ -2,17 +2,22 @@ if HAVE_DOXYGEN
 
 doc_srcs = $(top_srcdir)/src/libnetfilter_queue.c\
            $(top_srcdir)/src/nlmsg.c\
+           $(top_srcdir)/src/iftable.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)/include/libnetfilter_queue/linux_list.h\
            $(top_srcdir)/src/extra/icmp.c
 
 doxyfile.stamp: $(doc_srcs) Makefile build_man.sh
 	rm -rf html man
+	sed '/^static inline [^ ]* [^_]/s/static //' \
+	  $(top_srcdir)/include/libnetfilter_queue/linux_list.h > linux_list.h
 	doxygen doxygen.cfg >/dev/null
+	rm linux_list.h
 
 if BUILD_MAN
 	$(abs_top_srcdir)/doxygen/build_man.sh libnetfilter_queue libnetfilter_queue.c
diff --git a/doxygen/build_man.sh b/doxygen/build_man.sh
index 7eab8fa..d3bd748 100755
--- a/doxygen/build_man.sh
+++ b/doxygen/build_man.sh
@@ -7,6 +7,7 @@
 # Args: none or 2 being man7 page name & relative path of source with \mainpage
 
 declare -A renamed_page
+no_macroRI=maybe
 
 main(){
   set -e
@@ -84,7 +85,12 @@ post_process(){
 
 make_man7(){
   popd >/dev/null
-  target=$(grep -Ew INPUT doxygen.cfg | rev | cut -f1 -d' ' | rev)/$2
+
+  # This grep command works for multiple directories on the INPUT line,
+  # as long as the directory containing the source with the main page
+  # comes first.
+  target=/$(grep -Ew INPUT doxygen.cfg | cut -f2- -d/ | cut -f1 -d' ')/$2
+
   mypath=$(dirname $0)
 
   # Build up temporary source in temp.c
@@ -245,10 +251,18 @@ fix_name_line(){
   mygrep "^\\.SH \"Function Documentation" $target
   head -n$linnum $target >$fileC
 
+  # Different versions of doxygen present macros and functions differently.
+  # Some versions have .RI lines for macros then functions.
+  # Some versions have .SS lines for macros instead of .RI.
+  # All versions (so far) have .SS lines for macros after all .RI lines.
+  # Look for #define in .RI lines and look for .SS lines if none found
+  # to cater for either scenario.
+
   while :
-  do mygrep ^\\.RI $fileC
+  do mygrep2 ^\\.RI $fileC
     [ $linnum -ne 0 ] || break
     # Discard this entry
+    ! grep -E -q '#define' $fileG || no_macroRI=
     tail -n+$(($linnum + 1)) $fileC >$fileB
     cp $fileB $fileC
 
@@ -256,6 +270,16 @@ fix_name_line(){
     [ -z "$all_funcs" ] && all_funcs=$func ||\
       all_funcs="$all_funcs, $func"
   done
+
+  [ -z "$no_macroRI" ] ||
+  while :
+  do mygrep2 '^\.SS "#define' $fileC
+    [ $linnum -ne 0 ] || break
+    tail -n+$(($linnum + 1)) $fileC >$fileB
+    cp $fileB $fileC
+    func=$(cat $fileG | cut -f3 -d' ' | cut -f1 -d\()
+    [ -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" ")
@@ -299,15 +323,19 @@ delete_lines(){
 }
 
 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
+  linnum=$(grep -En "$1" $2 2>/dev/null | head -n1 | cut -f1 -d:)
+  [ $linnum ] || linnum=0
+}
+
+# mygrep2 copies found line to $fileG. Only fix_name_line() needs this.
+# Using mygrep everywhere else gives a measurable CPU saving.
+mygrep2(){
+  linnum=$(grep -En "$1" $2 2>/dev/null | head -n1 | tee $fileG | cut -f1 -d:)
+  [ $linnum ] || linnum=0
 }
 
 make_temp_files(){
-  temps="A B C G H"
+  temps="A B C G"
   for i in $temps
   do declare -g file$i=$(mktemp)
   done
diff --git a/doxygen/doxygen.cfg.in b/doxygen/doxygen.cfg.in
index 97174ff..d719105 100644
--- a/doxygen/doxygen.cfg.in
+++ b/doxygen/doxygen.cfg.in
@@ -5,14 +5,18 @@ ABBREVIATE_BRIEF       =
 FULL_PATH_NAMES        = NO
 TAB_SIZE               = 8
 OPTIMIZE_OUTPUT_FOR_C  = YES
-INPUT                  = @abs_top_srcdir@/src
-FILE_PATTERNS          = *.c
+INPUT                  = @abs_top_srcdir@/src .
+FILE_PATTERNS          = *.c linux_list.h
 RECURSIVE              = YES
 EXCLUDE_SYMBOLS        = EXPORT_SYMBOL \
                          tcp_word_hdr \
                          nfq_handle \
                          nfq_data \
                          nfq_q_handle \
+                         nfnl_handle \
+                         ifindex_node \
+                         list_head \
+                         nlif_handle \
                          tcp_flag_word
 EXAMPLE_PATTERNS       =
 INPUT_FILTER           = "sed 's/EXPORT_SYMBOL//g'"
diff --git a/include/libnetfilter_queue/libnetfilter_queue.h b/include/libnetfilter_queue/libnetfilter_queue.h
index f7e68d8..851f2ca 100644
--- a/include/libnetfilter_queue/libnetfilter_queue.h
+++ b/include/libnetfilter_queue/libnetfilter_queue.h
@@ -1,4 +1,4 @@
-/* libnfqnetlink.h: Header file for the Netfilter Queue library.
+/* libnetfilter_queue.h: Header file for the Netfilter Queue library.
  *
  * (C) 2005 by Harald Welte <laforge@gnumonks.org>
  *
@@ -10,12 +10,13 @@
  * of the GNU General Public License, incorporated herein by reference.
  */
 
-#ifndef __LIBCTNETLINK_H
-#define __LIBCTNETLINK_H
+#ifndef __LIBNETFILTER_QUEUE_H
+#define __LIBNETFILTER_QUEUE_H
 
 #include <sys/time.h>
-#include <libnfnetlink/libnfnetlink.h>
+#include <libmnl/libmnl.h>
 
+#include <libnetfilter_queue/linux_list.h>
 #include <libnetfilter_queue/linux_nfnetlink_queue.h>
 
 #ifdef __cplusplus
@@ -25,8 +26,7 @@ extern "C" {
 struct nfq_handle;
 struct nfq_q_handle;
 struct nfq_data;
-
-extern int nfq_errno;
+struct nlif_handle;
 
 extern struct nfnl_handle *nfq_nfnlh(struct nfq_handle *h);
 extern int nfq_fd(struct nfq_handle *h);
@@ -35,6 +35,8 @@ typedef int  nfq_callback(struct nfq_q_handle *gh, struct nfgenmsg *nfmsg,
 		       struct nfq_data *nfad, void *data);
 
 
+extern unsigned int nfnl_rcvbufsiz(const struct nfnl_handle *h,
+				   unsigned int size);
 extern struct nfq_handle *nfq_open(void);
 extern struct nfq_handle *nfq_open_nfnl(struct nfnl_handle *nfnlh);
 extern int nfq_close(struct nfq_handle *h);
@@ -153,8 +155,41 @@ 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);
 
+/*
+ * Network Interface Table API
+ */
+
+#ifndef IFNAMSIZ
+#define IFNAMSIZ 16
+#endif
+
+struct nlif_handle *nlif_open(void);
+void nlif_close(struct nlif_handle *orig);
+int nlif_fd(struct nlif_handle *nlif_handle);
+int nlif_query(struct nlif_handle *nlif_handle);
+int nlif_catch(struct nlif_handle *nlif_handle);
+int nlif_index2name(struct nlif_handle *nlif_handle, unsigned int if_index, char *name);
+int nlif_get_ifflags(const struct nlif_handle *h, unsigned int index, unsigned int *flags);
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
 
+/*
+ * __be46 stuff - should be in libmnl.h maybe?
+ */
+
+#include <byteswap.h>
+#if __BYTE_ORDER == __BIG_ENDIAN
+#  ifndef __be64_to_cpu
+#  define __be64_to_cpu(x)	(x)
+#  endif
+# else
+# if __BYTE_ORDER == __LITTLE_ENDIAN
+#  ifndef __be64_to_cpu
+#  define __be64_to_cpu(x)	__bswap_64(x)
+#  endif
+# endif
+#endif
+
 #endif	/* __LIBNFQNETLINK_H */
diff --git a/include/libnetfilter_queue/linux_list.h b/include/libnetfilter_queue/linux_list.h
new file mode 100644
index 0000000..b955609
--- /dev/null
+++ b/include/libnetfilter_queue/linux_list.h
@@ -0,0 +1,185 @@
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+#include <stddef.h>
+
+#undef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+/*
+ * This is a cut-down copy of libnfnetlink/include/linux_list.h which is itself
+ * an old snapshot of linux/include/linux/list.h.
+ * This file only contains what we use.
+ *
+ * 2024-01-27 12:45:41 +1100 duncan_roe@optusnet.com.au
+ * LIST_POISONx doesn't really work for user space - just use NULL
+ *
+ * 2024-01-27 18:16:51 +1100 duncan_roe@optusnet.com.au
+ * I can't see how the prefetch() calls do any good so remove them
+ * and #define of prefetch
+ *
+ * 2024-01-27 18:53:46 +1100 duncan_roe@optusnet.com.au
+ * Take a few doxygen comment improvements from 6.6 Linux source
+ */
+
+/**
+ * \defgroup List Circular doubly linked list implementation
+ *
+ * Unlike file units (which are re-used), network interface indicies
+ * increase monotonically as they are brought up and down.
+ *
+ * To keep memory usage predictable as indices increase,
+ * the nlif_* functions keep their data in a circular list
+ * (in fact a number of lists, to minimise search times).
+ * <br>An alternative approach, which is faster to look up, is to have an array
+ * of pointers indexed by network interface index. Each unused interface index
+ * wastes \b sizeof(void*) in this setup.
+ * \ref nfq6"nfq6.c" demonstrates both approaches:
+ * search the source for `tests[24]` (numerous occurrences).
+ *
+ * \manonly
+.SH SYNOPSIS
+.nf
+\fB
+#include <libnetfilter_queue/libnetfilter_queue.h>
+\endmanonly
+ * @{
+ */
+
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * \param ptr:	the pointer to the member.
+ * \param type:	the type of the container struct this is embedded in.
+ * \param member:	the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({			\
+	typeof( ((type *)0)->member ) *__mptr = (ptr);	\
+	(type *)( (char *)__mptr - offsetof(type,member) );})
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+/**
+ * \struct list_head
+ * Link to adjacent members of the circular list
+ * \note Each member of a list must start with this structure
+ * (containing structures OK)
+ * \var list_head::next
+ * pointer to the next list member
+ * \var list_head::prev
+ * pointer to the previous list member
+ */
+
+struct list_head {
+	struct list_head *next, *prev;
+};
+
+/**
+ * INIT_LIST_HEAD - Initialise first member of a new list
+ * \param ptr the &struct list_head pointer.
+ */
+#define INIT_LIST_HEAD(ptr) do { \
+	(ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+			      struct list_head *prev,
+			      struct list_head *next)
+{
+	next->prev = new;
+	new->next = next;
+	new->prev = prev;
+	prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * \param new: new entry to be added
+ * \param head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head, head->next);
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * \param ptr:	the &struct list_head pointer.
+ * \param type:	the type of the struct this is embedded in.
+ * \param member:	the name of the list_head within the struct.
+ */
+#define list_entry(ptr, type, member) \
+	container_of(ptr, type, member)
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+	next->prev = prev;
+	prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * \param entry: the element to delete from the list.
+ * \note
+ * list_empty() on **entry** does not return true after this, **entry** is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	entry->next = NULL;
+	entry->prev = NULL;
+}
+
+/**
+ * list_for_each_entry	-	iterate over list of given type
+ * \param pos:	the type * to use as a loop cursor.
+ * \param head:	the head for your list.
+ * \param member:	the name of the list_head within the struct.
+ */
+#define list_for_each_entry(pos, head, member)				\
+	for (pos = list_entry((head)->next, typeof(*pos), member);	\
+	     &pos->member != (head); 					\
+	     pos = list_entry(pos->member.next, typeof(*pos), member))	\
+
+/**
+ * list_empty - tests whether a list is empty
+ * \param head: the list to test.
+ * \return 1 if list is empty, 0 otherwise
+ */
+static inline int list_empty(const struct list_head *head)
+{
+	return head->next == head;
+}
+
+/**
+ * @}
+ */
+
+#endif
diff --git a/include/libnetfilter_queue/linux_nfnetlink_queue.h b/include/libnetfilter_queue/linux_nfnetlink_queue.h
index 6844270..f8c33cb 100644
--- a/include/libnetfilter_queue/linux_nfnetlink_queue.h
+++ b/include/libnetfilter_queue/linux_nfnetlink_queue.h
@@ -8,7 +8,6 @@
 #endif
 
 #include <linux/types.h>
-#include <libnfnetlink/linux_nfnetlink.h>
 
 enum nfqnl_msg_types {
 	NFQNL_MSG_PACKET,		/* packet from kernel to userspace */
diff --git a/libnetfilter_queue.pc.in b/libnetfilter_queue.pc.in
index 9c6c2c4..962c686 100644
--- a/libnetfilter_queue.pc.in
+++ b/libnetfilter_queue.pc.in
@@ -12,5 +12,4 @@ Version: @VERSION@
 Requires: libnfnetlink
 Conflicts:
 Libs: -L${libdir} -lnetfilter_queue
-Libs.private: @LIBNFNETLINK_LIBS@
 Cflags: -I${includedir}
diff --git a/src/Makefile.am b/src/Makefile.am
index 079853e..e5e1d66 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -30,6 +30,7 @@ libnetfilter_queue_la_LDFLAGS = -Wc,-nostartfiles \
 				-version-info $(LIBVERSION)
 libnetfilter_queue_la_SOURCES = libnetfilter_queue.c	\
 				nlmsg.c			\
+				iftable.c		\
 				extra/checksum.c	\
 				extra/icmp.c		\
 				extra/ipv6.c		\
@@ -38,4 +39,4 @@ libnetfilter_queue_la_SOURCES = libnetfilter_queue.c	\
 				extra/pktbuff.c		\
 				extra/udp.c
 
-libnetfilter_queue_la_LIBADD  = ${LIBNFNETLINK_LIBS} ${LIBMNL_LIBS}
+libnetfilter_queue_la_LIBADD  = ${LIBMNL_LIBS}
diff --git a/src/extra/pktbuff.c b/src/extra/pktbuff.c
index 40d2250..833456c 100644
--- a/src/extra/pktbuff.c
+++ b/src/extra/pktbuff.c
@@ -77,6 +77,13 @@ static void pktb_setup_metadata(struct pkt_buff *pktb, void *pkt_data,
 
 /**
  * pktb_alloc - allocate a new packet buffer
+ * \note
+ * pktb_alloc copies the IP datagram to the (<b>calloc</b>'d) packet buffer.
+ * This is essential if your application needs to hold on to multiple
+ * datagrams.
+ * <br>If your application processes each datagram as it arrives, you can avoid
+ * the datagram copy by calling pktb_setup_raw() instead.
+ *
  * \param family Indicate what family. Currently supported families are
  * AF_BRIDGE, AF_INET & AF_INET6.
  * \param data Pointer to packet data
@@ -131,11 +138,19 @@ struct pkt_buff *pktb_alloc(int family, void *data, size_t len, size_t extra)
  * \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.
+ * Use this function to set up a packet buffer in a memory area of size
+ * pktb_head_size(). The created packet buffer addresses the packet data
+ * in the buffer filled by <b>mnl_socket_recvfrom</b>() in the mainline.
+ * This function avoids a packet copy and uses less memory than the alternative
+ * pktb_alloc() interface.
+ *
+ * \note
+ * The extra space available for mangling is the buffer size minus the number of
+ * bytes returned by <b>mnl_socket_recvfrom</b>().
+ * The mainline needs to pass this datum to the callback,
+ * since that is where pktb_setup_raw() is called.
+ * The \b data argument of <b>mnl_cb_run</b>() is available:
+ * see \ref nfq6"nfq6.c" (search for `tests[7]`).
  *
  * \return Pointer to a new userspace packet buffer or NULL on failure.
  * \par Errors
@@ -201,7 +216,7 @@ void pktb_free(struct pkt_buff *pktb)
  * 1. Functions to get values of members of opaque __struct pktbuff__, described
  * below
  *
- * 2. Internal functions, described in Module __Internal functions__
+ * 2. Internal functions, described in Topic __Internal functions__
  *
  * \manonly
 .SH SYNOPSIS
diff --git a/src/iftable.c b/src/iftable.c
new file mode 100644
index 0000000..d0ee7dd
--- /dev/null
+++ b/src/iftable.c
@@ -0,0 +1,391 @@
+/* iftable - table of network interfaces
+ *
+ * (C) 2004 by Astaro AG, written by Harald Welte <hwelte@astaro.com>
+ * (C) 2008 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2024 by Duncan Roe <duncan_roe@optusnet.com.au>
+ *
+ * This software is Free Software and licensed under GNU GPLv2+.
+ */
+
+/* IFINDEX handling */
+
+#include <time.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/netfilter/nfnetlink_queue.h>
+#include <libnetfilter_queue/libnetfilter_queue.h>
+
+#include "internal.h"
+
+#define NUM_NLIF_BITS 4
+#define NUM_NLIF_ENTRIES (1 << NUM_NLIF_BITS)
+#define NLIF_ENTRY_MASK (NUM_NLIF_ENTRIES -1)
+
+/**
+ * \defgroup iftable Functions to manage a table of network interfaces
+ * These functions maintain a database of the name and flags of each
+ * network interface.
+ *
+ * mnl API programs may instead use
+ * [libmnl](https://netfilter.org/projects/libmnl/doxygen/html/)
+ * calls directly to maintain an
+ * interface table with more (or less) data points, e.g. with MTU.
+ * <br>\ref nfq6"nfq6.c" has example code:
+ * search the source for `tests[24]` (numerous occurrences).
+ * The <b><tt>!tests[24]</tt></b> code uses circular
+ * lists like the functions described here.
+ *
+ * Programs access an nlif database through an opaque __struct nlif_handle__
+ * interface resolving handle. Call nlif_open() to get a handle:
+ * \verbatim
+	h = nlif_open();
+	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);
+\endverbatim
+ * libnetfilter_queue 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 and netilter_queue
+ * file descriptors.
+ * E.g. use nlif_fd() to get the nlif file descriptor, then give this fd to
+ * **poll()** as in this code snippet (error-checking removed):
+ * \verbatim
+	if_fd = nlif_fd(h);
+	qfd = mnl_socket_get_fd(nl); // For mnl API or ...
+	qfd = nfq_fd(qh);            // For nfnl API
+	. . .
+	fds[0].fd = ifd;
+	fds[0].events = POLLIN;
+	fds[1].fd = qfd;
+	fds[1].events = POLLIN;
+	for(;;)
+	{
+		poll((struct pollfd *)&fds, 2, -1);
+		if (fds[0].revents & POLLIN)
+			nlif_catch(h);
+\endverbatim
+ * Don't forget to close the handler when you don't need the feature anymore:
+ * \verbatim
+	nlif_close(h);
+\endverbatim
+ *
+ * \manonly
+.SH SYNOPSIS
+.nf
+\fB
+#include <libnetfilter_queue/libnetfilter_queue.h>
+\endmanonly
+ * @{
+ */
+
+struct ifindex_node {
+	struct list_head head;
+
+	uint32_t index;
+	uint32_t type;
+	uint32_t flags;
+	char name[IFNAMSIZ];
+};
+
+struct nlif_handle {
+	struct list_head ifindex_hash[NUM_NLIF_ENTRIES];
+	struct mnl_socket *nl;
+	unsigned int portid;
+};
+
+/*
+ * find_ifindex_node - find node by index
+ */
+
+static struct ifindex_node *find_ifindex_node(uint32_t index,
+    struct nlif_handle *h)
+{
+	struct ifindex_node *result;
+	uint32_t hash;
+
+	if (!index)
+		goto err;
+
+	hash = index & NLIF_ENTRY_MASK;
+	list_for_each_entry(result, &h->ifindex_hash[hash], head) {
+		if (result->index == index)
+			return result;
+	}
+err:
+	errno = ENOENT;
+	return NULL;
+}
+
+/*
+ * data_cb - callback for rtnetlink messages
+ *           caller will put nlif_handle in data
+ */
+
+static int data_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct ifinfomsg *ifi_msg = mnl_nlmsg_get_payload(nlh);
+	struct nlif_handle *h = data;
+	struct nlattr *attr;
+	struct ifindex_node *this = find_ifindex_node(ifi_msg->ifi_index, h);
+
+	if (nlh->nlmsg_type != RTM_NEWLINK && nlh->nlmsg_type != RTM_DELLINK) {
+		errno = EPROTO;
+		return MNL_CB_ERROR;
+	}
+
+	/* RTM_DELLINK is simple, do it first for less indenting */
+	if (nlh->nlmsg_type == RTM_DELLINK) {
+		/*
+		 * rtnetlink.c used list_for_each_entry_safe()
+		 * and removed all entries with the wanted index number.
+		 * But we know there can be at max one entry,
+		 * so delete it if we have one.
+		 */
+		if (this) {
+			list_del((struct list_head *)this);
+			free(this);
+		}
+	return MNL_CB_OK;
+	}
+
+	if (!this) {
+		uint32_t hash = ifi_msg->ifi_index & NLIF_ENTRY_MASK;
+
+		this = calloc(1, sizeof(*this));
+		if (!this)
+			return MNL_CB_ERROR;
+		this->index = ifi_msg->ifi_index;
+		this->type = ifi_msg->ifi_type;
+		this->flags = ifi_msg->ifi_flags;
+		list_add((struct list_head *)this, &h->ifindex_hash[hash]);
+	}
+
+	mnl_attr_for_each(attr, nlh, sizeof(*ifi_msg)) {
+		/* All we want is the interface name */
+		if (mnl_attr_get_type(attr) == IFLA_IFNAME) {
+			if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
+				perror("mnl_attr_validate");
+				return MNL_CB_ERROR;
+			}
+			strcpy(this->name, mnl_attr_get_str(attr));
+			break;
+		}
+	}
+	return MNL_CB_OK;
+}
+
+/**
+ * nlif_index2name - get the name for an ifindex
+ *
+ * \param h pointer to nlif_handle created by nlif_open()
+ * \param index ifindex to be resolved
+ * \param name interface name, pass a buffer of IFNAMSIZ size
+ * \return -1 on error, 1 on success
+ */
+EXPORT_SYMBOL
+int nlif_index2name(struct nlif_handle *h,
+		    unsigned int index,
+		    char *name)
+{
+	unsigned int hash;
+	struct ifindex_node *this;
+
+	if (index == 0) {
+		strcpy(name, "*");
+		return 1;
+	}
+
+	hash = index & 0xF;
+	list_for_each_entry(this, &h->ifindex_hash[hash], head) {
+		if (this->index == index) {
+			strcpy(name, this->name);
+			return 1;
+		}
+	}
+
+	errno = ENOENT;
+	return -1;
+}
+
+/**
+ * nlif_get_ifflags - get the flags for an ifindex
+ *
+ * \param h pointer to nlif_handle created by nlif_open()
+ * \param index ifindex to be resolved
+ * \param flags pointer to variable used to store the interface flags
+ * \return -1 on error, 1 on success
+ */
+EXPORT_SYMBOL
+int nlif_get_ifflags(const struct nlif_handle *h,
+		     unsigned int index,
+		     unsigned int *flags)
+{
+	unsigned int hash;
+	struct ifindex_node *this;
+
+	if (!index)
+		goto err;
+
+	hash = index & 0xF;
+	list_for_each_entry(this, &h->ifindex_hash[hash], head) {
+		if (this->index == index) {
+			*flags = this->flags;
+			return 1;
+		}
+	}
+err:
+	errno = ENOENT;
+	return -1;
+}
+
+/**
+ * nlif_open - initialize interface table
+ *
+ * Open a netlink socket and initialise interface table.
+ * Call this before any other nlif_* function
+ *
+ * \return NULL on error, else valid pointer to an nlif_handle structure
+ */
+EXPORT_SYMBOL
+struct nlif_handle *nlif_open(void)
+{
+	int i;
+	struct nlif_handle *h;
+
+	h = calloc(1,  sizeof(struct nlif_handle));
+	if (h == NULL)
+		goto err;
+
+	for (i = NUM_NLIF_ENTRIES - 1; i>= 0; i--)
+		INIT_LIST_HEAD(&h->ifindex_hash[i]);
+
+	h->nl = mnl_socket_open(NETLINK_ROUTE);
+	if (!h->nl)
+		goto err_free;
+
+	if (mnl_socket_bind(h->nl, RTMGRP_LINK, MNL_SOCKET_AUTOPID) < 0)
+		goto err_close;
+	h->portid = mnl_socket_get_portid(h->nl);
+
+	return h;
+
+err_close:
+	mnl_socket_close(h->nl);
+err_free:
+	free(h);
+err:
+	return NULL;
+}
+
+/**
+ * nlif_close - free all resources associated with the interface table
+ *
+ * \param h pointer to nlif_handle created by nlif_open()
+ */
+EXPORT_SYMBOL
+void nlif_close(struct nlif_handle *h)
+{
+	int i;
+	struct list_head *tmp;
+
+	mnl_socket_close(h->nl);
+
+	for (i = NUM_NLIF_ENTRIES - 1; i>= 0; i--) {
+		while (h->ifindex_hash[i].next != &h->ifindex_hash[i]) {
+			tmp = h->ifindex_hash[i].next;
+			list_del(tmp);
+			free(tmp);
+		}
+	}
+
+	free(h);
+}
+
+/**
+ * nlif_catch - receive message from netlink and update interface table
+ *
+ * \param h pointer to nlif_handle created by nlif_open()
+ * \return 0 if OK
+ */
+EXPORT_SYMBOL
+int nlif_catch(struct nlif_handle *h)
+{
+	char buf[MNL_SOCKET_DUMP_SIZE];
+	int ret;
+
+	if (!h->nl)                /* The old library had this test */
+		return -1;
+
+	ret = mnl_socket_recvfrom(h->nl, buf, sizeof buf);
+	if (ret == -1)
+		return ret;
+	return mnl_cb_run(buf, ret, 0, h->portid, data_cb, h) == -1 ? -1 : 0;
+}
+
+/**
+ * nlif_query - request a dump of interfaces available in the system
+ * \param h: pointer to a valid nlif_handler
+ * \return -1 on error with errno set, else >=0
+ * \par Errors
+ * __EINTR__ Dump was interrupted
+ * (by creation or deletion of a network interface).
+ * <br> Caller must call nlif_close() and nlif_open() before re-trying
+ */
+EXPORT_SYMBOL
+int nlif_query(struct nlif_handle *h)
+{
+	char buf[MNL_SOCKET_DUMP_SIZE];
+	struct nlmsghdr *nlh;
+	uint32_t seq;
+	int ret;
+	struct rtgenmsg *rt;
+
+	nlh = mnl_nlmsg_put_header(buf);
+	nlh->nlmsg_type = RTM_GETLINK;
+	nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+	nlh->nlmsg_seq = seq = time(NULL);
+	rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
+	rt->rtgen_family = AF_PACKET;
+	if (mnl_socket_sendto(h->nl, nlh, nlh->nlmsg_len) < 0)
+		return -1;
+	ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf));
+	while (ret > 0) {
+		ret = mnl_cb_run(buf, ret, seq, h->portid, data_cb, h);
+		if (ret <= MNL_CB_STOP)
+			break;
+		ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf));
+	}
+	return ret;
+}
+
+/**
+ * nlif_fd - get file descriptor for the netlink socket
+ *
+ * \param h pointer to nlif_handle created by nlif_open()
+ * \return socket fd or -1 on error
+ */
+EXPORT_SYMBOL
+int nlif_fd(struct nlif_handle *h)
+{
+	return h->nl? mnl_socket_get_fd(h->nl) : -1;
+}
+
+/**
+ * @}
+ */
diff --git a/src/libnetfilter_queue.c b/src/libnetfilter_queue.c
index bf67a19..c9f8377 100644
--- a/src/libnetfilter_queue.c
+++ b/src/libnetfilter_queue.c
@@ -19,6 +19,7 @@
  *  2006-01-23 Andreas Florath <andreas@florath.net>
  *	Fix __set_verdict() that it can now handle payload.
  */
+//#define __LIBNFNETLINK_H
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -31,7 +32,14 @@
 #include <sys/socket.h>
 #include <linux/netfilter/nfnetlink_queue.h>
 
-#include <libnfnetlink/libnfnetlink.h>
+/* Use real headers since libnfnetlink is going away. */
+/* nfq_pkt_parse_attr_cb only knows attribates up to NFQA_SECCTX */
+/* so won't try to validate them but will store them. */
+/* mnl API programs will then be able to access them. */
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_compat.h>
+
+#include <libmnl/libmnl.h>
 #include <libnetfilter_queue/libnetfilter_queue.h>
 #include "internal.h"
 
@@ -41,12 +49,49 @@
  * 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
  * replaces the old ip_queue / libipq mechanism (withdrawn in kernel 3.5).
+ * \n
+ * libnetfilter_queue in fact offers 2 different APIs:
+ *   -# The modern API which provides helper functions for some
+ * libmnl functions. Users call other libmnl functions directly.
+ * The documentation calls this the **mnl** API.
+ *   -# An older API which provided wrappers for all relevant
+ * functions in the old libnfnetlink library.
+ * This API now also uses libmnl calls.
+ * The documentation calls this the **nfnl** API.
+ *
+ * When developing new software, you have to choose which of the mnl and nfnl
+ * APIs to use. Of the two, the nfnl API is higher level but restricts you to
+ * what the API provides: you can't, for instance, send a packet to another
+ * queue.
+ *
+ * With the mnl API, you have access to everything.
+ *
+ * Function groups specific to an API are indicated as such in
+ * the \e Topics (formerly <i>Modules</i>) tab in the HTML documentation;
+ * in man pages the indication (if any) is at the end of the \b NAME line.
+ *
+ * Whichever API you choose, the higher-level functions are available.
+ * These include everything to do with mangling
+ * (packet buffer and protocol helpers).
+ *
+ * libnfnetlink itself is deprecated and will eventually be removed.
  *
  * libnetfilter_queue homepage is:
  * 	https://netfilter.org/projects/libnetfilter_queue/
  *
- <h1>Dependencies</h1>
- * libnetfilter_queue requires libmnl, libnfnetlink and a kernel that includes
+ * \manonly
+.SS "Accessing source online "
+.PP
+The HTML documentation gives immediate access to the source code of each
+documented function\&. If you would like to use this but your Linux
+distribution does not include the HTML documentation, you can access it
+online by going to the homepage above then clicking the \fIdoxygen\fP link under
+the \fBDocumentation\fP heading\&.
+.PP
+\endmanonly
+ *
+ * <h1>Dependencies</h1>
+ * libnetfilter_queue requires libmnl and a kernel that includes
  * the Netfilter NFQUEUE over NFNETLINK interface (i.e. 2.6.14 or later).
  *
  * <h1>Main Features</h1>
@@ -69,7 +114,9 @@
  * needed information is the packet id.
  *
  * When a queue is full, packets that should have been enqueued are dropped by
- * kernel instead of being enqueued.
+ * the kernel instead of being enqueued. You can instead instruct the kernel to
+ * accept such packets by configuring the
+ * \ref failopen"fail-open" option.
  *
  * <h1>Git Tree</h1>
  * The current development version of libnetfilter_queue can be accessed at
@@ -83,33 +130,33 @@
  *
  * 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:
+ * \b examples/nf-queue.c
+ * https://git.netfilter.org/libnetfilter_queue/tree/examples/nf-queue.c
+ * <br>Simple compile line:
  * \verbatim
-gcc -g3 -ggdb -Wall -lmnl -lnetfilter_queue -o nf-queue nf-queue.c
+gcc -g3 -gdwarf-4 -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
+ * Somewhat outdated but still providing insight into
  * libnetfilter_queue usage is the following
  * article:
- *  https://home.regit.org/netfilter-en/using-nfqueue-and-libnetfilter_queue/
+ *  https://home.regit.org/netfilter-en/using-nfqueue-and-libnetfilter_queue/,
+ * which also documents the layout of
+ * <i>/proc/net/netfilter/nfnetlink_queue</i>.
+ *
+ * \anchor nfq6<b>nfq6.c</b>
+ * https://raw.githubusercontent.com/duncan-roe/nfq6/main/nfq6.c
+ * is an extension of nf-queue.c.
+ * The code has examples of packet mangling (any protocol)
+ * and other features mentioned elsewhere.
+ * Search for `Exit nfq6` to land in the middle of the usage summary.
  *
  * <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.
  * In that case, you can increase the socket buffer size by means of
- * nfnl_rcvbufsiz(). Although this delays the appearance of ENOBUFS errors,
+ * setsocketopt(). Although this delays the appearance of ENOBUFS errors,
  * you may hit it again sooner or later. The next section provides some hints
  * on how to obtain the best performance for your application.
  *
@@ -117,7 +164,11 @@ gcc -g3 -ggdb -Wall -lmnl -lnetfilter_queue -o nf-queue nf-queue.c
  * To improve your libnetfilter_queue application in terms of performance,
  * you may consider the following tweaks:
  *
- * - increase the default socket buffer size by means of nfnl_rcvbufsiz().
+ * - increase the default socket buffer size.
+ * Use setsocketopt() with SOL_SOCKET and SO_RCVBUFFORCE on the netlink socket
+ * fd returned by mnl_socket_get_fd()
+ * (software using the old nfnl API calls nfq_fd()).
+ * Software calling nfnl_rcvbufsiz() will continue to be supported.
  * - set nice value of your process to -20 (maximum priority).
  * - set the CPU affinity of your process to a spare core that is not used
  * to handle NIC interruptions.
@@ -125,42 +176,65 @@ gcc -g3 -ggdb -Wall -lmnl -lnetfilter_queue -o nf-queue nf-queue.c
  * (requires Linux kernel >= 2.6.30).
  * - see --queue-balance option in NFQUEUE target for multi-threaded apps
  * (it requires Linux kernel >= 2.6.31).
- * - consider using fail-open option see nfq_set_queue_flags() (it requires
- *  Linux kernel >= 3.6)
+ * - consider using
+ * \anchor failopen \b 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
  */
 
+/* We need a rump nfnl_handle to support nfnl_rcvbufsiz()
+ * which is documented in libnetfilter_queue(7) and on the HTML main page.
+ * Luckily fd is the 1st item and that's all we need
+ * Because we need to have an fd in struct nfnl_handle,
+ * we don't have one in struct nfq_handle (for e.g. nfq_fd()).
+ */
+
+struct nfnl_handle {
+	int			fd;
+};
+
 struct nfq_handle
 {
+	unsigned int portid;
 	struct nfnl_handle *nfnlh;
-	struct nfnl_subsys_handle *nfnlssh;
 	struct nfq_q_handle *qh_list;
+	struct mnl_socket *nl;
+	struct nfnl_handle rump_nfnl_handle;
 };
 
 struct nfq_q_handle
 {
 	struct nfq_q_handle *next;
 	struct nfq_handle *h;
-	uint16_t id;
+	uint16_t queue_num;
 
 	nfq_callback *cb;
 	void *data;
 };
 
 struct nfq_data {
-	struct nfattr **data;
+	struct nlattr **data;
 };
 
-EXPORT_SYMBOL int nfq_errno;
-
 /***********************************************************************
  * low level stuff
  ***********************************************************************/
 
+static int nfq_query(struct nfq_handle *h, struct nlmsghdr *nlh, char *buf,
+		      size_t bufsiz)
+{
+	int ret;
+
+	ret = mnl_socket_sendto(h->nl, nlh, nlh->nlmsg_len);
+	if (ret != -1)
+		ret = mnl_socket_recvfrom(h->nl, buf, bufsiz);
+	if (ret != -1)
+		ret = mnl_cb_run(buf, ret, 0, h->portid, NULL, NULL);
+	return ret;
+}
+
 static void del_qh(struct nfq_q_handle *qh)
 {
 	struct nfq_q_handle *cur_qh, *prev_qh = NULL;
@@ -183,12 +257,12 @@ static void add_qh(struct nfq_q_handle *qh)
 	qh->h->qh_list = qh;
 }
 
-static struct nfq_q_handle *find_qh(struct nfq_handle *h, uint16_t id)
+static struct nfq_q_handle *find_qh(struct nfq_handle *h, uint16_t queue_num)
 {
 	struct nfq_q_handle *qh;
 
 	for (qh = h->qh_list; qh; qh = qh->next) {
-		if (qh->id == id)
+		if (qh->queue_num == queue_num)
 			return qh;
 	}
 	return NULL;
@@ -199,46 +273,36 @@ static struct nfq_q_handle *find_qh(struct nfq_handle *h, uint16_t id)
 __build_send_cfg_msg(struct nfq_handle *h, uint8_t command,
 		uint16_t queuenum, uint16_t pf)
 {
-	union {
-		char buf[NFNL_HEADER_LEN
-			+NFA_LENGTH(sizeof(struct nfqnl_msg_config_cmd))];
-		struct nlmsghdr nmh;
-	} u;
-	struct nfqnl_msg_config_cmd cmd;
-
-	nfnl_fill_hdr(h->nfnlssh, &u.nmh, 0, AF_UNSPEC, queuenum,
-			NFQNL_MSG_CONFIG, NLM_F_REQUEST|NLM_F_ACK);
-
-	cmd._pad = 0;
-	cmd.command = command;
-	cmd.pf = htons(pf);
-	nfnl_addattr_l(&u.nmh, sizeof(u), NFQA_CFG_CMD, &cmd, sizeof(cmd));
-
-	return nfnl_query(h->nfnlh, &u.nmh);
-}
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
 
-static int __nfq_rcv_pkt(struct nlmsghdr *nlh, struct nfattr *nfa[],
-		void *data)
-{
-	struct nfgenmsg *nfmsg = NLMSG_DATA(nlh);
-	struct nfq_handle *h = data;
-	uint16_t queue_num = ntohs(nfmsg->res_id);
-	struct nfq_q_handle *qh = find_qh(h, queue_num);
-	struct nfq_data nfqa;
+	nlh = nfq_nlmsg_put2(buf, NFQNL_MSG_CONFIG, queuenum, NLM_F_ACK);
 
-	if (!qh)
-		return -ENODEV;
-
-	if (!qh->cb)
-		return -ENODEV;
-
-	nfqa.data = nfa;
+	nfq_nlmsg_cfg_put_cmd(nlh, AF_UNSPEC, command);
 
-	return qh->cb(qh, nfmsg, &nfqa, qh->data);
+	return nfq_query(h, nlh, buf, sizeof(buf));
 }
 
 /* public interface */
 
+/**
+ * \addtogroup LibrarySetup
+ * @{
+ */
+
+/**
+ *
+ * nfq_nfnlh - obtain a handle for nfnl_rcvbufsiz()
+ *
+ * \param h Netfilter queue connection handle obtained via call to nfq_open()
+ *
+ * \return pointer to struct nfnl_handle
+ *
+ * \warning
+ * The returned handle \b h is for nfnl_rcvbufsiz() only. It is not suitable
+ * for calling other functions in the deprecated \b libnfnetlink library.
+ */
+
 EXPORT_SYMBOL
 struct nfnl_handle *nfq_nfnlh(struct nfq_handle *h)
 {
@@ -246,14 +310,22 @@ struct nfnl_handle *nfq_nfnlh(struct nfq_handle *h)
 }
 
 /**
+ * @}
+ */
+
+/**
+ *
+ * \defgroup Queue Queue handling [nfnl API]
  *
- * \defgroup Queue Queue handling [DEPRECATED]
+ * \warning
+ * This page describes functions from the old nfnl API.
+ * Consider using the mnl API for new projects.
  *
  * 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().
+ * The queue can then be tuned via nfq_set_mode()
  *
  * Here's a little code snippet that create queue numbered 0:
  * \verbatim
@@ -328,14 +400,18 @@ struct nfnl_handle *nfq_nfnlh(struct nfq_handle *h)
 EXPORT_SYMBOL
 int nfq_fd(struct nfq_handle *h)
 {
-	return nfnl_fd(nfq_nfnlh(h));
+	return h->nfnlh->fd;
 }
 /**
  * @}
  */
 
 /**
- * \defgroup LibrarySetup Library setup [DEPRECATED]
+ * \defgroup LibrarySetup Library setup [nfnl API]
+ *
+ * \warning
+ * This page describes functions from the old nfnl API.
+ * Consider using the mnl API for new projects.
  *
  * Library initialisation is made in two steps.
  *
@@ -383,75 +459,36 @@ int nfq_fd(struct nfq_handle *h)
 EXPORT_SYMBOL
 struct nfq_handle *nfq_open(void)
 {
-	struct nfnl_handle *nfnlh = nfnl_open();
-	struct nfq_handle *qh;
-
-	if (!nfnlh)
-		return NULL;
-
-	/* unset netlink sequence tracking by default */
-	nfnl_unset_sequence_tracking(nfnlh);
-
-	qh = nfq_open_nfnl(nfnlh);
-	if (!qh)
-		nfnl_close(nfnlh);
-
-	return qh;
-}
-
-/**
- * @}
- */
-
-/**
- * nfq_open_nfnl - open a nfqueue handler from a existing nfnetlink handler
- * \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
- * nfq_open(), and should typically not be called directly.
- *
- * \return a pointer to a new queue handle or NULL on failure.
- */
-EXPORT_SYMBOL
-struct nfq_handle *nfq_open_nfnl(struct nfnl_handle *nfnlh)
-{
-	struct nfnl_callback pkt_cb = {
-		.call		= __nfq_rcv_pkt,
-		.attr_count	= NFQA_MAX,
-	};
-	struct nfq_handle *h;
-	int err;
+	struct nfq_handle *h = malloc(sizeof(*h));
 
-	h = malloc(sizeof(*h));
 	if (!h)
 		return NULL;
-
 	memset(h, 0, sizeof(*h));
-	h->nfnlh = nfnlh;
 
-	h->nfnlssh = nfnl_subsys_open(h->nfnlh, NFNL_SUBSYS_QUEUE,
-				      NFQNL_MSG_MAX, 0);
-	if (!h->nfnlssh) {
-		/* FIXME: nfq_errno */
-		goto out_free;
+	h->nl = mnl_socket_open(NETLINK_NETFILTER);
+	if (!h->nl) {
+		free(h);
+		return NULL;
 	}
 
-	pkt_cb.data = h;
-	err = nfnl_callback_register(h->nfnlssh, NFQNL_MSG_PACKET, &pkt_cb);
-	if (err < 0) {
-		nfq_errno = err;
-		goto out_close;
+	if (mnl_socket_bind(h->nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+		mnl_socket_close(h->nl);
+		free(h);
+		return NULL;
 	}
+	h->portid = mnl_socket_get_portid(h->nl);
+
+	/* fudges for nfnl_rcvbufsiz() */
+	h->nfnlh = &h->rump_nfnl_handle;
+	h->rump_nfnl_handle.fd = mnl_socket_get_fd(h->nl);
 
 	return h;
-out_close:
-	nfnl_subsys_close(h->nfnlssh);
-out_free:
-	free(h);
-	return NULL;
 }
 
+/**
+ * @}
+ */
+
 /**
  * \addtogroup LibrarySetup
  *
@@ -469,6 +506,55 @@ out_free:
  * @{
  */
 
+/**
+ * nfnl_rcvbufsiz - set the socket buffer size
+ * \param h nfnetlink connection handle obtained via call to nfq_nfnlh()
+ * \param size size of the buffer we want to set
+ *
+ * This nfnl-API function sets the new size of the socket buffer.
+ * Use this setting
+ * to increase the socket buffer size if your system is reporting ENOBUFS
+ * errors.
+ *
+ * This code snippet achieves the same result from the mnl API:
+ * \verbatim
+	socklen_t wanted_size;     // Set this to number of bytes you want
+	socklen_t read_size;       // Will be number of bytes you got
+	socklen_t socklen = sizeof wanted_size;
+	struct mnl_socket *nl;     // Returned by mnl_socket_open()
+
+	setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_RCVBUFFORCE,
+	    &wanted_size, sizeof(socklen_t));
+	getsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_RCVBUF, &read_size,
+	    &socklen);
+\endverbatim
+ *
+ * \return new size of kernel socket buffer
+ */
+
+EXPORT_SYMBOL
+unsigned int nfnl_rcvbufsiz(const struct nfnl_handle *h, unsigned int size)
+{
+	int status;
+	socklen_t socklen = sizeof(size);
+	unsigned int read_size = 0;
+
+	/* first we try the FORCE option, which is introduced in kernel
+	 * 2.6.14 to give "root" the ability to override the system wide
+	 * maximum
+	 */
+	status = setsockopt(h->fd, SOL_SOCKET, SO_RCVBUFFORCE, &size, socklen);
+	if (status < 0) {
+		/* if this didn't work, we try at least to get the system
+		 * wide maximum (or whatever the user requested)
+		 */
+		setsockopt(h->fd, SOL_SOCKET, SO_RCVBUF, &size, socklen);
+	}
+	getsockopt(h->fd, SOL_SOCKET, SO_RCVBUF, &read_size, &socklen);
+
+	return read_size;
+}
+
 /**
  * nfq_close - close a nfqueue handler
  * \param h Netfilter queue connection handle obtained via call to nfq_open()
@@ -480,12 +566,18 @@ out_free:
 EXPORT_SYMBOL
 int nfq_close(struct nfq_handle *h)
 {
-	int ret;
+	struct nfq_q_handle *qh;
 
-	ret = nfnl_close(h->nfnlh);
-	if (ret == 0)
-		free(h);
-	return ret;
+	mnl_socket_close(h->nl);
+
+	while (h->qh_list) {
+		qh = h->qh_list;
+		h->qh_list = qh->next;
+		free(qh);
+	}
+	free(h);
+
+	return 0;
 }
 
 /**
@@ -497,7 +589,7 @@ int nfq_close(struct nfq_handle *h)
  * the given protocol family (ie. PF_INET, PF_INET6, etc).
  * This call is obsolete, Linux kernels from 3.8 onwards ignore it.
  *
- * \return integer inferior to 0 in case of failure
+ * \return -1 on error with errno set, else 0
  */
 EXPORT_SYMBOL
 int nfq_bind_pf(struct nfq_handle *h, uint16_t pf)
@@ -521,7 +613,6 @@ int nfq_unbind_pf(struct nfq_handle *h, uint16_t pf)
 	return __build_send_cfg_msg(h, NFQNL_CFG_CMD_PF_UNBIND, 0, pf);
 }
 
-
 /**
  * @}
  */
@@ -544,7 +635,7 @@ int nfq_unbind_pf(struct nfq_handle *h, uint16_t pf)
  * Creates a new queue handle, and returns it.  The new queue is identified by
  * \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,
+ * a queue entry with queue_num \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:
@@ -579,13 +670,12 @@ struct nfq_q_handle *nfq_create_queue(struct nfq_handle *h, uint16_t num,
 
 	memset(qh, 0, sizeof(*qh));
 	qh->h = h;
-	qh->id = num;
+	qh->queue_num = num;
 	qh->cb = cb;
 	qh->data = data;
 
 	ret = __build_send_cfg_msg(h, NFQNL_CFG_CMD_BIND, num, 0);
 	if (ret < 0) {
-		nfq_errno = ret;
 		free(qh);
 		return NULL;
 	}
@@ -598,6 +688,25 @@ struct nfq_q_handle *nfq_create_queue(struct nfq_handle *h, uint16_t num,
  * @}
  */
 
+static int __nfq_handle_msg(const struct nlmsghdr *nlh, void *data)
+{
+	struct nfq_handle *h = data;
+	struct nfq_q_handle *qh;
+	struct nlattr *nfa[NFQA_MAX + 1] = {};
+	struct nfq_data nfad = {nfa};
+	struct nfgenmsg *nfmsg = NLMSG_DATA(nlh);
+
+	if (nfq_nlmsg_parse(nlh, nfa) < 0)
+		return MNL_CB_ERROR;
+
+	/* Find our queue handler (to get CB fn) */
+	qh = find_qh(h, ntohs(nfmsg->res_id));
+	if (!qh)
+		return MNL_CB_ERROR;
+
+	return qh->cb(qh, nfmsg, &nfad, qh->data);
+}
+
 /**
  * \addtogroup Queue
  * @{
@@ -613,7 +722,8 @@ struct nfq_q_handle *nfq_create_queue(struct nfq_handle *h, uint16_t num,
 EXPORT_SYMBOL
 int nfq_destroy_queue(struct nfq_q_handle *qh)
 {
-	int ret = __build_send_cfg_msg(qh->h, NFQNL_CFG_CMD_UNBIND, qh->id, 0);
+	int ret = __build_send_cfg_msg(qh->h, NFQNL_CFG_CMD_UNBIND,
+				       qh->queue_num, 0);
 	if (ret == 0) {
 		del_qh(qh);
 		free(qh);
@@ -632,12 +742,12 @@ int nfq_destroy_queue(struct nfq_q_handle *qh)
  * queue. Packets can be read from the queue using nfq_fd() and recv(). See
  * example code for nfq_fd().
  *
- * \return 0 on success, non-zero on failure.
+ * \return value returned by callback function specified to nfq_create_queue()
  */
 EXPORT_SYMBOL
 int nfq_handle_packet(struct nfq_handle *h, char *buf, int len)
 {
-	return nfnl_handle_packet(h->nfnlh, buf, len);
+	return mnl_cb_run(buf, len, 0, h->portid, __nfq_handle_msg, h);
 }
 
 /**
@@ -658,22 +768,14 @@ int nfq_handle_packet(struct nfq_handle *h, char *buf, int len)
 EXPORT_SYMBOL
 int nfq_set_mode(struct nfq_q_handle *qh, uint8_t mode, uint32_t range)
 {
-	union {
-		char buf[NFNL_HEADER_LEN
-			+NFA_LENGTH(sizeof(struct nfqnl_msg_config_params))];
-		struct nlmsghdr nmh;
-	} u;
-	struct nfqnl_msg_config_params params;
-
-	nfnl_fill_hdr(qh->h->nfnlssh, &u.nmh, 0, AF_UNSPEC, qh->id,
-			NFQNL_MSG_CONFIG, NLM_F_REQUEST|NLM_F_ACK);
-
-	params.copy_range = htonl(range);
-	params.copy_mode = mode;
-	nfnl_addattr_l(&u.nmh, sizeof(u), NFQA_CFG_PARAMS, &params,
-			sizeof(params));
-
-	return nfnl_query(qh->h->nfnlh, &u.nmh);
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+
+	nlh = nfq_nlmsg_put2(buf, NFQNL_MSG_CONFIG, qh->queue_num, NLM_F_ACK);
+
+	nfq_nlmsg_cfg_put_params(nlh, mode, range);
+
+	return nfq_query(qh->h, nlh, buf, sizeof(buf));
 }
 
 /**
@@ -707,6 +809,9 @@ int nfq_set_mode(struct nfq_q_handle *qh, uint8_t mode, uint32_t range)
  *   If your application validates checksums (e.g., tcp checksum),
  *   then you must also check if the NFQA_SKB_INFO attribute is present.
  *   If it is, you need to test the NFQA_SKB_CSUMNOTREADY bit:
+ *
+ * FIXME the code below is for the mnl API
+ *       but nfq_set_queue_flags is part of the nfnl API
  * \verbatim
 	if (attr[NFQA_SKB_INFO]) {
 		uint32_t info = ntohl(mnl_attr_get_u32(attr[NFQA_SKB_INFO]));
@@ -742,28 +847,23 @@ int nfq_set_mode(struct nfq_q_handle *qh, uint8_t mode, uint32_t range)
  *  dumps UID/GID and security context fields only for one fragment. To deal
  *  with this limitation always set NFQA_CFG_F_GSO.
  *
- * \return -1 on error with errno set appropriately; =0 otherwise.
+ * \return -1 on error with errno set, else 0
  */
 EXPORT_SYMBOL
 int nfq_set_queue_flags(struct nfq_q_handle *qh, uint32_t mask, uint32_t flags)
 {
-	union {
-		char buf[NFNL_HEADER_LEN
-			+NFA_LENGTH(sizeof(mask)
-			+NFA_LENGTH(sizeof(flags)))];
-		struct nlmsghdr nmh;
-	} u;
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
 
 	mask = htonl(mask);
 	flags = htonl(flags);
 
-	nfnl_fill_hdr(qh->h->nfnlssh, &u.nmh, 0, AF_UNSPEC, qh->id,
-		      NFQNL_MSG_CONFIG, NLM_F_REQUEST|NLM_F_ACK);
+	nlh = nfq_nlmsg_put2(buf, NFQNL_MSG_CONFIG, qh->queue_num, NLM_F_ACK);
 
-	nfnl_addattr32(&u.nmh, sizeof(u), NFQA_CFG_FLAGS, flags);
-	nfnl_addattr32(&u.nmh, sizeof(u), NFQA_CFG_MASK, mask);
+	mnl_attr_put_u32(nlh, NFQA_CFG_FLAGS, flags);
+	mnl_attr_put_u32(nlh, NFQA_CFG_MASK, mask);
 
-	return nfnl_query(qh->h->nfnlh, &u.nmh);
+	return nfq_query(qh->h, nlh, buf, sizeof(buf));
 }
 
 /**
@@ -775,25 +875,24 @@ int nfq_set_queue_flags(struct nfq_q_handle *qh, uint32_t mask, uint32_t flags)
  * of packets the kernel will store before internally before dropping
  * upcoming packets.
  *
+ * \note
+ * The kernel already sets this to several times the maximum that other parts
+ * of the system can implement.
+ * For experimenters, setting it to a low value does work.
+ *
  * \return -1 on error; >=0 otherwise.
  */
 EXPORT_SYMBOL
 int nfq_set_queue_maxlen(struct nfq_q_handle *qh, uint32_t queuelen)
 {
-	union {
-		char buf[NFNL_HEADER_LEN
-			+NFA_LENGTH(sizeof(struct nfqnl_msg_config_params))];
-		struct nlmsghdr nmh;
-	} u;
-	uint32_t queue_maxlen = htonl(queuelen);
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
 
-	nfnl_fill_hdr(qh->h->nfnlssh, &u.nmh, 0, AF_UNSPEC, qh->id,
-			NFQNL_MSG_CONFIG, NLM_F_REQUEST|NLM_F_ACK);
+	nlh = nfq_nlmsg_put2(buf, NFQNL_MSG_CONFIG, qh->queue_num, NLM_F_ACK);
 
-	nfnl_addattr_l(&u.nmh, sizeof(u), NFQA_CFG_QUEUE_MAXLEN, &queue_maxlen,
-			sizeof(queue_maxlen));
+	mnl_attr_put_u32(nlh, NFQA_CFG_QUEUE_MAXLEN, htonl(queuelen));
 
-	return nfnl_query(qh->h->nfnlh, &u.nmh);
+	return nfq_query(qh->h, nlh, buf, sizeof(buf));
 }
 
 /**
@@ -805,52 +904,55 @@ static int __set_verdict(struct nfq_q_handle *qh, uint32_t id,
 		uint32_t data_len, const unsigned char *data,
 		enum nfqnl_msg_types type)
 {
-	struct nfqnl_msg_verdict_hdr vh;
-	union {
-		char buf[NFNL_HEADER_LEN
-			+NFA_LENGTH(sizeof(mark))
-			+NFA_LENGTH(sizeof(vh))];
-		struct nlmsghdr nmh;
-	} u;
-
-	struct iovec iov[3];
-	int nvecs;
-
-	/* This must be declared here (and not inside the data
-	 * handling block) because the iovec points to this. */
-	struct nfattr data_attr;
-
-	memset(iov, 0, sizeof(iov));
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+	static struct sockaddr_nl snl = {.nl_family = AF_NETLINK };
 
-	vh.verdict = htonl(verdict);
-	vh.id = htonl(id);
-
-	nfnl_fill_hdr(qh->h->nfnlssh, &u.nmh, 0, AF_UNSPEC, qh->id,
-				type, NLM_F_REQUEST);
+	nlh = nfq_nlmsg_put(buf, type, qh->queue_num);
 
 	/* add verdict header */
-	nfnl_addattr_l(&u.nmh, sizeof(u), NFQA_VERDICT_HDR, &vh, sizeof(vh));
+	nfq_nlmsg_verdict_put(nlh, id, verdict);
 
 	if (set_mark)
-		nfnl_addattr32(&u.nmh, sizeof(u), NFQA_MARK, mark);
-
-	iov[0].iov_base = &u.nmh;
-	iov[0].iov_len = NLMSG_TAIL(&u.nmh) - (void *)&u.nmh;
-	nvecs = 1;
-
-	if (data_len) {
+		nfq_nlmsg_verdict_put_mark(nlh, mark);
+
+	/* Efficiency gain: when there is only 1 iov,
+	 * sendto() is faster than sendmsg() because the kernel only has
+	 * 1 userspace address to validate instead of 2.
+	 */
+	if (!data_len)
+		return mnl_socket_sendto(qh->h->nl, nlh, nlh->nlmsg_len);
+	else {
+		struct iovec iov[2];
+		struct nlattr *data_attr = mnl_nlmsg_get_payload_tail(nlh);
+		const struct msghdr msg = {
+			.msg_name = &snl,
+			.msg_namelen = sizeof snl,
+			.msg_iov = iov,
+			.msg_iovlen = 2,
+			.msg_control = NULL,
+			.msg_controllen = 0,
+			.msg_flags = 0,
+		};
+
+		mnl_attr_put(nlh, NFQA_PAYLOAD, 0, NULL);
+
+		iov[0].iov_base = nlh;
+		iov[0].iov_len = nlh->nlmsg_len;
 		/* The typecast here is to cast away data's const-ness: */
-		nfnl_build_nfa_iovec(&iov[1], &data_attr, NFQA_PAYLOAD,
-				data_len, (unsigned char *) data);
-		nvecs += 2;
+		iov[1].iov_base = (unsigned char *)data;
+		iov[1].iov_len = data_len;
+
 		/* Add the length of the appended data to the message
-		 * header.  The size of the attribute is given in the
-		 * nfa_len field and is set in the nfnl_build_nfa_iovec()
-		 * function. */
-		u.nmh.nlmsg_len += data_attr.nfa_len;
-	}
+		 * header and attribute length.
+		 * No padding is needed: this is the end of the message. */
+
+		nlh->nlmsg_len += data_len;
 
-	return nfnl_sendiov(qh->h->nfnlh, iov, nvecs, 0);
+		data_attr->nla_len += data_len;
+
+		return sendmsg(qh->h->nfnlh->fd, &msg, 0);
+	}
 }
 
 /**
@@ -904,7 +1006,7 @@ int nfq_set_verdict2(struct nfq_q_handle *qh, uint32_t id,
 		     uint32_t verdict, uint32_t mark,
 		     uint32_t data_len, const unsigned char *buf)
 {
-	return __set_verdict(qh, id, verdict, htonl(mark), 1, data_len,
+	return __set_verdict(qh, id, verdict, mark, 1, data_len,
 						buf, NFQNL_MSG_VERDICT);
 }
 
@@ -939,7 +1041,7 @@ EXPORT_SYMBOL
 int nfq_set_verdict_batch2(struct nfq_q_handle *qh, uint32_t id,
 			   uint32_t verdict, uint32_t mark)
 {
-	return __set_verdict(qh, id, verdict, htonl(mark), 1, 0,
+	return __set_verdict(qh, id, verdict, mark, 1, 0,
 				NULL, NFQNL_MSG_VERDICT_BATCH);
 }
 
@@ -962,7 +1064,7 @@ int nfq_set_verdict_mark(struct nfq_q_handle *qh, uint32_t id,
 			 uint32_t verdict, uint32_t mark,
 			 uint32_t data_len, const unsigned char *buf)
 {
-	return __set_verdict(qh, id, verdict, mark, 1, data_len, buf,
+	return __set_verdict(qh, id, verdict, ntohl(mark), 1, data_len, buf,
 						NFQNL_MSG_VERDICT);
 }
 
@@ -977,7 +1079,11 @@ int nfq_set_verdict_mark(struct nfq_q_handle *qh, uint32_t id,
  *************************************************************/
 
 /**
- * \defgroup Parsing Message parsing functions [DEPRECATED]
+ * \defgroup Parsing Message parsing functions [nfnl API]
+ *
+ * \warning
+ * This page describes functions from the old nfnl API.
+ * Consider using the mnl API for new projects.
  *
  * \manonly
 .SH SYNOPSIS
@@ -997,8 +1103,9 @@ int nfq_set_verdict_mark(struct nfq_q_handle *qh, uint32_t id,
  * \return the netfilter queue netlink packet header for the given
  * nfq_data argument.  Typically, the nfq_data value is passed as the 3rd
  * parameter to the callback function set by a call to nfq_create_queue().
-  *
- * The nfqnl_msg_packet_hdr structure is defined in libnetfilter_queue.h as:
+ *
+ * The nfqnl_msg_packet_hdr structure is defined in
+ * linux/netfilter/nfnetlink_queue.h as:
  *
  * \verbatim
 	struct nfqnl_msg_packet_hdr {
@@ -1011,8 +1118,10 @@ int nfq_set_verdict_mark(struct nfq_q_handle *qh, uint32_t id,
 EXPORT_SYMBOL
 struct nfqnl_msg_packet_hdr *nfq_get_msg_packet_hdr(struct nfq_data *nfad)
 {
-	return nfnl_get_pointer_to_data(nfad->data, NFQA_PACKET_HDR,
-					struct nfqnl_msg_packet_hdr);
+	if (!nfad->data[NFQA_PACKET_HDR])
+		return NULL;
+
+	return mnl_attr_get_payload(nfad->data[NFQA_PACKET_HDR]);
 }
 
 /**
@@ -1024,7 +1133,10 @@ struct nfqnl_msg_packet_hdr *nfq_get_msg_packet_hdr(struct nfq_data *nfad)
 EXPORT_SYMBOL
 uint32_t nfq_get_nfmark(struct nfq_data *nfad)
 {
-	return ntohl(nfnl_get_data(nfad->data, NFQA_MARK, uint32_t));
+	if (!nfad->data[NFQA_MARK])
+		return 0;
+
+	return ntohl(mnl_attr_get_u32(nfad->data[NFQA_MARK]));
 }
 
 /**
@@ -1040,11 +1152,12 @@ EXPORT_SYMBOL
 int nfq_get_timestamp(struct nfq_data *nfad, struct timeval *tv)
 {
 	struct nfqnl_msg_packet_timestamp *qpt;
-	qpt = nfnl_get_pointer_to_data(nfad->data, NFQA_TIMESTAMP,
-					struct nfqnl_msg_packet_timestamp);
-	if (!qpt)
+
+	if (!nfad->data[NFQA_TIMESTAMP])
 		return -1;
 
+	qpt = mnl_attr_get_payload(nfad->data[NFQA_TIMESTAMP]);
+
 	tv->tv_sec = __be64_to_cpu(qpt->sec);
 	tv->tv_usec = __be64_to_cpu(qpt->usec);
 
@@ -1065,7 +1178,10 @@ int nfq_get_timestamp(struct nfq_data *nfad, struct timeval *tv)
 EXPORT_SYMBOL
 uint32_t nfq_get_indev(struct nfq_data *nfad)
 {
-	return ntohl(nfnl_get_data(nfad->data, NFQA_IFINDEX_INDEV, uint32_t));
+	if (!nfad->data[NFQA_IFINDEX_INDEV])
+		return 0;
+
+	return ntohl(mnl_attr_get_u32(nfad->data[NFQA_IFINDEX_INDEV]));
 }
 
 /**
@@ -1079,7 +1195,10 @@ uint32_t nfq_get_indev(struct nfq_data *nfad)
 EXPORT_SYMBOL
 uint32_t nfq_get_physindev(struct nfq_data *nfad)
 {
-	return ntohl(nfnl_get_data(nfad->data, NFQA_IFINDEX_PHYSINDEV, uint32_t));
+	if (!nfad->data[NFQA_IFINDEX_PHYSINDEV])
+		return 0;
+
+	return ntohl(mnl_attr_get_u32(nfad->data[NFQA_IFINDEX_PHYSINDEV]));
 }
 
 /**
@@ -1093,7 +1212,10 @@ uint32_t nfq_get_physindev(struct nfq_data *nfad)
 EXPORT_SYMBOL
 uint32_t nfq_get_outdev(struct nfq_data *nfad)
 {
-	return ntohl(nfnl_get_data(nfad->data, NFQA_IFINDEX_OUTDEV, uint32_t));
+	if (!nfad->data[NFQA_IFINDEX_OUTDEV])
+		return 0;
+
+	return ntohl(mnl_attr_get_u32(nfad->data[NFQA_IFINDEX_OUTDEV]));
 }
 
 /**
@@ -1104,51 +1226,28 @@ uint32_t nfq_get_outdev(struct nfq_data *nfad)
  * 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.
+ * \return The index of physical interface that the packet output will be
+ *         routed to.
  */
 EXPORT_SYMBOL
 uint32_t nfq_get_physoutdev(struct nfq_data *nfad)
 {
-	return ntohl(nfnl_get_data(nfad->data, NFQA_IFINDEX_PHYSOUTDEV, uint32_t));
+	if (!nfad->data[NFQA_IFINDEX_PHYSOUTDEV])
+		return 0;
+
+	return ntohl(mnl_attr_get_u32(nfad->data[NFQA_IFINDEX_PHYSOUTDEV]));
 }
 
 /**
  * nfq_get_indev_name - get the name of the interface the packet
  * was received through
- * \param nlif_handle pointer to a nlif interface resolving handle
+ * \param nlif_handle pointer to an nlif interface resolving handle
  * \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.
- *
- * 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
- * during all your program life:
- * \verbatim
-	h = nlif_open();
-	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);
-\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
- * descriptor, you need to use nlif_fd:
- * \verbatim
-	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);
-\endverbatim
  *
+ * \return -1 on error, > 0 otherwise.
+ * \sa __nlif_open__(3)
  */
 EXPORT_SYMBOL
 int nfq_get_indev_name(struct nlif_handle *nlif_handle,
@@ -1161,14 +1260,13 @@ int nfq_get_indev_name(struct nlif_handle *nlif_handle,
 /**
  * nfq_get_physindev_name - get the name of the physical interface the
  * packet was received through
- * \param nlif_handle pointer to a nlif interface resolving handle
+ * \param nlif_handle pointer to an nlif interface resolving handle
  * \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.
  *
- * See nfq_get_indev_name() documentation for nlif_handle usage.
- *
- * \return  -1 in case of error, > 0 if it succeed.
+ * \return -1 on error, > 0 otherwise.
+ * \sa __nlif_open__(3)
  */
 EXPORT_SYMBOL
 int nfq_get_physindev_name(struct nlif_handle *nlif_handle,
@@ -1181,14 +1279,13 @@ int nfq_get_physindev_name(struct nlif_handle *nlif_handle,
 /**
  * nfq_get_outdev_name - get the name of the physical interface the
  * packet will be sent to
- * \param nlif_handle pointer to a nlif interface resolving handle
+ * \param nlif_handle pointer to an nlif interface resolving handle
  * \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.
  *
- * See nfq_get_indev_name() documentation for nlif_handle usage.
- *
- * \return  -1 in case of error, > 0 if it succeed.
+ * \return -1 on error, > 0 otherwise.
+ * \sa __nlif_open__(3)
  */
 EXPORT_SYMBOL
 int nfq_get_outdev_name(struct nlif_handle *nlif_handle,
@@ -1201,14 +1298,13 @@ int nfq_get_outdev_name(struct nlif_handle *nlif_handle,
 /**
  * nfq_get_physoutdev_name - get the name of the interface the
  * packet will be sent to
- * \param nlif_handle pointer to a nlif interface resolving handle
+ * \param nlif_handle pointer to an nlif interface resolving handle
  * \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.
  *
- * See nfq_get_indev_name() documentation for nlif_handle usage.
- *
- * \return  -1 in case of error, > 0 if it succeed.
+ * \return -1 on error, > 0 otherwise.
+ * \sa __nlif_open__(3)
  */
 
 EXPORT_SYMBOL
@@ -1244,8 +1340,10 @@ int nfq_get_physoutdev_name(struct nlif_handle *nlif_handle,
 EXPORT_SYMBOL
 struct nfqnl_msg_packet_hw *nfq_get_packet_hw(struct nfq_data *nfad)
 {
-	return nfnl_get_pointer_to_data(nfad->data, NFQA_HWADDR,
-					struct nfqnl_msg_packet_hw);
+	if (!nfad->data[NFQA_HWADDR])
+		return NULL;
+
+	return mnl_attr_get_payload(nfad->data[NFQA_HWADDR]);
 }
 
 /**
@@ -1273,10 +1371,10 @@ struct nfqnl_msg_packet_hw *nfq_get_packet_hw(struct nfq_data *nfad)
 EXPORT_SYMBOL
 uint32_t nfq_get_skbinfo(struct nfq_data *nfad)
 {
-	if (!nfnl_attr_present(nfad->data, NFQA_SKB_INFO))
+	if (!nfad->data[NFQA_SKB_INFO])
 		return 0;
 
-	return ntohl(nfnl_get_data(nfad->data, NFQA_SKB_INFO, uint32_t));
+	return ntohl(mnl_attr_get_u32(nfad->data[NFQA_SKB_INFO]));
 }
 
 /**
@@ -1293,10 +1391,10 @@ uint32_t nfq_get_skbinfo(struct nfq_data *nfad)
 EXPORT_SYMBOL
 int nfq_get_uid(struct nfq_data *nfad, uint32_t *uid)
 {
-	if (!nfnl_attr_present(nfad->data, NFQA_UID))
+	if (!nfad->data[NFQA_UID])
 		return 0;
 
-	*uid = ntohl(nfnl_get_data(nfad->data, NFQA_UID, uint32_t));
+	*uid = ntohl(mnl_attr_get_u32(nfad->data[NFQA_UID]));
 	return 1;
 }
 
@@ -1314,10 +1412,10 @@ int nfq_get_uid(struct nfq_data *nfad, uint32_t *uid)
 EXPORT_SYMBOL
 int nfq_get_gid(struct nfq_data *nfad, uint32_t *gid)
 {
-	if (!nfnl_attr_present(nfad->data, NFQA_GID))
+	if (!nfad->data[NFQA_GID])
 		return 0;
 
-	*gid = ntohl(nfnl_get_data(nfad->data, NFQA_GID, uint32_t));
+	*gid = ntohl(mnl_attr_get_u32(nfad->data[NFQA_GID]));
 	return 1;
 }
 
@@ -1330,19 +1428,18 @@ int nfq_get_gid(struct nfq_data *nfad, uint32_t *gid)
  * may be pushed into the queue. In this case, only one fragment will have the
  * SECCTX field set. To deal with this issue always set NFQA_CFG_F_GSO.
  *
- * \return -1 on error, otherwise > 0
+ * \return -1 on error, > 0 otherwise.
  */
 EXPORT_SYMBOL
 int nfq_get_secctx(struct nfq_data *nfad, unsigned char **secdata)
 {
-	if (!nfnl_attr_present(nfad->data, NFQA_SECCTX))
+	if (!nfad->data[NFQA_SECCTX])
 		return -1;
 
-	*secdata = (unsigned char *)nfnl_get_pointer_to_data(nfad->data,
-							NFQA_SECCTX, char);
+	*secdata = mnl_attr_get_payload(nfad->data[NFQA_SECCTX]);
 
 	if (*secdata)
-		return NFA_PAYLOAD(nfad->data[NFQA_SECCTX-1]);
+		return mnl_attr_get_payload_len(nfad->data[NFQA_SECCTX]);
 
 	return 0;
 }
@@ -1356,15 +1453,17 @@ int nfq_get_secctx(struct nfq_data *nfad, unsigned char **secdata)
  * data retrieved by this function will depend on the mode set with the
  * nfq_set_mode() function.
  *
- * \return -1 on error, otherwise > 0.
+ * \return -1 on error, > 0 otherwise.
  */
 EXPORT_SYMBOL
 int nfq_get_payload(struct nfq_data *nfad, unsigned char **data)
 {
-	*data = (unsigned char *)
-		nfnl_get_pointer_to_data(nfad->data, NFQA_PAYLOAD, char);
+	if (!nfad->data[NFQA_PAYLOAD])
+		return -1;
+
+	*data = mnl_attr_get_payload(nfad->data[NFQA_PAYLOAD]);
 	if (*data)
-		return NFA_PAYLOAD(nfad->data[NFQA_PAYLOAD-1]);
+		return mnl_attr_get_payload_len(nfad->data[NFQA_PAYLOAD]);
 
 	return -1;
 }
@@ -1385,7 +1484,11 @@ do {								\
 } while (0)
 
 /**
- * \defgroup Printing Printing [DEPRECATED]
+ * \defgroup Printing Printing [nfnl API]
+ *
+ * \warning
+ * This page describes functions from the old nfnl API.
+ * Consider using the mnl API for new projects.
  *
  * \manonly
 .SH SYNOPSIS
@@ -1415,11 +1518,12 @@ do {								\
  *	- NFQ_XML_TIME: include the timestamp
  *	- NFQ_XML_ALL: include all the logging information (all flags set)
  *
- * You can combine this flags with an binary OR.
+ * You can combine these flags with a bitwise OR.
  *
- * \return -1 in case of failure, otherwise the length of the string that
+ * \return -1 on error, otherwise the length of the string that
  * would have been printed into the buffer (in case that there is enough
- * room in it). See snprintf() return value for more information.
+ * room in it).
+ * \sa __snprintf__(3)
  */
 EXPORT_SYMBOL
 int nfq_snprintf_xml(char *buf, size_t rem, struct nfq_data *tb, int flags)
diff --git a/src/nlmsg.c b/src/nlmsg.c
index 39fd12d..4a482b3 100644
--- a/src/nlmsg.c
+++ b/src/nlmsg.c
@@ -26,7 +26,7 @@
 #include "internal.h"
 
 /**
- * \defgroup nfq_verd Verdict helpers
+ * \defgroup nfq_verdict Verdict helpers [mnl API]
  *
  * \manonly
 .SH SYNOPSIS
@@ -62,7 +62,7 @@
  * 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,
+ * queue number *new_queue*. As with the command-line <b>QUEUE STATEMENT</b>
  * 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:
@@ -71,12 +71,10 @@
 	       NF_VERDICT_FLAG_QUEUE_BYPASS);
 \endverbatim
  *
- * 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).
+ * See
+ * \ref nfq6 "nfq6.c"
+ * for an example of how to use this function in context:
+ * search the code for `tests[5]` (2nd occurrence).
  */
 EXPORT_SYMBOL
 void nfq_nlmsg_verdict_put(struct nlmsghdr *nlh, int id, int verdict)
@@ -114,28 +112,12 @@ EXPORT_SYMBOL
  * 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
+ * \note
+ * nfq_nlmsg_verdict_put_pkt copies the IP datagram to your output buffer.
+ * To avoid this packet copy, you may like to consider using **sendmsg**(2)
+ * much as nfq_set_verdict() does (scroll back to find <b>__set_verdict</b>()
+ * which does the work).
+ *
  */
 void nfq_nlmsg_verdict_put_pkt(struct nlmsghdr *nlh, const void *pkt,
 			       uint32_t plen)
@@ -148,7 +130,7 @@ void nfq_nlmsg_verdict_put_pkt(struct nlmsghdr *nlh, const void *pkt,
  */
 
 /**
- * \defgroup nfq_cfg Config helpers
+ * \defgroup nfq_cfg Config helpers [mnl API}
  *
  * \manonly
 .SH SYNOPSIS
@@ -223,7 +205,7 @@ void nfq_nlmsg_cfg_put_qmaxlen(struct nlmsghdr *nlh, uint32_t queue_maxlen)
  */
 
 /**
- * \defgroup nlmsg Netlink message helper functions
+ * \defgroup nlmsg Netlink message helper functions [mnl API]
  *
  * \manonly
 .SH SYNOPSIS
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* Re: [PATCH libnetfilter_queue 1/1] Convert libnetfilter_queue to use entirely libmnl functions
  2024-02-13 21:07 ` [PATCH libnetfilter_queue 1/1] " Duncan Roe
@ 2024-02-14 10:47   ` Pablo Neira Ayuso
  2024-02-16  1:41     ` Duncan Roe
                       ` (33 more replies)
  0 siblings, 34 replies; 39+ messages in thread
From: Pablo Neira Ayuso @ 2024-02-14 10:47 UTC (permalink / raw)
  To: Duncan Roe; +Cc: netfilter-devel

Hi Duncan,

On Wed, Feb 14, 2024 at 08:07:06AM +1100, Duncan Roe wrote:
> And no libnfnetlink headers either.
> Submitted as a single patch because the first change essentially broke
> it until the job was nearly finished.

This is too large. Can you start with smaller chunks?

For example, use mnl_attr_get_*(), then pick the next target
incrementally, so there is a chance of evaluating what could break,
because this conversion to libmnl _cannot_ break existing userspace
applications, that's the challenge.

> diff --git a/src/iftable.c b/src/iftable.c
> new file mode 100644
> index 0000000..d0ee7dd
> --- /dev/null
> +++ b/src/iftable.c

There is a iftable implementation that has been working for years with
no bug reports:

http://git.netfilter.org/nftables/tree/src/iface.c

It coiuld be reused for this purpose, this could be your second patch
after the one your suggest above.

Thanks.

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH libnetfilter_queue 1/1] Convert libnetfilter_queue to use entirely libmnl functions
  2024-02-14 10:47   ` Pablo Neira Ayuso
@ 2024-02-16  1:41     ` Duncan Roe
  2024-02-19  2:27       ` Duncan Roe
  2024-03-19 23:58       ` [PATCH libnetfilter_queue 00/32] Convert libnetfilter_queue to not need libnfnetlink Duncan Roe
  2024-03-15  7:33     ` Duncan Roe
                       ` (32 subsequent siblings)
  33 siblings, 2 replies; 39+ messages in thread
From: Duncan Roe @ 2024-02-16  1:41 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: Netfilter Development

Hi Pablo,

On Wed, Feb 14, 2024 at 11:47:30AM +0100, Pablo Neira Ayuso wrote:
> Hi Duncan,
[...]
> because this conversion to libmnl _cannot_ break existing userspace
> applications, that's the challenge.
>
Absolutely. utils/nfqnl_test.c builds and runs, are there any other examples I
could try?

Userspace applications *will* break if they either

1. Call libnfnetlink nfnl_* functions directly (other than nfnl_rcvbufsiz())

  OR

2. Call nfq_open_nfnl()

Is that acceptable?

Cheers ... Duncan.

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH libnetfilter_queue 1/1] Convert libnetfilter_queue to use entirely libmnl functions
  2024-02-16  1:41     ` Duncan Roe
@ 2024-02-19  2:27       ` Duncan Roe
  2024-03-19 23:58       ` [PATCH libnetfilter_queue 00/32] Convert libnetfilter_queue to not need libnfnetlink Duncan Roe
  1 sibling, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-02-19  2:27 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: Netfilter Development

Hi Pablo,

On Fri, Feb 16, 2024 at 12:41:35PM +1100, Duncan Roe wrote:
[...]
> Userspace applications *will* break if they either
>
> 1. Call libnfnetlink nfnl_* functions directly (other than nfnl_rcvbufsiz())
>
>   OR
>
> 2. Call nfq_open_nfnl()
>
> Is that acceptable?
>
If it's not acceptable then there's no point in my spending any more time on it.

So I won't post again until you reply.

Cheers ... Duncan.

^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 00/32] Convert libnetfilter_queue to not need libnfnetlink
  2024-02-14 10:47   ` Pablo Neira Ayuso
  2024-02-16  1:41     ` Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 01/32] src: Convert nfq_open() to use libmnl Duncan Roe
                       ` (31 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

Hi Pablo,

On Wed, Feb 14, 2024 at 11:47:30AM +0100, Pablo Neira Ayuso wrote:
> Hi Duncan,
>
> On Wed, Feb 14, 2024 at 08:07:06AM +1100, Duncan Roe wrote:
> > And no libnfnetlink headers either.
> > Submitted as a single patch because the first change essentially broke
> > it until the job was nearly finished.
>
> This is too large. Can you start with smaller chunks?
>
> For example, use mnl_attr_get_*(), then pick the next target
> incrementally, so there is a chance of evaluating what could break,
> because this conversion to libmnl _cannot_ break existing userspace
> applications, that's the challenge.
>
[SNIP}

This series is a re-spin of
 "Convert libnetfilter_queue to use entirely libmnl functions".

This time, I managed to convert nfq_open_nfnl(). Existing userspace
applications that use nfq_open_nfnl() or any other functions from
libnfnetlink should continue to run just fine.

However many patches you apply, the library will keep working with the
unpatched functions using libnfnetlink.

To assist with patch review, these patches don't contain any documentation
updates except for the nlif subsystem. I have plenty of documentation
updates ready to go but can defer them until you have committed the code.

Cheers ... Duncan.

Duncan Roe (32):
  src: Convert nfq_open() to use libmnl
  src: Convert nfq_open_nfnl() to use libmnl
  src: Convert nfq_close() to use libmnl
  src: Convert nfq_create_queue(), nfq_bind_pf() & nfq_unbind_pf() to
    use libmnl
  src: Convert nfq_set_queue_flags() & nfq_set_queue_maxlen() to use
    libmnl
  src: Convert nfq_handle_packet(), nfq_get_secctx(), nfq_get_payload()
    and all the nfq_get_ functions to use libmnl
  src: Convert nfq_set_verdict() and nfq_set_verdict2() to use libmnl if
    there is no data
  src: Incorporate nfnl_rcvbufsiz() in libnetfilter_queue
  src: Convert nfq_fd() to use libmnl
  src: Convert remaining nfq_* functions to use libmnl
  src: Fix checkpatch whitespace and block comment warnings
  src: Copy nlif-related code from libnfnetlink
  include: Cherry-pick macros and functions that nlif will need
  doc: Add linux_list.h to the doxygen system
  doc: Eliminate doxygen warnings from linux_list.h
  doc: Eliminate doxygen warnings from iftable.c
  whitespace: remove trailing spaces from iftable.c
  include: Use libmnl.h instead of libnfnetlink.h
  src: Convert all nlif_* functions to use libmnl
  src: Delete rtnl.c
  build: Remove libnfnetlink from the build
  include: Remove the last remaining use of a libnfnetlink header
  doc: Get doxygen to document useful static inline functions
  doc: SYNOPSIS of linux_list.h nominates
    libnetfilter_queue/libnetfilter_queue.h
  doc: Move nlif usage description from libnetfilter_queue.c to
    iftable.c
  build: Shave some time off build
  doc: Resolve most issues with man page generated from linux_list.h
  build: Get real & user times back to what they were
  doc: Cater for doxygen variants w.r.t. #define stmts
  doc: Fix list_empty() doxygen comments
  src: Use a cast in place of convoluted construct
  whitespace: Fix more checkpatch errors & warnings

 Make_global.am                                |   2 +-
 configure.ac                                  |   1 -
 doxygen/Makefile.am                           |   5 +
 doxygen/build_man.sh                          |  44 +-
 doxygen/doxygen.cfg.in                        |  11 +-
 .../libnetfilter_queue/libnetfilter_queue.h   |  39 +-
 include/libnetfilter_queue/linux_list.h       | 192 +++++++
 .../linux_nfnetlink_queue.h                   |   3 +-
 libnetfilter_queue.pc.in                      |   2 -
 src/Makefile.am                               |   3 +-
 src/iftable.c                                 | 376 +++++++++++++
 src/libnetfilter_queue.c                      | 515 +++++++++++-------
 12 files changed, 987 insertions(+), 206 deletions(-)
 create mode 100644 include/libnetfilter_queue/linux_list.h
 create mode 100644 src/iftable.c

-- 
2.35.8


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 01/32] src: Convert nfq_open() to use libmnl
  2024-02-14 10:47   ` Pablo Neira Ayuso
  2024-02-16  1:41     ` Duncan Roe
  2024-03-15  7:33     ` Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 02/32] src: Convert nfq_open_nfnl() " Duncan Roe
                       ` (30 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

Add copies of nfnl_handle, nfnl_subsys_handle & mnl_socket to
libnetfilter_queue.c. After calling mnl_socket_open() & mnl_socket_bind(),
it fills in the libnfnetlink structs as if nfnl_open() had been called.
The rest of the system still uses libnfnetlink functions but they keep
working.
Where possible, code is copied exactly from libnfnetlink. checkpatch
warns about space before tabs but these warnings are addressed in a
later commit.

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 doxygen/doxygen.cfg.in   |  3 ++
 src/libnetfilter_queue.c | 88 ++++++++++++++++++++++++++++++++++++----
 2 files changed, 82 insertions(+), 9 deletions(-)

diff --git a/doxygen/doxygen.cfg.in b/doxygen/doxygen.cfg.in
index 97174ff..6dd7017 100644
--- a/doxygen/doxygen.cfg.in
+++ b/doxygen/doxygen.cfg.in
@@ -13,6 +13,9 @@ EXCLUDE_SYMBOLS        = EXPORT_SYMBOL \
                          nfq_handle \
                          nfq_data \
                          nfq_q_handle \
+                         nfnl_handle \
+                         nfnl_subsys_handle \
+                         mnl_socket \
                          tcp_flag_word
 EXAMPLE_PATTERNS       =
 INPUT_FILTER           = "sed 's/EXPORT_SYMBOL//g'"
diff --git a/src/libnetfilter_queue.c b/src/libnetfilter_queue.c
index bf67a19..2aba68d 100644
--- a/src/libnetfilter_queue.c
+++ b/src/libnetfilter_queue.c
@@ -31,6 +31,7 @@
 #include <sys/socket.h>
 #include <linux/netfilter/nfnetlink_queue.h>
 
+#include <libmnl/libmnl.h>
 #include <libnfnetlink/libnfnetlink.h>
 #include <libnetfilter_queue/libnetfilter_queue.h>
 #include "internal.h"
@@ -134,11 +135,43 @@ gcc -g3 -ggdb -Wall -lmnl -lnetfilter_queue -o nf-queue nf-queue.c
  * burst
  */
 
+/* Copy of private libnfnetlink structures */
+
+#define NFNL_MAX_SUBSYS 16
+
+struct nfnl_subsys_handle {
+	struct nfnl_handle 	*nfnlh;
+	uint32_t		subscriptions;
+	uint8_t			subsys_id;
+	uint8_t			cb_count;
+	struct nfnl_callback 	*cb;	/* array of callbacks */
+};
+
+struct nfnl_handle {
+	int			fd;
+	struct sockaddr_nl	local;
+	struct sockaddr_nl	peer;
+	uint32_t		subscriptions;
+	uint32_t		seq;
+	uint32_t		dump;
+	uint32_t		rcv_buffer_size;	/* for nfnl_catch */
+	uint32_t		flags;
+	struct nlmsghdr 	*last_nlhdr;
+	struct nfnl_subsys_handle subsys[NFNL_MAX_SUBSYS+1];
+};
+
+/* Copy of private libmnl structure */
+struct mnl_socket {
+	int 			fd;
+	struct sockaddr_nl	addr;
+};
+
 struct nfq_handle
 {
 	struct nfnl_handle *nfnlh;
 	struct nfnl_subsys_handle *nfnlssh;
 	struct nfq_q_handle *qh_list;
+	struct mnl_socket *nl;
 };
 
 struct nfq_q_handle
@@ -383,20 +416,57 @@ int nfq_fd(struct nfq_handle *h)
 EXPORT_SYMBOL
 struct nfq_handle *nfq_open(void)
 {
-	struct nfnl_handle *nfnlh = nfnl_open();
-	struct nfq_handle *qh;
+	struct nfnl_callback pkt_cb = {
+		.call		= __nfq_rcv_pkt,
+		.attr_count	= NFQA_MAX,
+	};
+	struct nfq_handle *h = malloc(sizeof(*h));
 
-	if (!nfnlh)
+	if (!h)
 		return NULL;
+	memset(h, 0, sizeof(*h));
 
-	/* unset netlink sequence tracking by default */
-	nfnl_unset_sequence_tracking(nfnlh);
+	h->nfnlh = malloc(sizeof(*h->nfnlh));
+	if (!h->nfnlh)
+		goto err_free;
+	memset(h->nfnlh, 0, sizeof(*h->nfnlh));
+
+	h->nl = mnl_socket_open(NETLINK_NETFILTER);
+	if (!h->nl)
+		goto err_free2;
+
+	if (mnl_socket_bind(h->nl, 0, MNL_SOCKET_AUTOPID) < 0)
+		goto err_close;
+
+	/* Fill in nfnl handle */
+	h->nfnlh->fd = h->nl->fd;
+	h->nfnlh->local = h->nl->addr;
+	h->nfnlh->peer.nl_family = AF_NETLINK;
+	//h->nfnlh->seq = time(NULL);
+	h->nfnlh->rcv_buffer_size = NFNL_BUFFSIZE;
+
+	/* Fill in nfnl subsys handle with code adapted from libnfnetlink */
+	h->nfnlssh = &h->nfnlh->subsys[NFNL_SUBSYS_QUEUE];
+	h->nfnlssh->cb = calloc(NFQNL_MSG_MAX, sizeof(*(h->nfnlssh->cb)));
+	if (!h->nfnlssh->cb)
+		goto err_close;
+
+	h->nfnlssh->nfnlh = h->nfnlh;
+	h->nfnlssh->cb_count = NFQNL_MSG_MAX;
+	h->nfnlssh->subscriptions = 0;
+	h->nfnlssh->subsys_id = NFNL_SUBSYS_QUEUE;
+	pkt_cb.data = h;
+	memcpy(&h->nfnlssh->cb[NFQNL_MSG_PACKET], &pkt_cb, sizeof(pkt_cb));
 
-	qh = nfq_open_nfnl(nfnlh);
-	if (!qh)
-		nfnl_close(nfnlh);
+	return h;
 
-	return qh;
+err_close:
+	mnl_socket_close(h->nl);
+err_free2:
+	free(h->nfnlh);
+err_free:
+	free(h);
+	return NULL;
 }
 
 /**
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 02/32] src: Convert nfq_open_nfnl() to use libmnl
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (2 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 01/32] src: Convert nfq_open() to use libmnl Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 03/32] src: Convert nfq_close() " Duncan Roe
                       ` (29 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

Use copies of private libnfnetlink and libmnl structs to move required info
from one to the other.
Move (now) common code in nfq_open() and nfq_open_nfnl() to static
fill_nfnl_subsys_handle().

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 src/libnetfilter_queue.c | 64 ++++++++++++++++++++--------------------
 1 file changed, 32 insertions(+), 32 deletions(-)

diff --git a/src/libnetfilter_queue.c b/src/libnetfilter_queue.c
index 2aba68d..03c56ca 100644
--- a/src/libnetfilter_queue.c
+++ b/src/libnetfilter_queue.c
@@ -403,6 +403,27 @@ int nfq_fd(struct nfq_handle *h)
  * @{
  */
 
+static bool fill_nfnl_subsys_handle(struct nfq_handle *h)
+{
+	struct nfnl_callback pkt_cb = {
+		.call		= __nfq_rcv_pkt,
+		.attr_count	= NFQA_MAX,
+	};
+
+	/* Fill in nfnl subsys handle with code adapted from libnfnetlink */
+	h->nfnlssh = &h->nfnlh->subsys[NFNL_SUBSYS_QUEUE];
+	h->nfnlssh->cb = calloc(NFQNL_MSG_MAX, sizeof(*(h->nfnlssh->cb)));
+	if (!h->nfnlssh->cb)
+		return false;
+	h->nfnlssh->nfnlh = h->nfnlh;
+	h->nfnlssh->cb_count = NFQNL_MSG_MAX;
+	h->nfnlssh->subscriptions = 0;
+	h->nfnlssh->subsys_id = NFNL_SUBSYS_QUEUE;
+	pkt_cb.data = h;
+	memcpy(&h->nfnlssh->cb[NFQNL_MSG_PACKET], &pkt_cb, sizeof(pkt_cb));
+	return true;
+}
+
 /**
  * nfq_open - open a nfqueue handler
  *
@@ -416,10 +437,6 @@ int nfq_fd(struct nfq_handle *h)
 EXPORT_SYMBOL
 struct nfq_handle *nfq_open(void)
 {
-	struct nfnl_callback pkt_cb = {
-		.call		= __nfq_rcv_pkt,
-		.attr_count	= NFQA_MAX,
-	};
 	struct nfq_handle *h = malloc(sizeof(*h));
 
 	if (!h)
@@ -442,22 +459,11 @@ struct nfq_handle *nfq_open(void)
 	h->nfnlh->fd = h->nl->fd;
 	h->nfnlh->local = h->nl->addr;
 	h->nfnlh->peer.nl_family = AF_NETLINK;
-	//h->nfnlh->seq = time(NULL);
 	h->nfnlh->rcv_buffer_size = NFNL_BUFFSIZE;
 
-	/* Fill in nfnl subsys handle with code adapted from libnfnetlink */
-	h->nfnlssh = &h->nfnlh->subsys[NFNL_SUBSYS_QUEUE];
-	h->nfnlssh->cb = calloc(NFQNL_MSG_MAX, sizeof(*(h->nfnlssh->cb)));
-	if (!h->nfnlssh->cb)
+	if (!fill_nfnl_subsys_handle(h))
 		goto err_close;
 
-	h->nfnlssh->nfnlh = h->nfnlh;
-	h->nfnlssh->cb_count = NFQNL_MSG_MAX;
-	h->nfnlssh->subscriptions = 0;
-	h->nfnlssh->subsys_id = NFNL_SUBSYS_QUEUE;
-	pkt_cb.data = h;
-	memcpy(&h->nfnlssh->cb[NFQNL_MSG_PACKET], &pkt_cb, sizeof(pkt_cb));
-
 	return h;
 
 err_close:
@@ -473,6 +479,7 @@ err_free:
  * @}
  */
 
+#define NFNL_F_SEQTRACK_ENABLED             (1 << 0)
 /**
  * nfq_open_nfnl - open a nfqueue handler from a existing nfnetlink handler
  * \param nfnlh Netfilter netlink connection handle obtained by calling nfnl_open()
@@ -486,12 +493,7 @@ err_free:
 EXPORT_SYMBOL
 struct nfq_handle *nfq_open_nfnl(struct nfnl_handle *nfnlh)
 {
-	struct nfnl_callback pkt_cb = {
-		.call		= __nfq_rcv_pkt,
-		.attr_count	= NFQA_MAX,
-	};
 	struct nfq_handle *h;
-	int err;
 
 	h = malloc(sizeof(*h));
 	if (!h)
@@ -499,24 +501,22 @@ struct nfq_handle *nfq_open_nfnl(struct nfnl_handle *nfnlh)
 
 	memset(h, 0, sizeof(*h));
 	h->nfnlh = nfnlh;
+	h->nfnlh->seq = 0;
+	h->nfnlh->flags &= ~NFNL_F_SEQTRACK_ENABLED;
 
-	h->nfnlssh = nfnl_subsys_open(h->nfnlh, NFNL_SUBSYS_QUEUE,
-				      NFQNL_MSG_MAX, 0);
-	if (!h->nfnlssh) {
-		/* FIXME: nfq_errno */
+	h->nl = malloc(sizeof(*h->nl));
+	if (!h->nl)
 		goto out_free;
-	}
+	memset(h->nl, 0, sizeof(*h->nl));
+	h->nl->fd = h->nfnlh->fd;
+	h->nl->addr = h->nfnlh->local;
 
-	pkt_cb.data = h;
-	err = nfnl_callback_register(h->nfnlssh, NFQNL_MSG_PACKET, &pkt_cb);
-	if (err < 0) {
-		nfq_errno = err;
+	if (!fill_nfnl_subsys_handle(h))
 		goto out_close;
-	}
 
 	return h;
 out_close:
-	nfnl_subsys_close(h->nfnlssh);
+	mnl_socket_close(h->nl);
 out_free:
 	free(h);
 	return NULL;
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 03/32] src: Convert nfq_close() to use libmnl
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (3 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 02/32] src: Convert nfq_open_nfnl() " Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 04/32] src: Convert nfq_create_queue(), nfq_bind_pf() & nfq_unbind_pf() " Duncan Roe
                       ` (28 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 src/libnetfilter_queue.c | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/src/libnetfilter_queue.c b/src/libnetfilter_queue.c
index 03c56ca..db31446 100644
--- a/src/libnetfilter_queue.c
+++ b/src/libnetfilter_queue.c
@@ -550,12 +550,20 @@ out_free:
 EXPORT_SYMBOL
 int nfq_close(struct nfq_handle *h)
 {
-	int ret;
+	struct nfq_q_handle *qh;
 
-	ret = nfnl_close(h->nfnlh);
-	if (ret == 0)
-		free(h);
-	return ret;
+	mnl_socket_close(h->nl);
+
+	while (h->qh_list) {
+		qh = h->qh_list;
+		h->qh_list = qh->next;
+		free(qh);
+	}
+	free(h->nfnlssh->cb);
+	free(h->nfnlh);
+	free(h);
+
+	return 0;
 }
 
 /**
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 04/32] src: Convert nfq_create_queue(), nfq_bind_pf() & nfq_unbind_pf() to use libmnl
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (4 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 03/32] src: Convert nfq_close() " Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 05/32] src: Convert nfq_set_queue_flags() & nfq_set_queue_maxlen() " Duncan Roe
                       ` (27 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

Convert static function __build_send_cfg_msg() to use libmnl.
This by itself converts the 3 public functions.

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 src/libnetfilter_queue.c | 32 +++++++++++++++++++-------------
 1 file changed, 19 insertions(+), 13 deletions(-)

diff --git a/src/libnetfilter_queue.c b/src/libnetfilter_queue.c
index db31446..1ef6fb8 100644
--- a/src/libnetfilter_queue.c
+++ b/src/libnetfilter_queue.c
@@ -227,27 +227,33 @@ static struct nfq_q_handle *find_qh(struct nfq_handle *h, uint16_t id)
 	return NULL;
 }
 
+static int nfq_query(struct nfq_handle *h, struct nlmsghdr *nlh, char *buf,
+		      size_t bufsiz)
+{
+	int ret;
+
+	ret = mnl_socket_sendto(h->nl, nlh, nlh->nlmsg_len);
+	if (ret != -1)
+		ret = mnl_socket_recvfrom(h->nl, buf, bufsiz);
+	if (ret != -1)
+		ret = mnl_cb_run(buf, ret, 0, mnl_socket_get_portid(h->nl),
+				 NULL, NULL);
+	return ret;
+}
+
 /* build a NFQNL_MSG_CONFIG message */
 	static int
 __build_send_cfg_msg(struct nfq_handle *h, uint8_t command,
 		uint16_t queuenum, uint16_t pf)
 {
-	union {
-		char buf[NFNL_HEADER_LEN
-			+NFA_LENGTH(sizeof(struct nfqnl_msg_config_cmd))];
-		struct nlmsghdr nmh;
-	} u;
-	struct nfqnl_msg_config_cmd cmd;
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
 
-	nfnl_fill_hdr(h->nfnlssh, &u.nmh, 0, AF_UNSPEC, queuenum,
-			NFQNL_MSG_CONFIG, NLM_F_REQUEST|NLM_F_ACK);
+	nlh = nfq_nlmsg_put2(buf, NFQNL_MSG_CONFIG, queuenum, NLM_F_ACK);
 
-	cmd._pad = 0;
-	cmd.command = command;
-	cmd.pf = htons(pf);
-	nfnl_addattr_l(&u.nmh, sizeof(u), NFQA_CFG_CMD, &cmd, sizeof(cmd));
+	nfq_nlmsg_cfg_put_cmd(nlh, AF_UNSPEC, command);
 
-	return nfnl_query(h->nfnlh, &u.nmh);
+	return nfq_query(h, nlh, buf, sizeof(buf));
 }
 
 static int __nfq_rcv_pkt(struct nlmsghdr *nlh, struct nfattr *nfa[],
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 05/32] src: Convert nfq_set_queue_flags() & nfq_set_queue_maxlen() to use libmnl
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (5 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 04/32] src: Convert nfq_create_queue(), nfq_bind_pf() & nfq_unbind_pf() " Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 06/32] src: Convert nfq_handle_packet(), nfq_get_secctx(), nfq_get_payload() and all the nfq_get_ functions " Duncan Roe
                       ` (26 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

Use a buffer of MNL_SOCKET_BUFFER_SIZE; no union required.

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 src/libnetfilter_queue.c | 33 +++++++++++----------------------
 1 file changed, 11 insertions(+), 22 deletions(-)

diff --git a/src/libnetfilter_queue.c b/src/libnetfilter_queue.c
index 1ef6fb8..28aa771 100644
--- a/src/libnetfilter_queue.c
+++ b/src/libnetfilter_queue.c
@@ -831,23 +831,18 @@ int nfq_set_mode(struct nfq_q_handle *qh, uint8_t mode, uint32_t range)
 EXPORT_SYMBOL
 int nfq_set_queue_flags(struct nfq_q_handle *qh, uint32_t mask, uint32_t flags)
 {
-	union {
-		char buf[NFNL_HEADER_LEN
-			+NFA_LENGTH(sizeof(mask)
-			+NFA_LENGTH(sizeof(flags)))];
-		struct nlmsghdr nmh;
-	} u;
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
 
 	mask = htonl(mask);
 	flags = htonl(flags);
 
-	nfnl_fill_hdr(qh->h->nfnlssh, &u.nmh, 0, AF_UNSPEC, qh->id,
-		      NFQNL_MSG_CONFIG, NLM_F_REQUEST|NLM_F_ACK);
+	nlh = nfq_nlmsg_put2(buf, NFQNL_MSG_CONFIG, qh->id, NLM_F_ACK);
 
-	nfnl_addattr32(&u.nmh, sizeof(u), NFQA_CFG_FLAGS, flags);
-	nfnl_addattr32(&u.nmh, sizeof(u), NFQA_CFG_MASK, mask);
+	mnl_attr_put_u32(nlh, NFQA_CFG_FLAGS, flags);
+	mnl_attr_put_u32(nlh, NFQA_CFG_MASK, mask);
 
-	return nfnl_query(qh->h->nfnlh, &u.nmh);
+	return nfq_query(qh->h, nlh, buf, sizeof(buf));
 }
 
 /**
@@ -864,20 +859,14 @@ int nfq_set_queue_flags(struct nfq_q_handle *qh, uint32_t mask, uint32_t flags)
 EXPORT_SYMBOL
 int nfq_set_queue_maxlen(struct nfq_q_handle *qh, uint32_t queuelen)
 {
-	union {
-		char buf[NFNL_HEADER_LEN
-			+NFA_LENGTH(sizeof(struct nfqnl_msg_config_params))];
-		struct nlmsghdr nmh;
-	} u;
-	uint32_t queue_maxlen = htonl(queuelen);
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
 
-	nfnl_fill_hdr(qh->h->nfnlssh, &u.nmh, 0, AF_UNSPEC, qh->id,
-			NFQNL_MSG_CONFIG, NLM_F_REQUEST|NLM_F_ACK);
+	nlh = nfq_nlmsg_put2(buf, NFQNL_MSG_CONFIG, qh->id, NLM_F_ACK);
 
-	nfnl_addattr_l(&u.nmh, sizeof(u), NFQA_CFG_QUEUE_MAXLEN, &queue_maxlen,
-			sizeof(queue_maxlen));
+	mnl_attr_put_u32(nlh, NFQA_CFG_QUEUE_MAXLEN, htonl(queuelen));
 
-	return nfnl_query(qh->h->nfnlh, &u.nmh);
+	return nfq_query(qh->h, nlh, buf, sizeof(buf));
 }
 
 /**
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 06/32] src: Convert nfq_handle_packet(), nfq_get_secctx(), nfq_get_payload() and all the nfq_get_ functions to use libmnl
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (6 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 05/32] src: Convert nfq_set_queue_flags() & nfq_set_queue_maxlen() " Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 07/32] src: Convert nfq_set_verdict() and nfq_set_verdict2() to use libmnl if there is no data Duncan Roe
                       ` (25 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

The opaque struct nfq_data is now an array of struct nlattr instead of
struct nfattr.

Because of using mnl_attr_parse(), the first array element is for
attribute 0 instaed of attribute 1 as previously. Because of this,
all the nfq_get_ functions have to be converted for this commit.

Functions now using libmnl exclusively: nfq_get_msg_packet_hdr(),
nfq_get_nfmark(), nfq_get_timestamp(), nfq_get_indev(),
nfq_get_physindev(), nfq_get_outdev(), nfq_get_physoutdev(),
nfqnl_msg_packet_hw(), nfq_get_uid() & nfq_get_gid().

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 doxygen/doxygen.cfg.in   |   1 +
 src/libnetfilter_queue.c | 122 +++++++++++++++++++++++++++++----------
 2 files changed, 91 insertions(+), 32 deletions(-)

diff --git a/doxygen/doxygen.cfg.in b/doxygen/doxygen.cfg.in
index 6dd7017..fcfc045 100644
--- a/doxygen/doxygen.cfg.in
+++ b/doxygen/doxygen.cfg.in
@@ -16,6 +16,7 @@ EXCLUDE_SYMBOLS        = EXPORT_SYMBOL \
                          nfnl_handle \
                          nfnl_subsys_handle \
                          mnl_socket \
+                         nfnl_callback2 \
                          tcp_flag_word
 EXAMPLE_PATTERNS       =
 INPUT_FILTER           = "sed 's/EXPORT_SYMBOL//g'"
diff --git a/src/libnetfilter_queue.c b/src/libnetfilter_queue.c
index 28aa771..58875b1 100644
--- a/src/libnetfilter_queue.c
+++ b/src/libnetfilter_queue.c
@@ -32,6 +32,13 @@
 #include <linux/netfilter/nfnetlink_queue.h>
 
 #include <libmnl/libmnl.h>
+
+/* Use the real header since libnfnetlink is going away. */
+/* nfq_pkt_parse_attr_cb only knows attribates up to NFQA_SECCTX */
+/* so won't try to validate higher-numbered attrs but will store them. */
+/* mnl API programs will then be able to access them. */
+#include <linux/netfilter/nfnetlink.h>
+
 #include <libnfnetlink/libnfnetlink.h>
 #include <libnetfilter_queue/libnetfilter_queue.h>
 #include "internal.h"
@@ -166,6 +173,13 @@ struct mnl_socket {
 	struct sockaddr_nl	addr;
 };
 
+/* Amended callback prototype */
+struct nfnl_callback2 {
+	int (*call)(struct nlmsghdr *nlh, struct nlattr *nfa[], void *data);
+	void *data;
+	uint16_t attr_count;
+};
+
 struct nfq_handle
 {
 	struct nfnl_handle *nfnlh;
@@ -185,7 +199,7 @@ struct nfq_q_handle
 };
 
 struct nfq_data {
-	struct nfattr **data;
+	struct nlattr **data;
 };
 
 EXPORT_SYMBOL int nfq_errno;
@@ -256,7 +270,7 @@ __build_send_cfg_msg(struct nfq_handle *h, uint8_t command,
 	return nfq_query(h, nlh, buf, sizeof(buf));
 }
 
-static int __nfq_rcv_pkt(struct nlmsghdr *nlh, struct nfattr *nfa[],
+static int __nfq_rcv_pkt(struct nlmsghdr *nlh, struct nlattr *nfa[],
 		void *data)
 {
 	struct nfgenmsg *nfmsg = NLMSG_DATA(nlh);
@@ -411,7 +425,7 @@ int nfq_fd(struct nfq_handle *h)
 
 static bool fill_nfnl_subsys_handle(struct nfq_handle *h)
 {
-	struct nfnl_callback pkt_cb = {
+	struct nfnl_callback2 pkt_cb = {
 		.call		= __nfq_rcv_pkt,
 		.attr_count	= NFQA_MAX,
 	};
@@ -610,6 +624,25 @@ int nfq_unbind_pf(struct nfq_handle *h, uint16_t pf)
  * @}
  */
 
+ static int __nfq_handle_msg(const struct nlmsghdr *nlh, void *data)
+{
+	struct nfq_handle *h = data;
+	struct nfq_q_handle *qh;
+	struct nlattr *nfa[NFQA_MAX + 1] = {};
+	struct nfq_data nfad = {nfa};
+	struct nfgenmsg *nfmsg = NLMSG_DATA(nlh);
+
+	if (nfq_nlmsg_parse(nlh, nfa) < 0)
+		return MNL_CB_ERROR;
+
+	/* Find our queue handler (to get CB fn) */
+	qh = find_qh(h, ntohs(nfmsg->res_id));
+	if (!qh)
+		return MNL_CB_ERROR;
+
+	return qh->cb(qh, nfmsg, &nfad, qh->data);
+}
+
 /**
  * \addtogroup Queue
  * @{
@@ -721,7 +754,8 @@ int nfq_destroy_queue(struct nfq_q_handle *qh)
 EXPORT_SYMBOL
 int nfq_handle_packet(struct nfq_handle *h, char *buf, int len)
 {
-	return nfnl_handle_packet(h->nfnlh, buf, len);
+	return mnl_cb_run(buf, len, 0, mnl_socket_get_portid(h->nl),
+			  __nfq_handle_msg, h);
 }
 
 /**
@@ -891,7 +925,7 @@ static int __set_verdict(struct nfq_q_handle *qh, uint32_t id,
 
 	/* This must be declared here (and not inside the data
 	 * handling block) because the iovec points to this. */
-	struct nfattr data_attr;
+	struct nlattr data_attr;
 
 	memset(iov, 0, sizeof(iov));
 
@@ -912,15 +946,17 @@ static int __set_verdict(struct nfq_q_handle *qh, uint32_t id,
 	nvecs = 1;
 
 	if (data_len) {
-		/* The typecast here is to cast away data's const-ness: */
-		nfnl_build_nfa_iovec(&iov[1], &data_attr, NFQA_PAYLOAD,
-				data_len, (unsigned char *) data);
+		/* Temporary cast until we get rid of nfnl_build_nfa_iovec() */
+		nfnl_build_nfa_iovec(&iov[1], (struct nfattr *)&data_attr,
+		//nfnl_build_nfa_iovec(&iov[1], &data_attr,
+				     NFQA_PAYLOAD, data_len,
+				     (unsigned char *) data);
 		nvecs += 2;
 		/* Add the length of the appended data to the message
 		 * header.  The size of the attribute is given in the
-		 * nfa_len field and is set in the nfnl_build_nfa_iovec()
+		 * nla_len field and is set in the nfnl_build_nfa_iovec()
 		 * function. */
-		u.nmh.nlmsg_len += data_attr.nfa_len;
+		u.nmh.nlmsg_len += data_attr.nla_len;
 	}
 
 	return nfnl_sendiov(qh->h->nfnlh, iov, nvecs, 0);
@@ -1084,8 +1120,10 @@ int nfq_set_verdict_mark(struct nfq_q_handle *qh, uint32_t id,
 EXPORT_SYMBOL
 struct nfqnl_msg_packet_hdr *nfq_get_msg_packet_hdr(struct nfq_data *nfad)
 {
-	return nfnl_get_pointer_to_data(nfad->data, NFQA_PACKET_HDR,
-					struct nfqnl_msg_packet_hdr);
+	if (!nfad->data[NFQA_PACKET_HDR])
+		return NULL;
+
+	return mnl_attr_get_payload(nfad->data[NFQA_PACKET_HDR]);
 }
 
 /**
@@ -1097,6 +1135,10 @@ struct nfqnl_msg_packet_hdr *nfq_get_msg_packet_hdr(struct nfq_data *nfad)
 EXPORT_SYMBOL
 uint32_t nfq_get_nfmark(struct nfq_data *nfad)
 {
+	if (!nfad->data[NFQA_MARK])
+		return 0;
+
+	return ntohl(mnl_attr_get_u32(nfad->data[NFQA_MARK]));
 	return ntohl(nfnl_get_data(nfad->data, NFQA_MARK, uint32_t));
 }
 
@@ -1113,11 +1155,12 @@ EXPORT_SYMBOL
 int nfq_get_timestamp(struct nfq_data *nfad, struct timeval *tv)
 {
 	struct nfqnl_msg_packet_timestamp *qpt;
-	qpt = nfnl_get_pointer_to_data(nfad->data, NFQA_TIMESTAMP,
-					struct nfqnl_msg_packet_timestamp);
-	if (!qpt)
+
+	if (!nfad->data[NFQA_TIMESTAMP])
 		return -1;
 
+	qpt = mnl_attr_get_payload(nfad->data[NFQA_TIMESTAMP]);
+
 	tv->tv_sec = __be64_to_cpu(qpt->sec);
 	tv->tv_usec = __be64_to_cpu(qpt->usec);
 
@@ -1138,7 +1181,10 @@ int nfq_get_timestamp(struct nfq_data *nfad, struct timeval *tv)
 EXPORT_SYMBOL
 uint32_t nfq_get_indev(struct nfq_data *nfad)
 {
-	return ntohl(nfnl_get_data(nfad->data, NFQA_IFINDEX_INDEV, uint32_t));
+	if (!nfad->data[NFQA_IFINDEX_INDEV])
+		return 0;
+
+	return ntohl(mnl_attr_get_u32(nfad->data[NFQA_IFINDEX_INDEV]));
 }
 
 /**
@@ -1152,7 +1198,10 @@ uint32_t nfq_get_indev(struct nfq_data *nfad)
 EXPORT_SYMBOL
 uint32_t nfq_get_physindev(struct nfq_data *nfad)
 {
-	return ntohl(nfnl_get_data(nfad->data, NFQA_IFINDEX_PHYSINDEV, uint32_t));
+	if (!nfad->data[NFQA_IFINDEX_PHYSINDEV])
+		return 0;
+
+	return ntohl(mnl_attr_get_u32(nfad->data[NFQA_IFINDEX_PHYSINDEV]));
 }
 
 /**
@@ -1166,7 +1215,10 @@ uint32_t nfq_get_physindev(struct nfq_data *nfad)
 EXPORT_SYMBOL
 uint32_t nfq_get_outdev(struct nfq_data *nfad)
 {
-	return ntohl(nfnl_get_data(nfad->data, NFQA_IFINDEX_OUTDEV, uint32_t));
+	if (!nfad->data[NFQA_IFINDEX_OUTDEV])
+		return 0;
+
+	return ntohl(mnl_attr_get_u32(nfad->data[NFQA_IFINDEX_OUTDEV]));
 }
 
 /**
@@ -1182,7 +1234,10 @@ uint32_t nfq_get_outdev(struct nfq_data *nfad)
 EXPORT_SYMBOL
 uint32_t nfq_get_physoutdev(struct nfq_data *nfad)
 {
-	return ntohl(nfnl_get_data(nfad->data, NFQA_IFINDEX_PHYSOUTDEV, uint32_t));
+	if (!nfad->data[NFQA_IFINDEX_PHYSOUTDEV])
+		return 0;
+
+	return ntohl(mnl_attr_get_u32(nfad->data[NFQA_IFINDEX_PHYSOUTDEV]));
 }
 
 /**
@@ -1317,8 +1372,10 @@ int nfq_get_physoutdev_name(struct nlif_handle *nlif_handle,
 EXPORT_SYMBOL
 struct nfqnl_msg_packet_hw *nfq_get_packet_hw(struct nfq_data *nfad)
 {
-	return nfnl_get_pointer_to_data(nfad->data, NFQA_HWADDR,
-					struct nfqnl_msg_packet_hw);
+	if (!nfad->data[NFQA_HWADDR])
+		return NULL;
+
+	return mnl_attr_get_payload(nfad->data[NFQA_HWADDR]);
 }
 
 /**
@@ -1366,10 +1423,10 @@ uint32_t nfq_get_skbinfo(struct nfq_data *nfad)
 EXPORT_SYMBOL
 int nfq_get_uid(struct nfq_data *nfad, uint32_t *uid)
 {
-	if (!nfnl_attr_present(nfad->data, NFQA_UID))
+	if (!nfad->data[NFQA_UID])
 		return 0;
 
-	*uid = ntohl(nfnl_get_data(nfad->data, NFQA_UID, uint32_t));
+	*uid = ntohl(mnl_attr_get_u32(nfad->data[NFQA_UID]));
 	return 1;
 }
 
@@ -1387,10 +1444,10 @@ int nfq_get_uid(struct nfq_data *nfad, uint32_t *uid)
 EXPORT_SYMBOL
 int nfq_get_gid(struct nfq_data *nfad, uint32_t *gid)
 {
-	if (!nfnl_attr_present(nfad->data, NFQA_GID))
+	if (!nfad->data[NFQA_GID])
 		return 0;
 
-	*gid = ntohl(nfnl_get_data(nfad->data, NFQA_GID, uint32_t));
+	*gid = ntohl(mnl_attr_get_u32(nfad->data[NFQA_GID]));
 	return 1;
 }
 
@@ -1408,14 +1465,13 @@ int nfq_get_gid(struct nfq_data *nfad, uint32_t *gid)
 EXPORT_SYMBOL
 int nfq_get_secctx(struct nfq_data *nfad, unsigned char **secdata)
 {
-	if (!nfnl_attr_present(nfad->data, NFQA_SECCTX))
+	if (!nfad->data[NFQA_SECCTX])
 		return -1;
 
-	*secdata = (unsigned char *)nfnl_get_pointer_to_data(nfad->data,
-							NFQA_SECCTX, char);
+	*secdata = mnl_attr_get_payload(nfad->data[NFQA_SECCTX]);
 
 	if (*secdata)
-		return NFA_PAYLOAD(nfad->data[NFQA_SECCTX-1]);
+		return mnl_attr_get_payload_len(nfad->data[NFQA_SECCTX]);
 
 	return 0;
 }
@@ -1434,10 +1490,12 @@ int nfq_get_secctx(struct nfq_data *nfad, unsigned char **secdata)
 EXPORT_SYMBOL
 int nfq_get_payload(struct nfq_data *nfad, unsigned char **data)
 {
-	*data = (unsigned char *)
-		nfnl_get_pointer_to_data(nfad->data, NFQA_PAYLOAD, char);
+	if (!nfad->data[NFQA_PAYLOAD])
+		return -1;
+
+	*data = mnl_attr_get_payload(nfad->data[NFQA_PAYLOAD]);
 	if (*data)
-		return NFA_PAYLOAD(nfad->data[NFQA_PAYLOAD-1]);
+		return mnl_attr_get_payload_len(nfad->data[NFQA_PAYLOAD]);
 
 	return -1;
 }
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 07/32] src: Convert nfq_set_verdict() and nfq_set_verdict2() to use libmnl if there is no data
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (7 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 06/32] src: Convert nfq_handle_packet(), nfq_get_secctx(), nfq_get_payload() and all the nfq_get_ functions " Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 08/32] src: Incorporate nfnl_rcvbufsiz() in libnetfilter_queue Duncan Roe
                       ` (24 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

static __set_verdict() uses mnl-API calls in enough places that the path
for no (mangled) data doesn't use any nfnl-API functions.
With no data, __set_verdict() uses sendto() (faster than sendmsg()).
nfq_set_verdict2() must not use htonl() on the packet mark.
checkpatch Block comments warning is fixed in a later commit.

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 src/libnetfilter_queue.c | 35 ++++++++++++++++-------------------
 1 file changed, 16 insertions(+), 19 deletions(-)

diff --git a/src/libnetfilter_queue.c b/src/libnetfilter_queue.c
index 58875b1..17fe879 100644
--- a/src/libnetfilter_queue.c
+++ b/src/libnetfilter_queue.c
@@ -38,8 +38,8 @@
 /* so won't try to validate higher-numbered attrs but will store them. */
 /* mnl API programs will then be able to access them. */
 #include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_compat.h>
 
-#include <libnfnetlink/libnfnetlink.h>
 #include <libnetfilter_queue/libnetfilter_queue.h>
 #include "internal.h"
 
@@ -912,13 +912,8 @@ static int __set_verdict(struct nfq_q_handle *qh, uint32_t id,
 		uint32_t data_len, const unsigned char *data,
 		enum nfqnl_msg_types type)
 {
-	struct nfqnl_msg_verdict_hdr vh;
-	union {
-		char buf[NFNL_HEADER_LEN
-			+NFA_LENGTH(sizeof(mark))
-			+NFA_LENGTH(sizeof(vh))];
-		struct nlmsghdr nmh;
-	} u;
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
 
 	struct iovec iov[3];
 	int nvecs;
@@ -929,20 +924,22 @@ static int __set_verdict(struct nfq_q_handle *qh, uint32_t id,
 
 	memset(iov, 0, sizeof(iov));
 
-	vh.verdict = htonl(verdict);
-	vh.id = htonl(id);
-
-	nfnl_fill_hdr(qh->h->nfnlssh, &u.nmh, 0, AF_UNSPEC, qh->id,
-				type, NLM_F_REQUEST);
+	nlh = nfq_nlmsg_put(buf, NFQNL_MSG_VERDICT, qh->id);
 
 	/* add verdict header */
-	nfnl_addattr_l(&u.nmh, sizeof(u), NFQA_VERDICT_HDR, &vh, sizeof(vh));
+	nfq_nlmsg_verdict_put(nlh, id, verdict);
 
 	if (set_mark)
-		nfnl_addattr32(&u.nmh, sizeof(u), NFQA_MARK, mark);
+		nfq_nlmsg_verdict_put_mark(nlh, mark);
+
+	/* Efficiency gain: when there is only 1 iov,
+	 * sendto() is faster than sendmsg() because the kernel only has
+	 * 1 userspace address to validate instead of 2. */
+	if (!data_len)
+		return mnl_socket_sendto(qh->h->nl, nlh, nlh->nlmsg_len);
 
-	iov[0].iov_base = &u.nmh;
-	iov[0].iov_len = NLMSG_TAIL(&u.nmh) - (void *)&u.nmh;
+	iov[0].iov_base = nlh;
+	iov[0].iov_len = NLMSG_TAIL(nlh) - (void *)nlh;
 	nvecs = 1;
 
 	if (data_len) {
@@ -956,7 +953,7 @@ static int __set_verdict(struct nfq_q_handle *qh, uint32_t id,
 		 * header.  The size of the attribute is given in the
 		 * nla_len field and is set in the nfnl_build_nfa_iovec()
 		 * function. */
-		u.nmh.nlmsg_len += data_attr.nla_len;
+		nlh->nlmsg_len += data_attr.nla_len;
 	}
 
 	return nfnl_sendiov(qh->h->nfnlh, iov, nvecs, 0);
@@ -1013,7 +1010,7 @@ int nfq_set_verdict2(struct nfq_q_handle *qh, uint32_t id,
 		     uint32_t verdict, uint32_t mark,
 		     uint32_t data_len, const unsigned char *buf)
 {
-	return __set_verdict(qh, id, verdict, htonl(mark), 1, data_len,
+	return __set_verdict(qh, id, verdict, mark, 1, data_len,
 						buf, NFQNL_MSG_VERDICT);
 }
 
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 08/32] src: Incorporate nfnl_rcvbufsiz() in libnetfilter_queue
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (8 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 07/32] src: Convert nfq_set_verdict() and nfq_set_verdict2() to use libmnl if there is no data Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 09/32] src: Convert nfq_fd() to use libmnl Duncan Roe
                       ` (23 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

nfnl_rcvbufsiz() is the first bullet point in the Performance section
of the libnetfilter_queue HTML main page.
We have to assume people have used it,
so supply a version that uses libmnl.

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 .../libnetfilter_queue/libnetfilter_queue.h   |  2 ++
 src/libnetfilter_queue.c                      | 34 +++++++++++++++++++
 2 files changed, 36 insertions(+)

diff --git a/include/libnetfilter_queue/libnetfilter_queue.h b/include/libnetfilter_queue/libnetfilter_queue.h
index f7e68d8..9327f8c 100644
--- a/include/libnetfilter_queue/libnetfilter_queue.h
+++ b/include/libnetfilter_queue/libnetfilter_queue.h
@@ -35,6 +35,8 @@ typedef int  nfq_callback(struct nfq_q_handle *gh, struct nfgenmsg *nfmsg,
 		       struct nfq_data *nfad, void *data);
 
 
+extern unsigned int nfnl_rcvbufsiz(const struct nfnl_handle *h,
+				   unsigned int size);
 extern struct nfq_handle *nfq_open(void);
 extern struct nfq_handle *nfq_open_nfnl(struct nfnl_handle *nfnlh);
 extern int nfq_close(struct nfq_handle *h);
diff --git a/src/libnetfilter_queue.c b/src/libnetfilter_queue.c
index 17fe879..2051aca 100644
--- a/src/libnetfilter_queue.c
+++ b/src/libnetfilter_queue.c
@@ -559,6 +559,40 @@ out_free:
  * @{
  */
 
+/**
+ * nfnl_rcvbufsiz - set the socket buffer size
+ * \param h nfnetlink connection handle obtained via call to \b nfq_nfnlh()
+ * \param size size of the buffer we want to set
+ *
+ * This nfnl-API function sets the new size of the socket buffer.
+ * Use this setting
+ * to increase the socket buffer size if your system is reporting ENOBUFS
+ * errors.
+ *
+ * \return new size of kernel socket buffer
+ */
+
+EXPORT_SYMBOL
+unsigned int nfnl_rcvbufsiz(const struct nfnl_handle *h, unsigned int size)
+{
+	int status;
+	socklen_t socklen = sizeof(size);
+	unsigned int read_size = 0;
+
+	/* first we try the FORCE option, which is introduced in kernel
+	 * 2.6.14 to give "root" the ability to override the system wide
+	 * maximum */
+	status = setsockopt(h->fd, SOL_SOCKET, SO_RCVBUFFORCE, &size, socklen);
+	if (status < 0) {
+		/* if this didn't work, we try at least to get the system
+		 * wide maximum (or whatever the user requested) */
+		setsockopt(h->fd, SOL_SOCKET, SO_RCVBUF, &size, socklen);
+	}
+	getsockopt(h->fd, SOL_SOCKET, SO_RCVBUF, &read_size, &socklen);
+
+	return read_size;
+}
+
 /**
  * nfq_close - close a nfqueue handler
  * \param h Netfilter queue connection handle obtained via call to nfq_open()
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 09/32] src: Convert nfq_fd() to use libmnl
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (9 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 08/32] src: Incorporate nfnl_rcvbufsiz() in libnetfilter_queue Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 10/32] src: Convert remaining nfq_* functions " Duncan Roe
                       ` (22 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

struct nfq_handle has a struct mnl_socket * now, use that.

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 src/libnetfilter_queue.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libnetfilter_queue.c b/src/libnetfilter_queue.c
index 2051aca..5e09eb7 100644
--- a/src/libnetfilter_queue.c
+++ b/src/libnetfilter_queue.c
@@ -381,7 +381,7 @@ struct nfnl_handle *nfq_nfnlh(struct nfq_handle *h)
 EXPORT_SYMBOL
 int nfq_fd(struct nfq_handle *h)
 {
-	return nfnl_fd(nfq_nfnlh(h));
+	return mnl_socket_get_fd(h->nl);
 }
 /**
  * @}
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 10/32] src: Convert remaining nfq_* functions to use libmnl
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (10 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 09/32] src: Convert nfq_fd() to use libmnl Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 11/32] src: Fix checkpatch whitespace and block comment warnings Duncan Roe
                       ` (21 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

Converted: nfq_set_verdict2(), nfq_set_verdict_batch2(),
	   nfq_set_verdict_mark(), nfq_get_nfmark() [again],
	   nfq_get_skbinfo() & nfq_set_mode().
We only use 2 iovecs instead of 3
by tacking the data attribute onto the end of the first iovec buffer.

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 src/libnetfilter_queue.c | 98 ++++++++++++++++++++--------------------
 1 file changed, 50 insertions(+), 48 deletions(-)

diff --git a/src/libnetfilter_queue.c b/src/libnetfilter_queue.c
index 5e09eb7..56d51ca 100644
--- a/src/libnetfilter_queue.c
+++ b/src/libnetfilter_queue.c
@@ -810,22 +810,21 @@ int nfq_handle_packet(struct nfq_handle *h, char *buf, int len)
 EXPORT_SYMBOL
 int nfq_set_mode(struct nfq_q_handle *qh, uint8_t mode, uint32_t range)
 {
-	union {
-		char buf[NFNL_HEADER_LEN
-			+NFA_LENGTH(sizeof(struct nfqnl_msg_config_params))];
-		struct nlmsghdr nmh;
-	} u;
-	struct nfqnl_msg_config_params params;
-
-	nfnl_fill_hdr(qh->h->nfnlssh, &u.nmh, 0, AF_UNSPEC, qh->id,
-			NFQNL_MSG_CONFIG, NLM_F_REQUEST|NLM_F_ACK);
-
-	params.copy_range = htonl(range);
-	params.copy_mode = mode;
-	nfnl_addattr_l(&u.nmh, sizeof(u), NFQA_CFG_PARAMS, &params,
-			sizeof(params));
-
-	return nfnl_query(qh->h->nfnlh, &u.nmh);
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+	int ret;
+
+	nlh = nfq_nlmsg_put2(buf, NFQNL_MSG_CONFIG, qh->id, NLM_F_ACK);
+
+	nfq_nlmsg_cfg_put_params(nlh, mode, range);
+
+	ret = mnl_socket_sendto(qh->h->nl, nlh, nlh->nlmsg_len);
+	if (ret != -1)
+		ret = mnl_socket_recvfrom(qh->h->nl, buf, sizeof(buf));
+	if (ret != -1)
+		ret = mnl_cb_run(buf, ret, 0, mnl_socket_get_portid(qh->h->nl),
+		    NULL, NULL);
+	return ret;
 }
 
 /**
@@ -948,17 +947,9 @@ static int __set_verdict(struct nfq_q_handle *qh, uint32_t id,
 {
 	char buf[MNL_SOCKET_BUFFER_SIZE];
 	struct nlmsghdr *nlh;
+	static struct sockaddr_nl snl = {.nl_family = AF_NETLINK };
 
-	struct iovec iov[3];
-	int nvecs;
-
-	/* This must be declared here (and not inside the data
-	 * handling block) because the iovec points to this. */
-	struct nlattr data_attr;
-
-	memset(iov, 0, sizeof(iov));
-
-	nlh = nfq_nlmsg_put(buf, NFQNL_MSG_VERDICT, qh->id);
+	nlh = nfq_nlmsg_put(buf, type, qh->id);
 
 	/* add verdict header */
 	nfq_nlmsg_verdict_put(nlh, id, verdict);
@@ -971,26 +962,38 @@ static int __set_verdict(struct nfq_q_handle *qh, uint32_t id,
 	 * 1 userspace address to validate instead of 2. */
 	if (!data_len)
 		return mnl_socket_sendto(qh->h->nl, nlh, nlh->nlmsg_len);
+	{
+		struct iovec iov[2];
+		struct nlattr *data_attr = mnl_nlmsg_get_payload_tail(nlh);
+		const struct msghdr msg = {
+			.msg_name = &snl,
+			.msg_namelen = sizeof snl,
+			.msg_iov = iov,
+			.msg_iovlen = 2,
+			.msg_control = NULL,
+			.msg_controllen = 0,
+			.msg_flags = 0,
+		};
+
+		mnl_attr_put(nlh, NFQA_PAYLOAD, 0, NULL);
+
+		iov[0].iov_base = nlh;
+		iov[0].iov_len = nlh->nlmsg_len;
+		/* The typecast here is to cast away data's const-ness: */
+		iov[1].iov_base = (unsigned char *)data;
+		iov[1].iov_len = data_len;
 
-	iov[0].iov_base = nlh;
-	iov[0].iov_len = NLMSG_TAIL(nlh) - (void *)nlh;
-	nvecs = 1;
-
-	if (data_len) {
-		/* Temporary cast until we get rid of nfnl_build_nfa_iovec() */
-		nfnl_build_nfa_iovec(&iov[1], (struct nfattr *)&data_attr,
-		//nfnl_build_nfa_iovec(&iov[1], &data_attr,
-				     NFQA_PAYLOAD, data_len,
-				     (unsigned char *) data);
-		nvecs += 2;
 		/* Add the length of the appended data to the message
-		 * header.  The size of the attribute is given in the
-		 * nla_len field and is set in the nfnl_build_nfa_iovec()
-		 * function. */
-		nlh->nlmsg_len += data_attr.nla_len;
-	}
+		 * header and attribute length.
+		 * No padding is needed: this is the end of the message.
+		 */
+
+		nlh->nlmsg_len += data_len;
 
-	return nfnl_sendiov(qh->h->nfnlh, iov, nvecs, 0);
+		data_attr->nla_len += data_len;
+
+		return sendmsg(qh->h->nfnlh->fd, &msg, 0);
+	}
 }
 
 /**
@@ -1079,7 +1082,7 @@ EXPORT_SYMBOL
 int nfq_set_verdict_batch2(struct nfq_q_handle *qh, uint32_t id,
 			   uint32_t verdict, uint32_t mark)
 {
-	return __set_verdict(qh, id, verdict, htonl(mark), 1, 0,
+	return __set_verdict(qh, id, verdict, mark, 1, 0,
 				NULL, NFQNL_MSG_VERDICT_BATCH);
 }
 
@@ -1102,7 +1105,7 @@ int nfq_set_verdict_mark(struct nfq_q_handle *qh, uint32_t id,
 			 uint32_t verdict, uint32_t mark,
 			 uint32_t data_len, const unsigned char *buf)
 {
-	return __set_verdict(qh, id, verdict, mark, 1, data_len, buf,
+	return __set_verdict(qh, id, verdict, ntohl(mark), 1, data_len, buf,
 						NFQNL_MSG_VERDICT);
 }
 
@@ -1170,7 +1173,6 @@ uint32_t nfq_get_nfmark(struct nfq_data *nfad)
 		return 0;
 
 	return ntohl(mnl_attr_get_u32(nfad->data[NFQA_MARK]));
-	return ntohl(nfnl_get_data(nfad->data, NFQA_MARK, uint32_t));
 }
 
 /**
@@ -1434,10 +1436,10 @@ struct nfqnl_msg_packet_hw *nfq_get_packet_hw(struct nfq_data *nfad)
 EXPORT_SYMBOL
 uint32_t nfq_get_skbinfo(struct nfq_data *nfad)
 {
-	if (!nfnl_attr_present(nfad->data, NFQA_SKB_INFO))
+	if (!nfad->data[NFQA_SKB_INFO])
 		return 0;
 
-	return ntohl(nfnl_get_data(nfad->data, NFQA_SKB_INFO, uint32_t));
+	return ntohl(mnl_attr_get_u32(nfad->data[NFQA_SKB_INFO]));
 }
 
 /**
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 11/32] src: Fix checkpatch whitespace and block comment warnings
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (11 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 10/32] src: Convert remaining nfq_* functions " Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 12/32] src: Copy nlif-related code from libnfnetlink Duncan Roe
                       ` (20 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

The original patchset changed as little as possible from the original.
Whitespace fixes in particular are confusing during patch review
so leave them until now.

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 src/libnetfilter_queue.c | 21 ++++++++++++---------
 1 file changed, 12 insertions(+), 9 deletions(-)

diff --git a/src/libnetfilter_queue.c b/src/libnetfilter_queue.c
index 56d51ca..2f50b47 100644
--- a/src/libnetfilter_queue.c
+++ b/src/libnetfilter_queue.c
@@ -147,11 +147,11 @@ gcc -g3 -ggdb -Wall -lmnl -lnetfilter_queue -o nf-queue nf-queue.c
 #define NFNL_MAX_SUBSYS 16
 
 struct nfnl_subsys_handle {
-	struct nfnl_handle 	*nfnlh;
+	struct nfnl_handle	*nfnlh;
 	uint32_t		subscriptions;
 	uint8_t			subsys_id;
 	uint8_t			cb_count;
-	struct nfnl_callback 	*cb;	/* array of callbacks */
+	struct nfnl_callback	*cb;	/* array of callbacks */
 };
 
 struct nfnl_handle {
@@ -163,13 +163,13 @@ struct nfnl_handle {
 	uint32_t		dump;
 	uint32_t		rcv_buffer_size;	/* for nfnl_catch */
 	uint32_t		flags;
-	struct nlmsghdr 	*last_nlhdr;
+	struct nlmsghdr		*last_nlhdr;
 	struct nfnl_subsys_handle subsys[NFNL_MAX_SUBSYS+1];
 };
 
 /* Copy of private libmnl structure */
 struct mnl_socket {
-	int 			fd;
+	int			fd;
 	struct sockaddr_nl	addr;
 };
 
@@ -581,11 +581,13 @@ unsigned int nfnl_rcvbufsiz(const struct nfnl_handle *h, unsigned int size)
 
 	/* first we try the FORCE option, which is introduced in kernel
 	 * 2.6.14 to give "root" the ability to override the system wide
-	 * maximum */
+	 * maximum
+	 */
 	status = setsockopt(h->fd, SOL_SOCKET, SO_RCVBUFFORCE, &size, socklen);
 	if (status < 0) {
 		/* if this didn't work, we try at least to get the system
-		 * wide maximum (or whatever the user requested) */
+		 * wide maximum (or whatever the user requested)
+		 */
 		setsockopt(h->fd, SOL_SOCKET, SO_RCVBUF, &size, socklen);
 	}
 	getsockopt(h->fd, SOL_SOCKET, SO_RCVBUF, &read_size, &socklen);
@@ -658,7 +660,7 @@ int nfq_unbind_pf(struct nfq_handle *h, uint16_t pf)
  * @}
  */
 
- static int __nfq_handle_msg(const struct nlmsghdr *nlh, void *data)
+static int __nfq_handle_msg(const struct nlmsghdr *nlh, void *data)
 {
 	struct nfq_handle *h = data;
 	struct nfq_q_handle *qh;
@@ -959,7 +961,8 @@ static int __set_verdict(struct nfq_q_handle *qh, uint32_t id,
 
 	/* Efficiency gain: when there is only 1 iov,
 	 * sendto() is faster than sendmsg() because the kernel only has
-	 * 1 userspace address to validate instead of 2. */
+	 * 1 userspace address to validate instead of 2.
+	 */
 	if (!data_len)
 		return mnl_socket_sendto(qh->h->nl, nlh, nlh->nlmsg_len);
 	{
@@ -967,7 +970,7 @@ static int __set_verdict(struct nfq_q_handle *qh, uint32_t id,
 		struct nlattr *data_attr = mnl_nlmsg_get_payload_tail(nlh);
 		const struct msghdr msg = {
 			.msg_name = &snl,
-			.msg_namelen = sizeof snl,
+			.msg_namelen = sizeof(snl),
 			.msg_iov = iov,
 			.msg_iovlen = 2,
 			.msg_control = NULL,
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 12/32] src: Copy nlif-related code from libnfnetlink
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (12 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 11/32] src: Fix checkpatch whitespace and block comment warnings Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 13/32] include: Cherry-pick macros and functions that nlif will need Duncan Roe
                       ` (19 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

These sources are for reviewer's reference. In particular, the libmnl
updates to rtnl.c are slated to be done in iftable.c so rtnl.c will be
removed after the libmnl updates are done.
linux_list.h raises a number of checkpatch errors.
These will be addresses in a later commit.
This patch doesn't actually do anything with the new files.

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 include/libnetfilter_queue/linux_list.h | 727 ++++++++++++++++++++++++
 src/iftable.c                           | 348 ++++++++++++
 src/rtnl.c                              | 283 +++++++++
 3 files changed, 1358 insertions(+)
 create mode 100644 include/libnetfilter_queue/linux_list.h
 create mode 100644 src/iftable.c
 create mode 100644 src/rtnl.c

diff --git a/include/libnetfilter_queue/linux_list.h b/include/libnetfilter_queue/linux_list.h
new file mode 100644
index 0000000..cf71837
--- /dev/null
+++ b/include/libnetfilter_queue/linux_list.h
@@ -0,0 +1,727 @@
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+#include <stddef.h>
+
+#undef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * @ptr:	the pointer to the member.
+ * @type:	the type of the container struct this is embedded in.
+ * @member:	the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({			\
+        typeof( ((type *)0)->member ) *__mptr = (ptr);	\
+        (type *)( (char *)__mptr - offsetof(type,member) );})
+
+/*
+ * Check at compile time that something is of a particular type.
+ * Always evaluates to 1 so you may use it easily in comparisons.
+ */
+#define typecheck(type,x) \
+({	type __dummy; \
+	typeof(x) __dummy2; \
+	(void)(&__dummy == &__dummy2); \
+	1; \
+})
+
+#define prefetch(x) ((void)0)
+
+/* empty define to make this work in userspace -HW */
+#ifndef smp_wmb
+#define smp_wmb()
+#endif
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized list entries.
+ */
+#define LIST_POISON1  ((void *) 0x00100100)
+#define LIST_POISON2  ((void *) 0x00200200)
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+	struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+	struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+	(ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+			      struct list_head *prev,
+			      struct list_head *next)
+{
+	next->prev = new;
+	new->next = next;
+	new->prev = prev;
+	prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head->prev, head);
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add_rcu(struct list_head * new,
+		struct list_head * prev, struct list_head * next)
+{
+	new->next = next;
+	new->prev = prev;
+	smp_wmb();
+	next->prev = new;
+	prev->next = new;
+}
+
+/**
+ * list_add_rcu - add a new entry to rcu-protected list
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_add_rcu()
+ * or list_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ */
+static inline void list_add_rcu(struct list_head *new, struct list_head *head)
+{
+	__list_add_rcu(new, head, head->next);
+}
+
+/**
+ * list_add_tail_rcu - add a new entry to rcu-protected list
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_add_tail_rcu()
+ * or list_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ */
+static inline void list_add_tail_rcu(struct list_head *new,
+					struct list_head *head)
+{
+	__list_add_rcu(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+	next->prev = prev;
+	prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	entry->next = LIST_POISON1;
+	entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_del_rcu - deletes entry from list without re-initialization
+ * @entry: the element to delete from the list.
+ *
+ * Note: list_empty on entry does not return true after this,
+ * the entry is in an undefined state. It is useful for RCU based
+ * lockfree traversal.
+ *
+ * In particular, it means that we can not poison the forward
+ * pointers that may still be used for walking the list.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_del_rcu()
+ * or list_add_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ *
+ * Note that the caller is not permitted to immediately free
+ * the newly deleted entry.  Instead, either synchronize_kernel()
+ * or call_rcu() must be used to defer freeing until an RCU
+ * grace period has elapsed.
+ */
+static inline void list_del_rcu(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+        __list_del(list->prev, list->next);
+        list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+				  struct list_head *head)
+{
+        __list_del(list->prev, list->next);
+        list_add_tail(list, head);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+	return head->next == head;
+}
+
+/**
+ * list_empty_careful - tests whether a list is
+ * empty _and_ checks that no other CPU might be
+ * in the process of still modifying either member
+ *
+ * NOTE: using list_empty_careful() without synchronization
+ * can only be safe if the only activity that can happen
+ * to the list entry is list_del_init(). Eg. it cannot be used
+ * if another CPU could re-list_add() it.
+ *
+ * @head: the list to test.
+ */
+static inline int list_empty_careful(const struct list_head *head)
+{
+	struct list_head *next = head->next;
+	return (next == head) && (next == head->prev);
+}
+
+static inline void __list_splice(struct list_head *list,
+				 struct list_head *head)
+{
+	struct list_head *first = list->next;
+	struct list_head *last = list->prev;
+	struct list_head *at = head->next;
+
+	first->prev = head;
+	head->next = first;
+
+	last->next = at;
+	at->prev = last;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(struct list_head *list, struct list_head *head)
+{
+	if (!list_empty(list))
+		__list_splice(list, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+				    struct list_head *head)
+{
+	if (!list_empty(list)) {
+		__list_splice(list, head);
+		INIT_LIST_HEAD(list);
+	}
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:	the &struct list_head pointer.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+	container_of(ptr, type, member)
+
+/**
+ * list_for_each	-	iterate over a list
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @head:	the head for your list.
+ */
+#define list_for_each(pos, head) \
+	for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+        	pos = pos->next, prefetch(pos->next))
+
+/**
+ * __list_for_each	-	iterate over a list
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @head:	the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code, no prefetching is done.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+	for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev	-	iterate over a list backwards
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @head:	the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+	for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \
+        	pos = pos->prev, prefetch(pos->prev))
+
+/**
+ * list_for_each_safe	-	iterate over a list safe against removal of list entry
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @n:		another &struct list_head to use as temporary storage
+ * @head:	the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+	for (pos = (head)->next, n = pos->next; pos != (head); \
+		pos = n, n = pos->next)
+
+/**
+ * list_for_each_entry	-	iterate over list of given type
+ * @pos:	the type * to use as a loop counter.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member)				\
+	for (pos = list_entry((head)->next, typeof(*pos), member),	\
+		     prefetch(pos->member.next);			\
+	     &pos->member != (head); 					\
+	     pos = list_entry(pos->member.next, typeof(*pos), member),	\
+		     prefetch(pos->member.next))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos:	the type * to use as a loop counter.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member)			\
+	for (pos = list_entry((head)->prev, typeof(*pos), member),	\
+		     prefetch(pos->member.prev);			\
+	     &pos->member != (head); 					\
+	     pos = list_entry(pos->member.prev, typeof(*pos), member),	\
+		     prefetch(pos->member.prev))
+
+/**
+ * list_prepare_entry - prepare a pos entry for use as a start point in
+ *			list_for_each_entry_continue
+ * @pos:	the type * to use as a start point
+ * @head:	the head of the list
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_prepare_entry(pos, head, member) \
+	((pos) ? : list_entry(head, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue -	iterate over list of given type
+ *			continuing after existing point
+ * @pos:	the type * to use as a loop counter.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_continue(pos, head, member) 		\
+	for (pos = list_entry(pos->member.next, typeof(*pos), member),	\
+		     prefetch(pos->member.next);			\
+	     &pos->member != (head);					\
+	     pos = list_entry(pos->member.next, typeof(*pos), member),	\
+		     prefetch(pos->member.next))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos:	the type * to use as a loop counter.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member)			\
+	for (pos = list_entry((head)->next, typeof(*pos), member),	\
+		n = list_entry(pos->member.next, typeof(*pos), member);	\
+	     &pos->member != (head); 					\
+	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_rcu	-	iterate over an rcu-protected list
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @head:	the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_rcu(pos, head) \
+	for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+        	pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next))
+
+#define __list_for_each_rcu(pos, head) \
+	for (pos = (head)->next; pos != (head); \
+        	pos = pos->next, ({ smp_read_barrier_depends(); 0;}))
+
+/**
+ * list_for_each_safe_rcu	-	iterate over an rcu-protected list safe
+ *					against removal of list entry
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @n:		another &struct list_head to use as temporary storage
+ * @head:	the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_safe_rcu(pos, n, head) \
+	for (pos = (head)->next, n = pos->next; pos != (head); \
+		pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next)
+
+/**
+ * list_for_each_entry_rcu	-	iterate over rcu list of given type
+ * @pos:	the type * to use as a loop counter.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_entry_rcu(pos, head, member)			\
+	for (pos = list_entry((head)->next, typeof(*pos), member),	\
+		     prefetch(pos->member.next);			\
+	     &pos->member != (head); 					\
+	     pos = list_entry(pos->member.next, typeof(*pos), member),	\
+		     ({ smp_read_barrier_depends(); 0;}),		\
+		     prefetch(pos->member.next))
+
+
+/**
+ * list_for_each_continue_rcu	-	iterate over an rcu-protected list
+ *			continuing after existing point.
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @head:	the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_continue_rcu(pos, head) \
+	for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \
+        	(pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next))
+
+/*
+ * Double linked lists with a single pointer list head.
+ * Mostly useful for hash tables where the two pointer list head is
+ * too wasteful.
+ * You lose the ability to access the tail in O(1).
+ */
+
+struct hlist_head {
+	struct hlist_node *first;
+};
+
+struct hlist_node {
+	struct hlist_node *next, **pprev;
+};
+
+#define HLIST_HEAD_INIT { .first = NULL }
+#define HLIST_HEAD(name) struct hlist_head name = {  .first = NULL }
+#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
+#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL)
+
+static inline int hlist_unhashed(const struct hlist_node *h)
+{
+	return !h->pprev;
+}
+
+static inline int hlist_empty(const struct hlist_head *h)
+{
+	return !h->first;
+}
+
+static inline void __hlist_del(struct hlist_node *n)
+{
+	struct hlist_node *next = n->next;
+	struct hlist_node **pprev = n->pprev;
+	*pprev = next;
+	if (next)
+		next->pprev = pprev;
+}
+
+static inline void hlist_del(struct hlist_node *n)
+{
+	__hlist_del(n);
+	n->next = LIST_POISON1;
+	n->pprev = LIST_POISON2;
+}
+
+/**
+ * hlist_del_rcu - deletes entry from hash list without re-initialization
+ * @n: the element to delete from the hash list.
+ *
+ * Note: list_unhashed() on entry does not return true after this,
+ * the entry is in an undefined state. It is useful for RCU based
+ * lockfree traversal.
+ *
+ * In particular, it means that we can not poison the forward
+ * pointers that may still be used for walking the hash list.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_add_head_rcu()
+ * or hlist_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_for_each_entry().
+ */
+static inline void hlist_del_rcu(struct hlist_node *n)
+{
+	__hlist_del(n);
+	n->pprev = LIST_POISON2;
+}
+
+static inline void hlist_del_init(struct hlist_node *n)
+{
+	if (n->pprev)  {
+		__hlist_del(n);
+		INIT_HLIST_NODE(n);
+	}
+}
+
+#define hlist_del_rcu_init hlist_del_init
+
+static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
+{
+	struct hlist_node *first = h->first;
+	n->next = first;
+	if (first)
+		first->pprev = &n->next;
+	h->first = n;
+	n->pprev = &h->first;
+}
+
+
+/**
+ * hlist_add_head_rcu - adds the specified element to the specified hlist,
+ * while permitting racing traversals.
+ * @n: the element to add to the hash list.
+ * @h: the list to add to.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_add_head_rcu()
+ * or hlist_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_for_each_entry(), but only if smp_read_barrier_depends()
+ * is used to prevent memory-consistency problems on Alpha CPUs.
+ * Regardless of the type of CPU, the list-traversal primitive
+ * must be guarded by rcu_read_lock().
+ *
+ * OK, so why don't we have an hlist_for_each_entry_rcu()???
+ */
+static inline void hlist_add_head_rcu(struct hlist_node *n,
+					struct hlist_head *h)
+{
+	struct hlist_node *first = h->first;
+	n->next = first;
+	n->pprev = &h->first;
+	smp_wmb();
+	if (first)
+		first->pprev = &n->next;
+	h->first = n;
+}
+
+/* next must be != NULL */
+static inline void hlist_add_before(struct hlist_node *n,
+					struct hlist_node *next)
+{
+	n->pprev = next->pprev;
+	n->next = next;
+	next->pprev = &n->next;
+	*(n->pprev) = n;
+}
+
+static inline void hlist_add_after(struct hlist_node *n,
+					struct hlist_node *next)
+{
+	next->next = n->next;
+	n->next = next;
+	next->pprev = &n->next;
+
+	if(next->next)
+		next->next->pprev  = &next->next;
+}
+
+#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
+
+#define hlist_for_each(pos, head) \
+	for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
+	     pos = pos->next)
+
+#define hlist_for_each_safe(pos, n, head) \
+	for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
+	     pos = n)
+
+/**
+ * hlist_for_each_entry	- iterate over list of given type
+ * @tpos:	the type * to use as a loop counter.
+ * @pos:	the &struct hlist_node to use as a loop counter.
+ * @head:	the head for your list.
+ * @member:	the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry(tpos, pos, head, member)			 \
+	for (pos = (head)->first;					 \
+	     pos && ({ prefetch(pos->next); 1;}) &&			 \
+		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+	     pos = pos->next)
+
+/**
+ * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point
+ * @tpos:	the type * to use as a loop counter.
+ * @pos:	the &struct hlist_node to use as a loop counter.
+ * @member:	the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_continue(tpos, pos, member)		 \
+	for (pos = (pos)->next;						 \
+	     pos && ({ prefetch(pos->next); 1;}) &&			 \
+		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+	     pos = pos->next)
+
+/**
+ * hlist_for_each_entry_from - iterate over a hlist continuing from existing point
+ * @tpos:	the type * to use as a loop counter.
+ * @pos:	the &struct hlist_node to use as a loop counter.
+ * @member:	the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_from(tpos, pos, member)			 \
+	for (; pos && ({ prefetch(pos->next); 1;}) &&			 \
+		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+	     pos = pos->next)
+
+/**
+ * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @tpos:	the type * to use as a loop counter.
+ * @pos:	the &struct hlist_node to use as a loop counter.
+ * @n:		another &struct hlist_node to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_safe(tpos, pos, n, head, member) 		 \
+	for (pos = (head)->first;					 \
+	     pos && ({ n = pos->next; 1; }) && 				 \
+		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+	     pos = n)
+
+/**
+ * hlist_for_each_entry_rcu - iterate over rcu list of given type
+ * @pos:	the type * to use as a loop counter.
+ * @pos:	the &struct hlist_node to use as a loop counter.
+ * @head:	the head for your list.
+ * @member:	the name of the hlist_node within the struct.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as hlist_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define hlist_for_each_entry_rcu(tpos, pos, head, member)		 \
+	for (pos = (head)->first;					 \
+	     pos && ({ prefetch(pos->next); 1;}) &&			 \
+		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+	     pos = pos->next, ({ smp_read_barrier_depends(); 0; }) )
+
+#endif
diff --git a/src/iftable.c b/src/iftable.c
new file mode 100644
index 0000000..aab59b3
--- /dev/null
+++ b/src/iftable.c
@@ -0,0 +1,348 @@
+/* iftable - table of network interfaces
+ *
+ * (C) 2004 by Astaro AG, written by Harald Welte <hwelte@astaro.com>
+ * (C) 2008 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This software is Free Software and licensed under GNU GPLv2+.
+ */
+
+/* IFINDEX handling */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <linux/netdevice.h>
+
+#include <libnfnetlink/libnfnetlink.h>
+#include "rtnl.h"
+#include "linux_list.h"
+
+/**
+ * \defgroup iftable Functions in iftable.c [DEPRECATED]
+ * This documentation is provided for the benefit of maintainers of legacy code.
+ *
+ * New applications should use
+ * [libmnl](https://netfilter.org/projects/libmnl/doxygen/html/).
+ * @{
+ */
+
+struct ifindex_node {
+	struct list_head head;
+
+	uint32_t	index;
+	uint32_t	type;
+	uint32_t	alen;
+	uint32_t	flags;
+	char		addr[8];
+	char		name[16];
+};
+
+struct nlif_handle {
+	struct list_head ifindex_hash[16];
+	struct rtnl_handle *rtnl_handle;
+	struct rtnl_handler ifadd_handler;
+	struct rtnl_handler ifdel_handler;
+};
+
+/* iftable_add - Add/Update an entry to/in the interface table
+ * @n:		netlink message header of a RTM_NEWLINK message
+ * @arg:	not used
+ *
+ * This function adds/updates an entry in the intrface table.
+ * Returns -1 on error, 1 on success.
+ */
+static int iftable_add(struct nlmsghdr *n, void *arg)
+{
+	unsigned int hash, found = 0;
+	struct ifinfomsg *ifi_msg = NLMSG_DATA(n);
+	struct ifindex_node *this;
+	struct rtattr *cb[IFLA_MAX+1];
+	struct nlif_handle *h = arg;
+
+	if (n->nlmsg_type != RTM_NEWLINK)
+		return -1;
+
+	if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi_msg)))
+		return -1;
+
+	rtnl_parse_rtattr(cb, IFLA_MAX, IFLA_RTA(ifi_msg), IFLA_PAYLOAD(n));
+
+	if (!cb[IFLA_IFNAME])
+		return -1;
+
+	hash = ifi_msg->ifi_index & 0xF;
+	list_for_each_entry(this, &h->ifindex_hash[hash], head) {
+		if (this->index == ifi_msg->ifi_index) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (!found) {
+		this = malloc(sizeof(*this));
+		if (!this)
+			return -1;
+
+		this->index = ifi_msg->ifi_index;
+	}
+
+	this->type = ifi_msg->ifi_type;
+	this->flags = ifi_msg->ifi_flags;
+	if (cb[IFLA_ADDRESS]) {
+		unsigned int alen;
+		this->alen = alen = RTA_PAYLOAD(cb[IFLA_ADDRESS]);
+		if (alen > sizeof(this->addr))
+			alen = sizeof(this->addr);
+		memcpy(this->addr, RTA_DATA(cb[IFLA_ADDRESS]), alen);
+	} else {
+		this->alen = 0;
+		memset(this->addr, 0, sizeof(this->addr));
+	}
+	strcpy(this->name, RTA_DATA(cb[IFLA_IFNAME]));
+
+	if (!found)
+		list_add(&this->head, &h->ifindex_hash[hash]);
+
+	return 1;
+}
+
+/* iftable_del - Delete an entry from the interface table
+ * @n:		netlink message header of a RTM_DELLINK nlmsg
+ * @arg:	not used
+ *
+ * Delete an entry from the interface table.  
+ * Returns -1 on error, 0 if no matching entry was found or 1 on success.
+ */
+static int iftable_del(struct nlmsghdr *n, void *arg)
+{
+	struct ifinfomsg *ifi_msg = NLMSG_DATA(n);
+	struct rtattr *cb[IFLA_MAX+1];
+	struct nlif_handle *h = arg;
+	struct ifindex_node *this, *tmp;
+	unsigned int hash;
+
+	if (n->nlmsg_type != RTM_DELLINK)
+		return -1;
+
+	if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi_msg)))
+		return -1;
+
+	rtnl_parse_rtattr(cb, IFLA_MAX, IFLA_RTA(ifi_msg), IFLA_PAYLOAD(n));
+
+	hash = ifi_msg->ifi_index & 0xF;
+	list_for_each_entry_safe(this, tmp, &h->ifindex_hash[hash], head) {
+		if (this->index == ifi_msg->ifi_index) {
+			list_del(&this->head);
+			free(this);
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+/** Get the name for an ifindex
+ *
+ * \param nlif_handle A pointer to a ::nlif_handle created
+ * \param index ifindex to be resolved
+ * \param name interface name, pass a buffer of IFNAMSIZ size
+ * \return -1 on error, 1 on success 
+ */
+int nlif_index2name(struct nlif_handle *h, 
+		    unsigned int index,
+		    char *name)
+{
+	unsigned int hash;
+	struct ifindex_node *this;
+
+	assert(h != NULL);
+	assert(name != NULL);
+
+	if (index == 0) {
+		strcpy(name, "*");
+		return 1;
+	}
+
+	hash = index & 0xF;
+	list_for_each_entry(this, &h->ifindex_hash[hash], head) {
+		if (this->index == index) {
+			strcpy(name, this->name);
+			return 1;
+		}
+	}
+
+	errno = ENOENT;
+	return -1;
+}
+
+/** Get the flags for an ifindex
+ *
+ * \param nlif_handle A pointer to a ::nlif_handle created
+ * \param index ifindex to be resolved
+ * \param flags pointer to variable used to store the interface flags
+ * \return -1 on error, 1 on success 
+ */
+int nlif_get_ifflags(const struct nlif_handle *h,
+		     unsigned int index,
+		     unsigned int *flags)
+{
+	unsigned int hash;
+	struct ifindex_node *this;
+
+	assert(h != NULL);
+	assert(flags != NULL);
+
+	if (index == 0) {
+		errno = ENOENT;
+		return -1;
+	}
+
+	hash = index & 0xF;
+	list_for_each_entry(this, &h->ifindex_hash[hash], head) {
+		if (this->index == index) {
+			*flags = this->flags;
+			return 1;
+		}
+	}
+	errno = ENOENT;
+	return -1;
+}
+
+/** Initialize interface table
+ *
+ * Initialize rtnl interface and interface table
+ * Call this before any nlif_* function
+ *
+ * \return file descriptor to netlink socket
+ */
+struct nlif_handle *nlif_open(void)
+{
+	int i;
+	struct nlif_handle *h;
+
+	h = calloc(1,  sizeof(struct nlif_handle));
+	if (h == NULL)
+		goto err;
+
+	for (i=0; i<16; i++)
+		INIT_LIST_HEAD(&h->ifindex_hash[i]);
+
+	h->ifadd_handler.nlmsg_type = RTM_NEWLINK;
+	h->ifadd_handler.handlefn = iftable_add;
+	h->ifadd_handler.arg = h;
+	h->ifdel_handler.nlmsg_type = RTM_DELLINK;
+	h->ifdel_handler.handlefn = iftable_del;
+	h->ifdel_handler.arg = h;
+
+	h->rtnl_handle = rtnl_open();
+	if (h->rtnl_handle == NULL)
+		goto err;
+
+	if (rtnl_handler_register(h->rtnl_handle, &h->ifadd_handler) < 0)
+		goto err_close;
+
+	if (rtnl_handler_register(h->rtnl_handle, &h->ifdel_handler) < 0)
+		goto err_unregister;
+
+	return h;
+
+err_unregister:
+	rtnl_handler_unregister(h->rtnl_handle, &h->ifadd_handler);
+err_close:
+	rtnl_close(h->rtnl_handle);
+	free(h);
+err:
+	return NULL;
+}
+
+/** Destructor of interface table
+ *
+ * \param nlif_handle A pointer to a ::nlif_handle created 
+ * via nlif_open()
+ */
+void nlif_close(struct nlif_handle *h)
+{
+	int i;
+	struct ifindex_node *this, *tmp;
+
+	assert(h != NULL);
+
+	rtnl_handler_unregister(h->rtnl_handle, &h->ifadd_handler);
+	rtnl_handler_unregister(h->rtnl_handle, &h->ifdel_handler);
+	rtnl_close(h->rtnl_handle);
+
+	for (i=0; i<16; i++) {
+		list_for_each_entry_safe(this, tmp, &h->ifindex_hash[i], head) {
+			list_del(&this->head);
+			free(this);
+		}
+	}
+
+	free(h);
+	h = NULL; /* bugtrap */
+}
+
+/** Receive message from netlink and update interface table
+ *
+ * \param nlif_handle A pointer to a ::nlif_handle created
+ * \return 0 if OK
+ */
+int nlif_catch(struct nlif_handle *h)
+{
+	assert(h != NULL);
+
+	if (h->rtnl_handle)
+		return rtnl_receive(h->rtnl_handle);
+
+	return -1;
+}
+
+static int nlif_catch_multi(struct nlif_handle *h)
+{
+	assert(h != NULL);
+
+	if (h->rtnl_handle)
+		return rtnl_receive_multi(h->rtnl_handle);
+
+	return -1;
+}
+
+/** 
+ * nlif_query - request a dump of interfaces available in the system
+ * @h: pointer to a valid nlif_handler
+ */
+int nlif_query(struct nlif_handle *h)
+{
+	assert(h != NULL);
+
+	if (rtnl_dump_type(h->rtnl_handle, RTM_GETLINK) < 0)
+		return -1;
+
+	return nlif_catch_multi(h);
+}
+
+/** Returns socket descriptor for the netlink socket
+ *
+ * \param nlif_handle A pointer to a ::nlif_handle created
+ * \return The fd or -1 if there's an error
+ */
+int nlif_fd(struct nlif_handle *h)
+{
+	assert(h != NULL);
+
+	if (h->rtnl_handle)
+		return h->rtnl_handle->rtnl_fd;
+
+	return -1;
+}
+
+/**
+ * @}
+ */
diff --git a/src/rtnl.c b/src/rtnl.c
new file mode 100644
index 0000000..dff3bef
--- /dev/null
+++ b/src/rtnl.c
@@ -0,0 +1,283 @@
+/* rtnl - rtnetlink utility functions
+ *
+ * (C) 2004 by Astaro AG, written by Harald Welte <hwelte@astaro.com>
+ *
+ * Adapted to nfnetlink by Eric Leblond <eric@inl.fr>
+ *
+ * This software is free software and licensed under GNU GPLv2+.
+ *
+ */
+
+/* rtnetlink - routing table netlink interface */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include <netinet/in.h>
+
+#include <linux/types.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+#include "rtnl.h"
+
+#define rtnl_log(x, ...)
+
+static inline struct rtnl_handler *
+find_handler(struct rtnl_handle *rtnl_handle, uint16_t type)
+{
+	struct rtnl_handler *h;
+	for (h = rtnl_handle->handlers; h; h = h->next) {
+		if (h->nlmsg_type == type)
+			return h;
+	}
+	return NULL;
+}
+
+static int call_handler(struct rtnl_handle *rtnl_handle,
+			uint16_t type,
+			struct nlmsghdr *hdr)
+{
+	struct rtnl_handler *h = find_handler(rtnl_handle, type);
+
+	if (!h) {
+		rtnl_log(LOG_DEBUG, "no registered handler for type %u", type);
+		return 0;
+	}
+
+	return (h->handlefn)(hdr, h->arg);
+}
+
+/**
+ * \defgroup rtnetlink Functions in rtnl.c [DEPRECATED]
+ * This documentation is provided for the benefit of maintainers of legacy code.
+ *
+ * New applications should use
+ * [libmnl](https://netfilter.org/projects/libmnl/doxygen/html/).
+ * @{
+ */
+
+/**
+ * rtnl_handler_register - register handler for given nlmsg type
+ * \param: rtnl_handle: Handler ftom rtnl_open()
+ * \param: hdlr: callback handler structure
+ */
+int rtnl_handler_register(struct rtnl_handle *rtnl_handle,
+			  struct rtnl_handler *hdlr)
+{
+	rtnl_log(LOG_DEBUG, "registering handler for type %u",
+		 hdlr->nlmsg_type);
+	hdlr->next = rtnl_handle->handlers;
+	rtnl_handle->handlers = hdlr;
+	return 1;
+}
+
+/**
+ * rtnl_handler_unregister - unregister handler for given nlmst type
+ * \param: hdlr: callback handler structure
+ * \param: hdlr:	handler structure
+ */
+int rtnl_handler_unregister(struct rtnl_handle *rtnl_handle,
+			    struct rtnl_handler *hdlr)
+{
+	struct rtnl_handler *h, *prev = NULL;
+
+	rtnl_log(LOG_DEBUG, "unregistering handler for type %u",
+		 hdlr->nlmsg_type);
+
+	for (h = rtnl_handle->handlers; h; h = h->next) {
+		if (h == hdlr) {
+			if (prev)
+				prev->next = h->next;
+			else
+				rtnl_handle->handlers = h->next;
+			return 1;
+		}
+		prev = h;
+	}
+	return 0;
+}
+
+int rtnl_parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+	memset(tb, 0, sizeof(struct rtattr *) * max);
+
+	while (RTA_OK(rta, len)) {
+		if (rta->rta_type <= max)
+			tb[rta->rta_type] = rta;
+		rta = RTA_NEXT(rta,len);
+	}
+	if (len)
+		return -1;
+	return 0;
+}
+
+/* rtnl_dump_type - ask rtnetlink to dump a specific table
+ * \param: type:	type of table to be dumped
+ */
+int rtnl_dump_type(struct rtnl_handle *rtnl_handle, unsigned int type)
+{
+	struct {
+		struct nlmsghdr nlh;
+		struct rtgenmsg g;
+	} req;
+	struct sockaddr_nl nladdr;
+
+	memset(&nladdr, 0, sizeof(nladdr));
+	memset(&req, 0, sizeof(req));
+	nladdr.nl_family = AF_NETLINK;
+
+	req.nlh.nlmsg_len = sizeof(req);
+	req.nlh.nlmsg_type = type;
+	req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+	req.nlh.nlmsg_pid = 0;
+	req.nlh.nlmsg_seq = rtnl_handle->rtnl_dump = ++(rtnl_handle->rtnl_seq);
+	req.g.rtgen_family = AF_INET;
+
+	return sendto(rtnl_handle->rtnl_fd, &req, sizeof(req), 0,
+		      (struct sockaddr*)&nladdr, sizeof(nladdr));
+}
+
+/* rtnl_receive - receive netlink packets from rtnetlink socket */
+int rtnl_receive(struct rtnl_handle *rtnl_handle)
+{
+	int status;
+	char buf[8192];
+	struct sockaddr_nl nladdr;
+	struct iovec iov = { buf, sizeof(buf) };
+	struct nlmsghdr *h;
+
+	struct msghdr msg = {
+		.msg_name    = &nladdr,
+		.msg_namelen = sizeof(nladdr),
+		.msg_iov     = &iov,
+		.msg_iovlen  = 1,
+	};
+
+	status = recvmsg(rtnl_handle->rtnl_fd, &msg, 0);
+	if (status < 0) {
+		if (errno == EINTR)
+			return 0;
+		rtnl_log(LOG_NOTICE, "OVERRUN on rtnl socket");
+		return -1;
+	}
+	if (status == 0) {
+		rtnl_log(LOG_ERROR, "EOF on rtnl socket");
+		return -1;
+	}
+	if (msg.msg_namelen != sizeof(nladdr)) {
+		rtnl_log(LOG_ERROR, "invalid address size");
+		return -1;
+	}
+
+	h = (struct nlmsghdr *) buf;
+	while (NLMSG_OK(h, status)) {
+#if 0
+		if (h->nlmsg_pid != rtnl_local.nl_pid ||
+		    h->nlmsg_seq != rtnl_dump) {
+			goto skip;
+		}
+#endif
+
+		if (h->nlmsg_type == NLMSG_DONE) {
+			rtnl_log(LOG_NOTICE, "NLMSG_DONE");
+			return 0;
+		}
+		if (h->nlmsg_type == NLMSG_ERROR) {
+			struct nlmsgerr *err = NLMSG_DATA(h);
+			if (h->nlmsg_len>=NLMSG_LENGTH(sizeof(struct nlmsgerr)))
+				errno = -err->error;
+			rtnl_log(LOG_ERROR, "NLMSG_ERROR, errnp=%d",
+				 errno);
+			return -1;
+		}
+
+		if (call_handler(rtnl_handle, h->nlmsg_type, h) == 0)
+			rtnl_log(LOG_NOTICE, "unhandled nlmsg_type %u",
+				 h->nlmsg_type);
+		h = NLMSG_NEXT(h, status);
+	}
+	return 1;
+}
+
+int rtnl_receive_multi(struct rtnl_handle *rtnl_handle)
+{
+	while (1) {
+		if (rtnl_receive(rtnl_handle) <= 0)
+			break;
+	}
+	return 1;
+}
+
+/* rtnl_open - constructor of rtnetlink module */
+struct rtnl_handle *rtnl_open(void)
+{
+	socklen_t addrlen;
+	struct rtnl_handle *h;
+
+	h = calloc(1, sizeof(struct rtnl_handle));
+	if (!h)
+		return NULL;
+
+	addrlen = sizeof(h->rtnl_local);
+
+	h->rtnl_local.nl_pid = getpid();
+	h->rtnl_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+	if (h->rtnl_fd < 0) {
+		rtnl_log(LOG_ERROR, "unable to create rtnetlink socket");
+		goto err;
+	}
+
+	memset(&h->rtnl_local, 0, sizeof(h->rtnl_local));
+	h->rtnl_local.nl_family = AF_NETLINK;
+	h->rtnl_local.nl_groups = RTMGRP_LINK;
+	if (bind(h->rtnl_fd, (struct sockaddr *) &h->rtnl_local, addrlen) < 0) {
+		rtnl_log(LOG_ERROR, "unable to bind rtnetlink socket");
+		goto err_close;
+	}
+
+	if (getsockname(h->rtnl_fd,
+			(struct sockaddr *) &h->rtnl_local,
+			&addrlen) < 0) {
+		rtnl_log(LOG_ERROR, "cannot gescockname(rtnl_socket)");
+		goto err_close;
+	}
+
+	if (addrlen != sizeof(h->rtnl_local)) {
+		rtnl_log(LOG_ERROR, "invalid address size %u", addr_len);
+		goto err_close;
+	}
+
+	if (h->rtnl_local.nl_family != AF_NETLINK) {
+		rtnl_log(LOG_ERROR, "invalid AF %u", h->rtnl_local.nl_family);
+		goto err_close;
+	}
+
+	h->rtnl_seq = time(NULL);
+
+	return h;
+
+err_close:
+	close(h->rtnl_fd);
+err:
+	free(h);
+	return NULL;
+}
+
+/* rtnl_close - destructor of rtnetlink module */
+void rtnl_close(struct rtnl_handle *rtnl_handle)
+{
+	close(rtnl_handle->rtnl_fd);
+	free(rtnl_handle);
+	return;
+}
+
+/**
+ * @}
+ */
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 13/32] include: Cherry-pick macros and functions that nlif will need
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (13 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 12/32] src: Copy nlif-related code from libnfnetlink Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 14/32] doc: Add linux_list.h to the doxygen system Duncan Roe
                       ` (18 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

linux_list.h has macros and functions that mnl-api programs may want to
use. Will have to add linux_list.h to the doxygen system,
so cut out stuff we don't need to document.
Update libnetfilter_queue.h to include linux_list.h.

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 .../libnetfilter_queue/libnetfilter_queue.h   |   1 +
 include/libnetfilter_queue/linux_list.h       | 586 +-----------------
 2 files changed, 7 insertions(+), 580 deletions(-)

diff --git a/include/libnetfilter_queue/libnetfilter_queue.h b/include/libnetfilter_queue/libnetfilter_queue.h
index 9327f8c..9bd9c43 100644
--- a/include/libnetfilter_queue/libnetfilter_queue.h
+++ b/include/libnetfilter_queue/libnetfilter_queue.h
@@ -16,6 +16,7 @@
 #include <sys/time.h>
 #include <libnfnetlink/libnfnetlink.h>
 
+#include <libnetfilter_queue/linux_list.h>
 #include <libnetfilter_queue/linux_nfnetlink_queue.h>
 
 #ifdef __cplusplus
diff --git a/include/libnetfilter_queue/linux_list.h b/include/libnetfilter_queue/linux_list.h
index cf71837..eaa9c07 100644
--- a/include/libnetfilter_queue/linux_list.h
+++ b/include/libnetfilter_queue/linux_list.h
@@ -6,6 +6,12 @@
 #undef offsetof
 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
 
+/*
+ * This is a cut-down copy of libnfnetlink/include/linux_list.h which is itself
+ * an old snapshot of linux/include/linux/list.h.
+ * This file only contains what we use.
+ */
+
 /**
  * container_of - cast a member of a structure out to the containing structure
  *
@@ -18,24 +24,8 @@
         typeof( ((type *)0)->member ) *__mptr = (ptr);	\
         (type *)( (char *)__mptr - offsetof(type,member) );})
 
-/*
- * Check at compile time that something is of a particular type.
- * Always evaluates to 1 so you may use it easily in comparisons.
- */
-#define typecheck(type,x) \
-({	type __dummy; \
-	typeof(x) __dummy2; \
-	(void)(&__dummy == &__dummy2); \
-	1; \
-})
-
 #define prefetch(x) ((void)0)
 
-/* empty define to make this work in userspace -HW */
-#ifndef smp_wmb
-#define smp_wmb()
-#endif
-
 /*
  * These are non-NULL pointers that will result in page faults
  * under normal circumstances, used to verify that nobody uses
@@ -58,11 +48,6 @@ struct list_head {
 	struct list_head *next, *prev;
 };
 
-#define LIST_HEAD_INIT(name) { &(name), &(name) }
-
-#define LIST_HEAD(name) \
-	struct list_head name = LIST_HEAD_INIT(name)
-
 #define INIT_LIST_HEAD(ptr) do { \
 	(ptr)->next = (ptr); (ptr)->prev = (ptr); \
 } while (0)
@@ -96,78 +81,6 @@ static inline void list_add(struct list_head *new, struct list_head *head)
 	__list_add(new, head, head->next);
 }
 
-/**
- * list_add_tail - add a new entry
- * @new: new entry to be added
- * @head: list head to add it before
- *
- * Insert a new entry before the specified head.
- * This is useful for implementing queues.
- */
-static inline void list_add_tail(struct list_head *new, struct list_head *head)
-{
-	__list_add(new, head->prev, head);
-}
-
-/*
- * Insert a new entry between two known consecutive entries.
- *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
- */
-static inline void __list_add_rcu(struct list_head * new,
-		struct list_head * prev, struct list_head * next)
-{
-	new->next = next;
-	new->prev = prev;
-	smp_wmb();
-	next->prev = new;
-	prev->next = new;
-}
-
-/**
- * list_add_rcu - add a new entry to rcu-protected list
- * @new: new entry to be added
- * @head: list head to add it after
- *
- * Insert a new entry after the specified head.
- * This is good for implementing stacks.
- *
- * The caller must take whatever precautions are necessary
- * (such as holding appropriate locks) to avoid racing
- * with another list-mutation primitive, such as list_add_rcu()
- * or list_del_rcu(), running on this same list.
- * However, it is perfectly legal to run concurrently with
- * the _rcu list-traversal primitives, such as
- * list_for_each_entry_rcu().
- */
-static inline void list_add_rcu(struct list_head *new, struct list_head *head)
-{
-	__list_add_rcu(new, head, head->next);
-}
-
-/**
- * list_add_tail_rcu - add a new entry to rcu-protected list
- * @new: new entry to be added
- * @head: list head to add it before
- *
- * Insert a new entry before the specified head.
- * This is useful for implementing queues.
- *
- * The caller must take whatever precautions are necessary
- * (such as holding appropriate locks) to avoid racing
- * with another list-mutation primitive, such as list_add_tail_rcu()
- * or list_del_rcu(), running on this same list.
- * However, it is perfectly legal to run concurrently with
- * the _rcu list-traversal primitives, such as
- * list_for_each_entry_rcu().
- */
-static inline void list_add_tail_rcu(struct list_head *new,
-					struct list_head *head)
-{
-	__list_add_rcu(new, head->prev, head);
-}
-
 /*
  * Delete a list entry by making the prev/next entries
  * point to each other.
@@ -194,69 +107,6 @@ static inline void list_del(struct list_head *entry)
 	entry->prev = LIST_POISON2;
 }
 
-/**
- * list_del_rcu - deletes entry from list without re-initialization
- * @entry: the element to delete from the list.
- *
- * Note: list_empty on entry does not return true after this,
- * the entry is in an undefined state. It is useful for RCU based
- * lockfree traversal.
- *
- * In particular, it means that we can not poison the forward
- * pointers that may still be used for walking the list.
- *
- * The caller must take whatever precautions are necessary
- * (such as holding appropriate locks) to avoid racing
- * with another list-mutation primitive, such as list_del_rcu()
- * or list_add_rcu(), running on this same list.
- * However, it is perfectly legal to run concurrently with
- * the _rcu list-traversal primitives, such as
- * list_for_each_entry_rcu().
- *
- * Note that the caller is not permitted to immediately free
- * the newly deleted entry.  Instead, either synchronize_kernel()
- * or call_rcu() must be used to defer freeing until an RCU
- * grace period has elapsed.
- */
-static inline void list_del_rcu(struct list_head *entry)
-{
-	__list_del(entry->prev, entry->next);
-	entry->prev = LIST_POISON2;
-}
-
-/**
- * list_del_init - deletes entry from list and reinitialize it.
- * @entry: the element to delete from the list.
- */
-static inline void list_del_init(struct list_head *entry)
-{
-	__list_del(entry->prev, entry->next);
-	INIT_LIST_HEAD(entry);
-}
-
-/**
- * list_move - delete from one list and add as another's head
- * @list: the entry to move
- * @head: the head that will precede our entry
- */
-static inline void list_move(struct list_head *list, struct list_head *head)
-{
-        __list_del(list->prev, list->next);
-        list_add(list, head);
-}
-
-/**
- * list_move_tail - delete from one list and add as another's tail
- * @list: the entry to move
- * @head: the head that will follow our entry
- */
-static inline void list_move_tail(struct list_head *list,
-				  struct list_head *head)
-{
-        __list_del(list->prev, list->next);
-        list_add_tail(list, head);
-}
-
 /**
  * list_empty - tests whether a list is empty
  * @head: the list to test.
@@ -265,66 +115,6 @@ static inline int list_empty(const struct list_head *head)
 {
 	return head->next == head;
 }
-
-/**
- * list_empty_careful - tests whether a list is
- * empty _and_ checks that no other CPU might be
- * in the process of still modifying either member
- *
- * NOTE: using list_empty_careful() without synchronization
- * can only be safe if the only activity that can happen
- * to the list entry is list_del_init(). Eg. it cannot be used
- * if another CPU could re-list_add() it.
- *
- * @head: the list to test.
- */
-static inline int list_empty_careful(const struct list_head *head)
-{
-	struct list_head *next = head->next;
-	return (next == head) && (next == head->prev);
-}
-
-static inline void __list_splice(struct list_head *list,
-				 struct list_head *head)
-{
-	struct list_head *first = list->next;
-	struct list_head *last = list->prev;
-	struct list_head *at = head->next;
-
-	first->prev = head;
-	head->next = first;
-
-	last->next = at;
-	at->prev = last;
-}
-
-/**
- * list_splice - join two lists
- * @list: the new list to add.
- * @head: the place to add it in the first list.
- */
-static inline void list_splice(struct list_head *list, struct list_head *head)
-{
-	if (!list_empty(list))
-		__list_splice(list, head);
-}
-
-/**
- * list_splice_init - join two lists and reinitialise the emptied list.
- * @list: the new list to add.
- * @head: the place to add it in the first list.
- *
- * The list at @list is reinitialised
- */
-static inline void list_splice_init(struct list_head *list,
-				    struct list_head *head)
-{
-	if (!list_empty(list)) {
-		__list_splice(list, head);
-		INIT_LIST_HEAD(list);
-	}
-}
-
 /**
  * list_entry - get the struct for this entry
  * @ptr:	the &struct list_head pointer.
@@ -334,47 +124,6 @@ static inline void list_splice_init(struct list_head *list,
 #define list_entry(ptr, type, member) \
 	container_of(ptr, type, member)
 
-/**
- * list_for_each	-	iterate over a list
- * @pos:	the &struct list_head to use as a loop counter.
- * @head:	the head for your list.
- */
-#define list_for_each(pos, head) \
-	for (pos = (head)->next, prefetch(pos->next); pos != (head); \
-        	pos = pos->next, prefetch(pos->next))
-
-/**
- * __list_for_each	-	iterate over a list
- * @pos:	the &struct list_head to use as a loop counter.
- * @head:	the head for your list.
- *
- * This variant differs from list_for_each() in that it's the
- * simplest possible list iteration code, no prefetching is done.
- * Use this for code that knows the list to be very short (empty
- * or 1 entry) most of the time.
- */
-#define __list_for_each(pos, head) \
-	for (pos = (head)->next; pos != (head); pos = pos->next)
-
-/**
- * list_for_each_prev	-	iterate over a list backwards
- * @pos:	the &struct list_head to use as a loop counter.
- * @head:	the head for your list.
- */
-#define list_for_each_prev(pos, head) \
-	for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \
-        	pos = pos->prev, prefetch(pos->prev))
-
-/**
- * list_for_each_safe	-	iterate over a list safe against removal of list entry
- * @pos:	the &struct list_head to use as a loop counter.
- * @n:		another &struct list_head to use as temporary storage
- * @head:	the head for your list.
- */
-#define list_for_each_safe(pos, n, head) \
-	for (pos = (head)->next, n = pos->next; pos != (head); \
-		pos = n, n = pos->next)
-
 /**
  * list_for_each_entry	-	iterate over list of given type
  * @pos:	the type * to use as a loop counter.
@@ -388,43 +137,6 @@ static inline void list_splice_init(struct list_head *list,
 	     pos = list_entry(pos->member.next, typeof(*pos), member),	\
 		     prefetch(pos->member.next))
 
-/**
- * list_for_each_entry_reverse - iterate backwards over list of given type.
- * @pos:	the type * to use as a loop counter.
- * @head:	the head for your list.
- * @member:	the name of the list_struct within the struct.
- */
-#define list_for_each_entry_reverse(pos, head, member)			\
-	for (pos = list_entry((head)->prev, typeof(*pos), member),	\
-		     prefetch(pos->member.prev);			\
-	     &pos->member != (head); 					\
-	     pos = list_entry(pos->member.prev, typeof(*pos), member),	\
-		     prefetch(pos->member.prev))
-
-/**
- * list_prepare_entry - prepare a pos entry for use as a start point in
- *			list_for_each_entry_continue
- * @pos:	the type * to use as a start point
- * @head:	the head of the list
- * @member:	the name of the list_struct within the struct.
- */
-#define list_prepare_entry(pos, head, member) \
-	((pos) ? : list_entry(head, typeof(*pos), member))
-
-/**
- * list_for_each_entry_continue -	iterate over list of given type
- *			continuing after existing point
- * @pos:	the type * to use as a loop counter.
- * @head:	the head for your list.
- * @member:	the name of the list_struct within the struct.
- */
-#define list_for_each_entry_continue(pos, head, member) 		\
-	for (pos = list_entry(pos->member.next, typeof(*pos), member),	\
-		     prefetch(pos->member.next);			\
-	     &pos->member != (head);					\
-	     pos = list_entry(pos->member.next, typeof(*pos), member),	\
-		     prefetch(pos->member.next))
-
 /**
  * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
  * @pos:	the type * to use as a loop counter.
@@ -438,290 +150,4 @@ static inline void list_splice_init(struct list_head *list,
 	     &pos->member != (head); 					\
 	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
 
-/**
- * list_for_each_rcu	-	iterate over an rcu-protected list
- * @pos:	the &struct list_head to use as a loop counter.
- * @head:	the head for your list.
- *
- * This list-traversal primitive may safely run concurrently with
- * the _rcu list-mutation primitives such as list_add_rcu()
- * as long as the traversal is guarded by rcu_read_lock().
- */
-#define list_for_each_rcu(pos, head) \
-	for (pos = (head)->next, prefetch(pos->next); pos != (head); \
-        	pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next))
-
-#define __list_for_each_rcu(pos, head) \
-	for (pos = (head)->next; pos != (head); \
-        	pos = pos->next, ({ smp_read_barrier_depends(); 0;}))
-
-/**
- * list_for_each_safe_rcu	-	iterate over an rcu-protected list safe
- *					against removal of list entry
- * @pos:	the &struct list_head to use as a loop counter.
- * @n:		another &struct list_head to use as temporary storage
- * @head:	the head for your list.
- *
- * This list-traversal primitive may safely run concurrently with
- * the _rcu list-mutation primitives such as list_add_rcu()
- * as long as the traversal is guarded by rcu_read_lock().
- */
-#define list_for_each_safe_rcu(pos, n, head) \
-	for (pos = (head)->next, n = pos->next; pos != (head); \
-		pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next)
-
-/**
- * list_for_each_entry_rcu	-	iterate over rcu list of given type
- * @pos:	the type * to use as a loop counter.
- * @head:	the head for your list.
- * @member:	the name of the list_struct within the struct.
- *
- * This list-traversal primitive may safely run concurrently with
- * the _rcu list-mutation primitives such as list_add_rcu()
- * as long as the traversal is guarded by rcu_read_lock().
- */
-#define list_for_each_entry_rcu(pos, head, member)			\
-	for (pos = list_entry((head)->next, typeof(*pos), member),	\
-		     prefetch(pos->member.next);			\
-	     &pos->member != (head); 					\
-	     pos = list_entry(pos->member.next, typeof(*pos), member),	\
-		     ({ smp_read_barrier_depends(); 0;}),		\
-		     prefetch(pos->member.next))
-
-
-/**
- * list_for_each_continue_rcu	-	iterate over an rcu-protected list
- *			continuing after existing point.
- * @pos:	the &struct list_head to use as a loop counter.
- * @head:	the head for your list.
- *
- * This list-traversal primitive may safely run concurrently with
- * the _rcu list-mutation primitives such as list_add_rcu()
- * as long as the traversal is guarded by rcu_read_lock().
- */
-#define list_for_each_continue_rcu(pos, head) \
-	for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \
-        	(pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next))
-
-/*
- * Double linked lists with a single pointer list head.
- * Mostly useful for hash tables where the two pointer list head is
- * too wasteful.
- * You lose the ability to access the tail in O(1).
- */
-
-struct hlist_head {
-	struct hlist_node *first;
-};
-
-struct hlist_node {
-	struct hlist_node *next, **pprev;
-};
-
-#define HLIST_HEAD_INIT { .first = NULL }
-#define HLIST_HEAD(name) struct hlist_head name = {  .first = NULL }
-#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
-#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL)
-
-static inline int hlist_unhashed(const struct hlist_node *h)
-{
-	return !h->pprev;
-}
-
-static inline int hlist_empty(const struct hlist_head *h)
-{
-	return !h->first;
-}
-
-static inline void __hlist_del(struct hlist_node *n)
-{
-	struct hlist_node *next = n->next;
-	struct hlist_node **pprev = n->pprev;
-	*pprev = next;
-	if (next)
-		next->pprev = pprev;
-}
-
-static inline void hlist_del(struct hlist_node *n)
-{
-	__hlist_del(n);
-	n->next = LIST_POISON1;
-	n->pprev = LIST_POISON2;
-}
-
-/**
- * hlist_del_rcu - deletes entry from hash list without re-initialization
- * @n: the element to delete from the hash list.
- *
- * Note: list_unhashed() on entry does not return true after this,
- * the entry is in an undefined state. It is useful for RCU based
- * lockfree traversal.
- *
- * In particular, it means that we can not poison the forward
- * pointers that may still be used for walking the hash list.
- *
- * The caller must take whatever precautions are necessary
- * (such as holding appropriate locks) to avoid racing
- * with another list-mutation primitive, such as hlist_add_head_rcu()
- * or hlist_del_rcu(), running on this same list.
- * However, it is perfectly legal to run concurrently with
- * the _rcu list-traversal primitives, such as
- * hlist_for_each_entry().
- */
-static inline void hlist_del_rcu(struct hlist_node *n)
-{
-	__hlist_del(n);
-	n->pprev = LIST_POISON2;
-}
-
-static inline void hlist_del_init(struct hlist_node *n)
-{
-	if (n->pprev)  {
-		__hlist_del(n);
-		INIT_HLIST_NODE(n);
-	}
-}
-
-#define hlist_del_rcu_init hlist_del_init
-
-static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
-{
-	struct hlist_node *first = h->first;
-	n->next = first;
-	if (first)
-		first->pprev = &n->next;
-	h->first = n;
-	n->pprev = &h->first;
-}
-
-
-/**
- * hlist_add_head_rcu - adds the specified element to the specified hlist,
- * while permitting racing traversals.
- * @n: the element to add to the hash list.
- * @h: the list to add to.
- *
- * The caller must take whatever precautions are necessary
- * (such as holding appropriate locks) to avoid racing
- * with another list-mutation primitive, such as hlist_add_head_rcu()
- * or hlist_del_rcu(), running on this same list.
- * However, it is perfectly legal to run concurrently with
- * the _rcu list-traversal primitives, such as
- * hlist_for_each_entry(), but only if smp_read_barrier_depends()
- * is used to prevent memory-consistency problems on Alpha CPUs.
- * Regardless of the type of CPU, the list-traversal primitive
- * must be guarded by rcu_read_lock().
- *
- * OK, so why don't we have an hlist_for_each_entry_rcu()???
- */
-static inline void hlist_add_head_rcu(struct hlist_node *n,
-					struct hlist_head *h)
-{
-	struct hlist_node *first = h->first;
-	n->next = first;
-	n->pprev = &h->first;
-	smp_wmb();
-	if (first)
-		first->pprev = &n->next;
-	h->first = n;
-}
-
-/* next must be != NULL */
-static inline void hlist_add_before(struct hlist_node *n,
-					struct hlist_node *next)
-{
-	n->pprev = next->pprev;
-	n->next = next;
-	next->pprev = &n->next;
-	*(n->pprev) = n;
-}
-
-static inline void hlist_add_after(struct hlist_node *n,
-					struct hlist_node *next)
-{
-	next->next = n->next;
-	n->next = next;
-	next->pprev = &n->next;
-
-	if(next->next)
-		next->next->pprev  = &next->next;
-}
-
-#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
-
-#define hlist_for_each(pos, head) \
-	for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
-	     pos = pos->next)
-
-#define hlist_for_each_safe(pos, n, head) \
-	for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
-	     pos = n)
-
-/**
- * hlist_for_each_entry	- iterate over list of given type
- * @tpos:	the type * to use as a loop counter.
- * @pos:	the &struct hlist_node to use as a loop counter.
- * @head:	the head for your list.
- * @member:	the name of the hlist_node within the struct.
- */
-#define hlist_for_each_entry(tpos, pos, head, member)			 \
-	for (pos = (head)->first;					 \
-	     pos && ({ prefetch(pos->next); 1;}) &&			 \
-		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
-	     pos = pos->next)
-
-/**
- * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point
- * @tpos:	the type * to use as a loop counter.
- * @pos:	the &struct hlist_node to use as a loop counter.
- * @member:	the name of the hlist_node within the struct.
- */
-#define hlist_for_each_entry_continue(tpos, pos, member)		 \
-	for (pos = (pos)->next;						 \
-	     pos && ({ prefetch(pos->next); 1;}) &&			 \
-		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
-	     pos = pos->next)
-
-/**
- * hlist_for_each_entry_from - iterate over a hlist continuing from existing point
- * @tpos:	the type * to use as a loop counter.
- * @pos:	the &struct hlist_node to use as a loop counter.
- * @member:	the name of the hlist_node within the struct.
- */
-#define hlist_for_each_entry_from(tpos, pos, member)			 \
-	for (; pos && ({ prefetch(pos->next); 1;}) &&			 \
-		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
-	     pos = pos->next)
-
-/**
- * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
- * @tpos:	the type * to use as a loop counter.
- * @pos:	the &struct hlist_node to use as a loop counter.
- * @n:		another &struct hlist_node to use as temporary storage
- * @head:	the head for your list.
- * @member:	the name of the hlist_node within the struct.
- */
-#define hlist_for_each_entry_safe(tpos, pos, n, head, member) 		 \
-	for (pos = (head)->first;					 \
-	     pos && ({ n = pos->next; 1; }) && 				 \
-		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
-	     pos = n)
-
-/**
- * hlist_for_each_entry_rcu - iterate over rcu list of given type
- * @pos:	the type * to use as a loop counter.
- * @pos:	the &struct hlist_node to use as a loop counter.
- * @head:	the head for your list.
- * @member:	the name of the hlist_node within the struct.
- *
- * This list-traversal primitive may safely run concurrently with
- * the _rcu list-mutation primitives such as hlist_add_rcu()
- * as long as the traversal is guarded by rcu_read_lock().
- */
-#define hlist_for_each_entry_rcu(tpos, pos, head, member)		 \
-	for (pos = (head)->first;					 \
-	     pos && ({ prefetch(pos->next); 1;}) &&			 \
-		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
-	     pos = pos->next, ({ smp_read_barrier_depends(); 0; }) )
-
 #endif
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 14/32] doc: Add linux_list.h to the doxygen system
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (14 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 13/32] include: Cherry-pick macros and functions that nlif will need Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 15/32] doc: Eliminate doxygen warnings from linux_list.h Duncan Roe
                       ` (17 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

Produce web and man pages for list_for_each_entry() and other macros.
Mostly a straight conversion of the kerneldoc but also document
struct list_head and macro INIT_LIST_HEAD.

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 doxygen/Makefile.am                     |  1 +
 doxygen/build_man.sh                    |  7 ++-
 doxygen/doxygen.cfg.in                  |  5 ++-
 include/libnetfilter_queue/linux_list.h | 60 +++++++++++++++++--------
 4 files changed, 52 insertions(+), 21 deletions(-)

diff --git a/doxygen/Makefile.am b/doxygen/Makefile.am
index 68be963..6135f25 100644
--- a/doxygen/Makefile.am
+++ b/doxygen/Makefile.am
@@ -8,6 +8,7 @@ doc_srcs = $(top_srcdir)/src/libnetfilter_queue.c\
            $(top_srcdir)/src/extra/ipv6.c\
            $(top_srcdir)/src/extra/tcp.c\
            $(top_srcdir)/src/extra/udp.c\
+           $(top_srcdir)/include/libnetfilter_queue/linux_list.h\
            $(top_srcdir)/src/extra/icmp.c
 
 doxyfile.stamp: $(doc_srcs) Makefile build_man.sh
diff --git a/doxygen/build_man.sh b/doxygen/build_man.sh
index 7eab8fa..643ad42 100755
--- a/doxygen/build_man.sh
+++ b/doxygen/build_man.sh
@@ -84,7 +84,12 @@ post_process(){
 
 make_man7(){
   popd >/dev/null
-  target=$(grep -Ew INPUT doxygen.cfg | rev | cut -f1 -d' ' | rev)/$2
+
+  # This grep command works for multiple directories on the INPUT line,
+  # as long as the directory containing the source with the main page
+  # comes first.
+  target=/$(grep -Ew INPUT doxygen.cfg | cut -f2- -d/ | cut -f1 -d' ')/$2
+
   mypath=$(dirname $0)
 
   # Build up temporary source in temp.c
diff --git a/doxygen/doxygen.cfg.in b/doxygen/doxygen.cfg.in
index fcfc045..e69dcd7 100644
--- a/doxygen/doxygen.cfg.in
+++ b/doxygen/doxygen.cfg.in
@@ -5,8 +5,9 @@ ABBREVIATE_BRIEF       =
 FULL_PATH_NAMES        = NO
 TAB_SIZE               = 8
 OPTIMIZE_OUTPUT_FOR_C  = YES
-INPUT                  = @abs_top_srcdir@/src
-FILE_PATTERNS          = *.c
+INPUT                  = @abs_top_srcdir@/src \
+                         @abs_top_srcdir@/include/libnetfilter_queue
+FILE_PATTERNS          = *.c linux_list.h
 RECURSIVE              = YES
 EXCLUDE_SYMBOLS        = EXPORT_SYMBOL \
                          tcp_word_hdr \
diff --git a/include/libnetfilter_queue/linux_list.h b/include/libnetfilter_queue/linux_list.h
index eaa9c07..88ea386 100644
--- a/include/libnetfilter_queue/linux_list.h
+++ b/include/libnetfilter_queue/linux_list.h
@@ -12,17 +12,23 @@
  * This file only contains what we use.
  */
 
+/**
+ * \defgroup List Simple doubly linked list implementation
+ * @{
+ */
+
+
 /**
  * container_of - cast a member of a structure out to the containing structure
  *
- * @ptr:	the pointer to the member.
- * @type:	the type of the container struct this is embedded in.
- * @member:	the name of the member within the struct.
+ * \param ptr:	the pointer to the member.
+ * \param type:	the type of the container struct this is embedded in.
+ * \param member:	the name of the member within the struct.
  *
  */
 #define container_of(ptr, type, member) ({			\
-        typeof( ((type *)0)->member ) *__mptr = (ptr);	\
-        (type *)( (char *)__mptr - offsetof(type,member) );})
+	typeof( ((type *)0)->member ) *__mptr = (ptr);	\
+	(type *)( (char *)__mptr - offsetof(type,member) );})
 
 #define prefetch(x) ((void)0)
 
@@ -44,10 +50,24 @@
  * using the generic single-entry routines.
  */
 
+/**
+ * \struct list_head
+ * Link to adjacent members of the circular list
+ * \note Each member of a list must start with this structure
+ * (containing structures OK)
+ * \var list_head::next
+ * pointer to the next list member
+ * \var list_head::prev
+ * pointer to the previous list member
+ */
 struct list_head {
 	struct list_head *next, *prev;
 };
 
+/**
+ * INIT_LIST_HEAD - Initialise first member of a new list
+ * \param ptr the &struct list_head pointer.
+ */
 #define INIT_LIST_HEAD(ptr) do { \
 	(ptr)->next = (ptr); (ptr)->prev = (ptr); \
 } while (0)
@@ -70,8 +90,8 @@ static inline void __list_add(struct list_head *new,
 
 /**
  * list_add - add a new entry
- * @new: new entry to be added
- * @head: list head to add it after
+ * \param new: new entry to be added
+ * \param head: list head to add it after
  *
  * Insert a new entry after the specified head.
  * This is good for implementing stacks.
@@ -96,7 +116,7 @@ static inline void __list_del(struct list_head * prev, struct list_head * next)
 
 /**
  * list_del - deletes entry from list.
- * @entry: the element to delete from the list.
+ * \param entry: the element to delete from the list.
  * Note: list_empty on entry does not return true after this, the entry is
  * in an undefined state.
  */
@@ -117,18 +137,18 @@ static inline int list_empty(const struct list_head *head)
 }
 /**
  * list_entry - get the struct for this entry
- * @ptr:	the &struct list_head pointer.
- * @type:	the type of the struct this is embedded in.
- * @member:	the name of the list_struct within the struct.
+ * \param ptr:	the &struct list_head pointer.
+ * \param type:	the type of the struct this is embedded in.
+ * \param member:	the name of the list_struct within the struct.
  */
 #define list_entry(ptr, type, member) \
 	container_of(ptr, type, member)
 
 /**
  * list_for_each_entry	-	iterate over list of given type
- * @pos:	the type * to use as a loop counter.
- * @head:	the head for your list.
- * @member:	the name of the list_struct within the struct.
+ * \param pos:	the type * to use as a loop counter.
+ * \param head:	the head for your list.
+ * \param member:	the name of the list_struct within the struct.
  */
 #define list_for_each_entry(pos, head, member)				\
 	for (pos = list_entry((head)->next, typeof(*pos), member),	\
@@ -139,10 +159,10 @@ static inline int list_empty(const struct list_head *head)
 
 /**
  * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
- * @pos:	the type * to use as a loop counter.
- * @n:		another type * to use as temporary storage
- * @head:	the head for your list.
- * @member:	the name of the list_struct within the struct.
+ * \param pos:	the type * to use as a loop counter.
+ * \param n:		another type * to use as temporary storage
+ * \param head:	the head for your list.
+ * \param member:	the name of the list_struct within the struct.
  */
 #define list_for_each_entry_safe(pos, n, head, member)			\
 	for (pos = list_entry((head)->next, typeof(*pos), member),	\
@@ -150,4 +170,8 @@ static inline int list_empty(const struct list_head *head)
 	     &pos->member != (head); 					\
 	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
 
+/**
+ * @}
+ */
+
 #endif
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 15/32] doc: Eliminate doxygen warnings from linux_list.h
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (15 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 14/32] doc: Add linux_list.h to the doxygen system Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 16/32] doc: Eliminate doxygen warnings from iftable.c Duncan Roe
                       ` (16 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

The warnings concerned prefetch(), LIST_POISON1 & LIST_POISON2.
Remove all 3 macros since they do nothing useful in userspace programs.
Also take a few doxygen comment improvements from 6.6 Linux source.

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 include/libnetfilter_queue/linux_list.h | 44 ++++++++++++-------------
 1 file changed, 21 insertions(+), 23 deletions(-)

diff --git a/include/libnetfilter_queue/linux_list.h b/include/libnetfilter_queue/linux_list.h
index 88ea386..500481d 100644
--- a/include/libnetfilter_queue/linux_list.h
+++ b/include/libnetfilter_queue/linux_list.h
@@ -10,10 +10,20 @@
  * This is a cut-down copy of libnfnetlink/include/linux_list.h which is itself
  * an old snapshot of linux/include/linux/list.h.
  * This file only contains what we use.
+ *
+ * 2024-01-27 12:45:41 +1100 duncan_roe@optusnet.com.au
+ * LIST_POISONx doesn't really work for user space - just use NULL
+ *
+ * 2024-01-27 18:16:51 +1100 duncan_roe@optusnet.com.au
+ * I can't see how the prefetch() calls do any good so remove them
+ * and #define of prefetch
+ *
+ * 2024-01-27 18:53:46 +1100 duncan_roe@optusnet.com.au
+ * Take a few doxygen comment improvements from 6.6 Linux source
  */
 
 /**
- * \defgroup List Simple doubly linked list implementation
+ * \defgroup List Circular doubly linked list implementation
  * @{
  */
 
@@ -30,18 +40,8 @@
 	typeof( ((type *)0)->member ) *__mptr = (ptr);	\
 	(type *)( (char *)__mptr - offsetof(type,member) );})
 
-#define prefetch(x) ((void)0)
-
 /*
- * These are non-NULL pointers that will result in page faults
- * under normal circumstances, used to verify that nobody uses
- * non-initialized list entries.
- */
-#define LIST_POISON1  ((void *) 0x00100100)
-#define LIST_POISON2  ((void *) 0x00200200)
-
-/*
- * Simple doubly linked list implementation.
+ * Circular doubly linked list implementation.
  *
  * Some of the internal functions ("__xxx") are useful when
  * manipulating whole lists rather than single entries, as
@@ -123,8 +123,8 @@ static inline void __list_del(struct list_head * prev, struct list_head * next)
 static inline void list_del(struct list_head *entry)
 {
 	__list_del(entry->prev, entry->next);
-	entry->next = LIST_POISON1;
-	entry->prev = LIST_POISON2;
+	entry->next = NULL;
+	entry->prev = NULL;
 }
 
 /**
@@ -139,30 +139,28 @@ static inline int list_empty(const struct list_head *head)
  * list_entry - get the struct for this entry
  * \param ptr:	the &struct list_head pointer.
  * \param type:	the type of the struct this is embedded in.
- * \param member:	the name of the list_struct within the struct.
+ * \param member:	the name of the list_head within the struct.
  */
 #define list_entry(ptr, type, member) \
 	container_of(ptr, type, member)
 
 /**
  * list_for_each_entry	-	iterate over list of given type
- * \param pos:	the type * to use as a loop counter.
+ * \param pos:	the type * to use as a loop cursor.
  * \param head:	the head for your list.
- * \param member:	the name of the list_struct within the struct.
+ * \param member:	the name of the list_head within the struct.
  */
 #define list_for_each_entry(pos, head, member)				\
-	for (pos = list_entry((head)->next, typeof(*pos), member),	\
-		     prefetch(pos->member.next);			\
+	for (pos = list_entry((head)->next, typeof(*pos), member);	\
 	     &pos->member != (head); 					\
-	     pos = list_entry(pos->member.next, typeof(*pos), member),	\
-		     prefetch(pos->member.next))
+	     pos = list_entry(pos->member.next, typeof(*pos), member))	\
 
 /**
  * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
- * \param pos:	the type * to use as a loop counter.
+ * \param pos:	the type * to use as a loop cursor.
  * \param n:		another type * to use as temporary storage
  * \param head:	the head for your list.
- * \param member:	the name of the list_struct within the struct.
+ * \param member:	the name of the list_head within the struct.
  */
 #define list_for_each_entry_safe(pos, n, head, member)			\
 	for (pos = list_entry((head)->next, typeof(*pos), member),	\
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 16/32] doc: Eliminate doxygen warnings from iftable.c
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (16 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 15/32] doc: Eliminate doxygen warnings from linux_list.h Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 17/32] whitespace: remove trailing spaces " Duncan Roe
                       ` (15 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

Introduce some new doxygen content. Not yet converted to use libmnl.

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 doxygen/Makefile.am    |  1 +
 doxygen/doxygen.cfg.in |  2 ++
 src/iftable.c          | 53 +++++++++++++++++++++++++-----------------
 3 files changed, 35 insertions(+), 21 deletions(-)

diff --git a/doxygen/Makefile.am b/doxygen/Makefile.am
index 6135f25..aae1ccc 100644
--- a/doxygen/Makefile.am
+++ b/doxygen/Makefile.am
@@ -2,6 +2,7 @@ if HAVE_DOXYGEN
 
 doc_srcs = $(top_srcdir)/src/libnetfilter_queue.c\
            $(top_srcdir)/src/nlmsg.c\
+           $(top_srcdir)/src/iftable.c\
            $(top_srcdir)/src/extra/checksum.c\
            $(top_srcdir)/src/extra/ipv4.c\
            $(top_srcdir)/src/extra/pktbuff.c\
diff --git a/doxygen/doxygen.cfg.in b/doxygen/doxygen.cfg.in
index e69dcd7..c795df1 100644
--- a/doxygen/doxygen.cfg.in
+++ b/doxygen/doxygen.cfg.in
@@ -17,6 +17,8 @@ EXCLUDE_SYMBOLS        = EXPORT_SYMBOL \
                          nfnl_handle \
                          nfnl_subsys_handle \
                          mnl_socket \
+                         ifindex_node \
+                         nlif_handle \
                          nfnl_callback2 \
                          tcp_flag_word
 EXAMPLE_PATTERNS       =
diff --git a/src/iftable.c b/src/iftable.c
index aab59b3..22c3952 100644
--- a/src/iftable.c
+++ b/src/iftable.c
@@ -2,6 +2,7 @@
  *
  * (C) 2004 by Astaro AG, written by Harald Welte <hwelte@astaro.com>
  * (C) 2008 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2024 by Duncan Roe <duncan_roe@optusnet.com.au>
  *
  * This software is Free Software and licensed under GNU GPLv2+.
  */
@@ -25,11 +26,14 @@
 #include "linux_list.h"
 
 /**
- * \defgroup iftable Functions in iftable.c [DEPRECATED]
- * This documentation is provided for the benefit of maintainers of legacy code.
+ * \defgroup iftable Functions to manage a table of network interfaces
+ * These functions maintain a database of the name and flags of each
+ * network interface.
  *
- * New applications should use
- * [libmnl](https://netfilter.org/projects/libmnl/doxygen/html/).
+ * mnl API programs may instead use
+ * [libmnl](https://netfilter.org/projects/libmnl/doxygen/html/)
+ * calls directly to maintain an
+ * interface table with more (or less!) data points, e.g. MTU.
  * @{
  */
 
@@ -52,8 +56,8 @@ struct nlif_handle {
 };
 
 /* iftable_add - Add/Update an entry to/in the interface table
- * @n:		netlink message header of a RTM_NEWLINK message
- * @arg:	not used
+ * \param n:	netlink message header of a RTM_NEWLINK message
+ * \param arg:	not used
  *
  * This function adds/updates an entry in the intrface table.
  * Returns -1 on error, 1 on success.
@@ -114,8 +118,8 @@ static int iftable_add(struct nlmsghdr *n, void *arg)
 }
 
 /* iftable_del - Delete an entry from the interface table
- * @n:		netlink message header of a RTM_DELLINK nlmsg
- * @arg:	not used
+ * \param n:	netlink message header of a RTM_DELLINK nlmsg
+ * \param arg:	not used
  *
  * Delete an entry from the interface table.  
  * Returns -1 on error, 0 if no matching entry was found or 1 on success.
@@ -148,9 +152,10 @@ static int iftable_del(struct nlmsghdr *n, void *arg)
 	return 0;
 }
 
-/** Get the name for an ifindex
+/**
+ * nlif_index2name - get the name for an ifindex
  *
- * \param nlif_handle A pointer to a ::nlif_handle created
+ * \param h pointer to nlif_handle created by nlif_open()
  * \param index ifindex to be resolved
  * \param name interface name, pass a buffer of IFNAMSIZ size
  * \return -1 on error, 1 on success 
@@ -182,9 +187,10 @@ int nlif_index2name(struct nlif_handle *h,
 	return -1;
 }
 
-/** Get the flags for an ifindex
+/**
+ * nlif_get_ifflags - get the flags for an ifindex
  *
- * \param nlif_handle A pointer to a ::nlif_handle created
+ * \param h pointer to nlif_handle created by nlif_open()
  * \param index ifindex to be resolved
  * \param flags pointer to variable used to store the interface flags
  * \return -1 on error, 1 on success 
@@ -215,7 +221,8 @@ int nlif_get_ifflags(const struct nlif_handle *h,
 	return -1;
 }
 
-/** Initialize interface table
+/**
+ * nlif_open - initialize interface table
  *
  * Initialize rtnl interface and interface table
  * Call this before any nlif_* function
@@ -262,10 +269,10 @@ err:
 	return NULL;
 }
 
-/** Destructor of interface table
+/**
+ * nlif_close - free all resources associated with the interface table
  *
- * \param nlif_handle A pointer to a ::nlif_handle created 
- * via nlif_open()
+ * \param h pointer to nlif_handle created by nlif_open()
  */
 void nlif_close(struct nlif_handle *h)
 {
@@ -289,9 +296,12 @@ void nlif_close(struct nlif_handle *h)
 	h = NULL; /* bugtrap */
 }
 
-/** Receive message from netlink and update interface table
+/**
+ * nlif_catch - receive message from netlink and update interface table
+ *
+ * FIXME - elaborate a bit
  *
- * \param nlif_handle A pointer to a ::nlif_handle created
+ * \param h pointer to nlif_handle created by nlif_open()
  * \return 0 if OK
  */
 int nlif_catch(struct nlif_handle *h)
@@ -316,7 +326,7 @@ static int nlif_catch_multi(struct nlif_handle *h)
 
 /** 
  * nlif_query - request a dump of interfaces available in the system
- * @h: pointer to a valid nlif_handler
+ * \param h: pointer to a valid nlif_handler
  */
 int nlif_query(struct nlif_handle *h)
 {
@@ -328,9 +338,10 @@ int nlif_query(struct nlif_handle *h)
 	return nlif_catch_multi(h);
 }
 
-/** Returns socket descriptor for the netlink socket
+/**
+ * nlif_fd - get file descriptor for the netlink socket
  *
- * \param nlif_handle A pointer to a ::nlif_handle created
+ * \param h pointer to nlif_handle created by nlif_open()
  * \return The fd or -1 if there's an error
  */
 int nlif_fd(struct nlif_handle *h)
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 17/32] whitespace: remove trailing spaces from iftable.c
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (17 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 16/32] doc: Eliminate doxygen warnings from iftable.c Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 18/32] include: Use libmnl.h instead of libnfnetlink.h Duncan Roe
                       ` (14 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

Done in a separate commit to ease review of real changes.

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 src/iftable.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/iftable.c b/src/iftable.c
index 22c3952..307acc1 100644
--- a/src/iftable.c
+++ b/src/iftable.c
@@ -121,7 +121,7 @@ static int iftable_add(struct nlmsghdr *n, void *arg)
  * \param n:	netlink message header of a RTM_DELLINK nlmsg
  * \param arg:	not used
  *
- * Delete an entry from the interface table.  
+ * Delete an entry from the interface table.
  * Returns -1 on error, 0 if no matching entry was found or 1 on success.
  */
 static int iftable_del(struct nlmsghdr *n, void *arg)
@@ -158,9 +158,9 @@ static int iftable_del(struct nlmsghdr *n, void *arg)
  * \param h pointer to nlif_handle created by nlif_open()
  * \param index ifindex to be resolved
  * \param name interface name, pass a buffer of IFNAMSIZ size
- * \return -1 on error, 1 on success 
+ * \return -1 on error, 1 on success
  */
-int nlif_index2name(struct nlif_handle *h, 
+int nlif_index2name(struct nlif_handle *h,
 		    unsigned int index,
 		    char *name)
 {
@@ -193,7 +193,7 @@ int nlif_index2name(struct nlif_handle *h,
  * \param h pointer to nlif_handle created by nlif_open()
  * \param index ifindex to be resolved
  * \param flags pointer to variable used to store the interface flags
- * \return -1 on error, 1 on success 
+ * \return -1 on error, 1 on success
  */
 int nlif_get_ifflags(const struct nlif_handle *h,
 		     unsigned int index,
@@ -324,7 +324,7 @@ static int nlif_catch_multi(struct nlif_handle *h)
 	return -1;
 }
 
-/** 
+/**
  * nlif_query - request a dump of interfaces available in the system
  * \param h: pointer to a valid nlif_handler
  */
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 18/32] include: Use libmnl.h instead of libnfnetlink.h
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (18 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 17/32] whitespace: remove trailing spaces " Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 19/32] src: Convert all nlif_* functions to use libmnl Duncan Roe
                       ` (13 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

Add nlif_* prototypes and 64-bit converters to libnetfilter_queue.h.
Fix a couple of libnfnetlink.h references still in libnetfilter_queue.c.

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 .../libnetfilter_queue/libnetfilter_queue.h   | 36 ++++++++++++++++++-
 src/libnetfilter_queue.c                      |  4 +--
 2 files changed, 37 insertions(+), 3 deletions(-)

diff --git a/include/libnetfilter_queue/libnetfilter_queue.h b/include/libnetfilter_queue/libnetfilter_queue.h
index 9bd9c43..84a8a7e 100644
--- a/include/libnetfilter_queue/libnetfilter_queue.h
+++ b/include/libnetfilter_queue/libnetfilter_queue.h
@@ -14,7 +14,7 @@
 #define __LIBCTNETLINK_H
 
 #include <sys/time.h>
-#include <libnfnetlink/libnfnetlink.h>
+#include <libmnl/libmnl.h>
 
 #include <libnetfilter_queue/linux_list.h>
 #include <libnetfilter_queue/linux_nfnetlink_queue.h>
@@ -26,6 +26,7 @@ extern "C" {
 struct nfq_handle;
 struct nfq_q_handle;
 struct nfq_data;
+struct nlif_handle;
 
 extern int nfq_errno;
 
@@ -156,8 +157,41 @@ 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);
 
+/*
+ * Network Interface Table API
+ */
+
+#ifndef IFNAMSIZ
+#define IFNAMSIZ 16
+#endif
+
+struct nlif_handle *nlif_open(void);
+void nlif_close(struct nlif_handle *orig);
+int nlif_fd(struct nlif_handle *nlif_handle);
+int nlif_query(struct nlif_handle *nlif_handle);
+int nlif_catch(struct nlif_handle *nlif_handle);
+int nlif_index2name(struct nlif_handle *nlif_handle, unsigned int if_index, char *name);
+int nlif_get_ifflags(const struct nlif_handle *h, unsigned int index, unsigned int *flags);
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
 
+/*
+ * __be46 stuff - should be in libmnl.h maybe?
+ */
+
+#include <byteswap.h>
+#if __BYTE_ORDER == __BIG_ENDIAN
+#  ifndef __be64_to_cpu
+#  define __be64_to_cpu(x)     (x)
+#  endif
+# else
+# if __BYTE_ORDER == __LITTLE_ENDIAN
+#  ifndef __be64_to_cpu
+#  define __be64_to_cpu(x)     __bswap_64(x)
+#  endif
+# endif
+#endif
+
 #endif	/* __LIBNFQNETLINK_H */
diff --git a/src/libnetfilter_queue.c b/src/libnetfilter_queue.c
index 2f50b47..3c3f951 100644
--- a/src/libnetfilter_queue.c
+++ b/src/libnetfilter_queue.c
@@ -151,7 +151,7 @@ struct nfnl_subsys_handle {
 	uint32_t		subscriptions;
 	uint8_t			subsys_id;
 	uint8_t			cb_count;
-	struct nfnl_callback	*cb;	/* array of callbacks */
+	struct nfnl_callback2	*cb; /* Not an exact copy: array of callbacks */
 };
 
 struct nfnl_handle {
@@ -479,7 +479,7 @@ struct nfq_handle *nfq_open(void)
 	h->nfnlh->fd = h->nl->fd;
 	h->nfnlh->local = h->nl->addr;
 	h->nfnlh->peer.nl_family = AF_NETLINK;
-	h->nfnlh->rcv_buffer_size = NFNL_BUFFSIZE;
+	h->nfnlh->rcv_buffer_size = MNL_SOCKET_BUFFER_SIZE;
 
 	if (!fill_nfnl_subsys_handle(h))
 		goto err_close;
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 19/32] src: Convert all nlif_* functions to use libmnl
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (19 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 18/32] include: Use libmnl.h instead of libnfnetlink.h Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 20/32] src: Delete rtnl.c Duncan Roe
                       ` (12 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

In iftable.c, replace calls to functions in rtnetlink.c with inline code
(converted to use libmnl instead of libnfnetlink).

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 src/Makefile.am |   1 +
 src/iftable.c   | 304 ++++++++++++++++++++++--------------------------
 2 files changed, 138 insertions(+), 167 deletions(-)

diff --git a/src/Makefile.am b/src/Makefile.am
index 079853e..a6813e8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -30,6 +30,7 @@ libnetfilter_queue_la_LDFLAGS = -Wc,-nostartfiles \
 				-version-info $(LIBVERSION)
 libnetfilter_queue_la_SOURCES = libnetfilter_queue.c	\
 				nlmsg.c			\
+				iftable.c		\
 				extra/checksum.c	\
 				extra/icmp.c		\
 				extra/ipv6.c		\
diff --git a/src/iftable.c b/src/iftable.c
index 307acc1..76a6cad 100644
--- a/src/iftable.c
+++ b/src/iftable.c
@@ -11,19 +11,27 @@
 
 #include <unistd.h>
 #include <stdlib.h>
+#include <time.h>
 #include <stdio.h>
 #include <string.h>
 #include <sys/types.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <errno.h>
-#include <assert.h>
 
 #include <linux/netdevice.h>
 
-#include <libnfnetlink/libnfnetlink.h>
-#include "rtnl.h"
-#include "linux_list.h"
+#include <linux/rtnetlink.h>
+#include <linux/netfilter/nfnetlink_queue.h>
+#include <libnetfilter_queue/libnetfilter_queue.h>
+
+#include "internal.h"
+
+#define NUM_NLIF_BITS 4
+#define NUM_NLIF_ENTRIES (1 << NUM_NLIF_BITS)
+#define NLIF_ENTRY_MASK (NUM_NLIF_ENTRIES -1)
+
+static int data_cb(const struct nlmsghdr *nlh, void *data);
 
 /**
  * \defgroup iftable Functions to manage a table of network interfaces
@@ -42,116 +50,16 @@ struct ifindex_node {
 
 	uint32_t	index;
 	uint32_t	type;
-	uint32_t	alen;
 	uint32_t	flags;
-	char		addr[8];
-	char		name[16];
+	char		name[IFNAMSIZ];
 };
 
 struct nlif_handle {
-	struct list_head ifindex_hash[16];
-	struct rtnl_handle *rtnl_handle;
-	struct rtnl_handler ifadd_handler;
-	struct rtnl_handler ifdel_handler;
+	struct list_head ifindex_hash[NUM_NLIF_ENTRIES];
+	struct mnl_socket *nl;
+	unsigned int portid;
 };
 
-/* iftable_add - Add/Update an entry to/in the interface table
- * \param n:	netlink message header of a RTM_NEWLINK message
- * \param arg:	not used
- *
- * This function adds/updates an entry in the intrface table.
- * Returns -1 on error, 1 on success.
- */
-static int iftable_add(struct nlmsghdr *n, void *arg)
-{
-	unsigned int hash, found = 0;
-	struct ifinfomsg *ifi_msg = NLMSG_DATA(n);
-	struct ifindex_node *this;
-	struct rtattr *cb[IFLA_MAX+1];
-	struct nlif_handle *h = arg;
-
-	if (n->nlmsg_type != RTM_NEWLINK)
-		return -1;
-
-	if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi_msg)))
-		return -1;
-
-	rtnl_parse_rtattr(cb, IFLA_MAX, IFLA_RTA(ifi_msg), IFLA_PAYLOAD(n));
-
-	if (!cb[IFLA_IFNAME])
-		return -1;
-
-	hash = ifi_msg->ifi_index & 0xF;
-	list_for_each_entry(this, &h->ifindex_hash[hash], head) {
-		if (this->index == ifi_msg->ifi_index) {
-			found = 1;
-			break;
-		}
-	}
-
-	if (!found) {
-		this = malloc(sizeof(*this));
-		if (!this)
-			return -1;
-
-		this->index = ifi_msg->ifi_index;
-	}
-
-	this->type = ifi_msg->ifi_type;
-	this->flags = ifi_msg->ifi_flags;
-	if (cb[IFLA_ADDRESS]) {
-		unsigned int alen;
-		this->alen = alen = RTA_PAYLOAD(cb[IFLA_ADDRESS]);
-		if (alen > sizeof(this->addr))
-			alen = sizeof(this->addr);
-		memcpy(this->addr, RTA_DATA(cb[IFLA_ADDRESS]), alen);
-	} else {
-		this->alen = 0;
-		memset(this->addr, 0, sizeof(this->addr));
-	}
-	strcpy(this->name, RTA_DATA(cb[IFLA_IFNAME]));
-
-	if (!found)
-		list_add(&this->head, &h->ifindex_hash[hash]);
-
-	return 1;
-}
-
-/* iftable_del - Delete an entry from the interface table
- * \param n:	netlink message header of a RTM_DELLINK nlmsg
- * \param arg:	not used
- *
- * Delete an entry from the interface table.
- * Returns -1 on error, 0 if no matching entry was found or 1 on success.
- */
-static int iftable_del(struct nlmsghdr *n, void *arg)
-{
-	struct ifinfomsg *ifi_msg = NLMSG_DATA(n);
-	struct rtattr *cb[IFLA_MAX+1];
-	struct nlif_handle *h = arg;
-	struct ifindex_node *this, *tmp;
-	unsigned int hash;
-
-	if (n->nlmsg_type != RTM_DELLINK)
-		return -1;
-
-	if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi_msg)))
-		return -1;
-
-	rtnl_parse_rtattr(cb, IFLA_MAX, IFLA_RTA(ifi_msg), IFLA_PAYLOAD(n));
-
-	hash = ifi_msg->ifi_index & 0xF;
-	list_for_each_entry_safe(this, tmp, &h->ifindex_hash[hash], head) {
-		if (this->index == ifi_msg->ifi_index) {
-			list_del(&this->head);
-			free(this);
-			return 1;
-		}
-	}
-
-	return 0;
-}
-
 /**
  * nlif_index2name - get the name for an ifindex
  *
@@ -160,6 +68,7 @@ static int iftable_del(struct nlmsghdr *n, void *arg)
  * \param name interface name, pass a buffer of IFNAMSIZ size
  * \return -1 on error, 1 on success
  */
+EXPORT_SYMBOL
 int nlif_index2name(struct nlif_handle *h,
 		    unsigned int index,
 		    char *name)
@@ -167,9 +76,6 @@ int nlif_index2name(struct nlif_handle *h,
 	unsigned int hash;
 	struct ifindex_node *this;
 
-	assert(h != NULL);
-	assert(name != NULL);
-
 	if (index == 0) {
 		strcpy(name, "*");
 		return 1;
@@ -195,6 +101,7 @@ int nlif_index2name(struct nlif_handle *h,
  * \param flags pointer to variable used to store the interface flags
  * \return -1 on error, 1 on success
  */
+EXPORT_SYMBOL
 int nlif_get_ifflags(const struct nlif_handle *h,
 		     unsigned int index,
 		     unsigned int *flags)
@@ -202,9 +109,6 @@ int nlif_get_ifflags(const struct nlif_handle *h,
 	unsigned int hash;
 	struct ifindex_node *this;
 
-	assert(h != NULL);
-	assert(flags != NULL);
-
 	if (index == 0) {
 		errno = ENOENT;
 		return -1;
@@ -224,11 +128,12 @@ int nlif_get_ifflags(const struct nlif_handle *h,
 /**
  * nlif_open - initialize interface table
  *
- * Initialize rtnl interface and interface table
+ * Open a netlink socket and initialize interface table
  * Call this before any nlif_* function
  *
- * \return file descriptor to netlink socket
+ * \return NULL on error, else valid pointer to an nlif_handle structure
  */
+EXPORT_SYMBOL
 struct nlif_handle *nlif_open(void)
 {
 	int i;
@@ -238,32 +143,22 @@ struct nlif_handle *nlif_open(void)
 	if (h == NULL)
 		goto err;
 
-	for (i=0; i<16; i++)
+	for (i=0; i < NUM_NLIF_ENTRIES; i++)
 		INIT_LIST_HEAD(&h->ifindex_hash[i]);
 
-	h->ifadd_handler.nlmsg_type = RTM_NEWLINK;
-	h->ifadd_handler.handlefn = iftable_add;
-	h->ifadd_handler.arg = h;
-	h->ifdel_handler.nlmsg_type = RTM_DELLINK;
-	h->ifdel_handler.handlefn = iftable_del;
-	h->ifdel_handler.arg = h;
+	h->nl = mnl_socket_open(NETLINK_ROUTE);
+	if (!h->nl)
+		goto err_free;
 
-	h->rtnl_handle = rtnl_open();
-	if (h->rtnl_handle == NULL)
-		goto err;
-
-	if (rtnl_handler_register(h->rtnl_handle, &h->ifadd_handler) < 0)
+	if (mnl_socket_bind(h->nl, RTMGRP_LINK, MNL_SOCKET_AUTOPID) < 0)
 		goto err_close;
-
-	if (rtnl_handler_register(h->rtnl_handle, &h->ifdel_handler) < 0)
-		goto err_unregister;
+	h->portid = mnl_socket_get_portid(h->nl);
 
 	return h;
 
-err_unregister:
-	rtnl_handler_unregister(h->rtnl_handle, &h->ifadd_handler);
 err_close:
-	rtnl_close(h->rtnl_handle);
+	mnl_socket_close(h->nl);
+err_free:
 	free(h);
 err:
 	return NULL;
@@ -274,18 +169,15 @@ err:
  *
  * \param h pointer to nlif_handle created by nlif_open()
  */
+EXPORT_SYMBOL
 void nlif_close(struct nlif_handle *h)
 {
 	int i;
 	struct ifindex_node *this, *tmp;
 
-	assert(h != NULL);
+	mnl_socket_close(h->nl);
 
-	rtnl_handler_unregister(h->rtnl_handle, &h->ifadd_handler);
-	rtnl_handler_unregister(h->rtnl_handle, &h->ifdel_handler);
-	rtnl_close(h->rtnl_handle);
-
-	for (i=0; i<16; i++) {
+	for (i=0; i < NUM_NLIF_ENTRIES; i++) {
 		list_for_each_entry_safe(this, tmp, &h->ifindex_hash[i], head) {
 			list_del(&this->head);
 			free(this);
@@ -304,56 +196,134 @@ void nlif_close(struct nlif_handle *h)
  * \param h pointer to nlif_handle created by nlif_open()
  * \return 0 if OK
  */
+EXPORT_SYMBOL
 int nlif_catch(struct nlif_handle *h)
 {
-	assert(h != NULL);
-
-	if (h->rtnl_handle)
-		return rtnl_receive(h->rtnl_handle);
-
-	return -1;
-}
-
-static int nlif_catch_multi(struct nlif_handle *h)
-{
-	assert(h != NULL);
-
-	if (h->rtnl_handle)
-		return rtnl_receive_multi(h->rtnl_handle);
+	/*
+	 * Use MNL_SOCKET_BUFFER_SIZE instead of MNL_SOCKET_DUMP_SIZE
+	 * to keep memory footprint same as it was.
+	 */
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	int ret;
+
+	if (!h->nl)                /* The old library had this test */
+		return -1;
 
-	return -1;
+	ret = mnl_socket_recvfrom(h->nl, buf, sizeof buf);
+	if (ret == -1)
+		return -1;
+	return mnl_cb_run(buf, ret, 0, h->portid, data_cb, h) == -1 ? -1 : 0;
 }
 
 /**
  * nlif_query - request a dump of interfaces available in the system
  * \param h: pointer to a valid nlif_handler
+ * \return -1 on err with errno set, else >=0
  */
+EXPORT_SYMBOL
 int nlif_query(struct nlif_handle *h)
 {
-	assert(h != NULL);
-
-	if (rtnl_dump_type(h->rtnl_handle, RTM_GETLINK) < 0)
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+	uint32_t seq;
+	int ret;
+	struct rtgenmsg *rt;
+
+	nlh = mnl_nlmsg_put_header(buf);
+	nlh->nlmsg_type = RTM_GETLINK;
+	nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+	nlh->nlmsg_seq = seq = time(NULL);
+	rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
+	rt->rtgen_family = AF_PACKET;
+	if (mnl_socket_sendto(h->nl, nlh, nlh->nlmsg_len) < 0)
 		return -1;
-
-	return nlif_catch_multi(h);
+	ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf));
+	while (ret > 0) {
+		ret = mnl_cb_run(buf, ret, seq, h->portid, data_cb, h);
+		if (ret <= MNL_CB_STOP)
+			break;
+		ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf));
+	}
+	return ret;
 }
 
 /**
  * nlif_fd - get file descriptor for the netlink socket
  *
  * \param h pointer to nlif_handle created by nlif_open()
- * \return The fd or -1 if there's an error
+ * \return socket fd or -1 on error
  */
+EXPORT_SYMBOL
 int nlif_fd(struct nlif_handle *h)
 {
-	assert(h != NULL);
-
-	if (h->rtnl_handle)
-		return h->rtnl_handle->rtnl_fd;
-
-	return -1;
+	return h->nl? mnl_socket_get_fd(h->nl) : -1;
 }
 
 /**
  * @}
  */
+
+/*
+ * data_cb - callback for rtnetlink messages
+ *           caller will put nlif_handle in data
+ */
+
+static int data_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct ifinfomsg *ifi_msg = mnl_nlmsg_get_payload(nlh);
+	struct nlif_handle *h = data;
+	struct nlattr *attr;
+	uint32_t hash;
+	struct ifindex_node *this, *tmp;
+
+	if (nlh->nlmsg_type != RTM_NEWLINK && nlh->nlmsg_type != RTM_DELLINK) {
+		errno = EPROTO;
+		return MNL_CB_ERROR;
+	}
+	hash = ifi_msg->ifi_index & NLIF_ENTRY_MASK;
+
+	/* RTM_DELLINK is simple, do it first for less indenting */
+	if (nlh->nlmsg_type == RTM_DELLINK) {
+		/*
+		 * The original code used list_for_each_entry_safe when deleting
+		 * and list_for_each_entry when adding.
+		 * The code is only ever going to delete one entry
+		 * so what does the safe variant achieve?
+		 * In a multi-threaded app,
+		 * I'd suggest a pthread rwlock on all nlif accesses.
+		 */
+		list_for_each_entry_safe(this, tmp, &h->ifindex_hash[hash],
+					 head) {
+			if (this->index == ifi_msg->ifi_index) {
+				list_del(&this->head);
+				free(this);
+			}
+		}
+	return MNL_CB_OK;
+	}
+
+	list_for_each_entry(this, &h->ifindex_hash[hash], head) {
+		if (this->index == ifi_msg->ifi_index)
+			goto found;
+	}
+	this = calloc(1, sizeof(*this));
+	if (!this)
+		return MNL_CB_ERROR;
+	this->index = ifi_msg->ifi_index;
+	this->type = ifi_msg->ifi_type;
+	this->flags = ifi_msg->ifi_flags;
+	list_add(&this->head, &h->ifindex_hash[hash]);
+found:
+	mnl_attr_for_each(attr, nlh, sizeof(*ifi_msg)) {
+		/* All we want is the interface name */
+		if (mnl_attr_get_type(attr) == IFLA_IFNAME) {
+			if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
+				perror("mnl_attr_validate");
+				return MNL_CB_ERROR;
+			}
+			strcpy(this->name, mnl_attr_get_str(attr));
+			break;
+		}
+	}
+	return MNL_CB_OK;
+}
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 20/32] src: Delete rtnl.c
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (20 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 19/32] src: Convert all nlif_* functions to use libmnl Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 21/32] build: Remove libnfnetlink from the build Duncan Roe
                       ` (11 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

rtnl.c was copied from libnfnetlink for reference: it has now served its
purpose.
Without rtnl.c, doxygen again runs warning-free.

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 src/rtnl.c | 283 -----------------------------------------------------
 1 file changed, 283 deletions(-)
 delete mode 100644 src/rtnl.c

diff --git a/src/rtnl.c b/src/rtnl.c
deleted file mode 100644
index dff3bef..0000000
--- a/src/rtnl.c
+++ /dev/null
@@ -1,283 +0,0 @@
-/* rtnl - rtnetlink utility functions
- *
- * (C) 2004 by Astaro AG, written by Harald Welte <hwelte@astaro.com>
- *
- * Adapted to nfnetlink by Eric Leblond <eric@inl.fr>
- *
- * This software is free software and licensed under GNU GPLv2+.
- *
- */
-
-/* rtnetlink - routing table netlink interface */
-
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <time.h>
-#include <sys/types.h>
-#include <sys/uio.h>
-
-#include <netinet/in.h>
-
-#include <linux/types.h>
-#include <sys/socket.h>
-#include <linux/netlink.h>
-#include <linux/rtnetlink.h>
-
-#include "rtnl.h"
-
-#define rtnl_log(x, ...)
-
-static inline struct rtnl_handler *
-find_handler(struct rtnl_handle *rtnl_handle, uint16_t type)
-{
-	struct rtnl_handler *h;
-	for (h = rtnl_handle->handlers; h; h = h->next) {
-		if (h->nlmsg_type == type)
-			return h;
-	}
-	return NULL;
-}
-
-static int call_handler(struct rtnl_handle *rtnl_handle,
-			uint16_t type,
-			struct nlmsghdr *hdr)
-{
-	struct rtnl_handler *h = find_handler(rtnl_handle, type);
-
-	if (!h) {
-		rtnl_log(LOG_DEBUG, "no registered handler for type %u", type);
-		return 0;
-	}
-
-	return (h->handlefn)(hdr, h->arg);
-}
-
-/**
- * \defgroup rtnetlink Functions in rtnl.c [DEPRECATED]
- * This documentation is provided for the benefit of maintainers of legacy code.
- *
- * New applications should use
- * [libmnl](https://netfilter.org/projects/libmnl/doxygen/html/).
- * @{
- */
-
-/**
- * rtnl_handler_register - register handler for given nlmsg type
- * \param: rtnl_handle: Handler ftom rtnl_open()
- * \param: hdlr: callback handler structure
- */
-int rtnl_handler_register(struct rtnl_handle *rtnl_handle,
-			  struct rtnl_handler *hdlr)
-{
-	rtnl_log(LOG_DEBUG, "registering handler for type %u",
-		 hdlr->nlmsg_type);
-	hdlr->next = rtnl_handle->handlers;
-	rtnl_handle->handlers = hdlr;
-	return 1;
-}
-
-/**
- * rtnl_handler_unregister - unregister handler for given nlmst type
- * \param: hdlr: callback handler structure
- * \param: hdlr:	handler structure
- */
-int rtnl_handler_unregister(struct rtnl_handle *rtnl_handle,
-			    struct rtnl_handler *hdlr)
-{
-	struct rtnl_handler *h, *prev = NULL;
-
-	rtnl_log(LOG_DEBUG, "unregistering handler for type %u",
-		 hdlr->nlmsg_type);
-
-	for (h = rtnl_handle->handlers; h; h = h->next) {
-		if (h == hdlr) {
-			if (prev)
-				prev->next = h->next;
-			else
-				rtnl_handle->handlers = h->next;
-			return 1;
-		}
-		prev = h;
-	}
-	return 0;
-}
-
-int rtnl_parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
-{
-	memset(tb, 0, sizeof(struct rtattr *) * max);
-
-	while (RTA_OK(rta, len)) {
-		if (rta->rta_type <= max)
-			tb[rta->rta_type] = rta;
-		rta = RTA_NEXT(rta,len);
-	}
-	if (len)
-		return -1;
-	return 0;
-}
-
-/* rtnl_dump_type - ask rtnetlink to dump a specific table
- * \param: type:	type of table to be dumped
- */
-int rtnl_dump_type(struct rtnl_handle *rtnl_handle, unsigned int type)
-{
-	struct {
-		struct nlmsghdr nlh;
-		struct rtgenmsg g;
-	} req;
-	struct sockaddr_nl nladdr;
-
-	memset(&nladdr, 0, sizeof(nladdr));
-	memset(&req, 0, sizeof(req));
-	nladdr.nl_family = AF_NETLINK;
-
-	req.nlh.nlmsg_len = sizeof(req);
-	req.nlh.nlmsg_type = type;
-	req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
-	req.nlh.nlmsg_pid = 0;
-	req.nlh.nlmsg_seq = rtnl_handle->rtnl_dump = ++(rtnl_handle->rtnl_seq);
-	req.g.rtgen_family = AF_INET;
-
-	return sendto(rtnl_handle->rtnl_fd, &req, sizeof(req), 0,
-		      (struct sockaddr*)&nladdr, sizeof(nladdr));
-}
-
-/* rtnl_receive - receive netlink packets from rtnetlink socket */
-int rtnl_receive(struct rtnl_handle *rtnl_handle)
-{
-	int status;
-	char buf[8192];
-	struct sockaddr_nl nladdr;
-	struct iovec iov = { buf, sizeof(buf) };
-	struct nlmsghdr *h;
-
-	struct msghdr msg = {
-		.msg_name    = &nladdr,
-		.msg_namelen = sizeof(nladdr),
-		.msg_iov     = &iov,
-		.msg_iovlen  = 1,
-	};
-
-	status = recvmsg(rtnl_handle->rtnl_fd, &msg, 0);
-	if (status < 0) {
-		if (errno == EINTR)
-			return 0;
-		rtnl_log(LOG_NOTICE, "OVERRUN on rtnl socket");
-		return -1;
-	}
-	if (status == 0) {
-		rtnl_log(LOG_ERROR, "EOF on rtnl socket");
-		return -1;
-	}
-	if (msg.msg_namelen != sizeof(nladdr)) {
-		rtnl_log(LOG_ERROR, "invalid address size");
-		return -1;
-	}
-
-	h = (struct nlmsghdr *) buf;
-	while (NLMSG_OK(h, status)) {
-#if 0
-		if (h->nlmsg_pid != rtnl_local.nl_pid ||
-		    h->nlmsg_seq != rtnl_dump) {
-			goto skip;
-		}
-#endif
-
-		if (h->nlmsg_type == NLMSG_DONE) {
-			rtnl_log(LOG_NOTICE, "NLMSG_DONE");
-			return 0;
-		}
-		if (h->nlmsg_type == NLMSG_ERROR) {
-			struct nlmsgerr *err = NLMSG_DATA(h);
-			if (h->nlmsg_len>=NLMSG_LENGTH(sizeof(struct nlmsgerr)))
-				errno = -err->error;
-			rtnl_log(LOG_ERROR, "NLMSG_ERROR, errnp=%d",
-				 errno);
-			return -1;
-		}
-
-		if (call_handler(rtnl_handle, h->nlmsg_type, h) == 0)
-			rtnl_log(LOG_NOTICE, "unhandled nlmsg_type %u",
-				 h->nlmsg_type);
-		h = NLMSG_NEXT(h, status);
-	}
-	return 1;
-}
-
-int rtnl_receive_multi(struct rtnl_handle *rtnl_handle)
-{
-	while (1) {
-		if (rtnl_receive(rtnl_handle) <= 0)
-			break;
-	}
-	return 1;
-}
-
-/* rtnl_open - constructor of rtnetlink module */
-struct rtnl_handle *rtnl_open(void)
-{
-	socklen_t addrlen;
-	struct rtnl_handle *h;
-
-	h = calloc(1, sizeof(struct rtnl_handle));
-	if (!h)
-		return NULL;
-
-	addrlen = sizeof(h->rtnl_local);
-
-	h->rtnl_local.nl_pid = getpid();
-	h->rtnl_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
-	if (h->rtnl_fd < 0) {
-		rtnl_log(LOG_ERROR, "unable to create rtnetlink socket");
-		goto err;
-	}
-
-	memset(&h->rtnl_local, 0, sizeof(h->rtnl_local));
-	h->rtnl_local.nl_family = AF_NETLINK;
-	h->rtnl_local.nl_groups = RTMGRP_LINK;
-	if (bind(h->rtnl_fd, (struct sockaddr *) &h->rtnl_local, addrlen) < 0) {
-		rtnl_log(LOG_ERROR, "unable to bind rtnetlink socket");
-		goto err_close;
-	}
-
-	if (getsockname(h->rtnl_fd,
-			(struct sockaddr *) &h->rtnl_local,
-			&addrlen) < 0) {
-		rtnl_log(LOG_ERROR, "cannot gescockname(rtnl_socket)");
-		goto err_close;
-	}
-
-	if (addrlen != sizeof(h->rtnl_local)) {
-		rtnl_log(LOG_ERROR, "invalid address size %u", addr_len);
-		goto err_close;
-	}
-
-	if (h->rtnl_local.nl_family != AF_NETLINK) {
-		rtnl_log(LOG_ERROR, "invalid AF %u", h->rtnl_local.nl_family);
-		goto err_close;
-	}
-
-	h->rtnl_seq = time(NULL);
-
-	return h;
-
-err_close:
-	close(h->rtnl_fd);
-err:
-	free(h);
-	return NULL;
-}
-
-/* rtnl_close - destructor of rtnetlink module */
-void rtnl_close(struct rtnl_handle *rtnl_handle)
-{
-	close(rtnl_handle->rtnl_fd);
-	free(rtnl_handle);
-	return;
-}
-
-/**
- * @}
- */
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 21/32] build: Remove libnfnetlink from the build
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (21 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 20/32] src: Delete rtnl.c Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 22/32] include: Remove the last remaining use of a libnfnetlink header Duncan Roe
                       ` (10 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

libnfnetlink was a "private library" - always loaded whether user apps
used it or not. Remove it now it is no longer needed.

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 Make_global.am           | 2 +-
 configure.ac             | 1 -
 libnetfilter_queue.pc.in | 2 --
 src/Makefile.am          | 2 +-
 4 files changed, 2 insertions(+), 5 deletions(-)

diff --git a/Make_global.am b/Make_global.am
index 91da5da..4d8a58e 100644
--- a/Make_global.am
+++ b/Make_global.am
@@ -1,2 +1,2 @@
-AM_CPPFLAGS = -I${top_srcdir}/include ${LIBNFNETLINK_CFLAGS} ${LIBMNL_CFLAGS}
+AM_CPPFLAGS = -I${top_srcdir}/include ${LIBMNL_CFLAGS}
 AM_CFLAGS = -Wall ${GCC_FVISIBILITY_HIDDEN}
diff --git a/configure.ac b/configure.ac
index 7359fba..ba7b15f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -42,7 +42,6 @@ case "$host" in
 esac
 
 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],
diff --git a/libnetfilter_queue.pc.in b/libnetfilter_queue.pc.in
index 9c6c2c4..1927a8a 100644
--- a/libnetfilter_queue.pc.in
+++ b/libnetfilter_queue.pc.in
@@ -9,8 +9,6 @@ Name: libnetfilter_queue
 Description: netfilter userspace packet queueing library
 URL: http://netfilter.org/projects/libnetfilter_queue/
 Version: @VERSION@
-Requires: libnfnetlink
 Conflicts:
 Libs: -L${libdir} -lnetfilter_queue
-Libs.private: @LIBNFNETLINK_LIBS@
 Cflags: -I${includedir}
diff --git a/src/Makefile.am b/src/Makefile.am
index a6813e8..e5e1d66 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -39,4 +39,4 @@ libnetfilter_queue_la_SOURCES = libnetfilter_queue.c	\
 				extra/pktbuff.c		\
 				extra/udp.c
 
-libnetfilter_queue_la_LIBADD  = ${LIBNFNETLINK_LIBS} ${LIBMNL_LIBS}
+libnetfilter_queue_la_LIBADD  = ${LIBMNL_LIBS}
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 22/32] include: Remove the last remaining use of a libnfnetlink header
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (22 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 21/32] build: Remove libnfnetlink from the build Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 23/32] doc: Get doxygen to document useful static inline functions Duncan Roe
                       ` (9 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

Update (deprecated) include/libnetfilter_queue/linux_nfnetlink_queue.h
to use /usr/include/linux/nfnetlink.h
instead of libnfnetlink/linux_nfnetlink.h
As an aside, everything compiles fine with this line removed
(i.e library, utils and examples all compile),
so maybe we can do without it.

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 include/libnetfilter_queue/linux_nfnetlink_queue.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/include/libnetfilter_queue/linux_nfnetlink_queue.h b/include/libnetfilter_queue/linux_nfnetlink_queue.h
index 6844270..82d8ece 100644
--- a/include/libnetfilter_queue/linux_nfnetlink_queue.h
+++ b/include/libnetfilter_queue/linux_nfnetlink_queue.h
@@ -8,7 +8,8 @@
 #endif
 
 #include <linux/types.h>
-#include <libnfnetlink/linux_nfnetlink.h>
+/* Use the real header since libnfnetlink is going away. */
+#include <linux/nfnetlink.h>
 
 enum nfqnl_msg_types {
 	NFQNL_MSG_PACKET,		/* packet from kernel to userspace */
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 23/32] doc: Get doxygen to document useful static inline functions
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (23 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 22/32] include: Remove the last remaining use of a libnfnetlink header Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 24/32] doc: SYNOPSIS of linux_list.h nominates libnetfilter_queue/libnetfilter_queue.h Duncan Roe
                       ` (8 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

include/libnetfilter_queue/linux_list.h contains static inline list_add and
list_del which mnl programs may wish to use. Make a temporary copy of
linux_list.h with 'static' removed and get doxygen to process that.

Also add some detailed description and a SYNOPSIS line to linux_list.h.

Some problems remain with the generated man page.

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 doxygen/Makefile.am                     |  3 +++
 doxygen/doxygen.cfg.in                  |  3 +--
 include/libnetfilter_queue/linux_list.h | 14 ++++++++++++++
 3 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/doxygen/Makefile.am b/doxygen/Makefile.am
index aae1ccc..1784afa 100644
--- a/doxygen/Makefile.am
+++ b/doxygen/Makefile.am
@@ -14,7 +14,10 @@ doc_srcs = $(top_srcdir)/src/libnetfilter_queue.c\
 
 doxyfile.stamp: $(doc_srcs) Makefile build_man.sh
 	rm -rf html man
+	sed '/^static inline void [^_]/s/static //' \
+	  $(top_srcdir)/include/libnetfilter_queue/linux_list.h > linux_list.h
 	doxygen doxygen.cfg >/dev/null
+	rm linux_list.h
 
 if BUILD_MAN
 	$(abs_top_srcdir)/doxygen/build_man.sh libnetfilter_queue libnetfilter_queue.c
diff --git a/doxygen/doxygen.cfg.in b/doxygen/doxygen.cfg.in
index c795df1..601d4ab 100644
--- a/doxygen/doxygen.cfg.in
+++ b/doxygen/doxygen.cfg.in
@@ -5,8 +5,7 @@ ABBREVIATE_BRIEF       =
 FULL_PATH_NAMES        = NO
 TAB_SIZE               = 8
 OPTIMIZE_OUTPUT_FOR_C  = YES
-INPUT                  = @abs_top_srcdir@/src \
-                         @abs_top_srcdir@/include/libnetfilter_queue
+INPUT                  = @abs_top_srcdir@/src .
 FILE_PATTERNS          = *.c linux_list.h
 RECURSIVE              = YES
 EXCLUDE_SYMBOLS        = EXPORT_SYMBOL \
diff --git a/include/libnetfilter_queue/linux_list.h b/include/libnetfilter_queue/linux_list.h
index 500481d..6e67b9a 100644
--- a/include/libnetfilter_queue/linux_list.h
+++ b/include/libnetfilter_queue/linux_list.h
@@ -24,6 +24,20 @@
 
 /**
  * \defgroup List Circular doubly linked list implementation
+ *
+ * Unlike file units (which are re-used), network interface indicies
+ * increase monotonically as they are brought up and down.
+ *
+ * To keep memory usage predictable as indices increase,
+ * the nlif_* functions keep their data in a circular list
+ * (in fact a number of lists, to minimise search times).
+ *
+ * \manonly
+.SH SYNOPSIS
+.nf
+\fB
+#include <libnetfilter_queue/linux_list.h>
+\endmanonly
  * @{
  */
 
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 24/32] doc: SYNOPSIS of linux_list.h nominates libnetfilter_queue/libnetfilter_queue.h
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (24 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 23/32] doc: Get doxygen to document useful static inline functions Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 25/32] doc: Move nlif usage description from libnetfilter_queue.c to iftable.c Duncan Roe
                       ` (7 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

This patch enables user apps to compile w/out needing an extra #include

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 include/libnetfilter_queue/linux_list.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/libnetfilter_queue/linux_list.h b/include/libnetfilter_queue/linux_list.h
index 6e67b9a..76d24ea 100644
--- a/include/libnetfilter_queue/linux_list.h
+++ b/include/libnetfilter_queue/linux_list.h
@@ -36,7 +36,7 @@
 .SH SYNOPSIS
 .nf
 \fB
-#include <libnetfilter_queue/linux_list.h>
+#include <libnetfilter_queue/libnetfilter_queue.h>
 \endmanonly
  * @{
  */
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 25/32] doc: Move nlif usage description from libnetfilter_queue.c to iftable.c
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (25 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 24/32] doc: SYNOPSIS of linux_list.h nominates libnetfilter_queue/libnetfilter_queue.h Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 26/32] build: Shave some time off build Duncan Roe
                       ` (6 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

Also in iftable.c:
 - Expand usage description to cover nlif_catch.
 - Add SYNOPSIS.
 - Fix some doc typos.

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 src/iftable.c            | 57 ++++++++++++++++++++++++++++++++++++----
 src/libnetfilter_queue.c | 38 +++------------------------
 2 files changed, 56 insertions(+), 39 deletions(-)

diff --git a/src/iftable.c b/src/iftable.c
index 76a6cad..1a53893 100644
--- a/src/iftable.c
+++ b/src/iftable.c
@@ -42,6 +42,55 @@ static int data_cb(const struct nlmsghdr *nlh, void *data);
  * [libmnl](https://netfilter.org/projects/libmnl/doxygen/html/)
  * calls directly to maintain an
  * interface table with more (or less!) data points, e.g. MTU.
+ *
+ * Programs access an nlif database through an opaque __struct nlif_handle__
+ * interface resolving handle. Call nlif_open() to get a handle:
+ * \verbatim
+	h = nlif_open();
+	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);
+\endverbatim
+ * libnetfilter_queue 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 and netilter_queue
+ * file descriptors.
+ * E.g. use nlif_fd() to get the nlif file descriptor, then give this fd to
+ * **poll()** as in this code snippet (error-checking removed):
+ * \verbatim
+	if_fd = nlif_fd(h);
+	qfd = mnl_socket_get_fd(nl); // For mnl API or ...
+	qfd = nfq_fd(qh);            // For nfnl API
+	. . .
+	fds[0].fd = ifd;
+	fds[0].events = POLLIN;
+	fds[1].fd = qfd;
+	fds[1].events = POLLIN;
+	for(;;)
+	{
+		poll((struct pollfd *)&fds, 2, -1);
+		if (fds[0].revents & POLLIN)
+			nlif_catch(h);
+\endverbatim
+ * Don't forget to close the handler when you don't need the feature anymore:
+ * \verbatim
+	nlif_close(h);
+\endverbatim
+ *
+ * \manonly
+.SH SYNOPSIS
+.nf
+\fB
+#include <libnetfilter_queue/libnetfilter_queue.h>
+\endmanonly
  * @{
  */
 
@@ -128,8 +177,8 @@ int nlif_get_ifflags(const struct nlif_handle *h,
 /**
  * nlif_open - initialize interface table
  *
- * Open a netlink socket and initialize interface table
- * Call this before any nlif_* function
+ * Open a netlink socket and initialise interface table.
+ * Call this before any other nlif_* function
  *
  * \return NULL on error, else valid pointer to an nlif_handle structure
  */
@@ -191,8 +240,6 @@ void nlif_close(struct nlif_handle *h)
 /**
  * nlif_catch - receive message from netlink and update interface table
  *
- * FIXME - elaborate a bit
- *
  * \param h pointer to nlif_handle created by nlif_open()
  * \return 0 if OK
  */
@@ -218,7 +265,7 @@ int nlif_catch(struct nlif_handle *h)
 /**
  * nlif_query - request a dump of interfaces available in the system
  * \param h: pointer to a valid nlif_handler
- * \return -1 on err with errno set, else >=0
+ * \return -1 on error with errno set, else >=0
  */
 EXPORT_SYMBOL
 int nlif_query(struct nlif_handle *h)
diff --git a/src/libnetfilter_queue.c b/src/libnetfilter_queue.c
index 3c3f951..1be2333 100644
--- a/src/libnetfilter_queue.c
+++ b/src/libnetfilter_queue.c
@@ -1284,34 +1284,7 @@ uint32_t nfq_get_physoutdev(struct nfq_data *nfad)
  * \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.
- *
- * 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
- * during all your program life:
- * \verbatim
-	h = nlif_open();
-	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);
-\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
- * descriptor, you need to use nlif_fd:
- * \verbatim
-	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);
-\endverbatim
+ * \sa __nlif_open__(3)
  *
  */
 EXPORT_SYMBOL
@@ -1330,9 +1303,8 @@ int nfq_get_indev_name(struct nlif_handle *nlif_handle,
  * \param name pointer to the buffer to receive the interface name;
  *  not more than \c IFNAMSIZ bytes will be copied to it.
  *
- * See nfq_get_indev_name() documentation for nlif_handle usage.
- *
  * \return  -1 in case of error, > 0 if it succeed.
+ * \sa __nlif_open__(3)
  */
 EXPORT_SYMBOL
 int nfq_get_physindev_name(struct nlif_handle *nlif_handle,
@@ -1350,9 +1322,8 @@ int nfq_get_physindev_name(struct nlif_handle *nlif_handle,
  * \param name pointer to the buffer to receive the interface name;
  *  not more than \c IFNAMSIZ bytes will be copied to it.
  *
- * See nfq_get_indev_name() documentation for nlif_handle usage.
- *
  * \return  -1 in case of error, > 0 if it succeed.
+ * \sa __nlif_open__(3)
  */
 EXPORT_SYMBOL
 int nfq_get_outdev_name(struct nlif_handle *nlif_handle,
@@ -1370,9 +1341,8 @@ int nfq_get_outdev_name(struct nlif_handle *nlif_handle,
  * \param name pointer to the buffer to receive the interface name;
  *  not more than \c IFNAMSIZ bytes will be copied to it.
  *
- * See nfq_get_indev_name() documentation for nlif_handle usage.
- *
  * \return  -1 in case of error, > 0 if it succeed.
+ * \sa __nlif_open__(3)
  */
 
 EXPORT_SYMBOL
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 26/32] build: Shave some time off build
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (26 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 25/32] doc: Move nlif usage description from libnetfilter_queue.c to iftable.c Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 27/32] doc: Resolve most issues with man page generated from linux_list.h Duncan Roe
                       ` (5 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

Modify function mygrep in build_man.sh to use pipes rather than the
temporary files. Saves ~20% elapsed time in a make with no compiles on my
system although real & user times increase by about 10%.

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 doxygen/build_man.sh | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/doxygen/build_man.sh b/doxygen/build_man.sh
index 643ad42..0590009 100755
--- a/doxygen/build_man.sh
+++ b/doxygen/build_man.sh
@@ -304,15 +304,12 @@ delete_lines(){
 }
 
 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
+  linnum=$(grep -En "$1" $2 2>/dev/null | head -n1 | tee $fileG | cut -f1 -d:)
+  [ $linnum ] || linnum=0
 }
 
 make_temp_files(){
-  temps="A B C G H"
+  temps="A B C G"
   for i in $temps
   do declare -g file$i=$(mktemp)
   done
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 27/32] doc: Resolve most issues with man page generated from linux_list.h
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (27 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 26/32] build: Shave some time off build Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 28/32] build: Get real & user times back to what they were Duncan Roe
                       ` (4 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

build_man.sh has extra logic to extract documented macros into the "Name"
line.
doxygen.cfg.in excludes the list_head structure.

doxygen 1.10.0 has a bug which appends ".PP" to macro "Value:" headings.
This is fixed in the latest snapshot and should be in the next release.

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 doxygen/build_man.sh   | 10 ++++++++++
 doxygen/doxygen.cfg.in |  1 +
 2 files changed, 11 insertions(+)

diff --git a/doxygen/build_man.sh b/doxygen/build_man.sh
index 0590009..b3d1989 100755
--- a/doxygen/build_man.sh
+++ b/doxygen/build_man.sh
@@ -261,6 +261,16 @@ fix_name_line(){
     [ -z "$all_funcs" ] && all_funcs=$func ||\
       all_funcs="$all_funcs, $func"
   done
+
+  # macros (if any) come after functions
+  while :
+  do mygrep '^\.SS "#' $fileC
+    [ $linnum -ne 0 ] || break
+    tail -n+$(($linnum + 1)) $fileC >$fileB
+    cp $fileB $fileC
+    func=$(cat $fileG | cut -f3 -d' ' | cut -f1 -d\()
+    [ -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" ")
diff --git a/doxygen/doxygen.cfg.in b/doxygen/doxygen.cfg.in
index 601d4ab..ad83581 100644
--- a/doxygen/doxygen.cfg.in
+++ b/doxygen/doxygen.cfg.in
@@ -17,6 +17,7 @@ EXCLUDE_SYMBOLS        = EXPORT_SYMBOL \
                          nfnl_subsys_handle \
                          mnl_socket \
                          ifindex_node \
+                         list_head \
                          nlif_handle \
                          nfnl_callback2 \
                          tcp_flag_word
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 28/32] build: Get real & user times back to what they were
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (28 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 27/32] doc: Resolve most issues with man page generated from linux_list.h Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 29/32] doc: Cater for doxygen variants w.r.t. #define stmts Duncan Roe
                       ` (3 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

In build_man.sh, mygrep becomes mygrep2 and
all functions except fix_name_line use a new simpler mygrep.

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 doxygen/build_man.sh | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/doxygen/build_man.sh b/doxygen/build_man.sh
index b3d1989..5c1a019 100755
--- a/doxygen/build_man.sh
+++ b/doxygen/build_man.sh
@@ -251,7 +251,7 @@ fix_name_line(){
   head -n$linnum $target >$fileC
 
   while :
-  do mygrep ^\\.RI $fileC
+  do mygrep2 ^\\.RI $fileC
     [ $linnum -ne 0 ] || break
     # Discard this entry
     tail -n+$(($linnum + 1)) $fileC >$fileB
@@ -264,7 +264,7 @@ fix_name_line(){
 
   # macros (if any) come after functions
   while :
-  do mygrep '^\.SS "#' $fileC
+  do mygrep2 '^\.SS "#' $fileC
     [ $linnum -ne 0 ] || break
     tail -n+$(($linnum + 1)) $fileC >$fileB
     cp $fileB $fileC
@@ -314,6 +314,13 @@ delete_lines(){
 }
 
 mygrep(){
+  linnum=$(grep -En "$1" $2 2>/dev/null | head -n1 | cut -f1 -d:)
+  [ $linnum ] || linnum=0
+}
+
+# mygrep2 copies found line to $fileG. Only fix_name_line() needs this.
+# Using mygrep everywhere else gives a measurable CPU saving.
+mygrep2(){
   linnum=$(grep -En "$1" $2 2>/dev/null | head -n1 | tee $fileG | cut -f1 -d:)
   [ $linnum ] || linnum=0
 }
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 29/32] doc: Cater for doxygen variants w.r.t. #define stmts
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (29 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 28/32] build: Get real & user times back to what they were Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 30/32] doc: Fix list_empty() doxygen comments Duncan Roe
                       ` (2 subsequent siblings)
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

More changes to doxygen/build_man.sh.

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 doxygen/build_man.sh | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/doxygen/build_man.sh b/doxygen/build_man.sh
index 5c1a019..d3bd748 100755
--- a/doxygen/build_man.sh
+++ b/doxygen/build_man.sh
@@ -7,6 +7,7 @@
 # Args: none or 2 being man7 page name & relative path of source with \mainpage
 
 declare -A renamed_page
+no_macroRI=maybe
 
 main(){
   set -e
@@ -250,10 +251,18 @@ fix_name_line(){
   mygrep "^\\.SH \"Function Documentation" $target
   head -n$linnum $target >$fileC
 
+  # Different versions of doxygen present macros and functions differently.
+  # Some versions have .RI lines for macros then functions.
+  # Some versions have .SS lines for macros instead of .RI.
+  # All versions (so far) have .SS lines for macros after all .RI lines.
+  # Look for #define in .RI lines and look for .SS lines if none found
+  # to cater for either scenario.
+
   while :
   do mygrep2 ^\\.RI $fileC
     [ $linnum -ne 0 ] || break
     # Discard this entry
+    ! grep -E -q '#define' $fileG || no_macroRI=
     tail -n+$(($linnum + 1)) $fileC >$fileB
     cp $fileB $fileC
 
@@ -262,9 +271,9 @@ fix_name_line(){
       all_funcs="$all_funcs, $func"
   done
 
-  # macros (if any) come after functions
+  [ -z "$no_macroRI" ] ||
   while :
-  do mygrep2 '^\.SS "#' $fileC
+  do mygrep2 '^\.SS "#define' $fileC
     [ $linnum -ne 0 ] || break
     tail -n+$(($linnum + 1)) $fileC >$fileB
     cp $fileB $fileC
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 30/32] doc: Fix list_empty() doxygen comments
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (30 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 29/32] doc: Cater for doxygen variants w.r.t. #define stmts Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 31/32] src: Use a cast in place of convoluted construct Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 32/32] whitespace: Fix more checkpatch errors & warnings Duncan Roe
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

Need blank line between list_empty and list_entry.
Translate kerneldoc comments to doxygen.
Not all static inline functions are void any more.
Use \note instead of Note:.

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 doxygen/Makefile.am                     | 2 +-
 include/libnetfilter_queue/linux_list.h | 7 +++++--
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/doxygen/Makefile.am b/doxygen/Makefile.am
index 1784afa..4934e8e 100644
--- a/doxygen/Makefile.am
+++ b/doxygen/Makefile.am
@@ -14,7 +14,7 @@ doc_srcs = $(top_srcdir)/src/libnetfilter_queue.c\
 
 doxyfile.stamp: $(doc_srcs) Makefile build_man.sh
 	rm -rf html man
-	sed '/^static inline void [^_]/s/static //' \
+	sed '/^static inline [^ ]* [^_]/s/static //' \
 	  $(top_srcdir)/include/libnetfilter_queue/linux_list.h > linux_list.h
 	doxygen doxygen.cfg >/dev/null
 	rm linux_list.h
diff --git a/include/libnetfilter_queue/linux_list.h b/include/libnetfilter_queue/linux_list.h
index 76d24ea..f687d20 100644
--- a/include/libnetfilter_queue/linux_list.h
+++ b/include/libnetfilter_queue/linux_list.h
@@ -131,7 +131,8 @@ static inline void __list_del(struct list_head * prev, struct list_head * next)
 /**
  * list_del - deletes entry from list.
  * \param entry: the element to delete from the list.
- * Note: list_empty on entry does not return true after this, the entry is
+ * \note
+ * list_empty() on **entry** does not return true after this, **entry** is
  * in an undefined state.
  */
 static inline void list_del(struct list_head *entry)
@@ -143,12 +144,14 @@ static inline void list_del(struct list_head *entry)
 
 /**
  * list_empty - tests whether a list is empty
- * @head: the list to test.
+ * \param head: the list to test.
+ * \return 1 if list is empty, 0 otherwise
  */
 static inline int list_empty(const struct list_head *head)
 {
 	return head->next == head;
 }
+
 /**
  * list_entry - get the struct for this entry
  * \param ptr:	the &struct list_head pointer.
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 31/32] src: Use a cast in place of convoluted construct
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (31 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 30/32] doc: Fix list_empty() doxygen comments Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 32/32] whitespace: Fix more checkpatch errors & warnings Duncan Roe
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

I.e. when calling list_del() and list_add().
We have a list of struct ifindex_node but the fns want struct list_head
which is at the head of struct ifindex_node.
Also audit counter loops to count downwards (c/w 0 is faster).

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 src/iftable.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/iftable.c b/src/iftable.c
index 1a53893..7eada24 100644
--- a/src/iftable.c
+++ b/src/iftable.c
@@ -192,7 +192,7 @@ struct nlif_handle *nlif_open(void)
 	if (h == NULL)
 		goto err;
 
-	for (i=0; i < NUM_NLIF_ENTRIES; i++)
+	for (i = NUM_NLIF_ENTRIES - 1; i>= 0; i--)
 		INIT_LIST_HEAD(&h->ifindex_hash[i]);
 
 	h->nl = mnl_socket_open(NETLINK_ROUTE);
@@ -226,9 +226,9 @@ void nlif_close(struct nlif_handle *h)
 
 	mnl_socket_close(h->nl);
 
-	for (i=0; i < NUM_NLIF_ENTRIES; i++) {
+	for (i = NUM_NLIF_ENTRIES - 1; i>= 0; i--) {
 		list_for_each_entry_safe(this, tmp, &h->ifindex_hash[i], head) {
-			list_del(&this->head);
+			list_del((struct list_head *)this);
 			free(this);
 		}
 	}
@@ -359,7 +359,7 @@ static int data_cb(const struct nlmsghdr *nlh, void *data)
 	this->index = ifi_msg->ifi_index;
 	this->type = ifi_msg->ifi_type;
 	this->flags = ifi_msg->ifi_flags;
-	list_add(&this->head, &h->ifindex_hash[hash]);
+	list_add((struct list_head *)this, &h->ifindex_hash[hash]);
 found:
 	mnl_attr_for_each(attr, nlh, sizeof(*ifi_msg)) {
 		/* All we want is the interface name */
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH libnetfilter_queue 32/32] whitespace: Fix more checkpatch errors & warnings
  2024-02-14 10:47   ` Pablo Neira Ayuso
                       ` (32 preceding siblings ...)
  2024-03-15  7:33     ` [PATCH libnetfilter_queue 31/32] src: Use a cast in place of convoluted construct Duncan Roe
@ 2024-03-15  7:33     ` Duncan Roe
  33 siblings, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-15  7:33 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel

Fix errors & warnings in the copies of linux_list.h & iftable.c.
One possible false +ve:
 typeof(((type *)0)->member) *__mptr = (ptr);
gets "need consistent spacing around '*' (ctx:WxV)"

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 include/libnetfilter_queue/linux_list.h | 10 +++++-----
 src/iftable.c                           | 10 +++++-----
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/include/libnetfilter_queue/linux_list.h b/include/libnetfilter_queue/linux_list.h
index f687d20..738d834 100644
--- a/include/libnetfilter_queue/linux_list.h
+++ b/include/libnetfilter_queue/linux_list.h
@@ -51,8 +51,8 @@
  *
  */
 #define container_of(ptr, type, member) ({			\
-	typeof( ((type *)0)->member ) *__mptr = (ptr);	\
-	(type *)( (char *)__mptr - offsetof(type,member) );})
+	typeof(((type *)0)->member) *__mptr = (ptr);	\
+	(type *)((char *)__mptr - offsetof(type, member)); })
 
 /*
  * Circular doubly linked list implementation.
@@ -122,7 +122,7 @@ static inline void list_add(struct list_head *new, struct list_head *head)
  * This is only for internal list manipulation where we know
  * the prev/next entries already!
  */
-static inline void __list_del(struct list_head * prev, struct list_head * next)
+static inline void __list_del(struct list_head *prev, struct list_head *next)
 {
 	next->prev = prev;
 	prev->next = next;
@@ -169,7 +169,7 @@ static inline int list_empty(const struct list_head *head)
  */
 #define list_for_each_entry(pos, head, member)				\
 	for (pos = list_entry((head)->next, typeof(*pos), member);	\
-	     &pos->member != (head); 					\
+	     &pos->member != (head);					\
 	     pos = list_entry(pos->member.next, typeof(*pos), member))	\
 
 /**
@@ -182,7 +182,7 @@ static inline int list_empty(const struct list_head *head)
 #define list_for_each_entry_safe(pos, n, head, member)			\
 	for (pos = list_entry((head)->next, typeof(*pos), member),	\
 		n = list_entry(pos->member.next, typeof(*pos), member);	\
-	     &pos->member != (head); 					\
+	     &pos->member != (head);					\
 	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
 
 /**
diff --git a/src/iftable.c b/src/iftable.c
index 7eada24..322b18e 100644
--- a/src/iftable.c
+++ b/src/iftable.c
@@ -29,7 +29,7 @@
 
 #define NUM_NLIF_BITS 4
 #define NUM_NLIF_ENTRIES (1 << NUM_NLIF_BITS)
-#define NLIF_ENTRY_MASK (NUM_NLIF_ENTRIES -1)
+#define NLIF_ENTRY_MASK (NUM_NLIF_ENTRIES - 1)
 
 static int data_cb(const struct nlmsghdr *nlh, void *data);
 
@@ -192,7 +192,7 @@ struct nlif_handle *nlif_open(void)
 	if (h == NULL)
 		goto err;
 
-	for (i = NUM_NLIF_ENTRIES - 1; i>= 0; i--)
+	for (i = NUM_NLIF_ENTRIES - 1; i >= 0; i--)
 		INIT_LIST_HEAD(&h->ifindex_hash[i]);
 
 	h->nl = mnl_socket_open(NETLINK_ROUTE);
@@ -226,7 +226,7 @@ void nlif_close(struct nlif_handle *h)
 
 	mnl_socket_close(h->nl);
 
-	for (i = NUM_NLIF_ENTRIES - 1; i>= 0; i--) {
+	for (i = NUM_NLIF_ENTRIES - 1; i >= 0; i--) {
 		list_for_each_entry_safe(this, tmp, &h->ifindex_hash[i], head) {
 			list_del((struct list_head *)this);
 			free(this);
@@ -256,7 +256,7 @@ int nlif_catch(struct nlif_handle *h)
 	if (!h->nl)                /* The old library had this test */
 		return -1;
 
-	ret = mnl_socket_recvfrom(h->nl, buf, sizeof buf);
+	ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf));
 	if (ret == -1)
 		return -1;
 	return mnl_cb_run(buf, ret, 0, h->portid, data_cb, h) == -1 ? -1 : 0;
@@ -303,7 +303,7 @@ int nlif_query(struct nlif_handle *h)
 EXPORT_SYMBOL
 int nlif_fd(struct nlif_handle *h)
 {
-	return h->nl? mnl_socket_get_fd(h->nl) : -1;
+	return h->nl ? mnl_socket_get_fd(h->nl) : -1;
 }
 
 /**
-- 
2.35.8


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* Re: [PATCH libnetfilter_queue 00/32] Convert libnetfilter_queue to not need libnfnetlink
  2024-02-16  1:41     ` Duncan Roe
  2024-02-19  2:27       ` Duncan Roe
@ 2024-03-19 23:58       ` Duncan Roe
  1 sibling, 0 replies; 39+ messages in thread
From: Duncan Roe @ 2024-03-19 23:58 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: Netfilter Development

Hi Pablo,

On Fri, Feb 16, 2024 at 12:41:35PM +1100, Duncan Roe wrote:
> Hi Pablo,
>
> On Wed, Feb 14, 2024 at 11:47:30AM +0100, Pablo Neira Ayuso wrote:
> > Hi Duncan,
> [...]
> > because this conversion to libmnl _cannot_ break existing userspace
> > applications, that's the challenge.
> >
> Absolutely. utils/nfqnl_test.c builds and runs, are there any other examples I
> could try?
>
> Userspace applications *will* break if they either
>
> 1. Call libnfnetlink nfnl_* functions directly (other than nfnl_rcvbufsiz())
>
>   OR
>
> 2. Call nfq_open_nfnl()
>
> Is that acceptable?
>
> Cheers ... Duncan.
>
To clarify, the new patch series
https://patchwork.ozlabs.org/project/netfilter-devel/list/?series=399143
overcomes 1 and 2 above.

Existing userspace applications should *all* keep running.

Cheers ... Duncan.

^ permalink raw reply	[flat|nested] 39+ messages in thread

end of thread, other threads:[~2024-03-19 23:58 UTC | newest]

Thread overview: 39+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-02-13 21:07 [PATCH libnetfilter_queue 0/1] Convert libnetfilter_queue to use entirely libmnl functions Duncan Roe
2024-02-13 21:07 ` [PATCH libnetfilter_queue 1/1] " Duncan Roe
2024-02-14 10:47   ` Pablo Neira Ayuso
2024-02-16  1:41     ` Duncan Roe
2024-02-19  2:27       ` Duncan Roe
2024-03-19 23:58       ` [PATCH libnetfilter_queue 00/32] Convert libnetfilter_queue to not need libnfnetlink Duncan Roe
2024-03-15  7:33     ` Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 01/32] src: Convert nfq_open() to use libmnl Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 02/32] src: Convert nfq_open_nfnl() " Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 03/32] src: Convert nfq_close() " Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 04/32] src: Convert nfq_create_queue(), nfq_bind_pf() & nfq_unbind_pf() " Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 05/32] src: Convert nfq_set_queue_flags() & nfq_set_queue_maxlen() " Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 06/32] src: Convert nfq_handle_packet(), nfq_get_secctx(), nfq_get_payload() and all the nfq_get_ functions " Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 07/32] src: Convert nfq_set_verdict() and nfq_set_verdict2() to use libmnl if there is no data Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 08/32] src: Incorporate nfnl_rcvbufsiz() in libnetfilter_queue Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 09/32] src: Convert nfq_fd() to use libmnl Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 10/32] src: Convert remaining nfq_* functions " Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 11/32] src: Fix checkpatch whitespace and block comment warnings Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 12/32] src: Copy nlif-related code from libnfnetlink Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 13/32] include: Cherry-pick macros and functions that nlif will need Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 14/32] doc: Add linux_list.h to the doxygen system Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 15/32] doc: Eliminate doxygen warnings from linux_list.h Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 16/32] doc: Eliminate doxygen warnings from iftable.c Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 17/32] whitespace: remove trailing spaces " Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 18/32] include: Use libmnl.h instead of libnfnetlink.h Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 19/32] src: Convert all nlif_* functions to use libmnl Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 20/32] src: Delete rtnl.c Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 21/32] build: Remove libnfnetlink from the build Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 22/32] include: Remove the last remaining use of a libnfnetlink header Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 23/32] doc: Get doxygen to document useful static inline functions Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 24/32] doc: SYNOPSIS of linux_list.h nominates libnetfilter_queue/libnetfilter_queue.h Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 25/32] doc: Move nlif usage description from libnetfilter_queue.c to iftable.c Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 26/32] build: Shave some time off build Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 27/32] doc: Resolve most issues with man page generated from linux_list.h Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 28/32] build: Get real & user times back to what they were Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 29/32] doc: Cater for doxygen variants w.r.t. #define stmts Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 30/32] doc: Fix list_empty() doxygen comments Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 31/32] src: Use a cast in place of convoluted construct Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 32/32] whitespace: Fix more checkpatch errors & warnings Duncan Roe

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.