2 * Copyright (c) 2016 Nicira, Inc.
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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 #include "command-line.h"
25 #include "fatal-signal.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"
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"
47 VLOG_DEFINE_THIS_MODULE(ovntrace
);
49 /* --db: The database server to contact. */
50 static const char *db
;
52 /* --unixctl-path: Path to use for unixctl server, for "monitor" and "snoop"
54 static char *unixctl_path
;
56 /* The southbound database. */
57 static struct ovsdb_idl
*ovnsb_idl
;
59 /* --detailed: Show a detailed, table-by-table trace. */
62 /* --summary: Show a trace that omits table information. */
65 /* --minimal: Show a trace with only minimal information. */
68 /* --ovs: OVS instance to contact to get OpenFlow flows. */
69 static const char *ovs
;
70 static struct vconn
*vconn
;
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
;
80 main(int argc
, char *argv
[])
82 set_program_name(argv
[0]);
83 service_start(&argc
, &argv
);
84 fatal_ignore_sigpipe();
85 vlog_set_levels_from_string_assert("reconnect:warn");
87 /* Parse command line. */
88 parse_options(argc
, argv
);
94 ovs_fatal(0, "non-option arguments not supported with --detach "
95 "(use --help for help)");
99 ovs_fatal(0, "exactly two non-option arguments are required "
100 "(use --help for help)");
104 struct unixctl_server
*server
= NULL
;
105 bool exiting
= false;
107 daemonize_start(false);
108 int error
= unixctl_server_create(unixctl_path
, &server
);
110 ovs_fatal(error
, "failed to create unixctl server");
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
);
116 ovnsb_idl
= ovsdb_idl_create(db
, &sbrec_idl_class
, true, false);
118 bool already_read
= false;
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
));
128 if (ovsdb_idl_has_ever_connected(ovnsb_idl
)) {
134 daemonize_complete();
136 char *output
= trace(argv
[0], argv
[1]);
137 fputs(output
, stdout
);
146 ovsdb_idl_wait(ovnsb_idl
);
147 unixctl_server_wait(server
);
155 return xasprintf("unix:%s/br-int.mgmt", ovs_rundir());
159 parse_options(int argc
, char *argv
[])
162 OPT_DB
= UCHAR_MAX
+ 1,
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'},
185 STREAM_SSL_LONG_OPTIONS
,
188 char *short_options
= ovs_cmdl_long_options_to_short_options(long_options
);
194 c
= getopt_long(argc
, argv
, short_options
, long_options
, &idx
);
205 unixctl_path
= optarg
;
221 detailed
= summary
= minimal
= true;
225 ovs
= optarg
? optarg
: default_ovs();
232 ovs_print_version(0, 0);
233 printf("DB Schema %s\n", sbrec_get_db_version());
236 DAEMON_OPTION_HANDLERS
238 STREAM_SSL_OPTION_HANDLERS
250 db
= default_sb_db();
253 if (!detailed
&& !summary
&& !minimal
) {
262 %s: OVN trace utility\n\
263 usage: %s [OPTIONS] DATAPATH MICROFLOW\n\
264 %s [OPTIONS] --detach\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
);
276 --db=DATABASE connect to DATABASE\n\
278 --ovs[=REMOTE] obtain corresponding OpenFlow flows from REMOTE\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);
289 struct ovntrace_datapath
{
290 struct hmap_node sb_uuid_node
;
296 struct ovs_list mcgroups
; /* Contains "struct ovntrace_mcgroup"s. */
298 struct ovntrace_flow
**flows
;
299 size_t n_flows
, allocated_flows
;
301 struct hmap mac_bindings
; /* Contains "struct ovntrace_mac_binding"s. */
304 struct ovntrace_port
{
305 struct ovntrace_datapath
*dp
;
309 struct ovntrace_port
*peer
; /* Patch ports only. */
312 struct ovntrace_mcgroup
{
313 struct ovs_list list_node
; /* In struct ovntrace_datapath's 'mcgroups'. */
315 struct ovntrace_datapath
*dp
;
320 struct ovntrace_port
**ports
;
324 enum ovntrace_pipeline
{ P_INGRESS
, P_EGRESS
};
326 struct ovntrace_flow
{
328 enum ovntrace_pipeline pipeline
;
335 struct ovnact
*ovnacts
;
339 struct ovntrace_mac_binding
{
340 struct hmap_node node
;
346 static inline uint32_t
347 hash_mac_binding(uint16_t port_key
, const struct in6_addr
*ip
)
349 return hash_bytes(ip
, sizeof *ip
, port_key
);
352 /* Every ovntrace_datapath, by southbound Datapath_Binding record UUID. */
353 static struct hmap datapaths
;
355 /* Every ovntrace_port, by name. */
356 static struct shash ports
;
358 /* Symbol table for expressions and actions. */
359 static struct shash symtab
;
362 static struct shash address_sets
;
365 static struct hmap dhcp_opts
; /* Contains "struct dhcp_opts_map"s. */
366 static struct hmap dhcpv6_opts
; /* Contains "struct dhcp_opts_map"s. */
368 static struct ovntrace_datapath
*
369 ovntrace_datapath_find_by_sb_uuid(const struct uuid
*sb_uuid
)
371 struct ovntrace_datapath
*dp
;
372 HMAP_FOR_EACH_WITH_HASH (dp
, sb_uuid_node
, uuid_hash(sb_uuid
),
374 if (uuid_equals(&dp
->sb_uuid
, sb_uuid
)) {
381 static const struct ovntrace_datapath
*
382 ovntrace_datapath_find_by_name(const char *name
)
385 bool is_uuid
= uuid_from_string(&uuid
, name
);
387 struct ovntrace_datapath
*dp
;
388 HMAP_FOR_EACH (dp
, sb_uuid_node
, &datapaths
) {
389 if (!strcmp(name
, dp
->name
)
391 && (uuid_equals(&uuid
, &dp
->sb_uuid
) ||
392 uuid_equals(&uuid
, &dp
->nb_uuid
)))) {
399 static const struct ovntrace_port
*
400 ovntrace_port_find_by_key(const struct ovntrace_datapath
*dp
,
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
) {
413 static const struct ovntrace_mcgroup
*
414 ovntrace_mcgroup_find_by_key(const struct ovntrace_datapath
*dp
,
417 const struct ovntrace_mcgroup
*mcgroup
;
418 LIST_FOR_EACH (mcgroup
, list_node
, &dp
->mcgroups
) {
419 if (mcgroup
->tunnel_key
== tunnel_key
) {
426 static const struct ovntrace_mcgroup
*
427 ovntrace_mcgroup_find_by_name(const struct ovntrace_datapath
*dp
,
430 const struct ovntrace_mcgroup
*mcgroup
;
431 LIST_FOR_EACH (mcgroup
, list_node
, &dp
->mcgroups
) {
432 if (!strcmp(mcgroup
->name
, name
)) {
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
)
443 const struct ovntrace_mac_binding
*bind
;
444 HMAP_FOR_EACH_WITH_HASH (bind
, node
, hash_mac_binding(port_key
, ip
),
446 if (bind
->port_key
== port_key
&& ipv6_addr_equals(ip
, &bind
->ip
)) {
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
;
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
;
468 const char *name
= smap_get(ids
, "name");
471 : xasprintf(UUID_FMT
, UUID_ARGS(&dp
->nb_uuid
)));
473 dp
->tunnel_key
= sbdb
->tunnel_key
;
475 ovs_list_init(&dp
->mcgroups
);
476 hmap_init(&dp
->mac_bindings
);
478 hmap_insert(&datapaths
, &dp
->sb_uuid_node
, uuid_hash(&dp
->sb_uuid
));
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
);
492 VLOG_WARN("logical port %s missing datapath", port_name
);
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
);
503 port
->name
= xstrdup(port_name
);
504 port
->type
= xstrdup(sbpb
->type
);
505 port
->tunnel_key
= sbpb
->tunnel_key
;
507 if (!strcmp(sbpb
->type
, "patch")) {
508 const char *peer_name
= smap_get(&sbpb
->options
, "peer");
510 struct ovntrace_port
*peer
511 = shash_find_data(&ports
, peer_name
);
514 port
->peer
->peer
= port
;
522 compare_port(const void *a_
, const void *b_
)
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
;
529 return strcmp(a
->name
, b
->name
);
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
);
540 VLOG_WARN("logical multicast group %s missing datapath",
545 struct ovntrace_mcgroup
*mcgroup
= xzalloc(sizeof *mcgroup
);
546 ovs_list_push_back(&dp
->mcgroups
, &mcgroup
->list_node
);
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
);
555 VLOG_WARN("missing port %s", port_name
);
558 if (!uuid_equals(&sbmg
->ports
[i
]->datapath
->header_
.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
);
565 mcgroup
->ports
[mcgroup
->n_ports
++] = p
;
568 /* Sort the ports in alphabetical order to make output more
570 qsort(mcgroup
->ports
, mcgroup
->n_ports
, sizeof *mcgroup
->ports
,
576 read_address_sets(void)
578 shash_init(&address_sets
);
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
,
589 compare_flow(const void *a_
, const void *b_
)
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
;
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;
606 /* Otherwise who cares. */
614 ovn_init_symtab(&symtab
);
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
);
622 VLOG_WARN("logical flow missing datapath");
628 match
= expr_parse_string(sblf
->match
, &symtab
, &address_sets
, &error
);
630 VLOG_WARN("%s: parsing expression failed (%s)",
636 struct ovnact_parse_params pp
= {
638 .dhcp_opts
= &dhcp_opts
,
639 .dhcpv6_opts
= &dhcpv6_opts
,
641 .cur_ltable
= sblf
->table_id
,
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
);
648 VLOG_WARN("%s: parsing actions failed (%s)", sblf
->actions
, error
);
654 match
= expr_combine(EXPR_T_AND
, match
, prereqs
);
655 match
= expr_annotate(match
, &symtab
, &error
);
657 VLOG_WARN("match annotation failed (%s)", error
);
660 ovnacts_free(ovnacts
.data
, ovnacts
.size
);
661 ofpbuf_uninit(&ovnacts
);
665 match
= expr_simplify(match
);
668 struct ovntrace_flow
*flow
= xzalloc(sizeof *flow
);
669 flow
->uuid
= sblf
->header_
.uuid
;
670 flow
->pipeline
= (!strcmp(sblf
->pipeline
, "ingress")
673 flow
->table_id
= sblf
->table_id
;
674 flow
->stage_name
= nullable_xstrdup(smap_get(&sblf
->external_ids
,
676 flow
->source
= nullable_xstrdup(smap_get(&sblf
->external_ids
,
678 flow
->priority
= sblf
->priority
;
679 flow
->match_s
= xstrdup(sblf
->match
);
681 flow
->ovnacts_len
= ovnacts
.size
;
682 flow
->ovnacts
= ofpbuf_steal_data(&ovnacts
);
684 if (dp
->n_flows
>= dp
->allocated_flows
) {
685 dp
->flows
= x2nrealloc(dp
->flows
, &dp
->allocated_flows
,
688 dp
->flows
[dp
->n_flows
++] = flow
;
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
);
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
);
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
);
715 read_mac_bindings(void)
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
);
722 VLOG_WARN("missing port %s", sbmb
->logical_port
);
726 if (!uuid_equals(&port
->dp
->sb_uuid
, &sbmb
->datapath
->header_
.uuid
)) {
727 VLOG_WARN("port %s is in wrong datapath", sbmb
->logical_port
);
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
);
741 if (!eth_addr_from_string(sbmb
->mac
, &mac
)) {
742 VLOG_WARN("%s: bad Ethernet address", sbmb
->mac
);
746 struct ovntrace_mac_binding
*binding
= xmalloc(sizeof *binding
);
747 binding
->port_key
= port
->tunnel_key
;
750 hmap_insert(&port
->dp
->mac_bindings
, &binding
->node
,
751 hash_mac_binding(binding
->port_key
, &ip6
));
768 ovntrace_lookup_port(const void *dp_
, const char *port_name
,
771 const struct ovntrace_datapath
*dp
= dp_
;
773 if (port_name
[0] == '\0') {
778 const struct ovntrace_port
*port
= shash_find_data(&ports
, port_name
);
780 if (port
->dp
== dp
) {
781 *portp
= port
->tunnel_key
;
784 VLOG_WARN("%s: not in datapath %s", port_name
, dp
->name
);
787 const struct ovntrace_mcgroup
*mcgroup
= ovntrace_mcgroup_find_by_name(dp
, port_name
);
789 *portp
= mcgroup
->tunnel_key
;
793 VLOG_WARN("%s: unknown logical port\n", port_name
);
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
)
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
)) {
814 ovntrace_stage_name(const struct ovntrace_datapath
*dp
,
815 uint8_t table_id
, enum ovntrace_pipeline pipeline
)
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
);;
826 enum ovntrace_node_type
{
827 OVNTRACE_NODE_OUTPUT
,
828 OVNTRACE_NODE_MODIFY
,
829 OVNTRACE_NODE_PIPELINE
,
831 OVNTRACE_NODE_ACTION
,
833 OVNTRACE_NODE_TRANSFORMATION
837 ovntrace_node_type_is_terminal(enum ovntrace_node_type type
)
840 case OVNTRACE_NODE_OUTPUT
:
841 case OVNTRACE_NODE_MODIFY
:
842 case OVNTRACE_NODE_ACTION
:
843 case OVNTRACE_NODE_ERROR
:
846 case OVNTRACE_NODE_PIPELINE
:
847 case OVNTRACE_NODE_TABLE
:
848 case OVNTRACE_NODE_TRANSFORMATION
:
855 struct ovntrace_node
{
856 struct ovs_list node
; /* In parent. */
858 enum ovntrace_node_type type
;
861 struct ovs_list subs
; /* List of children. */
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
, ...)
869 va_start(args
, format
);
870 char *s
= xvasprintf(format
, args
);
873 struct ovntrace_node
*node
= xmalloc(sizeof *node
);
874 ovs_list_push_back(super
, &node
->node
);
877 node
->always_indent
= false;
878 ovs_list_init(&node
->subs
);
884 ovntrace_node_clone(const struct ovs_list
*old
, struct ovs_list
*new)
886 const struct ovntrace_node
*osub
;
887 LIST_FOR_EACH (osub
, node
, old
) {
888 struct ovntrace_node
*nsub
= ovntrace_node_append(new, osub
->type
,
890 nsub
->always_indent
= osub
->always_indent
;
891 ovntrace_node_clone(&osub
->subs
, &nsub
->subs
);
896 ovntrace_node_print_details(struct ds
*output
,
897 const struct ovs_list
*nodes
, int level
)
899 const struct ovntrace_node
*sub
;
900 LIST_FOR_EACH (sub
, node
, nodes
) {
901 if (sub
->type
== OVNTRACE_NODE_MODIFY
) {
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
);
909 ds_put_char(output
, '\n');
911 ds_put_char_multiple(output
, ' ', (level
+ more
) * 4);
912 ds_put_format(output
, "%s\n", sub
->name
);
914 ds_put_char_multiple(output
, ' ', (level
+ more
) * 4);
915 ds_put_char_multiple(output
, '-', strlen(sub
->name
));
916 ds_put_char(output
, '\n');
919 ovntrace_node_print_details(output
, &sub
->subs
, level
+ more
+ more
);
924 ovntrace_node_prune_summary(struct ovs_list
*nodes
)
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
);
938 ovntrace_node_print_summary(struct ds
*output
, const struct ovs_list
*nodes
,
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)) {
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
, '}');
956 if (sub
->type
!= OVNTRACE_NODE_ACTION
) {
957 ds_put_char(output
, ';');
959 ds_put_char(output
, '\n');
964 ovntrace_node_prune_hard(struct ovs_list
*nodes
)
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
);
980 execute_load(const struct ovnact_load
*load
,
981 const struct ovntrace_datapath
*dp
, struct flow
*uflow
,
982 struct ovs_list
*super OVS_UNUSED
)
984 const struct ovnact_encode_params ep
= {
985 .lookup_port
= ovntrace_lookup_port
,
988 uint64_t stub
[512 / 8];
989 struct ofpbuf ofpacts
= OFPBUF_STUB_INITIALIZER(stub
);
991 ovnacts_encode(&load
->ovnact
, sizeof *load
, &ep
, &ofpacts
);
994 OFPACT_FOR_EACH (a
, ofpacts
.data
, ofpacts
.size
) {
995 struct ofpact_set_field
*sf
= ofpact_get_SET_FIELD(a
);
997 if (!mf_is_register(sf
->field
->id
)) {
998 struct ds s
= DS_EMPTY_INITIALIZER
;
999 ovnacts_format(&load
->ovnact
, OVNACT_LOAD_SIZE
, &s
);
1002 ovntrace_node_append(super
, OVNTRACE_NODE_MODIFY
, "%s",
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
);
1013 ofpbuf_uninit(&ofpacts
);
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
)
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
, " = ");
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
);
1031 union mf_subvalue cst
;
1032 mf_read_subfield(rsrc
, uflow
, &cst
);
1033 ds_put_hex(&s
, &cst
, sizeof cst
);
1036 ovntrace_node_append(super
, OVNTRACE_NODE_MODIFY
, "%s", ds_cstr(&s
));
1043 execute_move(const struct ovnact_move
*move
, struct flow
*uflow
,
1044 struct ovs_list
*super
)
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
);
1053 execute_exchange(const struct ovnact_move
*move
, struct flow
*uflow
,
1054 struct ovs_list
*super
)
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
);
1064 trace__(const struct ovntrace_datapath
*dp
, struct flow
*uflow
,
1065 uint8_t table_id
, enum ovntrace_pipeline pipeline
,
1066 struct ovs_list
*super
);
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
);
1074 execute_output(const struct ovntrace_datapath
*dp
, struct flow
*uflow
,
1075 enum ovntrace_pipeline pipeline
, struct ovs_list
*super
)
1077 uint16_t key
= uflow
->regs
[MFF_LOG_OUTPORT
- MFF_REG0
];
1079 ovntrace_node_append(super
, OVNTRACE_NODE_ERROR
,
1080 "*** output to null logical port");
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
,
1087 const char *out_name
= (port
? port
->name
1088 : mcgroup
? mcgroup
->name
1090 if (!port
&& !mcgroup
) {
1091 ovntrace_node_append(super
, OVNTRACE_NODE_ERROR
,
1092 "*** unknown port or multicast group %"PRIu16
,
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
;
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
);
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
);
1113 ovntrace_node_append(super
, OVNTRACE_NODE_MODIFY
,
1114 "output(\"%s\")", out_name
);
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;
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;
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
];
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
);
1147 if (p
->tunnel_key
!= in_key
|| allow_loopback
) {
1148 node
->always_indent
= true;
1150 egress_uflow
.regs
[MFF_LOG_OUTPORT
- MFF_REG0
] = p
->tunnel_key
;
1151 trace__(dp
, &egress_uflow
, 0, P_EGRESS
, &node
->subs
);
1153 ovntrace_node_append(&node
->subs
, OVNTRACE_NODE_OUTPUT
,
1154 "/* omitting output because inport == outport && !flags.loopback */");
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
);
1163 trace__(dp
, &egress_uflow
, 0, P_EGRESS
, &node
->subs
);
1165 ovntrace_node_append(super
, OVNTRACE_NODE_OUTPUT
,
1166 "/* omitting output because inport == outport && !flags.loopback */");
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
)
1175 struct flow arp_flow
= *uflow
;
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;
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. */
1191 struct ovntrace_node
*node
= ovntrace_node_append(
1192 super
, OVNTRACE_NODE_TRANSFORMATION
, "arp");
1194 trace_actions(on
->nested
, on
->nested_len
, dp
, &arp_flow
,
1195 table_id
, pipeline
, &node
->subs
);
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
)
1203 struct flow na_flow
= *uflow
;
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
;
1214 struct ovntrace_node
*node
= ovntrace_node_append(
1215 super
, OVNTRACE_NODE_TRANSFORMATION
, "nd_na");
1217 trace_actions(on
->nested
, on
->nested_len
, dp
, &na_flow
,
1218 table_id
, pipeline
, &node
->subs
);
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
)
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
);
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
)
1240 const struct ovntrace_mac_binding
*binding
1241 = ovntrace_mac_binding_find(dp
, port_key
, &ip
);
1243 uflow
->dl_dst
= binding
? binding
->mac
: eth_addr_zero
;
1245 ovntrace_node_append(super
, OVNTRACE_NODE_ACTION
,
1246 "/* MAC binding to "ETH_ADDR_FMT
". */",
1247 ETH_ADDR_ARGS(uflow
->dl_dst
));
1249 ovntrace_node_append(super
, OVNTRACE_NODE_ACTION
,
1250 "/* No MAC binding. */");
1252 ovntrace_node_append(super
, OVNTRACE_NODE_MODIFY
,
1253 "eth.dst = "ETH_ADDR_FMT
,
1254 ETH_ADDR_ARGS(uflow
->dl_dst
));
1258 execute_put_dhcp_opts(const struct ovnact_put_dhcp_opts
*pdo
,
1259 const char *name
, struct flow
*uflow
,
1260 struct ovs_list
*super
)
1262 ovntrace_node_append(
1263 super
, OVNTRACE_NODE_ERROR
,
1264 "/* We assume that this packet is DHCPDISCOVER or DHCPREQUEST. */");
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
, ", ");
1273 ds_put_format(&s
, "%s = ", o
->option
->name
);
1274 expr_constant_set_format(&o
->value
, &s
);
1276 ovntrace_node_append(super
, OVNTRACE_NODE_MODIFY
, "%s(%s)",
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
));
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
);
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
)
1302 ovntrace_node_append(super
, OVNTRACE_NODE_ACTION
, "drop;");
1306 struct ds s
= DS_EMPTY_INITIALIZER
;
1307 const struct ovnact
*a
;
1308 OVNACT_FOR_EACH (a
, ovnacts
, ovnacts_len
) {
1310 ovnacts_format(a
, sizeof *a
* (ovnact_next(a
) - a
), &s
);
1311 ovntrace_node_append(super
, OVNTRACE_NODE_ACTION
, "%s", ds_cstr(&s
));
1315 execute_output(dp
, uflow
, pipeline
, super
);
1319 trace__(dp
, uflow
, table_id
+ 1, pipeline
, super
);
1323 execute_load(ovnact_get_LOAD(a
), dp
, uflow
, super
);
1327 execute_move(ovnact_get_MOVE(a
), uflow
, super
);
1330 case OVNACT_EXCHANGE
:
1331 execute_exchange(ovnact_get_EXCHANGE(a
), uflow
, super
);
1334 case OVNACT_DEC_TTL
:
1335 if (is_ip_any(uflow
)) {
1336 if (uflow
->nw_ttl
) {
1338 ovntrace_node_append(super
, OVNTRACE_NODE_MODIFY
,
1341 ovntrace_node_append(super
, OVNTRACE_NODE_ERROR
,
1342 "*** TTL underflow");
1345 ovntrace_node_append(super
, OVNTRACE_NODE_ERROR
,
1346 "*** TTL decrement of non-IP packet");
1350 case OVNACT_CT_NEXT
:
1351 case OVNACT_CT_COMMIT
:
1352 case OVNACT_CT_DNAT
:
1353 case OVNACT_CT_SNAT
:
1355 ovntrace_node_append(super
, OVNTRACE_NODE_ERROR
,
1356 "*** ct_* actions not implemented");
1360 execute_arp(ovnact_get_ARP(a
), dp
, uflow
, table_id
, pipeline
,
1365 execute_nd_na(ovnact_get_ND_NA(a
), dp
, uflow
, table_id
, pipeline
,
1369 case OVNACT_GET_ARP
:
1370 execute_get_mac_bind(ovnact_get_GET_ARP(a
), dp
, uflow
, super
);
1374 execute_get_mac_bind(ovnact_get_GET_ND(a
), dp
, uflow
, super
);
1377 case OVNACT_PUT_ARP
:
1379 /* Nothing to do for tracing. */
1382 case OVNACT_PUT_DHCPV4_OPTS
:
1383 execute_put_dhcp_opts(ovnact_get_PUT_DHCPV4_OPTS(a
),
1384 "put_dhcp_opts", uflow
, super
);
1387 case OVNACT_PUT_DHCPV6_OPTS
:
1388 execute_put_dhcp_opts(ovnact_get_PUT_DHCPV6_OPTS(a
),
1389 "put_dhcpv6_opts", uflow
, super
);
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. */
1410 may_omit_stage(const struct ovntrace_flow
*f
, uint8_t table_id
)
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);
1420 trace_openflow(const struct ovntrace_flow
*f
, struct ovs_list
*super
)
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
,
1430 struct ofputil_flow_stats
*fses
;
1432 int error
= vconn_dump_flows(vconn
, &fsr
, OFPUTIL_P_OF13_OXM
,
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
));
1444 struct ds s
= DS_EMPTY_INITIALIZER
;
1445 for (size_t i
= 0; i
< n_fses
; i
++) {
1447 ofp_print_flow_stats(&s
, &fses
[i
]);
1449 /* ofp_print_flow_stats() indents its output with a space.
1451 const char *p
= ds_cstr(&s
);
1452 p
+= strspn(p
, " ");
1453 ovntrace_node_append(super
, OVNTRACE_NODE_ACTION
, "%s", p
);
1457 ovntrace_node_append(super
, OVNTRACE_NODE_ERROR
,
1458 "*** no OpenFlow flows");
1461 for (size_t i
= 0; i
< n_fses
; i
++) {
1462 free(CONST_CAST(struct ofpact
*, fses
[i
].ofpacts
));
1468 trace__(const struct ovntrace_datapath
*dp
, struct flow
*uflow
,
1469 uint8_t table_id
, enum ovntrace_pipeline pipeline
,
1470 struct ovs_list
*super
)
1472 const struct ovntrace_flow
*f
;
1474 f
= ovntrace_flow_lookup(dp
, uflow
, table_id
, pipeline
);
1475 if (!may_omit_stage(f
, table_id
)) {
1481 struct ds s
= DS_EMPTY_INITIALIZER
;
1482 ds_put_format(&s
, "%2d. ", table_id
);
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
);
1491 ds_put_format(&s
, "%s, priority %d, uuid %08x",
1492 f
->match_s
, f
->priority
, f
->uuid
.parts
[0]);
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
? ": " : "");
1500 struct ovntrace_node
*node
= ovntrace_node_append(
1501 super
, OVNTRACE_NODE_TABLE
, "%s", ds_cstr(&s
));
1506 trace_openflow(f
, &node
->subs
);
1508 trace_actions(f
->ovnacts
, f
->ovnacts_len
, dp
, uflow
, table_id
,
1509 pipeline
, &node
->subs
);
1514 trace(const char *dp_s
, const char *flow_s
)
1516 const struct ovntrace_datapath
*dp
= ovntrace_datapath_find_by_name(dp_s
);
1518 return xasprintf("unknown datapath \"%s\"\n", dp_s
);
1522 char *error
= expr_parse_microflow(flow_s
, &symtab
, &address_sets
,
1523 ovntrace_lookup_port
, dp
, &uflow
);
1525 char *s
= xasprintf("error parsing flow: %s\n", error
);
1530 uint32_t in_key
= uflow
.regs
[MFF_LOG_INPORT
- MFF_REG0
];
1532 VLOG_WARN("microflow does not specify ingress port");
1534 const struct ovntrace_port
*inport
= ovntrace_port_find_by_key(dp
, in_key
);
1535 const char *inport_name
= inport
? inport
->name
: "(unnamed)";
1537 struct ds output
= DS_EMPTY_INITIALIZER
;
1539 ds_put_cstr(&output
, "# ");
1540 flow_format(&output
, &uflow
);
1541 ds_put_char(&output
, '\n');
1544 int retval
= vconn_open_block(ovs
, 1 << OFP13_VERSION
, 0, &vconn
);
1546 VLOG_WARN("%s: connection failed (%s)", ovs
, ovs_strerror(retval
));
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
);
1556 bool multiple
= (detailed
+ summary
+ minimal
) > 1;
1559 ds_put_cstr(&output
, "# Detailed trace.\n");
1561 ovntrace_node_print_details(&output
, &root
, 0);
1566 ds_put_cstr(&output
, "# Summary trace.\n");
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);
1576 ds_put_cstr(&output
, "# Minimal trace.\n");
1578 ovntrace_node_prune_hard(&root
);
1579 ovntrace_node_print_summary(&output
, &root
, 0);
1584 return ds_steal_cstr(&output
);
1588 ovntrace_exit(struct unixctl_conn
*conn
, int argc OVS_UNUSED
,
1589 const char *argv
[] OVS_UNUSED
, void *exiting_
)
1591 bool *exiting
= exiting_
;
1593 unixctl_command_reply(conn
, NULL
);
1597 ovntrace_trace(struct unixctl_conn
*conn
, int argc
,
1598 const char *argv
[], void *aux OVS_UNUSED
)
1600 detailed
= summary
= minimal
= false;
1601 while (argc
> 1 && argv
[1][0] == '-') {
1602 if (!strcmp(argv
[1], "--detailed")) {
1604 } else if (!strcmp(argv
[1], "--summary")) {
1606 } else if (!strcmp(argv
[1], "--minimal")) {
1608 } else if (!strcmp(argv
[1], "--all")) {
1609 detailed
= summary
= minimal
= true;
1611 unixctl_command_reply_error(conn
, "unknown option");
1617 if (!detailed
&& !summary
&& !minimal
) {
1622 unixctl_command_reply_error(
1623 conn
, "exactly 2 non-option arguments are required");
1627 char *output
= trace(argv
[1], argv
[2]);
1628 unixctl_command_reply(conn
, output
);