Merge "[OpenSwan] Enable usage of the MTU value of an IPSec connection"

This commit is contained in:
Jenkins 2015-09-20 05:29:33 +00:00 committed by Gerrit Code Review
commit d399aa8972
4 changed files with 107 additions and 50 deletions

View File

@ -28,6 +28,11 @@ conn {{ipsec_site_connection.id}}
# [subnet] # [subnet]
leftsubnet={{vpnservice.subnet.cidr}} leftsubnet={{vpnservice.subnet.cidr}}
# leftsubnet=networkA/netmaskA, networkB/netmaskB (IKEv2 only) # leftsubnet=networkA/netmaskA, networkB/netmaskB (IKEv2 only)
# [updown]
# What "updown" script to run to adjust routing and/or firewalling when
# the status of the connection changes (default "ipsec _updown").
# "--route yes" allows to specify such routing options as mtu and metric.
leftupdown="ipsec _updown --route yes"
###################### ######################
# ipsec_site_connections # ipsec_site_connections
###################### ######################
@ -39,8 +44,7 @@ conn {{ipsec_site_connection.id}}
rightsubnets={ {{ipsec_site_connection['peer_cidrs']|join(' ')}} } rightsubnets={ {{ipsec_site_connection['peer_cidrs']|join(' ')}} }
# rightsubnet=networkA/netmaskA, networkB/netmaskB (IKEv2 only) # rightsubnet=networkA/netmaskA, networkB/netmaskB (IKEv2 only)
# [mtu] # [mtu]
# Note It looks like not supported in the strongswan driver mtu={{ipsec_site_connection.mtu}}
# ignore it now
# [dpd_action] # [dpd_action]
dpdaction={{ipsec_site_connection.dpd_action}} dpdaction={{ipsec_site_connection.dpd_action}}
# [dpd_interval] # [dpd_interval]

View File

@ -155,14 +155,13 @@ def get_ovs_bridge(br_name):
return ovs_lib.OVSBridge(br_name) return ovs_lib.OVSBridge(br_name)
class TestIPSecScenario(base.BaseSudoTestCase): class TestIPSecBase(base.BaseSudoTestCase):
vpn_agent_ini = os.environ.get('VPN_AGENT_INI', vpn_agent_ini = os.environ.get('VPN_AGENT_INI',
'/etc/neutron/vpn_agent.ini') '/etc/neutron/vpn_agent.ini')
NESTED_NAMESPACE_SEPARATOR = '@' NESTED_NAMESPACE_SEPARATOR = '@'
def setUp(self): def setUp(self):
super(TestIPSecScenario, self).setUp() super(TestIPSecBase, self).setUp()
mock.patch('neutron.agent.l3.agent.L3PluginApi').start() mock.patch('neutron.agent.l3.agent.L3PluginApi').start()
# avoid report_status running periodically # avoid report_status running periodically
mock.patch('oslo_service.loopingcall.FixedIntervalLoopingCall').start() mock.patch('oslo_service.loopingcall.FixedIntervalLoopingCall').start()
@ -172,7 +171,12 @@ class TestIPSecScenario(base.BaseSudoTestCase):
# root_helper_daemon and instead use root_helper # root_helper_daemon and instead use root_helper
# https://bugs.launchpad.net/neutron/+bug/1482622 # https://bugs.launchpad.net/neutron/+bug/1482622
cfg.CONF.set_override('root_helper_daemon', None, group='AGENT') cfg.CONF.set_override('root_helper_daemon', None, group='AGENT')
self.fake_vpn_service = copy.deepcopy(FAKE_VPN_SERVICE)
self.fake_ipsec_connection = copy.deepcopy(FAKE_IPSEC_CONNECTION)
self.vpn_agent = self._configure_agent('agent1') self.vpn_agent = self._configure_agent('agent1')
self.driver = self.vpn_agent.device_drivers[0]
def connect_agents(self, agent1, agent2): def connect_agents(self, agent1, agent2):
"""Simulate both agents in the same host. """Simulate both agents in the same host.
@ -301,7 +305,7 @@ class TestIPSecScenario(base.BaseSudoTestCase):
return agent.router_info[router['id']] return agent.router_info[router['id']]
def prepare_vpn_service_info(self, router_id, external_ip, subnet_cidr): def prepare_vpn_service_info(self, router_id, external_ip, subnet_cidr):
service = copy.deepcopy(FAKE_VPN_SERVICE) service = copy.deepcopy(self.fake_vpn_service)
service.update({ service.update({
'id': _uuid(), 'id': _uuid(),
'router_id': router_id, 'router_id': router_id,
@ -310,7 +314,7 @@ class TestIPSecScenario(base.BaseSudoTestCase):
return service return service
def prepare_ipsec_conn_info(self, vpn_service, peer_vpn_service): def prepare_ipsec_conn_info(self, vpn_service, peer_vpn_service):
ipsec_conn = copy.deepcopy(FAKE_IPSEC_CONNECTION) ipsec_conn = copy.deepcopy(self.fake_ipsec_connection)
ipsec_conn.update({ ipsec_conn.update({
'id': _uuid(), 'id': _uuid(),
'vpnservice_id': vpn_service['id'], 'vpnservice_id': vpn_service['id'],
@ -402,8 +406,7 @@ class TestIPSecScenario(base.BaseSudoTestCase):
break break
return pm.active return pm.active
def test_ipsec_site_connections(self): def _create_ipsec_site_connection(self, l3ha=False):
device = self.vpn_agent.device_drivers[0]
# Mock the method below because it causes Exception: # Mock the method below because it causes Exception:
# RuntimeError: Second simultaneous read on fileno 5 detected. # RuntimeError: Second simultaneous read on fileno 5 detected.
# Unless you really know what you're doing, make sure that only # Unless you really know what you're doing, make sure that only
@ -414,11 +417,14 @@ class TestIPSecScenario(base.BaseSudoTestCase):
ip_lib.send_ip_addr_adv_notif = mock.Mock() ip_lib.send_ip_addr_adv_notif = mock.Mock()
# There are no vpn services yet. get_vpn_services_on_host returns # There are no vpn services yet. get_vpn_services_on_host returns
# empty list # empty list
device.agent_rpc.get_vpn_services_on_host = mock.Mock( self.driver.agent_rpc.get_vpn_services_on_host = mock.Mock(
return_value=[]) return_value=[])
# instantiate network resources "router", "private network" # instantiate network resources "router", "private network"
private_nets = list(PRIVATE_NET.subnet(24)) private_nets = list(PRIVATE_NET.subnet(24))
site1 = self.site_setup(PUBLIC_NET[4], private_nets[1]) site1 = self.site_setup(PUBLIC_NET[4], private_nets[1])
if l3ha:
site2 = self.setup_ha_routers(PUBLIC_NET[5], private_nets[2])
else:
site2 = self.site_setup(PUBLIC_NET[5], private_nets[2]) site2 = self.site_setup(PUBLIC_NET[5], private_nets[2])
# build vpn resources # build vpn resources
self.prepare_ipsec_conn_info(site1['vpn_service'], self.prepare_ipsec_conn_info(site1['vpn_service'],
@ -426,20 +432,34 @@ class TestIPSecScenario(base.BaseSudoTestCase):
self.prepare_ipsec_conn_info(site2['vpn_service'], self.prepare_ipsec_conn_info(site2['vpn_service'],
site1['vpn_service']) site1['vpn_service'])
device.report_status = mock.Mock() self.driver.report_status = mock.Mock()
device.agent_rpc.get_vpn_services_on_host = mock.Mock( self.driver.agent_rpc.get_vpn_services_on_host = mock.Mock(
return_value=[site1['vpn_service'], return_value=[site1['vpn_service'],
site2['vpn_service']]) site2['vpn_service']])
if l3ha:
self.failover_agent_driver.agent_rpc.get_vpn_services_on_host = (
mock.Mock(return_value=[]))
self.failover_agent_driver.report_status = mock.Mock()
self.failover_agent_driver.agent_rpc.get_vpn_services_on_host = (
mock.Mock(return_value=[site2['vpn_service']]))
return site1, site2
class TestIPSecScenario(TestIPSecBase):
def test_ipsec_site_connections(self):
site1, site2 = self._create_ipsec_site_connection()
net_helpers.assert_no_ping(site1['port_namespace'], site2['port_ip'], net_helpers.assert_no_ping(site1['port_namespace'], site2['port_ip'],
timeout=8, count=4) timeout=8, count=4)
net_helpers.assert_no_ping(site2['port_namespace'], site1['port_ip'], net_helpers.assert_no_ping(site2['port_namespace'], site1['port_ip'],
timeout=8, count=4) timeout=8, count=4)
device.sync(mock.Mock(), [{'id': site1['router'].router_id}, self.driver.sync(mock.Mock(), [{'id': site1['router'].router_id},
{'id': site2['router'].router_id}]) {'id': site2['router'].router_id}])
self.addCleanup( self.addCleanup(
device._delete_vpn_processes, self.driver._delete_vpn_processes,
[site1['router'].router_id, site2['router'].router_id], []) [site1['router'].router_id, site2['router'].router_id], [])
net_helpers.assert_ping(site1['port_namespace'], site2['port_ip'], net_helpers.assert_ping(site1['port_namespace'], site2['port_ip'],
@ -466,39 +486,14 @@ class TestIPSecScenario(base.BaseSudoTestCase):
self.connect_agents(self.vpn_agent, self.failover_agent) self.connect_agents(self.vpn_agent, self.failover_agent)
vpn_agent_driver = self.vpn_agent.device_drivers[0] vpn_agent_driver = self.vpn_agent.device_drivers[0]
failover_agent_driver = self.failover_agent.device_drivers[0] self.failover_agent_driver = self.failover_agent.device_drivers[0]
ip_lib.send_ip_addr_adv_notif = mock.Mock()
# There are no vpn services yet. get_vpn_services_on_host returns site1, site2 = self._create_ipsec_site_connection(l3ha=True)
# empty list
vpn_agent_driver.agent_rpc.get_vpn_services_on_host = mock.Mock(
return_value=[])
failover_agent_driver.agent_rpc.get_vpn_services_on_host = mock.Mock(
return_value=[])
# instantiate network resources "router", "private network"
private_nets = list(PRIVATE_NET.subnet(24))
site1 = self.site_setup(PUBLIC_NET[4], private_nets[1])
site2 = self.setup_ha_routers(PUBLIC_NET[5], private_nets[2])
router = site1['router'] router = site1['router']
router1 = site2['router1'] router1 = site2['router1']
router2 = site2['router2'] router2 = site2['router2']
# build vpn resources
self.prepare_ipsec_conn_info(site1['vpn_service'],
site2['vpn_service'])
self.prepare_ipsec_conn_info(site2['vpn_service'],
site1['vpn_service'])
vpn_agent_driver.report_status = mock.Mock()
failover_agent_driver.report_status = mock.Mock()
vpn_agent_driver.agent_rpc.get_vpn_services_on_host = mock.Mock(
return_value=[site1['vpn_service'],
site2['vpn_service']])
failover_agent_driver.agent_rpc.get_vpn_services_on_host = mock.Mock(
return_value=[site2['vpn_service']])
# No ipsec connection between legacy router and HA routers # No ipsec connection between legacy router and HA routers
net_helpers.assert_no_ping(site1['port_namespace'], site2['port_ip'], net_helpers.assert_no_ping(site1['port_namespace'], site2['port_ip'],
timeout=8, count=4) timeout=8, count=4)
@ -508,7 +503,8 @@ class TestIPSecScenario(base.BaseSudoTestCase):
# sync the routers # sync the routers
vpn_agent_driver.sync(mock.Mock(), [{'id': router.router_id}, vpn_agent_driver.sync(mock.Mock(), [{'id': router.router_id},
{'id': router1.router_id}]) {'id': router1.router_id}])
failover_agent_driver.sync(mock.Mock(), [{'id': router1.router_id}]) self.failover_agent_driver.sync(mock.Mock(),
[{'id': router1.router_id}])
self.addCleanup( self.addCleanup(
vpn_agent_driver._delete_vpn_processes, vpn_agent_driver._delete_vpn_processes,
@ -529,7 +525,8 @@ class TestIPSecScenario(base.BaseSudoTestCase):
# wait until ipsec process running in failover agent's HA router # wait until ipsec process running in failover agent's HA router
# check for both strongswan and openswan processes # check for both strongswan and openswan processes
path = failover_agent_driver.processes[router2.router_id].config_dir path = self.failover_agent_driver.processes[
router2.router_id].config_dir
pid_files = ['%s/var/run/charon.pid' % path, pid_files = ['%s/var/run/charon.pid' % path,
'%s/var/run/pluto.pid' % path] '%s/var/run/pluto.pid' % path]
linux_utils.wait_until_true( linux_utils.wait_until_true(

View File

@ -20,16 +20,34 @@
# functional test for the OpenSwan reference implementation. For now, just # functional test for the OpenSwan reference implementation. For now, just
# ignore the test cases herein. # ignore the test cases herein.
from neutron.tests.functional import base import mock
from neutron.agent.linux import ip_lib
from neutron.agent.linux import utils as linux_utils
from neutron_vpnaas.tests.functional.common import test_scenario
class TestOpenSwanDeviceDriver(base.BaseSudoTestCase): class TestOpenSwanDeviceDriver(test_scenario.TestIPSecBase):
"""Test the OpenSwan reference implmentation of the device driver.""" """Test the OpenSwan reference implementation of the device driver."""
# NOTE: Tests may be added/removed/changed, when this is fleshed out # NOTE: Tests may be added/removed/changed, when this is fleshed out
# in future commits. # in future commits.
def _ping_mtu(self, namespace, ip, size):
"""Pings ip address using packets of given size and with DF=1.
In order to ping it uses following cli command:
ip netns exec <namespace> ping -c 4 -M do -s <size> <ip>
"""
try:
cmd = ['ping', '-c', 4, '-M', 'do', '-s', size, ip]
cmd = ip_lib.add_namespace_to_cmd(cmd, namespace)
linux_utils.execute(cmd, run_as_root=True)
return True
except RuntimeError:
return False
def test_config_files_created_on_ipsec_connection_create(self): def test_config_files_created_on_ipsec_connection_create(self):
"""Verify that directory and config files are correct on create.""" """Verify that directory and config files are correct on create."""
pass pass
@ -64,3 +82,30 @@ class TestOpenSwanDeviceDriver(base.BaseSudoTestCase):
def test_status_reporting(self): def test_status_reporting(self):
"""Test status reported correctly to agent.""" """Test status reported correctly to agent."""
pass pass
def test_ipsec_site_connections_mtu_enforcement(self):
"""Test that mtu of ipsec site connections is enforced."""
# Set up non-default mtu value
self.fake_ipsec_connection['mtu'] = 1200
# Establish an ipsec connection between two sites
site1, site2 = self._create_ipsec_site_connection()
self.driver.sync(mock.Mock(), [{'id': site1['router'].router_id},
{'id': site2['router'].router_id}])
self.addCleanup(
self.driver._delete_vpn_processes,
[site1['router'].router_id, site2['router'].router_id], [])
# Validate that ip packets with 1172 (1200) bytes of data pass
self.assertTrue(self._ping_mtu(site1['port_namespace'],
site2['port_ip'], 1172))
self.assertTrue(self._ping_mtu(site2['port_namespace'],
site1['port_ip'], 1172))
# Validate that ip packets with 1173 (1201) bytes of data are dropped
self.assertFalse(self._ping_mtu(site1['port_namespace'],
site2['port_ip'], 1173))
self.assertFalse(self._ping_mtu(site2['port_namespace'],
site1['port_ip'], 1173))

View File

@ -67,6 +67,7 @@ FAKE_VPN_SERVICE = {
'id': FAKE_IPSEC_SITE_CONNECTION1_ID, 'id': FAKE_IPSEC_SITE_CONNECTION1_ID,
'external_ip': '50.0.0.4', 'external_ip': '50.0.0.4',
'peer_address': '30.0.0.5', 'peer_address': '30.0.0.5',
'mtu': 1500,
'peer_id': '30.0.0.5', 'peer_id': '30.0.0.5',
'psk': 'password', 'psk': 'password',
'initiator': 'bi-directional', 'initiator': 'bi-directional',
@ -79,6 +80,7 @@ FAKE_VPN_SERVICE = {
'external_ip': '50.0.0.4', 'external_ip': '50.0.0.4',
'peer_address': '50.0.0.5', 'peer_address': '50.0.0.5',
'peer_id': '50.0.0.5', 'peer_id': '50.0.0.5',
'mtu': 1500,
'psk': 'password', 'psk': 'password',
'id': FAKE_IPSEC_SITE_CONNECTION2_ID, 'id': FAKE_IPSEC_SITE_CONNECTION2_ID,
'initiator': 'bi-directional', 'initiator': 'bi-directional',
@ -99,8 +101,7 @@ AUTH_AH = '''ah
OPENSWAN_CONNECTION_DETAILS = '''# rightsubnet=networkA/netmaskA, networkB/netmaskB (IKEv2 only) OPENSWAN_CONNECTION_DETAILS = '''# rightsubnet=networkA/netmaskA, networkB/netmaskB (IKEv2 only)
# [mtu] # [mtu]
# Note It looks like not supported in the strongswan driver mtu=1500
# ignore it now
# [dpd_action] # [dpd_action]
dpdaction= dpdaction=
# [dpd_interval] # [dpd_interval]
@ -152,6 +153,11 @@ conn %(conn1_id)s
# [subnet] # [subnet]
leftsubnet=10.0.0.0/24 leftsubnet=10.0.0.0/24
# leftsubnet=networkA/netmaskA, networkB/netmaskB (IKEv2 only) # leftsubnet=networkA/netmaskA, networkB/netmaskB (IKEv2 only)
# [updown]
# What "updown" script to run to adjust routing and/or firewalling when
# the status of the connection changes (default "ipsec _updown").
# "--route yes" allows to specify such routing options as mtu and metric.
leftupdown="ipsec _updown --route yes"
###################### ######################
# ipsec_site_connections # ipsec_site_connections
###################### ######################
@ -172,6 +178,11 @@ conn %(conn1_id)s
# [subnet] # [subnet]
leftsubnet=10.0.0.0/24 leftsubnet=10.0.0.0/24
# leftsubnet=networkA/netmaskA, networkB/netmaskB (IKEv2 only) # leftsubnet=networkA/netmaskA, networkB/netmaskB (IKEv2 only)
# [updown]
# What "updown" script to run to adjust routing and/or firewalling when
# the status of the connection changes (default "ipsec _updown").
# "--route yes" allows to specify such routing options as mtu and metric.
leftupdown="ipsec _updown --route yes"
###################### ######################
# ipsec_site_connections # ipsec_site_connections
###################### ######################