def testSummarizeToAllSpace(self): nets = [ ipaddr.IPv4Network('0.0.0.0/1'), ipaddr.IPv4Network('128.0.0.0/1'), ] random.shuffle(nets) result = summarizer.Summarize(nets) self.assertEquals(result, [(0, 0)])
def testSummarizeNoNetworks(self): nets = [] for octet in range(0, 256): net = ipaddr.IPv4Network('192.' + str(255 - octet) + '.' + str(octet) + '.64/27') nets.append(net) random.shuffle(nets) result = summarizer.Summarize(nets) self.assertEqual(len(result), 256)
def testSummarizeAllNetworks(self): nets = [] for octet in range(0, 256): net = ipaddr.IPv4Network('192.168.' + str(octet) + '.64/27') nets.append(net) random.shuffle(nets) result = summarizer.Summarize(nets) # summarizes to 192.168.0.64 / 255.255.0.224 self.assertEquals(result, [(3232235584, 4294901984)])
def testSummarizeSomeNetworks(self): nets = [ # continiously summarizable to one /25 ipaddr.IPv4Network('192.168.0.0/27'), ipaddr.IPv4Network('192.168.0.32/27'), ipaddr.IPv4Network('192.168.0.64/27'), ipaddr.IPv4Network('192.168.0.96/27'), # discontiniously summarizable with above ipaddr.IPv4Network('128.168.0.0/25'), # not summarizable with above ipaddr.IPv4Network('10.0.0.0/8'), ] for octet in range(0, 256): net = ipaddr.IPv4Network('172.16.' + str(octet) + '.96/30') nets.append(net) random.shuffle(nets) result = summarizer.Summarize(nets) self.assertEquals(result, [(167772160, 4278190080), (2158493696, 3221225344), (2886729824, 4294902012)])
def testSummarizeDSMONetworks(self): fourth_octet = [ 2, 8, 20, 26, 28, 32, 40, 52, 58, 86, 130, 136, 148, 154, 156, 160, 168, 180, 186, 214 ] nets = list() for octet3 in range(56, 60): for octet4 in fourth_octet: nets.append( ipaddr.IPv4Network('192.168.' + str(octet3) + '.' + str(octet4) + '/31')) result = summarizer.Summarize(nets) self.assertEquals(result, [(3232249858, 4294966398), (3232249888, 4294966398), (3232249908, 4294966398), (3232249942, 4294966398), (3232249864, 4294966366), (3232249876, 4294966390), (3232249882, 4294966366)])
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 __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 '' ret_str = ['\n'] # Don't render icmpv6 protocol terms under inet, or icmp under inet6 if ((self.af == 6 and 'icmp' in self.term.protocol) or (self.af == 4 and 'icmpv6' in self.term.protocol)): logging.debug( self.NO_AF_LOG_PROTO.substitute(term=self.term.name, proto=self.term.protocol, af=self.text_af)) return '' if self.term_remark: ret_str.append(' remark ' + self.term.name) if self.term.owner: self.term.comment.append('Owner: %s' % self.term.owner) for comment in self.term.comment: for line in comment.split('\n'): ret_str.append(' remark ' + str(line)[:100].rstrip()) # Term verbatim output - this will skip over normal term creation # code by returning early. Warnings provided in policy.py. if self.term.verbatim: for next_verbatim in self.term.verbatim: if next_verbatim.value[0] == self.platform: ret_str.append(str(next_verbatim.value[1])) return '\n'.join(ret_str) # protocol if not self.term.protocol: if self.af == 6: protocol = ['ipv6'] elif self.platform == 'ciscoxr': protocol = ['ipv4'] else: protocol = ['ip'] elif self.term.protocol == ['hopopt']: protocol = ['hbh'] elif self.proto_int: protocol = [ proto if proto in self.ALLOWED_PROTO_STRINGS else self.PROTO_MAP.get(proto) for proto in self.term.protocol ] else: protocol = self.term.protocol # source address if self.term.source_address: source_address = self.term.GetAddressOfVersion( 'source_address', self.af) source_address_exclude = self.term.GetAddressOfVersion( 'source_address_exclude', self.af) if source_address_exclude: source_address = nacaddr.ExcludeAddrs(source_address, source_address_exclude) if not source_address: logging.debug( self.NO_AF_LOG_ADDR.substitute(term=self.term.name, direction='source', af=self.text_af)) return '' if self.enable_dsmo: source_address = summarizer.Summarize(source_address) else: # source address not set source_address = ['any'] # destination address if self.term.destination_address: destination_address = self.term.GetAddressOfVersion( 'destination_address', self.af) destination_address_exclude = self.term.GetAddressOfVersion( 'destination_address_exclude', self.af) if destination_address_exclude: destination_address = nacaddr.ExcludeAddrs( destination_address, destination_address_exclude) if not destination_address: logging.debug( self.NO_AF_LOG_ADDR.substitute(term=self.term.name, direction='destination', af=self.text_af)) return '' if self.enable_dsmo: destination_address = summarizer.Summarize(destination_address) else: # destination address not set destination_address = ['any'] # options opts = [str(x) for x in self.term.option] if ((self.PROTO_MAP['tcp'] in protocol or 'tcp' in protocol) and ('tcp-established' in opts or 'established' in opts)): if 'established' not in self.options: self.options.append('established') # ports source_port = [()] destination_port = [()] if self.term.source_port: source_port = self._FixConsecutivePorts(self.term.source_port) if self.term.destination_port: destination_port = self._FixConsecutivePorts( self.term.destination_port) # logging if self.term.logging: self.options.append('log') # dscp; unlike srx, cisco only supports single, non-except values if self.term.dscp_match: if len(self.term.dscp_match) > 1: raise ExtendedAclTermError( 'Extended ACLs cannot specify more than one dscp match value' ) else: self.options.append('dscp %s' % ' '.join(self.term.dscp_match)) # icmp-types icmp_types = [''] if self.term.icmp_type: icmp_types = self.NormalizeIcmpTypes(self.term.icmp_type, self.term.protocol, self.af) fixed_src_addresses = [self._GetIpString(x) for x in source_address] fixed_dst_addresses = [ self._GetIpString(x) for x in destination_address ] fixed_src_ports = [self._FormatPort(x) for x in source_port] fixed_dst_ports = [self._FormatPort(x) for x in destination_port] fixed_opts = {} for p in protocol: fixed_opts[p] = self._FixOptions(p, self.options) for saddr in fixed_src_addresses: for daddr in fixed_dst_addresses: for sport in fixed_src_ports: for dport in fixed_dst_ports: for proto in protocol: opts = fixed_opts[proto] for icmp_type in icmp_types: ret_str.extend( self._TermletToStr( _ACTION_TABLE.get( str(self.term.action[0])), proto, saddr, sport, daddr, dport, icmp_type, opts)) return '\n'.join(ret_str)
def testSummarizeEmptyList(self): nets = [] result = summarizer.Summarize(nets) self.assertEqual(result, [])