google.golang.org/grpc@v1.72.2/xds/internal/xdsclient/xdsresource/unmarshal_lds_test.go (about)

     1  /*
     2   *
     3   * Copyright 2021 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  package xdsresource
    19  
    20  import (
    21  	"fmt"
    22  	"strings"
    23  	"testing"
    24  	"time"
    25  
    26  	v2xdspb "github.com/envoyproxy/go-control-plane/envoy/api/v2"
    27  	"github.com/google/go-cmp/cmp"
    28  	"google.golang.org/grpc/internal/pretty"
    29  	"google.golang.org/grpc/internal/testutils"
    30  	"google.golang.org/grpc/internal/testutils/xds/e2e"
    31  	"google.golang.org/grpc/xds/internal/httpfilter"
    32  	"google.golang.org/grpc/xds/internal/xdsclient/xdsresource/version"
    33  	"google.golang.org/protobuf/proto"
    34  	"google.golang.org/protobuf/types/known/anypb"
    35  	"google.golang.org/protobuf/types/known/durationpb"
    36  	"google.golang.org/protobuf/types/known/structpb"
    37  	"google.golang.org/protobuf/types/known/wrapperspb"
    38  
    39  	v1xdsudpatypepb "github.com/cncf/xds/go/udpa/type/v1"
    40  	v3xdsxdstypepb "github.com/cncf/xds/go/xds/type/v3"
    41  	v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
    42  	v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
    43  	rpb "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v3"
    44  	v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
    45  	v3rbacpb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/rbac/v3"
    46  	v3httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
    47  	v3tlspb "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
    48  	v3discoverypb "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
    49  	v3matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
    50  
    51  	_ "google.golang.org/grpc/xds/internal/httpfilter/rbac"   // Register the RBAC HTTP filter.
    52  	_ "google.golang.org/grpc/xds/internal/httpfilter/router" // Register the router filter.
    53  )
    54  
    55  func (s) TestUnmarshalListener_ClientSide(t *testing.T) {
    56  	const (
    57  		v3LDSTarget       = "lds.target.good:3333"
    58  		v3RouteConfigName = "v3RouteConfig"
    59  		routeName         = "routeName"
    60  	)
    61  
    62  	var (
    63  		customFilter = &v3httppb.HttpFilter{
    64  			Name:       "customFilter",
    65  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: customFilterConfig},
    66  		}
    67  		oldTypedStructFilter = &v3httppb.HttpFilter{
    68  			Name:       "customFilter",
    69  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: testutils.MarshalAny(t, customFilterOldTypedStructConfig)},
    70  		}
    71  		newTypedStructFilter = &v3httppb.HttpFilter{
    72  			Name:       "customFilter",
    73  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: testutils.MarshalAny(t, customFilterNewTypedStructConfig)},
    74  		}
    75  		customOptionalFilter = &v3httppb.HttpFilter{
    76  			Name:       "customFilter",
    77  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: customFilterConfig},
    78  			IsOptional: true,
    79  		}
    80  		customFilter2 = &v3httppb.HttpFilter{
    81  			Name:       "customFilter2",
    82  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: customFilterConfig},
    83  		}
    84  		errFilter = &v3httppb.HttpFilter{
    85  			Name:       "errFilter",
    86  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: errFilterConfig},
    87  		}
    88  		errOptionalFilter = &v3httppb.HttpFilter{
    89  			Name:       "errFilter",
    90  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: errFilterConfig},
    91  			IsOptional: true,
    92  		}
    93  		clientOnlyCustomFilter = &v3httppb.HttpFilter{
    94  			Name:       "clientOnlyCustomFilter",
    95  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: clientOnlyCustomFilterConfig},
    96  		}
    97  		serverOnlyCustomFilter = &v3httppb.HttpFilter{
    98  			Name:       "serverOnlyCustomFilter",
    99  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: serverOnlyCustomFilterConfig},
   100  		}
   101  		serverOnlyOptionalCustomFilter = &v3httppb.HttpFilter{
   102  			Name:       "serverOnlyOptionalCustomFilter",
   103  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: serverOnlyCustomFilterConfig},
   104  			IsOptional: true,
   105  		}
   106  		unknownFilter = &v3httppb.HttpFilter{
   107  			Name:       "unknownFilter",
   108  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: unknownFilterConfig},
   109  		}
   110  		unknownOptionalFilter = &v3httppb.HttpFilter{
   111  			Name:       "unknownFilter",
   112  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: unknownFilterConfig},
   113  			IsOptional: true,
   114  		}
   115  		v3LisWithInlineRoute = testutils.MarshalAny(t, &v3listenerpb.Listener{
   116  			Name: v3LDSTarget,
   117  			ApiListener: &v3listenerpb.ApiListener{
   118  				ApiListener: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
   119  					RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
   120  						RouteConfig: &v3routepb.RouteConfiguration{
   121  							Name: routeName,
   122  							VirtualHosts: []*v3routepb.VirtualHost{{
   123  								Domains: []string{v3LDSTarget},
   124  								Routes: []*v3routepb.Route{{
   125  									Match: &v3routepb.RouteMatch{
   126  										PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"},
   127  									},
   128  									Action: &v3routepb.Route_Route{
   129  										Route: &v3routepb.RouteAction{
   130  											ClusterSpecifier: &v3routepb.RouteAction_Cluster{Cluster: clusterName},
   131  										}}}}}}},
   132  					},
   133  					HttpFilters: []*v3httppb.HttpFilter{emptyRouterFilter},
   134  					CommonHttpProtocolOptions: &v3corepb.HttpProtocolOptions{
   135  						MaxStreamDuration: durationpb.New(time.Second),
   136  					},
   137  				}),
   138  			},
   139  		})
   140  		v3LisWithFilters = func(fs ...*v3httppb.HttpFilter) *anypb.Any {
   141  			fs = append(fs, emptyRouterFilter)
   142  			return testutils.MarshalAny(t, &v3listenerpb.Listener{
   143  				Name: v3LDSTarget,
   144  				ApiListener: &v3listenerpb.ApiListener{
   145  					ApiListener: testutils.MarshalAny(t,
   146  						&v3httppb.HttpConnectionManager{
   147  							RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
   148  								Rds: &v3httppb.Rds{
   149  									ConfigSource: &v3corepb.ConfigSource{
   150  										ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{Ads: &v3corepb.AggregatedConfigSource{}},
   151  									},
   152  									RouteConfigName: v3RouteConfigName,
   153  								},
   154  							},
   155  							CommonHttpProtocolOptions: &v3corepb.HttpProtocolOptions{
   156  								MaxStreamDuration: durationpb.New(time.Second),
   157  							},
   158  							HttpFilters: fs,
   159  						}),
   160  				},
   161  			})
   162  		}
   163  		v3LisToTestRBAC = func(xffNumTrustedHops uint32, originalIpDetectionExtensions []*v3corepb.TypedExtensionConfig) *anypb.Any {
   164  			return testutils.MarshalAny(t, &v3listenerpb.Listener{
   165  				Name: v3LDSTarget,
   166  				ApiListener: &v3listenerpb.ApiListener{
   167  					ApiListener: testutils.MarshalAny(t,
   168  						&v3httppb.HttpConnectionManager{
   169  							RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
   170  								Rds: &v3httppb.Rds{
   171  									ConfigSource: &v3corepb.ConfigSource{
   172  										ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{Ads: &v3corepb.AggregatedConfigSource{}},
   173  									},
   174  									RouteConfigName: v3RouteConfigName,
   175  								},
   176  							},
   177  							CommonHttpProtocolOptions: &v3corepb.HttpProtocolOptions{
   178  								MaxStreamDuration: durationpb.New(time.Second),
   179  							},
   180  							HttpFilters:                   []*v3httppb.HttpFilter{emptyRouterFilter},
   181  							XffNumTrustedHops:             xffNumTrustedHops,
   182  							OriginalIpDetectionExtensions: originalIpDetectionExtensions,
   183  						}),
   184  				},
   185  			})
   186  		}
   187  
   188  		v3ListenerWithCDSConfigSourceSelf = testutils.MarshalAny(t, &v3listenerpb.Listener{
   189  			Name: v3LDSTarget,
   190  			ApiListener: &v3listenerpb.ApiListener{
   191  				ApiListener: testutils.MarshalAny(t,
   192  					&v3httppb.HttpConnectionManager{
   193  						RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
   194  							Rds: &v3httppb.Rds{
   195  								ConfigSource: &v3corepb.ConfigSource{
   196  									ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{},
   197  								},
   198  								RouteConfigName: v3RouteConfigName,
   199  							},
   200  						},
   201  						HttpFilters: []*v3httppb.HttpFilter{emptyRouterFilter},
   202  					}),
   203  			},
   204  		})
   205  	)
   206  
   207  	tests := []struct {
   208  		name       string
   209  		resource   *anypb.Any
   210  		wantName   string
   211  		wantUpdate ListenerUpdate
   212  		wantErr    bool
   213  	}{
   214  		{
   215  			name:     "non-listener resource",
   216  			resource: &anypb.Any{TypeUrl: version.V3HTTPConnManagerURL},
   217  			wantErr:  true,
   218  		},
   219  		{
   220  			name: "badly marshaled listener resource",
   221  			resource: &anypb.Any{
   222  				TypeUrl: version.V3ListenerURL,
   223  				Value: func() []byte {
   224  					lis := &v3listenerpb.Listener{
   225  						Name: v3LDSTarget,
   226  						ApiListener: &v3listenerpb.ApiListener{
   227  							ApiListener: &anypb.Any{
   228  								TypeUrl: version.V3HTTPConnManagerURL,
   229  								Value:   []byte{1, 2, 3, 4},
   230  							},
   231  						},
   232  					}
   233  					mLis, _ := proto.Marshal(lis)
   234  					return mLis
   235  				}(),
   236  			},
   237  			wantName: v3LDSTarget,
   238  			wantErr:  true,
   239  		},
   240  		{
   241  			name: "wrong type in apiListener",
   242  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
   243  				Name: v3LDSTarget,
   244  				ApiListener: &v3listenerpb.ApiListener{
   245  					ApiListener: testutils.MarshalAny(t, &v2xdspb.Listener{}),
   246  				},
   247  			}),
   248  			wantName: v3LDSTarget,
   249  			wantErr:  true,
   250  		},
   251  		{
   252  			name: "empty httpConnMgr in apiListener",
   253  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
   254  				Name: v3LDSTarget,
   255  				ApiListener: &v3listenerpb.ApiListener{
   256  					ApiListener: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
   257  						RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
   258  							Rds: &v3httppb.Rds{},
   259  						},
   260  					}),
   261  				},
   262  			}),
   263  			wantName: v3LDSTarget,
   264  			wantErr:  true,
   265  		},
   266  		{
   267  			name: "scopedRoutes routeConfig in apiListener",
   268  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
   269  				Name: v3LDSTarget,
   270  				ApiListener: &v3listenerpb.ApiListener{
   271  					ApiListener: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
   272  						RouteSpecifier: &v3httppb.HttpConnectionManager_ScopedRoutes{},
   273  					}),
   274  				},
   275  			}),
   276  			wantName: v3LDSTarget,
   277  			wantErr:  true,
   278  		},
   279  		{
   280  			name:     "rds.ConfigSource in apiListener is Self",
   281  			resource: v3ListenerWithCDSConfigSourceSelf,
   282  			wantName: v3LDSTarget,
   283  			wantUpdate: ListenerUpdate{
   284  				RouteConfigName: v3RouteConfigName,
   285  				HTTPFilters:     []HTTPFilter{makeRouterFilter(t)},
   286  				Raw:             v3ListenerWithCDSConfigSourceSelf,
   287  			},
   288  		},
   289  		{
   290  			name: "rds.ConfigSource in apiListener is not ADS or Self",
   291  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
   292  				Name: v3LDSTarget,
   293  				ApiListener: &v3listenerpb.ApiListener{
   294  					ApiListener: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
   295  						RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
   296  							Rds: &v3httppb.Rds{
   297  								ConfigSource: &v3corepb.ConfigSource{
   298  									ConfigSourceSpecifier: &v3corepb.ConfigSource_Path{
   299  										Path: "/some/path",
   300  									},
   301  								},
   302  								RouteConfigName: v3RouteConfigName,
   303  							},
   304  						},
   305  					}),
   306  				},
   307  			}),
   308  			wantName: v3LDSTarget,
   309  			wantErr:  true,
   310  		},
   311  		{
   312  			name:     "v3 with no filters",
   313  			resource: v3LisWithFilters(),
   314  			wantName: v3LDSTarget,
   315  			wantUpdate: ListenerUpdate{
   316  				RouteConfigName:   v3RouteConfigName,
   317  				MaxStreamDuration: time.Second,
   318  				HTTPFilters:       makeRouterFilterList(t),
   319  				Raw:               v3LisWithFilters(),
   320  			},
   321  		},
   322  		{
   323  			name: "v3 no terminal filter",
   324  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
   325  				Name: v3LDSTarget,
   326  				ApiListener: &v3listenerpb.ApiListener{
   327  					ApiListener: testutils.MarshalAny(t,
   328  						&v3httppb.HttpConnectionManager{
   329  							RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
   330  								Rds: &v3httppb.Rds{
   331  									ConfigSource: &v3corepb.ConfigSource{
   332  										ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{Ads: &v3corepb.AggregatedConfigSource{}},
   333  									},
   334  									RouteConfigName: v3RouteConfigName,
   335  								},
   336  							},
   337  							CommonHttpProtocolOptions: &v3corepb.HttpProtocolOptions{
   338  								MaxStreamDuration: durationpb.New(time.Second),
   339  							},
   340  						}),
   341  				},
   342  			}),
   343  			wantName: v3LDSTarget,
   344  			wantErr:  true,
   345  		},
   346  		{
   347  			name:     "v3 with custom filter",
   348  			resource: v3LisWithFilters(customFilter),
   349  			wantName: v3LDSTarget,
   350  			wantUpdate: ListenerUpdate{
   351  				RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
   352  				HTTPFilters: []HTTPFilter{
   353  					{
   354  						Name:   "customFilter",
   355  						Filter: httpFilter{},
   356  						Config: filterConfig{Cfg: customFilterConfig},
   357  					},
   358  					makeRouterFilter(t),
   359  				},
   360  				Raw: v3LisWithFilters(customFilter),
   361  			},
   362  		},
   363  		{
   364  			name:     "v3 with custom filter in old typed struct",
   365  			resource: v3LisWithFilters(oldTypedStructFilter),
   366  			wantName: v3LDSTarget,
   367  			wantUpdate: ListenerUpdate{
   368  				RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
   369  				HTTPFilters: []HTTPFilter{
   370  					{
   371  						Name:   "customFilter",
   372  						Filter: httpFilter{},
   373  						Config: filterConfig{Cfg: customFilterOldTypedStructConfig},
   374  					},
   375  					makeRouterFilter(t),
   376  				},
   377  				Raw: v3LisWithFilters(oldTypedStructFilter),
   378  			},
   379  		},
   380  		{
   381  			name:     "v3 with custom filter in new typed struct",
   382  			resource: v3LisWithFilters(newTypedStructFilter),
   383  			wantName: v3LDSTarget,
   384  			wantUpdate: ListenerUpdate{
   385  				RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
   386  				HTTPFilters: []HTTPFilter{
   387  					{
   388  						Name:   "customFilter",
   389  						Filter: httpFilter{},
   390  						Config: filterConfig{Cfg: customFilterNewTypedStructConfig},
   391  					},
   392  					makeRouterFilter(t),
   393  				},
   394  				Raw: v3LisWithFilters(newTypedStructFilter),
   395  			},
   396  		},
   397  		{
   398  			name:     "v3 with optional custom filter",
   399  			resource: v3LisWithFilters(customOptionalFilter),
   400  			wantName: v3LDSTarget,
   401  			wantUpdate: ListenerUpdate{
   402  				RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
   403  				HTTPFilters: []HTTPFilter{
   404  					{
   405  						Name:   "customFilter",
   406  						Filter: httpFilter{},
   407  						Config: filterConfig{Cfg: customFilterConfig},
   408  					},
   409  					makeRouterFilter(t),
   410  				},
   411  				Raw: v3LisWithFilters(customOptionalFilter),
   412  			},
   413  		},
   414  		{
   415  			name:     "v3 with two filters with same name",
   416  			resource: v3LisWithFilters(customFilter, customFilter),
   417  			wantName: v3LDSTarget,
   418  			wantErr:  true,
   419  		},
   420  		{
   421  			name:     "v3 with two filters - same type different name",
   422  			resource: v3LisWithFilters(customFilter, customFilter2),
   423  			wantName: v3LDSTarget,
   424  			wantUpdate: ListenerUpdate{
   425  				RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
   426  				HTTPFilters: []HTTPFilter{{
   427  					Name:   "customFilter",
   428  					Filter: httpFilter{},
   429  					Config: filterConfig{Cfg: customFilterConfig},
   430  				}, {
   431  					Name:   "customFilter2",
   432  					Filter: httpFilter{},
   433  					Config: filterConfig{Cfg: customFilterConfig},
   434  				},
   435  					makeRouterFilter(t),
   436  				},
   437  				Raw: v3LisWithFilters(customFilter, customFilter2),
   438  			},
   439  		},
   440  		{
   441  			name:     "v3 with server-only filter",
   442  			resource: v3LisWithFilters(serverOnlyCustomFilter),
   443  			wantName: v3LDSTarget,
   444  			wantErr:  true,
   445  		},
   446  		{
   447  			name:     "v3 with optional server-only filter",
   448  			resource: v3LisWithFilters(serverOnlyOptionalCustomFilter),
   449  			wantName: v3LDSTarget,
   450  			wantUpdate: ListenerUpdate{
   451  				RouteConfigName:   v3RouteConfigName,
   452  				MaxStreamDuration: time.Second,
   453  				Raw:               v3LisWithFilters(serverOnlyOptionalCustomFilter),
   454  				HTTPFilters:       makeRouterFilterList(t),
   455  			},
   456  		},
   457  		{
   458  			name:     "v3 with client-only filter",
   459  			resource: v3LisWithFilters(clientOnlyCustomFilter),
   460  			wantName: v3LDSTarget,
   461  			wantUpdate: ListenerUpdate{
   462  				RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
   463  				HTTPFilters: []HTTPFilter{
   464  					{
   465  						Name:   "clientOnlyCustomFilter",
   466  						Filter: clientOnlyHTTPFilter{},
   467  						Config: filterConfig{Cfg: clientOnlyCustomFilterConfig},
   468  					},
   469  					makeRouterFilter(t)},
   470  				Raw: v3LisWithFilters(clientOnlyCustomFilter),
   471  			},
   472  		},
   473  		{
   474  			name:     "v3 with err filter",
   475  			resource: v3LisWithFilters(errFilter),
   476  			wantName: v3LDSTarget,
   477  			wantErr:  true,
   478  		},
   479  		{
   480  			name:     "v3 with optional err filter",
   481  			resource: v3LisWithFilters(errOptionalFilter),
   482  			wantName: v3LDSTarget,
   483  			wantErr:  true,
   484  		},
   485  		{
   486  			name:     "v3 with unknown filter",
   487  			resource: v3LisWithFilters(unknownFilter),
   488  			wantName: v3LDSTarget,
   489  			wantErr:  true,
   490  		},
   491  		{
   492  			name:     "v3 with unknown filter (optional)",
   493  			resource: v3LisWithFilters(unknownOptionalFilter),
   494  			wantName: v3LDSTarget,
   495  			wantUpdate: ListenerUpdate{
   496  				RouteConfigName:   v3RouteConfigName,
   497  				MaxStreamDuration: time.Second,
   498  				HTTPFilters:       makeRouterFilterList(t),
   499  				Raw:               v3LisWithFilters(unknownOptionalFilter),
   500  			},
   501  		},
   502  		{
   503  			name:     "v3 listener resource",
   504  			resource: v3LisWithFilters(),
   505  			wantName: v3LDSTarget,
   506  			wantUpdate: ListenerUpdate{
   507  				RouteConfigName:   v3RouteConfigName,
   508  				MaxStreamDuration: time.Second,
   509  				HTTPFilters:       makeRouterFilterList(t),
   510  				Raw:               v3LisWithFilters(),
   511  			},
   512  		},
   513  		{
   514  			name:     "v3 listener resource wrapped",
   515  			resource: testutils.MarshalAny(t, &v3discoverypb.Resource{Resource: v3LisWithFilters()}),
   516  			wantName: v3LDSTarget,
   517  			wantUpdate: ListenerUpdate{
   518  				RouteConfigName:   v3RouteConfigName,
   519  				MaxStreamDuration: time.Second,
   520  				HTTPFilters:       makeRouterFilterList(t),
   521  				Raw:               v3LisWithFilters(),
   522  			},
   523  		},
   524  		// "To allow equating RBAC's direct_remote_ip and
   525  		// remote_ip...HttpConnectionManager.xff_num_trusted_hops must be unset
   526  		// or zero and HttpConnectionManager.original_ip_detection_extensions
   527  		// must be empty." - A41
   528  		{
   529  			name:     "rbac-allow-equating-direct-remote-ip-and-remote-ip-valid",
   530  			resource: v3LisToTestRBAC(0, nil),
   531  			wantName: v3LDSTarget,
   532  			wantUpdate: ListenerUpdate{
   533  				RouteConfigName:   v3RouteConfigName,
   534  				MaxStreamDuration: time.Second,
   535  				HTTPFilters:       []HTTPFilter{makeRouterFilter(t)},
   536  				Raw:               v3LisToTestRBAC(0, nil),
   537  			},
   538  		},
   539  		// In order to support xDS Configured RBAC HTTPFilter equating direct
   540  		// remote ip and remote ip, xffNumTrustedHops cannot be greater than
   541  		// zero. This is because if you can trust a ingress proxy hop when
   542  		// determining an origin clients ip address, direct remote ip != remote
   543  		// ip.
   544  		{
   545  			name:     "rbac-allow-equating-direct-remote-ip-and-remote-ip-invalid-num-untrusted-hops",
   546  			resource: v3LisToTestRBAC(1, nil),
   547  			wantName: v3LDSTarget,
   548  			wantErr:  true,
   549  		},
   550  		// In order to support xDS Configured RBAC HTTPFilter equating direct
   551  		// remote ip and remote ip, originalIpDetectionExtensions must be empty.
   552  		// This is because if you have to ask ip-detection-extension for the
   553  		// original ip, direct remote ip might not equal remote ip.
   554  		{
   555  			name:     "rbac-allow-equating-direct-remote-ip-and-remote-ip-invalid-original-ip-detection-extension",
   556  			resource: v3LisToTestRBAC(0, []*v3corepb.TypedExtensionConfig{{Name: "something"}}),
   557  			wantName: v3LDSTarget,
   558  			wantErr:  true,
   559  		},
   560  		{
   561  			name:     "v3 listener with inline route configuration",
   562  			resource: v3LisWithInlineRoute,
   563  			wantName: v3LDSTarget,
   564  			wantUpdate: ListenerUpdate{
   565  				InlineRouteConfig: &RouteConfigUpdate{
   566  					VirtualHosts: []*VirtualHost{{
   567  						Domains: []string{v3LDSTarget},
   568  						Routes:  []*Route{{Prefix: newStringP("/"), WeightedClusters: map[string]WeightedCluster{clusterName: {Weight: 1}}, ActionType: RouteActionRoute}},
   569  					}}},
   570  				MaxStreamDuration: time.Second,
   571  				Raw:               v3LisWithInlineRoute,
   572  				HTTPFilters:       makeRouterFilterList(t),
   573  			},
   574  		},
   575  	}
   576  
   577  	for _, test := range tests {
   578  		t.Run(test.name, func(t *testing.T) {
   579  			name, update, err := unmarshalListenerResource(test.resource)
   580  			if (err != nil) != test.wantErr {
   581  				t.Errorf("unmarshalListenerResource(%s), got err: %v, wantErr: %v", pretty.ToJSON(test.resource), err, test.wantErr)
   582  			}
   583  			if name != test.wantName {
   584  				t.Errorf("unmarshalListenerResource(%s), got name: %s, want: %s", pretty.ToJSON(test.resource), name, test.wantName)
   585  			}
   586  			if diff := cmp.Diff(update, test.wantUpdate, cmpOpts); diff != "" {
   587  				t.Errorf("unmarshalListenerResource(%s), got unexpected update, diff (-got +want): %v", pretty.ToJSON(test.resource), diff)
   588  			}
   589  		})
   590  	}
   591  }
   592  
   593  func (s) TestUnmarshalListener_ServerSide(t *testing.T) {
   594  	const (
   595  		v3LDSTarget = "grpc/server?xds.resource.listening_address=0.0.0.0:9999"
   596  		testVersion = "test-version-lds-server"
   597  	)
   598  
   599  	var (
   600  		serverOnlyCustomFilter = &v3httppb.HttpFilter{
   601  			Name:       "serverOnlyCustomFilter",
   602  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: serverOnlyCustomFilterConfig},
   603  		}
   604  		routeConfig = &v3routepb.RouteConfiguration{
   605  			Name: "routeName",
   606  			VirtualHosts: []*v3routepb.VirtualHost{{
   607  				Domains: []string{"lds.target.good:3333"},
   608  				Routes: []*v3routepb.Route{{
   609  					Match: &v3routepb.RouteMatch{
   610  						PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"},
   611  					},
   612  					Action: &v3routepb.Route_NonForwardingAction{},
   613  				}}}}}
   614  		inlineRouteConfig = &RouteConfigUpdate{
   615  			VirtualHosts: []*VirtualHost{{
   616  				Domains: []string{"lds.target.good:3333"},
   617  				Routes:  []*Route{{Prefix: newStringP("/"), ActionType: RouteActionNonForwardingAction}},
   618  			}}}
   619  		emptyValidNetworkFilters = []*v3listenerpb.Filter{
   620  			{
   621  				Name: "filter-1",
   622  				ConfigType: &v3listenerpb.Filter_TypedConfig{
   623  					TypedConfig: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
   624  						RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
   625  							RouteConfig: routeConfig,
   626  						},
   627  						HttpFilters: []*v3httppb.HttpFilter{e2e.RouterHTTPFilter},
   628  					}),
   629  				},
   630  			},
   631  		}
   632  		localSocketAddress = &v3corepb.Address{
   633  			Address: &v3corepb.Address_SocketAddress{
   634  				SocketAddress: &v3corepb.SocketAddress{
   635  					Address: "0.0.0.0",
   636  					PortSpecifier: &v3corepb.SocketAddress_PortValue{
   637  						PortValue: 9999,
   638  					},
   639  				},
   640  			},
   641  		}
   642  		listenerEmptyTransportSocket = testutils.MarshalAny(t, &v3listenerpb.Listener{
   643  			Name:    v3LDSTarget,
   644  			Address: localSocketAddress,
   645  			FilterChains: []*v3listenerpb.FilterChain{
   646  				{
   647  					Name:    "filter-chain-1",
   648  					Filters: emptyValidNetworkFilters,
   649  				},
   650  			},
   651  		})
   652  		listenerNoValidationContextDeprecatedFields = testutils.MarshalAny(t, &v3listenerpb.Listener{
   653  			Name:    v3LDSTarget,
   654  			Address: localSocketAddress,
   655  			FilterChains: []*v3listenerpb.FilterChain{
   656  				{
   657  					Name:    "filter-chain-1",
   658  					Filters: emptyValidNetworkFilters,
   659  					TransportSocket: &v3corepb.TransportSocket{
   660  						Name: "envoy.transport_sockets.tls",
   661  						ConfigType: &v3corepb.TransportSocket_TypedConfig{
   662  							TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
   663  								CommonTlsContext: &v3tlspb.CommonTlsContext{
   664  									TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
   665  										InstanceName:    "identityPluginInstance",
   666  										CertificateName: "identityCertName",
   667  									},
   668  								},
   669  							}),
   670  						},
   671  					},
   672  				},
   673  			},
   674  			DefaultFilterChain: &v3listenerpb.FilterChain{
   675  				Name:    "default-filter-chain-1",
   676  				Filters: emptyValidNetworkFilters,
   677  				TransportSocket: &v3corepb.TransportSocket{
   678  					Name: "envoy.transport_sockets.tls",
   679  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
   680  						TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
   681  							CommonTlsContext: &v3tlspb.CommonTlsContext{
   682  								TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
   683  									InstanceName:    "defaultIdentityPluginInstance",
   684  									CertificateName: "defaultIdentityCertName",
   685  								},
   686  							},
   687  						}),
   688  					},
   689  				},
   690  			},
   691  		})
   692  		listenerNoValidationContextNewFields = testutils.MarshalAny(t, &v3listenerpb.Listener{
   693  			Name:    v3LDSTarget,
   694  			Address: localSocketAddress,
   695  			FilterChains: []*v3listenerpb.FilterChain{
   696  				{
   697  					Name:    "filter-chain-1",
   698  					Filters: emptyValidNetworkFilters,
   699  					TransportSocket: &v3corepb.TransportSocket{
   700  						Name: "envoy.transport_sockets.tls",
   701  						ConfigType: &v3corepb.TransportSocket_TypedConfig{
   702  							TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
   703  								CommonTlsContext: &v3tlspb.CommonTlsContext{
   704  									TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   705  										InstanceName:    "identityPluginInstance",
   706  										CertificateName: "identityCertName",
   707  									},
   708  								},
   709  							}),
   710  						},
   711  					},
   712  				},
   713  			},
   714  			DefaultFilterChain: &v3listenerpb.FilterChain{
   715  				Name:    "default-filter-chain-1",
   716  				Filters: emptyValidNetworkFilters,
   717  				TransportSocket: &v3corepb.TransportSocket{
   718  					Name: "envoy.transport_sockets.tls",
   719  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
   720  						TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
   721  							CommonTlsContext: &v3tlspb.CommonTlsContext{
   722  								TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   723  									InstanceName:    "defaultIdentityPluginInstance",
   724  									CertificateName: "defaultIdentityCertName",
   725  								},
   726  							},
   727  						}),
   728  					},
   729  				},
   730  			},
   731  		})
   732  		listenerWithValidationContextDeprecatedFields = testutils.MarshalAny(t, &v3listenerpb.Listener{
   733  			Name:    v3LDSTarget,
   734  			Address: localSocketAddress,
   735  			FilterChains: []*v3listenerpb.FilterChain{
   736  				{
   737  					Name:    "filter-chain-1",
   738  					Filters: emptyValidNetworkFilters,
   739  					TransportSocket: &v3corepb.TransportSocket{
   740  						Name: "envoy.transport_sockets.tls",
   741  						ConfigType: &v3corepb.TransportSocket_TypedConfig{
   742  							TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
   743  								RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
   744  								CommonTlsContext: &v3tlspb.CommonTlsContext{
   745  									TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
   746  										InstanceName:    "identityPluginInstance",
   747  										CertificateName: "identityCertName",
   748  									},
   749  									ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{
   750  										ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
   751  											InstanceName:    "rootPluginInstance",
   752  											CertificateName: "rootCertName",
   753  										},
   754  									},
   755  								},
   756  							}),
   757  						},
   758  					},
   759  				},
   760  			},
   761  			DefaultFilterChain: &v3listenerpb.FilterChain{
   762  				Name:    "default-filter-chain-1",
   763  				Filters: emptyValidNetworkFilters,
   764  				TransportSocket: &v3corepb.TransportSocket{
   765  					Name: "envoy.transport_sockets.tls",
   766  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
   767  						TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
   768  							RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
   769  							CommonTlsContext: &v3tlspb.CommonTlsContext{
   770  								TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
   771  									InstanceName:    "defaultIdentityPluginInstance",
   772  									CertificateName: "defaultIdentityCertName",
   773  								},
   774  								ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{
   775  									ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
   776  										InstanceName:    "defaultRootPluginInstance",
   777  										CertificateName: "defaultRootCertName",
   778  									},
   779  								},
   780  							},
   781  						}),
   782  					},
   783  				},
   784  			},
   785  		})
   786  		listenerWithValidationContextNewFields = testutils.MarshalAny(t, &v3listenerpb.Listener{
   787  			Name:    v3LDSTarget,
   788  			Address: localSocketAddress,
   789  			FilterChains: []*v3listenerpb.FilterChain{
   790  				{
   791  					Name:    "filter-chain-1",
   792  					Filters: emptyValidNetworkFilters,
   793  					TransportSocket: &v3corepb.TransportSocket{
   794  						Name: "envoy.transport_sockets.tls",
   795  						ConfigType: &v3corepb.TransportSocket_TypedConfig{
   796  							TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
   797  								RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
   798  								CommonTlsContext: &v3tlspb.CommonTlsContext{
   799  									TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   800  										InstanceName:    "identityPluginInstance",
   801  										CertificateName: "identityCertName",
   802  									},
   803  									ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{
   804  										ValidationContext: &v3tlspb.CertificateValidationContext{
   805  											CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   806  												InstanceName:    "rootPluginInstance",
   807  												CertificateName: "rootCertName",
   808  											},
   809  											// SystemRootCerts is ignored when
   810  											// CaCertificateProviderInstance is
   811  											// present.
   812  											SystemRootCerts: &v3tlspb.CertificateValidationContext_SystemRootCerts{},
   813  										},
   814  									},
   815  								},
   816  							}),
   817  						},
   818  					},
   819  				},
   820  			},
   821  			DefaultFilterChain: &v3listenerpb.FilterChain{
   822  				Name:    "default-filter-chain-1",
   823  				Filters: emptyValidNetworkFilters,
   824  				TransportSocket: &v3corepb.TransportSocket{
   825  					Name: "envoy.transport_sockets.tls",
   826  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
   827  						TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
   828  							RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
   829  							CommonTlsContext: &v3tlspb.CommonTlsContext{
   830  								TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   831  									InstanceName:    "defaultIdentityPluginInstance",
   832  									CertificateName: "defaultIdentityCertName",
   833  								},
   834  								ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{
   835  									CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{
   836  										DefaultValidationContext: &v3tlspb.CertificateValidationContext{
   837  											CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   838  												InstanceName:    "defaultRootPluginInstance",
   839  												CertificateName: "defaultRootCertName",
   840  											},
   841  										},
   842  									},
   843  								},
   844  							},
   845  						}),
   846  					},
   847  				},
   848  			},
   849  		})
   850  	)
   851  	v3LisToTestRBAC := func(xffNumTrustedHops uint32, originalIpDetectionExtensions []*v3corepb.TypedExtensionConfig) *anypb.Any {
   852  		return testutils.MarshalAny(t, &v3listenerpb.Listener{
   853  			Name:    v3LDSTarget,
   854  			Address: localSocketAddress,
   855  			FilterChains: []*v3listenerpb.FilterChain{
   856  				{
   857  					Name: "filter-chain-1",
   858  					Filters: []*v3listenerpb.Filter{
   859  						{
   860  							Name: "filter-1",
   861  							ConfigType: &v3listenerpb.Filter_TypedConfig{
   862  								TypedConfig: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
   863  									RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
   864  										RouteConfig: routeConfig,
   865  									},
   866  									HttpFilters:                   []*v3httppb.HttpFilter{e2e.RouterHTTPFilter},
   867  									XffNumTrustedHops:             xffNumTrustedHops,
   868  									OriginalIpDetectionExtensions: originalIpDetectionExtensions,
   869  								}),
   870  							},
   871  						},
   872  					},
   873  				},
   874  			},
   875  		})
   876  	}
   877  	v3LisWithBadRBACConfiguration := func(rbacCfg *v3rbacpb.RBAC) *anypb.Any {
   878  		return testutils.MarshalAny(t, &v3listenerpb.Listener{
   879  			Name:    v3LDSTarget,
   880  			Address: localSocketAddress,
   881  			FilterChains: []*v3listenerpb.FilterChain{
   882  				{
   883  					Name: "filter-chain-1",
   884  					Filters: []*v3listenerpb.Filter{
   885  						{
   886  							Name: "filter-1",
   887  							ConfigType: &v3listenerpb.Filter_TypedConfig{
   888  								TypedConfig: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
   889  									RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
   890  										RouteConfig: routeConfig,
   891  									},
   892  									HttpFilters: []*v3httppb.HttpFilter{e2e.HTTPFilter("rbac", rbacCfg), e2e.RouterHTTPFilter},
   893  								}),
   894  							},
   895  						},
   896  					},
   897  				},
   898  			},
   899  		})
   900  	}
   901  	badRBACCfgRegex := &v3rbacpb.RBAC{
   902  		Rules: &rpb.RBAC{
   903  			Action: rpb.RBAC_ALLOW,
   904  			Policies: map[string]*rpb.Policy{
   905  				"bad-regex-value": {
   906  					Permissions: []*rpb.Permission{
   907  						{Rule: &rpb.Permission_Any{Any: true}},
   908  					},
   909  					Principals: []*rpb.Principal{
   910  						{Identifier: &rpb.Principal_Header{Header: &v3routepb.HeaderMatcher{Name: ":method", HeaderMatchSpecifier: &v3routepb.HeaderMatcher_SafeRegexMatch{SafeRegexMatch: &v3matcherpb.RegexMatcher{Regex: "["}}}}},
   911  					},
   912  				},
   913  			},
   914  		},
   915  	}
   916  	badRBACCfgDestIP := &v3rbacpb.RBAC{
   917  		Rules: &rpb.RBAC{
   918  			Action: rpb.RBAC_ALLOW,
   919  			Policies: map[string]*rpb.Policy{
   920  				"certain-destination-ip": {
   921  					Permissions: []*rpb.Permission{
   922  						{Rule: &rpb.Permission_DestinationIp{DestinationIp: &v3corepb.CidrRange{AddressPrefix: "not a correct address", PrefixLen: &wrapperspb.UInt32Value{Value: uint32(10)}}}},
   923  					},
   924  					Principals: []*rpb.Principal{
   925  						{Identifier: &rpb.Principal_Any{Any: true}},
   926  					},
   927  				},
   928  			},
   929  		},
   930  	}
   931  
   932  	tests := []struct {
   933  		name       string
   934  		resource   *anypb.Any
   935  		wantName   string
   936  		wantUpdate ListenerUpdate
   937  		wantErr    string
   938  	}{
   939  		{
   940  			name: "non-empty listener filters",
   941  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
   942  				Name: v3LDSTarget,
   943  				ListenerFilters: []*v3listenerpb.ListenerFilter{
   944  					{Name: "listener-filter-1"},
   945  				},
   946  			}),
   947  			wantName: v3LDSTarget,
   948  			wantErr:  "unsupported field 'listener_filters'",
   949  		},
   950  		{
   951  			name: "use_original_dst is set",
   952  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
   953  				Name:           v3LDSTarget,
   954  				UseOriginalDst: &wrapperspb.BoolValue{Value: true},
   955  			}),
   956  			wantName: v3LDSTarget,
   957  			wantErr:  "unsupported field 'use_original_dst'",
   958  		},
   959  		{
   960  			name:     "no address field",
   961  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{Name: v3LDSTarget}),
   962  			wantName: v3LDSTarget,
   963  			wantErr:  "no address field in LDS response",
   964  		},
   965  		{
   966  			name: "no socket address field",
   967  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
   968  				Name:    v3LDSTarget,
   969  				Address: &v3corepb.Address{},
   970  			}),
   971  			wantName: v3LDSTarget,
   972  			wantErr:  "no socket_address field in LDS response",
   973  		},
   974  		{
   975  			name: "no filter chains and no default filter chain",
   976  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
   977  				Name:    v3LDSTarget,
   978  				Address: localSocketAddress,
   979  				FilterChains: []*v3listenerpb.FilterChain{
   980  					{
   981  						FilterChainMatch: &v3listenerpb.FilterChainMatch{DestinationPort: &wrapperspb.UInt32Value{Value: 666}},
   982  						Filters:          emptyValidNetworkFilters,
   983  					},
   984  				},
   985  			}),
   986  			wantName: v3LDSTarget,
   987  			wantErr:  "no supported filter chains and no default filter chain",
   988  		},
   989  		{
   990  			name: "missing http connection manager network filter",
   991  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
   992  				Name:    v3LDSTarget,
   993  				Address: localSocketAddress,
   994  				FilterChains: []*v3listenerpb.FilterChain{
   995  					{
   996  						Name: "filter-chain-1",
   997  					},
   998  				},
   999  			}),
  1000  			wantName: v3LDSTarget,
  1001  			wantErr:  "missing HttpConnectionManager filter",
  1002  		},
  1003  		{
  1004  			name: "missing filter name in http filter",
  1005  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1006  				Name:    v3LDSTarget,
  1007  				Address: localSocketAddress,
  1008  				FilterChains: []*v3listenerpb.FilterChain{
  1009  					{
  1010  						Name: "filter-chain-1",
  1011  						Filters: []*v3listenerpb.Filter{
  1012  							{
  1013  								ConfigType: &v3listenerpb.Filter_TypedConfig{
  1014  									TypedConfig: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{}),
  1015  								},
  1016  							},
  1017  						},
  1018  					},
  1019  				},
  1020  			}),
  1021  			wantName: v3LDSTarget,
  1022  			wantErr:  "missing name field in filter",
  1023  		},
  1024  		{
  1025  			name: "duplicate filter names in http filter",
  1026  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1027  				Name:    v3LDSTarget,
  1028  				Address: localSocketAddress,
  1029  				FilterChains: []*v3listenerpb.FilterChain{
  1030  					{
  1031  						Name: "filter-chain-1",
  1032  						Filters: []*v3listenerpb.Filter{
  1033  							{
  1034  								Name: "name",
  1035  								ConfigType: &v3listenerpb.Filter_TypedConfig{
  1036  									TypedConfig: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
  1037  										RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
  1038  											RouteConfig: routeConfig,
  1039  										},
  1040  										HttpFilters: []*v3httppb.HttpFilter{emptyRouterFilter},
  1041  									}),
  1042  								},
  1043  							},
  1044  							{
  1045  								Name: "name",
  1046  								ConfigType: &v3listenerpb.Filter_TypedConfig{
  1047  									TypedConfig: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
  1048  										RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
  1049  											RouteConfig: routeConfig,
  1050  										},
  1051  										HttpFilters: []*v3httppb.HttpFilter{emptyRouterFilter},
  1052  									}),
  1053  								},
  1054  							},
  1055  						},
  1056  					},
  1057  				},
  1058  			}),
  1059  			wantName: v3LDSTarget,
  1060  			wantErr:  "duplicate filter name",
  1061  		},
  1062  		{
  1063  			name: "no terminal filter",
  1064  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1065  				Name:    v3LDSTarget,
  1066  				Address: localSocketAddress,
  1067  				FilterChains: []*v3listenerpb.FilterChain{
  1068  					{
  1069  						Name: "filter-chain-1",
  1070  						Filters: []*v3listenerpb.Filter{
  1071  							{
  1072  								Name: "name",
  1073  								ConfigType: &v3listenerpb.Filter_TypedConfig{
  1074  									TypedConfig: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
  1075  										RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
  1076  											RouteConfig: routeConfig,
  1077  										},
  1078  									}),
  1079  								},
  1080  							},
  1081  						},
  1082  					},
  1083  				},
  1084  			}),
  1085  			wantName: v3LDSTarget,
  1086  			wantErr:  "http filters list is empty",
  1087  		},
  1088  		{
  1089  			name: "terminal filter not last",
  1090  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1091  				Name:    v3LDSTarget,
  1092  				Address: localSocketAddress,
  1093  				FilterChains: []*v3listenerpb.FilterChain{
  1094  					{
  1095  						Name: "filter-chain-1",
  1096  						Filters: []*v3listenerpb.Filter{
  1097  							{
  1098  								Name: "name",
  1099  								ConfigType: &v3listenerpb.Filter_TypedConfig{
  1100  									TypedConfig: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
  1101  										RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
  1102  											RouteConfig: routeConfig,
  1103  										},
  1104  										HttpFilters: []*v3httppb.HttpFilter{emptyRouterFilter, serverOnlyCustomFilter},
  1105  									}),
  1106  								},
  1107  							},
  1108  						},
  1109  					},
  1110  				},
  1111  			}),
  1112  			wantName: v3LDSTarget,
  1113  			wantErr:  "is a terminal filter but it is not last in the filter chain",
  1114  		},
  1115  		{
  1116  			name: "last not terminal filter",
  1117  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1118  				Name:    v3LDSTarget,
  1119  				Address: localSocketAddress,
  1120  				FilterChains: []*v3listenerpb.FilterChain{
  1121  					{
  1122  						Name: "filter-chain-1",
  1123  						Filters: []*v3listenerpb.Filter{
  1124  							{
  1125  								Name: "name",
  1126  								ConfigType: &v3listenerpb.Filter_TypedConfig{
  1127  									TypedConfig: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
  1128  										RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
  1129  											RouteConfig: routeConfig,
  1130  										},
  1131  										HttpFilters: []*v3httppb.HttpFilter{serverOnlyCustomFilter},
  1132  									}),
  1133  								},
  1134  							},
  1135  						},
  1136  					},
  1137  				},
  1138  			}),
  1139  			wantName: v3LDSTarget,
  1140  			wantErr:  "is not a terminal filter",
  1141  		},
  1142  		{
  1143  			name: "unsupported oneof in typed config of http filter",
  1144  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1145  				Name:    v3LDSTarget,
  1146  				Address: localSocketAddress,
  1147  				FilterChains: []*v3listenerpb.FilterChain{
  1148  					{
  1149  						Name: "filter-chain-1",
  1150  						Filters: []*v3listenerpb.Filter{
  1151  							{
  1152  								Name:       "name",
  1153  								ConfigType: &v3listenerpb.Filter_ConfigDiscovery{},
  1154  							},
  1155  						},
  1156  					},
  1157  				},
  1158  			}),
  1159  			wantName: v3LDSTarget,
  1160  			wantErr:  "unsupported config_type",
  1161  		},
  1162  		{
  1163  			name: "overlapping filter chain match criteria",
  1164  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1165  				Name:    v3LDSTarget,
  1166  				Address: localSocketAddress,
  1167  				FilterChains: []*v3listenerpb.FilterChain{
  1168  					{
  1169  						FilterChainMatch: &v3listenerpb.FilterChainMatch{SourcePorts: []uint32{1, 2, 3, 4, 5}},
  1170  						Filters:          emptyValidNetworkFilters,
  1171  					},
  1172  					{
  1173  						FilterChainMatch: &v3listenerpb.FilterChainMatch{},
  1174  						Filters:          emptyValidNetworkFilters,
  1175  					},
  1176  					{
  1177  						FilterChainMatch: &v3listenerpb.FilterChainMatch{SourcePorts: []uint32{5, 6, 7}},
  1178  						Filters:          emptyValidNetworkFilters,
  1179  					},
  1180  				},
  1181  			}),
  1182  			wantName: v3LDSTarget,
  1183  			wantErr:  "multiple filter chains with overlapping matching rules are defined",
  1184  		},
  1185  		{
  1186  			name: "unsupported network filter",
  1187  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1188  				Name:    v3LDSTarget,
  1189  				Address: localSocketAddress,
  1190  				FilterChains: []*v3listenerpb.FilterChain{
  1191  					{
  1192  						Name: "filter-chain-1",
  1193  						Filters: []*v3listenerpb.Filter{
  1194  							{
  1195  								Name: "name",
  1196  								ConfigType: &v3listenerpb.Filter_TypedConfig{
  1197  									TypedConfig: testutils.MarshalAny(t, &v3httppb.LocalReplyConfig{}),
  1198  								},
  1199  							},
  1200  						},
  1201  					},
  1202  				},
  1203  			}),
  1204  			wantName: v3LDSTarget,
  1205  			wantErr:  "unsupported network filter",
  1206  		},
  1207  		{
  1208  			name: "badly marshaled network filter",
  1209  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1210  				Name:    v3LDSTarget,
  1211  				Address: localSocketAddress,
  1212  				FilterChains: []*v3listenerpb.FilterChain{
  1213  					{
  1214  						Name: "filter-chain-1",
  1215  						Filters: []*v3listenerpb.Filter{
  1216  							{
  1217  								Name: "name",
  1218  								ConfigType: &v3listenerpb.Filter_TypedConfig{
  1219  									TypedConfig: &anypb.Any{
  1220  										TypeUrl: version.V3HTTPConnManagerURL,
  1221  										Value:   []byte{1, 2, 3, 4},
  1222  									},
  1223  								},
  1224  							},
  1225  						},
  1226  					},
  1227  				},
  1228  			}),
  1229  			wantName: v3LDSTarget,
  1230  			wantErr:  "failed unmarshalling of network filter",
  1231  		},
  1232  		{
  1233  			name: "unexpected transport socket name",
  1234  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1235  				Name:    v3LDSTarget,
  1236  				Address: localSocketAddress,
  1237  				FilterChains: []*v3listenerpb.FilterChain{
  1238  					{
  1239  						Name:    "filter-chain-1",
  1240  						Filters: emptyValidNetworkFilters,
  1241  						TransportSocket: &v3corepb.TransportSocket{
  1242  							Name: "unsupported-transport-socket-name",
  1243  						},
  1244  					},
  1245  				},
  1246  			}),
  1247  			wantName: v3LDSTarget,
  1248  			wantErr:  "transport_socket field has unexpected name",
  1249  		},
  1250  		{
  1251  			name: "unexpected transport socket typedConfig URL",
  1252  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1253  				Name:    v3LDSTarget,
  1254  				Address: localSocketAddress,
  1255  				FilterChains: []*v3listenerpb.FilterChain{
  1256  					{
  1257  						Name:    "filter-chain-1",
  1258  						Filters: emptyValidNetworkFilters,
  1259  						TransportSocket: &v3corepb.TransportSocket{
  1260  							Name: "envoy.transport_sockets.tls",
  1261  							ConfigType: &v3corepb.TransportSocket_TypedConfig{
  1262  								TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{}),
  1263  							},
  1264  						},
  1265  					},
  1266  				},
  1267  			}),
  1268  			wantName: v3LDSTarget,
  1269  			wantErr:  "transport_socket field has unexpected typeURL",
  1270  		},
  1271  		{
  1272  			name: "badly marshaled transport socket",
  1273  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1274  				Name:    v3LDSTarget,
  1275  				Address: localSocketAddress,
  1276  				FilterChains: []*v3listenerpb.FilterChain{
  1277  					{
  1278  						Name:    "filter-chain-1",
  1279  						Filters: emptyValidNetworkFilters,
  1280  						TransportSocket: &v3corepb.TransportSocket{
  1281  							Name: "envoy.transport_sockets.tls",
  1282  							ConfigType: &v3corepb.TransportSocket_TypedConfig{
  1283  								TypedConfig: &anypb.Any{
  1284  									TypeUrl: version.V3DownstreamTLSContextURL,
  1285  									Value:   []byte{1, 2, 3, 4},
  1286  								},
  1287  							},
  1288  						},
  1289  					},
  1290  				},
  1291  			}),
  1292  			wantName: v3LDSTarget,
  1293  			wantErr:  "failed to unmarshal DownstreamTlsContext in LDS response",
  1294  		},
  1295  		{
  1296  			name: "missing CommonTlsContext",
  1297  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1298  				Name:    v3LDSTarget,
  1299  				Address: localSocketAddress,
  1300  				FilterChains: []*v3listenerpb.FilterChain{
  1301  					{
  1302  						Name:    "filter-chain-1",
  1303  						Filters: emptyValidNetworkFilters,
  1304  						TransportSocket: &v3corepb.TransportSocket{
  1305  							Name: "envoy.transport_sockets.tls",
  1306  							ConfigType: &v3corepb.TransportSocket_TypedConfig{
  1307  								TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{}),
  1308  							},
  1309  						},
  1310  					},
  1311  				},
  1312  			}),
  1313  			wantName: v3LDSTarget,
  1314  			wantErr:  "DownstreamTlsContext in LDS response does not contain a CommonTlsContext",
  1315  		},
  1316  		{
  1317  			name:     "rbac-allow-equating-direct-remote-ip-and-remote-ip-valid",
  1318  			resource: v3LisToTestRBAC(0, nil),
  1319  			wantName: v3LDSTarget,
  1320  			wantUpdate: ListenerUpdate{
  1321  				InboundListenerCfg: &InboundListenerConfig{
  1322  					Address: "0.0.0.0",
  1323  					Port:    "9999",
  1324  					FilterChains: &FilterChainManager{
  1325  						dstPrefixMap: map[string]*destPrefixEntry{
  1326  							unspecifiedPrefixMapKey: {
  1327  								srcTypeArr: [3]*sourcePrefixes{
  1328  									{
  1329  										srcPrefixMap: map[string]*sourcePrefixEntry{
  1330  											unspecifiedPrefixMapKey: {
  1331  												srcPortMap: map[int]*FilterChain{
  1332  													0: {
  1333  														InlineRouteConfig: inlineRouteConfig,
  1334  														HTTPFilters:       makeRouterFilterList(t),
  1335  													},
  1336  												},
  1337  											},
  1338  										},
  1339  									},
  1340  								},
  1341  							},
  1342  						},
  1343  					},
  1344  				},
  1345  				Raw: listenerEmptyTransportSocket,
  1346  			},
  1347  		},
  1348  		{
  1349  			name:     "rbac-allow-equating-direct-remote-ip-and-remote-ip-invalid-num-untrusted-hops",
  1350  			resource: v3LisToTestRBAC(1, nil),
  1351  			wantName: v3LDSTarget,
  1352  			wantErr:  "xff_num_trusted_hops must be unset or zero",
  1353  		},
  1354  		{
  1355  			name:     "rbac-allow-equating-direct-remote-ip-and-remote-ip-invalid-original-ip-detection-extension",
  1356  			resource: v3LisToTestRBAC(0, []*v3corepb.TypedExtensionConfig{{Name: "something"}}),
  1357  			wantName: v3LDSTarget,
  1358  			wantErr:  "original_ip_detection_extensions must be empty",
  1359  		},
  1360  		{
  1361  			name:     "rbac-with-invalid-regex",
  1362  			resource: v3LisWithBadRBACConfiguration(badRBACCfgRegex),
  1363  			wantName: v3LDSTarget,
  1364  			wantErr:  "error parsing config for filter",
  1365  		},
  1366  		{
  1367  			name:     "rbac-with-invalid-destination-ip-matcher",
  1368  			resource: v3LisWithBadRBACConfiguration(badRBACCfgDestIP),
  1369  			wantName: v3LDSTarget,
  1370  			wantErr:  "error parsing config for filter",
  1371  		},
  1372  		{
  1373  			name: "unsupported validation context in transport socket",
  1374  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1375  				Name:    v3LDSTarget,
  1376  				Address: localSocketAddress,
  1377  				FilterChains: []*v3listenerpb.FilterChain{
  1378  					{
  1379  						Name:    "filter-chain-1",
  1380  						Filters: emptyValidNetworkFilters,
  1381  						TransportSocket: &v3corepb.TransportSocket{
  1382  							Name: "envoy.transport_sockets.tls",
  1383  							ConfigType: &v3corepb.TransportSocket_TypedConfig{
  1384  								TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
  1385  									CommonTlsContext: &v3tlspb.CommonTlsContext{
  1386  										ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextSdsSecretConfig{
  1387  											ValidationContextSdsSecretConfig: &v3tlspb.SdsSecretConfig{
  1388  												Name: "foo-sds-secret",
  1389  											},
  1390  										},
  1391  									},
  1392  								}),
  1393  							},
  1394  						},
  1395  					},
  1396  				},
  1397  			}),
  1398  			wantName: v3LDSTarget,
  1399  			wantErr:  "validation context contains unexpected type",
  1400  		},
  1401  		{
  1402  			name:     "empty transport socket",
  1403  			resource: listenerEmptyTransportSocket,
  1404  			wantName: v3LDSTarget,
  1405  			wantUpdate: ListenerUpdate{
  1406  				InboundListenerCfg: &InboundListenerConfig{
  1407  					Address: "0.0.0.0",
  1408  					Port:    "9999",
  1409  					FilterChains: &FilterChainManager{
  1410  						dstPrefixMap: map[string]*destPrefixEntry{
  1411  							unspecifiedPrefixMapKey: {
  1412  								srcTypeArr: [3]*sourcePrefixes{
  1413  									{
  1414  										srcPrefixMap: map[string]*sourcePrefixEntry{
  1415  											unspecifiedPrefixMapKey: {
  1416  												srcPortMap: map[int]*FilterChain{
  1417  													0: {
  1418  														InlineRouteConfig: inlineRouteConfig,
  1419  														HTTPFilters:       makeRouterFilterList(t),
  1420  													},
  1421  												},
  1422  											},
  1423  										},
  1424  									},
  1425  								},
  1426  							},
  1427  						},
  1428  					},
  1429  				},
  1430  				Raw: listenerEmptyTransportSocket,
  1431  			},
  1432  		},
  1433  		{
  1434  			name: "no identity and root certificate providers using deprecated fields",
  1435  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1436  				Name:    v3LDSTarget,
  1437  				Address: localSocketAddress,
  1438  				FilterChains: []*v3listenerpb.FilterChain{
  1439  					{
  1440  						Name:    "filter-chain-1",
  1441  						Filters: emptyValidNetworkFilters,
  1442  						TransportSocket: &v3corepb.TransportSocket{
  1443  							Name: "envoy.transport_sockets.tls",
  1444  							ConfigType: &v3corepb.TransportSocket_TypedConfig{
  1445  								TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
  1446  									RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
  1447  									CommonTlsContext: &v3tlspb.CommonTlsContext{
  1448  										TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
  1449  											InstanceName:    "identityPluginInstance",
  1450  											CertificateName: "identityCertName",
  1451  										},
  1452  									},
  1453  								}),
  1454  							},
  1455  						},
  1456  					},
  1457  				},
  1458  			}),
  1459  			wantName: v3LDSTarget,
  1460  			wantErr:  "security configuration on the server-side does not contain root certificate provider instance name, but require_client_cert field is set",
  1461  		},
  1462  		{
  1463  			name: "no identity and root certificate providers using new fields",
  1464  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1465  				Name:    v3LDSTarget,
  1466  				Address: localSocketAddress,
  1467  				FilterChains: []*v3listenerpb.FilterChain{
  1468  					{
  1469  						Name:    "filter-chain-1",
  1470  						Filters: emptyValidNetworkFilters,
  1471  						TransportSocket: &v3corepb.TransportSocket{
  1472  							Name: "envoy.transport_sockets.tls",
  1473  							ConfigType: &v3corepb.TransportSocket_TypedConfig{
  1474  								TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
  1475  									RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
  1476  									CommonTlsContext: &v3tlspb.CommonTlsContext{
  1477  										TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
  1478  											InstanceName:    "identityPluginInstance",
  1479  											CertificateName: "identityCertName",
  1480  										},
  1481  									},
  1482  								}),
  1483  							},
  1484  						},
  1485  					},
  1486  				},
  1487  			}),
  1488  			wantName: v3LDSTarget,
  1489  			wantErr:  "security configuration on the server-side does not contain root certificate provider instance name, but require_client_cert field is set",
  1490  		},
  1491  		{
  1492  			name: "no identity certificate provider with require_client_cert",
  1493  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1494  				Name:    v3LDSTarget,
  1495  				Address: localSocketAddress,
  1496  				FilterChains: []*v3listenerpb.FilterChain{
  1497  					{
  1498  						Name:    "filter-chain-1",
  1499  						Filters: emptyValidNetworkFilters,
  1500  						TransportSocket: &v3corepb.TransportSocket{
  1501  							Name: "envoy.transport_sockets.tls",
  1502  							ConfigType: &v3corepb.TransportSocket_TypedConfig{
  1503  								TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
  1504  									CommonTlsContext: &v3tlspb.CommonTlsContext{},
  1505  								}),
  1506  							},
  1507  						},
  1508  					},
  1509  				},
  1510  			}),
  1511  			wantName: v3LDSTarget,
  1512  			wantErr:  "security configuration on the server-side does not contain identity certificate provider instance name",
  1513  		},
  1514  		{
  1515  			name:     "happy case with no validation context using deprecated fields",
  1516  			resource: listenerNoValidationContextDeprecatedFields,
  1517  			wantName: v3LDSTarget,
  1518  			wantUpdate: ListenerUpdate{
  1519  				InboundListenerCfg: &InboundListenerConfig{
  1520  					Address: "0.0.0.0",
  1521  					Port:    "9999",
  1522  					FilterChains: &FilterChainManager{
  1523  						dstPrefixMap: map[string]*destPrefixEntry{
  1524  							unspecifiedPrefixMapKey: {
  1525  								srcTypeArr: [3]*sourcePrefixes{
  1526  									{
  1527  										srcPrefixMap: map[string]*sourcePrefixEntry{
  1528  											unspecifiedPrefixMapKey: {
  1529  												srcPortMap: map[int]*FilterChain{
  1530  													0: {
  1531  														SecurityCfg: &SecurityConfig{
  1532  															IdentityInstanceName: "identityPluginInstance",
  1533  															IdentityCertName:     "identityCertName",
  1534  														},
  1535  														InlineRouteConfig: inlineRouteConfig,
  1536  														HTTPFilters:       makeRouterFilterList(t),
  1537  													},
  1538  												},
  1539  											},
  1540  										},
  1541  									},
  1542  								},
  1543  							},
  1544  						},
  1545  						def: &FilterChain{
  1546  							SecurityCfg: &SecurityConfig{
  1547  								IdentityInstanceName: "defaultIdentityPluginInstance",
  1548  								IdentityCertName:     "defaultIdentityCertName",
  1549  							},
  1550  							InlineRouteConfig: inlineRouteConfig,
  1551  							HTTPFilters:       makeRouterFilterList(t),
  1552  						},
  1553  					},
  1554  				},
  1555  				Raw: listenerNoValidationContextDeprecatedFields,
  1556  			},
  1557  		},
  1558  		{
  1559  			name:     "happy case with no validation context using new fields",
  1560  			resource: listenerNoValidationContextNewFields,
  1561  			wantName: v3LDSTarget,
  1562  			wantUpdate: ListenerUpdate{
  1563  				InboundListenerCfg: &InboundListenerConfig{
  1564  					Address: "0.0.0.0",
  1565  					Port:    "9999",
  1566  					FilterChains: &FilterChainManager{
  1567  						dstPrefixMap: map[string]*destPrefixEntry{
  1568  							unspecifiedPrefixMapKey: {
  1569  								srcTypeArr: [3]*sourcePrefixes{
  1570  									{
  1571  										srcPrefixMap: map[string]*sourcePrefixEntry{
  1572  											unspecifiedPrefixMapKey: {
  1573  												srcPortMap: map[int]*FilterChain{
  1574  													0: {
  1575  														SecurityCfg: &SecurityConfig{
  1576  															IdentityInstanceName: "identityPluginInstance",
  1577  															IdentityCertName:     "identityCertName",
  1578  														},
  1579  														InlineRouteConfig: inlineRouteConfig,
  1580  														HTTPFilters:       makeRouterFilterList(t),
  1581  													},
  1582  												},
  1583  											},
  1584  										},
  1585  									},
  1586  								},
  1587  							},
  1588  						},
  1589  						def: &FilterChain{
  1590  							SecurityCfg: &SecurityConfig{
  1591  								IdentityInstanceName: "defaultIdentityPluginInstance",
  1592  								IdentityCertName:     "defaultIdentityCertName",
  1593  							},
  1594  							InlineRouteConfig: inlineRouteConfig,
  1595  							HTTPFilters:       makeRouterFilterList(t),
  1596  						},
  1597  					},
  1598  				},
  1599  				Raw: listenerNoValidationContextNewFields,
  1600  			},
  1601  		},
  1602  		{
  1603  			name:     "happy case with validation context provider instance with deprecated fields",
  1604  			resource: listenerWithValidationContextDeprecatedFields,
  1605  			wantName: v3LDSTarget,
  1606  			wantUpdate: ListenerUpdate{
  1607  				InboundListenerCfg: &InboundListenerConfig{
  1608  					Address: "0.0.0.0",
  1609  					Port:    "9999",
  1610  					FilterChains: &FilterChainManager{
  1611  						dstPrefixMap: map[string]*destPrefixEntry{
  1612  							unspecifiedPrefixMapKey: {
  1613  								srcTypeArr: [3]*sourcePrefixes{
  1614  									{
  1615  										srcPrefixMap: map[string]*sourcePrefixEntry{
  1616  											unspecifiedPrefixMapKey: {
  1617  												srcPortMap: map[int]*FilterChain{
  1618  													0: {
  1619  														SecurityCfg: &SecurityConfig{
  1620  															RootInstanceName:     "rootPluginInstance",
  1621  															RootCertName:         "rootCertName",
  1622  															IdentityInstanceName: "identityPluginInstance",
  1623  															IdentityCertName:     "identityCertName",
  1624  															RequireClientCert:    true,
  1625  														},
  1626  														InlineRouteConfig: inlineRouteConfig,
  1627  														HTTPFilters:       makeRouterFilterList(t),
  1628  													},
  1629  												},
  1630  											},
  1631  										},
  1632  									},
  1633  								},
  1634  							},
  1635  						},
  1636  						def: &FilterChain{
  1637  							SecurityCfg: &SecurityConfig{
  1638  								RootInstanceName:     "defaultRootPluginInstance",
  1639  								RootCertName:         "defaultRootCertName",
  1640  								IdentityInstanceName: "defaultIdentityPluginInstance",
  1641  								IdentityCertName:     "defaultIdentityCertName",
  1642  								RequireClientCert:    true,
  1643  							},
  1644  							InlineRouteConfig: inlineRouteConfig,
  1645  							HTTPFilters:       makeRouterFilterList(t),
  1646  						},
  1647  					},
  1648  				},
  1649  				Raw: listenerWithValidationContextDeprecatedFields,
  1650  			},
  1651  		},
  1652  		{
  1653  			name:     "happy case with validation context provider instance with new fields",
  1654  			resource: listenerWithValidationContextNewFields,
  1655  			wantName: v3LDSTarget,
  1656  			wantUpdate: ListenerUpdate{
  1657  				InboundListenerCfg: &InboundListenerConfig{
  1658  					Address: "0.0.0.0",
  1659  					Port:    "9999",
  1660  					FilterChains: &FilterChainManager{
  1661  						dstPrefixMap: map[string]*destPrefixEntry{
  1662  							unspecifiedPrefixMapKey: {
  1663  								srcTypeArr: [3]*sourcePrefixes{
  1664  									{
  1665  										srcPrefixMap: map[string]*sourcePrefixEntry{
  1666  											unspecifiedPrefixMapKey: {
  1667  												srcPortMap: map[int]*FilterChain{
  1668  													0: {
  1669  														SecurityCfg: &SecurityConfig{
  1670  															RootInstanceName:     "rootPluginInstance",
  1671  															RootCertName:         "rootCertName",
  1672  															IdentityInstanceName: "identityPluginInstance",
  1673  															IdentityCertName:     "identityCertName",
  1674  															RequireClientCert:    true,
  1675  														},
  1676  														InlineRouteConfig: inlineRouteConfig,
  1677  														HTTPFilters:       makeRouterFilterList(t),
  1678  													},
  1679  												},
  1680  											},
  1681  										},
  1682  									},
  1683  								},
  1684  							},
  1685  						},
  1686  						def: &FilterChain{
  1687  							SecurityCfg: &SecurityConfig{
  1688  								RootInstanceName:     "defaultRootPluginInstance",
  1689  								RootCertName:         "defaultRootCertName",
  1690  								IdentityInstanceName: "defaultIdentityPluginInstance",
  1691  								IdentityCertName:     "defaultIdentityCertName",
  1692  								RequireClientCert:    true,
  1693  							},
  1694  							InlineRouteConfig: inlineRouteConfig,
  1695  							HTTPFilters:       makeRouterFilterList(t),
  1696  						},
  1697  					},
  1698  				},
  1699  				Raw: listenerWithValidationContextNewFields,
  1700  			},
  1701  		},
  1702  	}
  1703  
  1704  	for _, test := range tests {
  1705  		t.Run(test.name, func(t *testing.T) {
  1706  			name, update, err := unmarshalListenerResource(test.resource)
  1707  			if err != nil && !strings.Contains(err.Error(), test.wantErr) {
  1708  				t.Errorf("unmarshalListenerResource(%s) = %v wantErr: %q", pretty.ToJSON(test.resource), err, test.wantErr)
  1709  			}
  1710  			if name != test.wantName {
  1711  				t.Errorf("unmarshalListenerResource(%s), got name: %s, want: %s", pretty.ToJSON(test.resource), name, test.wantName)
  1712  			}
  1713  			if diff := cmp.Diff(update, test.wantUpdate, cmpOpts); diff != "" {
  1714  				t.Errorf("unmarshalListenerResource(%s), got unexpected update, diff (-got +want): %v", pretty.ToJSON(test.resource), diff)
  1715  			}
  1716  		})
  1717  	}
  1718  }
  1719  
  1720  type filterConfig struct {
  1721  	httpfilter.FilterConfig
  1722  	Cfg      proto.Message
  1723  	Override proto.Message
  1724  }
  1725  
  1726  // httpFilter allows testing the http filter registry and parsing functionality.
  1727  type httpFilter struct {
  1728  	httpfilter.ClientInterceptorBuilder
  1729  	httpfilter.ServerInterceptorBuilder
  1730  }
  1731  
  1732  func (httpFilter) TypeURLs() []string { return []string{"custom.filter"} }
  1733  
  1734  func (httpFilter) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) {
  1735  	return filterConfig{Cfg: cfg}, nil
  1736  }
  1737  
  1738  func (httpFilter) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) {
  1739  	return filterConfig{Override: override}, nil
  1740  }
  1741  
  1742  func (httpFilter) IsTerminal() bool {
  1743  	return false
  1744  }
  1745  
  1746  // errHTTPFilter returns errors no matter what is passed to ParseFilterConfig.
  1747  type errHTTPFilter struct {
  1748  	httpfilter.ClientInterceptorBuilder
  1749  }
  1750  
  1751  func (errHTTPFilter) TypeURLs() []string { return []string{"err.custom.filter"} }
  1752  
  1753  func (errHTTPFilter) ParseFilterConfig(proto.Message) (httpfilter.FilterConfig, error) {
  1754  	return nil, fmt.Errorf("error from ParseFilterConfig")
  1755  }
  1756  
  1757  func (errHTTPFilter) ParseFilterConfigOverride(proto.Message) (httpfilter.FilterConfig, error) {
  1758  	return nil, fmt.Errorf("error from ParseFilterConfigOverride")
  1759  }
  1760  
  1761  func (errHTTPFilter) IsTerminal() bool {
  1762  	return false
  1763  }
  1764  
  1765  func init() {
  1766  	httpfilter.Register(httpFilter{})
  1767  	httpfilter.Register(errHTTPFilter{})
  1768  	httpfilter.Register(serverOnlyHTTPFilter{})
  1769  	httpfilter.Register(clientOnlyHTTPFilter{})
  1770  }
  1771  
  1772  // serverOnlyHTTPFilter does not implement ClientInterceptorBuilder
  1773  type serverOnlyHTTPFilter struct {
  1774  	httpfilter.ServerInterceptorBuilder
  1775  }
  1776  
  1777  func (serverOnlyHTTPFilter) TypeURLs() []string { return []string{"serverOnly.custom.filter"} }
  1778  
  1779  func (serverOnlyHTTPFilter) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) {
  1780  	return filterConfig{Cfg: cfg}, nil
  1781  }
  1782  
  1783  func (serverOnlyHTTPFilter) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) {
  1784  	return filterConfig{Override: override}, nil
  1785  }
  1786  
  1787  func (serverOnlyHTTPFilter) IsTerminal() bool {
  1788  	return false
  1789  }
  1790  
  1791  // clientOnlyHTTPFilter does not implement ServerInterceptorBuilder
  1792  type clientOnlyHTTPFilter struct {
  1793  	httpfilter.ClientInterceptorBuilder
  1794  }
  1795  
  1796  func (clientOnlyHTTPFilter) TypeURLs() []string { return []string{"clientOnly.custom.filter"} }
  1797  
  1798  func (clientOnlyHTTPFilter) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) {
  1799  	return filterConfig{Cfg: cfg}, nil
  1800  }
  1801  
  1802  func (clientOnlyHTTPFilter) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) {
  1803  	return filterConfig{Override: override}, nil
  1804  }
  1805  
  1806  func (clientOnlyHTTPFilter) IsTerminal() bool {
  1807  	return false
  1808  }
  1809  
  1810  var customFilterConfig = &anypb.Any{
  1811  	TypeUrl: "custom.filter",
  1812  	Value:   []byte{1, 2, 3},
  1813  }
  1814  
  1815  var errFilterConfig = &anypb.Any{
  1816  	TypeUrl: "err.custom.filter",
  1817  	Value:   []byte{1, 2, 3},
  1818  }
  1819  
  1820  var serverOnlyCustomFilterConfig = &anypb.Any{
  1821  	TypeUrl: "serverOnly.custom.filter",
  1822  	Value:   []byte{1, 2, 3},
  1823  }
  1824  
  1825  var clientOnlyCustomFilterConfig = &anypb.Any{
  1826  	TypeUrl: "clientOnly.custom.filter",
  1827  	Value:   []byte{1, 2, 3},
  1828  }
  1829  
  1830  // This custom filter uses the old TypedStruct message from the cncf/udpa repo.
  1831  var customFilterOldTypedStructConfig = &v1xdsudpatypepb.TypedStruct{
  1832  	TypeUrl: "custom.filter",
  1833  	Value: &structpb.Struct{
  1834  		Fields: map[string]*structpb.Value{
  1835  			"foo": {Kind: &structpb.Value_StringValue{StringValue: "bar"}},
  1836  		},
  1837  	},
  1838  }
  1839  
  1840  // This custom filter uses the new TypedStruct message from the cncf/xds repo.
  1841  var customFilterNewTypedStructConfig = &v3xdsxdstypepb.TypedStruct{
  1842  	TypeUrl: "custom.filter",
  1843  	Value: &structpb.Struct{
  1844  		Fields: map[string]*structpb.Value{
  1845  			"foo": {Kind: &structpb.Value_StringValue{StringValue: "bar"}},
  1846  		},
  1847  	},
  1848  }
  1849  
  1850  var unknownFilterConfig = &anypb.Any{
  1851  	TypeUrl: "unknown.custom.filter",
  1852  	Value:   []byte{1, 2, 3},
  1853  }
  1854  
  1855  func wrappedOptionalFilter(t *testing.T, name string) *anypb.Any {
  1856  	return testutils.MarshalAny(t, &v3routepb.FilterConfig{
  1857  		IsOptional: true,
  1858  		Config: &anypb.Any{
  1859  			TypeUrl: name,
  1860  			Value:   []byte{1, 2, 3},
  1861  		},
  1862  	})
  1863  }