Download raw body.
rpki-client: refactor non-functional CA metrics accounting
Rpki-client detects non-functional CAs by initially marking every CA as
non-functional, and then later on (after a valid manifest is discovered)
clearing that mark. Statistics were done along the same lines: first
increment and later on substract for all functional ones. This resulted
in rather unwieldy code, e.g., a repo pointer needed to be hoisted
around which is getting in the way of upcoming work.
The below diff simplifies the dance described above, and also fixes a
subtle defect: non-functional CAs were counted towards the repository
those broken CAs were pointing towards, instead of being counted
towards the repository that contained the broken CA. Attributing the
non-functional CA to the issuing parent makes more sense to me, because
the issuing parent can actually do something about it, for example by
revoking the broken child.
In short, I think this diff helps better show which repository is
responsible for non-functional CAs. See the metrics output that follows.
Openmetrics while attributing to parent (behavior with the below diff):
rpki_client_repository_objects{type="cert",state="non-functional",name="apnic",carepo="rsync://rpki.cernet.edu.cn/repo"} 1
rpki_client_repository_objects{type="cert",state="non-functional",name="apnic",carepo="rsync://rpki.apnic.net/repository"} 1
rpki_client_repository_objects{type="cert",state="non-functional",name="arin",carepo="rsync://rpki.sunoaki.net/repo"} 3
rpki_client_repository_objects{type="cert",state="non-functional",name="arin",carepo="rsync://rsync.paas.rpki.ripe.net/repository"} 2
rpki_client_repository_objects{type="cert",state="non-functional",name="arin",carepo="rsync://rpki.arin.net/repository"} 16
rpki_client_repository_objects{type="cert",state="non-functional",name="lacnic",carepo="rsync://rpki-repo.registro.br/repo"} 46
rpki_client_repository_objects{type="cert",state="non-functional",name="ripe",carepo="rsync://rpki.komorebi.network/repo"} 2
rpki_client_repository_objects{type="cert",state="non-functional",name="ripe",carepo="rsync://rpki.cc/repo"} 1
rpki_client_repository_objects{type="cert",state="non-functional",name="ripe",carepo="rsync://krill.47272.net/repo"} 2
rpki_client_repository_objects{type="cert",state="non-functional",name="ripe",carepo="rsync://cloudie-repo.rpki.app/repo"} 2
rpki_client_repository_objects{type="cert",state="non-functional",name="ripe",carepo="rsync://rsync.paas.rpki.ripe.net/repository"} 20
rpki_client_repository_objects{type="cert",state="non-functional",name="ripe",carepo="rsync://rpki-rps.arin.net/repository"} 4
rpki_client_repository_objects{type="cert",state="non-functional",name="ripe",carepo="rsync://rpki.ripe.net/repository"} 46
Openmetrics while attributing to child (old behavior):
rpki_client_repository_objects{type="cert",state="non-functional",name="apnic",carepo="rsync://alias.rpki-measurement.site/repo"} 1
rpki_client_repository_objects{type="cert",state="non-functional",name="apnic",carepo="rsync://rsync.rp.ki/repo"} 1
rpki_client_repository_objects{type="cert",state="non-functional",name="arin",carepo="rsync://rpki-repo.canops.org/repo"} 1
rpki_client_repository_objects{type="cert",state="non-functional",name="arin",carepo="rsync://krill.ca-bc-01.ssmidge.xyz/repo"} 2
rpki_client_repository_objects{type="cert",state="non-functional",name="arin",carepo="rsync://rpki.leitecastro.com/repo"} 1
rpki_client_repository_objects{type="cert",state="non-functional",name="arin",carepo="rsync://rpki.sunoaki.net/repo"} 3
rpki_client_repository_objects{type="cert",state="non-functional",name="arin",carepo="rsync://repo.kagl.me/rpki"} 1
rpki_client_repository_objects{type="cert",state="non-functional",name="arin",carepo="rsync://rpki-repo.registro.br/repo"} 1
rpki_client_repository_objects{type="cert",state="non-functional",name="arin",carepo="rsync://rsync.rp.ki/repo"} 2
rpki_client_repository_objects{type="cert",state="non-functional",name="arin",carepo="rsync://rpki-rps.arin.net/repository"} 9
rpki_client_repository_objects{type="cert",state="non-functional",name="arin",carepo="rsync://rsync.paas.rpki.ripe.net/repository"} 1
rpki_client_repository_objects{type="cert",state="non-functional",name="lacnic",carepo="rsync://rpki-repo.registro.br/repo"} 46
rpki_client_repository_objects{type="cert",state="non-functional",name="ripe",carepo="rsync://krill.ca-bc-01.ssmidge.xyz/repo"} 1
rpki_client_repository_objects{type="cert",state="non-functional",name="ripe",carepo="rsync://rpki.qs.nu/repo"} 1
rpki_client_repository_objects{type="cert",state="non-functional",name="ripe",carepo="rsync://rki.plasmanodes.com/repo"} 1
rpki_client_repository_objects{type="cert",state="non-functional",name="ripe",carepo="rsync://rpki.leitecastro.com/repo"} 1
rpki_client_repository_objects{type="cert",state="non-functional",name="ripe",carepo="rsync://oto.wakuwaku.ne.jp/pki"} 1
rpki_client_repository_objects{type="cert",state="non-functional",name="ripe",carepo="rsync://krill.ipgua.com/repo"} 4
rpki_client_repository_objects{type="cert",state="non-functional",name="ripe",carepo="rsync://pub.krill.ausra.cloud/repo"} 1
rpki_client_repository_objects{type="cert",state="non-functional",name="ripe",carepo="rsync://repo.kagl.me/rpki"} 1
rpki_client_repository_objects{type="cert",state="non-functional",name="ripe",carepo="rsync://rpki.axivora.net/repo"} 4
rpki_client_repository_objects{type="cert",state="non-functional",name="ripe",carepo="rsync://rpki.folf.systems/repo"} 1
rpki_client_repository_objects{type="cert",state="non-functional",name="ripe",carepo="rsync://rpki.zappiehost.com/repo"} 1
rpki_client_repository_objects{type="cert",state="non-functional",name="ripe",carepo="rsync://krill.signalx.cloud/repo"} 3
rpki_client_repository_objects{type="cert",state="non-functional",name="ripe",carepo="rsync://rsync.rp.ki/repo"} 2
rpki_client_repository_objects{type="cert",state="non-functional",name="ripe",carepo="rsync://rpki-rps.arin.net/repository"} 2
rpki_client_repository_objects{type="cert",state="non-functional",name="ripe",carepo="rsync://rsync.paas.rpki.ripe.net/repository"} 53
OK?
Index: cert.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/cert.c,v
diff -u -p -r1.239 cert.c
--- cert.c 15 Jun 2026 17:30:04 -0000 1.239
+++ cert.c 21 Jun 2026 21:21:26 -0000
@@ -2113,7 +2113,7 @@ RB_GENERATE(brk_tree, brk, entry, brkcmp
* Add each CA cert into the non-functional CA tree.
*/
void
-cert_insert_nca(struct nca_tree *tree, const struct cert *cert, struct repo *rp)
+cert_insert_nca(struct nca_tree *tree, const struct cert *cert)
{
struct nonfunc_ca *nca;
@@ -2127,22 +2127,21 @@ cert_insert_nca(struct nca_tree *tree, c
err(1, NULL);
if ((nca->ski = strdup(cert->ski)) == NULL)
err(1, NULL);
+ nca->repoid = cert->repoid;
nca->certid = cert->certid;
nca->talid = cert->talid;
if (RB_INSERT(nca_tree, tree, nca) != NULL)
errx(1, "non-functional CA tree corrupted");
- repo_stat_inc(rp, nca->talid, RTYPE_CER, STYPE_NONFUNC);
}
void
-cert_remove_nca(struct nca_tree *tree, int cid, struct repo *rp)
+cert_remove_nca(struct nca_tree *tree, int cid)
{
struct nonfunc_ca *found, needle = { .certid = cid };
if ((found = RB_FIND(nca_tree, tree, &needle)) != NULL) {
RB_REMOVE(nca_tree, tree, found);
- repo_stat_inc(rp, found->talid, RTYPE_CER, STYPE_FUNC);
free(found->location);
free(found->carepo);
free(found->mfturi);
Index: extern.h
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/extern.h,v
diff -u -p -r1.282 extern.h
--- extern.h 15 Jun 2026 14:30:53 -0000 1.282
+++ extern.h 21 Jun 2026 21:21:26 -0000
@@ -159,6 +159,7 @@ struct nonfunc_ca {
char *mfturi;
char *ski;
int certid;
+ unsigned int repoid;
int talid;
};
@@ -725,9 +726,8 @@ struct cert *ta_validate(const char *, s
size_t);
struct cert *cert_read(struct ibuf *);
void cert_insert_brks(struct brk_tree *, struct cert *);
-void cert_insert_nca(struct nca_tree *, const struct cert *,
- struct repo *);
-void cert_remove_nca(struct nca_tree *, int, struct repo *);
+void cert_insert_nca(struct nca_tree *, const struct cert *);
+void cert_remove_nca(struct nca_tree *, int);
enum rtype rtype_from_file_extension(const char *);
void mft_buffer(struct ibuf *, const struct mft *);
@@ -895,6 +895,7 @@ void repo_cleanup(struct filepath_tree
int repo_check_timeout(int);
void repostats_new_files_inc(struct repo *, const char *);
void repo_stat_inc(struct repo *, int, enum rtype, enum stype);
+void repo_stat_inc_nca(struct nonfunc_ca *);
void repo_tal_stats_collect(void (*)(const struct repo *,
const struct repotalstats *, void *), int, void *);
void repo_stats_collect(void (*)(const struct repo *,
Index: main.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/main.c,v
diff -u -p -r1.306 main.c
--- main.c 9 May 2026 01:22:32 -0000 1.306
+++ main.c 21 Jun 2026 21:21:26 -0000
@@ -575,7 +575,8 @@ queue_add_from_cert(const struct cert *c
err(1, NULL);
}
- cert_insert_nca(ncas, cert, repo);
+ cert_insert_nca(ncas, cert);
+
entityq_add(npath, nfile, RTYPE_MFT, DIR_UNKNOWN, repo, NULL, 0,
cert->talid, cert->certid, NULL);
}
@@ -675,7 +676,7 @@ entity_process(struct ibuf *b, struct va
if (mft->seqnum_gap)
repo_stat_inc(rp, talid, type, STYPE_SEQNUM_GAP);
queue_add_from_mft(mft);
- cert_remove_nca(&vd->ncas, mft->certid, rp);
+ cert_remove_nca(&vd->ncas, mft->certid);
ccr_insert_mft(&vd->ccr.mfts, mft);
mft_free(mft);
break;
@@ -1030,6 +1031,7 @@ main(int argc, char *argv[])
struct rusage ru;
struct timespec start_time, now_time;
struct validation_data vd = { 0 };
+ struct nonfunc_ca *nca;
clock_gettime(CLOCK_MONOTONIC, &start_time);
@@ -1538,6 +1540,12 @@ main(int argc, char *argv[])
/* if processing in filemode the process is done, no cleanup */
if (filemode)
return rc;
+
+ /*
+ * Account non-functional CAs statistics.
+ */
+ RB_FOREACH(nca, nca_tree, &vd.ncas)
+ repo_stat_inc_nca(nca);
logx("all files parsed: generating output");
Index: repo.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/repo.c,v
diff -u -p -r1.84 repo.c
--- repo.c 15 Jun 2026 14:45:19 -0000 1.84
+++ repo.c 21 Jun 2026 21:21:26 -0000
@@ -1541,6 +1541,19 @@ repostats_new_files_inc(struct repo *rp,
rp->repostats.new_files++;
}
+void
+repo_stat_inc_nca(struct nonfunc_ca *nca)
+{
+ struct repo *rp;
+
+ SLIST_FOREACH(rp, &repos, entry) {
+ if (rp->id == nca->repoid) {
+ rp->stats[nca->talid].certs_nonfunc++;
+ break;
+ }
+ }
+}
+
/*
* Update stats object of repository depending on rtype and subtype.
*/
@@ -1556,10 +1569,6 @@ repo_stat_inc(struct repo *rp, int talid
rp->stats[talid].certs++;
if (subtype == STYPE_FAIL)
rp->stats[talid].certs_fail++;
- if (subtype == STYPE_NONFUNC)
- rp->stats[talid].certs_nonfunc++;
- if (subtype == STYPE_FUNC)
- rp->stats[talid].certs_nonfunc--;
if (subtype == STYPE_BGPSEC) {
rp->stats[talid].certs--;
rp->stats[talid].brks++;
rpki-client: refactor non-functional CA metrics accounting