def message_in(*args, **kwargs): """ Generic message_in handle function Triggers corresponding event :param kwargs: :return: """ packet = kwargs.get('packet') switch = kwargs.get('switch') try: pkt = Ether(packet.packet.payload) try: Configuration.get('system_done') except ConfigurationNotFound: return if pkt.type == 0xEE00: # its an port down packet Log.info("Port:", pkt[PortDown].port_num, "down", "on ingress", pkt[PortDown].pad1, "on pipe") Event.trigger("port_down", pkt=pkt) if pkt.type == 0xDD00: # its an topology packet Event.trigger("topology_packet_in", packet=pkt, switch=switch) if pkt.type == 0x800 and pkt[IP].proto == 2: # its an igmp packet igmp = pkt.payload pkt = proto.connection_pb2.GroupPacket(type=int(igmp.type), mc_address=str(igmp.gaddr), src_ip=str(igmp.src), switch=Configuration.get('name')) Event.trigger("igmp_packet_to_controller", pkt=pkt) Log.debug("Send igmp packet to controller") except Exception as e: # it's not an ethernet frame pass
def message_in(*args, **kwargs): """ Generic message_in handle function Triggers corresponding event :param kwargs: :return: """ packet = kwargs.get('packet') switch = kwargs.get('switch') try: pkt = Ether(packet.packet.payload) try: Configuration.get('system_done') except ConfigurationNotFound: return if pkt.type == 0xDD00: # its an topology packet Event.trigger("topology_packet_in", packet=pkt, switch=switch) if pkt.type == 0x800: # its an igmp packet igmp = pkt.payload.payload pkt = proto.connection_pb2.GroupPacket( type=int(igmp.type), mc_address=str(igmp.gaddr), src_ip=str(pkt.payload.src), switch=Configuration.get('name')) Event.trigger("igmp_packet_to_controller", pkt=pkt) except Exception as e: # it's not an ethernet frame pass
def connect_to_switches(controller=None): """ Connect to switches and set forwarding pipeline :param controller: base controller who handles connections :param topology_controller: topology controller, who will send topology packets :return: """ controller.connect() Configuration.set('system_done', True)
def Hello(self, request, context): Event.trigger('global_connection') GlobalConnection.global_connection = GlobalConnection(ip=request.ip, port=request.port) device = TopologyManager.get_device(Configuration.get('name')) return proto.connection_pb2.SwitchInfo(name=Configuration.get('name'), ip=device.get_ip(), mac=device.get_mac(), bfr_id=device.get_bfr_id(0))
def updatePorts(self, pkt=None): port = pkt[PortDown].port_num device = TopologyManager.get_device(Configuration.get('name')) device.remove_port(port=port) Event.trigger("topology_change") Event.trigger("port_msg_to_controller", info=proto.connection_pb2.PortInfo( switch=Configuration.get('name'), port=port, status=False))
def main(): # without this line, no events would be fired, no topology discovered and no entries computed Event.activate() # base controller controller = BaseController(Configuration.get('p4info'), Configuration.get('bmv2_json')) # register event for new switch connections, this will add switches to device list Event.on('new_switch_connection', TopologyManager.add_device) # register events for static classes Event.on("packet_in", MessageInHandler.message_in) # handles generic packet in Event.on("topology_to_controller", GlobalConnection.send_topology_packet ) # triggers the send routine to server Event.on("igmp_packet_to_controller", GlobalConnection.send_group_packet ) # triggers the send routine to server Event.on( "port_msg_to_controller", GlobalConnection.send_port_info) # triggers the send routine to server topology = TopologyController(controller) # Create instances of sub controller mac = MacController(controller) port = PortController( controller=controller, notification_socket=Configuration.get('notification_socket')) group = GroupController(thrift_port=Configuration.get('thrift_port'), base=controller) # start connection procedure init_switches(controller=controller, topology_controller=topology, group_controller=group) # start grpc server for connection to main controller grpc_server = GRPCServer(listen_port=Configuration.get('listen_port')) # set controller in local server for table entry LocalServer.controller = controller # start grpc server grpc_server.start() # start port monitor threading.Thread(target=port.monitor_messages()).start()
def main(): # without this line, no events would be fired, no topology discovered and no entries computed Event.activate() # base controller controller = BaseController(p4info_file_path=Configuration.get('p4info'), bmv2_path=Configuration.get('bmv2_json'), prog_name=Configuration.get('prog_name'), bin_path=Configuration.get('bin_path'), cxt_json_path=Configuration.get('cxt_path')) # register event for new switch connections, this will add switches to device list Event.on('new_switch_connection', TopologyManager.add_device) # register events for static classes Event.on("packet_in", MessageInHandler.message_in) # handles generic packet in Event.on("topology_to_controller", GlobalConnection.send_topology_packet) # triggers the send routine to server Event.on("port_msg_to_controller", GlobalConnection.send_port_info) # triggers the send routine to server topology = TopologyController(controller) # Create instances of sub controller mac = MacController(controller) port = PortController(controller=controller) pd = PDSetup() # start connection procedure init_switches(controller=controller, topology_controller=topology, pd=pd) # start grpc server for connection to main controller grpc_server = GRPCServer(listen_port=Configuration.get('listen_port')) # set controller in local server for table entry LocalServer.controller = controller # start grpc server grpc_server.start() # start port monitor #threading.Thread(target=port.monitor_messages()).start() try: while True: time.sleep(1) except KeyboardInterrupt: pd.end() Log.info("Shutting down") os._exit(0)
def handle_topology_answer(self, pkt=None): """ Handle topology packet :param pkt: contains the topology packet :return: """ # if the controller is not yet connected to all local controllers # don't handle topology packets if not Configuration.get('system_done'): return ip = pkt.ip.encode('utf-8') mac = pkt.mac.encode('utf-8') name = pkt.name.encode('utf-8') port = pkt.port switch = pkt.switch.encode('utf-8') if name.startswith('h'): # it's a host TopologyManager.add_device(name=name, device=Host(name=name, ip=ip, mac=mac)) TopologyManager.get_device(name=name).add_device_to_port( device=switch, port=1) Log.event("topology packet with identifier", name, "from switch", switch, "on port", port, "with ip", ip) if TopologyManager.get_device(name=switch).add_device_to_port( device=name, port=int(port)): Event.trigger("topology_change", src_device=switch, dst_device=name, port=int(port))
def update(self, *args, **kwargs): """ Update bier tables, bift Is called on certain events :return: """ if "port_update" in kwargs: try: d = Configuration.get('update') if "bier" not in d: return if "sleep" in kwargs: time.sleep(kwargs.get("sleep")) except ConfigurationNotFound: pass srcDevice = kwargs.get('src_device') for switch in self._baseController.get_connections(): self.update_bier_forwarding_entries(switch=switch) Log.async_info("Updated BIER entries.")
def monitor_messages(self): """ Wait for port status message """ Log.info("Start port monitor") while True: msg = self.sub.recv() msg_type = struct.unpack('4s', msg[:4]) if msg_type[0] == 'PRT|': switch_id = struct.unpack('Q', msg[4:12]) num_statuses = struct.unpack('I', msg[16:20]) # wir betrachten immer nur den ersten Status port, status = struct.unpack('ii', msg[32:40]) self.port_status[port] = status if status == 0: # save port status time # timestamp type, type 2 is port info Log.log_to_file(round((time.time() * 1000) % 1000000), 2, "\r\n", file="logs/port_info.txt") device = TopologyManager.get_device(Configuration.get('name')) device.remove_port(port=port) Event.trigger("topology_change") bool_stat = (status == 1) Event.trigger("port_msg_to_controller", info=proto.connection_pb2.PortInfo(switch=Configuration.get('name'), port=port, status=bool_stat))
def update_mac_entry(self): """ Add mac rewriting rule for switch->dst_dev via port Args: switch (str): switch where rule will be installed """ valid_entries = [] device = TopologyManager.get_device(Configuration.get('name')) for device_name in device.get_device_to_port_mapping(): dev = TopologyManager.get_device(device_name) port = device.get_device_to_port(device_name) entry = TableEntry( match_fields={"eg_intr_md.egress_port": int(port)}, action_name="egress.mac_c.set_mac", action_params={ "srcAddr": device.get_mac(), "dstAddr": dev.get_mac() }) TableEntryManager.handle_table_entry( manager=self.table_manager, table_name="egress.mac_c.adjust_mac", table_entry=entry) valid_entries.append(entry.match_fields) # remove possible old entries self.table_manager.remove_invalid_entries( table_name="egress.mac_c.adjust_mac", valid_entries=valid_entries)
def configureTopologyPackets(self): """ Configure topology packet trigger """ timeout = 10 * 100 * 100 * 100 * 100 # 1 s dt = DevTarget_t(0, hex_to_i16(0xFFFF)) switch = TopologyManager.get_device(Configuration.get('name')) pkt = Ether(src='00:00:00:00:00:00', dst='ff:ff:ff:ff:ff:ff', type=0xDD00) pkt = pkt / TopologyDiscovery(identifier=switch.get_bfr_id(0), port=1, ip=str(switch.get_ip()), mac=str(switch.get_mac())) / IP() / IP() pktlen = len(pkt) offset = (int(self.port_pkt_len/16) + 5) * 16 self.tc.conn_mgr.pktgen_write_pkt_buffer(self.tc.hdl, dt, offset, pktlen, str(pkt)) config = PktGenAppCfg_t(trigger_type=PktGenTriggerType_t.TIMER_PERIODIC, timer=timeout, src_port=68, buffer_offset=offset, length=pktlen) self.tc.conn_mgr.pktgen_cfg_app(self.tc.hdl, dt, 1, config) self.tc.conn_mgr.pktgen_app_enable(self.tc.hdl, dt, 1)
def get_mirror_port(self, data=None): mirror_ports = [] for configuration in data[Configuration.get('name')]: if 'mirror' in configuration and configuration['mirror']: m_port = self.pal.pal_port_front_panel_port_to_dev_port_get(0, configuration['port'], configuration['channel']) mirror_ports.append(m_port) return mirror_ports
def __init__(self): parser = OptionParser() parser.add_option("-f", "--conf", dest="conf", default="etc/qga_consumer.conf", help="Configuration file") (options, args) = parser.parse_args() self.cfg = Configuration(options.conf)
def init_switches(controller=None, topology_controller=None, group_controller=None): """ Connect to switches and set forwarding pipeline :param controller: base controller who handles connections :param topology_controller: topology controller, who will send topology packets :return: """ controller.connect_and_arbitrate(grpc_port=Configuration.get('grpc_port'), device_id=Configuration.get('device_id')) controller.set_forwarding_pipeline_config() Configuration.set('system_done', True) group_controller.add_flood_node() group_controller.init_flood_group() topology_controller.send_topology_packets()
def setPorts(self, data=None): for configuration in data[Configuration.get('name')]: p_id = self.pal.pal_port_front_panel_port_to_dev_port_get(0, configuration['port'], configuration['channel']) self.pal.pal_port_add(0, p_id, configuration['speed'], pal_fec_type_t.BF_FEC_TYP_NONE) self.pal.pal_port_an_set(0, p_id, 2) self.pal.pal_port_enable(0, p_id) if 'loopback' in configuration and configuration['loopback']: self.pal.pal_port_loopback_mode_set(0, p_id, 1) Log.debug("Set port", p_id, "to loopback")
def update_based_on_group(self, *args, **kwargs): """ Updates tunnel rules :return: """ for bfr in Configuration.get("switches"): # only update BFIRs if not bfr["ingress"]: continue self.update_bier_encap_entry(switch=bfr["name"])
def update_based_on_topology(self, *args, **kwargs): """ Run an update based on a topology change In this casae the bier decap rules have to be adjusted because a switch may now be in a different domain :return: """ for bfr in Configuration.get("switches"): switch = bfr["name"] self.update_bier_decap_rule(switch=switch)
def update(self, **kwargs): device = TopologyManager.get_device(Configuration.get('name')) live_ports = filter(lambda x: self.port_status[x] == 1, device.get_device_to_port_mapping().values()) port_ids = map(lambda x: int(2**(x-1)), live_ports) # this prevents an empty sequence and forces liveport bitstring of 0 port_ids.append(0) port_string = reduce(ior, port_ids) self.write_port_entry(port_string=port_string)
def load_static_rules(self): """ Load static rules from json file specified in config """ valid_entries = defaultdict(list) for switch in Configuration.get('switches'): if "static_rules" in switch and Configuration.get( 'static_rules') == True: data = Configuration.load(switch['static_rules'])["entries"] for entry in data: if entry['table'] != "ingress.ipv4_c.ipv4": continue e = TableEntry( switch=entry["switch"], match_fields={ "hdr.ipv4.dstAddr": (str(entry["match_fields"][0]), int(entry["match_fields"][1])), "meta.ports.status": (BierComputation.id_to_bitstring( id=int(entry["match_fields"][2])), int(entry["match_fields"][3])) }, action_name=entry["action_name"], action_params={"port": int(entry["action_params"])}, priority=1) TableEntryManager.handle_table_entry( self.table_manager, table_name=entry["table"], table_entry=e) valid_entries[entry["switch"]].append(e.match_fields) Log.async_info("Static rules for IPv4 loaded.") return valid_entries
def connect(self): """ All switches grpc addresses are in ascending order. Connect until a connection can't be established :return: """ for switch in Configuration.get("switches"): try: self.__connections[switch["name"]] = SwitchConnection( grpc_address='127.0.0.1:{0}'.format( switch["local_controller_port"])) Log.async_debug("Connected to controller on port", switch["local_controller_port"]) Event.trigger('switch_connected', name=switch["name"]) except grpc.RpcError as e: raise SwitchConnectionFailed(switch["name"], switch["local_controller_port"]) Log.async_info("Connected to", len(self.__connections), "controller") Configuration.set('connected', True)
def AddEntry(self, request, context): """ Add a table entry to the switch """ if LocalServer.controller.add_entry(entry=request): if Configuration.get("name") == "s1": Log.log_to_file(round((time.time() * 1000) % 1000000), request.table_name, "\r\n", file="logs/entry_info.txt") return proto.connection_pb2.Status(code=1, message="all good")
def setFlood(self, data=None): grp_id = self.mc.mc_mgrp_create(self.mc_sess_hdl, 0, 1) ports = [] for configuration in data[Configuration.get('name')]: p_id = self.pal.pal_port_front_panel_port_to_dev_port_get(0, configuration['port'], configuration['channel']) if configuration['flood']: ports.append(p_id) node = self.mc.mc_node_create(self.mc_sess_hdl, 0, 0, set_port_map(ports), set_lag_map([])) self.mc.mc_associate_node(self.mc_sess_hdl, 0, grp_id, node, 0, 0)
def main(): Configuration.set('system_done', False) # without this line, no events would be fired, no topology discovered and no entries computed Event.activate() # register event for new switch connections, this will add switches to device list Event.on('new_switch_connection', TopologyManager.add_device) # base controller controller = BaseController() # register events for static classes Event.on("igmp_packet_in", GroupManager.handle_packet_in) # handles (un-)sub requests Event.on("port_message", TopologyManager.react_to_port_change) Event.on("topology_change", TopologyManager.build_domain) topology = TopologyController(controller) # Create instances of sub controller for CLI ipv4 = IPv4Controller(controller) bier = BierController(controller) #tunnel = TunnelController(controller) # add some cli commands for static classes without init CLI.add_command("plot_topology", TopologyManager.plot, "Plot topology") CLI.add_command("describe_topology", TopologyManager.describe, "Describe topology") CLI.add_command("describe_groups", GroupManager.describe, "Describe groups") CLI.add_command("show_configuration", Configuration.show_all, "Show all configurations") CLI.add_command("table_manager", TableEntryManager.handle_cli_command, "show_tables <controller name>") CLI.add_command("load_static_rules", load_static_rules, "Load static rules", ipv4) # start global grpc control server GRPCServer(listen_port=Configuration.get('listen_port')).start() # start connection procedure in thread, so that cli will get initialized and logs can be printed threading.Timer(2, connect_to_switches, kwargs={'controller': controller}).start() CLI.start_cli()
def add_node(self, n): """ Add node to graph :param n: :return: """ self._graph.add_node(n) # if this node is a switch, add it to the switch graph for switch in Configuration.get("switches"): if switch["name"] == n: self._switch_graph.add_node(n) return
def update_mac_entry(self): """ Add mac rewriting rule for switch->dst_dev via port :param switch: switch where rule will be installed :param dst_dev: next_hop :param port: port which is used towards next_hop :return: """ valid_entries = [] device = TopologyManager.get_device(Configuration.get('name')) for device_name in device.get_device_to_port_mapping(): dev = TopologyManager.get_device(device_name) port = device.get_device_to_port(device_name) Log.debug("Mac:", Configuration.get('name'), "->", device_name, "via", port) entry = TableEntry( match_fields={"standard_metadata.egress_port": int(port)}, action_name="egress.mac_c.set_mac", action_params={ "srcAddr": device.get_mac(), "dstAddr": dev.get_mac() }) TableEntryManager.handle_table_entry( manager=self.table_manager, table_name="egress.mac_c.adjust_mac", table_entry=entry) valid_entries.append(entry.match_fields) Log.debug("Installed Mac rewriting rule for", Configuration.get('name')) # remove possible old entries self.table_manager.remove_invalid_entries( table_name="egress.mac_c.adjust_mac", valid_entries=valid_entries)
def connect_and_arbitrate(self, grpc_port=0, device_id=0): """ Connect and arbitrate to the switch :param grpc_port: grpc port of the p4 switch :param device_id: device id of the p4 switch :return: """ i = Configuration.get('bfr_id') try: # add connection to switch self.__connection = self.__add_switch_connection(name='s{0}'.format(i), address='127.0.0.1:{0}'.format(grpc_port), device_id=device_id) # start packet in thread self.__connection.start_thread() if self.__connection.MasterArbitrationUpdate(): base_mac = int('20:00:00:00:00:00'.translate(None, ":,-"), 16) real_mac = format(base_mac + i, 'x') mac = ":".join(real_mac[i:i + 2] for i in range(0, len(real_mac), 2)) Configuration.set('name', 's{0}'.format(i).encode('utf-8')) Event.trigger("new_switch_connection", name='s{0}'.format(i), device=Switch(name='s{0}'.format(i).encode('utf-8'), ip='20.0.{0}.0'.format(i).encode('utf-8'), mac=mac.encode('utf-8'), bfr_id=i)) Log.info("Arbitration done. Connected to swtich") Event.trigger("arbitration_done") else: Log.error("Master arbitration failed") except Exception as e: Log.error(e)
def __init__(self, grpc_address=None): self.channel = grpc.insecure_channel(grpc_address) self.stub = proto.connection_pb2_grpc.LocalServerStub(self.channel) response = self.stub.Hello( proto.connection_pb2.HelloMessage( ip="127.0.0.1", port=int(Configuration.get('listen_port')))) self.name = response.name.encode('utf-8') Event.trigger('new_switch_connection', name=self.name, device=Switch(name=self.name, ip=response.ip.encode('utf-8'), mac=response.mac.encode('utf-8'), bfr_id=response.bfr_id))
def update_igmp(self, pkt): """ Update port information on ipmc groups """ switch = TopologyManager.get_device(name=Configuration.get('name')) mc_addr = pkt.mc_address.encode('utf-8') src_ip = pkt.src_ip.encode('utf-8') port = switch.get_device_to_port( TopologyManager.get_device_by_ip(ip=src_ip).get_name()) if pkt.type == 0x16: if port not in self.mcgrp_to_port[mc_addr]: self.mcgrp_to_port[mc_addr].append(port) elif pkt.type == 0x17: if port in self.mcgrp_to_port[mc_addr]: self.mcgrp_to_port[mc_addr].remove(port) self.update_mc_table()
def handle_topology_answer(self, *args, **kwargs): """ Handle topology packet :param args: contains the topology packet :return: """ packet = kwargs.get('packet') switch = kwargs.get('switch') pkt = packet pkt = packet.payload Event.trigger("clear_port_down", port=int(pkt.port)) if pkt.device_type != 1: name = "s" + str(pkt.identifier) TopologyManager.add_device(name=name, device=Host(name=name, ip=pkt.ip, mac=pkt.mac)) else: # its a host name = "h" + str(pkt.identifier) TopologyManager.add_device(name=name, device=Host(name=name, ip=pkt.ip, mac=pkt.mac)) if TopologyManager.get_device(name=switch).add_device_to_port( device=name, port=int(pkt.port)): Event.trigger("topology_change", src_device=switch, dst_device=name, port=int(pkt.port)) #Log.info("Pkt in:", pkt.port) topology_packet = proto.connection_pb2.TopologyPacket( ip=pkt.ip, mac=pkt.mac, port=pkt.port, name=name, switch=Configuration.get('name')) Event.trigger("topology_to_controller", pkt=topology_packet)