Unicast reactive L3 flows to the correct tunnel port
In order to avoid flooding and learning for the L3 traffic The
controller marks in the flow the address of the destination VM compute
Node.
We use local vlan mapping to map the tunnel ports destination ip
In the tunnel bridge unicast_to_tunnel table we pre populate match for
the mapped vlan tag and the remote tunnel port.
This solution is needed only for software base tunneling (GRE,VxLAN..)
and not for VLAN base segmentation.
The proposed VLAN mapping is totally isolated from the integration br
VLAN mapping, different pipeline (NORMAL, Openflow).
Closes-Bug: 1480760
(merged form commit 85b0928b84
)
Change-Id: Ieaf07ad6fdb6f9d24652ea05444a10a7860b386c
This commit is contained in:
parent
e9d57e3a21
commit
b12d5ac014
|
@ -64,6 +64,7 @@ UINT32_MAX = 0xffffffff
|
|||
UINT64_MAX = 0xffffffffffffffff
|
||||
OFPFW_NW_PROTO = 1 << 5
|
||||
|
||||
REG_32BIT_ON_MASK = 0x80000000
|
||||
|
||||
HIGH_PRIORITY_FLOW = 1000
|
||||
MEDIUM_PRIORITY_FLOW = 100
|
||||
|
@ -942,6 +943,7 @@ class L3ReactiveApp(app_manager.RyuApp):
|
|||
local_switch.patch_port_num,
|
||||
dst_seg_id=dst_seg_id,
|
||||
cookie=cookie,
|
||||
dst_datapath=remote_switch.datapath,
|
||||
)
|
||||
|
||||
# Remote reverse flow install
|
||||
|
@ -960,6 +962,7 @@ class L3ReactiveApp(app_manager.RyuApp):
|
|||
remote_switch.patch_port_num,
|
||||
dst_seg_id=src_seg_id,
|
||||
cookie=cookie,
|
||||
dst_datapath=local_switch.datapath,
|
||||
)
|
||||
|
||||
self.handle_packet_out_l3(remote_switch.datapath,
|
||||
|
@ -980,7 +983,7 @@ class L3ReactiveApp(app_manager.RyuApp):
|
|||
src_seg_id, match_src_mac, match_dst_mac,
|
||||
match_dst_ip, match_src_ip, src_mac,
|
||||
dst_mac, out_port_num, dst_seg_id=None,
|
||||
cookie=0):
|
||||
cookie=0, dst_datapath=None):
|
||||
parser = datapath.ofproto_parser
|
||||
match = parser.OFPMatch()
|
||||
match.set_dl_type(ether.ETH_TYPE_IP)
|
||||
|
@ -996,11 +999,19 @@ class L3ReactiveApp(app_manager.RyuApp):
|
|||
actions.append(parser.OFPActionDecNwTtl())
|
||||
actions.append(parser.OFPActionSetField(eth_src=src_mac))
|
||||
actions.append(parser.OFPActionSetField(eth_dst=dst_mac))
|
||||
|
||||
if dst_datapath:
|
||||
dst_ip_hex = self._get_dp_ip_as_int(dst_datapath)
|
||||
if ryu.version_info >= (3, 24):
|
||||
#register Action set is supported only in 3.24
|
||||
actions.append(parser.OFPActionSetField(reg7=dst_ip_hex))
|
||||
|
||||
if dst_seg_id:
|
||||
# The dest vm is on another compute machine so we must set the
|
||||
# segmentation Id and set metadata for the tunnel bridge to
|
||||
# for this flow
|
||||
field = parser.OFPActionSetField(tunnel_id=dst_seg_id)
|
||||
mask_dst_seg = int(dst_seg_id) | REG_32BIT_ON_MASK
|
||||
field = parser.OFPActionSetField(tunnel_id=mask_dst_seg)
|
||||
actions.append(field)
|
||||
goto_inst = parser.OFPInstructionGotoTable(
|
||||
self.TUN_TRANSLATE_TABLE)
|
||||
|
@ -1388,7 +1399,7 @@ class L3ReactiveApp(app_manager.RyuApp):
|
|||
datapath,
|
||||
self.TUN_TRANSLATE_TABLE,
|
||||
port.port_no,
|
||||
HIGH_PRIORITY_FLOW)
|
||||
LOW_PRIORITY_FLOW)
|
||||
LOG.debug('OFPPortDescStatsReply received: %s', ports)
|
||||
switch.local_ports = ports
|
||||
#TODO(gampel) Install flows only for tenants with VMs running on
|
||||
|
@ -1498,6 +1509,13 @@ class L3ReactiveApp(app_manager.RyuApp):
|
|||
def check_direct_routing(self, tenant, from_subnet_id, to_subnet_id):
|
||||
return
|
||||
|
||||
def _get_dp_ip_as_int(self, datapath):
|
||||
try:
|
||||
return int(netaddr.IPAddress(datapath.address[0], version=4))
|
||||
except Exception:
|
||||
LOG.warn(_LW("Invalid remote IP: %s"), datapath.address)
|
||||
return
|
||||
|
||||
def _get_match_vrouter_arp_responder(self, datapath, segmentation_id,
|
||||
interface_ip):
|
||||
parser = datapath.ofproto_parser
|
||||
|
@ -1780,10 +1798,9 @@ class L3ReactiveApp(app_manager.RyuApp):
|
|||
match = parser.OFPMatch()
|
||||
match.set_dl_type(ether.ETH_TYPE_IP)
|
||||
actions = [parser.NXActionRegMove(src_field='tunnel_id',
|
||||
dst_field='pkt_mark',
|
||||
n_bits=32),
|
||||
dst_field='pkt_mark',
|
||||
n_bits=32),
|
||||
parser.OFPActionOutput(port=port)]
|
||||
|
||||
instructions = [parser.OFPInstructionActions(
|
||||
ofproto.OFPIT_APPLY_ACTIONS, actions)]
|
||||
self.mod_flow(
|
||||
|
|
|
@ -20,6 +20,8 @@ import eventlet
|
|||
|
||||
eventlet.monkey_patch()
|
||||
|
||||
from six import moves
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from neutron.agent.common import config
|
||||
|
@ -27,6 +29,7 @@ from neutron.agent.linux import ip_lib
|
|||
from neutron.agent.ovsdb import api as ovsdb
|
||||
|
||||
from neutron.common import config as common_config
|
||||
from neutron.common import constants as q_const
|
||||
from neutron.common import utils as q_utils
|
||||
|
||||
from neutron.i18n import _, _LE, _LI
|
||||
|
@ -43,10 +46,13 @@ agent_additional_opts = [
|
|||
help=("L3 Controler IP list list tcp:ip_addr:port;"
|
||||
"tcp:ip_addr:port..;..")),
|
||||
cfg.BoolOpt('enable_l3_controller', default=True,
|
||||
help=_("L3 SDN Controller"))
|
||||
help=_("L3 SDN Controller")),
|
||||
cfg.IntOpt('tunnel_map_check_rate', default=5,
|
||||
help=_("Rate in multiple of the rpc loop")),
|
||||
]
|
||||
|
||||
cfg.CONF.register_opts(agent_additional_opts, "AGENT")
|
||||
TUN_TRANSLATE_TABLE = 60
|
||||
|
||||
|
||||
class L2OVSControllerAgent(ona.OVSNeutronAgent):
|
||||
|
@ -74,6 +80,7 @@ class L2OVSControllerAgent(ona.OVSNeutronAgent):
|
|||
'''
|
||||
self.set_controller_lock = threading.Lock()
|
||||
self.enable_l3_controller = cfg.CONF.AGENT.enable_l3_controller
|
||||
self.tunnel_map_check_rate = cfg.CONF.AGENT.tunnel_map_check_rate
|
||||
|
||||
super(L2OVSControllerAgent, self) \
|
||||
.__init__(integ_br,
|
||||
|
@ -90,8 +97,12 @@ class L2OVSControllerAgent(ona.OVSNeutronAgent):
|
|||
use_veth_interconnection,
|
||||
quitting_rpc_timeout)
|
||||
|
||||
# Initialize controller
|
||||
self.df_available_local_vlans = set(moves.range(q_const.MIN_VLAN_TAG,
|
||||
q_const.MAX_VLAN_TAG))
|
||||
#mapping from vlan to destination tunnel ip
|
||||
self.df_local_to_vlan_map = {}
|
||||
self.controllers_ip_list = cfg.CONF.AGENT.L3controller_ip_list
|
||||
# Initialize controller
|
||||
self.set_controller_for_br(self.int_br, self.controllers_ip_list)
|
||||
|
||||
def set_controller_for_br(self, bridge, ip_address_list):
|
||||
|
@ -121,12 +132,7 @@ class L2OVSControllerAgent(ona.OVSNeutronAgent):
|
|||
if self.patch_tun_ofport > 0:
|
||||
# Mark the tunnel ID so the data will be transferred to the
|
||||
# br-tun virtual switch, tun id and metadata are local
|
||||
bridge.add_flow(table="60", priority=1,
|
||||
actions="move:NXM_NX_TUN_ID[0..31]"
|
||||
"->NXM_NX_PKT_MARK[],"
|
||||
"output:%s" %
|
||||
(self.patch_tun_ofport))
|
||||
|
||||
self._add_tun_translate_pack_mark_flow(bridge)
|
||||
# add the normal flow higher priority than the drop
|
||||
for br in self.phys_brs.values():
|
||||
br.add_flow(priority=3, actions="normal")
|
||||
|
@ -150,16 +156,75 @@ class L2OVSControllerAgent(ona.OVSNeutronAgent):
|
|||
local_vlan_map['physical_network'],
|
||||
local_vlan_map['segmentation_id'])
|
||||
|
||||
def _add_tun_translate_pack_mark_flow(self, bridge):
|
||||
bridge.add_flow(table=TUN_TRANSLATE_TABLE, priority=1,
|
||||
actions="move:NXM_NX_TUN_ID[0..31]"
|
||||
"->NXM_NX_PKT_MARK[],"
|
||||
"output:%s" %
|
||||
(self.patch_tun_ofport))
|
||||
|
||||
def _check_tunnel_map_table(self):
|
||||
"""Verify that the tunnel ip mapping is installed
|
||||
on the integration bridge
|
||||
"""
|
||||
|
||||
if p_const.TYPE_VLAN in self.tunnel_types:
|
||||
# TODO(gampel) check for the vlan flows here
|
||||
return
|
||||
if not self.df_local_to_vlan_map:
|
||||
return
|
||||
tunnel_flows = self.int_br.dump_flows_for_table(
|
||||
TUN_TRANSLATE_TABLE)
|
||||
for tunnel_ip in self.df_local_to_vlan_map:
|
||||
vlan_action = "mod_vlan_vid:%d" % (
|
||||
self.df_local_to_vlan_map[tunnel_ip])
|
||||
if vlan_action not in tunnel_flows:
|
||||
self.tunnel_sync()
|
||||
self._add_tun_translate_pack_mark_flow(self.int_br)
|
||||
|
||||
def check_ovs_status(self):
|
||||
if not self.enable_l3_controller:
|
||||
return super(L2OVSControllerAgent, self).check_ovs_status()
|
||||
|
||||
# Check for the canary flow
|
||||
# Add lock to avoid race condition of flows
|
||||
with self.set_controller_lock:
|
||||
ret = super(L2OVSControllerAgent, self).check_ovs_status()
|
||||
if not self.iter_num % self.tunnel_map_check_rate:
|
||||
self._check_tunnel_map_table()
|
||||
return ret
|
||||
|
||||
def _claim_df_tunnel_local_vlan(self, tunnel_ip_hex):
|
||||
lvid = None
|
||||
if tunnel_ip_hex in self.df_local_to_vlan_map:
|
||||
lvid = self.df_local_to_vlan_map[tunnel_ip_hex]
|
||||
else:
|
||||
lvid = self.df_available_local_vlans.pop()
|
||||
self.df_local_to_vlan_map[tunnel_ip_hex] = lvid
|
||||
return lvid
|
||||
|
||||
def _release_df_tunnel_local_vlan(self, tunnel_ip_hex):
|
||||
lvid = self.df_local_to_vlan_map.pop(tunnel_ip_hex, None)
|
||||
self.df_available_local_vlans.add(lvid)
|
||||
|
||||
def cleanup_tunnel_port(self, br, tun_ofport, tunnel_type):
|
||||
items = list(self.tun_br_ofports[tunnel_type].items())
|
||||
for remote_ip, ofport in items:
|
||||
if ofport == tun_ofport:
|
||||
tunnel_ip_hex = "0x%s" % self.get_ip_in_hex(remote_ip)
|
||||
lvid = self.df_local_to_vlan_map[tunnel_ip_hex]
|
||||
self.int_br.delete_flows(
|
||||
table=TUN_TRANSLATE_TABLE,
|
||||
reg7=tunnel_ip_hex)
|
||||
br.delete_flows(
|
||||
table=constants.UCAST_TO_TUN,
|
||||
dl_vlan=lvid)
|
||||
self._release_df_tunnel_local_vlan(tunnel_ip_hex)
|
||||
|
||||
return super(L2OVSControllerAgent, self).cleanup_tunnel_port(
|
||||
br,
|
||||
tun_ofport,
|
||||
tunnel_type)
|
||||
|
||||
def _setup_tunnel_port(self, br, port_name, remote_ip, tunnel_type):
|
||||
ofport = super(L2OVSControllerAgent, self) \
|
||||
._setup_tunnel_port(
|
||||
|
@ -167,14 +232,33 @@ class L2OVSControllerAgent(ona.OVSNeutronAgent):
|
|||
port_name,
|
||||
remote_ip,
|
||||
tunnel_type)
|
||||
if p_const.TYPE_VLAN not in self.tunnel_types:
|
||||
tunnel_ip_hex = "0x%s" % self.get_ip_in_hex(remote_ip)
|
||||
lvid = self._claim_df_tunnel_local_vlan(tunnel_ip_hex)
|
||||
self.int_br.add_flow(
|
||||
table=TUN_TRANSLATE_TABLE,
|
||||
priority=2000,
|
||||
reg7=tunnel_ip_hex,
|
||||
actions="mod_vlan_vid:%s,"
|
||||
"load:0->NXM_NX_REG7[0..31],"
|
||||
"resubmit(,%s)" %
|
||||
(lvid, TUN_TRANSLATE_TABLE))
|
||||
br.add_flow(table=constants.UCAST_TO_TUN,
|
||||
priority=100,
|
||||
dl_vlan=lvid,
|
||||
pkt_mark="0x80000000/0x80000000",
|
||||
actions="strip_vlan,move:NXM_NX_PKT_MARK[0..30]"
|
||||
"->NXM_NX_TUN_ID[0..30],"
|
||||
"output:%s" %
|
||||
(ofport))
|
||||
if ofport > 0:
|
||||
ofports = (ona._ofport_set_to_str
|
||||
(self.tun_br_ofports[tunnel_type].values()))
|
||||
if self.enable_l3_controller:
|
||||
if ofports:
|
||||
br.add_flow(table=constants.FLOOD_TO_TUN,
|
||||
actions="move:NXM_NX_PKT_MARK[]"
|
||||
"->NXM_NX_TUN_ID[0..31],"
|
||||
actions="move:NXM_NX_PKT_MARK[0..30]"
|
||||
"->NXM_NX_TUN_ID[0..30],"
|
||||
"output:%s" %
|
||||
(ofports))
|
||||
return ofport
|
||||
|
@ -186,7 +270,8 @@ class L2OVSControllerAgent(ona.OVSNeutronAgent):
|
|||
#outbound
|
||||
# The global vlan id is set in table 60
|
||||
# from segmentation id/tun id
|
||||
self.int_br.add_flow(table="60", priority=1,
|
||||
self.int_br.add_flow(table=TUN_TRANSLATE_TABLE,
|
||||
priority=1,
|
||||
actions="move:NXM_NX_TUN_ID[0..11]"
|
||||
"->OXM_OF_VLAN_VID[],"
|
||||
"output:%s" %
|
||||
|
|
Loading…
Reference in New Issue