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