Send connman mailing list submissions to
        connman@lists.01.org

To subscribe or unsubscribe via email, send a message with subject or
body 'help' to
        connman-requ...@lists.01.org

You can reach the person managing the list at
        connman-ow...@lists.01.org

When replying, please edit your Subject line so it is more specific
than "Re: Contents of connman digest..."

Today's Topics:

   1. [PATCH 00/11] Prevent IPv4 VPN data/DNS leak to IPv6 network(s)
      (Jussi Laakkonen)
   2. [PATCH 01/11] vpn: Return transport ident with get_property()
      (Jussi Laakkonen)
   3. [PATCH 02/11] provider: Add function to get transport ident from VPN
      (Jussi Laakkonen)
   4. [PATCH 03/11] service: Sort VPNs using the transport service if connected
      (Jussi Laakkonen)
   5. [PATCH 04/11] ipconfig: Refactor /proc value get/set to separate functions
      (Jussi Laakkonen)
   6. [PATCH 05/11] ipconfig: Add forced disabling of IPv6 and support IPv6 
method restore
      (Jussi Laakkonen)


----------------------------------------------------------------------

Date: Thu,  1 Apr 2021 17:46:07 +0300
From: Jussi Laakkonen <jussi.laakko...@jolla.com>
Subject: [PATCH 00/11] Prevent IPv4 VPN data/DNS leak to IPv6
        network(s)
To: connman@lists.01.org
Message-ID: <20210401144618.12936-1-jussi.laakko...@jolla.com>

The core idea of this is to prevent leaking of any data to an IPv6 network when
an IPv4 VPN is used with dual-stack transport or when there are other connected
IPv6 cabable technologies. Leak can happen when the DNS server of the VPN
responds also to AAAA requests providing an IPv6 address as well. In that case
IPv6 of the transport/other connected service will be used and traffic bypasses
VPN.

Prevention of data leak is achieved by disabling IPv6 on adapter basis with
disable_ipv6 and autoconf values. It was not seen reasonable to disable IPv6
on system basis as there may be other interfaces that are not managed by
ConnMan (blacklisted) which may require IPv6 to function properly.

Therefore, this simply does disable IPv6 on all connected services when the
IPv4 VPN is connected with the help of changes done to ipconfig.c. Setting the
internal value to block IPv6 use within ConnMan is utilized only when there can
be multiple connected technologies as otherwise the transport will get replaced
and VPN is re-connected using the new transport.

If multiple connected technologies is enabled (SingleConnectedTech is unset or
false) and a new service connects then in this case it cannot first establish
an IPv6 connection at all but if the particular service then ranks over the
current service the VPN will be disconnected, IPv6 is re-enabled and the new 
ervice starts to establish an IPv6 connection. Otherwise the new service
remains with IPv4 connectivity only - but if it is IPv6 only the service cannot
connect and this is expected and added as a comment to SingleConnectedTech
option in documentation as well as a TODO to consider improvement for the case.

To be future-proof it may be better to have the new services to have their IPv6
network not enabled but forcefully disabled in case at the time of connection
an IPv4 VPN is connected. Not only due to the fact that an IPv6 address is not
attempted to be retrieved with the current approach for a newly connected
network. Alternative would be to call
__connman_provider_set_ipv6_for_connected() in case the default service is an
IPv4 VPN to get the service list traversed. But in that case IPv6 connectivity
may have been established and there might be a small timeframe for leaking
traffic.

This was first sent as a RFC and the changes done since are summarized as:
 - Split ipconfig patch to two separate reformat and IPv6 related ones.
 - Removed use of force attribute and use separate functions. Instead use
   separate setter and getter for the value.
 - vpn.c after all required the content that was cleaned prior to RFC cleanup.
 - Added restoring of the saved IPv6 method if ipconfig was disabled when
   setting disconnected in network.c to notify kernel properly.
 - Use the original IPv6 method when saving to disk if IPv6 is force disabled.
 - Reorder patches to work around dependencies of new functions.
 - Always set the autoconf for IPv6 /proc when disabling/enabling.

Jussi Laakkonen (11):
  vpn: Return transport ident with get_property()
  provider: Add function to get transport ident from VPN
  service: Sort VPNs using the transport service if connected
  ipconfig: Refactor /proc value get/set to separate functions
  ipconfig: Add forced disabling of IPv6 and support IPv6 method restore
  network: Adopt to new IPv6 disabled functionality of ipconfig.c
  service: Support IPv6 enable/disable for connected services
  provider: Toggle IPv6 on the transport of IPv4 VPN connection
  service: Change IPv6 support if split routing value changes on IPv4
    VPN
  doc: Add note to SingleConnectedTech on IPv4 VPN data leak prevention
  TODO: Add task about IPv6 only connection with IPv4 VPN

 TODO                  |  11 ++
 doc/connman.conf.5.in |   4 +
 plugins/vpn.c         |  22 ++-
 src/connman.h         |  20 ++-
 src/ipconfig.c        | 305 +++++++++++++++++++++++++++++-------------
 src/network.c         |  41 +++++-
 src/provider.c        | 129 ++++++++++++++++++
 src/service.c         | 179 +++++++++++++++++++++++++
 8 files changed, 607 insertions(+), 104 deletions(-)

-- 
2.20.1

------------------------------

Date: Thu,  1 Apr 2021 17:46:08 +0300
From: Jussi Laakkonen <jussi.laakko...@jolla.com>
Subject: [PATCH 01/11] vpn: Return transport ident with get_property()
To: connman@lists.01.org
Message-ID: <20210401144618.12936-2-jussi.laakko...@jolla.com>

Return the service_ident with "Transport" keyword given to
get_property(). Plugin tracks the transport and this is can be used
elsewhere as well.

It is important to free the service_ident after notifying provider about
state change as the service_ident is used for IPv6 checks when disconnecting.
---
 plugins/vpn.c | 22 +++++++++++++++-------
 1 file changed, 15 insertions(+), 7 deletions(-)

diff --git a/plugins/vpn.c b/plugins/vpn.c
index 3ed9c322..d708d1ff 100644
--- a/plugins/vpn.c
+++ b/plugins/vpn.c
@@ -153,6 +153,8 @@ static const char *get_string(struct connman_provider 
*provider,
                        return data->host_ip[0];
        } else if (g_str_equal(key, "VPN.Domain"))
                return data->domain;
+       else if (g_str_equal(key, "Transport"))
+               return data->service_ident;
 
        return g_hash_table_lookup(data->setting_strings, key);
 }
@@ -268,14 +270,12 @@ static bool provider_is_connected(struct connection_data 
*data)
 static void set_provider_state(struct connection_data *data)
 {
        enum connman_provider_state state = CONNMAN_PROVIDER_STATE_UNKNOWN;
+       bool connected;
        int err = 0;
 
        DBG("provider %p new state %s", data->provider, data->state);
 
-       if (!provider_is_connected(data)) {
-               g_free(data->service_ident);
-               data->service_ident = NULL;
-       }
+       connected = provider_is_connected(data);
 
        if (g_str_equal(data->state, "ready")) {
                state = CONNMAN_PROVIDER_STATE_READY;
@@ -295,7 +295,7 @@ static void set_provider_state(struct connection_data *data)
        }
 
        connman_provider_set_state(data->provider, state);
-       return;
+       goto free;
 
 set:
        if (data->cb_data)
@@ -306,6 +306,12 @@ set:
 
        free_config_cb_data(data->cb_data);
        data->cb_data = NULL;
+
+free:
+       if (!connected) {
+               g_free(data->service_ident);
+               data->service_ident = NULL;
+       }
 }
 
 static int create_provider(struct connection_data *data, void *user_data)
@@ -1032,12 +1038,14 @@ static int disconnect_provider(struct connection_data 
*data)
        dbus_pending_call_set_notify(data->disconnect_call, disconnect_reply,
                                                                data, NULL);
 
-       g_free(data->service_ident);
-       data->service_ident = NULL;
        data->default_route_set = false;
 
        connman_provider_set_state(data->provider,
                                        CONNMAN_PROVIDER_STATE_DISCONNECT);
+
+       g_free(data->service_ident);
+       data->service_ident = NULL;
+
        return -EINPROGRESS;
 }
 
-- 
2.20.1

------------------------------

Date: Thu,  1 Apr 2021 17:46:09 +0300
From: Jussi Laakkonen <jussi.laakko...@jolla.com>
Subject: [PATCH 02/11] provider: Add function to get transport ident
        from VPN
To: connman@lists.01.org
Message-ID: <20210401144618.12936-3-jussi.laakko...@jolla.com>

Get the ident of the transport of the VPN. Use get_property() callback
with "Transport" to get the value.
---
 src/connman.h  |  2 ++
 src/provider.c | 12 ++++++++++++
 2 files changed, 14 insertions(+)

diff --git a/src/connman.h b/src/connman.h
index 59a78d9a..898174cb 100644
--- a/src/connman.h
+++ b/src/connman.h
@@ -663,6 +663,8 @@ void __connman_provider_list(DBusMessageIter *iter, void 
*user_data);
 bool __connman_provider_is_immutable(struct connman_provider *provider);
 int __connman_provider_create_and_connect(DBusMessage *msg);
 const char * __connman_provider_get_ident(struct connman_provider *provider);
+const char * __connman_provider_get_transport_ident(
+                                       struct connman_provider *provider);
 int __connman_provider_indicate_state(struct connman_provider *provider,
                                        enum connman_provider_state state);
 int __connman_provider_indicate_error(struct connman_provider *provider,
diff --git a/src/provider.c b/src/provider.c
index 1eae4951..51bbfdf4 100644
--- a/src/provider.c
+++ b/src/provider.c
@@ -442,6 +442,18 @@ const char *__connman_provider_get_ident(struct 
connman_provider *provider)
        return provider->identifier;
 }
 
+const char * __connman_provider_get_transport_ident(
+                                       struct connman_provider *provider)
+{
+       if (!provider)
+               return NULL;
+
+       if (provider->driver && provider->driver->get_property)
+               return provider->driver->get_property(provider, "Transport");
+
+       return NULL;
+}
+
 int connman_provider_set_string(struct connman_provider *provider,
                                        const char *key, const char *value)
 {
-- 
2.20.1

------------------------------

Date: Thu,  1 Apr 2021 17:46:10 +0300
From: Jussi Laakkonen <jussi.laakko...@jolla.com>
Subject: [PATCH 03/11] service: Sort VPNs using the transport service
        if connected
To: connman@lists.01.org
Message-ID: <20210401144618.12936-4-jussi.laakko...@jolla.com>

Use the transport to verify the order of the connected VPN services. If
there is a transport service in use that ranks lower than an another
service it means that the order must be changed based on comparing the
transport and the service instead of comparing VPN and the service. This
is because the higher ranking service should then become the transport
of the VPN.

This ensures that when the list is sorted the transport check in
plugins/vpn.c will make VPN to switch to the new transport that is
enabled to be the default. Use of the service ident from hash table for
searching is used because the index cannot be retrieved from the list
while sorting the list.
---
 src/service.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/src/service.c b/src/service.c
index 2e78bb04..7dfa89f2 100644
--- a/src/service.c
+++ b/src/service.c
@@ -5267,6 +5267,40 @@ void connman_service_unref_debug(struct connman_service 
*service,
        g_hash_table_remove(service_hash, service->identifier);
 }
 
+static gint service_compare(gconstpointer a, gconstpointer b);
+
+static gint service_compare_vpn(struct connman_service *a,
+                                               struct connman_service *b)
+{
+       struct connman_provider *provider;
+       struct connman_service *service;
+       struct connman_service *transport;
+       const char *ident;
+       bool reverse;
+
+       if (a->provider) {
+               provider = a->provider;
+               service = b;
+               reverse = false;
+       } else if (b->provider) {
+               provider = b->provider;
+               service = a;
+               reverse = true;
+       } else {
+               return 0;
+       }
+
+       ident = __connman_provider_get_transport_ident(provider);
+       transport = connman_service_lookup_from_identifier(ident);
+       if (!transport)
+               return 0;
+
+       if (reverse)
+               return service_compare(service, transport);
+
+       return service_compare(transport, service);
+}
+
 static gint service_compare(gconstpointer a, gconstpointer b)
 {
        struct connman_service *service_a = (void *) a;
@@ -5281,6 +5315,17 @@ static gint service_compare(gconstpointer a, 
gconstpointer b)
        b_connected = is_connected(state_b);
 
        if (a_connected && b_connected) {
+               int rval;
+
+               /* Compare the VPN transport and the service */
+               if ((service_a->type == CONNMAN_SERVICE_TYPE_VPN ||
+                               service_b->type == CONNMAN_SERVICE_TYPE_VPN) &&
+                               service_b->type != service_a->type) {
+                       rval = service_compare_vpn(service_a, service_b);
+                       if (rval)
+                               return rval;
+               }
+
                if (service_a->order > service_b->order)
                        return -1;
 
-- 
2.20.1

------------------------------

Date: Thu,  1 Apr 2021 17:46:11 +0300
From: Jussi Laakkonen <jussi.laakko...@jolla.com>
Subject: [PATCH 04/11] ipconfig: Refactor /proc value get/set to
        separate functions
To: connman@lists.01.org
Message-ID: <20210401144618.12936-5-jussi.laakko...@jolla.com>

Refactor getting and setting the proc conf values, have read and write
in their own respective general functions. If write/read is issued
without interface name (NULL) then "all" section is used to follow the
old behavior.
---
 src/ipconfig.c | 182 ++++++++++++++++++++++++++-----------------------
 1 file changed, 97 insertions(+), 85 deletions(-)

diff --git a/src/ipconfig.c b/src/ipconfig.c
index 2bfeefd4..1551826b 100644
--- a/src/ipconfig.c
+++ b/src/ipconfig.c
@@ -258,153 +258,165 @@ static const char *scope2str(unsigned char scope)
        return "";
 }
 
-static bool get_ipv6_state(gchar *ifname)
+#define PROC_IPV4_CONF_PREFIX "/proc/sys/net/ipv4/conf"
+#define PROC_IPV6_CONF_PREFIX "/proc/sys/net/ipv6/conf"
+
+static int read_conf_value(const char *prefix, const char *ifname,
+                                       const char *suffix, int *value)
 {
-       int disabled;
        gchar *path;
        FILE *f;
-       bool enabled = false;
-
-       if (!ifname)
-               path = g_strdup("/proc/sys/net/ipv6/conf/all/disable_ipv6");
-       else
-               path = g_strdup_printf(
-                       "/proc/sys/net/ipv6/conf/%s/disable_ipv6", ifname);
+       int err;
 
+       path = g_build_filename(prefix, ifname ? ifname : "all", suffix, NULL);
        if (!path)
-               return enabled;
+               return -ENOMEM;
 
+       errno = 0;
        f = fopen(path, "r");
+       if (!f) {
+               err = -errno;
+       } else {
+               errno = 0; /* Avoid stale errno values with fscanf */
 
-       g_free(path);
+               err = fscanf(f, "%d", value);
+               if (err <= 0 && errno)
+                       err = -errno;
 
-       if (f) {
-               if (fscanf(f, "%d", &disabled) > 0)
-                       enabled = !disabled;
                fclose(f);
        }
 
-       return enabled;
+       if (err <= 0)
+               connman_error("failed to read %s", path);
+
+       g_free(path);
+
+       return err;
 }
 
-static void set_ipv6_state(gchar *ifname, bool enable)
+static int read_ipv4_conf_value(const char *ifname, const char *suffix,
+                                                               int *value)
 {
+       return read_conf_value(PROC_IPV4_CONF_PREFIX, ifname, suffix, value);
+}
+
+static int read_ipv6_conf_value(const char *ifname, const char *suffix,
+                                                               int *value)
+{
+       return read_conf_value(PROC_IPV6_CONF_PREFIX, ifname, suffix, value);
+}
+
+static int write_conf_value(const char *prefix, const char *ifname,
+                                       const char *suffix, int value) {
        gchar *path;
        FILE *f;
+       int rval;
 
-       if (!ifname)
-               path = g_strdup("/proc/sys/net/ipv6/conf/all/disable_ipv6");
-       else
-               path = g_strdup_printf(
-                       "/proc/sys/net/ipv6/conf/%s/disable_ipv6", ifname);
-
+       path = g_build_filename(prefix, ifname ? ifname : "all", suffix, NULL);
        if (!path)
-               return;
+               return -ENOMEM;
 
        f = fopen(path, "r+");
+       if (!f) {
+               rval = -errno;
+       } else {
+               rval = fprintf(f, "%d", value);
+               fclose(f);
+       }
+
+       if (rval <= 0)
+               connman_error("failed to set %s value %d", path, value);
 
        g_free(path);
 
-       if (!f)
-               return;
+       return rval;
+}
 
-       if (!enable)
-               fprintf(f, "1");
-       else
-               fprintf(f, "0");
+static int write_ipv4_conf_value(const char *ifname, const char *suffix,
+                                                               int value)
+{
+       return write_conf_value(PROC_IPV4_CONF_PREFIX, ifname, suffix, value);
+}
 
-       fclose(f);
+static int write_ipv6_conf_value(const char *ifname, const char *suffix,
+                                                               int value)
+{
+       return write_conf_value(PROC_IPV6_CONF_PREFIX, ifname, suffix, value);
 }
 
-static int get_ipv6_privacy(gchar *ifname)
+static bool get_ipv6_state(gchar *ifname)
 {
-       gchar *path;
-       FILE *f;
-       int value;
+       int disabled;
+       bool enabled = false;
 
-       if (!ifname)
-               return 0;
+       if (read_ipv6_conf_value(ifname, "disable_ipv6", &disabled) > 0)
+               enabled = !disabled;
 
-       path = g_strdup_printf("/proc/sys/net/ipv6/conf/%s/use_tempaddr",
-                                                               ifname);
+       return enabled;
+}
 
-       if (!path)
-               return 0;
+static int set_ipv6_state(gchar *ifname, bool enable)
+{
+       int disabled = enable ? 0 : 1;
 
-       f = fopen(path, "r");
+       DBG("%s %d", ifname, disabled);
 
-       g_free(path);
+       return write_ipv6_conf_value(ifname, "disable_ipv6", disabled);
+}
+
+static int get_ipv6_privacy(gchar *ifname)
+{
+       int value;
 
-       if (!f)
+       if (!ifname)
                return 0;
 
-       if (fscanf(f, "%d", &value) <= 0)
+       if (read_ipv6_conf_value(ifname, "use_tempaddr", &value) < 0)
                value = 0;
 
-       fclose(f);
-
        return value;
 }
 
 /* Enable the IPv6 privacy extension for stateless address autoconfiguration.
  * The privacy extension is described in RFC 3041 and RFC 4941
  */
-static void set_ipv6_privacy(gchar *ifname, int value)
+static int set_ipv6_privacy(gchar *ifname, int value)
 {
-       gchar *path;
-       FILE *f;
-
        if (!ifname)
-               return;
-
-       path = g_strdup_printf("/proc/sys/net/ipv6/conf/%s/use_tempaddr",
-                                                               ifname);
-
-       if (!path)
-               return;
+               return -EINVAL;
 
        if (value < 0)
                value = 0;
 
-       f = fopen(path, "r+");
-
-       g_free(path);
-
-       if (!f)
-               return;
-
-       fprintf(f, "%d", value);
-       fclose(f);
+       return write_ipv6_conf_value(ifname, "use_tempaddr", value);
 }
 
 static int get_rp_filter(void)
 {
-       FILE *f;
-       int value = -EINVAL, tmp;
+       int value;
 
-       f = fopen("/proc/sys/net/ipv4/conf/all/rp_filter", "r");
-
-       if (f) {
-               if (fscanf(f, "%d", &tmp) == 1)
-                       value = tmp;
-               fclose(f);
-       }
+       if (read_ipv4_conf_value(NULL, "rp_filter", &value) < 0)
+               value = -EINVAL;
 
        return value;
 }
 
-static void set_rp_filter(int value)
+static int set_rp_filter(int value)
 {
-       FILE *f;
-
-       f = fopen("/proc/sys/net/ipv4/conf/all/rp_filter", "r+");
-
-       if (!f)
-               return;
-
-       fprintf(f, "%d", value);
+       /* 0 = no validation, 1 = strict mode, 2 = loose mode */
+       switch (value) {
+       case -1:
+               value = 0;
+               /* fall through */
+       case 0:
+       case 1:
+       case 2:
+               break;
+       default:
+               return -EINVAL;
+       }
 
-       fclose(f);
+       return write_ipv4_conf_value(NULL, "rp_filter", value);
 }
 
 int __connman_ipconfig_set_rp_filter()
-- 
2.20.1

------------------------------

Date: Thu,  1 Apr 2021 17:46:12 +0300
From: Jussi Laakkonen <jussi.laakko...@jolla.com>
Subject: [PATCH 05/11] ipconfig: Add forced disabling of IPv6 and
        support IPv6 method restore
To: connman@lists.01.org
Message-ID: <20210401144618.12936-6-jussi.laakko...@jolla.com>

Support complete disabling of IPv6 on both system and interface levels.
Disabling of IPv6 completely albeit temporarily is required when there
can be multiple connected technologies and a VPN is connected over IPv4
using an dual-stack transport and/or there is another technology with
dual-stack or only IPv6 in use. This approach effectively ensures that
no data from an IPv4 VPN leaks to any other interface. A DNS server
returning replies to AAAA requests may return an IPv6 address, which
could be then used over IPv6 routing on another technolology that is not
acting as the transport of the VPN.

Add getter and setter for the internal IPv6 status. When enabling check
the status with connman_inet_is_ipv6_supported(). It is not the
intention to disable IPv6 system-wide since there may be interfaces that
should not be managed by ConnMan and are handled for other OS needs
where IPv6 is required, e.g., VoLTE may be one of them.

Add a "ipv6_force_disabled" toggle to indicate that the particular
device tied to ipconfig should not have IPv6 enabled. This toggle is to
be used in scenarios where IPv6 should be prevented from reinstating
unless forcefully enabled. Added separate setter and getter functions
for the value (__connman_ipconfig_ipv6_{set,get}_force_disabled()).

When checking IPv6 enabled state return false if ipv6_force_disabled is
set as that is the real status of IPv6. The /proc disable_ipv6 can
change when kernel processes ICMPv6 (RA/RS) packets and brings the IPv6
interface up so using only the disable_ipv6 value is not consistent
enough in case when IPv6 has been forcefully disabled.

This change will prevent from changing the IPv6 status until it has
it has been forcefully enabled, until that -EOPNOSUPP is returned.
The most prominent use case for this is to prevent data as well as DNS
leak to IPv6 when IPv4 only VPN is connected over a transport supporting
both IPv4 and IPv6 connectivity.

In addition to changing disable_ipv6 value also the autoconf option is
managed. This is done in order to control the address setup for the
interface. It is worthy to note that disable_ipv6 may be temporarily
set to 0 by the kernel. This can happen when LL address is temporarily
set as a typical behavior of IPv6 on some configurations to handle
ICMPv6. After a while the value of disable_ipv6 as well as LL address
are reset/removed by kernel.

Add functions to save and restore old IPv6 method. This is useful when
IPv6 is disabled termporarily for the time IPv4 VPN is used. First save
the IPv6 method before disabling and call restore when IPv6 needs to be
set up again using the old method.

When saving ipconfig use the original IPv6 method if the ipconfig is
force disabled. This ensures that "OFF" method is not saved to settings
file if the service and its ipconfig gets saved.
---
 src/connman.h  |  13 ++++-
 src/ipconfig.c | 129 ++++++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 129 insertions(+), 13 deletions(-)

diff --git a/src/connman.h b/src/connman.h
index 898174cb..0a8adbd3 100644
--- a/src/connman.h
+++ b/src/connman.h
@@ -341,7 +341,9 @@ void __connman_ipconfig_set_ops(struct connman_ipconfig 
*ipconfig,
 int __connman_ipconfig_set_method(struct connman_ipconfig *ipconfig,
                                        enum connman_ipconfig_method method);
 void __connman_ipconfig_disable_ipv6(struct connman_ipconfig *ipconfig);
-void __connman_ipconfig_enable_ipv6(struct connman_ipconfig *ipconfig);
+int __connman_ipconfig_enable_ipv6(struct connman_ipconfig *ipconfig);
+int __connman_ipconfig_set_ipv6_support(bool enable);
+bool __connman_ipconfig_get_ipv6_support();
 
 int __connman_ipconfig_init(void);
 void __connman_ipconfig_cleanup(void);
@@ -406,6 +408,8 @@ 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_ipv6_method_save(struct connman_ipconfig *ipconfig);
+void __connman_ipconfig_ipv6_method_restore(struct connman_ipconfig *ipconfig);
 
 int __connman_ipconfig_address_add(struct connman_ipconfig *ipconfig);
 int __connman_ipconfig_address_remove(struct connman_ipconfig *ipconfig);
@@ -432,6 +436,13 @@ int __connman_ipconfig_ipv6_reset_privacy(struct 
connman_ipconfig *ipconfig);
 int __connman_ipconfig_ipv6_set_privacy(struct connman_ipconfig *ipconfig,
                                        const char *value);
 bool __connman_ipconfig_ipv6_is_enabled(struct connman_ipconfig *ipconfig);
+void __connman_ipconfig_ipv6_method_save(struct connman_ipconfig *ipconfig);
+void __connman_ipconfig_ipv6_method_restore(struct connman_ipconfig *ipconfig);
+void __connman_ipconfig_ipv6_set_force_disabled(
+                                       struct connman_ipconfig *ipconfig,
+                                       bool force_disabled);
+bool __connman_ipconfig_ipv6_get_force_disabled(
+                                       struct connman_ipconfig *ipconfig);
 
 int __connman_ipconfig_set_rp_filter();
 void __connman_ipconfig_unset_rp_filter(int old_value);
diff --git a/src/ipconfig.c b/src/ipconfig.c
index 1551826b..4dd98b74 100644
--- a/src/ipconfig.c
+++ b/src/ipconfig.c
@@ -49,12 +49,15 @@ struct connman_ipconfig {
        void *ops_data;
 
        enum connman_ipconfig_method method;
+       enum connman_ipconfig_method saved_method;
        struct connman_ipaddress *address;
        struct connman_ipaddress *system;
 
        int ipv6_privacy_config;
        char *last_dhcp_address;
        char **last_dhcpv6_prefixes;
+
+       bool ipv6_force_disabled;
 };
 
 struct connman_ipdevice {
@@ -391,6 +394,15 @@ static int set_ipv6_privacy(gchar *ifname, int value)
        return write_ipv6_conf_value(ifname, "use_tempaddr", value);
 }
 
+static int set_ipv6_autoconf(gchar *ifname, bool enable)
+{
+       int value = enable ? 1 : 0;
+
+       DBG("%s %d", ifname, enable);
+
+       return write_ipv6_conf_value(ifname, "autoconf", value);
+}
+
 static int get_rp_filter(void)
 {
        int value;
@@ -460,6 +472,13 @@ bool __connman_ipconfig_ipv6_is_enabled(struct 
connman_ipconfig *ipconfig)
        if (!ipconfig)
                return false;
 
+       /*
+        * Return forced value since kernel can enable LL address for IPv6
+        * for handling ICMPv6.
+        */
+       if (ipconfig->ipv6_force_disabled)
+               return false;
+
        ipdevice = g_hash_table_lookup(ipdevice_hash,
                                        GINT_TO_POINTER(ipconfig->index));
        if (!ipdevice)
@@ -472,6 +491,25 @@ bool __connman_ipconfig_ipv6_is_enabled(struct 
connman_ipconfig *ipconfig)
        return ret;
 }
 
+void __connman_ipconfig_ipv6_set_force_disabled(
+                                       struct connman_ipconfig *ipconfig,
+                                       bool force_disabled)
+{
+       if (!ipconfig || ipconfig->type != CONNMAN_IPCONFIG_TYPE_IPV6)
+               return;
+
+       ipconfig->ipv6_force_disabled = force_disabled;
+}
+
+bool __connman_ipconfig_ipv6_get_force_disabled(
+                                       struct connman_ipconfig *ipconfig)
+{
+       if (!ipconfig || ipconfig->type != CONNMAN_IPCONFIG_TYPE_IPV6)
+               return false;
+
+       return ipconfig->ipv6_force_disabled;
+}
+
 static void free_ipdevice(gpointer data)
 {
        struct connman_ipdevice *ipdevice = data;
@@ -1248,6 +1286,8 @@ static struct connman_ipconfig *create_ipv6config(int 
index)
        else
                ipv6config->method = CONNMAN_IPCONFIG_METHOD_AUTO;
 
+       ipv6config->saved_method = CONNMAN_IPCONFIG_METHOD_UNKNOWN;
+
        ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
        if (ipdevice)
                ipv6config->ipv6_privacy_config = ipdevice->ipv6_privacy;
@@ -1433,6 +1473,33 @@ enum connman_ipconfig_method 
__connman_ipconfig_get_method(
        return ipconfig->method;
 }
 
+void __connman_ipconfig_ipv6_method_save(struct connman_ipconfig *ipconfig)
+{
+       if (!ipconfig || ipconfig->type != CONNMAN_IPCONFIG_TYPE_IPV6)
+               return;
+
+       DBG("%p method %d", ipconfig, ipconfig->method);
+
+       ipconfig->saved_method = ipconfig->method;
+}
+
+void __connman_ipconfig_ipv6_method_restore(struct connman_ipconfig *ipconfig)
+{
+       if (!ipconfig || ipconfig->type != CONNMAN_IPCONFIG_TYPE_IPV6)
+               return;
+
+       /* If not previously set, default to AUTO */
+       if (ipconfig->saved_method == CONNMAN_IPCONFIG_METHOD_UNKNOWN)
+               ipconfig->method = CONNMAN_IPCONFIG_METHOD_AUTO;
+       else
+               ipconfig->method = ipconfig->saved_method;
+
+       DBG("%p saved method %d set method %d", ipconfig,
+                               ipconfig->saved_method, ipconfig->method);
+
+       ipconfig->saved_method = CONNMAN_IPCONFIG_METHOD_UNKNOWN;
+}
+
 int __connman_ipconfig_address_add(struct connman_ipconfig *ipconfig)
 {
        switch (ipconfig->method) {
@@ -1579,7 +1646,7 @@ char **__connman_ipconfig_get_dhcpv6_prefixes(struct 
connman_ipconfig *ipconfig)
        return ipconfig->last_dhcpv6_prefixes;
 }
 
-static void disable_ipv6(struct connman_ipconfig *ipconfig)
+static int disable_ipv6(struct connman_ipconfig *ipconfig)
 {
        struct connman_ipdevice *ipdevice;
        char *ifname;
@@ -1589,16 +1656,22 @@ static void disable_ipv6(struct connman_ipconfig 
*ipconfig)
        ipdevice = g_hash_table_lookup(ipdevice_hash,
                                        GINT_TO_POINTER(ipconfig->index));
        if (!ipdevice)
-               return;
+               return -EINVAL;
+
+       DBG("%p force_disabled %s", ipconfig,
+                               ipconfig->ipv6_force_disabled ? "yes" : "no");
 
        ifname = connman_inet_ifname(ipconfig->index);
 
        set_ipv6_state(ifname, false);
+       set_ipv6_autoconf(ifname, false);
 
        g_free(ifname);
+
+       return 0;
 }
 
-static void enable_ipv6(struct connman_ipconfig *ipconfig)
+static int enable_ipv6(struct connman_ipconfig *ipconfig)
 {
        struct connman_ipdevice *ipdevice;
        char *ifname;
@@ -1608,7 +1681,14 @@ static void enable_ipv6(struct connman_ipconfig 
*ipconfig)
        ipdevice = g_hash_table_lookup(ipdevice_hash,
                                        GINT_TO_POINTER(ipconfig->index));
        if (!ipdevice)
-               return;
+               return -EINVAL;
+
+       DBG("IPv6 %s %p force_disabled %s", is_ipv6_supported ? "on" : "off",
+                               ipconfig,
+                               ipconfig->ipv6_force_disabled ? "yes" : "no");
+
+       if (!is_ipv6_supported || ipconfig->ipv6_force_disabled)
+               return -EOPNOTSUPP;
 
        ifname = connman_inet_ifname(ipconfig->index);
 
@@ -1616,16 +1696,19 @@ static void enable_ipv6(struct connman_ipconfig 
*ipconfig)
                set_ipv6_privacy(ifname, ipconfig->ipv6_privacy_config);
 
        set_ipv6_state(ifname, true);
+       set_ipv6_autoconf(ifname, true);
 
        g_free(ifname);
+
+       return 0;
 }
 
-void __connman_ipconfig_enable_ipv6(struct connman_ipconfig *ipconfig)
+int __connman_ipconfig_enable_ipv6(struct connman_ipconfig *ipconfig)
 {
        if (!ipconfig || ipconfig->type != CONNMAN_IPCONFIG_TYPE_IPV6)
-               return;
+               return -EINVAL;
 
-       enable_ipv6(ipconfig);
+       return enable_ipv6(ipconfig);
 }
 
 void __connman_ipconfig_disable_ipv6(struct connman_ipconfig *ipconfig)
@@ -1636,6 +1719,18 @@ void __connman_ipconfig_disable_ipv6(struct 
connman_ipconfig *ipconfig)
        disable_ipv6(ipconfig);
 }
 
+int __connman_ipconfig_set_ipv6_support(bool enable)
+{
+       is_ipv6_supported = enable ? connman_inet_is_ipv6_supported() : false;
+
+       return 0;
+}
+
+bool __connman_ipconfig_get_ipv6_support()
+{
+       return is_ipv6_supported;
+}
+
 bool __connman_ipconfig_is_usable(struct connman_ipconfig *ipconfig)
 {
        if (!ipconfig)
@@ -1662,6 +1757,7 @@ int __connman_ipconfig_enable(struct connman_ipconfig 
*ipconfig)
        bool lower_up = false, lower_down = false;
        enum connman_ipconfig_type type;
        char *ifname;
+       int err;
 
        DBG("ipconfig %p", ipconfig);
 
@@ -1715,7 +1811,9 @@ int __connman_ipconfig_enable(struct connman_ipconfig 
*ipconfig)
        else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
                ipdevice->config_ipv6 = __connman_ipconfig_ref(ipconfig);
 
-               enable_ipv6(ipdevice->config_ipv6);
+               err = enable_ipv6(ipdevice->config_ipv6);
+               if (err)
+                       return err;
        }
        ipconfig_list = g_list_append(ipconfig_list, ipconfig);
 
@@ -1884,9 +1982,7 @@ int __connman_ipconfig_ipv6_set_privacy(struct 
connman_ipconfig *ipconfig,
 
        ipconfig->ipv6_privacy_config = privacy;
 
-       enable_ipv6(ipconfig);
-
-       return 0;
+       return enable_ipv6(ipconfig);
 }
 
 void __connman_ipconfig_append_ipv4(struct connman_ipconfig *ipconfig,
@@ -2374,12 +2470,21 @@ void __connman_ipconfig_load(struct connman_ipconfig 
*ipconfig,
 void __connman_ipconfig_save(struct connman_ipconfig *ipconfig,
                GKeyFile *keyfile, const char *identifier, const char *prefix)
 {
+       enum connman_ipconfig_method ipconfig_method;
        const char *method;
        struct ipconfig_store is = { .file = keyfile,
                                     .group = identifier,
                                     .prefix = prefix };
 
-       method = __connman_ipconfig_method2string(ipconfig->method);
+       /* Use the original method if IPv6 is force disabled */
+       if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
+                               ipconfig->ipv6_force_disabled)
+               ipconfig_method = ipconfig->saved_method;
+       else
+               ipconfig_method = ipconfig->method;
+
+       method = __connman_ipconfig_method2string(ipconfig_method);
+
        DBG("ipconfig %p identifier %s method %s", ipconfig, identifier,
                                                                method);
        store_set_str(&is, "method", method);
-- 
2.20.1

------------------------------

Subject: Digest Footer

_______________________________________________
connman mailing list -- connman@lists.01.org
To unsubscribe send an email to connman-le...@lists.01.org


------------------------------

End of connman Digest, Vol 65, Issue 15
***************************************

Reply via email to