Permalink
Browse files

Add an API to set "immediate mode".

In "immediate mode", packets are delivered as soon as they arrive.
  • Loading branch information...
guyharris committed May 8, 2013
1 parent 98b7898 commit 48bc6c35b191857f39b628bd586bd94a03fd13d7
Showing with 212 additions and 152 deletions.
  1. +1 −0 Makefile.in
  2. +4 −4 dlpisubs.c
  3. +1 −1 dlpisubs.h
  4. +58 −110 pcap-bpf.c
  5. +12 −5 pcap-dag.c
  6. +1 −1 pcap-dlpi.c
  7. +3 −2 pcap-int.h
  8. +1 −1 pcap-libdlpi.c
  9. +25 −10 pcap-nit.c
  10. +3 −1 pcap-pf.c
  11. +25 −10 pcap-snit.c
  12. +16 −4 pcap-win32.c
  13. +14 −3 pcap.c
  14. +1 −0 pcap/pcap.h
  15. +47 −0 pcap_set_immediate_mode.3pcap
View
@@ -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 \
View
@@ -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;
@@ -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;
View
@@ -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 *);
View
@@ -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 */
};
@@ -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));
}
@@ -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
@@ -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) {
@@ -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;
}
@@ -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?
@@ -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));
@@ -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));
@@ -2141,7 +2115,6 @@ pcap_activate_bpf(pcap_t *p)
#endif
}
#ifdef _AIX
#ifdef BIOCIMMEDIATE
/*
* Darren Reed notes that
@@ -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 */
View
@@ -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.
View
@@ -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
View
@@ -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;
};
View
@@ -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;
/*
Oops, something went wrong.

0 comments on commit 48bc6c3

Please sign in to comment.