github.com/osrg/gobgp@v2.0.0+incompatible/test/scenario_test/flow_spec_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 22 from fabric.api import local 23 import nose 24 25 from lib.noseplugin import OptionParser, parser_option 26 27 from lib import base 28 from lib.base import BGP_FSM_ESTABLISHED 29 from lib.gobgp import GoBGPContainer 30 from lib.exabgp import ExaBGPContainer 31 from lib.yabgp import YABGPContainer 32 33 34 class FlowSpecTest(unittest.TestCase): 35 36 """ 37 Test case for Flow Specification. 38 """ 39 # +------------+ +------------+ 40 # | G1(GoBGP) |---(iBGP)---| E1(ExaBGP) | 41 # | 172.17.0.2 | | 172.17.0.3 | 42 # +------------+ +------------+ 43 # | 44 # (iBGP) 45 # | 46 # +------------+ 47 # | Y1(YABGP) | 48 # | 172.17.0.4 | 49 # +------------+ 50 51 @classmethod 52 def setUpClass(cls): 53 gobgp_ctn_image_name = parser_option.gobgp_image 54 base.TEST_PREFIX = parser_option.test_prefix 55 56 cls.g1 = GoBGPContainer(name='g1', asn=65000, router_id='192.168.0.1', 57 ctn_image_name=gobgp_ctn_image_name, 58 log_level=parser_option.gobgp_log_level) 59 60 cls.e1 = ExaBGPContainer(name='e1', asn=65000, router_id='192.168.0.2') 61 62 cls.y1 = YABGPContainer(name='y1', asn=65000, router_id='192.168.0.3') 63 64 ctns = [cls.g1, cls.e1, cls.y1] 65 initial_wait_time = max(ctn.run() for ctn in ctns) 66 time.sleep(initial_wait_time) 67 68 # Add FlowSpec routes into GoBGP. 69 cls.g1.add_route( 70 route='ipv4/all', 71 rf='ipv4-flowspec', 72 matchs=[ 73 'destination 11.1.0.0/24', 74 'source 11.2.0.0/24', 75 "protocol '==tcp &=udp icmp >igmp >=egp <ipip <=rsvp !=gre'", 76 "port '==80 &=90 8080 >9090 >=8180 <9190 <=8081 !=9091 &!443'", 77 'destination-port 80', 78 'source-port 8080', 79 'icmp-type 0', 80 'icmp-code 2', 81 "tcp-flags '==S &=SA A !F !=U =!R'", 82 'packet-length 100', 83 'dscp 12', 84 'fragment dont-fragment is-fragment+first-fragment', 85 ], 86 thens=['discard']) 87 cls.g1.add_route( 88 route='ipv6/dst/src/label', # others are tested on IPv4 89 rf='ipv6-flowspec', 90 matchs=[ 91 'destination 2001:1::/64 10', 92 'source 2001:2::/64 15', 93 'label 12', 94 ], 95 thens=['discard']) 96 97 cls.g1.add_peer(cls.e1, flowspec=True) 98 cls.e1.add_peer(cls.g1, flowspec=True) 99 cls.g1.add_peer(cls.y1, flowspec=True) 100 cls.y1.add_peer(cls.g1, flowspec=True) 101 102 cls.g1.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=cls.e1) 103 cls.g1.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=cls.y1) 104 105 # Add FlowSpec routes into ExaBGP. 106 cls.e1.add_route( 107 route='ipv4/all', 108 rf='ipv4-flowspec', 109 matchs=[ 110 'destination 12.1.0.0/24', 111 'source 12.2.0.0/24', 112 'protocol =tcp', 113 'port >=80', 114 'destination-port >5000', 115 'source-port 8080', 116 'icmp-type <1', 117 'icmp-code <=2', 118 "tcp-flags FIN", 119 'packet-length >100&<200', 120 'dscp 12', 121 'fragment dont-fragment', 122 ], 123 thens=['discard']) 124 cls.e1.add_route( 125 route='ipv6/dst/src/protocol/label', # others are tested on IPv4 126 rf='ipv6-flowspec', 127 matchs=[ 128 'destination 2002:1::/64/10', 129 'source 2002:2::/64/15', 130 'next-header udp', 131 'flow-label >100', 132 ], 133 thens=['discard']) 134 135 # Add FlowSpec routes into YABGP. 136 cls.y1.add_route( 137 route='ipv4/all', 138 rf='ipv4-flowspec', 139 matchs=[ 140 'destination 13.1.0.0/24', 141 'source 13.2.0.0/24', 142 "protocol =6|=17", 143 # "port", # not seem to be supported 144 'destination-port =80', 145 'source-port <8080|>9090', 146 'icmp-type >=1', 147 'icmp-code <2', 148 # "tcp-flags", # not seem to be supported via REST API 149 'packet-length <=100', 150 'dscp =12', 151 # "fragment", # not seem to be supported via REST API 152 ], 153 thens=['traffic-rate:0:0']) # 'discard' 154 # IPv6 FlowSpec: not supported with YABGP v0.4.0 155 # cls.y1.add_route( 156 # route='ipv6/dst/src/label', # others are tested on IPv4 157 # rf='ipv6-flowspec', 158 # matchs=[ 159 # 'destination 2003:1::/64/10', 160 # 'source 2003:2::/64/15', 161 # 'label 12', 162 # ], 163 # thens=['traffic-rate:0:0']) # 'discard' 164 165 def test_01_ipv4_exabgp_adj_rib_in(self): 166 rib = self.e1.get_adj_rib_in(self.g1, rf='ipv4-flowspec') 167 self.assertEqual(1, len(rib)) 168 nlri = list(rib)[0] # advertised from G1(GoBGP) 169 _exp_fmt = ( 170 # INPUTS: 171 # 'destination 11.1.0.0/24', 172 "destination-ipv4 11.1.0.0/24 " 173 # 'source 11.2.0.0/24', 174 "source-ipv4 11.2.0.0/24 " 175 # "protocol '==tcp &=udp icmp >igmp >=egp <ipip <=rsvp !=gre'", 176 "protocol [ =tcp&=udp =icmp >igmp >=egp <ipip <=rsvp !=gre ] " 177 # "port '==80 &=90 8080 >9090 >=8180 <9190 <=8081 !=9091 &!443'", 178 "port [ =80&=90 =8080 >9090 >=8180 <9190 <=8081 !=9091&!=443 ] " 179 # 'destination-port 80', 180 "destination-port =80 " 181 # 'source-port 8080', 182 "source-port =8080 " 183 # 'icmp-type 0', 184 "icmp-type =echo-reply " 185 # 'icmp-code 2', 186 "icmp-code =2 " 187 # "tcp-flags '==S &=SA A !F !=U =!R'", 188 "tcp-flags [ =syn&=%s ack !fin !=urgent !=rst ] " 189 # 'packet-length 100', 190 "packet-length =100 " 191 # 'dscp 12', 192 "dscp =12 " 193 # 'fragment dont-fragment is-fragment+first-fragment', 194 "fragment [ dont-fragment is-fragment+first-fragment ]" 195 ) 196 # Note: Considers variants of SYN + ACK 197 expected_list = (_exp_fmt % 'syn+ack', _exp_fmt % 'ack+syn') 198 self.assertIn(nlri, expected_list) 199 200 def test_02_ipv6_exabgp_adj_rib_in(self): 201 rib = self.e1.get_adj_rib_in(self.g1, rf='ipv6-flowspec') 202 self.assertEqual(1, len(rib)) 203 nlri = list(rib)[0] # advertised from G1(GoBGP) 204 expected = ( 205 # INPUTS: 206 # 'destination 2001:1::/64 10', 207 "destination-ipv6 2001:1::/64/10 " 208 # 'source 2001:2::/64 15', 209 "source-ipv6 2001:2::/64/15 " 210 # 'label 12', 211 "flow-label =12" 212 ) 213 self.assertEqual(expected, nlri) 214 215 def test_03_ipv4_yabgp_adj_rib_in(self): 216 rib = self.y1.get_adj_rib_in(peer=self.g1, rf='flowspec') 217 self.assertEqual(1, len(rib)) 218 nlri = list(rib)[0] # advertised from G1(GoBGP) 219 expected = ( 220 # INPUTS: 221 # 'destination 11.1.0.0/24', 222 '{"1": "11.1.0.0/24",' 223 # 'source 11.2.0.0/24', 224 ' "2": "11.2.0.0/24",' 225 # "protocol '==tcp &=udp icmp >igmp >=egp <ipip <=rsvp !=gre'", 226 ' "3": "=6&=17|=1|>2|>=8|<94|<=46|><47",' 227 # "port '==80 &=90 8080 >9090 >=8180 <9190 <=8081 !=9091 &!443'", 228 ' "4": "=80&=90|=8080|>9090|>=8180|<9190|<=8081|><9091&><443",' 229 # 'destination-port 80', 230 ' "5": "=80",' 231 # 'source-port 8080', 232 ' "6": "=8080",' 233 # 'icmp-type 0', 234 ' "7": "=0",' 235 # 'icmp-code 2', 236 ' "8": "=2",' 237 # "tcp-flags '==S &=SA A !F !=U =!R'", 238 ' "9": "=2&=18|16|>1|>=32|>=4",' 239 # 'packet-length 100', 240 ' "10": "=100",' 241 # 'dscp 12', 242 ' "11": "=12",' 243 # 'fragment dont-fragment is-fragment+first-fragment', 244 ' "12": "1|6"}' 245 ) 246 self.assertEqual(expected, nlri) 247 248 def test_04_ipv6_yabgp_adj_rib_in(self): 249 # IPv6 FlowSpec: not supported with YABGP v0.4.0 250 pass 251 252 def test_05_ipv4_gobgp_global_rib(self): 253 rib = self.g1.get_global_rib(rf='ipv4-flowspec') 254 self.assertEqual(3, len(rib)) 255 output_nlri_list = [r['prefix'] for r in rib] 256 nlri_g1 = ( 257 # INPUTS: 258 # 'destination 11.1.0.0/24', 259 "[destination: 11.1.0.0/24]" 260 # 'source 11.2.0.0/24', 261 "[source: 11.2.0.0/24]" 262 # "protocol '==tcp &=udp icmp >igmp >=egp <ipip <=rsvp !=gre'", 263 "[protocol: ==tcp&==udp ==icmp >igmp >=egp <ipip <=rsvp !=gre]" 264 # "port '==80 &=90 8080 >9090 >=8180 <9190 <=8081 !=9091 &!443'", 265 "[port: ==80&==90 ==8080 >9090 >=8180 <9190 <=8081 !=9091&!=443]" 266 # 'destination-port 80', 267 "[destination-port: ==80]" 268 # 'source-port 8080', 269 "[source-port: ==8080]" 270 # 'icmp-type 0', 271 "[icmp-type: ==0]" 272 # 'icmp-code 2', 273 "[icmp-code: ==2]" 274 # "tcp-flags '==S &=SA A !F !=U =!R'", 275 "[tcp-flags: =S&=SA A !F !=U !=R]" 276 # 'packet-length 100', 277 "[packet-length: ==100]" 278 # 'dscp 12', 279 "[dscp: ==12]" 280 # 'fragment dont-fragment is-fragment+first-fragment', 281 "[fragment: dont-fragment is-fragment+first-fragment]" 282 ) 283 nlri_e1 = ( 284 # INPUTS: 285 # 'destination 12.1.0.0/24', 286 '[destination: 12.1.0.0/24]' 287 # 'source 12.2.0.0/24', 288 '[source: 12.2.0.0/24]' 289 # 'protocol =tcp', 290 '[protocol: ==tcp]' 291 # 'port >=80', 292 '[port: >=80]' 293 # 'destination-port >5000', 294 '[destination-port: >5000]' 295 # 'source-port 8080', 296 '[source-port: ==8080]' 297 # 'icmp-type <1', 298 '[icmp-type: <1]' 299 # 'icmp-code <=2', 300 '[icmp-code: <=2]' 301 # "tcp-flags FIN", 302 '[tcp-flags: F]' 303 # 'packet-length >100&<200', 304 '[packet-length: >100&<200]' 305 # 'dscp 12', 306 '[dscp: ==12]' 307 # 'fragment dont-fragment', 308 '[fragment: dont-fragment]' 309 ) 310 nlri_y1 = ( 311 # INPUTS: 312 # 'destination 13.1.0.0/24', 313 "[destination: 13.1.0.0/24]" 314 # 'source 13.2.0.0/24', 315 "[source: 13.2.0.0/24]" 316 # "protocol =6|=17", 317 "[protocol: ==tcp ==udp]" 318 # 'destination-port =80', 319 "[destination-port: ==80]" 320 # 'source-port <8080|>9090', 321 "[source-port: <8080 >9090]" 322 # 'icmp-type >=1', 323 "[icmp-type: >=1]" 324 # 'icmp-code <2', 325 "[icmp-code: <2]" 326 # 'packet-length <=100', 327 "[packet-length: <=100]" 328 # 'dscp =12', 329 "[dscp: ==12]" 330 ) 331 for nlri in [nlri_g1, nlri_e1, nlri_y1]: 332 self.assertIn(nlri, output_nlri_list) 333 334 def test_06_ipv6_gobgp_global_rib(self): 335 rib = self.g1.get_global_rib(rf='ipv6-flowspec') 336 import json 337 self.assertEqual(2, len(rib), json.dumps(rib)) 338 output_nlri_list = [r['prefix'] for r in rib] 339 nlri_g1 = ( 340 # INPUTS: 341 # 'destination 2001:1::/64 10', 342 "[destination: 2001:1::/64/10]" 343 # 'source 2001:2::/64 15', 344 "[source: 2001:2::/64/15]" 345 # 'label 12', 346 "[label: ==12]" 347 ) 348 nlri_e1 = ( 349 # INPUTS: 350 # 'destination 2002:1::/64/10', 351 '[destination: 2002:1::/64/10]' 352 # 'source 2002:2::/64/15', 353 '[source: 2002:2::/64/15]' 354 # 'next-header udp', 355 '[protocol: ==udp]' 356 # 'flow-label >100', 357 '[label: >100]' 358 ) 359 for nlri in [nlri_g1, nlri_e1]: 360 self.assertIn(nlri, output_nlri_list) 361 362 def test_07_ipv4_exabgp_delete_route(self): 363 # Delete a route on E1(ExaBGP) 364 self.e1.del_route(route='ipv4/all') 365 time.sleep(1) 366 # Test if the route is deleted or not 367 rib = self.g1.get_adj_rib_in(peer=self.e1, rf='ipv4-flowspec') 368 self.assertEqual(0, len(rib)) 369 370 def test_08_ipv6_exabgp_delete_route(self): 371 # Delete a route on E1(ExaBGP) 372 self.e1.del_route(route='ipv6/dst/src/protocol/label') 373 time.sleep(1) 374 # Test if the route is deleted or not 375 rib = self.g1.get_adj_rib_in(peer=self.e1, rf='ipv6-flowspec') 376 self.assertEqual(0, len(rib)) 377 378 def test_09_ipv4_yabgp_delete_route(self): 379 # Delete a route on Y1(YABGP) 380 self.y1.del_route(route='ipv4/all') 381 time.sleep(1) 382 # Test if the route is deleted or not 383 rib = self.g1.get_adj_rib_in(peer=self.y1, rf='ipv4-flowspec') 384 self.assertEqual(0, len(rib)) 385 386 def test_10_ipv6_yabgp_delete_route(self): 387 # IPv6 FlowSpec: not supported with YABGP v0.4.0 388 pass 389 390 def test_11_ipv4_gobgp_delete_route(self): 391 # Delete a route on G1(GoBGP) 392 self.g1.del_route(route='ipv4/all') 393 time.sleep(1) 394 # Test if the route is deleted or not 395 rib_e1 = self.e1.get_adj_rib_in(peer=self.g1, rf='ipv4-flowspec') 396 self.assertEqual(0, len(rib_e1)) 397 rib_y1 = self.y1.get_adj_rib_in(peer=self.g1, rf='ipv4-flowspec') 398 self.assertEqual(0, len(rib_y1)) 399 400 def test_12_ipv6_gobgp_delete_route(self): 401 # Delete a route on G1(GoBGP) 402 self.g1.del_route(route='ipv6/dst/src/label') 403 time.sleep(1) 404 # Test if the route is deleted or not 405 rib_e1 = self.e1.get_adj_rib_in(peer=self.g1, rf='ipv6-flowspec') 406 self.assertEqual(0, len(rib_e1)) 407 rib_y1 = self.y1.get_adj_rib_in(peer=self.g1, rf='ipv6-flowspec') 408 self.assertEqual(0, len(rib_y1)) 409 410 411 if __name__ == '__main__': 412 output = local("which docker 2>&1 > /dev/null ; echo $?", capture=True) 413 if int(output) is not 0: 414 print "docker not found" 415 sys.exit(1) 416 417 nose.main(argv=sys.argv, addplugins=[OptionParser()], 418 defaultTest=sys.argv[0])