[Stateless SG] Test if TCP packets with various conn state are allowed
In stateless SG there is no connection track so TCP packet of any type should be allowed by SG. This patch adds test which spawns 2 vms, connected to 2 different networks and the same router. Both vms are using different stateless SGs. Finally test asserts that packets of any of the TCP type (syn, ack, syn-ack, psh, rst, fin) sent from one of the vms can reach second one. Change-Id: I23a4b282c83101526af05aa309d578aecaef1fa9
This commit is contained in:
parent
365373910c
commit
32b0f8b7ae
@ -675,3 +675,15 @@ class BaseTempestTestCase(base_api.BaseNetworkTest):
|
|||||||
router = self.client.update_router(
|
router = self.client.update_router(
|
||||||
router['id'], **kwargs)['router']
|
router['id'], **kwargs)['router']
|
||||||
self.assertEqual(admin_state_up, router['admin_state_up'])
|
self.assertEqual(admin_state_up, router['admin_state_up'])
|
||||||
|
|
||||||
|
def _check_cmd_installed_on_server(self, ssh_client, server, cmd):
|
||||||
|
try:
|
||||||
|
ssh_client.execute_script('which %s' % cmd)
|
||||||
|
except SSH_EXC_TUPLE as ssh_e:
|
||||||
|
LOG.debug(ssh_e)
|
||||||
|
self._log_console_output([server])
|
||||||
|
self._log_local_network_status()
|
||||||
|
raise
|
||||||
|
except exceptions.SSHScriptFailed:
|
||||||
|
raise self.skipException(
|
||||||
|
"%s is not available on server %s" % (cmd, server['id']))
|
||||||
|
@ -116,18 +116,6 @@ class MacLearningTest(base.BaseTempestTestCase):
|
|||||||
pkey=self.keypair['private_key'])
|
pkey=self.keypair['private_key'])
|
||||||
return server
|
return server
|
||||||
|
|
||||||
def _check_cmd_installed_on_server(self, ssh_client, server, cmd):
|
|
||||||
try:
|
|
||||||
ssh_client.execute_script('which %s' % cmd)
|
|
||||||
except base.SSH_EXC_TUPLE as ssh_e:
|
|
||||||
LOG.debug(ssh_e)
|
|
||||||
self._log_console_output([server])
|
|
||||||
self._log_local_network_status()
|
|
||||||
raise
|
|
||||||
except exceptions.SSHScriptFailed:
|
|
||||||
raise self.skipException(
|
|
||||||
"%s is not available on server %s" % (cmd, server['id']))
|
|
||||||
|
|
||||||
def _prepare_sender(self, server, address):
|
def _prepare_sender(self, server, address):
|
||||||
check_script = get_sender_script(self.sender_output_file, address,
|
check_script = get_sender_script(self.sender_output_file, address,
|
||||||
self.completed_message)
|
self.completed_message)
|
||||||
|
@ -213,18 +213,6 @@ class BaseMulticastTest(object):
|
|||||||
server, PYTHON3_BIN)
|
server, PYTHON3_BIN)
|
||||||
return server
|
return server
|
||||||
|
|
||||||
def _check_cmd_installed_on_server(self, ssh_client, server, cmd):
|
|
||||||
try:
|
|
||||||
ssh_client.execute_script('which %s' % cmd)
|
|
||||||
except base.SSH_EXC_TUPLE as ssh_e:
|
|
||||||
LOG.debug(ssh_e)
|
|
||||||
self._log_console_output([server])
|
|
||||||
self._log_local_network_status()
|
|
||||||
raise
|
|
||||||
except exceptions.SSHScriptFailed:
|
|
||||||
raise self.skipException(
|
|
||||||
"%s is not available on server %s" % (cmd, server['id']))
|
|
||||||
|
|
||||||
def _prepare_sender(self, server, mcast_address):
|
def _prepare_sender(self, server, mcast_address):
|
||||||
check_script = get_sender_script(
|
check_script = get_sender_script(
|
||||||
group=mcast_address, port=self.multicast_port,
|
group=mcast_address, port=self.multicast_port,
|
||||||
|
@ -23,6 +23,7 @@ from tempest.lib.common.utils import data_utils
|
|||||||
from tempest.lib.common.utils import test_utils
|
from tempest.lib.common.utils import test_utils
|
||||||
from tempest.lib import decorators
|
from tempest.lib import decorators
|
||||||
|
|
||||||
|
from neutron_tempest_plugin.common import ip
|
||||||
from neutron_tempest_plugin.common import ssh
|
from neutron_tempest_plugin.common import ssh
|
||||||
from neutron_tempest_plugin.common import utils
|
from neutron_tempest_plugin.common import utils
|
||||||
from neutron_tempest_plugin import config
|
from neutron_tempest_plugin import config
|
||||||
@ -35,6 +36,16 @@ CONF = config.CONF
|
|||||||
EPHEMERAL_PORT_RANGE = {'min': 32768, 'max': 65535}
|
EPHEMERAL_PORT_RANGE = {'min': 32768, 'max': 65535}
|
||||||
|
|
||||||
|
|
||||||
|
def get_capture_script(interface, tcp_port, packet_types, result_file):
|
||||||
|
return """#!/bin/bash
|
||||||
|
tcpdump -i %(interface)s -vvneA -s0 -l -c1 \
|
||||||
|
"dst port %(port)s and tcp[tcpflags] == %(packet_types)s" &> %(result)s &
|
||||||
|
""" % {'interface': interface,
|
||||||
|
'port': tcp_port,
|
||||||
|
'packet_types': packet_types,
|
||||||
|
'result': result_file}
|
||||||
|
|
||||||
|
|
||||||
class BaseNetworkSecGroupTest(base.BaseTempestTestCase):
|
class BaseNetworkSecGroupTest(base.BaseTempestTestCase):
|
||||||
credentials = ['primary', 'admin']
|
credentials = ['primary', 'admin']
|
||||||
required_extensions = ['router', 'security-group']
|
required_extensions = ['router', 'security-group']
|
||||||
@ -98,20 +109,32 @@ class BaseNetworkSecGroupTest(base.BaseTempestTestCase):
|
|||||||
self.network_client.update_quotas(self.project_id, security_group=-1)
|
self.network_client.update_quotas(self.project_id, security_group=-1)
|
||||||
|
|
||||||
def create_vm_testing_sec_grp(self, num_servers=2, security_groups=None,
|
def create_vm_testing_sec_grp(self, num_servers=2, security_groups=None,
|
||||||
ports=None, network_id=None):
|
ports=None, network_id=None,
|
||||||
|
use_advanced_image=False):
|
||||||
"""Create instance for security group testing
|
"""Create instance for security group testing
|
||||||
|
|
||||||
:param num_servers (int): number of servers to spawn
|
:param num_servers (int): number of servers to spawn
|
||||||
:param security_groups (list): list of security groups
|
:param security_groups (list): list of security groups
|
||||||
:param ports* (list): list of ports
|
:param ports* (list): list of ports
|
||||||
|
:param: use_advanced_image (bool): use Cirros (False) or
|
||||||
|
advanced guest image
|
||||||
*Needs to be the same length as num_servers
|
*Needs to be the same length as num_servers
|
||||||
"""
|
"""
|
||||||
|
if (not use_advanced_image or
|
||||||
|
CONF.neutron_plugin_options.default_image_is_advanced):
|
||||||
|
flavor_ref = CONF.compute.flavor_ref
|
||||||
|
image_ref = CONF.compute.image_ref
|
||||||
|
username = CONF.validation.image_ssh_user
|
||||||
|
else:
|
||||||
|
flavor_ref = CONF.neutron_plugin_options.advanced_image_flavor_ref
|
||||||
|
image_ref = CONF.neutron_plugin_options.advanced_image_ref
|
||||||
|
username = CONF.neutron_plugin_options.advanced_image_ssh_user
|
||||||
network_id = network_id or self.network['id']
|
network_id = network_id or self.network['id']
|
||||||
servers, fips, server_ssh_clients = ([], [], [])
|
servers, fips, server_ssh_clients = ([], [], [])
|
||||||
for i in range(num_servers):
|
for i in range(num_servers):
|
||||||
server_args = {
|
server_args = {
|
||||||
'flavor_ref': CONF.compute.flavor_ref,
|
'flavor_ref': flavor_ref,
|
||||||
'image_ref': CONF.compute.image_ref,
|
'image_ref': image_ref,
|
||||||
'key_name': self.keypair['name'],
|
'key_name': self.keypair['name'],
|
||||||
'networks': [{'uuid': network_id}],
|
'networks': [{'uuid': network_id}],
|
||||||
'security_groups': security_groups
|
'security_groups': security_groups
|
||||||
@ -128,7 +151,7 @@ class BaseNetworkSecGroupTest(base.BaseTempestTestCase):
|
|||||||
'id'])['ports'][0]
|
'id'])['ports'][0]
|
||||||
fips.append(self.create_floatingip(port=port))
|
fips.append(self.create_floatingip(port=port))
|
||||||
server_ssh_clients.append(ssh.Client(
|
server_ssh_clients.append(ssh.Client(
|
||||||
fips[i]['floating_ip_address'], CONF.validation.image_ssh_user,
|
fips[i]['floating_ip_address'], username,
|
||||||
pkey=self.keypair['private_key']))
|
pkey=self.keypair['private_key']))
|
||||||
return server_ssh_clients, fips, servers
|
return server_ssh_clients, fips, servers
|
||||||
|
|
||||||
@ -145,8 +168,8 @@ class BaseNetworkSecGroupTest(base.BaseTempestTestCase):
|
|||||||
return super(BaseNetworkSecGroupTest, self).create_security_group(
|
return super(BaseNetworkSecGroupTest, self).create_security_group(
|
||||||
name=data_utils.rand_name(name_prefix), **kwargs)
|
name=data_utils.rand_name(name_prefix), **kwargs)
|
||||||
|
|
||||||
def _test_connectivity_between_vms_using_different_sec_groups(self):
|
def _create_client_and_server_vms(
|
||||||
TEST_TCP_PORT = 1022
|
self, allowed_tcp_port, use_advanced_image=False):
|
||||||
networks = {
|
networks = {
|
||||||
'server': self.network,
|
'server': self.network,
|
||||||
'client': self.create_network()}
|
'client': self.create_network()}
|
||||||
@ -162,13 +185,12 @@ class BaseNetworkSecGroupTest(base.BaseTempestTestCase):
|
|||||||
security_group_id=sg['id'],
|
security_group_id=sg['id'],
|
||||||
protocol=constants.PROTO_NAME_TCP,
|
protocol=constants.PROTO_NAME_TCP,
|
||||||
direction=constants.INGRESS_DIRECTION,
|
direction=constants.INGRESS_DIRECTION,
|
||||||
port_range_min=TEST_TCP_PORT,
|
port_range_min=allowed_tcp_port,
|
||||||
port_range_max=TEST_TCP_PORT)
|
port_range_max=allowed_tcp_port)
|
||||||
if self.stateless_sg:
|
if self.stateless_sg:
|
||||||
self.create_ingress_metadata_secgroup_rule(
|
self.create_ingress_metadata_secgroup_rule(
|
||||||
secgroup_id=sg['id'])
|
secgroup_id=sg['id'])
|
||||||
security_groups[sg_name] = sg
|
security_groups[sg_name] = sg
|
||||||
|
|
||||||
# NOTE(slaweq): we need to iterate over create_vm_testing_sec_grp as
|
# NOTE(slaweq): we need to iterate over create_vm_testing_sec_grp as
|
||||||
# this method plugs all SGs to all VMs and we need each vm to use other
|
# this method plugs all SGs to all VMs and we need each vm to use other
|
||||||
# SGs
|
# SGs
|
||||||
@ -179,17 +201,23 @@ class BaseNetworkSecGroupTest(base.BaseTempestTestCase):
|
|||||||
_ssh_clients, _fips, _servers = self.create_vm_testing_sec_grp(
|
_ssh_clients, _fips, _servers = self.create_vm_testing_sec_grp(
|
||||||
num_servers=1,
|
num_servers=1,
|
||||||
security_groups=[{'name': sg['name']}],
|
security_groups=[{'name': sg['name']}],
|
||||||
network_id=networks[server_name]['id'])
|
network_id=networks[server_name]['id'],
|
||||||
|
use_advanced_image=use_advanced_image)
|
||||||
ssh_clients[server_name] = _ssh_clients[0]
|
ssh_clients[server_name] = _ssh_clients[0]
|
||||||
fips[server_name] = _fips[0]
|
fips[server_name] = _fips[0]
|
||||||
servers[server_name] = _servers[0]
|
servers[server_name] = _servers[0]
|
||||||
|
return ssh_clients, fips, servers, security_groups
|
||||||
|
|
||||||
|
def _test_connectivity_between_vms_using_different_sec_groups(self):
|
||||||
|
TEST_TCP_PORT = 1022
|
||||||
|
ssh_clients, fips, servers, security_groups = (
|
||||||
|
self._create_client_and_server_vms(TEST_TCP_PORT))
|
||||||
|
|
||||||
# make sure tcp connectivity between vms works fine
|
# make sure tcp connectivity between vms works fine
|
||||||
for fip in fips.values():
|
for fip in fips.values():
|
||||||
self.check_connectivity(fip['floating_ip_address'],
|
self.check_connectivity(fip['floating_ip_address'],
|
||||||
CONF.validation.image_ssh_user,
|
CONF.validation.image_ssh_user,
|
||||||
self.keypair['private_key'])
|
self.keypair['private_key'])
|
||||||
|
|
||||||
# Check connectivity between servers
|
# Check connectivity between servers
|
||||||
def _message_received(server_ssh_client, client_ssh_client,
|
def _message_received(server_ssh_client, client_ssh_client,
|
||||||
dest_fip, servers):
|
dest_fip, servers):
|
||||||
@ -994,3 +1022,71 @@ class StatelessNetworkSecGroupTest(BaseNetworkSecGroupTest):
|
|||||||
@decorators.idempotent_id('7ede9ab5-a615-46c5-9dea-cf2aa1ea43cb')
|
@decorators.idempotent_id('7ede9ab5-a615-46c5-9dea-cf2aa1ea43cb')
|
||||||
def test_connectivity_between_vms_using_different_sec_groups(self):
|
def test_connectivity_between_vms_using_different_sec_groups(self):
|
||||||
self._test_connectivity_between_vms_using_different_sec_groups()
|
self._test_connectivity_between_vms_using_different_sec_groups()
|
||||||
|
|
||||||
|
@testtools.skipUnless(
|
||||||
|
(CONF.neutron_plugin_options.advanced_image_ref or
|
||||||
|
CONF.neutron_plugin_options.default_image_is_advanced),
|
||||||
|
"Advanced image is required to run this test.")
|
||||||
|
@decorators.idempotent_id('c3bb8073-97a2-4bea-a6fb-0a9d2e4df13f')
|
||||||
|
def test_packets_of_any_connection_state_can_reach_dest(self):
|
||||||
|
TEST_TCP_PORT = 1022
|
||||||
|
PKT_TYPES = [
|
||||||
|
{'nping': 'syn', 'tcpdump': 'tcp-syn'},
|
||||||
|
{'nping': 'ack', 'tcpdump': 'tcp-ack'},
|
||||||
|
{'nping': 'syn,ack', 'tcpdump': 'tcp-syn|tcp-ack'},
|
||||||
|
{'nping': 'rst', 'tcpdump': 'tcp-rst'},
|
||||||
|
{'nping': 'fin', 'tcpdump': 'tcp-fin'},
|
||||||
|
{'nping': 'psh', 'tcpdump': 'tcp-push'}]
|
||||||
|
ssh_clients, fips, servers, _ = self._create_client_and_server_vms(
|
||||||
|
TEST_TCP_PORT, use_advanced_image=True)
|
||||||
|
|
||||||
|
self._check_cmd_installed_on_server(
|
||||||
|
ssh_clients['server'], servers['server']['server'], 'nping')
|
||||||
|
self._check_cmd_installed_on_server(
|
||||||
|
ssh_clients['client'], servers['client']['server'], 'tcpdump')
|
||||||
|
server_port = self.network_client.show_port(
|
||||||
|
fips['server']['port_id'])['port']
|
||||||
|
server_ip_command = ip.IPCommand(ssh_client=ssh_clients['server'])
|
||||||
|
addresses = server_ip_command.list_addresses(port=server_port)
|
||||||
|
port_iface = ip.get_port_device_name(addresses, server_port)
|
||||||
|
|
||||||
|
def _get_file_suffix(pkt_type):
|
||||||
|
return pkt_type['tcpdump'].replace(
|
||||||
|
'tcp-', '').replace('|', '')
|
||||||
|
|
||||||
|
for pkt_type in PKT_TYPES:
|
||||||
|
file_suffix = _get_file_suffix(pkt_type)
|
||||||
|
capture_script_path = "/tmp/capture_%s.sh" % file_suffix
|
||||||
|
capture_out = "/tmp/capture_%s.out" % file_suffix
|
||||||
|
capture_script = get_capture_script(
|
||||||
|
port_iface, TEST_TCP_PORT, pkt_type['tcpdump'], capture_out)
|
||||||
|
ssh_clients['server'].execute_script(
|
||||||
|
'echo \'%s\' > %s' % (capture_script, capture_script_path))
|
||||||
|
ssh_clients['server'].execute_script(
|
||||||
|
"bash %s" % capture_script_path, become_root=True)
|
||||||
|
|
||||||
|
for pkt_type in PKT_TYPES:
|
||||||
|
ssh_clients['client'].execute_script(
|
||||||
|
"nping --tcp -p %(tcp_port)s --flags %(tcp_flag)s --ttl 10 "
|
||||||
|
"%(ip_address)s -c 3" % {
|
||||||
|
'tcp_port': TEST_TCP_PORT,
|
||||||
|
'tcp_flag': pkt_type['nping'],
|
||||||
|
'ip_address': fips['server']['fixed_ip_address']},
|
||||||
|
become_root=True)
|
||||||
|
|
||||||
|
def _packtet_received(pkt_type):
|
||||||
|
file_suffix = _get_file_suffix(pkt_type)
|
||||||
|
expected_msg = "1 packet captured"
|
||||||
|
result = ssh_clients['server'].execute_script(
|
||||||
|
"cat {path} || echo '{path} not exists yet'".format(
|
||||||
|
path="/tmp/capture_%s.out" % file_suffix))
|
||||||
|
return expected_msg in result
|
||||||
|
|
||||||
|
for pkt_type in PKT_TYPES:
|
||||||
|
utils.wait_until_true(
|
||||||
|
lambda: _packtet_received(pkt_type),
|
||||||
|
timeout=10,
|
||||||
|
exception=RuntimeError(
|
||||||
|
'No TCP packet of type %s received by server %s' % (
|
||||||
|
pkt_type['nping'],
|
||||||
|
fips['server']['fixed_ip_address'])))
|
||||||
|
@ -19,6 +19,7 @@ INSTALL_GUEST_PACKAGES=(
|
|||||||
iperf3
|
iperf3
|
||||||
iputils-ping
|
iputils-ping
|
||||||
ncat
|
ncat
|
||||||
|
nmap
|
||||||
psmisc # provides killall command
|
psmisc # provides killall command
|
||||||
python3
|
python3
|
||||||
tcpdump
|
tcpdump
|
||||||
|
Loading…
Reference in New Issue
Block a user