diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 8e8616c1557c..a963c9bd4271 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1674,11 +1674,23 @@ void bgp_zebra_announce_table(struct bgp *bgp, afi_t afi, safi_t safi) for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED) && - (pi->type == ZEBRA_ROUTE_BGP - && (pi->sub_type == BGP_ROUTE_NORMAL - || pi->sub_type == BGP_ROUTE_IMPORTED))) - bgp_zebra_route_install(dest, pi, bgp, true, - NULL, false); + (pi->type == ZEBRA_ROUTE_BGP && (pi->sub_type == BGP_ROUTE_NORMAL || + pi->sub_type == BGP_ROUTE_IMPORTED))) { + bool is_add = true; + + if (bgp->table_map[afi][safi].name) { + struct attr local_attr = *pi->attr; + struct bgp_path_info local_info = *pi; + + local_info.attr = &local_attr; + + is_add = bgp_table_map_apply(bgp->table_map[afi][safi].map, + bgp_dest_get_prefix(dest), + &local_info); + } + + bgp_zebra_route_install(dest, pi, bgp, is_add, NULL, false); + } } /* Announce routes of any bgp subtype of a table to zebra */ diff --git a/tests/topotests/bgp_table_map/r1/frr.conf b/tests/topotests/bgp_table_map/r1/frr.conf new file mode 100644 index 000000000000..f74440c38423 --- /dev/null +++ b/tests/topotests/bgp_table_map/r1/frr.conf @@ -0,0 +1,22 @@ +! +int r1-eth0 + ip address 10.255.0.1/24 +! +access-list AccList seq 5 permit 10.0.0.1/32 +! +route-map TableMap permit 10 + match ip address AccList +exit +! +router bgp 65001 + bgp router-id 10.255.0.1 + no bgp ebgp-requires-policy + neighbor 10.255.0.2 remote-as external + neighbor 10.255.0.2 timers 1 3 + neighbor 10.255.0.2 timers connect 1 + ! + address-family ipv4 unicast + table-map TableMap + exit-address-family +exit +! diff --git a/tests/topotests/bgp_table_map/r2/frr.conf b/tests/topotests/bgp_table_map/r2/frr.conf new file mode 100644 index 000000000000..4523fe49ea2a --- /dev/null +++ b/tests/topotests/bgp_table_map/r2/frr.conf @@ -0,0 +1,18 @@ +! +int r2-eth0 + ip address 10.255.0.2/24 +! +router bgp 65002 + bgp router-id 10.255.0.2 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.255.0.1 remote-as external + neighbor 10.255.0.1 timers 1 3 + neighbor 10.255.0.1 timers connect 1 + ! + address-family ipv4 unicast + network 10.0.0.1/32 + network 10.0.0.2/32 + exit-address-family +exit +! diff --git a/tests/topotests/bgp_table_map/test_bgp_table_map.py b/tests/topotests/bgp_table_map/test_bgp_table_map.py new file mode 100644 index 000000000000..b10680f741ab --- /dev/null +++ b/tests/topotests/bgp_table_map/test_bgp_table_map.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +import functools, json, os, pytest, re, sys + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +from lib import topotest +from lib.topogen import Topogen, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for _, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config( + os.path.join(CWD, "{}/frr.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_table_map(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + def _bgp_converge(): + output = json.loads( + r1.vtysh_cmd( "show bgp ipv4 unicast summary json") + ) + expected = { + "peers": { + "10.255.0.2": { + "remoteAs": 65002, + "state": "Established", + "peerState": "OK", + }, + }, + "totalPeers": 1, + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_converge, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Can't converge initial state" + + def _bgp_with_table_map(): + output = json.loads(r1.vtysh_cmd("show ip fib json")) + expected = { + "10.0.0.1/32": [], + "10.0.0.2/32": None, + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_with_table_map, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + assert result is None, "Should contain only one of two shared networks" + + # + # Unset table-map + # + r1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + address-family ipv4 unicast + no table-map TableMap + """ + ) + + def _bgp_without_table_map(): + output = json.loads(r1.vtysh_cmd("show ip fib json")) + expected = { + "10.0.0.1/32": [], + "10.0.0.2/32": [], + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_without_table_map, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + assert result is None, "Shouldn't contain both shared routes" + + # + # Reset table-map + # + r1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + address-family ipv4 unicast + table-map TableMap + """ + ) + + test_func = functools.partial( + _bgp_with_table_map, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + assert result is None, "Should contain only one of two shared networks" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args))