mirror of
https://github.com/osrg/gobgp.git
synced 2024-05-11 05:55:10 +00:00
420 lines
15 KiB
Python
420 lines
15 KiB
Python
# Copyright (C) 2015 Nippon Telegraph and Telephone Corporation.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
# implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
|
|
import sys
|
|
import time
|
|
import unittest
|
|
|
|
import collections
|
|
collections.Callable = collections.abc.Callable
|
|
|
|
import nose
|
|
|
|
from lib.noseplugin import OptionParser, parser_option
|
|
|
|
from lib import base
|
|
from lib.base import BGP_FSM_ESTABLISHED, local
|
|
from lib.gobgp import GoBGPContainer
|
|
from lib.exabgp import ExaBGPContainer
|
|
from lib.yabgp import YABGPContainer
|
|
|
|
|
|
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
|
|
|
|
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)
|
|
|
|
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)
|
|
|
|
# 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 <ipip <=rsvp !=gre'",
|
|
"port '==80 &=90 8080 >9090 >=8180 <9190 <=8081 !=9091 &!443'",
|
|
'destination-port 80',
|
|
'source-port 8080',
|
|
'icmp-type 0',
|
|
'icmp-code 2',
|
|
"tcp-flags '==S &=SA A !F !=U =!R'",
|
|
'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 ExaBGP.
|
|
cls.e1.add_route(
|
|
route='ipv4/all',
|
|
rf='ipv4-flowspec',
|
|
matchs=[
|
|
'destination 12.1.0.0/24',
|
|
'source 12.2.0.0/24',
|
|
'protocol =tcp',
|
|
'port >=80',
|
|
'destination-port >5000',
|
|
'source-port 8080',
|
|
'icmp-type <1',
|
|
'icmp-code <=2',
|
|
"tcp-flags FIN",
|
|
'packet-length >100&<200',
|
|
'dscp 12',
|
|
'fragment dont-fragment',
|
|
],
|
|
thens=['discard'])
|
|
cls.e1.add_route(
|
|
route='ipv6/dst/src/protocol/label', # others are tested on IPv4
|
|
rf='ipv6-flowspec',
|
|
matchs=[
|
|
'destination 2002:1::/64/10',
|
|
'source 2002:2::/64/15',
|
|
'next-header udp',
|
|
'flow-label >100',
|
|
],
|
|
thens=['discard'])
|
|
|
|
# Add FlowSpec routes into YABGP.
|
|
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_exabgp_adj_rib_in(self):
|
|
rib = self.e1.get_adj_rib_in(self.g1, rf='ipv4-flowspec')
|
|
self.assertEqual(1, len(rib))
|
|
nlri = list(rib)[0] # advertised from G1(GoBGP)
|
|
_exp_fmt = (
|
|
# INPUTS:
|
|
# 'destination 11.1.0.0/24',
|
|
"destination-ipv4 11.1.0.0/24 "
|
|
# 'source 11.2.0.0/24',
|
|
"source-ipv4 11.2.0.0/24 "
|
|
# "protocol '==tcp &=udp icmp >igmp >=egp <ipip <=rsvp !=gre'",
|
|
"protocol [ =tcp&=udp =icmp >igmp >=egp <ipip <=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 0',
|
|
"icmp-type =echo-reply "
|
|
# 'icmp-code 2',
|
|
"icmp-code =2 "
|
|
# "tcp-flags '==S &=SA A !F !=U =!R'",
|
|
"tcp-flags [ =syn&=%s ack !fin !=urgent !=rst ] "
|
|
# 'packet-length 100',
|
|
"packet-length =100 "
|
|
# 'dscp 12',
|
|
"dscp =12 "
|
|
# 'fragment dont-fragment is-fragment+first-fragment',
|
|
"fragment [ dont-fragment is-fragment+first-fragment ]"
|
|
)
|
|
# Note: Considers variants of SYN + ACK
|
|
expected_list = (_exp_fmt % 'syn+ack', _exp_fmt % 'ack+syn')
|
|
self.assertIn(nlri, expected_list)
|
|
|
|
def test_02_ipv6_exabgp_adj_rib_in(self):
|
|
rib = self.e1.get_adj_rib_in(self.g1, rf='ipv6-flowspec')
|
|
self.assertEqual(1, len(rib))
|
|
nlri = list(rib)[0] # advertised from G1(GoBGP)
|
|
expected = (
|
|
# INPUTS:
|
|
# 'destination 2001:1::/64 10',
|
|
"destination-ipv6 2001:1::/64/10 "
|
|
# 'source 2001:2::/64 15',
|
|
"source-ipv6 2001:2::/64/15 "
|
|
# 'label 12',
|
|
"flow-label =12"
|
|
)
|
|
self.assertEqual(expected, nlri)
|
|
|
|
def test_03_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 <ipip <=rsvp !=gre'",
|
|
' "3": "=6&=17|=1|>2|>=8|<94|<=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 0',
|
|
' "7": "=0",'
|
|
# 'icmp-code 2',
|
|
' "8": "=2",'
|
|
# "tcp-flags '==S &=SA A !F !=U =!R'",
|
|
' "9": "=2&=18|16|>1|>=32|>=4",'
|
|
# 'packet-length 100',
|
|
' "10": "=100",'
|
|
# 'dscp 12',
|
|
' "11": "=12",'
|
|
# 'fragment dont-fragment is-fragment+first-fragment',
|
|
' "12": "1|6"}'
|
|
)
|
|
self.assertEqual(expected, nlri)
|
|
|
|
def test_04_ipv6_yabgp_adj_rib_in(self):
|
|
# IPv6 FlowSpec: not supported with YABGP v0.4.0
|
|
pass
|
|
|
|
def test_05_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_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 <ipip <=rsvp !=gre'",
|
|
"[protocol: ==tcp&==udp ==icmp >igmp >=egp <ipip <=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 0',
|
|
"[icmp-type: ==0]"
|
|
# 'icmp-code 2',
|
|
"[icmp-code: ==2]"
|
|
# "tcp-flags '==S &=SA A !F !=U =!R'",
|
|
"[tcp-flags: =S&=SA A !F !=U !=R]"
|
|
# '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_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]'
|
|
# 'protocol =tcp',
|
|
'[protocol: ==tcp]'
|
|
# 'port >=80',
|
|
'[port: >=80]'
|
|
# 'destination-port >5000',
|
|
'[destination-port: >5000]'
|
|
# 'source-port 8080',
|
|
'[source-port: ==8080]'
|
|
# 'icmp-type <1',
|
|
'[icmp-type: <1]'
|
|
# 'icmp-code <=2',
|
|
'[icmp-code: <=2]'
|
|
# "tcp-flags FIN",
|
|
'[tcp-flags: F]'
|
|
# 'packet-length >100&<200',
|
|
'[packet-length: >100&<200]'
|
|
# 'dscp 12',
|
|
'[dscp: ==12]'
|
|
# 'fragment dont-fragment',
|
|
'[fragment: dont-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_g1, nlri_e1, nlri_y1]:
|
|
self.assertIn(nlri, output_nlri_list)
|
|
|
|
def test_06_ipv6_gobgp_global_rib(self):
|
|
rib = self.g1.get_global_rib(rf='ipv6-flowspec')
|
|
import json
|
|
self.assertEqual(2, len(rib), json.dumps(rib))
|
|
output_nlri_list = [r['prefix'] for r in rib]
|
|
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]"
|
|
)
|
|
nlri_e1 = (
|
|
# INPUTS:
|
|
# 'destination 2002:1::/64/10',
|
|
'[destination: 2002:1::/64/10]'
|
|
# 'source 2002:2::/64/15',
|
|
'[source: 2002:2::/64/15]'
|
|
# 'next-header udp',
|
|
'[protocol: ==udp]'
|
|
# 'flow-label >100',
|
|
'[label: >100]'
|
|
)
|
|
for nlri in [nlri_g1, nlri_e1]:
|
|
self.assertIn(nlri, output_nlri_list)
|
|
|
|
def test_07_ipv4_exabgp_delete_route(self):
|
|
# Delete a route on E1(ExaBGP)
|
|
self.e1.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.e1, rf='ipv4-flowspec')
|
|
self.assertEqual(0, len(rib))
|
|
|
|
def test_08_ipv6_exabgp_delete_route(self):
|
|
# Delete a route on E1(ExaBGP)
|
|
self.e1.del_route(route='ipv6/dst/src/protocol/label')
|
|
time.sleep(1)
|
|
# Test if the route is deleted or not
|
|
rib = self.g1.get_adj_rib_in(peer=self.e1, rf='ipv6-flowspec')
|
|
self.assertEqual(0, len(rib))
|
|
|
|
def test_09_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_10_ipv6_yabgp_delete_route(self):
|
|
# IPv6 FlowSpec: not supported with YABGP v0.4.0
|
|
pass
|
|
|
|
def test_11_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_e1 = self.e1.get_adj_rib_in(peer=self.g1, rf='ipv4-flowspec')
|
|
self.assertEqual(0, len(rib_e1))
|
|
rib_y1 = self.y1.get_adj_rib_in(peer=self.g1, rf='ipv4-flowspec')
|
|
self.assertEqual(0, len(rib_y1))
|
|
|
|
def test_12_ipv6_gobgp_delete_route(self):
|
|
# Delete a route on G1(GoBGP)
|
|
self.g1.del_route(route='ipv6/dst/src/label')
|
|
time.sleep(1)
|
|
# Test if the route is deleted or not
|
|
rib_e1 = self.e1.get_adj_rib_in(peer=self.g1, rf='ipv6-flowspec')
|
|
self.assertEqual(0, len(rib_e1))
|
|
rib_y1 = self.y1.get_adj_rib_in(peer=self.g1, rf='ipv6-flowspec')
|
|
self.assertEqual(0, len(rib_y1))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
output = local("which docker 2>&1 > /dev/null ; echo $?", capture=True)
|
|
if int(output) != 0:
|
|
print("docker not found")
|
|
sys.exit(1)
|
|
|
|
nose.main(argv=sys.argv, addplugins=[OptionParser()],
|
|
defaultTest=sys.argv[0])
|