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