• 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 <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