diff options
author | Adrian Budau <adbudau@bitdefender.com> | 2018-08-06 13:35:17 +0300 |
---|---|---|
committer | Adrian Budau <adbudau@bitdefender.com> | 2018-08-06 13:37:30 +0300 |
commit | 5c8b13d3e87542527ed9a3a79fb0f9b2edb74df1 (patch) | |
tree | a779b985f8edc5b1ed7e29634710dfd1a9fda268 /pcap-linux.c | |
parent | e217f88289b9601e3a9a8d13477f867bcb83b5e2 (diff) |
Proper breakloop for linux on pcap_dispatch
On platforms that support it use an eventfd to exit any polling.
Diffstat (limited to 'pcap-linux.c')
-rw-r--r-- | pcap-linux.c | 70 |
1 files changed, 60 insertions, 10 deletions
diff --git a/pcap-linux.c b/pcap-linux.c index caec4432..a00cb871 100644 --- a/pcap-linux.c +++ b/pcap-linux.c @@ -142,6 +142,11 @@ #include "pcap/sll.h" #include "pcap/vlan.h" +#ifdef HAVE_SYS_EVENTFD_H +#include <sys/eventfd.h> +#endif + + /* * If PF_PACKET is defined, we can use {SOCK_RAW,SOCK_DGRAM}/PF_PACKET * sockets rather than SOCK_PACKET sockets. @@ -324,6 +329,10 @@ struct pcap_linux { unsigned char *current_packet; /* Current packet within the TPACKET_V3 block. Move to next block if NULL. */ int packets_left; /* Unhandled packets left within the block from previous call to pcap_read_linux_mmap_v3 in case of TPACKET_V3. */ #endif +#ifdef HAVE_SYS_EVENTFD_H + int poll_breakloop_fd; /* fd to an eventfd to break from blocking operations */ +#endif + }; /* @@ -536,6 +545,11 @@ pcap_create_interface(const char *device, char *ebuf) handle->tstamp_precision_list[1] = PCAP_TSTAMP_PRECISION_NANO; #endif /* defined(SIOCGSTAMPNS) && defined(SO_TIMESTAMPNS) */ +#ifdef HAVE_SYS_EVENTFD_H + struct pcap_linux *handlep = handle->priv; + handlep->poll_breakloop_fd = eventfd(0, EFD_NONBLOCK); +#endif + return handle; } @@ -1369,6 +1383,10 @@ static void pcap_cleanup_linux( pcap_t *handle ) free(handlep->device); handlep->device = NULL; } + +#ifdef HAVE_SYS_EVENTFD_H + close(handlep->poll_breakloop_fd); +#endif pcap_cleanup_live_common(handle); } @@ -1460,6 +1478,17 @@ set_poll_timeout(struct pcap_linux *handlep) } } +#ifdef HAVE_SYS_EVENTFD_H +static void pcap_breakloop_linux(pcap_t *handle) +{ + pcap_breakloop_common(handle); + struct pcap_linux *handlep = handle->priv; + + uint64_t value = 1; + write(handlep->poll_breakloop_fd, &value, sizeof(value)); +} +#endif + /* * Get a handle for a live capture from the given device. You can * pass NULL as device to get all packages (without link level @@ -1515,6 +1544,9 @@ pcap_activate_linux(pcap_t *handle) handle->cleanup_op = pcap_cleanup_linux; handle->read_op = pcap_read_linux; handle->stats_op = pcap_stats_linux; +#ifdef HAVE_SYS_EVENTFD_H + handle->breakloop_op = pcap_breakloop_linux; +#endif /* * The "any" device is a special device which causes us not @@ -4919,11 +4951,16 @@ static int pcap_wait_for_frames_mmap(pcap_t *handle) { struct pcap_linux *handlep = handle->priv; char c; - struct pollfd pollinfo; int ret; - - pollinfo.fd = handle->fd; - pollinfo.events = POLLIN; +#ifdef HAVE_SYS_EVENTFD_H + struct pollfd pollinfo[2]; + pollinfo[1].fd = handlep->poll_breakloop_fd; + pollinfo[1].events = POLLIN; +#else + struct pollfd pollinfo[1]; +#endif + pollinfo[0].fd = handle->fd; + pollinfo[0].events = POLLIN; do { /* @@ -4934,26 +4971,31 @@ static int pcap_wait_for_frames_mmap(pcap_t *handle) * The timeout is 0 in non-blocking mode, so poll() * returns immediately. */ - ret = poll(&pollinfo, 1, handlep->poll_timeout); + +#ifdef HAVE_SYS_EVENTFD_H + ret = poll(pollinfo, 2, handlep->poll_timeout); +#else + ret = poll(pollinfo, 1, handlep->poll_timeout); +#endif if (ret < 0 && errno != EINTR) { pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "can't poll on packet socket"); return PCAP_ERROR; - } else if (ret > 0 && - (pollinfo.revents & (POLLHUP|POLLRDHUP|POLLERR|POLLNVAL))) { + } else if (ret > 0 && pollinfo[0].revents && + (pollinfo[0].revents & (POLLHUP|POLLRDHUP|POLLERR|POLLNVAL))) { /* * There's some indication other than * "you can read on this descriptor" on * the descriptor. */ - if (pollinfo.revents & (POLLHUP | POLLRDHUP)) { + if (pollinfo[0].revents & (POLLHUP | POLLRDHUP)) { pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Hangup on packet socket"); return PCAP_ERROR; } - if (pollinfo.revents & POLLERR) { + if (pollinfo[0].revents & POLLERR) { /* * A recv() will give us the actual error code. * @@ -4982,13 +5024,21 @@ static int pcap_wait_for_frames_mmap(pcap_t *handle) } return PCAP_ERROR; } - if (pollinfo.revents & POLLNVAL) { + if (pollinfo[0].revents & POLLNVAL) { pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Invalid polling request on packet socket"); return PCAP_ERROR; } } + +#ifdef HAVE_SYS_EVENTFD_H + if (pollinfo[1].revents & POLLIN) { + uint64_t value; + read(handlep->poll_breakloop_fd, &value, sizeof(value)); + } +#endif + /* check for break loop condition on interrupted syscall*/ if (handle->break_loop) { handle->break_loop = 0; |