diff options
Diffstat (limited to 'src/extra/pktbuff.c')
-rw-r--r-- | src/extra/pktbuff.c | 318 |
1 files changed, 243 insertions, 75 deletions
diff --git a/src/extra/pktbuff.c b/src/extra/pktbuff.c index 00af037..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,23 +23,81 @@ /** * \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 (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 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) */ EXPORT_SYMBOL struct pkt_buff *pktb_alloc(int family, void *data, size_t len, size_t extra) @@ -54,40 +113,54 @@ 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: - 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; } /** - * 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) @@ -96,8 +169,12 @@ uint8_t *pktb_data(struct pkt_buff *pktb) } /** - * 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) @@ -107,7 +184,7 @@ uint32_t pktb_len(struct pkt_buff *pktb) /** * 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) @@ -116,8 +193,51 @@ void pktb_free(struct pkt_buff *pktb) } /** - * 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) @@ -127,8 +247,9 @@ void pktb_push(struct pkt_buff *pktb, unsigned int len) } /** - * 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) @@ -139,18 +260,19 @@ void pktb_pull(struct pkt_buff *pktb, unsigned int len) /** * 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; } /** * 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) @@ -159,8 +281,17 @@ void pktb_trim(struct pkt_buff *pktb, unsigned int len) } /** - * 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) @@ -169,8 +300,11 @@ unsigned int pktb_tailroom(struct pkt_buff *pktb) } /** - * 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) @@ -179,8 +313,10 @@ uint8_t *pktb_mac_header(struct pkt_buff *pktb) } /** - * 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) @@ -189,8 +325,13 @@ uint8_t *pktb_network_header(struct pkt_buff *pktb) } /** - * 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) @@ -198,26 +339,29 @@ uint8_t *pktb_transport_header(struct pkt_buff *pktb) return 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; @@ -225,20 +369,27 @@ static int enlarge_pkt(struct pkt_buff *pkt, unsigned int extra) /** * pktb_mangle - adjust contents of a packet - * \param pkt Pointer to packet buffer - * \param dataoff Offset to layer 4 header. Specify zero to access layer 3 (IP) - * header + * \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 pkt is less than the excess - * of \b rep_len over \b match_len + * 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 *pkt, - unsigned int dataoff, +int pktb_mangle(struct pkt_buff *pktb, + int dataoff, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, @@ -247,39 +398,56 @@ int pktb_mangle(struct pkt_buff *pkt, 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; } /** - * 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 */ + EXPORT_SYMBOL -bool pktb_mangled(const struct pkt_buff *pkt) +size_t pktb_head_size(void) { - return pkt->mangled; + return sizeof(struct pkt_buff); } /** |