This changes the API for how pcapng is used. Before each interface was automatically added to the capture file. Now the application must add each interface. Note: API changes are allowed because this is an experimental interface.
This allows application to specify extra meta data like interface name, description and packet filter. Signed-off-by: Stephen Hemminger <step...@networkplumber.org> --- app/test/test_pcapng.c | 7 ++++ lib/pcapng/rte_pcapng.c | 79 ++++++++++++++++++++++++++--------------- lib/pcapng/rte_pcapng.h | 25 +++++++++++++ lib/pcapng/version.map | 1 + 4 files changed, 84 insertions(+), 28 deletions(-) diff --git a/app/test/test_pcapng.c b/app/test/test_pcapng.c index a7acbdc0586d..edba46d1fe0f 100644 --- a/app/test/test_pcapng.c +++ b/app/test/test_pcapng.c @@ -109,6 +109,13 @@ test_setup(void) return -1; } + /* Add interface to the file */ + if (rte_pcapng_add_interface(pcapng, port_id, + NULL, NULL, NULL) != 0) { + fprintf(stderr, "can not add port %u\n", port_id); + return -1; + } + /* Make a pool for cloned packets */ mp = rte_pktmbuf_pool_create_by_ops("pcapng_test_pool", IOV_MAX + NUM_PACKETS, 0, 0, diff --git a/lib/pcapng/rte_pcapng.c b/lib/pcapng/rte_pcapng.c index 80d08e1a3bde..ea004939e63e 100644 --- a/lib/pcapng/rte_pcapng.c +++ b/lib/pcapng/rte_pcapng.c @@ -32,6 +32,9 @@ /* Format of the capture file handle */ struct rte_pcapng { int outfd; /* output file */ + + unsigned int ports; /* number of interfaces added */ + /* DPDK port id to interface index in file */ uint32_t port_index[RTE_MAX_ETHPORTS]; }; @@ -185,8 +188,10 @@ pcapng_section_block(rte_pcapng_t *self, } /* Write an interface block for a DPDK port */ -static int -pcapng_add_interface(rte_pcapng_t *self, uint16_t port) +int +rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port, + const char *ifname, const char *ifdescr, + const char *filter) { struct pcapng_interface_block *hdr; struct rte_eth_dev_info dev_info; @@ -197,7 +202,7 @@ pcapng_add_interface(rte_pcapng_t *self, uint16_t port) const uint8_t tsresol = 9; /* nanosecond resolution */ uint32_t len; void *buf; - char ifname[IF_NAMESIZE]; + char ifname_buf[IF_NAMESIZE]; char ifhw[256]; uint64_t speed = 0; @@ -205,8 +210,14 @@ pcapng_add_interface(rte_pcapng_t *self, uint16_t port) return -1; /* make something like an interface name */ - if (if_indextoname(dev_info.if_index, ifname) == NULL) - snprintf(ifname, IF_NAMESIZE, "dpdk:%u", port); + if (ifname == NULL) { + /* Use kernel name if available */ + ifname = if_indextoname(dev_info.if_index, ifname_buf); + if (ifname == NULL) { + snprintf(ifname_buf, IF_NAMESIZE, "dpdk:%u", port); + ifname = ifname_buf; + } + } /* make a useful device hardware string */ dev = dev_info.device; @@ -230,10 +241,14 @@ pcapng_add_interface(rte_pcapng_t *self, uint16_t port) len += pcapng_optlen(sizeof(tsresol)); /* timestamp */ len += pcapng_optlen(strlen(ifname)); /* ifname */ + if (ifdescr) + len += pcapng_optlen(strlen(ifdescr)); if (ea) len += pcapng_optlen(RTE_ETHER_ADDR_LEN); /* macaddr */ if (speed != 0) len += pcapng_optlen(sizeof(uint64_t)); + if (filter) + len += pcapng_optlen(strlen(filter) + 1); if (dev) len += pcapng_optlen(strlen(ifhw)); @@ -256,6 +271,9 @@ pcapng_add_interface(rte_pcapng_t *self, uint16_t port) &tsresol, sizeof(tsresol)); opt = pcapng_add_option(opt, PCAPNG_IFB_NAME, ifname, strlen(ifname)); + if (ifdescr) + opt = pcapng_add_option(opt, PCAPNG_IFB_DESCRIPTION, + ifdescr, strlen(ifdescr)); if (ea) opt = pcapng_add_option(opt, PCAPNG_IFB_MACADDR, ea, RTE_ETHER_ADDR_LEN); @@ -265,31 +283,29 @@ pcapng_add_interface(rte_pcapng_t *self, uint16_t port) if (dev) opt = pcapng_add_option(opt, PCAPNG_IFB_HARDWARE, ifhw, strlen(ifhw)); + if (filter) { + /* Encoding is that the first octet indicates string vs BPF */ + size_t len; + char *buf; + + len = strlen(filter) + 1; + buf = alloca(len); + *buf = '\0'; + memcpy(buf + 1, filter, len); + + opt = pcapng_add_option(opt, PCAPNG_IFB_FILTER, + buf, len); + } + opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0); /* clone block_length after optionsa */ memcpy(opt, &hdr->block_length, sizeof(uint32_t)); - return write(self->outfd, buf, len); -} - -/* - * Write the list of possible interfaces at the start - * of the file. - */ -static int -pcapng_interfaces(rte_pcapng_t *self) -{ - uint16_t port_id; - uint16_t index = 0; + /* remember the file index */ + self->port_index[port] = self->ports++; - RTE_ETH_FOREACH_DEV(port_id) { - /* The list if ports in pcapng needs to be contiguous */ - self->port_index[port_id] = index++; - if (pcapng_add_interface(self, port_id) < 0) - return -1; - } - return 0; + return write(self->outfd, buf, len); } /* @@ -598,6 +614,13 @@ rte_pcapng_write_packets(rte_pcapng_t *self, return -1; } + /* check that this interface was added. */ + epb->interface_id = self->port_index[m->port]; + if (unlikely(epb->interface_id > RTE_MAX_ETHPORTS)) { + rte_errno = EINVAL; + return -1; + } + /* * Handle case of highly fragmented and large burst size * Note: this assumes that max segments per mbuf < IOV_MAX @@ -616,7 +639,6 @@ rte_pcapng_write_packets(rte_pcapng_t *self, * The DPDK port is recorded during pcapng_copy. * Map that to PCAPNG interface in file. */ - epb->interface_id = self->port_index[m->port]; do { iov[cnt].iov_base = rte_pktmbuf_mtod(m, void *); iov[cnt].iov_len = rte_pktmbuf_data_len(m); @@ -638,6 +660,7 @@ rte_pcapng_fdopen(int fd, const char *osname, const char *hardware, const char *appname, const char *comment) { + unsigned int i; rte_pcapng_t *self; self = malloc(sizeof(*self)); @@ -647,13 +670,13 @@ rte_pcapng_fdopen(int fd, } self->outfd = fd; + self->ports = 0; + for (i = 0; i < RTE_MAX_ETHPORTS; i++) + self->port_index[i] = UINT32_MAX; if (pcapng_section_block(self, osname, hardware, appname, comment) < 0) goto fail; - if (pcapng_interfaces(self) < 0) - goto fail; - return self; fail: free(self); diff --git a/lib/pcapng/rte_pcapng.h b/lib/pcapng/rte_pcapng.h index 7d2697c647ef..86b7996e291e 100644 --- a/lib/pcapng/rte_pcapng.h +++ b/lib/pcapng/rte_pcapng.h @@ -70,6 +70,31 @@ __rte_experimental void rte_pcapng_close(rte_pcapng_t *self); +/** + * Add interface information to the capture file + * + * @param self + * The handle to the packet capture file + * @param port + * The Ethernet port to report stats on. + * @param ifname (optional) + * Interface name to record in the file. + * If not specified, name will be constructed from port + * @param ifdescr (optional) + * Interface description to record in the file. + * @param filter + * Capture filter to record in the file. + * + * Interfaces must be added to the output file after opening + * and before any packet record. All ports used in packet capture + * must be added. + */ +__rte_experimental +int +rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port, + const char *ifname, const char *ifdescr, + const char *filter); + /** * Direction flag * These should match Enhanced Packet Block flag bits diff --git a/lib/pcapng/version.map b/lib/pcapng/version.map index 05a9c86a7d91..e98e71038ee6 100644 --- a/lib/pcapng/version.map +++ b/lib/pcapng/version.map @@ -7,6 +7,7 @@ EXPERIMENTAL { rte_pcapng_mbuf_size; rte_pcapng_write_packets; rte_pcapng_write_stats; + rte_pcapng_add_interface; local: *; }; -- 2.39.0