diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 5 | ||||
-rw-r--r-- | src/extra/checksum.c | 19 | ||||
-rw-r--r-- | src/extra/icmp.c | 57 | ||||
-rw-r--r-- | src/extra/ipv4.c | 77 | ||||
-rw-r--r-- | src/extra/ipv6.c | 68 | ||||
-rw-r--r-- | src/extra/pktbuff.c | 181 | ||||
-rw-r--r-- | src/extra/tcp.c | 129 | ||||
-rw-r--r-- | src/extra/udp.c | 132 | ||||
-rw-r--r-- | src/internal.h | 6 | ||||
-rw-r--r-- | src/libnetfilter_queue.c | 160 | ||||
-rw-r--r-- | src/nlmsg.c | 153 |
11 files changed, 797 insertions, 190 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 9fdccfb..079853e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,7 +18,7 @@ # set age to 0. # </snippet> # -LIBVERSION=5:0:4 +LIBVERSION=6:0:5 include ${top_srcdir}/Make_global.am @@ -26,11 +26,12 @@ lib_LTLIBRARIES = libnetfilter_queue.la noinst_HEADERS = internal.h -libnetfilter_queue_la_LDFLAGS = -Wc,-nostartfiles -lnfnetlink \ +libnetfilter_queue_la_LDFLAGS = -Wc,-nostartfiles \ -version-info $(LIBVERSION) libnetfilter_queue_la_SOURCES = libnetfilter_queue.c \ nlmsg.c \ extra/checksum.c \ + extra/icmp.c \ extra/ipv6.c \ extra/tcp.c \ extra/ipv4.c \ diff --git a/src/extra/checksum.c b/src/extra/checksum.c index 42389aa..33480af 100644 --- a/src/extra/checksum.c +++ b/src/extra/checksum.c @@ -17,6 +17,7 @@ #include <netinet/ip6.h> #include <netinet/tcp.h> +#include <linux/netfilter/nfnetlink_queue.h> #include <libnetfilter_queue/libnetfilter_queue.h> #include "internal.h" @@ -62,25 +63,21 @@ uint16_t nfq_checksum_tcpudp_ipv6(struct ip6_hdr *ip6h, void *transport_hdr, uint16_t protonum) { uint32_t sum = 0; - uint32_t hdr_len = (uint32_t *)transport_hdr - (uint32_t *)ip6h; - uint32_t len = ip6h->ip6_plen - hdr_len; + uint32_t hdr_len = (uint8_t *)transport_hdr - (uint8_t *)ip6h; + /* Allow for extra headers before the UDP header */ + /* TODO: Deal with routing headers */ + uint32_t len = ntohs(ip6h->ip6_plen) - (hdr_len - sizeof *ip6h); uint8_t *payload = (uint8_t *)ip6h + hdr_len; int i; for (i=0; i<8; i++) { - sum += (ip6h->ip6_src.s6_addr16[i] >> 16) & 0xFFFF; - sum += (ip6h->ip6_src.s6_addr16[i]) & 0xFFFF; + sum += (ip6h->ip6_src.s6_addr16[i]); } for (i=0; i<8; i++) { - sum += (ip6h->ip6_dst.s6_addr16[i] >> 16) & 0xFFFF; - sum += (ip6h->ip6_dst.s6_addr16[i]) & 0xFFFF; + sum += (ip6h->ip6_dst.s6_addr16[i]); } sum += htons(protonum); - sum += htons(ip6h->ip6_plen); + sum += htons(len); return nfq_checksum(sum, (uint16_t *)payload, len); } - -/** - * @} - */ diff --git a/src/extra/icmp.c b/src/extra/icmp.c new file mode 100644 index 0000000..eaade7b --- /dev/null +++ b/src/extra/icmp.c @@ -0,0 +1,57 @@ +/* + * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This code has been sponsored by Vyatta Inc. <http://www.vyatta.com> + */ + +#include <stdio.h> +#define _GNU_SOURCE +#include <netinet/ip_icmp.h> + +#include <libnetfilter_queue/libnetfilter_queue_icmp.h> + +#include "internal.h" + +/** + * \defgroup icmp ICMP helper functions + * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <libmnl/libmnl.h> +#include <libnetfilter_queue/libnetfilter_queue_icmp.h> +\endmanonly + * + * @{ + */ + +/** + * nfq_icmp_get_hdr - get the ICMP header. + * \param pktb: pointer to user-space network packet buffer + * \returns validated pointer to the ICMP header or NULL if the ICMP header was + * not set or if a minimal length check fails. + * \note You have to call nfq_ip_set_transport_header() or + * nfq_ip6_set_transport_header() first to set the ICMP header. + */ +EXPORT_SYMBOL +struct icmphdr *nfq_icmp_get_hdr(struct pkt_buff *pktb) +{ + if (pktb->transport_header == NULL) + return NULL; + + /* No room for the ICMP header. */ + if (pktb_tail(pktb) - pktb->transport_header < sizeof(struct icmphdr)) + return NULL; + + return (struct icmphdr *)pktb->transport_header; +} + +/** + * @} + */ diff --git a/src/extra/ipv4.c b/src/extra/ipv4.c index c03f23f..58fb471 100644 --- a/src/extra/ipv4.c +++ b/src/extra/ipv4.c @@ -14,6 +14,7 @@ #include <arpa/inet.h> #include <netinet/ip.h> +#include <linux/netfilter/nfnetlink_queue.h> #include <libnetfilter_queue/libnetfilter_queue.h> #include <libnetfilter_queue/libnetfilter_queue_ipv4.h> #include <libnetfilter_queue/pktbuff.h> @@ -22,21 +23,34 @@ /** * \defgroup ipv4 IPv4 helper functions + * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <libmnl/libmnl.h> +#include <libnetfilter_queue/libnetfilter_queue_ipv4.h> +\endmanonly + * * @{ */ /** - * nfq_ip_get_hdr - get IPv4 header + * nfq_ip_get_hdr - get the IPv4 header * \param pktb: Pointer to user-space network packet buffer + * \returns validated pointer to the IPv4 header or NULL if IP is malformed or + * not version 4 * - * This funcion returns NULL if the IPv4 is malformed or the protocol version - * is not 4. On success, it returns a valid pointer to the IPv4 header. + * Many programs will not need to call this function. A possible use is to + * determine the layer 4 protocol. The validation is that the buffer is big + * enough for the declared lengths in the header, i.e. an extra check for packet + * truncation. */ EXPORT_SYMBOL struct iphdr *nfq_ip_get_hdr(struct pkt_buff *pktb) { struct iphdr *iph; - unsigned int pktlen = pktb->tail - pktb->network_header; + unsigned int pktlen = pktb_tail(pktb) - pktb->network_header; /* Not enough room for IPv4 header. */ if (pktlen < sizeof(struct iphdr)) @@ -56,13 +70,14 @@ struct iphdr *nfq_ip_get_hdr(struct pkt_buff *pktb) } /** - * nfq_ip_set_transport_header - set transport header + * nfq_ip_set_transport_header - set the \b transport_header field in \b pktb * \param pktb: Pointer to user-space network packet buffer * \param iph: Pointer to the IPv4 header - * - * Sets the \b transport_header field in \b pktb - * - * Level 4 helper functions need this to be set. + * \returns 0 on success or -1 if a minimal validation check fails + * \note + * Most programs should call __nfq_ip_set_transport_header__ as soon as + * possible, since most layer 4 helper functions assume the + * \b transport_header field is valid. */ EXPORT_SYMBOL int nfq_ip_set_transport_header(struct pkt_buff *pktb, struct iphdr *iph) @@ -78,11 +93,29 @@ int nfq_ip_set_transport_header(struct pkt_buff *pktb, struct iphdr *iph) } /** + * \defgroup ip_internals Internal IP functions + * + * Most user-space programs will never need these. + * + * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <libmnl/libmnl.h> +#include <libnetfilter_queue/libnetfilter_queue_ipv4.h> +\endmanonly + * + * @{ + */ + +/** * nfq_ip_set_checksum - set IPv4 checksum * \param iph: Pointer to the IPv4 header - * - * \note Call to this function if you modified the IPv4 header to update the - * checksum. + * \note + * nfq_ip_mangle() invokes this function. + * As long as developers always use the appropriate mangler for the layer being + * mangled, there is no need to call __nfq_ip_set_checksum__. */ EXPORT_SYMBOL void nfq_ip_set_checksum(struct iphdr *iph) @@ -94,16 +127,20 @@ void nfq_ip_set_checksum(struct iphdr *iph) } /** + * @} + */ + +/** * nfq_ip_mangle - mangle IPv4 packet buffer * \param pktb: Pointer to user-space network packet buffer - * \param dataoff: Offset to layer 4 header + * \param dataoff: Offset to layer 4 header, or zero to mangle IP header * \param match_offset: Offset to content that you want to mangle * \param match_len: Length of the existing content you want to mangle * \param rep_buffer: Pointer to data you want to use to replace current content * \param rep_len: Length of data you want to use to replace current content * \returns 1 for success and 0 for failure. See pktb_mangle() for failure case - * \note This function updates the IPv4 length and recalculates the IPv4 - * checksum (if necessary) + * \note This function updates the IPv4 length if necessary and recalculates the + * IPv4 checksum. */ EXPORT_SYMBOL int nfq_ip_mangle(struct pkt_buff *pktb, unsigned int dataoff, @@ -117,21 +154,19 @@ int nfq_ip_mangle(struct pkt_buff *pktb, unsigned int dataoff, return 0; /* fix IP hdr checksum information */ - iph->tot_len = htons(pktb->tail - pktb->network_header); + iph->tot_len = htons(pktb_tail(pktb) - pktb->network_header); nfq_ip_set_checksum(iph); return 1; } /** - * nfq_pkt_snprintf_ip - print IPv4 header into buffer in iptables LOG format + * nfq_ip_snprintf - print IPv4 header into buffer in iptables LOG format * \param buf: Pointer to buffer that will be used to print the header * \param size: Size of the buffer (or remaining room in it) * \param iph: Pointer to a valid IPv4 header - * - * This function returns the number of bytes written (excluding the - * string-terminating NUL) *assuming sufficient room in the buffer*. - * Read the snprintf manpage for more information about this strange behaviour. + * \returns same as snprintf + * \sa **snprintf**(3) */ EXPORT_SYMBOL int nfq_ip_snprintf(char *buf, size_t size, const struct iphdr *iph) diff --git a/src/extra/ipv6.c b/src/extra/ipv6.c index f685b3b..fd8ebc4 100644 --- a/src/extra/ipv6.c +++ b/src/extra/ipv6.c @@ -15,6 +15,7 @@ #include <arpa/inet.h> #include <netinet/ip6.h> +#include <linux/netfilter/nfnetlink_queue.h> #include <libnetfilter_queue/libnetfilter_queue.h> #include <libnetfilter_queue/libnetfilter_queue_ipv6.h> #include <libnetfilter_queue/pktbuff.h> @@ -23,6 +24,17 @@ /** * \defgroup ipv6 IPv6 helper functions + * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <arpa/inet.h> +#include <linux/netfilter/nfnetlink_queue.h> +#include <libnetfilter_queue/libnetfilter_queue.h> +#include <libnetfilter_queue/libnetfilter_queue_ipv6.h> +\endmanonly + * * @{ */ @@ -36,7 +48,7 @@ EXPORT_SYMBOL struct ip6_hdr *nfq_ip6_get_hdr(struct pkt_buff *pktb) { struct ip6_hdr *ip6h; - unsigned int pktlen = pktb->tail - pktb->network_header; + unsigned int pktlen = pktb_tail(pktb) - pktb->network_header; /* Not enough room for IPv6 header. */ if (pktlen < sizeof(struct ip6_hdr)) @@ -67,17 +79,26 @@ int nfq_ip6_set_transport_header(struct pkt_buff *pktb, struct ip6_hdr *ip6h, uint8_t nexthdr = ip6h->ip6_nxt; uint8_t *cur = (uint8_t *)ip6h + sizeof(struct ip6_hdr); - while (nexthdr != target) { + while (nexthdr == IPPROTO_HOPOPTS || + nexthdr == IPPROTO_ROUTING || + nexthdr == IPPROTO_FRAGMENT || + nexthdr == IPPROTO_AH || + nexthdr == IPPROTO_NONE || + nexthdr == IPPROTO_DSTOPTS) { struct ip6_ext *ip6_ext; uint32_t hdrlen; + /* Extension header was requested, we're done. */ + if (nexthdr == target) + break; + /* No more extensions, we're done. */ if (nexthdr == IPPROTO_NONE) { cur = NULL; break; } /* No room for extension, bad packet. */ - if (pktb->tail - cur < sizeof(struct ip6_ext)) { + if (pktb_tail(pktb) - cur < sizeof(struct ip6_ext)) { cur = NULL; break; } @@ -87,16 +108,16 @@ int nfq_ip6_set_transport_header(struct pkt_buff *pktb, struct ip6_hdr *ip6h, uint16_t *frag_off; /* No room for full fragment header, bad packet. */ - if (pktb->tail - cur < sizeof(struct ip6_frag)) { + if (pktb_tail(pktb) - cur < sizeof(struct ip6_frag)) { cur = NULL; break; } - frag_off = (uint16_t *)cur + - offsetof(struct ip6_frag, ip6f_offlg); + frag_off = (uint16_t *)(cur + + offsetof(struct ip6_frag, ip6f_offlg)); /* Fragment offset is only 13 bits long. */ - if (htons(*frag_off & ~0x7)) { + if (htons(*frag_off) & ~0x7) { /* Not the first fragment, it does not contain * any headers. */ @@ -107,16 +128,47 @@ int nfq_ip6_set_transport_header(struct pkt_buff *pktb, struct ip6_hdr *ip6h, } else if (nexthdr == IPPROTO_AH) hdrlen = (ip6_ext->ip6e_len + 2) << 2; else - hdrlen = ip6_ext->ip6e_len; + hdrlen = (ip6_ext->ip6e_len + 1) << 3; nexthdr = ip6_ext->ip6e_nxt; cur += hdrlen; } + if (nexthdr != target) + cur = NULL; pktb->transport_header = cur; return cur ? 1 : 0; } /** + * nfq_ip6_mangle - mangle IPv6 packet buffer + * \param pktb: Pointer to user-space network packet buffer + * \param dataoff: Offset to layer 4 header + * \param match_offset: Offset to content that you want to mangle + * \param match_len: Length of the existing content you want to mangle + * \param rep_buffer: Pointer to data you want to use to replace current content + * \param rep_len: Length of data you want to use to replace current content + * \returns 1 for success and 0 for failure. See pktb_mangle() for failure case + * \note This function updates the IPv6 length (if necessary) + */ +EXPORT_SYMBOL +int nfq_ip6_mangle(struct pkt_buff *pktb, unsigned int dataoff, + unsigned int match_offset, unsigned int match_len, + const char *rep_buffer, unsigned int rep_len) +{ + struct ip6_hdr *ip6h = (struct ip6_hdr *)pktb->network_header; + + if (!pktb_mangle(pktb, dataoff, match_offset, match_len, rep_buffer, + rep_len)) + return 0; + + /* Fix IPv6 hdr length information */ + ip6h->ip6_plen = + htons(pktb_tail(pktb) - pktb->network_header - sizeof *ip6h); + + return 1; +} + +/** * nfq_ip6_snprintf - print IPv6 header into one buffer in iptables LOG format * \param buf: Pointer to buffer that is used to print the object * \param size: Size of the buffer (or remaining room in it). diff --git a/src/extra/pktbuff.c b/src/extra/pktbuff.c index 26d7ca8..40d2250 100644 --- a/src/extra/pktbuff.c +++ b/src/extra/pktbuff.c @@ -23,12 +23,58 @@ /** * \defgroup pktbuff User-space network packet buffer * - * This library provides the user-space network packet buffer. This abstraction - * is strongly inspired by Linux kernel network buffer, the so-called sk_buff. + * These functions provide the user-space network packet buffer. + * This abstraction is strongly inspired by Linux kernel network buffer, + * the so-called sk_buff. + * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <libmnl/libmnl.h> +#include <libnetfilter_queue/pktbuff.h> +\endmanonly * * @{ */ +static int __pktb_setup(int family, struct pkt_buff *pktb) +{ + struct ethhdr *ethhdr; + + switch (family) { + case AF_INET: + case AF_INET6: + pktb->network_header = pktb->data; + break; + case AF_BRIDGE: + ethhdr = (struct ethhdr *)pktb->data; + pktb->mac_header = pktb->data; + + switch(ethhdr->h_proto) { + case ETH_P_IP: + case ETH_P_IPV6: + pktb->network_header = pktb->data + ETH_HLEN; + break; + default: + /* This protocol is unsupported. */ + errno = EPROTONOSUPPORT; + return -1; + } + break; + } + + return 0; +} + +static void pktb_setup_metadata(struct pkt_buff *pktb, void *pkt_data, + size_t len, size_t extra) +{ + pktb->len = len; + pktb->data_len = len + extra; + pktb->data = pkt_data; +} + /** * pktb_alloc - allocate a new packet buffer * \param family Indicate what family. Currently supported families are @@ -38,7 +84,12 @@ * \param extra Extra memory in the tail to be allocated (for mangling) * * This function returns a packet buffer that contains the packet data and - * some extra memory room in the tail (if requested). + * some extra memory room in the tail (if requested). This function copies + * the memory area provided as a pointer to packet data into the packet buffer + * structure. + * + * The extra length provides extra packet data room at the tail of the packet + * buffer in case you need to mangle it. * * \return Pointer to a new userspace packet buffer or NULL on failure. * \par Errors @@ -62,37 +113,44 @@ struct pkt_buff *pktb_alloc(int family, void *data, size_t len, size_t extra) pkt_data = (uint8_t *)pktb + sizeof(struct pkt_buff); memcpy(pkt_data, data, len); - pktb->len = len; - pktb->data_len = len + extra; + pktb_setup_metadata(pktb, pkt_data, len, extra); - pktb->head = pkt_data; - pktb->data = pkt_data; - pktb->tail = pktb->head + len; + if (__pktb_setup(family, pktb) < 0) { + free(pktb); + return NULL; + } - switch(family) { - case AF_INET: - case AF_INET6: - pktb->network_header = pktb->data; - break; - case AF_BRIDGE: { - struct ethhdr *ethhdr = (struct ethhdr *)pktb->data; + return pktb; +} - pktb->mac_header = pktb->data; +/** + * pktb_setup_raw - set up a packet buffer from memory area + * \param pktb Pointer to memory of length pktb_head_size() bytes + * \param family Supported families are AF_BRIDGE, AF_INET & AF_INET6. + * \param data Pointer to packet data + * \param len Packet data length + * \param extra Extra memory available after packet data (for mangling). + * + * Use this function to set up a packet buffer from a memory area, minimum size + * of such memory area must be pktb_head_size(). This function attaches the + * packet data that is provided to the packet buffer (data is not copied). Use + * this function as an alternative to the pktb_alloc() interface for more + * control on memory management. + * + * \return Pointer to a new userspace packet buffer or NULL on failure. + * \par Errors + * __EPROTONOSUPPORT__ _family_ was __AF_BRIDGE__ and this is not an IP packet + * (v4 or v6) + */ +EXPORT_SYMBOL +struct pkt_buff *pktb_setup_raw(void *pktb, int family, void *data, + size_t len, size_t extra) +{ + memset(pktb, 0, sizeof (struct pkt_buff)); + pktb_setup_metadata(pktb, data, len, extra); + if (__pktb_setup(family, pktb) < 0) + pktb = NULL; - switch(ethhdr->h_proto) { - case ETH_P_IP: - case ETH_P_IPV6: - pktb->network_header = pktb->data + ETH_HLEN; - break; - default: - /* This protocol is unsupported. */ - errno = EPROTONOSUPPORT; - free(pktb); - return NULL; - } - break; - } - } return pktb; } @@ -111,7 +169,7 @@ uint8_t *pktb_data(struct pkt_buff *pktb) } /** - * pktb_len - return length of the packet buffer + * pktb_len - get length of packet buffer * \param pktb Pointer to userspace packet buffer * \return Length of packet contained within __pktb__ * \par @@ -142,21 +200,37 @@ void pktb_free(struct pkt_buff *pktb) * \n * 1. Functions to get values of members of opaque __struct pktbuff__, described * below - * \n + * * 2. Internal functions, described in Module __Internal functions__ * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <libmnl/libmnl.h> +#include <libnetfilter_queue/pktbuff.h> +\endmanonly + * * @{ */ /** - * \defgroup uselessfns Internal functions + * \defgroup do_not_use Internal functions * - * \warning Do not use these functions. Instead, always use the mangle + * Do not use these functions. Instead, always use the mangle * function appropriate to the level at which you are working. * \n * pktb_mangle() uses all the below functions except _pktb_pull_, which is not * used by anything. * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <libmnl/libmnl.h> +#include <libnetfilter_queue/pktbuff.h> +\endmanonly + * * @{ */ @@ -192,7 +266,6 @@ void pktb_pull(struct pkt_buff *pktb, unsigned int len) EXPORT_SYMBOL void pktb_put(struct pkt_buff *pktb, unsigned int len) { - pktb->tail += len; pktb->len += len; } @@ -205,7 +278,6 @@ EXPORT_SYMBOL void pktb_trim(struct pkt_buff *pktb, unsigned int len) { pktb->len = len; - pktb->tail = pktb->head + len; } /** @@ -271,26 +343,25 @@ uint8_t *pktb_transport_header(struct pkt_buff *pktb) * @} */ -static int pktb_expand_tail(struct pkt_buff *pkt, int extra) +static int pktb_expand_tail(struct pkt_buff *pktb, int extra) { /* No room in packet, cannot mangle it. We don't support dynamic * reallocation. Instead, increase the size of the extra room in * the tail in pktb_alloc. */ - if (pkt->len + extra > pkt->data_len) + if (pktb->len + extra > pktb->data_len) return 0; - pkt->len += extra; - pkt->tail = pkt->tail + extra; + pktb->len += extra; return 1; } -static int enlarge_pkt(struct pkt_buff *pkt, unsigned int extra) +static int enlarge_pkt(struct pkt_buff *pktb, unsigned int extra) { - if (pkt->len + extra > 65535) + if (pktb->len + extra > 65535) return 0; - if (!pktb_expand_tail(pkt, extra - pktb_tailroom(pkt))) + if (!pktb_expand_tail(pktb, extra - pktb_tailroom(pktb))) return 0; return 1; @@ -299,8 +370,10 @@ static int enlarge_pkt(struct pkt_buff *pkt, unsigned int extra) /** * pktb_mangle - adjust contents of a packet * \param pktb Pointer to userspace packet buffer - * \param dataoff Offset to layer 4 header. Specify zero to access layer 3 (IP) - * header (layer 2 for family \b AF_BRIDGE) + * \param dataoff Supplementary offset, usually offset from layer 3 (IP) header + * to the layer 4 (TCP or UDP) header. Specify zero to access the layer 3 + * header. If \b pktb was created in family \b AF_BRIDGE, specify + * \b -ETH_HLEN (a negative offset) to access the layer 2 (MAC) header. * \param match_offset Further offset to content that you want to mangle * \param match_len Length of the existing content you want to mangle * \param rep_buffer Pointer to data you want to use to replace current content @@ -310,13 +383,13 @@ static int enlarge_pkt(struct pkt_buff *pkt, unsigned int extra) * excess of \b rep_len over \b match_len \warning pktb_mangle does not update any checksums. Developers should use the appropriate mangler for the protocol level: nfq_ip_mangle(), - nfq_tcp_mangle_ipv4() or nfq_udp_mangle_ipv4(). IPv6 versions are planned. + nfq_tcp_mangle_ipv4(), nfq_udp_mangle_ipv4() or IPv6 variants. \n It is appropriate to use pktb_mangle to change the MAC header. */ EXPORT_SYMBOL int pktb_mangle(struct pkt_buff *pktb, - unsigned int dataoff, + int dataoff, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, @@ -334,7 +407,7 @@ int pktb_mangle(struct pkt_buff *pktb, /* move post-replacement */ memmove(data + match_offset + rep_len, data + match_offset + match_len, - pktb->tail - (pktb->network_header + dataoff + + pktb_tail(pktb) - (pktb->network_header + dataoff + match_offset + match_len)); /* insert data from buffer */ @@ -366,5 +439,17 @@ bool pktb_mangled(const struct pkt_buff *pktb) } /** + * pktb_head_size - get number of bytes needed for a packet buffer + * (control part only) + * \return size of struct pkt_buff + */ + +EXPORT_SYMBOL +size_t pktb_head_size(void) +{ + return sizeof(struct pkt_buff); +} + +/** * @} */ diff --git a/src/extra/tcp.c b/src/extra/tcp.c index 136d7ea..720afd2 100644 --- a/src/extra/tcp.c +++ b/src/extra/tcp.c @@ -18,27 +18,36 @@ #define _GNU_SOURCE #include <netinet/tcp.h> +#include <linux/netfilter/nfnetlink_queue.h> #include <libnetfilter_queue/libnetfilter_queue.h> #include <libnetfilter_queue/libnetfilter_queue_tcp.h> #include <libnetfilter_queue/libnetfilter_queue_ipv4.h> +#include <libnetfilter_queue/libnetfilter_queue_ipv6.h> #include <libnetfilter_queue/pktbuff.h> #include "internal.h" /** * \defgroup tcp TCP helper functions + * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <libmnl/libmnl.h> +#include <libnetfilter_queue/libnetfilter_queue_tcp.h> +\endmanonly + * * @{ */ /** - * nfq_tcp_get - get the TCP header + * nfq_tcp_get_hdr - get the TCP header * \param pktb: pointer to user-space network packet buffer - * - * This function returns NULL if an invalid TCP header is found. On success, - * it returns the TCP header. - * - * \note You have to call nfq_ip_set_transport_header or - * nfq_ip6_set_transport_header first to access the TCP header. + * \returns validated pointer to the TCP header or NULL if the TCP header was + * not set or if a minimal length check fails. + * \note You have to call nfq_ip_set_transport_header() or + * nfq_ip6_set_transport_header() first to set the TCP header. */ EXPORT_SYMBOL struct tcphdr *nfq_tcp_get_hdr(struct pkt_buff *pktb) @@ -47,7 +56,7 @@ struct tcphdr *nfq_tcp_get_hdr(struct pkt_buff *pktb) return NULL; /* No room for the TCP header. */ - if (pktb->tail - pktb->transport_header < sizeof(struct tcphdr)) + if (pktb_tail(pktb) - pktb->transport_header < sizeof(struct tcphdr)) return NULL; return (struct tcphdr *)pktb->transport_header; @@ -57,6 +66,7 @@ struct tcphdr *nfq_tcp_get_hdr(struct pkt_buff *pktb) * nfq_tcp_get_payload - get the TCP packet payload * \param tcph: pointer to the TCP header * \param pktb: pointer to user-space network packet buffer + * \returns Pointer to the TCP payload, or NULL if malformed TCP packet. */ EXPORT_SYMBOL void *nfq_tcp_get_payload(struct tcphdr *tcph, struct pkt_buff *pktb) @@ -68,7 +78,7 @@ void *nfq_tcp_get_payload(struct tcphdr *tcph, struct pkt_buff *pktb) return NULL; /* malformed TCP data offset. */ - if (pktb->transport_header + len > pktb->tail) + if (pktb->transport_header + len > pktb_tail(pktb)) return NULL; return pktb->transport_header + len; @@ -78,17 +88,42 @@ void *nfq_tcp_get_payload(struct tcphdr *tcph, struct pkt_buff *pktb) * nfq_tcp_get_payload_len - get the tcp packet payload * \param tcph: pointer to the TCP header * \param pktb: pointer to user-space network packet buffer + * \returns Length of TCP payload (user data) */ EXPORT_SYMBOL unsigned int nfq_tcp_get_payload_len(struct tcphdr *tcph, struct pkt_buff *pktb) { - return pktb->tail - pktb->transport_header; + return pktb_tail(pktb) - pktb->transport_header - (tcph->doff * 4); } /** - * nfq_tcp_set_checksum_ipv4 - computes IPv4/TCP packet checksum + * \defgroup tcp_internals Internal TCP functions + * + * Most user-space programs will never need these. + * + * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <netinet/tcp.h> +#include <libnetfilter_queue/libnetfilter_queue_tcp.h> +\endmanonly + * + * @{ + */ + +/** + * nfq_tcp_compute_checksum_ipv4 - computes IPv4/TCP packet checksum * \param tcph: pointer to the TCP header * \param iph: pointer to the IPv4 header + * \note + * nfq_tcp_mangle_ipv4() invokes this function. + * As long as developers always use __nfq_tcp_mangle_ipv4__ when changing the + * content of a TCP message, there is no need to call + * __nfq_tcp_compute_checksum_ipv4__. */ EXPORT_SYMBOL void nfq_tcp_compute_checksum_ipv4(struct tcphdr *tcph, struct iphdr *iph) @@ -99,9 +134,14 @@ void nfq_tcp_compute_checksum_ipv4(struct tcphdr *tcph, struct iphdr *iph) } /** - * nfq_tcp_set_checksum_ipv6 - computes IPv6/TCP packet checksum + * nfq_tcp_compute_checksum_ipv6 - computes IPv6/TCP packet checksum * \param tcph: pointer to the TCP header - * \param iph: pointer to the IPv6 header + * \param ip6h: pointer to the IPv6 header + * \note + * nfq_tcp_mangle_ipv6() invokes this function. + * As long as developers always use __nfq_tcp_mangle_ipv6__ when changing the + * content of a TCP message, there is no need to call + * __nfq_tcp_compute_checksum_ipv6__. */ EXPORT_SYMBOL void nfq_tcp_compute_checksum_ipv6(struct tcphdr *tcph, struct ip6_hdr *ip6h) @@ -111,6 +151,10 @@ void nfq_tcp_compute_checksum_ipv6(struct tcphdr *tcph, struct ip6_hdr *ip6h) tcph->check = nfq_checksum_tcpudp_ipv6(ip6h, tcph, IPPROTO_TCP); } +/** + * @} + */ + /* * The union cast uses a gcc extension to avoid aliasing problems * (union is compatible to any of its members) @@ -128,7 +172,9 @@ union tcp_word_hdr { * readable way * \param buf: pointer to buffer that is used to print the object * \param size: size of the buffer (or remaining room in it). - * \param tcp: pointer to a valid tcp header. + * \param tcph: pointer to a valid tcp header. + * \returns Same as \b snprintf + * \sa __snprintf__(3) * */ EXPORT_SYMBOL @@ -183,21 +229,25 @@ int nfq_tcp_snprintf(char *buf, size_t size, const struct tcphdr *tcph) * \param match_len: length of the existing content you want to mangle * \param rep_buffer: pointer to data you want to use to replace current content * \param rep_len: length of data you want to use to replace current content - * - * \note This function recalculates the IPv4 and TCP checksums for you. + * \returns 1 for success and 0 for failure. See pktb_mangle() for failure case + * \note This function updates the IPv4 length and recalculates the IPv4 & TCP + * checksums for you. + * \warning After changing the length of a TCP message, the application will + * need to mangle sequence numbers in both directions until another change + * puts them in sync again */ EXPORT_SYMBOL -int nfq_tcp_mangle_ipv4(struct pkt_buff *pkt, +int nfq_tcp_mangle_ipv4(struct pkt_buff *pktb, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, unsigned int rep_len) { struct iphdr *iph; struct tcphdr *tcph; - iph = (struct iphdr *)pkt->network_header; - tcph = (struct tcphdr *)(pkt->network_header + iph->ihl*4); + iph = (struct iphdr *)pktb->network_header; + tcph = (struct tcphdr *)(pktb->network_header + iph->ihl*4); - if (!nfq_ip_mangle(pkt, iph->ihl*4 + tcph->doff*4, + if (!nfq_ip_mangle(pktb, iph->ihl*4 + tcph->doff*4, match_offset, match_len, rep_buffer, rep_len)) return 0; @@ -207,5 +257,44 @@ int nfq_tcp_mangle_ipv4(struct pkt_buff *pkt, } /** + * nfq_tcp_mangle_ipv6 - Mangle TCP/IPv6 packet buffer + * \param pktb: Pointer to network packet buffer + * \param match_offset: Offset from start of TCP data of content that you want + * to mangle + * \param match_len: Length of the existing content you want to mangle + * \param rep_buffer: Pointer to data you want to use to replace current content + * \param rep_len: Length of data you want to use to replace current content + * \returns 1 for success and 0 for failure. See pktb_mangle() for failure case + * \note This function updates the IPv6 length and recalculates the TCP + * checksum for you. + * \warning After changing the length of a TCP message, the application will + * need to mangle sequence numbers in both directions until another change + * puts them in sync again + */ +EXPORT_SYMBOL +int nfq_tcp_mangle_ipv6(struct pkt_buff *pktb, + unsigned int match_offset, unsigned int match_len, + const char *rep_buffer, unsigned int rep_len) +{ + struct ip6_hdr *ip6h; + struct tcphdr *tcph; + + ip6h = (struct ip6_hdr *)pktb->network_header; + tcph = (struct tcphdr *)(pktb->transport_header); + if (!tcph) + return 0; + + if (!nfq_ip6_mangle(pktb, + pktb->transport_header - pktb->network_header + + tcph->doff * 4, + match_offset, match_len, rep_buffer, rep_len)) + return 0; + + nfq_tcp_compute_checksum_ipv6(tcph, ip6h); + + return 1; +} + +/** * @} */ diff --git a/src/extra/udp.c b/src/extra/udp.c index fed23e2..ede2196 100644 --- a/src/extra/udp.c +++ b/src/extra/udp.c @@ -17,24 +17,37 @@ #define _GNU_SOURCE #include <netinet/udp.h> +#include <linux/netfilter/nfnetlink_queue.h> #include <libnetfilter_queue/libnetfilter_queue.h> #include <libnetfilter_queue/libnetfilter_queue_udp.h> #include <libnetfilter_queue/libnetfilter_queue_ipv4.h> +#include <libnetfilter_queue/libnetfilter_queue_ipv6.h> #include <libnetfilter_queue/pktbuff.h> #include "internal.h" /** * \defgroup udp UDP helper functions + * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <libmnl/libmnl.h> +#include <libnetfilter_queue/libnetfilter_queue_udp.h> +\endmanonly + * * @{ */ /** * nfq_udp_get_hdr - get the UDP header. - * \param pktb: Pointer to network packet buffer + * \param pktb: Pointer to userspace network packet buffer * - * This function returns NULL if invalid UDP header is found. On success, - * it returns the UDP header. + * \returns validated pointer to the UDP header or NULL if the UDP header was + * not set or if a minimal length check fails. + * \note You have to call nfq_ip_set_transport_header() or + * nfq_ip6_set_transport_header() first to set the UDP header. */ EXPORT_SYMBOL struct udphdr *nfq_udp_get_hdr(struct pkt_buff *pktb) @@ -43,7 +56,7 @@ struct udphdr *nfq_udp_get_hdr(struct pkt_buff *pktb) return NULL; /* No room for the UDP header. */ - if (pktb->tail - pktb->transport_header < sizeof(struct udphdr)) + if (pktb_tail(pktb) - pktb->transport_header < sizeof(struct udphdr)) return NULL; return (struct udphdr *)pktb->transport_header; @@ -52,7 +65,8 @@ struct udphdr *nfq_udp_get_hdr(struct pkt_buff *pktb) /** * nfq_udp_get_payload - get the UDP packet payload. * \param udph: Pointer to UDP header - * \param pktb: Pointer to network packet buffer + * \param pktb: Pointer to userspace network packet buffer + * \returns Pointer to the UDP payload, or NULL if malformed UDP packet. */ EXPORT_SYMBOL void *nfq_udp_get_payload(struct udphdr *udph, struct pkt_buff *pktb) @@ -64,7 +78,7 @@ void *nfq_udp_get_payload(struct udphdr *udph, struct pkt_buff *pktb) return NULL; /* malformed UDP packet. */ - if (pktb->transport_header + len > pktb->tail) + if (pktb->transport_header + len > pktb_tail(pktb)) return NULL; return pktb->transport_header + sizeof(struct udphdr); @@ -73,23 +87,43 @@ void *nfq_udp_get_payload(struct udphdr *udph, struct pkt_buff *pktb) /** * nfq_udp_get_payload_len - get the udp packet payload. * \param udph: Pointer to UDP header - * \param pktb: Pointer to network packet buffer + * \param pktb: Pointer to userspace network packet buffer + * \returns Length of UDP payload (user data) */ EXPORT_SYMBOL unsigned int nfq_udp_get_payload_len(struct udphdr *udph, struct pkt_buff *pktb) { - return pktb->tail - pktb->transport_header; + return pktb_tail(pktb) - pktb->transport_header - sizeof(struct udphdr); } /** - * nfq_udp_set_checksum_ipv4 - computes a IPv4/TCP packet's segment - * \param iphdrp: pointer to the ip header - * \param ippayload: payload of the ip packet + * \defgroup udp_internals Internal UDP functions * - * \returns the checksum of the udp segment. + * Most user-space programs will never need these. + * + * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <netinet/udp.h> +#include <libnetfilter_queue/libnetfilter_queue_udp.h> +\endmanonly * - * \see nfq_pkt_compute_ip_checksum - * \see nfq_pkt_compute_udp_checksum + * @{ + */ + +/** + * nfq_udp_compute_checksum_ipv4 - sets up the UDP checksum in a UDP/IPv4 packet + * \param udph: pointer to the UDP header + * \param iph: pointer to the IPv4 header + * \note + * nfq_udp_mangle_ipv4() invokes this function. + * As long as developers always use __nfq_udp_mangle_ipv4__ when changing the + * content of a UDP message, there is no need to call + * __nfq_udp_compute_checksum_ipv4__. */ EXPORT_SYMBOL void nfq_udp_compute_checksum_ipv4(struct udphdr *udph, struct iphdr *iph) @@ -100,14 +134,14 @@ void nfq_udp_compute_checksum_ipv4(struct udphdr *udph, struct iphdr *iph) } /** - * nfq_udp_set_checksum_ipv6 - computes a IPv6/TCP packet's segment - * \param iphdrp: pointer to the ip header - * \param ippayload: payload of the ip packet - * - * \returns the checksum of the udp segment. - * - * \see nfq_pkt_compute_ip_checksum - * \see nfq_pkt_compute_udp_checksum + * nfq_udp_compute_checksum_ipv6 - sets up the UDP checksum in a UDP/IPv6 packet + * \param udph: pointer to the UDP header + * \param ip6h: pointer to the IPv6 header + * \note + * nfq_udp_mangle_ipv6() invokes this function. + * As long as developers always use __nfq_udp_mangle_ipv6__ when changing the + * content of a UDP message, there is no need to call + * __nfq_udp_compute_checksum_ipv6__. */ EXPORT_SYMBOL void nfq_udp_compute_checksum_ipv6(struct udphdr *udph, struct ip6_hdr *ip6h) @@ -118,6 +152,10 @@ void nfq_udp_compute_checksum_ipv6(struct udphdr *udph, struct ip6_hdr *ip6h) } /** + * @} + */ + +/** * nfq_udp_mangle_ipv4 - Mangle UDP/IPv4 packet buffer * \param pktb: Pointer to network packet buffer * \param match_offset: Offset from start of UDP data of content that you want @@ -130,19 +168,19 @@ void nfq_udp_compute_checksum_ipv6(struct udphdr *udph, struct ip6_hdr *ip6h) * checksums for you. */ EXPORT_SYMBOL -int nfq_udp_mangle_ipv4(struct pkt_buff *pkt, +int nfq_udp_mangle_ipv4(struct pkt_buff *pktb, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, unsigned int rep_len) { struct iphdr *iph; struct udphdr *udph; - iph = (struct iphdr *)pkt->network_header; - udph = (struct udphdr *)(pkt->network_header + iph->ihl*4); + iph = (struct iphdr *)pktb->network_header; + udph = (struct udphdr *)(pktb->network_header + iph->ihl*4); udph->len = htons(ntohs(udph->len) + rep_len - match_len); - if (!nfq_ip_mangle(pkt, iph->ihl*4 + sizeof(struct udphdr), + if (!nfq_ip_mangle(pktb, iph->ihl*4 + sizeof(struct udphdr), match_offset, match_len, rep_buffer, rep_len)) return 0; @@ -152,11 +190,51 @@ int nfq_udp_mangle_ipv4(struct pkt_buff *pkt, } /** + * nfq_udp_mangle_ipv6 - Mangle UDP/IPv6 packet buffer + * \param pktb: Pointer to network packet buffer + * \param match_offset: Offset from start of UDP data of content that you want + * to mangle + * \param match_len: Length of the existing content you want to mangle + * \param rep_buffer: Pointer to data you want to use to replace current content + * \param rep_len: Length of data you want to use to replace current content + * \returns 1 for success and 0 for failure. See pktb_mangle() for failure case + * \note This function updates the IPv6 and UDP lengths and recalculates the UDP + * checksum for you. + */ +EXPORT_SYMBOL +int nfq_udp_mangle_ipv6(struct pkt_buff *pktb, + unsigned int match_offset, unsigned int match_len, + const char *rep_buffer, unsigned int rep_len) +{ + struct ip6_hdr *ip6h; + struct udphdr *udph; + + ip6h = (struct ip6_hdr *)pktb->network_header; + udph = (struct udphdr *)(pktb->transport_header); + if (!udph) + return 0; + + udph->len = htons(ntohs(udph->len) + rep_len - match_len); + + if (!nfq_ip6_mangle(pktb, + pktb->transport_header - pktb->network_header + + sizeof(struct udphdr), + match_offset, match_len, rep_buffer, rep_len)) + return 0; + + nfq_udp_compute_checksum_ipv6(udph, ip6h); + + return 1; +} + +/** * nfq_pkt_snprintf_udp_hdr - print udp header into one buffer in a humnan * readable way * \param buf: pointer to buffer that is used to print the object * \param size: size of the buffer (or remaining room in it). - * \param udp: pointer to a valid udp header. + * \param udph: pointer to a valid udp header. + * \returns The number of characters notionally written (excluding trailing NUL) + * \sa __snprintf__(3) * */ EXPORT_SYMBOL diff --git a/src/internal.h b/src/internal.h index d968325..ae849d6 100644 --- a/src/internal.h +++ b/src/internal.h @@ -23,9 +23,7 @@ struct pkt_buff { uint8_t *network_header; uint8_t *transport_header; - uint8_t *head; uint8_t *data; - uint8_t *tail; uint32_t len; uint32_t data_len; @@ -33,4 +31,8 @@ struct pkt_buff { bool mangled; }; +static inline uint8_t *pktb_tail(struct pkt_buff *pktb) +{ + return pktb->data + pktb->len; +} #endif diff --git a/src/libnetfilter_queue.c b/src/libnetfilter_queue.c index cd14825..bf67a19 100644 --- a/src/libnetfilter_queue.c +++ b/src/libnetfilter_queue.c @@ -4,7 +4,7 @@ * (C) 2005, 2008-2010 by Pablo Neira Ayuso <pablo@netfilter.org> * * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 + * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation (or any later at your option) * * This program is distributed in the hope that it will be useful, @@ -29,6 +29,7 @@ #include <errno.h> #include <netinet/in.h> #include <sys/socket.h> +#include <linux/netfilter/nfnetlink_queue.h> #include <libnfnetlink/libnfnetlink.h> #include <libnetfilter_queue/libnetfilter_queue.h> @@ -44,11 +45,11 @@ * libnetfilter_queue homepage is: * https://netfilter.org/projects/libnetfilter_queue/ * - * \section deps Dependencies + <h1>Dependencies</h1> * libnetfilter_queue requires libmnl, libnfnetlink and a kernel that includes * the Netfilter NFQUEUE over NFNETLINK interface (i.e. 2.6.14 or later). * - * \section features Main Features + * <h1>Main Features</h1> * - receiving queued packets from the kernel nfnetlink_queue subsystem * - issuing verdicts and possibly reinjecting altered packets to the kernel * nfnetlink_queue subsystem @@ -70,16 +71,16 @@ * When a queue is full, packets that should have been enqueued are dropped by * kernel instead of being enqueued. * - * \section git Git Tree + * <h1>Git Tree</h1> * The current development version of libnetfilter_queue can be accessed at * https://git.netfilter.org/libnetfilter_queue. * - * \section privs Privileges + * <h1>Privileges</h1> * You need the CAP_NET_ADMIN capability in order to allow your application * to receive from and to send packets to kernel-space. * - * \section using Using libnetfilter_queue - * + * <h1>Using libnetfilter_queue</h1> + * * To write your own program using libnetfilter_queue, you should start by * reading (or, if feasible, compiling and stepping through with *gdb*) * nf-queue.c source file. @@ -87,7 +88,14 @@ * \verbatim gcc -g3 -ggdb -Wall -lmnl -lnetfilter_queue -o nf-queue nf-queue.c \endverbatim - * The doxygen documentation \link LibrarySetup \endlink is Deprecated and + *The doxygen documentation + * \htmlonly +<a class="el" href="group__LibrarySetup.html">LibrarySetup </a> +\endhtmlonly + * \manonly +\fBLibrarySetup\fP\ +\endmanonly + * is Deprecated and * incompatible with non-deprecated functions. It is hoped to produce a * corresponding non-deprecated (*Current*) topic soon. * @@ -96,7 +104,7 @@ gcc -g3 -ggdb -Wall -lmnl -lnetfilter_queue -o nf-queue nf-queue.c * article: * https://home.regit.org/netfilter-en/using-nfqueue-and-libnetfilter_queue/ * - * \section errors ENOBUFS errors in recv() + * <h1>ENOBUFS errors in recv()</h1> * * recv() may return -1 and errno is set to ENOBUFS in case that your * application is not fast enough to retrieve the packets from the kernel. @@ -105,7 +113,7 @@ gcc -g3 -ggdb -Wall -lmnl -lnetfilter_queue -o nf-queue nf-queue.c * you may hit it again sooner or later. The next section provides some hints * on how to obtain the best performance for your application. * - * \section perf Performance + * <h1>Performance</h1> * To improve your libnetfilter_queue application in terms of performance, * you may consider the following tweaks: * @@ -119,6 +127,9 @@ gcc -g3 -ggdb -Wall -lmnl -lnetfilter_queue -o nf-queue nf-queue.c * (it requires Linux kernel >= 2.6.31). * - consider using fail-open option see nfq_set_queue_flags() (it requires * Linux kernel >= 3.6) + * - make your application offload aware to avoid costly normalization on kernel + * side. See NFQA_CFG_F_GSO flag to nfq_set_queue_flags(). + * Linux kernel >= 3.10. * - increase queue max length with nfq_set_queue_maxlen() to resist to packets * burst */ @@ -147,7 +158,7 @@ struct nfq_data { EXPORT_SYMBOL int nfq_errno; /*********************************************************************** - * low level stuff + * low level stuff ***********************************************************************/ static void del_qh(struct nfq_q_handle *qh) @@ -238,12 +249,12 @@ struct nfnl_handle *nfq_nfnlh(struct nfq_handle *h) * * \defgroup Queue Queue handling [DEPRECATED] * - * Once libnetfilter_queue library has been initialised (See + * Once libnetfilter_queue library has been initialised (See * \link LibrarySetup \endlink), it is possible to bind the program to a * specific queue. This can be done by using nfq_create_queue(). * * The queue can then be tuned via nfq_set_mode() or nfq_set_queue_maxlen(). - * + * * Here's a little code snippet that create queue numbered 0: * \verbatim printf("binding this socket to queue '0'\n"); @@ -270,7 +281,7 @@ struct nfnl_handle *nfq_nfnlh(struct nfq_handle *h) nfq_handle_packet(h, buf, rv); } \endverbatim - * When the decision on a packet has been choosed, the verdict has to be given + * When the decision on a packet has been chosen, the verdict has to be given * by calling nfq_set_verdict() or nfq_set_verdict2(). The verdict * determines the destiny of the packet as follows: * @@ -287,8 +298,18 @@ struct nfnl_handle *nfq_nfnlh(struct nfq_handle *h) * is to also set an nfmark using nfq_set_verdict2, and set up the nefilter * rules to only queue a packet when the mark is not (yet) set. * - * Data and information about the packet can be fetch by using message parsing + * Data and information about the packet can be fetched by using message parsing * functions (See \link Parsing \endlink). + * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <linux/netfilter.h> +#include <linux/netfilter/nfnetlink_queue.h> +#include <libnetfilter_queue/libnetfilter_queue.h> +\endmanonly + * * @{ */ @@ -318,7 +339,7 @@ int nfq_fd(struct nfq_handle *h) * * Library initialisation is made in two steps. * - * First step is to call nfq_open() to open a NFQUEUE handler. + * First step is to call nfq_open() to open a NFQUEUE handler. * * Second step is to tell the kernel that userspace queueing is handle by * NFQUEUE for the selected protocol. This is made by calling nfq_unbind_pf() @@ -387,7 +408,7 @@ struct nfq_handle *nfq_open(void) * \param nfnlh Netfilter netlink connection handle obtained by calling nfnl_open() * * This function obtains a netfilter queue connection handle using an existing - * netlink connection. This function is used internally to implement + * netlink connection. This function is used internally to implement * nfq_open(), and should typically not be called directly. * * \return a pointer to a new queue handle or NULL on failure. @@ -409,7 +430,7 @@ struct nfq_handle *nfq_open_nfnl(struct nfnl_handle *nfnlh) memset(h, 0, sizeof(*h)); h->nfnlh = nfnlh; - h->nfnlssh = nfnl_subsys_open(h->nfnlh, NFNL_SUBSYS_QUEUE, + h->nfnlssh = nfnl_subsys_open(h->nfnlh, NFNL_SUBSYS_QUEUE, NFQNL_MSG_MAX, 0); if (!h->nfnlssh) { /* FIXME: nfq_errno */ @@ -437,6 +458,14 @@ out_free: * When the program has finished with libnetfilter_queue, it has to call * the nfq_close() function to free all associated resources. * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <linux/netfilter/nfnetlink_queue.h> +#include <libnetfilter_queue/libnetfilter_queue.h> +\endmanonly + * * @{ */ @@ -446,7 +475,7 @@ out_free: * * This function closes the nfqueue handler and free associated resources. * - * \return 0 on success, non-zero on failure. + * \return 0 on success, non-zero on failure. */ EXPORT_SYMBOL int nfq_close(struct nfq_handle *h) @@ -513,10 +542,10 @@ int nfq_unbind_pf(struct nfq_handle *h, uint16_t pf) * \return a nfq_q_handle pointing to the newly created queue * * Creates a new queue handle, and returns it. The new queue is identified by - * #num, and the callback specified by #cb will be called for each enqueued - * packet. The #data argument will be passed unchanged to the callback. If - * a queue entry with id #num already exists, this function will return failure - * and the existing entry is unchanged. + * \b num, and the callback specified by \b cb will be called for each enqueued + * packet. The \b data argument will be passed unchanged to the callback. If + * a queue entry with id \b num already exists, + * this function will return failure and the existing entry is unchanged. * * The nfq_callback type is defined in libnetfilter_queue.h as: * \verbatim @@ -651,7 +680,7 @@ int nfq_set_mode(struct nfq_q_handle *qh, uint8_t mode, uint32_t range) * nfq_set_queue_flags - set flags (options) for the kernel queue * \param qh Netfilter queue handle obtained by call to nfq_create_queue(). * \param mask specifies which flag bits to modify - * \param flag bitmask of flags + * \param flags bitmask of flags * * Existing flags, that you may want to combine, are: * @@ -830,14 +859,14 @@ static int __set_verdict(struct nfq_q_handle *qh, uint32_t id, */ /** - * nfq_set_verdict - issue a verdict on a packet + * nfq_set_verdict - issue a verdict on a packet * \param qh Netfilter queue handle obtained by call to nfq_create_queue(). * \param id ID assigned to packet by netfilter. * \param verdict verdict to return to netfilter (NF_ACCEPT, NF_DROP) - * \param data_len number of bytes of data pointed to by #buf + * \param data_len number of bytes of data pointed to by \b buf * \param buf the buffer that contains the packet data * - * Can be obtained by: + * Can be obtained by: * \verbatim int id; struct nfqnl_msg_packet_hdr *ph = nfq_get_msg_packet_hdr(tb); @@ -867,7 +896,7 @@ int nfq_set_verdict(struct nfq_q_handle *qh, uint32_t id, * \param id ID assigned to packet by netfilter. * \param verdict verdict to return to netfilter (NF_ACCEPT, NF_DROP) * \param mark mark to put on packet - * \param data_len number of bytes of data pointed to by #buf + * \param data_len number of bytes of data pointed to by \b buf * \param buf the buffer that contains the packet data */ EXPORT_SYMBOL @@ -886,7 +915,7 @@ int nfq_set_verdict2(struct nfq_q_handle *qh, uint32_t id, * \param verdict verdict to return to netfilter (NF_ACCEPT, NF_DROP) * * Unlike nfq_set_verdict, the verdict is applied to all queued packets - * whose packet id is smaller or equal to #id. + * whose packet id is smaller or equal to \b id. * * batch support was added in Linux 3.1. * These functions will fail silently on older kernels. @@ -920,7 +949,7 @@ int nfq_set_verdict_batch2(struct nfq_q_handle *qh, uint32_t id, * \param id ID assigned to packet by netfilter. * \param verdict verdict to return to netfilter (NF_ACCEPT, NF_DROP) * \param mark the mark to put on the packet, in network byte order. - * \param data_len number of bytes of data pointed to by #buf + * \param data_len number of bytes of data pointed to by \b buf * \param buf the buffer that contains the packet data * * \return -1 on error; >= 0 otherwise. @@ -944,11 +973,20 @@ int nfq_set_verdict_mark(struct nfq_q_handle *qh, uint32_t id, /************************************************************* - * Message parsing functions + * Message parsing functions *************************************************************/ /** * \defgroup Parsing Message parsing functions [DEPRECATED] + * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <linux/netfilter/nfnetlink_queue.h> +#include <libnetfilter_queue/libnetfilter_queue.h> +\endmanonly + * * @{ */ @@ -1065,7 +1103,7 @@ uint32_t nfq_get_outdev(struct nfq_data *nfad) * The index of the physical device the queued packet will be sent out. * If the returned index is 0, the packet is destined for localhost or the * physical output interface is not yet known (ie. PREROUTING?). - * + * * \return The index of physical interface that the packet output will be routed out. */ EXPORT_SYMBOL @@ -1081,10 +1119,10 @@ uint32_t nfq_get_physoutdev(struct nfq_data *nfad) * \param nfad Netlink packet data handle passed to callback function * \param name pointer to the buffer to receive the interface name; * not more than \c IFNAMSIZ bytes will be copied to it. - * \return -1 in case of error, >0 if it succeed. + * \return -1 in case of error, >0 if it succeed. * * To use a nlif_handle, You need first to call nlif_open() and to open - * an handler. Don't forget to store the result as it will be used + * an handler. Don't forget to store the result as it will be used * during all your program life: * \verbatim h = nlif_open(); @@ -1101,7 +1139,7 @@ uint32_t nfq_get_physoutdev(struct nfq_data *nfad) * libnfnetlink is able to update the interface mapping when a new interface * appears. To do so, you need to call nlif_catch() on the handler after each * interface related event. The simplest way to get and treat event is to run - * a select() or poll() against the nlif file descriptor. To get this file + * a select() or poll() against the nlif file descriptor. To get this file * descriptor, you need to use nlif_fd: * \verbatim if_fd = nlif_fd(h); @@ -1130,7 +1168,7 @@ int nfq_get_indev_name(struct nlif_handle *nlif_handle, * * See nfq_get_indev_name() documentation for nlif_handle usage. * - * \return -1 in case of error, > 0 if it succeed. + * \return -1 in case of error, > 0 if it succeed. */ EXPORT_SYMBOL int nfq_get_physindev_name(struct nlif_handle *nlif_handle, @@ -1150,7 +1188,7 @@ int nfq_get_physindev_name(struct nlif_handle *nlif_handle, * * See nfq_get_indev_name() documentation for nlif_handle usage. * - * \return -1 in case of error, > 0 if it succeed. + * \return -1 in case of error, > 0 if it succeed. */ EXPORT_SYMBOL int nfq_get_outdev_name(struct nlif_handle *nlif_handle, @@ -1170,7 +1208,7 @@ int nfq_get_outdev_name(struct nlif_handle *nlif_handle, * * See nfq_get_indev_name() documentation for nlif_handle usage. * - * \return -1 in case of error, > 0 if it succeed. + * \return -1 in case of error, > 0 if it succeed. */ EXPORT_SYMBOL @@ -1184,7 +1222,7 @@ int nfq_get_physoutdev_name(struct nlif_handle *nlif_handle, /** * nfq_get_packet_hw * - * get hardware address + * get hardware address * * \param nfad Netlink packet data handle passed to callback function * @@ -1211,8 +1249,40 @@ struct nfqnl_msg_packet_hw *nfq_get_packet_hw(struct nfq_data *nfad) } /** + * nfq_get_skbinfo - return the NFQA_SKB_INFO meta information + * \param nfad Netlink packet data handle passed to callback function + * + * This can be used to obtain extra information about a packet by testing + * the returned integer for any of the following bit flags: + * + * - NFQA_SKB_CSUMNOTREADY + * packet header checksums will be computed by hardware later on, i.e. + * tcp/ip checksums in the packet must not be validated, application + * should pretend they are correct. + * - NFQA_SKB_GSO + * packet is an aggregated super-packet. It exceeds device mtu and will + * be (re-)split on transmit by hardware. + * - NFQA_SKB_CSUM_NOTVERIFIED + * packet checksum was not yet verified by the kernel/hardware, for + * example because this is an incoming packet and the NIC does not + * perform checksum validation at hardware level. + * + * \return the skbinfo value + * \sa __nfq_set_queue_flags__(3) + */ +EXPORT_SYMBOL +uint32_t nfq_get_skbinfo(struct nfq_data *nfad) +{ + if (!nfnl_attr_present(nfad->data, NFQA_SKB_INFO)) + return 0; + + return ntohl(nfnl_get_data(nfad->data, NFQA_SKB_INFO, uint32_t)); +} + +/** * nfq_get_uid - get the UID of the user the packet belongs to * \param nfad Netlink packet data handle passed to callback function + * \param uid Set to UID on return * * \warning If the NFQA_CFG_F_GSO flag is not set, then fragmented packets * may be pushed into the queue. In this case, only one fragment will have the @@ -1233,6 +1303,7 @@ int nfq_get_uid(struct nfq_data *nfad, uint32_t *uid) /** * nfq_get_gid - get the GID of the user the packet belongs to * \param nfad Netlink packet data handle passed to callback function + * \param gid Set to GID on return * * \warning If the NFQA_CFG_F_GSO flag is not set, then fragmented packets * may be pushed into the queue. In this case, only one fragment will have the @@ -1277,7 +1348,7 @@ int nfq_get_secctx(struct nfq_data *nfad, unsigned char **secdata) } /** - * nfq_get_payload - get payload + * nfq_get_payload - get payload * \param nfad Netlink packet data handle passed to callback function * \param data Pointer of pointer that will be pointed to the payload * @@ -1315,6 +1386,15 @@ do { \ /** * \defgroup Printing Printing [DEPRECATED] + * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <linux/netfilter/nfnetlink_queue.h> +#include <libnetfilter_queue/libnetfilter_queue.h> +\endmanonly + * * @{ */ diff --git a/src/nlmsg.c b/src/nlmsg.c index c40a9e4..39fd12d 100644 --- a/src/nlmsg.c +++ b/src/nlmsg.c @@ -27,6 +27,16 @@ /** * \defgroup nfq_verd Verdict helpers + * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <linux/netfilter.h> +#include <linux/netfilter/nfnetlink_queue.h> +#include <libnetfilter_queue/libnetfilter_queue.h> +\endmanonly + * * @{ */ @@ -34,11 +44,39 @@ * nfq_nlmsg_verdict_put - Put a verdict into a Netlink message * \param nlh Pointer to netlink message * \param id ID assigned to packet by netfilter - * \param verdict verdict to return to netfilter (NF_ACCEPT, NF_DROP) + * \param verdict verdict to return to netfilter (see \b Verdicts below) + * \par Verdicts + * __NF_DROP__ Drop the packet. This is final. + * \n + * __NF_ACCEPT__ Accept the packet. Processing of the current base chain + * and any called chains terminates, + * but the packet may still be processed by subsequently invoked base chains. + * \n + * __NF_STOP__ Like __NF_ACCEPT__, but skip any further base chains using the + * current hook. + * \n + * __NF_REPEAT__ Like __NF_ACCEPT__, but re-queue this packet to the + * current base chain. One way to prevent a re-queueing loop is to + * also set a packet mark using nfq_nlmsg_verdict_put_mark() and have the + * program test for this mark in \c attr[NFQA_MARK]; or have the nefilter rules + * 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, + * 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: + * \verbatim + nfq_nlmsg_verdict_put(nlh, id, NF_QUEUE_NR(new_queue) | + NF_VERDICT_FLAG_QUEUE_BYPASS); +\endverbatim * - * See examples/nf-queue.c, line 46 for an example of how to use this function. + * 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). */ EXPORT_SYMBOL void nfq_nlmsg_verdict_put(struct nlmsghdr *nlh, int id, int verdict) @@ -90,14 +128,13 @@ EXPORT_SYMBOL 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); - // Update IP Datagram length - plen += rep_len - match_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), plen); + nfq_nlmsg_verdict_put_pkt(nlh, pktb_data(pktb), pktb_len(pktb)); \endverbatim */ void nfq_nlmsg_verdict_put_pkt(struct nlmsghdr *nlh, const void *pkt, @@ -112,6 +149,15 @@ void nfq_nlmsg_verdict_put_pkt(struct nlmsghdr *nlh, const void *pkt, /** * \defgroup nfq_cfg Config helpers + * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <linux/netfilter/nfnetlink_queue.h> +#include <libnetfilter_queue/libnetfilter_queue.h> +\endmanonly + * * @{ */ @@ -178,6 +224,15 @@ void nfq_nlmsg_cfg_put_qmaxlen(struct nlmsghdr *nlh, uint32_t queue_maxlen) /** * \defgroup nlmsg Netlink message helper functions + * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <linux/netfilter/nfnetlink_queue.h> +#include <libnetfilter_queue/libnetfilter_queue.h> +\endmanonly + * * @{ */ @@ -198,7 +253,6 @@ static int nfq_pkt_parse_attr_cb(const struct nlattr *attr, void *data) case NFQA_IFINDEX_PHYSOUTDEV: case NFQA_CAP_LEN: case NFQA_SKB_INFO: - case NFQA_SECCTX: case NFQA_UID: case NFQA_GID: case NFQA_CT_INFO: @@ -226,6 +280,7 @@ static int nfq_pkt_parse_attr_cb(const struct nlattr *attr, void *data) case NFQA_PAYLOAD: case NFQA_CT: case NFQA_EXP: + case NFQA_SECCTX: break; } tb[type] = attr; @@ -234,11 +289,9 @@ static int nfq_pkt_parse_attr_cb(const struct nlattr *attr, void *data) /** * nfq_nlmsg_parse - set packet attributes from netlink message - * \param nlh netlink message that you want to read. - * \param attr pointer to array of attributes to set. - * - * This function returns MNL_CB_ERROR if any error occurs, or MNL_CB_OK on - * success. + * \param nlh Pointer to netlink message + * \param attr Pointer to array of attributes to set + * \returns MNL_CB_OK on success or MNL_CB_ERROR if any error occurs */ EXPORT_SYMBOL int nfq_nlmsg_parse(const struct nlmsghdr *nlh, struct nlattr **attr) @@ -248,5 +301,83 @@ int nfq_nlmsg_parse(const struct nlmsghdr *nlh, struct nlattr **attr) } /** + * nfq_nlmsg_put - Convert memory buffer into a Netlink buffer + * \param *buf Pointer to memory buffer + * \param type Either NFQNL_MSG_CONFIG or NFQNL_MSG_VERDICT + * \param queue_num Queue number + * \returns Pointer to netlink message + */ +EXPORT_SYMBOL +struct nlmsghdr *nfq_nlmsg_put(char *buf, int type, uint32_t queue_num) +{ + return nfq_nlmsg_put2(buf, type, queue_num, 0); +} + +/** + * nfq_nlmsg_put2 - Set up a netlink header with user-specified flags + * in a memory buffer + * \param *buf Pointer to memory buffer + * \param type One of NFQNL_MSG_CONFIG, NFQNL_MSG_VERDICT + * or NFQNL_MSG_VERDICT_BATCH + * \param queue_num Queue number + * \param flags additional NLM_F_xxx flags to put in message header. These are + * defined in /usr/include/linux/netlink.h. nfq_nlmsg_put2() always + * sets NLM_F_REQUEST + * \returns Pointer to netlink header + * + * For most applications, the only sensible flag will be NLM_F_ACK. + * Use it to get an explicit acknowledgment from the kernel, e.g. + * attempt to configure NFQA_CFG_F_SECCTX on a kernel not supporting + * CONFIG_NETWORK_SECMARK. + * \n + * The kernel always sends a message in response to a failed command. + * NLM_F_ACK instructs the kernel to also send a message in response + * to a successful command. + * \n + * This code snippet demonstrates reading these responses: + * \verbatim + char buf[MNL_SOCKET_BUFFER_SIZE]; + + nlh = nfq_nlmsg_put2(buf, NFQNL_MSG_CONFIG, queue_num, + NLM_F_ACK); + mnl_attr_put_u32(nlh, NFQA_CFG_FLAGS, NFQA_CFG_F_SECCTX); + mnl_attr_put_u32(nlh, NFQA_CFG_MASK, NFQA_CFG_F_SECCTX); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_send"); + exit(EXIT_FAILURE); + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof buf); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + exit(EXIT_FAILURE); + } + + ret = mnl_cb_run(buf, ret, 0, portid, NULL, NULL); + if (ret == -1) + fprintf(stderr, "This kernel version does not allow to " + "retrieve security context.\n"); +\endverbatim + * + */ + +EXPORT_SYMBOL +struct nlmsghdr *nfq_nlmsg_put2(char *buf, int type, uint32_t queue_num, + uint16_t flags) +{ + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | type; + nlh->nlmsg_flags = NLM_F_REQUEST | flags; + + struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = AF_UNSPEC; + nfg->version = NFNETLINK_V0; + nfg->res_id = htons(queue_num); + + return nlh; +} + +/** * @} */ |