From 7a90a58c8c3d307ef2a75617eac06fd754060607 Mon Sep 17 00:00:00 2001 From: Jared Mauch Date: Wed, 17 Dec 2025 12:16:43 -0500 Subject: [PATCH] Implement RFC 9234 OTC (Only to Customer) attribute parsing - Add BGP_ATTR_OTC constant (type code 35) - Add otc_as field to struct attr - Parse OTC attribute with 4-octet length validation - Display OTC in verbose and compact output modes - Use 0 as sentinel value (instead of -1) for unsigned ASN type --- bgpdump.c | 21 ++++++++++++++++++--- bgpdump_attr.h | 4 ++++ bgpdump_lib.c | 12 ++++++++++++ 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/bgpdump.c b/bgpdump.c index d0f3233..457b077 100644 --- a/bgpdump.c +++ b/bgpdump.c @@ -1183,9 +1183,10 @@ void mrtd_table_line_announce(struct prefix *prefix, int count, BGPDUMP_ENTRY *e printf("|%s|", aggregate); if (entry->attr->aggregator_addr.s_addr != -1) - printf("%u %s|\n", entry->attr->aggregator_as, inet_ntoa(entry->attr->aggregator_addr)); - else - printf("|\n"); + printf("%u %s", entry->attr->aggregator_as, inet_ntoa(entry->attr->aggregator_addr)); + printf("|"); + + printf("%u|\n", entry->attr->otc_as); } else printf("\n"); @@ -1408,6 +1409,10 @@ void show_attr(attributes_t *attr) { if( (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES) ) !=0) printf("LARGE_COMMUNITY:%s\n",attr->lcommunity->str); + + /* RFC 9234: OTC attribute (type 35) - check value directly since flag is 32-bit */ + if(attr->otc_as != 0) + printf("OTC: AS%u\n", attr->otc_as); } @@ -1588,6 +1593,8 @@ static void table_line_announce(struct prefix *prefix,int count,BGPDUMP_ENTRY *e } printf("|"); + printf("%u|", entry->attr->otc_as); + if (show_unknown_attributes) { append_compact_unknown_attributes(entry->attr); } @@ -1747,6 +1754,8 @@ static void table_line_announce_1(struct mp_nlri *prefix,int count,BGPDUMP_ENTRY } printf("|"); + printf("%u|", entry->attr->otc_as); + if (show_unknown_attributes) { append_compact_unknown_attributes(entry->attr); } @@ -1866,6 +1875,8 @@ static void table_line_announce6(struct mp_nlri *prefix,int count,BGPDUMP_ENTRY } printf("|"); + printf("%u|", entry->attr->otc_as); + if (show_unknown_attributes) { append_compact_unknown_attributes(entry->attr); } @@ -1988,6 +1999,8 @@ static void table_line_mrtd_route(BGPDUMP_MRTD_TABLE_DUMP *route,BGPDUMP_ENTRY * } printf("|"); + printf("%u|", entry->attr->otc_as); + if (show_unknown_attributes) { append_compact_unknown_attributes(entry->attr); } @@ -2122,6 +2135,8 @@ static void table_line_dump_v2_prefix(BGPDUMP_TABLE_DUMP_V2_PREFIX *e,BGPDUMP_EN } printf("|"); + printf("%u|", attr->otc_as); + if (show_unknown_attributes) { append_compact_unknown_attributes(attr); } diff --git a/bgpdump_attr.h b/bgpdump_attr.h index 1cffeb0..3246cde 100644 --- a/bgpdump_attr.h +++ b/bgpdump_attr.h @@ -57,6 +57,7 @@ Original Author: Dan Ardelean (dan@ripe.net) #define BGP_ATTR_NEW_AS_PATH 17 #define BGP_ATTR_NEW_AGGREGATOR 18 #define BGP_ATTR_LARGE_COMMUNITIES 32 +#define BGP_ATTR_OTC 35 /* Flag macro */ #define ATTR_FLAG_BIT(X) (1 << ((X) - 1)) @@ -149,6 +150,9 @@ struct attr as_t old_aggregator_as; struct in_addr new_aggregator_addr; struct in_addr old_aggregator_addr; + + /* RFC 9234: Only to Customer (OTC) attribute */ + as_t otc_as; }; struct community diff --git a/bgpdump_lib.c b/bgpdump_lib.c index b26b01d..b67dc01 100644 --- a/bgpdump_lib.c +++ b/bgpdump_lib.c @@ -1063,6 +1063,7 @@ static attributes_t *attr_init(struct mstream *s, int len) { attr->old_aspath = NULL; attr->new_aggregator_as = -1; attr->new_aggregator_addr.s_addr = INADDR_NONE; + attr->otc_as = 0; return attr; } @@ -1216,6 +1217,17 @@ static void process_one_attr(struct mstream *outer_stream, attributes_t *attr, u for (i = 0; i < attr->cluster->length; i++) attr->cluster->list[i] = mstream_get_ipv4(s); break; + case BGP_ATTR_OTC: + /* RFC 9234: Only to Customer (OTC) attribute + * Type Code: 35, Length: 4 octets, Value: ASN */ + if(len != 4) { + warn("process_one_attr: OTC attribute has invalid length %d (expected 4)", len); + process_unknown_attr(s, attr, flag, type, len); + break; + } + assert(0 == attr->otc_as); + attr->otc_as = read_asn(s, ASN32_LEN); + break; default: process_unknown_attr(s, attr, flag, type, len); }