예제 #1
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
예제 #2
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)
예제 #3
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)
예제 #4
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)
예제 #5
0
    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
예제 #6
0
    def remove_entry(self, entry=None):
        """
        Adds an table entry and locks write request to prevent threading problems
        :param entry: TableEntry serialized as pickle string
        :return:
        """

        e = pickle.loads(entry.table_entry)

        # a table entry is identified by match fields and priority
        self.entries.remove((entry.table_name.encode('utf-8'), TableEntry(match_fields=e.match_fields, priority=e.priority)))

        return self.delete_table_entry(table_name=entry.table_name.encode('utf-8'), entry=e)
예제 #7
0
    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
예제 #8
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'))
예제 #9
0
    def add_entry(self, entry=None):
        """
        Adds an table entry and locks write request to prevent threading problems
        :param switch:
        :param table_name:
        :param match_fields:
        :param action_name:
        :param action_params:
        :return:
        """

        e = pickle.loads(entry.table_entry)

        # a table entry is identified by match fields and priority
        self.entries.append((entry.table_name.encode('utf-8'),
                            TableEntry(match_fields=e.match_fields, priority=e.priority)))

        return self.add_table_entry(table_name=entry.table_name.encode('utf-8'), entry=e)
예제 #10
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)
예제 #11
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
예제 #12
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)
예제 #13
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)
예제 #14
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)
예제 #15
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)