def send_packet(interface, args, program): # a l2 implementation #dstAddr = socket.gethostbyname(args.d) #print(socket.getaddrinfo(sys.argv[1], None, 0, socket.SOCK_STREAM)) #ether_dst = get_dst_mac(dstAddr) #if not ether_dst: # print "Mac address for %s was not found in the ARP table" % dstAddr # exit(1) #pkt= Ether(src=get_if_hwaddr(interface),dst=ether_dst) #pkt=pkt/IP(dst=dstAddr) #warning! #if want to send TCP ,must change the parser of p4 if program == "f": topo = Topology(db="../p4src_flowsize/topology.db") #set the topology elif program == "i": topo = Topology(db="../p4src_interval/topology.db") #set the topology dstAddr = topo.get_host_ip(args.d) pkt = IP(dst=dstAddr) if args.type == "tcp": pkt = pkt / TCP() elif args.type == "udp": pkt = pkt / UDP() pkt = pkt / flag() / "load0load1load2load3" else: pkt = pkt / ICMP() while True: raw_input("Testing! Press the return key to send a packet using " + args.type.lower()) print "Sending on interface %s \n" % (interface) #sendp(pkt, iface=iface, verbose=False) for i in range(args.number): send(pkt)
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 P4Obj: """Utility class for p4run-related stuff. Given by the p4run fixture.""" def __init__(self, workdir): self.topo_path = os.path.join(workdir, "topology.db") self.topo = Topology(self.topo_path) def host_IPs(self): """Returns a dict of hosts and IPs. Returns the first IP of a host if there are multiple. """ return { h: self.topo.get_host_ip(h) for h in self.topo.get_hosts() } def iponly(self, ip): """Utility function to strip netmask from IP: iponly('10.0.0.1/24') => '10.0.0.1'""" return ip.split('/')[0]
flow_size_max = length flow_gen_mode = 'perflow' per_flow_mode = 'tiled' num_flows = len(iperf_host_list) / 2 gap_dist = 'persist' gap_max = length + 1000 if mode == "mix": gen_memcached(mc_host_list, length) gen_iperf(iperf_host_list, length) traces = memcached_traces + iperf_traces traces.sort() f = open(out_file, "w") for i in mc_host_list: f.write(topo.get_host_ip("h%d" % i)) f.write(' ') f.write("\n") for trace in traces: f.write("h%d " % trace[1]) f.write("%f " % (trace[0] / 1000.0)) if "." in trace[2]: f.write("2 %s %f\n" % (trace[2], trace[3] / 1000.0)) else: if trace[3] == -1: f.write("1 %s\n" % trace[2]) else: f.write("0 %s %d\n" % (trace[2], trace[3]))
import random import time from p4utils.utils.topology import Topology from subprocess import Popen topo = Topology(db="topology.db") iperf_send = "mx {0} iperf3 -c {1} -M 9000 -t {2} --bind {3} -p {5} 2>&1 >/dev/null" iperf_recv = "mx {0} iperf3 -s -p {1} --one-off 2>&1 >/dev/null" Popen("sudo killall iperf iperf3", shell=True) dst_port1 = random.randint(1024, 65000) dst_port2 = random.randint(1024, 65000) Popen(iperf_recv.format("h3", dst_port1), shell=True) Popen(iperf_recv.format("h4", dst_port2), shell=True) time.sleep(1) import sys duration = int(sys.argv[1]) Popen(iperf_send.format("h1", topo.get_host_ip("h3"), duration, topo.get_host_ip("h1"), dst_port1, dst_port1), shell=True) Popen(iperf_send.format("h2", topo.get_host_ip("h4"), duration, topo.get_host_ip("h2"), dst_port2, dst_port2), shell=True)
def demo(reactor): lines = WaitForLines() stdio.StandardIO(lines) ##### Preparation ####################################################### # These are our servers. server_hosts = [ # host, port, num CPUs ('h1', 9000, 1), ('h1', 9001, 2), ('h2', 9002, 4), ('h2', 9003, 8), ] # Run the server and client programs and get a "remote control" to them. servers = yield all_results([ remote_module('myutils.server', port, ncpus, host=host) for host, port, ncpus in server_hosts ]) clients = yield all_results([ remote_module('myutils.client', host='h3'), remote_module('myutils.client', host='h4'), ]) # Build an (ip, port) => server remote dict to use later. topo = Topology('./topology.db') server_IPs = {(topo.get_host_ip(h), p): remote for (h, p, _), remote in zip(server_hosts, servers)} # Teach my load balancer controller how to get load from the servers. # In real life I could be e.g. SSHing into the servers, or using my # monitoring infrastructure. def get_load(ip, port): return server_IPs[(ip, port)].callRemote('get_load', 20) def set_weights(loads): return [1.0 / load for load in loads] # And start the controller. lb = yield MetricsLoadBalancer.get_initialised( 's1', get_metrics=get_load, metrics_to_weights=set_weights) # Create a server pool on the loadbalancer. pool_handle = yield lb.add_pool('10.0.0.1', 8000) for ip, port in server_IPs.keys(): yield lb.add_dip(pool_handle, ip, port) yield lb.commit() ##### Now the fun begins ################################################ setup_graph(server_IPs, lb, 10) print('---------------- press Enter to start clients -----------------') yield lines.line_received @defer.inlineCallbacks def client0(): """Client 0 will send long-running requests: closes after 10 seconds.""" print('client0 running') yield clients[0].callRemote('start_echo_clients', '10.0.0.1', 8000, count=4) yield sleep(10) yield clients[0].callRemote('close_all_connections') @defer.inlineCallbacks def client1(): """Client 1 will send bursts of short connections (2s).""" print('client1 running') yield clients[1].callRemote('start_echo_clients', '10.0.0.1', 8000, count=20) yield sleep(2) yield clients[1].callRemote('close_all_connections') # Run client0 every 13 seconds. task.LoopingCall(client0).start(13) # Run client1 every 4 seconds. task.LoopingCall(client1).start(3) print( '---------------- press Enter to start adjusting weights ----------------' ) yield lines.line_received lb.start_loop()
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()
import random import time from p4utils.utils.topology import Topology from subprocess import Popen topo = Topology(db="topology.db") iperf_send = "mx {0} iperf3 -c {1} -M 9000 -t {2} --bind {3} --cport {4} -p {5} 2>&1 >/dev/null" iperf_recv = "mx {0} iperf3 -s -p {1} --one-off 2>&1 >/dev/null" Popen("sudo killall iperf iperf3", shell=True) dst_port1 = random.randint(1024, 65000) dst_port2 = random.randint(1024, 65000) Popen(iperf_recv.format("h3", dst_port1), shell=True) Popen(iperf_recv.format("h4", dst_port2), shell=True) time.sleep(1) import sys duration = int(sys.argv[1]) Popen(iperf_send.format("h1", topo.get_host_ip("h3"), duration, topo.get_host_ip("h1"), dst_port1, dst_port1), shell=True) Popen(iperf_send.format("h2", topo.get_host_ip("h4"), duration, topo.get_host_ip("h2"), dst_port2, dst_port2), shell=True)
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 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 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 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 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") #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 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()
routing_info = json.load(json_data) for host in topo.get_hosts(): threshold_tmp = threshold if "threshold" in routing_info['switches'][sw_name]: threshold_tmp = routing_info['switches'][sw_name][ 'threshold'] do_register_write(sock, 'threshold_registers', mapping_dic[host]*2, \ threshold_tmp) do_register_write(sock, 'threshold_registers', mapping_dic[host]*2+1, \ threshold_tmp) for host, nh in routing_info['switches'][sw_name][ 'prefixes'].items(): host_prefix = topo.get_host_ip(host) + '/24' if "customer" in nh and len(nh["customer"]) > 0: # Add the set_meta forwarding rule for the <prefix,customer> tuple add_entry_fwtable(sock, 'meta_fwtable', 'set_meta', \ [str(host_prefix), 0], [mapping_dic[host]*2, \ 0 if len(nh["customer"]) == 1 else 1,\ mapping_dic[nh["customer"][0]]]) # If only one backup next-hop is avaible, use it two times if len(nh["customer"]) == 2: nh["customer"].append(nh["customer"][-1]) i = 0 for n in nh["customer"]: do_register_write(sock, 'next_hops_port', mapping_dic[host]*6+i, \
duration = int(sys.argv[1]) send_cmds = [] recv_cmds = [] Popen("sudo killall iperf", shell=True) num_hosts = 8 num_senders= 4 for src_host in sorted(topo.get_hosts().keys(), key = lambda x: int(x[1:]))[:num_senders]: dst_host = 'h' + str((int(src_host[1:]) + 3) % num_hosts + 1) src_port = random.randint(1025, 65000) dst_port = random.randint(1025, 65000) src_ip = topo.get_host_ip(src_host) dst_ip = topo.get_host_ip(dst_host) send_cmds.append(iperf_send.format(src_host, dst_ip, duration, src_ip, src_port, dst_port)) recv_cmds.append(iperf_recv.format(dst_host, dst_port)) #start receivers first for recv_cmd in recv_cmds: print "Running:", recv_cmd Popen(recv_cmd, shell=True) time.sleep(1) for send_cmd in send_cmds: print "Running:", send_cmd Popen(send_cmd, shell=True)
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()