/* * * ConnMan VPN daemon * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #ifndef IFF_LOWER_UP #define IFF_LOWER_UP 0x10000 #endif #include #include "../src/connman.h" #include "vpn.h" struct vpn_ipconfig { int refcount; int index; int family; bool enabled; struct connman_ipaddress *address; struct connman_ipaddress *system; }; struct vpn_ipdevice { int index; char *ifname; unsigned short type; unsigned int flags; char *address; uint16_t mtu; GSList *address_list; char *ipv4_gateway; char *ipv6_gateway; char *pac; }; static GHashTable *ipdevice_hash = NULL; struct connman_ipaddress * __vpn_ipconfig_get_address(struct vpn_ipconfig *ipconfig) { if (!ipconfig) return NULL; return ipconfig->address; } const char *__vpn_ipconfig_get_peer(struct vpn_ipconfig *ipconfig) { if (!ipconfig->address) return NULL; return ipconfig->address->peer; } unsigned short __vpn_ipconfig_get_type_from_index(int index) { struct vpn_ipdevice *ipdevice; ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index)); if (!ipdevice) return ARPHRD_VOID; return ipdevice->type; } unsigned int __vpn_ipconfig_get_flags_from_index(int index) { struct vpn_ipdevice *ipdevice; ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index)); if (!ipdevice) return 0; return ipdevice->flags; } void __vpn_ipconfig_foreach(void (*function) (int index, void *user_data), void *user_data) { GList *list, *keys; keys = g_hash_table_get_keys(ipdevice_hash); if (!keys) return; for (list = g_list_first(keys); list; list = g_list_next(list)) { int index = GPOINTER_TO_INT(list->data); function(index, user_data); } g_list_free(keys); } void __vpn_ipconfig_set_local(struct vpn_ipconfig *ipconfig, const char *address) { if (!ipconfig->address) return; g_free(ipconfig->address->local); ipconfig->address->local = g_strdup(address); } const char *__vpn_ipconfig_get_local(struct vpn_ipconfig *ipconfig) { if (!ipconfig->address) return NULL; return ipconfig->address->local; } void __vpn_ipconfig_set_peer(struct vpn_ipconfig *ipconfig, const char *address) { if (!ipconfig->address) return; g_free(ipconfig->address->peer); ipconfig->address->peer = g_strdup(address); } void __vpn_ipconfig_set_broadcast(struct vpn_ipconfig *ipconfig, const char *broadcast) { if (!ipconfig->address) return; g_free(ipconfig->address->broadcast); ipconfig->address->broadcast = g_strdup(broadcast); } void __vpn_ipconfig_set_gateway(struct vpn_ipconfig *ipconfig, const char *gateway) { DBG(""); if (!ipconfig->address) return; g_free(ipconfig->address->gateway); ipconfig->address->gateway = g_strdup(gateway); } const char * __vpn_ipconfig_get_gateway(struct vpn_ipconfig *ipconfig) { if (!ipconfig->address) return NULL; return ipconfig->address->gateway; } void __vpn_ipconfig_set_prefixlen(struct vpn_ipconfig *ipconfig, unsigned char prefixlen) { if (!ipconfig->address) return; ipconfig->address->prefixlen = prefixlen; } unsigned char __vpn_ipconfig_get_prefixlen(struct vpn_ipconfig *ipconfig) { if (!ipconfig->address) return 0; return ipconfig->address->prefixlen; } int __vpn_ipconfig_address_add(struct vpn_ipconfig *ipconfig, int family) { DBG("ipconfig %p family %d", ipconfig, family); if (!ipconfig) return -EINVAL; if (family == AF_INET) return connman_inet_set_address(ipconfig->index, ipconfig->address); else if (family == AF_INET6) return connman_inet_set_ipv6_address(ipconfig->index, ipconfig->address); return 0; } int __vpn_ipconfig_gateway_add(struct vpn_ipconfig *ipconfig, int family) { DBG("ipconfig %p family %d", ipconfig, family); if (!ipconfig || !ipconfig->address) return -EINVAL; DBG("family %d gw %s peer %s", family, ipconfig->address->gateway, ipconfig->address->peer); if (family == AF_INET) connman_inet_add_host_route(ipconfig->index, ipconfig->address->gateway, ipconfig->address->peer); else if (family == AF_INET6) connman_inet_add_ipv6_host_route(ipconfig->index, ipconfig->address->gateway, ipconfig->address->peer); else return -EINVAL; return 0; } void __vpn_ipconfig_unref_debug(struct vpn_ipconfig *ipconfig, const char *file, int line, const char *caller) { if (!ipconfig) return; DBG("%p ref %d by %s:%d:%s()", ipconfig, ipconfig->refcount - 1, file, line, caller); if (__sync_fetch_and_sub(&ipconfig->refcount, 1) != 1) return; connman_ipaddress_free(ipconfig->system); connman_ipaddress_free(ipconfig->address); g_free(ipconfig); } static struct vpn_ipconfig *create_ipv6config(int index) { struct vpn_ipconfig *ipv6config; DBG("index %d", index); ipv6config = g_try_new0(struct vpn_ipconfig, 1); if (!ipv6config) return NULL; ipv6config->refcount = 1; ipv6config->index = index; ipv6config->enabled = false; ipv6config->family = AF_INET6; ipv6config->address = connman_ipaddress_alloc(AF_INET6); if (!ipv6config->address) { g_free(ipv6config); return NULL; } ipv6config->system = connman_ipaddress_alloc(AF_INET6); DBG("ipconfig %p", ipv6config); return ipv6config; } struct vpn_ipconfig *__vpn_ipconfig_create(int index, int family) { struct vpn_ipconfig *ipconfig; if (family == AF_INET6) return create_ipv6config(index); DBG("index %d", index); ipconfig = g_try_new0(struct vpn_ipconfig, 1); if (!ipconfig) return NULL; ipconfig->refcount = 1; ipconfig->index = index; ipconfig->enabled = false; ipconfig->family = family; ipconfig->address = connman_ipaddress_alloc(AF_INET); if (!ipconfig->address) { g_free(ipconfig); return NULL; } ipconfig->system = connman_ipaddress_alloc(AF_INET); DBG("ipconfig %p", ipconfig); return ipconfig; } void __vpn_ipconfig_set_index(struct vpn_ipconfig *ipconfig, int index) { ipconfig->index = index; } static const char *type2str(unsigned short type) { switch (type) { case ARPHRD_ETHER: return "ETHER"; case ARPHRD_LOOPBACK: return "LOOPBACK"; case ARPHRD_PPP: return "PPP"; case ARPHRD_NONE: return "NONE"; case ARPHRD_VOID: return "VOID"; } return ""; } void __vpn_ipconfig_newlink(int index, unsigned short type, unsigned int flags, const char *address, unsigned short mtu, struct rtnl_link_stats *stats) { struct vpn_ipdevice *ipdevice; GString *str; if (type == ARPHRD_LOOPBACK) return; ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index)); if (ipdevice) goto update; ipdevice = g_try_new0(struct vpn_ipdevice, 1); if (!ipdevice) return; ipdevice->index = index; ipdevice->ifname = connman_inet_ifname(index); ipdevice->type = type; ipdevice->address = g_strdup(address); g_hash_table_insert(ipdevice_hash, GINT_TO_POINTER(index), ipdevice); connman_info("%s {create} index %d type %d <%s>", ipdevice->ifname, index, type, type2str(type)); update: ipdevice->mtu = mtu; if (flags == ipdevice->flags) return; ipdevice->flags = flags; str = g_string_new(NULL); if (!str) return; if (flags & IFF_UP) g_string_append(str, "UP"); else g_string_append(str, "DOWN"); if (flags & IFF_RUNNING) g_string_append(str, ",RUNNING"); if (flags & IFF_LOWER_UP) g_string_append(str, ",LOWER_UP"); connman_info("%s {update} flags %u <%s>", ipdevice->ifname, flags, str->str); g_string_free(str, TRUE); } void __vpn_ipconfig_dellink(int index, struct rtnl_link_stats *stats) { struct vpn_ipdevice *ipdevice; DBG("index %d", index); ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index)); if (!ipdevice) return; g_hash_table_remove(ipdevice_hash, GINT_TO_POINTER(index)); } static void free_ipdevice(gpointer data) { struct vpn_ipdevice *ipdevice = data; connman_info("%s {remove} index %d", ipdevice->ifname, ipdevice->index); g_free(ipdevice->ipv4_gateway); g_free(ipdevice->ipv6_gateway); g_free(ipdevice->pac); g_free(ipdevice->address); g_free(ipdevice->ifname); g_free(ipdevice); } int __vpn_ipconfig_init(void) { DBG(""); ipdevice_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_ipdevice); return 0; } void __vpn_ipconfig_cleanup(void) { DBG(""); g_hash_table_destroy(ipdevice_hash); ipdevice_hash = NULL; }