MMAP I/O NFLOG Daemon in Userspace Using Libmnl/nl-mmap

Here is my MMAP adapted code that extends a previous NFLOG userspace daemon. Note that I used libmnl nl-mmap branch for my libmnl library & I also used Linux Kernel 3.12.5.

  1. /**
  2.    * @copy (C) 2012 Pragmatic Software
  3.    This Source Code Form is subject to the terms of the Mozilla Public
  4.    License, v. 2.0. If a copy of the MPL was not distributed with this
  5.    file, You can obtain one at http://mozilla.org/MPL/2.0/
  6.    *
  7.    * @author [email protected] || www.pacificsimplicity.ca
  8.    * @note Modified source to use MMAP I/O which is available in the
  9.    * more recent Linux kernels 3.10+.
  10.    * gcc -g -I/usr/local/include/libmnl/ -L/usr/local/lib/ -o nflogd main.c -lmnl
  11.    * https://www.kernel.org/doc/Documentation/networking/netlink_mmap.txt
  12.    * https://git.netfilter.org/libmnl/tree/examples/netfilter/nf-queue.c?h=nl-mmap
  13.    *
  14.    * @author Original src https://code.google.com/p/iptableslog/
  15. */
  16.  
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <unistd.h>
  20. #include <string.h>
  21. #include <time.h>
  22. #include <arpa/inet.h>
  23.  
  24. #include <libmnl/libmnl.h>
  25. #include <linux/netfilter.h>
  26. #include <linux/netfilter/nfnetlink.h>
  27. #include <linux/netlink.h>
  28. #include <linux/ip.h>
  29. #include <linux/tcp.h>
  30. #include <linux/udp.h>
  31. #include <linux/icmp.h>
  32. #include <net/if.h>
  33. #include <sys/ioctl.h>
  34. #include <sys/socket.h>
  35. #include <errno.h>
  36. #include <poll.h>
  37. #include <errno.h>
  38.  
  39. #ifndef aligned_be64
  40. #define aligned_be64 u_int64_t __attribute__((aligned(8)))
  41. #endif
  42.  
  43. #include <linux/netfilter/nfnetlink_log.h>
  44.  
  45. char *netlog_if_indextoname(unsigned int ifindex, char *ifname);
  46. void free_net_devices(void);
  47. void cleanup(void);
  48.  
  49. #define MAX_NETDEVICES 32
  50. static char *devices[MAX_NETDEVICES] = { 0 };
  51.  
  52. static int parse_attr_cb(const struct nlattr *attr, void *data)
  53. {
  54.         const struct nlattr **tb = data;
  55.         int type = mnl_attr_get_type(attr);
  56.  
  57.         /* skip unsupported attribute in user-space */
  58.         if (mnl_attr_type_valid(attr, NFULA_MAX) < 0)
  59.                 return MNL_CB_OK;
  60.  
  61.         switch (type) {
  62.         case NFULA_MARK:
  63.         case NFULA_IFINDEX_INDEV:
  64.         case NFULA_IFINDEX_OUTDEV:
  65.         case NFULA_IFINDEX_PHYSINDEV:
  66.         case NFULA_IFINDEX_PHYSOUTDEV:
  67.                 if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
  68.                         perror("mnl_attr_validate");
  69.                         return MNL_CB_ERROR;
  70.                 }
  71.                 break;
  72.         case NFULA_TIMESTAMP:
  73.                 if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(struct nfulnl_msg_packet_timestamp)) < 0) {
  74.                         perror("mnl_attr_validate");
  75.                         return MNL_CB_ERROR;
  76.                 }
  77.                 break;
  78.         case NFULA_HWADDR:
  79.                 if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(struct nfulnl_msg_packet_hw)) < 0) {
  80.                         perror("mnl_attr_validate");
  81.                         return MNL_CB_ERROR;
  82.                 }
  83.                 break;
  84.         case NFULA_PREFIX:
  85.                 if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0) {
  86.                         perror("mnl_attr_validate");
  87.                         return MNL_CB_ERROR;
  88.                 }
  89.                 break;
  90.         case NFULA_PAYLOAD:
  91.                 break;
  92.         }
  93.         tb[type] = attr;
  94.         return MNL_CB_OK;
  95. }
  96.  
  97. char *netlog_if_indextoname(unsigned int ifindex, char *ifname)
  98. {
  99.         /* We may be able to do the conversion directly, rather than searching a
  100.          *      list.  This ioctl is not present in kernels before version 2.1.50.  */
  101.         struct ifreq ifr;
  102.         int fd;
  103.         int status;
  104.  
  105.         fd = socket(AF_INET, SOCK_DGRAM, 0);
  106.  
  107.         if (fd < 0)
  108.                 return NULL;
  109.  
  110.         ifr.ifr_ifindex = ifindex;
  111.         status = ioctl(fd, SIOCGIFNAME, &ifr);
  112.  
  113.         close(fd);
  114.  
  115.         if (status < 0) {
  116.                 if (errno == ENODEV)
  117.                         /* POSIX requires ENXIO.  */
  118.                         errno = ENXIO;
  119.  
  120.                 return NULL;
  121.         } else
  122.                 return strncpy(ifname, ifr.ifr_name, IFNAMSIZ);
  123. }
  124.  
  125. static inline char *get_net_device_name_by_index(int ifindex)
  126. {
  127.         if (ifindex < 0 || ifindex > MAX_NETDEVICES - 1) {
  128.                 return NULL;
  129.         }
  130.  
  131.         if (!devices[ifindex]) {
  132.                 devices[ifindex] = malloc(IFNAMSIZ);
  133.                 if (!devices[ifindex]) {
  134.                         perror("malloc");
  135.                         exit(EXIT_FAILURE);
  136.                 }
  137.  
  138.                 netlog_if_indextoname(ifindex, devices[ifindex]);
  139.                 if (!devices[ifindex]) {
  140.                         perror("if_indextoname");
  141.                         exit(EXIT_FAILURE);
  142.                 }
  143.         }
  144.  
  145.         return devices[ifindex];
  146. }
  147.  
  148. void free_net_devices(void)
  149. {
  150.         int i;
  151.         for (i = 0; i < MAX_NETDEVICES - 1; i++) {
  152.                 if (devices[i]) {
  153.                         free(devices[i]);
  154.                 }
  155.         }
  156. }
  157.  
  158. static int log_cb(const struct nlmsghdr *nlh, void *data)
  159. {
  160.         printf("here \n");
  161.         struct nlattr *tb[NFULA_MAX + 1] = { };
  162.  
  163.         mnl_attr_parse(nlh, sizeof(struct nfgenmsg), parse_attr_cb, tb);
  164.  
  165.         if (tb[NFULA_PREFIX]) {
  166.                 const char *prefix = mnl_attr_get_str(tb[NFULA_PREFIX]);
  167.                 printf("%s ", prefix);
  168.         }
  169.  
  170.         if (tb[NFULA_IFINDEX_INDEV]) {
  171.                 uint32_t indev = ntohl(mnl_attr_get_u32(tb[NFULA_IFINDEX_INDEV]));
  172.                 char *instr = get_net_device_name_by_index(indev);
  173.                 printf("IN=%s ", instr ? instr : "");
  174.         } else {
  175.                 printf("IN= ");
  176.         }
  177.  
  178.         if (tb[NFULA_IFINDEX_OUTDEV]) {
  179.                 uint32_t outdev = ntohl(mnl_attr_get_u32(tb[NFULA_IFINDEX_OUTDEV]));
  180.                 char *outstr = get_net_device_name_by_index(outdev);
  181.                 printf("OUT=%s ", outstr ? outstr : "");
  182.         } else {
  183.                 printf("OUT= ");
  184.         }
  185.  
  186.         if (tb[NFULA_PAYLOAD]) {
  187.                 struct iphdr *iph = (struct iphdr *)mnl_attr_get_payload(tb[NFULA_PAYLOAD]);
  188.  
  189.                 printf("SRC=%u.%u.%u.%u DST=%u.%u.%u.%u ",
  190.                        ((unsigned char *)&iph->saddr)[0],
  191.                        ((unsigned char *)&iph->saddr)[1],
  192.                        ((unsigned char *)&iph->saddr)[2],
  193.                        ((unsigned char *)&iph->saddr)[3],
  194.                        ((unsigned char *)&iph->daddr)[0],
  195.                        ((unsigned char *)&iph->daddr)[1],
  196.                        ((unsigned char *)&iph->daddr)[2], ((unsigned char *)&iph->daddr)[3]);
  197.  
  198.                 printf("LEN=%u ", ntohs(iph->tot_len));
  199.  
  200.                 switch (iph->protocol) {
  201.                 case IPPROTO_TCP:
  202.                         {
  203.                                 struct tcphdr *th = (struct tcphdr *)((__u32 *) iph + iph->ihl);
  204.                                 printf("PROTO=TCP SPT=%u DPT=%u ", ntohs(th->source), ntohs(th->dest));
  205.                                 break;
  206.                         }
  207.                 case IPPROTO_UDP:
  208.                         {
  209.                                 struct udphdr *uh = (struct udphdr *)((__u32 *) iph + iph->ihl);
  210.                                 printf("PROTO=UDP SPT=%u DPT=%u LEN=%u ",
  211.                                        ntohs(uh->source), ntohs(uh->dest), ntohs(uh->len));
  212.                                 break;
  213.                         }
  214.                 case IPPROTO_ICMP:
  215.                         {
  216.                                 struct icmphdr *ich = (struct icmphdr *)((__u32 *) iph + iph->ihl);
  217.                                 printf("PROTO=ICMP TYPE=%u CODE=%u ", ich->type, ich->code);
  218.                                 break;
  219.                         }
  220.                 default:
  221.                         {
  222.                                 printf("PROTO=%u ", iph->protocol);
  223.                         }
  224.                 }
  225.         }
  226.  
  227.         if (tb[NFULA_UID]) {
  228.                 uint32_t uid = ntohl(mnl_attr_get_u32(tb[NFULA_UID]));
  229.                 printf("UID=%u ", uid);
  230.         }
  231.  
  232.         puts("");
  233.         fflush(stdout);         // apparently this is necessary for some devices
  234.  
  235.         return MNL_CB_OK;
  236. }
  237.  
  238. static struct nlmsghdr *nflog_build_cfg_pf_request(struct mnl_socket *nl, uint8_t command)
  239. {
  240.         struct nl_mmap_hdr *hdr;
  241.  
  242.         hdr = mnl_socket_get_frame(nl, MNL_RING_TX);
  243.         if (hdr->nm_status != NL_MMAP_STATUS_UNUSED)
  244.                 return NULL;
  245.         mnl_socket_advance_ring(nl, MNL_RING_TX);
  246.  
  247.         struct nlmsghdr *nlh = mnl_nlmsg_put_header((void *)hdr + NL_MMAP_HDRLEN);
  248.         nlh->nlmsg_type = (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG;
  249.         nlh->nlmsg_flags = NLM_F_REQUEST;
  250.  
  251.         struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
  252.         nfg->nfgen_family = AF_INET;
  253.         nfg->version = NFNETLINK_V0;
  254.  
  255.         struct nfulnl_msg_config_cmd cmd = {
  256.                 .command = command,
  257.         };
  258.         mnl_attr_put(nlh, NFULA_CFG_CMD, sizeof(cmd), &cmd);
  259.  
  260.         hdr->nm_len = nlh->nlmsg_len;
  261.         hdr->nm_status = NL_MMAP_STATUS_VALID;
  262.         return nlh;
  263. }
  264.  
  265. static struct nlmsghdr *nflog_build_cfg_request(struct mnl_socket *nl, uint8_t command, int nflognum)
  266. {
  267.         struct nl_mmap_hdr *hdr;
  268.  
  269.         hdr = mnl_socket_get_frame(nl, MNL_RING_TX);
  270.         if (hdr->nm_status != NL_MMAP_STATUS_UNUSED)
  271.                 return NULL;
  272.         mnl_socket_advance_ring(nl, MNL_RING_TX);
  273.  
  274.         struct nlmsghdr *nlh = mnl_nlmsg_put_header((void *)hdr + NL_MMAP_HDRLEN);
  275.         nlh->nlmsg_type = (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG;
  276.         nlh->nlmsg_flags = NLM_F_REQUEST;
  277.  
  278.         struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
  279.         nfg->nfgen_family = AF_INET;
  280.         nfg->version = NFNETLINK_V0;
  281.         nfg->res_id = htons(nflognum);
  282.  
  283.         struct nfulnl_msg_config_cmd cmd = {
  284.                 .command = command,
  285.         };
  286.         mnl_attr_put(nlh, NFULA_CFG_CMD, sizeof(cmd), &cmd);
  287.  
  288.         hdr->nm_len = nlh->nlmsg_len;
  289.         hdr->nm_status = NL_MMAP_STATUS_VALID;
  290.         return nlh;
  291. }
  292.  
  293. static struct nlmsghdr *nflog_build_cfg_params(struct mnl_socket *nl, uint8_t mode, int range, int nflognum)
  294. {
  295.         struct nl_mmap_hdr *hdr;
  296.  
  297.         hdr = mnl_socket_get_frame(nl, MNL_RING_TX);
  298.         if (hdr->nm_status != NL_MMAP_STATUS_UNUSED)
  299.                 return NULL;
  300.         mnl_socket_advance_ring(nl, MNL_RING_TX);
  301.  
  302.         struct nlmsghdr *nlh = mnl_nlmsg_put_header((void *)hdr + NL_MMAP_HDRLEN);
  303.         nlh->nlmsg_type = (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG;
  304.         nlh->nlmsg_flags = NLM_F_REQUEST;
  305.  
  306.         struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
  307.         nfg->nfgen_family = AF_UNSPEC;
  308.         nfg->version = NFNETLINK_V0;
  309.         nfg->res_id = htons(nflognum);
  310.  
  311.         struct nfulnl_msg_config_mode params = {
  312.                 .copy_range = htonl(range),
  313.                 .copy_mode = mode,
  314.         };
  315.         mnl_attr_put(nlh, NFULA_CFG_MODE, sizeof(params), &params);
  316.  
  317.         hdr->nm_len = nlh->nlmsg_len;
  318.         hdr->nm_status = NL_MMAP_STATUS_VALID;
  319.         return nlh;
  320. }
  321.  
  322. struct mnl_socket *nl = 0;
  323.  
  324. void cleanup(void)
  325. {
  326.         if (nl != 0)
  327.                 mnl_socket_close(nl);
  328.         free_net_devices();
  329. }
  330.  
  331. static int mnl_socket_poll(struct mnl_socket *nl)
  332. {
  333.         struct pollfd pfds[1];
  334.  
  335.         while (1) {
  336.                 pfds[0].fd = mnl_socket_get_fd(nl);
  337.                 pfds[0].events = POLLIN | POLLERR;
  338.                 pfds[0].revents = 0;
  339.  
  340.                 if (poll(pfds, 1, -1) < 0 && errno != -EINTR)
  341.                         return -1;
  342.  
  343.                 if (pfds[0].revents & POLLIN)
  344.                         return 0;
  345.                 if (pfds[0].revents & POLLERR)
  346.                         return -1;
  347.         }
  348. }
  349.  
  350. int main(int argc, char *argv[])
  351. {
  352.         char buf[MNL_SOCKET_BUFFER_SIZE];
  353.         struct mnl_socket *nl;
  354.         struct nlmsghdr *nlh;
  355.         struct nl_mmap_hdr *hdr;
  356.         ssize_t len;
  357.         void *ptr;
  358.         int ret, buffersize = MNL_SOCKET_BUFFER_SIZE;
  359.         unsigned int portid, nflognum;
  360.  
  361.         atexit(cleanup);
  362.  
  363.         if (argc != 2) {
  364.                 printf("Usage: %s [nflog_group_num]\n", argv[0]);
  365.                 exit(EXIT_FAILURE);
  366.         }
  367.         nflognum = atoi(argv[1]);
  368.  
  369.         nl = mnl_socket_open(NETLINK_NETFILTER);
  370.         if (nl == NULL) {
  371.                 perror("mnl_socket_open");
  372.                 exit(EXIT_FAILURE);
  373.         }
  374.  
  375.         setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(socklen_t));
  376.  
  377.         mnl_socket_setsockopt(nl, NETLINK_BROADCAST_ERROR, 0, sizeof(int));
  378.         mnl_socket_setsockopt(nl, NETLINK_NO_ENOBUFS, 0, sizeof(int));
  379.  
  380.         if (mnl_socket_set_ring(nl, 0, 0) < 0) {
  381.                 perror("mnl_socket_set_ring");
  382.                 exit(EXIT_FAILURE);
  383.         }
  384.         if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
  385.                 perror("mnl_socket_bind");
  386.                 exit(EXIT_FAILURE);
  387.         }
  388.         portid = mnl_socket_get_portid(nl);
  389.  
  390.         nlh = nflog_build_cfg_pf_request(nl, NFULNL_CFG_CMD_PF_UNBIND);
  391.  
  392.         if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
  393.                 perror("mnl_socket_send");
  394.                 exit(EXIT_FAILURE);
  395.         }
  396.  
  397.         nlh = nflog_build_cfg_pf_request(nl, NFULNL_CFG_CMD_PF_BIND);
  398.  
  399.         if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
  400.                 perror("mnl_socket_send");
  401.                 exit(EXIT_FAILURE);
  402.         }
  403.  
  404.         nlh = nflog_build_cfg_request(nl, NFULNL_CFG_CMD_BIND, nflognum);
  405.  
  406.         if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
  407.                 perror("mnl_socket_send");
  408.                 exit(EXIT_FAILURE);
  409.         }
  410.  
  411.         nlh = nflog_build_cfg_params(nl, NFULNL_COPY_PACKET, 0xFFFF, nflognum);
  412.  
  413.         if (mnl_socket_sendto(nl, NULL, 0) < 0) {
  414.                 perror("mnl_socket_send");
  415.                 exit(EXIT_FAILURE);
  416.         }
  417.  
  418.         while (1) {
  419.                 ret = mnl_socket_poll(nl);
  420.                 if (ret < 0) {
  421.                         perror("mnl_socket_poll");
  422.                         exit(EXIT_FAILURE);
  423.                 }
  424.  
  425.                 while (1) {
  426.                         hdr = mnl_socket_get_frame(nl, MNL_RING_RX);
  427.                         if (hdr->nm_status == NL_MMAP_STATUS_VALID) {
  428.                                 ptr = (void *)hdr + NL_MMAP_HDRLEN;
  429.                                 len = hdr->nm_len;
  430.                                 if (len == 0) {
  431.                                         goto next;
  432.                                 }
  433.                         } else if (hdr->nm_status == NL_MMAP_STATUS_COPY) {
  434.                                 len = recv(mnl_socket_get_fd(nl), buf, sizeof(buf), MSG_DONTWAIT);
  435.                                 if (len <= 0) {
  436.                                         break;
  437.                                 }
  438.                                 ptr = buf;
  439.                         } else {
  440.                                 break;
  441.                         }
  442.  
  443.                         ret = mnl_cb_run(ptr, len, 0, portid, log_cb, NULL);
  444.  
  445.  next:
  446.                         hdr->nm_status = NL_MMAP_STATUS_UNUSED;
  447.                         mnl_socket_advance_ring(nl, MNL_RING_RX);
  448.  
  449.                 }
  450.  
  451.         }
  452.  
  453.         return 0;
  454. }

Blog tags: 

AttachmentSize
nflogd-libmnl-mmap.c11.07 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.