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:
eran gampel 2015-08-13 15:22:58 +03:00
parent e9d57e3a21
commit b12d5ac014
2 changed files with 120 additions and 18 deletions

View File

@ -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(

View File

@ -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" %