0
0
mirror of https://github.com/darkk/redsocks.git synced 2025-08-29 13:15:30 +00:00

Implement exponential backoff in case of accept() failure.

Busy-loop strikes the daemon without backoff and log flood fills disks.

See also https://github.com/darkk/redsocks/issues/19
This commit is contained in:
Leonid Evdokimov 2012-01-27 22:29:21 +04:00
parent 6f8312b21f
commit 128d730583
3 changed files with 54 additions and 2 deletions

View File

@ -65,6 +65,7 @@ static parser_entry redsocks_entries[] =
{ .key = "login", .type = pt_pchar },
{ .key = "password", .type = pt_pchar },
{ .key = "listenq", .type = pt_uint16 },
{ .key = "max_accept_backoff", .type = pt_uint16 },
{ }
};
@ -119,6 +120,7 @@ static int redsocks_onenter(parser_section *section)
* Linux: sysctl net.core.somaxconn
* FreeBSD: sysctl kern.ipc.somaxconn */
instance->config.listenq = SOMAXCONN;
instance->config.max_backoff_ms = 60000;
for (parser_entry *entry = &section->entries[0]; entry->key; entry++)
entry->addr =
@ -130,6 +132,7 @@ static int redsocks_onenter(parser_section *section)
(strcmp(entry->key, "login") == 0) ? (void*)&instance->config.login :
(strcmp(entry->key, "password") == 0) ? (void*)&instance->config.password :
(strcmp(entry->key, "listenq") == 0) ? (void*)&instance->config.listenq :
(strcmp(entry->key, "max_accept_backoff") == 0) ? (void*)&instance->config.max_backoff_ms :
NULL;
section->data = instance;
return 0;
@ -167,6 +170,10 @@ static int redsocks_onexit(parser_section *section)
err = "no `type` for redsocks";
}
if (!err && !instance->config.max_backoff_ms) {
err = "`max_accept_backoff` must be positive, 0 ms is too low";
}
if (err)
parser_error(section->context, err);
@ -561,6 +568,19 @@ void redsocks_connect_relay(redsocks_client *client)
}
}
static void redsocks_accept_backoff(int fd, short what, void *_arg)
{
redsocks_instance *self = _arg;
/* Isn't it already deleted? EV_PERSIST has nothing common with timeouts in
* old libevent... On the other hand libevent does not return any error. */
if (tracked_event_del(&self->accept_backoff) != 0)
log_errno(LOG_ERR, "event_del");
if (tracked_event_add(&self->listener, NULL) != 0)
log_errno(LOG_ERR, "event_add");
}
static void redsocks_accept_client(int fd, short what, void *_arg)
{
redsocks_instance *self = _arg;
@ -576,9 +596,27 @@ static void redsocks_accept_client(int fd, short what, void *_arg)
// working with client_fd
client_fd = accept(fd, (struct sockaddr*)&clientaddr, &addrlen);
if (client_fd == -1) {
log_errno(LOG_WARNING, "accept");
/* Different systems use different `errno` value to signal different
* `lack of file descriptors` conditions. Here are most of them. */
if (errno == ENFILE || errno == EMFILE || errno == ENOBUFS || errno == ENOMEM) {
// FIXME: should I log on every attempt?
self->accept_backoff_ms = (self->accept_backoff_ms << 1) + 1;
if (self->accept_backoff_ms > self->config.max_backoff_ms)
self->accept_backoff_ms = self->config.max_backoff_ms;
int delay = (random() % self->accept_backoff_ms) + 1;
log_errno(LOG_WARNING, "accept: out of file descriptos, backing off for %u ms", delay);
struct timeval tvdelay = { delay / 1000, (delay % 1000) * 1000 };
if (tracked_event_del(&self->listener) != 0)
log_errno(LOG_ERR, "event_del");
if (tracked_event_add(&self->accept_backoff, &tvdelay) != 0)
log_errno(LOG_ERR, "event_add");
}
else {
log_errno(LOG_WARNING, "accept");
}
goto fail;
}
self->accept_backoff_ms = 0;
// socket is really bound now (it could be bound to 0.0.0.0)
addrlen = sizeof(myaddr);
@ -742,6 +780,8 @@ static int redsocks_init_instance(redsocks_instance *instance)
}
tracked_event_set(&instance->listener, fd, EV_READ | EV_PERSIST, redsocks_accept_client, instance);
tracked_event_set(&instance->accept_backoff, -1, 0, redsocks_accept_backoff, instance);
error = tracked_event_add(&instance->listener, NULL);
if (error) {
log_errno(LOG_ERR, "event_add");
@ -781,6 +821,9 @@ static void redsocks_fini_instance(redsocks_instance *instance) {
if (instance->listener.inserted)
if (tracked_event_del(&instance->listener) != 0)
log_errno(LOG_WARNING, "event_del");
if (instance->accept_backoff.inserted)
if (tracked_event_del(&instance->accept_backoff) != 0)
log_errno(LOG_WARNING, "event_del");
if (close(EVENT_FD(&instance->listener.ev)) != 0)
log_errno(LOG_WARNING, "close");
memset(&instance->listener, 0, sizeof(instance->listener));

View File

@ -45,7 +45,13 @@ redsocks {
// listen() queue length. Default value is SOMAXCONN and it should be
// good enough for most of us.
// listenq = 128;
// listenq = 128; // SOMAXCONN equals 128 on my Linux box.
// `max_accept_backoff` is a delay to retry `accept()` after accept
// failure (e.g. due to lack of file descriptors). It's measured in
// milliseconds and maximal value is 65535. Setting it to zero leads to
// busy-loop.
// max_accept_backoff = 60000;
// `ip' and `port' are IP and tcp-port of proxy-server
// You can also use hostname instead of IP, only one (random)

View File

@ -29,6 +29,7 @@ typedef struct redsocks_config_t {
char *type;
char *login;
char *password;
uint16_t max_backoff_ms; // backoff capped by 65 seconds is enough :)
uint16_t listenq;
} redsocks_config;
@ -41,6 +42,8 @@ typedef struct redsocks_instance_t {
list_head list;
redsocks_config config;
struct tracked_event listener;
struct tracked_event accept_backoff;
uint16_t accept_backoff_ms;
list_head clients;
relay_subsys *relay_ss;
} redsocks_instance;