From d8185f8c6a8f517ee6bb7ca2178fe3aac2b9cf97 Mon Sep 17 00:00:00 2001
From: Martin Xu <martin.xu@intel.com>
Date: Tue, 1 Jun 2010 15:30:08 +0800
Subject: [PATCH] initial commit of static ipv6 support

---
 doc/service-api.txt   |   25 ++++
 include/element.h     |   11 ++
 include/ipconfig.h    |   12 ++-
 include/property.h    |    1 +
 src/connection.c      |   23 +++-
 src/connman.h         |   13 ++-
 src/element.c         |    7 +
 src/inet.c            |  309 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/ipconfig.c        |  265 ++++++++++++++++++++++++++++++++++++++++--
 src/ipv4.c            |   13 ++-
 src/network.c         |   57 +++++++++-
 src/service.c         |  120 ++++++++++++++++++--
 test/set-ipv6-address |   29 +++++
 13 files changed, 852 insertions(+), 33 deletions(-)
 create mode 100755 test/set-ipv6-address

diff --git a/doc/service-api.txt b/doc/service-api.txt
index a3e3c10..ee7dce9 100644
--- a/doc/service-api.txt
+++ b/doc/service-api.txt
@@ -353,6 +353,31 @@ Properties	string State [readonly]
 			until the new configuration has been successfully
 			installed.
 
+		dict IPv6 [readonly]
+
+			string Method [readonly]
+
+				Possible values are "manual" and "off".
+
+			string Address [readonly]
+
+				The current configured IPv6 address and prefix.
+
+			string Gateway [readonly]
+
+				The current configured IPv6 gateway.
+
+		dict IPv6.Configuration [readwrite]
+
+			Same values as IPv6 property. The IPv6 represents
+			the actual system configuration while this allows
+			user configuration.
+
+			Changing these settings will cause a state change
+			of the service. The service will become unavailable
+			until the new configuration has been successfully
+			installed.
+
 		dict Proxy [readonly]
 
 			string Method [readonly]
diff --git a/include/element.h b/include/element.h
index eab567e..10b5c0b 100644
--- a/include/element.h
+++ b/include/element.h
@@ -109,6 +109,17 @@ struct connman_element {
 		gchar *timeserver;
 		gchar *pac;
 	} ipv4;
+
+	struct {
+		enum connman_ipconfig_method method;
+		gchar *address;
+		gchar *gateway;
+		gchar *network;
+		gchar *broadcast;
+		gchar *nameserver;
+		gchar *timeserver;
+		gchar *pac;
+	} ipv6;
 };
 
 struct connman_element *connman_element_create(const char *name);
diff --git a/include/ipconfig.h b/include/ipconfig.h
index 0b70252..aa157b8 100644
--- a/include/ipconfig.h
+++ b/include/ipconfig.h
@@ -86,9 +86,19 @@ const char *connman_ipconfig_get_ifname(struct connman_ipconfig *ipconfig);
 
 void connman_ipconfig_set_ops(struct connman_ipconfig *ipconfig,
 				const struct connman_ipconfig_ops *ops);
-
+struct connman_ipconfig *connman_ipconfig_get_ipv6config(
+				struct connman_ipconfig *ipconfig);
 int connman_ipconfig_set_method(struct connman_ipconfig *ipconfig,
 					enum connman_ipconfig_method method);
+int connman_inet_set_ipv6_address(int index,
+		struct connman_ipaddress *ipaddress);
+int connman_inet_clear_ipv6_address(int index,
+		const char *address, int prefix_len);
+int connman_inet_add_ipv6_host_route(int index, const char *host);
+int connman_inet_del_ipv6_host_route(int index, const char *host);
+
+int connman_inet_set_ipv6_gateway_address(int index, const char *gateway);
+int connman_inet_clear_ipv6_gateway_address(int index, const char *gateway);
 
 void connman_ipconfig_bind(struct connman_ipconfig *ipconfig,
 					struct connman_ipaddress *ipaddress);
diff --git a/include/property.h b/include/property.h
index c7f2a7b..bb71d2b 100644
--- a/include/property.h
+++ b/include/property.h
@@ -48,6 +48,7 @@ enum connman_property_id {
 	CONNMAN_PROPERTY_ID_IPV4_NAMESERVER,
 	CONNMAN_PROPERTY_ID_IPV4_TIMESERVER,
 	CONNMAN_PROPERTY_ID_IPV4_PAC,
+	CONNMAN_PROPERTY_ID_IPV6_GATEWAY,
 };
 
 enum connman_property_type {
diff --git a/src/connection.c b/src/connection.c
index 154076b..c333473 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -32,6 +32,7 @@
 struct gateway_data {
 	int index;
 	char *gateway;
+	char *ipv6_gateway;
 	struct connman_element *element;
 	unsigned int order;
 	gboolean active;
@@ -75,6 +76,10 @@ static int del_routes(struct gateway_data *data)
 	} else if (g_strcmp0(data->gateway, "0.0.0.0") == 0) {
 		return connman_inet_clear_gateway_interface(data->index);
 	} else {
+		connman_inet_del_ipv6_host_route(data->index,
+						data->ipv6_gateway);
+		connman_inet_clear_ipv6_gateway_address(data->index,
+						data->ipv6_gateway);
 		connman_inet_del_host_route(data->index, data->gateway);
 		return connman_inet_clear_gateway_address(data->index,
 							data->gateway);
@@ -96,7 +101,8 @@ static void find_element(struct connman_element *element, gpointer user_data)
 	data->element = element;
 }
 
-static struct gateway_data *add_gateway(int index, const char *gateway)
+static struct gateway_data *add_gateway(int index, const char *gateway,
+						const char *ipv6_gateway)
 {
 	struct gateway_data *data;
 	struct connman_service *service;
@@ -107,6 +113,7 @@ static struct gateway_data *add_gateway(int index, const char *gateway)
 
 	data->index = index;
 	data->gateway = g_strdup(gateway);
+	data->ipv6_gateway = g_strdup(ipv6_gateway);
 	data->active = FALSE;
 	data->element = NULL;
 	data->vpn_ip = NULL;
@@ -157,8 +164,12 @@ static void set_default_gateway(struct gateway_data *data)
 		goto done;
 	}
 
-	connman_inet_add_host_route(element->index, data->gateway);
+	connman_inet_add_ipv6_host_route(element->index,
+						data->ipv6_gateway);
+	connman_inet_set_ipv6_gateway_address(element->index,
+						data->ipv6_gateway);
 
+	connman_inet_add_host_route(element->index, data->gateway);
 	if (connman_inet_set_gateway_address(element->index, data->gateway) < 0)
 		return;
 
@@ -245,7 +256,7 @@ static struct gateway_data *find_active_gateway(void)
 static int connection_probe(struct connman_element *element)
 {
 	struct connman_service *service = NULL;
-	const char *gateway = NULL;
+	const char *gateway = NULL, *ipv6_gateway = NULL;
 	const char *vpn_ip = NULL;
 	struct gateway_data *active_gateway = NULL;
 	struct gateway_data *new_gateway = NULL;
@@ -261,11 +272,13 @@ static int connection_probe(struct connman_element *element)
 
 	connman_element_get_value(element,
 				CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
+	connman_element_get_value(element,
+			CONNMAN_PROPERTY_ID_IPV6_GATEWAY, &ipv6_gateway);
 
 	connman_element_get_value(element,
 				  CONNMAN_PROPERTY_ID_IPV4_ADDRESS, &vpn_ip);
 
-	DBG("gateway %s", gateway);
+	DBG("gateway %s, ipv6_gateway %s", gateway, ipv6_gateway);
 
 	/*
 	 * If gateway is NULL, it's a point to point link and the default
@@ -283,7 +296,7 @@ static int connection_probe(struct connman_element *element)
 	connman_element_set_enabled(element, TRUE);
 
 	active_gateway = find_active_gateway();
-	new_gateway = add_gateway(element->index, gateway);
+	new_gateway = add_gateway(element->index, gateway, ipv6_gateway);
 
 	if (service == NULL) {
 		new_gateway->vpn = TRUE;
diff --git a/src/connman.h b/src/connman.h
index 2359620..2b9323a 100644
--- a/src/connman.h
+++ b/src/connman.h
@@ -233,14 +233,23 @@ void __connman_ipconfig_append_ipv4(struct connman_ipconfig *ipconfig,
 							DBusMessageIter *iter);
 void __connman_ipconfig_append_ipv4config(struct connman_ipconfig *ipconfig,
 							DBusMessageIter *iter);
-int __connman_ipconfig_set_ipv4config(struct connman_ipconfig *ipconfig,
-							DBusMessageIter *value);
+void __connman_ipconfig_append_ipv6(struct connman_ipconfig *ipconfig,
+							DBusMessageIter *iter);
+void __connman_ipconfig_append_ipv6config(struct connman_ipconfig *ipconfig,
+							DBusMessageIter *iter);
+int __connman_ipconfig_set_config(struct connman_ipconfig *ipconfig,
+		enum connman_ipconfig_type type, DBusMessageIter *array);
 void __connman_ipconfig_append_proxy(struct connman_ipconfig *ipconfig,
 							DBusMessageIter *iter);
 void __connman_ipconfig_append_ethernet(struct connman_ipconfig *ipconfig,
 							DBusMessageIter *iter);
 enum connman_ipconfig_method __connman_ipconfig_get_method(
 				struct connman_ipconfig *ipconfig);
+
+void __connman_ipconfig_set_element_ipv6_gateway(
+			struct connman_ipconfig *ipconfig,
+				struct connman_element *element);
+
 int __connman_ipconfig_set_gateway(struct connman_ipconfig *ipconfig,
 					struct connman_element *parent);
 int __connman_ipconfig_set_address(struct connman_ipconfig *ipconfig);
diff --git a/src/element.c b/src/element.c
index 27d078f..5bc11f2 100644
--- a/src/element.c
+++ b/src/element.c
@@ -784,6 +784,13 @@ int connman_element_get_value(struct connman_element *element,
 								id, value);
 		*((char **) value) = element->ipv4.pac;
 		break;
+	case CONNMAN_PROPERTY_ID_IPV6_GATEWAY:
+		if (element->ipv6.gateway == NULL)
+			return connman_element_get_value(element->parent,
+								id, value);
+		*((char **) value) = element->ipv6.gateway;
+		break;
+
 	default:
 		return -EINVAL;
 	}
diff --git a/src/inet.c b/src/inet.c
index 980bd48..741c21a 100644
--- a/src/inet.c
+++ b/src/inet.c
@@ -508,6 +508,64 @@ done:
 	return device;
 }
 
+struct in6_ifreq {
+	struct in6_addr ifr6_addr;
+	__u32 ifr6_prefixlen;
+	unsigned int ifr6_ifindex;
+};
+
+int connman_inet_set_ipv6_address(int index,
+		struct connman_ipaddress *ipaddress)
+{
+	int sk, ret;
+	struct in6_ifreq ifr6;
+
+	DBG("index %d ipaddress->local %s", index, ipaddress->local);
+
+	if (ipaddress->local == NULL)
+		return 0;
+
+	/* Check IPV6 address type */
+	if (strchr(ipaddress->local, ':') == NULL) {
+		ret = -1;
+		goto out;
+	}
+
+	if (ipaddress->prefixlen < 0 || ipaddress->prefixlen > 128) {
+		ret = -1;
+		goto out;
+	}
+
+	sk = socket(PF_INET6, SOCK_DGRAM, 0);
+	if (sk < 0) {
+		ret = -1;
+		goto out;
+	}
+
+	memset(&ifr6, 0, sizeof(ifr6));
+
+	if (inet_pton(AF_INET6, ipaddress->local, &ifr6.ifr6_addr) <= 0) {
+		ret = -1;
+		goto close;
+	}
+
+	ifr6.ifr6_ifindex = index;
+	ifr6.ifr6_prefixlen = ipaddress->prefixlen;
+
+	ret = ioctl(sk, SIOCSIFADDR, &ifr6);
+	if (ret < 0)
+		goto close;
+
+	ret = 0;
+close:
+	close(sk);
+out:
+	if (ret < 0)
+		perror("Set IPV6 address error");
+
+	return ret;
+}
+
 int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress)
 {
 	struct ifreq ifr;
@@ -574,8 +632,59 @@ int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress)
 	return 0;
 }
 
+int connman_inet_clear_ipv6_address(int index, const char *address,
+							int prefix_len)
+{
+	struct in6_ifreq ifr6;
+	int sk, ret;
+
+	DBG("index %d address %s prefix_len %d", index, address, prefix_len);
+
+	/* Check IPV6 address type */
+	if (strchr(address, ':') == NULL) {
+		ret = -1;
+		goto out;
+	}
+
+	if (prefix_len < 0 || prefix_len > 128) {
+		ret = -1;
+		goto out;
+	}
+
+	memset(&ifr6, 0, sizeof(ifr6));
+
+	if (inet_pton(AF_INET6, address, &ifr6.ifr6_addr) <= 0) {
+		ret = -1;
+		goto out;
+	}
+
+	ifr6.ifr6_ifindex = index;
+	ifr6.ifr6_prefixlen = prefix_len;
+
+	sk = socket(PF_INET6, SOCK_DGRAM, 0);
+	if (sk < 0) {
+		ret = -1;
+		goto out;
+	}
+
+	ret = ioctl(sk, SIOCDIFADDR, &ifr6);
+	if (ret < 0)
+		goto close;
+
+	ret = 0;
+close:
+	close(sk);
+out:
+	if (ret < 0)
+		perror("Clear IPV6 address error");
+
+	return ret;
+}
+
 int connman_inet_clear_address(int index)
 {
+
+
 	struct ifreq ifr;
 	struct sockaddr_in addr;
 	int sk, err;
@@ -755,6 +864,206 @@ int connman_inet_del_host_route(int index, const char *host)
 	return err;
 }
 
+int connman_inet_add_ipv6_host_route(int index, const char *host)
+{
+	struct in6_rtmsg rt;
+	int sk, ret;
+	char *c, *address = g_strdup(host);
+
+	DBG("index %d host %s", index, host);
+
+	if (host == NULL)
+		return 0;
+
+	c = strchr(address, '/');
+	if (c != NULL)
+		*c = 0;
+
+	memset(&rt, 0, sizeof(rt));
+
+	rt.rtmsg_dst_len = 128;
+
+	ret = inet_pton(AF_INET6, address, &rt.rtmsg_dst);
+	if (ret < 0)
+		goto out;
+
+	rt.rtmsg_flags = RTF_UP | RTF_HOST;
+	rt.rtmsg_metric = 1;
+	rt.rtmsg_ifindex = index;
+
+	sk = socket(AF_INET6, SOCK_DGRAM, 0);
+	if (sk < 0) {
+		ret = -1;
+		goto out;
+	}
+
+	ret = ioctl(sk, SIOCADDRT, &rt);
+	if (ret < 0)
+		goto close;
+close:
+	close(sk);
+out:
+	if (ret < 0)
+		perror("set host route error");
+	g_free(address);
+	return ret;
+}
+
+int connman_inet_del_ipv6_host_route(int index, const char *host)
+{
+	struct in6_rtmsg rt;
+	int sk, ret;
+	char *c, *address = g_strdup(host);
+
+	DBG("index %d host %s", index, host);
+
+	if (host == NULL)
+		return 0;
+
+	c = strchr(address, '/');
+	if (c != NULL)
+		*c = 0;
+
+	memset(&rt, 0, sizeof(rt));
+
+	rt.rtmsg_dst_len = 128;
+
+	ret = inet_pton(AF_INET6, address, &rt.rtmsg_dst);
+	if (ret < 0)
+		goto out;
+
+	rt.rtmsg_flags = RTF_UP | RTF_HOST;
+	rt.rtmsg_metric = 1;
+	rt.rtmsg_ifindex = index;
+
+	sk = socket(AF_INET6, SOCK_DGRAM, 0);
+	if (sk < 0) {
+		ret = -1;
+		goto out;
+	}
+
+	ret = ioctl(sk, SIOCDELRT, &rt);
+	if (ret < 0)
+		goto close;
+close:
+	close(sk);
+out:
+	if (ret < 0)
+		perror("Del host route error");
+	g_free(address);
+	return ret;
+}
+
+int connman_inet_set_ipv6_gateway_address(int index, const char *gateway)
+{
+	struct in6_rtmsg rt;
+	int sk, ret, prefix_len;
+	char *c, *address = g_strdup(gateway);
+
+	DBG("index %d gateway %s", index, gateway);
+
+	if (gateway == NULL)
+		return 0;
+
+
+	c = strchr(address, '/');
+	if (c == NULL) {
+		ret = -1;
+		goto out;
+	}
+
+	*c = 0;
+
+	prefix_len = atol(c + 1);
+	if (prefix_len < 0 || prefix_len > 128) {
+		ret = -1;
+		goto out;
+	}
+
+	memset(&rt, 0, sizeof(rt));
+
+	ret = inet_pton(AF_INET6, address, &rt.rtmsg_gateway);
+	if (ret < 0)
+		goto out;
+
+	rt.rtmsg_flags = RTF_UP;
+	rt.rtmsg_metric = 1;
+	rt.rtmsg_dst_len = prefix_len;
+	rt.rtmsg_ifindex = index;
+
+	sk = socket(AF_INET6, SOCK_DGRAM, 0);
+	if (sk < 0) {
+		ret = -1;
+		goto out;
+	}
+
+	ret = ioctl(sk, SIOCADDRT, &rt);
+	if (ret < 0)
+		goto close;
+close:
+	close(sk);
+out:
+	if (ret < 0)
+		perror("set host route error");
+
+	g_free(address);
+	return ret;
+}
+
+int connman_inet_clear_ipv6_gateway_address(int index, const char *gateway)
+{
+	struct in6_rtmsg rt;
+	int sk, ret, prefix_len;
+	char *c, *address = g_strdup(gateway);
+
+	DBG("index %d gateway %s", index, gateway);
+
+	if (gateway == NULL)
+		return 0;
+
+	c = strchr(address, '/');
+	if (c == NULL) {
+		ret = -1;
+		goto out;
+	}
+
+	*c = 0;
+
+	prefix_len = atol(c + 1);
+	if (prefix_len < 0 || prefix_len > 128) {
+		ret = -1;
+		goto out;
+	}
+
+	memset(&rt, 0, sizeof(rt));
+
+	ret = inet_pton(AF_INET6, address, &rt.rtmsg_gateway);
+	if (ret < 0)
+		goto out;
+
+	rt.rtmsg_flags = RTF_UP;
+	rt.rtmsg_metric = 1;
+	rt.rtmsg_dst_len = prefix_len;
+	rt.rtmsg_ifindex = index;
+
+	sk = socket(AF_INET6, SOCK_DGRAM, 0);
+	if (sk < 0) {
+		ret = -1;
+		goto out;
+	}
+
+	ret = ioctl(sk, SIOCDELRT, &rt);
+	if (ret < 0)
+		goto close;
+close:
+	close(sk);
+out:
+	if (ret < 0)
+		perror("Clear gateway error");
+	g_free(address);
+	return ret;
+}
+
 int connman_inet_set_gateway_address(int index, const char *gateway)
 {
 	struct ifreq ifr;
diff --git a/src/ipconfig.c b/src/ipconfig.c
index 51c4619..01ba258 100644
--- a/src/ipconfig.c
+++ b/src/ipconfig.c
@@ -26,6 +26,8 @@
 #include <net/if.h>
 #include <net/if_arp.h>
 #include <linux/if_link.h>
+#include <string.h>
+#include <stdlib.h>
 
 #ifndef IFF_LOWER_UP
 #define IFF_LOWER_UP	0x10000
@@ -34,10 +36,31 @@
 #include <gdbus.h>
 
 #include "connman.h"
+#if 0
+struct _ipconfig {
+	enum connman_ipconfig_type type;
+
+	const struct connman_ipconfig_ops *ops;
+	void *ops_data;
+
+	enum connman_ipconfig_method method;
+
+	struct connman_ipaddress *address;
+	struct connman_ipaddress *system;
+};
 
 struct connman_ipconfig {
 	gint refcount;
 	int index;
+	struct _ipconfig ipv4;
+	struct _ipconfig ipv6;
+}
+#endif
+
+struct connman_ipconfig {
+	gint refcount;
+	int index;
+	enum connman_ipconfig_type type;
 
 	struct connman_ipconfig *origin;
 
@@ -47,6 +70,8 @@ struct connman_ipconfig {
 	enum connman_ipconfig_method method;
 	struct connman_ipaddress *address;
 	struct connman_ipaddress *system;
+
+	struct connman_ipconfig *ipv6;
 };
 
 struct connman_ipdevice {
@@ -79,6 +104,12 @@ struct connman_ipaddress *connman_ipaddress_alloc(void)
 	if (ipaddress == NULL)
 		return NULL;
 
+	ipaddress->prefixlen = 0;
+	ipaddress->local = NULL;
+	ipaddress->peer = NULL;
+	ipaddress->broadcast = NULL;
+	ipaddress->gateway = NULL;
+
 	return ipaddress;
 }
 
@@ -110,7 +141,63 @@ static unsigned char netmask2prefixlen(const char *netmask)
 	return bits;
 }
 
-void connman_ipaddress_set(struct connman_ipaddress *ipaddress,
+static gboolean check_ipv6_address(const char *address)
+{
+	char *c;
+
+	if (address == NULL)
+		return FALSE;
+
+	c = strchr(address, ':');
+
+	if (c == NULL)
+		return FALSE;
+
+	return TRUE;
+}
+
+int connman_ipaddress_set_ipv6(struct connman_ipaddress *ipaddress,
+				const char *address, const char *gateway)
+{
+	char *prefix;
+	unsigned char prefix_len;
+
+	if (ipaddress == NULL)
+		return -EINVAL;
+
+	if (check_ipv6_address(address) == FALSE)
+		return -EINVAL;
+
+	if (check_ipv6_address(gateway) == FALSE)
+		return -EINVAL;
+
+	prefix = strchr(address, '/');
+	if (prefix == NULL)
+		return -EINVAL;
+
+	prefix_len = atol(prefix + 1);
+	if ((prefix_len < 0) || (prefix_len > 128))
+		return -EINVAL;
+
+	*prefix = 0;
+
+	DBG("prefix_len %d address %s gateway %s",
+			prefix_len, address, gateway);
+
+	ipaddress->prefixlen = prefix_len;
+
+//	ipaddress->type = CONNMAN_IPCONFIG_TYPE_IPV6;
+
+	g_free(ipaddress->local);
+	ipaddress->local = g_strdup(address);
+
+	g_free(ipaddress->gateway);
+	ipaddress->gateway = g_strdup(gateway);
+
+	return 0;
+}
+
+void connman_ipaddress_set_ipv4(struct connman_ipaddress *ipaddress,
 		const char *address, const char *netmask, const char *gateway)
 {
 	if (ipaddress == NULL)
@@ -121,6 +208,8 @@ void connman_ipaddress_set(struct connman_ipaddress *ipaddress,
 	else
 		ipaddress->prefixlen = 32;
 
+//	ipaddress->type = CONNMAN_IPCONFIG_TYPE_IPV4;
+
 	g_free(ipaddress->local);
 	ipaddress->local = g_strdup(address);
 
@@ -348,6 +437,9 @@ static void __connman_ipconfig_lower_down(struct connman_ipdevice *ipdevice)
 	ipdevice->driver_config = NULL;
 
 	connman_inet_clear_address(ipdevice->index);
+	connman_inet_clear_ipv6_address(ipdevice->index,
+			ipdevice->driver_config->address->local,
+			ipdevice->driver_config->address->prefixlen);
 }
 
 static void update_stats(struct connman_ipdevice *ipdevice,
@@ -737,6 +829,37 @@ const char *__connman_ipconfig_get_gateway(int index)
 void __connman_ipconfig_set_index(struct connman_ipconfig *ipconfig, int index)
 {
 	ipconfig->index = index;
+
+	if (ipconfig->ipv6 != NULL)
+		ipconfig->ipv6->index = index;
+}
+
+static struct connman_ipconfig *create_ipv6config(int index)
+{
+	struct connman_ipconfig *ipv6config;
+
+	DBG("index %d", index);
+
+	ipv6config = g_try_new0(struct connman_ipconfig, 1);
+	if (ipv6config == NULL)
+		return NULL;
+
+	ipv6config->index = index;
+	ipv6config->type = CONNMAN_IPCONFIG_TYPE_IPV6;
+
+	ipv6config->address = connman_ipaddress_alloc();
+	if (ipv6config->address == NULL) {
+		g_free(ipv6config);
+		return NULL;
+	}
+
+	ipv6config->system = connman_ipaddress_alloc();
+
+	ipv6config->ipv6 = NULL;
+
+	DBG("ipconfig %p", ipv6config);
+
+	return ipv6config;
 }
 
 /**
@@ -759,6 +882,7 @@ struct connman_ipconfig *connman_ipconfig_create(int index)
 	ipconfig->refcount = 1;
 
 	ipconfig->index = index;
+	ipconfig->type = CONNMAN_IPCONFIG_TYPE_IPV4;
 
 	ipconfig->address = connman_ipaddress_alloc();
 	if (ipconfig->address == NULL) {
@@ -768,6 +892,8 @@ struct connman_ipconfig *connman_ipconfig_create(int index)
 
 	ipconfig->system = connman_ipaddress_alloc();
 
+	ipconfig->ipv6 = create_ipv6config(index);
+
 	DBG("ipconfig %p", ipconfig);
 
 	return ipconfig;
@@ -812,6 +938,17 @@ struct connman_ipconfig *connman_ipconfig_ref(struct connman_ipconfig *ipconfig)
 	return ipconfig;
 }
 
+static void  free_ipv6config(struct connman_ipconfig *ipconfig)
+{
+	if (ipconfig == NULL)
+		return;
+
+	connman_ipconfig_set_ops(ipconfig, NULL);
+	connman_ipaddress_free(ipconfig->system);
+	connman_ipaddress_free(ipconfig->address);
+	g_free(ipconfig->ipv6);
+}
+
 /**
  * connman_ipconfig_unref:
  * @ipconfig: ipconfig structure
@@ -832,6 +969,7 @@ void connman_ipconfig_unref(struct connman_ipconfig *ipconfig)
 
 		connman_ipaddress_free(ipconfig->system);
 		connman_ipaddress_free(ipconfig->address);
+		free_ipv6config(ipconfig->ipv6);
 		g_free(ipconfig);
 	}
 }
@@ -913,6 +1051,15 @@ void connman_ipconfig_set_ops(struct connman_ipconfig *ipconfig,
 	ipconfig->ops = ops;
 }
 
+struct connman_ipconfig *connman_ipconfig_get_ipv6config(
+				struct connman_ipconfig *ipconfig)
+{
+	if (ipconfig == NULL)
+		return NULL;
+
+	return ipconfig->ipv6;
+}
+
 /**
  * connman_ipconfig_set_method:
  * @ipconfig: ipconfig structure
@@ -955,7 +1102,17 @@ void connman_ipconfig_bind(struct connman_ipconfig *ipconfig,
 	connman_inet_set_address(origin->index, origin->address);
 }
 
-/* FIXME: The element soulution should be removed in the future */
+void __connman_ipconfig_set_element_ipv6_gateway(
+			struct connman_ipconfig *ipconfig,
+				struct connman_element *element)
+{
+	element->ipv6.gateway = ipconfig->ipv6->address->gateway;
+}
+
+/*
+ * FIXME: The element soulution should be removed in the future
+ * Set IPv4 and IPv6 gateway
+ */
 int __connman_ipconfig_set_gateway(struct connman_ipconfig *ipconfig,
 						struct connman_element *parent)
 {
@@ -963,9 +1120,12 @@ int __connman_ipconfig_set_gateway(struct connman_ipconfig *ipconfig,
 
 	connection = connman_element_create(NULL);
 
+	DBG("ipconfig %p", ipconfig);
+
 	connection->type  = CONNMAN_ELEMENT_TYPE_CONNECTION;
 	connection->index = ipconfig->index;
 	connection->ipv4.gateway = ipconfig->address->gateway;
+	connection->ipv6.gateway = ipconfig->ipv6->address->gateway;
 
 	if (connman_element_register(connection, parent) < 0)
 		connman_element_unref(connection);
@@ -984,8 +1144,12 @@ int __connman_ipconfig_set_address(struct connman_ipconfig *ipconfig)
 	case CONNMAN_IPCONFIG_METHOD_DHCP:
 		break;
 	case CONNMAN_IPCONFIG_METHOD_MANUAL:
-		return connman_inet_set_address(ipconfig->index,
-						ipconfig->address);
+		if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV4)
+			return connman_inet_set_address(ipconfig->index,
+							ipconfig->address);
+		else if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV6)
+			return connman_inet_set_ipv6_address(
+					ipconfig->index, ipconfig->address);
 	}
 
 	return 0;
@@ -1007,11 +1171,16 @@ int __connman_ipconfig_clear_address(struct connman_ipconfig *ipconfig)
 	case CONNMAN_IPCONFIG_METHOD_DHCP:
 		break;
 	case CONNMAN_IPCONFIG_METHOD_MANUAL:
-		return connman_inet_clear_address(ipconfig->index);
+		if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV4)
+			return connman_inet_clear_address(ipconfig->index);
+		else if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV6)
+			return connman_inet_clear_ipv6_address(
+						ipconfig->index,
+						ipconfig->address->local,
+						ipconfig->address->prefixlen);
 	}
 
 	return 0;
-
 }
 
 int __connman_ipconfig_enable(struct connman_ipconfig *ipconfig)
@@ -1066,6 +1235,7 @@ int __connman_ipconfig_disable(struct connman_ipconfig *ipconfig)
 	ipconfig_list = g_list_remove(ipconfig_list, ipconfig);
 
 	connman_ipaddress_clear(ipdevice->config->system);
+	connman_ipaddress_clear(ipdevice->config->ipv6->system);
 
 	connman_ipconfig_unref(ipdevice->config);
 	ipdevice->config = NULL;
@@ -1139,6 +1309,70 @@ void __connman_ipconfig_append_ipv4(struct connman_ipconfig *ipconfig,
 				DBUS_TYPE_STRING, &ipconfig->system->gateway);
 }
 
+void __connman_ipconfig_append_ipv6(struct connman_ipconfig *ipconfig,
+							DBusMessageIter *iter)
+{
+	char *address;
+	const char *str;
+
+	str = __connman_ipconfig_method2string(ipconfig->method);
+	if (str == NULL)
+		return;
+
+	connman_dbus_dict_append_basic(iter, "Method", DBUS_TYPE_STRING, &str);
+
+	if (ipconfig->address == NULL)
+		return;
+
+	address = g_strdup_printf("%s/%d", ipconfig->address->local,
+					ipconfig->address->prefixlen);
+	if (ipconfig->address->local != NULL)
+		connman_dbus_dict_append_basic(iter, "Address",
+					DBUS_TYPE_STRING, &address);
+	g_free(address);
+
+	if (ipconfig->address->gateway != NULL)
+		connman_dbus_dict_append_basic(iter, "Gateway",
+				DBUS_TYPE_STRING, &ipconfig->address->gateway);
+}
+
+void __connman_ipconfig_append_ipv6config(struct connman_ipconfig *ipconfig,
+							DBusMessageIter *iter)
+{
+	char *address;
+	const char *str;
+
+	str = __connman_ipconfig_method2string(ipconfig->method);
+	if (str == NULL)
+		return;
+
+	connman_dbus_dict_append_basic(iter, "Method", DBUS_TYPE_STRING, &str);
+
+	switch (ipconfig->method) {
+	case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+	case CONNMAN_IPCONFIG_METHOD_OFF:
+	case CONNMAN_IPCONFIG_METHOD_FIXED:
+	case CONNMAN_IPCONFIG_METHOD_DHCP:
+		return;
+	case CONNMAN_IPCONFIG_METHOD_MANUAL:
+		break;
+	}
+
+	if (ipconfig->address == NULL)
+		return;
+
+	address = g_strdup_printf("%s/%d", ipconfig->address->local,
+					ipconfig->address->prefixlen);
+	if (ipconfig->address->local != NULL)
+		connman_dbus_dict_append_basic(iter, "Address",
+					DBUS_TYPE_STRING, &address);
+	g_free(address);
+
+	if (ipconfig->address->gateway != NULL)
+		connman_dbus_dict_append_basic(iter, "Gateway",
+				DBUS_TYPE_STRING, &ipconfig->address->gateway);
+}
+
 void __connman_ipconfig_append_ipv4config(struct connman_ipconfig *ipconfig,
 							DBusMessageIter *iter)
 {
@@ -1183,14 +1417,18 @@ void __connman_ipconfig_append_ipv4config(struct connman_ipconfig *ipconfig,
 				DBUS_TYPE_STRING, &ipconfig->address->gateway);
 }
 
-int __connman_ipconfig_set_ipv4config(struct connman_ipconfig *ipconfig,
-							DBusMessageIter *array)
+int __connman_ipconfig_set_config(struct connman_ipconfig *ipconfig,
+		enum connman_ipconfig_type type, DBusMessageIter *array)
 {
 	enum connman_ipconfig_method method = CONNMAN_IPCONFIG_METHOD_UNKNOWN;
 	const char *address = NULL, *netmask = NULL, *gateway = NULL;
 	DBusMessageIter dict;
 
-	DBG("ipconfig %p", ipconfig);
+	DBG("ipconfig %p type %d", ipconfig, type);
+
+	if (type != CONNMAN_IPCONFIG_TYPE_IPV4 &&
+			type != CONNMAN_IPCONFIG_TYPE_IPV6)
+		return -EINVAL;
 
 	if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
 		return -EINVAL;
@@ -1253,8 +1491,13 @@ int __connman_ipconfig_set_ipv4config(struct connman_ipconfig *ipconfig,
 			return -EINVAL;
 
 		ipconfig->method = method;
-		connman_ipaddress_set(ipconfig->address,
-				address, netmask, gateway);
+
+		if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+			connman_ipaddress_set_ipv4(ipconfig->address,
+						address, netmask, gateway);
+		else
+			return connman_ipaddress_set_ipv6(
+					ipconfig->address, address, gateway);
 		break;
 
 	case CONNMAN_IPCONFIG_METHOD_DHCP:
diff --git a/src/ipv4.c b/src/ipv4.c
index 4fc6a9f..1267e5a 100644
--- a/src/ipv4.c
+++ b/src/ipv4.c
@@ -164,6 +164,8 @@ static char *index2name(int index)
 
 static int ipv4_probe(struct connman_element *element)
 {
+	struct connman_service *service;
+	struct connman_ipconfig *ipconfig;
 	struct connman_element *connection;
 	struct connman_ipv4 ipv4;
 	const char *address = NULL, *netmask = NULL, *broadcast = NULL;
@@ -202,12 +204,10 @@ static int ipv4_probe(struct connman_element *element)
 
 	set_ipv4(element, &ipv4);
 
-	if (nameserver != NULL) {
-		struct connman_service *service;
+	service = __connman_element_get_service(element);
 
-		service = __connman_element_get_service(element);
+	if (nameserver != NULL)
 		__connman_service_append_nameserver(service, nameserver);
-	}
 
 	connman_timeserver_append(timeserver);
 
@@ -217,6 +217,11 @@ static int ipv4_probe(struct connman_element *element)
 	connection->index   = element->index;
 	connection->devname = index2name(element->index);
 
+	ipconfig = __connman_service_get_ipconfig(service);
+	if (ipconfig != NULL)
+		__connman_ipconfig_set_element_ipv6_gateway(
+						ipconfig, connection);
+
 	if (connman_element_register(connection, element) < 0)
 		connman_element_unref(connection);
 
diff --git a/src/network.c b/src/network.c
index c36298e..5bc31e9 100644
--- a/src/network.c
+++ b/src/network.c
@@ -845,6 +845,28 @@ static int set_connected_dhcp(struct connman_network *network)
 	return 0;
 }
 
+static int manual_ipv6_set(struct connman_network *network,
+				struct connman_ipconfig *ipconfig_ipv6)
+{
+	struct connman_service *service;
+	int err;
+
+	service = __connman_service_lookup_from_network(network);
+	if (service == NULL)
+		return -EINVAL;
+
+	err = __connman_ipconfig_set_address(ipconfig_ipv6);
+	if (err < 0) {
+		connman_network_set_error(network,
+			CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL);
+		return err;
+	}
+
+	/* READY state will be indicated by IPV4 setting */
+
+	return 0;
+}
+
 static gboolean set_connected(gpointer user_data)
 {
 	struct connman_network *network = user_data;
@@ -861,6 +883,23 @@ static gboolean set_connected(gpointer user_data)
 	DBG("method %d", method);
 
 	if (network->connected == TRUE) {
+		enum connman_ipconfig_method ipv6_method;
+		struct connman_ipconfig *ipv6config;
+		int ret;
+
+		ipv6config = connman_ipconfig_get_ipv6config(ipconfig);
+
+		/* Currently, only handle manual method of IPv6 */
+		ipv6_method = __connman_ipconfig_get_method(ipv6config);
+		if (ipv6_method == CONNMAN_IPCONFIG_METHOD_MANUAL) {
+			ret = manual_ipv6_set(network, ipv6config);
+			if (ret != 0) {
+				connman_network_set_error(network,
+					CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
+				return FALSE;
+			}
+		}
+
 		switch (method) {
 		case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
 		case CONNMAN_IPCONFIG_METHOD_OFF:
@@ -974,6 +1013,7 @@ connman_bool_t connman_network_get_associating(struct connman_network *network)
  */
 int __connman_network_connect(struct connman_network *network)
 {
+	struct connman_service *service;
 	int err;
 
 	DBG("network %p", network);
@@ -994,6 +1034,8 @@ int __connman_network_connect(struct connman_network *network)
 
 	network->connecting = TRUE;
 
+	service = __connman_service_lookup_from_network(network);
+
 	err = network->driver->connect(network);
 	if (err < 0) {
 		if (err == -EINPROGRESS)
@@ -1141,9 +1183,22 @@ int __connman_network_clear_ipconfig(struct connman_network *network,
 	return 0;
 }
 
-int __connman_network_set_ipconfig(struct connman_network *network, struct connman_ipconfig *ipconfig)
+int __connman_network_set_ipconfig(struct connman_network *network,
+					struct connman_ipconfig *ipconfig)
 {
+	struct connman_ipconfig *ipv6config;
 	enum connman_ipconfig_method method;
+	int ret;
+
+	ipv6config = connman_ipconfig_get_ipv6config(ipconfig);
+
+	/* Currently, only handle manual method of IPv6 */
+	method = __connman_ipconfig_get_method(ipv6config);
+	if (method == CONNMAN_IPCONFIG_METHOD_MANUAL) {
+		ret = manual_ipv6_set(network, ipv6config);
+		if (ret != 0)
+			return ret;
+	}
 
 	method = __connman_ipconfig_get_method(ipconfig);
 
diff --git a/src/service.c b/src/service.c
index fd694e8..436a54f 100644
--- a/src/service.c
+++ b/src/service.c
@@ -645,6 +645,24 @@ static void append_ipv4(DBusMessageIter *iter, void *user_data)
 		__connman_ipconfig_append_ipv4(service->ipconfig, iter);
 }
 
+static void append_ipv6(DBusMessageIter *iter, void *user_data)
+{
+	struct connman_service *service = user_data;
+	struct connman_ipconfig *ipv6config;
+
+	if (is_connected(service) == FALSE)
+		return;
+
+	if (service->ipconfig == NULL)
+		return;
+
+	ipv6config = connman_ipconfig_get_ipv6config(service->ipconfig);
+	if (ipv6config == NULL)
+		return;
+
+	__connman_ipconfig_append_ipv6(ipv6config, iter);
+}
+
 static void append_ipv4config(DBusMessageIter *iter, void *user_data)
 {
 	struct connman_service *service = user_data;
@@ -653,6 +671,21 @@ static void append_ipv4config(DBusMessageIter *iter, void *user_data)
 		__connman_ipconfig_append_ipv4config(service->ipconfig, iter);
 }
 
+static void append_ipv6config(DBusMessageIter *iter, void *user_data)
+{
+	struct connman_service *service = user_data;
+	struct connman_ipconfig *ipv6config;
+
+	if (service->ipconfig == NULL)
+		return;
+
+	ipv6config = connman_ipconfig_get_ipv6config(service->ipconfig);
+	if (ipv6config == NULL)
+		return;
+
+	__connman_ipconfig_append_ipv6config(ipv6config, iter);
+}
+
 static void append_dns(DBusMessageIter *iter, void *user_data)
 {
 	struct connman_service *service = user_data;
@@ -727,6 +760,10 @@ static void settings_changed(struct connman_service *service)
 	connman_dbus_property_changed_dict(service->path,
 					CONNMAN_SERVICE_INTERFACE, "IPv4",
 							append_ipv4, service);
+
+	connman_dbus_property_changed_dict(service->path,
+					CONNMAN_SERVICE_INTERFACE, "IPv6",
+							append_ipv6, service);
 }
 
 static void ipv4_configuration_changed(struct connman_service *service)
@@ -738,6 +775,15 @@ static void ipv4_configuration_changed(struct connman_service *service)
 							service);
 }
 
+static void ipv6_configuration_changed(struct connman_service *service)
+{
+	connman_dbus_property_changed_dict(service->path,
+					CONNMAN_SERVICE_INTERFACE,
+							"IPv6.Configuration",
+							append_ipv6config,
+							service);
+}
+
 static void dns_changed(struct connman_service *service)
 {
 	if (is_connected(service) == FALSE)
@@ -896,6 +942,11 @@ static void append_properties(DBusMessageIter *dict, dbus_bool_t limited,
 	connman_dbus_dict_append_dict(dict, "IPv4.Configuration",
 						append_ipv4config, service);
 
+	connman_dbus_dict_append_dict(dict, "IPv6", append_ipv6, service);
+
+	connman_dbus_dict_append_dict(dict, "IPv6.Configuration",
+						append_ipv6config, service);
+
 	connman_dbus_dict_append_array(dict, "Nameservers",
 				DBUS_TYPE_STRING, append_dns, service);
 
@@ -1171,29 +1222,51 @@ static DBusMessage *set_property(DBusConnection *conn,
 		domain_configuration_changed(service);
 
 		__connman_storage_save_service(service);
-	} else if (g_str_equal(name, "IPv4.Configuration") == TRUE) {
+	} else if (g_str_equal(name, "IPv4.Configuration") == TRUE ||
+			g_str_equal(name, "IPv6.Configuration")) {
+
+		enum connman_ipconfig_type type;
 		int err;
+		struct connman_ipconfig *ipv6config;
+
+		DBG("%s", name);
 
+		ipv6config = connman_ipconfig_get_ipv6config(
+						service->ipconfig);
 		if (service->ipconfig == NULL)
 			return __connman_error_invalid_property(msg);
 
 		if (is_connecting(service) ||
-				is_connected(service))
+				is_connected(service)) {
 			__connman_network_clear_ipconfig(service->network,
 							service->ipconfig);
+			__connman_network_clear_ipconfig(service->network,
+								ipv6config);
+		}
+
+		if (g_str_equal(name, "IPv4.Configuration") == TRUE) {
+			type = CONNMAN_IPCONFIG_TYPE_IPV4;
+			err = __connman_ipconfig_set_config(
+					service->ipconfig, type, &value);
+		} else if (g_str_equal(name, "IPv6.Configuration") == TRUE) {
+			type = CONNMAN_IPCONFIG_TYPE_IPV6;
+			err = __connman_ipconfig_set_config(
+						ipv6config, type, &value);
+		}
 
-		err = __connman_ipconfig_set_ipv4config(service->ipconfig,
-								&value);
 		if (err < 0) {
 			if (is_connected(service) ||
 					is_connecting(service))
-				__connman_network_set_ipconfig(service->network,
+				__connman_network_set_ipconfig(
+							service->network,
 							service->ipconfig);
-
 			return __connman_error_failed(msg, -err);
 		}
 
-		ipv4_configuration_changed(service);
+		if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+			ipv4_configuration_changed(service);
+		else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+			ipv6_configuration_changed(service);
 
 		if (is_connecting(service) ||
 				is_connected(service))
@@ -2284,6 +2357,7 @@ int __connman_service_connect(struct connman_service *service)
 
 int __connman_service_disconnect(struct connman_service *service)
 {
+	struct connman_ipconfig *ipv6config;
 	int err;
 
 	DBG("service %p", service);
@@ -2295,6 +2369,10 @@ int __connman_service_disconnect(struct connman_service *service)
 
 	__connman_ipconfig_clear_address(service->ipconfig);
 
+	ipv6config = connman_ipconfig_get_ipv6config(service->ipconfig);
+
+	__connman_ipconfig_clear_address(ipv6config);
+
 	__connman_ipconfig_disable(service->ipconfig);
 
 	if (err < 0) {
@@ -2694,6 +2772,7 @@ static void setup_ipconfig(struct connman_service *service, int index)
 void __connman_service_create_ipconfig(struct connman_service *service,
 								int index)
 {
+	struct connman_ipconfig *ipv6config;
 	const char *ident = service->profile;
 	GKeyFile *keyfile;
 
@@ -2709,6 +2788,12 @@ void __connman_service_create_ipconfig(struct connman_service *service,
 	if (keyfile == NULL)
 		return;
 
+
+	ipv6config = connman_ipconfig_get_ipv6config(service->ipconfig);
+	if (ipv6config != NULL)
+		__connman_ipconfig_load(ipv6config, keyfile,
+					service->identifier, "IPv6.");
+
 	__connman_ipconfig_load(service->ipconfig, keyfile,
 					service->identifier, "IPv4.");
 	g_key_file_free(keyfile);
@@ -3234,9 +3319,18 @@ static int service_load(struct connman_service *service)
 		service->passphrase = str;
 	}
 
-	if (service->ipconfig != NULL)
+	if (service->ipconfig != NULL) {
+		struct connman_ipconfig *ipv6config;
+
+		ipv6config = connman_ipconfig_get_ipv6config(
+						service->ipconfig);
+		if (ipv6config != NULL)
+			__connman_ipconfig_load(ipv6config, keyfile,
+					service->identifier, "IPv6.");
+
 		__connman_ipconfig_load(service->ipconfig, keyfile,
 					service->identifier, "IPv4.");
+	}
 
 	service->nameservers = g_key_file_get_string_list(keyfile,
 			service->identifier, "Nameservers", &length, NULL);
@@ -3382,9 +3476,17 @@ update:
 		g_key_file_remove_key(keyfile, service->identifier,
 							"Passphrase", NULL);
 
-	if (service->ipconfig != NULL)
+	if (service->ipconfig != NULL) {
+		struct connman_ipconfig *ipv6config;
+
+		ipv6config = connman_ipconfig_get_ipv6config(service->ipconfig);
+		if (ipv6config != NULL)
+			__connman_ipconfig_save(ipv6config, keyfile,
+						service->identifier, "IPv6.");
+
 		__connman_ipconfig_save(service->ipconfig, keyfile,
 					service->identifier, "IPv4.");
+	}
 
 	if (service->nameservers != NULL) {
 		guint len = g_strv_length(service->nameservers);
diff --git a/test/set-ipv6-address b/test/set-ipv6-address
new file mode 100755
index 0000000..6593dca
--- /dev/null
+++ b/test/set-ipv6-address
@@ -0,0 +1,29 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if (len(sys.argv) < 2):
+	print "Usage: %s <address> <gateway>" % (sys.argv[0])
+	sys.exit(1)
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.moblin.connman', "/"),
+					'org.moblin.connman.Manager')
+
+properties = manager.GetProperties()
+
+for path in properties["Services"]:
+	service = dbus.Interface(bus.get_object('org.moblin.connman', path),
+						'org.moblin.connman.Service')
+
+	properties = service.GetProperties()
+
+	print "Setting address %s for %s" % (sys.argv[1], path)
+
+	service.SetProperty("IPv6.Configuration",
+		{ "Method": "manual", "Address": sys.argv[1],
+					"Gateway": sys.argv[2]})
+
+	print
-- 
1.6.1.3

