2 * Copyright (c) 2016, 2017 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 /* --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
;
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
;
85 main(int argc
, char *argv
[])
87 set_program_name(argv
[0]);
88 service_start(&argc
, &argv
);
89 fatal_ignore_sigpipe();
90 vlog_set_levels_from_string_assert("reconnect:warn");
92 /* Parse command line. */
93 parse_options(argc
, argv
);
99 ovs_fatal(0, "non-option arguments not supported with --detach "
100 "(use --help for help)");
104 ovs_fatal(0, "exactly two non-option arguments are required "
105 "(use --help for help)");
109 struct unixctl_server
*server
= NULL
;
110 bool exiting
= false;
112 daemonize_start(false);
113 int error
= unixctl_server_create(unixctl_path
, &server
);
115 ovs_fatal(error
, "failed to create unixctl server");
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
);
121 ovnsb_idl
= ovsdb_idl_create(db
, &sbrec_idl_class
, true, false);
123 bool already_read
= false;
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
));
133 if (ovsdb_idl_has_ever_connected(ovnsb_idl
)) {
139 daemonize_complete();
141 char *output
= trace(argv
[0], argv
[1]);
142 fputs(output
, stdout
);
151 ovsdb_idl_wait(ovnsb_idl
);
152 unixctl_server_wait(server
);
160 return xasprintf("unix:%s/br-int.mgmt", ovs_rundir());
164 parse_ct_option(const char *state_s_
)
166 uint32_t state
= CS_TRACKED
;
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
);
174 ovs_fatal(0, "%s: unknown connection tracking state flag", cs
);
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",
186 if (state
& CS_NEW
&& state
& CS_ESTABLISHED
) {
187 VLOG_WARN("%s: invalid connection state: "
188 "\"new\" and \"est\" are mutually exclusive", state_s_
);
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_
);
195 ct_states
= xrealloc(ct_states
, (n_ct_states
+ 1) * sizeof *ct_states
);
196 ct_states
[n_ct_states
++] = state
;
200 parse_options(int argc
, char *argv
[])
203 OPT_DB
= UCHAR_MAX
+ 1,
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'},
228 STREAM_SSL_LONG_OPTIONS
,
231 char *short_options
= ovs_cmdl_long_options_to_short_options(long_options
);
237 c
= getopt_long(argc
, argv
, short_options
, long_options
, &idx
);
248 unixctl_path
= optarg
;
264 detailed
= summary
= minimal
= true;
268 ovs
= optarg
? optarg
: default_ovs();
272 parse_ct_option(optarg
);
279 ovs_print_version(0, 0);
280 printf("DB Schema %s\n", sbrec_get_db_version());
283 DAEMON_OPTION_HANDLERS
285 STREAM_SSL_OPTION_HANDLERS
297 db
= default_sb_db();
300 if (!detailed
&& !summary
&& !minimal
) {
309 %s: OVN trace utility\n\
310 usage: %s [OPTIONS] DATAPATH MICROFLOW\n\
311 %s [OPTIONS] --detach\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
);
323 --db=DATABASE connect to DATABASE\n\
325 --ovs[=REMOTE] obtain corresponding OpenFlow flows from REMOTE\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);
336 struct ovntrace_datapath
{
337 struct hmap_node sb_uuid_node
;
343 struct ovs_list mcgroups
; /* Contains "struct ovntrace_mcgroup"s. */
345 struct ovntrace_flow
**flows
;
346 size_t n_flows
, allocated_flows
;
348 struct hmap mac_bindings
; /* Contains "struct ovntrace_mac_binding"s. */
351 struct ovntrace_port
{
352 struct ovntrace_datapath
*dp
;
356 struct ovntrace_port
*peer
; /* Patch ports only. */
357 struct ovntrace_port
*distributed_port
; /* chassisredirect ports only. */
360 struct ovntrace_mcgroup
{
361 struct ovs_list list_node
; /* In struct ovntrace_datapath's 'mcgroups'. */
363 struct ovntrace_datapath
*dp
;
368 struct ovntrace_port
**ports
;
372 struct ovntrace_flow
{
374 enum ovnact_pipeline pipeline
;
381 struct ovnact
*ovnacts
;
385 struct ovntrace_mac_binding
{
386 struct hmap_node node
;
392 static inline uint32_t
393 hash_mac_binding(uint16_t port_key
, const struct in6_addr
*ip
)
395 return hash_bytes(ip
, sizeof *ip
, port_key
);
398 /* Every ovntrace_datapath, by southbound Datapath_Binding record UUID. */
399 static struct hmap datapaths
;
401 /* Every ovntrace_port, by name. */
402 static struct shash ports
;
404 /* Symbol table for expressions and actions. */
405 static struct shash symtab
;
408 static struct shash address_sets
;
411 static struct hmap dhcp_opts
; /* Contains "struct dhcp_opts_map"s. */
412 static struct hmap dhcpv6_opts
; /* Contains "struct dhcp_opts_map"s. */
414 static struct ovntrace_datapath
*
415 ovntrace_datapath_find_by_sb_uuid(const struct uuid
*sb_uuid
)
417 struct ovntrace_datapath
*dp
;
418 HMAP_FOR_EACH_WITH_HASH (dp
, sb_uuid_node
, uuid_hash(sb_uuid
),
420 if (uuid_equals(&dp
->sb_uuid
, sb_uuid
)) {
427 static const struct ovntrace_datapath
*
428 ovntrace_datapath_find_by_name(const char *name
)
431 bool is_uuid
= uuid_from_string(&uuid
, name
);
433 struct ovntrace_datapath
*dp
;
434 HMAP_FOR_EACH (dp
, sb_uuid_node
, &datapaths
) {
435 if (!strcmp(name
, dp
->name
)
437 && (uuid_equals(&uuid
, &dp
->sb_uuid
) ||
438 uuid_equals(&uuid
, &dp
->nb_uuid
)))) {
445 static const struct ovntrace_port
*
446 ovntrace_port_find_by_key(const struct ovntrace_datapath
*dp
,
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
) {
460 ovntrace_port_key_to_name(const struct ovntrace_datapath
*dp
,
463 const struct ovntrace_port
*port
= ovntrace_port_find_by_key(dp
, key
);
464 return (port
? port
->name
469 static const struct ovntrace_mcgroup
*
470 ovntrace_mcgroup_find_by_key(const struct ovntrace_datapath
*dp
,
473 const struct ovntrace_mcgroup
*mcgroup
;
474 LIST_FOR_EACH (mcgroup
, list_node
, &dp
->mcgroups
) {
475 if (mcgroup
->tunnel_key
== tunnel_key
) {
482 static const struct ovntrace_mcgroup
*
483 ovntrace_mcgroup_find_by_name(const struct ovntrace_datapath
*dp
,
486 const struct ovntrace_mcgroup
*mcgroup
;
487 LIST_FOR_EACH (mcgroup
, list_node
, &dp
->mcgroups
) {
488 if (!strcmp(mcgroup
->name
, name
)) {
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
)
499 const struct ovntrace_mac_binding
*bind
;
500 HMAP_FOR_EACH_WITH_HASH (bind
, node
, hash_mac_binding(port_key
, ip
),
502 if (bind
->port_key
== port_key
&& ipv6_addr_equals(ip
, &bind
->ip
)) {
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
;
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
;
524 const char *name
= smap_get(ids
, "name");
527 : xasprintf(UUID_FMT
, UUID_ARGS(&dp
->nb_uuid
)));
529 dp
->tunnel_key
= sbdb
->tunnel_key
;
531 ovs_list_init(&dp
->mcgroups
);
532 hmap_init(&dp
->mac_bindings
);
534 hmap_insert(&datapaths
, &dp
->sb_uuid_node
, uuid_hash(&dp
->sb_uuid
));
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
);
548 VLOG_WARN("logical port %s missing datapath", port_name
);
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
);
559 port
->name
= xstrdup(port_name
);
560 port
->type
= xstrdup(sbpb
->type
);
561 port
->tunnel_key
= sbpb
->tunnel_key
;
563 if (!strcmp(sbpb
->type
, "patch")) {
564 const char *peer_name
= smap_get(&sbpb
->options
, "peer");
566 struct ovntrace_port
*peer
567 = shash_find_data(&ports
, peer_name
);
570 port
->peer
->peer
= port
;
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
);
581 const char *distributed_name
= smap_get(&sbpb
->options
,
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
;
596 compare_port(const void *a_
, const void *b_
)
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
;
603 return strcmp(a
->name
, b
->name
);
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
);
614 VLOG_WARN("logical multicast group %s missing datapath",
619 struct ovntrace_mcgroup
*mcgroup
= xzalloc(sizeof *mcgroup
);
620 ovs_list_push_back(&dp
->mcgroups
, &mcgroup
->list_node
);
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
);
629 VLOG_WARN("missing port %s", port_name
);
632 if (!uuid_equals(&sbmg
->ports
[i
]->datapath
->header_
.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
);
639 mcgroup
->ports
[mcgroup
->n_ports
++] = p
;
642 /* Sort the ports in alphabetical order to make output more
644 qsort(mcgroup
->ports
, mcgroup
->n_ports
, sizeof *mcgroup
->ports
,
650 read_address_sets(void)
652 shash_init(&address_sets
);
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
,
663 ovntrace_is_chassis_resident(const void *aux OVS_UNUSED
,
664 const char *port_name
)
666 if (port_name
[0] == '\0') {
670 const struct ovntrace_port
*port
= shash_find_data(&ports
, port_name
);
672 /* Since ovntrace is not chassis specific, assume any port
673 * that exists is resident. */
677 VLOG_WARN("%s: unknown logical port\n", port_name
);
682 compare_flow(const void *a_
, const void *b_
)
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
;
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;
699 /* Otherwise who cares. */
707 ovn_init_symtab(&symtab
);
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
);
715 VLOG_WARN("logical flow missing datapath");
721 match
= expr_parse_string(sblf
->match
, &symtab
, &address_sets
, &error
);
723 VLOG_WARN("%s: parsing expression failed (%s)",
729 struct ovnact_parse_params pp
= {
731 .dhcp_opts
= &dhcp_opts
,
732 .dhcpv6_opts
= &dhcpv6_opts
,
733 .pipeline
= (!strcmp(sblf
->pipeline
, "ingress")
737 .cur_ltable
= sblf
->table_id
,
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
);
744 VLOG_WARN("%s: parsing actions failed (%s)", sblf
->actions
, error
);
750 match
= expr_combine(EXPR_T_AND
, match
, prereqs
);
751 match
= expr_annotate(match
, &symtab
, &error
);
753 VLOG_WARN("match annotation failed (%s)", error
);
756 ovnacts_free(ovnacts
.data
, ovnacts
.size
);
757 ofpbuf_uninit(&ovnacts
);
761 match
= expr_simplify(match
, ovntrace_is_chassis_resident
, NULL
);
764 struct ovntrace_flow
*flow
= xzalloc(sizeof *flow
);
765 flow
->uuid
= sblf
->header_
.uuid
;
766 flow
->pipeline
= (!strcmp(sblf
->pipeline
, "ingress")
769 flow
->table_id
= sblf
->table_id
;
770 flow
->stage_name
= nullable_xstrdup(smap_get(&sblf
->external_ids
,
772 flow
->source
= nullable_xstrdup(smap_get(&sblf
->external_ids
,
774 flow
->priority
= sblf
->priority
;
775 flow
->match_s
= xstrdup(sblf
->match
);
777 flow
->ovnacts_len
= ovnacts
.size
;
778 flow
->ovnacts
= ofpbuf_steal_data(&ovnacts
);
780 if (dp
->n_flows
>= dp
->allocated_flows
) {
781 dp
->flows
= x2nrealloc(dp
->flows
, &dp
->allocated_flows
,
784 dp
->flows
[dp
->n_flows
++] = flow
;
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
);
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
);
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
);
811 read_mac_bindings(void)
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
);
818 VLOG_WARN("missing port %s", sbmb
->logical_port
);
822 if (!uuid_equals(&port
->dp
->sb_uuid
, &sbmb
->datapath
->header_
.uuid
)) {
823 VLOG_WARN("port %s is in wrong datapath", sbmb
->logical_port
);
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
);
837 if (!eth_addr_from_string(sbmb
->mac
, &mac
)) {
838 VLOG_WARN("%s: bad Ethernet address", sbmb
->mac
);
842 struct ovntrace_mac_binding
*binding
= xmalloc(sizeof *binding
);
843 binding
->port_key
= port
->tunnel_key
;
846 hmap_insert(&port
->dp
->mac_bindings
, &binding
->node
,
847 hash_mac_binding(binding
->port_key
, &ip6
));
864 ovntrace_lookup_port(const void *dp_
, const char *port_name
,
867 const struct ovntrace_datapath
*dp
= dp_
;
869 if (port_name
[0] == '\0') {
874 const struct ovntrace_port
*port
= shash_find_data(&ports
, port_name
);
876 if (port
->dp
== dp
) {
877 *portp
= port
->tunnel_key
;
880 VLOG_WARN("%s: not in datapath %s", port_name
, dp
->name
);
883 const struct ovntrace_mcgroup
*mcgroup
= ovntrace_mcgroup_find_by_name(dp
, port_name
);
885 *portp
= mcgroup
->tunnel_key
;
889 VLOG_WARN("%s: unknown logical port\n", port_name
);
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
)
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
)) {
910 ovntrace_stage_name(const struct ovntrace_datapath
*dp
,
911 uint8_t table_id
, enum ovnact_pipeline pipeline
)
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
);;
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
,
930 /* Nodes that never have children (terminal nodes). */
931 OVNTRACE_NODE_PIPELINE
,
933 OVNTRACE_NODE_TRANSFORMATION
937 ovntrace_node_type_is_terminal(enum ovntrace_node_type type
)
940 case OVNTRACE_NODE_OUTPUT
:
941 case OVNTRACE_NODE_MODIFY
:
942 case OVNTRACE_NODE_ACTION
:
943 case OVNTRACE_NODE_ERROR
:
946 case OVNTRACE_NODE_PIPELINE
:
947 case OVNTRACE_NODE_TABLE
:
948 case OVNTRACE_NODE_TRANSFORMATION
:
955 struct ovntrace_node
{
956 struct ovs_list node
; /* In parent. */
958 enum ovntrace_node_type type
;
961 struct ovs_list subs
; /* List of children. */
964 static void ovntrace_node_destroy(struct ovntrace_node
*);
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
, ...)
971 va_start(args
, format
);
972 char *s
= xvasprintf(format
, args
);
975 struct ovntrace_node
*node
= xmalloc(sizeof *node
);
976 ovs_list_push_back(super
, &node
->node
);
979 node
->always_indent
= false;
980 ovs_list_init(&node
->subs
);
986 ovntrace_node_clone(const struct ovs_list
*old
, struct ovs_list
*new)
988 const struct ovntrace_node
*osub
;
989 LIST_FOR_EACH (osub
, node
, old
) {
990 struct ovntrace_node
*nsub
= ovntrace_node_append(new, osub
->type
,
992 nsub
->always_indent
= osub
->always_indent
;
993 ovntrace_node_clone(&osub
->subs
, &nsub
->subs
);
998 ovntrace_node_list_destroy(struct ovs_list
*list
)
1001 struct ovntrace_node
*node
, *next
;
1003 LIST_FOR_EACH_SAFE (node
, next
, node
, list
) {
1004 ovs_list_remove(&node
->node
);
1005 ovntrace_node_destroy(node
);
1011 ovntrace_node_destroy(struct ovntrace_node
*node
)
1014 ovntrace_node_list_destroy(&node
->subs
);
1021 ovntrace_node_print_details(struct ds
*output
,
1022 const struct ovs_list
*nodes
, int level
)
1024 const struct ovntrace_node
*sub
;
1025 LIST_FOR_EACH (sub
, node
, nodes
) {
1026 if (sub
->type
== OVNTRACE_NODE_MODIFY
) {
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
);
1036 ds_put_char(output
, '\n');
1038 ds_put_char_multiple(output
, ' ', (level
+ more
) * 4);
1039 ds_put_format(output
, "%s\n", sub
->name
);
1041 ds_put_char_multiple(output
, ' ', (level
+ more
) * 4);
1042 ds_put_char_multiple(output
, '-', strlen(sub
->name
));
1043 ds_put_char(output
, '\n');
1046 ovntrace_node_print_details(output
, &sub
->subs
, level
+ more
+ more
);
1051 ovntrace_node_prune_summary(struct ovs_list
*nodes
)
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
);
1067 ovntrace_node_print_summary(struct ds
*output
, const struct ovs_list
*nodes
,
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)) {
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
, '}');
1085 if (sub
->type
!= OVNTRACE_NODE_ACTION
) {
1086 ds_put_char(output
, ';');
1088 ds_put_char(output
, '\n');
1093 ovntrace_node_prune_hard(struct ovs_list
*nodes
)
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
);
1111 execute_load(const struct ovnact_load
*load
,
1112 const struct ovntrace_datapath
*dp
, struct flow
*uflow
,
1113 struct ovs_list
*super OVS_UNUSED
)
1115 const struct ovnact_encode_params ep
= {
1116 .lookup_port
= ovntrace_lookup_port
,
1119 uint64_t stub
[512 / 8];
1120 struct ofpbuf ofpacts
= OFPBUF_STUB_INITIALIZER(stub
);
1122 ovnacts_encode(&load
->ovnact
, sizeof *load
, &ep
, &ofpacts
);
1125 OFPACT_FOR_EACH (a
, ofpacts
.data
, ofpacts
.size
) {
1126 struct ofpact_set_field
*sf
= ofpact_get_SET_FIELD(a
);
1128 if (!mf_is_register(sf
->field
->id
)) {
1129 struct ds s
= DS_EMPTY_INITIALIZER
;
1130 ovnacts_format(&load
->ovnact
, OVNACT_LOAD_SIZE
, &s
);
1133 ovntrace_node_append(super
, OVNTRACE_NODE_MODIFY
, "%s",
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
);
1144 ofpbuf_uninit(&ofpacts
);
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
)
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
, " = ");
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
);
1162 union mf_subvalue cst
;
1163 mf_read_subfield(rsrc
, uflow
, &cst
);
1164 ds_put_hex(&s
, &cst
, sizeof cst
);
1167 ovntrace_node_append(super
, OVNTRACE_NODE_MODIFY
, "%s", ds_cstr(&s
));
1174 execute_move(const struct ovnact_move
*move
, struct flow
*uflow
,
1175 struct ovs_list
*super
)
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
);
1184 execute_exchange(const struct ovnact_move
*move
, struct flow
*uflow
,
1185 struct ovs_list
*super
)
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
);
1195 trace__(const struct ovntrace_datapath
*dp
, struct flow
*uflow
,
1196 uint8_t table_id
, enum ovnact_pipeline pipeline
,
1197 struct ovs_list
*super
);
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
);
1205 execute_output(const struct ovntrace_datapath
*dp
, struct flow
*uflow
,
1206 enum ovnact_pipeline pipeline
, struct ovs_list
*super
)
1208 uint16_t key
= uflow
->regs
[MFF_LOG_OUTPORT
- MFF_REG0
];
1210 ovntrace_node_append(super
, OVNTRACE_NODE_ERROR
,
1211 "*** output to null logical port");
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
,
1218 const char *out_name
= (port
? port
->name
1219 : mcgroup
? mcgroup
->name
1221 if (!port
&& !mcgroup
) {
1222 ovntrace_node_append(super
, OVNTRACE_NODE_ERROR
,
1223 "*** unknown port or multicast group %"PRIu16
,
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
;
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
);
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
);
1244 ovntrace_node_append(super
, OVNTRACE_NODE_MODIFY
,
1245 "output(\"%s\")", out_name
);
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;
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;
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
];
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
);
1277 if (p
->tunnel_key
!= in_key
|| allow_loopback
) {
1278 node
->always_indent
= true;
1280 egress_uflow
.regs
[MFF_LOG_OUTPORT
- MFF_REG0
] = p
->tunnel_key
;
1281 trace__(dp
, &egress_uflow
, 0, OVNACT_P_EGRESS
, &node
->subs
);
1283 ovntrace_node_append(&node
->subs
, OVNTRACE_NODE_OUTPUT
,
1284 "/* omitting output because inport == outport && !flags.loopback */");
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
;
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
);
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
);
1315 trace__(dp
, &egress_uflow
, 0, OVNACT_P_EGRESS
, &node
->subs
);
1317 ovntrace_node_append(super
, OVNTRACE_NODE_OUTPUT
,
1318 "/* omitting output because inport == outport && !flags.loopback */");
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
)
1327 struct flow cloned_flow
= *uflow
;
1329 struct ovntrace_node
*node
= ovntrace_node_append(
1330 super
, OVNTRACE_NODE_TRANSFORMATION
, "clone");
1332 trace_actions(on
->nested
, on
->nested_len
, dp
, &cloned_flow
,
1333 table_id
, pipeline
, &node
->subs
);
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
)
1341 struct flow arp_flow
= *uflow
;
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;
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. */
1357 struct ovntrace_node
*node
= ovntrace_node_append(
1358 super
, OVNTRACE_NODE_TRANSFORMATION
, "arp");
1360 trace_actions(on
->nested
, on
->nested_len
, dp
, &arp_flow
,
1361 table_id
, pipeline
, &node
->subs
);
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
)
1369 struct flow na_flow
= *uflow
;
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
;
1380 struct ovntrace_node
*node
= ovntrace_node_append(
1381 super
, OVNTRACE_NODE_TRANSFORMATION
, "nd_na");
1383 trace_actions(on
->nested
, on
->nested_len
, dp
, &na_flow
,
1384 table_id
, pipeline
, &node
->subs
);
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
)
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
);
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
)
1406 const struct ovntrace_mac_binding
*binding
1407 = ovntrace_mac_binding_find(dp
, port_key
, &ip
);
1409 uflow
->dl_dst
= binding
? binding
->mac
: eth_addr_zero
;
1411 ovntrace_node_append(super
, OVNTRACE_NODE_ACTION
,
1412 "/* MAC binding to "ETH_ADDR_FMT
". */",
1413 ETH_ADDR_ARGS(uflow
->dl_dst
));
1415 ovntrace_node_append(super
, OVNTRACE_NODE_ACTION
,
1416 "/* No MAC binding. */");
1418 ovntrace_node_append(super
, OVNTRACE_NODE_MODIFY
,
1419 "eth.dst = "ETH_ADDR_FMT
,
1420 ETH_ADDR_ARGS(uflow
->dl_dst
));
1424 execute_put_dhcp_opts(const struct ovnact_put_dhcp_opts
*pdo
,
1425 const char *name
, struct flow
*uflow
,
1426 struct ovs_list
*super
)
1428 ovntrace_node_append(
1429 super
, OVNTRACE_NODE_ERROR
,
1430 "/* We assume that this packet is DHCPDISCOVER or DHCPREQUEST. */");
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
, ", ");
1439 ds_put_format(&s
, "%s = ", o
->option
->name
);
1440 expr_constant_set_format(&o
->value
, &s
);
1442 ovntrace_node_append(super
, OVNTRACE_NODE_MODIFY
, "%s(%s)",
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
));
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
);
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
)
1466 if (pipeline
!= next
->pipeline
) {
1467 ovs_assert(next
->pipeline
== OVNACT_P_INGRESS
);
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
;
1475 trace__(dp
, uflow
, next
->ltable
, next
->pipeline
, super
);
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
)
1483 /* Figure out ct_state. */
1485 const char *comment
;
1486 if (ct_state_idx
< n_ct_states
) {
1487 state
= ct_states
[ct_state_idx
++];
1490 state
= CS_ESTABLISHED
| CS_TRACKED
;
1491 comment
= " /* default (use --ct to customize) */";
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
);
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
);
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. */
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
)
1519 ovntrace_node_append(super
, OVNTRACE_NODE_ACTION
, "drop;");
1523 struct ds s
= DS_EMPTY_INITIALIZER
;
1524 const struct ovnact
*a
;
1525 OVNACT_FOR_EACH (a
, ovnacts
, ovnacts_len
) {
1527 ovnacts_format(a
, sizeof *a
* (ovnact_next(a
) - a
), &s
);
1528 ovntrace_node_append(super
, OVNTRACE_NODE_ACTION
, "%s", ds_cstr(&s
));
1532 execute_output(dp
, uflow
, pipeline
, super
);
1536 execute_next(ovnact_get_NEXT(a
), dp
, uflow
, pipeline
, super
);
1540 execute_load(ovnact_get_LOAD(a
), dp
, uflow
, super
);
1544 execute_move(ovnact_get_MOVE(a
), uflow
, super
);
1547 case OVNACT_EXCHANGE
:
1548 execute_exchange(ovnact_get_EXCHANGE(a
), uflow
, super
);
1551 case OVNACT_DEC_TTL
:
1552 if (is_ip_any(uflow
)) {
1553 if (uflow
->nw_ttl
) {
1555 ovntrace_node_append(super
, OVNTRACE_NODE_MODIFY
,
1558 ovntrace_node_append(super
, OVNTRACE_NODE_ERROR
,
1559 "*** TTL underflow");
1562 ovntrace_node_append(super
, OVNTRACE_NODE_ERROR
,
1563 "*** TTL decrement of non-IP packet");
1567 case OVNACT_CT_NEXT
:
1568 execute_ct_next(ovnact_get_CT_NEXT(a
), dp
, uflow
, pipeline
, super
);
1571 case OVNACT_CT_COMMIT
:
1572 /* Nothing to do. */
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 "
1583 ovntrace_node_append(super
, OVNTRACE_NODE_ERROR
,
1584 "*** ct_lb action not implemented");
1587 case OVNACT_CT_CLEAR
:
1588 flow_clear_conntrack(uflow
);
1592 execute_clone(ovnact_get_CLONE(a
), dp
, uflow
, table_id
, pipeline
,
1597 execute_arp(ovnact_get_ARP(a
), dp
, uflow
, table_id
, pipeline
,
1602 execute_nd_na(ovnact_get_ND_NA(a
), dp
, uflow
, table_id
, pipeline
,
1606 case OVNACT_GET_ARP
:
1607 execute_get_mac_bind(ovnact_get_GET_ARP(a
), dp
, uflow
, super
);
1611 execute_get_mac_bind(ovnact_get_GET_ND(a
), dp
, uflow
, super
);
1614 case OVNACT_PUT_ARP
:
1616 /* Nothing to do for tracing. */
1619 case OVNACT_PUT_DHCPV4_OPTS
:
1620 execute_put_dhcp_opts(ovnact_get_PUT_DHCPV4_OPTS(a
),
1621 "put_dhcp_opts", uflow
, super
);
1624 case OVNACT_PUT_DHCPV6_OPTS
:
1625 execute_put_dhcp_opts(ovnact_get_PUT_DHCPV6_OPTS(a
),
1626 "put_dhcpv6_opts", uflow
, super
);
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. */
1647 may_omit_stage(const struct ovntrace_flow
*f
, uint8_t table_id
)
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);
1657 trace_openflow(const struct ovntrace_flow
*f
, struct ovs_list
*super
)
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
,
1667 struct ofputil_flow_stats
*fses
;
1669 int error
= vconn_dump_flows(vconn
, &fsr
, OFPUTIL_P_OF13_OXM
,
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
));
1681 struct ds s
= DS_EMPTY_INITIALIZER
;
1682 for (size_t i
= 0; i
< n_fses
; i
++) {
1684 ofp_print_flow_stats(&s
, &fses
[i
]);
1686 /* ofp_print_flow_stats() indents its output with a space.
1688 const char *p
= ds_cstr(&s
);
1689 p
+= strspn(p
, " ");
1690 ovntrace_node_append(super
, OVNTRACE_NODE_ACTION
, "%s", p
);
1694 ovntrace_node_append(super
, OVNTRACE_NODE_ERROR
,
1695 "*** no OpenFlow flows");
1698 for (size_t i
= 0; i
< n_fses
; i
++) {
1699 free(CONST_CAST(struct ofpact
*, fses
[i
].ofpacts
));
1705 trace__(const struct ovntrace_datapath
*dp
, struct flow
*uflow
,
1706 uint8_t table_id
, enum ovnact_pipeline pipeline
,
1707 struct ovs_list
*super
)
1709 const struct ovntrace_flow
*f
;
1711 f
= ovntrace_flow_lookup(dp
, uflow
, table_id
, pipeline
);
1712 if (!may_omit_stage(f
, table_id
)) {
1718 struct ds s
= DS_EMPTY_INITIALIZER
;
1719 ds_put_format(&s
, "%2d. ", table_id
);
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
);
1728 ds_put_format(&s
, "%s, priority %d, uuid %08x",
1729 f
->match_s
, f
->priority
, f
->uuid
.parts
[0]);
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
? ": " : "");
1737 struct ovntrace_node
*node
= ovntrace_node_append(
1738 super
, OVNTRACE_NODE_TABLE
, "%s", ds_cstr(&s
));
1743 trace_openflow(f
, &node
->subs
);
1745 trace_actions(f
->ovnacts
, f
->ovnacts_len
, dp
, uflow
, table_id
,
1746 pipeline
, &node
->subs
);
1751 trace(const char *dp_s
, const char *flow_s
)
1753 const struct ovntrace_datapath
*dp
= ovntrace_datapath_find_by_name(dp_s
);
1755 return xasprintf("unknown datapath \"%s\"\n", dp_s
);
1759 char *error
= expr_parse_microflow(flow_s
, &symtab
, &address_sets
,
1760 ovntrace_lookup_port
, dp
, &uflow
);
1762 char *s
= xasprintf("error parsing flow: %s\n", error
);
1767 uint32_t in_key
= uflow
.regs
[MFF_LOG_INPORT
- MFF_REG0
];
1769 VLOG_WARN("microflow does not specify ingress port");
1771 const struct ovntrace_port
*inport
= ovntrace_port_find_by_key(dp
, in_key
);
1772 const char *inport_name
= inport
? inport
->name
: "(unnamed)";
1774 struct ds output
= DS_EMPTY_INITIALIZER
;
1776 ds_put_cstr(&output
, "# ");
1777 flow_format(&output
, &uflow
);
1778 ds_put_char(&output
, '\n');
1781 int retval
= vconn_open_block(ovs
, 1 << OFP13_VERSION
, 0, &vconn
);
1783 VLOG_WARN("%s: connection failed (%s)", ovs
, ovs_strerror(retval
));
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
);
1793 bool multiple
= (detailed
+ summary
+ minimal
) > 1;
1796 ds_put_cstr(&output
, "# Detailed trace.\n");
1798 ovntrace_node_print_details(&output
, &root
, 0);
1803 ds_put_cstr(&output
, "# Summary trace.\n");
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
);
1814 ds_put_cstr(&output
, "# Minimal trace.\n");
1816 ovntrace_node_prune_hard(&root
);
1817 ovntrace_node_print_summary(&output
, &root
, 0);
1820 ovntrace_node_list_destroy(&root
);
1824 return ds_steal_cstr(&output
);
1828 ovntrace_exit(struct unixctl_conn
*conn
, int argc OVS_UNUSED
,
1829 const char *argv
[] OVS_UNUSED
, void *exiting_
)
1831 bool *exiting
= exiting_
;
1833 unixctl_command_reply(conn
, NULL
);
1837 ovntrace_trace(struct unixctl_conn
*conn
, int argc
,
1838 const char *argv
[], void *aux OVS_UNUSED
)
1840 detailed
= summary
= minimal
= false;
1841 while (argc
> 1 && argv
[1][0] == '-') {
1842 if (!strcmp(argv
[1], "--detailed")) {
1844 } else if (!strcmp(argv
[1], "--summary")) {
1846 } else if (!strcmp(argv
[1], "--minimal")) {
1848 } else if (!strcmp(argv
[1], "--all")) {
1849 detailed
= summary
= minimal
= true;
1851 unixctl_command_reply_error(conn
, "unknown option");
1857 if (!detailed
&& !summary
&& !minimal
) {
1862 unixctl_command_reply_error(
1863 conn
, "exactly 2 non-option arguments are required");
1867 char *output
= trace(argv
[1], argv
[2]);
1868 unixctl_command_reply(conn
, output
);