blob: 94c871d8a366bebf7972c5c48e3200088904094a [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>
Tyler Wear90e40632020-03-13 11:38:38 -070020#include <linux/filter.h>
markchienf87ebdc2019-12-07 22:02:28 +080021#include <nativehelper/JNIHelp.h>
Orion Hodson0cdccd72020-06-11 16:37:31 +010022#include <nativehelper/JNIHelpCompat.h>
markchienf87ebdc2019-12-07 22:02:28 +080023#include <nativehelper/ScopedUtfChars.h>
markchienf87ebdc2019-12-07 22:02:28 +080024#include <net/if.h>
Tyler Wear90e40632020-03-13 11:38:38 -070025#include <netinet/ether.h>
26#include <netinet/ip6.h>
markchienf87ebdc2019-12-07 22:02:28 +080027#include <netinet/icmp6.h>
28#include <sys/socket.h>
Tyler Wear90e40632020-03-13 11:38:38 -070029#include <stdio.h>
markchienf87ebdc2019-12-07 22:02:28 +080030
31#define LOG_TAG "TetheringUtils"
markchien547e1682020-02-05 12:42:25 +080032#include <android/log.h>
markchienf87ebdc2019-12-07 22:02:28 +080033
34namespace android {
35
Tyler Wear90e40632020-03-13 11:38:38 -070036static const uint32_t kIPv6NextHeaderOffset = offsetof(ip6_hdr, ip6_nxt);
37static const uint32_t kIPv6PayloadStart = sizeof(ip6_hdr);
38static const uint32_t kICMPv6TypeOffset = kIPv6PayloadStart + offsetof(icmp6_hdr, icmp6_type);
39
40static void android_net_util_setupIcmpFilter(JNIEnv *env, jobject javaFd, uint32_t type) {
41 sock_filter filter_code[] = {
42 // Check header is ICMPv6.
43 BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeaderOffset),
44 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3),
45
46 // Check ICMPv6 type.
47 BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset),
48 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, type, 0, 1),
49
50 // Accept or reject.
51 BPF_STMT(BPF_RET | BPF_K, 0xffff),
52 BPF_STMT(BPF_RET | BPF_K, 0)
53 };
54
55 const sock_fprog filter = {
56 sizeof(filter_code) / sizeof(filter_code[0]),
57 filter_code,
58 };
59
60 int fd = jniGetFDFromFileDescriptor(env, javaFd);
61 if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
62 jniThrowExceptionFmt(env, "java/net/SocketException",
63 "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
64 }
65}
66
67static void android_net_util_setupNaSocket(JNIEnv *env, jobject clazz, jobject javaFd)
68{
69 android_net_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_ADVERT);
70}
71
72static void android_net_util_setupNsSocket(JNIEnv *env, jobject clazz, jobject javaFd)
73{
74 android_net_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_SOLICIT);
75}
76
markchienf87ebdc2019-12-07 22:02:28 +080077static void android_net_util_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd,
78 jint ifIndex)
79{
80 static const int kLinkLocalHopLimit = 255;
81
82 int fd = jniGetFDFromFileDescriptor(env, javaFd);
83
84 // Set an ICMPv6 filter that only passes Router Solicitations.
85 struct icmp6_filter rs_only;
86 ICMP6_FILTER_SETBLOCKALL(&rs_only);
87 ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &rs_only);
88 socklen_t len = sizeof(rs_only);
89 if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &rs_only, len) != 0) {
90 jniThrowExceptionFmt(env, "java/net/SocketException",
91 "setsockopt(ICMP6_FILTER): %s", strerror(errno));
92 return;
93 }
94
95 // Most/all of the rest of these options can be set via Java code, but
96 // because we're here on account of setting an icmp6_filter go ahead
97 // and do it all natively for now.
98
99 // Set the multicast hoplimit to 255 (link-local only).
100 int hops = kLinkLocalHopLimit;
101 len = sizeof(hops);
102 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, len) != 0) {
103 jniThrowExceptionFmt(env, "java/net/SocketException",
104 "setsockopt(IPV6_MULTICAST_HOPS): %s", strerror(errno));
105 return;
106 }
107
108 // Set the unicast hoplimit to 255 (link-local only).
109 hops = kLinkLocalHopLimit;
110 len = sizeof(hops);
111 if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, len) != 0) {
112 jniThrowExceptionFmt(env, "java/net/SocketException",
113 "setsockopt(IPV6_UNICAST_HOPS): %s", strerror(errno));
114 return;
115 }
116
117 // Explicitly disable multicast loopback.
118 int off = 0;
119 len = sizeof(off);
120 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, len) != 0) {
121 jniThrowExceptionFmt(env, "java/net/SocketException",
122 "setsockopt(IPV6_MULTICAST_LOOP): %s", strerror(errno));
123 return;
124 }
125
126 // Specify the IPv6 interface to use for outbound multicast.
127 len = sizeof(ifIndex);
128 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifIndex, len) != 0) {
129 jniThrowExceptionFmt(env, "java/net/SocketException",
130 "setsockopt(IPV6_MULTICAST_IF): %s", strerror(errno));
131 return;
132 }
133
134 // Additional options to be considered:
135 // - IPV6_TCLASS
136 // - IPV6_RECVPKTINFO
137 // - IPV6_RECVHOPLIMIT
138
139 // Bind to [::].
140 const struct sockaddr_in6 sin6 = {
141 .sin6_family = AF_INET6,
142 .sin6_port = 0,
143 .sin6_flowinfo = 0,
144 .sin6_addr = IN6ADDR_ANY_INIT,
145 .sin6_scope_id = 0,
146 };
147 auto sa = reinterpret_cast<const struct sockaddr *>(&sin6);
148 len = sizeof(sin6);
149 if (bind(fd, sa, len) != 0) {
150 jniThrowExceptionFmt(env, "java/net/SocketException",
151 "bind(IN6ADDR_ANY): %s", strerror(errno));
152 return;
153 }
154
155 // Join the all-routers multicast group, ff02::2%index.
156 struct ipv6_mreq all_rtrs = {
157 .ipv6mr_multiaddr = {{{0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2}}},
158 .ipv6mr_interface = ifIndex,
159 };
160 len = sizeof(all_rtrs);
161 if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &all_rtrs, len) != 0) {
162 jniThrowExceptionFmt(env, "java/net/SocketException",
163 "setsockopt(IPV6_JOIN_GROUP): %s", strerror(errno));
164 return;
165 }
166}
167
168/*
169 * JNI registration.
170 */
171static const JNINativeMethod gMethods[] = {
172 /* name, signature, funcPtr */
Tyler Wear90e40632020-03-13 11:38:38 -0700173 { "setupNaSocket", "(Ljava/io/FileDescriptor;)V",
174 (void*) android_net_util_setupNaSocket },
175 { "setupNsSocket", "(Ljava/io/FileDescriptor;)V",
176 (void*) android_net_util_setupNsSocket },
177 { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V",
178 (void*) android_net_util_setupRaSocket },
markchienf87ebdc2019-12-07 22:02:28 +0800179};
180
181int register_android_net_util_TetheringUtils(JNIEnv* env) {
182 return jniRegisterNativeMethods(env,
183 "android/net/util/TetheringUtils",
184 gMethods, NELEM(gMethods));
185}
186
187extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
188 JNIEnv *env;
189 if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
markchien547e1682020-02-05 12:42:25 +0800190 __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "ERROR: GetEnv failed");
markchienf87ebdc2019-12-07 22:02:28 +0800191 return JNI_ERR;
192 }
193
194 if (register_android_net_util_TetheringUtils(env) < 0) {
195 return JNI_ERR;
196 }
197
198 return JNI_VERSION_1_6;
199}
200
201}; // namespace android