xds: fix filterChainMatch for multiple matchers, 0-length prefixLength and empty prefixRange list (#7985)
diff --git a/xds/src/main/java/io/grpc/xds/XdsClientWrapperForServerSds.java b/xds/src/main/java/io/grpc/xds/XdsClientWrapperForServerSds.java
index 17ba379..24d6f21 100644
--- a/xds/src/main/java/io/grpc/xds/XdsClientWrapperForServerSds.java
+++ b/xds/src/main/java/io/grpc/xds/XdsClientWrapperForServerSds.java
@@ -216,9 +216,10 @@
filterChains = filterOnIpAddress(filterChains, remoteInetAddr.getAddress(), false);
filterChains = filterOnSourcePort(filterChains, remoteInetAddr.getPort());
- // if we get more than 1, we ignore filterChains and use the defaultFilerChain
- // although spec not clear for that case
- if (filterChains.size() == 1) {
+ if (filterChains.size() > 1) {
+ // close the connection
+ throw new IllegalStateException("Found 2 matching filter-chains");
+ } else if (filterChains.size() == 1) {
return filterChains.get(0).getDownstreamTlsContext();
}
return curListener.getDefaultFilterChain().getDownstreamTlsContext();
@@ -307,10 +308,10 @@
? filterChainMatch.getPrefixRanges()
: filterChainMatch.getSourcePrefixRanges();
indexOfMatchingPrefixRange = -1;
- if (cidrRanges.isEmpty()) { // if there is no CidrRange assume there is perfect match
- matchingPrefixLength = isIPv6 ? 128 : 32;
- } else {
+ if (cidrRanges.isEmpty()) { // if there is no CidrRange assume 0-length match
matchingPrefixLength = 0;
+ } else {
+ matchingPrefixLength = -1;
int index = 0;
for (CidrRange cidrRange : cidrRanges) {
InetAddress cidrAddr = cidrRange.getAddressPrefix();
@@ -357,7 +358,7 @@
for (FilterChain filterChain : filterChains) {
QueueElement element = new QueueElement(filterChain, address, forDestination);
- if (element.matchingPrefixLength > 0) {
+ if (element.matchingPrefixLength >= 0) {
heap.add(element);
}
}
diff --git a/xds/src/test/java/io/grpc/xds/FilterChainMatchTest.java b/xds/src/test/java/io/grpc/xds/FilterChainMatchTest.java
index b92b1b8..e13064c 100644
--- a/xds/src/test/java/io/grpc/xds/FilterChainMatchTest.java
+++ b/xds/src/test/java/io/grpc/xds/FilterChainMatchTest.java
@@ -17,6 +17,7 @@
package io.grpc.xds;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
import static org.mockito.Mockito.when;
import com.google.protobuf.InvalidProtocolBufferException;
@@ -218,6 +219,37 @@
}
@Test
+ public void dest0LengthPrefixRange()
+ throws UnknownHostException, InvalidProtocolBufferException {
+ setupChannel(LOCAL_IP, REMOTE_IP, 15000);
+ DownstreamTlsContext tlsContext0Length =
+ CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1");
+ // 10.2.2.0/24 doesn't match LOCAL_IP
+ EnvoyServerProtoData.FilterChainMatch filterChainMatch0Length =
+ new EnvoyServerProtoData.FilterChainMatch(
+ 0,
+ Arrays.asList(new EnvoyServerProtoData.CidrRange("10.2.2.0", 0)),
+ Arrays.<String>asList(),
+ Arrays.<EnvoyServerProtoData.CidrRange>asList(),
+ EnvoyServerProtoData.ConnectionSourceType.ANY,
+ Arrays.<Integer>asList());
+ EnvoyServerProtoData.FilterChain filterChain0Length =
+ new EnvoyServerProtoData.FilterChain(filterChainMatch0Length, tlsContext0Length);
+ DownstreamTlsContext tlsContextForDefaultFilterChain =
+ CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT2", "VA2");
+ EnvoyServerProtoData.FilterChain defaultFilterChain =
+ new EnvoyServerProtoData.FilterChain(null, tlsContextForDefaultFilterChain);
+ EnvoyServerProtoData.Listener listener =
+ new EnvoyServerProtoData.Listener(
+ "listener1", LOCAL_IP, Arrays.asList(filterChain0Length), defaultFilterChain);
+ XdsClient.LdsUpdate listenerUpdate = new XdsClient.LdsUpdate(listener);
+ registeredWatcher.onChanged(listenerUpdate);
+ DownstreamTlsContext tlsContext1 =
+ xdsClientWrapperForServerSds.getDownstreamTlsContext(channel);
+ assertThat(tlsContext1).isSameInstanceAs(tlsContext0Length);
+ }
+
+ @Test
public void destPrefixRange_moreSpecificWins()
throws UnknownHostException, InvalidProtocolBufferException {
setupChannel(LOCAL_IP, REMOTE_IP, 15000);
@@ -262,6 +294,50 @@
}
@Test
+ public void destPrefixRange_emptyListLessSpecific()
+ throws UnknownHostException, InvalidProtocolBufferException {
+ setupChannel(LOCAL_IP, REMOTE_IP, 15000);
+ DownstreamTlsContext tlsContextLessSpecific =
+ CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1");
+ EnvoyServerProtoData.FilterChainMatch filterChainMatchLessSpecific =
+ new EnvoyServerProtoData.FilterChainMatch(
+ 0,
+ Arrays.<EnvoyServerProtoData.CidrRange>asList(),
+ Arrays.<String>asList(),
+ Arrays.<EnvoyServerProtoData.CidrRange>asList(),
+ EnvoyServerProtoData.ConnectionSourceType.ANY,
+ Arrays.<Integer>asList());
+ EnvoyServerProtoData.FilterChain filterChainLessSpecific =
+ new EnvoyServerProtoData.FilterChain(filterChainMatchLessSpecific, tlsContextLessSpecific);
+
+ DownstreamTlsContext tlsContextMoreSpecific =
+ CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT2", "VA2");
+ EnvoyServerProtoData.FilterChainMatch filterChainMatchMoreSpecific =
+ new EnvoyServerProtoData.FilterChainMatch(
+ 0,
+ Arrays.asList(new EnvoyServerProtoData.CidrRange("8.0.0.0", 5)),
+ Arrays.<String>asList(),
+ Arrays.<EnvoyServerProtoData.CidrRange>asList(),
+ EnvoyServerProtoData.ConnectionSourceType.ANY,
+ Arrays.<Integer>asList());
+ EnvoyServerProtoData.FilterChain filterChainMoreSpecific =
+ new EnvoyServerProtoData.FilterChain(filterChainMatchMoreSpecific, tlsContextMoreSpecific);
+ EnvoyServerProtoData.FilterChain defaultFilterChain =
+ new EnvoyServerProtoData.FilterChain(null, null);
+ EnvoyServerProtoData.Listener listener =
+ new EnvoyServerProtoData.Listener(
+ "listener1",
+ LOCAL_IP,
+ Arrays.asList(filterChainLessSpecific, filterChainMoreSpecific),
+ defaultFilterChain);
+ XdsClient.LdsUpdate listenerUpdate = new XdsClient.LdsUpdate(listener);
+ registeredWatcher.onChanged(listenerUpdate);
+ DownstreamTlsContext tlsContext1 =
+ xdsClientWrapperForServerSds.getDownstreamTlsContext(channel);
+ assertThat(tlsContext1).isSameInstanceAs(tlsContextMoreSpecific);
+ }
+
+ @Test
public void destPrefixRangeIpv6_moreSpecificWins()
throws UnknownHostException, InvalidProtocolBufferException {
setupChannel("FE80:0000:0000:0000:0202:B3FF:FE1E:8329", "2001:DB8::8:800:200C:417A", 15000);
@@ -458,6 +534,52 @@
}
@Test
+ public void sourcePrefixRange_2Matchers_expectException()
+ throws UnknownHostException, InvalidProtocolBufferException {
+ setupChannel(LOCAL_IP, REMOTE_IP, 15000);
+ DownstreamTlsContext tlsContext1 =
+ CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1");
+ EnvoyServerProtoData.FilterChainMatch filterChainMatch1 =
+ new EnvoyServerProtoData.FilterChainMatch(
+ 0,
+ Arrays.<EnvoyServerProtoData.CidrRange>asList(),
+ Arrays.<String>asList(),
+ Arrays.asList(
+ new EnvoyServerProtoData.CidrRange("10.4.2.0", 24),
+ new EnvoyServerProtoData.CidrRange("192.168.10.2", 32)),
+ EnvoyServerProtoData.ConnectionSourceType.ANY,
+ Arrays.<Integer>asList());
+ EnvoyServerProtoData.FilterChain filterChain1 =
+ new EnvoyServerProtoData.FilterChain(filterChainMatch1, tlsContext1);
+
+ DownstreamTlsContext tlsContext2 =
+ CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT2", "VA2");
+ EnvoyServerProtoData.FilterChainMatch filterChainMatch2 =
+ new EnvoyServerProtoData.FilterChainMatch(
+ 0,
+ Arrays.<EnvoyServerProtoData.CidrRange>asList(),
+ Arrays.<String>asList(),
+ Arrays.asList(new EnvoyServerProtoData.CidrRange("10.4.2.0", 24)),
+ EnvoyServerProtoData.ConnectionSourceType.ANY,
+ Arrays.<Integer>asList());
+ EnvoyServerProtoData.FilterChain filterChain2 =
+ new EnvoyServerProtoData.FilterChain(filterChainMatch2, tlsContext2);
+ EnvoyServerProtoData.FilterChain defaultFilterChain =
+ new EnvoyServerProtoData.FilterChain(null, null);
+ EnvoyServerProtoData.Listener listener =
+ new EnvoyServerProtoData.Listener(
+ "listener1", LOCAL_IP, Arrays.asList(filterChain1, filterChain2), defaultFilterChain);
+ XdsClient.LdsUpdate listenerUpdate = new XdsClient.LdsUpdate(listener);
+ registeredWatcher.onChanged(listenerUpdate);
+ try {
+ xdsClientWrapperForServerSds.getDownstreamTlsContext(channel);
+ fail("expect exception!");
+ } catch (IllegalStateException ise) {
+ assertThat(ise).hasMessageThat().isEqualTo("Found 2 matching filter-chains");
+ }
+ }
+
+ @Test
public void sourcePortMatch_exactMatchWinsOverEmptyList()
throws UnknownHostException, InvalidProtocolBufferException {
setupChannel(LOCAL_IP, REMOTE_IP, 15000);