com_android_server_connectivity_Vpn.cpp revision f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8b
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#include "netutils/ifc.h"
22
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"
41
42namespace android
43{
44
45static int inet4 = -1;
46static int inet6 = -1;
47
48static inline in_addr_t *as_in_addr(sockaddr *sa) {
49    return &((sockaddr_in *)sa)->sin_addr.s_addr;
50}
51
52//------------------------------------------------------------------------------
53
54#define SYSTEM_ERROR -1
55#define BAD_ARGUMENT -2
56
57static int create_interface(int mtu)
58{
59    int tun = open("/dev/tun", O_RDWR | O_NONBLOCK);
60
61    ifreq ifr4;
62    memset(&ifr4, 0, sizeof(ifr4));
63
64    // Allocate interface.
65    ifr4.ifr_flags = IFF_TUN | IFF_NO_PI;
66    if (ioctl(tun, TUNSETIFF, &ifr4)) {
67        ALOGE("Cannot allocate TUN: %s", strerror(errno));
68        goto error;
69    }
70
71    // Activate interface.
72    ifr4.ifr_flags = IFF_UP;
73    if (ioctl(inet4, SIOCSIFFLAGS, &ifr4)) {
74        ALOGE("Cannot activate %s: %s", ifr4.ifr_name, strerror(errno));
75        goto error;
76    }
77
78    // Set MTU if it is specified.
79    ifr4.ifr_mtu = mtu;
80    if (mtu > 0 && ioctl(inet4, SIOCSIFMTU, &ifr4)) {
81        ALOGE("Cannot set MTU on %s: %s", ifr4.ifr_name, strerror(errno));
82        goto error;
83    }
84
85    return tun;
86
87error:
88    close(tun);
89    return SYSTEM_ERROR;
90}
91
92static int get_interface_name(char *name, int tun)
93{
94    ifreq ifr4;
95    if (ioctl(tun, TUNGETIFF, &ifr4)) {
96        ALOGE("Cannot get interface name: %s", strerror(errno));
97        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)) {
108        ALOGE("Cannot get index of %s: %s", name, strerror(errno));
109        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;
122    memset(&ifr4, 0, sizeof(ifr4));
123    strncpy(ifr4.ifr_name, name, IFNAMSIZ);
124    ifr4.ifr_addr.sa_family = AF_INET;
125    ifr4.ifr_netmask.sa_family = AF_INET;
126
127    in6_ifreq ifr6;
128    memset(&ifr6, 0, sizeof(ifr6));
129    ifr6.ifr6_ifindex = index;
130
131    char address[65];
132    int prefix;
133    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;
169            *as_in_addr(&ifr4.ifr_netmask) = htonl(mask);
170            if (ioctl(inet4, SIOCSIFNETMASK, &ifr4)) {
171                count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
172                break;
173            }
174        }
175        ALOGD("Address added on %s: %s/%d", name, address, prefix);
176        ++count;
177    }
178
179    if (count == BAD_ARGUMENT) {
180        ALOGE("Invalid address: %s/%d", address, prefix);
181    } else if (count == SYSTEM_ERROR) {
182        ALOGE("Cannot add address: %s/%d: %s", address, prefix, strerror(errno));
183    } else if (*addresses) {
184        ALOGE("Invalid address: %s", addresses);
185        count = BAD_ARGUMENT;
186    }
187
188    return count;
189}
190
191static int reset_interface(const char *name)
192{
193    ifreq ifr4;
194    strncpy(ifr4.ifr_name, name, IFNAMSIZ);
195    ifr4.ifr_flags = 0;
196
197    if (ioctl(inet4, SIOCSIFFLAGS, &ifr4) && errno != ENODEV) {
198        ALOGE("Cannot reset %s: %s", name, strerror(errno));
199        return SYSTEM_ERROR;
200    }
201    return 0;
202}
203
204static int check_interface(const char *name)
205{
206    ifreq ifr4;
207    strncpy(ifr4.ifr_name, name, IFNAMSIZ);
208    ifr4.ifr_flags = 0;
209
210    if (ioctl(inet4, SIOCGIFFLAGS, &ifr4) && errno != ENODEV) {
211        ALOGE("Cannot check %s: %s", name, strerror(errno));
212    }
213    return ifr4.ifr_flags;
214}
215
216static 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) {
229            if (error = ifc_add_address(name, address, jPrefixLength)) {
230                ALOGE("Cannot add address %s/%d on interface %s (%s)", address, jPrefixLength, name,
231                      strerror(-error));
232            }
233        } else {
234            if (error = ifc_del_address(name, address, jPrefixLength)) {
235                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
250//------------------------------------------------------------------------------
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
261static jint create(JNIEnv *env, jobject thiz, jint mtu)
262{
263    int tun = create_interface(mtu);
264    if (tun < 0) {
265        throwException(env, tun, "Cannot create interface");
266        return -1;
267    }
268    return tun;
269}
270
271static jstring getName(JNIEnv *env, jobject thiz, jint tun)
272{
273    char name[IFNAMSIZ];
274    if (get_interface_name(name, tun) < 0) {
275        throwException(env, SYSTEM_ERROR, "Cannot get interface name");
276        return NULL;
277    }
278    return env->NewStringUTF(name);
279}
280
281static jint setAddresses(JNIEnv *env, jobject thiz, jstring jName,
282        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
314static void reset(JNIEnv *env, jobject thiz, jstring jName)
315{
316    const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
317    if (!name) {
318        jniThrowNullPointerException(env, "name");
319        return;
320    }
321    if (reset_interface(name) < 0) {
322        throwException(env, SYSTEM_ERROR, "Cannot reset interface");
323    }
324    env->ReleaseStringUTFChars(jName, name);
325}
326
327static jint check(JNIEnv *env, jobject thiz, jstring jName)
328{
329    const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
330    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
339static 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
351//------------------------------------------------------------------------------
352
353static JNINativeMethod gMethods[] = {
354    {"jniCreate", "(I)I", (void *)create},
355    {"jniGetName", "(I)Ljava/lang/String;", (void *)getName},
356    {"jniSetAddresses", "(Ljava/lang/String;Ljava/lang/String;)I", (void *)setAddresses},
357    {"jniReset", "(Ljava/lang/String;)V", (void *)reset},
358    {"jniCheck", "(Ljava/lang/String;)I", (void *)check},
359    {"jniAddAddress", "(Ljava/lang/String;Ljava/lang/String;I)Z", (void *)addAddress},
360    {"jniDelAddress", "(Ljava/lang/String;Ljava/lang/String;I)Z", (void *)delAddress},
361};
362
363int register_android_server_connectivity_Vpn(JNIEnv *env)
364{
365    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    }
371    return jniRegisterNativeMethods(env, "com/android/server/connectivity/Vpn",
372            gMethods, NELEM(gMethods));
373}
374
375};
376