Snap for 11507696 from 3fdcd1485fb2d9261c17bef35bb2d27223e2c870 to mainline-cellbroadcast-release Change-Id: I16623a72fdfc3ba59ee78cc907550d3e53e9d5b3
diff --git a/Android.bp b/Android.bp index a7f57ae..9af9da4 100644 --- a/Android.bp +++ b/Android.bp
@@ -42,6 +42,7 @@ // Common defaults to define SDK level package { + default_team: "trendy_team_fwk_core_networking", default_applicable_licenses: ["Android-Apache-2.0"], } @@ -125,9 +126,6 @@ srcs: ["apishim/common/**/*.java"], sdk_version: "system_current", visibility: ["//visibility:private"], - lint: { - baseline_filename: "lint-baseline.xml", - }, } // Each level of the shims (29, 30, ...) is its own java_library compiled against the corresponding @@ -141,9 +139,6 @@ ], sdk_version: "system_29", visibility: ["//visibility:private"], - lint: { - baseline_filename: "lint-baseline.xml", - }, } java_library { @@ -160,7 +155,7 @@ visibility: ["//visibility:private"], lint: { strict_updatability_linting: true, - baseline_filename: "lint-baseline.xml", + }, } @@ -184,9 +179,6 @@ ], sdk_version: "module_31", visibility: ["//visibility:private"], - lint: { - baseline_filename: "lint-baseline.xml", - }, } java_library { @@ -207,9 +199,6 @@ ], sdk_version: "module_33", visibility: ["//visibility:private"], - lint: { - baseline_filename: "lint-baseline.xml", - }, } java_library { @@ -231,9 +220,6 @@ ], sdk_version: module_34_version, visibility: ["//visibility:private"], - lint: { - baseline_filename: "lint-baseline.xml", - }, } // Shims for APIs being added to the current development version of Android. These APIs are not @@ -267,9 +253,6 @@ ], sdk_version: "module_current", visibility: ["//visibility:private"], - lint: { - baseline_filename: "lint-baseline.xml", - }, } // API current uses the API current shims directly. @@ -297,9 +280,6 @@ "//packages/modules/Connectivity/service-t", "//packages/modules/Connectivity/tests:__subpackages__", ], - lint: { - baseline_filename: "lint-baseline.xml", - }, } // API stable uses jarjar to rename the latest stable apishim package from @@ -326,9 +306,6 @@ "//packages/modules/Connectivity/service-t", "//packages/modules/Connectivity/tests:__subpackages__", ], - lint: { - baseline_filename: "lint-baseline.xml", - }, } // Common defaults for android libraries containing network stack code, used to compile variants of @@ -380,10 +357,6 @@ "//packages/modules/NetworkStack/tests/unit", "//packages/modules/NetworkStack/tests/integration", ], - lint: { - strict_updatability_linting: true, - baseline_filename: "lint-baseline.xml", - }, } android_library { @@ -408,10 +381,6 @@ "//packages/modules/NetworkStack/tests/unit", "//packages/modules/NetworkStack/tests/integration", ], - lint: { - strict_updatability_linting: true, - baseline_filename: "lint-baseline.xml", - }, } java_library { @@ -437,9 +406,6 @@ "//packages/modules/Connectivity/Tethering/tests/integration", "//packages/modules/Connectivity/tests/cts/net", ], - lint: { - baseline_filename: "lint-baseline.xml", - }, } java_genrule { @@ -508,9 +474,6 @@ required: [ "PlatformCaptivePortalLogin", ], - lint: { - baseline_filename: "lint-baseline.xml", - }, } // Pre-merge the AndroidManifest for NetworkStackNext, so that its manifest can be merged on top @@ -523,9 +486,6 @@ ], static_libs: ["NetworkStackApiCurrentLib"], manifest: "AndroidManifest.xml", - lint: { - baseline_filename: "lint-baseline.xml", - }, } // NetworkStack build targeting the current API release, for testing on in-development SDK @@ -543,10 +503,6 @@ "privapp_whitelist_com.android.networkstack", ], updatable: true, - lint: { - strict_updatability_linting: true, - baseline_filename: "lint-baseline.xml", - }, } // Updatable network stack for finalized API @@ -563,10 +519,6 @@ "privapp_whitelist_com.android.networkstack", ], updatable: true, - lint: { - strict_updatability_linting: true, - baseline_filename: "lint-baseline.xml", - }, } cc_library_shared { @@ -649,9 +601,6 @@ required: [ "privapp_whitelist_com.android.networkstack", ], - lint: { - baseline_filename: "lint-baseline.xml", - }, } // When adding or modifying protos, the jarjar rules and possibly proguard rules need @@ -668,7 +617,4 @@ "networkstackprotos", ], defaults: ["NetworkStackReleaseApiLevel"], - lint: { - baseline_filename: "lint-baseline.xml", - }, }
diff --git a/common/captiveportal/Android.bp b/common/captiveportal/Android.bp index ad860c9..b1e64de 100644 --- a/common/captiveportal/Android.bp +++ b/common/captiveportal/Android.bp
@@ -15,6 +15,7 @@ // package { + default_team: "trendy_team_fwk_core_networking", default_applicable_licenses: ["Android-Apache-2.0"], }
diff --git a/common/networkstackclient/Android.bp b/common/networkstackclient/Android.bp index 060f0da..c566715 100644 --- a/common/networkstackclient/Android.bp +++ b/common/networkstackclient/Android.bp
@@ -16,6 +16,7 @@ // AIDL interfaces between the core system and the networking mainline module. package { + default_team: "trendy_team_fwk_core_networking", default_applicable_licenses: ["Android-Apache-2.0"], } @@ -47,6 +48,9 @@ cpp: { enabled: false, }, + rust: { + enabled: false, + }, }, visibility: [ @@ -159,6 +163,9 @@ cpp: { enabled: false, }, + rust: { + enabled: false, + }, }, imports: ["ipmemorystore-aidl-interfaces-V10"], // TODO: have tethering depend on networkstack-client and set visibility to private @@ -201,6 +208,10 @@ version: "20", imports: ["ipmemorystore-aidl-interfaces-V10"], }, + { + version: "21", + imports: ["ipmemorystore-aidl-interfaces-V10"], + }, ], frozen: true, @@ -212,7 +223,7 @@ min_sdk_version: "30", static_libs: [ "ipmemorystore-aidl-interfaces-V10-java", - "networkstack-aidl-interfaces-V20-java", + "networkstack-aidl-interfaces-V21-java", ], visibility: ["//packages/modules/NetworkStack:__subpackages__"], apex_available: [ @@ -221,9 +232,6 @@ "com.android.tethering", "com.android.wifi", ], - lint: { - baseline_filename: "lint-baseline.xml", - }, } java_library { @@ -268,7 +276,4 @@ "com.android.tethering", "com.android.wifi", ], - lint: { - baseline_filename: "lint-baseline.xml", - }, }
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/.hash b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/.hash new file mode 100644 index 0000000..2959bec --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/.hash
@@ -0,0 +1 @@ +9bd9d687ddb816baf1faabcad0d56ac15b22c56e
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/DataStallReportParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/DataStallReportParcelable.aidl new file mode 100644 index 0000000..771deda --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/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/21/android/net/DhcpResultsParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/DhcpResultsParcelable.aidl new file mode 100644 index 0000000..31f2194 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/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/21/android/net/INetworkMonitor.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/INetworkMonitor.aidl new file mode 100644 index 0000000..fb13c0c --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/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/21/android/net/INetworkMonitorCallbacks.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/INetworkMonitorCallbacks.aidl new file mode 100644 index 0000000..36eda8e --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/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/21/android/net/INetworkStackConnector.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/INetworkStackConnector.aidl new file mode 100644 index 0000000..8120ffc --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/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/21/android/net/INetworkStackStatusCallback.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/INetworkStackStatusCallback.aidl new file mode 100644 index 0000000..0b6b778 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/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/21/android/net/InformationElementParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/InformationElementParcelable.aidl new file mode 100644 index 0000000..6103774 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/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/21/android/net/InitialConfigurationParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/InitialConfigurationParcelable.aidl new file mode 100644 index 0000000..6a597e6 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/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/21/android/net/Layer2InformationParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/Layer2InformationParcelable.aidl new file mode 100644 index 0000000..83796ee --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/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/21/android/net/Layer2PacketParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/Layer2PacketParcelable.aidl new file mode 100644 index 0000000..4b3fff5 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/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/21/android/net/NattKeepalivePacketDataParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/NattKeepalivePacketDataParcelable.aidl new file mode 100644 index 0000000..18cf954 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/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/21/android/net/NetworkTestResultParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/NetworkTestResultParcelable.aidl new file mode 100644 index 0000000..4d6d5a2 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/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/21/android/net/PrivateDnsConfigParcel.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/PrivateDnsConfigParcel.aidl new file mode 100644 index 0000000..ab62fe7 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/PrivateDnsConfigParcel.aidl
@@ -0,0 +1,44 @@ +/* + * 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(equals=true, toString=true) +parcelable PrivateDnsConfigParcel { + String hostname; + String[] ips; + int privateDnsMode = (-1) /* -1 */; + String dohName = ""; + String[] dohIps = {}; + String dohPath = ""; + int dohPort = (-1) /* -1 */; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/ProvisioningConfigurationParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/ProvisioningConfigurationParcelable.aidl new file mode 100644 index 0000000..0ce91f0 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/ProvisioningConfigurationParcelable.aidl
@@ -0,0 +1,65 @@ +/* +** +** 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; + int creatorUid; + int hostnameSetting; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/ScanResultInfoParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/ScanResultInfoParcelable.aidl new file mode 100644 index 0000000..94fc27f --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/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/21/android/net/TcpKeepalivePacketDataParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/TcpKeepalivePacketDataParcelable.aidl new file mode 100644 index 0000000..0e1c21c --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/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/21/android/net/dhcp/DhcpLeaseParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/dhcp/DhcpLeaseParcelable.aidl new file mode 100644 index 0000000..3cd8860 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/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/21/android/net/dhcp/DhcpServingParamsParcel.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/dhcp/DhcpServingParamsParcel.aidl new file mode 100644 index 0000000..7997936 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/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/21/android/net/dhcp/IDhcpEventCallbacks.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/dhcp/IDhcpEventCallbacks.aidl new file mode 100644 index 0000000..9312f47 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/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/21/android/net/dhcp/IDhcpServer.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/dhcp/IDhcpServer.aidl new file mode 100644 index 0000000..1109f35 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/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/21/android/net/dhcp/IDhcpServerCallbacks.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/dhcp/IDhcpServerCallbacks.aidl new file mode 100644 index 0000000..ab8577c --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/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/21/android/net/ip/IIpClient.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/ip/IIpClient.aidl new file mode 100644 index 0000000..87de4a6 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/ip/IIpClient.aidl
@@ -0,0 +1,62 @@ +/** + * 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); + oneway void updateApfCapabilities(in android.net.apf.ApfCapabilities apfCapabilities); + 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; + const int HOSTNAME_SETTING_UNSET = 0x00; + const int HOSTNAME_SETTING_SEND = 0x01; + const int HOSTNAME_SETTING_DO_NOT_SEND = 0x02; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/ip/IIpClientCallbacks.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/ip/IIpClientCallbacks.aidl new file mode 100644 index 0000000..9d36419 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/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/21/android/net/networkstack/aidl/NetworkMonitorParameters.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/networkstack/aidl/NetworkMonitorParameters.aidl new file mode 100644 index 0000000..2ab9db0 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/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/21/android/net/networkstack/aidl/dhcp/DhcpOption.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/networkstack/aidl/dhcp/DhcpOption.aidl new file mode 100644 index 0000000..eea3e0d --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/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/21/android/net/networkstack/aidl/ip/ReachabilityLossInfoParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/networkstack/aidl/ip/ReachabilityLossInfoParcelable.aidl new file mode 100644 index 0000000..bb88434 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/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/21/android/net/networkstack/aidl/ip/ReachabilityLossReason.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/android/net/networkstack/aidl/ip/ReachabilityLossReason.aidl new file mode 100644 index 0000000..f9bb3c4 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/21/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/ProvisioningConfigurationParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/ProvisioningConfigurationParcelable.aidl index 7061f1e..0ce91f0 100644 --- a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/ProvisioningConfigurationParcelable.aidl +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/ProvisioningConfigurationParcelable.aidl
@@ -61,4 +61,5 @@ int ipv6ProvisioningMode; boolean uniqueEui64AddressesOnly; int creatorUid; + int hostnameSetting; }
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 b81ec20..87de4a6 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
@@ -56,4 +56,7 @@ const int PROV_IPV6_DISABLED = 0x00; const int PROV_IPV6_SLAAC = 0x01; const int PROV_IPV6_LINKLOCAL = 0x02; + const int HOSTNAME_SETTING_UNSET = 0x00; + const int HOSTNAME_SETTING_SEND = 0x01; + const int HOSTNAME_SETTING_DO_NOT_SEND = 0x02; }
diff --git a/common/networkstackclient/lint-baseline.xml b/common/networkstackclient/lint-baseline.xml deleted file mode 100644 index 046b948..0000000 --- a/common/networkstackclient/lint-baseline.xml +++ /dev/null
@@ -1,169 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev"> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `android.net.KeepalivePacketData#getDstAddress`" - errorLine1=" final InetAddress dstAddress = pkt.getDstAddress();" - errorLine2=" ~~~~~~~~~~~~~"> - <location - file="packages/modules/NetworkStack/common/networkstackclient/src/android/net/util/KeepalivePacketDataUtil.java" - line="54" - column="44"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `android.net.KeepalivePacketData#getDstAddress`" - errorLine1=" final InetAddress dstAddress = pkt.getDstAddress();" - errorLine2=" ~~~~~~~~~~~~~"> - <location - file="packages/modules/NetworkStack/common/networkstackclient/src/android/net/util/KeepalivePacketDataUtil.java" - line="70" - column="44"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `android.net.KeepalivePacketData#getDstAddress`" - errorLine1=" p.dstAddress = data.getDstAddress().getAddress();" - errorLine2=" ~~~~~~~~~~~~~"> - <location - file="packages/modules/NetworkStack/common/networkstackclient/src/android/net/util/KeepalivePacketDataUtil.java" - line="136" - column="29"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `android.net.KeepalivePacketData#getDstPort`" - errorLine1=" p.dstPort = data.getDstPort();" - errorLine2=" ~~~~~~~~~~"> - <location - file="packages/modules/NetworkStack/common/networkstackclient/src/android/net/util/KeepalivePacketDataUtil.java" - line="137" - column="26"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `android.net.KeepalivePacketData#getDstPort`" - errorLine1=" parcel.dstPort = pkt.getDstPort();" - errorLine2=" ~~~~~~~~~~"> - <location - file="packages/modules/NetworkStack/common/networkstackclient/src/android/net/util/KeepalivePacketDataUtil.java" - line="58" - column="30"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `android.net.KeepalivePacketData#getDstPort`" - errorLine1=" parcel.dstPort = pkt.getDstPort();" - errorLine2=" ~~~~~~~~~~"> - <location - file="packages/modules/NetworkStack/common/networkstackclient/src/android/net/util/KeepalivePacketDataUtil.java" - line="74" - column="30"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `android.net.KeepalivePacketData#getPacket`" - errorLine1=" final ByteBuffer buffer = ByteBuffer.wrap(data.getPacket());" - errorLine2=" ~~~~~~~~~"> - <location - file="packages/modules/NetworkStack/common/networkstackclient/src/android/net/util/KeepalivePacketDataUtil.java" - line="110" - column="56"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `android.net.KeepalivePacketData#getSrcAddress`" - errorLine1=" final InetAddress srcAddress = pkt.getSrcAddress();" - errorLine2=" ~~~~~~~~~~~~~"> - <location - file="packages/modules/NetworkStack/common/networkstackclient/src/android/net/util/KeepalivePacketDataUtil.java" - line="53" - column="44"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `android.net.KeepalivePacketData#getSrcAddress`" - errorLine1=" final InetAddress srcAddress = pkt.getSrcAddress();" - errorLine2=" ~~~~~~~~~~~~~"> - <location - file="packages/modules/NetworkStack/common/networkstackclient/src/android/net/util/KeepalivePacketDataUtil.java" - line="69" - column="44"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `android.net.KeepalivePacketData#getSrcAddress`" - errorLine1=" p.srcAddress = data.getSrcAddress().getAddress();" - errorLine2=" ~~~~~~~~~~~~~"> - <location - file="packages/modules/NetworkStack/common/networkstackclient/src/android/net/util/KeepalivePacketDataUtil.java" - line="134" - column="29"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `android.net.KeepalivePacketData#getSrcPort`" - errorLine1=" p.srcPort = data.getSrcPort();" - errorLine2=" ~~~~~~~~~~"> - <location - file="packages/modules/NetworkStack/common/networkstackclient/src/android/net/util/KeepalivePacketDataUtil.java" - line="135" - column="26"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `android.net.KeepalivePacketData#getSrcPort`" - errorLine1=" parcel.srcPort = pkt.getSrcPort();" - errorLine2=" ~~~~~~~~~~"> - <location - file="packages/modules/NetworkStack/common/networkstackclient/src/android/net/util/KeepalivePacketDataUtil.java" - line="56" - column="30"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `android.net.KeepalivePacketData#getSrcPort`" - errorLine1=" parcel.srcPort = pkt.getSrcPort();" - errorLine2=" ~~~~~~~~~~"> - <location - file="packages/modules/NetworkStack/common/networkstackclient/src/android/net/util/KeepalivePacketDataUtil.java" - line="72" - column="30"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `android.net.NetworkStack#getService`" - errorLine1=" while ((nss = NetworkStack.getService()) == null) {" - errorLine2=" ~~~~~~~~~~"> - <location - file="packages/modules/NetworkStack/common/networkstackclient/src/android/net/networkstack/ModuleNetworkStackClient.java" - line="87" - column="40"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `android.net.NetworkStack#getService`" - errorLine1=" final IBinder nss = NetworkStack.getService();" - errorLine2=" ~~~~~~~~~~"> - <location - file="packages/modules/NetworkStack/common/networkstackclient/src/android/net/networkstack/ModuleNetworkStackClient.java" - line="68" - column="42"/> - </issue> - -</issues> \ No newline at end of file
diff --git a/common/networkstackclient/src/android/net/ProvisioningConfigurationParcelable.aidl b/common/networkstackclient/src/android/net/ProvisioningConfigurationParcelable.aidl index 7ab612f..fa4b263 100644 --- a/common/networkstackclient/src/android/net/ProvisioningConfigurationParcelable.aidl +++ b/common/networkstackclient/src/android/net/ProvisioningConfigurationParcelable.aidl
@@ -51,4 +51,5 @@ int ipv6ProvisioningMode; boolean uniqueEui64AddressesOnly; int creatorUid; + int hostnameSetting; }
diff --git a/common/networkstackclient/src/android/net/ip/IIpClient.aidl b/common/networkstackclient/src/android/net/ip/IIpClient.aidl index 6c3346e..ad5b2e2 100644 --- a/common/networkstackclient/src/android/net/ip/IIpClient.aidl +++ b/common/networkstackclient/src/android/net/ip/IIpClient.aidl
@@ -54,6 +54,21 @@ */ const int PROV_IPV6_LINKLOCAL = 0x02; + /** + * Unset hostname setting. + */ + const int HOSTNAME_SETTING_UNSET = 0x00; + + /** + * Send hostname to IP provisioning server. + */ + const int HOSTNAME_SETTING_SEND = 0x01; + + /** + * Do not send hostname to IP provisioning server. + */ + const int HOSTNAME_SETTING_DO_NOT_SEND = 0x02; + void completedPreDhcpAction(); void confirmConfiguration(); void readPacketFilterComplete(in byte[] data);
diff --git a/common/networkstackclient/src/android/net/shared/ProvisioningConfiguration.java b/common/networkstackclient/src/android/net/shared/ProvisioningConfiguration.java index 16ad201..d4383d2 100644 --- a/common/networkstackclient/src/android/net/shared/ProvisioningConfiguration.java +++ b/common/networkstackclient/src/android/net/shared/ProvisioningConfiguration.java
@@ -16,6 +16,7 @@ package android.net.shared; +import static android.net.ip.IIpClient.HOSTNAME_SETTING_UNSET; import static android.net.ip.IIpClient.PROV_IPV4_DHCP; import static android.net.ip.IIpClient.PROV_IPV4_DISABLED; import static android.net.ip.IIpClient.PROV_IPV4_STATIC; @@ -290,6 +291,17 @@ } /** + * Specify the hostname setting to use during IP provisioning. + * - {@link IIpClient#HOSTNAME_SETTING_UNSET}: Default value. + * - {@link IIpClient#HOSTNAME_SETTING_SEND}: Send the hostname. + * - {@link IIpClient#HOSTNAME_SETTING_DO_NOT_SEND}: Don't send the hostname. + */ + public Builder withHostnameSetting(int setting) { + mConfig.mHostnameSetting = setting; + return this; + } + + /** * Build the configuration using previously specified parameters. */ public ProvisioningConfiguration build() { @@ -501,6 +513,7 @@ public int mIPv4ProvisioningMode = PROV_IPV4_DHCP; public int mIPv6ProvisioningMode = PROV_IPV6_SLAAC; public int mCreatorUid; + public int mHostnameSetting = HOSTNAME_SETTING_UNSET; public ProvisioningConfiguration() {} // used by Builder @@ -525,6 +538,7 @@ mDhcpOptions = other.mDhcpOptions; mIPv4ProvisioningMode = other.mIPv4ProvisioningMode; mIPv6ProvisioningMode = other.mIPv6ProvisioningMode; + mHostnameSetting = other.mHostnameSetting; } /** @@ -554,6 +568,7 @@ p.scanResultInfo = (mScanResultInfo == null) ? null : mScanResultInfo.toStableParcelable(); p.layer2Info = (mLayer2Info == null) ? null : mLayer2Info.toStableParcelable(); p.options = (mDhcpOptions == null) ? null : new ArrayList<>(mDhcpOptions); + p.hostnameSetting = mHostnameSetting; return p; } @@ -594,6 +609,7 @@ config.mIPv4ProvisioningMode = p.ipv4ProvisioningMode; config.mIPv6ProvisioningMode = p.ipv6ProvisioningMode; } + config.mHostnameSetting = p.hostnameSetting; return config; } @@ -648,6 +664,7 @@ .add("mDhcpOptions: " + mDhcpOptions) .add("mIPv4ProvisioningMode: " + ipv4ProvisioningMode) .add("mIPv6ProvisioningMode: " + ipv6ProvisioningMode) + .add("mHostnameSetting: " + mHostnameSetting) .toString(); } @@ -694,7 +711,8 @@ && dhcpOptionListEquals(mDhcpOptions, other.mDhcpOptions) && mIPv4ProvisioningMode == other.mIPv4ProvisioningMode && mIPv6ProvisioningMode == other.mIPv6ProvisioningMode - && mCreatorUid == other.mCreatorUid; + && mCreatorUid == other.mCreatorUid + && mHostnameSetting == other.mHostnameSetting; } public boolean isValid() {
diff --git a/lint-baseline.xml b/lint-baseline.xml deleted file mode 100644 index 1517328..0000000 --- a/lint-baseline.xml +++ /dev/null
@@ -1,48 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev"> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `android.net.CaptivePortalData#getUserPortalUrl`" - errorLine1=" mLinkProperties.getCaptivePortalData().getUserPortalUrl()" - errorLine2=" ~~~~~~~~~~~~~~~~"> - <location - file="packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java" - line="1734" - column="72"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `android.net.CaptivePortalData#getUserPortalUrl`" - errorLine1=" mLinkProperties.getCaptivePortalData().getUserPortalUrl()" - errorLine2=" ~~~~~~~~~~~~~~~~"> - <location - file="packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java" - line="1370" - column="64"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `android.net.LinkProperties#getCaptivePortalData`" - errorLine1=" mLinkProperties.getCaptivePortalData().getUserPortalUrl()" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> - <location - file="packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java" - line="1734" - column="49"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `android.net.LinkProperties#getCaptivePortalData`" - errorLine1=" mLinkProperties.getCaptivePortalData().getUserPortalUrl()" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> - <location - file="packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java" - line="1370" - column="41"/> - </issue> - -</issues> \ No newline at end of file
diff --git a/src/android/net/apf/ApfCounterTracker.java b/src/android/net/apf/ApfCounterTracker.java index b02efa0..b2b52e9 100644 --- a/src/android/net/apf/ApfCounterTracker.java +++ b/src/android/net/apf/ApfCounterTracker.java
@@ -17,6 +17,7 @@ package android.net.apf; import android.util.ArrayMap; +import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -44,6 +45,9 @@ TOTAL_PACKETS, // hardcoded in APFv6 interpreter PASSED_ALLOCATE_FAILURE, // hardcoded in APFv6 interpreter PASSED_TRANSMIT_FAILURE, // hardcoded in APFv6 interpreter + CORRUPT_DNS_PACKET, // hardcoded in APFv6 interpreter + FILTER_AGE_SECONDS, + FILTER_AGE_16384THS, PASSED_ARP, PASSED_DHCP, PASSED_IPV4, @@ -89,6 +93,14 @@ } /** + * Returns the counter sequence number from the end of the APF data segment for + * a given counter. + */ + public int value() { + return this.ordinal(); + } + + /** * Returns the total size of the data segment in bytes. */ public static int totalSize() { @@ -96,6 +108,8 @@ } } + private static final String TAG = ApfCounterTracker.class.getSimpleName(); + private final List<Counter> mCounterList; // Store the counters' value private final Map<Counter, Long> mCounters = new ArrayMap<>(); @@ -110,17 +124,31 @@ */ public static long getCounterValue(byte[] data, Counter counter) throws ArrayIndexOutOfBoundsException { + int offset = data.length + Counter.ENDIANNESS.offset(); + int endianness = 0; + for (int i = 0; i < 4; i++) { + endianness = endianness << 8 | (data[offset + i] & 0xff); + } // Follow the same wrap-around addressing scheme of the interpreter. - int offset = counter.offset(); - if (offset < 0) { - offset = data.length + offset; + offset = data.length + counter.offset(); + + boolean isBe = true; + switch (endianness) { + case 0: + case 0x12345678: + isBe = true; + break; + case 0x78563412: + isBe = false; + break; + default: + Log.wtf(TAG, "Unknown endianness: 0x" + Integer.toHexString(endianness)); } // Decode 32bit big-endian integer into a long so we can count up beyond 2^31. long value = 0; for (int i = 0; i < 4; i++) { - value = value << 8 | (data[offset] & 0xFF); - offset++; + value = value << 8 | (data[offset + (isBe ? i : 3 - i)] & 0xff); } return value; }
diff --git a/src/android/net/apf/ApfFilter.java b/src/android/net/apf/ApfFilter.java index 9a3d58b..0b2c101 100644 --- a/src/android/net/apf/ApfFilter.java +++ b/src/android/net/apf/ApfFilter.java
@@ -16,6 +16,8 @@ package android.net.apf; +import static android.net.apf.BaseApfGenerator.Register.R0; +import static android.net.apf.BaseApfGenerator.Register.R1; import static android.net.util.SocketUtils.makePacketSocketAddress; import static android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED; import static android.os.PowerManager.ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED; @@ -46,8 +48,7 @@ import android.net.NattKeepalivePacketDataParcelable; import android.net.TcpKeepalivePacketDataParcelable; import android.net.apf.ApfCounterTracker.Counter; -import android.net.apf.ApfV4Generator.IllegalInstructionException; -import android.net.apf.ApfV4Generator.Register; +import android.net.apf.BaseApfGenerator.IllegalInstructionException; import android.net.ip.IpClient.IpClientCallbacksWrapper; import android.os.PowerManager; import android.os.SystemClock; @@ -141,7 +142,7 @@ */ private void maybeSetupCounter(ApfV4Generator gen, Counter c) { if (mApfCapabilities.hasDataAccess()) { - gen.addLoadImmediate(Register.R1, c.offset()); + gen.addLoadImmediate(R1, c.offset()); } } @@ -1049,10 +1050,10 @@ // // if lft < (oldLft + 2) // 3 -> PASS // if lft > oldLft -> PASS - // gen.addJumpIfR0LessThan((int) ((section.lifetime + 2) / 3), + // gen.addJumpIfR0LessThan(((section.lifetime + 2) / 3), // nextFilterLabel); if (lft < (section.lifetime + 2) / 3) return MatchType.MATCH_PASS; - // gen.addJumpIfR0GreaterThan((int) section.lifetime, nextFilterLabel); + // gen.addJumpIfR0GreaterThan(section.lifetime, nextFilterLabel); if (lft > section.lifetime) return MatchType.MATCH_PASS; } else if (section.lifetime < section.min) { // Case 2a) 0 < old lft < min @@ -1076,7 +1077,7 @@ // if lft > oldLft -> PASS // gen.addJumpIfR0Equals(0, nextFilterLabel); if (lft == 0) return MatchType.MATCH_PASS; - // gen.addJumpIfR0GreaterThan((int) section.lifetime, nextFilterLabel); + // gen.addJumpIfR0GreaterThan(section.lifetime, nextFilterLabel); if (lft > section.lifetime) return MatchType.MATCH_PASS; } else { // Case 4a) otherwise @@ -1089,10 +1090,10 @@ if (lft == 0) return MatchType.MATCH_PASS; // gen.addJumpIfR0LessThan(section.min, continueLabel); if (lft < section.min) continue; - // gen.addJumpIfR0LessThan((int) ((section.lifetime + 2) / 3), + // gen.addJumpIfR0LessThan(((section.lifetime + 2) / 3), // nextFilterLabel); if (lft < (section.lifetime + 2) / 3) return MatchType.MATCH_PASS; - // gen.addJumpIfR0GreaterThan((int) section.lifetime, nextFilterLabel); + // gen.addJumpIfR0GreaterThan(section.lifetime, nextFilterLabel); if (lft > section.lifetime) return MatchType.MATCH_PASS; } } @@ -1138,15 +1139,15 @@ throws IllegalInstructionException { String nextFilterLabel = "Ra" + getUniqueNumberLocked(); // Skip if packet is not the right size - gen.addLoadFromMemory(Register.R0, gen.PACKET_SIZE_MEMORY_SLOT); + gen.addLoadFromMemory(R0, gen.PACKET_SIZE_MEMORY_SLOT); gen.addJumpIfR0NotEquals(mPacket.capacity(), nextFilterLabel); // Skip filter if expired - gen.addLoadFromMemory(Register.R0, gen.FILTER_AGE_MEMORY_SLOT); + gen.addLoadFromMemory(R0, gen.FILTER_AGE_MEMORY_SLOT); gen.addJumpIfR0GreaterThan(getRemainingFilterLft(timeSeconds), nextFilterLabel); for (PacketSection section : mPacketSections) { // Generate code to match the packet bytes. if (section.type == PacketSection.Type.MATCH) { - gen.addLoadImmediate(Register.R0, section.start); + gen.addLoadImmediate(R0, section.start); gen.addJumpIfBytesAtR0NotEqual( Arrays.copyOfRange(mPacket.array(), section.start, section.start + section.length), @@ -1154,8 +1155,8 @@ } else { switch (section.length) { // length asserted to be either 2 or 4 on PacketSection construction - case 2: gen.addLoad16(Register.R0, section.start); break; - case 4: gen.addLoad32(Register.R0, section.start); break; + case 2: gen.addLoad16(R0, section.start); break; + case 4: gen.addLoad32(R0, section.start); break; } // WARNING: keep this in sync with matches()! @@ -1177,9 +1178,9 @@ // // if lft < (oldLft + 2) // 3 -> PASS // if lft > oldLft -> PASS - gen.addJumpIfR0LessThan((int) ((section.lifetime + 2) / 3), + gen.addJumpIfR0LessThan(((section.lifetime + 2) / 3), nextFilterLabel); - gen.addJumpIfR0GreaterThan((int) section.lifetime, nextFilterLabel); + gen.addJumpIfR0GreaterThan(section.lifetime, nextFilterLabel); } else if (section.lifetime < section.min) { // Case 2a) 0 < old lft < min // @@ -1199,7 +1200,7 @@ // if lft == 0 -> PASS // if lft > oldLft -> PASS gen.addJumpIfR0Equals(0, nextFilterLabel); - gen.addJumpIfR0GreaterThan((int) section.lifetime, nextFilterLabel); + gen.addJumpIfR0GreaterThan(section.lifetime, nextFilterLabel); } else { final String continueLabel = "Continue" + getUniqueNumberLocked(); // Case 4a) otherwise @@ -1210,9 +1211,9 @@ // if lft > oldLft -> PASS gen.addJumpIfR0Equals(0, nextFilterLabel); gen.addJumpIfR0LessThan(section.min, continueLabel); - gen.addJumpIfR0LessThan((int) ((section.lifetime + 2) / 3), + gen.addJumpIfR0LessThan(((section.lifetime + 2) / 3), nextFilterLabel); - gen.addJumpIfR0GreaterThan((int) section.lifetime, nextFilterLabel); + gen.addJumpIfR0GreaterThan(section.lifetime, nextFilterLabel); // CONTINUE gen.defineLabel(continueLabel); @@ -1282,21 +1283,21 @@ void generateFilterLocked(ApfV4Generator gen) throws IllegalInstructionException { final String nextFilterLabel = "natt_keepalive_filter" + getUniqueNumberLocked(); - gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN + IPV4_SRC_ADDR_OFFSET); + gen.addLoadImmediate(R0, ETH_HEADER_LEN + IPV4_SRC_ADDR_OFFSET); gen.addJumpIfBytesAtR0NotEqual(mSrcDstAddr, nextFilterLabel); // A NAT-T keepalive packet contains 1 byte payload with the value 0xff // Check payload length is 1 - gen.addLoadFromMemory(Register.R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); + gen.addLoadFromMemory(R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); gen.addAdd(UDP_HEADER_LEN); gen.addSwap(); - gen.addLoad16(Register.R0, IPV4_TOTAL_LENGTH_OFFSET); - gen.addNeg(Register.R1); + gen.addLoad16(R0, IPV4_TOTAL_LENGTH_OFFSET); + gen.addNeg(R1); gen.addAddR1(); gen.addJumpIfR0NotEquals(1, nextFilterLabel); // Check that the ports match - gen.addLoadFromMemory(Register.R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); + gen.addLoadFromMemory(R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); gen.addAdd(ETH_HEADER_LEN); gen.addJumpIfBytesAtR0NotEqual(mPortFingerprint, nextFilterLabel); @@ -1398,28 +1399,28 @@ void generateFilterLocked(ApfV4Generator gen) throws IllegalInstructionException { final String nextFilterLabel = "keepalive_ack" + getUniqueNumberLocked(); - gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN + IPV4_SRC_ADDR_OFFSET); + gen.addLoadImmediate(R0, ETH_HEADER_LEN + IPV4_SRC_ADDR_OFFSET); gen.addJumpIfBytesAtR0NotEqual(mSrcDstAddr, nextFilterLabel); // Skip to the next filter if it's not zero-sized : // TCP_HEADER_SIZE + IPV4_HEADER_SIZE - ipv4_total_length == 0 // Load the IP header size into R1 - gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); + gen.addLoadFromMemory(R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); // Load the TCP header size into R0 (it's indexed by R1) - gen.addLoad8Indexed(Register.R0, ETH_HEADER_LEN + TCP_HEADER_SIZE_OFFSET); + gen.addLoad8Indexed(R0, ETH_HEADER_LEN + TCP_HEADER_SIZE_OFFSET); // Size offset is in the top nibble, but it must be multiplied by 4, and the two // top bits of the low nibble are guaranteed to be zeroes. Right-shift R0 by 2. gen.addRightShift(2); // R0 += R1 -> R0 contains TCP + IP headers length gen.addAddR1(); // Load IPv4 total length - gen.addLoad16(Register.R1, IPV4_TOTAL_LENGTH_OFFSET); - gen.addNeg(Register.R0); + gen.addLoad16(R1, IPV4_TOTAL_LENGTH_OFFSET); + gen.addNeg(R0); gen.addAddR1(); gen.addJumpIfR0NotEquals(0, nextFilterLabel); // Add IPv4 header length - gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); - gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN); + gen.addLoadFromMemory(R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); + gen.addLoadImmediate(R0, ETH_HEADER_LEN); gen.addAddR1(); gen.addJumpIfBytesAtR0NotEqual(mPortSeqAckFingerprint, nextFilterLabel); @@ -1521,23 +1522,23 @@ final String checkTargetIPv4 = "checkTargetIPv4"; // Drop if not ARP IPv4. - gen.addLoadImmediate(Register.R0, ARP_HEADER_OFFSET); + gen.addLoadImmediate(R0, ARP_HEADER_OFFSET); maybeSetupCounter(gen, Counter.DROPPED_ARP_NON_IPV4); gen.addJumpIfBytesAtR0NotEqual(ARP_IPV4_HEADER, mCountAndDropLabel); // Drop if unknown ARP opcode. - gen.addLoad16(Register.R0, ARP_OPCODE_OFFSET); + gen.addLoad16(R0, ARP_OPCODE_OFFSET); gen.addJumpIfR0Equals(ARP_OPCODE_REQUEST, checkTargetIPv4); // Skip to unicast check maybeSetupCounter(gen, Counter.DROPPED_ARP_UNKNOWN); gen.addJumpIfR0NotEquals(ARP_OPCODE_REPLY, mCountAndDropLabel); // Drop if ARP reply source IP is 0.0.0.0 - gen.addLoad32(Register.R0, ARP_SOURCE_IP_ADDRESS_OFFSET); + gen.addLoad32(R0, ARP_SOURCE_IP_ADDRESS_OFFSET); maybeSetupCounter(gen, Counter.DROPPED_ARP_REPLY_SPA_NO_HOST); gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, mCountAndDropLabel); // Pass if non-broadcast reply. - gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET); + gen.addLoadImmediate(R0, ETH_DEST_ADDR_OFFSET); maybeSetupCounter(gen, Counter.PASSED_ARP_UNICAST_REPLY); gen.addJumpIfBytesAtR0NotEqual(ETHER_BROADCAST, mCountAndPassLabel); @@ -1545,13 +1546,13 @@ gen.defineLabel(checkTargetIPv4); if (mIPv4Address == null) { // When there is no IPv4 address, drop GARP replies (b/29404209). - gen.addLoad32(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET); + gen.addLoad32(R0, ARP_TARGET_IP_ADDRESS_OFFSET); maybeSetupCounter(gen, Counter.DROPPED_GARP_REPLY); gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, mCountAndDropLabel); } else { // When there is an IPv4 address, drop unicast/broadcast requests // and broadcast replies with a different target IPv4 address. - gen.addLoadImmediate(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET); + gen.addLoadImmediate(R0, ARP_TARGET_IP_ADDRESS_OFFSET); maybeSetupCounter(gen, Counter.DROPPED_ARP_OTHER_HOST); gen.addJumpIfBytesAtR0NotEqual(mIPv4Address, mCountAndDropLabel); } @@ -1588,17 +1589,17 @@ // Pass DHCP addressed to us. // Check it's UDP. - gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET); + gen.addLoad8(R0, IPV4_PROTOCOL_OFFSET); gen.addJumpIfR0NotEquals(IPPROTO_UDP, skipDhcpv4Filter); // Check it's not a fragment or is the initial fragment. - gen.addLoad16(Register.R0, IPV4_FRAGMENT_OFFSET_OFFSET); + gen.addLoad16(R0, IPV4_FRAGMENT_OFFSET_OFFSET); gen.addJumpIfR0AnyBitsSet(IPV4_FRAGMENT_OFFSET_MASK, skipDhcpv4Filter); // Check it's addressed to DHCP client port. - gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); - gen.addLoad16Indexed(Register.R0, TCP_UDP_DESTINATION_PORT_OFFSET); + gen.addLoadFromMemory(R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); + gen.addLoad16Indexed(R0, TCP_UDP_DESTINATION_PORT_OFFSET); gen.addJumpIfR0NotEquals(DHCP_CLIENT_PORT, skipDhcpv4Filter); // Check it's DHCP to our MAC address. - gen.addLoadImmediate(Register.R0, DHCP_CLIENT_MAC_OFFSET); + gen.addLoadImmediate(R0, DHCP_CLIENT_MAC_OFFSET); // NOTE: Relies on R1 containing IPv4 header offset. gen.addAddR1(); gen.addJumpIfBytesAtR0NotEqual(mHardwareAddress, skipDhcpv4Filter); @@ -1609,14 +1610,14 @@ gen.defineLabel(skipDhcpv4Filter); // If IPv4 destination address is in multicast range, drop. - gen.addLoad8(Register.R0, IPV4_DEST_ADDR_OFFSET); + gen.addLoad8(R0, IPV4_DEST_ADDR_OFFSET); gen.addAnd(0xf0); maybeSetupCounter(gen, Counter.DROPPED_IPV4_MULTICAST); gen.addJumpIfR0Equals(0xe0, mCountAndDropLabel); // If IPv4 broadcast packet, drop regardless of L2 (b/30231088). maybeSetupCounter(gen, Counter.DROPPED_IPV4_BROADCAST_ADDR); - gen.addLoad32(Register.R0, IPV4_DEST_ADDR_OFFSET); + gen.addLoad32(R0, IPV4_DEST_ADDR_OFFSET); gen.addJumpIfR0Equals(IPV4_BROADCAST_ADDRESS, mCountAndDropLabel); if (mIPv4Address != null && mIPv4PrefixLength < 31) { maybeSetupCounter(gen, Counter.DROPPED_IPV4_BROADCAST_NET); @@ -1639,7 +1640,7 @@ // If L2 broadcast packet, drop. // TODO: can we invert this condition to fall through to the common pass case below? maybeSetupCounter(gen, Counter.PASSED_IPV4_UNICAST); - gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET); + gen.addLoadImmediate(R0, ETH_DEST_ADDR_OFFSET); gen.addJumpIfBytesAtR0NotEqual(ETHER_BROADCAST, mCountAndPassLabel); maybeSetupCounter(gen, Counter.DROPPED_IPV4_L2_BROADCAST); gen.addJump(mCountAndDropLabel); @@ -1660,7 +1661,7 @@ if (!haveKeepaliveResponses) return; // If not the right proto, skip keepalive filters - gen.addLoad8(Register.R0, offset); + gen.addLoad8(R0, offset); gen.addJumpIfR0NotEquals(proto, label); // Drop Keepalive responses @@ -1709,7 +1710,7 @@ // if keepalive ack // drop - gen.addLoad8(Register.R0, IPV6_NEXT_HEADER_OFFSET); + gen.addLoad8(R0, IPV6_NEXT_HEADER_OFFSET); // MLD packets set the router-alert hop-by-hop option. // TODO: be smarter about not blindly passing every packet with HBH options. @@ -1728,7 +1729,7 @@ // ICMPv6 but not ECHO? -> Skip the multicast filter. // (ICMPv6 ECHO requests will go through the multicast filter below). - gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET); + gen.addLoad8(R0, ICMP6_TYPE_OFFSET); gen.addJumpIfR0NotEquals(ICMPV6_ECHO_REQUEST_TYPE, skipIPv6MulticastFilterLabel); } else { gen.addJumpIfR0Equals(IPPROTO_ICMPV6, skipIPv6MulticastFilterLabel); @@ -1737,7 +1738,7 @@ // Drop all other packets sent to ff00::/8 (multicast prefix). gen.defineLabel(dropAllIPv6MulticastsLabel); maybeSetupCounter(gen, Counter.DROPPED_IPV6_NON_ICMP_MULTICAST); - gen.addLoad8(Register.R0, IPV6_DEST_ADDR_OFFSET); + gen.addLoad8(R0, IPV6_DEST_ADDR_OFFSET); gen.addJumpIfR0Equals(0xff, mCountAndDropLabel); // If any keepalive filter matches, drop generateV6KeepaliveFilters(gen); @@ -1756,7 +1757,7 @@ // Add unsolicited multicast neighbor announcements filter String skipUnsolicitedMulticastNALabel = "skipUnsolicitedMulticastNA"; - gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET); + gen.addLoad8(R0, ICMP6_TYPE_OFFSET); // Drop all router solicitations (b/32833400) maybeSetupCounter(gen, Counter.DROPPED_IPV6_ROUTER_SOLICITATION); gen.addJumpIfR0Equals(ICMPV6_ROUTER_SOLICITATION, mCountAndDropLabel); @@ -1766,12 +1767,15 @@ // This is a way to cover ff02::1 and ff02::2 with a single JNEBS. // TODO: Drop only if they don't contain the address of on-link neighbours. final byte[] unsolicitedNaDropPrefix = Arrays.copyOf(IPV6_ALL_NODES_ADDRESS, 15); - gen.addLoadImmediate(Register.R0, IPV6_DEST_ADDR_OFFSET); + gen.addLoadImmediate(R0, IPV6_DEST_ADDR_OFFSET); gen.addJumpIfBytesAtR0NotEqual(unsolicitedNaDropPrefix, skipUnsolicitedMulticastNALabel); maybeSetupCounter(gen, Counter.DROPPED_IPV6_MULTICAST_NA); gen.addJump(mCountAndDropLabel); gen.defineLabel(skipUnsolicitedMulticastNALabel); + + // Note that this is immediately followed emitEpilogue which will: + // maybeSetupCounter(gen, Counter.PASSED_IPV6_ICMP); } /** Encodes qname in TLV pattern. */ @@ -1823,24 +1827,24 @@ // 3. it is a UDP packet with port 5353 // Check it's L2 mDNS multicast address. - gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET); + gen.addLoadImmediate(R0, ETH_DEST_ADDR_OFFSET); gen.addJumpIfBytesAtR0NotEqual(ETH_MULTICAST_MDNS_V4_MAC_ADDRESS, skipMdnsv4Filter); // Checks it's IPv4. - gen.addLoad16(Register.R0, ETH_ETHERTYPE_OFFSET); + gen.addLoad16(R0, ETH_ETHERTYPE_OFFSET); gen.addJumpIfR0NotEquals(ETH_P_IP, skipMdnsFilter); // Check it's not a fragment. - gen.addLoad16(Register.R0, IPV4_FRAGMENT_OFFSET_OFFSET); + gen.addLoad16(R0, IPV4_FRAGMENT_OFFSET_OFFSET); gen.addJumpIfR0AnyBitsSet(IPV4_FRAGMENT_MORE_FRAGS_MASK | IPV4_FRAGMENT_OFFSET_MASK, skipMdnsFilter); // Checks it's UDP. - gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET); + gen.addLoad8(R0, IPV4_PROTOCOL_OFFSET); gen.addJumpIfR0NotEquals(IPPROTO_UDP, skipMdnsFilter); // Set R1 to IPv4 header. - gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); + gen.addLoadFromMemory(R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); gen.addJump(checkMdnsUdpPort); gen.defineLabel(skipMdnsv4Filter); @@ -1850,28 +1854,28 @@ gen.addJumpIfBytesAtR0NotEqual(ETH_MULTICAST_MDNS_V6_MAC_ADDRESS, skipMdnsFilter); // Checks it's IPv6. - gen.addLoad16(Register.R0, ETH_ETHERTYPE_OFFSET); + gen.addLoad16(R0, ETH_ETHERTYPE_OFFSET); gen.addJumpIfR0NotEquals(ETH_P_IPV6, skipMdnsFilter); // Checks it's UDP. - gen.addLoad8(Register.R0, IPV6_NEXT_HEADER_OFFSET); + gen.addLoad8(R0, IPV6_NEXT_HEADER_OFFSET); gen.addJumpIfR0NotEquals(IPPROTO_UDP, skipMdnsFilter); // Set R1 to IPv6 header. - gen.addLoadImmediate(Register.R1, IPV6_HEADER_LEN); + gen.addLoadImmediate(R1, IPV6_HEADER_LEN); // Checks it's mDNS UDP port gen.defineLabel(checkMdnsUdpPort); - gen.addLoad16Indexed(Register.R0, TCP_UDP_DESTINATION_PORT_OFFSET); + gen.addLoad16Indexed(R0, TCP_UDP_DESTINATION_PORT_OFFSET); gen.addJumpIfR0NotEquals(MDNS_PORT, skipMdnsFilter); - gen.addLoad16Indexed(Register.R0, MDNS_QDCOUNT_OFFSET); + gen.addLoad16Indexed(R0, MDNS_QDCOUNT_OFFSET); // If QDCOUNT != 1, pass the packet gen.addJumpIfR0NotEquals(1, mDnsAcceptPacket); // If QDCOUNT == 1, matches the QNAME with allowlist. // Load offset for the first QNAME. - gen.addLoadImmediate(Register.R0, MDNS_QNAME_OFFSET); + gen.addLoadImmediate(R0, MDNS_QNAME_OFFSET); gen.addAddR1(); // Check first QNAME against allowlist @@ -1909,16 +1913,16 @@ final String skipPort7V4Filter = "skip_port7_v4_filter"; // Check it's TCP. - gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET); + gen.addLoad8(R0, IPV4_PROTOCOL_OFFSET); gen.addJumpIfR0NotEquals(IPPROTO_TCP, skipPort7V4Filter); // Check it's not a fragment or is the initial fragment. - gen.addLoad16(Register.R0, IPV4_FRAGMENT_OFFSET_OFFSET); + gen.addLoad16(R0, IPV4_FRAGMENT_OFFSET_OFFSET); gen.addJumpIfR0AnyBitsSet(IPV4_FRAGMENT_OFFSET_MASK, skipPort7V4Filter); // Check it's destination port 7. - gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); - gen.addLoad16Indexed(Register.R0, TCP_UDP_DESTINATION_PORT_OFFSET); + gen.addLoadFromMemory(R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); + gen.addLoad16Indexed(R0, TCP_UDP_DESTINATION_PORT_OFFSET); gen.addJumpIfR0NotEquals(ECHO_PORT, skipPort7V4Filter); // Drop it. @@ -1964,9 +1968,18 @@ if (mApfCapabilities.hasDataAccess()) { // Increment TOTAL_PACKETS maybeSetupCounter(gen, Counter.TOTAL_PACKETS); - gen.addLoadData(Register.R0, 0); // load counter + gen.addLoadData(R0, 0); // load counter gen.addAdd(1); - gen.addStoreData(Register.R0, 0); // write-back counter + gen.addStoreData(R0, 0); // write-back counter + + maybeSetupCounter(gen, Counter.FILTER_AGE_SECONDS); + gen.addLoadFromMemory(R0, 15); // m[15] is filter age in seconds + gen.addStoreData(R0, 0); // store 'counter' + + // requires a new enough APFv5+ interpreter, otherwise will be 0 + maybeSetupCounter(gen, Counter.FILTER_AGE_16384THS); + gen.addLoadFromMemory(R0, 9); // m[9] is filter age in 16384ths + gen.addStoreData(R0, 0); // store 'counter' } // Here's a basic summary of what the initial program does: @@ -1985,7 +1998,7 @@ // pass // insert IPv6 filter to drop, pass, or fall off the end for ICMPv6 packets - gen.addLoad16(Register.R0, ETH_ETHERTYPE_OFFSET); + gen.addLoad16(R0, ETH_ETHERTYPE_OFFSET); if (mDrop802_3Frames) { // drop 802.3 frames (ethtype < 0x0600) @@ -2007,7 +2020,7 @@ // Add mDNS filter: generateMdnsFilterLocked(gen); - gen.addLoad16(Register.R0, ETH_ETHERTYPE_OFFSET); + gen.addLoad16(R0, ETH_ETHERTYPE_OFFSET); // Add IPv4 filters: String skipIPv4FiltersLabel = "skipIPv4Filters"; @@ -2023,7 +2036,7 @@ gen.addJumpIfR0Equals(ETH_P_IPV6, ipv6FilterLabel); // Drop non-IP non-ARP broadcasts, pass the rest - gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET); + gen.addLoadImmediate(R0, ETH_DEST_ADDR_OFFSET); maybeSetupCounter(gen, Counter.PASSED_NON_IP_UNICAST); gen.addJumpIfBytesAtR0NotEqual(ETHER_BROADCAST, mCountAndPassLabel); maybeSetupCounter(gen, Counter.DROPPED_ETH_BROADCAST); @@ -2055,16 +2068,16 @@ // pointed to by R1, then jumps to the pass label. This saves a few bytes over inserting // the entire sequence inline for every counter. gen.defineLabel(mCountAndPassLabel); - gen.addLoadData(Register.R0, 0); // R0 = *(R1 + 0) + gen.addLoadData(R0, 0); // R0 = *(R1 + 0) gen.addAdd(1); // R0++ - gen.addStoreData(Register.R0, 0); // *(R1 + 0) = R0 + gen.addStoreData(R0, 0); // *(R1 + 0) = R0 gen.addJump(gen.PASS_LABEL); // Same as above for the count & drop trampoline. gen.defineLabel(mCountAndDropLabel); - gen.addLoadData(Register.R0, 0); // R0 = *(R1 + 0) + gen.addLoadData(R0, 0); // R0 = *(R1 + 0) gen.addAdd(1); // R0++ - gen.addStoreData(Register.R0, 0); // *(R1 + 0) = R0 + gen.addStoreData(R0, 0); // *(R1 + 0) = R0 gen.addJump(gen.DROP_LABEL); }
diff --git a/src/android/net/apf/ApfV4Generator.java b/src/android/net/apf/ApfV4Generator.java index a7986e6..e1b0fc3 100644 --- a/src/android/net/apf/ApfV4Generator.java +++ b/src/android/net/apf/ApfV4Generator.java
@@ -16,655 +16,40 @@ package android.net.apf; -import static android.net.apf.ApfV4Generator.Register.R0; -import static android.net.apf.ApfV4Generator.Register.R1; - -import androidx.annotation.NonNull; +import static android.net.apf.BaseApfGenerator.Rbit.Rbit0; +import static android.net.apf.BaseApfGenerator.Register.R1; import com.android.internal.annotations.VisibleForTesting; -import com.android.net.module.util.HexDump; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; /** * APF assembler/generator. A tool for generating an APF program. * * Call add*() functions to add instructions to the program, then call - * {@link ApfV4Generator#generate} to get the APF bytecode for the program. + * {@link BaseApfGenerator#generate} to get the APF bytecode for the program. + * + * @param <Type> the generator class * * @hide */ -public class ApfV4Generator { - /** - * This exception is thrown when an attempt is made to generate an illegal instruction. - */ - public static class IllegalInstructionException extends Exception { - IllegalInstructionException(String msg) { - super(msg); - } - } - private enum Opcodes { - LABEL(-1), - // Unconditionally pass (if R=0) or drop (if R=1) packet. - // An optional unsigned immediate value can be provided to encode the counter number. - // If the value is non-zero, the instruction increments the counter. - // The counter is located (-4 * counter number) bytes from the end of the data region. - // It is a U32 big-endian value and is always incremented by 1. - // This is more or less equivalent to: lddw R0, -N4; add R0,1; stdw R0, -N4; {pass,drop} - // e.g. "pass", "pass 1", "drop", "drop 1" - PASSDROP(0), - LDB(1), // Load 1 byte from immediate offset, e.g. "ldb R0, [5]" - LDH(2), // Load 2 bytes from immediate offset, e.g. "ldh R0, [5]" - LDW(3), // Load 4 bytes from immediate offset, e.g. "ldw R0, [5]" - LDBX(4), // Load 1 byte from immediate offset plus register, e.g. "ldbx R0, [5]R0" - LDHX(5), // Load 2 byte from immediate offset plus register, e.g. "ldhx R0, [5]R0" - LDWX(6), // Load 4 byte from immediate offset plus register, e.g. "ldwx R0, [5]R0" - ADD(7), // Add, e.g. "add R0,5" - MUL(8), // Multiply, e.g. "mul R0,5" - DIV(9), // Divide, e.g. "div R0,5" - AND(10), // And, e.g. "and R0,5" - OR(11), // Or, e.g. "or R0,5" - SH(12), // Left shift, e.g, "sh R0, 5" or "sh R0, -5" (shifts right) - LI(13), // Load immediate, e.g. "li R0,5" (immediate encoded as signed value) - // Jump, e.g. "jmp label" - // In APFv6, we use JMP(R=1) to encode the DATA instruction. DATA is executed as a jump. - // It tells how many bytes of the program regions are used to store the data and followed - // by the actual data bytes. - // "e.g. data 5, abcde" - JMP(14), - JEQ(15), // Compare equal and branch, e.g. "jeq R0,5,label" - JNE(16), // Compare not equal and branch, e.g. "jne R0,5,label" - JGT(17), // Compare greater than and branch, e.g. "jgt R0,5,label" - JLT(18), // Compare less than and branch, e.g. "jlt R0,5,label" - JSET(19), // Compare any bits set and branch, e.g. "jset R0,5,label" - JNEBS(20), // Compare not equal byte sequence, e.g. "jnebs R0,5,label,0x1122334455" - EXT(21), // Followed by immediate indicating ExtendedOpcodes. - LDDW(22), // Load 4 bytes from data memory address (register + immediate): "lddw R0, [5]R1" - STDW(23), // Store 4 bytes to data memory address (register + immediate): "stdw R0, [5]R1" - // Write 1, 2 or 4 bytes immediate to the output buffer and auto-increment the pointer to - // write. e.g. "write 5" - WRITE(24), - // Copy bytes from input packet/APF program/data region to output buffer and - // auto-increment the output buffer pointer. - // Register bit is used to specify the source of data copy. - // R=0 means copy from packet. - // R=1 means copy from APF program/data region. - // The copy length is stored in (u8)imm2. - // e.g. "pktcopy 5, 5" "datacopy 5, 5" - PKTDATACOPY(25); - - final int value; - - private Opcodes(int value) { - this.value = value; - } - } - // Extended opcodes. Primary opcode is Opcodes.EXT. ExtendedOpcodes are encoded in the immediate - // field. - private enum ExtendedOpcodes { - LDM(0), // Load from memory, e.g. "ldm R0,5" - STM(16), // Store to memory, e.g. "stm R0,5" - NOT(32), // Not, e.g. "not R0" - NEG(33), // Negate, e.g. "neg R0" - SWAP(34), // Swap, e.g. "swap R0,R1" - MOVE(35), // Move, e.g. "move R0,R1" - // Allocate writable output buffer. - // R=0, use register R0 to store the length. R=1, encode the length in the u16 int imm2. - // "e.g. allocate R0" - // "e.g. allocate 123" - ALLOCATE(36), - // Transmit and deallocate the buffer (transmission can be delayed until the program - // terminates). R=0 means discard the buffer, R=1 means transmit the buffer. - // "e.g. transmit" - // "e.g. discard" - TRANSMITDISCARD(37), - // Write 1, 2 or 4 byte value from register to the output buffer and auto-increment the - // output buffer pointer. - // e.g. "ewrite1 r0" - EWRITE1(38), - EWRITE2(39), - EWRITE4(40), - // Copy bytes from input packet/APF program/data region to output buffer and - // auto-increment the output buffer pointer. - // The copy src offset is stored in R0. - // when R=0, the copy length is stored in (u8)imm2. - // when R=1, the copy length is stored in R1. - // e.g. "pktcopy r0, 5", "pktcopy r0, r1", "datacopy r0, 5", "datacopy r0, r1" - EPKTCOPY(41), - EDATACOPY(42), - // Jumps if the UDP payload content (starting at R0) does not contain one - // of the specified QNAMEs, applying case insensitivity. - // R0: Offset to UDP payload content - // R=0/1 meaning 'does not match'/'matches' - // imm1: Opcode - // imm2: Label offset - // imm3(u8): Question type (PTR/SRV/TXT/A/AAAA) - // imm4(bytes): TLV-encoded QNAME list (null-terminated) - // e.g.: "jdnsqmatch R0,label,0x0c,\002aa\005local\0\0" - JDNSQMATCH(43), - // Jumps if the UDP payload content (starting at R0) does not contain one - // of the specified NAMEs in answers/authority/additional records, applying - // case insensitivity. - // R=0/1 meaning 'does not match'/'matches' - // R0: Offset to UDP payload content - // imm1: Opcode - // imm2: Label offset - // imm3(bytes): TLV-encoded QNAME list (null-terminated) - // e.g.: "jdnsamatch R0,label,0x0c,\002aa\005local\0\0" - JDNSAMATCH(44); - - final int value; - - private ExtendedOpcodes(int value) { - this.value = value; - } - } - public enum Register { - R0(0), - R1(1); - - final int value; - - private Register(int value) { - this.value = value; - } - } - - private enum IntImmediateType { - INDETERMINATE_SIZE_SIGNED, - INDETERMINATE_SIZE_UNSIGNED, - SIGNED_8, - UNSIGNED_8, - SIGNED_BE16, - UNSIGNED_BE16, - SIGNED_BE32, - UNSIGNED_BE32; - } - - private static class IntImmediate { - public final IntImmediateType mImmediateType; - public final int mValue; - - IntImmediate(int value, IntImmediateType type) { - mImmediateType = type; - mValue = value; - } - - private int calculateIndeterminateSize() { - switch (mImmediateType) { - case INDETERMINATE_SIZE_SIGNED: - return calculateImmSize(mValue, true /* signed */); - case INDETERMINATE_SIZE_UNSIGNED: - return calculateImmSize(mValue, false /* signed */); - default: - // For IMM with determinate size, return 0 to allow Math.max() calculation in - // caller function. - return 0; - } - } - - private int getEncodingSize(int immFieldSize) { - switch (mImmediateType) { - case SIGNED_8: - case UNSIGNED_8: - return 1; - case SIGNED_BE16: - case UNSIGNED_BE16: - return 2; - case SIGNED_BE32: - case UNSIGNED_BE32: - return 4; - case INDETERMINATE_SIZE_SIGNED: - case INDETERMINATE_SIZE_UNSIGNED: { - int minSizeRequired = calculateIndeterminateSize(); - if (minSizeRequired > immFieldSize) { - throw new IllegalStateException( - String.format("immFieldSize: %d is too small to encode value %d", - immFieldSize, mValue)); - } - return immFieldSize; - } - } - throw new IllegalStateException("UnhandledInvalid IntImmediateType: " + mImmediateType); - } - - private int writeValue(byte[] bytecode, Integer writingOffset, int immFieldSize) { - return Instruction.writeValue(mValue, bytecode, writingOffset, - getEncodingSize(immFieldSize)); - } - - public static IntImmediate newSigned(int imm) { - return new IntImmediate(imm, IntImmediateType.INDETERMINATE_SIZE_SIGNED); - } - - public static IntImmediate newUnsigned(long imm) { - // upperBound is 2^32 - 1 - checkRange("Unsigned IMM", imm, 0 /* lowerBound */, - 4294967295L /* upperBound */); - return new IntImmediate((int) imm, IntImmediateType.INDETERMINATE_SIZE_UNSIGNED); - } - - public static IntImmediate newTwosComplementUnsigned(long imm) { - checkRange("Unsigned TwosComplement IMM", imm, Integer.MIN_VALUE, - 4294967295L /* upperBound */); - return new IntImmediate((int) imm, IntImmediateType.INDETERMINATE_SIZE_UNSIGNED); - } - - public static IntImmediate newTwosComplementSigned(long imm) { - checkRange("Signed TwosComplement IMM", imm, Integer.MIN_VALUE, - 4294967295L /* upperBound */); - return new IntImmediate((int) imm, IntImmediateType.INDETERMINATE_SIZE_SIGNED); - } - - public static IntImmediate newS8(byte imm) { - checkRange("S8 IMM", imm, Byte.MIN_VALUE, Byte.MAX_VALUE); - return new IntImmediate(imm, IntImmediateType.SIGNED_8); - } - - public static IntImmediate newU8(int imm) { - checkRange("U8 IMM", imm, 0, 255); - return new IntImmediate(imm, IntImmediateType.UNSIGNED_8); - } - - public static IntImmediate newS16(short imm) { - return new IntImmediate(imm, IntImmediateType.SIGNED_BE16); - } - - public static IntImmediate newU16(int imm) { - checkRange("U16 IMM", imm, 0, 65535); - return new IntImmediate(imm, IntImmediateType.UNSIGNED_BE16); - } - - public static IntImmediate newS32(int imm) { - return new IntImmediate(imm, IntImmediateType.SIGNED_BE32); - } - - public static IntImmediate newU32(long imm) { - // upperBound is 2^32 - 1 - checkRange("U32 IMM", imm, 0 /* lowerBound */, - 4294967295L /* upperBound */); - return new IntImmediate((int) imm, IntImmediateType.UNSIGNED_BE32); - } - - @Override - public String toString() { - return "IntImmediate{" + "mImmediateType=" + mImmediateType + ", mValue=" + mValue - + '}'; - } - } - - private class Instruction { - private final byte mOpcode; // A "Opcode" value. - private final byte mRegister; // A "Register" value. - public final List<IntImmediate> mIntImms = new ArrayList<>(); - // When mOpcode is a jump: - private int mTargetLabelSize; - private int mLenFieldOverride = -1; - private String mTargetLabel; - // When mOpcode == Opcodes.LABEL: - private String mLabel; - private byte[] mBytesImm; - // Offset in bytes from the beginning of this program. Set by {@link ApfGenerator#generate}. - int offset; - - Instruction(Opcodes opcode, Register register) { - mOpcode = (byte) opcode.value; - mRegister = (byte) register.value; - } - - Instruction(ExtendedOpcodes extendedOpcodes, Register register) { - this(Opcodes.EXT, register); - addUnsigned(extendedOpcodes.value); - } - - Instruction(ExtendedOpcodes extendedOpcodes, int slot, Register register) - throws IllegalInstructionException { - this(Opcodes.EXT, register); - if (slot < 0 || slot >= MEMORY_SLOTS) { - throw new IllegalInstructionException("illegal memory slot number: " + slot); - } - addUnsigned(extendedOpcodes.value + slot); - } - - Instruction(Opcodes opcode) { - this(opcode, R0); - } - - Instruction(ExtendedOpcodes extendedOpcodes) { - this(extendedOpcodes, R0); - } - - Instruction addSigned(int imm) { - mIntImms.add(IntImmediate.newSigned(imm)); - return this; - } - - Instruction addUnsigned(int imm) { - mIntImms.add(IntImmediate.newUnsigned(imm)); - return this; - } - - - Instruction addTwosCompSigned(int imm) { - mIntImms.add(IntImmediate.newTwosComplementSigned(imm)); - return this; - } - - - Instruction addTwosCompUnsigned(int imm) { - mIntImms.add(IntImmediate.newTwosComplementUnsigned(imm)); - return this; - } - - Instruction addS8(byte imm) { - mIntImms.add(IntImmediate.newS8(imm)); - return this; - } - - Instruction addU8(int imm) { - mIntImms.add(IntImmediate.newU8(imm)); - return this; - } - - Instruction addS16(short imm) { - mIntImms.add(IntImmediate.newS16(imm)); - return this; - } - - Instruction addU16(int imm) { - mIntImms.add(IntImmediate.newU16(imm)); - return this; - } - - Instruction addS32(int imm) { - mIntImms.add(IntImmediate.newS32(imm)); - return this; - } - - Instruction addU32(long imm) { - mIntImms.add(IntImmediate.newU32(imm)); - return this; - } - - Instruction setLabel(String label) throws IllegalInstructionException { - if (mLabels.containsKey(label)) { - throw new IllegalInstructionException("duplicate label " + label); - } - if (mOpcode != Opcodes.LABEL.value) { - throw new IllegalStateException("adding label to non-label instruction"); - } - mLabel = label; - mLabels.put(label, this); - return this; - } - - Instruction setTargetLabel(String label) { - mTargetLabel = label; - mTargetLabelSize = 4; // May shrink later on in generate(). - return this; - } - - Instruction overrideLenField(int size) { - mLenFieldOverride = size; - return this; - } - - Instruction setBytesImm(byte[] bytes) { - mBytesImm = bytes; - return this; - } - - /** - * @return size of instruction in bytes. - */ - int size() { - if (mOpcode == Opcodes.LABEL.value) { - return 0; - } - int size = 1; - int indeterminateSize = calculateRequiredIndeterminateSize(); - for (IntImmediate imm : mIntImms) { - size += imm.getEncodingSize(indeterminateSize); - } - if (mTargetLabel != null) { - size += indeterminateSize; - } - if (mBytesImm != null) { - size += mBytesImm.length; - } - return size; - } - - /** - * Resize immediate value field so that it's only as big as required to - * contain the offset of the jump destination. - * @return {@code true} if shrunk. - */ - boolean shrink() throws IllegalInstructionException { - if (mTargetLabel == null) { - return false; - } - int oldTargetLabelSize = mTargetLabelSize; - mTargetLabelSize = calculateImmSize(calculateTargetLabelOffset(), false); - if (mTargetLabelSize > oldTargetLabelSize) { - throw new IllegalStateException("instruction grew"); - } - return mTargetLabelSize < oldTargetLabelSize; - } - - /** - * Assemble value for instruction size field. - */ - private int generateImmSizeField() { - // If we already know the size the length field, just use it - switch (mLenFieldOverride) { - case -1: - break; - case 1: - return 1; - case 2: - return 2; - case 4: - return 3; - default: - throw new IllegalStateException( - "mLenFieldOverride has invalid value: " + mLenFieldOverride); - } - // Otherwise, calculate - int immSize = calculateRequiredIndeterminateSize(); - // Encode size field to fit in 2 bits: 0->0, 1->1, 2->2, 3->4. - return immSize == 4 ? 3 : immSize; - } - - /** - * Assemble first byte of generated instruction. - */ - private byte generateInstructionByte() { - int sizeField = generateImmSizeField(); - return (byte)((mOpcode << 3) | (sizeField << 1) | mRegister); - } - - /** - * Write {@code value} at offset {@code writingOffset} into {@code bytecode}. - * {@code immSize} bytes are written. {@code value} is truncated to - * {@code immSize} bytes. {@code value} is treated simply as a - * 32-bit value, so unsigned values should be zero extended and the truncation - * should simply throw away their zero-ed upper bits, and signed values should - * be sign extended and the truncation should simply throw away their signed - * upper bits. - */ - private static int writeValue(int value, byte[] bytecode, int writingOffset, int immSize) { - for (int i = immSize - 1; i >= 0; i--) { - bytecode[writingOffset++] = (byte)((value >> (i * 8)) & 255); - } - return writingOffset; - } - - /** - * Generate bytecode for this instruction at offset {@link Instruction#offset}. - */ - void generate(byte[] bytecode) throws IllegalInstructionException { - if (mOpcode == Opcodes.LABEL.value) { - return; - } - int writingOffset = offset; - bytecode[writingOffset++] = generateInstructionByte(); - int indeterminateSize = calculateRequiredIndeterminateSize(); - int startOffset = 0; - if (mOpcode == Opcodes.EXT.value) { - // For extend opcode, always write the actual opcode first. - writingOffset = mIntImms.get(startOffset++).writeValue(bytecode, writingOffset, - indeterminateSize); - } - if (mTargetLabel != null) { - writingOffset = writeValue(calculateTargetLabelOffset(), bytecode, writingOffset, - indeterminateSize); - } - for (int i = startOffset; i < mIntImms.size(); ++i) { - writingOffset = mIntImms.get(i).writeValue(bytecode, writingOffset, - indeterminateSize); - } - if (mBytesImm != null) { - System.arraycopy(mBytesImm, 0, bytecode, writingOffset, mBytesImm.length); - writingOffset += mBytesImm.length; - } - if ((writingOffset - offset) != size()) { - throw new IllegalStateException("wrote " + (writingOffset - offset) + - " but should have written " + size()); - } - } - - /** - * Calculates the maximum indeterminate size of all IMMs in this instruction. - * <p> - * This method finds the largest size needed to encode any indeterminate-sized IMMs in - * the instruction. This size will be stored in the immLen field. - */ - private int calculateRequiredIndeterminateSize() { - int maxSize = mTargetLabelSize; - for (IntImmediate imm : mIntImms) { - maxSize = Math.max(maxSize, imm.calculateIndeterminateSize()); - } - return maxSize; - } - - private int calculateTargetLabelOffset() throws IllegalInstructionException { - Instruction targetLabelInstruction; - if (mTargetLabel == DROP_LABEL) { - targetLabelInstruction = mDropLabel; - } else if (mTargetLabel == PASS_LABEL) { - targetLabelInstruction = mPassLabel; - } else { - targetLabelInstruction = mLabels.get(mTargetLabel); - } - if (targetLabelInstruction == null) { - throw new IllegalInstructionException("label not found: " + mTargetLabel); - } - // Calculate distance from end of this instruction to instruction.offset. - final int targetLabelOffset = targetLabelInstruction.offset - (offset + size()); - return targetLabelOffset; - } - } +public class ApfV4Generator<Type extends BaseApfGenerator> extends BaseApfGenerator { /** - * Jump to this label to terminate the program and indicate the packet - * should be dropped. - */ - public static final String DROP_LABEL = "__DROP__"; - - /** - * Jump to this label to terminate the program and indicate the packet - * should be passed to the AP. - */ - public static final String PASS_LABEL = "__PASS__"; - - /** - * Number of memory slots available for access via APF stores to memory and loads from memory. - * The memory slots are numbered 0 to {@code MEMORY_SLOTS} - 1. This must be kept in sync with - * the APF interpreter. - */ - public static final int MEMORY_SLOTS = 16; - - /** - * Memory slot number that is prefilled with the IPv4 header length. - * Note that this memory slot may be overwritten by a program that - * executes stores to this memory slot. This must be kept in sync with - * the APF interpreter. - */ - public static final int IPV4_HEADER_SIZE_MEMORY_SLOT = 13; - - /** - * Memory slot number that is prefilled with the size of the packet being filtered in bytes. - * Note that this memory slot may be overwritten by a program that - * executes stores to this memory slot. This must be kept in sync with the APF interpreter. - */ - public static final int PACKET_SIZE_MEMORY_SLOT = 14; - - /** - * Memory slot number that is prefilled with the age of the filter in seconds. The age of the - * filter is the time since the filter was installed until now. - * Note that this memory slot may be overwritten by a program that - * executes stores to this memory slot. This must be kept in sync with the APF interpreter. - */ - public static final int FILTER_AGE_MEMORY_SLOT = 15; - - /** - * First memory slot containing prefilled values. Can be used in range comparisons to determine - * if memory slot index is within prefilled slots. - */ - public static final int FIRST_PREFILLED_MEMORY_SLOT = IPV4_HEADER_SIZE_MEMORY_SLOT; - - /** - * Last memory slot containing prefilled values. Can be used in range comparisons to determine - * if memory slot index is within prefilled slots. - */ - public static final int LAST_PREFILLED_MEMORY_SLOT = FILTER_AGE_MEMORY_SLOT; - - // This version number syncs up with APF_VERSION in hardware/google/apf/apf_interpreter.h - public static final int MIN_APF_VERSION = 2; - public static final int MIN_APF_VERSION_IN_DEV = 5; - public static final int APF_VERSION_4 = 4; - - - private final ArrayList<Instruction> mInstructions = new ArrayList<Instruction>(); - private final HashMap<String, Instruction> mLabels = new HashMap<String, Instruction>(); - private final Instruction mDropLabel = new Instruction(Opcodes.LABEL); - private final Instruction mPassLabel = new Instruction(Opcodes.LABEL); - private final int mVersion; - private boolean mGenerated; - - /** - * Creates an ApfGenerator instance which is able to emit instructions for the specified + * Creates an ApfV4Generator instance which is able to emit instructions for the specified * {@code version} of the APF interpreter. Throws {@code IllegalInstructionException} if * the requested version is unsupported. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public ApfV4Generator(int version) throws IllegalInstructionException { - mVersion = version; + super(version); requireApfVersion(MIN_APF_VERSION); } - /** - * Returns true if the ApfGenerator supports the specified {@code version}, otherwise false. - */ - public static boolean supportsVersion(int version) { - return version >= MIN_APF_VERSION; - } - - private void requireApfVersion(int minimumVersion) throws IllegalInstructionException { - if (mVersion < minimumVersion) { - throw new IllegalInstructionException("Requires APF >= " + minimumVersion); - } - } - - private ApfV4Generator append(Instruction instruction) { + Type append(Instruction instruction) { if (mGenerated) { throw new IllegalStateException("Program already generated"); } mInstructions.add(instruction); - return this; + return (Type) this; } /** @@ -682,14 +67,14 @@ * </pre> * In this case "next_filter" may not have any generated code associated with it. */ - public ApfV4Generator defineLabel(String name) throws IllegalInstructionException { + public Type defineLabel(String name) throws IllegalInstructionException { return append(new Instruction(Opcodes.LABEL).setLabel(name)); } /** * Add an unconditional jump instruction to the end of the program. */ - public ApfV4Generator addJump(String target) { + public Type addJump(String target) { return append(new Instruction(Opcodes.JMP).setTargetLabel(target)); } @@ -697,24 +82,24 @@ * Add an instruction to the end of the program to load the byte at offset {@code offset} * bytes from the beginning of the packet into {@code register}. */ - public ApfV4Generator addLoad8(Register r, int ofs) { - return append(new Instruction(Opcodes.LDB, r).addUnsigned(ofs)); + public Type addLoad8(Register r, int ofs) { + return append(new Instruction(Opcodes.LDB, r).addPacketOffset(ofs)); } /** * Add an instruction to the end of the program to load 16-bits at offset {@code offset} * bytes from the beginning of the packet into {@code register}. */ - public ApfV4Generator addLoad16(Register r, int ofs) { - return append(new Instruction(Opcodes.LDH, r).addUnsigned(ofs)); + public Type addLoad16(Register r, int ofs) { + return append(new Instruction(Opcodes.LDH, r).addPacketOffset(ofs)); } /** * Add an instruction to the end of the program to load 32-bits at offset {@code offset} * bytes from the beginning of the packet into {@code register}. */ - public ApfV4Generator addLoad32(Register r, int ofs) { - return append(new Instruction(Opcodes.LDW, r).addUnsigned(ofs)); + public Type addLoad32(Register r, int ofs) { + return append(new Instruction(Opcodes.LDW, r).addPacketOffset(ofs)); } /** @@ -722,8 +107,8 @@ * {@code register}. The offset of the loaded byte from the beginning of the packet is * the sum of {@code offset} and the value in register R1. */ - public ApfV4Generator addLoad8Indexed(Register r, int ofs) { - return append(new Instruction(Opcodes.LDBX, r).addUnsigned(ofs)); + public Type addLoad8Indexed(Register r, int ofs) { + return append(new Instruction(Opcodes.LDBX, r).addPacketOffset(ofs)); } /** @@ -731,8 +116,8 @@ * {@code register}. The offset of the loaded 16-bits from the beginning of the packet is * the sum of {@code offset} and the value in register R1. */ - public ApfV4Generator addLoad16Indexed(Register r, int ofs) { - return append(new Instruction(Opcodes.LDHX, r).addUnsigned(ofs)); + public Type addLoad16Indexed(Register r, int ofs) { + return append(new Instruction(Opcodes.LDHX, r).addPacketOffset(ofs)); } /** @@ -740,42 +125,42 @@ * {@code register}. The offset of the loaded 32-bits from the beginning of the packet is * the sum of {@code offset} and the value in register R1. */ - public ApfV4Generator addLoad32Indexed(Register r, int ofs) { - return append(new Instruction(Opcodes.LDWX, r).addUnsigned(ofs)); + public Type addLoad32Indexed(Register r, int ofs) { + return append(new Instruction(Opcodes.LDWX, r).addPacketOffset(ofs)); } /** * Add an instruction to the end of the program to add {@code value} to register R0. */ - public ApfV4Generator addAdd(int val) { + public Type addAdd(int val) { return append(new Instruction(Opcodes.ADD).addTwosCompUnsigned(val)); } /** * Add an instruction to the end of the program to multiply register R0 by {@code value}. */ - public ApfV4Generator addMul(int val) { + public Type addMul(long val) { return append(new Instruction(Opcodes.MUL).addUnsigned(val)); } /** * Add an instruction to the end of the program to divide register R0 by {@code value}. */ - public ApfV4Generator addDiv(int val) { + public Type addDiv(long val) { return append(new Instruction(Opcodes.DIV).addUnsigned(val)); } /** * Add an instruction to the end of the program to logically and register R0 with {@code value}. */ - public ApfV4Generator addAnd(int val) { + public Type addAnd(int val) { return append(new Instruction(Opcodes.AND).addTwosCompUnsigned(val)); } /** * Add an instruction to the end of the program to logically or register R0 with {@code value}. */ - public ApfV4Generator addOr(int val) { + public Type addOr(int val) { return append(new Instruction(Opcodes.OR).addTwosCompUnsigned(val)); } @@ -783,7 +168,7 @@ * Add an instruction to the end of the program to shift left register R0 by {@code value} bits. */ // TODO: consider whether should change the argument type to byte - public ApfV4Generator addLeftShift(int val) { + public Type addLeftShift(int val) { return append(new Instruction(Opcodes.SH).addSigned(val)); } @@ -792,28 +177,28 @@ * bits. */ // TODO: consider whether should change the argument type to byte - public ApfV4Generator addRightShift(int val) { + public Type addRightShift(int val) { return append(new Instruction(Opcodes.SH).addSigned(-val)); } /** * Add an instruction to the end of the program to add register R1 to register R0. */ - public ApfV4Generator addAddR1() { + public Type addAddR1() { return append(new Instruction(Opcodes.ADD, R1)); } /** * Add an instruction to the end of the program to multiply register R0 by register R1. */ - public ApfV4Generator addMulR1() { + public Type addMulR1() { return append(new Instruction(Opcodes.MUL, R1)); } /** * Add an instruction to the end of the program to divide register R0 by register R1. */ - public ApfV4Generator addDivR1() { + public Type addDivR1() { return append(new Instruction(Opcodes.DIV, R1)); } @@ -821,7 +206,7 @@ * Add an instruction to the end of the program to logically and register R0 with register R1 * and store the result back into register R0. */ - public ApfV4Generator addAndR1() { + public Type addAndR1() { return append(new Instruction(Opcodes.AND, R1)); } @@ -829,7 +214,7 @@ * Add an instruction to the end of the program to logically or register R0 with register R1 * and store the result back into register R0. */ - public ApfV4Generator addOrR1() { + public Type addOrR1() { return append(new Instruction(Opcodes.OR, R1)); } @@ -837,14 +222,14 @@ * Add an instruction to the end of the program to shift register R0 left by the value in * register R1. */ - public ApfV4Generator addLeftShiftR1() { + public Type addLeftShiftR1() { return append(new Instruction(Opcodes.SH, R1)); } /** * Add an instruction to the end of the program to move {@code value} into {@code register}. */ - public ApfV4Generator addLoadImmediate(Register register, int value) { + public Type addLoadImmediate(Register register, int value) { return append(new Instruction(Opcodes.LI, register).addSigned(value)); } @@ -852,7 +237,7 @@ * Add an instruction to the end of the program to jump to {@code target} if register R0's * value equals {@code value}. */ - public ApfV4Generator addJumpIfR0Equals(int val, String tgt) { + public Type addJumpIfR0Equals(int val, String tgt) { return append(new Instruction(Opcodes.JEQ).addTwosCompUnsigned(val).setTargetLabel(tgt)); } @@ -860,7 +245,7 @@ * Add an instruction to the end of the program to jump to {@code target} if register R0's * value does not equal {@code value}. */ - public ApfV4Generator addJumpIfR0NotEquals(int val, String tgt) { + public Type addJumpIfR0NotEquals(int val, String tgt) { return append(new Instruction(Opcodes.JNE).addTwosCompUnsigned(val).setTargetLabel(tgt)); } @@ -868,7 +253,7 @@ * Add an instruction to the end of the program to jump to {@code target} if register R0's * value is greater than {@code value}. */ - public ApfV4Generator addJumpIfR0GreaterThan(int val, String tgt) { + public Type addJumpIfR0GreaterThan(long val, String tgt) { return append(new Instruction(Opcodes.JGT).addUnsigned(val).setTargetLabel(tgt)); } @@ -876,7 +261,7 @@ * Add an instruction to the end of the program to jump to {@code target} if register R0's * value is less than {@code value}. */ - public ApfV4Generator addJumpIfR0LessThan(int val, String tgt) { + public Type addJumpIfR0LessThan(long val, String tgt) { return append(new Instruction(Opcodes.JLT).addUnsigned(val).setTargetLabel(tgt)); } @@ -884,14 +269,14 @@ * Add an instruction to the end of the program to jump to {@code target} if register R0's * value has any bits set that are also set in {@code value}. */ - public ApfV4Generator addJumpIfR0AnyBitsSet(int val, String tgt) { + public Type addJumpIfR0AnyBitsSet(int val, String tgt) { return append(new Instruction(Opcodes.JSET).addTwosCompUnsigned(val).setTargetLabel(tgt)); } /** * Add an instruction to the end of the program to jump to {@code target} if register R0's * value equals register R1's value. */ - public ApfV4Generator addJumpIfR0EqualsR1(String tgt) { + public Type addJumpIfR0EqualsR1(String tgt) { return append(new Instruction(Opcodes.JEQ, R1).setTargetLabel(tgt)); } @@ -899,7 +284,7 @@ * Add an instruction to the end of the program to jump to {@code target} if register R0's * value does not equal register R1's value. */ - public ApfV4Generator addJumpIfR0NotEqualsR1(String tgt) { + public Type addJumpIfR0NotEqualsR1(String tgt) { return append(new Instruction(Opcodes.JNE, R1).setTargetLabel(tgt)); } @@ -907,7 +292,7 @@ * Add an instruction to the end of the program to jump to {@code target} if register R0's * value is greater than register R1's value. */ - public ApfV4Generator addJumpIfR0GreaterThanR1(String tgt) { + public Type addJumpIfR0GreaterThanR1(String tgt) { return append(new Instruction(Opcodes.JGT, R1).setTargetLabel(tgt)); } @@ -915,7 +300,7 @@ * Add an instruction to the end of the program to jump to {@code target} if register R0's * value is less than register R1's value. */ - public ApfV4Generator addJumpIfR0LessThanR1(String target) { + public Type addJumpIfR0LessThanR1(String target) { return append(new Instruction(Opcodes.JLT, R1).setTargetLabel(target)); } @@ -923,7 +308,7 @@ * Add an instruction to the end of the program to jump to {@code target} if register R0's * value has any bits set that are also set in R1's value. */ - public ApfV4Generator addJumpIfR0AnyBitsSetR1(String tgt) { + public Type addJumpIfR0AnyBitsSetR1(String tgt) { return append(new Instruction(Opcodes.JSET, R1).setTargetLabel(tgt)); } @@ -932,7 +317,7 @@ * packet at an offset specified by {@code register} don't match {@code bytes} * R=0 means check for not equal */ - public ApfV4Generator addJumpIfBytesAtR0NotEqual(byte[] bytes, String tgt) { + public Type addJumpIfBytesAtR0NotEqual(byte[] bytes, String tgt) { return append(new Instruction(Opcodes.JNEBS).addUnsigned( bytes.length).setTargetLabel(tgt).setBytesImm(bytes)); } @@ -942,7 +327,7 @@ * packet at an offset specified by {@code register} match {@code bytes} * R=1 means check for equal. */ - public ApfV4Generator addJumpIfBytesAtR0Equal(byte[] bytes, String tgt) + public Type addJumpIfBytesAtR0Equal(byte[] bytes, String tgt) throws IllegalInstructionException { requireApfVersion(MIN_APF_VERSION_IN_DEV); return append(new Instruction(Opcodes.JNEBS, R1).addUnsigned( @@ -953,16 +338,16 @@ * Add an instruction to the end of the program to load memory slot {@code slot} into * {@code register}. */ - public ApfV4Generator addLoadFromMemory(Register r, int slot) + public Type addLoadFromMemory(Register r, int slot) throws IllegalInstructionException { - return append(new Instruction(ExtendedOpcodes.LDM, slot, r)); + return append(new BaseApfGenerator.Instruction(ExtendedOpcodes.LDM, slot, r)); } /** * Add an instruction to the end of the program to store {@code register} into memory slot * {@code slot}. */ - public ApfV4Generator addStoreToMemory(Register r, int slot) + public Type addStoreToMemory(Register r, int slot) throws IllegalInstructionException { return append(new Instruction(ExtendedOpcodes.STM, slot, r)); } @@ -970,21 +355,21 @@ /** * Add an instruction to the end of the program to logically not {@code register}. */ - public ApfV4Generator addNot(Register r) { + public Type addNot(Register r) { return append(new Instruction(ExtendedOpcodes.NOT, r)); } /** * Add an instruction to the end of the program to negate {@code register}. */ - public ApfV4Generator addNeg(Register r) { + public Type addNeg(Register r) { return append(new Instruction(ExtendedOpcodes.NEG, r)); } /** * Add an instruction to swap the values in register R0 and register R1. */ - public ApfV4Generator addSwap() { + public Type addSwap() { return append(new Instruction(ExtendedOpcodes.SWAP)); } @@ -992,337 +377,16 @@ * Add an instruction to the end of the program to move the value into * {@code register} from the other register. */ - public ApfV4Generator addMove(Register r) { + public Type addMove(Register r) { return append(new Instruction(ExtendedOpcodes.MOVE, r)); } /** * Add an instruction to the end of the program to let the program immediately return PASS. */ - public ApfV4Generator addPass() { - // PASS requires using R0 because it shares opcode with DROP - return append(new Instruction(Opcodes.PASSDROP)); - } - - /** - * Add an instruction to the end of the program to increment the counter value and - * immediately return PASS. - */ - public ApfV4Generator addCountAndPass(int cnt) throws IllegalInstructionException { - requireApfVersion(MIN_APF_VERSION_IN_DEV); - checkRange("CounterNumber", cnt /* value */, 1 /* lowerBound */, - 1000 /* upperBound */); - // PASS requires using R0 because it shares opcode with DROP - return append(new Instruction(Opcodes.PASSDROP).addUnsigned(cnt)); - } - - /** - * Add an instruction to the end of the program to let the program immediately return DROP. - */ - public ApfV4Generator addDrop() throws IllegalInstructionException { - requireApfVersion(MIN_APF_VERSION_IN_DEV); - // DROP requires using R1 because it shares opcode with PASS - return append(new Instruction(Opcodes.PASSDROP, R1)); - } - - /** - * Add an instruction to the end of the program to increment the counter value and - * immediately return DROP. - */ - public ApfV4Generator addCountAndDrop(int cnt) throws IllegalInstructionException { - requireApfVersion(MIN_APF_VERSION_IN_DEV); - checkRange("CounterNumber", cnt /* value */, 1 /* lowerBound */, - 1000 /* upperBound */); - // DROP requires using R1 because it shares opcode with PASS - return append(new Instruction(Opcodes.PASSDROP, R1).addUnsigned(cnt)); - } - - /** - * Add an instruction to the end of the program to call the apf_allocate_buffer() function. - * Buffer length to be allocated is stored in register 0. - */ - public ApfV4Generator addAllocateR0() throws IllegalInstructionException { - requireApfVersion(MIN_APF_VERSION_IN_DEV); - return append(new Instruction(ExtendedOpcodes.ALLOCATE)); - } - - /** - * Add an instruction to the end of the program to call the apf_allocate_buffer() function. - * - * @param size the buffer length to be allocated. - */ - public ApfV4Generator addAllocate(int size) throws IllegalInstructionException { - requireApfVersion(MIN_APF_VERSION_IN_DEV); - // R1 means the extra be16 immediate is present - return append(new Instruction(ExtendedOpcodes.ALLOCATE, R1).addU16(size)); - } - - /** - * Add an instruction to the beginning of the program to reserve the data region. - * @param data the actual data byte - */ - public ApfV4Generator addData(byte[] data) throws IllegalInstructionException { - requireApfVersion(MIN_APF_VERSION_IN_DEV); - if (!mInstructions.isEmpty()) { - throw new IllegalInstructionException("data instruction has to come first"); - } - return append(new Instruction(Opcodes.JMP, R1).addUnsigned(data.length).setBytesImm(data)); - } - - /** - * Add an instruction to the end of the program to transmit the allocated buffer. - */ - public ApfV4Generator addTransmit() throws IllegalInstructionException { - requireApfVersion(MIN_APF_VERSION_IN_DEV); - // TRANSMIT requires using R0 because it shares opcode with DISCARD - return append(new Instruction(ExtendedOpcodes.TRANSMITDISCARD)); - } - - /** - * Add an instruction to the end of the program to discard the allocated buffer. - */ - public ApfV4Generator addDiscard() throws IllegalInstructionException { - requireApfVersion(MIN_APF_VERSION_IN_DEV); - // DISCARD requires using R1 because it shares opcode with TRANSMIT - return append(new Instruction(ExtendedOpcodes.TRANSMITDISCARD, R1)); - } - - /** - * Add an instruction to the end of the program to write 1 byte value to output buffer. - */ - public ApfV4Generator addWriteU8(int val) throws IllegalInstructionException { - requireApfVersion(MIN_APF_VERSION_IN_DEV); - return append(new Instruction(Opcodes.WRITE).overrideLenField(1).addU8(val)); - } - - /** - * Add an instruction to the end of the program to write 2 bytes value to output buffer. - */ - public ApfV4Generator addWriteU16(int val) throws IllegalInstructionException { - requireApfVersion(MIN_APF_VERSION_IN_DEV); - return append(new Instruction(Opcodes.WRITE).overrideLenField(2).addU16(val)); - } - - /** - * Add an instruction to the end of the program to write 4 bytes value to output buffer. - */ - public ApfV4Generator addWriteU32(long val) throws IllegalInstructionException { - requireApfVersion(MIN_APF_VERSION_IN_DEV); - return append(new Instruction(Opcodes.WRITE).overrideLenField(4).addU32(val)); - } - - /** - * Add an instruction to the end of the program to write 1 byte value from register to output - * buffer. - */ - public ApfV4Generator addWriteU8(Register reg) throws IllegalInstructionException { - requireApfVersion(MIN_APF_VERSION_IN_DEV); - return append(new Instruction(ExtendedOpcodes.EWRITE1, reg)); - } - - /** - * Add an instruction to the end of the program to write 2 byte value from register to output - * buffer. - */ - public ApfV4Generator addWriteU16(Register reg) throws IllegalInstructionException { - requireApfVersion(MIN_APF_VERSION_IN_DEV); - return append(new Instruction(ExtendedOpcodes.EWRITE2, reg)); - } - - /** - * Add an instruction to the end of the program to write 4 byte value from register to output - * buffer. - */ - public ApfV4Generator addWriteU32(Register reg) throws IllegalInstructionException { - requireApfVersion(MIN_APF_VERSION_IN_DEV); - return append(new Instruction(ExtendedOpcodes.EWRITE4, reg)); - } - - /** - * Add an instruction to the end of the program to copy data from APF program/data region to - * output buffer and auto-increment the output buffer pointer. - * - * @param src the offset inside the APF program/data region for where to start copy. - * @param len the length of bytes needed to be copied, only <= 255 bytes can be copied at - * one time. - * @return the ApfGenerator object - */ - public ApfV4Generator addDataCopy(int src, int len) - throws IllegalInstructionException { - requireApfVersion(MIN_APF_VERSION_IN_DEV); - return append(new Instruction(Opcodes.PKTDATACOPY, R1).addUnsigned(src).addU8(len)); - } - - /** - * Add an instruction to the end of the program to copy data from input packet to output - * buffer and auto-increment the output buffer pointer. - * - * @param src the offset inside the input packet for where to start copy. - * @param len the length of bytes needed to be copied, only <= 255 bytes can be copied at - * one time. - * @return the ApfGenerator object - */ - public ApfV4Generator addPacketCopy(int src, int len) - throws IllegalInstructionException { - requireApfVersion(MIN_APF_VERSION_IN_DEV); - return append(new Instruction(Opcodes.PKTDATACOPY, R0).addUnsigned(src).addU8(len)); - } - - /** - * Add an instruction to the end of the program to copy data from APF program/data region to - * output buffer and auto-increment the output buffer pointer. - * Source offset is stored in R0. - * - * @param len the number of bytes to be copied, only <= 255 bytes can be copied at once. - * @return the ApfGenerator object - */ - public ApfV4Generator addDataCopyFromR0(int len) throws IllegalInstructionException { - requireApfVersion(MIN_APF_VERSION_IN_DEV); - return append(new Instruction(ExtendedOpcodes.EDATACOPY).addU8(len)); - } - - /** - * Add an instruction to the end of the program to copy data from input packet to output - * buffer and auto-increment the output buffer pointer. - * Source offset is stored in R0. - * - * @param len the number of bytes to be copied, only <= 255 bytes can be copied at once. - * @return the ApfGenerator object - */ - public ApfV4Generator addPacketCopyFromR0(int len) throws IllegalInstructionException { - requireApfVersion(MIN_APF_VERSION_IN_DEV); - return append(new Instruction(ExtendedOpcodes.EPKTCOPY).addU8(len)); - } - - /** - * Add an instruction to the end of the program to copy data from APF program/data region to - * output buffer and auto-increment the output buffer pointer. - * Source offset is stored in R0. - * Copy length is stored in R1. - * - * @return the ApfGenerator object - */ - public ApfV4Generator addDataCopyFromR0LenR1() throws IllegalInstructionException { - requireApfVersion(MIN_APF_VERSION_IN_DEV); - return append(new Instruction(ExtendedOpcodes.EDATACOPY, R1)); - } - - /** - * Add an instruction to the end of the program to copy data from input packet to output - * buffer and auto-increment the output buffer pointer. - * Source offset is stored in R0. - * Copy length is stored in R1. - * - * @return the ApfGenerator object - */ - public ApfV4Generator addPacketCopyFromR0LenR1() throws IllegalInstructionException { - requireApfVersion(MIN_APF_VERSION_IN_DEV); - return append(new Instruction(ExtendedOpcodes.EPKTCOPY, R1)); - } - - /** - * Check if the byte is valid dns character: A-Z,0-9,-,_ - */ - private static boolean isValidDnsCharacter(byte c) { - return (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '_'; - } - - private static void validateNames(@NonNull byte[] names) { - final int len = names.length; - if (len < 4) { - throw new IllegalArgumentException("qnames must have at least length 4"); - } - final String errorMessage = "qname: " + HexDump.toHexString(names) - + "is not null-terminated list of TLV-encoded names"; - int i = 0; - while (i < len - 1) { - int label_len = names[i++]; - if (label_len < 1 || label_len > 63) { - throw new IllegalArgumentException( - "label len: " + label_len + " must be between 1 and 63"); - } - if (i + label_len >= len - 1) { - throw new IllegalArgumentException(errorMessage); - } - while (label_len-- > 0) { - if (!isValidDnsCharacter(names[i++])) { - throw new IllegalArgumentException("qname: " + HexDump.toHexString(names) - + " contains invalid character"); - } - } - if (names[i] == 0) { - i++; // skip null terminator. - } - } - if (names[len - 1] != 0) { - throw new IllegalArgumentException(errorMessage); - } - } - - /** - * Appends a conditional jump instruction to the program: Jumps to {@code tgt} if the UDP - * payload's DNS questions do NOT contain the QNAMEs specified in {@code qnames} and qtype - * equals {@code qtype}. Examines the payload starting at the offset in R0. - * R = 0 means check for "does not contain". - */ - public ApfV4Generator addJumpIfPktAtR0DoesNotContainDnsQ(@NonNull byte[] qnames, int qtype, - @NonNull String tgt) throws IllegalInstructionException { - requireApfVersion(MIN_APF_VERSION_IN_DEV); - validateNames(qnames); - return append(new Instruction(ExtendedOpcodes.JDNSQMATCH).setTargetLabel(tgt).addU8( - qtype).setBytesImm(qnames)); - } - - /** - * Appends a conditional jump instruction to the program: Jumps to {@code tgt} if the UDP - * payload's DNS questions contain the QNAMEs specified in {@code qnames} and qtype - * equals {@code qtype}. Examines the payload starting at the offset in R0. - * R = 1 means check for "contain". - */ - public ApfV4Generator addJumpIfPktAtR0ContainDnsQ(@NonNull byte[] qnames, int qtype, - @NonNull String tgt) throws IllegalInstructionException { - requireApfVersion(MIN_APF_VERSION_IN_DEV); - validateNames(qnames); - return append(new Instruction(ExtendedOpcodes.JDNSQMATCH, R1).setTargetLabel(tgt).addU8( - qtype).setBytesImm(qnames)); - } - - /** - * Appends a conditional jump instruction to the program: Jumps to {@code tgt} if the UDP - * payload's DNS answers/authority/additional records do NOT contain the NAMEs - * specified in {@code Names}. Examines the payload starting at the offset in R0. - * R = 0 means check for "does not contain". - */ - public ApfV4Generator addJumpIfPktAtR0DoesNotContainDnsA(@NonNull byte[] names, - @NonNull String tgt) throws IllegalInstructionException { - requireApfVersion(MIN_APF_VERSION_IN_DEV); - validateNames(names); - return append(new Instruction(ExtendedOpcodes.JDNSAMATCH).setTargetLabel(tgt).setBytesImm( - names)); - } - - /** - * Appends a conditional jump instruction to the program: Jumps to {@code tgt} if the UDP - * payload's DNS answers/authority/additional records contain the NAMEs - * specified in {@code Names}. Examines the payload starting at the offset in R0. - * R = 1 means check for "contain". - */ - public ApfV4Generator addJumpIfPktAtR0ContainDnsA(@NonNull byte[] names, - @NonNull String tgt) throws IllegalInstructionException { - requireApfVersion(MIN_APF_VERSION_IN_DEV); - validateNames(names); - return append(new Instruction(ExtendedOpcodes.JDNSAMATCH, R1).setTargetLabel( - tgt).setBytesImm(names)); - } - - private static void checkRange(@NonNull String variableName, long value, long lowerBound, - long upperBound) { - if (value >= lowerBound && value <= upperBound) { - return; - } - throw new IllegalArgumentException( - String.format("%s: %d, must be in range [%d, %d]", variableName, value, lowerBound, - upperBound)); + public Type addPass() { + // PASS requires using Rbit0 because it shares opcode with DROP + return append(new Instruction(Opcodes.PASSDROP, Rbit0)); } /** @@ -1331,7 +395,7 @@ * @{code offset} to the other register. * Requires APF v4 or greater. */ - public ApfV4Generator addLoadData(Register dst, int ofs) + public Type addLoadData(Register dst, int ofs) throws IllegalInstructionException { requireApfVersion(APF_VERSION_4); return append(new Instruction(Opcodes.LDDW, dst).addSigned(ofs)); @@ -1343,92 +407,11 @@ * @{code offset} to the other register. * Requires APF v4 or greater. */ - public ApfV4Generator addStoreData(Register src, int ofs) + public Type addStoreData(Register src, int ofs) throws IllegalInstructionException { requireApfVersion(APF_VERSION_4); return append(new Instruction(Opcodes.STDW, src).addSigned(ofs)); } - /** - * Updates instruction offset fields using latest instruction sizes. - * @return current program length in bytes. - */ - private int updateInstructionOffsets() { - int offset = 0; - for (Instruction instruction : mInstructions) { - instruction.offset = offset; - offset += instruction.size(); - } - return offset; - } - - /** - * Calculate the size of the imm. - */ - private static int calculateImmSize(int imm, boolean signed) { - if (imm == 0) { - return 0; - } - if (signed && (imm >= -128 && imm <= 127) || !signed && (imm >= 0 && imm <= 255)) { - return 1; - } - if (signed && (imm >= -32768 && imm <= 32767) || !signed && (imm >= 0 && imm <= 65535)) { - return 2; - } - return 4; - } - - /** - * Returns an overestimate of the size of the generated program. {@link #generate} may return - * a program that is smaller. - */ - public int programLengthOverEstimate() { - return updateInstructionOffsets(); - } - - /** - * Generate the bytecode for the APF program. - * @return the bytecode. - * @throws IllegalStateException if a label is referenced but not defined. - */ - public byte[] generate() throws IllegalInstructionException { - // Enforce that we can only generate once because we cannot unshrink instructions and - // PASS/DROP labels may move further away requiring unshrinking if we add further - // instructions. - if (mGenerated) { - throw new IllegalStateException("Can only generate() once!"); - } - mGenerated = true; - int total_size; - boolean shrunk; - // Shrink the immediate value fields of instructions. - // As we shrink the instructions some branch offset - // fields may shrink also, thereby shrinking the - // instructions further. Loop until we've reached the - // minimum size. Rarely will this loop more than a few times. - // Limit iterations to avoid O(n^2) behavior. - int iterations_remaining = 10; - do { - total_size = updateInstructionOffsets(); - // Update drop and pass label offsets. - mDropLabel.offset = total_size + 1; - mPassLabel.offset = total_size; - // Limit run-time in aberant circumstances. - if (iterations_remaining-- == 0) break; - // Attempt to shrink instructions. - shrunk = false; - for (Instruction instruction : mInstructions) { - if (instruction.shrink()) { - shrunk = true; - } - } - } while (shrunk); - // Generate bytecode for instructions. - byte[] bytecode = new byte[total_size]; - for (Instruction instruction : mInstructions) { - instruction.generate(bytecode); - } - return bytecode; - } }
diff --git a/src/android/net/apf/ApfV6Generator.java b/src/android/net/apf/ApfV6Generator.java new file mode 100644 index 0000000..40f5778 --- /dev/null +++ b/src/android/net/apf/ApfV6Generator.java
@@ -0,0 +1,397 @@ +/* + * Copyright (C) 2024 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.BaseApfGenerator.Rbit.Rbit0; +import static android.net.apf.BaseApfGenerator.Rbit.Rbit1; + +import androidx.annotation.NonNull; + +import com.android.net.module.util.HexDump; + +/** + * APFv6 assembler/generator. A tool for generating an APFv6 program. + * + * @hide + */ +public class ApfV6Generator extends ApfV4Generator<ApfV6Generator> { + + /** + * Creates an ApfV6Generator instance which is able to emit instructions for the specified + * {@code version} of the APF interpreter. Throws {@code IllegalInstructionException} if + * the requested version is unsupported. + * + */ + public ApfV6Generator() throws IllegalInstructionException { + super(MIN_APF_VERSION_IN_DEV); + } + + /** + * Add an instruction to the end of the program to increment the counter value and + * immediately return PASS. + */ + public ApfV6Generator addCountAndPass(int cnt) { + checkRange("CounterNumber", cnt /* value */, 1 /* lowerBound */, + 1000 /* upperBound */); + // PASS requires using Rbit0 because it shares opcode with DROP + return append(new Instruction(Opcodes.PASSDROP, Rbit0).addUnsigned(cnt)); + } + + /** + * Add an instruction to the end of the program to let the program immediately return DROP. + */ + public ApfV6Generator addDrop() { + // DROP requires using Rbit1 because it shares opcode with PASS + return append(new Instruction(Opcodes.PASSDROP, Rbit1)); + } + + /** + * Add an instruction to the end of the program to increment the counter value and + * immediately return DROP. + */ + public ApfV6Generator addCountAndDrop(int cnt) { + checkRange("CounterNumber", cnt /* value */, 1 /* lowerBound */, + 1000 /* upperBound */); + // DROP requires using Rbit1 because it shares opcode with PASS + return append(new Instruction(Opcodes.PASSDROP, Rbit1).addUnsigned(cnt)); + } + + /** + * Add an instruction to the end of the program to call the apf_allocate_buffer() function. + * Buffer length to be allocated is stored in register 0. + */ + public ApfV6Generator addAllocateR0() { + return append(new Instruction(ExtendedOpcodes.ALLOCATE)); + } + + /** + * Add an instruction to the end of the program to call the apf_allocate_buffer() function. + * + * @param size the buffer length to be allocated. + */ + public ApfV6Generator addAllocate(int size) { + // Rbit1 means the extra be16 immediate is present + return append(new Instruction(ExtendedOpcodes.ALLOCATE, Rbit1).addU16(size)); + } + + /** + * Add an instruction to the beginning of the program to reserve the data region. + * @param data the actual data byte + */ + public ApfV6Generator addData(byte[] data) throws IllegalInstructionException { + if (!mInstructions.isEmpty()) { + throw new IllegalInstructionException("data instruction has to come first"); + } + return append(new Instruction(Opcodes.JMP, Rbit1).addUnsigned(data.length) + .setBytesImm(data)); + } + + /** + * Add an instruction to the end of the program to transmit the allocated buffer without + * checksum. + */ + public ApfV6Generator addTransmitWithoutChecksum() { + return addTransmit(-1 /* ipOfs */); + } + + /** + * Add an instruction to the end of the program to transmit the allocated buffer. + */ + public ApfV6Generator addTransmit(int ipOfs) { + if (ipOfs >= 255) { + throw new IllegalArgumentException("IP offset of " + ipOfs + " must be < 255"); + } + if (ipOfs == -1) ipOfs = 255; + return append(new Instruction(ExtendedOpcodes.TRANSMIT, Rbit0).addU8(ipOfs).addU8(255)); + } + + /** + * Add an instruction to the end of the program to transmit the allocated buffer. + */ + public ApfV6Generator addTransmitL4(int ipOfs, int csumOfs, int csumStart, int partialCsum, + boolean isUdp) { + if (ipOfs >= 255) { + throw new IllegalArgumentException("IP offset of " + ipOfs + " must be < 255"); + } + if (ipOfs == -1) ipOfs = 255; + if (csumOfs >= 255) { + throw new IllegalArgumentException("L4 checksum requires csum offset of " + + csumOfs + " < 255"); + } + return append(new Instruction(ExtendedOpcodes.TRANSMIT, isUdp ? Rbit1 : Rbit0) + .addU8(ipOfs).addU8(csumOfs).addU8(csumStart).addU16(partialCsum)); + } + + /** + * Add an instruction to the end of the program to write 1 byte value to output buffer. + */ + public ApfV6Generator addWriteU8(int val) { + return append(new Instruction(Opcodes.WRITE).overrideLenField(1).addU8(val)); + } + + /** + * Add an instruction to the end of the program to write 2 bytes value to output buffer. + */ + public ApfV6Generator addWriteU16(int val) { + return append(new Instruction(Opcodes.WRITE).overrideLenField(2).addU16(val)); + } + + /** + * Add an instruction to the end of the program to write 4 bytes value to output buffer. + */ + public ApfV6Generator addWriteU32(long val) { + return append(new Instruction(Opcodes.WRITE).overrideLenField(4).addU32(val)); + } + + /** + * Add an instruction to the end of the program to write 1 byte value from register to output + * buffer. + */ + public ApfV6Generator addWriteU8(Register reg) { + return append(new Instruction(ExtendedOpcodes.EWRITE1, reg)); + } + + /** + * Add an instruction to the end of the program to write 2 byte value from register to output + * buffer. + */ + public ApfV6Generator addWriteU16(Register reg) { + return append(new Instruction(ExtendedOpcodes.EWRITE2, reg)); + } + + /** + * Add an instruction to the end of the program to write 4 byte value from register to output + * buffer. + */ + public ApfV6Generator addWriteU32(Register reg) { + return append(new Instruction(ExtendedOpcodes.EWRITE4, reg)); + } + + /** + * Add an instruction to the end of the program to copy data from APF program/data region to + * output buffer and auto-increment the output buffer pointer. + * + * @param src the offset inside the APF program/data region for where to start copy. + * @param len the length of bytes needed to be copied, only <= 255 bytes can be copied at + * one time. + * @return the ApfV6Generator object + */ + public ApfV6Generator addDataCopy(int src, int len) { + return append(new Instruction(Opcodes.PKTDATACOPY, Rbit1).addDataOffset(src).addU8(len)); + } + + /** + * Add an instruction to the end of the program to copy data from input packet to output + * buffer and auto-increment the output buffer pointer. + * + * @param src the offset inside the input packet for where to start copy. + * @param len the length of bytes needed to be copied, only <= 255 bytes can be copied at + * one time. + * @return the ApfV6Generator object + */ + public ApfV6Generator addPacketCopy(int src, int len) { + return append(new Instruction(Opcodes.PKTDATACOPY, Rbit0).addPacketOffset(src).addU8(len)); + } + + /** + * Add an instruction to the end of the program to copy data from APF program/data region to + * output buffer and auto-increment the output buffer pointer. + * Source offset is stored in R0. + * + * @param len the number of bytes to be copied, only <= 255 bytes can be copied at once. + * @return the ApfV6Generator object + */ + public ApfV6Generator addDataCopyFromR0(int len) { + return append(new Instruction(ExtendedOpcodes.EPKTDATACOPYIMM, Rbit1).addU8(len)); + } + + /** + * Add an instruction to the end of the program to copy data from input packet to output + * buffer and auto-increment the output buffer pointer. + * Source offset is stored in R0. + * + * @param len the number of bytes to be copied, only <= 255 bytes can be copied at once. + * @return the ApfV6Generator object + */ + public ApfV6Generator addPacketCopyFromR0(int len) { + return append(new Instruction(ExtendedOpcodes.EPKTDATACOPYIMM, Rbit0).addU8(len)); + } + + /** + * Add an instruction to the end of the program to copy data from APF program/data region to + * output buffer and auto-increment the output buffer pointer. + * Source offset is stored in R0. + * Copy length is stored in R1. + * + * @return the ApfV6Generator object + */ + public ApfV6Generator addDataCopyFromR0LenR1() { + return append(new Instruction(ExtendedOpcodes.EPKTDATACOPYR1, Rbit1)); + } + + /** + * Add an instruction to the end of the program to copy data from input packet to output + * buffer and auto-increment the output buffer pointer. + * Source offset is stored in R0. + * Copy length is stored in R1. + * + * @return the ApfV6Generator object + */ + public ApfV6Generator addPacketCopyFromR0LenR1() { + return append(new Instruction(ExtendedOpcodes.EPKTDATACOPYR1, Rbit0)); + } + + /** + * Appends a conditional jump instruction to the program: Jumps to {@code tgt} if the UDP + * payload's DNS questions do NOT contain the QNAMEs specified in {@code qnames} and qtype + * equals {@code qtype}. Examines the payload starting at the offset in R0. + * R = 0 means check for "does not contain". + * Drops packets if packets are corrupted. + */ + public ApfV6Generator addJumpIfPktAtR0DoesNotContainDnsQ(@NonNull byte[] qnames, int qtype, + @NonNull String tgt) { + validateNames(qnames); + return append(new Instruction(ExtendedOpcodes.JDNSQMATCH, Rbit0).setTargetLabel(tgt).addU8( + qtype).setBytesImm(qnames)); + } + + /** + * Same as {@link #addJumpIfPktAtR0DoesNotContainDnsQ} except passes packets if packets are + * corrupted. + */ + public ApfV6Generator addJumpIfPktAtR0DoesNotContainDnsQSafe(@NonNull byte[] qnames, int qtype, + @NonNull String tgt) { + validateNames(qnames); + return append(new Instruction(ExtendedOpcodes.JDNSQMATCHSAFE, Rbit0).setTargetLabel( + tgt).addU8(qtype).setBytesImm(qnames)); + } + + /** + * Appends a conditional jump instruction to the program: Jumps to {@code tgt} if the UDP + * payload's DNS questions contain the QNAMEs specified in {@code qnames} and qtype + * equals {@code qtype}. Examines the payload starting at the offset in R0. + * R = 1 means check for "contain". + * Drops packets if packets are corrupted. + */ + public ApfV6Generator addJumpIfPktAtR0ContainDnsQ(@NonNull byte[] qnames, int qtype, + @NonNull String tgt) { + validateNames(qnames); + return append(new Instruction(ExtendedOpcodes.JDNSQMATCH, Rbit1).setTargetLabel(tgt).addU8( + qtype).setBytesImm(qnames)); + } + + /** + * Same as {@link #addJumpIfPktAtR0ContainDnsQ} except passes packets if packets are + * corrupted. + */ + public ApfV6Generator addJumpIfPktAtR0ContainDnsQSafe(@NonNull byte[] qnames, int qtype, + @NonNull String tgt) { + validateNames(qnames); + return append(new Instruction(ExtendedOpcodes.JDNSQMATCHSAFE, Rbit1).setTargetLabel( + tgt).addU8(qtype).setBytesImm(qnames)); + } + + /** + * Appends a conditional jump instruction to the program: Jumps to {@code tgt} if the UDP + * payload's DNS answers/authority/additional records do NOT contain the NAMEs + * specified in {@code Names}. Examines the payload starting at the offset in R0. + * R = 0 means check for "does not contain". + * Drops packets if packets are corrupted. + */ + public ApfV6Generator addJumpIfPktAtR0DoesNotContainDnsA(@NonNull byte[] names, + @NonNull String tgt) { + validateNames(names); + return append(new Instruction(ExtendedOpcodes.JDNSAMATCH, Rbit0).setTargetLabel(tgt) + .setBytesImm(names)); + } + + /** + * Same as {@link #addJumpIfPktAtR0DoesNotContainDnsA} except passes packets if packets are + * corrupted. + */ + public ApfV6Generator addJumpIfPktAtR0DoesNotContainDnsASafe(@NonNull byte[] names, + @NonNull String tgt) { + validateNames(names); + return append(new Instruction(ExtendedOpcodes.JDNSAMATCHSAFE, Rbit0).setTargetLabel(tgt) + .setBytesImm(names)); + } + + /** + * Appends a conditional jump instruction to the program: Jumps to {@code tgt} if the UDP + * payload's DNS answers/authority/additional records contain the NAMEs + * specified in {@code Names}. Examines the payload starting at the offset in R0. + * R = 1 means check for "contain". + * Drops packets if packets are corrupted. + */ + public ApfV6Generator addJumpIfPktAtR0ContainDnsA(@NonNull byte[] names, + @NonNull String tgt) { + validateNames(names); + return append(new Instruction(ExtendedOpcodes.JDNSAMATCH, Rbit1).setTargetLabel( + tgt).setBytesImm(names)); + } + + /** + * Same as {@link #addJumpIfPktAtR0ContainDnsA} except passes packets if packets are + * corrupted. + */ + public ApfV6Generator addJumpIfPktAtR0ContainDnsASafe(@NonNull byte[] names, + @NonNull String tgt) { + validateNames(names); + return append(new Instruction(ExtendedOpcodes.JDNSAMATCHSAFE, Rbit1).setTargetLabel( + tgt).setBytesImm(names)); + } + + /** + * Check if the byte is valid dns character: A-Z,0-9,-,_ + */ + private static boolean isValidDnsCharacter(byte c) { + return (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '%'; + } + + private static void validateNames(@NonNull byte[] names) { + final int len = names.length; + if (len < 4) { + throw new IllegalArgumentException("qnames must have at least length 4"); + } + final String errorMessage = "qname: " + HexDump.toHexString(names) + + "is not null-terminated list of TLV-encoded names"; + int i = 0; + while (i < len - 1) { + int label_len = names[i++]; + // byte == 0xff means it is a '*' wildcard + if (label_len == -1) continue; + if (label_len < 1 || label_len > 63) { + throw new IllegalArgumentException( + "label len: " + label_len + " must be between 1 and 63"); + } + if (i + label_len >= len - 1) { + throw new IllegalArgumentException(errorMessage); + } + while (label_len-- > 0) { + if (!isValidDnsCharacter(names[i++])) { + throw new IllegalArgumentException("qname: " + HexDump.toHexString(names) + + " contains invalid character"); + } + } + if (names[i] == 0) { + i++; // skip null terminator. + } + } + if (names[len - 1] != 0) { + throw new IllegalArgumentException(errorMessage); + } + } +}
diff --git a/src/android/net/apf/BaseApfGenerator.java b/src/android/net/apf/BaseApfGenerator.java new file mode 100644 index 0000000..75ef639 --- /dev/null +++ b/src/android/net/apf/BaseApfGenerator.java
@@ -0,0 +1,778 @@ +/* + * Copyright (C) 2024 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.BaseApfGenerator.Rbit.Rbit0; +import static android.net.apf.BaseApfGenerator.Rbit.Rbit1; +import static android.net.apf.BaseApfGenerator.Register.R0; + +import androidx.annotation.NonNull; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * The base class for APF assembler/generator. + * + * @hide + */ +public abstract class BaseApfGenerator { + + public BaseApfGenerator(int mVersion) { + this.mVersion = mVersion; + } + + /** + * This exception is thrown when an attempt is made to generate an illegal instruction. + */ + public static class IllegalInstructionException extends Exception { + IllegalInstructionException(String msg) { + super(msg); + } + } + enum Opcodes { + LABEL(-1), + // Unconditionally pass (if R=0) or drop (if R=1) packet. + // An optional unsigned immediate value can be provided to encode the counter number. + // If the value is non-zero, the instruction increments the counter. + // The counter is located (-4 * counter number) bytes from the end of the data region. + // It is a U32 big-endian value and is always incremented by 1. + // This is more or less equivalent to: lddw R0, -N4; add R0,1; stdw R0, -N4; {pass,drop} + // e.g. "pass", "pass 1", "drop", "drop 1" + PASSDROP(0), + LDB(1), // Load 1 byte from immediate offset, e.g. "ldb R0, [5]" + LDH(2), // Load 2 bytes from immediate offset, e.g. "ldh R0, [5]" + LDW(3), // Load 4 bytes from immediate offset, e.g. "ldw R0, [5]" + LDBX(4), // Load 1 byte from immediate offset plus register, e.g. "ldbx R0, [5]R0" + LDHX(5), // Load 2 byte from immediate offset plus register, e.g. "ldhx R0, [5]R0" + LDWX(6), // Load 4 byte from immediate offset plus register, e.g. "ldwx R0, [5]R0" + ADD(7), // Add, e.g. "add R0,5" + MUL(8), // Multiply, e.g. "mul R0,5" + DIV(9), // Divide, e.g. "div R0,5" + AND(10), // And, e.g. "and R0,5" + OR(11), // Or, e.g. "or R0,5" + SH(12), // Left shift, e.g. "sh R0, 5" or "sh R0, -5" (shifts right) + LI(13), // Load immediate, e.g. "li R0,5" (immediate encoded as signed value) + // Jump, e.g. "jmp label" + // In APFv6, we use JMP(R=1) to encode the DATA instruction. DATA is executed as a jump. + // It tells how many bytes of the program regions are used to store the data and followed + // by the actual data bytes. + // "e.g. data 5, abcde" + JMP(14), + JEQ(15), // Compare equal and branch, e.g. "jeq R0,5,label" + JNE(16), // Compare not equal and branch, e.g. "jne R0,5,label" + JGT(17), // Compare greater than and branch, e.g. "jgt R0,5,label" + JLT(18), // Compare less than and branch, e.g. "jlt R0,5,label" + JSET(19), // Compare any bits set and branch, e.g. "jset R0,5,label" + JNEBS(20), // Compare not equal byte sequence, e.g. "jnebs R0,5,label,0x1122334455" + EXT(21), // Followed by immediate indicating ExtendedOpcodes. + LDDW(22), // Load 4 bytes from data memory address (register + immediate): "lddw R0, [5]R1" + STDW(23), // Store 4 bytes to data memory address (register + immediate): "stdw R0, [5]R1" + // Write 1, 2 or 4 bytes immediate to the output buffer and auto-increment the pointer to + // write. e.g. "write 5" + WRITE(24), + // Copy bytes from input packet/APF program/data region to output buffer and + // auto-increment the output buffer pointer. + // Register bit is used to specify the source of data copy. + // R=0 means copy from packet. + // R=1 means copy from APF program/data region. + // The copy length is stored in (u8)imm2. + // e.g. "pktcopy 5, 5" "datacopy 5, 5" + PKTDATACOPY(25); + + final int value; + + Opcodes(int value) { + this.value = value; + } + } + // Extended opcodes. Primary opcode is Opcodes.EXT. ExtendedOpcodes are encoded in the immediate + // field. + enum ExtendedOpcodes { + LDM(0), // Load from memory, e.g. "ldm R0,5" + STM(16), // Store to memory, e.g. "stm R0,5" + NOT(32), // Not, e.g. "not R0" + NEG(33), // Negate, e.g. "neg R0" + SWAP(34), // Swap, e.g. "swap R0,R1" + MOVE(35), // Move, e.g. "move R0,R1" + // Allocate writable output buffer. + // R=0, use register R0 to store the length. R=1, encode the length in the u16 int imm2. + // "e.g. allocate R0" + // "e.g. allocate 123" + ALLOCATE(36), + // Transmit and deallocate the buffer (transmission can be delayed until the program + // terminates). Length of buffer is the output buffer pointer (0 means discard). + // R=1 iff udp style L4 checksum + // u8 imm2 - ip header offset from start of buffer (255 for non-ip packets) + // u8 imm3 - offset from start of buffer to store L4 checksum (255 for no L4 checksum) + // u8 imm4 - offset from start of buffer to begin L4 checksum calc (present iff imm3 != 255) + // u16 imm5 - partial checksum value to include in L4 checksum (present iff imm3 != 255) + // "e.g. transmit" + TRANSMIT(37), + // Write 1, 2 or 4 byte value from register to the output buffer and auto-increment the + // output buffer pointer. + // e.g. "ewrite1 r0" + EWRITE1(38), + EWRITE2(39), + EWRITE4(40), + // Copy bytes from input packet/APF program/data region to output buffer and + // auto-increment the output buffer pointer. + // Register bit is used to specify the source of data copy. + // R=0 means copy from packet. + // R=1 means copy from APF program/data region. + // The source offset is stored in R0, copy length is stored in u8 imm2 or R1. + // e.g. "epktcopy r0, 16", "edatacopy r0, 16", "epktcopy r0, r1", "edatacopy r0, r1" + EPKTDATACOPYIMM(41), + EPKTDATACOPYR1(42), + // Jumps if the UDP payload content (starting at R0) does [not] match one + // of the specified QNAMEs in question records, applying case insensitivity. + // SAFE version PASSES corrupt packets, while the other one DROPS. + // R=0/1 meaning 'does not match'/'matches' + // R0: Offset to UDP payload content + // imm1: Extended opcode + // imm2: Jump label offset + // imm3(u8): Question type (PTR/SRV/TXT/A/AAAA) + // imm4(bytes): null terminated list of null terminated LV-encoded QNAMEs + // e.g.: "jdnsqeq R0,label,0xc,\002aa\005local\0\0", + // "jdnsqne R0,label,0xc,\002aa\005local\0\0" + JDNSQMATCH(43), + JDNSQMATCHSAFE(45), + // Jumps if the UDP payload content (starting at R0) does [not] match one + // of the specified NAMEs in answers/authority/additional records, applying + // case insensitivity. + // SAFE version PASSES corrupt packets, while the other one DROPS. + // R=0/1 meaning 'does not match'/'matches' + // R0: Offset to UDP payload content + // imm1: Extended opcode + // imm2: Jump label offset + // imm3(bytes): null terminated list of null terminated LV-encoded NAMEs + // e.g.: "jdnsaeq R0,label,0xc,\002aa\005local\0\0", + // "jdnsane R0,label,0xc,\002aa\005local\0\0" + + JDNSAMATCH(44), + JDNSAMATCHSAFE(46); + + final int value; + + ExtendedOpcodes(int value) { + this.value = value; + } + } + public enum Register { + R0, + R1; + } + + public enum Rbit { + Rbit0(0), + Rbit1(1); + + final int value; + + Rbit(int value) { + this.value = value; + } + } + + private enum IntImmediateType { + INDETERMINATE_SIZE_SIGNED, + INDETERMINATE_SIZE_UNSIGNED, + SIGNED_8, + UNSIGNED_8, + SIGNED_BE16, + UNSIGNED_BE16, + SIGNED_BE32, + UNSIGNED_BE32; + } + + private static class IntImmediate { + public final IntImmediateType mImmediateType; + public final int mValue; + + IntImmediate(int value, IntImmediateType type) { + mImmediateType = type; + mValue = value; + } + + private int calculateIndeterminateSize() { + switch (mImmediateType) { + case INDETERMINATE_SIZE_SIGNED: + return calculateImmSize(mValue, true /* signed */); + case INDETERMINATE_SIZE_UNSIGNED: + return calculateImmSize(mValue, false /* signed */); + default: + // For IMM with determinate size, return 0 to allow Math.max() calculation in + // caller function. + return 0; + } + } + + private int getEncodingSize(int immFieldSize) { + switch (mImmediateType) { + case SIGNED_8: + case UNSIGNED_8: + return 1; + case SIGNED_BE16: + case UNSIGNED_BE16: + return 2; + case SIGNED_BE32: + case UNSIGNED_BE32: + return 4; + case INDETERMINATE_SIZE_SIGNED: + case INDETERMINATE_SIZE_UNSIGNED: { + int minSizeRequired = calculateIndeterminateSize(); + if (minSizeRequired > immFieldSize) { + throw new IllegalStateException( + String.format("immFieldSize: %d is too small to encode value %d", + immFieldSize, mValue)); + } + return immFieldSize; + } + } + throw new IllegalStateException("UnhandledInvalid IntImmediateType: " + mImmediateType); + } + + private int writeValue(byte[] bytecode, Integer writingOffset, int immFieldSize) { + return Instruction.writeValue(mValue, bytecode, writingOffset, + getEncodingSize(immFieldSize)); + } + + public static IntImmediate newSigned(int imm) { + return new IntImmediate(imm, IntImmediateType.INDETERMINATE_SIZE_SIGNED); + } + + public static IntImmediate newUnsigned(long imm) { + // upperBound is 2^32 - 1 + checkRange("Unsigned IMM", imm, 0 /* lowerBound */, + 4294967295L /* upperBound */); + return new IntImmediate((int) imm, IntImmediateType.INDETERMINATE_SIZE_UNSIGNED); + } + + public static IntImmediate newTwosComplementUnsigned(long imm) { + checkRange("Unsigned TwosComplement IMM", imm, Integer.MIN_VALUE, + 4294967295L /* upperBound */); + return new IntImmediate((int) imm, IntImmediateType.INDETERMINATE_SIZE_UNSIGNED); + } + + public static IntImmediate newTwosComplementSigned(long imm) { + checkRange("Signed TwosComplement IMM", imm, Integer.MIN_VALUE, + 4294967295L /* upperBound */); + return new IntImmediate((int) imm, IntImmediateType.INDETERMINATE_SIZE_SIGNED); + } + + public static IntImmediate newS8(byte imm) { + checkRange("S8 IMM", imm, Byte.MIN_VALUE, Byte.MAX_VALUE); + return new IntImmediate(imm, IntImmediateType.SIGNED_8); + } + + public static IntImmediate newU8(int imm) { + checkRange("U8 IMM", imm, 0, 255); + return new IntImmediate(imm, IntImmediateType.UNSIGNED_8); + } + + public static IntImmediate newS16(short imm) { + return new IntImmediate(imm, IntImmediateType.SIGNED_BE16); + } + + public static IntImmediate newU16(int imm) { + checkRange("U16 IMM", imm, 0, 65535); + return new IntImmediate(imm, IntImmediateType.UNSIGNED_BE16); + } + + public static IntImmediate newS32(int imm) { + return new IntImmediate(imm, IntImmediateType.SIGNED_BE32); + } + + public static IntImmediate newU32(long imm) { + // upperBound is 2^32 - 1 + checkRange("U32 IMM", imm, 0 /* lowerBound */, + 4294967295L /* upperBound */); + return new IntImmediate((int) imm, IntImmediateType.UNSIGNED_BE32); + } + + @Override + public String toString() { + return "IntImmediate{" + "mImmediateType=" + mImmediateType + ", mValue=" + mValue + + '}'; + } + } + + class Instruction { + private final Opcodes mOpcode; + private final Rbit mRbit; + public final List<IntImmediate> mIntImms = new ArrayList<>(); + // When mOpcode is a jump: + private int mTargetLabelSize; + private int mLenFieldOverride = -1; + private String mTargetLabel; + // When mOpcode == Opcodes.LABEL: + private String mLabel; + private byte[] mBytesImm; + // Offset in bytes from the beginning of this program. + // Set by {@link BaseApfGenerator#generate}. + int offset; + + Instruction(Opcodes opcode, Rbit rbit) { + mOpcode = opcode; + mRbit = rbit; + } + + Instruction(Opcodes opcode, Register register) { + this(opcode, register == R0 ? Rbit0 : Rbit1); + } + + Instruction(ExtendedOpcodes extendedOpcodes, Rbit rbit) { + this(Opcodes.EXT, rbit); + addUnsigned(extendedOpcodes.value); + } + + Instruction(ExtendedOpcodes extendedOpcodes, Register register) { + this(Opcodes.EXT, register); + addUnsigned(extendedOpcodes.value); + } + + Instruction(ExtendedOpcodes extendedOpcodes, int slot, Register register) + throws IllegalInstructionException { + this(Opcodes.EXT, register); + if (slot < 0 || slot >= MEMORY_SLOTS) { + throw new IllegalInstructionException("illegal memory slot number: " + slot); + } + addUnsigned(extendedOpcodes.value + slot); + } + + Instruction(Opcodes opcode) { + this(opcode, R0); + } + + Instruction(ExtendedOpcodes extendedOpcodes) { + this(extendedOpcodes, R0); + } + + Instruction addSigned(int imm) { + mIntImms.add(IntImmediate.newSigned(imm)); + return this; + } + + Instruction addUnsigned(long imm) { + mIntImms.add(IntImmediate.newUnsigned(imm)); + return this; + } + + // in practice, 'int' always enough for packet offset + Instruction addPacketOffset(int imm) { + return addUnsigned(imm); + } + + // in practice, 'int' always enough for data offset + Instruction addDataOffset(int imm) { + return addUnsigned(imm); + } + + Instruction addTwosCompSigned(int imm) { + mIntImms.add(IntImmediate.newTwosComplementSigned(imm)); + return this; + } + + + Instruction addTwosCompUnsigned(int imm) { + mIntImms.add(IntImmediate.newTwosComplementUnsigned(imm)); + return this; + } + + Instruction addS8(byte imm) { + mIntImms.add(IntImmediate.newS8(imm)); + return this; + } + + Instruction addU8(int imm) { + mIntImms.add(IntImmediate.newU8(imm)); + return this; + } + + Instruction addS16(short imm) { + mIntImms.add(IntImmediate.newS16(imm)); + return this; + } + + Instruction addU16(int imm) { + mIntImms.add(IntImmediate.newU16(imm)); + return this; + } + + Instruction addS32(int imm) { + mIntImms.add(IntImmediate.newS32(imm)); + return this; + } + + Instruction addU32(long imm) { + mIntImms.add(IntImmediate.newU32(imm)); + return this; + } + + Instruction setLabel(String label) throws IllegalInstructionException { + if (mLabels.containsKey(label)) { + throw new IllegalInstructionException("duplicate label " + label); + } + if (mOpcode != Opcodes.LABEL) { + throw new IllegalStateException("adding label to non-label instruction"); + } + mLabel = label; + mLabels.put(label, this); + return this; + } + + Instruction setTargetLabel(String label) { + mTargetLabel = label; + mTargetLabelSize = 4; // May shrink later on in generate(). + return this; + } + + Instruction overrideLenField(int size) { + mLenFieldOverride = size; + return this; + } + + Instruction setBytesImm(byte[] bytes) { + mBytesImm = bytes; + return this; + } + + /** + * @return size of instruction in bytes. + */ + int size() { + if (mOpcode == Opcodes.LABEL) { + return 0; + } + int size = 1; + int indeterminateSize = calculateRequiredIndeterminateSize(); + for (IntImmediate imm : mIntImms) { + size += imm.getEncodingSize(indeterminateSize); + } + if (mTargetLabel != null) { + size += indeterminateSize; + } + if (mBytesImm != null) { + size += mBytesImm.length; + } + return size; + } + + /** + * Resize immediate value field so that it's only as big as required to + * contain the offset of the jump destination. + * @return {@code true} if shrunk. + */ + boolean shrink() throws IllegalInstructionException { + if (mTargetLabel == null) { + return false; + } + int oldTargetLabelSize = mTargetLabelSize; + mTargetLabelSize = calculateImmSize(calculateTargetLabelOffset(), false); + if (mTargetLabelSize > oldTargetLabelSize) { + throw new IllegalStateException("instruction grew"); + } + return mTargetLabelSize < oldTargetLabelSize; + } + + /** + * Assemble value for instruction size field. + */ + private int generateImmSizeField() { + // If we already know the size the length field, just use it + switch (mLenFieldOverride) { + case -1: + break; + case 1: + return 1; + case 2: + return 2; + case 4: + return 3; + default: + throw new IllegalStateException( + "mLenFieldOverride has invalid value: " + mLenFieldOverride); + } + // Otherwise, calculate + int immSize = calculateRequiredIndeterminateSize(); + // Encode size field to fit in 2 bits: 0->0, 1->1, 2->2, 3->4. + return immSize == 4 ? 3 : immSize; + } + + /** + * Assemble first byte of generated instruction. + */ + private byte generateInstructionByte() { + int sizeField = generateImmSizeField(); + return (byte) ((mOpcode.value << 3) | (sizeField << 1) | (byte) mRbit.value); + } + + /** + * Write {@code value} at offset {@code writingOffset} into {@code bytecode}. + * {@code immSize} bytes are written. {@code value} is truncated to + * {@code immSize} bytes. {@code value} is treated simply as a + * 32-bit value, so unsigned values should be zero extended and the truncation + * should simply throw away their zero-ed upper bits, and signed values should + * be sign extended and the truncation should simply throw away their signed + * upper bits. + */ + private static int writeValue(int value, byte[] bytecode, int writingOffset, int immSize) { + for (int i = immSize - 1; i >= 0; i--) { + bytecode[writingOffset++] = (byte) ((value >> (i * 8)) & 255); + } + return writingOffset; + } + + /** + * Generate bytecode for this instruction at offset {@link Instruction#offset}. + */ + void generate(byte[] bytecode) throws IllegalInstructionException { + if (mOpcode == Opcodes.LABEL) { + return; + } + int writingOffset = offset; + bytecode[writingOffset++] = generateInstructionByte(); + int indeterminateSize = calculateRequiredIndeterminateSize(); + int startOffset = 0; + if (mOpcode == Opcodes.EXT) { + // For extend opcode, always write the actual opcode first. + writingOffset = mIntImms.get(startOffset++).writeValue(bytecode, writingOffset, + indeterminateSize); + } + if (mTargetLabel != null) { + writingOffset = writeValue(calculateTargetLabelOffset(), bytecode, writingOffset, + indeterminateSize); + } + for (int i = startOffset; i < mIntImms.size(); ++i) { + writingOffset = mIntImms.get(i).writeValue(bytecode, writingOffset, + indeterminateSize); + } + if (mBytesImm != null) { + System.arraycopy(mBytesImm, 0, bytecode, writingOffset, mBytesImm.length); + writingOffset += mBytesImm.length; + } + if ((writingOffset - offset) != size()) { + throw new IllegalStateException("wrote " + (writingOffset - offset) + + " but should have written " + size()); + } + } + + /** + * Calculates the maximum indeterminate size of all IMMs in this instruction. + * <p> + * This method finds the largest size needed to encode any indeterminate-sized IMMs in + * the instruction. This size will be stored in the immLen field. + */ + private int calculateRequiredIndeterminateSize() { + int maxSize = mTargetLabelSize; + for (IntImmediate imm : mIntImms) { + maxSize = Math.max(maxSize, imm.calculateIndeterminateSize()); + } + return maxSize; + } + + private int calculateTargetLabelOffset() throws IllegalInstructionException { + Instruction targetLabelInstruction; + if (mTargetLabel == DROP_LABEL) { + targetLabelInstruction = mDropLabel; + } else if (mTargetLabel == PASS_LABEL) { + targetLabelInstruction = mPassLabel; + } else { + targetLabelInstruction = mLabels.get(mTargetLabel); + } + if (targetLabelInstruction == null) { + throw new IllegalInstructionException("label not found: " + mTargetLabel); + } + // Calculate distance from end of this instruction to instruction.offset. + final int targetLabelOffset = targetLabelInstruction.offset - (offset + size()); + return targetLabelOffset; + } + } + + /** + * Updates instruction offset fields using latest instruction sizes. + * @return current program length in bytes. + */ + private int updateInstructionOffsets() { + int offset = 0; + for (Instruction instruction : mInstructions) { + instruction.offset = offset; + offset += instruction.size(); + } + return offset; + } + + /** + * Calculate the size of the imm. + */ + private static int calculateImmSize(int imm, boolean signed) { + if (imm == 0) { + return 0; + } + if (signed && (imm >= -128 && imm <= 127) || !signed && (imm >= 0 && imm <= 255)) { + return 1; + } + if (signed && (imm >= -32768 && imm <= 32767) || !signed && (imm >= 0 && imm <= 65535)) { + return 2; + } + return 4; + } + + static void checkRange(@NonNull String variableName, long value, long lowerBound, + long upperBound) { + if (value >= lowerBound && value <= upperBound) { + return; + } + throw new IllegalArgumentException( + String.format("%s: %d, must be in range [%d, %d]", variableName, value, lowerBound, + upperBound)); + } + + /** + * Returns an overestimate of the size of the generated program. {@link #generate} may return + * a program that is smaller. + */ + public int programLengthOverEstimate() { + return updateInstructionOffsets(); + } + + /** + * Generate the bytecode for the APF program. + * @return the bytecode. + * @throws IllegalStateException if a label is referenced but not defined. + */ + public byte[] generate() throws IllegalInstructionException { + // Enforce that we can only generate once because we cannot unshrink instructions and + // PASS/DROP labels may move further away requiring unshrinking if we add further + // instructions. + if (mGenerated) { + throw new IllegalStateException("Can only generate() once!"); + } + mGenerated = true; + int total_size; + boolean shrunk; + // Shrink the immediate value fields of instructions. + // As we shrink the instructions some branch offset + // fields may shrink also, thereby shrinking the + // instructions further. Loop until we've reached the + // minimum size. Rarely will this loop more than a few times. + // Limit iterations to avoid O(n^2) behavior. + int iterations_remaining = 10; + do { + total_size = updateInstructionOffsets(); + // Update drop and pass label offsets. + mDropLabel.offset = total_size + 1; + mPassLabel.offset = total_size; + // Limit run-time in aberant circumstances. + if (iterations_remaining-- == 0) break; + // Attempt to shrink instructions. + shrunk = false; + for (Instruction instruction : mInstructions) { + if (instruction.shrink()) { + shrunk = true; + } + } + } while (shrunk); + // Generate bytecode for instructions. + byte[] bytecode = new byte[total_size]; + for (Instruction instruction : mInstructions) { + instruction.generate(bytecode); + } + return bytecode; + } + + /** + * Returns true if the BaseApfGenerator supports the specified {@code version}, otherwise false. + */ + public static boolean supportsVersion(int version) { + return version >= MIN_APF_VERSION; + } + + void requireApfVersion(int minimumVersion) throws IllegalInstructionException { + if (mVersion < minimumVersion) { + throw new IllegalInstructionException("Requires APF >= " + minimumVersion); + } + } + + /** + * Jump to this label to terminate the program and indicate the packet + * should be dropped. + */ + public static final String DROP_LABEL = "__DROP__"; + + /** + * Jump to this label to terminate the program and indicate the packet + * should be passed to the AP. + */ + public static final String PASS_LABEL = "__PASS__"; + + /** + * Number of memory slots available for access via APF stores to memory and loads from memory. + * The memory slots are numbered 0 to {@code MEMORY_SLOTS} - 1. This must be kept in sync with + * the APF interpreter. + */ + public static final int MEMORY_SLOTS = 16; + + /** + * Memory slot number that is prefilled with the IPv4 header length. + * Note that this memory slot may be overwritten by a program that + * executes stores to this memory slot. This must be kept in sync with + * the APF interpreter. + */ + public static final int IPV4_HEADER_SIZE_MEMORY_SLOT = 13; + + /** + * Memory slot number that is prefilled with the size of the packet being filtered in bytes. + * Note that this memory slot may be overwritten by a program that + * executes stores to this memory slot. This must be kept in sync with the APF interpreter. + */ + public static final int PACKET_SIZE_MEMORY_SLOT = 14; + + /** + * Memory slot number that is prefilled with the age of the filter in seconds. The age of the + * filter is the time since the filter was installed until now. + * Note that this memory slot may be overwritten by a program that + * executes stores to this memory slot. This must be kept in sync with the APF interpreter. + */ + public static final int FILTER_AGE_MEMORY_SLOT = 15; + + /** + * First memory slot containing prefilled values. Can be used in range comparisons to determine + * if memory slot index is within prefilled slots. + */ + public static final int FIRST_PREFILLED_MEMORY_SLOT = IPV4_HEADER_SIZE_MEMORY_SLOT; + + /** + * Last memory slot containing prefilled values. Can be used in range comparisons to determine + * if memory slot index is within prefilled slots. + */ + public static final int LAST_PREFILLED_MEMORY_SLOT = FILTER_AGE_MEMORY_SLOT; + + // This version number syncs up with APF_VERSION in hardware/google/apf/apf_interpreter.h + public static final int MIN_APF_VERSION = 2; + public static final int MIN_APF_VERSION_IN_DEV = 5; + public static final int APF_VERSION_4 = 4; + + + final ArrayList<Instruction> mInstructions = new ArrayList<Instruction>(); + private final HashMap<String, Instruction> mLabels = new HashMap<String, Instruction>(); + private final Instruction mDropLabel = new Instruction(Opcodes.LABEL); + private final Instruction mPassLabel = new Instruction(Opcodes.LABEL); + private final int mVersion; + public boolean mGenerated; +}
diff --git a/src/android/net/apf/DnsUtils.java b/src/android/net/apf/DnsUtils.java index f0f3039..4fa02be 100644 --- a/src/android/net/apf/DnsUtils.java +++ b/src/android/net/apf/DnsUtils.java
@@ -16,8 +16,8 @@ package android.net.apf; -import static android.net.apf.ApfV4Generator.Register.R0; -import static android.net.apf.ApfV4Generator.Register.R1; +import static android.net.apf.BaseApfGenerator.Register.R0; +import static android.net.apf.BaseApfGenerator.Register.R1; import static com.android.net.module.util.NetworkStackConstants.ETHER_HEADER_LEN; import static com.android.net.module.util.NetworkStackConstants.UDP_HEADER_LEN;
diff --git a/src/android/net/apf/JumpTable.java b/src/android/net/apf/JumpTable.java index 9b01225..016b01b 100644 --- a/src/android/net/apf/JumpTable.java +++ b/src/android/net/apf/JumpTable.java
@@ -16,7 +16,7 @@ package android.net.apf; -import static android.net.apf.ApfV4Generator.Register.R0; +import static android.net.apf.BaseApfGenerator.Register.R0; import androidx.annotation.NonNull;
diff --git a/src/android/net/apf/LegacyApfFilter.java b/src/android/net/apf/LegacyApfFilter.java index d81f8b4..20a9e33 100644 --- a/src/android/net/apf/LegacyApfFilter.java +++ b/src/android/net/apf/LegacyApfFilter.java
@@ -16,6 +16,8 @@ package android.net.apf; +import static android.net.apf.BaseApfGenerator.Register.R0; +import static android.net.apf.BaseApfGenerator.Register.R1; import static android.net.util.SocketUtils.makePacketSocketAddress; import static android.system.OsConstants.AF_PACKET; import static android.system.OsConstants.ARPHRD_ETHER; @@ -43,8 +45,7 @@ import android.net.NattKeepalivePacketDataParcelable; import android.net.TcpKeepalivePacketDataParcelable; import android.net.apf.ApfCounterTracker.Counter; -import android.net.apf.ApfV4Generator.IllegalInstructionException; -import android.net.apf.ApfV4Generator.Register; +import android.net.apf.BaseApfGenerator.IllegalInstructionException; import android.net.ip.IpClient.IpClientCallbacksWrapper; import android.net.metrics.ApfProgramEvent; import android.net.metrics.ApfStats; @@ -126,7 +127,7 @@ */ private void maybeSetupCounter(ApfV4Generator gen, Counter c) { if (mApfCapabilities.hasDataAccess()) { - gen.addLoadImmediate(Register.R1, c.offset()); + gen.addLoadImmediate(R1, c.offset()); } } @@ -972,15 +973,15 @@ long generateFilterLocked(ApfV4Generator gen) throws IllegalInstructionException { String nextFilterLabel = "Ra" + getUniqueNumberLocked(); // Skip if packet is not the right size - gen.addLoadFromMemory(Register.R0, gen.PACKET_SIZE_MEMORY_SLOT); + gen.addLoadFromMemory(R0, gen.PACKET_SIZE_MEMORY_SLOT); gen.addJumpIfR0NotEquals(mPacket.capacity(), nextFilterLabel); // Skip filter if expired - gen.addLoadFromMemory(Register.R0, gen.FILTER_AGE_MEMORY_SLOT); + gen.addLoadFromMemory(R0, gen.FILTER_AGE_MEMORY_SLOT); gen.addJumpIfR0GreaterThan(filterLifetime(), nextFilterLabel); for (PacketSection section : mPacketSections) { // Generate code to match the packet bytes. if (section.type == PacketSection.Type.MATCH) { - gen.addLoadImmediate(Register.R0, section.start); + gen.addLoadImmediate(R0, section.start); gen.addJumpIfBytesAtR0NotEqual( Arrays.copyOfRange(mPacket.array(), section.start, section.start + section.length), @@ -991,8 +992,8 @@ // The packet is accepted if any non-ignored lifetime is lower than filterLifetime. if (isRelevantLifetime(section)) { switch (section.length) { - case 4: gen.addLoad32(Register.R0, section.start); break; - case 2: gen.addLoad16(Register.R0, section.start); break; + case 4: gen.addLoad32(R0, section.start); break; + case 2: gen.addLoad16(R0, section.start); break; default: throw new IllegalStateException( "bogus lifetime size " + section.length); @@ -1064,21 +1065,21 @@ void generateFilterLocked(ApfV4Generator gen) throws IllegalInstructionException { final String nextFilterLabel = "natt_keepalive_filter" + getUniqueNumberLocked(); - gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN + IPV4_SRC_ADDR_OFFSET); + gen.addLoadImmediate(R0, ETH_HEADER_LEN + IPV4_SRC_ADDR_OFFSET); gen.addJumpIfBytesAtR0NotEqual(mSrcDstAddr, nextFilterLabel); // A NAT-T keepalive packet contains 1 byte payload with the value 0xff // Check payload length is 1 - gen.addLoadFromMemory(Register.R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); + gen.addLoadFromMemory(R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); gen.addAdd(UDP_HEADER_LEN); gen.addSwap(); - gen.addLoad16(Register.R0, IPV4_TOTAL_LENGTH_OFFSET); - gen.addNeg(Register.R1); + gen.addLoad16(R0, IPV4_TOTAL_LENGTH_OFFSET); + gen.addNeg(R1); gen.addAddR1(); gen.addJumpIfR0NotEquals(1, nextFilterLabel); // Check that the ports match - gen.addLoadFromMemory(Register.R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); + gen.addLoadFromMemory(R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); gen.addAdd(ETH_HEADER_LEN); gen.addJumpIfBytesAtR0NotEqual(mPortFingerprint, nextFilterLabel); @@ -1180,28 +1181,28 @@ void generateFilterLocked(ApfV4Generator gen) throws IllegalInstructionException { final String nextFilterLabel = "keepalive_ack" + getUniqueNumberLocked(); - gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN + IPV4_SRC_ADDR_OFFSET); + gen.addLoadImmediate(R0, ETH_HEADER_LEN + IPV4_SRC_ADDR_OFFSET); gen.addJumpIfBytesAtR0NotEqual(mSrcDstAddr, nextFilterLabel); // Skip to the next filter if it's not zero-sized : // TCP_HEADER_SIZE + IPV4_HEADER_SIZE - ipv4_total_length == 0 // Load the IP header size into R1 - gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); + gen.addLoadFromMemory(R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); // Load the TCP header size into R0 (it's indexed by R1) - gen.addLoad8Indexed(Register.R0, ETH_HEADER_LEN + TCP_HEADER_SIZE_OFFSET); + gen.addLoad8Indexed(R0, ETH_HEADER_LEN + TCP_HEADER_SIZE_OFFSET); // Size offset is in the top nibble, but it must be multiplied by 4, and the two // top bits of the low nibble are guaranteed to be zeroes. Right-shift R0 by 2. gen.addRightShift(2); // R0 += R1 -> R0 contains TCP + IP headers length gen.addAddR1(); // Load IPv4 total length - gen.addLoad16(Register.R1, IPV4_TOTAL_LENGTH_OFFSET); - gen.addNeg(Register.R0); + gen.addLoad16(R1, IPV4_TOTAL_LENGTH_OFFSET); + gen.addNeg(R0); gen.addAddR1(); gen.addJumpIfR0NotEquals(0, nextFilterLabel); // Add IPv4 header length - gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); - gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN); + gen.addLoadFromMemory(R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); + gen.addLoadImmediate(R0, ETH_HEADER_LEN); gen.addAddR1(); gen.addJumpIfBytesAtR0NotEqual(mPortSeqAckFingerprint, nextFilterLabel); @@ -1315,23 +1316,23 @@ final String checkTargetIPv4 = "checkTargetIPv4"; // Pass if not ARP IPv4. - gen.addLoadImmediate(Register.R0, ARP_HEADER_OFFSET); + gen.addLoadImmediate(R0, ARP_HEADER_OFFSET); maybeSetupCounter(gen, Counter.PASSED_ARP_NON_IPV4); gen.addJumpIfBytesAtR0NotEqual(ARP_IPV4_HEADER, mCountAndPassLabel); // Pass if unknown ARP opcode. - gen.addLoad16(Register.R0, ARP_OPCODE_OFFSET); + gen.addLoad16(R0, ARP_OPCODE_OFFSET); gen.addJumpIfR0Equals(ARP_OPCODE_REQUEST, checkTargetIPv4); // Skip to unicast check maybeSetupCounter(gen, Counter.PASSED_ARP_UNKNOWN); gen.addJumpIfR0NotEquals(ARP_OPCODE_REPLY, mCountAndPassLabel); // Drop if ARP reply source IP is 0.0.0.0 - gen.addLoad32(Register.R0, ARP_SOURCE_IP_ADDRESS_OFFSET); + gen.addLoad32(R0, ARP_SOURCE_IP_ADDRESS_OFFSET); maybeSetupCounter(gen, Counter.DROPPED_ARP_REPLY_SPA_NO_HOST); gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, mCountAndDropLabel); // Pass if unicast reply. - gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET); + gen.addLoadImmediate(R0, ETH_DEST_ADDR_OFFSET); maybeSetupCounter(gen, Counter.PASSED_ARP_UNICAST_REPLY); gen.addJumpIfBytesAtR0NotEqual(ETHER_BROADCAST, mCountAndPassLabel); @@ -1339,13 +1340,13 @@ gen.defineLabel(checkTargetIPv4); if (mIPv4Address == null) { // When there is no IPv4 address, drop GARP replies (b/29404209). - gen.addLoad32(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET); + gen.addLoad32(R0, ARP_TARGET_IP_ADDRESS_OFFSET); maybeSetupCounter(gen, Counter.DROPPED_GARP_REPLY); gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, mCountAndDropLabel); } else { // When there is an IPv4 address, drop unicast/broadcast requests // and broadcast replies with a different target IPv4 address. - gen.addLoadImmediate(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET); + gen.addLoadImmediate(R0, ARP_TARGET_IP_ADDRESS_OFFSET); maybeSetupCounter(gen, Counter.DROPPED_ARP_OTHER_HOST); gen.addJumpIfBytesAtR0NotEqual(mIPv4Address, mCountAndDropLabel); } @@ -1382,17 +1383,17 @@ // Pass DHCP addressed to us. // Check it's UDP. - gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET); + gen.addLoad8(R0, IPV4_PROTOCOL_OFFSET); gen.addJumpIfR0NotEquals(IPPROTO_UDP, skipDhcpv4Filter); // Check it's not a fragment. This matches the BPF filter installed by the DHCP client. - gen.addLoad16(Register.R0, IPV4_FRAGMENT_OFFSET_OFFSET); + gen.addLoad16(R0, IPV4_FRAGMENT_OFFSET_OFFSET); gen.addJumpIfR0AnyBitsSet(IPV4_FRAGMENT_OFFSET_MASK, skipDhcpv4Filter); // Check it's addressed to DHCP client port. - gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); - gen.addLoad16Indexed(Register.R0, UDP_DESTINATION_PORT_OFFSET); + gen.addLoadFromMemory(R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); + gen.addLoad16Indexed(R0, UDP_DESTINATION_PORT_OFFSET); gen.addJumpIfR0NotEquals(DHCP_CLIENT_PORT, skipDhcpv4Filter); // Check it's DHCP to our MAC address. - gen.addLoadImmediate(Register.R0, DHCP_CLIENT_MAC_OFFSET); + gen.addLoadImmediate(R0, DHCP_CLIENT_MAC_OFFSET); // NOTE: Relies on R1 containing IPv4 header offset. gen.addAddR1(); gen.addJumpIfBytesAtR0NotEqual(mHardwareAddress, skipDhcpv4Filter); @@ -1403,14 +1404,14 @@ gen.defineLabel(skipDhcpv4Filter); // If IPv4 destination address is in multicast range, drop. - gen.addLoad8(Register.R0, IPV4_DEST_ADDR_OFFSET); + gen.addLoad8(R0, IPV4_DEST_ADDR_OFFSET); gen.addAnd(0xf0); maybeSetupCounter(gen, Counter.DROPPED_IPV4_MULTICAST); gen.addJumpIfR0Equals(0xe0, mCountAndDropLabel); // If IPv4 broadcast packet, drop regardless of L2 (b/30231088). maybeSetupCounter(gen, Counter.DROPPED_IPV4_BROADCAST_ADDR); - gen.addLoad32(Register.R0, IPV4_DEST_ADDR_OFFSET); + gen.addLoad32(R0, IPV4_DEST_ADDR_OFFSET); gen.addJumpIfR0Equals(IPV4_BROADCAST_ADDRESS, mCountAndDropLabel); if (mIPv4Address != null && mIPv4PrefixLength < 31) { maybeSetupCounter(gen, Counter.DROPPED_IPV4_BROADCAST_NET); @@ -1428,7 +1429,7 @@ // If L2 broadcast packet, drop. // TODO: can we invert this condition to fall through to the common pass case below? maybeSetupCounter(gen, Counter.PASSED_IPV4_UNICAST); - gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET); + gen.addLoadImmediate(R0, ETH_DEST_ADDR_OFFSET); gen.addJumpIfBytesAtR0NotEqual(ETHER_BROADCAST, mCountAndPassLabel); maybeSetupCounter(gen, Counter.DROPPED_IPV4_L2_BROADCAST); gen.addJump(mCountAndDropLabel); @@ -1452,7 +1453,7 @@ if (!haveKeepaliveResponses) return; // If not the right proto, skip keepalive filters - gen.addLoad8(Register.R0, offset); + gen.addLoad8(R0, offset); gen.addJumpIfR0NotEquals(proto, label); // Drop Keepalive responses @@ -1501,7 +1502,7 @@ // if keepalive ack // drop - gen.addLoad8(Register.R0, IPV6_NEXT_HEADER_OFFSET); + gen.addLoad8(R0, IPV6_NEXT_HEADER_OFFSET); // MLD packets set the router-alert hop-by-hop option. // TODO: be smarter about not blindly passing every packet with HBH options. @@ -1520,7 +1521,7 @@ // ICMPv6 but not ECHO? -> Skip the multicast filter. // (ICMPv6 ECHO requests will go through the multicast filter below). - gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET); + gen.addLoad8(R0, ICMP6_TYPE_OFFSET); gen.addJumpIfR0NotEquals(ICMPV6_ECHO_REQUEST_TYPE, skipIPv6MulticastFilterLabel); } else { gen.addJumpIfR0Equals(IPPROTO_ICMPV6, skipIPv6MulticastFilterLabel); @@ -1529,7 +1530,7 @@ // Drop all other packets sent to ff00::/8 (multicast prefix). gen.defineLabel(dropAllIPv6MulticastsLabel); maybeSetupCounter(gen, Counter.DROPPED_IPV6_NON_ICMP_MULTICAST); - gen.addLoad8(Register.R0, IPV6_DEST_ADDR_OFFSET); + gen.addLoad8(R0, IPV6_DEST_ADDR_OFFSET); gen.addJumpIfR0Equals(0xff, mCountAndDropLabel); // If any keepalive filter matches, drop generateV6KeepaliveFilters(gen); @@ -1548,7 +1549,7 @@ // Add unsolicited multicast neighbor announcements filter String skipUnsolicitedMulticastNALabel = "skipUnsolicitedMulticastNA"; - gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET); + gen.addLoad8(R0, ICMP6_TYPE_OFFSET); // Drop all router solicitations (b/32833400) maybeSetupCounter(gen, Counter.DROPPED_IPV6_ROUTER_SOLICITATION); gen.addJumpIfR0Equals(ICMPV6_ROUTER_SOLICITATION, mCountAndDropLabel); @@ -1558,7 +1559,7 @@ // This is a way to cover ff02::1 and ff02::2 with a single JNEBS. // TODO: Drop only if they don't contain the address of on-link neighbours. final byte[] unsolicitedNaDropPrefix = Arrays.copyOf(IPV6_ALL_NODES_ADDRESS, 15); - gen.addLoadImmediate(Register.R0, IPV6_DEST_ADDR_OFFSET); + gen.addLoadImmediate(R0, IPV6_DEST_ADDR_OFFSET); gen.addJumpIfBytesAtR0NotEqual(unsolicitedNaDropPrefix, skipUnsolicitedMulticastNALabel); maybeSetupCounter(gen, Counter.DROPPED_IPV6_MULTICAST_NA); @@ -1615,19 +1616,19 @@ // 3. it is a UDP packet with port 5353 // Check it's L2 mDNS multicast address. - gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET); + gen.addLoadImmediate(R0, ETH_DEST_ADDR_OFFSET); gen.addJumpIfBytesAtR0NotEqual(ETH_MULTICAST_MDNS_V4_MAC_ADDRESS, skipMdnsv4Filter); // Checks it's IPv4. - gen.addLoad16(Register.R0, ETH_ETHERTYPE_OFFSET); + gen.addLoad16(R0, ETH_ETHERTYPE_OFFSET); gen.addJumpIfR0NotEquals(ETH_P_IP, skipMdnsFilter); // Checks it's UDP. - gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET); + gen.addLoad8(R0, IPV4_PROTOCOL_OFFSET); gen.addJumpIfR0NotEquals(IPPROTO_UDP, skipMdnsFilter); // Set R1 to IPv4 header. - gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); + gen.addLoadFromMemory(R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); gen.addJump(checkMdnsUdpPort); gen.defineLabel(skipMdnsv4Filter); @@ -1637,28 +1638,28 @@ gen.addJumpIfBytesAtR0NotEqual(ETH_MULTICAST_MDNS_V6_MAC_ADDRESS, skipMdnsFilter); // Checks it's IPv6. - gen.addLoad16(Register.R0, ETH_ETHERTYPE_OFFSET); + gen.addLoad16(R0, ETH_ETHERTYPE_OFFSET); gen.addJumpIfR0NotEquals(ETH_P_IPV6, skipMdnsFilter); // Checks it's UDP. - gen.addLoad8(Register.R0, IPV6_NEXT_HEADER_OFFSET); + gen.addLoad8(R0, IPV6_NEXT_HEADER_OFFSET); gen.addJumpIfR0NotEquals(IPPROTO_UDP, skipMdnsFilter); // Set R1 to IPv6 header. - gen.addLoadImmediate(Register.R1, IPV6_HEADER_LEN); + gen.addLoadImmediate(R1, IPV6_HEADER_LEN); // Checks it's mDNS UDP port gen.defineLabel(checkMdnsUdpPort); - gen.addLoad16Indexed(Register.R0, UDP_DESTINATION_PORT_OFFSET); + gen.addLoad16Indexed(R0, UDP_DESTINATION_PORT_OFFSET); gen.addJumpIfR0NotEquals(MDNS_PORT, skipMdnsFilter); - gen.addLoad16Indexed(Register.R0, MDNS_QDCOUNT_OFFSET); + gen.addLoad16Indexed(R0, MDNS_QDCOUNT_OFFSET); // If QDCOUNT != 1, pass the packet gen.addJumpIfR0NotEquals(1, mDnsAcceptPacket); // If QDCOUNT == 1, matches the QNAME with allowlist. // Load offset for the first QNAME. - gen.addLoadImmediate(Register.R0, MDNS_QNAME_OFFSET); + gen.addLoadImmediate(R0, MDNS_QNAME_OFFSET); gen.addAddR1(); // Check first QNAME against allowlist @@ -1718,9 +1719,18 @@ if (mApfCapabilities.hasDataAccess()) { // Increment TOTAL_PACKETS maybeSetupCounter(gen, Counter.TOTAL_PACKETS); - gen.addLoadData(Register.R0, 0); // load counter + gen.addLoadData(R0, 0); // load counter gen.addAdd(1); - gen.addStoreData(Register.R0, 0); // write-back counter + gen.addStoreData(R0, 0); // write-back counter + + maybeSetupCounter(gen, Counter.FILTER_AGE_SECONDS); + gen.addLoadFromMemory(R0, 15); // m[15] is filter age in seconds + gen.addStoreData(R0, 0); // store 'counter' + + // requires a new enough APFv5+ interpreter, otherwise will be 0 + maybeSetupCounter(gen, Counter.FILTER_AGE_16384THS); + gen.addLoadFromMemory(R0, 9); // m[9] is filter age in 16384ths + gen.addStoreData(R0, 0); // store 'counter' } // Here's a basic summary of what the initial program does: @@ -1739,7 +1749,7 @@ // pass // insert IPv6 filter to drop, pass, or fall off the end for ICMPv6 packets - gen.addLoad16(Register.R0, ETH_ETHERTYPE_OFFSET); + gen.addLoad16(R0, ETH_ETHERTYPE_OFFSET); if (mDrop802_3Frames) { // drop 802.3 frames (ethtype < 0x0600) @@ -1761,7 +1771,7 @@ // Add mDNS filter: generateMdnsFilterLocked(gen); - gen.addLoad16(Register.R0, ETH_ETHERTYPE_OFFSET); + gen.addLoad16(R0, ETH_ETHERTYPE_OFFSET); // Add IPv4 filters: String skipIPv4FiltersLabel = "skipIPv4Filters"; @@ -1777,7 +1787,7 @@ gen.addJumpIfR0Equals(ETH_P_IPV6, ipv6FilterLabel); // Drop non-IP non-ARP broadcasts, pass the rest - gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET); + gen.addLoadImmediate(R0, ETH_DEST_ADDR_OFFSET); maybeSetupCounter(gen, Counter.PASSED_NON_IP_UNICAST); gen.addJumpIfBytesAtR0NotEqual(ETHER_BROADCAST, mCountAndPassLabel); maybeSetupCounter(gen, Counter.DROPPED_ETH_BROADCAST); @@ -1809,16 +1819,16 @@ // pointed to by R1, then jumps to the pass label. This saves a few bytes over inserting // the entire sequence inline for every counter. gen.defineLabel(mCountAndPassLabel); - gen.addLoadData(Register.R0, 0); // R0 = *(R1 + 0) + gen.addLoadData(R0, 0); // R0 = *(R1 + 0) gen.addAdd(1); // R0++ - gen.addStoreData(Register.R0, 0); // *(R1 + 0) = R0 + gen.addStoreData(R0, 0); // *(R1 + 0) = R0 gen.addJump(gen.PASS_LABEL); // Same as above for the count & drop trampoline. gen.defineLabel(mCountAndDropLabel); - gen.addLoadData(Register.R0, 0); // R0 = *(R1 + 0) + gen.addLoadData(R0, 0); // R0 = *(R1 + 0) gen.addAdd(1); // R0++ - gen.addStoreData(Register.R0, 0); // *(R1 + 0) = R0 + gen.addStoreData(R0, 0); // *(R1 + 0) = R0 gen.addJump(gen.DROP_LABEL); }
diff --git a/src/android/net/dhcp/DhcpClient.java b/src/android/net/dhcp/DhcpClient.java index 4e6a222..e415698 100644 --- a/src/android/net/dhcp/DhcpClient.java +++ b/src/android/net/dhcp/DhcpClient.java
@@ -64,6 +64,7 @@ import android.net.MacAddress; import android.net.NetworkStackIpMemoryStore; import android.net.TrafficStats; +import android.net.ip.IIpClient; import android.net.ip.IpClient; import android.net.ipmemorystore.NetworkAttributes; import android.net.ipmemorystore.OnNetworkAttributesRetrievedListener; @@ -424,7 +425,7 @@ * Get the configuration from RRO to check whether or not to send hostname option in * DHCPDISCOVER/DHCPREQUEST message. */ - public boolean getSendHostnameOption(final Context context) { + public boolean getSendHostnameOverlaySetting(final Context context) { return context.getResources().getBoolean(R.bool.config_dhcp_client_hostname); } @@ -533,11 +534,19 @@ mRebindAlarm = makeWakeupMessage("REBIND", CMD_REBIND_DHCP); mExpiryAlarm = makeWakeupMessage("EXPIRY", CMD_EXPIRE_DHCP); - // Transliterate hostname read from system settings if RRO option is enabled. - final boolean sendHostname = deps.getSendHostnameOption(context); - mHostname = sendHostname ? new HostnameTransliterator().transliterate( - deps.getDeviceName(mContext)) : null; - mMetrics.setHostnameTransinfo(sendHostname, mHostname != null); + mHostname = new HostnameTransliterator().transliterate(deps.getDeviceName(mContext)); + mMetrics.setHostnameTransinfo(deps.getSendHostnameOverlaySetting(context), + mHostname != null); + } + + @Nullable + private String maybeGetHostnameForSending() { + boolean sendHostname = mDependencies.getSendHostnameOverlaySetting(mContext); + if (mConfiguration != null + && mConfiguration.hostnameSetting != IIpClient.HOSTNAME_SETTING_UNSET) { + sendHostname = mConfiguration.hostnameSetting == IIpClient.HOSTNAME_SETTING_SEND; + } + return sendHostname ? mHostname : null; } public void registerForPreDhcpNotification() { @@ -557,7 +566,7 @@ * check whether or not to support DHCP Rapid Commit option. */ public boolean isDhcpRapidCommitEnabled() { - return mDependencies.isFeatureEnabled(mContext, DHCP_RAPID_COMMIT_VERSION); + return mDependencies.isFeatureNotChickenedOut(mContext, DHCP_RAPID_COMMIT_VERSION); } /** @@ -758,7 +767,7 @@ final boolean requestRapidCommit = isDhcpRapidCommitEnabled() && (getSecs() <= 4); final ByteBuffer packet = DhcpPacket.buildDiscoverPacket( DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr, - DO_UNICAST, getRequestedParams(), requestRapidCommit, mHostname, + DO_UNICAST, getRequestedParams(), requestRapidCommit, maybeGetHostnameForSending(), mConfiguration.options); mMetrics.incrementCountForDiscover(); return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST); @@ -773,7 +782,7 @@ final ByteBuffer packet = DhcpPacket.buildRequestPacket( encap, mTransactionId, getSecs(), clientAddress, DO_UNICAST, mHwAddr, - requestedAddress, serverAddress, getRequestedParams(), mHostname, + requestedAddress, serverAddress, getRequestedParams(), maybeGetHostnameForSending(), mConfiguration.options); String serverStr = (serverAddress != null) ? serverAddress.getHostAddress() : null; String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() + @@ -996,14 +1005,17 @@ @NonNull public final List<DhcpOption> options; public final boolean isWifiManagedProfile; + public final int hostnameSetting; public Configuration(@Nullable final String l2Key, final boolean isPreconnectionEnabled, @NonNull final List<DhcpOption> options, - final boolean isWifiManagedProfile) { + final boolean isWifiManagedProfile, + final int hostnameSetting) { this.l2Key = l2Key; this.isPreconnectionEnabled = isPreconnectionEnabled; this.options = options; this.isWifiManagedProfile = isWifiManagedProfile; + this.hostnameSetting = hostnameSetting; } } @@ -1400,7 +1412,8 @@ final Layer2PacketParcelable l2Packet = new Layer2PacketParcelable(); final ByteBuffer packet = DhcpPacket.buildDiscoverPacket( DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr, - DO_UNICAST, getRequestedParams(), true /* rapid commit */, mHostname, + DO_UNICAST, getRequestedParams(), true /* rapid commit */, + maybeGetHostnameForSending(), mConfiguration.options); l2Packet.dstMacAddress = MacAddress.fromBytes(DhcpPacket.ETHER_BROADCAST);
diff --git a/src/android/net/dhcp/DhcpServer.java b/src/android/net/dhcp/DhcpServer.java index 041417d..276fee1 100644 --- a/src/android/net/dhcp/DhcpServer.java +++ b/src/android/net/dhcp/DhcpServer.java
@@ -195,6 +195,14 @@ * @param name Specific experimental flag name. */ boolean isFeatureEnabled(@NonNull Context context, @NonNull String name); + + /** + * Check whether one specific experimental feature for connectivity namespace is not + * disabled. + * @param context The global context information about an app environment. + * @param name Specific experimental flag name. + */ + boolean isFeatureNotChickenedOut(@NonNull Context context, @NonNull String name); } private class DependenciesImpl implements Dependencies { @@ -234,6 +242,11 @@ public boolean isFeatureEnabled(@NonNull Context context, @NonNull String name) { return DeviceConfigUtils.isNetworkStackFeatureEnabled(context, name); } + + @Override + public boolean isFeatureNotChickenedOut(final Context context, final String name) { + return DeviceConfigUtils.isNetworkStackFeatureNotChickenedOut(context, name); + } } private static class MalformedPacketException extends Exception { @@ -262,7 +275,8 @@ mDeps = deps; mClock = deps.makeClock(); mLeaseRepo = deps.makeLeaseRepository(mServingParams, mLog, mClock); - mDhcpRapidCommitEnabled = deps.isFeatureEnabled(context, DHCP_RAPID_COMMIT_VERSION); + mDhcpRapidCommitEnabled = + deps.isFeatureNotChickenedOut(context, DHCP_RAPID_COMMIT_VERSION); // CHECKSTYLE:OFF IndentationCheck addState(mStoppedState);
diff --git a/src/android/net/ip/IpClient.java b/src/android/net/ip/IpClient.java index 56dbb6f..c1d18ae 100644 --- a/src/android/net/ip/IpClient.java +++ b/src/android/net/ip/IpClient.java
@@ -51,6 +51,7 @@ import static com.android.networkstack.util.NetworkStackUtils.IPCLIENT_GARP_NA_ROAMING_VERSION; import static com.android.networkstack.util.NetworkStackUtils.IPCLIENT_GRATUITOUS_NA_VERSION; import static com.android.networkstack.util.NetworkStackUtils.IPCLIENT_IGNORE_LOW_RA_LIFETIME_VERSION; +import static com.android.networkstack.util.NetworkStackUtils.IPCLIENT_POPULATE_LINK_ADDRESS_LIFETIME_VERSION; import static com.android.networkstack.util.NetworkStackUtils.createInet6AddressFromEui64; import static com.android.networkstack.util.NetworkStackUtils.macAddressToEui64; import static com.android.server.util.PermissionUtil.enforceNetworkStackCallingPermission; @@ -705,6 +706,7 @@ private final boolean mEnableIpClientIgnoreLowRaLifetime; private final boolean mApfShouldHandleLightDoze; private final boolean mEnableApfPollingCounters; + private final boolean mPopulateLinkAddressLifetime; private InterfaceParams mInterfaceParams; @@ -936,17 +938,21 @@ mApfCounterPollingIntervalMs = mDependencies.getDeviceConfigPropertyInt( CONFIG_APF_COUNTER_POLLING_INTERVAL_SECS, DEFAULT_APF_COUNTER_POLLING_INTERVAL_SECS) * DateUtils.SECOND_IN_MILLIS; - mUseNewApfFilter = mDependencies.isFeatureEnabled(context, APF_NEW_RA_FILTER_VERSION); + mUseNewApfFilter = SdkLevel.isAtLeastV() || mDependencies.isFeatureEnabled(context, + APF_NEW_RA_FILTER_VERSION); mEnableApfPollingCounters = mDependencies.isFeatureEnabled(context, APF_POLLING_COUNTERS_VERSION); - mEnableIpClientIgnoreLowRaLifetime = mDependencies.isFeatureEnabled(context, - IPCLIENT_IGNORE_LOW_RA_LIFETIME_VERSION); + mEnableIpClientIgnoreLowRaLifetime = + SdkLevel.isAtLeastV() || mDependencies.isFeatureEnabled(context, + IPCLIENT_IGNORE_LOW_RA_LIFETIME_VERSION); // Light doze mode status checking API is only available at T or later releases. mApfShouldHandleLightDoze = SdkLevel.isAtLeastT() && mDependencies.isFeatureNotChickenedOut( mContext, APF_HANDLE_LIGHT_DOZE_FORCE_DISABLE); + mPopulateLinkAddressLifetime = mDependencies.isFeatureEnabled(context, + IPCLIENT_POPULATE_LINK_ADDRESS_LIFETIME_VERSION); IpClientLinkObserver.Configuration config = new IpClientLinkObserver.Configuration( - mMinRdnssLifetimeSec); + mMinRdnssLifetimeSec, mPopulateLinkAddressLifetime); mLinkObserver = new IpClientLinkObserver( mContext, getHandler(), @@ -2259,13 +2265,6 @@ setIpv6Sysctl(DAD_TRANSMITS, 0 /* dad_transmits */); } } - // Check the feature flag first before reading IPv6 sysctl, which can prevent from - // triggering a potential kernel bug about the sysctl. - // TODO: add unit test to check if the setIpv6Sysctl() is called or not. - if (mEnableIpClientIgnoreLowRaLifetime && mUseNewApfFilter - && mDependencies.hasIpv6Sysctl(mInterfaceName, ACCEPT_RA_MIN_LFT)) { - setIpv6Sysctl(ACCEPT_RA_MIN_LFT, mAcceptRaMinLft); - } return mInterfaceCtrl.setIPv6PrivacyExtensions(true) && mInterfaceCtrl.setIPv6AddrGenModeIfSupported(mConfiguration.mIPv6AddrGenMode) && mInterfaceCtrl.enableIPv6(); @@ -2450,7 +2449,17 @@ } apfConfig.minRdnssLifetimeSec = mMinRdnssLifetimeSec; - apfConfig.acceptRaMinLft = mAcceptRaMinLft; + // Check the feature flag first before reading IPv6 sysctl, which can prevent from + // triggering a potential kernel bug about the sysctl. + // TODO: add unit test to check if the setIpv6Sysctl() is called or not. + if (mEnableIpClientIgnoreLowRaLifetime && mUseNewApfFilter + && mDependencies.hasIpv6Sysctl(mInterfaceName, ACCEPT_RA_MIN_LFT)) { + setIpv6Sysctl(ACCEPT_RA_MIN_LFT, mAcceptRaMinLft); + final Integer acceptRaMinLft = getIpv6Sysctl(ACCEPT_RA_MIN_LFT); + apfConfig.acceptRaMinLft = acceptRaMinLft == null ? 0 : acceptRaMinLft; + } else { + apfConfig.acceptRaMinLft = 0; + } apfConfig.shouldHandleLightDoze = mApfShouldHandleLightDoze; apfConfig.minMetricsSessionDurationMs = mApfCounterPollingIntervalMs; return mDependencies.maybeCreateApfFilter(mContext, apfConfig, mInterfaceParams, @@ -2665,7 +2674,8 @@ isManagedWifiProfile = true; } mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP, new DhcpClient.Configuration(mL2Key, - isUsingPreconnection(), options, isManagedWifiProfile)); + isUsingPreconnection(), options, isManagedWifiProfile, + mConfiguration.mHostnameSetting)); } private boolean hasPermission(String permissionName) { @@ -3258,19 +3268,25 @@ break; case DhcpClient.CMD_CONFIGURE_LINKADDRESS: { - final int leaseDuration = msg.arg1; final LinkAddress ipAddress = (LinkAddress) msg.obj; - // For IPv4 link addresses, there is no concept of preferred/valid lifetimes. - // Populate the ifa_cacheinfo attribute in the netlink message with the DHCP - // lease duration, which is used by the kernel to maintain the validity of the - // IP addresses. - if (NetlinkUtils.sendRtmNewAddressRequest(mInterfaceParams.index, - ipAddress.getAddress(), - (short) ipAddress.getPrefixLength(), - 0 /* flags */, - (byte) RT_SCOPE_UNIVERSE /* scope */, - leaseDuration /* preferred */, - leaseDuration /* valid */)) { + final boolean success; + if (mPopulateLinkAddressLifetime) { + // For IPv4 link addresses, there is no concept of preferred/valid + // lifetimes. Populate the ifa_cacheinfo attribute in the netlink + // message with the DHCP lease duration, which is used by the kernel + // to maintain the validity of the IP addresses. + final int leaseDuration = msg.arg1; + success = NetlinkUtils.sendRtmNewAddressRequest(mInterfaceParams.index, + ipAddress.getAddress(), + (short) ipAddress.getPrefixLength(), + 0 /* flags */, + (byte) RT_SCOPE_UNIVERSE /* scope */, + leaseDuration /* preferred */, + leaseDuration /* valid */); + } else { + success = mInterfaceCtrl.setIPv4Address(ipAddress); + } + if (success) { // Although it's impossible to happen that DHCP client becomes null in // RunningState and then NPE is thrown when it attempts to send a message // on an null object, sometimes it's found during stress tests. If this
diff --git a/src/android/net/ip/IpClientLinkObserver.java b/src/android/net/ip/IpClientLinkObserver.java index 22ee101..738a50b 100644 --- a/src/android/net/ip/IpClientLinkObserver.java +++ b/src/android/net/ip/IpClientLinkObserver.java
@@ -143,9 +143,11 @@ /** Configuration parameters for IpClientLinkObserver. */ public static class Configuration { public final int minRdnssLifetime; + public final boolean populateLinkAddressLifetime; - public Configuration(int minRdnssLifetime) { + public Configuration(int minRdnssLifetime, boolean populateLinkAddressLifetime) { this.minRdnssLifetime = minRdnssLifetime; + this.populateLinkAddressLifetime = populateLinkAddressLifetime; } } @@ -160,7 +162,7 @@ private final Handler mHandler; private final IpClient.Dependencies mDependencies; private final String mClatInterfaceName; - private final MyNetlinkMonitor mNetlinkMonitor; + private final IpClientNetlinkMonitor mNetlinkMonitor; private final boolean mNetlinkEventParsingEnabled; private boolean mClatInterfaceExists; @@ -194,7 +196,7 @@ mDependencies = deps; mNetlinkEventParsingEnabled = deps.isFeatureNotChickenedOut(context, IPCLIENT_PARSE_NETLINK_EVENTS_FORCE_DISABLE); - mNetlinkMonitor = new MyNetlinkMonitor(h, log, mTag); + mNetlinkMonitor = new IpClientNetlinkMonitor(h, log, mTag); mHandler.post(() -> { if (!mNetlinkMonitor.start()) { Log.wtf(mTag, "Fail to start NetlinkMonitor."); @@ -427,10 +429,10 @@ * Simple NetlinkMonitor. Listen for netlink events from kernel. * All methods except the constructor must be called on the handler thread. */ - private class MyNetlinkMonitor extends NetlinkMonitor { + private class IpClientNetlinkMonitor extends NetlinkMonitor { private final Handler mHandler; - MyNetlinkMonitor(Handler h, SharedLog log, String tag) { + IpClientNetlinkMonitor(Handler h, SharedLog log, String tag) { super(h, log, tag, OsConstants.NETLINK_ROUTE, !mNetlinkEventParsingEnabled ? NetlinkConstants.RTMGRP_ND_USEROPT @@ -634,9 +636,13 @@ // The preferred/valid in ifa_cacheinfo expressed in units of seconds, convert // it to milliseconds for deprecationTime or expirationTime used in LinkAddress. - private static long getDeprecationOrExpirationTime( - @Nullable final StructIfacacheInfo cacheInfo, long now, boolean deprecationTime) { - if (cacheInfo == null) return LinkAddress.LIFETIME_UNKNOWN; + // If the experiment flag is not enabled, LinkAddress.LIFETIME_UNKNOWN is retuend, + // the same as before. + private long getDeprecationOrExpirationTime(@Nullable final StructIfacacheInfo cacheInfo, + long now, boolean deprecationTime) { + if (!mConfig.populateLinkAddressLifetime || (cacheInfo == null)) { + return LinkAddress.LIFETIME_UNKNOWN; + } final long lifetime = deprecationTime ? cacheInfo.preferred : cacheInfo.valid; return (lifetime == Integer.toUnsignedLong(INFINITE_LEASE)) ? LinkAddress.LIFETIME_PERMANENT
diff --git a/src/android/net/ip/IpReachabilityMonitor.java b/src/android/net/ip/IpReachabilityMonitor.java index d2b1064..daf9e51 100644 --- a/src/android/net/ip/IpReachabilityMonitor.java +++ b/src/android/net/ip/IpReachabilityMonitor.java
@@ -531,7 +531,12 @@ isNudFailureDueToRoam(), lostProvisioning); if (lostProvisioning) { - final String logMsg = "FAILURE: LOST_PROVISIONING, " + event; + final boolean isOrganicNudFailureAndToBeIgnored = + ((type == NudEventType.NUD_ORGANIC_FAILED_CRITICAL) + && mIgnoreOrganicNudFailure); + final String logMsg = "FAILURE: LOST_PROVISIONING, " + event + + ", NUD event type: " + type.name() + + (isOrganicNudFailureAndToBeIgnored ? ", to be ignored" : ""); Log.w(TAG, logMsg); // TODO: remove |ip| when the callback signature no longer has // an InetAddress argument. @@ -539,7 +544,7 @@ // are not from kernel organic or the NUD failure event type is // NUD_ORGANIC_FAILED_CRITICAL but the experiment flag is not // enabled. Regardless, the event metrics are still recoreded. - if (type != NudEventType.NUD_ORGANIC_FAILED_CRITICAL || !mIgnoreOrganicNudFailure) { + if (!isOrganicNudFailureAndToBeIgnored) { mCallback.notifyLost(ip, logMsg, type); } }
diff --git a/src/com/android/networkstack/ipmemorystore/IpMemoryStoreService.java b/src/com/android/networkstack/ipmemorystore/IpMemoryStoreService.java index aa7d698..76ed56c 100644 --- a/src/com/android/networkstack/ipmemorystore/IpMemoryStoreService.java +++ b/src/com/android/networkstack/ipmemorystore/IpMemoryStoreService.java
@@ -424,6 +424,12 @@ @Nullable final IOnStatusAndCountListener listener) { mExecutor.execute(() -> { try { + if (null == mDb) { + if (null != listener) { + listener.onComplete(makeStatus(ERROR_DATABASE_CANNOT_BE_OPENED), 0); + } + return; + } final StatusAndCount res = IpMemoryStoreDatabase.delete(mDb, l2Key, needWipe); if (null != listener) listener.onComplete(makeStatus(res.status), res.count); } catch (final RemoteException e) { @@ -450,6 +456,12 @@ @Nullable final IOnStatusAndCountListener listener) { mExecutor.execute(() -> { try { + if (null == mDb) { + if (null != listener) { + listener.onComplete(makeStatus(ERROR_DATABASE_CANNOT_BE_OPENED), 0); + } + return; + } final StatusAndCount res = IpMemoryStoreDatabase.deleteCluster(mDb, cluster, needWipe); if (null != listener) listener.onComplete(makeStatus(res.status), res.count); @@ -464,7 +476,12 @@ */ @Override public void factoryReset() { - mExecutor.execute(() -> IpMemoryStoreDatabase.wipeDataUponNetworkReset(mDb)); + mExecutor.execute(() -> { + if (null == mDb) { + return; + } + IpMemoryStoreDatabase.wipeDataUponNetworkReset(mDb); + }); } /** Get db size threshold. */ @@ -474,6 +491,9 @@ } private long getDbSize() { + if (null == mDb) { + return 0; + } final File dbFile = new File(mDb.getPath()); try { return dbFile.length();
diff --git a/src/com/android/networkstack/netlink/TcpSocketTracker.java b/src/com/android/networkstack/netlink/TcpSocketTracker.java index 658fe8a..0d77dca 100644 --- a/src/com/android/networkstack/netlink/TcpSocketTracker.java +++ b/src/com/android/networkstack/netlink/TcpSocketTracker.java
@@ -792,7 +792,9 @@ * to deal with flag values changing at runtime. */ public boolean shouldIgnoreTcpInfoForBlockedUids() { - return SdkLevel.isAtLeastT() && DeviceConfigUtils.isFeatureSupported( + // Note b/326143935 - can trigger crash due to kernel bug / missing + // feature on some T devices. + return SdkLevel.isAtLeastU() && DeviceConfigUtils.isFeatureSupported( mContext, FEATURE_IS_UID_NETWORKING_BLOCKED) && DeviceConfigUtils.isNetworkStackFeatureNotChickenedOut(mContext, IGNORE_TCP_INFO_FOR_BLOCKED_UIDS);
diff --git a/src/com/android/networkstack/util/NetworkStackUtils.java b/src/com/android/networkstack/util/NetworkStackUtils.java index c2de03c..0cb31fe 100755 --- a/src/com/android/networkstack/util/NetworkStackUtils.java +++ b/src/com/android/networkstack/util/NetworkStackUtils.java
@@ -268,6 +268,14 @@ public static final String NETWORKMONITOR_ASYNC_PRIVDNS_RESOLUTION = "networkmonitor_async_privdns_resolution"; + /** + * Experiment flag to populate the IP link address lifetime such as deprecationTime and + * expirationtTime. + */ + public static final String IPCLIENT_POPULATE_LINK_ADDRESS_LIFETIME_VERSION = + "ipclient_populate_link_address_lifetime_version"; + + /**** BEGIN Feature Kill Switch Flags ****/ /**
diff --git a/src/com/android/server/connectivity/NetworkMonitor.java b/src/com/android/server/connectivity/NetworkMonitor.java index 78a47ea..c62fb90 100755 --- a/src/com/android/server/connectivity/NetworkMonitor.java +++ b/src/com/android/server/connectivity/NetworkMonitor.java
@@ -2092,6 +2092,7 @@ mThread = new Thread(() -> sendMessage(obtainMessage(CMD_PROBE_COMPLETE, token, 0, isCaptivePortal(deps, httpsUrls, httpUrls, fallbackUrl)))); mThread.start(); + mDependencies.onThreadCreated(mThread); } @Override @@ -3205,6 +3206,7 @@ ? new HttpsProbe(properties, proxy, url, captivePortalApiUrl) : new HttpProbe(properties, proxy, url, captivePortalApiUrl); mResult = CaptivePortalProbeResult.failed(probeType); + mDependencies.onThreadCreated(this); } private volatile CaptivePortalProbeResult mResult; @@ -3382,6 +3384,7 @@ // Fixed pool to prevent configuring too many urls to exhaust system resource. final ExecutorService executor = Executors.newFixedThreadPool( Math.min(num, MAX_PROBE_THREAD_POOL_SIZE)); + mDependencies.onExecutorServiceCreated(executor); final CompletionService<CaptivePortalProbeResult> ecs = new ExecutorCompletionService<CaptivePortalProbeResult>(executor); final Uri capportApiUrl = getCaptivePortalApiUrl(mLinkProperties); @@ -3726,6 +3729,24 @@ DataStallStatsUtils.write(stats, result); } + /** + * Callback to be called when a probing thread instance is created. + * + * This method is designed for overriding in test classes to collect + * created threads and waits for the termination. + */ + public void onThreadCreated(@NonNull Thread thread) { + } + + /** + * Callback to be called when a ExecutorService instance is created. + * + * This method is designed for overriding in test classes to collect + * created threads and waits for the termination. + */ + public void onExecutorServiceCreated(@NonNull ExecutorService ecs) { + } + public static final Dependencies DEFAULT = new Dependencies(); }
diff --git a/tests/hostlib/Android.bp b/tests/hostlib/Android.bp index 189a88c..2ebcb3e 100644 --- a/tests/hostlib/Android.bp +++ b/tests/hostlib/Android.bp
@@ -15,6 +15,7 @@ // package { + default_team: "trendy_team_fwk_core_networking", default_applicable_licenses: ["Android-Apache-2.0"], }
diff --git a/tests/integration/Android.bp b/tests/integration/Android.bp index ec058d7..65a94f3 100644 --- a/tests/integration/Android.bp +++ b/tests/integration/Android.bp
@@ -15,6 +15,7 @@ // package { + default_team: "trendy_team_fwk_core_networking", default_applicable_licenses: ["Android-Apache-2.0"], } @@ -68,9 +69,6 @@ static_libs: [ "NetworkStackApiStableLib", ], - lint: { - baseline_filename: "lint-baseline.xml", - }, } // Network stack integration tests. @@ -87,9 +85,6 @@ jarjar_rules: ":NetworkStackJarJarRules", host_required: ["net-tests-utils-host-common"], test_config_template: "AndroidTestTemplate_Integration.xml", - lint: { - baseline_filename: "lint-baseline.xml", - }, } // Network stack next integration tests. @@ -113,9 +108,6 @@ jarjar_rules: ":NetworkStackJarJarRules", host_required: ["net-tests-utils-host-common"], test_config_template: "AndroidTestTemplate_Integration.xml", - lint: { - baseline_filename: "lint-baseline.xml", - }, } // Network stack integration root tests. @@ -142,9 +134,6 @@ jarjar_rules: ":NetworkStackJarJarRules", host_required: ["net-tests-utils-host-common"], test_config_template: "AndroidTestTemplate_Integration.xml", - lint: { - baseline_filename: "lint-baseline.xml", - }, } // Special version of the network stack tests that includes all tests necessary for code coverage @@ -172,7 +161,4 @@ compile_multilib: "both", manifest: "AndroidManifest_coverage.xml", jarjar_rules: ":NetworkStackJarJarRules", - lint: { - baseline_filename: "lint-baseline.xml", - }, }
diff --git a/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java b/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java index 72a6b4d..06b0ca2 100644 --- a/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java +++ b/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java
@@ -169,6 +169,7 @@ import android.net.shared.Layer2Information; import android.net.shared.ProvisioningConfiguration; import android.net.shared.ProvisioningConfiguration.ScanResultInfo; +import android.net.util.HostnameTransliterator; import android.os.Build; import android.os.Handler; import android.os.HandlerThread; @@ -178,6 +179,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; +import android.provider.Settings; import android.stats.connectivity.NudEventType; import android.system.ErrnoException; import android.system.Os; @@ -306,10 +308,10 @@ protected static final long TEST_TIMEOUT_MS = 2_000L; private static final long TEST_WAIT_ENOBUFS_TIMEOUT_MS = 30_000L; private static final long TEST_WAIT_RENEW_REBIND_RETRANSMIT_MS = 15_000L; - // To prevent the flakiness about deprecationTime and expirationTime check, +/- 2s tolerance + // To prevent the flakiness about deprecationTime and expirationTime check, +/- 4s tolerance // should be enough between the timestamp when the IP provisioning completes successfully and // when IpClientLinkObserver sees the RTM_NEWADDR netlink events. - private static final long TEST_LIFETIME_TOLERANCE_MS = 2_000L; + private static final long TEST_LIFETIME_TOLERANCE_MS = 4_000L; @Rule public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(); @@ -598,13 +600,13 @@ } @Override - public boolean getSendHostnameOption(final Context context) { + public boolean getSendHostnameOverlaySetting(final Context context) { return mIsHostnameConfigurationEnabled; } @Override public String getDeviceName(final Context context) { - return mIsHostnameConfigurationEnabled ? mHostname : null; + return mHostname; } }; } @@ -746,6 +748,10 @@ setFeatureEnabled(NetworkStackUtils.IPCLIENT_DHCPV6_PREFIX_DELEGATION_VERSION, true /* isDhcp6PrefixDelegationEnabled */); + // Enable populating the IP Link Address lifetime. + setFeatureEnabled(NetworkStackUtils.IPCLIENT_POPULATE_LINK_ADDRESS_LIFETIME_VERSION, + true /* enabled */); + setUpTapInterface(); // It turns out that Router Solicitation will also be sent out even after the tap interface // is brought up, however, we want to wait for RS which is sent due to IPv6 stack is enabled @@ -1255,14 +1261,14 @@ assertIpMemoryNeverStoreNetworkAttributes(TEST_L2KEY, TEST_TIMEOUT_MS); } - private void assertHostname(final boolean isHostnameConfigurationEnabled, + private void assertHostname(final boolean expectSendHostname, final String hostname, final String hostnameAfterTransliteration, final List<DhcpPacket> packetList) throws Exception { for (DhcpPacket packet : packetList) { - if (!isHostnameConfigurationEnabled || hostname == null) { + if (!expectSendHostname || hostname == null) { assertNoHostname(packet.getHostname()); } else { - assertEquals(packet.getHostname(), hostnameAfterTransliteration); + assertEquals(hostnameAfterTransliteration, packet.getHostname()); } } } @@ -5849,4 +5855,90 @@ } } } + + private void doDhcpHostnameSettingTest(int hostnameSetting, + boolean isHostnameConfigurationEnabled, boolean expectSendHostname) throws Exception { + final ProvisioningConfiguration cfg = new ProvisioningConfiguration.Builder() + .withoutIPv6() + .withHostnameSetting(hostnameSetting) + .build(); + final String expectedHostname; + final String expectedHostnameAfterTransliteration; + if (mDependencies != null) { + mDependencies.setHostnameConfiguration(isHostnameConfigurationEnabled, + TEST_HOST_NAME); + expectedHostname = TEST_HOST_NAME; + expectedHostnameAfterTransliteration = TEST_HOST_NAME_TRANSLITERATION; + } else { + expectedHostname = Settings.Global.getString( + InstrumentationRegistry.getInstrumentation().getContext().getContentResolver(), + Settings.Global.DEVICE_NAME); + expectedHostnameAfterTransliteration = new HostnameTransliterator() + .transliterate(expectedHostname); + } + startIpClientProvisioning(cfg); + + // perform DHCP handshake and capture the packets sent from client such as + // DHCPDISCOVER and DHCPREQUEST. + final List<DhcpPacket> sentPackets = handleDhcpPackets(true /* isSuccessLease */, + DhcpPacket.INFINITE_LEASE, + false /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU, + null /* captivePortalApiUrl */, null /* ipv6OnlyWaitTime */, + null /* domainName */, null /* domainSearchList */); + + // check if the DHCP packet sent from the client takes a hostname option per different + // configs. Do not consider the null hostname case. + assertHostname(expectSendHostname, expectedHostname, expectedHostnameAfterTransliteration, + sentPackets); + } + + @Test + @SignatureRequiredTest(reason = "need to mock setHostnameConfiguration") + public void testHostname_hostnameSettingUnset_enableHostnameConfig() throws Exception { + // If hostname setting is unset but legacy hostname overlay config is enabled, + // we expect that the DHCP packet takes a hostname option. + doDhcpHostnameSettingTest(IIpClient.HOSTNAME_SETTING_UNSET, + true /* isHostnameConfigurationEnabled */, true /* expectSendHostname */); + } + + @Test + @SignatureRequiredTest(reason = "need to mock setHostnameConfiguration") + public void testHostname_hostnameSettingUnset_disableHostnameConfig() throws Exception { + // If hostname setting is unset and legacy hostname overlay config is disabled, + // we expect that the DHCP packet doesn't take a hostname option. + doDhcpHostnameSettingTest(IIpClient.HOSTNAME_SETTING_UNSET, + false /* isHostnameConfigurationEnabled */, false /* expectSendHostname */); + } + + @Test + public void testHostname_hostnameSettingSend_enableHostnameConfig() throws Exception { + // If hostname setting is set and legacy hostname overlay config is enabled, + // we expect that the DHCP packet takes a hostname option. + doDhcpHostnameSettingTest(IIpClient.HOSTNAME_SETTING_SEND, + true /* isHostnameConfigurationEnabled */, true /* expectSendHostname */); + } + + @Test + public void testHostname_hostnameSettingSend_disableHostnameConfig() throws Exception { + // If hostname setting is set and legacy hostname overlay config is disabled, + // we still expect that the DHCP packet takes a hostname option. + doDhcpHostnameSettingTest(IIpClient.HOSTNAME_SETTING_SEND, + false /* isHostnameConfigurationEnabled */, true /* expectSendHostname */); + } + + @Test + public void testHostname_hostnameSettingNotSend_enableHostnameConfig() throws Exception { + // If hostname setting is not send and even if legacy hostname overlay config is + // enabled, we expect that the DHCP packet doesn't take a hostname option. + doDhcpHostnameSettingTest(IIpClient.HOSTNAME_SETTING_DO_NOT_SEND, + true /* isHostnameConfigurationEnabled */, false /* expectSendHostname */); + } + + @Test + public void testHostname_hostnameSettingNotSend_disableHostnameConfig() throws Exception { + // If hostname setting is not send and even if legacy hostname overlay config is + // disabled, we expect that the DHCP packet doesn't take a hostname option. + doDhcpHostnameSettingTest(IIpClient.HOSTNAME_SETTING_DO_NOT_SEND, + false /* isHostnameConfigurationEnabled */, false /* expectSendHostname */); + } }
diff --git a/tests/integration/lint-baseline.xml b/tests/integration/lint-baseline.xml deleted file mode 100644 index edc9dac..0000000 --- a/tests/integration/lint-baseline.xml +++ /dev/null
@@ -1,44 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `android.net.LinkProperties#getDhcpServerAddress`"> - <location - file="packages/modules/NetworkStack/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java" - line="1574"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `android.net.LinkProperties#getNat64Prefix`"> - <location - file="packages/modules/NetworkStack/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java" - line="2014"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `android.net.LinkProperties#getNat64Prefix`"> - <location - file="packages/modules/NetworkStack/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java" - line="2020"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `android.net.LinkProperties#getNat64Prefix`"> - <location - file="packages/modules/NetworkStack/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java" - line="2050"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `android.net.LinkProperties#getNat64Prefix`"> - <location - file="packages/modules/NetworkStack/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java" - line="2051"/> - </issue> - -</issues> \ No newline at end of file
diff --git a/tests/integration/signature/android/net/NetworkStatsIntegrationTest.kt b/tests/integration/signature/android/net/NetworkStatsIntegrationTest.kt index 2e52e3f..6c56add 100644 --- a/tests/integration/signature/android/net/NetworkStatsIntegrationTest.kt +++ b/tests/integration/signature/android/net/NetworkStatsIntegrationTest.kt
@@ -28,15 +28,12 @@ import android.net.NetworkStatsIntegrationTest.Direction.UPLOAD import android.net.NetworkTemplate.MATCH_TEST import android.os.Build -import android.os.ParcelFileDescriptor.AutoCloseInputStream import android.os.Process -import android.util.Log import androidx.test.platform.app.InstrumentationRegistry import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo import com.android.testutils.DevSdkIgnoreRunner import com.android.testutils.PacketBridge import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged -import com.android.testutils.SkipPresubmit import com.android.testutils.TestDnsServer import com.android.testutils.TestHttpServer import com.android.testutils.TestableNetworkCallback @@ -44,7 +41,6 @@ import fi.iki.elonen.NanoHTTPD import java.io.BufferedInputStream import java.io.BufferedOutputStream -import java.io.BufferedReader import java.net.HttpURLConnection import java.net.HttpURLConnection.HTTP_OK import java.net.InetSocketAddress @@ -52,7 +48,6 @@ import java.nio.charset.Charset import kotlin.math.ceil import kotlin.test.assertEquals -import kotlin.test.assertNotNull import kotlin.test.assertTrue import org.junit.After import org.junit.Assume.assumeTrue @@ -82,7 +77,9 @@ 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 HTTP_SERVER_PORT = 8080 // Use port > 1024 to avoid restrictions on system ports + private val DNS_INTERNAL_SERVER_PORT = 53 + private val DNS_EXTERNAL_SERVER_PORT = 1053 private val TCP_ACK_SIZE = 72 // Packet overheads that are not part of the actual data transmission, these @@ -99,13 +96,21 @@ private val inst = InstrumentationRegistry.getInstrumentation() private val context = inst.getContext() private val packetBridge = runAsShell(MANAGE_TEST_NETWORKS) { - PacketBridge(context, listOf(LOCAL_V6ADDR), REMOTE_V6ADDR.address) + PacketBridge( + context, + listOf(LOCAL_V6ADDR), + REMOTE_V6ADDR.address, + listOf( + Pair(DNS_INTERNAL_SERVER_PORT, DNS_EXTERNAL_SERVER_PORT) + ) + ) } private val cm = context.getSystemService(ConnectivityManager::class.java)!! // Set up DNS server for testing server and DNS64. private val fakeDns = TestDnsServer( - packetBridge.externalNetwork, InetSocketAddress(LOCAL_V6ADDR.address, DNS_SERVER_PORT) + packetBridge.externalNetwork, + InetSocketAddress(LOCAL_V6ADDR.address, DNS_EXTERNAL_SERVER_PORT) ).apply { start() setAnswer( @@ -116,7 +121,10 @@ } // Start up test http server. - private val httpServer = TestHttpServer(LOCAL_V6ADDR.address.hostAddress).apply { + private val httpServer = TestHttpServer( + LOCAL_V6ADDR.address.hostAddress, + HTTP_SERVER_PORT + ).apply { start() } @@ -182,7 +190,6 @@ * While the packets are being forwarded to the external interface, the servers will see * the packets originated from the mocked v6 address, and destined to a local v6 address. */ - @SkipPresubmit(reason = "Out of SLO flakiness") @Test fun test464XlatTcpStats() { // Wait for 464Xlat to be ready. @@ -384,7 +391,6 @@ val taggedUid = getUidDetail(iface, TEST_TAG) val trafficStatsIface = getTrafficStatsIface(iface) val trafficStatsUid = getTrafficStatsUid(Process.myUid()) - val xtBpfStats = getXtBpfStatsInternal() private fun getUidDetail(iface: String, tag: Int): BareStats { return getNetworkStatsThat(iface, tag) { nsm, template -> @@ -451,40 +457,6 @@ TrafficStats.getUidTxBytes(uid), TrafficStats.getUidTxPackets(uid) ) - - private fun getXtBpfStatsInternal(): BareStats { - // The following pattern matches ip(6)tables-save -c output like below: - // [119:37802] -A bw_raw_PREROUTING -m bpf --object-pinned - // /sys/fs/bpf/netd_shared/prog_netd_skfilter_ingress_xtbpf - // [141:26439] -A bw_mangle_POSTROUTING -m bpf --object-pinned - // /sys/fs/bpf/netd_shared/prog_netd_skfilter_egress_xtbpf - val ingressRegex = Regex("""\[(?<rxPackets>\d+):(?<rxBytes>\d+)\]""" + - """.*prog_netd_skfilter_ingress_xtbpf""") - val egressRegex = Regex("""\[(?<txPackets>\d+):(?<txBytes>\d+)\]""" + - """.*prog_netd_skfilter_egress_xtbpf""") - val (v4Stats, v6Stats) = listOf("iptables-save -c", "ip6tables-save -c").map { - val output = runShellCommand(it) - val rxMatches = ingressRegex.find(output) - val txMatches = egressRegex.find(output) - assertNotNull(rxMatches) - assertNotNull(txMatches) - - BareStats( - rxBytes = rxMatches.groups["rxBytes"]!!.value.toLong(), - rxPackets = rxMatches.groups["rxPackets"]!!.value.toLong(), - txBytes = txMatches.groups["txBytes"]!!.value.toLong(), - txPackets = txMatches.groups["txPackets"]!!.value.toLong() - ) - } - return v4Stats.plus(v6Stats) - } - - private fun runShellCommand(cmd: String): String { - return InstrumentationRegistry.getInstrumentation().getUiAutomation() - .executeShellCommand(cmd).use { pfd -> - AutoCloseInputStream(pfd).bufferedReader().use(BufferedReader::readText) - } - } } private fun assertAllStatsIncreases( @@ -513,15 +485,6 @@ lower: BareStats, upper: BareStats ) { - // XtBpf iptables hook counted traffic on all interfaces. Thus, this might see traffic - // on other interfaces as well. Also, other thread/process could reload the relevant - // iptables table. Thus, instead of asserting the readings, print logs when it is - // unexpected to provide more debug information when failing other items. - if (!checkInRange(before.xtBpfStats, after.xtBpfStats, - lower + lower.reverse(), upper + upper.reverse())) { - Log.d(TAG, "Unexpected xtbpf stats: ${after.xtBpfStats} - ${before.xtBpfStats} " + - "is not within range [$lower, $upper]") - } assertInRange( "Unexpected iface traffic stats", after.iface,
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp index ea29714..88d995f 100644 --- a/tests/unit/Android.bp +++ b/tests/unit/Android.bp
@@ -15,6 +15,7 @@ // package { + default_team: "trendy_team_fwk_core_networking", default_applicable_licenses: ["Android-Apache-2.0"], } @@ -69,9 +70,6 @@ static_libs: ["NetworkStackApiCurrentLib"], compile_multilib: "both", // Workaround for b/147785146 for mainline-presubmit jarjar_rules: ":NetworkStackJarJarRules", - lint: { - baseline_filename: "lint-baseline.xml", - }, } // Library containing the unit tests. This is used by the coverage test target to pull in the @@ -84,7 +82,7 @@ static_libs: ["NetworkStackApiStableLib"], lint: { test: true, - baseline_filename: "lint-baseline.xml", + }, visibility: [ "//packages/modules/NetworkStack/tests/integration", @@ -107,9 +105,6 @@ static_libs: ["NetworkStackApiStableLib"], compile_multilib: "both", jarjar_rules: ":NetworkStackJarJarRules", - lint: { - baseline_filename: "lint-baseline.xml", - }, } // Additional dependencies of libnetworkstackutilsjni that are not provided by the system when
diff --git a/tests/unit/jni/Android.bp b/tests/unit/jni/Android.bp index 50576cc..428dacc 100644 --- a/tests/unit/jni/Android.bp +++ b/tests/unit/jni/Android.bp
@@ -15,13 +15,14 @@ // package { + default_team: "trendy_team_fwk_core_networking", default_applicable_licenses: ["Android-Apache-2.0"], } cc_library_shared { name: "libnetworkstacktestsjni", srcs: [ - "**/*.cpp" + "**/*.cpp", ], cflags: [ "-Wall",
diff --git a/tests/unit/jni/apf_jni.cpp b/tests/unit/jni/apf_jni.cpp index 39dd2c2..1889a89 100644 --- a/tests/unit/jni/apf_jni.cpp +++ b/tests/unit/jni/apf_jni.cpp
@@ -32,12 +32,12 @@ #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #define LOG_TAG "NetworkStackUtils-JNI" -static int run_apf_interpreter(int apf_version, uint8_t* program, +static int run_apf_interpreter(int apf_version, uint32_t* program, uint32_t program_len, uint32_t ram_len, const uint8_t* packet, uint32_t packet_len, uint32_t filter_age) { if (apf_version == 4) { - return accept_packet(program, program_len, ram_len, packet, packet_len, + return accept_packet((uint8_t*)program, program_len, ram_len, packet, packet_len, filter_age); } else { return apf_run(nullptr, program, program_len, ram_len, packet, packet_len, @@ -55,23 +55,30 @@ uint32_t packet_len = (uint32_t)packet.size(); uint32_t program_len = env->GetArrayLength(jprogram); uint32_t data_len = jdata ? env->GetArrayLength(jdata) : 0; - std::vector<uint8_t> buf(program_len + data_len, 0); + // we need to guarantee room for APFv6's 5 u32 counters (20 bytes) + // and we need to make sure ram_len is a multiple of 4 bytes, + // so that the counters (which are indexed from the back are aligned. + uint32_t ram_len = program_len + data_len; + if (apf_version > 4) { + ram_len += 3; ram_len &= ~3; + if (data_len < 20) ram_len += 20; + } + std::vector<uint32_t> buf((ram_len + 3) / 4, 0); + jbyte* jbuf = reinterpret_cast<jbyte*>(buf.data()); - env->GetByteArrayRegion(jprogram, 0, program_len, reinterpret_cast<jbyte*>(buf.data())); + env->GetByteArrayRegion(jprogram, 0, program_len, jbuf); if (jdata) { // Merge program and data into a single buffer. - env->GetByteArrayRegion(jdata, 0, data_len, - reinterpret_cast<jbyte*>(buf.data() + program_len)); + env->GetByteArrayRegion(jdata, 0, data_len, jbuf + ram_len - data_len); } jint result = run_apf_interpreter( - apf_version, buf.data(), program_len, program_len + data_len, + apf_version, buf.data(), program_len, ram_len, reinterpret_cast<const uint8_t *>(packet.get()), packet_len, filter_age); if (jdata) { - env->SetByteArrayRegion(jdata, 0, data_len, - reinterpret_cast<jbyte*>(buf.data() + program_len)); + env->SetByteArrayRegion(jdata, 0, data_len, jbuf + ram_len - data_len); } return result; @@ -141,7 +148,13 @@ jstring jpcap_filename, jbyteArray japf_program) { ScopedUtfChars filter(env, jfilter); ScopedUtfChars pcap_filename(env, jpcap_filename); - ScopedByteArrayRO apf_program(env, japf_program); + uint32_t program_len = env->GetArrayLength(japf_program); + uint32_t data_len = (apf_version > 4) ? 20 : 0; + uint32_t ram_len = program_len + data_len; + if (apf_version > 4) { ram_len += 3; ram_len &= ~3; } + std::vector<uint32_t> apf_program((ram_len + 3) / 4, 0); + env->GetByteArrayRegion(japf_program, 0, program_len, + reinterpret_cast<jbyte*>(apf_program.data())); // Open pcap file for BPF filtering ScopedFILE bpf_fp(fopen(pcap_filename.c_str(), "rb")); @@ -183,8 +196,7 @@ do { apf_packet = pcap_next(apf_pcap.get(), &apf_header); } while (apf_packet != NULL && !run_apf_interpreter(apf_version, - reinterpret_cast<uint8_t*>(const_cast<int8_t*>(apf_program.get())), - apf_program.size(), 0 /* data_len */, + apf_program.data(), program_len, ram_len, apf_packet, apf_header.len, 0 /* filter_age */)); // Make sure both filters matched the same packet. @@ -208,15 +220,20 @@ ScopedByteArrayRO apf_program(env, jprogram); uint32_t apf_program_len = (uint32_t)apf_program.size(); uint32_t data_len = env->GetArrayLength(jdata); + uint32_t ram_len = apf_program_len + data_len; + if (apf_version > 4) { + ram_len += 3; ram_len &= ~3; + if (data_len < 20) ram_len += 20; + } pcap_pkthdr apf_header; const uint8_t* apf_packet; char pcap_error[PCAP_ERRBUF_SIZE]; - std::vector<uint8_t> buf(apf_program_len + data_len, 0); + std::vector<uint32_t> buf((ram_len + 3) / 4, 0); + jbyte* jbuf = reinterpret_cast<jbyte*>(buf.data()); // Merge program and data into a single buffer. - env->GetByteArrayRegion(jprogram, 0, apf_program_len, reinterpret_cast<jbyte*>(buf.data())); - env->GetByteArrayRegion(jdata, 0, data_len, - reinterpret_cast<jbyte*>(buf.data() + apf_program_len)); + env->GetByteArrayRegion(jprogram, 0, apf_program_len, jbuf); + env->GetByteArrayRegion(jdata, 0, data_len, jbuf + ram_len - data_len); // Open pcap file ScopedFILE apf_fp(fopen(pcap_filename.c_str(), "rb")); @@ -229,19 +246,16 @@ while ((apf_packet = pcap_next(apf_pcap.get(), &apf_header)) != NULL) { int result = run_apf_interpreter( - apf_version, buf.data(), apf_program_len, - apf_program_len + data_len, apf_packet, apf_header.len, 0); + apf_version, buf.data(), apf_program_len, ram_len, apf_packet, apf_header.len, 0); // Return false once packet passes the filter if (result) { - env->SetByteArrayRegion(jdata, 0, data_len, - reinterpret_cast<jbyte*>(buf.data() + apf_program_len)); + env->SetByteArrayRegion(jdata, 0, data_len, jbuf + ram_len - data_len); return false; } } - env->SetByteArrayRegion(jdata, 0, data_len, - reinterpret_cast<jbyte*>(buf.data() + apf_program_len)); + env->SetByteArrayRegion(jdata, 0, data_len, jbuf + ram_len - data_len); return true; }
diff --git a/tests/unit/lint-baseline.xml b/tests/unit/lint-baseline.xml deleted file mode 100644 index f8b1c29..0000000 --- a/tests/unit/lint-baseline.xml +++ /dev/null
@@ -1,60 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `android.net.NetworkCapabilities()`"> - <location - file="packages/modules/NetworkStack/tests/unit/src/com/android/networkstack/NetworkStackNotifierTest.kt" - line="136"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `android.net.NetworkCapabilities()`"> - <location - file="packages/modules/NetworkStack/tests/unit/src/com/android/networkstack/NetworkStackNotifierTest.kt" - line="137"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `new android.net.NetworkCapabilities`"> - <location - file="packages/modules/NetworkStack/tests/unit/src/com/android/networkstack/metrics/NetworkValidationMetricsTest.java" - line="57"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `new android.net.NetworkCapabilities`"> - <location - file="packages/modules/NetworkStack/tests/unit/src/com/android/networkstack/metrics/NetworkValidationMetricsTest.java" - line="109"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `new android.net.NetworkCapabilities`"> - <location - file="packages/modules/NetworkStack/tests/unit/src/com/android/networkstack/metrics/NetworkValidationMetricsTest.java" - line="117"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `new android.net.NetworkCapabilities`"> - <location - file="packages/modules/NetworkStack/tests/unit/src/com/android/networkstack/metrics/NetworkValidationMetricsTest.java" - line="123"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 30 (current min is 29): `new android.net.NetworkCapabilities`"> - <location - file="packages/modules/NetworkStack/tests/unit/src/com/android/networkstack/metrics/NetworkValidationMetricsTest.java" - line="129"/> - </issue> - -</issues> \ No newline at end of file
diff --git a/tests/unit/src/android/net/apf/ApfTest.java b/tests/unit/src/android/net/apf/ApfTest.java index 5c6a906..29a5045 100644 --- a/tests/unit/src/android/net/apf/ApfTest.java +++ b/tests/unit/src/android/net/apf/ApfTest.java
@@ -16,11 +16,11 @@ package android.net.apf; -import static android.net.apf.ApfV4Generator.APF_VERSION_4; -import static android.net.apf.ApfV4Generator.DROP_LABEL; -import static android.net.apf.ApfV4Generator.PASS_LABEL; -import static android.net.apf.ApfV4Generator.Register.R0; -import static android.net.apf.ApfV4Generator.Register.R1; +import static android.net.apf.BaseApfGenerator.APF_VERSION_4; +import static android.net.apf.BaseApfGenerator.DROP_LABEL; +import static android.net.apf.BaseApfGenerator.PASS_LABEL; +import static android.net.apf.BaseApfGenerator.Register.R0; +import static android.net.apf.BaseApfGenerator.Register.R1; import static android.net.apf.ApfJniUtils.compareBpfApf; import static android.net.apf.ApfJniUtils.compileToBpf; import static android.net.apf.ApfJniUtils.dropsAllPackets; @@ -70,7 +70,7 @@ import android.net.apf.ApfTestUtils.MockIpClientCallback; import android.net.apf.ApfTestUtils.TestApfFilter; import android.net.apf.ApfTestUtils.TestLegacyApfFilter; -import android.net.apf.ApfV4Generator.IllegalInstructionException; +import android.net.apf.BaseApfGenerator.IllegalInstructionException; import android.net.metrics.IpConnectivityLog; import android.os.Build; import android.os.PowerManager; @@ -263,6 +263,12 @@ ApfV4Generator gen = new ApfV4Generator(MIN_APF_VERSION); assertPass(gen); + // Test pass opcode + gen = new ApfV4Generator(MIN_APF_VERSION); + gen.addPass(); + gen.addJump(DROP_LABEL); + assertPass(gen); + // Test jumping to pass label. gen = new ApfV4Generator(MIN_APF_VERSION); gen.addJump(PASS_LABEL); @@ -871,9 +877,16 @@ 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. + // 3 instructions, all normal opcodes (LI, LDDW, JMP) with 1 byte immediate = 6 byte program + // 32 byte data length, for a total of 38 byte ram len. + // APFv6 needs to round this up to be a multiple of 4, so 40. gen = new ApfV4Generator(APF_VERSION_4); gen.addLoadImmediate(R0, 20); - gen.addLoadData(R1, 15); // 20 + 15 > 32 + if (mApfVersion == 4) { + gen.addLoadData(R1, 15); // R0(20)+15+U32[0..3] >= 6 prog + 32 data, so invalid + } else { + gen.addLoadData(R1, 17); // R0(20)+17+U32[0..3] >= 6 prog + 2 pad + 32 data, so invalid + } gen.addJump(DROP_LABEL); // Not reached. assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data); @@ -2795,6 +2808,70 @@ apfFilter.shutdown(); } + @Test + public void testProcessRaWithInfiniteLifeTimeWithoutCrash() throws Exception { + final MockIpClientCallback ipClientCallback = new MockIpClientCallback(); + // configure accept_ra_min_lft + final ApfConfiguration config = getDefaultConfig(); + config.acceptRaMinLft = 180; + TestApfFilter apfFilter; + // Template packet: + // Frame 1: 150 bytes on wire (1200 bits), 150 bytes captured (1200 bits) + // Ethernet II, Src: Netgear_23:67:2c (28:c6:8e:23:67:2c), Dst: IPv6mcast_01 (33:33:00:00:00:01) + // Internet Protocol Version 6, Src: fe80::2ac6:8eff:fe23:672c, Dst: ff02::1 + // Internet Control Message Protocol v6 + // Type: Router Advertisement (134) + // Code: 0 + // Checksum: 0x0acd [correct] + // Checksum Status: Good + // Cur hop limit: 64 + // Flags: 0xc0, Managed address configuration, Other configuration, Prf (Default Router Preference): Medium + // Router lifetime (s): 7000 + // Reachable time (ms): 0 + // Retrans timer (ms): 0 + // ICMPv6 Option (Source link-layer address : 28:c6:8e:23:67:2c) + // Type: Source link-layer address (1) + // Length: 1 (8 bytes) + // Link-layer address: Netgear_23:67:2c (28:c6:8e:23:67:2c) + // Source Link-layer address: Netgear_23:67:2c (28:c6:8e:23:67:2c) + // ICMPv6 Option (MTU : 1500) + // Type: MTU (5) + // Length: 1 (8 bytes) + // Reserved + // MTU: 1500 + // ICMPv6 Option (Prefix information : 2401:fa00:480:f000::/64) + // Type: Prefix information (3) + // Length: 4 (32 bytes) + // Prefix Length: 64 + // Flag: 0xc0, On-link flag(L), Autonomous address-configuration flag(A) + // Valid Lifetime: Infinity (4294967295) + // Preferred Lifetime: Infinity (4294967295) + // Reserved + // Prefix: 2401:fa00:480:f000:: + // ICMPv6 Option (Recursive DNS Server 2401:fa00:480:f000::1) + // Type: Recursive DNS Server (25) + // Length: 3 (24 bytes) + // Reserved + // Lifetime: 7000 + // Recursive DNS Servers: 2401:fa00:480:f000::1 + // ICMPv6 Option (Advertisement Interval : 600000) + // Type: Advertisement Interval (7) + // Length: 1 (8 bytes) + // Reserved + // Advertisement Interval: 600000 + final String packetStringFmt = "33330000000128C68E23672C86DD60054C6B00603AFFFE800000000000002AC68EFFFE23672CFF02000000000000000000000000000186000ACD40C01B580000000000000000010128C68E23672C05010000000005DC030440C0%s000000002401FA000480F00000000000000000001903000000001B582401FA000480F000000000000000000107010000000927C0"; + final List<String> lifetimes = List.of("FFFFFFFF", "00000000", "00000001", "00001B58"); + for (String lifetime : lifetimes) { + apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mNetworkQuirkMetrics); + final byte[] ra = hexStringToByteArray( + String.format(packetStringFmt, lifetime + lifetime)); + // feed the RA into APF and generate the filter, the filter shouldn't crash. + apfFilter.pretendPacketReceived(ra); + ipClientCallback.assertProgramUpdateAndGet(); + apfFilter.shutdown(); + } + } + // Test for go/apf-ra-filter Case 1a. // Old lifetime is 0 @Test @@ -3821,4 +3898,183 @@ final String referenceProgramHexString = "6bfcb03a01b8120c6b9494014a06006b907c014388a27c013e88a47c013988b87c013488cd7c012f88e17c012a88e384003f08066a0e6bdca40110000600010800060412147a1c016bd884010400021a1c6b8c7c01010000686bd4a2ef06ffffffffffff6a266bbca2ea04c0a801be6bf872e0120c84008d08000a17821e1112149c00171fffab0d2a108210446a3239a20406ea42226789c06bf472b60a1e52f06bac7ab3e06bb41a1e7e000000a6ffffffff6bb07e0000009bc0a801ff0a178230116a1aa223086b7a1f1fc0a801beaa0d3a08aa221210ab2139821501aa0d3a0ea20a041194ceca3a08a20401ff6b8072666be868a25406ffffffffffff6bb872566bf0724c7c001086dd686bd0a23b06ffffffffffff6bc8723d0a147a32007a0b3a6b980a267a2eff6be072240a366ba87a23858218886a26a2040fff02000000000000000000000000006ba472086be4b03a01b87206b03a01b87201"; assertEquals(referenceProgramHexString, programString); } + + @Test + public void testInfiniteLifetimeFullApfV4ProgramGeneration() throws IllegalInstructionException { + ApfV4Generator gen = new ApfV4Generator(APF_VERSION_4); + gen.addLoadImmediate(R1, -8); + gen.addLoadData(R0, 0); + gen.addAdd(1); + gen.addStoreData(R0, 0); + gen.addLoad16(R0, 12); + gen.addLoadImmediate(R1, -120); + gen.addJumpIfR0LessThan(0x600, "LABEL_582"); + gen.addLoadImmediate(R1, -124); + gen.addJumpIfR0Equals(0x88a2, "LABEL_582"); + gen.addJumpIfR0Equals(0x88a4, "LABEL_582"); + gen.addJumpIfR0Equals(0x88b8, "LABEL_582"); + gen.addJumpIfR0Equals(0x88cd, "LABEL_582"); + gen.addJumpIfR0Equals(0x88e1, "LABEL_582"); + gen.addJumpIfR0Equals(0x88e3, "LABEL_582"); + gen.addJumpIfR0NotEquals(0x806, "LABEL_122"); + gen.addLoadImmediate(R0, 14); + gen.addLoadImmediate(R1, -152); + gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("000108000604"), "LABEL_582"); + gen.addLoad16(R0, 20); + gen.addJumpIfR0Equals(0x1, "LABEL_104"); + gen.addLoadImmediate(R1, -156); + gen.addJumpIfR0NotEquals(0x2, "LABEL_582"); + gen.addLoad32(R0, 28); + gen.addLoadImmediate(R1, -128); + gen.addJumpIfR0Equals(0x0, "LABEL_582"); + gen.addLoadImmediate(R0, 0); + gen.addLoadImmediate(R1, -56); + gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("ffffffffffff"), "LABEL_576"); + + gen.defineLabel("LABEL_104"); + gen.addLoadImmediate(R0, 38); + gen.addLoadImmediate(R1, -80); + gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("c0a801ec"), "LABEL_582"); + gen.addLoadImmediate(R1, -20); + gen.addJump("LABEL_576"); + + gen.defineLabel("LABEL_122"); + gen.addLoad16(R0, 12); + gen.addJumpIfR0NotEquals(0x800, "LABEL_249"); + gen.addLoad8(R0, 23); + gen.addJumpIfR0NotEquals(0x11, "LABEL_165"); + gen.addLoad16(R0, 20); + gen.addJumpIfR0AnyBitsSet(0x1fff, "LABEL_165"); + gen.addLoadFromMemory(R1, 13); + gen.addLoad16Indexed(R0, 16); + gen.addJumpIfR0NotEquals(0x44, "LABEL_165"); + gen.addLoadImmediate(R0, 50); + gen.addAddR1(); + gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("7e9046bc7008"), "LABEL_165"); + gen.addLoadImmediate(R1, -24); + gen.addJump("LABEL_576"); + + gen.defineLabel("LABEL_165"); + gen.addLoad8(R0, 30); + gen.addAnd(240); + gen.addLoadImmediate(R1, -96); + gen.addJumpIfR0Equals(0xe0, "LABEL_582"); + gen.addLoadImmediate(R1, -88); + gen.addLoad32(R0, 30); + gen.addJumpIfR0Equals(0xffffffff, "LABEL_582"); + gen.addLoadImmediate(R1, -92); + gen.addJumpIfR0Equals(0xc0a801ff, "LABEL_582"); + gen.addLoad8(R0, 23); + gen.addJumpIfR0NotEquals(0x6, "LABEL_225"); + gen.addLoad16(R0, 20); + gen.addJumpIfR0AnyBitsSet(0x1fff, "LABEL_225"); + gen.addLoadFromMemory(R1, 13); + gen.addLoad16Indexed(R0, 16); + gen.addJumpIfR0NotEquals(0x7, "LABEL_225"); + gen.addLoadImmediate(R1, -148); + gen.addJump("LABEL_582"); + + gen.defineLabel("LABEL_225"); + gen.addLoadImmediate(R1, -36); + gen.addLoadImmediate(R0, 0); + gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("ffffffffffff"), "LABEL_576"); + gen.addLoadImmediate(R1, -84); + gen.addJump("LABEL_582"); + gen.addLoadImmediate(R1, -28); + gen.addJump("LABEL_576"); + + gen.defineLabel("LABEL_249"); + gen.addJumpIfR0Equals(0x86dd, "LABEL_273"); + gen.addLoadImmediate(R0, 0); + gen.addLoadImmediate(R1, -60); + gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("ffffffffffff"), "LABEL_576"); + gen.addLoadImmediate(R1, -68); + gen.addJump("LABEL_582"); + + gen.defineLabel("LABEL_273"); + gen.addLoad8(R0, 20); + gen.addJumpIfR0Equals(0x0, "LABEL_576"); + gen.addJumpIfR0Equals(0x3a, "LABEL_297"); + gen.addLoadImmediate(R1, -116); + gen.addLoad8(R0, 38); + gen.addJumpIfR0Equals(0xff, "LABEL_582"); + gen.addLoadImmediate(R1, -44); + gen.addJump("LABEL_576"); + + gen.defineLabel("LABEL_297"); + gen.addLoad8(R0, 54); + gen.addLoadImmediate(R1, -100); + gen.addJumpIfR0Equals(0x85, "LABEL_582"); + gen.addJumpIfR0NotEquals(0x88, "LABEL_333"); + gen.addLoadImmediate(R0, 38); + gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("ff0200000000000000000000000000"), "LABEL_333"); + gen.addLoadImmediate(R1, -104); + gen.addJump("LABEL_582"); + + gen.defineLabel("LABEL_333"); + gen.addLoadFromMemory(R0, 14); + gen.addJumpIfR0NotEquals(0x96, "LABEL_574"); + gen.addLoadFromMemory(R0, 15); + gen.addJumpIfR0GreaterThan(0x48e, "LABEL_574"); + gen.addLoadImmediate(R0, 0); + gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("7e9046bc700828c68e23672c86dd60"), "LABEL_574"); + gen.addLoadImmediate(R0, 18); + gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("00603afffe800000000000002ac68efffe23672c"), "LABEL_574"); + gen.addLoadImmediate(R0, 54); + gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("8600"), "LABEL_574"); + gen.addLoadImmediate(R0, 58); + gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("40c0"), "LABEL_574"); + gen.addLoad16(R0, 60); + gen.addJumpIfR0Equals(0x0, "LABEL_574"); + gen.addJumpIfR0LessThan(0xb4, "LABEL_421"); + gen.addJumpIfR0LessThan(0x91e, "LABEL_574"); + gen.addJumpIfR0GreaterThan(0x1b58, "LABEL_574"); + + gen.defineLabel("LABEL_421"); + gen.addLoadImmediate(R0, 62); + gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("0000000000000000010128c68e23672c05010000000005dc030440c0"), "LABEL_574"); + gen.addLoad32(R0, 90); + gen.addJumpIfR0Equals(0x0, "LABEL_574"); + gen.addJumpIfR0LessThan(0xb4, "LABEL_480"); + gen.addJumpIfR0LessThan(0x55555555, "LABEL_574"); + gen.addJumpIfR0GreaterThan(0xffffffffL, "LABEL_574"); + + gen.defineLabel("LABEL_480"); + gen.addLoad32(R0, 94); + gen.addJumpIfR0LessThan(0x55555555, "LABEL_574"); + gen.addJumpIfR0GreaterThan(0xffffffffL, "LABEL_574"); + gen.addLoadImmediate(R0, 98); + gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("000000002401fa000480f000000000000000000019030000"), "LABEL_574"); + gen.addLoad32(R0, 122); + gen.addJumpIfR0Equals(0x0, "LABEL_574"); + gen.addJumpIfR0LessThan(0x78, "LABEL_547"); + gen.addJumpIfR0LessThan(0x91e, "LABEL_574"); + gen.addJumpIfR0GreaterThan(0x1b58, "LABEL_574"); + + gen.defineLabel("LABEL_547"); + gen.addLoadImmediate(R0, 126); + gen.addJumpIfBytesAtR0NotEqual(hexStringToByteArray("2401fa000480f00000000000000000010701"), "LABEL_574"); + gen.addLoadImmediate(R1, -72); + gen.addJump("LABEL_582"); + + gen.defineLabel("LABEL_574"); + gen.addLoadImmediate(R1, -40); + + gen.defineLabel("LABEL_576"); + gen.addLoadData(R0, 0); + gen.addAdd(1); + gen.addStoreData(R0, 0); + gen.addJump(PASS_LABEL); + + gen.defineLabel("LABEL_582"); + gen.addLoadData(R0, 0); + gen.addAdd(1); + gen.addStoreData(R0, 0); + gen.addJump(DROP_LABEL); + + byte[] program = gen.generate(); + final String programString = toHexString(program).toLowerCase(); + final String referenceProgramHexString = "6bf8b03a01b8120c6b8894023706006b847c023088a27c022b88a47c022688b87c022188cd7c021c88e17c021788e384004608066a0e6dff68a40202000600010800060412147a1f016dff648401f500021a1c6b807c01ec0000686bc8a401d80006ffffffffffff6a266bb0a401d10004c0a801ec6bec7401c6120c84007808000a17821f1112149c00181fffab0d2a108211446a3239a205067e9046bc70086be874019b0a1e52f06ba07c019600e06ba81a1e7e00000189ffffffff6ba47e0000017ec0a801ff0a1782140612149c000d1fffab0d2a108206076dff6c7401656bdc68a401510006ffffffffffff6bac7401526be47401477c001386dd686bc4a401340006ffffffffffff6bbc7401350a147c012800007a0e3a6b8c0a267c012200ff6bd47401170a366b9c7c011400858218886a26a2040fff02000000000000000000000000006b9872f9aa0e82ec96aa0f8c00e5048e68a2d20f7e9046bc700828c68e23672c86dd606a12a2b91400603afffe800000000000002ac68efffe23672c6a36a2b20286006a3aa2ab0240c0123c7aa600920ab494009e091e8c00991b586a3ea2781c0000000000000000010128c68e23672c05010000000005dc030440c01a5a7a73009212b49600000067555555558e0000005effffffff1a5e9600000053555555558e0000004affffffff6a62a22d18000000002401fa000480f0000000000000000000190300001a7a7a2800920a78940020091e8c001b1b586a7ea204122401fa000480f000000000000000000107016bb872086bd8b03a01b87206b03a01b87201"; + assertEquals(referenceProgramHexString, programString); + } }
diff --git a/tests/unit/src/android/net/apf/ApfTestUtils.java b/tests/unit/src/android/net/apf/ApfTestUtils.java index 56d7cae..3fcb8d7 100644 --- a/tests/unit/src/android/net/apf/ApfTestUtils.java +++ b/tests/unit/src/android/net/apf/ApfTestUtils.java
@@ -28,7 +28,7 @@ import android.content.Context; import android.net.LinkAddress; import android.net.LinkProperties; -import android.net.apf.ApfV4Generator.IllegalInstructionException; +import android.net.apf.BaseApfGenerator.IllegalInstructionException; import android.net.ip.IIpClientCallbacks; import android.net.ip.IpClient; import android.net.metrics.IpConnectivityLog;
diff --git a/tests/unit/src/android/net/apf/ApfV5Test.kt b/tests/unit/src/android/net/apf/ApfV5Test.kt index 30d19b2..421ed5b 100644 --- a/tests/unit/src/android/net/apf/ApfV5Test.kt +++ b/tests/unit/src/android/net/apf/ApfV5Test.kt
@@ -15,15 +15,28 @@ */ package android.net.apf -import android.net.apf.ApfV4Generator.IllegalInstructionException -import android.net.apf.ApfV4Generator.MIN_APF_VERSION -import android.net.apf.ApfV4Generator.MIN_APF_VERSION_IN_DEV -import android.net.apf.ApfV4Generator.Register.R0 -import android.net.apf.ApfV4Generator.Register.R1 +import android.net.apf.ApfCounterTracker.Counter +import android.net.apf.ApfTestUtils.DROP +import android.net.apf.ApfTestUtils.MIN_PKT_SIZE +import android.net.apf.ApfTestUtils.PASS +import android.net.apf.ApfTestUtils.assertDrop +import android.net.apf.ApfTestUtils.assertPass +import android.net.apf.ApfTestUtils.assertVerdict +import android.net.apf.BaseApfGenerator.DROP_LABEL +import android.net.apf.BaseApfGenerator.IllegalInstructionException +import android.net.apf.BaseApfGenerator.MIN_APF_VERSION +import android.net.apf.BaseApfGenerator.MIN_APF_VERSION_IN_DEV +import android.net.apf.BaseApfGenerator.Register.R0 +import android.net.apf.BaseApfGenerator.Register.R1 import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 -import java.lang.IllegalArgumentException +import com.android.net.module.util.Struct +import com.android.net.module.util.structs.EthernetHeader +import com.android.net.module.util.structs.Ipv4Header +import com.android.net.module.util.structs.UdpHeader +import java.nio.ByteBuffer import kotlin.test.assertContentEquals +import kotlin.test.assertEquals import kotlin.test.assertFailsWith import org.junit.Test import org.junit.runner.RunWith @@ -35,54 +48,19 @@ @SmallTest class ApfV5Test { - @Test - fun testApfInstructionVersionCheck() { - var gen = ApfV4Generator(MIN_APF_VERSION) - assertFailsWith<IllegalInstructionException> { gen.addDrop() } - assertFailsWith<IllegalInstructionException> { gen.addCountAndDrop(12) } - assertFailsWith<IllegalInstructionException> { gen.addCountAndPass(1000) } - assertFailsWith<IllegalInstructionException> { gen.addTransmit() } - assertFailsWith<IllegalInstructionException> { gen.addDiscard() } - assertFailsWith<IllegalInstructionException> { gen.addAllocateR0() } - assertFailsWith<IllegalInstructionException> { gen.addAllocate(100) } - assertFailsWith<IllegalInstructionException> { gen.addData(ByteArray(3) { 0x01 }) } - assertFailsWith<IllegalInstructionException> { gen.addWriteU8(100) } - assertFailsWith<IllegalInstructionException> { gen.addWriteU16(100) } - assertFailsWith<IllegalInstructionException> { gen.addWriteU32(100) } - assertFailsWith<IllegalInstructionException> { gen.addPacketCopy(100, 100) } - assertFailsWith<IllegalInstructionException> { gen.addDataCopy(100, 100) } - assertFailsWith<IllegalInstructionException> { gen.addWriteU8(R0) } - assertFailsWith<IllegalInstructionException> { gen.addWriteU16(R0) } - assertFailsWith<IllegalInstructionException> { gen.addWriteU32(R0) } - assertFailsWith<IllegalInstructionException> { gen.addWriteU8(R1) } - assertFailsWith<IllegalInstructionException> { gen.addWriteU16(R1) } - assertFailsWith<IllegalInstructionException> { gen.addWriteU32(R1) } - assertFailsWith<IllegalInstructionException> { gen.addPacketCopyFromR0LenR1() } - assertFailsWith<IllegalInstructionException> { gen.addDataCopyFromR0LenR1() } - assertFailsWith<IllegalInstructionException> { gen.addPacketCopyFromR0(10) } - assertFailsWith<IllegalInstructionException> { gen.addDataCopyFromR0(10) } - assertFailsWith<IllegalInstructionException> { - gen.addJumpIfBytesAtR0Equal(byteArrayOf('A'.code.toByte()), ApfV4Generator.DROP_LABEL) } - assertFailsWith<IllegalInstructionException> { gen.addJumpIfPktAtR0DoesNotContainDnsQ( - byteArrayOf(1, 'A'.code.toByte()), 0x0c, ApfV4Generator.DROP_LABEL) } - assertFailsWith<IllegalInstructionException> { gen.addJumpIfPktAtR0ContainDnsQ( - byteArrayOf(1, 'A'.code.toByte()), 0x0c, ApfV4Generator.DROP_LABEL) } - assertFailsWith<IllegalInstructionException> { gen.addJumpIfPktAtR0DoesNotContainDnsA( - byteArrayOf(1, 'A'.code.toByte()), ApfV4Generator.DROP_LABEL) } - assertFailsWith<IllegalInstructionException> { gen.addJumpIfPktAtR0ContainDnsA( - byteArrayOf(1, 'A'.code.toByte()), ApfV4Generator.DROP_LABEL) } - } + private val testPacket = byteArrayOf(1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16) @Test fun testDataInstructionMustComeFirst() { - var gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV) + var gen = ApfV6Generator() gen.addAllocateR0() assertFailsWith<IllegalInstructionException> { gen.addData(ByteArray(3) { 0x01 }) } } @Test fun testApfInstructionEncodingSizeCheck() { - var gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV) + var gen = ApfV6Generator() assertFailsWith<IllegalArgumentException> { gen.addAllocate(65536) } assertFailsWith<IllegalArgumentException> { gen.addAllocate(-1) } assertFailsWith<IllegalArgumentException> { gen.addDataCopy(-1, 1) } @@ -170,10 +148,25 @@ } @Test + fun testValidateDnsNames() { + // '%' is a valid label character in mDNS subtype + // byte == 0xff means it is a '*' wildcard, which is a valid encoding. + val program = ApfV6Generator().addJumpIfPktAtR0ContainDnsQ( + byteArrayOf(1, '%'.code.toByte(), 0, 0), + 1, + DROP_LABEL) + .addJumpIfPktAtR0ContainDnsA( + byteArrayOf(0xff.toByte(), 1, 'B'.code.toByte(), 0, 0), + DROP_LABEL + ) + .generate() + } + + @Test fun testApfInstructionsEncoding() { - var gen = ApfV4Generator(MIN_APF_VERSION) - gen.addPass() - var program = gen.generate() + val v4gen = ApfV4Generator<ApfV4Generator<BaseApfGenerator>>(MIN_APF_VERSION) + v4gen.addPass() + var program = v4gen.generate() // encoding PASS opcode: opcode=0, imm_len=0, R=0 assertContentEquals( byteArrayOf(encodeInstruction(opcode = 0, immLength = 0, register = 0)), program) @@ -181,7 +174,7 @@ listOf("0: pass"), ApfJniUtils.disassembleApf(program).map { it.trim() } ) - gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV) + var gen = ApfV6Generator() gen.addDrop() program = gen.generate() // encoding DROP opcode: opcode=0, imm_len=0, R=1 @@ -191,7 +184,7 @@ listOf("0: drop"), ApfJniUtils.disassembleApf(program).map { it.trim() } ) - gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV) + gen = ApfV6Generator() gen.addCountAndPass(129) program = gen.generate() // encoding COUNT(PASS) opcode: opcode=0, imm_len=size_of(imm), R=0, imm=counterNumber @@ -202,7 +195,7 @@ listOf("0: pass 129"), ApfJniUtils.disassembleApf(program).map { it.trim() } ) - gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV) + gen = ApfV6Generator() gen.addCountAndDrop(1000) program = gen.generate() // encoding COUNT(DROP) opcode: opcode=0, imm_len=size_of(imm), R=1, imm=counterNumber @@ -213,7 +206,7 @@ listOf("0: drop 1000"), ApfJniUtils.disassembleApf(program).map { it.trim() } ) - gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV) + gen = ApfV6Generator() gen.addAllocateR0() gen.addAllocate(1500) program = gen.generate() @@ -227,21 +220,23 @@ assertContentEquals(listOf("0: allocate r0", "2: allocate 1500"), ApfJniUtils.disassembleApf(program).map { it.trim() }) - gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV) - gen.addTransmit() - gen.addDiscard() + gen = ApfV6Generator() + gen.addTransmitWithoutChecksum() + gen.addTransmitL4(30, 40, 50, 256, true) program = gen.generate() - // encoding TRANSMIT/DISCARD opcode: opcode=21(EXT opcode number), - // imm=37(TRANSMIT/DISCARD opcode number), - // R=0 means discard the buffer. R=1 means transmit the buffer. + // encoding TRANSMIT opcode: opcode=21(EXT opcode number), + // imm=37(TRANSMIT opcode number), assertContentEquals(byteArrayOf( - encodeInstruction(opcode = 21, immLength = 1, register = 0), 37, - encodeInstruction(opcode = 21, immLength = 1, register = 1), 37, + encodeInstruction(opcode = 21, immLength = 1, register = 0), + 37, 255.toByte(), 255.toByte(), + encodeInstruction(opcode = 21, immLength = 1, register = 1), 37, 30, 40, 50, 1, 0 ), program) - assertContentEquals(listOf("0: discard", "2: transmit"), - ApfJniUtils.disassembleApf(program).map { it.trim() }) + assertContentEquals(listOf( + "0: transmit ip_ofs=255", + "4: transmitudp ip_ofs=30, csum_ofs=40, csum_start=50, partial_csum=0x0100", + ), ApfJniUtils.disassembleApf(program).map { it.trim() }) - gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV) + gen = ApfV6Generator() val largeByteArray = ByteArray(256) { 0x01 } gen.addData(largeByteArray) program = gen.generate() @@ -249,10 +244,10 @@ assertContentEquals(byteArrayOf( encodeInstruction(opcode = 14, immLength = 2, register = 1), 0x01, 0x00) + largeByteArray, program) - assertContentEquals(listOf("0: data 256," + "01".repeat(256) ), + assertContentEquals(listOf("0: data 256, " + "01".repeat(256) ), ApfJniUtils.disassembleApf(program).map { it.trim() }) - gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV) + gen = ApfV6Generator() gen.addWriteU8(0x01) gen.addWriteU16(0x0102) gen.addWriteU32(0x01020304) @@ -287,7 +282,7 @@ ), ApfJniUtils.disassembleApf(program).map { it.trim() }) - gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV) + gen = ApfV6Generator() gen.addWriteU8(R0) gen.addWriteU16(R0) gen.addWriteU32(R0) @@ -311,7 +306,7 @@ "8: ewrite2 r1", "10: ewrite4 r1"), ApfJniUtils.disassembleApf(program).map { it.trim() }) - gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV) + gen = ApfV6Generator() gen.addDataCopy(0, 10) gen.addDataCopy(1, 5) gen.addPacketCopy(1000, 255) @@ -323,36 +318,42 @@ 0x03.toByte(), 0xe8.toByte(), 0xff.toByte(), ), program) assertContentEquals(listOf( - "0: dcopy 0, 10", - "2: dcopy 1, 5", - "5: pcopy 1000, 255"), ApfJniUtils.disassembleApf(program).map { it.trim() }) + "0: datacopy src=0, len=10", + "2: datacopy src=1, len=5", + "5: pktcopy src=1000, len=255" + ), + ApfJniUtils.disassembleApf(program).map { it.trim() }) - gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV) - gen.addPacketCopyFromR0LenR1() + gen = ApfV6Generator() + gen.addDataCopyFromR0(5) gen.addPacketCopyFromR0(5) gen.addDataCopyFromR0LenR1() - gen.addDataCopyFromR0(5) + gen.addPacketCopyFromR0LenR1() program = gen.generate() assertContentEquals(byteArrayOf( - encodeInstruction(21, 1, 1), 41, + encodeInstruction(21, 1, 1), 41, 5, encodeInstruction(21, 1, 0), 41, 5, encodeInstruction(21, 1, 1), 42, - encodeInstruction(21, 1, 0), 42, 5, + encodeInstruction(21, 1, 0), 42, ), program) - // TODO: add back the following test case when implementing EPKTCOPY, EDATACOPY opcodes. -// assertContentEquals(arrayOf( -// " 0: dcopy [r1+0], 5", -// " 4: pcopy [r0+1000], 255"), ApfJniUtils.disassembleApf(program)) + assertContentEquals(listOf( + "0: edatacopy src=r0, len=5", + "3: epktcopy src=r0, len=5", + "6: edatacopy src=r0, len=r1", + "8: epktcopy src=r0, len=r1"), ApfJniUtils.disassembleApf(program).map{ it.trim() }) - gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV) + gen = ApfV6Generator() gen.addJumpIfBytesAtR0Equal(byteArrayOf('a'.code.toByte()), ApfV4Generator.DROP_LABEL) program = gen.generate() assertContentEquals( byteArrayOf(encodeInstruction(opcode = 20, immLength = 1, register = 1), 1, 1, 'a'.code.toByte()), program) + assertContentEquals(listOf( + "0: jbseq r0, 0x1, DROP, 61"), + ApfJniUtils.disassembleApf(program).map{ it.trim() }) val qnames = byteArrayOf(1, 'A'.code.toByte(), 1, 'B'.code.toByte(), 0, 0) - gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV) + gen = ApfV6Generator() gen.addJumpIfPktAtR0DoesNotContainDnsQ(qnames, 0x0c, ApfV4Generator.DROP_LABEL) gen.addJumpIfPktAtR0ContainDnsQ(qnames, 0x0c, ApfV4Generator.DROP_LABEL) program = gen.generate() @@ -361,8 +362,26 @@ ) + qnames + byteArrayOf( encodeInstruction(21, 1, 1), 43, 1, 0x0c.toByte(), ) + qnames, program) + assertContentEquals(listOf( + "0: jdnsqne r0, DROP, 12, (1)A(1)B(0)(0)", + "10: jdnsqeq r0, DROP, 12, (1)A(1)B(0)(0)"), + ApfJniUtils.disassembleApf(program).map{ it.trim() }) - gen = ApfV4Generator(MIN_APF_VERSION_IN_DEV) + gen = ApfV6Generator() + gen.addJumpIfPktAtR0DoesNotContainDnsQSafe(qnames, 0x0c, ApfV4Generator.DROP_LABEL) + gen.addJumpIfPktAtR0ContainDnsQSafe(qnames, 0x0c, ApfV4Generator.DROP_LABEL) + program = gen.generate() + assertContentEquals(byteArrayOf( + encodeInstruction(21, 1, 0), 45, 11, 0x0c.toByte(), + ) + qnames + byteArrayOf( + encodeInstruction(21, 1, 1), 45, 1, 0x0c.toByte(), + ) + qnames, program) + assertContentEquals(listOf( + "0: jdnsqnesafe r0, DROP, 12, (1)A(1)B(0)(0)", + "10: jdnsqeqsafe r0, DROP, 12, (1)A(1)B(0)(0)"), + ApfJniUtils.disassembleApf(program).map{ it.trim() }) + + gen = ApfV6Generator() gen.addJumpIfPktAtR0DoesNotContainDnsA(qnames, ApfV4Generator.DROP_LABEL) gen.addJumpIfPktAtR0ContainDnsA(qnames, ApfV4Generator.DROP_LABEL) program = gen.generate() @@ -371,10 +390,419 @@ ) + qnames + byteArrayOf( encodeInstruction(21, 1, 1), 44, 1, ) + qnames, program) + assertContentEquals(listOf( + "0: jdnsane r0, DROP, (1)A(1)B(0)(0)", + "9: jdnsaeq r0, DROP, (1)A(1)B(0)(0)"), + ApfJniUtils.disassembleApf(program).map{ it.trim() }) + + gen = ApfV6Generator() + gen.addJumpIfPktAtR0DoesNotContainDnsASafe(qnames, ApfV4Generator.DROP_LABEL) + gen.addJumpIfPktAtR0ContainDnsASafe(qnames, ApfV4Generator.DROP_LABEL) + program = gen.generate() + assertContentEquals(byteArrayOf( + encodeInstruction(21, 1, 0), 46, 10, + ) + qnames + byteArrayOf( + encodeInstruction(21, 1, 1), 46, 1, + ) + qnames, program) + assertContentEquals(listOf( + "0: jdnsanesafe r0, DROP, (1)A(1)B(0)(0)", + "9: jdnsaeqsafe r0, DROP, (1)A(1)B(0)(0)"), + ApfJniUtils.disassembleApf(program).map{ it.trim() }) + } + + @Test + fun testWriteToTxBuffer() { + var program = ApfV6Generator() + .addAllocate(14) + .addWriteU8(0x01) + .addWriteU16(0x0203) + .addWriteU32(0x04050607) + .addLoadImmediate(R0, 1) + .addWriteU8(R0) + .addLoadImmediate(R0, 0x0203) + .addWriteU16(R0) + .addLoadImmediate(R1, 0x04050607) + .addWriteU32(R1) + .addTransmitWithoutChecksum() + .generate() + assertPass(MIN_APF_VERSION_IN_DEV, program, ByteArray(MIN_PKT_SIZE)) + assertContentEquals(byteArrayOf(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07), ApfJniUtils.getTransmittedPacket()) + } + + @Test + fun testCopyToTxBuffer() { + var program = ApfV6Generator() + .addData(byteArrayOf(33, 34, 35)) + .addAllocate(14) + .addDataCopy(2 /* src */, 2 /* len */) + .addDataCopy(4 /* src */, 1 /* len */) + .addPacketCopy(0 /* src */, 1 /* len */) + .addPacketCopy(1 /* src */, 3 /* len */) + .addLoadImmediate(R0, 2) // data copy offset + .addDataCopyFromR0(2 /* len */) + .addLoadImmediate(R0, 4) // data copy offset + .addLoadImmediate(R1, 1) // len + .addDataCopyFromR0LenR1() + .addLoadImmediate(R0, 0) // packet copy offset + .addPacketCopyFromR0(1 /* len */) + .addLoadImmediate(R0, 1) // packet copy offset + .addLoadImmediate(R1, 3) // len + .addPacketCopyFromR0LenR1() + .addTransmitWithoutChecksum() + .generate() + assertPass(MIN_APF_VERSION_IN_DEV, program, testPacket) + assertContentEquals(byteArrayOf(33, 34, 35, 1, 2, 3, 4, 33, 34, 35, 1, 2, 3, 4), + ApfJniUtils.getTransmittedPacket()) + } + + @Test + fun testPassDrop() { + var program = ApfV6Generator() + .addDrop() + .addPass() + .generate() + assertDrop(MIN_APF_VERSION_IN_DEV, program, testPacket) + + var dataRegion = ByteArray(Counter.totalSize()) { 0 } + program = ApfV6Generator() + .addData(byteArrayOf()) + .addCountAndDrop(Counter.DROPPED_ETH_BROADCAST.value()) + .generate() + assertVerdict(MIN_APF_VERSION_IN_DEV, DROP, program, testPacket, dataRegion) + var counterMap = decodeCountersIntoMap(dataRegion) + assertEquals(mapOf<Counter, Long>( + Counter.TOTAL_PACKETS to 1, + Counter.DROPPED_ETH_BROADCAST to 1), counterMap) + + dataRegion = ByteArray(Counter.totalSize()) { 0 } + program = ApfV6Generator() + .addData(byteArrayOf()) + .addCountAndPass(Counter.PASSED_ARP.value()) + .generate() + assertVerdict(MIN_APF_VERSION_IN_DEV, PASS, program, testPacket, dataRegion) + counterMap = decodeCountersIntoMap(dataRegion) + assertEquals(mapOf<Counter, Long>( + Counter.TOTAL_PACKETS to 1, + Counter.PASSED_ARP to 1), counterMap) + } + + @Test + fun testAllocateFailure() { + val program = ApfV6Generator() + .addData(byteArrayOf()) + // allocate size: 65535 > sizeof(apf_test_buffer): 1514, trigger allocate failure. + .addAllocate(65535) + .addDrop() + .generate() + val dataRegion = ByteArray(Counter.totalSize()) { 0 } + assertVerdict(MIN_APF_VERSION_IN_DEV, PASS, program, testPacket, dataRegion) + val counterMap = decodeCountersIntoMap(dataRegion) + assertEquals(mapOf<Counter, Long>( + Counter.TOTAL_PACKETS to 1, + Counter.PASSED_ALLOCATE_FAILURE to 1), counterMap) + } + + @Test + fun testTransmitFailure() { + val program = ApfV6Generator() + .addData(byteArrayOf()) + .addAllocate(14) + // len: 13 is less than ETH_HLEN, trigger transmit failure. + .addLoadImmediate(R0, 13) + .addStoreToMemory(R0, 10) + .addTransmitWithoutChecksum() + .addDrop() + .generate() + val dataRegion = ByteArray(Counter.totalSize()) { 0 } + assertVerdict(MIN_APF_VERSION_IN_DEV, PASS, program, testPacket, dataRegion) + val counterMap = decodeCountersIntoMap(dataRegion) + assertEquals(mapOf<Counter, Long>( + Counter.TOTAL_PACKETS to 1, + Counter.PASSED_TRANSMIT_FAILURE to 1), counterMap) + } + + @Test + fun testTransmitL4() { + val etherIpv4UdpPacket = intArrayOf( + 0x01, 0x00, 0x5e, 0x00, 0x00, 0xfb, + 0x38, 0xca, 0x84, 0xb7, 0x7f, 0x16, + 0x08, 0x00, // end of ethernet header + 0x45, + 0x04, + 0x00, 0x3f, + 0x43, 0xcd, + 0x40, 0x00, + 0xff, + 0x11, + 0x00, 0x00, // ipv4 checksum set to 0 + 0xc0, 0xa8, 0x01, 0x03, + 0xe0, 0x00, 0x00, 0xfb, // end of ipv4 header + 0x14, 0xe9, + 0x14, 0xe9, + 0x00, 0x2b, + 0x00, 0x2b, // end of udp header. udp checksum set to udp (header + payload) size + 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x62, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, + 0x00, 0x00, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x04, 0xc0, 0xa8, 0x01, + 0x09, + ).map { it.toByte() }.toByteArray() + val program = ApfV6Generator() + .addData(etherIpv4UdpPacket) + .addAllocate(etherIpv4UdpPacket.size) + .addDataCopy(2 /* src */, etherIpv4UdpPacket.size /* len */) + .addTransmitL4(ETH_HLEN /* ipOfs */, + ETH_HLEN + IPV4_HLEN + 6 /* csumOfs */, + ETH_HLEN + IPV4_HLEN - 8 /* csumStart */, + IPPROTO_UDP /* partialCsum */, + true /* isUdp */) + .generate() + assertPass(MIN_APF_VERSION_IN_DEV, program, testPacket) + val txBuf = ByteBuffer.wrap(ApfJniUtils.getTransmittedPacket()) + Struct.parse(EthernetHeader::class.java, txBuf) + val ipv4Hdr = Struct.parse(Ipv4Header::class.java, txBuf) + val udpHdr = Struct.parse(UdpHeader::class.java, txBuf) + assertEquals(0x9535.toShort(), ipv4Hdr.checksum) + assertEquals(0xa73d.toShort(), udpHdr.checksum) + } + + @Test + fun testDnsQuestionMatch() { + // needles = { A, B.LOCAL } + val needlesMatch = intArrayOf( + 0x01, 'A'.code, + 0x00, + 0x01, 'B'.code, + 0x05, 'L'.code, 'O'.code, 'C'.code, 'A'.code, 'L'.code, + 0x00, + 0x00 + ).map { it.toByte() }.toByteArray() + val udpPayload = intArrayOf( + 0x00, 0x00, 0x00, 0x00, // tid = 0x00, flags = 0x00, + 0x00, 0x02, // qdcount = 2 + 0x00, 0x00, // ancount = 0 + 0x00, 0x00, // nscount = 0 + 0x00, 0x00, // arcount = 0 + 0x01, 'a'.code, + 0x01, 'b'.code, + 0x05, 'l'.code, 'o'.code, 'c'.code, 'a'.code, 'l'.code, + 0x00, // qname1 = a.b.local + 0x00, 0x01, 0x00, 0x01, // type = A, class = 0x0001 + 0xc0, 0x0e, // qname2 = b.local (name compression) + 0x00, 0x01, 0x00, 0x01 // type = A, class = 0x0001 + ).map { it.toByte() }.toByteArray() + + var program = ApfV6Generator() + .addData(byteArrayOf()) + .addLoadImmediate(R0, 0) + .addJumpIfPktAtR0ContainDnsQ(needlesMatch, 0x01 /* qtype */, DROP_LABEL) + .addPass() + .generate() + assertDrop(MIN_APF_VERSION_IN_DEV, program, udpPayload) + + program = ApfV6Generator() + .addData(byteArrayOf()) + .addLoadImmediate(R0, 0) + .addJumpIfPktAtR0ContainDnsQSafe(needlesMatch, 0x01 /* qtype */, DROP_LABEL) + .addPass() + .generate() + assertDrop(MIN_APF_VERSION_IN_DEV, program, udpPayload) + + program = ApfV6Generator() + .addData(byteArrayOf()) + .addLoadImmediate(R0, 0) + .addJumpIfPktAtR0DoesNotContainDnsQ(needlesMatch, 0x01 /* qtype */, DROP_LABEL) + .addPass() + .generate() + assertPass(MIN_APF_VERSION_IN_DEV, program, udpPayload) + + program = ApfV6Generator() + .addData(byteArrayOf()) + .addLoadImmediate(R0, 0) + .addJumpIfPktAtR0DoesNotContainDnsQSafe(needlesMatch, 0x01 /* qtype */, DROP_LABEL) + .addPass() + .generate() + assertPass(MIN_APF_VERSION_IN_DEV, program, udpPayload) + + val badUdpPayload = intArrayOf( + 0x00, 0x00, 0x00, 0x00, // tid = 0x00, flags = 0x00, + 0x00, 0x02, // qdcount = 2 + 0x00, 0x00, // ancount = 0 + 0x00, 0x00, // nscount = 0 + 0x00, 0x00, // arcount = 0 + 0x01, 'a'.code, + 0x01, 'b'.code, + 0x05, 'l'.code, 'o'.code, 'c'.code, 'a'.code, 'l'.code, + 0x00, // qname1 = a.b.local + 0x00, 0x01, 0x00, 0x01, // type = A, class = 0x0001 + 0xc0, 0x1b, // corrupted pointer cause infinite loop + 0x00, 0x01, 0x00, 0x01 // type = A, class = 0x0001 + ).map { it.toByte() }.toByteArray() + + program = ApfV6Generator() + .addData(byteArrayOf()) + .addLoadImmediate(R0, 0) + .addJumpIfPktAtR0ContainDnsQ(needlesMatch, 0x01 /* qtype */, DROP_LABEL) + .addPass() + .generate() + var dataRegion = ByteArray(Counter.totalSize()) { 0 } + assertVerdict(MIN_APF_VERSION_IN_DEV, DROP, program, badUdpPayload, dataRegion) + var counterMap = decodeCountersIntoMap(dataRegion) + assertEquals(mapOf<Counter, Long>( + Counter.TOTAL_PACKETS to 1, + Counter.CORRUPT_DNS_PACKET to 1), counterMap) + + program = ApfV6Generator() + .addData(byteArrayOf()) + .addLoadImmediate(R0, 0) + .addJumpIfPktAtR0ContainDnsQSafe(needlesMatch, 0x01 /* qtype */, DROP_LABEL) + .addPass() + .generate() + dataRegion = ByteArray(Counter.totalSize()) { 0 } + assertVerdict(MIN_APF_VERSION_IN_DEV, PASS, program, badUdpPayload, dataRegion) + counterMap = decodeCountersIntoMap(dataRegion) + assertEquals(mapOf<Counter, Long>( + Counter.TOTAL_PACKETS to 1, + Counter.CORRUPT_DNS_PACKET to 1), counterMap) + } + + @Test + fun testDnsAnswerMatch() { + // needles = { A, B.LOCAL } + val needlesMatch = intArrayOf( + 0x01, 'A'.code, + 0x00, + 0x01, 'B'.code, + 0x05, 'L'.code, 'O'.code, 'C'.code, 'A'.code, 'L'.code, + 0x00, + 0x00 + ).map { it.toByte() }.toByteArray() + + val udpPayload = intArrayOf( + 0x00, 0x00, 0x84, 0x00, // tid = 0x00, flags = 0x8400, + 0x00, 0x00, // qdcount = 0 + 0x00, 0x02, // ancount = 2 + 0x00, 0x00, // nscount = 0 + 0x00, 0x00, // arcount = 0 + 0x01, 'a'.code, + 0x01, 'b'.code, + 0x05, 'l'.code, 'o'.code, 'c'.code, 'a'.code, 'l'.code, + 0x00, // name1 = a.b.local + 0x00, 0x01, 0x80, 0x01, // type = A, class = 0x8001 + 0x00, 0x00, 0x00, 0x78, // ttl = 120 + 0x00, 0x04, 0xc0, 0xa8, 0x01, 0x09, // rdlengh = 4, rdata = 192.168.1.9 + 0xc0, 0x0e, // name2 = b.local (name compression) + 0x00, 0x01, 0x80, 0x01, // type = A, class = 0x8001 + 0x00, 0x00, 0x00, 0x78, // ttl = 120 + 0x00, 0x04, 0xc0, 0xa8, 0x01, 0x09 // rdlengh = 4, rdata = 192.168.1.9 + ).map { it.toByte() }.toByteArray() + + var program = ApfV6Generator() + .addData(byteArrayOf()) + .addLoadImmediate(R0, 0) + .addJumpIfPktAtR0ContainDnsA(needlesMatch, DROP_LABEL) + .addPass() + .generate() + assertDrop(MIN_APF_VERSION_IN_DEV, program, udpPayload) + + program = ApfV6Generator() + .addData(byteArrayOf()) + .addLoadImmediate(R0, 0) + .addJumpIfPktAtR0ContainDnsASafe(needlesMatch, DROP_LABEL) + .addPass() + .generate() + assertDrop(MIN_APF_VERSION_IN_DEV, program, udpPayload) + + program = ApfV6Generator() + .addData(byteArrayOf()) + .addLoadImmediate(R0, 0) + .addJumpIfPktAtR0DoesNotContainDnsA(needlesMatch, DROP_LABEL) + .addPass() + .generate() + assertPass(MIN_APF_VERSION_IN_DEV, program, udpPayload) + + program = ApfV6Generator() + .addData(byteArrayOf()) + .addLoadImmediate(R0, 0) + .addJumpIfPktAtR0DoesNotContainDnsASafe(needlesMatch, DROP_LABEL) + .addPass() + .generate() + assertPass(MIN_APF_VERSION_IN_DEV, program, udpPayload) + + val badUdpPayload = intArrayOf( + 0x00, 0x00, 0x84, 0x00, // tid = 0x00, flags = 0x8400, + 0x00, 0x00, // qdcount = 0 + 0x00, 0x02, // ancount = 2 + 0x00, 0x00, // nscount = 0 + 0x00, 0x00, // arcount = 0 + 0x01, 'a'.code, + 0x01, 'b'.code, + 0x05, 'l'.code, 'o'.code, 'c'.code, 'a'.code, 'l'.code, + 0x00, // name1 = a.b.local + 0x00, 0x01, 0x80, 0x01, // type = A, class = 0x8001 + 0x00, 0x00, 0x00, 0x78, // ttl = 120 + 0x00, 0x04, 0xc0, 0xa8, 0x01, 0x09, // rdlengh = 4, rdata = 192.168.1.9 + 0xc0, 0x25, // corrupted pointer cause infinite loop + 0x00, 0x01, 0x80, 0x01, // type = A, class = 0x8001 + 0x00, 0x00, 0x00, 0x78, // ttl = 120 + 0x00, 0x04, 0xc0, 0xa8, 0x01, 0x09 // rdlengh = 4, rdata = 192.168.1.9 + ).map { it.toByte() }.toByteArray() + + program = ApfV6Generator() + .addData(byteArrayOf()) + .addLoadImmediate(R0, 0) + .addJumpIfPktAtR0ContainDnsA(needlesMatch, DROP_LABEL) + .addPass() + .generate() + var dataRegion = ByteArray(Counter.totalSize()) { 0 } + assertVerdict(MIN_APF_VERSION_IN_DEV, DROP, program, badUdpPayload, dataRegion) + var counterMap = decodeCountersIntoMap(dataRegion) + assertEquals(mapOf<Counter, Long>( + Counter.TOTAL_PACKETS to 1, + Counter.CORRUPT_DNS_PACKET to 1), counterMap) + + program = ApfV6Generator() + .addData(byteArrayOf()) + .addLoadImmediate(R0, 0) + .addJumpIfPktAtR0ContainDnsASafe(needlesMatch, DROP_LABEL) + .addPass() + .generate() + dataRegion = ByteArray(Counter.totalSize()) { 0 } + assertVerdict(MIN_APF_VERSION_IN_DEV, PASS, program, badUdpPayload, dataRegion) + counterMap = decodeCountersIntoMap(dataRegion) + assertEquals(mapOf<Counter, Long>( + Counter.TOTAL_PACKETS to 1, + Counter.CORRUPT_DNS_PACKET to 1), counterMap) + } + + @Test + fun testGetCounterValue() { + val counterBytes = intArrayOf(0xff, 0, 0, 0, 0x78, 0x56, 0x34, 0x12) + .map { it.toByte() }.toByteArray() + assertEquals(0xff, ApfCounterTracker.getCounterValue(counterBytes, Counter.TOTAL_PACKETS)) + } + + private fun decodeCountersIntoMap(counterBytes: ByteArray): Map<Counter, Long> { + val counters = Counter::class.java.enumConstants + val ret = HashMap<Counter, Long>() + // starting from index 2 to skip the endianness mark + for (c in listOf(*counters).subList(2, counters.size)) { + val value = ApfCounterTracker.getCounterValue(counterBytes, c) + if (value != 0L) { + ret[c] = value + } + } + return ret } private fun encodeInstruction(opcode: Int, immLength: Int, register: Int): Byte { val immLengthEncoding = if (immLength == 4) 3 else immLength return opcode.shl(3).or(immLengthEncoding.shl(1)).or(register).toByte() } + + companion object { + const val ETH_HLEN = 14 + const val IPV4_HLEN = 20 + const val IPPROTO_UDP = 17 + } }
diff --git a/tests/unit/src/android/net/apf/Bpf2Apf.java b/tests/unit/src/android/net/apf/Bpf2Apf.java index 795c2b3..5d2f9a9 100644 --- a/tests/unit/src/android/net/apf/Bpf2Apf.java +++ b/tests/unit/src/android/net/apf/Bpf2Apf.java
@@ -16,8 +16,11 @@ package android.net.apf; -import android.net.apf.ApfV4Generator.IllegalInstructionException; -import android.net.apf.ApfV4Generator.Register; +import static android.net.apf.BaseApfGenerator.Register.R0; +import static android.net.apf.BaseApfGenerator.Register.R1; + +import android.net.apf.BaseApfGenerator.IllegalInstructionException; +import android.net.apf.BaseApfGenerator.Register; import java.io.BufferedReader; import java.io.InputStreamReader; @@ -29,7 +32,7 @@ * translation of all BPF programs. * * Example usage: - * javac net/java/android/net/apf/ApfGenerator.java \ + * javac net/java/android/net/apf/ApfV4Generator.java \ * tests/servicestests/src/android/net/apf/Bpf2Apf.java * sudo tcpdump -i em1 -d icmp | java -classpath tests/servicestests/src:net/java \ * android.net.apf.Bpf2Apf @@ -67,7 +70,7 @@ case "ldx": case "ldxb": case "ldxh": - Register dest = opcode.contains("x") ? Register.R1 : Register.R0; + Register dest = opcode.contains("x") ? R1 : R0; if (arg.equals("4*([14]&0xf)")) { if (!opcode.equals("ldxb")) { throw new IllegalArgumentException("Unhandled instruction: " + line); @@ -140,7 +143,7 @@ break; case "st": case "stx": - Register src = opcode.contains("x") ? Register.R1 : Register.R0; + Register src = opcode.contains("x") ? R1 : R0; if (!arg.startsWith("M[")) { throw new IllegalArgumentException("Unhandled instruction: " + line); } @@ -169,9 +172,9 @@ gen.addOrR1(); break; case "sub": - gen.addNeg(Register.R1); + gen.addNeg(R1); gen.addAddR1(); - gen.addNeg(Register.R1); + gen.addNeg(R1); break; } } else { @@ -291,10 +294,10 @@ } break; case "tax": - gen.addMove(Register.R1); + gen.addMove(R1); break; case "txa": - gen.addMove(Register.R0); + gen.addMove(R0); break; default: throw new IllegalArgumentException("Unhandled instruction: " + line);
diff --git a/tests/unit/src/android/net/apf/JumpTableTest.kt b/tests/unit/src/android/net/apf/JumpTableTest.kt index 3858aac..2c48e38 100644 --- a/tests/unit/src/android/net/apf/JumpTableTest.kt +++ b/tests/unit/src/android/net/apf/JumpTableTest.kt
@@ -16,6 +16,7 @@ package android.net.apf +import android.net.apf.BaseApfGenerator.Register.R0 import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 import com.android.testutils.assertThrows @@ -34,7 +35,7 @@ class JumpTableTest { @Mock - lateinit var gen: ApfV4Generator + lateinit var gen: ApfV4Generator<ApfV4Generator<BaseApfGenerator>> @Before fun setUp() { @@ -94,7 +95,7 @@ j.generate(gen) inOrder.verify(gen).defineLabel(name) - inOrder.verify(gen).addLoadFromMemory(ApfV4Generator.Register.R0, slot) + inOrder.verify(gen).addLoadFromMemory(R0, slot) inOrder.verify(gen).addJumpIfR0Equals(0, "foo") inOrder.verify(gen).addJumpIfR0Equals(1, "bar") inOrder.verify(gen).addJumpIfR0Equals(2, "baz")
diff --git a/tests/unit/src/android/net/dhcp/DhcpServerTest.java b/tests/unit/src/android/net/dhcp/DhcpServerTest.java index 6d4bc13..1853071 100644 --- a/tests/unit/src/android/net/dhcp/DhcpServerTest.java +++ b/tests/unit/src/android/net/dhcp/DhcpServerTest.java
@@ -197,7 +197,8 @@ when(mDeps.makeLeaseRepository(any(), any(), any())).thenReturn(mRepository); when(mDeps.makeClock()).thenReturn(mClock); when(mDeps.makePacketListener(any())).thenReturn(mPacketListener); - when(mDeps.isFeatureEnabled(eq(mContext), eq(DHCP_RAPID_COMMIT_VERSION))).thenReturn(true); + when(mDeps.isFeatureNotChickenedOut(eq(mContext), eq(DHCP_RAPID_COMMIT_VERSION))) + .thenReturn(true); doNothing().when(mDeps) .sendPacket(any(), mSentPacketCaptor.capture(), mResponseDstAddrCaptor.capture()); when(mClock.elapsedRealtime()).thenReturn(TEST_CLOCK_TIME); @@ -253,7 +254,8 @@ public void testDiscover_RapidCommit() throws Exception { startServer(); - when(mDeps.isFeatureEnabled(eq(mContext), eq(DHCP_RAPID_COMMIT_VERSION))).thenReturn(true); + when(mDeps.isFeatureNotChickenedOut(eq(mContext), eq(DHCP_RAPID_COMMIT_VERSION))) + .thenReturn(true); when(mRepository.getCommittedLease(isNull() /* clientId */, eq(TEST_CLIENT_MAC), eq(INADDR_ANY) /* relayAddr */, isNull() /* hostname */)).thenReturn(TEST_LEASE);
diff --git a/tests/unit/src/android/net/ip/IpReachabilityMonitorTest.kt b/tests/unit/src/android/net/ip/IpReachabilityMonitorTest.kt index f93a3bd..fbf311b 100644 --- a/tests/unit/src/android/net/ip/IpReachabilityMonitorTest.kt +++ b/tests/unit/src/android/net/ip/IpReachabilityMonitorTest.kt
@@ -72,7 +72,9 @@ import kotlin.test.fail import org.junit.After import org.junit.Before +import org.junit.Rule import org.junit.Test +import org.junit.rules.TestName import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.any @@ -186,6 +188,7 @@ @RunWith(AndroidJUnit4::class) @SmallTest class IpReachabilityMonitorTest { + @get:Rule val mTestName = TestName() private val callback = mock(IpReachabilityMonitor.Callback::class.java) private val dependencies = mock(IpReachabilityMonitor.Dependencies::class.java) private val log = mock(SharedLog::class.java) @@ -272,6 +275,19 @@ doReturn(true).`when`(dependencies).isFeatureNotChickenedOut(any(), eq(IP_REACHABILITY_ROUTER_MAC_CHANGE_FAILURE_ONLY_AFTER_ROAM_VERSION)) + val ignoreOrganicNudFailureTestList = listOf( + "testLoseProvisioning_ignoreOrganicIpv4DnsLost", + "testLoseProvisioning_ignoreOrganicIpv6DnsLost", + "testLoseProvisioning_ignoreOrganicIpv4GatewayLost", + "testLoseProvisioning_ignoreOrganicIpv6GatewayLost") + // The experiment flag: IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION is read at + // the IpReachabilityMonitor constructor, so we have to set the value before initializing + // an IpReachabilityMonitor instance. + if (ignoreOrganicNudFailureTestList.contains(mTestName.methodName)) { + doReturn(true).`when`(dependencies).isFeatureEnabled(any(), + eq(IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION)) + } + val monitorFuture = CompletableFuture<IpReachabilityMonitor>() // IpReachabilityMonitor needs to be started from the handler thread handler.post { @@ -330,6 +346,8 @@ neighborMonitor.enqueuePacket(makeNewNeighMessage(TEST_IPV6_DNS, NUD_STALE)) neighborMonitor.enqueuePacket(makeNewNeighMessage(lostNeighbor, NUD_FAILED)) + handlerThread.waitForIdle(TEST_TIMEOUT_MS) + if (expectedNotifyLost) { verify(callback, timeout(TEST_TIMEOUT_MS)).notifyLost(eq(lostNeighbor), anyString(), eq(eventType)) @@ -411,32 +429,24 @@ @Test fun testLoseProvisioning_ignoreOrganicIpv4DnsLost() { - doReturn(true).`when`(dependencies).isFeatureEnabled(any(), - eq(IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION)) runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV4_DNS, NUD_ORGANIC_FAILED_CRITICAL, false /* expectedNotifyLost */) } @Test fun testLoseProvisioning_ignoreOrganicIpv6DnsLost() { - doReturn(true).`when`(dependencies).isFeatureEnabled(any(), - eq(IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION)) runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV6_DNS, NUD_ORGANIC_FAILED_CRITICAL, false /* expectedNotifyLost */) } @Test fun testLoseProvisioning_ignoreOrganicIpv4GatewayLost() { - doReturn(true).`when`(dependencies).isFeatureEnabled(any(), - eq(IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION)) runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV4_GATEWAY, NUD_ORGANIC_FAILED_CRITICAL, false /* expectedNotifyLost */) } @Test fun testLoseProvisioning_ignoreOrganicIpv6GatewayLost() { - doReturn(true).`when`(dependencies).isFeatureEnabled(any(), - eq(IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION)) runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY, NUD_ORGANIC_FAILED_CRITICAL, false /* expectedNotifyLost */) }
diff --git a/tests/unit/src/android/net/shared/ProvisioningConfigurationTest.java b/tests/unit/src/android/net/shared/ProvisioningConfigurationTest.java index 29ddf21..11bf5a0 100644 --- a/tests/unit/src/android/net/shared/ProvisioningConfigurationTest.java +++ b/tests/unit/src/android/net/shared/ProvisioningConfigurationTest.java
@@ -23,6 +23,9 @@ import static android.net.ip.IIpClient.PROV_IPV6_DISABLED; import static android.net.ip.IIpClient.PROV_IPV6_LINKLOCAL; import static android.net.ip.IIpClient.PROV_IPV6_SLAAC; +import static android.net.ip.IIpClient.HOSTNAME_SETTING_UNSET; +import static android.net.ip.IIpClient.HOSTNAME_SETTING_DO_NOT_SEND; +import static android.net.ip.IIpClient.HOSTNAME_SETTING_SEND; import static android.net.shared.ProvisioningConfiguration.fromStableParcelable; import static android.net.shared.ProvisioningConfiguration.ipv4ProvisioningModeToString; import static android.net.shared.ProvisioningConfiguration.ipv6ProvisioningModeToString; @@ -113,6 +116,7 @@ config.mIPv6ProvisioningMode = PROV_IPV6_SLAAC; config.mUniqueEui64AddressesOnly = false; config.mCreatorUid = 10136; + config.mHostnameSetting = HOSTNAME_SETTING_SEND; return config; } @@ -144,6 +148,7 @@ p.layer2Info = layer2Info.toStableParcelable(); p.options = makeCustomizedDhcpOptions((byte) 60, new String("android-dhcp-11").getBytes()); p.creatorUid = 10136; + p.hostnameSetting = HOSTNAME_SETTING_SEND; return p; } @@ -151,7 +156,7 @@ public void setUp() { mConfig = makeTestProvisioningConfiguration(); // Any added field must be included in equals() to be tested properly - assertFieldCountEquals(18, ProvisioningConfiguration.class); + assertFieldCountEquals(19, ProvisioningConfiguration.class); } @Test @@ -284,7 +289,9 @@ assertNotEqualsAfterChange(c -> c.mIPv6ProvisioningMode = PROV_IPV6_LINKLOCAL); assertNotEqualsAfterChange(c -> c.mUniqueEui64AddressesOnly = true); assertNotEqualsAfterChange(c -> c.mCreatorUid = 10138); - assertFieldCountEquals(18, ProvisioningConfiguration.class); + assertNotEqualsAfterChange(c -> c.mHostnameSetting = HOSTNAME_SETTING_UNSET); + assertNotEqualsAfterChange(c -> c.mHostnameSetting = HOSTNAME_SETTING_DO_NOT_SEND); + assertFieldCountEquals(19, ProvisioningConfiguration.class); } private void assertNotEqualsAfterChange(Consumer<ProvisioningConfiguration> mutator) {
diff --git a/tests/unit/src/com/android/networkstack/ipmemorystore/IpMemoryStoreServiceTest.java b/tests/unit/src/com/android/networkstack/ipmemorystore/IpMemoryStoreServiceTest.java index 37bd808..d192425 100644 --- a/tests/unit/src/com/android/networkstack/ipmemorystore/IpMemoryStoreServiceTest.java +++ b/tests/unit/src/com/android/networkstack/ipmemorystore/IpMemoryStoreServiceTest.java
@@ -937,4 +937,43 @@ assertEquals(testCase.first, Utils.byteArrayToString(testCase.second)); } } + + @Test + public void testNullDb() throws Exception { + // Init IpMemoryStoreService with a file that can't be opened + final File file = new File("/", TEST_DATABASE_NAME); + doReturn(file).when(mMockContext).getDatabasePath(anyString()); + final IpMemoryStoreService ipMemoryStoreService = + new IpMemoryStoreService(mMockContext); + + //test delete, no NullPointerException, got expected status and deleting count + doLatched("Did not get fail callback", latch -> + ipMemoryStoreService.delete("key", false /* needWipe */, + onDeleteStatus((status, deleted) -> { + assertEquals("Unexpected status: ", + Status.ERROR_DATABASE_CANNOT_BE_OPENED, + status.resultCode); + assertEquals("Deleting count != 1 :" + + deleted, 0, deleted.intValue()); + latch.countDown(); + })), LONG_TIMEOUT_MS); + + //Test deleteCluster, no NullPointerException, got expected status and deletedCount + doLatched("Did not get fail callback", latch -> + ipMemoryStoreService.deleteCluster("key", false /* needWipe */, + onDeleteStatus((status, deletedCount) -> { + assertEquals("Unexpected status: ", + Status.ERROR_DATABASE_CANNOT_BE_OPENED, + status.resultCode); + assertEquals("Unexpected deleted count : ", + 0, deletedCount.intValue()); + latch.countDown(); + })), LONG_TIMEOUT_MS); + + // Try to wipe all data in tables, no NullPointerException + ipMemoryStoreService.factoryReset(); + + //db is null, the db size could not over the threshold. + assertFalse(ipMemoryStoreService.isDbSizeOverThreshold()); + } }
diff --git a/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java b/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java index 3857b04..4944812 100644 --- a/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java +++ b/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java
@@ -332,15 +332,17 @@ assertFalse(tst.isDataStallSuspected()); } - @IgnoreAfter(Build.VERSION_CODES.S_V2) + // b/326143935 isUidNetworkingBlocked is not supported on pre-U device. + @IgnoreAfter(Build.VERSION_CODES.TIRAMISU) @Test - public void testPollSocketsInfo_ignoreBlockedUid_featureDisabled_beforeT() throws Exception { + public void testPollSocketsInfo_ignoreBlockedUid_featureDisabled_beforeU() throws Exception { doTestPollSocketsInfo_ignoreBlockedUid_featureDisabled(); } - @IgnoreUpTo(Build.VERSION_CODES.S_V2) + // b/326143935 isUidNetworkingBlocked is not supported on pre-U device. + @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) @Test - public void testPollSocketsInfo_ignoreBlockedUid_featureDisabled_TOrAbove() throws Exception { + public void testPollSocketsInfo_ignoreBlockedUid_featureDisabled_UOrAbove() throws Exception { doTestPollSocketsInfo_ignoreBlockedUid_featureDisabled(); verify(mCm, never()).isUidNetworkingBlocked(anyInt(), anyBoolean()); } @@ -371,8 +373,8 @@ assertTrue(tst.isDataStallSuspected()); } - // The feature is not enabled on pre-T device, because it needs bpf support. - @IgnoreUpTo(Build.VERSION_CODES.S_V2) + // b/326143935 isUidNetworkingBlocked is not supported on pre-U device. + @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) @Test public void testPollSocketsInfo_ignoreBlockedUid_featureEnabled() throws Exception { doReturn(true).when(mDependencies).shouldIgnoreTcpInfoForBlockedUids(); @@ -403,8 +405,8 @@ assertTrue(tst.isDataStallSuspected()); } - // The feature is not enabled on pre-T device, because it needs bpf support. - @IgnoreUpTo(Build.VERSION_CODES.S_V2) + // b/326143935 isUidNetworkingBlocked is not supported on pre-U device. + @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) @Test public void testPollSocketsInfo_ignoreBlockedUid_featureEnabled_dataSaver() throws Exception { doReturn(true).when(mDependencies).shouldIgnoreTcpInfoForBlockedUids();
diff --git a/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java b/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java index 43bee55..d6e9c8e 100644 --- a/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java +++ b/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -154,8 +154,8 @@ import android.util.ArrayMap; import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; +import com.android.internal.annotations.GuardedBy; import com.android.modules.utils.build.SdkLevel; import com.android.net.module.util.SharedLog; import com.android.networkstack.NetworkStackNotifier; @@ -180,6 +180,8 @@ import com.android.testutils.DevSdkIgnoreRule; import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; +import com.android.testutils.DevSdkIgnoreRunner; +import com.android.testutils.FunctionalUtils.ThrowingConsumer; import com.android.testutils.HandlerUtils; import com.google.protobuf.nano.MessageNano; @@ -194,6 +196,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.Spy; import org.mockito.invocation.InvocationOnMock; @@ -223,12 +226,15 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.function.Supplier; import javax.net.ssl.SSLHandshakeException; -@RunWith(AndroidJUnit4.class) +@DevSdkIgnoreRunner.MonitorThreadLeak +@RunWith(DevSdkIgnoreRunner.class) @SmallTest @SuppressLint("NewApi") // Uses hidden APIs, which the linter would identify as missing APIs. public class NetworkMonitorTest { @@ -272,8 +278,10 @@ private @Mock TcpSocketTracker.Dependencies mTstDependencies; private @Mock INetd mNetd; private @Mock TcpSocketTracker mTst; - private HashSet<WrappedNetworkMonitor> mCreatedNetworkMonitors; - private HashSet<BroadcastReceiver> mRegisteredReceivers; + @GuardedBy("mCreatedNetworkMonitors") + private final HashSet<WrappedNetworkMonitor> mCreatedNetworkMonitors = new HashSet<>(); + @GuardedBy("mRegisteredReceivers") + private final HashSet<BroadcastReceiver> mRegisteredReceivers = new HashSet<>(); private @Mock Context mMccContext; private @Mock Resources mMccResource; private @Mock WifiInfo mWifiInfo; @@ -321,6 +329,7 @@ private static final NetworkAgentConfigShim TEST_AGENT_CONFIG = NetworkAgentConfigShimImpl.newInstance(null); private static final LinkProperties TEST_LINK_PROPERTIES = new LinkProperties(); + private static final int THREAD_QUIT_MAX_RETRY_COUNT = 3; // Cannot have a static member for the LinkProperties with captive portal API information, as // the initializer would crash on Q (the members in LinkProperties were introduced in R). @@ -564,6 +573,11 @@ private FakeDns mFakeDns; + @GuardedBy("mThreadsToBeCleared") + private final ArrayList<Thread> mThreadsToBeCleared = new ArrayList<>(); + @GuardedBy("mExecutorServiceToBeCleared") + private final ArrayList<ExecutorService> mExecutorServiceToBeCleared = new ArrayList<>(); + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); @@ -578,6 +592,18 @@ .getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTP_URL), any()); doReturn(TEST_HTTPS_URL).when(mDependencies) .getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTPS_URL), any()); + doAnswer((invocation) -> { + synchronized (mThreadsToBeCleared) { + mThreadsToBeCleared.add(invocation.getArgument(0)); + } + return null; + }).when(mDependencies).onThreadCreated(any()); + doAnswer((invocation) -> { + synchronized (mExecutorServiceToBeCleared) { + mExecutorServiceToBeCleared.add(invocation.getArgument(0)); + } + return null; + }).when(mDependencies).onExecutorServiceCreated(any()); doReturn(mCleartextDnsNetwork).when(mNetwork).getPrivateDnsBypassingCopy(); @@ -657,16 +683,22 @@ mFakeDns.setAnswer(PRIVATE_DNS_PROBE_HOST_SUFFIX, new String[]{"2001:db8::1"}, TYPE_AAAA); doAnswer((invocation) -> { - mRegisteredReceivers.add(invocation.getArgument(0)); + synchronized (mRegisteredReceivers) { + mRegisteredReceivers.add(invocation.getArgument(0)); + } return new Intent(); }).when(mContext).registerReceiver(any(BroadcastReceiver.class), any()); doAnswer((invocation) -> { - mRegisteredReceivers.add(invocation.getArgument(0)); + synchronized (mRegisteredReceivers) { + mRegisteredReceivers.add(invocation.getArgument(0)); + } return new Intent(); }).when(mContext).registerReceiver(any(BroadcastReceiver.class), any(), anyInt()); doAnswer((invocation) -> { - mRegisteredReceivers.remove(invocation.getArgument(0)); + synchronized (mRegisteredReceivers) { + mRegisteredReceivers.remove(invocation.getArgument(0)); + } return null; }).when(mContext).unregisterReceiver(any()); @@ -676,25 +708,79 @@ setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS); setValidDataStallDnsTimeThreshold(TEST_MIN_VALID_STALL_DNS_TIME_THRESHOLD_MS); setConsecutiveDnsTimeoutThreshold(5); - mCreatedNetworkMonitors = new HashSet<>(); - mRegisteredReceivers = new HashSet<>(); + } + + private static <T> void quitResourcesThat(Supplier<List<T>> supplier, + ThrowingConsumer terminator) throws Exception { + // Run it multiple times since new threads might be generated in a thread + // that is about to be terminated, e.g. each thread that runs + // isCaptivePortal could generate 2 more probing threads. + for (int retryCount = 0; retryCount < THREAD_QUIT_MAX_RETRY_COUNT; retryCount++) { + final List<T> resourcesToBeCleared = supplier.get(); + if (resourcesToBeCleared.isEmpty()) return; + for (final T resource : resourcesToBeCleared) { + terminator.accept(resource); + } + } + + assertEquals(Collections.emptyList(), supplier.get()); + } + + private void quitNetworkMonitors() throws Exception { + quitResourcesThat(() -> { + synchronized (mCreatedNetworkMonitors) { + final ArrayList<WrappedNetworkMonitor> ret = + new ArrayList<>(mCreatedNetworkMonitors); + mCreatedNetworkMonitors.clear(); + return ret; + } + }, (it) -> { + final WrappedNetworkMonitor nm = (WrappedNetworkMonitor) it; + nm.notifyNetworkDisconnected(); + nm.awaitQuit(); + }); + synchronized (mRegisteredReceivers) { + assertEquals("BroadcastReceiver still registered after disconnect", + 0, mRegisteredReceivers.size()); + } + quitThreads(); + quitExecutorServices(); + } + + private void quitExecutorServices() throws Exception { + quitResourcesThat(() -> { + synchronized (mExecutorServiceToBeCleared) { + final ArrayList<ExecutorService> ret = new ArrayList<>(mExecutorServiceToBeCleared); + mExecutorServiceToBeCleared.clear(); + return ret; + } + }, (it) -> { + final ExecutorService ecs = (ExecutorService) it; + ecs.awaitTermination(HANDLER_TIMEOUT_MS, TimeUnit.MILLISECONDS); + }); + } + + private void quitThreads() throws Exception { + quitResourcesThat(() -> { + synchronized (mThreadsToBeCleared) { + final ArrayList<Thread> ret = new ArrayList<>(mThreadsToBeCleared); + mThreadsToBeCleared.clear(); + return ret; + } + }, (it) -> { + final Thread th = (Thread) it; + th.interrupt(); + th.join(HANDLER_TIMEOUT_MS); + if (th.isAlive()) fail("Threads did not terminate within timeout."); + }); } @After - public void tearDown() { + public void tearDown() throws Exception { mFakeDns.clearAll(); - // Make a local copy of mCreatedNetworkMonitors because during the iteration below, - // WrappedNetworkMonitor#onQuitting will delete elements from it on the handler threads. - WrappedNetworkMonitor[] networkMonitors = mCreatedNetworkMonitors.toArray( - new WrappedNetworkMonitor[0]); - for (WrappedNetworkMonitor nm : networkMonitors) { - nm.notifyNetworkDisconnected(); - nm.awaitQuit(); - } - assertEquals("NetworkMonitor still running after disconnect", - 0, mCreatedNetworkMonitors.size()); - assertEquals("BroadcastReceiver still registered after disconnect", - 0, mRegisteredReceivers.size()); + quitNetworkMonitors(); + // Clear mocks to prevent from stubs holding instances and cause memory leaks. + Mockito.framework().clearInlineMocks(); } private void initHttpConnection(HttpURLConnection connection) { @@ -777,7 +863,6 @@ @Override protected void onQuitting() { super.onQuitting(); - assertTrue(mCreatedNetworkMonitors.remove(this)); mQuitCv.open(); } @@ -1099,7 +1184,9 @@ verify(mContext, never()).registerReceiver(receiverCaptor.capture(), argThat(receiver -> ACTION_CONFIGURATION_CHANGED.equals(receiver.getAction(0)))); nm.start(); - mCreatedNetworkMonitors.add(nm); + synchronized (mCreatedNetworkMonitors) { + mCreatedNetworkMonitors.add(nm); + } HandlerUtils.waitForIdle(nm.getHandler(), HANDLER_TIMEOUT_MS); verify(mContext, times(1)).registerReceiver(receiverCaptor.capture(), argThat(receiver -> ACTION_CONFIGURATION_CHANGED.equals(receiver.getAction(0)))); @@ -3701,6 +3788,8 @@ // started. If captive portal app receiver is registered, then the size of the registered // receivers will be 2. Otherwise, mRegisteredReceivers should only contain 1 configuration // change receiver. - assertEquals(isPortal ? 2 : 1, mRegisteredReceivers.size()); + synchronized (mRegisteredReceivers) { + assertEquals(isPortal ? 2 : 1, mRegisteredReceivers.size()); + } } }