diff options
-rw-r--r-- | doxygen/Makefile.am | 4 | ||||
-rwxr-xr-x | doxygen/build_man.sh | 103 | ||||
-rw-r--r-- | doxygen/doxygen.cfg.in | 1 | ||||
-rw-r--r-- | examples/nf-queue.c | 34 | ||||
-rw-r--r-- | include/libnetfilter_queue/libnetfilter_queue.h | 9 | ||||
-rw-r--r-- | include/libnetfilter_queue/pktbuff.h | 5 | ||||
-rw-r--r-- | src/extra/ipv6.c | 6 | ||||
-rw-r--r-- | src/extra/pktbuff.c | 63 | ||||
-rw-r--r-- | src/libnetfilter_queue.c | 26 | ||||
-rw-r--r-- | src/nlmsg.c | 58 |
10 files changed, 281 insertions, 28 deletions
diff --git a/doxygen/Makefile.am b/doxygen/Makefile.am index c6eeed7..68be963 100644 --- a/doxygen/Makefile.am +++ b/doxygen/Makefile.am @@ -10,12 +10,12 @@ doc_srcs = $(top_srcdir)/src/libnetfilter_queue.c\ $(top_srcdir)/src/extra/udp.c\ $(top_srcdir)/src/extra/icmp.c -doxyfile.stamp: $(doc_srcs) Makefile +doxyfile.stamp: $(doc_srcs) Makefile build_man.sh rm -rf html man doxygen doxygen.cfg >/dev/null if BUILD_MAN - $(abs_top_srcdir)/doxygen/build_man.sh + $(abs_top_srcdir)/doxygen/build_man.sh libnetfilter_queue libnetfilter_queue.c endif touch doxyfile.stamp diff --git a/doxygen/build_man.sh b/doxygen/build_man.sh index 0d3be4c..7eab8fa 100755 --- a/doxygen/build_man.sh +++ b/doxygen/build_man.sh @@ -1,19 +1,20 @@ #!/bin/sh -[ -n "$BASH" ] || exec bash -p $0 +[ -n "$BASH" ] || exec bash -p $0 $@ # Script to process man pages output by doxygen. # We need to use bash for its associative array facility. # (`bash -p` prevents import of functions from the environment). +# Args: none or 2 being man7 page name & relative path of source with \mainpage declare -A renamed_page main(){ set -e - cd man/man3; rm -f _* + pushd man/man3 >/dev/null; rm -f _* count_real_pages rename_real_pages make_symlinks - post_process + post_process $@ } count_real_pages(){ @@ -76,9 +77,103 @@ post_process(){ done + [ $# -ne 2 ] || make_man7 $@ + remove_temp_files } +make_man7(){ + popd >/dev/null + target=$(grep -Ew INPUT doxygen.cfg | rev | cut -f1 -d' ' | rev)/$2 + mypath=$(dirname $0) + + # Build up temporary source in temp.c + # (doxygen only makes man pages from .c files). + mygrep \\\\mainpage $target + tail -n+$((linnum-1)) $target | head -n1 >temp.c + echo " * \\defgroup $1 $1 overview" >>temp.c + tail -n+$((linnum+1)) $target >$fileA + linnum=$(grep -En '\*/' $fileA | head -n1 | cut -d: -f1) + head -n$((linnum - 1)) $fileA >> temp.c + + echo ' */' >> temp.c + cat >> temp.c <<//// + + /** + * @{ + * + * $1 - DELETE_ME + */ +int $1(void) +{ + return 0; +} +/** + * @} + */ +//// + + # Create temporary doxygen config in fileC + cat /dev/null >$fileC + for i in \ + PROJECT_NAME \ + PROJECT_NUMBER \ + ABBREVIATE_BRIEF \ + FULL_PATH_NAMES \ + TAB_SIZE \ + OPTIMIZE_OUTPUT_FOR_C \ + EXAMPLE_PATTERNS \ + ALPHABETICAL_INDEX \ + SEARCHENGINE \ + GENERATE_LATEX \ + ; do grep -Ew $i doxygen.cfg >>$fileC; done + cat >>$fileC <<//// +INPUT = temp.c +GENERATE_HTML = NO +GENERATE_MAN = YES +MAN_EXTENSION = .7 +//// + + doxygen $fileC >/dev/null + + # Remove SYNOPSIS line if there is one + target=man/man7/$1.7 + mygrep "SH SYNOPSIS" $target + [ $linnum -eq 0 ] || delete_lines $linnum $((linnum+1)) + + # doxygen 1.8.9.1 and possibly newer run the first para into NAME + # (i.e. in this unusual group). There won't be a SYNOPSIS when this happens + if grep -Eq "overview$1" $target; then + head -n2 temp.c >$fileA + cat >>$fileA <<//// + * \\manonly +.PP +.SH "Detailed Description" +.PP +\\endmanonly +//// + tail -n+3 temp.c >>$fileA + cat $fileA >temp.c + doxygen $fileC >/dev/null + fi + + # Insert top-level "See also" of man7 page in all real man3 pages + for target in $(find man/man3 -type f) + do mygrep "Detailed Description" $target + [ $linnum -ne 0 ] || mygrep "Function Documentation" $target + [ $linnum -ne 0 ] || { echo "NO HEADER IN $target" >&2; continue; } + head -n$((linnum-1)) $target >$fileA + cat >>$fileA <<//// +.SH "See also" +\\fB${1}\\fP(7) +//// + tail -n+$linnum $target >>$fileA + cp $fileA $target + done + + rm temp.c +} + fix_double_blanks(){ linnum=1 # @@ -225,4 +320,4 @@ remove_temp_files(){ done } -main +main $@ diff --git a/doxygen/doxygen.cfg.in b/doxygen/doxygen.cfg.in index 14bd0cf..97174ff 100644 --- a/doxygen/doxygen.cfg.in +++ b/doxygen/doxygen.cfg.in @@ -25,4 +25,3 @@ GENERATE_MAN = @GEN_MAN@ GENERATE_HTML = @GEN_HTML@ MAN_LINKS = YES HAVE_DOT = @HAVE_DOT@ -DOT_TRANSPARENT = YES diff --git a/examples/nf-queue.c b/examples/nf-queue.c index 4466ca8..1ae52e4 100644 --- a/examples/nf-queue.c +++ b/examples/nf-queue.c @@ -54,6 +54,9 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data) struct nfgenmsg *nfg; uint16_t plen; + /* Parse netlink message received from the kernel, the array of + * attributes is set up to store metadata and the actual packet. + */ if (nfq_nlmsg_parse(nlh, attr) < 0) { perror("problems parsing"); return MNL_CB_ERROR; @@ -66,13 +69,30 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data) return MNL_CB_ERROR; } + /* Access packet metadata, which provides unique packet ID, hook number + * and ethertype. See struct nfqnl_msg_packet_hdr for details. + */ ph = mnl_attr_get_payload(attr[NFQA_PACKET_HDR]); + /* Access actual packet data length. */ plen = mnl_attr_get_payload_len(attr[NFQA_PAYLOAD]); + + /* Access actual packet data */ /* void *payload = mnl_attr_get_payload(attr[NFQA_PAYLOAD]); */ + /* Fetch metadata flags, possible flags values are: + * + * - NFQA_SKB_CSUMNOTREADY: + * Kernel performed partial checksum validation, see CHECKSUM_PARTIAL. + * - NFQA_SKB_CSUM_NOTVERIFIED: + * Kernel already verified checksum. + * - NFQA_SKB_GSO: + * Not the original packet received from the wire. Kernel has + * aggregated several packets into one single packet via GSO. + */ skbinfo = attr[NFQA_SKB_INFO] ? ntohl(mnl_attr_get_u32(attr[NFQA_SKB_INFO])) : 0; + /* Kernel has truncated the packet, fetch original packet length. */ if (attr[NFQA_CAP_LEN]) { uint32_t orig_len = ntohl(mnl_attr_get_u32(attr[NFQA_CAP_LEN])); if (orig_len != plen) @@ -86,6 +106,7 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data) printf("packet received (id=%u hw=0x%04x hook=%u, payload len %u", id, ntohs(ph->hw_protocol), ph->hook, plen); + /* Fetch ethernet destination address. */ if (attr[NFQA_HWADDR]) { struct nfqnl_msg_packet_hw *hw = mnl_attr_get_payload(attr[NFQA_HWADDR]); unsigned int hwlen = ntohs(hw->hw_addrlen); @@ -135,6 +156,9 @@ int main(int argc, char *argv[]) } queue_num = atoi(argv[1]); + /* + * Set up netlink socket to communicate with the netfilter subsystem. + */ nl = mnl_socket_open(NETLINK_NETFILTER); if (nl == NULL) { perror("mnl_socket_open"); @@ -153,6 +177,10 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } + /* Configure the pipeline between kernel and userspace, build and send + * a netlink message to specify queue number to bind to. Your ruleset + * has to use this queue number to deliver packets to userspace. + */ nlh = nfq_nlmsg_put(buf, NFQNL_MSG_CONFIG, queue_num); nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, NFQNL_CFG_CMD_BIND); @@ -161,6 +189,9 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } + /* Build and send a netlink message to specify how many bytes are + * copied from kernel to userspace for this queue. + */ nlh = nfq_nlmsg_put(buf, NFQNL_MSG_CONFIG, queue_num); nfq_nlmsg_cfg_put_params(nlh, NFQNL_COPY_PACKET, 0xffff); @@ -179,6 +210,9 @@ int main(int argc, char *argv[]) ret = 1; mnl_socket_setsockopt(nl, NETLINK_NO_ENOBUFS, &ret, sizeof(int)); + /* Loop forever on packets received from the kernel and run the + * callback handler. + */ for (;;) { ret = mnl_socket_recvfrom(nl, buf, sizeof_buf); if (ret == -1) { diff --git a/include/libnetfilter_queue/libnetfilter_queue.h b/include/libnetfilter_queue/libnetfilter_queue.h index a19122f..f7e68d8 100644 --- a/include/libnetfilter_queue/libnetfilter_queue.h +++ b/include/libnetfilter_queue/libnetfilter_queue.h @@ -3,9 +3,9 @@ * (C) 2005 by Harald Welte <laforge@gnumonks.org> * * - * Changelog : + * Changelog : * (2005/08/11) added parsing function (Eric Leblond <regit@inl.fr>) - * + * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. */ @@ -82,7 +82,7 @@ extern int nfq_set_verdict_batch2(struct nfq_q_handle *qh, uint32_t mark); extern __attribute__((deprecated)) -int nfq_set_verdict_mark(struct nfq_q_handle *qh, +int nfq_set_verdict_mark(struct nfq_q_handle *qh, uint32_t id, uint32_t verdict, uint32_t mark, @@ -111,7 +111,7 @@ extern int nfq_get_secctx(struct nfq_data *nfad, unsigned char **secdata); extern int nfq_get_indev_name(struct nlif_handle *nlif_handle, struct nfq_data *nfad, char *name); extern int nfq_get_physindev_name(struct nlif_handle *nlif_handle, - struct nfq_data *nfad, char *name); + struct nfq_data *nfad, char *name); extern int nfq_get_outdev_name(struct nlif_handle *nlif_handle, struct nfq_data *nfad, char *name); extern int nfq_get_physoutdev_name(struct nlif_handle *nlif_handle, @@ -151,6 +151,7 @@ void nfq_nlmsg_verdict_put_pkt(struct nlmsghdr *nlh, const void *pkt, uint32_t p int nfq_nlmsg_parse(const struct nlmsghdr *nlh, struct nlattr **attr); struct nlmsghdr *nfq_nlmsg_put(char *buf, int type, uint32_t queue_num); +struct nlmsghdr *nfq_nlmsg_put2(char *buf, int type, uint32_t queue_num, uint16_t flags); #ifdef __cplusplus } /* extern "C" */ diff --git a/include/libnetfilter_queue/pktbuff.h b/include/libnetfilter_queue/pktbuff.h index 42bc153..d3588c7 100644 --- a/include/libnetfilter_queue/pktbuff.h +++ b/include/libnetfilter_queue/pktbuff.h @@ -1,11 +1,16 @@ #ifndef _PKTBUFF_H_ #define _PKTBUFF_H_ +#include <stdbool.h> + struct pkt_buff; struct pkt_buff *pktb_alloc(int family, void *data, size_t len, size_t extra); void pktb_free(struct pkt_buff *pktb); +struct pkt_buff *pktb_setup_raw(void *pktb, int family, void *data, size_t len, size_t extra); +size_t pktb_head_size(void); + uint8_t *pktb_data(struct pkt_buff *pktb); uint32_t pktb_len(struct pkt_buff *pktb); diff --git a/src/extra/ipv6.c b/src/extra/ipv6.c index 69d86a8..fd8ebc4 100644 --- a/src/extra/ipv6.c +++ b/src/extra/ipv6.c @@ -113,11 +113,11 @@ int nfq_ip6_set_transport_header(struct pkt_buff *pktb, struct ip6_hdr *ip6h, break; } - frag_off = (uint16_t *)cur + - offsetof(struct ip6_frag, ip6f_offlg); + frag_off = (uint16_t *)(cur + + offsetof(struct ip6_frag, ip6f_offlg)); /* Fragment offset is only 13 bits long. */ - if (htons(*frag_off & ~0x7)) { + if (htons(*frag_off) & ~0x7) { /* Not the first fragment, it does not contain * any headers. */ diff --git a/src/extra/pktbuff.c b/src/extra/pktbuff.c index 005172c..40d2250 100644 --- a/src/extra/pktbuff.c +++ b/src/extra/pktbuff.c @@ -67,6 +67,14 @@ static int __pktb_setup(int family, struct pkt_buff *pktb) return 0; } +static void pktb_setup_metadata(struct pkt_buff *pktb, void *pkt_data, + size_t len, size_t extra) +{ + pktb->len = len; + pktb->data_len = len + extra; + pktb->data = pkt_data; +} + /** * pktb_alloc - allocate a new packet buffer * \param family Indicate what family. Currently supported families are @@ -76,7 +84,12 @@ static int __pktb_setup(int family, struct pkt_buff *pktb) * \param extra Extra memory in the tail to be allocated (for mangling) * * This function returns a packet buffer that contains the packet data and - * some extra memory room in the tail (if requested). + * some extra memory room in the tail (if requested). This function copies + * the memory area provided as a pointer to packet data into the packet buffer + * structure. + * + * The extra length provides extra packet data room at the tail of the packet + * buffer in case you need to mangle it. * * \return Pointer to a new userspace packet buffer or NULL on failure. * \par Errors @@ -100,10 +113,7 @@ struct pkt_buff *pktb_alloc(int family, void *data, size_t len, size_t extra) pkt_data = (uint8_t *)pktb + sizeof(struct pkt_buff); memcpy(pkt_data, data, len); - pktb->len = len; - pktb->data_len = len + extra; - - pktb->data = pkt_data; + pktb_setup_metadata(pktb, pkt_data, len, extra); if (__pktb_setup(family, pktb) < 0) { free(pktb); @@ -114,6 +124,37 @@ struct pkt_buff *pktb_alloc(int family, void *data, size_t len, size_t extra) } /** + * pktb_setup_raw - set up a packet buffer from memory area + * \param pktb Pointer to memory of length pktb_head_size() bytes + * \param family Supported families are AF_BRIDGE, AF_INET & AF_INET6. + * \param data Pointer to packet data + * \param len Packet data length + * \param extra Extra memory available after packet data (for mangling). + * + * Use this function to set up a packet buffer from a memory area, minimum size + * of such memory area must be pktb_head_size(). This function attaches the + * packet data that is provided to the packet buffer (data is not copied). Use + * this function as an alternative to the pktb_alloc() interface for more + * control on memory management. + * + * \return Pointer to a new userspace packet buffer or NULL on failure. + * \par Errors + * __EPROTONOSUPPORT__ _family_ was __AF_BRIDGE__ and this is not an IP packet + * (v4 or v6) + */ +EXPORT_SYMBOL +struct pkt_buff *pktb_setup_raw(void *pktb, int family, void *data, + size_t len, size_t extra) +{ + memset(pktb, 0, sizeof (struct pkt_buff)); + pktb_setup_metadata(pktb, data, len, extra); + if (__pktb_setup(family, pktb) < 0) + pktb = NULL; + + return pktb; +} + +/** * pktb_data - get pointer to network packet * \param pktb Pointer to userspace packet buffer * \return Pointer to start of network packet data within __pktb__ @@ -398,5 +439,17 @@ bool pktb_mangled(const struct pkt_buff *pktb) } /** + * pktb_head_size - get number of bytes needed for a packet buffer + * (control part only) + * \return size of struct pkt_buff + */ + +EXPORT_SYMBOL +size_t pktb_head_size(void) +{ + return sizeof(struct pkt_buff); +} + +/** * @} */ diff --git a/src/libnetfilter_queue.c b/src/libnetfilter_queue.c index a170143..bf67a19 100644 --- a/src/libnetfilter_queue.c +++ b/src/libnetfilter_queue.c @@ -45,11 +45,11 @@ * libnetfilter_queue homepage is: * https://netfilter.org/projects/libnetfilter_queue/ * - * \section deps Dependencies + <h1>Dependencies</h1> * libnetfilter_queue requires libmnl, libnfnetlink and a kernel that includes * the Netfilter NFQUEUE over NFNETLINK interface (i.e. 2.6.14 or later). * - * \section features Main Features + * <h1>Main Features</h1> * - receiving queued packets from the kernel nfnetlink_queue subsystem * - issuing verdicts and possibly reinjecting altered packets to the kernel * nfnetlink_queue subsystem @@ -71,15 +71,15 @@ * When a queue is full, packets that should have been enqueued are dropped by * kernel instead of being enqueued. * - * \section git Git Tree + * <h1>Git Tree</h1> * The current development version of libnetfilter_queue can be accessed at * https://git.netfilter.org/libnetfilter_queue. * - * \section privs Privileges + * <h1>Privileges</h1> * You need the CAP_NET_ADMIN capability in order to allow your application * to receive from and to send packets to kernel-space. * - * \section using Using libnetfilter_queue + * <h1>Using libnetfilter_queue</h1> * * To write your own program using libnetfilter_queue, you should start by * reading (or, if feasible, compiling and stepping through with *gdb*) @@ -88,7 +88,14 @@ * \verbatim gcc -g3 -ggdb -Wall -lmnl -lnetfilter_queue -o nf-queue nf-queue.c \endverbatim - * The doxygen documentation \link LibrarySetup \endlink is Deprecated and + *The doxygen documentation + * \htmlonly +<a class="el" href="group__LibrarySetup.html">LibrarySetup </a> +\endhtmlonly + * \manonly +\fBLibrarySetup\fP\ +\endmanonly + * is Deprecated and * incompatible with non-deprecated functions. It is hoped to produce a * corresponding non-deprecated (*Current*) topic soon. * @@ -97,7 +104,7 @@ gcc -g3 -ggdb -Wall -lmnl -lnetfilter_queue -o nf-queue nf-queue.c * article: * https://home.regit.org/netfilter-en/using-nfqueue-and-libnetfilter_queue/ * - * \section errors ENOBUFS errors in recv() + * <h1>ENOBUFS errors in recv()</h1> * * recv() may return -1 and errno is set to ENOBUFS in case that your * application is not fast enough to retrieve the packets from the kernel. @@ -106,7 +113,7 @@ gcc -g3 -ggdb -Wall -lmnl -lnetfilter_queue -o nf-queue nf-queue.c * you may hit it again sooner or later. The next section provides some hints * on how to obtain the best performance for your application. * - * \section perf Performance + * <h1>Performance</h1> * To improve your libnetfilter_queue application in terms of performance, * you may consider the following tweaks: * @@ -120,6 +127,9 @@ gcc -g3 -ggdb -Wall -lmnl -lnetfilter_queue -o nf-queue nf-queue.c * (it requires Linux kernel >= 2.6.31). * - consider using fail-open option see nfq_set_queue_flags() (it requires * Linux kernel >= 3.6) + * - make your application offload aware to avoid costly normalization on kernel + * side. See NFQA_CFG_F_GSO flag to nfq_set_queue_flags(). + * Linux kernel >= 3.10. * - increase queue max length with nfq_set_queue_maxlen() to resist to packets * burst */ diff --git a/src/nlmsg.c b/src/nlmsg.c index 5400dd7..39fd12d 100644 --- a/src/nlmsg.c +++ b/src/nlmsg.c @@ -310,9 +310,65 @@ int nfq_nlmsg_parse(const struct nlmsghdr *nlh, struct nlattr **attr) EXPORT_SYMBOL struct nlmsghdr *nfq_nlmsg_put(char *buf, int type, uint32_t queue_num) { + return nfq_nlmsg_put2(buf, type, queue_num, 0); +} + +/** + * nfq_nlmsg_put2 - Set up a netlink header with user-specified flags + * in a memory buffer + * \param *buf Pointer to memory buffer + * \param type One of NFQNL_MSG_CONFIG, NFQNL_MSG_VERDICT + * or NFQNL_MSG_VERDICT_BATCH + * \param queue_num Queue number + * \param flags additional NLM_F_xxx flags to put in message header. These are + * defined in /usr/include/linux/netlink.h. nfq_nlmsg_put2() always + * sets NLM_F_REQUEST + * \returns Pointer to netlink header + * + * For most applications, the only sensible flag will be NLM_F_ACK. + * Use it to get an explicit acknowledgment from the kernel, e.g. + * attempt to configure NFQA_CFG_F_SECCTX on a kernel not supporting + * CONFIG_NETWORK_SECMARK. + * \n + * The kernel always sends a message in response to a failed command. + * NLM_F_ACK instructs the kernel to also send a message in response + * to a successful command. + * \n + * This code snippet demonstrates reading these responses: + * \verbatim + char buf[MNL_SOCKET_BUFFER_SIZE]; + + nlh = nfq_nlmsg_put2(buf, NFQNL_MSG_CONFIG, queue_num, + NLM_F_ACK); + mnl_attr_put_u32(nlh, NFQA_CFG_FLAGS, NFQA_CFG_F_SECCTX); + mnl_attr_put_u32(nlh, NFQA_CFG_MASK, NFQA_CFG_F_SECCTX); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_send"); + exit(EXIT_FAILURE); + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof buf); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + exit(EXIT_FAILURE); + } + + ret = mnl_cb_run(buf, ret, 0, portid, NULL, NULL); + if (ret == -1) + fprintf(stderr, "This kernel version does not allow to " + "retrieve security context.\n"); +\endverbatim + * + */ + +EXPORT_SYMBOL +struct nlmsghdr *nfq_nlmsg_put2(char *buf, int type, uint32_t queue_num, + uint16_t flags) +{ struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | type; - nlh->nlmsg_flags = NLM_F_REQUEST; + nlh->nlmsg_flags = NLM_F_REQUEST | flags; struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); nfg->nfgen_family = AF_UNSPEC; |