LCOV - code coverage report
Current view: top level - ovn/utilities - ovn-trace.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 29 667 4.3 %
Date: 2016-09-14 01:02:56 Functions: 3 44 6.8 %
Branches: 12 418 2.9 %

           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                 :            : }

Generated by: LCOV version 1.12