android_net_NetUtils.cpp revision 461e4fff312773d8883d837d3f07b9fe60f73b03
1/*
2 * Copyright 2008, 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#define LOG_TAG "NetUtils"
18
19#include "jni.h"
20#include "JNIHelp.h"
21#include "NetdClient.h"
22#include <utils/misc.h>
23#include <android_runtime/AndroidRuntime.h>
24#include <utils/Log.h>
25#include <arpa/inet.h>
26#include <net/if.h>
27#include <linux/filter.h>
28#include <linux/if_arp.h>
29#include <netinet/ether.h>
30#include <netinet/icmp6.h>
31#include <netinet/ip.h>
32#include <netinet/ip6.h>
33#include <netinet/udp.h>
34#include <cutils/properties.h>
35
36#include "core_jni_helpers.h"
37
38extern "C" {
39int ifc_enable(const char *ifname);
40int ifc_disable(const char *ifname);
41}
42
43#define NETUTILS_PKG_NAME "android/net/NetworkUtils"
44
45namespace android {
46
47static const uint16_t kDhcpClientPort = 68;
48
49static void android_net_utils_attachDhcpFilter(JNIEnv *env, jobject clazz, jobject javaFd)
50{
51    uint32_t ip_offset = sizeof(ether_header);
52    uint32_t proto_offset = ip_offset + offsetof(iphdr, protocol);
53    uint32_t flags_offset = ip_offset + offsetof(iphdr, frag_off);
54    uint32_t dport_indirect_offset = ip_offset + offsetof(udphdr, dest);
55    struct sock_filter filter_code[] = {
56        // Check the protocol is UDP.
57        BPF_STMT(BPF_LD  | BPF_B   | BPF_ABS,  proto_offset),
58        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,    IPPROTO_UDP, 0, 6),
59
60        // Check this is not a fragment.
61        BPF_STMT(BPF_LD  | BPF_H    | BPF_ABS, flags_offset),
62        BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K,   0x1fff, 4, 0),
63
64        // Get the IP header length.
65        BPF_STMT(BPF_LDX | BPF_B    | BPF_MSH, ip_offset),
66
67        // Check the destination port.
68        BPF_STMT(BPF_LD  | BPF_H    | BPF_IND, dport_indirect_offset),
69        BPF_JUMP(BPF_JMP | BPF_JEQ  | BPF_K,   kDhcpClientPort, 0, 1),
70
71        // Accept or reject.
72        BPF_STMT(BPF_RET | BPF_K,              0xffff),
73        BPF_STMT(BPF_RET | BPF_K,              0)
74    };
75    struct sock_fprog filter = {
76        sizeof(filter_code) / sizeof(filter_code[0]),
77        filter_code,
78    };
79
80    int fd = jniGetFDFromFileDescriptor(env, javaFd);
81    if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
82        jniThrowExceptionFmt(env, "java/net/SocketException",
83                "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
84    }
85}
86
87static void android_net_utils_attachRaFilter(JNIEnv *env, jobject clazz, jobject javaFd,
88        jint hardwareAddressType)
89{
90    if (hardwareAddressType != ARPHRD_ETHER) {
91        jniThrowExceptionFmt(env, "java/net/SocketException",
92                "attachRaFilter only supports ARPHRD_ETHER");
93        return;
94    }
95
96    uint32_t ipv6_offset = sizeof(ether_header);
97    uint32_t ipv6_next_header_offset = ipv6_offset + offsetof(ip6_hdr, ip6_nxt);
98    uint32_t icmp6_offset = ipv6_offset + sizeof(ip6_hdr);
99    uint32_t icmp6_type_offset = icmp6_offset + offsetof(icmp6_hdr, icmp6_type);
100    struct sock_filter filter_code[] = {
101        // Check IPv6 Next Header is ICMPv6.
102        BPF_STMT(BPF_LD  | BPF_B   | BPF_ABS,  ipv6_next_header_offset),
103        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,    IPPROTO_ICMPV6, 0, 3),
104
105        // Check ICMPv6 type is Router Advertisement.
106        BPF_STMT(BPF_LD  | BPF_B   | BPF_ABS,  icmp6_type_offset),
107        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,    ND_ROUTER_ADVERT, 0, 1),
108
109        // Accept or reject.
110        BPF_STMT(BPF_RET | BPF_K,              0xffff),
111        BPF_STMT(BPF_RET | BPF_K,              0)
112    };
113    struct sock_fprog filter = {
114        sizeof(filter_code) / sizeof(filter_code[0]),
115        filter_code,
116    };
117
118    int fd = jniGetFDFromFileDescriptor(env, javaFd);
119    if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
120        jniThrowExceptionFmt(env, "java/net/SocketException",
121                "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
122    }
123}
124
125static void android_net_utils_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd,
126        jint ifIndex)
127{
128    static const int kLinkLocalHopLimit = 255;
129
130    int fd = jniGetFDFromFileDescriptor(env, javaFd);
131
132    // Set an ICMPv6 filter that only passes Router Solicitations.
133    struct icmp6_filter rs_only;
134    ICMP6_FILTER_SETBLOCKALL(&rs_only);
135    ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &rs_only);
136    socklen_t len = sizeof(rs_only);
137    if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &rs_only, len) != 0) {
138        jniThrowExceptionFmt(env, "java/net/SocketException",
139                "setsockopt(ICMP6_FILTER): %s", strerror(errno));
140        return;
141    }
142
143    // Most/all of the rest of these options can be set via Java code, but
144    // because we're here on account of setting an icmp6_filter go ahead
145    // and do it all natively for now.
146    //
147    // TODO: Consider moving these out to Java.
148
149    // Set the multicast hoplimit to 255 (link-local only).
150    int hops = kLinkLocalHopLimit;
151    len = sizeof(hops);
152    if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, len) != 0) {
153        jniThrowExceptionFmt(env, "java/net/SocketException",
154                "setsockopt(IPV6_MULTICAST_HOPS): %s", strerror(errno));
155        return;
156    }
157
158    // Set the unicast hoplimit to 255 (link-local only).
159    hops = kLinkLocalHopLimit;
160    len = sizeof(hops);
161    if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, len) != 0) {
162        jniThrowExceptionFmt(env, "java/net/SocketException",
163                "setsockopt(IPV6_UNICAST_HOPS): %s", strerror(errno));
164        return;
165    }
166
167    // Explicitly disable multicast loopback.
168    int off = 0;
169    len = sizeof(off);
170    if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, len) != 0) {
171        jniThrowExceptionFmt(env, "java/net/SocketException",
172                "setsockopt(IPV6_MULTICAST_LOOP): %s", strerror(errno));
173        return;
174    }
175
176    // Specify the IPv6 interface to use for outbound multicast.
177    len = sizeof(ifIndex);
178    if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifIndex, len) != 0) {
179        jniThrowExceptionFmt(env, "java/net/SocketException",
180                "setsockopt(IPV6_MULTICAST_IF): %s", strerror(errno));
181        return;
182    }
183
184    // Additional options to be considered:
185    //     - IPV6_TCLASS
186    //     - IPV6_RECVPKTINFO
187    //     - IPV6_RECVHOPLIMIT
188
189    // Bind to [::].
190    const struct sockaddr_in6 sin6 = {
191            .sin6_family = AF_INET6,
192            .sin6_port = 0,
193            .sin6_flowinfo = 0,
194            .sin6_addr = IN6ADDR_ANY_INIT,
195            .sin6_scope_id = 0,
196    };
197    auto sa = reinterpret_cast<const struct sockaddr *>(&sin6);
198    len = sizeof(sin6);
199    if (bind(fd, sa, len) != 0) {
200        jniThrowExceptionFmt(env, "java/net/SocketException",
201                "bind(IN6ADDR_ANY): %s", strerror(errno));
202        return;
203    }
204
205    // Join the all-routers multicast group, ff02::2%index.
206    struct ipv6_mreq all_rtrs = {
207        .ipv6mr_multiaddr = {{{0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2}}},
208        .ipv6mr_interface = ifIndex,
209    };
210    len = sizeof(all_rtrs);
211    if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &all_rtrs, len) != 0) {
212        jniThrowExceptionFmt(env, "java/net/SocketException",
213                "setsockopt(IPV6_JOIN_GROUP): %s", strerror(errno));
214        return;
215    }
216}
217
218static jboolean android_net_utils_bindProcessToNetwork(JNIEnv *env, jobject thiz, jint netId)
219{
220    return (jboolean) !setNetworkForProcess(netId);
221}
222
223static jint android_net_utils_getBoundNetworkForProcess(JNIEnv *env, jobject thiz)
224{
225    return getNetworkForProcess();
226}
227
228static jboolean android_net_utils_bindProcessToNetworkForHostResolution(JNIEnv *env, jobject thiz,
229        jint netId)
230{
231    return (jboolean) !setNetworkForResolv(netId);
232}
233
234static jint android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jint socket,
235        jint netId)
236{
237    return setNetworkForSocket(netId, socket);
238}
239
240static jboolean android_net_utils_protectFromVpn(JNIEnv *env, jobject thiz, jint socket)
241{
242    return (jboolean) !protectFromVpn(socket);
243}
244
245static jboolean android_net_utils_queryUserAccess(JNIEnv *env, jobject thiz, jint uid, jint netId)
246{
247    return (jboolean) !queryUserAccess(uid, netId);
248}
249
250
251// ----------------------------------------------------------------------------
252
253/*
254 * JNI registration.
255 */
256static const JNINativeMethod gNetworkUtilMethods[] = {
257    /* name, signature, funcPtr */
258    { "bindProcessToNetwork", "(I)Z", (void*) android_net_utils_bindProcessToNetwork },
259    { "getBoundNetworkForProcess", "()I", (void*) android_net_utils_getBoundNetworkForProcess },
260    { "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
261    { "bindSocketToNetwork", "(II)I", (void*) android_net_utils_bindSocketToNetwork },
262    { "protectFromVpn", "(I)Z", (void*)android_net_utils_protectFromVpn },
263    { "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess },
264    { "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDhcpFilter },
265    { "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachRaFilter },
266    { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_setupRaSocket },
267};
268
269int register_android_net_NetworkUtils(JNIEnv* env)
270{
271    return RegisterMethodsOrDie(env, NETUTILS_PKG_NAME, gNetworkUtilMethods,
272                                NELEM(gNetworkUtilMethods));
273}
274
275}; // namespace android
276