doc: document some performance tweaks for libnetfilter_queue
[platform/upstream/libnetfilter_queue.git] / src / libnetfilter_queue.c
index ab19814..b2ca219 100644 (file)
@@ -1,10 +1,11 @@
-/* libnfqnetlink.c: generic library for access to nf_queue
+/* libnetfilter_queue.c: generic library for access to nf_queue
  *
  * (C) 2005 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2005, 2008-2010 by Pablo Neira Ayuso <pablo@netfilter.org>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 
- *  as published by the Free Software Foundation
+ *  as published by the Free Software Foundation (or any later at your option)
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -14,6 +15,9 @@
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  2006-01-23 Andreas Florath <andreas@florath.net>
+ *     Fix __set_verdict() that it can now handle payload.
  */
 
 #include <stdio.h>
 #include <libnfnetlink/libnfnetlink.h>
 #include <libnetfilter_queue/libnetfilter_queue.h>
 
+/**
+ * \mainpage
+ *
+ * libnetfilter_queue is a userspace library providing an API to packets that
+ * have been queued by the kernel packet filter. It is is part of a system that
+ * deprecates the old ip_queue / libipq mechanism.
+ *
+ * libnetfilter_queue homepage is:
+ *     http://netfilter.org/projects/libnetfilter_queue/
+ *
+ * \section Dependencies
+ * libnetfilter_queue requires libnfnetlink and a kernel that includes the
+ * nfnetlink_queue subsystem (i.e. 2.6.14 or later).
+ *
+ * \section Main Features
+ *  - receiving queued packets from the kernel nfnetlink_queue subsystem
+ *  - issuing verdicts and/or reinjecting altered packets to the kernel
+ *  nfnetlink_queue subsystem
+ * 
+ * \section Git Tree
+ * The current development version of libnetfilter_queue can be accessed
+ * at https://git.netfilter.org/cgi-bin/gitweb.cgi?p=libnetfilter_queue.git;a=summary.
+ *
+ * \section Privileges
+ * You need the CAP_NET_ADMIN capability in order to allow your application
+ * to receive from and to send packets to kernel-space.
+ *
+ * \section Using libnetfilter_queue
+ * 
+ * To write your own program using libnetfilter_queue, you should start by reading
+ * the doxygen documentation (start by \link LibrarySetup \endlink page) and nfqnl_test.c source file.
+ *
+ * \section errors ENOBUFS errors in recv()
+ *
+ * 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,
+ * you may hit it again sooner or later. The next section provides some hints
+ * on how to obtain the best performance for your application.
+ *
+ * \section perf Performance
+ * 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().
+ * - 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.
+ * - set NETLINK_NO_ENOBUFS socket option to avoid receiving ENOBUFS errors
+ * (requires Linux kernel >= 2.6.30).
+ * - see --queue-balance option in NFQUEUE target for multi-threaded apps
+ * (it requires Linux kernel >= 2.6.31).
+ */
+
 struct nfq_handle
 {
        struct nfnl_handle *nfnlh;
@@ -94,19 +153,21 @@ static struct nfq_q_handle *find_qh(struct nfq_handle *h, u_int16_t id)
 __build_send_cfg_msg(struct nfq_handle *h, u_int8_t command,
                u_int16_t queuenum, u_int16_t pf)
 {
-       char buf[NFNL_HEADER_LEN
-               +NFA_LENGTH(sizeof(struct nfqnl_msg_config_cmd))];
+       union {
+               char buf[NFNL_HEADER_LEN
+                       +NFA_LENGTH(sizeof(struct nfqnl_msg_config_cmd))];
+               struct nlmsghdr nmh;
+       } u;
        struct nfqnl_msg_config_cmd cmd;
-       struct nlmsghdr *nmh = (struct nlmsghdr *) buf;
 
-       nfnl_fill_hdr(h->nfnlssh, nmh, 0, AF_UNSPEC, queuenum,
+       nfnl_fill_hdr(h->nfnlssh, &u.nmh, 0, AF_UNSPEC, queuenum,
                        NFQNL_MSG_CONFIG, NLM_F_REQUEST|NLM_F_ACK);
 
        cmd.command = command;
        cmd.pf = htons(pf);
-       nfnl_addattr_l(nmh, sizeof(buf), NFQA_CFG_CMD, &cmd, sizeof(cmd));
+       nfnl_addattr_l(&u.nmh, sizeof(u), NFQA_CFG_CMD, &cmd, sizeof(cmd));
 
-       return nfnl_talk(h->nfnlh, nmh, 0, 0, NULL, NULL, NULL);
+       return nfnl_query(h->nfnlh, &u.nmh);
 }
 
 static int __nfq_rcv_pkt(struct nlmsghdr *nlh, struct nfattr *nfa[],
@@ -141,11 +202,126 @@ struct nfnl_handle *nfq_nfnlh(struct nfq_handle *h)
        return h->nfnlh;
 }
 
+/**
+ *
+ * \defgroup Queue Queue handling
+ *
+ * Once libnetfilter_queue library has been initialised (See 
+ * \link LibrarySetup \endlink), it is possible to bind the program to a
+ * specific queue. This can be done by using nfq_create_queue().
+ *
+ * The queue can then be tuned via nfq_set_mode() or nfq_set_queue_maxlen().
+ * 
+ * Here's a little code snippet that create queue numbered 0:
+ * \verbatim
+       printf("binding this socket to queue '0'\n");
+       qh = nfq_create_queue(h,  0, &cb, NULL);
+       if (!qh) {
+               fprintf(stderr, "error during nfq_create_queue()\n");
+               exit(1);
+       }
+
+       printf("setting copy_packet mode\n");
+       if (nfq_set_mode(qh, NFQNL_COPY_PACKET, 0xffff) < 0) {
+               fprintf(stderr, "can't set packet_copy mode\n");
+               exit(1);
+       }
+\endverbatim
+ *
+ * Next step is the handling of incoming packets which can be done via a loop:
+ *
+ * \verbatim
+       fd = nfq_fd(h);
+
+       while ((rv = recv(fd, buf, sizeof(buf), 0)) >= 0) {
+               printf("pkt received\n");
+               nfq_handle_packet(h, buf, rv);
+       }
+\endverbatim
+ * When the decision on a packet has been choosed, the verdict has to be given
+ * by calling nfq_set_verdict() or nfq_set_verdict2(). The verdict
+ * determines the destiny of the packet as follows:
+ *
+ *   - NF_DROP discarded the packet
+ *   - NF_ACCEPT the packet passes, continue iterations
+ *   - NF_STOLEN gone away
+ *   - NF_QUEUE inject the packet into a different queue
+ *     (the target queue number is in the high 16 bits of the verdict)
+ *   - NF_REPEAT iterate the same cycle once more
+ *   - NF_STOP accept, but don't continue iterations
+ *
+ * Data and information about the packet can be fetch by using message parsing
+ * functions (See \link Parsing \endlink).
+ * @{
+ */
+
+/**
+ * nfq_fd - get the file descriptor associated with the nfqueue handler
+ * \param h Netfilter queue connection handle obtained via call to nfq_open()
+ *
+ * \return a file descriptor for the netlink connection associated with the
+ * given queue connection handle. The file descriptor can then be used for
+ * receiving the queued packets for processing.
+ *
+  * This function returns a file descriptor that can be used for communication
+ * over the netlink connection associated with the given queue connection
+ * handle.
+ */
 int nfq_fd(struct nfq_handle *h)
 {
        return nfnl_fd(nfq_nfnlh(h));
 }
 
+/**
+ * @}
+ */
+
+/**
+ * \defgroup LibrarySetup Library setup
+ *
+ * Library initialisation is made in two steps.
+ *
+ * First step is to call nfq_open() to open a NFQUEUE handler. 
+ *
+ * Second step is to tell the kernel that userspace queueing is handle by
+ * NFQUEUE for the selected protocol. This is made by calling nfq_unbind_pf()
+ * and nfq_bind_pf() with protocol information. The idea behind this is to
+ * enable simultaneously loaded modules to be used for queuing.
+ *
+ * Here's a little code snippet that bind with AF_INET:
+ * \verbatim
+       h = nfq_open();
+       if (!h) {
+               fprintf(stderr, "error during nfq_open()\n");
+               exit(1);
+       }
+
+       printf("unbinding existing nf_queue handler for AF_INET (if any)\n");
+       if (nfq_unbind_pf(h, AF_INET) < 0) {
+               fprintf(stderr, "error during nfq_unbind_pf()\n");
+               exit(1);
+       }
+
+       printf("binding nfnetlink_queue as nf_queue handler for AF_INET\n");
+       if (nfq_bind_pf(h, AF_INET) < 0) {
+               fprintf(stderr, "error during nfq_bind_pf()\n");
+               exit(1);
+       }
+\endverbatim
+ * Once this is done, you can setup and use a \link Queue \endlink.
+ * @{
+ */
+
+/**
+ * nfq_open - open a nfqueue handler
+ *
+ * This function obtains a netfilter queue connection handle. When you are
+ * finished with the handle returned by this function, you should destroy
+ * it by calling nfq_close(). A new netlink connection is obtained internally
+ * and associated with the queue connection handle returned.
+ *
+ * \return a pointer to a new queue handle or NULL on failure.
+ */
 struct nfq_handle *nfq_open(void)
 {
        struct nfnl_handle *nfnlh = nfnl_open();
@@ -154,6 +330,9 @@ struct nfq_handle *nfq_open(void)
        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);
@@ -161,6 +340,20 @@ struct nfq_handle *nfq_open(void)
        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.
+ */
 struct nfq_handle *nfq_open_nfnl(struct nfnl_handle *nfnlh)
 {
        struct nfq_handle *h;
@@ -195,30 +388,104 @@ out_free:
        return NULL;
 }
 
+/**
+ * \addtogroup LibrarySetup
+ *
+ * When the program has finished with libnetfilter_queue, it has to call
+ * the nfq_close() function to free all associated resources.
+ *
+ * @{
+ */
+
+/**
+ * nfq_close - close a nfqueue handler
+ * \param h Netfilter queue connection handle obtained via call to nfq_open()
+ *
+ * This function closes the nfqueue handler and free associated resources.
+ *
+ * \return 0 on success, non-zero on failure. 
+ */
 int nfq_close(struct nfq_handle *h)
 {
        int ret;
        
-       nfnl_subsys_close(h->nfnlssh);
        ret = nfnl_close(h->nfnlh);
        if (ret == 0)
                free(h);
        return ret;
 }
 
-/* bind nf_queue from a specific protocol family */
+/**
+ * nfq_bind_pf - bind a nfqueue handler to a given protocol family
+ * \param h Netfilter queue connection handle obtained via call to nfq_open()
+ * \param pf protocol family to bind to nfqueue handler obtained from nfq_open()
+ *
+ * Binds the given queue connection handle to process packets belonging to 
+ * the given protocol family (ie. PF_INET, PF_INET6, etc).
+ *
+ * \return integer inferior to 0 in case of failure
+ */
 int nfq_bind_pf(struct nfq_handle *h, u_int16_t pf)
 {
        return __build_send_cfg_msg(h, NFQNL_CFG_CMD_PF_BIND, 0, pf);
 }
 
-/* unbind nf_queue from a specific protocol family */
+/**
+ * nfq_unbind_pf - unbind nfqueue handler from a protocol family
+ * \param h Netfilter queue connection handle obtained via call to nfq_open()
+ * \param pf protocol family to unbind family from
+ *
+ * Unbinds the given queue connection handle from processing packets belonging
+ * to the given protocol family.
+ */
 int nfq_unbind_pf(struct nfq_handle *h, u_int16_t pf)
 {
        return __build_send_cfg_msg(h, NFQNL_CFG_CMD_PF_UNBIND, 0, pf);
 }
 
-/* bind this socket to a specific queue number */
+
+
+/**
+ * @}
+ */
+
+/**
+ * \addtogroup Queue
+ * @{
+ */
+
+/**
+ * nfq_create_queue - create a new queue handle and return it.
+ *
+ * \param h Netfilter queue connection handle obtained via call to nfq_open()
+ * \param num the number of the queue to bind to
+ * \param cb callback function to call for each queued packet
+ * \param data custom data to pass to the callback function
+ *
+ * \return a nfq_q_handle pointing to the newly created queue
+ *
+ * Creates a new queue handle, and returns it.  The new queue is identified by
+ * #num, and the callback specified by #cb will be called for each enqueued
+ * packet.  The #data argument will be passed unchanged to the callback.  If
+ * a queue entry with id #num already exists, this function will return failure
+ * and the existing entry is unchanged.
+ *
+ * The nfq_callback type is defined in libnetfilter_queue.h as:
+ * \verbatim
+typedef int nfq_callback(struct nfq_q_handle *qh,
+                        struct nfgenmsg *nfmsg,
+                        struct nfq_data *nfad, void *data);
+\endverbatim
+ *
+ * Parameters:
+ *  - qh The queue handle returned by nfq_create_queue
+ *  - nfmsg message objetc that contains the packet
+ *  - nfad Netlink packet data handle
+ *  - data the value passed to the data parameter of nfq_create_queue
+ *
+ * The callback should return < 0 to stop processing.
+ */
+
 struct nfq_q_handle *nfq_create_queue(struct nfq_handle *h, 
                u_int16_t num,
                nfq_callback *cb,
@@ -249,7 +516,22 @@ struct nfq_q_handle *nfq_create_queue(struct nfq_handle *h,
        return qh;
 }
 
-/* unbind this socket from a specific queue number */
+/**
+ * @}
+ */
+
+/**
+ * \addtogroup Queue
+ * @{
+ */
+
+/**
+ * nfq_destroy_queue - destroy a queue handle
+ * \param qh queue handle that we want to destroy created via nfq_create_queue
+ *
+ * Removes the binding for the specified queue handle. This call also unbind
+ * from the nfqueue handler, so you don't have to call nfq_unbind_pf.
+ */
 int nfq_destroy_queue(struct nfq_q_handle *qh)
 {
        int ret = __build_send_cfg_msg(qh->h, NFQNL_CFG_CMD_UNBIND, qh->id, 0);
@@ -261,101 +543,275 @@ int nfq_destroy_queue(struct nfq_q_handle *qh)
        return ret;
 }
 
+/**
+ * nfq_handle_packet - handle a packet received from the nfqueue subsystem
+ * \param h Netfilter queue connection handle obtained via call to nfq_open()
+ * \param buf data to pass to the callback
+ * \param len length of packet data in buffer
+ *
+ * Triggers an associated callback for the given packet received from the
+ * 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.
+ */
 int nfq_handle_packet(struct nfq_handle *h, char *buf, int len)
 {
        return nfnl_handle_packet(h->nfnlh, buf, len);
 }
 
+/**
+ * nfq_set_mode - set the amount of packet data that nfqueue copies to userspace
+ * \param qh Netfilter queue handle obtained by call to nfq_create_queue().
+ * \param mode the part of the packet that we are interested in
+ * \param range size of the packet that we want to get
+ *
+ * Sets the amount of data to be copied to userspace for each packet queued
+ * to the given queue.
+ *
+ * - NFQNL_COPY_NONE - do not copy any data
+ * - NFQNL_COPY_META - copy only packet metadata
+ * - NFQNL_COPY_PACKET - copy entire packet
+ *
+ * \return -1 on error; >=0 otherwise.
+ */
 int nfq_set_mode(struct nfq_q_handle *qh,
                u_int8_t mode, u_int32_t range)
 {
-       char buf[NFNL_HEADER_LEN
-               +NFA_LENGTH(sizeof(struct nfqnl_msg_config_params))];
+       union {
+               char buf[NFNL_HEADER_LEN
+                       +NFA_LENGTH(sizeof(struct nfqnl_msg_config_params))];
+               struct nlmsghdr nmh;
+       } u;
        struct nfqnl_msg_config_params params;
-       struct nlmsghdr *nmh = (struct nlmsghdr *) buf;
 
-       nfnl_fill_hdr(qh->h->nfnlssh, nmh, 0, AF_UNSPEC, qh->id,
+       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(nmh, sizeof(buf), NFQA_CFG_PARAMS, &params,
+       nfnl_addattr_l(&u.nmh, sizeof(u), NFQA_CFG_PARAMS, &params,
                        sizeof(params));
 
-       return nfnl_talk(qh->h->nfnlh, nmh, 0, 0, NULL, NULL, NULL);
+       return nfnl_query(qh->h->nfnlh, &u.nmh);
+}
+
+/**
+ * nfq_set_queue_maxlen - Set kernel queue maximum length parameter
+ * \param qh Netfilter queue handle obtained by call to nfq_create_queue().
+ * \param queuelen the length of the queue
+ *
+ * Sets the size of the queue in kernel. This fixes the maximum number
+ * of packets the kernel will store before internally before dropping
+ * upcoming packets.
+ *
+ * \return -1 on error; >=0 otherwise.
+ */
+int nfq_set_queue_maxlen(struct nfq_q_handle *qh,
+                               u_int32_t queuelen)
+{
+       union {
+               char buf[NFNL_HEADER_LEN
+                       +NFA_LENGTH(sizeof(struct nfqnl_msg_config_params))];
+               struct nlmsghdr nmh;
+       } u;
+       u_int32_t queue_maxlen = htonl(queuelen);
+
+       nfnl_fill_hdr(qh->h->nfnlssh, &u.nmh, 0, AF_UNSPEC, qh->id,
+                       NFQNL_MSG_CONFIG, NLM_F_REQUEST|NLM_F_ACK);
+
+       nfnl_addattr_l(&u.nmh, sizeof(u), NFQA_CFG_QUEUE_MAXLEN, &queue_maxlen,
+                       sizeof(queue_maxlen));
+
+       return nfnl_query(qh->h->nfnlh, &u.nmh);
 }
 
+/**
+ * @}
+ */
+
 static int __set_verdict(struct nfq_q_handle *qh, u_int32_t id,
                u_int32_t verdict, u_int32_t mark, int set_mark,
-               u_int32_t data_len, unsigned char *data)
+               u_int32_t data_len, const unsigned char *data)
 {
        struct nfqnl_msg_verdict_hdr vh;
-       char buf[NFNL_HEADER_LEN
-               +NFA_LENGTH(sizeof(mark))
-               +NFA_LENGTH(sizeof(vh))];
-       struct nlmsghdr *nmh = (struct nlmsghdr *) buf;
+       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));
 
        vh.verdict = htonl(verdict);
        vh.id = htonl(id);
 
-       nfnl_fill_hdr(qh->h->nfnlssh, nmh, 0, AF_UNSPEC, qh->id,
+       nfnl_fill_hdr(qh->h->nfnlssh, &u.nmh, 0, AF_UNSPEC, qh->id,
                        NFQNL_MSG_VERDICT, NLM_F_REQUEST);
 
        /* add verdict header */
-       nfnl_addattr_l(nmh, sizeof(buf), NFQA_VERDICT_HDR, &vh, sizeof(vh));
+       nfnl_addattr_l(&u.nmh, sizeof(u), NFQA_VERDICT_HDR, &vh, sizeof(vh));
 
        if (set_mark)
-               nfnl_addattr32(nmh, sizeof(buf), NFQA_MARK, mark);
+               nfnl_addattr32(&u.nmh, sizeof(u), NFQA_MARK, mark);
 
-       iov[0].iov_base = nmh;
-       iov[0].iov_len = NLMSG_TAIL(nmh) - (void *)nmh;
+       iov[0].iov_base = &u.nmh;
+       iov[0].iov_len = NLMSG_TAIL(&u.nmh) - (void *)&u.nmh;
        nvecs = 1;
 
        if (data_len) {
-               struct nfattr data_attr;
-
+               /* The typecast here is to cast away data's const-ness: */
                nfnl_build_nfa_iovec(&iov[1], &data_attr, NFQA_PAYLOAD,
-                               data_len, data);
+                               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()
+                * function. */
+               u.nmh.nlmsg_len += data_attr.nfa_len;
        }
 
        return nfnl_sendiov(qh->h->nfnlh, iov, nvecs, 0);
 }
 
+/**
+ * \addtogroup Queue
+ * @{
+ */
+
+/**
+ * nfq_set_verdict - issue a verdict on a packet 
+ * \param qh Netfilter queue handle obtained by call to nfq_create_queue().
+ * \param id   ID assigned to packet by netfilter.
+ * \param verdict verdict to return to netfilter (NF_ACCEPT, NF_DROP)
+ * \param data_len number of bytes of data pointed to by #buf
+ * \param buf the buffer that contains the packet data
+ *
+ * Can be obtained by: 
+ * \verbatim
+       int id;
+       struct nfqnl_msg_packet_hdr *ph = nfq_get_msg_packet_hdr(tb);
+       if (ph)
+               id = ntohl(ph->packet_id);
+\endverbatim
+ *
+ * Notifies netfilter of the userspace verdict for the given packet.  Every
+ * queued packet _must_ have a verdict specified by userspace, either by
+ * calling this function, or by calling the nfq_set_verdict2() function.
+ *
+ * \return -1 on error; >= 0 otherwise.
+ */
 int nfq_set_verdict(struct nfq_q_handle *qh, u_int32_t id,
                u_int32_t verdict, u_int32_t data_len, 
-               unsigned char *buf)
+               const unsigned char *buf)
 {
        return __set_verdict(qh, id, verdict, 0, 0, data_len, buf);
 }      
 
+/**
+ * nfq_set_verdict2 - like nfq_set_verdict, but you can set the mark.
+ * \param qh Netfilter queue handle obtained by call to nfq_create_queue().
+ * \param id   ID assigned to packet by netfilter.
+ * \param verdict verdict to return to netfilter (NF_ACCEPT, NF_DROP)
+ * \param mark mark to put on packet
+ * \param data_len number of bytes of data pointed to by #buf
+ * \param buf the buffer that contains the packet data
+ */
+int nfq_set_verdict2(struct nfq_q_handle *qh, u_int32_t id,
+                    u_int32_t verdict, u_int32_t mark,
+                    u_int32_t data_len, const unsigned char *buf)
+{
+       return __set_verdict(qh, id, verdict, htonl(mark), 1, data_len, buf);
+}
+
+/**
+ * nfq_set_verdict_mark - like nfq_set_verdict, but you can set the mark.
+ * \param qh Netfilter queue handle obtained by call to nfq_create_queue().
+ * \param id   ID assigned to packet by netfilter.
+ * \param verdict verdict to return to netfilter (NF_ACCEPT, NF_DROP)
+ * \param mark the mark to put on the packet, in network byte order.
+ * \param data_len number of bytes of data pointed to by #buf
+ * \param buf the buffer that contains the packet data
+ *
+ * \return -1 on error; >= 0 otherwise.
+ *
+ * This function is deprecated since it is broken, its use is highly
+ * discouraged. Please, use nfq_set_verdict2 instead.
+ */
 int nfq_set_verdict_mark(struct nfq_q_handle *qh, u_int32_t id,
                u_int32_t verdict, u_int32_t mark,
-               u_int32_t datalen, unsigned char *buf)
+               u_int32_t data_len, const unsigned char *buf)
 {
-       return __set_verdict(qh, id, verdict, mark, 1, datalen, buf);
+       return __set_verdict(qh, id, verdict, mark, 1, data_len, buf);
 }
 
+/**
+ * @}
+ */
+
+
+
 /*************************************************************
  * Message parsing functions 
  *************************************************************/
 
+/**
+ * \defgroup Parsing Message parsing functions
+ * @{
+ */
+
+/**
+ * nfqnl_msg_packet_hdr - return the metaheader that wraps the packet
+ * \param nfad Netlink packet data handle passed to callback function
+ *
+ * \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:
+ *
+ * \verbatim
+       struct nfqnl_msg_packet_hdr {
+               u_int32_t       packet_id;      // unique ID of packet in queue
+               u_int16_t       hw_protocol;    // hw protocol (network order)
+               u_int8_t        hook;           // netfilter hook
+       } __attribute__ ((packed));
+\endverbatim
+ */
 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);
 }
 
+/**
+ * nfq_get_nfmark - get the packet mark
+ * \param nfad Netlink packet data handle passed to callback function
+ *
+ * \return the netfilter mark currently assigned to the given queued packet.
+ */
 uint32_t nfq_get_nfmark(struct nfq_data *nfad)
 {
        return ntohl(nfnl_get_data(nfad->data, NFQA_MARK, u_int32_t));
 }
 
+/**
+ * nfq_get_timestamp - get the packet timestamp
+ * \param nfad Netlink packet data handle passed to callback function
+ * \param tv structure to fill with timestamp info
+ *
+ * Retrieves the received timestamp when the given queued packet.
+ *
+ * \return 0 on success, non-zero on failure.
+ */
 int nfq_get_timestamp(struct nfq_data *nfad, struct timeval *tv)
 {
        struct nfqnl_msg_packet_timestamp *qpt;
@@ -370,35 +826,206 @@ int nfq_get_timestamp(struct nfq_data *nfad, struct timeval *tv)
        return 0;
 }
 
-/* all nfq_get_*dev() functions return 0 if not set, since linux only allows
- * ifindex >= 1, see net/core/dev.c:2600  (in 2.6.13.1) */
+/**
+ * nfq_get_indev - get the interface that the packet was received through
+ * \param nfad Netlink packet data handle passed to callback function
+ *
+ * \return The index of the device the queued packet was received via.  If the
+ * returned index is 0, the packet was locally generated or the input
+ * interface is not known (ie. POSTROUTING?).
+ *
+ * \warning all nfq_get_dev() functions return 0 if not set, since linux
+ * only allows ifindex >= 1, see net/core/dev.c:2600  (in 2.6.13.1)
+ */
 u_int32_t nfq_get_indev(struct nfq_data *nfad)
 {
        return ntohl(nfnl_get_data(nfad->data, NFQA_IFINDEX_INDEV, u_int32_t));
 }
 
+/**
+ * nfq_get_physindev - get the physical interface that the packet was received
+ * \param nfad Netlink packet data handle passed to callback function
+ *
+ * \return The index of the physical device the queued packet was received via.
+ * If the returned index is 0, the packet was locally generated or the
+ * physical input interface is no longer known (ie. POSTROUTING?).
+ */
 u_int32_t nfq_get_physindev(struct nfq_data *nfad)
 {
        return ntohl(nfnl_get_data(nfad->data, NFQA_IFINDEX_PHYSINDEV, u_int32_t));
 }
 
+/**
+ * nfq_get_outdev - gets the interface that the packet will be routed out
+ * \param nfad Netlink packet data handle passed to callback function
+ *
+ * \return The index of the device the queued packet will be sent out.  If the
+ * returned index is 0, the packet is destined for localhost or the output
+ * interface is not yet known (ie. PREROUTING?).
+ */
 u_int32_t nfq_get_outdev(struct nfq_data *nfad)
 {
        return ntohl(nfnl_get_data(nfad->data, NFQA_IFINDEX_OUTDEV, u_int32_t));
 }
 
+/**
+ * nfq_get_physoutdev - get the physical interface that the packet output
+ * \param nfad Netlink packet data handle passed to callback function
+ *
+ * The index of the physical device the queued packet will be sent out.
+ * If the returned index is 0, the packet is destined for localhost or the
+ * physical output interface is not yet known (ie. PREROUTING?).
+ * 
+ * \return The index of physical interface that the packet output will be routed out.
+ */
 u_int32_t nfq_get_physoutdev(struct nfq_data *nfad)
 {
        return ntohl(nfnl_get_data(nfad->data, NFQA_IFINDEX_PHYSOUTDEV, u_int32_t));
 }
 
+/**
+ * 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 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
+ *
+ */
+int nfq_get_indev_name(struct nlif_handle *nlif_handle,
+                       struct nfq_data *nfad, char *name)
+{
+       u_int32_t ifindex = nfq_get_indev(nfad);
+       return nlif_index2name(nlif_handle, ifindex, name);
+}
+
+/**
+ * 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 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. 
+ */
+int nfq_get_physindev_name(struct nlif_handle *nlif_handle,
+                          struct nfq_data *nfad, char *name)
+{
+       u_int32_t ifindex = nfq_get_physindev(nfad);
+       return nlif_index2name(nlif_handle, ifindex, name);
+}
+
+/**
+ * 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 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. 
+ */
+int nfq_get_outdev_name(struct nlif_handle *nlif_handle,
+                       struct nfq_data *nfad, char *name)
+{
+       u_int32_t ifindex = nfq_get_outdev(nfad);
+       return nlif_index2name(nlif_handle, ifindex, name);
+}
+
+/**
+ * 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 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. 
+ */
+
+int nfq_get_physoutdev_name(struct nlif_handle *nlif_handle,
+                           struct nfq_data *nfad, char *name)
+{
+       u_int32_t ifindex = nfq_get_physoutdev(nfad);
+       return nlif_index2name(nlif_handle, ifindex, name);
+}
+
+/**
+ * nfq_get_packet_hw
+ *
+ * get hardware address 
+ *
+ * \param nfad Netlink packet data handle passed to callback function
+ *
+ * Retrieves the hardware address associated with the given queued packet.
+ * For ethernet packets, the hardware address returned (if any) will be the
+ * MAC address of the packet source host.  The destination MAC address is not
+ * known until after POSTROUTING and a successful ARP request, so cannot
+ * currently be retrieved.
+ *
+ * The nfqnl_msg_packet_hw structure is defined in libnetfilter_queue.h as:
+ * \verbatim
+       struct nfqnl_msg_packet_hw {
+               u_int16_t       hw_addrlen;
+               u_int16_t       _pad;
+               u_int8_t        hw_addr[8];
+       } __attribute__ ((packed));
+\endverbatim
+ */
 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);
 }
 
-int nfq_get_payload(struct nfq_data *nfad, char **data)
+/**
+ * nfq_get_payload - get payload 
+ * \param nfad Netlink packet data handle passed to callback function
+ * \param data Pointer of pointer that will be pointed to the payload
+ *
+ * Retrieve the payload for a queued packet. The actual amount and type of
+ * data retrieved by this function will depend on the mode set with the
+ * nfq_set_mode() function.
+ *
+ * \return -1 on error, otherwise > 0.
+ */
+int nfq_get_payload(struct nfq_data *nfad, unsigned char **data)
 {
        *data = nfnl_get_pointer_to_data(nfad->data, NFQA_PAYLOAD, char);
        if (*data)
@@ -406,3 +1033,193 @@ int nfq_get_payload(struct nfq_data *nfad, char **data)
 
        return -1;
 }
+
+/**
+ * @}
+ */
+
+#define SNPRINTF_FAILURE(ret, rem, offset, len)                        \
+do {                                                           \
+       if (ret < 0)                                            \
+               return ret;                                     \
+       len += ret;                                             \
+       if (ret > rem)                                          \
+               ret = rem;                                      \
+       offset += ret;                                          \
+       rem -= ret;                                             \
+} while (0)
+
+/**
+ * \defgroup Printing
+ * @{
+ */
+
+/**
+ * nfq_snprintf_xml - print the enqueued packet in XML format into a buffer
+ * \param buf The buffer that you want to use to print the logged packet
+ * \param rem The size of the buffer that you have passed
+ * \param tb Netlink packet data handle passed to callback function
+ * \param flags The flag that tell what to print into the buffer
+ *
+ * This function supports the following flags:
+ *
+ *     - NFQ_XML_HW: include the hardware link layer address
+ *     - NFQ_XML_MARK: include the packet mark
+ *     - NFQ_XML_DEV: include the device information
+ *     - NFQ_XML_PHYSDEV: include the physical device information
+ *     - NFQ_XML_PAYLOAD: include the payload (in hexadecimal)
+ *     - 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.
+ *
+ * \return -1 in case of failure, 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.
+ */
+int nfq_snprintf_xml(char *buf, size_t rem, struct nfq_data *tb, int flags)
+{
+       struct nfqnl_msg_packet_hdr *ph;
+       struct nfqnl_msg_packet_hw *hwph;
+       u_int32_t mark, ifi;
+       int size, offset = 0, len = 0, ret;
+       unsigned char *data;
+
+       size = snprintf(buf + offset, rem, "<pkt>");
+       SNPRINTF_FAILURE(size, rem, offset, len);
+
+       if (flags & NFQ_XML_TIME) {
+               time_t t;
+               struct tm tm;
+
+               t = time(NULL);
+               if (localtime_r(&t, &tm) == NULL)
+                       return -1;
+
+               size = snprintf(buf + offset, rem, "<when>");
+               SNPRINTF_FAILURE(size, rem, offset, len);
+
+               size = snprintf(buf + offset, rem,
+                               "<hour>%d</hour>", tm.tm_hour);
+               SNPRINTF_FAILURE(size, rem, offset, len);
+
+               size = snprintf(buf + offset,
+                               rem, "<min>%02d</min>", tm.tm_min);
+               SNPRINTF_FAILURE(size, rem, offset, len);
+
+               size = snprintf(buf + offset,
+                               rem, "<sec>%02d</sec>", tm.tm_sec);
+               SNPRINTF_FAILURE(size, rem, offset, len);
+
+               size = snprintf(buf + offset, rem, "<wday>%d</wday>",
+                               tm.tm_wday + 1);
+               SNPRINTF_FAILURE(size, rem, offset, len);
+
+               size = snprintf(buf + offset, rem, "<day>%d</day>", tm.tm_mday);
+               SNPRINTF_FAILURE(size, rem, offset, len);
+
+               size = snprintf(buf + offset, rem, "<month>%d</month>",
+                               tm.tm_mon + 1);
+               SNPRINTF_FAILURE(size, rem, offset, len);
+
+               size = snprintf(buf + offset, rem, "<year>%d</year>",
+                               1900 + tm.tm_year);
+               SNPRINTF_FAILURE(size, rem, offset, len);
+
+               size = snprintf(buf + offset, rem, "</when>");
+               SNPRINTF_FAILURE(size, rem, offset, len);
+       }
+
+       ph = nfq_get_msg_packet_hdr(tb);
+       if (ph) {
+               size = snprintf(buf + offset, rem,
+                               "<hook>%u</hook><id>%u</id>",
+                               ph->hook, ntohl(ph->packet_id));
+               SNPRINTF_FAILURE(size, rem, offset, len);
+
+               hwph = nfq_get_packet_hw(tb);
+               if (hwph && (flags & NFQ_XML_HW)) {
+                       int i, hlen = ntohs(hwph->hw_addrlen);
+
+                       size = snprintf(buf + offset, rem, "<hw><proto>%04x"
+                                                          "</proto>",
+                                       ntohs(ph->hw_protocol));
+                       SNPRINTF_FAILURE(size, rem, offset, len);
+
+                       size = snprintf(buf + offset, rem, "<src>");
+                       SNPRINTF_FAILURE(size, rem, offset, len);
+
+                       for (i=0; i<hlen; i++) {
+                               size = snprintf(buf + offset, rem, "%02x",
+                                               hwph->hw_addr[i]);
+                               SNPRINTF_FAILURE(size, rem, offset, len);
+                       }
+
+                       size = snprintf(buf + offset, rem, "</src></hw>");
+                       SNPRINTF_FAILURE(size, rem, offset, len);
+               } else if (flags & NFQ_XML_HW) {
+                       size = snprintf(buf + offset, rem, "<hw><proto>%04x"
+                                                   "</proto></hw>",
+                                ntohs(ph->hw_protocol));
+                       SNPRINTF_FAILURE(size, rem, offset, len);
+               }
+       }
+
+       mark = nfq_get_nfmark(tb);
+       if (mark && (flags & NFQ_XML_MARK)) {
+               size = snprintf(buf + offset, rem, "<mark>%u</mark>", mark);
+               SNPRINTF_FAILURE(size, rem, offset, len);
+       }
+
+       ifi = nfq_get_indev(tb);
+       if (ifi && (flags & NFQ_XML_DEV)) {
+               size = snprintf(buf + offset, rem, "<indev>%u</indev>", ifi);
+               SNPRINTF_FAILURE(size, rem, offset, len);
+       }
+
+       ifi = nfq_get_outdev(tb);
+       if (ifi && (flags & NFQ_XML_DEV)) {
+               size = snprintf(buf + offset, rem, "<outdev>%u</outdev>", ifi);
+               SNPRINTF_FAILURE(size, rem, offset, len);
+       }
+
+       ifi = nfq_get_physindev(tb);
+       if (ifi && (flags & NFQ_XML_PHYSDEV)) {
+               size = snprintf(buf + offset, rem,
+                               "<physindev>%u</physindev>", ifi);
+               SNPRINTF_FAILURE(size, rem, offset, len);
+       }
+
+       ifi = nfq_get_physoutdev(tb);
+       if (ifi && (flags & NFQ_XML_PHYSDEV)) {
+               size = snprintf(buf + offset, rem,
+                               "<physoutdev>%u</physoutdev>", ifi);
+               SNPRINTF_FAILURE(size, rem, offset, len);
+       }
+
+       ret = nfq_get_payload(tb, &data);
+       if (ret >= 0 && (flags & NFQ_XML_PAYLOAD)) {
+               int i;
+
+               size = snprintf(buf + offset, rem, "<payload>");
+               SNPRINTF_FAILURE(size, rem, offset, len);
+
+               for (i=0; i<ret; i++) {
+                       size = snprintf(buf + offset, rem, "%02x",
+                                       data[i] & 0xff);
+                       SNPRINTF_FAILURE(size, rem, offset, len);
+               }
+
+               size = snprintf(buf + offset, rem, "</payload>");
+               SNPRINTF_FAILURE(size, rem, offset, len);
+       }
+
+       size = snprintf(buf + offset, rem, "</pkt>");
+       SNPRINTF_FAILURE(size, rem, offset, len);
+
+       return len;
+}
+
+/**
+ * @}
+ */