summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--redis.conf29
-rw-r--r--src/config.c19
-rw-r--r--src/networking.c1
-rw-r--r--src/replication.c62
-rw-r--r--src/server.c12
-rw-r--r--src/server.h23
6 files changed, 129 insertions, 17 deletions
diff --git a/redis.conf b/redis.conf
index 05158b4e7..b9217fdb4 100644
--- a/redis.conf
+++ b/redis.conf
@@ -443,6 +443,35 @@ slave-priority 100
# By default min-slaves-to-write is set to 0 (feature disabled) and
# min-slaves-max-lag is set to 10.
+# A Redis master is able to list the address and port of the attached
+# slaves in different ways. For example the "INFO replication" section
+# offers this information, which is used, among other tools, by
+# Redis Sentinel in order to discover slave instances.
+# Another place where this info is available is in the output of the
+# "ROLE" command of a masteer.
+#
+# The listed IP and address normally reported by a slave is obtained
+# in the following way:
+#
+# IP: The address is auto detected by checking the peer address
+# of the socket used by the slave to connect with the master.
+#
+# Port: The port is communicated by the slave during the replication
+# handshake, and is normally the port that the slave is using to
+# list for connections.
+#
+# However when port forwarding or Network Address Translation (NAT) is
+# used, the slave may be actually reachable via different IP and port
+# pairs. The following two options can be used by a slave in order to
+# report to its master a specific set of IP and port, so that both INFO
+# and ROLE will report those values.
+#
+# There is no need to use both the options if you need to override just
+# the port or the IP address.
+#
+# slave-announce-ip 5.5.5.5
+# slave-announce-port 1234
+
################################## SECURITY ###################################
# Require clients to issue AUTH <PASSWORD> before processing any other
diff --git a/src/config.c b/src/config.c
index 686f80cfd..dd21a0aca 100644
--- a/src/config.c
+++ b/src/config.c
@@ -633,6 +633,16 @@ void loadServerConfigFromString(char *config) {
}
} else if (!strcasecmp(argv[0],"slave-priority") && argc == 2) {
server.slave_priority = atoi(argv[1]);
+ } else if (!strcasecmp(argv[0],"slave-announce-ip") && argc == 2) {
+ zfree(server.slave_announce_ip);
+ server.slave_announce_ip = zstrdup(argv[1]);
+ } else if (!strcasecmp(argv[0],"slave-announce-port") && argc == 2) {
+ server.slave_announce_port = atoi(argv[1]);
+ if (server.slave_announce_port < 0 ||
+ server.slave_announce_port > 65535)
+ {
+ err = "Invalid port"; goto loaderr;
+ }
} else if (!strcasecmp(argv[0],"min-slaves-to-write") && argc == 2) {
server.repl_min_slaves_to_write = atoi(argv[1]);
if (server.repl_min_slaves_to_write < 0) {
@@ -925,6 +935,9 @@ void configSetCommand(client *c) {
if (flags == -1) goto badfmt;
server.notify_keyspace_events = flags;
+ } config_set_special_field("slave-announce-ip") {
+ zfree(server.slave_announce_ip);
+ server.slave_announce_ip = ((char*)o->ptr)[0] ? zstrdup(o->ptr) : NULL;
/* Boolean fields.
* config_set_bool_field(name,var). */
@@ -1014,6 +1027,8 @@ void configSetCommand(client *c) {
} config_set_numerical_field(
"slave-priority",server.slave_priority,0,LLONG_MAX) {
} config_set_numerical_field(
+ "slave-announce-port",server.slave_announce_port,0,65535) {
+ } config_set_numerical_field(
"min-slaves-to-write",server.repl_min_slaves_to_write,0,LLONG_MAX) {
refreshGoodSlavesCount();
} config_set_numerical_field(
@@ -1133,6 +1148,7 @@ void configGetCommand(client *c) {
config_get_string_field("unixsocket",server.unixsocket);
config_get_string_field("logfile",server.logfile);
config_get_string_field("pidfile",server.pidfile);
+ config_get_string_field("slave-announce-ip",server.slave_announce_ip);
/* Numerical values */
config_get_numerical_field("maxmemory",server.maxmemory);
@@ -1177,6 +1193,7 @@ void configGetCommand(client *c) {
config_get_numerical_field("maxclients",server.maxclients);
config_get_numerical_field("watchdog-period",server.watchdog_period);
config_get_numerical_field("slave-priority",server.slave_priority);
+ config_get_numerical_field("slave-announce-port",server.slave_announce_port);
config_get_numerical_field("min-slaves-to-write",server.repl_min_slaves_to_write);
config_get_numerical_field("min-slaves-max-lag",server.repl_min_slaves_max_lag);
config_get_numerical_field("hz",server.hz);
@@ -1865,6 +1882,7 @@ int rewriteConfig(char *path) {
rewriteConfigOctalOption(state,"unixsocketperm",server.unixsocketperm,CONFIG_DEFAULT_UNIX_SOCKET_PERM);
rewriteConfigNumericalOption(state,"timeout",server.maxidletime,CONFIG_DEFAULT_CLIENT_TIMEOUT);
rewriteConfigNumericalOption(state,"tcp-keepalive",server.tcpkeepalive,CONFIG_DEFAULT_TCP_KEEPALIVE);
+ rewriteConfigNumericalOption(state,"slave-announce-port",server.slave_announce_port,CONFIG_DEFAULT_SLAVE_ANNOUNCE_PORT);
rewriteConfigEnumOption(state,"loglevel",server.verbosity,loglevel_enum,CONFIG_DEFAULT_VERBOSITY);
rewriteConfigStringOption(state,"logfile",server.logfile,CONFIG_DEFAULT_LOGFILE);
rewriteConfigYesNoOption(state,"syslog-enabled",server.syslog_enabled,CONFIG_DEFAULT_SYSLOG_ENABLED);
@@ -1878,6 +1896,7 @@ int rewriteConfig(char *path) {
rewriteConfigStringOption(state,"dbfilename",server.rdb_filename,CONFIG_DEFAULT_RDB_FILENAME);
rewriteConfigDirOption(state);
rewriteConfigSlaveofOption(state);
+ rewriteConfigStringOption(state,"slave-announce-ip",server.slave_announce_ip,CONFIG_DEFAULT_SLAVE_ANNOUNCE_IP);
rewriteConfigStringOption(state,"masterauth",server.masterauth,NULL);
rewriteConfigStringOption(state,"cluster-announce-ip",server.cluster_announce_ip,NULL);
rewriteConfigYesNoOption(state,"slave-serve-stale-data",server.repl_serve_stale_data,CONFIG_DEFAULT_SLAVE_SERVE_STALE_DATA);
diff --git a/src/networking.c b/src/networking.c
index 242022a03..723cd599d 100644
--- a/src/networking.c
+++ b/src/networking.c
@@ -109,6 +109,7 @@ client *createClient(int fd) {
c->repl_ack_off = 0;
c->repl_ack_time = 0;
c->slave_listening_port = 0;
+ c->slave_ip[0] = '\0';
c->slave_capa = SLAVE_CAPA_NONE;
c->reply = listCreate();
c->reply_bytes = 0;
diff --git a/src/replication.c b/src/replication.c
index 1d40677e5..471ad1a03 100644
--- a/src/replication.c
+++ b/src/replication.c
@@ -47,7 +47,7 @@ int cancelReplicationHandshake(void);
/* Return the pointer to a string representing the slave ip:listening_port
* pair. Mostly useful for logging, since we want to log a slave using its
- * IP address and it's listening port which is more clear for the user, for
+ * IP address and its listening port which is more clear for the user, for
* example: "Closing connection with slave 10.1.2.3:6380". */
char *replicationGetSlaveName(client *c) {
static char buf[NET_PEER_ID_LEN];
@@ -55,7 +55,12 @@ char *replicationGetSlaveName(client *c) {
ip[0] = '\0';
buf[0] = '\0';
- if (anetPeerToString(c->fd,ip,sizeof(ip),NULL) != -1) {
+ if (c->slave_ip[0] != '\0' ||
+ anetPeerToString(c->fd,ip,sizeof(ip),NULL) != -1)
+ {
+ /* Note that the 'ip' buffer is always larger than 'c->slave_ip' */
+ if (c->slave_ip[0] != '\0') memcpy(ip,c->slave_ip,sizeof(c->slave_ip));
+
if (c->slave_listening_port)
anetFormatAddr(buf,sizeof(buf),ip,c->slave_listening_port);
else
@@ -717,6 +722,15 @@ void replconfCommand(client *c) {
&port,NULL) != C_OK))
return;
c->slave_listening_port = port;
+ } else if (!strcasecmp(c->argv[j]->ptr,"ip-address")) {
+ sds ip = c->argv[j+1]->ptr;
+ if (sdslen(ip) < sizeof(c->slave_ip)) {
+ memcpy(c->slave_ip,ip,sdslen(ip)+1);
+ } else {
+ addReplyErrorFormat(c,"REPLCONF ip-address provided by "
+ "slave instance is too long: %zd bytes", sdslen(ip));
+ return;
+ }
} else if (!strcasecmp(c->argv[j]->ptr,"capa")) {
/* Ignore capabilities not understood by this master. */
if (!strcasecmp(c->argv[j+1]->ptr,"eof"))
@@ -1462,7 +1476,8 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
/* Set the slave port, so that Master's INFO command can list the
* slave listening port correctly. */
if (server.repl_state == REPL_STATE_SEND_PORT) {
- sds port = sdsfromlonglong(server.port);
+ sds port = sdsfromlonglong(server.slave_announce_port ?
+ server.slave_announce_port : server.port);
err = sendSynchronousCommand(SYNC_CMD_WRITE,fd,"REPLCONF",
"listening-port",port, NULL);
sdsfree(port);
@@ -1482,6 +1497,37 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
"REPLCONF listening-port: %s", err);
}
sdsfree(err);
+ server.repl_state = REPL_STATE_SEND_IP;
+ }
+
+ /* Skip REPLCONF ip-address if there is no slave-announce-ip option set. */
+ if (server.repl_state == REPL_STATE_SEND_IP &&
+ server.slave_announce_ip == NULL)
+ {
+ server.repl_state = REPL_STATE_SEND_CAPA;
+ }
+
+ /* Set the slave ip, so that Master's INFO command can list the
+ * slave IP address port correctly in case of port forwarding or NAT. */
+ if (server.repl_state == REPL_STATE_SEND_IP) {
+ err = sendSynchronousCommand(SYNC_CMD_WRITE,fd,"REPLCONF",
+ "ip-address",server.slave_announce_ip, NULL);
+ if (err) goto write_error;
+ sdsfree(err);
+ server.repl_state = REPL_STATE_RECEIVE_IP;
+ return;
+ }
+
+ /* Receive REPLCONF ip-address reply. */
+ if (server.repl_state == REPL_STATE_RECEIVE_IP) {
+ err = sendSynchronousCommand(SYNC_CMD_READ,fd,NULL);
+ /* Ignore the error if any, not all the Redis versions support
+ * REPLCONF listening-port. */
+ if (err[0] == '-') {
+ serverLog(LL_NOTICE,"(Non critical) Master does not understand "
+ "REPLCONF ip-address: %s", err);
+ }
+ sdsfree(err);
server.repl_state = REPL_STATE_SEND_CAPA;
}
@@ -1787,12 +1833,16 @@ void roleCommand(client *c) {
listRewind(server.slaves,&li);
while((ln = listNext(&li))) {
client *slave = ln->value;
- char ip[NET_IP_STR_LEN];
+ char ip[NET_IP_STR_LEN], *slaveip = slave->slave_ip;
- if (anetPeerToString(slave->fd,ip,sizeof(ip),NULL) == -1) continue;
+ if (slaveip[0] == '\0') {
+ if (anetPeerToString(slave->fd,ip,sizeof(ip),NULL) == -1)
+ continue;
+ slaveip = ip;
+ }
if (slave->replstate != SLAVE_STATE_ONLINE) continue;
addReplyMultiBulkLen(c,3);
- addReplyBulkCString(c,ip);
+ addReplyBulkCString(c,slaveip);
addReplyBulkLongLong(c,slave->slave_listening_port);
addReplyBulkLongLong(c,slave->repl_ack_off);
slaves++;
diff --git a/src/server.c b/src/server.c
index f9806d280..c8eaebdea 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1412,6 +1412,8 @@ void initServerConfig(void) {
server.repl_min_slaves_to_write = CONFIG_DEFAULT_MIN_SLAVES_TO_WRITE;
server.repl_min_slaves_max_lag = CONFIG_DEFAULT_MIN_SLAVES_MAX_LAG;
server.slave_priority = CONFIG_DEFAULT_SLAVE_PRIORITY;
+ server.slave_announce_ip = CONFIG_DEFAULT_SLAVE_ANNOUNCE_IP;
+ server.slave_announce_port = CONFIG_DEFAULT_SLAVE_ANNOUNCE_PORT;
server.master_repl_offset = 0;
/* Replication partial resync backlog */
@@ -3056,11 +3058,15 @@ sds genRedisInfoString(char *section) {
while((ln = listNext(&li))) {
client *slave = listNodeValue(ln);
char *state = NULL;
- char ip[NET_IP_STR_LEN];
+ char ip[NET_IP_STR_LEN], *slaveip = slave->slave_ip;
int port;
long lag = 0;
- if (anetPeerToString(slave->fd,ip,sizeof(ip),&port) == -1) continue;
+ if (slaveip[0] == '\0') {
+ if (anetPeerToString(slave->fd,ip,sizeof(ip),&port) == -1)
+ continue;
+ slaveip = ip;
+ }
switch(slave->replstate) {
case SLAVE_STATE_WAIT_BGSAVE_START:
case SLAVE_STATE_WAIT_BGSAVE_END:
@@ -3080,7 +3086,7 @@ sds genRedisInfoString(char *section) {
info = sdscatprintf(info,
"slave%d:ip=%s,port=%d,state=%s,"
"offset=%lld,lag=%ld\r\n",
- slaveid,ip,slave->slave_listening_port,state,
+ slaveid,slaveip,slave->slave_listening_port,state,
slave->repl_ack_off, lag);
slaveid++;
}
diff --git a/src/server.h b/src/server.h
index 534b59bd5..f3f6b4ddd 100644
--- a/src/server.h
+++ b/src/server.h
@@ -126,6 +126,8 @@ typedef long long mstime_t; /* millisecond time type. */
#define CONFIG_DEFAULT_REPL_DISKLESS_SYNC_DELAY 5
#define CONFIG_DEFAULT_SLAVE_SERVE_STALE_DATA 1
#define CONFIG_DEFAULT_SLAVE_READ_ONLY 1
+#define CONFIG_DEFAULT_SLAVE_ANNOUNCE_IP NULL
+#define CONFIG_DEFAULT_SLAVE_ANNOUNCE_PORT 0
#define CONFIG_DEFAULT_REPL_DISABLE_TCP_NODELAY 0
#define CONFIG_DEFAULT_MAXMEMORY 0
#define CONFIG_DEFAULT_MAXMEMORY_SAMPLES 5
@@ -267,13 +269,15 @@ typedef long long mstime_t; /* millisecond time type. */
#define REPL_STATE_RECEIVE_AUTH 5 /* Wait for AUTH reply */
#define REPL_STATE_SEND_PORT 6 /* Send REPLCONF listening-port */
#define REPL_STATE_RECEIVE_PORT 7 /* Wait for REPLCONF reply */
-#define REPL_STATE_SEND_CAPA 8 /* Send REPLCONF capa */
-#define REPL_STATE_RECEIVE_CAPA 9 /* Wait for REPLCONF reply */
-#define REPL_STATE_SEND_PSYNC 10 /* Send PSYNC */
-#define REPL_STATE_RECEIVE_PSYNC 11 /* Wait for PSYNC reply */
+#define REPL_STATE_SEND_IP 8 /* Send REPLCONF ip-address */
+#define REPL_STATE_RECEIVE_IP 9 /* Wait for REPLCONF reply */
+#define REPL_STATE_SEND_CAPA 10 /* Send REPLCONF capa */
+#define REPL_STATE_RECEIVE_CAPA 11 /* Wait for REPLCONF reply */
+#define REPL_STATE_SEND_PSYNC 12 /* Send PSYNC */
+#define REPL_STATE_RECEIVE_PSYNC 13 /* Wait for PSYNC reply */
/* --- End of handshake states --- */
-#define REPL_STATE_TRANSFER 12 /* Receiving .rdb from master */
-#define REPL_STATE_CONNECTED 13 /* Connected to master */
+#define REPL_STATE_TRANSFER 14 /* Receiving .rdb from master */
+#define REPL_STATE_CONNECTED 15 /* Connected to master */
/* State of slaves from the POV of the master. Used in client->replstate.
* In SEND_BULK and ONLINE state the slave receives new updates
@@ -665,7 +669,8 @@ typedef struct client {
copying this slave output buffer
should use. */
char replrunid[CONFIG_RUN_ID_SIZE+1]; /* Master run id if is a master. */
- int slave_listening_port; /* As configured with: SLAVECONF listening-port */
+ int slave_listening_port; /* As configured with: REPLCONF listening-port */
+ char slave_ip[NET_IP_STR_LEN]; /* Optionally given by REPLCONF ip-address */
int slave_capa; /* Slave capabilities: SLAVE_CAPA_* bitwise OR. */
multiState mstate; /* MULTI/EXEC state */
int btype; /* Type of blocking op if CLIENT_BLOCKED. */
@@ -971,7 +976,9 @@ struct redisServer {
time_t repl_down_since; /* Unix time at which link with master went down */
int repl_disable_tcp_nodelay; /* Disable TCP_NODELAY after SYNC? */
int slave_priority; /* Reported in INFO and used by Sentinel. */
- char repl_master_runid[CONFIG_RUN_ID_SIZE+1]; /* Master run id for PSYNC. */
+ int slave_announce_port; /* Give the master this listening port. */
+ char *slave_announce_ip; /* Give the master this ip address. */
+ char repl_master_runid[CONFIG_RUN_ID_SIZE+1]; /* Master run id for PSYNC.*/
long long repl_master_initial_offset; /* Master PSYNC offset. */
int repl_slave_lazy_flush; /* Lazy FLUSHALL before loading DB? */
/* Replication script cache. */