Exemplo n.º 1
0
    def build_birt(switch=None, domain=None):
        """
        Build bit index routing table
        :param switch: switch for which the birt should be calculated
        :param domain: domain for which the birt should be build
        :return: birt in format {bfr-id: [dest prefix, nbr prefix]
        """
        # {bfr-id: [dest prefix, nbr prefix]
        birt = {}
        for destination in TopologyManager.get_topology(domain).get_nodes():
            if destination != switch:
                destination_device = TopologyManager.get_device(destination)

                if destination_device.get_type(
                ) == "Host":  # dont calculate entry for host
                    continue

                try:
                    next_hop = TopologyManager.get_next_hop(
                        switch, destination, domain)
                    birt[destination_device.get_bfr_id(domain)] = [
                        destination_device.get_ip(),
                        next_hop.get_ip()
                    ]
                except NextHopNotFound:  # maybe a path towards the destination is not yet learned
                    continue

        return birt
Exemplo n.º 2
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)
Exemplo n.º 3
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
Exemplo n.º 4
0
    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)
Exemplo n.º 5
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))
Exemplo n.º 6
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
Exemplo n.º 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
Exemplo n.º 8
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)
Exemplo n.º 9
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))
Exemplo n.º 10
0
    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))
Exemplo n.º 11
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()
Exemplo n.º 12
0
    def compute_bier_header(mc_addr=None, domain=None):
        """
        Compute the bier header for a given mc_addr in a given domain
        :param mc_addr: multicast address
        :param domain: domain identifier
        :return:
        """
        # only use bfrs in given domain
        all_bfrs = filter(
            lambda x: domain in TopologyManager.get_domain_for_device(x),
            GroupManager.get_bfr_by_mc_addr(mc_addr))

        # get (domain specific) bfr ids
        bfrs = map(lambda x: TopologyManager.get_device(x).get_bfr_id(domain),
                   all_bfrs)

        bier_header = reduce(
            ior, map(lambda x: BierComputation.id_to_bitstring(x), bfrs), 0)

        return bier_header
Exemplo n.º 13
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))
Exemplo n.º 14
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)
Exemplo 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)
Exemplo n.º 16
0
    def build_bift(switch=None, domain=0):
        """
        Generates the bit index forwarding table
        :param switch: Switch for which the table should be calculated
        :param domain: Domain for which the bift should be build
        :return: bift {bfr-id: [fbm, nextHop]
        """
        birt = BierComputation.build_birt(switch=switch, domain=domain)

        # {bfr-id: [fbm, nextHop]
        bift = {}

        for dest in birt:
            fbm = BierComputation.get_fbm(birt=birt,
                                          next_hop_prefix=birt.get(dest)[1])
            destination = TopologyManager.get_device_by_domain_and_id(
                domain, dest)

            next_hop = TopologyManager.get_next_hop(switch, destination,
                                                    domain)

            bift[dest] = [fbm, next_hop.get_name()]
        return bift
Exemplo n.º 17
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)
Exemplo n.º 18
0
    def get_domains_for_mc_addr(mc_addr):
        """
        Get domains which have subscriptions on a given multicast address
        :param mc_addr: multicast address
        :return: list of domains
        """
        bfrs = GroupManager.get_bfr_by_mc_addr(mc_addr)

        domains = [0]

        for bfr in bfrs:
            domains.extend(TopologyManager.get_domain_for_device(bfr))

        domains = list(set(domains))

        return domains
Exemplo n.º 19
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'))
Exemplo n.º 20
0
    def send_topology_packets(self):
        """
        Send topology packet for given switch
        :return:
        """

        sw = Configuration.get('name')
        # get bfr id off switch for identification in top packet
        switch = TopologyManager.get_device(sw)
        pkt = Ether(src='00:00:00:00:00:00', dst='ff:ff:ff:ff:ff:ff')

        pkt = pkt / TopologyDiscovery(identifier=switch.get_bfr_id(0),
                                      port=1,
                                      ip=str(switch.get_ip()),
                                      mac=str(switch.get_mac()))

        self.__baseController.get_connection().send_packet_out(str(pkt))

        Log.debug("Send topology packet to switch", sw)

        # send topology packet periodically
        threading.Timer(2, self.send_topology_packets).start()
Exemplo n.º 21
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)