X-Git-Url: https://git.tcpdump.org/libpcap/blobdiff_plain/e31793ccad591a4e39d99bb9da842ecc3ea583f3..09b51d326c38ea8e10ce4da09c09d50e08c5aeb8:/pcap-dag.c diff --git a/pcap-dag.c b/pcap-dag.c index 03946870..f261ead0 100644 --- a/pcap-dag.c +++ b/pcap-dag.c @@ -1,18 +1,10 @@ /* - * pcap-dag.c: Packet capture interface for Emulex EndaceDAG cards. - * - * The functionality of this code attempts to mimic that of pcap-linux as much - * as possible. This code is compiled in several different ways depending on - * whether DAG_ONLY and HAVE_DAG_API are defined. If HAVE_DAG_API is not - * defined it should not get compiled in, otherwise if DAG_ONLY is defined then - * the 'dag_' function calls are renamed to 'pcap_' equivalents. If DAG_ONLY - * is not defined then nothing is altered - the dag_ functions will be - * called as required from their pcap-linux/bpf equivalents. + * pcap-dag.c: Packet capture interface for Endace DAG cards. * * Authors: Richard Littin, Sean Irvine ({richard,sean}@reeltwo.com) * Modifications: Jesper Peterson * Koryn Grant - * Stephen Donnelly + * Stephen Donnelly */ #ifdef HAVE_CONFIG_H @@ -27,7 +19,6 @@ #include "pcap-int.h" -#include #include #include #include @@ -41,6 +32,7 @@ struct rtentry; /* declarations in */ #include "dagnew.h" #include "dagapi.h" #include "dagpci.h" +#include "dag_config_api.h" #include "pcap-dag.h" @@ -185,6 +177,11 @@ struct pcap_dag { int dag_timeout; /* timeout specified to pcap_open_live. * Same as in linux above, introduce * generally? */ + dag_card_ref_t dag_ref; /* DAG Configuration/Status API card reference */ + dag_component_t dag_root; /* DAG CSAPI Root component */ + attr_uuid_t drop_attr; /* DAG Stream Drop Attribute handle, if available */ + struct timeval required_select_timeout; + /* Timeout caller must use in event loops */ }; typedef struct pcap_dag_node { @@ -210,14 +207,13 @@ static unsigned char TempPkt[MAX_DAG_PACKET]; #define dag_size_t uint32_t #endif -static int dag_setfilter(pcap_t *p, struct bpf_program *fp); static int dag_stats(pcap_t *p, struct pcap_stat *ps); static int dag_set_datalink(pcap_t *p, int dlt); static int dag_get_datalink(pcap_t *p); static int dag_setnonblock(pcap_t *p, int nonblock); static void -delete_pcap_dag(pcap_t *p) +delete_pcap_dag(const pcap_t *p) { pcap_dag_node_t *curr = NULL, *prev = NULL; @@ -250,14 +246,20 @@ dag_platform_cleanup(pcap_t *p) if(dag_detach_stream(p->fd, pd->dag_stream) < 0) fprintf(stderr,"dag_detach_stream: %s\n", strerror(errno)); - if(p->fd != -1) { - if(dag_close(p->fd) < 0) - fprintf(stderr,"dag_close: %s\n", strerror(errno)); + if(pd->dag_ref != NULL) { + dag_config_dispose(pd->dag_ref); + /* + * Note: we don't need to call close(p->fd) or + * dag_close(p->fd), as dag_config_dispose(pd->dag_ref) + * does this. + * + * Set p->fd to -1 to make sure that's not done. + */ p->fd = -1; + pd->dag_ref = NULL; } delete_pcap_dag(p); pcap_cleanup_live_common(p); - /* Note: don't need to call close(p->fd) here as dag_close(p->fd) does this. */ } static void @@ -297,7 +299,7 @@ new_pcap_dag(pcap_t *p) } static unsigned int -dag_erf_ext_header_count(uint8_t * erf, size_t len) +dag_erf_ext_header_count(const uint8_t *erf, size_t len) { uint32_t hdr_num = 0; uint8_t hdr_type; @@ -331,7 +333,7 @@ dag_erf_ext_header_count(uint8_t * erf, size_t len) /* * 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. + * error occurred, or -2 if we were told to break out of the loop. */ static int dag_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) @@ -389,7 +391,12 @@ dag_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) } - /* Process the packets. */ + /* + * Process the packets. + * + * This assumes that a single buffer of packets will have + * <= INT_MAX packets, so the packet count doesn't overflow. + */ while (pd->dag_mem_top - pd->dag_mem_bottom >= dag_record_size) { unsigned short packet_len = 0; @@ -417,7 +424,8 @@ dag_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) rlen = ntohs(header->rlen); if (rlen < dag_record_size) { - strncpy(p->errbuf, "dag_read: record too small", PCAP_ERRBUF_SIZE); + pcap_strlcpy(p->errbuf, "dag_read: record too small", + PCAP_ERRBUF_SIZE); return -1; } pd->dag_mem_bottom += rlen; @@ -435,12 +443,8 @@ dag_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) break; default: - if (header->lctr) { - if (pd->stat.ps_drop > (UINT_MAX - ntohs(header->lctr))) { - pd->stat.ps_drop = UINT_MAX; - } else { - pd->stat.ps_drop += ntohs(header->lctr); - } + if ( (pd->drop_attr == kNullAttributeUuid) && (header->lctr) ) { + pd->stat.ps_drop += ntohs(header->lctr); } } @@ -664,7 +668,7 @@ dag_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) caplen = p->snapshot; /* Run the packet filter if there is one. */ - if ((p->fcode.bf_insns == NULL) || bpf_filter(p->fcode.bf_insns, dp, packet_len, caplen)) { + if ((p->fcode.bf_insns == NULL) || pcap_filter(p->fcode.bf_insns, dp, packet_len, caplen)) { /* convert between timestamp formats */ register unsigned long long ts; @@ -718,9 +722,9 @@ dag_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) } static int -dag_inject(pcap_t *p, const void *buf _U_, size_t size _U_) +dag_inject(pcap_t *p, const void *buf _U_, int size _U_) { - strlcpy(p->errbuf, "Sending packets isn't supported on DAG cards", + pcap_strlcpy(p->errbuf, "Sending packets isn't supported on DAG cards", PCAP_ERRBUF_SIZE); return (-1); } @@ -732,7 +736,7 @@ dag_inject(pcap_t *p, const void *buf _U_, size_t size _U_) * API polling parameters. * * snaplen is now also ignored, until we get per-stream slen support. Set - * slen with approprite DAG tool BEFORE pcap_activate(). + * slen with appropriate DAG tool BEFORE pcap_activate(). * * See also pcap(3). */ @@ -744,55 +748,115 @@ static int dag_activate(pcap_t* p) daginf_t* daginf; char * newDev = NULL; char * device = p->opt.device; + int ret; dag_size_t mindata; struct timeval maxwait; struct timeval poll; if (device == NULL) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "device is NULL: %s", pcap_strerror(errno)); - return -1; + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "device is NULL"); + return PCAP_ERROR; } /* Initialize some components of the pcap structure. */ newDev = (char *)malloc(strlen(device) + 16); if (newDev == NULL) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Can't allocate string for device name: %s", pcap_strerror(errno)); + ret = PCAP_ERROR; + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "Can't allocate string for device name"); goto fail; } /* Parse input name to get dag device and stream number if provided */ if (dag_parse_name(device, newDev, strlen(device) + 16, &pd->dag_stream) < 0) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "dag_parse_name: %s", pcap_strerror(errno)); + /* + * XXX - it'd be nice if this indicated what was wrong + * with the name. Does this reliably set errno? + * Should this return PCAP_ERROR_NO_SUCH_DEVICE in some + * cases? + */ + ret = PCAP_ERROR; + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "dag_parse_name"); goto fail; } device = newDev; if (pd->dag_stream%2) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "dag_parse_name: tx (even numbered) streams not supported for capture"); + ret = PCAP_ERROR; + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "dag_parse_name: tx (even numbered) streams not supported for capture"); goto fail; } /* setup device parameters */ - if((p->fd = dag_open((char *)device)) < 0) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "dag_open %s: %s", device, pcap_strerror(errno)); + if((pd->dag_ref = dag_config_init((char *)device)) == NULL) { + /* + * XXX - does this reliably set errno? + */ + if (errno == ENOENT) { + /* + * There's nothing more to say, so clear + * the error message. + */ + ret = PCAP_ERROR_NO_SUCH_DEVICE; + p->errbuf[0] = '\0'; + } else if (errno == EPERM || errno == EACCES) { + ret = PCAP_ERROR_PERM_DENIED; + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Attempt to open %s failed with %s - additional privileges may be required", + device, (errno == EPERM) ? "EPERM" : "EACCES"); + } else { + ret = PCAP_ERROR; + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "dag_config_init %s", device); + } goto fail; } + if((p->fd = dag_config_get_card_fd(pd->dag_ref)) < 0) { + /* + * XXX - does this reliably set errno? + */ + ret = PCAP_ERROR; + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "dag_config_get_card_fd %s", device); + goto failclose; + } + /* Open requested stream. Can fail if already locked or on error */ if (dag_attach_stream64(p->fd, pd->dag_stream, 0, 0) < 0) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "dag_attach_stream: %s", pcap_strerror(errno)); + ret = PCAP_ERROR; + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "dag_attach_stream"); goto failclose; } + /* Try to find Stream Drop attribute */ + pd->drop_attr = kNullAttributeUuid; + pd->dag_root = dag_config_get_root_component(pd->dag_ref); + if ( dag_component_get_subcomponent(pd->dag_root, kComponentStreamFeatures, 0) ) + { + pd->drop_attr = dag_config_get_indexed_attribute_uuid(pd->dag_ref, kUint32AttributeStreamDropCount, pd->dag_stream/2); + } + /* Set up default poll parameters for stream * Can be overridden by pcap_set_nonblock() */ if (dag_get_stream_poll64(p->fd, pd->dag_stream, &mindata, &maxwait, &poll) < 0) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "dag_get_stream_poll: %s", pcap_strerror(errno)); + ret = PCAP_ERROR; + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "dag_get_stream_poll"); goto faildetach; } + /* Use the poll time as the required select timeout for callers + * who are using select()/etc. in an event loop waiting for + * packets to arrive. + */ + pd->required_select_timeout = poll; + p->required_select_timeout = &pd->required_select_timeout; + /* * Turn a negative snapshot value (invalid), a snapshot value of * 0 (unspecified), or a value bigger than the normal maximum @@ -825,7 +889,9 @@ static int dag_activate(pcap_t* p) if (dag_set_stream_poll64(p->fd, pd->dag_stream, mindata, &maxwait, &poll) < 0) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "dag_set_stream_poll: %s", pcap_strerror(errno)); + ret = PCAP_ERROR; + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "dag_set_stream_poll"); goto faildetach; } @@ -846,7 +912,9 @@ static int dag_activate(pcap_t* p) #endif if(dag_start_stream(p->fd, pd->dag_stream) < 0) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "dag_start_stream %s: %s", device, pcap_strerror(errno)); + ret = PCAP_ERROR; + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "dag_start_stream %s", device); goto faildetach; } @@ -880,7 +948,8 @@ static int dag_activate(pcap_t* p) if ((n = atoi(s)) == 0 || n == 16 || n == 32) { pd->dag_fcs_bits = n; } else { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + ret = PCAP_ERROR; + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "pcap_activate %s: bad ERF_FCS_BITS value (%d) in environment", device, n); goto failstop; } @@ -890,7 +959,7 @@ static int dag_activate(pcap_t* p) * Did the user request that they not be stripped? */ if ((s = getenv("ERF_DONT_STRIP_FCS")) != NULL) { - /* Yes. Note the number of bytes that will be + /* Yes. Note the number of 16-bit words that will be supplied. */ p->linktype_ext = LT_FCS_DATALINK_EXT(pd->dag_fcs_bits/16); @@ -902,13 +971,17 @@ static int dag_activate(pcap_t* p) pd->dag_timeout = p->opt.timeout; p->linktype = -1; - if (dag_get_datalink(p) < 0) + if (dag_get_datalink(p) < 0) { + ret = PCAP_ERROR; goto failstop; + } p->bufsize = 0; if (new_pcap_dag(p) < 0) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "new_pcap_dag %s: %s", device, pcap_strerror(errno)); + ret = PCAP_ERROR; + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "new_pcap_dag %s", device); goto failstop; } @@ -923,7 +996,7 @@ static int dag_activate(pcap_t* p) p->read_op = dag_read; p->inject_op = dag_inject; - p->setfilter_op = dag_setfilter; + p->setfilter_op = install_bpf_program; p->setdirection_op = NULL; /* Not implemented.*/ p->set_datalink_op = dag_set_datalink; p->getnonblock_op = pcap_getnonblock_fd; @@ -945,8 +1018,15 @@ faildetach: fprintf(stderr,"dag_detach_stream: %s\n", strerror(errno)); failclose: - if (dag_close(p->fd) < 0) - fprintf(stderr,"dag_close: %s\n", strerror(errno)); + dag_config_dispose(pd->dag_ref); + /* + * Note: we don't need to call close(p->fd) or dag_close(p->fd), + * as dag_config_dispose(pd->dag_ref) does this. + * + * Set p->fd to -1 to make sure that's not done. + */ + p->fd = -1; + pd->dag_ref = NULL; delete_pcap_dag(p); fail: @@ -955,7 +1035,7 @@ fail: free((char *)newDev); } - return PCAP_ERROR; + return ret; } pcap_t *dag_create(const char *device, char *ebuf, int *is_ours) @@ -1005,7 +1085,7 @@ pcap_t *dag_create(const char *device, char *ebuf, int *is_ours) /* OK, it's probably ours. */ *is_ours = 1; - p = pcap_create_common(ebuf, sizeof (struct pcap_dag)); + p = PCAP_CREATE_COMMON(ebuf, struct pcap_dag); if (p == NULL) return NULL; @@ -1018,28 +1098,44 @@ pcap_t *dag_create(const char *device, char *ebuf, int *is_ours) * XXX Our native precision is 2^-32s, but libpcap doesn't support * power of two precisions yet. We can convert to either MICRO or NANO. */ - p->tstamp_precision_count = 2; p->tstamp_precision_list = malloc(2 * sizeof(u_int)); if (p->tstamp_precision_list == NULL) { - pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", - pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); pcap_close(p); return NULL; } p->tstamp_precision_list[0] = PCAP_TSTAMP_PRECISION_MICRO; p->tstamp_precision_list[1] = PCAP_TSTAMP_PRECISION_NANO; + p->tstamp_precision_count = 2; return p; } static int dag_stats(pcap_t *p, struct pcap_stat *ps) { struct pcap_dag *pd = p->priv; + uint32_t stream_drop; + dag_err_t dag_error; - /* This needs to be filled out correctly. Hopefully a dagapi call will - provide all necessary information. - */ - /*pd->stat.ps_recv = 0;*/ - /*pd->stat.ps_drop = 0;*/ + /* + * Packet records received (ps_recv) are counted in dag_read(). + * Packet records dropped (ps_drop) are read from Stream Drop attribute if present, + * otherwise integrate the ERF Header lctr counts (if available) in dag_read(). + * We are reporting that no records are dropped by the card/driver (ps_ifdrop). + */ + + if(pd->drop_attr != kNullAttributeUuid) { + /* Note this counter is cleared at start of capture and will wrap at UINT_MAX. + * The application is responsible for polling ps_drop frequently enough + * to detect each wrap and integrate total drop with a wider counter */ + if ((dag_error = dag_config_get_uint32_attribute_ex(pd->dag_ref, pd->drop_attr, &stream_drop)) == kDagErrNone) { + pd->stat.ps_drop = stream_drop; + } else { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "reading stream drop attribute: %s", + dag_config_strerror(dag_error)); + return -1; + } + } *ps = pd->stat; @@ -1053,7 +1149,6 @@ int dag_findalldevs(pcap_if_list_t *devlistp, char *errbuf) { char name[12]; /* XXX - pick a size */ - int ret = 0; int c; char dagname[DAGNAME_BUFSIZE]; int dagstream; @@ -1064,36 +1159,45 @@ dag_findalldevs(pcap_if_list_t *devlistp, char *errbuf) /* Try all the DAGs 0-DAG_MAX_BOARDS */ for (c = 0; c < DAG_MAX_BOARDS; c++) { - pcap_snprintf(name, 12, "dag%d", c); + snprintf(name, 12, "dag%d", c); if (-1 == dag_parse_name(name, dagname, DAGNAME_BUFSIZE, &dagstream)) { - (void) pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + (void) snprintf(errbuf, PCAP_ERRBUF_SIZE, "dag: device name %s can't be parsed", name); - return -1; + return (-1); } if ( (dagfd = dag_open(dagname)) >= 0 ) { description = NULL; if ((inf = dag_pciinfo(dagfd))) description = dag_device_name(inf->device_code, 1); + /* + * XXX - is there a way to determine whether + * the card is plugged into a network or not? + * If so, we should check that and set + * PCAP_IF_CONNECTION_STATUS_CONNECTED or + * PCAP_IF_CONNECTION_STATUS_DISCONNECTED. + * + * Also, are there notions of "up" and "running"? + */ if (add_dev(devlistp, name, 0, description, errbuf) == NULL) { /* * Failure. */ - ret = -1; + return (-1); } rxstreams = dag_rx_get_stream_count(dagfd); for(stream=0;streamerrbuf, "setfilter: No filter specified", - sizeof(p->errbuf)); - return -1; } - - /* Make our private copy of the filter */ - - if (install_bpf_program(p, fp) < 0) - return -1; - return (0); } @@ -1158,7 +1238,8 @@ dag_setnonblock(pcap_t *p, int nonblock) if (dag_get_stream_poll64(p->fd, pd->dag_stream, &mindata, &maxwait, &poll) < 0) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "dag_get_stream_poll: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "dag_get_stream_poll"); return -1; } @@ -1173,7 +1254,8 @@ dag_setnonblock(pcap_t *p, int nonblock) if (dag_set_stream_poll64(p->fd, pd->dag_stream, mindata, &maxwait, &poll) < 0) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "dag_set_stream_poll: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "dag_set_stream_poll"); return -1; } @@ -1195,7 +1277,8 @@ dag_get_datalink(pcap_t *p) memset(types, 0, 255); if (p->dlt_list == NULL && (p->dlt_list = malloc(255*sizeof(*(p->dlt_list)))) == NULL) { - (void)pcap_snprintf(p->errbuf, sizeof(p->errbuf), "malloc: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, sizeof(p->errbuf), + errno, "malloc"); return (-1); } @@ -1204,7 +1287,8 @@ dag_get_datalink(pcap_t *p) #ifdef HAVE_DAG_GET_STREAM_ERF_TYPES /* Get list of possible ERF types for this card */ if (dag_get_stream_erf_types(p->fd, pd->dag_stream, types, 255) < 0) { - pcap_snprintf(p->errbuf, sizeof(p->errbuf), "dag_get_stream_erf_types: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, sizeof(p->errbuf), + errno, "dag_get_stream_erf_types"); return (-1); } @@ -1213,7 +1297,8 @@ dag_get_datalink(pcap_t *p) #elif defined HAVE_DAG_GET_ERF_TYPES /* Get list of possible ERF types for this card */ if (dag_get_erf_types(p->fd, types, 255) < 0) { - pcap_snprintf(p->errbuf, sizeof(p->errbuf), "dag_get_erf_types: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, sizeof(p->errbuf), + errno, "dag_get_erf_types"); return (-1); } @@ -1296,7 +1381,7 @@ dag_get_datalink(pcap_t *p) if(!p->linktype) p->linktype = DLT_RAW; break; - + case ERF_TYPE_IPV6: if (p->dlt_list != NULL) { p->dlt_list[dlt_index++] = DLT_RAW; @@ -1355,17 +1440,17 @@ pcap_platform_finddevs(pcap_if_list_t *devlistp _U_, char *errbuf) pcap_t * pcap_create_interface(const char *device, char *errbuf) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "This version of libpcap only supports DAG cards"); return NULL; } /* - * Platform-specific information. + * Libpcap version string. */ const char * -pcap_platform_lib_version(void) +pcap_lib_version(void) { - return ("DAG-only"); + return (PCAP_VERSION_STRING " (DAG-only)"); } #endif