[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:
parent
33814c49b0
commit
db7ac1f4d0
@ -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]
|
||||
|
@ -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(
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
######################
|
||||
|
Loading…
Reference in New Issue
Block a user