ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/src/trunk/contrib/libpcap/pcap-netfilter-linux.c
(Generate patch)

Comparing trunk/contrib/libpcap/pcap-netfilter-linux.c (file contents):
Revision 11136 by laffer1, Sat Sep 17 22:41:55 2016 UTC vs.
Revision 11137 by laffer1, Wed Jun 27 00:14:22 2018 UTC

# Line 51 | Line 51
51   #include <linux/types.h>
52  
53   #include <linux/netlink.h>
54 + #include <linux/netfilter.h>
55   #include <linux/netfilter/nfnetlink.h>
56   #include <linux/netfilter/nfnetlink_log.h>
57 + #include <linux/netfilter/nfnetlink_queue.h>
58  
59 + /* NOTE: if your program drops privilages after pcap_activate() it WON'T work with nfqueue.
60 + *       It took me quite some time to debug ;/
61 + *
62 + *       Sending any data to nfnetlink socket requires CAP_NET_ADMIN privilages,
63 + *       and in nfqueue we need to send verdict reply after recving packet.
64 + *
65 + *       In tcpdump you can disable dropping privilages with -Z root
66 + */
67 +
68   #include "pcap-netfilter-linux.h"
69  
70   #define HDR_LENGTH (NLMSG_LENGTH(NLMSG_ALIGN(sizeof(struct nfgenmsg))))
71  
72   #define NFLOG_IFACE "nflog"
73 + #define NFQUEUE_IFACE "nfqueue"
74  
75 + typedef enum { OTHER = -1, NFLOG, NFQUEUE } nftype_t;
76 +
77 + static int nfqueue_send_verdict(const pcap_t *handle, u_int16_t group_id, u_int32_t id, u_int32_t verdict);
78 +
79   static int
80 < nflog_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user)
80 > netfilter_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user)
81   {
82          const unsigned char *buf;
83          int count = 0;
# Line 85 | Line 101 | nflog_read_linux(pcap_t *handle, int max_packets, pcap
101          while (len >= NLMSG_SPACE(0)) {
102                  const struct nlmsghdr *nlh = (const struct nlmsghdr *) buf;
103                  u_int32_t msg_len;
104 +                nftype_t type = OTHER;
105  
106                  if (nlh->nlmsg_len < sizeof(struct nlmsghdr) || len < nlh->nlmsg_len) {
107                          snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Message truncated: (got: %d) (nlmsg_len: %u)", len, nlh->nlmsg_len);
# Line 93 | Line 110 | nflog_read_linux(pcap_t *handle, int max_packets, pcap
110  
111                  if (NFNL_SUBSYS_ID(nlh->nlmsg_type) == NFNL_SUBSYS_ULOG &&
112                          NFNL_MSG_TYPE(nlh->nlmsg_type) == NFULNL_MSG_PACKET)
113 <                {
113 >                                type = NFLOG;
114 >
115 >                if (NFNL_SUBSYS_ID(nlh->nlmsg_type) == NFNL_SUBSYS_QUEUE &&
116 >                        NFNL_MSG_TYPE(nlh->nlmsg_type) == NFQNL_MSG_PACKET)
117 >                                type = NFQUEUE;
118 >
119 >                if (type != OTHER) {
120                          const unsigned char *payload = NULL;
121                          struct pcap_pkthdr pkth;
122  
123 +                        const struct nfgenmsg *nfg;
124 +                        int id = 0;
125 +
126                          if (handle->linktype != DLT_NFLOG) {
127                                  const struct nfattr *payload_attr = NULL;
128  
# Line 105 | Line 131 | nflog_read_linux(pcap_t *handle, int max_packets, pcap
131                                          return -1;
132                                  }
133  
134 +                                nfg = NLMSG_DATA(nlh);
135                                  if (nlh->nlmsg_len > HDR_LENGTH) {
136 <                                        struct nfattr *attr = NFM_NFA(NLMSG_DATA(nlh));
136 >                                        struct nfattr *attr = NFM_NFA(nfg);
137                                          int attr_len = nlh->nlmsg_len - NLMSG_ALIGN(HDR_LENGTH);
138  
139                                          while (NFA_OK(attr, attr_len)) {
140 <                                                switch (NFA_TYPE(attr)) {
141 <                                                        case NFULA_PAYLOAD:
142 <                                                                payload_attr = attr;
143 <                                                                break;
140 >                                                if (type == NFQUEUE) {
141 >                                                        switch (NFA_TYPE(attr)) {
142 >                                                                case NFQA_PACKET_HDR:
143 >                                                                        {
144 >                                                                                const struct nfqnl_msg_packet_hdr *pkt_hdr = (const struct nfqnl_msg_packet_hdr *) NFA_DATA(attr);
145 >
146 >                                                                                id = ntohl(pkt_hdr->packet_id);
147 >                                                                                break;
148 >                                                                        }
149 >                                                                case NFQA_PAYLOAD:
150 >                                                                        payload_attr = attr;
151 >                                                                        break;
152 >                                                        }
153 >
154 >                                                } else if (type == NFLOG) {
155 >                                                        switch (NFA_TYPE(attr)) {
156 >                                                                case NFULA_PAYLOAD:
157 >                                                                        payload_attr = attr;
158 >                                                                        break;
159 >                                                        }
160                                                  }
161                                                  attr = NFA_NEXT(attr, attr_len);
162                                          }
# Line 141 | Line 184 | nflog_read_linux(pcap_t *handle, int max_packets, pcap
184                                          count++;
185                                  }
186                          }
187 +
188 +                        if (type == NFQUEUE) {
189 +                                /* XXX, possible responses: NF_DROP, NF_ACCEPT, NF_STOLEN, NF_QUEUE, NF_REPEAT, NF_STOP */
190 +                                nfqueue_send_verdict(handle, ntohs(nfg->res_id), id, NF_ACCEPT);
191 +                        }
192                  }
193  
194                  msg_len = NLMSG_ALIGN(nlh->nlmsg_len);
# Line 183 | Line 231 | struct my_nfattr {
231   };
232  
233   static int
234 < nflog_send_config_msg(const pcap_t *handle, u_int8_t family, u_int16_t res_id, const struct my_nfattr *mynfa)
234 > netfilter_send_config_msg(const pcap_t *handle, u_int16_t msg_type, int ack, u_int8_t family, u_int16_t res_id, const struct my_nfattr *mynfa)
235   {
236          char buf[1024] __attribute__ ((aligned));
237  
# Line 198 | Line 246 | nflog_send_config_msg(const pcap_t *handle, u_int8_t f
246          ++seq_id;
247  
248          nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nfgenmsg));
249 <        nlh->nlmsg_type = (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG;
250 <        nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
249 >        nlh->nlmsg_type = msg_type;
250 >        nlh->nlmsg_flags = NLM_F_REQUEST | (ack ? NLM_F_ACK : 0);
251          nlh->nlmsg_pid = 0;     /* to kernel */
252          nlh->nlmsg_seq = seq_id;
253  
# Line 222 | Line 270 | nflog_send_config_msg(const pcap_t *handle, u_int8_t f
270          if (sendto(handle->fd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *) &snl, sizeof(snl)) == -1)
271                  return -1;
272  
273 +        if (!ack)
274 +                return 0;
275 +
276          /* waiting for reply loop */
277          do {
278                  socklen_t addrlen = sizeof(snl);
# Line 261 | Line 312 | nflog_send_config_msg(const pcap_t *handle, u_int8_t f
312   }
313  
314   static int
315 + nflog_send_config_msg(const pcap_t *handle, u_int8_t family, u_int16_t group_id, const struct my_nfattr *mynfa)
316 + {
317 +        return netfilter_send_config_msg(handle, (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG, 1, family, group_id, mynfa);
318 + }
319 +
320 + static int
321   nflog_send_config_cmd(const pcap_t *handle, u_int16_t group_id, u_int8_t cmd, u_int8_t family)
322   {
323          struct nfulnl_msg_config_cmd msg;
# Line 292 | Line 349 | nflog_send_config_mode(const pcap_t *handle, u_int16_t
349   }
350  
351   static int
352 < nflog_activate(pcap_t* handle)
352 > nfqueue_send_verdict(const pcap_t *handle, u_int16_t group_id, u_int32_t id, u_int32_t verdict)
353   {
354 +        struct nfqnl_msg_verdict_hdr msg;
355 +        struct my_nfattr nfa;
356 +
357 +        msg.id = htonl(id);
358 +        msg.verdict = htonl(verdict);
359 +
360 +        nfa.data = &msg;
361 +        nfa.nfa_type = NFQA_VERDICT_HDR;
362 +        nfa.nfa_len = sizeof(msg);
363 +
364 +        return netfilter_send_config_msg(handle, (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_VERDICT, 0, AF_UNSPEC, group_id, &nfa);
365 + }
366 +
367 + static int
368 + nfqueue_send_config_msg(const pcap_t *handle, u_int8_t family, u_int16_t group_id, const struct my_nfattr *mynfa)
369 + {
370 +        return netfilter_send_config_msg(handle, (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_CONFIG, 1, family, group_id, mynfa);
371 + }
372 +
373 + static int
374 + nfqueue_send_config_cmd(const pcap_t *handle, u_int16_t group_id, u_int8_t cmd, u_int16_t pf)
375 + {
376 +        struct nfqnl_msg_config_cmd msg;
377 +        struct my_nfattr nfa;
378 +
379 +        msg.command = cmd;
380 +        msg.pf = htons(pf);
381 +
382 +        nfa.data = &msg;
383 +        nfa.nfa_type = NFQA_CFG_CMD;
384 +        nfa.nfa_len = sizeof(msg);
385 +
386 +        return nfqueue_send_config_msg(handle, AF_UNSPEC, group_id, &nfa);
387 + }
388 +
389 + static int
390 + nfqueue_send_config_mode(const pcap_t *handle, u_int16_t group_id, u_int8_t copy_mode, u_int32_t copy_range)
391 + {
392 +        struct nfqnl_msg_config_params msg;
393 +        struct my_nfattr nfa;
394 +
395 +        msg.copy_range = htonl(copy_range);
396 +        msg.copy_mode = copy_mode;
397 +
398 +        nfa.data = &msg;
399 +        nfa.nfa_type = NFQA_CFG_PARAMS;
400 +        nfa.nfa_len = sizeof(msg);
401 +
402 +        return nfqueue_send_config_msg(handle, AF_UNSPEC, group_id, &nfa);
403 + }
404 +
405 + static int
406 + netfilter_activate(pcap_t* handle)
407 + {
408          const char *dev = handle->opt.source;
409          unsigned short groups[32];
410          int group_count = 0;
411 +        nftype_t type = OTHER;
412          int i;
413  
414 <        if (strncmp(dev, NFLOG_IFACE, strlen(NFLOG_IFACE)) == 0) {
415 <                dev += strlen(NFLOG_IFACE);
414 >        if (strncmp(dev, NFLOG_IFACE, strlen(NFLOG_IFACE)) == 0) {
415 >                dev += strlen(NFLOG_IFACE);
416 >                type = NFLOG;
417  
418 <                /* nflog:30,33,42 looks nice, allow it */
419 <                if (*dev == ':')
420 <                        dev++;
421 <
418 >        } else if (strncmp(dev, NFQUEUE_IFACE, strlen(NFQUEUE_IFACE)) == 0) {
419 >                dev += strlen(NFQUEUE_IFACE);
420 >                type = NFQUEUE;
421 >        }
422 >
423 >        if (type != OTHER && *dev == ':') {
424 >                dev++;
425                  while (*dev) {
426                          long int group_id;
427                          char *end_dev;
# Line 335 | Line 451 | nflog_activate(pcap_t* handle)
451                  }
452          }
453  
454 <        if (*dev) {
454 >        if (type == OTHER || *dev) {
455                  snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
456                                  "Can't get netfilter group(s) index from %s",
457                                  handle->opt.source);
# Line 351 | Line 467 | nflog_activate(pcap_t* handle)
467          /* Initialize some components of the pcap structure. */
468          handle->bufsize = 128 + handle->snapshot;
469          handle->offset = 0;
470 <        handle->linktype = DLT_NFLOG;
355 <        handle->read_op = nflog_read_linux;
470 >        handle->read_op = netfilter_read_linux;
471          handle->inject_op = netfilter_inject_linux;
472          handle->setfilter_op = install_bpf_program; /* no kernel filtering */
473          handle->setdirection_op = NULL;
# Line 369 | Line 484 | nflog_activate(pcap_t* handle)
484                  return PCAP_ERROR;
485          }
486  
487 <        handle->dlt_list = (u_int *) malloc(sizeof(u_int) * 2);
488 <        if (handle->dlt_list != NULL) {
489 <                handle->dlt_list[0] = DLT_NFLOG;
490 <                handle->dlt_list[1] = DLT_IPV4;
491 <                handle->dlt_count = 2;
492 <        }
487 >        if (type == NFLOG) {
488 >                handle->linktype = DLT_NFLOG;
489 >                handle->dlt_list = (u_int *) malloc(sizeof(u_int) * 2);
490 >                if (handle->dlt_list != NULL) {
491 >                        handle->dlt_list[0] = DLT_NFLOG;
492 >                        handle->dlt_list[1] = DLT_IPV4;
493 >                        handle->dlt_count = 2;
494 >                }
495  
496 +        } else
497 +                handle->linktype = DLT_IPV4;
498 +
499          handle->buffer = malloc(handle->bufsize);
500          if (!handle->buffer) {
501                  snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't allocate dump buffer: %s", pcap_strerror(errno));
502                  goto close_fail;
503          }
504  
505 <        if (nflog_send_config_cmd(handle, 0, NFULNL_CFG_CMD_PF_UNBIND, AF_INET) < 0) {
506 <                snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "NFULNL_CFG_CMD_PF_UNBIND: %s", pcap_strerror(errno));
507 <                goto close_fail;
508 <        }
505 >        if (type == NFLOG) {
506 >                if (nflog_send_config_cmd(handle, 0, NFULNL_CFG_CMD_PF_UNBIND, AF_INET) < 0) {
507 >                        snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "NFULNL_CFG_CMD_PF_UNBIND: %s", pcap_strerror(errno));
508 >                        goto close_fail;
509 >                }
510  
511 <        if (nflog_send_config_cmd(handle, 0, NFULNL_CFG_CMD_PF_BIND, AF_INET) < 0) {
512 <                snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "NFULNL_CFG_CMD_PF_BIND: %s", pcap_strerror(errno));
513 <                goto close_fail;
514 <        }
511 >                if (nflog_send_config_cmd(handle, 0, NFULNL_CFG_CMD_PF_BIND, AF_INET) < 0) {
512 >                        snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "NFULNL_CFG_CMD_PF_BIND: %s", pcap_strerror(errno));
513 >                        goto close_fail;
514 >                }
515  
516 <        /* Bind socket to the nflog groups */
517 <        for (i = 0; i < group_count; i++) {
518 <                if (nflog_send_config_cmd(handle, groups[i], NFULNL_CFG_CMD_BIND, AF_UNSPEC) < 0) {
519 <                        snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't listen on group group index: %s", pcap_strerror(errno));
516 >                /* Bind socket to the nflog groups */
517 >                for (i = 0; i < group_count; i++) {
518 >                        if (nflog_send_config_cmd(handle, groups[i], NFULNL_CFG_CMD_BIND, AF_UNSPEC) < 0) {
519 >                                snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't listen on group group index: %s", pcap_strerror(errno));
520 >                                goto close_fail;
521 >                        }
522 >
523 >                        if (nflog_send_config_mode(handle, groups[i], NFULNL_COPY_PACKET, handle->snapshot) < 0) {
524 >                                snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "NFULNL_COPY_PACKET: %s", pcap_strerror(errno));
525 >                                goto close_fail;
526 >                        }
527 >                }
528 >
529 >        } else {
530 >                if (nfqueue_send_config_cmd(handle, 0, NFQNL_CFG_CMD_PF_UNBIND, AF_INET) < 0) {
531 >                        snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "NFQNL_CFG_CMD_PF_UNBIND: %s", pcap_strerror(errno));
532                          goto close_fail;
533                  }
534  
535 <                if (nflog_send_config_mode(handle, groups[i], NFULNL_COPY_PACKET, handle->snapshot) < 0) {
536 <                        snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "NFULNL_COPY_PACKET: %s", pcap_strerror(errno));
535 >                if (nfqueue_send_config_cmd(handle, 0, NFQNL_CFG_CMD_PF_BIND, AF_INET) < 0) {
536 >                        snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "NFQNL_CFG_CMD_PF_BIND: %s", pcap_strerror(errno));
537                          goto close_fail;
538                  }
539 +
540 +                /* Bind socket to the nfqueue groups */
541 +                for (i = 0; i < group_count; i++) {
542 +                        if (nfqueue_send_config_cmd(handle, groups[i], NFQNL_CFG_CMD_BIND, AF_UNSPEC) < 0) {
543 +                                snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't listen on group group index: %s", pcap_strerror(errno));
544 +                                goto close_fail;
545 +                        }
546 +
547 +                        if (nfqueue_send_config_mode(handle, groups[i], NFQNL_COPY_PACKET, handle->snapshot) < 0) {
548 +                                snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "NFQNL_COPY_PACKET: %s", pcap_strerror(errno));
549 +                                goto close_fail;
550 +                        }
551 +                }
552          }
553  
554          if (handle->opt.rfmon) {
# Line 432 | Line 578 | close_fail:
578   }
579  
580   pcap_t *
581 < nflog_create(const char *device, char *ebuf)
581 > netfilter_create(const char *device, char *ebuf, int *is_ours)
582   {
583 +        const char *cp;
584          pcap_t *p;
585  
586 +        /* Does this look like an netfilter device? */
587 +        cp = strrchr(device, '/');
588 +        if (cp == NULL)
589 +                cp = device;
590 +
591 +        /* Does it begin with NFLOG_IFACE or NFQUEUE_IFACE? */
592 +        if (strncmp(cp, NFLOG_IFACE, sizeof NFLOG_IFACE - 1) == 0)
593 +                cp += sizeof NFLOG_IFACE - 1;
594 +        else if (strncmp(cp, NFQUEUE_IFACE, sizeof NFQUEUE_IFACE - 1) == 0)
595 +                cp += sizeof NFQUEUE_IFACE - 1;
596 +        else {
597 +                /* Nope, doesn't begin with NFLOG_IFACE nor NFQUEUE_IFACE */
598 +                *is_ours = 0;
599 +                return NULL;
600 +        }
601 +
602 +        /*
603 +         * Yes - is that either the end of the name, or is it followed
604 +         * by a colon?
605 +         */
606 +        if (*cp != ':' && *cp != '\0') {
607 +                /* Nope */
608 +                *is_ours = 0;
609 +                return NULL;
610 +        }
611 +
612 +        /* OK, it's probably ours. */
613 +        *is_ours = 1;
614 +
615          p = pcap_create_common(device, ebuf);
616          if (p == NULL)
617                  return (NULL);
618  
619 <        p->activate_op = nflog_activate;
619 >        p->activate_op = netfilter_activate;
620          return (p);
621   }
622  
623   int
624 < netfilter_platform_finddevs(pcap_if_t **alldevsp, char *err_str)
624 > netfilter_findalldevs(pcap_if_t **alldevsp, char *err_str)
625   {
626          pcap_if_t *found_dev = *alldevsp;
627          int sock;
# Line 463 | Line 639 | netfilter_platform_finddevs(pcap_if_t **alldevsp, char
639  
640          if (pcap_add_if(&found_dev, NFLOG_IFACE, 0, "Linux netfilter log (NFLOG) interface", err_str) < 0)
641                  return -1;
642 +        if (pcap_add_if(&found_dev, NFQUEUE_IFACE, 0, "Linux netfilter queue (NFQUEUE) interface", err_str) < 0)
643 +                return -1;
644          return 0;
645   }
468

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines