diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:53:30 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:53:30 +0000 |
commit | 2c7cac91ed6e7db0f6937923d2b57f97dbdbc337 (patch) | |
tree | c05dc0f8e6aa3accc84e3e5cffc933ed94941383 /tests/topotests/bgp_peer_type_multipath_relax | |
parent | Initial commit. (diff) | |
download | frr-2c7cac91ed6e7db0f6937923d2b57f97dbdbc337.tar.xz frr-2c7cac91ed6e7db0f6937923d2b57f97dbdbc337.zip |
Adding upstream version 8.4.4.upstream/8.4.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests/topotests/bgp_peer_type_multipath_relax')
25 files changed, 1020 insertions, 0 deletions
diff --git a/tests/topotests/bgp_peer_type_multipath_relax/exabgp.env b/tests/topotests/bgp_peer_type_multipath_relax/exabgp.env new file mode 100644 index 0000000..6c554f5 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/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_peer_type_multipath_relax/peer1/exa_readpipe.py b/tests/topotests/bgp_peer_type_multipath_relax/peer1/exa_readpipe.py new file mode 100644 index 0000000..0f998c1 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer1/exa_readpipe.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python2 +"Helper script to read api commands from a pipe and feed them to ExaBGP" + +import sys + +if len(sys.argv) != 2: + sys.exit(1) +fifo = sys.argv[1] + +while True: + pipe = open(fifo, "r") + with pipe: + line = pipe.readline().strip() + if line != "": + sys.stdout.write("{}\n".format(line)) + sys.stdout.flush() + pipe.close() + +sys.exit(0) diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer1/exabgp.cfg b/tests/topotests/bgp_peer_type_multipath_relax/peer1/exabgp.cfg new file mode 100644 index 0000000..4a7dc48 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer1/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer1.in"; + encoder text; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 1"; + receive-routes; + encoder text; + } + + neighbor 10.0.1.1 { + router-id 10.0.1.2; + local-address 10.0.1.2; + local-as 64510; + peer-as 64510; + } + +} diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer2/exa_readpipe.py b/tests/topotests/bgp_peer_type_multipath_relax/peer2/exa_readpipe.py new file mode 100644 index 0000000..0f998c1 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer2/exa_readpipe.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python2 +"Helper script to read api commands from a pipe and feed them to ExaBGP" + +import sys + +if len(sys.argv) != 2: + sys.exit(1) +fifo = sys.argv[1] + +while True: + pipe = open(fifo, "r") + with pipe: + line = pipe.readline().strip() + if line != "": + sys.stdout.write("{}\n".format(line)) + sys.stdout.flush() + pipe.close() + +sys.exit(0) diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer2/exabgp.cfg b/tests/topotests/bgp_peer_type_multipath_relax/peer2/exabgp.cfg new file mode 100644 index 0000000..b53b054 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer2/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer2.in"; + encoder text; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 2"; + receive-routes; + encoder text; + } + + neighbor 10.0.2.1 { + router-id 10.0.2.2; + local-address 10.0.2.2; + local-as 64511; + peer-as 64511; + } + +} diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer3/exa_readpipe.py b/tests/topotests/bgp_peer_type_multipath_relax/peer3/exa_readpipe.py new file mode 100644 index 0000000..0f998c1 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer3/exa_readpipe.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python2 +"Helper script to read api commands from a pipe and feed them to ExaBGP" + +import sys + +if len(sys.argv) != 2: + sys.exit(1) +fifo = sys.argv[1] + +while True: + pipe = open(fifo, "r") + with pipe: + line = pipe.readline().strip() + if line != "": + sys.stdout.write("{}\n".format(line)) + sys.stdout.flush() + pipe.close() + +sys.exit(0) diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer3/exabgp.cfg b/tests/topotests/bgp_peer_type_multipath_relax/peer3/exabgp.cfg new file mode 100644 index 0000000..6a1cc2f --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer3/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer3.in"; + encoder text; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 3"; + receive-routes; + encoder text; + } + + neighbor 10.0.3.1 { + router-id 10.0.3.2; + local-address 10.0.3.2; + local-as 64502; + peer-as 64501; + } + +} diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer4/exa_readpipe.py b/tests/topotests/bgp_peer_type_multipath_relax/peer4/exa_readpipe.py new file mode 100644 index 0000000..0f998c1 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer4/exa_readpipe.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python2 +"Helper script to read api commands from a pipe and feed them to ExaBGP" + +import sys + +if len(sys.argv) != 2: + sys.exit(1) +fifo = sys.argv[1] + +while True: + pipe = open(fifo, "r") + with pipe: + line = pipe.readline().strip() + if line != "": + sys.stdout.write("{}\n".format(line)) + sys.stdout.flush() + pipe.close() + +sys.exit(0) diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer4/exabgp.cfg b/tests/topotests/bgp_peer_type_multipath_relax/peer4/exabgp.cfg new file mode 100644 index 0000000..2cc26cb --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/peer4/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer4.in"; + encoder text; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 4"; + receive-routes; + encoder text; + } + + neighbor 10.0.4.1 { + router-id 10.0.4.2; + local-address 10.0.4.2; + local-as 64503; + peer-as 64501; + } + +} diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/bgpd.conf b/tests/topotests/bgp_peer_type_multipath_relax/r1/bgpd.conf new file mode 100644 index 0000000..038f108 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/bgpd.conf @@ -0,0 +1,16 @@ +! +router bgp 64510 + bgp router-id 10.0.1.1 + no bgp ebgp-requires-policy + bgp confederation identifier 64501 + bgp confederation peers 64511 + bgp bestpath as-path multipath-relax + bgp bestpath compare-routerid + bgp bestpath peer-type multipath-relax + neighbor 10.0.1.2 remote-as 64510 + neighbor 10.0.3.2 remote-as 64502 + neighbor 10.0.4.2 remote-as 64503 + neighbor 10.0.5.2 remote-as 64511 +! +line vty +! diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/multipath.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/multipath.json new file mode 100644 index 0000000..11dad78 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/multipath.json @@ -0,0 +1,50 @@ +{ + "routes": { "203.0.113.0/30": [ + { + "valid":true, + "multipath":true, + "pathFrom":"external", + "peerId":"10.0.5.2" + }, + { + "valid":true, + "bestpath":true, + "selectionReason":"Peer Type", + "pathFrom":"external", + "peerId":"10.0.4.2" + }, + { + "valid":true, + "multipath":true, + "pathFrom":"internal", + "peerId":"10.0.1.2" + } +],"203.0.113.4/30": [ + { + "valid":true, + "bestpath":true, + "selectionReason":"Confed Peer Type", + "pathFrom":"external", + "peerId":"10.0.5.2" + }, + { + "valid":true, + "multipath":true, + "pathFrom":"internal", + "peerId":"10.0.1.2" + } +],"203.0.113.8/30": [ + { + "valid":true, + "multipath":true, + "pathFrom":"external", + "peerId":"10.0.4.2" + }, + { + "valid":true, + "bestpath":true, + "selectionReason":"Router ID", + "pathFrom":"external", + "peerId":"10.0.3.2" + } +] } } diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/not-multipath.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/not-multipath.json new file mode 100644 index 0000000..c621832 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/not-multipath.json @@ -0,0 +1,50 @@ +{ + "routes": { "203.0.113.0/30": [ + { + "valid":true, + "multipath":null, + "pathFrom":"external", + "peerId":"10.0.5.2" + }, + { + "valid":true, + "bestpath":true, + "selectionReason":"Peer Type", + "pathFrom":"external", + "peerId":"10.0.4.2" + }, + { + "valid":true, + "multipath":null, + "pathFrom":"internal", + "peerId":"10.0.1.2" + } +],"203.0.113.4/30": [ + { + "valid":true, + "bestpath":true, + "selectionReason":"Confed Peer Type", + "pathFrom":"external", + "peerId":"10.0.5.2" + }, + { + "valid":true, + "multipath":null, + "pathFrom":"internal", + "peerId":"10.0.1.2" + } +],"203.0.113.8/30": [ + { + "valid":true, + "multipath":true, + "pathFrom":"external", + "peerId":"10.0.4.2" + }, + { + "valid":true, + "bestpath":true, + "selectionReason":"Router ID", + "pathFrom":"external", + "peerId":"10.0.3.2" + } +] } } diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-eBGP-confed.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-eBGP-confed.json new file mode 100644 index 0000000..22ec2c2 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-eBGP-confed.json @@ -0,0 +1,33 @@ +{ + "203.0.113.0\/30":[ + { + "prefix":"203.0.113.0\/30", + "protocol":"bgp", + "installed":true, + "internalNextHopNum":4, + "internalNextHopActiveNum":4, + "nexthops":[ + { + "ip":"198.51.100.2", + "active":true, + "recursive":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "active":true + }, + { + "ip":"198.51.100.10", + "active":true, + "recursive":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-eBGP-iBGP.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-eBGP-iBGP.json new file mode 100644 index 0000000..facddcd --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-eBGP-iBGP.json @@ -0,0 +1,33 @@ +{ + "203.0.113.0\/30":[ + { + "prefix":"203.0.113.0\/30", + "protocol":"bgp", + "installed":true, + "internalNextHopNum":4, + "internalNextHopActiveNum":4, + "nexthops":[ + { + "ip":"198.51.100.1", + "active":true, + "recursive":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "active":true + }, + { + "ip":"198.51.100.10", + "active":true, + "recursive":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-no-recursive.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-no-recursive.json new file mode 100644 index 0000000..5399cee --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-no-recursive.json @@ -0,0 +1,35 @@ +{ + "prefix":"203.0.113.0\/30", + "paths":[ + { + "valid":false, + "peer":{ + "peerId":"10.0.4.2", + "routerId":"10.0.4.2", + "type":"external" + } + }, + { + "valid":true, + "multipath":true, + "bestpath":{ + "overall":true, + "selectionReason":"Confed Peer Type" + }, + "peer":{ + "peerId":"10.0.5.2", + "routerId":"10.0.5.2", + "type":"confed-external" + } + }, + { + "valid":true, + "multipath":true, + "peer":{ + "peerId":"10.0.1.2", + "routerId":"10.0.1.2", + "type":"confed-internal" + } + } + ] +} diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-recursive.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-recursive.json new file mode 100644 index 0000000..7da95ae --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-recursive.json @@ -0,0 +1,36 @@ +{ + "prefix":"203.0.113.0\/30", + "paths":[ + { + "valid":true, + "multipath":true, + "bestpath":{ + "overall":true, + "selectionReason":"Peer Type" + }, + "peer":{ + "peerId":"10.0.4.2", + "routerId":"10.0.4.2", + "type":"external" + } + }, + { + "valid":true, + "multipath":true, + "peer":{ + "peerId":"10.0.5.2", + "routerId":"10.0.5.2", + "type":"confed-external" + } + }, + { + "valid":true, + "multipath":true, + "peer":{ + "peerId":"10.0.1.2", + "routerId":"10.0.1.2", + "type":"confed-internal" + } + } + ] +} diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1.json new file mode 100644 index 0000000..a90669a --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1.json @@ -0,0 +1,33 @@ +{ + "prefix":"203.0.113.0\/30", + "paths":[ + { + "multipath":true, + "peer":{ + "peerId":"10.0.5.2", + "routerId":"10.0.5.2", + "type":"confed-external" + } + }, + { + "multipath":true, + "bestpath":{ + "overall":true, + "selectionReason":"Peer Type" + }, + "peer":{ + "peerId":"10.0.4.2", + "routerId":"10.0.4.2", + "type":"external" + } + }, + { + "multipath":true, + "peer":{ + "peerId":"10.0.1.2", + "routerId":"10.0.1.2", + "type":"confed-internal" + } + } + ] +} diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-ip-route.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-ip-route.json new file mode 100644 index 0000000..1bf38ef --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-ip-route.json @@ -0,0 +1,23 @@ +{ + "203.0.113.8\/30":[ + { + "prefix":"203.0.113.8\/30", + "protocol":"bgp", + "installed":true, + "internalNextHopNum":2, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "fib":true, + "ip":"10.0.3.2", + "active":true + }, + { + "fib":null, + "ip":"198.51.100.10", + "active":null + } + ] + } + ] +} diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-no-recursive.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-no-recursive.json new file mode 100644 index 0000000..33d0f2d --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-no-recursive.json @@ -0,0 +1,21 @@ +{ + "prefix":"203.0.113.8\/30", + "paths":[ + { + "valid":false, + "peer":{ + "peerId":"10.0.4.2", + "routerId":"10.0.4.2", + "type":"external" + } + }, + { + "valid":true, + "peer":{ + "peerId":"10.0.3.2", + "routerId":"10.0.3.2", + "type":"external" + } + } + ] +} diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-recursive.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-recursive.json new file mode 100644 index 0000000..6ac2512 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-recursive.json @@ -0,0 +1,23 @@ +{ + "prefix":"203.0.113.8\/30", + "paths":[ + { + "valid":true, + "multipath":true, + "peer":{ + "peerId":"10.0.4.2", + "routerId":"10.0.4.2", + "type":"external" + } + }, + { + "valid":true, + "multipath":true, + "peer":{ + "peerId":"10.0.3.2", + "routerId":"10.0.3.2", + "type":"external" + } + } + ] +} diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/zebra.conf b/tests/topotests/bgp_peer_type_multipath_relax/r1/zebra.conf new file mode 100644 index 0000000..911aa1c --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/zebra.conf @@ -0,0 +1,27 @@ +! +hostname r1 +! +interface r1-eth0 + description ExaBGP iBGP peer1 + ip address 10.0.1.1/24 + no link-detect +! +interface r1-eth1 + description ExaBGP peer3 + ip address 10.0.3.1/24 + no link-detect +! +interface r1-eth2 + description ExaBGP peer4 + ip address 10.0.4.1/24 + no link-detect +! +interface r1-eth3 + description r2 confed peer + ip address 10.0.5.1/24 + no link-detect +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r2/bgpd.conf b/tests/topotests/bgp_peer_type_multipath_relax/r2/bgpd.conf new file mode 100644 index 0000000..2362a19 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r2/bgpd.conf @@ -0,0 +1,19 @@ +! +!log file bgpd.log +! +router bgp 64511 + bgp confederation identifier 64501 + bgp confederation peers 64510 + bgp router-id 10.0.5.2 + no bgp ebgp-requires-policy + neighbor 10.0.2.2 remote-as 64511 + neighbor 10.0.5.1 remote-as 64510 + ! + address-family ipv4 unicast + neighbor 10.0.5.1 route-map dropall in + exit-address-family +! +route-map dropall deny 10 +! +line vty +! diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r2/staticd.conf b/tests/topotests/bgp_peer_type_multipath_relax/r2/staticd.conf new file mode 100644 index 0000000..35ebe0d --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r2/staticd.conf @@ -0,0 +1,4 @@ +hostname r2 +! +ip route 198.51.100.0/24 10.0.2.2 +! diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r2/zebra.conf b/tests/topotests/bgp_peer_type_multipath_relax/r2/zebra.conf new file mode 100644 index 0000000..900e7d4 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/r2/zebra.conf @@ -0,0 +1,19 @@ +! +! +hostname r2 +! +interface r2-eth0 + description ExaBGP peer + ip address 10.0.2.1/24 + no link-detect +! +interface r2-eth1 + description r1 confed peer + ip address 10.0.5.2/24 + no link-detect +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/bgp_peer_type_multipath_relax/test_bgp_peer-type_multipath-relax.py b/tests/topotests/bgp_peer_type_multipath_relax/test_bgp_peer-type_multipath-relax.py new file mode 100755 index 0000000..8321a57 --- /dev/null +++ b/tests/topotests/bgp_peer_type_multipath_relax/test_bgp_peer-type_multipath-relax.py @@ -0,0 +1,385 @@ +#!/usr/bin/env python + +# +# Part of NetDEF Topology Tests +# +# Copyright (c) 2021 Arista Networks, Inc. +# +# 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 Arista Networks 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_peer-type_multipath-relax.py: + +Test the effects of the "bgp bestpath peer-type multipath-relax" command + +- enabling the command allows eBGP, iBGP, and confed routes to be multipath +- the choice of best path is not affected +- disabling the command removes iBGP/confed routes from multipath +- enabling the command does not forgive eBGP routes of the requirement + (when enabled) that next hops resolve over connected routes +- a mixed-type multipath next hop, when published to zebra, does not + require resolving next hops over connected routes +- with the command enabled, an all-eBGP multipath next hop still requires + resolving next hops over connected routes when published to zebra + +Topology used by the test: + + eBGP +------+ iBGP + peer1 ---- | r1 | ---- peer3 + | | +peer2 ---- r2 ---- | | ---- peer4 + iBGP confed +------+ eBGP + +r2 is present in this topology because ExaBGP does not currently support +confederations so we use FRR to advertise the required AS_CONFED_SEQUENCE. + +Routes are advertised from different peers to form interesting multipaths. + + peer1 peer2 peer3 peer4 multipath on r1 + +203.0.113.0/30 x x x all 3 +203.0.113.4/30 x x confed-iBGP +203.0.113.8/30 x x eBGP-only + +There is also a BGP-advertised route used only for recursively resolving +next hops. +""" + +import functools +import json +import os +import pytest +import sys + +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 + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + + +def build_topo(tgen): + "Build function" + + # Set up routers + tgen.add_router("r1") # DUT + tgen.add_router("r2") + + # Set up peers + for peern in range(1, 5): + peer = tgen.add_exabgp_peer( + "peer{}".format(peern), + ip="10.0.{}.2/24".format(peern), + defaultRoute="via 10.0.{}.1".format(peern), + ) + if peern == 2: + tgen.add_link(tgen.gears["r2"], peer) + else: + tgen.add_link(tgen.gears["r1"], peer) + tgen.add_link(tgen.gears["r1"], tgen.gears["r2"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + # For all registered routers, load the zebra configuration file + for rname, router in tgen.routers().items(): + router.run("/bin/bash {}/setup_vrfs".format(CWD)) + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # After loading the configurations, this function loads configured daemons. + tgen.start_router() + + # Start up exabgp peers + peers = tgen.exabgp_peers() + for peer in peers: + fifo_in = "/var/run/exabgp_{}.in".format(peer) + if os.path.exists(fifo_in): + os.remove(fifo_in) + os.mkfifo(fifo_in, 0o777) + logger.info("Starting ExaBGP on peer {}".format(peer)) + peer_dir = os.path.join(CWD, peer) + env_file = os.path.join(CWD, "exabgp.env") + peers[peer].start(peer_dir, env_file) + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def test_bgp_peer_type_multipath_relax(): + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def exabgp_cmd(peer, cmd): + pipe = open("/run/exabgp_{}.in".format(peer), "w") + with pipe: + pipe.write(cmd) + pipe.close() + + # Prefixes used in the test + prefix1 = "203.0.113.0/30" + prefix2 = "203.0.113.4/30" + prefix3 = "203.0.113.8/30" + # Next hops used for iBGP/confed routes + resolved_nh1 = "198.51.100.1" + resolved_nh2 = "198.51.100.2" + # BGP route used for recursive resolution + bgp_resolving_prefix = "198.51.100.0/24" + # Next hop that will require non-connected recursive resolution + ebgp_resolved_nh = "198.51.100.10" + + # Send a non-connected route to resolve others + exabgp_cmd( + "peer3", "announce route {} next-hop self\n".format(bgp_resolving_prefix) + ) + router = tgen.gears["r1"] + + # It seems that if you write to the exabgp socket too quickly in + # succession, requests get lost. So verify prefix1 now instead of + # after all the prefixes are advertised. + logger.info("Create and verify mixed-type multipaths") + exabgp_cmd( + "peer1", + "announce route {} next-hop {} as-path [ 64499 ]\n".format( + prefix1, resolved_nh1 + ), + ) + exabgp_cmd( + "peer2", + "announce route {} next-hop {} as-path [ 64499 ]\n".format( + prefix1, resolved_nh2 + ), + ) + exabgp_cmd("peer4", "announce route {} next-hop self\n".format(prefix1)) + reffile = os.path.join(CWD, "r1/prefix1.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip bgp {} json".format(prefix1), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "Mixed-type multipath not found" + assert res is None, assertMsg + + logger.info("Create and verify eBGP and iBGP+confed multipaths") + exabgp_cmd( + "peer1", + "announce route {} next-hop {} as-path [ 64499 ]\n".format( + prefix2, resolved_nh1 + ), + ) + exabgp_cmd( + "peer2", + "announce route {} next-hop {} as-path [ 64499 ]\n".format( + prefix2, resolved_nh2 + ), + ) + exabgp_cmd("peer3", "announce route {} next-hop self".format(prefix3)) + exabgp_cmd("peer4", "announce route {} next-hop self".format(prefix3)) + reffile = os.path.join(CWD, "r1/multipath.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "Not all expected multipaths found" + assert res is None, assertMsg + + logger.info("Toggle peer-type multipath-relax and verify the changes") + router.vtysh_cmd( + "conf\n router bgp 64510\n no bgp bestpath peer-type multipath-relax\n" + ) + # This file verifies "multipath" is not set + reffile = os.path.join(CWD, "r1/not-multipath.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "Disabling peer-type multipath-relax did not take effect" + assert res is None, assertMsg + + router.vtysh_cmd( + "conf\n router bgp 64510\n bgp bestpath peer-type multipath-relax\n" + ) + reffile = os.path.join(CWD, "r1/multipath.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "Reenabling peer-type multipath-relax did not take effect" + assert res is None, assertMsg + + logger.info("Check recursive resolution of eBGP next hops is not affected") + # eBGP next hop resolution rejects recursively resolved next hops by + # default, even with peer-type multipath-relax + exabgp_cmd( + "peer4", "announce route {} next-hop {}\n".format(prefix3, ebgp_resolved_nh) + ) + reffile = os.path.join(CWD, "r1/prefix3-no-recursive.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip bgp {} json".format(prefix3), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "Recursive eBGP next hop not as expected for {}".format(prefix3) + assert res is None, assertMsg + + exabgp_cmd( + "peer4", "announce route {} next-hop {}\n".format(prefix1, ebgp_resolved_nh) + ) + reffile = os.path.join(CWD, "r1/prefix1-no-recursive.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip bgp {} json".format(prefix1), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "Recursive eBGP next hop not as expected for {}".format(prefix1) + assert res is None, assertMsg + + # When other config allows recursively resolved eBGP next hops, + # such next hops in all-eBGP multipaths should be valid + router.vtysh_cmd("conf\n router bgp 64510\n neighbor 10.0.4.2 ebgp-multihop\n") + reffile = os.path.join(CWD, "r1/prefix3-recursive.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip bgp {} json".format(prefix3), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "Recursive eBGP next hop not as expected for {}".format(prefix3) + assert res is None, assertMsg + + reffile = os.path.join(CWD, "r1/prefix1-recursive.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip bgp {} json".format(prefix1), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "Recursive eBGP next hop not as expected for {}".format(prefix1) + assert res is None, assertMsg + + logger.info("Check mixed-type multipath next hop recursive resolution in FIB") + # There are now two eBGP-learned routes with a recursively resolved next; + # hop; one is all-eBGP multipath, and the other is iBGP/eBGP/ + # confed-external. The peer-type multipath-relax feature only enables + # recursive resolution in FIB if any next hop is iBGP/confed-learned. The + # all-eBGP multipath will have only one valid next hop in the FIB. + reffile = os.path.join(CWD, "r1/prefix3-ip-route.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip route {} json".format(prefix3), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "FIB next hops mismatch for all-eBGP multipath" + assert res is None, assertMsg + + # check confed-external enables recursively resolved next hops by itself + exabgp_cmd( + "peer1", + "withdraw route {} next-hop {} as-path [ 64499 ]\n".format( + prefix1, resolved_nh1 + ), + ) + reffile = os.path.join(CWD, "r1/prefix1-eBGP-confed.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip route {} json".format(prefix1), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "FIB next hops mismatch for eBGP+confed-external multipath" + assert res is None, assertMsg + + # check iBGP by itself + exabgp_cmd( + "peer1", + "announce route {} next-hop {} as-path [ 64499 ]\n".format( + prefix1, resolved_nh1 + ), + ) + exabgp_cmd( + "peer2", + "withdraw route {} next-hop {} as-path [ 64499 ]\n".format( + prefix1, resolved_nh2 + ), + ) + reffile = os.path.join(CWD, "r1/prefix1-eBGP-iBGP.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip route {} json".format(prefix1), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "FIB next hops mismatch for eBGP+iBGP multipath" + assert res is None, assertMsg + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) |