add new libnetfilter_queue API for libmnl
authorPablo Neira Ayuso <pablo@netfilter.org>
Mon, 16 Apr 2012 17:12:58 +0000 (19:12 +0200)
committerr.kubiak <r.kubiak@samsung.com>
Mon, 16 Nov 2015 13:12:06 +0000 (14:12 +0100)
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
15 files changed:
Make_global.am
Makefile.am
configure.ac
examples/Makefile.am [new file with mode: 0644]
examples/nf-queue.c [new file with mode: 0644]
include/Makefile.am
include/libnetfilter_queue/Makefile.am
include/libnetfilter_queue/libnetfilter_queue.h
include/linux/Makefile.am [new file with mode: 0644]
include/linux/netfilter/Makefile.am [new file with mode: 0644]
include/linux/netfilter/nfnetlink_queue.h [new file with mode: 0644]
m4/gcc4_visibility.m4 [new file with mode: 0644]
src/Makefile.am
src/internal.h [new file with mode: 0644]
src/nlmsg.c [new file with mode: 0644]

index a4e9bd9..9bc8ea1 100644 (file)
@@ -1,2 +1,2 @@
-AM_CPPFLAGS = -I${top_srcdir}/include ${LIBNFNETLINK_CFLAGS}
+AM_CPPFLAGS = -I${top_srcdir}/include ${LIBNFNETLINK_CFLAGS} ${LIBMNL_CFLAGS}
 AM_CFLAGS = -Wall
index bc2f61c..6b4ef77 100644 (file)
@@ -1,8 +1,8 @@
 ACLOCAL_AMFLAGS = -I m4
 
-EXTRA_DIST = $(man_MANS) 
+EXTRA_DIST = $(man_MANS) include/linux
 
-SUBDIRS = include src utils
+SUBDIRS = src utils include examples
 
 man_MANS = #nfnetlink_queue.3 nfnetlink_queue.7
 
index 4f17aee..6ebba4f 100644 (file)
@@ -4,6 +4,7 @@ AC_INIT([libnetfilter_queue], [1.0.1])
 AC_CONFIG_AUX_DIR([build-aux])
 AC_CANONICAL_HOST
 AC_CONFIG_MACRO_DIR([m4])
+AC_CONFIG_HEADERS([config.h])
 
 AM_INIT_AUTOMAKE([-Wall foreign subdir-objects
        tar-pax no-dist-gzip dist-bzip2 1.6])
@@ -16,6 +17,7 @@ AM_PROG_CC_C_O
 AC_DISABLE_STATIC
 AM_PROG_LIBTOOL
 AC_PROG_INSTALL
+CHECK_GCC_FVISIBILITY
 
 case "$host" in
 *-*-linux*) ;;
@@ -24,8 +26,11 @@ esac
 
 dnl Dependencies
 PKG_CHECK_MODULES([LIBNFNETLINK], [libnfnetlink >= 0.0.41])
+PKG_CHECK_MODULES([LIBMNL], [libmnl >= 1.0.3])
 
 dnl Output the makefiles
-AC_CONFIG_FILES([Makefile include/Makefile include/libnetfilter_queue/Makefile
-       src/Makefile utils/Makefile libnetfilter_queue.pc doxygen.cfg])
+AC_CONFIG_FILES([Makefile src/Makefile utils/Makefile examples/Makefile
+        libnetfilter_queue.pc doxygen.cfg
+       include/Makefile include/libnetfilter_queue/Makefile
+       include/linux/Makefile include/linux/netfilter/Makefile])
 AC_OUTPUT
diff --git a/examples/Makefile.am b/examples/Makefile.am
new file mode 100644 (file)
index 0000000..1906697
--- /dev/null
@@ -0,0 +1,7 @@
+include ${top_srcdir}/Make_global.am
+
+check_PROGRAMS = nf-queue
+
+nf_queue_SOURCES = nf-queue.c
+nf_queue_LDADD = ../src/libnetfilter_queue.la
+nf_queue_LDFLAGS = -dynamic -lmnl
diff --git a/examples/nf-queue.c b/examples/nf-queue.c
new file mode 100644 (file)
index 0000000..8b4b63d
--- /dev/null
@@ -0,0 +1,164 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <arpa/inet.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nfnetlink.h>
+
+#include <linux/types.h>
+#include <linux/netfilter/nfnetlink_queue.h>
+
+#include <libnetfilter_queue/libnetfilter_queue.h>
+
+static struct mnl_socket *nl;
+
+static struct nlmsghdr *
+nfq_hdr_put(char *buf, int type, uint32_t queue_num)
+{
+       struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
+       nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | type;
+       nlh->nlmsg_flags = NLM_F_REQUEST;
+
+       struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
+       nfg->nfgen_family = AF_UNSPEC;
+       nfg->version = NFNETLINK_V0;
+       nfg->res_id = htons(queue_num);
+
+       return nlh;
+}
+
+static int
+nfq_send_verdict(int queue_num, uint32_t id)
+{
+       char buf[MNL_SOCKET_BUFFER_SIZE];
+       struct nlmsghdr *nlh;
+       int ret;
+
+       nlh = nfq_hdr_put(buf, NFQNL_MSG_VERDICT, queue_num);
+       nfq_nlmsg_verdict_put(nlh, id, NF_ACCEPT);
+
+       if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+               perror("mnl_socket_send");
+               exit(EXIT_FAILURE);
+       }
+
+       return ret;
+}
+
+static int queue_cb(const struct nlmsghdr *nlh, void *data)
+{
+       struct nfqnl_msg_packet_hdr *ph = NULL;
+       struct nlattr *attr[NFQA_MAX+1];
+       uint32_t id = 0;
+       struct nfgenmsg *nfg;
+
+       if (nfq_nlmsg_parse(nlh, attr) < 0) {
+               perror("problems parsing");
+               return MNL_CB_ERROR;
+       }
+
+       nfg = mnl_nlmsg_get_payload(nlh);
+
+       ph = (struct nfqnl_msg_packet_hdr *)
+               mnl_attr_get_payload(attr[NFQA_PACKET_HDR]);
+       if (ph == NULL) {
+               perror("problems retrieving metaheader");
+               return MNL_CB_ERROR;
+       }
+
+       id = ntohl(ph->packet_id);
+
+       printf("packet received (id=%u hw=0x%04x hook=%u)\n",
+               id, ntohs(ph->hw_protocol), ph->hook);
+
+       nfq_send_verdict(ntohs(nfg->res_id), id);
+
+       return MNL_CB_OK;
+}
+
+int main(int argc, char *argv[])
+{
+       char buf[MNL_SOCKET_BUFFER_SIZE];
+       struct nlmsghdr *nlh;
+       int ret;
+       unsigned int portid, queue_num;
+
+       if (argc != 2) {
+               printf("Usage: %s [queue_num]\n", argv[0]);
+               exit(EXIT_FAILURE);
+       }
+       queue_num = atoi(argv[1]);
+
+       nl = mnl_socket_open(NETLINK_NETFILTER);
+       if (nl == NULL) {
+               perror("mnl_socket_open");
+               exit(EXIT_FAILURE);
+       }
+
+       if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+               perror("mnl_socket_bind");
+               exit(EXIT_FAILURE);
+       }
+       portid = mnl_socket_get_portid(nl);
+
+       nlh = nfq_hdr_put(buf, NFQNL_MSG_CONFIG, 0);
+       nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, NFQNL_CFG_CMD_PF_UNBIND);
+
+       if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+               perror("mnl_socket_send");
+               exit(EXIT_FAILURE);
+       }
+
+       nlh = nfq_hdr_put(buf, NFQNL_MSG_CONFIG, 0);
+       nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, NFQNL_CFG_CMD_PF_BIND);
+
+       if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+               perror("mnl_socket_send");
+               exit(EXIT_FAILURE);
+       }
+
+       nlh = nfq_hdr_put(buf, NFQNL_MSG_CONFIG, queue_num);
+       nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, NFQNL_CFG_CMD_BIND);
+
+       if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+               perror("mnl_socket_send");
+               exit(EXIT_FAILURE);
+       }
+
+       nlh = nfq_hdr_put(buf, NFQNL_MSG_CONFIG, queue_num);
+       nfq_nlmsg_cfg_put_params(nlh, NFQNL_COPY_PACKET, 0xffff);
+
+       if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+               perror("mnl_socket_send");
+               exit(EXIT_FAILURE);
+       }
+
+       ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+       if (ret == -1) {
+               perror("mnl_socket_recvfrom");
+               exit(EXIT_FAILURE);
+       }
+       while (ret > 0) {
+               uint32_t id;
+
+               ret = mnl_cb_run(buf, ret, 0, portid, queue_cb, NULL);
+               if (ret < 0){
+                       perror("mnl_cb_run");
+                       exit(EXIT_FAILURE);
+               }
+
+               ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+               if (ret == -1) {
+                       perror("mnl_socket_recvfrom");
+                       exit(EXIT_FAILURE);
+               }
+       }
+
+       mnl_socket_close(nl);
+
+       return 0;
+}
index 42fd733..54ea0b4 100644 (file)
@@ -1,3 +1 @@
-
-SUBDIRS = libnetfilter_queue
-
+SUBDIRS= libnetfilter_queue linux
index 188a927..1a92fc6 100644 (file)
@@ -1,3 +1,2 @@
-
-pkginclude_HEADERS = libnetfilter_queue.h linux_nfnetlink_queue.h
-
+pkginclude_HEADERS = libnetfilter_queue.h      \
+                    linux_nfnetlink_queue.h
index 6b8acd2..b9f16e2 100644 (file)
@@ -130,6 +130,20 @@ enum {
 
 extern int nfq_snprintf_xml(char *buf, size_t len, struct nfq_data *tb, int flags);
 
+/*
+ * New API based on libmnl
+ */
+
+void nfq_nlmsg_cfg_put_cmd(struct nlmsghdr *nlh, uint16_t pf, uint8_t cmd);
+void nfq_nlmsg_cfg_put_params(struct nlmsghdr *nlh, uint8_t mode, int range);
+void nfq_nlmsg_cfg_put_qmaxlen(struct nlmsghdr *nlh, uint32_t qmaxlen);
+
+void nfq_nlmsg_verdict_put(struct nlmsghdr *nlh, int id, int verdict);
+void nfq_nlmsg_verdict_put_mark(struct nlmsghdr *nlh, uint32_t mark);
+void nfq_nlmsg_verdict_put_pkt(struct nlmsghdr *nlh, const void *pkt, uint32_t pktlen);
+
+int nfq_nlmsg_parse(const struct nlmsghdr *nlh, struct nlattr **pkt);
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
diff --git a/include/linux/Makefile.am b/include/linux/Makefile.am
new file mode 100644 (file)
index 0000000..38eb109
--- /dev/null
@@ -0,0 +1 @@
+SUBDIRS = netfilter
diff --git a/include/linux/netfilter/Makefile.am b/include/linux/netfilter/Makefile.am
new file mode 100644 (file)
index 0000000..d0937cb
--- /dev/null
@@ -0,0 +1 @@
+noinst_HEADERS = nfnetlink_queue.h
diff --git a/include/linux/netfilter/nfnetlink_queue.h b/include/linux/netfilter/nfnetlink_queue.h
new file mode 100644 (file)
index 0000000..da44b33
--- /dev/null
@@ -0,0 +1,98 @@
+#ifndef _NFNETLINK_QUEUE_H
+#define _NFNETLINK_QUEUE_H
+
+#include <linux/types.h>
+#include <linux/netfilter/nfnetlink.h>
+
+enum nfqnl_msg_types {
+       NFQNL_MSG_PACKET,               /* packet from kernel to userspace */
+       NFQNL_MSG_VERDICT,              /* verdict from userspace to kernel */
+       NFQNL_MSG_CONFIG,               /* connect to a particular queue */
+       NFQNL_MSG_VERDICT_BATCH,        /* batchv from userspace to kernel */
+
+       NFQNL_MSG_MAX
+};
+
+struct nfqnl_msg_packet_hdr {
+       __be32          packet_id;      /* unique ID of packet in queue */
+       __be16          hw_protocol;    /* hw protocol (network order) */
+       __u8    hook;           /* netfilter hook */
+} __attribute__ ((packed));
+
+struct nfqnl_msg_packet_hw {
+       __be16          hw_addrlen;
+       __u16   _pad;
+       __u8    hw_addr[8];
+};
+
+struct nfqnl_msg_packet_timestamp {
+       __aligned_be64  sec;
+       __aligned_be64  usec;
+};
+
+enum nfqnl_attr_type {
+       NFQA_UNSPEC,
+       NFQA_PACKET_HDR,
+       NFQA_VERDICT_HDR,               /* nfqnl_msg_verdict_hrd */
+       NFQA_MARK,                      /* __u32 nfmark */
+       NFQA_TIMESTAMP,                 /* nfqnl_msg_packet_timestamp */
+       NFQA_IFINDEX_INDEV,             /* __u32 ifindex */
+       NFQA_IFINDEX_OUTDEV,            /* __u32 ifindex */
+       NFQA_IFINDEX_PHYSINDEV,         /* __u32 ifindex */
+       NFQA_IFINDEX_PHYSOUTDEV,        /* __u32 ifindex */
+       NFQA_HWADDR,                    /* nfqnl_msg_packet_hw */
+       NFQA_PAYLOAD,                   /* opaque data payload */
+       NFQA_CT,                        /* nf_conntrack_netlink.h */
+       NFQA_CT_INFO,                   /* enum ip_conntrack_info */
+
+       __NFQA_MAX
+};
+#define NFQA_MAX (__NFQA_MAX - 1)
+
+struct nfqnl_msg_verdict_hdr {
+       __be32 verdict;
+       __be32 id;
+};
+
+
+enum nfqnl_msg_config_cmds {
+       NFQNL_CFG_CMD_NONE,
+       NFQNL_CFG_CMD_BIND,
+       NFQNL_CFG_CMD_UNBIND,
+       NFQNL_CFG_CMD_PF_BIND,
+       NFQNL_CFG_CMD_PF_UNBIND,
+};
+
+struct nfqnl_msg_config_cmd {
+       __u8    command;        /* nfqnl_msg_config_cmds */
+       __u8    _pad;
+       __be16          pf;             /* AF_xxx for PF_[UN]BIND */
+};
+
+enum nfqnl_config_mode {
+       NFQNL_COPY_NONE,
+       NFQNL_COPY_META,
+       NFQNL_COPY_PACKET,
+};
+
+struct nfqnl_msg_config_params {
+       __be32          copy_range;
+       __u8    copy_mode;      /* enum nfqnl_config_mode */
+} __attribute__ ((packed));
+
+enum nfqnl_flags {
+       NFQNL_F_NONE            = 0,
+       NFQNL_F_CONNTRACK       = (1 << 0),
+};
+
+enum nfqnl_attr_config {
+       NFQA_CFG_UNSPEC,
+       NFQA_CFG_CMD,                   /* nfqnl_msg_config_cmd */
+       NFQA_CFG_PARAMS,                /* nfqnl_msg_config_params */
+       NFQA_CFG_QUEUE_MAXLEN,          /* __u32 */
+       NFQA_CFG_FLAGS,                 /* __u32 */
+       __NFQA_CFG_MAX
+};
+#define NFQA_CFG_MAX (__NFQA_CFG_MAX-1)
+
+#endif /* _NFNETLINK_QUEUE_H */
diff --git a/m4/gcc4_visibility.m4 b/m4/gcc4_visibility.m4
new file mode 100644 (file)
index 0000000..214d3f3
--- /dev/null
@@ -0,0 +1,21 @@
+
+# GCC 4.x -fvisibility=hidden
+
+AC_DEFUN([CHECK_GCC_FVISIBILITY], [
+       AC_LANG_PUSH([C])
+       saved_CFLAGS="$CFLAGS"
+       CFLAGS="$saved_CFLAGS -fvisibility=hidden"
+       AC_CACHE_CHECK([whether compiler accepts -fvisibility=hidden],
+         [ac_cv_fvisibility_hidden], AC_COMPILE_IFELSE(
+               [AC_LANG_SOURCE()],
+               [ac_cv_fvisibility_hidden=yes],
+               [ac_cv_fvisibility_hidden=no]
+       ))
+       if test "$ac_cv_fvisibility_hidden" = "yes"; then
+               AC_DEFINE([HAVE_VISIBILITY_HIDDEN], [1],
+                       [True if compiler supports -fvisibility=hidden])
+               AC_SUBST([GCC_FVISIBILITY_HIDDEN], [-fvisibility=hidden])
+       fi
+       CFLAGS="$saved_CFLAGS"
+       AC_LANG_POP([C])
+])
index 2196aef..884311f 100644 (file)
@@ -24,7 +24,10 @@ include ${top_srcdir}/Make_global.am
 
 lib_LTLIBRARIES = libnetfilter_queue.la
 
+include_HEADERS = internal.h
+
 libnetfilter_queue_la_LDFLAGS = -Wc,-nostartfiles -lnfnetlink \
                                -version-info $(LIBVERSION)
-libnetfilter_queue_la_SOURCES = libnetfilter_queue.c 
-libnetfilter_queue_la_LIBADD  = ${LIBNFNETLINK_LIBS}
+libnetfilter_queue_la_SOURCES = libnetfilter_queue.c   \
+                               nlmsg.c
+libnetfilter_queue_la_LIBADD  = ${LIBNFNETLINK_LIBS} ${LIBMNL_LIBS}
diff --git a/src/internal.h b/src/internal.h
new file mode 100644 (file)
index 0000000..3a88d1a
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef INTERNAL_H
+#define INTERNAL_H 1
+
+#include "config.h"
+#ifdef HAVE_VISIBILITY_HIDDEN
+#      define __visible        __attribute__((visibility("default")))
+#      define EXPORT_SYMBOL(x) typeof(x) (x) __visible
+#else
+#      define EXPORT_SYMBOL
+#endif
+
+#endif
diff --git a/src/nlmsg.c b/src/nlmsg.c
new file mode 100644 (file)
index 0000000..4637fd5
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <arpa/inet.h>
+#include <time.h>
+#include <endian.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libmnl/libmnl.h>
+
+#ifndef __aligned_be64
+#define __aligned_be64 __be64 __attribute__((aligned(8)))
+#define __aligned_le64 __le64 __attribute__((aligned(8)))
+#endif
+
+#include <linux/netfilter/nfnetlink_queue.h>
+
+#include <libnetfilter_queue/libnetfilter_queue.h>
+
+#include "internal.h"
+
+/**
+ * \defgroup nfq_verd Queue verdict object handling
+ * @{
+ */
+
+void nfq_nlmsg_verdict_put(struct nlmsghdr *nlh, int id, int verdict)
+{
+       struct nfqnl_msg_verdict_hdr vh = {
+               .verdict        = htonl(verdict),
+               .id             = htonl(id),
+       };
+       mnl_attr_put(nlh, NFQA_VERDICT_HDR, sizeof(vh), &vh);
+}
+EXPORT_SYMBOL(nfq_nlmsg_verdict_put);
+
+void nfq_nlmsg_verdict_put_mark(struct nlmsghdr *nlh, uint32_t mark)
+{
+       mnl_attr_put_u32(nlh, NFQA_MARK, htonl(mark));
+}
+EXPORT_SYMBOL(nfq_nlmsg_verdict_put_mark);
+
+void
+nfq_nlmsg_verdict_put_pkt(struct nlmsghdr *nlh, const void *pkt, uint32_t plen)
+{
+       mnl_attr_put(nlh, NFQA_PAYLOAD, plen, pkt);
+}
+EXPORT_SYMBOL(nfq_nlmsg_verdict_put_pkt);
+
+/**
+ * @}
+ */
+
+/**
+ * \defgroup nfq_cfg Queue config object handling
+ * @{
+ */
+
+/**
+ * nfq_nlmsg_cfg_build_request- build netlink config message
+ * \param buf Buffer where netlink message is going to be written.
+ * \param cfg Structure that contains the config parameters.
+ * \param command nfqueue nfnetlink command.
+ *
+ * This function returns a pointer to the netlink message. If something goes
+ * wrong it returns NULL.
+ *
+ * Possible commands are:
+ *
+ * - NFQNL_CFG_CMD_NONE: Do nothing. It can be useful to know if the queue
+ *   subsystem is working.
+ * - NFQNL_CFG_CMD_BIND: Binds the program to a specific queue.
+ * - NFQNL_CFG_CMD_UNBIND: Unbinds the program to a specifiq queue.
+ * - NFQNL_CFG_CMD_PF_BIND: Binds to process packets belonging to the given
+ *   protocol family (ie. PF_INET, PF_INET6, etc).
+ * - NFQNL_CFG_CMD_PF_UNBIND: Unbinds from processing packets belonging to the
+ *   given protocol family.
+ */
+void nfq_nlmsg_cfg_put_cmd(struct nlmsghdr *nlh, uint16_t pf, uint8_t cmd)
+{
+       struct nfqnl_msg_config_cmd command = {
+               .command = cmd,
+               .pf = htons(pf),
+       };
+       mnl_attr_put(nlh, NFQA_CFG_CMD, sizeof(command), &command);
+}
+EXPORT_SYMBOL(nfq_nlmsg_cfg_put_cmd);
+
+void nfq_nlmsg_cfg_put_params(struct nlmsghdr *nlh, uint8_t mode, int range)
+{
+       struct nfqnl_msg_config_params params = {
+               .copy_range = htonl(range),
+               .copy_mode = mode,
+       };
+       mnl_attr_put(nlh, NFQA_CFG_PARAMS, sizeof(params), &params);
+}
+EXPORT_SYMBOL(nfq_nlmsg_cfg_put_params);
+
+void nfq_nlmsg_cfg_put_qmaxlen(struct nlmsghdr *nlh, uint32_t queue_maxlen)
+{
+       mnl_attr_put_u32(nlh, NFQA_CFG_QUEUE_MAXLEN, htonl(queue_maxlen));
+}
+EXPORT_SYMBOL(nfq_nlmsg_cfg_put_qmaxlen);
+
+/**
+ * @}
+ */
+
+/**
+ * \defgroup nlmsg Netlink message helper functions
+ * @{
+ */
+
+static int nfq_pkt_parse_attr_cb(const struct nlattr *attr, void *data)
+{
+       const struct nlattr **tb = data;
+       int type = mnl_attr_get_type(attr);
+
+       /* skip unsupported attribute in user-space */
+       if (mnl_attr_type_valid(attr, NFQA_MAX) < 0)
+               return MNL_CB_OK;
+
+       switch(type) {
+       case NFQA_MARK:
+       case NFQA_IFINDEX_INDEV:
+       case NFQA_IFINDEX_OUTDEV:
+       case NFQA_IFINDEX_PHYSINDEV:
+       case NFQA_IFINDEX_PHYSOUTDEV:
+               if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+                       return MNL_CB_ERROR;
+               break;
+       case NFQA_TIMESTAMP:
+               if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC,
+                   sizeof(struct nfqnl_msg_packet_timestamp)) < 0) {
+                       return MNL_CB_ERROR;
+               }
+               break;
+       case NFQA_HWADDR:
+               if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC,
+                   sizeof(struct nfqnl_msg_packet_hw)) < 0) {
+                       return MNL_CB_ERROR;
+               }
+               break;
+       case NFQA_PAYLOAD:
+               break;
+       }
+       tb[type] = attr;
+       return MNL_CB_OK;
+}
+
+/**
+ * nfq_pkt_parse - set packet attributes from netlink message
+ * \param nlh netlink message that you want to read.
+ * \param pkt pointer to the packet to set.
+ *
+ * This function returns MNL_CB_ERROR if any error occurs, or MNL_CB_OK on
+ * success.
+ */
+int nfq_nlmsg_parse(const struct nlmsghdr *nlh, struct nlattr **attr)
+{
+       return mnl_attr_parse(nlh, sizeof(struct nfgenmsg),
+                             nfq_pkt_parse_attr_cb, attr);
+}
+EXPORT_SYMBOL(nfq_nlmsg_parse);
+
+/**
+ * @}
+ */