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