• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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