def testToDottedQuad(self): net = (1<<32, 4294967264) self.assertRaises(ValueError) net = (3232235584, 1<<16) self.assertRaises(ValueError) net = (3232235584, 4294967264) self.assertEquals(summarizer.ToDottedQuad(net), ('192.168.0.64', '255.255.255.224')) net = (3232235584, 4294901984) self.assertEquals(summarizer.ToDottedQuad(net, negate=True), ('192.168.0.64', '0.0.255.31')) test_data = [((3232235584, 4294967295), True, ('192.168.0.64', '32')), ((3232235584, 4294901760), True, ('192.168.0.64', '16')), ((3232235584, 4294967294), True, ('192.168.0.64', '31')), ((3232235584, 4290772992), True, ('192.168.0.64', '10')), ((3232235584, 4294966016), True, ('192.168.0.64', '255.255.251.0')), ((3232235584, 4294901504), True, ('192.168.0.64', '255.254.255.0'))] for net, nondsm, expected in test_data: self.assertEquals(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 type(addr) is nacaddr.IPv4 or type(addr) is ipaddr.IPv4Network: if addr.numhosts > 1: return '%s %s' % (addr.ip, addr.hostmask) return 'host %s' % (addr.ip) if type(addr) is nacaddr.IPv6 or type(addr) is ipaddr.IPv6Network: if addr.numhosts > 1: return '%s' % (addr.with_prefixlen) return 'host %s' % (addr.ip) # DSMO enabled if type(addr) is tuple: return '%s %s' % summarizer.ToDottedQuad(addr, negate=True) return addr
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, ipaddr.IPv4Network): if addr.numhosts > 1: if self.platform == 'arista': return addr.with_prefixlen return '%s %s' % (addr.ip, addr.hostmask) return 'host %s' % (addr.ip) if isinstance(addr, nacaddr.IPv6) or isinstance( addr, ipaddr.IPv6Network): if addr.numhosts > 1: return addr.with_prefixlen return 'host %s' % (addr.ip) # DSMO enabled if isinstance(addr, tuple): 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)): logging.debug( self.NO_AF_LOG_PROTO.substitute(term=self.term.name, proto=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.value[0] == self._PLATFORM: config.Append(str(next_term.value[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 TcpEstablishedWithNonTcp( '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) # term name config.Append('term %s {' % self.term.name) # 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.ether_type or self.term.flexible_match_range or self.term.forwarding_class or self.term.forwarding_class_except 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) 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: if self.enable_dsmo: config.Append( '%s/%s;' % summarizer.ToDottedQuad(addr, nondsm=True)) else: for comment in self._Comment(addr): config.Append('%s' % comment) 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: if self.enable_dsmo: config.Append( '%s/%s;' % summarizer.ToDottedQuad(addr, nondsm=True)) else: for comment in self._Comment(addr): config.Append('%s' % comment) config.Append('%s;' % addr) for addr in src_addr_ex: if self.enable_dsmo: config.Append( '%s/%s except;' % summarizer.ToDottedQuad(addr, nondsm=True)) else: for comment in self._Comment(addr, exclude=True): config.Append('%s' % comment) 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: if self.enable_dsmo: config.Append( '%s/%s;' % summarizer.ToDottedQuad(addr, nondsm=True)) else: for comment in self._Comment(addr): config.Append('%s' % comment) config.Append('%s;' % addr) for addr in dst_addr_ex: if self.enable_dsmo: config.Append( '%s/%s except;' % summarizer.ToDottedQuad(addr, nondsm=True)) else: for comment in self._Comment(addr, exclude=True): config.Append('%s' % comment) 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)) # forwarding-class-except if self.term.forwarding_class_except: config.Append('forwarding-class-except %s' % self._Group(self.term.forwarding_class_except)) # 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('}') # 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.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; # }" # unique_actions = set(self.extra_actions) if not self.term.routing_instance: unique_actions.update(self.term.action) 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('}') 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.warn( '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])) 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)
def _TermletToStr(self, action, proto, saddr, sport, daddr, dport, icmp_type, option): """Take the various compenents and turn them into a cisco acl line. Args: action: str, action proto: str or int, protocol saddr: str or ipaddr, source address sport: str list or none, the source port daddr: str or ipaddr, the destination address dport: str list or none, the destination port icmp_type: icmp-type numeric specification (if any) option: list or none, optional, eg. 'logging' tokens. Returns: string of the cisco acl line, suitable for printing. Raises: UnsupportedCiscoAccessListError: When unknown icmp-types specified """ # inet4 if type(saddr) is nacaddr.IPv4 or type(saddr) is ipaddr.IPv4Network: if saddr.numhosts > 1: saddr = '%s %s' % (saddr.ip, saddr.hostmask) else: saddr = 'host %s' % (saddr.ip) if type(daddr) is nacaddr.IPv4 or type(daddr) is ipaddr.IPv4Network: if daddr.numhosts > 1: daddr = '%s %s' % (daddr.ip, daddr.hostmask) else: daddr = 'host %s' % (daddr.ip) # inet6 if type(saddr) is nacaddr.IPv6 or type(saddr) is ipaddr.IPv6Network: if saddr.numhosts > 1: saddr = '%s' % (saddr.with_prefixlen) else: saddr = 'host %s' % (saddr.ip) if type(daddr) is nacaddr.IPv6 or type(daddr) is ipaddr.IPv6Network: if daddr.numhosts > 1: daddr = '%s' % (daddr.with_prefixlen) else: daddr = 'host %s' % (daddr.ip) # DSMO enabled if type(saddr) is tuple: saddr = '%s %s' % summarizer.ToDottedQuad(saddr, negate=True) if type(daddr) is tuple: daddr = '%s %s' % summarizer.ToDottedQuad(daddr, negate=True) # fix ports if not sport: sport = '' elif sport[0] != sport[1]: sport = 'range %d %d' % (sport[0], sport[1]) else: sport = 'eq %d' % (sport[0]) if not dport: dport = '' elif dport[0] != dport[1]: dport = 'range %d %d' % (dport[0], dport[1]) else: dport = 'eq %d' % (dport[0]) if not option: option = [''] # Prevent UDP from appending 'established' to ACL line sane_options = list(option) if ((proto == self.PROTO_MAP['udp'] or proto == 'udp') and 'established' in sane_options): sane_options.remove('established') ret_lines = [] # str(icmp_type) is needed to ensure 0 maps to '0' instead of FALSE icmp_type = str(icmp_type) if icmp_type: ret_lines.append(' %s %s %s %s %s %s %s %s' % (action, proto, saddr, sport, daddr, dport, icmp_type, ' '.join(sane_options))) else: ret_lines.append(' %s %s %s %s %s %s %s' % (action, proto, saddr, sport, daddr, dport, ' '.join(sane_options))) # remove any trailing spaces and replace multiple spaces with singles stripped_ret_lines = [ re.sub(r'\s+', ' ', x).rstrip() for x in ret_lines ] return stripped_ret_lines