def check_connectivity(simulation): ''' Return any pairs that couldn't reach each other ''' # Always check liveness down_controllers = InvariantChecker.check_liveness(simulation) if down_controllers != []: return down_controllers # Effectively, run compute physical omega, ignore concrete values of headers, and # check that all pairs can reach eachother physical_omega = InvariantChecker.compute_physical_omega(simulation.topology.live_switches, simulation.topology.live_links, simulation.topology.access_links) connected_pairs = set() # Omegas are { original port -> [(final hs1, final port1), (final hs2, final port2)...] } for start_port, final_location_list in physical_omega.iteritems(): for _, final_port in final_location_list: connected_pairs.add((start_port, final_port)) # TODO(cs): translate HSA port numbers to ofp_phy_ports in the # headerspace/ module instead of computing uniq_port_id here access_links = simulation.topology.access_links all_pairs = [ (get_uniq_port_id(l1.switch, l1.switch_port),get_uniq_port_id(l2.switch, l2.switch_port)) for l1 in access_links for l2 in access_links if l1 != l2 ] all_pairs = set(all_pairs) remaining_pairs = all_pairs - connected_pairs # TODO(cs): don't print results here if len(remaining_pairs) > 0: msg.fail("Not all %d pairs are connected! (%d missing)" % (len(all_pairs),len(remaining_pairs))) log.info("remaining_pairs: %s" % (str(remaining_pairs))) else: msg.success("Fully connected!") return list(remaining_pairs)
def invoke_hassel_c(start_port, edge_ports): ''' invoke reachability test, and return the proc object ''' if type(start_port) != int: start_port = get_uniq_port_id(start_port.switch, start_port.switch_port) if type(edge_ports) != list: edge_ports = list(edge_ports) if type(edge_ports[0]) != int: edge_ports = map(lambda access_link: get_uniq_port_id(access_link.switch, access_link.switch_port), edge_ports) edge_ports.remove(start_port) # Note that invoking # `sts 200002 200002 1000002` returns nothing, whereas # `sts 200002 100002 2000002` returns something. # It appears that hassel-c assumes that the out ports are sorted. edge_ports.sort() log.debug("port %d is being checked" % start_port) str_start_port = str(start_port) str_edge_ports = map(str, edge_ports) old_cwd = os.getcwd() try: os.chdir(HASSEL_C_PATH) proc = subprocess.Popen(["./sts", str(start_port)] + str_edge_ports, stdout=subprocess.PIPE, stderr=open('/dev/null', "w")) finally: os.chdir(old_cwd) return proc
def invoke_hassel_c(start_port, edge_ports): ''' invoke reachability test, and return the proc object ''' if type(start_port) != int: start_port = get_uniq_port_id(start_port.switch, start_port.switch_port) if type(edge_ports) != list: edge_ports = list(edge_ports) if type(edge_ports[0]) != int: edge_ports = map(lambda access_link: get_uniq_port_id(access_link.switch, access_link.switch_port), edge_ports) # TODO(cs): just noticed that invoking # `sts 200002 200002 1000002` returns nothing, whereas # `sts 200002 100002 2000002` returns something. # Why should the order of the out ports matter? Need to check that # hassel-c is actually computing all paths for all out ports edge_ports.remove(start_port) log.debug("port %d is being checked" % start_port) str_start_port = str(start_port) str_edge_ports = map(str, edge_ports) old_cwd = os.getcwd() try: os.chdir(HASSEL_C_PATH) proc = subprocess.Popen(["./sts", str(start_port)] + str_edge_ports, stdout=subprocess.PIPE, stderr=open('/dev/null', "w")) finally: os.chdir(old_cwd) return proc
def check_connectivity(simulation): ''' Return any pairs that couldn't reach each other ''' # Always check liveness down_controllers = InvariantChecker.check_liveness(simulation) if down_controllers != []: return down_controllers # Effectively, run compute physical omega, ignore concrete values of headers, and # check that all pairs can reach eachother physical_omega = InvariantChecker.compute_physical_omega( simulation.topology.live_switches, simulation.topology.live_links, simulation.topology.access_links) connected_pairs = set() # Omegas are { original port -> [(final hs1, final port1), (final hs2, final port2)...] } for start_port, final_location_list in physical_omega.iteritems(): for _, final_port in final_location_list: connected_pairs.add((start_port, final_port)) # TODO(cs): translate HSA port numbers to ofp_phy_ports in the # headerspace/ module instead of computing uniq_port_id here access_links = simulation.topology.access_links all_pairs = [(get_uniq_port_id(l1.switch, l1.switch_port), get_uniq_port_id(l2.switch, l2.switch_port)) for l1 in access_links for l2 in access_links if l1 != l2] all_pairs = set(all_pairs) remaining_pairs = all_pairs - connected_pairs # TODO(cs): don't print results here if len(remaining_pairs) > 0: msg.fail("Not all %d pairs are connected! (%d missing)" % (len(all_pairs), len(remaining_pairs))) log.info("remaining_pairs: %s" % (str(remaining_pairs))) else: msg.success("Fully connected!") return list(remaining_pairs)
def _get_all_pairs(simulation): # TODO(cs): translate HSA port numbers to ofp_phy_ports in the # headerspace/ module instead of computing uniq_port_id here access_links = simulation.topology.access_links all_pairs = [ (get_uniq_port_id(l1.switch, l1.switch_port),get_uniq_port_id(l2.switch, l2.switch_port)) for l1 in access_links for l2 in access_links if l1 != l2 ] all_pairs = set(all_pairs) return all_pairs
def generate_TTF(all_links): ''' Takes a list of sts.debugger_entities.Link objects (directed) ''' ttf = tf.TF(of.HS_FORMAT()) for link in all_links: uniq_from_port = of.get_uniq_port_id(link.start_software_switch, link.start_port) uniq_to_port = of.get_uniq_port_id(link.end_software_switch, link.end_port) rule = tf.TF.create_standard_rule([uniq_from_port], None,[uniq_to_port], None, None) ttf.add_link_rule(rule) log.debug("topology transfer function (links): %s" % str(ttf)) return ttf
def translate_ports(ports): ports = list(ports) if type(ports[0]) != int: # They are switch objects port_nos = [] for sw in ports: for port_no in sw.ports.keys(): port_nos.append(get_uniq_port_id(sw, port_no)) ports = port_nos return ports
def detect_loop(NTF, TTF, ports, reverse_map, test_packet = None): if type(ports[0]) != int: ports = map(lambda access_link: get_uniq_port_id(access_link.switch, access_link.switch_port), ports) loops = [] for port in ports: log.debug("port %d is being checked"%port) propagation = [] # put all-x test packet in propagation graph test_pkt = test_packet if test_pkt == None: test_pkt = get_all_x(NTF) p_node = {} p_node["hdr"] = test_pkt p_node["port"] = port p_node["visits"] = [] p_node["hs_history"] = [] propagation.append(p_node) while len(propagation)>0: #get the next node in propagation graph and apply it to NTF and TTF log.debug("Propagation has length: %d"%len(propagation)) tmp_propag = [] for p_node in propagation: # hp is "header port" next_hp = NTF.T(p_node["hdr"],p_node["port"]) for (next_h,next_ps) in next_hp: for next_p in next_ps: linked = TTF.T(next_h,next_p) for (linked_h,linked_ports) in linked: for linked_p in linked_ports: new_p_node = {} new_p_node["hdr"] = linked_h new_p_node["port"] = linked_p new_p_node["visits"] = list(p_node["visits"]) new_p_node["visits"].append(p_node["port"]) #new_p_node["visits"].append(next_p) new_p_node["hs_history"] = list(p_node["hs_history"]) new_p_node["hs_history"].append(p_node["hdr"]) #print new_p_node if len(new_p_node["visits"]) > 0 and new_p_node["visits"][0] == linked_p: loops.append(new_p_node) log.warn("loop detected") elif linked_p in new_p_node["visits"]: # if (linked_p not in ports): # print "WARNING: detected a loop whose port is not in checked ports - branch aborted:" # print_loops([new_p_node],reverse_map) # print "END OF WARNING" pass else: tmp_propag.append(new_p_node) propagation = tmp_propag return loops
def compute_omega(name_tf_pairs, TTF, edge_links): prepare_hassel_c(name_tf_pairs, TTF) omega = {} # TODO(cs): need to model host end of link, or does switch end suffice? edge_ports = map(lambda access_link: get_uniq_port_id(access_link.switch, access_link.switch_port), edge_links) log.debug("edge_ports: %s" % edge_ports) for start_port in edge_ports: port_omega = compute_single_omega(start_port, list(edge_ports)) omega = dict(omega.items() + port_omega.items()) return omega
def compute_single_omega(start_port, edge_ports): if type(start_port) != int: start_port = get_uniq_port_id(start_port.switch, start_port.switch_port) proc = invoke_hassel_c(start_port, edge_ports) omega = { start_port : [] } second_to_last_line = '' last_line = '' while True: # Format is: # <Original> Port: ... # <Next> Port: ... # ... # <Edge> Port: ... # HS: D0,D0,D0,D8,D123,D123,... # ----- # <Original> Port -> ... # ... line = proc.stdout.readline() if line == '': break print line.rstrip() if line.startswith("-----"): hs_string = last_line.split(":")[1].strip() # Get rid of commas (otherwise, will yield incorrect byte array) hs_string = hs_string.replace(",", "") port_stripped = second_to_last_line.split(":")[1].lstrip() out_port_string = port_stripped[0:port_stripped.index(",")] out_port = int(out_port_string) readable_hs = headerspace(of.hs_format) readable_hs.add_hs(hs_string_to_byte_array(hs_string)) omega[start_port].append((readable_hs, out_port)) second_to_last_line = last_line last_line = line return omega
def check_partitions(switches, live_links, access_links): # TODO(cs): lifted directly from pox.forwarding.l2_multi. Highly # redundant! # Adjacency map. [sw1][sw2] -> port from sw1 to sw2 adjacency = defaultdict(lambda:defaultdict(lambda:None)) for link in live_links: # Make sure to disregard links that are adjacent to down switches # (technically those links are still `live', but it's easier to treat it # this way) if not (link.start_software_switch.failed or link.end_software_switch.failed): adjacency[link.start_software_switch][link.end_software_switch] = link # Switches we know of. [dpid] -> Switch switches = { sw.dpid : sw for sw in switches } # [sw1][sw2] -> (distance, intermediate) path_map = defaultdict(lambda:defaultdict(lambda:(None,None))) def _calc_paths (): """ Essentially Floyd-Warshall algorithm """ sws = switches.values() path_map.clear() for k in sws: for j,port in adjacency[k].iteritems(): if port is None: continue path_map[k][j] = (1,None) path_map[k][k] = (0,None) # distance, intermediate """ for i in sws: for j in sws: a = path_map[i][j][0] #a = adjacency[i][j] if a is None: a = "*" print a, print """ for k in sws: for i in sws: for j in sws: if path_map[i][k][0] is not None: if path_map[k][j][0] is not None: # i -> k -> j exists ikj_dist = path_map[i][k][0]+path_map[k][j][0] if path_map[i][j][0] is None or ikj_dist < path_map[i][j][0]: # i -> k -> j is better than existing path_map[i][j] = (ikj_dist, k) """ print "--------------------" for i in sws: for j in sws: print path_map[i][j][0], print """ all_link_pairs = [ (l1,l2) for l1 in access_links for l2 in access_links if l1 != l2 ] _calc_paths() partioned_pairs = set() for link_pair in all_link_pairs: if path_map[link_pair[0].switch][link_pair[1].switch] == (None,None): id1 = get_uniq_port_id(link_pair[0].switch, link_pair[0].switch_port) id2 = get_uniq_port_id(link_pair[1].switch, link_pair[1].switch_port) partioned_pairs.add((id1,id2)) return partioned_pairs
def find_reachability(NTF, TTF, edge_links, test_packet=None): edge_ports = map(lambda access_link: get_uniq_port_id(access_link.switch, access_link.switch_port), edge_links) paths = defaultdict(list) propagation = [] if len(edge_ports) == 0: log.warn("No ports to check!") return [] for in_port in edge_ports: out_ports = list(set(edge_ports) - set([in_port])) # put all-x test packet in propagation graph input_pkt = test_packet if input_pkt == None: input_pkt = get_all_x(NTF) p_node = {} p_node["hdr"] = input_pkt p_node["port"] = in_port p_node["visits"] = [] p_node["hs_history"] = [] propagation.append(p_node) while len(propagation)>0: #get the next node in propagation graph and apply it to NTF and TTF log.debug("Propagation has length: %d"%len(propagation)) tmp_propagate = [] for p_node in propagation: next_hp = NTF.T(p_node["hdr"],p_node["port"]) for (next_h,next_ps) in next_hp: for next_p in next_ps: if next_p in out_ports: reached = {} reached["hdr"] = next_h reached["port"] = next_p reached["visits"] = list(p_node["visits"]) reached["visits"].append(p_node["port"]) reached["hs_history"] = list(p_node["hs_history"]) paths[in_port].append(reached) else: linked = TTF.T(next_h,next_p) for (linked_h,linked_ports) in linked: for linked_p in linked_ports: new_p_node = {} new_p_node["hdr"] = linked_h new_p_node["port"] = linked_p new_p_node["visits"] = list(p_node["visits"]) new_p_node["visits"].append(p_node["port"]) new_p_node["visits"].append(next_p) new_p_node["hs_history"] = list(p_node["hs_history"]) new_p_node["hs_history"].append(p_node["hdr"]) if linked_p in out_ports: paths[in_port].append(new_p_node) elif linked_p in new_p_node["visits"]: log.warn("WARNING: detected a loop - branch aborted: \nHeaderSpace: %s\n Visited Ports: %s\nLast Port %d "%(\ new_p_node["hdr"],new_p_node["visits"],new_p_node["port"])) else: tmp_propagate.append(new_p_node) propagation = tmp_propagate return paths
def find_blackholes(NTF, TTF, edge_links, test_packet=None): '''Do any switches: - send packets into a down link? - drop packets that are supposed to go out their in_port? Specifically, checks whether it's possible for any packets to fall into the blackhole in the first place. ''' edge_ports = map(lambda access_link: get_uniq_port_id(access_link.switch, access_link.switch_port), edge_links) blackholes = [] propagation = [] if len(edge_ports) == 0: log.warn("No ports to check!") return [] for in_port in edge_ports: out_ports = list(set(edge_ports) - set([in_port])) # put all-x test packet in propagation graph input_pkt = test_packet if input_pkt == None: input_pkt = get_all_x(NTF) p_node = {} p_node["hdr"] = input_pkt p_node["port"] = in_port p_node["visits"] = [] p_node["hs_history"] = [] propagation.append(p_node) while len(propagation)>0: #get the next node in propagation graph and apply it to NTF and TTF log.debug("Propagation has length: %d"%len(propagation)) tmp_propagate = [] for p_node in propagation: next_hp = NTF.T(p_node["hdr"],p_node["port"]) propagated = False for (next_h,next_ps) in next_hp: propagated = True for next_p in next_ps: if next_p in out_ports: pass else: # Is an internal port linked = TTF.T(next_h,next_p) for (linked_h,linked_ports) in linked: for linked_p in linked_ports: new_p_node = {} new_p_node["hdr"] = linked_h new_p_node["port"] = linked_p new_p_node["visits"] = list(p_node["visits"]) new_p_node["visits"].append(p_node["port"]) new_p_node["visits"].append(next_p) new_p_node["hs_history"] = list(p_node["hs_history"]) new_p_node["hs_history"].append(p_node["hdr"]) if linked_p in out_ports: pass elif linked_p in new_p_node["visits"]: log.warn("WARNING: detected a loop - branch aborted: \nHeaderSpace: %s\n Visited Ports: %s\nLast Port %d "%(\ new_p_node["hdr"],new_p_node["visits"],new_p_node["port"])) else: tmp_propagate.append(new_p_node) if not propagated and len(list(p_node["visits"])) != 0: # Append a tuple: (last egress port, [preceding ports]) blackholes.append((next_p,list(p_node["visits"]))) propagation = tmp_propagate return blackholes