def _CalculateAddresses(self, src_addr_list, src_ex_addr_list, dst_addr_list, dst_ex_addr_list): """Calculate source and destination address list for a term. Since ipset is very efficient at matching large number of addresses, we never return eny exclude addresses. Instead least positive match is calculated for both source and destination addresses. For source and destination address list, three cases are possible. First case is when there is no addresses. In that case we return _all_ips. Second case is when there is strictly one address. In that case, we optimize by not generating a set, and it's then the only element of returned set. Third case case is when there is more than one address in a set. In that case we generate a set and also return _all_ips. Note the difference to the first case where no set is actually generated. Args: src_addr_list: source address list of the term. src_ex_addr_list: source address exclude list of the term. dst_addr_list: destination address list of the term. dst_ex_addr_list: destination address exclude list of the term. Returns: tuple containing source address list, source exclude address list, destination address list, destination exclude address list in that order. """ if not src_addr_list: src_addr_list = [self._all_ips] src_addr_list = [src_addr for src_addr in src_addr_list if src_addr.version == self.AF_MAP[self.af]] if src_ex_addr_list: src_ex_addr_list = [src_ex_addr for src_ex_addr in src_ex_addr_list if src_ex_addr.version == self.AF_MAP[self.af]] src_addr_list = nacaddr.ExcludeAddrs(src_addr_list, src_ex_addr_list) if len(src_addr_list) > 1: set_name = self._GenerateSetName(self.term.name, 'src') self.addr_sets['src'] = (set_name, src_addr_list) src_addr_list = [self._all_ips] if not dst_addr_list: dst_addr_list = [self._all_ips] dst_addr_list = [dst_addr for dst_addr in dst_addr_list if dst_addr.version == self.AF_MAP[self.af]] if dst_ex_addr_list: dst_ex_addr_list = [dst_ex_addr for dst_ex_addr in dst_ex_addr_list if dst_ex_addr.version == self.AF_MAP[self.af]] dst_addr_list = nacaddr.ExcludeAddrs(dst_addr_list, dst_ex_addr_list) if len(dst_addr_list) > 1: set_name = self._GenerateSetName(self.term.name, 'dst') self.addr_sets['dst'] = (set_name, dst_addr_list) dst_addr_list = [self._all_ips] return (src_addr_list, [], dst_addr_list, [])
def _CalculateAddrList(self, addr_list, addr_exclude_list, target_af, direction): """Calculates and stores address list for target AF and direction. Args: addr_list: address list. addr_exclude_list: address exclude list of the term. target_af: target address family. direction: direction in which address list will be used. Returns: calculated address list. """ if not addr_list: addr_list = [self._all_ips] addr_list = [addr for addr in addr_list if addr.version == target_af] if addr_exclude_list: addr_exclude_list = [addr_exclude for addr_exclude in addr_exclude_list if addr_exclude.version == target_af] addr_list = nacaddr.ExcludeAddrs(addr_list, addr_exclude_list) if len(addr_list) > 1: set_name = self._GenerateSetName(self.term.name, direction) self.addr_sets[direction] = (set_name, addr_list) addr_list = [self._all_ips] return addr_list
def __str__(self): # Verify platform specific terms. Skip whole term if platform does not # match. if self.term.platform: if 'ciscoasa' not in self.term.platform: return '' if self.term.platform_exclude: if 'ciscoasa' 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)): ret_str.append('remark Term %s' % self.term.name) ret_str.append('remark not rendered due to protocol/AF mismatch.') return '\n'.join(ret_str) ret_str.append('access-list %s remark %s' % (self.filter_name, 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('access-list %s remark %s' % (self.filter_name, str(line)[:100])) # 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 in self.term.verbatim: if next.value[0] == 'ciscoasa': ret_str.append(str(next.value[1])) return '\n'.join(ret_str) # protocol if not self.term.protocol: protocol = ['ip'] else: # fix the protocol 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) 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) else: # destination address not set destination_address = ['any'] # options extra_options = [] for opt in [str(x) for x in self.term.option]: if opt.find('tcp-established') == 0 and 6 in protocol: extra_options.append('established') elif opt.find('established') == 0 and 6 in protocol: # only needed for TCP, for other protocols policy.py handles high-ports extra_options.append('established') self.options.extend(extra_options) # ports source_port = [()] destination_port = [()] if self.term.source_port: source_port = self.term.source_port if self.term.destination_port: destination_port = self.term.destination_port # logging if self.term.logging: self.options.append('log') if 'disable' in [x.value for x in self.term.logging]: self.options.append('disable') # icmp-types icmp_types = [''] if self.term.icmp_type: icmp_types = self.NormalizeIcmpTypes(self.term.icmp_type, self.term.protocol, self.af) for saddr in source_address: for daddr in destination_address: for sport in source_port: for dport in destination_port: for proto in protocol: for icmp_type in icmp_types: # only output address family appropriate IP addresses do_output = False if self.af == 4: if (((type(saddr) is nacaddr.IPv4) or (saddr == 'any')) and ((type(daddr) is nacaddr.IPv4) or (daddr == 'any'))): do_output = True if self.af == 6: if (((type(saddr) is nacaddr.IPv6) or (saddr == 'any')) and ((type(daddr) is nacaddr.IPv6) or (daddr == 'any'))): do_output = True if do_output: ret_str.extend( self._TermletToStr( self.filter_name, _ACTION_TABLE.get( str(self.term.action[0])), proto, saddr, sport, daddr, dport, icmp_type, self.options)) return '\n'.join(ret_str)
def __str__(self): """Convert term to a rule string. Returns: A rule as a string. Raises: NsxvAclTermError: When unknown icmp-types are specified """ # Verify platform specific terms. Skip whole term if platform does not # match. if self.term.platform: if 'nsxv' not in self.term.platform: return '' if self.term.platform_exclude: if 'nsxv' in self.term.platform_exclude: return '' ret_str = [''] # 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 '' # Term verbatim is not supported if self.term.verbatim: raise NsxvAclTermError( 'Verbatim are not implemented in standard ACLs') # Term option is not supported if self.term.option: raise NsxvAclTermError( 'Option are not implemented in standard ACLs') # check for keywords Nsxv does not support term_keywords = self.term.__dict__ unsupported_keywords = [] for key in term_keywords: if term_keywords[key]: # translated is obj attribute not keyword if ('translated' not in key) and (key not in _NSXV_SUPPORTED_KEYWORDS): unsupported_keywords.append(key) if unsupported_keywords: logging.warn( 'WARNING: The keywords %s in Term %s are not supported in Nsxv ', unsupported_keywords, self.term.name) name = '%s%s%s' % (_XML_TABLE.get('nameStart'), self.term.name, _XML_TABLE.get('nameEnd')) notes = '' if self.term.comment: for comment in self.term.comment: notes = '%s%s' % (notes, comment) notes = '%s%s%s' % (_XML_TABLE.get('noteStart'), notes, _XML_TABLE.get('noteEnd')) # protocol protocol = None if self.term.protocol: protocol = map(self.PROTO_MAP.get, self.term.protocol, self.term.protocol) # icmp-types icmp_types = [''] if self.term.icmp_type: icmp_types = self.NormalizeIcmpTypes(self.term.icmp_type, self.term.protocol, self.af) # for mixed filter type get both IPV4address and IPv6Address af_list = [] if self.filter_type == 'mixed': af_list = [4, 6] else: af_list = [self.af] source_address = None destination_address = None source_addr = [] destination_addr = [] for af in af_list: # source address if self.term.source_address: source_address = self.term.GetAddressOfVersion( 'source_address', af) source_address_exclude = self.term.GetAddressOfVersion( 'source_address_exclude', af) if source_address_exclude: source_address = nacaddr.ExcludeAddrs( source_address, source_address_exclude) if not source_address: logging.warn( self.NO_AF_LOG_ADDR.substitute(term=self.term.name, direction='source', af=self.text_af)) return '' if not source_addr: source_addr.append(source_address) else: source_addr = source_address # destination address if self.term.destination_address: destination_address = self.term.GetAddressOfVersion( 'destination_address', af) destination_address_exclude = self.term.GetAddressOfVersion( 'destination_address_exclude', af) if destination_address_exclude: destination_address = nacaddr.ExcludeAddrs( destination_address, destination_address_exclude) if not destination_address: logging.warn( self.NO_AF_LOG_ADDR.substitute(term=self.term.name, direction='destination', af=self.text_af)) return '' destination_addr.append(destination_address) # creating single list out of lists of lists source_addresses = [val for sublist in source_addr for val in sublist] destination_addresses = [ val for sublist in destination_addr for val in sublist ] # ports source_port = None destination_port = None if self.term.source_port: source_port = self.term.source_port if self.term.destination_port: destination_port = self.term.destination_port # logging log = 'false' if self.term.logging: log = 'true' sources = '' if source_addresses: sources = '<sources excluded="false">' for saddr in source_addresses: #inet4 if type(saddr) is nacaddr.IPv4 or type( saddr) is ipaddr.IPv4Network: if saddr.numhosts > 1: saddr = '%s%s%s' % ( _XML_TABLE.get('srcIpv4Start'), saddr.with_prefixlen, _XML_TABLE.get('srcIpv4End'), ) else: saddr = '%s%s%s' % (_XML_TABLE.get('srcIpv4Start'), saddr.ip, _XML_TABLE.get('srcIpv4End')) sources = '%s%s' % (sources, saddr) # inet6 if type(saddr) is nacaddr.IPv6 or type( saddr) is ipaddr.IPv6Network: if saddr.numhosts > 1: saddr = '%s%s%s' % ( _XML_TABLE.get('srcIpv6Start'), saddr.with_prefixlen, _XML_TABLE.get('srcIpv6End'), ) else: saddr = '%s%s%s' % (_XML_TABLE.get('srcIpv6Start'), saddr.ip, _XML_TABLE.get('srcIpv6End')) sources = '%s%s' % (sources, saddr) sources = '%s%s' % (sources, '</sources>') destinations = '' if destination_addresses: destinations = '<destinations excluded="false">' for daddr in destination_addresses: #inet4 if type(daddr) is nacaddr.IPv4 or type( daddr) is ipaddr.IPv4Network: if daddr.numhosts > 1: daddr = '%s%s%s' % ( _XML_TABLE.get('destIpv4Start'), daddr.with_prefixlen, _XML_TABLE.get('destIpv4End'), ) else: daddr = '%s%s%s' % (_XML_TABLE.get('destIpv4Start'), daddr.ip, _XML_TABLE.get('destIpv4End')) destinations = '%s%s' % (destinations, daddr) #inet6 if type(daddr) is nacaddr.IPv6 or type( daddr) is ipaddr.IPv6Network: if daddr.numhosts > 1: daddr = '%s%s%s' % ( _XML_TABLE.get('destIpv6Start'), daddr.with_prefixlen, _XML_TABLE.get('destIpv6End'), ) else: daddr = '%s%s%s' % (_XML_TABLE.get('destIpv6Start'), daddr.ip, _XML_TABLE.get('destIpv6End')) destinations = '%s%s' % (destinations, daddr) destinations = '%s%s' % (destinations, '</destinations>') services = [] if protocol: services.append('<services>') for proto in protocol: if proto != 'any': services.append( self._ServiceToString(proto, source_port, destination_port, icmp_types)) services.append('</services>') service = '' for s in services: service = '%s%s' % (service, s) #action action = '%s%s%s' % (_XML_TABLE.get('actionStart'), _ACTION_TABLE.get(str(self.term.action[0])), _XML_TABLE.get('actionEnd')) ret_lines = [] ret_lines.append( '<rule logged="%s"> %s %s %s %s %s %s </rule>' % (log, name, action, sources, destinations, service, notes)) # remove any trailing spaces and replace multiple spaces with singles stripped_ret_lines = [ re.sub(r'\s+', ' ', x).rstrip() for x in ret_lines ] ret_str.extend(stripped_ret_lines) return '\n'.join(ret_str)
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 = [] # Don't render icmpv6 protocol terms under inet, or icmp under inet6 if ((self.af == 'inet6' and 'icmp' in self.term.protocol) or (self.af == 'inet' and 'icmpv6' in self.term.protocol)): ret_str.append('# Term %s' % self.term.name) ret_str.append('# not rendered due to protocol/AF mismatch.') return '\n'.join(ret_str) # Term verbatim output - this will skip over most 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) # We don't support these keywords for filtering, so unless users # put in a "verbatim:: iptables" statement, any output we emitted # would misleadingly suggest that we applied their filters. # Instead, we fail loudly. if self.term.ether_type: raise UnsupportedFilterError( '\n%s %s %s %s' % ('ether_type unsupported by', self._PLATFORM, '\nError in term', self.term.name)) if self.term.address: raise UnsupportedFilterError( '\n%s %s %s %s %s' % ('address unsupported by', self._PLATFORM, '- specify source or dest', '\nError in term:', self.term.name)) if self.term.port: raise UnsupportedFilterError( '\n%s %s %s %s %s' % ('port unsupported by', self._PLATFORM, '- specify source or dest', '\nError in term:', self.term.name)) # Create a new term ret_str.append('-N %s' % self.term_name) # New term if self._PREJUMP_FORMAT: ret_str.append(self._PREJUMP_FORMAT % (self.filter, self.term_name)) if self.term.owner: self.term.comment.append('Owner: %s' % self.term.owner) # reformat long comments, if needed # # iptables allows individual comments up to 256 chars. # But our generator will limit a single comment line to < 120, using: # max = 119 - 27 (static chars in comment command) - [length of term name] comment_max_width = 92 - len(self.term_name) if comment_max_width < 40: comment_max_width = 40 comments = aclgenerator.WrapWords(self.term.comment, comment_max_width) # append comments to output if comments and comments[0]: for line in comments: if not line: continue # iptables-restore does not like 0-length comments. # term comments ret_str.append('-A %s -m comment --comment "%s"' % (self.term_name, str(line))) # if terms does not specify action, use filter default action if not self.term.action: self.term.action[0].value = self.default_action # Unsupported configuration; in the case of 'accept' or 'next', we # skip the rule. In other cases, we blow up (raise an exception) # to ensure that this is not considered valid configuration. if self.term.source_prefix or self.term.destination_prefix: if str(self.term.action[0]) not in set(['accept', 'next']): raise UnsupportedFilterError( '%s %s %s %s %s %s %s %s' % ('\nTerm', self.term.name, 'has action', str(self.term.action[0]), 'with source_prefix or destination_prefix,', ' which is unsupported in', self._PLATFORM, 'iptables output.')) return ('# skipped %s due to source or destination prefix rule' % self.term.name) # protocol if self.term.protocol: protocol = self.term.protocol else: protocol = ['all'] if self.term.protocol_except: raise UnsupportedFilterError( '%s %s %s' % ('\n', self.term.name, 'protocol_except logic not currently supported.')) # source address term_saddr = self.term.source_address exclude_saddr = self.term.source_address_exclude term_saddr_excluded = [] if not term_saddr: term_saddr = [self._all_ips] if exclude_saddr: term_saddr_excluded.extend( nacaddr.ExcludeAddrs(term_saddr, exclude_saddr)) # destination address term_daddr = self.term.destination_address exclude_daddr = self.term.destination_address_exclude term_daddr_excluded = [] if not term_daddr: term_daddr = [self._all_ips] if exclude_daddr: term_daddr_excluded.extend( nacaddr.ExcludeAddrs(term_daddr, exclude_daddr)) # Just to be safe, always have a result of at least 1 to avoid * by zero # returning incorrect results (10src*10dst=100, but 10src*0dst=0, not 10) bailout_count = len(exclude_saddr) + len(exclude_daddr) + ( (len(self.term.source_address) or 1) * (len(self.term.destination_address) or 1)) exclude_count = ((len(term_saddr_excluded) or 1) * (len(term_daddr_excluded) or 1)) # Use bailout jumps for excluded addresses if it results in fewer output # lines than nacaddr.ExcludeAddrs() method. if exclude_count < bailout_count: exclude_saddr = [] exclude_daddr = [] if term_saddr_excluded: term_saddr = term_saddr_excluded if term_daddr_excluded: term_daddr = term_daddr_excluded # With many sources and destinations, iptables needs to generate the # cartesian product of sources and destinations. If there are no # exclude rules, this can instead be written as exclude [0/0 - # srcs], exclude [0/0 - dsts]. v4_src_count = len([x for x in term_saddr if x.version == 4]) v4_dst_count = len([x for x in term_daddr if x.version == 4]) v6_src_count = len([x for x in term_saddr if x.version == 6]) v6_dst_count = len([x for x in term_daddr if x.version == 6]) num_pairs = v4_src_count * v4_dst_count + v6_src_count * v6_dst_count if num_pairs > 100: new_exclude_source = nacaddr.ExcludeAddrs([self._all_ips], term_saddr) new_exclude_dest = nacaddr.ExcludeAddrs([self._all_ips], term_daddr) # Invert the shortest list that does not already have exclude addresses if len(new_exclude_source) < len( new_exclude_dest) and not exclude_saddr: if len(new_exclude_source) + len(term_daddr) < num_pairs: exclude_saddr = new_exclude_source term_saddr = [self._all_ips] elif not exclude_daddr: if len(new_exclude_dest) + len(term_saddr) < num_pairs: exclude_daddr = new_exclude_dest term_daddr = [self._all_ips] # ports source_port = [] destination_port = [] if self.term.source_port: source_port = self.term.source_port if self.term.destination_port: destination_port = self.term.destination_port # icmp-types icmp_types = [''] if self.term.icmp_type: icmp_types = self.NormalizeIcmpTypes(self.term.icmp_type, protocol, self.af) source_interface = '' if self.term.source_interface: source_interface = self.term.source_interface destination_interface = '' if self.term.destination_interface: destination_interface = self.term.destination_interface log_hits = False if self.term.logging: # Iptables sends logs to hosts configured syslog log_hits = True # options tcp_flags = [] tcp_track_options = [] for next_opt in [str(x) for x in self.term.option]: # # Sanity checking and high-ports are added as appropriate in # pre-processing that is done in __str__ within class Iptables. # Option established will add destination port high-ports if protocol # contains only tcp, udp or both. This is done earlier in class Iptables. # if ((next_opt.find('established') == 0 or next_opt.find('tcp-established') == 0) and 'ESTABLISHED' not in [x.strip() for x in self.options]): if next_opt.find( 'tcp-established') == 0 and protocol != ['tcp']: raise TcpEstablishedError('%s %s %s' % ( '\noption tcp-established can only be applied for proto tcp.', '\nError in term:', self.term.name)) if self.trackstate: # Use nf_conntrack to track state -- works with any proto self.options.append('-m state --state ESTABLISHED,RELATED') elif protocol == ['tcp']: # Simple established-only rule for TCP: Must have ACK field # (SYN/ACK or subsequent ACK), or RST and no other flags. tcp_track_options = [(['ACK'], ['ACK']), (['SYN', 'FIN', 'ACK', 'RST'], ['RST'])] # Iterate through flags table, and create list of tcp-flags to append for next_flag in self._TCP_FLAGS_TABLE: if next_opt.find(next_flag) == 0: tcp_flags.append(self._TCP_FLAGS_TABLE.get(next_flag)) if next_opt in self._KNOWN_OPTIONS_MATCHERS: self.options.append(self._KNOWN_OPTIONS_MATCHERS[next_opt]) if self.term.packet_length: # Policy format is "#-#", but iptables format is "#:#" self.options.append('-m length --length %s' % self.term.packet_length.replace('-', ':')) if self.term.fragment_offset: self.options.append('-m u32 --u32 4&0x1FFF=%s' % self.term.fragment_offset.replace('-', ':')) for saddr in exclude_saddr: ret_str.extend( self._FormatPart(self.af, '', saddr, '', '', '', '', '', '', '', '', '', '', self._ACTION_TABLE.get('next'))) for daddr in exclude_daddr: ret_str.extend( self._FormatPart(self.af, '', '', '', daddr, '', '', '', '', '', '', '', '', self._ACTION_TABLE.get('next'))) for saddr in term_saddr: for daddr in term_daddr: for icmp in icmp_types: for proto in protocol: for tcp_matcher in tcp_track_options or (([], []), ): ret_str.extend( self._FormatPart( self.af, str(proto), saddr, source_port, daddr, destination_port, self.options, tcp_flags, icmp, tcp_matcher, source_interface, destination_interface, log_hits, self._ACTION_TABLE.get( str(self.term.action[0])))) if self._POSTJUMP_FORMAT: ret_str.append(self._POSTJUMP_FORMAT % (self.filter, self.term_name)) return '\n'.join(str(v) for v in ret_str if v is not '')
def __str__(self): # Verify platform specific terms. Skip whole term if platform does not # match. if self.term.platform: if 'cisco' not in self.term.platform: return '' if self.term.platform_exclude: if 'cisco' 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)): ret_str.append('remark Term %s' % self.term.name) ret_str.append('remark not rendered due to protocol/AF mismatch.') return '\n'.join(ret_str) 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]) # 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] == 'cisco': 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'] else: protocol = ['ip'] else: # pylint: disable-msg=C6402 protocol = map(self.PROTO_MAP.get, self.term.protocol, self.term.protocol) # pylint: disable-msg=C6402 # 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.warn( self.NO_AF_LOG_FORMAT.substitute(term=self.term.name, direction='source', af=self.text_af)) return '' 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.warn( self.NO_AF_LOG_FORMAT.substitute(term=self.term.name, direction='destination', af=self.text_af)) return '' 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 and ('tcp-established' in opts or 'established' in opts): self.options.extend(['established']) # ports source_port = [()] destination_port = [()] if self.term.source_port: source_port = self.term.source_port if self.term.destination_port: destination_port = self.term.destination_port # logging if self.term.logging: self.options.append('log') # icmp-types icmp_types = [''] if self.term.icmp_type: icmp_types = self.NormalizeIcmpTypes(self.term.icmp_type, self.term.protocol, self.af) for saddr in source_address: for daddr in destination_address: for sport in source_port: for dport in destination_port: for proto in protocol: 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, self.options)) return '\n'.join(ret_str)
def _CalculateAddresses(self, term_saddr, exclude_saddr, term_daddr, exclude_daddr): """Calculate source and destination address list for a term. Args: term_saddr: source address list of the term exclude_saddr: source address exclude list of the term term_daddr: destination address list of the term exclude_daddr: destination address exclude list of the term Returns: tuple containing source address list, source exclude address list, destination address list, destination exclude address list in that order """ # source address term_saddr_excluded = [] if not term_saddr: term_saddr = [self._all_ips] if exclude_saddr: term_saddr_excluded.extend( nacaddr.ExcludeAddrs(term_saddr, exclude_saddr)) # destination address term_daddr_excluded = [] if not term_daddr: term_daddr = [self._all_ips] if exclude_daddr: term_daddr_excluded.extend( nacaddr.ExcludeAddrs(term_daddr, exclude_daddr)) # Just to be safe, always have a result of at least 1 to avoid * by zero # returning incorrect results (10src*10dst=100, but 10src*0dst=0, not 10) bailout_count = len(exclude_saddr) + len(exclude_daddr) + ( (len(self.term.source_address) or 1) * (len(self.term.destination_address) or 1)) exclude_count = ((len(term_saddr_excluded) or 1) * (len(term_daddr_excluded) or 1)) # Use bailout jumps for excluded addresses if it results in fewer output # lines than nacaddr.ExcludeAddrs() method. if exclude_count < bailout_count: exclude_saddr = [] exclude_daddr = [] if term_saddr_excluded: term_saddr = term_saddr_excluded if term_daddr_excluded: term_daddr = term_daddr_excluded # With many sources and destinations, iptables needs to generate the # cartesian product of sources and destinations. If there are no # exclude rules, this can instead be written as exclude [0/0 - # srcs], exclude [0/0 - dsts]. v4_src_count = len([x for x in term_saddr if x.version == 4]) v4_dst_count = len([x for x in term_daddr if x.version == 4]) v6_src_count = len([x for x in term_saddr if x.version == 6]) v6_dst_count = len([x for x in term_daddr if x.version == 6]) num_pairs = v4_src_count * v4_dst_count + v6_src_count * v6_dst_count if num_pairs > 100: new_exclude_source = nacaddr.ExcludeAddrs([self._all_ips], term_saddr) new_exclude_dest = nacaddr.ExcludeAddrs([self._all_ips], term_daddr) # Invert the shortest list that does not already have exclude addresses if len(new_exclude_source) < len( new_exclude_dest) and not exclude_saddr: if len(new_exclude_source) + len(term_daddr) < num_pairs: exclude_saddr = new_exclude_source term_saddr = [self._all_ips] elif not exclude_daddr: if len(new_exclude_dest) + len(term_saddr) < num_pairs: exclude_daddr = new_exclude_dest term_daddr = [self._all_ips] term_saddr = [ x for x in term_saddr if x.version == self.AF_MAP[self.af] ] exclude_saddr = [ x for x in exclude_saddr if x.version == self.AF_MAP[self.af] ] term_daddr = [ x for x in term_daddr if x.version == self.AF_MAP[self.af] ] exclude_daddr = [ x for x in exclude_daddr if x.version == self.AF_MAP[self.af] ] return (term_saddr, exclude_saddr, term_daddr, exclude_daddr)