diff --git a/capirca/lib/juniper.py b/capirca/lib/juniper.py index 575984f7..954a178e 100644 --- a/capirca/lib/juniper.py +++ b/capirca/lib/juniper.py @@ -313,6 +313,7 @@ def __str__(self): self.term.forwarding_class_except or self.term.fragment_offset or self.term.hop_limit or + self.term.hop_limit_except or self.term.next_ip or self.term.port or self.term.precedence or @@ -544,6 +545,11 @@ def __str__(self): if self.term_type == 'inet6': config.Append('hop-limit %s;' % (self.term.hop_limit)) + if self.term.hop_limit_except: + # Only generate a hop-limit-except if inet6, inet4 has not hop-limit-except. + if self.term_type == 'inet6': + config.Append('hop-limit-except %s;' % (self.term.hop_limit_except)) + # flexible-match if self.term.flexible_match_range: config.Append('flexible-match-range {') @@ -911,6 +917,7 @@ def _BuildTokens(self): 'forwarding_class_except', 'fragment_offset', 'hop_limit', + 'hop_limit_except', 'icmp_code', 'logging', 'loss_priority', diff --git a/capirca/lib/policy.py b/capirca/lib/policy.py index 47cddd36..601a6afc 100644 --- a/capirca/lib/policy.py +++ b/capirca/lib/policy.py @@ -444,6 +444,7 @@ def __init__(self, obj): self.packet_length = None self.fragment_offset = None self.hop_limit = None + self.hop_limit_except = None self.icmp_type = [] self.icmp_code = [] self.ether_type = [] @@ -904,6 +905,8 @@ def __eq__(self, other): return False if self.hop_limit != other.hop_limit: return False + if self.hop_limit_except != other.hop_limit_except: + return False if sorted(self.icmp_type) != sorted(other.icmp_type): return False if sorted(self.icmp_code) != sorted(other.icmp_code): @@ -1240,6 +1243,8 @@ def AddObject(self, obj): self.fragment_offset = obj.value elif obj.var_type is VarType.HOP_LIMIT: self.hop_limit = obj.value + elif obj.var_type is VarType.HOP_LIMIT_EXCEPT: + self.hop_limit_except = obj.value elif obj.var_type is VarType.SINTERFACE: self.source_interface = obj.value elif obj.var_type is VarType.DINTERFACE: @@ -1582,6 +1587,7 @@ class VarType: SZONE = 65 DZONE = 66 TTL_EXCEPT = 67 + HOP_LIMIT_EXCEPT = 68 def __init__(self, var_type, value): self.var_type = var_type @@ -1766,6 +1772,7 @@ def __ne__(self, other): 'FORWARDING_CLASS_EXCEPT', 'FRAGMENT_OFFSET', 'HOP_LIMIT', + 'HOP_LIMIT_EXCEPT', 'APPLY_GROUPS', 'APPLY_GROUPS_EXCEPT', 'HEADER', @@ -1854,6 +1861,7 @@ def __ne__(self, other): 'fragment-offset': 'FRAGMENT_OFFSET', 'hex': 'HEX', 'hop-limit': 'HOP_LIMIT', + 'hop-limit-except': 'HOP_LIMIT_EXCEPT', 'apply-groups': 'APPLY_GROUPS', 'apply-groups-except': 'APPLY_GROUPS_EXCEPT', 'header': 'HEADER', @@ -2039,6 +2047,7 @@ def p_term_spec(p): | term_spec forwarding_class_except_spec | term_spec fragment_offset_spec | term_spec hop_limit_spec + | term_spec hop_limit_except_spec | term_spec icmp_type_spec | term_spec icmp_code_spec | term_spec interface_spec @@ -2207,6 +2216,9 @@ def p_hop_limit_spec(p): else: p[0] = VarType(VarType.HOP_LIMIT, str(p[4]) + '-' + str(p[6])) +def p_hop_limit_except_spec(p): + """ hop_limit_except_spec : HOP_LIMIT_EXCEPT ':' ':' INTEGER""" + p[0] = VarType(VarType.HOP_LIMIT_EXCEPT, p[4]) def p_one_or_more_dscps(p): """ one_or_more_dscps : one_or_more_dscps DSCP_RANGE diff --git a/doc/generators/juniper.md b/doc/generators/juniper.md index abc4b405..37f4d7de 100644 --- a/doc/generators/juniper.md +++ b/doc/generators/juniper.md @@ -54,6 +54,7 @@ The default format is _inet4_, and is implied if not other argument is given. * _forwarding-class_except::_ Do not match the specified forwarding classes. * _fragement-offset::_ specify a fragment offset of a fragmented packet * _hop-limit::_ Match the hop limit to the specified hop limit or set of hop limits. +* _hop-limit-except::_ Allow all hop limits "except" the one specified. * _icmp-code::_ Specifies the ICMP code to filter on. * _icmp-type::_ Specify icmp-type code to match, see section [ICMP TYPES](PolicyFormat#ICMP_TYPES.md) for list of valid arguments * _logging::_ Specify that this packet should be logged via syslog. diff --git a/tests/lib/juniper_test.py b/tests/lib/juniper_test.py index 022ef0d9..172fff0f 100644 --- a/tests/lib/juniper_test.py +++ b/tests/lib/juniper_test.py @@ -251,6 +251,12 @@ action:: accept } """ +GOOD_TERM_V6_HOP_LIMIT_EXCEPT = """ +term good-term-v6-hl { + hop-limit-except:: 25 + action:: accept +} +""" GOOD_TERM_20_V6 = """ term good-term-20-v6 { protocol-except:: icmpv6 @@ -588,7 +594,6 @@ action:: deny } """ - MIXED_TESTING_TERM = """ term good-term { protocol:: tcp @@ -621,6 +626,7 @@ 'forwarding_class_except', 'fragment_offset', 'hop_limit', + 'hop_limit_except', 'icmp_code', 'icmp_type', 'stateless_reply', @@ -926,6 +932,20 @@ def testHopLimitInet(self): output = str(jcl) self.assertNotIn('hop-limit 25;', output, output) + def testHopLimitExcept(self): + jcl = juniper.Juniper(policy.ParsePolicy(GOOD_HEADER_V6 + + GOOD_TERM_V6_HOP_LIMIT_EXCEPT, + self.naming), EXP_INFO) + output = str(jcl) + self.assertIn('hop-limit-except 25;', output, output) + + def testHopLimitExceptInet(self): + jcl = juniper.Juniper(policy.ParsePolicy(GOOD_HEADER + + GOOD_TERM_V6_HOP_LIMIT_EXCEPT, + self.naming), EXP_INFO) + output = str(jcl) + self.assertNotIn('hop-limit-except 25;', output, output) + def testProtocolExcept(self): jcl = juniper.Juniper(policy.ParsePolicy(GOOD_HEADER_V6 + GOOD_TERM_7, self.naming), EXP_INFO) diff --git a/tests/lib/policy_test.py b/tests/lib/policy_test.py index 5fd4d0eb..6ce2a49c 100644 --- a/tests/lib/policy_test.py +++ b/tests/lib/policy_test.py @@ -457,6 +457,12 @@ action:: accept } """ +GOOD_TERM_V6_HOP_LIMIT_EXCEPT = """ +term good-term-v6-1 { + hop-limit-except:: 5 + action:: accept +} +""" TERM_SUPER_2 = """ term term-super { @@ -695,6 +701,13 @@ def testHopLimitRange(self): _, terms = ret.filters[0] self.assertEqual(str(terms[0].hop_limit[2]), '7') + def testHopLimitExcept(self): + pol = HEADER_V6 + GOOD_TERM_V6_HOP_LIMIT_EXCEPT + ret = policy.ParsePolicy(pol, self.naming) + self.assertEqual(len(ret.filters), 1) + _, terms = ret.filters[0] + self.assertEqual(str(terms[0].hop_limit_except[0]), '5') + def testBadPortProtocols(self): pol = HEADER + BAD_TERM_3 self.naming.GetServiceByProto('SNMP', 'tcp').AndReturn([]) diff --git a/tests/lib/srxlo_test.py b/tests/lib/srxlo_test.py index e88d7ee3..65c762c7 100644 --- a/tests/lib/srxlo_test.py +++ b/tests/lib/srxlo_test.py @@ -93,6 +93,7 @@ 'forwarding_class_except', 'fragment_offset', 'hop_limit', + 'hop_limit_except', 'icmp_code', 'icmp_type', 'stateless_reply',