blob: 5493440644053aad293c2ba247f38b1e3c70019c [file] [log] [blame]
markchienf87ebdc2019-12-07 22:02:28 +08001/*
2 * Copyright (C) 2017 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 <error.h>
markchienf87ebdc2019-12-07 22:02:28 +080019#include <jni.h>
20#include <nativehelper/JNIHelp.h>
21#include <nativehelper/ScopedUtfChars.h>
markchienf87ebdc2019-12-07 22:02:28 +080022#include <net/if.h>
23#include <netinet/icmp6.h>
24#include <sys/socket.h>
markchienf87ebdc2019-12-07 22:02:28 +080025
26#define LOG_TAG "TetheringUtils"
markchien547e1682020-02-05 12:42:25 +080027#include <android/log.h>
markchienf87ebdc2019-12-07 22:02:28 +080028
29namespace android {
30
markchienf87ebdc2019-12-07 22:02:28 +080031static void android_net_util_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd,
32 jint ifIndex)
33{
34 static const int kLinkLocalHopLimit = 255;
35
36 int fd = jniGetFDFromFileDescriptor(env, javaFd);
37
38 // Set an ICMPv6 filter that only passes Router Solicitations.
39 struct icmp6_filter rs_only;
40 ICMP6_FILTER_SETBLOCKALL(&rs_only);
41 ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &rs_only);
42 socklen_t len = sizeof(rs_only);
43 if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &rs_only, len) != 0) {
44 jniThrowExceptionFmt(env, "java/net/SocketException",
45 "setsockopt(ICMP6_FILTER): %s", strerror(errno));
46 return;
47 }
48
49 // Most/all of the rest of these options can be set via Java code, but
50 // because we're here on account of setting an icmp6_filter go ahead
51 // and do it all natively for now.
52
53 // Set the multicast hoplimit to 255 (link-local only).
54 int hops = kLinkLocalHopLimit;
55 len = sizeof(hops);
56 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, len) != 0) {
57 jniThrowExceptionFmt(env, "java/net/SocketException",
58 "setsockopt(IPV6_MULTICAST_HOPS): %s", strerror(errno));
59 return;
60 }
61
62 // Set the unicast hoplimit to 255 (link-local only).
63 hops = kLinkLocalHopLimit;
64 len = sizeof(hops);
65 if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, len) != 0) {
66 jniThrowExceptionFmt(env, "java/net/SocketException",
67 "setsockopt(IPV6_UNICAST_HOPS): %s", strerror(errno));
68 return;
69 }
70
71 // Explicitly disable multicast loopback.
72 int off = 0;
73 len = sizeof(off);
74 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, len) != 0) {
75 jniThrowExceptionFmt(env, "java/net/SocketException",
76 "setsockopt(IPV6_MULTICAST_LOOP): %s", strerror(errno));
77 return;
78 }
79
80 // Specify the IPv6 interface to use for outbound multicast.
81 len = sizeof(ifIndex);
82 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifIndex, len) != 0) {
83 jniThrowExceptionFmt(env, "java/net/SocketException",
84 "setsockopt(IPV6_MULTICAST_IF): %s", strerror(errno));
85 return;
86 }
87
88 // Additional options to be considered:
89 // - IPV6_TCLASS
90 // - IPV6_RECVPKTINFO
91 // - IPV6_RECVHOPLIMIT
92
93 // Bind to [::].
94 const struct sockaddr_in6 sin6 = {
95 .sin6_family = AF_INET6,
96 .sin6_port = 0,
97 .sin6_flowinfo = 0,
98 .sin6_addr = IN6ADDR_ANY_INIT,
99 .sin6_scope_id = 0,
100 };
101 auto sa = reinterpret_cast<const struct sockaddr *>(&sin6);
102 len = sizeof(sin6);
103 if (bind(fd, sa, len) != 0) {
104 jniThrowExceptionFmt(env, "java/net/SocketException",
105 "bind(IN6ADDR_ANY): %s", strerror(errno));
106 return;
107 }
108
109 // Join the all-routers multicast group, ff02::2%index.
110 struct ipv6_mreq all_rtrs = {
111 .ipv6mr_multiaddr = {{{0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2}}},
112 .ipv6mr_interface = ifIndex,
113 };
114 len = sizeof(all_rtrs);
115 if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &all_rtrs, len) != 0) {
116 jniThrowExceptionFmt(env, "java/net/SocketException",
117 "setsockopt(IPV6_JOIN_GROUP): %s", strerror(errno));
118 return;
119 }
120}
121
122/*
123 * JNI registration.
124 */
125static const JNINativeMethod gMethods[] = {
126 /* name, signature, funcPtr */
markchienf87ebdc2019-12-07 22:02:28 +0800127 { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_util_setupRaSocket },
128};
129
130int register_android_net_util_TetheringUtils(JNIEnv* env) {
131 return jniRegisterNativeMethods(env,
132 "android/net/util/TetheringUtils",
133 gMethods, NELEM(gMethods));
134}
135
136extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
137 JNIEnv *env;
138 if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
markchien547e1682020-02-05 12:42:25 +0800139 __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "ERROR: GetEnv failed");
markchienf87ebdc2019-12-07 22:02:28 +0800140 return JNI_ERR;
141 }
142
143 if (register_android_net_util_TetheringUtils(env) < 0) {
144 return JNI_ERR;
145 }
146
147 return JNI_VERSION_1_6;
148}
149
150}; // namespace android