示例#1
0
  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)
示例#2
0
    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
示例#3
0
    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
示例#4
0
    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)
示例#5
0
    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