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 #define LOG_TAG "jniClatCoordinator"
17
18 #include <arpa/inet.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <inttypes.h>
22 #include <linux/if_tun.h>
23 #include <linux/ioctl.h>
24 #include <log/log.h>
25 #include <nativehelper/JNIHelp.h>
26 #include <net/if.h>
27 #include <spawn.h>
28 #include <sys/wait.h>
29 #include <string>
30
31 #include <bpf/BpfMap.h>
32 #include <bpf/BpfUtils.h>
33 #include <bpf_shared.h>
34 #include <netjniutils/netjniutils.h>
35 #include <private/android_filesystem_config.h>
36
37 #include "libclat/clatutils.h"
38 #include "nativehelper/scoped_utf_chars.h"
39
40 // Sync from system/netd/include/netid_client.h
41 #define MARK_UNSET 0u
42
43 // Sync from system/netd/server/NetdConstants.h
44 #define __INT_STRLEN(i) sizeof(#i)
45 #define _INT_STRLEN(i) __INT_STRLEN(i)
46 #define INT32_STRLEN _INT_STRLEN(INT32_MIN)
47
48 #define DEVICEPREFIX "v4-"
49
50 namespace android {
51 static const char* kClatdPath = "/apex/com.android.tethering/bin/for-system/clatd";
52
throwIOException(JNIEnv * env,const char * msg,int error)53 static void throwIOException(JNIEnv* env, const char* msg, int error) {
54 jniThrowExceptionFmt(env, "java/io/IOException", "%s: %s", msg, strerror(error));
55 }
56
com_android_server_connectivity_ClatCoordinator_selectIpv4Address(JNIEnv * env,jobject clazz,jstring v4addr,jint prefixlen)57 jstring com_android_server_connectivity_ClatCoordinator_selectIpv4Address(JNIEnv* env,
58 jobject clazz,
59 jstring v4addr,
60 jint prefixlen) {
61 ScopedUtfChars address(env, v4addr);
62 in_addr ip;
63 if (inet_pton(AF_INET, address.c_str(), &ip) != 1) {
64 throwIOException(env, "invalid address", EINVAL);
65 return nullptr;
66 }
67
68 // Pick an IPv4 address.
69 // TODO: this picks the address based on other addresses that are assigned to interfaces, but
70 // the address is only actually assigned to an interface once clatd starts up. So we could end
71 // up with two clatd instances with the same IPv4 address.
72 // Stop doing this and instead pick a free one from the kV4Addr pool.
73 in_addr v4 = {net::clat::selectIpv4Address(ip, prefixlen)};
74 if (v4.s_addr == INADDR_NONE) {
75 jniThrowExceptionFmt(env, "java/io/IOException", "No free IPv4 address in %s/%d",
76 address.c_str(), prefixlen);
77 return nullptr;
78 }
79
80 char addrstr[INET_ADDRSTRLEN];
81 if (!inet_ntop(AF_INET, (void*)&v4, addrstr, sizeof(addrstr))) {
82 throwIOException(env, "invalid address", EADDRNOTAVAIL);
83 return nullptr;
84 }
85 return env->NewStringUTF(addrstr);
86 }
87
88 // Picks a random interface ID that is checksum neutral with the IPv4 address and the NAT64 prefix.
com_android_server_connectivity_ClatCoordinator_generateIpv6Address(JNIEnv * env,jobject clazz,jstring ifaceStr,jstring v4Str,jstring prefix64Str)89 jstring com_android_server_connectivity_ClatCoordinator_generateIpv6Address(
90 JNIEnv* env, jobject clazz, jstring ifaceStr, jstring v4Str, jstring prefix64Str) {
91 ScopedUtfChars iface(env, ifaceStr);
92 ScopedUtfChars addr4(env, v4Str);
93 ScopedUtfChars prefix64(env, prefix64Str);
94
95 if (iface.c_str() == nullptr) {
96 jniThrowExceptionFmt(env, "java/io/IOException", "Invalid null interface name");
97 return nullptr;
98 }
99
100 in_addr v4;
101 if (inet_pton(AF_INET, addr4.c_str(), &v4) != 1) {
102 jniThrowExceptionFmt(env, "java/io/IOException", "Invalid clat v4 address %s",
103 addr4.c_str());
104 return nullptr;
105 }
106
107 in6_addr nat64Prefix;
108 if (inet_pton(AF_INET6, prefix64.c_str(), &nat64Prefix) != 1) {
109 jniThrowExceptionFmt(env, "java/io/IOException", "Invalid prefix %s", prefix64.c_str());
110 return nullptr;
111 }
112
113 in6_addr v6;
114 if (net::clat::generateIpv6Address(iface.c_str(), v4, nat64Prefix, &v6)) {
115 jniThrowExceptionFmt(env, "java/io/IOException",
116 "Unable to find global source address on %s for %s", iface.c_str(),
117 prefix64.c_str());
118 return nullptr;
119 }
120
121 char addrstr[INET6_ADDRSTRLEN];
122 if (!inet_ntop(AF_INET6, (void*)&v6, addrstr, sizeof(addrstr))) {
123 throwIOException(env, "invalid address", EADDRNOTAVAIL);
124 return nullptr;
125 }
126 return env->NewStringUTF(addrstr);
127 }
128
com_android_server_connectivity_ClatCoordinator_createTunInterface(JNIEnv * env,jobject clazz,jstring tuniface)129 static jint com_android_server_connectivity_ClatCoordinator_createTunInterface(JNIEnv* env,
130 jobject clazz,
131 jstring tuniface) {
132 ScopedUtfChars v4interface(env, tuniface);
133
134 // open the tun device in non blocking mode as required by clatd
135 jint fd = open("/dev/net/tun", O_RDWR | O_NONBLOCK | O_CLOEXEC);
136 if (fd == -1) {
137 jniThrowExceptionFmt(env, "java/io/IOException", "open tun device failed (%s)",
138 strerror(errno));
139 return -1;
140 }
141
142 struct ifreq ifr = {
143 .ifr_flags = IFF_TUN,
144 };
145 strlcpy(ifr.ifr_name, v4interface.c_str(), sizeof(ifr.ifr_name));
146
147 if (ioctl(fd, TUNSETIFF, &ifr, sizeof(ifr))) {
148 close(fd);
149 jniThrowExceptionFmt(env, "java/io/IOException", "ioctl(TUNSETIFF) failed (%s)",
150 strerror(errno));
151 return -1;
152 }
153
154 return fd;
155 }
156
com_android_server_connectivity_ClatCoordinator_detectMtu(JNIEnv * env,jobject clazz,jstring platSubnet,jint plat_suffix,jint mark)157 static jint com_android_server_connectivity_ClatCoordinator_detectMtu(JNIEnv* env, jobject clazz,
158 jstring platSubnet,
159 jint plat_suffix, jint mark) {
160 ScopedUtfChars platSubnetStr(env, platSubnet);
161
162 in6_addr plat_subnet;
163 if (inet_pton(AF_INET6, platSubnetStr.c_str(), &plat_subnet) != 1) {
164 jniThrowExceptionFmt(env, "java/io/IOException", "Invalid plat prefix address %s",
165 platSubnetStr.c_str());
166 return -1;
167 }
168
169 int ret = net::clat::detect_mtu(&plat_subnet, plat_suffix, mark);
170 if (ret < 0) {
171 jniThrowExceptionFmt(env, "java/io/IOException", "detect mtu failed: %s", strerror(-ret));
172 return -1;
173 }
174
175 return ret;
176 }
177
com_android_server_connectivity_ClatCoordinator_openPacketSocket(JNIEnv * env,jobject clazz)178 static jint com_android_server_connectivity_ClatCoordinator_openPacketSocket(JNIEnv* env,
179 jobject clazz) {
180 // Will eventually be bound to htons(ETH_P_IPV6) protocol,
181 // but only after appropriate bpf filter is attached.
182 int sock = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
183 if (sock < 0) {
184 throwIOException(env, "packet socket failed", errno);
185 return -1;
186 }
187 return sock;
188 }
189
com_android_server_connectivity_ClatCoordinator_openRawSocket6(JNIEnv * env,jobject clazz,jint mark)190 static jint com_android_server_connectivity_ClatCoordinator_openRawSocket6(JNIEnv* env,
191 jobject clazz,
192 jint mark) {
193 int sock = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_RAW);
194 if (sock < 0) {
195 throwIOException(env, "raw socket failed", errno);
196 return -1;
197 }
198
199 // TODO: check the mark validation
200 if (mark != MARK_UNSET && setsockopt(sock, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) {
201 throwIOException(env, "could not set mark on raw socket", errno);
202 close(sock);
203 return -1;
204 }
205
206 return sock;
207 }
208
com_android_server_connectivity_ClatCoordinator_addAnycastSetsockopt(JNIEnv * env,jobject clazz,jobject javaFd,jstring addr6,jint ifindex)209 static void com_android_server_connectivity_ClatCoordinator_addAnycastSetsockopt(
210 JNIEnv* env, jobject clazz, jobject javaFd, jstring addr6, jint ifindex) {
211 int sock = netjniutils::GetNativeFileDescriptor(env, javaFd);
212 if (sock < 0) {
213 jniThrowExceptionFmt(env, "java/io/IOException", "Invalid file descriptor");
214 return;
215 }
216
217 ScopedUtfChars addrStr(env, addr6);
218
219 in6_addr addr;
220 if (inet_pton(AF_INET6, addrStr.c_str(), &addr) != 1) {
221 jniThrowExceptionFmt(env, "java/io/IOException", "Invalid IPv6 address %s",
222 addrStr.c_str());
223 return;
224 }
225
226 struct ipv6_mreq mreq = {addr, ifindex};
227 int ret = setsockopt(sock, SOL_IPV6, IPV6_JOIN_ANYCAST, &mreq, sizeof(mreq));
228 if (ret) {
229 jniThrowExceptionFmt(env, "java/io/IOException", "setsockopt IPV6_JOIN_ANYCAST failed: %s",
230 strerror(errno));
231 return;
232 }
233 }
234
com_android_server_connectivity_ClatCoordinator_configurePacketSocket(JNIEnv * env,jobject clazz,jobject javaFd,jstring addr6,jint ifindex)235 static void com_android_server_connectivity_ClatCoordinator_configurePacketSocket(
236 JNIEnv* env, jobject clazz, jobject javaFd, jstring addr6, jint ifindex) {
237 ScopedUtfChars addrStr(env, addr6);
238
239 int sock = netjniutils::GetNativeFileDescriptor(env, javaFd);
240 if (sock < 0) {
241 jniThrowExceptionFmt(env, "java/io/IOException", "Invalid file descriptor");
242 return;
243 }
244
245 in6_addr addr;
246 if (inet_pton(AF_INET6, addrStr.c_str(), &addr) != 1) {
247 jniThrowExceptionFmt(env, "java/io/IOException", "Invalid IPv6 address %s",
248 addrStr.c_str());
249 return;
250 }
251
252 int ret = net::clat::configure_packet_socket(sock, &addr, ifindex);
253 if (ret < 0) {
254 throwIOException(env, "configure packet socket failed", -ret);
255 return;
256 }
257 }
258
com_android_server_connectivity_ClatCoordinator_startClatd(JNIEnv * env,jobject clazz,jobject tunJavaFd,jobject readSockJavaFd,jobject writeSockJavaFd,jstring iface,jstring pfx96,jstring v4,jstring v6)259 static jint com_android_server_connectivity_ClatCoordinator_startClatd(
260 JNIEnv* env, jobject clazz, jobject tunJavaFd, jobject readSockJavaFd,
261 jobject writeSockJavaFd, jstring iface, jstring pfx96, jstring v4, jstring v6) {
262 ScopedUtfChars ifaceStr(env, iface);
263 ScopedUtfChars pfx96Str(env, pfx96);
264 ScopedUtfChars v4Str(env, v4);
265 ScopedUtfChars v6Str(env, v6);
266
267 int tunFd = netjniutils::GetNativeFileDescriptor(env, tunJavaFd);
268 if (tunFd < 0) {
269 jniThrowExceptionFmt(env, "java/io/IOException", "Invalid tun file descriptor");
270 return -1;
271 }
272
273 int readSock = netjniutils::GetNativeFileDescriptor(env, readSockJavaFd);
274 if (readSock < 0) {
275 jniThrowExceptionFmt(env, "java/io/IOException", "Invalid read socket");
276 return -1;
277 }
278
279 int writeSock = netjniutils::GetNativeFileDescriptor(env, writeSockJavaFd);
280 if (writeSock < 0) {
281 jniThrowExceptionFmt(env, "java/io/IOException", "Invalid write socket");
282 return -1;
283 }
284
285 // 1. these are the FD we'll pass to clatd on the cli, so need it as a string
286 char tunFdStr[INT32_STRLEN];
287 char sockReadStr[INT32_STRLEN];
288 char sockWriteStr[INT32_STRLEN];
289 snprintf(tunFdStr, sizeof(tunFdStr), "%d", tunFd);
290 snprintf(sockReadStr, sizeof(sockReadStr), "%d", readSock);
291 snprintf(sockWriteStr, sizeof(sockWriteStr), "%d", writeSock);
292
293 // 2. we're going to use this as argv[0] to clatd to make ps output more useful
294 std::string progname("clatd-");
295 progname += ifaceStr.c_str();
296
297 // clang-format off
298 const char* args[] = {progname.c_str(),
299 "-i", ifaceStr.c_str(),
300 "-p", pfx96Str.c_str(),
301 "-4", v4Str.c_str(),
302 "-6", v6Str.c_str(),
303 "-t", tunFdStr,
304 "-r", sockReadStr,
305 "-w", sockWriteStr,
306 nullptr};
307 // clang-format on
308
309 // 3. register vfork requirement
310 posix_spawnattr_t attr;
311 if (int ret = posix_spawnattr_init(&attr)) {
312 throwIOException(env, "posix_spawnattr_init failed", ret);
313 return -1;
314 }
315
316 // TODO: use android::base::ScopeGuard.
317 if (int ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_USEVFORK
318 #ifdef POSIX_SPAWN_CLOEXEC_DEFAULT
319 | POSIX_SPAWN_CLOEXEC_DEFAULT
320 #endif
321 )) {
322 posix_spawnattr_destroy(&attr);
323 throwIOException(env, "posix_spawnattr_setflags failed", ret);
324 return -1;
325 }
326
327 // 4. register dup2() action: this is what 'clears' the CLOEXEC flag
328 // on the tun fd that we want the child clatd process to inherit
329 // (this will happen after the vfork, and before the execve).
330 // Note that even though dup2(2) is a no-op if fd == new_fd but O_CLOEXEC flag will be removed.
331 // See implementation of bionic's posix_spawn_file_actions_adddup2().
332 posix_spawn_file_actions_t fa;
333 if (int ret = posix_spawn_file_actions_init(&fa)) {
334 posix_spawnattr_destroy(&attr);
335 throwIOException(env, "posix_spawn_file_actions_init failed", ret);
336 return -1;
337 }
338
339 if (int ret = posix_spawn_file_actions_adddup2(&fa, tunFd, tunFd)) {
340 posix_spawnattr_destroy(&attr);
341 posix_spawn_file_actions_destroy(&fa);
342 throwIOException(env, "posix_spawn_file_actions_adddup2 for tun fd failed", ret);
343 return -1;
344 }
345 if (int ret = posix_spawn_file_actions_adddup2(&fa, readSock, readSock)) {
346 posix_spawnattr_destroy(&attr);
347 posix_spawn_file_actions_destroy(&fa);
348 throwIOException(env, "posix_spawn_file_actions_adddup2 for read socket failed", ret);
349 return -1;
350 }
351 if (int ret = posix_spawn_file_actions_adddup2(&fa, writeSock, writeSock)) {
352 posix_spawnattr_destroy(&attr);
353 posix_spawn_file_actions_destroy(&fa);
354 throwIOException(env, "posix_spawn_file_actions_adddup2 for write socket failed", ret);
355 return -1;
356 }
357
358 // 5. actually perform vfork/dup2/execve
359 pid_t pid;
360 if (int ret = posix_spawn(&pid, kClatdPath, &fa, &attr, (char* const*)args, nullptr)) {
361 posix_spawnattr_destroy(&attr);
362 posix_spawn_file_actions_destroy(&fa);
363 throwIOException(env, "posix_spawn failed", ret);
364 return -1;
365 }
366
367 posix_spawnattr_destroy(&attr);
368 posix_spawn_file_actions_destroy(&fa);
369
370 return pid;
371 }
372
373 // Stop clatd process. SIGTERM with timeout first, if fail, SIGKILL.
374 // See stopProcess() in system/netd/server/NetdConstants.cpp.
375 // TODO: have a function stopProcess(int pid, const char *name) in common location and call it.
376 static constexpr int WAITPID_ATTEMPTS = 50;
377 static constexpr int WAITPID_RETRY_INTERVAL_US = 100000;
378
stopClatdProcess(int pid)379 static void stopClatdProcess(int pid) {
380 int err = kill(pid, SIGTERM);
381 if (err) {
382 err = errno;
383 }
384 if (err == ESRCH) {
385 ALOGE("clatd child process %d unexpectedly disappeared", pid);
386 return;
387 }
388 if (err) {
389 ALOGE("Error killing clatd child process %d: %s", pid, strerror(err));
390 }
391 int status = 0;
392 int ret = 0;
393 for (int count = 0; ret == 0 && count < WAITPID_ATTEMPTS; count++) {
394 usleep(WAITPID_RETRY_INTERVAL_US);
395 ret = waitpid(pid, &status, WNOHANG);
396 }
397 if (ret == 0) {
398 ALOGE("Failed to SIGTERM clatd pid=%d, try SIGKILL", pid);
399 // TODO: fix that kill failed or waitpid doesn't return.
400 kill(pid, SIGKILL);
401 ret = waitpid(pid, &status, 0);
402 }
403 if (ret == -1) {
404 ALOGE("Error waiting for clatd child process %d: %s", pid, strerror(errno));
405 } else {
406 ALOGD("clatd process %d terminated status=%d", pid, status);
407 }
408 }
409
com_android_server_connectivity_ClatCoordinator_stopClatd(JNIEnv * env,jobject clazz,jstring iface,jstring pfx96,jstring v4,jstring v6,jint pid)410 static void com_android_server_connectivity_ClatCoordinator_stopClatd(JNIEnv* env, jobject clazz,
411 jstring iface, jstring pfx96,
412 jstring v4, jstring v6,
413 jint pid) {
414 ScopedUtfChars ifaceStr(env, iface);
415 ScopedUtfChars pfx96Str(env, pfx96);
416 ScopedUtfChars v4Str(env, v4);
417 ScopedUtfChars v6Str(env, v6);
418
419 if (pid <= 0) {
420 jniThrowExceptionFmt(env, "java/io/IOException", "Invalid pid");
421 return;
422 }
423
424 stopClatdProcess(pid);
425 }
426
com_android_server_connectivity_ClatCoordinator_tagSocketAsClat(JNIEnv * env,jobject clazz,jobject sockJavaFd)427 static jlong com_android_server_connectivity_ClatCoordinator_tagSocketAsClat(
428 JNIEnv* env, jobject clazz, jobject sockJavaFd) {
429 int sockFd = netjniutils::GetNativeFileDescriptor(env, sockJavaFd);
430 if (sockFd < 0) {
431 jniThrowExceptionFmt(env, "java/io/IOException", "Invalid socket file descriptor");
432 return -1;
433 }
434
435 uint64_t sock_cookie = bpf::getSocketCookie(sockFd);
436 if (sock_cookie == bpf::NONEXISTENT_COOKIE) {
437 throwIOException(env, "get socket cookie failed", errno);
438 return -1;
439 }
440
441 bpf::BpfMap<uint64_t, UidTagValue> cookieTagMap;
442 auto res = cookieTagMap.init(COOKIE_TAG_MAP_PATH);
443 if (!res.ok()) {
444 throwIOException(env, "failed to init the cookieTagMap", res.error().code());
445 return -1;
446 }
447
448 // Tag raw socket with uid AID_CLAT and set tag as zero because tag is unused in bpf
449 // program for counting data usage in netd.c. Tagging socket is used to avoid counting
450 // duplicated clat traffic in bpf stat.
451 UidTagValue newKey = {.uid = (uint32_t)AID_CLAT, .tag = 0 /* unused */};
452 res = cookieTagMap.writeValue(sock_cookie, newKey, BPF_ANY);
453 if (!res.ok()) {
454 jniThrowExceptionFmt(env, "java/io/IOException", "Failed to tag the socket: %s, fd: %d",
455 strerror(res.error().code()), cookieTagMap.getMap().get());
456 return -1;
457 }
458
459 ALOGI("tag uid AID_CLAT to socket fd %d, cookie %" PRIu64 "", sockFd, sock_cookie);
460 return static_cast<jlong>(sock_cookie);
461 }
462
com_android_server_connectivity_ClatCoordinator_untagSocket(JNIEnv * env,jobject clazz,jlong cookie)463 static void com_android_server_connectivity_ClatCoordinator_untagSocket(JNIEnv* env, jobject clazz,
464 jlong cookie) {
465 uint64_t sock_cookie = static_cast<uint64_t>(cookie);
466 if (sock_cookie == bpf::NONEXISTENT_COOKIE) {
467 jniThrowExceptionFmt(env, "java/io/IOException", "Invalid socket cookie");
468 return;
469 }
470
471 // The reason that deleting entry from cookie tag map directly is that the tag socket destroy
472 // listener only monitors on group INET_TCP, INET_UDP, INET6_TCP, INET6_UDP. The other socket
473 // types, ex: raw, are not able to be removed automatically by the listener.
474 // See TrafficController::makeSkDestroyListener.
475 bpf::BpfMap<uint64_t, UidTagValue> cookieTagMap;
476 auto res = cookieTagMap.init(COOKIE_TAG_MAP_PATH);
477 if (!res.ok()) {
478 throwIOException(env, "failed to init the cookieTagMap", res.error().code());
479 return;
480 }
481
482 res = cookieTagMap.deleteValue(sock_cookie);
483 if (!res.ok()) {
484 jniThrowExceptionFmt(env, "java/io/IOException", "Failed to untag the socket: %s",
485 strerror(res.error().code()));
486 return;
487 }
488
489 ALOGI("untag socket cookie %" PRIu64 "", sock_cookie);
490 return;
491 }
492
493 /*
494 * JNI registration.
495 */
496 static const JNINativeMethod gMethods[] = {
497 /* name, signature, funcPtr */
498 {"native_selectIpv4Address", "(Ljava/lang/String;I)Ljava/lang/String;",
499 (void*)com_android_server_connectivity_ClatCoordinator_selectIpv4Address},
500 {"native_generateIpv6Address",
501 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
502 (void*)com_android_server_connectivity_ClatCoordinator_generateIpv6Address},
503 {"native_createTunInterface", "(Ljava/lang/String;)I",
504 (void*)com_android_server_connectivity_ClatCoordinator_createTunInterface},
505 {"native_detectMtu", "(Ljava/lang/String;II)I",
506 (void*)com_android_server_connectivity_ClatCoordinator_detectMtu},
507 {"native_openPacketSocket", "()I",
508 (void*)com_android_server_connectivity_ClatCoordinator_openPacketSocket},
509 {"native_openRawSocket6", "(I)I",
510 (void*)com_android_server_connectivity_ClatCoordinator_openRawSocket6},
511 {"native_addAnycastSetsockopt", "(Ljava/io/FileDescriptor;Ljava/lang/String;I)V",
512 (void*)com_android_server_connectivity_ClatCoordinator_addAnycastSetsockopt},
513 {"native_configurePacketSocket", "(Ljava/io/FileDescriptor;Ljava/lang/String;I)V",
514 (void*)com_android_server_connectivity_ClatCoordinator_configurePacketSocket},
515 {"native_startClatd",
516 "(Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Ljava/lang/"
517 "String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
518 (void*)com_android_server_connectivity_ClatCoordinator_startClatd},
519 {"native_stopClatd",
520 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V",
521 (void*)com_android_server_connectivity_ClatCoordinator_stopClatd},
522 {"native_tagSocketAsClat", "(Ljava/io/FileDescriptor;)J",
523 (void*)com_android_server_connectivity_ClatCoordinator_tagSocketAsClat},
524 {"native_untagSocket", "(J)V",
525 (void*)com_android_server_connectivity_ClatCoordinator_untagSocket},
526 };
527
register_com_android_server_connectivity_ClatCoordinator(JNIEnv * env)528 int register_com_android_server_connectivity_ClatCoordinator(JNIEnv* env) {
529 return jniRegisterNativeMethods(env, "com/android/server/connectivity/ClatCoordinator",
530 gMethods, NELEM(gMethods));
531 }
532
533 }; // namespace android
534