Periodically reinitialize RRDP sessions to snapshot at random intervals
authorjob <job@openbsd.org>
Thu, 29 Aug 2024 09:53:04 +0000 (09:53 +0000)
committerjob <job@openbsd.org>
Thu, 29 Aug 2024 09:53:04 +0000 (09:53 +0000)
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
usr.sbin/rpki-client/repo.c

index 298585f..8d5d9c2 100644 (file)
@@ -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 <kristaps@bsd.lv>
  *
@@ -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
index 0d67777..8d46862 100644 (file)
@@ -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 <claudio@openbsd.org>
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -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) {