Hi all,
This patch contains a number of improvements for libpcap's support for
Endace DAG cards:
* Much improved performance. Previously the read operation would only ever
retrieve and process 1 packet, even if asked for a bufferful, which added a
large amount of overhead.
* Support for nonblocking operation.
* Miscellaneous bug/portability fixes.
Cheers,
Koryn
--
Dr Koryn Grant phone +64 7 8346729
Senior Software Engineer mobile +64 21 488386
Endace Technology Ltd email [EMAIL PROTECTED]
Hamilton, New Zealand web http://www.endace.com/
Only in libpcap-mod: .#pcap-dag.c.1.10
Only in libpcap-mod: .#pcap.c.1.63
Only in libpcap-mod: .#pcap.c.1.64
Common subdirectories: libpcap/CVS and libpcap-mod/CVS
Common subdirectories: libpcap/SUNOS4 and libpcap-mod/SUNOS4
Common subdirectories: libpcap/Win32 and libpcap-mod/Win32
Common subdirectories: libpcap/bpf and libpcap-mod/bpf
Common subdirectories: libpcap/lbl and libpcap-mod/lbl
Only in libpcap-mod: net
Common subdirectories: libpcap/packaging and libpcap-mod/packaging
diff -ubB libpcap/pcap-dag.c libpcap-mod/pcap-dag.c
--- libpcap/pcap-dag.c Wed Nov 19 11:55:16 2003
+++ libpcap-mod/pcap-dag.c Wed Nov 19 09:53:38 2003
@@ -15,6 +15,16 @@
* 2003 May - Jesper Peterson <[EMAIL PROTECTED]>
* Code shuffled around to suit fad-xxx.c structure
* Added atexit() handler to stop DAG if application is too lazy
+ * 2003 September - Koryn Grant <[EMAIL PROTECTED]>
+ * Added support for nonblocking operation.
+ * Added support for processing more than a single packet in
pcap_dispatch().
+ * Fixed bug in loss counter code.
+ * Improved portability of loss counter code (e.g. use UINT_MAX instead
of 0xffff).
+ * Removed unused local variables.
+ * Added required headers (ctype.h, limits.h, unistd.h, netinet/in.h).
+ * 2003 October - Koryn Grant <[EMAIL PROTECTED]>
+ * Changed semantics to match those of standard pcap on linux.
+ * - packets rejected by the filter are not counted.
*/
#ifndef lint
@@ -34,8 +44,12 @@
#include "pcap-int.h"
+#include <ctype.h>
+#include <netinet/in.h>
#include <sys/mman.h>
#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
struct mbuf; /* Squelch compiler warnings on some platforms for */
struct rtentry; /* declarations in <net/if.h> */
@@ -44,28 +58,10 @@
#include <dagnew.h>
#include <dagapi.h>
-#ifndef min
-#define min(a, b) ((a) > (b) ? (b) : (a))
-#endif
-
#define MIN_DAG_SNAPLEN 12
#define MAX_DAG_SNAPLEN 2040
#define ATM_SNAPLEN 48
-/* Size of ATM payload */
-#define ATM_WLEN(h) ATM_SNAPLEN
-#define ATM_SLEN(h) ATM_SNAPLEN
-
-/* Size Ethernet payload */
-#define ETHERNET_WLEN(h, b) (ntohs((h)->wlen) - ((b) >> 3))
-#define ETHERNET_SLEN(h, b) min(ETHERNET_WLEN(h, b), \
- ntohs((h)->rlen) - dag_record_size - 2)
-
-/* Size of HDLC payload */
-#define HDLC_WLEN(h, b) (ntohs((h)->wlen) - ((b) >> 3))
-#define HDLC_SLEN(h, b) min(HDLC_WLEN(h, b), \
- ntohs((h)->rlen) - dag_record_size)
-
typedef struct pcap_dag_node {
struct pcap_dag_node *next;
pcap_t *p;
@@ -74,7 +70,7 @@
static pcap_dag_node_t *pcap_dags = NULL;
static int atexit_handler_installed = 0;
-static unsigned short endian_test_word = 0x0100;
+static const unsigned short endian_test_word = 0x0100;
#define IS_BIGENDIAN() (*((unsigned char *)&endian_test_word))
@@ -82,19 +78,18 @@
* Swap byte ordering of unsigned long long timestamp on a big endian
* machine.
*/
-#define SWAP_TS(ull) \
- (IS_BIGENDIAN() ? ((ull & 0xff00000000000000LL) >> 56) | \
+#define SWAP_TS(ull) ((ull & 0xff00000000000000LL) >> 56) | \
((ull & 0x00ff000000000000LL) >> 40) | \
((ull & 0x0000ff0000000000LL) >> 24) | \
((ull & 0x000000ff00000000LL) >> 8) | \
((ull & 0x00000000ff000000LL) << 8) | \
((ull & 0x0000000000ff0000LL) << 24) | \
((ull & 0x000000000000ff00LL) << 40) | \
- ((ull & 0x00000000000000ffLL) << 56) \
- : ull)
+ ((ull & 0x00000000000000ffLL) << 56)
+
#ifdef DAG_ONLY
-/* This code is reguired when compiling for a DAG device only. */
+/* This code is required when compiling for a DAG device only. */
#include "pcap-dag.h"
/* Replace dag function names with pcap equivalent. */
@@ -150,6 +145,7 @@
}
#endif
delete_pcap_dag(p);
+ /* Note: don't need to call close(p->fd) here as dag_close(p->fd) does this. */
}
static void atexit_handler(void) {
@@ -184,122 +180,155 @@
}
/*
- * Get pointer to the ERF header for the next packet in the input
- * stream. This function blocks until a packet becomes available.
- */
-static dag_record_t *get_next_dag_header(pcap_t *p) {
- register dag_record_t *record;
- int rlen;
-
- /*
- * The buffer is guaranteed to only contain complete records so any
- * time top and bottom differ there will be at least one record available.
- * Here we test the difference is at least the size of a record header
- * using the poorly named constant 'dag_record_size'.
+ * Read at most max_packets from the capture stream and call the callback
+ * for each of them. Returns the number of packets handled or -1 if an
+ * error occured.
*/
- while ((p->md.dag_mem_top - p->md.dag_mem_bottom) < dag_record_size) {
- p->md.dag_mem_top = dag_offset(p->fd, &(p->md.dag_mem_bottom), 0);
- }
+static int dag_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) {
+ unsigned int processed = 0;
+ int flags = p->md.dag_offset_flags;
+ unsigned int nonblocking = flags & DAGF_NONBLOCK;
- record = (dag_record_t *)(p->md.dag_mem_base + p->md.dag_mem_bottom);
+ for (;;)
+ {
+ /* Get the next bufferful of packets (if necessary). */
+ while (p->md.dag_mem_top - p->md.dag_mem_bottom < dag_record_size) {
- p->md.dag_mem_bottom += ntohs(record->rlen);
+ p->md.dag_mem_top = dag_offset(p->fd, &(p->md.dag_mem_bottom),
flags);
+ if ((p->md.dag_mem_top - p->md.dag_mem_bottom <
dag_record_size) && nonblocking)
+ {
+ /* Pcap is configured to process only available
packets, and there aren't any. */
+ return 0;
+ }
+ }
- return record;
-}
+ /* Process the packets. */
+ while (p->md.dag_mem_top - p->md.dag_mem_bottom >= dag_record_size) {
-/*
- * Read at most max_packets from the capture stream and call the callback
- * for each of them. Returns the number of packets handled, -1 if an
- * error occured, or -2 if we were told to break out of the loop.
- * A blocking
- */
-static int dag_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) {
- u_char *dp = NULL;
- int packet_len = 0, caplen = 0;
+ unsigned short packet_len = 0;
+ int caplen = 0;
struct pcap_pkthdr pcap_header;
- dag_record_t *header;
- register unsigned long long ts;
+ dag_record_t *header = (dag_record_t *)(p->md.dag_mem_base +
p->md.dag_mem_bottom);
+ u_char *dp = ((u_char *)header) + dag_record_size;
+ unsigned short rlen;
- /*
- * Has "pcap_breakloop()" been called?
- */
- if (p->break_loop) {
- /*
- * Yes - clear the flag that indicates that it has, and return -2
- * to indicate that we were told to break out of the loop.
- */
- p->break_loop = 0;
- return -2;
+ if (IS_BIGENDIAN())
+ {
+ rlen = header->rlen;
}
-
- /* Receive a single packet from the kernel */
- header = get_next_dag_header(p);
- dp = ((u_char *)header) + dag_record_size;
+ else
+ {
+ rlen = ntohs(header->rlen);
+ }
+ p->md.dag_mem_bottom += rlen;
switch(header->type) {
case TYPE_ATM:
- packet_len = ATM_WLEN(header);
- caplen = ATM_SLEN(header);
+ packet_len = ATM_SNAPLEN;
+ caplen = ATM_SNAPLEN;
dp += 4;
break;
+
case TYPE_ETH:
- packet_len = ETHERNET_WLEN(header, p->md.dag_fcs_bits);
- caplen = ETHERNET_SLEN(header, p->md.dag_fcs_bits);
+ if (IS_BIGENDIAN())
+ {
+ packet_len = header->wlen;
+ }
+ else
+ {
+ packet_len = ntohs(header->wlen);
+ }
+ packet_len -= (p->md.dag_fcs_bits >> 3);
+ caplen = rlen - dag_record_size - 2;
+ if (caplen > packet_len)
+ {
+ caplen = packet_len;
+ }
dp += 2;
break;
+
case TYPE_HDLC_POS:
- packet_len = HDLC_WLEN(header, p->md.dag_fcs_bits);
- caplen = HDLC_SLEN(header, p->md.dag_fcs_bits);
+ if (IS_BIGENDIAN())
+ {
+ packet_len = header->wlen;
+ }
+ else
+ {
+ packet_len = ntohs(header->wlen);
+ }
+ packet_len -= (p->md.dag_fcs_bits >> 3);
+ caplen = rlen - dag_record_size;
+ if (caplen > packet_len)
+ {
+ caplen = packet_len;
+ }
break;
}
if (caplen > p->snapshot)
caplen = p->snapshot;
- /* Count lost packets */
- if (header->lctr > 0 && (p->md.stat.ps_drop+1) != 0) {
- if (header->lctr == 0xffff ||
- (p->md.stat.ps_drop + header->lctr) < p->md.stat.ps_drop) {
- p->md.stat.ps_drop == ~0;
+ /* Count lost packets. */
+ if (header->lctr) {
+ if (p->md.stat.ps_drop > (UINT_MAX - header->lctr)) {
+ p->md.stat.ps_drop = UINT_MAX;
} else {
p->md.stat.ps_drop += header->lctr;
}
}
- /* Run the packet filter if not using kernel filter */
- if (p->fcode.bf_insns) {
- if (bpf_filter(p->fcode.bf_insns, dp, packet_len, caplen) == 0) {
- /* rejected by filter */
- return 0;
- }
- }
+ /* Run the packet filter if there is one. */
+ if ((p->fcode.bf_insns == NULL) ||
bpf_filter(p->fcode.bf_insns, dp, packet_len, caplen)) {
/* convert between timestamp formats */
+ register unsigned long long ts;
+
+ if (IS_BIGENDIAN())
+ {
ts = SWAP_TS(header->ts);
+ }
+ else
+ {
+ ts = header->ts;
+ }
+
pcap_header.ts.tv_sec = ts >> 32;
- ts = ((ts & 0xffffffffULL) * 1000 * 1000);
- ts += (ts & 0x80000000ULL) << 1; /* rounding */
+ ts = (ts & 0xffffffffULL) * 1000000;
+ ts += 0x80000000; /* rounding */
pcap_header.ts.tv_usec = ts >> 32;
if (pcap_header.ts.tv_usec >= 1000000) {
pcap_header.ts.tv_usec -= 1000000;
- pcap_header.ts.tv_sec += 1;
+ pcap_header.ts.tv_sec++;
}
/* Fill in our own header data */
pcap_header.caplen = caplen;
pcap_header.len = packet_len;
- /*
- * Count the packet.
- */
+ /* Count the packet. */
p->md.stat.ps_recv++;
/* Call the user supplied callback function */
callback(user, &pcap_header, dp);
- return 1;
+ /* Only count packets that pass the filter, for
consistency with standard Linux behaviour. */
+ processed++;
+ if (processed == cnt)
+ {
+ /* Reached the user-specified limit. */
+ return cnt;
+ }
+ }
+ }
+
+ if (nonblocking || processed)
+ {
+ return processed;
+ }
+ }
+
+ return processed;
}
/*
@@ -338,19 +367,25 @@
strcat(newDev, "/dev/");
strcat(newDev,device);
device = newDev;
+ } else {
+ device = strdup(device);
+ }
+
+ if (device == NULL) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE, "str_dup: %s\n", pcap_strerror(errno));
+ goto fail;
}
/* setup device parameters */
if((handle->fd = dag_open((char *)device)) < 0) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "dag_open %s: %s", device, pcap_strerror(errno));
- return NULL;
+ goto fail;
}
- /* set the card snap length as specified by the specified snaplen parameter */
+ /* set the card snap length to the specified snaplen parameter */
if (snaplen == 0 || snaplen > MAX_DAG_SNAPLEN) {
snaplen = MAX_DAG_SNAPLEN;
- } else
- if (snaplen < MIN_DAG_SNAPLEN) {
+ } else if (snaplen < MIN_DAG_SNAPLEN) {
snaplen = MIN_DAG_SNAPLEN;
}
/* snap len has to be a multiple of 4 */
@@ -359,17 +394,17 @@
fprintf(stderr, "Configuring DAG with '%s'.\n", conf);
if(dag_configure(handle->fd, conf) < 0) {
snprintf(ebuf, PCAP_ERRBUF_SIZE,"dag_configure %s: %s\n", device,
pcap_strerror(errno));
- return NULL;
+ goto fail;
}
if((handle->md.dag_mem_base = dag_mmap(handle->fd)) == MAP_FAILED) {
snprintf(ebuf, PCAP_ERRBUF_SIZE,"dag_mmap %s: %s\n", device,
pcap_strerror(errno));
- return NULL;
+ goto fail;
}
if(dag_start(handle->fd) < 0) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "dag_start %s: %s\n", device,
pcap_strerror(errno));
- return NULL;
+ goto fail;
}
/*
@@ -388,37 +423,32 @@
} else {
snprintf(ebuf, PCAP_ERRBUF_SIZE,
"pcap_open_live %s: bad ERF_FCS_BITS value (%d) in environment\n", device, n);
- return NULL;
+ goto fail;
}
}
handle->snapshot = snaplen;
/*handle->md.timeout = to_ms; */
-#ifdef linux
- if (device) {
- handle->md.device = strdup(device);
- }
-
- if (handle->md.device == NULL) {
- snprintf(ebuf, PCAP_ERRBUF_SIZE, "str_dup %s: %s\n", device,
pcap_strerror(errno));
- free(handle);
- return NULL;
- }
-#endif
-
if ((handle->linktype = dag_get_datalink(handle)) < 0) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "dag_get_linktype %s: unknown linktype\n",
device);
- return NULL;
+ goto fail;
}
handle->bufsize = 0;
if (new_pcap_dag(handle) < 0) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "new_pcap_dag %s: %s\n", device,
pcap_strerror(errno));
- return NULL;
+ goto fail;
}
+#ifdef linux
+ handle->md.device = (char *)device;
+#else
+ free((char *)device);
+ device = NULL;
+#endif
+
handle->read_op = dag_read;
handle->setfilter_op = dag_setfilter;
handle->set_datalink_op = dag_set_datalink;
@@ -426,6 +456,16 @@
handle->close_op = dag_platform_close;
return handle;
+
+fail:
+ if (device != NULL) {
+ free((char *)device);
+ }
+ if (handle != NULL) {
+ free(handle);
+ }
+
+ return NULL;
}
static int dag_stats(pcap_t *p, struct pcap_stat *ps) {
@@ -458,15 +498,14 @@
int linenum;
unsigned char *p;
char name[512]; /* XXX - pick a size */
- char *q, *saveq;
- struct ifreq ifrflags;
+ char *q;
int ret = 0;
/* Quick exit if /proc/dag not readable */
proc_dag_f = fopen("/proc/dag", "r");
if (proc_dag_f == NULL)
{
- int i, fd;
+ int i;
char dev[16] = "dagx";
for (i = '0'; ret == 0 && i <= '9'; i++) {
@@ -538,7 +577,7 @@
}
/*
- * Installs the gven bpf filter program in the given pcap structure. There is
+ * Installs the given bpf filter program in the given pcap structure. There is
* no attempt to store the filter in kernel memory as that is not supported
* with DAG cards.
*/
diff -ubB libpcap/pcap-int.h libpcap-mod/pcap-int.h
--- libpcap/pcap-int.h Tue Nov 4 20:05:34 2003
+++ libpcap-mod/pcap-int.h Fri Nov 7 12:29:45 2003
@@ -89,6 +89,7 @@
u_int dag_mem_bottom; /* DAG card current memory bottom pointer */
u_int dag_mem_top; /* DAG card current memory top pointer */
int dag_fcs_bits; /* Number of checksum bits from link layer */
+ int dag_offset_flags; /* Flags to pass to dag_offset(). */
#endif
};
diff -ubB libpcap/pcap.c libpcap-mod/pcap.c
--- libpcap/pcap.c Wed Nov 19 11:55:19 2003
+++ libpcap-mod/pcap.c Wed Nov 19 11:58:24 2003
@@ -61,6 +61,11 @@
#include "pcap-int.h"
+#ifdef HAVE_DAG_API
+#include <dagnew.h>
+#include <dagapi.h>
+#endif
+
int
pcap_dispatch(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
{
@@ -558,6 +564,15 @@
*/
return (0);
}
+
+#if HAVE_DAG_API
+ if (nonblock) {
+ p->md.dag_offset_flags |= DAGF_NONBLOCK;
+ } else {
+ p->md.dag_offset_flags &= ~DAGF_NONBLOCK;
+ }
+#endif /* HAVE_DAG_API */
+
#ifndef WIN32
fdflags = fcntl(p->fd, F_GETFL, 0);
if (fdflags == -1) {