/* * 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 "TrafficController.h" #include #include #include #include #include #include #include #include #include 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, error code = %d", __func__, status.code()); } } 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, error 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, error 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, error 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, error 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(); static_assert(sizeof(*(uids.get())) == sizeof(int32_t)); 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 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 = mTc.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) { // Null ifName is a wildcard to allow apps to receive packets on all interfaces and ifIndex is // set to 0. int ifIndex; if (ifName != nullptr) { const ScopedUtfChars ifNameUtf8(env, ifName); const std::string interfaceName(ifNameUtf8.c_str()); ifIndex = if_nametoindex(interfaceName.c_str()); } else { ifIndex = 0; } ScopedIntArrayRO uids(env, jUids); if (uids.get() == nullptr) { return -EINVAL; } size_t size = uids.size(); static_assert(sizeof(*(uids.get())) == sizeof(int32_t)); 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(); static_assert(sizeof(*(uids.get())) == sizeof(int32_t)); 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 void native_dump(JNIEnv* env, jobject clazz, jobject javaFd, jboolean verbose) { int fd = netjniutils::GetNativeFileDescriptor(env, javaFd); if (fd < 0) { jniThrowExceptionFmt(env, "java/io/IOException", "Invalid file descriptor"); return; } mTc.dump(fd, verbose); } /* * 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_dump", "(Ljava/io/FileDescriptor;Z)V", (void*)native_dump}, }; // clang-format on int register_com_android_server_BpfNetMaps(JNIEnv* env) { return jniRegisterNativeMethods(env, "com/android/server/BpfNetMaps", gMethods, NELEM(gMethods)); } }; // namespace android