diff --git a/redis.conf b/redis.conf index 4fbbaf7e0..d2074f13f 100644 --- a/redis.conf +++ b/redis.conf @@ -175,6 +175,16 @@ timeout 0 # Redis default starting with Redis 3.2.1. tcp-keepalive 300 +# Apply OS-specific mechanism to mark the listening socket with the specified +# ID, to support advanced routing and filtering capabilities. +# +# On Linux, the ID represents a connection mark. +# On FreeBSD, the ID represents a socket cookie ID. +# On OpenBSD, the ID represents a route table ID. +# +# The default value is 0, which implies no marking is required. +# socket-mark-id 0 + ################################# TLS/SSL ##################################### # By default, TLS/SSL is disabled. To enable it, the "tls-port" configuration diff --git a/src/anet.c b/src/anet.c index bde460fc8..3ded135b0 100644 --- a/src/anet.c +++ b/src/anet.c @@ -49,6 +49,8 @@ #include "anet.h" #include "config.h" +#define UNUSED(x) (void)(x) + static void anetSetError(char *err, const char *fmt, ...) { va_list ap; @@ -680,3 +682,18 @@ error: close(fds[1]); return -1; } + +int anetSetSockMarkId(char *err, int fd, uint32_t id) { +#ifdef HAVE_SOCKOPTMARKID + if (setsockopt(fd, SOL_SOCKET, SOCKOPTMARKID, (void *)&id, sizeof(id)) == -1) { + anetSetError(err, "setsockopt: %s", strerror(errno)); + return ANET_ERR; + } + return ANET_OK; +#else + UNUSED(fd); + UNUSED(id); + anetSetError(err,"anetSetSockMarkid unsupported on this platform"); + return ANET_OK; +#endif +} diff --git a/src/anet.h b/src/anet.h index 96238aaf4..ff86e2029 100644 --- a/src/anet.h +++ b/src/anet.h @@ -73,5 +73,6 @@ int anetKeepAlive(char *err, int fd, int interval); int anetFormatAddr(char *fmt, size_t fmt_len, char *ip, int port); int anetFormatFdAddr(int fd, char *buf, size_t buf_len, int fd_to_str_type); int anetPipe(int fds[2], int read_flags, int write_flags); +int anetSetSockMarkId(char *err, int fd, uint32_t id); #endif diff --git a/src/config.c b/src/config.c index c4b893c28..d5e347892 100644 --- a/src/config.c +++ b/src/config.c @@ -2977,6 +2977,7 @@ standardConfig static_configs[] = { /* Unsigned int configs */ createUIntConfig("maxclients", NULL, MODIFIABLE_CONFIG, 1, UINT_MAX, server.maxclients, 10000, INTEGER_CONFIG, NULL, updateMaxclients), createUIntConfig("unixsocketperm", NULL, IMMUTABLE_CONFIG, 0, 0777, server.unixsocketperm, 0, OCTAL_CONFIG, NULL, NULL), + createUIntConfig("socket-mark-id", NULL, IMMUTABLE_CONFIG, 0, UINT_MAX, server.socket_mark_id, 0, INTEGER_CONFIG, NULL, NULL), /* Unsigned Long configs */ createULongConfig("active-defrag-max-scan-fields", NULL, MODIFIABLE_CONFIG, 1, LONG_MAX, server.active_defrag_max_scan_fields, 1000, INTEGER_CONFIG, NULL, NULL), /* Default: keys with more than 1000 fields will be processed separately */ diff --git a/src/config.h b/src/config.h index 210e55a87..6baa8bd0f 100644 --- a/src/config.h +++ b/src/config.h @@ -80,6 +80,10 @@ /* MSG_NOSIGNAL. */ #ifdef __linux__ #define HAVE_MSG_NOSIGNAL 1 +#if defined(SO_MARK) +#define HAVE_SOCKOPTMARKID 1 +#define SOCKOPTMARKID SO_MARK +#endif #endif /* Test for polling API */ @@ -113,6 +117,20 @@ #define redis_fsync(fd) fsync(fd) #endif +#if defined(__FreeBSD__) +#if defined(SO_USER_COOKIE) +#define HAVE_SOCKOPTMARKID 1 +#define SOCKOPTMARKID SO_USER_COOKIE +#endif +#endif + +#if defined(__OpenBSD__) +#if defined(SO_RTABLE) +#define HAVE_SOCKOPTMARKID 1 +#define SOCKOPTMARKID SO_RTABLE +#endif +#endif + #if __GNUC__ >= 4 #define redis_unreachable __builtin_unreachable #else diff --git a/src/server.c b/src/server.c index f632279fa..285ed8963 100644 --- a/src/server.c +++ b/src/server.c @@ -2293,6 +2293,7 @@ int listenToPort(int port, socketFds *sfd) { closeSocketListeners(sfd); return C_ERR; } + if (server.socket_mark_id > 0) anetSetSockMarkId(NULL, sfd->fd[sfd->count], server.socket_mark_id); anetNonBlock(NULL,sfd->fd[sfd->count]); anetCloexec(sfd->fd[sfd->count]); sfd->count++; diff --git a/src/server.h b/src/server.h index b57f39d38..ace9f1219 100644 --- a/src/server.h +++ b/src/server.h @@ -1494,6 +1494,7 @@ struct redisServer { socketFds ipfd; /* TCP socket file descriptors */ socketFds tlsfd; /* TLS socket file descriptors */ int sofd; /* Unix socket file descriptor */ + uint32_t socket_mark_id; /* ID for listen socket marking */ socketFds cfd; /* Cluster bus listening socket */ list *clients; /* List of active clients */ list *clients_to_close; /* Clients to close asynchronously */ diff --git a/tests/unit/introspection.tcl b/tests/unit/introspection.tcl index 339d4c731..89d8f170b 100644 --- a/tests/unit/introspection.tcl +++ b/tests/unit/introspection.tcl @@ -215,6 +215,7 @@ start_server {tags {"introspection"}} { dbfilename logfile dir + socket-mark-id } if {!$::tls} {