#!/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, "enabled": True, "ipv4": { "forwarding": True, "address": [{ "ip": "10.0.12.1", "prefix-length": 30 }]} }, { "name": ring2, "enabled": True, "ipv4": { "forwarding": True, "address": [{ "ip": "10.0.41.2", "prefix-length": 30 }]} }, { "name": link, "enabled": True, "ipv4": { "forwarding": True, "address": [{ "ip": "192.168.1.1", "prefix-length": 24 }, { "ip": "11.0.8.1", "prefix-length": 24 }] } }, { "name": cross, "enabled": True, "ipv4": { "forwarding": True, "address": [{ "ip": "10.0.13.1", "prefix-length": 30 }] } }, { "name": "lo", "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, "enabled": True, "ipv4": { "forwarding": True, "address": [{ "ip": "10.0.23.1", "prefix-length": 30 }]} }, { "name": ring2, "enabled": True, "ipv4": { "forwarding": True, "address": [{ "ip": "10.0.12.2", "prefix-length": 30 }]} }, { "name": cross, "enabled": True, "ipv4": { "forwarding": True, "address": [{ "ip": "10.0.24.1", "prefix-length": 30 }]} }, { "name": link, "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", "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, "enabled": True, "ipv4": { "forwarding": True, "address": [{ "ip": "10.0.23.2", "prefix-length": 30 }] } }, { "name": link, "enabled": True, "ipv4": { "forwarding": True, "address": [{ "ip": "192.168.3.1", "prefix-length": 24 }] } }, { "name": cross, "enabled": True, "ipv4": { "forwarding": True, "address": [{ "ip": "10.0.13.2", "prefix-length": 30 }] } }, { "name": "lo", "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, "enabled": True, "ipv4": { "forwarding": True, "address": [{ "ip": "10.0.41.1", "prefix-length": 30 }]} }, { "name": cross, "enabled": True, "ipv4": { "forwarding": True, "address": [{ "ip": "10.0.24.2", "prefix-length": 30 }]} }, { "name": link, "enabled": True, "ipv4": { "forwarding": True, "address": [{ "ip": "192.168.4.1", "prefix-length": 24 }] } }, { "name": "lo", "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=200) until(lambda: route.ospf_get_neighbor(dut1, "0.0.0.1", dut1cross, "10.0.0.3"), attempts=200) until(lambda: route.ospf_get_neighbor(dut2, "0.0.0.1", dut2ring1, "10.0.0.3"), attempts=200) until(lambda: route.ospf_get_neighbor(dut2, "0.0.0.0", dut2ring2, "10.0.0.1"), attempts=200) until(lambda: route.ospf_get_neighbor(dut2, "0.0.0.2", dut2cross, "10.0.0.4"), attempts=200) 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=200) until(lambda: route.ipv4_route_exist(dut1, "10.0.0.3/32", nexthop="10.0.13.2", source_protocol = "infix-routing:ospf"), attempts=200) until(lambda: route.ipv4_route_exist(dut1, "10.0.0.4/32", nexthop="10.0.41.1", source_protocol = "infix-routing:ospf"), attempts=200) until(lambda: route.ipv4_route_exist(dut1, "192.168.4.0/24", nexthop="10.0.41.1", source_protocol = "infix-routing:ospf"), attempts=200) until(lambda: route.ipv4_route_exist(dut4, "10.0.0.3/32", nexthop="10.0.41.2", source_protocol = "infix-routing:ospf"), attempts=200) 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=200) until(lambda: route.ipv4_route_exist(dut4, "11.0.9.0/24"), attempts=200) until(lambda: route.ipv4_route_exist(dut4, "11.0.10.0/24"), attempts=200) until(lambda: route.ipv4_route_exist(dut4, "11.0.11.0/24"), attempts=200) until(lambda: route.ipv4_route_exist(dut4, "11.0.12.0/24"), attempts=200) until(lambda: route.ipv4_route_exist(dut3, "0.0.0.0/0"), attempts=200) until(lambda: route.ipv4_route_exist(dut3, "10.0.12.0/30") == False, attempts=5) until(lambda: route.ipv4_route_exist(dut3, "10.0.12.0/30") == False, attempts=5) until(lambda: route.ipv4_route_exist(dut3, "11.0.8.0/24") == False, attempts=5) until(lambda: route.ipv4_route_exist(dut3, "11.0.9.0/24") == False, attempts=5) until(lambda: route.ipv4_route_exist(dut3, "11.0.10.0/24") == False, attempts=5) until(lambda: route.ipv4_route_exist(dut3, "11.0.11.0/24") == False, attempts=5) until(lambda: route.ipv4_route_exist(dut3, "11.0.12.0/24") == False, attempts=5) until(lambda: route.ipv4_route_exist(dut3, "11.0.13.0/24") == False, attempts=5) until(lambda: route.ipv4_route_exist(dut3, "11.0.14.0/24") == False, attempts=5) until(lambda: route.ipv4_route_exist(dut3, "11.0.15.0/24") == False, attempts=5) 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=100) until(lambda: route.ipv4_route_exist(dut4, "10.0.0.3/32", nexthop="10.0.41.2") == False, attempts = 10) _, 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()