class RoutingController(object): def __init__(self): self.topo = Topology(db="topology.db") self.controllers = {} self.init() def init(self): self.connect_to_switches() self.reset_states() self.set_table_defaults() def reset_states(self): [controller.reset_state() for controller in self.controllers.values()] def connect_to_switches(self): for p4switch in self.topo.get_p4switches(): thrift_port = self.topo.get_thrift_port(p4switch) self.controllers[p4switch] = SimpleSwitchAPI(thrift_port) def set_table_defaults(self): for controller in self.controllers.values(): controller.table_set_default("ipv4_lpm", "drop", []) controller.table_set_default("ecmp_group_to_nhop", "drop", []) def add_mirroring_ids(self): for sw_name, controller in self.controllers.items(): controller.mirroring_add(100, 1) def set_egress_type_table(self): for sw_name, controller in self.controllers.items(): for intf, node in self.topo.get_interfaces_to_node( sw_name).items(): node_type = self.topo.get_node_type(node) port_number = self.topo.interface_to_port(sw_name, intf) if node_type == 'host': node_type_num = 1 elif node_type == 'switch': node_type_num = 2 print "table_add at {}:".format(sw_name) self.controllers[sw_name].table_add("egress_type", "set_egress_type", [str(port_number)], [str(node_type_num)]) def route(self): switch_ecmp_groups = { sw_name: {} for sw_name in self.topo.get_p4switches().keys() } for sw_name, controller in self.controllers.items(): for sw_dst in self.topo.get_p4switches(): #if its ourselves we create direct connections if sw_name == sw_dst: for host in self.topo.get_hosts_connected_to(sw_name): sw_port = self.topo.node_to_node_port_num( sw_name, host) host_ip = self.topo.get_host_ip(host) + "/32" host_mac = self.topo.get_host_mac(host) #add rule print "table_add at {}:".format(sw_name) self.controllers[sw_name].table_add( "ipv4_lpm", "set_nhop", [str(host_ip)], [str(host_mac), str(sw_port)]) #check if there are directly connected hosts else: if self.topo.get_hosts_connected_to(sw_dst): paths = self.topo.get_shortest_paths_between_nodes( sw_name, sw_dst) for host in self.topo.get_hosts_connected_to(sw_dst): if len(paths) == 1: next_hop = paths[0][1] host_ip = self.topo.get_host_ip(host) + "/24" sw_port = self.topo.node_to_node_port_num( sw_name, next_hop) dst_sw_mac = self.topo.node_to_node_mac( next_hop, sw_name) #add rule print "table_add at {}:".format(sw_name) self.controllers[sw_name].table_add( "ipv4_lpm", "set_nhop", [str(host_ip)], [str(dst_sw_mac), str(sw_port)]) elif len(paths) > 1: next_hops = [x[1] for x in paths] dst_macs_ports = [ (self.topo.node_to_node_mac( next_hop, sw_name), self.topo.node_to_node_port_num( sw_name, next_hop)) for next_hop in next_hops ] host_ip = self.topo.get_host_ip(host) + "/24" #check if the ecmp group already exists. The ecmp group is defined by the number of next #ports used, thus we can use dst_macs_ports as key if switch_ecmp_groups[sw_name].get( tuple(dst_macs_ports), None): ecmp_group_id = switch_ecmp_groups[ sw_name].get(tuple(dst_macs_ports), None) print "table_add at {}:".format(sw_name) self.controllers[sw_name].table_add( "ipv4_lpm", "ecmp_group", [str(host_ip)], [ str(ecmp_group_id), str(len(dst_macs_ports)) ]) #new ecmp group for this switch else: new_ecmp_group_id = len( switch_ecmp_groups[sw_name]) + 1 switch_ecmp_groups[sw_name][tuple( dst_macs_ports)] = new_ecmp_group_id #add group for i, (mac, port) in enumerate(dst_macs_ports): print "table_add at {}:".format( sw_name) self.controllers[sw_name].table_add( "ecmp_group_to_nhop", "set_nhop", [str(new_ecmp_group_id), str(i)], [str(mac), str(port)]) #add forwarding rule print "table_add at {}:".format(sw_name) self.controllers[sw_name].table_add( "ipv4_lpm", "ecmp_group", [str(host_ip)], [ str(new_ecmp_group_id), str(len(dst_macs_ports)) ]) def main(self): self.set_egress_type_table() self.add_mirroring_ids() self.route()
class P4CLI(CLI): def __init__(self, *args, **kwargs): self.conf_file = kwargs.get("conf_file", None) self.import_last_modifications = {} self.last_compilation_state = False if not self.conf_file: log.warn("No configuration given to the CLI. P4 functionalities are disabled.") else: self.config = load_conf(self.conf_file) # class CLI from mininet.cli does not have config parameter, thus remove it kwargs.__delitem__("conf_file") CLI.__init__(self, *args, **kwargs) def failed_status(self): self.last_compilation_state = False return FAILED_STATUS def do_load_topo_conf(self, line= ""): """ Updates the topo config Args: line: Returns: """ args = line.split() if args: conf_file = args[0] self.conf_file = conf_file #re-load conf file self.config = load_conf(self.conf_file) def do_set_p4conf(self, line=""): """Updates configuration file location, and reloads it.""" args = line.split() conf = args[0] if not os.path.exists(conf): warn('Configuratuion file %s does not exist' % conf) return self.conf_file = conf self.config = load_conf(conf) def do_test_p4(self, line=""): """Tests start stop functionalities.""" self.do_p4switch_stop("s1") self.do_p4switch_start("s1") self.do_p4switch_reboot("s1") self.do_p4switches_reboot() def do_p4switch_stop(self, line=""): """Stop simple switch from switch namespace.""" switch_name = line.split() if not switch_name or len(switch_name) > 1: error('usage: p4switch_stop <p4switch name>\n') else: switch_name = switch_name[0] if switch_name not in self.mn: error("p4switch %s not in the network\n" % switch_name) else: p4switch = self.mn[switch_name] p4switch.stop_p4switch() def do_p4switch_start(self, line=""): """Start again simple switch from namespace.""" args = line.split() # check args validity if len(args) > 5: error('usage: p4switch_start <p4switch name> [--p4src <path>] [--cmds path]\n') return self.failed_status() switch_name = args[0] if switch_name not in self.mn: error('usage: p4switch_start <p4switch name> [--p4src <path>] [--cmds path]\n') return self.failed_status() p4switch = self.mn[switch_name] # check if switch is running if p4switch.check_switch_started(): error('P4 Switch already running, stop it first: p4switch_stop %s \n' % switch_name) return self.failed_status() #load default configuration # mandatory defaults if not defined we should complain default_p4 = self.config.get("program", None) default_options = self.config.get("options", None) # non mandatory defaults. default_compiler = self.config.get("compiler", DEFAULT_COMPILER) default_config = {"program": default_p4, "options": default_options, "compiler": default_compiler} #merge with switch conf switch_conf = default_config.copy() switch_conf.update(self.config['topology']['switches'][switch_name]) if "--p4src" in args: p4source_path = args[args.index("--p4src")+1] switch_conf['program'] = p4source_path # check if file exists if not os.path.exists(p4source_path): warn('File Error: p4source %s does not exist\n' % p4source_path) return self.failed_status() #check if its not a file if not os.path.isfile(p4source_path): warn('File Error: p4source %s is not a file\n' % p4source_path) return self.failed_status() p4source_path_source = switch_conf['program'] # generate output file name output_file = p4source_path_source.replace(".p4", "") + ".json" program_flag = last_modified(p4source_path_source, output_file) includes_flag = check_imports_last_modified(p4source_path_source, self.import_last_modifications) log.debug("%s %s %s %s\n" % (p4source_path_source, output_file, program_flag, includes_flag)) if program_flag or includes_flag or (not self.last_compilation_state): # compile program try: compile_p4_to_bmv2(switch_conf) self.last_compilation_state = True except CompilationError: log.error('Compilation failed\n') return self.failed_status() # update output program p4switch.json_path = output_file # start switch p4switch.start() # load command entries if "--cmds" in args: commands_path = args[args.index("--cmds")+1] # check if file exists else: commands_path = switch_conf.get('cli_input', None) if commands_path: if not os.path.exists(commands_path): error('File Error: commands file %s does not exist\n' % commands_path) return self.failed_status() entries = read_entries(commands_path) add_entries(p4switch.thrift_port, entries) return SUCCESS_STATUS def do_printSwitches(self, line=""): """Print names of all switches.""" for sw in self.mn.p4switches: print((sw.name)) def do_p4switches_reboot(self, line=""): """Reboot all P4 switches with new program. Note: If you provide a P4 source code or cmd, all switches will have the same. """ self.config = load_conf(self.conf_file) for sw in self.mn.p4switches: switch_name = sw.name self.do_p4switch_stop(line=switch_name) tmp_line = switch_name + " " +line self.do_p4switch_start(line=tmp_line) #run scripts if isinstance(self.config.get('exec_scripts', None), list): for script in self.config.get('exec_scripts'): if script["reboot_run"]: info("Exec Script: {}\n".format(script["cmd"])) run_command(script["cmd"]) def do_p4switch_reboot(self, line=""): """Reboot a P4 switch with a new program.""" self.config = load_conf(self.conf_file) if not line or len(line.split()) > 5: error('usage: p4switch_reboot <p4switch name> [--p4src <path>] [--cmds path]\n') else: switch_name = line.split()[0] self.do_p4switch_stop(line=switch_name) self.do_p4switch_start(line=line) def do_pingset(self ,line=""): hosts_names = line.strip().split() hosts = [x for x in self.mn.hosts if x.name in hosts_names] self.mn.ping(hosts=hosts, timeout=1) def do_printNetInfo(self, line=""): """Prints Topology Info""" self.topo = Topology(db="topology.db") print("\n*********************") print("Network Information:") print("*********************\n") switches = self.topo.get_switches() for sw in sorted(switches.keys()): # skip linux bridge if sw == "sw-cpu": continue thrift_port = self.topo.get_thrift_port(sw) switch_id = self.topo[sw].get("sw_id", "N/A") cpu_index = self.topo.get_cpu_port_index(sw, quiet=True) header = "{}(thirft->{}, cpu_port->{})".format(sw, thrift_port, cpu_index) header2 = "{:>4} {:>15} {:>8} {:>20} {:>16} {:>8} {:>8} {:>8} {:>8} {:>8}".format("port", "intf", "node", "mac", "ip", "bw", "weight", "delay", "loss","queue") print(header) print((len(header2)*"-")) print(header2) for intf,port_number in sorted(list(self.topo.get_interfaces_to_port(sw).items()), key=lambda x: x[1]): if intf == "lo": continue other_node = self.topo.get_interfaces_to_node(sw)[intf] mac = self.topo[sw][other_node]['mac'] ip = self.topo[sw][other_node]['ip'].split("/")[0] bw = self.topo[sw][other_node]['bw'] weight = self.topo[sw][other_node]['weight'] delay = self.topo[sw][other_node]['delay'] loss = self.topo[sw][other_node]['loss'] queue_length = self.topo[sw][other_node]['queue_length'] print(("{:>4} {:>15} {:>8} {:>20} {:>16} {:>8} {:>8} {:>8} {:>8} {:>8}".format(port_number, intf, other_node, mac, ip, bw, weight, delay, loss, queue_length))) print((len(header2)*"-")) print("") # HOST INFO print("Hosts Info") header = "{:>4} {:>15} {:>8} {:>20} {:>16} {:>8} {:>8} {:>8} {:>8} {:>8}".format( "name", "intf", "node", "mac", "ip", "bw", "weight", "delay", "loss","queue") print((len(header)*"-")) print(header) for host in sorted(self.topo.get_hosts()): for intf,port_number in sorted(list(self.topo.get_interfaces_to_port(host).items()), key=lambda x: x[1]): other_node = self.topo.get_interfaces_to_node(host)[intf] mac = self.topo[host][other_node]['mac'] ip = self.topo[host][other_node]['ip'].split("/")[0] bw = self.topo[host][other_node]['bw'] weight = self.topo[host][other_node]['weight'] delay = self.topo[host][other_node]['delay'] loss = self.topo[host][other_node]['loss'] queue_length = self.topo[host][other_node]['queue_length'] print(("{:>4} {:>15} {:>8} {:>20} {:>16} {:>8} {:>8} {:>8} {:>8} {:>8}".format(host, intf, other_node, mac, ip, bw, weight, delay, loss, queue_length))) print((len(header)*"-")) print("") #def describe(self, sw_addr=None, sw_mac=None): # print "**********" # print "Network configuration for: %s" % self.name # print "Default interface: %s\t%s\t%s" %( # self.defaultIntf().name, # self.defaultIntf().IP(), # self.defaultIntf().MAC() # ) # if sw_addr is not None or sw_mac is not None: # print "Default route to switch: %s (%s)" % (sw_addr, sw_mac) # print "**********" # #def describe(self): # print "%s -> Thrift port: %d" % (self.name, self.thrift_port)
class RoutingController(object): def __init__(self): self.topo = Topology(db="topology.db") self.controllers = {} self.init() def init(self): self.connect_to_switches() self.reset_states() self.set_table_defaults() ''' OPTIONS FOR DEMO ''' self.apply_src_priority = True self.apply_dst_priority = False self.src_high_priority = 'h1' self.src_low_priority = 'h4' self.dst_high_priority = 'h5' self.dst_low_priority = 'h8' def reset_states(self): [controller.reset_state() for controller in self.controllers.values()] def connect_to_switches(self): for p4switch in self.topo.get_p4switches(): thrift_port = self.topo.get_thrift_port(p4switch) self.controllers[p4switch] = SimpleSwitchAPI(thrift_port) def set_table_defaults(self): for controller in self.controllers.values(): controller.table_set_default("ipv4_lpm", "drop", []) controller.table_set_default("ecmp_group_to_nhop", "drop", []) def set_tables(self): # From project 6 # Function outside of route() that sets the egress type table # loops through all switches for sw_name, controller in self.controllers.items(): # gets the interface and node type for interface, node in self.topo.get_interfaces_to_node( sw_name).items(): node_type = self.topo.get_node_type(node) port_number = self.topo.interface_to_port(sw_name, interface) # numerates the node types to be put in the table if node_type == 'host': node_type_num = 1 # NEW - CODE TO SET PRIORITY BASED ON HOST NUMBER host_ip = self.topo.get_host_ip(node) + "/24" priority_num = 2 if str( node ) == self.src_high_priority and self.apply_src_priority: priority_num = 1 elif str( node ) == self.src_low_priority and self.apply_src_priority: priority_num = 3 elif str( node ) == self.dst_high_priority and self.apply_dst_priority: priority_num = 1 elif str( node ) == self.dst_low_priority and self.apply_dst_priority: priority_num = 3 print "Node name: {}, ip address: {}, priority: {}".format( str(node), str(host_ip), str(priority_num)) self.controllers[sw_name].table_add( "priority_type", "set_priority", [str(host_ip)], [str(priority_num)]) if self.apply_dst_priority: self.controllers[sw_name].table_add( "priority_type_dst", "set_priority", [str(host_ip)], [str(priority_num)]) elif node_type == 'switch': node_type_num = 2 # fills the table self.controllers[sw_name].table_add("egress_type", "set_type", [str(port_number)], [str(node_type_num)]) def add_mirroring_ids(self): for sw_name, controller in self.controllers.items(): # adding port 1 (it seems like the first argument is standard) controller.mirroring_add(100, 1) def route(self): switch_ecmp_groups = { sw_name: {} for sw_name in self.topo.get_p4switches().keys() } for sw_name, controller in self.controllers.items(): for sw_dst in self.topo.get_p4switches(): #if its ourselves we create direct connections if sw_name == sw_dst: for host in self.topo.get_hosts_connected_to(sw_name): sw_port = self.topo.node_to_node_port_num( sw_name, host) host_ip = self.topo.get_host_ip(host) + "/32" host_mac = self.topo.get_host_mac(host) #add rule print "table_add at {}:".format(sw_name) self.controllers[sw_name].table_add( "ipv4_lpm", "set_nhop", [str(host_ip)], [str(host_mac), str(sw_port)]) #check if there are directly connected hosts else: if self.topo.get_hosts_connected_to(sw_dst): paths = self.topo.get_shortest_paths_between_nodes( sw_name, sw_dst) for host in self.topo.get_hosts_connected_to(sw_dst): if len(paths) == 1: next_hop = paths[0][1] host_ip = self.topo.get_host_ip(host) + "/24" sw_port = self.topo.node_to_node_port_num( sw_name, next_hop) dst_sw_mac = self.topo.node_to_node_mac( next_hop, sw_name) #add rule print "table_add at {}:".format(sw_name) self.controllers[sw_name].table_add( "ipv4_lpm", "set_nhop", [str(host_ip)], [str(dst_sw_mac), str(sw_port)]) elif len(paths) > 1: next_hops = [x[1] for x in paths] dst_macs_ports = [ (self.topo.node_to_node_mac( next_hop, sw_name), self.topo.node_to_node_port_num( sw_name, next_hop)) for next_hop in next_hops ] host_ip = self.topo.get_host_ip(host) + "/24" #check if the ecmp group already exists. The ecmp group is defined by the number of next #ports used, thus we can use dst_macs_ports as key if switch_ecmp_groups[sw_name].get( tuple(dst_macs_ports), None): ecmp_group_id = switch_ecmp_groups[ sw_name].get(tuple(dst_macs_ports), None) print "table_add at {}:".format(sw_name) self.controllers[sw_name].table_add( "ipv4_lpm", "ecmp_group", [str(host_ip)], [ str(ecmp_group_id), str(len(dst_macs_ports)) ]) #new ecmp group for this switch else: new_ecmp_group_id = len( switch_ecmp_groups[sw_name]) + 1 switch_ecmp_groups[sw_name][tuple( dst_macs_ports)] = new_ecmp_group_id #add group for i, (mac, port) in enumerate(dst_macs_ports): print "table_add at {}:".format( sw_name) self.controllers[sw_name].table_add( "ecmp_group_to_nhop", "set_nhop", [str(new_ecmp_group_id), str(i)], [str(mac), str(port)]) #add forwarding rule print "table_add at {}:".format(sw_name) self.controllers[sw_name].table_add( "ipv4_lpm", "ecmp_group", [str(host_ip)], [ str(new_ecmp_group_id), str(len(dst_macs_ports)) ]) def main(self): self.set_tables() self.add_mirroring_ids() self.route()
class RoutingController(object): def __init__(self): self.topo = Topology(db="topology.db") self.controllers = {} self.init() def init(self): self.connect_to_switches() self.reset_states() self.set_table_defaults() def reset_states(self): [controller.reset_state() for controller in self.controllers.values()] def connect_to_switches(self): for p4switch in self.topo.get_p4switches(): thrift_port = self.topo.get_thrift_port(p4switch) self.controllers[p4switch] = SimpleSwitchAPI(thrift_port) def set_table_defaults(self): for controller in self.controllers.values(): controller.table_set_default("ipv4_lpm", "drop", []) controller.table_set_default("ecmp_group_to_nhop", "drop", []) def set_icmp_ingress_port_table(self): #TODO 2: Fill the icmp_ingress_port table for all the switches in the topology for p4switch, ss_api in self.controllers.items(): intfs = self.topo.get_interfaces_to_node(p4switch) for intf, neigh in intfs.items(): intf_ip = self.topo.node_to_node_interface_ip(p4switch, neigh) intf_ip = intf_ip[:-3] port = self.topo.interface_to_port(p4switch, intf) ss_api.table_add('icmp_ingress_port', 'set_src_icmp_ip', [str(port)] ,[str(intf_ip)]) def get_conn_host_infos(self, p4switch): connected_hosts = self.topo.get_hosts_connected_to(p4switch) if connected_hosts: host = connected_hosts[0] switch_infos = self.topo.node(p4switch) host_mac = self.topo.get_host_mac(host) host_ip = self.topo.get_host_ip(host) + '/32' output_iface = self.topo.interface_to_port(p4switch,switch_infos[host]['intf']) return host_ip, host_mac, output_iface else: return None, None, None def add_ecmp_group(self, p4switch, ss_api, neigh, paths, ecmp_group): host_ip, host_mac, output_iface = self.get_conn_host_infos(neigh) if host_ip: next_hops = [path[1] for path in paths] dst_macs_ports = [(self.topo.node_to_node_mac(next_hop, p4switch), self.topo.node_to_node_port_num(p4switch, next_hop)) for next_hop in next_hops] if ecmp_group.get(p4switch): if ecmp_group[p4switch].get(tuple(dst_macs_ports)): ecmp_group[p4switch][tuple(dst_macs_ports)] = ecmp_group[p4switch][tuple(dst_macs_ports)] + 1 else: ecmp_group[p4switch][tuple(dst_macs_ports)] = 1 else: ecmp_group[p4switch] = {} ecmp_group[p4switch][tuple(dst_macs_ports)] = 1 print('Adding multipath entries') ss_api.table_add('ipv4_lpm', 'ecmp_group', [host_ip] ,[str(1), str(len(next_hops))]) index = 0 for dst_mac_port in dst_macs_ports: ss_api.table_add('ecmp_group_to_nhop', 'set_nhop', [str(1),str(index)] ,[dst_mac_port[0], str(dst_mac_port[1])]) index = index + 1 return None def add_route_via_best(self, p4switch, ss_api, neigh, path): host_ip, host_mac, output_iface = self.get_conn_host_infos(neigh) if host_ip: neigh_mac = self.topo.node_to_node_mac(neigh, p4switch) output_iface = self.topo.node_to_node_port_num(p4switch, neigh) print('Add route via best',host_ip, neigh_mac, output_iface) ss_api.table_add('ipv4_lpm', 'set_nhop', [host_ip] ,[neigh_mac, str(output_iface)]) def add_directly_conn_host(self, p4switch, ss_api): host_ip, host_mac, output_iface = self.get_conn_host_infos(p4switch) if host_ip: print('Add directly connected route ',host_ip,host_mac,output_iface) ss_api.table_add('ipv4_lpm', 'set_nhop', [host_ip] ,[host_mac, str(output_iface)]) def route(self): """implement this function""" ecmp_group = {} for p4switch, ss_api in self.controllers.items(): for neigh in self.topo.get_p4switches(): if p4switch == neigh: # Check if we have connected hosts self.add_directly_conn_host(p4switch, ss_api) else: shortest_path = self.topo.get_shortest_paths_between_nodes(p4switch, neigh) if len(shortest_path) < 2: # There is only 1 path self.add_route_via_best(p4switch, ss_api, neigh, shortest_path) else: # multipath self.add_ecmp_group(p4switch, ss_api, neigh, shortest_path, ecmp_group) def main(self): self.set_icmp_ingress_port_table() self.route()