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 <linux/pfkeyv2.h>
19 #include <sys/socket.h>
20 #include <jni.h>
21 #include <nativehelper/JNIHelp.h>
22 #include <nativehelper/ScopedLocalRef.h>
23 
24 #include "nativehelper/scoped_primitive_array.h"
25 #include "nativehelper/scoped_utf_chars.h"
26 
27 #define BPF_FD_JUST_USE_INT
28 #include "BpfSyscallWrappers.h"
29 
30 #include "bpf/KernelUtils.h"
31 
32 namespace android {
33 
com_android_net_module_util_BpfMap_nativeBpfFdGet(JNIEnv * env,jclass clazz,jstring path,jint mode,jint keySize,jint valueSize)34 static jint com_android_net_module_util_BpfMap_nativeBpfFdGet(JNIEnv *env, jclass clazz,
35         jstring path, jint mode, jint keySize, jint valueSize) {
36     ScopedUtfChars pathname(env, path);
37 
38     jint fd = -1;
39     switch (mode) {
40       case 0:
41         fd = bpf::mapRetrieveRW(pathname.c_str());
42         break;
43       case BPF_F_RDONLY:
44         fd = bpf::mapRetrieveRO(pathname.c_str());
45         break;
46       case BPF_F_WRONLY:
47         fd = bpf::mapRetrieveWO(pathname.c_str());
48         break;
49       case BPF_F_RDONLY|BPF_F_WRONLY:
50         fd = bpf::mapRetrieveExclusiveRW(pathname.c_str());
51         break;
52       default:
53         errno = EINVAL;
54         break;
55     }
56 
57     if (fd < 0) {
58         jniThrowErrnoException(env, "nativeBpfFdGet", errno);
59         return -1;
60     }
61 
62     if (bpf::isAtLeastKernelVersion(4, 14, 0)) {
63         // These likely fail with -1 and set errno to EINVAL on <4.14
64         if (bpf::bpfGetFdKeySize(fd) != keySize) {
65             close(fd);
66             jniThrowErrnoException(env, "nativeBpfFdGet KeySize", EBADFD);
67             return -1;
68         }
69         if (bpf::bpfGetFdValueSize(fd) != valueSize) {
70             close(fd);
71             jniThrowErrnoException(env, "nativeBpfFdGet ValueSize", EBADFD);
72             return -1;
73         }
74     }
75 
76     return fd;
77 }
78 
com_android_net_module_util_BpfMap_nativeWriteToMapEntry(JNIEnv * env,jobject self,jint fd,jbyteArray key,jbyteArray value,jint flags)79 static void com_android_net_module_util_BpfMap_nativeWriteToMapEntry(JNIEnv *env, jobject self,
80         jint fd, jbyteArray key, jbyteArray value, jint flags) {
81     ScopedByteArrayRO keyRO(env, key);
82     ScopedByteArrayRO valueRO(env, value);
83 
84     int ret = bpf::writeToMapEntry(static_cast<int>(fd), keyRO.get(), valueRO.get(),
85             static_cast<int>(flags));
86 
87     if (ret) jniThrowErrnoException(env, "nativeWriteToMapEntry", errno);
88 }
89 
throwIfNotEnoent(JNIEnv * env,const char * functionName,int ret,int err)90 static jboolean throwIfNotEnoent(JNIEnv *env, const char* functionName, int ret, int err) {
91     if (ret == 0) return true;
92 
93     if (err != ENOENT) jniThrowErrnoException(env, functionName, err);
94     return false;
95 }
96 
com_android_net_module_util_BpfMap_nativeDeleteMapEntry(JNIEnv * env,jobject self,jint fd,jbyteArray key)97 static jboolean com_android_net_module_util_BpfMap_nativeDeleteMapEntry(JNIEnv *env, jobject self,
98         jint fd, jbyteArray key) {
99     ScopedByteArrayRO keyRO(env, key);
100 
101     // On success, zero is returned.  If the element is not found, -1 is returned and errno is set
102     // to ENOENT.
103     int ret = bpf::deleteMapEntry(static_cast<int>(fd), keyRO.get());
104 
105     return throwIfNotEnoent(env, "nativeDeleteMapEntry", ret, errno);
106 }
107 
com_android_net_module_util_BpfMap_nativeGetNextMapKey(JNIEnv * env,jobject self,jint fd,jbyteArray key,jbyteArray nextKey)108 static jboolean com_android_net_module_util_BpfMap_nativeGetNextMapKey(JNIEnv *env, jobject self,
109         jint fd, jbyteArray key, jbyteArray nextKey) {
110     // If key is found, the operation returns zero and sets the next key pointer to the key of the
111     // next element.  If key is not found, the operation returns zero and sets the next key pointer
112     // to the key of the first element.  If key is the last element, -1 is returned and errno is
113     // set to ENOENT.  Other possible errno values are ENOMEM, EFAULT, EPERM, and EINVAL.
114     ScopedByteArrayRW nextKeyRW(env, nextKey);
115     int ret;
116     if (key == nullptr) {
117         // Called by getFirstKey. Find the first key in the map.
118         ret = bpf::getNextMapKey(static_cast<int>(fd), nullptr, nextKeyRW.get());
119     } else {
120         ScopedByteArrayRO keyRO(env, key);
121         ret = bpf::getNextMapKey(static_cast<int>(fd), keyRO.get(), nextKeyRW.get());
122     }
123 
124     return throwIfNotEnoent(env, "nativeGetNextMapKey", ret, errno);
125 }
126 
com_android_net_module_util_BpfMap_nativeFindMapEntry(JNIEnv * env,jobject self,jint fd,jbyteArray key,jbyteArray value)127 static jboolean com_android_net_module_util_BpfMap_nativeFindMapEntry(JNIEnv *env, jobject self,
128         jint fd, jbyteArray key, jbyteArray value) {
129     ScopedByteArrayRO keyRO(env, key);
130     ScopedByteArrayRW valueRW(env, value);
131 
132     // If an element is found, the operation returns zero and stores the element's value into
133     // "value".  If no element is found, the operation returns -1 and sets errno to ENOENT.
134     int ret = bpf::findMapEntry(static_cast<int>(fd), keyRO.get(), valueRW.get());
135 
136     return throwIfNotEnoent(env, "nativeFindMapEntry", ret, errno);
137 }
138 
com_android_net_module_util_BpfMap_nativeSynchronizeKernelRCU(JNIEnv * env,jclass clazz)139 static void com_android_net_module_util_BpfMap_nativeSynchronizeKernelRCU(JNIEnv *env,
140                                                                           jclass clazz) {
141     const int pfSocket = socket(AF_KEY, SOCK_RAW | SOCK_CLOEXEC, PF_KEY_V2);
142 
143     if (pfSocket < 0) {
144         jniThrowErrnoException(env, "nativeSynchronizeKernelRCU:socket", errno);
145         return;
146     }
147 
148     if (close(pfSocket)) {
149         jniThrowErrnoException(env, "nativeSynchronizeKernelRCU:close", errno);
150         return;
151     }
152     return;
153 }
154 
155 /*
156  * JNI registration.
157  */
158 static const JNINativeMethod gMethods[] = {
159     /* name, signature, funcPtr */
160     { "nativeBpfFdGet", "(Ljava/lang/String;III)I",
161         (void*) com_android_net_module_util_BpfMap_nativeBpfFdGet },
162     { "nativeWriteToMapEntry", "(I[B[BI)V",
163         (void*) com_android_net_module_util_BpfMap_nativeWriteToMapEntry },
164     { "nativeDeleteMapEntry", "(I[B)Z",
165         (void*) com_android_net_module_util_BpfMap_nativeDeleteMapEntry },
166     { "nativeGetNextMapKey", "(I[B[B)Z",
167         (void*) com_android_net_module_util_BpfMap_nativeGetNextMapKey },
168     { "nativeFindMapEntry", "(I[B[B)Z",
169         (void*) com_android_net_module_util_BpfMap_nativeFindMapEntry },
170     { "nativeSynchronizeKernelRCU", "()V",
171         (void*) com_android_net_module_util_BpfMap_nativeSynchronizeKernelRCU },
172 
173 };
174 
register_com_android_net_module_util_BpfMap(JNIEnv * env,char const * class_name)175 int register_com_android_net_module_util_BpfMap(JNIEnv* env, char const* class_name) {
176     return jniRegisterNativeMethods(env,
177             class_name,
178             gMethods, NELEM(gMethods));
179 }
180 
181 }; // namespace android
182