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