From 6305252415a03ce1b5327f6347b9952da5ad4d75 Mon Sep 17 00:00:00 2001 From: Sewook Seo Date: Sun, 30 Mar 2025 11:31:57 +0000 Subject: [PATCH] Extend QosFilter address matching function Extend QosFilter address match function for IP address range based on prefix length. Bug: 406146808 Bug: 399960748 Flag: com.android.net.flags.qos_filter_ip_range_matching Test: atest CtsNetTestCases Test: atest ConnectivityCoverageTests Change-Id: I78d995fda111d258da9a912c7afa5e472e43437f --- common/Android.bp | 5 ++ common/flags.aconfig | 8 ++ framework/api/system-current.txt | 2 + framework/src/android/net/QosFilter.java | 35 +++++++++ .../src/android/net/QosSocketFilter.java | 62 +++++++++++----- .../src/android/net/cts/NetworkAgentTest.kt | 16 ++++ .../java/android/net/QosSocketFilterTest.java | 73 +++++++++++++++---- 7 files changed, 169 insertions(+), 32 deletions(-) diff --git a/common/Android.bp b/common/Android.bp index 95d84cc879..acd2349bc9 100644 --- a/common/Android.bp +++ b/common/Android.bp @@ -39,6 +39,11 @@ java_aconfig_library { ], } +// This library MUST NOT be available to platform. This is because if the +// platform depends on a flag, it will not be able to delete that flag until +// that platform version is no longer supported by mainline. Additionally, +// there is no any safety mechanism that prevents the module from +// deleting the flag by mistake, regressing on previous platform versions. java_aconfig_library { name: "com.android.net.flags-aconfig-java-export", aconfig_declarations: "com.android.net.flags-aconfig", diff --git a/common/flags.aconfig b/common/flags.aconfig index 17ddff8dc3..9fcd981be9 100644 --- a/common/flags.aconfig +++ b/common/flags.aconfig @@ -191,3 +191,11 @@ flag { bug: "397536853" is_fixed_read_only: true } + +flag { + name: "qos_filter_ip_range_matching" + namespace: "android_core_networking" + description: "Flag for API to provide IP address range based QoS socket matching." + bug: "406146808" + is_fixed_read_only: true +} diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt index bef29a4283..e631e5d9a4 100644 --- a/framework/api/system-current.txt +++ b/framework/api/system-current.txt @@ -433,8 +433,10 @@ package android.net { public abstract class QosFilter { method @NonNull public abstract android.net.Network getNetwork(); method public abstract boolean matchesLocalAddress(@NonNull java.net.InetAddress, int, int); + method @FlaggedApi("com.android.net.flags.qos_filter_ip_range_matching") public abstract boolean matchesLocalPrefix(@NonNull android.net.IpPrefix, int, int); method public boolean matchesProtocol(int); method public abstract boolean matchesRemoteAddress(@NonNull java.net.InetAddress, int, int); + method @FlaggedApi("com.android.net.flags.qos_filter_ip_range_matching") public abstract boolean matchesRemotePrefix(@NonNull android.net.IpPrefix, int, int); } public final class QosSession implements android.os.Parcelable { diff --git a/framework/src/android/net/QosFilter.java b/framework/src/android/net/QosFilter.java index a731b23c10..c28671085f 100644 --- a/framework/src/android/net/QosFilter.java +++ b/framework/src/android/net/QosFilter.java @@ -16,9 +16,12 @@ package android.net; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.SystemApi; +import com.android.net.flags.Flags; + import java.net.InetAddress; /** @@ -93,6 +96,38 @@ public abstract class QosFilter { public abstract boolean matchesRemoteAddress(@NonNull InetAddress address, int startPort, int endPort); + /** + * Determines whether or not the parameters will be matched with prefix length, source address + * and port. + * + * @param ipPrefix the UE side IP address and prefix length included in IP packet filter set of + * a QoS flow assigned on {@link Network}. + * @param startPort the start of UE side port range included in IP packet filter set of a QoS + * flow assigned on {@link Network}. + * @param endPort the end of UE side port range included in IP packet filter set of a QoS flow + * assigned on {@link Network}. + * @return whether the parameters match the UE side address and port of the filter + */ + @FlaggedApi(Flags.FLAG_QOS_FILTER_IP_RANGE_MATCHING) + public abstract boolean matchesLocalPrefix( + @NonNull IpPrefix ipPrefix, int startPort, int endPort); + + /** + * Determines whether or not the parameters will be matched with prefix length, remote address + * and port. + * + * @param ipPrefix the remote IP address and prefix length included in IP packet filter set of a + * QoS flow assigned on {@link Network}. + * @param startPort the start of remote port range included in IP packet filter set of a QoS + * flow assigned on {@link Network}. + * @param endPort the end of the remote range included in IP packet filter set of a QoS flow + * assigned on {@link Network}. + * @return whether the parameters match the remote address and port of the filter + */ + @FlaggedApi(Flags.FLAG_QOS_FILTER_IP_RANGE_MATCHING) + public abstract boolean matchesRemotePrefix( + @NonNull IpPrefix ipPrefix, int startPort, int endPort); + /** * Determines whether or not the parameter will be matched with this filter. * diff --git a/framework/src/android/net/QosSocketFilter.java b/framework/src/android/net/QosSocketFilter.java index 5ceeb67127..ecc2af1446 100644 --- a/framework/src/android/net/QosSocketFilter.java +++ b/framework/src/android/net/QosSocketFilter.java @@ -36,6 +36,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import java.io.FileDescriptor; +import java.net.Inet4Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; @@ -179,10 +180,11 @@ public class QosSocketFilter extends QosFilter { @Override public boolean matchesLocalAddress(@NonNull final InetAddress address, final int startPort, final int endPort) { - if (mQosSocketInfo.getLocalSocketAddress() == null) { - return false; - } - return matchesAddress(mQosSocketInfo.getLocalSocketAddress(), address, startPort, + return matchesLocalPrefix( + address instanceof Inet4Address + ? new IpPrefix(address, 32) + : new IpPrefix(address, 128), + startPort, endPort); } @@ -192,11 +194,33 @@ public class QosSocketFilter extends QosFilter { @Override public boolean matchesRemoteAddress(@NonNull final InetAddress address, final int startPort, final int endPort) { + return matchesRemotePrefix( + address instanceof Inet4Address + ? new IpPrefix(address, 32) + : new IpPrefix(address, 128), + startPort, + endPort); + } + + /** {@inheritDoc} */ + @Override + public boolean matchesLocalPrefix( + @NonNull final IpPrefix ipPrefix, final int startPort, final int endPort) { + if (mQosSocketInfo.getLocalSocketAddress() == null) { + return false; + } + return matchesAddress(mQosSocketInfo.getLocalSocketAddress(), ipPrefix, startPort, endPort); + } + + /** {@inheritDoc} */ + @Override + public boolean matchesRemotePrefix( + @NonNull final IpPrefix ipPrefix, final int startPort, final int endPort) { if (mQosSocketInfo.getRemoteSocketAddress() == null) { return false; } - return matchesAddress(mQosSocketInfo.getRemoteSocketAddress(), address, startPort, - endPort); + return matchesAddress( + mQosSocketInfo.getRemoteSocketAddress(), ipPrefix, startPort, endPort); } /** @@ -212,25 +236,27 @@ public class QosSocketFilter extends QosFilter { } /** - * Called from {@link QosSocketFilter#matchesLocalAddress(InetAddress, int, int)} - * and {@link QosSocketFilter#matchesRemoteAddress(InetAddress, int, int)} with the - * filterSocketAddress coming from {@link QosSocketInfo#getLocalSocketAddress()}. - *

- * This method exists for testing purposes since {@link QosSocketInfo} couldn't be mocked - * due to being final. + * Called from {@link QosSocketFilter#matchesLocalPrefix(IpPrefix, int, int)} and {@link + * QosSocketFilter#matchesRemotePrefix(IpPrefix, int, int)} with the filterSocketAddress coming + * from {@link QosSocketInfo#getLocalSocketAddress()}. + * + *

This method exists for testing purposes since {@link QosSocketInfo} couldn't be mocked due + * to being final. * * @param filterSocketAddress the socket address of the filter - * @param address the address to compare the filterSocketAddressWith + * @param ipPrefix the IP address with prefix length to compare the filterSocketAddressWith * @param startPort the start of the port range to check * @param endPort the end of the port range to check */ @VisibleForTesting - public static boolean matchesAddress(@NonNull final InetSocketAddress filterSocketAddress, - @NonNull final InetAddress address, - final int startPort, final int endPort) { + public static boolean matchesAddress( + @NonNull final InetSocketAddress filterSocketAddress, + @NonNull final IpPrefix ipPrefix, + final int startPort, + final int endPort) { return startPort <= filterSocketAddress.getPort() && endPort >= filterSocketAddress.getPort() - && (address.isAnyLocalAddress() - || filterSocketAddress.getAddress().equals(address)); + && (ipPrefix.getAddress().isAnyLocalAddress() + || ipPrefix.contains(filterSocketAddress.getAddress())); } } diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt index 7c75211151..147998c8c6 100644 --- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt +++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt @@ -1392,6 +1392,22 @@ class NetworkAgentTest { agent.expectCallback().let { callbackId = it.callbackId assertTrue(it.filter.matchesProtocol(proto)) + // This test is only validating QosFilter address match APIs can be called. + // Detail functionality checks are executed on QosSocketFilterTest. + // Verify the match of test socket's Local address, currently the test socket + // binds to the loopback address. + assertTrue(it.filter.matchesLocalPrefix( + IpPrefix(InetAddress.getLoopbackAddress(), 128), + 0, + 65535 + )) + // Since the test socket doesn't connect to the remote address, we expect + // unmatched result. + assertFalse(it.filter.matchesRemotePrefix( + IpPrefix(InetAddress.getLoopbackAddress(), 128), + 0, + 65535 + )) } assertFailsWith( diff --git a/tests/unit/java/android/net/QosSocketFilterTest.java b/tests/unit/java/android/net/QosSocketFilterTest.java index 6820b4048c..c2e65fdae2 100644 --- a/tests/unit/java/android/net/QosSocketFilterTest.java +++ b/tests/unit/java/android/net/QosSocketFilterTest.java @@ -52,50 +52,95 @@ public class QosSocketFilterTest { public void testPortExactMatch() { final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4"); final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4"); - assertTrue(QosSocketFilter.matchesAddress( - new InetSocketAddress(addressA, 10), addressB, 10, 10)); + assertTrue( + QosSocketFilter.matchesAddress( + new InetSocketAddress(addressA, 10), new IpPrefix(addressB, 32), 10, 10)); } @Test public void testPortLessThanStart() { final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4"); final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4"); - assertFalse(QosSocketFilter.matchesAddress( - new InetSocketAddress(addressA, 8), addressB, 10, 10)); + assertFalse( + QosSocketFilter.matchesAddress( + new InetSocketAddress(addressA, 8), new IpPrefix(addressB, 32), 10, 10)); } @Test public void testPortGreaterThanEnd() { final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4"); final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4"); - assertFalse(QosSocketFilter.matchesAddress( - new InetSocketAddress(addressA, 18), addressB, 10, 10)); + assertFalse( + QosSocketFilter.matchesAddress( + new InetSocketAddress(addressA, 18), new IpPrefix(addressB, 32), 10, 10)); } @Test public void testPortBetweenStartAndEnd() { final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4"); final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4"); - assertTrue(QosSocketFilter.matchesAddress( - new InetSocketAddress(addressA, 10), addressB, 8, 18)); + assertTrue( + QosSocketFilter.matchesAddress( + new InetSocketAddress(addressA, 10), new IpPrefix(addressB, 32), 8, 18)); } @Test public void testAddressesDontMatch() { final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4"); final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.5"); - assertFalse(QosSocketFilter.matchesAddress( - new InetSocketAddress(addressA, 10), addressB, 10, 10)); + assertFalse( + QosSocketFilter.matchesAddress( + new InetSocketAddress(addressA, 10), new IpPrefix(addressB, 32), 10, 10)); } @Test public void testAddressMatchWithAnyLocalAddresses() { final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4"); final InetAddress addressB = InetAddresses.parseNumericAddress("0.0.0.0"); - assertTrue(QosSocketFilter.matchesAddress( - new InetSocketAddress(addressA, 10), addressB, 10, 10)); - assertFalse(QosSocketFilter.matchesAddress( - new InetSocketAddress(addressB, 10), addressA, 10, 10)); + assertTrue( + QosSocketFilter.matchesAddress( + new InetSocketAddress(addressA, 10), new IpPrefix(addressB, 32), 10, 10)); + assertFalse( + QosSocketFilter.matchesAddress( + new InetSocketAddress(addressB, 10), new IpPrefix(addressA, 32), 10, 10)); + } + + @Test + public void testAddressMatchWithPrefixLength() { + final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.100"); + final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.132"); + final InetAddress addressC = InetAddresses.parseNumericAddress("1.2.3.96"); + final InetAddress addressD = InetAddresses.parseNumericAddress("2001:2:3:5::13:D17"); + final InetAddress addressE = InetAddresses.parseNumericAddress("2001:2:3:4::13:D17"); + final InetAddress addressF = InetAddresses.parseNumericAddress("2001:2:3:5::"); + final InetAddress addressG = InetAddresses.parseNumericAddress("135.23.185.35"); + final InetAddress addressH = InetAddresses.parseNumericAddress("135.23.186.127"); + final InetAddress addressI = InetAddresses.parseNumericAddress("135.23.183.127"); + + assertTrue( + QosSocketFilter.matchesAddress( + new InetSocketAddress(addressA, 10), new IpPrefix(addressC, 27), 10, 10)); + assertFalse( + QosSocketFilter.matchesAddress( + new InetSocketAddress(addressB, 10), new IpPrefix(addressC, 27), 10, 10)); + assertTrue( + QosSocketFilter.matchesAddress( + new InetSocketAddress(addressG, 10), new IpPrefix(addressH, 22), 10, 10)); + assertFalse( + QosSocketFilter.matchesAddress( + new InetSocketAddress(addressI, 10), new IpPrefix(addressH, 22), 10, 10)); + assertTrue( + QosSocketFilter.matchesAddress( + new InetSocketAddress(addressD, 10), new IpPrefix(addressF, 64), 10, 10)); + assertFalse( + QosSocketFilter.matchesAddress( + new InetSocketAddress(addressE, 10), new IpPrefix(addressF, 64), 10, 10)); + assertFalse( + QosSocketFilter.matchesAddress( + new InetSocketAddress(addressA, 10), new IpPrefix(addressF, 64), 10, 10)); + assertTrue( + QosSocketFilter.matchesAddress( + new InetSocketAddress(addressD, 10), new IpPrefix(addressD, 128), 10, 10)); } @Test