Add support for cloning IP configurations
authorMarcel Holtmann <marcel@holtmann.org>
Sun, 23 Aug 2009 20:16:13 +0000 (13:16 -0700)
committerMarcel Holtmann <marcel@holtmann.org>
Sun, 23 Aug 2009 20:16:13 +0000 (13:16 -0700)
include/ipconfig.h
src/connman.h
src/device.c
src/ipconfig.c
src/service.c

index 76d0b28..8888d09 100644 (file)
@@ -69,6 +69,7 @@ struct connman_ipconfig_ops {
 };
 
 struct connman_ipconfig *connman_ipconfig_create(int index);
+struct connman_ipconfig *connman_ipconfig_clone(struct connman_ipconfig *ipconfig);
 struct connman_ipconfig *connman_ipconfig_ref(struct connman_ipconfig *ipconfig);
 void connman_ipconfig_unref(struct connman_ipconfig *ipconfig);
 
index 5ea9ea4..8392201 100644 (file)
@@ -112,6 +112,9 @@ unsigned short __connman_ipconfig_get_type(int index);
 unsigned int __connman_ipconfig_get_flags(int index);
 const char *__connman_ipconfig_get_gateway(int index);
 
+int __connman_ipconfig_enable(struct connman_ipconfig *ipconfig);
+int __connman_ipconfig_disable(struct connman_ipconfig *ipconfig);
+
 const char *__connman_ipconfig_method2string(enum connman_ipconfig_method method);
 enum connman_ipconfig_method __connman_ipconfig_string2method(const char *method);
 
index 1f0e20a..2745893 100644 (file)
@@ -1019,45 +1019,6 @@ const char *connman_device_get_path(struct connman_device *device)
        return device->element.path;
 }
 
-static void device_up(struct connman_ipconfig *ipconfig)
-{
-       connman_info("%s up", connman_ipconfig_get_ifname(ipconfig));
-}
-
-static void device_down(struct connman_ipconfig *ipconfig)
-{
-       connman_info("%s down", connman_ipconfig_get_ifname(ipconfig));
-}
-
-static void device_lower_up(struct connman_ipconfig *ipconfig)
-{
-       connman_info("%s lower up", connman_ipconfig_get_ifname(ipconfig));
-}
-
-static void device_lower_down(struct connman_ipconfig *ipconfig)
-{
-       connman_info("%s lower down", connman_ipconfig_get_ifname(ipconfig));
-}
-
-static void device_ip_bound(struct connman_ipconfig *ipconfig)
-{
-       connman_info("%s ip bound", connman_ipconfig_get_ifname(ipconfig));
-}
-
-static void device_ip_release(struct connman_ipconfig *ipconfig)
-{
-       connman_info("%s ip release", connman_ipconfig_get_ifname(ipconfig));
-}
-
-static const struct connman_ipconfig_ops device_ops = {
-       .up             = device_up,
-       .down           = device_down,
-       .lower_up       = device_lower_up,
-       .lower_down     = device_lower_down,
-       .ip_bound       = device_ip_bound,
-       .ip_release     = device_ip_release,
-};
-
 /**
  * connman_device_set_index:
  * @device: device structure
@@ -1077,8 +1038,6 @@ void connman_device_set_index(struct connman_device *device, int index)
        }
 
        device->ipconfig = connman_ipconfig_create(index);
-
-       connman_ipconfig_set_ops(device->ipconfig, &device_ops);
 }
 
 /**
index 2e68a65..2f5d521 100644 (file)
@@ -38,6 +38,8 @@ struct connman_ipconfig {
        gint refcount;
        int index;
 
+       struct connman_ipconfig *origin;
+
        const struct connman_ipconfig_ops *ops;
        void *ops_data;
 
@@ -55,7 +57,9 @@ struct connman_ipdevice {
        char *gateway;
 
        struct connman_ipconfig *config;
+
        struct connman_ipconfig_driver *driver;
+       struct connman_ipconfig *driver_config;
 };
 
 static GHashTable *ipdevice_hash = NULL;
@@ -74,6 +78,9 @@ struct connman_ipaddress *connman_ipaddress_alloc(void)
 
 void connman_ipaddress_free(struct connman_ipaddress *ipaddress)
 {
+       if (ipaddress == NULL)
+               return;
+
        g_free(ipaddress->broadcast);
        g_free(ipaddress->peer);
        g_free(ipaddress->local);
@@ -83,6 +90,9 @@ void connman_ipaddress_free(struct connman_ipaddress *ipaddress)
 void connman_ipaddress_copy(struct connman_ipaddress *ipaddress,
                                        struct connman_ipaddress *source)
 {
+       if (ipaddress == NULL || source == NULL)
+               return;
+
        ipaddress->prefixlen = source->prefixlen;
 
        g_free(ipaddress->local);
@@ -235,14 +245,23 @@ static void __connman_ipconfig_lower_up(struct connman_ipdevice *ipdevice)
        if (ipdevice->driver != NULL)
                return;
 
+       ipdevice->driver_config = connman_ipconfig_clone(ipdevice->config);
+       if (ipdevice->driver_config == NULL)
+               return;
+
        for (list = driver_list; list; list = list->next) {
                struct connman_ipconfig_driver *driver = list->data;
 
-               if (driver->request(ipdevice->config) == 0) {
+               if (driver->request(ipdevice->driver_config) == 0) {
                        ipdevice->driver = driver;
                        break;
                }
        }
+
+       if (ipdevice->driver == NULL) {
+               connman_ipconfig_unref(ipdevice->driver_config);
+               ipdevice->driver_config = NULL;
+       }
 }
 
 static void __connman_ipconfig_lower_down(struct connman_ipdevice *ipdevice)
@@ -255,7 +274,12 @@ static void __connman_ipconfig_lower_down(struct connman_ipdevice *ipdevice)
        if (ipdevice->driver == NULL)
                return;
 
-       ipdevice->driver->release(ipdevice->config);
+       ipdevice->driver->release(ipdevice->driver_config);
+
+       ipdevice->driver = NULL;
+
+       connman_ipconfig_unref(ipdevice->driver_config);
+       ipdevice->driver_config = NULL;
 
        connman_inet_clear_address(ipdevice->index);
 }
@@ -283,17 +307,6 @@ void __connman_ipconfig_newlink(int index, unsigned short type,
        ipdevice->ifname = connman_inet_ifname(index);
        ipdevice->type = type;
 
-       ipdevice->config = connman_ipconfig_create(index);
-       if (ipdevice->config == NULL) {
-               g_free(ipdevice->ifname);
-               g_free(ipdevice);
-               return;
-       }
-
-       if (type == ARPHRD_ETHER)
-               connman_ipconfig_set_method(ipdevice->config,
-                                       CONNMAN_IPCONFIG_METHOD_IGNORE);
-
        g_hash_table_insert(ipdevice_hash, GINT_TO_POINTER(index), ipdevice);
 
        connman_info("%s {create} index %d type %d <%s>", ipdevice->ifname,
@@ -611,6 +624,32 @@ struct connman_ipconfig *connman_ipconfig_create(int index)
 }
 
 /**
+ * connman_ipconfig_clone:
+ *
+ * Clone an ipconfig structure and create new reference.
+ *
+ * Returns: a newly-allocated #connman_ipconfig structure
+ */
+struct connman_ipconfig *connman_ipconfig_clone(struct connman_ipconfig *ipconfig)
+{
+       struct connman_ipconfig *ipconfig_clone;
+
+       DBG("ipconfig %p", ipconfig);
+
+       ipconfig_clone = g_try_new0(struct connman_ipconfig, 1);
+       if (ipconfig_clone == NULL)
+               return NULL;
+
+       ipconfig_clone->refcount = 1;
+
+       ipconfig_clone->origin = connman_ipconfig_ref(ipconfig);
+
+       ipconfig_clone->index = -1;
+
+       return ipconfig_clone;
+}
+
+/**
  * connman_ipconfig_ref:
  * @ipconfig: ipconfig structure
  *
@@ -634,6 +673,11 @@ void connman_ipconfig_unref(struct connman_ipconfig *ipconfig)
        if (g_atomic_int_dec_and_test(&ipconfig->refcount) == TRUE) {
                connman_ipconfig_set_ops(ipconfig, NULL);
 
+               if (ipconfig->origin != NULL) {
+                       connman_ipconfig_unref(ipconfig->origin);
+                       ipconfig->origin = NULL;
+               }
+
                connman_ipaddress_free(ipconfig->address);
                g_free(ipconfig);
        }
@@ -670,6 +714,9 @@ void connman_ipconfig_set_data(struct connman_ipconfig *ipconfig, void *data)
  */
 int connman_ipconfig_get_index(struct connman_ipconfig *ipconfig)
 {
+       if (ipconfig->origin != NULL)
+               return ipconfig->origin->index;
+
        return ipconfig->index;
 }
 
@@ -738,9 +785,58 @@ int connman_ipconfig_set_method(struct connman_ipconfig *ipconfig,
 void connman_ipconfig_bind(struct connman_ipconfig *ipconfig,
                                        struct connman_ipaddress *ipaddress)
 {
-       connman_ipaddress_copy(ipconfig->address, ipaddress);
+       struct connman_ipconfig *origin;
 
-       connman_inet_set_address(ipconfig->index, ipconfig->address);
+       origin = ipconfig->origin ? ipconfig->origin : ipconfig;
+
+       connman_ipaddress_copy(origin->address, ipaddress);
+
+       connman_inet_set_address(origin->index, origin->address);
+}
+
+int __connman_ipconfig_enable(struct connman_ipconfig *ipconfig)
+{
+       struct connman_ipdevice *ipdevice;
+
+       DBG("ipconfig %p", ipconfig);
+
+       if (ipconfig == NULL || ipconfig->index < 0)
+               return -ENODEV;
+
+       ipdevice = g_hash_table_lookup(ipdevice_hash,
+                                       GINT_TO_POINTER(ipconfig->index));
+       if (ipdevice == NULL)
+               return -ENXIO;
+
+       if (ipdevice->config != NULL)
+               connman_ipconfig_unref(ipdevice->config);
+
+       ipdevice->config = connman_ipconfig_ref(ipconfig);
+
+       return 0;
+}
+
+int __connman_ipconfig_disable(struct connman_ipconfig *ipconfig)
+{
+       struct connman_ipdevice *ipdevice;
+
+       DBG("ipconfig %p", ipconfig);
+
+       if (ipconfig == NULL || ipconfig->index < 0)
+               return -ENODEV;
+
+       ipdevice = g_hash_table_lookup(ipdevice_hash,
+                                       GINT_TO_POINTER(ipconfig->index));
+       if (ipdevice == NULL)
+               return -ENXIO;
+
+       if (ipdevice->config == NULL || ipdevice->config != ipconfig)
+               return -EINVAL;
+
+       connman_ipconfig_unref(ipdevice->config);
+       ipdevice->config = NULL;
+
+       return 0;
 }
 
 const char *__connman_ipconfig_method2string(enum connman_ipconfig_method method)
index 7287c0d..df49a21 100644 (file)
@@ -703,6 +703,8 @@ static gboolean connect_timeout(gpointer user_data)
        else if (service->device != NULL)
                __connman_device_disconnect(service->device);
 
+       __connman_ipconfig_disable(service->ipconfig);
+
        if (service->pending != NULL) {
                DBusMessage *reply;
 
@@ -1283,18 +1285,24 @@ int __connman_service_connect(struct connman_service *service)
                if (prepare_network(service) == FALSE)
                        return -EINVAL;
 
+               __connman_ipconfig_enable(service->ipconfig);
+
                err = __connman_network_connect(service->network);
        } else if (service->device != NULL) {
                if (service->favorite == FALSE)
                        return -ENOLINK;
 
+               __connman_ipconfig_enable(service->ipconfig);
+
                err = __connman_device_connect(service->device);
        } else
                return -EOPNOTSUPP;
 
        if (err < 0) {
-               if (err != -EINPROGRESS)
+               if (err != -EINPROGRESS) {
+                       __connman_ipconfig_disable(service->ipconfig);
                        return err;
+               }
 
                service->timeout = g_timeout_add_seconds(45,
                                                connect_timeout, service);
@@ -1322,6 +1330,8 @@ int __connman_service_disconnect(struct connman_service *service)
        } else
                return -EOPNOTSUPP;
 
+       __connman_ipconfig_disable(service->ipconfig);
+
        if (err < 0) {
                if (err != -EINPROGRESS)
                        return err;
@@ -1608,6 +1618,64 @@ static int service_register(struct connman_service *service)
        return 0;
 }
 
+static void service_up(struct connman_ipconfig *ipconfig)
+{
+       connman_info("%s up", connman_ipconfig_get_ifname(ipconfig));
+}
+
+static void service_down(struct connman_ipconfig *ipconfig)
+{
+       connman_info("%s down", connman_ipconfig_get_ifname(ipconfig));
+}
+
+static void service_lower_up(struct connman_ipconfig *ipconfig)
+{
+       connman_info("%s lower up", connman_ipconfig_get_ifname(ipconfig));
+}
+
+static void service_lower_down(struct connman_ipconfig *ipconfig)
+{
+       connman_info("%s lower down", connman_ipconfig_get_ifname(ipconfig));
+}
+
+static void service_ip_bound(struct connman_ipconfig *ipconfig)
+{
+       connman_info("%s ip bound", connman_ipconfig_get_ifname(ipconfig));
+}
+
+static void service_ip_release(struct connman_ipconfig *ipconfig)
+{
+       connman_info("%s ip release", connman_ipconfig_get_ifname(ipconfig));
+}
+
+static const struct connman_ipconfig_ops service_ops = {
+       .up             = service_up,
+       .down           = service_down,
+       .lower_up       = service_lower_up,
+       .lower_down     = service_lower_down,
+       .ip_bound       = service_ip_bound,
+       .ip_release     = service_ip_release,
+};
+
+static void setup_ipconfig(struct connman_service *service, int index)
+{
+       if (index < 0)
+               return;
+
+       service->ipconfig = connman_ipconfig_create(index);
+       if (service->ipconfig == NULL)
+               return;
+
+       connman_ipconfig_set_method(service->ipconfig,
+                                       CONNMAN_IPCONFIG_METHOD_DHCP);
+
+       __connman_storage_load_service(service);
+
+       connman_ipconfig_set_data(service->ipconfig, service);
+
+       connman_ipconfig_set_ops(service->ipconfig, &service_ops);
+}
+
 /**
  * __connman_service_lookup_from_device:
  * @device: device structure
@@ -1667,6 +1735,8 @@ struct connman_service *__connman_service_create_from_device(struct connman_devi
 
        service->device = device;
 
+       setup_ipconfig(service, connman_device_get_index(device));
+
        service_register(service);
 
        __connman_profile_changed(TRUE);
@@ -1903,6 +1973,8 @@ struct connman_service *__connman_service_create_from_network(struct connman_net
 
        update_from_network(service, network);
 
+       setup_ipconfig(service, connman_network_get_index(network));
+
        service_register(service);
 
        __connman_profile_changed(TRUE);