Line data Source code
1 : /* SPDX-License-Identifier: Apache-2.0
2 : * Copyright(c) 2021 Cisco Systems, Inc.
3 : */
4 :
5 : #include <vnet/plugin/plugin.h>
6 : #include <vpp/app/version.h>
7 :
8 : typedef struct
9 : {
10 : u8 *pcap_buffer;
11 : } dispatch_trace_thread_t;
12 :
13 : typedef struct
14 : {
15 : u32 enable : 1;
16 : pcap_main_t dispatch_pcap_main;
17 : u32 *dispatch_buffer_trace_nodes;
18 : dispatch_trace_thread_t *threads;
19 : u32 epoll_input_node_index;
20 : } dispatch_trace_main_t;
21 :
22 : dispatch_trace_main_t dispatch_trace_main;
23 :
24 : #define VLIB_PCAP_MAJOR_VERSION 1
25 : #define VLIB_PCAP_MINOR_VERSION 0
26 :
27 : typedef struct
28 : {
29 : u8 *filename;
30 : int enable;
31 : int status;
32 : int post_mortem;
33 : u32 packets_to_capture;
34 : u32 buffer_trace_node_index;
35 : u32 buffer_traces_to_capture;
36 : } vlib_pcap_dispatch_trace_args_t;
37 :
38 : static u8 *
39 100 : format_buffer_metadata (u8 *s, va_list *args)
40 : {
41 100 : vlib_buffer_t *b = va_arg (*args, vlib_buffer_t *);
42 :
43 100 : s = format (s, "flags: %U\n", format_vnet_buffer_flags, b);
44 100 : s = format (s, "current_data: %d, current_length: %d\n",
45 100 : (i32) (b->current_data), (i32) (b->current_length));
46 100 : s = format (
47 : s, "current_config_index/punt_reason: %d, flow_id: %x, next_buffer: %x\n",
48 : b->current_config_index, b->flow_id, b->next_buffer);
49 100 : s = format (s, "error: %d, ref_count: %d, buffer_pool_index: %d\n",
50 100 : (u32) (b->error), (u32) (b->ref_count),
51 100 : (u32) (b->buffer_pool_index));
52 100 : s = format (s, "trace_handle: 0x%x, len_not_first_buf: %d\n",
53 : b->trace_handle, b->total_length_not_including_first_buffer);
54 100 : return s;
55 : }
56 :
57 : #define A(x) vec_add1 (dtt->pcap_buffer, (x))
58 :
59 : uword
60 33 : dispatch_pcap_trace (vlib_main_t *vm, vlib_node_runtime_t *node,
61 : vlib_frame_t *frame)
62 : {
63 : int i;
64 : vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **bufp, *b;
65 33 : dispatch_trace_main_t *dtm = &dispatch_trace_main;
66 33 : pcap_main_t *pm = &dtm->dispatch_pcap_main;
67 33 : dispatch_trace_thread_t *dtt =
68 33 : vec_elt_at_index (dtm->threads, vm->thread_index);
69 33 : vlib_trace_main_t *tm = &vm->trace_main;
70 : u32 capture_size;
71 : vlib_node_t *n;
72 : i32 n_left;
73 33 : f64 time_now = vlib_time_now (vm);
74 : u32 *from;
75 : u8 *d;
76 : u8 string_count;
77 :
78 : /* Input nodes don't have frames yet */
79 33 : if (frame == 0 || frame->n_vectors == 0)
80 5 : goto done;
81 :
82 28 : from = vlib_frame_vector_args (frame);
83 28 : vlib_get_buffers (vm, from, bufs, frame->n_vectors);
84 28 : bufp = bufs;
85 :
86 28 : n = vlib_get_node (vm, node->node_index);
87 :
88 200 : for (i = 0; i < frame->n_vectors; i++)
89 : {
90 172 : if (PREDICT_TRUE (pm->n_packets_captured < pm->n_packets_to_capture))
91 : {
92 100 : b = bufp[i];
93 :
94 100 : vec_reset_length (dtt->pcap_buffer);
95 100 : string_count = 0;
96 :
97 : /* Version, flags */
98 100 : A ((u8) VLIB_PCAP_MAJOR_VERSION);
99 100 : A ((u8) VLIB_PCAP_MINOR_VERSION);
100 100 : A (0 /* string_count */);
101 100 : A (n->protocol_hint);
102 :
103 : /* Buffer index (big endian) */
104 100 : A ((from[i] >> 24) & 0xff);
105 100 : A ((from[i] >> 16) & 0xff);
106 100 : A ((from[i] >> 8) & 0xff);
107 100 : A ((from[i] >> 0) & 0xff);
108 :
109 : /* Node name, NULL-terminated ASCII */
110 100 : dtt->pcap_buffer = format (dtt->pcap_buffer, "%v%c", n->name, 0);
111 100 : string_count++;
112 :
113 100 : dtt->pcap_buffer =
114 100 : format (dtt->pcap_buffer, "%U%c", format_buffer_metadata, b, 0);
115 100 : string_count++;
116 100 : dtt->pcap_buffer =
117 100 : format (dtt->pcap_buffer, "%U%c", format_vnet_buffer_opaque, b, 0);
118 100 : string_count++;
119 100 : dtt->pcap_buffer = format (dtt->pcap_buffer, "%U%c",
120 : format_vnet_buffer_opaque2, b, 0);
121 100 : string_count++;
122 :
123 : /* Is this packet traced? */
124 100 : if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED))
125 : {
126 100 : vlib_trace_header_t **h = pool_elt_at_index (
127 : tm->trace_buffer_pool, vlib_buffer_get_trace_index (b));
128 :
129 100 : dtt->pcap_buffer = format (dtt->pcap_buffer, "%U%c",
130 : format_vlib_trace, vm, h[0], 0);
131 100 : string_count++;
132 : }
133 :
134 : /* Save the string count */
135 100 : dtt->pcap_buffer[2] = string_count;
136 :
137 : /* Figure out how many bytes in the pcap trace */
138 100 : capture_size =
139 100 : vec_len (dtt->pcap_buffer) + +vlib_buffer_length_in_chain (vm, b);
140 :
141 100 : clib_spinlock_lock_if_init (&pm->lock);
142 100 : n_left = clib_min (capture_size, 16384);
143 100 : d = pcap_add_packet (pm, time_now, n_left, capture_size);
144 :
145 : /* Copy the header */
146 100 : clib_memcpy_fast (d, dtt->pcap_buffer, vec_len (dtt->pcap_buffer));
147 100 : d += vec_len (dtt->pcap_buffer);
148 :
149 100 : n_left = clib_min (vlib_buffer_length_in_chain (vm, b),
150 : (16384 - vec_len (dtt->pcap_buffer)));
151 : /* Copy the packet data */
152 : while (1)
153 0 : {
154 100 : u32 copy_length = clib_min ((u32) n_left, b->current_length);
155 100 : clib_memcpy_fast (d, b->data + b->current_data, copy_length);
156 100 : n_left -= b->current_length;
157 100 : if (n_left <= 0)
158 100 : break;
159 0 : d += b->current_length;
160 0 : ASSERT (b->flags & VLIB_BUFFER_NEXT_PRESENT);
161 0 : b = vlib_get_buffer (vm, b->next_buffer);
162 : }
163 100 : clib_spinlock_unlock_if_init (&pm->lock);
164 : }
165 : }
166 28 : done:
167 33 : return node->function (vm, node, frame);
168 : }
169 :
170 : static void
171 0 : pcap_postmortem_reset (vlib_main_t *vm)
172 : {
173 0 : dispatch_trace_main_t *dtm = &dispatch_trace_main;
174 0 : pcap_main_t *pm = &dtm->dispatch_pcap_main;
175 :
176 : /* Reset the trace buffer and capture count */
177 0 : clib_spinlock_lock_if_init (&pm->lock);
178 0 : vec_reset_length (pm->pcap_data);
179 0 : pm->n_packets_captured = 0;
180 0 : if (vec_len (vlib_worker_threads) == 1 && dtm->epoll_input_node_index)
181 : {
182 : vlib_node_runtime_t *epoll_input_rt =
183 0 : vlib_node_get_runtime (vm, dtm->epoll_input_node_index);
184 0 : epoll_input_rt->input_main_loops_per_call = 0;
185 : }
186 0 : clib_spinlock_unlock_if_init (&pm->lock);
187 0 : }
188 :
189 : static void
190 0 : pcap_postmortem_dump (void)
191 : {
192 0 : dispatch_trace_main_t *dtm = &dispatch_trace_main;
193 0 : pcap_main_t *pm = &dtm->dispatch_pcap_main;
194 : clib_error_t *error;
195 :
196 0 : pm->n_packets_to_capture = pm->n_packets_captured;
197 0 : pm->file_name =
198 0 : (char *) format (0, "/tmp/dispatch_post_mortem.%d%c", getpid (), 0);
199 0 : error = pcap_write (pm);
200 0 : pcap_close (pm);
201 0 : if (error)
202 0 : clib_error_report (error);
203 : /*
204 : * We're in the middle of crashing. Don't try to free the filename.
205 : */
206 0 : }
207 : static int
208 2 : vlib_pcap_dispatch_trace_configure (vlib_pcap_dispatch_trace_args_t *a)
209 : {
210 2 : vlib_main_t *vm = vlib_get_main ();
211 2 : vlib_thread_main_t *vtm = vlib_get_thread_main ();
212 2 : dispatch_trace_main_t *dtm = &dispatch_trace_main;
213 2 : pcap_main_t *pm = &dtm->dispatch_pcap_main;
214 : vlib_trace_main_t *tm;
215 : vlib_trace_node_t *tn;
216 :
217 2 : vec_validate (dtm->threads, vtm->n_vlib_mains);
218 :
219 2 : if (a->status)
220 : {
221 0 : if (dtm->enable)
222 : {
223 : int i;
224 0 : vlib_cli_output (vm,
225 : "pcap dispatch capture enabled: %d of %d pkts...",
226 : pm->n_packets_captured, pm->n_packets_to_capture);
227 0 : vlib_cli_output (vm, "capture to file %s", pm->file_name);
228 :
229 0 : for (i = 0; i < vec_len (dtm->dispatch_buffer_trace_nodes); i++)
230 : {
231 0 : vlib_cli_output (
232 : vm, "Buffer trace of %d pkts from %U enabled...",
233 : a->buffer_traces_to_capture, format_vlib_node_name, vm,
234 0 : dtm->dispatch_buffer_trace_nodes[i]);
235 : }
236 : }
237 : else
238 0 : vlib_cli_output (vm, "pcap dispatch capture disabled");
239 0 : return 0;
240 : }
241 :
242 : /* Consistency checks */
243 :
244 : /* Enable w/ capture already enabled not allowed */
245 2 : if (dtm->enable && a->enable)
246 0 : return VNET_API_ERROR_INVALID_VALUE;
247 :
248 : /* Disable capture with capture already disabled, not interesting */
249 2 : if (dtm->enable == 0 && a->enable == 0)
250 0 : return VNET_API_ERROR_VALUE_EXIST;
251 :
252 : /* Change number of packets to capture while capturing */
253 2 : if (dtm->enable && a->enable &&
254 0 : (pm->n_packets_to_capture != a->packets_to_capture))
255 0 : return VNET_API_ERROR_INVALID_VALUE_2;
256 :
257 : /* Independent of enable/disable, to allow buffer trace multi nodes */
258 2 : if (a->buffer_trace_node_index != ~0)
259 : {
260 2 : foreach_vlib_main ()
261 : {
262 1 : tm = &this_vlib_main->trace_main;
263 1 : tm->verbose = 0; /* not sure this ever did anything... */
264 1 : vec_validate (tm->nodes, a->buffer_trace_node_index);
265 1 : tn = tm->nodes + a->buffer_trace_node_index;
266 1 : tn->limit += a->buffer_traces_to_capture;
267 1 : if (a->post_mortem)
268 : {
269 0 : tm->filter_flag = FILTER_FLAG_POST_MORTEM;
270 0 : tm->filter_count = ~0;
271 : }
272 1 : tm->trace_enable = 1;
273 1 : if (vlib_node_set_dispatch_wrapper (this_vlib_main,
274 : dispatch_pcap_trace))
275 0 : clib_warning (0, "Dispatch wrapper already in use on thread %u",
276 : this_vlib_main->thread_index);
277 : }
278 1 : vec_add1 (dtm->dispatch_buffer_trace_nodes, a->buffer_trace_node_index);
279 : }
280 :
281 2 : if (a->enable)
282 : {
283 : /* Clean up from previous run, if any */
284 1 : vec_free (pm->file_name);
285 1 : vec_free (pm->pcap_data);
286 1 : memset (pm, 0, sizeof (*pm));
287 :
288 1 : vec_validate_aligned (vnet_trace_placeholder, 2048,
289 : CLIB_CACHE_LINE_BYTES);
290 1 : if (pm->lock == 0)
291 1 : clib_spinlock_init (&(pm->lock));
292 :
293 1 : if (a->filename == 0)
294 1 : a->filename = format (0, "/tmp/dispatch.pcap%c", 0);
295 :
296 1 : pm->file_name = (char *) a->filename;
297 1 : pm->n_packets_captured = 0;
298 1 : pm->packet_type = PCAP_PACKET_TYPE_vpp;
299 1 : pm->n_packets_to_capture = a->packets_to_capture;
300 1 : dtm->enable = 1;
301 : }
302 : else
303 : {
304 1 : dtm->enable = 0;
305 2 : foreach_vlib_main ()
306 : {
307 1 : tm = &this_vlib_main->trace_main;
308 1 : tm->filter_flag = 0;
309 1 : tm->filter_count = 0;
310 1 : vlib_node_set_dispatch_wrapper (this_vlib_main, 0);
311 : }
312 1 : vec_reset_length (dtm->dispatch_buffer_trace_nodes);
313 1 : if (pm->n_packets_captured)
314 : {
315 : clib_error_t *error;
316 1 : pm->n_packets_to_capture = pm->n_packets_captured;
317 1 : vlib_cli_output (vm, "Write %d packets to %s, and stop capture...",
318 : pm->n_packets_captured, pm->file_name);
319 1 : error = pcap_write (pm);
320 1 : if (pm->flags & PCAP_MAIN_INIT_DONE)
321 1 : pcap_close (pm);
322 : /* Report I/O errors... */
323 1 : if (error)
324 : {
325 0 : clib_error_report (error);
326 0 : return VNET_API_ERROR_SYSCALL_ERROR_1;
327 : }
328 1 : return 0;
329 : }
330 : else
331 0 : return VNET_API_ERROR_NO_SUCH_ENTRY;
332 : }
333 :
334 : vlib_node_t *epoll_input_node =
335 1 : vlib_get_node_by_name (vm, (u8 *) "unix-epoll-input");
336 :
337 : /* Save the input node index, see the post-mortem callback */
338 1 : if (epoll_input_node)
339 1 : dtm->epoll_input_node_index = epoll_input_node->index;
340 :
341 : /* main thread only */
342 1 : clib_callback_enable_disable (vm->worker_thread_main_loop_callbacks,
343 : vm->worker_thread_main_loop_callback_tmp,
344 : vm->worker_thread_main_loop_callback_lock,
345 : pcap_postmortem_reset, a->post_mortem);
346 1 : vlib_add_del_post_mortem_callback (pcap_postmortem_dump, a->post_mortem);
347 :
348 1 : return 0;
349 : }
350 :
351 : static clib_error_t *
352 2 : dispatch_trace_command_fn (vlib_main_t *vm, unformat_input_t *input,
353 : vlib_cli_command_t *cmd)
354 : {
355 2 : unformat_input_t _line_input, *line_input = &_line_input;
356 2 : vlib_pcap_dispatch_trace_args_t _a, *a = &_a;
357 2 : u8 *filename = 0;
358 2 : u32 max = 1000;
359 : int rv;
360 2 : int enable = 0;
361 2 : int status = 0;
362 2 : int post_mortem = 0;
363 2 : u32 node_index = ~0, buffer_traces_to_capture = 100;
364 :
365 : /* Get a line of input. */
366 2 : if (!unformat_user (input, unformat_line_input, line_input))
367 0 : return 0;
368 :
369 6 : while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
370 : {
371 4 : if (unformat (line_input, "on %=", &enable, 1))
372 : ;
373 3 : else if (unformat (line_input, "enable %=", &enable, 1))
374 : ;
375 3 : else if (unformat (line_input, "off %=", &enable, 0))
376 : ;
377 2 : else if (unformat (line_input, "disable %=", &enable, 0))
378 : ;
379 2 : else if (unformat (line_input, "max %d", &max))
380 : ;
381 1 : else if (unformat (line_input, "packets-to-capture %d", &max))
382 : ;
383 1 : else if (unformat (line_input, "file %U", unformat_vlib_tmpfile,
384 : &filename))
385 : ;
386 1 : else if (unformat (line_input, "status %=", &status, 1))
387 : ;
388 1 : else if (unformat (line_input, "buffer-trace %U %d", unformat_vlib_node,
389 : vm, &node_index, &buffer_traces_to_capture))
390 : ;
391 0 : else if (unformat (line_input, "post-mortem %=", &post_mortem, 1))
392 : ;
393 : else
394 : {
395 0 : return clib_error_return (0, "unknown input `%U'",
396 : format_unformat_error, line_input);
397 : }
398 : }
399 :
400 2 : unformat_free (line_input);
401 :
402 : /* no need for memset (a, 0, sizeof (*a)), set all fields here. */
403 2 : a->filename = filename;
404 2 : a->enable = enable;
405 2 : a->status = status;
406 2 : a->packets_to_capture = max;
407 2 : a->buffer_trace_node_index = node_index;
408 2 : a->buffer_traces_to_capture = buffer_traces_to_capture;
409 2 : a->post_mortem = post_mortem;
410 :
411 2 : rv = vlib_pcap_dispatch_trace_configure (a);
412 :
413 2 : switch (rv)
414 : {
415 2 : case 0:
416 2 : break;
417 :
418 0 : case -7:
419 0 : return clib_error_return (0, "dispatch trace already enabled...");
420 :
421 0 : case -81:
422 0 : return clib_error_return (0, "dispatch trace already disabled...");
423 :
424 0 : case -8:
425 0 : return clib_error_return (
426 : 0, "can't change number of records to capture while tracing...");
427 :
428 0 : case -11:
429 0 : return clib_error_return (0, "I/O writing trace capture...");
430 :
431 0 : case -6:
432 0 : return clib_error_return (0, "No packets captured...");
433 :
434 0 : default:
435 0 : vlib_cli_output (vm, "WARNING: trace configure returned %d", rv);
436 0 : break;
437 : }
438 2 : return 0;
439 : }
440 :
441 : /*?
442 : * This command is used to start or stop pcap dispatch trace capture, or show
443 : * the capture status.
444 : *
445 : * This command has the following optional parameters:
446 : *
447 : * - <b>on|off</b> - Used to start or stop capture.
448 : *
449 : * - <b>max <nn></b> - Depth of local buffer. Once '<em>nn</em>' number
450 : * of packets have been received, buffer is flushed to file. Once another
451 : * '<em>nn</em>' number of packets have been received, buffer is flushed
452 : * to file, overwriting previous write. If not entered, value defaults
453 : * to 100. Can only be updated if packet capture is off.
454 : *
455 : * - <b>file <name></b> - Used to specify the output filename. The file will
456 : * be placed in the '<em>/tmp</em>' directory, so only the filename is
457 : * supported. Directory should not be entered. If file already exists, file
458 : * will be overwritten. If no filename is provided, '<em>/tmp/vpe.pcap</em>'
459 : * will be used. Can only be updated if packet capture is off.
460 : *
461 : * - <b>status</b> - Displays the current status and configured attributes
462 : * associated with a packet capture. If packet capture is in progress,
463 : * '<em>status</em>' also will return the number of packets currently in
464 : * the local buffer. All additional attributes entered on command line
465 : * with '<em>status</em>' will be ignored and not applied.
466 : *
467 : * @cliexpar
468 : * Example of how to display the status of capture when off:
469 : * @cliexstart{pcap dispatch trace status}
470 : * max is 100, for any interface to file /tmp/vpe.pcap
471 : * pcap dispatch capture is off...
472 : * @cliexend
473 : * Example of how to start a dispatch trace capture:
474 : * @cliexstart{pcap dispatch trace on max 35 file dispatchTrace.pcap}
475 : * pcap dispatch capture on...
476 : * @cliexend
477 : * Example of how to start a dispatch trace capture with buffer tracing
478 : * @cliexstart{pcap dispatch trace on max 10000 file dispatchTrace.pcap
479 : * buffer-trace dpdk-input 1000}
480 : * pcap dispatch capture on...
481 : * @cliexend
482 : * Example of how to display the status of a tx packet capture in progress:
483 : * @cliexstart{pcap trace tx status}
484 : * max is 35, dispatch trace to file /tmp/vppTest.pcap
485 : * pcap tx capture is on: 20 of 35 pkts...
486 : * @cliexend
487 : * Example of how to stop a tx packet capture:
488 : * @cliexstart{vppctl pcap dispatch trace off}
489 : * captured 21 pkts...
490 : * saved to /tmp/dispatchTrace.pcap...
491 : * Example of how to start a post-mortem dispatch trace:
492 : * pcap dispatch trace on max 20000 buffer-trace
493 : * dpdk-input 3000000000 post-mortem
494 : * @cliexend
495 : ?*/
496 :
497 224167 : VLIB_CLI_COMMAND (pcap_dispatch_trace_command, static) = {
498 : .path = "pcap dispatch trace",
499 : .short_help =
500 : "pcap dispatch trace [on|off] [max <nn>] [file <name>] [status]\n"
501 : " [buffer-trace <input-node-name> <nn>][post-mortem]",
502 : .function = dispatch_trace_command_fn,
503 : };
504 :
505 : VLIB_PLUGIN_REGISTER () = {
506 : .version = VPP_BUILD_VER,
507 : .description = "Dispatch Trace",
508 : };
|