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 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 reset_states(self): [controller.reset_state() for controller in self.controllers.values()] def set_table_defaults(self): for controller in self.controllers.values(): controller.table_set_default("dmac", "drop", []) def route(self): k = int(sys.argv[1]) for sw_name, controller in self.controllers.items(): # TODO: forwarding rules for all switches def main(self): 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", []) def route(self): def main(self): self.route() if __name__ == "__main__": controller = RoutingController().main()
class ReadCounter(object): def __init__(self): self.topo = Topology(db="topology.db") self.controllers = {} self.init() def init(self): self.connect_to_switches() 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 read_indirect_counters(self): for sw_name, controller in self.controllers.items(): for i in range(10): self.controllers[sw_name].counter_read("port_counter", i) def read_direct_counters(self): for sw_name, controller in self.controllers.items(): num_table_entries = self.controllers[sw_name].table_num_entries('ipv4_lpm') for i in range(int(num_table_entries)): self.controllers[sw_name].counter_read("direct_port_counter", i)
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 route(self): """implement this function""" def main(self): 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", []) def ipv4_lpm(self,sw): for host in self.topo.get_hosts_connected_to("s1"): dstip=self.topo.get_host_ip(host) shortestways=self.topo.get_shortest_paths_between_nodes(sw, host) nhopmac=self.topo.node_to_node_mac(shortestways[0][1],sw) nhopport=self.topo.node_to_node_port_num(sw, shortestways[0][1]) self.controllers[sw].table_add("ipv4_lpm", "set_nhop", [dstip+'/24'],[nhopmac, str(nhopport)]) print("From switch "+str(sw)+" to host "+str(dstip)+", next hop is "+str(shortestways[0][1])+", egress port is "+str(nhopport)+'\n') for sw2 in self.controllers.keys(): if sw==sw2 : continue shortestways=self.topo.get_shortest_paths_between_nodes(sw, sw2) nhopmac=self.topo.node_to_node_mac(shortestways[0][1],sw) nhopport=self.topo.node_to_node_port_num(sw, shortestways[0][1]) dstip="20.0."+str(sw2[1:])+".0/24" self.controllers[sw].table_add("ipv4_lpm", "set_nhop", [dstip],[nhopmac, str(nhopport)]) print("From switch "+str(sw)+" to switch "+str(sw2)+" at "+str(dstip)+", next hop is "+str(shortestways[0][1])+", egress port is "+str(nhopport)+'\n') def op(self,sw): self.controllers[sw].table_add("op","NetChain_insert",[str(3)],[]) self.controllers[sw].table_add("op","NetChain_delete",[str(4)],[]) def pkt_for_me(self,sw): self.controllers[sw].table_add("pkt_for_me", "NoAction", ["20.0."+str(sw[1:])+".0/24",'35678'],[]) def seq(self,sw): self.controllers[sw].table_add("seq","assignseq",[str(0)],[]) def read_write_trans(self,sw): self.controllers[sw].table_add("read_write_trans","NetChain_write",["20.0."+str(sw[1:])+".1",str(1)],[]) self.controllers[sw].table_add("read_write_trans","NetChain_read",["20.0."+str(sw[1:])+".1",str(2)],[]) self.controllers[sw].table_add("read_write_trans","NetChain_transfer",["20.0."+str(sw[1:])+".1",str(6)],[])
class myController(object): def __init__(self): self.topo = Topology(db="topology.db") self.controllers = {} self.connect_to_switches() def connect_to_switches(self): for p4switch in self.topo.get_p4switches(): thrift_port = self.topo.get_thrift_port(p4switch) #print "p4switch:", p4switch, "thrift_port:", thrift_port self.controllers[p4switch] = SimpleSwitchAPI(thrift_port) def recv_msg_cpu(self, pkt): print "interface:", pkt.sniffed_on print "summary:", pkt.summary() global val1,val2 if TCP in pkt and pkt[TCP].flags==2: src = pkt.sprintf('{IP:%IP.src%}') dst = pkt.sprintf('{IP:%IP.dst%}') count1[(src, dst)] += 1 val1=count1[(src, dst)] print "count1[",src,",",dst,"]=",count1[(src, dst)] if TCP in pkt and pkt[TCP].flags==18: src = pkt.sprintf('{IP:%IP.src%}') dst = pkt.sprintf('{IP:%IP.dst%}') count2[(dst, src)] += 1 val2=count2[(dst, src)] print "count2[",dst,",",src,"]=",count2[(dst, src)] print "val1:", val1, " val2:", val2 if (val1-val2>=3) and (TCP in pkt) and pkt[TCP].flags==2: src = pkt.sprintf('{IP:%IP.src%}') if src not in blockip: self.controllers["s1"].table_add("block_pkt", "_drop", [str(src)], []) blockip.append(src) def run_cpu_port_loop(self): cpu_interfaces = [str(self.topo.get_cpu_port_intf(sw_name).replace("eth0", "eth1")) for sw_name in self.controllers] sniff(iface=cpu_interfaces, prn=self.recv_msg_cpu)
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() # Establishes a connection with the simple switch `thrift` server # using the `SimpleSwitchAPI` object # and saves those objects in the `self.controllers` dictionary. # This dictionary has the form of: `{'sw_name' : SimpleSwitchAPI()}`. 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) # Iterates over the `self.controllers` object # and runs the `reset_state` function which empties the state # (registers, tables, etc) for every switch. def reset_states(self): [controller.reset_state() for controller in self.controllers.values()] # For each P4 switch, it sets the default action for `dmac` table def set_table_defaults(self): for controller in self.controllers.values(): controller.table_set_default("dmac", "drop", []) # Create forwarding rules for the dmac table def route(self): #TODO: install routing rules for circle topology print('ERROR: routes not installed\n') def main(self): 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 pass def route(self): #copy from the previous exercise pass def main(self): self.set_icmp_ingress_port_table() 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() # Establishes a connection with the simple switch `thrift` server # using the `SimpleSwitchAPI` object # and saves those objects in the `self.controllers` dictionary. # This dictionary has the form of: `{'sw_name' : SimpleSwitchAPI()}`. 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) # Iterates over the `self.controllers` object # and runs the `reset_state` function which empties the state # (registers, tables, etc) for every switch. def reset_states(self): [controller.reset_state() for controller in self.controllers.values()] # For each P4 switch, it sets the default action for `dmac` table def set_table_defaults(self): for controller in self.controllers.values(): controller.table_set_default("dmac", "drop", []) # Create forwarding rules for the dmac table def route(self): for sw_name, controller in self.controllers.items(): if sw_name == "s1": # table_add function: create a rule in "dmac" table, the key is the dmac address # the action is "forward", and the parameter is the output port id # E.g., when receiving a packet to "00:00:0a:00:00:01", "forward" the packet to "1" port. controller.table_add("dmac", "forward", ["00:00:0a:00:00:01"], ["1"]) controller.table_add("dmac", "forward", ["00:00:0a:00:00:02"], ["2"]) controller.table_add("dmac", "forward", ["00:00:0a:00:00:03"], ["2"]) elif sw_name == "s2": controller.table_add("dmac", "forward", ["00:00:0a:00:00:01"], ["2"]) controller.table_add("dmac", "forward", ["00:00:0a:00:00:02"], ["1"]) controller.table_add("dmac", "forward", ["00:00:0a:00:00:03"], ["3"]) elif sw_name == "s3": controller.table_add("dmac", "forward", ["00:00:0a:00:00:01"], ["2"]) controller.table_add("dmac", "forward", ["00:00:0a:00:00:02"], ["2"]) controller.table_add("dmac", "forward", ["00:00:0a:00:00:03"], ["1"]) def main(self): self.route()
topo_db = args.topo_db routing_file = args.routing_file threshold = args.threshold # Logger for the controller logger.setup_logger('controller', log_dir + '/controller.log', level=log_level) log = logging.getLogger('controller') log.info(str(port)+'\t'+str(log_dir)+'\t'+str(log_level)+'\t'+str(routing_file)+ \ '\t'+str(threshold)) # Read the topology topo = Topology(db=topo_db) mapping_dic = {} tmp = list(topo.get_hosts()) + list(topo.get_p4switches()) mapping_dic = {k: v for v, k in enumerate(tmp)} log.info(str(mapping_dic)) """ This function adds an entry in a match+action table of the switch """ def add_entry_fwtable(connection, fwtable_name, action_name, match_list, args_list): args_str = '' for a in args_list: args_str += str(a) + ' ' args_str = args_str[:-1] match_str = ''
help='File (json) with the routing') args = parser.parse_args() topo_db = args.topo_db ip_controller = args.controller_ip port_controller = args.controller_port log_dir = args.log_dir routing_file = args.routing_file routing_file_param = '' if routing_file is None else '--routing_file ' + routing_file # Read the topology topo = Topology(db=topo_db) pid_list = [] for s in topo.get_p4switches(): print "sudo python -m controller.p4_controller --topo_db " + str( topo_db) + " \ --sw_name " + str(s) + " --controller_ip " + str( ip_controller) + " --controller_port \ " + str(port_controller) + " --log_dir " + str( log_dir) + " " + routing_file_param pid_list.append(Popen("sudo python -m controller.p4_controller --topo_db \ " +str(topo_db)+" --sw_name "+str(s)+" --controller_ip "+str(ip_controller)+" \ --controller_port " +str(port_controller)+" --log_dir "+str(log_dir)+" "+ \ routing_file_param, shell=True)) # call subprocess for pid in pid_list: pid.wait()
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, subnets=0): self.topo = Topology(db="topology.db") self.controllers = {} destination_subnets = self.generate_ips("10.0.1.0/24", "10.250.250.0/24") h2 = destination_subnets[0] h3 = destination_subnets[-1] #remove them destination_subnets = destination_subnets[1:-1] #filter entries destination_subnets = destination_subnets[:subnets] # put them back destination_subnets = [h2] + destination_subnets + [h3] self.destination_subnets= destination_subnets def init(self): print "Start Controller" 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", []) def generate_ips(self, start, finish): dest_subets = [] prefix_pool = generate_prefix_pool(start) ip = next(prefix_pool) dest_subets.append(ip) while ip != finish: ip = next(prefix_pool) dest_subets.append(ip) return dest_subets def initialize_tables(self): #set static rules self.controllers["s1"].table_add("ipv4_lpm", "ipv4_forward", ["10.0.1.2/32"], ["00:00:0a:00:01:02", "1"]) self.controllers["s2"].table_add("ipv4_lpm", "ipv4_forward", ["10.0.1.0/24"], ["00:00:0a:00:01:02", "1"]) self.controllers["s2"].table_add("ipv4_lpm", "ipv4_forward", ["10.0.0.0/8"], ["00:00:0a:00:01:02", "2"]) self.controllers["s3"].table_add("ipv4_lpm", "ipv4_forward", ["10.0.1.0/24"], ["00:00:0a:00:01:02", "1"]) self.controllers["s3"].table_add("ipv4_lpm", "ipv4_forward", ["10.0.0.0/8"], ["00:00:0a:00:01:02", "2"]) self.controllers["s4"].table_add("ipv4_lpm", "ipv4_forward", ["10.0.1.0/24"], ["00:00:0a:00:01:02", "2"]) self.controllers["s4"].table_add("ipv4_lpm", "ipv4_forward", ["10.0.2.2/32"], ["00:00:0a:00:02:02", "3"]) self.controllers["s4"].table_add("ipv4_lpm", "ipv4_forward", ["10.250.250.2/32"], ["00:00:0a:fa:fa:02", "4"]) #dynamic entries for s1 for entry in self.destination_subnets: self.controllers["s1"].table_add("ipv4_lpm", "ipv4_forward", [entry], ["00:00:00:02:01:00", "2"]) def fail_link(self): subprocess.call("sudo ifconfig s1-eth2 down", shell=True) def recover_from_failure(self, out): for entry in self.destination_subnets: self.controllers["s1"].table_modify_match("ipv4_lpm", "ipv4_forward", [entry], ["00:00:00:03:01:00", str(out)]) def fail_and_reroute(self): self.fail_link() time.sleep(2) self.recover_from_failure(3) def recover_link_reroute(self): subprocess.call("sudo ifconfig s1-eth2 up", shell=True) time.sleep(1) self.recover_from_failure(2) def main(self): pass
class PacketLossController(object): def __init__(self, num_hashes=3): self.topo = Topology(db="topology.db") self.controllers = {} self.num_hashes = num_hashes # gets a controller API for each switch: {"s1": controller, "s2": controller...} self.connect_to_switches() # creates the 3 hashes that will use the p4 switch self.create_local_hashes() # initializes the switch # resets all registers, configures the 3 x 2 hash functions # reads the registers # populates the tables and mirroring id self.init() self.registers = {} def init(self): self.reset_all_registers() self.set_crc_custom_hashes_all() self.read_registers() self.configure_switches() 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 configure_switches(self): for sw, controller in self.controllers.items(): # ads cpu port controller.mirroring_add(100, 3) # set the basic forwarding rules controller.table_add("forwarding", "set_egress_port", ["1"], ["2"]) controller.table_add("forwarding", "set_egress_port", ["2"], ["1"]) # set the remove header rules when there is a host in a port direct_hosts = self.topo.get_hosts_connected_to(sw) for host in direct_hosts: port = self.topo.node_to_node_port_num(sw,host) controller.table_add("remove_loss_header", "remove_header", [str(port)], []) def set_crc_custom_hashes_all(self): for sw_name in self.controllers: self.set_crc_custom_hashes(sw_name) def set_crc_custom_hashes(self, sw_name): custom_calcs = sorted(self.controllers[sw_name].get_custom_crc_calcs().items()) i = 0 # Set the first 3 hashes for the um for custom_crc32, width in custom_calcs[:self.num_hashes]: self.controllers[sw_name].set_crc32_parameters(custom_crc32, crc32_polinomials[i], 0xffffffff, 0xffffffff, True, True) i += 1 i = 0 # Sets the 3 hashes for the dm, they have to be the same, thus we use the same index for custom_crc32, width in custom_calcs[self.num_hashes:]: self.controllers[sw_name].set_crc32_parameters(custom_crc32, crc32_polinomials[i], 0xffffffff, 0xffffffff, True, True) i += 1 def create_local_hashes(self): self.hashes = [] for i in range(self.num_hashes): self.hashes.append(Crc(32, crc32_polinomials[i], True, 0xffffffff, True, 0xffffffff)) def reset_all_registers(self): for sw, controller in self.controllers.items(): for register in controller.get_register_arrays(): controller.register_reset(register) def reset_registers(self, sw, stream, port, batch_id): start = (batch_id * REGISTER_BATCH_SIZE) + ((port-1) * REGISTER_PORT_SIZE) end = start + REGISTER_PORT_SIZE for register in self.controllers[sw].get_register_arrays(): if stream in register: self.controllers[sw].register_write(register, [start, end], 0) def flow_to_bytestream(self, flow): # flow fields are: srcip , dstip, srcport, dstport, protocol, ip id return socket.inet_aton(flow[0]) + socket.inet_aton(flow[1]) + struct.pack(">HHBH",flow[2], flow[3], flow[4], flow[5]) def read_registers(self): # reads all the registers self.registers = {sw: {} for sw in self.controllers.keys()} for sw, controller in self.controllers.items(): for register in controller.get_register_arrays(): self.registers[sw][register] = (controller.register_read(register)) def extract_register_information(self, sw, stream, port, batch_id): # reads the region of a um or dm register: uses port, batch id. start = (batch_id * REGISTER_BATCH_SIZE) + ((port-1) * REGISTER_PORT_SIZE) end = start + REGISTER_PORT_SIZE res = {} for name, values in self.registers[sw].items(): if stream in name: res[name] = values[start:end] return res def decode_meter_pair(self, um_registers, dm_registers): # xor the registers counters = [x - y for x, y in zip(um_registers['MyIngress.um_counter'], dm_registers['MyIngress.dm_counter'])] ip_src = [x ^ y for x, y in zip(um_registers['MyIngress.um_ip_src'], dm_registers['MyIngress.dm_ip_src'])] ip_dst = [x ^ y for x, y in zip(um_registers['MyIngress.um_ip_dst'], dm_registers['MyIngress.dm_ip_dst'])] ports_proto_id = [x ^ y for x, y in zip(um_registers['MyIngress.um_ports_proto_id'], dm_registers['MyIngress.dm_ports_proto_id'])] dropped_packets = set() while 1 in counters: i = counters.index(1) tmp_src = ip_src[i] tmp_dst = ip_dst[i] src = socket.inet_ntoa(struct.pack("!I", tmp_src)) dst = socket.inet_ntoa(struct.pack("!I", tmp_dst)) misc = ports_proto_id[i] id = misc & 0xffff proto = misc >> 16 & 0xff dst_port = misc >> 24 & 0xffff src_port = misc >> 40 & 0xffff flow = (src, dst, src_port, dst_port, proto, id) # get the three indexes flow_stream = self.flow_to_bytestream(flow) index0 = self.hashes[0].bit_by_bit_fast(flow_stream) % REGISTER_PORT_SIZE index1 = self.hashes[1].bit_by_bit_fast(flow_stream) % REGISTER_PORT_SIZE index2 = self.hashes[2].bit_by_bit_fast(flow_stream) % REGISTER_PORT_SIZE # clean this entries everywhere an continue counters[index0] -= 1 counters[index1] -= 1 counters[index2] -= 1 ip_src[index0] ^= tmp_src ip_src[index1] ^= tmp_src ip_src[index2] ^= tmp_src ip_dst[index0] ^= tmp_dst ip_dst[index1] ^= tmp_dst ip_dst[index2] ^= tmp_dst ports_proto_id[index0] ^= misc ports_proto_id[index1] ^= misc ports_proto_id[index2] ^= misc # if there is a bad sync we skip this round # do not ask this in the readme # mainly the problem is the amount of buffer the switch allows if any(x < 0 for x in counters): return dropped_packets dropped_packets.add(flow) return dropped_packets def verify_link(self, sw1, sw2, batch_id): sw1_to_sw2_interface = self.topo.node_to_node_port_num(sw1, sw2) sw2_to_sw1_interface = self.topo.node_to_node_port_num(sw2, sw1) sw1_um = self.extract_register_information(sw1, 'um', sw1_to_sw2_interface, batch_id) sw2_dm = self.extract_register_information(sw2, 'dm', sw2_to_sw1_interface, batch_id) dropped_packets = self.decode_meter_pair(sw1_um, sw2_dm) # clean registers self.reset_registers(sw1, 'um', sw1_to_sw2_interface, batch_id) self.reset_registers(sw2, 'dm', sw2_to_sw1_interface, batch_id) # report if dropped_packets: print "Packets dropped: {} at link {}->{}:".format(len(dropped_packets), sw1, sw2) print "Details:" for packet in dropped_packets: print packet def check_sw_links(self, sw, batch_id): # just in case for the delay # increase decrease depending on the batch timeing time.sleep(0.25) # read all registers since its a small topo self.read_registers() # Process the right links and clean registers neighboring_p4_switches = [x for x in self.topo.get_neighbors(sw) if x in self.topo.get_p4switches()] for neighboring_switch in neighboring_p4_switches: self.verify_link(sw, neighboring_switch, batch_id) # When a batch_id changes the controller gets triggered def recv_msg_cpu(self, pkt): interface = pkt.sniffed_on print interface switch_name = interface.split("-")[0] packet = Ether(str(pkt)) if packet.type == 0x1234: loss_header = LossHeader(packet.payload) batch_id = loss_header.batch_id >> 7 print switch_name, batch_id self.check_sw_links(switch_name, batch_id) def run_cpu_port_loop(self): cpu_interfaces = [str(self.topo.get_cpu_port_intf(sw_name).replace("eth0", "eth1")) for sw_name in self.controllers] sniff(iface=cpu_interfaces, prn=self.recv_msg_cpu)
actionType = sys.argv[1] if (actionType != "u"): # 1) Make sure we have a register name if (not existsRegisterName()): writeRegisterName() # Set the switch values if (actionType == "a"): topo = Topology(db="./topology.db") # Get all the switches switches = topo.get_p4switches().keys() else: switches = [actionType] # Just the inputted switch # Read the bloom filter and output the register for each of these switches signal.signal(signal.SIGINT, handler) # We write data into files for each link for switch in switches: # Get the register value handler = ReadCounters(switch) res = handler.get_qlen() print("========================") print("SWITCH NAME: " + switch)
class RerouteController(object): """Controller for the fast rerouting exercise.""" def __init__(self): """Initializes the topology and data structures.""" if not os.path.exists("topology.db"): print "Could not find topology object!\n" raise Exception self.topo = Topology(db="topology.db") self.controllers = {} self.connect_to_switches() self.reset_states() # Preconfigure all MAC addresses self.install_macs() # Install nexthop indices and populate registers. self.install_nexthop_indices() self.update_nexthops() def connect_to_switches(self): """Connects to all the switches in the topology.""" for p4switch in self.topo.get_p4switches(): thrift_port = self.topo.get_thrift_port(p4switch) self.controllers[p4switch] = SimpleSwitchAPI(thrift_port) def reset_states(self): """Resets registers, tables, etc.""" for control in self.controllers.values(): control.reset_state() def install_macs(self): """Install the port-to-mac map on all switches. You do not need to change this. Note: Real switches would rely on L2 learning to achieve this. """ for switch, control in self.controllers.items(): print "Installing MAC addresses for switch '%s'." % switch print "=========================================\n" for neighbor in self.topo.get_neighbors(switch): mac = self.topo.node_to_node_mac(neighbor, switch) port = self.topo.node_to_node_port_num(switch, neighbor) control.table_add('rewrite_mac', 'rewriteMac', [str(port)], [str(mac)]) def install_nexthop_indices(self): """Install the mapping from prefix to nexthop ids for all switches.""" for switch, control in self.controllers.items(): print "Installing nexthop indices for switch '%s'." % switch print "===========================================\n" control.table_clear('ipv4_lpm') for host in self.topo.get_hosts(): subnet = self.get_host_net(host) index = self.get_nexthop_index(host) control.table_add('ipv4_lpm', 'read_port', [subnet], [str(index)]) def get_host_net(self, host): """Return ip and subnet of a host. Args: host (str): The host for which the net will be retruned. Returns: str: IP and subnet in the format "address/mask". """ gateway = self.topo.get_host_gateway_name(host) return self.topo[host][gateway]['ip'] def get_nexthop_index(self, host): """Return the nexthop index for a destination. Args: host (str): Name of destination node (host). Returns: int: nexthop index, used to look up nexthop ports. """ # For now, give each host an individual nexthop id. host_list = sorted(list(self.topo.get_hosts().keys())) return host_list.index(host) def get_port(self, node, nexthop_node): """Return egress port for nexthop from the view of node. Args: node (str): Name of node for which the port is determined. nexthop_node (str): Name of node to reach. Returns: int: nexthop port """ return self.topo.node_to_node_port_num(node, nexthop_node) def failure_notification(self, failures): """Called if a link fails. Args: failures (list(tuple(str, str))): List of failed links. """ self.update_nexthops(failures=failures) # Helpers to update nexthops. # =========================== def dijkstra(self, failures=None): """Compute shortest paths and distances. Args: failures (list(tuple(str, str))): List of failed links. Returns: tuple(dict, dict): First dict: distances, second: paths. """ graph = self.topo.network_graph if failures is not None: graph = graph.copy() for failure in failures: graph.remove_edge(*failure) # Compute the shortest paths from switches to hosts. dijkstra = dict(all_pairs_dijkstra(graph, weight='weight')) distances = {node: data[0] for node, data in dijkstra.items()} paths = {node: data[1] for node, data in dijkstra.items()} return distances, paths def compute_nexthops(self, failures=None): """Compute the best nexthops for all switches to each host. Optionally, a link can be marked as failed. This link will be excluded when computing the shortest paths. Args: failures (list(tuple(str, str))): List of failed links. Returns: dict(str, list(str, str, int))): Mapping from all switches to subnets, MAC, port. """ # Compute the shortest paths from switches to hosts. all_shortest_paths = self.dijkstra(failures=failures)[1] # Translate shortest paths to mapping from host to nexthop node # (per switch). results = {} for switch in self.controllers: switch_results = results[switch] = [] for host in self.topo.network_graph.get_hosts(): try: path = all_shortest_paths[switch][host] except KeyError: print "WARNING: The graph is not connected!" print "'%s' cannot reach '%s'." % (switch, host) continue nexthop = path[1] # path[0] is the switch itself. switch_results.append((host, nexthop)) return results # Update nexthops. # ================ def update_nexthops(self, failures=None): """Install nexthops in all switches.""" nexthops = self.compute_nexthops(failures=failures) for switch, destinations in nexthops.items(): print "Updating nexthops for switch '%s'." % switch control = self.controllers[switch] for host, nexthop in destinations: nexthop_id = self.get_nexthop_index(host) port = self.get_port(switch, nexthop) # Write the port in the nexthop lookup register. control.register_write('primaryNH', nexthop_id, port) ####################################################################### # Compute loop-free alternate nexthops and install them below. ####################################################################### pass
class Controller(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", []) def install_rules(self): 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): 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)]) def main(self): self.install_rules()
class RoutingController(object): def __init__(self): self.topo = Topology(db="./topology.db") #set the topology self.controllers = {} #the switches self.custom_calcs={} self.register_num={} self.registers={} self.init() def init(self): self.connect_to_switches() self.reset_states() self.set_table_defaults() self.set_custom_calcs() self.reset_all_registers() self.set_crc_custom_hashes() def connect_to_switches(self): for p4switch in self.topo.get_p4switches():# topology line 632 thrift_port = self.topo.get_thrift_port(p4switch) self.controllers[p4switch] = SimpleSwitchAPI(thrift_port) def reset_states(self): [controllers.reset_state() for controllers in self.controllers.values()] def set_table_defaults(self): for controllers in self.controllers.values(): controllers.table_set_default("ipv4_lpm", "drop", []) controllers.table_set_default("ecmp_group_to_nhop", "drop", []) def set_custom_calcs(self): for p4switch in self.topo.get_p4switches(): self.custom_calcs[p4switch]=self.controllers[p4switch].get_custom_crc_calcs() self.register_num[p4switch] =len(self.custom_calcs[p4switch]) def reset_all_registers(self): for sw, controller in self.controllers.items(): for register in controller.get_register_arrays(): controller.register_reset(register) def set_crc_custom_hashes(self): for sw_name in self.controllers.keys(): i = 0 for custom_crc32, width in sorted(self.custom_calcs[sw_name].items()): self.controllers[sw_name].set_crc32_parameters(custom_crc32, crc32_polinomials[i], 0xffffffff, 0xffffffff, True, True) i+=1 def route(self): switch_ecmp_groups = {sw_name:{} for sw_name in self.topo.get_p4switches().keys()} # self.topo.network_graph.remove_node("sw-cpu") for sw_name, controllers 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.route() # for switch_id, controller in enumerate(self.controllers.values()): # controller.register_write("switch_id", 0, switch_id) # controller.register_write("swap_control", 0, 0) # controller.register_write("sketch_fg", 0, 0) # controller.register_write("previous_ingress_timestamp", 0, 0) for switch_id, switch_name in enumerate(self.controllers.keys()): print "{} {}".format(switch_id, switch_name)
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 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) #print(self.topo.node(p4switch)['interfaces_to_node']) #for iface, neigh in self.topo.node(p4switch)['interfaces_to_node'].items(): # print(self.topo.node_to_node_port_num(p4switch, neigh)) def main(self): self.route()
class BlinkController: def __init__(self, topo_db, sw_name, ip_controller, port_controller, log_dir, \ monitoring=True, routing_file=None): self.topo = Topology(db=topo_db) self.sw_name = sw_name self.thrift_port = self.topo.get_thrift_port(sw_name) self.cpu_port = self.topo.get_cpu_port_index(self.sw_name) self.controller = SimpleSwitchAPI(self.thrift_port) self.controller.reset_state() self.log_dir = log_dir print 'connecting to ', ip_controller, port_controller # Socket used to communicate with the controller self.sock_controller = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_address = (ip_controller, port_controller) self.sock_controller.connect(server_address) print 'Connected!' # Send the switch name to the controller self.sock_controller.sendall(str(sw_name)) self.make_logging() if monitoring: # Monitoring scheduler self.t_sched = sched_timer.RepeatingTimer(10, 0.5, self.scheduling) self.t_sched.start() self.mapping_dic = {} tmp = list(self.topo.get_hosts()) + list(self.topo.get_p4switches()) self.mapping_dic = {k: v for v, k in enumerate(tmp)} self.log.info(str(self.mapping_dic)) self.routing_file = routing_file print 'routing_file ', routing_file if self.routing_file is not None: json_data = open(self.routing_file) self.topo_routing = json.load(json_data) def make_logging(self): # Logger for the pipeline logger.setup_logger('p4_to_controller', self.log_dir+'/p4_to_controller_'+ \ str(self.sw_name)+'.log', level=logging.INFO) self.log = logging.getLogger('p4_to_controller') # Logger for the sliding window logger.setup_logger('p4_to_controller_sw', self.log_dir+'/p4_to_controller_'+ \ str(self.sw_name)+'_sw.log', level=logging.INFO) self.log_sw = logging.getLogger('p4_to_controller_sw') # Logger for the rerouting logger.setup_logger('p4_to_controller_rerouting', self.log_dir+'/p4_to_controller_'+ \ str(self.sw_name)+'_rerouting.log', level=logging.INFO) self.log_rerouting = logging.getLogger('p4_to_controller_rerouting') # Logger for the Flow Selector logger.setup_logger('p4_to_controller_fs', self.log_dir+'/p4_to_controller_'+ \ str(self.sw_name)+'_fs.log', level=logging.INFO) self.log_fs = logging.getLogger('p4_to_controller_fs') def scheduling(self): for host in list(self.topo.get_hosts()): prefix = self.topo.get_host_ip(host) + '/24' # Print log about the sliding window for id_prefix in [ self.mapping_dic[host] * 2, self.mapping_dic[host] * 2 + 1 ]: with HiddenPrints(): sw_time = float( self.controller.register_read('sw_time', index=id_prefix)) / 1000. sw_index = self.controller.register_read('sw_index', index=id_prefix) sw_sum = self.controller.register_read('sw_sum', index=id_prefix) self.log_sw.info('sw_time\t' + host + '\t' + prefix + '\t' + str(id_prefix) + '\t' + str(sw_time)) self.log_sw.info('sw_index\t' + host + '\t' + prefix + '\t' + str(id_prefix) + '\t' + str(sw_index)) if sw_sum >= 32: self.log_sw.info('sw_sum\t' + host + '\t' + prefix + '\t' + str(id_prefix) + '\t' + str(sw_sum) + '\tREROUTING') else: self.log_sw.info('sw_sum\t' + host + '\t' + prefix + '\t' + str(id_prefix) + '\t' + str(sw_sum)) sw = [] tmp = 'sw ' + host + ' ' + prefix + ' ' + str(id_prefix) + '\t' for i in range(0, 10): with HiddenPrints(): binvalue = int( self.controller.register_read( 'sw', (id_prefix * 10) + i)) tmp = tmp + str(binvalue) + ',' sw.append(binvalue) tmp = tmp[:-1] self.log_sw.info(str(tmp)) # Print log about rerouting for host in list(self.topo.get_hosts()): prefix = self.topo.get_host_ip(host) + '/24' for id_prefix in [ self.mapping_dic[host] * 2, self.mapping_dic[host] * 2 + 1 ]: with HiddenPrints(): nh_avaibility_1 = self.controller.register_read( 'nh_avaibility_1', index=id_prefix) nh_avaibility_2 = self.controller.register_read( 'nh_avaibility_2', index=id_prefix) nh_avaibility_3 = self.controller.register_read( 'nh_avaibility_3', index=id_prefix) nbflows_progressing_2 = self.controller.register_read( 'nbflows_progressing_2', index=id_prefix) nbflows_progressing_3 = self.controller.register_read( 'nbflows_progressing_3', index=id_prefix) rerouting_ts = self.controller.register_read( 'rerouting_ts', index=id_prefix) threshold = self.controller.register_read( 'threshold_registers', index=id_prefix) self.log_rerouting.info('nh_avaibility\t'+host+'\t'+prefix+'\t'+ \ str(id_prefix)+'\t'+str(nh_avaibility_1)+'\t'+ \ str(nh_avaibility_2)+'\t'+str(nh_avaibility_3)) self.log_rerouting.info('nblows_progressing\t'+host+'\t'+prefix+'\t'+ \ str(id_prefix)+'\t'+str(nbflows_progressing_2)+'\t'+ \ str(nbflows_progressing_3)) self.log_rerouting.info('rerouting_ts\t'+host+'\t'+prefix+'\t'+ \ str(id_prefix)+'\t'+str(rerouting_ts)) self.log_rerouting.info('threshold\t'+host+'\t'+prefix+'\t'+ \ str(id_prefix)+'\t'+str(threshold)) nexthop_str = '' nha = [nh_avaibility_1, nh_avaibility_2, nh_avaibility_3] i = 0 if self.routing_file is not None: bgp_type = 'customer' if id_prefix % 2 == 0 else 'customer_provider_peer' if bgp_type not in self.topo_routing['switches'][ self.sw_name]['prefixes'][host]: nexthop_str = 'NoPathAvailable' else: if len(self.topo_routing['switches'][self.sw_name] ['prefixes'][host][bgp_type]) == 2: self.topo_routing['switches'][self.sw_name][ 'prefixes'][host][bgp_type].append( self.topo_routing['switches'][self.sw_name] ['prefixes'][host][bgp_type][-1]) for nexthop in self.topo_routing['switches'][ self.sw_name]['prefixes'][host][bgp_type]: tmp = 'y' if nha[i] == 0 else 'n' nexthop_str = nexthop_str + str( nexthop) + '(' + tmp + ')\t' i += 1 nexthop_str = nexthop_str[:-1] self.log_rerouting.info('nexthop\t'+host+'\t'+prefix+'\t'+ \ str(id_prefix)+'\t'+str(nexthop_str)) # Print log about the flow selector for host in list(self.topo.get_hosts()): prefix = self.topo.get_host_ip(host) + '/24' for id_prefix in [ self.mapping_dic[host] * 2, self.mapping_dic[host] * 2 + 1 ]: sw = [] tmp = 'fs_key ' + host + ' ' + prefix + ' ' + str( id_prefix) + '\t' for i in range(0, 64): with HiddenPrints(): binvalue = int( self.controller.register_read( 'flowselector_key', 64 * id_prefix + i)) tmp = tmp + str(binvalue) + ',' sw.append(binvalue) tmp = tmp[:-1] self.log_fs.info(str(tmp)) sw = [] tmp = 'fs ' + host + ' ' + prefix + ' ' + str(id_prefix) + '\t' for i in range(0, 64): with HiddenPrints(): binvalue = int( self.controller.register_read( 'flowselector_ts', 64 * id_prefix + i)) tmp = tmp + str(binvalue) + ',' sw.append(binvalue) tmp = tmp[:-1] self.log_fs.info(str(tmp)) sw = [] tmp = 'fs_last_ret ' + host + ' ' + prefix + ' ' + str( id_prefix) + '\t' for i in range(0, 64): with HiddenPrints(): binvalue = int( self.controller.register_read( 'flowselector_last_ret', 64 * id_prefix + i)) tmp = tmp + str(binvalue) + ',' sw.append(binvalue) tmp = tmp[:-1] self.log_fs.info(str(tmp)) sw = [] tmp = 'fs_last_ret_bin ' + host + ' ' + prefix + ' ' + str( id_prefix) + '\t' for i in range(0, 64): with HiddenPrints(): binvalue = int( self.controller.register_read( 'flowselector_last_ret_bin', 64 * id_prefix + i)) tmp = tmp + str(binvalue) + ',' sw.append(binvalue) tmp = tmp[:-1] self.log_fs.info(str(tmp)) sw = [] tmp = 'fs_fwloops ' + host + ' ' + prefix + ' ' + str( id_prefix) + '\t' for i in range(0, 64): with HiddenPrints(): binvalue = int( self.controller.register_read( 'flowselector_fwloops', 64 * id_prefix + i)) tmp = tmp + str(binvalue) + ',' sw.append(binvalue) tmp = tmp[:-1] self.log_fs.info(str(tmp)) sw = [] tmp = 'fs_correctness ' + host + ' ' + prefix + ' ' + str( id_prefix) + '\t' for i in range(0, 64): with HiddenPrints(): binvalue = int( self.controller.register_read( 'flowselector_correctness', 64 * id_prefix + i)) tmp = tmp + str(binvalue) + ',' sw.append(binvalue) tmp = tmp[:-1] self.log_fs.info(str(tmp)) def forwarding(self): p4switches = self.topo.get_p4switches() interfaces_to_node = p4switches[self.sw_name]['interfaces_to_node'] for k, v in interfaces_to_node.items(): try: dst_mac = self.topo.get_hosts()[v][self.sw_name]['mac'] except KeyError: dst_mac = self.topo.get_p4switches()[v][self.sw_name]['mac'] src_mac = p4switches[self.sw_name][v]['mac'] outport = p4switches[self.sw_name]['interfaces_to_port'][ p4switches[self.sw_name][v]['intf']] self.log.info('table add send set_nh ' + str(self.mapping_dic[v]) + ' => ' + str(outport) + ' ' + str(src_mac) + ' ' + str(dst_mac)) self.controller.table_add( 'send', 'set_nh', [str(self.mapping_dic[v])], [str(outport), str(src_mac), str(dst_mac)]) def run(self): sock_list = [self.sock_controller] controller_data = '' while True: inready, outready, excepready = select.select(sock_list, [], []) for sock in inready: if sock == self.sock_controller: data_tmp = '' toreturn = None try: data_tmp = sock.recv(100000000) except socket.error, e: err = e.args[0] if not (err == errno.EAGAIN or err == errno.EWOULDBLOCK): print 'p4_to_controller: ', e sock.close() sock = None if len(data_tmp) > 0: controller_data += data_tmp next_data = '' while len(controller_data ) > 0 and controller_data[-1] != '\n': next_data = controller_data[-1] + next_data controller_data = controller_data[:-1] toreturn = controller_data controller_data = next_data if toreturn is not None: for line in toreturn.split('\n'): if line.startswith('table add '): line = line.rstrip('\n').replace( 'table add ', '') fwtable_name = line.split(' ')[0] action_name = line.split(' ')[1] match_list = line.split(' => ')[0].split( ' ')[2:] action_list = line.split(' => ')[1].split(' ') print line print fwtable_name, action_name, match_list, action_list self.log.info(line) self.controller.table_add(fwtable_name, action_name, \ match_list, action_list) if line.startswith('do_register_write'): line = line.rstrip('\n') linetab = line.split(' ') register_name = linetab[1] index = int(linetab[2]) value = int(linetab[3]) self.log.info(line) self.controller.register_write(register_name, \ index, value) if line.startswith('reset_states'): self.log.info('RESETTING_STATES') # First stop the scheduler to avoid concurrent used # of the Thirft server self.t_sched.cancel() while self.t_sched.running: # Wait the end of the log printing time.sleep(0.5) time.sleep(1) # Reset the state of the switch self.controller.register_reset( 'nh_avaibility_1') self.controller.register_reset( 'nh_avaibility_2') self.controller.register_reset( 'nh_avaibility_3') self.controller.register_reset( 'nbflows_progressing_2') self.controller.register_reset( 'nbflows_progressing_3') self.controller.register_reset('rerouting_ts') self.controller.register_reset( 'timestamp_reference') self.controller.register_reset('sw_time') self.controller.register_reset('sw_index') self.controller.register_reset('sw_sum') self.controller.register_reset('sw') self.controller.register_reset( 'flowselector_key') self.controller.register_reset( 'flowselector_nep') self.controller.register_reset( 'flowselector_ts') self.controller.register_reset( 'flowselector_last_ret') self.controller.register_reset( 'flowselector_last_ret_bin') self.controller.register_reset( 'flowselector_correctness') self.controller.register_reset( 'flowselector_fwloops') print self.sw_name, ' RESET.' # Restart the scheduler time.sleep(1) self.t_sched.start()
class GenFault(object): def __init__(self, program): if program == "f": self.topo = Topology( db="../p4src_flowsize/topology.db") #set the topology elif program == "i": self.topo = Topology( db="../p4src_interval/topology.db") #set the topology self.controllers = {} #the switches self.init() def init(self): self.connect_to_switches() 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 loop(self): switches = raw_input( "type the switch's name to gen loop,seperated by ','\nmust be physically loop-able:\n" ).split(',') IPs = [] for sw_name in self.controllers.keys(): for host in self.topo.get_hosts_connected_to(sw_name): host_ip = self.topo.get_host_ip(host) + "/24" IPs.append(host_ip) for i in range(len(switches)): sw_name = switches[i] self.controllers[sw_name].table_clear("ecmp_group_to_nhop") self.controllers[sw_name].table_clear("ipv4_lpm") #next_hop=NULL if i == len(switches) - 1: next_hop = switches[0] else: next_hop = switches[i + 1] 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) #print "table_add at {}:".format(sw_name) for host_ip in IPs: self.controllers[sw_name].table_add("ipv4_lpm", "set_nhop", [str(host_ip)],\ [str(dst_sw_mac), str(sw_port)]) def blackhole(self, args): if args.sw_name == None: pass print "Not implemented yet,please specify the switch name" else: self.controllers[args.sw_name].table_clear("ecmp_group_to_nhop") self.controllers[args.sw_name].table_clear("ipv4_lpm") print args.sw_name, "has been shut down" def remove_cpu(self): # log=open("./router.log","w") # log.write(str(self.topo)) print(1) print(self.topo.get_shortest_paths_between_nodes("s5", "h2")) # print(self.topo["sw-cpu"]) # print(self.topo.network_graph["sw-cpu"]) self.topo.network_graph.remove_node("sw-cpu") # self.topo.save("../p4src_interval/topology.db") # self.topo.load("../p4src_interval/topology.db") # del self.topo #self.topo=Topology(db="../p4src_interval/topology.db") print("\n\n\n\n\n") print(2) print(self.topo.get_shortest_paths_between_nodes("h1", "h8")) # print(self.topo["sw-cpu"]) # print(self.topo.network_graph["sw-cpu"]) # log=open("./router1.log","w") # log.write(str(self.topo)) def reroute(self): #log=open("./router.log","w") #log.write(str(self.topo)) self.topo.network_graph.remove_node("sw-cpu") switch_ecmp_groups = { sw_name: {} for sw_name in self.topo.get_p4switches().keys() } for sw_name, controllers in self.controllers.items(): controllers.table_clear("ecmp_group_to_nhop") controllers.table_clear("ipv4_lpm") 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) # log.write("[1] table_add ipv4_lpm set_nhop at {} to host {} using port {}\n".format(sw_name,host,sw_port)) 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) # log.write("[2] table_add ipv4_lpm set_nhop at {} to host {} using port {} to nexthop {}\n".format(sw_name,host,sw_port,next_hop)) 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) # log.write("[3] table_add ipv4_lpm ecmp_group at {} to switch {} to paths{}\n".format(sw_name,sw_dst,paths)) 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) #log.write("[4] table_add ipv4_lpm ecmp_group at {} to switch {} to paths{}\n".format(sw_name,sw_dst,paths)) # log.write("[4] table_add ipv4_lpm ecmp_group at {} to switch {} using port {}\n".format(sw_name,sw_dst,port)) 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) # log.write("[5] table_add ipv4_lpm ecmp_group at {} to switch {} to paths{}\n".format(sw_name,sw_dst,paths)) self.controllers[sw_name].table_add( "ipv4_lpm", "ecmp_group", [str(host_ip)], [ str(new_ecmp_group_id), str(len(dst_macs_ports)) ])
class RSVPController(object): def __init__(self): """Initializes the topology and data structures """ if not os.path.exists("topology.db"): print("Could not find topology object!!!\n") raise (Exception) self.topo = Topology(db="topology.db") self.controllers = {} self.init() # sorted by timeouts self.current_reservations = {} # initial link capacity self.links_capacity = self.build_links_capacity() self.update_lock = threading.Lock() self.timeout_thread = threading.Thread( target=self.reservations_timeout_thread, args=(1, )) self.timeout_thread.daemon = True self.timeout_thread.start() def init(self): """Connects to switches and resets. """ self.connect_to_switches() self.reset_states() def reset_states(self): """Resets registers, tables, etc. """ [controller.reset_state() for controller in self.controllers.values()] def connect_to_switches(self): """Connects to all the switches in the topology and saves them in self.controllers. """ for p4switch in self.topo.get_p4switches(): thrift_port = self.topo.get_thrift_port(p4switch) self.controllers[p4switch] = SimpleSwitchAPI(thrift_port) def build_links_capacity(self): """Builds link capacities dictionary Returns: dict: {edge: bw} """ links_capacity = {} # Iterates all the edges in the topology formed by switches for src, dst in self.topo.network_graph.keep_only_p4switches().edges: bw = self.topo.network_graph.edges[(src, dst)]['bw'] # add both directions links_capacity[(src, dst)] = bw links_capacity[(dst, src)] = bw return links_capacity def reservations_timeout_thread(self, refresh_rate=1): """Every refresh_rate checks all the reservations. If any times out tries to delete it. Args: refresh_rate (int, optional): Refresh rate. Defaults to 1. """ while True: # sleeps time.sleep(refresh_rate) # locks the self.current_reservations data structure. This is done # because the CLI can also access the reservations. with self.update_lock: to_remove = [] # iterates all the reservations and updates its timeouts # if timeout is reached we delete it for reservation, data in self.current_reservations.items(): data['timeout'] -= refresh_rate # has expired? if data['timeout'] <= 0: to_remove.append(reservation) # removes all the reservations that expired for reservation in to_remove: self.del_reservation(*reservation) def set_mpls_tbl_labels(self): """We set all the table defaults to reach all the hosts/networks in the network """ # for all switches for sw_name, controller in self.controllers.items(): # get all direct hosts and add direct entry 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) host_mac = self.topo.get_host_mac(host) # adds direct forwarding rule controller.table_add( "FEC_tbl", "ipv4_forward", ["0.0.0.0/0", str(host_ip)], [str(host_mac), str(sw_port)]) for switch in self.topo.get_switches_connected_to(sw_name): sw_port = self.topo.node_to_node_port_num(sw_name, switch) # reverse port mac other_switch_mac = self.topo.node_to_node_mac(switch, sw_name) # we add a normal rule and a penultimate one controller.table_add("mpls_tbl", "mpls_forward", [str(sw_port), '0'], [str(other_switch_mac), str(sw_port)]) controller.table_add("mpls_tbl", "penultimate", [str(sw_port), '1'], [str(other_switch_mac), str(sw_port)]) def build_mpls_path(self, switches_path): """Using a path of switches builds the mpls path. In our simplification labels are port indexes. Args: switches_path (list): path of switches to allocate Returns: list: label path """ # label path label_path = [] # iterate over all pair of switches in the path for current_node, next_node in zip(switches_path, switches_path[1:]): # we get sw1->sw2 port number from topo object label = self.topo.node_to_node_port_num(current_node, next_node) label_path.append(label) return label_path def get_sorted_paths(self, src, dst): """Gets all paths between src, dst sorted by length. This function uses the internal networkx API. Args: src (str): src name dst (str): dst name Returns: list: paths between src and dst """ paths = self.topo.get_all_paths_between_nodes(src, dst) # trim src and dst paths = [x[1:-1] for x in paths] return paths def get_shortest_path(self, src, dst): """Computes shortest path. Simple function used to test the system by always allocating the shortest path. Args: src (str): src name dst (str): dst name Returns: list: shortest path between src,dst """ return self.get_sorted_paths(src, dst)[0] def check_if_reservation_fits(self, path, bw): """Checks if a the candidate reservation fits in the current state of the network. Using the path of switches, checks if all the edges (links) have enough space. Otherwise, returns False. Args: path (list): list of switches bw (float): requested bandwidth in mbps Returns: bool: true if allocation can be performed on path """ # iterates over all pairs of switches (edges) for link in zip(path, path[1:]): # checks if there is enough capacity if (self.links_capacity[link] - bw) < 0: return False return True def add_link_capacity(self, path, bw): """Adds bw capacity to a all the edges along path. This function is used when an allocation is removed. Args: path (list): list of switches bw (float): requested bandwidth in mbps """ # iterates over all pairs of switches (edges) for link in zip(path, path[1:]): # adds capacity self.links_capacity[link] += bw def sub_link_capacity(self, path, bw): """subtracts bw capacity to a all the edges along path. This function is used when an allocation is added. Args: path (list): list of switches bw (float): requested bandwidth in mbps """ # iterates over all pairs of switches (edges) for link in zip(path, path[1:]): # subtracts capacity self.links_capacity[link] -= bw def get_available_path(self, src, dst, bw): """Checks all paths from src to dst and picks the shortest path that can allocate bw. Args: src (str): src name dst (str): dst name bw (float): requested bandwidth in mbps Returns: list/bool: best path/ False if none """ # get all paths sorted from shorter to longer paths = self.get_sorted_paths(src, dst) for path in paths: # checks if the path has capacity if self.check_if_reservation_fits(path, bw): return path return False def get_meter_rates_from_bw(self, bw, burst_size=700000): """Returns the CIR and PIR rates and bursts to configure meters at bw. Args: bw (float): desired bandwdith in mbps burst_size (int, optional): Max capacity of the meter buckets. Defaults to 50000. Returns: list: [(rate1, burst1), (rate2, burst2)] """ rates = [] rates.append((0.125 * bw, burst_size)) rates.append((0.125 * bw, burst_size)) return rates def set_direct_meter_bandwidth(self, sw_name, meter_name, handle, bw): """Sets a meter entry (using a table handle) to color packets using bw mbps Args: sw_name (str): switch name meter_name (str): meter name handle (int): entry handle bw (float): desired bandwidth to rate limit """ rates = self.get_meter_rates_from_bw(bw) self.controllers[sw_name].meter_set_rates(meter_name, handle, rates) def _add_reservation(self, src, dst, duration, bandwidth, priority, path, update): """Adds or updates a single reservation Args: src (str): src name dst (str): dst name duration (float): reservation timeout bandwidth (float): requested bandwidth in mbps priority (int): reservation priority path (list): switch path were to allocate the reservation update (bool): update flag """ # We build the label path. For that we use self.build_mpls_path and # reverse the returned labels, since our rsvp.p4 will push them in # reverse order. label_path = [str(x) for x in self.build_mpls_path(path)[::-1]] # Get required info to add a table rule # get ingress switch as the first node in the path src_gw = path[0] # compute the action name using the length of the labels path action = "mpls_ingress_{}_hop".format(len(label_path)) # src lpm address src_ip = str(self.topo.get_host_ip(src) + "/32") # dst exact address dst_ip = str(self.topo.get_host_ip(dst)) # match list match = [src_ip, dst_ip] # if we have a label path if len(label_path) != 0: # If the entry is new we simply add it if not update: entry_handle = self.controllers[src_gw].table_add( "FEC_tbl", action, match, label_path) self.set_direct_meter_bandwidth(src_gw, "rsvp_meter", entry_handle, bandwidth) # if the entry is being updated we modify if using its handle else: entry = self.current_reservations.get((src, dst), None) entry_handle = self.controllers[src_gw].table_modify( "FEC_tbl", action, entry['handle'], label_path) self.set_direct_meter_bandwidth(src_gw, "rsvp_meter", entry_handle, bandwidth) # udpates controllers link and reservation structures if rules were added succesfully if entry_handle: self.sub_link_capacity(path, bandwidth) self.current_reservations[(src, dst)] = { "timeout": (duration), "bw": (bandwidth), "priority": (priority), 'handle': entry_handle, 'path': path } print("Successful reservation({}->{}): path: {}".format( src, dst, "->".join(path))) else: print("\033[91mFailed reservation({}->{}): path: {}\033[0m". format(src, dst, "->".join(path))) else: print("Warning: Hosts are connected to the same switch!") def add_reservation(self, src, dst, duration, bandwidth, priority): """Adds a new reservation taking into account the priority. This addition can potentially move or delete other allocations. Args: src (str): src name dst (str): dst name duration (float): reservation timeout bandwidth (float): requested bandwidth in mbps priority (int): reservation priority """ # locks the self.current_reservations data structure. This is done # because there is a thread that could access it concurrently. with self.update_lock: # if reservation exists, we allocate it again, by just updating the entry # for that we set the FLAG UPDATE_ENTRY and restore its link capacity # such the new re-allocation with a possible new bw/prioirty can be done # taking new capacities into account. UPDATE_ENTRY = False if self.current_reservations.get((src, dst), None): data = self.current_reservations[(src, dst)] path = data['path'] bw = data['bw'] # updates link capacities self.add_link_capacity(path, bw) UPDATE_ENTRY = True # finds the best (if exists) path to allocate the requestes reservation path = self.get_available_path(src, dst, bandwidth) if path: # add or update the reservation self._add_reservation(src, dst, duration, bandwidth, priority, path, UPDATE_ENTRY) # Cant be allocated! However, it might be possible to re-allocate things else: # check if the flow could be placed removing lower priorities previous_links_capacities = self.links_capacity.copy() for reservation, data in self.current_reservations.items(): # make sure we do not remove ourselves # again in case this is a modification if reservation == (src, dst): continue if data['priority'] < priority: self.add_link_capacity(data['path'], data['bw']) # check if it fits in a newtwork without lower priority flows path = self.get_available_path(src, dst, bandwidth) # we rebalance lower priority reservations if possible if path: # adds main new allocation self._add_reservation(src, dst, duration, bandwidth, priority, path, UPDATE_ENTRY) # re-allocate everything if possible for reservation, data in sorted( self.current_reservations.items(), key=lambda x: x[1]['priority'], reverse=True): if data['priority'] < priority: src, dst = reservation[0], reservation[1] path = self.get_available_path( src, dst, data['bw']) if path: # add or update the reservation self._add_reservation(src, dst, data['timeout'], data['bw'], data['priority'], path, True) else: # delete it data = self.current_reservations[(src, dst)] path = data['path'] bw = data['bw'] self.sub_link_capacity(path, bw) print( "\033[91mDeleting allocation {}->{} due to a higher priority allocation!\033[0m" .format(src, dst)) self.del_reservation(src, dst) else: # restore capacities self.links_capacity = previous_links_capacities # if we failed and it was an entry to be updated we remove it if UPDATE_ENTRY: data = self.current_reservations[(src, dst)] path = data['path'] bw = data['bw'] self.sub_link_capacity(path, bw) print("Deleting new allocation. Does not fit anymore!") self.del_reservation(src, dst) print( "\033[91mRESERVATION FAILURE: no bandwidth available!\033[0m" ) def del_reservation(self, src, dst): """Deletes a reservation between src and dst, if exists. To delete the reservation the self.current_reservations data structure is used to retrieve all the needed information. After deleting the reservation from the ingress switch, path capacities are updated. Args: src (str): src name dst (str): dst name """ # checks if there is an allocation between src->dst entry = self.current_reservations.get((src, dst), None) if entry: # gets handle to delete entry entry_handle = entry['handle'] # gets src ingress switch sw_gw = self.topo.get_host_gateway_name(src) # removes table entry using the handle self.controllers[sw_gw].table_delete("FEC_tbl", entry_handle, True) # updates links capacity self.add_link_capacity(entry['path'], entry['bw']) # removes the reservation from the controllers memory del (self.current_reservations[(src, dst)]) print( "\nRSVP Deleted/Expired Reservation({}->{}): path: {}".format( src, dst, "->".join(entry['path']))) else: print("No entry for {} -> {}".format(src, dst)) def del_all_reservations(self): """Deletes all the current reservations """ # locks the self.current_reservations data structure. This is done # because there is a thread that could access it concurrently. with self.update_lock: # makes a copy of all the reservation pairs reservation_keys = self.current_reservations.keys() for src, dst in reservation_keys: self.del_reservation(src, dst)
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 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 reset_states(self): [controller.reset_state() for controller in self.controllers.values()] def set_table_defaults(self): for controller in self.controllers.values(): controller.table_set_default("dmac", "drop", []) def route(self): I = int(sys.argv[1]) host_num = 2**(I) print I if I == 4: # mapping the first letter of sw_name/device_name to the layer number (from top to bottom) in binary tree. device_id_mapping = {"a": 0, "b": 1, "c": 2, "d": 3, "h": 4} elif I == 5: device_id_mapping = { "a": 0, "b": 1, "c": 2, "d": 3, "e": 4, "h": 5 } elif I == 6: device_id_mapping = { "a": 0, "b": 1, "c": 2, "d": 3, "e": 4, "f": 5, "h": 6 } elif I == 7: device_id_mapping = { "a": 0, "b": 1, "c": 2, "d": 3, "e": 4, "f": 5, "g": 6, "h": 7 } else: print "please enter I in 4,5,6,7" sys.exit(0) for sw_name, controller in self.controllers.items(): # get which layer (starting from 0, from top to bottom) the current sw belongs to layer_id = device_id_mapping[sw_name[0]] # get the device number (starting from 0, from left to right) of the current sw device_id = int(sw_name[1:]) - 1 print device_id # for switches in the the top layer, just forward based on whether the (dest) host_id locates in # the left part of the Binary Tree -- forward via the left port (ie, port 1), # or the right part of the Binary Tree -- forward via the right port (ie, port 2). if layer_id == 0: for host_id in range(host_num): out_port = host_id // (host_num / 2) + 1 controller.table_add( "dmac", "forward", ["00:00:0a:00:00:%02x" % (host_id + 1, )], ["%d" % (out_port, )]) # for switches in the other layers, we forward based on whether the (dest) host_id locates in # the left part of the subtree starting from the current sw -- forward via the left port (ie, port 2), # or the right part of the subtree -- forward via the right port (ie, port 3), # or ourside of the subtree -- forward to the upper layer via the top port (ie, port 1). else: interval = host_num // (2**(layer_id + 1)) axis = 2 * device_id * interval + interval for host_id in range(host_num): if host_id in range(axis - interval, axis): out_port = 2 elif host_id in range(axis, axis + interval): out_port = 3 else: out_port = 1 controller.table_add( "dmac", "forward", ["00:00:0a:00:00:%02x" % (host_id + 1, )], ["%d" % (out_port, )]) def main(self): self.route()
class FlowtableManager(object): def __init__(self): self.topo = Topology(db='topology.db') dic = self.topo.get_p4switches() self.sw_name = [sw for sw in dic.keys()] self.controller = { sw: SimpleSwitchAPI(self.topo.get_thrift_port(sw)) for sw in self.sw_name } self.multicast_table = dict() def add_forward_table(self): host_list = [h for h in self.topo.get_hosts().keys()] for src in self.sw_name: self.controller[src].table_set_default('ingress.ipv4_c.ipv4', 'drop', []) direct_sw_list = self.topo.get_switches_connected_to(src) for sw in direct_sw_list: port = self.topo.node_to_node_port_num(src, sw) self.controller[src].table_add( 'egress.mac_c.adjust_mac', 'set_mac', [str(port)], [ str(self.topo.node_to_node_mac(src, sw)), str(self.topo.node_to_node_mac(sw, src)) ]) direct_host_list = self.topo.get_hosts_connected_to(src) for h in direct_host_list: ip = self.topo.get_host_ip(h) port = self.topo.node_to_node_port_num(src, h) self.controller[src].table_add('ingress.ipv4_c.ipv4', 'forward', [str(ip) + '/32'], [str(port)]) self.controller[src].table_add( 'egress.mac_c.adjust_mac', 'set_mac', [str(port)], [ str(self.topo.node_to_node_mac(src, h)), str(self.topo.node_to_node_mac(h, src)) ]) indirect_host_list = list( set(host_list).difference(direct_host_list)) for h in indirect_host_list: ip = self.topo.get_host_ip(h) path = self.topo.get_shortest_paths_between_nodes(src, h)[0] port = self.topo.node_to_node_port_num(src, path[1]) self.controller[src].table_add('ingress.ipv4_c.ipv4', 'forward', [str(ip) + '/32'], [str(port)]) def add_multicast_table(self): for sw in self.sw_name: self.multicast_table.update({sw: {}}) port = self.topo.get_interfaces_to_port(sw) num = len(port) - 1 if sw + '-cpu-eth0' in port.keys(): num -= 1 self.controller[sw].mc_mgrp_create('1') for i in range(int(comb(num, 2))): self.controller[sw].mc_mgrp_create(str(i + 2)) port_list = [] for i in range(num): port_list.append(str(i + 1)) self.controller[sw].mc_node_create('0', port_list) self.controller[sw].mc_node_associate('1', '0') n = 2 for i in range(num): for j in range(i + 1, num): port_list = [str(i + 1), str(j + 1)] self.controller[sw].mc_node_create(str(n - 1), port_list) self.controller[sw].mc_node_associate(str(n), str(n - 1)) self.multicast_table[sw].update({ (str(i + 1), str(j + 1)): n }) n += 1 def add_forward_entry(self, sw, ip, port): try: self.controller[sw].table_add('ingress.ipv4_c.ipv4', 'forward', [str(ip)], [str(port)]) except: print('add_forward_entry error') def add_l3_entry(self, sw, act=[], key=[]): try: self.controller[sw].table_add('ingress.ipv4_c.l3_match_to_index', 'protect', key, act) except: print('add_l3_entry error')
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 RSVPController(object): def __init__(self): """Initializes the topology and data structures """ if not os.path.exists("topology.db"): print("Could not find topology object!!!\n") raise (Exception) self.topo = Topology(db="topology.db") self.controllers = {} self.init() # sorted by timeouts self.current_reservations = {} # initial link capacity self.links_capacity = self.build_links_capacity() self.update_lock = threading.Lock() self.timeout_thread = threading.Thread( target=self.reservations_timeout_thread, args=(1, )) self.timeout_thread.daemon = True self.timeout_thread.start() def init(self): """Connects to switches and resets. """ self.connect_to_switches() self.reset_states() def reset_states(self): """Resets registers, tables, etc. """ [controller.reset_state() for controller in self.controllers.values()] def connect_to_switches(self): """Connects to all the switches in the topology and saves them in self.controllers. """ for p4switch in self.topo.get_p4switches(): thrift_port = self.topo.get_thrift_port(p4switch) self.controllers[p4switch] = SimpleSwitchAPI(thrift_port) def build_links_capacity(self): """Builds link capacities dictionary Returns: dict: {edge: bw} """ links_capacity = {} # Iterates all the edges in the topology formed by switches for src, dst in self.topo.network_graph.keep_only_p4switches().edges: bw = self.topo.network_graph.edges[(src, dst)]['bw'] # add both directions links_capacity[(src, dst)] = bw links_capacity[(dst, src)] = bw return links_capacity def reservations_timeout_thread(self, refresh_rate=1): """Every refresh_rate checks all the reservations. If any times out tries to delete it. Args: refresh_rate (int, optional): Refresh rate. Defaults to 1. """ while True: # sleeps time.sleep(refresh_rate) # locks the self.current_reservations data structure. This is done # because the CLI can also access the reservations. with self.update_lock: pass # PART 1, TASK 5.1 Iterate self.current_reservations, update timeout, and if any # entry expired, delete it. def set_mpls_tbl_labels(self): """We set all the table defaults to reach all the hosts/networks in the network """ # for all switches for sw_name, controller in self.controllers.items(): pass # TODO PART 1 TASK 2 # 1) for all the hosts connected to this switch add the FEC_tbl entry # 2) for all switches connected to this switch add the 2 mplt_tbl entries def build_mpls_path(self, switches_path): """Using a path of switches builds the mpls path. In our simplification labels are port indexes. Args: switches_path (list): path of switches to allocate Returns: list: label path """ label_path = [] # PART 1, TASK 3.2 Get mpls stack of labels return label_path def get_sorted_paths(self, src, dst): """Gets all paths between src, dst sorted by length. This function uses the internal networkx API. Args: src (str): src name dst (str): dst name Returns: list: paths between src and dst """ paths = self.topo.get_all_paths_between_nodes(src, dst) # trim src and dst paths = [x[1:-1] for x in paths] return paths def get_shortest_path(self, src, dst): """Computes shortest path. Simple function used to test the system by always allocating the shortest path. Args: src (str): src name dst (str): dst name Returns: list: shortest path between src,dst """ return self.get_sorted_paths(src, dst)[0] def check_if_reservation_fits(self, path, bw): """Checks if a the candidate reservation fits in the current state of the network. Using the path of switches, checks if all the edges (links) have enough space. Otherwise, returns False. Args: path (list): list of switches bw (float): requested bandwidth in mbps Returns: bool: true if allocation can be performed on path """ # PART 1, TASK 3.1 Implement this function and its helper check_if_reservation_fits def add_link_capacity(self, path, bw): """Adds bw capacity to a all the edges along path. This function is used when an allocation is removed. Args: path (list): list of switches bw (float): requested bandwidth in mbps """ # PART 1, TASK 3.4 add bw to edges def sub_link_capacity(self, path, bw): """subtracts bw capacity to a all the edges along path. This function is used when an allocation is added. Args: path (list): list of switches bw (float): requested bandwidth in mbps """ # PART 1, TASK 3.4 sub bw to edges def get_available_path(self, src, dst, bw): """Checks all paths from src to dst and picks the shortest path that can allocate bw. Args: src (str): src name dst (str): dst name bw (float): requested bandwidth in mbps Returns: list/bool: best path/ False if none """ # PART 1, TASK 3.1 Implement this function and its helper check_if_reservation_fits def get_meter_rates_from_bw(self, bw, burst_size=700000): """Returns the CIR and PIR rates and bursts to configure meters at bw. Args: bw (float): desired bandwdith in mbps burst_size (int, optional): Max capacity of the meter buckets. Defaults to 50000. Returns: list: [(rate1, burst1), (rate2, burst2)] """ # PART 2 TASK 1.2 get the CIR and PIR from bw def set_direct_meter_bandwidth(self, sw_name, meter_name, handle, bw): """Sets a meter entry (using a table handle) to color packets using bw mbps Args: sw_name (str): switch name meter_name (str): meter name handle (int): entry handle bw (float): desired bandwidth to rate limit """ # PART 2 TASK 1.3 use the controller to configure the meter def add_reservation(self, src, dst, duration, bandwidth, priority): """[summary] Args: src (str): src name dst (str): dst name duration (float): reservation timeout bw (float): requested bandwidth in mbps priority (int): reservation priority """ # locks the self.current_reservations data structure. This is done # because there is a thread that could access it concurrently. with self.update_lock: # PART 1, TASK 3.4 check if there is an existing reservation for (src,dst). # you can use the self.current_reservations dictionary to check it. # If the reservation exists get the path and bw and update the links capacity # data structure using `self.add_link_capacity(path, bw)` # PART 1, TASK 3.1. Once get_available_path is implemented call it to get a path. path = self.get_available_path(src, dst, bandwidth) # PART 1, TASK 3.2 If there is an available path if path: pass # PART 1, TASK 3.2 Get mpls stack of labels # PART 1, TASK 3.3 get: # 1) ingress switch name # 2) action name using `mpls_ingress_x_hop` set x as number of labels # 3) src and dst ips (your match) # 4) make sure all your labels are strings and use them as action parameters # PART 1, TASK 3.4 # check if its a new or an existing reservation (to update) # add entry or modify # PART 2 TASK 1.4 Configure the associated meter properly. # update controllers data structures: self.current_reservation & self.links_capacity # PART 1, TASK 3.2 otherwise we print no path available else: # PART 1, task 4.3 if we dont find a path but the reservation existed # you have to erase it while making sure you update links_capacity accordingly print( "\033[91mRESERVATION FAILURE: no bandwidth available!\033[0m" ) def del_reservation(self, src, dst): """Deletes a reservation between src and dst, if exists. To delete the reservation the self.current_reservations data structure is used to retrieve all the needed information. After deleting the reservation from the ingress switch, path capacities are updated. Args: src (str): src name dst (str): dst name """ # PART 1, TASK 4.1 remove the reservation from the switch, controller and update links capacities. def del_all_reservations(self): """Deletes all the current reservations """ # locks the self.current_reservations data structure. This is done # because there is a thread that could access it concurrently. with self.update_lock: pass