diff --git a/capirca/lib/cisco.py b/capirca/lib/cisco.py index b1528176..699cada4 100644 --- a/capirca/lib/cisco.py +++ b/capirca/lib/cisco.py @@ -837,6 +837,15 @@ def __str__(self): else self.PROTO_MAP.get(proto) for proto in self.term.protocol] + # icmp lookups + icmp_types = [''] + if self.term.icmp_type: + icmp_types = self.NormalizeIcmpTypes(self.term.icmp_type, + self.term.protocol, self.af) + icmp_codes = [''] + if self.term.icmp_code: + icmp_codes = self.term.icmp_code + # addresses source_address = self.term.source_address if not self.term.source_address: @@ -859,14 +868,24 @@ def __str__(self): for sport in source_port: for dport in destination_port: for proto in protocol: - ret_str.append( - self._TermletToStr(_ACTION_TABLE.get(str( - self.term.action[0])), proto, saddr, sport, daddr, dport)) + # cisconx uses icmp for both ipv4 and ipv6 + if self.platform == 'cisconx': + if self.af == 6: + proto = 'icmp' if proto == 'icmpv6' else proto + for icmp_type in icmp_types: + for icmp_code in icmp_codes: + ret_str.append( + self._TermletToStr(_ACTION_TABLE.get(str( + self.term.action[0])), proto, saddr, sport, daddr, dport, icmp_type, icmp_code)) return '\n'.join(ret_str) - def _TermletToStr(self, action, proto, saddr, sport, daddr, dport): + def _TermletToStr(self, action, proto, saddr, sport, daddr, dport, icmp_type, icmp_code): """Output a portion of a cisco term/filter only, based on the 5-tuple.""" + # str(icmp_type) is needed to ensure 0 maps to '0' instead of FALSE + icmp_type = str(icmp_type) + icmp_code = str(icmp_code) + # Empty addr/port destinations should emit 'any' if saddr and saddr != 'any': saddr = 'net-group %s' % saddr @@ -882,6 +901,11 @@ def _TermletToStr(self, action, proto, saddr, sport, daddr, dport): else: dport = '' + if icmp_type: + if icmp_code: + return (' %s %s %s %s %s %s' % (action, proto, saddr, daddr, icmp_type, icmp_code)).rstrip() + return (' %s %s %s %s %s' % (action, proto, saddr, daddr, icmp_type)).rstrip() + return (' %s %s %s%s %s%s' % ( action, proto, saddr, sport, daddr, dport)).rstrip() diff --git a/tests/lib/cisco_test.py b/tests/lib/cisco_test.py index 81239d2d..2acbd712 100644 --- a/tests/lib/cisco_test.py +++ b/tests/lib/cisco_test.py @@ -340,6 +340,23 @@ action:: accept } """ +GOOD_TERM_24 = """ +term good_term_24 { + protocol:: icmp + source-address:: SOME_HOST + icmp-type:: echo-reply + action:: accept +} +""" +GOOD_TERM_25 = """ +term good_term_25 { + protocol:: icmp + source-address:: SOME_HOST + icmp-type:: unreachable + icmp-code:: 13 + action:: accept +} +""" LONG_COMMENT_TERM = """ term long-comment-term { comment:: "%s " @@ -656,6 +673,21 @@ def testObjectGroup(self): self.naming.GetNetAddr.assert_has_calls([mock.call('SOME_HOST'), mock.call('SOME_HOST')]) self.naming.GetServiceByProto.assert_called_once_with('HTTP', 'tcp') + + def testObjectGroupIcmp(self): + ip_grp = ['object-group network ipv4 SOME_HOST'] + ip_grp.append(' 10.0.0.0/8') + ip_grp.append('exit') + + self.naming.GetNetAddr.return_value = [ + nacaddr.IP('10.0.0.0/8', token='SOME_HOST')] + + pol = policy.ParsePolicy( + GOOD_OBJGRP_HEADER + GOOD_TERM_24 + GOOD_TERM_25, self.naming) + acl = cisco.Cisco(pol, EXP_INFO) + + self.assertIn(' permit icmp net-group SOME_HOST any 0', str(acl)) + self.assertIn(' permit icmp net-group SOME_HOST any 3 13', str(acl)) def testInet6(self): self.naming.GetNetAddr.return_value = [nacaddr.IP('10.0.0.0/8'),