1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "TrafficControllerJni"
18 
19 #include "TrafficController.h"
20 
21 #include "netd.h"
22 
23 #include <jni.h>
24 #include <log/log.h>
25 #include <nativehelper/JNIHelp.h>
26 #include <nativehelper/ScopedUtfChars.h>
27 #include <nativehelper/ScopedPrimitiveArray.h>
28 #include <netjniutils/netjniutils.h>
29 #include <net/if.h>
30 #include <private/android_filesystem_config.h>
31 #include <unistd.h>
32 #include <vector>
33 
34 
35 using android::net::TrafficController;
36 using android::netdutils::Status;
37 
38 using UidOwnerMatchType::PENALTY_BOX_MATCH;
39 using UidOwnerMatchType::HAPPY_BOX_MATCH;
40 
41 static android::net::TrafficController mTc;
42 
43 namespace android {
44 
45 #define CHECK_LOG(status) \
46   do { \
47     if (!isOk(status)) \
48       ALOGE("%s failed, error code = %d", __func__, status.code()); \
49   } while (0)
50 
native_init(JNIEnv * env,jclass clazz,jboolean startSkDestroyListener)51 static void native_init(JNIEnv* env, jclass clazz, jboolean startSkDestroyListener) {
52   Status status = mTc.start(startSkDestroyListener);
53   CHECK_LOG(status);
54   if (!isOk(status)) {
55     uid_t uid = getuid();
56     ALOGE("BpfNetMaps jni init failure as uid=%d", uid);
57     // TODO: Fix tests to not use this jni lib, so we can unconditionally abort()
58     if (uid == AID_SYSTEM || uid == AID_NETWORK_STACK) abort();
59   }
60 }
61 
native_addNaughtyApp(JNIEnv * env,jobject self,jint uid)62 static jint native_addNaughtyApp(JNIEnv* env, jobject self, jint uid) {
63   const uint32_t appUids = static_cast<uint32_t>(abs(uid));
64   Status status = mTc.updateUidOwnerMap(appUids, PENALTY_BOX_MATCH,
65       TrafficController::IptOp::IptOpInsert);
66   CHECK_LOG(status);
67   return (jint)status.code();
68 }
69 
native_removeNaughtyApp(JNIEnv * env,jobject self,jint uid)70 static jint native_removeNaughtyApp(JNIEnv* env, jobject self, jint uid) {
71   const uint32_t appUids = static_cast<uint32_t>(abs(uid));
72   Status status = mTc.updateUidOwnerMap(appUids, PENALTY_BOX_MATCH,
73       TrafficController::IptOp::IptOpDelete);
74   CHECK_LOG(status);
75   return (jint)status.code();
76 }
77 
native_addNiceApp(JNIEnv * env,jobject self,jint uid)78 static jint native_addNiceApp(JNIEnv* env, jobject self, jint uid) {
79   const uint32_t appUids = static_cast<uint32_t>(abs(uid));
80   Status status = mTc.updateUidOwnerMap(appUids, HAPPY_BOX_MATCH,
81       TrafficController::IptOp::IptOpInsert);
82   CHECK_LOG(status);
83   return (jint)status.code();
84 }
85 
native_removeNiceApp(JNIEnv * env,jobject self,jint uid)86 static jint native_removeNiceApp(JNIEnv* env, jobject self, jint uid) {
87   const uint32_t appUids = static_cast<uint32_t>(abs(uid));
88   Status status = mTc.updateUidOwnerMap(appUids, HAPPY_BOX_MATCH,
89       TrafficController::IptOp::IptOpDelete);
90   CHECK_LOG(status);
91   return (jint)status.code();
92 }
93 
native_setChildChain(JNIEnv * env,jobject self,jint childChain,jboolean enable)94 static jint native_setChildChain(JNIEnv* env, jobject self, jint childChain, jboolean enable) {
95   auto chain = static_cast<ChildChain>(childChain);
96   int res = mTc.toggleUidOwnerMap(chain, enable);
97   if (res) ALOGE("%s failed, error code = %d", __func__, res);
98   return (jint)res;
99 }
100 
native_replaceUidChain(JNIEnv * env,jobject self,jstring name,jboolean isAllowlist,jintArray jUids)101 static jint native_replaceUidChain(JNIEnv* env, jobject self, jstring name, jboolean isAllowlist,
102                                    jintArray jUids) {
103     const ScopedUtfChars chainNameUtf8(env, name);
104     if (chainNameUtf8.c_str() == nullptr) return -EINVAL;
105     const std::string chainName(chainNameUtf8.c_str());
106 
107     ScopedIntArrayRO uids(env, jUids);
108     if (uids.get() == nullptr) return -EINVAL;
109 
110     size_t size = uids.size();
111     static_assert(sizeof(*(uids.get())) == sizeof(int32_t));
112     std::vector<int32_t> data ((int32_t *)&uids[0], (int32_t*)&uids[size]);
113     int res = mTc.replaceUidOwnerMap(chainName, isAllowlist, data);
114     if (res) ALOGE("%s failed, error code = %d", __func__, res);
115     return (jint)res;
116 }
117 
native_setUidRule(JNIEnv * env,jobject self,jint childChain,jint uid,jint firewallRule)118 static jint native_setUidRule(JNIEnv* env, jobject self, jint childChain, jint uid,
119                               jint firewallRule) {
120     auto chain = static_cast<ChildChain>(childChain);
121     auto rule = static_cast<FirewallRule>(firewallRule);
122     FirewallType fType = mTc.getFirewallType(chain);
123 
124     int res = mTc.changeUidOwnerRule(chain, uid, rule, fType);
125     if (res) ALOGE("%s failed, error code = %d", __func__, res);
126     return (jint)res;
127 }
128 
native_addUidInterfaceRules(JNIEnv * env,jobject self,jstring ifName,jintArray jUids)129 static jint native_addUidInterfaceRules(JNIEnv* env, jobject self, jstring ifName,
130                                         jintArray jUids) {
131     // Null ifName is a wildcard to allow apps to receive packets on all interfaces and ifIndex is
132     // set to 0.
133     int ifIndex = 0;
134     if (ifName != nullptr) {
135         const ScopedUtfChars ifNameUtf8(env, ifName);
136         const std::string interfaceName(ifNameUtf8.c_str());
137         ifIndex = if_nametoindex(interfaceName.c_str());
138     }
139 
140     ScopedIntArrayRO uids(env, jUids);
141     if (uids.get() == nullptr) return -EINVAL;
142 
143     size_t size = uids.size();
144     static_assert(sizeof(*(uids.get())) == sizeof(int32_t));
145     std::vector<int32_t> data ((int32_t *)&uids[0], (int32_t*)&uids[size]);
146     Status status = mTc.addUidInterfaceRules(ifIndex, data);
147     CHECK_LOG(status);
148     return (jint)status.code();
149 }
150 
native_removeUidInterfaceRules(JNIEnv * env,jobject self,jintArray jUids)151 static jint native_removeUidInterfaceRules(JNIEnv* env, jobject self, jintArray jUids) {
152     ScopedIntArrayRO uids(env, jUids);
153     if (uids.get() == nullptr) return -EINVAL;
154 
155     size_t size = uids.size();
156     static_assert(sizeof(*(uids.get())) == sizeof(int32_t));
157     std::vector<int32_t> data ((int32_t *)&uids[0], (int32_t*)&uids[size]);
158     Status status = mTc.removeUidInterfaceRules(data);
159     CHECK_LOG(status);
160     return (jint)status.code();
161 }
162 
native_updateUidLockdownRule(JNIEnv * env,jobject self,jint uid,jboolean add)163 static jint native_updateUidLockdownRule(JNIEnv* env, jobject self, jint uid, jboolean add) {
164     Status status = mTc.updateUidLockdownRule(uid, add);
165     CHECK_LOG(status);
166     return (jint)status.code();
167 }
168 
native_swapActiveStatsMap(JNIEnv * env,jobject self)169 static jint native_swapActiveStatsMap(JNIEnv* env, jobject self) {
170     Status status = mTc.swapActiveStatsMap();
171     CHECK_LOG(status);
172     return (jint)status.code();
173 }
174 
native_setPermissionForUids(JNIEnv * env,jobject self,jint permission,jintArray jUids)175 static void native_setPermissionForUids(JNIEnv* env, jobject self, jint permission,
176                                         jintArray jUids) {
177     ScopedIntArrayRO uids(env, jUids);
178     if (uids.get() == nullptr) return;
179 
180     size_t size = uids.size();
181     static_assert(sizeof(*(uids.get())) == sizeof(uid_t));
182     std::vector<uid_t> data ((uid_t *)&uids[0], (uid_t*)&uids[size]);
183     mTc.setPermissionForUids(permission, data);
184 }
185 
native_dump(JNIEnv * env,jobject self,jobject javaFd,jboolean verbose)186 static void native_dump(JNIEnv* env, jobject self, jobject javaFd, jboolean verbose) {
187     int fd = netjniutils::GetNativeFileDescriptor(env, javaFd);
188     if (fd < 0) {
189         jniThrowExceptionFmt(env, "java/io/IOException", "Invalid file descriptor");
190         return;
191     }
192     mTc.dump(fd, verbose);
193 }
194 
native_synchronizeKernelRCU(JNIEnv * env,jobject self)195 static jint native_synchronizeKernelRCU(JNIEnv* env, jobject self) {
196     return -bpf::synchronizeKernelRCU();
197 }
198 
199 /*
200  * JNI registration.
201  */
202 // clang-format off
203 static const JNINativeMethod gMethods[] = {
204     /* name, signature, funcPtr */
205     {"native_init", "(Z)V",
206     (void*)native_init},
207     {"native_addNaughtyApp", "(I)I",
208     (void*)native_addNaughtyApp},
209     {"native_removeNaughtyApp", "(I)I",
210     (void*)native_removeNaughtyApp},
211     {"native_addNiceApp", "(I)I",
212     (void*)native_addNiceApp},
213     {"native_removeNiceApp", "(I)I",
214     (void*)native_removeNiceApp},
215     {"native_setChildChain", "(IZ)I",
216     (void*)native_setChildChain},
217     {"native_replaceUidChain", "(Ljava/lang/String;Z[I)I",
218     (void*)native_replaceUidChain},
219     {"native_setUidRule", "(III)I",
220     (void*)native_setUidRule},
221     {"native_addUidInterfaceRules", "(Ljava/lang/String;[I)I",
222     (void*)native_addUidInterfaceRules},
223     {"native_removeUidInterfaceRules", "([I)I",
224     (void*)native_removeUidInterfaceRules},
225     {"native_updateUidLockdownRule", "(IZ)I",
226     (void*)native_updateUidLockdownRule},
227     {"native_swapActiveStatsMap", "()I",
228     (void*)native_swapActiveStatsMap},
229     {"native_setPermissionForUids", "(I[I)V",
230     (void*)native_setPermissionForUids},
231     {"native_dump", "(Ljava/io/FileDescriptor;Z)V",
232     (void*)native_dump},
233     {"native_synchronizeKernelRCU", "()I",
234     (void*)native_synchronizeKernelRCU},
235 };
236 // clang-format on
237 
register_com_android_server_BpfNetMaps(JNIEnv * env)238 int register_com_android_server_BpfNetMaps(JNIEnv* env) {
239     return jniRegisterNativeMethods(env, "android/net/connectivity/com/android/server/BpfNetMaps",
240                                     gMethods, NELEM(gMethods));
241 }
242 
243 }; // namespace android
244