From ea2c14980b153c28990f8aa15da4192d04e42173 Mon Sep 17 00:00:00 2001 From: Martin Xu Date: Wed, 28 Jul 2010 17:28:42 +0200 Subject: [PATCH] Initial IPv6 support Only manual/fixed setting supported for now. --- doc/service-api.txt | 36 ++++++++ include/element.h | 8 ++ include/inet.h | 12 +++ include/ipconfig.h | 7 +- include/property.h | 5 ++ src/connection.c | 24 ++++- src/connman.h | 13 ++- src/element.c | 7 ++ src/inet.c | 229 ++++++++++++++++++++++++++++++++++++++++++++++ src/ipconfig.c | 254 +++++++++++++++++++++++++++++++++++++++++++++++++--- src/ipv4.c | 13 ++- src/network.c | 75 +++++++++++++++- src/service.c | 122 ++++++++++++++++++++++--- 13 files changed, 768 insertions(+), 37 deletions(-) diff --git a/doc/service-api.txt b/doc/service-api.txt index 9bf0907..29ef96a 100644 --- a/doc/service-api.txt +++ b/doc/service-api.txt @@ -359,6 +359,42 @@ Properties string State [readonly] until the new configuration has been successfully installed. + dict IPv6 [readonly] + + string Method [readonly] + + Possible values are "dhcp", "manual" + and "off". + + The value "fixed" indicates an IP address + that can not be modified. For example + cellular networks return fixed information. + + "dhcp" is not supported currently. + + string Address [readonly] + + The current configured IPv6 address. + + uint8 PrefixLength [readonly] + + The prefix length of the IPv6 address. + + 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..4161d7e 100644 --- a/include/element.h +++ b/include/element.h @@ -109,6 +109,14 @@ struct connman_element { gchar *timeserver; gchar *pac; } ipv4; + + struct { + enum connman_ipconfig_method method; + gchar *address; + int prefix_len; + gchar *gateway; + gchar *network; + } ipv6; }; struct connman_element *connman_element_create(const char *name); diff --git a/include/inet.h b/include/inet.h index 389a584..ce0a0b2 100644 --- a/include/inet.h +++ b/include/inet.h @@ -51,6 +51,18 @@ int connman_inet_clear_gateway_address(int index, const char *gateway); int connman_inet_set_gateway_interface(int index); int connman_inet_clear_gateway_interface(int index); connman_bool_t connman_inet_compare_subnet(int index, const char *host); +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, + const char *gateway); +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_ipaddress_set_ipv4(struct connman_ipaddress *ipaddress, + const char *address, const char *netmask, const char *gateway); #ifdef __cplusplus } diff --git a/include/ipconfig.h b/include/ipconfig.h index 0b70252..5f67dcc 100644 --- a/include/ipconfig.h +++ b/include/ipconfig.h @@ -86,10 +86,13 @@ 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); - +int connman_ipaddress_set_ipv6(struct connman_ipaddress *ipaddress, + const char *address, const char *gateway, + unsigned char prefix_length); +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); - void connman_ipconfig_bind(struct connman_ipconfig *ipconfig, struct connman_ipaddress *ipaddress); diff --git a/include/property.h b/include/property.h index c7f2a7b..d00c61b 100644 --- a/include/property.h +++ b/include/property.h @@ -48,6 +48,11 @@ enum connman_property_id { CONNMAN_PROPERTY_ID_IPV4_NAMESERVER, CONNMAN_PROPERTY_ID_IPV4_TIMESERVER, CONNMAN_PROPERTY_ID_IPV4_PAC, + + CONNMAN_PROPERTY_ID_IPV6_METHOD, + CONNMAN_PROPERTY_ID_IPV6_ADDRESS, + CONNMAN_PROPERTY_ID_IPV6_PREFIXLEN, + CONNMAN_PROPERTY_ID_IPV6_GATEWAY, }; enum connman_property_type { diff --git a/src/connection.c b/src/connection.c index cb0b528..319024f 100644 --- a/src/connection.c +++ b/src/connection.c @@ -33,6 +33,7 @@ struct gateway_data { int index; char *ipv4_gateway; + char *ipv6_gateway; struct connman_element *element; unsigned int order; gboolean active; @@ -77,6 +78,10 @@ static int del_routes(struct gateway_data *data) } else if (g_strcmp0(data->ipv4_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->ipv4_gateway); return connman_inet_clear_gateway_address(data->index, data->ipv4_gateway); @@ -98,11 +103,15 @@ 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; + DBG("index %d gateway %s ipv6_gateway %s", index, gateway, + ipv6_gateway); + if (strlen(gateway) == 0) return NULL; @@ -112,6 +121,7 @@ static struct gateway_data *add_gateway(int index, const char *gateway) data->index = index; data->ipv4_gateway = g_strdup(gateway); + data->ipv6_gateway = g_strdup(ipv6_gateway); data->active = FALSE; data->element = NULL; data->vpn_ip = NULL; @@ -162,6 +172,8 @@ static void set_default_gateway(struct gateway_data *data) goto done; } + connman_inet_set_ipv6_gateway_address(element->index, + data->ipv6_gateway); if (connman_inet_set_gateway_address(element->index, data->ipv4_gateway) < 0) return; @@ -249,7 +261,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; @@ -265,11 +277,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,12 +297,14 @@ 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 (new_gateway == NULL) return 0; service = __connman_element_get_service(element); + connman_inet_add_ipv6_host_route(element->index, + new_gateway->ipv6_gateway, NULL); connman_inet_add_host_route(element->index, new_gateway->ipv4_gateway, NULL); __connman_service_nameserver_add_routes(service, diff --git a/src/connman.h b/src/connman.h index f33f745..b014c2e 100644 --- a/src/connman.h +++ b/src/connman.h @@ -236,14 +236,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 094c788..3f1547a 100644 --- a/src/inet.c +++ b/src/inet.c @@ -508,6 +508,47 @@ 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, err; + struct in6_ifreq ifr6; + + DBG("index %d ipaddress->local %s", index, ipaddress->local); + + if (ipaddress->local == NULL) + return 0; + + sk = socket(PF_INET6, SOCK_DGRAM, 0); + if (sk < 0) { + err = -1; + goto out; + } + + memset(&ifr6, 0, sizeof(ifr6)); + + err = inet_pton(AF_INET6, ipaddress->local, &ifr6.ifr6_addr); + if (err < 0) + goto out; + + ifr6.ifr6_ifindex = index; + ifr6.ifr6_prefixlen = ipaddress->prefixlen; + + err = ioctl(sk, SIOCSIFADDR, &ifr6); + close(sk); +out: + if (err < 0) + connman_error("Set IPv6 address error"); + + return err; +} + int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress) { struct ifreq ifr; @@ -574,8 +615,42 @@ 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, err; + + DBG("index %d address %s prefix_len %d", index, address, prefix_len); + + memset(&ifr6, 0, sizeof(ifr6)); + + err = inet_pton(AF_INET6, address, &ifr6.ifr6_addr); + if (err < 0) + goto out; + + ifr6.ifr6_ifindex = index; + ifr6.ifr6_prefixlen = prefix_len; + + sk = socket(PF_INET6, SOCK_DGRAM, 0); + if (sk < 0) { + err = -1; + goto out; + } + + err = ioctl(sk, SIOCDIFADDR, &ifr6); + close(sk); +out: + if (err < 0) + connman_error("Clear IPv6 address error"); + + return err; +} + int connman_inet_clear_address(int index) { + + struct ifreq ifr; struct sockaddr_in addr; int sk, err; @@ -709,6 +784,160 @@ int connman_inet_del_host_route(int index, const char *host) return err; } +int connman_inet_del_ipv6_host_route(int index, const char *host) +{ + struct in6_rtmsg rt; + int sk, err; + + DBG("index %d host %s", index, host); + + if (host == NULL) + return -EINVAL; + + memset(&rt, 0, sizeof(rt)); + + rt.rtmsg_dst_len = 128; + + err = inet_pton(AF_INET6, host, &rt.rtmsg_dst); + if (err < 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) { + err = -1; + goto out; + } + + err = ioctl(sk, SIOCDELRT, &rt); + close(sk); +out: + if (err < 0) + connman_error("Del IPv6 host route error"); + + return err; +} + +int connman_inet_add_ipv6_host_route(int index, const char *host, + const char *gateway) +{ + struct in6_rtmsg rt; + int sk, err; + + DBG("index %d host %s gateway %s", index, host, gateway); + + if (host == NULL) + return -EINVAL; + + memset(&rt, 0, sizeof(rt)); + + rt.rtmsg_dst_len = 128; + + err = inet_pton(AF_INET6, host, &rt.rtmsg_dst); + if (err < 0) + goto out; + + rt.rtmsg_flags = RTF_UP | RTF_HOST; + + if (gateway != NULL) { + rt.rtmsg_flags |= RTF_GATEWAY; + inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway); + } + + rt.rtmsg_metric = 1; + rt.rtmsg_ifindex = index; + + sk = socket(AF_INET6, SOCK_DGRAM, 0); + if (sk < 0) { + err = -1; + goto out; + } + + err = ioctl(sk, SIOCADDRT, &rt); + close(sk); +out: + if (err < 0) + connman_error("Set IPv6 host route error"); + + return err; +} + +int connman_inet_set_ipv6_gateway_address(int index, const char *gateway) +{ + struct in6_rtmsg rt; + int sk, err; + + DBG("index %d, gateway %s", index, gateway); + + if (gateway == NULL) + return -EINVAL; + + memset(&rt, 0, sizeof(rt)); + + err = inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway); + if (err < 0) + goto out; + + rt.rtmsg_flags = RTF_UP | RTF_GATEWAY; + rt.rtmsg_metric = 1; + rt.rtmsg_dst_len = 0; + rt.rtmsg_ifindex = index; + + sk = socket(AF_INET6, SOCK_DGRAM, 0); + if (sk < 0) { + err = -1; + goto out; + } + + err = ioctl(sk, SIOCADDRT, &rt); + close(sk); +out: + if (err < 0) + connman_error("Set default IPv6 gateway error"); + + return err; +} + +int connman_inet_clear_ipv6_gateway_address(int index, const char *gateway) +{ + struct in6_rtmsg rt; + int sk, err; + + DBG("index %d, gateway %s", index, gateway); + + if (gateway == NULL) + return -EINVAL; + + memset(&rt, 0, sizeof(rt)); + + err = inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway); + if (err < 0) + goto out; + + rt.rtmsg_flags = RTF_UP | RTF_GATEWAY; + rt.rtmsg_metric = 1; + rt.rtmsg_dst_len = 0; + rt.rtmsg_ifindex = index; + + sk = socket(AF_INET6, SOCK_DGRAM, 0); + if (sk < 0) { + err = -1; + goto out; + } + + err = ioctl(sk, SIOCDELRT, &rt); + close(sk); +out: + if (err < 0) + connman_error("Clear default IPv6 gateway error"); + + return err; +} + 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 c589223..f486c99 100644 --- a/src/ipconfig.c +++ b/src/ipconfig.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include #ifndef IFF_LOWER_UP #define IFF_LOWER_UP 0x10000 @@ -38,6 +40,7 @@ struct connman_ipconfig { gint refcount; int index; + enum connman_ipconfig_type type; struct connman_ipconfig *origin; @@ -47,6 +50,8 @@ struct connman_ipconfig { enum connman_ipconfig_method method; struct connman_ipaddress *address; struct connman_ipaddress *system; + + struct connman_ipconfig *ipv6; }; struct connman_ipdevice { @@ -85,6 +90,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; } @@ -116,7 +127,46 @@ 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) +{ + unsigned char buf[sizeof(struct in6_addr)]; + int err; + + err = inet_pton(AF_INET6, address, buf); + if (err > 0) + return TRUE; + + return FALSE; +} + +int connman_ipaddress_set_ipv6(struct connman_ipaddress *ipaddress, + const char *address, const char *gateway, + unsigned char prefix_length) +{ + if (ipaddress == NULL) + return -EINVAL; + + if (check_ipv6_address(address) == FALSE) + return -EINVAL; + + if (check_ipv6_address(gateway) == FALSE) + return -EINVAL; + + DBG("prefix_len %d address %s gateway %s", + prefix_length, address, gateway); + + ipaddress->prefixlen = prefix_length; + + 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) @@ -354,6 +404,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, @@ -759,6 +812,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; } /** @@ -781,6 +865,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) { @@ -790,6 +875,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; @@ -834,6 +921,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 @@ -854,6 +952,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); } } @@ -935,6 +1034,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 @@ -977,7 +1085,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) { @@ -985,9 +1103,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); @@ -1006,8 +1127,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; @@ -1029,11 +1154,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) @@ -1111,6 +1241,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; @@ -1155,6 +1286,8 @@ void __connman_ipconfig_append_ipv4(struct connman_ipconfig *ipconfig, { const char *str; + DBG(""); + str = __connman_ipconfig_method2string(ipconfig->method); if (str == NULL) return; @@ -1184,11 +1317,81 @@ 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) +{ + const char *str; + + DBG(""); + + str = __connman_ipconfig_method2string(ipconfig->method); + if (str == NULL) + return; + + connman_dbus_dict_append_basic(iter, "Method", DBUS_TYPE_STRING, &str); + + if (ipconfig->system == NULL) + return; + + if (ipconfig->system->local != NULL) { + connman_dbus_dict_append_basic(iter, "Address", + DBUS_TYPE_STRING, &ipconfig->system->local); + connman_dbus_dict_append_basic(iter, "PrefixLength", + DBUS_TYPE_BYTE, + &ipconfig->system->prefixlen); + } + + if (ipconfig->system->gateway != NULL) + connman_dbus_dict_append_basic(iter, "Gateway", + DBUS_TYPE_STRING, &ipconfig->system->gateway); +} + +void __connman_ipconfig_append_ipv6config(struct connman_ipconfig *ipconfig, + DBusMessageIter *iter) +{ + const char *str; + + DBG(""); + + 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_DHCP: + return; + case CONNMAN_IPCONFIG_METHOD_FIXED: + case CONNMAN_IPCONFIG_METHOD_MANUAL: + break; + } + + if (ipconfig->address == NULL) + return; + + if (ipconfig->address->local != NULL) { + connman_dbus_dict_append_basic(iter, "Address", + DBUS_TYPE_STRING, &ipconfig->address->local); + connman_dbus_dict_append_basic(iter, "PrefixLength", + DBUS_TYPE_BYTE, + &ipconfig->address->prefixlen); + } + + 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) { const char *str; + DBG(""); + str = __connman_ipconfig_method2string(ipconfig->method); if (str == NULL) return; @@ -1228,14 +1431,20 @@ 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; + const char *address = NULL, *netmask = NULL, *gateway = NULL, + *prefix_length_string = NULL; + int prefix_length = 0; 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; @@ -1270,6 +1479,17 @@ int __connman_ipconfig_set_ipv4config(struct connman_ipconfig *ipconfig, return -EINVAL; dbus_message_iter_get_basic(&entry, &address); + } else if (g_str_equal(key, "PrefixLength") == TRUE) { + if (type != DBUS_TYPE_STRING) + return -EINVAL; + + dbus_message_iter_get_basic(&entry, + &prefix_length_string); + + prefix_length = atoi(prefix_length_string); + if (prefix_length < 0 || prefix_length > 128) + return -EINVAL; + } else if (g_str_equal(key, "Netmask") == TRUE) { if (type != DBUS_TYPE_STRING) return -EINVAL; @@ -1284,8 +1504,8 @@ int __connman_ipconfig_set_ipv4config(struct connman_ipconfig *ipconfig, dbus_message_iter_next(&dict); } - DBG("method %d address %s netmask %s gateway %s", - method, address, netmask, gateway); + DBG("method %d address %s netmask %s gateway %s prefix_length %d", + method, address, netmask, gateway, prefix_length); switch (method) { case CONNMAN_IPCONFIG_METHOD_UNKNOWN: @@ -1298,8 +1518,14 @@ 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, prefix_length); 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..a74043c 100644 --- a/src/network.c +++ b/src/network.c @@ -845,6 +845,31 @@ 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 + * gateway will be set by IPV4 setting + */ + + return 0; +} + static gboolean set_connected(gpointer user_data) { struct connman_network *network = user_data; @@ -861,6 +886,29 @@ 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); + ipv6_method = __connman_ipconfig_get_method(ipv6config); + switch (ipv6_method) { + case CONNMAN_IPCONFIG_METHOD_UNKNOWN: + case CONNMAN_IPCONFIG_METHOD_OFF: + break; + case CONNMAN_IPCONFIG_METHOD_FIXED: + case 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; + } + break; + case CONNMAN_IPCONFIG_METHOD_DHCP: + break; + } + switch (method) { case CONNMAN_IPCONFIG_METHOD_UNKNOWN: case CONNMAN_IPCONFIG_METHOD_OFF: @@ -974,6 +1022,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 +1043,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 +1192,31 @@ 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); + method = __connman_ipconfig_get_method(ipv6config); + switch (method) { + case CONNMAN_IPCONFIG_METHOD_UNKNOWN: + case CONNMAN_IPCONFIG_METHOD_OFF: + break; + case CONNMAN_IPCONFIG_METHOD_FIXED: + case 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; + } + break; + case CONNMAN_IPCONFIG_METHOD_DHCP: + break; + } method = __connman_ipconfig_get_method(ipconfig); diff --git a/src/service.c b/src/service.c index 8aa0634..5723aab 100644 --- a/src/service.c +++ b/src/service.c @@ -791,6 +791,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; @@ -799,6 +817,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; @@ -873,6 +906,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) @@ -884,6 +921,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) @@ -1045,6 +1091,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); @@ -1331,29 +1382,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) { - int err; + } else if (g_str_equal(name, "IPv4.Configuration") == TRUE || + g_str_equal(name, "IPv6.Configuration")) { + + enum connman_ipconfig_type type = CONNMAN_IPCONFIG_TYPE_UNKNOWN; + int err = 0; + 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)) @@ -2464,6 +2537,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); @@ -2475,6 +2549,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) { @@ -2883,6 +2961,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; @@ -2898,6 +2977,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); @@ -3443,9 +3528,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); @@ -3591,9 +3685,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); -- 2.7.4