0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-12-22 09:41:54 +00:00

Merge commit '997d2f57' into thread-merge-2.16

This commit is contained in:
Maria Matejka 2024-11-29 11:43:49 +01:00
commit cd63810e4e
12 changed files with 312 additions and 53 deletions

View File

@ -1068,14 +1068,15 @@ inherited from templates can be updated by new definitions.
<tag><label id="proto-rpki-reload">rpki reload <m/switch/</tag> <tag><label id="proto-rpki-reload">rpki reload <m/switch/</tag>
Import or export filters may depend on route RPKI status (using Import or export filters may depend on route RPKI status (using
<cf/roa_check()/ operator). In contrast to to other filter operators, <cf/roa_check()/ or <cf/aspa_check()/ operators). In contrast to other
this status for the same route may change as the content of ROA tables filter operators, this status for the same route may change as the
changes. When this option is active, BIRD activates automatic reload of content of ROA and ASPA tables changes. When this option is active, BIRD
the appropriate subset of prefixes imported or exported by the channels activates automatic reload of the appropriate subset of prefixes imported
whenever ROA tables are updated (after a short settle or exported by the channels whenever ROA and ASPA
time). When disabled, route reloads have to be requested manually. The tables are updated (after a short settle time). When disabled, route
option is ignored if <cf/roa_check()/ is not used in channel filters. reloads have to be requested manually. The option is ignored if neither
Note that for BGP channels, automatic reload requires <cf/roa_check()/ nor <cf/aspa_check()/ is used in channel filters. Note
that for BGP channels, automatic reload requires
<ref id="bgp-import-table" name="import table"> or <ref id="bgp-import-table" name="import table"> or
<ref id="bgp-export-table" name="export table"> (for respective <ref id="bgp-export-table" name="export table"> (for respective
direction). Default: on. direction). Default: on.
@ -1979,16 +1980,67 @@ of a set" operation - it can be used on:
clist that is also a member of the pair/quad set). clist that is also a member of the pair/quad set).
</itemize> </itemize>
<p>There is one operator related to ROA infrastructure - <cf/roa_check()/. It <p>There are also operators related to RPKI infrastructure used to run
examines a ROA table and does <rfc id="6483"> route origin validation for a <rfc id="6483"> route origin validation and (draft) AS path validation.
given network prefix. The basic usage is <cf>roa_check(<m/table/)</cf>, 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
<cf>roa_check(<m/table/, <m/prefix/, <m/asn/)</cf>, which allows to specify a
prefix and an ASN as arguments.
<itemize>
<item><cf>roa_check(<m/table/)</cf> checks the current route in the specified
ROA table and returns <cf>ROA_UNKNOWN</cf>, <cf>ROA_INVALID</cf> or <cf>ROA_VALID</cf>,
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.
<item><cf>roa_check(<m/table/, <m/prefix/, <m/asn/)</cf> 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 <cf>roa_check(<m/table/, net, bgp_path.last)</cf> and it is faster
to call the short variant.
<item><cf>aspa_check_downstream(<m/table/)</cf> checks the current route
in the specified ASPA table and returns <cf>ASPA_UNKNOWN</cf>, <cf>ASPA_INVALID</cf>,
or <cf>ASPA_VALID</cf> 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.
<item><cf>aspa_check_upstream(<m/table/)</cf> 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.
<item><cf>aspa_check(<m/table/, <m/path/, <m/is_upstream/)</cf> is
an explicit version of the former two ASPA check operators. The equivalent
of <cf>aspa_check_downstream</cf> is <cf>aspa_check(<m/table/, bgp_path, false)</cf>
and for <cf>aspa_check_upstream</cf> it is
<cf>aspa_check(<m/table/, bgp_path, true)</cf>.
Note: the ASPA check does not include the local ASN in the AS path.
Also, <cf>ASPA_INVALID</cf> is returned for an empty AS path
or for AS path containing <cf>CONFED_SET</cf> or <cf>CONFED_SEQUENCE</cf> blocks,
as the (draft) stipulates.
</itemize>
<p>The following example checks for ROA and ASPA on routes from a customer:
<code>
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;
}
</code>
<sect>Control structures <sect>Control structures
<label id="control-structures"> <label id="control-structures">
@ -5722,6 +5774,10 @@ affected routes after RPKI update, see option <ref id="proto-rpki-reload"
name="rpki reload">. Or you can use a BIRD client command <cf>reload in name="rpki reload">. Or you can use a BIRD client command <cf>reload in
<m/bgp_protocol_name/</cf> for manual call of revalidation of all routes. <m/bgp_protocol_name/</cf> for manual call of revalidation of all routes.
<p>The same protocol, since version 2, also receives and maintains a set
of ASPAs. You can then validate AS paths using function <cf/aspa_check()/
in (import) filters.
<sect1>Supported transports <sect1>Supported transports
<p> <p>
<itemize> <itemize>
@ -5741,6 +5797,7 @@ define more RPKI protocols generally.
protocol rpki [&lt;name&gt;] { protocol rpki [&lt;name&gt;] {
roa4 { table &lt;tab&gt;; }; roa4 { table &lt;tab&gt;; };
roa6 { table &lt;tab&gt;; }; roa6 { table &lt;tab&gt;; };
aspa { table &lt;tab&gt;; };
remote &lt;ip&gt; | "&lt;domain&gt;" [port &lt;num&gt;]; remote &lt;ip&gt; | "&lt;domain&gt;" [port &lt;num&gt;];
port &lt;num&gt;; port &lt;num&gt;;
local address &lt;ip&gt;; local address &lt;ip&gt;;
@ -5756,10 +5813,12 @@ protocol rpki [&lt;name&gt;] {
remote public key "&lt;/path/to/known_host&gt;"; remote public key "&lt;/path/to/known_host&gt;";
user "&lt;name&gt;"; user "&lt;name&gt;";
}; };
max version 2;
min version 2;
} }
</code> </code>
<p>Alse note that you have to specify the ROA channel. If you want to import <p>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 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 prefixes only. If you want to fetch both IPv4 and even IPv6 ROAs you have to
specify both channels. specify both channels.
@ -5806,6 +5865,16 @@ specify both channels.
instead. This may be useful for implementing loose RPKI check for instead. This may be useful for implementing loose RPKI check for
blackholes. Default: disabled. blackholes. Default: disabled.
<tag>min version <m/num/</tag>
Minimal allowed version of the RTR protocol. BIRD will refuse to
downgrade a connection below this version and drop the session instead.
Default: 0
<tag>max version <m/num/</tag>
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
<tag>transport tcp { <m/TCP transport options.../ }</tag> Transport over <tag>transport tcp { <m/TCP transport options.../ }</tag> Transport over
TCP, it's the default transport. Cannot be combined with a SSH transport. TCP, it's the default transport. Cannot be combined with a SSH transport.
Default: TCP, no authentication. Default: TCP, no authentication.

View File

@ -2439,11 +2439,11 @@ function t_aspa_check()
p1.prepend(65542); p1.prepend(65542);
bt_assert(aspa_check(at, p1, false) = ASPA_VALID); 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); p1.prepend(65555);
bt_assert(aspa_check(at, p1, false) = ASPA_UNKNOWN); 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+; bgppath p2 = +empty+;
p2.prepend(65554); p2.prepend(65554);
@ -2454,13 +2454,13 @@ function t_aspa_check()
p2.prepend(65543); p2.prepend(65543);
bt_assert(aspa_check(at, p2, false) = ASPA_UNKNOWN); 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+; bgppath p3 = +empty+;
p3.prepend(65541); p3.prepend(65541);
p3.prepend(65544); p3.prepend(65544);
bt_assert(aspa_check(at, p3, false) = ASPA_INVALID_LEAK); bt_assert(aspa_check(at, p3, false) = ASPA_INVALID);
bt_assert(aspa_check(at, p3, true) = ASPA_INVALID_LEAK); bt_assert(aspa_check(at, p3, true) = ASPA_INVALID);
} }
bt_test_suite(t_aspa_check, "Testing ASPA"); bt_test_suite(t_aspa_check, "Testing ASPA");

View File

@ -177,7 +177,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_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINED)
CF_ENUM(T_ENUM_RTD, RTD_, UNICAST, BLACKHOLE, UNREACHABLE, PROHIBIT) CF_ENUM(T_ENUM_RTD, RTD_, UNICAST, BLACKHOLE, UNREACHABLE, PROHIBIT)
CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID) 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_PX(T_ENUM_AF, AF_, AFI_, IPV4, IPV6)
CF_ENUM(T_ENUM_MPLS_POLICY, MPLS_POLICY_, NONE, STATIC, PREFIX, AGGREGATE, VRF) CF_ENUM(T_ENUM_MPLS_POLICY, MPLS_POLICY_, NONE, STATIC, PREFIX, AGGREGATE, VRF)

View File

@ -613,6 +613,11 @@ channel_roa_subscribe_filter(struct channel *c, int dir)
found = 1; found = 1;
break; 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: default:
break; break;
} }

View File

@ -932,9 +932,7 @@ void ea_show_nexthop_list(struct cli *c, struct nexthop_adata *nhad);
enum aspa_result { enum aspa_result {
ASPA_UNKNOWN = 0, ASPA_UNKNOWN = 0,
ASPA_VALID, ASPA_VALID,
ASPA_INVALID_EMPTY, ASPA_INVALID,
ASPA_INVALID_CONFED,
ASPA_INVALID_LEAK,
}; };
int net_roa_check(rtable *tab, const net_addr *n, u32 asn); int net_roa_check(rtable *tab, const net_addr *n, u32 asn);

View File

@ -731,12 +731,12 @@ enum aspa_result aspa_check(rtable *tab, const adata *path, bool force_upstream)
/* No support for confed paths */ /* No support for confed paths */
if (as_path_contains_confed(path)) if (as_path_contains_confed(path))
return ASPA_INVALID_CONFED; return ASPA_INVALID;
/* Check path length */ /* Check path length */
uint len = as_path_getlen(path); uint len = as_path_getlen(path);
if (len == 0) if (len == 0)
return ASPA_INVALID_EMPTY; return ASPA_INVALID;
/* Normalize the AS Path: drop stuffings */ /* Normalize the AS Path: drop stuffings */
u32 *asns = alloca(sizeof(u32) * len); u32 *asns = alloca(sizeof(u32) * len);
@ -796,7 +796,7 @@ end_of_aspa:;
min_up = ap; min_up = ap;
else if (ap && !up) else if (ap && !up)
/* Exists but doesn't allow this upstream */ /* Exists but doesn't allow this upstream */
return ASPA_INVALID_LEAK; return ASPA_INVALID;
} }
/* Fast path for no ASPA here */ /* Fast path for no ASPA here */
@ -844,7 +844,7 @@ end_of_aspa:;
return ASPA_UNKNOWN; return ASPA_UNKNOWN;
/* Now there is surely a valley there. */ /* Now there is surely a valley there. */
return ASPA_INVALID_LEAK; return ASPA_INVALID;
} }
struct rte_storage * struct rte_storage *

View File

@ -40,7 +40,7 @@ CF_ENUM(T_ENUM_BGP_ORIGIN, ORIGIN_, IGP, EGP, INCOMPLETE)
CF_KEYWORDS(CEASE, PREFIX, LIMIT, HIT, ADMINISTRATIVE, SHUTDOWN, RESET, PEER, CF_KEYWORDS(CEASE, PREFIX, LIMIT, HIT, ADMINISTRATIVE, SHUTDOWN, RESET, PEER,
CONFIGURATION, CHANGE, DECONFIGURED, CONNECTION, REJECTED, COLLISION, CONFIGURATION, CHANGE, DECONFIGURED, CONNECTION, REJECTED, COLLISION,
OUT, OF, RESOURCES, ASPA_CHECK_CUSTOMER) OUT, OF, RESOURCES, ASPA_CHECK_UPSTREAM, ASPA_CHECK_DOWNSTREAM)
%type<i> bgp_cease_mask bgp_cease_list bgp_cease_flag bgp_role_name %type<i> bgp_cease_mask bgp_cease_list bgp_cease_flag bgp_role_name
@ -384,7 +384,7 @@ CF_CLI(RELOAD BGP OUT, proto_patt, [<name>], [[Refresh routes to neighbor]])
} }
/* ASPA shortcuts */ /* ASPA shortcuts */
term: ASPA_CHECK '(' rtable ')' { $$ = term: ASPA_CHECK_DOWNSTREAM '(' rtable ')' { $$ =
f_new_inst(FI_ASPA_CHECK_EXPLICIT, f_new_inst(FI_ASPA_CHECK_EXPLICIT,
f_new_inst(FI_EA_GET, f_new_inst(FI_EA_GET,
f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_ROUTE, .val.rte = NULL, }), f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_ROUTE, .val.rte = NULL, }),
@ -395,7 +395,7 @@ term: ASPA_CHECK '(' rtable ')' { $$ =
); );
} }
term: ASPA_CHECK_CUSTOMER '(' rtable ')' { $$ = term: ASPA_CHECK_UPSTREAM '(' rtable ')' { $$ =
f_new_inst(FI_ASPA_CHECK_EXPLICIT, f_new_inst(FI_ASPA_CHECK_EXPLICIT,
f_new_inst(FI_EA_GET, f_new_inst(FI_EA_GET,
f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_ROUTE, .val.rte = NULL, }), f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_ROUTE, .val.rte = NULL, }),

View File

@ -33,8 +33,8 @@ rpki_check_unused_transport(void)
CF_DECLS CF_DECLS
CF_KEYWORDS(RPKI, REMOTE, BIRD, PRIVATE, PUBLIC, KEY, TCP, SSH, TRANSPORT, USER, CF_KEYWORDS(RPKI, REMOTE, BIRD, PRIVATE, PUBLIC, KEY, TCP, SSH, TRANSPORT, USER,
RETRY, REFRESH, EXPIRE, KEEP, IGNORE, MAX, LENGTH, LOCAL, ADDRESS, RETRY, REFRESH, EXPIRE, KEEP, IGNORE, MAX, MIN, LENGTH, LOCAL, ADDRESS,
AUTHENTICATION, NONE, MD5, PASSWORD) AUTHENTICATION, NONE, MD5, PASSWORD, VERSION)
%type <i> rpki_keep_interval %type <i> rpki_keep_interval
@ -48,6 +48,8 @@ rpki_proto_start: proto_start RPKI {
RPKI_CFG->retry_interval = RPKI_RETRY_INTERVAL; RPKI_CFG->retry_interval = RPKI_RETRY_INTERVAL;
RPKI_CFG->refresh_interval = RPKI_REFRESH_INTERVAL; RPKI_CFG->refresh_interval = RPKI_REFRESH_INTERVAL;
RPKI_CFG->expire_interval = RPKI_EXPIRE_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); }; rpki_proto: rpki_proto_start proto_name '{' rpki_proto_opts '}' { rpki_check_config(RPKI_CFG); };
@ -84,6 +86,14 @@ rpki_proto_item:
RPKI_CFG->keep_expire_interval = $2; RPKI_CFG->keep_expire_interval = $2;
} }
| IGNORE MAX LENGTH bool { RPKI_CFG->ignore_max_length = $4; } | 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: rpki_keep_interval:

View File

@ -62,6 +62,7 @@ enum pdu_type {
CACHE_RESET = 8, CACHE_RESET = 8,
ROUTER_KEY = 9, ROUTER_KEY = 9,
ERROR = 10, ERROR = 10,
ASPA = 11,
PDU_TYPE_MAX PDU_TYPE_MAX
}; };
@ -76,7 +77,8 @@ static const char *str_pdu_type_[] = {
[END_OF_DATA] = "End of Data", [END_OF_DATA] = "End of Data",
[CACHE_RESET] = "Cache Reset", [CACHE_RESET] = "Cache Reset",
[ROUTER_KEY] = "Router Key", [ROUTER_KEY] = "Router Key",
[ERROR] = "Error" [ERROR] = "Error",
[ASPA] = "ASPA",
}; };
static const char *str_pdu_type(uint type) { static const char *str_pdu_type(uint type) {
@ -193,6 +195,35 @@ struct pdu_error {
* Error Diagnostic Message */ * Error Diagnostic Message */
} PACKED; } 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 { struct pdu_reset_query {
u8 ver; u8 ver;
u8 type; u8 type;
@ -230,9 +261,13 @@ static const size_t min_pdu_size[] = {
[END_OF_DATA] = sizeof(struct pdu_end_of_data_v0), [END_OF_DATA] = sizeof(struct pdu_end_of_data_v0),
[CACHE_RESET] = sizeof(struct pdu_cache_response), [CACHE_RESET] = sizeof(struct pdu_cache_response),
[ROUTER_KEY] = sizeof(struct pdu_header), /* FIXME */ [ROUTER_KEY] = sizeof(struct pdu_header), /* FIXME */
[ASPA] = sizeof(struct pdu_aspa),
[ERROR] = 16, [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 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, ...);
#define rpki_send_error_pdu(cache, error_code, err_pdu_len, erroneous_pdu, fmt...) ({ \ #define rpki_send_error_pdu(cache, error_code, err_pdu_len, erroneous_pdu, fmt...) ({ \
@ -298,7 +333,7 @@ rpki_pdu_to_host_byte_order(struct pdu_header *pdu)
struct pdu_end_of_data_v0 *eod0 = (void *) pdu; struct pdu_end_of_data_v0 *eod0 = (void *) pdu;
eod0->serial_num = ntohl(eod0->serial_num); /* Same either for version 1 */ 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; struct pdu_end_of_data_v1 *eod1 = (void *) pdu;
eod1->expire_interval = ntohl(eod1->expire_interval); eod1->expire_interval = ntohl(eod1->expire_interval);
@ -334,6 +369,21 @@ rpki_pdu_to_host_byte_order(struct pdu_header *pdu)
break; 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: case ROUTER_KEY:
/* Router Key PDU is not supported yet */ /* Router Key PDU is not supported yet */
@ -359,6 +409,9 @@ rpki_pdu_to_host_byte_order(struct pdu_header *pdu)
static struct pdu_header * static struct pdu_header *
rpki_pdu_back_to_network_byte_order(struct pdu_header *out, const struct pdu_header *in) 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); memcpy(out, in, in->len);
rpki_pdu_to_host_byte_order(out); rpki_pdu_to_host_byte_order(out);
return out; return out;
@ -392,7 +445,7 @@ rpki_log_packet(struct rpki_cache *cache, const struct pdu_header *pdu, const en
case END_OF_DATA: case END_OF_DATA:
{ {
const struct pdu_end_of_data_v1 *eod = (void *) pdu; 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)); 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 else
SAVE(bsnprintf(detail, sizeof(detail), "(session id: %u, serial number: %u)", eod->session_id, eod->serial_num)); SAVE(bsnprintf(detail, sizeof(detail), "(session id: %u, serial number: %u)", eod->session_id, eod->serial_num));
@ -460,6 +513,25 @@ rpki_log_packet(struct rpki_cache *cache, const struct pdu_header *pdu, const en
break; 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: default:
*detail = '\0'; *detail = '\0';
} }
@ -561,7 +633,9 @@ rpki_check_receive_packet(struct rpki_cache *cache, const struct pdu_header *pdu
} }
else if (!cache->last_update && else if (!cache->last_update &&
(pdu->ver <= RPKI_MAX_VERSION) && (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_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; cache->version = pdu->ver;
@ -576,15 +650,21 @@ 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); rpki_send_error_pdu(cache, UNSUPPORTED_PDU_TYPE, pdu_len, pdu, "Unsupported PDU type %u received", pdu->type);
return RPKI_ERROR; 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; return RPKI_ERROR;
} }
@ -611,7 +691,8 @@ rpki_handle_error_pdu(struct rpki_cache *cache, const struct pdu_error *pdu)
case UNSUPPORTED_PROTOCOL_VER: case UNSUPPORTED_PROTOCOL_VER:
CACHE_TRACE(D_PACKETS, cache, "Client uses unsupported protocol version"); CACHE_TRACE(D_PACKETS, cache, "Client uses unsupported protocol version");
if (pdu->ver <= RPKI_MAX_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_TRACE(D_EVENTS, cache, "Downgrading from protocol version %d to version %d", cache->version, pdu->ver);
cache->version = pdu->ver; cache->version = pdu->ver;
@ -789,6 +870,29 @@ rpki_handle_prefix_pdu(struct rpki_cache *cache, const struct pdu_header *pdu)
return RPKI_SUCCESS; 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 static uint
rpki_check_interval(struct rpki_cache *cache, const char *(check_fn)(uint), uint interval) rpki_check_interval(struct rpki_cache *cache, const char *(check_fn)(uint), uint interval)
{ {
@ -814,7 +918,7 @@ rpki_handle_end_of_data_pdu(struct rpki_cache *cache, const struct pdu_end_of_da
return; 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)) if (!cf->keep_refresh_interval && rpki_check_interval(cache, rpki_check_refresh_interval, pdu->refresh_interval))
cache->refresh_interval = pdu->refresh_interval; cache->refresh_interval = pdu->refresh_interval;
@ -898,6 +1002,10 @@ rpki_rx_packet(struct rpki_cache *cache, struct pdu_header *pdu)
/* TODO: Implement Router Key PDU handling */ /* TODO: Implement Router Key PDU handling */
break; break;
case ASPA:
rpki_handle_aspa_pdu(cache, (void *) pdu);
break;
default: default:
CACHE_TRACE(D_PACKETS, cache, "Received unsupported type (%u)", pdu->type); CACHE_TRACE(D_PACKETS, cache, "Received unsupported type (%u)", pdu->type);
}; };

View File

@ -23,8 +23,10 @@
* +4 bytes (Length of inserted text) * +4 bytes (Length of inserted text)
* +800 bytes (UTF-8 text 400*2 bytes) * +800 bytes (UTF-8 text 400*2 bytes)
* ------------ * ------------
* = 848 bytes (Maximal expected PDU size) */ * = 848 bytes (Maximal expected PDU size)
#define RPKI_PDU_MAX_LEN 848 *
* 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 */ /* RX buffer size has a great impact to scheduler granularity */
#define RPKI_RX_BUFFER_SIZE 4096 #define RPKI_RX_BUFFER_SIZE 4096

View File

@ -137,6 +137,33 @@ rpki_table_remove_roa(struct rpki_cache *cache, struct channel *channel, const n
rte_update(channel, &pfxr->n, NULL, p->p.main_source); rte_update(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) };
ea_list *ea = NULL;
ea_set_attr_u32(&ea, &ea_gen_preference, 0, channel->preference);
ea_set_attr_u32(&ea, &ea_gen_source, 0, RTS_RPKI);
ea_set_attr_data(&ea, &ea_gen_aspa_providers, 0, providers, providers_length);
rte e0 = { .attrs = ea, .src = p->p.main_source, };
rte_update(channel, &n.n, &e0, p->p.main_source);
}
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_update(channel, &n.n, NULL, p->p.main_source);
}
void void
rpki_start_refresh(struct rpki_proto *p) rpki_start_refresh(struct rpki_proto *p)
{ {
@ -144,6 +171,8 @@ rpki_start_refresh(struct rpki_proto *p)
rt_refresh_begin(&p->roa4_channel->in_req); rt_refresh_begin(&p->roa4_channel->in_req);
if (p->roa6_channel) if (p->roa6_channel)
rt_refresh_begin(&p->roa6_channel->in_req); rt_refresh_begin(&p->roa6_channel->in_req);
if (p->aspa_channel)
rt_refresh_begin(&p->aspa_channel->in_req);
p->refresh_channels = 1; p->refresh_channels = 1;
} }
@ -160,6 +189,8 @@ rpki_stop_refresh(struct rpki_proto *p)
rt_refresh_end(&p->roa4_channel->in_req); rt_refresh_end(&p->roa4_channel->in_req);
if (p->roa6_channel) if (p->roa6_channel)
rt_refresh_end(&p->roa6_channel->in_req); rt_refresh_end(&p->roa6_channel->in_req);
if (p->aspa_channel)
rt_refresh_end(&p->aspa_channel->in_req);
} }
/* /*
@ -608,7 +639,8 @@ rpki_init_cache(struct rpki_proto *p, struct rpki_config *cf)
cache->state = RPKI_CS_SHUTDOWN; cache->state = RPKI_CS_SHUTDOWN;
cache->request_session_id = 1; 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->refresh_interval = cf->refresh_interval;
cache->retry_interval = cf->retry_interval; cache->retry_interval = cf->retry_interval;
@ -714,6 +746,23 @@ rpki_reconfigure_cache(struct rpki_proto *p UNUSED, struct rpki_cache *cache, st
return NEED_RESTART; 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) if (old->tr_config.type != new->tr_config.type)
{ {
CACHE_TRACE(D_EVENTS, cache, "Transport type changed"); CACHE_TRACE(D_EVENTS, cache, "Transport type changed");
@ -814,7 +863,8 @@ rpki_reconfigure(struct proto *P, struct proto_config *CF)
struct rpki_cache *cache = p->cache; struct rpki_cache *cache = p->cache;
if (!proto_configure_channel(&p->p, &p->roa4_channel, proto_cf_find_channel(CF, NET_ROA4)) || 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; return NEED_RESTART;
if (rpki_reconfigure_cache(p, cache, new, old) != SUCCESSFUL_RECONF) if (rpki_reconfigure_cache(p, cache, new, old) != SUCCESSFUL_RECONF)
@ -836,6 +886,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->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 P; return P;
} }
@ -949,6 +1000,11 @@ rpki_show_proto_info(struct proto *P)
channel_show_info(p->roa6_channel); channel_show_info(p->roa6_channel);
else else
cli_msg(-1006, " No roa6 channel"); cli_msg(-1006, " No roa6 channel");
if (p->aspa_channel)
channel_show_info(p->aspa_channel);
else
cli_msg(-1006, " No aspa channel");
} }
} }
@ -966,6 +1022,9 @@ rpki_show_proto_info(struct proto *P)
void void
rpki_check_config(struct rpki_config *cf) 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 */ /* Do not check templates at all */
if (cf->c.class == SYM_TEMPLATE) if (cf->c.class == SYM_TEMPLATE)
return; return;
@ -1020,7 +1079,7 @@ struct protocol proto_rpki = {
.init = rpki_init, .init = rpki_init,
.start = rpki_start, .start = rpki_start,
.postconfig = rpki_postconfig, .postconfig = rpki_postconfig,
.channel_mask = (NB_ROA4 | NB_ROA6), .channel_mask = (NB_ROA4 | NB_ROA6 | NB_ASPA),
.show_proto_info = rpki_show_proto_info, .show_proto_info = rpki_show_proto_info,
.shutdown = rpki_shutdown, .shutdown = rpki_shutdown,
.copy_config = rpki_copy_config, .copy_config = rpki_copy_config,

View File

@ -29,7 +29,8 @@
#define RPKI_VERSION_0 0 #define RPKI_VERSION_0 0
#define RPKI_VERSION_1 1 #define RPKI_VERSION_1 1
#define RPKI_MAX_VERSION RPKI_VERSION_1 #define RPKI_VERSION_2 2
#define RPKI_MAX_VERSION RPKI_VERSION_2
/* /*
@ -60,6 +61,7 @@ struct rpki_cache {
u8 request_session_id; /* 1: have to request new session id; 0: we have already received session id */ 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 */ u32 serial_num; /* Serial number denotes the logical version of data from cache server */
u8 version; /* Protocol version */ u8 version; /* Protocol version */
u8 min_version; /* Minimum allowed protocol version */
btime last_update; /* Last successful synchronization with cache server */ btime last_update; /* Last successful synchronization with cache server */
btime last_rx_prefix; /* Last received prefix PDU */ btime last_rx_prefix; /* Last received prefix PDU */
@ -83,6 +85,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_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_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);
void rpki_start_refresh(struct rpki_proto *p); void rpki_start_refresh(struct rpki_proto *p);
void rpki_stop_refresh(struct rpki_proto *p); void rpki_stop_refresh(struct rpki_proto *p);
@ -112,6 +117,7 @@ struct rpki_proto {
struct channel *roa4_channel; struct channel *roa4_channel;
struct channel *roa6_channel; struct channel *roa6_channel;
struct channel *aspa_channel;
u8 refresh_channels; /* For non-incremental updates using rt_refresh_begin(), rt_refresh_end() */ u8 refresh_channels; /* For non-incremental updates using rt_refresh_begin(), rt_refresh_end() */
}; };
@ -129,6 +135,8 @@ struct rpki_config {
u8 keep_retry_interval:1; /* Do not overwrite retry interval by cache server update */ 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 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 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); void rpki_check_config(struct rpki_config *cf);