diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/scenario_test/flow_spec_test.py | 279 |
1 files changed, 243 insertions, 36 deletions
diff --git a/test/scenario_test/flow_spec_test.py b/test/scenario_test/flow_spec_test.py index 2671bf5e..d4b401a7 100644 --- a/test/scenario_test/flow_spec_test.py +++ b/test/scenario_test/flow_spec_test.py @@ -15,7 +15,6 @@ from __future__ import absolute_import -from itertools import combinations import sys import time import unittest @@ -29,57 +28,265 @@ from lib import base from lib.base import BGP_FSM_ESTABLISHED from lib.gobgp import GoBGPContainer from lib.exabgp import ExaBGPContainer +from lib.yabgp import YABGPContainer -class GoBGPTestBase(unittest.TestCase): +class FlowSpecTest(unittest.TestCase): + + """ + Test case for Flow Specification. + """ + # +------------+ +------------+ + # | G1(GoBGP) |---(iBGP)---| E1(ExaBGP) | + # | 172.17.0.2 | | 172.17.0.3 | + # +------------+ +------------+ + # | + # (iBGP) + # | + # +------------+ + # | Y1(YABGP) | + # | 172.17.0.4 | + # +------------+ @classmethod def setUpClass(cls): gobgp_ctn_image_name = parser_option.gobgp_image base.TEST_PREFIX = parser_option.test_prefix - g1 = GoBGPContainer(name='g1', asn=65000, router_id='192.168.0.1', - ctn_image_name=gobgp_ctn_image_name, - log_level=parser_option.gobgp_log_level) - e1 = ExaBGPContainer(name='e1', asn=65000, router_id='192.168.0.2') - - ctns = [g1, e1] - - # advertise a route from q1, q2 - matchs = ['destination 10.0.0.0/24', 'source 20.0.0.0/24'] - thens = ['discard'] - e1.add_route(route='flow1', rf='ipv4-flowspec', matchs=matchs, thens=thens) - matchs2 = ['tcp-flags S', 'protocol ==tcp ==udp', "packet-length '>1000&<2000'", "source-port '!=2&!=22&!=222'"] - thens2 = ['rate-limit 9600', 'redirect 0.10:100', 'mark 20', 'action sample'] - g1.add_route(route='flow1', rf='ipv4-flowspec', matchs=matchs2, thens=thens2) - matchs3 = ['destination 2001::/24/10', 'source 2002::/24/15'] - thens3 = ['discard'] - e1.add_route(route='flow2', rf='ipv6-flowspec', matchs=matchs3, thens=thens3) - matchs4 = ['destination 2001::/24 10', "label '==100'"] - thens4 = ['discard'] - g1.add_route(route='flow2', rf='ipv6-flowspec', matchs=matchs4, thens=thens4) + cls.g1 = GoBGPContainer(name='g1', asn=65000, router_id='192.168.0.1', + ctn_image_name=gobgp_ctn_image_name, + log_level=parser_option.gobgp_log_level) - initial_wait_time = max(ctn.run() for ctn in ctns) + cls.e1 = ExaBGPContainer(name='e1', asn=65000, router_id='192.168.0.2') + + cls.y1 = YABGPContainer(name='y1', asn=65000, router_id='192.168.0.3') + ctns = [cls.g1, cls.e1, cls.y1] + initial_wait_time = max(ctn.run() for ctn in ctns) time.sleep(initial_wait_time) - # ibgp peer. loop topology - for a, b in combinations(ctns, 2): - a.add_peer(b, flowspec=True) - b.add_peer(a, flowspec=True) + # Add FlowSpec routes into ExaBGP. + # Note: Currently, ExaBGPContainer only supports to add routes by + # reloading configuration, so we add routes here. + cls.e1.add_route( + route='ipv4/dst/src', + rf='ipv4-flowspec', + matchs=[ + 'destination 12.1.0.0/24', + 'source 12.2.0.0/24', + ], + thens=['discard']) + cls.e1.add_route( + route='ipv6/dst/src', + rf='ipv6-flowspec', + matchs=[ + 'destination 2002:1::/64/10', + 'source 2002:2::/64/15', + ], + thens=['discard']) + + # Add FlowSpec routes into GoBGP. + cls.g1.add_route( + route='ipv4/all', + rf='ipv4-flowspec', + matchs=[ + 'destination 11.1.0.0/24', + 'source 11.2.0.0/24', + "protocol '==tcp &=udp icmp >igmp >=egp <igp <=rsvp !=gre'", + "port '==80 &=90 8080 >9090 >=8180 <9190 <=8081 !=9091 &!443'", + 'destination-port 80', + 'source-port 8080', + 'icmp-type 1', + 'icmp-code 2', + "tcp-flags '==S &=SA A !F !=U =!C'", + 'packet-length 100', + 'dscp 12', + 'fragment dont-fragment is-fragment+first-fragment', + ], + thens=['discard']) + cls.g1.add_route( + route='ipv6/dst/src/label', # others are tested on IPv4 + rf='ipv6-flowspec', + matchs=[ + 'destination 2001:1::/64 10', + 'source 2001:2::/64 15', + 'label 12', + ], + thens=['discard']) + + cls.g1.add_peer(cls.e1, flowspec=True) + cls.e1.add_peer(cls.g1, flowspec=True) + cls.g1.add_peer(cls.y1, flowspec=True) + cls.y1.add_peer(cls.g1, flowspec=True) + + cls.g1.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=cls.e1) + cls.g1.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=cls.y1) + + # Add FlowSpec routes into YABGP. + # Note: Currently, YABGPContainer only supports to add routes via + # REST API after connection established, so we add routes here. + cls.y1.add_route( + route='ipv4/all', + rf='ipv4-flowspec', + matchs=[ + 'destination 13.1.0.0/24', + 'source 13.2.0.0/24', + "protocol =6|=17", + # "port", # not seem to be supported + 'destination-port =80', + 'source-port <8080|>9090', + 'icmp-type >=1', + 'icmp-code <2', + # "tcp-flags", # not seem to be supported via REST API + 'packet-length <=100', + 'dscp =12', + # "fragment", # not seem to be supported via REST API + ], + thens=['traffic-rate:0:0']) # 'discard' + # IPv6 FlowSpec: not supported with YABGP v0.4.0 + # cls.y1.add_route( + # route='ipv6/dst/src/label', # others are tested on IPv4 + # rf='ipv6-flowspec', + # matchs=[ + # 'destination 2003:1::/64/10', + # 'source 2003:2::/64/15', + # 'label 12', + # ], + # thens=['traffic-rate:0:0']) # 'discard' + + def test_01_ipv4_yabgp_adj_rib_in(self): + rib = self.y1.get_adj_rib_in(peer=self.g1, rf='flowspec') + self.assertEqual(1, len(rib)) + nlri = list(rib)[0] # advertised from G1(GoBGP) + expected = ( + # INPUTS: + # 'destination 11.1.0.0/24', + '{"1": "11.1.0.0/24",' + # 'source 11.2.0.0/24', + ' "2": "11.2.0.0/24",' + # "protocol '==tcp &=udp icmp >igmp >=egp <igp <=rsvp !=gre'", + ' "3": "=6&=17|=1|>2|>=8|<9|<=46|><47",' + # "port '==80 &=90 8080 >9090 >=8180 <9190 <=8081 !=9091 &!443'", + ' "4": "=80&=90|=8080|>9090|>=8180|<9190|<=8081|><9091&><443",' + # 'destination-port 80', + ' "5": "=80",' + # 'source-port 8080', + ' "6": "=8080",' + # 'icmp-type 1', + ' "7": "=1",' + # 'icmp-code 2', + ' "8": "=2",' + # "tcp-flags '==S &=SA A !F !=U =!C'", + ' "9": "=2&=18|16|>1|>=32|>=128",' + # 'packet-length 100', + ' "10": "=100",' + # 'dscp 12', + ' "11": "=12",' + # 'fragment dont-fragment is-fragment+first-fragment', + ' "12": "1|6"}' + ) + self.assertEqual(expected, nlri) - cls.gobgp = g1 - cls.exabgp = e1 + def test_02_ipv4_gobgp_global_rib(self): + rib = self.g1.get_global_rib(rf='ipv4-flowspec') + self.assertEqual(3, len(rib)) + output_nlri_list = [r['prefix'] for r in rib] + nlri_e1 = ( + # INPUTS: + # 'destination 12.1.0.0/24', + "[destination: 12.1.0.0/24]" + # 'source 12.2.0.0/24', + "[source: 12.2.0.0/24]" + ) + nlri_g1 = ( + # INPUTS: + # 'destination 11.1.0.0/24', + "[destination: 11.1.0.0/24]" + # 'source 11.2.0.0/24', + "[source: 11.2.0.0/24]" + # "protocol '==tcp &=udp icmp >igmp >=egp <igp <=rsvp !=gre'", + "[protocol: ==tcp&==udp ==icmp >igmp >=egp <igp <=rsvp !=gre]" + # "port '==80 &=90 8080 >9090 >=8180 <9190 <=8081 !=9091 &!443'", + "[port: ==80&==90 ==8080 >9090 >=8180 <9190 <=8081 !=9091&!=443]" + # 'destination-port 80', + "[destination-port: ==80]" + # 'source-port 8080', + "[source-port: ==8080]" + # 'icmp-type 1', + "[icmp-type: ==1]" + # 'icmp-code 2', + "[icmp-code: ==2]" + # "tcp-flags '==S &=SA A !F !=U =!C'", + "[tcp-flags: =S&=SA A !F !=U !=C]" + # 'packet-length 100', + "[packet-length: ==100]" + # 'dscp 12', + "[dscp: ==12]" + # 'fragment dont-fragment is-fragment+first-fragment', + "[fragment: dont-fragment is-fragment+first-fragment]" + ) + nlri_y1 = ( + # INPUTS: + # 'destination 13.1.0.0/24', + "[destination: 13.1.0.0/24]" + # 'source 13.2.0.0/24', + "[source: 13.2.0.0/24]" + # "protocol =6|=17", + "[protocol: ==tcp ==udp]" + # 'destination-port =80', + "[destination-port: ==80]" + # 'source-port <8080|>9090', + "[source-port: <8080 >9090]" + # 'icmp-type >=1', + "[icmp-type: >=1]" + # 'icmp-code <2', + "[icmp-code: <2]" + # 'packet-length <=100', + "[packet-length: <=100]" + # 'dscp =12', + "[dscp: ==12]" + ) + for nlri in [nlri_e1, nlri_g1, nlri_y1]: + self.assertIn(nlri, output_nlri_list) - # test each neighbor state is turned establish - def test_01_neighbor_established(self): - self.gobgp.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=self.exabgp) + def test_03_ipv6_gobgp_global_rib(self): + rib = self.g1.get_global_rib(rf='ipv6-flowspec') + self.assertEqual(2, len(rib)) + output_nlri_list = [r['prefix'] for r in rib] + nlri_e1 = ( + # INPUTS: + # 'destination 2002:1::/64/10', + "[destination: 2002:1::/64/10]" + # 'source 2002:2::/64/15', + "[source: 2002:2::/64/15]" + ) + nlri_g1 = ( + # INPUTS: + # 'destination 2001:1::/64 10', + "[destination: 2001:1::/64/10]" + # 'source 2001:2::/64 15', + "[source: 2001:2::/64/15]" + # 'label 12', + "[label: ==12]" + ) + for nlri in [nlri_e1, nlri_g1]: + self.assertIn(nlri, output_nlri_list) - def test_02_check_gobgp_global_rib(self): - self.assertTrue(len(self.gobgp.get_global_rib(rf='ipv4-flowspec')) == 2) + def test_04_ipv4_yabgp_delete_route(self): + # Delete a route on Y1(YABGP) + self.y1.del_route(route='ipv4/all') + time.sleep(1) + # Test if the route is deleted or not + rib = self.g1.get_adj_rib_in(peer=self.y1, rf='ipv4-flowspec') + self.assertEqual(0, len(rib)) - def test_03_check_gobgp_global_rib(self): - self.assertTrue(len(self.gobgp.get_global_rib(rf='ipv6-flowspec')) == 2) + def test_05_ipv4_gobgp_delete_route(self): + # Delete a route on G1(GoBGP) + self.g1.del_route(route='ipv4/all') + time.sleep(1) + # Test if the route is deleted or not + rib = self.y1.get_adj_rib_in(peer=self.g1, rf='ipv4-flowspec') + self.assertEqual(0, len(rib)) if __name__ == '__main__': |