Change ARP reply MAC to router interface
In some scenario, dvr router interface will try to ARP some device which is not hosted in the same host. When the ARP request send out, the ethernet source MAC will be changed to dvr_host_mac. Then thoses devices will reply ARP with the dvr_host_mac in ethernet dest MAC. So finally the dvr router interface will drop this, and the ARP get failed. This patch adds one flow for this, it will match the dest MAC, ARP op-code=2 and arp_tha address, then change the dest MAC to the right router interface's MAC address. Closes-Bug: #1913646 Related-Bug: #1859638 Change-Id: Ibc7f01450a3da026ca5c4fb667dada912cf472e3
This commit is contained in:
parent
00d5f7f2ab
commit
7be2dc976a
|
@ -14,6 +14,7 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from os_ken.lib.packet import arp
|
||||
from os_ken.lib.packet import ether_types
|
||||
from os_ken.lib.packet import icmpv6
|
||||
from os_ken.lib.packet import in_proto
|
||||
|
@ -21,6 +22,35 @@ from os_ken.lib.packet import in_proto
|
|||
from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants
|
||||
|
||||
|
||||
class OVSDVRInterfaceMixin(object):
|
||||
|
||||
def change_arp_destination_mac(self, target_mac_address,
|
||||
orig_mac_address):
|
||||
"""Change destination MAC from dvr_host_mac to internal gateway MAC.
|
||||
"""
|
||||
(_dp, ofp, ofpp) = self._get_dp()
|
||||
# TODO(liuyulong): except ARP, reconsider if we can change all IPv4
|
||||
# packet's destination MAC to internal gateway MAC based
|
||||
# on the match rule nw_dst=gateway_ip.
|
||||
match = ofpp.OFPMatch(eth_dst=orig_mac_address,
|
||||
eth_type=ether_types.ETH_TYPE_ARP,
|
||||
arp_tha=target_mac_address,
|
||||
arp_op=arp.ARP_REPLY)
|
||||
|
||||
# dst_mac: dvr_host_mac -> qr_dev_mac
|
||||
actions = [
|
||||
ofpp.OFPActionSetField(eth_dst=target_mac_address),
|
||||
]
|
||||
instructions = [
|
||||
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions),
|
||||
ofpp.OFPInstructionGotoTable(table_id=constants.TRANSIENT_TABLE)]
|
||||
|
||||
self.install_instructions(table_id=constants.LOCAL_SWITCHING,
|
||||
priority=99,
|
||||
match=match,
|
||||
instructions=instructions)
|
||||
|
||||
|
||||
class OVSDVRProcessMixin(object):
|
||||
"""Common logic for br-tun and br-phys' DVR_PROCESS tables.
|
||||
|
||||
|
|
|
@ -27,6 +27,8 @@ from os_ken.lib.packet import in_proto
|
|||
from oslo_log import log as logging
|
||||
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native \
|
||||
import br_dvr_process
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native \
|
||||
import ovs_bridge
|
||||
|
||||
|
@ -34,7 +36,8 @@ from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native \
|
|||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OVSIntegrationBridge(ovs_bridge.OVSAgentBridge):
|
||||
class OVSIntegrationBridge(ovs_bridge.OVSAgentBridge,
|
||||
br_dvr_process.OVSDVRInterfaceMixin):
|
||||
"""openvswitch agent br-int specific logic."""
|
||||
|
||||
of_tables = constants.INT_BR_ALL_TABLES
|
||||
|
|
|
@ -515,6 +515,14 @@ class OVSDVRNeutronAgent(object):
|
|||
self.local_ports[port.vif_id] = ovsport
|
||||
ldm.add_dvr_ofport(port.vif_id, port.ofport)
|
||||
|
||||
if (ip_version == n_const.IP_VERSION_4 and
|
||||
subnet_info.get('gateway_mac')):
|
||||
# Change ARP reply destination MAC address from
|
||||
# dvr_host_mac to gateway_mac.
|
||||
self.int_br.change_arp_destination_mac(
|
||||
target_mac_address=subnet_info['gateway_mac'],
|
||||
orig_mac_address=self.dvr_mac_address)
|
||||
|
||||
def _bind_port_on_dvr_subnet(self, port, lvm, fixed_ips,
|
||||
device_owner):
|
||||
# Handle new compute port added use-case
|
||||
|
|
|
@ -3129,6 +3129,7 @@ class TestOvsDvrNeutronAgent(object):
|
|||
phys_br = mock.create_autospec(self.br_phys_cls('br-phys'))
|
||||
int_br.set_db_attribute.return_value = True
|
||||
int_br.db_get_val.return_value = {}
|
||||
int_br.change_arp_destination_mac = mock.Mock()
|
||||
with mock.patch.object(self.agent.dvr_agent.plugin_rpc,
|
||||
'get_subnet_for_dvr',
|
||||
return_value={'gateway_ip': gateway_ip,
|
||||
|
@ -3176,6 +3177,11 @@ class TestOvsDvrNeutronAgent(object):
|
|||
),
|
||||
] + self._expected_port_bound(self._port, lvid,
|
||||
network_type=network_type)
|
||||
if ip_version == n_const.IP_VERSION_4:
|
||||
expected_on_int_br += [
|
||||
mock.call.change_arp_destination_mac(
|
||||
target_mac_address=gateway_mac,
|
||||
orig_mac_address=self.agent.dvr_agent.dvr_mac_address)]
|
||||
int_br.assert_has_calls(expected_on_int_br, any_order=True)
|
||||
tun_br.assert_not_called()
|
||||
phys_br.assert_has_calls(expected_on_phys_br)
|
||||
|
@ -3222,6 +3228,7 @@ class TestOvsDvrNeutronAgent(object):
|
|||
phys_br = mock.create_autospec(self.br_phys_cls('br-phys'))
|
||||
int_br.set_db_attribute.return_value = True
|
||||
int_br.db_get_val.return_value = {}
|
||||
int_br.change_arp_destination_mac = mock.Mock()
|
||||
with mock.patch.object(self.agent.dvr_agent.plugin_rpc,
|
||||
'get_subnet_for_dvr',
|
||||
return_value={'gateway_ip': gateway_ip,
|
||||
|
@ -3249,6 +3256,11 @@ class TestOvsDvrNeutronAgent(object):
|
|||
lvid = self.agent.vlan_manager.get(self._net_uuid).vlan
|
||||
expected_on_int_br = self._expected_port_bound(
|
||||
self._port, lvid)
|
||||
if ip_version == n_const.IP_VERSION_4:
|
||||
expected_on_int_br += [
|
||||
mock.call.change_arp_destination_mac(
|
||||
target_mac_address=gateway_mac,
|
||||
orig_mac_address=self.agent.dvr_agent.dvr_mac_address)]
|
||||
expected_on_tun_br = [
|
||||
mock.call.provision_local_vlan(
|
||||
network_type=network_type,
|
||||
|
|
Loading…
Reference in New Issue