diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp index 72c83fa7e3..611f100678 100644 --- a/Tethering/apex/Android.bp +++ b/Tethering/apex/Android.bp @@ -51,7 +51,8 @@ apex { first: { jni_libs: [ "libservice-connectivity", - "libcom_android_connectivity_com_android_net_module_util_jni" + "libcom_android_connectivity_com_android_net_module_util_jni", + "libtraffic_controller_jni", ], }, both: { diff --git a/service/native/Android.bp b/service/native/Android.bp index eb2183d2ea..581631843f 100644 --- a/service/native/Android.bp +++ b/service/native/Android.bp @@ -48,6 +48,37 @@ cc_library { min_sdk_version: "30", } +cc_library_shared { + name: "libtraffic_controller_jni", + cflags: [ + "-Wall", + "-Werror", + "-Wno-unused-parameter", + "-Wthread-safety", + ], + srcs: [ + "jni/*.cpp", + ], + header_libs: [ + "bpf_connectivity_headers", + ], + static_libs: [ + "libnetdutils", + "libtraffic_controller", + "netd_aidl_interface-lateststable-ndk", + ], + shared_libs: [ + "libbase", + "liblog", + "libutils", + "libnativehelper", + ], + apex_available: [ + "com.android.tethering", + ], + min_sdk_version: "30", +} + cc_test { name: "traffic_controller_unit_test", test_suites: ["general-tests"], diff --git a/service/native/jni/com_android_server_BpfNetMaps.cpp b/service/native/jni/com_android_server_BpfNetMaps.cpp new file mode 100644 index 0000000000..7ab4d46ceb --- /dev/null +++ b/service/native/jni/com_android_server_BpfNetMaps.cpp @@ -0,0 +1,266 @@ +/* + * 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. + */ + +#define LOG_TAG "TrafficControllerJni" + +#include +#include +#include +#include +#include +#include + +#include "TrafficController.h" +#include "android-base/logging.h" +#include "bpf_shared.h" +#include "utils/Log.h" + +using android::net::TrafficController; +using android::netdutils::Status; + +using UidOwnerMatchType::PENALTY_BOX_MATCH; +using UidOwnerMatchType::HAPPY_BOX_MATCH; + +static android::net::TrafficController mTc; + +namespace android { + +static void native_init(JNIEnv* env, jobject clazz) { + Status status = mTc.start(); + if (!isOk(status)) { + ALOGE("%s failed", __func__); + } +} + +static jint native_addNaughtyApp(JNIEnv* env, jobject clazz, jint uid) { + const uint32_t appUids = static_cast(abs(uid)); + Status status = mTc.updateUidOwnerMap(appUids, PENALTY_BOX_MATCH, + TrafficController::IptOp::IptOpInsert); + if (!isOk(status)) { + ALOGE("%s failed, errer code = %d", __func__, status.code()); + } + return (jint)status.code(); +} + +static jint native_removeNaughtyApp(JNIEnv* env, jobject clazz, jint uid) { + const uint32_t appUids = static_cast(abs(uid)); + Status status = mTc.updateUidOwnerMap(appUids, PENALTY_BOX_MATCH, + TrafficController::IptOp::IptOpDelete); + if (!isOk(status)) { + ALOGE("%s failed, errer code = %d", __func__, status.code()); + } + return (jint)status.code(); +} + +static jint native_addNiceApp(JNIEnv* env, jobject clazz, jint uid) { + const uint32_t appUids = static_cast(abs(uid)); + Status status = mTc.updateUidOwnerMap(appUids, HAPPY_BOX_MATCH, + TrafficController::IptOp::IptOpInsert); + if (!isOk(status)) { + ALOGE("%s failed, errer code = %d", __func__, status.code()); + } + return (jint)status.code(); +} + +static jint native_removeNiceApp(JNIEnv* env, jobject clazz, jint uid) { + const uint32_t appUids = static_cast(abs(uid)); + Status status = mTc.updateUidOwnerMap(appUids, HAPPY_BOX_MATCH, + TrafficController::IptOp::IptOpDelete); + if (!isOk(status)) { + ALOGD("%s failed, errer code = %d", __func__, status.code()); + } + return (jint)status.code(); +} + +static jint native_setChildChain(JNIEnv* env, jobject clazz, jint childChain, jboolean enable) { + auto chain = static_cast(childChain); + int res = mTc.toggleUidOwnerMap(chain, enable); + if (res) { + ALOGE("%s failed, error code = %d", __func__, res); + } + return (jint)res; +} + +static jint native_replaceUidChain(JNIEnv* env, jobject clazz, jstring name, jboolean isAllowlist, + jintArray jUids) { + const ScopedUtfChars chainNameUtf8(env, name); + if (chainNameUtf8.c_str() == nullptr) { + return -EINVAL; + } + const std::string chainName(chainNameUtf8.c_str()); + + ScopedIntArrayRO uids(env, jUids); + if (uids.get() == nullptr) { + return -EINVAL; + } + + size_t size = uids.size(); + std::vector data ((int32_t *)&uids[0], (int32_t*)&uids[size]); + int res = mTc.replaceUidOwnerMap(chainName, isAllowlist, data); + if (res) { + ALOGE("%s failed, error code = %d", __func__, res); + } + return (jint)res; +} + +static FirewallType getFirewallType(ChildChain chain) { + switch (chain) { + case DOZABLE: + return ALLOWLIST; + case STANDBY: + return DENYLIST; + case POWERSAVE: + return ALLOWLIST; + case RESTRICTED: + return ALLOWLIST; + case NONE: + default: + return DENYLIST; + } +} + +static jint native_setUidRule(JNIEnv* env, jobject clazz, jint childChain, jint uid, + jint firewallRule) { + auto chain = static_cast(childChain); + auto rule = static_cast(firewallRule); + FirewallType fType = getFirewallType(chain); + + int res = mTc.changeUidOwnerRule(chain, uid, rule, fType); + if (res) { + ALOGE("%s failed, error code = %d", __func__, res); + } + return (jint)res; +} + +static jint native_addUidInterfaceRules(JNIEnv* env, jobject clazz, jstring ifName, + jintArray jUids) { + const ScopedUtfChars ifNameUtf8(env, ifName); + if (ifNameUtf8.c_str() == nullptr) { + return -EINVAL; + } + const std::string interfaceName(ifNameUtf8.c_str()); + const int ifIndex = if_nametoindex(interfaceName.c_str()); + + ScopedIntArrayRO uids(env, jUids); + if (uids.get() == nullptr) { + return -EINVAL; + } + + size_t size = uids.size(); + std::vector data ((int32_t *)&uids[0], (int32_t*)&uids[size]); + Status status = mTc.addUidInterfaceRules(ifIndex, data); + if (!isOk(status)) { + ALOGE("%s failed, error code = %d", __func__, status.code()); + } + return (jint)status.code(); +} + +static jint native_removeUidInterfaceRules(JNIEnv* env, jobject clazz, jintArray jUids) { + ScopedIntArrayRO uids(env, jUids); + if (uids.get() == nullptr) { + return -EINVAL; + } + + size_t size = uids.size(); + std::vector data ((int32_t *)&uids[0], (int32_t*)&uids[size]); + Status status = mTc.removeUidInterfaceRules(data); + if (!isOk(status)) { + ALOGE("%s failed, error code = %d", __func__, status.code()); + } + return (jint)status.code(); +} + +static jint native_swapActiveStatsMap(JNIEnv* env, jobject clazz) { + Status status = mTc.swapActiveStatsMap(); + if (!isOk(status)) { + ALOGD("%s failed, error code = %d", __func__, status.code()); + } + return (jint)status.code(); +} + +static void native_setPermissionForUids(JNIEnv* env, jobject clazz, jint permission, + jintArray jUids) { + ScopedIntArrayRO uids(env, jUids); + if (uids.get() == nullptr) return; + + size_t size = uids.size(); + static_assert(sizeof(*(uids.get())) == sizeof(uid_t)); + std::vector data ((uid_t *)&uids[0], (uid_t*)&uids[size]); + mTc.setPermissionForUids(permission, data); +} + +static jint native_setCounterSet(JNIEnv* env, jobject clazz, jint setNum, jint uid) { + uid_t callingUid = getuid(); + int res = mTc.setCounterSet(setNum, (uid_t)uid, callingUid); + if (res) { + ALOGE("%s failed, error code = %d", __func__, res); + } + return (jint)res; +} + +static jint native_deleteTagData(JNIEnv* env, jobject clazz, jint tagNum, jint uid) { + uid_t callingUid = getuid(); + int res = mTc.deleteTagData(tagNum, (uid_t)uid, callingUid); + if (res) { + ALOGE("%s failed, error code = %d", __func__, res); + } + return (jint)res; +} + +/* + * JNI registration. + */ +// clang-format off +static const JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + {"native_init", "()V", + (void*)native_init}, + {"native_addNaughtyApp", "(I)I", + (void*)native_addNaughtyApp}, + {"native_removeNaughtyApp", "(I)I", + (void*)native_removeNaughtyApp}, + {"native_addNiceApp", "(I)I", + (void*)native_addNiceApp}, + {"native_removeNiceApp", "(I)I", + (void*)native_removeNiceApp}, + {"native_setChildChain", "(IZ)I", + (void*)native_setChildChain}, + {"native_replaceUidChain", "(Ljava/lang/String;Z[I)I", + (void*)native_replaceUidChain}, + {"native_setUidRule", "(III)I", + (void*)native_setUidRule}, + {"native_addUidInterfaceRules", "(Ljava/lang/String;[I)I", + (void*)native_addUidInterfaceRules}, + {"native_removeUidInterfaceRules", "([I)I", + (void*)native_removeUidInterfaceRules}, + {"native_swapActiveStatsMap", "()I", + (void*)native_swapActiveStatsMap}, + {"native_setPermissionForUids", "(I[I)V", + (void*)native_setPermissionForUids}, + {"native_setCounterSet", "(II)I", + (void*)native_setCounterSet}, + {"native_deleteTagData", "(II)I", + (void*)native_deleteTagData}, +}; +// clang-format on + +int register_com_android_server_BpfNetMaps(JNIEnv* env) { + return jniRegisterNativeMethods(env, + "com/android/server/BpfNetMaps", + gMethods, NELEM(gMethods)); +} + +}; // namespace android diff --git a/service/native/jni/onload.cpp b/service/native/jni/onload.cpp new file mode 100644 index 0000000000..df7c77bfb6 --- /dev/null +++ b/service/native/jni/onload.cpp @@ -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. + */ + +#define LOG_TAG "TrafficControllerJni" + +#include +#include + +#include "utils/Log.h" + +namespace android { + +int register_com_android_server_BpfNetMaps(JNIEnv* env); + +extern "C" jint JNI_OnLoad(JavaVM* vm, void*) { + JNIEnv *env; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + ALOGE("%s: ERROR: GetEnv failed", __func__); + return JNI_ERR; + } + + if (register_com_android_server_BpfNetMaps(env) < 0) + return JNI_ERR; + + return JNI_VERSION_1_6; +} + +}; // namespace android diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java index bc63eef698..a6909c079d 100644 --- a/service/src/com/android/server/BpfNetMaps.java +++ b/service/src/com/android/server/BpfNetMaps.java @@ -28,6 +28,11 @@ import android.util.Log; public class BpfNetMaps { private static final String TAG = "BpfNetMaps"; + static { + System.loadLibrary("traffic_controller_jni"); + native_init(); + } + /** * Add naughty app bandwidth rule for specific app * @@ -239,6 +244,7 @@ public class BpfNetMaps { return -err; } + private static native void native_init(); private native int native_addNaughtyApp(int uid); private native int native_removeNaughtyApp(int uid); private native int native_addNiceApp(int uid);