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