From 224fbe59802ea45820ce49fd1f59b91314b17e7e Mon Sep 17 00:00:00 2001 From: Katerina Kubecova Date: Wed, 8 Nov 2023 13:46:42 +0100 Subject: [PATCH 1/6] ASPA: Automatic channel reload --- nest/proto.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/nest/proto.c b/nest/proto.c index a6c5a0c1..36c5b576 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -422,6 +422,11 @@ channel_roa_subscribe_filter(struct channel *c, int dir) found = 1; break; + case FI_ASPA_CHECK_EXPLICIT: + tab = fi->i_FI_ASPA_CHECK_EXPLICIT.rtc->table; + if (valid) channel_roa_subscribe(c, tab, dir); + found = 1; + break; default: break; } From fa65e063721d963a29749ad63e47c97b52ce98d8 Mon Sep 17 00:00:00 2001 From: Katerina Kubecova Date: Fri, 8 Nov 2024 15:26:37 +0100 Subject: [PATCH 2/6] RPKI: protocol version 2, loading ASPA Implemented draft-ietf-sidrops-8210bis-16, interoperable with StayRTR development branches. --- proto/rpki/packets.c | 113 +++++++++++++++++++++++++++++++++++++++++-- proto/rpki/packets.h | 6 ++- proto/rpki/rpki.c | 42 +++++++++++++++- proto/rpki/rpki.h | 7 ++- 4 files changed, 158 insertions(+), 10 deletions(-) diff --git a/proto/rpki/packets.c b/proto/rpki/packets.c index d246dd50..de9e8619 100644 --- a/proto/rpki/packets.c +++ b/proto/rpki/packets.c @@ -62,6 +62,7 @@ enum pdu_type { CACHE_RESET = 8, ROUTER_KEY = 9, ERROR = 10, + ASPA = 11, PDU_TYPE_MAX }; @@ -76,7 +77,8 @@ static const char *str_pdu_type_[] = { [END_OF_DATA] = "End of Data", [CACHE_RESET] = "Cache Reset", [ROUTER_KEY] = "Router Key", - [ERROR] = "Error" + [ERROR] = "Error", + [ASPA] = "ASPA", }; static const char *str_pdu_type(uint type) { @@ -193,6 +195,35 @@ struct pdu_error { * Error Diagnostic Message */ } PACKED; +/* + *0 8 16 24 31 + * .-------------------------------------------. + * | Protocol | PDU | | | + * | Version | Type | Flags | zero | + * | 2 | 11 | | | + * +-------------------------------------------+ + * | | + * | Length | + * | | + * +-------------------------------------------+ + * | | + * | Customer Autonomous System Number | + * | | + * +-------------------------------------------+ + * | | + * ~ Provider Autonomous System Numbers ~ + * | | + * ~-------------------------------------------~ */ +struct pdu_aspa { + u8 ver; + u8 type; + u8 flags; + u8 zero; + u32 len; + u32 customer_as_num; + u32 provider_as_nums[0]; +} PACKED; + struct pdu_reset_query { u8 ver; u8 type; @@ -230,9 +261,13 @@ static const size_t min_pdu_size[] = { [END_OF_DATA] = sizeof(struct pdu_end_of_data_v0), [CACHE_RESET] = sizeof(struct pdu_cache_response), [ROUTER_KEY] = sizeof(struct pdu_header), /* FIXME */ + [ASPA] = sizeof(struct pdu_aspa), [ERROR] = 16, }; +static inline int rpki_pdu_aspa_provider_asn_count(const struct pdu_aspa *pdu) +{ return (pdu->len - sizeof(struct pdu_aspa)) / (sizeof(u32)); } + static int rpki_send_error_pdu(struct rpki_cache *cache, const enum pdu_error_type error_code, const u32 err_pdu_len, const struct pdu_header *erroneous_pdu, const char *fmt, ...); static void @@ -293,7 +328,7 @@ rpki_pdu_to_host_byte_order(struct pdu_header *pdu) struct pdu_end_of_data_v0 *eod0 = (void *) pdu; eod0->serial_num = ntohl(eod0->serial_num); /* Same either for version 1 */ - if (pdu->ver == RPKI_VERSION_1) + if (pdu->ver > RPKI_VERSION_0) { struct pdu_end_of_data_v1 *eod1 = (void *) pdu; eod1->expire_interval = ntohl(eod1->expire_interval); @@ -329,6 +364,21 @@ rpki_pdu_to_host_byte_order(struct pdu_header *pdu) break; } + case ASPA: + { + struct pdu_aspa *aspa = (void *) pdu; + int provider_asn_count = rpki_pdu_aspa_provider_asn_count(aspa); + + /* Convert customer ASN */ + aspa->customer_as_num = ntohl(aspa->customer_as_num); + + /* Convert provider ASNs */ + for (int i = 0; i < provider_asn_count ; i++) + aspa->provider_as_nums[i] = ntohl(aspa->provider_as_nums[i]); + + break; + } + case ROUTER_KEY: /* Router Key PDU is not supported yet */ @@ -354,6 +404,9 @@ rpki_pdu_to_host_byte_order(struct pdu_header *pdu) static struct pdu_header * rpki_pdu_back_to_network_byte_order(struct pdu_header *out, const struct pdu_header *in) { + /* Only valid for fixed-length PDUs */ + ASSERT_DIE(in->type != ERROR && in->type != ASPA); + memcpy(out, in, in->len); rpki_pdu_to_host_byte_order(out); return out; @@ -387,7 +440,7 @@ rpki_log_packet(struct rpki_cache *cache, const struct pdu_header *pdu, const en case END_OF_DATA: { const struct pdu_end_of_data_v1 *eod = (void *) pdu; - if (eod->ver == RPKI_VERSION_1) + if (eod->ver > RPKI_VERSION_0) SAVE(bsnprintf(detail, sizeof(detail), "(session id: %u, serial number: %u, refresh: %us, retry: %us, expire: %us)", eod->session_id, eod->serial_num, eod->refresh_interval, eod->retry_interval, eod->expire_interval)); else SAVE(bsnprintf(detail, sizeof(detail), "(session id: %u, serial number: %u)", eod->session_id, eod->serial_num)); @@ -455,6 +508,25 @@ rpki_log_packet(struct rpki_cache *cache, const struct pdu_header *pdu, const en break; } + case ASPA: + { + const struct pdu_aspa *aspa = (void *) pdu; + int provider_asn_count = rpki_pdu_aspa_provider_asn_count(aspa); + + if (provider_asn_count <= 0) + SAVE(bsnprintf(detail + strlen(detail), sizeof(detail) - strlen(detail), + "%u transit", aspa->customer_as_num)); + else + { + SAVE(bsnprintf(detail + strlen(detail), sizeof(detail) - strlen(detail), + "%u (providers", aspa->customer_as_num)); + for (int i = 0; i < provider_asn_count; i++) + SAVE(bsnprintf(detail + strlen(detail), sizeof(detail) - strlen(detail), + " %u%c", aspa->provider_as_nums[i], (i == provider_asn_count-1) ? ')' : ',')); + } + break; + } + default: *detail = '\0'; } @@ -571,7 +643,9 @@ rpki_check_receive_packet(struct rpki_cache *cache, const struct pdu_header *pdu } } - if ((pdu->type >= PDU_TYPE_MAX) || (pdu->ver == RPKI_VERSION_0 && pdu->type == ROUTER_KEY)) + if ((pdu->type >= PDU_TYPE_MAX) || + (pdu->ver < RPKI_VERSION_1 && pdu->type == ROUTER_KEY) || + (pdu->ver < RPKI_VERSION_2 && pdu->type == ASPA)) { rpki_send_error_pdu(cache, UNSUPPORTED_PDU_TYPE, pdu_len, pdu, "Unsupported PDU type %u received", pdu->type); return RPKI_ERROR; @@ -797,6 +871,29 @@ rpki_handle_prefix_pdu(struct rpki_cache *cache, const struct pdu_header *pdu) return RPKI_SUCCESS; } +static int +rpki_handle_aspa_pdu(struct rpki_cache *cache, const struct pdu_header *pdu) +{ + struct pdu_aspa *aspa = (void *) pdu; + struct channel *channel = cache->p->aspa_channel; + uint providers_length = aspa->len - sizeof(struct pdu_aspa); + + if (!channel) + { + CACHE_TRACE(D_ROUTES, cache, "Skip AS%u, missing aspa channel", aspa->customer_as_num); + return RPKI_ERROR; + } + + cache->last_rx_prefix = current_time(); + + if (aspa->flags & RPKI_ADD_FLAG) + rpki_table_add_aspa(cache, channel, aspa->customer_as_num, aspa->provider_as_nums, providers_length); + else + rpki_table_remove_aspa(cache, channel, aspa->customer_as_num); + + return RPKI_SUCCESS; +} + static uint rpki_check_interval(struct rpki_cache *cache, const char *(check_fn)(uint), uint interval) { @@ -822,7 +919,7 @@ rpki_handle_end_of_data_pdu(struct rpki_cache *cache, const struct pdu_end_of_da return; } - if (pdu->ver == RPKI_VERSION_1) + if (pdu->ver > RPKI_VERSION_0) { if (!cf->keep_refresh_interval && rpki_check_interval(cache, rpki_check_refresh_interval, pdu->refresh_interval)) cache->refresh_interval = pdu->refresh_interval; @@ -849,6 +946,8 @@ rpki_handle_end_of_data_pdu(struct rpki_cache *cache, const struct pdu_end_of_da rt_refresh_end(cache->p->roa4_channel->table, cache->p->roa4_channel); if (cache->p->roa6_channel) rt_refresh_end(cache->p->roa6_channel->table, cache->p->roa6_channel); + if (cache->p->aspa_channel) + rt_refresh_end(cache->p->aspa_channel->table, cache->p->aspa_channel); } cache->last_update = current_time(); @@ -913,6 +1012,10 @@ rpki_rx_packet(struct rpki_cache *cache, struct pdu_header *pdu) /* TODO: Implement Router Key PDU handling */ break; + case ASPA: + rpki_handle_aspa_pdu(cache, (void *) pdu); + break; + default: CACHE_TRACE(D_PACKETS, cache, "Received unsupported type (%u)", pdu->type); }; diff --git a/proto/rpki/packets.h b/proto/rpki/packets.h index d6f8a249..f7897d9a 100644 --- a/proto/rpki/packets.h +++ b/proto/rpki/packets.h @@ -23,8 +23,10 @@ * +4 bytes (Length of inserted text) * +800 bytes (UTF-8 text 400*2 bytes) * ------------ - * = 848 bytes (Maximal expected PDU size) */ -#define RPKI_PDU_MAX_LEN 848 + * = 848 bytes (Maximal expected PDU size) + * + * Received ASPA PDU can have any size, so let's start with 4k */ +#define RPKI_PDU_MAX_LEN 4096 /* RX buffer size has a great impact to scheduler granularity */ #define RPKI_RX_BUFFER_SIZE 4096 diff --git a/proto/rpki/rpki.c b/proto/rpki/rpki.c index cbd94492..e9d2a60a 100644 --- a/proto/rpki/rpki.c +++ b/proto/rpki/rpki.c @@ -140,6 +140,37 @@ rpki_table_remove_roa(struct rpki_cache *cache, struct channel *channel, const n rte_update2(channel, &pfxr->n, NULL, p->p.main_source); } +void +rpki_table_add_aspa(struct rpki_cache *cache, struct channel *channel, + u32 customer, void *providers, uint providers_length) +{ + struct rpki_proto *p = cache->p; + + net_addr_union n = { .aspa = NET_ADDR_ASPA(customer) }; + rta a0 = { + .pref = channel->preference, + .source = RTS_RPKI, + .scope = SCOPE_UNIVERSE, + .dest = RTD_NONE, + }; + + ea_set_attr_data(&a0.eattrs, tmp_linpool, EA_ASPA_PROVIDERS, 0, + EAF_TYPE_INT_SET, providers, providers_length); + + rta *a = rta_lookup(&a0); + rte *e = rte_get_temp(a, p->p.main_source); + + rte_update2(channel, &n.n, e, e->src); +} + +void +rpki_table_remove_aspa(struct rpki_cache *cache, struct channel *channel, u32 customer) +{ + struct rpki_proto *p = cache->p; + net_addr_union n = { .aspa = NET_ADDR_ASPA(customer) }; + rte_update2(channel, &n.n, NULL, p->p.main_source); +} + /* * RPKI Protocol Logic @@ -773,7 +804,8 @@ rpki_reconfigure(struct proto *P, struct proto_config *CF) struct rpki_cache *cache = p->cache; if (!proto_configure_channel(&p->p, &p->roa4_channel, proto_cf_find_channel(CF, NET_ROA4)) || - !proto_configure_channel(&p->p, &p->roa6_channel, proto_cf_find_channel(CF, NET_ROA6))) + !proto_configure_channel(&p->p, &p->roa6_channel, proto_cf_find_channel(CF, NET_ROA6)) || + !proto_configure_channel(&p->p, &p->aspa_channel, proto_cf_find_channel(CF, NET_ASPA))) return NEED_RESTART; if (rpki_reconfigure_cache(p, cache, new, old) != SUCCESSFUL_RECONF) @@ -795,6 +827,7 @@ rpki_init(struct proto_config *CF) proto_configure_channel(&p->p, &p->roa4_channel, proto_cf_find_channel(CF, NET_ROA4)); proto_configure_channel(&p->p, &p->roa6_channel, proto_cf_find_channel(CF, NET_ROA6)); + proto_configure_channel(&p->p, &p->aspa_channel, proto_cf_find_channel(CF, NET_ASPA)); return P; } @@ -908,6 +941,11 @@ rpki_show_proto_info(struct proto *P) channel_show_info(p->roa6_channel); else cli_msg(-1006, " No roa6 channel"); + + if (p->aspa_channel) + channel_show_info(p->aspa_channel); + else + cli_msg(-1006, " No aspa channel"); } } @@ -979,7 +1017,7 @@ struct protocol proto_rpki = { .init = rpki_init, .start = rpki_start, .postconfig = rpki_postconfig, - .channel_mask = (NB_ROA4 | NB_ROA6), + .channel_mask = (NB_ROA4 | NB_ROA6 | NB_ASPA), .show_proto_info = rpki_show_proto_info, .shutdown = rpki_shutdown, .copy_config = rpki_copy_config, diff --git a/proto/rpki/rpki.h b/proto/rpki/rpki.h index e67eb0e3..307ec315 100644 --- a/proto/rpki/rpki.h +++ b/proto/rpki/rpki.h @@ -29,7 +29,8 @@ #define RPKI_VERSION_0 0 #define RPKI_VERSION_1 1 -#define RPKI_MAX_VERSION RPKI_VERSION_1 +#define RPKI_VERSION_2 2 +#define RPKI_MAX_VERSION RPKI_VERSION_2 /* @@ -83,6 +84,9 @@ const char *rpki_cache_state_to_str(enum rpki_cache_state state); void rpki_table_add_roa(struct rpki_cache *cache, struct channel *channel, const net_addr_union *pfxr); void rpki_table_remove_roa(struct rpki_cache *cache, struct channel *channel, const net_addr_union *pfxr); +void rpki_table_add_aspa(struct rpki_cache *cache, struct channel *channel, u32 customer, void *providers, uint providers_length); +void rpki_table_remove_aspa(struct rpki_cache *cache, struct channel *channel, u32 customer); + /* * RPKI Protocol Logic @@ -110,6 +114,7 @@ struct rpki_proto { struct channel *roa4_channel; struct channel *roa6_channel; + struct channel *aspa_channel; u8 refresh_channels; /* For non-incremental updates using rt_refresh_begin(), rt_refresh_end() */ }; From e330fb1614d83e3529f4405d0875160d425f8bb3 Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Mon, 25 Nov 2024 09:35:33 +0100 Subject: [PATCH 3/6] RPKI: added documentation and RTR version config options --- doc/bird.sgml | 19 ++++++++++++++++++- proto/rpki/config.Y | 14 ++++++++++++-- proto/rpki/packets.c | 7 +++++-- proto/rpki/rpki.c | 23 ++++++++++++++++++++++- proto/rpki/rpki.h | 3 +++ 5 files changed, 60 insertions(+), 6 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 0d1e6f49..ed45f605 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -5728,6 +5728,10 @@ affected routes after RPKI update, see option . Or you can use a BIRD client command reload in for manual call of revalidation of all routes. +

The same protocol, since version 2, also receives and maintains a set +of ASPAs. You can then validate AS paths using function Supported transports

@@ -5747,6 +5751,7 @@ define more RPKI protocols generally. protocol rpki [<name>] { roa4 { table <tab>; }; roa6 { table <tab>; }; + aspa { table <tab>; }; remote <ip> | "<domain>" [port <num>]; port <num>; local address <ip>; @@ -5762,10 +5767,12 @@ protocol rpki [<name>] { remote public key "</path/to/known_host>"; user "<name>"; }; + max version 2; + min version 2; } -

Alse note that you have to specify the ROA channel. If you want to import +

Alse note that you have to specify the ROA and ASPA channels. If you want to import only IPv4 prefixes you have to specify only roa4 channel. Similarly with IPv6 prefixes only. If you want to fetch both IPv4 and even IPv6 ROAs you have to specify both channels. @@ -5812,6 +5819,16 @@ specify both channels. instead. This may be useful for implementing loose RPKI check for blackholes. Default: disabled. + min version + Minimal allowed version of the RTR protocol. BIRD will refuse to + downgrade a connection below this version and drop the session instead. + Default: 0 + + max version + Maximal allowed version of the RTR protocol. BIRD will start with this + version. Use this option if sending version 2 to your cache causes + problems. Default: 2 + transport tcp { Transport over TCP, it's the default transport. Cannot be combined with a SSH transport. Default: TCP, no authentication. diff --git a/proto/rpki/config.Y b/proto/rpki/config.Y index 60a4b9f0..68e0ebfd 100644 --- a/proto/rpki/config.Y +++ b/proto/rpki/config.Y @@ -33,8 +33,8 @@ rpki_check_unused_transport(void) CF_DECLS CF_KEYWORDS(RPKI, REMOTE, BIRD, PRIVATE, PUBLIC, KEY, TCP, SSH, TRANSPORT, USER, - RETRY, REFRESH, EXPIRE, KEEP, IGNORE, MAX, LENGTH, LOCAL, ADDRESS, - AUTHENTICATION, NONE, MD5, PASSWORD) + RETRY, REFRESH, EXPIRE, KEEP, IGNORE, MAX, MIN, LENGTH, LOCAL, ADDRESS, + AUTHENTICATION, NONE, MD5, PASSWORD, VERSION) %type rpki_keep_interval @@ -47,6 +47,8 @@ rpki_proto_start: proto_start RPKI { RPKI_CFG->retry_interval = RPKI_RETRY_INTERVAL; RPKI_CFG->refresh_interval = RPKI_REFRESH_INTERVAL; RPKI_CFG->expire_interval = RPKI_EXPIRE_INTERVAL; + RPKI_CFG->min_version = 0; + RPKI_CFG->max_version = RPKI_MAX_VERSION; }; rpki_proto: rpki_proto_start proto_name '{' rpki_proto_opts '}' { rpki_check_config(RPKI_CFG); }; @@ -83,6 +85,14 @@ rpki_proto_item: RPKI_CFG->keep_expire_interval = $2; } | IGNORE MAX LENGTH bool { RPKI_CFG->ignore_max_length = $4; } + | MIN VERSION expr { + if ($3 > RPKI_MAX_VERSION) cf_error("RPKI version %u unsupported, min version must be in range 0-%u", $3, RPKI_MAX_VERSION); + RPKI_CFG->min_version = $3; + } + | MAX VERSION expr { + if ($3 > RPKI_MAX_VERSION) cf_error("RPKI version %u unsupported, max version must be in range 0-%u", $3, RPKI_MAX_VERSION); + RPKI_CFG->max_version = $3; + } ; rpki_keep_interval: diff --git a/proto/rpki/packets.c b/proto/rpki/packets.c index de9e8619..c26c46d1 100644 --- a/proto/rpki/packets.c +++ b/proto/rpki/packets.c @@ -628,7 +628,9 @@ rpki_check_receive_packet(struct rpki_cache *cache, const struct pdu_header *pdu } else if (!cache->last_update && (pdu->ver <= RPKI_MAX_VERSION) && - (pdu->ver < cache->version)) + (pdu->ver < cache->version) && + (pdu->ver >= cache->min_version) + ) { CACHE_TRACE(D_EVENTS, cache, "Downgrade session to %s from %u to %u version", rpki_get_cache_ident(cache), cache->version, pdu->ver); cache->version = pdu->ver; @@ -679,7 +681,8 @@ rpki_handle_error_pdu(struct rpki_cache *cache, const struct pdu_error *pdu) case UNSUPPORTED_PROTOCOL_VER: CACHE_TRACE(D_PACKETS, cache, "Client uses unsupported protocol version"); if (pdu->ver <= RPKI_MAX_VERSION && - pdu->ver < cache->version) + pdu->ver < cache->version && + pdu->ver >= cache->min_version) { CACHE_TRACE(D_EVENTS, cache, "Downgrading from protocol version %d to version %d", cache->version, pdu->ver); cache->version = pdu->ver; diff --git a/proto/rpki/rpki.c b/proto/rpki/rpki.c index e9d2a60a..327c6215 100644 --- a/proto/rpki/rpki.c +++ b/proto/rpki/rpki.c @@ -599,7 +599,8 @@ rpki_init_cache(struct rpki_proto *p, struct rpki_config *cf) cache->state = RPKI_CS_SHUTDOWN; cache->request_session_id = 1; - cache->version = RPKI_MAX_VERSION; + cache->version = cf->max_version; + cache->min_version = cf->min_version; cache->refresh_interval = cf->refresh_interval; cache->retry_interval = cf->retry_interval; @@ -704,6 +705,23 @@ rpki_reconfigure_cache(struct rpki_proto *p UNUSED, struct rpki_cache *cache, st return NEED_RESTART; } + if (new->min_version > cache->version) + { + CACHE_TRACE(D_EVENTS, cache, "Protocol min version %u higher than current version %u", + new->min_version, cache->version); + return NEED_RESTART; + } + else + cache->min_version = new->min_version; + + if (new->max_version < cache->version) + { + CACHE_TRACE(D_EVENTS, cache, "Protocol max version %u lower than current version %u", + new->max_version, cache->version); + cache->version = new->max_version; + try_reset = 1; + } + if (old->tr_config.type != new->tr_config.type) { CACHE_TRACE(D_EVENTS, cache, "Transport type changed"); @@ -963,6 +981,9 @@ rpki_show_proto_info(struct proto *P) void rpki_check_config(struct rpki_config *cf) { + if (cf->min_version > cf->max_version) + cf_error("Impossible min/max version for RPKI: %u/%u", cf->min_version, cf->max_version); + /* Do not check templates at all */ if (cf->c.class == SYM_TEMPLATE) return; diff --git a/proto/rpki/rpki.h b/proto/rpki/rpki.h index 307ec315..1f5f978e 100644 --- a/proto/rpki/rpki.h +++ b/proto/rpki/rpki.h @@ -61,6 +61,7 @@ struct rpki_cache { u8 request_session_id; /* 1: have to request new session id; 0: we have already received session id */ u32 serial_num; /* Serial number denotes the logical version of data from cache server */ u8 version; /* Protocol version */ + u8 min_version; /* Minimum allowed protocol version */ btime last_update; /* Last successful synchronization with cache server */ btime last_rx_prefix; /* Last received prefix PDU */ @@ -132,6 +133,8 @@ struct rpki_config { u8 keep_retry_interval:1; /* Do not overwrite retry interval by cache server update */ u8 keep_expire_interval:1; /* Do not overwrite expire interval by cache server update */ u8 ignore_max_length:1; /* Ignore received max length and use MAX_PREFIX_LENGTH instead */ + u8 min_version; /* Minimum version allowed */ + u8 max_version; /* Maximum version allowed (to start with) */ }; void rpki_check_config(struct rpki_config *cf); From fb919ac2a266aed53638ebc4f006c7ef48bb9e31 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 26 Nov 2024 17:46:27 +0100 Subject: [PATCH 4/6] RPKI: Fix PDU length check The END_OF_DATA PDU was extended in version 1, so it has different length in different versions. We should do the PDU length check according to its version. --- proto/rpki/packets.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/proto/rpki/packets.c b/proto/rpki/packets.c index c26c46d1..6d2ec9d1 100644 --- a/proto/rpki/packets.c +++ b/proto/rpki/packets.c @@ -653,9 +653,13 @@ rpki_check_receive_packet(struct rpki_cache *cache, const struct pdu_header *pdu return RPKI_ERROR; } - if (pdu_len < min_pdu_size[pdu->type]) + uint min_pdu_length = min_pdu_size[pdu->type]; + if (pdu->type == END_OF_DATA && pdu->ver >= RPKI_VERSION_1) + min_pdu_length = sizeof(struct pdu_end_of_data_v1); + + if (pdu_len < min_pdu_length) { - rpki_send_error_pdu(cache, CORRUPT_DATA, pdu_len, pdu, "Received %s packet with %d bytes, but expected at least %d bytes", str_pdu_type(pdu->type), pdu_len, min_pdu_size[pdu->type]); + rpki_send_error_pdu(cache, CORRUPT_DATA, pdu_len, pdu, "Received %s packet with %u bytes, but expected at least %u bytes", str_pdu_type(pdu->type), pdu_len, min_pdu_length); return RPKI_ERROR; } From 01377599da2f14567da46d8c0387a660189a8065 Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Tue, 26 Nov 2024 16:36:13 +0100 Subject: [PATCH 5/6] RPKI: Polishing of ASPA syntax and documentation --- doc/bird.sgml | 82 ++++++++++++++++++++++++++++++++++++---------- proto/bgp/config.Y | 14 ++++---- 2 files changed, 71 insertions(+), 25 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index ed45f605..4f48490b 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -766,7 +766,6 @@ to set options. triggered, after a short settle time. Minimum settle time is a delay from the last ROA table change to wait for more updates. Default: 1 s. -

There is one operator related to ROA infrastructure - route origin validation for a -given network prefix. The basic usage is roa_check(, which -checks the current route (which should be from BGP to have AS_PATH argument) in -the specified ROA table and returns ROA_UNKNOWN if there is no relevant ROA, -ROA_VALID if there is a matching ROA, or ROA_INVALID if there are some relevant -ROAs but none of them match. There is also an extended variant -roa_check(, which allows to specify a -prefix and an ASN as arguments. +

There are also operators related to RPKI infrastructure used to run + route origin validation and (draft) AS path validation. + + roa_check( checks the current route in the specified + ROA table and returns ROA_UNKNOWN, ROA_INVALID or ROA_VALID, + if the validation result is unknown, invalid, or valid, respectively. The result is + valid if there is a matching ROA, it is invalid if there is either matching ROA + with a different ASN, or any covering ROA with shorter maximal prefix length. + + roa_check( is an explicit version + of the ROA check if the user for whatever reason needs to check a different prefix + or different ASN than the default one. The equivalent call of the short variant + is roa_check( and it is faster + to call the short variant. + + aspa_check_downstream( checks the current route + in the specified ASPA table and returns ASPA_UNKNOWN, ASPA_INVALID, + or ASPA_VALID if the validation result is unknown, invalid, or valid, + respectively. The result is valid if there is a full coverage of matching + ASPA records according to the Algorithm for Downstream Paths by the (draft). + This operator is not present if BGP is not compiled in. + + aspa_check_upstream( checks the current route + in the specified ASPA table as the former operator, but it applies the + (stricter) Algorithm for Upstream Paths by the (draft). + This operator is not present if BGP is not compiled in. + + aspa_check( is + an explicit version of the former two ASPA check operators. The equivalent + of aspa_check_downstream is aspa_check( + and for aspa_check_upstream it is + aspa_check(. + Note: the ASPA check does not include the local ASN in the AS path. + + +

The following example checks for ROA and ASPA on routes from a customer: + + +roa6 table r6; +aspa table at; +attribute int valid_roa; +attribute int valid_aspa; + +filter customer_check { + case roa_check(r6) { + ROA_INVALID: reject "Invalid ROA"; + ROA_VALID: valid_roa = 1; + } + + case aspa_check_upstream(at) { + ASPA_INVALID: reject "Invalid ASPA"; + ASPA_VALID: valid_aspa = 1; + } + + accept; +} + Control structures

The following example checks for ROA and ASPA on routes from a customer: diff --git a/filter/test.conf b/filter/test.conf index 488e2b75..88123c51 100644 --- a/filter/test.conf +++ b/filter/test.conf @@ -2267,11 +2267,11 @@ function t_aspa_check() p1.prepend(65542); bt_assert(aspa_check(at, p1, false) = ASPA_VALID); - bt_assert(aspa_check(at, p1, true) = ASPA_INVALID_LEAK); + bt_assert(aspa_check(at, p1, true) = ASPA_INVALID); p1.prepend(65555); bt_assert(aspa_check(at, p1, false) = ASPA_UNKNOWN); - bt_assert(aspa_check(at, p1, true) = ASPA_INVALID_LEAK); + bt_assert(aspa_check(at, p1, true) = ASPA_INVALID); bgppath p2 = +empty+; p2.prepend(65554); @@ -2282,13 +2282,13 @@ function t_aspa_check() p2.prepend(65543); bt_assert(aspa_check(at, p2, false) = ASPA_UNKNOWN); - bt_assert(aspa_check(at, p2, true) = ASPA_INVALID_LEAK); + bt_assert(aspa_check(at, p2, true) = ASPA_INVALID); bgppath p3 = +empty+; p3.prepend(65541); p3.prepend(65544); - bt_assert(aspa_check(at, p3, false) = ASPA_INVALID_LEAK); - bt_assert(aspa_check(at, p3, true) = ASPA_INVALID_LEAK); + bt_assert(aspa_check(at, p3, false) = ASPA_INVALID); + bt_assert(aspa_check(at, p3, true) = ASPA_INVALID); } bt_test_suite(t_aspa_check, "Testing ASPA"); diff --git a/nest/config.Y b/nest/config.Y index 3fcf3068..5d755715 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -139,7 +139,7 @@ CF_ENUM(T_ENUM_RTS, RTS_, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT, CF_ENUM(T_ENUM_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINED) CF_ENUM(T_ENUM_RTD, RTD_, UNICAST, BLACKHOLE, UNREACHABLE, PROHIBIT) CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID) -CF_ENUM(T_ENUM_ASPA, ASPA_, UNKNOWN, VALID, INVALID_EMPTY, INVALID_LEAK, INVALID_CONFED) +CF_ENUM(T_ENUM_ASPA, ASPA_, UNKNOWN, VALID, INVALID) CF_ENUM_PX(T_ENUM_AF, AF_, AFI_, IPV4, IPV6) CF_ENUM(T_ENUM_MPLS_POLICY, MPLS_POLICY_, NONE, STATIC, PREFIX, AGGREGATE, VRF) diff --git a/nest/route.h b/nest/route.h index c74c410f..81efebeb 100644 --- a/nest/route.h +++ b/nest/route.h @@ -786,9 +786,7 @@ int rt_flowspec_check(rtable *tab_ip, rtable *tab_flow, const net_addr *n, rta * enum aspa_result { ASPA_UNKNOWN = 0, ASPA_VALID, - ASPA_INVALID_EMPTY, - ASPA_INVALID_CONFED, - ASPA_INVALID_LEAK, + ASPA_INVALID, }; #endif diff --git a/nest/rt-table.c b/nest/rt-table.c index 8cee48d6..8fa79f02 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -360,12 +360,12 @@ enum aspa_result aspa_check(rtable *tab, const adata *path, bool force_upstream) /* No support for confed paths */ if (as_path_contains_confed(path)) - return ASPA_INVALID_CONFED; + return ASPA_INVALID; /* Check path length */ uint len = as_path_getlen(path); if (len == 0) - return ASPA_INVALID_EMPTY; + return ASPA_INVALID; /* Normalize the AS Path: drop stuffings */ u32 *asns = alloca(sizeof(u32) * len); @@ -420,7 +420,7 @@ end_of_aspa:; min_up = ap; else if (ap && !up) /* Exists but doesn't allow this upstream */ - return ASPA_INVALID_LEAK; + return ASPA_INVALID; } /* Fast path for no ASPA here */ @@ -468,7 +468,7 @@ end_of_aspa:; return ASPA_UNKNOWN; /* Now there is surely a valley there. */ - return ASPA_INVALID_LEAK; + return ASPA_INVALID; } /**