Skip to content

Commit

Permalink
Add an API to set "immediate mode".
Browse files Browse the repository at this point in the history
In "immediate mode", packets are delivered as soon as they arrive.
  • Loading branch information
guyharris committed May 8, 2013
1 parent 98b7898 commit 48bc6c3
Show file tree
Hide file tree
Showing 15 changed files with 212 additions and 152 deletions.
1 change: 1 addition & 0 deletions Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ MAN3PCAP_NOEXPAND = \
pcap_open_live.3pcap \
pcap_set_buffer_size.3pcap \
pcap_set_datalink.3pcap \
pcap_set_immediate_mode.3pcap \
pcap_set_promisc.3pcap \
pcap_set_rfmon.3pcap \
pcap_set_snaplen.3pcap \
Expand Down
8 changes: 4 additions & 4 deletions dlpisubs.c
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ pcap_process_mactype(pcap_t *p, u_int mactype)
* Push and configure the buffer module. Returns -1 for error, otherwise 0.
*/
int
pcap_conf_bufmod(pcap_t *p, int snaplen, int timeout)
pcap_conf_bufmod(pcap_t *p, int snaplen)
{
int retv = 0;

Expand All @@ -293,11 +293,11 @@ pcap_conf_bufmod(pcap_t *p, int snaplen, int timeout)
}

/* Set up the bufmod timeout. */
if (timeout != 0) {
if (!p->opt.immediate && p->opt.timeout != 0) {
struct timeval to;

to.tv_sec = timeout / 1000;
to.tv_usec = (timeout * 1000) % 1000000;
to.tv_sec = p->opt.timeout / 1000;
to.tv_usec = (p->opt.timeout * 1000) % 1000000;
if (strioctl(p->fd, SBIOCSTIME, sizeof(to), (char *)&to) != 0) {
pcap_stream_err("SBIOCSTIME", errno, p->errbuf);
retv = -1;
Expand Down
2 changes: 1 addition & 1 deletion dlpisubs.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ int pcap_stats_dlpi(pcap_t *, struct pcap_stat *);
int pcap_process_pkts(pcap_t *, pcap_handler, u_char *, int, u_char *, int);
int pcap_process_mactype(pcap_t *, u_int);
#ifdef HAVE_SYS_BUFMOD_H
int pcap_conf_bufmod(pcap_t *, int, int);
int pcap_conf_bufmod(pcap_t *, int);
#endif
int pcap_alloc_databuf(pcap_t *);
int strioctl(int, int, int, char *);
Expand Down
168 changes: 58 additions & 110 deletions pcap-bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,31 +143,31 @@ struct pcap_bpf {
#endif

#ifdef HAVE_ZEROCOPY_BPF
/*
* Zero-copy read buffer -- for zero-copy BPF. 'buffer' above will
* alternative between these two actual mmap'd buffers as required.
* As there is a header on the front size of the mmap'd buffer, only
* some of the buffer is exposed to libpcap as a whole via bufsize;
* zbufsize is the true size. zbuffer tracks the current zbuf
* assocated with buffer so that it can be used to decide which the
* next buffer to read will be.
*/
u_char *zbuf1, *zbuf2, *zbuffer;
u_int zbufsize;
u_int zerocopy;
u_int interrupted;
struct timespec firstsel;
/*
* If there's currently a buffer being actively processed, then it is
* referenced here; 'buffer' is also pointed at it, but offset by the
* size of the header.
*/
struct bpf_zbuf_header *bzh;
/*
* Zero-copy read buffer -- for zero-copy BPF. 'buffer' above will
* alternative between these two actual mmap'd buffers as required.
* As there is a header on the front size of the mmap'd buffer, only
* some of the buffer is exposed to libpcap as a whole via bufsize;
* zbufsize is the true size. zbuffer tracks the current zbuf
* assocated with buffer so that it can be used to decide which the
* next buffer to read will be.
*/
u_char *zbuf1, *zbuf2, *zbuffer;
u_int zbufsize;
u_int zerocopy;
u_int interrupted;
struct timespec firstsel;
/*
* If there's currently a buffer being actively processed, then it is
* referenced here; 'buffer' is also pointed at it, but offset by the
* size of the header.
*/
struct bpf_zbuf_header *bzh;
int nonblock; /* true if in nonblocking mode */
#endif /* HAVE_ZEROCOPY_BPF */

char *device; /* device name */
int filtering_in_kernel; /* using kernel filter */
int timeout; /* timeout for buffering */
int must_do_on_close; /* stuff we must do when we close */
};

Expand Down Expand Up @@ -234,24 +234,17 @@ static int pcap_set_datalink_bpf(pcap_t *p, int dlt);

/*
* For zerocopy bpf, the setnonblock/getnonblock routines need to modify
* pb->timeout so we don't call select(2) if the pcap handle is in non-
* blocking mode. We preserve the timeout supplied by pcap_open functions
* to make sure it does not get clobbered if the pcap handle moves between
* blocking and non-blocking mode.
* pb->nonblock so we don't call select(2) if the pcap handle is in non-
* blocking mode.
*/
static int
pcap_getnonblock_bpf(pcap_t *p, char *errbuf)
{
#ifdef HAVE_ZEROCOPY_BPF
struct pcap_bpf *pb = p->private;

if (pb->zerocopy) {
/*
* Use a negative value for the timeout to represent that the
* pcap handle is in non-blocking mode.
*/
return (pb->timeout < 0);
}
if (pb->zerocopy)
return (pb->nonblock);
#endif
return (pcap_getnonblock_fd(p, errbuf));
}
Expand All @@ -263,30 +256,7 @@ pcap_setnonblock_bpf(pcap_t *p, int nonblock, char *errbuf)
struct pcap_bpf *pb = p->private;

if (pb->zerocopy) {
/*
* Map each value to their corresponding negation to
* preserve the timeout value provided with pcap_set_timeout.
* (from pcap-linux.c).
*/
if (nonblock) {
if (pb->timeout >= 0) {
/*
* Indicate that we're switching to
* non-blocking mode.
*/
pb->timeout = ~pb->timeout;
}
} else {
if (pb->timeout < 0) {
/*
* Timeout is negative, so we're currently
* in blocking mode; reverse the previous
* operation, to make the timeout non-negative
* again.
*/
pb->timeout = ~pb->timeout;
}
}
pb->nonblock = nonblock;
return (0);
}
#endif
Expand Down Expand Up @@ -368,11 +338,11 @@ pcap_next_zbuf(pcap_t *p, int *cc)
* our timeout is less then or equal to zero, handle it like a
* regular timeout.
*/
tmout = pb->timeout;
tmout = p->opt.timeout;
if (tmout)
(void) clock_gettime(CLOCK_MONOTONIC, &cur);
if (pb->interrupted && pb->timeout) {
expire = TSTOMILLI(&pb->firstsel) + pb->timeout;
if (pb->interrupted && p->opt.timeout) {
expire = TSTOMILLI(&pb->firstsel) + p->opt.timeout;
tmout = expire - TSTOMILLI(&cur);
#undef TSTOMILLI
if (tmout <= 0) {
Expand All @@ -393,17 +363,17 @@ pcap_next_zbuf(pcap_t *p, int *cc)
* the next timeout. Note that we only call select if the handle
* is in blocking mode.
*/
if (pb->timeout >= 0) {
if (!pb->nonblock) {
FD_ZERO(&r_set);
FD_SET(p->fd, &r_set);
if (tmout != 0) {
tv.tv_sec = tmout / 1000;
tv.tv_usec = (tmout * 1000) % 1000000;
}
r = select(p->fd + 1, &r_set, NULL, NULL,
pb->timeout != 0 ? &tv : NULL);
p->opt.timeout != 0 ? &tv : NULL);
if (r < 0 && errno == EINTR) {
if (!pb->interrupted && pb->timeout) {
if (!pb->interrupted && p->opt.timeout) {
pb->interrupted = 1;
pb->firstsel = cur;
}
Expand Down Expand Up @@ -2090,11 +2060,15 @@ pcap_activate_bpf(pcap_t *p)
}
#endif
/* set timeout */
pb->timeout = p->opt.timeout;
#ifdef HAVE_ZEROCOPY_BPF
if (pb->timeout != 0 && !pb->zerocopy) {
/*
* In zero-copy mode, we just use the timeout in select().
* XXX - what if we're in non-blocking mode and the *application*
* is using select() or poll() or kqueues or....?
*/
if (p->opt.timeout && !pb->zerocopy) {
#else
if (pb->timeout) {
if (p->opt.timeout) {
#endif
/*
* XXX - is this seconds/nanoseconds in AIX?
Expand All @@ -2118,8 +2092,8 @@ pcap_activate_bpf(pcap_t *p)
struct BPF_TIMEVAL bpf_to;

if (IOCPARM_LEN(BIOCSRTIMEOUT) != sizeof(struct timeval)) {
bpf_to.tv_sec = pb->timeout / 1000;
bpf_to.tv_usec = (pb->timeout * 1000) % 1000000;
bpf_to.tv_sec = p->opt.timeout / 1000;
bpf_to.tv_usec = (p->opt.timeout * 1000) % 1000000;
if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&bpf_to) < 0) {
snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"BIOCSRTIMEOUT: %s", pcap_strerror(errno));
Expand All @@ -2128,8 +2102,8 @@ pcap_activate_bpf(pcap_t *p)
}
} else {
#endif
to.tv_sec = pb->timeout / 1000;
to.tv_usec = (pb->timeout * 1000) % 1000000;
to.tv_sec = p->opt.timeout / 1000;
to.tv_usec = (p->opt.timeout * 1000) % 1000000;
if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&to) < 0) {
snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"BIOCSRTIMEOUT: %s", pcap_strerror(errno));
Expand All @@ -2141,7 +2115,6 @@ pcap_activate_bpf(pcap_t *p)
#endif
}

#ifdef _AIX
#ifdef BIOCIMMEDIATE
/*
* Darren Reed notes that
Expand All @@ -2155,49 +2128,24 @@ pcap_activate_bpf(pcap_t *p)
*
* so we turn BIOCIMMEDIATE mode on if this is AIX.
*
* We don't turn it on for other platforms, as that means we
* get woken up for every packet, which may not be what we want;
* in the Winter 1993 USENIX paper on BPF, they say:
*
* Since a process might want to look at every packet on a
* network and the time between packets can be only a few
* microseconds, it is not possible to do a read system call
* per packet and BPF must collect the data from several
* packets and return it as a unit when the monitoring
* application does a read.
*
* which I infer is the reason for the timeout - it means we
* wait that amount of time, in the hopes that more packets
* will arrive and we'll get them all with one read.
*
* Setting BIOCIMMEDIATE mode on FreeBSD (and probably other
* BSDs) causes the timeout to be ignored.
*
* On the other hand, some platforms (e.g., Linux) don't support
* timeouts, they just hand stuff to you as soon as it arrives;
* if that doesn't cause a problem on those platforms, it may
* be OK to have BIOCIMMEDIATE mode on BSD as well.
*
* (Note, though, that applications may depend on the read
* completing, even if no packets have arrived, when the timeout
* expires, e.g. GUI applications that have to check for input
* while waiting for packets to arrive; a non-zero timeout
* prevents "select()" from working right on FreeBSD and
* possibly other BSDs, as the timer doesn't start until a
* "read()" is done, so the timer isn't in effect if the
* application is blocked on a "select()", and the "select()"
* doesn't get woken up for a BPF device until the buffer
* fills up.)
* We don't, by default, turn it on for other platforms, as that
* means we get woken up for every packet, which may not be what
* we want; we only turn it on if requested.
*/
v = 1;
if (ioctl(p->fd, BIOCIMMEDIATE, &v) < 0) {
snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCIMMEDIATE: %s",
pcap_strerror(errno));
status = PCAP_ERROR;
goto bad;
#ifndef _AIX
if (p->opt.immediate) {
#endif /* _AIX */
v = 1;
if (ioctl(p->fd, BIOCIMMEDIATE, &v) < 0) {
snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"BIOCIMMEDIATE: %s", pcap_strerror(errno));
status = PCAP_ERROR;
goto bad;
}
#ifndef _AIX
}
#endif /* BIOCIMMEDIATE */
#endif /* _AIX */
#endif /* BIOCIMMEDIATE */

if (p->opt.promisc) {
/* set promiscuous mode, just warn if it fails */
Expand Down
17 changes: 12 additions & 5 deletions pcap-dag.c
Original file line number Diff line number Diff line change
Expand Up @@ -683,11 +683,18 @@ static int dag_activate(pcap_t* handle)
goto faildetach;
}

/* Amount of data to collect in Bytes before calling callbacks.
* Important for efficiency, but can introduce latency
* at low packet rates if to_ms not set!
*/
mindata = 65536;
if (handle->opt.immediate) {
/* Call callback immediately.
* XXX - is this the right way to handle this?
*/
mindata = 0;
} else {
/* Amount of data to collect in Bytes before calling callbacks.
* Important for efficiency, but can introduce latency
* at low packet rates if to_ms not set!
*/
mindata = 65536;
}

/* Obey opt.timeout (was to_ms) if supplied. This is a good idea!
* Recommend 10-100ms. Calls will time out even if no data arrived.
Expand Down
2 changes: 1 addition & 1 deletion pcap-dlpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -739,7 +739,7 @@ pcap_activate_dlpi(pcap_t *p)
#endif

/* Push and configure bufmod. */
if (pcap_conf_bufmod(p, ss, p->opt.timeout) != 0)
if (pcap_conf_bufmod(p, ss) != 0)
goto bad;
#endif

Expand Down
5 changes: 3 additions & 2 deletions pcap-int.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,12 @@ extern CRITICAL_SECTION g_PcapCompileCriticalSection;
#endif /* _MSC_VER */

struct pcap_opt {
char *source;
int timeout; /* timeout for buffering */
int buffer_size;
char *source;
int promisc;
int rfmon;
int rfmon; /* monitor mode */
int immediate; /* immediate mode - deliver packets as soon as they arrive */
int tstamp_type;
};

Expand Down
2 changes: 1 addition & 1 deletion pcap-libdlpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ pcap_activate_libdlpi(pcap_t *p)
p->fd = dlpi_fd(pd->dlpi_hd);

/* Push and configure bufmod. */
if (pcap_conf_bufmod(p, p->snapshot, p->opt.timeout) != 0)
if (pcap_conf_bufmod(p, p->snapshot) != 0)
goto bad;

/*
Expand Down
Loading

0 comments on commit 48bc6c3

Please sign in to comment.