The service part of the user space VPN support.

The dialogs will be in another change.

Change-Id: I0cdfd2ef21ffd40ee955b3cbde5ada65dbfdb0bc
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index a1c3283..f33920d 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -16,6 +16,7 @@
     com_android_server_UsbHostManager.cpp \
     com_android_server_VibratorService.cpp \
     com_android_server_location_GpsLocationProvider.cpp \
+    com_android_server_connectivity_Vpn.cpp \
     onload.cpp
 
 LOCAL_C_INCLUDES += \
diff --git a/services/jni/com_android_server_connectivity_Vpn.cpp b/services/jni/com_android_server_connectivity_Vpn.cpp
new file mode 100644
index 0000000..374fd3b
--- /dev/null
+++ b/services/jni/com_android_server_connectivity_Vpn.cpp
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_NDEBUG 0
+
+#define LOG_TAG "VpnJni"
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <linux/route.h>
+#include <linux/ipv6_route.h>
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_util_Binder.h"
+
+namespace android
+{
+
+static inline void init_sockaddr(sockaddr *sa) {
+    ((sockaddr_in *)sa)->sin_family = AF_INET;
+    ((sockaddr_in *)sa)->sin_port = 0;
+}
+
+static inline in_addr_t *as_in_addr(sockaddr *sa) {
+    return &((sockaddr_in *)sa)->sin_addr.s_addr;
+}
+
+static inline in_addr_t *as_in_addr(sockaddr_storage *ss) {
+    return &((sockaddr_in *)ss)->sin_addr.s_addr;
+}
+
+static inline in6_addr *as_in6_addr(sockaddr_storage *ss) {
+    return &((sockaddr_in6 *)&ss)->sin6_addr;
+}
+
+//------------------------------------------------------------------------------
+
+#define SYSTEM_ERROR -1
+#define BAD_ARGUMENT -2
+
+static int create_interface(char *name, int *index)
+{
+    int tun = open("/dev/tun", O_RDWR);
+    int inet4 = socket(AF_INET, SOCK_DGRAM, 0);
+
+    ifreq ifr4;
+    memset(&ifr4, 0, sizeof(ifr4));
+
+    // Allocate interface.
+    ifr4.ifr_flags = IFF_TUN;
+    if (ioctl(tun, TUNSETIFF, &ifr4)) {
+        LOGE("Cannot allocate TUN: %s", strerror(errno));
+        goto error;
+    }
+
+    // Activate interface.
+    ifr4.ifr_flags = IFF_UP;
+    if (ioctl(inet4, SIOCSIFFLAGS, &ifr4)) {
+        LOGE("Cannot activate %s: %s", ifr4.ifr_name, strerror(errno));
+        goto error;
+    }
+
+    // Get interface index.
+    if (ioctl(inet4, SIOGIFINDEX, &ifr4)) {
+        LOGE("Cannot get index of %s: %s", ifr4.ifr_name, strerror(errno));
+        goto error;
+    }
+
+    strcpy(name, ifr4.ifr_name);
+    *index = ifr4.ifr_ifindex;
+    close(inet4);
+    return tun;
+
+error:
+    close(tun);
+    close(inet4);
+    return SYSTEM_ERROR;
+}
+
+static int set_addresses(const char *name, int index, const char *addresses)
+{
+    int inet4 = socket(AF_INET, SOCK_DGRAM, 0);
+    int inet6 = socket(AF_INET6, SOCK_DGRAM, 0);
+
+    ifreq ifr4;
+    memset(&ifr4, 0, sizeof(ifr4));
+    strcpy(ifr4.ifr_name, name);
+    init_sockaddr(&ifr4.ifr_addr);
+
+    in6_ifreq ifr6;
+    memset(&ifr6, 0, sizeof(ifr6));
+    ifr6.ifr6_ifindex = index;
+
+    char address[65];
+    int prefix;
+
+    int chars;
+    int count = 0;
+
+    while (sscanf(addresses, " %64[^/]/%d %n", address, &prefix, &chars) == 2) {
+        addresses += chars;
+
+        if (strchr(address, ':')) {
+            // Add an IPv6 address.
+            if (inet_pton(AF_INET6, address, &ifr6.ifr6_addr) != 1 ||
+                    prefix < 0 || prefix > 128) {
+                count = BAD_ARGUMENT;
+                break;
+            }
+
+            ifr6.ifr6_prefixlen = prefix;
+            if (ioctl(inet6, SIOCSIFADDR, &ifr6)) {
+                count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
+                break;
+            }
+        } else {
+            // Add an IPv4 address.
+            if (inet_pton(AF_INET, address, as_in_addr(&ifr4.ifr_addr)) != 1 ||
+                    prefix < 0 || prefix > 32) {
+                count = BAD_ARGUMENT;
+                break;
+            }
+
+            if (count) {
+                sprintf(ifr4.ifr_name, "%s:%d", name, count);
+            }
+            if (ioctl(inet4, SIOCSIFADDR, &ifr4)) {
+                count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
+                break;
+            }
+
+            in_addr_t mask = prefix ? (~0 << (32 - prefix)) : 0;
+            *as_in_addr(&ifr4.ifr_addr) = htonl(mask);
+            if (ioctl(inet4, SIOCSIFNETMASK, &ifr4)) {
+                count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
+                break;
+            }
+        }
+        LOGV("Address added on %s: %s/%d", name, address, prefix);
+        ++count;
+    }
+
+    if (count == BAD_ARGUMENT) {
+        LOGE("Invalid address: %s/%d", address, prefix);
+    } else if (count == SYSTEM_ERROR) {
+        LOGE("Cannot add address: %s/%d: %s", address, prefix, strerror(errno));
+    } else if (*addresses) {
+        LOGE("Invalid address: %s", addresses);
+        count = BAD_ARGUMENT;
+    }
+
+    close(inet4);
+    close(inet6);
+    return count;
+}
+
+static int set_routes(const char *name, int index, const char *routes)
+{
+    int inet4 = socket(AF_INET, SOCK_DGRAM, 0);
+    int inet6 = socket(AF_INET6, SOCK_DGRAM, 0);
+
+    rtentry rt4;
+    memset(&rt4, 0, sizeof(rt4));
+    rt4.rt_dev = (char *)name;
+    rt4.rt_flags = RTF_UP;
+    init_sockaddr(&rt4.rt_dst);
+    init_sockaddr(&rt4.rt_genmask);
+    init_sockaddr(&rt4.rt_gateway);
+
+    in6_rtmsg rt6;
+    memset(&rt6, 0, sizeof(rt6));
+    rt6.rtmsg_ifindex = index;
+    rt6.rtmsg_flags = RTF_UP;
+
+    char address[65];
+    int prefix;
+    char gateway[65];
+
+    int chars;
+    int count = 0;
+
+    while (sscanf(routes, " %64[^/]/%d>%64[^ ] %n",
+            address, &prefix, gateway, &chars) == 3) {
+        routes += chars;
+
+        if (strchr(address, ':')) {
+            // Add an IPv6 route.
+            if (inet_pton(AF_INET6, gateway, &rt6.rtmsg_gateway) != 1 ||
+                    inet_pton(AF_INET6, address, &rt6.rtmsg_dst) != 1 ||
+                    prefix < 0 || prefix > 128) {
+                count = BAD_ARGUMENT;
+                break;
+            }
+
+            rt6.rtmsg_dst_len = prefix;
+            if (memcmp(&rt6.rtmsg_gateway, &in6addr_any, sizeof(in6addr_any))) {
+                rt6.rtmsg_flags |= RTF_GATEWAY;
+            }
+            if (ioctl(inet6, SIOCADDRT, &rt6)) {
+                count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
+                break;
+            }
+        } else {
+            // Add an IPv4 route.
+            if (inet_pton(AF_INET, gateway, as_in_addr(&rt4.rt_gateway)) != 1 ||
+                    inet_pton(AF_INET, address, as_in_addr(&rt4.rt_dst)) != 1 ||
+                    prefix < 0 || prefix > 32) {
+                count = BAD_ARGUMENT;
+                break;
+            }
+
+            in_addr_t mask = prefix ? (~0 << (32 - prefix)) : 0;
+            *as_in_addr(&rt4.rt_genmask) = htonl(mask);
+            if (*as_in_addr(&rt4.rt_gateway)) {
+                rt4.rt_flags |= RTF_GATEWAY;
+            }
+            if (ioctl(inet4, SIOCADDRT, &rt4)) {
+                count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
+                break;
+            }
+        }
+        LOGV("Route added on %s: %s/%d -> %s", name, address, prefix, gateway);
+        ++count;
+    }
+
+    if (count == BAD_ARGUMENT) {
+        LOGE("Invalid route: %s/%d -> %s", address, prefix, gateway);
+    } else if (count == SYSTEM_ERROR) {
+        LOGE("Cannot add route: %s/%d -> %s: %s",
+                address, prefix, gateway, strerror(errno));
+    } else if (*routes) {
+        LOGE("Invalid route: %s", routes);
+        count = BAD_ARGUMENT;
+    }
+
+    close(inet4);
+    close(inet6);
+    return count;
+}
+
+static int get_interface_name(char *name, int tun)
+{
+    ifreq ifr4;
+    if (ioctl(tun, TUNGETIFF, &ifr4)) {
+        LOGE("Cannot get interface name: %s", strerror(errno));
+        return SYSTEM_ERROR;
+    }
+    strcpy(name, ifr4.ifr_name);
+    return 0;
+}
+
+static int reset_interface(const char *name)
+{
+    int inet4 = socket(AF_INET, SOCK_DGRAM, 0);
+
+    ifreq ifr4;
+    ifr4.ifr_flags = 0;
+    strncpy(ifr4.ifr_name, name, IFNAMSIZ);
+
+    if (ioctl(inet4, SIOCSIFFLAGS, &ifr4) && errno != ENODEV) {
+        LOGE("Cannot reset %s: %s", name, strerror(errno));
+        close(inet4);
+        return SYSTEM_ERROR;
+    }
+    close(inet4);
+    return 0;
+}
+
+static int check_interface(const char *name)
+{
+    int inet4 = socket(AF_INET, SOCK_DGRAM, 0);
+
+    ifreq ifr4;
+    strncpy(ifr4.ifr_name, name, IFNAMSIZ);
+
+    if (ioctl(inet4, SIOCGIFFLAGS, &ifr4) && errno != ENODEV) {
+        LOGE("Cannot check %s: %s", name, strerror(errno));
+        ifr4.ifr_flags = 0;
+    }
+    close(inet4);
+    return ifr4.ifr_flags;
+}
+
+static int bind_to_interface(int fd, const char *name)
+{
+    if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, name, strlen(name))) {
+        LOGE("Cannot bind socket to %s: %s", name, strerror(errno));
+        return SYSTEM_ERROR;
+    }
+    return 0;
+}
+
+//------------------------------------------------------------------------------
+
+static void throwException(JNIEnv *env, int error, const char *message)
+{
+    if (error == SYSTEM_ERROR) {
+        jniThrowException(env, "java/lang/IllegalStateException", message);
+    } else {
+        jniThrowException(env, "java/lang/IllegalArgumentException", message);
+    }
+}
+
+static jobject configure(JNIEnv *env, jobject thiz,
+        jstring jAddresses, jstring jRoutes)
+{
+    char name[IFNAMSIZ];
+    int index;
+    int tun = create_interface(name, &index);
+    if (tun < 0) {
+        throwException(env, tun, "Cannot create interface");
+        return NULL;
+    }
+    LOGD("%s is created", name);
+
+    const char *addresses;
+    const char *routes;
+    int count;
+
+    // Addresses are required.
+    addresses = jAddresses ? env->GetStringUTFChars(jAddresses, NULL) : NULL;
+    if (!addresses) {
+        jniThrowNullPointerException(env, "address");
+        goto error;
+    }
+    count = set_addresses(name, index, addresses);
+    env->ReleaseStringUTFChars(jAddresses, addresses);
+    if (count <= 0) {
+        throwException(env, count, "Cannot set address");
+        goto error;
+    }
+    LOGD("Configured %d address(es) on %s", count, name);
+
+    // Routes are optional.
+    routes = jRoutes ? env->GetStringUTFChars(jRoutes, NULL) : NULL;
+    if (routes) {
+        count = set_routes(name, index, routes);
+        env->ReleaseStringUTFChars(jRoutes, routes);
+        if (count < 0) {
+            throwException(env, count, "Cannot set route");
+            goto error;
+        }
+        LOGD("Configured %d route(s) on %s", count, name);
+    }
+
+    return newParcelFileDescriptor(env, jniCreateFileDescriptor(env, tun));
+
+error:
+    close(tun);
+    LOGD("%s is destroyed", name);
+    return NULL;
+}
+
+static jstring getName(JNIEnv *env, jobject thiz, jint fd)
+{
+    char name[IFNAMSIZ];
+    if (get_interface_name(name, fd) < 0) {
+        throwException(env, SYSTEM_ERROR, "Cannot get interface name");
+        return NULL;
+    }
+    return env->NewStringUTF(name);
+}
+
+static void reset(JNIEnv *env, jobject thiz, jstring jName)
+{
+    const char *name = jName ?
+            env->GetStringUTFChars(jName, NULL) : NULL;
+    if (!name) {
+        jniThrowNullPointerException(env, "name");
+        return;
+    }
+    if (reset_interface(name) < 0) {
+        throwException(env, SYSTEM_ERROR, "Cannot reset interface");
+    } else {
+        LOGD("%s is deactivated", name);
+    }
+    env->ReleaseStringUTFChars(jName, name);
+}
+
+static jint check(JNIEnv *env, jobject thiz, jstring jName)
+{
+    const char *name = jName ?
+            env->GetStringUTFChars(jName, NULL) : NULL;
+    if (!name) {
+        jniThrowNullPointerException(env, "name");
+        return 0;
+    }
+    int flags = check_interface(name);
+    env->ReleaseStringUTFChars(jName, name);
+    return flags;
+}
+
+static void protect(JNIEnv *env, jobject thiz, jint fd, jstring jName)
+{
+    const char *name = jName ?
+            env->GetStringUTFChars(jName, NULL) : NULL;
+    if (!name) {
+        jniThrowNullPointerException(env, "name");
+        return;
+    }
+    if (bind_to_interface(fd, name) < 0) {
+        throwException(env, SYSTEM_ERROR, "Cannot protect socket");
+    }
+    env->ReleaseStringUTFChars(jName, name);
+}
+
+//------------------------------------------------------------------------------
+
+static JNINativeMethod gMethods[] = {
+    {"nativeConfigure", "(Ljava/lang/String;Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;", (void *)configure},
+    {"nativeGetName", "(I)Ljava/lang/String;", (void *)getName},
+    {"nativeReset", "(Ljava/lang/String;)V", (void *)reset},
+    {"nativeCheck", "(Ljava/lang/String;)I", (void *)check},
+    {"nativeProtect", "(ILjava/lang/String;)V", (void *)protect},
+};
+
+int register_android_server_connectivity_Vpn(JNIEnv *env)
+{
+    return jniRegisterNativeMethods(env, "com/android/server/connectivity/Vpn",
+            gMethods, NELEM(gMethods));
+}
+
+};
diff --git a/services/jni/onload.cpp b/services/jni/onload.cpp
index 469e818..9dff48b 100644
--- a/services/jni/onload.cpp
+++ b/services/jni/onload.cpp
@@ -34,6 +34,7 @@
 int register_android_server_VibratorService(JNIEnv* env);
 int register_android_server_SystemServer(JNIEnv* env);
 int register_android_server_location_GpsLocationProvider(JNIEnv* env);
+int register_android_server_connectivity_Vpn(JNIEnv* env);
 };
 
 using namespace android;
@@ -63,6 +64,7 @@
     register_android_server_VibratorService(env);
     register_android_server_SystemServer(env);
     register_android_server_location_GpsLocationProvider(env);
+    register_android_server_connectivity_Vpn(env);
 
     return JNI_VERSION_1_4;
 }