1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2019 Microsoft Corporation
11 #include <sys/utsname.h>
15 #include <rte_common.h>
16 #include <rte_cycles.h>
18 #include <rte_errno.h>
19 #include <rte_ethdev.h>
20 #include <rte_ether.h>
22 #include <rte_pcapng.h>
25 #include "pcapng_proto.h"
27 /* conversion from DPDK speed to PCAPNG */
28 #define PCAPNG_MBPS_SPEED 1000000ull
30 /* Format of the capture file handle */
32 int outfd; /* output file */
33 /* DPDK port id to interface index in file */
34 uint32_t port_index[RTE_MAX_ETHPORTS];
37 /* For converting TSC cycles to PCAPNG ns format */
47 pcapng_time.cycles = rte_get_tsc_cycles();
48 clock_gettime(CLOCK_REALTIME, &ts);
49 pcapng_time.ns = rte_timespec_to_ns(&ts);
52 /* PCAPNG timestamps are in nanoseconds */
53 static uint64_t pcapng_tsc_to_ns(uint64_t cycles)
57 delta = cycles - pcapng_time.cycles;
58 return pcapng_time.ns + (delta * NSEC_PER_SEC) / rte_get_tsc_hz();
61 /* length of option including padding */
62 static uint16_t pcapng_optlen(uint16_t len)
64 return RTE_ALIGN(sizeof(struct pcapng_option) + len,
68 /* build TLV option and return location of next */
69 static struct pcapng_option *
70 pcapng_add_option(struct pcapng_option *popt, uint16_t code,
71 const void *data, uint16_t len)
75 memcpy(popt->data, data, len);
77 return (struct pcapng_option *)((uint8_t *)popt + pcapng_optlen(len));
81 * Write required initial section header describing the capture
84 pcapng_section_block(rte_pcapng_t *self,
85 const char *os, const char *hw,
86 const char *app, const char *comment)
88 struct pcapng_section_header *hdr;
89 struct pcapng_option *opt;
96 len += pcapng_optlen(strlen(hw));
98 len += pcapng_optlen(strlen(os));
100 len += pcapng_optlen(strlen(app));
102 len += pcapng_optlen(strlen(comment));
104 /* reserve space for OPT_END */
105 len += pcapng_optlen(0);
106 len += sizeof(uint32_t);
108 buf = calloc(1, len);
112 hdr = (struct pcapng_section_header *)buf;
113 *hdr = (struct pcapng_section_header) {
114 .block_type = PCAPNG_SECTION_BLOCK,
116 .byte_order_magic = PCAPNG_BYTE_ORDER_MAGIC,
117 .major_version = PCAPNG_MAJOR_VERS,
118 .minor_version = PCAPNG_MINOR_VERS,
119 .section_length = UINT64_MAX,
122 /* After the section header insert variable length options. */
123 opt = (struct pcapng_option *)(hdr + 1);
125 opt = pcapng_add_option(opt, PCAPNG_OPT_COMMENT,
126 comment, strlen(comment));
128 opt = pcapng_add_option(opt, PCAPNG_SHB_HARDWARE,
131 opt = pcapng_add_option(opt, PCAPNG_SHB_OS,
134 opt = pcapng_add_option(opt, PCAPNG_SHB_USERAPPL,
137 /* The standard requires last option to be OPT_END */
138 opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0);
140 /* clone block_length after option */
141 memcpy(opt, &hdr->block_length, sizeof(uint32_t));
143 cc = write(self->outfd, buf, len);
149 /* Write an interface block for a DPDK port */
151 pcapng_add_interface(rte_pcapng_t *self, uint16_t port)
153 struct pcapng_interface_block *hdr;
154 struct rte_eth_dev_info dev_info;
155 struct rte_ether_addr *ea, macaddr;
156 const struct rte_device *dev;
157 struct rte_eth_link link;
158 struct pcapng_option *opt;
159 const uint8_t tsresol = 9; /* nanosecond resolution */
162 char ifname[IF_NAMESIZE];
166 if (rte_eth_dev_info_get(port, &dev_info) < 0)
169 /* make something like an interface name */
170 if (if_indextoname(dev_info.if_index, ifname) == NULL)
171 snprintf(ifname, IF_NAMESIZE, "dpdk:%u", port);
173 /* make a useful device hardware string */
174 dev = dev_info.device;
176 snprintf(ifhw, sizeof(ifhw),
177 "%s-%s", dev->bus->name, dev->name);
179 /* DPDK reports in units of Mbps */
180 rte_eth_link_get(port, &link);
181 if (link.link_status == ETH_LINK_UP)
182 speed = link.link_speed * PCAPNG_MBPS_SPEED;
184 if (rte_eth_macaddr_get(port, &macaddr) < 0)
189 /* Compute length of interface block options */
192 len += pcapng_optlen(sizeof(tsresol)); /* timestamp */
193 len += pcapng_optlen(strlen(ifname)); /* ifname */
196 len += pcapng_optlen(RTE_ETHER_ADDR_LEN); /* macaddr */
198 len += pcapng_optlen(sizeof(uint64_t));
200 len += pcapng_optlen(strlen(ifhw));
202 len += pcapng_optlen(0);
203 len += sizeof(uint32_t);
209 hdr = (struct pcapng_interface_block *)buf;
210 *hdr = (struct pcapng_interface_block) {
211 .block_type = PCAPNG_INTERFACE_BLOCK,
212 .link_type = 1, /* DLT_EN10MB - Ethernet */
216 opt = (struct pcapng_option *)(hdr + 1);
217 opt = pcapng_add_option(opt, PCAPNG_IFB_TSRESOL,
218 &tsresol, sizeof(tsresol));
219 opt = pcapng_add_option(opt, PCAPNG_IFB_NAME,
220 ifname, strlen(ifname));
222 opt = pcapng_add_option(opt, PCAPNG_IFB_MACADDR,
223 ea, RTE_ETHER_ADDR_LEN);
225 opt = pcapng_add_option(opt, PCAPNG_IFB_SPEED,
226 &speed, sizeof(uint64_t));
228 opt = pcapng_add_option(opt, PCAPNG_IFB_HARDWARE,
230 opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0);
232 /* clone block_length after optionsa */
233 memcpy(opt, &hdr->block_length, sizeof(uint32_t));
235 return write(self->outfd, buf, len);
239 * Write the list of possible interfaces at the start
243 pcapng_interfaces(rte_pcapng_t *self)
248 RTE_ETH_FOREACH_DEV(port_id) {
249 /* The list if ports in pcapng needs to be contiguous */
250 self->port_index[port_id] = index++;
251 if (pcapng_add_interface(self, port_id) < 0)
258 * Write an Interface statistics block at the end of capture.
261 rte_pcapng_write_stats(rte_pcapng_t *self, uint16_t port_id,
263 uint64_t start_time, uint64_t end_time,
264 uint64_t ifrecv, uint64_t ifdrop)
266 struct pcapng_statistics *hdr;
267 struct pcapng_option *opt;
268 uint32_t optlen, len;
272 RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL);
276 if (ifrecv != UINT64_MAX)
277 optlen += pcapng_optlen(sizeof(ifrecv));
278 if (ifdrop != UINT64_MAX)
279 optlen += pcapng_optlen(sizeof(ifdrop));
281 optlen += pcapng_optlen(sizeof(start_time));
283 optlen += pcapng_optlen(sizeof(end_time));
285 optlen += pcapng_optlen(strlen(comment));
287 optlen += pcapng_optlen(0);
289 len = sizeof(*hdr) + optlen + sizeof(uint32_t);
294 hdr = (struct pcapng_statistics *)buf;
295 opt = (struct pcapng_option *)(hdr + 1);
298 opt = pcapng_add_option(opt, PCAPNG_OPT_COMMENT,
299 comment, strlen(comment));
301 opt = pcapng_add_option(opt, PCAPNG_ISB_STARTTIME,
302 &start_time, sizeof(start_time));
304 opt = pcapng_add_option(opt, PCAPNG_ISB_ENDTIME,
305 &end_time, sizeof(end_time));
306 if (ifrecv != UINT64_MAX)
307 opt = pcapng_add_option(opt, PCAPNG_ISB_IFRECV,
308 &ifrecv, sizeof(ifrecv));
309 if (ifdrop != UINT64_MAX)
310 opt = pcapng_add_option(opt, PCAPNG_ISB_IFDROP,
311 &ifdrop, sizeof(ifdrop));
313 opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0);
315 hdr->block_type = PCAPNG_INTERFACE_STATS_BLOCK;
316 hdr->block_length = len;
317 hdr->interface_id = self->port_index[port_id];
319 ns = pcapng_tsc_to_ns(rte_get_tsc_cycles());
320 hdr->timestamp_hi = ns >> 32;
321 hdr->timestamp_lo = (uint32_t)ns;
323 /* clone block_length after option */
324 memcpy(opt, &len, sizeof(uint32_t));
326 return write(self->outfd, buf, len);
330 rte_pcapng_mbuf_size(uint32_t length)
332 /* The VLAN and EPB header must fit in the mbuf headroom. */
333 RTE_ASSERT(sizeof(struct pcapng_enhance_packet_block) +
334 sizeof(struct rte_vlan_hdr) <= RTE_PKTMBUF_HEADROOM);
336 /* The flags and queue information are added at the end. */
337 return sizeof(struct rte_mbuf)
338 + RTE_ALIGN(length, sizeof(uint32_t))
339 + pcapng_optlen(sizeof(uint32_t)) /* flag option */
340 + pcapng_optlen(sizeof(uint32_t)) /* queue option */
341 + sizeof(uint32_t); /* length */
344 /* More generalized version rte_vlan_insert() */
346 pcapng_vlan_insert(struct rte_mbuf *m, uint16_t ether_type, uint16_t tci)
348 struct rte_ether_hdr *nh, *oh;
349 struct rte_vlan_hdr *vh;
351 if (!RTE_MBUF_DIRECT(m) || rte_mbuf_refcnt_read(m) > 1)
354 if (rte_pktmbuf_data_len(m) < sizeof(*oh))
357 oh = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
358 nh = (struct rte_ether_hdr *)
359 rte_pktmbuf_prepend(m, sizeof(struct rte_vlan_hdr));
363 memmove(nh, oh, 2 * RTE_ETHER_ADDR_LEN);
364 nh->ether_type = rte_cpu_to_be_16(ether_type);
366 vh = (struct rte_vlan_hdr *) (nh + 1);
367 vh->vlan_tci = rte_cpu_to_be_16(tci);
373 * The mbufs created use the Pcapng standard enhanced packet block.
376 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
377 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
378 * 0 | Block Type = 0x00000006 |
379 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
380 * 4 | Block Total Length |
381 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
383 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
384 * 12 | Timestamp (High) |
385 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
386 * 16 | Timestamp (Low) |
387 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
388 * 20 | Captured Packet Length |
389 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
390 * 24 | Original Packet Length |
391 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
394 * / variable length, padded to 32 bits /
396 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
397 * | Option Code = 0x0002 | Option Length = 0x004 |
398 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
399 * | Flags (direction) |
400 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
401 * | Option Code = 0x0006 | Option Length = 0x002 |
402 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
404 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
405 * | Block Total Length |
406 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
409 /* Make a copy of original mbuf with pcapng header and options */
411 rte_pcapng_copy(uint16_t port_id, uint32_t queue,
412 const struct rte_mbuf *md,
413 struct rte_mempool *mp,
414 uint32_t length, uint64_t cycles,
415 enum rte_pcapng_direction direction)
417 struct pcapng_enhance_packet_block *epb;
418 uint32_t orig_len, data_len, padding, flags;
419 struct pcapng_option *opt;
420 const uint16_t optlen = pcapng_optlen(sizeof(flags)) + pcapng_optlen(sizeof(queue));
424 #ifdef RTE_LIBRTE_ETHDEV_DEBUG
425 RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, NULL);
427 ns = pcapng_tsc_to_ns(cycles);
429 orig_len = rte_pktmbuf_pkt_len(md);
431 /* Take snapshot of the data */
432 mc = rte_pktmbuf_copy(md, mp, 0, length);
433 if (unlikely(mc == NULL))
436 /* Expand any offloaded VLAN information */
437 if ((direction == RTE_PCAPNG_DIRECTION_IN &&
438 (md->ol_flags & PKT_RX_VLAN_STRIPPED)) ||
439 (direction == RTE_PCAPNG_DIRECTION_OUT &&
440 (md->ol_flags & PKT_TX_VLAN))) {
441 if (pcapng_vlan_insert(mc, RTE_ETHER_TYPE_VLAN,
446 if ((direction == RTE_PCAPNG_DIRECTION_IN &&
447 (md->ol_flags & PKT_RX_QINQ_STRIPPED)) ||
448 (direction == RTE_PCAPNG_DIRECTION_OUT &&
449 (md->ol_flags & PKT_TX_QINQ))) {
450 if (pcapng_vlan_insert(mc, RTE_ETHER_TYPE_QINQ,
451 md->vlan_tci_outer) != 0)
455 /* pad the packet to 32 bit boundary */
456 data_len = rte_pktmbuf_data_len(mc);
457 padding = RTE_ALIGN(data_len, sizeof(uint32_t)) - data_len;
459 void *tail = rte_pktmbuf_append(mc, padding);
463 memset(tail, 0, padding);
466 /* reserve trailing options and block length */
467 opt = (struct pcapng_option *)
468 rte_pktmbuf_append(mc, optlen + sizeof(uint32_t));
469 if (unlikely(opt == NULL))
473 case RTE_PCAPNG_DIRECTION_IN:
474 flags = PCAPNG_IFB_INBOUND;
476 case RTE_PCAPNG_DIRECTION_OUT:
477 flags = PCAPNG_IFB_OUTBOUND;
483 opt = pcapng_add_option(opt, PCAPNG_EPB_FLAGS,
484 &flags, sizeof(flags));
486 opt = pcapng_add_option(opt, PCAPNG_EPB_QUEUE,
487 &queue, sizeof(queue));
489 /* Note: END_OPT necessary here. Wireshark doesn't do it. */
491 /* Add PCAPNG packet header */
492 epb = (struct pcapng_enhance_packet_block *)
493 rte_pktmbuf_prepend(mc, sizeof(*epb));
494 if (unlikely(epb == NULL))
497 epb->block_type = PCAPNG_ENHANCED_PACKET_BLOCK;
498 epb->block_length = rte_pktmbuf_data_len(mc);
500 /* Interface index is filled in later during write */
503 epb->timestamp_hi = ns >> 32;
504 epb->timestamp_lo = (uint32_t)ns;
505 epb->capture_length = data_len;
506 epb->original_length = orig_len;
508 /* set trailer of block length */
509 *(uint32_t *)opt = epb->block_length;
514 rte_pktmbuf_free(mc);
518 /* Count how many segments are in this array of mbufs */
520 mbuf_burst_segs(struct rte_mbuf *pkts[], unsigned int n)
522 unsigned int i, iovcnt;
524 for (iovcnt = 0, i = 0; i < n; i++) {
525 const struct rte_mbuf *m = pkts[i];
527 __rte_mbuf_sanity_check(m, 1);
529 iovcnt += m->nb_segs;
534 /* Write pre-formatted packets to file. */
536 rte_pcapng_write_packets(rte_pcapng_t *self,
537 struct rte_mbuf *pkts[], uint16_t nb_pkts)
539 int iovcnt = mbuf_burst_segs(pkts, nb_pkts);
540 struct iovec iov[iovcnt];
544 for (i = cnt = 0; i < nb_pkts; i++) {
545 struct rte_mbuf *m = pkts[i];
546 struct pcapng_enhance_packet_block *epb;
548 /* sanity check that is really a pcapng mbuf */
549 epb = rte_pktmbuf_mtod(m, struct pcapng_enhance_packet_block *);
550 if (unlikely(epb->block_type != PCAPNG_ENHANCED_PACKET_BLOCK ||
551 epb->block_length != rte_pktmbuf_data_len(m))) {
557 * The DPDK port is recorded during pcapng_copy.
558 * Map that to PCAPNG interface in file.
560 epb->interface_id = self->port_index[m->port];
562 iov[cnt].iov_base = rte_pktmbuf_mtod(m, void *);
563 iov[cnt].iov_len = rte_pktmbuf_data_len(m);
565 } while ((m = m->next));
568 ret = writev(self->outfd, iov, iovcnt);
569 if (unlikely(ret < 0))
574 /* Create new pcapng writer handle */
576 rte_pcapng_fdopen(int fd,
577 const char *osname, const char *hardware,
578 const char *appname, const char *comment)
582 self = malloc(sizeof(*self));
590 if (pcapng_section_block(self, osname, hardware, appname, comment) < 0)
593 if (pcapng_interfaces(self) < 0)
603 rte_pcapng_close(rte_pcapng_t *self)