summaryrefslogtreecommitdiffstats
path: root/src/extra/pktbuff.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/extra/pktbuff.c')
-rw-r--r--src/extra/pktbuff.c318
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);
}
/**