def test_sshd_example(): try: net = IPNet(topo=SSHTopo()) net.start() ssh_key = None with open("/tmp/sshd_r2.cfg") as fileobj: for line in fileobj: if "AuthorizedKeysFile" in line: ssh_key = line.split(" ")[1].split(".")[0] assert ssh_key is not None, "No authorized SSH key found in the configuration" assert os.path.isfile(ssh_key), "Cannot find key file at %s" % ssh_key ip = net["r2"].intf("r2-eth0").ip cmd = "ssh -oStrictHostKeyChecking=no -oConnectTimeout=1 -oPasswordAuthentication=no" \ " -i %s %s ls" % (ssh_key, ip) t = 0 while t < 60 and net["r1"].popen(cmd.split(" ")).wait() != 0: time.sleep(0.5) t += 1 p = net["r1"].popen(cmd.split(" ")) assert p.wait() == 0, "Cannot connect with SSH to the router" net.stop() finally: cleanup()
def test_LongestPrefixMatch_failed_2(): ipv6addr = [ "2042:1a::", "2042:2b::", "2042:3c::", "2042:12::", "2042:13::", "2042:23::" ] ipv4addr = [ "10.1.4.0", "10.1.5.0", "10.1.6.0", "10.12.0.0", "10.13.0.0", "10.23.0.0" ] net = IPNet(topo=LongestPrefixMatch(ipv6addr, ipv4addr, "logFT2.txt", "logFF2.txt"), allocate_IPs=False) try: net.start() net["r1"].cmd("ip -4 route add 10.1.4.0/22 via 10.12.0.2") net["r2"].cmd("ip -4 route add 10.1.4.0/24 via 10.12.0.1") net["r2"].cmd("ip -4 route add 10.1.6.0/24 via 10.23.0.3") net["r3"].cmd("ip -4 route add 10.1.4.0/24 via 10.13.0.1") net["r3"].cmd("ip -4 route add 10.1.5.0/24 via 10.23.0.2") net.stop() with open('logFT2.txt', 'r') as f: state = f.read().replace('\n', '') f.close() assert "Failed" in state finally: cleanup()
def test_v4_and_v6_only_network(topo, use_v4, use_v6): try: net = IPNet(topo=topo(), use_v4=use_v4, use_v6=use_v6) net.start() for n in net.routers + net.hosts: for itf in n.intfList(): if itf.node.use_v4 and net.use_v4: assert len(list(itf.ips())) == itf.interface_width[0], \ "Did not allocate enough IPv4 addresses on interface " \ "{}".format(itf) else: assert len(list(itf.ips())) == 0,\ "Should not allocate IPv4 addresses on interface " \ "{}".format(itf) if itf.node.use_v6 and net.use_v6: assert len(list(itf.ip6s(exclude_lls=True))) == \ itf.interface_width[1], \ "Did not allocate enough IPv6 addresses on interface " \ "{}".format(itf) else: assert len(list(itf.ip6s(exclude_lls=True))) == 0, \ "Should not allocate IPv6 addresses on interface " \ "{}".format(itf) net.stop() finally: cleanup()
def test_DefaultRoutesOnly_firstsolution(): """ First solution R1 has R3 as default route R3 has R2 as default route R2 has R1 as default route""" subnet=["192.168.1.0", "192.168.2.0", "192.168.3.0", "192.168.4.0", \ "192.168.5.0", "192.168.6.0"] prefixlen = ["24", "24", "24", "24", "24", "24"] gw=["192.168.1.1", "192.168.2.1", "192.168.3.1", "192.168.6.2", "192.168.5.1", \ "192.168.4.1"] try: net = IPNet(topo=DefaultRoutesOnly("4", subnet, prefixlen, gw) \ , use_v4=False, use_v6=False, allocate_IPs=False) net.start() net["A"].cmd("ip route add default via 192.168.1.1") net["B"].cmd("ip route add default via 192.168.2.1") net["C"].cmd("ip route add default via 192.168.3.1") net["R1"].cmd("ip route add default via 192.168.6.2") net["R3"].cmd("ip route add default via 192.168.5.1") net["R2"].cmd("ip route add default via 192.168.4.1") r = net.pingAll() print(r) net.stop() with open('state6.txt', 'r') as f: state = f.read().replace('\n', '') f.close() assert "Failed" not in state finally: cleanup()
def test_iptables_example(): try: net = IPNet(topo=IPTablesTopo()) net.start() ip = net["r2"].intf("r2-eth0").ip cmd = "ping -W 1 -c 1 %s" % ip p = net["r1"].popen(cmd.split(" ")) out, err = p.communicate() code = p.poll() assert code == 0, "Pings over IPv4 should not be blocked.\n" \ "[stdout]\n%s\n[stderr]\n%s" % (out, err) ip6 = net["r2"].intf("r2-eth0").ip6 cmd = "ping6 -W 1 -c 1 %s" % ip6 p = net["r1"].popen(cmd.split(" ")) assert p.wait() != 0, "Pings over IPv6 should be blocked" code, _, _ = check_tcp_connectivity(net["r1"], net["r2"], server_port=80, timeout=.5) assert code != 0, "TCP over port 80 should be blocked over IPv4" code, out, err = check_tcp_connectivity(net["r1"], net["r2"], v6=True, server_port=80) assert code == 0, "TCP over port 80 should not be blocked over IPv6.\n" \ "[stdout]\n%s\n[stderr]\n%s" % (out, err) net.stop() finally: cleanup()
def _main(autostart=False): graph = _PaperGraph() net = IPNet(topo=graph, use_v6=False) net.start() db = TopologyDB(net=net) for e in net.topo.egresses: db._network[e]['is_egress'] = True db.save(TOPO_DB) sink = net['sink'] source = net['source'] sink_addr = sink.IP() src_addr = source.IP() MNLOG.debug('Source is at ', src_addr, 'sink is at ', sink_addr, '\n') with open(REQ_FILE, 'w') as f: f.write('MIRROR {sink} ON [A B C D]\n' 'CONFINE {sink} ON [A B L C D]\n' 'USING {cnt} M DURING 500ms'.format(sink=sink_addr, cnt=FLOW_COUNT)) MNLOG.info('Starting sink') sink.filter_source(src_addr) MNLOG.info('Starting source') source.start_src(sink_addr, src_addr, FLOW_COUNT) time.sleep(5) if autostart: MNLOG.info('Starting collector') net[COLLECTOR_ID].start_collector() _CLI(net) net.stop() if os.path.exists(REQ_FILE): os.unlink(REQ_FILE)
def test_partial_static_example(): try: net = IPNet(topo=PartialStaticAddressNet()) net.start() # Check allocated addresses assert net["h3"].intf("h3-eth0").ip == "192.168.1.2" assert net["h3"].intf("h3-eth0").ip6 == "fc00:1::2" assert net["r1"].intf("lo").ip6 == "2042:1::1" assert net["r1"].intf("r1-eth1").ip == "192.168.0.1" assert net["r1"].intf("r1-eth1").ip6 == "fc00::1" assert net["r2"].intf("r2-eth0").ip == "192.168.0.2" assert net["r2"].intf("r2-eth0").ip6 == "fc00::2" assert net["r2"].intf("r2-eth1").ip == "192.168.1.1" assert net["r2"].intf("r2-eth1").ip6 == "fc00:1::1" # Check connectivity assert_connectivity(net, v6=False) assert_connectivity(net, v6=True) net.stop() finally: cleanup()
def test_addr_intf(): try: net = IPNet(topo=StaticAddressNet()) net.start() itf = net["r1"].intf("r1-eth1") # Check IP6 update itf.ip6 = "2001:21::1" ip6s = list(itf.ip6s(exclude_lls=True)) assert len(ip6s) == 1 and ip6s[0].with_prefixlen == "2001:21::1/64",\ "Cannot update an IPv6 address" assert itf.prefixLen6 == 64 itf.prefixLen6 = 48 assert itf.prefixLen6 == 48, "Cannot update prefix len of an IPv6 address" # Check IP update itf.ip = "10.1.2.1" ips = list(itf.ips()) assert len(ips) == 1 and ips[0].with_prefixlen == "10.1.2.1/24",\ "Cannot update an IPv4 address" assert itf.prefixLen == 24 itf.prefixLen = 28 assert itf.prefixLen == 28, "Cannot update prefix len of an IPv4 address" # Check MAC getters assert itf.updateMAC() == itf.updateAddr()[1],\ "MAC address obtained through two methods is not identical" net.stop() finally: cleanup()
def test_tmp_isolation(): try: net = IPNet(topo=SimpleOpenrTopo()) net.start() tmp_dir = '/tmp' with tempfile.NamedTemporaryFile(dir=tmp_dir) as file: host_file_name = file.name host_file_base_name = os.path.basename(host_file_name) host_tmp_dir_content = os.listdir(tmp_dir) assert os.path.isfile(host_file_name) assert host_file_base_name in host_tmp_dir_content for i in range(1, 5): node_tmp_dir_content = net['r_{}'.format(i)] \ .cmd('ls {}'.format(tmp_dir)) \ .split() assert host_file_base_name not in node_tmp_dir_content node_file_base_name = str(uuid.uuid1()) node_file_name = '{}/{}'.format(tmp_dir, node_file_base_name) net['r_1'].cmd('touch {}'.format(node_file_name)) node_tmp_dir_content = net['r_1'].cmd('ls {}'.format(tmp_dir)).split() host_tmp_dir_content = os.listdir(tmp_dir) assert node_file_base_name in node_tmp_dir_content assert not os.path.isfile(node_file_name) assert node_file_base_name not in host_tmp_dir_content net.stop() finally: cleanup()
def test_tc_example(topo, delay, bw): """ :param topo: The topology class :param delay: The delay between host h1 and h2 in ms :param bw: The bandwidth between h1 and h2 in Mbps """ try: net = IPNet(topo=topo()) net.start() # Check connectivity assert_connectivity(net, v6=False) assert_connectivity(net, v6=True) # Check delay assert_delay(net["h1"], net["h2"], delay, v6=False) assert_delay(net["h1"], net["h2"], delay, v6=True) # Check bandwidth assert_bw(net["h1"], net["h2"], bw, v6=False, tolerance=bw // 10) assert_bw(net["h1"], net["h2"], bw, v6=True, tolerance=bw // 10) net.stop() finally: cleanup()
def test_TwoPCLANWithRouter_IPv4_wrong_gw(): try: net = IPNet(topo=TwoPCLANWithRouter("4", "192.168.1.0", "24", \ "192.168.1.1", "192.168.2.0", "24", "192.168.2.1") \ , use_v4=False, use_v6=False, allocate_IPs=False) net.start() itf1 = net["h1"].intf("h1-eth0") itf1.ip = "192.168.1.2/24" itf1.prefixLen == 24 itf2 = net["r"].intf("r-eth0") itf2.ip = "192.168.1.1/24" itf2.prefixLen == 24 r = net["h1"].cmd("ip route add default via 192.168.2.1") itf3 = net["h2"].intf("h2-eth0") itf3.ip = "192.168.2.2/24" itf3.prefixLen == 24 itf4 = net["r"].intf("r-eth1") itf4.ip = "192.168.2.1/24" itf4.prefixLen == 24 r = net["h2"].cmd("ip route add default via 192.168.1.1") net.stop() with open('state2.txt', 'r') as f: state = f.read().replace('\n', '') f.close() assert "Failed" in state finally: cleanup()
def test_dns_network(named_cfg, zone_args, exp_named_cfg, exp_zone_cfg): try: net = IPNet(topo=CustomDNSNetwork(named_cfg, zone_args)) net.start() # Check generated configurations with open("/tmp/named_master2.cfg") as fileobj: cfg = fileobj.readlines() for line in exp_named_cfg: assert line + "\n" in cfg,\ "Cannot find the line '%s' in the generated " \ "main configuration:\n%s" % (line, "".join(cfg)) with open("/tmp/named_master2.test.org.zone.cfg") as fileobj: cfg = fileobj.readlines() for line in exp_zone_cfg: assert line + "\n" in cfg,\ "Cannot find the line '%s' in the generated zone " \ "configuration:\n%s" % (line, "".join(cfg)) # Check port number configuration dns_server_port = named_cfg.get("dns_server_port", 53) assert_dns_record(net["master2"], "localhost", AAAARecord("master2.test.org", net["master2"].defaultIntf().ip6), port=dns_server_port) # Check connectivity assert_connectivity(net, v6=False) assert_connectivity(net, v6=True) # Check generated DNS record records = [ NSRecord("mydomain.org", "master"), NSRecord("mydomain.org", "slave"), ARecord("master.mydomain.org", net["master"].defaultIntf().ip), AAAARecord("master.mydomain.org", net["master"].defaultIntf().ip6), ARecord("slave.mydomain.org", net["slave"].defaultIntf().ip), AAAARecord("slave.mydomain.org", net["slave"].defaultIntf().ip6), ARecord("server.mydomain.org", net["server"].defaultIntf().ip), AAAARecord("server.mydomain.org", net["server"].defaultIntf().ip6, ttl=120), PTRRecord(net["master"].defaultIntf().ip, "master.mydomain.org"), PTRRecord(net["master"].defaultIntf().ip6, "master.mydomain.org"), PTRRecord(net["slave"].defaultIntf().ip, "slave.mydomain.org"), PTRRecord(net["slave"].defaultIntf().ip6, "slave.mydomain.org"), PTRRecord(net["server"].defaultIntf().ip, "server.mydomain.org"), PTRRecord(net["server"].defaultIntf().ip6, "server.mydomain.org", ttl=120) ] for node in [net["master"], net["slave"]]: for record in records: assert_dns_record(node, "localhost", record) time.sleep(10) net.stop() finally: cleanup()
def test_DebugTraceroute_ipv6_2(): ipv6addr = [ "2042:1a::", "2042:2b::", "2042:12::", "2042:13::", "2042:23::" ] ipv4addr = [ "10.51.0.0", "10.62.0.0", "10.12.0.0", "10.13.0.0", "10.23.0.0" ] net = IPNet(topo=DebugTraceroute(ipv6addr, ipv4addr, "debugT2.txt", "debugF2.txt"), allocate_IPs=False) try: net.start() net["r1"].cmd("ip -6 route add 2042:2b::/64 nexthop via 2042:12::2") net["r2"].cmd("ip -6 route add 2042:2b::/64 nexthop via 2042:23::3") net["r3"].cmd("ip -6 route add 2042:1a::/64 nexthop via 2042:13::1") net["r1"].cmd("ip route add 10.62.0.0/24 nexthop via 10.12.0.2") net["r2"].cmd("ip route add 10.62.0.0/24 nexthop via 10.23.0.3") net["r3"].cmd("ip route add 10.51.0.0/24 nexthop via 10.13.0.1") net.stop() with open('debugT2.txt', 'r') as f: state = f.read().replace('\n', '') f.close() assert "Failed" in state finally: cleanup()
def test_TwoPCLANWithRouter_IPv6(): try: net = IPNet(topo=TwoPCLANWithRouter("6", "fc00:0:1::", "48", \ "fc00:0:1::1", "fc00:0:2::", "48", "fc00:0:2::1") \ , use_v4=False, use_v6=False, allocate_IPs=False) net.start() itf1 = net["h1"].intf("h1-eth0") itf1.ip = "fc00:0:1::2/48" itf1.prefixLen == 48 itf2 = net["r"].intf("r-eth0") itf2.ip = "fc00:0:1::1/48" itf2.prefixLen == 48 r = net["h1"].cmd("ip -6 route add default via fc00:0:1::1") itf3 = net["h2"].intf("h2-eth0") itf3.ip = "fc00:0:2::2/48" itf3.prefixLen == 48 itf4 = net["r"].intf("r-eth1") itf4.ip = "fc00:0:2::1/48" itf4.prefixLen == 48 r = net["h2"].cmd("ip -6 route add default via fc00:0:2::1") net.stop() with open('state2.txt', 'r') as f: state = f.read().replace('\n', '') f.close() assert "Failed" not in state finally: cleanup()
def test_radvd_example(): try: net = IPNet(topo=RouterAdvNet(), use_v4=False, use_v6=True, allocate_IPs=False) net.start() assert_connectivity(net, v6=True) net.stop() finally: cleanup()
def test_openr_connectivity(): try: net = IPNet(topo=SimpleOpenrTopo()) net.start() assert_connectivity(net, v6=True) net.stop() finally: cleanup()
def test_address_pair(node, use_v4, use_v6, expected): try: net = IPNet(topo=StaticAddressNet()) net.start() assert utils.address_pair(net[node], use_v4, use_v6) == expected net.stop() finally: cleanup()
def test_ospf6_example(): try: net = IPNet(topo=SimpleOSPFv3Net()) net.start() assert_connectivity(net, v6=True) net.stop() finally: cleanup()
def test_ripng_flush_connectivity(): try: net = IPNet(topo=MinimalRIPngNet(is_test_flush=True)) net.start() sleep(10) assert_no_connectivity(net, v6=True) net.stop() finally: cleanup()
def test_bgp_example(): try: net = IPNet(topo=SimpleBGPTopo()) net.start() assert_connectivity(net, v6=False) assert_connectivity(net, v6=True) net.stop() finally: cleanup()
def test_bgp_rr(): try: net = IPNet(topo=BGPTopoRR()) net.start() for path in rr_paths: assert_path(net, path, v6=True) net.stop() finally: cleanup()
def test_bgp_local_pref(): try: net = IPNet(topo=BGPTopoLocalPref()) net.start() for path in local_pref_paths: assert_path(net, path, v6=True) net.stop() finally: cleanup()
def test_bgp_full_config(): try: net = IPNet(topo=BGPTopoFull()) net.start() for path in full_paths: assert_path(net, path, v6=True) net.stop() finally: cleanup()
def test_bgp_policies(topology): try: net = IPNet(topo=topology()) net.start() for path in policies_paths[topology.__name__]: assert_path(net, path, v6=True) net.stop() finally: cleanup()
def topology(): net = IPNet(use_v4=False, allocate_IPs=False, switch=OVSKernelSwitch, controller=RemoteController, topo=topo1) net.start() IPCLI(net) net.stop()
def test_StaticECMPRouting(): subnet=["192.168.1.0", "192.168.2.0", "192.168.3.0", "192.168.4.0", \ "192.168.5.0", "192.168.6.0"] prefixlen = ["24", "24", "24", "24", "24", "24"] gw = ["192.168.1.1", "192.168.2.1"] try: net = IPNet(topo=StaticECMPRouting("4", subnet, prefixlen, gw) \ , use_v4=False, use_v6=False, allocate_IPs=False) net.start() net["A"].cmd("ip route add default via 192.168.1.1") net["B"].cmd("ip route add default via 192.168.1.1") net["R1"].cmd( "ip route add 192.168.2.0/24 nexthop dev R1-eth1 via 192.168.3.2 nexthop dev R1-eth2 via 192.168.6.2" ) net["R1"].cmd( "ip route add 192.168.4.0/24 dev R1-eth1 via 192.168.3.2") net["R1"].cmd( "ip route add 192.168.5.0/24 dev R1-eth2 via 192.168.6.2") #net["R1"].cmd("ip route add 192.168.2.0/24 dev R1-eth2 via 192.168.6.2") net["R2"].cmd( "ip route add 192.168.1.0/24 dev R2-eth0 via 192.168.3.1") net["R2"].cmd( "ip route add 192.168.2.0/24 dev R2-eth1 via 192.168.4.2") net["R2"].cmd( "ip route add 192.168.5.0/24 dev R2-eth1 via 192.168.4.2") net["R2"].cmd( "ip route add 192.168.6.0/24 dev R2-eth0 via 192.168.3.1") net["R3"].cmd( "ip route add 192.168.1.0/24 dev R3-eth0 via 192.168.6.1") net["R3"].cmd( "ip route add 192.168.2.0/24 dev R3-eth1 via 192.168.5.2") net["R3"].cmd( "ip route add 192.168.3.0/24 dev R3-eth0 via 192.168.6.1") net["R3"].cmd( "ip route add 192.168.4.0/24 dev R3-eth1 via 192.168.5.2") net["R4"].cmd( "ip route add 192.168.1.0/24 nexthop dev R4-eth2 via 192.168.5.1 nexthop dev R4-eth1 via 192.168.4.1" ) net["R4"].cmd( "ip route add 192.168.3.0/24 dev R4-eth1 via 192.168.4.1") net["R4"].cmd( "ip route add 192.168.6.0/24 dev R4-eth2 via 192.168.5.1") net.stop() with open('state3.txt', 'r') as f: state = f.read().replace('\n', '') f.close() assert "Failed" not in state finally: cleanup()
def test_bgp_policies_adjust(): try: # Adding this new peering link should enable all hosts # to ping each others net = IPNet(topo=BGPPoliciesAdjustTopo( as_start="as5r", as_end="as2r", bgp_policy=CLIENT_PROVIDER)) net.start() assert_connectivity(net, v6=True) net.stop() finally: cleanup()
def test_ripng_adjust(): try: net = IPNet(topo=RIPngNetworkAdjust(lr1r5_cost=5)) net.start() assert_connectivity(net, v6=True) for path in expected_paths["RIPngNetworkAdjust-mod"]: assert_path(net, path, v6=True) net.stop() finally: cleanup()
def test_etc_hosts(topo): try: net = IPNet(topo=topo()) net.start() assert_connectivity(net, v6=True, translate_address=False) assert_connectivity(net, v6=False, translate_address=False) net.stop() finally: cleanup()
def test_ripng(): try: net = IPNet(topo=MinimalRIPngNet()) net.start() assert_connectivity(net, v6=True) for path in expected_paths: assert_path(net, path, v6=True) net.stop() finally: cleanup()
def test_bgp_daemon_params(bgp_params, expected_cfg): try: net = IPNet(topo=BGPTopo(bgp_params), allocate_IPs=False) net.start() # Check generated configuration with open("/tmp/bgpd_as2r1.cfg") as fileobj: cfg = fileobj.readlines() for line in expected_cfg: assert (line + "\n") in cfg, "Cannot find the line '%s' in the generated configuration:\n%s"\ % (line, "".join(cfg)) # Check reachability assert_connectivity(net, v6=False) net.stop() finally: cleanup()
def test_radvd_daemon_params(link_params, expected_cfg): try: net = IPNet(topo=CustomRouterAdvNet(link_params), use_v4=False, use_v6=True, allocate_IPs=False) net.start() # Check generated configuration with open("/tmp/radvd_r.cfg") as fileobj: cfg = fileobj.readlines() for line in expected_cfg: assert (line + "\n") in cfg, "Cannot find the line '%s' in the generated configuration:\n%s"\ % (line, "".join(cfg)) # Check reachability assert_connectivity(net, v6=True) net.stop() finally: cleanup()
def test_gre_example(): try: net = IPNet(topo=GRETopo()) net.start() t = 0 cmd = "ping -W 1 -c 1 -I 10.0.1.1 10.0.1.2".split(" ") while t < 60 and net["h1"].popen(cmd).wait() != 0: t += 1 time.sleep(.5) p = net["h1"].popen(cmd) code = p.wait() out, err = p.communicate() assert code == 0, "Cannot use GRE tunnel.\n" \ "The command '%s' printed: [stdout]\n%s\n[stderr]\n%s" % (cmd, out, err) net.stop() finally: cleanup()
def test_ospf6_daemon_params(ospf6_params, link_params, expected_cfg, expected_paths): try: net = IPNet(topo=MinimalOSPFv3Net(ospf6_params, link_params)) net.start() # Check generated configuration with open("/tmp/ospf6d_r1.cfg") as fileobj: cfg = fileobj.readlines() for line in expected_cfg: assert (line + "\n") in cfg, "Cannot find the line '%s' in the generated configuration:\n%s"\ % (line, "".join(cfg)) # Check reachability assert_connectivity(net, v6=True) for path in expected_paths: assert_path(net, path, v6=True) net.stop() finally: cleanup()
def test_capture_dummy_interface(dummy_interface): check_addresses(dummy_interface, node=None) net = IPNet(topo=LinkTopo()) net.start() check_addresses(dummy_interface, node=net['r1']) net.stop()
# Start network net = IPNet(topo=SimpleTopo(), use_v4=True, allocate_IPs=False) try: net.start() # Execute Commands # Enable SRv6 On Routers. r1 = RouterConfiguration(net.get('r1'),1,r1_routes) r2 = RouterConfiguration(net.get('r2'),2,r2_routes) r3 = RouterConfiguration(net.get('r3'),3,r3_routes) r4 = RouterConfiguration(net.get('r4'),4,r4_routes,False) ids = RouterConfiguration(net.get('ids'),5,ids_routes) HostConfiguration(net.get('a'),'a',1) HostConfiguration(net.get('b'),'b',4) HostConfiguration(net.get('c'),'c',3) # RouterConfiguration.build_dx4_tunnel(r1,r3,"a") # RouterConfiguration.build_dx4_tunnel(r3,r4,"b") # RouterConfiguration.build_dx4_tunnel(r1,r4,"c") # RouterConfiguration.build_dx6_tunnel(r1,r3,"a1") # RouterConfiguration.build_dx6_tunnel(r3,r4,"b1") # RouterConfiguration.build_dx6_tunnel(r1,r4,"c1") IPCLI(net) finally: net.stop()