Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2019 Microsoft Corporation
3 : : */
4 : :
5 : : #include <errno.h>
6 : : #include <stdbool.h>
7 : : #include <stdio.h>
8 : : #include <stdlib.h>
9 : : #include <string.h>
10 : : #include <time.h>
11 : : #include <unistd.h>
12 : :
13 : : #ifndef RTE_EXEC_ENV_WINDOWS
14 : : #include <net/if.h>
15 : : #include <sys/uio.h>
16 : : #endif
17 : :
18 : : #include <bus_driver.h>
19 : : #include <rte_common.h>
20 : : #include <rte_cycles.h>
21 : : #include <dev_driver.h>
22 : : #include <rte_errno.h>
23 : : #include <rte_ethdev.h>
24 : : #include <rte_ether.h>
25 : : #include <rte_mbuf.h>
26 : : #include <rte_os_shim.h>
27 : : #include <rte_pcapng.h>
28 : : #include <rte_reciprocal.h>
29 : : #include <rte_time.h>
30 : :
31 : : #include "pcapng_proto.h"
32 : :
33 : : /* conversion from DPDK speed to PCAPNG */
34 : : #define PCAPNG_MBPS_SPEED 1000000ull
35 : :
36 : : /* upper bound for section, stats and interface blocks */
37 : : #define PCAPNG_BLKSIZ 2048
38 : :
39 : : /* Format of the capture file handle */
40 : : struct rte_pcapng {
41 : : int outfd; /* output file */
42 : : unsigned int ports; /* number of interfaces added */
43 : : uint64_t offset_ns; /* ns since 1/1/1970 when initialized */
44 : : uint64_t tsc_base; /* TSC when started */
45 : :
46 : : /* DPDK port id to interface index in file */
47 : : uint32_t port_index[RTE_MAX_ETHPORTS];
48 : : };
49 : :
50 : : #ifdef RTE_EXEC_ENV_WINDOWS
51 : : /*
52 : : * Windows does not have writev() call.
53 : : * Emulate this by copying to a new buffer.
54 : : * The copy is necessary since pcapng needs to be thread-safe
55 : : * and do atomic write operations.
56 : : */
57 : :
58 : : #define IOV_MAX 128
59 : : struct iovec {
60 : : void *iov_base;
61 : : size_t iov_len;
62 : : };
63 : :
64 : : static ssize_t writev(int fd, const struct iovec *iov, int iovcnt)
65 : : {
66 : : size_t bytes = 0;
67 : : uint8_t *ptr;
68 : : void *tmp_buf;
69 : : ssize_t ret;
70 : : int i;
71 : :
72 : : for (i = 0; i < iovcnt; i++)
73 : : bytes += iov[i].iov_len;
74 : :
75 : : if (unlikely(bytes == 0))
76 : : return 0;
77 : :
78 : : tmp_buf = malloc(bytes);
79 : : if (unlikely(tmp_buf == NULL)) {
80 : : errno = ENOMEM;
81 : : return -1;
82 : : }
83 : :
84 : : ptr = tmp_buf;
85 : : for (i = 0; i < iovcnt; i++) {
86 : : rte_memcpy(ptr, iov[i].iov_base, iov[i].iov_len);
87 : : ptr += iov[i].iov_len;
88 : : }
89 : :
90 : : ret = write(fd, tmp_buf, bytes);
91 : : free(tmp_buf);
92 : : return ret;
93 : : }
94 : :
95 : : #define IF_NAMESIZE 16
96 : : /* compatibility wrapper because name is optional */
97 : : #define if_indextoname(ifindex, ifname) NULL
98 : : #endif
99 : :
100 : : /* Convert from TSC (CPU cycles) to nanoseconds */
101 : : static uint64_t
102 : : pcapng_timestamp(const rte_pcapng_t *self, uint64_t cycles)
103 : : {
104 : : uint64_t delta, rem, secs, ns;
105 : 0 : const uint64_t hz = rte_get_tsc_hz();
106 : :
107 : 0 : delta = cycles - self->tsc_base;
108 : :
109 : : /* Avoid numeric wraparound by computing seconds first */
110 : 0 : secs = delta / hz;
111 : 0 : rem = delta % hz;
112 : 0 : ns = (rem * NS_PER_S) / hz;
113 : :
114 : 0 : return secs * NS_PER_S + ns + self->offset_ns;
115 : : }
116 : :
117 : : /* length of option including padding */
118 : : static uint16_t pcapng_optlen(uint16_t len)
119 : : {
120 : 0 : return RTE_ALIGN(sizeof(struct pcapng_option) + len,
121 : : sizeof(uint32_t));
122 : : }
123 : :
124 : : /* build TLV option and return location of next */
125 : : static struct pcapng_option *
126 : : pcapng_add_option(struct pcapng_option *popt, uint16_t code,
127 : : const void *data, uint16_t len)
128 : : {
129 : 0 : popt->code = code;
130 : 0 : popt->length = len;
131 [ # # ]: 0 : memcpy(popt->data, data, len);
132 : :
133 : 0 : return (struct pcapng_option *)((uint8_t *)popt + pcapng_optlen(len));
134 : : }
135 : :
136 : : /*
137 : : * Write required initial section header describing the capture
138 : : */
139 : : static int
140 : 0 : pcapng_section_block(rte_pcapng_t *self,
141 : : const char *os, const char *hw,
142 : : const char *app, const char *comment)
143 : : {
144 : : struct pcapng_section_header *hdr;
145 : : struct pcapng_option *opt;
146 : : uint8_t buf[PCAPNG_BLKSIZ];
147 : : uint32_t len;
148 : :
149 : : len = sizeof(*hdr);
150 [ # # ]: 0 : if (hw)
151 : 0 : len += pcapng_optlen(strlen(hw));
152 [ # # ]: 0 : if (os)
153 : 0 : len += pcapng_optlen(strlen(os));
154 [ # # ]: 0 : if (app)
155 : 0 : len += pcapng_optlen(strlen(app));
156 [ # # ]: 0 : if (comment)
157 : 0 : len += pcapng_optlen(strlen(comment));
158 : :
159 : : /* reserve space for OPT_END */
160 : : len += pcapng_optlen(0);
161 : 0 : len += sizeof(uint32_t);
162 : :
163 [ # # ]: 0 : if (len > sizeof(buf))
164 : : return -1;
165 : :
166 : : hdr = (struct pcapng_section_header *)buf;
167 : 0 : *hdr = (struct pcapng_section_header) {
168 : : .block_type = PCAPNG_SECTION_BLOCK,
169 : : .block_length = len,
170 : : .byte_order_magic = PCAPNG_BYTE_ORDER_MAGIC,
171 : : .major_version = PCAPNG_MAJOR_VERS,
172 : : .minor_version = PCAPNG_MINOR_VERS,
173 : : .section_length = UINT64_MAX,
174 : : };
175 : :
176 : : /* After the section header insert variable length options. */
177 : : opt = (struct pcapng_option *)(hdr + 1);
178 [ # # ]: 0 : if (comment)
179 : 0 : opt = pcapng_add_option(opt, PCAPNG_OPT_COMMENT,
180 : 0 : comment, strlen(comment));
181 [ # # ]: 0 : if (hw)
182 : 0 : opt = pcapng_add_option(opt, PCAPNG_SHB_HARDWARE,
183 : 0 : hw, strlen(hw));
184 [ # # ]: 0 : if (os)
185 : 0 : opt = pcapng_add_option(opt, PCAPNG_SHB_OS,
186 : 0 : os, strlen(os));
187 [ # # ]: 0 : if (app)
188 : 0 : opt = pcapng_add_option(opt, PCAPNG_SHB_USERAPPL,
189 : 0 : app, strlen(app));
190 : :
191 : : /* The standard requires last option to be OPT_END */
192 : : opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0);
193 : :
194 : : /* clone block_length after option */
195 : : memcpy(opt, &hdr->block_length, sizeof(uint32_t));
196 : :
197 : 0 : return write(self->outfd, buf, len);
198 : : }
199 : :
200 : : /* Write an interface block for a DPDK port */
201 : : int
202 : 0 : rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port,
203 : : const char *ifname, const char *ifdescr,
204 : : const char *filter)
205 : : {
206 : : struct pcapng_interface_block *hdr;
207 : : struct rte_eth_dev_info dev_info;
208 : : struct rte_ether_addr *ea, macaddr;
209 : : const struct rte_device *dev;
210 : : struct rte_eth_link link;
211 : : struct pcapng_option *opt;
212 : 0 : const uint8_t tsresol = 9; /* nanosecond resolution */
213 : : uint32_t len;
214 : : uint8_t buf[PCAPNG_BLKSIZ];
215 : : char ifname_buf[IF_NAMESIZE];
216 : : char ifhw[256];
217 : 0 : uint64_t speed = 0;
218 : :
219 [ # # ]: 0 : if (rte_eth_dev_info_get(port, &dev_info) < 0)
220 : : return -1;
221 : :
222 : : /* make something like an interface name */
223 [ # # ]: 0 : if (ifname == NULL) {
224 : : /* Use kernel name if available */
225 : 0 : ifname = if_indextoname(dev_info.if_index, ifname_buf);
226 [ # # ]: 0 : if (ifname == NULL) {
227 : : snprintf(ifname_buf, IF_NAMESIZE, "dpdk:%u", port);
228 : : ifname = ifname_buf;
229 : : }
230 : : }
231 : :
232 : : /* make a useful device hardware string */
233 : 0 : dev = dev_info.device;
234 [ # # ]: 0 : if (dev)
235 : : snprintf(ifhw, sizeof(ifhw),
236 : 0 : "%s-%s", dev->bus->name, dev->name);
237 : :
238 : : /* DPDK reports in units of Mbps */
239 [ # # ]: 0 : if (rte_eth_link_get(port, &link) == 0 &&
240 [ # # ]: 0 : link.link_status == RTE_ETH_LINK_UP)
241 : 0 : speed = link.link_speed * PCAPNG_MBPS_SPEED;
242 : :
243 [ # # ]: 0 : if (rte_eth_macaddr_get(port, &macaddr) < 0)
244 : : ea = NULL;
245 : : else
246 : : ea = &macaddr;
247 : :
248 : : /* Compute length of interface block options */
249 : : len = sizeof(*hdr);
250 : :
251 : : len += pcapng_optlen(sizeof(tsresol)); /* timestamp */
252 : 0 : len += pcapng_optlen(strlen(ifname)); /* ifname */
253 : :
254 [ # # ]: 0 : if (ifdescr)
255 : 0 : len += pcapng_optlen(strlen(ifdescr));
256 [ # # ]: 0 : if (ea)
257 : 0 : len += pcapng_optlen(RTE_ETHER_ADDR_LEN); /* macaddr */
258 [ # # ]: 0 : if (speed != 0)
259 : 0 : len += pcapng_optlen(sizeof(uint64_t));
260 [ # # ]: 0 : if (filter)
261 : 0 : len += pcapng_optlen(strlen(filter) + 1);
262 [ # # ]: 0 : if (dev)
263 : 0 : len += pcapng_optlen(strlen(ifhw));
264 : :
265 : : len += pcapng_optlen(0);
266 : 0 : len += sizeof(uint32_t);
267 : :
268 [ # # ]: 0 : if (len > sizeof(buf))
269 : : return -1;
270 : :
271 : : hdr = (struct pcapng_interface_block *)buf;
272 [ # # ]: 0 : *hdr = (struct pcapng_interface_block) {
273 : : .block_type = PCAPNG_INTERFACE_BLOCK,
274 : : .link_type = 1, /* DLT_EN10MB - Ethernet */
275 : : .block_length = len,
276 : : };
277 : :
278 : : opt = (struct pcapng_option *)(hdr + 1);
279 : : opt = pcapng_add_option(opt, PCAPNG_IFB_TSRESOL,
280 : : &tsresol, sizeof(tsresol));
281 : 0 : opt = pcapng_add_option(opt, PCAPNG_IFB_NAME,
282 : 0 : ifname, strlen(ifname));
283 [ # # ]: 0 : if (ifdescr)
284 : 0 : opt = pcapng_add_option(opt, PCAPNG_IFB_DESCRIPTION,
285 : 0 : ifdescr, strlen(ifdescr));
286 [ # # ]: 0 : if (ea)
287 : : opt = pcapng_add_option(opt, PCAPNG_IFB_MACADDR,
288 : : ea, RTE_ETHER_ADDR_LEN);
289 [ # # ]: 0 : if (speed != 0)
290 : : opt = pcapng_add_option(opt, PCAPNG_IFB_SPEED,
291 : : &speed, sizeof(uint64_t));
292 [ # # ]: 0 : if (dev)
293 : 0 : opt = pcapng_add_option(opt, PCAPNG_IFB_HARDWARE,
294 : 0 : ifhw, strlen(ifhw));
295 [ # # ]: 0 : if (filter) {
296 : : size_t len;
297 : :
298 : 0 : len = strlen(filter) + 1;
299 : 0 : opt->code = PCAPNG_IFB_FILTER;
300 : 0 : opt->length = len;
301 : : /* Encoding is that the first octet indicates string vs BPF */
302 : 0 : opt->data[0] = 0;
303 : 0 : memcpy(opt->data + 1, filter, strlen(filter));
304 : :
305 : 0 : opt = (struct pcapng_option *)((uint8_t *)opt + pcapng_optlen(len));
306 : : }
307 : :
308 : : opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0);
309 : :
310 : : /* clone block_length after optionsa */
311 : : memcpy(opt, &hdr->block_length, sizeof(uint32_t));
312 : :
313 : : /* remember the file index */
314 : 0 : self->port_index[port] = self->ports++;
315 : :
316 : 0 : return write(self->outfd, buf, len);
317 : : }
318 : :
319 : : /*
320 : : * Write an Interface statistics block at the end of capture.
321 : : */
322 : : ssize_t
323 : 0 : rte_pcapng_write_stats(rte_pcapng_t *self, uint16_t port_id,
324 : : uint64_t ifrecv, uint64_t ifdrop,
325 : : const char *comment)
326 : : {
327 : : struct pcapng_statistics *hdr;
328 : : struct pcapng_option *opt;
329 : 0 : uint64_t start_time = self->offset_ns;
330 : : uint64_t sample_time;
331 : : uint32_t optlen, len;
332 : : uint8_t buf[PCAPNG_BLKSIZ];
333 : :
334 [ # # ]: 0 : RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL);
335 : :
336 : : optlen = 0;
337 : :
338 [ # # ]: 0 : if (ifrecv != UINT64_MAX)
339 : : optlen += pcapng_optlen(sizeof(ifrecv));
340 [ # # ]: 0 : if (ifdrop != UINT64_MAX)
341 : 0 : optlen += pcapng_optlen(sizeof(ifdrop));
342 : :
343 [ # # ]: 0 : if (start_time != 0)
344 : 0 : optlen += pcapng_optlen(sizeof(start_time));
345 : :
346 [ # # ]: 0 : if (comment)
347 : 0 : optlen += pcapng_optlen(strlen(comment));
348 [ # # ]: 0 : if (optlen != 0)
349 : 0 : optlen += pcapng_optlen(0);
350 : :
351 : 0 : len = sizeof(*hdr) + optlen + sizeof(uint32_t);
352 [ # # ]: 0 : if (len > sizeof(buf))
353 : : return -1;
354 : :
355 : : hdr = (struct pcapng_statistics *)buf;
356 : : opt = (struct pcapng_option *)(hdr + 1);
357 : :
358 [ # # ]: 0 : if (comment)
359 : 0 : opt = pcapng_add_option(opt, PCAPNG_OPT_COMMENT,
360 : 0 : comment, strlen(comment));
361 [ # # ]: 0 : if (start_time != 0)
362 : : opt = pcapng_add_option(opt, PCAPNG_ISB_STARTTIME,
363 : : &start_time, sizeof(start_time));
364 [ # # ]: 0 : if (ifrecv != UINT64_MAX)
365 : : opt = pcapng_add_option(opt, PCAPNG_ISB_IFRECV,
366 : : &ifrecv, sizeof(ifrecv));
367 [ # # ]: 0 : if (ifdrop != UINT64_MAX)
368 : : opt = pcapng_add_option(opt, PCAPNG_ISB_IFDROP,
369 : : &ifdrop, sizeof(ifdrop));
370 [ # # ]: 0 : if (optlen != 0)
371 : : opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0);
372 : :
373 : 0 : hdr->block_type = PCAPNG_INTERFACE_STATS_BLOCK;
374 : 0 : hdr->block_length = len;
375 : 0 : hdr->interface_id = self->port_index[port_id];
376 : :
377 : : sample_time = pcapng_timestamp(self, rte_get_tsc_cycles());
378 : 0 : hdr->timestamp_hi = sample_time >> 32;
379 : 0 : hdr->timestamp_lo = (uint32_t)sample_time;
380 : :
381 : : /* clone block_length after option */
382 : : memcpy(opt, &len, sizeof(uint32_t));
383 : :
384 : 0 : return write(self->outfd, buf, len);
385 : : }
386 : :
387 : : uint32_t
388 : 0 : rte_pcapng_mbuf_size(uint32_t length)
389 : : {
390 : : /* The VLAN and EPB header must fit in the mbuf headroom. */
391 : : RTE_ASSERT(sizeof(struct pcapng_enhance_packet_block) +
392 : : sizeof(struct rte_vlan_hdr) <= RTE_PKTMBUF_HEADROOM);
393 : :
394 : : /* The flags and queue information are added at the end. */
395 : : return sizeof(struct rte_mbuf)
396 : 0 : + RTE_ALIGN(length, sizeof(uint32_t))
397 : : + pcapng_optlen(sizeof(uint32_t)) /* flag option */
398 : : + pcapng_optlen(sizeof(uint32_t)) /* queue option */
399 : 0 : + sizeof(uint32_t); /* length */
400 : : }
401 : :
402 : : /* More generalized version rte_vlan_insert() */
403 : : static int
404 : 0 : pcapng_vlan_insert(struct rte_mbuf *m, uint16_t ether_type, uint16_t tci)
405 : : {
406 : : struct rte_ether_hdr *nh, *oh;
407 : : struct rte_vlan_hdr *vh;
408 : :
409 [ # # # # ]: 0 : if (!RTE_MBUF_DIRECT(m) || rte_mbuf_refcnt_read(m) > 1)
410 : 0 : return -EINVAL;
411 : :
412 [ # # ]: 0 : if (rte_pktmbuf_data_len(m) < sizeof(*oh))
413 : : return -EINVAL;
414 : :
415 [ # # ]: 0 : oh = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
416 : : nh = (struct rte_ether_hdr *)
417 : : rte_pktmbuf_prepend(m, sizeof(struct rte_vlan_hdr));
418 [ # # ]: 0 : if (nh == NULL)
419 : 0 : return -ENOSPC;
420 : :
421 : : memmove(nh, oh, 2 * RTE_ETHER_ADDR_LEN);
422 [ # # ]: 0 : nh->ether_type = rte_cpu_to_be_16(ether_type);
423 : :
424 : : vh = (struct rte_vlan_hdr *) (nh + 1);
425 [ # # ]: 0 : vh->vlan_tci = rte_cpu_to_be_16(tci);
426 : :
427 : 0 : return 0;
428 : : }
429 : :
430 : : /*
431 : : * The mbufs created use the Pcapng standard enhanced packet block.
432 : : *
433 : : * 1 2 3
434 : : * 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
435 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
436 : : * 0 | Block Type = 0x00000006 |
437 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
438 : : * 4 | Block Total Length |
439 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
440 : : * 8 | Interface ID |
441 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
442 : : * 12 | Timestamp (High) |
443 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
444 : : * 16 | Timestamp (Low) |
445 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
446 : : * 20 | Captured Packet Length |
447 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
448 : : * 24 | Original Packet Length |
449 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
450 : : * 28 / /
451 : : * / Packet Data /
452 : : * / variable length, padded to 32 bits /
453 : : * / /
454 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
455 : : * | Option Code = 0x0002 | Option Length = 0x004 |
456 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
457 : : * | Flags (direction) |
458 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
459 : : * | Option Code = 0x0006 | Option Length = 0x002 |
460 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
461 : : * | Queue id |
462 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
463 : : * | Block Total Length |
464 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
465 : : */
466 : :
467 : : /* Make a copy of original mbuf with pcapng header and options */
468 : : struct rte_mbuf *
469 : 0 : rte_pcapng_copy(uint16_t port_id, uint32_t queue,
470 : : const struct rte_mbuf *md,
471 : : struct rte_mempool *mp,
472 : : uint32_t length,
473 : : enum rte_pcapng_direction direction,
474 : : const char *comment)
475 : : {
476 : : struct pcapng_enhance_packet_block *epb;
477 : : uint32_t orig_len, data_len, padding, flags;
478 : : struct pcapng_option *opt;
479 : : uint64_t timestamp;
480 : : uint16_t optlen;
481 : : struct rte_mbuf *mc;
482 : : bool rss_hash;
483 : :
484 : : #ifdef RTE_LIBRTE_ETHDEV_DEBUG
485 : : RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, NULL);
486 : : #endif
487 : 0 : orig_len = rte_pktmbuf_pkt_len(md);
488 : :
489 : : /* Take snapshot of the data */
490 : 0 : mc = rte_pktmbuf_copy(md, mp, 0, length);
491 [ # # ]: 0 : if (unlikely(mc == NULL))
492 : : return NULL;
493 : :
494 : : /* Expand any offloaded VLAN information */
495 [ # # ]: 0 : if ((direction == RTE_PCAPNG_DIRECTION_IN &&
496 [ # # # # ]: 0 : (md->ol_flags & RTE_MBUF_F_RX_VLAN_STRIPPED)) ||
497 : 0 : (direction == RTE_PCAPNG_DIRECTION_OUT &&
498 [ # # ]: 0 : (md->ol_flags & RTE_MBUF_F_TX_VLAN))) {
499 [ # # ]: 0 : if (pcapng_vlan_insert(mc, RTE_ETHER_TYPE_VLAN,
500 : 0 : md->vlan_tci) != 0)
501 : 0 : goto fail;
502 : : }
503 : :
504 [ # # ]: 0 : if ((direction == RTE_PCAPNG_DIRECTION_IN &&
505 [ # # # # ]: 0 : (md->ol_flags & RTE_MBUF_F_RX_QINQ_STRIPPED)) ||
506 : 0 : (direction == RTE_PCAPNG_DIRECTION_OUT &&
507 [ # # ]: 0 : (md->ol_flags & RTE_MBUF_F_TX_QINQ))) {
508 [ # # ]: 0 : if (pcapng_vlan_insert(mc, RTE_ETHER_TYPE_QINQ,
509 : 0 : md->vlan_tci_outer) != 0)
510 : 0 : goto fail;
511 : : }
512 : :
513 : : /* record HASH on incoming packets */
514 [ # # ]: 0 : rss_hash = (direction == RTE_PCAPNG_DIRECTION_IN &&
515 [ # # ]: 0 : (md->ol_flags & RTE_MBUF_F_RX_RSS_HASH));
516 : :
517 : : /* pad the packet to 32 bit boundary */
518 : 0 : data_len = rte_pktmbuf_data_len(mc);
519 : 0 : padding = RTE_ALIGN(data_len, sizeof(uint32_t)) - data_len;
520 [ # # ]: 0 : if (padding > 0) {
521 : 0 : void *tail = rte_pktmbuf_append(mc, padding);
522 : :
523 [ # # ]: 0 : if (tail == NULL)
524 : 0 : goto fail;
525 : 0 : memset(tail, 0, padding);
526 : : }
527 : :
528 : : optlen = pcapng_optlen(sizeof(flags));
529 : : optlen += pcapng_optlen(sizeof(queue));
530 [ # # ]: 0 : if (rss_hash)
531 : : optlen += pcapng_optlen(sizeof(uint8_t) + sizeof(uint32_t));
532 : :
533 [ # # ]: 0 : if (comment)
534 : 0 : optlen += pcapng_optlen(strlen(comment));
535 : :
536 : : /* reserve trailing options and block length */
537 : : opt = (struct pcapng_option *)
538 : 0 : rte_pktmbuf_append(mc, optlen + sizeof(uint32_t));
539 [ # # ]: 0 : if (unlikely(opt == NULL))
540 : 0 : goto fail;
541 : :
542 [ # # # ]: 0 : switch (direction) {
543 : 0 : case RTE_PCAPNG_DIRECTION_IN:
544 : 0 : flags = PCAPNG_IFB_INBOUND;
545 : 0 : break;
546 : 0 : case RTE_PCAPNG_DIRECTION_OUT:
547 : 0 : flags = PCAPNG_IFB_OUTBOUND;
548 : 0 : break;
549 : 0 : default:
550 : 0 : flags = 0;
551 : : }
552 : :
553 : : opt = pcapng_add_option(opt, PCAPNG_EPB_FLAGS,
554 : : &flags, sizeof(flags));
555 : :
556 : : opt = pcapng_add_option(opt, PCAPNG_EPB_QUEUE,
557 : : &queue, sizeof(queue));
558 : :
559 [ # # ]: 0 : if (rss_hash) {
560 : : uint8_t hash_opt[5];
561 : :
562 : : /* The algorithm could be something else if
563 : : * using rte_flow_action_rss; but the current API does not
564 : : * have a way for ethdev to report this on a per-packet basis.
565 : : */
566 : 0 : hash_opt[0] = PCAPNG_HASH_TOEPLITZ;
567 : :
568 : : memcpy(&hash_opt[1], &md->hash.rss, sizeof(uint32_t));
569 : : opt = pcapng_add_option(opt, PCAPNG_EPB_HASH,
570 : : &hash_opt, sizeof(hash_opt));
571 : : }
572 : :
573 [ # # ]: 0 : if (comment)
574 : 0 : opt = pcapng_add_option(opt, PCAPNG_OPT_COMMENT, comment,
575 : 0 : strlen(comment));
576 : :
577 : : /* Note: END_OPT necessary here. Wireshark doesn't do it. */
578 : :
579 : : /* Add PCAPNG packet header */
580 : : epb = (struct pcapng_enhance_packet_block *)
581 : : rte_pktmbuf_prepend(mc, sizeof(*epb));
582 [ # # ]: 0 : if (unlikely(epb == NULL))
583 : 0 : goto fail;
584 : :
585 : 0 : epb->block_type = PCAPNG_ENHANCED_PACKET_BLOCK;
586 : 0 : epb->block_length = rte_pktmbuf_data_len(mc);
587 : :
588 : : /* Interface index is filled in later during write */
589 : 0 : mc->port = port_id;
590 : :
591 : : /* Put timestamp in cycles here - adjust in packet write */
592 : : timestamp = rte_get_tsc_cycles();
593 : 0 : epb->timestamp_hi = timestamp >> 32;
594 : 0 : epb->timestamp_lo = (uint32_t)timestamp;
595 : 0 : epb->capture_length = data_len;
596 : 0 : epb->original_length = orig_len;
597 : :
598 : : /* set trailer of block length */
599 : 0 : *(uint32_t *)opt = epb->block_length;
600 : :
601 : 0 : return mc;
602 : :
603 : 0 : fail:
604 : 0 : rte_pktmbuf_free(mc);
605 : 0 : return NULL;
606 : : }
607 : :
608 : : /* Write pre-formatted packets to file. */
609 : : ssize_t
610 : 0 : rte_pcapng_write_packets(rte_pcapng_t *self,
611 : : struct rte_mbuf *pkts[], uint16_t nb_pkts)
612 : : {
613 : : struct iovec iov[IOV_MAX];
614 : : unsigned int i, cnt = 0;
615 : : ssize_t ret, total = 0;
616 : :
617 [ # # ]: 0 : for (i = 0; i < nb_pkts; i++) {
618 : 0 : struct rte_mbuf *m = pkts[i];
619 : : struct pcapng_enhance_packet_block *epb;
620 : : uint64_t cycles, timestamp;
621 : :
622 : : /* sanity check that is really a pcapng mbuf */
623 : 0 : epb = rte_pktmbuf_mtod(m, struct pcapng_enhance_packet_block *);
624 [ # # # # ]: 0 : if (unlikely(epb->block_type != PCAPNG_ENHANCED_PACKET_BLOCK ||
625 : : epb->block_length != rte_pktmbuf_data_len(m))) {
626 : 0 : rte_errno = EINVAL;
627 : 0 : return -1;
628 : : }
629 : :
630 : : /* check that this interface was added. */
631 : 0 : epb->interface_id = self->port_index[m->port];
632 [ # # ]: 0 : if (unlikely(epb->interface_id > RTE_MAX_ETHPORTS)) {
633 : 0 : rte_errno = EINVAL;
634 : 0 : return -1;
635 : : }
636 : :
637 : : /* adjust timestamp recorded in packet */
638 : 0 : cycles = (uint64_t)epb->timestamp_hi << 32;
639 : 0 : cycles += epb->timestamp_lo;
640 : : timestamp = pcapng_timestamp(self, cycles);
641 : 0 : epb->timestamp_hi = timestamp >> 32;
642 : 0 : epb->timestamp_lo = (uint32_t)timestamp;
643 : :
644 : : /*
645 : : * Handle case of highly fragmented and large burst size
646 : : * Note: this assumes that max segments per mbuf < IOV_MAX
647 : : */
648 [ # # ]: 0 : if (unlikely(cnt + m->nb_segs >= IOV_MAX)) {
649 : 0 : ret = writev(self->outfd, iov, cnt);
650 [ # # ]: 0 : if (unlikely(ret < 0)) {
651 : 0 : rte_errno = errno;
652 : 0 : return -1;
653 : : }
654 : 0 : total += ret;
655 : : cnt = 0;
656 : : }
657 : :
658 : : /*
659 : : * The DPDK port is recorded during pcapng_copy.
660 : : * Map that to PCAPNG interface in file.
661 : : */
662 : : do {
663 : 0 : iov[cnt].iov_base = rte_pktmbuf_mtod(m, void *);
664 : 0 : iov[cnt].iov_len = rte_pktmbuf_data_len(m);
665 : 0 : ++cnt;
666 [ # # ]: 0 : } while ((m = m->next));
667 : : }
668 : :
669 : 0 : ret = writev(self->outfd, iov, cnt);
670 [ # # ]: 0 : if (unlikely(ret < 0)) {
671 : 0 : rte_errno = errno;
672 : 0 : return -1;
673 : : }
674 : 0 : return total + ret;
675 : : }
676 : :
677 : : /* Create new pcapng writer handle */
678 : : rte_pcapng_t *
679 : 0 : rte_pcapng_fdopen(int fd,
680 : : const char *osname, const char *hardware,
681 : : const char *appname, const char *comment)
682 : : {
683 : : unsigned int i;
684 : : rte_pcapng_t *self;
685 : : struct timespec ts;
686 : : uint64_t cycles;
687 : :
688 : 0 : self = malloc(sizeof(*self));
689 [ # # ]: 0 : if (!self) {
690 : 0 : rte_errno = ENOMEM;
691 : 0 : return NULL;
692 : : }
693 : :
694 : 0 : self->outfd = fd;
695 : 0 : self->ports = 0;
696 : :
697 : : /* record start time in ns since 1/1/1970 */
698 : : cycles = rte_get_tsc_cycles();
699 : 0 : clock_gettime(CLOCK_REALTIME, &ts);
700 : 0 : self->tsc_base = (cycles + rte_get_tsc_cycles()) / 2;
701 : 0 : self->offset_ns = rte_timespec_to_ns(&ts);
702 : :
703 [ # # ]: 0 : for (i = 0; i < RTE_MAX_ETHPORTS; i++)
704 : 0 : self->port_index[i] = UINT32_MAX;
705 : :
706 [ # # ]: 0 : if (pcapng_section_block(self, osname, hardware, appname, comment) < 0)
707 : 0 : goto fail;
708 : :
709 : : return self;
710 : : fail:
711 : 0 : free(self);
712 : 0 : return NULL;
713 : : }
714 : :
715 : : void
716 : 0 : rte_pcapng_close(rte_pcapng_t *self)
717 : : {
718 : 0 : close(self->outfd);
719 : 0 : free(self);
720 : 0 : }
|