]> git.proxmox.com Git - mirror_ovs.git/blob - ovn/utilities/ovn-trace.c
ovn-trace: New --ovs option to also print OpenFlow flows.
[mirror_ovs.git] / ovn / utilities / ovn-trace.c
1 /*
2 * Copyright (c) 2016 Nicira, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <config.h>
18
19 #include <getopt.h>
20
21 #include "command-line.h"
22 #include "compiler.h"
23 #include "daemon.h"
24 #include "dirs.h"
25 #include "fatal-signal.h"
26 #include "flow.h"
27 #include "nx-match.h"
28 #include "openvswitch/dynamic-string.h"
29 #include "openvswitch/ofp-actions.h"
30 #include "openvswitch/ofp-print.h"
31 #include "openvswitch/vconn.h"
32 #include "openvswitch/vlog.h"
33 #include "ovn/actions.h"
34 #include "ovn/expr.h"
35 #include "ovn/lex.h"
36 #include "ovn/lib/logical-fields.h"
37 #include "ovn/lib/ovn-sb-idl.h"
38 #include "ovn/lib/ovn-dhcp.h"
39 #include "ovn/lib/ovn-util.h"
40 #include "ovsdb-idl.h"
41 #include "poll-loop.h"
42 #include "stream-ssl.h"
43 #include "stream.h"
44 #include "unixctl.h"
45 #include "util.h"
46
47 VLOG_DEFINE_THIS_MODULE(ovntrace);
48
49 /* --db: The database server to contact. */
50 static const char *db;
51
52 /* --unixctl-path: Path to use for unixctl server, for "monitor" and "snoop"
53 commands. */
54 static char *unixctl_path;
55
56 /* The southbound database. */
57 static struct ovsdb_idl *ovnsb_idl;
58
59 /* --detailed: Show a detailed, table-by-table trace. */
60 static bool detailed;
61
62 /* --summary: Show a trace that omits table information. */
63 static bool summary;
64
65 /* --minimal: Show a trace with only minimal information. */
66 static bool minimal;
67
68 /* --ovs: OVS instance to contact to get OpenFlow flows. */
69 static const char *ovs;
70 static struct vconn *vconn;
71
72 OVS_NO_RETURN static void usage(void);
73 static void parse_options(int argc, char *argv[]);
74 static char *trace(const char *datapath, const char *flow);
75 static void read_db(void);
76 static unixctl_cb_func ovntrace_exit;
77 static unixctl_cb_func ovntrace_trace;
78
79 int
80 main(int argc, char *argv[])
81 {
82 set_program_name(argv[0]);
83 service_start(&argc, &argv);
84 fatal_ignore_sigpipe();
85 vlog_set_levels_from_string_assert("reconnect:warn");
86
87 /* Parse command line. */
88 parse_options(argc, argv);
89 argc -= optind;
90 argv += optind;
91
92 if (get_detach()) {
93 if (argc != 0) {
94 ovs_fatal(0, "non-option arguments not supported with --detach "
95 "(use --help for help)");
96 }
97 } else {
98 if (argc != 2) {
99 ovs_fatal(0, "exactly two non-option arguments are required "
100 "(use --help for help)");
101 }
102 }
103
104 struct unixctl_server *server = NULL;
105 bool exiting = false;
106 if (get_detach()) {
107 daemonize_start(false);
108 int error = unixctl_server_create(unixctl_path, &server);
109 if (error) {
110 ovs_fatal(error, "failed to create unixctl server");
111 }
112 unixctl_command_register("exit", "", 0, 0, ovntrace_exit, &exiting);
113 unixctl_command_register("trace", "[OPTIONS] DATAPATH MICROFLOW",
114 2, INT_MAX, ovntrace_trace, NULL);
115 }
116 ovnsb_idl = ovsdb_idl_create(db, &sbrec_idl_class, true, false);
117
118 bool already_read = false;
119 for (;;) {
120 ovsdb_idl_run(ovnsb_idl);
121 unixctl_server_run(server);
122 if (!ovsdb_idl_is_alive(ovnsb_idl)) {
123 int retval = ovsdb_idl_get_last_error(ovnsb_idl);
124 ovs_fatal(0, "%s: database connection failed (%s)",
125 db, ovs_retval_to_string(retval));
126 }
127
128 if (ovsdb_idl_has_ever_connected(ovnsb_idl)) {
129 if (!already_read) {
130 already_read = true;
131 read_db();
132 }
133
134 daemonize_complete();
135 if (!get_detach()) {
136 char *output = trace(argv[0], argv[1]);
137 fputs(output, stdout);
138 free(output);
139 return 0;
140 }
141 }
142
143 if (exiting) {
144 break;
145 }
146 ovsdb_idl_wait(ovnsb_idl);
147 unixctl_server_wait(server);
148 poll_block();
149 }
150 }
151
152 static char *
153 default_ovs(void)
154 {
155 return xasprintf("unix:%s/br-int.mgmt", ovs_rundir());
156 }
157
158 static void
159 parse_options(int argc, char *argv[])
160 {
161 enum {
162 OPT_DB = UCHAR_MAX + 1,
163 OPT_UNIXCTL,
164 OPT_DETAILED,
165 OPT_SUMMARY,
166 OPT_MINIMAL,
167 OPT_ALL,
168 OPT_OVS,
169 DAEMON_OPTION_ENUMS,
170 SSL_OPTION_ENUMS,
171 VLOG_OPTION_ENUMS
172 };
173 static const struct option long_options[] = {
174 {"db", required_argument, NULL, OPT_DB},
175 {"unixctl", required_argument, NULL, OPT_UNIXCTL},
176 {"detailed", no_argument, NULL, OPT_DETAILED},
177 {"summary", no_argument, NULL, OPT_SUMMARY},
178 {"minimal", no_argument, NULL, OPT_MINIMAL},
179 {"all", no_argument, NULL, OPT_ALL},
180 {"ovs", optional_argument, NULL, OPT_OVS},
181 {"help", no_argument, NULL, 'h'},
182 {"version", no_argument, NULL, 'V'},
183 DAEMON_LONG_OPTIONS,
184 VLOG_LONG_OPTIONS,
185 STREAM_SSL_LONG_OPTIONS,
186 {NULL, 0, NULL, 0},
187 };
188 char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
189
190 for (;;) {
191 int idx;
192 int c;
193
194 c = getopt_long(argc, argv, short_options, long_options, &idx);
195 if (c == -1) {
196 break;
197 }
198
199 switch (c) {
200 case OPT_DB:
201 db = optarg;
202 break;
203
204 case OPT_UNIXCTL:
205 unixctl_path = optarg;
206 break;
207
208 case OPT_DETAILED:
209 detailed = true;
210 break;
211
212 case OPT_SUMMARY:
213 summary = true;
214 break;
215
216 case OPT_MINIMAL:
217 minimal = true;
218 break;
219
220 case OPT_ALL:
221 detailed = summary = minimal = true;
222 break;
223
224 case OPT_OVS:
225 ovs = optarg ? optarg : default_ovs();
226 break;
227
228 case 'h':
229 usage();
230
231 case 'V':
232 ovs_print_version(0, 0);
233 printf("DB Schema %s\n", sbrec_get_db_version());
234 exit(EXIT_SUCCESS);
235
236 DAEMON_OPTION_HANDLERS
237 VLOG_OPTION_HANDLERS
238 STREAM_SSL_OPTION_HANDLERS
239
240 case '?':
241 exit(EXIT_FAILURE);
242
243 default:
244 abort();
245 }
246 }
247 free(short_options);
248
249 if (!db) {
250 db = default_sb_db();
251 }
252
253 if (!detailed && !summary && !minimal) {
254 detailed = true;
255 }
256 }
257
258 static void
259 usage(void)
260 {
261 printf("\
262 %s: OVN trace utility\n\
263 usage: %s [OPTIONS] DATAPATH MICROFLOW\n\
264 %s [OPTIONS] --detach\n\
265 \n\
266 Output format options:\n\
267 --detailed table-by-table \"backtrace\" (default)\n\
268 --summary less detailed, more parseable\n\
269 --minimal minimum to explain externally visible behavior\n\
270 --all provide all forms of output\n",
271 program_name, program_name, program_name);
272 daemon_usage();
273 vlog_usage();
274 printf("\n\
275 Other options:\n\
276 --db=DATABASE connect to DATABASE\n\
277 (default: %s)\n\
278 --ovs[=REMOTE] obtain corresponding OpenFlow flows from REMOTE\n\
279 (default: %s)\n\
280 --unixctl=SOCKET set control socket name\n\
281 -h, --help display this help message\n\
282 -V, --version display version information\n",
283 default_sb_db(), default_ovs());
284 stream_usage("database", true, true, false);
285 vconn_usage(true, false, false);
286 exit(EXIT_SUCCESS);
287 }
288 \f
289 struct ovntrace_datapath {
290 struct hmap_node sb_uuid_node;
291 struct uuid sb_uuid;
292 struct uuid nb_uuid;
293 char *name;
294 uint32_t tunnel_key;
295
296 struct ovs_list mcgroups; /* Contains "struct ovntrace_mcgroup"s. */
297
298 struct ovntrace_flow **flows;
299 size_t n_flows, allocated_flows;
300
301 struct hmap mac_bindings; /* Contains "struct ovntrace_mac_binding"s. */
302 };
303
304 struct ovntrace_port {
305 struct ovntrace_datapath *dp;
306 char *name;
307 char *type;
308 uint16_t tunnel_key;
309 struct ovntrace_port *peer; /* Patch ports only. */
310 };
311
312 struct ovntrace_mcgroup {
313 struct ovs_list list_node; /* In struct ovntrace_datapath's 'mcgroups'. */
314
315 struct ovntrace_datapath *dp;
316 char *name;
317
318 uint16_t tunnel_key;
319
320 struct ovntrace_port **ports;
321 size_t n_ports;
322 };
323
324 enum ovntrace_pipeline { P_INGRESS, P_EGRESS };
325
326 struct ovntrace_flow {
327 struct uuid uuid;
328 enum ovntrace_pipeline pipeline;
329 int table_id;
330 char *stage_name;
331 char *source;
332 int priority;
333 char *match_s;
334 struct expr *match;
335 struct ovnact *ovnacts;
336 size_t ovnacts_len;
337 };
338
339 struct ovntrace_mac_binding {
340 struct hmap_node node;
341 uint16_t port_key;
342 struct in6_addr ip;
343 struct eth_addr mac;
344 };
345
346 static inline uint32_t
347 hash_mac_binding(uint16_t port_key, const struct in6_addr *ip)
348 {
349 return hash_bytes(ip, sizeof *ip, port_key);
350 }
351
352 /* Every ovntrace_datapath, by southbound Datapath_Binding record UUID. */
353 static struct hmap datapaths;
354
355 /* Every ovntrace_port, by name. */
356 static struct shash ports;
357
358 /* Symbol table for expressions and actions. */
359 static struct shash symtab;
360
361 /* Address sets. */
362 static struct shash address_sets;
363
364 /* DHCP options. */
365 static struct hmap dhcp_opts; /* Contains "struct dhcp_opts_map"s. */
366 static struct hmap dhcpv6_opts; /* Contains "struct dhcp_opts_map"s. */
367
368 static struct ovntrace_datapath *
369 ovntrace_datapath_find_by_sb_uuid(const struct uuid *sb_uuid)
370 {
371 struct ovntrace_datapath *dp;
372 HMAP_FOR_EACH_WITH_HASH (dp, sb_uuid_node, uuid_hash(sb_uuid),
373 &datapaths) {
374 if (uuid_equals(&dp->sb_uuid, sb_uuid)) {
375 return dp;
376 }
377 }
378 return NULL;
379 }
380
381 static const struct ovntrace_datapath *
382 ovntrace_datapath_find_by_name(const char *name)
383 {
384 struct uuid uuid;
385 bool is_uuid = uuid_from_string(&uuid, name);
386
387 struct ovntrace_datapath *dp;
388 HMAP_FOR_EACH (dp, sb_uuid_node, &datapaths) {
389 if (!strcmp(name, dp->name)
390 || (is_uuid
391 && (uuid_equals(&uuid, &dp->sb_uuid) ||
392 uuid_equals(&uuid, &dp->nb_uuid)))) {
393 return dp;
394 }
395 }
396 return NULL;
397 }
398
399 static const struct ovntrace_port *
400 ovntrace_port_find_by_key(const struct ovntrace_datapath *dp,
401 uint16_t tunnel_key)
402 {
403 const struct shash_node *node;
404 SHASH_FOR_EACH (node, &ports) {
405 const struct ovntrace_port *port = node->data;
406 if (port->dp == dp && port->tunnel_key == tunnel_key) {
407 return port;
408 }
409 }
410 return NULL;
411 }
412
413 static const struct ovntrace_mcgroup *
414 ovntrace_mcgroup_find_by_key(const struct ovntrace_datapath *dp,
415 uint16_t tunnel_key)
416 {
417 const struct ovntrace_mcgroup *mcgroup;
418 LIST_FOR_EACH (mcgroup, list_node, &dp->mcgroups) {
419 if (mcgroup->tunnel_key == tunnel_key) {
420 return mcgroup;
421 }
422 }
423 return NULL;
424 }
425
426 static const struct ovntrace_mcgroup *
427 ovntrace_mcgroup_find_by_name(const struct ovntrace_datapath *dp,
428 const char *name)
429 {
430 const struct ovntrace_mcgroup *mcgroup;
431 LIST_FOR_EACH (mcgroup, list_node, &dp->mcgroups) {
432 if (!strcmp(mcgroup->name, name)) {
433 return mcgroup;
434 }
435 }
436 return NULL;
437 }
438
439 static const struct ovntrace_mac_binding *
440 ovntrace_mac_binding_find(const struct ovntrace_datapath *dp,
441 uint16_t port_key, const struct in6_addr *ip)
442 {
443 const struct ovntrace_mac_binding *bind;
444 HMAP_FOR_EACH_WITH_HASH (bind, node, hash_mac_binding(port_key, ip),
445 &dp->mac_bindings) {
446 if (bind->port_key == port_key && ipv6_addr_equals(ip, &bind->ip)) {
447 return bind;
448 }
449 }
450 return NULL;
451 }
452
453 static void
454 read_datapaths(void)
455 {
456 hmap_init(&datapaths);
457 const struct sbrec_datapath_binding *sbdb;
458 SBREC_DATAPATH_BINDING_FOR_EACH (sbdb, ovnsb_idl) {
459 struct ovntrace_datapath *dp = xzalloc(sizeof *dp);
460 const struct smap *ids = &sbdb->external_ids;
461
462 dp->sb_uuid = sbdb->header_.uuid;
463 if (!smap_get_uuid(ids, "logical-switch", &dp->nb_uuid) &&
464 !smap_get_uuid(ids, "logical-router", &dp->nb_uuid)) {
465 dp->nb_uuid = dp->sb_uuid;
466 }
467
468 const char *name = smap_get(ids, "name");
469 dp->name = (name
470 ? xstrdup(name)
471 : xasprintf(UUID_FMT, UUID_ARGS(&dp->nb_uuid)));
472
473 dp->tunnel_key = sbdb->tunnel_key;
474
475 ovs_list_init(&dp->mcgroups);
476 hmap_init(&dp->mac_bindings);
477
478 hmap_insert(&datapaths, &dp->sb_uuid_node, uuid_hash(&dp->sb_uuid));
479 }
480 }
481
482 static void
483 read_ports(void)
484 {
485 shash_init(&ports);
486 const struct sbrec_port_binding *sbpb;
487 SBREC_PORT_BINDING_FOR_EACH (sbpb, ovnsb_idl) {
488 const char *port_name = sbpb->logical_port;
489 struct ovntrace_datapath *dp
490 = ovntrace_datapath_find_by_sb_uuid(&sbpb->datapath->header_.uuid);
491 if (!dp) {
492 VLOG_WARN("logical port %s missing datapath", port_name);
493 continue;
494 }
495
496 struct ovntrace_port *port = xzalloc(sizeof *port);
497 if (!shash_add_once(&ports, port_name, port)) {
498 VLOG_WARN("duplicate logical port name %s", port_name);
499 free(port);
500 continue;
501 }
502 port->dp = dp;
503 port->name = xstrdup(port_name);
504 port->type = xstrdup(sbpb->type);
505 port->tunnel_key = sbpb->tunnel_key;
506
507 if (!strcmp(sbpb->type, "patch")) {
508 const char *peer_name = smap_get(&sbpb->options, "peer");
509 if (peer_name) {
510 struct ovntrace_port *peer
511 = shash_find_data(&ports, peer_name);
512 if (peer) {
513 port->peer = peer;
514 port->peer->peer = port;
515 }
516 }
517 }
518 }
519 }
520
521 static int
522 compare_port(const void *a_, const void *b_)
523 {
524 struct ovntrace_port *const *ap = a_;
525 struct ovntrace_port *const *bp = b_;
526 const struct ovntrace_port *a = *ap;
527 const struct ovntrace_port *b = *bp;
528
529 return strcmp(a->name, b->name);
530 }
531
532 static void
533 read_mcgroups(void)
534 {
535 const struct sbrec_multicast_group *sbmg;
536 SBREC_MULTICAST_GROUP_FOR_EACH (sbmg, ovnsb_idl) {
537 struct ovntrace_datapath *dp
538 = ovntrace_datapath_find_by_sb_uuid(&sbmg->datapath->header_.uuid);
539 if (!dp) {
540 VLOG_WARN("logical multicast group %s missing datapath",
541 sbmg->name);
542 continue;
543 }
544
545 struct ovntrace_mcgroup *mcgroup = xzalloc(sizeof *mcgroup);
546 ovs_list_push_back(&dp->mcgroups, &mcgroup->list_node);
547 mcgroup->dp = dp;
548 mcgroup->tunnel_key = sbmg->tunnel_key;
549 mcgroup->name = xstrdup(sbmg->name);
550 mcgroup->ports = xmalloc(sbmg->n_ports * sizeof *mcgroup->ports);
551 for (size_t i = 0; i < sbmg->n_ports; i++) {
552 const char *port_name = sbmg->ports[i]->logical_port;
553 struct ovntrace_port *p = shash_find_data(&ports, port_name);
554 if (!p) {
555 VLOG_WARN("missing port %s", port_name);
556 continue;
557 }
558 if (!uuid_equals(&sbmg->ports[i]->datapath->header_.uuid,
559 &p->dp->sb_uuid)) {
560 VLOG_WARN("multicast group %s in datapath %s contains "
561 "port %s outside that datapath",
562 mcgroup->name, mcgroup->dp->name, port_name);
563 continue;
564 }
565 mcgroup->ports[mcgroup->n_ports++] = p;
566 }
567
568 /* Sort the ports in alphabetical order to make output more
569 * predictable. */
570 qsort(mcgroup->ports, mcgroup->n_ports, sizeof *mcgroup->ports,
571 compare_port);
572 }
573 }
574
575 static void
576 read_address_sets(void)
577 {
578 shash_init(&address_sets);
579
580 const struct sbrec_address_set *sbas;
581 SBREC_ADDRESS_SET_FOR_EACH (sbas, ovnsb_idl) {
582 expr_macros_add(&address_sets, sbas->name,
583 (const char *const *) sbas->addresses,
584 sbas->n_addresses);
585 }
586 }
587
588 static int
589 compare_flow(const void *a_, const void *b_)
590 {
591 struct ovntrace_flow *const *ap = a_;
592 struct ovntrace_flow *const *bp = b_;
593 const struct ovntrace_flow *a = *ap;
594 const struct ovntrace_flow *b = *bp;
595
596 if (a->pipeline != b->pipeline) {
597 /* Sort P_INGRESS before P_EGRESS. */
598 return a->pipeline == P_EGRESS ? 1 : -1;
599 } else if (a->table_id != b->table_id) {
600 /* Sort in increasing order of table_id. */
601 return a->table_id > b->table_id ? 1 : -1;
602 } else if (a->priority != b->priority) {
603 /* Sort in decreasing order of priority. */
604 return a->priority > b->priority ? -1 : 1;
605 } else {
606 /* Otherwise who cares. */
607 return 0;
608 }
609 }
610
611 static void
612 read_flows(void)
613 {
614 ovn_init_symtab(&symtab);
615
616 const struct sbrec_logical_flow *sblf;
617 SBREC_LOGICAL_FLOW_FOR_EACH (sblf, ovnsb_idl) {
618 const struct sbrec_datapath_binding *sbdb = sblf->logical_datapath;
619 struct ovntrace_datapath *dp
620 = ovntrace_datapath_find_by_sb_uuid(&sbdb->header_.uuid);
621 if (!dp) {
622 VLOG_WARN("logical flow missing datapath");
623 continue;
624 }
625
626 char *error;
627 struct expr *match;
628 match = expr_parse_string(sblf->match, &symtab, &address_sets, &error);
629 if (error) {
630 VLOG_WARN("%s: parsing expression failed (%s)",
631 sblf->match, error);
632 free(error);
633 continue;
634 }
635
636 struct ovnact_parse_params pp = {
637 .symtab = &symtab,
638 .dhcp_opts = &dhcp_opts,
639 .dhcpv6_opts = &dhcpv6_opts,
640 .n_tables = 16,
641 .cur_ltable = sblf->table_id,
642 };
643 uint64_t stub[1024 / 8];
644 struct ofpbuf ovnacts = OFPBUF_STUB_INITIALIZER(stub);
645 struct expr *prereqs;
646 error = ovnacts_parse_string(sblf->actions, &pp, &ovnacts, &prereqs);
647 if (error) {
648 VLOG_WARN("%s: parsing actions failed (%s)", sblf->actions, error);
649 free(error);
650 expr_destroy(match);
651 continue;
652 }
653
654 match = expr_combine(EXPR_T_AND, match, prereqs);
655 match = expr_annotate(match, &symtab, &error);
656 if (error) {
657 VLOG_WARN("match annotation failed (%s)", error);
658 free(error);
659 expr_destroy(match);
660 ovnacts_free(ovnacts.data, ovnacts.size);
661 ofpbuf_uninit(&ovnacts);
662 continue;
663 }
664 if (match) {
665 match = expr_simplify(match);
666 }
667
668 struct ovntrace_flow *flow = xzalloc(sizeof *flow);
669 flow->uuid = sblf->header_.uuid;
670 flow->pipeline = (!strcmp(sblf->pipeline, "ingress")
671 ? P_INGRESS
672 : P_EGRESS);
673 flow->table_id = sblf->table_id;
674 flow->stage_name = nullable_xstrdup(smap_get(&sblf->external_ids,
675 "stage-name"));
676 flow->source = nullable_xstrdup(smap_get(&sblf->external_ids,
677 "source"));
678 flow->priority = sblf->priority;
679 flow->match_s = xstrdup(sblf->match);
680 flow->match = match;
681 flow->ovnacts_len = ovnacts.size;
682 flow->ovnacts = ofpbuf_steal_data(&ovnacts);
683
684 if (dp->n_flows >= dp->allocated_flows) {
685 dp->flows = x2nrealloc(dp->flows, &dp->allocated_flows,
686 sizeof *dp->flows);
687 }
688 dp->flows[dp->n_flows++] = flow;
689 }
690
691 const struct ovntrace_datapath *dp;
692 HMAP_FOR_EACH (dp, sb_uuid_node, &datapaths) {
693 qsort(dp->flows, dp->n_flows, sizeof *dp->flows, compare_flow);
694 }
695 }
696
697 static void
698 read_dhcp_opts(void)
699 {
700 hmap_init(&dhcp_opts);
701 const struct sbrec_dhcp_options *sdo;
702 SBREC_DHCP_OPTIONS_FOR_EACH (sdo, ovnsb_idl) {
703 dhcp_opt_add(&dhcp_opts, sdo->name, sdo->code, sdo->type);
704 }
705
706
707 hmap_init(&dhcpv6_opts);
708 const struct sbrec_dhcpv6_options *sdo6;
709 SBREC_DHCPV6_OPTIONS_FOR_EACH(sdo6, ovnsb_idl) {
710 dhcp_opt_add(&dhcpv6_opts, sdo6->name, sdo6->code, sdo6->type);
711 }
712 }
713
714 static void
715 read_mac_bindings(void)
716 {
717 const struct sbrec_mac_binding *sbmb;
718 SBREC_MAC_BINDING_FOR_EACH (sbmb, ovnsb_idl) {
719 const struct ovntrace_port *port = shash_find_data(
720 &ports, sbmb->logical_port);
721 if (!port) {
722 VLOG_WARN("missing port %s", sbmb->logical_port);
723 continue;
724 }
725
726 if (!uuid_equals(&port->dp->sb_uuid, &sbmb->datapath->header_.uuid)) {
727 VLOG_WARN("port %s is in wrong datapath", sbmb->logical_port);
728 continue;
729 }
730
731 struct in6_addr ip6;
732 ovs_be32 ip4;
733 if (ip_parse(sbmb->ip, &ip4)) {
734 ip6 = in6_addr_mapped_ipv4(ip4);
735 } else if (!ipv6_parse(sbmb->ip, &ip6)) {
736 VLOG_WARN("%s: bad IP address", sbmb->ip);
737 continue;
738 }
739
740 struct eth_addr mac;
741 if (!eth_addr_from_string(sbmb->mac, &mac)) {
742 VLOG_WARN("%s: bad Ethernet address", sbmb->mac);
743 continue;
744 }
745
746 struct ovntrace_mac_binding *binding = xmalloc(sizeof *binding);
747 binding->port_key = port->tunnel_key;
748 binding->ip = ip6;
749 binding->mac = mac;
750 hmap_insert(&port->dp->mac_bindings, &binding->node,
751 hash_mac_binding(binding->port_key, &ip6));
752 }
753 }
754
755 static void
756 read_db(void)
757 {
758 read_datapaths();
759 read_ports();
760 read_mcgroups();
761 read_address_sets();
762 read_dhcp_opts();
763 read_flows();
764 read_mac_bindings();
765 }
766
767 static bool
768 ovntrace_lookup_port(const void *dp_, const char *port_name,
769 unsigned int *portp)
770 {
771 const struct ovntrace_datapath *dp = dp_;
772
773 if (port_name[0] == '\0') {
774 *portp = 0;
775 return true;
776 }
777
778 const struct ovntrace_port *port = shash_find_data(&ports, port_name);
779 if (port) {
780 if (port->dp == dp) {
781 *portp = port->tunnel_key;
782 return true;
783 }
784 VLOG_WARN("%s: not in datapath %s", port_name, dp->name);
785 }
786
787 const struct ovntrace_mcgroup *mcgroup = ovntrace_mcgroup_find_by_name(dp, port_name);
788 if (mcgroup) {
789 *portp = mcgroup->tunnel_key;
790 return true;
791 }
792
793 VLOG_WARN("%s: unknown logical port\n", port_name);
794 return false;
795 }
796
797 static const struct ovntrace_flow *
798 ovntrace_flow_lookup(const struct ovntrace_datapath *dp,
799 const struct flow *uflow,
800 uint8_t table_id, enum ovntrace_pipeline pipeline)
801 {
802 for (size_t i = 0; i < dp->n_flows; i++) {
803 const struct ovntrace_flow *flow = dp->flows[i];
804 if (flow->pipeline == pipeline &&
805 flow->table_id == table_id &&
806 expr_evaluate(flow->match, uflow, ovntrace_lookup_port, dp)) {
807 return flow;
808 }
809 }
810 return NULL;
811 }
812
813 static char *
814 ovntrace_stage_name(const struct ovntrace_datapath *dp,
815 uint8_t table_id, enum ovntrace_pipeline pipeline)
816 {
817 for (size_t i = 0; i < dp->n_flows; i++) {
818 const struct ovntrace_flow *flow = dp->flows[i];
819 if (flow->pipeline == pipeline && flow->table_id == table_id) {
820 return xstrdup(flow->stage_name);;
821 }
822 }
823 return NULL;
824 }
825
826 enum ovntrace_node_type {
827 OVNTRACE_NODE_OUTPUT,
828 OVNTRACE_NODE_MODIFY,
829 OVNTRACE_NODE_PIPELINE,
830 OVNTRACE_NODE_TABLE,
831 OVNTRACE_NODE_ACTION,
832 OVNTRACE_NODE_ERROR,
833 OVNTRACE_NODE_TRANSFORMATION
834 };
835
836 static bool
837 ovntrace_node_type_is_terminal(enum ovntrace_node_type type)
838 {
839 switch (type) {
840 case OVNTRACE_NODE_OUTPUT:
841 case OVNTRACE_NODE_MODIFY:
842 case OVNTRACE_NODE_ACTION:
843 case OVNTRACE_NODE_ERROR:
844 return true;
845
846 case OVNTRACE_NODE_PIPELINE:
847 case OVNTRACE_NODE_TABLE:
848 case OVNTRACE_NODE_TRANSFORMATION:
849 return false;
850 }
851
852 OVS_NOT_REACHED();
853 }
854
855 struct ovntrace_node {
856 struct ovs_list node; /* In parent. */
857
858 enum ovntrace_node_type type;
859 const char *name;
860 bool always_indent;
861 struct ovs_list subs; /* List of children. */
862 };
863
864 static struct ovntrace_node * OVS_PRINTF_FORMAT(3, 4)
865 ovntrace_node_append(struct ovs_list *super, enum ovntrace_node_type type,
866 const char *format, ...)
867 {
868 va_list args;
869 va_start(args, format);
870 char *s = xvasprintf(format, args);
871 va_end(args);
872
873 struct ovntrace_node *node = xmalloc(sizeof *node);
874 ovs_list_push_back(super, &node->node);
875 node->type = type;
876 node->name = s;
877 node->always_indent = false;
878 ovs_list_init(&node->subs);
879
880 return node;
881 }
882
883 static void
884 ovntrace_node_clone(const struct ovs_list *old, struct ovs_list *new)
885 {
886 const struct ovntrace_node *osub;
887 LIST_FOR_EACH (osub, node, old) {
888 struct ovntrace_node *nsub = ovntrace_node_append(new, osub->type,
889 "%s", osub->name);
890 nsub->always_indent = osub->always_indent;
891 ovntrace_node_clone(&osub->subs, &nsub->subs);
892 }
893 }
894
895 static void
896 ovntrace_node_print_details(struct ds *output,
897 const struct ovs_list *nodes, int level)
898 {
899 const struct ovntrace_node *sub;
900 LIST_FOR_EACH (sub, node, nodes) {
901 if (sub->type == OVNTRACE_NODE_MODIFY) {
902 continue;
903 }
904
905 bool more = sub->node.next != nodes || sub->always_indent || ovntrace_node_type_is_terminal(sub->type);
906 bool title = (sub->type == OVNTRACE_NODE_PIPELINE ||
907 sub->type == OVNTRACE_NODE_TRANSFORMATION);
908 if (title) {
909 ds_put_char(output, '\n');
910 }
911 ds_put_char_multiple(output, ' ', (level + more) * 4);
912 ds_put_format(output, "%s\n", sub->name);
913 if (title) {
914 ds_put_char_multiple(output, ' ', (level + more) * 4);
915 ds_put_char_multiple(output, '-', strlen(sub->name));
916 ds_put_char(output, '\n');
917 }
918
919 ovntrace_node_print_details(output, &sub->subs, level + more + more);
920 }
921 }
922
923 static void
924 ovntrace_node_prune_summary(struct ovs_list *nodes)
925 {
926 struct ovntrace_node *sub, *next;
927 LIST_FOR_EACH_SAFE (sub, next, node, nodes) {
928 ovntrace_node_prune_summary(&sub->subs);
929 if (sub->type == OVNTRACE_NODE_MODIFY ||
930 sub->type == OVNTRACE_NODE_TABLE) {
931 ovs_list_remove(&sub->node);
932 ovs_list_splice(&next->node, sub->subs.next, &sub->subs);
933 }
934 }
935 }
936
937 static void
938 ovntrace_node_print_summary(struct ds *output, const struct ovs_list *nodes,
939 int level)
940 {
941 const struct ovntrace_node *sub;
942 LIST_FOR_EACH (sub, node, nodes) {
943 if (sub->type == OVNTRACE_NODE_ACTION
944 && !strncmp(sub->name, "next(", 5)) {
945 continue;
946 }
947
948 ds_put_char_multiple(output, ' ', level * 4);
949 ds_put_cstr(output, sub->name);
950 if (!ovs_list_is_empty(&sub->subs)) {
951 ds_put_cstr(output, " {\n");
952 ovntrace_node_print_summary(output, &sub->subs, level + 1);
953 ds_put_char_multiple(output, ' ', level * 4);
954 ds_put_char(output, '}');
955 }
956 if (sub->type != OVNTRACE_NODE_ACTION) {
957 ds_put_char(output, ';');
958 }
959 ds_put_char(output, '\n');
960 }
961 }
962
963 static void
964 ovntrace_node_prune_hard(struct ovs_list *nodes)
965 {
966 struct ovntrace_node *sub, *next;
967 LIST_FOR_EACH_SAFE (sub, next, node, nodes) {
968 ovntrace_node_prune_hard(&sub->subs);
969 if (sub->type == OVNTRACE_NODE_ACTION ||
970 sub->type == OVNTRACE_NODE_PIPELINE ||
971 sub->type == OVNTRACE_NODE_TABLE ||
972 sub->type == OVNTRACE_NODE_OUTPUT) {
973 ovs_list_remove(&sub->node);
974 ovs_list_splice(&next->node, sub->subs.next, &sub->subs);
975 }
976 }
977 }
978
979 static void
980 execute_load(const struct ovnact_load *load,
981 const struct ovntrace_datapath *dp, struct flow *uflow,
982 struct ovs_list *super OVS_UNUSED)
983 {
984 const struct ovnact_encode_params ep = {
985 .lookup_port = ovntrace_lookup_port,
986 .aux = dp,
987 };
988 uint64_t stub[512 / 8];
989 struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub);
990
991 ovnacts_encode(&load->ovnact, sizeof *load, &ep, &ofpacts);
992
993 struct ofpact *a;
994 OFPACT_FOR_EACH (a, ofpacts.data, ofpacts.size) {
995 struct ofpact_set_field *sf = ofpact_get_SET_FIELD(a);
996
997 if (!mf_is_register(sf->field->id)) {
998 struct ds s = DS_EMPTY_INITIALIZER;
999 ovnacts_format(&load->ovnact, OVNACT_LOAD_SIZE, &s);
1000 ds_chomp(&s, ';');
1001
1002 ovntrace_node_append(super, OVNTRACE_NODE_MODIFY, "%s",
1003 ds_cstr(&s));
1004
1005 ds_destroy(&s);
1006 }
1007
1008 if (mf_are_prereqs_ok(sf->field, uflow, NULL)) {
1009 mf_set_flow_value_masked(sf->field, sf->value,
1010 ofpact_set_field_mask(sf), uflow);
1011 }
1012 }
1013 ofpbuf_uninit(&ofpacts);
1014 }
1015
1016 static void
1017 summarize_move(const struct mf_subfield *rsrc,
1018 const struct expr_field *dst, const struct mf_subfield *rdst,
1019 const struct flow *uflow, struct ovs_list *super OVS_UNUSED)
1020 {
1021 if (!mf_is_register(rdst->field->id)) {
1022 struct ds s = DS_EMPTY_INITIALIZER;
1023 expr_field_format(dst, &s);
1024 ds_put_cstr(&s, " = ");
1025
1026 if (rsrc->ofs == 0 && rsrc->n_bits >= rsrc->field->n_bits) {
1027 union mf_value value;
1028 mf_get_value(rsrc->field, uflow, &value);
1029 mf_format(rsrc->field, &value, NULL, &s);
1030 } else {
1031 union mf_subvalue cst;
1032 mf_read_subfield(rsrc, uflow, &cst);
1033 ds_put_hex(&s, &cst, sizeof cst);
1034 }
1035
1036 ovntrace_node_append(super, OVNTRACE_NODE_MODIFY, "%s", ds_cstr(&s));
1037
1038 ds_destroy(&s);
1039 }
1040 }
1041
1042 static void
1043 execute_move(const struct ovnact_move *move, struct flow *uflow,
1044 struct ovs_list *super)
1045 {
1046 struct mf_subfield dst = expr_resolve_field(&move->lhs);
1047 struct mf_subfield src = expr_resolve_field(&move->rhs);
1048 summarize_move(&src, &move->lhs, &dst, uflow, super);
1049 mf_subfield_copy(&src, &dst, uflow, NULL);
1050 }
1051
1052 static void
1053 execute_exchange(const struct ovnact_move *move, struct flow *uflow,
1054 struct ovs_list *super)
1055 {
1056 struct mf_subfield a = expr_resolve_field(&move->lhs);
1057 struct mf_subfield b = expr_resolve_field(&move->rhs);
1058 summarize_move(&b, &move->lhs, &a, uflow, super);
1059 summarize_move(&a, &move->rhs, &b, uflow, super);
1060 mf_subfield_swap(&a, &b, uflow, NULL);
1061 }
1062
1063 static void
1064 trace__(const struct ovntrace_datapath *dp, struct flow *uflow,
1065 uint8_t table_id, enum ovntrace_pipeline pipeline,
1066 struct ovs_list *super);
1067
1068 static void
1069 trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
1070 const struct ovntrace_datapath *dp, struct flow *uflow,
1071 uint8_t table_id, enum ovntrace_pipeline pipeline,
1072 struct ovs_list *super);
1073 static void
1074 execute_output(const struct ovntrace_datapath *dp, struct flow *uflow,
1075 enum ovntrace_pipeline pipeline, struct ovs_list *super)
1076 {
1077 uint16_t key = uflow->regs[MFF_LOG_OUTPORT - MFF_REG0];
1078 if (!key) {
1079 ovntrace_node_append(super, OVNTRACE_NODE_ERROR,
1080 "*** output to null logical port");
1081 return;
1082 }
1083
1084 const struct ovntrace_port *port = ovntrace_port_find_by_key(dp, key);
1085 const struct ovntrace_mcgroup *mcgroup = ovntrace_mcgroup_find_by_key(dp,
1086 key);
1087 const char *out_name = (port ? port->name
1088 : mcgroup ? mcgroup->name
1089 : "(unnamed)");
1090 if (!port && !mcgroup) {
1091 ovntrace_node_append(super, OVNTRACE_NODE_ERROR,
1092 "*** unknown port or multicast group %"PRIu16,
1093 key);
1094 }
1095
1096 if (pipeline == P_EGRESS) {
1097 ovntrace_node_append(super, OVNTRACE_NODE_OUTPUT,
1098 "/* output to \"%s\", type \"%s\" */",
1099 out_name, port ? port->type : "");
1100 if (port && port->peer) {
1101 const struct ovntrace_port *peer = port->peer;
1102
1103 struct ovntrace_node *node = ovntrace_node_append(
1104 super, OVNTRACE_NODE_PIPELINE,
1105 "ingress(dp=\"%s\", inport=\"%s\")",
1106 peer->dp->name, peer->name);
1107
1108 struct flow new_uflow = *uflow;
1109 new_uflow.regs[MFF_LOG_INPORT - MFF_REG0] = peer->tunnel_key;
1110 new_uflow.regs[MFF_LOG_OUTPORT - MFF_REG0] = 0;
1111 trace__(peer->dp, &new_uflow, 0, P_INGRESS, &node->subs);
1112 } else {
1113 ovntrace_node_append(super, OVNTRACE_NODE_MODIFY,
1114 "output(\"%s\")", out_name);
1115
1116 }
1117 return;
1118 }
1119
1120 struct flow egress_uflow = *uflow;
1121 for (int i = 0; i < FLOW_N_REGS; i++) {
1122 if (i != MFF_LOG_INPORT - MFF_REG0 &&
1123 i != MFF_LOG_OUTPORT - MFF_REG0) {
1124 egress_uflow.regs[i] = 0;
1125 }
1126 }
1127
1128 uint16_t in_key = uflow->regs[MFF_LOG_INPORT - MFF_REG0];
1129 const struct ovntrace_port *inport = ovntrace_port_find_by_key(dp, in_key);
1130 const char *inport_name = !in_key ? "" : inport ? inport->name : "(unnamed)";
1131 uint32_t flags = uflow->regs[MFF_LOG_FLAGS - MFF_REG0];
1132 bool allow_loopback = (flags & MLF_ALLOW_LOOPBACK) != 0;
1133
1134 if (mcgroup) {
1135 struct ovntrace_node *mcnode = ovntrace_node_append(
1136 super, OVNTRACE_NODE_PIPELINE,
1137 "multicast(dp=\"%s\", mcgroup=\"%s\")",
1138 dp->name, mcgroup->name);
1139 for (size_t i = 0; i < mcgroup->n_ports; i++) {
1140 const struct ovntrace_port *p = mcgroup->ports[i];
1141
1142 struct ovntrace_node *node = ovntrace_node_append(
1143 &mcnode->subs, OVNTRACE_NODE_PIPELINE,
1144 "egress(dp=\"%s\", inport=\"%s\", outport=\"%s\")",
1145 dp->name, inport_name, p->name);
1146
1147 if (p->tunnel_key != in_key || allow_loopback) {
1148 node->always_indent = true;
1149
1150 egress_uflow.regs[MFF_LOG_OUTPORT - MFF_REG0] = p->tunnel_key;
1151 trace__(dp, &egress_uflow, 0, P_EGRESS, &node->subs);
1152 } else {
1153 ovntrace_node_append(&node->subs, OVNTRACE_NODE_OUTPUT,
1154 "/* omitting output because inport == outport && !flags.loopback */");
1155 }
1156 }
1157 } else if (port->tunnel_key != in_key || allow_loopback) {
1158 struct ovntrace_node *node = ovntrace_node_append(
1159 super, OVNTRACE_NODE_PIPELINE,
1160 "egress(dp=\"%s\", inport=\"%s\", outport=\"%s\")",
1161 dp->name, inport_name, out_name);
1162
1163 trace__(dp, &egress_uflow, 0, P_EGRESS, &node->subs);
1164 } else {
1165 ovntrace_node_append(super, OVNTRACE_NODE_OUTPUT,
1166 "/* omitting output because inport == outport && !flags.loopback */");
1167 }
1168 }
1169
1170 static void
1171 execute_arp(const struct ovnact_nest *on, const struct ovntrace_datapath *dp,
1172 const struct flow *uflow, uint8_t table_id,
1173 enum ovntrace_pipeline pipeline, struct ovs_list *super)
1174 {
1175 struct flow arp_flow = *uflow;
1176
1177 /* Zero fields that are no longer relevant. */
1178 arp_flow.nw_frag = 0;
1179 arp_flow.nw_tos = 0;
1180 arp_flow.nw_ttl = 0;
1181 arp_flow.tcp_flags = 0;
1182
1183 /* Update fields for ARP. */
1184 arp_flow.dl_type = htons(ETH_TYPE_ARP);
1185 arp_flow.nw_proto = ARP_OP_REQUEST;
1186 arp_flow.arp_sha = arp_flow.dl_src;
1187 arp_flow.arp_tha = eth_addr_zero;
1188 /* ARP SPA is already in arp_flow.nw_src. */
1189 /* ARP TPA is already in arp_flow.nw_dst. */
1190
1191 struct ovntrace_node *node = ovntrace_node_append(
1192 super, OVNTRACE_NODE_TRANSFORMATION, "arp");
1193
1194 trace_actions(on->nested, on->nested_len, dp, &arp_flow,
1195 table_id, pipeline, &node->subs);
1196 }
1197
1198 static void
1199 execute_nd_na(const struct ovnact_nest *on, const struct ovntrace_datapath *dp,
1200 const struct flow *uflow, uint8_t table_id,
1201 enum ovntrace_pipeline pipeline, struct ovs_list *super)
1202 {
1203 struct flow na_flow = *uflow;
1204
1205 /* Update fields for NA. */
1206 na_flow.dl_src = uflow->dl_dst;
1207 na_flow.dl_dst = uflow->dl_src;
1208 na_flow.ipv6_dst = uflow->ipv6_src;
1209 na_flow.ipv6_src = uflow->nd_target;
1210 na_flow.tp_src = htons(136);
1211 na_flow.arp_sha = eth_addr_zero;
1212 na_flow.arp_tha = uflow->dl_dst;
1213
1214 struct ovntrace_node *node = ovntrace_node_append(
1215 super, OVNTRACE_NODE_TRANSFORMATION, "nd_na");
1216
1217 trace_actions(on->nested, on->nested_len, dp, &na_flow,
1218 table_id, pipeline, &node->subs);
1219 }
1220
1221 static void
1222 execute_get_mac_bind(const struct ovnact_get_mac_bind *bind,
1223 const struct ovntrace_datapath *dp,
1224 struct flow *uflow, struct ovs_list *super)
1225 {
1226 /* Get logical port number.*/
1227 struct mf_subfield port_sf = expr_resolve_field(&bind->port);
1228 ovs_assert(port_sf.n_bits == 32);
1229 uint32_t port_key = mf_get_subfield(&port_sf, uflow);
1230
1231 /* Get IP address. */
1232 struct mf_subfield ip_sf = expr_resolve_field(&bind->ip);
1233 ovs_assert(ip_sf.n_bits == 32 || ip_sf.n_bits == 128);
1234 union mf_subvalue ip_sv;
1235 mf_read_subfield(&ip_sf, uflow, &ip_sv);
1236 struct in6_addr ip = (ip_sf.n_bits == 32
1237 ? in6_addr_mapped_ipv4(ip_sv.ipv4)
1238 : ip_sv.ipv6);
1239
1240 const struct ovntrace_mac_binding *binding
1241 = ovntrace_mac_binding_find(dp, port_key, &ip);
1242
1243 uflow->dl_dst = binding ? binding->mac : eth_addr_zero;
1244 if (binding) {
1245 ovntrace_node_append(super, OVNTRACE_NODE_ACTION,
1246 "/* MAC binding to "ETH_ADDR_FMT". */",
1247 ETH_ADDR_ARGS(uflow->dl_dst));
1248 } else {
1249 ovntrace_node_append(super, OVNTRACE_NODE_ACTION,
1250 "/* No MAC binding. */");
1251 }
1252 ovntrace_node_append(super, OVNTRACE_NODE_MODIFY,
1253 "eth.dst = "ETH_ADDR_FMT,
1254 ETH_ADDR_ARGS(uflow->dl_dst));
1255 }
1256
1257 static void
1258 execute_put_dhcp_opts(const struct ovnact_put_dhcp_opts *pdo,
1259 const char *name, struct flow *uflow,
1260 struct ovs_list *super)
1261 {
1262 ovntrace_node_append(
1263 super, OVNTRACE_NODE_ERROR,
1264 "/* We assume that this packet is DHCPDISCOVER or DHCPREQUEST. */");
1265
1266 /* Format the put_dhcp_opts action. */
1267 struct ds s = DS_EMPTY_INITIALIZER;
1268 for (const struct ovnact_dhcp_option *o = pdo->options;
1269 o < &pdo->options[pdo->n_options]; o++) {
1270 if (o != pdo->options) {
1271 ds_put_cstr(&s, ", ");
1272 }
1273 ds_put_format(&s, "%s = ", o->option->name);
1274 expr_constant_set_format(&o->value, &s);
1275 }
1276 ovntrace_node_append(super, OVNTRACE_NODE_MODIFY, "%s(%s)",
1277 name, ds_cstr(&s));
1278 ds_destroy(&s);
1279
1280 struct mf_subfield dst = expr_resolve_field(&pdo->dst);
1281 if (!mf_is_register(dst.field->id)) {
1282 /* Format assignment. */
1283 struct ds s = DS_EMPTY_INITIALIZER;
1284 expr_field_format(&pdo->dst, &s);
1285 ovntrace_node_append(super, OVNTRACE_NODE_MODIFY,
1286 "%s = 1", ds_cstr(&s));
1287 ds_destroy(&s);
1288 }
1289
1290 struct mf_subfield sf = expr_resolve_field(&pdo->dst);
1291 union mf_subvalue sv = { .u8_val = 1 };
1292 mf_write_subfield_flow(&sf, &sv, uflow);
1293 }
1294
1295 static void
1296 trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
1297 const struct ovntrace_datapath *dp, struct flow *uflow,
1298 uint8_t table_id, enum ovntrace_pipeline pipeline,
1299 struct ovs_list *super)
1300 {
1301 if (!ovnacts_len) {
1302 ovntrace_node_append(super, OVNTRACE_NODE_ACTION, "drop;");
1303 return;
1304 }
1305
1306 struct ds s = DS_EMPTY_INITIALIZER;
1307 const struct ovnact *a;
1308 OVNACT_FOR_EACH (a, ovnacts, ovnacts_len) {
1309 ds_clear(&s);
1310 ovnacts_format(a, sizeof *a * (ovnact_next(a) - a), &s);
1311 ovntrace_node_append(super, OVNTRACE_NODE_ACTION, "%s", ds_cstr(&s));
1312
1313 switch (a->type) {
1314 case OVNACT_OUTPUT:
1315 execute_output(dp, uflow, pipeline, super);
1316 break;
1317
1318 case OVNACT_NEXT:
1319 trace__(dp, uflow, table_id + 1, pipeline, super);
1320 break;
1321
1322 case OVNACT_LOAD:
1323 execute_load(ovnact_get_LOAD(a), dp, uflow, super);
1324 break;
1325
1326 case OVNACT_MOVE:
1327 execute_move(ovnact_get_MOVE(a), uflow, super);
1328 break;
1329
1330 case OVNACT_EXCHANGE:
1331 execute_exchange(ovnact_get_EXCHANGE(a), uflow, super);
1332 break;
1333
1334 case OVNACT_DEC_TTL:
1335 if (is_ip_any(uflow)) {
1336 if (uflow->nw_ttl) {
1337 uflow->nw_ttl--;
1338 ovntrace_node_append(super, OVNTRACE_NODE_MODIFY,
1339 "ip.ttl--");
1340 } else {
1341 ovntrace_node_append(super, OVNTRACE_NODE_ERROR,
1342 "*** TTL underflow");
1343 }
1344 } else {
1345 ovntrace_node_append(super, OVNTRACE_NODE_ERROR,
1346 "*** TTL decrement of non-IP packet");
1347 }
1348 break;
1349
1350 case OVNACT_CT_NEXT:
1351 case OVNACT_CT_COMMIT:
1352 case OVNACT_CT_DNAT:
1353 case OVNACT_CT_SNAT:
1354 case OVNACT_CT_LB:
1355 ovntrace_node_append(super, OVNTRACE_NODE_ERROR,
1356 "*** ct_* actions not implemented");
1357 break;
1358
1359 case OVNACT_ARP:
1360 execute_arp(ovnact_get_ARP(a), dp, uflow, table_id, pipeline,
1361 super);
1362 break;
1363
1364 case OVNACT_ND_NA:
1365 execute_nd_na(ovnact_get_ND_NA(a), dp, uflow, table_id, pipeline,
1366 super);
1367 break;
1368
1369 case OVNACT_GET_ARP:
1370 execute_get_mac_bind(ovnact_get_GET_ARP(a), dp, uflow, super);
1371 break;
1372
1373 case OVNACT_GET_ND:
1374 execute_get_mac_bind(ovnact_get_GET_ND(a), dp, uflow, super);
1375 break;
1376
1377 case OVNACT_PUT_ARP:
1378 case OVNACT_PUT_ND:
1379 /* Nothing to do for tracing. */
1380 break;
1381
1382 case OVNACT_PUT_DHCPV4_OPTS:
1383 execute_put_dhcp_opts(ovnact_get_PUT_DHCPV4_OPTS(a),
1384 "put_dhcp_opts", uflow, super);
1385 break;
1386
1387 case OVNACT_PUT_DHCPV6_OPTS:
1388 execute_put_dhcp_opts(ovnact_get_PUT_DHCPV6_OPTS(a),
1389 "put_dhcpv6_opts", uflow, super);
1390 break;
1391
1392 case OVNACT_SET_QUEUE:
1393 /* The set_queue action is slippery from a logical perspective. It
1394 * has no visible effect as long as the packet remains on the same
1395 * chassis: it can bounce from one logical datapath to another
1396 * retaining the queue and even end up at a VM on the same chassis.
1397 * Without taking the physical arrangement into account, we can't
1398 * do anything with this action other than just to note that it
1399 * happened. If we ever add some physical knowledge to ovn-trace,
1400 * though, it would be easy enough to track the queue information
1401 * by adjusting uflow->skb_priority. */
1402 break;
1403 }
1404
1405 }
1406 ds_destroy(&s);
1407 }
1408
1409 static bool
1410 may_omit_stage(const struct ovntrace_flow *f, uint8_t table_id)
1411 {
1412 return (f
1413 && f->match->type == EXPR_T_BOOLEAN && f->match->boolean
1414 && f->ovnacts_len == OVNACT_NEXT_SIZE
1415 && f->ovnacts->type == OVNACT_NEXT
1416 && ovnact_get_NEXT(f->ovnacts)->ltable == table_id + 1);
1417 }
1418
1419 static void
1420 trace_openflow(const struct ovntrace_flow *f, struct ovs_list *super)
1421 {
1422 struct ofputil_flow_stats_request fsr = {
1423 .cookie = htonll(f->uuid.parts[0]),
1424 .cookie_mask = OVS_BE64_MAX,
1425 .out_port = OFPP_ANY,
1426 .out_group = OFPG_ANY,
1427 .table_id = OFPTT_ALL,
1428 };
1429
1430 struct ofputil_flow_stats *fses;
1431 size_t n_fses;
1432 int error = vconn_dump_flows(vconn, &fsr, OFPUTIL_P_OF13_OXM,
1433 &fses, &n_fses);
1434 if (error) {
1435 ovntrace_node_append(super, OVNTRACE_NODE_ERROR,
1436 "*** error obtaining flow stats (%s)",
1437 ovs_strerror(error));
1438 VLOG_WARN("%s: error obtaining flow stats (%s)",
1439 ovs, ovs_strerror(error));
1440 return;
1441 }
1442
1443 if (n_fses) {
1444 struct ds s = DS_EMPTY_INITIALIZER;
1445 for (size_t i = 0; i < n_fses; i++) {
1446 ds_clear(&s);
1447 ofp_print_flow_stats(&s, &fses[i]);
1448
1449 /* ofp_print_flow_stats() indents its output with a space.
1450 * Omit it. */
1451 const char *p = ds_cstr(&s);
1452 p += strspn(p, " ");
1453 ovntrace_node_append(super, OVNTRACE_NODE_ACTION, "%s", p);
1454 }
1455 ds_destroy(&s);
1456 } else {
1457 ovntrace_node_append(super, OVNTRACE_NODE_ERROR,
1458 "*** no OpenFlow flows");
1459 }
1460
1461 for (size_t i = 0; i < n_fses; i++) {
1462 free(CONST_CAST(struct ofpact *, fses[i].ofpacts));
1463 }
1464 free(fses);
1465 }
1466
1467 static void
1468 trace__(const struct ovntrace_datapath *dp, struct flow *uflow,
1469 uint8_t table_id, enum ovntrace_pipeline pipeline,
1470 struct ovs_list *super)
1471 {
1472 const struct ovntrace_flow *f;
1473 for (;;) {
1474 f = ovntrace_flow_lookup(dp, uflow, table_id, pipeline);
1475 if (!may_omit_stage(f, table_id)) {
1476 break;
1477 }
1478 table_id++;
1479 }
1480
1481 struct ds s = DS_EMPTY_INITIALIZER;
1482 ds_put_format(&s, "%2d. ", table_id);
1483 if (f) {
1484 if (f->stage_name && f->source) {
1485 ds_put_format(&s, "%s (%s): ", f->stage_name, f->source);
1486 } else if (f->stage_name) {
1487 ds_put_format(&s, "%s: ", f->stage_name);
1488 } else if (f->source) {
1489 ds_put_format(&s, "(%s): ", f->source);
1490 }
1491 ds_put_format(&s, "%s, priority %d, uuid %08x",
1492 f->match_s, f->priority, f->uuid.parts[0]);
1493 } else {
1494 char *stage_name = ovntrace_stage_name(dp, table_id, pipeline);
1495 ds_put_format(&s, "%s%sno match (implicit drop)",
1496 stage_name ? stage_name : "",
1497 stage_name ? ": " : "");
1498 free(stage_name);
1499 }
1500 struct ovntrace_node *node = ovntrace_node_append(
1501 super, OVNTRACE_NODE_TABLE, "%s", ds_cstr(&s));
1502 ds_destroy(&s);
1503
1504 if (f) {
1505 if (vconn) {
1506 trace_openflow(f, &node->subs);
1507 }
1508 trace_actions(f->ovnacts, f->ovnacts_len, dp, uflow, table_id,
1509 pipeline, &node->subs);
1510 }
1511 }
1512
1513 static char *
1514 trace(const char *dp_s, const char *flow_s)
1515 {
1516 const struct ovntrace_datapath *dp = ovntrace_datapath_find_by_name(dp_s);
1517 if (!dp) {
1518 return xasprintf("unknown datapath \"%s\"\n", dp_s);
1519 }
1520
1521 struct flow uflow;
1522 char *error = expr_parse_microflow(flow_s, &symtab, &address_sets,
1523 ovntrace_lookup_port, dp, &uflow);
1524 if (error) {
1525 char *s = xasprintf("error parsing flow: %s\n", error);
1526 free(error);
1527 return s;
1528 }
1529
1530 uint32_t in_key = uflow.regs[MFF_LOG_INPORT - MFF_REG0];
1531 if (!in_key) {
1532 VLOG_WARN("microflow does not specify ingress port");
1533 }
1534 const struct ovntrace_port *inport = ovntrace_port_find_by_key(dp, in_key);
1535 const char *inport_name = inport ? inport->name : "(unnamed)";
1536
1537 struct ds output = DS_EMPTY_INITIALIZER;
1538
1539 ds_put_cstr(&output, "# ");
1540 flow_format(&output, &uflow);
1541 ds_put_char(&output, '\n');
1542
1543 if (ovs) {
1544 int retval = vconn_open_block(ovs, 1 << OFP13_VERSION, 0, &vconn);
1545 if (retval) {
1546 VLOG_WARN("%s: connection failed (%s)", ovs, ovs_strerror(retval));
1547 }
1548 }
1549
1550 struct ovs_list root = OVS_LIST_INITIALIZER(&root);
1551 struct ovntrace_node *node = ovntrace_node_append(
1552 &root, OVNTRACE_NODE_PIPELINE, "ingress(dp=\"%s\", inport=\"%s\")",
1553 dp->name, inport_name);
1554 trace__(dp, &uflow, 0, P_INGRESS, &node->subs);
1555
1556 bool multiple = (detailed + summary + minimal) > 1;
1557 if (detailed) {
1558 if (multiple) {
1559 ds_put_cstr(&output, "# Detailed trace.\n");
1560 }
1561 ovntrace_node_print_details(&output, &root, 0);
1562 }
1563
1564 if (summary) {
1565 if (multiple) {
1566 ds_put_cstr(&output, "# Summary trace.\n");
1567 }
1568 struct ovs_list clone = OVS_LIST_INITIALIZER(&clone);
1569 ovntrace_node_clone(&root, &clone);
1570 ovntrace_node_prune_summary(&clone);
1571 ovntrace_node_print_summary(&output, &clone, 0);
1572 }
1573
1574 if (minimal) {
1575 if (multiple) {
1576 ds_put_cstr(&output, "# Minimal trace.\n");
1577 }
1578 ovntrace_node_prune_hard(&root);
1579 ovntrace_node_print_summary(&output, &root, 0);
1580 }
1581
1582 vconn_close(vconn);
1583
1584 return ds_steal_cstr(&output);
1585 }
1586 \f
1587 static void
1588 ovntrace_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
1589 const char *argv[] OVS_UNUSED, void *exiting_)
1590 {
1591 bool *exiting = exiting_;
1592 *exiting = true;
1593 unixctl_command_reply(conn, NULL);
1594 }
1595
1596 static void
1597 ovntrace_trace(struct unixctl_conn *conn, int argc,
1598 const char *argv[], void *aux OVS_UNUSED)
1599 {
1600 detailed = summary = minimal = false;
1601 while (argc > 1 && argv[1][0] == '-') {
1602 if (!strcmp(argv[1], "--detailed")) {
1603 detailed = true;
1604 } else if (!strcmp(argv[1], "--summary")) {
1605 summary = true;
1606 } else if (!strcmp(argv[1], "--minimal")) {
1607 minimal = true;
1608 } else if (!strcmp(argv[1], "--all")) {
1609 detailed = summary = minimal = true;
1610 } else {
1611 unixctl_command_reply_error(conn, "unknown option");
1612 return;
1613 }
1614 argc--;
1615 argv++;
1616 }
1617 if (!detailed && !summary && !minimal) {
1618 detailed = true;
1619 }
1620
1621 if (argc != 3) {
1622 unixctl_command_reply_error(
1623 conn, "exactly 2 non-option arguments are required");
1624 return;
1625 }
1626
1627 char *output = trace(argv[1], argv[2]);
1628 unixctl_command_reply(conn, output);
1629 free(output);
1630 }