def _build_flood_rule_actions( self, vlan, exclude_unicast, in_port, # pylint: disable=too-many-arguments exclude_all_external=False, exclude_restricted_bcast_arpnd=False): """Compiles all the possible flood rule actions for a port on a stack node""" exclude_ports = list(self.stack_manager.inactive_away_ports) external_ports = vlan.loop_protect_external_ports() if in_port and self.stack_manager.is_stack_port(in_port): in_port_peer_dp = in_port.stack['dp'] exclude_ports = exclude_ports + self.stack_manager.adjacent_stack_ports( in_port_peer_dp) local_flood_actions = tuple( self._build_flood_local_rule_actions( vlan, exclude_unicast, in_port, exclude_all_external, exclude_restricted_bcast_arpnd)) away_flood_actions = tuple( valve_of.flood_tagged_port_outputs(self.stack_manager.away_ports, in_port, exclude_ports=exclude_ports)) toward_flood_actions = tuple( valve_of.flood_tagged_port_outputs( self.stack_manager.chosen_towards_ports, in_port)) flood_acts = self._flood_actions(in_port, external_ports, away_flood_actions, toward_flood_actions, local_flood_actions) return flood_acts
def _build_flood_rule_actions(self, vlan, exclude_unicast, in_port, exclude_all_external=False, exclude_restricted_bcast_arpnd=False): exclude_ports = self._inactive_away_stack_ports() external_ports = vlan.loop_protect_external_ports() if in_port and in_port in self.stack_ports: in_port_peer_dp = in_port.stack['dp'] exclude_ports = exclude_ports + [ port for port in self.stack_ports if port.stack['dp'] == in_port_peer_dp ] local_flood_actions = self._build_flood_local_rule_actions( vlan, exclude_unicast, in_port, exclude_all_external, exclude_restricted_bcast_arpnd) away_flood_actions = valve_of.flood_tagged_port_outputs( self.away_from_root_stack_ports, in_port, exclude_ports=exclude_ports) toward_flood_actions = valve_of.flood_tagged_port_outputs( self.towards_root_stack_ports, in_port) flood_acts = self._flood_actions(in_port, external_ports, away_flood_actions, toward_flood_actions, local_flood_actions) return flood_acts
def _build_flood_local_rule_actions(vlan, exclude_unicast, in_port): """Return a list of flood actions to flood packets from a port.""" flood_acts = [] tagged_ports = vlan.tagged_flood_ports(exclude_unicast) flood_acts.extend( valve_of.flood_tagged_port_outputs(tagged_ports, in_port)) untagged_ports = vlan.untagged_flood_ports(exclude_unicast) flood_acts.extend( valve_of.flood_untagged_port_outputs(untagged_ports, in_port)) return flood_acts
def _build_flood_local_rule_actions(vlan, exclude_unicast, in_port): """Return a list of flood actions to flood packets from a port.""" flood_acts = [] exclude_ports = [] if in_port is not None and in_port.lacp: lags = vlan.lags() exclude_ports = lags[in_port.lacp] tagged_ports = vlan.tagged_flood_ports(exclude_unicast) flood_acts.extend(valve_of.flood_tagged_port_outputs( tagged_ports, in_port=in_port, exclude_ports=exclude_ports)) untagged_ports = vlan.untagged_flood_ports(exclude_unicast) flood_acts.extend(valve_of.flood_untagged_port_outputs( untagged_ports, in_port=in_port, exclude_ports=exclude_ports)) return flood_acts
def _build_flood_local_rule_actions(vlan, exclude_unicast, in_port): """Return a list of flood actions to flood packets from a port.""" flood_acts = [] exclude_ports = set() lags = vlan.lags() if lags: if in_port is not None and in_port.lacp: # Don't flood from one LACP bundle member, to another. exclude_ports.update(lags[in_port.lacp]) # Pick one up bundle member to flood to. for ports in list(lags.values()): exclude_ports.update(ports[1:]) tagged_ports = vlan.tagged_flood_ports(exclude_unicast) flood_acts.extend(valve_of.flood_tagged_port_outputs( tagged_ports, in_port=in_port, exclude_ports=exclude_ports)) untagged_ports = vlan.untagged_flood_ports(exclude_unicast) flood_acts.extend(valve_of.flood_untagged_port_outputs( untagged_ports, in_port=in_port, exclude_ports=exclude_ports)) return flood_acts
def _build_flood_rule_actions(self, vlan, exclude_unicast, in_port, exclude_all_external=False): """Calculate flooding destinations based on this DP's position. If a standalone switch, then flood to local VLAN ports. If a distributed switch where all switches are directly connected to the root (star topology), edge switches flood locally and to the root, and the root floods to the other edges. If a non-star distributed switch topologies, use selective flooding (see the following example). Hosts |||| |||| +----+ +----+ +----+ ---+1 | |1234| | 1+--- Hosts ---+2 | | | | 2+--- Hosts ---+3 | | | | 3+--- ---+4 5+-------+5 6+-------+5 4+--- +----+ +----+ +----+ Root DP Non-root switches flood only to the root. The root switch reflects incoming floods back out. Non-root switches flood packets from the root locally and further away. Flooding is entirely implemented in the dataplane. A host connected to a non-root switch can receive a copy of its own flooded packet (because the non-root switch does not know it has seen the packet already). A host connected to the root switch does not have this problem (because flooding is always away from the root). Therefore, connections to other non-FAUCET stacking networks should only be made to the root. On the root switch (left), flood destinations are: 1: 2 3 4 5(s) 2: 1 3 4 5(s) 3: 1 2 4 5(s) 4: 1 2 3 5(s) 5: 1 2 3 4 5(s, note reflection) On the middle switch: 1: 5(s) 2: 5(s) 3: 5(s) 4: 5(s) 5: 1 2 3 4 6(s) 6: 5(s) On the rightmost switch: 1: 5(s) 2: 5(s) 3: 5(s) 4: 5(s) 5: 1 2 3 4 """ exclude_ports = [] external_ports = vlan.loop_protect_external_ports_up() if in_port and in_port in self.stack_ports: in_port_peer_dp = in_port.stack['dp'] exclude_ports = [ port for port in self.stack_ports if port.stack['dp'] == in_port_peer_dp ] local_flood_actions = self._build_flood_local_rule_actions( vlan, exclude_unicast, in_port, exclude_all_external) away_flood_actions = valve_of.flood_tagged_port_outputs( self.away_from_root_stack_ports, in_port, exclude_ports=exclude_ports) toward_flood_actions = valve_of.flood_tagged_port_outputs( self.towards_root_stack_ports, in_port) if self.stack_size == 2: flood_acts = self._flood_actions_size2(in_port, external_ports, away_flood_actions, toward_flood_actions, local_flood_actions) else: flood_acts = self._flood_actions(in_port, external_ports, away_flood_actions, toward_flood_actions, local_flood_actions) return valve_of.dedupe_ofmsgs(flood_acts)
def _build_flood_rule_actions(self, vlan, exclude_unicast, in_port): """Calculate flooding destinations based on this DP's position. If a standalone switch, then flood to local VLAN ports. If a distributed switch where all switches are directly connected to the root (star topology), edge switches flood locally and to the root, and the root floods to the other edges. If a non-star distributed switch topologies, use selective flooding (see the following example). Hosts |||| |||| +----+ +----+ +----+ ---+1 | |1234| | 1+--- Hosts ---+2 | | | | 2+--- Hosts ---+3 | | | | 3+--- ---+4 5+-------+5 6+-------+5 4+--- +----+ +----+ +----+ Root DP Non-root switches flood only to the root. The root switch reflects incoming floods back out. Non-root switches flood packets from the root locally and further away. Flooding is entirely implemented in the dataplane. A host connected to a non-root switch can receive a copy of its own flooded packet (because the non-root switch does not know it has seen the packet already). A host connected to the root switch does not have this problem (because flooding is always away from the root). Therefore, connections to other non-FAUCET stacking networks should only be made to the root. On the root switch (left), flood destinations are: 1: 2 3 4 5(s) 2: 1 3 4 5(s) 3: 1 2 4 5(s) 4: 1 2 3 5(s) 5: 1 2 3 4 5(s, note reflection) On the middle switch: 1: 5(s) 2: 5(s) 3: 5(s) 4: 5(s) 5: 1 2 3 4 6(s) 6: 5(s) On the rightmost switch: 1: 5(s) 2: 5(s) 3: 5(s) 4: 5(s) 5: 1 2 3 4 """ exclude_ports = [] dp_local_in_port = self._port_is_dp_local(in_port) if not dp_local_in_port: in_port_peer_dp = in_port.stack['dp'] exclude_ports = [ port for port in self.stack_ports if port.stack and port.stack['dp'] == in_port_peer_dp] local_flood_actions = self._build_flood_local_rule_actions( vlan, exclude_unicast, in_port) away_flood_actions = valve_of.flood_tagged_port_outputs( self.away_from_root_stack_ports, in_port, exclude_ports=exclude_ports) toward_flood_actions = valve_of.flood_tagged_port_outputs( self.towards_root_stack_ports, in_port) flood_all_except_self = away_flood_actions + local_flood_actions # TODO: optimization for 2 layer stack - no need to reflect off root. # We should generalize the edge switch case, too. if self.stack.get('longest_path_to_root_len', None) == 2: if self._dp_is_root(): return away_flood_actions + local_flood_actions if dp_local_in_port: return toward_flood_actions + local_flood_actions return local_flood_actions if self._dp_is_root(): if dp_local_in_port: return flood_all_except_self # If input port non-local, then flood outward again return [valve_of.output_in_port()] + flood_all_except_self # We are not the root of the distributed switch # If input port was connected to a switch closer to the root, # then flood outwards (local VLAN and stacks further than us) if in_port in self.towards_root_stack_ports: return flood_all_except_self # If input port local or from a further away switch, flood # towards the root. return toward_flood_actions
def _build_flood_rule_actions(self, vlan, exclude_unicast, in_port): """Calculate flooding destinations based on this DP's position. If a standalone switch, then flood to local VLAN ports. If a distributed switch, see the following example. Hosts |||| |||| +----+ +----+ +----+ ---+1 | |1234| | 1+--- Hosts ---+2 | | | | 2+--- Hosts ---+3 | | | | 3+--- ---+4 5+-------+5 6+-------+5 4+--- +----+ +----+ +----+ Root DP The basic strategy is flood-towards-root. The root reflects the flood back out. There are no loops and flooding is done entirely in the dataplane. On the root switch (left), flood destinations are: 1: 2 3 4 5(s) 2: 1 3 4 5(s) 3: 1 2 4 5(s) 4: 1 2 3 5(s) 5: 1 2 3 4 5(s, note reflection) On the middle switch: 1: 5(s) 2: 5(s) 3: 5(s) 4: 5(s) 5: 1 2 3 4 6(s) 6: 5(s) On the rightmost switch: 1: 5(s) 2: 5(s) 3: 5(s) 4: 5(s) 5: 1 2 3 4 """ local_flood_actions = self._build_flood_local_rule_actions( vlan, exclude_unicast, in_port) away_flood_actions = valve_of.flood_tagged_port_outputs( self.away_from_root_stack_ports, in_port) toward_flood_actions = valve_of.flood_tagged_port_outputs( self.towards_root_stack_ports, in_port) flood_all_except_self = away_flood_actions + local_flood_actions # If we're the root of a distributed switch.. if self._dp_is_root(): # If the input port was local, then flood local VLAN and stacks. if self._port_is_dp_local(in_port): return flood_all_except_self # If input port non-local, then flood outward again return [valve_of.output_in_port()] + flood_all_except_self # We are not the root of the distributed switch # If input port was connected to a switch closer to the root, # then flood outwards (local VLAN and stacks further than us) if in_port in self.towards_root_stack_ports: return flood_all_except_self # If input port local or from a further away switch, flood # towards the root. return toward_flood_actions
def _build_flood_rule_actions(self, vlan, exclude_unicast, in_port, exclude_all_external=False): """Calculate flooding destinations based on this DP's position. If a standalone switch, then flood to local VLAN ports. If a distributed switch where all switches are directly connected to the root (star topology), edge switches flood locally and to the root, and the root floods to the other edges. If a non-star distributed switch topologies, use selective flooding (see the following example). Hosts |||| |||| +----+ +----+ +----+ ---+1 | |1234| | 1+--- Hosts ---+2 | | | | 2+--- Hosts ---+3 | | | | 3+--- ---+4 5+-------+5 6+-------+5 4+--- +----+ +----+ +----+ Root DP Non-root switches flood only to the root. The root switch reflects incoming floods back out. Non-root switches flood packets from the root locally and further away. Flooding is entirely implemented in the dataplane. A host connected to a non-root switch can receive a copy of its own flooded packet (because the non-root switch does not know it has seen the packet already). A host connected to the root switch does not have this problem (because flooding is always away from the root). Therefore, connections to other non-FAUCET stacking networks should only be made to the root. On the root switch (left), flood destinations are: 1: 2 3 4 5(s) 2: 1 3 4 5(s) 3: 1 2 4 5(s) 4: 1 2 3 5(s) 5: 1 2 3 4 5(s, note reflection) On the middle switch: 1: 5(s) 2: 5(s) 3: 5(s) 4: 5(s) 5: 1 2 3 4 6(s) 6: 5(s) On the rightmost switch: 1: 5(s) 2: 5(s) 3: 5(s) 4: 5(s) 5: 1 2 3 4 """ exclude_ports = [] external_ports = vlan.loop_protect_external_ports_up() if in_port and in_port in self.stack_ports: in_port_peer_dp = in_port.stack['dp'] exclude_ports = [ port for port in self.stack_ports if port.stack['dp'] == in_port_peer_dp] local_flood_actions = self._build_flood_local_rule_actions( vlan, exclude_unicast, in_port, exclude_all_external) away_flood_actions = valve_of.flood_tagged_port_outputs( self.away_from_root_stack_ports, in_port, exclude_ports=exclude_ports) toward_flood_actions = valve_of.flood_tagged_port_outputs( self.towards_root_stack_ports, in_port) flood_acts = self._flood_actions_func( in_port, external_ports, away_flood_actions, toward_flood_actions, local_flood_actions) return flood_acts
def _build_flood_rule_actions(self, vlan, exclude_unicast, in_port): """Calculate flooding destinations based on this DP's position. If a standalone switch, then flood to local VLAN ports. If a distributed switch, use selective flooding (see the following example). Hosts |||| |||| +----+ +----+ +----+ ---+1 | |1234| | 1+--- Hosts ---+2 | | | | 2+--- Hosts ---+3 | | | | 3+--- ---+4 5+-------+5 6+-------+5 4+--- +----+ +----+ +----+ Root DP Non-root switches flood only to the root. The root switch reflects incoming floods back out. Non-root switches flood packets from the root locally and further away. Flooding is entirely implemented in the dataplane. A host connected to a non-root switch can receive a copy of its own flooded packet (because the non-root switch does not know it has seen the packet already). A host connected to the root switch does not have this problem (because flooding is always away from the root). Therefore, connections to other non-FAUCET stacking networks should only be made to the root. On the root switch (left), flood destinations are: 1: 2 3 4 5(s) 2: 1 3 4 5(s) 3: 1 2 4 5(s) 4: 1 2 3 5(s) 5: 1 2 3 4 5(s, note reflection) On the middle switch: 1: 5(s) 2: 5(s) 3: 5(s) 4: 5(s) 5: 1 2 3 4 6(s) 6: 5(s) On the rightmost switch: 1: 5(s) 2: 5(s) 3: 5(s) 4: 5(s) 5: 1 2 3 4 """ local_flood_actions = self._build_flood_local_rule_actions( vlan, exclude_unicast, in_port) away_flood_actions = valve_of.flood_tagged_port_outputs( self.away_from_root_stack_ports, in_port) toward_flood_actions = valve_of.flood_tagged_port_outputs( self.towards_root_stack_ports, in_port) flood_all_except_self = away_flood_actions + local_flood_actions # If we're the root of a distributed switch.. if self._dp_is_root(): # If the input port was local, then flood local VLAN and stacks. if self._port_is_dp_local(in_port): return flood_all_except_self # If input port non-local, then flood outward again return [valve_of.output_in_port()] + flood_all_except_self # We are not the root of the distributed switch # If input port was connected to a switch closer to the root, # then flood outwards (local VLAN and stacks further than us) if in_port in self.towards_root_stack_ports: return flood_all_except_self # If input port local or from a further away switch, flood # towards the root. return toward_flood_actions
def _build_flood_rule_actions(self, vlan, exclude_unicast, in_port): """Calculate flooding destinations based on this DP's position. If a standalone switch, then flood to local VLAN ports. If a distributed switch where all switches are directly connected to the root (star topology), edge switches flood locally and to the root, and the root floods to the other edges. If a non-star distributed switch topologies, use selective flooding (see the following example). Hosts |||| |||| +----+ +----+ +----+ ---+1 | |1234| | 1+--- Hosts ---+2 | | | | 2+--- Hosts ---+3 | | | | 3+--- ---+4 5+-------+5 6+-------+5 4+--- +----+ +----+ +----+ Root DP Non-root switches flood only to the root. The root switch reflects incoming floods back out. Non-root switches flood packets from the root locally and further away. Flooding is entirely implemented in the dataplane. A host connected to a non-root switch can receive a copy of its own flooded packet (because the non-root switch does not know it has seen the packet already). A host connected to the root switch does not have this problem (because flooding is always away from the root). Therefore, connections to other non-FAUCET stacking networks should only be made to the root. On the root switch (left), flood destinations are: 1: 2 3 4 5(s) 2: 1 3 4 5(s) 3: 1 2 4 5(s) 4: 1 2 3 5(s) 5: 1 2 3 4 5(s, note reflection) On the middle switch: 1: 5(s) 2: 5(s) 3: 5(s) 4: 5(s) 5: 1 2 3 4 6(s) 6: 5(s) On the rightmost switch: 1: 5(s) 2: 5(s) 3: 5(s) 4: 5(s) 5: 1 2 3 4 """ exclude_ports = [] dp_local_in_port = self._port_is_dp_local(in_port) if not dp_local_in_port: in_port_peer_dp = in_port.stack['dp'] exclude_ports = [ port for port in self.stack_ports if port.stack and port.stack['dp'] == in_port_peer_dp] local_flood_actions = self._build_flood_local_rule_actions( vlan, exclude_unicast, in_port) away_flood_actions = valve_of.flood_tagged_port_outputs( self.away_from_root_stack_ports, in_port, exclude_ports=exclude_ports) toward_flood_actions = valve_of.flood_tagged_port_outputs( self.towards_root_stack_ports, in_port) flood_all_except_self = away_flood_actions + local_flood_actions # TODO: optimization for 2 layer stack - no need to reflect off root. # We should generalize the edge switch case, too. if self.stack.get('longest_path_to_root_len', None) == 2: if self._dp_is_root(): return away_flood_actions + local_flood_actions if dp_local_in_port: return toward_flood_actions + local_flood_actions return local_flood_actions if self._dp_is_root(): if dp_local_in_port: return flood_all_except_self # If input port non-local, then flood outward again return [valve_of.output_in_port()] + flood_all_except_self # We are not the root of the distributed switch # If input port was connected to a switch closer to the root, # then flood outwards (local VLAN and stacks further than us) if in_port in self.towards_root_stack_ports: return flood_all_except_self # If input port local or from a further away switch, flood # towards the root. return toward_flood_actions