def _flood_actions( self, in_port, external_ports, # pylint: disable=too-many-arguments away_flood_actions, toward_flood_actions, local_flood_actions): if self.stack_manager.stack.is_root(): if external_ports: flood_prefix = self._set_nonext_port_flag else: flood_prefix = self._set_ext_port_flag flood_actions = (away_flood_actions + local_flood_actions) if in_port and self.stack_manager.is_away(in_port): # Packet from a non-root switch, flood locally and to all non-root switches # (reflect it). flood_actions = (away_flood_actions + (valve_of.output_in_port(), ) + local_flood_actions) flood_actions = flood_prefix + flood_actions else: # Default non-root strategy is flood towards root. if external_ports: flood_actions = self._set_nonext_port_flag + toward_flood_actions else: flood_actions = self._set_ext_port_flag + toward_flood_actions if in_port: # Packet from switch further away, flood it to the root. if self.stack_manager.is_away(in_port): flood_actions = toward_flood_actions # Packet from the root. elif self.stack_manager.is_towards_root(in_port): # If we have external ports, and packet hasn't already been flooded # externally, flood it externally before passing it to further away switches, # and mark it flooded. if external_ports: flood_actions = (self._set_nonext_port_flag + away_flood_actions + local_flood_actions) else: flood_actions = (away_flood_actions + self._set_nonext_port_flag + local_flood_actions) # Packet from external port, locally. Mark it already flooded externally and # flood to root (it came from an external switch so keep it within the stack). elif in_port.loop_protect_external: flood_actions = self._set_nonext_port_flag + toward_flood_actions else: flood_actions = self._set_ext_port_flag + toward_flood_actions return flood_actions
def _flood_actions(self, in_port, external_ports, away_flood_actions, toward_flood_actions, local_flood_actions): # General case for stack with maximum distance > 2 if self._dp_is_root(): flood_actions = (self.ext_flood_needed + away_flood_actions + local_flood_actions) if in_port: if in_port in self.away_from_root_stack_ports: # Packet from a non-root switch, flood locally and to all non-root switches # (reflect it). flood_actions = (away_flood_actions + [valve_of.output_in_port()] + local_flood_actions) # If we have external ports, let the non-roots know they don't have to # flood externally. if external_ports: flood_actions = self.ext_flood_not_needed + flood_actions else: flood_actions = self.ext_flood_needed + flood_actions elif external_ports: # Packet from an external switch, locally. As above, let the non-roots # know they don't have to flood externally again. flood_actions = (self.ext_flood_not_needed + away_flood_actions + local_flood_actions) else: # Default non-root strategy is flood towards root. flood_actions = self.ext_flood_needed + toward_flood_actions if in_port: # Packet from switch further away, flood it to the root. if in_port in self.away_from_root_stack_ports: flood_actions = toward_flood_actions # Packet from the root. elif in_port in self.towards_root_stack_ports: # If we have external ports, and packet hasn't already been flooded # externally, flood it externally before passing it to further away switches, # and mark it flooded. if external_ports: flood_actions = (self.ext_flood_not_needed + away_flood_actions + local_flood_actions) else: flood_actions = (away_flood_actions + self.ext_flood_not_needed + local_flood_actions) # Packet from external port, locally. Mark it already flooded externally and # flood to root (it came from an external switch so keep it within the stack). elif in_port.loop_protect_external: flood_actions = self.ext_flood_not_needed + toward_flood_actions return flood_actions
def _flood_actions(self, in_port, external_ports, away_flood_actions, toward_flood_actions, local_flood_actions): # General case for stack with maximum distance > 2 if self._dp_is_root(): flood_actions = ( self.ext_flood_needed + away_flood_actions + local_flood_actions) if in_port: if in_port in self.away_from_root_stack_ports: # Packet from a non-root switch, flood locally and to all non-root switches # (reflect it). flood_actions = ( away_flood_actions + [valve_of.output_in_port()] + local_flood_actions) # If we have external ports, let the non-roots know they don't have to # flood externally. if external_ports: flood_actions = self.ext_flood_not_needed + flood_actions else: flood_actions = self.ext_flood_needed + flood_actions elif external_ports: # Packet from an external switch, locally. As above, let the non-roots # know they don't have to flood externally again. flood_actions = ( self.ext_flood_not_needed + away_flood_actions + local_flood_actions) else: # Default non-root strategy is flood towards root. flood_actions = self.ext_flood_needed + toward_flood_actions if in_port: # Packet from switch further away, flood it to the root. if in_port in self.away_from_root_stack_ports: flood_actions = toward_flood_actions # Packet from the root. elif in_port in self.towards_root_stack_ports: # If we have external ports, and packet hasn't already been flooded # externally, flood it externally before passing it to further away switches, # and mark it flooded. if external_ports: flood_actions = ( self.ext_flood_not_needed + away_flood_actions + local_flood_actions) else: flood_actions = ( away_flood_actions + self.ext_flood_not_needed + local_flood_actions) # Packet from external port, locally. Mark it already flooded externally and # flood to root (it came from an external switch so keep it within the stack). elif in_port.loop_protect_external: flood_actions = self.ext_flood_not_needed + toward_flood_actions return 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
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): """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