mirror of
https://github.com/darkk/redsocks.git
synced 2025-08-29 05:05: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:
parent
6f8312b21f
commit
128d730583
45
redsocks.c
45
redsocks.c
@ -65,6 +65,7 @@ static parser_entry redsocks_entries[] =
|
|||||||
{ .key = "login", .type = pt_pchar },
|
{ .key = "login", .type = pt_pchar },
|
||||||
{ .key = "password", .type = pt_pchar },
|
{ .key = "password", .type = pt_pchar },
|
||||||
{ .key = "listenq", .type = pt_uint16 },
|
{ .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
|
* Linux: sysctl net.core.somaxconn
|
||||||
* FreeBSD: sysctl kern.ipc.somaxconn */
|
* FreeBSD: sysctl kern.ipc.somaxconn */
|
||||||
instance->config.listenq = SOMAXCONN;
|
instance->config.listenq = SOMAXCONN;
|
||||||
|
instance->config.max_backoff_ms = 60000;
|
||||||
|
|
||||||
for (parser_entry *entry = §ion->entries[0]; entry->key; entry++)
|
for (parser_entry *entry = §ion->entries[0]; entry->key; entry++)
|
||||||
entry->addr =
|
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, "login") == 0) ? (void*)&instance->config.login :
|
||||||
(strcmp(entry->key, "password") == 0) ? (void*)&instance->config.password :
|
(strcmp(entry->key, "password") == 0) ? (void*)&instance->config.password :
|
||||||
(strcmp(entry->key, "listenq") == 0) ? (void*)&instance->config.listenq :
|
(strcmp(entry->key, "listenq") == 0) ? (void*)&instance->config.listenq :
|
||||||
|
(strcmp(entry->key, "max_accept_backoff") == 0) ? (void*)&instance->config.max_backoff_ms :
|
||||||
NULL;
|
NULL;
|
||||||
section->data = instance;
|
section->data = instance;
|
||||||
return 0;
|
return 0;
|
||||||
@ -167,6 +170,10 @@ static int redsocks_onexit(parser_section *section)
|
|||||||
err = "no `type` for redsocks";
|
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)
|
if (err)
|
||||||
parser_error(section->context, 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)
|
static void redsocks_accept_client(int fd, short what, void *_arg)
|
||||||
{
|
{
|
||||||
redsocks_instance *self = _arg;
|
redsocks_instance *self = _arg;
|
||||||
@ -576,9 +596,27 @@ static void redsocks_accept_client(int fd, short what, void *_arg)
|
|||||||
// working with client_fd
|
// working with client_fd
|
||||||
client_fd = accept(fd, (struct sockaddr*)&clientaddr, &addrlen);
|
client_fd = accept(fd, (struct sockaddr*)&clientaddr, &addrlen);
|
||||||
if (client_fd == -1) {
|
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;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
self->accept_backoff_ms = 0;
|
||||||
|
|
||||||
// socket is really bound now (it could be bound to 0.0.0.0)
|
// socket is really bound now (it could be bound to 0.0.0.0)
|
||||||
addrlen = sizeof(myaddr);
|
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->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);
|
error = tracked_event_add(&instance->listener, NULL);
|
||||||
if (error) {
|
if (error) {
|
||||||
log_errno(LOG_ERR, "event_add");
|
log_errno(LOG_ERR, "event_add");
|
||||||
@ -781,6 +821,9 @@ static void redsocks_fini_instance(redsocks_instance *instance) {
|
|||||||
if (instance->listener.inserted)
|
if (instance->listener.inserted)
|
||||||
if (tracked_event_del(&instance->listener) != 0)
|
if (tracked_event_del(&instance->listener) != 0)
|
||||||
log_errno(LOG_WARNING, "event_del");
|
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)
|
if (close(EVENT_FD(&instance->listener.ev)) != 0)
|
||||||
log_errno(LOG_WARNING, "close");
|
log_errno(LOG_WARNING, "close");
|
||||||
memset(&instance->listener, 0, sizeof(instance->listener));
|
memset(&instance->listener, 0, sizeof(instance->listener));
|
||||||
|
@ -45,7 +45,13 @@ redsocks {
|
|||||||
|
|
||||||
// listen() queue length. Default value is SOMAXCONN and it should be
|
// listen() queue length. Default value is SOMAXCONN and it should be
|
||||||
// good enough for most of us.
|
// 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
|
// `ip' and `port' are IP and tcp-port of proxy-server
|
||||||
// You can also use hostname instead of IP, only one (random)
|
// You can also use hostname instead of IP, only one (random)
|
||||||
|
@ -29,6 +29,7 @@ typedef struct redsocks_config_t {
|
|||||||
char *type;
|
char *type;
|
||||||
char *login;
|
char *login;
|
||||||
char *password;
|
char *password;
|
||||||
|
uint16_t max_backoff_ms; // backoff capped by 65 seconds is enough :)
|
||||||
uint16_t listenq;
|
uint16_t listenq;
|
||||||
} redsocks_config;
|
} redsocks_config;
|
||||||
|
|
||||||
@ -41,6 +42,8 @@ typedef struct redsocks_instance_t {
|
|||||||
list_head list;
|
list_head list;
|
||||||
redsocks_config config;
|
redsocks_config config;
|
||||||
struct tracked_event listener;
|
struct tracked_event listener;
|
||||||
|
struct tracked_event accept_backoff;
|
||||||
|
uint16_t accept_backoff_ms;
|
||||||
list_head clients;
|
list_head clients;
|
||||||
relay_subsys *relay_ss;
|
relay_subsys *relay_ss;
|
||||||
} redsocks_instance;
|
} redsocks_instance;
|
||||||
|
Loading…
Reference in New Issue
Block a user