diff --git a/xds/src/main/java/io/grpc/xds/FilterChainMatchingProtocolNegotiators.java b/xds/src/main/java/io/grpc/xds/FilterChainMatchingProtocolNegotiators.java index 77a66495614..abfcee76195 100644 --- a/xds/src/main/java/io/grpc/xds/FilterChainMatchingProtocolNegotiators.java +++ b/xds/src/main/java/io/grpc/xds/FilterChainMatchingProtocolNegotiators.java @@ -303,6 +303,12 @@ private static Collection filterOnSourceType( return filtered; } + // Returned when no CidrRange matches the address. + private static final int NO_MATCHING_PREFIX = -2; + // Returned when there are no CidrRanges. This matches any address, but is less specific than + // a matching CidrRange, including a wildcard (0.0.0.0/0 or ::/0) of the same address family. + private static final int UNSPECIFIED_PREFIX = -1; + private static int getMatchingPrefixLength( FilterChainMatch filterChainMatch, InetAddress address, boolean forDestination) { boolean isIPv6 = address instanceof Inet6Address; @@ -311,10 +317,10 @@ private static int getMatchingPrefixLength( ? filterChainMatch.prefixRanges() : filterChainMatch.sourcePrefixRanges(); int matchingPrefixLength; - if (cidrRanges.isEmpty()) { // if there is no CidrRange assume 0-length match - matchingPrefixLength = 0; + if (cidrRanges.isEmpty()) { + matchingPrefixLength = UNSPECIFIED_PREFIX; } else { - matchingPrefixLength = -1; + matchingPrefixLength = NO_MATCHING_PREFIX; for (CidrRange cidrRange : cidrRanges) { InetAddress cidrAddr = cidrRange.addressPrefix(); boolean cidrIsIpv6 = cidrAddr instanceof Inet6Address; @@ -335,12 +341,12 @@ private static Collection filterOnIpAddress( Collection filterChains, InetAddress address, boolean forDestination) { // current list of top ones ArrayList topOnes = new ArrayList<>(filterChains.size()); - int topMatchingPrefixLen = -1; + int topMatchingPrefixLen = NO_MATCHING_PREFIX; for (FilterChain filterChain : filterChains) { int currentMatchingPrefixLen = getMatchingPrefixLength( filterChain.filterChainMatch(), address, forDestination); - if (currentMatchingPrefixLen >= 0) { + if (currentMatchingPrefixLen != NO_MATCHING_PREFIX) { if (currentMatchingPrefixLen < topMatchingPrefixLen) { continue; } diff --git a/xds/src/test/java/io/grpc/xds/FilterChainMatchingProtocolNegotiatorsTest.java b/xds/src/test/java/io/grpc/xds/FilterChainMatchingProtocolNegotiatorsTest.java index 722f915dbea..64f07fa3d14 100644 --- a/xds/src/test/java/io/grpc/xds/FilterChainMatchingProtocolNegotiatorsTest.java +++ b/xds/src/test/java/io/grpc/xds/FilterChainMatchingProtocolNegotiatorsTest.java @@ -1197,6 +1197,118 @@ public void filterChainMatch_unsupportedMatchers() throws Exception { .getCertificateName()).isEqualTo("CERT3"); } + @Test + public void destPrefixRange_zeroLengthMoreSpecificThanUnspecified() throws Exception { + EnvoyServerProtoData.DownstreamTlsContext tlsContextUnspecified = + CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1"); + EnvoyServerProtoData.DownstreamTlsContext tlsContextZeroLength = + CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT2", "VA2"); + + EnvoyServerProtoData.FilterChainMatch filterChainMatchUnspecified = + EnvoyServerProtoData.FilterChainMatch.create( + 0 /* destinationPort */, + ImmutableList.of() /* prefixRange */, + ImmutableList.of() /* applicationProtocol */, + ImmutableList.of() /* sourcePrefixRanges */, + EnvoyServerProtoData.ConnectionSourceType.ANY /* sourceType */, + ImmutableList.of() /* sourcePorts */, + ImmutableList.of() /* serverNames */, + "" /* transportProtocol */); + EnvoyServerProtoData.FilterChainMatch filterChainMatchZeroLength = + EnvoyServerProtoData.FilterChainMatch.create( + 0 /* destinationPort */, + ImmutableList.of(EnvoyServerProtoData.CidrRange.create( + InetAddresses.forString("0.0.0.0"), 0)) /* prefixRange */, + ImmutableList.of() /* applicationProtocol */, + ImmutableList.of() /* sourcePrefixRanges */, + EnvoyServerProtoData.ConnectionSourceType.ANY /* sourceType */, + ImmutableList.of() /* sourcePorts */, + ImmutableList.of() /* serverNames */, + "" /* transportProtocol */); + EnvoyServerProtoData.FilterChain filterChainUnspecified = + EnvoyServerProtoData.FilterChain.create( + "filter-chain-unspecified", filterChainMatchUnspecified, HTTP_CONNECTION_MANAGER, + tlsContextUnspecified, tlsContextManager); + EnvoyServerProtoData.FilterChain filterChainZeroLength = + EnvoyServerProtoData.FilterChain.create( + "filter-chain-zero-length", filterChainMatchZeroLength, HTTP_CONNECTION_MANAGER, + tlsContextZeroLength, tlsContextManager); + + selectorManager.updateSelector(new FilterChainSelector( + ImmutableMap.of(filterChainUnspecified, randomConfig("unspecified"), + filterChainZeroLength, noopConfig), + null, new AtomicReference())); + + FilterChainMatchingHandler filterChainMatchingHandler = + new FilterChainMatchingHandler(grpcHandler, selectorManager, mockDelegate); + ChannelHandler next = captureAttrHandler(sslSet, routingSettable); + when(mockDelegate.newHandler(grpcHandler)).thenReturn(next); + setupChannel(LOCAL_IP, REMOTE_IP, 15000, filterChainMatchingHandler); + pipeline.fireUserEventTriggered(event); + channel.runPendingTasks(); + channel.checkException(); + assertThat(sslSet.isDone()).isTrue(); + assertThat(sslSet.get()).isEqualTo(filterChainZeroLength.sslContextProviderSupplier()); + assertThat(routingSettable.get()).isEqualTo(noopConfig); + assertThat(sslSet.get().getTlsContext()).isSameInstanceAs(tlsContextZeroLength); + } + + @Test + public void sourcePrefixRange_zeroLengthMoreSpecificThanUnspecified() throws Exception { + EnvoyServerProtoData.DownstreamTlsContext tlsContextUnspecified = + CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1"); + EnvoyServerProtoData.DownstreamTlsContext tlsContextZeroLength = + CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT2", "VA2"); + + EnvoyServerProtoData.FilterChainMatch filterChainMatchUnspecified = + EnvoyServerProtoData.FilterChainMatch.create( + 0 /* destinationPort */, + ImmutableList.of() /* prefixRange */, + ImmutableList.of() /* applicationProtocol */, + ImmutableList.of() /* sourcePrefixRanges */, + EnvoyServerProtoData.ConnectionSourceType.ANY /* sourceType */, + ImmutableList.of() /* sourcePorts */, + ImmutableList.of() /* serverNames */, + "" /* transportProtocol */); + EnvoyServerProtoData.FilterChainMatch filterChainMatchZeroLength = + EnvoyServerProtoData.FilterChainMatch.create( + 0 /* destinationPort */, + ImmutableList.of() /* prefixRange */, + ImmutableList.of() /* applicationProtocol */, + ImmutableList.of(EnvoyServerProtoData.CidrRange.create( + InetAddresses.forString("0.0.0.0"), 0)) /* sourcePrefixRanges */, + EnvoyServerProtoData.ConnectionSourceType.ANY /* sourceType */, + ImmutableList.of() /* sourcePorts */, + ImmutableList.of() /* serverNames */, + "" /* transportProtocol */); + EnvoyServerProtoData.FilterChain filterChainUnspecified = + EnvoyServerProtoData.FilterChain.create( + "filter-chain-unspecified", filterChainMatchUnspecified, HTTP_CONNECTION_MANAGER, + tlsContextUnspecified, tlsContextManager); + EnvoyServerProtoData.FilterChain filterChainZeroLength = + EnvoyServerProtoData.FilterChain.create( + "filter-chain-zero-length", filterChainMatchZeroLength, HTTP_CONNECTION_MANAGER, + tlsContextZeroLength, tlsContextManager); + + selectorManager.updateSelector(new FilterChainSelector( + ImmutableMap.of(filterChainUnspecified, randomConfig("unspecified"), + filterChainZeroLength, noopConfig), + null, new AtomicReference())); + + FilterChainMatchingHandler filterChainMatchingHandler = + new FilterChainMatchingHandler(grpcHandler, selectorManager, mockDelegate); + ChannelHandler next = captureAttrHandler(sslSet, routingSettable); + when(mockDelegate.newHandler(grpcHandler)).thenReturn(next); + setupChannel(LOCAL_IP, REMOTE_IP, 15000, filterChainMatchingHandler); + pipeline.fireUserEventTriggered(event); + channel.runPendingTasks(); + channel.checkException(); + assertThat(sslSet.isDone()).isTrue(); + assertThat(sslSet.get()).isEqualTo(filterChainZeroLength.sslContextProviderSupplier()); + assertThat(routingSettable.get()).isEqualTo(noopConfig); + assertThat(sslSet.get().getTlsContext()).isSameInstanceAs(tlsContextZeroLength); + } + private static HttpConnectionManager createRds(String name) { return HttpConnectionManager.forRdsName(0L, name, new ArrayList());