Libmnl and Queuing Example

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

  1. git clone git://git.netfilter.org/libmnl
  2. cd libmnl/
  3. ./configure
  4. make
  5. make install

Using the example

In iptables, run the following commands (modify if you want to do something different)

  1. sudo iptables -I FORWARD -p tcp --dport 80 -j NFQUEUE --queue-num 80
  2. sudo iptables -I FORWARD -p tcp --sport 80 -j NFQUEUE --queue-num 80

Download, untar and build my example

  1. wget http://www.pacificsimplicity.ca/sites/default/files/uploads/libmnl-example.tar.gz
  2. tar -xzvf libmnl-example.tar.gz
  3. cd libmnl-example/
  4. make clean;make

To run the example:

sudo ./example 80

Example source readout

  1. /**
  2.  * @file main.c
  3.  *
  4.  * @author Ron Brash
  5.  * @brief Example libmnl application based on example in src provided in libmnl - for tcp only
  6.  *
  7.  * */
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <unistd.h>
  11. #include <string.h>
  12. #include <time.h>
  13. #include <arpa/inet.h>
  14.  
  15. #include <libmnl/libmnl.h>
  16. #include <linux/netfilter.h>
  17. #include <linux/netfilter/nfnetlink.h>
  18.  
  19. #ifndef aligned_be64
  20. #define aligned_be64 u_int64_t __attribute__((aligned(8)))
  21. #endif
  22.  
  23. /** Header for verdicts */
  24. #include <linux/netfilter/nfnetlink_queue.h>
  25.  
  26.  
  27. /** Global verdict variable - lazy... */
  28. int verdict;
  29.  
  30. /**
  31.  * parse_attr_cb(const struct nlattr *attr, void *data)
  32.  */
  33. static int parse_attr_cb(const struct nlattr *attr, void *data)
  34. {
  35.         const struct nlattr **tb = data;
  36.         int type = mnl_attr_get_type(attr);
  37.  
  38.         /* skip unsupported attribute in user-space */
  39.         if (mnl_attr_type_valid(attr, NFQA_MAX) < 0)
  40.                 return MNL_CB_OK;
  41.  
  42.         switch (type) {
  43.         case NFQA_MARK:
  44.         case NFQA_IFINDEX_INDEV:
  45.         case NFQA_IFINDEX_OUTDEV:
  46.         case NFQA_IFINDEX_PHYSINDEV:
  47.         case NFQA_IFINDEX_PHYSOUTDEV:
  48.                 if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
  49.                         perror("mnl_attr_validate");
  50.                         return MNL_CB_ERROR;
  51.                 }
  52.                 break;
  53.         case NFQA_TIMESTAMP:
  54.                 if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(struct nfqnl_msg_packet_timestamp)) < 0) {
  55.                         perror("mnl_attr_validate2");
  56.                         return MNL_CB_ERROR;
  57.                 }
  58.                 break;
  59.         case NFQA_HWADDR:
  60.                 if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(struct nfqnl_msg_packet_hw)) < 0) {
  61.                         perror("mnl_attr_validate2");
  62.                         return MNL_CB_ERROR;
  63.                 }
  64.                 break;
  65.         case NFQA_PAYLOAD:
  66.                 break;
  67.         }
  68.         tb[type] = attr;
  69.         return MNL_CB_OK;
  70. }
  71.  
  72. /**
  73.  * queue_cb(const struct nlmsghdr *nlh, void *data)
  74.  *
  75.  * @brief Provides the call back function which determines that there
  76.  * is data within the packet header, sets pointers, determines packetID (very important)
  77.  */
  78. static int queue_cb(const struct nlmsghdr *nlh, void *data)
  79. {
  80.         struct nlattr *tb[NFQA_MAX + 1] = { };
  81.         struct nfqnl_msg_packet_hdr *ph = NULL;
  82.         uint32_t id = 0;
  83.         unsigned char *pkt = NULL, *payload_ptr = NULL;
  84.         uint16_t pkt_size = 0, payload_len = 0;
  85.         struct iphdr *ip_ptr = NULL;
  86.         struct tcphdr *tcp_ptr = NULL;
  87.  
  88.         mnl_attr_parse(nlh, sizeof(struct nfgenmsg), parse_attr_cb, tb);
  89.         if (tb[NFQA_PACKET_HDR]) {
  90.                 ph = mnl_attr_get_payload(tb[NFQA_PACKET_HDR]);
  91.                 id = ntohl(ph->packet_id);
  92.  
  93.                 printf("packet received (id=%u hw=0xx hook=%u)\n", id, ntohs(ph->hw_protocol), ph->hook);
  94.         }
  95.  
  96.         /** Retrieve payload size */
  97.         pkt_size = mnl_attr_get_payload_len(tb[NFQA_PAYLOAD]);
  98.  
  99.         /** If there is a payload (tb[NFQA_PAYLOAD]), lets setup the pointers!*/
  100.         if (tb[NFQA_PAYLOAD]) {
  101.                 pkt = mnl_attr_get_payload(tb[NFQA_PAYLOAD]);
  102.  
  103.                 /** @note Notice that there is no Ethernet header in NFQUEUE */
  104.                 ip_ptr = (struct iphdr *)((u_int8_t *) pkt);
  105.                 u_int8_t ip_len = (ip_ptr->ihl * 4);
  106.                 tcp_ptr = (struct tcphdr *)((u_int8_t *) ip_ptr + ip_len);
  107.  
  108.                 /** If we have data, set the pointers */
  109.                 if (tcp_ptr->doff != 0) {
  110.                         payload_ptr = (u_int8_t *) tcp_ptr + (tcp_ptr->doff * sizeof(u_int32_t));
  111.                         payload_len = pkt_size - (ip_len + tcp_ptr->doff * 4);
  112.                 }
  113.  
  114.                 verdict = NF_ACCEPT;
  115.                 printf("\tverdict:%d\n",verdict);
  116.  
  117.         }
  118.  
  119.         return MNL_CB_OK + id;
  120. }
  121.  
  122. /**
  123.  * nfq_build_cfg_pf_request(char *buf, uint8_t command)
  124.  */
  125. static struct nlmsghdr *nfq_build_cfg_pf_request(char *buf, uint8_t command)
  126. {
  127.         struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
  128.         nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_CONFIG;
  129.         nlh->nlmsg_flags = NLM_F_REQUEST;
  130.  
  131.         struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
  132.         nfg->nfgen_family = AF_UNSPEC;
  133.         nfg->version = NFNETLINK_V0;
  134.  
  135.         struct nfqnl_msg_config_cmd cmd = {
  136.                 .command = command,
  137.                 .pf = htons(AF_INET),
  138.         };
  139.         mnl_attr_put(nlh, NFQA_CFG_CMD, sizeof(cmd), &cmd);
  140.  
  141.         return nlh;
  142. }
  143.  
  144. /**
  145.  * nfq_build_cfg_request(char *buf, uint8_t command, int queue_num)
  146.  */
  147. static struct nlmsghdr *nfq_build_cfg_request(char *buf, uint8_t command, int queue_num)
  148. {
  149.         struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
  150.         nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_CONFIG;
  151.         nlh->nlmsg_flags = NLM_F_REQUEST;
  152.  
  153.         struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
  154.         nfg->nfgen_family = AF_UNSPEC;
  155.         nfg->version = NFNETLINK_V0;
  156.         nfg->res_id = htons(queue_num);
  157.  
  158.         struct nfqnl_msg_config_cmd cmd = {
  159.                 .command = command,
  160.                 .pf = htons(AF_INET),
  161.         };
  162.         mnl_attr_put(nlh, NFQA_CFG_CMD, sizeof(cmd), &cmd);
  163.  
  164.         return nlh;
  165. }
  166.  
  167. /**
  168.  * nfq_build_cfg_params(char *buf, uint8_t mode, int range, int queue_num)
  169.  */
  170. static struct nlmsghdr *nfq_build_cfg_params(char *buf, uint8_t mode, int range, int queue_num)
  171. {
  172.         struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
  173.         nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_CONFIG;
  174.         nlh->nlmsg_flags = NLM_F_REQUEST;
  175.  
  176.         struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
  177.         nfg->nfgen_family = AF_UNSPEC;
  178.         nfg->version = NFNETLINK_V0;
  179.         nfg->res_id = htons(queue_num);
  180.  
  181.         struct nfqnl_msg_config_params params = {
  182.                 .copy_range = htonl(range),
  183.                 .copy_mode = mode,
  184.         };
  185.         mnl_attr_put(nlh, NFQA_CFG_PARAMS, sizeof(params), &params);
  186.  
  187.         return nlh;
  188. }
  189.  
  190. /**
  191.  * nfq_build_verdict(char *buf, int id, int queue_num, int verd)
  192.  */
  193. static struct nlmsghdr *nfq_build_verdict(char *buf, int id, int queue_num, int verd)
  194. {
  195.         struct nlmsghdr *nlh;
  196.         struct nfgenmsg *nfg;
  197.  
  198.         nlh = mnl_nlmsg_put_header(buf);
  199.         nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_VERDICT;
  200.         nlh->nlmsg_flags = NLM_F_REQUEST;
  201.         nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
  202.         nfg->nfgen_family = AF_UNSPEC;
  203.         nfg->version = NFNETLINK_V0;
  204.         nfg->res_id = htons(queue_num);
  205.  
  206.         struct nfqnl_msg_verdict_hdr vh = {
  207.                 .verdict = htonl(verd),
  208.                 .id = htonl(id),
  209.         };
  210.         mnl_attr_put(nlh, NFQA_VERDICT_HDR, sizeof(vh), &vh);
  211.  
  212.         return nlh;
  213. }
  214.  
  215. /**
  216.  * int main(int argc, char *argv[])
  217.  */
  218. int main(int argc, char *argv[])
  219. {
  220.         struct mnl_socket *nl;
  221.         char buf[MNL_SOCKET_BUFFER_SIZE];
  222.         struct nlmsghdr *nlh;
  223.         int ret;
  224.         int portid, queue_num;
  225.  
  226.         queue_num = (atoi(argv[1]));
  227.         printf("queue num: %d\n",queue_num);
  228.  
  229.         nl = mnl_socket_open(NETLINK_NETFILTER);
  230.         if (nl == NULL) {
  231.                 perror("mnl_socket_open");
  232.                 exit(EXIT_FAILURE);
  233.         }
  234.  
  235.         if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
  236.                 perror("mnl_socket_bind");
  237.                 exit(EXIT_FAILURE);
  238.         }
  239.         portid = mnl_socket_get_portid(nl);
  240.  
  241.         nlh = nfq_build_cfg_pf_request(buf, NFQNL_CFG_CMD_PF_UNBIND);
  242.  
  243.         if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
  244.                 perror("mnl_socket_sendto");
  245.                 exit(EXIT_FAILURE);
  246.         }
  247.  
  248.         nlh = nfq_build_cfg_pf_request(buf, NFQNL_CFG_CMD_PF_BIND);
  249.  
  250.         if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
  251.                 perror("mnl_socket_sendto");
  252.                 exit(EXIT_FAILURE);
  253.         }
  254.  
  255.         nlh = nfq_build_cfg_request(buf, NFQNL_CFG_CMD_BIND, queue_num);
  256.  
  257.         if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
  258.                 perror("mnl_socket_sendto");
  259.                 exit(EXIT_FAILURE);
  260.         }
  261.  
  262.         nlh = nfq_build_cfg_params(buf, NFQNL_COPY_PACKET, 0xFFFF, queue_num);
  263.  
  264.         if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
  265.                 perror("mnl_socket_sendto");
  266.                 exit(EXIT_FAILURE);
  267.         }
  268.  
  269.         printf("Starting packet loop...\n");
  270.         ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
  271.         if (ret == -1) {
  272.                 perror("mnl_socket_recvfrom");
  273.                 exit(EXIT_FAILURE);
  274.         }
  275.  
  276.         uint32_t id;
  277.         verdict = NF_ACCEPT;
  278.  
  279.         /** Packet loop */
  280.         while (ret > 0) {
  281.  
  282.                 /** Run mnl_cb - which is queue_cb */
  283.                 ret = mnl_cb_run(buf, ret, 0, portid, queue_cb, NULL);
  284.                 if (ret < 0) {
  285.                         perror("mnl_cb_run");
  286.                         exit(EXIT_FAILURE);
  287.                 }
  288.  
  289.                 /** Get packetID to generate verdict back to kernel */
  290.                 id = ret - MNL_CB_OK;
  291.                 nlh = nfq_build_verdict(buf, id, queue_num, verdict);
  292.                 if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
  293.                         perror("mnl_socket_sendto");
  294.                         exit(EXIT_FAILURE);
  295.                 }
  296.  
  297.                 ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
  298.                 if (ret == -1) {
  299.                         perror("mnl_socket_recvfrom");
  300.                         exit(EXIT_FAILURE);
  301.                 }
  302.         }
  303.  
  304.         mnl_socket_close(nl);
  305.  
  306.         return 0;
  307. }

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: 

AttachmentSize
libmnl-example.tar.gz3.13 KB

Add new comment

Filtered HTML

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
By submitting this form, you accept the Mollom privacy policy.