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