Fix irregular stats-report-time

Currently 'ocserv' sends session accounting statistics at irregular intervals.
For example, if 'stats-report-time' is set to 60, the actual intervals may vary
between 50, 60, 70, or even 80 seconds.  Moreover, these intervals are not
constant - they fluctuate arbitrarily with each statistics update.

This behavior was intentionally introduced to avoid worker processes acting
simultaneously in scenarios like server restarts, where all clients reconnect
at the same time, which could impose heavy load on the secmod process.

However, it causes issues for RADIUS servers that require accurate and
consistent timing.

Summary of changes:
 - Apply randomization only once when the timer is initially set up, affecting
   only the first timer firing.  All subsequent firings will occur at regular
   intervals relative to the first one.
 - Remove fuzzing from 'interim_update_secs'. This value originates either from
   RADIUS or from 'stats-report-time' and should not be altered.

Closes: #630

Signed-off-by: Grigory Trenin <grigory.trenin@gmail.com>
This commit is contained in:
Grigory Trenin
2025-12-23 14:33:46 -05:00
committed by Nikos Mavrogiannopoulos
parent dbbe6de40c
commit 1893047e8f
2 changed files with 16 additions and 9 deletions

2
NEWS
View File

@@ -14,6 +14,8 @@
- Fixed DTLS not working with systemd socket activation (#647)
- Fixed a bug in the ban timer logic that could prevent IP addresses
from being banned or cause premature unbans (#678)
- Session statisics are now reported at consistent intervals
for RADIUS compatibility (#630)
* Version 1.3.0 (released 2024-05-05)
- Switch to https://github.com/nodejs/llhttp from http-parser.

View File

@@ -1332,14 +1332,8 @@ static int periodic_check(worker_st *ws, struct timespec *tnow,
{
int max, ret;
time_t now = tnow->tv_sec;
time_t periodic_check_time = PERIODIC_CHECK_TIME;
/* modify timers with a fuzzying factor, to prevent all worker processes
* to act at exactly the same time (e.g., after a server restart on which
* all clients reconnect at the same time). */
FUZZ(periodic_check_time, 5, tnow->tv_nsec);
if (now - ws->last_periodic_check < periodic_check_time)
if (now - ws->last_periodic_check < PERIODIC_CHECK_TIME)
return 0;
/* we set an alarm at each periodic check to prevent any
@@ -2074,7 +2068,6 @@ static int connect_handler(worker_st *ws)
sizeof(ws->req.hostname));
}
FUZZ(ws->user_config->interim_update_secs, 5, rnd);
FUZZ(WSCONFIG(ws)->rekey_time, 30, rnd);
/* Connected. Turn of the alarm */
@@ -2960,6 +2953,8 @@ static void periodic_check_watcher_cb(EV_P_ ev_timer *w, int revents)
static int worker_event_loop(struct worker_st *ws)
{
struct timespec tnow;
unsigned int rnd;
int ret;
#if defined(__linux__) && defined(HAVE_LIBSECCOMP)
worker_loop = ev_default_loop(EVFLAG_NOENV | EVBACKEND_EPOLL);
@@ -3003,7 +2998,17 @@ static int worker_event_loop(struct worker_st *ws)
ev_io_start(worker_loop, &tun_watcher);
ev_init(&period_check_watcher, periodic_check_watcher_cb);
ev_timer_set(&period_check_watcher, WORKER_MAINTENANCE_TIME,
/* generate a random offset to prevent all worker processes from
* acting at exactly the same time (e.g., after a server restart
* on which all clients reconnect at the same time). */
ret = gnutls_rnd(GNUTLS_RND_NONCE, &rnd, sizeof(rnd));
if (ret < 0) {
oclog(ws, LOG_ERR, "error in the random generator: %s",
gnutls_strerror(ret));
goto exit;
}
ev_timer_set(&period_check_watcher,
rnd % (unsigned int)WORKER_MAINTENANCE_TIME,
WORKER_MAINTENANCE_TIME);
ev_timer_start(worker_loop, &period_check_watcher);