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