ovsfw: Fix port_ranges handling

ovsfw ignored port_ranges when a SG rule protocol was sctp or given
in a number rather than a token.  This commit fixes that.

Change-Id: I6c810a152990246d42d98c3673c4b5ee126ebb4b
Closes-bug: #1708580
(cherry picked from commit effa12889b)
This commit is contained in:
IWAMOTO Toshihiro 2017-08-04 15:20:08 +09:00 committed by Jakub Libosvar
parent c62662f3c3
commit 6d43f2b1ad
5 changed files with 67 additions and 25 deletions

View File

@ -35,7 +35,14 @@ CT_MARK_INVALID = '0x1'
REG_PORT = 5
REG_NET = 6
PROTOCOLS_WITH_PORTS = (constants.PROTO_NAME_TCP, constants.PROTO_NAME_UDP)
PROTOCOLS_WITH_PORTS = (constants.PROTO_NAME_SCTP,
constants.PROTO_NAME_TCP,
constants.PROTO_NAME_UDP)
# Only map protocols that need special handling
REVERSE_IP_PROTOCOL_MAP_WITH_PORTS = {
constants.IP_PROTOCOL_MAP[proto]: proto for proto in
PROTOCOLS_WITH_PORTS}
ethertype_to_dl_type_map = {
constants.IPv4: n_const.ETHERTYPE_IP,

View File

@ -82,11 +82,27 @@ class SecurityGroup(object):
self.ports = set()
def update_rules(self, rules):
"""Separate raw and remote rules."""
self.raw_rules = [rule for rule in rules
if 'remote_group_id' not in rule]
self.remote_rules = [rule for rule in rules
if 'remote_group_id' in rule]
"""Separate raw and remote rules.
If a rule has a protocol field, it is normalized to a number
here in order to ease later processing.
"""
self.raw_rules = []
self.remote_rules = []
for rule in rules:
protocol = rule.get('protocol')
if protocol is not None:
if protocol.isdigit():
rule['protocol'] = int(protocol)
elif (rule.get('ethertype') == lib_const.IPv6 and
protocol == lib_const.PROTO_NAME_ICMP):
rule['protocol'] = lib_const.PROTO_NUM_IPV6_ICMP
else:
rule['protocol'] = lib_const.IP_PROTOCOL_MAP.get(
protocol, protocol)
if 'remote_group_id' in rule:
self.remote_rules.append(rule)
else:
self.raw_rules.append(rule)
def get_ethertype_filtered_addresses(self, ethertype):
return self.members.get(ethertype, [])

View File

@ -92,21 +92,17 @@ def create_protocol_flows(direction, flow_template, port, rule):
flow_template.copy(),
port)
protocol = rule.get('protocol')
if protocol:
if (rule.get('ethertype') == n_consts.IPv6 and
protocol == n_consts.PROTO_NAME_ICMP):
flow_template['nw_proto'] = n_consts.PROTO_NUM_IPV6_ICMP
else:
flow_template['nw_proto'] = n_consts.IP_PROTOCOL_MAP.get(
protocol, protocol)
if protocol is not None:
flow_template['nw_proto'] = protocol
flows = create_port_range_flows(flow_template, rule)
return flows or [flow_template]
def create_port_range_flows(flow_template, rule):
protocol = rule.get('protocol')
if protocol not in ovsfw_consts.PROTOCOLS_WITH_PORTS:
protocol = ovsfw_consts.REVERSE_IP_PROTOCOL_MAP_WITH_PORTS.get(
rule.get('protocol'))
if protocol is None:
return []
flows = []
src_port_match = '{:s}_src'.format(protocol)

View File

@ -56,7 +56,7 @@ class TestSecurityGroup(base.BaseTestCase):
self.sg = ovsfw.SecurityGroup('123')
self.sg.members = {'type': [1, 2, 3, 4]}
def test_update_rules(self):
def test_update_rules_split(self):
rules = [
{'foo': 'bar', 'rule': 'all'}, {'bar': 'foo'},
{'remote_group_id': '123456', 'foo': 'bar'}]
@ -67,6 +67,29 @@ class TestSecurityGroup(base.BaseTestCase):
self.assertEqual(expected_raw_rules, self.sg.raw_rules)
self.assertEqual(expected_remote_rules, self.sg.remote_rules)
def test_update_rules_protocols(self):
rules = [
{'foo': 'bar', 'protocol': constants.PROTO_NAME_ICMP,
'ethertype': constants.IPv4},
{'foo': 'bar', 'protocol': constants.PROTO_NAME_ICMP,
'ethertype': constants.IPv6},
{'foo': 'bar', 'protocol': constants.PROTO_NAME_IPV6_ICMP_LEGACY,
'ethertype': constants.IPv6},
{'foo': 'bar', 'protocol': constants.PROTO_NAME_TCP},
{'foo': 'bar', 'protocol': '94'},
{'foo': 'bar', 'protocol': 'baz'},
{'foo': 'no_proto'}]
self.sg.update_rules(rules)
self.assertEqual({'foo': 'no_proto'}, self.sg.raw_rules.pop())
protos = [rule['protocol'] for rule in self.sg.raw_rules]
self.assertEqual([constants.PROTO_NUM_ICMP,
constants.PROTO_NUM_IPV6_ICMP,
constants.PROTO_NUM_IPV6_ICMP,
constants.PROTO_NUM_TCP,
94,
'baz'], protos)
def test_get_ethertype_filtered_addresses(self):
addresses = self.sg.get_ethertype_filtered_addresses('type')
expected_addresses = [1, 2, 3, 4]

View File

@ -183,7 +183,7 @@ class TestCreateProtocolFlows(base.BaseTestCase):
self.assertEqual(expected_flows, flows)
def test_create_protocol_flows_ingress(self):
rule = {'protocol': constants.PROTO_NAME_TCP}
rule = {'protocol': constants.PROTO_NUM_TCP}
expected_flows = [{
'table': ovs_consts.RULES_INGRESS_TABLE,
'actions': 'output:1',
@ -193,7 +193,7 @@ class TestCreateProtocolFlows(base.BaseTestCase):
firewall.INGRESS_DIRECTION, rule, expected_flows)
def test_create_protocol_flows_egress(self):
rule = {'protocol': constants.PROTO_NAME_TCP}
rule = {'protocol': constants.PROTO_NUM_TCP}
expected_flows = [{
'table': ovs_consts.RULES_EGRESS_TABLE,
'actions': 'resubmit(,{:d})'.format(
@ -215,7 +215,7 @@ class TestCreateProtocolFlows(base.BaseTestCase):
def test_create_protocol_flows_icmp6(self):
rule = {'ethertype': constants.IPv6,
'protocol': constants.PROTO_NAME_ICMP}
'protocol': constants.PROTO_NUM_IPV6_ICMP}
expected_flows = [{
'table': ovs_consts.RULES_EGRESS_TABLE,
'actions': 'resubmit(,{:d})'.format(
@ -227,7 +227,7 @@ class TestCreateProtocolFlows(base.BaseTestCase):
def test_create_protocol_flows_port_range(self):
rule = {'ethertype': constants.IPv4,
'protocol': constants.PROTO_NAME_TCP,
'protocol': constants.PROTO_NUM_TCP,
'port_range_min': 22,
'port_range_max': 23}
expected_flows = [{
@ -251,7 +251,7 @@ class TestCreatePortRangeFlows(base.BaseTestCase):
def test_create_port_range_flows_with_source_and_destination(self):
rule = {
'protocol': constants.PROTO_NAME_TCP,
'protocol': constants.PROTO_NUM_TCP,
'source_port_range_min': 123,
'source_port_range_max': 124,
'port_range_min': 10,
@ -265,7 +265,7 @@ class TestCreatePortRangeFlows(base.BaseTestCase):
def test_create_port_range_flows_with_source(self):
rule = {
'protocol': constants.PROTO_NAME_TCP,
'protocol': constants.PROTO_NUM_TCP,
'source_port_range_min': 123,
'source_port_range_max': 124,
}
@ -277,7 +277,7 @@ class TestCreatePortRangeFlows(base.BaseTestCase):
def test_create_port_range_flows_with_destination(self):
rule = {
'protocol': constants.PROTO_NAME_TCP,
'protocol': constants.PROTO_NUM_TCP,
'port_range_min': 10,
'port_range_max': 11,
}
@ -288,14 +288,14 @@ class TestCreatePortRangeFlows(base.BaseTestCase):
def test_create_port_range_flows_without_port_range(self):
rule = {
'protocol': constants.PROTO_NAME_TCP,
'protocol': constants.PROTO_NUM_TCP,
}
expected_flows = []
self._test_create_port_range_flows_helper(expected_flows, rule)
def test_create_port_range_with_icmp_protocol(self):
rule = {
'protocol': 'icmp',
'protocol': constants.PROTO_NUM_ICMP,
'port_range_min': 10,
'port_range_max': 11,
}