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)
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)
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.")
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)
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()
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"])
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)
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.")
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()