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])