From: claudio Date: Tue, 9 Nov 2021 11:03:39 +0000 (+0000) Subject: Limit the number of publication points under a given TAL. X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=7af68c5c1dfeede72d2bfbe7c82cefbbaddf6ead;p=openbsd Limit the number of publication points under a given TAL. Introduce an additional timeout for each publication point. The limits are large enough to accomodate normal operating levels. With and OK benno@ job@ tb@ beck@ deraadt@ --- diff --git a/usr.sbin/rpki-client/extern.h b/usr.sbin/rpki-client/extern.h index bd6b11af238..399fc7c81f7 100644 --- a/usr.sbin/rpki-client/extern.h +++ b/usr.sbin/rpki-client/extern.h @@ -1,4 +1,4 @@ -/* $OpenBSD: extern.h,v 1.94 2021/11/05 10:50:41 claudio Exp $ */ +/* $OpenBSD: extern.h,v 1.95 2021/11/09 11:03:39 claudio Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons * @@ -390,6 +390,7 @@ struct msgbuf; extern int verbose; extern const char *tals[]; extern const char *taldescs[]; +extern unsigned int talrepocnt[]; extern size_t talsz; /* Routines for RPKI entities. */ @@ -501,8 +502,8 @@ void rrdp_save_state(size_t, struct rrdp_session *); int rrdp_handle_file(size_t, enum publish_type, char *, char *, size_t, char *, size_t); char *repo_filename(const struct repo *, const char *); -struct repo *ta_lookup(struct tal *); -struct repo *repo_lookup(const char *, const char *); +struct repo *ta_lookup(int, struct tal *); +struct repo *repo_lookup(int, const char *, const char *); int repo_queued(struct repo *, struct entity *); void repo_cleanup(struct filepath_tree *); void repo_free(void); @@ -517,6 +518,8 @@ void rrdp_fetch(size_t, const char *, const char *, struct rrdp_session *); void rrdp_http_done(size_t, enum http_result, const char *); +int repo_next_timeout(int); +void repo_check_timeout(void); /* Logging (though really used for OpenSSL errors). */ @@ -620,4 +623,10 @@ int mkpath(const char *); /* Maximum number of concurrent rsync processes. */ #define MAX_RSYNC_PROCESSES 16 +/* Maximum allowd repositories per tal */ +#define MAX_REPO_PER_TAL 1000 + +/* Timeout for repository synchronisation, in seconds */ +#define MAX_REPO_TIMEOUT (15 * 60) + #endif /* ! EXTERN_H */ diff --git a/usr.sbin/rpki-client/main.c b/usr.sbin/rpki-client/main.c index 4ea97df451a..15708183d24 100644 --- a/usr.sbin/rpki-client/main.c +++ b/usr.sbin/rpki-client/main.c @@ -1,4 +1,4 @@ -/* $OpenBSD: main.c,v 1.163 2021/11/04 18:00:07 claudio Exp $ */ +/* $OpenBSD: main.c,v 1.164 2021/11/09 11:03:39 claudio Exp $ */ /* * Copyright (c) 2021 Claudio Jeker * Copyright (c) 2019 Kristaps Dzonsons @@ -51,6 +51,7 @@ const char *tals[TALSZ_MAX]; const char *taldescs[TALSZ_MAX]; +unsigned int talrepocnt[TALSZ_MAX]; size_t talsz; size_t entity_queue; @@ -428,7 +429,9 @@ queue_add_from_tal(struct tal *tal) err(1, NULL); /* Look up the repository. */ - repo = ta_lookup(tal); + repo = ta_lookup(tal->id, tal); + if (repo == NULL) + return; /* steal the pkey from the tal structure */ data = tal->pkey; @@ -446,11 +449,10 @@ queue_add_from_cert(const struct cert *cert) struct repo *repo; char *nfile; - repo = repo_lookup(cert->repo, rrdpon ? cert->notify : NULL); - if (repo == NULL) { - warnx("%s: repository lookup failed", cert->repo); + repo = repo_lookup(cert->talid, cert->repo, + rrdpon ? cert->notify : NULL); + if (repo == NULL) return; - } if ((nfile = strdup(cert->mft)) == NULL) err(1, NULL); @@ -1007,13 +1009,17 @@ main(int argc, char *argv[]) err(1, "fchdir"); while (entity_queue > 0 && !killme) { + int polltim; + for (i = 0; i < NPFD; i++) { pfd[i].events = POLLIN; if (queues[i]->queued) pfd[i].events |= POLLOUT; } - if ((c = poll(pfd, NPFD, INFTIM)) == -1) { + polltim = repo_next_timeout(INFTIM); + + if ((c = poll(pfd, NPFD, polltim)) == -1) { if (errno == EINTR) continue; err(1, "poll"); @@ -1043,6 +1049,8 @@ main(int argc, char *argv[]) if (hangup) break; + repo_check_timeout(); + /* * Check the rsync and http process. * This means that one of our modules has completed diff --git a/usr.sbin/rpki-client/repo.c b/usr.sbin/rpki-client/repo.c index 02580d53e6e..68b3aa8f1d3 100644 --- a/usr.sbin/rpki-client/repo.c +++ b/usr.sbin/rpki-client/repo.c @@ -1,4 +1,4 @@ -/* $OpenBSD: repo.c,v 1.10 2021/11/04 17:35:09 claudio Exp $ */ +/* $OpenBSD: repo.c,v 1.11 2021/11/09 11:03:39 claudio Exp $ */ /* * Copyright (c) 2021 Claudio Jeker * Copyright (c) 2019 Kristaps Dzonsons @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -94,6 +95,8 @@ struct repo { const struct rsyncrepo *rsync; const struct tarepo *ta; struct entityq queue; /* files waiting for repo */ + time_t alarm; /* sync timeout */ + int talid; size_t id; /* identifier */ }; SLIST_HEAD(, repo) repos = SLIST_HEAD_INITIALIZER(repos); @@ -608,14 +611,22 @@ rrdp_basedir(const char *dir) * Allocate and insert a new repository. */ static struct repo * -repo_alloc(void) +repo_alloc(int talid) { struct repo *rp; + if (++talrepocnt[talid] >= MAX_REPO_PER_TAL) { + if (talrepocnt[talid] == MAX_REPO_PER_TAL) + warnx("too many repositories under %s", tals[talid]); + return NULL; + } + if ((rp = calloc(1, sizeof(*rp))) == NULL) err(1, NULL); rp->id = ++repoid; + rp->talid = talid; + rp->alarm = getmonotime() + MAX_REPO_TIMEOUT; TAILQ_INIT(&rp->queue); SLIST_INSERT_HEAD(&repos, rp, entry); @@ -932,6 +943,9 @@ rsync_finish(size_t id, int ok) tr = ta_find(id); if (tr != NULL) { + /* repository changed state already, ignore request */ + if (tr->state != REPO_LOADING) + return; if (ok) { logx("ta/%s: loaded from network", tr->descr); stats.rsync_repos++; @@ -954,6 +968,9 @@ rsync_finish(size_t id, int ok) if (rr == NULL) errx(1, "unknown rsync repo %zu", id); + /* repository changed state already, ignore request */ + if (rr->state != REPO_LOADING) + return; if (ok) { logx("%s: loaded from network", rr->basedir); stats.rsync_repos++; @@ -982,6 +999,9 @@ rrdp_finish(size_t id, int ok) rr = rrdp_find(id); if (rr == NULL) errx(1, "unknown RRDP repo %zu", id); + /* repository changed state already, ignore request */ + if (rr->state != REPO_LOADING) + return; if (ok && rrdp_merge_repo(rr)) { logx("%s: loaded from network", rr->notifyuri); @@ -1033,6 +1053,10 @@ http_finish(size_t id, enum http_result res, const char *last_mod) return; } + /* repository changed state already, ignore request */ + if (tr->state != REPO_LOADING) + return; + /* Move downloaded TA file into place, or unlink on failure. */ if (res == HTTP_OK) { char *file; @@ -1066,7 +1090,7 @@ http_finish(size_t id, enum http_result res, const char *last_mod) * Look up a trust anchor, queueing it for download if not found. */ struct repo * -ta_lookup(struct tal *tal) +ta_lookup(int id, struct tal *tal) { struct repo *rp; @@ -1076,7 +1100,10 @@ ta_lookup(struct tal *tal) return rp; } - rp = repo_alloc(); + rp = repo_alloc(id); + if (rp == NULL) + return NULL; + if ((rp->repouri = strdup(tal->descr)) == NULL) err(1, NULL); rp->ta = ta_get(tal); @@ -1088,7 +1115,7 @@ ta_lookup(struct tal *tal) * Look up a repository, queueing it for discovery if not found. */ struct repo * -repo_lookup(const char *uri, const char *notify) +repo_lookup(int id, const char *uri, const char *notify) { struct repo *rp; char *repouri; @@ -1112,7 +1139,12 @@ repo_lookup(const char *uri, const char *notify) return rp; } - rp = repo_alloc(); + rp = repo_alloc(id); + if (rp == NULL) { + free(repouri); + return NULL; + } + rp->repouri = repouri; if (notify != NULL) if ((rp->notifyuri = strdup(notify)) == NULL) @@ -1169,6 +1201,60 @@ repo_queued(struct repo *rp, struct entity *p) return 0; } +int +repo_next_timeout(int timeout) +{ + struct repo *rp; + time_t now; + + now = getmonotime(); + /* Look up in repository table. (Lookup should actually fail here) */ + SLIST_FOREACH(rp, &repos, entry) { + if (repo_state(rp) == REPO_LOADING) { + int diff = rp->alarm - now; + diff *= 1000; + if (timeout == INFTIM || diff < timeout) + timeout = diff; + } + } + return timeout; +} + +static void +repo_fail(struct repo *rp) +{ + /* reset the alarm since code may fallback to rsync */ + rp->alarm = getmonotime() + MAX_REPO_TIMEOUT; + + if (rp->ta) + http_finish(rp->ta->id, HTTP_FAILED, NULL); + else if (rp->rrdp) + rrdp_finish(rp->rrdp->id, 0); + else if (rp->rsync) + rsync_finish(rp->rsync->id, 0); + else + errx(1, "%s: bad repo", rp->repouri); +} + +void +repo_check_timeout(void) +{ + struct repo *rp; + time_t now; + + now = getmonotime(); + /* Look up in repository table. (Lookup should actually fail here) */ + SLIST_FOREACH(rp, &repos, entry) { + if (repo_state(rp) == REPO_LOADING) { + if (rp->alarm <= now) { + warnx("%s: synchronisation timeout", + rp->repouri); + repo_fail(rp); + } + } + } +} + static char ** add_to_del(char **del, size_t *dsz, char *file) {