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