// Copyright 2025 Envoy Project Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto3";

package envoy.config.listener.v3;

import "envoy/config/core/v3/address.proto";
import "envoy/config/core/v3/base.proto";
import "envoy/config/core/v3/config_source.proto";
import "envoy/type/v3/range.proto";

import "google/protobuf/any.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/wrappers.proto";

import "envoy/annotations/deprecation.proto";
import "udpa/annotations/status.proto";
import "udpa/annotations/versioning.proto";
import "validate/validate.proto";

option java_package = "io.envoyproxy.envoy.config.listener.v3";
option java_outer_classname = "ListenerComponentsProto";
option java_multiple_files = true;
option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3;listenerv3";
option (udpa.annotations.file_status).package_version_status = ACTIVE;

// [#protodoc-title: Listener components]
// Listener :ref:`configuration overview <config_listeners>`

// [#next-free-field: 6]
message Filter {
  option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.listener.Filter";

  reserved 3, 2;

  reserved "config";

  // The name of the filter configuration.
  string name = 1 [(validate.rules).string = {min_len: 1}];

  oneof config_type {
    // Filter specific configuration which depends on the filter being
    // instantiated. See the supported filters for further documentation.
    // [#extension-category: envoy.filters.network]
    google.protobuf.Any typed_config = 4;

    // Configuration source specifier for an extension configuration discovery
    // service. In case of a failure and without the default configuration, the
    // listener closes the connections.
    core.v3.ExtensionConfigSource config_discovery = 5;
  }
}

// Specifies the match criteria for selecting a specific filter chain for a
// listener.
//
// In order for a filter chain to be selected, *ALL* of its criteria must be
// fulfilled by the incoming connection, properties of which are set by the
// networking stack and/or listener filters.
//
// The following order applies:
//
// 1. Destination port.
// 2. Destination IP address.
// 3. Server name (e.g. SNI for TLS protocol),
// 4. Transport protocol.
// 5. Application protocols (e.g. ALPN for TLS protocol).
// 6. Directly connected source IP address (this will only be different from the source IP address
//    when using a listener filter that overrides the source address, such as the :ref:`Proxy Protocol
//    listener filter <config_listener_filters_proxy_protocol>`).
// 7. Source type (e.g. any, local or external network).
// 8. Source IP address.
// 9. Source port.
//
// For criteria that allow ranges or wildcards, the most specific value in any
// of the configured filter chains that matches the incoming connection is going
// to be used (e.g. for SNI ``www.example.com`` the most specific match would be
// ``www.example.com``, then ``*.example.com``, then ``*.com``, then any filter
// chain without ``server_names`` requirements).
//
// A different way to reason about the filter chain matches:
// Suppose there exists N filter chains. Prune the filter chain set using the above 8 steps.
// In each step, filter chains which most specifically matches the attributes continue to the next step.
// The listener guarantees at most 1 filter chain is left after all of the steps.
//
// Example:
//
// For destination port, filter chains specifying the destination port of incoming traffic are the
// most specific match. If none of the filter chains specifies the exact destination port, the filter
// chains which do not specify ports are the most specific match. Filter chains specifying the
// wrong port can never be the most specific match.
//
// [#comment: Implemented rules are kept in the preference order, with deprecated fields
// listed at the end, because that's how we want to list them in the docs.
//
// [#comment:TODO(PiotrSikora): Add support for configurable precedence of the rules]
// [#next-free-field: 14]
message FilterChainMatch {
  option (udpa.annotations.versioning).previous_message_type =
      "envoy.api.v2.listener.FilterChainMatch";

  enum ConnectionSourceType {
    // Any connection source matches.
    ANY = 0;

    // Match a connection originating from the same host.
    SAME_IP_OR_LOOPBACK = 1;

    // Match a connection originating from a different host.
    EXTERNAL = 2;
  }

  reserved 1;

  // Optional destination port to consider when use_original_dst is set on the
  // listener in determining a filter chain match.
  google.protobuf.UInt32Value destination_port = 8 [(validate.rules).uint32 = {lte: 65535 gte: 1}];

  // If non-empty, an IP address and prefix length to match addresses when the
  // listener is bound to 0.0.0.0/:: or when use_original_dst is specified.
  repeated core.v3.CidrRange prefix_ranges = 3;

  // If non-empty, an IP address and suffix length to match addresses when the
  // listener is bound to 0.0.0.0/:: or when use_original_dst is specified.
  // [#not-implemented-hide:]
  string address_suffix = 4;

  // [#not-implemented-hide:]
  google.protobuf.UInt32Value suffix_len = 5;

  // The criteria is satisfied if the directly connected source IP address of the downstream
  // connection is contained in at least one of the specified subnets. If the parameter is not
  // specified or the list is empty, the directly connected source IP address is ignored.
  repeated core.v3.CidrRange direct_source_prefix_ranges = 13;

  // Specifies the connection source IP match type. Can be any, local or external network.
  ConnectionSourceType source_type = 12 [(validate.rules).enum = {defined_only: true}];

  // The criteria is satisfied if the source IP address of the downstream
  // connection is contained in at least one of the specified subnets. If the
  // parameter is not specified or the list is empty, the source IP address is
  // ignored.
  repeated core.v3.CidrRange source_prefix_ranges = 6;

  // The criteria is satisfied if the source port of the downstream connection
  // is contained in at least one of the specified ports. If the parameter is
  // not specified, the source port is ignored.
  repeated uint32 source_ports = 7
      [(validate.rules).repeated = {items {uint32 {lte: 65535 gte: 1}}}];

  // If non-empty, a list of server names (e.g. SNI for TLS protocol) to consider when determining
  // a filter chain match. Those values will be compared against the server names of a new
  // connection, when detected by one of the listener filters.
  //
  // The server name will be matched against all wildcard domains, i.e. ``www.example.com``
  // will be first matched against ``www.example.com``, then ``*.example.com``, then ``*.com``.
  //
  // Note that partial wildcards are not supported, and values like ``*w.example.com`` are invalid.
  // The value ``*`` is also not supported, and ``server_names`` should be omitted instead.
  //
  // .. attention::
  //
  //   See the :ref:`FAQ entry <faq_how_to_setup_sni>` on how to configure SNI for more
  //   information.
  repeated string server_names = 11;

  // If non-empty, a transport protocol to consider when determining a filter chain match.
  // This value will be compared against the transport protocol of a new connection, when
  // it's detected by one of the listener filters.
  //
  // Suggested values include:
  //
  // * ``raw_buffer`` - default, used when no transport protocol is detected,
  // * ``tls`` - set by :ref:`envoy.filters.listener.tls_inspector <config_listener_filters_tls_inspector>`
  //   when TLS protocol is detected.
  string transport_protocol = 9;

  // If non-empty, a list of application protocols (e.g. ALPN for TLS protocol) to consider when
  // determining a filter chain match. Those values will be compared against the application
  // protocols of a new connection, when detected by one of the listener filters.
  //
  // Suggested values include:
  //
  // * ``http/1.1`` - set by :ref:`envoy.filters.listener.tls_inspector
  //   <config_listener_filters_tls_inspector>`,
  // * ``h2`` - set by :ref:`envoy.filters.listener.tls_inspector <config_listener_filters_tls_inspector>`
  //
  // .. attention::
  //
  //   Currently, only :ref:`TLS Inspector <config_listener_filters_tls_inspector>` provides
  //   application protocol detection based on the requested
  //   `ALPN <https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation>`_ values.
  //
  //   However, the use of ALPN is pretty much limited to the HTTP/2 traffic on the Internet,
  //   and matching on values other than ``h2`` is going to lead to a lot of false negatives,
  //   unless all connecting clients are known to use ALPN.
  repeated string application_protocols = 10;
}

// A filter chain wraps a set of match criteria, an option TLS context, a set of filters, and
// various other parameters.
// [#next-free-field: 10]
message FilterChain {
  option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.listener.FilterChain";

  reserved 2, 8;

  reserved "tls_context", "on_demand_configuration";

  // The criteria to use when matching a connection to this filter chain.
  FilterChainMatch filter_chain_match = 1;

  // A list of individual network filters that make up the filter chain for
  // connections established with the listener. Order matters as the filters are
  // processed sequentially as connection events happen. Note: If the filter
  // list is empty, the connection will close by default.
  //
  // For QUIC listeners, network filters other than HTTP Connection Manager (HCM)
  // can be created, but due to differences in the connection implementation compared
  // to TCP, the onData() method will never be called. Therefore, network filters
  // for QUIC listeners should only expect to do work at the start of a new connection
  // (i.e. in onNewConnection()). HCM must be the last (or only) filter in the chain.
  repeated Filter filters = 3;

  // Whether the listener should expect a PROXY protocol V1 header on new
  // connections. If this option is enabled, the listener will assume that that
  // remote address of the connection is the one specified in the header. Some
  // load balancers including the AWS ELB support this option. If the option is
  // absent or set to false, Envoy will use the physical peer address of the
  // connection as the remote address.
  //
  // This field is deprecated. Add a
  // :ref:`PROXY protocol listener filter <config_listener_filters_proxy_protocol>`
  // explicitly instead.
  google.protobuf.BoolValue use_proxy_proto = 4
      [deprecated = true, (envoy.annotations.deprecated_at_minor_version) = "3.0"];

  // [#not-implemented-hide:] filter chain metadata.
  core.v3.Metadata metadata = 5;

  // Optional custom transport socket implementation to use for downstream connections.
  // To setup TLS, set a transport socket with name ``envoy.transport_sockets.tls`` and
  // :ref:`DownstreamTlsContext <envoy_v3_api_msg_extensions.transport_sockets.tls.v3.DownstreamTlsContext>` in the ``typed_config``.
  // If no transport socket configuration is specified, new connections
  // will be set up with plaintext.
  // [#extension-category: envoy.transport_sockets.downstream]
  core.v3.TransportSocket transport_socket = 6;

  // If present and nonzero, the amount of time to allow incoming connections to complete any
  // transport socket negotiations. If this expires before the transport reports connection
  // establishment, the connection is summarily closed.
  google.protobuf.Duration transport_socket_connect_timeout = 9;

  // The unique name (or empty) by which this filter chain is known.
  // Note: :ref:`filter_chain_matcher
  // <envoy_v3_api_field_config.listener.v3.Listener.filter_chain_matcher>`
  // requires that filter chains are uniquely named within a listener.
  string name = 7;
}

// Listener filter chain match configuration. This is a recursive structure which allows complex
// nested match configurations to be built using various logical operators.
//
// Examples:
//
// * Matches if the destination port is 3306.
//
// .. code-block:: yaml
//
//  destination_port_range:
//   start: 3306
//   end: 3307
//
// * Matches if the destination port is 3306 or 15000.
//
// .. code-block:: yaml
//
//  or_match:
//    rules:
//      - destination_port_range:
//          start: 3306
//          end: 3307
//      - destination_port_range:
//          start: 15000
//          end: 15001
//
// [#next-free-field: 6]
message ListenerFilterChainMatchPredicate {
  option (udpa.annotations.versioning).previous_message_type =
      "envoy.api.v2.listener.ListenerFilterChainMatchPredicate";

  // A set of match configurations used for logical operations.
  message MatchSet {
    option (udpa.annotations.versioning).previous_message_type =
        "envoy.api.v2.listener.ListenerFilterChainMatchPredicate.MatchSet";

    // The list of rules that make up the set.
    repeated ListenerFilterChainMatchPredicate rules = 1
        [(validate.rules).repeated = {min_items: 2}];
  }

  oneof rule {
    option (validate.required) = true;

    // A set that describes a logical OR. If any member of the set matches, the match configuration
    // matches.
    MatchSet or_match = 1;

    // A set that describes a logical AND. If all members of the set match, the match configuration
    // matches.
    MatchSet and_match = 2;

    // A negation match. The match configuration will match if the negated match condition matches.
    ListenerFilterChainMatchPredicate not_match = 3;

    // The match configuration will always match.
    bool any_match = 4 [(validate.rules).bool = {const: true}];

    // Match destination port. Particularly, the match evaluation must use the recovered local port if
    // the owning listener filter is after :ref:`an original_dst listener filter <config_listener_filters_original_dst>`.
    type.v3.Int32Range destination_port_range = 5;
  }
}

// [#next-free-field: 6]
message ListenerFilter {
  option (udpa.annotations.versioning).previous_message_type =
      "envoy.api.v2.listener.ListenerFilter";

  reserved 2;

  reserved "config";

  // The name of the filter configuration.
  string name = 1 [(validate.rules).string = {min_len: 1}];

  oneof config_type {
    // Filter specific configuration which depends on the filter being
    // instantiated. See the supported filters for further documentation.
    // [#extension-category: envoy.filters.listener,envoy.filters.udp_listener]
    google.protobuf.Any typed_config = 3;

    // Configuration source specifier for an extension configuration discovery
    // service. In case of a failure and without the default configuration, the
    // listener closes the connections.
    core.v3.ExtensionConfigSource config_discovery = 5;
  }

  // Optional match predicate used to disable the filter. The filter is enabled when this field is empty.
  // See :ref:`ListenerFilterChainMatchPredicate <envoy_v3_api_msg_config.listener.v3.ListenerFilterChainMatchPredicate>`
  // for further examples.
  ListenerFilterChainMatchPredicate filter_disabled = 4;
}
