istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/simulation/traffic.go (about)

     1  // Copyright Istio Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package simulation
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  	"net"
    21  	"net/http"
    22  	"regexp"
    23  	"strings"
    24  	"testing"
    25  
    26  	cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
    27  	envoycore "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
    28  	listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
    29  	route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
    30  	tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
    31  	"github.com/google/go-cmp/cmp"
    32  	"github.com/google/go-cmp/cmp/cmpopts"
    33  	"github.com/yl2chen/cidranger"
    34  
    35  	"istio.io/istio/pilot/pkg/model"
    36  	"istio.io/istio/pilot/pkg/networking/core"
    37  	xdsfilters "istio.io/istio/pilot/pkg/xds/filters"
    38  	"istio.io/istio/pilot/test/xds"
    39  	"istio.io/istio/pilot/test/xdstest"
    40  	"istio.io/istio/pkg/config/host"
    41  	istiolog "istio.io/istio/pkg/log"
    42  	"istio.io/istio/pkg/test"
    43  	"istio.io/istio/pkg/util/sets"
    44  )
    45  
    46  var log = istiolog.RegisterScope("simulation", "")
    47  
    48  type Protocol string
    49  
    50  const (
    51  	HTTP  Protocol = "http"
    52  	HTTP2 Protocol = "http2"
    53  	TCP   Protocol = "tcp"
    54  )
    55  
    56  type TLSMode string
    57  
    58  const (
    59  	Plaintext TLSMode = "plaintext"
    60  	TLS       TLSMode = "tls"
    61  	MTLS      TLSMode = "mtls"
    62  )
    63  
    64  func (c Call) IsHTTP() bool {
    65  	return httpProtocols.Contains(string(c.Protocol)) && (c.TLS == Plaintext || c.TLS == "")
    66  }
    67  
    68  var httpProtocols = sets.New(string(HTTP), string(HTTP2))
    69  
    70  var (
    71  	ErrNoListener          = errors.New("no listener matched")
    72  	ErrNoFilterChain       = errors.New("no filter chains matched")
    73  	ErrNoRoute             = errors.New("no route matched")
    74  	ErrTLSRedirect         = errors.New("tls required, sending 301")
    75  	ErrNoVirtualHost       = errors.New("no virtual host matched")
    76  	ErrMultipleFilterChain = errors.New("multiple filter chains matched")
    77  	// ErrProtocolError happens when sending TLS/TCP request to HCM, for example
    78  	ErrProtocolError = errors.New("protocol error")
    79  	ErrTLSError      = errors.New("invalid TLS")
    80  	ErrMTLSError     = errors.New("invalid mTLS")
    81  )
    82  
    83  type Expect struct {
    84  	Name   string
    85  	Call   Call
    86  	Result Result
    87  }
    88  
    89  type CallMode string
    90  
    91  type CustomFilterChainValidation func(filterChain *listener.FilterChain) error
    92  
    93  var (
    94  	// CallModeGateway simulate no iptables
    95  	CallModeGateway CallMode = "gateway"
    96  	// CallModeOutbound simulate iptables redirect to 15001
    97  	CallModeOutbound CallMode = "outbound"
    98  	// CallModeInbound simulate iptables redirect to 15006
    99  	CallModeInbound CallMode = "inbound"
   100  )
   101  
   102  type Call struct {
   103  	Address string
   104  	Port    int
   105  	Path    string
   106  
   107  	// Protocol describes the protocol type. TLS encapsulation is separate
   108  	Protocol Protocol
   109  	// TLS describes the connection tls parameters
   110  	// TODO: currently this does not verify TLS vs mTLS
   111  	TLS  TLSMode
   112  	Alpn string
   113  
   114  	// HostHeader is a convenience field for Headers
   115  	HostHeader string
   116  	Headers    http.Header
   117  
   118  	Sni string
   119  
   120  	// CallMode describes the type of call to make.
   121  	CallMode CallMode
   122  
   123  	CustomListenerValidations []CustomFilterChainValidation
   124  
   125  	MtlsSecretConfigName string
   126  }
   127  
   128  func (c Call) FillDefaults() Call {
   129  	if c.Headers == nil {
   130  		c.Headers = http.Header{}
   131  	}
   132  	if c.HostHeader != "" {
   133  		c.Headers["Host"] = []string{c.HostHeader}
   134  	}
   135  	// For simplicity, set SNI automatically for TLS traffic.
   136  	if c.Sni == "" && (c.TLS == TLS) {
   137  		c.Sni = c.HostHeader
   138  	}
   139  	if c.Path == "" {
   140  		c.Path = "/"
   141  	}
   142  	if c.TLS == "" {
   143  		c.TLS = Plaintext
   144  	}
   145  	if c.Address == "" {
   146  		// pick a random address, assumption is the test does not care
   147  		c.Address = "1.3.3.7"
   148  	}
   149  	if c.TLS == MTLS && c.Alpn == "" {
   150  		c.Alpn = protocolToMTLSAlpn(c.Protocol)
   151  	}
   152  	if c.TLS == TLS && c.Alpn == "" {
   153  		c.Alpn = protocolToTLSAlpn(c.Protocol)
   154  	}
   155  	return c
   156  }
   157  
   158  type Result struct {
   159  	Error              error
   160  	ListenerMatched    string
   161  	FilterChainMatched string
   162  	RouteMatched       string
   163  	RouteConfigMatched string
   164  	VirtualHostMatched string
   165  	ClusterMatched     string
   166  	// StrictMatch controls whether we will strictly match the result. If unset, empty fields will
   167  	// be ignored, allowing testing only fields we care about This allows asserting that the result
   168  	// is *exactly* equal, allowing asserting a field is empty
   169  	StrictMatch bool
   170  	// If set, this will mark a test as skipped. Note the result is still checked first - we skip only
   171  	// if we pass the test. This is to ensure that if the behavior changes, we still capture it; the skip
   172  	// just ensures we notice a test is wrong
   173  	Skip string
   174  	t    test.Failer
   175  }
   176  
   177  func (r Result) Matches(t *testing.T, want Result) {
   178  	t.Helper()
   179  	r.StrictMatch = want.StrictMatch // to make diff pass
   180  	r.Skip = want.Skip               // to make diff pass
   181  	diff := cmp.Diff(want, r, cmpopts.IgnoreUnexported(Result{}), cmpopts.EquateErrors())
   182  	if want.StrictMatch && diff != "" {
   183  		t.Errorf("Diff: %v", diff)
   184  		return
   185  	}
   186  	if want.Error != r.Error {
   187  		t.Errorf("want error %v got %v", want.Error, r.Error)
   188  	}
   189  	if want.ListenerMatched != "" && want.ListenerMatched != r.ListenerMatched {
   190  		t.Errorf("want listener matched %q got %q", want.ListenerMatched, r.ListenerMatched)
   191  	} else {
   192  		// Populate each field in case we did not care about it. This avoids confusing errors when we have fields
   193  		// we don't care about in the test that are present in the result.
   194  		want.ListenerMatched = r.ListenerMatched
   195  	}
   196  	if want.FilterChainMatched != "" && want.FilterChainMatched != r.FilterChainMatched {
   197  		t.Errorf("want filter chain matched %q got %q", want.FilterChainMatched, r.FilterChainMatched)
   198  	} else {
   199  		want.FilterChainMatched = r.FilterChainMatched
   200  	}
   201  	if want.RouteMatched != "" && want.RouteMatched != r.RouteMatched {
   202  		t.Errorf("want route matched %q got %q", want.RouteMatched, r.RouteMatched)
   203  	} else {
   204  		want.RouteMatched = r.RouteMatched
   205  	}
   206  	if want.RouteConfigMatched != "" && want.RouteConfigMatched != r.RouteConfigMatched {
   207  		t.Errorf("want route config matched %q got %q", want.RouteConfigMatched, r.RouteConfigMatched)
   208  	} else {
   209  		want.RouteConfigMatched = r.RouteConfigMatched
   210  	}
   211  	if want.VirtualHostMatched != "" && want.VirtualHostMatched != r.VirtualHostMatched {
   212  		t.Errorf("want virtual host matched %q got %q", want.VirtualHostMatched, r.VirtualHostMatched)
   213  	} else {
   214  		want.VirtualHostMatched = r.VirtualHostMatched
   215  	}
   216  	if want.ClusterMatched != "" && want.ClusterMatched != r.ClusterMatched {
   217  		t.Errorf("want cluster matched %q got %q", want.ClusterMatched, r.ClusterMatched)
   218  	} else {
   219  		want.ClusterMatched = r.ClusterMatched
   220  	}
   221  	if t.Failed() {
   222  		t.Logf("Diff: %+v", diff)
   223  		t.Logf("Full Diff: %+v", cmp.Diff(want, r, cmpopts.IgnoreUnexported(Result{}), cmpopts.EquateErrors()))
   224  	} else if want.Skip != "" {
   225  		t.Skipf("Known bug: %v", r.Skip)
   226  	}
   227  }
   228  
   229  type Simulation struct {
   230  	t         *testing.T
   231  	Listeners []*listener.Listener
   232  	Clusters  []*cluster.Cluster
   233  	Routes    []*route.RouteConfiguration
   234  }
   235  
   236  func NewSimulationFromConfigGen(t *testing.T, s *core.ConfigGenTest, proxy *model.Proxy) *Simulation {
   237  	l := s.Listeners(proxy)
   238  	sim := &Simulation{
   239  		t:         t,
   240  		Listeners: l,
   241  		Clusters:  s.Clusters(proxy),
   242  		Routes:    s.RoutesFromListeners(proxy, l),
   243  	}
   244  	return sim
   245  }
   246  
   247  func NewSimulation(t *testing.T, s *xds.FakeDiscoveryServer, proxy *model.Proxy) *Simulation {
   248  	return NewSimulationFromConfigGen(t, s.ConfigGenTest, proxy)
   249  }
   250  
   251  // withT swaps out the testing struct. This allows executing sub tests.
   252  func (sim *Simulation) withT(t *testing.T) *Simulation {
   253  	cpy := *sim
   254  	cpy.t = t
   255  	return &cpy
   256  }
   257  
   258  func (sim *Simulation) RunExpectations(es []Expect) {
   259  	for _, e := range es {
   260  		sim.t.Run(e.Name, func(t *testing.T) {
   261  			sim.withT(t).Run(e.Call).Matches(t, e.Result)
   262  		})
   263  	}
   264  }
   265  
   266  func hasFilterOnPort(l *listener.Listener, filter string, port int) bool {
   267  	got, f := xdstest.ExtractListenerFilters(l)[filter]
   268  	if !f {
   269  		return false
   270  	}
   271  	if got.FilterDisabled == nil {
   272  		return true
   273  	}
   274  	return !xdstest.EvaluateListenerFilterPredicates(got.FilterDisabled, port)
   275  }
   276  
   277  func (sim *Simulation) Run(input Call) (result Result) {
   278  	result = Result{t: sim.t}
   279  	input = input.FillDefaults()
   280  	if input.Alpn != "" && input.TLS == Plaintext {
   281  		result.Error = fmt.Errorf("invalid call, ALPN can only be sent in TLS requests")
   282  		return result
   283  	}
   284  
   285  	// First we will match a listener
   286  	l := matchListener(sim.Listeners, input)
   287  	if l == nil {
   288  		result.Error = ErrNoListener
   289  		return
   290  	}
   291  	result.ListenerMatched = l.Name
   292  
   293  	hasTLSInspector := hasFilterOnPort(l, xdsfilters.TLSInspector.Name, input.Port)
   294  	if !hasTLSInspector {
   295  		// Without tls inspector, Envoy would not read the ALPN in the TLS handshake
   296  		// HTTP inspector still may set it though
   297  		input.Alpn = ""
   298  	}
   299  
   300  	// Apply listener filters
   301  	if hasFilterOnPort(l, xdsfilters.HTTPInspector.Name, input.Port) {
   302  		if alpn := protocolToAlpn(input.Protocol); alpn != "" && input.TLS == Plaintext {
   303  			input.Alpn = alpn
   304  		}
   305  	}
   306  
   307  	fc, err := sim.matchFilterChain(l.FilterChains, l.DefaultFilterChain, input, hasTLSInspector)
   308  	if err != nil {
   309  		result.Error = err
   310  		return
   311  	}
   312  	result.FilterChainMatched = fc.Name
   313  	// Plaintext to TLS is an error
   314  	if fc.TransportSocket != nil && input.TLS == Plaintext {
   315  		result.Error = ErrTLSError
   316  		return
   317  	}
   318  
   319  	mTLSSecretConfigName := "default"
   320  	if input.MtlsSecretConfigName != "" {
   321  		mTLSSecretConfigName = input.MtlsSecretConfigName
   322  	}
   323  
   324  	// mTLS listener will only accept mTLS traffic
   325  	if fc.TransportSocket != nil && sim.requiresMTLS(fc, mTLSSecretConfigName) != (input.TLS == MTLS) {
   326  		// If there is no tls inspector, then
   327  		result.Error = ErrMTLSError
   328  		return
   329  	}
   330  
   331  	if len(input.CustomListenerValidations) > 0 {
   332  		for _, validation := range input.CustomListenerValidations {
   333  			if err := validation(fc); err != nil {
   334  				result.Error = err
   335  			}
   336  		}
   337  	}
   338  
   339  	if hcm := xdstest.ExtractHTTPConnectionManager(sim.t, fc); hcm != nil {
   340  		// We matched HCM and didn't terminate TLS, but we are sending TLS traffic - decoding will fail
   341  		if input.TLS != Plaintext && fc.TransportSocket == nil {
   342  			result.Error = ErrProtocolError
   343  			return
   344  		}
   345  		// TCP to HCM is invalid
   346  		if input.Protocol != HTTP && input.Protocol != HTTP2 {
   347  			result.Error = ErrProtocolError
   348  			return
   349  		}
   350  
   351  		// Fetch inline route
   352  		rc := hcm.GetRouteConfig()
   353  		if rc == nil {
   354  			// If not set, fallback to RDS
   355  			routeName := hcm.GetRds().RouteConfigName
   356  			result.RouteConfigMatched = routeName
   357  			rc = xdstest.ExtractRouteConfigurations(sim.Routes)[routeName]
   358  		}
   359  		hostHeader := ""
   360  		if len(input.Headers["Host"]) > 0 {
   361  			hostHeader = input.Headers["Host"][0]
   362  		}
   363  		vh := sim.matchVirtualHost(rc, hostHeader)
   364  		if vh == nil {
   365  			result.Error = ErrNoVirtualHost
   366  			return
   367  		}
   368  		result.VirtualHostMatched = vh.Name
   369  		if vh.RequireTls == route.VirtualHost_ALL && input.TLS == Plaintext {
   370  			result.Error = ErrTLSRedirect
   371  			return
   372  		}
   373  
   374  		r := sim.matchRoute(vh, input)
   375  		if r == nil {
   376  			result.Error = ErrNoRoute
   377  			return
   378  		}
   379  		result.RouteMatched = r.Name
   380  		switch t := r.GetAction().(type) {
   381  		case *route.Route_Route:
   382  			result.ClusterMatched = t.Route.GetCluster()
   383  		}
   384  	} else if tcp := xdstest.ExtractTCPProxy(sim.t, fc); tcp != nil {
   385  		result.ClusterMatched = tcp.GetCluster()
   386  	}
   387  	return
   388  }
   389  
   390  func (sim *Simulation) requiresMTLS(fc *listener.FilterChain, mTLSSecretConfigName string) bool {
   391  	if fc.TransportSocket == nil {
   392  		return false
   393  	}
   394  	t := &tls.DownstreamTlsContext{}
   395  	if err := fc.GetTransportSocket().GetTypedConfig().UnmarshalTo(t); err != nil {
   396  		sim.t.Fatal(err)
   397  	}
   398  
   399  	if len(t.GetCommonTlsContext().GetTlsCertificateSdsSecretConfigs()) == 0 {
   400  		return false
   401  	}
   402  	// This is a lazy heuristic, we could check for explicit default resource or spiffe if it becomes necessary
   403  	if t.GetCommonTlsContext().GetTlsCertificateSdsSecretConfigs()[0].Name != mTLSSecretConfigName {
   404  		return false
   405  	}
   406  	if !t.RequireClientCertificate.Value {
   407  		return false
   408  	}
   409  	return true
   410  }
   411  
   412  func (sim *Simulation) matchRoute(vh *route.VirtualHost, input Call) *route.Route {
   413  	for _, r := range vh.Routes {
   414  		// check path
   415  		switch pt := r.Match.GetPathSpecifier().(type) {
   416  		case *route.RouteMatch_Prefix:
   417  			if !strings.HasPrefix(input.Path, pt.Prefix) {
   418  				continue
   419  			}
   420  		case *route.RouteMatch_PathSeparatedPrefix:
   421  			if !strings.HasPrefix(input.Path, pt.PathSeparatedPrefix) {
   422  				continue
   423  			}
   424  		case *route.RouteMatch_Path:
   425  			if input.Path != pt.Path {
   426  				continue
   427  			}
   428  		case *route.RouteMatch_SafeRegex:
   429  			r, err := regexp.Compile(pt.SafeRegex.GetRegex())
   430  			if err != nil {
   431  				sim.t.Fatalf("invalid regex %v: %v", pt.SafeRegex.GetRegex(), err)
   432  			}
   433  			if !r.MatchString(input.Path) {
   434  				continue
   435  			}
   436  		default:
   437  			sim.t.Fatalf("unknown route path type %T", pt)
   438  		}
   439  
   440  		// TODO this only handles path - we need to add headers, query params, etc to be complete.
   441  
   442  		return r
   443  	}
   444  	return nil
   445  }
   446  
   447  func (sim *Simulation) matchVirtualHost(rc *route.RouteConfiguration, host string) *route.VirtualHost {
   448  	if rc.GetIgnorePortInHostMatching() {
   449  		if h, _, err := net.SplitHostPort(host); err == nil {
   450  			host = h
   451  		}
   452  	}
   453  	// Exact match
   454  	for _, vh := range rc.VirtualHosts {
   455  		for _, d := range vh.Domains {
   456  			if d == host {
   457  				return vh
   458  			}
   459  		}
   460  	}
   461  	// prefix match
   462  	var bestMatch *route.VirtualHost
   463  	longest := 0
   464  	for _, vh := range rc.VirtualHosts {
   465  		for _, d := range vh.Domains {
   466  			if d[0] != '*' {
   467  				continue
   468  			}
   469  			if len(host) >= len(d) && strings.HasSuffix(host, d[1:]) && len(d) > longest {
   470  				bestMatch = vh
   471  				longest = len(d)
   472  			}
   473  		}
   474  	}
   475  	if bestMatch != nil {
   476  		return bestMatch
   477  	}
   478  	// Suffix match
   479  	longest = 0
   480  	for _, vh := range rc.VirtualHosts {
   481  		for _, d := range vh.Domains {
   482  			if d[len(d)-1] != '*' {
   483  				continue
   484  			}
   485  			if len(host) >= len(d) && strings.HasPrefix(host, d[:len(d)-1]) && len(d) > longest {
   486  				bestMatch = vh
   487  				longest = len(d)
   488  			}
   489  		}
   490  	}
   491  	if bestMatch != nil {
   492  		return bestMatch
   493  	}
   494  	// wildcard match
   495  	for _, vh := range rc.VirtualHosts {
   496  		for _, d := range vh.Domains {
   497  			if d == "*" {
   498  				return vh
   499  			}
   500  		}
   501  	}
   502  	return nil
   503  }
   504  
   505  // Follow the 8 step Sieve as in
   506  // https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/listener/v3/listener_components.proto.html#config-listener-v3-filterchainmatch
   507  // The implementation may initially be confusing because of a property of the
   508  // Envoy algorithm - at each level we will filter out all FilterChains that do
   509  // not match. This means an empty match (`{}`) may not match if another chain
   510  // matches one criteria but not another.
   511  func (sim *Simulation) matchFilterChain(chains []*listener.FilterChain, defaultChain *listener.FilterChain,
   512  	input Call, hasTLSInspector bool,
   513  ) (*listener.FilterChain, error) {
   514  	chains = filter("DestinationPort", chains, func(fc *listener.FilterChainMatch) bool {
   515  		return fc.GetDestinationPort() == nil
   516  	}, func(fc *listener.FilterChainMatch) bool {
   517  		return int(fc.GetDestinationPort().GetValue()) == input.Port
   518  	})
   519  	chains = filter("PrefixRanges", chains, func(fc *listener.FilterChainMatch) bool {
   520  		return fc.GetPrefixRanges() == nil
   521  	}, func(fc *listener.FilterChainMatch) bool {
   522  		ranger := cidranger.NewPCTrieRanger()
   523  		for _, a := range fc.GetPrefixRanges() {
   524  			s := fmt.Sprintf("%s/%d", a.AddressPrefix, a.GetPrefixLen().GetValue())
   525  			_, cidr, err := net.ParseCIDR(s)
   526  			if err != nil {
   527  				sim.t.Fatalf("failed to parse cidr %v: %v", s, err)
   528  			}
   529  			if err := ranger.Insert(cidranger.NewBasicRangerEntry(*cidr)); err != nil {
   530  				sim.t.Fatalf("failed to insert cidr %v: %v", cidr, err)
   531  			}
   532  		}
   533  		f, err := ranger.Contains(net.ParseIP(input.Address))
   534  		if err != nil {
   535  			sim.t.Fatalf("cidr containers %v failed: %v", input.Address, err)
   536  		}
   537  		return f
   538  	})
   539  	chains = filter("ServerNames", chains, func(fc *listener.FilterChainMatch) bool {
   540  		return fc.GetServerNames() == nil
   541  	}, func(fc *listener.FilterChainMatch) bool {
   542  		sni := host.Name(input.Sni)
   543  		for _, s := range fc.GetServerNames() {
   544  			if sni.SubsetOf(host.Name(s)) {
   545  				return true
   546  			}
   547  		}
   548  		return false
   549  	})
   550  	chains = filter("TransportProtocol", chains, func(fc *listener.FilterChainMatch) bool {
   551  		return fc.GetTransportProtocol() == ""
   552  	}, func(fc *listener.FilterChainMatch) bool {
   553  		if !hasTLSInspector {
   554  			// Without tls inspector, transport protocol will always be raw buffer
   555  			return fc.GetTransportProtocol() == xdsfilters.RawBufferTransportProtocol
   556  		}
   557  		switch fc.GetTransportProtocol() {
   558  		case xdsfilters.TLSTransportProtocol:
   559  			return input.TLS == TLS || input.TLS == MTLS
   560  		case xdsfilters.RawBufferTransportProtocol:
   561  			return input.TLS == Plaintext
   562  		}
   563  		return false
   564  	})
   565  	chains = filter("ApplicationProtocols", chains, func(fc *listener.FilterChainMatch) bool {
   566  		return fc.GetApplicationProtocols() == nil
   567  	}, func(fc *listener.FilterChainMatch) bool {
   568  		return sets.New(fc.GetApplicationProtocols()...).Contains(input.Alpn)
   569  	})
   570  	// We do not implement the "source" based filters as we do not use them
   571  	if len(chains) > 1 {
   572  		for _, c := range chains {
   573  			log.Warnf("Matched chain %v", c.Name)
   574  		}
   575  		return nil, ErrMultipleFilterChain
   576  	}
   577  	if len(chains) == 0 {
   578  		if defaultChain != nil {
   579  			return defaultChain, nil
   580  		}
   581  		return nil, ErrNoFilterChain
   582  	}
   583  	return chains[0], nil
   584  }
   585  
   586  func filter(desc string, chains []*listener.FilterChain,
   587  	empty func(fc *listener.FilterChainMatch) bool,
   588  	match func(fc *listener.FilterChainMatch) bool,
   589  ) []*listener.FilterChain {
   590  	res := []*listener.FilterChain{}
   591  	anySet := false
   592  	for _, c := range chains {
   593  		if !empty(c.GetFilterChainMatch()) {
   594  			anySet = true
   595  			break
   596  		}
   597  	}
   598  	if !anySet {
   599  		log.Debugf("%v: none set, skipping", desc)
   600  		return chains
   601  	}
   602  	for i, c := range chains {
   603  		if match(c.GetFilterChainMatch()) {
   604  			log.Debugf("%v: matched chain %v/%v", desc, i, c.GetName())
   605  			res = append(res, c)
   606  		}
   607  	}
   608  	// Return all matching filter chains
   609  	if len(res) > 0 {
   610  		return res
   611  	}
   612  	// Unless there were no matches - in which case we return all filter chains that did not have a
   613  	// match set
   614  	for i, c := range chains {
   615  		if empty(c.GetFilterChainMatch()) {
   616  			log.Debugf("%v: no matches, found empty chain match %v/%v", desc, i, c.GetName())
   617  			res = append(res, c)
   618  		}
   619  	}
   620  	return res
   621  }
   622  
   623  func protocolToMTLSAlpn(s Protocol) string {
   624  	switch s {
   625  	case HTTP:
   626  		return "istio-http/1.1"
   627  	case HTTP2:
   628  		return "istio-h2"
   629  	default:
   630  		return "istio"
   631  	}
   632  }
   633  
   634  func protocolToTLSAlpn(s Protocol) string {
   635  	switch s {
   636  	case HTTP:
   637  		return "http/1.1"
   638  	case HTTP2:
   639  		return "h2"
   640  	default:
   641  		return ""
   642  	}
   643  }
   644  
   645  func protocolToAlpn(s Protocol) string {
   646  	switch s {
   647  	case HTTP:
   648  		return "http/1.1"
   649  	case HTTP2:
   650  		return "h2c"
   651  	default:
   652  		return ""
   653  	}
   654  }
   655  
   656  func matchListener(listeners []*listener.Listener, input Call) *listener.Listener {
   657  	if input.CallMode == CallModeInbound {
   658  		return xdstest.ExtractListener(model.VirtualInboundListenerName, listeners)
   659  	}
   660  	// First find exact match for the IP/Port, then fallback to wildcard IP/Port
   661  	// There is no wildcard port
   662  	for _, l := range listeners {
   663  		if matchAddress(l.GetAddress(), input.Address, input.Port) {
   664  			return l
   665  		}
   666  	}
   667  	for _, l := range listeners {
   668  		if matchAddress(l.GetAddress(), "0.0.0.0", input.Port) {
   669  			return l
   670  		}
   671  	}
   672  
   673  	// Fallback to the outbound listener
   674  	// TODO - support inbound
   675  	for _, l := range listeners {
   676  		if l.Name == model.VirtualOutboundListenerName {
   677  			return l
   678  		}
   679  	}
   680  	return nil
   681  }
   682  
   683  func matchAddress(a *envoycore.Address, address string, port int) bool {
   684  	if a.GetSocketAddress().GetAddress() != address {
   685  		return false
   686  	}
   687  	if int(a.GetSocketAddress().GetPortValue()) != port {
   688  		return false
   689  	}
   690  	return true
   691  }