Add the vpn service 42/74742/4
authorhyunuktak <hyunuk.tak@samsung.com>
Wed, 15 Jun 2016 10:25:01 +0000 (19:25 +0900)
committerhyunuktak <hyunuk.tak@samsung.com>
Thu, 16 Jun 2016 07:01:37 +0000 (16:01 +0900)
Change-Id: Ie2bae21f5ee325fb2eea1ab040f50dbc56d227cd
Signed-off-by: hyunuktak <hyunuk.tak@samsung.com>
13 files changed:
CMakeLists.txt
include/netdbus.h
include/vpnsvc-internal.h [new file with mode: 0755]
include/vpnsvc.h [new file with mode: 0755]
interfaces/netconfig-iface-network-state.xml [changed mode: 0644->0755]
interfaces/netconfig-iface-vpnsvc.xml [new file with mode: 0755]
packaging/net-config.spec
resources/etc/dbus-1/system.d/net-config.conf
src/dbus/netdbus.c
src/main.c
src/network-state.c
src/vpnsvc-internal.c [new file with mode: 0755]
src/vpnsvc.c [new file with mode: 0755]

index 237f2b5..256ddf8 100755 (executable)
@@ -21,6 +21,8 @@ SET(SRCS
        src/wifi-passpoint.c
        src/utils/log.c
        src/utils/util.c
+       src/vpnsvc.c
+       src/vpnsvc-internal.c
        src/dbus/netdbus.c
        src/network-clock.c
        src/network-state.c
@@ -142,7 +144,7 @@ ADD_CUSTOM_COMMAND(
                        --generate-c-code generated-code
                        --c-generate-object-manager
                        --generate-docbook generated-code-docs
-                       ${INTERFACES}/netconfig-iface-network-state.xml ${INTERFACES}/netconfig-iface-network-statistics.xml ${INTERFACES}/netconfig-iface-wifi.xml
+                       ${INTERFACES}/netconfig-iface-network-state.xml ${INTERFACES}/netconfig-iface-network-statistics.xml ${INTERFACES}/netconfig-iface-wifi.xml ${INTERFACES}/netconfig-iface-vpnsvc.xml
                        COMMENT "Generating GDBus .c/.h")
 
 ADD_EXECUTABLE(${PROJECT_NAME} ${SRCS} ${CMAKE_SOURCE_DIR}/generated-code.c)
index fb55c8f..fe51502 100755 (executable)
@@ -61,6 +61,8 @@ extern "C" {
 #define NETCONFIG_NETWORK_STATISTICS_PATH      "/net/netconfig/network_statistics"
 #define NETCONFIG_NETWORK_PATH                 "/net/netconfig/network"
 #define NETCONFIG_NETWORK_INTERFACE            "net.netconfig.network"
+#define NETCONFIG_VPNSVC_PATH          "/net/netconfig/vpnsvc"
+#define NETCONFIG_VPNSVC_INTERFACE     "net.netconfig.vpnsvc"
 
 #define DBUS_PATH_MAX_BUFLEN           512
 #define DBUS_STATE_MAX_BUFLEN          64
@@ -75,6 +77,7 @@ typedef void (*got_name_cb)(void);
 GDBusObjectManagerServer       *netdbus_get_wifi_manager(void);
 GDBusObjectManagerServer       *netdbus_get_state_manager(void);
 GDBusObjectManagerServer       *netdbus_get_statistics_manager(void);
+GDBusObjectManagerServer       *netdbus_get_vpn_manager(void);
 
 GDBusConnection                                *netdbus_get_connection(void);
 GCancellable                           *netdbus_get_cancellable(void);
diff --git a/include/vpnsvc-internal.h b/include/vpnsvc-internal.h
new file mode 100755 (executable)
index 0000000..ce68b92
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Network Configuration - VPN Service Internal Module
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+
+#ifndef __NETCONFIG_VPN_SERVICE_INTERNAL_H__
+#define __NETCONFIG_VPN_SERVICE_INTERNAL_H__
+
+#include <vpn_service.h>
+
+typedef struct _vpnsvc_tun_s {
+       GDBusConnection *connection;            /**< D-Bus Connection */
+       int fd;                                 /**< tun socket fd */
+       int index;                              /**< tun index (if.iface_index) */
+       char name[VPNSVC_VPN_IFACE_NAME_LEN];      /**< tun name (if.iface_name) */
+       char session[VPNSVC_SESSION_STRING_LEN];/**< session name (user setting) */
+       unsigned int mtu;                       /**< mtu (user setting) */
+} vpnsvc_tun_s;
+
+int vpn_service_init(const char* iface_name, size_t iface_name_len, int fd, vpnsvc_tun_s *handle_s);
+int vpn_service_deinit(const char* dev_name);
+int vpn_service_protect(int socket, const char* dev_name);
+int vpn_service_up(int iface_index, const char* local_ip, const char* remote_ip,
+                                               char* routes[], int prefix[], size_t nr_routes,
+                                               char** dns_servers, size_t nr_dns, size_t total_dns_string_cnt,
+                                               const char* dns_suffix, const unsigned int mtu);
+int vpn_service_down(int iface_index);
+int vpn_service_block_networks(char* nets_vpn[], int prefix_vpn[], size_t nr_nets_vpn,
+               char* nets_orig[], int prefix_orig[], size_t nr_nets_orig);
+int vpn_service_unblock_networks(void);
+
+#endif /* __NETCONFIG_VPN_SERVICE_INTERNAL_H__ */
+
diff --git a/include/vpnsvc.h b/include/vpnsvc.h
new file mode 100755 (executable)
index 0000000..9225af4
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Network Configuration - VPN Service Module
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __NETCONFIG_VPN_SERVICE_H__
+#define __NETCONFIG_VPN_SERVICE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <glib-object.h>
+
+#include "generated-code.h"
+
+typedef enum _net_vpn_service_privilege_e {
+       PRIVILEGE_VPN_SERVICE = 0x00,
+       PRIVILEGE_VPN_SERVICE_ADMIN,
+       PRIVILEGE_INTERNET,
+} net_vpn_service_privilege_e;
+
+void vpnsvc_create_and_init(void);
+void vpnsvc_destroy_deinit(void);
+Vpnsvc *get_vpnsvc_object(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __NETCONFIG_VPN_SERVICE_H__ */
+
old mode 100644 (file)
new mode 100755 (executable)
index 698f35b..baa583a
@@ -22,6 +22,7 @@
                </method>
                <method name="CheckGetPrivilege"></method>
                <method name="CheckProfilePrivilege"></method>
+               <method name="CheckInternetPrivilege"></method>
                <method name="LaunchMdns"></method>
                <method name="RefMdns"></method>
                <method name="UnrefMdns"></method>
diff --git a/interfaces/netconfig-iface-vpnsvc.xml b/interfaces/netconfig-iface-vpnsvc.xml
new file mode 100755 (executable)
index 0000000..ee76ba7
--- /dev/null
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<node name="/net/netconfig/vpnsvc">
+       <interface name="net.netconfig.vpnsvc">
+               <method name="vpn_init">
+                       <arg type="s" name="iface_name" direction="in"/>
+                       <arg type="u" name="iface_name_len" direction="in"/>
+                       <arg type="i" name="result" direction="out"/>
+                       <arg type="i" name="h_index" direction="out"/>
+                       <arg type="s" name="h_name" direction="out"/>
+               </method>
+               <method name="vpn_deinit">
+                       <arg type="s" name="dev_name" direction="in"/>
+                       <arg type="i" name="result" direction="out"/>
+               </method>
+               <method name="vpn_protect">
+                       <arg type="s" name="dev_name" direction="in"/>
+                       <arg type="i" name="result" direction="out"/>
+               </method>
+               <method name="vpn_up">
+                       <arg type="i" name="iface_index" direction="in"/>
+                       <arg type="s" name="local_ip" direction="in"/>
+                       <arg type="s" name="remote_ip" direction="in"/>
+                       <arg type="v" name="routes" direction="in"/>
+                       <arg type="u" name="nr_routes" direction="in"/>
+                       <arg type="v" name="dns_servers" direction="in"/>
+                       <arg type="u" name="nr_dns" direction="in"/>
+                       <arg type="s" name="dns_suffix" direction="in"/>
+                       <arg type="u" name="mtu" direction="in"/>
+                       <arg type="i" name="result" direction="out"/>
+               </method>
+               <method name="vpn_down">
+                       <arg type="i" name="iface_index" direction="in"/>
+                       <arg type="i" name="result" direction="out"/>
+               </method>
+               <method name="vpn_block_networks">
+                       <arg type="v" name="nets_vpn" direction="in"/>
+                       <arg type="u" name="nr_nets_vpn" direction="in"/>
+                       <arg type="v" name="nets_orig" direction="in"/>
+                       <arg type="u" name="nr_nets_orig" direction="in"/>
+                       <arg type="i" name="result" direction="out"/>
+               </method>
+               <method name="vpn_unblock_networks">
+                       <arg type="i" name="result" direction="out"/>
+               </method>
+       </interface>
+</node>
+
index 2b7b7e2..d7c1f4b 100755 (executable)
@@ -1,6 +1,6 @@
 Name:          net-config
 Summary:       TIZEN Network Configuration service
-Version:       1.1.75
+Version:       1.1.76
 Release:       2
 Group:         System/Network
 License:       Apache-2.0
@@ -18,6 +18,7 @@ BuildRequires:        pkgconfig(syspopup-caller)
 BuildRequires: pkgconfig(capi-system-info)
 BuildRequires: pkgconfig(capi-appfw-application)
 BuildRequires: pkgconfig(capi-network-wifi-direct)
+BuildRequires: pkgconfig(capi-vpnsvc)
 BuildRequires: cmake
 BuildRequires: pkgconfig(sqlite3)
 BuildRequires: pkgconfig(libtzplatform-config)
index ad238e8..35030f6 100644 (file)
@@ -11,6 +11,7 @@
                <check send_destination="net.netconfig" send_interface="net.netconfig.network" send_member="EthernetCableState" privilege="http://tizen.org/privilege/network.get" />
                <check send_destination="net.netconfig" send_interface="net.netconfig.network" send_member="CheckGetPrivilege" privilege="http://tizen.org/privilege/network.get" />
                <check send_destination="net.netconfig" send_interface="net.netconfig.network" send_member="CheckProfilePrivilege" privilege="http://tizen.org/privilege/network.profile" />
+               <check send_destination="net.netconfig" send_interface="net.netconfig.network" send_member="CheckInternetPrivilege" privilege="http://tizen.org/privilege/internet" />
                <allow send_destination="net.netconfig" send_interface="net.netconfig.network" send_member="LaunchMdns"/>
                <allow send_destination="net.netconfig" send_interface="net.netconfig.network" send_member="RefMdns"/>
                <allow send_destination="net.netconfig" send_interface="net.netconfig.network" send_member="UnrefMdns"/>
                <check send_destination="net.netconfig" send_interface="net.netconfig.wifi" send_member="TdlsConnectedPeer" privilege="http://tizen.org/privilege/network.get" />
 
                <check send_destination="net.netconfig" send_interface="net.connman.Agent" send_member="SetField" privilege="http://tizen.org/privilege/network.profile" />
+
+               <check send_destination="net.netconfig" send_interface="net.netconfig.vpnsvc" send_member="vpn_init" privilege="http://tizen.org/privilege/vpnservice" />
+               <check send_destination="net.netconfig" send_interface="net.netconfig.vpnsvc" send_member="vpn_deinit" privilege="http://tizen.org/privilege/vpnservice" />
+               <check send_destination="net.netconfig" send_interface="net.netconfig.vpnsvc" send_member="vpn_protect" privilege="http://tizen.org/privilege/vpnservice" />
+               <check send_destination="net.netconfig" send_interface="net.netconfig.vpnsvc" send_member="vpn_up" privilege="http://tizen.org/privilege/vpnservice" />
+               <check send_destination="net.netconfig" send_interface="net.netconfig.vpnsvc" send_member="vpn_down" privilege="http://tizen.org/privilege/vpnservice" />
+               <check send_destination="net.netconfig" send_interface="net.netconfig.vpnsvc" send_member="vpn_block_networks" privilege="http://tizen.org/privilege/vpnservice" />
+               <check send_destination="net.netconfig" send_interface="net.netconfig.vpnsvc" send_member="vpn_unblock_networks" privilege="http://tizen.org/privilege/vpnservice" />
        </policy>
 </busconfig>
index e9f6dfe..7e78176 100755 (executable)
@@ -41,6 +41,7 @@
 static GDBusObjectManagerServer *manager_server_wifi = NULL;
 static GDBusObjectManagerServer *manager_server_state = NULL;
 static GDBusObjectManagerServer *manager_server_statistics = NULL;
+static GDBusObjectManagerServer *manager_server_vpn = NULL;
 static guint owner_id = 0;
 static got_name_cb g_callback = NULL;
 
@@ -67,6 +68,11 @@ GDBusObjectManagerServer *netdbus_get_statistics_manager(void)
        return manager_server_statistics;
 }
 
+GDBusObjectManagerServer *netdbus_get_vpn_manager(void)
+{
+       return manager_server_vpn;
+}
+
 GDBusConnection *netdbus_get_connection(void)
 {
        return gconn_data.connection;
@@ -318,6 +324,12 @@ int setup_gdbus(got_name_cb cb)
                exit(1);
        }
 
+       manager_server_vpn= g_dbus_object_manager_server_new(NETCONFIG_VPNSVC_PATH);
+       if (manager_server_vpn == NULL) {
+               ERR("Manager server for VPNSVC_PATH not created.");
+               exit(1);
+       }
+
        owner_id = g_bus_own_name(G_BUS_TYPE_SYSTEM, NETCONFIG_SERVICE,
                                                          G_BUS_NAME_OWNER_FLAGS_NONE,
                                                          _got_bus_cb, _got_name_cb, _lost_name_cb,
index a4605ed..9a7a833 100755 (executable)
@@ -30,6 +30,7 @@
 #include "neterror.h"
 #include "wifi-agent.h"
 #include "wifi-power.h"
+#include "vpnsvc.h"
 #include "network-clock.h"
 #include "network-dpm.h"
 #include "network-state.h"
@@ -56,6 +57,7 @@ void _got_name_cb(void)
        wifi_object_create_and_init();
        state_object_create_and_init();
        statistics_object_create_and_init();
+       vpnsvc_create_and_init();
 
        register_gdbus_signal();
        connman_register_agent();
@@ -71,6 +73,7 @@ static void _objects_deinit(void)
        wifi_object_deinit();
        state_object_deinit();
        statistics_object_deinit();
+       vpnsvc_destroy_deinit();
 }
 
 int main(int argc, char *argv[])
index d6db4cf..0e438fd 100755 (executable)
@@ -998,6 +998,13 @@ static gboolean handle_check_profile_privilege(Network *object,
        return TRUE;
 }
 
+static gboolean handle_check_internet_privilege(Network *object,
+               GDBusMethodInvocation *context)
+{
+       network_complete_check_internet_privilege(object, context);
+       return TRUE;
+}
+
 gboolean handle_ethernet_cable_state(Network *object,
        GDBusMethodInvocation *context)
 {
@@ -1038,6 +1045,8 @@ void state_object_create_and_init(void)
                                G_CALLBACK(handle_check_get_privilege), NULL);
        g_signal_connect(netconfigstate, "handle-check-profile-privilege",
                                G_CALLBACK(handle_check_profile_privilege), NULL);
+       g_signal_connect(netconfigstate, "handle-check-internet-privilege",
+                               G_CALLBACK(handle_check_internet_privilege), NULL);
        g_signal_connect(netconfigstate, "handle-ethernet-cable-state",
                                G_CALLBACK(handle_ethernet_cable_state), NULL);
        g_signal_connect(netconfigstate, "handle-remove-route",
diff --git a/src/vpnsvc-internal.c b/src/vpnsvc-internal.c
new file mode 100755 (executable)
index 0000000..a3b4132
--- /dev/null
@@ -0,0 +1,1065 @@
+/*
+ * Network Configuration - VPN Service Internal Module
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+
+#include <errno.h>
+#include <net/route.h>
+#include <glib.h>
+#include <gio/gio.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/un.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+
+#include "vpnsvc-internal.h"
+#include "log.h"
+
+#define BUF_SIZE_FOR_ERR 100
+
+#define CONNMAN_SERVICE "net.connman"
+#define CONNMAN_INTERFACE_MANAGER "net.connman.Manager"
+#define CONNMAN_INTERFACE_SERVICE "net.connman.Service"
+
+
+/* for iptables */
+static char iptables_cmd[] = "/usr/sbin/iptables";
+static char iptables_filter_prefix[] = "CAPI_VPN_SERVICE_";
+static char iptables_filter_out[] = "OUTPUT";
+static char iptables_filter_in[] = "INPUT";
+static char iptables_filter_interface_wlan[] = "wlan0";
+/* static char iptables_register_fmt[] = "%s -N %s%s -w;" "%s -F %s%s -w;" "%s -A %s%s -j RETURN -w;" "%s -I %s -j %s%s -w;"; */
+static char iptables_register_fmt[] = "%s -N %s%s -w;" "%s -F %s%s -w;" "%s -A %s%s -j DROP -w;" "%s -A %s%s -j RETURN -w;" "%s -I %s -j %s%s -w;";
+static char iptables_unregister_fmt[] = "%s -D %s -j %s%s -w;" "%s -F %s%s -w;" "%s -X %s%s -w;";
+static char iptables_rule_fmt[] = "%s -%c %s%s -%c %s/%d -j ACCEPT -w;";
+static char iptables_rule_with_interface_fmt[] = "%s -%c %s%s -%c %s -%c %s/%d -j ACCEPT -w;";
+/*static char iptables_usage_fmt[] = "%s -L %s%s -n -v -w;";*/
+/* iptables -t nat -A CAPI_VPN_SERVICE_OUTPUT -p udp -d <vpn dns address> --dport 53 -j DNAT --to <vpn defice address:53> */
+static char iptables_nat_chain_name[] = "CAPI_VPN_SERVICE_NAT_OUTPUT";
+#if 0
+static char iptables_nat_register_init_fmt[] = "%s -t nat -N %s -w;" "%s -t nat -F %s -w;" "%s -t nat -I %s -j %s -w;";
+static char iptables_nat_register_rule_fmt[] = "%s -t nat -A %s -p udp -d %s --dport 53 -j DNAT --to %s:53 -w;";
+#endif
+static char iptables_nat_unregister_fmt[] = "%s -t nat -D %s -j %s -w;" "%s -t nat -F %s -w;" "%s -t nat -X %s -w;";
+
+typedef unsigned long int ipv4;        /* Declare variable type for ipv4 net address. */
+
+static GDBusConnection *global_connection = NULL;
+
+static ipv4 make_mask(int prefix)
+{
+       ipv4 mask = 0;
+       int i = 0;
+
+       for (i = prefix; i > 0; i--)
+               mask += (ipv4) (1 << (32 - i));
+       return mask;
+}
+
+static in_addr_t host2net(ipv4 host)
+{
+       in_addr_t net;
+
+       net = 0;
+
+       net |= (host & 0x000000FF) << 24;
+       net |= (host & 0x0000FF00) <<  8;
+       net |= (host & 0x00FF0000) >>  8;
+       net |= (host & 0xFF000000) >> 24;
+
+       return net;
+}
+
+static int add_routes(char* iface_name, char* routes[], int prefix[], size_t nr_routes)
+{
+       struct rtentry rt;
+       struct sockaddr_in addr;
+       int sk;
+       unsigned int i = 0;
+       char buf[BUF_SIZE_FOR_ERR] = { 0 };
+
+       DBG("Enter add_routes");
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0) {
+               ERR("socket failed : %s", strerror_r(errno, buf, BUF_SIZE_FOR_ERR));
+               return VPNSVC_ERROR_IO_ERROR;
+       }
+
+       for (i = 0; i < nr_routes; i++) {
+               memset(&rt, 0, sizeof(rt));
+               rt.rt_flags = RTF_UP;
+
+               memset(&addr, 0, sizeof(addr));
+               addr.sin_family = AF_INET;
+               addr.sin_addr.s_addr = inet_addr(routes[i]);
+               memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst));
+
+               memset(&addr, 0, sizeof(addr));
+               addr.sin_family = AF_INET;
+               addr.sin_addr.s_addr = INADDR_ANY;
+               memcpy(&rt.rt_gateway, &addr, sizeof(rt.rt_gateway));
+
+               /* set mask using by prefix length */
+               memset(&addr, 0, sizeof(addr));
+               addr.sin_family = AF_INET;
+               addr.sin_addr.s_addr = INADDR_ANY;
+               addr.sin_addr.s_addr = host2net(make_mask(prefix[i]));
+               memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask));
+
+               rt.rt_dev = iface_name;
+
+               if (ioctl(sk, SIOCADDRT, &rt) < 0) {
+                       ERR("ioctl SIOCADDRT failed : %s", strerror_r(errno, buf, BUF_SIZE_FOR_ERR));
+                       close(sk);
+                       return VPNSVC_ERROR_IO_ERROR;
+               }
+       }
+
+       close(sk);
+
+       return VPNSVC_ERROR_NONE;
+}
+
+static int add_dns_routes(char* if_name, char** dns_servers, size_t nr_dns)
+{
+       struct rtentry rt;
+       struct sockaddr_in addr;
+       int sk;
+       unsigned int i = 0;
+       char buf[BUF_SIZE_FOR_ERR] = { 0 };
+
+       DBG("Enter add_routes");
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0) {
+               ERR("socket failed : %s", strerror_r(errno, buf, BUF_SIZE_FOR_ERR));
+               return VPNSVC_ERROR_IO_ERROR;
+       }
+
+       for (i = 0; i < nr_dns; i++) {
+               memset(&rt, 0, sizeof(rt));
+               rt.rt_flags = RTF_UP;
+
+               memset(&addr, 0, sizeof(addr));
+               addr.sin_family = AF_INET;
+               addr.sin_addr.s_addr = inet_addr(dns_servers[i]);
+               memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst));
+
+               memset(&addr, 0, sizeof(addr));
+               addr.sin_family = AF_INET;
+               addr.sin_addr.s_addr = INADDR_ANY;
+               memcpy(&rt.rt_gateway, &addr, sizeof(rt.rt_gateway));
+
+               /* set mask using by prefix length */
+               memset(&addr, 0, sizeof(addr));
+               addr.sin_family = AF_INET;
+               addr.sin_addr.s_addr = INADDR_ANY;
+               addr.sin_addr.s_addr = host2net(make_mask(32));
+               memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask));
+
+               rt.rt_dev = if_name;
+
+               if (ioctl(sk, SIOCADDRT, &rt) < 0) {
+                       ERR("ioctl SIOCADDRT failed : %s", strerror_r(errno, buf, BUF_SIZE_FOR_ERR));
+                       close(sk);
+                       return VPNSVC_ERROR_IO_ERROR;
+               }
+       }
+
+       close(sk);
+
+       return VPNSVC_ERROR_NONE;
+}
+
+static void connman_connection_open(void)
+{
+       if (global_connection == NULL) {
+               GError *error = NULL;
+#if !GLIB_CHECK_VERSION(2, 36, 0)
+               g_type_init();
+#endif
+
+               global_connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
+               if (global_connection == NULL) {
+                       if (error != NULL) {
+                               ERR("Error connman connection open: %s", error->message);
+                               g_error_free(error);
+                       }
+               }
+       }
+}
+
+static void connman_connection_close(GDBusConnection *connection)
+{
+       if (connection)
+               g_object_unref(connection);
+}
+
+static GVariant *connman_method_call(
+       GDBusConnection *connection, char *service, char *path,
+       char *interface, char *method, GVariant *params)
+{
+       GError *error = NULL;
+       GVariant *message = NULL;
+
+       message = g_dbus_connection_call_sync(
+               connection, service, path, interface, method, params,
+               NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
+
+       if (message == NULL) {
+               if (error != NULL) {
+                       ERR("error: g_dbus_connection_call_sync [%d: %s]", error->code, error->message);
+                       g_error_free(error);
+               } else {
+                       ERR("error: g_dbus_connection_call_sync\n");
+               }
+       }
+
+       return message;
+}
+
+static char *connman_default_profile(GDBusConnection *connection)
+{
+       gchar *key = NULL;
+       GVariantIter *value = NULL;
+       GVariant *message = NULL;
+       GVariantIter *iter = NULL;
+       char *profile = NULL;
+
+       message = connman_method_call(connection, CONNMAN_SERVICE, "/",
+                                                                 CONNMAN_INTERFACE_MANAGER, "GetServices", NULL);
+
+       if (message) {
+               g_variant_get(message, "(a(oa{sv}))", &iter);
+               while (g_variant_iter_loop(iter, "(oa{sv})", &key, &value)) {
+                       profile = strdup(key);
+                       break;
+               }
+
+               if (value)
+                       g_variant_iter_free(value);
+               if (key)
+                       g_free(key);
+
+               g_variant_iter_free(iter);
+               g_variant_unref(message);
+       }
+
+       return profile;
+}
+
+#if 0
+static char *connman_get_items(GDBusConnection *connection, char *profile, const char *keystr)
+{
+       GVariant *message = NULL;
+       GVariantIter *iter = NULL;
+       GVariantIter *next = NULL;
+       gchar *obj = NULL;
+       char *items = NULL;
+
+       message = connman_method_call(connection, CONNMAN_SERVICE, "/",
+                                                                 CONNMAN_INTERFACE_MANAGER, "GetServices", NULL);
+
+       if (message) {
+               g_variant_get(message, "(a(oa{sv}))", &iter);
+               while (g_variant_iter_loop(iter, "(oa{sv})", &obj, &next)) {
+                       if (strcmp(obj, profile) == 0) {
+                               GVariant *var;
+                               gchar *key;
+
+                               while (g_variant_iter_loop(next, "{sv}", &key, &var)) {
+                                       if (g_strcmp0(key, keystr) == 0) {
+                                               GVariantIter *iter_item;
+                                               const gchar *value = NULL;
+
+                                               g_variant_get(var, "as", &iter_item);
+                                               while (g_variant_iter_loop(iter_item, "s", &value)) {
+                                                       if (items) {
+                                                               char *tmp_items;
+
+                                                               tmp_items = (char *) malloc(strlen(items) + 1 + strlen(value) + 1);
+                                                               if (items) {
+                                                                       snprintf(tmp_items, strlen(tmp_items), "%s,%s", items, value);
+                                                                       free(items);
+                                                                       items = tmp_items;
+                                                               }
+                                                       } else {
+                                                               items = strdup(value);
+                                                       }
+                                               }
+                                               g_variant_iter_free(iter_item);
+                                               break;
+                                       }
+                               }
+                               break;
+                       }
+               }
+               g_variant_iter_free(iter);
+               g_variant_unref(message);
+       }
+
+       return items;
+}
+#endif
+
+static void connman_set_items(GDBusConnection *connection, char *profile,
+                                                         const char *keystr, char *items)
+{
+       GVariant *message = NULL;
+       GVariantBuilder *builder = NULL;
+       GVariant *params = NULL;
+       char *strings = strdup(items);
+       char *addr = NULL;
+       char *temp = NULL;
+
+       builder = g_variant_builder_new(G_VARIANT_TYPE("as"));
+       if ((addr = strtok_r(strings, ", ", &temp)) != NULL) {
+               do {
+                       g_variant_builder_add(builder, "s", addr);
+               } while ((addr = strtok_r(NULL, ", ", &temp)) != NULL);
+       }
+       free(strings);
+       params = g_variant_new("(sv)", keystr,
+                                                  g_variant_builder_end(builder));
+       g_variant_builder_unref(builder);
+
+       message = connman_method_call(connection, CONNMAN_SERVICE, profile,
+                                                                 CONNMAN_INTERFACE_SERVICE, "SetProperty", params);
+       if (message)
+               g_variant_unref(message);
+
+}
+
+#if 0
+static char *connman_get_nameservers(GDBusConnection *connection, char *profile)
+{
+       return connman_get_items(connection, profile, "Nameservers");
+}
+
+static char *connman_get_nameservers_conf(GDBusConnection *connection, char *profile)
+{
+       return connman_get_items(connection, profile, "Nameservers.Configuration");
+}
+#endif
+
+static void connman_set_nameservers(GDBusConnection *connection, char *profile,
+                                                                       char *nameservers)
+{
+       return connman_set_items(connection, profile,
+                                                        "Nameservers.Configuration", nameservers);
+}
+
+#if 0
+static char *connman_get_domains(GDBusConnection *connection, char *profile)
+{
+       return connman_get_items(connection, profile, "Domains");
+}
+
+static char *connman_get_domains_conf(GDBusConnection *connection, char *profile)
+{
+       return connman_get_items(connection, profile, "Domains.Configuration");
+}
+#endif
+
+static void connman_set_domains(GDBusConnection *connection, char *profile,
+                                                                       char *domains)
+{
+       return connman_set_items(connection, profile,
+                                                        "Domains.Configuration", domains);
+}
+
+#if 0
+static int add_dns_servers(char** dns_servers, size_t nr_dns, size_t total_dns_string_cnt)
+{
+       char *profile = NULL;
+       char *items = NULL;
+       char *org_items = NULL;
+       char *new_items = NULL;
+       unsigned int i = 0;
+
+       connman_connection_open();
+
+       profile = connman_default_profile(global_connection);
+       if (profile == NULL) {
+               ERR("connman_default_profile failed");
+               connman_connection_close(global_connection);
+               return VPNSVC_ERROR_IPC_FAILED;
+       }
+
+       DBG("profile : %s\n", profile);
+
+       /* add name servers */
+       org_items = connman_get_nameservers(global_connection, profile);
+
+       if (org_items) {
+               DBG("original DNS : %s\n", org_items);
+               /* nr_dns = comma(,) count */
+               items = (char *) calloc((total_dns_string_cnt + nr_dns + strlen(org_items) + 1), sizeof(char));
+               if (items == NULL) {
+                       ERR("OOM while malloc\n");
+                       return VPNSVC_ERROR_OUT_OF_MEMORY;
+               }
+               strncpy(items, org_items, strlen(org_items));
+               for (i = 0 ; i < nr_dns ; i++) {
+                       strncat(items, ",", 1);
+                       strncat(items, dns_servers[i], strlen(dns_servers[i]));
+               }
+               free(org_items);
+               org_items = NULL;
+       } else {
+               /* nr_dns = comma(,) count + end null char */
+               items = (char *) calloc(total_dns_string_cnt + nr_dns, sizeof(char));
+               if (items == NULL) {
+                       ERR("OOM while malloc\n");
+                       return VPNSVC_ERROR_OUT_OF_MEMORY;
+               }
+               for (i = 0 ; i < nr_dns ; i++) {
+                       strncat(items, dns_servers[i], strlen(dns_servers[i]));
+                       if (i != nr_dns - 1)
+                               strncat(items, ",", 1);
+               }
+       }
+
+       if (items) {
+               DBG("adding DNS : %s\n", items);
+               connman_set_nameservers(global_connection, profile, items);
+               free(items);
+               items = NULL;
+       }
+
+       /* print new DNSs */
+       new_items = connman_get_nameservers_conf(global_connection, profile);
+       DBG("new_dns : %s\n", new_items);
+
+       if (new_items)
+               free(new_items);
+       free(profile);
+       return VPNSVC_ERROR_NONE;
+}
+#endif
+
+static int del_dns_servers()
+{
+       char *profile = NULL;
+
+       connman_connection_open();
+
+       profile = connman_default_profile(global_connection);
+       if (profile == NULL) {
+               ERR("connman_default_profile failed");
+               connman_connection_close(global_connection);
+               return VPNSVC_ERROR_IPC_FAILED;
+       }
+
+       DBG("profile : %s", profile);
+
+       /* del name servers */
+       connman_set_nameservers(global_connection, profile, "");
+
+       if (profile)
+               free(profile);
+
+       return VPNSVC_ERROR_NONE;
+}
+
+#if 0
+static int add_dns_suffix(const char* dns_suffix, size_t dns_suffix_len)
+{
+       char *profile = NULL;
+       char *items = NULL;
+       char *org_items = NULL;
+       char *new_items = NULL;
+
+       connman_connection_open();
+
+       profile = connman_default_profile(global_connection);
+       if (profile == NULL) {
+               ERR("connman_default_profile failed");
+               connman_connection_close(global_connection);
+               return VPNSVC_ERROR_IPC_FAILED;
+       }
+
+       DBG("profile : %s", profile);
+
+       /* add name servers */
+       org_items = connman_get_domains(global_connection, profile);
+
+       if (org_items) {
+               DBG("original DNS suffix : %s", org_items);
+               /* comma(,) and end null character included */
+               items = (char *) calloc((dns_suffix_len + strlen(org_items) + 2), sizeof(char));
+               if (items == NULL) {
+                       ERR("OOM while malloc");
+                       return VPNSVC_ERROR_OUT_OF_MEMORY;
+               }
+               strncpy(items, org_items, strlen(org_items));
+               strncat(items, ",", 1);
+               strncat(items, dns_suffix, dns_suffix_len);
+               free(org_items);
+               org_items = NULL;
+       } else {
+               /* nr_dns = comma(,) count + end null char */
+               items = (char *) calloc((dns_suffix_len + 1), sizeof(char));
+               if (items == NULL) {
+                       ERR("OOM while malloc");
+                       return VPNSVC_ERROR_OUT_OF_MEMORY;
+               }
+               strncat(items, dns_suffix, dns_suffix_len);
+       }
+
+       if (items) {
+               DBG("adding DNS suffix : %s\n", items);
+               connman_set_domains(global_connection, profile, items);
+               free(items);
+               items = NULL;
+       }
+
+       /* print new domains */
+       new_items = connman_get_domains_conf(global_connection, profile);
+       DBG("new DNS suffix : %s\n", new_items);
+
+       if (new_items)
+               free(new_items);
+
+       if (profile)
+               free(profile);
+
+       return VPNSVC_ERROR_NONE;
+}
+#endif
+
+static int del_dns_suffix()
+{
+       char *profile = NULL;
+
+       connman_connection_open();
+
+       profile = connman_default_profile(global_connection);
+       if (profile == NULL) {
+               ERR("connman_default_profile failed");
+               connman_connection_close(global_connection);
+               return VPNSVC_ERROR_IPC_FAILED;
+       }
+
+       DBG("profile : %s", profile);
+
+       /* del DNS suffix */
+       connman_set_domains(global_connection, profile, "");
+
+       if (profile)
+               free(profile);
+
+       return VPNSVC_ERROR_NONE;
+}
+
+
+static void iptables_exec(char *cmdline)
+{
+       FILE *fp = NULL;
+
+       fp = popen(cmdline, "r");
+
+       if (fp != NULL)
+               pclose(fp);
+}
+
+#if 0
+static void dns_nat_register(char **vpn_dns_address, size_t nr_dns, char *vpn_device_address)
+{
+       int size = 0, i;
+       char buf[8192];
+
+       snprintf(buf + size, sizeof(buf) - size, iptables_nat_register_init_fmt,
+                       iptables_cmd, iptables_nat_chain_name,
+                       iptables_cmd, iptables_nat_chain_name,
+                       iptables_cmd, iptables_filter_out, iptables_nat_chain_name);
+       size = strlen(buf);
+
+       for (i = 0 ; i < nr_dns ; i++) {
+               snprintf(buf + size, sizeof(buf) - size, iptables_nat_register_rule_fmt,
+                               iptables_cmd, iptables_nat_chain_name, vpn_dns_address[i], vpn_device_address);
+               size = strlen(buf);
+       }
+       DBG("iptable dns nat reg cmd : %s", buf);
+       iptables_exec(buf);
+}
+#endif
+
+static void dns_nat_unregister(void)
+{
+       int size = 0;
+       char buf[8192];
+
+       snprintf(buf + size, sizeof(buf) - size, iptables_nat_unregister_fmt,
+                       iptables_cmd, iptables_filter_out, iptables_nat_chain_name,
+                       iptables_cmd, iptables_nat_chain_name,
+                       iptables_cmd, iptables_nat_chain_name);
+       size = strlen(buf);
+       DBG("iptable dns nat unreg cmd : %s", buf);
+       iptables_exec(buf);
+}
+
+static void iptables_register(void)
+{
+       int size = 0;
+       char buf[8192], *filter;
+
+       filter = iptables_filter_out;
+       snprintf(buf + size, sizeof(buf) - size, iptables_register_fmt,
+                        iptables_cmd, iptables_filter_prefix, filter,
+                        iptables_cmd, iptables_filter_prefix, filter,
+                        iptables_cmd, iptables_filter_prefix, filter,
+                        iptables_cmd, iptables_filter_prefix, filter,
+                        iptables_cmd, filter, iptables_filter_prefix, filter);
+       size = strlen(buf);
+       filter = iptables_filter_in;
+       snprintf(buf + size, sizeof(buf) - size, iptables_register_fmt,
+                        iptables_cmd, iptables_filter_prefix, filter,
+                        iptables_cmd, iptables_filter_prefix, filter,
+                        iptables_cmd, iptables_filter_prefix, filter,
+                        iptables_cmd, iptables_filter_prefix, filter,
+                        iptables_cmd, filter, iptables_filter_prefix, filter);
+       DBG("iptable reg cmd : %s", buf);
+       iptables_exec(buf);
+}
+
+static void iptables_unregister(void)
+{
+       int size = 0;
+       char buf[8192], *filter;
+
+       filter = iptables_filter_out;
+       snprintf(buf + size, sizeof(buf) - size, iptables_unregister_fmt,
+                        iptables_cmd, filter, iptables_filter_prefix, filter,
+                        iptables_cmd, iptables_filter_prefix, filter,
+                        iptables_cmd, iptables_filter_prefix, filter);
+       size = strlen(buf);
+       filter = iptables_filter_in;
+       snprintf(buf + size, sizeof(buf) - size, iptables_unregister_fmt,
+                        iptables_cmd, filter, iptables_filter_prefix, filter,
+                        iptables_cmd, iptables_filter_prefix, filter,
+                        iptables_cmd, iptables_filter_prefix, filter);
+       DBG("iptable unreg cmd : %s", buf);
+       iptables_exec(buf);
+}
+
+static void iptables_rule(const char c, const char *addr, const int mask)
+{
+       int size = 0;
+       char buf[4096];
+
+       snprintf(buf + size, sizeof(buf) - size, iptables_rule_fmt, iptables_cmd, c,
+                        iptables_filter_prefix, iptables_filter_out, 'd', addr, mask);
+       size = strlen(buf);
+       snprintf(buf + size, sizeof(buf) - size, iptables_rule_fmt, iptables_cmd, c,
+                        iptables_filter_prefix, iptables_filter_in, 's', addr, mask);
+       DBG("iptable cmd : %s", buf);
+       iptables_exec(buf);
+}
+
+static void iptables_rule_interface(const char c, const char *addr, const int mask, const char *interface)
+{
+       int size = 0;
+       char buf[4096];
+
+       snprintf(buf + size, sizeof(buf) - size,
+                       iptables_rule_with_interface_fmt, iptables_cmd,
+                       c, iptables_filter_prefix, iptables_filter_out,
+                       'o', interface, 'd', addr, mask);
+       size = strlen(buf);
+       snprintf(buf + size, sizeof(buf) - size,
+                       iptables_rule_with_interface_fmt, iptables_cmd,
+                       c, iptables_filter_prefix, iptables_filter_in,
+                       'i', interface, 's', addr, mask);
+       DBG("iptable cmd : %s", buf);
+       iptables_exec(buf);
+}
+
+void iptables_add_orig(const char *addr, const int mask)
+{
+       iptables_rule_interface('I', addr, mask, iptables_filter_interface_wlan);
+}
+
+void iptables_delete_orig(const char *addr, const int mask)
+{
+       iptables_rule_interface('D', addr, mask, iptables_filter_interface_wlan);
+}
+
+void iptables_add(const char *addr, const int mask)
+{
+       iptables_rule('I', addr, mask);
+}
+
+void iptables_delete(const char *addr, const int mask)
+{
+       iptables_rule('D', addr, mask);
+}
+
+static int get_interface_index(const char *iface_name)
+{
+       struct ifreq ifr;
+       int sk = 0;
+       char buf[BUF_SIZE_FOR_ERR] = { 0 };
+
+       DBG("enter get_interface_index, iface_name : %s", iface_name);
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0) {
+               ERR("socket failed : %s", strerror_r(errno, buf, BUF_SIZE_FOR_ERR));
+               return VPNSVC_ERROR_IO_ERROR;
+       }
+
+       memset(&ifr, 0, sizeof(ifr));
+
+       if (*iface_name)
+       strncpy(ifr.ifr_name, iface_name, strlen(iface_name));
+
+       /* get an interface name by ifindex */
+       if (ioctl(sk, SIOCGIFINDEX, &ifr) < 0) {
+               ERR("ioctl SIOCGIFINDEX failed : %s", strerror_r(errno, buf, BUF_SIZE_FOR_ERR));
+               close(sk);
+               return VPNSVC_ERROR_IO_ERROR;
+       }
+
+       close(sk);
+
+       return ifr.ifr_ifindex;
+}
+
+
+int vpn_service_init(const char* iface_name, size_t iface_name_len, int fd, vpnsvc_tun_s *handle_s)
+{
+       struct ifreq ifr;
+       size_t len = 0;
+       char buf[BUF_SIZE_FOR_ERR] = { 0 };
+
+       DBG("enter vpn_daemon_init, iface_name : %s, iface_name_len : %d, fd : %d\n", iface_name, iface_name_len, fd);
+
+       memset(&ifr, 0, sizeof(ifr));
+
+       /* Flags: IFF_TUN   - TUN device (no Ethernet headers)
+       *               IFF_TAP   - TAP device
+       *
+       *               IFF_NO_PI - Do not provide packet information
+       */
+
+       ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
+
+       if (*iface_name)
+               strncpy(ifr.ifr_name, iface_name, iface_name_len);
+
+       DBG("before init, ifindex : %d", ifr.ifr_ifindex);
+
+       if (ioctl(fd, TUNSETIFF, (void *) &ifr) < 0) {
+               ERR("TUNSETIFF Failed : %s", strerror_r(errno, buf, BUF_SIZE_FOR_ERR));
+               close(fd);
+               return VPNSVC_ERROR_IO_ERROR;
+       }
+
+       if (ioctl(fd, TUNSETOWNER, 5000) < 0) {
+               ERR("TUNSETOWNER Failed : %s", strerror_r(errno, buf, BUF_SIZE_FOR_ERR));
+               close(fd);
+               return VPNSVC_ERROR_IO_ERROR;
+       }
+
+       if (ioctl(fd, TUNSETPERSIST, 1) < 0) {
+               ERR("TUNSETPERSIST Failed : %s", strerror_r(errno, buf, BUF_SIZE_FOR_ERR));
+               close(fd);
+               return VPNSVC_ERROR_IO_ERROR;
+       }
+
+       handle_s->fd = 0;   /* server fd does not meaning */
+       handle_s->index = get_interface_index(iface_name);
+       len = strlen(ifr.ifr_name);
+       strncpy(handle_s->name, ifr.ifr_name, len);
+       handle_s->name[len] = '\0';
+
+       return VPNSVC_ERROR_NONE;
+}
+
+int vpn_service_deinit(const char* dev_name)
+{
+       char buf[100];
+       FILE *fp = NULL;
+
+       snprintf(buf, sizeof(buf), "/usr/sbin/ip link del %s", dev_name);
+       DBG("link delete cmd : %s", buf);
+
+       fp = popen(buf, "r");
+       if (fp != NULL) {
+               pclose(fp);
+               return VPNSVC_ERROR_NONE;
+       } else {
+               return VPNSVC_ERROR_IO_ERROR;
+       }
+}
+
+int vpn_service_protect(int socket_fd, const char* dev_name)
+{
+       int ret = VPNSVC_ERROR_NONE;
+       char buf[BUF_SIZE_FOR_ERR] = { 0 };
+       DBG("enter vpn_daemon_protect, socket : %d, dev_name : %s\n", socket_fd, dev_name);
+
+       ret = setsockopt(socket_fd, SOL_SOCKET, SO_BINDTODEVICE,
+                                       dev_name, strlen(dev_name));
+
+       if (ret < 0) {
+               DBG("setsockopt failed : %d, %s", ret, strerror_r(errno, buf, BUF_SIZE_FOR_ERR));
+               ret = VPNSVC_ERROR_IO_ERROR;
+       } else {
+               ret = VPNSVC_ERROR_NONE;
+       }
+
+       return ret;
+}
+
+int vpn_service_up(int iface_index, const char* local_ip, const char* remote_ip,
+                                               char* routes[], int prefix[], size_t nr_routes,
+                                               char** dns_servers, size_t nr_dns, size_t total_dns_string_cnt,
+                                               const char* dns_suffix, const unsigned int mtu) {
+
+       struct sockaddr_in local_addr;
+       struct sockaddr_in remote_addr;
+       struct ifreq ifr_tun;
+       int sk;
+       int ret = VPNSVC_ERROR_NONE;
+       char buf[BUF_SIZE_FOR_ERR] = { 0 };
+
+       DBG("enter vpn_daemon_up");
+
+       DBG("iface_index : %d", iface_index);
+       DBG("local ip : %s", local_ip);
+       DBG("remote ip : %s", remote_ip);
+       DBG("route pointer : %p, nr_routes : %d, dns_server pointer : %p, nr_dns : %d, dns_suffix : %s, mtu : %d", routes, nr_routes, dns_servers, nr_dns, dns_suffix, mtu);
+
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0) {
+               ERR("socket failed : %s", strerror_r(errno, buf, BUF_SIZE_FOR_ERR));
+               return VPNSVC_ERROR_IO_ERROR;
+       }
+
+       memset(&ifr_tun, 0, sizeof(ifr_tun));
+       ifr_tun.ifr_ifindex = iface_index;
+
+       /* get an interface name by ifindex */
+       if (ioctl(sk, SIOCGIFNAME, &ifr_tun) < 0) {
+               ERR("ioctl SIOCGIFNAME failed : %s", strerror_r(errno, buf, BUF_SIZE_FOR_ERR));
+               close(sk);
+               return VPNSVC_ERROR_IO_ERROR;
+       }
+
+       /* local ip setting */
+       memset(&local_addr, 0, sizeof(local_addr));
+       local_addr.sin_addr.s_addr = inet_addr(local_ip); /* network byte order */
+       local_addr.sin_family = AF_INET;
+       memcpy(&ifr_tun.ifr_addr, &local_addr, sizeof(ifr_tun.ifr_addr));
+       if (ioctl(sk, SIOCSIFADDR, &ifr_tun) < 0) {
+               ERR("ioctl SIOCSIFADDR failed : %s", strerror_r(errno, buf, BUF_SIZE_FOR_ERR));
+               close(sk);
+               return VPNSVC_ERROR_IO_ERROR;
+       }
+
+       /* remote ip setting */
+       memset(&remote_addr, 0, sizeof(remote_addr));
+       remote_addr.sin_addr.s_addr = inet_addr(remote_ip); /*network byte order*/
+       remote_addr.sin_family = AF_INET;
+       memcpy(&ifr_tun.ifr_dstaddr, &remote_addr, sizeof(ifr_tun.ifr_dstaddr));
+       if (ioctl(sk, SIOCSIFDSTADDR, &ifr_tun) < 0) {
+               ERR("ioctl SIOCSIFDSTADDR failed : %s", strerror_r(errno, buf, BUF_SIZE_FOR_ERR));
+               close(sk);
+               return VPNSVC_ERROR_IO_ERROR;
+       }
+
+       /* set the flags for vpn up */
+       if (ioctl(sk, SIOCGIFFLAGS, &ifr_tun) < 0) {
+               ERR("ioctl SIOCGIFFLAGS failed : %s", strerror_r(errno, buf, BUF_SIZE_FOR_ERR));
+               close(sk);
+               return VPNSVC_ERROR_IO_ERROR;
+       }
+
+       ifr_tun.ifr_flags |= IFF_UP;
+       ifr_tun.ifr_flags |= IFF_RUNNING;
+
+       if (ioctl(sk, SIOCSIFFLAGS, &ifr_tun) < 0)  {
+               ERR("ioctl SIOCSIFFLAGS failed : %s", strerror_r(errno, buf, BUF_SIZE_FOR_ERR));
+               close(sk);
+               return VPNSVC_ERROR_IO_ERROR;
+       }
+
+       /* mtu setting */
+       if (ioctl(sk, SIOCGIFMTU, &ifr_tun) < 0) {
+               ERR("ioctl SIOCGIFMTU failed : %s", strerror_r(errno, buf, BUF_SIZE_FOR_ERR));
+               close(sk);
+               return VPNSVC_ERROR_IO_ERROR;
+       }
+
+       if (mtu > 0 && ifr_tun.ifr_mtu != (int)mtu) {
+               ifr_tun.ifr_mtu = mtu;
+               if (ioctl(sk, SIOCSIFMTU, &ifr_tun) < 0) {
+                       ERR("ioctl SIOCSIFMTU failed : %s", strerror_r(errno, buf, BUF_SIZE_FOR_ERR));
+                       close(sk);
+                       return VPNSVC_ERROR_IO_ERROR;
+               }
+       }
+
+       close(sk);
+
+       /* add routes */
+       if (nr_routes > 0) {
+               ret = add_routes(ifr_tun.ifr_name, routes, prefix, nr_routes);
+               if (ret != VPNSVC_ERROR_NONE) {
+                       ERR("add_routes failed");
+                       return ret;
+               }
+       }
+
+       /* add DNS routes */
+       if (nr_dns > 0) {
+               ret = add_dns_routes(ifr_tun.ifr_name, dns_servers, nr_dns);
+               if (ret != VPNSVC_ERROR_NONE) {
+                       ERR("add_dns failed");
+                       return ret;
+               }
+       }
+
+#if 0
+       /* add DNS servers */
+       if (nr_dns > 0) {
+               ret = add_dns_servers(dns_servers, nr_dns, total_dns_string_cnt);
+               if (ret != VPNSVC_ERROR_NONE) {
+                       ERR("add_dns failed");
+                       return ret;
+               }
+       }
+
+       /* add_dns_suffix */
+       if (dns_suffix) {
+               ret = add_dns_suffix(dns_suffix, strlen(dns_suffix));
+               if (ret != VPNSVC_ERROR_NONE) {
+                       ERR("add_dns_suffix failed");
+                       return ret;
+               }
+       }
+
+       if (nr_dns > 0)
+               dns_nat_register(dns_servers, nr_dns, local_ip);
+#endif
+
+       return ret;
+}
+
+
+
+int vpn_service_down(int iface_index)
+{
+       struct ifreq ifr, addr_ifr;
+       struct sockaddr_in *addr = NULL;
+       int sk;
+       char buf[BUF_SIZE_FOR_ERR] = { 0 };
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0) {
+               ERR("socket failed : %s", strerror_r(errno, buf, BUF_SIZE_FOR_ERR));
+               return VPNSVC_ERROR_IO_ERROR;
+       }
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = iface_index;
+
+       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               ERR("ioctl SIOCGIFNAME failed : %s", strerror_r(errno, buf, BUF_SIZE_FOR_ERR));
+               close(sk);
+               return VPNSVC_ERROR_IO_ERROR;
+       }
+
+       if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
+               ERR("ioctl SIOCGIFFLAGS failed : %s", strerror_r(errno, buf, BUF_SIZE_FOR_ERR));
+               close(sk);
+               return VPNSVC_ERROR_IO_ERROR;
+       }
+
+       memset(&addr_ifr, 0, sizeof(addr_ifr));
+       memcpy(&addr_ifr.ifr_name, &ifr.ifr_name, sizeof(ifr.ifr_name) - 1);
+       addr = (struct sockaddr_in *)&addr_ifr.ifr_addr;
+       addr->sin_family = AF_INET;
+       if (ioctl(sk, SIOCSIFADDR, &addr_ifr) < 0)
+               DBG("ioctl SIOCSIFADDR (could not clear IP address) failed : %s", strerror_r(errno, buf, BUF_SIZE_FOR_ERR));
+
+       if (!(ifr.ifr_flags & IFF_UP)) {
+               DBG("Interface already down");
+               close(sk);
+               return VPNSVC_ERROR_NONE;
+       }
+
+       ifr.ifr_flags = (ifr.ifr_flags & ~IFF_UP) | IFF_DYNAMIC;
+       if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0) {
+               ERR("ioctl SIOCSIFFLAGS (interface down) failed : %s", strerror_r(errno, buf, BUF_SIZE_FOR_ERR));
+               close(sk);
+               return VPNSVC_ERROR_IO_ERROR;
+       }
+
+       close(sk);
+
+       /* routes are will be removed  automatically while down interfaces */
+       /* remove dns servers */
+       del_dns_servers();
+
+       /* remove dns suffix */
+       del_dns_suffix();
+
+       /* remove dns filter */
+       dns_nat_unregister();
+
+       return VPNSVC_ERROR_NONE;
+}
+
+int vpn_service_block_networks(char* nets_vpn[], int prefix_vpn[], size_t nr_nets_vpn,
+               char* nets_orig[], int prefix_orig[], size_t nr_nets_orig) {
+       unsigned int i;
+
+       /* iptable chain regist */
+       iptables_register();
+
+       for (i = 0; i < nr_nets_vpn; i++) {
+               DBG("block[%d] ip/mask : %s/%d", i, nets_vpn[i], prefix_vpn[i]);
+               iptables_add(nets_vpn[i], prefix_vpn[i]);
+       }
+
+       for (i = 0; i < nr_nets_orig; i++) {
+               DBG("allow[%d] ip/mask : %s/%d", i, nets_orig[i], prefix_orig[i]);
+               iptables_add_orig(nets_orig[i], prefix_orig[i]);
+       }
+
+       return VPNSVC_ERROR_NONE;
+}
+
+int vpn_service_unblock_networks(void)
+{
+       iptables_unregister();
+
+       return VPNSVC_ERROR_NONE;
+}
+
diff --git a/src/vpnsvc.c b/src/vpnsvc.c
new file mode 100755 (executable)
index 0000000..f83bd2e
--- /dev/null
@@ -0,0 +1,386 @@
+/*
+ * Network Configuration - VPN Service Module
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <gio/gunixfdlist.h>
+
+#include "vpnsvc.h"
+#include "vpnsvc-internal.h"
+#include "netdbus.h"
+#include "log.h"
+
+static Vpnsvc *vpnsvc = NULL;
+
+/*********************
+ * Handler Functions *
+ ********************/
+gboolean handle_vpn_init(Vpnsvc *object,
+                                                               GDBusMethodInvocation *invocation,
+                                                               const gchar *arg_iface_name,
+                                                               guint arg_iface_name_len)
+{
+       DBG("handle_vpn_init");
+
+       int result = VPNSVC_ERROR_NONE;
+
+       vpnsvc_tun_s handle_s;
+       GDBusMessage *msg;
+       GUnixFDList *fd_list;
+       int fd_list_length;
+       const int *fds;
+
+       DBG("vpn_init, %s, %u\n", arg_iface_name, arg_iface_name_len);
+
+       msg = g_dbus_method_invocation_get_message(invocation);
+       fd_list = g_dbus_message_get_unix_fd_list(msg);
+       fds = g_unix_fd_list_peek_fds(fd_list, &fd_list_length);
+
+       if (fd_list_length <= 0)
+               DBG("D-Bus Message doesn't contain any fd!");
+
+       DBG("fd:%d\n", *fds);
+
+       result = vpn_service_init(arg_iface_name, arg_iface_name_len, *fds, &handle_s);
+
+       DBG("handle_s.fd : %d, handle_s.index : %d, handle_s.name : %s",
+                       handle_s.fd, handle_s.index, handle_s.name);
+
+       vpnsvc_complete_vpn_init(object, invocation, result, handle_s.index, handle_s.name);
+
+       return TRUE;
+}
+
+gboolean handle_vpn_deinit(Vpnsvc *object,
+                                                                       GDBusMethodInvocation *invocation,
+                                                                       const gchar *arg_dev_name)
+{
+       DBG("handle_vpn_deinit");
+
+       int result = VPNSVC_ERROR_NONE;
+
+       DBG("vpn_deinit, %s\n", arg_dev_name);
+
+       result = vpn_service_deinit(arg_dev_name);
+
+       vpnsvc_complete_vpn_deinit(object, invocation, result);
+
+       return TRUE;
+}
+
+gboolean handle_vpn_protect(Vpnsvc *object,
+                                                                       GDBusMethodInvocation *invocation,
+                                                                       const gchar *arg_dev_name)
+{
+       DBG("handle_vpn_protect");
+
+       int result = VPNSVC_ERROR_NONE;
+
+       int socket;
+       GDBusMessage *msg;
+       GUnixFDList *fd_list;
+       int fd_list_length;
+       const int *fds;
+
+       msg = g_dbus_method_invocation_get_message(invocation);
+       fd_list = g_dbus_message_get_unix_fd_list(msg);
+       fds = g_unix_fd_list_peek_fds(fd_list, &fd_list_length);
+       if (fd_list_length <= 0)
+               DBG("D-Bus Message doesn't contain any fd!");
+
+       socket = *fds;
+       DBG("vpn_protect, %d, %s\n", socket, arg_dev_name);
+
+       result = vpn_service_protect(socket, arg_dev_name);
+
+       vpnsvc_complete_vpn_protect(object, invocation, result);
+
+       return TRUE;
+}
+
+gboolean handle_vpn_up(Vpnsvc *object,
+                                                               GDBusMethodInvocation *invocation,
+                                                               gint arg_iface_index,
+                                                               const gchar *arg_local_ip,
+                                                               const gchar *arg_remote_ip,
+                                                               GVariant *arg_routes,
+                                                               guint arg_nr_routes,
+                                                               GVariant *arg_dns_servers,
+                                                               guint arg_nr_dns,
+                                                               const gchar *arg_dns_suffix,
+                                                               guint arg_mtu)
+{
+       DBG("handle_vpn_up");
+
+       int result = VPNSVC_ERROR_NONE;
+
+       char *routes[arg_nr_routes];
+       int prefix[arg_nr_routes];
+       char **dns_servers = NULL;
+
+       unsigned int i = 0;
+       size_t total_dns_string_cnt = 0;
+       gchar* temp_dns_server;
+       GVariantIter iter;
+
+       gchar* route_dest;
+       gint route_prefix;
+
+       DBG("iface_index : %d", arg_iface_index);
+       DBG("local ip : %s", arg_local_ip);
+       DBG("remote ip : %s", arg_remote_ip);
+       DBG("dns_suffix : %s", arg_dns_suffix);
+       DBG("mtu : %u", arg_mtu);
+       DBG("arg_routes: %p", arg_routes);
+       DBG("nr_routes : %u", arg_nr_routes);
+       DBG("arg_dns_servers: %p", arg_dns_servers);
+       DBG("nr_dns : %u", arg_nr_dns);
+
+       /* arg_routes check */
+       if (arg_nr_routes > 0) {
+               if (arg_routes != NULL) {
+                       GVariant *dict = g_variant_get_variant(arg_routes);
+                       g_variant_iter_init(&iter, dict);
+                       i = 0;
+                       while (g_variant_iter_loop(&iter, "{si}", &route_dest, &route_prefix)) {
+                               int temp_dest_str_len = strlen(route_dest);
+                               routes[i] = g_try_malloc0((sizeof(char) * temp_dest_str_len)+1);
+                               strncpy(routes[i], route_dest, temp_dest_str_len);
+                               routes[i][temp_dest_str_len] = '\0';
+                               prefix[i] = route_prefix;
+                               DBG("routes[%d] = %s \t", i, (routes[i] == NULL) ? "" : routes[i]);
+                               DBG("prefix[%d] = %d ", i, prefix[i]);
+                               i++;
+                       }
+               }
+       }
+
+
+       /* arg_nr_dns check */
+       if (arg_nr_dns > 0) {
+               if (arg_dns_servers != NULL) {
+                       GVariant *array = g_variant_get_variant(arg_dns_servers);
+                       dns_servers = (char **)g_try_malloc0(arg_nr_dns*sizeof(char *));
+                       if (dns_servers == NULL) {
+                               ERR("malloc failed.");
+                               result = VPNSVC_ERROR_OUT_OF_MEMORY;
+                               goto done;
+                       }
+                       g_variant_iter_init(&iter, array);
+                       i = 0;
+                       while (g_variant_iter_loop(&iter, "s", &temp_dns_server)) {
+                               int temp_dns_str_len = strlen(temp_dns_server);
+                               dns_servers[i] = (char *)g_try_malloc0((temp_dns_str_len + 1) * sizeof(char));
+                               strncpy(dns_servers[i], temp_dns_server, strlen(temp_dns_server));
+                               dns_servers[i][temp_dns_str_len] = '\0';
+                               total_dns_string_cnt += temp_dns_str_len;
+                               DBG("dns_servers[%d] : %s", i, (dns_servers[i] == NULL) ? "" : dns_servers[i]);
+                               i++;
+                       }
+               }
+       }
+
+       result = vpn_service_up(arg_iface_index, arg_local_ip, arg_remote_ip,
+                       routes, prefix, arg_nr_routes, dns_servers, arg_nr_dns,
+                       total_dns_string_cnt, arg_dns_suffix, arg_mtu);
+done:
+       /* free pointers */
+       for (i = 0; i < arg_nr_routes; i++) {
+               if (routes[i])
+                       g_free(routes[i]);
+       }
+
+       if (dns_servers) {
+               for (i = 0; i < arg_nr_dns; i++) {
+                       if (dns_servers[i])
+                               g_free(dns_servers[i]);
+               }
+               g_free(dns_servers);
+       }
+
+       vpnsvc_complete_vpn_up(object, invocation, result);
+
+       return TRUE;
+}
+
+gboolean handle_vpn_down(Vpnsvc *object,
+                                                                       GDBusMethodInvocation *invocation,
+                                                                       gint arg_iface_index)
+{
+       DBG("handle_vpn_down");
+
+       int result = VPNSVC_ERROR_NONE;
+
+       DBG("vpn_down, %d\n", arg_iface_index);
+
+       result = vpn_service_down(arg_iface_index);
+
+       vpnsvc_complete_vpn_down(object, invocation, result);
+
+       return TRUE;
+}
+
+gboolean handle_vpn_block_networks(Vpnsvc *object,
+                                                                                       GDBusMethodInvocation *invocation,
+                                                                                       GVariant *arg_nets_vpn,
+                                                                                       guint arg_nr_nets_vpn,
+                                                                                       GVariant *arg_nets_orig,
+                                                                                       guint arg_nr_nets_orig)
+{
+       DBG("handle_vpn_block_networks");
+
+       int result = VPNSVC_ERROR_NONE;
+
+       char *nets_vpn[arg_nr_nets_vpn];
+       int prefix_vpn[arg_nr_nets_vpn];
+
+       char *nets_orig[arg_nr_nets_vpn];
+       int prefix_orig[arg_nr_nets_vpn];
+
+       int i = 0;
+       GVariantIter iter;
+       gchar* route_dest;
+       gint route_prefix;
+
+       DBG("vpn_block_networks");
+
+       /* arg_nets_vpn check */
+       if (arg_nr_nets_vpn > 0) {
+               if (arg_nets_vpn != NULL) {
+                       GVariant *dict_nets_vpn = g_variant_get_variant(arg_nets_vpn);
+                       g_variant_iter_init(&iter, dict_nets_vpn);
+                       i = 0;
+                       while (g_variant_iter_loop(&iter, "{si}", &route_dest, &route_prefix)) {
+                               int tmp_route_len = strlen(route_dest);
+                               nets_vpn[i] = g_try_malloc0(sizeof(char) * tmp_route_len + 1);
+                               strncpy(nets_vpn[i], route_dest, tmp_route_len);
+                               nets_vpn[i][tmp_route_len] = '\0';
+                               prefix_vpn[i] = route_prefix;
+                               DBG("nets_vpn[%d] = %s \t", i, (nets_vpn[i] == NULL) ? "" : nets_vpn[i]);
+                               DBG("prefix_vpn[%d] = %d ", i, prefix_vpn[i]);
+                               i++;
+                       }
+               }
+       }
+
+       /* arg_nets_orig check */
+       if (arg_nr_nets_orig > 0) {
+               if (arg_nets_orig != NULL) {
+                       GVariant *dict_nets_orig = g_variant_get_variant(arg_nets_orig);
+                       g_variant_iter_init(&iter, dict_nets_orig);
+                       i = 0;
+                       while (g_variant_iter_loop(&iter, "{si}", &route_dest, &route_prefix)) {
+                               int tmp_route_len = strlen(route_dest);
+                               nets_orig[i] = g_try_malloc0(sizeof(char) * tmp_route_len + 1);
+                               strncpy(nets_orig[i], route_dest, tmp_route_len);
+                               nets_orig[i][tmp_route_len] = '\0';
+                               prefix_orig[i] = route_prefix;
+                               DBG("nets_orig[%d] = %s \t", i, (nets_orig[i] == NULL) ? "" : nets_orig[i]);
+                               DBG("prefix_orig[%d] = %d ", i, prefix_orig[i]);
+                               i++;
+                       }
+               }
+       }
+
+       /* call function */
+       result = vpn_service_block_networks(nets_vpn, prefix_vpn, arg_nr_nets_vpn, nets_orig, prefix_orig, arg_nr_nets_orig);
+
+       for (i = 0; i < arg_nr_nets_vpn; ++i) {
+               g_free(nets_orig[i]);
+               g_free(nets_vpn[i]);
+       }
+
+       vpnsvc_complete_vpn_block_networks(object, invocation, result);
+
+       return TRUE;
+}
+
+gboolean handle_vpn_unblock_networks(Vpnsvc *object,
+                                                                                       GDBusMethodInvocation *invocation)
+{
+       DBG("handle_vpn_unblock_networks");
+
+       int result = VPNSVC_ERROR_NONE;
+
+       DBG("vpn_unblock_networks");
+
+       result = vpn_service_unblock_networks();
+
+       vpnsvc_complete_vpn_unblock_networks(object, invocation, result);
+
+       return TRUE;
+}
+
+/*****************************
+ * Initializations Functions *
+ ****************************/
+Vpnsvc *get_vpnsvc_object(void)
+{
+       return vpnsvc;
+}
+
+void vpnsvc_create_and_init(void)
+{
+       DBG("Create vpn object.");
+       GDBusInterfaceSkeleton *interface_vpn = NULL;
+       GDBusConnection *connection = NULL;
+       GDBusObjectManagerServer *server = netdbus_get_vpn_manager();
+       if (server == NULL)
+               return;
+
+       connection = netdbus_get_connection();
+       g_dbus_object_manager_server_set_connection(server, connection);
+
+       /* Interface */
+       vpnsvc = vpnsvc_skeleton_new();
+       interface_vpn = G_DBUS_INTERFACE_SKELETON(vpnsvc);
+
+       /* VPN Service */
+       g_signal_connect(vpnsvc, "handle-vpn-init",
+                       G_CALLBACK(handle_vpn_init), NULL);
+       g_signal_connect(vpnsvc, "handle-vpn-deinit",
+                       G_CALLBACK(handle_vpn_deinit), NULL);
+       g_signal_connect(vpnsvc, "handle-vpn-protect",
+                       G_CALLBACK(handle_vpn_protect), NULL);
+       g_signal_connect(vpnsvc, "handle-vpn-up",
+                       G_CALLBACK(handle_vpn_up), NULL);
+       g_signal_connect(vpnsvc, "handle-vpn-down",
+                       G_CALLBACK(handle_vpn_down), NULL);
+       g_signal_connect(vpnsvc, "handle-vpn-block-networks",
+                       G_CALLBACK(handle_vpn_block_networks), NULL);
+       g_signal_connect(vpnsvc, "handle-vpn-unblock-networks",
+                       G_CALLBACK(handle_vpn_unblock_networks), NULL);
+
+       if (!g_dbus_interface_skeleton_export(interface_vpn, connection,
+                       NETCONFIG_VPNSVC_PATH, NULL)) {
+               ERR("Export NETCONFIG_VPNSVC_PATH for vpn failed");
+       }
+
+       return;
+}
+
+void vpnsvc_destroy_deinit(void)
+{
+       DBG("Deinit vpn object.");
+
+       if (vpnsvc)
+               g_object_unref(vpnsvc);
+}
+