Increase RTR PDU limit to 48k and limit number of SPAS to 10'000.
authorclaudio <claudio@openbsd.org>
Tue, 9 Apr 2024 12:09:19 +0000 (12:09 +0000)
committerclaudio <claudio@openbsd.org>
Tue, 9 Apr 2024 12:09:19 +0000 (12:09 +0000)
PDU larger then 48k will result in a session reset while ASPA records
with more than 10'000 entries will be implicitly withdrawn.

Also truncate RTR error PDUs to only include 256 bytes of the faulty PDU.
It makes no sense to include more to identify the issue.
OK tb@

usr.sbin/bgpd/bgpd.h
usr.sbin/bgpd/rtr_proto.c

index 8e791d0..6cf22dc 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: bgpd.h,v 1.490 2024/04/09 09:03:18 claudio Exp $ */
+/*     $OpenBSD: bgpd.h,v 1.491 2024/04/09 12:09:19 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -53,6 +53,7 @@
 #define        RT_BUF_SIZE                     16384
 #define        MAX_RTSOCK_BUF                  (2 * 1024 * 1024)
 #define        MAX_COMM_MATCH                  3
+#define        MAX_ASPA_SPAS_COUNT             10000
 
 #define        BGPD_OPT_VERBOSE                0x0001
 #define        BGPD_OPT_VERBOSE2               0x0002
index 7ceda79..ddb3ba4 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rtr_proto.c,v 1.34 2024/03/22 15:41:34 claudio Exp $ */
+/*     $OpenBSD: rtr_proto.c,v 1.35 2024/04/09 12:09:20 claudio Exp $ */
 
 /*
  * Copyright (c) 2020 Claudio Jeker <claudio@openbsd.org>
@@ -36,7 +36,8 @@ struct rtr_header {
 } __packed;
 
 #define RTR_MAX_VERSION                2
-#define RTR_MAX_LEN            2048
+#define RTR_MAX_PDU_SIZE       49152   /* XXX < IBUF_READ_SIZE */
+#define RTR_MAX_PDU_ERROR_SIZE 256
 #define RTR_DEFAULT_REFRESH    3600
 #define RTR_DEFAULT_RETRY      600
 #define RTR_DEFAULT_EXPIRE     7200
@@ -272,7 +273,7 @@ rtr_newmsg(struct rtr_session *rs, enum rtr_pdu_type type, uint32_t len,
        struct ibuf *buf;
        int saved_errno;
 
-       if (len > RTR_MAX_LEN) {
+       if (len > RTR_MAX_PDU_SIZE) {
                errno = ERANGE;
                return NULL;
        }
@@ -328,6 +329,11 @@ rtr_send_error(struct rtr_session *rs, struct ibuf *pdu, enum rtr_error err,
        if (pdu != NULL) {
                ibuf_rewind(pdu);
                len = ibuf_size(pdu);
+               if (len > RTR_MAX_PDU_ERROR_SIZE) {
+                       len = RTR_MAX_PDU_ERROR_SIZE;
+                       /* truncate down can not fail */
+                       ibuf_truncate(pdu, RTR_MAX_PDU_ERROR_SIZE);
+               }
        }
 
        buf = rtr_newmsg(rs, ERROR_REPORT, 2 * sizeof(uint32_t) + len + mlen,
@@ -426,7 +432,7 @@ rtr_parse_header(struct rtr_session *rs, struct ibuf *hdr,
 
        len = ntohl(rh.length);
 
-       if (len > RTR_MAX_LEN) {
+       if (len > RTR_MAX_PDU_SIZE) {
                rtr_send_error(rs, hdr, CORRUPT_DATA, "%s: too big: %zu bytes",
                    log_rtr_type(rh.type), len);
                return -1;
@@ -754,6 +760,22 @@ rtr_parse_aspa(struct rtr_session *rs, struct ibuf *pdu)
                aspatree = &rs->aspa;
        }
 
+       /* treat ASPA records with too many SPAS like a withdraw */
+       if (cnt > MAX_ASPA_SPAS_COUNT) {
+               struct aspa_set needle = { 0 };
+               needle.as = ntohl(rtr_aspa.cas);
+
+               log_warnx("rtr %s: oversized ASPA PDU: "
+                   "imlicit withdraw of customerAS %s",
+                   log_rtr(rs), log_as(needle.as));
+               a = RB_FIND(aspa_tree, aspatree, &needle);
+               if (a != NULL) {
+                       RB_REMOVE(aspa_tree, aspatree, a);
+                       free_aspa(a);
+               }
+               return 0;
+       }
+
        /* create aspa_set entry from the rtr aspa pdu */
        if ((aspa = calloc(1, sizeof(*aspa))) == NULL) {
                rtr_send_error(rs, NULL, INTERNAL_ERROR, "out of memory");