diff --git a/board/x86_64/board.mk b/board/x86_64/board.mk index a2aa3301..61f662d2 100644 --- a/board/x86_64/board.mk +++ b/board/x86_64/board.mk @@ -7,7 +7,7 @@ test-env = $(test-dir)/env \ -p $(BINARIES_DIR)/infix-x86_64.pkg \ $(1) $(2) -test-env-qeneth = $(call test-env,-q $(test-dir)/virt/dual,$(1)) +test-env-qeneth = $(call test-env,-q $(test-dir)/virt/quad,$(1)) test-env-run = $(call test-env,-C -t $(BINARIES_DIR)/qemu.dot,$(1)) .PHONY: test-% diff --git a/test/case/ietf_routing/ospf_multiarea.py b/test/case/ietf_routing/ospf_multiarea.py new file mode 100755 index 00000000..948405f6 --- /dev/null +++ b/test/case/ietf_routing/ospf_multiarea.py @@ -0,0 +1,660 @@ +#!/usr/bin/env python3 +# +# +# 10.0.0.1/32 (lo) +# | +# +------+---------+ .1 10.0.12.1/30 .2+--------------------+ +# | R1 +-----------------------------+ R2 | +# | | AREA0 | +-10.0.0.2/32 (lo) +# +-------+--------+--.1 .1 -+--------+-----------+ +# .2 | \--- ---/ |.1 +# | \--- ----/ | +# 10.0.41.0 /30 | AREA2 \--- ---/ | 10.0.23.0/30 +# | 10.0.24.0/30 ---/\--- 10.0.13.0/30 | +# | ----/ \--- AREA1 | +# .1 | .2 ---/ \--- .2 |.2 +# +-------+--------+-/ \-+--------+----------+ +# | R4 .2 | | R3 | +# | +---------+ .1| +-10.0.0.3/32 (lo) +# +------+---------+.1 | 192.168.4.0/24 +-------------------+ +# | |.2 +# 10.0.0.4/32 (lo) +-------+ +# | | +# | PC | +# | | +# +-------+ +# +# +import infamy + +import infamy.route as route +from infamy.util import until +def config_target1(target, ring1, ring2, cross, link): + target.put_config_dict("ietf-interfaces", { + "interfaces": { + "interface": [ + { + "name": ring1, + "type": "infix-if-type:ethernet", + "enabled": True, + "ipv4": { + "forwarding": True, + "address": [{ + "ip": "10.0.12.1", + "prefix-length": 30 + }]} + }, + { + "name": ring2, + "type": "infix-if-type:ethernet", + "enabled": True, + "ipv4": { + "forwarding": True, + "address": [{ + "ip": "10.0.41.2", + "prefix-length": 30 + }]} + }, + { + "name": link, + "type": "infix-if-type:ethernet", + "enabled": True, + "ipv4": { + "forwarding": True, + "address": [{ + "ip": "192.168.1.1", + "prefix-length": 24 + }, { + "ip": "11.0.8.1", + "prefix-length": 24 + }] + } + }, + { + "name": cross, + "type": "infix-if-type:ethernet", + "enabled": True, + "ipv4": { + "forwarding": True, + "address": [{ + "ip": "10.0.13.1", + "prefix-length": 30 + }] + } + }, + { + "name": "lo", + "type": "infix-if-type:ethernet", + "enabled": True, + "ipv4": { + "address": [{ + "ip": "10.0.0.1", + "prefix-length": 32 + }] + } + } + ] + } + }) + + target.put_config_dict("ietf-routing", { + "routing": { + "control-plane-protocols": { + "control-plane-protocol": [{ + "type": "ietf-ospf:ospfv2", + "name": "default", + "ospf": { + "explicit-router-id": "10.0.0.1", + "areas": { + "area": [{ + "area-id": "0.0.0.0", + "interfaces": + { + "interface": [{ + "bfd": { + "enabled": True + }, + "name": ring1, + "hello-interval": 1, + "enabled": True + },{ + "name": link, + "passive": True, + "enabled": True + }] + } + },{ + "area-id": "0.0.0.1", + "area-type": "nssa-area", + "summary": False, + "interfaces": + { + "interface": [{ + "bfd": { + "enabled": True + }, + "name": cross, + "hello-interval": 1, + "enabled": True, + "cost": 2000 + },{ + "name": "lo", + "enabled": True + + }] + } + },{ + "area-id": "0.0.0.2", + "interfaces": + { + "interface": [{ + "bfd": { + "enabled": True + }, + "name": ring2, + "hello-interval": 1, + "enabled": True + }, + { + "name": "lo", + "enabled": True + }] + } + }] + } + } + + }] + } + } + }) + target.put_config_dict("ietf-system", { + "system": { + "hostname": "R1" + } + }) + +def config_target2(target, ring1, ring2, cross, link): + target.put_config_dict("ietf-interfaces", { + "interfaces": { + "interface": [ + { + "name": ring1, + "type": "infix-if-type:ethernet", + "enabled": True, + "ipv4": { + "forwarding": True, + "address": [{ + "ip": "10.0.23.1", + "prefix-length": 30 + }]} + }, + { + "name": ring2, + "type": "infix-if-type:ethernet", + "enabled": True, + "ipv4": { + "forwarding": True, + "address": [{ + "ip": "10.0.12.2", + "prefix-length": 30 + }]} + }, + { + "name": cross, + "type": "infix-if-type:ethernet", + "enabled": True, + "ipv4": { + "forwarding": True, + "address": [{ + "ip": "10.0.24.1", + "prefix-length": 30 + }]} + }, + { + "name": link, + "type": "infix-if-type:ethernet", + "enabled": True, + "ipv4": { + "forwarding": True, + "address": [{ + "ip": "192.168.2.1", + "prefix-length": 24 + },{ + "ip": "11.0.9.1", + "prefix-length": 24 + + },{ + "ip": "11.0.10.1", + "prefix-length": 24 + },{ + "ip": "11.0.11.1", + "prefix-length": 24 + },{ + "ip": "11.0.12.1", + "prefix-length": 24 + },{ + "ip": "11.0.13.1", + "prefix-length": 24 + },{ + "ip": "11.0.14.1", + "prefix-length": 24 + },{ + "ip": "11.0.15.1", + "prefix-length": 24 + }] + } + }, + { + "name": "lo", + "type": "infix-if-type:ethernet", + "enabled": True, + "ipv4": { + "address": [{ + "ip": "10.0.0.2", + "prefix-length": 32 + }] + } + } + ] + } + }) + target.put_config_dict("ietf-system", { + "system": { + "hostname": "R2" + } + }) + + target.put_config_dict("ietf-routing", { + "routing": { + "control-plane-protocols": { + "control-plane-protocol": [{ + "type": "ietf-ospf:ospfv2", + "name": "default", + "ospf": { + "explicit-router-id": "10.0.0.2", + "areas": { + "area": [{ + "area-id": "0.0.0.0", + "interfaces":{ + "interface": [{ + "bfd": { + "enabled": True + }, + "name": ring2, + "hello-interval": 1, + "enabled": True + },{ + "name": "lo", + "enabled": True + },{ + "name": link, + "enabled": True, + "passive": True + }] + } + }, + { + "area-id": "0.0.0.1", + "area-type": "nssa-area", + "summary": False, + "interfaces":{ + "interface": [{ + "bfd": { + "enabled": True + }, + "name": ring1, + "hello-interval": 1, + },{ + "name": "lo", + "enabled": True + + }] + } + }, + { + "area-id": "0.0.0.2", + "interfaces":{ + "interface": [{ + "bfd": { + "enabled": True + }, + "name": cross, + "hello-interval": 1, + "cost": 2000 + },{ + "name": "lo", + "enabled": True + }] + } + }] + } + } + }] + } + } + }) + +def config_target3(target, ring1, ring2, cross, link): + target.put_config_dict("ietf-interfaces", { + "interfaces": { + "interface": [ + { + "name": ring2, + "type": "infix-if-type:ethernet", + "enabled": True, + "ipv4": { + "forwarding": True, + "address": [{ + "ip": "10.0.23.2", + "prefix-length": 30 + }] + } + }, + { + "name": link, + "type": "infix-if-type:ethernet", + "enabled": True, + "ipv4": { + "forwarding": True, + "address": [{ + "ip": "192.168.3.1", + "prefix-length": 24 + }] + } + }, + { + "name": cross, + "type": "infix-if-type:ethernet", + "enabled": True, + "ipv4": { + "forwarding": True, + "address": [{ + "ip": "10.0.13.2", + "prefix-length": 30 + }] + } + }, + { + "name": "lo", + "type": "infix-if-type:ethernet", + "enabled": True, + "ipv4": { + "address": [{ + "ip": "10.0.0.3", + "prefix-length": 32 + }] + } + } + ] + } + }) + + target.put_config_dict("ietf-system", { + "system": { + "hostname": "R3" + } + }) + + target.put_config_dict("ietf-routing", { + "routing": { + "control-plane-protocols": { + "control-plane-protocol": [{ + "type": "ietf-ospf:ospfv2", + "name": "default", + "ospf": { + "areas": { + "area": [{ + "area-id": "0.0.0.1", + "area-type": "nssa-area", + "summary": False, + "interfaces":{ + "interface": [{ + "bfd": { + "enabled": True + }, + "name": cross, + "hello-interval": 1, + "enabled": True, + "cost": 2000 + },{ + "bfd": { + "enabled": True + }, + "name": ring2, + "hello-interval": 1, + "enabled": True + },{ + "name": link, + "enabled": True, + "passive": True + + },{ + "name": "lo", + "enabled": True + }] + } + }] + } + } + }] + } + } + }) + + +def config_target4(target, ring1, ring2, cross, link): + target.put_config_dict("ietf-interfaces", { + "interfaces": { + "interface": [ + { + "name": ring1, + "type": "infix-if-type:ethernet", + "enabled": True, + "ipv4": { + "forwarding": True, + "address": [{ + "ip": "10.0.41.1", + "prefix-length": 30 + }]} + }, + { + "name": cross, + "type": "infix-if-type:ethernet", + "enabled": True, + "ipv4": { + "forwarding": True, + "address": [{ + "ip": "10.0.24.2", + "prefix-length": 30 + }]} + }, + { + "name": link, + "type": "infix-if-type:ethernet", + "enabled": True, + "ipv4": { + "forwarding": True, + "address": [{ + "ip": "192.168.4.1", + "prefix-length": 24 + }] + } + }, + { + "name": "lo", + "type": "infix-if-type:ethernet", + "enabled": True, + "ipv4": { + "address": [{ + "ip": "10.0.0.4", + "prefix-length": 32 + }] + } + } + ] + } + }) + + target.put_config_dict("ietf-system", { + "system": { + "hostname": "R4" + } + }) + + target.put_config_dict("ietf-routing", { + "routing": { + "control-plane-protocols": { + "control-plane-protocol": [ + { + "type": "ietf-ospf:ospfv2", + "name": "default", + "ospf": { + "redistribute": { + "redistribute": [ + { + "protocol": "connected" + }] + }, + "areas": { + "area": [{ + "area-id": "0.0.0.2", + "interfaces":{ + "interface": [{ + "bfd": { + "enabled": True + }, + "name": ring1, + "hello-interval": 1, + "enabled": True + },{ + "bfd": { + "enabled": True + }, + "name": cross, + "hello-interval": 1, + "enabled": True, + "cost": 5000 + },{ + "name": "lo", + "enabled": True + }] + } + }] + } + } + } + ] + } + } + }) + +def disable_link(target, link): + target.put_config_dict("ietf-interfaces", { + "interfaces": { + "interface": [ + { + "name": link, + "enabled": False + }] + } + }) + +with infamy.Test() as test: + with test.step("Initialize"): + env = infamy.Env(infamy.std_topology("ring-4-duts")) + dut1 = env.attach("dut1", "mgmt") + dut2 = env.attach("dut2", "mgmt") + dut3 = env.attach("dut3", "mgmt") + dut4 = env.attach("dut4", "mgmt") + + with test.step("Configure targets"): + _, dut1ring1 = env.ltop.xlate("dut1", "ring1") + _, dut1ring2 = env.ltop.xlate("dut1", "ring2") + _, dut2ring1 = env.ltop.xlate("dut2", "ring1") + _, dut2ring2 = env.ltop.xlate("dut2", "ring2") + _, dut3ring1 = env.ltop.xlate("dut3", "ring1") + _, dut3ring2 = env.ltop.xlate("dut3", "ring2") + _, dut4ring1 = env.ltop.xlate("dut4", "ring1") + _, dut4ring2 = env.ltop.xlate("dut4", "ring2") + _, dut1data = env.ltop.xlate("dut1", "data") + _, dut2data = env.ltop.xlate("dut2", "data") + _, dut3data = env.ltop.xlate("dut3", "data") + _, dut4data = env.ltop.xlate("dut4", "data") + + _, dut1cross = env.ltop.xlate("dut1", "cross") + _, dut2cross = env.ltop.xlate("dut2", "cross") + _, dut3cross = env.ltop.xlate("dut3", "cross") + _, dut4cross = env.ltop.xlate("dut4", "cross") + + config_target1(dut1, dut1ring1, dut1ring2, dut1cross, dut1data) + config_target2(dut2, dut2ring1, dut2ring2, dut2cross, dut2data) + config_target3(dut3, dut3ring1, dut3ring2, dut3cross, dut3data) + config_target4(dut4, dut4ring1, dut4ring2, dut4cross, dut4data) + with test.step("Wait for neighbors"): + print("Waiting for neighbors to peer") + until(lambda: route.ospf_get_neighbor(dut1, "0.0.0.0", dut1ring1, "10.0.0.2"), attempts=100) + until(lambda: route.ospf_get_neighbor(dut1, "0.0.0.1", dut1cross, "10.0.0.3"), attempts=100) + until(lambda: route.ospf_get_neighbor(dut2, "0.0.0.1", dut2ring1, "10.0.0.3"), attempts=100) + until(lambda: route.ospf_get_neighbor(dut2, "0.0.0.0", dut2ring2, "10.0.0.1"), attempts=100) + until(lambda: route.ospf_get_neighbor(dut2, "0.0.0.2", dut2cross, "10.0.0.4"), attempts=100) + + with test.step("Wait for routes"): + print("Waiting for routes from OSPF") + until(lambda: route.ipv4_route_exist(dut1, "10.0.0.2/32", nexthop="10.0.12.2", source_protocol = "infix-routing:ospf"), attempts=100) + until(lambda: route.ipv4_route_exist(dut1, "10.0.0.3/32", nexthop="10.0.13.2", source_protocol = "infix-routing:ospf"), attempts=100) + until(lambda: route.ipv4_route_exist(dut1, "10.0.0.4/32", nexthop="10.0.41.1", source_protocol = "infix-routing:ospf"), attempts=100) + until(lambda: route.ipv4_route_exist(dut1, "192.168.4.0/24", nexthop="10.0.41.1", source_protocol = "infix-routing:ospf"), attempts=100) + until(lambda: route.ipv4_route_exist(dut4, "10.0.0.3/32", nexthop="10.0.41.2", source_protocol = "infix-routing:ospf"), attempts=100) + + with test.step("Verify NSSA area"): # Should be only default route out of the area. + until(lambda: route.ipv4_route_exist(dut4, "11.0.8.0/24"), attempts=100) + until(lambda: route.ipv4_route_exist(dut4, "11.0.9.0/24"), attempts=100) + until(lambda: route.ipv4_route_exist(dut4, "11.0.10.0/24"), attempts=100) + until(lambda: route.ipv4_route_exist(dut4, "11.0.11.0/24"), attempts=100) + until(lambda: route.ipv4_route_exist(dut4, "11.0.12.0/24"), attempts=100) + until(lambda: route.ipv4_route_exist(dut3, "0.0.0.0/0"), attempts=100) + until(lambda: route.ipv4_route_exist(dut3, "10.0.12.0/30") == False, attempts=3) + until(lambda: route.ipv4_route_exist(dut3, "10.0.12.0/30") == False, attempts=3) + until(lambda: route.ipv4_route_exist(dut3, "11.0.8.0/24") == False, attempts=3) + until(lambda: route.ipv4_route_exist(dut3, "11.0.9.0/24") == False, attempts=3) + until(lambda: route.ipv4_route_exist(dut3, "11.0.10.0/24") == False, attempts=3) + until(lambda: route.ipv4_route_exist(dut3, "11.0.11.0/24") == False, attempts=3) + until(lambda: route.ipv4_route_exist(dut3, "11.0.12.0/24") == False, attempts=3) + until(lambda: route.ipv4_route_exist(dut3, "11.0.13.0/24") == False, attempts=3) + until(lambda: route.ipv4_route_exist(dut3, "11.0.14.0/24") == False, attempts=3) + until(lambda: route.ipv4_route_exist(dut3, "11.0.15.0/24") == False, attempts=3) + assert(route.ospf_is_area_nssa(dut3, "0.0.0.1")) + + _, hport0 = env.ltop.xlate("host", "data3") + with infamy.IsolatedMacVlan(hport0) as ns0: + ns0.addip("192.168.3.2") + ns0.addroute("0.0.0.0/0", "192.168.3.1") + trace=ns0.traceroute("10.0.0.3") + ns0.must_reach("11.0.8.1") + + with test.step("Verify correct hops"): + _, hport0 = env.ltop.xlate("host", "data4") + with infamy.IsolatedMacVlan(hport0) as ns0: + ns0.addip("192.168.4.2") + ns0.addroute("0.0.0.0/0", "192.168.4.1") + trace=ns0.traceroute("10.0.0.3") + assert(len(trace) == 3) + assert(trace[1][1] == "10.0.41.2") + assert(trace[2][1] == "10.0.0.3") + + with test.step("Disable link between R1 and R4, and verify correct hops"): + disable_link(dut1, dut1ring2) # Here we should test with link breakers, to test BFD recouppling, for now disable the link + until(lambda: route.ipv4_route_exist(dut4, "10.0.0.3/32", nexthop="10.0.24.1", source_protocol = "infix-routing:ospf"), attempts=10) + until(lambda: route.ipv4_route_exist(dut4, "10.0.0.3/32", nexthop="10.0.41.2") == False, attempts = 2) + _, hport0 = env.ltop.xlate("host", "data4") + with infamy.IsolatedMacVlan(hport0) as ns0: + ns0.addip("192.168.4.2") + ns0.addroute("0.0.0.0/0", "192.168.4.1") + trace=ns0.traceroute("10.0.0.3") + assert(len(trace) == 3) + assert(trace[1][1] == "10.0.24.1") + assert(trace[2][1] == "10.0.0.3") + ns0.must_reach("10.0.0.3") + + test.succeed()