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 : 4117 : delta = cycles - self->tsc_base;
109 : :
110 : : /* Avoid numeric wraparound by computing seconds first */
111 : 4117 : secs = delta / hz;
112 : 4117 : rem = delta % hz;
113 : 4117 : ns = (rem * NS_PER_S) / hz;
114 : :
115 : 4117 : 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 : 4142 : popt->code = code;
131 : 4142 : popt->length = len;
132 : 12 : if (len > 0)
133 [ - + ]: 4130 : 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, uint16_t link_type,
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 = link_type,
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 : 1 : const size_t filter_len = strlen(filter) + 1;
300 : :
301 : 1 : opt->code = PCAPNG_IFB_FILTER;
302 : 1 : opt->length = filter_len;
303 : : /* Encoding is that the first octet indicates string vs BPF */
304 : 1 : opt->data[0] = 0;
305 : 1 : memcpy(opt->data + 1, filter, strlen(filter));
306 : :
307 : 1 : opt = (struct pcapng_option *)((uint8_t *)opt + pcapng_optlen(filter_len));
308 : : }
309 : :
310 : : opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0);
311 : :
312 : : /* clone block_length after optionsa */
313 : : memcpy(opt, &hdr->block_length, sizeof(uint32_t));
314 : :
315 : : /* remember the file index */
316 : 4 : self->port_index[port] = self->ports++;
317 : :
318 : 4 : return write(self->outfd, buf, len);
319 : : }
320 : :
321 : : /*
322 : : * Write an Interface statistics block at the end of capture.
323 : : */
324 : : RTE_EXPORT_SYMBOL(rte_pcapng_write_stats)
325 : : ssize_t
326 : 1 : rte_pcapng_write_stats(rte_pcapng_t *self, uint16_t port_id,
327 : : uint64_t ifrecv, uint64_t ifdrop,
328 : : const char *comment)
329 : : {
330 : : struct pcapng_statistics *hdr;
331 : : struct pcapng_option *opt;
332 : 1 : uint64_t start_time = self->offset_ns;
333 : : uint64_t sample_time;
334 : : uint32_t optlen, len;
335 : : uint32_t buf[PCAPNG_BLKSIZ];
336 : :
337 [ - + ]: 1 : RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL);
338 : :
339 : : optlen = 0;
340 : :
341 [ + - ]: 1 : if (ifrecv != UINT64_MAX)
342 : : optlen += pcapng_optlen(sizeof(ifrecv));
343 [ + - ]: 1 : if (ifdrop != UINT64_MAX)
344 : 1 : optlen += pcapng_optlen(sizeof(ifdrop));
345 : :
346 [ + - ]: 1 : if (start_time != 0)
347 : 1 : optlen += pcapng_optlen(sizeof(start_time));
348 : :
349 [ + - ]: 1 : if (comment)
350 : 1 : optlen += pcapng_optlen(strlen(comment));
351 [ + - ]: 1 : if (optlen != 0)
352 : 1 : optlen += pcapng_optlen(0);
353 : :
354 : 1 : len = sizeof(*hdr) + optlen + sizeof(uint32_t);
355 [ + - ]: 1 : if (len > sizeof(buf))
356 : : return -1;
357 : :
358 : : hdr = (struct pcapng_statistics *)buf;
359 : : opt = (struct pcapng_option *)(hdr + 1);
360 : :
361 [ + - ]: 1 : if (comment)
362 : 1 : opt = pcapng_add_option(opt, PCAPNG_OPT_COMMENT,
363 [ + - ]: 1 : comment, strlen(comment));
364 [ + - ]: 1 : if (start_time != 0)
365 : : opt = pcapng_add_option(opt, PCAPNG_ISB_STARTTIME,
366 : : &start_time, sizeof(start_time));
367 [ + - ]: 1 : if (ifrecv != UINT64_MAX)
368 : : opt = pcapng_add_option(opt, PCAPNG_ISB_IFRECV,
369 : : &ifrecv, sizeof(ifrecv));
370 [ + - ]: 1 : if (ifdrop != UINT64_MAX)
371 : : opt = pcapng_add_option(opt, PCAPNG_ISB_IFDROP,
372 : : &ifdrop, sizeof(ifdrop));
373 [ + - ]: 1 : if (optlen != 0)
374 : : opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0);
375 : :
376 : 1 : hdr->block_type = PCAPNG_INTERFACE_STATS_BLOCK;
377 : 1 : hdr->block_length = len;
378 : 1 : hdr->interface_id = self->port_index[port_id];
379 : :
380 : : sample_time = pcapng_timestamp(self, rte_get_tsc_cycles());
381 : 1 : hdr->timestamp_hi = sample_time >> 32;
382 : 1 : hdr->timestamp_lo = (uint32_t)sample_time;
383 : :
384 : : /* clone block_length after option */
385 : : memcpy(opt, &len, sizeof(uint32_t));
386 : :
387 : 1 : return write(self->outfd, buf, len);
388 : : }
389 : :
390 : : RTE_EXPORT_SYMBOL(rte_pcapng_mbuf_size)
391 : : uint32_t
392 : 1 : rte_pcapng_mbuf_size(uint32_t length)
393 : : {
394 : : /* The VLAN and EPB header must fit in the mbuf headroom. */
395 : : RTE_ASSERT(sizeof(struct pcapng_enhance_packet_block) +
396 : : sizeof(struct rte_vlan_hdr) <= RTE_PKTMBUF_HEADROOM);
397 : :
398 : : /* The flags and queue information are added at the end. */
399 : : return sizeof(struct rte_mbuf)
400 : 1 : + RTE_ALIGN(length, sizeof(uint32_t))
401 : : + pcapng_optlen(sizeof(uint32_t)) /* flag option */
402 : : + pcapng_optlen(sizeof(uint32_t)) /* queue option */
403 : 1 : + sizeof(uint32_t); /* length */
404 : : }
405 : :
406 : : /* More generalized version rte_vlan_insert() */
407 : : static int
408 : 0 : pcapng_vlan_insert(struct rte_mbuf *m, uint16_t ether_type, uint16_t tci)
409 : : {
410 : : struct rte_ether_hdr *nh, *oh;
411 : : struct rte_vlan_hdr *vh;
412 : :
413 [ # # # # ]: 0 : if (!RTE_MBUF_DIRECT(m) || rte_mbuf_refcnt_read(m) > 1)
414 : 0 : return -EINVAL;
415 : :
416 [ # # ]: 0 : if (rte_pktmbuf_data_len(m) < sizeof(*oh))
417 : : return -EINVAL;
418 : :
419 [ # # ]: 0 : oh = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
420 : : nh = (struct rte_ether_hdr *)
421 : : rte_pktmbuf_prepend(m, sizeof(struct rte_vlan_hdr));
422 [ # # ]: 0 : if (nh == NULL)
423 : 0 : return -ENOSPC;
424 : :
425 : : memmove(nh, oh, 2 * RTE_ETHER_ADDR_LEN);
426 [ # # ]: 0 : nh->ether_type = rte_cpu_to_be_16(ether_type);
427 : :
428 : : vh = (struct rte_vlan_hdr *) (nh + 1);
429 [ # # ]: 0 : vh->vlan_tci = rte_cpu_to_be_16(tci);
430 : :
431 : 0 : return 0;
432 : : }
433 : :
434 : : /*
435 : : * The mbufs created use the Pcapng standard enhanced packet block.
436 : : *
437 : : * 1 2 3
438 : : * 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
439 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
440 : : * 0 | Block Type = 0x00000006 |
441 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
442 : : * 4 | Block Total Length |
443 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
444 : : * 8 | Interface ID |
445 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
446 : : * 12 | Timestamp (High) |
447 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
448 : : * 16 | Timestamp (Low) |
449 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
450 : : * 20 | Captured Packet Length |
451 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
452 : : * 24 | Original Packet Length |
453 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
454 : : * 28 / /
455 : : * / Packet Data /
456 : : * / variable length, padded to 32 bits /
457 : : * / /
458 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
459 : : * | Option Code = 0x0002 | Option Length = 0x004 |
460 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
461 : : * | Flags (direction) |
462 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
463 : : * | Option Code = 0x0006 | Option Length = 0x002 |
464 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
465 : : * | Queue id |
466 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
467 : : * | Block Total Length |
468 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
469 : : */
470 : :
471 : : /* Make a copy of original mbuf with pcapng header and options */
472 : : RTE_EXPORT_SYMBOL(rte_pcapng_copy)
473 : : struct rte_mbuf *
474 : 4116 : rte_pcapng_copy(uint16_t port_id, uint32_t queue,
475 : : const struct rte_mbuf *md,
476 : : struct rte_mempool *mp,
477 : : uint32_t length,
478 : : enum rte_pcapng_direction direction,
479 : : const char *comment)
480 : : {
481 : : struct pcapng_enhance_packet_block *epb;
482 : : uint32_t orig_len, pkt_len, padding, flags;
483 : : struct pcapng_option *opt;
484 : : uint64_t timestamp;
485 : : uint16_t optlen;
486 : : struct rte_mbuf *mc;
487 : : bool rss_hash;
488 : :
489 : : #ifdef RTE_LIBRTE_ETHDEV_DEBUG
490 : : RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, NULL);
491 : : #endif
492 : 4116 : orig_len = rte_pktmbuf_pkt_len(md);
493 : :
494 : : /* Take snapshot of the data */
495 : 4116 : mc = rte_pktmbuf_copy(md, mp, 0, length);
496 [ + - ]: 4116 : if (unlikely(mc == NULL))
497 : : return NULL;
498 : :
499 : : /* Expand any offloaded VLAN information */
500 [ + - ]: 4116 : if ((direction == RTE_PCAPNG_DIRECTION_IN &&
501 [ + - - + ]: 4116 : (md->ol_flags & RTE_MBUF_F_RX_VLAN_STRIPPED)) ||
502 : 0 : (direction == RTE_PCAPNG_DIRECTION_OUT &&
503 [ # # ]: 0 : (md->ol_flags & RTE_MBUF_F_TX_VLAN))) {
504 [ # # ]: 0 : if (pcapng_vlan_insert(mc, RTE_ETHER_TYPE_VLAN,
505 : 0 : md->vlan_tci) != 0)
506 : 0 : goto fail;
507 : : }
508 : :
509 [ + - ]: 4116 : if ((direction == RTE_PCAPNG_DIRECTION_IN &&
510 [ + - - + ]: 4116 : (md->ol_flags & RTE_MBUF_F_RX_QINQ_STRIPPED)) ||
511 : 0 : (direction == RTE_PCAPNG_DIRECTION_OUT &&
512 [ # # ]: 0 : (md->ol_flags & RTE_MBUF_F_TX_QINQ))) {
513 [ # # ]: 0 : if (pcapng_vlan_insert(mc, RTE_ETHER_TYPE_QINQ,
514 : 0 : md->vlan_tci_outer) != 0)
515 : 0 : goto fail;
516 : : }
517 : :
518 : : /* record HASH on incoming packets */
519 [ + - ]: 4116 : rss_hash = (direction == RTE_PCAPNG_DIRECTION_IN &&
520 [ + - ]: 4116 : (md->ol_flags & RTE_MBUF_F_RX_RSS_HASH));
521 : :
522 : : /* pad the packet to 32 bit boundary */
523 : 4116 : pkt_len = rte_pktmbuf_pkt_len(mc);
524 : 4116 : padding = RTE_ALIGN(pkt_len, sizeof(uint32_t)) - pkt_len;
525 [ - + ]: 4116 : if (padding > 0) {
526 : 0 : void *tail = rte_pktmbuf_append(mc, padding);
527 : :
528 [ # # ]: 0 : if (tail == NULL)
529 : 0 : goto fail;
530 : 0 : memset(tail, 0, padding);
531 : : }
532 : :
533 : : optlen = pcapng_optlen(sizeof(flags));
534 : : optlen += pcapng_optlen(sizeof(queue));
535 [ - + ]: 4116 : if (rss_hash)
536 : : optlen += pcapng_optlen(sizeof(uint8_t) + sizeof(uint32_t));
537 : :
538 [ - + ]: 4116 : if (comment)
539 : 0 : optlen += pcapng_optlen(strlen(comment));
540 : :
541 : : /* reserve trailing options and block length */
542 : : opt = (struct pcapng_option *)
543 : 4116 : rte_pktmbuf_append(mc, optlen + sizeof(uint32_t));
544 [ - + ]: 4116 : if (unlikely(opt == NULL))
545 : 0 : goto fail;
546 : :
547 [ + - - ]: 4116 : switch (direction) {
548 : 4116 : case RTE_PCAPNG_DIRECTION_IN:
549 : 4116 : flags = PCAPNG_IFB_INBOUND;
550 : 4116 : break;
551 : 0 : case RTE_PCAPNG_DIRECTION_OUT:
552 : 0 : flags = PCAPNG_IFB_OUTBOUND;
553 : 0 : break;
554 : 0 : default:
555 : 0 : flags = 0;
556 : : }
557 : :
558 : : opt = pcapng_add_option(opt, PCAPNG_EPB_FLAGS,
559 : : &flags, sizeof(flags));
560 : :
561 : : opt = pcapng_add_option(opt, PCAPNG_EPB_QUEUE,
562 : : &queue, sizeof(queue));
563 : :
564 [ - + ]: 4116 : if (rss_hash) {
565 : : uint8_t hash_opt[5];
566 : :
567 : : /* The algorithm could be something else if
568 : : * using rte_flow_action_rss; but the current API does not
569 : : * have a way for ethdev to report this on a per-packet basis.
570 : : */
571 : 0 : hash_opt[0] = PCAPNG_HASH_TOEPLITZ;
572 : :
573 : : memcpy(&hash_opt[1], &md->hash.rss, sizeof(uint32_t));
574 : : opt = pcapng_add_option(opt, PCAPNG_EPB_HASH,
575 : : &hash_opt, sizeof(hash_opt));
576 : : }
577 : :
578 [ - + ]: 4116 : if (comment)
579 : 0 : opt = pcapng_add_option(opt, PCAPNG_OPT_COMMENT, comment,
580 [ # # ]: 0 : strlen(comment));
581 : :
582 : : /* Note: END_OPT necessary here. Wireshark doesn't do it. */
583 : :
584 : : /* Add PCAPNG packet header */
585 : : epb = (struct pcapng_enhance_packet_block *)
586 : : rte_pktmbuf_prepend(mc, sizeof(*epb));
587 [ - + ]: 4116 : if (unlikely(epb == NULL))
588 : 0 : goto fail;
589 : :
590 : 4116 : epb->block_type = PCAPNG_ENHANCED_PACKET_BLOCK;
591 : 4116 : epb->block_length = rte_pktmbuf_pkt_len(mc);
592 : :
593 : : /* Interface index is filled in later during write */
594 : 4116 : mc->port = port_id;
595 : :
596 : : /* Put timestamp in cycles here - adjust in packet write */
597 : : timestamp = rte_get_tsc_cycles();
598 : 4116 : epb->timestamp_hi = timestamp >> 32;
599 : 4116 : epb->timestamp_lo = (uint32_t)timestamp;
600 : 4116 : epb->capture_length = pkt_len;
601 : 4116 : epb->original_length = orig_len;
602 : :
603 : : /* set trailer of block length */
604 : 4116 : *(uint32_t *)opt = epb->block_length;
605 : :
606 : 4116 : return mc;
607 : :
608 : 0 : fail:
609 : 0 : rte_pktmbuf_free(mc);
610 : 0 : return NULL;
611 : : }
612 : :
613 : : /* Write pre-formatted packets to file. */
614 : : RTE_EXPORT_SYMBOL(rte_pcapng_write_packets)
615 : : ssize_t
616 : 122 : rte_pcapng_write_packets(rte_pcapng_t *self,
617 : : struct rte_mbuf *pkts[], uint16_t nb_pkts)
618 : : {
619 : : struct iovec iov[IOV_MAX];
620 : : unsigned int i, cnt = 0;
621 : : ssize_t ret, total = 0;
622 : :
623 [ + + ]: 4238 : for (i = 0; i < nb_pkts; i++) {
624 : 4116 : struct rte_mbuf *m = pkts[i];
625 : : struct pcapng_enhance_packet_block *epb;
626 : : uint64_t cycles, timestamp;
627 : :
628 : : /* sanity check that is really a pcapng mbuf */
629 : 4116 : epb = rte_pktmbuf_mtod(m, struct pcapng_enhance_packet_block *);
630 [ + - - + ]: 4116 : if (unlikely(epb->block_type != PCAPNG_ENHANCED_PACKET_BLOCK ||
631 : : epb->block_length != rte_pktmbuf_pkt_len(m))) {
632 : 0 : rte_errno = EINVAL;
633 : 0 : return -1;
634 : : }
635 : :
636 : : /* check that this interface was added. */
637 : 4116 : epb->interface_id = self->port_index[m->port];
638 [ - + ]: 4116 : if (unlikely(epb->interface_id > RTE_MAX_ETHPORTS)) {
639 : 0 : rte_errno = EINVAL;
640 : 0 : return -1;
641 : : }
642 : :
643 : : /* adjust timestamp recorded in packet */
644 : 4116 : cycles = (uint64_t)epb->timestamp_hi << 32;
645 : 4116 : cycles += epb->timestamp_lo;
646 : : timestamp = pcapng_timestamp(self, cycles);
647 : 4116 : epb->timestamp_hi = timestamp >> 32;
648 : 4116 : epb->timestamp_lo = (uint32_t)timestamp;
649 : :
650 : : /*
651 : : * Handle case of highly fragmented and large burst size
652 : : * Note: this assumes that max segments per mbuf < IOV_MAX
653 : : */
654 [ - + ]: 4116 : if (unlikely(cnt + m->nb_segs >= IOV_MAX)) {
655 : 0 : ret = writev(self->outfd, iov, cnt);
656 [ # # ]: 0 : if (unlikely(ret < 0)) {
657 : 0 : rte_errno = errno;
658 : 0 : return -1;
659 : : }
660 : 0 : total += ret;
661 : : cnt = 0;
662 : : }
663 : :
664 : : /*
665 : : * The DPDK port is recorded during pcapng_copy.
666 : : * Map that to PCAPNG interface in file.
667 : : */
668 : : do {
669 : 8232 : iov[cnt].iov_base = rte_pktmbuf_mtod(m, void *);
670 : 8232 : iov[cnt].iov_len = rte_pktmbuf_data_len(m);
671 : 8232 : ++cnt;
672 [ + + ]: 8232 : } while ((m = m->next));
673 : : }
674 : :
675 : 122 : ret = writev(self->outfd, iov, cnt);
676 [ - + ]: 122 : if (unlikely(ret < 0)) {
677 : 0 : rte_errno = errno;
678 : 0 : return -1;
679 : : }
680 : 122 : return total + ret;
681 : : }
682 : :
683 : : /* Create new pcapng writer handle */
684 : : RTE_EXPORT_SYMBOL(rte_pcapng_fdopen)
685 : : rte_pcapng_t *
686 : 2 : rte_pcapng_fdopen(int fd,
687 : : const char *osname, const char *hardware,
688 : : const char *appname, const char *comment)
689 : : {
690 : : unsigned int i;
691 : : rte_pcapng_t *self;
692 : : struct timespec ts;
693 : : uint64_t cycles;
694 : :
695 : 2 : self = malloc(sizeof(*self));
696 [ - + ]: 2 : if (!self) {
697 : 0 : rte_errno = ENOMEM;
698 : 0 : return NULL;
699 : : }
700 : :
701 : 2 : self->outfd = fd;
702 : 2 : self->ports = 0;
703 : :
704 : : /* record start time in ns since 1/1/1970 */
705 : : cycles = rte_get_tsc_cycles();
706 : 2 : clock_gettime(CLOCK_REALTIME, &ts);
707 : 2 : self->tsc_base = (cycles + rte_get_tsc_cycles()) / 2;
708 : 2 : self->offset_ns = rte_timespec_to_ns(&ts);
709 : :
710 [ + + ]: 66 : for (i = 0; i < RTE_MAX_ETHPORTS; i++)
711 : 64 : self->port_index[i] = UINT32_MAX;
712 : :
713 [ - + ]: 2 : if (pcapng_section_block(self, osname, hardware, appname, comment) < 0)
714 : 0 : goto fail;
715 : :
716 : : return self;
717 : : fail:
718 : 0 : free(self);
719 : 0 : return NULL;
720 : : }
721 : :
722 : : RTE_EXPORT_SYMBOL(rte_pcapng_close)
723 : : void
724 : 2 : rte_pcapng_close(rte_pcapng_t *self)
725 : : {
726 [ + - ]: 2 : if (self) {
727 : 2 : close(self->outfd);
728 : 2 : free(self);
729 : : }
730 : 2 : }
|