github.com/osrg/gobgp/v3@v3.30.0/test/scenario_test/bgp_router_test.py (about) 1 # Copyright (C) 2015 Nippon Telegraph and Telephone Corporation. 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 # implied. 13 # See the License for the specific language governing permissions and 14 # limitations under the License. 15 16 17 import json 18 import sys 19 import time 20 import unittest 21 22 import collections 23 collections.Callable = collections.abc.Callable 24 25 import nose 26 27 from lib.noseplugin import OptionParser, parser_option 28 29 from lib import base 30 from lib.base import ( 31 BGP_FSM_IDLE, 32 BGP_FSM_ACTIVE, 33 BGP_FSM_ESTABLISHED, 34 BGP_ATTR_TYPE_MULTI_EXIT_DISC, 35 BGP_ATTR_TYPE_LOCAL_PREF, 36 wait_for_completion, 37 assert_several_times, 38 local, 39 ) 40 from lib.gobgp import ( 41 GoBGPContainer, 42 extract_path_attribute, 43 ) 44 from lib.quagga import QuaggaBGPContainer 45 from lib.exabgp import ExaBGPContainer 46 47 48 class GoBGPTestBase(unittest.TestCase): 49 50 @classmethod 51 def setUpClass(cls): 52 gobgp_ctn_image_name = parser_option.gobgp_image 53 base.TEST_PREFIX = parser_option.test_prefix 54 55 g1 = GoBGPContainer(name='g1', asn=65000, router_id='192.168.0.1', 56 ctn_image_name=gobgp_ctn_image_name, 57 log_level=parser_option.gobgp_log_level) 58 q1 = QuaggaBGPContainer(name='q1', asn=65001, router_id='192.168.0.2') 59 q2 = QuaggaBGPContainer(name='q2', asn=65002, router_id='192.168.0.3') 60 q3 = QuaggaBGPContainer(name='q3', asn=65003, router_id='192.168.0.4') 61 62 qs = [q1, q2, q3] 63 ctns = [g1, q1, q2, q3] 64 65 initial_wait_time = max(ctn.run() for ctn in ctns) 66 time.sleep(initial_wait_time) 67 68 for q in qs: 69 g1.add_peer(q, passwd='passwd') 70 q.add_peer(g1, passwd='passwd', passive=True) 71 72 # advertise a route from q1, q2, q3 73 for idx, q in enumerate(qs): 74 route = '10.0.{0}.0/24'.format(idx + 1) 75 q.add_route(route) 76 77 cls.gobgp = g1 78 cls.quaggas = {'q1': q1, 'q2': q2, 'q3': q3} 79 80 # test each neighbor state is turned establish 81 def test_01_neighbor_established(self): 82 for q in self.quaggas.values(): 83 self.gobgp.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q) 84 85 def test_02_check_gobgp_global_rib(self): 86 for q in self.quaggas.values(): 87 # paths expected to exist in gobgp's global rib 88 routes = list(q.routes.keys()) 89 timeout = 120 90 interval = 1 91 count = 0 92 93 while True: 94 # gobgp's global rib 95 state = self.gobgp.get_neighbor_state(q) 96 self.assertEqual(state, BGP_FSM_ESTABLISHED) 97 global_rib = [p['prefix'] for p in self.gobgp.get_global_rib()] 98 99 for p in global_rib: 100 if p in routes: 101 routes.remove(p) 102 103 if len(routes) == 0: 104 break 105 106 time.sleep(interval) 107 count += interval 108 if count >= timeout: 109 raise Exception('timeout') 110 111 # check gobgp properly add it's own asn to aspath 112 def test_03_check_gobgp_adj_out_rib(self): 113 for q in self.quaggas.values(): 114 for path in self.gobgp.get_adj_rib_out(q): 115 asns = path['aspath'] 116 self.assertTrue(self.gobgp.asn in asns) 117 118 # check routes are properly advertised to all BGP speaker 119 def test_04_check_quagga_global_rib(self): 120 interval = 1 121 timeout = int(120 / interval) 122 for q in self.quaggas.values(): 123 done = False 124 for _ in range(timeout): 125 if done: 126 break 127 global_rib = q.get_global_rib() 128 global_rib = [p['prefix'] for p in global_rib] 129 if len(global_rib) < len(self.quaggas): 130 time.sleep(interval) 131 continue 132 133 self.assertEqual(len(global_rib), len(self.quaggas)) 134 135 for c in self.quaggas.values(): 136 for r in c.routes: 137 self.assertTrue(r in global_rib) 138 done = True 139 if done: 140 continue 141 # should not reach here 142 raise AssertionError 143 144 def test_05_add_quagga(self): 145 q4 = QuaggaBGPContainer(name='q4', asn=65004, router_id='192.168.0.5') 146 self.quaggas['q4'] = q4 147 initial_wait_time = q4.run() 148 time.sleep(initial_wait_time) 149 150 self.gobgp.add_peer(q4) 151 q4.add_peer(self.gobgp) 152 153 q4.add_route('10.0.4.0/24') 154 155 self.gobgp.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q4) 156 157 def test_06_check_global_rib(self): 158 self.test_02_check_gobgp_global_rib() 159 self.test_04_check_quagga_global_rib() 160 161 def test_07_stop_one_quagga(self): 162 g1 = self.gobgp 163 q4 = self.quaggas['q4'] 164 q4.stop() 165 self.gobgp.wait_for(expected_state=BGP_FSM_ACTIVE, peer=q4) 166 167 g1.del_peer(q4) 168 del self.quaggas['q4'] 169 170 # check gobgp properly send withdrawal message with q4's route 171 def test_08_check_global_rib(self): 172 self.test_02_check_gobgp_global_rib() 173 self.test_04_check_quagga_global_rib() 174 175 def test_09_add_distant_relative(self): 176 q1 = self.quaggas['q1'] 177 q2 = self.quaggas['q2'] 178 q3 = self.quaggas['q3'] 179 q5 = QuaggaBGPContainer(name='q5', asn=65005, router_id='192.168.0.6') 180 181 initial_wait_time = q5.run() 182 time.sleep(initial_wait_time) 183 184 for q in [q2, q3]: 185 q5.add_peer(q) 186 q.add_peer(q5) 187 188 med200 = {'name': 'med200', 189 'type': 'permit', 190 'match': '0.0.0.0/0', 191 'med': 200} 192 q2.add_policy(med200, self.gobgp, 'out') 193 med100 = {'name': 'med100', 194 'type': 'permit', 195 'match': '0.0.0.0/0', 196 'med': 100} 197 q3.add_policy(med100, self.gobgp, 'out') 198 199 q5.add_route('10.0.6.0/24') 200 201 self.gobgp.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q2) 202 self.gobgp.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q3) 203 q2.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q5) 204 q3.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q5) 205 206 timeout = 120 207 interval = 1 208 count = 0 209 while True: 210 paths = self.gobgp.get_adj_rib_out(q1, '10.0.6.0/24') 211 if len(paths) > 0: 212 path = paths[0] 213 print("{0}'s nexthop is {1}".format(path['nlri']['prefix'], 214 path['nexthop'])) 215 n_addrs = [i[1].split('/')[0] for i in self.gobgp.ip_addrs] 216 if path['nexthop'] in n_addrs: 217 break 218 219 time.sleep(interval) 220 count += interval 221 if count >= timeout: 222 raise Exception('timeout') 223 224 def test_10_originate_path(self): 225 self.gobgp.add_route('10.10.0.0/24') 226 dst = self.gobgp.get_global_rib('10.10.0.0/24') 227 self.assertEqual(len(dst), 1) 228 self.assertEqual(len(dst[0]['paths']), 1) 229 path = dst[0]['paths'][0] 230 self.assertEqual(path['nexthop'], '0.0.0.0') 231 self.assertEqual(len(path['aspath']), 0) 232 233 def test_11_check_adj_rib_out(self): 234 for q in self.quaggas.values(): 235 paths = self.gobgp.get_adj_rib_out(q, '10.10.0.0/24') 236 self.assertEqual(len(paths), 1) 237 path = paths[0] 238 peer_info = self.gobgp.peers[q] 239 local_addr = peer_info['local_addr'].split('/')[0] 240 self.assertEqual(path['nexthop'], local_addr) 241 self.assertEqual(path['aspath'], [self.gobgp.asn]) 242 243 def test_12_disable_peer(self): 244 q1 = self.quaggas['q1'] 245 self.gobgp.disable_peer(q1) 246 self.gobgp.wait_for(expected_state=BGP_FSM_IDLE, peer=q1) 247 248 time.sleep(3) 249 250 for route in q1.routes.keys(): 251 dst = self.gobgp.get_global_rib(route) 252 self.assertEqual(len(dst), 0) 253 254 for q in self.quaggas.values(): 255 if q is q1: 256 continue 257 paths = self.gobgp.get_adj_rib_out(q, route) 258 self.assertEqual(len(paths), 0) 259 260 def test_13_enable_peer(self): 261 q1 = self.quaggas['q1'] 262 self.gobgp.enable_peer(q1) 263 self.gobgp.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q1) 264 265 def test_14_check_adj_rib_out(self): 266 self.test_11_check_adj_rib_out() 267 268 def test_15_check_active_connection(self): 269 g1 = self.gobgp 270 g2 = GoBGPContainer(name='g2', asn=65000, router_id='192.168.0.7', 271 ctn_image_name=self.gobgp.image, 272 log_level=parser_option.gobgp_log_level) 273 time.sleep(g2.run()) 274 self.quaggas['g2'] = g2 275 g2.add_peer(g1, passive=True) 276 g1.add_peer(g2) 277 g1.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=g2) 278 279 def test_16_check_local_pref_and_med_handling(self): 280 g1 = self.gobgp 281 g1.add_route('10.20.0.0/24', local_pref=1000, med=2000) 282 # iBGP peer 283 g2 = self.quaggas['g2'] 284 paths = g2.get_global_rib('10.20.0.0/24') 285 self.assertEqual(len(paths), 1) 286 self.assertEqual(len(paths[0]['paths']), 1) 287 path = paths[0]['paths'][0] 288 local_pref = extract_path_attribute(path, BGP_ATTR_TYPE_LOCAL_PREF) 289 self.assertEqual(local_pref['value'], 1000) 290 med = extract_path_attribute(path, BGP_ATTR_TYPE_MULTI_EXIT_DISC) 291 self.assertEqual(med['metric'], 2000) 292 293 # eBGP peer 294 q1 = self.quaggas['q1'] 295 paths = q1.get_global_rib('10.20.0.0/24') 296 self.assertEqual(len(paths), 1) 297 path = paths[0] 298 local_pref = extract_path_attribute(path, BGP_ATTR_TYPE_LOCAL_PREF) 299 # local_pref's default value is 100 300 self.assertEqual(local_pref['value'], 100) 301 med = extract_path_attribute(path, BGP_ATTR_TYPE_MULTI_EXIT_DISC) 302 self.assertEqual(med['metric'], 2000) 303 304 def test_17_check_shutdown(self): 305 g1 = self.gobgp 306 q1 = self.quaggas['q1'] 307 q2 = self.quaggas['q2'] 308 q3 = self.quaggas['q3'] 309 310 q2.add_route('20.0.0.0/24') 311 q3.add_route('20.0.0.0/24') 312 313 self.test_01_neighbor_established() 314 315 self.test_02_check_gobgp_global_rib() 316 317 paths = q1.get_global_rib('20.0.0.0/24') 318 self.assertEqual(len(paths), 1) 319 n_addrs = [i[1].split('/')[0] for i in self.gobgp.ip_addrs] 320 self.assertIn(paths[0]['nexthop'], n_addrs) 321 322 q3.stop() 323 324 self.gobgp.wait_for(expected_state=BGP_FSM_ACTIVE, peer=q3) 325 326 def f(): 327 paths = q1.get_global_rib('20.0.0.0/24') 328 self.assertEqual(len(paths), 1) 329 self.assertIn(paths[0]['nexthop'], n_addrs) 330 331 assert_several_times(f) 332 333 g1.del_peer(q3) 334 del self.quaggas['q3'] 335 336 def test_18_check_withdrawal(self): 337 g1 = self.gobgp 338 q1 = self.quaggas['q1'] 339 q2 = self.quaggas['q2'] 340 341 g1.add_route('30.0.0.0/24') 342 q1.add_route('30.0.0.0/24') 343 344 self.test_01_neighbor_established() 345 346 self.test_02_check_gobgp_global_rib() 347 348 paths = g1.get_adj_rib_out(q1, '30.0.0.0/24') 349 self.assertEqual(len(paths), 1) 350 self.assertNotIn('source-id', paths[0]) 351 paths = g1.get_adj_rib_out(q2, '30.0.0.0/24') 352 self.assertEqual(len(paths), 1) 353 self.assertNotIn('source-id', paths[0]) 354 355 g1.local('gobgp global rib del 30.0.0.0/24') 356 357 def f(): 358 paths = g1.get_adj_rib_out(q1, '30.0.0.0/24') 359 self.assertEqual(len(paths), 0) 360 paths = g1.get_adj_rib_out(q2, '30.0.0.0/24') 361 self.assertEqual(len(paths), 1) 362 self.assertEqual(paths[0]['source-id'], '192.168.0.2') 363 364 assert_several_times(f) 365 366 def test_19_check_grpc_add_neighbor(self): 367 g1 = self.gobgp 368 e1 = ExaBGPContainer(name='e1', asn=65000, router_id='192.168.0.7') 369 time.sleep(e1.run()) 370 e1.add_peer(g1) 371 self.quaggas['e1'] = e1 372 n = e1.peers[g1]['local_addr'].split('/')[0] 373 g1.local('gobgp n add {0} as 65000'.format(n)) 374 g1.add_peer(e1, reload_config=False) 375 376 g1.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=e1) 377 378 def test_20_check_grpc_del_neighbor(self): 379 g1 = self.gobgp 380 e1 = self.quaggas['e1'] 381 n = e1.peers[g1]['local_addr'].split('/')[0] 382 g1.local('gobgp n del {0}'.format(n)) 383 g1.del_peer(e1, reload_config=False) 384 385 def test_21_check_withdrawal_2(self): 386 g1 = self.gobgp 387 g2 = self.quaggas['g2'] 388 389 prefix = '40.10.0.0/24' 390 g1.add_route(prefix) 391 wait_for_completion(lambda: len(g1.get_global_rib(prefix)) == 1) 392 wait_for_completion(lambda: len(g2.get_global_rib(prefix)) == 1) 393 394 r = g2.local('gobgp monitor global rib -j', stream=True, tty=False) 395 396 g1.local('gobgp global rib del 40.10.0.0/24') 397 del g1.routes[prefix] 398 399 wait_for_completion(lambda: len(g1.get_global_rib(prefix)) == 0) 400 wait_for_completion(lambda: len(g2.get_global_rib(prefix)) == 0) 401 402 ret = json.loads(next(r)) 403 self.assertEqual(ret[0]['nlri']['prefix'], prefix) 404 self.assertTrue('withdrawal' in ret[0]) 405 406 def test_22_check_cli_sorted(self): 407 g1 = self.gobgp 408 cnt = 0 409 410 def next_prefix(): 411 for i in range(100, 105): 412 for j in range(100, 105): 413 yield '{0}.{1}.0.0/24'.format(i, j) 414 415 for p in next_prefix(): 416 g1.local('gobgp global rib add {0}'.format(p)) 417 cnt += 1 418 419 cnt2 = 0 420 g = next_prefix() 421 n = next(g) 422 for path in g1.local("gobgp global rib", capture=True).split('\n')[1:]: 423 if [elem for elem in path.split(' ') if elem != ''][1] == n: 424 try: 425 cnt2 += 1 426 n = next(g) 427 except StopIteration: 428 break 429 430 self.assertEqual(cnt, cnt2) 431 432 def test_23_check_withdrawal3(self): 433 gobgp_ctn_image_name = parser_option.gobgp_image 434 g1 = self.gobgp 435 g3 = GoBGPContainer(name='g3', asn=65006, router_id='192.168.0.8', 436 ctn_image_name=gobgp_ctn_image_name, 437 log_level=parser_option.gobgp_log_level) 438 g4 = GoBGPContainer(name='g4', asn=65007, router_id='192.168.0.9', 439 ctn_image_name=gobgp_ctn_image_name, 440 log_level=parser_option.gobgp_log_level) 441 442 initial_wait_time = max(ctn.run() for ctn in [g3, g4]) 443 time.sleep(initial_wait_time) 444 445 self.quaggas = {'g3': g3, 'g4': g4} 446 447 g3.local('gobgp global rib add 50.0.0.0/24') 448 449 g1.add_peer(g3, passive=True) 450 g3.add_peer(g1) 451 g1.add_peer(g4, passive=True) 452 g4.add_peer(g1) 453 454 self.test_01_neighbor_established() 455 456 self.test_02_check_gobgp_global_rib() 457 458 g4.local('gobgp global rib add 50.0.0.0/24 med 10') 459 460 paths = g1.get_adj_rib_out(g3, '50.0.0.0/24') 461 self.assertEqual(len(paths), 0) 462 paths = g1.get_adj_rib_out(g4, '50.0.0.0/24') 463 self.assertEqual(len(paths), 1) 464 self.assertEqual(paths[0]['source-id'], '192.168.0.8') 465 466 g3.local('gobgp global rib del 50.0.0.0/24') 467 468 paths = g1.get_adj_rib_out(g3, '50.0.0.0/24') 469 self.assertEqual(len(paths), 1) 470 self.assertEqual(paths[0]['source-id'], '192.168.0.9') 471 paths = g1.get_adj_rib_out(g4, '50.0.0.0/24') 472 self.assertEqual(len(paths), 0) 473 474 def test_24_add_quagga_md5(self): 475 q6 = QuaggaBGPContainer(name='q6', asn=65004, router_id='192.168.0.5') 476 self.quaggas['q6'] = q6 477 initial_wait_time = q6.run() 478 time.sleep(initial_wait_time) 479 480 self.gobgp.add_peer(q6, passwd='password') 481 q6.add_peer(self.gobgp, passwd='password') 482 483 self.gobgp.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q6) 484 485 def test_25_add_quagga_md5_badpass(self): 486 q7 = QuaggaBGPContainer(name='q6', asn=65004, router_id='192.168.0.7') 487 self.quaggas['q7'] = q7 488 initial_wait_time = q7.run() 489 time.sleep(initial_wait_time) 490 491 self.gobgp.add_peer(q7, passwd='password') 492 q7.add_peer(self.gobgp, passwd='passwordBAD') 493 494 time.sleep(10) 495 state = self.gobgp.get_neighbor_state(q7) 496 # should not establish 497 self.assertEqual(state, BGP_FSM_ACTIVE) 498 499 def test_26_dynamic_peer(self): 500 gobgp_ctn_image_name = parser_option.gobgp_image 501 g5 = GoBGPContainer(name='g5', asn=65008, router_id='192.168.0.8', 502 ctn_image_name=gobgp_ctn_image_name, 503 log_level=parser_option.gobgp_log_level, 504 bgp_config={ 505 'peer-groups': [ 506 { 507 'config':{ 508 'peer-group-name':'PG1', 509 'peer-as': 65004, 510 } 511 } 512 ], 513 'dynamic-neighbors':[ 514 { 515 'config': { 516 'prefix':'172.17.0.0/16', 517 'peer-group':'PG1' 518 } 519 } 520 521 ] 522 }) 523 q8 = QuaggaBGPContainer(name='q8', asn=65004, router_id='192.168.0.5') 524 self.quaggas = { 'g8': q8 } 525 526 time.sleep(g5.run()) 527 initial_wait_time = q8.run() 528 time.sleep(initial_wait_time) 529 530 q8.add_peer(g5) 531 532 time.sleep(initial_wait_time) 533 534 # should establish via the dynamic link 535 # note that we are checking it on the Quagga side since GoBGP 536 # config doesn't explicitly "know about" the peer 537 q8.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=g5) 538 539 def test_27_dynamic_peer_md5(self): 540 gobgp_ctn_image_name = parser_option.gobgp_image 541 g6 = GoBGPContainer(name='g6', asn=65008, router_id='192.168.0.9', 542 ctn_image_name=gobgp_ctn_image_name, 543 log_level=parser_option.gobgp_log_level, 544 bgp_config={ 545 'peer-groups': [ 546 { 547 'config':{ 548 'peer-group-name':'PG1', 549 'peer-as': 65004, 550 'auth-password': 'password', 551 } 552 } 553 ], 554 'dynamic-neighbors':[ 555 { 556 'config': { 557 'prefix':'172.17.0.0/16', 558 'peer-group':'PG1' 559 } 560 } 561 562 ] 563 }) 564 q9 = QuaggaBGPContainer(name='q9', asn=65004, router_id='192.168.0.10') 565 self.quaggas = { 'g9': q9 } 566 567 time.sleep(g6.run()) 568 initial_wait_time = q9.run() 569 time.sleep(initial_wait_time) 570 571 q9.add_peer(g6, passwd='password', passive=False) 572 573 # should establish via the dynamic link 574 # note that we are checking it on the Quagga side since GoBGP 575 # config doesn't explicitly "know about" the peer 576 q9.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=g6, timeout=30) 577 578 579 if __name__ == '__main__': 580 output = local("which docker 2>&1 > /dev/null ; echo $?", capture=True) 581 if int(output) != 0: 582 print("docker not found") 583 sys.exit(1) 584 585 nose.main(argv=sys.argv, addplugins=[OptionParser()], 586 defaultTest=sys.argv[0])