Make change and version bump to aml_net_331910000 for mainline module file: AndroidManifest.xml Snap for 10066519 from 9f31270334e28a3bdc0a577e5e3541417f5ae5fd to mainline-networking-release Change-Id: Ibd01e0844b949b6694ed999dc915ed333a62a350
diff --git a/Android.bp b/Android.bp index 8c7253e..e10a30e 100644 --- a/Android.bp +++ b/Android.bp
@@ -452,6 +452,7 @@ required: [ "privapp_whitelist_com.android.networkstack", ], + updatable: true, lint: { strict_updatability_linting: true }, }
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index c75e832..ad39242 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml
@@ -19,8 +19,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.networkstack" android:sharedUserId="android.uid.networkstack" - android:versionCode="331810000" - android:versionName="aml_net_331810000" + android:versionCode="331910000" + android:versionName="aml_net_331910000" coreApp="true" > <!--- Defines the MAINLINE_NETWORK_STACK permission used by the networkstack process. --> @@ -43,10 +43,6 @@ <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> <!-- Signature permission defined in NetworkStackStub --> <uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK" /> - <!-- Used by creating test network --> - <uses-permission android:name="android.permission.MANAGE_TEST_NETWORKS" /> - <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> - <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <application android:extractNativeLibs="false" android:persistent="true"
diff --git a/apishim/29/com/android/networkstack/apishim/api29/Ikev2VpnProfileBuilderShimImpl.java b/apishim/29/com/android/networkstack/apishim/api29/Ikev2VpnProfileBuilderShimImpl.java deleted file mode 100644 index 46d6e13..0000000 --- a/apishim/29/com/android/networkstack/apishim/api29/Ikev2VpnProfileBuilderShimImpl.java +++ /dev/null
@@ -1,37 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.apishim.api29; - -import android.os.Build; - -import androidx.annotation.RequiresApi; - -import com.android.networkstack.apishim.common.Ikev2VpnProfileBuilderShim; - -/** - * Implementation of Ikev2VpnProfileBuilderShim for API 29. - * - * @param <T> type of builder, typically Ikev2VpnProfile.Builder. This is necessary because at - * compile time, shims for older releases will not have access to this class as it - * debuted in SDK30. So the user of the shim has to pass it in. - */ -// NOTE: The trick with the formal parameter only works because when this shim was introduced, -// the stable API already contained the class that the caller needs to pass in; this won't -// work for a class added in the latest API level. -@RequiresApi(Build.VERSION_CODES.Q) -public class Ikev2VpnProfileBuilderShimImpl<T> implements Ikev2VpnProfileBuilderShim<T> { -}
diff --git a/apishim/29/com/android/networkstack/apishim/api29/Ikev2VpnProfileShimImpl.java b/apishim/29/com/android/networkstack/apishim/api29/Ikev2VpnProfileShimImpl.java deleted file mode 100644 index f84999b..0000000 --- a/apishim/29/com/android/networkstack/apishim/api29/Ikev2VpnProfileShimImpl.java +++ /dev/null
@@ -1,25 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.apishim.api29; - -import com.android.networkstack.apishim.common.Ikev2VpnProfileShim; -/** - * Implementation of Ikev2VpnProfileShim for API 29. - * @param <T> type of profile, typically Ikev2VpnProfile - */ -public class Ikev2VpnProfileShimImpl<T> implements Ikev2VpnProfileShim<T> { -}
diff --git a/apishim/30/com/android/networkstack/apishim/api30/Ikev2VpnProfileBuilderShimImpl.java b/apishim/30/com/android/networkstack/apishim/api30/Ikev2VpnProfileBuilderShimImpl.java index 9a24e63..2597840 100644 --- a/apishim/30/com/android/networkstack/apishim/api30/Ikev2VpnProfileBuilderShimImpl.java +++ b/apishim/30/com/android/networkstack/apishim/api30/Ikev2VpnProfileBuilderShimImpl.java
@@ -37,8 +37,7 @@ */ @RequiresApi(Build.VERSION_CODES.R) public class Ikev2VpnProfileBuilderShimImpl - extends com.android.networkstack.apishim.api29.Ikev2VpnProfileBuilderShimImpl< - Ikev2VpnProfile.Builder> { + implements Ikev2VpnProfileBuilderShim<Ikev2VpnProfile.Builder> { protected final Ikev2VpnProfile.Builder mBuilder; protected Ikev2VpnProfileBuilderShimImpl(@NonNull Ikev2VpnProfile.Builder builder) {
diff --git a/apishim/30/com/android/networkstack/apishim/api30/Ikev2VpnProfileShimImpl.java b/apishim/30/com/android/networkstack/apishim/api30/Ikev2VpnProfileShimImpl.java index 389bc44..54a2a35 100644 --- a/apishim/30/com/android/networkstack/apishim/api30/Ikev2VpnProfileShimImpl.java +++ b/apishim/30/com/android/networkstack/apishim/api30/Ikev2VpnProfileShimImpl.java
@@ -25,11 +25,8 @@ * Implementation of Ikev2VpnProfileShim for API 30. */ // TODO : when API29 is no longer supported, remove the type argument -public class Ikev2VpnProfileShimImpl extends - com.android.networkstack.apishim.api29.Ikev2VpnProfileShimImpl<Ikev2VpnProfile> { - protected Ikev2VpnProfile mProfile; - - protected Ikev2VpnProfileShimImpl() {} +public class Ikev2VpnProfileShimImpl implements Ikev2VpnProfileShim<Ikev2VpnProfile> { + protected final Ikev2VpnProfile mProfile; protected Ikev2VpnProfileShimImpl(Ikev2VpnProfile profile) { mProfile = profile;
diff --git a/apishim/31/com/android/networkstack/apishim/api31/Ikev2VpnProfileBuilderShimImpl.java b/apishim/31/com/android/networkstack/apishim/api31/Ikev2VpnProfileBuilderShimImpl.java deleted file mode 100644 index 36363ef..0000000 --- a/apishim/31/com/android/networkstack/apishim/api31/Ikev2VpnProfileBuilderShimImpl.java +++ /dev/null
@@ -1,39 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.apishim.api31; - -import android.net.Ikev2VpnProfile; -import android.os.Build; - -import androidx.annotation.NonNull; -import androidx.annotation.RequiresApi; - -/** - * Implementation of Ikev2VpnProfileBuilderShim for API 31. - */ -@RequiresApi(Build.VERSION_CODES.S) -public class Ikev2VpnProfileBuilderShimImpl - extends com.android.networkstack.apishim.api30.Ikev2VpnProfileBuilderShimImpl { - protected Ikev2VpnProfileBuilderShimImpl(@NonNull Ikev2VpnProfile.Builder builder) { - super(builder); - } - - protected Ikev2VpnProfileBuilderShimImpl(@NonNull String serverAddr, - @NonNull String identity) { - super(serverAddr, identity); - } -}
diff --git a/apishim/31/com/android/networkstack/apishim/api31/Ikev2VpnProfileShimImpl.java b/apishim/31/com/android/networkstack/apishim/api31/Ikev2VpnProfileShimImpl.java deleted file mode 100644 index 8e0c078..0000000 --- a/apishim/31/com/android/networkstack/apishim/api31/Ikev2VpnProfileShimImpl.java +++ /dev/null
@@ -1,24 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.apishim.api31; - -/** - * Implementation of Ikev2VpnProfileShim for API 31. - */ -public class Ikev2VpnProfileShimImpl extends - com.android.networkstack.apishim.api30.Ikev2VpnProfileShimImpl { -}
diff --git a/apishim/33/com/android/networkstack/apishim/api33/Ikev2VpnProfileBuilderShimImpl.java b/apishim/33/com/android/networkstack/apishim/api33/Ikev2VpnProfileBuilderShimImpl.java index 64c7dad..308407b 100644 --- a/apishim/33/com/android/networkstack/apishim/api33/Ikev2VpnProfileBuilderShimImpl.java +++ b/apishim/33/com/android/networkstack/apishim/api33/Ikev2VpnProfileBuilderShimImpl.java
@@ -32,7 +32,7 @@ */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) public class Ikev2VpnProfileBuilderShimImpl - extends com.android.networkstack.apishim.api31.Ikev2VpnProfileBuilderShimImpl { + extends com.android.networkstack.apishim.api30.Ikev2VpnProfileBuilderShimImpl { protected Ikev2VpnProfileBuilderShimImpl(@NonNull IkeTunnelConnectionParams params) { super(new Ikev2VpnProfile.Builder(params)); }
diff --git a/apishim/33/com/android/networkstack/apishim/api33/Ikev2VpnProfileShimImpl.java b/apishim/33/com/android/networkstack/apishim/api33/Ikev2VpnProfileShimImpl.java index 6821326..8e82218 100644 --- a/apishim/33/com/android/networkstack/apishim/api33/Ikev2VpnProfileShimImpl.java +++ b/apishim/33/com/android/networkstack/apishim/api33/Ikev2VpnProfileShimImpl.java
@@ -16,9 +16,14 @@ package com.android.networkstack.apishim.api33; +import android.net.Ikev2VpnProfile; + /** * A shim for Ikev2VpnProfile */ public class Ikev2VpnProfileShimImpl extends com.android.networkstack.apishim.api30.Ikev2VpnProfileShimImpl { + protected Ikev2VpnProfileShimImpl(Ikev2VpnProfile profile) { + super(profile); + } }
diff --git a/apishim/34/com/android/networkstack/apishim/Ikev2VpnProfileShimImpl.java b/apishim/34/com/android/networkstack/apishim/Ikev2VpnProfileShimImpl.java index cd4a8ca..23e5ffc 100644 --- a/apishim/34/com/android/networkstack/apishim/Ikev2VpnProfileShimImpl.java +++ b/apishim/34/com/android/networkstack/apishim/Ikev2VpnProfileShimImpl.java
@@ -29,7 +29,7 @@ */ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public class Ikev2VpnProfileShimImpl - extends com.android.networkstack.apishim.api30.Ikev2VpnProfileShimImpl { + extends com.android.networkstack.apishim.api33.Ikev2VpnProfileShimImpl { protected Ikev2VpnProfileShimImpl(Ikev2VpnProfile profile) { super(profile); } @@ -41,7 +41,7 @@ if (SdkLevel.isAtLeastU()) { return new Ikev2VpnProfileShimImpl(profile); } else { - return com.android.networkstack.apishim.api30.Ikev2VpnProfileShimImpl + return com.android.networkstack.apishim.api33.Ikev2VpnProfileShimImpl .newInstance(profile); } }
diff --git a/common/networkstackclient/Android.bp b/common/networkstackclient/Android.bp index 89de36b..035ddc7 100644 --- a/common/networkstackclient/Android.bp +++ b/common/networkstackclient/Android.bp
@@ -128,30 +128,40 @@ }, }, imports: ["ipmemorystore-aidl-interfaces-V10"], - versions: [ - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "9", - "10", - "11", - "12", - "13", - "14", - "15", - "16", - ], // TODO: have tethering depend on networkstack-client and set visibility to private visibility: [ "//system/tools/aidl/build", "//frameworks/base/packages/Tethering", "//packages/modules/Connectivity/Tethering", ], + versions_with_info: [ + // Remove old networkstack aidl interface version info that is no longer used. + { + version: "13", + imports: ["ipmemorystore-aidl-interfaces-V10"], + }, + { + version: "14", + imports: ["ipmemorystore-aidl-interfaces-V10"], + }, + { + version: "15", + imports: ["ipmemorystore-aidl-interfaces-V10"], + }, + { + version: "16", + imports: ["ipmemorystore-aidl-interfaces-V10"], + }, + { + version: "17", + imports: ["ipmemorystore-aidl-interfaces-V10"], + }, + + ], + + // "frozen: true" is removed manually after each freeze, this property cannot be unrecognized + // in some downstream branches and TH will get the build error, see b/262507066 for details. + } java_library { @@ -160,7 +170,7 @@ min_sdk_version: "29", static_libs: [ "ipmemorystore-aidl-interfaces-V10-java", - "networkstack-aidl-interfaces-V16-java", + "networkstack-aidl-interfaces-V17-java", ], visibility: ["//packages/modules/NetworkStack:__subpackages__"], apex_available: [
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/.hash b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/.hash new file mode 100644 index 0000000..a9db7c1 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/.hash
@@ -0,0 +1 @@ +e29ecca6f4e40189d76cf0a1ac6cbc7bccd1db8a
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/DataStallReportParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/DataStallReportParcelable.aidl new file mode 100644 index 0000000..771deda --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/DataStallReportParcelable.aidl
@@ -0,0 +1,42 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@JavaDerive(toString=true) +parcelable DataStallReportParcelable { + long timestampMillis = 0; + int detectionMethod = 1; + int tcpPacketFailRate = 2; + int tcpMetricsCollectionPeriodMillis = 3; + int dnsConsecutiveTimeouts = 4; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/DhcpResultsParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/DhcpResultsParcelable.aidl new file mode 100644 index 0000000..31f2194 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/DhcpResultsParcelable.aidl
@@ -0,0 +1,44 @@ +/** + * Copyright (c) 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@JavaDerive(toString=true) +parcelable DhcpResultsParcelable { + android.net.StaticIpConfiguration baseConfiguration; + int leaseDuration; + int mtu; + String serverAddress; + String vendorInfo; + @nullable String serverHostName; + @nullable String captivePortalApiUrl; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/INetworkMonitor.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/INetworkMonitor.aidl new file mode 100644 index 0000000..fb13c0c --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/INetworkMonitor.aidl
@@ -0,0 +1,60 @@ +/** + * Copyright (c) 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +interface INetworkMonitor { + oneway void start(); + oneway void launchCaptivePortalApp(); + oneway void notifyCaptivePortalAppFinished(int response); + oneway void setAcceptPartialConnectivity(); + oneway void forceReevaluation(int uid); + oneway void notifyPrivateDnsChanged(in android.net.PrivateDnsConfigParcel config); + oneway void notifyDnsResponse(int returnCode); + oneway void notifyNetworkConnected(in android.net.LinkProperties lp, in android.net.NetworkCapabilities nc); + oneway void notifyNetworkDisconnected(); + oneway void notifyLinkPropertiesChanged(in android.net.LinkProperties lp); + oneway void notifyNetworkCapabilitiesChanged(in android.net.NetworkCapabilities nc); + oneway void notifyNetworkConnectedParcel(in android.net.networkstack.aidl.NetworkMonitorParameters params); + const int NETWORK_TEST_RESULT_VALID = 0; + const int NETWORK_TEST_RESULT_INVALID = 1; + const int NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY = 2; + const int NETWORK_VALIDATION_RESULT_VALID = 0x01; + const int NETWORK_VALIDATION_RESULT_PARTIAL = 0x02; + const int NETWORK_VALIDATION_RESULT_SKIPPED = 0x04; + const int NETWORK_VALIDATION_PROBE_DNS = 0x04; + const int NETWORK_VALIDATION_PROBE_HTTP = 0x08; + const int NETWORK_VALIDATION_PROBE_HTTPS = 0x10; + const int NETWORK_VALIDATION_PROBE_FALLBACK = 0x20; + const int NETWORK_VALIDATION_PROBE_PRIVDNS = 0x40; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/INetworkMonitorCallbacks.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/INetworkMonitorCallbacks.aidl new file mode 100644 index 0000000..36eda8e --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/INetworkMonitorCallbacks.aidl
@@ -0,0 +1,46 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +interface INetworkMonitorCallbacks { + oneway void onNetworkMonitorCreated(in android.net.INetworkMonitor networkMonitor) = 0; + oneway void notifyNetworkTested(int testResult, @nullable String redirectUrl) = 1; + oneway void notifyPrivateDnsConfigResolved(in android.net.PrivateDnsConfigParcel config) = 2; + oneway void showProvisioningNotification(String action, String packageName) = 3; + oneway void hideProvisioningNotification() = 4; + oneway void notifyProbeStatusChanged(int probesCompleted, int probesSucceeded) = 5; + oneway void notifyNetworkTestedWithExtras(in android.net.NetworkTestResultParcelable result) = 6; + oneway void notifyDataStallSuspected(in android.net.DataStallReportParcelable report) = 7; + oneway void notifyCaptivePortalDataChanged(in android.net.CaptivePortalData data) = 8; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/INetworkStackConnector.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/INetworkStackConnector.aidl new file mode 100644 index 0000000..8120ffc --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/INetworkStackConnector.aidl
@@ -0,0 +1,42 @@ +/** + * Copyright (c) 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +interface INetworkStackConnector { + oneway void makeDhcpServer(in String ifName, in android.net.dhcp.DhcpServingParamsParcel params, in android.net.dhcp.IDhcpServerCallbacks cb); + oneway void makeNetworkMonitor(in android.net.Network network, String name, in android.net.INetworkMonitorCallbacks cb); + oneway void makeIpClient(in String ifName, in android.net.ip.IIpClientCallbacks callbacks); + oneway void fetchIpMemoryStore(in android.net.IIpMemoryStoreCallbacks cb); + oneway void allowTestUid(int uid, in android.net.INetworkStackStatusCallback cb); +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/INetworkStackStatusCallback.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/INetworkStackStatusCallback.aidl new file mode 100644 index 0000000..0b6b778 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/INetworkStackStatusCallback.aidl
@@ -0,0 +1,38 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +interface INetworkStackStatusCallback { + oneway void onStatusAvailable(int statusCode); +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/InformationElementParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/InformationElementParcelable.aidl new file mode 100644 index 0000000..6103774 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/InformationElementParcelable.aidl
@@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@JavaDerive(toString=true) +parcelable InformationElementParcelable { + int id; + byte[] payload; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/InitialConfigurationParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/InitialConfigurationParcelable.aidl new file mode 100644 index 0000000..6a597e6 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/InitialConfigurationParcelable.aidl
@@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@JavaDerive(toString=true) +parcelable InitialConfigurationParcelable { + android.net.LinkAddress[] ipAddresses; + android.net.IpPrefix[] directlyConnectedRoutes; + String[] dnsServers; + String gateway; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/Layer2InformationParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/Layer2InformationParcelable.aidl new file mode 100644 index 0000000..83796ee --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/Layer2InformationParcelable.aidl
@@ -0,0 +1,40 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@JavaDerive(toString=true) +parcelable Layer2InformationParcelable { + String l2Key; + String cluster; + android.net.MacAddress bssid; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/Layer2PacketParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/Layer2PacketParcelable.aidl new file mode 100644 index 0000000..4b3fff5 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/Layer2PacketParcelable.aidl
@@ -0,0 +1,39 @@ +/** + * Copyright (c) 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@JavaDerive(toString=true) +parcelable Layer2PacketParcelable { + android.net.MacAddress dstMacAddress; + byte[] payload; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/NattKeepalivePacketDataParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/NattKeepalivePacketDataParcelable.aidl new file mode 100644 index 0000000..18cf954 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/NattKeepalivePacketDataParcelable.aidl
@@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@JavaDerive(toString=true) +parcelable NattKeepalivePacketDataParcelable { + byte[] srcAddress; + int srcPort; + byte[] dstAddress; + int dstPort; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/NetworkTestResultParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/NetworkTestResultParcelable.aidl new file mode 100644 index 0000000..4d6d5a2 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/NetworkTestResultParcelable.aidl
@@ -0,0 +1,42 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@JavaDerive(toString=true) +parcelable NetworkTestResultParcelable { + long timestampMillis; + int result; + int probesSucceeded; + int probesAttempted; + String redirectUrl; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/PrivateDnsConfigParcel.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/PrivateDnsConfigParcel.aidl new file mode 100644 index 0000000..1457caf --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/PrivateDnsConfigParcel.aidl
@@ -0,0 +1,39 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@JavaDerive(toString=true) +parcelable PrivateDnsConfigParcel { + String hostname; + String[] ips; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/ProvisioningConfigurationParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/ProvisioningConfigurationParcelable.aidl new file mode 100644 index 0000000..fba524b --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/ProvisioningConfigurationParcelable.aidl
@@ -0,0 +1,63 @@ +/* +** +** Copyright (C) 2019 The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@JavaDerive(toString=true) +parcelable ProvisioningConfigurationParcelable { + /** + * @deprecated use ipv4ProvisioningMode instead. + */ + boolean enableIPv4; + /** + * @deprecated use ipv6ProvisioningMode instead. + */ + boolean enableIPv6; + boolean usingMultinetworkPolicyTracker; + boolean usingIpReachabilityMonitor; + int requestedPreDhcpActionMs; + android.net.InitialConfigurationParcelable initialConfig; + android.net.StaticIpConfiguration staticIpConfig; + android.net.apf.ApfCapabilities apfCapabilities; + int provisioningTimeoutMs; + int ipv6AddrGenMode; + android.net.Network network; + String displayName; + boolean enablePreconnection; + @nullable android.net.ScanResultInfoParcelable scanResultInfo; + @nullable android.net.Layer2InformationParcelable layer2Info; + @nullable List<android.net.networkstack.aidl.dhcp.DhcpOption> options; + int ipv4ProvisioningMode; + int ipv6ProvisioningMode; + boolean uniqueEui64AddressesOnly; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/ScanResultInfoParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/ScanResultInfoParcelable.aidl new file mode 100644 index 0000000..94fc27f --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/ScanResultInfoParcelable.aidl
@@ -0,0 +1,40 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@JavaDerive(toString=true) +parcelable ScanResultInfoParcelable { + String ssid; + String bssid; + android.net.InformationElementParcelable[] informationElements; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/TcpKeepalivePacketDataParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/TcpKeepalivePacketDataParcelable.aidl new file mode 100644 index 0000000..0e1c21c --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/TcpKeepalivePacketDataParcelable.aidl
@@ -0,0 +1,47 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@JavaDerive(toString=true) +parcelable TcpKeepalivePacketDataParcelable { + byte[] srcAddress; + int srcPort; + byte[] dstAddress; + int dstPort; + int seq; + int ack; + int rcvWnd; + int rcvWndScale; + int tos; + int ttl; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/dhcp/DhcpLeaseParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/dhcp/DhcpLeaseParcelable.aidl new file mode 100644 index 0000000..3cd8860 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/dhcp/DhcpLeaseParcelable.aidl
@@ -0,0 +1,43 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.dhcp; +@JavaDerive(toString=true) +parcelable DhcpLeaseParcelable { + byte[] clientId; + byte[] hwAddr; + int netAddr; + int prefixLength; + long expTime; + String hostname; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/dhcp/DhcpServingParamsParcel.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/dhcp/DhcpServingParamsParcel.aidl new file mode 100644 index 0000000..7997936 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/dhcp/DhcpServingParamsParcel.aidl
@@ -0,0 +1,49 @@ +/** + * + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.dhcp; +@JavaDerive(toString=true) +parcelable DhcpServingParamsParcel { + int serverAddr; + int serverAddrPrefixLength; + int[] defaultRouters; + int[] dnsServers; + int[] excludedAddrs; + long dhcpLeaseTimeSecs; + int linkMtu; + boolean metered; + int singleClientAddr = 0; + boolean changePrefixOnDecline = false; + int leasesSubnetPrefixLength = 0; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/dhcp/IDhcpEventCallbacks.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/dhcp/IDhcpEventCallbacks.aidl new file mode 100644 index 0000000..9312f47 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/dhcp/IDhcpEventCallbacks.aidl
@@ -0,0 +1,38 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.dhcp; +interface IDhcpEventCallbacks { + oneway void onLeasesChanged(in List<android.net.dhcp.DhcpLeaseParcelable> newLeases); + oneway void onNewPrefixRequest(in android.net.IpPrefix currentPrefix); +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/dhcp/IDhcpServer.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/dhcp/IDhcpServer.aidl new file mode 100644 index 0000000..1109f35 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/dhcp/IDhcpServer.aidl
@@ -0,0 +1,45 @@ +/** + * Copyright (c) 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.dhcp; +/* @hide */ +interface IDhcpServer { + oneway void start(in android.net.INetworkStackStatusCallback cb) = 0; + oneway void startWithCallbacks(in android.net.INetworkStackStatusCallback statusCb, in android.net.dhcp.IDhcpEventCallbacks eventCb) = 3; + oneway void updateParams(in android.net.dhcp.DhcpServingParamsParcel params, in android.net.INetworkStackStatusCallback cb) = 1; + oneway void stop(in android.net.INetworkStackStatusCallback cb) = 2; + const int STATUS_UNKNOWN = 0; + const int STATUS_SUCCESS = 1; + const int STATUS_INVALID_ARGUMENT = 2; + const int STATUS_UNKNOWN_ERROR = 3; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/dhcp/IDhcpServerCallbacks.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/dhcp/IDhcpServerCallbacks.aidl new file mode 100644 index 0000000..ab8577c --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/dhcp/IDhcpServerCallbacks.aidl
@@ -0,0 +1,38 @@ +/** + * Copyright (c) 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.dhcp; +/* @hide */ +interface IDhcpServerCallbacks { + oneway void onDhcpServerCreated(int statusCode, in android.net.dhcp.IDhcpServer server); +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/ip/IIpClient.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/ip/IIpClient.aidl new file mode 100644 index 0000000..3ba1d0b --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/ip/IIpClient.aidl
@@ -0,0 +1,58 @@ +/** + * Copyright (c) 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.ip; +/* @hide */ +interface IIpClient { + oneway void completedPreDhcpAction(); + oneway void confirmConfiguration(); + oneway void readPacketFilterComplete(in byte[] data); + oneway void shutdown(); + oneway void startProvisioning(in android.net.ProvisioningConfigurationParcelable req); + oneway void stop(); + oneway void setTcpBufferSizes(in String tcpBufferSizes); + oneway void setHttpProxy(in android.net.ProxyInfo proxyInfo); + oneway void setMulticastFilter(boolean enabled); + oneway void addKeepalivePacketFilter(int slot, in android.net.TcpKeepalivePacketDataParcelable pkt); + oneway void removeKeepalivePacketFilter(int slot); + oneway void setL2KeyAndGroupHint(in String l2Key, in String cluster); + oneway void addNattKeepalivePacketFilter(int slot, in android.net.NattKeepalivePacketDataParcelable pkt); + oneway void notifyPreconnectionComplete(boolean success); + oneway void updateLayer2Information(in android.net.Layer2InformationParcelable info); + const int PROV_IPV4_DISABLED = 0x00; + const int PROV_IPV4_STATIC = 0x01; + const int PROV_IPV4_DHCP = 0x02; + const int PROV_IPV6_DISABLED = 0x00; + const int PROV_IPV6_SLAAC = 0x01; + const int PROV_IPV6_LINKLOCAL = 0x02; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/ip/IIpClientCallbacks.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/ip/IIpClientCallbacks.aidl new file mode 100644 index 0000000..9d36419 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/ip/IIpClientCallbacks.aidl
@@ -0,0 +1,54 @@ +/** + * Copyright (c) 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.ip; +/* @hide */ +interface IIpClientCallbacks { + oneway void onIpClientCreated(in android.net.ip.IIpClient ipClient); + oneway void onPreDhcpAction(); + oneway void onPostDhcpAction(); + oneway void onNewDhcpResults(in android.net.DhcpResultsParcelable dhcpResults); + oneway void onProvisioningSuccess(in android.net.LinkProperties newLp); + oneway void onProvisioningFailure(in android.net.LinkProperties newLp); + oneway void onLinkPropertiesChange(in android.net.LinkProperties newLp); + oneway void onReachabilityLost(in String logMsg); + oneway void onQuit(); + oneway void installPacketFilter(in byte[] filter); + oneway void startReadPacketFilter(); + oneway void setFallbackMulticastFilter(boolean enabled); + oneway void setNeighborDiscoveryOffload(boolean enable); + oneway void onPreconnectionStart(in List<android.net.Layer2PacketParcelable> packets); + oneway void onReachabilityFailure(in android.net.networkstack.aidl.ip.ReachabilityLossInfoParcelable lossInfo); + oneway void setMaxDtimMultiplier(int multiplier); + const int DTIM_MULTIPLIER_RESET = 0; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/networkstack/aidl/NetworkMonitorParameters.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/networkstack/aidl/NetworkMonitorParameters.aidl new file mode 100644 index 0000000..2ab9db0 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/networkstack/aidl/NetworkMonitorParameters.aidl
@@ -0,0 +1,41 @@ +/** + * + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.networkstack.aidl; +@JavaDerive(equals=true, toString=true) +parcelable NetworkMonitorParameters { + android.net.NetworkAgentConfig networkAgentConfig; + android.net.NetworkCapabilities networkCapabilities; + android.net.LinkProperties linkProperties; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/networkstack/aidl/dhcp/DhcpOption.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/networkstack/aidl/dhcp/DhcpOption.aidl new file mode 100644 index 0000000..eea3e0d --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/networkstack/aidl/dhcp/DhcpOption.aidl
@@ -0,0 +1,39 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.networkstack.aidl.dhcp; +@JavaDerive(toString=true) +parcelable DhcpOption { + byte type; + @nullable byte[] value; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/networkstack/aidl/ip/ReachabilityLossInfoParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/networkstack/aidl/ip/ReachabilityLossInfoParcelable.aidl new file mode 100644 index 0000000..bb88434 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/networkstack/aidl/ip/ReachabilityLossInfoParcelable.aidl
@@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.networkstack.aidl.ip; +@JavaDerive(equals=true, toString=true) @JavaOnlyImmutable +parcelable ReachabilityLossInfoParcelable { + String message; + android.net.networkstack.aidl.ip.ReachabilityLossReason reason; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/networkstack/aidl/ip/ReachabilityLossReason.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/networkstack/aidl/ip/ReachabilityLossReason.aidl new file mode 100644 index 0000000..f9bb3c4 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/17/android/net/networkstack/aidl/ip/ReachabilityLossReason.aidl
@@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.networkstack.aidl.ip; +@Backing(type="int") +enum ReachabilityLossReason { + ROAM, + CONFIRM, + ORGANIC, +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/INetworkMonitor.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/INetworkMonitor.aidl index fc6a70e..fb13c0c 100644 --- a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/INetworkMonitor.aidl +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/INetworkMonitor.aidl
@@ -49,12 +49,12 @@ const int NETWORK_TEST_RESULT_VALID = 0; const int NETWORK_TEST_RESULT_INVALID = 1; const int NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY = 2; - const int NETWORK_VALIDATION_RESULT_VALID = 1; - const int NETWORK_VALIDATION_RESULT_PARTIAL = 2; - const int NETWORK_VALIDATION_RESULT_SKIPPED = 4; - const int NETWORK_VALIDATION_PROBE_DNS = 4; - const int NETWORK_VALIDATION_PROBE_HTTP = 8; - const int NETWORK_VALIDATION_PROBE_HTTPS = 16; - const int NETWORK_VALIDATION_PROBE_FALLBACK = 32; - const int NETWORK_VALIDATION_PROBE_PRIVDNS = 64; + const int NETWORK_VALIDATION_RESULT_VALID = 0x01; + const int NETWORK_VALIDATION_RESULT_PARTIAL = 0x02; + const int NETWORK_VALIDATION_RESULT_SKIPPED = 0x04; + const int NETWORK_VALIDATION_PROBE_DNS = 0x04; + const int NETWORK_VALIDATION_PROBE_HTTP = 0x08; + const int NETWORK_VALIDATION_PROBE_HTTPS = 0x10; + const int NETWORK_VALIDATION_PROBE_FALLBACK = 0x20; + const int NETWORK_VALIDATION_PROBE_PRIVDNS = 0x40; }
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/ip/IIpClient.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/ip/IIpClient.aidl index a97511e..3ba1d0b 100644 --- a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/ip/IIpClient.aidl +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/ip/IIpClient.aidl
@@ -49,10 +49,10 @@ oneway void addNattKeepalivePacketFilter(int slot, in android.net.NattKeepalivePacketDataParcelable pkt); oneway void notifyPreconnectionComplete(boolean success); oneway void updateLayer2Information(in android.net.Layer2InformationParcelable info); - const int PROV_IPV4_DISABLED = 0; - const int PROV_IPV4_STATIC = 1; - const int PROV_IPV4_DHCP = 2; - const int PROV_IPV6_DISABLED = 0; - const int PROV_IPV6_SLAAC = 1; - const int PROV_IPV6_LINKLOCAL = 2; + const int PROV_IPV4_DISABLED = 0x00; + const int PROV_IPV4_STATIC = 0x01; + const int PROV_IPV4_DHCP = 0x02; + const int PROV_IPV6_DISABLED = 0x00; + const int PROV_IPV6_SLAAC = 0x01; + const int PROV_IPV6_LINKLOCAL = 0x02; }
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/ip/IIpClientCallbacks.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/ip/IIpClientCallbacks.aidl index 24bbf64..9d36419 100644 --- a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/ip/IIpClientCallbacks.aidl +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/ip/IIpClientCallbacks.aidl
@@ -49,4 +49,6 @@ oneway void setNeighborDiscoveryOffload(boolean enable); oneway void onPreconnectionStart(in List<android.net.Layer2PacketParcelable> packets); oneway void onReachabilityFailure(in android.net.networkstack.aidl.ip.ReachabilityLossInfoParcelable lossInfo); + oneway void setMaxDtimMultiplier(int multiplier); + const int DTIM_MULTIPLIER_RESET = 0; }
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/networkstack/aidl/ip/ReachabilityLossReason.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/networkstack/aidl/ip/ReachabilityLossReason.aidl index 70a7db2..f9bb3c4 100644 --- a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/networkstack/aidl/ip/ReachabilityLossReason.aidl +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/networkstack/aidl/ip/ReachabilityLossReason.aidl
@@ -34,7 +34,7 @@ package android.net.networkstack.aidl.ip; @Backing(type="int") enum ReachabilityLossReason { - ROAM = 0, - CONFIRM = 1, - ORGANIC = 2, + ROAM, + CONFIRM, + ORGANIC, }
diff --git a/common/networkstackclient/src/android/net/ip/IIpClientCallbacks.aidl b/common/networkstackclient/src/android/net/ip/IIpClientCallbacks.aidl index 932672e..5bb0cf3 100644 --- a/common/networkstackclient/src/android/net/ip/IIpClientCallbacks.aidl +++ b/common/networkstackclient/src/android/net/ip/IIpClientCallbacks.aidl
@@ -72,4 +72,20 @@ // Called when the internal IpReachabilityMonitor (if enabled) has detected the loss of a // critical number of required neighbors or DHCP roaming fails. void onReachabilityFailure(in ReachabilityLossInfoParcelable lossInfo); + + // Reset the DTIM multiplier to the default hardware driver value. + const int DTIM_MULTIPLIER_RESET = 0; + + // Set maximum acceptable DTIM multiplier to hardware driver. Any multiplier larger than the + // maximum value must not be accepted, it will cause packet loss higher than what the system + // can accept, which will cause unexpected behavior for apps, and may interrupt the network + // connection. + // + // DTIM multiplier controls how often the device should wake up to receive multicast/broadcast + // packets. Typically the wake up interval is decided by multiplier * AP's DTIM period if + // multiplier is non-zero. For example, when hardware driver sets the DTIM multiplier to 2, it + // means device wakes up once every 2 DTIM periods, 50% of multicast packets will be dropped. + // Setting DTIM multiplier to DTIM_MULTIPLIER_RESET(0) applies the hardware driver default + // value. + void setMaxDtimMultiplier(int multiplier); }
diff --git a/common/networkstackclient/src/android/net/ip/IpClientCallbacks.java b/common/networkstackclient/src/android/net/ip/IpClientCallbacks.java index 7cf46f5..e46d4c9 100644 --- a/common/networkstackclient/src/android/net/ip/IpClientCallbacks.java +++ b/common/networkstackclient/src/android/net/ip/IpClientCallbacks.java
@@ -146,4 +146,11 @@ // onReachabilityLost method. onReachabilityLost(lossInfo.message); } + + /** + * Set maximum acceptable DTIM multiplier to hardware driver. + * + * @param multiplier an integer maximum DTIM multiplier value to set. + */ + public void setMaxDtimMultiplier(int multiplier) {} }
diff --git a/common/networkstackclient/src/android/net/ip/IpClientUtil.java b/common/networkstackclient/src/android/net/ip/IpClientUtil.java index 7636abd..b953d35 100644 --- a/common/networkstackclient/src/android/net/ip/IpClientUtil.java +++ b/common/networkstackclient/src/android/net/ip/IpClientUtil.java
@@ -198,6 +198,12 @@ mCb.onReachabilityFailure(lossInfo); } + // Set maximum acceptable DTIM multiplier to hardware driver. + @Override + public void setMaxDtimMultiplier(int multiplier) { + mCb.setMaxDtimMultiplier(multiplier); + } + @Override public int getInterfaceVersion() { return this.VERSION;
diff --git a/jni/network_stack_utils_jni.cpp b/jni/network_stack_utils_jni.cpp index e63483f..5d84f57 100644 --- a/jni/network_stack_utils_jni.cpp +++ b/jni/network_stack_utils_jni.cpp
@@ -99,17 +99,15 @@ } } -static void network_stack_utils_attachDhcpFilter(JNIEnv *env, jclass clazz, jobject javaFd, jboolean dropMF) { - const __u32 frag_mask = static_cast<__u32>((dropMF ? IP_MF : 0) | IP_OFFMASK); - - sock_filter filter_code[] = { +static void network_stack_utils_attachDhcpFilter(JNIEnv *env, jclass clazz, jobject javaFd) { + static sock_filter filter_code[] = { // Check the protocol is UDP. BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv4Protocol), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_UDP, 0, 6), // Check this is not a fragment. BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kIPv4FlagsOffset), - BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, frag_mask, 4, 0), + BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, IP_MF | IP_OFFMASK, 4, 0), // Get the IP header length. BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, kEtherHeaderLen), @@ -253,7 +251,7 @@ static const JNINativeMethod gNetworkStackUtilsMethods[] = { /* name, signature, funcPtr */ { "addArpEntry", "([B[BLjava/lang/String;Ljava/io/FileDescriptor;)V", (void*) network_stack_utils_addArpEntry }, - { "attachDhcpFilter", "(Ljava/io/FileDescriptor;Z)V", (void*) network_stack_utils_attachDhcpFilter }, + { "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) network_stack_utils_attachDhcpFilter }, { "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) network_stack_utils_attachRaFilter }, { "attachControlPacketFilter", "(Ljava/io/FileDescriptor;I)V", (void*) network_stack_utils_attachControlPacketFilter }, };
diff --git a/src/android/net/apf/ApfGenerator.java b/src/android/net/apf/ApfGenerator.java index db51186..ee713c5 100644 --- a/src/android/net/apf/ApfGenerator.java +++ b/src/android/net/apf/ApfGenerator.java
@@ -282,10 +282,6 @@ } // Calculate distance from end of this instruction to instruction.offset. final int targetLabelOffset = targetLabelInstruction.offset - (offset + size()); - if (targetLabelOffset < 0) { - throw new IllegalInstructionException("backward branches disallowed; label: " + - mTargetLabel); - } return targetLabelOffset; }
diff --git a/src/android/net/apf/DnsUtils.java b/src/android/net/apf/DnsUtils.java new file mode 100644 index 0000000..6b0bdc4 --- /dev/null +++ b/src/android/net/apf/DnsUtils.java
@@ -0,0 +1,413 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.apf; + +import static android.net.apf.ApfGenerator.Register.R0; +import static android.net.apf.ApfGenerator.Register.R1; + +import static com.android.net.module.util.NetworkStackConstants.ETHER_HEADER_LEN; +import static com.android.net.module.util.NetworkStackConstants.UDP_HEADER_LEN; + +import androidx.annotation.NonNull; + +/** + * Utility class that generates generating APF filters for DNS packets. + */ +public class DnsUtils { + + /** Length of the DNS header. */ + private static final int DNS_HEADER_LEN = 12; + /** Offset of the qdcount field within the DNS header. */ + private static final int DNS_QDCOUNT_OFFSET = 4; + + // Static labels + private static final String LABEL_START_MATCH = "start_match"; + private static final String LABEL_PARSE_DNS_LABEL = "parse_dns_label"; + private static final String LABEL_FIND_NEXT_DNS_QUESTION = "find_next_dns_question"; + + // Length of the pointers used by compressed names. + private static final int LABEL_SIZE = Byte.BYTES; + private static final int POINTER_SIZE = Short.BYTES; + private static final int QUESTION_HEADER_SIZE = Short.BYTES + Short.BYTES; + private static final int LABEL_AND_QUESTION_HEADER_SIZE = LABEL_SIZE + QUESTION_HEADER_SIZE; + private static final int POINTER_AND_QUESTION_HEADER_SIZE = POINTER_SIZE + QUESTION_HEADER_SIZE; + + /** Memory slot that stores the offset within the packet of the DNS header. */ + private static final int SLOT_DNS_HEADER_OFFSET = 1; + /** Memory slot that stores the current parsing offset. */ + private static final int SLOT_CURRENT_PARSE_OFFSET = 2; + /** + * Memory slot that stores the offset after the current question, if the code is currently + * parsing a pointer, or 0 if it is not. + */ + private static final int SLOT_AFTER_POINTER_OFFSET = 3; + /** + * Contains qdcount remaining, as a negative number. For example, will be -1 when starting to + * parse a DNS packet with one question in it. It's stored as a negative number because adding 1 + * is much easier than subtracting 1 (which can't be done just by adding -1, because that just + * adds 254). + */ + private static final int SLOT_NEGATIVE_QDCOUNT_REMAINING = 6; + /** Memory slot used by the jump table. */ + private static final int SLOT_RETURN_VALUE_INDEX = 10; + + /** + * APF function: parse_dns_label + * + * Parses a label potentially containing a pointer, and calculates the label length and the + * offset of the label data. + * + * Inputs: + * - m[SLOT_DNS_HEADER_OFFSET]: offset of DNS header + * - m[SLOT_CURRENT_PARSE_OFFSET]: current parsing offset + * - m[SLOT_AFTER_POINTER_OFFSET]: offset after the question (e.g., offset of the next question, + * or offset of the answer section) if a pointer is being chased, 0 otherwise + * - m[SLOT_RETURN_VALUE_INDEX]: index into return jump table + * + * Outputs: + * - R1: label length + * - m[SLOT_CURRENT_PARSE_OFFSET]: offset of label text + */ + private static void genParseDnsLabel(ApfGenerator gen, JumpTable jumpTable) throws Exception { + final String labelParseDnsLabelReal = "parse_dns_label_real"; + final String labelPointerOffsetStored = "pointer_offset_stored"; + + /** + * :parse_dns_label + * // Load parsing offset. + * LDM R1, 2 // R1 = parsing offset. (All indexed loads use R1.) + */ + gen.defineLabel(LABEL_PARSE_DNS_LABEL); + gen.addLoadFromMemory(R1, SLOT_CURRENT_PARSE_OFFSET); + + + /** + * // Check that we’re in the DNS packet, i.e., that R1 >= m[SLOT_DNS_HEADER_OFFSET]. + * LDM R0, 1 // R0 = DNS header offset + * JGT R0, R1, DROP // Bad pointer. Drop. + */ + gen.addLoadFromMemory(R0, SLOT_DNS_HEADER_OFFSET); + gen.addJumpIfR0GreaterThanR1(ApfGenerator.DROP_LABEL); + + /** + * // Now parse the label. + * LDBX R0, [R1+0] // R0 = label length, R1 = parsing offset + * AND R0, 0xc0 // Is this a pointer? + * + * JEQ R0, 0, :parse_dns_label_real + */ + gen.addLoad8Indexed(R0, 0); + gen.addAnd(0xc0); + gen.addJumpIfR0Equals(0, labelParseDnsLabelReal); + + + /** + * // If we’re not already chasing a pointer, store offset after pointer into + * // m[SLOT_AFTER_POINTER_OFFSET]. + * LDM R0, 3 // R0 = previous offset after pointer + * JNE 0, :pointer_offset_stored + * MOV R0, R1 // R0 = R1 + * ADD R0, 6 // R0 = offset after pointer and record + * STM R0, 3 // Store offset after pointer + */ + gen.addLoadFromMemory(R0, SLOT_AFTER_POINTER_OFFSET); + gen.addJumpIfR0NotEquals(0, labelPointerOffsetStored); + gen.addMove(R0); + gen.addAdd(POINTER_AND_QUESTION_HEADER_SIZE); + gen.addStoreToMemory(R0, SLOT_AFTER_POINTER_OFFSET); + + /** + * :pointer_offset_stored + * LDHX R0, [R1+0] // R0 = 2-byte pointer value + * AND R0, 0x3ff // R0 = pointer destination offset (from DNS header) + * LDM R1, 1 // R1 = offset in packet of DNS header + * ADD R0, R1 // R0 = pointer destination offset + * LDM R1, 2 // R1 = current parsing offset + * JEQ R0, R1, DROP // Drop if pointer points here... + * JGT R0, R1, DROP // ... or after here (must point backwards) + * STM R0, 2 // Set next parsing offset to pointer destination + */ + gen.defineLabel(labelPointerOffsetStored); + gen.addLoad16Indexed(R0, 0); + gen.addAnd(0x3ff); + gen.addLoadFromMemory(R1, SLOT_DNS_HEADER_OFFSET); + gen.addAddR1(); + gen.addLoadFromMemory(R1, SLOT_CURRENT_PARSE_OFFSET); + gen.addJumpIfR0EqualsR1(ApfGenerator.DROP_LABEL); + gen.addJumpIfR0GreaterThanR1(ApfGenerator.DROP_LABEL); + gen.addStoreToMemory(R0, SLOT_CURRENT_PARSE_OFFSET); + + /** // Pointer chased. Parse starting from the pointer destination (which may also be a + * pointer). + * JMP :parse_dns_label + */ + gen.addJump(LABEL_PARSE_DNS_LABEL); + + /** + * :parse_real_label + * // This is where the real (non-pointer) label starts. + * // Load label length into R1, and return to caller. + * // m[SLOT_CURRENT_PARSE_OFFSET] already contains label offset. + * LDHX R1 [R1+0] // R1 = label length + */ + gen.defineLabel(labelParseDnsLabelReal); + gen.addLoad8Indexed(R1, 0); + + /** // Return + * LDM R0, 10 + * JMP :jump_table + */ + gen.addLoadFromMemory(R0, SLOT_RETURN_VALUE_INDEX); + gen.addJump(jumpTable.getStartLabel()); + } + + /** + * APF function: find_next_dns_question + * + * Finds the next question in the question section, or drops the packet if there is none. + * + * Inputs: + * - m[SLOT_CURRENT_PARSE_OFFSET]: current parsing offset + * - m[SLOT_AFTER_POINTER_OFFSET]: offset after first pointer in name, or 0 if not chasing a + * pointer + * - m[SLOT_NEGATIVE_QDCOUNT_REMAINING]: qdcount remaining, as a negative number. This is + * because adding 1 is much easier than subtracting 1 (which can't be done just by + * adding -1, because that just adds 254) + * - m[SLOT_RETURN_VALUE_INDEX]: index into return jump table + * + * Outputs: + * None + */ + private static void genFindNextDnsQuestion(ApfGenerator gen, JumpTable jumpTable) + throws Exception { + final String labelFindNextDnsQuestionFollow = "find_next_dns_question_follow"; + final String labelFindNextDnsQuestionLabel = "find_next_dns_question_label"; + final String labelFindNextDnsQuestionLoop = "find_next_dns_question_loop"; + final String labelFindNextDnsQuestionNoPointer = "find_next_dns_question_no_pointer"; + final String labelFindNextDnsQuestionReturn = "find_next_dns_question_return"; + + // Function entry point. + gen.defineLabel(LABEL_FIND_NEXT_DNS_QUESTION); + + // Are we chasing a pointer? + gen.addLoadFromMemory(R0, SLOT_AFTER_POINTER_OFFSET); + gen.addJumpIfR0Equals(0, labelFindNextDnsQuestionFollow); + + // If so, offset after the pointer and question is stored in m[SLOT_AFTER_POINTER_OFFSET]. + // Move parsing offset there, clear m[SLOT_AFTER_POINTER_OFFSET], and return. + gen.addStoreToMemory(R0, SLOT_CURRENT_PARSE_OFFSET); + gen.addLoadImmediate(R0, 0); + gen.addStoreToMemory(R0, SLOT_AFTER_POINTER_OFFSET); + gen.addJump(labelFindNextDnsQuestionReturn); + + // We weren't chasing a pointer. Loop, following the label chain, until we reach a + // zero-length label or a pointer. At the beginning of the loop, the current parsing offset + // is m[SLOT_CURRENT_PARSE_OFFSET]. Move it to R1 and keep it in R1 throughout the loop. + gen.defineLabel(labelFindNextDnsQuestionFollow); + gen.addLoadFromMemory(R1, SLOT_CURRENT_PARSE_OFFSET); + + // Load label length. + gen.defineLabel(labelFindNextDnsQuestionLoop); + gen.addLoad8Indexed(R0, 0); + // Is it a pointer? + gen.addAnd(0xc0); + gen.addJumpIfR0Equals(0, labelFindNextDnsQuestionNoPointer); + // It's a pointer. Skip the pointer and question, and return. + gen.addLoadImmediate(R0, POINTER_AND_QUESTION_HEADER_SIZE); + gen.addAddR1(); + gen.addStoreToMemory(R0, SLOT_CURRENT_PARSE_OFFSET); + gen.addJump(labelFindNextDnsQuestionReturn); + + // R1 still contains parsing offset. + gen.defineLabel(labelFindNextDnsQuestionNoPointer); + gen.addLoad8Indexed(R0, 0); + + // Zero-length label? We're done. + // Skip the label (1 byte) and query (2 bytes qtype, 2 bytes qclass) and return. + gen.addJumpIfR0NotEquals(0, labelFindNextDnsQuestionLabel); + gen.addLoadImmediate(R0, LABEL_AND_QUESTION_HEADER_SIZE); + gen.addAddR1(); + gen.addStoreToMemory(R0, SLOT_CURRENT_PARSE_OFFSET); + gen.addJump(labelFindNextDnsQuestionReturn); + + // Non-zero length label. Consume it and continue. + gen.defineLabel(labelFindNextDnsQuestionLabel); + gen.addAdd(1); + gen.addAddR1(); + gen.addMove(R1); + gen.addJump(labelFindNextDnsQuestionLoop); + + gen.defineLabel(labelFindNextDnsQuestionReturn); + + // Is this the last question? If so, drop. + gen.addLoadFromMemory(R0, SLOT_NEGATIVE_QDCOUNT_REMAINING); + gen.addAdd(1); + gen.addStoreToMemory(R0, SLOT_NEGATIVE_QDCOUNT_REMAINING); + gen.addJumpIfR0Equals(0, ApfGenerator.DROP_LABEL); + + // If not, return. + gen.addJump(jumpTable.getStartLabel()); + } + + /** @return jump label that points to the start of a DNS label's parsing code. */ + private static String getStartMatchLabel(int labelIndex) { + return "dns_parse_" + labelIndex; + } + + /** @return jump label used while parsing the specified DNS label. */ + private static String getPostMatchJumpTargetForLabel(int labelIndex) { + return "dns_parsed_" + labelIndex; + } + + /** @return jump label used when the match for the specified DNS label fails. */ + private static String getNoMatchLabel(int labelIndex) { + return "dns_nomatch_" + labelIndex; + } + + private static void addMatchLabel(@NonNull ApfGenerator gen, @NonNull JumpTable jumpTable, + int labelIndex, @NonNull String label, @NonNull String nextLabel) throws Exception { + final String parsedLabel = getPostMatchJumpTargetForLabel(labelIndex); + final String noMatchLabel = getNoMatchLabel(labelIndex); + gen.defineLabel(getStartMatchLabel(labelIndex)); + + // Store return address. + gen.addLoadImmediate(R0, jumpTable.getIndex(parsedLabel)); + gen.addStoreToMemory(R0, SLOT_RETURN_VALUE_INDEX); + + // Call the parse_label function. + gen.addJump(LABEL_PARSE_DNS_LABEL); + + gen.defineLabel(parsedLabel); + + // If label length is 0, this is the end of the name and the match failed. + gen.addSwap(); // Move label length from R1 to R0 + gen.addJumpIfR0Equals(0, noMatchLabel); + + // Label parsed, check it matches what we're looking for. + gen.addJumpIfR0NotEquals(label.length(), noMatchLabel); + gen.addLoadFromMemory(R0, SLOT_CURRENT_PARSE_OFFSET); + gen.addAdd(1); + gen.addJumpIfBytesNotEqual(R0, label.getBytes(), noMatchLabel); + + // Prep offset of next label. + gen.addAdd(label.length()); + gen.addStoreToMemory(R0, SLOT_CURRENT_PARSE_OFFSET); + + // Match, go to next label. + gen.addJump(nextLabel); + + // Match failed. Go to next name, and restart from the first match. + gen.defineLabel(noMatchLabel); + gen.addLoadImmediate(R1, jumpTable.getIndex(LABEL_START_MATCH)); + gen.addStoreToMemory(R1, SLOT_RETURN_VALUE_INDEX); + gen.addJump(LABEL_FIND_NEXT_DNS_QUESTION); + } + + /** + * Generates a filter that accepts DNS packet that ask for the specified name. + * + * The filter supports compressed DNS names and scanning through multiple questions in the same + * packet, e.g., as used by MDNS. However, it currently only supports one DNS name. + * + * Limitations: + * <ul> + * <li>Filter size is just under 300 bytes for a typical question. + * <li>Because the bytecode extensively uses backwards jumps, it can hit the APF interpreter + * instruction limit. This limit causes the APF interpreter to accept the packet once it has + * executed a number of instructions equal to the program length in bytes. + * A program that consists *only* of this filter will be able to execute just under 300 + * instructions, and will be able to correctly drop packets with two questions but not three + * questions. In a real APF setup, there will be other code (e.g., RA filtering) which counts + * against the limit, so the filter should be able to parse packets with more questions. + * <li>Matches are case-sensitive. This is due to the use of JNEBS to match DNS labels and is + * likely impossible to overcome without interpreter changes. + * </ul> + * + * TODO: + * <ul> + * <li>Add unit tests for the parse_dns_label and find_next_dns_question functions. + * <li>Support accepting more than one name. + * <li>For devices where power saving is a priority (e.g., flat panel TVs), add support for + * dropping packets with more than X queries, to ensure the filter will drop the packet rather + * than hit the instruction limit. + * </ul> + */ + public static void generateFilter(ApfGenerator gen, String[] labels) throws Exception { + final int etherPlusUdpLen = ETHER_HEADER_LEN + UDP_HEADER_LEN; + + final String labelJumpTable = "jump_table"; + + // Initialize parsing + /** + * - R1: length of IP header. + * - m[SLOT_DNS_HEADER_OFFSET]: offset of DNS header + * - m[SLOT_CURRENT_PARSE_OFFSET]: current parsing offset (start of question section) + * - m[SLOT_AFTER_POINTER_OFFSET]: offset after first pointer in name, must be 0 when + * starting a new name + * - m[SLOT_NEGATIVE_QDCOUNT_REMAINING]: negative qdcount + */ + // Move IP header length to R0 and use it to find the DNS header offset. + // TODO: this uses R1 for consistency with ApfFilter#generateMdnsFilterLocked. Evaluate + // using R0 instead. + gen.addMove(R0); + gen.addAdd(etherPlusUdpLen); + gen.addStoreToMemory(R0, SLOT_DNS_HEADER_OFFSET); + + gen.addAdd(DNS_QDCOUNT_OFFSET); + gen.addMove(R1); + gen.addLoad16Indexed(R1, 0); + gen.addNeg(R1); + gen.addStoreToMemory(R1, SLOT_NEGATIVE_QDCOUNT_REMAINING); + + gen.addAdd(DNS_HEADER_LEN - DNS_QDCOUNT_OFFSET); + gen.addStoreToMemory(R0, SLOT_CURRENT_PARSE_OFFSET); + + gen.addLoadImmediate(R0, 0); + gen.addStoreToMemory(R0, SLOT_AFTER_POINTER_OFFSET); + + gen.addJump(LABEL_START_MATCH); + + // Create JumpTable but + final JumpTable table = new JumpTable(labelJumpTable, SLOT_RETURN_VALUE_INDEX); + + // Generate bytecode for parse_label function. + genParseDnsLabel(gen, table); + genFindNextDnsQuestion(gen, table); + + // Populate jump table. Should be before the code that calls to it (i.e., the addMatchLabel + // calls below) because otherwise all the jumps are backwards, and backwards jumps are more + // expensive (5 bytes of bytecode) + for (int i = 0; i < labels.length; i++) { + table.addLabel(getPostMatchJumpTargetForLabel(i)); + } + table.addLabel(LABEL_START_MATCH); + table.generate(gen); + + // Add match statements for name. + gen.defineLabel(LABEL_START_MATCH); + for (int i = 0; i < labels.length; i++) { + final String nextLabel = (i == labels.length - 1) + ? ApfGenerator.PASS_LABEL + : getStartMatchLabel(i + 1); + addMatchLabel(gen, table, i, labels[i], nextLabel); + } + gen.addJump(ApfGenerator.DROP_LABEL); + } + + private DnsUtils() { + } +}
diff --git a/src/android/net/apf/JumpTable.java b/src/android/net/apf/JumpTable.java new file mode 100644 index 0000000..b449697 --- /dev/null +++ b/src/android/net/apf/JumpTable.java
@@ -0,0 +1,136 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.apf; + +import static android.net.apf.ApfGenerator.Register.R0; + +import androidx.annotation.NonNull; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; + +/** + * A table that stores program labels to jump to. + * + * This is needed to implement subroutines because APF jump targets must be known at compile + * time and cannot be computed dynamically. + * + * At compile time, any code that calls a subroutine must: + * + * <ul> + * <li>Define a label (via {@link ApfGenerator#defineLabel}) immediately after the code that invokes + * the subroutine. + * <li>Add the label to the jump table using {@link #addLabel}. + * <li>Generate the jump table in the program. + * </ul> + * + * <p>At runtime, before invoking the subroutine, the APF code must store the index of the return + * label (obtained via {@link #getIndex}) into the jump table's return address memory slot, and then + * jump to the subroutine. To return to the caller, the subroutine must jump to the label returned + * by {@link #getStartLabel}, and the jump table will then jump to the return label. + * + * <p>Implementation details: + * <ul> + * <li>The jumps are added to the program in the same order as the labels were added. + * <li>Using the jump table will overwrite the value of register R0. + * <li>If, before calling a subroutine, the APF code stores a nonexistent return label index, then + * the jump table will pass the packet. This cannot happen if the code correctly obtains the + * label using {@link #getIndex}, as that would throw an exception when generating the program. + * </ul> + * + * For example: + * <pre> + * JumpTable t = new JumpTable("my_jump_table", 7); + * t.addLabel("jump_1"); + * ... + * t.addLabel("after_parsing"); + * ... + * t.addLabel("after_subroutine"); + * t.generate(gen); + *</pre> + * generates the following APF code: + * <pre> + * :my_jump_table + * ldm r0, 7 + * jeq r0, 0, jump_1 + * jeq r0, 1, after_parsing + * jeq r0, 2, after_subroutine + * jmp DROP + * </pre> + */ +public class JumpTable { + /** Maps jump indices to jump labels. LinkedHashMap guarantees iteration in insertion order. */ + private final Map<String, Integer> mJumpLabels = new LinkedHashMap<>(); + /** Label to jump to to execute this jump table. */ + private final String mStartLabel; + /** Memory slot that contains the return value index. */ + private final int mReturnAddressMemorySlot; + + private int mIndex = 0; + + public JumpTable(@NonNull String startLabel, int returnAddressMemorySlot) { + Objects.requireNonNull(startLabel); + mStartLabel = startLabel; + if (returnAddressMemorySlot < 0 + || returnAddressMemorySlot >= ApfGenerator.FIRST_PREFILLED_MEMORY_SLOT) { + throw new IllegalArgumentException("Invalid memory slot " + returnAddressMemorySlot); + } + mReturnAddressMemorySlot = returnAddressMemorySlot; + } + + /** Returns the label to jump to to start executing the table. */ + @NonNull + public String getStartLabel() { + return mStartLabel; + } + + /** + * Adds a jump label to this table. Passing a label that was already added is not an error. + * + * @param label the label to add + */ + public void addLabel(@NonNull String label) { + Objects.requireNonNull(label); + if (mJumpLabels.putIfAbsent(label, mIndex) == null) mIndex++; + } + + /** + * Gets the index of a previously-added label. + * @return the label's index. + * @throws NoSuchElementException if the label was never added. + */ + public int getIndex(@NonNull String label) { + final Integer index = mJumpLabels.get(label); + if (index == null) throw new NoSuchElementException("Unknown label " + label); + return index; + } + + /** Generates APF code for this jump table */ + public void generate(@NonNull ApfGenerator gen) + throws ApfGenerator.IllegalInstructionException { + gen.defineLabel(mStartLabel); + gen.addLoadFromMemory(R0, mReturnAddressMemorySlot); + for (Map.Entry<String, Integer> e : mJumpLabels.entrySet()) { + gen.addJumpIfR0Equals(e.getValue(), e.getKey()); + } + // Cannot happen unless the program is malformed (i.e., the APF code loads an invalid return + // label index before jumping to the subroutine. + gen.addJump(ApfGenerator.PASS_LABEL); + } +}
diff --git a/src/android/net/dhcp/DhcpClient.java b/src/android/net/dhcp/DhcpClient.java index 8f59839..7ffd500 100644 --- a/src/android/net/dhcp/DhcpClient.java +++ b/src/android/net/dhcp/DhcpClient.java
@@ -52,7 +52,6 @@ import static com.android.net.module.util.NetworkStackConstants.IPV4_CONFLICT_ANNOUNCE_NUM; import static com.android.net.module.util.NetworkStackConstants.IPV4_CONFLICT_PROBE_NUM; import static com.android.net.module.util.SocketUtils.closeSocketQuietly; -import static com.android.networkstack.util.NetworkStackUtils.DHCP_DISABLE_DROP_MF; import static com.android.networkstack.util.NetworkStackUtils.DHCP_INIT_REBOOT_VERSION; import static com.android.networkstack.util.NetworkStackUtils.DHCP_IPV6_ONLY_PREFERRED_VERSION; import static com.android.networkstack.util.NetworkStackUtils.DHCP_IP_CONFLICT_DETECT_VERSION; @@ -693,11 +692,9 @@ @Override protected FileDescriptor createFd() { - boolean dropMF = !DeviceConfigUtils.getDeviceConfigPropertyBoolean( - NAMESPACE_CONNECTIVITY, DHCP_DISABLE_DROP_MF, false); try { mPacketSock = Os.socket(AF_PACKET, SOCK_RAW | SOCK_NONBLOCK, 0 /* protocol */); - NetworkStackUtils.attachDhcpFilter(mPacketSock, dropMF); + NetworkStackUtils.attachDhcpFilter(mPacketSock); final SocketAddress addr = makePacketSocketAddress(ETH_P_IP, mIface.index); Os.bind(mPacketSock, addr); } catch (SocketException | ErrnoException e) {
diff --git a/src/android/net/dhcp6/Dhcp6AdvertisePacket.java b/src/android/net/dhcp6/Dhcp6AdvertisePacket.java new file mode 100644 index 0000000..3eb5503 --- /dev/null +++ b/src/android/net/dhcp6/Dhcp6AdvertisePacket.java
@@ -0,0 +1,55 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.dhcp6; + +import static com.android.net.module.util.NetworkStackConstants.DHCP_MAX_LENGTH; + +import androidx.annotation.NonNull; + +import java.nio.ByteBuffer; + +/** + * DHCPv6 ADVERTISE packet class, a server sends an Advertise message to indicate that it's + * available for DHCP service, in response to a Solicit message received from a client. + * + * https://tools.ietf.org/html/rfc8415#page-24 + */ +public class Dhcp6AdvertisePacket extends Dhcp6Packet { + /** + * Generates an advertise packet with the specified parameters. + */ + Dhcp6AdvertisePacket(int transId, @NonNull final byte[] clientDuid, + @NonNull final byte[] serverDuid, final byte[] iapd) { + super(transId, (short) 0 /* secs */, clientDuid, serverDuid, iapd); + } + + /** + * Build a DHCPv6 Advertise message with the specific parameters. + */ + public ByteBuffer buildPacket() { + final ByteBuffer packet = ByteBuffer.allocate(DHCP_MAX_LENGTH); + final int msgTypeAndTransId = (DHCP6_MESSAGE_TYPE_ADVERTISE << 24) | (mTransId & 0x0FFF); + packet.putInt(msgTypeAndTransId); + + addTlv(packet, DHCP6_CLIENT_IDENTIFIER, mClientDuid); + addTlv(packet, DHCP6_SERVER_IDENTIFIER, mServerDuid); + addTlv(packet, DHCP6_IA_PD, mIaPd); + + packet.flip(); + return packet; + } +}
diff --git a/src/android/net/dhcp6/Dhcp6Client.java b/src/android/net/dhcp6/Dhcp6Client.java new file mode 100644 index 0000000..3fa7cb0 --- /dev/null +++ b/src/android/net/dhcp6/Dhcp6Client.java
@@ -0,0 +1,132 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.dhcp6; + +import static android.system.OsConstants.AF_INET6; +import static android.system.OsConstants.IPPROTO_UDP; +import static android.system.OsConstants.SOCK_DGRAM; +import static android.system.OsConstants.SOCK_NONBLOCK; + +import static com.android.net.module.util.NetworkStackConstants.ALL_DHCP_RELAY_AGENTS_AND_SERVERS; +import static com.android.net.module.util.NetworkStackConstants.DHCP6_CLIENT_PORT; +import static com.android.net.module.util.NetworkStackConstants.DHCP6_SERVER_PORT; +import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ANY; + +import android.content.Context; +import android.net.ip.IpClient; +import android.net.util.SocketUtils; +import android.os.Handler; +import android.system.ErrnoException; +import android.system.Os; +import android.util.Log; + +import androidx.annotation.NonNull; + +import com.android.internal.util.StateMachine; +import com.android.net.module.util.InterfaceParams; +import com.android.net.module.util.PacketReader; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.net.SocketException; +import java.nio.ByteBuffer; + +/** + * A DHCPv6 client. + * + * So far only support IA_PD (prefix delegation), not for IA_NA/IA_TA yet. + * + * @hide + */ +public class Dhcp6Client extends StateMachine { + private static final String TAG = Dhcp6Client.class.getSimpleName(); + private static final boolean DBG = true; + + // Internal messages. + // Dhcp6Client shares the same handler with IpClient, define the base command range for + // both public and private messages used in Dhcp6Client, to avoid commands overlap. + private static final int PRIVATE_BASE = IpClient.DHCP6CLIENT_CMD_BASE + 100; + private static final int CMD_RECEIVED_PACKET = PRIVATE_BASE + 1; + + @NonNull private final Context mContext; + + // State variables. + @NonNull private final StateMachine mController; + @NonNull private final String mIfaceName; + + private Dhcp6Client(@NonNull final Context context, @NonNull final StateMachine controller, + @NonNull final InterfaceParams iface) { + super(TAG, controller.getHandler()); + + mContext = context; + mController = controller; + mIfaceName = iface.name; + + // TODO: add state machine initialization. + } + + private class Dhcp6PacketHandler extends PacketReader { + private FileDescriptor mUdpSock; + + Dhcp6PacketHandler(Handler handler) { + super(handler); + } + + @Override + protected void handlePacket(byte[] recvbuf, int length) { + try { + final Dhcp6Packet packet = Dhcp6Packet.decodePacket(recvbuf, length); + if (DBG) Log.d(TAG, "Received packet: " + packet); + sendMessage(CMD_RECEIVED_PACKET, packet); + } catch (Dhcp6Packet.ParseException e) { + Log.e(TAG, "Can't parse DHCPv6 packet: " + e.getMessage()); + } + } + + @Override + protected FileDescriptor createFd() { + try { + mUdpSock = Os.socket(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); + SocketUtils.bindSocketToInterface(mUdpSock, mIfaceName); + Os.bind(mUdpSock, IPV6_ADDR_ANY, DHCP6_CLIENT_PORT); + } catch (SocketException | ErrnoException e) { + Log.e(TAG, "Error creating udp socket", e); + closeFd(mUdpSock); + mUdpSock = null; + return null; + } + return mUdpSock; + } + + @Override + protected int readPacket(FileDescriptor fd, byte[] packetBuffer) throws Exception { + try { + return Os.read(fd, packetBuffer, 0, packetBuffer.length); + } catch (IOException | ErrnoException e) { + Log.e(TAG, "Fail to read packet"); + throw e; + } + } + + public int transmitPacket(final ByteBuffer buf) throws ErrnoException, SocketException { + int ret = Os.sendto(mUdpSock, buf.array(), 0 /* byteOffset */, + buf.limit() /* byteCount */, 0 /* flags */, ALL_DHCP_RELAY_AGENTS_AND_SERVERS, + DHCP6_SERVER_PORT); + return ret; + } + } +}
diff --git a/src/android/net/dhcp6/Dhcp6Packet.java b/src/android/net/dhcp6/Dhcp6Packet.java new file mode 100644 index 0000000..6d77d4e --- /dev/null +++ b/src/android/net/dhcp6/Dhcp6Packet.java
@@ -0,0 +1,442 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.dhcp6; + +import static com.android.net.module.util.NetworkStackConstants.DHCP_MAX_OPTION_LEN; + +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; + +import com.android.net.module.util.Struct; +import com.android.net.module.util.structs.IaPrefixOption; + +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; + +/** + * Defines basic data and operations needed to build and use packets for the + * DHCPv6 protocol. Subclasses create the specific packets used at each + * stage of the negotiation. + * + * @hide + */ +public class Dhcp6Packet { + + /** + * DHCPv6 Message Type. + */ + public static final byte DHCP6_MESSAGE_TYPE_SOLICIT = 1; + public static final byte DHCP6_MESSAGE_TYPE_ADVERTISE = 2; + public static final byte DHCP6_MESSAGE_TYPE_REQUEST = 3; + public static final byte DHCP6_MESSAGE_TYPE_CONFIRM = 4; + public static final byte DHCP6_MESSAGE_TYPE_RENEW = 5; + public static final byte DHCP6_MESSAGE_TYPE_REBIND = 6; + public static final byte DHCP6_MESSAGE_TYPE_REPLY = 7; + public static final byte DHCP6_MESSAGE_TYPE_RELEASE = 8; + public static final byte DHCP6_MESSAGE_TYPE_DECLINE = 9; + public static final byte DHCP6_MESSAGE_TYPE_RECONFIGURE = 10; + public static final byte DHCP6_MESSAGE_TYPE_INFORMATION_REQUEST = 11; + public static final byte DHCP6_MESSAGE_TYPE_RELAY_FORW = 12; + public static final byte DHCP6_MESSAGE_TYPE_RELAY_REPL = 13; + + /** + * DHCPv6 Optional Type: Client Identifier. + * DHCPv6 message from client must have this option. + */ + public static final byte DHCP6_CLIENT_IDENTIFIER = 1; + @NonNull + protected final byte[] mClientDuid; + + /** + * DHCPv6 Optional Type: Server Identifier. + */ + public static final byte DHCP6_SERVER_IDENTIFIER = 2; + protected final byte[] mServerDuid; + + /** + * DHCPv6 Optional Type: Elapsed time. + */ + public static final byte DHCP6_ELAPSED_TIME = 8; + protected final short mSecs; + + /** + * DHCPv6 Optional Type: Status Code. + */ + public static final byte DHCP6_STATUS_CODE = 13; + protected short mStatusCode; + protected String mStatusMsg; + + public static final short STATUS_SUCCESS = 0; + public static final short STATUS_UNSPEC_FAIL = 1; + public static final short STATUS_NO_ADDR_AVAI = 2; + public static final short STATUS_NO_BINDING = 3; + public static final short STATUS_PREFIX_NOT_ONLINK = 4; + public static final short STATUS_USE_MULTICAST = 5; + public static final short STATUS_NO_PREFIX_AVAI = 6; + + /** + * DHCPv6 Optional Type: IA_PD. + */ + public static final byte DHCP6_IA_PD = 25; + @NonNull + protected final byte[] mIaPd; + @NonNull + protected PrefixDelegation mPrefixDelegation; + + /** + * The transaction identifier used in this particular DHCPv6 negotiation + */ + protected final int mTransId; + + /** + * The unique identifier for IA_NA, IA_TA, IA_PD used in this particular DHCPv6 negotiation + */ + protected int mIaId; + + Dhcp6Packet(int transId, short secs, @NonNull final byte[] clientDuid, final byte[] serverDuid, + @NonNull final byte[] iapd) { + mTransId = transId; + mSecs = secs; + mClientDuid = clientDuid; + mServerDuid = serverDuid; + mIaPd = iapd; + } + + /** + * Returns the transaction ID. + */ + public int getTransactionId() { + return mTransId; + } + + /** + * Returns IA_ID associated to IA_PD. + */ + public int getIaId() { + return mIaId; + } + + /** + * Returns the client's DUID. + */ + @NonNull + public byte[] getClientDuid() { + return mClientDuid; + } + + /** + * Returns the server's DUID. + */ + public byte[] getServerDuid() { + return mServerDuid; + } + + /** + * A class to take DHCPv6 IA_PD option allocated from server. + * https://www.rfc-editor.org/rfc/rfc8415.html#section-21.21 + */ + public static class PrefixDelegation { + public int iaid; + public int t1; + public int t2; + public final IaPrefixOption ipo; + + PrefixDelegation(int iaid, int t1, int t2, final IaPrefixOption ipo) { + this.iaid = iaid; + this.t1 = t1; + this.t2 = t2; + this.ipo = ipo; + } + + @Override + public String toString() { + return "Prefix Delegation: iaid " + iaid + ", t1 " + t1 + ", t2 " + t2 + + ", prefix " + ipo; + } + } + + /** + * DHCPv6 packet parsing exception. + */ + public static class ParseException extends Exception { + ParseException(String msg) { + super(msg); + } + } + + private static void skipOption(@NonNull final ByteBuffer packet, int optionLen) + throws BufferUnderflowException { + for (int i = 0; i < optionLen; i++) { + packet.get(); + } + } + + /** + * Reads a string of specified length from the buffer. + * + * TODO: move to a common place which can be shared with DhcpClient. + */ + private static String readAsciiString(@NonNull final ByteBuffer buf, int byteCount, + boolean isNullOk) { + final byte[] bytes = new byte[byteCount]; + buf.get(bytes); + return readAsciiString(bytes, isNullOk); + } + + private static String readAsciiString(@NonNull final byte[] payload, boolean isNullOk) { + final byte[] bytes = payload; + int length = bytes.length; + if (!isNullOk) { + // Stop at the first null byte. This is because some DHCP options (e.g., the domain + // name) are passed to netd via FrameworkListener, which refuses arguments containing + // null bytes. We don't do this by default because vendorInfo is an opaque string which + // could in theory contain null bytes. + for (length = 0; length < bytes.length; length++) { + if (bytes[length] == 0) { + break; + } + } + } + return new String(bytes, 0, length, StandardCharsets.US_ASCII); + } + + /** + * Creates a concrete Dhcp6Packet from the supplied ByteBuffer. + * + * The buffer only starts with a UDP encapsulation (i.e. DHCPv6 message). A subset of the + * optional parameters are parsed and are stored in object fields. Client/Server message + * format: + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | msg-type | transaction-id | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * . options . + * . (variable number and length) . + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + @VisibleForTesting + static Dhcp6Packet decodePacket(@NonNull final ByteBuffer packet) throws ParseException { + short secs = 0; + byte[] iapd = null; + byte[] serverDuid = null; + byte[] clientDuid = null; + short statusCode = STATUS_SUCCESS; + String statusMsg = null; + + packet.order(ByteOrder.BIG_ENDIAN); + + // DHCPv6 message contents. + final int msgTypeAndTransId = packet.getInt(); + final byte messageType = (byte) (msgTypeAndTransId >> 24); + final int transId = msgTypeAndTransId & 0x0FFF; + + /** + * Parse DHCPv6 options, option format: + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | option-code | option-len | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | option-data | + * | (option-len octets) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + while (packet.hasRemaining()) { + try { + final short optionType = packet.getShort(); + final int optionLen = packet.getShort() & 0xFFFF; + int expectedLen = 0; + + switch(optionType) { + case DHCP6_SERVER_IDENTIFIER: + expectedLen = optionLen; + final byte[] sduid = new byte[expectedLen]; + packet.get(sduid, 0 /* offset */, expectedLen); + serverDuid = sduid; + break; + case DHCP6_CLIENT_IDENTIFIER: + expectedLen = optionLen; + final byte[] cduid = new byte[expectedLen]; + packet.get(cduid, 0 /* offset */, expectedLen); + clientDuid = cduid; + break; + case DHCP6_IA_PD: + expectedLen = optionLen; + final byte[] bytes = new byte[expectedLen]; + packet.get(bytes, 0 /* offset */, expectedLen); + iapd = bytes; + break; + case DHCP6_ELAPSED_TIME: + expectedLen = 2; + secs = packet.getShort(); + break; + case DHCP6_STATUS_CODE: + expectedLen = optionLen; + statusCode = packet.getShort(); + statusMsg = readAsciiString(packet, expectedLen - 2, false /* isNullOk */); + break; + default: + expectedLen = optionLen; + // BufferUnderflowException will be thrown if option is truncated. + skipOption(packet, optionLen); + break; + } + if (expectedLen != optionLen) { + throw new ParseException( + "Invalid length " + optionLen + " for option " + optionType + + ", expected " + expectedLen); + } + } catch (BufferUnderflowException e) { + throw new ParseException(e.getMessage()); + } + } + + Dhcp6Packet newPacket; + + switch(messageType) { + case DHCP6_MESSAGE_TYPE_SOLICIT: + newPacket = new Dhcp6SolicitPacket(transId, secs, clientDuid, iapd); + break; + case DHCP6_MESSAGE_TYPE_ADVERTISE: + newPacket = new Dhcp6AdvertisePacket(transId, clientDuid, serverDuid, iapd); + break; + case DHCP6_MESSAGE_TYPE_REQUEST: + newPacket = new Dhcp6RequestPacket(transId, secs, clientDuid, serverDuid, iapd); + break; + case DHCP6_MESSAGE_TYPE_REPLY: + newPacket = new Dhcp6ReplyPacket(transId, clientDuid, serverDuid, iapd); + break; + case DHCP6_MESSAGE_TYPE_RENEW: + newPacket = new Dhcp6RenewPacket(transId, secs, clientDuid, serverDuid, iapd); + break; + case DHCP6_MESSAGE_TYPE_REBIND: + newPacket = new Dhcp6RebindPacket(transId, secs, clientDuid, iapd); + break; + default: + throw new ParseException("Unimplemented DHCP6 message type %d" + messageType); + } + + if (iapd != null) { + final ByteBuffer buffer = ByteBuffer.wrap(iapd); + final int iaid = buffer.getInt(); + final int t1 = buffer.getInt(); + final int t2 = buffer.getInt(); + final IaPrefixOption ipo = Struct.parse(IaPrefixOption.class, buffer); + newPacket.mPrefixDelegation = new PrefixDelegation(iaid, t1, t2, ipo); + newPacket.mIaId = iaid; + } + newPacket.mStatusCode = statusCode; + newPacket.mStatusMsg = statusMsg; + + return newPacket; + } + + /** + * Parse a packet from an array of bytes, stopping at the given length. + */ + public static Dhcp6Packet decodePacket(@NonNull final byte[] packet, int length) + throws ParseException { + final ByteBuffer buffer = ByteBuffer.wrap(packet, 0, length).order(ByteOrder.BIG_ENDIAN); + return decodePacket(buffer); + } + + /** + * Adds an optional parameter containing an array of bytes. + */ + protected static void addTlv(ByteBuffer buf, short type, @NonNull byte[] payload) { + if (payload.length > DHCP_MAX_OPTION_LEN) { + throw new IllegalArgumentException("DHCP option too long: " + + payload.length + " vs. " + DHCP_MAX_OPTION_LEN); + } + buf.putShort(type); + buf.putShort((short) payload.length); + buf.put(payload); + } + + /** + * Adds an optional parameter containing a short integer. + */ + protected static void addTlv(ByteBuffer buf, short type, short value) { + buf.putShort(type); + buf.putShort((short) 2); + buf.putShort(value); + } + + /** + * Builds a DHCPv6 SOLICIT packet from the required specified parameters. + */ + public static ByteBuffer buildSolicitPacket(int transId, short secs, @NonNull final byte[] iapd, + @NonNull final byte[] clientDuid) { + final Dhcp6SolicitPacket pkt = new Dhcp6SolicitPacket(transId, secs, clientDuid, iapd); + return pkt.buildPacket(); + } + + /** + * Builds a DHCPv6 ADVERTISE packet from the required specified parameters. + */ + public static ByteBuffer buildAdvertisePacket(int transId, @NonNull final byte[] iapd, + @NonNull final byte[] clientDuid, @NonNull final byte[] serverDuid) { + final Dhcp6AdvertisePacket pkt = + new Dhcp6AdvertisePacket(transId, clientDuid, serverDuid, iapd); + return pkt.buildPacket(); + } + + /** + * Builds a DHCPv6 REPLY packet from the required specified parameters. + */ + public static ByteBuffer buildReplyPacket(int transId, @NonNull final byte[] iapd, + @NonNull final byte[] clientDuid, @NonNull final byte[] serverDuid) { + final Dhcp6ReplyPacket pkt = new Dhcp6ReplyPacket(transId, clientDuid, serverDuid, iapd); + return pkt.buildPacket(); + } + + /** + * Builds a DHCPv6 REQUEST packet from the required specified parameters. + */ + public static ByteBuffer buildRequestPacket(int transId, short secs, @NonNull final byte[] iapd, + @NonNull final byte[] clientDuid, @NonNull final byte[] serverDuid) { + final Dhcp6RequestPacket pkt = + new Dhcp6RequestPacket(transId, secs, clientDuid, serverDuid, iapd); + return pkt.buildPacket(); + } + + /** + * Builds a DHCPv6 RENEW packet from the required specified parameters. + */ + public static ByteBuffer buildRenewPacket(int transId, short secs, @NonNull final byte[] iapd, + @NonNull final byte[] clientDuid, @NonNull final byte[] serverDuid) { + final Dhcp6RenewPacket pkt = + new Dhcp6RenewPacket(transId, secs, clientDuid, serverDuid, iapd); + return pkt.buildPacket(); + } + + /** + * Builds a DHCPv6 REBIND packet from the required specified parameters. + */ + public static ByteBuffer buildRebindPacket(int transId, short secs, @NonNull final byte[] iapd, + @NonNull final byte[] clientDuid) { + final Dhcp6RebindPacket pkt = new Dhcp6RebindPacket(transId, secs, clientDuid, iapd); + return pkt.buildPacket(); + } +}
diff --git a/src/android/net/dhcp6/Dhcp6RebindPacket.java b/src/android/net/dhcp6/Dhcp6RebindPacket.java new file mode 100644 index 0000000..10c10b9 --- /dev/null +++ b/src/android/net/dhcp6/Dhcp6RebindPacket.java
@@ -0,0 +1,56 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.dhcp6; + +import static com.android.net.module.util.NetworkStackConstants.DHCP_MAX_LENGTH; + +import androidx.annotation.NonNull; + +import java.nio.ByteBuffer; + +/** + * DHCPv6 REBIND packet class, a client sends a Rebind message to any available server to extend + * the lifetimes on the leases assigned to the client and to update other configuration parameters. + * This message is sent after a client receives no response to a Renew message. + * + * https://tools.ietf.org/html/rfc8415#page-24 + */ +public class Dhcp6RebindPacket extends Dhcp6Packet { + /** + * Generates a rebind packet with the specified parameters. + */ + Dhcp6RebindPacket(int transId, short secs, @NonNull final byte[] clientDuid, + @NonNull final byte[] iapd) { + super(transId, secs, clientDuid, null /* serverDuid */, iapd); + } + + /** + * Build a DHCPv6 Rebind message with the specific parameters. + */ + public ByteBuffer buildPacket() { + final ByteBuffer packet = ByteBuffer.allocate(DHCP_MAX_LENGTH); + final int msgTypeAndTransId = (DHCP6_MESSAGE_TYPE_REBIND << 24) | (mTransId & 0x0FFF); + packet.putInt(msgTypeAndTransId); + + addTlv(packet, DHCP6_CLIENT_IDENTIFIER, getClientDuid()); + addTlv(packet, DHCP6_ELAPSED_TIME, mSecs); + addTlv(packet, DHCP6_IA_PD, mIaPd); + + packet.flip(); + return packet; + } +}
diff --git a/src/android/net/dhcp6/Dhcp6RenewPacket.java b/src/android/net/dhcp6/Dhcp6RenewPacket.java new file mode 100644 index 0000000..8715b59 --- /dev/null +++ b/src/android/net/dhcp6/Dhcp6RenewPacket.java
@@ -0,0 +1,57 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.dhcp6; + +import static com.android.net.module.util.NetworkStackConstants.DHCP_MAX_LENGTH; + +import androidx.annotation.NonNull; + +import java.nio.ByteBuffer; + +/** + * DHCPv6 RENEW packet class, a client sends an Renew message to the server that originally + * provided the client's leases and configuration parameters to extend the lifetimes on the + * leases assigned to the client and to update other configuration parameters. + * + * https://tools.ietf.org/html/rfc8415#page-24 + */ +public class Dhcp6RenewPacket extends Dhcp6Packet { + /** + * Generates a renew packet with the specified parameters. + */ + Dhcp6RenewPacket(int transId, short secs, @NonNull final byte[] clientDuid, + @NonNull final byte[] serverDuid, final byte[] iapd) { + super(transId, secs, clientDuid, serverDuid, iapd); + } + + /** + * Build a DHCPv6 Renew message with the specific parameters. + */ + public ByteBuffer buildPacket() { + final ByteBuffer packet = ByteBuffer.allocate(DHCP_MAX_LENGTH); + final int msgTypeAndTransId = (DHCP6_MESSAGE_TYPE_RENEW << 24) | (mTransId & 0x0FFF); + packet.putInt(msgTypeAndTransId); + + addTlv(packet, DHCP6_SERVER_IDENTIFIER, getServerDuid()); + addTlv(packet, DHCP6_CLIENT_IDENTIFIER, getClientDuid()); + addTlv(packet, DHCP6_ELAPSED_TIME, mSecs); + addTlv(packet, DHCP6_IA_PD, mIaPd); + + packet.flip(); + return packet; + } +}
diff --git a/src/android/net/dhcp6/Dhcp6ReplyPacket.java b/src/android/net/dhcp6/Dhcp6ReplyPacket.java new file mode 100644 index 0000000..4b37478 --- /dev/null +++ b/src/android/net/dhcp6/Dhcp6ReplyPacket.java
@@ -0,0 +1,56 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.dhcp6; + +import static com.android.net.module.util.NetworkStackConstants.DHCP_MAX_LENGTH; + +import androidx.annotation.NonNull; + +import java.nio.ByteBuffer; + +/** + * DHCPv6 REPLY packet class, a server sends an Reply message containing assigned leases + * and configuration parameters in response to a Solicit, Request, Renew or Rebind messages + * received from a client. + * + * https://tools.ietf.org/html/rfc8415#page-24 + */ +public class Dhcp6ReplyPacket extends Dhcp6Packet { + /** + * Generates a reply packet with the specified parameters. + */ + Dhcp6ReplyPacket(int transId, @NonNull final byte[] clientDuid, + @NonNull final byte[] serverDuid, final byte[] iapd) { + super(transId, (short) 0 /* secs */, clientDuid, serverDuid, iapd); + } + + /** + * Build a DHCPv6 Reply message with the specific parameters. + */ + public ByteBuffer buildPacket() { + final ByteBuffer packet = ByteBuffer.allocate(DHCP_MAX_LENGTH); + final int msgTypeAndTransId = (DHCP6_MESSAGE_TYPE_REPLY << 24) | (mTransId & 0x0FFF); + packet.putInt(msgTypeAndTransId); + + addTlv(packet, DHCP6_CLIENT_IDENTIFIER, mClientDuid); + addTlv(packet, DHCP6_SERVER_IDENTIFIER, mServerDuid); + addTlv(packet, DHCP6_IA_PD, mIaPd); + + packet.flip(); + return packet; + } +}
diff --git a/src/android/net/dhcp6/Dhcp6RequestPacket.java b/src/android/net/dhcp6/Dhcp6RequestPacket.java new file mode 100644 index 0000000..f2f398d --- /dev/null +++ b/src/android/net/dhcp6/Dhcp6RequestPacket.java
@@ -0,0 +1,56 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.dhcp6; + +import static com.android.net.module.util.NetworkStackConstants.DHCP_MAX_LENGTH; + +import androidx.annotation.NonNull; + +import java.nio.ByteBuffer; + +/** + * DHCPv6 REQUEST packet class, a client sends a Request message to request configuration + * parameters, including addresses and/or delegated prefixes from a specific server. + * + * https://tools.ietf.org/html/rfc8415#page-24 + */ +public class Dhcp6RequestPacket extends Dhcp6Packet { + /** + * Generates a request packet with the specified parameters. + */ + Dhcp6RequestPacket(int transId, short secs, @NonNull final byte[] clientDuid, + @NonNull final byte[] serverDuid, final byte[] iapd) { + super(transId, secs, clientDuid, serverDuid, iapd); + } + + /** + * Build a DHCPv6 Request message with the specific parameters. + */ + public ByteBuffer buildPacket() { + final ByteBuffer packet = ByteBuffer.allocate(DHCP_MAX_LENGTH); + final int msgTypeAndTransId = (DHCP6_MESSAGE_TYPE_REQUEST << 24) | (mTransId & 0x0FFF); + packet.putInt(msgTypeAndTransId); + + addTlv(packet, DHCP6_SERVER_IDENTIFIER, getServerDuid()); + addTlv(packet, DHCP6_CLIENT_IDENTIFIER, getClientDuid()); + addTlv(packet, DHCP6_ELAPSED_TIME, mSecs); + addTlv(packet, DHCP6_IA_PD, mIaPd); + + packet.flip(); + return packet; + } +}
diff --git a/src/android/net/dhcp6/Dhcp6SolicitPacket.java b/src/android/net/dhcp6/Dhcp6SolicitPacket.java new file mode 100644 index 0000000..909ff2c --- /dev/null +++ b/src/android/net/dhcp6/Dhcp6SolicitPacket.java
@@ -0,0 +1,54 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.dhcp6; + +import static com.android.net.module.util.NetworkStackConstants.DHCP_MAX_LENGTH; + +import androidx.annotation.NonNull; + +import java.nio.ByteBuffer; + +/** + * DHCPv6 SOLICIT packet class, a client sends a Solicit message to locate DHCPv6 servers. + * + * https://tools.ietf.org/html/rfc8415#page-24 + */ +public class Dhcp6SolicitPacket extends Dhcp6Packet { + /** + * Generates a solicit packet with the specified parameters. + */ + Dhcp6SolicitPacket(int transId, short secs, @NonNull final byte[] clientDuid, + final byte[] iapd) { + super(transId, secs, clientDuid, null /* serverDuid */, iapd); + } + + /** + * Build a DHCPv6 Solicit message with the specific parameters. + */ + public ByteBuffer buildPacket() { + final ByteBuffer packet = ByteBuffer.allocate(DHCP_MAX_LENGTH); + final int msgTypeAndTransId = (DHCP6_MESSAGE_TYPE_SOLICIT << 24) | (mTransId & 0x0FFF); + packet.putInt(msgTypeAndTransId); + + addTlv(packet, DHCP6_ELAPSED_TIME, mSecs); + addTlv(packet, DHCP6_CLIENT_IDENTIFIER, mClientDuid); + addTlv(packet, DHCP6_IA_PD, mIaPd); + + packet.flip(); + return packet; + } +}
diff --git a/src/android/net/ip/IpClient.java b/src/android/net/ip/IpClient.java index fa03e08..5737d87 100644 --- a/src/android/net/ip/IpClient.java +++ b/src/android/net/ip/IpClient.java
@@ -21,6 +21,7 @@ import static android.net.ip.IIpClient.PROV_IPV4_DISABLED; import static android.net.ip.IIpClient.PROV_IPV6_DISABLED; import static android.net.ip.IIpClient.PROV_IPV6_LINKLOCAL; +import static android.net.ip.IIpClientCallbacks.DTIM_MULTIPLIER_RESET; import static android.net.ip.IpReachabilityMonitor.INVALID_REACHABILITY_LOSS_TYPE; import static android.net.ip.IpReachabilityMonitor.nudEventTypeToInt; import static android.net.util.SocketUtils.makePacketSocketAddress; @@ -104,6 +105,7 @@ import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.internal.util.WakeupMessage; +import com.android.net.module.util.CollectionUtils; import com.android.net.module.util.DeviceConfigUtils; import com.android.net.module.util.InterfaceParams; import com.android.net.module.util.SharedLog; @@ -432,6 +434,18 @@ } /** + * Set maximum acceptable DTIM multiplier to hardware driver. + */ + public void setMaxDtimMultiplier(int multiplier) { + log("setMaxDtimMultiplier(" + multiplier + ")"); + try { + mCallback.setMaxDtimMultiplier(multiplier); + } catch (RemoteException e) { + log("Failed to call setMaxDtimMultiplier", e); + } + } + + /** * Get the version of the IIpClientCallbacks AIDL interface. */ public int getInterfaceVersion() { @@ -471,6 +485,7 @@ private static final int CMD_UPDATE_L2KEY_CLUSTER = 15; private static final int CMD_COMPLETE_PRECONNECTION = 16; private static final int CMD_UPDATE_L2INFORMATION = 17; + private static final int CMD_SET_DTIM_MULTIPLIER_AFTER_DELAY = 18; private static final int ARG_LINKPROP_CHANGED_LINKSTATE_DOWN = 0; private static final int ARG_LINKPROP_CHANGED_LINKSTATE_UP = 1; @@ -485,6 +500,9 @@ // IpClient shares a handler with DhcpClient: commands must not overlap public static final int DHCPCLIENT_CMD_BASE = 1000; + // IpClient shares a handler with Dhcp6Client: commands must not overlap + public static final int DHCP6CLIENT_CMD_BASE = 2000; + // Settings and default values. private static final int MAX_LOG_RECORDS = 500; private static final int MAX_PACKET_RECORDS = 100; @@ -494,6 +512,47 @@ private static final int DEFAULT_MIN_RDNSS_LIFETIME = ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q) ? 120 : 0; + // Used to wait for the provisioning to complete eventually and then decide the target + // network type, which gives the accurate hint to set DTIM multiplier. Per current IPv6 + // provisioning connection latency metrics, the latency of 95% can go up to 16s, so pick + // ProvisioningConfiguration.DEFAULT_TIMEOUT_MS value for this delay. + @VisibleForTesting + static final String CONFIG_INITIAL_PROVISIONING_DTIM_DELAY_MS = + "ipclient_initial_provisioning_dtim_delay"; + private static final int DEFAULT_INITIAL_PROVISIONING_DTIM_DELAY_MS = 18000; + + @VisibleForTesting + static final String CONFIG_MULTICAST_LOCK_MAX_DTIM_MULTIPLIER = + "ipclient_multicast_lock_max_dtim_multiplier"; + @VisibleForTesting + static final int DEFAULT_MULTICAST_LOCK_MAX_DTIM_MULTIPLIER = 1; + + @VisibleForTesting + static final String CONFIG_IPV6_ONLY_NETWORK_MAX_DTIM_MULTIPLIER = + "ipclient_ipv6_only_max_dtim_multiplier"; + @VisibleForTesting + static final int DEFAULT_IPV6_ONLY_NETWORK_MAX_DTIM_MULTIPLIER = 2; + + @VisibleForTesting + static final String CONFIG_IPV4_ONLY_NETWORK_MAX_DTIM_MULTIPLIER = + "ipclient_ipv4_only_max_dtim_multiplier"; + @VisibleForTesting + static final int DEFAULT_IPV4_ONLY_NETWORK_MAX_DTIM_MULTIPLIER = 9; + + @VisibleForTesting + static final String CONFIG_DUAL_STACK_MAX_DTIM_MULTIPLIER = + "ipclient_dual_stack_max_dtim_multiplier"; + // The default value for dual-stack networks is the min of maximum DTIM multiplier to use for + // IPv4-only and IPv6-only networks. + @VisibleForTesting + static final int DEFAULT_DUAL_STACK_MAX_DTIM_MULTIPLIER = 2; + + @VisibleForTesting + static final String CONFIG_BEFORE_IPV6_PROV_MAX_DTIM_MULTIPLIER = + "ipclient_before_ipv6_prov_max_dtim_multiplier"; + @VisibleForTesting + static final int DEFAULT_BEFORE_IPV6_PROV_MAX_DTIM_MULTIPLIER = 1; + private static final boolean NO_CALLBACKS = false; private static final boolean SEND_CALLBACKS = true; @@ -583,9 +642,11 @@ private String mCluster; // The cluster for this network, for writing into the memory store private boolean mMulticastFiltering; private long mStartTimeMillis; + private long mInitialProvisioningEndTimeMillis; private MacAddress mCurrentBssid; private boolean mHasDisabledIpv6OrAcceptRaOnProvLoss; private Integer mDadTransmits = null; + private int mMaxDtimMultiplier = DTIM_MULTIPLIER_RESET; /** * Reading the snapshot is an asynchronous operation initiated by invoking @@ -1805,6 +1866,10 @@ maybeSaveNetworkToIpMemoryStore(); if (sendCallbacks) { dispatchCallback(delta, newLp); + // We cannot do this along with onProvisioningSuccess callback, because the network + // can become dual-stack after a success IPv6 provisioning, and the multiplier also + // needs to be updated upon the loss of IPv4 and/or IPv6 provisioning. + updateMaxDtimMultiplier(); } return (delta != PROV_CHANGE_LOST_PROVISIONING); } @@ -2196,6 +2261,12 @@ maybeRestoreInterfaceMtu(); // Reset number of dad_transmits to default value if changed. maybeRestoreDadTransmits(); + // Reset DTIM multiplier to default value if changed. + if (mMaxDtimMultiplier != DTIM_MULTIPLIER_RESET) { + mCallback.setMaxDtimMultiplier(DTIM_MULTIPLIER_RESET); + mMaxDtimMultiplier = DTIM_MULTIPLIER_RESET; + mInitialProvisioningEndTimeMillis = 0; + } } @Override @@ -2374,11 +2445,19 @@ public void enter() { mIpProvisioningMetrics.reset(); mStartTimeMillis = SystemClock.elapsedRealtime(); + final int delay = mDependencies.getDeviceConfigPropertyInt( + CONFIG_INITIAL_PROVISIONING_DTIM_DELAY_MS, + DEFAULT_INITIAL_PROVISIONING_DTIM_DELAY_MS); + mInitialProvisioningEndTimeMillis = mStartTimeMillis + delay; + if (mConfiguration.mProvisioningTimeoutMs > 0) { final long alarmTime = SystemClock.elapsedRealtime() + mConfiguration.mProvisioningTimeoutMs; mProvisioningTimeoutAlarm.schedule(alarmTime); } + // Send a delay message to wait for IP provisioning to complete eventually and set the + // specific DTIM multiplier by checking the target network type. + sendMessageDelayed(CMD_SET_DTIM_MULTIPLIER_AFTER_DELAY, delay); } @Override @@ -2615,6 +2694,7 @@ } else { mCallback.setFallbackMulticastFilter(mMulticastFiltering); } + updateMaxDtimMultiplier(); break; } @@ -2733,6 +2813,10 @@ mDhcpClient = null; break; + case CMD_SET_DTIM_MULTIPLIER_AFTER_DELAY: + updateMaxDtimMultiplier(); + break; + default: return NOT_HANDLED; } @@ -2742,6 +2826,71 @@ } } + /** + * Set the maximum DTIM multiplier to hardware driver per network condition. Any multiplier + * larger than the maximum value must not be accepted, it will cause packet loss higher than + * what the system can accept, which will cause unexpected behavior for apps, and may interrupt + * the network connection. + * + * When Wifi STA is in the power saving mode and the system is suspended, the wakeup interval + * will be set to: + * 1) multiplier * AP's DTIM period if multiplier > 0. + * 2) the driver default value if multiplier <= 0. + * Some implementations may apply an additional cap to wakeup interval in the case of 1). + */ + private void updateMaxDtimMultiplier() { + int multiplier = deriveDtimMultiplier(); + if (mMaxDtimMultiplier == multiplier) return; + + mMaxDtimMultiplier = multiplier; + log("set max DTIM multiplier to " + multiplier); + mCallback.setMaxDtimMultiplier(multiplier); + } + + private int deriveDtimMultiplier() { + final boolean hasIpv4Addr = mLinkProperties.hasIpv4Address(); + // For a host in the network that has only ULA and link-local but no GUA, consider + // that it also has IPv6 connectivity. LinkProperties#isIpv6Provisioned only returns + // true when it has a GUA, so we cannot use it for IPv6-only network case. + final boolean hasIpv6Addr = CollectionUtils.any(mLinkProperties.getLinkAddresses(), + la -> { + final InetAddress address = la.getAddress(); + return (address instanceof Inet6Address) && !address.isLinkLocalAddress(); + }); + + final int multiplier; + if (!mMulticastFiltering) { + multiplier = mDependencies.getDeviceConfigPropertyInt( + CONFIG_MULTICAST_LOCK_MAX_DTIM_MULTIPLIER, + DEFAULT_MULTICAST_LOCK_MAX_DTIM_MULTIPLIER); + } else if (!hasIpv6Addr + && (SystemClock.elapsedRealtime() < mInitialProvisioningEndTimeMillis)) { + // IPv6 provisioning may or may not complete soon in the future, we don't know when + // it will complete, however, setting multiplier to a high value will cause higher + // RA packet loss, that increases the overall IPv6 provisioning latency. So just set + // multiplier to 1 before device gains the IPv6 provisioning, make sure device won't + // miss any RA packet later. + multiplier = mDependencies.getDeviceConfigPropertyInt( + CONFIG_BEFORE_IPV6_PROV_MAX_DTIM_MULTIPLIER, + DEFAULT_BEFORE_IPV6_PROV_MAX_DTIM_MULTIPLIER); + } else if (hasIpv6Addr && !hasIpv4Addr) { + multiplier = mDependencies.getDeviceConfigPropertyInt( + CONFIG_IPV6_ONLY_NETWORK_MAX_DTIM_MULTIPLIER, + DEFAULT_IPV6_ONLY_NETWORK_MAX_DTIM_MULTIPLIER); + } else if (hasIpv4Addr && !hasIpv6Addr) { + multiplier = mDependencies.getDeviceConfigPropertyInt( + CONFIG_IPV4_ONLY_NETWORK_MAX_DTIM_MULTIPLIER, + DEFAULT_IPV4_ONLY_NETWORK_MAX_DTIM_MULTIPLIER); + } else if (hasIpv6Addr && hasIpv4Addr) { + multiplier = mDependencies.getDeviceConfigPropertyInt( + CONFIG_DUAL_STACK_MAX_DTIM_MULTIPLIER, + DEFAULT_DUAL_STACK_MAX_DTIM_MULTIPLIER); + } else { + multiplier = DTIM_MULTIPLIER_RESET; + } + return multiplier; + } + private static class MessageHandlingLogger { public String processedInState; public String receivedInState;
diff --git a/src/android/net/ip/IpClientLinkObserver.java b/src/android/net/ip/IpClientLinkObserver.java index 0771bff..f1fb3da 100644 --- a/src/android/net/ip/IpClientLinkObserver.java +++ b/src/android/net/ip/IpClientLinkObserver.java
@@ -46,6 +46,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.net.module.util.HexDump; +import com.android.net.module.util.InetAddressUtils; import com.android.net.module.util.InterfaceParams; import com.android.net.module.util.SharedLog; import com.android.net.module.util.ip.NetlinkMonitor; @@ -539,7 +540,8 @@ if (!mNetlinkEventParsingEnabled) return; final String[] addresses = new String[opt.servers.length]; for (int i = 0; i < opt.servers.length; i++) { - addresses[i] = opt.servers[i].getHostAddress(); + final Inet6Address addr = opt.servers[i]; + addresses[i] = InetAddressUtils.withScopeId(addr, mIfindex).getHostAddress(); } updateInterfaceDnsServerInfo(opt.header.lifetime, addresses); }
diff --git a/src/android/net/ip/IpReachabilityMonitor.java b/src/android/net/ip/IpReachabilityMonitor.java index b7cb9ce..ff6d65e 100644 --- a/src/android/net/ip/IpReachabilityMonitor.java +++ b/src/android/net/ip/IpReachabilityMonitor.java
@@ -259,7 +259,7 @@ mCm = context.getSystemService(ConnectivityManager.class); mDependencies = dependencies; mMulticastResolicitEnabled = dependencies.isFeatureEnabled(context, - IP_REACHABILITY_MCAST_RESOLICIT_VERSION, false /* defaultEnabled */); + IP_REACHABILITY_MCAST_RESOLICIT_VERSION, true /* defaultEnabled */); mIgnoreIncompleteIpv6DnsServerEnabled = dependencies.isFeatureEnabled(context, IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DNS_SERVER_VERSION, false /* defaultEnabled */);
diff --git a/src/com/android/networkstack/util/NetworkStackUtils.java b/src/com/android/networkstack/util/NetworkStackUtils.java index 5c072de..147c33a 100755 --- a/src/com/android/networkstack/util/NetworkStackUtils.java +++ b/src/com/android/networkstack/util/NetworkStackUtils.java
@@ -17,6 +17,7 @@ package com.android.networkstack.util; import android.content.Context; +import android.net.IpPrefix; import android.net.LinkAddress; import android.net.MacAddress; import android.system.ErrnoException; @@ -26,6 +27,7 @@ import androidx.annotation.Nullable; import com.android.net.module.util.DeviceConfigUtils; +import com.android.net.module.util.HexDump; import java.io.FileDescriptor; import java.io.IOException; @@ -165,11 +167,6 @@ public static final String DHCP_RAPID_COMMIT_ENABLED = "dhcp_rapid_commit_enabled"; /** - * Disable dropping DHCP packets with IPv4 MF flag set. - */ - public static final String DHCP_DISABLE_DROP_MF = "dhcp_disable_drop_mf"; - - /** * Minimum module version at which to enable the DHCP INIT-REBOOT state. */ public static final String DHCP_INIT_REBOOT_VERSION = "dhcp_init_reboot_version"; @@ -326,10 +323,50 @@ } /** + * Convert 48bits MAC address to 64bits link-layer address(EUI64). + * 1. insert the 0xFFFE in the middle of mac address + * 2. flip the 7th bit(universal/local) of the first byte. + */ + public static byte[] macAddressToEui64(@NonNull final MacAddress hwAddr) { + final byte[] eui64 = new byte[8]; + final byte[] mac48 = hwAddr.toByteArray(); + System.arraycopy(mac48 /* src */, 0 /* srcPos */, eui64 /* dest */, 0 /* destPos */, + 3 /* length */); + eui64[3] = (byte) 0xFF; + eui64[4] = (byte) 0xFE; + System.arraycopy(mac48 /* src */, 3 /* srcPos */, eui64 /* dest */, 5 /* destPos */, + 3 /* length */); + eui64[0] = (byte) (eui64[0] ^ 0x02); // flip 7th bit + return eui64; + } + + /** + * Generate an IPv6 address based on the given prefix(/64) and stable interface + * identifier(EUI64). + */ + public static Inet6Address createInet6AddressFromEui64(@NonNull final IpPrefix prefix, + @NonNull final byte[] eui64) { + if (prefix.getPrefixLength() != 64) { + Log.e(TAG, "Invalid IPv6 prefix length " + prefix.getPrefixLength()); + return null; + } + final byte[] address = new byte[16]; + System.arraycopy(prefix.getRawAddress() /* src */, 0 /* srcPos */, address /* dest */, + 0 /* destPos*/, 8 /* length */); + System.arraycopy(eui64 /* src */, 0 /* srcPos */, address /* dest */, 8 /* destPos */, + eui64.length); + try { + return (Inet6Address) InetAddress.getByAddress(address); + } catch (UnknownHostException e) { + Log.e(TAG, "Invalid IPv6 address " + HexDump.toHexString(address), e); + return null; + } + } + + /** * Attaches a socket filter that accepts DHCP packets to the given socket. */ - public static native void attachDhcpFilter(FileDescriptor fd, boolean dropMF) - throws ErrnoException; + public static native void attachDhcpFilter(FileDescriptor fd) throws ErrnoException; /** * Attaches a socket filter that accepts ICMPv6 router advertisements to the given socket.
diff --git a/tests/integration/AndroidManifest.xml b/tests/integration/AndroidManifest.xml index bfd3735..85c971a 100644 --- a/tests/integration/AndroidManifest.xml +++ b/tests/integration/AndroidManifest.xml
@@ -23,7 +23,10 @@ 05-14 00:41:02.723 18330 18330 E AndroidRuntime: java.lang.IllegalStateException: Signature|privileged permissions not in privapp-permissions whitelist: {com.android.server.networkstack.integrationtests: android.permission.CONNECTIVITY_INTERNAL} --> - + <!-- Used by creating test network --> + <uses-permission android:name="android.permission.MANAGE_TEST_NETWORKS" /> + <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> + <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <application android:debuggable="true"> <uses-library android:name="android.test.runner" /> </application>
diff --git a/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java b/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java index e36af57..2fb5ac8 100644 --- a/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java +++ b/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java
@@ -35,6 +35,7 @@ import static android.net.dhcp.DhcpPacket.INFINITE_LEASE; import static android.net.dhcp.DhcpPacket.MIN_V6ONLY_WAIT_MS; import static android.net.dhcp.DhcpResultsParcelableUtil.fromStableParcelable; +import static android.net.ip.IIpClientCallbacks.DTIM_MULTIPLIER_RESET; import static android.net.ip.IpClientLinkObserver.CLAT_PREFIX; import static android.net.ip.IpClientLinkObserver.CONFIG_SOCKET_RECV_BUFSIZE; import static android.net.ip.IpReachabilityMonitor.NUD_MCAST_RESOLICIT_NUM; @@ -86,6 +87,7 @@ import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.contains; import static org.mockito.ArgumentMatchers.longThat; +import static org.mockito.Mockito.after; import static org.mockito.Mockito.any; import static org.mockito.Mockito.argThat; import static org.mockito.Mockito.atLeastOnce; @@ -566,6 +568,8 @@ protected abstract void setFeatureEnabled(String name, boolean enabled); + protected abstract void setDeviceConfigProperty(String name, int value); + protected abstract boolean isFeatureEnabled(String name, boolean defaultEnabled); protected abstract boolean useNetworkStackSignature(); @@ -598,6 +602,21 @@ isIPv6OnlyPreferredEnabled); } + private void setDeviceConfigForMaxDtimMultiplier() { + setDeviceConfigProperty(IpClient.CONFIG_INITIAL_PROVISIONING_DTIM_DELAY_MS, + 500 /* default value */); + setDeviceConfigProperty(IpClient.CONFIG_MULTICAST_LOCK_MAX_DTIM_MULTIPLIER, + IpClient.DEFAULT_MULTICAST_LOCK_MAX_DTIM_MULTIPLIER); + setDeviceConfigProperty(IpClient.CONFIG_IPV6_ONLY_NETWORK_MAX_DTIM_MULTIPLIER, + IpClient.DEFAULT_IPV6_ONLY_NETWORK_MAX_DTIM_MULTIPLIER); + setDeviceConfigProperty(IpClient.CONFIG_IPV4_ONLY_NETWORK_MAX_DTIM_MULTIPLIER, + IpClient.DEFAULT_IPV4_ONLY_NETWORK_MAX_DTIM_MULTIPLIER); + setDeviceConfigProperty(IpClient.CONFIG_DUAL_STACK_MAX_DTIM_MULTIPLIER, + IpClient.DEFAULT_DUAL_STACK_MAX_DTIM_MULTIPLIER); + setDeviceConfigProperty(IpClient.CONFIG_BEFORE_IPV6_PROV_MAX_DTIM_MULTIPLIER, + IpClient.DEFAULT_BEFORE_IPV6_PROV_MAX_DTIM_MULTIPLIER); + } + @Before public void setUp() throws Exception { // Suffix "[0]" or "[1]" is added to the end of test method name after running with @@ -640,6 +659,11 @@ } mIIpClient = makeIIpClient(mIfaceName, mCb); + + // Enable multicast filtering after creating IpClient instance, make the integration test + // more realistic. + mIIpClient.setMulticastFilter(true); + setDeviceConfigForMaxDtimMultiplier(); } protected void setUpMocks() throws Exception { @@ -660,6 +684,12 @@ when(mNetworkStackServiceManager.getIpMemoryStoreService()) .thenReturn(mIpMemoryStoreService); when(mCb.getInterfaceVersion()).thenReturn(IpClient.VERSION_ADDED_REACHABILITY_FAILURE); + // This mock is required, otherwise, ignoreIPv6ProvisioningLoss variable is always true, + // and IpReachabilityMonitor#avoidingBadLinks() will always return false as well, that + // results in the target tested IPv6 off-link DNS server won't be removed from LP and + // notifyLost won't be invoked, or the wrong code path when receiving RA with 0 router + // liftime. + when(mCm.shouldAvoidBadWifi()).thenReturn(true); mDependencies.setDeviceConfigProperty(IpClient.CONFIG_MIN_RDNSS_LIFETIME, 67); mDependencies.setDeviceConfigProperty(DhcpClient.DHCP_RESTART_CONFIG_DELAY, 10); @@ -699,7 +729,14 @@ final TestNetworkInterface iface = runAsShell(MANAGE_TEST_NETWORKS, () -> { final TestNetworkManager tnm = inst.getContext().getSystemService(TestNetworkManager.class); - return tnm.createTapInterface(); + try { + return tnm.createTapInterface(true /* carrierUp */, true /* bringUp */, + true /* disableIpv6ProvisioningDelay */); + } catch (NoSuchMethodError e) { + // createTapInterface(boolean, boolean, boolean) has been introduced since T, + // use the legancy API if the method is not found on previous platforms. + return tnm.createTapInterface(); + } }); mIfaceName = iface.getInterfaceName(); mClientMac = getIfaceMacAddr(mIfaceName).toByteArray(); @@ -979,7 +1016,7 @@ startIpClientProvisioning(prov.build()); if (!isPreconnectionEnabled) { - verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false); + verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(true); } verify(mCb, never()).onProvisioningFailure(any()); } @@ -1287,7 +1324,7 @@ mDependencies.mDhcpClient.sendMessage(DhcpClient.CMD_TIMEOUT); } } - verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false); + verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(true); final LinkAddress ipAddress = new LinkAddress(CLIENT_ADDR, PREFIX_LENGTH); verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetCfg(ifConfig.capture()); @@ -2009,6 +2046,33 @@ reset(mCb); } + @Test + public void testRaRdnss_Ipv6LinkLocalDns() throws Exception { + ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() + .withoutIpReachabilityMonitor() + .withoutIPv4() + .build(); + startIpClientProvisioning(config); + + final ByteBuffer pio = buildPioOption(600, 300, "2001:db8:1::/64"); + // put an IPv6 link-local DNS server + final ByteBuffer rdnss = buildRdnssOption(600, ROUTER_LINK_LOCAL.getHostAddress()); + // put SLLA option to avoid address resolution for "fe80::1" + final ByteBuffer slla = buildSllaOption(); + final ByteBuffer ra = buildRaPacket(pio, rdnss, slla); + + waitForRouterSolicitation(); + mPacketReader.sendResponse(ra); + + final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class); + verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture()); + final LinkProperties lp = captor.getValue(); + assertNotNull(lp); + assertEquals(1, lp.getDnsServers().size()); + assertEquals(ROUTER_LINK_LOCAL, (Inet6Address) lp.getDnsServers().get(0)); + assertTrue(lp.isIpv6Provisioned()); + } + private void expectNat64PrefixUpdate(InOrder inOrder, IpPrefix expected) throws Exception { inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange( argThat(lp -> Objects.equals(expected, lp.getNat64Prefix()))); @@ -2352,7 +2416,7 @@ // Force IpClient transition to RunningState from PreconnectionState. mIIpClient.notifyPreconnectionComplete(false /* success */); HandlerUtils.waitForIdle(mDependencies.mDhcpClient.getHandler(), TEST_TIMEOUT_MS); - verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false); + verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(true); } @Test @@ -2852,13 +2916,22 @@ final InOrder inOrder = inOrder(mCb); final CompletableFuture<LinkProperties> lpFuture = new CompletableFuture<>(); - doIpv6OnlyProvisioning(inOrder, ra); - - // Start IPv4 provisioning and wait until entire provisioning completes. + // Start IPv4 provisioning first and wait IPv4 provisioning to succeed, and then start + // IPv6 provisioning, which is more realistic and avoid the flaky case of both IPv4 and + // IPv6 provisioning complete at the same time. handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S, true /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU, null /* serverSentUrl */); + verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(any()); + + waitForRouterSolicitation(); + mPacketReader.sendResponse(ra); + + // Wait until we see both success IPv4 and IPv6 provisioning, then there would be 4 + // addresses in LinkProperties, they are IPv4 address, IPv6 link-local address, stable + // privacy address and privacy address. verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange(argThat(x -> { if (!x.isIpv4Provisioned() || !x.isIpv6Provisioned()) return false; + if (x.getLinkAddresses().size() != 4) return false; lpFuture.complete(x); return true; })); @@ -2867,13 +2940,14 @@ assertNotNull(lp); assertTrue(lp.getDnsServers().contains(dnsServer)); assertTrue(lp.getDnsServers().contains(SERVER_ADDR)); + assertHasAddressThat("link-local address", lp, x -> x.getAddress().isLinkLocalAddress()); + assertHasAddressThat("privacy address", lp, this::isPrivacyAddress); + assertHasAddressThat("stable privacy address", lp, this::isStablePrivacyAddress); return lp; } private void doDualStackProvisioning(boolean shouldDisableAcceptRa) throws Exception { - when(mCm.shouldAvoidBadWifi()).thenReturn(true); - final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() .withoutIpReachabilityMonitor() .build(); @@ -2884,7 +2958,12 @@ // not strictly necessary. setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */, false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); - mIpc.startProvisioning(config); + // Both signature and root tests can use this function to do dual-stack provisioning. + if (useNetworkStackSignature()) { + mIpc.startProvisioning(config); + } else { + mIIpClient.startProvisioning(config.toStableParcelable()); + } performDualStackProvisioning(); } @@ -3063,7 +3142,7 @@ // Force IpClient transition to RunningState from PreconnectionState. mIpc.notifyPreconnectionComplete(true /* success */); HandlerUtils.waitForIdle(mDependencies.mDhcpClient.getHandler(), TEST_TIMEOUT_MS); - verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false); + verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(true); // DHCP server SHOULD NOT honor the Rapid-Commit option if the response would // contain the IPv6-only Preferred option to the client, instead respond with @@ -3246,7 +3325,7 @@ false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); startIpClientProvisioning(prov.build()); - verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false); + verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(true); verify(mCb, never()).onProvisioningFailure(any()); return getNextDhcpPacket(); @@ -3798,7 +3877,7 @@ setFeatureEnabled(NetworkStackUtils.IP_REACHABILITY_MCAST_RESOLICIT_VERSION, isMulticastResolicitEnabled); startIpClientProvisioning(config); - verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false); + verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(true); doIpv6OnlyProvisioning(); // Simulate the roaming. @@ -3936,11 +4015,6 @@ final boolean isIgnoreIncompleteIpv6DnsServerEnabled, final boolean isIgnoreIncompleteIpv6DefaultRouterEnabled, final boolean expectNeighborLost) throws Exception { - // This mock is required, otherwise, IpReachabilityMonitor#avoidingBadLinks() will always - // return false, that results in the target tested IPv6 off-link DNS server won't be removed - // from LP and notifyLost won't be invoked. - when(mCm.shouldAvoidBadWifi()).thenReturn(true); - mNetworkAgentThread = new HandlerThread(IpClientIntegrationTestCommon.class.getSimpleName()); mNetworkAgentThread.start(); @@ -3957,7 +4031,7 @@ final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() .build(); startIpClientProvisioning(config); - verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false); + verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(true); final List<ByteBuffer> options = new ArrayList<ByteBuffer>(); options.add(buildPioOption(3600, 1800, "2001:db8:1::/64")); // PIO @@ -4327,4 +4401,106 @@ assertFalse(lp.hasGlobalIpv6Address()); assertEquals(1, lp.getLinkAddresses().size()); // only link-local } + + @Test + public void testMaxDtimMultiplier_IPv6OnlyNetwork() throws Exception { + ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() + .withoutIPv4() + .build(); + startIpClientProvisioning(config); + + verify(mCb, timeout(TEST_TIMEOUT_MS)).setMaxDtimMultiplier( + IpClient.DEFAULT_BEFORE_IPV6_PROV_MAX_DTIM_MULTIPLIER); + + LinkProperties lp = doIpv6OnlyProvisioning(); + assertNotNull(lp); + assertEquals(3, lp.getLinkAddresses().size()); // IPv6 privacy, stable privacy, link-local + verify(mCb, timeout(TEST_TIMEOUT_MS)).setMaxDtimMultiplier( + IpClient.DEFAULT_IPV6_ONLY_NETWORK_MAX_DTIM_MULTIPLIER); + } + + @Test + public void testMaxDtimMultiplier_IPv6LinkLocalOnlyMode() throws Exception { + final InOrder inOrder = inOrder(mCb); + ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() + .withoutIPv4() + .withIpv6LinkLocalOnly() + .build(); + startIpClientProvisioning(config); + verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(any()); + inOrder.verify(mCb).setMaxDtimMultiplier( + IpClient.DEFAULT_BEFORE_IPV6_PROV_MAX_DTIM_MULTIPLIER); + inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).setMaxDtimMultiplier( + DTIM_MULTIPLIER_RESET); + } + + @Test + public void testMaxDtimMultiplier_IPv4OnlyNetwork() throws Exception { + final InOrder inOrder = inOrder(mCb); + performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S, + true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */, + TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */); + verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); + inOrder.verify(mCb).setMaxDtimMultiplier( + IpClient.DEFAULT_BEFORE_IPV6_PROV_MAX_DTIM_MULTIPLIER); + inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).setMaxDtimMultiplier( + IpClient.DEFAULT_IPV4_ONLY_NETWORK_MAX_DTIM_MULTIPLIER); + } + + private void runDualStackNetworkDtimMultiplierSetting(final InOrder inOrder) throws Exception { + doDualStackProvisioning(false /* shouldDisableAcceptRa */); + inOrder.verify(mCb).setMaxDtimMultiplier( + IpClient.DEFAULT_BEFORE_IPV6_PROV_MAX_DTIM_MULTIPLIER); + inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).setMaxDtimMultiplier( + IpClient.DEFAULT_DUAL_STACK_MAX_DTIM_MULTIPLIER); + } + + @Test + public void testMaxDtimMultiplier_DualStackNetwork() throws Exception { + final InOrder inOrder = inOrder(mCb); + runDualStackNetworkDtimMultiplierSetting(inOrder); + } + + @Test + public void testMaxDtimMultiplier_MulticastLock() throws Exception { + final InOrder inOrder = inOrder(mCb); + runDualStackNetworkDtimMultiplierSetting(inOrder); + + // Simulate to hold the multicast lock by disabling the multicast filter. + mIIpClient.setMulticastFilter(false); + inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).setMaxDtimMultiplier( + IpClient.DEFAULT_MULTICAST_LOCK_MAX_DTIM_MULTIPLIER); + + // Simulate to disable the multicast lock again, then check the multiplier should be + // changed to 2 (dual-stack setting) + mIIpClient.setMulticastFilter(true); + inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).setMaxDtimMultiplier( + IpClient.DEFAULT_DUAL_STACK_MAX_DTIM_MULTIPLIER); + } + + @Test + public void testMaxDtimMultiplier_MulticastLockEnabled_StoppedState() throws Exception { + // Simulate to hold the multicast lock by disabling the multicast filter at StoppedState, + // verify no callback to be sent, start dual-stack provisioning and verify the multiplier + // to be set to 1 (multicast lock setting) later. + mIIpClient.setMulticastFilter(false); + verify(mCb, after(10).never()).setMaxDtimMultiplier( + IpClient.DEFAULT_MULTICAST_LOCK_MAX_DTIM_MULTIPLIER); + + doDualStackProvisioning(false /* shouldDisableAcceptRa */); + verify(mCb, times(1)).setMaxDtimMultiplier( + IpClient.DEFAULT_MULTICAST_LOCK_MAX_DTIM_MULTIPLIER); + } + + @Test + public void testMaxDtimMultiplier_resetMultiplier() throws Exception { + final InOrder inOrder = inOrder(mCb); + runDualStackNetworkDtimMultiplierSetting(inOrder); + + verify(mCb, never()).setMaxDtimMultiplier(DTIM_MULTIPLIER_RESET); + + // Stop IpClient and verify if the multiplier has been reset. + mIIpClient.stop(); + inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).setMaxDtimMultiplier(DTIM_MULTIPLIER_RESET); + } }
diff --git a/tests/integration/root/android/net/ip/IpClientRootTest.kt b/tests/integration/root/android/net/ip/IpClientRootTest.kt index f1fa1c7..0f7ec0c 100644 --- a/tests/integration/root/android/net/ip/IpClientRootTest.kt +++ b/tests/integration/root/android/net/ip/IpClientRootTest.kt
@@ -138,7 +138,7 @@ } } - private val originalFlagValues = ArrayMap<String, String>() + private val originalPropertyValues = ArrayMap<String, String>() /** * Wrapper class for IIpClientCallbacks. @@ -154,11 +154,11 @@ } @After - fun tearDownFlags() { + fun tearDownDeviceConfigProperties() { if (testSkipped()) return automation.adoptShellPermissionIdentity(READ_DEVICE_CONFIG, WRITE_DEVICE_CONFIG) try { - for ((key, value) in originalFlagValues.entries) { + for ((key, value) in originalPropertyValues.entries) { if (key == null) continue DeviceConfig.setProperty(DeviceConfig.NAMESPACE_CONNECTIVITY, key, value, false /* makeDefault */) @@ -195,25 +195,34 @@ return ipClientCaptor.value } - override fun setFeatureEnabled(feature: String, enabled: Boolean) { + private fun setDeviceConfigProperty(name: String, value: String) { automation.adoptShellPermissionIdentity(READ_DEVICE_CONFIG, WRITE_DEVICE_CONFIG) try { - // Do not use computeIfAbsent as it would overwrite null values (flag originally unset) - if (!originalFlagValues.containsKey(feature)) { - originalFlagValues[feature] = - DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONNECTIVITY, feature) + // Do not use computeIfAbsent as it would overwrite null values, + // property originally unset. + if (!originalPropertyValues.containsKey(name)) { + originalPropertyValues[name] = + DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONNECTIVITY, name) } - // The feature is enabled if the flag is lower than the package version. - // Package versions follow a standard format with 9 digits. - // TODO: consider resetting flag values on reboot when set to special values like "1" or - // "999999999" - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_CONNECTIVITY, feature, - if (enabled) "1" else "999999999", false) + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_CONNECTIVITY, name, value, + false /* makeDefault */) } finally { automation.dropShellPermissionIdentity() } } + override fun setFeatureEnabled(feature: String, enabled: Boolean) { + // The feature is enabled if the flag is lower than the package version. + // Package versions follow a standard format with 9 digits. + // TODO: consider resetting flag values on reboot when set to special values like "1" or + // "999999999" + setDeviceConfigProperty(feature, if (enabled) "1" else "999999999") + } + + override fun setDeviceConfigProperty(name: String, value: Int) { + setDeviceConfigProperty(name, value.toString()) + } + override fun isFeatureEnabled(name: String, defaultEnabled: Boolean): Boolean { automation.adoptShellPermissionIdentity(READ_DEVICE_CONFIG, WRITE_DEVICE_CONFIG) try {
diff --git a/tests/integration/signature/android/net/NetworkStatsIntegrationTest.kt b/tests/integration/signature/android/net/NetworkStatsIntegrationTest.kt index 11ec1c2..f1cbd21 100644 --- a/tests/integration/signature/android/net/NetworkStatsIntegrationTest.kt +++ b/tests/integration/signature/android/net/NetworkStatsIntegrationTest.kt
@@ -18,8 +18,10 @@ import android.Manifest.permission.MANAGE_TEST_NETWORKS import android.app.usage.NetworkStats +import android.app.usage.NetworkStats.Bucket.TAG_NONE import android.app.usage.NetworkStatsManager import android.net.NetworkTemplate.MATCH_TEST +import android.os.Process import androidx.test.platform.app.InstrumentationRegistry import com.android.testutils.PacketBridge import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged @@ -56,10 +58,12 @@ private val REMOTE_V4ADDR = LinkAddress(InetAddresses.parseNumericAddress("8.8.8.8"), 32) private val DEFAULT_BUFFER_SIZE = 1000 + private val CONNECTION_TIMEOUT_MILLIS = 15000 private val TEST_DOWNLOAD_SIZE = 10000L private val TEST_UPLOAD_SIZE = 20000L private val HTTP_SERVER_NAME = "test.com" private val DNS_SERVER_PORT = 53 + private val TEST_TAG = 0xF00D // Set up the packet bridge with two IPv6 address only test networks. private val inst = InstrumentationRegistry.getInstrumentation() @@ -133,6 +137,7 @@ val internalInterfaceName = waitFor464XlatReady(packetBridge.internalNetwork) val (_, rxBytesBeforeTest) = getTotalTxRxBytes(internalInterfaceName) + val (_, rxTaggedBytesBeforeTest) = getTaggedTxRxBytes(internalInterfaceName, TEST_TAG) // Generate the download traffic. genHttpTraffic(packetBridge.internalNetwork, uploadSize = 0L, TEST_DOWNLOAD_SIZE) @@ -140,25 +145,58 @@ // In practice, for one way 10k download payload, the download usage is about // 11222~12880 bytes. And the upload usage is about 1279~1626 bytes, which is majorly // contributed by TCP ACK packets. - val (txBytesAfterDownload, rxBytesAfterDownload) = getTotalTxRxBytes(internalInterfaceName) - assertTrue( - rxBytesAfterDownload - rxBytesBeforeTest in - TEST_DOWNLOAD_SIZE..(TEST_DOWNLOAD_SIZE * 1.3).toLong(), - "Download size on $internalInterfaceName" + val (txBytesAfterDownload, rxBytesAfterDownload) = + getTotalTxRxBytes(internalInterfaceName) + val (txTaggedBytesAfterDownload, rxTaggedBytesAfterDownload) = getTaggedTxRxBytes( + internalInterfaceName, + TEST_TAG + ) + assertInRange( + "Download size", internalInterfaceName, + rxBytesAfterDownload - rxBytesBeforeTest, + TEST_DOWNLOAD_SIZE, (TEST_DOWNLOAD_SIZE * 1.3).toLong() + ) + // Increment of tagged data should be zero since no tagged traffic was generated. + assertEquals( + rxTaggedBytesBeforeTest, + rxTaggedBytesAfterDownload, + "Tagged download size of uid ${Process.myUid()} on $internalInterfaceName" ) - genHttpTraffic(packetBridge.internalNetwork, TEST_UPLOAD_SIZE, downloadSize = 0L) + // Generate upload traffic with tag to verify tagged data accounting as well. + genHttpTrafficWithTag( + packetBridge.internalNetwork, + TEST_UPLOAD_SIZE, + downloadSize = 0L, + TEST_TAG + ) // Verify upload data usage accounting. val (txBytesAfterUpload, _) = getTotalTxRxBytes(internalInterfaceName) - assertTrue( - txBytesAfterUpload - txBytesAfterDownload in - TEST_UPLOAD_SIZE..(TEST_UPLOAD_SIZE * 1.3).toLong(), - "Upload size on $internalInterfaceName" + val (txTaggedBytesAfterUpload, _) = getTaggedTxRxBytes(internalInterfaceName, TEST_TAG) + assertInRange( + "Upload size", internalInterfaceName, + txBytesAfterUpload - txBytesAfterDownload, + TEST_UPLOAD_SIZE, (TEST_UPLOAD_SIZE * 1.3).toLong() + ) + assertInRange( + "Tagged upload size of uid ${Process.myUid()}", + internalInterfaceName, + txTaggedBytesAfterUpload - txTaggedBytesAfterDownload, + TEST_UPLOAD_SIZE, + (TEST_UPLOAD_SIZE * 1.3).toLong() ) } - private fun genHttpTraffic(network: Network, uploadSize: Long, downloadSize: Long) { + private fun genHttpTraffic(network: Network, uploadSize: Long, downloadSize: Long) = + genHttpTrafficWithTag(network, uploadSize, downloadSize, NetworkStats.Bucket.TAG_NONE) + + private fun genHttpTrafficWithTag( + network: Network, + uploadSize: Long, + downloadSize: Long, + tag: Int + ) { val path = "/test_upload_download" val buf = ByteArray(DEFAULT_BUFFER_SIZE) @@ -166,50 +204,84 @@ TestHttpServer.Request(path, NanoHTTPD.Method.POST), NanoHTTPD.Response.Status.OK, content = getRandomString(downloadSize) ) - val spec = "http://$HTTP_SERVER_NAME:${httpServer.listeningPort}$path" - val url = URL(spec) - val httpConnection = network.openConnection(url) as HttpURLConnection - httpConnection.requestMethod = "POST" - httpConnection.doOutput = true - // Tell the server that the response should not be compressed. Otherwise, the data usage - // accounted will be less than expected. - httpConnection.setRequestProperty("Accept-Encoding", "identity") + var httpConnection: HttpURLConnection? = null + try { + TrafficStats.setThreadStatsTag(tag) + val spec = "http://$HTTP_SERVER_NAME:${httpServer.listeningPort}$path" + val url = URL(spec) + httpConnection = network.openConnection(url) as HttpURLConnection + httpConnection.connectTimeout = CONNECTION_TIMEOUT_MILLIS + httpConnection.requestMethod = "POST" + httpConnection.doOutput = true + // Tell the server that the response should not be compressed. Otherwise, the data usage + // accounted will be less than expected. + httpConnection.setRequestProperty("Accept-Encoding", "identity") + // Tell the server that to close connection after this request, this is needed to + // prevent from reusing the same socket that has different tagging requirement. + httpConnection.setRequestProperty("Connection", "close") - // Send http body. - val outputStream = BufferedOutputStream(httpConnection.outputStream) - outputStream.write(getRandomString(uploadSize).toByteArray(Charset.forName("UTF-8"))) - outputStream.close() - assertEquals(HTTP_OK, httpConnection.responseCode) + // Send http body. + val outputStream = BufferedOutputStream(httpConnection.outputStream) + outputStream.write(getRandomString(uploadSize).toByteArray(Charset.forName("UTF-8"))) + outputStream.close() + assertEquals(HTTP_OK, httpConnection.responseCode) - // Receive response from the server. - val inputStream = BufferedInputStream(httpConnection.getInputStream()) - var total = 0L - while (true) { - val count = inputStream.read(buf) - if (count == -1) break // End-of-Stream - total += count + // Receive response from the server. + val inputStream = BufferedInputStream(httpConnection.getInputStream()) + var total = 0L + while (true) { + val count = inputStream.read(buf) + if (count == -1) break // End-of-Stream + total += count + } + assertEquals(downloadSize, total) + } finally { + httpConnection?.inputStream?.close() + TrafficStats.clearThreadStatsTag() } - assertEquals(downloadSize, total) - httpConnection.getInputStream().close() } private fun getTotalTxRxBytes(iface: String): Pair<Long, Long> { + return getNetworkStatsThat(iface, TAG_NONE) { nsm, template -> + nsm.querySummary(template, Long.MIN_VALUE, Long.MAX_VALUE) + } + } + + private fun getTaggedTxRxBytes(iface: String, tag: Int): Pair<Long, Long> { + return getNetworkStatsThat(iface, tag) { nsm, template -> + nsm.queryTaggedSummary(template, Long.MIN_VALUE, Long.MAX_VALUE) + } + } + + private fun getNetworkStatsThat( + iface: String, + tag: Int, + queryApi: (nsm: NetworkStatsManager, template: NetworkTemplate) -> NetworkStats + ): Pair<Long, Long> { val nsm = context.getSystemService(NetworkStatsManager::class.java) nsm.forceUpdate() val testTemplate = NetworkTemplate.Builder(MATCH_TEST) .setWifiNetworkKeys(setOf(iface)).build() - val xtStats = nsm.querySummary(testTemplate, Long.MIN_VALUE, Long.MAX_VALUE) + val stats = queryApi.invoke(nsm, testTemplate) val recycled = NetworkStats.Bucket() var rx = 0L var tx = 0L - while (xtStats.hasNextBucket()) { - xtStats.getNextBucket(recycled) + while (stats.hasNextBucket()) { + stats.getNextBucket(recycled) + if (recycled.uid != Process.myUid() || recycled.tag != tag) continue rx += recycled.rxBytes tx += recycled.txBytes } return tx to rx } + /** Verify the given value is in range [lower, upper] */ + private fun assertInRange(tag: String, iface: String, value: Long, lower: Long, upper: Long) = + assertTrue( + value in lower..upper, + "$tag on $iface: $value is not within range [$lower, $upper]" + ) + fun getRandomString(length: Long): String { val allowedChars = ('A'..'Z') + ('a'..'z') + ('0'..'9') return (1..length)
diff --git a/tests/integration/signature/android/net/ip/IpClientSignatureTest.kt b/tests/integration/signature/android/net/ip/IpClientSignatureTest.kt index 26938b6..b494732 100644 --- a/tests/integration/signature/android/net/ip/IpClientSignatureTest.kt +++ b/tests/integration/signature/android/net/ip/IpClientSignatureTest.kt
@@ -52,6 +52,10 @@ mEnabledFeatures.put(name, enabled) } + override fun setDeviceConfigProperty(name: String, value: Int) { + mDependencies.setDeviceConfigProperty(name, value) + } + override fun getStoredNetworkAttributes(l2Key: String, timeout: Long): NetworkAttributes { val networkAttributesCaptor = ArgumentCaptor.forClass(NetworkAttributes::class.java)
diff --git a/tests/integration/signature/android/net/util/NetworkStackUtilsIntegrationTest.kt b/tests/integration/signature/android/net/util/NetworkStackUtilsIntegrationTest.kt index 0a80d70..29c5a6c 100644 --- a/tests/integration/signature/android/net/util/NetworkStackUtilsIntegrationTest.kt +++ b/tests/integration/signature/android/net/util/NetworkStackUtilsIntegrationTest.kt
@@ -47,9 +47,9 @@ import com.android.net.module.util.NetworkStackConstants.ETHER_ADDR_LEN import com.android.net.module.util.NetworkStackConstants.IPV4_ADDR_ANY import com.android.net.module.util.NetworkStackConstants.IPV4_CHECKSUM_OFFSET +import com.android.net.module.util.NetworkStackConstants.IPV4_FLAGS_OFFSET import com.android.net.module.util.NetworkStackConstants.IPV4_FLAG_DF import com.android.net.module.util.NetworkStackConstants.IPV4_FLAG_MF -import com.android.net.module.util.NetworkStackConstants.IPV4_FLAGS_OFFSET import com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_NODES_MULTICAST import com.android.net.module.util.structs.PrefixInformationOption import com.android.networkstack.util.NetworkStackUtils @@ -59,10 +59,6 @@ import com.android.testutils.IPv4UdpFilter import com.android.testutils.TapPacketReader import com.android.testutils.UDP_HEADER_LENGTH -import org.junit.After -import org.junit.Assert.assertArrayEquals -import org.junit.Before -import org.junit.Test import java.io.FileDescriptor import java.net.Inet4Address import java.net.Inet6Address @@ -71,8 +67,13 @@ import kotlin.reflect.KClass import kotlin.test.assertEquals import kotlin.test.assertNotNull +import kotlin.test.assertNull import kotlin.test.assertTrue import kotlin.test.fail +import org.junit.After +import org.junit.Assert.assertArrayEquals +import org.junit.Before +import org.junit.Test class NetworkStackUtilsIntegrationTest { private val inst by lazy { InstrumentationRegistry.getInstrumentation() } @@ -225,6 +226,41 @@ assertSolicitedNodeMulticastAddress(addr3, TEST_INET6ADDR_3) } + @Test + fun testConvertMacAddressToEui64() { + // MAC address with universal/local bit set (the first byte: 0xBA) + var expected = byteArrayOf( + 0xB8.toByte(), 0x98.toByte(), 0x76.toByte(), 0xFF.toByte(), + 0xFE.toByte(), 0x54.toByte(), 0x32.toByte(), 0x10.toByte()) + val srcEui64 = NetworkStackUtils.macAddressToEui64(TEST_SRC_MAC) + assertArrayEquals(expected, srcEui64) + + // MAC address with universal/local bit unset (the first byte: 0x01). + expected = byteArrayOf( + 0x03.toByte(), 0x23.toByte(), 0x45.toByte(), 0xFF.toByte(), + 0xFE.toByte(), 0x67.toByte(), 0x89.toByte(), 0x0A.toByte()) + val targetEui64 = NetworkStackUtils.macAddressToEui64(TEST_TARGET_MAC) + assertArrayEquals(expected, targetEui64) + } + + @Test + fun testGenerateIpv6AddressFromEui64() { + val eui64 = NetworkStackUtils.macAddressToEui64(TEST_SRC_MAC) + var prefix = IpPrefix("2001:db8:1::/80") + // Don't accept the prefix length larger than 64. + assertNull(NetworkStackUtils.createInet6AddressFromEui64(prefix, eui64)) + + prefix = IpPrefix("2001:db8:1::/48") + // Don't accept the prefix length less than 64. + assertNull(NetworkStackUtils.createInet6AddressFromEui64(prefix, eui64)) + + prefix = IpPrefix("2001:db8:1::/64") + // IPv6 address string is formed by combining the IPv6 prefix("2001:db8:1::") and + // EUI64 converted from TEST_SRC_MAC, see above test for the output EUI64 example. + val expected = parseNumericAddress("2001:db8:1::b898:76ff:fe54:3210") as Inet6Address + assertEquals(expected, NetworkStackUtils.createInet6AddressFromEui64(prefix, eui64)) + } + private fun assertSocketReadErrno(msg: String, fd: FileDescriptor, errno: Int) { val received = ByteBuffer.allocate(TEST_MTU) try { @@ -257,11 +293,12 @@ packet.putShort(checksumOffset, IpUtils.ipChecksum(packet, ETHER_HEADER_LENGTH)) } - private fun doTestDhcpResponseWithMfBit(dropMf: Boolean) { + @Test + fun testDhcpResponseWithMfBitDropped() { val ifindex = InterfaceParams.getByName(iface.interfaceName).index val packetSock = Os.socket(AF_PACKET, SOCK_RAW or SOCK_NONBLOCK, /*protocol=*/0) try { - NetworkStackUtils.attachDhcpFilter(packetSock, dropMf) + NetworkStackUtils.attachDhcpFilter(packetSock) val addr = SocketUtils.makePacketSocketAddress(OsConstants.ETH_P_IP, ifindex) Os.bind(packetSock, addr) val packet = DhcpPacket.buildNakPacket(DhcpPacket.ENCAP_L2, 42, @@ -270,15 +307,11 @@ setMfBit(packet, true) reader.sendResponse(packet) - // Packet with MF bit set is received iff dropMf is false. - if (dropMf) { - assertSocketReadErrno("Packet with MF bit should have been dropped", - packetSock, OsConstants.EAGAIN) - } else { - assertNextPacketOnSocket(packetSock, packet) - } + // Packet with MF bit set is not received. + assertSocketReadErrno("Packet with MF bit should have been dropped", + packetSock, OsConstants.EAGAIN) - // Identical packet, except with MF bit cleared, should always be received. + // Identical packet, except with MF bit cleared, should be received. setMfBit(packet, false) reader.sendResponse(packet) assertNextPacketOnSocket(packetSock, packet) @@ -286,16 +319,6 @@ Os.close(packetSock) } } - - @Test - fun testDhcpResponseWithMfBitDropped() { - doTestDhcpResponseWithMfBit(/*dropMf=*/ true) - } - - @Test - fun testDhcpResponseWithMfBitReceived() { - doTestDhcpResponseWithMfBit(/*dropMf=*/ false) - } } private fun ByteBuffer.readAsArray(): ByteArray {
diff --git a/tests/unit/src/android/net/apf/ApfTest.java b/tests/unit/src/android/net/apf/ApfTest.java index f9f7e64..285fcb4 100644 --- a/tests/unit/src/android/net/apf/ApfTest.java +++ b/tests/unit/src/android/net/apf/ApfTest.java
@@ -16,12 +16,15 @@ package android.net.apf; +import static android.net.apf.ApfGenerator.Register.R0; +import static android.net.apf.ApfGenerator.Register.R1; import static android.system.OsConstants.AF_UNIX; import static android.system.OsConstants.ARPHRD_ETHER; import static android.system.OsConstants.ETH_P_ARP; import static android.system.OsConstants.ETH_P_IP; import static android.system.OsConstants.ETH_P_IPV6; import static android.system.OsConstants.IPPROTO_ICMPV6; +import static android.system.OsConstants.IPPROTO_IPV6; import static android.system.OsConstants.IPPROTO_TCP; import static android.system.OsConstants.IPPROTO_UDP; import static android.system.OsConstants.SOCK_STREAM; @@ -51,7 +54,6 @@ import android.net.TcpKeepalivePacketDataParcelable; import android.net.apf.ApfFilter.ApfConfiguration; import android.net.apf.ApfGenerator.IllegalInstructionException; -import android.net.apf.ApfGenerator.Register; import android.net.ip.IIpClientCallbacks; import android.net.ip.IpClient.IpClientCallbacksWrapper; import android.net.metrics.IpConnectivityLog; @@ -61,8 +63,10 @@ import android.os.SystemClock; import android.system.ErrnoException; import android.system.Os; +import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Log; +import android.util.Pair; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -99,7 +103,10 @@ import java.net.Inet6Address; import java.net.InetAddress; import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Random; @@ -155,7 +162,7 @@ private static final byte SIZE8 = (byte)(1 << 1); private static final byte SIZE16 = (byte)(2 << 1); private static final byte SIZE32 = (byte)(3 << 1); - private static final byte R1 = 1; + private static final byte R1_REG = 1; private static ApfConfiguration getDefaultConfig() { ApfFilter.ApfConfiguration config = new ApfConfiguration(); @@ -176,16 +183,28 @@ } } + private static void assertReturnCodesEqual(String msg, int expected, int got) { + assertEquals(msg, label(expected), label(got)); + } + private static void assertReturnCodesEqual(int expected, int got) { assertEquals(label(expected), label(got)); } private void assertVerdict(int expected, byte[] program, byte[] packet, int filterAge) { - assertReturnCodesEqual(expected, apfSimulate(program, packet, null, filterAge)); + final String msg = "Unexpected APF verdict. To debug:\n" + + " apf_run --program " + HexDump.toHexString(program) + + " --packet " + HexDump.toHexString(packet) + " --trace | less\n "; + assertReturnCodesEqual(msg, expected, apfSimulate(program, packet, null, filterAge)); + } + + private void assertVerdict(String msg, int expected, byte[] program, byte[] packet, + int filterAge) { + assertReturnCodesEqual(msg, expected, apfSimulate(program, packet, null, filterAge)); } private void assertVerdict(int expected, byte[] program, byte[] packet) { - assertReturnCodesEqual(expected, apfSimulate(program, packet, null, 0)); + assertVerdict(expected, program, packet, 0); } private void assertPass(byte[] program, byte[] packet, int filterAge) { @@ -293,7 +312,7 @@ gen.addJumpIfR0NotEquals(0, gen.DROP_LABEL); assertPass(gen); gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1); + gen.addLoadImmediate(R0, 1); gen.addJumpIfR0NotEquals(0, gen.DROP_LABEL); assertDrop(gen); @@ -307,13 +326,13 @@ gen.addJumpIfR0NotEqualsR1(gen.DROP_LABEL); assertPass(gen); gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1); + gen.addLoadImmediate(R0, 1); gen.addJumpIfR0NotEqualsR1(gen.DROP_LABEL); assertDrop(gen); // Test load immediate. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1234567890); + gen.addLoadImmediate(R0, 1234567890); gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); assertDrop(gen); @@ -337,35 +356,35 @@ // Test and. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1234567890); + gen.addLoadImmediate(R0, 1234567890); gen.addAnd(123456789); gen.addJumpIfR0Equals(1234567890 & 123456789, gen.DROP_LABEL); assertDrop(gen); // Test left shift. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1234567890); + gen.addLoadImmediate(R0, 1234567890); gen.addLeftShift(1); gen.addJumpIfR0Equals(1234567890 << 1, gen.DROP_LABEL); assertDrop(gen); // Test right shift. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1234567890); + gen.addLoadImmediate(R0, 1234567890); gen.addRightShift(1); gen.addJumpIfR0Equals(1234567890 >> 1, gen.DROP_LABEL); assertDrop(gen); // Test multiply. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 123456789); + gen.addLoadImmediate(R0, 123456789); gen.addMul(2); gen.addJumpIfR0Equals(123456789 * 2, gen.DROP_LABEL); assertDrop(gen); // Test divide. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1234567890); + gen.addLoadImmediate(R0, 1234567890); gen.addDiv(2); gen.addJumpIfR0Equals(1234567890 / 2, gen.DROP_LABEL); assertDrop(gen); @@ -378,61 +397,61 @@ // Test add. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, 1234567890); + gen.addLoadImmediate(R1, 1234567890); gen.addAddR1(); gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); assertDrop(gen); // Test subtract. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, -1234567890); + gen.addLoadImmediate(R1, -1234567890); gen.addAddR1(); gen.addJumpIfR0Equals(-1234567890, gen.DROP_LABEL); assertDrop(gen); // Test or. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, 1234567890); + gen.addLoadImmediate(R1, 1234567890); gen.addOrR1(); gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); assertDrop(gen); // Test and. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1234567890); - gen.addLoadImmediate(Register.R1, 123456789); + gen.addLoadImmediate(R0, 1234567890); + gen.addLoadImmediate(R1, 123456789); gen.addAndR1(); gen.addJumpIfR0Equals(1234567890 & 123456789, gen.DROP_LABEL); assertDrop(gen); // Test left shift. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1234567890); - gen.addLoadImmediate(Register.R1, 1); + gen.addLoadImmediate(R0, 1234567890); + gen.addLoadImmediate(R1, 1); gen.addLeftShiftR1(); gen.addJumpIfR0Equals(1234567890 << 1, gen.DROP_LABEL); assertDrop(gen); // Test right shift. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1234567890); - gen.addLoadImmediate(Register.R1, -1); + gen.addLoadImmediate(R0, 1234567890); + gen.addLoadImmediate(R1, -1); gen.addLeftShiftR1(); gen.addJumpIfR0Equals(1234567890 >> 1, gen.DROP_LABEL); assertDrop(gen); // Test multiply. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 123456789); - gen.addLoadImmediate(Register.R1, 2); + gen.addLoadImmediate(R0, 123456789); + gen.addLoadImmediate(R1, 2); gen.addMulR1(); gen.addJumpIfR0Equals(123456789 * 2, gen.DROP_LABEL); assertDrop(gen); // Test divide. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1234567890); - gen.addLoadImmediate(Register.R1, 2); + gen.addLoadImmediate(R0, 1234567890); + gen.addLoadImmediate(R1, 2); gen.addDivR1(); gen.addJumpIfR0Equals(1234567890 / 2, gen.DROP_LABEL); assertDrop(gen); @@ -445,53 +464,53 @@ // Test byte load. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoad8(Register.R0, 1); + gen.addLoad8(R0, 1); gen.addJumpIfR0Equals(45, gen.DROP_LABEL); assertDrop(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0); // Test out of bounds load. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoad8(Register.R0, 16); + gen.addLoad8(R0, 16); gen.addJumpIfR0Equals(0, gen.DROP_LABEL); assertPass(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0); // Test half-word load. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoad16(Register.R0, 1); + gen.addLoad16(R0, 1); gen.addJumpIfR0Equals((45 << 8) | 67, gen.DROP_LABEL); assertDrop(gen, new byte[]{123,45,67,0,0,0,0,0,0,0,0,0,0,0,0}, 0); // Test word load. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoad32(Register.R0, 1); + gen.addLoad32(R0, 1); gen.addJumpIfR0Equals((45 << 24) | (67 << 16) | (89 << 8) | 12, gen.DROP_LABEL); assertDrop(gen, new byte[]{123,45,67,89,12,0,0,0,0,0,0,0,0,0,0}, 0); // Test byte indexed load. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, 1); - gen.addLoad8Indexed(Register.R0, 0); + gen.addLoadImmediate(R1, 1); + gen.addLoad8Indexed(R0, 0); gen.addJumpIfR0Equals(45, gen.DROP_LABEL); assertDrop(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0); // Test out of bounds indexed load. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, 8); - gen.addLoad8Indexed(Register.R0, 8); + gen.addLoadImmediate(R1, 8); + gen.addLoad8Indexed(R0, 8); gen.addJumpIfR0Equals(0, gen.DROP_LABEL); assertPass(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0); // Test half-word indexed load. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, 1); - gen.addLoad16Indexed(Register.R0, 0); + gen.addLoadImmediate(R1, 1); + gen.addLoad16Indexed(R0, 0); gen.addJumpIfR0Equals((45 << 8) | 67, gen.DROP_LABEL); assertDrop(gen, new byte[]{123,45,67,0,0,0,0,0,0,0,0,0,0,0,0}, 0); // Test word indexed load. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, 1); - gen.addLoad32Indexed(Register.R0, 0); + gen.addLoadImmediate(R1, 1); + gen.addLoad32Indexed(R0, 0); gen.addJumpIfR0Equals((45 << 24) | (67 << 16) | (89 << 8) | 12, gen.DROP_LABEL); assertDrop(gen, new byte[]{123,45,67,89,12,0,0,0,0,0,0,0,0,0,0}, 0); @@ -500,7 +519,7 @@ gen.addJumpIfR0GreaterThan(0, gen.DROP_LABEL); assertPass(gen); gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1); + gen.addLoadImmediate(R0, 1); gen.addJumpIfR0GreaterThan(0, gen.DROP_LABEL); assertDrop(gen); @@ -517,11 +536,11 @@ gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL); assertPass(gen); gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1); + gen.addLoadImmediate(R0, 1); gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL); assertDrop(gen); gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 3); + gen.addLoadImmediate(R0, 3); gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL); assertDrop(gen); @@ -530,8 +549,8 @@ gen.addJumpIfR0GreaterThanR1(gen.DROP_LABEL); assertPass(gen); gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 2); - gen.addLoadImmediate(Register.R1, 1); + gen.addLoadImmediate(R0, 2); + gen.addLoadImmediate(R1, 1); gen.addJumpIfR0GreaterThanR1(gen.DROP_LABEL); assertDrop(gen); @@ -540,100 +559,100 @@ gen.addJumpIfR0LessThanR1(gen.DROP_LABEL); assertPass(gen); gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, 1); + gen.addLoadImmediate(R1, 1); gen.addJumpIfR0LessThanR1(gen.DROP_LABEL); assertDrop(gen); // Test jumping if any bits set in register. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, 3); + gen.addLoadImmediate(R1, 3); gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL); assertPass(gen); gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, 3); - gen.addLoadImmediate(Register.R0, 1); + gen.addLoadImmediate(R1, 3); + gen.addLoadImmediate(R0, 1); gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL); assertDrop(gen); gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, 3); - gen.addLoadImmediate(Register.R0, 3); + gen.addLoadImmediate(R1, 3); + gen.addLoadImmediate(R0, 3); gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL); assertDrop(gen); // Test load from memory. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadFromMemory(Register.R0, 0); + gen.addLoadFromMemory(R0, 0); gen.addJumpIfR0Equals(0, gen.DROP_LABEL); assertDrop(gen); // Test store to memory. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, 1234567890); - gen.addStoreToMemory(Register.R1, 12); - gen.addLoadFromMemory(Register.R0, 12); + gen.addLoadImmediate(R1, 1234567890); + gen.addStoreToMemory(R1, 12); + gen.addLoadFromMemory(R0, 12); gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); assertDrop(gen); // Test filter age pre-filled memory. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadFromMemory(Register.R0, gen.FILTER_AGE_MEMORY_SLOT); + gen.addLoadFromMemory(R0, gen.FILTER_AGE_MEMORY_SLOT); gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); assertDrop(gen, new byte[MIN_PKT_SIZE], 1234567890); // Test packet size pre-filled memory. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadFromMemory(Register.R0, gen.PACKET_SIZE_MEMORY_SLOT); + gen.addLoadFromMemory(R0, gen.PACKET_SIZE_MEMORY_SLOT); gen.addJumpIfR0Equals(MIN_PKT_SIZE, gen.DROP_LABEL); assertDrop(gen); // Test IPv4 header size pre-filled memory. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadFromMemory(Register.R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); + gen.addLoadFromMemory(R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); gen.addJumpIfR0Equals(20, gen.DROP_LABEL); assertDrop(gen, new byte[]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x45}, 0); // Test not. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1234567890); - gen.addNot(Register.R0); + gen.addLoadImmediate(R0, 1234567890); + gen.addNot(R0); gen.addJumpIfR0Equals(~1234567890, gen.DROP_LABEL); assertDrop(gen); // Test negate. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1234567890); - gen.addNeg(Register.R0); + gen.addLoadImmediate(R0, 1234567890); + gen.addNeg(R0); gen.addJumpIfR0Equals(-1234567890, gen.DROP_LABEL); assertDrop(gen); // Test move. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, 1234567890); - gen.addMove(Register.R0); + gen.addLoadImmediate(R1, 1234567890); + gen.addMove(R0); gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); assertDrop(gen); gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1234567890); - gen.addMove(Register.R1); + gen.addLoadImmediate(R0, 1234567890); + gen.addMove(R1); gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); assertDrop(gen); // Test swap. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, 1234567890); + gen.addLoadImmediate(R1, 1234567890); gen.addSwap(); gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); assertDrop(gen); gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1234567890); + gen.addLoadImmediate(R0, 1234567890); gen.addSwap(); gen.addJumpIfR0Equals(0, gen.DROP_LABEL); assertDrop(gen); // Test jump if bytes not equal. gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1); - gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL); + gen.addLoadImmediate(R0, 1); + gen.addJumpIfBytesNotEqual(R0, new byte[]{123}, gen.DROP_LABEL); program = gen.generate(); assertEquals(6, program.length); assertEquals((13 << 3) | (1 << 1) | 0, program[0]); @@ -644,21 +663,21 @@ assertEquals(123, program[5]); assertDrop(program, new byte[MIN_PKT_SIZE], 0); gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1); - gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL); + gen.addLoadImmediate(R0, 1); + gen.addJumpIfBytesNotEqual(R0, new byte[]{123}, gen.DROP_LABEL); byte[] packet123 = {0,123,0,0,0,0,0,0,0,0,0,0,0,0,0}; assertPass(gen, packet123, 0); gen = new ApfGenerator(MIN_APF_VERSION); - gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL); + gen.addJumpIfBytesNotEqual(R0, new byte[]{123}, gen.DROP_LABEL); assertDrop(gen, packet123, 0); gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1); - gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{1,2,30,4,5}, gen.DROP_LABEL); + gen.addLoadImmediate(R0, 1); + gen.addJumpIfBytesNotEqual(R0, new byte[]{1,2,30,4,5}, gen.DROP_LABEL); byte[] packet12345 = {0,1,2,3,4,5,0,0,0,0,0,0,0,0,0}; assertDrop(gen, packet12345, 0); gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1); - gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{1,2,3,4,5}, gen.DROP_LABEL); + gen.addLoadImmediate(R0, 1); + gen.addJumpIfBytesNotEqual(R0, new byte[]{1,2,3,4,5}, gen.DROP_LABEL); assertPass(gen, packet12345, 0); } @@ -672,13 +691,13 @@ public void testApfDataOpcodesWantApfV3() throws IllegalInstructionException, Exception { ApfGenerator gen = new ApfGenerator(MIN_APF_VERSION); try { - gen.addStoreData(Register.R0, 0); + gen.addStoreData(R0, 0); fail(); } catch (IllegalInstructionException expected) { /* pass */ } try { - gen.addLoadData(Register.R0, 0); + gen.addLoadData(R0, 0); fail(); } catch (IllegalInstructionException expected) { /* pass */ @@ -694,22 +713,22 @@ // 0-byte immediate: li R0, 0 gen = new ApfGenerator(4); - gen.addLoadImmediate(Register.R0, 0); + gen.addLoadImmediate(R0, 0); assertProgramEquals(new byte[]{LI_OP | SIZE0}, gen.generate()); // 1-byte immediate: li R0, 42 gen = new ApfGenerator(4); - gen.addLoadImmediate(Register.R0, 42); + gen.addLoadImmediate(R0, 42); assertProgramEquals(new byte[]{LI_OP | SIZE8, 42}, gen.generate()); // 2-byte immediate: li R1, 0x1234 gen = new ApfGenerator(4); - gen.addLoadImmediate(Register.R1, 0x1234); - assertProgramEquals(new byte[]{LI_OP | SIZE16 | R1, 0x12, 0x34}, gen.generate()); + gen.addLoadImmediate(R1, 0x1234); + assertProgramEquals(new byte[]{LI_OP | SIZE16 | R1_REG, 0x12, 0x34}, gen.generate()); // 4-byte immediate: li R0, 0x12345678 gen = new ApfGenerator(3); - gen.addLoadImmediate(Register.R0, 0x12345678); + gen.addLoadImmediate(R0, 0x12345678); assertProgramEquals( new byte[]{LI_OP | SIZE32, 0x12, 0x34, 0x56, 0x78}, gen.generate()); @@ -724,18 +743,18 @@ // 1-byte negative immediate: li R0, -42 gen = new ApfGenerator(3); - gen.addLoadImmediate(Register.R0, -42); + gen.addLoadImmediate(R0, -42); assertProgramEquals(new byte[]{LI_OP | SIZE8, -42}, gen.generate()); // 2-byte negative immediate: li R1, -0x1122 gen = new ApfGenerator(3); - gen.addLoadImmediate(Register.R1, -0x1122); - assertProgramEquals(new byte[]{LI_OP | SIZE16 | R1, (byte)0xEE, (byte)0xDE}, + gen.addLoadImmediate(R1, -0x1122); + assertProgramEquals(new byte[]{LI_OP | SIZE16 | R1_REG, (byte)0xEE, (byte)0xDE}, gen.generate()); // 4-byte negative immediate: li R0, -0x11223344 gen = new ApfGenerator(3); - gen.addLoadImmediate(Register.R0, -0x11223344); + gen.addLoadImmediate(R0, -0x11223344); assertProgramEquals( new byte[]{LI_OP | SIZE32, (byte)0xEE, (byte)0xDD, (byte)0xCC, (byte)0xBC}, gen.generate()); @@ -750,25 +769,26 @@ // Load data with no offset: lddw R0, [0 + r1] gen = new ApfGenerator(3); - gen.addLoadData(Register.R0, 0); + gen.addLoadData(R0, 0); assertProgramEquals(new byte[]{LDDW_OP | SIZE0}, gen.generate()); // Store data with 8bit negative offset: lddw r0, [-42 + r1] gen = new ApfGenerator(3); - gen.addStoreData(Register.R0, -42); + gen.addStoreData(R0, -42); assertProgramEquals(new byte[]{STDW_OP | SIZE8, -42}, gen.generate()); // Store data to R1 with 16bit negative offset: stdw r1, [-0x1122 + r0] gen = new ApfGenerator(3); - gen.addStoreData(Register.R1, -0x1122); - assertProgramEquals(new byte[]{STDW_OP | SIZE16 | R1, (byte)0xEE, (byte)0xDE}, + gen.addStoreData(R1, -0x1122); + assertProgramEquals(new byte[]{STDW_OP | SIZE16 | R1_REG, (byte)0xEE, (byte)0xDE}, gen.generate()); // Load data to R1 with 32bit negative offset: lddw r1, [0xDEADBEEF + r0] gen = new ApfGenerator(3); - gen.addLoadData(Register.R1, 0xDEADBEEF); + gen.addLoadData(R1, 0xDEADBEEF); assertProgramEquals( - new byte[]{LDDW_OP | SIZE32 | R1, (byte)0xDE, (byte)0xAD, (byte)0xBE, (byte)0xEF}, + new byte[]{LDDW_OP | SIZE32 | R1_REG, + (byte)0xDE, (byte)0xAD, (byte)0xBE, (byte)0xEF}, gen.generate()); } @@ -788,9 +808,9 @@ // Expect value 0x87654321 to be stored starting from address -11 from the end of the // data buffer, in big-endian order. gen = new ApfGenerator(3); - gen.addLoadImmediate(Register.R0, 0x87654321); - gen.addLoadImmediate(Register.R1, -5); - gen.addStoreData(Register.R0, -6); // -5 + -6 = -11 (offset +5 with data_len=16) + gen.addLoadImmediate(R0, 0x87654321); + gen.addLoadImmediate(R1, -5); + gen.addStoreData(R0, -6); // -5 + -6 = -11 (offset +5 with data_len=16) expected_data[5] = (byte)0x87; expected_data[6] = (byte)0x65; expected_data[7] = (byte)0x43; @@ -805,8 +825,8 @@ public void testApfDataRead() throws IllegalInstructionException, Exception { // Program that DROPs if address 10 (-6) contains 0x87654321. ApfGenerator gen = new ApfGenerator(3); - gen.addLoadImmediate(Register.R1, 1000); - gen.addLoadData(Register.R0, -1006); // 1000 + -1006 = -6 (offset +10 with data_len=16) + gen.addLoadImmediate(R1, 1000); + gen.addLoadData(R0, -1006); // 1000 + -1006 = -6 (offset +10 with data_len=16) gen.addJumpIfR0Equals(0x87654321, gen.DROP_LABEL); byte[] program = gen.generate(); byte[] packet = new byte[MIN_PKT_SIZE]; @@ -834,10 +854,10 @@ @Test public void testApfDataReadModifyWrite() throws IllegalInstructionException, Exception { ApfGenerator gen = new ApfGenerator(3); - gen.addLoadImmediate(Register.R1, -22); - gen.addLoadData(Register.R0, 0); // Load from address 32 -22 + 0 = 10 + gen.addLoadImmediate(R1, -22); + gen.addLoadData(R0, 0); // Load from address 32 -22 + 0 = 10 gen.addAdd(0x78453412); // 87654321 + 78453412 = FFAA7733 - gen.addStoreData(Register.R0, 4); // Write back to address 32 -22 + 4 = 14 + gen.addStoreData(R0, 4); // Write back to address 32 -22 + 4 = 14 byte[] packet = new byte[MIN_PKT_SIZE]; byte[] data = new byte[32]; @@ -861,36 +881,36 @@ // Program that DROPs unconditionally. This is our the baseline. ApfGenerator gen = new ApfGenerator(3); - gen.addLoadImmediate(Register.R0, 3); - gen.addLoadData(Register.R1, 7); + gen.addLoadImmediate(R0, 3); + gen.addLoadData(R1, 7); gen.addJump(gen.DROP_LABEL); assertDataMemoryContents(DROP, gen.generate(), packet, data, expected_data); // Same program as before, but this time we're trying to load past the end of the data. gen = new ApfGenerator(3); - gen.addLoadImmediate(Register.R0, 20); - gen.addLoadData(Register.R1, 15); // 20 + 15 > 32 + gen.addLoadImmediate(R0, 20); + gen.addLoadData(R1, 15); // 20 + 15 > 32 gen.addJump(gen.DROP_LABEL); // Not reached. assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data); // Subtracting an immediate should work... gen = new ApfGenerator(3); - gen.addLoadImmediate(Register.R0, 20); - gen.addLoadData(Register.R1, -4); + gen.addLoadImmediate(R0, 20); + gen.addLoadData(R1, -4); gen.addJump(gen.DROP_LABEL); assertDataMemoryContents(DROP, gen.generate(), packet, data, expected_data); // ...and underflowing simply wraps around to the end of the buffer... gen = new ApfGenerator(3); - gen.addLoadImmediate(Register.R0, 20); - gen.addLoadData(Register.R1, -30); + gen.addLoadImmediate(R0, 20); + gen.addLoadData(R1, -30); gen.addJump(gen.DROP_LABEL); assertDataMemoryContents(DROP, gen.generate(), packet, data, expected_data); // ...but doesn't allow accesses before the start of the buffer gen = new ApfGenerator(3); - gen.addLoadImmediate(Register.R0, 20); - gen.addLoadData(Register.R1, -1000); + gen.addLoadImmediate(R0, 20); + gen.addLoadData(R1, -1000); gen.addJump(gen.DROP_LABEL); // Not reached. assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data); } @@ -1155,8 +1175,6 @@ private static final byte[] IPV4_MDNS_MULTICAST_ADDR = {(byte) 224, 0, 0, (byte) 251}; private static final byte[] IPV6_MDNS_MULTICAST_ADDR = {(byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfb}; - private static final int DNS_HEADER_LEN = 12; - private static final int DNS_QDCOUNT_OFFSET = 4; private static final int IPV6_UDP_DEST_PORT_OFFSET = IPV6_PAYLOAD_OFFSET + 2; private static final int MDNS_UDP_PORT = 5353; @@ -1295,8 +1313,17 @@ apfFilter.shutdown(); } - private static byte[] makeMdnsV4Packet(String qname) throws IOException { - final ByteBuffer buf = ByteBuffer.wrap(new byte[100]); + private static void fillQuestionSection(ByteBuffer buf, String... qnames) throws IOException { + buf.put(new DnsPacket.DnsHeader(0 /* id */, 0 /* flags */, qnames.length, 0 /* ancount */) + .getBytes()); + for (String qname : qnames) { + buf.put(DnsPacket.DnsRecord.makeQuestion(qname, 0 /* nsType */, 0 /* nsClass */) + .getBytes()); + } + } + + private static byte[] makeMdnsV4Packet(String... qnames) throws IOException { + final ByteBuffer buf = ByteBuffer.wrap(new byte[256]); final PacketBuilder builder = new PacketBuilder(buf); builder.writeL2Header(MacAddress.fromString("11:22:33:44:55:66"), MacAddress.fromBytes(ETH_MULTICAST_MDNS_v4_MAC_ADDRESS), @@ -1306,13 +1333,12 @@ (Inet4Address) Inet4Address.getByAddress(IPV4_SOURCE_ADDR), (Inet4Address) Inet4Address.getByAddress(IPV4_MDNS_MULTICAST_ADDR)); builder.writeUdpHeader((short) MDNS_UDP_PORT, (short) MDNS_UDP_PORT); - buf.put(new DnsPacket.DnsHeader(0, 0, 1, 0).getBytes()); - buf.put(DnsPacket.DnsRecord.makeQuestion(qname, 0, 0).getBytes()); + fillQuestionSection(buf, qnames); return builder.finalizePacket().array(); } - private static byte[] makeMdnsV6Packet(String qname) throws IOException { - ByteBuffer buf = ByteBuffer.wrap(new byte[100]); + private static byte[] makeMdnsV6Packet(String... qnames) throws IOException { + ByteBuffer buf = ByteBuffer.wrap(new byte[256]); final PacketBuilder builder = new PacketBuilder(buf); builder.writeL2Header(MacAddress.fromString("11:22:33:44:55:66"), MacAddress.fromBytes(ETH_MULTICAST_MDNS_V6_MAC_ADDRESS), @@ -1321,11 +1347,171 @@ (Inet6Address) InetAddress.getByAddress(IPV6_ANOTHER_ADDR), (Inet6Address) Inet6Address.getByAddress(IPV6_MDNS_MULTICAST_ADDR)); builder.writeUdpHeader((short) MDNS_UDP_PORT, (short) MDNS_UDP_PORT); - buf.put(new DnsPacket.DnsHeader(0, 0, 1, 0).getBytes()); - buf.put(DnsPacket.DnsRecord.makeQuestion(qname, 0, 0).getBytes()); + fillQuestionSection(buf, qnames); return builder.finalizePacket().array(); } + private static void putLabel(ByteBuffer buf, String label) { + final byte[] bytes = label.getBytes(StandardCharsets.UTF_8); + buf.put((byte) bytes.length); + buf.put(bytes); + } + + private static void putPointer(ByteBuffer buf, int offset) { + short pointer = (short) (offset | 0xc000); + buf.putShort(pointer); + } + + + // Simplistic DNS compression code that intentionally does not depend on production code. + private static List<Pair<Integer, String>> getDnsLabels(int startOffset, String... names) { + // Maps all possible name suffixes to packet offsets. + final HashMap<String, Integer> mPointerOffsets = new HashMap<>(); + final List<Pair<Integer, String>> out = new ArrayList<>(); + int offset = startOffset; + for (int i = 0; i < names.length; i++) { + String name = names[i]; + while (true) { + if (name.length() == 0) { + out.add(label("")); + offset += 1 + 4; // 1-byte label, DNS query + break; + } + + final int pointerOffset = mPointerOffsets.getOrDefault(name, -1); + if (pointerOffset != -1) { + out.add(pointer(pointerOffset)); + offset += 2 + 4; // 2-byte pointer, DNS query + break; + } + + mPointerOffsets.put(name, offset); + + final int indexOfDot = name.indexOf("."); + final String label; + if (indexOfDot == -1) { + label = name; + name = ""; + } else { + label = name.substring(0, indexOfDot); + name = name.substring(indexOfDot + 1); + } + out.add(label(label)); + offset += 1 + label.length(); + } + } + return out; + } + + static Pair<Integer, String> label(String label) { + return Pair.create(label.length(), label); + } + + static Pair<Integer, String> pointer(int offset) { + return Pair.create(0xc000 | offset, null); + } + + @Test + public void testGetDnsLabels() throws Exception { + int startOffset = 12; + List<Pair<Integer, String>> actual = getDnsLabels(startOffset, "myservice.tcp.local"); + assertEquals(4, actual.size()); + assertEquals(label("myservice"), actual.get(0)); + assertEquals(label("tcp"), actual.get(1)); + assertEquals(label("local"), actual.get(2)); + assertEquals(label(""), actual.get(3)); + + startOffset = 30; + actual = getDnsLabels(startOffset, + "myservice.tcp.local", "foo.tcp.local", "myhostname.local", "bar.udp.local", + "foo.myhostname.local"); + final int tcpLocalOffset = startOffset + 1 + "myservice".length(); + final int localOffset = startOffset + 1 + "myservice".length() + 1 + "tcp".length(); + final int myhostnameLocalOffset = 30 + + 1 + "myservice".length() + 1 + "tcp".length() + 1 + "local".length() + 1 + 4 + + 1 + "foo".length() + 2 + 4; + + assertEquals(13, actual.size()); + assertEquals(label("myservice"), actual.get(0)); + assertEquals(label("tcp"), actual.get(1)); + assertEquals(label("local"), actual.get(2)); + assertEquals(label(""), actual.get(3)); + assertEquals(label("foo"), actual.get(4)); + assertEquals(pointer(tcpLocalOffset), actual.get(5)); + assertEquals(label("myhostname"), actual.get(6)); + assertEquals(pointer(localOffset), actual.get(7)); + assertEquals(label("bar"), actual.get(8)); + assertEquals(label("udp"), actual.get(9)); + assertEquals(pointer(localOffset), actual.get(10)); + assertEquals(label("foo"), actual.get(11)); + assertEquals(pointer(myhostnameLocalOffset), actual.get(12)); + + } + + private static byte[] makeMdnsCompressedV6Packet(String... names) throws IOException { + ByteBuffer questions = ByteBuffer.allocate(1500); + questions.put(new DnsPacket.DnsHeader(123, 0, names.length, 0).getBytes()); + final List<Pair<Integer, String>> labels = getDnsLabels(questions.position(), names); + for (Pair<Integer, String> label : labels) { + final String name = label.second; + if (name == null) { + putPointer(questions, label.first); + } else { + putLabel(questions, name); + } + if (TextUtils.isEmpty(name)) { + questions.put(new byte[4]); + } + } + questions.flip(); + + ByteBuffer buf = PacketBuilder.allocate(/*hasEther=*/ true, IPPROTO_IPV6, IPPROTO_UDP, + questions.limit()); + final PacketBuilder builder = new PacketBuilder(buf); + builder.writeL2Header(MacAddress.fromString("11:22:33:44:55:66"), + MacAddress.fromBytes(ETH_MULTICAST_MDNS_V6_MAC_ADDRESS), + (short) ETH_P_IPV6); + builder.writeIpv6Header(0x680515ca /* vtf */, (byte) IPPROTO_UDP, (short) 0 /* hopLimit */, + (Inet6Address) InetAddress.getByAddress(IPV6_ANOTHER_ADDR), + (Inet6Address) Inet6Address.getByAddress(IPV6_MDNS_MULTICAST_ADDR)); + builder.writeUdpHeader((short) MDNS_UDP_PORT, (short) MDNS_UDP_PORT); + + buf.put(questions); + + return builder.finalizePacket().array(); + } + + private static byte[] makeMdnsCompressedV6Packet() throws IOException { + return makeMdnsCompressedV6Packet("myservice.tcp.local", "googlecast.tcp.local", + "matter.tcp.local", "myhostname.local"); + } + + private static byte[] makeMdnsCompressedV6PacketWithManyNames() throws IOException { + return makeMdnsCompressedV6Packet("myservice.tcp.local", "googlecast.tcp.local", + "matter.tcp.local", "myhostname.local", "myhostname2.local", "myhostname3.local", + "myhostname4.local", "myhostname5.local", "myhostname6.local", "myhostname7.local"); + + } + + /** Adds to the program a no-op instruction that is one byte long. */ + private void addOneByteNoop(ApfGenerator gen) { + gen.addOr(0); + } + + @Test + public void testAddOneByteNoopAddsOneByte() throws Exception { + ApfGenerator gen = new ApfGenerator(MIN_APF_VERSION); + addOneByteNoop(gen); + assertEquals(1, gen.generate().length); + + final int count = 42; + gen = new ApfGenerator(MIN_APF_VERSION); + for (int i = 0; i < count; i++) { + addOneByteNoop(gen); + } + assertEquals(count, gen.generate().length); + } + @Test public void testQnameEncoding() { String[] qname = new String[]{"abcd", "ef", "日本"}; @@ -1388,6 +1574,178 @@ apfFilter.shutdown(); } + private ApfGenerator generateDnsFilter(boolean ipv6, String... labels) throws Exception { + ApfGenerator gen = new ApfGenerator(MIN_APF_VERSION); + gen.addLoadImmediate(R1, ipv6 ? IPV6_HEADER_LEN : IPV4_HEADER_LEN); + DnsUtils.generateFilter(gen, labels); + return gen; + } + + private void doTestDnsParsing(boolean expectPass, boolean ipv6, String filterName, + byte[] pkt) throws Exception { + final String[] labels = filterName.split(/*regex=*/ "[.]"); + ApfGenerator gen = generateDnsFilter(ipv6, labels); + + // Hack to prevent the APF instruction limit triggering. + for (int i = 0; i < 500; i++) { + addOneByteNoop(gen); + } + + byte[] program = gen.generate(); + Log.d(TAG, "prog_len=" + program.length); + if (expectPass) { + assertPass(program, pkt, 0); + } else { + assertDrop(program, pkt, 0); + } + } + + private void doTestDnsParsing(boolean expectPass, boolean ipv6, String filterName, + String... packetNames) throws Exception { + final byte[] pkt = ipv6 ? makeMdnsV6Packet(packetNames) : makeMdnsV4Packet(packetNames); + doTestDnsParsing(expectPass, ipv6, filterName, pkt); + } + + @Test + public void testDnsParsing() throws Exception { + final boolean ipv4 = false, ipv6 = true; + + // Packets with one question. + // Names don't start with _ because DnsPacket thinks such names are invalid. + doTestDnsParsing(true, ipv6, "googlecast.tcp.local", "googlecast.tcp.local"); + doTestDnsParsing(true, ipv4, "googlecast.tcp.local", "googlecast.tcp.local"); + doTestDnsParsing(false, ipv6, "googlecast.tcp.lozal", "googlecast.tcp.local"); + doTestDnsParsing(false, ipv4, "googlecast.tcp.lozal", "googlecast.tcp.local"); + doTestDnsParsing(false, ipv6, "googlecast.udp.local", "googlecast.tcp.local"); + doTestDnsParsing(false, ipv4, "googlecast.udp.local", "googlecast.tcp.local"); + + // Packets with multiple questions that can't be compressed. Not realistic for MDNS since + // everything ends in .local, but useful to ensure only the non-compression code is tested. + doTestDnsParsing(true, ipv6, "googlecast.tcp.local", + "googlecast.tcp.local", "developer.android.com"); + doTestDnsParsing(true, ipv4, "googlecast.tcp.local", + "developer.android.com", "googlecast.tcp.local"); + doTestDnsParsing(false, ipv4, "googlecast.tcp.local", + "developer.android.com", "googlecast.tcp.invalid"); + doTestDnsParsing(true, ipv6, "googlecast.tcp.local", + "developer.android.com", "www.google.co.jp", "googlecast.tcp.local"); + doTestDnsParsing(false, ipv4, "veryverylongservicename.tcp.local", + "www.google.co.jp", "veryverylongservicename.tcp.invalid"); + doTestDnsParsing(true, ipv6, "googlecast.tcp.local", + "www.google.co.jp", "googlecast.tcp.local", "developer.android.com"); + + // Name with duplicate labels. + doTestDnsParsing(true, ipv6, "local.tcp.local", "local.tcp.local"); + + final byte[] pkt = makeMdnsCompressedV6Packet(); + doTestDnsParsing(true, ipv6, "googlecast.tcp.local", pkt); + doTestDnsParsing(true, ipv6, "matter.tcp.local", pkt); + doTestDnsParsing(true, ipv6, "myservice.tcp.local", pkt); + doTestDnsParsing(false, ipv6, "otherservice.tcp.local", pkt); + } + + private void doTestDnsParsingProgramLength(int expectedLength, + String filterName) throws Exception { + final String[] labels = filterName.split(/*regex=*/ "[.]"); + + ApfGenerator gen = generateDnsFilter(/*ipv6=*/ true, labels); + assertEquals("Program for " + filterName + " had unexpected length:", + expectedLength, gen.generate().length); + } + + /** + * Rough metric of code size. Checks how large the generated filter is in various scenarios. + * Helps ensure any changes to the code do not substantially increase APF code size. + */ + @Test + public void testDnsParsingProgramLength() throws Exception { + doTestDnsParsingProgramLength(237, "MyDevice.local"); + doTestDnsParsingProgramLength(285, "_googlecast.tcp.local"); + doTestDnsParsingProgramLength(291, "_googlecast12345.tcp.local"); + doTestDnsParsingProgramLength(244, "_googlecastZtcp.local"); + doTestDnsParsingProgramLength(249, "_googlecastZtcp12345.local"); + } + + private void doTestDnsParsingNecessaryOverhead(int expectedNecessaryOverhead, + String filterName, byte[] pkt, String description) throws Exception { + final String[] labels = filterName.split(/*regex=*/ "[.]"); + + // Check that the generated code, when the program contains the specified number of extra + // bytes, is capable of dropping the packet. + ApfGenerator gen = generateDnsFilter(/*ipv6=*/ true, labels); + for (int i = 0; i < expectedNecessaryOverhead; i++) { + addOneByteNoop(gen); + } + final byte[] programWithJustEnoughOverhead = gen.generate(); + assertVerdict( + "Overhead too low: filter for " + filterName + " with " + expectedNecessaryOverhead + + " extra instructions unexpectedly passed " + description, + DROP, programWithJustEnoughOverhead, pkt, 0); + + if (expectedNecessaryOverhead == 0) return; + + // Check that the generated code, without the specified number of extra program bytes, + // cannot correctly drop the packet because it hits the interpreter instruction limit. + gen = generateDnsFilter(/*ipv6=*/ true, labels); + for (int i = 0; i < expectedNecessaryOverhead - 1; i++) { + addOneByteNoop(gen); + } + final byte[] programWithNotEnoughOverhead = gen.generate(); + + assertVerdict( + "Overhead too high: filter for " + filterName + " with " + expectedNecessaryOverhead + + " extra instructions unexpectedly dropped " + description, + PASS, programWithNotEnoughOverhead, pkt, 0); + } + + private void doTestDnsParsingNecessaryOverhead(int expectedNecessaryOverhead, + String filterName, String... packetNames) throws Exception { + doTestDnsParsingNecessaryOverhead(expectedNecessaryOverhead, filterName, + makeMdnsV6Packet(packetNames), + "IPv6 MDNS packet containing: " + Arrays.toString(packetNames)); + } + + /** + * Rough metric of filter efficiency. Because the filter uses backwards jumps, on complex + * packets it will not finish running before the interpreter hits the maximum number of allowed + * instructions (== number of bytes in the program) and unconditionally accepts the packet. + * This test checks much extra code the program must contain in order for the generated filter + * to successfully drop the packet. It helps ensure any changes to the code do not reduce the + * complexity of packets that the APF code can drop. + */ + @Test + public void testDnsParsingNecessaryOverhead() throws Exception { + // Simple packets can be parsed with zero extra code. + doTestDnsParsingNecessaryOverhead(0, "googlecast.tcp.local", + "matter.tcp.local", "developer.android.com"); + + doTestDnsParsingNecessaryOverhead(0, "googlecast.tcp.local", + "developer.android.com", "matter.tcp.local"); + + doTestDnsParsingNecessaryOverhead(0, "googlecast.tcp.local", + "developer.android.com", "matter.tcp.local", "www.google.co.jp"); + + doTestDnsParsingNecessaryOverhead(0, "googlecast.tcp.local", + "developer.android.com", "matter.tcp.local", "www.google.co.jp", + "example.org"); + + // More complicated packets cause more instructions to be run and can only be dropped if + // the program contains lots of extra code. + doTestDnsParsingNecessaryOverhead(57, "googlecast.tcp.local", + "developer.android.com", "matter.tcp.local", "www.google.co.jp", + "example.org", "otherexample.net"); + + doTestDnsParsingNecessaryOverhead(115, "googlecast.tcp.local", + "developer.android.com", "matter.tcp.local", "www.google.co.jp", + "example.org", "otherexample.net", "docs.new"); + + doTestDnsParsingNecessaryOverhead(0, "foo.tcp.local", + makeMdnsCompressedV6Packet(), "compressed packet"); + + doTestDnsParsingNecessaryOverhead(235, "foo.tcp.local", + makeMdnsCompressedV6PacketWithManyNames(), "compressed packet with many names"); + } + @Test public void testApfFilterMulticast() throws Exception { final byte[] unicastIpv4Addr = {(byte)192,0,2,63};
diff --git a/tests/unit/src/android/net/apf/JumpTableTest.kt b/tests/unit/src/android/net/apf/JumpTableTest.kt new file mode 100644 index 0000000..6fdf38f --- /dev/null +++ b/tests/unit/src/android/net/apf/JumpTableTest.kt
@@ -0,0 +1,104 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.apf + +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.testutils.assertThrows +import java.util.NoSuchElementException +import java.util.concurrent.atomic.AtomicReference +import kotlin.test.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.inOrder +import org.mockito.MockitoAnnotations + +@RunWith(AndroidJUnit4::class) +@SmallTest +class JumpTableTest { + + @Mock + lateinit var gen: ApfGenerator + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + } + + @Test(expected = NullPointerException::class) + fun testNullStartLabel() { + // Can't use "null" because the method is @NonNull. + JumpTable(AtomicReference<String>(null).get(), 10) + } + + @Test(expected = IllegalArgumentException::class) + fun testNegativeSlot() { + JumpTable("my_jump_table", -1) + } + + @Test(expected = IllegalArgumentException::class) + fun testSlotTooLarge() { + JumpTable("my_jump_table", 13) + } + + @Test + fun testValidSlotNumbers() { + JumpTable("my_jump_table", 1) + JumpTable("my_jump_table", 10) + JumpTable("my_jump_table", 12) + } + + @Test + fun testGetStartLabel() { + assertEquals("xyz", JumpTable("xyz", 3).startLabel) + assertEquals("abc", JumpTable("abc", 9).startLabel) + } + + @Test + fun testCodeGeneration() { + val name = "my_jump_table" + val slot = 7 + + val j = JumpTable(name, slot) + j.addLabel("foo") + j.addLabel("bar") + j.addLabel("bar") + j.addLabel("baz") + + assertEquals(0, j.getIndex("foo")) + assertEquals(1, j.getIndex("bar")) + assertEquals(2, j.getIndex("baz")) + + assertThrows(NoSuchElementException::class.java) { + j.getIndex("nonexistent") + } + + val inOrder = inOrder(gen) + + j.generate(gen) + + inOrder.verify(gen).defineLabel(name) + inOrder.verify(gen).addLoadFromMemory(ApfGenerator.Register.R0, slot) + inOrder.verify(gen).addJumpIfR0Equals(0, "foo") + inOrder.verify(gen).addJumpIfR0Equals(1, "bar") + inOrder.verify(gen).addJumpIfR0Equals(2, "baz") + inOrder.verify(gen).addJump(ApfGenerator.PASS_LABEL) + inOrder.verifyNoMoreInteractions() + } +}
diff --git a/tests/unit/src/android/net/dhcp6/Dhcp6PacketTest.kt b/tests/unit/src/android/net/dhcp6/Dhcp6PacketTest.kt new file mode 100644 index 0000000..2d093f7 --- /dev/null +++ b/tests/unit/src/android/net/dhcp6/Dhcp6PacketTest.kt
@@ -0,0 +1,148 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.dhcp6 + +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.net.module.util.HexDump +import com.android.testutils.assertThrows +import java.nio.ByteBuffer +import kotlin.test.assertTrue +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +@SmallTest +class Dhcp6PacketTest { + @Test + fun testDecodeDhcp6SolicitPacket() { + val solicitHex = + // Solicit, Transaction ID + "01000F51" + + // client identifier option(option_len=12) + "0001000C0003001B024CCBFFFE5F6EA9" + + // elapsed time option(option_len=2) + "000800020000" + + // IA_PD option(option_len=41, including IA prefix option) + "00190029DE3570F50000000000000000" + + // IA prefix option(option_len=25) + "001A001900000000000000004000000000000000000000000000000000" + val bytes = HexDump.hexStringToByteArray(solicitHex) + val packet = Dhcp6Packet.decodePacket(ByteBuffer.wrap(bytes)) + assertTrue(packet is Dhcp6SolicitPacket) + } + + @Test + fun testDecodeDhcp6SolicitPacket_incorrectOptionLength() { + val solicitHex = + // Solicit, Transaction ID + "01000F51" + + // client identifier option(option_len=12) + "0001000C0003001B024CCBFFFE5F6EA9" + + // elapsed time option(wrong option_len=4) + "000800040000" + + // IA_PD option(option_len=41, including IA prefix option) + "00190029DE3570F50000000000000000" + + // IA prefix option(option_len=25) + "001A001900000000000000004000000000000000000000000000000000" + val bytes = HexDump.hexStringToByteArray(solicitHex) + assertThrows(Dhcp6Packet.ParseException::class.java) { + Dhcp6Packet.decodePacket(ByteBuffer.wrap(bytes)) + } + } + + @Test + fun testDecodeDhcp6SolicitPacket_lastTruncatedOption() { + val solicitHex = + // Solicit, Transaction ID + "01000F51" + + // client identifier option(option_len=12) + "0001000C0003001B024CCBFFFE5F6EA9" + + // elapsed time option(option_len=2) + "000800020000" + + // IA_PD option(option_len=41, including IA prefix option) + "00190029DE3570F50000000000000000" + + // IA prefix option(option_len=25, missing one byte) + "001A0019000000000000000040000000000000000000000000000000" + val bytes = HexDump.hexStringToByteArray(solicitHex) + assertThrows(Dhcp6Packet.ParseException::class.java) { + Dhcp6Packet.decodePacket(ByteBuffer.wrap(bytes)) + } + } + + @Test + fun testDecodeDhcp6SolicitPacket_middleTruncatedOption() { + val solicitHex = + // Solicit, Transaction ID + "01000F51" + + // client identifier option(option_len=12, missing one byte) + "0001000C0003001B024CCBFFFE5F6E" + + // elapsed time option(option_len=2) + "000800020000" + + // IA_PD option(option_len=41, including IA prefix option) + "00190029DE3570F50000000000000000" + + // IA prefix option(option_len=25) + "001A001900000000000000004000000000000000000000000000000000" + val bytes = HexDump.hexStringToByteArray(solicitHex) + assertThrows(Dhcp6Packet.ParseException::class.java) { + Dhcp6Packet.decodePacket(ByteBuffer.wrap(bytes)) + } + } + + @Test + fun testDecodeDhcp6AdvertisePacket() { + val advertiseHex = + // Advertise, Transaction ID + "0200078A" + + // server identifier option(option_len=10) + "0002000A0003000186C9B26AED4D" + + // client identifier option(option_len=12) + "0001000C0003001B024CCBFFFE5F6EA9" + + // IA_PD option(option_len=70, including IA prefix option) + "001900460CDDCA0C000000CF0000014C" + + // IA prefix option(option_len=25, prefix="2401:fa00:49c:412::/64") + "001A00190000019F0000064F402401FA00049C04810000000000000000" + + // IA prefix option(option_len=25, prefix="fdfd:9ed6:7950:2::/64") + "001A00190000019F0000A8C040FDFD9ED6795000010000000000000000" + val bytes = HexDump.hexStringToByteArray(advertiseHex) + val packet = Dhcp6Packet.decodePacket(ByteBuffer.wrap(bytes)) + assertTrue(packet is Dhcp6AdvertisePacket) + } + + @Test + fun testDecodeDhcp6SolicitPacket_unsupportedOption() { + val advertiseHex = + // Advertise, Transaction ID + "0200078A" + + // server identifier option(option_len=10) + "0002000A0003000186C9B26AED4D" + + // client identifier option(option_len=12) + "0001000C0003001B024CCBFFFE5F6EA9" + + // SOL_MAX_RT (don't support this option yet) + "005200040000003C" + + // IA_PD option(option_len=70, including IA prefix option) + "001900460CDDCA0C000000CF0000014C" + + // IA prefix option(option_len=25, prefix="2401:fa00:49c:412::/64") + "001A00190000019F0000064F402401FA00049C04810000000000000000" + + // IA prefix option(option_len=25, prefix="fdfd:9ed6:7950:2::/64") + "001A00190000019F0000A8C040FDFD9ED6795000010000000000000000" + val bytes = HexDump.hexStringToByteArray(advertiseHex) + // The unsupported option will be skipped normally and won't throw ParseException. + val packet = Dhcp6Packet.decodePacket(ByteBuffer.wrap(bytes)) + assertTrue(packet is Dhcp6AdvertisePacket) + } +}