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