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 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_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)
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 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)
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 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 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)
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)
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
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)
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)
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)
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)