Hello BIRD maintainers,

I would like to report a possible RFC 7607 compliance issue in BIRD's BGP
UPDATE validation: routes whose AS_PATH contains AS number 0 appear to be
accepted into the BGP table instead of being treated as malformed.
Summary

BIRD appears not to reject AS 0 when it appears inside the AS_PATH path
attribute.

I tested this with an eBGP session where the peer sent a valid UPDATE whose
AS_PATH was:

65002 0

BIRD accepted the route and displayed the AS_PATH with the reserved AS 0
still present.

RFC 7607 Section 2 states that a BGP speaker must not originate or
propagate a route with AS number 0 in AS_PATH, and that an UPDATE
containing AS number 0 in AS_PATH must be considered malformed and handled
according to RFC 7606.

For a malformed AS_PATH attribute with parseable NLRI, RFC 7606 handling
should be treat-as-withdraw: the route should not be installed, while the
BGP session should remain established.
Affected Version

I reproduced this on:

BIRD version 2.19.0+branch.master.880200b1f94c
commit 880200b1

Observed Behavior

After establishing an eBGP session, the peer sent an UPDATE for 192.0.2.0/24
 with:

ORIGIN: IGP
AS_PATH: 65002 0
NEXT_HOP: 10.0.0.2
NLRI: 192.0.2.0/24

BIRD accepted the UPDATE. birdc show route 192.0.2.0/24 all showed:

Table master4:192.0.2.0/24         unreachable [as0_test ... from
10.100.0.2] * (100) [AS0i]
        Type: BGP univ
        BGP.origin: IGP
        BGP.as_path: 65002 0
        BGP.next_hop: 10.0.0.2
        BGP.local_pref: 100

The route was marked unreachable because of next-hop resolution in the
local test setup, but the important point is that the route was accepted
into the BGP table with:

BGP.as_path: 65002 0

I did not observe an error, warning, or treat-as-withdraw behavior for the
reserved AS 0.

The BIRD log showed the UPDATE being accepted:

as0_test: BGP session established
as0_test: Got UPDATE
as0_test.ipv4 > added [best] 192.0.2.0/24 ...

Expected Behavior

BIRD should detect AS 0 in AS_PATH and treat the UPDATE as malformed.

Since the UPDATE contains reachable NLRI and the NLRI is parseable, the
expected behavior is:

   - do not install the route,
   - keep the BGP session established,
   - handle the route using treat-as-withdraw, consistent with RFC 7606
   error handling for malformed AS_PATH attributes.

Source-Level Analysis

The AS_PATH decoder validates AS_PATH structure but does not appear to
validate individual AS numbers against the reserved AS 0 value.

In proto/bgp/attrs.c, bgp_decode_as_path() calls as_path_valid() and then
stores the attribute:

if (!as_path_valid(data, len, as_length, as_sets, as_confed, err, sizeof(err)))
  WITHDRAW("Malformed AS_PATH attribute - %s", err);

...

bgp_set_attr_data(to, s->pool, BA_AS_PATH, flags, data, len);

The later post-decode checks in bgp_decode_attrs() cover mandatory
attribute presence, local-AS loop detection, confederation loop detection,
ORIGINATOR_ID loop detection, and CLUSTER_LIST loop detection, but I could
not find a check for AS 0:

if (bgp_as_path_loopy(p, attrs, p->local_as))
  goto loop;

if ((p->public_as != p->local_as) && bgp_as_path_loopy(p, attrs, p->public_as))
  goto loop;

In nest/a-path.c, as_path_valid() checks segment framing, segment type,
whether AS_SET / AS_CONFED_* are allowed, and zero-length segments:

switch (type)
{
case AS_PATH_SET:
  if (!sets)
    BAD("AS_SET segment", type);
  break;

case AS_PATH_SEQUENCE:
  break;

case AS_PATH_CONFED_SEQUENCE:
  if (!confed)
    BAD("AS_CONFED_SEQUENCE segment", type);
  break;

case AS_PATH_CONFED_SET:
  if (!sets || !confed)
    BAD("AS_CONFED_SET segment", type);
  break;

default:
  BAD("unknown segment", type);
}

if (pos[1] == 0)
  BAD("zero-length segment", type);

I did not find an equivalent validation pass that iterates through the AS
numbers in the path and rejects asn == 0.
Suggested Fix

Add AS 0 validation during AS_PATH parsing or immediately after AS_PATH
decoding.

Conceptually:

/* RFC 7607: AS 0 is reserved and must not appear in AS_PATH */
if (as_path_contains_as_zero(...))
  WITHDRAW("Malformed AS_PATH attribute - AS 0 is reserved");

A similar validation may also be relevant for AS4_PATH, AGGREGATOR, and
AS4_AGGREGATOR, since RFC 7607 also covers those attributes. However, this
report only describes the AS_PATH case that I reproduced dynamically.
Impact

Accepting AS 0 in AS_PATH can cause interoperability and policy issues:

   - routes accepted by BIRD may later be rejected by stricter BGP speakers,
   - AS_PATH-based filters may behave unexpectedly,
   - reserved-AS input is allowed into routing policy and best-path
   processing,
   - this appears inconsistent with RFC 7607's reserved-AS handling.

This does not require tearing down the BGP session. Treat-as-withdraw
should be sufficient and would match the usual RFC 7606 approach for
malformed AS_PATH attributes.

Please let me know if you would like a minimal reproducer or packet capture.

Best regards,
Xinzhe Liu

Reply via email to