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
38 extern "C" {
39 int ifc_enable(const char *ifname);
40 int ifc_disable(const char *ifname);
41 int ifc_reset_connections(const char *ifname, int reset_mask);
42
43 int dhcp_start(const char * const ifname);
44 int dhcp_start_renew(const char * const ifname);
45 int 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
56 int dhcp_stop(const char *ifname);
57 int dhcp_release_lease(const char *ifname);
58 char *dhcp_get_errmsg();
59 }
60
61 #define NETUTILS_PKG_NAME "android/net/NetworkUtils"
62
63 namespace android {
64
65 static 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 */
72 static 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
android_net_utils_resetConnections(JNIEnv * env,jobject clazz,jstring ifname,jint mask)83 static 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
android_net_utils_getDhcpResults(JNIEnv * env,jobject clazz,jstring ifname,jobject dhcpResults)98 static 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
android_net_utils_startDhcp(JNIEnv * env,jobject clazz,jstring ifname)182 static 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
android_net_utils_startDhcpRenew(JNIEnv * env,jobject clazz,jstring ifname)193 static 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
android_net_utils_stopDhcp(JNIEnv * env,jobject clazz,jstring ifname)204 static 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
android_net_utils_releaseDhcpLease(JNIEnv * env,jobject clazz,jstring ifname)214 static 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
android_net_utils_getDhcpError(JNIEnv * env,jobject clazz)224 static jstring android_net_utils_getDhcpError(JNIEnv* env, jobject clazz)
225 {
226 return env->NewStringUTF(::dhcp_get_errmsg());
227 }
228
android_net_utils_attachDhcpFilter(JNIEnv * env,jobject clazz,jobject javaFd)229 static 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
android_net_utils_bindProcessToNetwork(JNIEnv * env,jobject thiz,jint netId)267 static jboolean android_net_utils_bindProcessToNetwork(JNIEnv *env, jobject thiz, jint netId)
268 {
269 return (jboolean) !setNetworkForProcess(netId);
270 }
271
android_net_utils_getBoundNetworkForProcess(JNIEnv * env,jobject thiz)272 static jint android_net_utils_getBoundNetworkForProcess(JNIEnv *env, jobject thiz)
273 {
274 return getNetworkForProcess();
275 }
276
android_net_utils_bindProcessToNetworkForHostResolution(JNIEnv * env,jobject thiz,jint netId)277 static jboolean android_net_utils_bindProcessToNetworkForHostResolution(JNIEnv *env, jobject thiz,
278 jint netId)
279 {
280 return (jboolean) !setNetworkForResolv(netId);
281 }
282
android_net_utils_bindSocketToNetwork(JNIEnv * env,jobject thiz,jint socket,jint netId)283 static jint android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jint socket,
284 jint netId)
285 {
286 return setNetworkForSocket(netId, socket);
287 }
288
android_net_utils_protectFromVpn(JNIEnv * env,jobject thiz,jint socket)289 static jboolean android_net_utils_protectFromVpn(JNIEnv *env, jobject thiz, jint socket)
290 {
291 return (jboolean) !protectFromVpn(socket);
292 }
293
android_net_utils_queryUserAccess(JNIEnv * env,jobject thiz,jint uid,jint netId)294 static jboolean android_net_utils_queryUserAccess(JNIEnv *env, jobject thiz, jint uid, jint netId)
295 {
296 return (jboolean) !queryUserAccess(uid, netId);
297 }
298
299
300 // ----------------------------------------------------------------------------
301
302 /*
303 * JNI registration.
304 */
305 static JNINativeMethod gNetworkUtilMethods[] = {
306 /* name, signature, funcPtr */
307 { "resetConnections", "(Ljava/lang/String;I)I", (void *)android_net_utils_resetConnections },
308 { "startDhcp", "(Ljava/lang/String;)Z", (void *)android_net_utils_startDhcp },
309 { "startDhcpRenew", "(Ljava/lang/String;)Z", (void *)android_net_utils_startDhcpRenew },
310 { "getDhcpResults", "(Ljava/lang/String;Landroid/net/DhcpResults;)Z", (void *)android_net_utils_getDhcpResults },
311 { "stopDhcp", "(Ljava/lang/String;)Z", (void *)android_net_utils_stopDhcp },
312 { "releaseDhcpLease", "(Ljava/lang/String;)Z", (void *)android_net_utils_releaseDhcpLease },
313 { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_utils_getDhcpError },
314 { "bindProcessToNetwork", "(I)Z", (void*) android_net_utils_bindProcessToNetwork },
315 { "getBoundNetworkForProcess", "()I", (void*) android_net_utils_getBoundNetworkForProcess },
316 { "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
317 { "bindSocketToNetwork", "(II)I", (void*) android_net_utils_bindSocketToNetwork },
318 { "protectFromVpn", "(I)Z", (void*)android_net_utils_protectFromVpn },
319 { "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess },
320 { "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDhcpFilter },
321 };
322
register_android_net_NetworkUtils(JNIEnv * env)323 int register_android_net_NetworkUtils(JNIEnv* env)
324 {
325 jclass dhcpResultsClass = FindClassOrDie(env, "android/net/DhcpResults");
326
327 dhcpResultsFieldIds.clear = GetMethodIDOrDie(env, dhcpResultsClass, "clear", "()V");
328 dhcpResultsFieldIds.setIpAddress =GetMethodIDOrDie(env, dhcpResultsClass, "setIpAddress",
329 "(Ljava/lang/String;I)Z");
330 dhcpResultsFieldIds.setGateway = GetMethodIDOrDie(env, dhcpResultsClass, "setGateway",
331 "(Ljava/lang/String;)Z");
332 dhcpResultsFieldIds.addDns = GetMethodIDOrDie(env, dhcpResultsClass, "addDns",
333 "(Ljava/lang/String;)Z");
334 dhcpResultsFieldIds.setDomains = GetMethodIDOrDie(env, dhcpResultsClass, "setDomains",
335 "(Ljava/lang/String;)V");
336 dhcpResultsFieldIds.setServerAddress = GetMethodIDOrDie(env, dhcpResultsClass,
337 "setServerAddress", "(Ljava/lang/String;)Z");
338 dhcpResultsFieldIds.setLeaseDuration = GetMethodIDOrDie(env, dhcpResultsClass,
339 "setLeaseDuration", "(I)V");
340 dhcpResultsFieldIds.setVendorInfo = GetMethodIDOrDie(env, dhcpResultsClass, "setVendorInfo",
341 "(Ljava/lang/String;)V");
342
343 return RegisterMethodsOrDie(env, NETUTILS_PKG_NAME, gNetworkUtilMethods,
344 NELEM(gNetworkUtilMethods));
345 }
346
347 }; // namespace android
348