Skip to content

Commit

Permalink
Merge pull request #3459 from mab68/ttinterflood2
Browse files Browse the repository at this point in the history
Flood to stack ports for inter-VLAN routing
  • Loading branch information
anarkiwi authored Feb 12, 2020
2 parents ae3f2ac + ec13ef6 commit dc8f363
Show file tree
Hide file tree
Showing 4 changed files with 281 additions and 33 deletions.
49 changes: 26 additions & 23 deletions faucet/valve.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,29 +170,6 @@ def dp_init(self, new_dp=None):
self._port_highwater[vlan_vid] = {}
for port_number in self.dp.ports.keys():
self._port_highwater[vlan_vid][port_number] = 0
for ipv, route_manager_class, neighbor_timeout in (
(4, valve_route.ValveIPv4RouteManager, self.dp.arp_neighbor_timeout),
(6, valve_route.ValveIPv6RouteManager, self.dp.nd_neighbor_timeout)):
fib_table_name = 'ipv%u_fib' % ipv
if not fib_table_name in self.dp.tables:
continue
fib_table = self.dp.tables[fib_table_name]
proactive_learn = getattr(self.dp, 'proactive_learn_v%u' % ipv)
route_manager = route_manager_class(
self.logger, self.notify, self.dp.global_vlan, neighbor_timeout,
self.dp.max_hosts_per_resolve_cycle,
self.dp.max_host_fib_retry_count,
self.dp.max_resolve_backoff_time, proactive_learn,
self.DEC_TTL, self.dp.multi_out, fib_table,
self.dp.tables['vip'], self.pipeline, self.dp.routers)
self._route_manager_by_ipv[route_manager.IPV] = route_manager
for vlan in self.dp.vlans.values():
if vlan.faucet_vips_by_ipv(route_manager.IPV):
route_manager.active = True
self.logger.info('IPv%u routing is active on %s with VIPs %s' % (
route_manager.IPV, vlan, vlan.faucet_vips_by_ipv(route_manager.IPV)))
for eth_type in route_manager.CONTROL_ETH_TYPES:
self._route_manager_by_eth_type[eth_type] = route_manager
restricted_bcast_arpnd = bool(self.dp.restricted_bcast_arpnd_ports())
if self.dp.stack:
flood_class = valve_flood.ValveFloodStackManagerNoReflection
Expand All @@ -216,6 +193,32 @@ def dp_init(self, new_dp=None):
self.dp.group_table, self.dp.groups,
self.dp.combinatorial_port_flood, self.dp.canonical_port_order,
restricted_bcast_arpnd)
for ipv, route_manager_class, neighbor_timeout in (
(4, valve_route.ValveIPv4RouteManager, self.dp.arp_neighbor_timeout),
(6, valve_route.ValveIPv6RouteManager, self.dp.nd_neighbor_timeout)):
fib_table_name = 'ipv%u_fib' % ipv
if fib_table_name not in self.dp.tables:
continue
fib_table = self.dp.tables[fib_table_name]
proactive_learn = getattr(self.dp, 'proactive_learn_v%u' % ipv)
valve_flood_manager = None
if self.dp.stack:
valve_flood_manager = self.flood_manager
route_manager = route_manager_class(
self.logger, self.notify, self.dp.global_vlan, neighbor_timeout,
self.dp.max_hosts_per_resolve_cycle,
self.dp.max_host_fib_retry_count,
self.dp.max_resolve_backoff_time, proactive_learn,
self.DEC_TTL, self.dp.multi_out, fib_table,
self.dp.tables['vip'], self.pipeline, self.dp.routers, valve_flood_manager)
self._route_manager_by_ipv[route_manager.IPV] = route_manager
for vlan in self.dp.vlans.values():
if vlan.faucet_vips_by_ipv(route_manager.IPV):
route_manager.active = True
self.logger.info('IPv%u routing is active on %s with VIPs %s' % (
route_manager.IPV, vlan, vlan.faucet_vips_by_ipv(route_manager.IPV)))
for eth_type in route_manager.CONTROL_ETH_TYPES:
self._route_manager_by_eth_type[eth_type] = route_manager
eth_dst_hairpin_table = self.dp.tables.get('eth_dst_hairpin', None)
host_manager_cl = valve_host.ValveHostManager
if self.dp.use_idle_timeout:
Expand Down
53 changes: 53 additions & 0 deletions faucet/valve_flood.py
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,59 @@ def _non_stack_learned(self, other_valves, pkt_meta):
return other_external_dp_entries[0]
return None

def _stack_flood_ports(self):
"""Return output ports of a DP that have been pruned and follow reflection rules"""
# TODO: Consolidate stack port selection logic,
# this reuses logic from _build_mask_flood_rules()
away_flood_ports = []
towards_flood_ports = []
# Obtain away ports
away_up_ports_by_dp = defaultdict(list)
for port in self._canonical_stack_up_ports(self.away_from_root_stack_ports):
away_up_ports_by_dp[port.stack['dp']].append(port)
# Obtain the towards root path port (this is the designated root port)
towards_up_port = None
towards_up_ports = self._canonical_stack_up_ports(self.towards_root_stack_ports)
if towards_up_ports:
towards_up_port = towards_up_ports[0]
# Figure out what stack ports will need to be flooded
for port in self.stack_ports:
remote_dp = port.stack['dp']
away_up_port = None
away_up_ports = away_up_ports_by_dp.get(remote_dp, None)
if away_up_ports:
# Pick the lowest port number on the remote DP.
remote_away_ports = self.canonical_port_order(
[away_port.stack['port'] for away_port in away_up_ports])
away_up_port = remote_away_ports[0].stack['port']
# Is the port to an away DP, (away from the stack root)
away_port = port in self.away_from_root_stack_ports
# Otherwise it is towards the stack root
towards_port = not away_port

# Prune == True for ports that do not need to be flooded
if towards_port:
# If towards the stack root, then if the port is not the chosen
# root path port, then we do not need to flood to it
prune = port != towards_up_port
if not prune and not self.is_stack_root():
# Port is chosen towards port and not the root so flood
# towards the root
towards_flood_ports.append(port)
else:
# If away from stack root, then if the port is not the chosen
# away port for that DP, we do not need to flood to it
prune = port != away_up_port
if not prune and self.is_stack_root():
# Port is chosen away port and the root switch
# so flood away from the root
away_flood_ports.append(port)

# Also need to turn off inactive away ports (for DPs that have a better way to get to root)
exclude_ports = self._inactive_away_stack_ports()
away_flood_ports = [port for port in away_flood_ports if port not in exclude_ports]
return towards_flood_ports + away_flood_ports


class ValveFloodStackManagerNoReflection(ValveFloodStackManagerBase):
"""Stacks of size 2 - all switches directly connected to root.
Expand Down
33 changes: 30 additions & 3 deletions faucet/valve_route.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ class ValveRouteManager(ValveManagerBase):
'route_priority',
'routers',
'vip_table',
'flood_manager',
]

IPV = 0
Expand All @@ -123,7 +124,7 @@ class ValveRouteManager(ValveManagerBase):
def __init__(self, logger, notify, global_vlan, neighbor_timeout,
max_hosts_per_resolve_cycle, max_host_fib_retry_count,
max_resolve_backoff_time, proactive_learn, dec_ttl, multi_out,
fib_table, vip_table, pipeline, routers):
fib_table, vip_table, pipeline, routers, flood_manager):
self.notify = notify
self.logger = logger
self.global_vlan = AnonVLAN(global_vlan)
Expand All @@ -141,6 +142,7 @@ def __init__(self, logger, notify, global_vlan, neighbor_timeout,
self.routers = routers
self.active = False
self.global_routing = self._global_routing()
self.flood_manager = flood_manager
if self.global_routing:
self.logger.info('global routing enabled')

Expand Down Expand Up @@ -170,12 +172,37 @@ def _gw_resolve_pkt():
def _gw_respond_pkt():
return None

def _flood_stack_links(self, pkt_builder, vlan, multi_out=True, *args):
"""Return flood packet-out actions to stack ports for gw resolving"""
ofmsgs = []
if self.flood_manager:
ports = self.flood_manager._stack_flood_ports()
if ports:
running_port_nos = [port.number for port in ports if port.running()]
pkt = pkt_builder(vlan.vid, *args)
if running_port_nos:
random.shuffle(running_port_nos)
if multi_out:
ofmsgs.append(valve_of.packetouts(running_port_nos, pkt.data))
else:
ofmsgs.extend(
[valve_of.packetout(port_no, pkt.data) for port_no in running_port_nos])
return ofmsgs

def _resolve_gw_on_vlan(self, vlan, faucet_vip, ip_gw):
"""Return flood packet-out actions for gw resolving"""
# TODO: in multi DP routing, need to flood out stack ports as well
return vlan.flood_pkt(
ofmsgs = []
stack_ofmsgs = self._flood_stack_links(
self._gw_resolve_pkt(), vlan, self.multi_out,
vlan.faucet_mac, valve_of.mac.BROADCAST_STR, faucet_vip.ip, ip_gw)
if stack_ofmsgs:
ofmsgs.extend(stack_ofmsgs)
vlan_ofmsgs = vlan.flood_pkt(
self._gw_resolve_pkt(), self.multi_out,
vlan.faucet_mac, valve_of.mac.BROADCAST_STR, faucet_vip.ip, ip_gw)
if vlan_ofmsgs:
ofmsgs.extend(vlan_ofmsgs)
return ofmsgs

def _resolve_gw_on_port(self, vlan, port, faucet_vip, ip_gw, eth_dst):
"""Return packet-out actions for outputting to a specific port"""
Expand Down
Loading

0 comments on commit dc8f363

Please sign in to comment.