blob: 2e76501caeaaaae26b3faaad81e7deff3f56e0b4 [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>
22#include <nativehelper/ScopedUtfChars.h>
Orion Hodsone5bd0db2020-12-08 09:57:42 +000023#include <netjniutils/netjniutils.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
markchienf87ebdc2019-12-07 22:02:28 +080031namespace android {
32
Tyler Wear90e40632020-03-13 11:38:38 -070033static const uint32_t kIPv6NextHeaderOffset = offsetof(ip6_hdr, ip6_nxt);
34static const uint32_t kIPv6PayloadStart = sizeof(ip6_hdr);
35static const uint32_t kICMPv6TypeOffset = kIPv6PayloadStart + offsetof(icmp6_hdr, icmp6_type);
36
markchien08360622021-07-16 23:07:19 +080037static void throwSocketException(JNIEnv *env, const char* msg, int error) {
38 jniThrowExceptionFmt(env, "java/net/SocketException", "%s: %s", msg, strerror(error));
39}
40
Tyler Wear90e40632020-03-13 11:38:38 -070041static void android_net_util_setupIcmpFilter(JNIEnv *env, jobject javaFd, uint32_t type) {
42 sock_filter filter_code[] = {
43 // Check header is ICMPv6.
44 BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeaderOffset),
45 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3),
46
47 // Check ICMPv6 type.
48 BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset),
49 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, type, 0, 1),
50
51 // Accept or reject.
52 BPF_STMT(BPF_RET | BPF_K, 0xffff),
53 BPF_STMT(BPF_RET | BPF_K, 0)
54 };
55
56 const sock_fprog filter = {
57 sizeof(filter_code) / sizeof(filter_code[0]),
58 filter_code,
59 };
60
Orion Hodsone5bd0db2020-12-08 09:57:42 +000061 int fd = netjniutils::GetNativeFileDescriptor(env, javaFd);
Tyler Wear90e40632020-03-13 11:38:38 -070062 if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
markchien08360622021-07-16 23:07:19 +080063 throwSocketException(env, "setsockopt(SO_ATTACH_FILTER)", errno);
Tyler Wear90e40632020-03-13 11:38:38 -070064 }
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
Orion Hodsone5bd0db2020-12-08 09:57:42 +000082 int fd = netjniutils::GetNativeFileDescriptor(env, javaFd);
markchienf87ebdc2019-12-07 22:02:28 +080083
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) {
markchien08360622021-07-16 23:07:19 +080090 throwSocketException(env, "setsockopt(ICMP6_FILTER)", errno);
markchienf87ebdc2019-12-07 22:02:28 +080091 return;
92 }
93
94 // Most/all of the rest of these options can be set via Java code, but
95 // because we're here on account of setting an icmp6_filter go ahead
96 // and do it all natively for now.
97
98 // Set the multicast hoplimit to 255 (link-local only).
99 int hops = kLinkLocalHopLimit;
100 len = sizeof(hops);
101 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, len) != 0) {
markchien08360622021-07-16 23:07:19 +0800102 throwSocketException(env, "setsockopt(IPV6_MULTICAST_HOPS)", errno);
markchienf87ebdc2019-12-07 22:02:28 +0800103 return;
104 }
105
106 // Set the unicast hoplimit to 255 (link-local only).
107 hops = kLinkLocalHopLimit;
108 len = sizeof(hops);
109 if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, len) != 0) {
markchien08360622021-07-16 23:07:19 +0800110 throwSocketException(env, "setsockopt(IPV6_UNICAST_HOPS)", errno);
markchienf87ebdc2019-12-07 22:02:28 +0800111 return;
112 }
113
114 // Explicitly disable multicast loopback.
115 int off = 0;
116 len = sizeof(off);
117 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, len) != 0) {
markchien08360622021-07-16 23:07:19 +0800118 throwSocketException(env, "setsockopt(IPV6_MULTICAST_LOOP)", errno);
markchienf87ebdc2019-12-07 22:02:28 +0800119 return;
120 }
121
122 // Specify the IPv6 interface to use for outbound multicast.
123 len = sizeof(ifIndex);
124 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifIndex, len) != 0) {
markchien08360622021-07-16 23:07:19 +0800125 throwSocketException(env, "setsockopt(IPV6_MULTICAST_IF)", errno);
markchienf87ebdc2019-12-07 22:02:28 +0800126 return;
127 }
128
129 // Additional options to be considered:
130 // - IPV6_TCLASS
131 // - IPV6_RECVPKTINFO
132 // - IPV6_RECVHOPLIMIT
133
134 // Bind to [::].
135 const struct sockaddr_in6 sin6 = {
136 .sin6_family = AF_INET6,
137 .sin6_port = 0,
138 .sin6_flowinfo = 0,
139 .sin6_addr = IN6ADDR_ANY_INIT,
140 .sin6_scope_id = 0,
141 };
142 auto sa = reinterpret_cast<const struct sockaddr *>(&sin6);
143 len = sizeof(sin6);
144 if (bind(fd, sa, len) != 0) {
markchien08360622021-07-16 23:07:19 +0800145 throwSocketException(env, "bind(IN6ADDR_ANY)", errno);
markchienf87ebdc2019-12-07 22:02:28 +0800146 return;
147 }
148
149 // Join the all-routers multicast group, ff02::2%index.
150 struct ipv6_mreq all_rtrs = {
151 .ipv6mr_multiaddr = {{{0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2}}},
152 .ipv6mr_interface = ifIndex,
153 };
154 len = sizeof(all_rtrs);
155 if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &all_rtrs, len) != 0) {
markchien08360622021-07-16 23:07:19 +0800156 throwSocketException(env, "setsockopt(IPV6_JOIN_GROUP)", errno);
markchienf87ebdc2019-12-07 22:02:28 +0800157 return;
158 }
159}
160
161/*
162 * JNI registration.
163 */
164static const JNINativeMethod gMethods[] = {
165 /* name, signature, funcPtr */
Tyler Wear90e40632020-03-13 11:38:38 -0700166 { "setupNaSocket", "(Ljava/io/FileDescriptor;)V",
167 (void*) android_net_util_setupNaSocket },
168 { "setupNsSocket", "(Ljava/io/FileDescriptor;)V",
169 (void*) android_net_util_setupNsSocket },
170 { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V",
171 (void*) android_net_util_setupRaSocket },
markchienf87ebdc2019-12-07 22:02:28 +0800172};
173
174int register_android_net_util_TetheringUtils(JNIEnv* env) {
175 return jniRegisterNativeMethods(env,
176 "android/net/util/TetheringUtils",
177 gMethods, NELEM(gMethods));
178}
179
markchienf87ebdc2019-12-07 22:02:28 +0800180}; // namespace android