I’ve found a glitch in ROS BGP that leads to a mismatch in the state of two BGP peers, and posting this for community awareness.
Test Bed:
Router OS version 6.34 on CHR platform running on Virtualbox in GNS3.
The Glitch:
In some cases, a route can change states such that it is no longer accepted by a route filter.
When this happens, the originating router removes the prefix from its list of advertised routes, but fails to actually send a route withdrawal update message. Afterwards, no soft-updates (refresh) can remove the route from the peer’s routing table. The route should eventually expire when the TTL is reached, but only a complete stop/start of the peering session, or re-asserting the route and subsequently blocking it with a different filtering rule will clear the bugged route from the remote peer’s table otherwise.
How to replicate this behavior:
Not all accept->discard transitions are affected. I’m not sure what the underlying difference is, but this scenario causes it.
I configured a system which uses route filters to apply community strings to locally-originated routes and then filter outbound advertisements based on this community. There are 2 filters in play: global-out and peer-out. R1 (advertising router) will match locally-originated routes and append community 1:1 [global-out], and then filter routes to peer R2 such that only routes with community 1:1 will be sent [peer-out].
Configuration of R1:
/ip route
add distance=254 dst-address=192.168.1.0/24 type=blackhole
add distance=254 dst-address=192.168.2.0/24 type=blackhole
/routing filter
add action=accept bgp-communities=1:1 chain=peer-out
add action=discard chain=peer-out
add action=accept append-bgp-communities=1:1 chain=global-out locally-originated-bgp=yes
/routing bgp instance
set default out-filter=bgp-global-out as=65530
/routing bgp network
add network=192.168.1.0/24 synchronize=yes
add network=192.168.2.0/24 synchronize=yes
/routing bgp peer
add name=R2 out-filter=peer-out remote-address=10.1.2.2 remote-as=65520 ttl=default
The configuration of R2 is trivial - it accepts all routes from R1 over an eBGP session.
If you create a third network in /routing bgp network e.g. 192.168.3.0/24, with synchronize=no, then the global-out filter will not match this prefix as a “locally-originated” route, so 192.168.3.0/24 will not get the 1:1 community, thus the peer-out filter chain will block it from being advertised to R2 (it was testing this behavior that led me to find this bug).
However, if you modify an existing prefix (192.168.2.0/24) by changing synchronize yes->no, then the router will internally remove the 1:1 community from that prefix, and will then block it in the advertisements list - but the router fails to actually send the BGP Update message withdrawing the route from R2.
[admin@R1] /routing bgp advertisements> print
PEER PREFIX NEXTHOP AS-PATH ORIGIN LOCAL-PREF
R2 192.168.1.0/24 10.1.2.1 igp
[admin@R2] /ip route> print where bgp
Flags: X - disabled, A - active, D - dynamic,
C - connect, S - static, r - rip, b - bgp, o - ospf, m - mme,
B - blackhole, U - unreachable, P - prohibit
# DST-ADDRESS PREF-SRC GATEWAY DISTANCE
0 ADb 192.168.1.0/24 10.1.2.1 20
1 ADb 192.168.2.0/24 10.1.2.1 20
A Wireshark capture shows that no withdraw update message was sent by R1.
If R2 or R1 perform a refresh, then R1 sends only the prefix 192.168.1.0/24 - R2 simply leaves the 192.168.2.0/24 prefix in its table because it did not receive any new information about that prefix.
I know this sounds like a fringe case, but it’s bad that there is a scenario where a filter begins to block a route, and the router thinks it has withdrawn the route, but in actuality it did not.
There is another issue with the route filters - changing the rule order by dragging a rule to a new position in the chain does not trigger the filter to re-evaluate the routes. Modifying an existing rule in the chain (even simply changing the comment) will cause the filter to re-evaluate the routes properly.
One last thing - if using a single filter (no global filter on the instance itself) then the behavior changes so that the BGP matcher will match locally-originated=yes, even if the network is set to synchronize=no, and therefore the bug is not applicable.