Esempio n. 1
0
  def _TranslatePolicy(self, pol, exp_info):
    """Translates a Capirca policy into a HF-specific data structure.

    Takes in a POL file, parses each term and populates the policy
    dict. Each term in this list is a dictionary formatted according to
    HF's rule API specification.  Additionally, checks for its quota.

    Args:
      pol: A Policy() object representing a given POL file.
      exp_info: An int that specifies number of weeks until policy expiry.

    Raises:
      ExceededCostError: Raised when the cost of a policy exceeds the default
          maximum cost.
      HeaderError: Raised when the header cannot be parsed or a header option is
          invalid.
    """
    self.policies = []
    for header, terms in pol.filters:
      counter = 1
      total_cost = 0

      policy = {
          'rules': []
      }

      if self._PLATFORM not in header.platforms:
        continue

      filter_options = header.FilterOptions(self._PLATFORM)

      # Get the policy name.
      filter_name = header.FilterName(self._PLATFORM)
      filter_options.remove(filter_name)
      policy['display_name'] = filter_name

      # Get term direction if set.
      direction = 'INGRESS'
      for i in self._GOOD_DIRECTION:
        if i in filter_options:
          direction = i
          filter_options.remove(i)

      # Get the address family if set.
      address_family = 'inet'
      for i in self._SUPPORTED_AF:
        if i in filter_options:
          address_family = i
          filter_options.remove(i)

      # Find the default maximum cost of a policy, an integer, if specified.
      max_cost = self._DEFAULT_MAXIMUM_COST
      for opt in filter_options:
        try:
          max_cost = int(opt)
          filter_options.remove(opt)
          break
        except ValueError:
          continue

      if max_cost > 65536:
        raise gcp.HeaderError(
            'Default maximum cost cannot be higher than 65536')

      # If there are remaining options, they are unknown/unsupported options.
      if filter_options:
        raise gcp.HeaderError(
            'Unsupported or unknown filter options %s in policy %s ' %
            (str(filter_options), filter_name))

      for term in terms:
        if term.stateless_reply:
          continue
        total_cost += GetCost(term)
        if total_cost > max_cost:
          raise ExceededCostError('Policy cost (%d) reached the maximum (%d)'
                                  % (total_cost, max_cost))
        if gcp.IsDefaultDeny(term):
          if direction == 'EGRESS':
            term.destination_address = self._ANY
          else:
            term.source_address = self._ANY
        term.name = self.FixTermLength(term.name)
        term.direction = direction
        dict_term = Term(term, address_family=address_family).ConvertToDict(
            priority_index=counter)
        counter += 1
        policy['rules'].append(dict_term)

      self.policies.append(policy)
Esempio n. 2
0
    def _TranslatePolicy(self, pol, exp_info):
        """Translates a Capirca policy into a HF-specific data structure.

    Takes in a POL file, parses each term and populates the policy
    dict. Each term in this list is a dictionary formatted according to
    HF's rule API specification.  Additionally, checks for its quota.

    Args:
      pol: A Policy() object representing a given POL file.
      exp_info: An int that specifies number of weeks until policy expiry.

    Raises:
      ExceededCostError: Raised when the cost of a policy exceeds the default
          maximum cost.
      HeaderError: Raised when the header cannot be parsed or a header option is
          invalid.
      DifferentPolicyNameError: Raised when a header policy name differs from
          other in the same policy.
    """
        self.policies = []
        policy = {'rules': [], 'type': 'FIREWALL'}
        is_policy_modified = False
        counter = 1
        total_cost = 0
        for header, terms in pol.filters:

            if self._PLATFORM not in header.platforms:
                continue

            filter_options = header.FilterOptions(self._PLATFORM)

            is_policy_modified = True

            # Get term direction if set.
            direction = 'INGRESS'
            for i in self._GOOD_DIRECTION:
                if i in filter_options:
                    direction = i
                    filter_options.remove(i)

            # Get the address family if set.
            address_family = 'inet'
            for i in self._SUPPORTED_AF:
                if i in filter_options:
                    address_family = i
                    filter_options.remove(i)

            # Get the compute API version if set.
            api_version = 'beta'
            for i in self._SUPPORTED_API_VERSION:
                if i in filter_options:
                    api_version = i
                    filter_options.remove(i)
                    break

            # Find the default maximum cost of a policy, an integer, if specified.
            max_cost = self._DEFAULT_MAXIMUM_COST
            for opt in filter_options:
                try:
                    max_cost = int(opt)
                    filter_options.remove(opt)
                    break
                except ValueError:
                    continue

            if max_cost > 65536:
                raise gcp.HeaderError(
                    'Default maximum cost cannot be higher than 65536')

            display_name = ApiVersionSyntaxMap.SYNTAX_MAP[api_version][
                'display_name']

            # Get policy name and validate it to meet displayName requirements.
            policy_name = header.FilterName(self._PLATFORM)
            if not policy_name:
                raise gcp.HeaderError(
                    'Policy name was not specified in header')
            filter_options.remove(policy_name)
            if len(policy_name) > 63:
                raise gcp.HeaderError(
                    'Policy name "%s" is too long; the maximum number of characters '
                    'allowed is 63' % (policy_name))
            if not bool(re.match('^[a-z]([-a-z0-9]*[a-z0-9])?$', policy_name)):
                raise gcp.HeaderError(
                    'Invalid string for displayName, "%s"; the first character must be '
                    'a lowercase letter, and all following characters must be a dash, '
                    'lowercase letter, or digit, except the last character, which '
                    'cannot be a dash.' % (policy_name))
            if display_name in policy and policy[display_name] != policy_name:
                raise DifferentPolicyNameError(
                    'Policy names that are from the same policy are expected to be '
                    'equal, but %s is different to %s' %
                    (policy[display_name], policy_name))
            policy[display_name] = policy_name

            # If there are remaining options, they are unknown/unsupported options.
            if filter_options:
                raise gcp.HeaderError(
                    'Unsupported or unknown filter options %s in policy %s ' %
                    (str(filter_options), policy_name))

            # Handle mixed for each indvidual term as inet and inet6.
            # inet/inet6 are treated the same.
            term_address_families = []
            if address_family == 'mixed':
                term_address_families = ['inet', 'inet6']
            else:
                term_address_families = [address_family]

            for term in terms:
                if term.stateless_reply:
                    continue

                if gcp.IsDefaultDeny(term):
                    if direction == 'EGRESS':
                        if address_family != 'mixed':
                            # Default deny also gets processed as part of terms processing.
                            # The name and priority get updated there.
                            term.destination_address = [
                                self._ANY_IP[address_family]
                            ]
                        else:
                            term.destination_address = [
                                self._ANY_IP['inet'], self._ANY_IP['inet6']
                            ]
                    else:
                        if address_family != 'mixed':
                            term.source_address = [
                                self._ANY_IP[address_family]
                            ]
                        else:
                            term.source_address = [
                                self._ANY_IP['inet'], self._ANY_IP['inet6']
                            ]
                term.name = self.FixTermLength(term.name)
                term.direction = direction

                # Only generate the term if it's for the appropriate platform
                if term.platform:
                    if self._PLATFORM not in term.platform:
                        continue
                if term.platform_exclude:
                    if self._PLATFORM in term.platform_exclude:
                        continue

                for term_af in term_address_families:
                    rules = Term(term,
                                 address_family=term_af,
                                 policy_inet_version=address_family,
                                 api_version=api_version).ConvertToDict(
                                     priority_index=counter)
                    if not rules:
                        continue
                    for dict_term in rules:
                        total_cost += GetRuleTupleCount(dict_term, api_version)
                        if total_cost > max_cost:
                            raise ExceededCostError(
                                'Policy cost (%d) for %s reached the '
                                'maximum (%d)' %
                                (total_cost, policy[display_name], max_cost))
                        policy['rules'].append(dict_term)
                    counter += len(rules)

        self.policies.append(policy)

        # Do not render an empty rules if no policies have been evaluated.
        if not is_policy_modified:
            self.policies = []

        if total_cost > 0:
            logging.info('Policy %s quota cost: %d', policy[display_name],
                         total_cost)