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