From f70a33ece98c54b8529de29be11840d6fd39a7c3 Mon Sep 17 00:00:00 2001 From: Andrew Bates Date: Wed, 26 Jan 2022 10:04:15 -0500 Subject: [PATCH 1/3] Fixes google/capirca#295 --- capirca/lib/cisco.py | 20 +++++++++------ capirca/lib/cisconx.py | 2 +- capirca/lib/ciscoxr.py | 6 ++--- doc/generators/cisco.md | 3 ++- tests/lib/cisco_test.py | 52 ++++++++++++++++++++++++++++++++++++--- tests/lib/ciscoxr_test.py | 24 ++++++++++++++++-- 6 files changed, 89 insertions(+), 18 deletions(-) diff --git a/capirca/lib/cisco.py b/capirca/lib/cisco.py index 0bb5c229..c60a331f 100644 --- a/capirca/lib/cisco.py +++ b/capirca/lib/cisco.py @@ -807,8 +807,8 @@ class ObjectGroupTerm(Term): # Protocols should be emitted as integers rather than strings. _PROTO_INT = True - def __init__(self, term, filter_name, platform='cisco', verbose=True): - super().__init__(term) + def __init__(self, term, filter_name, af=4, platform='cisco', verbose=True): + super().__init__(term, af=af) self.term = term self.filter_name = filter_name self.platform = platform @@ -947,8 +947,8 @@ def _TranslatePolicy(self, pol, exp_info): exp_info_date = current_date + datetime.timedelta(weeks=exp_info) # a mixed filter outputs both ipv4 and ipv6 acls in the same output file - good_filters = ['extended', 'standard', 'object-group', 'inet6', - 'mixed', 'enable_dsmo'] + good_filters = ['extended', 'standard', 'object-group', + 'object-group-inet6', 'inet6', 'mixed', 'enable_dsmo'] for header, terms in pol.filters: if self._PLATFORM not in header.platforms: @@ -1060,6 +1060,10 @@ def _TranslatePolicy(self, pol, exp_info): term, filter_name, verbose=self.verbose ) ) + elif next_filter == 'object-group-inet6': + obj_target.AddTerm(term) + new_terms.append(self._GetObjectGroupTerm(term, filter_name, af=6, + verbose=self.verbose)) elif next_filter == 'inet6': new_terms.append( Term( @@ -1078,9 +1082,9 @@ def _TranslatePolicy(self, pol, exp_info): (header, filter_name, [next_filter], new_terms, obj_target) ) - def _GetObjectGroupTerm(self, term, filter_name, verbose=True): + def _GetObjectGroupTerm(self, term, filter_name, af=4, verbose=True): """Returns an ObjectGroupTerm object.""" - return ObjectGroupTerm(term, filter_name, verbose=verbose) + return ObjectGroupTerm(term, filter_name, af=af, verbose=verbose) def _AppendTargetByFilterType(self, filter_name, filter_type): """Takes in the filter name and type and appends headers. @@ -1108,7 +1112,7 @@ def _AppendTargetByFilterType(self, filter_name, filter_type): elif filter_type == 'object-group': target.append('no ip access-list extended %s' % filter_name) target.append('ip access-list extended %s' % filter_name) - elif filter_type == 'inet6': + elif filter_type == 'inet6' or filter_type == 'object-group-inet6': target.append('no ipv6 access-list %s' % filter_name) target.append('ipv6 access-list %s' % filter_name) else: @@ -1138,7 +1142,7 @@ def __str__(self): ) in self.cisco_policies: for filter_type in filter_list: target.extend(self._AppendTargetByFilterType(filter_name, filter_type)) - if filter_type == 'object-group': + if filter_type == 'object-group' or filter_type == 'object-group-inet6': obj_target.AddName(filter_name) # Add the Perforce Id/Date tags, these must come after diff --git a/capirca/lib/cisconx.py b/capirca/lib/cisconx.py index 13a232c4..cf623d87 100644 --- a/capirca/lib/cisconx.py +++ b/capirca/lib/cisconx.py @@ -66,7 +66,7 @@ def _AppendTargetByFilterType(self, filter_name, filter_type): elif filter_type == 'object-group': target.append('no ip access-list %s' % filter_name) target.append('ip access-list %s' % filter_name) - elif filter_type == 'inet6': + elif filter_type == 'inet6' or filter_type == 'object-group-inet6': target.append('no ipv6 access-list %s' % filter_name) target.append('ipv6 access-list %s' % filter_name) else: diff --git a/capirca/lib/ciscoxr.py b/capirca/lib/ciscoxr.py index a9d13938..ffddd828 100644 --- a/capirca/lib/ciscoxr.py +++ b/capirca/lib/ciscoxr.py @@ -37,7 +37,7 @@ def _AppendTargetByFilterType(self, filter_name, filter_type): list of strings """ target = [] - if filter_type == 'inet6': + if filter_type == 'inet6' or filter_type == 'object-group-inet6': target.append('no ipv6 access-list %s' % filter_name) target.append('ipv6 access-list %s' % filter_name) else: @@ -57,9 +57,9 @@ def _BuildTokens(self): return supported_tokens, supported_sub_tokens - def _GetObjectGroupTerm(self, term, filter_name, verbose=True): + def _GetObjectGroupTerm(self, term, filter_name, af=4, verbose=True): """Returns an ObjectGroupTerm object.""" - return CiscoXRObjectGroupTerm(term, filter_name, + return CiscoXRObjectGroupTerm(term, filter_name, af=af, platform=self._PLATFORM, verbose=verbose) diff --git a/doc/generators/cisco.md b/doc/generators/cisco.md index 7319afb7..7b2995a4 100644 --- a/doc/generators/cisco.md +++ b/doc/generators/cisco.md @@ -2,12 +2,13 @@ The cisco header designation has the following format: ``` -target:: cisco [filter name] {extended|standard|object-group|inet6|mixed} {dsmo} +target:: cisco [filter name] {extended|standard|object-group|object-group-inet6|inet6|mixed} {dsmo} ``` * _filter name_: defines the name or number of the cisco filter. * _extended_: specifies that the output should be an extended access list, and the filter name should be non-numeric. This is the default option. * _standard_: specifies that the output should be a standard access list, and the filter name should be numeric and in the range of 1-99. * _object-group_: specifies this is a cisco extended access list, and that object-groups should be used for ports and addresses. + * _object-group-inet6_: specifies this is a cisco extended ipv6 access list, and that object-groups should be used for ports and addresses. * _inet6_: specifies the output be for IPv6 only filters. * _mixed_: specifies output will include both IPv6 and IPv4 filters. * _dsmo_: Enable discontinuous subnet mask summarization. diff --git a/tests/lib/cisco_test.py b/tests/lib/cisco_test.py index a258400c..7eae754b 100644 --- a/tests/lib/cisco_test.py +++ b/tests/lib/cisco_test.py @@ -50,12 +50,18 @@ target:: cisco 50 standard } """ -GOOD_OBJGRP_HEADER = """ +GOOD_OBJGRP_HEADER_1 = """ header { comment:: "obj group header test" target:: cisco objgroupheader object-group } """ +GOOD_OBJGRP_HEADER_2 = """ +header { + comment:: "obj group header test" + target:: cisco objgroupheader object-group-inet6 +} +""" GOOD_INET6_HEADER = """ header { comment:: "inet6 header test" @@ -638,9 +644,8 @@ def testObjectGroup(self): self.naming.GetServiceByProto.return_value = ['80'] pol = policy.ParsePolicy( - GOOD_OBJGRP_HEADER + GOOD_TERM_2 + GOOD_TERM_18, self.naming) + GOOD_OBJGRP_HEADER_1 + GOOD_TERM_2 + GOOD_TERM_18, self.naming) acl = cisco.Cisco(pol, EXP_INFO) - self.assertIn('\n'.join(ip_grp), str(acl), '%s %s' % ( '\n'.join(ip_grp), str(acl))) self.assertIn('\n'.join(port_grp1), str(acl), '%s %s' % ( @@ -664,6 +669,47 @@ def testObjectGroup(self): mock.call('SOME_HOST')]) self.naming.GetServiceByProto.assert_called_once_with('HTTP', 'tcp') + def testObjectGroupInet6(self): + ip_grp = ['object-group network ipv6 SOME_HOST'] + ip_grp.append(' 2001::3/128') + ip_grp.append('exit') + port_grp1 = ['object-group port 80-80'] + port_grp1.append(' eq 80') + port_grp1.append('exit') + port_grp2 = ['object-group port 1024-65535'] + port_grp2.append(' range 1024 65535') + port_grp2.append('exit') + + self.naming.GetNetAddr.return_value = [ + nacaddr.IP('2001::3/128', token='SOME_HOST')] + self.naming.GetServiceByProto.return_value = ['80'] + + pol = policy.ParsePolicy( + GOOD_OBJGRP_HEADER_2 + GOOD_TERM_2 + GOOD_TERM_18, self.naming) + acl = cisco.Cisco(pol, EXP_INFO) + self.assertIn('\n'.join(ip_grp), str(acl), '%s %s' % ( + '\n'.join(ip_grp), str(acl))) + self.assertIn('\n'.join(port_grp1), str(acl), '%s %s' % ( + '\n'.join(port_grp1), str(acl))) + self.assertIn('\n'.join(port_grp2), str(acl), '%s %s' % ( + '\n'.join(port_grp2), str(acl))) + + # Object-group terms should use the object groups created. + self.assertIn( + ' permit tcp any port-group 80-80 net-group SOME_HOST port-group' + ' 1024-65535', str(acl), str(acl)) + self.assertIn( + ' permit ipv6 net-group SOME_HOST net-group SOME_HOST', str(acl), + str(acl)) + + # There should be no addrgroups that look like IP addresses. + for addrgroup in re.findall(r'net-group ([a-f0-9.:/]+)', str(acl)): + self.assertRaises(ValueError, nacaddr.IP(addrgroup)) + + self.naming.GetNetAddr.assert_has_calls([mock.call('SOME_HOST'), + mock.call('SOME_HOST')]) + self.naming.GetServiceByProto.assert_called_once_with('HTTP', 'tcp') + def testInet6(self): self.naming.GetNetAddr.return_value = [nacaddr.IP('10.0.0.0/8'), nacaddr.IP('2001:4860:8000::/33')] diff --git a/tests/lib/ciscoxr_test.py b/tests/lib/ciscoxr_test.py index 0ed33417..0c4fa72b 100644 --- a/tests/lib/ciscoxr_test.py +++ b/tests/lib/ciscoxr_test.py @@ -21,6 +21,11 @@ from capirca.lib import naming from capirca.lib import policy +OBJECT_GROUP_HEADER = """ +header { + target:: ciscoxr foo object-group +} +""" GOOD_HEADER_1 = """ header { @@ -36,11 +41,18 @@ } """ -OBJECT_GROUP_HEADER = """ +OBJECT_GROUP_HEADER_1 = """ header { target:: ciscoxr foo object-group } """ + +OBJECT_GROUP_HEADER_2 = """ +header { + target:: ciscoxr foo object-group-inet6 +} +""" + GOOD_TERM_1 = """ term good-term-1 { source-address:: SOME_HOST @@ -324,7 +336,7 @@ def testBuildWarningTokens(self): def testVerbatimObjectGroup(self): self.naming.GetNetAddr.return_value = [nacaddr.IP('10.1.1.1/32')] - pol = policy.ParsePolicy(OBJECT_GROUP_HEADER + VERBATIM_TERM, self.naming) + pol = policy.ParsePolicy(OBJECT_GROUP_HEADER_1 + VERBATIM_TERM, self.naming) acl = ciscoxr.CiscoXR(pol, EXP_INFO) self.assertIn('permit tcp any', str(acl)) @@ -335,5 +347,13 @@ def testObjectGroup(self): self.assertIn('permit ipv4 any', str(acl)) + def testVerbatimObjectGroupIPv6(self): + self.naming.GetNetAddr.return_value = [nacaddr.IP('2001::3/128')] + pol = policy.ParsePolicy(OBJECT_GROUP_HEADER_2 + VERBATIM_TERM, self.naming) + acl = ciscoxr.CiscoXR(pol, EXP_INFO) + self.assertIn('permit tcp any', str(acl)) + self.assertIn('ipv6 access-list foo', str(acl)) + + if __name__ == '__main__': absltest.main() From 086b8add70ce98aace5df85462ffd6cc509448b5 Mon Sep 17 00:00:00 2001 From: Andrew Bates Date: Wed, 14 Sep 2022 13:11:01 -0400 Subject: [PATCH 2/3] Added additional test --- tests/lib/cisco_test.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/lib/cisco_test.py b/tests/lib/cisco_test.py index 7eae754b..4d7b509d 100644 --- a/tests/lib/cisco_test.py +++ b/tests/lib/cisco_test.py @@ -710,6 +710,18 @@ def testObjectGroupInet6(self): mock.call('SOME_HOST')]) self.naming.GetServiceByProto.assert_called_once_with('HTTP', 'tcp') + # There should be no ipv4 addresses + self.naming.GetNetAddr.reset_mock() + self.naming.GetNetAddr.return_value = [ + nacaddr.IP('192.168.0.1/32', token='SOME_HOST'), + ] + pol = policy.ParsePolicy( + GOOD_OBJGRP_HEADER_2 + GOOD_TERM_2 + GOOD_TERM_18, self.naming) + acl = cisco.Cisco(pol, EXP_INFO) + self.assertNotIn(' permit ip net-group SOME_HOST net-group SOME_HOME', str(acl)) + self.naming.GetNetAddr.assert_has_calls([mock.call('SOME_HOST'), + mock.call('SOME_HOST')]) + def testInet6(self): self.naming.GetNetAddr.return_value = [nacaddr.IP('10.0.0.0/8'), nacaddr.IP('2001:4860:8000::/33')] From a987ef5c7782c8c8a750bcff29ef2192adbd0dcd Mon Sep 17 00:00:00 2001 From: Andrew Bates Date: Wed, 14 Sep 2022 13:12:31 -0400 Subject: [PATCH 3/3] Corrected spelling error --- capirca/lib/cisco.py | 2 +- tests/lib/cisco_test.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/capirca/lib/cisco.py b/capirca/lib/cisco.py index c60a331f..29422159 100644 --- a/capirca/lib/cisco.py +++ b/capirca/lib/cisco.py @@ -947,7 +947,7 @@ def _TranslatePolicy(self, pol, exp_info): exp_info_date = current_date + datetime.timedelta(weeks=exp_info) # a mixed filter outputs both ipv4 and ipv6 acls in the same output file - good_filters = ['extended', 'standard', 'object-group', + good_filters = ['extended', 'standard', 'object-group', 'object-group-inet6', 'inet6', 'mixed', 'enable_dsmo'] for header, terms in pol.filters: diff --git a/tests/lib/cisco_test.py b/tests/lib/cisco_test.py index 4d7b509d..c1a36207 100644 --- a/tests/lib/cisco_test.py +++ b/tests/lib/cisco_test.py @@ -718,7 +718,7 @@ def testObjectGroupInet6(self): pol = policy.ParsePolicy( GOOD_OBJGRP_HEADER_2 + GOOD_TERM_2 + GOOD_TERM_18, self.naming) acl = cisco.Cisco(pol, EXP_INFO) - self.assertNotIn(' permit ip net-group SOME_HOST net-group SOME_HOME', str(acl)) + self.assertNotIn(' permit ip net-group SOME_HOST net-group SOME_HOST', str(acl)) self.naming.GetNetAddr.assert_has_calls([mock.call('SOME_HOST'), mock.call('SOME_HOST')])