1 /*
2 * Copyright (C) 2020 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 #include <errno.h>
18 #include <jni.h>
19 #include <nativehelper/JNIHelp.h>
20 #include <nativehelper/ScopedLocalRef.h>
21
22 #include "nativehelper/scoped_primitive_array.h"
23 #include "nativehelper/scoped_utf_chars.h"
24
25 #define BPF_FD_JUST_USE_INT
26 #include "BpfSyscallWrappers.h"
27
28 namespace android {
29
30 static jclass sErrnoExceptionClass;
31 static jmethodID sErrnoExceptionCtor2;
32 static jmethodID sErrnoExceptionCtor3;
33
throwErrnoException(JNIEnv * env,const char * functionName,int error)34 static void throwErrnoException(JNIEnv* env, const char* functionName, int error) {
35 if (sErrnoExceptionClass == nullptr || sErrnoExceptionClass == nullptr) return;
36
37 jthrowable cause = nullptr;
38 if (env->ExceptionCheck()) {
39 cause = env->ExceptionOccurred();
40 env->ExceptionClear();
41 }
42
43 ScopedLocalRef<jstring> msg(env, env->NewStringUTF(functionName));
44
45 // Not really much we can do here if msg is null, let's try to stumble on...
46 if (msg.get() == nullptr) env->ExceptionClear();
47
48 jobject errnoException;
49 if (cause != nullptr) {
50 errnoException = env->NewObject(sErrnoExceptionClass, sErrnoExceptionCtor3, msg.get(),
51 error, cause);
52 } else {
53 errnoException = env->NewObject(sErrnoExceptionClass, sErrnoExceptionCtor2, msg.get(),
54 error);
55 }
56 env->Throw(static_cast<jthrowable>(errnoException));
57 }
58
com_android_networkstack_tethering_BpfMap_closeMap(JNIEnv * env,jobject clazz,jint fd)59 static jint com_android_networkstack_tethering_BpfMap_closeMap(JNIEnv *env, jobject clazz,
60 jint fd) {
61 int ret = close(fd);
62
63 if (ret) throwErrnoException(env, "closeMap", errno);
64
65 return ret;
66 }
67
com_android_networkstack_tethering_BpfMap_bpfFdGet(JNIEnv * env,jobject clazz,jstring path,jint mode)68 static jint com_android_networkstack_tethering_BpfMap_bpfFdGet(JNIEnv *env, jobject clazz,
69 jstring path, jint mode) {
70 ScopedUtfChars pathname(env, path);
71
72 jint fd = bpf::bpfFdGet(pathname.c_str(), static_cast<unsigned>(mode));
73
74 return fd;
75 }
76
com_android_networkstack_tethering_BpfMap_writeToMapEntry(JNIEnv * env,jobject clazz,jint fd,jbyteArray key,jbyteArray value,jint flags)77 static void com_android_networkstack_tethering_BpfMap_writeToMapEntry(JNIEnv *env, jobject clazz,
78 jint fd, jbyteArray key, jbyteArray value, jint flags) {
79 ScopedByteArrayRO keyRO(env, key);
80 ScopedByteArrayRO valueRO(env, value);
81
82 int ret = bpf::writeToMapEntry(static_cast<int>(fd), keyRO.get(), valueRO.get(),
83 static_cast<int>(flags));
84
85 if (ret) throwErrnoException(env, "writeToMapEntry", errno);
86 }
87
throwIfNotEnoent(JNIEnv * env,const char * functionName,int ret,int err)88 static jboolean throwIfNotEnoent(JNIEnv *env, const char* functionName, int ret, int err) {
89 if (ret == 0) return true;
90
91 if (err != ENOENT) throwErrnoException(env, functionName, err);
92 return false;
93 }
94
com_android_networkstack_tethering_BpfMap_deleteMapEntry(JNIEnv * env,jobject clazz,jint fd,jbyteArray key)95 static jboolean com_android_networkstack_tethering_BpfMap_deleteMapEntry(JNIEnv *env, jobject clazz,
96 jint fd, jbyteArray key) {
97 ScopedByteArrayRO keyRO(env, key);
98
99 // On success, zero is returned. If the element is not found, -1 is returned and errno is set
100 // to ENOENT.
101 int ret = bpf::deleteMapEntry(static_cast<int>(fd), keyRO.get());
102
103 return throwIfNotEnoent(env, "deleteMapEntry", ret, errno);
104 }
105
com_android_networkstack_tethering_BpfMap_getNextMapKey(JNIEnv * env,jobject clazz,jint fd,jbyteArray key,jbyteArray nextKey)106 static jboolean com_android_networkstack_tethering_BpfMap_getNextMapKey(JNIEnv *env, jobject clazz,
107 jint fd, jbyteArray key, jbyteArray nextKey) {
108 // If key is found, the operation returns zero and sets the next key pointer to the key of the
109 // next element. If key is not found, the operation returns zero and sets the next key pointer
110 // to the key of the first element. If key is the last element, -1 is returned and errno is
111 // set to ENOENT. Other possible errno values are ENOMEM, EFAULT, EPERM, and EINVAL.
112 ScopedByteArrayRW nextKeyRW(env, nextKey);
113 int ret;
114 if (key == nullptr) {
115 // Called by getFirstKey. Find the first key in the map.
116 ret = bpf::getNextMapKey(static_cast<int>(fd), nullptr, nextKeyRW.get());
117 } else {
118 ScopedByteArrayRO keyRO(env, key);
119 ret = bpf::getNextMapKey(static_cast<int>(fd), keyRO.get(), nextKeyRW.get());
120 }
121
122 return throwIfNotEnoent(env, "getNextMapKey", ret, errno);
123 }
124
com_android_networkstack_tethering_BpfMap_findMapEntry(JNIEnv * env,jobject clazz,jint fd,jbyteArray key,jbyteArray value)125 static jboolean com_android_networkstack_tethering_BpfMap_findMapEntry(JNIEnv *env, jobject clazz,
126 jint fd, jbyteArray key, jbyteArray value) {
127 ScopedByteArrayRO keyRO(env, key);
128 ScopedByteArrayRW valueRW(env, value);
129
130 // If an element is found, the operation returns zero and stores the element's value into
131 // "value". If no element is found, the operation returns -1 and sets errno to ENOENT.
132 int ret = bpf::findMapEntry(static_cast<int>(fd), keyRO.get(), valueRW.get());
133
134 return throwIfNotEnoent(env, "findMapEntry", ret, errno);
135 }
136
137 /*
138 * JNI registration.
139 */
140 static const JNINativeMethod gMethods[] = {
141 /* name, signature, funcPtr */
142 { "closeMap", "(I)I",
143 (void*) com_android_networkstack_tethering_BpfMap_closeMap },
144 { "bpfFdGet", "(Ljava/lang/String;I)I",
145 (void*) com_android_networkstack_tethering_BpfMap_bpfFdGet },
146 { "writeToMapEntry", "(I[B[BI)V",
147 (void*) com_android_networkstack_tethering_BpfMap_writeToMapEntry },
148 { "deleteMapEntry", "(I[B)Z",
149 (void*) com_android_networkstack_tethering_BpfMap_deleteMapEntry },
150 { "getNextMapKey", "(I[B[B)Z",
151 (void*) com_android_networkstack_tethering_BpfMap_getNextMapKey },
152 { "findMapEntry", "(I[B[B)Z",
153 (void*) com_android_networkstack_tethering_BpfMap_findMapEntry },
154
155 };
156
register_com_android_networkstack_tethering_BpfMap(JNIEnv * env)157 int register_com_android_networkstack_tethering_BpfMap(JNIEnv* env) {
158 sErrnoExceptionClass = static_cast<jclass>(env->NewGlobalRef(
159 env->FindClass("android/system/ErrnoException")));
160 if (sErrnoExceptionClass == nullptr) return JNI_ERR;
161
162 sErrnoExceptionCtor2 = env->GetMethodID(sErrnoExceptionClass, "<init>",
163 "(Ljava/lang/String;I)V");
164 if (sErrnoExceptionCtor2 == nullptr) return JNI_ERR;
165
166 sErrnoExceptionCtor3 = env->GetMethodID(sErrnoExceptionClass, "<init>",
167 "(Ljava/lang/String;ILjava/lang/Throwable;)V");
168 if (sErrnoExceptionCtor3 == nullptr) return JNI_ERR;
169
170 return jniRegisterNativeMethods(env,
171 "com/android/networkstack/tethering/BpfMap",
172 gMethods, NELEM(gMethods));
173 }
174
175 }; // namespace android
176