From: job Date: Tue, 30 May 2023 16:02:28 +0000 (+0000) Subject: Fixup file modification timestamps to optimize failover from RRDP to RSYNC X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=08ac1330e7955e03b6bba4571e0a9906d7ef5279;p=openbsd Fixup file modification timestamps to optimize failover from RRDP to RSYNC In the RSYNC protocol a file's last modification time and its size are used to determine whether sending a (partial) copy over the wire is needed. Previously, when RRDP data structures are serialized to disk, the mtime of files in DIR_VALID ended up being UTIME_NOW. Thus, the mtimes of files obtained through RRDP will never match the mtimes of the same files available through RSYNC - causing each and every file to be added to the file transfer list. Instead, use the internal timestamps of RPKI files as the last modified timestamp. Specifically, for Signed Objects (ROAs, MFTs, GBRs, TAKs, ASPAs) the CMS signing-time, for .cer files the X.509 notBefore, and for .crl files the CRL lastUpdate. This results in a surprising optimization for the number files which have to be transfered. OK claudio@ --- diff --git a/usr.sbin/rpki-client/extern.h b/usr.sbin/rpki-client/extern.h index 64c75cc87e0..c3e3be89ce6 100644 --- a/usr.sbin/rpki-client/extern.h +++ b/usr.sbin/rpki-client/extern.h @@ -1,4 +1,4 @@ -/* $OpenBSD: extern.h,v 1.182 2023/05/30 12:14:48 claudio Exp $ */ +/* $OpenBSD: extern.h,v 1.183 2023/05/30 16:02:28 job Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons * @@ -348,6 +348,7 @@ struct gbr { time_t notbefore; /* EE cert's Not Before */ time_t notafter; /* Not After of the GBR EE */ time_t expires; /* when the signature path expires */ + int talid; /* TAL the GBR is chained up to */ }; struct aspa_provider { @@ -755,7 +756,7 @@ void proc_http(char *, int) __attribute__((noreturn)); void proc_rrdp(int) __attribute__((noreturn)); /* Repository handling */ -int filepath_add(struct filepath_tree *, char *); +int filepath_add(struct filepath_tree *, char *, time_t); void rrdp_clear(unsigned int); void rrdp_save_state(unsigned int, struct rrdp_session *); int rrdp_handle_file(unsigned int, enum publish_type, char *, diff --git a/usr.sbin/rpki-client/filemode.c b/usr.sbin/rpki-client/filemode.c index 0acb2385c60..6a0cbecbd51 100644 --- a/usr.sbin/rpki-client/filemode.c +++ b/usr.sbin/rpki-client/filemode.c @@ -1,4 +1,4 @@ -/* $OpenBSD: filemode.c,v 1.32 2023/05/30 12:02:22 claudio Exp $ */ +/* $OpenBSD: filemode.c,v 1.33 2023/05/30 16:02:28 job Exp $ */ /* * Copyright (c) 2019 Claudio Jeker * Copyright (c) 2019 Kristaps Dzonsons @@ -589,6 +589,7 @@ parse_file(struct entityq *q, struct msgbuf *msgq) struct entity *entp; struct ibuf *b; struct tal *tal; + time_t dummy = 0; while ((entp = TAILQ_FIRST(q)) != NULL) { TAILQ_REMOVE(q, entp, entries); @@ -615,6 +616,7 @@ parse_file(struct entityq *q, struct msgbuf *msgq) io_simple_buffer(b, &entp->repoid, sizeof(entp->repoid)); io_simple_buffer(b, &entp->talid, sizeof(entp->talid)); io_str_buffer(b, entp->file); + io_simple_buffer(b, &dummy, sizeof(dummy)); io_close_buffer(msgq, b); entity_free(entp); } diff --git a/usr.sbin/rpki-client/main.c b/usr.sbin/rpki-client/main.c index 22c1c34001d..1982e2747be 100644 --- a/usr.sbin/rpki-client/main.c +++ b/usr.sbin/rpki-client/main.c @@ -1,4 +1,4 @@ -/* $OpenBSD: main.c,v 1.240 2023/05/30 12:14:48 claudio Exp $ */ +/* $OpenBSD: main.c,v 1.241 2023/05/30 16:02:28 job Exp $ */ /* * Copyright (c) 2021 Claudio Jeker * Copyright (c) 2019 Kristaps Dzonsons @@ -559,6 +559,7 @@ entity_process(struct ibuf *b, struct stats *st, struct vrp_tree *tree, struct aspa *aspa; struct repo *rp; char *file; + time_t mtime; unsigned int id; int talid; int c; @@ -573,12 +574,13 @@ entity_process(struct ibuf *b, struct stats *st, struct vrp_tree *tree, io_read_buf(b, &id, sizeof(id)); io_read_buf(b, &talid, sizeof(talid)); io_read_str(b, &file); + io_read_buf(b, &mtime, sizeof(mtime)); /* in filemode messages can be ignored, only the accounting matters */ if (filemode) goto done; - if (filepath_add(&fpt, file) == 0) { + if (filepath_add(&fpt, file, mtime) == 0) { warnx("%s: File already visited", file); goto done; } diff --git a/usr.sbin/rpki-client/parser.c b/usr.sbin/rpki-client/parser.c index 93c0eca95b8..107375fc2ce 100644 --- a/usr.sbin/rpki-client/parser.c +++ b/usr.sbin/rpki-client/parser.c @@ -1,4 +1,4 @@ -/* $OpenBSD: parser.c,v 1.95 2023/05/30 12:14:48 claudio Exp $ */ +/* $OpenBSD: parser.c,v 1.96 2023/05/30 16:02:28 job Exp $ */ /* * Copyright (c) 2019 Claudio Jeker * Copyright (c) 2019 Kristaps Dzonsons @@ -352,7 +352,8 @@ proc_parser_mft_post(char *file, struct mft *mft, const char *path, * Load the most recent MFT by opening both options and comparing the two. */ static char * -proc_parser_mft(struct entity *entp, struct mft **mp, char **crlfile) +proc_parser_mft(struct entity *entp, struct mft **mp, char **crlfile, + time_t *crlmtime) { struct mft *mft1 = NULL, *mft2 = NULL; struct crl *crl, *crl1, *crl2; @@ -360,6 +361,7 @@ proc_parser_mft(struct entity *entp, struct mft **mp, char **crlfile) const char *err1, *err2; *mp = NULL; + *crlmtime = 0; mft1 = proc_parser_mft_pre(entp, DIR_VALID, &file1, &crl1, &crl1file, &err1); @@ -392,6 +394,7 @@ proc_parser_mft(struct entity *entp, struct mft **mp, char **crlfile) } if (*mp != NULL) { + *crlmtime = crl->lastupdate; if (!crl_insert(&crlt, crl)) { warnx("%s: duplicate AKI %s", file, crl->aki); crl_free(crl); @@ -488,7 +491,7 @@ proc_parser_root_cert(char *file, const unsigned char *der, size_t len, /* * Parse a ghostbuster record */ -static void +static struct gbr * proc_parser_gbr(char *file, const unsigned char *der, size_t len, const char *mftaki) { @@ -499,17 +502,23 @@ proc_parser_gbr(char *file, const unsigned char *der, size_t len, const char *errstr; if ((gbr = gbr_parse(&x509, file, der, len)) == NULL) - return; + return NULL; a = valid_ski_aki(file, &auths, gbr->ski, gbr->aki, mftaki); crl = crl_get(&crlt, a); /* return value can be ignored since nothing happens here */ - if (!valid_x509(file, ctx, x509, a, crl, &errstr)) + if (!valid_x509(file, ctx, x509, a, crl, &errstr)) { warnx("%s: %s", file, errstr); - + X509_free(x509); + gbr_free(gbr); + return NULL; + } X509_free(x509); - gbr_free(gbr); + + gbr->talid = a->cert->talid; + + return gbr; } /* @@ -618,8 +627,11 @@ parse_entity(struct entityq *q, struct msgbuf *msgq) struct mft *mft; struct roa *roa; struct aspa *aspa; + struct gbr *gbr; + struct tak *tak; struct ibuf *b; unsigned char *f; + time_t mtime, crlmtime; size_t flen; char *file, *crlfile; int c; @@ -642,9 +654,13 @@ parse_entity(struct entityq *q, struct msgbuf *msgq) file = NULL; f = NULL; + mtime = 0; + crlmtime = 0; + switch (entp->type) { case RTYPE_TAL: io_str_buffer(b, entp->file); + io_simple_buffer(b, &mtime, sizeof(mtime)); if ((tal = tal_parse(entp->file, entp->data, entp->datasz)) == NULL) errx(1, "%s: could not parse tal file", @@ -663,6 +679,9 @@ parse_entity(struct entityq *q, struct msgbuf *msgq) else cert = proc_parser_cert(file, f, flen, entp->mftaki); + if (cert != NULL) + mtime = cert->notbefore; + io_simple_buffer(b, &mtime, sizeof(mtime)); c = (cert != NULL); io_simple_buffer(b, &c, sizeof(int)); if (cert != NULL) { @@ -676,8 +695,11 @@ parse_entity(struct entityq *q, struct msgbuf *msgq) */ break; case RTYPE_MFT: - file = proc_parser_mft(entp, &mft, &crlfile); + file = proc_parser_mft(entp, &mft, &crlfile, &crlmtime); io_str_buffer(b, file); + if (mft != NULL) + mtime = mft->signtime; + io_simple_buffer(b, &mtime, sizeof(mtime)); c = (mft != NULL); io_simple_buffer(b, &c, sizeof(int)); if (mft != NULL) @@ -696,6 +718,8 @@ parse_entity(struct entityq *q, struct msgbuf *msgq) io_simple_buffer(b2, &entp->talid, sizeof(entp->talid)); io_str_buffer(b2, crlfile); + io_simple_buffer(b2, &crlmtime, + sizeof(crlmtime)); free(crlfile); io_close_buffer(msgq, b2); @@ -706,6 +730,9 @@ parse_entity(struct entityq *q, struct msgbuf *msgq) file = parse_load_file(entp, &f, &flen); io_str_buffer(b, file); roa = proc_parser_roa(file, f, flen, entp->mftaki); + if (roa != NULL) + mtime = roa->signtime; + io_simple_buffer(b, &mtime, sizeof(mtime)); c = (roa != NULL); io_simple_buffer(b, &c, sizeof(int)); if (roa != NULL) @@ -715,12 +742,19 @@ parse_entity(struct entityq *q, struct msgbuf *msgq) case RTYPE_GBR: file = parse_load_file(entp, &f, &flen); io_str_buffer(b, file); - proc_parser_gbr(file, f, flen, entp->mftaki); + gbr = proc_parser_gbr(file, f, flen, entp->mftaki); + if (gbr != NULL) + mtime = gbr->signtime; + io_simple_buffer(b, &mtime, sizeof(mtime)); + gbr_free(gbr); break; case RTYPE_ASPA: file = parse_load_file(entp, &f, &flen); io_str_buffer(b, file); aspa = proc_parser_aspa(file, f, flen, entp->mftaki); + if (aspa != NULL) + mtime = aspa->signtime; + io_simple_buffer(b, &mtime, sizeof(mtime)); c = (aspa != NULL); io_simple_buffer(b, &c, sizeof(int)); if (aspa != NULL) @@ -730,13 +764,18 @@ parse_entity(struct entityq *q, struct msgbuf *msgq) case RTYPE_TAK: file = parse_load_file(entp, &f, &flen); io_str_buffer(b, file); - proc_parser_tak(file, f, flen, entp->mftaki); + tak = proc_parser_tak(file, f, flen, entp->mftaki); + if (tak != NULL) + mtime = tak->signtime; + io_simple_buffer(b, &mtime, sizeof(mtime)); + tak_free(tak); break; case RTYPE_CRL: default: file = parse_filepath(entp->repoid, entp->path, entp->file, entp->location); io_str_buffer(b, file); + io_simple_buffer(b, &mtime, sizeof(mtime)); warnx("%s: unhandled type %d", file, entp->type); break; } diff --git a/usr.sbin/rpki-client/repo.c b/usr.sbin/rpki-client/repo.c index ee79b1e3195..009ce254613 100644 --- a/usr.sbin/rpki-client/repo.c +++ b/usr.sbin/rpki-client/repo.c @@ -1,4 +1,4 @@ -/* $OpenBSD: repo.c,v 1.46 2023/05/25 12:49:39 claudio Exp $ */ +/* $OpenBSD: repo.c,v 1.47 2023/05/30 16:02:28 job Exp $ */ /* * Copyright (c) 2021 Claudio Jeker * Copyright (c) 2019 Kristaps Dzonsons @@ -119,6 +119,7 @@ static void remove_contents(char *); struct filepath { RB_ENTRY(filepath) entry; char *file; + time_t mtime; }; static inline int @@ -133,12 +134,13 @@ RB_PROTOTYPE(filepath_tree, filepath, entry, filepathcmp); * Functions to lookup which files have been accessed during computation. */ int -filepath_add(struct filepath_tree *tree, char *file) +filepath_add(struct filepath_tree *tree, char *file, time_t mtime) { struct filepath *fp; if ((fp = malloc(sizeof(*fp))) == NULL) err(1, NULL); + fp->mtime = mtime; if ((fp->file = strdup(file)) == NULL) err(1, NULL); @@ -838,7 +840,7 @@ rrdp_handle_file(unsigned int id, enum publish_type pt, char *uri, /* write new content or mark uri as deleted. */ if (pt == PUB_DEL) { - filepath_add(&rr->deleted, uri); + filepath_add(&rr->deleted, uri, 0); } else { fp = filepath_find(&rr->deleted, uri); if (fp != NULL) @@ -1536,6 +1538,28 @@ repo_move_valid(struct filepath_tree *tree) base = strchr(fp->file + rrdpsz, '/'); assert(base != NULL); fn = base + 1; + + /* + * Adjust file last modification time in order to + * minimize RSYNC synchronization load after transport + * failover. + * While serializing RRDP datastructures to disk, set + * the last modified timestamp to the CMS signing-time, + * the X.509 notBefore, or CRL lastUpdate timestamp. + */ + if (fp->mtime != 0) { + int ret; + struct timespec ts[2]; + + ts[0].tv_nsec = UTIME_OMIT; + ts[1].tv_sec = fp->mtime; + ts[1].tv_nsec = 0; + ret = utimensat(AT_FDCWD, fp->file, ts, 0); + if (ret == -1) { + warn("utimensat %s", fp->file); + continue; + } + } } if (repo_mkpath(AT_FDCWD, fn) == -1)