blob: 2d0dfd2f7f26abd319022e2e7e128ca8c309d810 [file] [log] [blame]
Chia-chi Yehff3bdca2011-05-23 17:26:46 -07001/*
2 * Copyright (C) 2011 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_NDEBUG 0
18
19#define LOG_TAG "VpnJni"
20#include <cutils/log.h>
Sreeram Ramachandranf4e0c0c2014-07-27 14:18:26 -070021#include "netutils/ifc.h"
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070022
23#include <stdio.h>
24#include <string.h>
25#include <sys/ioctl.h>
26#include <sys/types.h>
27#include <sys/stat.h>
28#include <sys/socket.h>
29#include <netinet/in.h>
30#include <arpa/inet.h>
31#include <errno.h>
32#include <fcntl.h>
33
34#include <linux/if.h>
35#include <linux/if_tun.h>
36#include <linux/route.h>
37#include <linux/ipv6_route.h>
38
39#include "jni.h"
40#include "JNIHelp.h"
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070041
42namespace android
43{
44
Chia-chi Yehf4e3bf82011-06-30 12:33:17 -070045static int inet4 = -1;
46static int inet6 = -1;
47
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070048static inline in_addr_t *as_in_addr(sockaddr *sa) {
49 return &((sockaddr_in *)sa)->sin_addr.s_addr;
50}
51
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070052//------------------------------------------------------------------------------
53
54#define SYSTEM_ERROR -1
55#define BAD_ARGUMENT -2
56
Chia-chi Yeh97a61562011-07-14 15:05:05 -070057static int create_interface(int mtu)
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070058{
Chia-chi Yehf4e3bf82011-06-30 12:33:17 -070059 int tun = open("/dev/tun", O_RDWR | O_NONBLOCK);
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070060
61 ifreq ifr4;
62 memset(&ifr4, 0, sizeof(ifr4));
63
64 // Allocate interface.
Chia-chi Yeh6224b5e2011-06-13 15:26:51 -070065 ifr4.ifr_flags = IFF_TUN | IFF_NO_PI;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070066 if (ioctl(tun, TUNSETIFF, &ifr4)) {
Steve Block3762c312012-01-06 19:20:56 +000067 ALOGE("Cannot allocate TUN: %s", strerror(errno));
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070068 goto error;
69 }
70
71 // Activate interface.
72 ifr4.ifr_flags = IFF_UP;
73 if (ioctl(inet4, SIOCSIFFLAGS, &ifr4)) {
Steve Block3762c312012-01-06 19:20:56 +000074 ALOGE("Cannot activate %s: %s", ifr4.ifr_name, strerror(errno));
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070075 goto error;
76 }
77
Chia-chi Yeh36632272011-06-13 15:05:37 -070078 // Set MTU if it is specified.
79 ifr4.ifr_mtu = mtu;
80 if (mtu > 0 && ioctl(inet4, SIOCSIFMTU, &ifr4)) {
Steve Block3762c312012-01-06 19:20:56 +000081 ALOGE("Cannot set MTU on %s: %s", ifr4.ifr_name, strerror(errno));
Chia-chi Yeh36632272011-06-13 15:05:37 -070082 goto error;
83 }
84
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070085 return tun;
86
87error:
88 close(tun);
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070089 return SYSTEM_ERROR;
90}
91
Chia-chi Yeh97a61562011-07-14 15:05:05 -070092static int get_interface_name(char *name, int tun)
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070093{
Chia-chi Yehf4e3bf82011-06-30 12:33:17 -070094 ifreq ifr4;
Chia-chi Yeh97a61562011-07-14 15:05:05 -070095 if (ioctl(tun, TUNGETIFF, &ifr4)) {
Steve Block3762c312012-01-06 19:20:56 +000096 ALOGE("Cannot get interface name: %s", strerror(errno));
Chia-chi Yeh97a61562011-07-14 15:05:05 -070097 return SYSTEM_ERROR;
98 }
99 strncpy(name, ifr4.ifr_name, IFNAMSIZ);
100 return 0;
101}
102
103static int get_interface_index(const char *name)
104{
105 ifreq ifr4;
106 strncpy(ifr4.ifr_name, name, IFNAMSIZ);
107 if (ioctl(inet4, SIOGIFINDEX, &ifr4)) {
Steve Block3762c312012-01-06 19:20:56 +0000108 ALOGE("Cannot get index of %s: %s", name, strerror(errno));
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700109 return SYSTEM_ERROR;
110 }
111 return ifr4.ifr_ifindex;
112}
113
114static int set_addresses(const char *name, const char *addresses)
115{
116 int index = get_interface_index(name);
117 if (index < 0) {
118 return index;
119 }
120
121 ifreq ifr4;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700122 memset(&ifr4, 0, sizeof(ifr4));
Chia-chi Yehf4e3bf82011-06-30 12:33:17 -0700123 strncpy(ifr4.ifr_name, name, IFNAMSIZ);
Chia-chi Yeh36673692011-06-13 14:24:13 -0700124 ifr4.ifr_addr.sa_family = AF_INET;
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700125 ifr4.ifr_netmask.sa_family = AF_INET;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700126
127 in6_ifreq ifr6;
128 memset(&ifr6, 0, sizeof(ifr6));
129 ifr6.ifr6_ifindex = index;
130
131 char address[65];
132 int prefix;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700133 int chars;
134 int count = 0;
135
136 while (sscanf(addresses, " %64[^/]/%d %n", address, &prefix, &chars) == 2) {
137 addresses += chars;
138
139 if (strchr(address, ':')) {
140 // Add an IPv6 address.
141 if (inet_pton(AF_INET6, address, &ifr6.ifr6_addr) != 1 ||
142 prefix < 0 || prefix > 128) {
143 count = BAD_ARGUMENT;
144 break;
145 }
146
147 ifr6.ifr6_prefixlen = prefix;
148 if (ioctl(inet6, SIOCSIFADDR, &ifr6)) {
149 count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
150 break;
151 }
152 } else {
153 // Add an IPv4 address.
154 if (inet_pton(AF_INET, address, as_in_addr(&ifr4.ifr_addr)) != 1 ||
155 prefix < 0 || prefix > 32) {
156 count = BAD_ARGUMENT;
157 break;
158 }
159
160 if (count) {
161 sprintf(ifr4.ifr_name, "%s:%d", name, count);
162 }
163 if (ioctl(inet4, SIOCSIFADDR, &ifr4)) {
164 count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
165 break;
166 }
167
168 in_addr_t mask = prefix ? (~0 << (32 - prefix)) : 0;
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700169 *as_in_addr(&ifr4.ifr_netmask) = htonl(mask);
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700170 if (ioctl(inet4, SIOCSIFNETMASK, &ifr4)) {
171 count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
172 break;
173 }
174 }
Steve Block5baa3a62011-12-20 16:23:08 +0000175 ALOGD("Address added on %s: %s/%d", name, address, prefix);
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700176 ++count;
177 }
178
179 if (count == BAD_ARGUMENT) {
Steve Block3762c312012-01-06 19:20:56 +0000180 ALOGE("Invalid address: %s/%d", address, prefix);
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700181 } else if (count == SYSTEM_ERROR) {
Steve Block3762c312012-01-06 19:20:56 +0000182 ALOGE("Cannot add address: %s/%d: %s", address, prefix, strerror(errno));
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700183 } else if (*addresses) {
Steve Block3762c312012-01-06 19:20:56 +0000184 ALOGE("Invalid address: %s", addresses);
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700185 count = BAD_ARGUMENT;
186 }
187
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700188 return count;
189}
190
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700191static int reset_interface(const char *name)
192{
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700193 ifreq ifr4;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700194 strncpy(ifr4.ifr_name, name, IFNAMSIZ);
Chia-chi Yehf4e3bf82011-06-30 12:33:17 -0700195 ifr4.ifr_flags = 0;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700196
197 if (ioctl(inet4, SIOCSIFFLAGS, &ifr4) && errno != ENODEV) {
Steve Block3762c312012-01-06 19:20:56 +0000198 ALOGE("Cannot reset %s: %s", name, strerror(errno));
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700199 return SYSTEM_ERROR;
200 }
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700201 return 0;
202}
203
204static int check_interface(const char *name)
205{
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700206 ifreq ifr4;
207 strncpy(ifr4.ifr_name, name, IFNAMSIZ);
Chia-chi Yeh6ddd5742011-06-13 16:08:01 -0700208 ifr4.ifr_flags = 0;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700209
210 if (ioctl(inet4, SIOCGIFFLAGS, &ifr4) && errno != ENODEV) {
Steve Block3762c312012-01-06 19:20:56 +0000211 ALOGE("Cannot check %s: %s", name, strerror(errno));
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700212 }
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700213 return ifr4.ifr_flags;
214}
215
Sreeram Ramachandranf4e0c0c2014-07-27 14:18:26 -0700216static bool modifyAddress(JNIEnv *env, jobject thiz, jstring jName, jstring jAddress,
217 jint jPrefixLength, bool add)
218{
219 int error = SYSTEM_ERROR;
220 const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
221 const char *address = jAddress ? env->GetStringUTFChars(jAddress, NULL) : NULL;
222
223 if (!name) {
224 jniThrowNullPointerException(env, "name");
225 } else if (!address) {
226 jniThrowNullPointerException(env, "address");
227 } else {
228 if (add) {
Andreas Gampe8dcf5932014-09-30 16:41:19 -0700229 if ((error = ifc_add_address(name, address, jPrefixLength)) != 0) {
Sreeram Ramachandranf4e0c0c2014-07-27 14:18:26 -0700230 ALOGE("Cannot add address %s/%d on interface %s (%s)", address, jPrefixLength, name,
231 strerror(-error));
232 }
233 } else {
Andreas Gampe8dcf5932014-09-30 16:41:19 -0700234 if ((error = ifc_del_address(name, address, jPrefixLength)) != 0) {
Sreeram Ramachandranf4e0c0c2014-07-27 14:18:26 -0700235 ALOGE("Cannot del address %s/%d on interface %s (%s)", address, jPrefixLength, name,
236 strerror(-error));
237 }
238 }
239 }
240
241 if (name) {
242 env->ReleaseStringUTFChars(jName, name);
243 }
244 if (address) {
245 env->ReleaseStringUTFChars(jAddress, address);
246 }
247 return !error;
248}
249
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700250//------------------------------------------------------------------------------
251
252static void throwException(JNIEnv *env, int error, const char *message)
253{
254 if (error == SYSTEM_ERROR) {
255 jniThrowException(env, "java/lang/IllegalStateException", message);
256 } else {
257 jniThrowException(env, "java/lang/IllegalArgumentException", message);
258 }
259}
260
Andreas Gampe8dcf5932014-09-30 16:41:19 -0700261static jint create(JNIEnv *env, jobject /* thiz */, jint mtu)
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700262{
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700263 int tun = create_interface(mtu);
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700264 if (tun < 0) {
265 throwException(env, tun, "Cannot create interface");
Chia-chi Yeh36673692011-06-13 14:24:13 -0700266 return -1;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700267 }
Chia-chi Yeh36673692011-06-13 14:24:13 -0700268 return tun;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700269}
270
Andreas Gampe8dcf5932014-09-30 16:41:19 -0700271static jstring getName(JNIEnv *env, jobject /* thiz */, jint tun)
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700272{
273 char name[IFNAMSIZ];
Chia-chi Yehf4e3bf82011-06-30 12:33:17 -0700274 if (get_interface_name(name, tun) < 0) {
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700275 throwException(env, SYSTEM_ERROR, "Cannot get interface name");
276 return NULL;
277 }
278 return env->NewStringUTF(name);
279}
280
Andreas Gampe8dcf5932014-09-30 16:41:19 -0700281static jint setAddresses(JNIEnv *env, jobject /* thiz */, jstring jName,
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700282 jstring jAddresses)
283{
284 const char *name = NULL;
285 const char *addresses = NULL;
286 int count = -1;
287
288 name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
289 if (!name) {
290 jniThrowNullPointerException(env, "name");
291 goto error;
292 }
293 addresses = jAddresses ? env->GetStringUTFChars(jAddresses, NULL) : NULL;
294 if (!addresses) {
295 jniThrowNullPointerException(env, "addresses");
296 goto error;
297 }
298 count = set_addresses(name, addresses);
299 if (count < 0) {
300 throwException(env, count, "Cannot set address");
301 count = -1;
302 }
303
304error:
305 if (name) {
306 env->ReleaseStringUTFChars(jName, name);
307 }
308 if (addresses) {
309 env->ReleaseStringUTFChars(jAddresses, addresses);
310 }
311 return count;
312}
313
Andreas Gampe8dcf5932014-09-30 16:41:19 -0700314static void reset(JNIEnv *env, jobject /* thiz */, jstring jName)
Chia-chi Yehf4e3bf82011-06-30 12:33:17 -0700315{
316 const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700317 if (!name) {
318 jniThrowNullPointerException(env, "name");
319 return;
320 }
321 if (reset_interface(name) < 0) {
322 throwException(env, SYSTEM_ERROR, "Cannot reset interface");
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700323 }
324 env->ReleaseStringUTFChars(jName, name);
325}
326
Andreas Gampe8dcf5932014-09-30 16:41:19 -0700327static jint check(JNIEnv *env, jobject /* thiz */, jstring jName)
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700328{
Chia-chi Yehf4e3bf82011-06-30 12:33:17 -0700329 const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700330 if (!name) {
331 jniThrowNullPointerException(env, "name");
332 return 0;
333 }
334 int flags = check_interface(name);
335 env->ReleaseStringUTFChars(jName, name);
336 return flags;
337}
338
Sreeram Ramachandranf4e0c0c2014-07-27 14:18:26 -0700339static bool addAddress(JNIEnv *env, jobject thiz, jstring jName, jstring jAddress,
340 jint jPrefixLength)
341{
342 return modifyAddress(env, thiz, jName, jAddress, jPrefixLength, true);
343}
344
345static bool delAddress(JNIEnv *env, jobject thiz, jstring jName, jstring jAddress,
346 jint jPrefixLength)
347{
348 return modifyAddress(env, thiz, jName, jAddress, jPrefixLength, false);
349}
350
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700351//------------------------------------------------------------------------------
352
Daniel Micay76f6a862015-09-19 17:31:01 -0400353static const JNINativeMethod gMethods[] = {
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700354 {"jniCreate", "(I)I", (void *)create},
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700355 {"jniGetName", "(I)Ljava/lang/String;", (void *)getName},
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700356 {"jniSetAddresses", "(Ljava/lang/String;Ljava/lang/String;)I", (void *)setAddresses},
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700357 {"jniReset", "(Ljava/lang/String;)V", (void *)reset},
358 {"jniCheck", "(Ljava/lang/String;)I", (void *)check},
Sreeram Ramachandranf4e0c0c2014-07-27 14:18:26 -0700359 {"jniAddAddress", "(Ljava/lang/String;Ljava/lang/String;I)Z", (void *)addAddress},
360 {"jniDelAddress", "(Ljava/lang/String;Ljava/lang/String;I)Z", (void *)delAddress},
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700361};
362
363int register_android_server_connectivity_Vpn(JNIEnv *env)
364{
Chia-chi Yehf4e3bf82011-06-30 12:33:17 -0700365 if (inet4 == -1) {
366 inet4 = socket(AF_INET, SOCK_DGRAM, 0);
367 }
368 if (inet6 == -1) {
369 inet6 = socket(AF_INET6, SOCK_DGRAM, 0);
370 }
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700371 return jniRegisterNativeMethods(env, "com/android/server/connectivity/Vpn",
372 gMethods, NELEM(gMethods));
373}
374
375};