com_android_server_connectivity_Vpn.cpp revision 6bc2c2c34f2b23eae79ad733c97a691734055c4f
1/*
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>
21
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"
40
41namespace android
42{
43
44static int inet4 = -1;
45static int inet6 = -1;
46
47static inline in_addr_t *as_in_addr(sockaddr *sa) {
48    return &((sockaddr_in *)sa)->sin_addr.s_addr;
49}
50
51//------------------------------------------------------------------------------
52
53#define SYSTEM_ERROR -1
54#define BAD_ARGUMENT -2
55
56static int create_interface(int mtu)
57{
58    int tun = open("/dev/tun", O_RDWR | O_NONBLOCK);
59
60    ifreq ifr4;
61    memset(&ifr4, 0, sizeof(ifr4));
62
63    // Allocate interface.
64    ifr4.ifr_flags = IFF_TUN | IFF_NO_PI;
65    if (ioctl(tun, TUNSETIFF, &ifr4)) {
66        ALOGE("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        ALOGE("Cannot activate %s: %s", ifr4.ifr_name, strerror(errno));
74        goto error;
75    }
76
77    // Set MTU if it is specified.
78    ifr4.ifr_mtu = mtu;
79    if (mtu > 0 && ioctl(inet4, SIOCSIFMTU, &ifr4)) {
80        ALOGE("Cannot set MTU on %s: %s", ifr4.ifr_name, strerror(errno));
81        goto error;
82    }
83
84    return tun;
85
86error:
87    close(tun);
88    return SYSTEM_ERROR;
89}
90
91static int get_interface_name(char *name, int tun)
92{
93    ifreq ifr4;
94    if (ioctl(tun, TUNGETIFF, &ifr4)) {
95        ALOGE("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        ALOGE("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;
121    memset(&ifr4, 0, sizeof(ifr4));
122    strncpy(ifr4.ifr_name, name, IFNAMSIZ);
123    ifr4.ifr_addr.sa_family = AF_INET;
124    ifr4.ifr_netmask.sa_family = AF_INET;
125
126    in6_ifreq ifr6;
127    memset(&ifr6, 0, sizeof(ifr6));
128    ifr6.ifr6_ifindex = index;
129
130    char address[65];
131    int prefix;
132    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;
168            *as_in_addr(&ifr4.ifr_netmask) = htonl(mask);
169            if (ioctl(inet4, SIOCSIFNETMASK, &ifr4)) {
170                count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
171                break;
172            }
173        }
174        ALOGD("Address added on %s: %s/%d", name, address, prefix);
175        ++count;
176    }
177
178    if (count == BAD_ARGUMENT) {
179        ALOGE("Invalid address: %s/%d", address, prefix);
180    } else if (count == SYSTEM_ERROR) {
181        ALOGE("Cannot add address: %s/%d: %s", address, prefix, strerror(errno));
182    } else if (*addresses) {
183        ALOGE("Invalid address: %s", addresses);
184        count = BAD_ARGUMENT;
185    }
186
187    return count;
188}
189
190static int reset_interface(const char *name)
191{
192    ifreq ifr4;
193    strncpy(ifr4.ifr_name, name, IFNAMSIZ);
194    ifr4.ifr_flags = 0;
195
196    if (ioctl(inet4, SIOCSIFFLAGS, &ifr4) && errno != ENODEV) {
197        ALOGE("Cannot reset %s: %s", name, strerror(errno));
198        return SYSTEM_ERROR;
199    }
200    return 0;
201}
202
203static int check_interface(const char *name)
204{
205    ifreq ifr4;
206    strncpy(ifr4.ifr_name, name, IFNAMSIZ);
207    ifr4.ifr_flags = 0;
208
209    if (ioctl(inet4, SIOCGIFFLAGS, &ifr4) && errno != ENODEV) {
210        ALOGE("Cannot check %s: %s", name, strerror(errno));
211    }
212    return ifr4.ifr_flags;
213}
214
215//------------------------------------------------------------------------------
216
217static void throwException(JNIEnv *env, int error, const char *message)
218{
219    if (error == SYSTEM_ERROR) {
220        jniThrowException(env, "java/lang/IllegalStateException", message);
221    } else {
222        jniThrowException(env, "java/lang/IllegalArgumentException", message);
223    }
224}
225
226static jint create(JNIEnv *env, jobject thiz, jint mtu)
227{
228    int tun = create_interface(mtu);
229    if (tun < 0) {
230        throwException(env, tun, "Cannot create interface");
231        return -1;
232    }
233    return tun;
234}
235
236static jstring getName(JNIEnv *env, jobject thiz, jint tun)
237{
238    char name[IFNAMSIZ];
239    if (get_interface_name(name, tun) < 0) {
240        throwException(env, SYSTEM_ERROR, "Cannot get interface name");
241        return NULL;
242    }
243    return env->NewStringUTF(name);
244}
245
246static jint setAddresses(JNIEnv *env, jobject thiz, jstring jName,
247        jstring jAddresses)
248{
249    const char *name = NULL;
250    const char *addresses = NULL;
251    int count = -1;
252
253    name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
254    if (!name) {
255        jniThrowNullPointerException(env, "name");
256        goto error;
257    }
258    addresses = jAddresses ? env->GetStringUTFChars(jAddresses, NULL) : NULL;
259    if (!addresses) {
260        jniThrowNullPointerException(env, "addresses");
261        goto error;
262    }
263    count = set_addresses(name, addresses);
264    if (count < 0) {
265        throwException(env, count, "Cannot set address");
266        count = -1;
267    }
268
269error:
270    if (name) {
271        env->ReleaseStringUTFChars(jName, name);
272    }
273    if (addresses) {
274        env->ReleaseStringUTFChars(jAddresses, addresses);
275    }
276    return count;
277}
278
279static void reset(JNIEnv *env, jobject thiz, jstring jName)
280{
281    const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
282    if (!name) {
283        jniThrowNullPointerException(env, "name");
284        return;
285    }
286    if (reset_interface(name) < 0) {
287        throwException(env, SYSTEM_ERROR, "Cannot reset interface");
288    }
289    env->ReleaseStringUTFChars(jName, name);
290}
291
292static jint check(JNIEnv *env, jobject thiz, jstring jName)
293{
294    const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
295    if (!name) {
296        jniThrowNullPointerException(env, "name");
297        return 0;
298    }
299    int flags = check_interface(name);
300    env->ReleaseStringUTFChars(jName, name);
301    return flags;
302}
303
304//------------------------------------------------------------------------------
305
306static JNINativeMethod gMethods[] = {
307    {"jniCreate", "(I)I", (void *)create},
308    {"jniGetName", "(I)Ljava/lang/String;", (void *)getName},
309    {"jniSetAddresses", "(Ljava/lang/String;Ljava/lang/String;)I", (void *)setAddresses},
310    {"jniReset", "(Ljava/lang/String;)V", (void *)reset},
311    {"jniCheck", "(Ljava/lang/String;)I", (void *)check},
312};
313
314int register_android_server_connectivity_Vpn(JNIEnv *env)
315{
316    if (inet4 == -1) {
317        inet4 = socket(AF_INET, SOCK_DGRAM, 0);
318    }
319    if (inet6 == -1) {
320        inet6 = socket(AF_INET6, SOCK_DGRAM, 0);
321    }
322    return jniRegisterNativeMethods(env, "com/android/server/connectivity/Vpn",
323            gMethods, NELEM(gMethods));
324}
325
326};
327