X-Git-Url: https://git.tcpdump.org/libpcap/blobdiff_plain/f4c3cf29cbb291901258c2f05aba77adab40a686..a973128a85d7dd75c7ea6fdcf746fc143a987d03:/pcap-dpdk.c diff --git a/pcap-dpdk.c b/pcap-dpdk.c index 2116ef44..6c2f21fc 100644 --- a/pcap-dpdk.c +++ b/pcap-dpdk.c @@ -79,7 +79,6 @@ env DPDK_CFG="--log-level=debug -l0 -dlibrte_pmd_e1000.so -dlibrte_pmd_ixgbe.so #include #endif -#include #include #include #include @@ -91,7 +90,9 @@ env DPDK_CFG="--log-level=debug -l0 -dlibrte_pmd_e1000.so -dlibrte_pmd_ixgbe.so #include //header for calling dpdk +#include #include +#include #include #include #include @@ -115,6 +116,12 @@ env DPDK_CFG="--log-level=debug -l0 -dlibrte_pmd_e1000.so -dlibrte_pmd_ixgbe.so #include "pcap-dpdk.h" #define DPDK_DEF_LOG_LEV RTE_LOG_ERR +// +// This is set to 0 if we haven't initialized DPDK yet, 1 if we've +// successfully initialized it, a negative value, which is the negative +// of the rte_errno from rte_eal_init(), if we tried to initialize it +// and got an error. +// static int is_dpdk_pre_inited=0; #define DPDK_LIB_NAME "libpcap_dpdk" #define DPDK_DESC "Data Plane Development Kit (DPDK) Interface" @@ -184,6 +191,53 @@ static struct rte_eth_conf port_conf = { }, }; +static void dpdk_fmt_errmsg_for_rte_errno(char *, size_t, int, + PCAP_FORMAT_STRING(const char *), ...) PCAP_PRINTFLIKE(4, 5); + +/* + * Generate an error message based on a format, arguments, and an + * rte_errno, with a message for the rte_errno after the formatted output. + */ +static void dpdk_fmt_errmsg_for_rte_errno(char *errbuf, size_t errbuflen, + int errnum, const char *fmt, ...) +{ + va_list ap; + size_t msglen; + char *p; + size_t errbuflen_remaining; + + va_start(ap, fmt); + vsnprintf(errbuf, errbuflen, fmt, ap); + va_end(ap); + msglen = strlen(errbuf); + + /* + * Do we have enough space to append ": "? + * Including the terminating '\0', that's 3 bytes. + */ + if (msglen + 3 > errbuflen) { + /* No - just give them what we've produced. */ + return; + } + p = errbuf + msglen; + errbuflen_remaining = errbuflen - msglen; + *p++ = ':'; + *p++ = ' '; + *p = '\0'; + msglen += 2; + errbuflen_remaining -= 2; + + /* + * Now append the string for the error code. + * rte_strerror() is thread-safe, at least as of dpdk 18.11, + * unlike strerror() - it uses strerror_r() rather than strerror() + * for UN*X errno values, and prints to what I assume is a per-thread + * buffer (based on the "PER_LCORE" in "RTE_DEFINE_PER_LCORE" used + * to declare the buffers statically) for DPDK errors. + */ + snprintf(p, errbuflen_remaining, "%s", rte_strerror(errnum)); +} + static int dpdk_init_timer(struct pcap_dpdk *pd){ gettimeofday(&(pd->ts_helper.start_time),NULL); pd->ts_helper.start_cycles = rte_get_timer_cycles(); @@ -216,13 +270,13 @@ static uint32_t dpdk_gather_data(unsigned char *data, int len, struct rte_mbuf * } -static int dpdk_read_with_timeout(pcap_t *p, uint16_t portid, uint16_t queueid,struct rte_mbuf **pkts_burst, const uint16_t burst_cnt){ +static int dpdk_read_with_timeout(pcap_t *p, uint16_t portid, struct rte_mbuf **pkts_burst, const uint16_t burst_cnt){ struct pcap_dpdk *pd = (struct pcap_dpdk*)(p->priv); int nb_rx = 0; int timeout_ms = p->opt.timeout; int sleep_ms = 0; if (pd->nonblock){ - // In non-blocking mode, just read once, no mater how many packets are captured. + // In non-blocking mode, just read once, no matter how many packets are captured. nb_rx = (int)rte_eth_rx_burst(pd->portid, 0, pkts_burst, burst_cnt); }else{ // In blocking mode, read many times until packets are captured or timeout or break_loop is setted. @@ -256,12 +310,11 @@ static int pcap_dpdk_dispatch(pcap_t *p, int max_cnt, pcap_handler cb, u_char *c uint16_t portid = pd->portid; // In DPDK, pkt_len is sum of lengths for all segments. And data_len is for one segment uint32_t pkt_len = 0; - int caplen = 0; + uint32_t caplen = 0; u_char *bp = NULL; int i=0; unsigned int gather_len =0; int pkt_cnt = 0; - int is_accepted=0; u_char *large_buffer=NULL; int timeout_ms = p->opt.timeout; @@ -278,7 +331,7 @@ static int pcap_dpdk_dispatch(pcap_t *p, int max_cnt, pcap_handler cb, u_char *c } // read once in non-blocking mode, or try many times waiting for timeout_ms. // if timeout_ms == 0, it will be blocked until one packet arrives or break_loop is setted. - nb_rx = dpdk_read_with_timeout(p, portid, 0, pkts_burst, burst_cnt); + nb_rx = dpdk_read_with_timeout(p, portid, pkts_burst, burst_cnt); if (nb_rx == 0){ if (pd->nonblock){ RTE_LOG(DEBUG, USER1, "dpdk: no packets available in non-blocking mode.\n"); @@ -301,7 +354,7 @@ static int pcap_dpdk_dispatch(pcap_t *p, int max_cnt, pcap_handler cb, u_char *c pkt_len = rte_pktmbuf_pkt_len(m); // caplen = min(pkt_len, p->snapshot); // caplen will not be changed, no matter how long the rte_pktmbuf - caplen = pkt_len < p->snapshot ? pkt_len: p->snapshot; + caplen = pkt_len < (uint32_t)p->snapshot ? pkt_len: (uint32_t)p->snapshot; pcap_header.caplen = caplen; pcap_header.len = pkt_len; // volatile prefetch @@ -345,8 +398,9 @@ static int pcap_dpdk_dispatch(pcap_t *p, int max_cnt, pcap_handler cb, u_char *c static int pcap_dpdk_inject(pcap_t *p, const void *buf _U_, int size _U_) { //not implemented yet - pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, - errno, "dpdk error: Inject function has not been implemented yet"); + pcap_strlcpy(p->errbuf, + "dpdk error: Inject function has not been implemented yet", + PCAP_ERRBUF_SIZE); return PCAP_ERROR; } @@ -422,7 +476,7 @@ static void eth_addr_str(struct ether_addr *addrp, char* mac_str, int len) { int offset=0; if (addrp == NULL){ - pcap_snprintf(mac_str, len-1, DPDK_DEF_MAC_ADDR); + snprintf(mac_str, len-1, DPDK_DEF_MAC_ADDR); return; } for (int i=0; i<6; i++) @@ -433,10 +487,10 @@ static void eth_addr_str(struct ether_addr *addrp, char* mac_str, int len) } if (i==0) { - pcap_snprintf(mac_str+offset, len-1-offset, "%02X",addrp->addr_bytes[i]); + snprintf(mac_str+offset, len-1-offset, "%02X",addrp->addr_bytes[i]); offset+=2; // FF }else{ - pcap_snprintf(mac_str+offset, len-1-offset, ":%02X", addrp->addr_bytes[i]); + snprintf(mac_str+offset, len-1-offset, ":%02X", addrp->addr_bytes[i]); offset+=3; // :FF } } @@ -497,27 +551,40 @@ static int parse_dpdk_cfg(char* dpdk_cfg,char** dargv) } // only called once -static int dpdk_pre_init(char * ebuf) +// Returns: +// +// 1 on success; +// +// 0 if "the EAL cannot initialize on this system", which we treat as +// meaning "DPDK isn't available"; +// +// a PCAP_ERROR_ code for other errors. +// +// If eaccess_not_fatal is non-zero, treat "a permissions issue" the way +// we treat "the EAL cannot initialize on this system". We use that +// when trying to find DPDK devices, as we don't want to fail to return +// *any* devices just because we can't support DPDK; when we're trying +// to open a device, we need to return a permissions error in that case. +static int dpdk_pre_init(char * ebuf, int eaccess_not_fatal) { int dargv_cnt=0; char *dargv[DPDK_ARGC_MAX]; char *ptr_dpdk_cfg = NULL; - int ret = PCAP_ERROR; + int ret; // globale var - if (is_dpdk_pre_inited) - { - // already inited - return 0; - } - // check for root permission - if( geteuid() != 0) + if (is_dpdk_pre_inited != 0) { - RTE_LOG(ERR, USER1, "%s\n", DPDK_ERR_PERM_MSG); - pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, - errno, "dpdk error: %s", - DPDK_ERR_PERM_MSG); - ret = PCAP_ERROR_PERM_DENIED; - return ret; + // already inited; did that succeed? + if (is_dpdk_pre_inited < 0) + { + // failed + goto error; + } + else + { + // succeeded + return 1; + } } // init EAL ptr_dpdk_cfg = getenv(DPDK_CFG_ENV_NAME); @@ -532,11 +599,127 @@ static int dpdk_pre_init(char * ebuf) snprintf(dpdk_cfg_buf,DPDK_CFG_MAX_LEN-1,"%s %s",DPDK_LIB_NAME,ptr_dpdk_cfg); dargv_cnt = parse_dpdk_cfg(dpdk_cfg_buf,dargv); ret = rte_eal_init(dargv_cnt,dargv); - // if init successed, we do not need to do it again later. - if (ret == 0){ - is_dpdk_pre_inited = 1; + if (ret == -1) + { + // Indicate that we've called rte_eal_init() by setting + // is_dpdk_pre_inited to the negative of the error code, + // and process the error. + is_dpdk_pre_inited = -rte_errno; + goto error; } - return ret; + // init succeeded, so we do not need to do it again later. + is_dpdk_pre_inited = 1; + return 1; + +error: + switch (-is_dpdk_pre_inited) + { + case EACCES: + // This "indicates a permissions issue.". + RTE_LOG(ERR, USER1, "%s\n", DPDK_ERR_PERM_MSG); + // If we were told to treat this as just meaning + // DPDK isn't available, do so. + if (eaccess_not_fatal) + return 0; + // Otherwise report a fatal error. + snprintf(ebuf, PCAP_ERRBUF_SIZE, + "DPDK requires that it run as root"); + return PCAP_ERROR_PERM_DENIED; + + case EAGAIN: + // This "indicates either a bus or system + // resource was not available, setup may + // be attempted again." + // There's no such error in pcap, so I'm + // not sure what we should do here. + snprintf(ebuf, PCAP_ERRBUF_SIZE, + "Bus or system resource was not available"); + break; + + case EALREADY: + // This "indicates that the rte_eal_init + // function has already been called, and + // cannot be called again." + // That's not an error; set the "we've + // been here before" flag and return + // success. + is_dpdk_pre_inited = 1; + return 1; + + case EFAULT: + // This "indicates the tailq configuration + // name was not found in memory configuration." + snprintf(ebuf, PCAP_ERRBUF_SIZE, + "The tailq configuration name was not found in the memory configuration"); + return PCAP_ERROR; + + case EINVAL: + // This "indicates invalid parameters were + // passed as argv/argc." Those came from + // the configuration file. + snprintf(ebuf, PCAP_ERRBUF_SIZE, + "The configuration file has invalid parameters"); + break; + + case ENOMEM: + // This "indicates failure likely caused by + // an out-of-memory condition." + snprintf(ebuf, PCAP_ERRBUF_SIZE, + "Out of memory"); + break; + + case ENODEV: + // This "indicates memory setup issues." + snprintf(ebuf, PCAP_ERRBUF_SIZE, + "An error occurred setting up memory"); + break; + + case ENOTSUP: + // This "indicates that the EAL cannot + // initialize on this system." We treat + // that as meaning DPDK isn't available + // on this machine, rather than as a + // fatal error, and let our caller decide + // whether that's a fatal error (if trying + // to activate a DPDK device) or not (if + // trying to enumerate devices). + return 0; + + case EPROTO: + // This "indicates that the PCI bus is + // either not present, or is not readable + // by the eal." Does "the PCI bus is not + // present" mean "this machine has no PCI + // bus", which strikes me as a "not available" + // case? If so, should "is not readable by + // the EAL" also something we should treat + // as a "not available" case? If not, we + // can't distinguish between the two, so + // we're stuck. + snprintf(ebuf, PCAP_ERRBUF_SIZE, + "PCI bus is not present or not readable by the EAL"); + break; + + case ENOEXEC: + // This "indicates that a service core + // failed to launch successfully." + snprintf(ebuf, PCAP_ERRBUF_SIZE, + "A service core failed to launch successfully"); + break; + + default: + // + // That's not in the list of errors in + // the documentation; let it be reported + // as an error. + // + dpdk_fmt_errmsg_for_rte_errno(ebuf, + PCAP_ERRBUF_SIZE, -is_dpdk_pre_inited, + "dpdk error: dpdk_pre_init failed"); + break; + } + // Error. + return PCAP_ERROR; } static int pcap_dpdk_activate(pcap_t *p) @@ -554,21 +737,32 @@ static int pcap_dpdk_activate(pcap_t *p) int is_port_up = 0; struct rte_eth_link link; do{ - //init EAL - ret = dpdk_pre_init(p->errbuf); + //init EAL; fail if we have insufficient permission + char dpdk_pre_init_errbuf[PCAP_ERRBUF_SIZE]; + ret = dpdk_pre_init(dpdk_pre_init_errbuf, 0); if (ret < 0) { - pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, - errno, "dpdk error: Init failed with device %s", - p->opt.device); - ret = PCAP_ERROR; + // This returns a negative value on an error. + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Can't open device %s: %s", + p->opt.device, dpdk_pre_init_errbuf); + // ret is set to the correct error break; } + if (ret == 0) + { + // This means DPDK isn't available on this machine. + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Can't open device %s: DPDK is not available on this machine", + p->opt.device); + return PCAP_ERROR_NO_SUCH_DEVICE; + } + ret = dpdk_init_timer(pd); if (ret<0) { - pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, - errno, "dpdk error: Init timer error with device %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "dpdk error: Init timer is zero with device %s", p->opt.device); ret = PCAP_ERROR; break; @@ -577,16 +771,16 @@ static int pcap_dpdk_activate(pcap_t *p) nb_ports = rte_eth_dev_count_avail(); if (nb_ports == 0) { - pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, - errno, "dpdk error: No Ethernet ports"); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "dpdk error: No Ethernet ports"); ret = PCAP_ERROR; break; } portid = portid_by_device(p->opt.device); if (portid == DPDK_PORTID_MAX){ - pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, - errno, "dpdk error: portid is invalid. device %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "dpdk error: portid is invalid. device %s", p->opt.device); ret = PCAP_ERROR_NO_SUCH_DEVICE; break; @@ -604,8 +798,9 @@ static int pcap_dpdk_activate(pcap_t *p) rte_socket_id()); if (pd->pktmbuf_pool == NULL) { - pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, - errno, "dpdk error: Cannot init mbuf pool"); + dpdk_fmt_errmsg_for_rte_errno(p->errbuf, + PCAP_ERRBUF_SIZE, rte_errno, + "dpdk error: Cannot init mbuf pool"); ret = PCAP_ERROR; break; } @@ -619,9 +814,10 @@ static int pcap_dpdk_activate(pcap_t *p) ret = rte_eth_dev_configure(portid, 1, 1, &local_port_conf); if (ret < 0) { - pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, - errno, "dpdk error: Cannot configure device: err=%d, port=%u", - ret, portid); + dpdk_fmt_errmsg_for_rte_errno(p->errbuf, + PCAP_ERRBUF_SIZE, -ret, + "dpdk error: Cannot configure device: port=%u", + portid); ret = PCAP_ERROR; break; } @@ -629,9 +825,10 @@ static int pcap_dpdk_activate(pcap_t *p) ret = rte_eth_dev_adjust_nb_rx_tx_desc(portid, &nb_rxd, &nb_txd); if (ret < 0) { - pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, - errno, "dpdk error: Cannot adjust number of descriptors: err=%d, port=%u", - ret, portid); + dpdk_fmt_errmsg_for_rte_errno(p->errbuf, + PCAP_ERRBUF_SIZE, -ret, + "dpdk error: Cannot adjust number of descriptors: port=%u", + portid); ret = PCAP_ERROR; break; } @@ -648,9 +845,10 @@ static int pcap_dpdk_activate(pcap_t *p) pd->pktmbuf_pool); if (ret < 0) { - pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, - errno, "dpdk error: rte_eth_rx_queue_setup:err=%d, port=%u", - ret, portid); + dpdk_fmt_errmsg_for_rte_errno(p->errbuf, + PCAP_ERRBUF_SIZE, -ret, + "dpdk error: rte_eth_rx_queue_setup:port=%u", + portid); ret = PCAP_ERROR; break; } @@ -663,9 +861,10 @@ static int pcap_dpdk_activate(pcap_t *p) &txq_conf); if (ret < 0) { - pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, - errno, "dpdk error: rte_eth_tx_queue_setup:err=%d, port=%u", - ret, portid); + dpdk_fmt_errmsg_for_rte_errno(p->errbuf, + PCAP_ERRBUF_SIZE, -ret, + "dpdk error: rte_eth_tx_queue_setup:port=%u", + portid); ret = PCAP_ERROR; break; } @@ -675,8 +874,8 @@ static int pcap_dpdk_activate(pcap_t *p) rte_eth_dev_socket_id(portid)); if (tx_buffer == NULL) { - pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, - errno, "dpdk error: Cannot allocate buffer for tx on port %u", portid); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "dpdk error: Cannot allocate buffer for tx on port %u", portid); ret = PCAP_ERROR; break; } @@ -685,9 +884,10 @@ static int pcap_dpdk_activate(pcap_t *p) ret = rte_eth_dev_start(portid); if (ret < 0) { - pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, - errno, "dpdk error: rte_eth_dev_start:err=%d, port=%u", - ret, portid); + dpdk_fmt_errmsg_for_rte_errno(p->errbuf, + PCAP_ERRBUF_SIZE, -ret, + "dpdk error: rte_eth_dev_start:port=%u", + portid); ret = PCAP_ERROR; break; } @@ -699,8 +899,8 @@ static int pcap_dpdk_activate(pcap_t *p) // check link status is_port_up = check_link_status(portid, &link); if (!is_port_up){ - pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, - errno, "dpdk error: link is down, port=%u",portid); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "dpdk error: link is down, port=%u",portid); ret = PCAP_ERROR_IFACE_NOT_UP; break; } @@ -777,31 +977,41 @@ int pcap_dpdk_findalldevs(pcap_if_list_t *devlistp, char *ebuf) char mac_addr[DPDK_MAC_ADDR_SIZE]; char pci_addr[DPDK_PCI_ADDR_SIZE]; do{ - ret = dpdk_pre_init(ebuf); + // init EAL; return "DPDK not available" if we + // have insufficient permission + char dpdk_pre_init_errbuf[PCAP_ERRBUF_SIZE]; + ret = dpdk_pre_init(dpdk_pre_init_errbuf, 1); if (ret < 0) { - pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, - errno, "error: Init failed with device"); + // This returns a negative value on an error. + snprintf(ebuf, PCAP_ERRBUF_SIZE, + "Can't look for DPDK devices: %s", + dpdk_pre_init_errbuf); ret = PCAP_ERROR; break; } + if (ret == 0) + { + // This means DPDK isn't available on this machine. + // That just means "don't return any devices". + break; + } nb_ports = rte_eth_dev_count_avail(); if (nb_ports == 0) { - pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, - errno, "DPDK error: No Ethernet ports"); - ret = PCAP_ERROR; + // That just means "don't return any devices". + ret = 0; break; } for (unsigned int i=0; i