Exemple #1
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
Exemple #2
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 == 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
Exemple #3
0
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))
Exemple #6
0
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()
Exemple #7
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("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))
Exemple #9
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.")
Exemple #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))
Exemple #11
0
    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)
Exemple #12
0
    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)
Exemple #13
0
    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
Exemple #14
0
    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)
Exemple #15
0
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()
Exemple #16
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")
Exemple #17
0
    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)
Exemple #19
0
    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)
Exemple #20
0
    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
Exemple #21
0
    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)
Exemple #22
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")
Exemple #23
0
    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)
Exemple #24
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()
Exemple #25
0
    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
Exemple #26
0
    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)
Exemple #27
0
    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))
Exemple #29
0
    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)