ASPA result when ASPA table is empty? (disconnected or 0 entries supplied)
Jeroen Massar
jeroen at massar.ch
Tue Apr 15 12:05:03 CEST 2025
[2 replies in one]
> On 14 Apr 2025, at 16:56, Alarig Le Lay via Bird-users <bird-users at network.cz> wrote:
>
> Hello,
>
> On Mon 14 Apr 2025 15:57:48 GMT, Jeroen Massar via Bird-users wrote:
>> If we simply define:
>>
>> ```
>> aspa table aspas;
>> ```
>>
>> and then call a `aspa_check(aspas, ...., ...)` then I sometimes
>> receive a ASPA_VALID back, especially for paths with only 1 ASN.
>>
>> This, while that table is really empty.
>
> The RFC wording isn’t 100% clear, but it’s exepected:
> https://www.ietf.org/archive/id/draft-ietf-sidrops-aspa-verification-22.html#name-as_path-verification
>
> TL;DR: if the path lenght is one, and the peer AS equals the AS path,
> then it’s a valid ASPA path.
Considering no validation can be done if there are no known ASPAs, there should IMHO never be a ASPA_VALID coming out in that situation.
(but awaiting for Job etc to chime in on this :) )
I am primarily interested in "is the path invalid" (be that RPKI or ASPA), this as those should never exist and should be discarded (filtered).
Till RPKI/ASPA are the norm (read: most prefixes covered), we'll have to treat ASPA_UNKNOWN and ROA_UNKNOWN as exactly that, an unknown state: no decision to drop that path. afaik a mix of VALID + UNKNOWN for the same prefix should not happen. And UNKNOWN will be also the case when for instance the tables are empty due to the RTR being down etc.
For the case of checking direct peers, IMHO it only makes sense to use (see below for an update though):
```
function fn_aspa_is_invalid(bool is_upstream) -> bool
{
if bgp_path.len < 2 then
{
// effectively unknown as no ASPA path can be verified, thus return "not invalid"
return false;
}
case aspa_check(aspas, bgp_path, is_upstream)
{
ASPA_VALID: return false;
ASPA_UNKNOWN: return false;
ASPA_INVALID: return true;
}
// Fallthrough (should not happen)
return false;
}
```
Though, IMHO that bgp_path.len check should be in the aspa_check() function and likely be part of the ASPA RFC/draft; though maybe the argument is that one should not check ASPA paths for direct peers?
For that use case RPKI ROV is likely the better part and otherwise IRR can cover it (though if everybody had RPKI entries we could get rid of IRR).
> On 15 Apr 2025, at 08:15, Maria Matejka <maria.matejka at nic.cz> wrote:
>
> Hello Jeroen,
>
>> I was checking the ASPA possibilities, and the is_upstream option of aspa_check definitely is confusing.
>> The downstream/upstream variant also, as the logic seems reverse to what one would expect.
>
> The draft defines downstream / upstream that way, so sorry for that confusion, I would also kinda prefer wording like "received from transit, only for my customers" and "just some parts of the internet behind this peer". Maybe we should add this as an alias to "downstream". Any preference for exact wording?
It is a tricky one, the definitions in the RFC definitely do not help much either (and neither that they refer to the 'other document'...
Also aspa_check_upstream(table) + aspa_check_downstream(table) already exist and are aliases to aspa_check(..., ..., true / false).
For most operators I think we care more about the difference between transit & peers/downstreams.
> Also, in BIRD 3 it is later going to be possible to infer this information from the BGP OTC setting. We don't have the right data structure for that yet tho.
OTC, what a great hint, as we do have access to that, thus we can do something like:
```
function fn_aspa_is_invalid() -> bool
bool is_upstream;
{
# Is the BGP role that of provider (0) then it is not an "ASPA upstream path"
if bgp_otc = 0 then is_upstream = false;
else is_upstream = true;
# Directly connected ASN do not have a ASN path, thus cannot check the ASPA for that
if bgp_path.len < 2 then return false;
case aspa_check(aspas, bgp_path, is_upstream)
{
ASPA_VALID: return false;
ASPA_UNKNOWN: return false;
ASPA_INVALID: return true;
}
// Fallthrough (should not happen)
return false;
}
```
I think this will make decent sense; but then, I could be wrong ;)
>> A better example would be rather welcome, though, I think I have it correct now with details from the list.
>
> It's quite a complicated matter. There are some better examples in <https://datatracker.ietf.org/meeting/122/materials/slides-122-sidrops-aspa-based-as-path-verification-examples-and-unit-tests-00>, and we are expecting to use and merge these with our documentation. (Contributions of this kind welcome, until somebody fixes this internally.)
>
> Would you appreciate even more than that example set?
I guess one thing that would be useful is if there was a semi-'standard' full example of a "Full ISP setup" in the Wiki or directly in the documentation, that would likely help a lot of folks who are 'where do I start'.
There are these:
https://gitlab.nic.cz/labs/bird/-/wikis/BGP_example_1
but as the date shows, they are quite a bit older.
And https://gitlab.nic.cz/labs/bird/-/wikis/Route_server_with_community_based_filtering_and_single_RIB does not include OTC for instance.
I would be happy to clean up what I am using and then contribute that for others to use too (and then receive a lot of comments about how things could be better ;) )
>> It would also be useful if we could do something like we can do for RPKI:
>>
>> print roa_check(rpki4, 84.205.83.0/24, 12654) = ROA_UNKNOWN;
>> print roa_check(rpki4, 93.175.146.0/24, 12654) = ROA_VALID;
>> print roa_check(rpki4, 93.175.147.0/24, 12654) = ROA_INVALID;
>>
>> and that we could run a similar:
>> print aspa_check(aspas, "54874 970", false) = ASPA_INVALID;
>>
>> Currently though there is no datatype that would allow one to provide the BGP path thus that test would not be possible.
>
> There is one, actually, but you have to build the BGP path like this:
>
> +empty+.prepend(970).prepend(54874)
>
> It's quite hard to make our filter syntax accept BGP path literals or constructors, but… now I'm thinking of something which might work, tho. (Internal note for the team: issue #209.)
Thanks, that is already perfectly fit for my purpose, as now I can do:
```
# birdc "eval test_ripe_beacons()"
# tail -f /var/log/bird.log
function test_ripe_beacons() -> string
{
print "Testing ROA";
print "Should be TRUE TRUE TRUE: ";
print roa_check(rpki4, 84.205.83.0/24, 12654) = ROA_UNKNOWN;
print roa_check(rpki4, 93.175.146.0/24, 12654) = ROA_VALID;
print roa_check(rpki4, 93.175.147.0/24, 12654) = ROA_INVALID;
case aspa_check(aspas, +empty+.prepend(970).prepend(54874), false)
{
ASPA_VALID: print "ASPA_CHECK(970 54874): VALID (OK)";
ASPA_UNKNOWN: print "ASPA_CHECK(970 54874): UNKNOWN (WRONG)";
ASPA_INVALID: print "ASPA_CHECK(970 54874): INVALID (OK)";
}
case aspa_check(aspas, +empty+, false)
{
ASPA_VALID: print "ASPA_CHECK(empty): VALID (WRONG)";
ASPA_UNKNOWN: print "ASPA_CHECK(empty): UNKNOWN (ok)";
ASPA_INVALID: print "ASPA_CHECK(empty): INVALID (WRONG)";
}
case aspa_check(aspas, +empty+.prepend(57777), false)
{
ASPA_VALID: print "ASPA_CHECK(57777): VALID (WRONG)";
ASPA_UNKNOWN: print "ASPA_CHECK(57777): UNKNOWN (ok)";
ASPA_INVALID: print "ASPA_CHECK(57777): INVALID (WRONG)";
}
return "Check the logs";
}
```
and voila, I can do a quick check on those ASPA entries; yes, I have to modify the config and do a configure to test, but for the few tests that is good enough to get out of the engine what I want to know from it:
```
Testing ROA
Should be TRUE TRUE TRUE:
TRUE
TRUE
TRUE
ASPA_CHECK(970 54874): UNKNOWN (WRONG)
ASPA_CHECK(empty): INVALID (WRONG)
ASPA_CHECK(57777): VALID (WRONG)
```
The 970 54874 path says UNKNOWN, but that is correct as there are no ASPA entries (stayrtr does not export them at the moment).
For my version of understanding, the latter two should thus like I suggest above: direct peers = ASPA_UNKNOWN
If one could append strings then I could build a simple sanity check if expected situations (like the RIPE beacons) are functioning fine and report on that and/or optionally even disable features when things are not looking too sane. But for testing the above is already great to have.
Greets,
Jeroen
More information about the Bird-users
mailing list