@@ -375,6 +375,16 @@ def __init__(self, logger, flood_table, pipeline, # pylint: disable=too-many-arg
375
375
self .external_root_only = True
376
376
self ._reset_peer_distances ()
377
377
378
+ def _build_flood_acts_for_port (self , vlan , exclude_unicast , port , # pylint: disable=too-many-arguments
379
+ exclude_all_external = False ,
380
+ exclude_restricted_bcast_arpnd = False ):
381
+ if self .external_root_only :
382
+ exclude_all_external = True
383
+ return super (ValveFloodStackManagerBase , self )._build_flood_acts_for_port (
384
+ vlan , exclude_unicast , port ,
385
+ exclude_all_external = exclude_all_external ,
386
+ exclude_restricted_bcast_arpnd = exclude_restricted_bcast_arpnd )
387
+
378
388
def _flood_actions (self , in_port , external_ports ,
379
389
away_flood_actions , toward_flood_actions , local_flood_actions ):
380
390
raise NotImplementedError
@@ -521,8 +531,6 @@ def _build_mask_flood_rules(self, vlan, eth_type, eth_dst, eth_dst_mask, # pyli
521
531
for ext_port_flag , exclude_all_external in (
522
532
(valve_of .PCP_NONEXT_PORT_FLAG , True ),
523
533
(valve_of .PCP_EXT_PORT_FLAG , False )):
524
- if self .external_root_only :
525
- exclude_all_external = True
526
534
if not prune :
527
535
flood_acts , _ , _ = self ._build_flood_acts_for_port (
528
536
vlan , exclude_unicast , port ,
@@ -589,12 +597,22 @@ def edge_learn_port(self, other_valves, pkt_meta):
589
597
Returns:
590
598
port to learn host on, or None.
591
599
"""
600
+ # Got a packet from another DP.
592
601
if pkt_meta .port .stack :
593
602
edge_dp = self ._edge_dp_for_host (other_valves , pkt_meta )
594
- # No edge DP may have learned this host yet.
595
- if edge_dp is None :
596
- return None
597
- return self .shortest_path_port (edge_dp .name )
603
+ if edge_dp :
604
+ return self .shortest_path_port (edge_dp .name )
605
+ # Assuming no DP has learned this host.
606
+ return None
607
+
608
+ # Got a packet locally.
609
+ # If learning on an external port, check another DP hasn't
610
+ # already learned on a local/non-external port.
611
+ if pkt_meta .port .loop_protect_external :
612
+ edge_dp = self ._non_stack_learned (other_valves , pkt_meta )
613
+ if edge_dp :
614
+ return self .shortest_path_port (edge_dp .name )
615
+ # Locally learn.
598
616
return super (ValveFloodStackManagerBase , self ).edge_learn_port (
599
617
other_valves , pkt_meta )
600
618
@@ -609,6 +627,33 @@ def _edge_dp_for_host(self, other_valves, pkt_meta):
609
627
"""
610
628
raise NotImplementedError
611
629
630
+ def _non_stack_learned (self , other_valves , pkt_meta ):
631
+ other_local_dp_entries = []
632
+ other_external_dp_entries = []
633
+ vlan_vid = pkt_meta .vlan .vid
634
+ for other_valve in other_valves :
635
+ other_dp_vlan = other_valve .dp .vlans .get (vlan_vid , None )
636
+ if other_dp_vlan is not None :
637
+ entry = other_dp_vlan .cached_host (pkt_meta .eth_src )
638
+ if not entry :
639
+ continue
640
+ if entry .port .stack :
641
+ continue
642
+ if entry .port .loop_protect_external :
643
+ other_external_dp_entries .append (other_valve .dp )
644
+ else :
645
+ other_local_dp_entries .append (other_valve .dp )
646
+ # Another DP has learned locally, has priority.
647
+ if other_local_dp_entries :
648
+ return other_local_dp_entries [0 ]
649
+ # No other DP has learned locally, but at least one has learned externally.
650
+ if other_external_dp_entries :
651
+ entry = pkt_meta .vlan .cached_host (pkt_meta .eth_src )
652
+ # This DP has not learned the host either, use other's external.
653
+ if entry is None :
654
+ return other_external_dp_entries [0 ]
655
+ return None
656
+
612
657
613
658
class ValveFloodStackManagerNoReflection (ValveFloodStackManagerBase ):
614
659
"""Stacks of size 2 - all switches directly connected to root.
@@ -635,7 +680,11 @@ def _flood_actions(self, in_port, external_ports,
635
680
636
681
def _edge_dp_for_host (self , other_valves , pkt_meta ):
637
682
"""Size 2 means root shortest path is always directly connected."""
638
- return pkt_meta .port .stack ['dp' ]
683
+ peer_dp = pkt_meta .port .stack ['dp' ]
684
+ if peer_dp .dyn_running :
685
+ return self ._non_stack_learned (other_valves , pkt_meta )
686
+ # Fall back to assuming peer knows if we are not the peer's controller.
687
+ return peer_dp
639
688
640
689
641
690
class ValveFloodStackManagerReflection (ValveFloodStackManagerBase ):
@@ -757,14 +806,12 @@ def _edge_dp_for_host(self, other_valves, pkt_meta):
757
806
# (for example, just default switch to a neighbor).
758
807
# Find port that forwards closer to destination DP that
759
808
# has already learned this host (if any).
760
- vlan_vid = pkt_meta .vlan .vid
761
- for other_valve in other_valves :
762
- other_dp_vlan = other_valve .dp .vlans .get (vlan_vid , None )
763
- if other_dp_vlan is not None :
764
- entry = other_dp_vlan .cached_host (pkt_meta .eth_src )
765
- if entry and not entry .port .stack :
766
- return other_valve .dp
767
809
peer_dp = pkt_meta .port .stack ['dp' ]
768
- if peer_dp .is_stack_edge () or peer_dp .is_stack_root ():
769
- return peer_dp
810
+ if peer_dp .dyn_running :
811
+ return self ._non_stack_learned (other_valves , pkt_meta )
812
+ else :
813
+ # Fall back to peer knows if edge or root if we are not the peer's controller.
814
+ if peer_dp .is_stack_edge () or peer_dp .is_stack_root ():
815
+ return peer_dp
816
+ # No DP has learned this host, yet. Take no action to allow remote learning to occur.
770
817
return None
0 commit comments