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

It is possible to specify the MTU parameter when creating IPSec Site
Connection but this parameter is ignored, because it is missing in
ipsec.conf.template.
This change adds the mtu option to OpenSwan ipsec.conf template.

Refactored existing test_scenario and added a functional test for
OpenSwan driver.

DocImpact

Change-Id: If822454a7acaa3fd003cae3e5e342c8b66ef656c
Closes-Bug: #1478949
This commit is contained in:
Elena Ezhova 2015-07-28 16:17:53 +03:00
parent 33814c49b0
commit db7ac1f4d0
4 changed files with 107 additions and 50 deletions

View File

@ -28,6 +28,11 @@ conn {{ipsec_site_connection.id}}
# [subnet]
leftsubnet={{vpnservice.subnet.cidr}}
# 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
######################
@ -39,8 +44,7 @@ conn {{ipsec_site_connection.id}}
rightsubnets={ {{ipsec_site_connection['peer_cidrs']|join(' ')}} }
# rightsubnet=networkA/netmaskA, networkB/netmaskB (IKEv2 only)
# [mtu]
# Note It looks like not supported in the strongswan driver
# ignore it now
mtu={{ipsec_site_connection.mtu}}
# [dpd_action]
dpdaction={{ipsec_site_connection.dpd_action}}
# [dpd_interval]

View File

@ -155,14 +155,13 @@ def get_ovs_bridge(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',
'/etc/neutron/vpn_agent.ini')
NESTED_NAMESPACE_SEPARATOR = '@'
def setUp(self):
super(TestIPSecScenario, self).setUp()
super(TestIPSecBase, self).setUp()
mock.patch('neutron.agent.l3.agent.L3PluginApi').start()
# avoid report_status running periodically
mock.patch('oslo_service.loopingcall.FixedIntervalLoopingCall').start()
@ -172,7 +171,12 @@ class TestIPSecScenario(base.BaseSudoTestCase):
# root_helper_daemon and instead use root_helper
# https://bugs.launchpad.net/neutron/+bug/1482622
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.driver = self.vpn_agent.device_drivers[0]
def connect_agents(self, agent1, agent2):
"""Simulate both agents in the same host.
@ -301,7 +305,7 @@ class TestIPSecScenario(base.BaseSudoTestCase):
return agent.router_info[router['id']]
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({
'id': _uuid(),
'router_id': router_id,
@ -310,7 +314,7 @@ class TestIPSecScenario(base.BaseSudoTestCase):
return 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({
'id': _uuid(),
'vpnservice_id': vpn_service['id'],
@ -402,8 +406,7 @@ class TestIPSecScenario(base.BaseSudoTestCase):
break
return pm.active
def test_ipsec_site_connections(self):
device = self.vpn_agent.device_drivers[0]
def _create_ipsec_site_connection(self, l3ha=False):
# Mock the method below because it causes Exception:
# RuntimeError: Second simultaneous read on fileno 5 detected.
# Unless you really know what you're doing, make sure that only
@ -414,32 +417,49 @@ class TestIPSecScenario(base.BaseSudoTestCase):
ip_lib.send_ip_addr_adv_notif = mock.Mock()
# There are no vpn services yet. get_vpn_services_on_host returns
# 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=[])
# 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.site_setup(PUBLIC_NET[5], private_nets[2])
if l3ha:
site2 = self.setup_ha_routers(PUBLIC_NET[5], private_nets[2])
else:
site2 = self.site_setup(PUBLIC_NET[5], private_nets[2])
# 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'])
device.report_status = mock.Mock()
device.agent_rpc.get_vpn_services_on_host = mock.Mock(
self.driver.report_status = mock.Mock()
self.driver.agent_rpc.get_vpn_services_on_host = mock.Mock(
return_value=[site1['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'],
timeout=8, count=4)
net_helpers.assert_no_ping(site2['port_namespace'], site1['port_ip'],
timeout=8, count=4)
device.sync(mock.Mock(), [{'id': site1['router'].router_id},
{'id': site2['router'].router_id}])
self.driver.sync(mock.Mock(), [{'id': site1['router'].router_id},
{'id': site2['router'].router_id}])
self.addCleanup(
device._delete_vpn_processes,
self.driver._delete_vpn_processes,
[site1['router'].router_id, site2['router'].router_id], [])
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)
vpn_agent_driver = self.vpn_agent.device_drivers[0]
failover_agent_driver = self.failover_agent.device_drivers[0]
ip_lib.send_ip_addr_adv_notif = mock.Mock()
self.failover_agent_driver = self.failover_agent.device_drivers[0]
# There are no vpn services yet. get_vpn_services_on_host returns
# 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=[])
site1, site2 = self._create_ipsec_site_connection(l3ha=True)
# 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']
router1 = site2['router1']
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
net_helpers.assert_no_ping(site1['port_namespace'], site2['port_ip'],
timeout=8, count=4)
@ -508,7 +503,8 @@ class TestIPSecScenario(base.BaseSudoTestCase):
# sync the routers
vpn_agent_driver.sync(mock.Mock(), [{'id': router.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(
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
# 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,
'%s/var/run/pluto.pid' % path]
linux_utils.wait_until_true(

View File

@ -20,16 +20,34 @@
# functional test for the OpenSwan reference implementation. For now, just
# 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
# 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):
"""Verify that directory and config files are correct on create."""
pass
@ -64,3 +82,30 @@ class TestOpenSwanDeviceDriver(base.BaseSudoTestCase):
def test_status_reporting(self):
"""Test status reported correctly to agent."""
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

@ -66,6 +66,7 @@ FAKE_VPN_SERVICE = {
'id': FAKE_IPSEC_SITE_CONNECTION1_ID,
'external_ip': '50.0.0.4',
'peer_address': '30.0.0.5',
'mtu': 1500,
'peer_id': '30.0.0.5',
'psk': 'password',
'initiator': 'bi-directional',
@ -78,6 +79,7 @@ FAKE_VPN_SERVICE = {
'external_ip': '50.0.0.4',
'peer_address': '50.0.0.5',
'peer_id': '50.0.0.5',
'mtu': 1500,
'psk': 'password',
'id': FAKE_IPSEC_SITE_CONNECTION2_ID,
'initiator': 'bi-directional',
@ -98,8 +100,7 @@ AUTH_AH = '''ah
OPENSWAN_CONNECTION_DETAILS = '''# rightsubnet=networkA/netmaskA, networkB/netmaskB (IKEv2 only)
# [mtu]
# Note It looks like not supported in the strongswan driver
# ignore it now
mtu=1500
# [dpd_action]
dpdaction=
# [dpd_interval]
@ -151,6 +152,11 @@ conn %(conn1_id)s
# [subnet]
leftsubnet=10.0.0.0/24
# 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
######################
@ -171,6 +177,11 @@ conn %(conn1_id)s
# [subnet]
leftsubnet=10.0.0.0/24
# 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
######################