def testToDottedQuad(self): net = summarizer.DSMNet(1 << 32, 4294967264) self.assertRaises(ValueError) net = summarizer.DSMNet(3232235584, 1 << 16) self.assertRaises(ValueError) net = summarizer.DSMNet(3232235584, 4294967264) self.assertEqual(summarizer.ToDottedQuad(net), ('192.168.0.64', '255.255.255.224')) net = summarizer.DSMNet(3232235584, 4294901984) self.assertEqual(summarizer.ToDottedQuad(net, negate=True), ('192.168.0.64', '0.0.255.31')) test_data = [ (summarizer.DSMNet(3232235584, 4294967295), True, ('192.168.0.64', '32')), (summarizer.DSMNet(3232235584, 4294901760), True, ('192.168.0.64', '16')), (summarizer.DSMNet(3232235584, 4294967294), True, ('192.168.0.64', '31')), (summarizer.DSMNet(3232235584, 4290772992), True, ('192.168.0.64', '10')), (summarizer.DSMNet(3232235584, 4294966016), True, ('192.168.0.64', '255.255.251.0')), (summarizer.DSMNet(3232235584, 4294901504), True, ('192.168.0.64', '255.254.255.0')) ] for net, nondsm, expected in test_data: self.assertEqual(summarizer.ToDottedQuad(net, nondsm=nondsm), expected)
def _GetIpString(self, addr): """Formats the address object for printing in the ACL. Args: addr: str or ipaddr, address Returns: An address string suitable for the ACL. """ if isinstance(addr, nacaddr.IPv4) or isinstance( addr, ipaddress.IPv4Network): addr = cast(self.IPV4_ADDRESS, addr) if addr.num_addresses > 1: if self.platform == 'arista': return addr.with_prefixlen return '%s %s' % (addr.network_address, addr.hostmask) return 'host %s' % (addr.network_address) if isinstance(addr, nacaddr.IPv6) or isinstance( addr, ipaddress.IPv6Network): addr = cast(self.IPV6_ADDRESS, addr) if addr.num_addresses > 1: return addr.with_prefixlen return 'host %s' % (addr.network_address) # DSMO enabled if isinstance(addr, summarizer.DSMNet): return '%s %s' % summarizer.ToDottedQuad(addr, negate=True) return addr
def __str__(self): # Verify platform specific terms. Skip whole term if platform does not # match. if self.term.platform: if self._PLATFORM not in self.term.platform: return '' if self.term.platform_exclude: if self._PLATFORM in self.term.platform_exclude: return '' config = Config(indent=self._DEFAULT_INDENT) from_str = [] # Don't render icmpv6 protocol terms under inet, or icmp under inet6 if ((self.term_type == 'inet6' and 'icmp' in self.term.protocol) or (self.term_type == 'inet' and ('icmpv6' in self.term.protocol or 'icmp6' in self.term.protocol))): logging.debug( self.NO_AF_LOG_PROTO.substitute(term=self.term.name, proto=', '.join( self.term.protocol), af=self.term_type)) return '' # comment # this deals just fine with multi line comments, but we could probably # output them a little cleaner; do things like make sure the # len(output) < 80, etc. Note, if 'noverbose' is set for the filter, skip # all comment processing. if self.term.owner and not self.noverbose: self.term.comment.append('Owner: %s' % self.term.owner) if self.term.comment and not self.noverbose: config.Append('/*') for comment in self.term.comment: for line in comment.split('\n'): config.Append('** ' + line) config.Append('*/') # Term verbatim output - this will skip over normal term creation # code. Warning generated from policy.py if appropriate. if self.term.verbatim: for next_term in self.term.verbatim: if next_term[0] == self._PLATFORM: config.Append(str(next_term[1]), verbatim=True) return str(config) # Helper for per-address-family keywords. family_keywords = self._TERM_TYPE.get(self.term_type) # option # this is going to be a little ugly b/c there are a few little messed # up options we can deal with. if self.term.option: for opt in [str(x) for x in self.term.option]: # there should be a better way to search the array of protocols if opt.startswith('sample'): self.extra_actions.append('sample') # only append tcp-established for option established when # tcp is the only protocol, otherwise other protos break on juniper elif opt.startswith('established'): if self.term.protocol == ['tcp']: if 'tcp-established;' not in from_str: from_str.append(family_keywords['tcp-est'] + ';') # if tcp-established specified, but more than just tcp is included # in the protocols, raise an error elif opt.startswith('tcp-established'): flag = family_keywords['tcp-est'] + ';' if self.term.protocol == ['tcp']: if flag not in from_str: from_str.append(flag) else: raise TcpEstablishedWithNonTcpError( 'tcp-established can only be used with tcp protocol in term %s' % self.term.name) elif opt.startswith('rst'): from_str.append('tcp-flags "rst";') elif opt.startswith('initial') and 'tcp' in self.term.protocol: from_str.append('tcp-initial;') elif opt.startswith('first-fragment'): from_str.append('first-fragment;') # we don't have a special way of dealing with this, so we output it and # hope the user knows what they're doing. else: from_str.append('%s;' % opt) # if the term is inactive we have to set the prefix if self.term.inactive: term_prefix = 'inactive:' else: term_prefix = '' # term name config.Append('%s term %s {' % (term_prefix, self.term.name)) # The "filter" keyword is not compatible with from or then if self.term.filter_term: config.Append('filter %s;' % self.term.filter_term) config.Append('}') # end term accept-foo-to-bar { ... } return str(config) # a default action term doesn't have any from { clause has_match_criteria = ( self.term.address or self.term.dscp_except or self.term.dscp_match or self.term.destination_address or self.term.destination_port or self.term.destination_prefix or self.term.destination_prefix_except or self.term.encapsulate or self.term.ether_type or self.term.flexible_match_range or self.term.forwarding_class or self.term.forwarding_class_except or self.term.fragment_offset or self.term.hop_limit or self.term.next_ip or self.term.port or self.term.precedence or self.term.protocol or self.term.protocol_except or self.term.source_address or self.term.source_port or self.term.source_prefix or self.term.source_prefix_except or self.term.traffic_type or self.term.ttl) if has_match_criteria: config.Append('from {') term_af = self.AF_MAP.get(self.term_type) # address address = self.term.GetAddressOfVersion('address', term_af) if self.enable_dsmo: address = summarizer.Summarize(address) if address: config.Append('%s {' % family_keywords['addr']) for addr in address: for comment in self._Comment(addr): config.Append('%s' % comment) if self.enable_dsmo: config.Append( '%s/%s;' % summarizer.ToDottedQuad(addr, nondsm=True)) else: config.Append('%s;' % addr) config.Append('}') elif self.term.address: logging.debug( self.NO_AF_LOG_ADDR.substitute(term=self.term.name, af=self.term_type)) return '' # source address src_addr = self.term.GetAddressOfVersion('source_address', term_af) src_addr_ex = self.term.GetAddressOfVersion( 'source_address_exclude', term_af) if self.enable_dsmo: src_addr = summarizer.Summarize(src_addr) src_addr_ex = summarizer.Summarize(src_addr_ex) else: src_addr, src_addr_ex = self._MinimizePrefixes( src_addr, src_addr_ex) if src_addr: config.Append('%s {' % family_keywords['saddr']) for addr in src_addr: for comment in self._Comment(addr): config.Append('%s' % comment) if self.enable_dsmo: config.Append( '%s/%s;' % summarizer.ToDottedQuad(addr, nondsm=True)) else: config.Append('%s;' % addr) for addr in src_addr_ex: for comment in self._Comment(addr, exclude=True): config.Append('%s' % comment) if self.enable_dsmo: config.Append( '%s/%s except;' % summarizer.ToDottedQuad(addr, nondsm=True)) else: config.Append('%s except;' % addr) config.Append('}') elif self.term.source_address: logging.debug( self.NO_AF_LOG_ADDR.substitute(term=self.term.name, direction='source', af=self.term_type)) return '' # destination address dst_addr = self.term.GetAddressOfVersion('destination_address', term_af) dst_addr_ex = self.term.GetAddressOfVersion( 'destination_address_exclude', term_af) if self.enable_dsmo: dst_addr = summarizer.Summarize(dst_addr) dst_addr_ex = summarizer.Summarize(dst_addr_ex) else: dst_addr, dst_addr_ex = self._MinimizePrefixes( dst_addr, dst_addr_ex) if dst_addr: config.Append('%s {' % family_keywords['daddr']) for addr in dst_addr: for comment in self._Comment(addr): config.Append('%s' % comment) if self.enable_dsmo: config.Append( '%s/%s;' % summarizer.ToDottedQuad(addr, nondsm=True)) else: config.Append('%s;' % addr) for addr in dst_addr_ex: for comment in self._Comment(addr, exclude=True): config.Append('%s' % comment) if self.enable_dsmo: config.Append( '%s/%s except;' % summarizer.ToDottedQuad(addr, nondsm=True)) else: config.Append('%s except;' % addr) config.Append('}') elif self.term.destination_address: logging.debug( self.NO_AF_LOG_ADDR.substitute(term=self.term.name, direction='destination', af=self.term_type)) return '' # forwarding-class if self.term.forwarding_class: config.Append( 'forwarding-class %s' % self._Group(self.term.forwarding_class, lc=False)) # forwarding-class-except if self.term.forwarding_class_except: config.Append( 'forwarding-class-except %s' % self._Group(self.term.forwarding_class_except, lc=False)) # source prefix <except> list if self.term.source_prefix or self.term.source_prefix_except: config.Append('source-prefix-list {') for pfx in self.term.source_prefix: config.Append(pfx + ';') for epfx in self.term.source_prefix_except: config.Append(epfx + ' except;') config.Append('}') # destination prefix <except> list if self.term.destination_prefix or self.term.destination_prefix_except: config.Append('destination-prefix-list {') for pfx in self.term.destination_prefix: config.Append(pfx + ';') for epfx in self.term.destination_prefix_except: config.Append(epfx + ' except;') config.Append('}') # Only generate ttl if inet, inet6 uses hop-limit instead. if self.term.ttl and self.term_type == 'inet': config.Append('ttl %s;' % self.term.ttl) # protocol if self.term.protocol: # both are supported on JunOS, but only icmp6 is supported # on SRX loopback stateless filter config.Append(family_keywords['protocol'] + ' ' + self._Group(self.term.protocol)) # protocol if self.term.protocol_except: # same as above config.Append(family_keywords['protocol-except'] + ' ' + self._Group(self.term.protocol_except)) # port if self.term.port: config.Append('port %s' % self._Group(self.term.port)) # source port if self.term.source_port: config.Append('source-port %s' % self._Group(self.term.source_port)) # destination port if self.term.destination_port: config.Append('destination-port %s' % self._Group(self.term.destination_port)) # append any options beloging in the from {} section for next_str in from_str: config.Append(next_str) # packet length if self.term.packet_length: config.Append('packet-length %s;' % self.term.packet_length) # fragment offset if self.term.fragment_offset: config.Append('fragment-offset %s;' % self.term.fragment_offset) # icmp-types icmp_types = [''] if self.term.icmp_type: icmp_types = self.NormalizeIcmpTypes(self.term.icmp_type, self.term.protocol, self.term_type) if icmp_types != ['']: config.Append('icmp-type %s' % self._Group(icmp_types)) if self.term.icmp_code: config.Append('icmp-code %s' % self._Group(self.term.icmp_code)) if self.term.ether_type: config.Append('ether-type %s' % self._Group(self.term.ether_type)) if self.term.traffic_type: config.Append('traffic-type %s' % self._Group(self.term.traffic_type)) if self.term.precedence: # precedence may be a single integer, or a space separated list policy_precedences = set() # precedence values may only be 0 through 7 for precedence in self.term.precedence: if int(precedence) in range(0, 8): policy_precedences.add(precedence) else: raise PrecedenceError( 'Precedence value %s is out of bounds in %s' % (precedence, self.term.name)) config.Append('precedence %s' % self._Group(sorted(policy_precedences))) # DSCP Match if self.term.dscp_match: if self.term_type == 'inet6': config.Append('traffic-class [ %s ];' % (' '.join(self.term.dscp_match))) else: config.Append('dscp [ %s ];' % ' '.join(self.term.dscp_match)) # DSCP Except if self.term.dscp_except: if self.term_type == 'inet6': config.Append('traffic-class-except [ %s ];' % (' '.join(self.term.dscp_except))) else: config.Append('dscp-except [ %s ];' % ' '.join(self.term.dscp_except)) if self.term.hop_limit: # Only generate a hop-limit if inet6, inet4 has not hop-limit. if self.term_type == 'inet6': config.Append('hop-limit %s;' % (self.term.hop_limit)) # flexible-match if self.term.flexible_match_range: config.Append('flexible-match-range {') for fm_opt in self.term.flexible_match_range: config.Append('%s %s;' % (fm_opt[0], fm_opt[1])) config.Append('}') config.Append('}') # end from { ... } #### # ACTIONS go below here #### # If the action is only one line, include it in the same line as "then " # statement. # For example, if the action is only accept, it should be: # "then accept;" rather than: # "then { # accept; # }" # self.CheckTerminatingAction() unique_actions = set(self.extra_actions) if not self.term.routing_instance: unique_actions.update(self.term.action) if self.term.encapsulate: unique_actions.add('encapsulate') if len(unique_actions) <= 1: for action in [ self.term.logging, self.term.routing_instance, self.term.counter, self.term.policer, self.term.qos, self.term.loss_priority, self.term.dscp_set, self.term.next_ip, self.term.traffic_class_count ]: if action: try: unique_actions.update(action) except TypeError: unique_actions.add(action) if len(unique_actions) > 1: break if len(unique_actions) == 1: # b/21795531: Juniper device treats a set of IPv4 actions differently # than any other actions. # For example, if the term is in IPv4 and the action is only discard, # it should be: # "then { # discard; # }" rather than: # "then discard;" current_action = self.ACTIONS.get(unique_actions.pop(), 'next_ip') if (self.term_type == 'inet' and current_action in [ 'discard', 'reject', 'reject tcp-reset' ]) or (self.term_type == 'inet6' and current_action in ['reject', 'reject tcp-reset']): config.Append('then {') config.Append('%s;' % current_action) config.Append('}') elif current_action == 'next_ip': self.NextIpCheck(self.term.next_ip, self.term.name) config.Append('then {') if self.term.next_ip[0].version == 4: config.Append('next-ip %s;' % str(self.term.next_ip[0])) else: config.Append('next-ip6 %s;' % str(self.term.next_ip[0])) config.Append('}') elif current_action == 'encapsulate': config.Append('then {') config.Append('encapsulate %s;' % str(self.term.encapsulate)) config.Append('}') else: config.Append('then %s;' % current_action) elif len(unique_actions) > 1: config.Append('then {') # logging if self.term.logging: for log_target in self.term.logging: if str(log_target) == 'local': config.Append('log;') else: config.Append('syslog;') if self.term.routing_instance: config.Append('routing-instance %s;' % self.term.routing_instance) if self.term.counter: config.Append('count %s;' % self.term.counter) if self.term.traffic_class_count: config.Append('traffic-class-count %s;' % self.term.traffic_class_count) oid_length = 128 if self.term.policer: config.Append('policer %s;' % self.term.policer) if len(self.term.policer) > oid_length: logging.warning( 'WARNING: %s is longer than %d bytes. Due to ' 'limitation in JUNOS, OIDs longer than %dB can ' 'cause SNMP timeout issues.', self.term.policer, oid_length, oid_length) if self.term.qos: config.Append('forwarding-class %s;' % self.term.qos) if self.term.loss_priority: config.Append('loss-priority %s;' % self.term.loss_priority) if self.term.next_ip: self.NextIpCheck(self.term.next_ip, self.term.name) if self.term.next_ip[0].version == 4: config.Append('next-ip %s;' % str(self.term.next_ip[0])) else: config.Append('next-ip6 %s;' % str(self.term.next_ip[0])) if self.term.encapsulate: config.Append('encapsulate %s;' % str(self.term.encapsulate)) for action in self.extra_actions: config.Append(action + ';') # If there is a routing-instance defined, skip reject/accept/etc actions. if not self.term.routing_instance: for action in self.term.action: config.Append(self.ACTIONS.get(action) + ';') # DSCP SET if self.term.dscp_set: if self.term_type == 'inet6': config.Append('traffic-class %s;' % self.term.dscp_set) else: config.Append('dscp %s;' % self.term.dscp_set) config.Append('}') # end then{...} config.Append('}') # end term accept-foo-to-bar { ... } return str(config)