Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions capirca/lib/juniper.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -323,7 +324,8 @@ def __str__(self):
self.term.source_prefix or
self.term.source_prefix_except or
self.term.traffic_type or
self.term.ttl)
self.term.ttl or
self.term.ttl_except)

if has_match_criteria:
config.Append('from {')
Expand Down Expand Up @@ -452,6 +454,10 @@ def __str__(self):
if self.term.ttl and self.term_type == 'inet':
config.Append('ttl %s;' % self.term.ttl)

# ttl-except, same logic as ttl above.
if self.term.ttl_except and self.term_type == 'inet':
config.Append('ttl-except %s;' % self.term.ttl_except)

# protocol
if self.term.protocol:
# both are supported on JunOS, but only icmp6 is supported
Expand Down Expand Up @@ -539,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 {')
Expand Down Expand Up @@ -906,6 +917,7 @@ def _BuildTokens(self):
'forwarding_class_except',
'fragment_offset',
'hop_limit',
'hop_limit_except',
'icmp_code',
'logging',
'loss_priority',
Expand All @@ -923,7 +935,8 @@ def _BuildTokens(self):
'source_prefix_except',
'traffic_type',
'traffic_class_count',
'ttl'}
'ttl',
'ttl_except'}
supported_sub_tokens.update({
'option': {
'established',
Expand Down
35 changes: 34 additions & 1 deletion capirca/lib/policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -438,11 +438,13 @@ def __init__(self, obj):
self.source_port = []
self.source_prefix = []
self.ttl = None
self.ttl_except = None
self.verbatim = []
# juniper specific.
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 = []
Expand Down Expand Up @@ -793,6 +795,8 @@ def __str__(self):
ret_str.append(' platform_exclude: %s' % self.platform_exclude)
if self.ttl:
ret_str.append(' ttl: %s' % self.ttl)
if self.ttl_except:
ret_str.append(' ttl_except: %s' % self.ttl_except)
if self.timeout:
ret_str.append(' timeout: %s' % self.timeout)
if self.vpn:
Expand Down Expand Up @@ -884,6 +888,9 @@ def __eq__(self, other):
if self.ttl != other.ttl:
return False

if self.ttl_except != other.ttl_except:
return False

if sorted(self.logging) != sorted(other.logging):
return False
if self.log_limit != other.log_limit:
Expand All @@ -898,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):
Expand Down Expand Up @@ -1234,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:
Expand All @@ -1246,6 +1257,8 @@ def AddObject(self, obj):
self.vpn = (obj.value[0], obj.value[1])
elif obj.var_type is VarType.TTL:
self.ttl = int(obj.value)
elif obj.var_type is VarType.TTL_EXCEPT:
self.ttl_except = int(obj.value)
elif obj.var_type is VarType.TARGET_RESOURCES:
self.target_resources.append(obj.value)
elif obj.var_type is VarType.TARGET_SERVICE_ACCOUNTS:
Expand Down Expand Up @@ -1351,6 +1364,12 @@ def SanityCheck(self):
raise InvalidTermTTLValue('Term %s contains invalid TTL: %s'
% (self.name, self.ttl))

if self.ttl_except:
if not _MIN_TTL <= self.ttl_except <= _MAX_TTL:

raise InvalidTermTTLValue('Term %s contains invalid TTL: %s'
% (self.name, self.ttl_except))

def AddressCleanup(self, optimize=True, addressbook=False):
"""Do Address and Port collapsing.

Expand Down Expand Up @@ -1567,7 +1586,8 @@ class VarType:
PORT_MIRROR = 64
SZONE = 65
DZONE = 66

TTL_EXCEPT = 67
HOP_LIMIT_EXCEPT = 68

def __init__(self, var_type, value):
self.var_type = var_type
Expand Down Expand Up @@ -1752,6 +1772,7 @@ def __ne__(self, other):
'FORWARDING_CLASS_EXCEPT',
'FRAGMENT_OFFSET',
'HOP_LIMIT',
'HOP_LIMIT_EXCEPT',
'APPLY_GROUPS',
'APPLY_GROUPS_EXCEPT',
'HEADER',
Expand Down Expand Up @@ -1800,6 +1821,7 @@ def __ne__(self, other):
'TRAFFIC_CLASS_COUNT',
'TRAFFIC_TYPE',
'TTL',
'TTL_EXCEPT',
'VERBATIM',
'VPN',
)
Expand Down Expand Up @@ -1839,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',
Expand Down Expand Up @@ -1880,6 +1903,7 @@ def __ne__(self, other):
'traffic-class-count': 'TRAFFIC_CLASS_COUNT',
'traffic-type': 'TRAFFIC_TYPE',
'ttl': 'TTL',
'ttl-except': 'TTL_EXCEPT',
'verbatim': 'VERBATIM',
'vpn': 'VPN',
}
Expand Down Expand Up @@ -2023,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
Expand Down Expand Up @@ -2051,6 +2076,7 @@ def p_term_spec(p):
| term_spec target_service_accounts_spec
| term_spec timeout_spec
| term_spec ttl_spec
| term_spec ttl_except_spec
| term_spec traffic_type_spec
| term_spec verbatim_spec
| term_spec vpn_spec
Expand Down Expand Up @@ -2190,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
Expand Down Expand Up @@ -2475,6 +2504,10 @@ def p_ttl_spec(p):
""" ttl_spec : TTL ':' ':' INTEGER """
p[0] = VarType(VarType.TTL, p[4])

def p_ttl_except_spec(p):
""" ttl_except_spec : TTL_EXCEPT ':' ':' INTEGER """
p[0] = VarType(VarType.TTL_EXCEPT, p[4])

def p_filter_term_spec(p):
""" filter_term_spec : FILTER_TERM ':' ':' STRING """
p[0] = VarType(VarType.FILTER_TERM, p[4])
Expand Down
2 changes: 2 additions & 0 deletions doc/generators/juniper.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -81,6 +82,7 @@ The default format is _inet4_, and is implied if not other argument is given.
* _traffic-class-count::_
* _traffic-type::_ specify traffic-type
* _ttl::_ Matches on TTL.
* _ttl-except::_ Allow all TTL "except" specified.
* _verbatim::_ this specifies that the text enclosed within quotes should be rendered into the output without interpretation or modification. This is sometimes used as a temporary workaround while new required features are being added.
## Sub Tokens
### Actions
Expand Down
41 changes: 40 additions & 1 deletion tests/lib/juniper_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -392,6 +398,12 @@
action:: accept
}
"""
GOOD_TERM_38 = """
term good-term-38 {
ttl-except:: 10
action:: accept
}
"""
GOOD_TERM_COMMENT = """
term good-term-comment {
comment:: "This is a COMMENT"
Expand Down Expand Up @@ -582,7 +594,6 @@
action:: deny
}
"""

MIXED_TESTING_TERM = """
term good-term {
protocol:: tcp
Expand Down Expand Up @@ -615,6 +626,7 @@
'forwarding_class_except',
'fragment_offset',
'hop_limit',
'hop_limit_except',
'icmp_code',
'icmp_type',
'stateless_reply',
Expand Down Expand Up @@ -645,6 +657,7 @@
'traffic_type',
'translated',
'ttl',
'ttl_except',
'verbatim'])

SUPPORTED_SUB_TOKENS = {
Expand Down Expand Up @@ -919,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)
Expand Down Expand Up @@ -1504,12 +1531,24 @@ def testTTL(self):
output = str(jcl)
self.assertIn('ttl 10;', output)

def testTTLExcept(self):
jcl = juniper.Juniper(policy.ParsePolicy(GOOD_HEADER + GOOD_TERM_38,
self.naming), EXP_INFO)
output = str(jcl)
self.assertIn('ttl-except 10;', output)

def testTTLInet6(self):
jcl = juniper.Juniper(policy.ParsePolicy(GOOD_HEADER_V6 + GOOD_TERM_21,
self.naming), EXP_INFO)
output = str(jcl)
self.assertNotIn('ttl 10;', output)

def testTTLExceptInet6(self):
jcl = juniper.Juniper(policy.ParsePolicy(GOOD_HEADER_V6 + GOOD_TERM_38,
self.naming), EXP_INFO)
output = str(jcl)
self.assertNotIn('ttl-except 10;', output)

def testNextIpFormat(self):
self.naming.GetNetAddr.return_value = [nacaddr.IP('10.1.1.1/32')]

Expand Down
35 changes: 35 additions & 0 deletions tests/lib/policy_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,12 @@
action:: accept
}
"""
GOOD_TERM_49 = """
term good-term-59 {
ttl-except:: 10
action:: accept
}
"""
GOOD_TERM_V6_1 = """
term good-term-v6-1 {
hop-limit:: 5
Expand All @@ -451,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 {
Expand Down Expand Up @@ -584,6 +596,12 @@
action:: accept
}
"""
BAD_TERM_17 = """
term bad-term-17 {
ttl-except:: 257
action:: accept
}
"""

# pylint: disable=maybe-no-member

Expand Down Expand Up @@ -683,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([])
Expand Down Expand Up @@ -1269,6 +1294,16 @@ def testInvalidTTL(self):
self.assertRaises(policy.InvalidTermTTLValue, policy.ParsePolicy,
pol, self.naming)

def testTTLExcept(self):
pol = HEADER + GOOD_TERM_49
result = policy.ParsePolicy(pol, self.naming)
self.assertIn('ttl_except: 10', str(result))

def testInvalidTTLExcept(self):
pol = HEADER + BAD_TERM_17
self.assertRaises(policy.InvalidTermTTLValue, policy.ParsePolicy,
pol, self.naming)

def testNeedAddressBook(self):
pol1 = policy.ParsePolicy(HEADER + GOOD_TERM_1, self.naming)
pol2 = policy.ParsePolicy(HEADER_SRX + GOOD_TERM_1, self.naming)
Expand Down
Loading