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:
commit
cd63810e4e
105
doc/bird.sgml
105
doc/bird.sgml
@ -1068,14 +1068,15 @@ inherited from templates can be updated by new definitions.
|
||||
|
||||
<tag><label id="proto-rpki-reload">rpki reload <m/switch/</tag>
|
||||
Import or export filters may depend on route RPKI status (using
|
||||
<cf/roa_check()/ operator). In contrast to to other filter operators,
|
||||
this status for the same route may change as the content of ROA tables
|
||||
changes. When this option is active, BIRD activates automatic reload of
|
||||
the appropriate subset of prefixes imported or exported by the channels
|
||||
whenever ROA tables are updated (after a short settle
|
||||
time). When disabled, route reloads have to be requested manually. The
|
||||
option is ignored if <cf/roa_check()/ is not used in channel filters.
|
||||
Note that for BGP channels, automatic reload requires
|
||||
<cf/roa_check()/ or <cf/aspa_check()/ operators). In contrast to other
|
||||
filter operators, this status for the same route may change as the
|
||||
content of ROA and ASPA tables changes. When this option is active, BIRD
|
||||
activates automatic reload of the appropriate subset of prefixes imported
|
||||
or exported by the channels whenever ROA and ASPA
|
||||
tables are updated (after a short settle time). When disabled, route
|
||||
reloads have to be requested manually. The option is ignored if neither
|
||||
<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-export-table" name="export table"> (for respective
|
||||
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).
|
||||
</itemize>
|
||||
|
||||
<p>There is one operator related to ROA infrastructure - <cf/roa_check()/. It
|
||||
examines a ROA table and does <rfc id="6483"> route origin validation for a
|
||||
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.
|
||||
<p>There are also operators related to RPKI infrastructure used to run
|
||||
<rfc id="6483"> route origin validation and (draft) AS path validation.
|
||||
|
||||
<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
|
||||
<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
|
||||
<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
|
||||
<p>
|
||||
<itemize>
|
||||
@ -5741,6 +5797,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>;
|
||||
@ -5756,10 +5813,12 @@ protocol rpki [<name>] {
|
||||
remote public key "</path/to/known_host>";
|
||||
user "<name>";
|
||||
};
|
||||
max version 2;
|
||||
min version 2;
|
||||
}
|
||||
</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
|
||||
prefixes only. If you want to fetch both IPv4 and even IPv6 ROAs you have to
|
||||
specify both channels.
|
||||
@ -5806,6 +5865,16 @@ specify both channels.
|
||||
instead. This may be useful for implementing loose RPKI check for
|
||||
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
|
||||
TCP, it's the default transport. Cannot be combined with a SSH transport.
|
||||
Default: TCP, no authentication.
|
||||
|
@ -2439,11 +2439,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);
|
||||
@ -2454,13 +2454,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");
|
||||
|
@ -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_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)
|
||||
|
||||
|
@ -613,6 +613,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;
|
||||
}
|
||||
|
@ -932,9 +932,7 @@ void ea_show_nexthop_list(struct cli *c, struct nexthop_adata *nhad);
|
||||
enum aspa_result {
|
||||
ASPA_UNKNOWN = 0,
|
||||
ASPA_VALID,
|
||||
ASPA_INVALID_EMPTY,
|
||||
ASPA_INVALID_CONFED,
|
||||
ASPA_INVALID_LEAK,
|
||||
ASPA_INVALID,
|
||||
};
|
||||
|
||||
int net_roa_check(rtable *tab, const net_addr *n, u32 asn);
|
||||
|
@ -731,12 +731,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);
|
||||
@ -796,7 +796,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 */
|
||||
@ -844,7 +844,7 @@ end_of_aspa:;
|
||||
return ASPA_UNKNOWN;
|
||||
|
||||
/* Now there is surely a valley there. */
|
||||
return ASPA_INVALID_LEAK;
|
||||
return ASPA_INVALID;
|
||||
}
|
||||
|
||||
struct rte_storage *
|
||||
|
@ -40,7 +40,7 @@ CF_ENUM(T_ENUM_BGP_ORIGIN, ORIGIN_, IGP, EGP, INCOMPLETE)
|
||||
|
||||
CF_KEYWORDS(CEASE, PREFIX, LIMIT, HIT, ADMINISTRATIVE, SHUTDOWN, RESET, PEER,
|
||||
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
|
||||
|
||||
@ -384,23 +384,23 @@ CF_CLI(RELOAD BGP OUT, proto_patt, [<name>], [[Refresh routes to neighbor]])
|
||||
}
|
||||
|
||||
/* ASPA shortcuts */
|
||||
term: ASPA_CHECK '(' rtable ')' { $$ =
|
||||
term: ASPA_CHECK_DOWNSTREAM '(' rtable ')' { $$ =
|
||||
f_new_inst(FI_ASPA_CHECK_EXPLICIT,
|
||||
f_new_inst(FI_EA_GET,
|
||||
f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_ROUTE, .val.rte = NULL, }),
|
||||
ea_class_find_by_name("bgp_path")
|
||||
),
|
||||
),
|
||||
f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 0, }),
|
||||
$3
|
||||
);
|
||||
}
|
||||
|
||||
term: ASPA_CHECK_CUSTOMER '(' rtable ')' { $$ =
|
||||
term: ASPA_CHECK_UPSTREAM '(' rtable ')' { $$ =
|
||||
f_new_inst(FI_ASPA_CHECK_EXPLICIT,
|
||||
f_new_inst(FI_EA_GET,
|
||||
f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_ROUTE, .val.rte = NULL, }),
|
||||
ea_class_find_by_name("bgp_path")
|
||||
),
|
||||
),
|
||||
f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 1, }),
|
||||
$3
|
||||
);
|
||||
|
@ -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 <i> rpki_keep_interval
|
||||
|
||||
@ -48,6 +48,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); };
|
||||
@ -84,6 +86,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:
|
||||
|
@ -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, ...);
|
||||
|
||||
#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;
|
||||
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);
|
||||
@ -334,6 +369,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 */
|
||||
|
||||
@ -359,6 +409,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;
|
||||
@ -392,7 +445,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));
|
||||
@ -460,6 +513,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';
|
||||
}
|
||||
@ -561,7 +633,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;
|
||||
@ -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);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -611,7 +691,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;
|
||||
@ -789,6 +870,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)
|
||||
{
|
||||
@ -814,7 +918,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;
|
||||
@ -898,6 +1002,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);
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
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
|
||||
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);
|
||||
if (p->roa6_channel)
|
||||
rt_refresh_begin(&p->roa6_channel->in_req);
|
||||
if (p->aspa_channel)
|
||||
rt_refresh_begin(&p->aspa_channel->in_req);
|
||||
|
||||
p->refresh_channels = 1;
|
||||
}
|
||||
@ -160,6 +189,8 @@ rpki_stop_refresh(struct rpki_proto *p)
|
||||
rt_refresh_end(&p->roa4_channel->in_req);
|
||||
if (p->roa6_channel)
|
||||
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->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;
|
||||
@ -714,6 +746,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");
|
||||
@ -814,7 +863,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)
|
||||
@ -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->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;
|
||||
}
|
||||
@ -949,6 +1000,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");
|
||||
}
|
||||
}
|
||||
|
||||
@ -966,6 +1022,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;
|
||||
@ -1020,7 +1079,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,
|
||||
|
@ -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
|
||||
|
||||
|
||||
/*
|
||||
@ -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 */
|
||||
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 */
|
||||
|
||||
@ -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_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_stop_refresh(struct rpki_proto *p);
|
||||
|
||||
@ -112,6 +117,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() */
|
||||
};
|
||||
|
||||
@ -129,6 +135,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);
|
||||
|
Loading…
Reference in New Issue
Block a user