blob: d9b8a1488d690f321f70d7dee226b89cf50aa093 [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>
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070021
22#include <stdio.h>
23#include <string.h>
24#include <sys/ioctl.h>
25#include <sys/types.h>
26#include <sys/stat.h>
27#include <sys/socket.h>
28#include <netinet/in.h>
29#include <arpa/inet.h>
30#include <errno.h>
31#include <fcntl.h>
32
33#include <linux/if.h>
34#include <linux/if_tun.h>
35#include <linux/route.h>
36#include <linux/ipv6_route.h>
37
38#include "jni.h"
39#include "JNIHelp.h"
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070040
41namespace android
42{
43
Chia-chi Yehf4e3bf82011-06-30 12:33:17 -070044static int inet4 = -1;
45static int inet6 = -1;
46
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070047static inline in_addr_t *as_in_addr(sockaddr *sa) {
48 return &((sockaddr_in *)sa)->sin_addr.s_addr;
49}
50
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070051//------------------------------------------------------------------------------
52
53#define SYSTEM_ERROR -1
54#define BAD_ARGUMENT -2
55
Chia-chi Yeh97a61562011-07-14 15:05:05 -070056static int create_interface(int mtu)
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070057{
Chia-chi Yehf4e3bf82011-06-30 12:33:17 -070058 int tun = open("/dev/tun", O_RDWR | O_NONBLOCK);
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070059
60 ifreq ifr4;
61 memset(&ifr4, 0, sizeof(ifr4));
62
63 // Allocate interface.
Chia-chi Yeh6224b5e2011-06-13 15:26:51 -070064 ifr4.ifr_flags = IFF_TUN | IFF_NO_PI;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070065 if (ioctl(tun, TUNSETIFF, &ifr4)) {
66 LOGE("Cannot allocate TUN: %s", strerror(errno));
67 goto error;
68 }
69
70 // Activate interface.
71 ifr4.ifr_flags = IFF_UP;
72 if (ioctl(inet4, SIOCSIFFLAGS, &ifr4)) {
73 LOGE("Cannot activate %s: %s", ifr4.ifr_name, strerror(errno));
74 goto error;
75 }
76
Chia-chi Yeh36632272011-06-13 15:05:37 -070077 // Set MTU if it is specified.
78 ifr4.ifr_mtu = mtu;
79 if (mtu > 0 && ioctl(inet4, SIOCSIFMTU, &ifr4)) {
80 LOGE("Cannot set MTU on %s: %s", ifr4.ifr_name, strerror(errno));
81 goto error;
82 }
83
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070084 return tun;
85
86error:
87 close(tun);
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070088 return SYSTEM_ERROR;
89}
90
Chia-chi Yeh97a61562011-07-14 15:05:05 -070091static int get_interface_name(char *name, int tun)
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070092{
Chia-chi Yehf4e3bf82011-06-30 12:33:17 -070093 ifreq ifr4;
Chia-chi Yeh97a61562011-07-14 15:05:05 -070094 if (ioctl(tun, TUNGETIFF, &ifr4)) {
95 LOGE("Cannot get interface name: %s", strerror(errno));
96 return SYSTEM_ERROR;
97 }
98 strncpy(name, ifr4.ifr_name, IFNAMSIZ);
99 return 0;
100}
101
102static int get_interface_index(const char *name)
103{
104 ifreq ifr4;
105 strncpy(ifr4.ifr_name, name, IFNAMSIZ);
106 if (ioctl(inet4, SIOGIFINDEX, &ifr4)) {
107 LOGE("Cannot get index of %s: %s", name, strerror(errno));
108 return SYSTEM_ERROR;
109 }
110 return ifr4.ifr_ifindex;
111}
112
113static int set_addresses(const char *name, const char *addresses)
114{
115 int index = get_interface_index(name);
116 if (index < 0) {
117 return index;
118 }
119
120 ifreq ifr4;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700121 memset(&ifr4, 0, sizeof(ifr4));
Chia-chi Yehf4e3bf82011-06-30 12:33:17 -0700122 strncpy(ifr4.ifr_name, name, IFNAMSIZ);
Chia-chi Yeh36673692011-06-13 14:24:13 -0700123 ifr4.ifr_addr.sa_family = AF_INET;
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700124 ifr4.ifr_netmask.sa_family = AF_INET;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700125
126 in6_ifreq ifr6;
127 memset(&ifr6, 0, sizeof(ifr6));
128 ifr6.ifr6_ifindex = index;
129
130 char address[65];
131 int prefix;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700132 int chars;
133 int count = 0;
134
135 while (sscanf(addresses, " %64[^/]/%d %n", address, &prefix, &chars) == 2) {
136 addresses += chars;
137
138 if (strchr(address, ':')) {
139 // Add an IPv6 address.
140 if (inet_pton(AF_INET6, address, &ifr6.ifr6_addr) != 1 ||
141 prefix < 0 || prefix > 128) {
142 count = BAD_ARGUMENT;
143 break;
144 }
145
146 ifr6.ifr6_prefixlen = prefix;
147 if (ioctl(inet6, SIOCSIFADDR, &ifr6)) {
148 count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
149 break;
150 }
151 } else {
152 // Add an IPv4 address.
153 if (inet_pton(AF_INET, address, as_in_addr(&ifr4.ifr_addr)) != 1 ||
154 prefix < 0 || prefix > 32) {
155 count = BAD_ARGUMENT;
156 break;
157 }
158
159 if (count) {
160 sprintf(ifr4.ifr_name, "%s:%d", name, count);
161 }
162 if (ioctl(inet4, SIOCSIFADDR, &ifr4)) {
163 count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
164 break;
165 }
166
167 in_addr_t mask = prefix ? (~0 << (32 - prefix)) : 0;
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700168 *as_in_addr(&ifr4.ifr_netmask) = htonl(mask);
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700169 if (ioctl(inet4, SIOCSIFNETMASK, &ifr4)) {
170 count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
171 break;
172 }
173 }
Steve Block5baa3a62011-12-20 16:23:08 +0000174 ALOGD("Address added on %s: %s/%d", name, address, prefix);
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700175 ++count;
176 }
177
178 if (count == BAD_ARGUMENT) {
179 LOGE("Invalid address: %s/%d", address, prefix);
180 } else if (count == SYSTEM_ERROR) {
181 LOGE("Cannot add address: %s/%d: %s", address, prefix, strerror(errno));
182 } else if (*addresses) {
183 LOGE("Invalid address: %s", addresses);
184 count = BAD_ARGUMENT;
185 }
186
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700187 return count;
188}
189
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700190static int set_routes(const char *name, const char *routes)
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700191{
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700192 int index = get_interface_index(name);
193 if (index < 0) {
194 return index;
195 }
196
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700197 rtentry rt4;
198 memset(&rt4, 0, sizeof(rt4));
199 rt4.rt_dev = (char *)name;
200 rt4.rt_flags = RTF_UP;
Chia-chi Yeh36673692011-06-13 14:24:13 -0700201 rt4.rt_dst.sa_family = AF_INET;
202 rt4.rt_genmask.sa_family = AF_INET;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700203
204 in6_rtmsg rt6;
205 memset(&rt6, 0, sizeof(rt6));
206 rt6.rtmsg_ifindex = index;
207 rt6.rtmsg_flags = RTF_UP;
208
209 char address[65];
210 int prefix;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700211 int chars;
212 int count = 0;
213
Chia-chi Yeh8ea92892011-06-13 16:18:39 -0700214 while (sscanf(routes, " %64[^/]/%d %n", address, &prefix, &chars) == 2) {
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700215 routes += chars;
216
217 if (strchr(address, ':')) {
218 // Add an IPv6 route.
Chia-chi Yeh8ea92892011-06-13 16:18:39 -0700219 if (inet_pton(AF_INET6, address, &rt6.rtmsg_dst) != 1 ||
Chia-chi Yehf4e3bf82011-06-30 12:33:17 -0700220 prefix < 0 || prefix > 128) {
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700221 count = BAD_ARGUMENT;
222 break;
223 }
224
Chia-chi Yehf4e3bf82011-06-30 12:33:17 -0700225 rt6.rtmsg_dst_len = prefix ? prefix : 1;
Chia-chi Yeh23a5e4b2011-06-13 16:14:49 -0700226 if (ioctl(inet6, SIOCADDRT, &rt6) && errno != EEXIST) {
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700227 count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
228 break;
229 }
Chia-chi Yehf4e3bf82011-06-30 12:33:17 -0700230
231 if (!prefix) {
232 // Split the route instead of replacing the default route.
233 rt6.rtmsg_dst.s6_addr[0] ^= 0x80;
234 if (ioctl(inet6, SIOCADDRT, &rt6) && errno != EEXIST) {
235 count = SYSTEM_ERROR;
236 break;
237 }
238 }
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700239 } else {
240 // Add an IPv4 route.
Chia-chi Yeh8ea92892011-06-13 16:18:39 -0700241 if (inet_pton(AF_INET, address, as_in_addr(&rt4.rt_dst)) != 1 ||
Chia-chi Yehf4e3bf82011-06-30 12:33:17 -0700242 prefix < 0 || prefix > 32) {
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700243 count = BAD_ARGUMENT;
244 break;
245 }
246
Chia-chi Yeh9c0835f2011-06-30 22:59:18 -0700247 in_addr_t mask = prefix ? (~0 << (32 - prefix)) : 0x80000000;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700248 *as_in_addr(&rt4.rt_genmask) = htonl(mask);
Chia-chi Yeh23a5e4b2011-06-13 16:14:49 -0700249 if (ioctl(inet4, SIOCADDRT, &rt4) && errno != EEXIST) {
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700250 count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
251 break;
252 }
Chia-chi Yehf4e3bf82011-06-30 12:33:17 -0700253
254 if (!prefix) {
255 // Split the route instead of replacing the default route.
256 *as_in_addr(&rt4.rt_dst) ^= htonl(0x80000000);
257 if (ioctl(inet4, SIOCADDRT, &rt4) && errno != EEXIST) {
258 count = SYSTEM_ERROR;
259 break;
260 }
261 }
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700262 }
Steve Block5baa3a62011-12-20 16:23:08 +0000263 ALOGD("Route added on %s: %s/%d", name, address, prefix);
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700264 ++count;
265 }
266
267 if (count == BAD_ARGUMENT) {
Chia-chi Yeh8ea92892011-06-13 16:18:39 -0700268 LOGE("Invalid route: %s/%d", address, prefix);
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700269 } else if (count == SYSTEM_ERROR) {
Chia-chi Yeh8ea92892011-06-13 16:18:39 -0700270 LOGE("Cannot add route: %s/%d: %s",
271 address, prefix, strerror(errno));
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700272 } else if (*routes) {
273 LOGE("Invalid route: %s", routes);
274 count = BAD_ARGUMENT;
275 }
276
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700277 return count;
278}
279
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700280static int reset_interface(const char *name)
281{
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700282 ifreq ifr4;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700283 strncpy(ifr4.ifr_name, name, IFNAMSIZ);
Chia-chi Yehf4e3bf82011-06-30 12:33:17 -0700284 ifr4.ifr_flags = 0;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700285
286 if (ioctl(inet4, SIOCSIFFLAGS, &ifr4) && errno != ENODEV) {
287 LOGE("Cannot reset %s: %s", name, strerror(errno));
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700288 return SYSTEM_ERROR;
289 }
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700290 return 0;
291}
292
293static int check_interface(const char *name)
294{
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700295 ifreq ifr4;
296 strncpy(ifr4.ifr_name, name, IFNAMSIZ);
Chia-chi Yeh6ddd5742011-06-13 16:08:01 -0700297 ifr4.ifr_flags = 0;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700298
299 if (ioctl(inet4, SIOCGIFFLAGS, &ifr4) && errno != ENODEV) {
300 LOGE("Cannot check %s: %s", name, strerror(errno));
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700301 }
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700302 return ifr4.ifr_flags;
303}
304
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700305static int bind_to_interface(int socket, const char *name)
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700306{
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700307 if (setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, name, strlen(name))) {
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700308 LOGE("Cannot bind socket to %s: %s", name, strerror(errno));
309 return SYSTEM_ERROR;
310 }
311 return 0;
312}
313
314//------------------------------------------------------------------------------
315
316static void throwException(JNIEnv *env, int error, const char *message)
317{
318 if (error == SYSTEM_ERROR) {
319 jniThrowException(env, "java/lang/IllegalStateException", message);
320 } else {
321 jniThrowException(env, "java/lang/IllegalArgumentException", message);
322 }
323}
324
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700325static jint create(JNIEnv *env, jobject thiz, jint mtu)
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700326{
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700327 int tun = create_interface(mtu);
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700328 if (tun < 0) {
329 throwException(env, tun, "Cannot create interface");
Chia-chi Yeh36673692011-06-13 14:24:13 -0700330 return -1;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700331 }
Chia-chi Yeh36673692011-06-13 14:24:13 -0700332 return tun;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700333}
334
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700335static jstring getName(JNIEnv *env, jobject thiz, jint tun)
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700336{
337 char name[IFNAMSIZ];
Chia-chi Yehf4e3bf82011-06-30 12:33:17 -0700338 if (get_interface_name(name, tun) < 0) {
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700339 throwException(env, SYSTEM_ERROR, "Cannot get interface name");
340 return NULL;
341 }
342 return env->NewStringUTF(name);
343}
344
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700345static jint setAddresses(JNIEnv *env, jobject thiz, jstring jName,
346 jstring jAddresses)
347{
348 const char *name = NULL;
349 const char *addresses = NULL;
350 int count = -1;
351
352 name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
353 if (!name) {
354 jniThrowNullPointerException(env, "name");
355 goto error;
356 }
357 addresses = jAddresses ? env->GetStringUTFChars(jAddresses, NULL) : NULL;
358 if (!addresses) {
359 jniThrowNullPointerException(env, "addresses");
360 goto error;
361 }
362 count = set_addresses(name, addresses);
363 if (count < 0) {
364 throwException(env, count, "Cannot set address");
365 count = -1;
366 }
367
368error:
369 if (name) {
370 env->ReleaseStringUTFChars(jName, name);
371 }
372 if (addresses) {
373 env->ReleaseStringUTFChars(jAddresses, addresses);
374 }
375 return count;
376}
377
378static jint setRoutes(JNIEnv *env, jobject thiz, jstring jName,
379 jstring jRoutes)
380{
381 const char *name = NULL;
382 const char *routes = NULL;
383 int count = -1;
384
385 name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
386 if (!name) {
387 jniThrowNullPointerException(env, "name");
388 goto error;
389 }
390 routes = jRoutes ? env->GetStringUTFChars(jRoutes, NULL) : NULL;
391 if (!routes) {
392 jniThrowNullPointerException(env, "routes");
393 goto error;
394 }
395 count = set_routes(name, routes);
396 if (count < 0) {
397 throwException(env, count, "Cannot set route");
398 count = -1;
399 }
400
401error:
402 if (name) {
403 env->ReleaseStringUTFChars(jName, name);
404 }
405 if (routes) {
406 env->ReleaseStringUTFChars(jRoutes, routes);
407 }
408 return count;
409}
410
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700411static void reset(JNIEnv *env, jobject thiz, jstring jName)
Chia-chi Yehf4e3bf82011-06-30 12:33:17 -0700412{
413 const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700414 if (!name) {
415 jniThrowNullPointerException(env, "name");
416 return;
417 }
418 if (reset_interface(name) < 0) {
419 throwException(env, SYSTEM_ERROR, "Cannot reset interface");
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700420 }
421 env->ReleaseStringUTFChars(jName, name);
422}
423
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700424static jint check(JNIEnv *env, jobject thiz, jstring jName)
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700425{
Chia-chi Yehf4e3bf82011-06-30 12:33:17 -0700426 const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700427 if (!name) {
428 jniThrowNullPointerException(env, "name");
429 return 0;
430 }
431 int flags = check_interface(name);
432 env->ReleaseStringUTFChars(jName, name);
433 return flags;
434}
435
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700436static void protect(JNIEnv *env, jobject thiz, jint socket, jstring jName)
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700437{
Chia-chi Yehf4e3bf82011-06-30 12:33:17 -0700438 const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700439 if (!name) {
440 jniThrowNullPointerException(env, "name");
441 return;
442 }
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700443 if (bind_to_interface(socket, name) < 0) {
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700444 throwException(env, SYSTEM_ERROR, "Cannot protect socket");
445 }
446 env->ReleaseStringUTFChars(jName, name);
447}
448
449//------------------------------------------------------------------------------
450
451static JNINativeMethod gMethods[] = {
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700452 {"jniCreate", "(I)I", (void *)create},
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700453 {"jniGetName", "(I)Ljava/lang/String;", (void *)getName},
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700454 {"jniSetAddresses", "(Ljava/lang/String;Ljava/lang/String;)I", (void *)setAddresses},
455 {"jniSetRoutes", "(Ljava/lang/String;Ljava/lang/String;)I", (void *)setRoutes},
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700456 {"jniReset", "(Ljava/lang/String;)V", (void *)reset},
457 {"jniCheck", "(Ljava/lang/String;)I", (void *)check},
458 {"jniProtect", "(ILjava/lang/String;)V", (void *)protect},
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700459};
460
461int register_android_server_connectivity_Vpn(JNIEnv *env)
462{
Chia-chi Yehf4e3bf82011-06-30 12:33:17 -0700463 if (inet4 == -1) {
464 inet4 = socket(AF_INET, SOCK_DGRAM, 0);
465 }
466 if (inet6 == -1) {
467 inet6 = socket(AF_INET6, SOCK_DGRAM, 0);
468 }
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700469 return jniRegisterNativeMethods(env, "com/android/server/connectivity/Vpn",
470 gMethods, NELEM(gMethods));
471}
472
473};