diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 5 | ||||
-rw-r--r-- | src/extra/checksum.c | 38 | ||||
-rw-r--r-- | src/extra/icmp.c | 57 | ||||
-rw-r--r-- | src/extra/ipv4.c | 116 | ||||
-rw-r--r-- | src/extra/ipv6.c | 103 | ||||
-rw-r--r-- | src/extra/pktbuff.c | 360 | ||||
-rw-r--r-- | src/extra/tcp.c | 161 | ||||
-rw-r--r-- | src/extra/udp.c | 185 | ||||
-rw-r--r-- | src/internal.h | 14 | ||||
-rw-r--r-- | src/libnetfilter_queue.c | 355 | ||||
-rw-r--r-- | src/nlmsg.c | 238 |
11 files changed, 1214 insertions, 418 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 f367f75..33480af 100644 --- a/src/extra/checksum.c +++ b/src/extra/checksum.c @@ -11,11 +11,13 @@ #include <stdio.h> #include <stdbool.h> +#include <endian.h> #include <arpa/inet.h> #include <netinet/ip.h> #include <netinet/ip6.h> #include <netinet/tcp.h> +#include <linux/netfilter/nfnetlink_queue.h> #include <libnetfilter_queue/libnetfilter_queue.h> #include "internal.h" @@ -26,8 +28,13 @@ uint16_t nfq_checksum(uint32_t sum, uint16_t *buf, int size) sum += *buf++; size -= sizeof(uint16_t); } - if (size) - sum += *(uint8_t *)buf; + if (size) { +#if __BYTE_ORDER == __BIG_ENDIAN + sum += (uint16_t)*(uint8_t *)buf << 8; +#else + sum += (uint16_t)*(uint8_t *)buf; +#endif + } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >>16); @@ -35,7 +42,7 @@ uint16_t nfq_checksum(uint32_t sum, uint16_t *buf, int size) return (uint16_t)(~sum); } -uint16_t nfq_checksum_tcpudp_ipv4(struct iphdr *iph) +uint16_t nfq_checksum_tcpudp_ipv4(struct iphdr *iph, uint16_t protonum) { uint32_t sum = 0; uint32_t iph_len = iph->ihl*4; @@ -46,34 +53,31 @@ uint16_t nfq_checksum_tcpudp_ipv4(struct iphdr *iph) sum += (iph->saddr) & 0xFFFF; sum += (iph->daddr >> 16) & 0xFFFF; sum += (iph->daddr) & 0xFFFF; - sum += htons(IPPROTO_TCP); + sum += htons(protonum); sum += htons(len); return nfq_checksum(sum, (uint16_t *)payload, len); } -uint16_t nfq_checksum_tcpudp_ipv6(struct ip6_hdr *ip6h, void *transport_hdr) +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(IPPROTO_TCP); - sum += htons(ip6h->ip6_plen); + sum += htons(protonum); + 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 a93d113..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,20 +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 - * \param pktb: pointer to network packet buffer + * 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)) @@ -53,13 +68,18 @@ struct iphdr *nfq_ip_get_hdr(struct pkt_buff *pktb) return iph; } -EXPORT_SYMBOL(nfq_ip_get_hdr); /** - * nfq_ip_set_transport_header - set transport header - * \param pktb: pointer to network packet buffer - * \param iph: pointer to the IPv4 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 + * \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) { int doff = iph->ihl * 4; @@ -71,15 +91,33 @@ int nfq_ip_set_transport_header(struct pkt_buff *pktb, struct iphdr *iph) pktb->transport_header = pktb->network_header + doff; return 0; } -EXPORT_SYMBOL(nfq_ip_set_transport_header); /** - * nfq_ip_set_checksum - set IPv4 checksum - * \param iph: pointer to the IPv4 header + * \defgroup ip_internals Internal IP functions + * + * Most user-space programs will never need these. * - * \note Call to this function if you modified the IPv4 header to update the - * checksum. + * + * \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 + * 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) { uint32_t iph_len = iph->ihl * 4; @@ -87,47 +125,50 @@ void nfq_ip_set_checksum(struct iphdr *iph) iph->check = 0; iph->check = nfq_checksum(0, (uint16_t *)iph, iph_len); } -EXPORT_SYMBOL(nfq_ip_set_checksum); + +/** + * @} + */ /** * nfq_ip_mangle - mangle IPv4 packet buffer - * \param pktb: pointer to 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 - * - * \note This function recalculates the IPv4 checksum (if needed). + * \param pktb: Pointer to user-space network packet buffer + * \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 if necessary and recalculates the + * IPv4 checksum. */ -int nfq_ip_mangle(struct pkt_buff *pkt, unsigned int dataoff, +EXPORT_SYMBOL +int nfq_ip_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 iphdr *iph = (struct iphdr *) pkt->network_header; + struct iphdr *iph = (struct iphdr *) pktb->network_header; - if (!pktb_mangle(pkt, dataoff, match_offset, match_len, - rep_buffer, rep_len)) + if (!pktb_mangle(pktb, dataoff, match_offset, match_len, rep_buffer, + rep_len)) return 0; /* fix IP hdr checksum information */ - iph->tot_len = htons(pkt->len); + iph->tot_len = htons(pktb_tail(pktb) - pktb->network_header); nfq_ip_set_checksum(iph); return 1; } -EXPORT_SYMBOL(nfq_ip_mangle); /** - * nfq_pkt_snprintf_ip - 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 ip: pointer to a valid IPv4 header - * - * This function returns the number of bytes that would have been written in - * case that there is enough room in the buffer. Read snprintf manpage for more - * information to know more about this strange behaviour. + * 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 + * \returns same as snprintf + * \sa **snprintf**(3) */ +EXPORT_SYMBOL int nfq_ip_snprintf(char *buf, size_t size, const struct iphdr *iph) { int ret; @@ -147,7 +188,6 @@ int nfq_ip_snprintf(char *buf, size_t size, const struct iphdr *iph) return ret; } -EXPORT_SYMBOL(nfq_ip_snprintf); /** * @} diff --git a/src/extra/ipv6.c b/src/extra/ipv6.c index 7c5dc9b..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,61 +24,81 @@ /** * \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 + * * @{ */ /** * nfq_ip6_get_hdr - get IPv6 header - * \param pktb: pointer to user-space network packet buffer + * \param pktb: Pointer to user-space network packet buffer * - * This funcion returns NULL if an invalid header is found. On sucess, it - * returns a valid pointer to the header. + * \returns pointer to IPv6 header if a valid header found, else NULL. */ +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 IPv4 header. */ + /* Not enough room for IPv6 header. */ if (pktlen < sizeof(struct ip6_hdr)) return NULL; ip6h = (struct ip6_hdr *)pktb->network_header; /* Not IPv6 packet. */ - if (ip6h->ip6_flow != 0x60) + if ((*(uint8_t *)ip6h & 0xf0) != 0x60) return NULL; return ip6h; } -EXPORT_SYMBOL(nfq_ip6_get_hdr); /** * nfq_ip6_set_transport_header - set transport header pointer for IPv6 packet - * \param pktb: pointer to user-space network packet buffer - * \param ip6h: pointer to IPv6 header - * \param target: protocol number to find transport header (ie. IPPROTO_*) + * \param pktb: Pointer to user-space network packet buffer + * \param ip6h: Pointer to IPv6 header + * \param target: Protocol number to find transport header (ie. IPPROTO_*) * - * This function returns 1 if the protocol has been found and the transport - * header has been set. Otherwise, it returns 0. + * \returns 1 if the protocol has been found and the transport + * header has been set, else 0. */ +EXPORT_SYMBOL int nfq_ip6_set_transport_header(struct pkt_buff *pktb, struct ip6_hdr *ip6h, uint8_t target) { 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,23 +128,56 @@ 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; } -EXPORT_SYMBOL(nfq_ip6_set_transport_header); + +/** + * 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). - * \param ip6_hdr: pointer to a valid IPv6 header. + * \param buf: Pointer to buffer that is used to print the object + * \param size: Size of the buffer (or remaining room in it). + * \param ip6h: Pointer to a valid IPv6 header. + * \returns same as snprintf + * \sa **snprintf**(3) * */ +EXPORT_SYMBOL int nfq_ip6_snprintf(char *buf, size_t size, const struct ip6_hdr *ip6h) { int ret; @@ -133,7 +187,7 @@ int nfq_ip6_snprintf(char *buf, size_t size, const struct ip6_hdr *ip6h) inet_ntop(AF_INET6, &ip6h->ip6_src, src, INET6_ADDRSTRLEN); inet_ntop(AF_INET6, &ip6h->ip6_dst, dst, INET6_ADDRSTRLEN); - ret = snprintf(buf, size, "SRC=%s DST=%s LEN=%Zu TC=0x%X " + ret = snprintf(buf, size, "SRC=%s DST=%s LEN=%zu TC=0x%X " "HOPLIMIT=%u FLOWLBL=%u ", src, dst, ntohs(ip6h->ip6_plen) + sizeof(struct ip6_hdr), @@ -143,7 +197,6 @@ int nfq_ip6_snprintf(char *buf, size_t size, const struct ip6_hdr *ip6h) return ret; } -EXPORT_SYMBOL(nfq_ip6_snprintf); /** * @} diff --git a/src/extra/pktbuff.c b/src/extra/pktbuff.c index 1c15a00..40d2250 100644 --- a/src/extra/pktbuff.c +++ b/src/extra/pktbuff.c @@ -9,6 +9,7 @@ * This code has been sponsored by Vyatta Inc. <http://www.vyatta.com> */ +#include <errno.h> #include <stdlib.h> #include <string.h> /* for memcpy */ #include <stdbool.h> @@ -22,26 +23,84 @@ /** * \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, eg. AF_BRIDGE, AF_INET, AF_INET6, ... + * \param family Indicate what family. Currently supported families are + * AF_BRIDGE, AF_INET & AF_INET6. * \param data Pointer to packet data * \param len Packet length * \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 (in case of 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 a pointer to a new queue handle or NULL on failure. + * \return Pointer to a new userspace packet buffer or NULL on failure. + * \par Errors + * __ENOMEM__ From __calloc__() + * \n + * __EPROTONOSUPPORT__ _family_ was __AF_BRIDGE__ and this is not an IP packet + * (v4 or v6) + * \sa __calloc__(3) */ -struct pkt_buff * -pktb_alloc(int family, void *data, size_t len, size_t extra) +EXPORT_SYMBOL +struct pkt_buff *pktb_alloc(int family, void *data, size_t len, size_t extra) { struct pkt_buff *pktb; void *pkt_data; @@ -54,221 +113,342 @@ 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: - 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: - pktb->network_header = pktb->data + ETH_HLEN; - break; - default: - /* This protocol is unsupported. */ - free(pktb); - return NULL; - } - break; - } - } return pktb; } -EXPORT_SYMBOL(pktb_alloc); /** - * pktb_data - return pointer to the beginning of the packet buffer - * \param pktb Pointer to packet buffer + * pktb_data - get pointer to network packet + * \param pktb Pointer to userspace packet buffer + * \return Pointer to start of network packet data within __pktb__ + * \par + * It is appropriate to use _pktb_data_ as the second argument of + * nfq_nlmsg_verdict_put_pkt() */ +EXPORT_SYMBOL uint8_t *pktb_data(struct pkt_buff *pktb) { return pktb->data; } -EXPORT_SYMBOL(pktb_data); /** - * pktb_len - return length of the packet buffer - * \param pktb Pointer to packet buffer + * pktb_len - get length of packet buffer + * \param pktb Pointer to userspace packet buffer + * \return Length of packet contained within __pktb__ + * \par + * It is appropriate to use _pktb_len_ as the third argument of + * nfq_nlmsg_verdict_put_pkt() */ +EXPORT_SYMBOL uint32_t pktb_len(struct pkt_buff *pktb) { return pktb->len; } -EXPORT_SYMBOL(pktb_len); /** * pktb_free - release packet buffer - * \param pktb Pointer to packet buffer + * \param pktb Pointer to userspace packet buffer */ +EXPORT_SYMBOL void pktb_free(struct pkt_buff *pktb) { free(pktb); } -EXPORT_SYMBOL(pktb_free); /** - * pktb_push - update pointer to the beginning of the packet buffer - * \param pktb Pointer to packet buffer + * \defgroup otherfns Other functions + * + * The library provides a number of other functions which many user-space + * programs will never need. These divide into 2 groups: + * \n + * 1. Functions to get values of members of opaque __struct pktbuff__, described + * below + * + * 2. Internal functions, described in Module __Internal functions__ + * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <libmnl/libmnl.h> +#include <libnetfilter_queue/pktbuff.h> +\endmanonly + * + * @{ + */ + +/** + * \defgroup do_not_use Internal functions + * + * 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 + * + * @{ + */ + +/** + * pktb_push - decrement pointer to packet buffer + * \param pktb Pointer to userspace packet buffer + * \param len Number of bytes to subtract from packet start address */ +EXPORT_SYMBOL void pktb_push(struct pkt_buff *pktb, unsigned int len) { pktb->data -= len; pktb->len += len; } -EXPORT_SYMBOL(pktb_push); /** - * pktb_pull - update pointer to the beginning of the packet buffer - * \param pktb Pointer to packet buffer + * pktb_pull - increment pointer to packet buffer + * \param pktb Pointer to userspace packet buffer + * \param len Number of bytes to add to packet start address */ +EXPORT_SYMBOL void pktb_pull(struct pkt_buff *pktb, unsigned int len) { pktb->data += len; pktb->len -= len; } -EXPORT_SYMBOL(pktb_pull); /** * pktb_put - add extra bytes to the tail of the packet buffer - * \param pktb Pointer to packet buffer + * \param pktb Pointer to userspace packet buffer + * \param len Number of bytes to add to packet tail (and length) */ +EXPORT_SYMBOL void pktb_put(struct pkt_buff *pktb, unsigned int len) { - pktb->tail += len; pktb->len += len; } -EXPORT_SYMBOL(pktb_put); /** * pktb_trim - set new length for this packet buffer - * \param pktb Pointer to packet buffer + * \param pktb Pointer to userspace packet buffer + * \param len New packet length (tail is adjusted to reflect this) */ +EXPORT_SYMBOL void pktb_trim(struct pkt_buff *pktb, unsigned int len) { pktb->len = len; } -EXPORT_SYMBOL(pktb_trim); /** - * pktb_tailroom - get room in bytes in the tail of the packet buffer - * \param pktb Pointer to packet buffer + * @} */ + +/** + * pktb_tailroom - get room available for packet expansion + * \param pktb Pointer to userspace packet buffer + * \return room in bytes after the tail of the packet buffer + * \n + * This starts off as the __extra__ argument to pktb_alloc(). + * Programmers should ensure this __extra__ argument is sufficient for any + * packet mangle, as packet buffers cannot be expanded dynamically. + */ +EXPORT_SYMBOL unsigned int pktb_tailroom(struct pkt_buff *pktb) { return pktb->data_len - pktb->len; } -EXPORT_SYMBOL(pktb_tailroom); /** - * pktb_mac_header - return pointer to layer 2 header (if any) - * \param pktb Pointer to packet buffer + * pktb_mac_header - get address of layer 2 header (if any) + * \param pktb Pointer to userspace packet buffer + * \return Pointer to MAC header or NULL if no such header present. + * \n + * Only packet buffers in family __AF_BRIDGE__ have a non-NULL MAC header. */ +EXPORT_SYMBOL uint8_t *pktb_mac_header(struct pkt_buff *pktb) { return pktb->mac_header; } -EXPORT_SYMBOL(pktb_mac_header); /** - * pktb_network_header - return pointer to layer 3 header - * \param pktb Pointer to packet buffer + * pktb_network_header - get address of layer 3 header + * \param pktb Pointer to userspace packet buffer + * \return Pointer to layer 3 header or NULL if the packet buffer was created + * with an unsupported family */ +EXPORT_SYMBOL uint8_t *pktb_network_header(struct pkt_buff *pktb) { return pktb->network_header; } -EXPORT_SYMBOL(pktb_network_header); /** - * pktb_transport_header - return pointer to layer 4 header (if any) - * \param pktb Pointer to packet buffer + * pktb_transport_header - get address of layer 4 header (if known) + * \param pktb Pointer to userspace packet buffer + * \return Pointer to layer 4 header or NULL if not (yet) set + * \note + * Unlike the lower-level headers, it is the programmer's responsibility to + * create the level 4 (transport) header pointer by caling e.g. + * nfq_ip_set_transport_header() */ +EXPORT_SYMBOL uint8_t *pktb_transport_header(struct pkt_buff *pktb) { return pktb->transport_header; } -EXPORT_SYMBOL(pktb_transport_header); -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; } -int pktb_mangle(struct pkt_buff *pkt, - unsigned int dataoff, - unsigned int match_offset, - unsigned int match_len, - const char *rep_buffer, - unsigned int rep_len) +/** + * pktb_mangle - adjust contents of a packet + * \param pktb Pointer to userspace packet buffer + * \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 + * \param rep_len Length of data you want to use to replace current content + * \returns 1 for success and 0 for failure. Failure will occur if the \b extra + * argument to the pktb_alloc() call that created \b pktb is less than the + * 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(), 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, + int dataoff, + unsigned int match_offset, + unsigned int match_len, + const char *rep_buffer, + unsigned int rep_len) { unsigned char *data; if (rep_len > match_len && - rep_len - match_len > pktb_tailroom(pkt) && - !enlarge_pkt(pkt, rep_len - match_len)) + rep_len - match_len > pktb_tailroom(pktb) && + !enlarge_pkt(pktb, rep_len - match_len)) return 0; - data = pkt->network_header + dataoff; + data = pktb->network_header + dataoff; /* move post-replacement */ memmove(data + match_offset + rep_len, data + match_offset + match_len, - pkt->tail - (pkt->network_header + dataoff + + pktb_tail(pktb) - (pktb->network_header + dataoff + match_offset + match_len)); /* insert data from buffer */ memcpy(data + match_offset, rep_buffer, rep_len); - /* update pkt info */ + /* update packet info */ if (rep_len > match_len) - pktb_put(pkt, rep_len - match_len); + pktb_put(pktb, rep_len - match_len); else - pktb_trim(pkt, pkt->len + rep_len - match_len); + pktb_trim(pktb, pktb->len + rep_len - match_len); - pkt->mangled = true; + pktb->mangled = true; return 1; } -EXPORT_SYMBOL(pktb_mangle); /** - * pktb_mangled - return true if packet has been mangled - * \param pktb Pointer to packet buffer + * pktb_mangled - test whether packet has been mangled + * \param pktb Pointer to userspace packet buffer + * \return __true__ if packet has been mangled (modified), else __false__ + * \par + * When assembling a verdict, it is not necessary to return the contents of + * un-modified packets. Use _pktb_mangled_ to decide whether packet contents + * need to be returned. + */ +EXPORT_SYMBOL +bool pktb_mangled(const struct pkt_buff *pktb) +{ + return pktb->mangled; +} + +/** + * pktb_head_size - get number of bytes needed for a packet buffer + * (control part only) + * \return size of struct pkt_buff */ -bool pktb_mangled(const struct pkt_buff *pkt) + +EXPORT_SYMBOL +size_t pktb_head_size(void) { - return pkt->mangled; + return sizeof(struct pkt_buff); } -EXPORT_SYMBOL(pktb_mangled); /** * @} diff --git a/src/extra/tcp.c b/src/extra/tcp.c index d1cd79d..720afd2 100644 --- a/src/extra/tcp.c +++ b/src/extra/tcp.c @@ -18,46 +18,57 @@ #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) { if (pktb->transport_header == NULL) 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; } -EXPORT_SYMBOL(nfq_tcp_get_hdr); /** * 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) { unsigned int len = tcph->doff * 4; @@ -67,52 +78,82 @@ 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; } -EXPORT_SYMBOL(nfq_tcp_get_payload); /** * 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) */ -unsigned int -nfq_tcp_get_payload_len(struct tcphdr *tcph, struct pkt_buff *pktb) +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); } -EXPORT_SYMBOL(nfq_tcp_get_payload_len); /** - * 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__. */ -void -nfq_tcp_compute_checksum_ipv4(struct tcphdr *tcph, struct iphdr *iph) +EXPORT_SYMBOL +void nfq_tcp_compute_checksum_ipv4(struct tcphdr *tcph, struct iphdr *iph) { /* checksum field in header needs to be zero for calculation. */ tcph->check = 0; - tcph->check = nfq_checksum_tcpudp_ipv4(iph); + tcph->check = nfq_checksum_tcpudp_ipv4(iph, IPPROTO_TCP); } -EXPORT_SYMBOL(nfq_tcp_compute_checksum_ipv4); /** - * 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__. */ -void -nfq_tcp_compute_checksum_ipv6(struct tcphdr *tcph, struct ip6_hdr *ip6h) +EXPORT_SYMBOL +void nfq_tcp_compute_checksum_ipv6(struct tcphdr *tcph, struct ip6_hdr *ip6h) { /* checksum field in header needs to be zero for calculation. */ tcph->check = 0; - tcph->check = nfq_checksum_tcpudp_ipv6(ip6h, tcph); + tcph->check = nfq_checksum_tcpudp_ipv6(ip6h, tcph, IPPROTO_TCP); } -EXPORT_SYMBOL(nfq_tcp_compute_checksum_ipv6); + +/** + * @} + */ /* * The union cast uses a gcc extension to avoid aliasing problems @@ -131,9 +172,12 @@ 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 int nfq_tcp_snprintf(char *buf, size_t size, const struct tcphdr *tcph) { int ret, len = 0; @@ -177,7 +221,6 @@ int nfq_tcp_snprintf(char *buf, size_t size, const struct tcphdr *tcph) return ret; } -EXPORT_SYMBOL(nfq_tcp_snprintf); /** * nfq_tcp_mangle_ipv4 - mangle TCP/IPv4 packet buffer @@ -186,21 +229,25 @@ EXPORT_SYMBOL(nfq_tcp_snprintf); * \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 */ -int -nfq_tcp_mangle_ipv4(struct pkt_buff *pkt, - unsigned int match_offset, unsigned int match_len, - const char *rep_buffer, unsigned int rep_len) +EXPORT_SYMBOL +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; @@ -208,7 +255,45 @@ nfq_tcp_mangle_ipv4(struct pkt_buff *pkt, return 1; } -EXPORT_SYMBOL(nfq_tcp_mangle_ipv4); + +/** + * 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 8c44a66..ede2196 100644 --- a/src/extra/udp.c +++ b/src/extra/udp.c @@ -17,44 +17,58 @@ #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 head: pointer to the beginning of the packet - * \param tail: pointer to the tail of the packet + * \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) { if (pktb->transport_header == NULL) 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; } -EXPORT_SYMBOL(nfq_udp_get_hdr); /** * nfq_udp_get_payload - get the UDP packet payload. - * \param udph: the pointer to the UDP header. - * \param tail: pointer to the tail of the packet + * \param udph: Pointer to UDP header + * \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) { uint16_t len = ntohs(udph->len); @@ -64,83 +78,109 @@ 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); } -EXPORT_SYMBOL(nfq_udp_get_payload); /** * nfq_udp_get_payload_len - get the udp packet payload. - * \param udp: the pointer to the udp header. + * \param udph: Pointer to UDP header + * \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); } -EXPORT_SYMBOL(nfq_udp_get_payload_len); /** - * 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 + * + * Most user-space programs will never need these. + * * - * \returns the checksum of the udp segment. + * \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 + * @{ */ -void -nfq_udp_compute_checksum_ipv4(struct udphdr *udph, struct iphdr *iph) + +/** + * 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) { /* checksum field in header needs to be zero for calculation. */ udph->check = 0; - udph->check = nfq_checksum_tcpudp_ipv4(iph); + udph->check = nfq_checksum_tcpudp_ipv4(iph, IPPROTO_UDP); } -EXPORT_SYMBOL(nfq_udp_compute_checksum_ipv4); /** - * 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__. */ -void -nfq_udp_compute_checksum_ipv6(struct udphdr *udph, struct ip6_hdr *ip6h) +EXPORT_SYMBOL +void nfq_udp_compute_checksum_ipv6(struct udphdr *udph, struct ip6_hdr *ip6h) { /* checksum field in header needs to be zero for calculation. */ udph->check = 0; - udph->check = nfq_checksum_tcpudp_ipv6(ip6h, udph); + udph->check = nfq_checksum_tcpudp_ipv6(ip6h, udph, IPPROTO_UDP); } -EXPORT_SYMBOL(nfq_udp_compute_checksum_ipv6); /** - * nfq_tcp_mangle_ipv4 - mangle TCP/IPv4 packet buffer - * \param pktb: pointer to network packet buffer - * \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 - * - * \note This function recalculates the IPv4 and TCP checksums for you. + * @} */ -int -nfq_udp_mangle_ipv4(struct pkt_buff *pkt, - unsigned int match_offset, unsigned int match_len, - const char *rep_buffer, unsigned int rep_len) + +/** + * 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 + * 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 and UDP lengths and recalculates their + * checksums for you. + */ +EXPORT_SYMBOL +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; @@ -148,22 +188,61 @@ nfq_udp_mangle_ipv4(struct pkt_buff *pkt, return 1; } -EXPORT_SYMBOL(nfq_udp_mangle_ipv4); + +/** + * 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 int nfq_udp_snprintf(char *buf, size_t size, const struct udphdr *udph) { return snprintf(buf, size, "SPT=%u DPT=%u ", htons(udph->source), htons(udph->dest)); } -EXPORT_SYMBOL(nfq_udp_snprintf); /** * @} diff --git a/src/internal.h b/src/internal.h index 558d267..ae849d6 100644 --- a/src/internal.h +++ b/src/internal.h @@ -5,8 +5,7 @@ #include <stdint.h> #include <stdbool.h> #ifdef HAVE_VISIBILITY_HIDDEN -# define __visible __attribute__((visibility("default"))) -# define EXPORT_SYMBOL(x) typeof(x) (x) __visible +# define EXPORT_SYMBOL __attribute__((visibility("default"))) #else # define EXPORT_SYMBOL #endif @@ -15,17 +14,16 @@ struct iphdr; struct ip6_hdr; uint16_t nfq_checksum(uint32_t sum, uint16_t *buf, int size); -uint16_t nfq_checksum_tcpudp_ipv4(struct iphdr *iph); -uint16_t nfq_checksum_tcpudp_ipv6(struct ip6_hdr *ip6h, void *transport_hdr); +uint16_t nfq_checksum_tcpudp_ipv4(struct iphdr *iph, uint16_t protonum); +uint16_t nfq_checksum_tcpudp_ipv6(struct ip6_hdr *ip6h, void *transport_hdr, + uint16_t protonum); struct pkt_buff { uint8_t *mac_header; 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 1702158..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> @@ -39,53 +40,71 @@ * * 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. + * replaces the old ip_queue / libipq mechanism (withdrawn in kernel 3.5). * * libnetfilter_queue homepage is: - * http://netfilter.org/projects/libnetfilter_queue/ + * https://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). + <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 Main Features + * <h1>Main Features</h1> * - receiving queued packets from the kernel nfnetlink_queue subsystem - * - issuing verdicts and/or reinjecting altered packets to the kernel + * - issuing verdicts and possibly reinjecting altered packets to the kernel * nfnetlink_queue subsystem * - * The cinematic is the following: When an iptables rules with target NFQUEUE - * matches, the kernel en-queued the packet in a chained list. It then format - * a nfnetlink message and sends the information (packet data , packet id and - * metadata) via a socket to the software connected to the queue. The software - * can then read the message. + * The cinematic is the following: When an nft rule with action **queue** + * matches, the kernel terminates the current nft chain and enqueues the packet + * in a chained list. It then formats and sends an nfnetlink message containing + * the packet id and whatever information the userspace program configured to + * receive (packet data and/or metadata) via a socket to the userspace program. * - * To remove the packet from the queue, the userspace software must issue a - * verdict asking kernel to accept or drop the packet. Userspace can also alter - * the packet. Verdict can be done in asynchronous manner, as the only needed - * information is the packet id. + * The userspace program must issue a verdict advising the kernel to **accept** + * or **drop** the packet. Either verdict takes the packet off the queue: + * **drop** discards the packet while + * **accept** passes it on to the next chain. + * Userspace can also alter packet contents or metadata (e.g. packet mark, + * contrack mark). Verdict can be done in asynchronous manner, as the only + * needed information is the packet id. * - * When a queue is full, packets that should have been en-queued are dropped by - * kernel instead of being en-queued. + * When a queue is full, packets that should have been enqueued are dropped by + * kernel instead of being enqueued. * - * \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. + * <h1>Git Tree</h1> + * The current development version of libnetfilter_queue can be accessed at + * https://git.netfilter.org/libnetfilter_queue. * - * \section 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 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 - * nf-queue.c source file. + * <h1>Using libnetfilter_queue</h1> * - * Another source of information on libnetfilter_queue usage is the following + * To write your own program using libnetfilter_queue, you should start by + * reading (or, if feasible, compiling and stepping through with *gdb*) + * nf-queue.c source file. + * Simple compile line: + * \verbatim +gcc -g3 -ggdb -Wall -lmnl -lnetfilter_queue -o nf-queue nf-queue.c +\endverbatim + *The doxygen documentation + * \htmlonly +<a class="el" href="group__LibrarySetup.html">LibrarySetup </a> +\endhtmlonly + * \manonly +\fBLibrarySetup\fP\ +\endmanonly + * is Deprecated and + * incompatible with non-deprecated functions. It is hoped to produce a + * corresponding non-deprecated (*Current*) topic soon. + * + * Somewhat outdated but possibly providing some insight into + * libnetfilter_queue usage is the following * 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. @@ -94,7 +113,7 @@ * 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: * @@ -108,6 +127,9 @@ * (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 */ @@ -133,11 +155,10 @@ struct nfq_data { struct nfattr **data; }; -int nfq_errno; -EXPORT_SYMBOL(nfq_errno); +EXPORT_SYMBOL int nfq_errno; /*********************************************************************** - * low level stuff + * low level stuff ***********************************************************************/ static void del_qh(struct nfq_q_handle *qh) @@ -218,22 +239,22 @@ static int __nfq_rcv_pkt(struct nlmsghdr *nlh, struct nfattr *nfa[], /* public interface */ +EXPORT_SYMBOL struct nfnl_handle *nfq_nfnlh(struct nfq_handle *h) { return h->nfnlh; } -EXPORT_SYMBOL(nfq_nfnlh); /** * * \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"); @@ -260,7 +281,7 @@ EXPORT_SYMBOL(nfq_nfnlh); 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: * @@ -277,8 +298,18 @@ EXPORT_SYMBOL(nfq_nfnlh); * 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 + * * @{ */ @@ -294,11 +325,11 @@ EXPORT_SYMBOL(nfq_nfnlh); * over the netlink connection associated with the given queue connection * handle. */ +EXPORT_SYMBOL int nfq_fd(struct nfq_handle *h) { return nfnl_fd(nfq_nfnlh(h)); } -EXPORT_SYMBOL(nfq_fd); /** * @} */ @@ -308,7 +339,7 @@ EXPORT_SYMBOL(nfq_fd); * * 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() @@ -349,6 +380,7 @@ EXPORT_SYMBOL(nfq_fd); * * \return a pointer to a new queue handle or NULL on failure. */ +EXPORT_SYMBOL struct nfq_handle *nfq_open(void) { struct nfnl_handle *nfnlh = nfnl_open(); @@ -366,7 +398,6 @@ struct nfq_handle *nfq_open(void) return qh; } -EXPORT_SYMBOL(nfq_open); /** * @} @@ -377,11 +408,12 @@ EXPORT_SYMBOL(nfq_open); * \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. */ +EXPORT_SYMBOL struct nfq_handle *nfq_open_nfnl(struct nfnl_handle *nfnlh) { struct nfnl_callback pkt_cb = { @@ -398,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 */ @@ -419,7 +451,6 @@ out_free: free(h); return NULL; } -EXPORT_SYMBOL(nfq_open_nfnl); /** * \addtogroup LibrarySetup @@ -427,6 +458,14 @@ EXPORT_SYMBOL(nfq_open_nfnl); * 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 + * * @{ */ @@ -436,18 +475,18 @@ EXPORT_SYMBOL(nfq_open_nfnl); * * 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) { int ret; - + ret = nfnl_close(h->nfnlh); if (ret == 0) free(h); return ret; } -EXPORT_SYMBOL(nfq_close); /** * nfq_bind_pf - bind a nfqueue handler to a given protocol family @@ -460,11 +499,11 @@ EXPORT_SYMBOL(nfq_close); * * \return integer inferior to 0 in case of failure */ +EXPORT_SYMBOL int nfq_bind_pf(struct nfq_handle *h, uint16_t pf) { return __build_send_cfg_msg(h, NFQNL_CFG_CMD_PF_BIND, 0, pf); } -EXPORT_SYMBOL(nfq_bind_pf); /** * nfq_unbind_pf - unbind nfqueue handler from a protocol family @@ -476,11 +515,11 @@ EXPORT_SYMBOL(nfq_bind_pf); * * This call is obsolete, Linux kernels from 3.8 onwards ignore it. */ +EXPORT_SYMBOL int nfq_unbind_pf(struct nfq_handle *h, uint16_t pf) { return __build_send_cfg_msg(h, NFQNL_CFG_CMD_PF_UNBIND, 0, pf); } -EXPORT_SYMBOL(nfq_unbind_pf); /** @@ -503,15 +542,15 @@ EXPORT_SYMBOL(nfq_unbind_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 typedef int nfq_callback(struct nfq_q_handle *qh, - struct nfgenmsg *nfmsg, + struct nfgenmsg *nfmsg, struct nfq_data *nfad, void *data); \endverbatim * @@ -524,10 +563,9 @@ typedef int nfq_callback(struct nfq_q_handle *qh, * The callback should return < 0 to stop processing. */ -struct nfq_q_handle *nfq_create_queue(struct nfq_handle *h, - uint16_t num, - nfq_callback *cb, - void *data) +EXPORT_SYMBOL +struct nfq_q_handle *nfq_create_queue(struct nfq_handle *h, uint16_t num, + nfq_callback *cb, void *data) { int ret; struct nfq_q_handle *qh; @@ -555,7 +593,6 @@ struct nfq_q_handle *nfq_create_queue(struct nfq_handle *h, add_qh(qh); return qh; } -EXPORT_SYMBOL(nfq_create_queue); /** * @} @@ -573,6 +610,7 @@ EXPORT_SYMBOL(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. */ +EXPORT_SYMBOL int nfq_destroy_queue(struct nfq_q_handle *qh) { int ret = __build_send_cfg_msg(qh->h, NFQNL_CFG_CMD_UNBIND, qh->id, 0); @@ -583,7 +621,6 @@ int nfq_destroy_queue(struct nfq_q_handle *qh) return ret; } -EXPORT_SYMBOL(nfq_destroy_queue); /** * nfq_handle_packet - handle a packet received from the nfqueue subsystem @@ -597,11 +634,11 @@ EXPORT_SYMBOL(nfq_destroy_queue); * * \return 0 on success, non-zero on failure. */ +EXPORT_SYMBOL int nfq_handle_packet(struct nfq_handle *h, char *buf, int len) { return nfnl_handle_packet(h->nfnlh, buf, len); } -EXPORT_SYMBOL(nfq_handle_packet); /** * nfq_set_mode - set the amount of packet data that nfqueue copies to userspace @@ -618,8 +655,8 @@ EXPORT_SYMBOL(nfq_handle_packet); * * \return -1 on error; >=0 otherwise. */ -int nfq_set_mode(struct nfq_q_handle *qh, - uint8_t mode, uint32_t range) +EXPORT_SYMBOL +int nfq_set_mode(struct nfq_q_handle *qh, uint8_t mode, uint32_t range) { union { char buf[NFNL_HEADER_LEN @@ -638,13 +675,12 @@ int nfq_set_mode(struct nfq_q_handle *qh, return nfnl_query(qh->h->nfnlh, &u.nmh); } -EXPORT_SYMBOL(nfq_set_mode); /** * 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: * @@ -698,11 +734,18 @@ EXPORT_SYMBOL(nfq_set_mode); flags &= ~NFQA_CFG_F_FAIL_OPEN; err = nfq_set_queue_flags(qh, mask, flags); \endverbatim + * - NFQA_CFG_F_SECCTX: the kernel will dump security context of the socket to + * which each packet belongs. + * + * \warning + * When fragmentation occurs and NFQA_CFG_F_GSO is NOT set then the kernel + * dumps UID/GID and security context fields only for one fragment. To deal + * with this limitation always set NFQA_CFG_F_GSO. * * \return -1 on error with errno set appropriately; =0 otherwise. */ -int nfq_set_queue_flags(struct nfq_q_handle *qh, - uint32_t mask, uint32_t flags) +EXPORT_SYMBOL +int nfq_set_queue_flags(struct nfq_q_handle *qh, uint32_t mask, uint32_t flags) { union { char buf[NFNL_HEADER_LEN @@ -722,7 +765,6 @@ int nfq_set_queue_flags(struct nfq_q_handle *qh, return nfnl_query(qh->h->nfnlh, &u.nmh); } -EXPORT_SYMBOL(nfq_set_queue_flags); /** * nfq_set_queue_maxlen - Set kernel queue maximum length parameter @@ -735,8 +777,8 @@ EXPORT_SYMBOL(nfq_set_queue_flags); * * \return -1 on error; >=0 otherwise. */ -int nfq_set_queue_maxlen(struct nfq_q_handle *qh, - uint32_t queuelen) +EXPORT_SYMBOL +int nfq_set_queue_maxlen(struct nfq_q_handle *qh, uint32_t queuelen) { union { char buf[NFNL_HEADER_LEN @@ -753,7 +795,6 @@ int nfq_set_queue_maxlen(struct nfq_q_handle *qh, return nfnl_query(qh->h->nfnlh, &u.nmh); } -EXPORT_SYMBOL(nfq_set_queue_maxlen); /** * @} @@ -818,19 +859,19 @@ 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); if (ph) - id = ntohl(ph->packet_id); + id = ntohl(ph->packet_id); \endverbatim * * Notifies netfilter of the userspace verdict for the given packet. Every @@ -840,14 +881,14 @@ static int __set_verdict(struct nfq_q_handle *qh, uint32_t id, * * \return -1 on error; >= 0 otherwise. */ +EXPORT_SYMBOL int nfq_set_verdict(struct nfq_q_handle *qh, uint32_t id, - uint32_t verdict, uint32_t data_len, - const unsigned char *buf) + uint32_t verdict, uint32_t data_len, + const unsigned char *buf) { return __set_verdict(qh, id, verdict, 0, 0, data_len, buf, NFQNL_MSG_VERDICT); } -EXPORT_SYMBOL(nfq_set_verdict); /** * nfq_set_verdict2 - like nfq_set_verdict, but you can set the mark. @@ -855,9 +896,10 @@ EXPORT_SYMBOL(nfq_set_verdict); * \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 int nfq_set_verdict2(struct nfq_q_handle *qh, uint32_t id, uint32_t verdict, uint32_t mark, uint32_t data_len, const unsigned char *buf) @@ -865,7 +907,6 @@ int nfq_set_verdict2(struct nfq_q_handle *qh, uint32_t id, return __set_verdict(qh, id, verdict, htonl(mark), 1, data_len, buf, NFQNL_MSG_VERDICT); } -EXPORT_SYMBOL(nfq_set_verdict2); /** * nfq_set_verdict_batch - issue verdicts on several packets at once @@ -874,18 +915,18 @@ EXPORT_SYMBOL(nfq_set_verdict2); * \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. */ +EXPORT_SYMBOL int nfq_set_verdict_batch(struct nfq_q_handle *qh, uint32_t id, - uint32_t verdict) + uint32_t verdict) { return __set_verdict(qh, id, verdict, 0, 0, 0, NULL, NFQNL_MSG_VERDICT_BATCH); } -EXPORT_SYMBOL(nfq_set_verdict_batch); /** * nfq_set_verdict_batch2 - like nfq_set_verdict_batch, but you can set a mark. @@ -894,13 +935,13 @@ EXPORT_SYMBOL(nfq_set_verdict_batch); * \param verdict verdict to return to netfilter (NF_ACCEPT, NF_DROP) * \param mark mark to put on packet */ +EXPORT_SYMBOL int nfq_set_verdict_batch2(struct nfq_q_handle *qh, uint32_t id, - uint32_t verdict, uint32_t mark) + uint32_t verdict, uint32_t mark) { return __set_verdict(qh, id, verdict, htonl(mark), 1, 0, NULL, NFQNL_MSG_VERDICT_BATCH); } -EXPORT_SYMBOL(nfq_set_verdict_batch2); /** * nfq_set_verdict_mark - like nfq_set_verdict, but you can set the mark. @@ -908,7 +949,7 @@ EXPORT_SYMBOL(nfq_set_verdict_batch2); * \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. @@ -916,14 +957,14 @@ EXPORT_SYMBOL(nfq_set_verdict_batch2); * This function is deprecated since it is broken, its use is highly * discouraged. Please, use nfq_set_verdict2 instead. */ +EXPORT_SYMBOL int nfq_set_verdict_mark(struct nfq_q_handle *qh, uint32_t id, - uint32_t verdict, uint32_t mark, - uint32_t data_len, const unsigned char *buf) + uint32_t verdict, uint32_t mark, + uint32_t data_len, const unsigned char *buf) { return __set_verdict(qh, id, verdict, mark, 1, data_len, buf, NFQNL_MSG_VERDICT); } -EXPORT_SYMBOL(nfq_set_verdict_mark); /** * @} @@ -932,11 +973,20 @@ EXPORT_SYMBOL(nfq_set_verdict_mark); /************************************************************* - * 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 + * * @{ */ @@ -958,12 +1008,12 @@ EXPORT_SYMBOL(nfq_set_verdict_mark); } __attribute__ ((packed)); \endverbatim */ +EXPORT_SYMBOL struct nfqnl_msg_packet_hdr *nfq_get_msg_packet_hdr(struct nfq_data *nfad) { return nfnl_get_pointer_to_data(nfad->data, NFQA_PACKET_HDR, struct nfqnl_msg_packet_hdr); } -EXPORT_SYMBOL(nfq_get_msg_packet_hdr); /** * nfq_get_nfmark - get the packet mark @@ -971,11 +1021,11 @@ EXPORT_SYMBOL(nfq_get_msg_packet_hdr); * * \return the netfilter mark currently assigned to the given queued packet. */ +EXPORT_SYMBOL uint32_t nfq_get_nfmark(struct nfq_data *nfad) { return ntohl(nfnl_get_data(nfad->data, NFQA_MARK, uint32_t)); } -EXPORT_SYMBOL(nfq_get_nfmark); /** * nfq_get_timestamp - get the packet timestamp @@ -986,6 +1036,7 @@ EXPORT_SYMBOL(nfq_get_nfmark); * * \return 0 on success, non-zero on failure. */ +EXPORT_SYMBOL int nfq_get_timestamp(struct nfq_data *nfad, struct timeval *tv) { struct nfqnl_msg_packet_timestamp *qpt; @@ -999,7 +1050,6 @@ int nfq_get_timestamp(struct nfq_data *nfad, struct timeval *tv) return 0; } -EXPORT_SYMBOL(nfq_get_timestamp); /** * nfq_get_indev - get the interface that the packet was received through @@ -1012,11 +1062,11 @@ EXPORT_SYMBOL(nfq_get_timestamp); * \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) */ +EXPORT_SYMBOL uint32_t nfq_get_indev(struct nfq_data *nfad) { return ntohl(nfnl_get_data(nfad->data, NFQA_IFINDEX_INDEV, uint32_t)); } -EXPORT_SYMBOL(nfq_get_indev); /** * nfq_get_physindev - get the physical interface that the packet was received @@ -1026,11 +1076,11 @@ EXPORT_SYMBOL(nfq_get_indev); * If the returned index is 0, the packet was locally generated or the * physical input interface is no longer known (ie. POSTROUTING?). */ +EXPORT_SYMBOL uint32_t nfq_get_physindev(struct nfq_data *nfad) { return ntohl(nfnl_get_data(nfad->data, NFQA_IFINDEX_PHYSINDEV, uint32_t)); } -EXPORT_SYMBOL(nfq_get_physindev); /** * nfq_get_outdev - gets the interface that the packet will be routed out @@ -1040,11 +1090,11 @@ EXPORT_SYMBOL(nfq_get_physindev); * returned index is 0, the packet is destined for localhost or the output * interface is not yet known (ie. PREROUTING?). */ +EXPORT_SYMBOL uint32_t nfq_get_outdev(struct nfq_data *nfad) { return ntohl(nfnl_get_data(nfad->data, NFQA_IFINDEX_OUTDEV, uint32_t)); } -EXPORT_SYMBOL(nfq_get_outdev); /** * nfq_get_physoutdev - get the physical interface that the packet output @@ -1053,14 +1103,14 @@ EXPORT_SYMBOL(nfq_get_outdev); * 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 uint32_t nfq_get_physoutdev(struct nfq_data *nfad) { return ntohl(nfnl_get_data(nfad->data, NFQA_IFINDEX_PHYSOUTDEV, uint32_t)); } -EXPORT_SYMBOL(nfq_get_physoutdev); /** * nfq_get_indev_name - get the name of the interface the packet @@ -1069,44 +1119,44 @@ EXPORT_SYMBOL(nfq_get_physoutdev); * \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(); - if (h == NULL) { - perror("nlif_open"); - exit(EXIT_FAILURE); - } + 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); + 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 + * 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); + 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); + nlif_close(h); \endverbatim * */ +EXPORT_SYMBOL int nfq_get_indev_name(struct nlif_handle *nlif_handle, - struct nfq_data *nfad, char *name) + struct nfq_data *nfad, char *name) { uint32_t ifindex = nfq_get_indev(nfad); return nlif_index2name(nlif_handle, ifindex, name); } -EXPORT_SYMBOL(nfq_get_indev_name); /** * nfq_get_physindev_name - get the name of the physical interface the @@ -1118,15 +1168,15 @@ EXPORT_SYMBOL(nfq_get_indev_name); * * 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, struct nfq_data *nfad, char *name) { uint32_t ifindex = nfq_get_physindev(nfad); return nlif_index2name(nlif_handle, ifindex, name); } -EXPORT_SYMBOL(nfq_get_physindev_name); /** * nfq_get_outdev_name - get the name of the physical interface the @@ -1138,15 +1188,15 @@ EXPORT_SYMBOL(nfq_get_physindev_name); * * 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, struct nfq_data *nfad, char *name) { uint32_t ifindex = nfq_get_outdev(nfad); return nlif_index2name(nlif_handle, ifindex, name); } -EXPORT_SYMBOL(nfq_get_outdev_name); /** * nfq_get_physoutdev_name - get the name of the interface the @@ -1158,21 +1208,21 @@ EXPORT_SYMBOL(nfq_get_outdev_name); * * 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_physoutdev_name(struct nlif_handle *nlif_handle, struct nfq_data *nfad, char *name) { uint32_t ifindex = nfq_get_physoutdev(nfad); return nlif_index2name(nlif_handle, ifindex, name); } -EXPORT_SYMBOL(nfq_get_physoutdev_name); /** * nfq_get_packet_hw * - * get hardware address + * get hardware address * * \param nfad Netlink packet data handle passed to callback function * @@ -1191,19 +1241,56 @@ EXPORT_SYMBOL(nfq_get_physoutdev_name); } __attribute__ ((packed)); \endverbatim */ +EXPORT_SYMBOL struct nfqnl_msg_packet_hw *nfq_get_packet_hw(struct nfq_data *nfad) { return nfnl_get_pointer_to_data(nfad->data, NFQA_HWADDR, struct nfqnl_msg_packet_hw); } -EXPORT_SYMBOL(nfq_get_packet_hw); + +/** + * 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 + * UID field set. To deal with this issue always set NFQA_CFG_F_GSO. * * \return 1 if there is a UID available, 0 otherwise. */ +EXPORT_SYMBOL int nfq_get_uid(struct nfq_data *nfad, uint32_t *uid) { if (!nfnl_attr_present(nfad->data, NFQA_UID)) @@ -1212,14 +1299,19 @@ int nfq_get_uid(struct nfq_data *nfad, uint32_t *uid) *uid = ntohl(nfnl_get_data(nfad->data, NFQA_UID, uint32_t)); return 1; } -EXPORT_SYMBOL(nfq_get_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 + * GID field set. To deal with this issue always set NFQA_CFG_F_GSO. * * \return 1 if there is a GID available, 0 otherwise. */ +EXPORT_SYMBOL int nfq_get_gid(struct nfq_data *nfad, uint32_t *gid) { if (!nfnl_attr_present(nfad->data, NFQA_GID)) @@ -1228,16 +1320,19 @@ int nfq_get_gid(struct nfq_data *nfad, uint32_t *gid) *gid = ntohl(nfnl_get_data(nfad->data, NFQA_GID, uint32_t)); return 1; } -EXPORT_SYMBOL(nfq_get_gid); - /** * nfq_get_secctx - get the security context for this packet * \param nfad Netlink packet data handle passed to callback function * \param secdata data to write the security context to * + * \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 + * SECCTX field set. To deal with this issue always set NFQA_CFG_F_GSO. + * * \return -1 on error, otherwise > 0 */ +EXPORT_SYMBOL int nfq_get_secctx(struct nfq_data *nfad, unsigned char **secdata) { if (!nfnl_attr_present(nfad->data, NFQA_SECCTX)) @@ -1251,10 +1346,9 @@ int nfq_get_secctx(struct nfq_data *nfad, unsigned char **secdata) return 0; } -EXPORT_SYMBOL(nfq_get_secctx); /** - * 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 * @@ -1264,6 +1358,7 @@ EXPORT_SYMBOL(nfq_get_secctx); * * \return -1 on error, otherwise > 0. */ +EXPORT_SYMBOL int nfq_get_payload(struct nfq_data *nfad, unsigned char **data) { *data = (unsigned char *) @@ -1273,7 +1368,6 @@ int nfq_get_payload(struct nfq_data *nfad, unsigned char **data) return -1; } -EXPORT_SYMBOL(nfq_get_payload); /** * @} @@ -1292,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 + * * @{ */ @@ -1318,6 +1421,7 @@ do { \ * would have been printed into the buffer (in case that there is enough * room in it). See snprintf() return value for more information. */ +EXPORT_SYMBOL int nfq_snprintf_xml(char *buf, size_t rem, struct nfq_data *tb, int flags) { struct nfqnl_msg_packet_hdr *ph; @@ -1471,7 +1575,6 @@ int nfq_snprintf_xml(char *buf, size_t rem, struct nfq_data *tb, int flags) return len; } -EXPORT_SYMBOL(nfq_snprintf_xml); /** * @} diff --git a/src/nlmsg.c b/src/nlmsg.c index ba28c77..39fd12d 100644 --- a/src/nlmsg.c +++ b/src/nlmsg.c @@ -27,9 +27,58 @@ /** * \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 + * * @{ */ +/** + * 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 (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 + * <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) { struct nfqnl_msg_verdict_hdr vh = { @@ -38,20 +87,61 @@ void nfq_nlmsg_verdict_put(struct nlmsghdr *nlh, int id, int verdict) }; mnl_attr_put(nlh, NFQA_VERDICT_HDR, sizeof(vh), &vh); } -EXPORT_SYMBOL(nfq_nlmsg_verdict_put); +/** + * nfq_nlmsg_verdict_put_mark - Put a packet mark into a netlink message + * \param nlh Pointer to netlink message + * \param mark Value of mark to put + * + * The mark becomes part of the packet's metadata, and may be tested by the *nft + * primary expression* **meta mark** + * \sa __nft__(1) + */ +EXPORT_SYMBOL void nfq_nlmsg_verdict_put_mark(struct nlmsghdr *nlh, uint32_t mark) { mnl_attr_put_u32(nlh, NFQA_MARK, htonl(mark)); } -EXPORT_SYMBOL(nfq_nlmsg_verdict_put_mark); -void -nfq_nlmsg_verdict_put_pkt(struct nlmsghdr *nlh, const void *pkt, uint32_t plen) +EXPORT_SYMBOL +/** + * nfq_nlmsg_verdict_put_pkt - Put replacement packet content into a netlink + * message + * \param nlh Pointer to netlink message + * \param pkt Pointer to start of modified IP datagram + * \param plen Length of modified IP datagram + * + * There is only ever a need to return packet content if it has been modified. + * Usually one of the nfq_*_mangle_* functions does the modifying. + * + * This code snippet uses nfq_udp_mangle_ipv4. See nf-queue.c for + * context: + * \verbatim +// main calls queue_cb (line 64) to process an enqueued packet: + // Extra variables + uint8_t *payload, *rep_data; + unsigned int match_offset, match_len, rep_len; + + // The next line was commented-out (with payload void*) + payload = mnl_attr_get_payload(attr[NFQA_PAYLOAD]); + // Copy data to a packet buffer (allow 255 bytes for mangling). + pktb = pktb_alloc(AF_INET, payload, plen, 255); + // (decide that this packet needs mangling) + nfq_udp_mangle_ipv4(pktb, match_offset, match_len, rep_data, rep_len); + // nfq_udp_mangle_ipv4 updates packet length, no need to track locally + + // Eventually nfq_send_verdict (line 39) gets called + // The received packet may or may not have been modified. + // Add this code before nfq_nlmsg_verdict_put call: + if (pktb_mangled(pktb)) + nfq_nlmsg_verdict_put_pkt(nlh, pktb_data(pktb), pktb_len(pktb)); +\endverbatim + */ +void nfq_nlmsg_verdict_put_pkt(struct nlmsghdr *nlh, const void *pkt, + uint32_t plen) { mnl_attr_put(nlh, NFQA_PAYLOAD, plen, pkt); } -EXPORT_SYMBOL(nfq_nlmsg_verdict_put_pkt); /** * @} @@ -59,17 +149,23 @@ EXPORT_SYMBOL(nfq_nlmsg_verdict_put_pkt); /** * \defgroup nfq_cfg Config helpers + * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <linux/netfilter/nfnetlink_queue.h> +#include <libnetfilter_queue/libnetfilter_queue.h> +\endmanonly + * * @{ */ /** - * nfq_nlmsg_cfg_build_request- build netlink config message - * \param buf Buffer where netlink message is going to be written. - * \param cfg Structure that contains the config parameters. - * \param command nfqueue nfnetlink command. - * - * This function returns a pointer to the netlink message. If something goes - * wrong it returns NULL. + * nfq_nlmsg_cfg_put_cmd Add netlink config command to netlink message + * \param nlh Pointer to netlink message + * \param pf Packet family (e.g. AF_INET) + * \param cmd nfqueue nfnetlink command. * * Possible commands are: * @@ -85,6 +181,7 @@ EXPORT_SYMBOL(nfq_nlmsg_verdict_put_pkt); * given protocol family. Both commands are ignored by Linux kernel 3.8 and * later versions. */ +EXPORT_SYMBOL void nfq_nlmsg_cfg_put_cmd(struct nlmsghdr *nlh, uint16_t pf, uint8_t cmd) { struct nfqnl_msg_config_cmd command = { @@ -93,8 +190,14 @@ void nfq_nlmsg_cfg_put_cmd(struct nlmsghdr *nlh, uint16_t pf, uint8_t cmd) }; mnl_attr_put(nlh, NFQA_CFG_CMD, sizeof(command), &command); } -EXPORT_SYMBOL(nfq_nlmsg_cfg_put_cmd); +/** + * nfq_nlmsg_cfg_put_params Add parameter to netlink message + * \param nlh Pointer to netlink message + * \param mode one of NFQNL_COPY_NONE, NFQNL_COPY_META or NFQNL_COPY_PACKET + * \param range value of parameter + */ +EXPORT_SYMBOL void nfq_nlmsg_cfg_put_params(struct nlmsghdr *nlh, uint8_t mode, int range) { struct nfqnl_msg_config_params params = { @@ -103,13 +206,17 @@ void nfq_nlmsg_cfg_put_params(struct nlmsghdr *nlh, uint8_t mode, int range) }; mnl_attr_put(nlh, NFQA_CFG_PARAMS, sizeof(params), ¶ms); } -EXPORT_SYMBOL(nfq_nlmsg_cfg_put_params); +/** + * nfq_nlmsg_cfg_put_qmaxlen Add queue maximum length to netlink message + * \param nlh Pointer to netlink message + * \param queue_maxlen Maximum queue length + */ +EXPORT_SYMBOL void nfq_nlmsg_cfg_put_qmaxlen(struct nlmsghdr *nlh, uint32_t queue_maxlen) { mnl_attr_put_u32(nlh, NFQA_CFG_QUEUE_MAXLEN, htonl(queue_maxlen)); } -EXPORT_SYMBOL(nfq_nlmsg_cfg_put_qmaxlen); /** * @} @@ -117,6 +224,15 @@ EXPORT_SYMBOL(nfq_nlmsg_cfg_put_qmaxlen); /** * \defgroup nlmsg Netlink message helper functions + * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <linux/netfilter/nfnetlink_queue.h> +#include <libnetfilter_queue/libnetfilter_queue.h> +\endmanonly + * * @{ */ @@ -137,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: @@ -165,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; @@ -172,19 +288,95 @@ static int nfq_pkt_parse_attr_cb(const struct nlattr *attr, void *data) } /** - * nfq_pkt_parse - set packet attributes from netlink message - * \param nlh netlink message that you want to read. - * \param pkt pointer to the packet to set. - * - * This function returns MNL_CB_ERROR if any error occurs, or MNL_CB_OK on - * success. + * nfq_nlmsg_parse - set packet attributes from netlink message + * \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) { return mnl_attr_parse(nlh, sizeof(struct nfgenmsg), nfq_pkt_parse_attr_cb, attr); } -EXPORT_SYMBOL(nfq_nlmsg_parse); + +/** + * 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; +} /** * @} |