github.com/osrg/gobgp/v3@v3.30.0/test/scenario_test/route_server_malformed_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 sys 18 import time 19 import unittest 20 import inspect 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 BGP_FSM_ESTABLISHED, local 31 from lib.gobgp import GoBGPContainer 32 from lib.exabgp import ExaBGPContainer 33 34 35 counter = 1 36 _SCENARIOS = {} 37 38 39 def register_scenario(cls): 40 global counter 41 _SCENARIOS[counter] = cls 42 counter += 1 43 44 45 def lookup_scenario(name): 46 for value in list(_SCENARIOS.values()): 47 if value.__name__ == name: 48 return value 49 return None 50 51 52 def wait_for(f, timeout=120): 53 interval = 1 54 count = 0 55 while True: 56 if f(): 57 return 58 59 time.sleep(interval) 60 count += interval 61 if count >= timeout: 62 raise Exception('timeout') 63 64 65 @register_scenario 66 class MalformedMpReachNlri(object): 67 """ 68 No.1 malformaed mp-reach-nlri 69 """ 70 71 @staticmethod 72 def boot(env): 73 gobgp_ctn_image_name = env.parser_option.gobgp_image 74 log_level = env.parser_option.gobgp_log_level 75 g1 = GoBGPContainer(name='g1', asn=65000, router_id='192.168.0.1', 76 ctn_image_name=gobgp_ctn_image_name, 77 log_level=log_level) 78 e1 = ExaBGPContainer(name='e1', asn=65001, router_id='192.168.0.2') 79 e2 = ExaBGPContainer(name='e2', asn=65001, router_id='192.168.0.2') 80 81 ctns = [g1, e1, e2] 82 initial_wait_time = max(ctn.run() for ctn in ctns) 83 time.sleep(initial_wait_time) 84 85 for q in [e1, e2]: 86 g1.add_peer(q, is_rs_client=True) 87 q.add_peer(g1) 88 89 env.g1 = g1 90 env.e1 = e1 91 env.e2 = e2 92 93 @staticmethod 94 def setup(env): 95 g1 = env.g1 96 e1 = env.e1 97 e2 = env.e2 98 for c in [e1, e2]: 99 g1.wait_for(BGP_FSM_ESTABLISHED, c) 100 101 # advertise malformed MP_REACH_NLRI 102 e1.add_route('10.7.0.17/32', attribute='0x0e 0x60 0x11223344') 103 104 @staticmethod 105 def check(env): 106 g1 = env.g1 107 e1 = env.e1 108 e2 = env.e2 109 110 def f(): 111 for line in e1.log().split('\n'): 112 if 'UPDATE message error / Attribute Flags Error / 0x600E0411223344' in line: 113 return True 114 return False 115 116 wait_for(f) 117 # check e2 is still established 118 g1.wait_for(BGP_FSM_ESTABLISHED, e2) 119 120 @staticmethod 121 def executor(env): 122 lookup_scenario("MalformedMpReachNlri").boot(env) 123 lookup_scenario("MalformedMpReachNlri").setup(env) 124 lookup_scenario("MalformedMpReachNlri").check(env) 125 126 127 @register_scenario 128 class MalformedMpUnReachNlri(object): 129 """ 130 No.2 malformaed mp-unreach-nlri 131 """ 132 133 @staticmethod 134 def boot(env): 135 lookup_scenario("MalformedMpReachNlri").boot(env) 136 137 @staticmethod 138 def setup(env): 139 g1 = env.g1 140 e1 = env.e1 141 e2 = env.e2 142 for c in [e1, e2]: 143 g1.wait_for(BGP_FSM_ESTABLISHED, c) 144 145 # advertise malformed MP_UNREACH_NLRI 146 e1.add_route('10.7.0.17/32', attribute='0x0f 0x60 0x11223344') 147 148 @staticmethod 149 def check(env): 150 g1 = env.g1 151 e1 = env.e1 152 e2 = env.e2 153 154 def f(): 155 for line in e1.log().split('\n'): 156 if 'UPDATE message error / Attribute Flags Error / 0x600F0411223344' in line: 157 return True 158 return False 159 160 wait_for(f) 161 # check e2 is still established 162 g1.wait_for(BGP_FSM_ESTABLISHED, e2) 163 164 @staticmethod 165 def executor(env): 166 lookup_scenario("MalformedMpUnReachNlri").boot(env) 167 lookup_scenario("MalformedMpUnReachNlri").setup(env) 168 lookup_scenario("MalformedMpUnReachNlri").check(env) 169 170 171 @register_scenario 172 class MalformedAsPath(object): 173 """ 174 No.3 malformaed as-path 175 """ 176 177 @staticmethod 178 def boot(env): 179 lookup_scenario("MalformedMpReachNlri").boot(env) 180 181 @staticmethod 182 def setup(env): 183 g1 = env.g1 184 e1 = env.e1 185 e2 = env.e2 186 for c in [e1, e2]: 187 g1.wait_for(BGP_FSM_ESTABLISHED, c) 188 189 # advertise malformed AS_PATH 190 # Send the attribute to the length and number of aspath is inconsistent 191 # Attribute Type 0x02 (AS_PATH) 192 # Attribute Flag 0x40 (well-known transitive) 193 # Attribute Value 0x02020000ffdc ( 194 # segment type = 02 195 # segment length = 02 -> # correct value = 01 196 # as number = 65500 ) 197 e1.add_route('10.7.0.17/32', attribute='0x02 0x60 0x11223344') 198 199 @staticmethod 200 def check(env): 201 g1 = env.g1 202 e1 = env.e1 203 e2 = env.e2 204 205 def f(): 206 for line in e1.log().split('\n'): 207 if 'UPDATE message error / Attribute Flags Error / 0x60020411223344' in line: 208 return True 209 return False 210 211 wait_for(f) 212 # check e2 is still established 213 g1.wait_for(BGP_FSM_ESTABLISHED, e2) 214 215 @staticmethod 216 def executor(env): 217 lookup_scenario("MalformedAsPath").boot(env) 218 lookup_scenario("MalformedAsPath").setup(env) 219 lookup_scenario("MalformedAsPath").check(env) 220 221 222 @register_scenario 223 class MalformedAs4Path(object): 224 """ 225 No.4 malformaed as4-path 226 """ 227 228 @staticmethod 229 def boot(env): 230 lookup_scenario("MalformedMpReachNlri").boot(env) 231 232 @staticmethod 233 def setup(env): 234 g1 = env.g1 235 e1 = env.e1 236 e2 = env.e2 237 for c in [e1, e2]: 238 g1.wait_for(BGP_FSM_ESTABLISHED, c) 239 240 # advertise malformed AS4_PATH 241 e1.add_route('10.7.0.17/32', attribute='0x11 0x60 0x11223344') 242 243 @staticmethod 244 def check(env): 245 g1 = env.g1 246 e1 = env.e1 247 e2 = env.e2 248 249 def f(): 250 for line in e1.log().split('\n'): 251 if 'UPDATE message error / Attribute Flags Error / 0x60110411223344' in line: 252 return True 253 return False 254 255 wait_for(f) 256 # check e2 is still established 257 g1.wait_for(BGP_FSM_ESTABLISHED, e2) 258 259 @staticmethod 260 def executor(env): 261 lookup_scenario("MalformedAs4Path").boot(env) 262 lookup_scenario("MalformedAs4Path").setup(env) 263 lookup_scenario("MalformedAs4Path").check(env) 264 265 266 @register_scenario 267 class MalformedNexthop(object): 268 """ 269 No.5 malformaed nexthop 270 """ 271 272 @staticmethod 273 def boot(env): 274 lookup_scenario("MalformedMpReachNlri").boot(env) 275 276 @staticmethod 277 def setup(env): 278 g1 = env.g1 279 e1 = env.e1 280 e2 = env.e2 281 for c in [e1, e2]: 282 g1.wait_for(BGP_FSM_ESTABLISHED, c) 283 284 # advertise malformed NEXT_HOP 285 # 0x0e: MP_REACH_NLRI 286 # 0x60: Optional, Transitive 287 # 0x01: AFI(IPv4) 288 # 0x01: SAFI(unicast) 289 # 0x10: Length of Next Hop Address 290 # 0xffffff00: Network address of Next Hop 291 # 0x00: Reserved 292 e1.add_route('10.7.0.17/32', attribute='0x0e 0x60 0x010110ffffff0000') 293 294 @staticmethod 295 def check(env): 296 g1 = env.g1 297 e1 = env.e1 298 e2 = env.e2 299 300 def f(): 301 for line in e1.log().split('\n'): 302 if 'UPDATE message error / Attribute Flags Error / 0x600E08010110FFFFFF0000' in line: 303 return True 304 return False 305 306 wait_for(f) 307 # check e2 is still established 308 g1.wait_for(BGP_FSM_ESTABLISHED, e2) 309 310 @staticmethod 311 def executor(env): 312 lookup_scenario("MalformedNexthop").boot(env) 313 lookup_scenario("MalformedNexthop").setup(env) 314 lookup_scenario("MalformedNexthop").check(env) 315 316 317 @register_scenario 318 class MalformedRouteFamily(object): 319 """ 320 No.6 malformaed route family 321 """ 322 323 @staticmethod 324 def boot(env): 325 lookup_scenario("MalformedMpReachNlri").boot(env) 326 327 @staticmethod 328 def setup(env): 329 g1 = env.g1 330 e1 = env.e1 331 e2 = env.e2 332 for c in [e1, e2]: 333 g1.wait_for(BGP_FSM_ESTABLISHED, c) 334 335 # advertise malformed ROUTE_FAMILY 336 # 0x0e: MP_REACH_NLRI 337 # 0x60: Optional, Transitive 338 # 0x01: AFI(IPv4) 339 # 0x01: SAFI(unicast) 340 # 0x10: Length of Next Hop Address 341 # 0xffffff00: Network address of Next Hop 342 # 0x00: Reserved 343 e1.add_route('10.7.0.17/32', attribute='0x0e 0x60 0x0002011020010db800000000000000000000000100') 344 345 @staticmethod 346 def check(env): 347 g1 = env.g1 348 e1 = env.e1 349 e2 = env.e2 350 351 def f(): 352 for line in e1.log().split('\n'): 353 if 'UPDATE message error / Attribute Flags Error / 0x600E150002011020010DB800000000000000000000000100' in line: 354 return True 355 return False 356 357 wait_for(f) 358 # check e2 is still established 359 g1.wait_for(BGP_FSM_ESTABLISHED, e2) 360 361 @staticmethod 362 def executor(env): 363 lookup_scenario("MalformedRouteFamily").boot(env) 364 lookup_scenario("MalformedRouteFamily").setup(env) 365 lookup_scenario("MalformedRouteFamily").check(env) 366 367 368 @register_scenario 369 class MalformedAsPathSegmentLengthInvalid(object): 370 """ 371 No.7 malformaed aspath segment length invalid 372 """ 373 374 @staticmethod 375 def boot(env): 376 lookup_scenario("MalformedMpReachNlri").boot(env) 377 378 @staticmethod 379 def setup(env): 380 g1 = env.g1 381 e1 = env.e1 382 e2 = env.e2 383 for c in [e1, e2]: 384 g1.wait_for(BGP_FSM_ESTABLISHED, c) 385 386 # advertise malformed AS_PATH SEGMENT LENGTH 387 # Send the attribute to the length and number of aspath is inconsistent 388 # Attribute Type 0x02 (AS_PATH) 389 # Attribute Flag 0x40 (well-known transitive) 390 # Attribute Value 0x02020000ffdc ( 391 # segment type = 02 392 # segment length = 02 -> # correct value = 01 393 # as number = 65500 ) 394 e1.add_route('10.7.0.17/32', attribute='0x02 0x40 0x0202ffdc') 395 396 @staticmethod 397 def check(env): 398 g1 = env.g1 399 e1 = env.e1 400 e2 = env.e2 401 402 def f(): 403 for line in e1.log().split('\n'): 404 if 'UPDATE message error / Malformed AS_PATH / 0x4002040202FFDC' in line: 405 return True 406 return False 407 408 wait_for(f) 409 # check e2 is still established 410 g1.wait_for(BGP_FSM_ESTABLISHED, e2) 411 412 @staticmethod 413 def executor(env): 414 lookup_scenario("MalformedAsPathSegmentLengthInvalid").boot(env) 415 lookup_scenario("MalformedAsPathSegmentLengthInvalid").setup(env) 416 lookup_scenario("MalformedAsPathSegmentLengthInvalid").check(env) 417 418 419 @register_scenario 420 class MalformedNexthopLoopbackAddr(object): 421 """ 422 No.8 malformaed nexthop loopback addr 423 """ 424 425 @staticmethod 426 def boot(env): 427 lookup_scenario("MalformedMpReachNlri").boot(env) 428 429 @staticmethod 430 def setup(env): 431 g1 = env.g1 432 e1 = env.e1 433 e2 = env.e2 434 for c in [e1, e2]: 435 g1.wait_for(BGP_FSM_ESTABLISHED, c) 436 437 # Malformed Invalid NEXT_HOP Attribute 438 # Send the attribute of invalid nexthop 439 # next-hop 127.0.0.1 -> # correct value = other than loopback and 0.0.0.0 address 440 e1.add_route('10.7.0.17/32', nexthop='127.0.0.1') 441 442 @staticmethod 443 def check(env): 444 g1 = env.g1 445 e1 = env.e1 446 e2 = env.e2 447 448 def f(): 449 for line in e1.log().split('\n'): 450 if 'UPDATE message error / Invalid NEXT_HOP Attribute / 0x4003047F000001' in line: 451 return True 452 return False 453 454 wait_for(f) 455 # check e2 is still established 456 g1.wait_for(BGP_FSM_ESTABLISHED, e2) 457 458 @staticmethod 459 def executor(env): 460 lookup_scenario("MalformedNexthopLoopbackAddr").boot(env) 461 lookup_scenario("MalformedNexthopLoopbackAddr").setup(env) 462 lookup_scenario("MalformedNexthopLoopbackAddr").check(env) 463 464 465 @register_scenario 466 class MalformedOriginType(object): 467 """ 468 No.9 malformaed origin type 469 """ 470 471 @staticmethod 472 def boot(env): 473 lookup_scenario("MalformedMpReachNlri").boot(env) 474 475 @staticmethod 476 def setup(env): 477 g1 = env.g1 478 e1 = env.e1 479 e2 = env.e2 480 for c in [e1, e2]: 481 g1.wait_for(BGP_FSM_ESTABLISHED, c) 482 483 # Invalid ORIGIN Attribute 484 # Send the attribute of origin type 4 485 # Attribute Type 0x01 (Origin) 486 # Attribute Flag 0x40 (well-known transitive) 487 # Attribute Value 0x04 ( 488 # origin type = 04 -> # correct value = 01 or 02 or 03 ) 489 e1.add_route('10.7.0.17/32', attribute='0x1 0x40 0x04') 490 491 @staticmethod 492 def check(env): 493 g1 = env.g1 494 e1 = env.e1 495 e2 = env.e2 496 497 def f(): 498 for line in e1.log().split('\n'): 499 if 'UPDATE message error / Invalid ORIGIN Attribute / 0x40010104' in line: 500 return True 501 return False 502 503 wait_for(f) 504 # check e2 is still established 505 g1.wait_for(BGP_FSM_ESTABLISHED, e2) 506 507 @staticmethod 508 def executor(env): 509 lookup_scenario("MalformedOriginType").boot(env) 510 lookup_scenario("MalformedOriginType").setup(env) 511 lookup_scenario("MalformedOriginType").check(env) 512 513 514 class TestGoBGPBase(unittest.TestCase): 515 516 wait_per_retry = 5 517 retry_limit = 10 518 519 @classmethod 520 def setUpClass(cls): 521 idx = parser_option.test_index 522 base.TEST_PREFIX = parser_option.test_prefix 523 cls.parser_option = parser_option 524 cls.executors = [] 525 if idx == 0: 526 print('unset test-index. run all test sequential') 527 for _, v in list(_SCENARIOS.items()): 528 for k, m in inspect.getmembers(v, inspect.isfunction): 529 if k == 'executor': 530 cls.executor = m 531 cls.executors.append(cls.executor) 532 elif idx not in _SCENARIOS: 533 print('invalid test-index. # of scenarios: {0}'.format(len(_SCENARIOS))) 534 sys.exit(1) 535 else: 536 for k, m in inspect.getmembers(_SCENARIOS[idx], inspect.isfunction): 537 if k == 'executor': 538 cls.executor = m 539 cls.executors.append(cls.executor) 540 541 def test(self): 542 for e in self.executors: 543 yield e 544 545 546 if __name__ == '__main__': 547 output = local("which docker 2>&1 > /dev/null ; echo $?", capture=True) 548 if int(output) != 0: 549 print("docker not found") 550 sys.exit(1) 551 552 nose.main(argv=sys.argv, addplugins=[OptionParser()], 553 defaultTest=sys.argv[0])