android_net_NetUtils.cpp revision cbe4f7c225f87ef1e8cb496bce434f334774bc88
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.h>
29#include <linux/if_ether.h>
30#include <linux/if_packet.h>
31#include <net/if_ether.h>
32#include <netinet/ip.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);
41int ifc_reset_connections(const char *ifname, int reset_mask);
42
43int dhcp_start(const char * const ifname);
44int dhcp_start_renew(const char * const ifname);
45int dhcp_get_results(const char * const ifname,
46                     const char *ipaddr,
47                     const char *gateway,
48                     uint32_t *prefixLength,
49                     const char *dns[],
50                     const char *server,
51                     uint32_t *lease,
52                     const char *vendorInfo,
53                     const char *domains,
54                     const char *mtu);
55
56int dhcp_stop(const char *ifname);
57int dhcp_release_lease(const char *ifname);
58char *dhcp_get_errmsg();
59}
60
61#define NETUTILS_PKG_NAME "android/net/NetworkUtils"
62
63namespace android {
64
65static const uint16_t kDhcpClientPort = 68;
66
67/*
68 * The following remembers the jfieldID's of the fields
69 * of the DhcpInfo Java object, so that we don't have
70 * to look them up every time.
71 */
72static struct fieldIds {
73    jmethodID clear;
74    jmethodID setIpAddress;
75    jmethodID setGateway;
76    jmethodID addDns;
77    jmethodID setDomains;
78    jmethodID setServerAddress;
79    jmethodID setLeaseDuration;
80    jmethodID setVendorInfo;
81} dhcpResultsFieldIds;
82
83static jint android_net_utils_resetConnections(JNIEnv* env, jobject clazz,
84      jstring ifname, jint mask)
85{
86    int result;
87
88    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
89
90    ALOGD("android_net_utils_resetConnections in env=%p clazz=%p iface=%s mask=0x%x\n",
91          env, clazz, nameStr, mask);
92
93    result = ::ifc_reset_connections(nameStr, mask);
94    env->ReleaseStringUTFChars(ifname, nameStr);
95    return (jint)result;
96}
97
98static jboolean android_net_utils_getDhcpResults(JNIEnv* env, jobject clazz, jstring ifname,
99        jobject dhcpResults)
100{
101    int result;
102    char  ipaddr[PROPERTY_VALUE_MAX];
103    uint32_t prefixLength;
104    char gateway[PROPERTY_VALUE_MAX];
105    char    dns1[PROPERTY_VALUE_MAX];
106    char    dns2[PROPERTY_VALUE_MAX];
107    char    dns3[PROPERTY_VALUE_MAX];
108    char    dns4[PROPERTY_VALUE_MAX];
109    const char *dns[5] = {dns1, dns2, dns3, dns4, NULL};
110    char  server[PROPERTY_VALUE_MAX];
111    uint32_t lease;
112    char vendorInfo[PROPERTY_VALUE_MAX];
113    char domains[PROPERTY_VALUE_MAX];
114    char mtu[PROPERTY_VALUE_MAX];
115
116    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
117    if (nameStr == NULL) return (jboolean)false;
118
119    result = ::dhcp_get_results(nameStr, ipaddr, gateway, &prefixLength,
120            dns, server, &lease, vendorInfo, domains, mtu);
121    if (result != 0) {
122        ALOGD("dhcp_get_results failed : %s (%s)", nameStr, ::dhcp_get_errmsg());
123    }
124
125    env->ReleaseStringUTFChars(ifname, nameStr);
126    if (result == 0) {
127        env->CallVoidMethod(dhcpResults, dhcpResultsFieldIds.clear);
128
129        // set the linkAddress
130        // dhcpResults->addLinkAddress(inetAddress, prefixLength)
131        result = env->CallBooleanMethod(dhcpResults, dhcpResultsFieldIds.setIpAddress,
132                env->NewStringUTF(ipaddr), prefixLength);
133    }
134
135    if (result == 0) {
136        // set the gateway
137        result = env->CallBooleanMethod(dhcpResults,
138                dhcpResultsFieldIds.setGateway, env->NewStringUTF(gateway));
139    }
140
141    if (result == 0) {
142        // dhcpResults->addDns(new InetAddress(dns1))
143        result = env->CallBooleanMethod(dhcpResults,
144                dhcpResultsFieldIds.addDns, env->NewStringUTF(dns1));
145    }
146
147    if (result == 0) {
148        env->CallVoidMethod(dhcpResults, dhcpResultsFieldIds.setDomains,
149                env->NewStringUTF(domains));
150
151        result = env->CallBooleanMethod(dhcpResults,
152                dhcpResultsFieldIds.addDns, env->NewStringUTF(dns2));
153
154        if (result == 0) {
155            result = env->CallBooleanMethod(dhcpResults,
156                    dhcpResultsFieldIds.addDns, env->NewStringUTF(dns3));
157            if (result == 0) {
158                result = env->CallBooleanMethod(dhcpResults,
159                        dhcpResultsFieldIds.addDns, env->NewStringUTF(dns4));
160            }
161        }
162    }
163
164    if (result == 0) {
165        // dhcpResults->setServerAddress(new InetAddress(server))
166        result = env->CallBooleanMethod(dhcpResults, dhcpResultsFieldIds.setServerAddress,
167                env->NewStringUTF(server));
168    }
169
170    if (result == 0) {
171        // dhcpResults->setLeaseDuration(lease)
172        env->CallVoidMethod(dhcpResults,
173                dhcpResultsFieldIds.setLeaseDuration, lease);
174
175        // dhcpResults->setVendorInfo(vendorInfo)
176        env->CallVoidMethod(dhcpResults, dhcpResultsFieldIds.setVendorInfo,
177                env->NewStringUTF(vendorInfo));
178    }
179    return (jboolean)(result == 0);
180}
181
182static jboolean android_net_utils_startDhcp(JNIEnv* env, jobject clazz, jstring ifname)
183{
184    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
185    if (nameStr == NULL) return (jboolean)false;
186    if (::dhcp_start(nameStr) != 0) {
187        ALOGD("dhcp_start failed : %s", nameStr);
188        return (jboolean)false;
189    }
190    return (jboolean)true;
191}
192
193static jboolean android_net_utils_startDhcpRenew(JNIEnv* env, jobject clazz, jstring ifname)
194{
195    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
196    if (nameStr == NULL) return (jboolean)false;
197    if (::dhcp_start_renew(nameStr) != 0) {
198        ALOGD("dhcp_start_renew failed : %s", nameStr);
199        return (jboolean)false;
200    }
201    return (jboolean)true;
202}
203
204static jboolean android_net_utils_stopDhcp(JNIEnv* env, jobject clazz, jstring ifname)
205{
206    int result;
207
208    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
209    result = ::dhcp_stop(nameStr);
210    env->ReleaseStringUTFChars(ifname, nameStr);
211    return (jboolean)(result == 0);
212}
213
214static jboolean android_net_utils_releaseDhcpLease(JNIEnv* env, jobject clazz, jstring ifname)
215{
216    int result;
217
218    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
219    result = ::dhcp_release_lease(nameStr);
220    env->ReleaseStringUTFChars(ifname, nameStr);
221    return (jboolean)(result == 0);
222}
223
224static jstring android_net_utils_getDhcpError(JNIEnv* env, jobject clazz)
225{
226    return env->NewStringUTF(::dhcp_get_errmsg());
227}
228
229static void android_net_utils_attachDhcpFilter(JNIEnv *env, jobject clazz, jobject javaFd)
230{
231    int fd = jniGetFDFromFileDescriptor(env, javaFd);
232    uint32_t ip_offset = sizeof(ether_header);
233    uint32_t proto_offset = ip_offset + offsetof(iphdr, protocol);
234    uint32_t flags_offset = ip_offset +  offsetof(iphdr, frag_off);
235    uint32_t dport_indirect_offset = ip_offset + offsetof(udphdr, dest);
236    struct sock_filter filter_code[] = {
237        // Check the protocol is UDP.
238        BPF_STMT(BPF_LD  | BPF_B   | BPF_ABS,  proto_offset),
239        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,    IPPROTO_UDP, 0, 6),
240
241        // Check this is not a fragment.
242        BPF_STMT(BPF_LD  | BPF_H    | BPF_ABS, flags_offset),
243        BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K,   0x1fff, 4, 0),
244
245        // Get the IP header length.
246        BPF_STMT(BPF_LDX | BPF_B    | BPF_MSH, ip_offset),
247
248        // Check the destination port.
249        BPF_STMT(BPF_LD  | BPF_H    | BPF_IND, dport_indirect_offset),
250        BPF_JUMP(BPF_JMP | BPF_JEQ  | BPF_K,   kDhcpClientPort, 0, 1),
251
252        // Accept or reject.
253        BPF_STMT(BPF_RET | BPF_K,              0xffff),
254        BPF_STMT(BPF_RET | BPF_K,              0)
255    };
256    struct sock_fprog filter = {
257        sizeof(filter_code) / sizeof(filter_code[0]),
258        filter_code,
259    };
260
261    if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
262        jniThrowExceptionFmt(env, "java/net/SocketException",
263                "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
264    }
265}
266
267static jboolean android_net_utils_bindProcessToNetwork(JNIEnv *env, jobject thiz, jint netId)
268{
269    return (jboolean) !setNetworkForProcess(netId);
270}
271
272static jint android_net_utils_getNetworkBoundToProcess(JNIEnv *env, jobject thiz)
273{
274    return getNetworkForProcess();
275}
276
277static jboolean android_net_utils_bindProcessToNetworkForHostResolution(JNIEnv *env, jobject thiz,
278        jint netId)
279{
280    return (jboolean) !setNetworkForResolv(netId);
281}
282
283static jint android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jint socket,
284        jint netId)
285{
286    return setNetworkForSocket(netId, socket);
287}
288
289static jboolean android_net_utils_protectFromVpn(JNIEnv *env, jobject thiz, jint socket)
290{
291    return (jboolean) !protectFromVpn(socket);
292}
293
294
295// ----------------------------------------------------------------------------
296
297/*
298 * JNI registration.
299 */
300static JNINativeMethod gNetworkUtilMethods[] = {
301    /* name, signature, funcPtr */
302    { "resetConnections", "(Ljava/lang/String;I)I",  (void *)android_net_utils_resetConnections },
303    { "startDhcp", "(Ljava/lang/String;)Z",  (void *)android_net_utils_startDhcp },
304    { "startDhcpRenew", "(Ljava/lang/String;)Z",  (void *)android_net_utils_startDhcpRenew },
305    { "getDhcpResults", "(Ljava/lang/String;Landroid/net/DhcpResults;)Z",  (void *)android_net_utils_getDhcpResults },
306    { "stopDhcp", "(Ljava/lang/String;)Z",  (void *)android_net_utils_stopDhcp },
307    { "releaseDhcpLease", "(Ljava/lang/String;)Z",  (void *)android_net_utils_releaseDhcpLease },
308    { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_utils_getDhcpError },
309    { "bindProcessToNetwork", "(I)Z", (void*) android_net_utils_bindProcessToNetwork },
310    { "getNetworkBoundToProcess", "()I", (void*) android_net_utils_getNetworkBoundToProcess },
311    { "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
312    { "bindSocketToNetwork", "(II)I", (void*) android_net_utils_bindSocketToNetwork },
313    { "protectFromVpn", "(I)Z", (void*)android_net_utils_protectFromVpn },
314    { "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDhcpFilter },
315};
316
317int register_android_net_NetworkUtils(JNIEnv* env)
318{
319    jclass dhcpResultsClass = FindClassOrDie(env, "android/net/DhcpResults");
320
321    dhcpResultsFieldIds.clear = GetMethodIDOrDie(env, dhcpResultsClass, "clear", "()V");
322    dhcpResultsFieldIds.setIpAddress =GetMethodIDOrDie(env, dhcpResultsClass, "setIpAddress",
323            "(Ljava/lang/String;I)Z");
324    dhcpResultsFieldIds.setGateway = GetMethodIDOrDie(env, dhcpResultsClass, "setGateway",
325            "(Ljava/lang/String;)Z");
326    dhcpResultsFieldIds.addDns = GetMethodIDOrDie(env, dhcpResultsClass, "addDns",
327            "(Ljava/lang/String;)Z");
328    dhcpResultsFieldIds.setDomains = GetMethodIDOrDie(env, dhcpResultsClass, "setDomains",
329            "(Ljava/lang/String;)V");
330    dhcpResultsFieldIds.setServerAddress = GetMethodIDOrDie(env, dhcpResultsClass,
331            "setServerAddress", "(Ljava/lang/String;)Z");
332    dhcpResultsFieldIds.setLeaseDuration = GetMethodIDOrDie(env, dhcpResultsClass,
333            "setLeaseDuration", "(I)V");
334    dhcpResultsFieldIds.setVendorInfo = GetMethodIDOrDie(env, dhcpResultsClass, "setVendorInfo",
335            "(Ljava/lang/String;)V");
336
337    return RegisterMethodsOrDie(env, NETUTILS_PKG_NAME, gNetworkUtilMethods,
338                                NELEM(gNetworkUtilMethods));
339}
340
341}; // namespace android
342