diff options
Diffstat (limited to 'src/extra')
-rw-r--r-- | src/extra/checksum.c | 19 | ||||
-rw-r--r-- | src/extra/icmp.c | 57 | ||||
-rw-r--r-- | src/extra/ipv4.c | 77 | ||||
-rw-r--r-- | src/extra/ipv6.c | 68 | ||||
-rw-r--r-- | src/extra/pktbuff.c | 181 | ||||
-rw-r--r-- | src/extra/tcp.c | 129 | ||||
-rw-r--r-- | src/extra/udp.c | 132 |
7 files changed, 528 insertions, 135 deletions
diff --git a/src/extra/checksum.c b/src/extra/checksum.c index 42389aa..33480af 100644 --- a/src/extra/checksum.c +++ b/src/extra/checksum.c @@ -17,6 +17,7 @@ #include <netinet/ip6.h> #include <netinet/tcp.h> +#include <linux/netfilter/nfnetlink_queue.h> #include <libnetfilter_queue/libnetfilter_queue.h> #include "internal.h" @@ -62,25 +63,21 @@ uint16_t nfq_checksum_tcpudp_ipv6(struct ip6_hdr *ip6h, void *transport_hdr, uint16_t protonum) { uint32_t sum = 0; - uint32_t hdr_len = (uint32_t *)transport_hdr - (uint32_t *)ip6h; - uint32_t len = ip6h->ip6_plen - hdr_len; + uint32_t hdr_len = (uint8_t *)transport_hdr - (uint8_t *)ip6h; + /* Allow for extra headers before the UDP header */ + /* TODO: Deal with routing headers */ + uint32_t len = ntohs(ip6h->ip6_plen) - (hdr_len - sizeof *ip6h); uint8_t *payload = (uint8_t *)ip6h + hdr_len; int i; for (i=0; i<8; i++) { - sum += (ip6h->ip6_src.s6_addr16[i] >> 16) & 0xFFFF; - sum += (ip6h->ip6_src.s6_addr16[i]) & 0xFFFF; + sum += (ip6h->ip6_src.s6_addr16[i]); } for (i=0; i<8; i++) { - sum += (ip6h->ip6_dst.s6_addr16[i] >> 16) & 0xFFFF; - sum += (ip6h->ip6_dst.s6_addr16[i]) & 0xFFFF; + sum += (ip6h->ip6_dst.s6_addr16[i]); } sum += htons(protonum); - sum += htons(ip6h->ip6_plen); + sum += htons(len); return nfq_checksum(sum, (uint16_t *)payload, len); } - -/** - * @} - */ diff --git a/src/extra/icmp.c b/src/extra/icmp.c new file mode 100644 index 0000000..eaade7b --- /dev/null +++ b/src/extra/icmp.c @@ -0,0 +1,57 @@ +/* + * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This code has been sponsored by Vyatta Inc. <http://www.vyatta.com> + */ + +#include <stdio.h> +#define _GNU_SOURCE +#include <netinet/ip_icmp.h> + +#include <libnetfilter_queue/libnetfilter_queue_icmp.h> + +#include "internal.h" + +/** + * \defgroup icmp ICMP helper functions + * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <libmnl/libmnl.h> +#include <libnetfilter_queue/libnetfilter_queue_icmp.h> +\endmanonly + * + * @{ + */ + +/** + * nfq_icmp_get_hdr - get the ICMP header. + * \param pktb: pointer to user-space network packet buffer + * \returns validated pointer to the ICMP header or NULL if the ICMP header was + * not set or if a minimal length check fails. + * \note You have to call nfq_ip_set_transport_header() or + * nfq_ip6_set_transport_header() first to set the ICMP header. + */ +EXPORT_SYMBOL +struct icmphdr *nfq_icmp_get_hdr(struct pkt_buff *pktb) +{ + if (pktb->transport_header == NULL) + return NULL; + + /* No room for the ICMP header. */ + if (pktb_tail(pktb) - pktb->transport_header < sizeof(struct icmphdr)) + return NULL; + + return (struct icmphdr *)pktb->transport_header; +} + +/** + * @} + */ diff --git a/src/extra/ipv4.c b/src/extra/ipv4.c index c03f23f..58fb471 100644 --- a/src/extra/ipv4.c +++ b/src/extra/ipv4.c @@ -14,6 +14,7 @@ #include <arpa/inet.h> #include <netinet/ip.h> +#include <linux/netfilter/nfnetlink_queue.h> #include <libnetfilter_queue/libnetfilter_queue.h> #include <libnetfilter_queue/libnetfilter_queue_ipv4.h> #include <libnetfilter_queue/pktbuff.h> @@ -22,21 +23,34 @@ /** * \defgroup ipv4 IPv4 helper functions + * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <libmnl/libmnl.h> +#include <libnetfilter_queue/libnetfilter_queue_ipv4.h> +\endmanonly + * * @{ */ /** - * nfq_ip_get_hdr - get IPv4 header + * nfq_ip_get_hdr - get the IPv4 header * \param pktb: Pointer to user-space network packet buffer + * \returns validated pointer to the IPv4 header or NULL if IP is malformed or + * not version 4 * - * This funcion returns NULL if the IPv4 is malformed or the protocol version - * is not 4. On success, it returns a valid pointer to the IPv4 header. + * Many programs will not need to call this function. A possible use is to + * determine the layer 4 protocol. The validation is that the buffer is big + * enough for the declared lengths in the header, i.e. an extra check for packet + * truncation. */ EXPORT_SYMBOL struct iphdr *nfq_ip_get_hdr(struct pkt_buff *pktb) { struct iphdr *iph; - unsigned int pktlen = pktb->tail - pktb->network_header; + unsigned int pktlen = pktb_tail(pktb) - pktb->network_header; /* Not enough room for IPv4 header. */ if (pktlen < sizeof(struct iphdr)) @@ -56,13 +70,14 @@ struct iphdr *nfq_ip_get_hdr(struct pkt_buff *pktb) } /** - * nfq_ip_set_transport_header - set transport header + * nfq_ip_set_transport_header - set the \b transport_header field in \b pktb * \param pktb: Pointer to user-space network packet buffer * \param iph: Pointer to the IPv4 header - * - * Sets the \b transport_header field in \b pktb - * - * Level 4 helper functions need this to be set. + * \returns 0 on success or -1 if a minimal validation check fails + * \note + * Most programs should call __nfq_ip_set_transport_header__ as soon as + * possible, since most layer 4 helper functions assume the + * \b transport_header field is valid. */ EXPORT_SYMBOL int nfq_ip_set_transport_header(struct pkt_buff *pktb, struct iphdr *iph) @@ -78,11 +93,29 @@ int nfq_ip_set_transport_header(struct pkt_buff *pktb, struct iphdr *iph) } /** + * \defgroup ip_internals Internal IP functions + * + * Most user-space programs will never need these. + * + * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <libmnl/libmnl.h> +#include <libnetfilter_queue/libnetfilter_queue_ipv4.h> +\endmanonly + * + * @{ + */ + +/** * nfq_ip_set_checksum - set IPv4 checksum * \param iph: Pointer to the IPv4 header - * - * \note Call to this function if you modified the IPv4 header to update the - * checksum. + * \note + * nfq_ip_mangle() invokes this function. + * As long as developers always use the appropriate mangler for the layer being + * mangled, there is no need to call __nfq_ip_set_checksum__. */ EXPORT_SYMBOL void nfq_ip_set_checksum(struct iphdr *iph) @@ -94,16 +127,20 @@ void nfq_ip_set_checksum(struct iphdr *iph) } /** + * @} + */ + +/** * nfq_ip_mangle - mangle IPv4 packet buffer * \param pktb: Pointer to user-space network packet buffer - * \param dataoff: Offset to layer 4 header + * \param dataoff: Offset to layer 4 header, or zero to mangle IP header * \param match_offset: Offset to content that you want to mangle * \param match_len: Length of the existing content you want to mangle * \param rep_buffer: Pointer to data you want to use to replace current content * \param rep_len: Length of data you want to use to replace current content * \returns 1 for success and 0 for failure. See pktb_mangle() for failure case - * \note This function updates the IPv4 length and recalculates the IPv4 - * checksum (if necessary) + * \note This function updates the IPv4 length if necessary and recalculates the + * IPv4 checksum. */ EXPORT_SYMBOL int nfq_ip_mangle(struct pkt_buff *pktb, unsigned int dataoff, @@ -117,21 +154,19 @@ int nfq_ip_mangle(struct pkt_buff *pktb, unsigned int dataoff, return 0; /* fix IP hdr checksum information */ - iph->tot_len = htons(pktb->tail - pktb->network_header); + iph->tot_len = htons(pktb_tail(pktb) - pktb->network_header); nfq_ip_set_checksum(iph); return 1; } /** - * nfq_pkt_snprintf_ip - print IPv4 header into buffer in iptables LOG format + * nfq_ip_snprintf - print IPv4 header into buffer in iptables LOG format * \param buf: Pointer to buffer that will be used to print the header * \param size: Size of the buffer (or remaining room in it) * \param iph: Pointer to a valid IPv4 header - * - * This function returns the number of bytes written (excluding the - * string-terminating NUL) *assuming sufficient room in the buffer*. - * Read the snprintf manpage for more information about this strange behaviour. + * \returns same as snprintf + * \sa **snprintf**(3) */ EXPORT_SYMBOL int nfq_ip_snprintf(char *buf, size_t size, const struct iphdr *iph) diff --git a/src/extra/ipv6.c b/src/extra/ipv6.c index f685b3b..fd8ebc4 100644 --- a/src/extra/ipv6.c +++ b/src/extra/ipv6.c @@ -15,6 +15,7 @@ #include <arpa/inet.h> #include <netinet/ip6.h> +#include <linux/netfilter/nfnetlink_queue.h> #include <libnetfilter_queue/libnetfilter_queue.h> #include <libnetfilter_queue/libnetfilter_queue_ipv6.h> #include <libnetfilter_queue/pktbuff.h> @@ -23,6 +24,17 @@ /** * \defgroup ipv6 IPv6 helper functions + * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <arpa/inet.h> +#include <linux/netfilter/nfnetlink_queue.h> +#include <libnetfilter_queue/libnetfilter_queue.h> +#include <libnetfilter_queue/libnetfilter_queue_ipv6.h> +\endmanonly + * * @{ */ @@ -36,7 +48,7 @@ EXPORT_SYMBOL struct ip6_hdr *nfq_ip6_get_hdr(struct pkt_buff *pktb) { struct ip6_hdr *ip6h; - unsigned int pktlen = pktb->tail - pktb->network_header; + unsigned int pktlen = pktb_tail(pktb) - pktb->network_header; /* Not enough room for IPv6 header. */ if (pktlen < sizeof(struct ip6_hdr)) @@ -67,17 +79,26 @@ int nfq_ip6_set_transport_header(struct pkt_buff *pktb, struct ip6_hdr *ip6h, uint8_t nexthdr = ip6h->ip6_nxt; uint8_t *cur = (uint8_t *)ip6h + sizeof(struct ip6_hdr); - while (nexthdr != target) { + while (nexthdr == IPPROTO_HOPOPTS || + nexthdr == IPPROTO_ROUTING || + nexthdr == IPPROTO_FRAGMENT || + nexthdr == IPPROTO_AH || + nexthdr == IPPROTO_NONE || + nexthdr == IPPROTO_DSTOPTS) { struct ip6_ext *ip6_ext; uint32_t hdrlen; + /* Extension header was requested, we're done. */ + if (nexthdr == target) + break; + /* No more extensions, we're done. */ if (nexthdr == IPPROTO_NONE) { cur = NULL; break; } /* No room for extension, bad packet. */ - if (pktb->tail - cur < sizeof(struct ip6_ext)) { + if (pktb_tail(pktb) - cur < sizeof(struct ip6_ext)) { cur = NULL; break; } @@ -87,16 +108,16 @@ int nfq_ip6_set_transport_header(struct pkt_buff *pktb, struct ip6_hdr *ip6h, uint16_t *frag_off; /* No room for full fragment header, bad packet. */ - if (pktb->tail - cur < sizeof(struct ip6_frag)) { + if (pktb_tail(pktb) - cur < sizeof(struct ip6_frag)) { cur = NULL; break; } - frag_off = (uint16_t *)cur + - offsetof(struct ip6_frag, ip6f_offlg); + frag_off = (uint16_t *)(cur + + offsetof(struct ip6_frag, ip6f_offlg)); /* Fragment offset is only 13 bits long. */ - if (htons(*frag_off & ~0x7)) { + if (htons(*frag_off) & ~0x7) { /* Not the first fragment, it does not contain * any headers. */ @@ -107,16 +128,47 @@ int nfq_ip6_set_transport_header(struct pkt_buff *pktb, struct ip6_hdr *ip6h, } else if (nexthdr == IPPROTO_AH) hdrlen = (ip6_ext->ip6e_len + 2) << 2; else - hdrlen = ip6_ext->ip6e_len; + hdrlen = (ip6_ext->ip6e_len + 1) << 3; nexthdr = ip6_ext->ip6e_nxt; cur += hdrlen; } + if (nexthdr != target) + cur = NULL; pktb->transport_header = cur; return cur ? 1 : 0; } /** + * nfq_ip6_mangle - mangle IPv6 packet buffer + * \param pktb: Pointer to user-space network packet buffer + * \param dataoff: Offset to layer 4 header + * \param match_offset: Offset to content that you want to mangle + * \param match_len: Length of the existing content you want to mangle + * \param rep_buffer: Pointer to data you want to use to replace current content + * \param rep_len: Length of data you want to use to replace current content + * \returns 1 for success and 0 for failure. See pktb_mangle() for failure case + * \note This function updates the IPv6 length (if necessary) + */ +EXPORT_SYMBOL +int nfq_ip6_mangle(struct pkt_buff *pktb, unsigned int dataoff, + unsigned int match_offset, unsigned int match_len, + const char *rep_buffer, unsigned int rep_len) +{ + struct ip6_hdr *ip6h = (struct ip6_hdr *)pktb->network_header; + + if (!pktb_mangle(pktb, dataoff, match_offset, match_len, rep_buffer, + rep_len)) + return 0; + + /* Fix IPv6 hdr length information */ + ip6h->ip6_plen = + htons(pktb_tail(pktb) - pktb->network_header - sizeof *ip6h); + + return 1; +} + +/** * nfq_ip6_snprintf - print IPv6 header into one buffer in iptables LOG format * \param buf: Pointer to buffer that is used to print the object * \param size: Size of the buffer (or remaining room in it). diff --git a/src/extra/pktbuff.c b/src/extra/pktbuff.c index 26d7ca8..40d2250 100644 --- a/src/extra/pktbuff.c +++ b/src/extra/pktbuff.c @@ -23,12 +23,58 @@ /** * \defgroup pktbuff User-space network packet buffer * - * This library provides the user-space network packet buffer. This abstraction - * is strongly inspired by Linux kernel network buffer, the so-called sk_buff. + * These functions provide the user-space network packet buffer. + * This abstraction is strongly inspired by Linux kernel network buffer, + * the so-called sk_buff. + * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <libmnl/libmnl.h> +#include <libnetfilter_queue/pktbuff.h> +\endmanonly * * @{ */ +static int __pktb_setup(int family, struct pkt_buff *pktb) +{ + struct ethhdr *ethhdr; + + switch (family) { + case AF_INET: + case AF_INET6: + pktb->network_header = pktb->data; + break; + case AF_BRIDGE: + ethhdr = (struct ethhdr *)pktb->data; + pktb->mac_header = pktb->data; + + switch(ethhdr->h_proto) { + case ETH_P_IP: + case ETH_P_IPV6: + pktb->network_header = pktb->data + ETH_HLEN; + break; + default: + /* This protocol is unsupported. */ + errno = EPROTONOSUPPORT; + return -1; + } + break; + } + + return 0; +} + +static void pktb_setup_metadata(struct pkt_buff *pktb, void *pkt_data, + size_t len, size_t extra) +{ + pktb->len = len; + pktb->data_len = len + extra; + pktb->data = pkt_data; +} + /** * pktb_alloc - allocate a new packet buffer * \param family Indicate what family. Currently supported families are @@ -38,7 +84,12 @@ * \param extra Extra memory in the tail to be allocated (for mangling) * * This function returns a packet buffer that contains the packet data and - * some extra memory room in the tail (if requested). + * some extra memory room in the tail (if requested). This function copies + * the memory area provided as a pointer to packet data into the packet buffer + * structure. + * + * The extra length provides extra packet data room at the tail of the packet + * buffer in case you need to mangle it. * * \return Pointer to a new userspace packet buffer or NULL on failure. * \par Errors @@ -62,37 +113,44 @@ struct pkt_buff *pktb_alloc(int family, void *data, size_t len, size_t extra) pkt_data = (uint8_t *)pktb + sizeof(struct pkt_buff); memcpy(pkt_data, data, len); - pktb->len = len; - pktb->data_len = len + extra; + pktb_setup_metadata(pktb, pkt_data, len, extra); - pktb->head = pkt_data; - pktb->data = pkt_data; - pktb->tail = pktb->head + len; + if (__pktb_setup(family, pktb) < 0) { + free(pktb); + return NULL; + } - switch(family) { - case AF_INET: - case AF_INET6: - pktb->network_header = pktb->data; - break; - case AF_BRIDGE: { - struct ethhdr *ethhdr = (struct ethhdr *)pktb->data; + return pktb; +} - pktb->mac_header = pktb->data; +/** + * pktb_setup_raw - set up a packet buffer from memory area + * \param pktb Pointer to memory of length pktb_head_size() bytes + * \param family Supported families are AF_BRIDGE, AF_INET & AF_INET6. + * \param data Pointer to packet data + * \param len Packet data length + * \param extra Extra memory available after packet data (for mangling). + * + * Use this function to set up a packet buffer from a memory area, minimum size + * of such memory area must be pktb_head_size(). This function attaches the + * packet data that is provided to the packet buffer (data is not copied). Use + * this function as an alternative to the pktb_alloc() interface for more + * control on memory management. + * + * \return Pointer to a new userspace packet buffer or NULL on failure. + * \par Errors + * __EPROTONOSUPPORT__ _family_ was __AF_BRIDGE__ and this is not an IP packet + * (v4 or v6) + */ +EXPORT_SYMBOL +struct pkt_buff *pktb_setup_raw(void *pktb, int family, void *data, + size_t len, size_t extra) +{ + memset(pktb, 0, sizeof (struct pkt_buff)); + pktb_setup_metadata(pktb, data, len, extra); + if (__pktb_setup(family, pktb) < 0) + pktb = NULL; - switch(ethhdr->h_proto) { - case ETH_P_IP: - case ETH_P_IPV6: - pktb->network_header = pktb->data + ETH_HLEN; - break; - default: - /* This protocol is unsupported. */ - errno = EPROTONOSUPPORT; - free(pktb); - return NULL; - } - break; - } - } return pktb; } @@ -111,7 +169,7 @@ uint8_t *pktb_data(struct pkt_buff *pktb) } /** - * pktb_len - return length of the packet buffer + * pktb_len - get length of packet buffer * \param pktb Pointer to userspace packet buffer * \return Length of packet contained within __pktb__ * \par @@ -142,21 +200,37 @@ void pktb_free(struct pkt_buff *pktb) * \n * 1. Functions to get values of members of opaque __struct pktbuff__, described * below - * \n + * * 2. Internal functions, described in Module __Internal functions__ * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <libmnl/libmnl.h> +#include <libnetfilter_queue/pktbuff.h> +\endmanonly + * * @{ */ /** - * \defgroup uselessfns Internal functions + * \defgroup do_not_use Internal functions * - * \warning Do not use these functions. Instead, always use the mangle + * Do not use these functions. Instead, always use the mangle * function appropriate to the level at which you are working. * \n * pktb_mangle() uses all the below functions except _pktb_pull_, which is not * used by anything. * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <libmnl/libmnl.h> +#include <libnetfilter_queue/pktbuff.h> +\endmanonly + * * @{ */ @@ -192,7 +266,6 @@ void pktb_pull(struct pkt_buff *pktb, unsigned int len) EXPORT_SYMBOL void pktb_put(struct pkt_buff *pktb, unsigned int len) { - pktb->tail += len; pktb->len += len; } @@ -205,7 +278,6 @@ EXPORT_SYMBOL void pktb_trim(struct pkt_buff *pktb, unsigned int len) { pktb->len = len; - pktb->tail = pktb->head + len; } /** @@ -271,26 +343,25 @@ uint8_t *pktb_transport_header(struct pkt_buff *pktb) * @} */ -static int pktb_expand_tail(struct pkt_buff *pkt, int extra) +static int pktb_expand_tail(struct pkt_buff *pktb, int extra) { /* No room in packet, cannot mangle it. We don't support dynamic * reallocation. Instead, increase the size of the extra room in * the tail in pktb_alloc. */ - if (pkt->len + extra > pkt->data_len) + if (pktb->len + extra > pktb->data_len) return 0; - pkt->len += extra; - pkt->tail = pkt->tail + extra; + pktb->len += extra; return 1; } -static int enlarge_pkt(struct pkt_buff *pkt, unsigned int extra) +static int enlarge_pkt(struct pkt_buff *pktb, unsigned int extra) { - if (pkt->len + extra > 65535) + if (pktb->len + extra > 65535) return 0; - if (!pktb_expand_tail(pkt, extra - pktb_tailroom(pkt))) + if (!pktb_expand_tail(pktb, extra - pktb_tailroom(pktb))) return 0; return 1; @@ -299,8 +370,10 @@ static int enlarge_pkt(struct pkt_buff *pkt, unsigned int extra) /** * pktb_mangle - adjust contents of a packet * \param pktb Pointer to userspace packet buffer - * \param dataoff Offset to layer 4 header. Specify zero to access layer 3 (IP) - * header (layer 2 for family \b AF_BRIDGE) + * \param dataoff Supplementary offset, usually offset from layer 3 (IP) header + * to the layer 4 (TCP or UDP) header. Specify zero to access the layer 3 + * header. If \b pktb was created in family \b AF_BRIDGE, specify + * \b -ETH_HLEN (a negative offset) to access the layer 2 (MAC) header. * \param match_offset Further offset to content that you want to mangle * \param match_len Length of the existing content you want to mangle * \param rep_buffer Pointer to data you want to use to replace current content @@ -310,13 +383,13 @@ static int enlarge_pkt(struct pkt_buff *pkt, unsigned int extra) * excess of \b rep_len over \b match_len \warning pktb_mangle does not update any checksums. Developers should use the appropriate mangler for the protocol level: nfq_ip_mangle(), - nfq_tcp_mangle_ipv4() or nfq_udp_mangle_ipv4(). IPv6 versions are planned. + nfq_tcp_mangle_ipv4(), nfq_udp_mangle_ipv4() or IPv6 variants. \n It is appropriate to use pktb_mangle to change the MAC header. */ EXPORT_SYMBOL int pktb_mangle(struct pkt_buff *pktb, - unsigned int dataoff, + int dataoff, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, @@ -334,7 +407,7 @@ int pktb_mangle(struct pkt_buff *pktb, /* move post-replacement */ memmove(data + match_offset + rep_len, data + match_offset + match_len, - pktb->tail - (pktb->network_header + dataoff + + pktb_tail(pktb) - (pktb->network_header + dataoff + match_offset + match_len)); /* insert data from buffer */ @@ -366,5 +439,17 @@ bool pktb_mangled(const struct pkt_buff *pktb) } /** + * pktb_head_size - get number of bytes needed for a packet buffer + * (control part only) + * \return size of struct pkt_buff + */ + +EXPORT_SYMBOL +size_t pktb_head_size(void) +{ + return sizeof(struct pkt_buff); +} + +/** * @} */ diff --git a/src/extra/tcp.c b/src/extra/tcp.c index 136d7ea..720afd2 100644 --- a/src/extra/tcp.c +++ b/src/extra/tcp.c @@ -18,27 +18,36 @@ #define _GNU_SOURCE #include <netinet/tcp.h> +#include <linux/netfilter/nfnetlink_queue.h> #include <libnetfilter_queue/libnetfilter_queue.h> #include <libnetfilter_queue/libnetfilter_queue_tcp.h> #include <libnetfilter_queue/libnetfilter_queue_ipv4.h> +#include <libnetfilter_queue/libnetfilter_queue_ipv6.h> #include <libnetfilter_queue/pktbuff.h> #include "internal.h" /** * \defgroup tcp TCP helper functions + * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <libmnl/libmnl.h> +#include <libnetfilter_queue/libnetfilter_queue_tcp.h> +\endmanonly + * * @{ */ /** - * nfq_tcp_get - get the TCP header + * nfq_tcp_get_hdr - get the TCP header * \param pktb: pointer to user-space network packet buffer - * - * This function returns NULL if an invalid TCP header is found. On success, - * it returns the TCP header. - * - * \note You have to call nfq_ip_set_transport_header or - * nfq_ip6_set_transport_header first to access the TCP header. + * \returns validated pointer to the TCP header or NULL if the TCP header was + * not set or if a minimal length check fails. + * \note You have to call nfq_ip_set_transport_header() or + * nfq_ip6_set_transport_header() first to set the TCP header. */ EXPORT_SYMBOL struct tcphdr *nfq_tcp_get_hdr(struct pkt_buff *pktb) @@ -47,7 +56,7 @@ struct tcphdr *nfq_tcp_get_hdr(struct pkt_buff *pktb) return NULL; /* No room for the TCP header. */ - if (pktb->tail - pktb->transport_header < sizeof(struct tcphdr)) + if (pktb_tail(pktb) - pktb->transport_header < sizeof(struct tcphdr)) return NULL; return (struct tcphdr *)pktb->transport_header; @@ -57,6 +66,7 @@ struct tcphdr *nfq_tcp_get_hdr(struct pkt_buff *pktb) * nfq_tcp_get_payload - get the TCP packet payload * \param tcph: pointer to the TCP header * \param pktb: pointer to user-space network packet buffer + * \returns Pointer to the TCP payload, or NULL if malformed TCP packet. */ EXPORT_SYMBOL void *nfq_tcp_get_payload(struct tcphdr *tcph, struct pkt_buff *pktb) @@ -68,7 +78,7 @@ void *nfq_tcp_get_payload(struct tcphdr *tcph, struct pkt_buff *pktb) return NULL; /* malformed TCP data offset. */ - if (pktb->transport_header + len > pktb->tail) + if (pktb->transport_header + len > pktb_tail(pktb)) return NULL; return pktb->transport_header + len; @@ -78,17 +88,42 @@ void *nfq_tcp_get_payload(struct tcphdr *tcph, struct pkt_buff *pktb) * nfq_tcp_get_payload_len - get the tcp packet payload * \param tcph: pointer to the TCP header * \param pktb: pointer to user-space network packet buffer + * \returns Length of TCP payload (user data) */ EXPORT_SYMBOL unsigned int nfq_tcp_get_payload_len(struct tcphdr *tcph, struct pkt_buff *pktb) { - return pktb->tail - pktb->transport_header; + return pktb_tail(pktb) - pktb->transport_header - (tcph->doff * 4); } /** - * nfq_tcp_set_checksum_ipv4 - computes IPv4/TCP packet checksum + * \defgroup tcp_internals Internal TCP functions + * + * Most user-space programs will never need these. + * + * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <netinet/tcp.h> +#include <libnetfilter_queue/libnetfilter_queue_tcp.h> +\endmanonly + * + * @{ + */ + +/** + * nfq_tcp_compute_checksum_ipv4 - computes IPv4/TCP packet checksum * \param tcph: pointer to the TCP header * \param iph: pointer to the IPv4 header + * \note + * nfq_tcp_mangle_ipv4() invokes this function. + * As long as developers always use __nfq_tcp_mangle_ipv4__ when changing the + * content of a TCP message, there is no need to call + * __nfq_tcp_compute_checksum_ipv4__. */ EXPORT_SYMBOL void nfq_tcp_compute_checksum_ipv4(struct tcphdr *tcph, struct iphdr *iph) @@ -99,9 +134,14 @@ void nfq_tcp_compute_checksum_ipv4(struct tcphdr *tcph, struct iphdr *iph) } /** - * nfq_tcp_set_checksum_ipv6 - computes IPv6/TCP packet checksum + * nfq_tcp_compute_checksum_ipv6 - computes IPv6/TCP packet checksum * \param tcph: pointer to the TCP header - * \param iph: pointer to the IPv6 header + * \param ip6h: pointer to the IPv6 header + * \note + * nfq_tcp_mangle_ipv6() invokes this function. + * As long as developers always use __nfq_tcp_mangle_ipv6__ when changing the + * content of a TCP message, there is no need to call + * __nfq_tcp_compute_checksum_ipv6__. */ EXPORT_SYMBOL void nfq_tcp_compute_checksum_ipv6(struct tcphdr *tcph, struct ip6_hdr *ip6h) @@ -111,6 +151,10 @@ void nfq_tcp_compute_checksum_ipv6(struct tcphdr *tcph, struct ip6_hdr *ip6h) tcph->check = nfq_checksum_tcpudp_ipv6(ip6h, tcph, IPPROTO_TCP); } +/** + * @} + */ + /* * The union cast uses a gcc extension to avoid aliasing problems * (union is compatible to any of its members) @@ -128,7 +172,9 @@ union tcp_word_hdr { * readable way * \param buf: pointer to buffer that is used to print the object * \param size: size of the buffer (or remaining room in it). - * \param tcp: pointer to a valid tcp header. + * \param tcph: pointer to a valid tcp header. + * \returns Same as \b snprintf + * \sa __snprintf__(3) * */ EXPORT_SYMBOL @@ -183,21 +229,25 @@ int nfq_tcp_snprintf(char *buf, size_t size, const struct tcphdr *tcph) * \param match_len: length of the existing content you want to mangle * \param rep_buffer: pointer to data you want to use to replace current content * \param rep_len: length of data you want to use to replace current content - * - * \note This function recalculates the IPv4 and TCP checksums for you. + * \returns 1 for success and 0 for failure. See pktb_mangle() for failure case + * \note This function updates the IPv4 length and recalculates the IPv4 & TCP + * checksums for you. + * \warning After changing the length of a TCP message, the application will + * need to mangle sequence numbers in both directions until another change + * puts them in sync again */ EXPORT_SYMBOL -int nfq_tcp_mangle_ipv4(struct pkt_buff *pkt, +int nfq_tcp_mangle_ipv4(struct pkt_buff *pktb, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, unsigned int rep_len) { struct iphdr *iph; struct tcphdr *tcph; - iph = (struct iphdr *)pkt->network_header; - tcph = (struct tcphdr *)(pkt->network_header + iph->ihl*4); + iph = (struct iphdr *)pktb->network_header; + tcph = (struct tcphdr *)(pktb->network_header + iph->ihl*4); - if (!nfq_ip_mangle(pkt, iph->ihl*4 + tcph->doff*4, + if (!nfq_ip_mangle(pktb, iph->ihl*4 + tcph->doff*4, match_offset, match_len, rep_buffer, rep_len)) return 0; @@ -207,5 +257,44 @@ int nfq_tcp_mangle_ipv4(struct pkt_buff *pkt, } /** + * nfq_tcp_mangle_ipv6 - Mangle TCP/IPv6 packet buffer + * \param pktb: Pointer to network packet buffer + * \param match_offset: Offset from start of TCP data of content that you want + * to mangle + * \param match_len: Length of the existing content you want to mangle + * \param rep_buffer: Pointer to data you want to use to replace current content + * \param rep_len: Length of data you want to use to replace current content + * \returns 1 for success and 0 for failure. See pktb_mangle() for failure case + * \note This function updates the IPv6 length and recalculates the TCP + * checksum for you. + * \warning After changing the length of a TCP message, the application will + * need to mangle sequence numbers in both directions until another change + * puts them in sync again + */ +EXPORT_SYMBOL +int nfq_tcp_mangle_ipv6(struct pkt_buff *pktb, + unsigned int match_offset, unsigned int match_len, + const char *rep_buffer, unsigned int rep_len) +{ + struct ip6_hdr *ip6h; + struct tcphdr *tcph; + + ip6h = (struct ip6_hdr *)pktb->network_header; + tcph = (struct tcphdr *)(pktb->transport_header); + if (!tcph) + return 0; + + if (!nfq_ip6_mangle(pktb, + pktb->transport_header - pktb->network_header + + tcph->doff * 4, + match_offset, match_len, rep_buffer, rep_len)) + return 0; + + nfq_tcp_compute_checksum_ipv6(tcph, ip6h); + + return 1; +} + +/** * @} */ diff --git a/src/extra/udp.c b/src/extra/udp.c index fed23e2..ede2196 100644 --- a/src/extra/udp.c +++ b/src/extra/udp.c @@ -17,24 +17,37 @@ #define _GNU_SOURCE #include <netinet/udp.h> +#include <linux/netfilter/nfnetlink_queue.h> #include <libnetfilter_queue/libnetfilter_queue.h> #include <libnetfilter_queue/libnetfilter_queue_udp.h> #include <libnetfilter_queue/libnetfilter_queue_ipv4.h> +#include <libnetfilter_queue/libnetfilter_queue_ipv6.h> #include <libnetfilter_queue/pktbuff.h> #include "internal.h" /** * \defgroup udp UDP helper functions + * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <libmnl/libmnl.h> +#include <libnetfilter_queue/libnetfilter_queue_udp.h> +\endmanonly + * * @{ */ /** * nfq_udp_get_hdr - get the UDP header. - * \param pktb: Pointer to network packet buffer + * \param pktb: Pointer to userspace network packet buffer * - * This function returns NULL if invalid UDP header is found. On success, - * it returns the UDP header. + * \returns validated pointer to the UDP header or NULL if the UDP header was + * not set or if a minimal length check fails. + * \note You have to call nfq_ip_set_transport_header() or + * nfq_ip6_set_transport_header() first to set the UDP header. */ EXPORT_SYMBOL struct udphdr *nfq_udp_get_hdr(struct pkt_buff *pktb) @@ -43,7 +56,7 @@ struct udphdr *nfq_udp_get_hdr(struct pkt_buff *pktb) return NULL; /* No room for the UDP header. */ - if (pktb->tail - pktb->transport_header < sizeof(struct udphdr)) + if (pktb_tail(pktb) - pktb->transport_header < sizeof(struct udphdr)) return NULL; return (struct udphdr *)pktb->transport_header; @@ -52,7 +65,8 @@ struct udphdr *nfq_udp_get_hdr(struct pkt_buff *pktb) /** * nfq_udp_get_payload - get the UDP packet payload. * \param udph: Pointer to UDP header - * \param pktb: Pointer to network packet buffer + * \param pktb: Pointer to userspace network packet buffer + * \returns Pointer to the UDP payload, or NULL if malformed UDP packet. */ EXPORT_SYMBOL void *nfq_udp_get_payload(struct udphdr *udph, struct pkt_buff *pktb) @@ -64,7 +78,7 @@ void *nfq_udp_get_payload(struct udphdr *udph, struct pkt_buff *pktb) return NULL; /* malformed UDP packet. */ - if (pktb->transport_header + len > pktb->tail) + if (pktb->transport_header + len > pktb_tail(pktb)) return NULL; return pktb->transport_header + sizeof(struct udphdr); @@ -73,23 +87,43 @@ void *nfq_udp_get_payload(struct udphdr *udph, struct pkt_buff *pktb) /** * nfq_udp_get_payload_len - get the udp packet payload. * \param udph: Pointer to UDP header - * \param pktb: Pointer to network packet buffer + * \param pktb: Pointer to userspace network packet buffer + * \returns Length of UDP payload (user data) */ EXPORT_SYMBOL unsigned int nfq_udp_get_payload_len(struct udphdr *udph, struct pkt_buff *pktb) { - return pktb->tail - pktb->transport_header; + return pktb_tail(pktb) - pktb->transport_header - sizeof(struct udphdr); } /** - * nfq_udp_set_checksum_ipv4 - computes a IPv4/TCP packet's segment - * \param iphdrp: pointer to the ip header - * \param ippayload: payload of the ip packet + * \defgroup udp_internals Internal UDP functions * - * \returns the checksum of the udp segment. + * Most user-space programs will never need these. + * + * + * \manonly +.SH SYNOPSIS +.nf +\fB +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <netinet/udp.h> +#include <libnetfilter_queue/libnetfilter_queue_udp.h> +\endmanonly * - * \see nfq_pkt_compute_ip_checksum - * \see nfq_pkt_compute_udp_checksum + * @{ + */ + +/** + * nfq_udp_compute_checksum_ipv4 - sets up the UDP checksum in a UDP/IPv4 packet + * \param udph: pointer to the UDP header + * \param iph: pointer to the IPv4 header + * \note + * nfq_udp_mangle_ipv4() invokes this function. + * As long as developers always use __nfq_udp_mangle_ipv4__ when changing the + * content of a UDP message, there is no need to call + * __nfq_udp_compute_checksum_ipv4__. */ EXPORT_SYMBOL void nfq_udp_compute_checksum_ipv4(struct udphdr *udph, struct iphdr *iph) @@ -100,14 +134,14 @@ void nfq_udp_compute_checksum_ipv4(struct udphdr *udph, struct iphdr *iph) } /** - * nfq_udp_set_checksum_ipv6 - computes a IPv6/TCP packet's segment - * \param iphdrp: pointer to the ip header - * \param ippayload: payload of the ip packet - * - * \returns the checksum of the udp segment. - * - * \see nfq_pkt_compute_ip_checksum - * \see nfq_pkt_compute_udp_checksum + * nfq_udp_compute_checksum_ipv6 - sets up the UDP checksum in a UDP/IPv6 packet + * \param udph: pointer to the UDP header + * \param ip6h: pointer to the IPv6 header + * \note + * nfq_udp_mangle_ipv6() invokes this function. + * As long as developers always use __nfq_udp_mangle_ipv6__ when changing the + * content of a UDP message, there is no need to call + * __nfq_udp_compute_checksum_ipv6__. */ EXPORT_SYMBOL void nfq_udp_compute_checksum_ipv6(struct udphdr *udph, struct ip6_hdr *ip6h) @@ -118,6 +152,10 @@ void nfq_udp_compute_checksum_ipv6(struct udphdr *udph, struct ip6_hdr *ip6h) } /** + * @} + */ + +/** * nfq_udp_mangle_ipv4 - Mangle UDP/IPv4 packet buffer * \param pktb: Pointer to network packet buffer * \param match_offset: Offset from start of UDP data of content that you want @@ -130,19 +168,19 @@ void nfq_udp_compute_checksum_ipv6(struct udphdr *udph, struct ip6_hdr *ip6h) * checksums for you. */ EXPORT_SYMBOL -int nfq_udp_mangle_ipv4(struct pkt_buff *pkt, +int nfq_udp_mangle_ipv4(struct pkt_buff *pktb, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, unsigned int rep_len) { struct iphdr *iph; struct udphdr *udph; - iph = (struct iphdr *)pkt->network_header; - udph = (struct udphdr *)(pkt->network_header + iph->ihl*4); + iph = (struct iphdr *)pktb->network_header; + udph = (struct udphdr *)(pktb->network_header + iph->ihl*4); udph->len = htons(ntohs(udph->len) + rep_len - match_len); - if (!nfq_ip_mangle(pkt, iph->ihl*4 + sizeof(struct udphdr), + if (!nfq_ip_mangle(pktb, iph->ihl*4 + sizeof(struct udphdr), match_offset, match_len, rep_buffer, rep_len)) return 0; @@ -152,11 +190,51 @@ int nfq_udp_mangle_ipv4(struct pkt_buff *pkt, } /** + * nfq_udp_mangle_ipv6 - Mangle UDP/IPv6 packet buffer + * \param pktb: Pointer to network packet buffer + * \param match_offset: Offset from start of UDP data of content that you want + * to mangle + * \param match_len: Length of the existing content you want to mangle + * \param rep_buffer: Pointer to data you want to use to replace current content + * \param rep_len: Length of data you want to use to replace current content + * \returns 1 for success and 0 for failure. See pktb_mangle() for failure case + * \note This function updates the IPv6 and UDP lengths and recalculates the UDP + * checksum for you. + */ +EXPORT_SYMBOL +int nfq_udp_mangle_ipv6(struct pkt_buff *pktb, + unsigned int match_offset, unsigned int match_len, + const char *rep_buffer, unsigned int rep_len) +{ + struct ip6_hdr *ip6h; + struct udphdr *udph; + + ip6h = (struct ip6_hdr *)pktb->network_header; + udph = (struct udphdr *)(pktb->transport_header); + if (!udph) + return 0; + + udph->len = htons(ntohs(udph->len) + rep_len - match_len); + + if (!nfq_ip6_mangle(pktb, + pktb->transport_header - pktb->network_header + + sizeof(struct udphdr), + match_offset, match_len, rep_buffer, rep_len)) + return 0; + + nfq_udp_compute_checksum_ipv6(udph, ip6h); + + return 1; +} + +/** * nfq_pkt_snprintf_udp_hdr - print udp header into one buffer in a humnan * readable way * \param buf: pointer to buffer that is used to print the object * \param size: size of the buffer (or remaining room in it). - * \param udp: pointer to a valid udp header. + * \param udph: pointer to a valid udp header. + * \returns The number of characters notionally written (excluding trailing NUL) + * \sa __snprintf__(3) * */ EXPORT_SYMBOL |