ovsfw: Support protocol numbers instead of just tcp and udp

Neutron API accepts also protocol numbers as protocols for security
groups. This patch makes support for it in OVS firewall driver. iptables
driver already supports it.

Fullstack test covering SCTP connection was added and it requires
ip_conntrack_proto_sctp kernel module in order to make conntrack work
with SCTP.

Change-Id: I6c5665a994c4a50ddbb95cd1360be0de0a6c7e40
Closes-bug: 1625516
This commit is contained in:
Jakub Libosvar 2016-11-24 12:32:55 -05:00
parent 0c05d49949
commit d5c07fe512
5 changed files with 42 additions and 22 deletions

View File

@ -35,12 +35,6 @@ CT_MARK_INVALID = '0x1'
REG_PORT = 5 REG_PORT = 5
REG_NET = 6 REG_NET = 6
protocol_to_nw_proto = {
constants.PROTO_NAME_ICMP: constants.PROTO_NUM_ICMP,
constants.PROTO_NAME_TCP: constants.PROTO_NUM_TCP,
constants.PROTO_NAME_UDP: constants.PROTO_NUM_UDP,
}
PROTOCOLS_WITH_PORTS = (constants.PROTO_NAME_TCP, constants.PROTO_NAME_UDP) PROTOCOLS_WITH_PORTS = (constants.PROTO_NAME_TCP, constants.PROTO_NAME_UDP)
ethertype_to_dl_type_map = { ethertype_to_dl_type_map = {

View File

@ -79,12 +79,13 @@ def create_protocol_flows(direction, flow_template, port, rule):
flow_template['actions'] = 'resubmit(,{:d})'.format( flow_template['actions'] = 'resubmit(,{:d})'.format(
ovs_consts.ACCEPT_OR_INGRESS_TABLE) ovs_consts.ACCEPT_OR_INGRESS_TABLE)
protocol = rule.get('protocol') protocol = rule.get('protocol')
try: if protocol:
flow_template['nw_proto'] = ovsfw_consts.protocol_to_nw_proto[protocol] if (rule.get('ethertype') == n_consts.IPv6 and
if rule['ethertype'] == n_consts.IPv6 and protocol == 'icmp': protocol == n_consts.PROTO_NAME_ICMP):
flow_template['nw_proto'] = n_consts.PROTO_NUM_IPV6_ICMP flow_template['nw_proto'] = n_consts.PROTO_NUM_IPV6_ICMP
except KeyError: else:
pass flow_template['nw_proto'] = n_consts.IP_PROTOCOL_MAP.get(
protocol, protocol)
flows = create_port_range_flows(flow_template, rule) flows = create_port_range_flows(flow_template, rule)
return flows or [flow_template] return flows or [flow_template]

View File

@ -72,7 +72,8 @@ READ_TIMEOUT = os.environ.get('OS_TEST_READ_TIMEOUT', 5)
CHILD_PROCESS_TIMEOUT = os.environ.get('OS_TEST_CHILD_PROCESS_TIMEOUT', 20) CHILD_PROCESS_TIMEOUT = os.environ.get('OS_TEST_CHILD_PROCESS_TIMEOUT', 20)
CHILD_PROCESS_SLEEP = os.environ.get('OS_TEST_CHILD_PROCESS_SLEEP', 0.5) CHILD_PROCESS_SLEEP = os.environ.get('OS_TEST_CHILD_PROCESS_SLEEP', 0.5)
TRANSPORT_PROTOCOLS = (n_const.PROTO_NAME_TCP, n_const.PROTO_NAME_UDP) TRANSPORT_PROTOCOLS = (n_const.PROTO_NAME_TCP, n_const.PROTO_NAME_UDP,
n_const.PROTO_NAME_SCTP)
OVS_MANAGER_TEST_PORT_FIRST = 6610 OVS_MANAGER_TEST_PORT_FIRST = 6610
OVS_MANAGER_TEST_PORT_LAST = 6639 OVS_MANAGER_TEST_PORT_LAST = 6639
@ -403,6 +404,7 @@ class Pinger(object):
class NetcatTester(object): class NetcatTester(object):
TCP = n_const.PROTO_NAME_TCP TCP = n_const.PROTO_NAME_TCP
UDP = n_const.PROTO_NAME_UDP UDP = n_const.PROTO_NAME_UDP
SCTP = n_const.PROTO_NAME_SCTP
VERSION_TO_ALL_ADDRESS = { VERSION_TO_ALL_ADDRESS = {
4: '0.0.0.0', 4: '0.0.0.0',
6: '::', 6: '::',
@ -423,7 +425,7 @@ class NetcatTester(object):
will be spawned will be spawned
:param address: Server address from client point of view :param address: Server address from client point of view
:param dst_port: Port on which netcat listens :param dst_port: Port on which netcat listens
:param protocol: Transport protocol, either 'tcp' or 'udp' :param protocol: Transport protocol, either 'tcp', 'udp' or 'sctp'
:param server_address: Address in server namespace on which netcat :param server_address: Address in server namespace on which netcat
should listen should listen
:param src_port: Source port of netcat process spawned in client :param src_port: Source port of netcat process spawned in client
@ -515,9 +517,12 @@ class NetcatTester(object):
cmd = ['ncat', address, self.dst_port] cmd = ['ncat', address, self.dst_port]
if self.protocol == self.UDP: if self.protocol == self.UDP:
cmd.append('-u') cmd.append('-u')
elif self.protocol == self.SCTP:
cmd.append('--sctp')
if listen: if listen:
cmd.append('-l') cmd.append('-l')
if self.protocol == self.TCP: if self.protocol in (self.TCP, self.SCTP):
cmd.append('-k') cmd.append('-k')
else: else:
cmd.extend(['-w', '20']) cmd.extend(['-w', '20'])

View File

@ -61,15 +61,17 @@ class BaseSecurityGroupsSameNetworkTest(base.BaseFullStackTestCase):
def assert_connection(self, *args, **kwargs): def assert_connection(self, *args, **kwargs):
netcat = net_helpers.NetcatTester(*args, **kwargs) netcat = net_helpers.NetcatTester(*args, **kwargs)
self.addCleanup(netcat.stop_processes) try:
self.assertTrue(netcat.test_connectivity()) self.assertTrue(netcat.test_connectivity())
netcat.stop_processes() finally:
netcat.stop_processes()
def assert_no_connection(self, *args, **kwargs): def assert_no_connection(self, *args, **kwargs):
netcat = net_helpers.NetcatTester(*args, **kwargs) netcat = net_helpers.NetcatTester(*args, **kwargs)
self.addCleanup(netcat.stop_processes) try:
self.assertRaises(RuntimeError, netcat.test_connectivity) self.assertRaises(RuntimeError, netcat.test_connectivity)
netcat.stop_processes() finally:
netcat.stop_processes()
class TestSecurityGroupsSameNetwork(BaseSecurityGroupsSameNetworkTest): class TestSecurityGroupsSameNetwork(BaseSecurityGroupsSameNetworkTest):
@ -98,14 +100,15 @@ class TestSecurityGroupsSameNetwork(BaseSecurityGroupsSameNetworkTest):
# NOTE(toshii): As a firewall_driver can interfere with others, # NOTE(toshii): As a firewall_driver can interfere with others,
# the recommended way to add test is to expand this method, not # the recommended way to add test is to expand this method, not
# adding another. # adding another.
def test_tcp_securitygroup(self): def test_securitygroup(self):
"""Tests if a TCP security group rule is working, by confirming """Tests if a security group rules are working, by confirming
that 1. connection from allowed security group is allowed, that 1. connection from allowed security group is allowed,
2. connection from elsewhere is blocked, 2. connection from elsewhere is blocked,
3. traffic not explicitly allowed (eg. ICMP) is blocked, 3. traffic not explicitly allowed (eg. ICMP) is blocked,
4. a security group update takes effect, 4. a security group update takes effect,
5. a remote security group member addition works, and 5. a remote security group member addition works, and
6. an established connection stops by deleting a SG rule. 6. an established connection stops by deleting a SG rule.
7. test other protocol functionality by using SCTP protocol
""" """
index_to_sg = [0, 0, 1] index_to_sg = [0, 0, 1]
if self.firewall_driver == 'iptables_hybrid': if self.firewall_driver == 'iptables_hybrid':
@ -228,3 +231,19 @@ class TestSecurityGroupsSameNetwork(BaseSecurityGroupsSameNetworkTest):
common_utils.wait_until_true(lambda: netcat.test_no_connectivity(), common_utils.wait_until_true(lambda: netcat.test_no_connectivity(),
sleep=8) sleep=8)
netcat.stop_processes() netcat.stop_processes()
# 7. check SCTP is supported by security group
self.assert_no_connection(
vms[1].namespace, vms[0].namespace, vms[0].ip, 3366,
net_helpers.NetcatTester.SCTP)
self.safe_client.create_security_group_rule(
tenant_uuid, sgs[0]['id'],
remote_group_id=sgs[0]['id'], direction='ingress',
ethertype=constants.IPv4,
protocol=constants.PROTO_NUM_SCTP,
port_range_min=3366, port_range_max=3366)
self.assert_connection(
vms[1].namespace, vms[0].namespace, vms[0].ip, 3366,
net_helpers.NetcatTester.SCTP)

View File

@ -283,4 +283,5 @@ fi
if [[ "$VENV" =~ "dsvm-fullstack" ]]; then if [[ "$VENV" =~ "dsvm-fullstack" ]]; then
_configure_iptables_rules _configure_iptables_rules
sudo modprobe ip_conntrack_proto_sctp
fi fi