예제 #1
0
    def update_bier_encap_entry(self, switch=None):
        """
        Add bier encap entry for given prefix
        :param switch: switch where rules should be installed
        :return:
        """

        valid_entries = []

        for mc_addr in GroupManager.get_mc_addresses():
            for domain in GroupManager.get_domains_for_mc_addr(mc_addr):
                domain = int(domain)
                bitstring = BierComputation.compute_bier_header(
                    mc_addr=mc_addr, domain=domain)

                entry = TableEntry(switch=switch,
                                   match_fields={
                                       "hdr.ipv4.dstAddr": mc_addr,
                                   },
                                   action_name="ingress.ipv4_c.add_bier",
                                   action_params={"bs": bitstring})

                if TableEntryManager.handle_table_entry(
                        manager=self.table_manager,
                        table_name="ingress.ipv4_c.encap_ipv4",
                        table_entry=entry):
                    Log.async_debug("Installed encap ipv4 rule for", switch,
                                    mc_addr, bitstring)

                valid_entries.append(entry.match_fields)

        self.table_manager.remove_invalid_entries(
            switch=switch,
            table_name="ingress.ipv4_c.encap_ipv4",
            valid_entries=valid_entries)
예제 #2
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))
예제 #3
0
 def handle_cli_command(arg1, arg2):
     if arg1 == 'describe':
         TableEntryManager.describe(arg2)
     elif arg1 == 'show_tables':
         TableEntryManager.show_tables(arg2)
     else:
         Log.echo("Command not found.")
예제 #4
0
    def trigger(event, *args, **kw):
        """
        Trigger an event. The corresponding handlers will be executed
        :param event: event name
        :param args: arguments without key
        :param kw: arguments with key
        :return:
        """

        if not Event.activated:
            return

        callbacks = list(Event.events.get(event, []))

        if not callbacks:
            return False

        Event.lock.acquire()
        for cb in callbacks:
            try:
                cb(*args, **kw)
            except Exception as e:
                Log.error(str((traceback.format_exc())))

        # release this method
        Event.lock.release()
        return True
예제 #5
0
    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
예제 #6
0
    def setupProtection(self, data=None):
        self.data = data
        Log.info("Setup port protection")
	dt = DevTarget_t(0, hex_to_i16(0xFFFF))

        p = port_down_packet()
        pktlen = self.port_pkt_len = len(p)

        self.tc.conn_mgr.pktgen_write_pkt_buffer(self.tc.hdl, dt, 0, pktlen, str(p))

	offset = 0
        # enable on all pipes

        for pipe in range(0, self.pal.pal_num_pipes_get(0)):
	    port = (pipe << 7 | 68)
            self.tc.conn_mgr.pktgen_enable(self.tc.hdl, 0, (pipe << 7 | 68))
	    Log.debug("Enable pkt gen on port", port)

        config = PktGenAppCfg_t(trigger_type=PktGenTriggerType_t.PORT_DOWN,
                                timer=0,
                                src_port=68,
                                buffer_offset=offset,
                                length=pktlen)
        self.tc.conn_mgr.pktgen_cfg_app(self.tc.hdl, dt, 0, config)
        self.tc.conn_mgr.pktgen_app_enable(self.tc.hdl, dt, 0)
        offset=pktlen
예제 #7
0
    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.")
예제 #8
0
        def accept(event=None):
            """
            This method is called wenn enter is hit
            :param event:
            :return:
            """
            user_input = input_field.text

            # get user_input, split on blank, user_input[0] is command, user_input[i>0] are arguments
            user_input = user_input.split(" ")

            if user_input[0] in CLI.prompt_to_execution:
                command = CLI.prompt_to_execution.get(user_input[0],
                                                      lambda: 'Invalid')[0]
                args = CLI.prompt_to_execution.get(user_input[0],
                                                   lambda: 'Invalid')[1]

                # currently only up to 2 arguments are supported (for send command in host cli)
                if len(user_input) == 2:
                    command(user_input[1])
                elif len(user_input) == 3:
                    command(user_input[1], user_input[2])
                elif args is not None:
                    command(args)
                else:
                    command()
            else:
                Log.error("Command not found")

            input_field.text = ''
예제 #9
0
 def purge(self):
     """
     Delete all current table entries
     """
     [self.delete_table_entry(table_name=e[0], entry=e[1]) for e in self.entries]
     Log.info("Entries purged")
     self.entries = []
예제 #10
0
    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))
예제 #11
0
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("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)

    pd = PDSetup()
    mc = MulticastController(pd=pd, base=controller)

    # start connection procedure
    init_switches(controller=controller, topology_controller=topology, pd=pd)

    bier = BierController(controller)

    # 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)
예제 #12
0
    def update_ipv4_entries(self, switch=None):
        """
        Update ipv4 entries based on shortest path on switch
        :param switch: switch where ipv4 entries will be installed
        :return:
        """

        paths = TopologyManager.get_paths(domain_id=0)
        valid_entries = []

        cur_dev = TopologyManager.get_device(switch)

        for dst in [d for d in paths.get(switch, {})
                    if d != switch]:  # don't check path from i->i
            # get the next_hop towards the destination along the shortest path
            dst_dev = TopologyManager.get_device(dst)
            next_hop = TopologyManager.get_next_hop(start_node=switch,
                                                    destination_node=dst,
                                                    domain_id=0)

            port = cur_dev.get_device_to_port(next_hop.get_name())

            entry = TableEntry(
                switch=switch,
                match_fields={
                    "hdr.ipv4.dstAddr": (str(dst_dev.get_ip()), 32),
                    "meta.ports.status":
                    (BierComputation.id_to_bitstring(id=int(port)),
                     BierComputation.id_to_bitstring(id=int(port)))
                },
                action_name="ingress.ipv4_c.forward",
                action_params={"port": int(port)},
                priority=1)

            if TableEntryManager.handle_table_entry(
                    manager=self.table_manager,
                    table_name="ingress.ipv4_c.ipv4",
                    table_entry=entry):
                Log.async_debug("Installed IPv4 forwarding rule for", switch,
                                "->", dst)

            valid_entries.append(entry.match_fields)

        # Add decap entry
        entry = TableEntry(
            switch=cur_dev.get_name(),
            match_fields={"hdr.ipv4.dstAddr": (str(cur_dev.get_ip()), 32)},
            action_name="ingress.ipv4_c.decap",
            priority=1)

        if TableEntryManager.handle_table_entry(
                manager=self.table_manager,
                table_name="ingress.ipv4_c.ipv4",
                table_entry=entry):
            Log.async_debug("Installed IPv4 decap rule for", switch)

        valid_entries.append(entry.match_fields)

        return valid_entries
예제 #13
0
    def addTableEntry(self, tableEntry=None):
        """
        Add a table entry to the switch
        """
        response = self.stub.AddEntry(tableEntry)

        if response.code == 0:
            Log.error("Error for entry:", tableEntry, "on switch", self.name)
예제 #14
0
 def start(self):
     """
     Start grpc server
     This grpc server will be used to connect the local controller with the global
     """
     proto.connection_pb2_grpc.add_GlobalServerServicer_to_server(GlobalServer(), self.server)
     self.server.add_insecure_port('[::]:' + str(self.listen_port))
     Log.async_info("Start GRPC Server on port", self.listen_port)
     self.server.start()
예제 #15
0
 def start(self):
     """
     Start grpc server
     This grpc server will be used for the direction global-controller ----> local-controller
     """
     proto.connection_pb2_grpc.add_LocalServerServicer_to_server(LocalServer(), self.server)
     self.server.add_insecure_port('0.0.0.0:' + str(self.listen_port))
     Log.info("Start GRPC Server on port", self.listen_port)
     self.server.start()
예제 #16
0
    def __init__(self, ip=None, port=0):
        self.channel = grpc.insecure_channel(ip + ":" + str(port))
        self.stub = proto.connection_pb2_grpc.GlobalServerStub(self.channel)

        reponse = self.stub.CheckConnection(proto.connection_pb2.Empty())
        Log.info("Global connection to", ip + ":" + str(port))

        # remove possible old connection when a new global connection is initialized
        Event.on('global_connection', self.close)
예제 #17
0
    def removeTableEntry(self, tableEntry=None):
        """
        Remove a table entry from the switch
        """
        response = self.stub.RemoveEntry(tableEntry)

        if response.code == 0:
            Log.error("Error while removing entry:", tableEntry, "on switch",
                      self.name)
예제 #18
0
 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")
예제 #19
0
    def stream_iterator(self):
        while True:
            try:
                p = self.stream_requests.get()
            except Exception as e:
                Log.error("Error in stream_iterator", e)

            if p is None:
                break
            yield p
예제 #20
0
    def describe(manager):
        data = PrettyTable()
        manager = TableEntryManager.get(manager=manager)

        data.field_names = ["Table name", "# table entries"]

        for table in manager.tables.keys():
            t = manager.get_table_entries(table_name=table)
            data.add_row([table, len(t)])

        Log.echo(data)
예제 #21
0
    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")
예제 #22
0
    def set_forwarding_pipeline_config(self):
        """
        Set forwarding pipeline on the switch based on p4info file
        :return:
        """
        try:
            self.__connection.SetForwardingPipelineConfig(p4info=self.__p4info_helper.p4info, prog_name=self.prog_name, bin_path=self.bin_path, cxt_json_path=self.cxt_json_path)
            Event.trigger("switch_arbitrated")
        except Exception as e:
                Log.error("Error in forwarding pipeline", e)

        Log.info("Forwarding pipeline set.")
예제 #23
0
    def describe():
        data = PrettyTable()

        data.field_names = ["MC Address", "Subscribed BFRs", "Domains"]

        for mc_addr in GroupManager.get_mc_addresses():
            data.add_row([
                mc_addr,
                GroupManager.get_bfr_by_mc_addr(mc_addr),
                GroupManager.get_domains_for_mc_addr(mc_addr)
            ])

        Log.echo(data)
예제 #24
0
    def PortMessage(self, request, context):
        """
        This method receives a port message
        """
        Log.async_info("Got port message")
        Log.async_debug(request)

        # this event is not catched yet
        # for demonstration purpose, the topology doesn't get updated
        # on a link failure
        Event.trigger("port_message", message=request)

        return proto.connection_pb2.Status(code=1, message="Accepted")
예제 #25
0
    def set_forwarding_pipeline_config(self):
        """
        Set forwarding pipeline on the switch based on p4info file
        :return:
        """
        try:
            self.__connection.SetForwardingPipelineConfig(p4info=self.__p4info_helper.p4info,
                                                          bmv2_json_file_path=self.__bmv2_file_path.encode())
            Event.trigger("switch_arbitrated")
        except Exception as e:
                Log.error("Error in forwarding pipeline", e)

        Log.info("Forwarding pipeline set.")
예제 #26
0
    def handle_packet_in(pkt):
        switch = pkt.switch.encode('utf-8')
        mc_addr = pkt.mc_address.encode('utf-8')
        src_ip = pkt.src_ip.encode('utf-8')

        if pkt.type == 0x16:
            GroupManager.add_to_group(switch, src_ip, mc_addr)
        elif pkt.type == 0x17:
            GroupManager.remove_from_group(switch, src_ip, mc_addr)

        Event.trigger("group_update")

        Log.event("Got igmp packet with type", hex(pkt.type), "and src",
                  src_ip, "for group", mc_addr, "from", switch)
예제 #27
0
    def list_commands():
        """
        List all commands with info text for help output
        :return:
        """

        data = PrettyTable()

        data.field_names = ["Command", "Info"]

        for k, v in sorted(CLI.prompt_to_info.iteritems()):
            data.add_row([k, v])

        Log.echo(data)
예제 #28
0
 def remove_from_group(switch, member, group):
     """
     Removes a host from a given multicast group
     :param switch: switch which got the unsub message
     :param member: host which unsubscribes
     :param group: multicast group
     :return:
     """
     try:
         GroupManager.group_to_member.get(switch, {}).get(group,
                                                          []).remove(member)
     except ValueError as e:
         Log.error("try to remove", member, "from", switch, "and group",
                   group, e, GroupManager.group_to_member)
         pass
예제 #29
0
    def add_flood_node(self):
        """
        Add mc mc nodes
        """

        p = Popen([self.cli, '--thrift-port',
                   str(self.thrift_port)],
                  stdout=PIPE,
                  stdin=PIPE,
                  stderr=STDOUT)
        out, err = p.communicate(
            input="mc_node_create 0 " +
            " ".join(map(str, list(range(self.max_port)))))

        if err:
            Log.error(err)
예제 #30
0
    def show_tables(manager):
        manager = TableEntryManager.get(manager=manager)
        prettyTables = []

        for table in manager.tables.keys():
            t = manager.get_table_entries(table_name=table)
            data = PrettyTable()

            data.field_names = ["Switch", "Table Name", "Match fields", "Action name", "Action params", "Priority"]
            for entry in t:
                data.add_row([entry.switch, table, entry.match_fields, entry.action_name, entry.action_params, entry.priority])

            prettyTables.append(data)

        for table in prettyTables:
            Log.echo(table)