diff --git a/dragonflow/controller/l3_openflow_app.py b/dragonflow/controller/l3_openflow_app.py index f0ad17b68..0283fea99 100755 --- a/dragonflow/controller/l3_openflow_app.py +++ b/dragonflow/controller/l3_openflow_app.py @@ -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) @@ -1401,7 +1412,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 @@ -1511,6 +1522,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 @@ -1793,10 +1811,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( diff --git a/dragonflow/neutron/agent/l2/ovs_dragonflow_neutron_agent.py b/dragonflow/neutron/agent/l2/ovs_dragonflow_neutron_agent.py index f6c3fed05..0940b063e 100644 --- a/dragonflow/neutron/agent/l2/ovs_dragonflow_neutron_agent.py +++ b/dragonflow/neutron/agent/l2/ovs_dragonflow_neutron_agent.py @@ -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" %