def do_ips(self, line): """ips n1 n2 ...: return the ips associated to the given node name""" for n in line.split(' '): try: l = [itf.ip for itf in self.mn[n].intfList()] except KeyError: l = 'unknown node' finally: lg.output(n, '|', l, "\n")
def restoreIntfs(interfaces: List[IPIntf]): """Restore interfaces :param interfaces: the list of interfaces to restore """ log.output("** starting restoring link\n") for interface in interfaces: interface.up(restore=True) log.output("** interfaces " + str(interface) + " up\n")
def do_ip(self, line): """ip IP1 IP2 ...: return the node associated to the given IP""" for ip in line.split(' '): try: n = self.mn.node_for_ip(ip) except KeyError: n = 'unknown IP' finally: lg.output(ip, '|', n, "\n")
def get_config(): conf = None try: line = sys.stdin.readline() conf = eval(line) except Exception as e: lg.output(str(e)) if 'controllers' not in conf: return None if 'topo_discovery' not in conf: return None return conf
def do_ips(self, line: str): """ips n1 n2 ...: return the ips associated to the given node name""" for n in line.split(' '): try: ips = [ ip.ip.compressed for itf in self.mn[n].intfList() for ip in list(itf.ips()) + list(itf.ip6s(exclude_lls=True)) ] except KeyError: ips = 'unknown node' finally: lg.output(n, '|', ips, "\n")
def main(): # Set Log Level lg.setLogLevel('info') #lg.setLogLevel( 'error' ) #Read and parse configuration conf = get_config() if conf is None: send_output(None) return ctlip = list() ctlport = list() for i in range(0, len(conf['controllers'])): ctlip.append(conf['controllers'][i][0]) ctlport.append(conf['controllers'][i][1]) #Setup Network ts = time.time() topo = FixedTree(depth=1, fanout=2) network = Mininet(topo=topo, controller=None, switch=OVSKernelSwitch) for i in range(0, len(ctlip)): network.addController('c' + str(i + 1), controller=RemoteController, ip=ctlip[i], port=ctlport[i]) network.start() if ENABLE_STAT: lg.output('[timer] Setup network: %f sec.\n' % (time.time() - ts)) #Start Switch Monitor monitor_id = procmon.start(pname=SWITCH_PNAME, lg=lg) #Tests ts = time.time() hc = HostController(network, lg, conf) results = hc.do_test() if results == None: results = [False] if ENABLE_STAT: lg.output('[timer] Tests: %f sec.\n' % (time.time() - ts)) #Dump process monitor output stat_dict = procmon.stop(monitor_id, lg) # Pull Rules raw = [] for s in network.switches: raw.append(s.name + "," + s.dpctl("dump-flows")) #Write Output d = {"results": results, "rules": raw, "stat": stat_dict} send_output(d) lg.output(str(d) + "\n") # Cleanup Network ts = time.time() network.stop() if ENABLE_STAT: lg.output('[timer] Stop network: %f sec.\n' % (time.time() - ts))
def _ping_set(self, src: Node, dst_dict: Mapping[Node, Union[IPv4Address, IPv6Address, str]], timeout: Optional[str], v4=True) -> Tuple[int, int]: """Do the actual ping to the dict of {dst: dst_ip} from src :param src: origin of the ping :param dst_dict: destinations {dst: dst_ip} of the ping :param timeout: the time to wait for a response, as string :param v4: whether IPv4 or IPv6 is used :param return: a tuple (lost packets, sent packets)""" if len(dst_dict) == 0: return 0, 0 lost = 0 packets = 0 opts = '' if timeout: opts = '-W %s' % timeout log.output("%s --%s--> " % (src.name, "IPv4" if v4 else "IPv6")) for dst, dst_ip in dst_dict.items(): result = src.cmd('%s -c1 %s %s' % ("ping" if v4 else PING6_CMD, opts, dst_ip)) sent, received = self._parsePing(result) lost += sent - received packets += sent log.output("%s " % dst.name if received else "X ") log.output('\n') return lost, packets
def _ping_set(self, src, dst_dict, timeout, v4=True): """Do the actual ping to the dict of {dst: dst_ip} from src :param src: origin of the ping :param dst_dict: destinations {dst: dst_ip} of the ping :param timeout: the time to wait for a response, as string :param v4: whether IPv4 or IPv6 is used :param return: a tuple (lost packets, sent packets)""" if len(dst_dict) == 0: return 0, 0 lost = 0 packets = 0 opts = '' if timeout: opts = '-W %s' % timeout log.output("%s --%s--> " % (src.name, "IPv4" if v4 else "IPv6")) for dst, dst_ip in dst_dict.iteritems(): result = src.cmd('%s -c1 %s %s' % ("ping" if v4 else PING6_CMD, opts, dst_ip)) sent, received = self._parsePing(result) lost += sent - received packets += sent log.output("%s " % dst.name if received else "X ") log.output('\n') return lost, packets
def runFailurePlan(self, failure_plan: List[Tuple[str, str]]) \ -> List[IPIntf]: """Run a failure plan :param: A list of pairs of node names: links connecting these two links will be downed :return: A list of interfaces that were downed """ log.output("** Starting failure plan\n") interfaces_down = [] for node1, node2 in failure_plan: try: links = self[node1].connectionsTo(self[node2]) for link in links: interfaces_down.extend(link) except KeyError as e: log.error("Node " + str(e) + " does not exist\n") interfaces_down = [] for interface in interfaces_down: interface.down(backup=True) log.output("** Interface " + str(interface) + " down\n") return interfaces_down
def randomFailure(self, n: int, weak_links: Optional[List[IPLink]] = None)\ -> List[IPIntf]: """Randomly down 'n' link :param n: the number of link to be downed :param weak_links: the list of links that can be downed; if set to None, every network link can be downed :return: the list of interfaces which were downed """ all_links = weak_links if weak_links is not None else self.links number_of_links = len(all_links) if n > number_of_links: log.error("More link down requested than number of link" " that can be downed\n") return [] downed_interfaces = [] down_links = random.sample(all_links, k=n) for link in down_links: for intf in [link.intf1, link.intf2]: intf.down(backup=True) log.output("** Interface " + str(intf) + " down\n") downed_interfaces.append(intf) return downed_interfaces
def ping(self, hosts: Optional[List[Node]] = None, timeout: Optional[str] = None, use_v4=True, use_v6=True) -> float: """Ping between all specified hosts. If use_v4 is true, pings over IPv4 are used between any pair of hosts having at least one IPv4 address on one of their interfaces (loopback excluded). If use_v6 is true, pings over IPv6 are used between any pair of hosts having at least one non-link-local IPv6 address on one of their interfaces (loopback excluded). :param hosts: list of hosts or None if all must be pinged :param timeout: time to wait for a response, as string :param use_v4: whether IPv4 addresses can be used :param use_v6: whether IPv6 addresses can be used :return: the packet loss percentage of IPv4 connectivity if self.use_v4 is set the loss percentage of IPv6 connectivity otherwise""" packets = lost = 0 host_list = self.hosts if hosts is not None: host_list = hosts incompatible_hosts = {} # type: Dict[str, Set[str]] if not use_v4 and not use_v6: log.output("*** Warning: Parameters forbid both IPv4 and IPv6 for " "pings\n") return 0 log.output("*** Ping: testing reachability over %s%s%s\n" % ("IPv4" if use_v4 else "", " and " if use_v4 and use_v6 else "", "IPv6" if use_v6 else "")) for src in host_list: src_ip, src_ip6 = address_pair(src, use_v4, use_v6) ping_dict = {} ping6_dict = {} for dst in host_list: if src != dst: dst_ip, dst_ip6 = address_pair(dst, src_ip is not None, src_ip6 is not None) if dst_ip is not None: ping_dict[dst] = dst_ip if dst_ip6 is not None: ping6_dict[dst] = dst_ip6 if (use_v4 and dst_ip is None and use_v6 and dst_ip6 is None): node1 = src if src.name <= dst.name else dst node2 = src if node1 != src else dst if node2.name not in incompatible_hosts.setdefault( node1.name, set()): incompatible_hosts[node1.name].add(node2.name) result = self._ping_set(src, ping_dict, timeout, True) lost += result[0] packets += result[1] result = self._ping_set(src, ping6_dict, timeout, False) lost += result[0] packets += result[1] for node1, incompatibilities in incompatible_hosts.items(): for node2 in incompatibilities: log.output("*** Warning: %s and %s have no global address " "in the same IP version\n" % (node1, node2)) if packets > 0: ploss = 100.0 * lost / packets received = packets - lost log.output("*** Results: %i%% dropped (%d/%d received)\n" % (ploss, received, packets)) else: ploss = 0 log.output("*** Warning: No packets sent\n") return ploss
stat_dict = procmon.stop(monitor_id, lg) except Exception as e: lg.output(str(e)) stat_dict = {} # Pull Rules raw = [] for s in network.switches: raw.append(s.name + "," + s.dpctl("dump-flows")) #Write Output d = {"results": results, "rules": raw, "stat": stat_dict} send_output(d) lg.output(str(d) + "\n") # Cleanup Network ts = time.time() network.stop() if ENABLE_STAT: lg.output('[timer] Stop network: %f sec.\n' % (time.time() - ts)) # Main if __name__ == '__main__': try: main() except Exception as e: send_output(None) lg.output(e) os.system("mn -c > /dev/null 2>/dev/null")
def ping(self, hosts=None, timeout=None, use_v4=True, use_v6=True): """Ping between all specified hosts. If use_v4 is true, pings over IPv4 are used between any pair of hosts having at least one IPv4 address on one of their interfaces (loopback excluded). If use_v6 is true, pings over IPv6 are used between any pair of hosts having at least one non-link-local IPv6 address on one of their interfaces (loopback excluded). :param hosts: list of hosts or None if all must be pinged :param timeout: time to wait for a response, as string :param use_v4: whether IPv4 addresses can be used :param use_v6: whether IPv6 addresses can be used :return: the packet loss percentage of IPv4 connectivity if self.use_v4 is set the loss percentage of IPv6 connectivity otherwise""" packets = lost = 0 if not hosts: hosts = self.hosts incompatible_hosts = {} if not use_v4 and not use_v6: log.output("*** Warning: Parameters forbid both IPv4 and IPv6 for " "pings\n") return 0 log.output("*** Ping: testing reachability over %s%s%s\n" % ("IPv4" if use_v4 else "", " and " if use_v4 and use_v6 else "", "IPv6" if use_v6 else "")) for src in hosts: src_ip, src_ip6 = address_pair(src, use_v4, use_v6) ping_dict = {} ping6_dict = {} for dst in hosts: if src != dst: dst_ip, dst_ip6 = address_pair(dst, src_ip is not None, src_ip6 is not None) if dst_ip is not None: ping_dict[dst] = dst_ip if dst_ip6 is not None: ping6_dict[dst] = dst_ip6 if (use_v4 and dst_ip is None and use_v6 and dst_ip6 is None): node1 = src if src.name <= dst.name else dst node2 = src if node1 != src else dst if node2.name not in incompatible_hosts.setdefault( node1.name, set()): incompatible_hosts[node1.name].add(node2.name) result = self._ping_set(src, ping_dict, timeout, True) lost += result[0] packets += result[1] result = self._ping_set(src, ping6_dict, timeout, False) lost += result[0] packets += result[1] for node1, incompatibilities in incompatible_hosts.iteritems(): for node2 in incompatibilities: log.output("*** Warning: %s and %s have no global address " "in the same IP version\n" % (node1, node2)) if packets > 0: ploss = 100.0 * lost / packets received = packets - lost log.output("*** Results: %i%% dropped (%d/%d received)\n" % (ploss, received, packets)) else: ploss = 0 log.output("*** Warning: No packets sent\n") return ploss
def do(datadir, jsondir, tmpdir, runs): """ :param datadir: directory where to save pcaps :param jsondir: directory where to save jsons :param tmpdir: directory where to save pcaps temporarily :param runs: list of topology information """ runtime_params = [] # it is possible to have multiple topologies in parallel (untested, we only used one) for run in runs: runtime_params.append({}) runtime_params[-1]['time'] = run['time'] runtime_params[-1]['opts'] = run ok = False while True: # find unused filenames uniqueid = uuid.uuid4().hex runtime_params[-1]['uniquename'] = run['pcapName'] + '+' + uniqueid logfilename = jsondir + runtime_params[-1]['uniquename'] + ".log" templogfilename = tmpdir + '/' + runtime_params[-1][ 'uniquename'] + ".log" runtime_params[-1]['templogfilename'] = templogfilename runtime_params[-1]['logfilename'] = logfilename runtime_params[-1]['jsonfilename'] = jsondir + runtime_params[-1][ 'uniquename'] + ".json" if not os._exists(logfilename): break # log into file runtime_params[-1]['filehandler'] = logging.FileHandler( templogfilename) runtime_params[-1]['filehandler'].setLevel(logging.INFO) lg.addHandler(runtime_params[-1]['filehandler']) retry = 0 while True: retry += 1 # setup topology from params topo = DynamicTopo(runs) net = MininetCong( topo=topo, link=TCLinkTBF, controller=None, # OVSController, switch=OVSBridge) # setup and build network net.start() net.waitConnected() for runtime in runtime_params: lg.output("%s\n" % runtime['opts']) run_senders = topo.getSenders() run_recordsenders = topo.getRecordsenders() run_receivers = topo.getReceivers() run_recorddevcons = topo.getRecordDevCons() # print(run_recorddevcons) run_recordlinks = topo.getRecordingLinks() switch_by_name = {s.name: s for s in net.switches} hosts_by_name = {h.name: h for h in net.hosts} run_senders = [[hosts_by_name[sender] for sender in senders] for senders in run_senders] run_receivers = [[hosts_by_name[receiver] for receiver in receivers] for receivers in run_receivers] run_recordsenders = [[ hosts_by_name[recordsender] for recordsender in recordsenders ] for recordsenders in run_recordsenders] run_recorddevcons = [[(hosts_by_name[a], hosts_by_name[b], c, p) for a, b, c, p in devcons] for devcons in run_recorddevcons] recs = [] # set up recordings for senders, recordlinks, runtime, recordsenders in zip( run_senders, run_recordlinks, runtime_params, run_recordsenders): last_sender = recordsenders[0] assert len(recordsenders) == 1 # switches = [(s0, "s0"), (s1, "s1"), (s4, "s4"), (s5, "s5")] # edgeswitches = [(switch_by_name[first_switch], "s0"), (switch_by_name[last_switch], "s5")] runtime['opts']['ip'] = last_sender.IP() for link, switchname, name in recordlinks: print(link()) switch = switch_by_name[switchname] filename = runtime['uniquename'] + "+" + name + ".pcap" recs.append( PcapRecorder(switch, tmpdir + '/' + filename, link().name, last_sender.IP(), datadir + filename)) # try up to 40 times to ping the connected hots for i in range(40): l = 0 for senders, receivers, recordsenders in zip( run_senders, run_receivers, run_recordsenders): all_hosts = senders + recordsenders + receivers l += net.ping(all_hosts) if l == 0: break sleep(2) # start trafficgen server for receiver, runtime in zip(run_receivers, runtime_params): for h2 in receiver: lg.output("start own server %s\n" % h2.name) # h1 is client sender, h2 is server receiver # data is sent from h1 to h2 net.ownStartServer(h2, seconds=runtime['time'] + 10) # h2.cmd("timeout 20 nc -l -p 5001 > /dev/null &") sleep(2) # start test is server is listening con = True for recorddevcon, runtime in zip(run_recorddevcons, runtime_params): for (h1, h2, c, p) in recorddevcon: lg.output("test connection between server %s and client %s\n" % (h2.name, h1.name)) con = net.ownTestConnection(h2, h1) if not con: break if not con: lg.output("connection failed\n") with open("errors.txt", "a+") as f: f.write("connection failed\n") for receiver in run_receivers: for h2 in receiver: lg.output("stop own server %s\n" % h2.name) # h1 is client, h2 is server # data is sent from h1 to h2 net.ownStopServer(h2) try: net.stop() except: pass cleanup() if retry <= 3: continue else: lg.output("3 retries failed\n") with open("errors.txt", "a+") as f: f.write("3 retries failed\n") break lg.output("run generation\n") # start client (sender) of background connections i = 0 for recordsenders, devcon, runtime in zip(run_recordsenders, run_recorddevcons, runtime_params): # for (h1, h2) in zip(sender, receiver): for (h1, h2, cong, pacing) in devcon: if h1 in recordsenders: continue net.ownStartClient(h2, h1, seconds=runtime['time'] + 2 + 2, cong=cong, pacing=pacing) # h1.cmd("timeout 15 nc 10.0.0.1 5001 & ") i += 1 # start tcpdump recording for rec in recs: rec.start() sleep(2) # start client (sender) which should be recorded for recordsenders, devcon, runtime in zip(run_recordsenders, run_recorddevcons, runtime_params): # for (h1, h2) in zip(sender, receiver): for (h1, h2, cong, pacing) in devcon: if not h1 in recordsenders: continue net.ownStartClient(h2, h1, seconds=runtime['time'], cong=cong, pacing=pacing) # h1.cmd("timeout 10 dd if=/dev/zero | nc 10.0.0.2 5001") # net.iperf((h1, h2), seconds=5, cong=cong[-1]) sleep(max([runtime['time'] for runtime in runtime_params]) + 2) # stop recording for rec in recs: rec.stop() # stop client and server and check whether succesful try: for sender, receiver, recordsender, runtime in zip( run_senders, run_receivers, run_recordsenders, runtime_params): for h1 in sender + recordsender: # h1 is client, h2 is server lg.output("stop %s\n" % h1.name) net._parseOwn(net.ownStopClient(h1)) for h2 in receiver: lg.output("stop %s\n" % h2.name) net._parseOwn(net.ownStopServer(h2)) except Exception as e: lg.output("stopping hosts failed\n") with open("errors.txt", "a+") as f: f.write("stopping hosts failed " + str(e) + "\n") for sender, receiver, recordsender, runtime in zip( run_senders, run_receivers, run_recordsenders, runtime_params): for h1 in sender + recordsender: # h1 is client, h2 is server lg.output("stop %s\n" % h1.name) net.ownStopClient(h1) for h2 in receiver: lg.output("stop %s\n" % h2.name) net.ownStopServer(h2) cleanup() if retry <= 3: continue else: lg.output("3 retries failed\n") with open("errors.txt", "a+") as f: f.write("3 retries failed\n") break net.stop() ok = True break print("remove handler\n") for runtime in runtime_params: with open(runtime['jsonfilename'], 'w') as f: f.write(json.dumps(runtime['opts'])) f.flush() lg.removeHandler(runtime['filehandler']) shutil.move(runtime['templogfilename'], runtime['logfilename']) cleanup() print("done\n") return ok
def do_route(self, line=""): """route destination: Print all the routes towards that destination for every router in the network""" for r in self.mn.routers: lg.output("[%s] " % r.name) self.default('%s ip route get %s' % (r.name, line))