3 # Copyright (c) 2020 by
4 # Donatas Abraitis <donatas.abraitis@gmail.com>
6 # Permission to use, copy, modify, and/or distribute this software
7 # for any purpose with or without fee is hereby granted, provided
8 # that the above copyright notice and this permission notice appear
11 # THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
12 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
14 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
15 # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
16 # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
17 # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
22 Reference: https://www.cmand.org/communityexploration
26 c1 ---- x1 ---- y1 | z1
30 1. z1 announces 192.168.255.254/32 to y2, y3.
31 2. y2 and y3 tags this prefix at ingress with appropriate
32 communities 65004:2 (y2) and 65004:3 (y3).
33 3. x1 filters all communities at the egress to c1.
34 4. Shutdown the link between y1 and y2.
35 5. y1 will generate a BGP UPDATE message regarding the next-hop change.
36 6. x1 will generate a BGP UPDATE message regarding community change.
38 To avoid sending duplicate BGP UPDATE messages we should make sure
39 we send only actual route updates. In this example, x1 will skip
40 BGP UPDATE to c1 because the actual route is the same
41 (filtered communities - nothing changes).
50 CWD
= os
.path
.dirname(os
.path
.realpath(__file__
))
51 sys
.path
.append(os
.path
.join(CWD
, "../"))
53 # pylint: disable=C0413
54 from lib
import topotest
55 from lib
.topogen
import Topogen
, TopoRouter
, get_topogen
57 from lib
.common_config
import step
58 from time
import sleep
60 pytestmark
= [pytest
.mark
.bgpd
]
72 switch
= tgen
.add_switch("s1")
73 switch
.add_link(tgen
.gears
["c1"])
74 switch
.add_link(tgen
.gears
["x1"])
77 switch
= tgen
.add_switch("s2")
78 switch
.add_link(tgen
.gears
["x1"])
79 switch
.add_link(tgen
.gears
["y1"])
82 switch
= tgen
.add_switch("s3")
83 switch
.add_link(tgen
.gears
["y1"])
84 switch
.add_link(tgen
.gears
["y2"])
87 switch
= tgen
.add_switch("s4")
88 switch
.add_link(tgen
.gears
["y1"])
89 switch
.add_link(tgen
.gears
["y3"])
92 switch
= tgen
.add_switch("s5")
93 switch
.add_link(tgen
.gears
["y2"])
94 switch
.add_link(tgen
.gears
["y3"])
97 switch
= tgen
.add_switch("s6")
98 switch
.add_link(tgen
.gears
["y2"])
99 switch
.add_link(tgen
.gears
["z1"])
102 switch
= tgen
.add_switch("s7")
103 switch
.add_link(tgen
.gears
["y3"])
104 switch
.add_link(tgen
.gears
["z1"])
107 def setup_module(mod
):
108 tgen
= Topogen(build_topo
, mod
.__name
__)
109 tgen
.start_topology()
111 router_list
= tgen
.routers()
113 for i
, (rname
, router
) in enumerate(router_list
.items(), 1):
115 TopoRouter
.RD_ZEBRA
, os
.path
.join(CWD
, "{}/zebra.conf".format(rname
))
118 TopoRouter
.RD_BGP
, os
.path
.join(CWD
, "{}/bgpd.conf".format(rname
))
124 def teardown_module(mod
):
129 def test_bgp_community_update_path_change():
132 if tgen
.routers_have_failure():
133 pytest
.skip(tgen
.errors
)
135 def _bgp_converge_initial():
137 tgen
.gears
["c1"].vtysh_cmd("show ip bgp neighbor 10.0.1.2 json")
141 "bgpState": "Established",
142 "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 8}},
145 return topotest
.json_cmp(output
, expected
)
147 step("Check if an initial topology is converged")
148 test_func
= functools
.partial(_bgp_converge_initial
)
149 success
, result
= topotest
.run_and_expect(test_func
, None, count
=60, wait
=0.5)
150 assert result
is None, "Failed to see bgp convergence in c1"
152 step("Disable link between y1 and y2")
153 tgen
.gears
["y1"].run("ip link set dev y1-eth1 down")
155 def _bgp_converge_link_disabled():
156 output
= json
.loads(tgen
.gears
["y1"].vtysh_cmd("show ip bgp nei 10.0.3.2 json"))
157 expected
= {"10.0.3.2": {"bgpState": "Active"}}
158 return topotest
.json_cmp(output
, expected
)
160 step("Check if a topology is converged after a link down between y1 and y2")
161 test_func
= functools
.partial(_bgp_converge_link_disabled
)
162 success
, result
= topotest
.run_and_expect(test_func
, None, count
=60, wait
=0.5)
163 assert result
is None, "Failed to see bgp convergence in y1"
165 def _bgp_check_for_duplicate_updates():
171 tgen
.gears
["c1"].run(
172 'grep "10.0.1.2(x1) rcvd 192.168.255.254/32 IPv4 unicast...duplicate ignored" bgpd.log'
182 step("Check if we see duplicate BGP UPDATE message in c1 (suppress-duplicates)")
184 _bgp_check_for_duplicate_updates() == False
185 ), "Seen duplicate BGP UPDATE message in c1 from x1"
187 step("Disable bgp suppress-duplicates at x1")
188 tgen
.gears
["x1"].run(
189 "vtysh -c 'conf' -c 'router bgp' -c 'no bgp suppress-duplicates'"
192 step("Enable link between y1 and y2")
193 tgen
.gears
["y1"].run("ip link set dev y1-eth1 up")
195 def _bgp_converge_link_enabled():
196 output
= json
.loads(tgen
.gears
["y1"].vtysh_cmd("show ip bgp nei 10.0.3.2 json"))
199 "bgpState": "Established",
200 "addressFamilyInfo": {
201 "ipv4Unicast": {"acceptedPrefixCounter": 5, "sentPrefixCounter": 4}
205 return topotest
.json_cmp(output
, expected
)
207 step("Check if a topology is converged after a link up between y1 and y2")
208 test_func
= functools
.partial(_bgp_converge_link_enabled
)
209 success
, result
= topotest
.run_and_expect(test_func
, None, count
=60, wait
=0.5)
210 assert result
is None, "Failed to see bgp convergence in y1"
213 "Check if we see duplicate BGP UPDATE message in c1 (no bgp suppress-duplicates)"
216 _bgp_check_for_duplicate_updates() == True
217 ), "Didn't see duplicate BGP UPDATE message in c1 from x1"
220 if __name__
== "__main__":
221 args
= ["-s"] + sys
.argv
[1:]
222 sys
.exit(pytest
.main(args
))