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