"""Build the lab network, apply your plan, and check it. Given; do not change it. Run inside the course container, from this folder: python3 lab.py check # build, apply plan.py, run the checks, stop python3 lab.py cli # build, apply plan.py, then a mininet> prompt The network, three LANs cut from 192.168.1.0/24: net A 192.168.1.0/26 net B 192.168.1.64/26 net C 192.168.1.128/26 h1 --+ +-- h3 s1 -- r1 ------------- s2 ---------- r2 -- s3 -- h4 h2 --+ (r1-eth0 | r1-eth1) (r2-eth0 | r2-eth1) Every interface starts with no address. plan.py says what to run on each node. Mininet reference: http://mininet.org/walkthrough/ """ import ipaddress import sys from mininet.log import setLogLevel from mininet.net import Mininet from mininet.nodelib import LinuxBridge from mininet.topo import Topo import plan NETS = {"A": ipaddress.ip_network("192.168.1.0/26"), "B": ipaddress.ip_network("192.168.1.64/26"), "C": ipaddress.ip_network("192.168.1.128/26")} # which network each interface is on INTERFACES = {"h1-eth0": "A", "h2-eth0": "A", "r1-eth0": "A", "r1-eth1": "B", "h3-eth0": "B", "r2-eth0": "B", "r2-eth1": "C", "h4-eth0": "C"} HOSTS = ["h1", "h2", "h3", "h4"] class Lab(Topo): def build(self): s1, s2, s3 = (self.addSwitch(s) for s in ("s1", "s2", "s3")) for name in ("h1", "h2", "h3", "h4", "r1", "r2"): self.addHost(name, ip=None) self.addLink("h1", s1, intfName1="h1-eth0") self.addLink("h2", s1, intfName1="h2-eth0") self.addLink("r1", s1, intfName1="r1-eth0") self.addLink("r1", s2, intfName1="r1-eth1") self.addLink("h3", s2, intfName1="h3-eth0") self.addLink("r2", s2, intfName1="r2-eth0") self.addLink("r2", s3, intfName1="r2-eth1") self.addLink("h4", s3, intfName1="h4-eth0") def start(): net = Mininet(topo=Lab(), switch=LinuxBridge, controller=None, autoSetMacs=True) net.start() for node in net.hosts: for intf in node.intfList(): if intf.name != "lo": node.cmd(f"ip addr flush dev {intf.name}") node.cmd("sysctl -qw net.ipv4.ip_forward=0") for node, commands in plan.PLAN.items(): for command in commands: out = net.get(node).cmd(command).strip() if out: print(f"{node}$ {command}\n {out}") return net def addresses(net): """{interface: IPv4 interface address} as configured.""" found = {} for name in INTERFACES: node = net.get(name.split("-")[0]) for line in node.cmd(f"ip -4 -o addr show dev {name}").splitlines(): found[name] = ipaddress.ip_interface(line.split()[3]) return found def ip_of(net, host): found = addresses(net).get(f"{host}-eth0") if found is None: raise LookupError(f"{host} has no address yet, so this cannot be tested (see the first check)") return str(found.ip) def check_addresses(net): found, problems = addresses(net), [] for name, where in INTERFACES.items(): a = found.get(name) if a is None: problems.append(f"{name} has no IPv4 address") elif a.network != NETS[where]: problems.append(f"{name} is {a}, but it is on net {where}, {NETS[where]}") elif a.ip in (NETS[where].network_address, NETS[where].broadcast_address): problems.append(f"{name} is {a.ip}, which is net {where}'s network or broadcast address") return "; ".join(problems) or None def check_reachability(net): missing = [] for a in HOSTS: for b in HOSTS: # up to three tries: the first packet on a new network can be lost while it settles if a != b and " 0 received" in net.get(a).cmd(f"ping -c 3 -i 0.3 -W 1 {ip_of(net, b)}"): missing.append(f"{a}->{b}") return f"no reply for {', '.join(missing)}" if missing else None def check_path(net): out = net.get("h1").cmd(f"traceroute -n -w 1 -q 1 {ip_of(net, 'h4')}") hops = [line.split()[1] for line in out.splitlines()[1:] if line.strip() and line.split()[1] != "*"] if len(hops) != 3: return f"h1 to h4 should cross r1 and r2 (three hops), traceroute shows {hops}" ttl = [w for w in net.get("h1").cmd(f"ping -c 1 {ip_of(net, 'h4')}").split() if w.startswith("ttl=")] return None if ttl == ["ttl=62"] else f"h4's reply should arrive with TTL 62 (two routers), got {ttl}" def main(): setLogLevel("info") mode = sys.argv[1] if len(sys.argv) > 1 else "check" net = start() try: if mode == "cli": from mininet.cli import CLI CLI(net) return results = [] for title, check in [("every interface is on its own network", check_addresses), ("every host reaches every other host", check_reachability), ("h1 to h4 crosses both routers", check_path)]: try: problem = check(net) except Exception as e: # reported, then the next check runs problem = str(e) if isinstance(e, LookupError) else f"{type(e).__name__}: {e}" results.append(problem is None) print(("PASS " if problem is None else "FAIL ") + title + ("" if problem is None else f": {problem}")) print(f"\n{sum(results)} passed, {len(results) - sum(results)} failed") sys.exit(0 if all(results) else 1) finally: net.stop() if __name__ == "__main__": main()