Add aspa-set to openbgpd config output.
authorclaudio <claudio@openbsd.org>
Fri, 13 Jan 2023 08:58:36 +0000 (08:58 +0000)
committerclaudio <claudio@openbsd.org>
Fri, 13 Jan 2023 08:58:36 +0000 (08:58 +0000)
Change the way the validated ASPA tree is built since OpenBGPD config
follows more the ASPA profile and puts the optional AFI to each provider
ASnum instead of duplicated everything into an IPv4 and IPv6 tree.
The JSON output of ASPA is still the same.

The inclusion of the aspa-set can currently be disabled by the -A flag.
OK tb@

usr.sbin/rpki-client/aspa.c
usr.sbin/rpki-client/extern.h
usr.sbin/rpki-client/main.c
usr.sbin/rpki-client/output-bgpd.c
usr.sbin/rpki-client/output-json.c
usr.sbin/rpki-client/rpki-client.8

index d41ef8b..363288a 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: aspa.c,v 1.10 2022/12/15 12:02:29 claudio Exp $ */
+/*     $OpenBSD: aspa.c,v 1.11 2023/01/13 08:58:36 claudio Exp $ */
 /*
  * Copyright (c) 2022 Job Snijders <job@fastly.com>
  * Copyright (c) 2022 Theo Buehler <tb@openbsd.org>
@@ -328,120 +328,80 @@ aspa_read(struct ibuf *b)
 }
 
 /*
- * draft-ietf-sidrops-8210bis section 5.12 states:
- *
- *     "The router MUST see at most one ASPA for a given AFI from a cache for
- *      a particular Customer ASID active at any time. As a number of conditions
- *      in the global RPKI may present multiple valid ASPA RPKI records for a
- *      single customer to a particular RP cache, this places a burden on the
- *      cache to form the union of multiple ASPA records it has received from
- *      the global RPKI into one RPKI-To-Router (RTR) ASPA PDU."
- *
- * The above described 'burden' (which is specific to RTR) is resolved in
- * insert_vap() and aspa_insert_vaps() functions below.
- *
- * XXX: for bgpd(8), ASPA config injection (via /var/db/rpki-client/openbgpd)
- * we probably want to undo the 'burden solving' and compress into implicit
- * AFIs.
+ * Insert a new aspa_provider at index idx in the struct vap v.
+ * All elements in the provider array from idx are moved up by one
+ * to make space for the new element.
  */
-
-/*
- * If the CustomerASID (CAS) showed up before, append the ProviderAS (PAS);
- * otherwise create a new entry in the RB tree.
- * Ensure there are no duplicates in the 'providers' array.
- * Always compare 'expires': use the soonest expiration moment.
- */
-static int
-insert_vap(struct vap_tree *tree, uint32_t cas, uint32_t pas, time_t expires,
-    enum afi afi, struct repo *rp)
+static void
+insert_vap(struct vap *v, uint32_t idx, struct aspa_provider *p)
 {
-       struct vap      *v, *found;
-       size_t           i;
-
-       if ((v = malloc(sizeof(*v))) == NULL)
-               err(1, NULL);
-       v->afi = afi;
-       v->custasid = cas;
-       v->expires = expires;
-
-       if ((found = RB_INSERT(vap_tree, tree, v)) == NULL) {
-               if ((v->providers = malloc(sizeof(uint32_t))) == NULL)
-                       err(1, NULL);
-
-               v->providers[0] = pas;
-               v->providersz = 1;
-
-               repo_stat_inc(rp, RTYPE_ASPA, STYPE_UNIQUE);
-               return 1;
-       }
-
-       free(v);
-
-       if (found->expires > expires)
-               found->expires = expires;
-
-       for (i = 0; i < found->providersz; i++) {
-               if (found->providers[i] == pas)
-                       return 0;
-       }
-
-       found->providers = reallocarray(found->providers,
-           found->providersz + 1, sizeof(uint32_t));
-       if (found->providers == NULL)
-               err(1, NULL);
-       found->providers[found->providersz++] = pas;
-       return 1;
+       if (idx < v->providersz)
+               memmove(v->providers + idx + 1, v->providers + idx,
+                   (v->providersz - idx) * sizeof(*v->providers));
+       v->providers[idx] = *p;
+       v->providersz++;
 }
 
 /*
  * Add each ProviderAS entry into the Validated ASPA Providers (VAP) tree.
- * Updates "vaps" to be the total number of VAPs, and "uniqs" to be the
- * pre-'AFI explosion' deduplicated count.
+ * Duplicated entries are merged.
  */
 void
 aspa_insert_vaps(struct vap_tree *tree, struct aspa *aspa, struct repo *rp)
 {
-       size_t           i;
-       uint32_t         cas, pas;
-       time_t           expires;
-       int              new;
-
-       cas = aspa->custasid;
-       expires = aspa->expires;
+       struct vap      *v, *found;
+       size_t           i, j;
 
        repo_stat_inc(rp, RTYPE_ASPA, STYPE_TOTAL);
 
-       for (i = 0; i < aspa->providersz; i++) {
-               pas = aspa->providers[i].as;
-
-               switch (aspa->providers[i].afi) {
-               case AFI_IPV4:
-                       if (insert_vap(tree, cas, pas, expires, AFI_IPV4, rp))
-                               repo_stat_inc(rp, RTYPE_ASPA, STYPE_ONLY_IPV4);
-                       break;
-               case AFI_IPV6:
-                       if (insert_vap(tree, cas, pas, expires, AFI_IPV6, rp))
-                               repo_stat_inc(rp, RTYPE_ASPA, STYPE_ONLY_IPV6);
-                       break;
-               default:
-                       new = insert_vap(tree, cas, pas, expires, AFI_IPV4, rp);
-                       new += insert_vap(tree, cas, pas, expires, AFI_IPV6,
-                           rp);
-                       if (new != 0)
-                               repo_stat_inc(rp, RTYPE_ASPA, STYPE_BOTH);
-                       break;
+       if ((v = calloc(1, sizeof(*v))) == NULL)
+               err(1, NULL);
+       v->custasid = aspa->custasid;
+       v->expires = aspa->expires;
+
+       if ((found = RB_INSERT(vap_tree, tree, v)) != NULL) {
+               if (found->expires > v->expires)
+                       found->expires = v->expires;
+               free(v);
+               v = found;
+       } else
+               repo_stat_inc(rp, RTYPE_ASPA, STYPE_UNIQUE);
+
+       v->providers = reallocarray(v->providers,
+           v->providersz + aspa->providersz, sizeof(*v->providers));
+       if (v->providers == NULL)
+               err(1, NULL);
+
+       /*
+        * Merge all data from aspa into v: loop over all aspa providers,
+        * insert them in the right place in v->providers while keeping the
+        * order of the providers array.
+        */
+       for (i = 0, j = 0; i < aspa->providersz; ) {
+               if (j == v->providersz ||
+                   aspa->providers[i].as < v->providers[j].as) {
+                       /* merge provider from aspa into v */
+                       repo_stat_inc(rp, RTYPE_ASPA,
+                           STYPE_BOTH + aspa->providers[i].afi);
+                       insert_vap(v, j, &aspa->providers[i]);
+                       i++;
+               } else if (aspa->providers[i].as == v->providers[j].as) {
+                       /* duplicate provider, merge afi */
+                       if (v->providers[j].afi != aspa->providers[i].afi) {
+                               repo_stat_inc(rp, RTYPE_ASPA,
+                                   STYPE_BOTH + aspa->providers[i].afi);
+                               v->providers[j].afi = 0;
+                       }
+                       i++;
                }
+               if (j < v->providersz)
+                       j++;
        }
 }
 
 static inline int
 vapcmp(struct vap *a, struct vap *b)
 {
-       if (a->afi > b->afi)
-               return 1;
-       if (a->afi < b->afi)
-               return -1;
-
        if (a->custasid > b->custasid)
                return 1;
        if (a->custasid < b->custasid)
index ed76f35..9470d77 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: extern.h,v 1.166 2023/01/04 14:22:43 claudio Exp $ */
+/*     $OpenBSD: extern.h,v 1.167 2023/01/13 08:58:36 claudio Exp $ */
 /*
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
  *
@@ -356,9 +356,8 @@ struct aspa {
  */
 struct vap {
        RB_ENTRY(vap)            entry;
-       enum afi                 afi;
        uint32_t                 custasid;
-       uint32_t                *providers;
+       struct aspa_provider    *providers;
        size_t                   providersz;
        time_t                   expires;
 };
@@ -503,9 +502,9 @@ enum stype {
        STYPE_TOTAL,
        STYPE_UNIQUE,
        STYPE_DEC_UNIQUE,
+       STYPE_BOTH,
        STYPE_ONLY_IPV4,
        STYPE_ONLY_IPV6,
-       STYPE_BOTH,
 };
 
 struct repo;
@@ -568,6 +567,7 @@ struct msgbuf;
 /* global variables */
 extern int verbose;
 extern int filemode;
+extern int excludeaspa;
 extern const char *tals[];
 extern const char *taldescs[];
 extern unsigned int talrepocnt[];
index 2be19c9..7150511 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: main.c,v 1.230 2023/01/06 16:06:43 claudio Exp $ */
+/*     $OpenBSD: main.c,v 1.231 2023/01/13 08:58:36 claudio Exp $ */
 /*
  * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -67,6 +67,7 @@ const char    *bird_tablename = "ROAS";
 
 int    verbose;
 int    noop;
+int    excludeaspa;
 int    filemode;
 int    shortlistmode;
 int    rrdpon = 1;
@@ -942,8 +943,11 @@ main(int argc, char *argv[])
            "proc exec unveil", NULL) == -1)
                err(1, "pledge");
 
-       while ((c = getopt(argc, argv, "b:Bcd:e:fH:jmnorRs:S:t:T:vV")) != -1)
+       while ((c = getopt(argc, argv, "Ab:Bcd:e:fH:jmnorRs:S:t:T:vV")) != -1)
                switch (c) {
+               case 'A':
+                       excludeaspa = 1;
+                       break;
                case 'b':
                        bind_addr = optarg;
                        break;
@@ -1423,7 +1427,7 @@ main(int argc, char *argv[])
 
 usage:
        fprintf(stderr,
-           "usage: rpki-client [-BcjmnoRrVv] [-b sourceaddr] [-d cachedir]"
+           "usage: rpki-client [-ABcjmnoRrVv] [-b sourceaddr] [-d cachedir]"
            " [-e rsync_prog]\n"
            "                   [-H fqdn] [-S skiplist] [-s timeout] [-T table]"
            " [-t tal]\n"
index 54c00e5..98c2d71 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: output-bgpd.c,v 1.24 2022/08/30 18:56:49 job Exp $ */
+/*     $OpenBSD: output-bgpd.c,v 1.25 2023/01/13 08:58:36 claudio Exp $ */
 /*
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
  *
@@ -23,7 +23,9 @@ int
 output_bgpd(FILE *out, struct vrp_tree *vrps, struct brk_tree *brks,
     struct vap_tree *vaps, struct stats *st)
 {
-       struct vrp      *v;
+       struct vrp      *vrp;
+       struct vap      *vap;
+       size_t           i;
 
        if (outputheader(out, st) < 0)
                return -1;
@@ -31,23 +33,58 @@ output_bgpd(FILE *out, struct vrp_tree *vrps, struct brk_tree *brks,
        if (fprintf(out, "roa-set {\n") < 0)
                return -1;
 
-       RB_FOREACH(v, vrp_tree, vrps) {
+       RB_FOREACH(vrp, vrp_tree, vrps) {
                char ipbuf[64], maxlenbuf[100];
 
-               ip_addr_print(&v->addr, v->afi, ipbuf, sizeof(ipbuf));
-               if (v->maxlength > v->addr.prefixlen) {
+               ip_addr_print(&vrp->addr, vrp->afi, ipbuf, sizeof(ipbuf));
+               if (vrp->maxlength > vrp->addr.prefixlen) {
                        int ret = snprintf(maxlenbuf, sizeof(maxlenbuf),
-                           "maxlen %u ", v->maxlength);
+                           "maxlen %u ", vrp->maxlength);
                        if (ret < 0 || (size_t)ret > sizeof(maxlenbuf))
                                return -1;
                } else
                        maxlenbuf[0] = '\0';
                if (fprintf(out, "\t%s %ssource-as %u expires %lld\n",
-                   ipbuf, maxlenbuf, v->asid, (long long)v->expires) < 0)
+                   ipbuf, maxlenbuf, vrp->asid, (long long)vrp->expires) < 0)
                        return -1;
        }
 
        if (fprintf(out, "}\n") < 0)
                return -1;
+
+       if (excludeaspa)
+               return 0;
+
+       if (fprintf(out, "\naspa-set {\n") < 0)
+               return -1;
+       RB_FOREACH(vap, vap_tree, vaps) {
+               if (fprintf(out, "\tcustomer-as %d expires %lld "
+                   "provider-as { ", vap->custasid,
+                   (long long)vap->expires) < 0)
+                       return -1;
+               for (i = 0; i < vap->providersz; i++) {
+                       if (fprintf(out, "%u", vap->providers[i].as) < 0)
+                               return -1;
+                       switch (vap->providers[i].afi) {
+                       case AFI_IPV4:
+                               if (fprintf(out, "allow inet") < 0)
+                                       return -1;
+                               break;
+                       case AFI_IPV6:
+                               if (fprintf(out, "allow inet6") < 0)
+                                       return -1;
+                               break;
+                       }
+                       if (i + 1 < vap->providersz)
+                               if (fprintf(out, ", ") < 0)
+                                       return -1;
+               }
+
+               if (fprintf(out, " }\n") < 0)
+                       return -1;
+       }
+       if (fprintf(out, "}\n") < 0)
+               return -1;
+
        return 0;
 }
index 4945af1..95bb5b1 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: output-json.c,v 1.30 2022/12/15 12:02:29 claudio Exp $ */
+/*     $OpenBSD: output-json.c,v 1.31 2023/01/13 08:58:36 claudio Exp $ */
 /*
  * Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org>
  *
@@ -112,7 +112,7 @@ outputheader_json(FILE *out, struct stats *st)
 }
 
 static int
-print_vap(FILE *out, struct vap *v)
+print_vap(FILE *out, struct vap *v, enum afi afi)
 {
        size_t i;
 
@@ -120,7 +120,9 @@ print_vap(FILE *out, struct vap *v)
            v->custasid) < 0)
                return -1;
        for (i = 0; i < v->providersz; i++) {
-               if (fprintf(out, "%u", v->providers[i]) < 0)
+               if (v->providers[i].afi != 0 && v->providers[i].afi != afi)
+                       continue;
+               if (fprintf(out, "%u", v->providers[i].as) < 0)
                        return -1;
                if (i + 1 < v->providersz)
                        if (fprintf(out, ", ") < 0)
@@ -143,31 +145,28 @@ output_aspa(FILE *out, struct vap_tree *vaps)
                return -1;
 
        first = 1;
-       RB_FOREACH(v, vap_tree, vaps)
-               if (v->afi == AFI_IPV4) {
-                       if (!first) {
-                               if (fprintf(out, ",\n") < 0)
-                                       return -1;
-                       }
-                       first = 0;
-                       if (print_vap(out, v))
+       RB_FOREACH(v, vap_tree, vaps) {
+               if (!first) {
+                       if (fprintf(out, ",\n") < 0)
                                return -1;
                }
+               first = 0;
+               if (print_vap(out, v, AFI_IPV4))
+                       return -1;
+       }
 
        if (fprintf(out, "\n\t\t],\n\t\t\"ipv6\": [\n") < 0)
                return -1;
 
        first = 1;
        RB_FOREACH(v, vap_tree, vaps) {
-               if (v->afi == AFI_IPV6) {
-                       if (!first) {
-                               if (fprintf(out, ",\n") < 0)
-                                       return -1;
-                       }
-                       first = 0;
-                       if (print_vap(out, v))
+               if (!first) {
+                       if (fprintf(out, ",\n") < 0)
                                return -1;
                }
+               first = 0;
+               if (print_vap(out, v, AFI_IPV6))
+                       return -1;
        }
 
        if (fprintf(out, "\n\t\t]\n\t}\n") < 0)
index 4c8fdd8..e5431a9 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: rpki-client.8,v 1.82 2022/12/15 12:02:29 claudio Exp $
+.\"    $OpenBSD: rpki-client.8,v 1.83 2023/01/13 08:58:36 claudio Exp $
 .\"
 .\" Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
 .\"
@@ -14,7 +14,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: December 15 2022 $
+.Dd $Mdocdate: January 13 2023 $
 .Dt RPKI-CLIENT 8
 .Os
 .Sh NAME
@@ -22,7 +22,7 @@
 .Nd RPKI validator to support BGP routing security
 .Sh SYNOPSIS
 .Nm
-.Op Fl BcjmnoRrVv
+.Op Fl ABcjmnoRrVv
 .Op Fl b Ar sourceaddr
 .Op Fl d Ar cachedir
 .Op Fl e Ar rsync_prog
@@ -62,6 +62,8 @@ in various formats.
 .Pp
 The options are as follows:
 .Bl -tag -width Ds
+.It Fl A
+Exclude the aspa-set in the OpenBGPD specific output file.
 .It Fl B
 Create output in the files
 .Pa bird1v4 ,