From 147af208efece5145cfc4f97695dd782b6beca45 Mon Sep 17 00:00:00 2001 From: hyunuktak Date: Wed, 15 Jun 2016 19:25:01 +0900 Subject: [PATCH] Add the vpn service Change-Id: Ie2bae21f5ee325fb2eea1ab040f50dbc56d227cd Signed-off-by: hyunuktak --- CMakeLists.txt | 4 +- include/netdbus.h | 3 + include/vpnsvc-internal.h | 48 ++ include/vpnsvc.h | 48 ++ interfaces/netconfig-iface-network-state.xml | 1 + interfaces/netconfig-iface-vpnsvc.xml | 47 ++ packaging/net-config.spec | 3 +- resources/etc/dbus-1/system.d/net-config.conf | 9 + src/dbus/netdbus.c | 12 + src/main.c | 3 + src/network-state.c | 9 + src/vpnsvc-internal.c | 1065 +++++++++++++++++++++++++ src/vpnsvc.c | 386 +++++++++ 13 files changed, 1636 insertions(+), 2 deletions(-) create mode 100755 include/vpnsvc-internal.h create mode 100755 include/vpnsvc.h mode change 100644 => 100755 interfaces/netconfig-iface-network-state.xml create mode 100755 interfaces/netconfig-iface-vpnsvc.xml create mode 100755 src/vpnsvc-internal.c create mode 100755 src/vpnsvc.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 237f2b5..256ddf8 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/include/netdbus.h b/include/netdbus.h index fb55c8f..fe51502 100755 --- a/include/netdbus.h +++ b/include/netdbus.h @@ -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 index 0000000..ce68b92 --- /dev/null +++ b/include/vpnsvc-internal.h @@ -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 + +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 index 0000000..9225af4 --- /dev/null +++ b/include/vpnsvc.h @@ -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 +#include +#include + +#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__ */ + diff --git a/interfaces/netconfig-iface-network-state.xml b/interfaces/netconfig-iface-network-state.xml old mode 100644 new mode 100755 index 698f35b..baa583a --- a/interfaces/netconfig-iface-network-state.xml +++ b/interfaces/netconfig-iface-network-state.xml @@ -22,6 +22,7 @@ + diff --git a/interfaces/netconfig-iface-vpnsvc.xml b/interfaces/netconfig-iface-vpnsvc.xml new file mode 100755 index 0000000..ee76ba7 --- /dev/null +++ b/interfaces/netconfig-iface-vpnsvc.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packaging/net-config.spec b/packaging/net-config.spec index 2b7b7e2..d7c1f4b 100755 --- a/packaging/net-config.spec +++ b/packaging/net-config.spec @@ -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) diff --git a/resources/etc/dbus-1/system.d/net-config.conf b/resources/etc/dbus-1/system.d/net-config.conf index ad238e8..35030f6 100644 --- a/resources/etc/dbus-1/system.d/net-config.conf +++ b/resources/etc/dbus-1/system.d/net-config.conf @@ -11,6 +11,7 @@ + @@ -56,5 +57,13 @@ + + + + + + + + diff --git a/src/dbus/netdbus.c b/src/dbus/netdbus.c index e9f6dfe..7e78176 100755 --- a/src/dbus/netdbus.c +++ b/src/dbus/netdbus.c @@ -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, diff --git a/src/main.c b/src/main.c index a4605ed..9a7a833 100755 --- a/src/main.c +++ b/src/main.c @@ -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[]) diff --git a/src/network-state.c b/src/network-state.c index d6db4cf..0e438fd 100755 --- a/src/network-state.c +++ b/src/network-state.c @@ -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 index 0000000..a3b4132 --- /dev/null +++ b/src/vpnsvc-internal.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 --dport 53 -j DNAT --to */ +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 index 0000000..f83bd2e --- /dev/null +++ b/src/vpnsvc.c @@ -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 +#include +#include +#include + +#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); +} + -- 2.7.4