aboutsummaryrefslogtreecommitdiff
path: root/pcap-dag.c
diff options
context:
space:
mode:
authorguy <guy>2003-11-20 01:21:25 +0000
committerguy <guy>2003-11-20 01:21:25 +0000
commit028bb1b31de62247b5c23a2ce76501c985ea1f14 (patch)
treea4bfb151952749fd1621bd6181ad560234b6c6ea /pcap-dag.c
parentd7bc98d08b428b7d6a100bc02025e3f9074bc804 (diff)
From Koryn Grant <koryn@endace.com> - DAG support enhancements and fixes:
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). Changed semantics to match those of standard pcap on linux. - packets rejected by the filter are not counted.
Diffstat (limited to 'pcap-dag.c')
-rw-r--r--pcap-dag.c375
1 files changed, 220 insertions, 155 deletions
diff --git a/pcap-dag.c b/pcap-dag.c
index 9cecd55e..fc940dab 100644
--- a/pcap-dag.c
+++ b/pcap-dag.c
@@ -15,11 +15,21 @@
* 2003 May - Jesper Peterson <support@endace.com>
* Code shuffled around to suit fad-xxx.c structure
* Added atexit() handler to stop DAG if application is too lazy
+ * 2003 September - Koryn Grant <koryn@endace.com>
+ * 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 <koryn@endace.com.>
+ * Changed semantics to match those of standard pcap on linux.
+ * - packets rejected by the filter are not counted.
*/
#ifndef lint
static const char rcsid[] _U_ =
- "@(#) $Header: /tcpdump/master/libpcap/pcap-dag.c,v 1.11 2003-11-15 23:24:02 guy Exp $ (LBL)";
+ "@(#) $Header: /tcpdump/master/libpcap/pcap-dag.c,v 1.12 2003-11-20 01:21:26 guy Exp $ (LBL)";
#endif
#ifdef HAVE_CONFIG_H
@@ -34,8 +44,12 @@ static const char rcsid[] _U_ =
#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 @@ struct rtentry; /* declarations in <net/if.h> */
#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 @@ typedef struct pcap_dag_node {
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 @@ static unsigned short endian_test_word = 0x0100;
* 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 @@ static void dag_platform_close(pcap_t *p) {
}
#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,181 @@ static int new_pcap_dag(pcap_t *p) {
}
/*
- * 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'.
- */
- 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);
- }
-
- record = (dag_record_t *)(p->md.dag_mem_base + p->md.dag_mem_bottom);
-
- p->md.dag_mem_bottom += ntohs(record->rlen);
-
- return record;
-}
-
-/*
* 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;
- struct pcap_pkthdr pcap_header;
-
- dag_record_t *header;
- register unsigned long long ts;
+ unsigned int processed = 0;
+ int flags = p->md.dag_offset_flags;
+ unsigned int nonblocking = flags & DAGF_NONBLOCK;
+
+ for (;;)
+ {
+ /* Get the next bufferful of packets (if necessary). */
+ while (p->md.dag_mem_top - p->md.dag_mem_bottom < dag_record_size) {
- /*
- * 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;
- }
+ /*
+ * 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;
+ }
+
+ 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;
+ }
+ }
+
+ /* Process the packets. */
+ while (p->md.dag_mem_top - p->md.dag_mem_bottom >= dag_record_size) {
- /* Receive a single packet from the kernel */
- header = get_next_dag_header(p);
- dp = ((u_char *)header) + dag_record_size;
+ unsigned short packet_len = 0;
+ int caplen = 0;
+ struct pcap_pkthdr pcap_header;
- switch(header->type) {
- case TYPE_ATM:
- packet_len = ATM_WLEN(header);
- caplen = ATM_SLEN(header);
- 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);
- 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);
- break;
- }
+ 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;
- 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;
- } 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;
- }
- }
-
- /* convert between timestamp formats */
- ts = SWAP_TS(header->ts);
- pcap_header.ts.tv_sec = ts >> 32;
- ts = ((ts & 0xffffffffULL) * 1000 * 1000);
- ts += (ts & 0x80000000ULL) << 1; /* 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;
- }
-
- /* Fill in our own header data */
- pcap_header.caplen = caplen;
- pcap_header.len = packet_len;
+ /*
+ * 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;
+ }
+ else
+ {
+ rlen = ntohs(header->rlen);
+ }
+ p->md.dag_mem_bottom += rlen;
+
+ switch(header->type) {
+ case TYPE_ATM:
+ packet_len = ATM_SNAPLEN;
+ caplen = ATM_SNAPLEN;
+ dp += 4;
+ break;
+
+ case TYPE_ETH:
+ 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:
+ 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) {
+ 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 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) * 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++;
+ }
+
+ /* Fill in our own header data */
+ pcap_header.caplen = caplen;
+ pcap_header.len = packet_len;
- /*
- * Count the packet.
- */
- p->md.stat.ps_recv++;
+ /* Count the packet. */
+ p->md.stat.ps_recv++;
+
+ /* Call the user supplied callback function */
+ callback(user, &pcap_header, dp);
- /* Call the user supplied callback function */
- callback(user, &pcap_header, dp);
+ /* 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 1;
+ return processed;
}
/*
@@ -338,19 +393,25 @@ pcap_t *dag_open_live(const char *device, int snaplen, int promisc, int to_ms, c
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 +420,17 @@ pcap_t *dag_open_live(const char *device, int snaplen, int promisc, int to_ms, c
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 +449,32 @@ pcap_t *dag_open_live(const char *device, int snaplen, int promisc, int to_ms, c
} 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 +482,16 @@ pcap_t *dag_open_live(const char *device, int snaplen, int promisc, int to_ms, c
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 +524,14 @@ dag_platform_finddevs(pcap_if_t **devlistp, char *errbuf)
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 +603,7 @@ dag_platform_finddevs(pcap_if_t **devlistp, char *errbuf)
}
/*
- * 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.
*/