Ejemplo n.º 1
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)
Ejemplo n.º 2
0
    def write_port_entry(self, port_string=None):
        entry = TableEntry(action_name="ingress.port_c.set_port_status",
                           action_params={"livePorts": port_string})

        TableEntryManager.handle_table_entry(manager=self.table_manager,
                                             table_name="ingress.port_c.port_status",
                                             table_entry=entry)
Ejemplo n.º 3
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
Ejemplo n.º 4
0
    def __init__(self, base):
        self._baseController = base
        Event.on("topology_change",
                 self.update)  # update bier tables when topology changes

        # this controller manages the following tables
        self.table_manager = TableEntryManager(controller=base,
                                               name="BierController")
        self.table_manager.init_table("ingress.bier_c.bift")
Ejemplo n.º 5
0
class PortController:
    """
    This module monitors the port status of the switch.
    """
    def __init__(self, controller=None):
        """
        Initialize PortController with base controller and notification_socket
        :param controller: BaseController which manages SwitchConnection
        :param notification_socket: notification_socket for nanomsg
        """
        # this may be removed later when registers are used
        self.table_manager = TableEntryManager(controller=controller,
                                               name="PortController")

        self.table_manager.init_table(
            table_name="SwitchIngress.port_c.port_status")

        # save port status received by nanomsg message, default up
        self.port_status = defaultdict(lambda: 1)

        Event.on("port_down", self.updatePorts)
        #Event.on("topology_change", self.update)

    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 write_port_entry(self, port_string=None):
        entry = TableEntry(action_name="SwitchIngress.port_c.set_port_status",
                           action_params={"livePorts": port_string},
                           priority=1)

        TableEntryManager.handle_table_entry(
            manager=self.table_manager,
            table_name="SwitchIngress.port_c.port_status",
            table_entry=entry)

    def update(self, **kwargs):
        device = TopologyManager.get_device(Configuration.get('name'))
        live_ports = 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)
Ejemplo n.º 6
0
    def __init__(self, base):
        """
        Init Maccontroller with base controller
        :param base:
        """

        # table manager
        self.table_manager = TableEntryManager(controller=base,
                                               name="MacController")
        self.table_manager.init_table("egress.mac_c.adjust_mac")

        Event.on("topology_change", self.update)
Ejemplo n.º 7
0
    def __init__(self, thrift_port=9090, base=None):
        self.thrift_port = thrift_port
        self.cli = "simple_switch_CLI"

        self.max_port = 8

        self.mcgrp_to_port = defaultdict(list)

        Event.on("igmp_packet_to_controller", self.update_igmp)

        self.table_manager = TableEntryManager(controller=base,
                                               name="GroupController")
        self.table_manager.init_table("ingress.ipv4_c.ipv4_mc")
Ejemplo n.º 8
0
    def __init__(self, base):
        """
        Init IPv4Controller with base controller and add IPv4 cli commands
        :param base:
        """
        self._baseController = base

        self.table_manager = TableEntryManager(controller=base,
                                               name="IPv4Controller")
        self.table_manager.init_table("ingress.ipv4_c.ipv4")
        self.table_manager.init_table("ingress.ipv4_c.encap_ipv4")

        Event.on("group_update", self.update_based_on_group)

        Event.on("topology_change", self.update_ipv4_rules)
Ejemplo n.º 9
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)
Ejemplo n.º 10
0
    def __init__(self, controller=None):
        """
        Initialize PortController with base controller and notification_socket
        :param controller: BaseController which manages SwitchConnection
        :param notification_socket: notification_socket for nanomsg
        """
        # this may be removed later when registers are used
        self.table_manager = TableEntryManager(controller=controller,
                                               name="PortController")

        self.table_manager.init_table(
            table_name="SwitchIngress.port_c.port_status")

        # save port status received by nanomsg message, default up
        self.port_status = defaultdict(lambda: 1)

        Event.on("port_down", self.updatePorts)
Ejemplo n.º 11
0
    def __init__(self, base):
        """
        Init Tunnelcontroller with base controller and add cli commands
        :param base: basecontroller
        :param type: Sets bier or bier-te type
        """
        self._baseController = base
        Event.on("group_update", self.update_based_on_group)
        Event.on("topology_change", self.update_based_on_topology)

        # add decap rules for devices
        Event.on("switch_connected", self.add_ipv4_decap_rule)

        self.table_manager = TableEntryManager(controller=base,
                                               name="TunnelController")
        self.table_manager.init_table("ingress.tunnel_c.decap_bier")
        self.table_manager.init_table("ingress.tunnel_c.decap_ipv4")
        self.table_manager.init_table("egress.tunnel_c.encap_ipv4")
Ejemplo n.º 12
0
    def __init__(self, base):
        """
        Init BierController with base controller

        Args:
            base (libs.core.BaseController): Base controller
        """

        # table manager
        self.table_manager = TableEntryManager(controller=base,
                                               name="BierController")
        self.table_manager.init_table("ingress.reset_clone")

        entry = TableEntry(match_fields={"hdr.bier_md.bs": 0},
                           action_name="ingress.nop")

        TableEntryManager.handle_table_entry(manager=self.table_manager,
                                             table_name="ingress.reset_clone",
                                             table_entry=entry)
Ejemplo n.º 13
0
    def __init__(self, controller=None, notification_socket=None):
        """
        Initialize PortController with base controller and notification_socket
        :param controller: BaseController which manages SwitchConnection
        :param notification_socket: notification_socket for nanomsg
        """
        # this may be removed later when registers are used
        self.table_manager = TableEntryManager(controller=controller, name="PortController")

        self.table_manager.init_table(table_name="ingress.port_c.port_status")

        # save port status received by nanomsg message, default up
        self.port_status = defaultdict(lambda: 1)

        self.sub = nnpy.Socket(nnpy.AF_SP, nnpy.SUB)
        self.sub.connect(notification_socket)
        self.sub.setsockopt(nnpy.SUB, nnpy.SUB_SUBSCRIBE, '')

        Event.on("topology_change", self.update)
Ejemplo n.º 14
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
Ejemplo n.º 15
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)
Ejemplo n.º 16
0
    def update_mc_table(self):
        valid_entries = []
        id = 2

        for mcgrp in self.mcgrp_to_port:
            if self.mcgrp_to_port[mcgrp]:
                self.update_mc_group(id=id, ports=self.mcgrp_to_port[mcgrp])

                entry = TableEntry(match_fields={"hdr.ipv4.dstAddr": mcgrp},
                                   action_name="ingress.ipv4_c.set_mc_grp",
                                   action_params={"grp": id})

                id += 1

                TableEntryManager.handle_table_entry(
                    manager=self.table_manager,
                    table_name="ingress.ipv4_c.ipv4_mc",
                    table_entry=entry)

                valid_entries.append(entry.match_fields)

        self.table_manager.remove_invalid_entries(
            table_name="ingress.ipv4_c.ipv4_mc", valid_entries=valid_entries)
Ejemplo n.º 17
0
class BierController(object):
    """
    This module implements an MacController and
    sets rewrite rules for layer 2
    """
    def __init__(self, base):
        """
        Init BierController with base controller

        Args:
            base (libs.core.BaseController): Base controller
        """

        # table manager
        self.table_manager = TableEntryManager(controller=base,
                                               name="BierController")
        self.table_manager.init_table("ingress.reset_clone")

        entry = TableEntry(match_fields={"hdr.bier_md.bs": 0},
                           action_name="ingress.nop")

        TableEntryManager.handle_table_entry(manager=self.table_manager,
                                             table_name="ingress.reset_clone",
                                             table_entry=entry)
Ejemplo n.º 18
0
    def add_ipv4_decap_rule(self, *args, **kwargs):
        """
        Adds an ipv4 decap rule for the switch
        This event is triggered when a switch is arbitrated
        :return:
        """
        device = TopologyManager.get_device(kwargs.get('name'))
        entry = TableEntry(switch=device.get_name(),
                           match_fields={"hdr.ipv4.dstAddr": device.get_ip()},
                           action_name="ingress.tunnel_c.ipv4_decap")

        if TableEntryManager.handle_table_entry(
                manager=self.table_manager,
                table_name="ingress.tunnel_c.decap_ipv4",
                table_entry=entry):
            Log.async_debug("Ipv4 decap rule installed for",
                            kwargs.get('name'))
Ejemplo n.º 19
0
    def update_bier_decap_rule(self, switch=None):
        """
        Updates the bier decap rules based on the current topology
        :param switch: switch where decap rules should be installed
        :return:
        """
        valid_entries = []

        # bier decap rules
        for domain in TopologyManager.get_domain_for_device(switch):
            domain = int(domain)
            bfr_id = BierComputation.id_to_bitstring(
                TopologyManager.get_device(switch).get_bfr_id(domain))

            entry = TableEntry(switch=switch,
                               match_fields={
                                   "hdr.bier[0].BitString": (bfr_id, bfr_id),
                                   "hdr.bier[0].Domain": domain
                               },
                               action_name="ingress.tunnel_c.bier_decap",
                               action_params={"decapBit": bfr_id},
                               priority=1)

            if TableEntryManager.handle_table_entry(
                    manager=self.table_manager,
                    table_name="ingress.tunnel_c.decap_bier",
                    table_entry=entry):
                Log.async_debug("BIER decap rule updated on", switch,
                                "for domain", domain)

            valid_entries.append(entry.match_fields)

        self.table_manager.remove_invalid_entries(
            switch=switch,
            table_name="ingress.tunnel_c.decap_bier",
            valid_entries=valid_entries)
Ejemplo n.º 20
0
class MacController(object):
    def __init__(self, base):
        """
        Init Maccontroller with base controller
        :param base:
        """

        # table manager
        self.table_manager = TableEntryManager(controller=base,
                                               name="MacController")
        self.table_manager.init_table("egress.mac_c.adjust_mac")

        Event.on("topology_change", self.update)

    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)

    #############################################################
    #                   Event Listener                          #
    #############################################################

    def update(self, *args, **kwargs):
        """
        Update mac entries
        triggered by event
        :return:
        """

        self.update_mac_entry()
Ejemplo n.º 21
0
class BierController:
    """
    This class implements an bier controller which sets the
    bier forwarding entries (inside a domain) and the bier domain forwarding entries
    (between domains)
    It does not set the tunnel entry to add the bier header, this is done by the Tunnel Controller
    """
    def __init__(self, base):
        self._baseController = base
        Event.on("topology_change",
                 self.update)  # update bier tables when topology changes

        # this controller manages the following tables
        self.table_manager = TableEntryManager(controller=base,
                                               name="BierController")
        self.table_manager.init_table("ingress.bier_c.bift")

    def update_frr_node_protection(self,
                                   switch=None,
                                   domain=None,
                                   port=None,
                                   to_id=None,
                                   fbm=None,
                                   next_hop=None):
        """
        Implements BIER-FRR node protection
        """

        switch_device = TopologyManager.get_device(name=switch)
        remainingBits = BierComputation.id_to_bitstring(id=to_id)

        # get bift for failed nh
        bift_nh = BierComputation.build_bift(switch=next_hop, domain=domain)

        next_hop_device = TopologyManager.get_device(name=next_hop)

        # send a copy of the packet to the broken node in case its only a link failure
        if next_hop_device.get_bfr_id(domain) == to_id:
            bift_nh[to_id] = [BierComputation.id_to_bitstring(to_id), next_hop]

        if to_id not in bift_nh:
            return

        # the backup fbm is the intersection of the old fbm and the fbm of the nh for this entry
        new_fbm = fbm & bift_nh.get(to_id)[0]

        # tunnel node is nh of bift from failed nh for this entry
        tunnel_node = TopologyManager.get_device(name=bift_nh.get(to_id)[1])

        src_ip = switch_device.get_ip()
        dst_ip = tunnel_node.get_ip()

        entry = TableEntry(switch=switch,
                           match_fields={
                               "meta.bier_md.remainingBits":
                               (remainingBits, remainingBits),
                               "meta.ports.status":
                               (0, BierComputation.id_to_bitstring(port))
                           },
                           action_name="ingress.bier_c.forward_encap",
                           action_params={
                               "fbm": new_fbm,
                               "srcAddr": src_ip,
                               "dstAddr": dst_ip
                           },
                           priority=1)

        return entry

    def update_frr_link_protection(self,
                                   switch=None,
                                   domain=None,
                                   port=None,
                                   to_id=None,
                                   fbm=None,
                                   next_hop=None):
        """
        In BIER-FRR link protection, just use an IP tunnel to the NH behind the failed link
        """

        switch_device = TopologyManager.get_device(name=switch)
        src_ip = switch_device.get_ip()
        dst_ip = TopologyManager.get_device(name=next_hop).get_ip()
        remainingBits = BierComputation.id_to_bitstring(to_id)

        entry = TableEntry(switch=switch,
                           match_fields={
                               "meta.bier_md.remainingBits":
                               (remainingBits, remainingBits),
                               "meta.ports.status":
                               (0, BierComputation.id_to_bitstring(port))
                           },
                           action_name="ingress.bier_c.forward_encap",
                           action_params={
                               "fbm": fbm,
                               "srcAddr": src_ip,
                               "dstAddr": dst_ip
                           },
                           priority=1)

        return entry

    def update_bier_forwarding_entries(self, switch=None):
        """
        Adds bier forwarding entry for given switch in specifc domain
        :param switch:
        :param domain: domain identifier
        :return:
        """

        table_name = "ingress.bier_c.bift"

        valid_entries = []

        for domain in TopologyManager.get_domain_for_device(switch):
            domain = int(domain)
            bift = BierComputation.build_bift(switch=switch, domain=domain)

            for entry in bift:
                bit_string = BierComputation.id_to_bitstring(id=entry)

                out_port = TopologyManager.get_device(
                    switch).get_device_to_port(bift.get(entry)[1])

                # generate default bier entry
                e = TableEntry(
                    switch=switch,
                    match_fields={
                        "meta.bier_md.remainingBits": (bit_string, bit_string),
                        "meta.ports.status":
                        (BierComputation.id_to_bitstring(id=out_port),
                         BierComputation.id_to_bitstring(id=out_port))
                    },
                    action_name="ingress.bier_c.forward",
                    action_params={
                        "fbm": bift.get(entry)[0],
                        "port": out_port
                    },
                    priority=1)

                if TableEntryManager.handle_table_entry(
                        manager=self.table_manager,
                        table_name=table_name,
                        table_entry=e):
                    Log.async_debug("Installed BIER rule for", switch,
                                    "to bfr-id", entry)

                valid_entries.append(e.match_fields)

                # generate bier-frr link protection entry for this switch and bfr
                if Configuration.get('protection') == 'Link':
                    frr_entry = self.update_frr_link_protection(
                        switch=switch,
                        domain=domain,
                        port=out_port,
                        to_id=entry,
                        fbm=bift.get(entry)[0],
                        next_hop=bift.get(entry)[1])

                    if TableEntryManager.handle_table_entry(
                            manager=self.table_manager,
                            table_name=table_name,
                            table_entry=frr_entry):
                        Log.async_debug(
                            "Installed BIER-FRR link protection rule for",
                            switch)

                    valid_entries.append(frr_entry.match_fields)

                # generate bier-frr node protection entry for this switch and bfr
                if Configuration.get('protection') == 'Node':
                    frr_entry = self.update_frr_node_protection(
                        switch=switch,
                        domain=domain,
                        port=out_port,
                        to_id=entry,
                        fbm=bift.get(entry)[0],
                        next_hop=bift.get(entry)[1])

                    if frr_entry:
                        if TableEntryManager.handle_table_entry(
                                manager=self.table_manager,
                                table_name=table_name,
                                table_entry=frr_entry):
                            Log.async_debug(
                                "Installed BIER-FRR node protection rule for",
                                switch, "to", entry)

                        valid_entries.append(frr_entry.match_fields)

        bfr_id = BierComputation.id_to_bitstring(
            TopologyManager.get_device(switch).get_bfr_id(0))
        # Add decap entry
        entry = TableEntry(
            switch=switch,
            match_fields={"meta.bier_md.remainingBits": (bfr_id, bfr_id)},
            action_name="ingress.bier_c.decap",
            action_params={"decapBit": bfr_id},
            priority=1)

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

        valid_entries.append(entry.match_fields)

        self.table_manager.remove_invalid_entries(switch=switch,
                                                  table_name=table_name,
                                                  valid_entries=valid_entries)

    def load_static_rules(self):
        """
        Load static rules from json file specified in config
        """
        pass

    #############################################################
    #                   Event Listener                          #
    #############################################################

    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.")
Ejemplo n.º 22
0
    def update_bier_forwarding_entries(self, switch=None):
        """
        Adds bier forwarding entry for given switch in specifc domain
        :param switch:
        :param domain: domain identifier
        :return:
        """

        table_name = "ingress.bier_c.bift"

        valid_entries = []

        for domain in TopologyManager.get_domain_for_device(switch):
            domain = int(domain)
            bift = BierComputation.build_bift(switch=switch, domain=domain)

            for entry in bift:
                bit_string = BierComputation.id_to_bitstring(id=entry)

                out_port = TopologyManager.get_device(
                    switch).get_device_to_port(bift.get(entry)[1])

                # generate default bier entry
                e = TableEntry(
                    switch=switch,
                    match_fields={
                        "meta.bier_md.remainingBits": (bit_string, bit_string),
                        "meta.ports.status":
                        (BierComputation.id_to_bitstring(id=out_port),
                         BierComputation.id_to_bitstring(id=out_port))
                    },
                    action_name="ingress.bier_c.forward",
                    action_params={
                        "fbm": bift.get(entry)[0],
                        "port": out_port
                    },
                    priority=1)

                if TableEntryManager.handle_table_entry(
                        manager=self.table_manager,
                        table_name=table_name,
                        table_entry=e):
                    Log.async_debug("Installed BIER rule for", switch,
                                    "to bfr-id", entry)

                valid_entries.append(e.match_fields)

                # generate bier-frr link protection entry for this switch and bfr
                if Configuration.get('protection') == 'Link':
                    frr_entry = self.update_frr_link_protection(
                        switch=switch,
                        domain=domain,
                        port=out_port,
                        to_id=entry,
                        fbm=bift.get(entry)[0],
                        next_hop=bift.get(entry)[1])

                    if TableEntryManager.handle_table_entry(
                            manager=self.table_manager,
                            table_name=table_name,
                            table_entry=frr_entry):
                        Log.async_debug(
                            "Installed BIER-FRR link protection rule for",
                            switch)

                    valid_entries.append(frr_entry.match_fields)

                # generate bier-frr node protection entry for this switch and bfr
                if Configuration.get('protection') == 'Node':
                    frr_entry = self.update_frr_node_protection(
                        switch=switch,
                        domain=domain,
                        port=out_port,
                        to_id=entry,
                        fbm=bift.get(entry)[0],
                        next_hop=bift.get(entry)[1])

                    if frr_entry:
                        if TableEntryManager.handle_table_entry(
                                manager=self.table_manager,
                                table_name=table_name,
                                table_entry=frr_entry):
                            Log.async_debug(
                                "Installed BIER-FRR node protection rule for",
                                switch, "to", entry)

                        valid_entries.append(frr_entry.match_fields)

        bfr_id = BierComputation.id_to_bitstring(
            TopologyManager.get_device(switch).get_bfr_id(0))
        # Add decap entry
        entry = TableEntry(
            switch=switch,
            match_fields={"meta.bier_md.remainingBits": (bfr_id, bfr_id)},
            action_name="ingress.bier_c.decap",
            action_params={"decapBit": bfr_id},
            priority=1)

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

        valid_entries.append(entry.match_fields)

        self.table_manager.remove_invalid_entries(switch=switch,
                                                  table_name=table_name,
                                                  valid_entries=valid_entries)
Ejemplo n.º 23
0
class IPv4Controller(object):
    def __init__(self, base):
        """
        Init IPv4Controller with base controller and add IPv4 cli commands
        :param base:
        """
        self._baseController = base

        self.table_manager = TableEntryManager(controller=base,
                                               name="IPv4Controller")
        self.table_manager.init_table("ingress.ipv4_c.ipv4")
        self.table_manager.init_table("ingress.ipv4_c.encap_ipv4")

        Event.on("group_update", self.update_based_on_group)

        Event.on("topology_change", self.update_ipv4_rules)

    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

    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)

    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

    #############################################################
    #                   Event Listener                          #
    #############################################################

    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_ipv4_rules(self, *args, **kwargs):
        """
        Update ipv4 forwarding entries
        triggered by event
        :return:
        """

        if "port_update" in kwargs:
            try:
                d = Configuration.get('update')

                if "ipv4" not in d:
                    return

                if "sleep" in kwargs:
                    time.sleep(kwargs.get("sleep"))

            except ConfigurationNotFound:
                pass

        entries = defaultdict(list)

        for switch in self._baseController.get_connections():
            entries[switch].extend(self.update_ipv4_entries(switch=switch))

        static_rules = self.load_static_rules()

        for switch in entries:
            v_entries = entries.get(switch)
            v_entries.extend(static_rules[switch])
            self.table_manager.remove_invalid_entries(
                switch=switch,
                table_name="ingress.ipv4_c.ipv4",
                valid_entries=v_entries)

        Log.async_info("IP rules update.")
Ejemplo n.º 24
0
class PortController:
    def __init__(self, controller=None, notification_socket=None):
        """
        Initialize PortController with base controller and notification_socket
        :param controller: BaseController which manages SwitchConnection
        :param notification_socket: notification_socket for nanomsg
        """
        # this may be removed later when registers are used
        self.table_manager = TableEntryManager(controller=controller, name="PortController")

        self.table_manager.init_table(table_name="ingress.port_c.port_status")

        # save port status received by nanomsg message, default up
        self.port_status = defaultdict(lambda: 1)

        self.sub = nnpy.Socket(nnpy.AF_SP, nnpy.SUB)
        self.sub.connect(notification_socket)
        self.sub.setsockopt(nnpy.SUB, nnpy.SUB_SUBSCRIBE, '')

        Event.on("topology_change", self.update)

    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 write_port_entry(self, port_string=None):
        entry = TableEntry(action_name="ingress.port_c.set_port_status",
                           action_params={"livePorts": port_string})

        TableEntryManager.handle_table_entry(manager=self.table_manager,
                                             table_name="ingress.port_c.port_status",
                                             table_entry=entry)

    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)
Ejemplo n.º 25
0
class TunnelController(object):
    def __init__(self, base):
        """
        Init Tunnelcontroller with base controller and add cli commands
        :param base: basecontroller
        :param type: Sets bier or bier-te type
        """
        self._baseController = base
        Event.on("group_update", self.update_based_on_group)
        Event.on("topology_change", self.update_based_on_topology)

        # add decap rules for devices
        Event.on("switch_connected", self.add_ipv4_decap_rule)

        self.table_manager = TableEntryManager(controller=base,
                                               name="TunnelController")
        self.table_manager.init_table("ingress.tunnel_c.decap_bier")
        self.table_manager.init_table("ingress.tunnel_c.decap_ipv4")
        self.table_manager.init_table("egress.tunnel_c.encap_ipv4")

    ##########################################################
    #                                                        #
    #               BIER encap /vdecap section               #
    #                                                        #
    ##########################################################

    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)
                type = 0xBB00

                entry = TableEntry(switch=switch,
                                   match_fields={
                                       "hdr.ipv4.dstAddr": mc_addr,
                                   },
                                   action_name="egress.tunnel_c.add_bier",
                                   action_params={
                                       "bs": bitstring,
                                       "etherType": type,
                                       "domain": domain
                                   })

                if TableEntryManager.handle_table_entry(
                        manager=self.table_manager,
                        table_name="egress.tunnel_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="egress.tunnel_c.encap_ipv4",
            valid_entries=valid_entries)

    def update_bier_decap_rule(self, switch=None):
        """
        Updates the bier decap rules based on the current topology
        :param switch: switch where decap rules should be installed
        :return:
        """
        valid_entries = []

        # bier decap rules
        for domain in TopologyManager.get_domain_for_device(switch):
            domain = int(domain)
            bfr_id = BierComputation.id_to_bitstring(
                TopologyManager.get_device(switch).get_bfr_id(domain))

            entry = TableEntry(switch=switch,
                               match_fields={
                                   "hdr.bier[0].BitString": (bfr_id, bfr_id),
                                   "hdr.bier[0].Domain": domain
                               },
                               action_name="ingress.tunnel_c.bier_decap",
                               action_params={"decapBit": bfr_id},
                               priority=1)

            if TableEntryManager.handle_table_entry(
                    manager=self.table_manager,
                    table_name="ingress.tunnel_c.decap_bier",
                    table_entry=entry):
                Log.async_debug("BIER decap rule updated on", switch,
                                "for domain", domain)

            valid_entries.append(entry.match_fields)

        self.table_manager.remove_invalid_entries(
            switch=switch,
            table_name="ingress.tunnel_c.decap_bier",
            valid_entries=valid_entries)

    #############################################################
    #                   Event Listener                          #
    #############################################################

    def add_ipv4_decap_rule(self, *args, **kwargs):
        """
        Adds an ipv4 decap rule for the switch
        This event is triggered when a switch is arbitrated
        :return:
        """
        device = TopologyManager.get_device(kwargs.get('name'))
        entry = TableEntry(switch=device.get_name(),
                           match_fields={"hdr.ipv4.dstAddr": device.get_ip()},
                           action_name="ingress.tunnel_c.ipv4_decap")

        if TableEntryManager.handle_table_entry(
                manager=self.table_manager,
                table_name="ingress.tunnel_c.decap_ipv4",
                table_entry=entry):
            Log.async_debug("Ipv4 decap rule installed for",
                            kwargs.get('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_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"])
Ejemplo n.º 26
0
class GroupController(object):
    def __init__(self, thrift_port=9090, base=None):
        self.thrift_port = thrift_port
        self.cli = "simple_switch_CLI"

        self.max_port = 8

        self.mcgrp_to_port = defaultdict(list)

        Event.on("igmp_packet_to_controller", self.update_igmp)

        self.table_manager = TableEntryManager(controller=base,
                                               name="GroupController")
        self.table_manager.init_table("ingress.ipv4_c.ipv4_mc")

    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)

    def init_flood_group(self):
        """
        This method initializes the multicast group that is responsible for flooding
        """
        p = Popen([self.cli, '--thrift-port',
                   str(self.thrift_port)],
                  stdout=PIPE,
                  stdin=PIPE,
                  stderr=STDOUT)

        # multicast group 1 is flood group
        out, err = p.communicate(input="mc_mgrp_create 1")

        if err:
            Log.error(err)

        p = Popen([self.cli, '--thrift-port',
                   str(self.thrift_port)],
                  stdout=PIPE,
                  stdin=PIPE,
                  stderr=STDOUT)
        out, err = p.communicate(input="mc_node_associate 1 0")
        if err:
            Log.error(err)

        Log.info("Initialize flood group")

    def update_mc_group(self, id=2, ports=None):

        ################################################################################
        # destory old mc grp
        ################################################################################
        p = Popen([self.cli, '--thrift-port',
                   str(self.thrift_port)],
                  stdout=PIPE,
                  stdin=PIPE,
                  stderr=STDOUT)
        out, err = p.communicate(input="mc_mgrp_destroy " + str(id))

        ################################################################################
        # create new mc grp
        ################################################################################
        p = Popen([self.cli, '--thrift-port',
                   str(self.thrift_port)],
                  stdout=PIPE,
                  stdin=PIPE,
                  stderr=STDOUT)
        out, err = p.communicate(input="mc_mgrp_create " + str(id))

        ################################################################################
        # destroy node associated with ports
        ################################################################################
        p = Popen([self.cli, '--thrift-port',
                   str(self.thrift_port)],
                  stdout=PIPE,
                  stdin=PIPE,
                  stderr=STDOUT)
        out, err = p.communicate(input="mc_node_destroy " + str(id))

        ################################################################################
        # create node associated with ports
        ################################################################################
        p = Popen([self.cli, '--thrift-port',
                   str(self.thrift_port)],
                  stdout=PIPE,
                  stdin=PIPE,
                  stderr=STDOUT)
        out, err = p.communicate(input="mc_node_create " + str(id) + " " +
                                 " ".join(map(str, ports)))

        ################################################################################
        # associate node and grp
        ################################################################################
        p = Popen([self.cli, '--thrift-port',
                   str(self.thrift_port)],
                  stdout=PIPE,
                  stdin=PIPE,
                  stderr=STDOUT)
        out, err = p.communicate(input="mc_node_associate " + str(id) + " " +
                                 str(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 update_mc_table(self):
        valid_entries = []
        id = 2

        for mcgrp in self.mcgrp_to_port:
            if self.mcgrp_to_port[mcgrp]:
                self.update_mc_group(id=id, ports=self.mcgrp_to_port[mcgrp])

                entry = TableEntry(match_fields={"hdr.ipv4.dstAddr": mcgrp},
                                   action_name="ingress.ipv4_c.set_mc_grp",
                                   action_params={"grp": id})

                id += 1

                TableEntryManager.handle_table_entry(
                    manager=self.table_manager,
                    table_name="ingress.ipv4_c.ipv4_mc",
                    table_entry=entry)

                valid_entries.append(entry.match_fields)

        self.table_manager.remove_invalid_entries(
            table_name="ingress.ipv4_c.ipv4_mc", valid_entries=valid_entries)
Ejemplo n.º 27
0
class MacController(object):
    """
    This module implements an MacController and
    sets rewrite rules for layer 2
    """
    def __init__(self, base):
        """
        Init Maccontroller with base controller

        Args:
            base (libs.core.BaseController): Base controller
        """

        # table manager
        self.table_manager = TableEntryManager(controller=base,
                                               name="MacController")
        self.table_manager.init_table("egress.mac_c.adjust_mac")

        Event.on("topology_change", self.update)

    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)

    #############################################################
    #                   Event Listener                          #
    #############################################################

    def update(self, *args, **kwargs):
        """
        Update mac entries
        triggered by event
        """

        self.update_mac_entry()