ovs-vsctl: Prevent double-free when retrying a transaction
[sliver-openvswitch.git] / utilities / ovs-vsctl.c
index 0435302..9b4d9f7 100644 (file)
@@ -507,6 +507,7 @@ struct vsctl_context {
     struct ovsdb_idl_txn *txn;
     struct ovsdb_symbol_table *symtab;
     const struct ovsrec_open_vswitch *ovs;
+    bool verified_ports;
 
     /* A command may set this member to true if some prerequisite is not met
      * and the caller should wait for something to change and then retry. */
@@ -534,6 +535,7 @@ struct vsctl_iface {
 };
 
 struct vsctl_info {
+    struct vsctl_context *ctx;
     struct shash bridges;
     struct shash ports;
     struct shash ifaces;
@@ -563,6 +565,25 @@ vsctl_context_to_string(const struct vsctl_context *ctx)
     return s;
 }
 
+static void
+verify_ports(struct vsctl_context *ctx)
+{
+    if (!ctx->verified_ports) {
+        const struct ovsrec_bridge *bridge;
+        const struct ovsrec_port *port;
+
+        ovsrec_open_vswitch_verify_bridges(ctx->ovs);
+        OVSREC_BRIDGE_FOR_EACH (bridge, ctx->idl) {
+            ovsrec_bridge_verify_ports(bridge);
+        }
+        OVSREC_PORT_FOR_EACH (port, ctx->idl) {
+            ovsrec_port_verify_interfaces(port);
+        }
+
+        ctx->verified_ports = true;
+    }
+}
+
 static struct vsctl_bridge *
 add_bridge(struct vsctl_info *b,
            struct ovsrec_bridge *br_cfg, const char *name,
@@ -627,11 +648,13 @@ free_info(struct vsctl_info *info)
 }
 
 static void
-get_info(const struct ovsrec_open_vswitch *ovs, struct vsctl_info *info)
+get_info(struct vsctl_context *ctx, struct vsctl_info *info)
 {
+    const struct ovsrec_open_vswitch *ovs = ctx->ovs;
     struct shash bridges, ports;
     size_t i;
 
+    info->ctx = ctx;
     shash_init(&info->bridges);
     shash_init(&info->ports);
     shash_init(&info->ifaces);
@@ -737,6 +760,8 @@ check_conflicts(struct vsctl_info *info, const char *name,
     struct vsctl_iface *iface;
     struct vsctl_port *port;
 
+    verify_ports(info->ctx);
+
     if (shash_find(&info->bridges, name)) {
         vsctl_fatal("%s because a bridge named %s already exists",
                     msg, name);
@@ -764,6 +789,7 @@ find_bridge(struct vsctl_info *info, const char *name, bool must_exist)
     if (must_exist && !br) {
         vsctl_fatal("no bridge named %s", name);
     }
+    ovsrec_open_vswitch_verify_bridges(info->ctx->ovs);
     return br;
 }
 
@@ -787,6 +813,7 @@ find_port(struct vsctl_info *info, const char *name, bool must_exist)
     if (must_exist && !port) {
         vsctl_fatal("no port named %s", name);
     }
+    verify_ports(info->ctx);
     return port;
 }
 
@@ -800,6 +827,7 @@ find_iface(struct vsctl_info *info, const char *name, bool must_exist)
     if (must_exist && !iface) {
         vsctl_fatal("no interface named %s", name);
     }
+    verify_ports(info->ctx);
     return iface;
 }
 
@@ -972,7 +1000,7 @@ cmd_add_br(struct vsctl_context *ctx)
                     ctx->argv[0]);
     }
 
-    get_info(ctx->ovs, &info);
+    get_info(ctx, &info);
     if (may_exist) {
         struct vsctl_bridge *br;
 
@@ -1080,7 +1108,7 @@ cmd_del_br(struct vsctl_context *ctx)
     struct vsctl_bridge *bridge;
     struct vsctl_info info;
 
-    get_info(ctx->ovs, &info);
+    get_info(ctx, &info);
     bridge = find_bridge(&info, ctx->argv[1], must_exist);
     if (bridge) {
         struct shash_node *node;
@@ -1119,7 +1147,7 @@ cmd_list_br(struct vsctl_context *ctx)
     struct vsctl_info info;
     struct svec bridges;
 
-    get_info(ctx->ovs, &info);
+    get_info(ctx, &info);
 
     svec_init(&bridges);
     SHASH_FOR_EACH (node, &info.bridges) {
@@ -1137,7 +1165,7 @@ cmd_br_exists(struct vsctl_context *ctx)
 {
     struct vsctl_info info;
 
-    get_info(ctx->ovs, &info);
+    get_info(ctx, &info);
     if (!find_bridge(&info, ctx->argv[1], false)) {
         vsctl_exit(2);
     }
@@ -1191,7 +1219,7 @@ cmd_br_set_external_id(struct vsctl_context *ctx)
     char **keys, **values;
     size_t n;
 
-    get_info(ctx->ovs, &info);
+    get_info(ctx, &info);
     bridge = find_bridge(&info, ctx->argv[1], true);
     if (bridge->br_cfg) {
         set_external_id(bridge->br_cfg->key_external_ids,
@@ -1199,6 +1227,7 @@ cmd_br_set_external_id(struct vsctl_context *ctx)
                         bridge->br_cfg->n_external_ids,
                         ctx->argv[2], ctx->argc >= 4 ? ctx->argv[3] : NULL,
                         &keys, &values, &n);
+        ovsrec_bridge_verify_external_ids(bridge->br_cfg);
         ovsrec_bridge_set_external_ids(bridge->br_cfg, keys, values, n);
     } else {
         char *key = xasprintf("fake-bridge-%s", ctx->argv[2]);
@@ -1208,6 +1237,7 @@ cmd_br_set_external_id(struct vsctl_context *ctx)
                         port->port_cfg->n_external_ids,
                         key, ctx->argc >= 4 ? ctx->argv[3] : NULL,
                         &keys, &values, &n);
+        ovsrec_port_verify_external_ids(port->port_cfg);
         ovsrec_port_set_external_ids(port->port_cfg, keys, values, n);
         free(key);
     }
@@ -1246,9 +1276,10 @@ cmd_br_get_external_id(struct vsctl_context *ctx)
     struct vsctl_info info;
     struct vsctl_bridge *bridge;
 
-    get_info(ctx->ovs, &info);
+    get_info(ctx, &info);
     bridge = find_bridge(&info, ctx->argv[1], true);
     if (bridge->br_cfg) {
+        ovsrec_bridge_verify_external_ids(bridge->br_cfg);
         get_external_id(bridge->br_cfg->key_external_ids,
                         bridge->br_cfg->value_external_ids,
                         bridge->br_cfg->n_external_ids,
@@ -1256,6 +1287,7 @@ cmd_br_get_external_id(struct vsctl_context *ctx)
                         &ctx->output);
     } else {
         struct vsctl_port *port = shash_find_data(&info.ports, ctx->argv[1]);
+        ovsrec_port_verify_external_ids(port->port_cfg);
         get_external_id(port->port_cfg->key_external_ids,
                         port->port_cfg->value_external_ids,
                         port->port_cfg->n_external_ids,
@@ -1273,8 +1305,9 @@ cmd_list_ports(struct vsctl_context *ctx)
     struct vsctl_info info;
     struct svec ports;
 
-    get_info(ctx->ovs, &info);
+    get_info(ctx, &info);
     br = find_bridge(&info, ctx->argv[1], true);
+    ovsrec_bridge_verify_ports(br->br_cfg ? br->br_cfg : br->parent->br_cfg);
 
     svec_init(&ports);
     SHASH_FOR_EACH (node, &info.ports) {
@@ -1303,7 +1336,7 @@ add_port(struct vsctl_context *ctx,
     struct ovsrec_port *port;
     size_t i;
 
-    get_info(ctx->ovs, &info);
+    get_info(ctx, &info);
     if (may_exist) {
         struct vsctl_port *port;
 
@@ -1423,7 +1456,7 @@ cmd_del_port(struct vsctl_context *ctx)
     struct vsctl_port *port;
     struct vsctl_info info;
 
-    get_info(ctx->ovs, &info);
+    get_info(ctx, &info);
     if (!with_iface) {
         port = find_port(&info, ctx->argv[ctx->argc - 1], must_exist);
     } else {
@@ -1472,7 +1505,7 @@ cmd_port_to_br(struct vsctl_context *ctx)
     struct vsctl_port *port;
     struct vsctl_info info;
 
-    get_info(ctx->ovs, &info);
+    get_info(ctx, &info);
     port = find_port(&info, ctx->argv[1], true);
     ds_put_format(&ctx->output, "%s\n", port->bridge->name);
     free_info(&info);
@@ -1484,7 +1517,7 @@ cmd_br_to_vlan(struct vsctl_context *ctx)
     struct vsctl_bridge *bridge;
     struct vsctl_info info;
 
-    get_info(ctx->ovs, &info);
+    get_info(ctx, &info);
     bridge = find_bridge(&info, ctx->argv[1], true);
     ds_put_format(&ctx->output, "%d\n", bridge->vlan);
     free_info(&info);
@@ -1496,7 +1529,7 @@ cmd_br_to_parent(struct vsctl_context *ctx)
     struct vsctl_bridge *bridge;
     struct vsctl_info info;
 
-    get_info(ctx->ovs, &info);
+    get_info(ctx, &info);
     bridge = find_bridge(&info, ctx->argv[1], true);
     if (bridge->parent) {
         bridge = bridge->parent;
@@ -1513,8 +1546,9 @@ cmd_list_ifaces(struct vsctl_context *ctx)
     struct vsctl_info info;
     struct svec ifaces;
 
-    get_info(ctx->ovs, &info);
+    get_info(ctx, &info);
     br = find_bridge(&info, ctx->argv[1], true);
+    verify_ports(ctx);
 
     svec_init(&ifaces);
     SHASH_FOR_EACH (node, &info.ifaces) {
@@ -1537,12 +1571,25 @@ cmd_iface_to_br(struct vsctl_context *ctx)
     struct vsctl_iface *iface;
     struct vsctl_info info;
 
-    get_info(ctx->ovs, &info);
+    get_info(ctx, &info);
     iface = find_iface(&info, ctx->argv[1], true);
     ds_put_format(&ctx->output, "%s\n", iface->port->bridge->name);
     free_info(&info);
 }
 
+static void
+verify_controllers(struct ovsrec_bridge *bridge)
+{
+    if (bridge) {
+        size_t i;
+
+        ovsrec_bridge_verify_controller(bridge);
+        for (i = 0; i < bridge->n_controller; i++) {
+            ovsrec_controller_verify_target(bridge->controller[i]);
+        }
+    }
+}
+
 static void
 cmd_get_controller(struct vsctl_context *ctx)
 {
@@ -1551,8 +1598,9 @@ cmd_get_controller(struct vsctl_context *ctx)
     struct svec targets;
     size_t i;
 
-    get_info(ctx->ovs, &info);
+    get_info(ctx, &info);
     br = find_bridge(&info, ctx->argv[1], true);
+    verify_controllers(br->br_cfg);
 
     /* Print the targets in sorted order for reproducibility. */
     svec_init(&targets);
@@ -1586,8 +1634,9 @@ cmd_del_controller(struct vsctl_context *ctx)
     struct vsctl_info info;
     struct vsctl_bridge *br;
 
-    get_info(ctx->ovs, &info);
+    get_info(ctx, &info);
     br = find_real_bridge(&info, ctx->argv[1], true);
+    verify_controllers(br->br_cfg);
 
     if (br->ctrl) {
         delete_controllers(br->ctrl, br->n_ctrl);
@@ -1620,8 +1669,9 @@ cmd_set_controller(struct vsctl_context *ctx)
     struct ovsrec_controller **controllers;
     size_t n;
 
-    get_info(ctx->ovs, &info);
+    get_info(ctx, &info);
     br = find_real_bridge(&info, ctx->argv[1], true);
+    verify_controllers(br->br_cfg);
 
     delete_controllers(br->ctrl, br->n_ctrl);
 
@@ -1639,9 +1689,12 @@ cmd_get_fail_mode(struct vsctl_context *ctx)
     struct vsctl_info info;
     struct vsctl_bridge *br;
 
-    get_info(ctx->ovs, &info);
+    get_info(ctx, &info);
     br = find_bridge(&info, ctx->argv[1], true);
 
+    if (br->br_cfg) {
+        ovsrec_bridge_verify_fail_mode(br->br_cfg);
+    }
     if (br->fail_mode && strlen(br->fail_mode)) {
         ds_put_format(&ctx->output, "%s\n", br->fail_mode);
     }
@@ -1655,7 +1708,7 @@ cmd_del_fail_mode(struct vsctl_context *ctx)
     struct vsctl_info info;
     struct vsctl_bridge *br;
 
-    get_info(ctx->ovs, &info);
+    get_info(ctx, &info);
     br = find_real_bridge(&info, ctx->argv[1], true);
 
     ovsrec_bridge_set_fail_mode(br->br_cfg, NULL);
@@ -1670,7 +1723,7 @@ cmd_set_fail_mode(struct vsctl_context *ctx)
     struct vsctl_bridge *br;
     const char *fail_mode = ctx->argv[2];
 
-    get_info(ctx->ovs, &info);
+    get_info(ctx, &info);
     br = find_real_bridge(&info, ctx->argv[1], true);
 
     if (strcmp(fail_mode, "standalone") && strcmp(fail_mode, "secure")) {
@@ -1687,7 +1740,13 @@ cmd_get_ssl(struct vsctl_context *ctx)
 {
     struct ovsrec_ssl *ssl = ctx->ovs->ssl;
 
+    ovsrec_open_vswitch_verify_ssl(ctx->ovs);
     if (ssl) {
+        ovsrec_ssl_verify_private_key(ssl);
+        ovsrec_ssl_verify_certificate(ssl);
+        ovsrec_ssl_verify_ca_cert(ssl);
+        ovsrec_ssl_verify_bootstrap_ca_cert(ssl);
+
         ds_put_format(&ctx->output, "Private key: %s\n", ssl->private_key);
         ds_put_format(&ctx->output, "Certificate: %s\n", ssl->certificate);
         ds_put_format(&ctx->output, "CA Certificate: %s\n", ssl->ca_cert);
@@ -1702,6 +1761,7 @@ cmd_del_ssl(struct vsctl_context *ctx)
     struct ovsrec_ssl *ssl = ctx->ovs->ssl;
 
     if (ssl) {
+        ovsrec_open_vswitch_verify_ssl(ctx->ovs);
         ovsrec_ssl_delete(ssl);
         ovsrec_open_vswitch_set_ssl(ctx->ovs, NULL);
     }
@@ -1713,6 +1773,7 @@ cmd_set_ssl(struct vsctl_context *ctx)
     bool bootstrap = shash_find(&ctx->options, "--bootstrap");
     struct ovsrec_ssl *ssl = ctx->ovs->ssl;
 
+    ovsrec_open_vswitch_verify_ssl(ctx->ovs);
     if (ssl) {
         ovsrec_ssl_delete(ssl);
     }
@@ -1897,6 +1958,7 @@ get_row_by_id(struct vsctl_context *ctx, const struct vsctl_table_class *table,
     if (id->uuid_column) {
         const struct ovsdb_datum *uuid;
 
+        ovsdb_idl_txn_verify(referrer, id->uuid_column);
         uuid = ovsdb_idl_get(referrer, id->uuid_column,
                              OVSDB_TYPE_UUID, OVSDB_TYPE_VOID);
         if (uuid->n == 1) {
@@ -2170,6 +2232,7 @@ cmd_get(struct vsctl_context *ctx)
                                             &column, &key_string,
                                             NULL, NULL, 0, NULL));
 
+        ovsdb_idl_txn_verify(row, column);
         datum = ovsdb_idl_read(row, column);
         if (key_string) {
             union ovsdb_atom key;
@@ -2362,6 +2425,7 @@ cmd_add(struct vsctl_context *ctx)
                     type->value.type == OVSDB_TYPE_VOID ? "values" : "pairs",
                     column->name, table->class->name, type->n_max);
     }
+    ovsdb_idl_txn_verify(row, column);
     ovsdb_idl_txn_write(row, column, &old);
 }
 
@@ -2410,6 +2474,7 @@ cmd_remove(struct vsctl_context *ctx)
                     type->value.type == OVSDB_TYPE_VOID ? "values" : "pairs",
                     column->name, table->class->name, type->n_min);
     }
+    ovsdb_idl_txn_verify(row, column);
     ovsdb_idl_txn_write(row, column, &old);
 }
 
@@ -2650,6 +2715,7 @@ vsctl_context_init(struct vsctl_context *ctx, struct vsctl_command *command,
     ctx->txn = txn;
     ctx->ovs = ovs;
     ctx->symtab = symtab;
+    ctx->verified_ports = false;
 
     ctx->try_again = false;
 }
@@ -2725,7 +2791,7 @@ do_vsctl(const char *args, struct vsctl_command *commands, size_t n_commands,
     }
     error = xstrdup(ovsdb_idl_txn_get_error(txn));
     ovsdb_idl_txn_destroy(txn);
-    the_idl_txn = NULL;
+    txn = the_idl_txn = NULL;
 
     unused = ovsdb_symbol_table_find_unused(symtab);
     if (unused) {
@@ -2816,8 +2882,10 @@ do_vsctl(const char *args, struct vsctl_command *commands, size_t n_commands,
 try_again:
     /* Our transaction needs to be rerun, or a prerequisite was not met.  Free
      * resources and return so that the caller can try again. */
-    ovsdb_idl_txn_abort(txn);
-    ovsdb_idl_txn_destroy(txn);
+    if (txn) {
+        ovsdb_idl_txn_abort(txn);
+        ovsdb_idl_txn_destroy(txn);
+    }
     ovsdb_symbol_table_destroy(symtab);
     for (c = commands; c < &commands[n_commands]; c++) {
         ds_destroy(&c->output);