[2 replies in one]

> On 14 Apr 2025, at 16:56, Alarig Le Lay via Bird-users 
> <[email protected]> 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 <[email protected]> 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




Reply via email to