Esempio n. 1
0
    def testToDottedQuad(self):
        net = summarizer.DSMNet(1 << 32, 4294967264)
        self.assertRaises(ValueError)
        net = summarizer.DSMNet(3232235584, 1 << 16)
        self.assertRaises(ValueError)
        net = summarizer.DSMNet(3232235584, 4294967264)
        self.assertEqual(summarizer.ToDottedQuad(net),
                         ('192.168.0.64', '255.255.255.224'))
        net = summarizer.DSMNet(3232235584, 4294901984)
        self.assertEqual(summarizer.ToDottedQuad(net, negate=True),
                         ('192.168.0.64', '0.0.255.31'))

        test_data = [
            (summarizer.DSMNet(3232235584,
                               4294967295), True, ('192.168.0.64', '32')),
            (summarizer.DSMNet(3232235584,
                               4294901760), True, ('192.168.0.64', '16')),
            (summarizer.DSMNet(3232235584,
                               4294967294), True, ('192.168.0.64', '31')),
            (summarizer.DSMNet(3232235584,
                               4290772992), True, ('192.168.0.64', '10')),
            (summarizer.DSMNet(3232235584, 4294966016), True,
             ('192.168.0.64', '255.255.251.0')),
            (summarizer.DSMNet(3232235584, 4294901504), True,
             ('192.168.0.64', '255.254.255.0'))
        ]

        for net, nondsm, expected in test_data:
            self.assertEqual(summarizer.ToDottedQuad(net, nondsm=nondsm),
                             expected)
Esempio n. 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 isinstance(addr, nacaddr.IPv4) or isinstance(
                addr, ipaddress.IPv4Network):
            addr = cast(self.IPV4_ADDRESS, addr)
            if addr.num_addresses > 1:
                if self.platform == 'arista':
                    return addr.with_prefixlen
                return '%s %s' % (addr.network_address, addr.hostmask)
            return 'host %s' % (addr.network_address)
        if isinstance(addr, nacaddr.IPv6) or isinstance(
                addr, ipaddress.IPv6Network):
            addr = cast(self.IPV6_ADDRESS, addr)
            if addr.num_addresses > 1:
                return addr.with_prefixlen
            return 'host %s' % (addr.network_address)
        # DSMO enabled
        if isinstance(addr, summarizer.DSMNet):
            return '%s %s' % summarizer.ToDottedQuad(addr, negate=True)
        return addr
Esempio n. 3
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
                                           or 'icmp6' in self.term.protocol))):
            logging.debug(
                self.NO_AF_LOG_PROTO.substitute(term=self.term.name,
                                                proto=', '.join(
                                                    self.term.protocol),
                                                af=self.term_type))
            return ''

        # comment
        # this deals just fine with multi line comments, but we could probably
        # output them a little cleaner; do things like make sure the
        # len(output) < 80, etc. Note, if 'noverbose' is set for the filter, skip
        # all comment processing.
        if self.term.owner and not self.noverbose:
            self.term.comment.append('Owner: %s' % self.term.owner)
        if self.term.comment and not self.noverbose:
            config.Append('/*')
            for comment in self.term.comment:
                for line in comment.split('\n'):
                    config.Append('** ' + line)
            config.Append('*/')

        # Term verbatim output - this will skip over normal term creation
        # code.  Warning generated from policy.py if appropriate.
        if self.term.verbatim:
            for next_term in self.term.verbatim:
                if next_term[0] == self._PLATFORM:
                    config.Append(str(next_term[1]), verbatim=True)
            return str(config)

        # Helper for per-address-family keywords.
        family_keywords = self._TERM_TYPE.get(self.term_type)

        # option
        # this is going to be a little ugly b/c there are a few little messed
        # up options we can deal with.
        if self.term.option:
            for opt in [str(x) for x in self.term.option]:
                # there should be a better way to search the array of protocols
                if opt.startswith('sample'):
                    self.extra_actions.append('sample')

                # only append tcp-established for option established when
                # tcp is the only protocol, otherwise other protos break on juniper
                elif opt.startswith('established'):
                    if self.term.protocol == ['tcp']:
                        if 'tcp-established;' not in from_str:
                            from_str.append(family_keywords['tcp-est'] + ';')

                # if tcp-established specified, but more than just tcp is included
                # in the protocols, raise an error
                elif opt.startswith('tcp-established'):
                    flag = family_keywords['tcp-est'] + ';'
                    if self.term.protocol == ['tcp']:
                        if flag not in from_str:
                            from_str.append(flag)
                    else:
                        raise TcpEstablishedWithNonTcpError(
                            'tcp-established can only be used with tcp protocol in term %s'
                            % self.term.name)
                elif opt.startswith('rst'):
                    from_str.append('tcp-flags "rst";')
                elif opt.startswith('initial') and 'tcp' in self.term.protocol:
                    from_str.append('tcp-initial;')
                elif opt.startswith('first-fragment'):
                    from_str.append('first-fragment;')

                # we don't have a special way of dealing with this, so we output it and
                # hope the user knows what they're doing.
                else:
                    from_str.append('%s;' % opt)

        # if the term is inactive we have to set the prefix
        if self.term.inactive:
            term_prefix = 'inactive:'
        else:
            term_prefix = ''

        # term name
        config.Append('%s term %s {' % (term_prefix, self.term.name))

        # The "filter" keyword is not compatible with from or then
        if self.term.filter_term:
            config.Append('filter %s;' % self.term.filter_term)
            config.Append('}')  # end term accept-foo-to-bar { ... }
            return str(config)

        # a default action term doesn't have any from { clause
        has_match_criteria = (
            self.term.address or self.term.dscp_except or self.term.dscp_match
            or self.term.destination_address or self.term.destination_port
            or self.term.destination_prefix
            or self.term.destination_prefix_except or self.term.encapsulate
            or self.term.ether_type or self.term.flexible_match_range
            or self.term.forwarding_class or self.term.forwarding_class_except
            or self.term.fragment_offset or self.term.hop_limit
            or self.term.next_ip or self.term.port or self.term.precedence
            or self.term.protocol or self.term.protocol_except
            or self.term.source_address or self.term.source_port
            or self.term.source_prefix or self.term.source_prefix_except
            or self.term.traffic_type or self.term.ttl)

        if has_match_criteria:
            config.Append('from {')

            term_af = self.AF_MAP.get(self.term_type)

            # address
            address = self.term.GetAddressOfVersion('address', term_af)
            if self.enable_dsmo:
                address = summarizer.Summarize(address)

            if address:
                config.Append('%s {' % family_keywords['addr'])
                for addr in address:
                    for comment in self._Comment(addr):
                        config.Append('%s' % comment)
                    if self.enable_dsmo:
                        config.Append(
                            '%s/%s;' %
                            summarizer.ToDottedQuad(addr, nondsm=True))
                    else:
                        config.Append('%s;' % addr)
                config.Append('}')
            elif self.term.address:
                logging.debug(
                    self.NO_AF_LOG_ADDR.substitute(term=self.term.name,
                                                   af=self.term_type))
                return ''

            # source address

            src_addr = self.term.GetAddressOfVersion('source_address', term_af)
            src_addr_ex = self.term.GetAddressOfVersion(
                'source_address_exclude', term_af)
            if self.enable_dsmo:
                src_addr = summarizer.Summarize(src_addr)
                src_addr_ex = summarizer.Summarize(src_addr_ex)
            else:
                src_addr, src_addr_ex = self._MinimizePrefixes(
                    src_addr, src_addr_ex)

            if src_addr:
                config.Append('%s {' % family_keywords['saddr'])
                for addr in src_addr:
                    for comment in self._Comment(addr):
                        config.Append('%s' % comment)
                    if self.enable_dsmo:
                        config.Append(
                            '%s/%s;' %
                            summarizer.ToDottedQuad(addr, nondsm=True))
                    else:
                        config.Append('%s;' % addr)
                for addr in src_addr_ex:
                    for comment in self._Comment(addr, exclude=True):
                        config.Append('%s' % comment)
                    if self.enable_dsmo:
                        config.Append(
                            '%s/%s except;' %
                            summarizer.ToDottedQuad(addr, nondsm=True))
                    else:
                        config.Append('%s except;' % addr)
                config.Append('}')
            elif self.term.source_address:
                logging.debug(
                    self.NO_AF_LOG_ADDR.substitute(term=self.term.name,
                                                   direction='source',
                                                   af=self.term_type))
                return ''

            # destination address
            dst_addr = self.term.GetAddressOfVersion('destination_address',
                                                     term_af)
            dst_addr_ex = self.term.GetAddressOfVersion(
                'destination_address_exclude', term_af)
            if self.enable_dsmo:
                dst_addr = summarizer.Summarize(dst_addr)
                dst_addr_ex = summarizer.Summarize(dst_addr_ex)
            else:
                dst_addr, dst_addr_ex = self._MinimizePrefixes(
                    dst_addr, dst_addr_ex)

            if dst_addr:
                config.Append('%s {' % family_keywords['daddr'])
                for addr in dst_addr:
                    for comment in self._Comment(addr):
                        config.Append('%s' % comment)
                    if self.enable_dsmo:
                        config.Append(
                            '%s/%s;' %
                            summarizer.ToDottedQuad(addr, nondsm=True))
                    else:
                        config.Append('%s;' % addr)
                for addr in dst_addr_ex:
                    for comment in self._Comment(addr, exclude=True):
                        config.Append('%s' % comment)
                    if self.enable_dsmo:
                        config.Append(
                            '%s/%s except;' %
                            summarizer.ToDottedQuad(addr, nondsm=True))
                    else:
                        config.Append('%s except;' % addr)
                config.Append('}')
            elif self.term.destination_address:
                logging.debug(
                    self.NO_AF_LOG_ADDR.substitute(term=self.term.name,
                                                   direction='destination',
                                                   af=self.term_type))
                return ''

            # forwarding-class
            if self.term.forwarding_class:
                config.Append(
                    'forwarding-class %s' %
                    self._Group(self.term.forwarding_class, lc=False))

            # forwarding-class-except
            if self.term.forwarding_class_except:
                config.Append(
                    'forwarding-class-except %s' %
                    self._Group(self.term.forwarding_class_except, lc=False))

            # source prefix <except> list
            if self.term.source_prefix or self.term.source_prefix_except:
                config.Append('source-prefix-list {')
                for pfx in self.term.source_prefix:
                    config.Append(pfx + ';')
                for epfx in self.term.source_prefix_except:
                    config.Append(epfx + ' except;')
                config.Append('}')

            # destination prefix <except> list
            if self.term.destination_prefix or self.term.destination_prefix_except:
                config.Append('destination-prefix-list {')
                for pfx in self.term.destination_prefix:
                    config.Append(pfx + ';')
                for epfx in self.term.destination_prefix_except:
                    config.Append(epfx + ' except;')
                config.Append('}')

            # Only generate ttl if inet, inet6 uses hop-limit instead.
            if self.term.ttl and self.term_type == 'inet':
                config.Append('ttl %s;' % self.term.ttl)

            # protocol
            if self.term.protocol:
                # both are supported on JunOS, but only icmp6 is supported
                # on SRX loopback stateless filter
                config.Append(family_keywords['protocol'] + ' ' +
                              self._Group(self.term.protocol))

            # protocol
            if self.term.protocol_except:
                # same as above
                config.Append(family_keywords['protocol-except'] + ' ' +
                              self._Group(self.term.protocol_except))

            # port
            if self.term.port:
                config.Append('port %s' % self._Group(self.term.port))

            # source port
            if self.term.source_port:
                config.Append('source-port %s' %
                              self._Group(self.term.source_port))

            # destination port
            if self.term.destination_port:
                config.Append('destination-port %s' %
                              self._Group(self.term.destination_port))

            # append any options beloging in the from {} section
            for next_str in from_str:
                config.Append(next_str)

            # packet length
            if self.term.packet_length:
                config.Append('packet-length %s;' % self.term.packet_length)

            # fragment offset
            if self.term.fragment_offset:
                config.Append('fragment-offset %s;' %
                              self.term.fragment_offset)

            # icmp-types
            icmp_types = ['']
            if self.term.icmp_type:
                icmp_types = self.NormalizeIcmpTypes(self.term.icmp_type,
                                                     self.term.protocol,
                                                     self.term_type)
            if icmp_types != ['']:
                config.Append('icmp-type %s' % self._Group(icmp_types))
            if self.term.icmp_code:
                config.Append('icmp-code %s' %
                              self._Group(self.term.icmp_code))
            if self.term.ether_type:
                config.Append('ether-type %s' %
                              self._Group(self.term.ether_type))

            if self.term.traffic_type:
                config.Append('traffic-type %s' %
                              self._Group(self.term.traffic_type))

            if self.term.precedence:
                # precedence may be a single integer, or a space separated list
                policy_precedences = set()
                # precedence values may only be 0 through 7
                for precedence in self.term.precedence:
                    if int(precedence) in range(0, 8):
                        policy_precedences.add(precedence)
                    else:
                        raise PrecedenceError(
                            'Precedence value %s is out of bounds in %s' %
                            (precedence, self.term.name))
                config.Append('precedence %s' %
                              self._Group(sorted(policy_precedences)))

            # DSCP Match
            if self.term.dscp_match:
                if self.term_type == 'inet6':
                    config.Append('traffic-class [ %s ];' %
                                  (' '.join(self.term.dscp_match)))
                else:
                    config.Append('dscp [ %s ];' %
                                  ' '.join(self.term.dscp_match))

            # DSCP Except
            if self.term.dscp_except:
                if self.term_type == 'inet6':
                    config.Append('traffic-class-except [ %s ];' %
                                  (' '.join(self.term.dscp_except)))
                else:
                    config.Append('dscp-except [ %s ];' %
                                  ' '.join(self.term.dscp_except))

            if self.term.hop_limit:
                # Only generate a hop-limit if inet6, inet4 has not hop-limit.
                if self.term_type == 'inet6':
                    config.Append('hop-limit %s;' % (self.term.hop_limit))

            # flexible-match
            if self.term.flexible_match_range:
                config.Append('flexible-match-range {')
                for fm_opt in self.term.flexible_match_range:
                    config.Append('%s %s;' % (fm_opt[0], fm_opt[1]))
                config.Append('}')

            config.Append('}')  # end from { ... }

        ####
        # ACTIONS go below here
        ####

        # If the action is only one line, include it in the same line as "then "
        # statement.
        # For example, if the action is only accept, it should be:
        # "then accept;" rather than:
        # "then {
        #     accept;
        # }"
        #
        self.CheckTerminatingAction()
        unique_actions = set(self.extra_actions)
        if not self.term.routing_instance:
            unique_actions.update(self.term.action)
        if self.term.encapsulate:
            unique_actions.add('encapsulate')
        if len(unique_actions) <= 1:
            for action in [
                    self.term.logging, self.term.routing_instance,
                    self.term.counter, self.term.policer, self.term.qos,
                    self.term.loss_priority, self.term.dscp_set,
                    self.term.next_ip, self.term.traffic_class_count
            ]:
                if action:
                    try:
                        unique_actions.update(action)
                    except TypeError:
                        unique_actions.add(action)
                    if len(unique_actions) > 1:
                        break

        if len(unique_actions) == 1:
            # b/21795531: Juniper device treats a set of IPv4 actions differently
            # than any other actions.
            # For example, if the term is in IPv4 and the action is only discard,
            # it should be:
            # "then {
            #     discard;
            # }" rather than:
            # "then discard;"
            current_action = self.ACTIONS.get(unique_actions.pop(), 'next_ip')
            if (self.term_type == 'inet' and current_action in [
                    'discard', 'reject', 'reject tcp-reset'
            ]) or (self.term_type == 'inet6'
                   and current_action in ['reject', 'reject tcp-reset']):
                config.Append('then {')
                config.Append('%s;' % current_action)
                config.Append('}')
            elif current_action == 'next_ip':
                self.NextIpCheck(self.term.next_ip, self.term.name)
                config.Append('then {')
                if self.term.next_ip[0].version == 4:
                    config.Append('next-ip %s;' % str(self.term.next_ip[0]))
                else:
                    config.Append('next-ip6 %s;' % str(self.term.next_ip[0]))
                config.Append('}')
            elif current_action == 'encapsulate':
                config.Append('then {')
                config.Append('encapsulate %s;' % str(self.term.encapsulate))
                config.Append('}')
            else:
                config.Append('then %s;' % current_action)
        elif len(unique_actions) > 1:
            config.Append('then {')
            # logging
            if self.term.logging:
                for log_target in self.term.logging:
                    if str(log_target) == 'local':
                        config.Append('log;')
                    else:
                        config.Append('syslog;')

            if self.term.routing_instance:
                config.Append('routing-instance %s;' %
                              self.term.routing_instance)

            if self.term.counter:
                config.Append('count %s;' % self.term.counter)

            if self.term.traffic_class_count:
                config.Append('traffic-class-count %s;' %
                              self.term.traffic_class_count)

            oid_length = 128
            if self.term.policer:
                config.Append('policer %s;' % self.term.policer)
                if len(self.term.policer) > oid_length:
                    logging.warning(
                        'WARNING: %s is longer than %d bytes. Due to '
                        'limitation in JUNOS, OIDs longer than %dB can '
                        'cause SNMP timeout issues.', self.term.policer,
                        oid_length, oid_length)

            if self.term.qos:
                config.Append('forwarding-class %s;' % self.term.qos)

            if self.term.loss_priority:
                config.Append('loss-priority %s;' % self.term.loss_priority)
            if self.term.next_ip:
                self.NextIpCheck(self.term.next_ip, self.term.name)
                if self.term.next_ip[0].version == 4:
                    config.Append('next-ip %s;' % str(self.term.next_ip[0]))
                else:
                    config.Append('next-ip6 %s;' % str(self.term.next_ip[0]))
            if self.term.encapsulate:
                config.Append('encapsulate %s;' % str(self.term.encapsulate))
            for action in self.extra_actions:
                config.Append(action + ';')

            # If there is a routing-instance defined, skip reject/accept/etc actions.
            if not self.term.routing_instance:
                for action in self.term.action:
                    config.Append(self.ACTIONS.get(action) + ';')

            # DSCP SET
            if self.term.dscp_set:
                if self.term_type == 'inet6':
                    config.Append('traffic-class %s;' % self.term.dscp_set)
                else:
                    config.Append('dscp %s;' % self.term.dscp_set)

            config.Append('}')  # end then{...}

        config.Append('}')  # end term accept-foo-to-bar { ... }

        return str(config)