1// +build go1.12
2
3/*
4 *
5 * Copyright 2021 gRPC authors.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *     http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 */
20
21package xdsclient
22
23import (
24	"fmt"
25	"net"
26	"strings"
27	"testing"
28
29	v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
30	v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
31	v3httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
32	v3tlspb "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
33	"github.com/google/go-cmp/cmp"
34	"github.com/google/go-cmp/cmp/cmpopts"
35	"google.golang.org/protobuf/testing/protocmp"
36	"google.golang.org/protobuf/types/known/anypb"
37	"google.golang.org/protobuf/types/known/wrapperspb"
38
39	"google.golang.org/grpc/internal/testutils"
40	"google.golang.org/grpc/xds/internal/version"
41)
42
43var (
44	emptyValidNetworkFilters = []*v3listenerpb.Filter{
45		{
46			Name: "filter-1",
47			ConfigType: &v3listenerpb.Filter_TypedConfig{
48				TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{}),
49			},
50		},
51	}
52	validServerSideHTTPFilter1 = &v3httppb.HttpFilter{
53		Name:       "serverOnlyCustomFilter",
54		ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: serverOnlyCustomFilterConfig},
55	}
56	validServerSideHTTPFilter2 = &v3httppb.HttpFilter{
57		Name:       "serverOnlyCustomFilter2",
58		ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: serverOnlyCustomFilterConfig},
59	}
60)
61
62// TestNewFilterChainImpl_Failure_BadMatchFields verifies cases where we have a
63// single filter chain with match criteria that contains unsupported fields.
64func TestNewFilterChainImpl_Failure_BadMatchFields(t *testing.T) {
65	tests := []struct {
66		desc string
67		lis  *v3listenerpb.Listener
68	}{
69		{
70			desc: "unsupported destination port field",
71			lis: &v3listenerpb.Listener{
72				FilterChains: []*v3listenerpb.FilterChain{
73					{
74						FilterChainMatch: &v3listenerpb.FilterChainMatch{DestinationPort: &wrapperspb.UInt32Value{Value: 666}},
75					},
76				},
77			},
78		},
79		{
80			desc: "unsupported server names field",
81			lis: &v3listenerpb.Listener{
82				FilterChains: []*v3listenerpb.FilterChain{
83					{
84						FilterChainMatch: &v3listenerpb.FilterChainMatch{ServerNames: []string{"example-server"}},
85					},
86				},
87			},
88		},
89		{
90			desc: "unsupported transport protocol ",
91			lis: &v3listenerpb.Listener{
92				FilterChains: []*v3listenerpb.FilterChain{
93					{
94						FilterChainMatch: &v3listenerpb.FilterChainMatch{TransportProtocol: "tls"},
95					},
96				},
97			},
98		},
99		{
100			desc: "unsupported application protocol field",
101			lis: &v3listenerpb.Listener{
102				FilterChains: []*v3listenerpb.FilterChain{
103					{
104						FilterChainMatch: &v3listenerpb.FilterChainMatch{ApplicationProtocols: []string{"h2"}},
105					},
106				},
107			},
108		},
109		{
110			desc: "bad dest address prefix",
111			lis: &v3listenerpb.Listener{
112				FilterChains: []*v3listenerpb.FilterChain{
113					{
114						FilterChainMatch: &v3listenerpb.FilterChainMatch{PrefixRanges: []*v3corepb.CidrRange{{AddressPrefix: "a.b.c.d"}}},
115					},
116				},
117			},
118		},
119		{
120			desc: "bad dest prefix length",
121			lis: &v3listenerpb.Listener{
122				FilterChains: []*v3listenerpb.FilterChain{
123					{
124						FilterChainMatch: &v3listenerpb.FilterChainMatch{PrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("10.1.1.0", 50)}},
125					},
126				},
127			},
128		},
129		{
130			desc: "bad source address prefix",
131			lis: &v3listenerpb.Listener{
132				FilterChains: []*v3listenerpb.FilterChain{
133					{
134						FilterChainMatch: &v3listenerpb.FilterChainMatch{SourcePrefixRanges: []*v3corepb.CidrRange{{AddressPrefix: "a.b.c.d"}}},
135					},
136				},
137			},
138		},
139		{
140			desc: "bad source prefix length",
141			lis: &v3listenerpb.Listener{
142				FilterChains: []*v3listenerpb.FilterChain{
143					{
144						FilterChainMatch: &v3listenerpb.FilterChainMatch{SourcePrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("10.1.1.0", 50)}},
145					},
146				},
147			},
148		},
149	}
150
151	for _, test := range tests {
152		t.Run(test.desc, func(t *testing.T) {
153			if fci, err := NewFilterChainManager(test.lis); err == nil {
154				t.Fatalf("NewFilterChainManager() returned %v when expected to fail", fci)
155			}
156		})
157	}
158}
159
160// TestNewFilterChainImpl_Failure_OverlappingMatchingRules verifies cases where
161// there are multiple filter chains and they have overlapping match rules.
162func TestNewFilterChainImpl_Failure_OverlappingMatchingRules(t *testing.T) {
163	tests := []struct {
164		desc string
165		lis  *v3listenerpb.Listener
166	}{
167		{
168			desc: "matching destination prefixes with no other matchers",
169			lis: &v3listenerpb.Listener{
170				FilterChains: []*v3listenerpb.FilterChain{
171					{
172						FilterChainMatch: &v3listenerpb.FilterChainMatch{
173							PrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.1.1", 16), cidrRangeFromAddressAndPrefixLen("10.0.0.0", 0)},
174						},
175						Filters: emptyValidNetworkFilters,
176					},
177					{
178						FilterChainMatch: &v3listenerpb.FilterChainMatch{
179							PrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.2.2", 16)},
180						},
181						Filters: emptyValidNetworkFilters,
182					},
183				},
184			},
185		},
186		{
187			desc: "matching source type",
188			lis: &v3listenerpb.Listener{
189				FilterChains: []*v3listenerpb.FilterChain{
190					{
191						FilterChainMatch: &v3listenerpb.FilterChainMatch{SourceType: v3listenerpb.FilterChainMatch_ANY},
192						Filters:          emptyValidNetworkFilters,
193					},
194					{
195						FilterChainMatch: &v3listenerpb.FilterChainMatch{SourceType: v3listenerpb.FilterChainMatch_SAME_IP_OR_LOOPBACK},
196						Filters:          emptyValidNetworkFilters,
197					},
198					{
199						FilterChainMatch: &v3listenerpb.FilterChainMatch{SourceType: v3listenerpb.FilterChainMatch_EXTERNAL},
200						Filters:          emptyValidNetworkFilters,
201					},
202					{
203						FilterChainMatch: &v3listenerpb.FilterChainMatch{SourceType: v3listenerpb.FilterChainMatch_EXTERNAL},
204						Filters:          emptyValidNetworkFilters,
205					},
206				},
207			},
208		},
209		{
210			desc: "matching source prefixes",
211			lis: &v3listenerpb.Listener{
212				FilterChains: []*v3listenerpb.FilterChain{
213					{
214						FilterChainMatch: &v3listenerpb.FilterChainMatch{
215							SourcePrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.1.1", 16), cidrRangeFromAddressAndPrefixLen("10.0.0.0", 0)},
216						},
217						Filters: emptyValidNetworkFilters,
218					},
219					{
220						FilterChainMatch: &v3listenerpb.FilterChainMatch{
221							SourcePrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.2.2", 16)},
222						},
223						Filters: emptyValidNetworkFilters,
224					},
225				},
226			},
227		},
228		{
229			desc: "matching source ports",
230			lis: &v3listenerpb.Listener{
231				FilterChains: []*v3listenerpb.FilterChain{
232					{
233						FilterChainMatch: &v3listenerpb.FilterChainMatch{SourcePorts: []uint32{1, 2, 3, 4, 5}},
234						Filters:          emptyValidNetworkFilters,
235					},
236					{
237						FilterChainMatch: &v3listenerpb.FilterChainMatch{},
238						Filters:          emptyValidNetworkFilters,
239					},
240					{
241						FilterChainMatch: &v3listenerpb.FilterChainMatch{SourcePorts: []uint32{5, 6, 7}},
242						Filters:          emptyValidNetworkFilters,
243					},
244				},
245			},
246		},
247	}
248
249	const wantErr = "multiple filter chains with overlapping matching rules are defined"
250	for _, test := range tests {
251		t.Run(test.desc, func(t *testing.T) {
252			_, err := NewFilterChainManager(test.lis)
253			if err == nil || !strings.Contains(err.Error(), wantErr) {
254				t.Fatalf("NewFilterChainManager() returned err: %v, wantErr: %s", err, wantErr)
255			}
256		})
257	}
258}
259
260// TestNewFilterChainImpl_Failure_BadSecurityConfig verifies cases where the
261// security configuration in the filter chain is invalid.
262func TestNewFilterChainImpl_Failure_BadSecurityConfig(t *testing.T) {
263	tests := []struct {
264		desc    string
265		lis     *v3listenerpb.Listener
266		wantErr string
267	}{
268		{
269			desc:    "no filter chains",
270			lis:     &v3listenerpb.Listener{},
271			wantErr: "no supported filter chains and no default filter chain",
272		},
273		{
274			desc: "unexpected transport socket name",
275			lis: &v3listenerpb.Listener{
276				FilterChains: []*v3listenerpb.FilterChain{
277					{
278						TransportSocket: &v3corepb.TransportSocket{Name: "unsupported-transport-socket-name"},
279						Filters:         emptyValidNetworkFilters,
280					},
281				},
282			},
283			wantErr: "transport_socket field has unexpected name",
284		},
285		{
286			desc: "unexpected transport socket URL",
287			lis: &v3listenerpb.Listener{
288				FilterChains: []*v3listenerpb.FilterChain{
289					{
290						TransportSocket: &v3corepb.TransportSocket{
291							Name: "envoy.transport_sockets.tls",
292							ConfigType: &v3corepb.TransportSocket_TypedConfig{
293								TypedConfig: testutils.MarshalAny(&v3tlspb.UpstreamTlsContext{}),
294							},
295						},
296						Filters: emptyValidNetworkFilters,
297					},
298				},
299			},
300			wantErr: "transport_socket field has unexpected typeURL",
301		},
302		{
303			desc: "badly marshaled transport socket",
304			lis: &v3listenerpb.Listener{
305				FilterChains: []*v3listenerpb.FilterChain{
306					{
307						TransportSocket: &v3corepb.TransportSocket{
308							Name: "envoy.transport_sockets.tls",
309							ConfigType: &v3corepb.TransportSocket_TypedConfig{
310								TypedConfig: &anypb.Any{
311									TypeUrl: version.V3DownstreamTLSContextURL,
312									Value:   []byte{1, 2, 3, 4},
313								},
314							},
315						},
316						Filters: emptyValidNetworkFilters,
317					},
318				},
319			},
320			wantErr: "failed to unmarshal DownstreamTlsContext in LDS response",
321		},
322		{
323			desc: "missing CommonTlsContext",
324			lis: &v3listenerpb.Listener{
325				FilterChains: []*v3listenerpb.FilterChain{
326					{
327						TransportSocket: &v3corepb.TransportSocket{
328							Name: "envoy.transport_sockets.tls",
329							ConfigType: &v3corepb.TransportSocket_TypedConfig{
330								TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{}),
331							},
332						},
333						Filters: emptyValidNetworkFilters,
334					},
335				},
336			},
337			wantErr: "DownstreamTlsContext in LDS response does not contain a CommonTlsContext",
338		},
339		{
340			desc: "unsupported validation context in transport socket",
341			lis: &v3listenerpb.Listener{
342				FilterChains: []*v3listenerpb.FilterChain{
343					{
344						TransportSocket: &v3corepb.TransportSocket{
345							Name: "envoy.transport_sockets.tls",
346							ConfigType: &v3corepb.TransportSocket_TypedConfig{
347								TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
348									CommonTlsContext: &v3tlspb.CommonTlsContext{
349										ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextSdsSecretConfig{
350											ValidationContextSdsSecretConfig: &v3tlspb.SdsSecretConfig{
351												Name: "foo-sds-secret",
352											},
353										},
354									},
355								}),
356							},
357						},
358						Filters: emptyValidNetworkFilters,
359					},
360				},
361			},
362			wantErr: "validation context contains unexpected type",
363		},
364		{
365			desc: "no root certificate provider with require_client_cert",
366			lis: &v3listenerpb.Listener{
367				FilterChains: []*v3listenerpb.FilterChain{
368					{
369						TransportSocket: &v3corepb.TransportSocket{
370							Name: "envoy.transport_sockets.tls",
371							ConfigType: &v3corepb.TransportSocket_TypedConfig{
372								TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
373									RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
374									CommonTlsContext: &v3tlspb.CommonTlsContext{
375										TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
376											InstanceName:    "identityPluginInstance",
377											CertificateName: "identityCertName",
378										},
379									},
380								}),
381							},
382						},
383						Filters: emptyValidNetworkFilters,
384					},
385				},
386			},
387			wantErr: "security configuration on the server-side does not contain root certificate provider instance name, but require_client_cert field is set",
388		},
389		{
390			desc: "no identity certificate provider",
391			lis: &v3listenerpb.Listener{
392				FilterChains: []*v3listenerpb.FilterChain{
393					{
394						TransportSocket: &v3corepb.TransportSocket{
395							Name: "envoy.transport_sockets.tls",
396							ConfigType: &v3corepb.TransportSocket_TypedConfig{
397								TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
398									CommonTlsContext: &v3tlspb.CommonTlsContext{},
399								}),
400							},
401						},
402						Filters: emptyValidNetworkFilters,
403					},
404				},
405			},
406			wantErr: "security configuration on the server-side does not contain identity certificate provider instance name",
407		},
408	}
409
410	for _, test := range tests {
411		t.Run(test.desc, func(t *testing.T) {
412			_, err := NewFilterChainManager(test.lis)
413			if err == nil || !strings.Contains(err.Error(), test.wantErr) {
414				t.Fatalf("NewFilterChainManager() returned err: %v, wantErr: %s", err, test.wantErr)
415			}
416		})
417	}
418}
419
420// TestNewFilterChainImpl_Failure_BadHTTPFilters verifies cases where the HTTP
421// Filters in the filter chain are invalid.
422func TestNewFilterChainImpl_Failure_BadHTTPFilters(t *testing.T) {
423	tests := []struct {
424		name    string
425		lis     *v3listenerpb.Listener
426		wantErr string
427	}{
428		{
429			name: "client side HTTP filter",
430			lis: &v3listenerpb.Listener{
431				Name: "grpc/server?xds.resource.listening_address=0.0.0.0:9999",
432				FilterChains: []*v3listenerpb.FilterChain{
433					{
434						Name: "filter-chain-1",
435						Filters: []*v3listenerpb.Filter{
436							{
437								Name: "hcm",
438								ConfigType: &v3listenerpb.Filter_TypedConfig{
439									TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
440										HttpFilters: []*v3httppb.HttpFilter{
441											{
442												Name:       "clientOnlyCustomFilter",
443												ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: clientOnlyCustomFilterConfig},
444											},
445										},
446									}),
447								},
448							},
449						},
450					},
451				},
452			},
453			wantErr: "invalid server side HTTP Filters",
454		},
455		{
456			name: "one valid then one invalid HTTP filter",
457			lis: &v3listenerpb.Listener{
458				Name: "grpc/server?xds.resource.listening_address=0.0.0.0:9999",
459				FilterChains: []*v3listenerpb.FilterChain{
460					{
461						Name: "filter-chain-1",
462						Filters: []*v3listenerpb.Filter{
463							{
464								Name: "hcm",
465								ConfigType: &v3listenerpb.Filter_TypedConfig{
466									TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
467										HttpFilters: []*v3httppb.HttpFilter{
468											validServerSideHTTPFilter1,
469											{
470												Name:       "clientOnlyCustomFilter",
471												ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: clientOnlyCustomFilterConfig},
472											},
473										},
474									}),
475								},
476							},
477						},
478					},
479				},
480			},
481			wantErr: "invalid server side HTTP Filters",
482		},
483	}
484	for _, test := range tests {
485		t.Run(test.name, func(t *testing.T) {
486			_, err := NewFilterChainManager(test.lis)
487			if err == nil || !strings.Contains(err.Error(), test.wantErr) {
488				t.Fatalf("NewFilterChainManager() returned err: %v, wantErr: %s", err, test.wantErr)
489			}
490		})
491	}
492}
493
494// TestNewFilterChainImpl_Success_HTTPFilters tests the construction of the
495// filter chain with valid HTTP Filters present.
496func TestNewFilterChainImpl_Success_HTTPFilters(t *testing.T) {
497	tests := []struct {
498		name   string
499		lis    *v3listenerpb.Listener
500		wantFC *FilterChainManager
501	}{
502		{
503			name: "singular valid http filter",
504			lis: &v3listenerpb.Listener{
505				FilterChains: []*v3listenerpb.FilterChain{
506					{
507						Name: "filter-chain-1",
508						Filters: []*v3listenerpb.Filter{
509							{
510								Name: "hcm",
511								ConfigType: &v3listenerpb.Filter_TypedConfig{
512									TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
513										HttpFilters: []*v3httppb.HttpFilter{
514											validServerSideHTTPFilter1,
515										},
516									}),
517								},
518							},
519						},
520					},
521				},
522				DefaultFilterChain: &v3listenerpb.FilterChain{
523					Filters: []*v3listenerpb.Filter{
524						{
525							Name: "hcm",
526							ConfigType: &v3listenerpb.Filter_TypedConfig{
527								TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
528									HttpFilters: []*v3httppb.HttpFilter{
529										validServerSideHTTPFilter1,
530									},
531								}),
532							},
533						},
534					},
535				},
536			},
537			wantFC: &FilterChainManager{
538				dstPrefixMap: map[string]*destPrefixEntry{
539					unspecifiedPrefixMapKey: {
540						srcTypeArr: [3]*sourcePrefixes{
541							{
542								srcPrefixMap: map[string]*sourcePrefixEntry{
543									unspecifiedPrefixMapKey: {
544										srcPortMap: map[int]*FilterChain{
545											0: {HTTPFilters: []HTTPFilter{
546												{
547													Name:   "serverOnlyCustomFilter",
548													Filter: serverOnlyHTTPFilter{},
549													Config: filterConfig{Cfg: serverOnlyCustomFilterConfig},
550												},
551											}},
552										},
553									},
554								},
555							},
556						},
557					},
558				},
559				def: &FilterChain{HTTPFilters: []HTTPFilter{
560					{
561						Name:   "serverOnlyCustomFilter",
562						Filter: serverOnlyHTTPFilter{},
563						Config: filterConfig{Cfg: serverOnlyCustomFilterConfig},
564					},
565				}},
566			},
567		},
568		{
569			name: "two valid http filters",
570			lis: &v3listenerpb.Listener{
571				FilterChains: []*v3listenerpb.FilterChain{
572					{
573						Name: "filter-chain-1",
574						Filters: []*v3listenerpb.Filter{
575							{
576								Name: "hcm",
577								ConfigType: &v3listenerpb.Filter_TypedConfig{
578									TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
579										HttpFilters: []*v3httppb.HttpFilter{
580											validServerSideHTTPFilter1,
581											validServerSideHTTPFilter2,
582										},
583									}),
584								},
585							},
586						},
587					},
588				},
589				DefaultFilterChain: &v3listenerpb.FilterChain{
590					Filters: []*v3listenerpb.Filter{
591						{
592							Name: "hcm",
593							ConfigType: &v3listenerpb.Filter_TypedConfig{
594								TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
595									HttpFilters: []*v3httppb.HttpFilter{
596										validServerSideHTTPFilter1,
597										validServerSideHTTPFilter2,
598									},
599								}),
600							},
601						},
602					},
603				},
604			},
605			wantFC: &FilterChainManager{
606				dstPrefixMap: map[string]*destPrefixEntry{
607					unspecifiedPrefixMapKey: {
608						srcTypeArr: [3]*sourcePrefixes{
609							{
610								srcPrefixMap: map[string]*sourcePrefixEntry{
611									unspecifiedPrefixMapKey: {
612										srcPortMap: map[int]*FilterChain{
613											0: {HTTPFilters: []HTTPFilter{
614												{
615													Name:   "serverOnlyCustomFilter",
616													Filter: serverOnlyHTTPFilter{},
617													Config: filterConfig{Cfg: serverOnlyCustomFilterConfig},
618												},
619												{
620													Name:   "serverOnlyCustomFilter2",
621													Filter: serverOnlyHTTPFilter{},
622													Config: filterConfig{Cfg: serverOnlyCustomFilterConfig},
623												},
624											}},
625										},
626									},
627								},
628							},
629						},
630					},
631				},
632				def: &FilterChain{HTTPFilters: []HTTPFilter{
633					{
634						Name:   "serverOnlyCustomFilter",
635						Filter: serverOnlyHTTPFilter{},
636						Config: filterConfig{Cfg: serverOnlyCustomFilterConfig},
637					},
638					{
639						Name:   "serverOnlyCustomFilter2",
640						Filter: serverOnlyHTTPFilter{},
641						Config: filterConfig{Cfg: serverOnlyCustomFilterConfig},
642					},
643				},
644				},
645			},
646		},
647		// In the case of two HTTP Connection Manager's being present, the
648		// second HTTP Connection Manager should be validated, but ignored.
649		{
650			name: "two hcms",
651			lis: &v3listenerpb.Listener{
652				FilterChains: []*v3listenerpb.FilterChain{
653					{
654						Name: "filter-chain-1",
655						Filters: []*v3listenerpb.Filter{
656							{
657								Name: "hcm",
658								ConfigType: &v3listenerpb.Filter_TypedConfig{
659									TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
660										HttpFilters: []*v3httppb.HttpFilter{
661											validServerSideHTTPFilter1,
662											validServerSideHTTPFilter2,
663										},
664									}),
665								},
666							},
667							{
668								Name: "hcm2",
669								ConfigType: &v3listenerpb.Filter_TypedConfig{
670									TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
671										HttpFilters: []*v3httppb.HttpFilter{
672											validServerSideHTTPFilter1,
673										},
674									}),
675								},
676							},
677						},
678					},
679				},
680				DefaultFilterChain: &v3listenerpb.FilterChain{
681					Filters: []*v3listenerpb.Filter{
682						{
683							Name: "hcm",
684							ConfigType: &v3listenerpb.Filter_TypedConfig{
685								TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
686									HttpFilters: []*v3httppb.HttpFilter{
687										validServerSideHTTPFilter1,
688										validServerSideHTTPFilter2,
689									},
690								}),
691							},
692						},
693						{
694							Name: "hcm2",
695							ConfigType: &v3listenerpb.Filter_TypedConfig{
696								TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
697									HttpFilters: []*v3httppb.HttpFilter{
698										validServerSideHTTPFilter1,
699									},
700								}),
701							},
702						},
703					},
704				},
705			},
706			wantFC: &FilterChainManager{
707				dstPrefixMap: map[string]*destPrefixEntry{
708					unspecifiedPrefixMapKey: {
709						srcTypeArr: [3]*sourcePrefixes{
710							{
711								srcPrefixMap: map[string]*sourcePrefixEntry{
712									unspecifiedPrefixMapKey: {
713										srcPortMap: map[int]*FilterChain{
714											0: {HTTPFilters: []HTTPFilter{
715												{
716													Name:   "serverOnlyCustomFilter",
717													Filter: serverOnlyHTTPFilter{},
718													Config: filterConfig{Cfg: serverOnlyCustomFilterConfig},
719												},
720												{
721													Name:   "serverOnlyCustomFilter2",
722													Filter: serverOnlyHTTPFilter{},
723													Config: filterConfig{Cfg: serverOnlyCustomFilterConfig},
724												},
725											}},
726										},
727									},
728								},
729							},
730						},
731					},
732				},
733				def: &FilterChain{HTTPFilters: []HTTPFilter{
734					{
735						Name:   "serverOnlyCustomFilter",
736						Filter: serverOnlyHTTPFilter{},
737						Config: filterConfig{Cfg: serverOnlyCustomFilterConfig},
738					},
739					{
740						Name:   "serverOnlyCustomFilter2",
741						Filter: serverOnlyHTTPFilter{},
742						Config: filterConfig{Cfg: serverOnlyCustomFilterConfig},
743					},
744				},
745				},
746			},
747		},
748	}
749
750	for _, test := range tests {
751		t.Run(test.name, func(t *testing.T) {
752			gotFC, err := NewFilterChainManager(test.lis)
753			if err != nil {
754				t.Fatalf("NewFilterChainManager() returned err: %v, wantErr: nil", err)
755			}
756			if !cmp.Equal(gotFC, test.wantFC, cmp.AllowUnexported(FilterChainManager{}, destPrefixEntry{}, sourcePrefixes{}, sourcePrefixEntry{}), cmpOpts) {
757				t.Fatalf("NewFilterChainManager() returned %+v, want: %+v", gotFC, test.wantFC)
758			}
759		})
760	}
761}
762
763// TestNewFilterChainImpl_Success_SecurityConfig verifies cases where the
764// security configuration in the filter chain contains valid data.
765func TestNewFilterChainImpl_Success_SecurityConfig(t *testing.T) {
766	tests := []struct {
767		desc   string
768		lis    *v3listenerpb.Listener
769		wantFC *FilterChainManager
770	}{
771		{
772			desc: "empty transport socket",
773			lis: &v3listenerpb.Listener{
774				FilterChains: []*v3listenerpb.FilterChain{
775					{
776						Name:    "filter-chain-1",
777						Filters: emptyValidNetworkFilters,
778					},
779				},
780				DefaultFilterChain: &v3listenerpb.FilterChain{
781					Filters: emptyValidNetworkFilters,
782				},
783			},
784			wantFC: &FilterChainManager{
785				dstPrefixMap: map[string]*destPrefixEntry{
786					unspecifiedPrefixMapKey: {
787						srcTypeArr: [3]*sourcePrefixes{
788							{
789								srcPrefixMap: map[string]*sourcePrefixEntry{
790									unspecifiedPrefixMapKey: {
791										srcPortMap: map[int]*FilterChain{
792											0: {},
793										},
794									},
795								},
796							},
797						},
798					},
799				},
800				def: &FilterChain{},
801			},
802		},
803		{
804			desc: "no validation context",
805			lis: &v3listenerpb.Listener{
806				FilterChains: []*v3listenerpb.FilterChain{
807					{
808						TransportSocket: &v3corepb.TransportSocket{
809							Name: "envoy.transport_sockets.tls",
810							ConfigType: &v3corepb.TransportSocket_TypedConfig{
811								TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
812									CommonTlsContext: &v3tlspb.CommonTlsContext{
813										TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
814											InstanceName:    "identityPluginInstance",
815											CertificateName: "identityCertName",
816										},
817									},
818								}),
819							},
820						},
821						Filters: emptyValidNetworkFilters,
822					},
823				},
824				DefaultFilterChain: &v3listenerpb.FilterChain{
825					TransportSocket: &v3corepb.TransportSocket{
826						Name: "envoy.transport_sockets.tls",
827						ConfigType: &v3corepb.TransportSocket_TypedConfig{
828							TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
829								CommonTlsContext: &v3tlspb.CommonTlsContext{
830									TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
831										InstanceName:    "defaultIdentityPluginInstance",
832										CertificateName: "defaultIdentityCertName",
833									},
834								},
835							}),
836						},
837					},
838					Filters: emptyValidNetworkFilters,
839				},
840			},
841			wantFC: &FilterChainManager{
842				dstPrefixMap: map[string]*destPrefixEntry{
843					unspecifiedPrefixMapKey: {
844						srcTypeArr: [3]*sourcePrefixes{
845							{
846								srcPrefixMap: map[string]*sourcePrefixEntry{
847									unspecifiedPrefixMapKey: {
848										srcPortMap: map[int]*FilterChain{
849											0: {
850												SecurityCfg: &SecurityConfig{
851													IdentityInstanceName: "identityPluginInstance",
852													IdentityCertName:     "identityCertName",
853												},
854											},
855										},
856									},
857								},
858							},
859						},
860					},
861				},
862				def: &FilterChain{
863					SecurityCfg: &SecurityConfig{
864						IdentityInstanceName: "defaultIdentityPluginInstance",
865						IdentityCertName:     "defaultIdentityCertName",
866					},
867				},
868			},
869		},
870		{
871			desc: "validation context with certificate provider",
872			lis: &v3listenerpb.Listener{
873				FilterChains: []*v3listenerpb.FilterChain{
874					{
875						TransportSocket: &v3corepb.TransportSocket{
876							Name: "envoy.transport_sockets.tls",
877							ConfigType: &v3corepb.TransportSocket_TypedConfig{
878								TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
879									RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
880									CommonTlsContext: &v3tlspb.CommonTlsContext{
881										TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
882											InstanceName:    "identityPluginInstance",
883											CertificateName: "identityCertName",
884										},
885										ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{
886											ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
887												InstanceName:    "rootPluginInstance",
888												CertificateName: "rootCertName",
889											},
890										},
891									},
892								}),
893							},
894						},
895						Filters: emptyValidNetworkFilters,
896					},
897				},
898				DefaultFilterChain: &v3listenerpb.FilterChain{
899					Name: "default-filter-chain-1",
900					TransportSocket: &v3corepb.TransportSocket{
901						Name: "envoy.transport_sockets.tls",
902						ConfigType: &v3corepb.TransportSocket_TypedConfig{
903							TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
904								RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
905								CommonTlsContext: &v3tlspb.CommonTlsContext{
906									TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
907										InstanceName:    "defaultIdentityPluginInstance",
908										CertificateName: "defaultIdentityCertName",
909									},
910									ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{
911										ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
912											InstanceName:    "defaultRootPluginInstance",
913											CertificateName: "defaultRootCertName",
914										},
915									},
916								},
917							}),
918						},
919					},
920					Filters: emptyValidNetworkFilters,
921				},
922			},
923			wantFC: &FilterChainManager{
924				dstPrefixMap: map[string]*destPrefixEntry{
925					unspecifiedPrefixMapKey: {
926						srcTypeArr: [3]*sourcePrefixes{
927							{
928								srcPrefixMap: map[string]*sourcePrefixEntry{
929									unspecifiedPrefixMapKey: {
930										srcPortMap: map[int]*FilterChain{
931											0: {
932												SecurityCfg: &SecurityConfig{
933													RootInstanceName:     "rootPluginInstance",
934													RootCertName:         "rootCertName",
935													IdentityInstanceName: "identityPluginInstance",
936													IdentityCertName:     "identityCertName",
937													RequireClientCert:    true,
938												},
939											},
940										},
941									},
942								},
943							},
944						},
945					},
946				},
947				def: &FilterChain{
948					SecurityCfg: &SecurityConfig{
949						RootInstanceName:     "defaultRootPluginInstance",
950						RootCertName:         "defaultRootCertName",
951						IdentityInstanceName: "defaultIdentityPluginInstance",
952						IdentityCertName:     "defaultIdentityCertName",
953						RequireClientCert:    true,
954					},
955				},
956			},
957		},
958	}
959
960	for _, test := range tests {
961		t.Run(test.desc, func(t *testing.T) {
962			gotFC, err := NewFilterChainManager(test.lis)
963			if err != nil {
964				t.Fatalf("NewFilterChainManager() returned err: %v, wantErr: nil", err)
965			}
966			if !cmp.Equal(gotFC, test.wantFC, cmp.AllowUnexported(FilterChainManager{}, destPrefixEntry{}, sourcePrefixes{}, sourcePrefixEntry{}), cmpopts.EquateEmpty()) {
967				t.Fatalf("NewFilterChainManager() returned %+v, want: %+v", gotFC, test.wantFC)
968			}
969		})
970	}
971}
972
973// TestNewFilterChainImpl_Success_UnsupportedMatchFields verifies cases where
974// there are multiple filter chains, and one of them is valid while the other
975// contains unsupported match fields. These configurations should lead to
976// success at config validation time and the filter chains which contains
977// unsupported match fields will be skipped at lookup time.
978func TestNewFilterChainImpl_Success_UnsupportedMatchFields(t *testing.T) {
979	unspecifiedEntry := &destPrefixEntry{
980		srcTypeArr: [3]*sourcePrefixes{
981			{
982				srcPrefixMap: map[string]*sourcePrefixEntry{
983					unspecifiedPrefixMapKey: {
984						srcPortMap: map[int]*FilterChain{
985							0: {},
986						},
987					},
988				},
989			},
990		},
991	}
992
993	tests := []struct {
994		desc   string
995		lis    *v3listenerpb.Listener
996		wantFC *FilterChainManager
997	}{
998		{
999			desc: "unsupported destination port",
1000			lis: &v3listenerpb.Listener{
1001				FilterChains: []*v3listenerpb.FilterChain{
1002					{
1003						Name:    "good-chain",
1004						Filters: emptyValidNetworkFilters,
1005					},
1006					{
1007						Name: "unsupported-destination-port",
1008						FilterChainMatch: &v3listenerpb.FilterChainMatch{
1009							PrefixRanges:    []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.1.1", 16)},
1010							DestinationPort: &wrapperspb.UInt32Value{Value: 666},
1011						},
1012						Filters: emptyValidNetworkFilters,
1013					},
1014				},
1015				DefaultFilterChain: &v3listenerpb.FilterChain{Filters: emptyValidNetworkFilters},
1016			},
1017			wantFC: &FilterChainManager{
1018				dstPrefixMap: map[string]*destPrefixEntry{
1019					unspecifiedPrefixMapKey: unspecifiedEntry,
1020				},
1021				def: &FilterChain{},
1022			},
1023		},
1024		{
1025			desc: "unsupported server names",
1026			lis: &v3listenerpb.Listener{
1027				FilterChains: []*v3listenerpb.FilterChain{
1028					{
1029						Name:    "good-chain",
1030						Filters: emptyValidNetworkFilters,
1031					},
1032					{
1033						Name: "unsupported-server-names",
1034						FilterChainMatch: &v3listenerpb.FilterChainMatch{
1035							PrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.1.1", 16)},
1036							ServerNames:  []string{"example-server"},
1037						},
1038						Filters: emptyValidNetworkFilters,
1039					},
1040				},
1041				DefaultFilterChain: &v3listenerpb.FilterChain{Filters: emptyValidNetworkFilters},
1042			},
1043			wantFC: &FilterChainManager{
1044				dstPrefixMap: map[string]*destPrefixEntry{
1045					unspecifiedPrefixMapKey: unspecifiedEntry,
1046					"192.168.0.0/16": {
1047						net: ipNetFromCIDR("192.168.2.2/16"),
1048					},
1049				},
1050				def: &FilterChain{},
1051			},
1052		},
1053		{
1054			desc: "unsupported transport protocol",
1055			lis: &v3listenerpb.Listener{
1056				FilterChains: []*v3listenerpb.FilterChain{
1057					{
1058						Name:    "good-chain",
1059						Filters: emptyValidNetworkFilters,
1060					},
1061					{
1062						Name: "unsupported-transport-protocol",
1063						FilterChainMatch: &v3listenerpb.FilterChainMatch{
1064							PrefixRanges:      []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.1.1", 16)},
1065							TransportProtocol: "tls",
1066						},
1067						Filters: emptyValidNetworkFilters,
1068					},
1069				},
1070				DefaultFilterChain: &v3listenerpb.FilterChain{Filters: emptyValidNetworkFilters},
1071			},
1072			wantFC: &FilterChainManager{
1073				dstPrefixMap: map[string]*destPrefixEntry{
1074					unspecifiedPrefixMapKey: unspecifiedEntry,
1075					"192.168.0.0/16": {
1076						net: ipNetFromCIDR("192.168.2.2/16"),
1077					},
1078				},
1079				def: &FilterChain{},
1080			},
1081		},
1082		{
1083			desc: "unsupported application protocol",
1084			lis: &v3listenerpb.Listener{
1085				FilterChains: []*v3listenerpb.FilterChain{
1086					{
1087						Name:    "good-chain",
1088						Filters: emptyValidNetworkFilters,
1089					},
1090					{
1091						Name: "unsupported-application-protocol",
1092						FilterChainMatch: &v3listenerpb.FilterChainMatch{
1093							PrefixRanges:         []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.1.1", 16)},
1094							ApplicationProtocols: []string{"h2"},
1095						},
1096						Filters: emptyValidNetworkFilters,
1097					},
1098				},
1099				DefaultFilterChain: &v3listenerpb.FilterChain{Filters: emptyValidNetworkFilters},
1100			},
1101			wantFC: &FilterChainManager{
1102				dstPrefixMap: map[string]*destPrefixEntry{
1103					unspecifiedPrefixMapKey: unspecifiedEntry,
1104					"192.168.0.0/16": {
1105						net: ipNetFromCIDR("192.168.2.2/16"),
1106					},
1107				},
1108				def: &FilterChain{},
1109			},
1110		},
1111	}
1112
1113	for _, test := range tests {
1114		t.Run(test.desc, func(t *testing.T) {
1115			gotFC, err := NewFilterChainManager(test.lis)
1116			if err != nil {
1117				t.Fatalf("NewFilterChainManager() returned err: %v, wantErr: nil", err)
1118			}
1119			if !cmp.Equal(gotFC, test.wantFC, cmp.AllowUnexported(FilterChainManager{}, destPrefixEntry{}, sourcePrefixes{}, sourcePrefixEntry{}), cmpopts.EquateEmpty()) {
1120				t.Fatalf("NewFilterChainManager() returned %+v, want: %+v", gotFC, test.wantFC)
1121			}
1122		})
1123	}
1124}
1125
1126// TestNewFilterChainImpl_Success_AllCombinations verifies different
1127// combinations of the supported match criteria.
1128func TestNewFilterChainImpl_Success_AllCombinations(t *testing.T) {
1129	tests := []struct {
1130		desc   string
1131		lis    *v3listenerpb.Listener
1132		wantFC *FilterChainManager
1133	}{
1134		{
1135			desc: "multiple destination prefixes",
1136			lis: &v3listenerpb.Listener{
1137				FilterChains: []*v3listenerpb.FilterChain{
1138					{
1139						// Unspecified destination prefix.
1140						FilterChainMatch: &v3listenerpb.FilterChainMatch{},
1141						Filters:          emptyValidNetworkFilters,
1142					},
1143					{
1144						// v4 wildcard destination prefix.
1145						FilterChainMatch: &v3listenerpb.FilterChainMatch{
1146							PrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("0.0.0.0", 0)},
1147							SourceType:   v3listenerpb.FilterChainMatch_EXTERNAL,
1148						},
1149						Filters: emptyValidNetworkFilters,
1150					},
1151					{
1152						// v6 wildcard destination prefix.
1153						FilterChainMatch: &v3listenerpb.FilterChainMatch{
1154							PrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("::", 0)},
1155							SourceType:   v3listenerpb.FilterChainMatch_EXTERNAL,
1156						},
1157						Filters: emptyValidNetworkFilters,
1158					},
1159					{
1160						FilterChainMatch: &v3listenerpb.FilterChainMatch{PrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.1.1", 16)}},
1161						Filters:          emptyValidNetworkFilters,
1162					},
1163					{
1164						FilterChainMatch: &v3listenerpb.FilterChainMatch{PrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("10.0.0.0", 8)}},
1165						Filters:          emptyValidNetworkFilters,
1166					},
1167				},
1168				DefaultFilterChain: &v3listenerpb.FilterChain{Filters: emptyValidNetworkFilters},
1169			},
1170			wantFC: &FilterChainManager{
1171				dstPrefixMap: map[string]*destPrefixEntry{
1172					unspecifiedPrefixMapKey: {
1173						srcTypeArr: [3]*sourcePrefixes{
1174							{
1175								srcPrefixMap: map[string]*sourcePrefixEntry{
1176									unspecifiedPrefixMapKey: {
1177										srcPortMap: map[int]*FilterChain{
1178											0: {},
1179										},
1180									},
1181								},
1182							},
1183						},
1184					},
1185					"0.0.0.0/0": {
1186						net: ipNetFromCIDR("0.0.0.0/0"),
1187						srcTypeArr: [3]*sourcePrefixes{
1188							nil,
1189							nil,
1190							{
1191								srcPrefixMap: map[string]*sourcePrefixEntry{
1192									unspecifiedPrefixMapKey: {
1193										srcPortMap: map[int]*FilterChain{
1194											0: {},
1195										},
1196									},
1197								},
1198							},
1199						},
1200					},
1201					"::/0": {
1202						net: ipNetFromCIDR("::/0"),
1203						srcTypeArr: [3]*sourcePrefixes{
1204							nil,
1205							nil,
1206							{
1207								srcPrefixMap: map[string]*sourcePrefixEntry{
1208									unspecifiedPrefixMapKey: {
1209										srcPortMap: map[int]*FilterChain{
1210											0: {},
1211										},
1212									},
1213								},
1214							},
1215						},
1216					},
1217					"192.168.0.0/16": {
1218						net: ipNetFromCIDR("192.168.2.2/16"),
1219						srcTypeArr: [3]*sourcePrefixes{
1220							{
1221								srcPrefixMap: map[string]*sourcePrefixEntry{
1222									unspecifiedPrefixMapKey: {
1223										srcPortMap: map[int]*FilterChain{
1224											0: {},
1225										},
1226									},
1227								},
1228							},
1229						},
1230					},
1231					"10.0.0.0/8": {
1232						net: ipNetFromCIDR("10.0.0.0/8"),
1233						srcTypeArr: [3]*sourcePrefixes{
1234							{
1235								srcPrefixMap: map[string]*sourcePrefixEntry{
1236									unspecifiedPrefixMapKey: {
1237										srcPortMap: map[int]*FilterChain{
1238											0: {},
1239										},
1240									},
1241								},
1242							},
1243						},
1244					},
1245				},
1246				def: &FilterChain{},
1247			},
1248		},
1249		{
1250			desc: "multiple source types",
1251			lis: &v3listenerpb.Listener{
1252				FilterChains: []*v3listenerpb.FilterChain{
1253					{
1254						FilterChainMatch: &v3listenerpb.FilterChainMatch{SourceType: v3listenerpb.FilterChainMatch_SAME_IP_OR_LOOPBACK},
1255						Filters:          emptyValidNetworkFilters,
1256					},
1257					{
1258						FilterChainMatch: &v3listenerpb.FilterChainMatch{
1259							PrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.1.1", 16)},
1260							SourceType:   v3listenerpb.FilterChainMatch_EXTERNAL,
1261						},
1262						Filters: emptyValidNetworkFilters,
1263					},
1264				},
1265				DefaultFilterChain: &v3listenerpb.FilterChain{Filters: emptyValidNetworkFilters},
1266			},
1267			wantFC: &FilterChainManager{
1268				dstPrefixMap: map[string]*destPrefixEntry{
1269					unspecifiedPrefixMapKey: {
1270						srcTypeArr: [3]*sourcePrefixes{
1271							nil,
1272							{
1273								srcPrefixMap: map[string]*sourcePrefixEntry{
1274									unspecifiedPrefixMapKey: {
1275										srcPortMap: map[int]*FilterChain{
1276											0: {},
1277										},
1278									},
1279								},
1280							},
1281						},
1282					},
1283					"192.168.0.0/16": {
1284						net: ipNetFromCIDR("192.168.2.2/16"),
1285						srcTypeArr: [3]*sourcePrefixes{
1286							nil,
1287							nil,
1288							{
1289								srcPrefixMap: map[string]*sourcePrefixEntry{
1290									unspecifiedPrefixMapKey: {
1291										srcPortMap: map[int]*FilterChain{
1292											0: {},
1293										},
1294									},
1295								},
1296							},
1297						},
1298					},
1299				},
1300				def: &FilterChain{},
1301			},
1302		},
1303		{
1304			desc: "multiple source prefixes",
1305			lis: &v3listenerpb.Listener{
1306				FilterChains: []*v3listenerpb.FilterChain{
1307					{
1308						FilterChainMatch: &v3listenerpb.FilterChainMatch{SourcePrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("10.0.0.0", 8)}},
1309						Filters:          emptyValidNetworkFilters,
1310					},
1311					{
1312						FilterChainMatch: &v3listenerpb.FilterChainMatch{
1313							PrefixRanges:       []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.1.1", 16)},
1314							SourcePrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.1.1", 16)},
1315						},
1316						Filters: emptyValidNetworkFilters,
1317					},
1318				},
1319				DefaultFilterChain: &v3listenerpb.FilterChain{Filters: emptyValidNetworkFilters},
1320			},
1321			wantFC: &FilterChainManager{
1322				dstPrefixMap: map[string]*destPrefixEntry{
1323					unspecifiedPrefixMapKey: {
1324						srcTypeArr: [3]*sourcePrefixes{
1325							{
1326								srcPrefixMap: map[string]*sourcePrefixEntry{
1327									"10.0.0.0/8": {
1328										net: ipNetFromCIDR("10.0.0.0/8"),
1329										srcPortMap: map[int]*FilterChain{
1330											0: {},
1331										},
1332									},
1333								},
1334							},
1335						},
1336					},
1337					"192.168.0.0/16": {
1338						net: ipNetFromCIDR("192.168.2.2/16"),
1339						srcTypeArr: [3]*sourcePrefixes{
1340							{
1341								srcPrefixMap: map[string]*sourcePrefixEntry{
1342									"192.168.0.0/16": {
1343										net: ipNetFromCIDR("192.168.0.0/16"),
1344										srcPortMap: map[int]*FilterChain{
1345											0: {},
1346										},
1347									},
1348								},
1349							},
1350						},
1351					},
1352				},
1353				def: &FilterChain{},
1354			},
1355		},
1356		{
1357			desc: "multiple source ports",
1358			lis: &v3listenerpb.Listener{
1359				FilterChains: []*v3listenerpb.FilterChain{
1360					{
1361						FilterChainMatch: &v3listenerpb.FilterChainMatch{SourcePorts: []uint32{1, 2, 3}},
1362						Filters:          emptyValidNetworkFilters,
1363					},
1364					{
1365						FilterChainMatch: &v3listenerpb.FilterChainMatch{
1366							PrefixRanges:       []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.1.1", 16)},
1367							SourcePrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.1.1", 16)},
1368							SourceType:         v3listenerpb.FilterChainMatch_EXTERNAL,
1369							SourcePorts:        []uint32{1, 2, 3},
1370						},
1371						Filters: emptyValidNetworkFilters,
1372					},
1373				},
1374				DefaultFilterChain: &v3listenerpb.FilterChain{Filters: emptyValidNetworkFilters},
1375			},
1376			wantFC: &FilterChainManager{
1377				dstPrefixMap: map[string]*destPrefixEntry{
1378					unspecifiedPrefixMapKey: {
1379						srcTypeArr: [3]*sourcePrefixes{
1380							{
1381								srcPrefixMap: map[string]*sourcePrefixEntry{
1382									unspecifiedPrefixMapKey: {
1383										srcPortMap: map[int]*FilterChain{
1384											1: {},
1385											2: {},
1386											3: {},
1387										},
1388									},
1389								},
1390							},
1391						},
1392					},
1393					"192.168.0.0/16": {
1394						net: ipNetFromCIDR("192.168.2.2/16"),
1395						srcTypeArr: [3]*sourcePrefixes{
1396							nil,
1397							nil,
1398							{
1399								srcPrefixMap: map[string]*sourcePrefixEntry{
1400									"192.168.0.0/16": {
1401										net: ipNetFromCIDR("192.168.0.0/16"),
1402										srcPortMap: map[int]*FilterChain{
1403											1: {},
1404											2: {},
1405											3: {},
1406										},
1407									},
1408								},
1409							},
1410						},
1411					},
1412				},
1413				def: &FilterChain{},
1414			},
1415		},
1416		{
1417			desc: "some chains have unsupported fields",
1418			lis: &v3listenerpb.Listener{
1419				FilterChains: []*v3listenerpb.FilterChain{
1420					{
1421						FilterChainMatch: &v3listenerpb.FilterChainMatch{},
1422						Filters:          emptyValidNetworkFilters,
1423					},
1424					{
1425						FilterChainMatch: &v3listenerpb.FilterChainMatch{PrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.1.1", 16)}},
1426						Filters:          emptyValidNetworkFilters,
1427					},
1428					{
1429						FilterChainMatch: &v3listenerpb.FilterChainMatch{
1430							PrefixRanges:      []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("10.0.0.0", 8)},
1431							TransportProtocol: "raw_buffer",
1432						},
1433						Filters: emptyValidNetworkFilters,
1434					},
1435					{
1436						// This chain will be dropped in favor of the above
1437						// filter chain because they both have the same
1438						// destination prefix, but this one has an empty
1439						// transport protocol while the above chain has the more
1440						// preferred "raw_buffer".
1441						FilterChainMatch: &v3listenerpb.FilterChainMatch{
1442							PrefixRanges:       []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("10.0.0.0", 8)},
1443							TransportProtocol:  "",
1444							SourceType:         v3listenerpb.FilterChainMatch_EXTERNAL,
1445							SourcePrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("10.0.0.0", 16)},
1446						},
1447						Filters: emptyValidNetworkFilters,
1448					},
1449					{
1450						// This chain will be dropped for unsupported server
1451						// names.
1452						FilterChainMatch: &v3listenerpb.FilterChainMatch{
1453							PrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.100.1", 32)},
1454							ServerNames:  []string{"foo", "bar"},
1455						},
1456						Filters: emptyValidNetworkFilters,
1457					},
1458					{
1459						// This chain will be dropped for unsupported transport
1460						// protocol.
1461						FilterChainMatch: &v3listenerpb.FilterChainMatch{
1462							PrefixRanges:      []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.100.2", 32)},
1463							TransportProtocol: "not-raw-buffer",
1464						},
1465						Filters: emptyValidNetworkFilters,
1466					},
1467					{
1468						// This chain will be dropped for unsupported
1469						// application protocol.
1470						FilterChainMatch: &v3listenerpb.FilterChainMatch{
1471							PrefixRanges:         []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.100.3", 32)},
1472							ApplicationProtocols: []string{"h2"},
1473						},
1474						Filters: emptyValidNetworkFilters,
1475					},
1476				},
1477				DefaultFilterChain: &v3listenerpb.FilterChain{Filters: emptyValidNetworkFilters},
1478			},
1479			wantFC: &FilterChainManager{
1480				dstPrefixMap: map[string]*destPrefixEntry{
1481					unspecifiedPrefixMapKey: {
1482						srcTypeArr: [3]*sourcePrefixes{
1483							{
1484								srcPrefixMap: map[string]*sourcePrefixEntry{
1485									unspecifiedPrefixMapKey: {
1486										srcPortMap: map[int]*FilterChain{
1487											0: {},
1488										},
1489									},
1490								},
1491							},
1492						},
1493					},
1494					"192.168.0.0/16": {
1495						net: ipNetFromCIDR("192.168.2.2/16"),
1496						srcTypeArr: [3]*sourcePrefixes{
1497							{
1498								srcPrefixMap: map[string]*sourcePrefixEntry{
1499									unspecifiedPrefixMapKey: {
1500										srcPortMap: map[int]*FilterChain{
1501											0: {},
1502										},
1503									},
1504								},
1505							},
1506						},
1507					},
1508					"10.0.0.0/8": {
1509						net: ipNetFromCIDR("10.0.0.0/8"),
1510						srcTypeArr: [3]*sourcePrefixes{
1511							{
1512								srcPrefixMap: map[string]*sourcePrefixEntry{
1513									unspecifiedPrefixMapKey: {
1514										srcPortMap: map[int]*FilterChain{
1515											0: {},
1516										},
1517									},
1518								},
1519							},
1520						},
1521					},
1522					"192.168.100.1/32": {
1523						net:        ipNetFromCIDR("192.168.100.1/32"),
1524						srcTypeArr: [3]*sourcePrefixes{},
1525					},
1526					"192.168.100.2/32": {
1527						net:        ipNetFromCIDR("192.168.100.2/32"),
1528						srcTypeArr: [3]*sourcePrefixes{},
1529					},
1530					"192.168.100.3/32": {
1531						net:        ipNetFromCIDR("192.168.100.3/32"),
1532						srcTypeArr: [3]*sourcePrefixes{},
1533					},
1534				},
1535				def: &FilterChain{},
1536			},
1537		},
1538	}
1539
1540	for _, test := range tests {
1541		t.Run(test.desc, func(t *testing.T) {
1542			gotFC, err := NewFilterChainManager(test.lis)
1543			if err != nil {
1544				t.Fatalf("NewFilterChainManager() returned err: %v, wantErr: nil", err)
1545			}
1546			if !cmp.Equal(gotFC, test.wantFC, cmp.AllowUnexported(FilterChainManager{}, destPrefixEntry{}, sourcePrefixes{}, sourcePrefixEntry{})) {
1547				t.Fatalf("NewFilterChainManager() returned %+v, want: %+v", gotFC, test.wantFC)
1548			}
1549		})
1550	}
1551}
1552
1553func TestLookup_Failures(t *testing.T) {
1554	tests := []struct {
1555		desc    string
1556		lis     *v3listenerpb.Listener
1557		params  FilterChainLookupParams
1558		wantErr string
1559	}{
1560		{
1561			desc: "no destination prefix match",
1562			lis: &v3listenerpb.Listener{
1563				FilterChains: []*v3listenerpb.FilterChain{
1564					{
1565						FilterChainMatch: &v3listenerpb.FilterChainMatch{PrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.1.1", 16)}},
1566						Filters:          emptyValidNetworkFilters,
1567					},
1568				},
1569			},
1570			params: FilterChainLookupParams{
1571				IsUnspecifiedListener: true,
1572				DestAddr:              net.IPv4(10, 1, 1, 1),
1573			},
1574			wantErr: "no matching filter chain based on destination prefix match",
1575		},
1576		{
1577			desc: "no source type match",
1578			lis: &v3listenerpb.Listener{
1579				FilterChains: []*v3listenerpb.FilterChain{
1580					{
1581						FilterChainMatch: &v3listenerpb.FilterChainMatch{
1582							PrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.1.1", 16)},
1583							SourceType:   v3listenerpb.FilterChainMatch_SAME_IP_OR_LOOPBACK,
1584						},
1585						Filters: emptyValidNetworkFilters,
1586					},
1587				},
1588			},
1589			params: FilterChainLookupParams{
1590				IsUnspecifiedListener: true,
1591				DestAddr:              net.IPv4(192, 168, 100, 1),
1592				SourceAddr:            net.IPv4(192, 168, 100, 2),
1593			},
1594			wantErr: "no matching filter chain based on source type match",
1595		},
1596		{
1597			desc: "no source prefix match",
1598			lis: &v3listenerpb.Listener{
1599				FilterChains: []*v3listenerpb.FilterChain{
1600					{
1601						FilterChainMatch: &v3listenerpb.FilterChainMatch{
1602							SourcePrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.1.1", 24)},
1603							SourceType:         v3listenerpb.FilterChainMatch_SAME_IP_OR_LOOPBACK,
1604						},
1605						Filters: emptyValidNetworkFilters,
1606					},
1607				},
1608			},
1609			params: FilterChainLookupParams{
1610				IsUnspecifiedListener: true,
1611				DestAddr:              net.IPv4(192, 168, 100, 1),
1612				SourceAddr:            net.IPv4(192, 168, 100, 1),
1613			},
1614			wantErr: "no matching filter chain after all match criteria",
1615		},
1616		{
1617			desc: "multiple matching filter chains",
1618			lis: &v3listenerpb.Listener{
1619				FilterChains: []*v3listenerpb.FilterChain{
1620					{
1621						FilterChainMatch: &v3listenerpb.FilterChainMatch{SourcePorts: []uint32{1, 2, 3}},
1622						Filters:          emptyValidNetworkFilters,
1623					},
1624					{
1625						FilterChainMatch: &v3listenerpb.FilterChainMatch{
1626							PrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.1.1", 16)},
1627							SourcePorts:  []uint32{1},
1628						},
1629						Filters: emptyValidNetworkFilters,
1630					},
1631				},
1632			},
1633			params: FilterChainLookupParams{
1634				// IsUnspecified is not set. This means that the destination
1635				// prefix matchers will be ignored.
1636				DestAddr:   net.IPv4(192, 168, 100, 1),
1637				SourceAddr: net.IPv4(192, 168, 100, 1),
1638				SourcePort: 1,
1639			},
1640			wantErr: "multiple matching filter chains",
1641		},
1642		{
1643			desc: "no default filter chain",
1644			lis: &v3listenerpb.Listener{
1645				FilterChains: []*v3listenerpb.FilterChain{
1646					{
1647						FilterChainMatch: &v3listenerpb.FilterChainMatch{SourcePorts: []uint32{1, 2, 3}},
1648						Filters:          emptyValidNetworkFilters,
1649					},
1650				},
1651			},
1652			params: FilterChainLookupParams{
1653				IsUnspecifiedListener: true,
1654				DestAddr:              net.IPv4(192, 168, 100, 1),
1655				SourceAddr:            net.IPv4(192, 168, 100, 1),
1656				SourcePort:            80,
1657			},
1658			wantErr: "no matching filter chain after all match criteria",
1659		},
1660		{
1661			desc: "most specific match dropped for unsupported field",
1662			lis: &v3listenerpb.Listener{
1663				FilterChains: []*v3listenerpb.FilterChain{
1664					{
1665						// This chain will be picked in the destination prefix
1666						// stage, but will be dropped at the server names stage.
1667						FilterChainMatch: &v3listenerpb.FilterChainMatch{
1668							PrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.100.1", 32)},
1669							ServerNames:  []string{"foo"},
1670						},
1671						Filters: emptyValidNetworkFilters,
1672					},
1673					{
1674						FilterChainMatch: &v3listenerpb.FilterChainMatch{
1675							PrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.100.0", 16)},
1676						},
1677						Filters: emptyValidNetworkFilters,
1678					},
1679				},
1680			},
1681			params: FilterChainLookupParams{
1682				IsUnspecifiedListener: true,
1683				DestAddr:              net.IPv4(192, 168, 100, 1),
1684				SourceAddr:            net.IPv4(192, 168, 100, 1),
1685				SourcePort:            80,
1686			},
1687			wantErr: "no matching filter chain based on source type match",
1688		},
1689	}
1690
1691	for _, test := range tests {
1692		t.Run(test.desc, func(t *testing.T) {
1693			fci, err := NewFilterChainManager(test.lis)
1694			if err != nil {
1695				t.Fatalf("NewFilterChainManager() failed: %v", err)
1696			}
1697			fc, err := fci.Lookup(test.params)
1698			if err == nil || !strings.Contains(err.Error(), test.wantErr) {
1699				t.Fatalf("FilterChainManager.Lookup(%v) = (%v, %v) want (nil, %s)", test.params, fc, err, test.wantErr)
1700			}
1701		})
1702	}
1703}
1704
1705func TestLookup_Successes(t *testing.T) {
1706	lisWithDefaultChain := &v3listenerpb.Listener{
1707		FilterChains: []*v3listenerpb.FilterChain{
1708			{
1709				FilterChainMatch: &v3listenerpb.FilterChainMatch{PrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.1.1", 16)}},
1710				TransportSocket: &v3corepb.TransportSocket{
1711					Name: "envoy.transport_sockets.tls",
1712					ConfigType: &v3corepb.TransportSocket_TypedConfig{
1713						TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
1714							CommonTlsContext: &v3tlspb.CommonTlsContext{
1715								TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{InstanceName: "instance1"},
1716							},
1717						}),
1718					},
1719				},
1720				Filters: emptyValidNetworkFilters,
1721			},
1722		},
1723		// A default filter chain with an empty transport socket.
1724		DefaultFilterChain: &v3listenerpb.FilterChain{
1725			TransportSocket: &v3corepb.TransportSocket{
1726				Name: "envoy.transport_sockets.tls",
1727				ConfigType: &v3corepb.TransportSocket_TypedConfig{
1728					TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
1729						CommonTlsContext: &v3tlspb.CommonTlsContext{
1730							TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{InstanceName: "default"},
1731						},
1732					}),
1733				},
1734			},
1735			Filters: emptyValidNetworkFilters,
1736		},
1737	}
1738	lisWithoutDefaultChain := &v3listenerpb.Listener{
1739		FilterChains: []*v3listenerpb.FilterChain{
1740			{
1741				TransportSocket: transportSocketWithInstanceName("unspecified-dest-and-source-prefix"),
1742				Filters:         emptyValidNetworkFilters,
1743			},
1744			{
1745				FilterChainMatch: &v3listenerpb.FilterChainMatch{
1746					PrefixRanges:       []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("0.0.0.0", 0)},
1747					SourcePrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("0.0.0.0", 0)},
1748				},
1749				TransportSocket: transportSocketWithInstanceName("wildcard-prefixes-v4"),
1750				Filters:         emptyValidNetworkFilters,
1751			},
1752			{
1753				FilterChainMatch: &v3listenerpb.FilterChainMatch{
1754					SourcePrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("::", 0)},
1755				},
1756				TransportSocket: transportSocketWithInstanceName("wildcard-source-prefix-v6"),
1757				Filters:         emptyValidNetworkFilters,
1758			},
1759			{
1760				FilterChainMatch: &v3listenerpb.FilterChainMatch{PrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.1.1", 16)}},
1761				TransportSocket:  transportSocketWithInstanceName("specific-destination-prefix-unspecified-source-type"),
1762				Filters:          emptyValidNetworkFilters,
1763			},
1764			{
1765				FilterChainMatch: &v3listenerpb.FilterChainMatch{
1766					PrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.1.1", 24)},
1767					SourceType:   v3listenerpb.FilterChainMatch_EXTERNAL,
1768				},
1769				TransportSocket: transportSocketWithInstanceName("specific-destination-prefix-specific-source-type"),
1770				Filters:         emptyValidNetworkFilters,
1771			},
1772			{
1773				FilterChainMatch: &v3listenerpb.FilterChainMatch{
1774					PrefixRanges:       []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.1.1", 24)},
1775					SourcePrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.92.1", 24)},
1776					SourceType:         v3listenerpb.FilterChainMatch_EXTERNAL,
1777				},
1778				TransportSocket: transportSocketWithInstanceName("specific-destination-prefix-specific-source-type-specific-source-prefix"),
1779				Filters:         emptyValidNetworkFilters,
1780			},
1781			{
1782				FilterChainMatch: &v3listenerpb.FilterChainMatch{
1783					PrefixRanges:       []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.1.1", 24)},
1784					SourcePrefixRanges: []*v3corepb.CidrRange{cidrRangeFromAddressAndPrefixLen("192.168.92.1", 24)},
1785					SourceType:         v3listenerpb.FilterChainMatch_EXTERNAL,
1786					SourcePorts:        []uint32{80},
1787				},
1788				TransportSocket: transportSocketWithInstanceName("specific-destination-prefix-specific-source-type-specific-source-prefix-specific-source-port"),
1789				Filters:         emptyValidNetworkFilters,
1790			},
1791		},
1792	}
1793
1794	tests := []struct {
1795		desc   string
1796		lis    *v3listenerpb.Listener
1797		params FilterChainLookupParams
1798		wantFC *FilterChain
1799	}{
1800		{
1801			desc: "default filter chain",
1802			lis:  lisWithDefaultChain,
1803			params: FilterChainLookupParams{
1804				IsUnspecifiedListener: true,
1805				DestAddr:              net.IPv4(10, 1, 1, 1),
1806			},
1807			wantFC: &FilterChain{SecurityCfg: &SecurityConfig{IdentityInstanceName: "default"}},
1808		},
1809		{
1810			desc: "unspecified destination match",
1811			lis:  lisWithoutDefaultChain,
1812			params: FilterChainLookupParams{
1813				IsUnspecifiedListener: true,
1814				DestAddr:              net.ParseIP("2001:68::db8"),
1815				SourceAddr:            net.IPv4(10, 1, 1, 1),
1816				SourcePort:            1,
1817			},
1818			wantFC: &FilterChain{SecurityCfg: &SecurityConfig{IdentityInstanceName: "unspecified-dest-and-source-prefix"}},
1819		},
1820		{
1821			desc: "wildcard destination match v4",
1822			lis:  lisWithoutDefaultChain,
1823			params: FilterChainLookupParams{
1824				IsUnspecifiedListener: true,
1825				DestAddr:              net.IPv4(10, 1, 1, 1),
1826				SourceAddr:            net.IPv4(10, 1, 1, 1),
1827				SourcePort:            1,
1828			},
1829			wantFC: &FilterChain{SecurityCfg: &SecurityConfig{IdentityInstanceName: "wildcard-prefixes-v4"}},
1830		},
1831		{
1832			desc: "wildcard source match v6",
1833			lis:  lisWithoutDefaultChain,
1834			params: FilterChainLookupParams{
1835				IsUnspecifiedListener: true,
1836				DestAddr:              net.ParseIP("2001:68::1"),
1837				SourceAddr:            net.ParseIP("2001:68::2"),
1838				SourcePort:            1,
1839			},
1840			wantFC: &FilterChain{SecurityCfg: &SecurityConfig{IdentityInstanceName: "wildcard-source-prefix-v6"}},
1841		},
1842		{
1843			desc: "specific destination and wildcard source type match",
1844			lis:  lisWithoutDefaultChain,
1845			params: FilterChainLookupParams{
1846				IsUnspecifiedListener: true,
1847				DestAddr:              net.IPv4(192, 168, 100, 1),
1848				SourceAddr:            net.IPv4(192, 168, 100, 1),
1849				SourcePort:            80,
1850			},
1851			wantFC: &FilterChain{SecurityCfg: &SecurityConfig{IdentityInstanceName: "specific-destination-prefix-unspecified-source-type"}},
1852		},
1853		{
1854			desc: "specific destination and source type match",
1855			lis:  lisWithoutDefaultChain,
1856			params: FilterChainLookupParams{
1857				IsUnspecifiedListener: true,
1858				DestAddr:              net.IPv4(192, 168, 1, 1),
1859				SourceAddr:            net.IPv4(10, 1, 1, 1),
1860				SourcePort:            80,
1861			},
1862			wantFC: &FilterChain{SecurityCfg: &SecurityConfig{IdentityInstanceName: "specific-destination-prefix-specific-source-type"}},
1863		},
1864		{
1865			desc: "specific destination source type and source prefix",
1866			lis:  lisWithoutDefaultChain,
1867			params: FilterChainLookupParams{
1868				IsUnspecifiedListener: true,
1869				DestAddr:              net.IPv4(192, 168, 1, 1),
1870				SourceAddr:            net.IPv4(192, 168, 92, 100),
1871				SourcePort:            70,
1872			},
1873			wantFC: &FilterChain{SecurityCfg: &SecurityConfig{IdentityInstanceName: "specific-destination-prefix-specific-source-type-specific-source-prefix"}},
1874		},
1875		{
1876			desc: "specific destination source type source prefix and source port",
1877			lis:  lisWithoutDefaultChain,
1878			params: FilterChainLookupParams{
1879				IsUnspecifiedListener: true,
1880				DestAddr:              net.IPv4(192, 168, 1, 1),
1881				SourceAddr:            net.IPv4(192, 168, 92, 100),
1882				SourcePort:            80,
1883			},
1884			wantFC: &FilterChain{SecurityCfg: &SecurityConfig{IdentityInstanceName: "specific-destination-prefix-specific-source-type-specific-source-prefix-specific-source-port"}},
1885		},
1886	}
1887
1888	for _, test := range tests {
1889		t.Run(test.desc, func(t *testing.T) {
1890			fci, err := NewFilterChainManager(test.lis)
1891			if err != nil {
1892				t.Fatalf("NewFilterChainManager() failed: %v", err)
1893			}
1894			gotFC, err := fci.Lookup(test.params)
1895			if err != nil {
1896				t.Fatalf("FilterChainManager.Lookup(%v) failed: %v", test.params, err)
1897			}
1898			if !cmp.Equal(gotFC, test.wantFC, cmpopts.EquateEmpty()) {
1899				t.Fatalf("FilterChainManager.Lookup(%v) = %v, want %v", test.params, gotFC, test.wantFC)
1900			}
1901		})
1902	}
1903}
1904
1905// The Equal() methods defined below help with using cmp.Equal() on these types
1906// which contain all unexported fields.
1907
1908func (fci *FilterChainManager) Equal(other *FilterChainManager) bool {
1909	if (fci == nil) != (other == nil) {
1910		return false
1911	}
1912	if fci == nil {
1913		return true
1914	}
1915	switch {
1916	case !cmp.Equal(fci.dstPrefixMap, other.dstPrefixMap, cmpopts.EquateEmpty()):
1917		return false
1918	// TODO: Support comparing dstPrefixes slice?
1919	case !cmp.Equal(fci.def, other.def, cmpopts.EquateEmpty(), protocmp.Transform()):
1920		return false
1921	}
1922	return true
1923}
1924
1925func (dpe *destPrefixEntry) Equal(other *destPrefixEntry) bool {
1926	if (dpe == nil) != (other == nil) {
1927		return false
1928	}
1929	if dpe == nil {
1930		return true
1931	}
1932	if !cmp.Equal(dpe.net, other.net) {
1933		return false
1934	}
1935	for i, st := range dpe.srcTypeArr {
1936		if !cmp.Equal(st, other.srcTypeArr[i], cmpopts.EquateEmpty()) {
1937			return false
1938		}
1939	}
1940	return true
1941}
1942
1943func (sp *sourcePrefixes) Equal(other *sourcePrefixes) bool {
1944	if (sp == nil) != (other == nil) {
1945		return false
1946	}
1947	if sp == nil {
1948		return true
1949	}
1950	// TODO: Support comparing srcPrefixes slice?
1951	return cmp.Equal(sp.srcPrefixMap, other.srcPrefixMap, cmpopts.EquateEmpty())
1952}
1953
1954func (spe *sourcePrefixEntry) Equal(other *sourcePrefixEntry) bool {
1955	if (spe == nil) != (other == nil) {
1956		return false
1957	}
1958	if spe == nil {
1959		return true
1960	}
1961	switch {
1962	case !cmp.Equal(spe.net, other.net):
1963		return false
1964	case !cmp.Equal(spe.srcPortMap, other.srcPortMap, cmpopts.EquateEmpty(), protocmp.Transform()):
1965		return false
1966	}
1967	return true
1968}
1969
1970// The String() methods defined below help with debugging test failures as the
1971// regular %v or %+v formatting directives do not expands pointer fields inside
1972// structs, and these types have a lot of pointers pointing to other structs.
1973func (fci *FilterChainManager) String() string {
1974	if fci == nil {
1975		return ""
1976	}
1977
1978	var sb strings.Builder
1979	if fci.dstPrefixMap != nil {
1980		sb.WriteString("destination_prefix_map: map {\n")
1981		for k, v := range fci.dstPrefixMap {
1982			sb.WriteString(fmt.Sprintf("%q: %v\n", k, v))
1983		}
1984		sb.WriteString("}\n")
1985	}
1986	if fci.dstPrefixes != nil {
1987		sb.WriteString("destination_prefixes: [")
1988		for _, p := range fci.dstPrefixes {
1989			sb.WriteString(fmt.Sprintf("%v ", p))
1990		}
1991		sb.WriteString("]")
1992	}
1993	if fci.def != nil {
1994		sb.WriteString(fmt.Sprintf("default_filter_chain: %+v ", fci.def))
1995	}
1996	return sb.String()
1997}
1998
1999func (dpe *destPrefixEntry) String() string {
2000	if dpe == nil {
2001		return ""
2002	}
2003	var sb strings.Builder
2004	if dpe.net != nil {
2005		sb.WriteString(fmt.Sprintf("destination_prefix: %s ", dpe.net.String()))
2006	}
2007	sb.WriteString("source_types_array: [")
2008	for _, st := range dpe.srcTypeArr {
2009		sb.WriteString(fmt.Sprintf("%v ", st))
2010	}
2011	sb.WriteString("]")
2012	return sb.String()
2013}
2014
2015func (sp *sourcePrefixes) String() string {
2016	if sp == nil {
2017		return ""
2018	}
2019	var sb strings.Builder
2020	if sp.srcPrefixMap != nil {
2021		sb.WriteString("source_prefix_map: map {")
2022		for k, v := range sp.srcPrefixMap {
2023			sb.WriteString(fmt.Sprintf("%q: %v ", k, v))
2024		}
2025		sb.WriteString("}")
2026	}
2027	if sp.srcPrefixes != nil {
2028		sb.WriteString("source_prefixes: [")
2029		for _, p := range sp.srcPrefixes {
2030			sb.WriteString(fmt.Sprintf("%v ", p))
2031		}
2032		sb.WriteString("]")
2033	}
2034	return sb.String()
2035}
2036
2037func (spe *sourcePrefixEntry) String() string {
2038	if spe == nil {
2039		return ""
2040	}
2041	var sb strings.Builder
2042	if spe.net != nil {
2043		sb.WriteString(fmt.Sprintf("source_prefix: %s ", spe.net.String()))
2044	}
2045	if spe.srcPortMap != nil {
2046		sb.WriteString("source_ports_map: map {")
2047		for k, v := range spe.srcPortMap {
2048			sb.WriteString(fmt.Sprintf("%d: %+v ", k, v))
2049		}
2050		sb.WriteString("}")
2051	}
2052	return sb.String()
2053}
2054
2055func (f *FilterChain) String() string {
2056	if f == nil || f.SecurityCfg == nil {
2057		return ""
2058	}
2059	return fmt.Sprintf("security_config: %v", f.SecurityCfg)
2060}
2061
2062func ipNetFromCIDR(cidr string) *net.IPNet {
2063	_, ipnet, err := net.ParseCIDR(cidr)
2064	if err != nil {
2065		panic(err)
2066	}
2067	return ipnet
2068}
2069
2070func transportSocketWithInstanceName(name string) *v3corepb.TransportSocket {
2071	return &v3corepb.TransportSocket{
2072		Name: "envoy.transport_sockets.tls",
2073		ConfigType: &v3corepb.TransportSocket_TypedConfig{
2074			TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
2075				CommonTlsContext: &v3tlspb.CommonTlsContext{
2076					TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{InstanceName: name},
2077				},
2078			}),
2079		},
2080	}
2081}
2082
2083func cidrRangeFromAddressAndPrefixLen(address string, len int) *v3corepb.CidrRange {
2084	return &v3corepb.CidrRange{
2085		AddressPrefix: address,
2086		PrefixLen: &wrapperspb.UInt32Value{
2087			Value: uint32(len),
2088		},
2089	}
2090}
2091