[ovs-dev] [PATCH 3/3] ovsdb: Prevent OVSDB server from replicating itself.

Andy Zhou azhou at ovn.org
Wed Feb 8 04:40:22 UTC 2017


Replication OVSDB server from itself is usually caused by configuration
errors. Such configuration errors can lead to OVSDB server data loss.
See "reported-at" for more details.

This patch adds logics that prevent OVSDB server from replicating
itself.

Reported-by: Guishuai Li <ligs at dtdream.com>
Reported-at: https://mail.openvswitch.org/pipermail/ovs-dev/2017-January/326963.html
Suggested-by: Ben Pfaff <blp at ovn.org>
Signed-off-by: Andy Zhou <azhou at ovn.org>
---
 ovsdb/jsonrpc-server.c  |  8 +++++++
 ovsdb/ovsdb-server.1.in | 29 ++++++++++++++++++++++++
 ovsdb/ovsdb-server.c    | 24 ++++++++++++++------
 ovsdb/replication.c     | 59 +++++++++++++++++++++++++++++++++++++++++--------
 ovsdb/replication.h     |  3 ++-
 tests/ovsdb-server.at   | 32 +++++++++++++++++++++++++++
 6 files changed, 138 insertions(+), 17 deletions(-)

diff --git a/ovsdb/jsonrpc-server.c b/ovsdb/jsonrpc-server.c
index dca6666..cff6ea7 100644
--- a/ovsdb/jsonrpc-server.c
+++ b/ovsdb/jsonrpc-server.c
@@ -944,6 +944,14 @@ ovsdb_jsonrpc_session_got_request(struct ovsdb_jsonrpc_session *s,
         }
         reply = jsonrpc_create_reply(json_array_create(dbs, n_dbs),
                                      request->id);
+    } else if (!strcmp(request->method, "get_server_id")) {
+        const struct uuid *uuid;
+        struct json *result;
+
+        uuid = ovsdb_server_get_uuid(s->up.server);
+        result = json_string_create_nocopy(xasprintf(UUID_FMT,
+                                                    UUID_ARGS(uuid)));
+        reply = jsonrpc_create_reply(result, request->id);
     } else if (!strcmp(request->method, "lock")) {
         reply = ovsdb_jsonrpc_session_lock(s, request, OVSDB_LOCK_WAIT);
     } else if (!strcmp(request->method, "steal")) {
diff --git a/ovsdb/ovsdb-server.1.in b/ovsdb/ovsdb-server.1.in
index a55af39..b0f488e 100644
--- a/ovsdb/ovsdb-server.1.in
+++ b/ovsdb/ovsdb-server.1.in
@@ -577,6 +577,35 @@ Initial views of rows are not presented in update2 notifications,
 but in the response object to the monitor_cond request. The formatting
 of the <table-updates2> object, however, is the same in either case.
 .
+.IP "4.1.15. Get Server ID"
+A new RPC method added in Open vSWitch version 2.7. The request contains
+the following members:
+.
+.PP
+.RS
+.nf
+"method": "get_server_id"
+"params": null
+"id": <nonnull-json-value>
+.fi
+.RE
+.
+.IP
+The response object contains the following members:
+.
+.PP
+.RS
+.nf
+"result": "<server_id>"
+"error": null
+"id": same "id" as request
+.fi
+.RE
+.
+.IP
+<server_id> is JSON string that contains a UUID that uniquely identifies
+the OVSDB server instance.
+.
 .IP "5.1. Notation"
 For <condition>, RFC 7047 only allows the use of \fB!=\fR, \fB==\fR,
 \fBincludes\fR, and \fBexcludes\fR operators with set types.  Open
diff --git a/ovsdb/ovsdb-server.c b/ovsdb/ovsdb-server.c
index 9b669c9..52af4d3 100644
--- a/ovsdb/ovsdb-server.c
+++ b/ovsdb/ovsdb-server.c
@@ -139,9 +139,9 @@ static void load_config(FILE *config_file, struct sset *remotes,
 
 static void
 ovsdb_replication_init(const char *sync_from, const char *exclude,
-                       struct shash *all_dbs)
+                       struct shash *all_dbs, const struct uuid *server_uuid)
 {
-    replication_init(sync_from, exclude);
+    replication_init(sync_from, exclude, server_uuid);
     struct shash_node *node;
     SHASH_FOR_EACH (node, all_dbs) {
         struct db *db = node->data;
@@ -425,7 +425,9 @@ main(int argc, char *argv[])
                              ovsdb_server_disable_monitor_cond, jsonrpc);
 
     if (is_backup) {
-        ovsdb_replication_init(sync_from, sync_exclude, &all_dbs);
+        const struct uuid *server_uuid;
+        server_uuid = ovsdb_jsonrpc_server_get_uuid(jsonrpc);
+        ovsdb_replication_init(sync_from, sync_exclude, &all_dbs, server_uuid);
     }
 
     main_loop(jsonrpc, &all_dbs, unixctl, &remotes, run_process, &exiting,
@@ -1185,8 +1187,10 @@ ovsdb_server_connect_active_ovsdb_server(struct unixctl_conn *conn,
     if ( !*config->sync_from) {
         msg = "Unable to connect: active server is not specified.\n";
     } else {
+        const struct uuid *server_uuid;
+        server_uuid = ovsdb_jsonrpc_server_get_uuid(config->jsonrpc);
         ovsdb_replication_init(*config->sync_from, *config->sync_exclude,
-                               config->all_dbs);
+                               config->all_dbs, server_uuid);
         if (!*config->is_backup) {
             *config->is_backup = true;
             save_config(config);
@@ -1223,8 +1227,10 @@ ovsdb_server_set_sync_exclude_tables(struct unixctl_conn *conn,
         *config->sync_exclude = xstrdup(argv[1]);
         save_config(config);
         if (*config->is_backup) {
+            const struct uuid *server_uuid;
+            server_uuid = ovsdb_jsonrpc_server_get_uuid(config->jsonrpc);
             ovsdb_replication_init(*config->sync_from, *config->sync_exclude,
-                                   config->all_dbs);
+                                   config->all_dbs, server_uuid);
         }
         err = set_blacklist_tables(argv[1], false);
     }
@@ -1430,8 +1436,10 @@ ovsdb_server_add_database(struct unixctl_conn *conn, int argc OVS_UNUSED,
     if (!error) {
         save_config(config);
         if (*config->is_backup) {
+            const struct uuid *server_uuid;
+            server_uuid = ovsdb_jsonrpc_server_get_uuid(config->jsonrpc);
             ovsdb_replication_init(*config->sync_from, *config->sync_exclude,
-                                   config->all_dbs);
+                                   config->all_dbs, server_uuid);
         }
         unixctl_command_reply(conn, NULL);
     } else {
@@ -1464,8 +1472,10 @@ ovsdb_server_remove_database(struct unixctl_conn *conn, int argc OVS_UNUSED,
 
     save_config(config);
     if (*config->is_backup) {
+        const struct uuid *server_uuid;
+        server_uuid = ovsdb_jsonrpc_server_get_uuid(config->jsonrpc);
         ovsdb_replication_init(*config->sync_from, *config->sync_exclude,
-                               config->all_dbs);
+                               config->all_dbs, server_uuid);
     }
     unixctl_command_reply(conn, NULL);
 }
diff --git a/ovsdb/replication.c b/ovsdb/replication.c
index 1c77b18..81fc0fc 100644
--- a/ovsdb/replication.c
+++ b/ovsdb/replication.c
@@ -1,6 +1,6 @@
 /*
  * (c) Copyright 2016 Hewlett Packard Enterprise Development LP
- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2016 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2016, 2017 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -34,10 +34,12 @@
 #include "svec.h"
 #include "table.h"
 #include "transaction.h"
+#include "uuid.h"
 
 VLOG_DEFINE_THIS_MODULE(replication);
 
 static char *sync_from;
+static struct uuid server_uuid;
 static struct jsonrpc_session *session;
 static unsigned int session_seqno = UINT_MAX;
 
@@ -88,6 +90,7 @@ void request_ids_clear(void);
 
 enum ovsdb_replication_state {
     RPL_S_INIT,
+    RPL_S_SERVER_ID_REQUESTED,
     RPL_S_DB_REQUESTED,
     RPL_S_SCHEMA_REQUESTED,
     RPL_S_MONITOR_REQUESTED,
@@ -110,7 +113,8 @@ static struct ovsdb* find_db(const char *db_name);
 
 
 void
-replication_init(const char *sync_from_, const char *exclude_tables)
+replication_init(const char *sync_from_, const char *exclude_tables,
+                 const struct uuid *server)
 {
     free(sync_from);
     sync_from = xstrdup(sync_from_);
@@ -128,6 +132,10 @@ replication_init(const char *sync_from_, const char *exclude_tables)
 
     session = jsonrpc_session_open(sync_from, true);
     session_seqno = UINT_MAX;
+
+    /* Keep a copy of local server uuid.  */
+    memcpy(&server_uuid, server, sizeof(*server));
+
     state = RPL_S_INIT;
 }
 
@@ -155,16 +163,13 @@ replication_run(void)
             session_seqno = seqno;
             request_ids_clear();
             struct jsonrpc_msg *request;
-            request = jsonrpc_create_request("list_dbs",
+            request = jsonrpc_create_request("get_server_id",
                                              json_array_create_empty(), NULL);
             request_ids_add(request->id, NULL);
             jsonrpc_session_send(session, request);
 
-            replication_dbs_destroy();
-            replication_dbs = replication_db_clone(&local_dbs);
-
-            state = RPL_S_DB_REQUESTED;
-            VLOG_DBG("Send list_dbs request");
+            state = RPL_S_SERVER_ID_REQUESTED;
+            VLOG_DBG("send server ID request.");
         }
 
         msg = jsonrpc_session_recv(session);
@@ -197,10 +202,45 @@ replication_run(void)
             }
 
             switch (state) {
+            case RPL_S_SERVER_ID_REQUESTED: {
+                struct uuid uuid;
+                if (msg->result->type != JSON_STRING ||
+                    !uuid_from_string(&uuid, json_string(msg->result))) {
+                    struct ovsdb_error *error;
+                    error = ovsdb_error("get_server_id failed",
+                                        "Server ID is not valid UUID");
+
+                    ovsdb_error_assert(error);
+                    state = RPL_S_ERR;
+                    break;
+                }
+
+                if (uuid_equals(&uuid, &server_uuid)) {
+                    struct ovsdb_error *error;
+                    error = ovsdb_error("Server ID check failed",
+                                        "Self replicating is not allowed");
+
+                    ovsdb_error_assert(error);
+                    state = RPL_S_ERR;
+                    break;
+                }
+
+                struct jsonrpc_msg *request;
+                request = jsonrpc_create_request("list_dbs",
+                                                 json_array_create_empty(),
+                                                 NULL);
+                request_ids_add(request->id, NULL);
+                jsonrpc_session_send(session, request);
+
+                replication_dbs_destroy();
+                replication_dbs = replication_db_clone(&local_dbs);
+                state = RPL_S_DB_REQUESTED;
+                break;
+            }
             case RPL_S_DB_REQUESTED:
                 if (msg->result->type != JSON_ARRAY) {
                     struct ovsdb_error *error;
-                    error = ovsdb_error("list-dbs failed",
+                    error = ovsdb_error("list_dbs failed",
                                         "list_dbs response is not array");
                     ovsdb_error_assert(error);
                     state = RPL_S_ERR;
@@ -802,6 +842,7 @@ replication_status(void)
     if (alive) {
         switch(state) {
         case RPL_S_INIT:
+        case RPL_S_SERVER_ID_REQUESTED:
         case RPL_S_DB_REQUESTED:
         case RPL_S_SCHEMA_REQUESTED:
         case RPL_S_MONITOR_REQUESTED:
diff --git a/ovsdb/replication.h b/ovsdb/replication.h
index 622d355..1f9c32f 100644
--- a/ovsdb/replication.h
+++ b/ovsdb/replication.h
@@ -44,7 +44,8 @@ struct ovsdb;
  *    used mainly by uinxctl commands.
  */
 
-void replication_init(const char *sync_from, const char *exclude_tables);
+void replication_init(const char *sync_from, const char *exclude_tables,
+                      const struct uuid *server);
 void replication_run(void);
 void replication_wait(void);
 void replication_destroy(void);
diff --git a/tests/ovsdb-server.at b/tests/ovsdb-server.at
index 855c237..e72a65b 100644
--- a/tests/ovsdb-server.at
+++ b/tests/ovsdb-server.at
@@ -1440,6 +1440,38 @@ dnl OVSDB_SERVER_SHUTDOWN
 dnl OVSDB_SERVER_SHUTDOWN2
 AT_CLEANUP
 
+#ovsdb-server prevent self replicating
+AT_SETUP([ovsdb-server prevent self replicating])
+AT_KEYWORDS([ovsdb server replication])
+replication_schema > schema
+AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore])
+
+dnl Add some data to both DBs
+AT_CHECK([ovsdb-tool transact db \
+'[["mydb",
+  {"op": "insert",
+   "table": "a",
+   "row": {"number": 9, "name": "nine"}}]]'], [0], [ignore], [ignore])
+
+dnl Start 'db', then try to be a back up server of itself.
+AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server.log --pidfile="`pwd`"/pid --remote=punix:db.sock --unixctl="`pwd`"/unixctl db --sync-from=unix:db.sock --active ], [0], [ignore], [ignore])
+on_exit 'test ! -e pid || kill `cat pid`'
+
+dnl Save the current content
+AT_CHECK([ovsdb-client dump unix:db.sock], [0], [stdout])
+cp stdout dump1
+
+AT_CHECK([ovs-appctl -t "`pwd`"/unixctl ovsdb-server/connect-active-ovsdb-server])
+dnl Check that self replicating is blocked.
+AT_CHECK([grep "Self replicating is not allowed" ovsdb-server.log], [0], [stdout])
+
+dnl Check current DB content is preserved.
+AT_CHECK([ovsdb-client dump unix:db.sock], [0], [stdout])
+cat stdout > dump2
+
+AT_CHECK([diff dump1 dump2])
+AT_CLEANUP
+
 AT_SETUP([ovsdb-server/read-only db:ptcp connection])
 AT_KEYWORDS([ovsdb server read-only])
 AT_DATA([schema],
-- 
1.9.1



More information about the dev mailing list