1*381ee599Stb /* $OpenBSD: roa.c,v 1.80 2024/11/12 09:23:07 tb Exp $ */ 29a7e9e7fSjob /* 3740e9a54Stb * Copyright (c) 2022 Theo Buehler <tb@openbsd.org> 49a7e9e7fSjob * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> 59a7e9e7fSjob * 69a7e9e7fSjob * Permission to use, copy, modify, and distribute this software for any 79a7e9e7fSjob * purpose with or without fee is hereby granted, provided that the above 89a7e9e7fSjob * copyright notice and this permission notice appear in all copies. 99a7e9e7fSjob * 109a7e9e7fSjob * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 119a7e9e7fSjob * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 129a7e9e7fSjob * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 139a7e9e7fSjob * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 149a7e9e7fSjob * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 159a7e9e7fSjob * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 169a7e9e7fSjob * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 179a7e9e7fSjob */ 189a7e9e7fSjob 199a7e9e7fSjob #include <assert.h> 209a7e9e7fSjob #include <err.h> 219a7e9e7fSjob #include <stdint.h> 229a7e9e7fSjob #include <stdlib.h> 239a7e9e7fSjob #include <string.h> 249a7e9e7fSjob #include <unistd.h> 259a7e9e7fSjob 26a1753de6Sclaudio #include <openssl/asn1.h> 2723e50b68Stb #include <openssl/asn1t.h> 2823e50b68Stb #include <openssl/stack.h> 2923e50b68Stb #include <openssl/safestack.h> 30a1753de6Sclaudio #include <openssl/x509.h> 31a1753de6Sclaudio 329a7e9e7fSjob #include "extern.h" 339a7e9e7fSjob 34de9b6f5dSclaudio extern ASN1_OBJECT *roa_oid; 35d2e465bbSclaudio 369a7e9e7fSjob /* 3723e50b68Stb * Types and templates for the ROA eContent, RFC 6482, section 3. 389a7e9e7fSjob */ 399a7e9e7fSjob 40d3d26873Sjob ASN1_ITEM_EXP ROAIPAddress_it; 41d3d26873Sjob ASN1_ITEM_EXP ROAIPAddressFamily_it; 42d3d26873Sjob ASN1_ITEM_EXP RouteOriginAttestation_it; 43d3d26873Sjob 4423e50b68Stb typedef struct { 4523e50b68Stb ASN1_BIT_STRING *address; 4623e50b68Stb ASN1_INTEGER *maxLength; 4723e50b68Stb } ROAIPAddress; 489a7e9e7fSjob 4923e50b68Stb DECLARE_STACK_OF(ROAIPAddress); 509a7e9e7fSjob 5123e50b68Stb typedef struct { 5223e50b68Stb ASN1_OCTET_STRING *addressFamily; 5323e50b68Stb STACK_OF(ROAIPAddress) *addresses; 5423e50b68Stb } ROAIPAddressFamily; 559a7e9e7fSjob 5623e50b68Stb DECLARE_STACK_OF(ROAIPAddressFamily); 579a7e9e7fSjob 589395df6fStb #ifndef DEFINE_STACK_OF 5923e50b68Stb #define sk_ROAIPAddress_num(st) SKM_sk_num(ROAIPAddress, (st)) 6023e50b68Stb #define sk_ROAIPAddress_value(st, i) SKM_sk_value(ROAIPAddress, (st), (i)) 619a7e9e7fSjob 6223e50b68Stb #define sk_ROAIPAddressFamily_num(st) SKM_sk_num(ROAIPAddressFamily, (st)) 6323e50b68Stb #define sk_ROAIPAddressFamily_value(st, i) \ 6423e50b68Stb SKM_sk_value(ROAIPAddressFamily, (st), (i)) 6523e50b68Stb #endif 669a7e9e7fSjob 6723e50b68Stb typedef struct { 6823e50b68Stb ASN1_INTEGER *version; 6923e50b68Stb ASN1_INTEGER *asid; 7023e50b68Stb STACK_OF(ROAIPAddressFamily) *ipAddrBlocks; 7123e50b68Stb } RouteOriginAttestation; 729a7e9e7fSjob 7323e50b68Stb ASN1_SEQUENCE(ROAIPAddress) = { 7423e50b68Stb ASN1_SIMPLE(ROAIPAddress, address, ASN1_BIT_STRING), 7523e50b68Stb ASN1_OPT(ROAIPAddress, maxLength, ASN1_INTEGER), 7623e50b68Stb } ASN1_SEQUENCE_END(ROAIPAddress); 779a7e9e7fSjob 7823e50b68Stb ASN1_SEQUENCE(ROAIPAddressFamily) = { 7923e50b68Stb ASN1_SIMPLE(ROAIPAddressFamily, addressFamily, ASN1_OCTET_STRING), 8023e50b68Stb ASN1_SEQUENCE_OF(ROAIPAddressFamily, addresses, ROAIPAddress), 8123e50b68Stb } ASN1_SEQUENCE_END(ROAIPAddressFamily); 829a7e9e7fSjob 8323e50b68Stb ASN1_SEQUENCE(RouteOriginAttestation) = { 84889cefa2Sjob ASN1_EXP_OPT(RouteOriginAttestation, version, ASN1_INTEGER, 0), 8523e50b68Stb ASN1_SIMPLE(RouteOriginAttestation, asid, ASN1_INTEGER), 8623e50b68Stb ASN1_SEQUENCE_OF(RouteOriginAttestation, ipAddrBlocks, 8723e50b68Stb ROAIPAddressFamily), 8823e50b68Stb } ASN1_SEQUENCE_END(RouteOriginAttestation); 899a7e9e7fSjob 9023e50b68Stb DECLARE_ASN1_FUNCTIONS(RouteOriginAttestation); 9123e50b68Stb IMPLEMENT_ASN1_FUNCTIONS(RouteOriginAttestation); 929a7e9e7fSjob 939a7e9e7fSjob /* 949a7e9e7fSjob * Parses the eContent section of an ROA file, RFC 6482, section 3. 959a7e9e7fSjob * Returns zero on failure, non-zero on success. 969a7e9e7fSjob */ 979a7e9e7fSjob static int 98be6e5ad5Stb roa_parse_econtent(const char *fn, struct roa *roa, const unsigned char *d, 99be6e5ad5Stb size_t dsz) 1009a7e9e7fSjob { 101d115f50dSjob const unsigned char *oder; 102cd9dc441Stb RouteOriginAttestation *roa_asn1; 10323e50b68Stb const ROAIPAddressFamily *addrfam; 10423e50b68Stb const STACK_OF(ROAIPAddress) *addrs; 105de494ec3Stb int addrsz, ipv4_seen = 0, ipv6_seen = 0; 10623e50b68Stb enum afi afi; 10723e50b68Stb const ROAIPAddress *addr; 108331e816cStb uint64_t maxlen; 10923e50b68Stb struct ip_addr ipaddr; 11023e50b68Stb struct roa_ip *res; 111b9ea95bbSjob int ipaddrblocksz; 11223e50b68Stb int i, j, rc = 0; 1139a7e9e7fSjob 114d115f50dSjob oder = d; 115cd9dc441Stb if ((roa_asn1 = d2i_RouteOriginAttestation(NULL, &d, dsz)) == NULL) { 116c0528901Stb warnx("%s: RFC 6482 section 3: failed to parse " 117be6e5ad5Stb "RouteOriginAttestation", fn); 1189a7e9e7fSjob goto out; 1199a7e9e7fSjob } 120d115f50dSjob if (d != oder + dsz) { 121be6e5ad5Stb warnx("%s: %td bytes trailing garbage in eContent", fn, 122d115f50dSjob oder + dsz - d); 123d115f50dSjob goto out; 124d115f50dSjob } 1259a7e9e7fSjob 126be6e5ad5Stb if (!valid_econtent_version(fn, roa_asn1->version, 0)) 1279a7e9e7fSjob goto out; 1289a7e9e7fSjob 129be6e5ad5Stb if (!as_id_parse(roa_asn1->asid, &roa->asid)) { 1309a7e9e7fSjob warnx("%s: RFC 6482 section 3.2: asID: " 131be6e5ad5Stb "malformed AS identifier", fn); 1329a7e9e7fSjob goto out; 1339a7e9e7fSjob } 1349a7e9e7fSjob 135cd9dc441Stb ipaddrblocksz = sk_ROAIPAddressFamily_num(roa_asn1->ipAddrBlocks); 136de494ec3Stb if (ipaddrblocksz != 1 && ipaddrblocksz != 2) { 137c7a965b3Stb warnx("%s: RFC 9582: unexpected number of ipAddrBlocks " 138be6e5ad5Stb "(got %d, expected 1 or 2)", fn, ipaddrblocksz); 139b9ea95bbSjob goto out; 140b9ea95bbSjob } 141b9ea95bbSjob 142b9ea95bbSjob for (i = 0; i < ipaddrblocksz; i++) { 143be6e5ad5Stb addrfam = sk_ROAIPAddressFamily_value(roa_asn1->ipAddrBlocks, 144be6e5ad5Stb i); 14523e50b68Stb addrs = addrfam->addresses; 14623e50b68Stb addrsz = sk_ROAIPAddress_num(addrs); 1479a7e9e7fSjob 148be6e5ad5Stb if (!ip_addr_afi_parse(fn, addrfam->addressFamily, &afi)) { 14923e50b68Stb warnx("%s: RFC 6482 section 3.3: addressFamily: " 150be6e5ad5Stb "invalid", fn); 1519a7e9e7fSjob goto out; 152820799d0Sjob } 15323e50b68Stb 154de494ec3Stb switch (afi) { 155de494ec3Stb case AFI_IPV4: 156de494ec3Stb if (ipv4_seen++ > 0) { 157c7a965b3Stb warnx("%s: RFC 9582 section 4.3.2: " 158be6e5ad5Stb "IPv4 appears twice", fn); 159de494ec3Stb goto out; 160de494ec3Stb } 161de494ec3Stb break; 162de494ec3Stb case AFI_IPV6: 163de494ec3Stb if (ipv6_seen++ > 0) { 164c7a965b3Stb warnx("%s: RFC 9582 section 4.3.2: " 165be6e5ad5Stb "IPv6 appears twice", fn); 166de494ec3Stb goto out; 167de494ec3Stb } 168de494ec3Stb break; 169de494ec3Stb } 170de494ec3Stb 171de494ec3Stb if (addrsz == 0) { 172c7a965b3Stb warnx("%s: RFC 9582, section 4.3.2: " 173be6e5ad5Stb "empty ROAIPAddressFamily", fn); 174de494ec3Stb goto out; 175de494ec3Stb } 176de494ec3Stb 177*381ee599Stb if (roa->num_ips + addrsz >= MAX_IP_SIZE) { 17816fbb15cSjob warnx("%s: too many ROAIPAddress entries: limit %d", 179be6e5ad5Stb fn, MAX_IP_SIZE); 1809a7e9e7fSjob goto out; 18123e50b68Stb } 182*381ee599Stb roa->ips = recallocarray(roa->ips, roa->num_ips, 183*381ee599Stb roa->num_ips + addrsz, sizeof(struct roa_ip)); 184be6e5ad5Stb if (roa->ips == NULL) 18523e50b68Stb err(1, NULL); 18623e50b68Stb 18723e50b68Stb for (j = 0; j < addrsz; j++) { 18823e50b68Stb addr = sk_ROAIPAddress_value(addrs, j); 18923e50b68Stb 190be6e5ad5Stb if (!ip_addr_parse(addr->address, afi, fn, &ipaddr)) { 19123e50b68Stb warnx("%s: RFC 6482 section 3.3: address: " 192be6e5ad5Stb "invalid IP address", fn); 19323e50b68Stb goto out; 19423e50b68Stb } 19523e50b68Stb maxlen = ipaddr.prefixlen; 19623e50b68Stb 19723e50b68Stb if (addr->maxLength != NULL) { 198331e816cStb if (!ASN1_INTEGER_get_uint64(&maxlen, 199331e816cStb addr->maxLength)) { 20023e50b68Stb warnx("%s: RFC 6482 section 3.2: " 201331e816cStb "ASN1_INTEGER_get_uint64 failed", 202be6e5ad5Stb fn); 20323e50b68Stb goto out; 20423e50b68Stb } 20523e50b68Stb if (ipaddr.prefixlen > maxlen) { 20623e50b68Stb warnx("%s: prefixlen (%d) larger than " 207be6e5ad5Stb "maxLength (%llu)", fn, 208331e816cStb ipaddr.prefixlen, 209331e816cStb (unsigned long long)maxlen); 21023e50b68Stb goto out; 21123e50b68Stb } 21223e50b68Stb if (maxlen > ((afi == AFI_IPV4) ? 32 : 128)) { 213331e816cStb warnx("%s: maxLength (%llu) too large", 214be6e5ad5Stb fn, (unsigned long long)maxlen); 21523e50b68Stb goto out; 21623e50b68Stb } 21723e50b68Stb } 21823e50b68Stb 219*381ee599Stb res = &roa->ips[roa->num_ips++]; 22023e50b68Stb res->addr = ipaddr; 22123e50b68Stb res->afi = afi; 22223e50b68Stb res->maxlength = maxlen; 22323e50b68Stb ip_roa_compose_ranges(res); 22423e50b68Stb } 22523e50b68Stb } 2269a7e9e7fSjob 2279a7e9e7fSjob rc = 1; 2289a7e9e7fSjob out: 229cd9dc441Stb RouteOriginAttestation_free(roa_asn1); 2309a7e9e7fSjob return rc; 2319a7e9e7fSjob } 2329a7e9e7fSjob 2339a7e9e7fSjob /* 234fc5c0efeSclaudio * Parse a full RFC 6482 file. 2359a7e9e7fSjob * Returns the ROA or NULL if the document was malformed. 2369a7e9e7fSjob */ 2379a7e9e7fSjob struct roa * 2380636c4d0Stb roa_parse(X509 **x509, const char *fn, int talid, const unsigned char *der, 2390636c4d0Stb size_t len) 2409a7e9e7fSjob { 241be6e5ad5Stb struct roa *roa; 2429a7e9e7fSjob size_t cmsz; 2439a7e9e7fSjob unsigned char *cms; 24499dbdb7fStb struct cert *cert = NULL; 245beccb378Stb time_t signtime = 0; 24699dbdb7fStb int rc = 0; 2479a7e9e7fSjob 2481330c972Stb cms = cms_parse_validate(x509, fn, der, len, roa_oid, &cmsz, &signtime); 2499a7e9e7fSjob if (cms == NULL) 2509a7e9e7fSjob return NULL; 2519a7e9e7fSjob 252be6e5ad5Stb if ((roa = calloc(1, sizeof(struct roa))) == NULL) 2537fe44250Sbenno err(1, NULL); 254be6e5ad5Stb roa->signtime = signtime; 2551f25fa5dStb 256be6e5ad5Stb if (!x509_get_aia(*x509, fn, &roa->aia)) 257f999fe57Sclaudio goto out; 258be6e5ad5Stb if (!x509_get_aki(*x509, fn, &roa->aki)) 259f999fe57Sclaudio goto out; 260be6e5ad5Stb if (!x509_get_sia(*x509, fn, &roa->sia)) 2612cf0e122Sjob goto out; 262be6e5ad5Stb if (!x509_get_ski(*x509, fn, &roa->ski)) 263f999fe57Sclaudio goto out; 264be6e5ad5Stb if (roa->aia == NULL || roa->aki == NULL || roa->sia == NULL || 265be6e5ad5Stb roa->ski == NULL) { 2661f25fa5dStb warnx("%s: RFC 6487 section 4.8: " 2672cf0e122Sjob "missing AIA, AKI, SIA, or SKI X509 extension", fn); 2689a7e9e7fSjob goto out; 2691f25fa5dStb } 2701f25fa5dStb 271be6e5ad5Stb if (!x509_get_notbefore(*x509, fn, &roa->notbefore)) 272a66158d7Sjob goto out; 273be6e5ad5Stb if (!x509_get_notafter(*x509, fn, &roa->notafter)) 274a66158d7Sjob goto out; 275a66158d7Sjob 276be6e5ad5Stb if (!roa_parse_econtent(fn, roa, cms, cmsz)) 2779a7e9e7fSjob goto out; 2789a7e9e7fSjob 279c9e39c95Sjob if (x509_any_inherits(*x509)) { 280ef8bdb37Sjob warnx("%s: inherit elements not allowed in EE cert", fn); 281c9e39c95Sjob goto out; 282c9e39c95Sjob } 283c9e39c95Sjob 284891d6bceSjob if ((cert = cert_parse_ee_cert(fn, talid, *x509)) == NULL) 28599dbdb7fStb goto out; 28699dbdb7fStb 287*381ee599Stb if (cert->num_ases > 0) { 28899dbdb7fStb warnx("%s: superfluous AS Resources extension present", fn); 28999dbdb7fStb goto out; 29099dbdb7fStb } 29199dbdb7fStb 292*381ee599Stb if (cert->num_ips == 0) { 293f814cda1Stb warnx("%s: no IP address present", fn); 294f814cda1Stb goto out; 295f814cda1Stb } 296f814cda1Stb 29799dbdb7fStb /* 29899dbdb7fStb * If the ROA isn't valid, we accept it anyway and depend upon 29999dbdb7fStb * the code around roa_read() to check the "valid" field itself. 30099dbdb7fStb */ 301be6e5ad5Stb roa->valid = valid_roa(fn, cert, roa); 30299dbdb7fStb 3039a7e9e7fSjob rc = 1; 3049a7e9e7fSjob out: 3059a7e9e7fSjob if (rc == 0) { 306be6e5ad5Stb roa_free(roa); 307be6e5ad5Stb roa = NULL; 3089a7e9e7fSjob X509_free(*x509); 3099a7e9e7fSjob *x509 = NULL; 3109a7e9e7fSjob } 31199dbdb7fStb cert_free(cert); 3129a7e9e7fSjob free(cms); 313be6e5ad5Stb return roa; 3149a7e9e7fSjob } 3159a7e9e7fSjob 3169a7e9e7fSjob /* 3179a7e9e7fSjob * Free an ROA pointer. 3189a7e9e7fSjob * Safe to call with NULL. 3199a7e9e7fSjob */ 3209a7e9e7fSjob void 3219a7e9e7fSjob roa_free(struct roa *p) 3229a7e9e7fSjob { 3239a7e9e7fSjob 3249a7e9e7fSjob if (p == NULL) 3259a7e9e7fSjob return; 326ebd55816Sjob free(p->aia); 3279a7e9e7fSjob free(p->aki); 3282cf0e122Sjob free(p->sia); 3299a7e9e7fSjob free(p->ski); 3309a7e9e7fSjob free(p->ips); 3319a7e9e7fSjob free(p); 3329a7e9e7fSjob } 3339a7e9e7fSjob 3349a7e9e7fSjob /* 3359a7e9e7fSjob * Serialise parsed ROA content. 3369a7e9e7fSjob * See roa_read() for reader. 3379a7e9e7fSjob */ 3389a7e9e7fSjob void 33908db1177Sclaudio roa_buffer(struct ibuf *b, const struct roa *p) 3409a7e9e7fSjob { 341dc508150Sclaudio io_simple_buffer(b, &p->valid, sizeof(p->valid)); 342dc508150Sclaudio io_simple_buffer(b, &p->asid, sizeof(p->asid)); 343dc508150Sclaudio io_simple_buffer(b, &p->talid, sizeof(p->talid)); 344*381ee599Stb io_simple_buffer(b, &p->num_ips, sizeof(p->num_ips)); 345534b6674Sjob io_simple_buffer(b, &p->expires, sizeof(p->expires)); 3469a7e9e7fSjob 347*381ee599Stb io_simple_buffer(b, p->ips, p->num_ips * sizeof(p->ips[0])); 3489a7e9e7fSjob 349ebd55816Sjob io_str_buffer(b, p->aia); 35008db1177Sclaudio io_str_buffer(b, p->aki); 35108db1177Sclaudio io_str_buffer(b, p->ski); 3529a7e9e7fSjob } 3539a7e9e7fSjob 3549a7e9e7fSjob /* 3559a7e9e7fSjob * Read parsed ROA content from descriptor. 3569a7e9e7fSjob * See roa_buffer() for writer. 3579a7e9e7fSjob * Result must be passed to roa_free(). 3589a7e9e7fSjob */ 3599a7e9e7fSjob struct roa * 3607eb79a4aSclaudio roa_read(struct ibuf *b) 3619a7e9e7fSjob { 3629a7e9e7fSjob struct roa *p; 3639a7e9e7fSjob 3649a7e9e7fSjob if ((p = calloc(1, sizeof(struct roa))) == NULL) 3657fe44250Sbenno err(1, NULL); 3669a7e9e7fSjob 367dc508150Sclaudio io_read_buf(b, &p->valid, sizeof(p->valid)); 368dc508150Sclaudio io_read_buf(b, &p->asid, sizeof(p->asid)); 369dc508150Sclaudio io_read_buf(b, &p->talid, sizeof(p->talid)); 370*381ee599Stb io_read_buf(b, &p->num_ips, sizeof(p->num_ips)); 371534b6674Sjob io_read_buf(b, &p->expires, sizeof(p->expires)); 3729a7e9e7fSjob 373*381ee599Stb if (p->num_ips > 0) { 374*381ee599Stb if ((p->ips = calloc(p->num_ips, sizeof(p->ips[0]))) == NULL) 3757fe44250Sbenno err(1, NULL); 376*381ee599Stb io_read_buf(b, p->ips, p->num_ips * sizeof(p->ips[0])); 377f814cda1Stb } 3789a7e9e7fSjob 3797eb79a4aSclaudio io_read_str(b, &p->aia); 3807eb79a4aSclaudio io_read_str(b, &p->aki); 3817eb79a4aSclaudio io_read_str(b, &p->ski); 382dc508150Sclaudio assert(p->aia && p->aki && p->ski); 38352c8fec2Sclaudio 3849a7e9e7fSjob return p; 3859a7e9e7fSjob } 386a382efa2Sclaudio 3879a6cbf4dSclaudio /* 3889a6cbf4dSclaudio * Add each IP address in the ROA into the VRP tree. 3899a6cbf4dSclaudio * Updates "vrps" to be the number of VRPs and "uniqs" to be the unique 3909a6cbf4dSclaudio * number of addresses. 3919a6cbf4dSclaudio */ 392a382efa2Sclaudio void 3934f5f25cbSclaudio roa_insert_vrps(struct vrp_tree *tree, struct roa *roa, struct repo *rp) 394a382efa2Sclaudio { 395a66158d7Sjob struct vrp *v, *found; 396a382efa2Sclaudio size_t i; 397a382efa2Sclaudio 398*381ee599Stb for (i = 0; i < roa->num_ips; i++) { 399a382efa2Sclaudio if ((v = malloc(sizeof(*v))) == NULL) 4007fe44250Sbenno err(1, NULL); 401a382efa2Sclaudio v->afi = roa->ips[i].afi; 402a382efa2Sclaudio v->addr = roa->ips[i].addr; 403a382efa2Sclaudio v->maxlength = roa->ips[i].maxlength; 404a382efa2Sclaudio v->asid = roa->asid; 405dc508150Sclaudio v->talid = roa->talid; 4064f5f25cbSclaudio if (rp != NULL) 4074f5f25cbSclaudio v->repoid = repo_id(rp); 4084f5f25cbSclaudio else 4094f5f25cbSclaudio v->repoid = 0; 410534b6674Sjob v->expires = roa->expires; 411a66158d7Sjob 412a66158d7Sjob /* 413a66158d7Sjob * Check if a similar VRP already exists in the tree. 414a66158d7Sjob * If the found VRP expires sooner, update it to this 415a66158d7Sjob * ROAs later expiry moment. 416a66158d7Sjob */ 417a66158d7Sjob if ((found = RB_INSERT(vrp_tree, tree, v)) != NULL) { 418a66158d7Sjob /* already exists */ 419a66158d7Sjob if (found->expires < v->expires) { 420a66158d7Sjob /* update found with preferred data */ 4214f5f25cbSclaudio /* adjust unique count */ 4224f5f25cbSclaudio repo_stat_inc(repo_byid(found->repoid), 4231fc2657fSclaudio found->talid, RTYPE_ROA, STYPE_DEC_UNIQUE); 4241fc2657fSclaudio found->expires = v->expires; 4251fc2657fSclaudio found->talid = v->talid; 4264f5f25cbSclaudio found->repoid = v->repoid; 4271fc2657fSclaudio repo_stat_inc(rp, v->talid, RTYPE_ROA, 4281fc2657fSclaudio STYPE_UNIQUE); 429a66158d7Sjob } 430a382efa2Sclaudio free(v); 431a66158d7Sjob } else 4321fc2657fSclaudio repo_stat_inc(rp, v->talid, RTYPE_ROA, STYPE_UNIQUE); 433a66158d7Sjob 4341fc2657fSclaudio repo_stat_inc(rp, roa->talid, RTYPE_ROA, STYPE_TOTAL); 435a382efa2Sclaudio } 436a382efa2Sclaudio } 437a382efa2Sclaudio 438a382efa2Sclaudio static inline int 439a382efa2Sclaudio vrpcmp(struct vrp *a, struct vrp *b) 440a382efa2Sclaudio { 441a382efa2Sclaudio int rv; 442a382efa2Sclaudio 443a382efa2Sclaudio if (a->afi > b->afi) 444a382efa2Sclaudio return 1; 445a382efa2Sclaudio if (a->afi < b->afi) 446a382efa2Sclaudio return -1; 447a382efa2Sclaudio switch (a->afi) { 448a382efa2Sclaudio case AFI_IPV4: 449a382efa2Sclaudio rv = memcmp(&a->addr.addr, &b->addr.addr, 4); 450a382efa2Sclaudio if (rv) 451a382efa2Sclaudio return rv; 452a382efa2Sclaudio break; 453a382efa2Sclaudio case AFI_IPV6: 454a382efa2Sclaudio rv = memcmp(&a->addr.addr, &b->addr.addr, 16); 455a382efa2Sclaudio if (rv) 456a382efa2Sclaudio return rv; 457a382efa2Sclaudio break; 458a29ddfd5Sjob default: 459a29ddfd5Sjob break; 460a382efa2Sclaudio } 461a382efa2Sclaudio /* a smaller prefixlen is considered bigger, e.g. /8 vs /10 */ 462a382efa2Sclaudio if (a->addr.prefixlen < b->addr.prefixlen) 463a382efa2Sclaudio return 1; 464a382efa2Sclaudio if (a->addr.prefixlen > b->addr.prefixlen) 465a382efa2Sclaudio return -1; 466a382efa2Sclaudio if (a->maxlength < b->maxlength) 467a382efa2Sclaudio return 1; 468a382efa2Sclaudio if (a->maxlength > b->maxlength) 469a382efa2Sclaudio return -1; 470a382efa2Sclaudio 471a382efa2Sclaudio if (a->asid > b->asid) 472a382efa2Sclaudio return 1; 473a382efa2Sclaudio if (a->asid < b->asid) 474a382efa2Sclaudio return -1; 475a382efa2Sclaudio 476a382efa2Sclaudio return 0; 477a382efa2Sclaudio } 478a382efa2Sclaudio 479a382efa2Sclaudio RB_GENERATE(vrp_tree, vrp, entry, vrpcmp); 480