blob: c096fa37b92da38ac62f5f71a833ac3a24f41a5e [file] [log] [blame]
/*
*
* 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 <config.h>
#endif
#include <errno.h>
#include <stdio.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <linux/if_link.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <arpa/inet.h>
#ifndef IFF_LOWER_UP
#define IFF_LOWER_UP 0x10000
#endif
#include <gdbus.h>
#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;
}