One interesting thing that many people are not aware of is to use libmnl instead of libnetfilter-queue. The later is GPL while the former is LGPL - guess which one I like to use?
This document is a simple example on how to install libmnl and to queue traffic to userspace from iptables.
Installing libmnl
-
git clone git://git.netfilter.org/libmnl
-
cd libmnl/
-
./configure
-
make
-
make install
Using the example
In iptables, run the following commands (modify if you want to do something different)
-
sudo iptables -I FORWARD -p tcp --dport 80 -j NFQUEUE --queue-num 80
-
sudo iptables -I FORWARD -p tcp --sport 80 -j NFQUEUE --queue-num 80
Download, untar and build my example
-
wget http://www.pacificsimplicity.ca/sites/default/files/uploads/libmnl-example.tar.gz
-
tar -xzvf libmnl-example.tar.gz
-
cd libmnl-example/
-
make clean;make
To run the example:
sudo ./example 80
Example source readout
-
/**
-
* @file main.c
-
*
-
* @author Ron Brash
-
* @brief Example libmnl application based on example in src provided in libmnl - for tcp only
-
*
-
* */
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <unistd.h>
-
#include <string.h>
-
#include <time.h>
-
#include <arpa/inet.h>
-
-
#include <libmnl/libmnl.h>
-
#include <linux/netfilter.h>
-
#include <linux/netfilter/nfnetlink.h>
-
-
#ifndef aligned_be64
-
#define aligned_be64 u_int64_t __attribute__((aligned(8)))
-
#endif
-
-
/** Header for verdicts */
-
#include <linux/netfilter/nfnetlink_queue.h>
-
-
-
/** Global verdict variable - lazy... */
-
int verdict;
-
-
/**
-
* parse_attr_cb(const struct nlattr *attr, void *data)
-
*/
-
static int parse_attr_cb(const struct nlattr *attr, void *data)
-
{
-
const struct nlattr **tb = data;
-
int type = mnl_attr_get_type(attr);
-
-
/* skip unsupported attribute in user-space */
-
if (mnl_attr_type_valid(attr, NFQA_MAX) < 0)
-
return MNL_CB_OK;
-
-
switch (type) {
-
case NFQA_MARK:
-
case NFQA_IFINDEX_INDEV:
-
case NFQA_IFINDEX_OUTDEV:
-
case NFQA_IFINDEX_PHYSINDEV:
-
case NFQA_IFINDEX_PHYSOUTDEV:
-
if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
-
perror("mnl_attr_validate");
-
return MNL_CB_ERROR;
-
}
-
break;
-
case NFQA_TIMESTAMP:
-
if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(struct nfqnl_msg_packet_timestamp)) < 0) {
-
perror("mnl_attr_validate2");
-
return MNL_CB_ERROR;
-
}
-
break;
-
case NFQA_HWADDR:
-
if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(struct nfqnl_msg_packet_hw)) < 0) {
-
perror("mnl_attr_validate2");
-
return MNL_CB_ERROR;
-
}
-
break;
-
case NFQA_PAYLOAD:
-
break;
-
}
-
tb[type] = attr;
-
return MNL_CB_OK;
-
}
-
-
/**
-
* queue_cb(const struct nlmsghdr *nlh, void *data)
-
*
-
* @brief Provides the call back function which determines that there
-
* is data within the packet header, sets pointers, determines packetID (very important)
-
*/
-
static int queue_cb(const struct nlmsghdr *nlh, void *data)
-
{
-
struct nlattr *tb[NFQA_MAX + 1] = { };
-
struct nfqnl_msg_packet_hdr *ph = NULL;
-
uint32_t id = 0;
-
unsigned char *pkt = NULL, *payload_ptr = NULL;
-
uint16_t pkt_size = 0, payload_len = 0;
-
struct iphdr *ip_ptr = NULL;
-
struct tcphdr *tcp_ptr = NULL;
-
-
mnl_attr_parse(nlh, sizeof(struct nfgenmsg), parse_attr_cb, tb);
-
if (tb[NFQA_PACKET_HDR]) {
-
ph = mnl_attr_get_payload(tb[NFQA_PACKET_HDR]);
-
id = ntohl(ph->packet_id);
-
-
printf("packet received (id=%u hw=0xx hook=%u)\n", id, ntohs(ph->hw_protocol), ph->hook);
-
}
-
-
/** Retrieve payload size */
-
pkt_size = mnl_attr_get_payload_len(tb[NFQA_PAYLOAD]);
-
-
/** If there is a payload (tb[NFQA_PAYLOAD]), lets setup the pointers!*/
-
if (tb[NFQA_PAYLOAD]) {
-
pkt = mnl_attr_get_payload(tb[NFQA_PAYLOAD]);
-
-
/** @note Notice that there is no Ethernet header in NFQUEUE */
-
ip_ptr = (struct iphdr *)((u_int8_t *) pkt);
-
u_int8_t ip_len = (ip_ptr->ihl * 4);
-
tcp_ptr = (struct tcphdr *)((u_int8_t *) ip_ptr + ip_len);
-
-
/** If we have data, set the pointers */
-
if (tcp_ptr->doff != 0) {
-
payload_ptr = (u_int8_t *) tcp_ptr + (tcp_ptr->doff * sizeof(u_int32_t));
-
payload_len = pkt_size - (ip_len + tcp_ptr->doff * 4);
-
}
-
-
verdict = NF_ACCEPT;
-
printf("\tverdict:%d\n",verdict);
-
-
}
-
-
return MNL_CB_OK + id;
-
}
-
-
/**
-
* nfq_build_cfg_pf_request(char *buf, uint8_t command)
-
*/
-
static struct nlmsghdr *nfq_build_cfg_pf_request(char *buf, uint8_t command)
-
{
-
struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
-
nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_CONFIG;
-
nlh->nlmsg_flags = NLM_F_REQUEST;
-
-
struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
-
nfg->nfgen_family = AF_UNSPEC;
-
nfg->version = NFNETLINK_V0;
-
-
struct nfqnl_msg_config_cmd cmd = {
-
.command = command,
-
.pf = htons(AF_INET),
-
};
-
mnl_attr_put(nlh, NFQA_CFG_CMD, sizeof(cmd), &cmd);
-
-
return nlh;
-
}
-
-
/**
-
* nfq_build_cfg_request(char *buf, uint8_t command, int queue_num)
-
*/
-
static struct nlmsghdr *nfq_build_cfg_request(char *buf, uint8_t command, int queue_num)
-
{
-
struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
-
nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_CONFIG;
-
nlh->nlmsg_flags = NLM_F_REQUEST;
-
-
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);
-
-
struct nfqnl_msg_config_cmd cmd = {
-
.command = command,
-
.pf = htons(AF_INET),
-
};
-
mnl_attr_put(nlh, NFQA_CFG_CMD, sizeof(cmd), &cmd);
-
-
return nlh;
-
}
-
-
/**
-
* nfq_build_cfg_params(char *buf, uint8_t mode, int range, int queue_num)
-
*/
-
static struct nlmsghdr *nfq_build_cfg_params(char *buf, uint8_t mode, int range, int queue_num)
-
{
-
struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
-
nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_CONFIG;
-
nlh->nlmsg_flags = NLM_F_REQUEST;
-
-
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);
-
-
struct nfqnl_msg_config_params params = {
-
.copy_range = htonl(range),
-
.copy_mode = mode,
-
};
-
mnl_attr_put(nlh, NFQA_CFG_PARAMS, sizeof(params), ¶ms);
-
-
return nlh;
-
}
-
-
/**
-
* nfq_build_verdict(char *buf, int id, int queue_num, int verd)
-
*/
-
static struct nlmsghdr *nfq_build_verdict(char *buf, int id, int queue_num, int verd)
-
{
-
struct nlmsghdr *nlh;
-
struct nfgenmsg *nfg;
-
-
nlh = mnl_nlmsg_put_header(buf);
-
nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_VERDICT;
-
nlh->nlmsg_flags = NLM_F_REQUEST;
-
nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
-
nfg->nfgen_family = AF_UNSPEC;
-
nfg->version = NFNETLINK_V0;
-
nfg->res_id = htons(queue_num);
-
-
struct nfqnl_msg_verdict_hdr vh = {
-
.verdict = htonl(verd),
-
.id = htonl(id),
-
};
-
mnl_attr_put(nlh, NFQA_VERDICT_HDR, sizeof(vh), &vh);
-
-
return nlh;
-
}
-
-
/**
-
* int main(int argc, char *argv[])
-
*/
-
int main(int argc, char *argv[])
-
{
-
struct mnl_socket *nl;
-
char buf[MNL_SOCKET_BUFFER_SIZE];
-
struct nlmsghdr *nlh;
-
int ret;
-
int portid, queue_num;
-
-
queue_num = (atoi(argv[1]));
-
printf("queue num: %d\n",queue_num);
-
-
nl = mnl_socket_open(NETLINK_NETFILTER);
-
if (nl == NULL) {
-
perror("mnl_socket_open");
-
exit(EXIT_FAILURE);
-
}
-
-
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
-
perror("mnl_socket_bind");
-
exit(EXIT_FAILURE);
-
}
-
portid = mnl_socket_get_portid(nl);
-
-
nlh = nfq_build_cfg_pf_request(buf, NFQNL_CFG_CMD_PF_UNBIND);
-
-
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
-
perror("mnl_socket_sendto");
-
exit(EXIT_FAILURE);
-
}
-
-
nlh = nfq_build_cfg_pf_request(buf, NFQNL_CFG_CMD_PF_BIND);
-
-
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
-
perror("mnl_socket_sendto");
-
exit(EXIT_FAILURE);
-
}
-
-
nlh = nfq_build_cfg_request(buf, NFQNL_CFG_CMD_BIND, queue_num);
-
-
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
-
perror("mnl_socket_sendto");
-
exit(EXIT_FAILURE);
-
}
-
-
nlh = nfq_build_cfg_params(buf, NFQNL_COPY_PACKET, 0xFFFF, queue_num);
-
-
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
-
perror("mnl_socket_sendto");
-
exit(EXIT_FAILURE);
-
}
-
-
printf("Starting packet loop...\n");
-
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
-
if (ret == -1) {
-
perror("mnl_socket_recvfrom");
-
exit(EXIT_FAILURE);
-
}
-
-
uint32_t id;
-
verdict = NF_ACCEPT;
-
-
/** Packet loop */
-
while (ret > 0) {
-
-
/** Run mnl_cb - which is queue_cb */
-
ret = mnl_cb_run(buf, ret, 0, portid, queue_cb, NULL);
-
if (ret < 0) {
-
perror("mnl_cb_run");
-
exit(EXIT_FAILURE);
-
}
-
-
/** Get packetID to generate verdict back to kernel */
-
id = ret - MNL_CB_OK;
-
nlh = nfq_build_verdict(buf, id, queue_num, verdict);
-
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
-
perror("mnl_socket_sendto");
-
exit(EXIT_FAILURE);
-
}
-
-
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
-
if (ret == -1) {
-
perror("mnl_socket_recvfrom");
-
exit(EXIT_FAILURE);
-
}
-
}
-
-
mnl_socket_close(nl);
-
-
return 0;
-
}
Caveats
There are a number of caveats for DPI in userspace; one of which is performance, the other is you have to get the L2 header sometimes (which isn't done by default in the kernel).
Blog tags:
Attachment | Size |
---|---|
libmnl-example.tar.gz | 3.13 KB |
Add new comment