|
Packit |
c43939 |
/*
|
|
Packit |
c43939 |
* (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
|
|
Packit |
c43939 |
*
|
|
Packit |
c43939 |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
c43939 |
* it under the terms of the GNU General Public License as published by
|
|
Packit |
c43939 |
* the Free Software Foundation; either version 2 of the License, or
|
|
Packit |
c43939 |
* (at your option) any later version.
|
|
Packit |
c43939 |
*
|
|
Packit |
c43939 |
* This code has been sponsored by Vyatta Inc. <http://www.vyatta.com>
|
|
Packit |
c43939 |
*/
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
#include <errno.h>
|
|
Packit |
c43939 |
#include <stdlib.h>
|
|
Packit |
c43939 |
#include <string.h> /* for memcpy */
|
|
Packit |
c43939 |
#include <stdbool.h>
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
#include <netinet/if_ether.h>
|
|
Packit |
c43939 |
#include <netinet/ip.h>
|
|
Packit |
c43939 |
#include <netinet/tcp.h>
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
#include "internal.h"
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
/**
|
|
Packit |
c43939 |
* \defgroup pktbuff User-space network packet buffer
|
|
Packit |
c43939 |
*
|
|
Packit |
c43939 |
* This library provides the user-space network packet buffer. This abstraction
|
|
Packit |
c43939 |
* is strongly inspired by Linux kernel network buffer, the so-called sk_buff.
|
|
Packit |
c43939 |
*
|
|
Packit |
c43939 |
* @{
|
|
Packit |
c43939 |
*/
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
/**
|
|
Packit |
c43939 |
* pktb_alloc - allocate a new packet buffer
|
|
Packit |
c43939 |
* \param family Indicate what family. Currently supported families are
|
|
Packit |
c43939 |
* AF_BRIDGE, AF_INET & AF_INET6.
|
|
Packit |
c43939 |
* \param data Pointer to packet data
|
|
Packit |
c43939 |
* \param len Packet length
|
|
Packit |
c43939 |
* \param extra Extra memory in the tail to be allocated (for mangling)
|
|
Packit |
c43939 |
*
|
|
Packit |
c43939 |
* This function returns a packet buffer that contains the packet data and
|
|
Packit |
c43939 |
* some extra memory room in the tail (if requested).
|
|
Packit |
c43939 |
*
|
|
Packit |
c43939 |
* \return Pointer to a new userspace packet buffer or NULL on failure.
|
|
Packit |
c43939 |
* \par Errors
|
|
Packit |
c43939 |
* __ENOMEM__ From __calloc__()
|
|
Packit |
c43939 |
* \n
|
|
Packit |
c43939 |
* __EPROTONOSUPPORT__ _family_ was __AF_BRIDGE__ and this is not an IP packet
|
|
Packit |
c43939 |
* (v4 or v6)
|
|
Packit |
c43939 |
* \sa __calloc__(3)
|
|
Packit |
c43939 |
*/
|
|
Packit |
c43939 |
EXPORT_SYMBOL
|
|
Packit |
c43939 |
struct pkt_buff *pktb_alloc(int family, void *data, size_t len, size_t extra)
|
|
Packit |
c43939 |
{
|
|
Packit |
c43939 |
struct pkt_buff *pktb;
|
|
Packit |
c43939 |
struct ethhdr *ethhdr;
|
|
Packit |
c43939 |
void *pkt_data;
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
pktb = calloc(1, sizeof(struct pkt_buff) + len + extra);
|
|
Packit |
c43939 |
if (pktb == NULL)
|
|
Packit |
c43939 |
return NULL;
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
/* Better make sure alignment is correct. */
|
|
Packit |
c43939 |
pkt_data = (uint8_t *)pktb + sizeof(struct pkt_buff);
|
|
Packit |
c43939 |
memcpy(pkt_data, data, len);
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
pktb->len = len;
|
|
Packit |
c43939 |
pktb->data_len = len + extra;
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
pktb->data = pkt_data;
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
switch(family) {
|
|
Packit |
c43939 |
case AF_INET:
|
|
Packit |
c43939 |
case AF_INET6:
|
|
Packit |
c43939 |
pktb->network_header = pktb->data;
|
|
Packit |
c43939 |
break;
|
|
Packit |
c43939 |
case AF_BRIDGE:
|
|
Packit |
c43939 |
ethhdr = (struct ethhdr *)pktb->data;
|
|
Packit |
c43939 |
pktb->mac_header = pktb->data;
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
switch(ethhdr->h_proto) {
|
|
Packit |
c43939 |
case ETH_P_IP:
|
|
Packit |
c43939 |
case ETH_P_IPV6:
|
|
Packit |
c43939 |
pktb->network_header = pktb->data + ETH_HLEN;
|
|
Packit |
c43939 |
break;
|
|
Packit |
c43939 |
default:
|
|
Packit |
c43939 |
/* This protocol is unsupported. */
|
|
Packit |
c43939 |
errno = EPROTONOSUPPORT;
|
|
Packit |
c43939 |
free(pktb);
|
|
Packit |
c43939 |
return NULL;
|
|
Packit |
c43939 |
}
|
|
Packit |
c43939 |
break;
|
|
Packit |
c43939 |
}
|
|
Packit |
c43939 |
return pktb;
|
|
Packit |
c43939 |
}
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
/**
|
|
Packit |
c43939 |
* pktb_data - get pointer to network packet
|
|
Packit |
c43939 |
* \param pktb Pointer to userspace packet buffer
|
|
Packit |
c43939 |
* \return Pointer to start of network packet data within __pktb__
|
|
Packit |
c43939 |
* \par
|
|
Packit |
c43939 |
* It is appropriate to use _pktb_data_ as the second argument of
|
|
Packit |
c43939 |
* nfq_nlmsg_verdict_put_pkt()
|
|
Packit |
c43939 |
*/
|
|
Packit |
c43939 |
EXPORT_SYMBOL
|
|
Packit |
c43939 |
uint8_t *pktb_data(struct pkt_buff *pktb)
|
|
Packit |
c43939 |
{
|
|
Packit |
c43939 |
return pktb->data;
|
|
Packit |
c43939 |
}
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
/**
|
|
Packit |
c43939 |
* pktb_len - get length of packet buffer
|
|
Packit |
c43939 |
* \param pktb Pointer to userspace packet buffer
|
|
Packit |
c43939 |
* \return Length of packet contained within __pktb__
|
|
Packit |
c43939 |
* \par
|
|
Packit |
c43939 |
* It is appropriate to use _pktb_len_ as the third argument of
|
|
Packit |
c43939 |
* nfq_nlmsg_verdict_put_pkt()
|
|
Packit |
c43939 |
*/
|
|
Packit |
c43939 |
EXPORT_SYMBOL
|
|
Packit |
c43939 |
uint32_t pktb_len(struct pkt_buff *pktb)
|
|
Packit |
c43939 |
{
|
|
Packit |
c43939 |
return pktb->len;
|
|
Packit |
c43939 |
}
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
/**
|
|
Packit |
c43939 |
* pktb_free - release packet buffer
|
|
Packit |
c43939 |
* \param pktb Pointer to userspace packet buffer
|
|
Packit |
c43939 |
*/
|
|
Packit |
c43939 |
EXPORT_SYMBOL
|
|
Packit |
c43939 |
void pktb_free(struct pkt_buff *pktb)
|
|
Packit |
c43939 |
{
|
|
Packit |
c43939 |
free(pktb);
|
|
Packit |
c43939 |
}
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
/**
|
|
Packit |
c43939 |
* \defgroup otherfns Other functions
|
|
Packit |
c43939 |
*
|
|
Packit |
c43939 |
* The library provides a number of other functions which many user-space
|
|
Packit |
c43939 |
* programs will never need. These divide into 2 groups:
|
|
Packit |
c43939 |
* \n
|
|
Packit |
c43939 |
* 1. Functions to get values of members of opaque __struct pktbuff__, described
|
|
Packit |
c43939 |
* below
|
|
Packit |
c43939 |
* \n
|
|
Packit |
c43939 |
* 2. Internal functions, described in Module __Internal functions__
|
|
Packit |
c43939 |
*
|
|
Packit |
c43939 |
* @{
|
|
Packit |
c43939 |
*/
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
/**
|
|
Packit |
c43939 |
* \defgroup uselessfns Internal functions
|
|
Packit |
c43939 |
*
|
|
Packit |
c43939 |
* \warning Do not use these functions. Instead, always use the mangle
|
|
Packit |
c43939 |
* function appropriate to the level at which you are working.
|
|
Packit |
c43939 |
* \n
|
|
Packit |
c43939 |
* pktb_mangle() uses all the below functions except _pktb_pull_, which is not
|
|
Packit |
c43939 |
* used by anything.
|
|
Packit |
c43939 |
*
|
|
Packit |
c43939 |
* @{
|
|
Packit |
c43939 |
*/
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
/**
|
|
Packit |
c43939 |
* pktb_push - decrement pointer to packet buffer
|
|
Packit |
c43939 |
* \param pktb Pointer to userspace packet buffer
|
|
Packit |
c43939 |
* \param len Number of bytes to subtract from packet start address
|
|
Packit |
c43939 |
*/
|
|
Packit |
c43939 |
EXPORT_SYMBOL
|
|
Packit |
c43939 |
void pktb_push(struct pkt_buff *pktb, unsigned int len)
|
|
Packit |
c43939 |
{
|
|
Packit |
c43939 |
pktb->data -= len;
|
|
Packit |
c43939 |
pktb->len += len;
|
|
Packit |
c43939 |
}
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
/**
|
|
Packit |
c43939 |
* pktb_pull - increment pointer to packet buffer
|
|
Packit |
c43939 |
* \param pktb Pointer to userspace packet buffer
|
|
Packit |
c43939 |
* \param len Number of bytes to add to packet start address
|
|
Packit |
c43939 |
*/
|
|
Packit |
c43939 |
EXPORT_SYMBOL
|
|
Packit |
c43939 |
void pktb_pull(struct pkt_buff *pktb, unsigned int len)
|
|
Packit |
c43939 |
{
|
|
Packit |
c43939 |
pktb->data += len;
|
|
Packit |
c43939 |
pktb->len -= len;
|
|
Packit |
c43939 |
}
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
/**
|
|
Packit |
c43939 |
* pktb_put - add extra bytes to the tail of the packet buffer
|
|
Packit |
c43939 |
* \param pktb Pointer to userspace packet buffer
|
|
Packit |
c43939 |
* \param len Number of bytes to add to packet tail (and length)
|
|
Packit |
c43939 |
*/
|
|
Packit |
c43939 |
EXPORT_SYMBOL
|
|
Packit |
c43939 |
void pktb_put(struct pkt_buff *pktb, unsigned int len)
|
|
Packit |
c43939 |
{
|
|
Packit |
c43939 |
pktb->len += len;
|
|
Packit |
c43939 |
}
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
/**
|
|
Packit |
c43939 |
* pktb_trim - set new length for this packet buffer
|
|
Packit |
c43939 |
* \param pktb Pointer to userspace packet buffer
|
|
Packit |
c43939 |
* \param len New packet length (tail is adjusted to reflect this)
|
|
Packit |
c43939 |
*/
|
|
Packit |
c43939 |
EXPORT_SYMBOL
|
|
Packit |
c43939 |
void pktb_trim(struct pkt_buff *pktb, unsigned int len)
|
|
Packit |
c43939 |
{
|
|
Packit |
c43939 |
pktb->len = len;
|
|
Packit |
c43939 |
}
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
/**
|
|
Packit |
c43939 |
* @}
|
|
Packit |
c43939 |
*/
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
/**
|
|
Packit |
c43939 |
* pktb_tailroom - get room available for packet expansion
|
|
Packit |
c43939 |
* \param pktb Pointer to userspace packet buffer
|
|
Packit |
c43939 |
* \return room in bytes after the tail of the packet buffer
|
|
Packit |
c43939 |
* \n
|
|
Packit |
c43939 |
* This starts off as the __extra__ argument to pktb_alloc().
|
|
Packit |
c43939 |
* Programmers should ensure this __extra__ argument is sufficient for any
|
|
Packit |
c43939 |
* packet mangle, as packet buffers cannot be expanded dynamically.
|
|
Packit |
c43939 |
*/
|
|
Packit |
c43939 |
EXPORT_SYMBOL
|
|
Packit |
c43939 |
unsigned int pktb_tailroom(struct pkt_buff *pktb)
|
|
Packit |
c43939 |
{
|
|
Packit |
c43939 |
return pktb->data_len - pktb->len;
|
|
Packit |
c43939 |
}
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
/**
|
|
Packit |
c43939 |
* pktb_mac_header - get address of layer 2 header (if any)
|
|
Packit |
c43939 |
* \param pktb Pointer to userspace packet buffer
|
|
Packit |
c43939 |
* \return Pointer to MAC header or NULL if no such header present.
|
|
Packit |
c43939 |
* \n
|
|
Packit |
c43939 |
* Only packet buffers in family __AF_BRIDGE__ have a non-NULL MAC header.
|
|
Packit |
c43939 |
*/
|
|
Packit |
c43939 |
EXPORT_SYMBOL
|
|
Packit |
c43939 |
uint8_t *pktb_mac_header(struct pkt_buff *pktb)
|
|
Packit |
c43939 |
{
|
|
Packit |
c43939 |
return pktb->mac_header;
|
|
Packit |
c43939 |
}
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
/**
|
|
Packit |
c43939 |
* pktb_network_header - get address of layer 3 header
|
|
Packit |
c43939 |
* \param pktb Pointer to userspace packet buffer
|
|
Packit |
c43939 |
* \return Pointer to layer 3 header or NULL if the packet buffer was created
|
|
Packit |
c43939 |
* with an unsupported family
|
|
Packit |
c43939 |
*/
|
|
Packit |
c43939 |
EXPORT_SYMBOL
|
|
Packit |
c43939 |
uint8_t *pktb_network_header(struct pkt_buff *pktb)
|
|
Packit |
c43939 |
{
|
|
Packit |
c43939 |
return pktb->network_header;
|
|
Packit |
c43939 |
}
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
/**
|
|
Packit |
c43939 |
* pktb_transport_header - get address of layer 4 header (if known)
|
|
Packit |
c43939 |
* \param pktb Pointer to userspace packet buffer
|
|
Packit |
c43939 |
* \return Pointer to layer 4 header or NULL if not (yet) set
|
|
Packit |
c43939 |
* \note
|
|
Packit |
c43939 |
* Unlike the lower-level headers, it is the programmer's responsibility to
|
|
Packit |
c43939 |
* create the level 4 (transport) header pointer by caling e.g.
|
|
Packit |
c43939 |
* nfq_ip_set_transport_header()
|
|
Packit |
c43939 |
*/
|
|
Packit |
c43939 |
EXPORT_SYMBOL
|
|
Packit |
c43939 |
uint8_t *pktb_transport_header(struct pkt_buff *pktb)
|
|
Packit |
c43939 |
{
|
|
Packit |
c43939 |
return pktb->transport_header;
|
|
Packit |
c43939 |
}
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
/**
|
|
Packit |
c43939 |
* @}
|
|
Packit |
c43939 |
*/
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
static int pktb_expand_tail(struct pkt_buff *pktb, int extra)
|
|
Packit |
c43939 |
{
|
|
Packit |
c43939 |
/* No room in packet, cannot mangle it. We don't support dynamic
|
|
Packit |
c43939 |
* reallocation. Instead, increase the size of the extra room in
|
|
Packit |
c43939 |
* the tail in pktb_alloc.
|
|
Packit |
c43939 |
*/
|
|
Packit |
c43939 |
if (pktb->len + extra > pktb->data_len)
|
|
Packit |
c43939 |
return 0;
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
pktb->len += extra;
|
|
Packit |
c43939 |
return 1;
|
|
Packit |
c43939 |
}
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
static int enlarge_pkt(struct pkt_buff *pktb, unsigned int extra)
|
|
Packit |
c43939 |
{
|
|
Packit |
c43939 |
if (pktb->len + extra > 65535)
|
|
Packit |
c43939 |
return 0;
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
if (!pktb_expand_tail(pktb, extra - pktb_tailroom(pktb)))
|
|
Packit |
c43939 |
return 0;
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
return 1;
|
|
Packit |
c43939 |
}
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
/**
|
|
Packit |
c43939 |
* pktb_mangle - adjust contents of a packet
|
|
Packit |
c43939 |
* \param pktb Pointer to userspace packet buffer
|
|
Packit |
c43939 |
* \param dataoff Supplementary offset, usually offset from layer 3 (IP) header
|
|
Packit |
c43939 |
* to the layer 4 (TCP or UDP) header. Specify zero to access the layer 3
|
|
Packit |
c43939 |
* header. If \b pktb was created in family \b AF_BRIDGE, specify
|
|
Packit |
c43939 |
* \b -ETH_HLEN (a negative offset) to access the layer 2 (MAC) header.
|
|
Packit |
c43939 |
* \param match_offset Further offset to content that you want to mangle
|
|
Packit |
c43939 |
* \param match_len Length of the existing content you want to mangle
|
|
Packit |
c43939 |
* \param rep_buffer Pointer to data you want to use to replace current content
|
|
Packit |
c43939 |
* \param rep_len Length of data you want to use to replace current content
|
|
Packit |
c43939 |
* \returns 1 for success and 0 for failure. Failure will occur if the \b extra
|
|
Packit |
c43939 |
* argument to the pktb_alloc() call that created \b pktb is less than the
|
|
Packit |
c43939 |
* excess of \b rep_len over \b match_len
|
|
Packit |
c43939 |
\warning pktb_mangle does not update any checksums. Developers should use the
|
|
Packit |
c43939 |
appropriate mangler for the protocol level: nfq_ip_mangle(),
|
|
Packit |
c43939 |
nfq_tcp_mangle_ipv4() or nfq_udp_mangle_ipv4(). IPv6 versions are planned.
|
|
Packit |
c43939 |
\n
|
|
Packit |
c43939 |
It is appropriate to use pktb_mangle to change the MAC header.
|
|
Packit |
c43939 |
*/
|
|
Packit |
c43939 |
EXPORT_SYMBOL
|
|
Packit |
c43939 |
int pktb_mangle(struct pkt_buff *pktb,
|
|
Packit |
c43939 |
int dataoff,
|
|
Packit |
c43939 |
unsigned int match_offset,
|
|
Packit |
c43939 |
unsigned int match_len,
|
|
Packit |
c43939 |
const char *rep_buffer,
|
|
Packit |
c43939 |
unsigned int rep_len)
|
|
Packit |
c43939 |
{
|
|
Packit |
c43939 |
unsigned char *data;
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
if (rep_len > match_len &&
|
|
Packit |
c43939 |
rep_len - match_len > pktb_tailroom(pktb) &&
|
|
Packit |
c43939 |
!enlarge_pkt(pktb, rep_len - match_len))
|
|
Packit |
c43939 |
return 0;
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
data = pktb->network_header + dataoff;
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
/* move post-replacement */
|
|
Packit |
c43939 |
memmove(data + match_offset + rep_len,
|
|
Packit |
c43939 |
data + match_offset + match_len,
|
|
Packit |
c43939 |
pktb_tail(pktb) - (pktb->network_header + dataoff +
|
|
Packit |
c43939 |
match_offset + match_len));
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
/* insert data from buffer */
|
|
Packit |
c43939 |
memcpy(data + match_offset, rep_buffer, rep_len);
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
/* update packet info */
|
|
Packit |
c43939 |
if (rep_len > match_len)
|
|
Packit |
c43939 |
pktb_put(pktb, rep_len - match_len);
|
|
Packit |
c43939 |
else
|
|
Packit |
c43939 |
pktb_trim(pktb, pktb->len + rep_len - match_len);
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
pktb->mangled = true;
|
|
Packit |
c43939 |
return 1;
|
|
Packit |
c43939 |
}
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
/**
|
|
Packit |
c43939 |
* pktb_mangled - test whether packet has been mangled
|
|
Packit |
c43939 |
* \param pktb Pointer to userspace packet buffer
|
|
Packit |
c43939 |
* \return __true__ if packet has been mangled (modified), else __false__
|
|
Packit |
c43939 |
* \par
|
|
Packit |
c43939 |
* When assembling a verdict, it is not necessary to return the contents of
|
|
Packit |
c43939 |
* un-modified packets. Use _pktb_mangled_ to decide whether packet contents
|
|
Packit |
c43939 |
* need to be returned.
|
|
Packit |
c43939 |
*/
|
|
Packit |
c43939 |
EXPORT_SYMBOL
|
|
Packit |
c43939 |
bool pktb_mangled(const struct pkt_buff *pktb)
|
|
Packit |
c43939 |
{
|
|
Packit |
c43939 |
return pktb->mangled;
|
|
Packit |
c43939 |
}
|
|
Packit |
c43939 |
|
|
Packit |
c43939 |
/**
|
|
Packit |
c43939 |
* @}
|
|
Packit |
c43939 |
*/
|