dovecot-2.1: director: Avoid infinite SYNC loops if the originat...

dovecot at dovecot.org dovecot at dovecot.org
Mon Jul 29 23:21:50 EEST 2013


details:   http://hg.dovecot.org/dovecot-2.1/rev/c9d7586d01ae
changeset: 14988:c9d7586d01ae
user:      Timo Sirainen <tss at iki.fi>
date:      Mon Jul 29 23:21:38 2013 +0300
description:
director: Avoid infinite SYNC loops if the originating director goes away for a long time.

diffstat:

 src/director/director-connection.c |  51 +++++++++++++++++++++++++++++--------
 src/director/director-host.h       |   3 ++
 src/director/director.c            |  12 +++++---
 src/director/director.h            |   3 +-
 4 files changed, 51 insertions(+), 18 deletions(-)

diffs (184 lines):

diff -r 77213cdc8d7f -r c9d7586d01ae src/director/director-connection.c
--- a/src/director/director-connection.c	Mon Jul 29 22:48:36 2013 +0300
+++ b/src/director/director-connection.c	Mon Jul 29 23:21:38 2013 +0300
@@ -73,6 +73,7 @@
 #define DIRECTOR_WAIT_DISCONNECT_SECS 10
 #define DIRECTOR_HANDSHAKE_WARN_SECS 29
 #define DIRECTOR_HANDSHAKE_BYTES_LOG_MIN_SECS (60*30)
+#define DIRECTOR_MAX_SYNC_SEQ_DUPLICATES 4
 
 #if DIRECTOR_CONNECTION_DONE_TIMEOUT_MSECS <= DIRECTOR_CONNECTION_PING_TIMEOUT_MSECS
 #  error DIRECTOR_CONNECTION_DONE_TIMEOUT_MSECS is too low
@@ -217,7 +218,7 @@
 		dir->sync_seq++;
 		director_set_ring_unsynced(dir);
 		director_sync_send(dir, dir->self_host, dir->sync_seq,
-				   DIRECTOR_VERSION_MINOR);
+				   DIRECTOR_VERSION_MINOR, ioloop_time);
 	}
 	director_connection_set_ping_timeout(conn);
 }
@@ -1084,10 +1085,11 @@
 	return 0;
 }
 
-static void
+static bool
 director_connection_sync_host(struct director_connection *conn,
 			      struct director_host *host,
-			      uint32_t seq, unsigned int minor_version)
+			      uint32_t seq, unsigned int minor_version,
+			      unsigned int timestamp)
 {
 	struct director *dir = conn->dir;
 
@@ -1099,7 +1101,7 @@
 	if (host->self) {
 		if (dir->sync_seq != seq) {
 			/* stale SYNC event */
-			return;
+			return FALSE;
 		}
 		/* sync_seq increases when we get disconnected, so we must be
 		   successfully connected to both directions */
@@ -1117,10 +1119,28 @@
 				  conn->name, seq);
 			director_set_ring_synced(dir);
 		}
-	} else if (dir->right != NULL) {
-		/* forward it to the connection on right */
-		director_sync_send(dir, host, seq, minor_version);
+	} else {
+		if (seq < host->last_sync_seq) {
+			/* stale SYNC event */
+			return FALSE;
+		} else if (host->last_sync_seq != seq ||
+			   timestamp < host->last_sync_timestamp) {
+			host->last_sync_seq = seq;
+			host->last_sync_timestamp = timestamp;
+			host->last_sync_seq_counter = 1;
+		} else if (++host->last_sync_seq_counter >
+			   DIRECTOR_MAX_SYNC_SEQ_DUPLICATES) {
+			/* we've received this too many times already */
+			return FALSE;
+		}
+
+		if (dir->right != NULL) {
+			/* forward it to the connection on right */
+			director_sync_send(dir, host, seq, minor_version,
+					   timestamp);
+		}
 	}
+	return TRUE;
 }
 
 static bool director_connection_sync(struct director_connection *conn,
@@ -1129,7 +1149,7 @@
 	struct director *dir = conn->dir;
 	struct director_host *host;
 	struct ip_addr ip;
-	unsigned int port, seq, minor_version = 0;
+	unsigned int port, seq, minor_version = 0, timestamp = ioloop_time;
 
 	if (str_array_length(args) < 3 ||
 	    !director_args_parse_ip_port(conn, args, &ip, &port) ||
@@ -1137,18 +1157,25 @@
 		director_cmd_error(conn, "Invalid parameters");
 		return FALSE;
 	}
-	if (args[3] != NULL)
+	if (args[3] != NULL) {
 		minor_version = atoi(args[3]);
+		if (args[4] != NULL && str_to_uint(args[4], &timestamp) < 0) {
+			director_cmd_error(conn, "Invalid parameters");
+			return FALSE;
+		}
+	}
 
 	/* find the originating director. if we don't see it, it was already
 	   removed and we can ignore this sync. */
 	host = director_host_lookup(dir, &ip, port);
 	if (host != NULL) {
-		director_connection_sync_host(conn, host, seq,
-					      minor_version);
+		if (!director_connection_sync_host(conn, host, seq,
+						   minor_version, timestamp))
+			return TRUE;
 	}
 
-	if (host == NULL || !host->self)
+	if ((host == NULL || !host->self) &&
+	    dir->self_host->last_sync_timestamp != ioloop_time)
 		director_resend_sync(dir);
 	return TRUE;
 }
diff -r 77213cdc8d7f -r c9d7586d01ae src/director/director-host.h
--- a/src/director/director-host.h	Mon Jul 29 22:48:36 2013 +0300
+++ b/src/director/director-host.h	Mon Jul 29 23:21:38 2013 +0300
@@ -20,6 +20,9 @@
 	   it can be ignored (or: it must be ignored to avoid potential command
 	   loops) */
 	unsigned int last_seq;
+	/* use these to avoid infinitely sending SYNCs for directors that
+	   aren't connected in the ring. */
+	unsigned int last_sync_seq, last_sync_seq_counter, last_sync_timestamp;
 	/* Last time host was detected to be down */
 	time_t last_network_failure;
 	time_t last_protocol_failure;
diff -r 77213cdc8d7f -r c9d7586d01ae src/director/director.c
--- a/src/director/director.c	Mon Jul 29 22:48:36 2013 +0300
+++ b/src/director/director.c	Mon Jul 29 23:21:38 2013 +0300
@@ -302,7 +302,8 @@
 }
 
 void director_sync_send(struct director *dir, struct director_host *host,
-			uint32_t seq, unsigned int minor_version)
+			uint32_t seq, unsigned int minor_version,
+			unsigned int timestamp)
 {
 	string_t *str;
 
@@ -311,8 +312,8 @@
 		    net_ip2addr(&host->ip), host->port, seq);
 	if (minor_version > 0 &&
 	    director_connection_get_minor_version(dir->right) > 0) {
-		/* only minor_version>0 supports this parameter */
-		str_printfa(str, "\t%u", minor_version);
+		/* only minor_version>0 supports extra parameters */
+		str_printfa(str, "\t%u\t%u", minor_version, timestamp);
 	}
 	str_append_c(str, '\n');
 	director_connection_send(dir->right, str_c(str));
@@ -329,8 +330,9 @@
 {
 	if (!dir->ring_synced && dir->left != NULL && dir->right != NULL) {
 		/* send a new SYNC in case the previous one got dropped */
+		dir->self_host->last_sync_timestamp = ioloop_time;
 		director_sync_send(dir, dir->self_host, dir->sync_seq,
-				   DIRECTOR_VERSION_MINOR);
+				   DIRECTOR_VERSION_MINOR, ioloop_time);
 		if (dir->to_sync != NULL)
 			timeout_reset(dir->to_sync);
 		return TRUE;
@@ -393,7 +395,7 @@
 		director_connection_set_synced(dir->left, FALSE);
 	director_connection_set_synced(dir->right, FALSE);
 	director_sync_send(dir, dir->self_host, dir->sync_seq,
-			   DIRECTOR_VERSION_MINOR);
+			   DIRECTOR_VERSION_MINOR, ioloop_time);
 }
 
 void director_sync_freeze(struct director *dir)
diff -r 77213cdc8d7f -r c9d7586d01ae src/director/director.h
--- a/src/director/director.h	Mon Jul 29 22:48:36 2013 +0300
+++ b/src/director/director.h	Mon Jul 29 23:21:38 2013 +0300
@@ -103,7 +103,8 @@
 void director_set_ring_unsynced(struct director *dir);
 void director_set_state_changed(struct director *dir);
 void director_sync_send(struct director *dir, struct director_host *host,
-			uint32_t seq, unsigned int minor_version);
+			uint32_t seq, unsigned int minor_version,
+			unsigned int timestamp);
 bool director_resend_sync(struct director *dir);
 
 void director_notify_ring_added(struct director_host *added_host,


More information about the dovecot-cvs mailing list