From 5440febba42d0f46572d0617f0fdd5d2d38922de Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Wed, 8 Nov 2017 15:34:03 +0100 Subject: [SERVER] Add multiple config options for limiting stuff maxClients, maxImages, maxPayload, maxReplicationSize Refs #3231 --- src/server/globals.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++----- src/server/globals.h | 29 ++++++++++ src/server/image.c | 7 ++- src/server/net.c | 2 +- src/server/uplink.c | 4 +- 5 files changed, 183 insertions(+), 19 deletions(-) (limited to 'src/server') diff --git a/src/server/globals.c b/src/server/globals.c index 50d7885..854698a 100644 --- a/src/server/globals.c +++ b/src/server/globals.c @@ -3,29 +3,47 @@ #include "../shared/log.h" #include #include +#include +#include char *_configDir = NULL; -char *_basePath = NULL; -bool _vmdkLegacyMode = false; volatile bool _shutdown = false; +// [dnbd3] +int _listenPort = PORT; +char *_basePath = NULL; int _serverPenalty = 0; int _clientPenalty = 0; -bool _removeMissingImages = true; bool _isProxy = false; -bool _proxyPrivateOnly = false; bool _backgroundReplication = true; bool _lookupMissingForProxy = true; -int _listenPort = PORT; +bool _removeMissingImages = true; int _uplinkTimeout = SOCKET_TIMEOUT_UPLINK; int _clientTimeout = SOCKET_TIMEOUT_CLIENT; bool _closeUnusedFd = false; +bool _vmdkLegacyMode = false; +// Not really needed anymore since we have '+' and '-' in alt-servers +bool _proxyPrivateOnly = false; +// [limits] +int _maxClients = SERVER_MAX_CLIENTS; +int _maxImages = SERVER_MAX_IMAGES; +int _maxPayload = 9000000; // 9MB +uint64_t _maxReplicationSize = (uint64_t)100000000000LL; #define SAVE_TO_VAR_STR(ss, kk) do { if (strcmp(section, #ss) == 0 && strcmp(key, #kk) == 0) { if (_ ## kk != NULL) free(_ ## kk); _ ## kk = strdup(value); } } while (0) #define SAVE_TO_VAR_BOOL(ss, kk) do { if (strcmp(section, #ss) == 0 && strcmp(key, #kk) == 0) _ ## kk = atoi(value) != 0 || strcmp(value, "true") == 0 || strcmp(value, "True") == 0 || strcmp(value, "TRUE") == 0; } while (0) -#define SAVE_TO_VAR_INT(ss, kk) do { if (strcmp(section, #ss) == 0 && strcmp(key, #kk) == 0) _ ## kk = atoi(value); } while (0) +#define SAVE_TO_VAR_INT(ss, kk) do { if (strcmp(section, #ss) == 0 && strcmp(key, #kk) == 0) parse32(value, &_ ## kk, #ss); } while (0) +#define SAVE_TO_VAR_UINT(ss, kk) do { if (strcmp(section, #ss) == 0 && strcmp(key, #kk) == 0) parse32u(value, &_ ## kk, #ss); } while (0) +#define SAVE_TO_VAR_UINT64(ss, kk) do { if (strcmp(section, #ss) == 0 && strcmp(key, #kk) == 0) parse64u(value, &_ ## kk, #ss); } while (0) static void handleMaskString( const char *value, void(*func)(logmask_t) ); +static const char* units = "KMGTPEZY"; + +static bool parse64(const char *in, int64_t *out, const char *optname); +static bool parse64u(const char *in, uint64_t *out, const char *optname); +static bool parse32(const char *in, int *out, const char *optname) UNUSED; +static bool parse32u(const char *in, int *out, const char *optname); + static int ini_handler(void *custom UNUSED, const char* section, const char* key, const char* value) { if ( _basePath == NULL ) SAVE_TO_VAR_STR( dnbd3, basePath ); @@ -36,11 +54,15 @@ static int ini_handler(void *custom UNUSED, const char* section, const char* key SAVE_TO_VAR_BOOL( dnbd3, lookupMissingForProxy ); SAVE_TO_VAR_BOOL( dnbd3, removeMissingImages ); SAVE_TO_VAR_BOOL( dnbd3, closeUnusedFd ); - SAVE_TO_VAR_INT( dnbd3, serverPenalty ); - SAVE_TO_VAR_INT( dnbd3, clientPenalty ); - SAVE_TO_VAR_INT( dnbd3, uplinkTimeout ); - SAVE_TO_VAR_INT( dnbd3, clientTimeout ); - SAVE_TO_VAR_INT( dnbd3, listenPort ); + SAVE_TO_VAR_UINT( dnbd3, serverPenalty ); + SAVE_TO_VAR_UINT( dnbd3, clientPenalty ); + SAVE_TO_VAR_UINT( dnbd3, uplinkTimeout ); + SAVE_TO_VAR_UINT( dnbd3, clientTimeout ); + SAVE_TO_VAR_UINT( dnbd3, listenPort ); + SAVE_TO_VAR_UINT( limits, maxClients ); + SAVE_TO_VAR_UINT( limits, maxImages ); + SAVE_TO_VAR_UINT( limits, maxPayload ); + SAVE_TO_VAR_UINT64( limits, maxReplicationSize ); if ( strcmp( section, "logging" ) == 0 && strcmp( key, "fileMask" ) == 0 ) handleMaskString( value, &log_setFileMask ); if ( strcmp( section, "logging" ) == 0 && strcmp( key, "consoleMask" ) == 0 ) handleMaskString( value, &log_setConsoleMask ); if ( strcmp( section, "logging" ) == 0 && strcmp( key, "file" ) == 0 ) { @@ -82,9 +104,13 @@ void globals_loadConfig() logadd( LOG_ERROR, "listenPort must be 1-65535, but is %d", _listenPort ); exit( EXIT_FAILURE ); } - // Silently "fix" invalid values - if ( _serverPenalty < 0 ) _serverPenalty = 0; - if ( _clientPenalty < 0 ) _clientPenalty = 0; + // Cap to hard limit + if ( _maxClients > SERVER_MAX_CLIENTS ) _maxClients = SERVER_MAX_CLIENTS; + if ( _maxImages > SERVER_MAX_IMAGES ) _maxImages = SERVER_MAX_IMAGES; + // Dump config as interpreted + char buffer[2000]; + globals_dumpConfig( buffer, sizeof(buffer) ); + logadd( LOG_DEBUG1, "Effective configuration:\n%s", buffer ); } #define SETLOGBIT(name) do { if ( strstr( value, #name ) != NULL ) mask |= LOG_ ## name; } while (0) @@ -100,3 +126,109 @@ static void handleMaskString( const char *value, void(*func)(logmask_t) ) (*func)( mask ); } +static bool parse64(const char *in, int64_t *out, const char *optname) +{ + if ( *in == '\0' ) { + logadd( LOG_WARNING, "Ignoring empty numeric setting '%s'", optname ); + return false; + } + char *end; + long long int num = strtoll( in, &end, 10 ); + if ( end == in ) { + logadd( LOG_WARNING, "Ignoring value '%s' for '%s': Not a number", in, optname ); + return false; + } + int exp, base = 1024; + while ( *end == ' ' ) end++; + if ( *end == '\0' ) { + exp = 0; + } else { + char *pos = strchr( units, *end > 'Z' ? (*end - 32) : *end ); + if ( pos == NULL ) { + logadd( LOG_ERROR, "Invalid unit '%s' for '%s'", end, optname ); + return false; + } + exp = (int)( pos - units ) + 1; + end++; + if ( *end == 'B' || *end == 'b' ) { + base = 1000; + } + } + while ( exp-- > 0 ) num *= base; + *out = (int64_t)num; + return true; +} + +static bool parse64u(const char *in, uint64_t *out, const char *optname) +{ + int64_t v; + if ( !parse64( in, &v, optname ) ) return false; + if ( v < 0 ) { + logadd( LOG_WARNING, "Ignoring value '%s' for '%s': Cannot be negative", in, optname ); + return false; + } + *out = (uint64_t)v; + return true; +} + +static bool parse32(const char *in, int *out, const char *optname) +{ + int64_t v; + if ( !parse64( in, &v, optname ) ) return false; + if ( v < INT_MIN || v > INT_MAX ) { + logadd( LOG_WARNING, "'%s' must be between %d and %d, but is '%s'", optname, (int)INT_MIN, (int)INT_MAX, in ); + return false; + } + *out = (int)v; + return true; +} + +static bool parse32u(const char *in, int *out, const char *optname) +{ + int64_t v; + if ( !parse64( in, &v, optname ) ) return false; + if ( v < 0 || v > INT_MAX ) { + logadd( LOG_WARNING, "'%s' must be between %d and %d, but is '%s'", optname, (int)0, (int)INT_MAX, in ); + return false; + } + *out = (int)v; + return true; +} + +#define P_ARG(...) do { \ + int r = snprintf(buffer, rem, __VA_ARGS__); \ + if ( r < 0 || (size_t)r >= rem ) return size - 1; \ + rem -= r; \ + buffer += r; \ +} while (0) +#define PVAR(var,type) P_ARG(#var "=%" type "\n", _ ## var) +#define PINT(var) PVAR(var, "d") +#define PUINT64(var) PVAR(var, PRIu64) +#define PSTR(var) PVAR(var, "s") +#define PBOOL(var) P_ARG(#var "=%s\n", _ ## var ? "true" : "false") + +size_t globals_dumpConfig(char *buffer, size_t size) +{ + size_t rem = size; + P_ARG("[dnbd3]\n"); + PINT(listenPort); + PSTR(basePath); + PINT(serverPenalty); + PINT(clientPenalty); + PBOOL(isProxy); + PBOOL(backgroundReplication); + PBOOL(lookupMissingForProxy); + PBOOL(removeMissingImages); + PINT(uplinkTimeout); + PINT(clientTimeout); + PBOOL(closeUnusedFd); + PBOOL(vmdkLegacyMode); + PBOOL(proxyPrivateOnly); + P_ARG("[limits]\n"); + PINT(maxClients); + PINT(maxImages); + PINT(maxPayload); + PUINT64(maxReplicationSize); + return size - rem; +} + diff --git a/src/server/globals.h b/src/server/globals.h index 1475981..c1d5d78 100644 --- a/src/server/globals.h +++ b/src/server/globals.h @@ -218,9 +218,38 @@ extern bool _lookupMissingForProxy; */ extern int _listenPort; +/** + * Max number of DNBD3 clients we accept + */ +extern int _maxClients; + +/** + * Max number of Images we support (in baseDir) + */ +extern int _maxImages; + +/** + * Maximum payload length we accept on uplinks and thus indirectly + * from clients in case the requested range is not cached locally. + * Usually this isn't even a megabyte for "real" clients (blockdev + * or fuse). + */ +extern int _maxPayload; + +/** + * If in proxy mode, don't replicate images that are + * larger than this according to the uplink server. + */ +extern uint64_t _maxReplicationSize; + /** * Load the server configuration. */ void globals_loadConfig(); +/** + * Dump the effective configuration in use to given buffer. + */ +size_t globals_dumpConfig(char *buffer, size_t size); + #endif /* GLOBALS_H_ */ diff --git a/src/server/image.c b/src/server/image.c index 45463a0..ca00c63 100644 --- a/src/server/image.c +++ b/src/server/image.c @@ -762,7 +762,7 @@ static bool image_addToList(dnbd3_image_t *image) break; } if ( i >= _num_images ) { - if ( _num_images >= SERVER_MAX_IMAGES ) { + if ( _num_images >= _maxImages ) { spin_unlock( &imageListLock ); return false; } @@ -1270,7 +1270,10 @@ static dnbd3_image_t *loadImageProxy(char * const name, const uint16_t revision, if ( revision != 0 && remoteRid != revision ) goto server_fail; // Want specific revision but uplink supplied different rid if ( revision == 0 && image != NULL && image->rid >= remoteRid ) goto server_fail; // Not actually a failure: Highest remote rid is <= highest local rid - don't clone! if ( remoteImageSize < DNBD3_BLOCK_SIZE || remoteName == NULL || strcmp( name, remoteName ) != 0 ) goto server_fail; - if ( remoteImageSize > SERVER_MAX_PROXY_IMAGE_SIZE ) goto server_fail; + if ( remoteImageSize > _maxReplicationSize ) { + logadd( LOG_MINOR, "Won't proxy '%s:%d': Larger than maxReplicationSize", name, (int)revision ); + goto server_fail; + } pthread_mutex_lock( &reloadLock ); ok = image_ensureDiskSpace( remoteImageSize ) && image_clone( sock, name, remoteRid, remoteImageSize ); // This sets up the file+map+crc and loads the img diff --git a/src/server/net.c b/src/server/net.c index e77da45..86b0a84 100644 --- a/src/server/net.c +++ b/src/server/net.c @@ -726,7 +726,7 @@ static bool addToList(dnbd3_client_t *client) spin_unlock( &_clients_lock ); return true; } - if ( _num_clients >= SERVER_MAX_CLIENTS ) { + if ( _num_clients >= _maxClients ) { spin_unlock( &_clients_lock ); logadd( LOG_ERROR, "Maximum number of clients reached!" ); return false; diff --git a/src/server/uplink.c b/src/server/uplink.c index de53657..bed7b54 100644 --- a/src/server/uplink.c +++ b/src/server/uplink.c @@ -564,13 +564,13 @@ static void uplink_handleReceive(dnbd3_connection_t *link) logadd( LOG_INFO, "Uplink: Connection error %d (%s)", ret, link->image->path ); goto error_cleanup; } - if ( inReply.size > 9000000 ) { // TODO: Configurable + if ( inReply.size > (uint32_t)_maxPayload ) { logadd( LOG_WARNING, "Pure evil: Uplink server sent too much payload for %s", link->image->path ); goto error_cleanup; } if ( link->recvBufferLen < inReply.size ) { - link->recvBufferLen = MIN(9000000, inReply.size + 65536); // XXX dont miss occurrence + link->recvBufferLen = MIN((uint32_t)_maxPayload, inReply.size + 65536); link->recvBuffer = realloc( link->recvBuffer, link->recvBufferLen ); } if ( (uint32_t)sock_recv( link->fd, link->recvBuffer, inReply.size ) != inReply.size ) { -- cgit v1.2.3-55-g7522