main.c revision 1.241
1/* $OpenBSD: main.c,v 1.241 2023/05/30 16:02:28 job Exp $ */ 2/* 3 * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org> 4 * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include <sys/types.h> 20#include <sys/queue.h> 21#include <sys/resource.h> 22#include <sys/socket.h> 23#include <sys/statvfs.h> 24#include <sys/time.h> 25#include <sys/tree.h> 26#include <sys/wait.h> 27 28#include <assert.h> 29#include <dirent.h> 30#include <err.h> 31#include <errno.h> 32#include <fcntl.h> 33#include <fnmatch.h> 34#include <limits.h> 35#include <poll.h> 36#include <pwd.h> 37#include <signal.h> 38#include <stdarg.h> 39#include <stdio.h> 40#include <stdlib.h> 41#include <string.h> 42#include <syslog.h> 43#include <time.h> 44#include <unistd.h> 45 46#include <imsg.h> 47 48#include "extern.h" 49#include "version.h" 50 51const char *tals[TALSZ_MAX]; 52const char *taldescs[TALSZ_MAX]; 53unsigned int talrepocnt[TALSZ_MAX]; 54struct repotalstats talstats[TALSZ_MAX]; 55int talsz; 56 57size_t entity_queue; 58int timeout = 60*60; 59volatile sig_atomic_t killme; 60void suicide(int sig); 61 62static struct filepath_tree fpt = RB_INITIALIZER(&fpt); 63static struct msgbuf procq, rsyncq, httpq, rrdpq; 64static int cachefd, outdirfd; 65 66const char *bird_tablename = "ROAS"; 67 68int verbose; 69int noop; 70int excludeaspa; 71int filemode; 72int shortlistmode; 73int rrdpon = 1; 74int repo_timeout; 75time_t deadline; 76 77int64_t evaluation_time = X509_TIME_MIN; 78 79struct stats stats; 80 81struct fqdnlistentry { 82 LIST_ENTRY(fqdnlistentry) entry; 83 char *fqdn; 84}; 85LIST_HEAD(fqdns, fqdnlistentry); 86 87struct fqdns shortlist = LIST_HEAD_INITIALIZER(fqdns); 88struct fqdns skiplist = LIST_HEAD_INITIALIZER(fqdns); 89 90/* 91 * Log a message to stderr if and only if "verbose" is non-zero. 92 * This uses the err(3) functionality. 93 */ 94void 95logx(const char *fmt, ...) 96{ 97 va_list ap; 98 99 if (verbose && fmt != NULL) { 100 va_start(ap, fmt); 101 vwarnx(fmt, ap); 102 va_end(ap); 103 } 104} 105 106time_t 107getmonotime(void) 108{ 109 struct timespec ts; 110 111 if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) 112 err(1, "clock_gettime"); 113 return (ts.tv_sec); 114} 115 116void 117entity_free(struct entity *ent) 118{ 119 if (ent == NULL) 120 return; 121 122 free(ent->path); 123 free(ent->file); 124 free(ent->mftaki); 125 free(ent->data); 126 free(ent); 127} 128 129time_t 130get_current_time(void) 131{ 132 if (evaluation_time > X509_TIME_MIN) 133 return (time_t) evaluation_time; 134 return time(NULL); 135} 136 137/* 138 * Read a queue entity from the descriptor. 139 * Matched by entity_buffer_req(). 140 * The pointer must be passed entity_free(). 141 */ 142void 143entity_read_req(struct ibuf *b, struct entity *ent) 144{ 145 io_read_buf(b, &ent->type, sizeof(ent->type)); 146 io_read_buf(b, &ent->location, sizeof(ent->location)); 147 io_read_buf(b, &ent->repoid, sizeof(ent->repoid)); 148 io_read_buf(b, &ent->talid, sizeof(ent->talid)); 149 io_read_str(b, &ent->path); 150 io_read_str(b, &ent->file); 151 io_read_str(b, &ent->mftaki); 152 io_read_buf_alloc(b, (void **)&ent->data, &ent->datasz); 153} 154 155/* 156 * Write the queue entity. 157 * Matched by entity_read_req(). 158 */ 159static void 160entity_write_req(const struct entity *ent) 161{ 162 struct ibuf *b; 163 164 b = io_new_buffer(); 165 io_simple_buffer(b, &ent->type, sizeof(ent->type)); 166 io_simple_buffer(b, &ent->location, sizeof(ent->location)); 167 io_simple_buffer(b, &ent->repoid, sizeof(ent->repoid)); 168 io_simple_buffer(b, &ent->talid, sizeof(ent->talid)); 169 io_str_buffer(b, ent->path); 170 io_str_buffer(b, ent->file); 171 io_str_buffer(b, ent->mftaki); 172 io_buf_buffer(b, ent->data, ent->datasz); 173 io_close_buffer(&procq, b); 174} 175 176static void 177entity_write_repo(struct repo *rp) 178{ 179 struct ibuf *b; 180 enum rtype type = RTYPE_REPO; 181 enum location loc = DIR_UNKNOWN; 182 unsigned int repoid; 183 char *path, *altpath; 184 int talid = 0; 185 186 repoid = repo_id(rp); 187 path = repo_basedir(rp, 0); 188 altpath = repo_basedir(rp, 1); 189 b = io_new_buffer(); 190 io_simple_buffer(b, &type, sizeof(type)); 191 io_simple_buffer(b, &loc, sizeof(loc)); 192 io_simple_buffer(b, &repoid, sizeof(repoid)); 193 io_simple_buffer(b, &talid, sizeof(talid)); 194 io_str_buffer(b, path); 195 io_str_buffer(b, altpath); 196 io_buf_buffer(b, NULL, 0); /* ent->mftaki */ 197 io_buf_buffer(b, NULL, 0); /* ent->data */ 198 io_close_buffer(&procq, b); 199 free(path); 200 free(altpath); 201} 202 203/* 204 * Scan through all queued requests and see which ones are in the given 205 * repo, then flush those into the parser process. 206 */ 207void 208entityq_flush(struct entityq *q, struct repo *rp) 209{ 210 struct entity *p, *np; 211 212 entity_write_repo(rp); 213 214 TAILQ_FOREACH_SAFE(p, q, entries, np) { 215 entity_write_req(p); 216 TAILQ_REMOVE(q, p, entries); 217 entity_free(p); 218 } 219} 220 221/* 222 * Add the heap-allocated file to the queue for processing. 223 */ 224static void 225entityq_add(char *path, char *file, enum rtype type, enum location loc, 226 struct repo *rp, unsigned char *data, size_t datasz, int talid, 227 char *mftaki) 228{ 229 struct entity *p; 230 231 if ((p = calloc(1, sizeof(struct entity))) == NULL) 232 err(1, NULL); 233 234 p->type = type; 235 p->location = loc; 236 p->talid = talid; 237 p->mftaki = mftaki; 238 p->path = path; 239 if (rp != NULL) 240 p->repoid = repo_id(rp); 241 p->file = file; 242 p->data = data; 243 p->datasz = (data != NULL) ? datasz : 0; 244 245 entity_queue++; 246 247 /* 248 * Write to the queue if there's no repo or the repo has already 249 * been loaded else enqueue it for later. 250 */ 251 252 if (rp == NULL || !repo_queued(rp, p)) { 253 entity_write_req(p); 254 entity_free(p); 255 } 256} 257 258static void 259rrdp_file_resp(unsigned int id, int ok) 260{ 261 enum rrdp_msg type = RRDP_FILE; 262 struct ibuf *b; 263 264 b = io_new_buffer(); 265 io_simple_buffer(b, &type, sizeof(type)); 266 io_simple_buffer(b, &id, sizeof(id)); 267 io_simple_buffer(b, &ok, sizeof(ok)); 268 io_close_buffer(&rrdpq, b); 269} 270 271void 272rrdp_fetch(unsigned int id, const char *uri, const char *local, 273 struct rrdp_session *s) 274{ 275 enum rrdp_msg type = RRDP_START; 276 struct ibuf *b; 277 278 b = io_new_buffer(); 279 io_simple_buffer(b, &type, sizeof(type)); 280 io_simple_buffer(b, &id, sizeof(id)); 281 io_str_buffer(b, local); 282 io_str_buffer(b, uri); 283 io_str_buffer(b, s->session_id); 284 io_simple_buffer(b, &s->serial, sizeof(s->serial)); 285 io_str_buffer(b, s->last_mod); 286 io_close_buffer(&rrdpq, b); 287} 288 289void 290rrdp_abort(unsigned int id) 291{ 292 enum rrdp_msg type = RRDP_ABORT; 293 struct ibuf *b; 294 295 b = io_new_buffer(); 296 io_simple_buffer(b, &type, sizeof(type)); 297 io_simple_buffer(b, &id, sizeof(id)); 298 io_close_buffer(&rrdpq, b); 299} 300 301/* 302 * Request a repository sync via rsync URI to directory local. 303 */ 304void 305rsync_fetch(unsigned int id, const char *uri, const char *local, 306 const char *base) 307{ 308 struct ibuf *b; 309 310 b = io_new_buffer(); 311 io_simple_buffer(b, &id, sizeof(id)); 312 io_str_buffer(b, local); 313 io_str_buffer(b, base); 314 io_str_buffer(b, uri); 315 io_close_buffer(&rsyncq, b); 316} 317 318void 319rsync_abort(unsigned int id) 320{ 321 struct ibuf *b; 322 323 b = io_new_buffer(); 324 io_simple_buffer(b, &id, sizeof(id)); 325 io_str_buffer(b, NULL); 326 io_str_buffer(b, NULL); 327 io_str_buffer(b, NULL); 328 io_close_buffer(&rsyncq, b); 329} 330 331/* 332 * Request a file from a https uri, data is written to the file descriptor fd. 333 */ 334void 335http_fetch(unsigned int id, const char *uri, const char *last_mod, int fd) 336{ 337 struct ibuf *b; 338 339 b = io_new_buffer(); 340 io_simple_buffer(b, &id, sizeof(id)); 341 io_str_buffer(b, uri); 342 io_str_buffer(b, last_mod); 343 /* pass file as fd */ 344 b->fd = fd; 345 io_close_buffer(&httpq, b); 346} 347 348/* 349 * Request some XML file on behalf of the rrdp parser. 350 * Create a pipe and pass the pipe endpoints to the http and rrdp process. 351 */ 352static void 353rrdp_http_fetch(unsigned int id, const char *uri, const char *last_mod) 354{ 355 enum rrdp_msg type = RRDP_HTTP_INI; 356 struct ibuf *b; 357 int pi[2]; 358 359 if (pipe2(pi, O_CLOEXEC | O_NONBLOCK) == -1) 360 err(1, "pipe"); 361 362 b = io_new_buffer(); 363 io_simple_buffer(b, &type, sizeof(type)); 364 io_simple_buffer(b, &id, sizeof(id)); 365 b->fd = pi[0]; 366 io_close_buffer(&rrdpq, b); 367 368 http_fetch(id, uri, last_mod, pi[1]); 369} 370 371void 372rrdp_http_done(unsigned int id, enum http_result res, const char *last_mod) 373{ 374 enum rrdp_msg type = RRDP_HTTP_FIN; 375 struct ibuf *b; 376 377 /* RRDP request, relay response over to the rrdp process */ 378 b = io_new_buffer(); 379 io_simple_buffer(b, &type, sizeof(type)); 380 io_simple_buffer(b, &id, sizeof(id)); 381 io_simple_buffer(b, &res, sizeof(res)); 382 io_str_buffer(b, last_mod); 383 io_close_buffer(&rrdpq, b); 384} 385 386/* 387 * Add a file (CER, ROA, CRL) from an MFT file, RFC 6486. 388 * These are always relative to the directory in which "mft" sits. 389 */ 390static void 391queue_add_from_mft(const struct mft *mft) 392{ 393 size_t i; 394 struct repo *rp; 395 const struct mftfile *f; 396 char *mftaki, *nfile, *npath = NULL; 397 398 rp = repo_byid(mft->repoid); 399 for (i = 0; i < mft->filesz; i++) { 400 f = &mft->files[i]; 401 402 if (f->type == RTYPE_INVALID || f->type == RTYPE_CRL) 403 continue; 404 405 if (mft->path != NULL) 406 if ((npath = strdup(mft->path)) == NULL) 407 err(1, NULL); 408 if ((nfile = strdup(f->file)) == NULL) 409 err(1, NULL); 410 if ((mftaki = strdup(mft->aki)) == NULL) 411 err(1, NULL); 412 entityq_add(npath, nfile, f->type, f->location, rp, NULL, 0, 413 mft->talid, mftaki); 414 } 415} 416 417/* 418 * Add a local file to the queue of files to fetch. 419 */ 420static void 421queue_add_file(const char *file, enum rtype type, int talid) 422{ 423 unsigned char *buf = NULL; 424 char *nfile; 425 size_t len = 0; 426 427 if (!filemode || strncmp(file, "rsync://", strlen("rsync://")) != 0) { 428 buf = load_file(file, &len); 429 if (buf == NULL) 430 err(1, "%s", file); 431 } 432 433 if ((nfile = strdup(file)) == NULL) 434 err(1, NULL); 435 /* Not in a repository, so directly add to queue. */ 436 entityq_add(NULL, nfile, type, DIR_UNKNOWN, NULL, buf, len, talid, 437 NULL); 438} 439 440/* 441 * Add URIs (CER) from a TAL file, RFC 8630. 442 */ 443static void 444queue_add_from_tal(struct tal *tal) 445{ 446 struct repo *repo; 447 unsigned char *data; 448 char *nfile; 449 450 assert(tal->urisz); 451 452 if ((taldescs[tal->id] = strdup(tal->descr)) == NULL) 453 err(1, NULL); 454 455 /* figure out the TA filename, must be done before repo lookup */ 456 nfile = strrchr(tal->uri[0], '/'); 457 assert(nfile != NULL); 458 if ((nfile = strdup(nfile + 1)) == NULL) 459 err(1, NULL); 460 461 /* Look up the repository. */ 462 repo = ta_lookup(tal->id, tal); 463 if (repo == NULL) { 464 free(nfile); 465 return; 466 } 467 468 /* steal the pkey from the tal structure */ 469 data = tal->pkey; 470 tal->pkey = NULL; 471 entityq_add(NULL, nfile, RTYPE_CER, DIR_VALID, repo, data, 472 tal->pkeysz, tal->id, NULL); 473} 474 475/* 476 * Add a manifest (MFT) found in an X509 certificate, RFC 6487. 477 */ 478static void 479queue_add_from_cert(const struct cert *cert) 480{ 481 struct repo *repo; 482 struct fqdnlistentry *le; 483 char *nfile, *npath, *host; 484 const char *uri, *repouri, *file; 485 size_t repourisz; 486 int shortlisted = 0; 487 488 if (strncmp(cert->repo, "rsync://", 8) != 0) 489 errx(1, "unexpected protocol"); 490 host = cert->repo + 8; 491 492 LIST_FOREACH(le, &skiplist, entry) { 493 if (strncasecmp(host, le->fqdn, strcspn(host, "/")) == 0) { 494 warnx("skipping %s (listed in skiplist)", cert->repo); 495 return; 496 } 497 } 498 499 LIST_FOREACH(le, &shortlist, entry) { 500 if (strncasecmp(host, le->fqdn, strcspn(host, "/")) == 0) { 501 shortlisted = 1; 502 break; 503 } 504 } 505 if (shortlistmode && shortlisted == 0) { 506 if (verbose) 507 warnx("skipping %s (not shortlisted)", cert->repo); 508 return; 509 } 510 511 repo = repo_lookup(cert->talid, cert->repo, 512 rrdpon ? cert->notify : NULL); 513 if (repo == NULL) 514 return; 515 516 /* 517 * Figure out the cert filename and path by chopping up the 518 * MFT URI in the cert based on the repo base URI. 519 */ 520 uri = cert->mft; 521 repouri = repo_uri(repo); 522 repourisz = strlen(repouri); 523 if (strncmp(repouri, cert->mft, repourisz) != 0) { 524 warnx("%s: URI %s outside of repository", repouri, uri); 525 return; 526 } 527 uri += repourisz + 1; /* skip base and '/' */ 528 file = strrchr(uri, '/'); 529 if (file == NULL) { 530 npath = NULL; 531 if ((nfile = strdup(uri)) == NULL) 532 err(1, NULL); 533 } else { 534 if ((npath = strndup(uri, file - uri)) == NULL) 535 err(1, NULL); 536 if ((nfile = strdup(file + 1)) == NULL) 537 err(1, NULL); 538 } 539 540 entityq_add(npath, nfile, RTYPE_MFT, DIR_UNKNOWN, repo, NULL, 0, 541 cert->talid, NULL); 542} 543 544/* 545 * Process parsed content. 546 * For non-ROAs, we grok for more data. 547 * For ROAs, we want to extract the valid info. 548 * In all cases, we gather statistics. 549 */ 550static void 551entity_process(struct ibuf *b, struct stats *st, struct vrp_tree *tree, 552 struct brk_tree *brktree, struct vap_tree *vaptree) 553{ 554 enum rtype type; 555 struct tal *tal; 556 struct cert *cert; 557 struct mft *mft; 558 struct roa *roa; 559 struct aspa *aspa; 560 struct repo *rp; 561 char *file; 562 time_t mtime; 563 unsigned int id; 564 int talid; 565 int c; 566 567 /* 568 * For most of these, we first read whether there's any content 569 * at all---this means that the syntactic parse failed (X509 570 * certificate, for example). 571 * We follow that up with whether the resources didn't parse. 572 */ 573 io_read_buf(b, &type, sizeof(type)); 574 io_read_buf(b, &id, sizeof(id)); 575 io_read_buf(b, &talid, sizeof(talid)); 576 io_read_str(b, &file); 577 io_read_buf(b, &mtime, sizeof(mtime)); 578 579 /* in filemode messages can be ignored, only the accounting matters */ 580 if (filemode) 581 goto done; 582 583 if (filepath_add(&fpt, file, mtime) == 0) { 584 warnx("%s: File already visited", file); 585 goto done; 586 } 587 588 rp = repo_byid(id); 589 repo_stat_inc(rp, talid, type, STYPE_OK); 590 switch (type) { 591 case RTYPE_TAL: 592 st->tals++; 593 tal = tal_read(b); 594 queue_add_from_tal(tal); 595 tal_free(tal); 596 break; 597 case RTYPE_CER: 598 io_read_buf(b, &c, sizeof(c)); 599 if (c == 0) { 600 repo_stat_inc(rp, talid, type, STYPE_FAIL); 601 break; 602 } 603 cert = cert_read(b); 604 switch (cert->purpose) { 605 case CERT_PURPOSE_CA: 606 queue_add_from_cert(cert); 607 break; 608 case CERT_PURPOSE_BGPSEC_ROUTER: 609 cert_insert_brks(brktree, cert); 610 repo_stat_inc(rp, talid, type, STYPE_BGPSEC); 611 break; 612 default: 613 errx(1, "unexpected cert purpose received"); 614 break; 615 } 616 cert_free(cert); 617 break; 618 case RTYPE_MFT: 619 io_read_buf(b, &c, sizeof(c)); 620 if (c == 0) { 621 repo_stat_inc(rp, talid, type, STYPE_FAIL); 622 break; 623 } 624 mft = mft_read(b); 625 if (!mft->stale) 626 queue_add_from_mft(mft); 627 else 628 repo_stat_inc(rp, talid, type, STYPE_STALE); 629 mft_free(mft); 630 break; 631 case RTYPE_CRL: 632 /* CRLs are sent together with MFT and not accounted for */ 633 entity_queue++; 634 break; 635 case RTYPE_ROA: 636 io_read_buf(b, &c, sizeof(c)); 637 if (c == 0) { 638 repo_stat_inc(rp, talid, type, STYPE_FAIL); 639 break; 640 } 641 roa = roa_read(b); 642 if (roa->valid) 643 roa_insert_vrps(tree, roa, rp); 644 else 645 repo_stat_inc(rp, talid, type, STYPE_INVALID); 646 roa_free(roa); 647 break; 648 case RTYPE_GBR: 649 break; 650 case RTYPE_ASPA: 651 io_read_buf(b, &c, sizeof(c)); 652 if (c == 0) { 653 repo_stat_inc(rp, talid, type, STYPE_FAIL); 654 break; 655 } 656 aspa = aspa_read(b); 657 if (aspa->valid) 658 aspa_insert_vaps(vaptree, aspa, rp); 659 else 660 repo_stat_inc(rp, talid, type, STYPE_INVALID); 661 aspa_free(aspa); 662 break; 663 case RTYPE_TAK: 664 break; 665 case RTYPE_FILE: 666 break; 667 default: 668 warnx("%s: unknown entity type %d", file, type); 669 break; 670 } 671 672done: 673 free(file); 674 entity_queue--; 675} 676 677static void 678rrdp_process(struct ibuf *b) 679{ 680 enum rrdp_msg type; 681 enum publish_type pt; 682 struct rrdp_session s; 683 char *uri, *last_mod, *data; 684 char hash[SHA256_DIGEST_LENGTH]; 685 size_t dsz; 686 unsigned int id; 687 int ok; 688 689 io_read_buf(b, &type, sizeof(type)); 690 io_read_buf(b, &id, sizeof(id)); 691 692 switch (type) { 693 case RRDP_END: 694 io_read_buf(b, &ok, sizeof(ok)); 695 rrdp_finish(id, ok); 696 break; 697 case RRDP_HTTP_REQ: 698 io_read_str(b, &uri); 699 io_read_str(b, &last_mod); 700 rrdp_http_fetch(id, uri, last_mod); 701 break; 702 case RRDP_SESSION: 703 io_read_str(b, &s.session_id); 704 io_read_buf(b, &s.serial, sizeof(s.serial)); 705 io_read_str(b, &s.last_mod); 706 rrdp_save_state(id, &s); 707 free(s.session_id); 708 free(s.last_mod); 709 break; 710 case RRDP_FILE: 711 io_read_buf(b, &pt, sizeof(pt)); 712 if (pt != PUB_ADD) 713 io_read_buf(b, &hash, sizeof(hash)); 714 io_read_str(b, &uri); 715 io_read_buf_alloc(b, (void **)&data, &dsz); 716 717 ok = rrdp_handle_file(id, pt, uri, hash, sizeof(hash), 718 data, dsz); 719 rrdp_file_resp(id, ok); 720 721 free(uri); 722 free(data); 723 break; 724 case RRDP_CLEAR: 725 rrdp_clear(id); 726 break; 727 default: 728 errx(1, "unexpected rrdp response"); 729 } 730} 731 732static void 733sum_stats(const struct repo *rp, const struct repotalstats *in, void *arg) 734{ 735 struct repotalstats *out = arg; 736 737 out->mfts += in->mfts; 738 out->mfts_fail += in->mfts_fail; 739 out->mfts_stale += in->mfts_stale; 740 out->certs += in->certs; 741 out->certs_fail += in->certs_fail; 742 out->roas += in->roas; 743 out->roas_fail += in->roas_fail; 744 out->roas_invalid += in->roas_invalid; 745 out->aspas += in->aspas; 746 out->aspas_fail += in->aspas_fail; 747 out->aspas_invalid += in->aspas_invalid; 748 out->brks += in->brks; 749 out->crls += in->crls; 750 out->gbrs += in->gbrs; 751 out->taks += in->taks; 752 out->vrps += in->vrps; 753 out->vrps_uniqs += in->vrps_uniqs; 754 out->vaps += in->vaps; 755 out->vaps_uniqs += in->vaps_uniqs; 756 out->vaps_pas += in->vaps_pas; 757 out->vaps_pas4 += in->vaps_pas4; 758 out->vaps_pas6 += in->vaps_pas6; 759} 760 761static void 762sum_repostats(const struct repo *rp, const struct repostats *in, void *arg) 763{ 764 struct repostats *out = arg; 765 766 out->del_files += in->del_files; 767 out->extra_files += in->extra_files; 768 out->del_extra_files += in->del_extra_files; 769 out->del_dirs += in->del_dirs; 770 timespecadd(&in->sync_time, &out->sync_time, &out->sync_time); 771} 772 773/* 774 * Assign filenames ending in ".tal" in "/etc/rpki" into "tals", 775 * returning the number of files found and filled-in. 776 * This may be zero. 777 * Don't exceed "max" filenames. 778 */ 779static int 780tal_load_default(void) 781{ 782 static const char *confdir = "/etc/rpki"; 783 int s = 0; 784 char *path; 785 DIR *dirp; 786 struct dirent *dp; 787 788 dirp = opendir(confdir); 789 if (dirp == NULL) 790 err(1, "open %s", confdir); 791 while ((dp = readdir(dirp)) != NULL) { 792 if (fnmatch("*.tal", dp->d_name, FNM_PERIOD) == FNM_NOMATCH) 793 continue; 794 if (s >= TALSZ_MAX) 795 err(1, "too many tal files found in %s", 796 confdir); 797 if (asprintf(&path, "%s/%s", confdir, dp->d_name) == -1) 798 err(1, NULL); 799 tals[s++] = path; 800 } 801 closedir(dirp); 802 return s; 803} 804 805/* 806 * Load the list of FQDNs from the skiplist which are to be distrusted. 807 * Return 0 on success. 808 */ 809static void 810load_skiplist(const char *slf) 811{ 812 struct fqdnlistentry *le; 813 FILE *fp; 814 char *line = NULL; 815 size_t linesize = 0, linelen; 816 817 if ((fp = fopen(slf, "r")) == NULL) { 818 if (errno == ENOENT && strcmp(slf, DEFAULT_SKIPLIST_FILE) == 0) 819 return; 820 err(1, "failed to open %s", slf); 821 } 822 823 while (getline(&line, &linesize, fp) != -1) { 824 /* just eat comment lines or empty lines*/ 825 if (line[0] == '#' || line[0] == '\n') 826 continue; 827 828 if (line[0] == ' ' || line[0] == '\t') 829 errx(1, "invalid entry in skiplist: %s", line); 830 831 /* 832 * Ignore anything after comment sign, whitespaces, 833 * also chop off LF or CR. 834 */ 835 linelen = strcspn(line, " #\r\n\t"); 836 line[linelen] = '\0'; 837 838 if (!valid_uri(line, linelen, NULL)) 839 errx(1, "invalid entry in skiplist: %s", line); 840 841 if ((le = malloc(sizeof(struct fqdnlistentry))) == NULL) 842 err(1, NULL); 843 if ((le->fqdn = strdup(line)) == NULL) 844 err(1, NULL); 845 846 LIST_INSERT_HEAD(&skiplist, le, entry); 847 stats.skiplistentries++; 848 } 849 850 fclose(fp); 851 free(line); 852} 853 854/* 855 * Load shortlist entries. 856 */ 857static void 858load_shortlist(const char *fqdn) 859{ 860 struct fqdnlistentry *le; 861 862 if (!valid_uri(fqdn, strlen(fqdn), NULL)) 863 errx(1, "invalid fqdn passed to -q: %s", fqdn); 864 865 if ((le = malloc(sizeof(struct fqdnlistentry))) == NULL) 866 err(1, NULL); 867 868 if ((le->fqdn = strdup(fqdn)) == NULL) 869 err(1, NULL); 870 871 LIST_INSERT_HEAD(&shortlist, le, entry); 872} 873 874static void 875check_fs_size(int fd, const char *cachedir) 876{ 877 struct statvfs fs; 878 const long long minsize = 500 * 1024 * 1024; 879 const long long minnode = 300 * 1000; 880 881 if (fstatvfs(fd, &fs) == -1) 882 err(1, "statfs %s", cachedir); 883 884 if (fs.f_bavail < minsize / fs.f_frsize || 885 (fs.f_ffree > 0 && fs.f_favail < minnode)) { 886 fprintf(stderr, "WARNING: rpki-client may need more than " 887 "the available disk space\n" 888 "on the file-system holding %s.\n", cachedir); 889 fprintf(stderr, "available space: %lldkB, " 890 "suggested minimum %lldkB\n", 891 (long long)fs.f_bavail * fs.f_frsize / 1024, 892 minsize / 1024); 893 fprintf(stderr, "available inodes %lld, " 894 "suggested minimum %lld\n\n", 895 (long long)fs.f_favail, minnode); 896 fflush(stderr); 897 } 898} 899 900static pid_t 901process_start(const char *title, int *fd) 902{ 903 int fl = SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK; 904 pid_t pid; 905 int pair[2]; 906 907 if (socketpair(AF_UNIX, fl, 0, pair) == -1) 908 err(1, "socketpair"); 909 if ((pid = fork()) == -1) 910 err(1, "fork"); 911 912 if (pid == 0) { 913 setproctitle("%s", title); 914 /* change working directory to the cache directory */ 915 if (fchdir(cachefd) == -1) 916 err(1, "fchdir"); 917 if (!filemode && timeout > 0) 918 alarm(timeout); 919 close(pair[1]); 920 *fd = pair[0]; 921 } else { 922 close(pair[0]); 923 *fd = pair[1]; 924 } 925 return pid; 926} 927 928void 929suicide(int sig __attribute__((unused))) 930{ 931 killme = 1; 932} 933 934#define NPFD 4 935 936int 937main(int argc, char *argv[]) 938{ 939 int rc, c, i, st, proc, rsync, http, rrdp, hangup = 0; 940 pid_t pid, procpid, rsyncpid, httppid, rrdppid; 941 struct pollfd pfd[NPFD]; 942 struct msgbuf *queues[NPFD]; 943 struct ibuf *b, *httpbuf = NULL, *procbuf = NULL; 944 struct ibuf *rrdpbuf = NULL, *rsyncbuf = NULL; 945 char *rsync_prog = "openrsync"; 946 char *bind_addr = NULL; 947 const char *cachedir = NULL, *outputdir = NULL; 948 const char *errs, *name; 949 const char *skiplistfile = NULL; 950 struct vrp_tree vrps = RB_INITIALIZER(&vrps); 951 struct brk_tree brks = RB_INITIALIZER(&brks); 952 struct vap_tree vaps = RB_INITIALIZER(&vaps); 953 struct rusage ru; 954 struct timespec start_time, now_time; 955 956 clock_gettime(CLOCK_MONOTONIC, &start_time); 957 958 /* If started as root, priv-drop to _rpki-client */ 959 if (getuid() == 0) { 960 struct passwd *pw; 961 962 pw = getpwnam("_rpki-client"); 963 if (!pw) 964 errx(1, "no _rpki-client user to revoke to"); 965 if (setgroups(1, &pw->pw_gid) == -1 || 966 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 || 967 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) 968 err(1, "unable to revoke privs"); 969 } 970 cachedir = RPKI_PATH_BASE_DIR; 971 outputdir = RPKI_PATH_OUT_DIR; 972 repo_timeout = timeout / 4; 973 skiplistfile = DEFAULT_SKIPLIST_FILE; 974 975 if (pledge("stdio rpath wpath cpath inet fattr dns sendfd recvfd " 976 "proc exec unveil", NULL) == -1) 977 err(1, "pledge"); 978 979 while ((c = getopt(argc, argv, "Ab:Bcd:e:fH:jmnoP:rRs:S:t:T:vV")) != -1) 980 switch (c) { 981 case 'A': 982 excludeaspa = 1; 983 break; 984 case 'b': 985 bind_addr = optarg; 986 break; 987 case 'B': 988 outformats |= FORMAT_BIRD; 989 break; 990 case 'c': 991 outformats |= FORMAT_CSV; 992 break; 993 case 'd': 994 cachedir = optarg; 995 break; 996 case 'e': 997 rsync_prog = optarg; 998 break; 999 case 'f': 1000 filemode = 1; 1001 noop = 1; 1002 break; 1003 case 'H': 1004 shortlistmode = 1; 1005 load_shortlist(optarg); 1006 break; 1007 case 'j': 1008 outformats |= FORMAT_JSON; 1009 break; 1010 case 'm': 1011 outformats |= FORMAT_OMETRIC; 1012 break; 1013 case 'n': 1014 noop = 1; 1015 break; 1016 case 'o': 1017 outformats |= FORMAT_OPENBGPD; 1018 break; 1019 case 'P': 1020 evaluation_time = strtonum(optarg, X509_TIME_MIN + 1, 1021 X509_TIME_MAX, &errs); 1022 if (errs) 1023 errx(1, "-P: time in seconds %s", errs); 1024 break; 1025 case 'R': 1026 rrdpon = 0; 1027 break; 1028 case 'r': /* Remove after OpenBSD 7.3 */ 1029 rrdpon = 1; 1030 break; 1031 case 's': 1032 timeout = strtonum(optarg, 0, 24*60*60, &errs); 1033 if (errs) 1034 errx(1, "-s: %s", errs); 1035 if (timeout == 0) 1036 repo_timeout = 24*60*60; 1037 else 1038 repo_timeout = timeout / 4; 1039 break; 1040 case 'S': 1041 skiplistfile = optarg; 1042 break; 1043 case 't': 1044 if (talsz >= TALSZ_MAX) 1045 err(1, "too many tal files specified"); 1046 tals[talsz++] = optarg; 1047 break; 1048 case 'T': 1049 bird_tablename = optarg; 1050 break; 1051 case 'v': 1052 verbose++; 1053 break; 1054 case 'V': 1055 fprintf(stderr, "rpki-client %s\n", RPKI_VERSION); 1056 return 0; 1057 default: 1058 goto usage; 1059 } 1060 1061 argv += optind; 1062 argc -= optind; 1063 1064 if (!filemode) { 1065 if (argc == 1) 1066 outputdir = argv[0]; 1067 else if (argc > 1) 1068 goto usage; 1069 1070 if (outputdir == NULL) { 1071 warnx("output directory required"); 1072 goto usage; 1073 } 1074 } else { 1075 if (argc == 0) 1076 goto usage; 1077 outputdir = NULL; 1078 } 1079 1080 if (cachedir == NULL) { 1081 warnx("cache directory required"); 1082 goto usage; 1083 } 1084 1085 signal(SIGPIPE, SIG_IGN); 1086 1087 if ((cachefd = open(cachedir, O_RDONLY | O_DIRECTORY)) == -1) 1088 err(1, "cache directory %s", cachedir); 1089 if (outputdir != NULL) { 1090 if ((outdirfd = open(outputdir, O_RDONLY | O_DIRECTORY)) == -1) 1091 err(1, "output directory %s", outputdir); 1092 if (outformats == 0) 1093 outformats = FORMAT_OPENBGPD; 1094 } 1095 1096 check_fs_size(cachefd, cachedir); 1097 1098 if (talsz == 0) 1099 talsz = tal_load_default(); 1100 if (talsz == 0) 1101 err(1, "no TAL files found in %s", "/etc/rpki"); 1102 1103 /* 1104 * Create the file reader as a jailed child process. 1105 * It will be responsible for reading all of the files (ROAs, 1106 * manifests, certificates, etc.) and returning contents. 1107 */ 1108 1109 procpid = process_start("parser", &proc); 1110 if (procpid == 0) { 1111 if (!filemode) 1112 proc_parser(proc); 1113 else 1114 proc_filemode(proc); 1115 } 1116 1117 /* 1118 * Create a process that will do the rsync'ing. 1119 * This process is responsible for making sure that all the 1120 * repositories referenced by a certificate manifest (or the 1121 * TAL) exists and has been downloaded. 1122 */ 1123 1124 if (!noop) { 1125 rsyncpid = process_start("rsync", &rsync); 1126 if (rsyncpid == 0) { 1127 close(proc); 1128 proc_rsync(rsync_prog, bind_addr, rsync); 1129 } 1130 } else { 1131 rsync = -1; 1132 rsyncpid = -1; 1133 } 1134 1135 /* 1136 * Create a process that will fetch data via https. 1137 * With every request the http process receives a file descriptor 1138 * where the data should be written to. 1139 */ 1140 1141 if (!noop && rrdpon) { 1142 httppid = process_start("http", &http); 1143 1144 if (httppid == 0) { 1145 close(proc); 1146 close(rsync); 1147 proc_http(bind_addr, http); 1148 } 1149 } else { 1150 http = -1; 1151 httppid = -1; 1152 } 1153 1154 /* 1155 * Create a process that will process RRDP. 1156 * The rrdp process requires the http process to fetch the various 1157 * XML files and does this via the main process. 1158 */ 1159 1160 if (!noop && rrdpon) { 1161 rrdppid = process_start("rrdp", &rrdp); 1162 if (rrdppid == 0) { 1163 close(proc); 1164 close(rsync); 1165 close(http); 1166 proc_rrdp(rrdp); 1167 } 1168 } else { 1169 rrdp = -1; 1170 rrdppid = -1; 1171 } 1172 1173 if (!filemode && timeout > 0) { 1174 /* 1175 * Commit suicide eventually 1176 * cron will normally start a new one 1177 */ 1178 alarm(timeout); 1179 signal(SIGALRM, suicide); 1180 1181 /* give up a bit before the hard timeout and try to finish up */ 1182 if (!noop) 1183 deadline = getmonotime() + timeout - repo_timeout / 2; 1184 } 1185 1186 if (pledge("stdio rpath wpath cpath fattr sendfd unveil", NULL) == -1) 1187 err(1, "pledge"); 1188 1189 msgbuf_init(&procq); 1190 msgbuf_init(&rsyncq); 1191 msgbuf_init(&httpq); 1192 msgbuf_init(&rrdpq); 1193 procq.fd = proc; 1194 rsyncq.fd = rsync; 1195 httpq.fd = http; 1196 rrdpq.fd = rrdp; 1197 1198 /* 1199 * The main process drives the top-down scan to leaf ROAs using 1200 * data downloaded by the rsync process and parsed by the 1201 * parsing process. 1202 */ 1203 1204 pfd[0].fd = proc; 1205 queues[0] = &procq; 1206 pfd[1].fd = rsync; 1207 queues[1] = &rsyncq; 1208 pfd[2].fd = http; 1209 queues[2] = &httpq; 1210 pfd[3].fd = rrdp; 1211 queues[3] = &rrdpq; 1212 1213 load_skiplist(skiplistfile); 1214 1215 /* 1216 * Prime the process with our TAL files. 1217 * These will (hopefully) contain links to manifests and we 1218 * can get the ball rolling. 1219 */ 1220 1221 for (i = 0; i < talsz; i++) 1222 queue_add_file(tals[i], RTYPE_TAL, i); 1223 1224 if (filemode) { 1225 while (*argv != NULL) 1226 queue_add_file(*argv++, RTYPE_FILE, 0); 1227 1228 if (unveil(cachedir, "r") == -1) 1229 err(1, "unveil cachedir"); 1230 } else { 1231 if (unveil(outputdir, "rwc") == -1) 1232 err(1, "unveil outputdir"); 1233 if (unveil(cachedir, "rwc") == -1) 1234 err(1, "unveil cachedir"); 1235 } 1236 if (pledge("stdio rpath wpath cpath fattr sendfd", NULL) == -1) 1237 err(1, "unveil"); 1238 1239 /* change working directory to the cache directory */ 1240 if (fchdir(cachefd) == -1) 1241 err(1, "fchdir"); 1242 1243 while (entity_queue > 0 && !killme) { 1244 int polltim; 1245 1246 for (i = 0; i < NPFD; i++) { 1247 pfd[i].events = POLLIN; 1248 if (queues[i]->queued) 1249 pfd[i].events |= POLLOUT; 1250 } 1251 1252 polltim = repo_check_timeout(INFTIM); 1253 1254 if (poll(pfd, NPFD, polltim) == -1) { 1255 if (errno == EINTR) 1256 continue; 1257 err(1, "poll"); 1258 } 1259 1260 for (i = 0; i < NPFD; i++) { 1261 if (pfd[i].revents & (POLLERR|POLLNVAL)) { 1262 warnx("poll[%d]: bad fd", i); 1263 hangup = 1; 1264 } 1265 if (pfd[i].revents & POLLHUP) 1266 hangup = 1; 1267 if (pfd[i].revents & POLLOUT) { 1268 switch (msgbuf_write(queues[i])) { 1269 case 0: 1270 warnx("write[%d]: " 1271 "connection closed", i); 1272 hangup = 1; 1273 break; 1274 case -1: 1275 warn("write[%d]", i); 1276 hangup = 1; 1277 break; 1278 } 1279 } 1280 } 1281 if (hangup) 1282 break; 1283 1284 /* 1285 * Check the rsync and http process. 1286 * This means that one of our modules has completed 1287 * downloading and we can flush the module requests into 1288 * the parser process. 1289 */ 1290 1291 if ((pfd[1].revents & POLLIN)) { 1292 b = io_buf_read(rsync, &rsyncbuf); 1293 if (b != NULL) { 1294 unsigned int id; 1295 int ok; 1296 1297 io_read_buf(b, &id, sizeof(id)); 1298 io_read_buf(b, &ok, sizeof(ok)); 1299 rsync_finish(id, ok); 1300 ibuf_free(b); 1301 } 1302 } 1303 1304 if ((pfd[2].revents & POLLIN)) { 1305 b = io_buf_read(http, &httpbuf); 1306 if (b != NULL) { 1307 unsigned int id; 1308 enum http_result res; 1309 char *last_mod; 1310 1311 io_read_buf(b, &id, sizeof(id)); 1312 io_read_buf(b, &res, sizeof(res)); 1313 io_read_str(b, &last_mod); 1314 http_finish(id, res, last_mod); 1315 free(last_mod); 1316 ibuf_free(b); 1317 } 1318 } 1319 1320 /* 1321 * Handle RRDP requests here. 1322 */ 1323 if ((pfd[3].revents & POLLIN)) { 1324 b = io_buf_read(rrdp, &rrdpbuf); 1325 if (b != NULL) { 1326 rrdp_process(b); 1327 ibuf_free(b); 1328 } 1329 } 1330 1331 /* 1332 * The parser has finished something for us. 1333 * Dequeue these one by one. 1334 */ 1335 1336 if ((pfd[0].revents & POLLIN)) { 1337 b = io_buf_read(proc, &procbuf); 1338 if (b != NULL) { 1339 entity_process(b, &stats, &vrps, &brks, &vaps); 1340 ibuf_free(b); 1341 } 1342 } 1343 } 1344 1345 signal(SIGALRM, SIG_DFL); 1346 if (killme) { 1347 syslog(LOG_CRIT|LOG_DAEMON, 1348 "excessive runtime (%d seconds), giving up", timeout); 1349 errx(1, "excessive runtime (%d seconds), giving up", timeout); 1350 } 1351 1352 /* 1353 * For clean-up, close the input for the parser and rsync 1354 * process. 1355 * This will cause them to exit, then we reap them. 1356 */ 1357 1358 close(proc); 1359 close(rsync); 1360 close(http); 1361 close(rrdp); 1362 1363 rc = 0; 1364 for (;;) { 1365 pid = waitpid(WAIT_ANY, &st, 0); 1366 if (pid == -1) { 1367 if (errno == EINTR) 1368 continue; 1369 if (errno == ECHILD) 1370 break; 1371 err(1, "wait"); 1372 } 1373 1374 if (pid == procpid) 1375 name = "parser"; 1376 else if (pid == rsyncpid) 1377 name = "rsync"; 1378 else if (pid == httppid) 1379 name = "http"; 1380 else if (pid == rrdppid) 1381 name = "rrdp"; 1382 else 1383 name = "unknown"; 1384 1385 if (WIFSIGNALED(st)) { 1386 warnx("%s terminated signal %d", name, WTERMSIG(st)); 1387 rc = 1; 1388 } else if (!WIFEXITED(st) || WEXITSTATUS(st) != 0) { 1389 warnx("%s process exited abnormally", name); 1390 rc = 1; 1391 } 1392 } 1393 1394 /* processing did not finish because of error */ 1395 if (entity_queue != 0) 1396 errx(1, "not all files processed, giving up"); 1397 1398 /* if processing in filemode the process is done, no cleanup */ 1399 if (filemode) 1400 return rc; 1401 1402 logx("all files parsed: generating output"); 1403 1404 if (!noop) 1405 repo_cleanup(&fpt, cachefd); 1406 1407 clock_gettime(CLOCK_MONOTONIC, &now_time); 1408 timespecsub(&now_time, &start_time, &stats.elapsed_time); 1409 if (getrusage(RUSAGE_SELF, &ru) == 0) { 1410 TIMEVAL_TO_TIMESPEC(&ru.ru_utime, &stats.user_time); 1411 TIMEVAL_TO_TIMESPEC(&ru.ru_stime, &stats.system_time); 1412 } 1413 if (getrusage(RUSAGE_CHILDREN, &ru) == 0) { 1414 struct timespec ts; 1415 1416 TIMEVAL_TO_TIMESPEC(&ru.ru_utime, &ts); 1417 timespecadd(&stats.user_time, &ts, &stats.user_time); 1418 TIMEVAL_TO_TIMESPEC(&ru.ru_stime, &ts); 1419 timespecadd(&stats.system_time, &ts, &stats.system_time); 1420 } 1421 1422 /* change working directory to the output directory */ 1423 if (fchdir(outdirfd) == -1) 1424 err(1, "fchdir output dir"); 1425 1426 for (i = 0; i < talsz; i++) { 1427 repo_tal_stats_collect(sum_stats, i, &talstats[i]); 1428 repo_tal_stats_collect(sum_stats, i, &stats.repo_tal_stats); 1429 } 1430 repo_stats_collect(sum_repostats, &stats.repo_stats); 1431 1432 if (outputfiles(&vrps, &brks, &vaps, &stats)) 1433 rc = 1; 1434 1435 printf("Processing time %lld seconds " 1436 "(%lld seconds user, %lld seconds system)\n", 1437 (long long)stats.elapsed_time.tv_sec, 1438 (long long)stats.user_time.tv_sec, 1439 (long long)stats.system_time.tv_sec); 1440 printf("Skiplist entries: %u\n", stats.skiplistentries); 1441 printf("Route Origin Authorizations: %u (%u failed parse, %u " 1442 "invalid)\n", stats.repo_tal_stats.roas, 1443 stats.repo_tal_stats.roas_fail, 1444 stats.repo_tal_stats.roas_invalid); 1445 printf("AS Provider Attestations: %u (%u failed parse, %u " 1446 "invalid)\n", stats.repo_tal_stats.aspas, 1447 stats.repo_tal_stats.aspas_fail, 1448 stats.repo_tal_stats.aspas_invalid); 1449 printf("BGPsec Router Certificates: %u\n", stats.repo_tal_stats.brks); 1450 printf("Certificates: %u (%u invalid)\n", 1451 stats.repo_tal_stats.certs, stats.repo_tal_stats.certs_fail); 1452 printf("Trust Anchor Locators: %u (%u invalid)\n", 1453 stats.tals, talsz - stats.tals); 1454 printf("Manifests: %u (%u failed parse, %u stale)\n", 1455 stats.repo_tal_stats.mfts, stats.repo_tal_stats.mfts_fail, 1456 stats.repo_tal_stats.mfts_stale); 1457 printf("Certificate revocation lists: %u\n", stats.repo_tal_stats.crls); 1458 printf("Ghostbuster records: %u\n", stats.repo_tal_stats.gbrs); 1459 printf("Trust Anchor Keys: %u\n", stats.repo_tal_stats.taks); 1460 printf("Repositories: %u\n", stats.repos); 1461 printf("Cleanup: removed %u files, %u directories\n" 1462 "Repository cleanup: kept %u and removed %u superfluous files\n", 1463 stats.repo_stats.del_files, stats.repo_stats.del_dirs, 1464 stats.repo_stats.extra_files, stats.repo_stats.del_extra_files); 1465 printf("VRP Entries: %u (%u unique)\n", stats.repo_tal_stats.vrps, 1466 stats.repo_tal_stats.vrps_uniqs); 1467 printf("VAP Entries: %u (%u unique)\n", stats.repo_tal_stats.vaps, 1468 stats.repo_tal_stats.vaps_uniqs); 1469 1470 /* Memory cleanup. */ 1471 repo_free(); 1472 1473 return rc; 1474 1475usage: 1476 fprintf(stderr, 1477 "usage: rpki-client [-ABcjmnoRrVv] [-b sourceaddr] [-d cachedir]" 1478 " [-e rsync_prog]\n" 1479 " [-H fqdn] [-P epoch] [-S skiplist] [-s timeout]" 1480 " [-T table]\n" 1481 " [-t tal] [outputdir]\n" 1482 " rpki-client [-Vv] [-d cachedir] [-j] [-t tal] -f file ..." 1483 "\n"); 1484 return 1; 1485} 1486