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