blob: 1cf8f988432c53c70f120f27959ca017c44cabd4 [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>
19#include <hidl/HidlSupport.h>
20#include <jni.h>
21#include <nativehelper/JNIHelp.h>
22#include <nativehelper/ScopedUtfChars.h>
23#include <linux/netfilter/nfnetlink.h>
24#include <linux/netlink.h>
25#include <net/if.h>
26#include <netinet/icmp6.h>
27#include <sys/socket.h>
28#include <android-base/unique_fd.h>
29#include <android/hardware/tetheroffload/config/1.0/IOffloadConfig.h>
30
31#define LOG_TAG "TetheringUtils"
32#include <utils/Log.h>
33
34namespace android {
35
36using hardware::hidl_handle;
37using hardware::hidl_string;
38using hardware::tetheroffload::config::V1_0::IOffloadConfig;
39
40namespace {
41
42inline const sockaddr * asSockaddr(const sockaddr_nl *nladdr) {
43 return reinterpret_cast<const sockaddr *>(nladdr);
44}
45
46int conntrackSocket(unsigned groups) {
47 base::unique_fd s(socket(AF_NETLINK, SOCK_DGRAM, NETLINK_NETFILTER));
48 if (s.get() < 0) return -errno;
49
50 const struct sockaddr_nl bind_addr = {
51 .nl_family = AF_NETLINK,
52 .nl_pad = 0,
53 .nl_pid = 0,
54 .nl_groups = groups,
55 };
56 if (bind(s.get(), asSockaddr(&bind_addr), sizeof(bind_addr)) != 0) {
57 return -errno;
58 }
59
60 const struct sockaddr_nl kernel_addr = {
61 .nl_family = AF_NETLINK,
62 .nl_pad = 0,
63 .nl_pid = 0,
64 .nl_groups = groups,
65 };
66 if (connect(s.get(), asSockaddr(&kernel_addr), sizeof(kernel_addr)) != 0) {
67 return -errno;
68 }
69
70 return s.release();
71}
72
73// Return a hidl_handle that owns the file descriptor owned by fd, and will
74// auto-close it (otherwise there would be double-close problems).
75//
76// Rely upon the compiler to eliminate the constexprs used for clarity.
77hidl_handle handleFromFileDescriptor(base::unique_fd fd) {
78 hidl_handle h;
79
80 static constexpr int kNumFds = 1;
81 static constexpr int kNumInts = 0;
82 native_handle_t *nh = native_handle_create(kNumFds, kNumInts);
83 nh->data[0] = fd.release();
84
85 static constexpr bool kTakeOwnership = true;
86 h.setTo(nh, kTakeOwnership);
87
88 return h;
89}
90
91} // namespace
92
93static jboolean android_net_util_configOffload(
94 JNIEnv* /* env */) {
95 sp<IOffloadConfig> configInterface = IOffloadConfig::getService();
96 if (configInterface.get() == nullptr) {
97 ALOGD("Could not find IOffloadConfig service.");
98 return false;
99 }
100
101 // Per the IConfigOffload definition:
102 //
103 // fd1 A file descriptor bound to the following netlink groups
104 // (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY).
105 //
106 // fd2 A file descriptor bound to the following netlink groups
107 // (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY).
108 base::unique_fd
109 fd1(conntrackSocket(NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY)),
110 fd2(conntrackSocket(NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY));
111 if (fd1.get() < 0 || fd2.get() < 0) {
112 ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno));
113 return false;
114 }
115
116 hidl_handle h1(handleFromFileDescriptor(std::move(fd1))),
117 h2(handleFromFileDescriptor(std::move(fd2)));
118
119 bool rval(false);
120 hidl_string msg;
121 const auto status = configInterface->setHandles(h1, h2,
122 [&rval, &msg](bool success, const hidl_string& errMsg) {
123 rval = success;
124 msg = errMsg;
125 });
126 if (!status.isOk() || !rval) {
127 ALOGE("IOffloadConfig::setHandles() error: '%s' / '%s'",
128 status.description().c_str(), msg.c_str());
129 // If status is somehow not ok, make sure rval captures this too.
130 rval = false;
131 }
132
133 return rval;
134}
135
136static void android_net_util_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd,
137 jint ifIndex)
138{
139 static const int kLinkLocalHopLimit = 255;
140
141 int fd = jniGetFDFromFileDescriptor(env, javaFd);
142
143 // Set an ICMPv6 filter that only passes Router Solicitations.
144 struct icmp6_filter rs_only;
145 ICMP6_FILTER_SETBLOCKALL(&rs_only);
146 ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &rs_only);
147 socklen_t len = sizeof(rs_only);
148 if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &rs_only, len) != 0) {
149 jniThrowExceptionFmt(env, "java/net/SocketException",
150 "setsockopt(ICMP6_FILTER): %s", strerror(errno));
151 return;
152 }
153
154 // Most/all of the rest of these options can be set via Java code, but
155 // because we're here on account of setting an icmp6_filter go ahead
156 // and do it all natively for now.
157
158 // Set the multicast hoplimit to 255 (link-local only).
159 int hops = kLinkLocalHopLimit;
160 len = sizeof(hops);
161 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, len) != 0) {
162 jniThrowExceptionFmt(env, "java/net/SocketException",
163 "setsockopt(IPV6_MULTICAST_HOPS): %s", strerror(errno));
164 return;
165 }
166
167 // Set the unicast hoplimit to 255 (link-local only).
168 hops = kLinkLocalHopLimit;
169 len = sizeof(hops);
170 if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, len) != 0) {
171 jniThrowExceptionFmt(env, "java/net/SocketException",
172 "setsockopt(IPV6_UNICAST_HOPS): %s", strerror(errno));
173 return;
174 }
175
176 // Explicitly disable multicast loopback.
177 int off = 0;
178 len = sizeof(off);
179 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, len) != 0) {
180 jniThrowExceptionFmt(env, "java/net/SocketException",
181 "setsockopt(IPV6_MULTICAST_LOOP): %s", strerror(errno));
182 return;
183 }
184
185 // Specify the IPv6 interface to use for outbound multicast.
186 len = sizeof(ifIndex);
187 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifIndex, len) != 0) {
188 jniThrowExceptionFmt(env, "java/net/SocketException",
189 "setsockopt(IPV6_MULTICAST_IF): %s", strerror(errno));
190 return;
191 }
192
193 // Additional options to be considered:
194 // - IPV6_TCLASS
195 // - IPV6_RECVPKTINFO
196 // - IPV6_RECVHOPLIMIT
197
198 // Bind to [::].
199 const struct sockaddr_in6 sin6 = {
200 .sin6_family = AF_INET6,
201 .sin6_port = 0,
202 .sin6_flowinfo = 0,
203 .sin6_addr = IN6ADDR_ANY_INIT,
204 .sin6_scope_id = 0,
205 };
206 auto sa = reinterpret_cast<const struct sockaddr *>(&sin6);
207 len = sizeof(sin6);
208 if (bind(fd, sa, len) != 0) {
209 jniThrowExceptionFmt(env, "java/net/SocketException",
210 "bind(IN6ADDR_ANY): %s", strerror(errno));
211 return;
212 }
213
214 // Join the all-routers multicast group, ff02::2%index.
215 struct ipv6_mreq all_rtrs = {
216 .ipv6mr_multiaddr = {{{0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2}}},
217 .ipv6mr_interface = ifIndex,
218 };
219 len = sizeof(all_rtrs);
220 if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &all_rtrs, len) != 0) {
221 jniThrowExceptionFmt(env, "java/net/SocketException",
222 "setsockopt(IPV6_JOIN_GROUP): %s", strerror(errno));
223 return;
224 }
225}
226
227/*
228 * JNI registration.
229 */
230static const JNINativeMethod gMethods[] = {
231 /* name, signature, funcPtr */
232 { "configOffload", "()Z", (void*) android_net_util_configOffload },
233 { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_util_setupRaSocket },
234};
235
236int register_android_net_util_TetheringUtils(JNIEnv* env) {
237 return jniRegisterNativeMethods(env,
238 "android/net/util/TetheringUtils",
239 gMethods, NELEM(gMethods));
240}
241
242extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
243 JNIEnv *env;
244 if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
245 ALOGE("ERROR: GetEnv failed");
246 return JNI_ERR;
247 }
248
249 if (register_android_net_util_TetheringUtils(env) < 0) {
250 return JNI_ERR;
251 }
252
253 return JNI_VERSION_1_6;
254}
255
256}; // namespace android