From 893799b011b8667fd3ebfa4dd504de92eee53126 Mon Sep 17 00:00:00 2001 From: Hiroki Shirokura Date: Sun, 2 Feb 2020 12:18:05 +0000 Subject: [PATCH] topotests: add bgp_prefix_sid This commit add behavior test for BGP Prefix-SID path attribute generically. In this time, there are only 1-test for Prefix-SID type-1 Label-Index TLV. There are 3 nodes r1(FRR), peer1(exabgp) and peer2(exabgp) on this topotest. And it perform following: * peer1 advertise Prefix-SID to r1 * r1 is received Prefix-SID from peer1 * bgpd on r1 check the path attribute and parse correctly. * user can check information from type-1 information via show cli * bgpd on r1 advertise Prefix-SID to peer2 * peer2 is received Prefix-SID from r1 * peer2 check the path attribute and parse correctly. This test uses exabgp's generic path attribute feature of exabgp is used to advertise Prefix-SID path attribute to bgpd. generic path attribute feature enable exabgp users to specify binary format path attribute. we can send valious binary pattern (but overflow test doesn't can be performed). The reason why this commit uses generic attribute feature is that exabgp v3 doesn't support Prefix-SID path attribute and topotest support exabgp only v3. (fyr. exabgp v4 supports it). Thus this test includes little complicated binary format, so I wrote full binary desection and explanation. If topotest support exabgp v4, this test should be rewrite with non generic attribute feature. Signed-off-by: Hiroki Shirokura --- tests/topotests/bgp_prefix_sid/__init__.py | 0 tests/topotests/bgp_prefix_sid/exabgp.env | 53 ++++++ .../topotests/bgp_prefix_sid/peer1/exabgp.cfg | 103 +++++++++++ .../bgp_prefix_sid/peer2/exa-receive.py | 37 ++++ .../topotests/bgp_prefix_sid/peer2/exabgp.cfg | 19 ++ tests/topotests/bgp_prefix_sid/r1/bgpd.conf | 15 ++ tests/topotests/bgp_prefix_sid/r1/zebra.conf | 7 + .../bgp_prefix_sid/test_bgp_prefix_sid.py | 173 ++++++++++++++++++ 8 files changed, 407 insertions(+) create mode 100644 tests/topotests/bgp_prefix_sid/__init__.py create mode 100644 tests/topotests/bgp_prefix_sid/exabgp.env create mode 100644 tests/topotests/bgp_prefix_sid/peer1/exabgp.cfg create mode 100755 tests/topotests/bgp_prefix_sid/peer2/exa-receive.py create mode 100644 tests/topotests/bgp_prefix_sid/peer2/exabgp.cfg create mode 100644 tests/topotests/bgp_prefix_sid/r1/bgpd.conf create mode 100644 tests/topotests/bgp_prefix_sid/r1/zebra.conf create mode 100755 tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py diff --git a/tests/topotests/bgp_prefix_sid/__init__.py b/tests/topotests/bgp_prefix_sid/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_prefix_sid/exabgp.env b/tests/topotests/bgp_prefix_sid/exabgp.env new file mode 100644 index 0000000000..6c554f5fa8 --- /dev/null +++ b/tests/topotests/bgp_prefix_sid/exabgp.env @@ -0,0 +1,53 @@ + +[exabgp.api] +encoder = text +highres = false +respawn = false +socket = '' + +[exabgp.bgp] +openwait = 60 + +[exabgp.cache] +attributes = true +nexthops = true + +[exabgp.daemon] +daemonize = true +pid = '/var/run/exabgp/exabgp.pid' +user = 'exabgp' + +[exabgp.log] +all = false +configuration = true +daemon = true +destination = '/var/log/exabgp.log' +enable = true +level = INFO +message = false +network = true +packets = false +parser = false +processes = true +reactor = true +rib = false +routes = false +short = false +timers = false + +[exabgp.pdb] +enable = false + +[exabgp.profile] +enable = false +file = '' + +[exabgp.reactor] +speed = 1.0 + +[exabgp.tcp] +acl = false +bind = '' +delay = 0 +once = false +port = 179 diff --git a/tests/topotests/bgp_prefix_sid/peer1/exabgp.cfg b/tests/topotests/bgp_prefix_sid/peer1/exabgp.cfg new file mode 100644 index 0000000000..5b55366a0e --- /dev/null +++ b/tests/topotests/bgp_prefix_sid/peer1/exabgp.cfg @@ -0,0 +1,103 @@ +group controller { + neighbor 10.0.0.1 { + router-id 10.0.0.101; + local-address 10.0.0.101; + local-as 2; + peer-as 1; + + family { + ipv4 nlri-mpls; + } + + static { + # ref: draft-ietf-idr-bgp-prefix-sid-27 + # + # IANA temporarily assigned the following: + # attribute code type (suggested value: 40) to + # the BGP Prefix-SID attribute + # + # 0 1 2 3 + # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Type | Length | RESERVED | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Flags | Label Index | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Label Index | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # Figure. Label-Index TLV (Prefix-SID type-1) + # + # 0 1 2 3 + # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Type | Length | Flags | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Flags | + # +-+-+-+-+-+-+-+-+ + # + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | SRGB 1 (6 octets) | + # | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | SRGB n (6 octets) | + # | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # Figure. Originator SRGB TLV (Prefix-SID type-3) + + # ExaBGP generic-attribute binary pattern: + # Attribute-type: 0x28 (40:BGP_PREFIX_SID) + # Attribute-flag: 0xc0 (Option, Transitive) + # Attribute-body: Label-Index TLV and Originator SRGB TLV + # Label-Index TLV: 0x01000700000000000001 + # Type (08bit): 0x01 + # Length (16bit): 0x0007 + # RESERVED (08bit): 0x00 + # Flags (16bit): 0x0000 + # Label Index (32bit): 0x00000001 + # Originator SRGB TLV: 0x03000800000c350000000a + # Type (08bit): 0x03 + # Length (16bit): 0x0008 (nb-SRGB is 1) + # Flags (16bit): 0x0000 + # SRGB1 (48bit): 0x0c3500:0x00000a (800000-800010 is SRGB1) + route 3.0.0.1/32 next-hop 10.0.0.101 label [800001] attribute [0x28 0xc0 0x0100070000000000000103000800000c350000000a]; + + # ExaBGP generic-attribute binary pattern: + # Attribute-type: 0x28 (40:BGP_PREFIX_SID) + # Attribute-flag: 0xc0 (Option, Transitive) + # Attribute-body: Label-Index TLV and Originator SRGB TLV + # Label-Index TLV: 0x01000700000000000001 + # Type (08bit): 0x01 + # Length (16bit): 0x0007 + # RESERVED (08bit): 0x00 + # Flags (16bit): 0x0000 + # Label Index (32bit): 0x00000002 + # Originator SRGB TLV: 0x03000800000c350000000a + # Type (08bit): 0x03 + # Length (16bit): 0x0008 (nb-SRGB is 1) + # Flags (16bit): 0x0000 + # SRGB1 (48bit): 0x0c3500:0x00000a (800000-800010 is SRGB1) + route 3.0.0.2/32 next-hop 10.0.0.101 label [800002] attribute [0x28 0xc0 0x0100070000000000000203000800000c350000000a]; + + # ExaBGP generic-attribute binary pattern: + # Attribute-type: 0x28 (40:BGP_PREFIX_SID) + # Attribute-flag: 0xc0 (Option, Transitive) + # Attribute-body: Label-Index TLV and Originator SRGB TLV + # Label-Index TLV: 0x01000700000000000001 + # Type (08bit): 0x01 + # Length (16bit): 0x0007 + # RESERVED (08bit): 0x00 + # Flags (16bit): 0x0000 + # Label Index (32bit): 0x00000003 + # Originator SRGB TLV: 0x03000800000c350000000a + # Type (08bit): 0x03 + # Length (16bit): 0x0008 (nb-SRGB is 1) + # Flags (16bit): 0x0000 + # SRGB1 (48bit): 0x0c3500:0x00000a (800000-800010 is SRGB1) + route 3.0.0.3/32 next-hop 10.0.0.101 label [800003] attribute [0x28 0xc0 0x0100070000000000000303000800000c350000000a]; + } + } +} diff --git a/tests/topotests/bgp_prefix_sid/peer2/exa-receive.py b/tests/topotests/bgp_prefix_sid/peer2/exa-receive.py new file mode 100755 index 0000000000..eaa6a67872 --- /dev/null +++ b/tests/topotests/bgp_prefix_sid/peer2/exa-receive.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python + +""" +exa-receive.py: Save received routes form ExaBGP into file +""" + +from sys import stdin,argv +from datetime import datetime + +# 1st arg is peer number +peer = int(argv[1]) + +# When the parent dies we are seeing continual newlines, so we only access so many before stopping +counter = 0 + +routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') + +while True: + try: + line = stdin.readline() + routesavefile.write(line) + routesavefile.flush() + + if line == "": + counter += 1 + if counter > 100: + break + continue + + counter = 0 + except KeyboardInterrupt: + pass + except IOError: + # most likely a signal during readline + pass + +routesavefile.close() diff --git a/tests/topotests/bgp_prefix_sid/peer2/exabgp.cfg b/tests/topotests/bgp_prefix_sid/peer2/exabgp.cfg new file mode 100644 index 0000000000..dabd88e03d --- /dev/null +++ b/tests/topotests/bgp_prefix_sid/peer2/exabgp.cfg @@ -0,0 +1,19 @@ +group controller { + + process receive-routes { + run "/etc/exabgp/exa-receive.py 2"; + receive-routes; + encoder json; + } + + neighbor 10.0.0.1 { + router-id 10.0.0.102; + local-address 10.0.0.102; + local-as 3; + peer-as 1; + + family { + ipv4 nlri-mpls; + } + } +} diff --git a/tests/topotests/bgp_prefix_sid/r1/bgpd.conf b/tests/topotests/bgp_prefix_sid/r1/bgpd.conf new file mode 100644 index 0000000000..7a38cc307f --- /dev/null +++ b/tests/topotests/bgp_prefix_sid/r1/bgpd.conf @@ -0,0 +1,15 @@ +log stdout notifications +log monitor notifications +log commands +! +router bgp 1 + bgp router-id 10.0.0.1 + no bgp default ipv4-unicast + neighbor 10.0.0.101 remote-as 2 + neighbor 10.0.0.102 remote-as 3 + ! + address-family ipv4 labeled-unicast + neighbor 10.0.0.101 activate + neighbor 10.0.0.102 activate + exit-address-family +! diff --git a/tests/topotests/bgp_prefix_sid/r1/zebra.conf b/tests/topotests/bgp_prefix_sid/r1/zebra.conf new file mode 100644 index 0000000000..0cd26052f2 --- /dev/null +++ b/tests/topotests/bgp_prefix_sid/r1/zebra.conf @@ -0,0 +1,7 @@ +hostname r1 +! +interface r1-eth0 + ip address 10.0.0.1/24 + no shutdown +! +line vty diff --git a/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py b/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py new file mode 100755 index 0000000000..dc203cabc5 --- /dev/null +++ b/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python + +# +# test_bgp_prefix_sid.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by LINE Corporation +# Copyright (c) 2020 by Hiroki Shirokura +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bgp_prefix_sid.py: Test BGP topology with EBGP on prefix-sid +""" + +import json +import os +import sys +import functools +import pytest + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo + + +class TemplateTopo(Topo): + def build(self, **_opts): + tgen = get_topogen(self) + router = tgen.add_router('r1') + switch = tgen.add_switch('s1') + switch.add_link(router) + + switch = tgen.gears['s1'] + peer1 = tgen.add_exabgp_peer('peer1', ip='10.0.0.101', defaultRoute='via 10.0.0.1') + peer2 = tgen.add_exabgp_peer('peer2', ip='10.0.0.102', defaultRoute='via 10.0.0.1') + switch.add_link(peer1) + switch.add_link(peer2) + + +def setup_module(module): + tgen = Topogen(TemplateTopo, module.__name__) + tgen.start_topology() + + router = tgen.gears['r1'] + router.load_config(TopoRouter.RD_ZEBRA, os.path.join(CWD, '{}/zebra.conf'.format('r1'))) + router.load_config(TopoRouter.RD_BGP, os.path.join(CWD, '{}/bgpd.conf'.format('r1'))) + router.start() + + logger.info('starting exaBGP on peer1') + peer_list = tgen.exabgp_peers() + for pname, peer in peer_list.iteritems(): + peer_dir = os.path.join(CWD, pname) + env_file = os.path.join(CWD, 'exabgp.env') + logger.info('Running ExaBGP peer') + peer.start(peer_dir, env_file) + logger.info(pname) + + +def teardown_module(module): + tgen = get_topogen() + tgen.stop_topology() + + +def test_r1_receive_and_advertise_prefix_sid_type1(): + tgen = get_topogen() + router = tgen.gears['r1'] + + def _check_type1_r1(router, prefix, remoteLabel, labelIndex): + output = router.vtysh_cmd('show bgp ipv4 labeled-unicast {} json'.format(prefix)) + output = json.loads(output) + expected = { + 'prefix': prefix, + 'advertisedTo': { '10.0.0.101':{}, '10.0.0.102':{} }, + 'paths': [{ + 'valid':True, + 'remoteLabel': remoteLabel, + 'labelIndex': labelIndex, + }] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_check_type1_r1, router, '3.0.0.1/32', 800001, 1) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result is None, 'Failed _check_type1_r1 in "{}"'.format(router) + + test_func = functools.partial(_check_type1_r1, router, '3.0.0.2/32', 800002, 2) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result is None, 'Failed _check_type1_r1 in "{}"'.format(router) + + +def exabgp_get_update_prefix(filename, afi, nexthop, prefix): + with open('/tmp/peer2-received.log') as f: + for line in f.readlines(): + output = json.loads(line) + ret = output.get('neighbor') + if ret is None: + continue + ret = ret.get('message') + if ret is None: + continue + ret = ret.get('update') + if ret is None: + continue + ret = ret.get('announce') + if ret is None: + continue + ret = ret.get(afi) + if ret is None: + continue + ret = ret.get(nexthop) + if ret is None: + continue + ret = ret.get(prefix) + if ret is None: + continue + return output + return "Not found" + + +def test_peer2_receive_prefix_sid_type1(): + tgen = get_topogen() + peer2 = tgen.gears['peer2'] + + def _check_type1_peer2(prefix, labelindex): + output = exabgp_get_update_prefix('/tmp/peer2-received.log', 'ipv4 nlri-mpls', '10.0.0.101', prefix) + expected = { + 'type': 'update', + 'neighbor': { + 'ip': '10.0.0.1', + 'message': { + 'update': { + 'attribute': { + 'attribute-0x28-0xE0': '0x010007000000{:08x}'.format(labelindex) + }, + 'announce': { 'ipv4 nlri-mpls': { '10.0.0.101': {} } } + } + } + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_check_type1_peer2, '3.0.0.1/32', labelindex=1) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result is None, 'Failed _check_type1_peer2 in "{}"'.format('peer2') + + test_func = functools.partial(_check_type1_peer2, '3.0.0.2/32', labelindex=2) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result is None, 'Failed _check_type1_peer2 in "{}"'.format('peer2') + + +if __name__ == '__main__': + args = ["-s"] + sys.argv[1:] + ret = pytest.main(args) + sys.exit(ret) -- 2.39.5