From d0a27ef8bed1b219dae70293dae719e0009c5824 Mon Sep 17 00:00:00 2001 From: job Date: Thu, 29 Aug 2024 09:53:04 +0000 Subject: [PATCH] Periodically reinitialize RRDP sessions to snapshot at random intervals It is technically possible for a series of RRDP deltas and a snapshot to diverge. An RRDP server could distribute files via Deltas and then forget about those files, causing copies to remain stuck in the caches of RRDP clients. Resetting RRDP sessions once every few weeks helps with garbage collection. In week 0 the probability of triggering re-initialization is ~0.025% and doubles every week, in week 11 its 50% and always after week 12. Thus, RPs will reinitialize at least once every 3 months. OK tb@ claudio@ --- usr.sbin/rpki-client/extern.h | 5 ++++- usr.sbin/rpki-client/repo.c | 30 ++++++++++++++++++++++++++---- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/usr.sbin/rpki-client/extern.h b/usr.sbin/rpki-client/extern.h index 298585f6520..8d5d9c2983a 100644 --- a/usr.sbin/rpki-client/extern.h +++ b/usr.sbin/rpki-client/extern.h @@ -1,4 +1,4 @@ -/* $OpenBSD: extern.h,v 1.226 2024/08/21 19:35:31 job Exp $ */ +/* $OpenBSD: extern.h,v 1.227 2024/08/29 09:53:04 job Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons * @@ -985,6 +985,9 @@ int mkpathat(int, const char *); #define DEFAULT_SKIPLIST_FILE "/etc/rpki/skiplist" +/* Interval in which random reinitialization to an RRDP snapshot happens. */ +#define RRDP_RANDOM_REINIT_MAX 12 /* weeks */ + /* Maximum number of TAL files we'll load. */ #define TALSZ_MAX 8 #define CERTID_MAX 1000000 diff --git a/usr.sbin/rpki-client/repo.c b/usr.sbin/rpki-client/repo.c index 0d6777792c7..8d46862ef25 100644 --- a/usr.sbin/rpki-client/repo.c +++ b/usr.sbin/rpki-client/repo.c @@ -1,4 +1,4 @@ -/* $OpenBSD: repo.c,v 1.62 2024/08/15 11:30:43 job Exp $ */ +/* $OpenBSD: repo.c,v 1.63 2024/08/29 09:53:04 job Exp $ */ /* * Copyright (c) 2021 Claudio Jeker * Copyright (c) 2019 Kristaps Dzonsons @@ -62,6 +62,7 @@ struct rrdprepo { struct filepath_tree deleted; unsigned int id; enum repo_state state; + time_t last_reset; }; static SLIST_HEAD(, rrdprepo) rrdprepos = SLIST_HEAD_INITIALIZER(rrdprepos); @@ -651,7 +652,7 @@ repo_alloc(int talid) * based on that information. */ static struct rrdp_session * -rrdp_session_parse(const struct rrdprepo *rr) +rrdp_session_parse(struct rrdprepo *rr) { FILE *f; struct rrdp_session *state; @@ -660,6 +661,9 @@ rrdp_session_parse(const struct rrdprepo *rr) char *line = NULL, *file; size_t len = 0; ssize_t n; + time_t now, weeks; + + now = time(NULL); if ((state = calloc(1, sizeof(*state))) == NULL) err(1, NULL); @@ -690,6 +694,11 @@ rrdp_session_parse(const struct rrdprepo *rr) goto fail; break; case 2: + rr->last_reset = strtonum(line, 1, LLONG_MAX, &errstr); + if (errstr) + goto fail; + break; + case 3: if (strcmp(line, "-") == 0) break; if ((state->last_mod = strdup(line)) == NULL) @@ -705,6 +714,17 @@ rrdp_session_parse(const struct rrdprepo *rr) ln++; } + /* check if it's time for reinitialization */ + weeks = (now - rr->last_reset) / (86400 * 7); + if (now <= rr->last_reset || weeks > RRDP_RANDOM_REINIT_MAX) { + warnx("%s: reinitializing", rr->notifyuri); + goto reset; + } + if (arc4random_uniform(1 << RRDP_RANDOM_REINIT_MAX) < (1 << weeks)) { + warnx("%s: reinitializing", rr->notifyuri); + goto reset; + } + if (ferror(f)) goto fail; fclose(f); @@ -713,11 +733,13 @@ rrdp_session_parse(const struct rrdprepo *rr) fail: warnx("%s: troubles reading state file", rr->basedir); + reset: fclose(f); free(line); free(state->session_id); free(state->last_mod); memset(state, 0, sizeof(*state)); + rr->last_reset = now; return state; } @@ -747,8 +769,8 @@ rrdp_session_save(unsigned int id, struct rrdp_session *state) err(1, "fdopen"); /* write session state file out */ - if (fprintf(f, "%s\n%lld\n", state->session_id, - state->serial) < 0) + if (fprintf(f, "%s\n%lld\n%lld\n", state->session_id, + state->serial, (long long)rr->last_reset) < 0) goto fail; if (state->last_mod != NULL) { -- 2.20.1