Add support for switching default route based on service order
authorMarcel Holtmann <marcel@holtmann.org>
Fri, 26 Jun 2009 12:33:56 +0000 (14:33 +0200)
committerMarcel Holtmann <marcel@holtmann.org>
Fri, 26 Jun 2009 12:33:56 +0000 (14:33 +0200)
src/connection.c
src/connman.h

index 293b911..d057d8a 100644 (file)
@@ -38,6 +38,9 @@
 struct gateway_data {
        int index;
        char *gateway;
+       struct connman_element *element;
+       unsigned int order;
+       gboolean active;
 };
 
 static GSList *gateway_list = NULL;
@@ -63,17 +66,6 @@ static struct gateway_data *find_gateway(int index, const char *gateway)
        return NULL;
 }
 
-static void remove_gateway(int index, const char *gateway)
-{
-       struct gateway_data *data;
-
-       data = find_gateway(index, gateway);
-       if (data == NULL)
-               return;
-
-       gateway_list = g_slist_remove(gateway_list, data);
-}
-
 static int set_route(struct connman_element *element, const char *gateway)
 {
        struct ifreq ifr;
@@ -218,87 +210,99 @@ static void emit_default_signal(struct connman_element *element)
        g_dbus_send_message(connection, signal);
 }
 
-static void set_default(struct connman_element *element, gpointer user_data)
+static void find_element(struct connman_element *element, gpointer user_data)
 {
        struct gateway_data *data = user_data;
 
        DBG("element %p name %s", element, element->name);
 
-       if (element->index != data->index)
+       if (data->element != NULL)
                return;
 
-       if (element->enabled == TRUE)
+       if (element->index != data->index)
                return;
 
-       connman_element_set_enabled(element, TRUE);
-       emit_default_signal(element);
+       data->element = element;
 }
 
-static void del_default(struct connman_element *element, gpointer user_data)
+static struct gateway_data *add_gateway(int index, const char *gateway)
 {
-       struct gateway_data *data = user_data;
+       struct gateway_data *data;
+       struct connman_service *service;
 
-       DBG("element %p name %s", element, element->name);
+       data = g_try_new0(struct gateway_data, 1);
+       if (data == NULL)
+               return NULL;
 
-       if (element->index != data->index)
-               return;
+       data->index = index;
+       data->gateway = g_strdup(gateway);
+       data->active = FALSE;
+       data->element = NULL;
 
-       if (element->enabled == FALSE)
-               return;
+       __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_CONNECTION,
+                                                       find_element, data);
 
-       connman_element_set_enabled(element, FALSE);
-       emit_default_signal(element);
+       service = __connman_element_get_service(data->element);
+       if (service != NULL)
+               data->order = __connman_service_get_order(service);
+
+       gateway_list = g_slist_append(gateway_list, data);
+
+       return data;
 }
 
-static void new_default(struct connman_element *element, gpointer user_data)
+static void connection_newgateway(int index, const char *gateway)
 {
-       struct connman_service *service;
-       const char *gateway;
+       struct gateway_data *data;
 
-       DBG("element %p name %s", element, element->name);
+       DBG("index %d gateway %s", index, gateway);
 
-       if (g_slist_length(gateway_list) > 0)
+       data = find_gateway(index, gateway);
+       if (data == NULL)
                return;
 
-       connman_element_get_value(element,
-                               CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
+       data->active = TRUE;
+}
 
-       DBG("gateway %s", gateway);
+static void set_default_gateway(struct gateway_data *data)
+{
+       struct connman_element *element = data->element;
+       struct connman_service *service = NULL;
 
-       if (gateway == NULL)
-               return;
+       DBG("gateway %s", data->gateway);
 
-       if (set_route(element, gateway) < 0)
+       if (set_route(element, data->gateway) < 0)
                return;
 
        service = __connman_element_get_service(element);
        __connman_service_indicate_default(service);
-
-       connman_element_set_enabled(element, TRUE);
-       emit_default_signal(element);
 }
 
-static void connection_newgateway(int index, const char *gateway)
+static struct gateway_data *find_default_gateway(void)
 {
-       struct gateway_data *data;
+       struct gateway_data *found = NULL;
+       GSList *list;
 
-       DBG("index %d gateway %s", index, gateway);
+       for (list = gateway_list; list; list = list->next) {
+               struct gateway_data *data = list->data;
+               /* just return the last one for now */
+               found = data;
+       }
 
-       data = find_gateway(index, gateway);
-       if (data != NULL)
-               return;
+       return found;
+}
 
-       data = g_try_new0(struct gateway_data, 1);
-       if (data == NULL)
-               return;
+static void remove_gateway(struct gateway_data *data)
+{
+       DBG("gateway %s", data->gateway);
 
-       data->index = index;
-       data->gateway = g_strdup(gateway);
+       gateway_list = g_slist_remove(gateway_list, data);
 
-       gateway_list = g_slist_append(gateway_list, data);
+       if (data->active == TRUE)
+               del_route(data->element, data->gateway);
 
-       __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_CONNECTION,
-                                                       set_default, data);
+       g_free(data->gateway);
+       g_free(data);
 }
 
 static void connection_delgateway(int index, const char *gateway)
@@ -308,24 +312,12 @@ static void connection_delgateway(int index, const char *gateway)
        DBG("index %d gateway %s", index, gateway);
 
        data = find_gateway(index, gateway);
-       if (data == NULL)
-               return;
-
-       gateway_list = g_slist_remove(gateway_list, data);
-
-       __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_CONNECTION,
-                                                       del_default, data);
-
-       g_free(data->gateway);
-       g_free(data);
-
-       if (g_slist_length(gateway_list) > 0)
-               return;
-
-       DBG("selecting new default gateway");
+       if (data != NULL)
+               data->active = FALSE;
 
-       __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_CONNECTION,
-                                                       new_default, NULL);
+       data = find_default_gateway();
+       if (data != NULL)
+               set_default_gateway(data);
 }
 
 static struct connman_rtnl connection_rtnl = {
@@ -455,6 +447,8 @@ static void emit_connections_signal(void)
        DBusMessage *signal;
        DBusMessageIter entry;
 
+       DBG("");
+
        signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
                                CONNMAN_MANAGER_INTERFACE, "PropertyChanged");
        if (signal == NULL)
@@ -469,7 +463,8 @@ static void emit_connections_signal(void)
 
 static int register_interface(struct connman_element *element)
 {
-       DBG("element %p name %s", element, element->name);
+       DBG("element %p name %s path %s",
+                               element, element->name, element->path);
 
        if (g_dbus_register_interface(connection, element->path,
                                        CONNMAN_CONNECTION_INTERFACE,
@@ -494,10 +489,27 @@ static void unregister_interface(struct connman_element *element)
                                                CONNMAN_CONNECTION_INTERFACE);
 }
 
+static struct gateway_data *find_active_gateway(void)
+{
+       GSList *list;
+
+       DBG("");
+
+       for (list = gateway_list; list; list = list->next) {
+               struct gateway_data *data = list->data;
+               if (data->active == TRUE)
+                       return data;
+       }
+
+       return NULL;
+}
+
 static int connection_probe(struct connman_element *element)
 {
-       struct connman_service *service;
+       struct connman_service *service = NULL;
        const char *gateway = NULL;
+       struct gateway_data *active_gateway = NULL;
+       struct gateway_data *new_gateway = NULL;
 
        DBG("element %p name %s", element, element->name);
 
@@ -514,33 +526,27 @@ static int connection_probe(struct connman_element *element)
 
        if (register_interface(element) < 0)
                return -ENODEV;
-
        service = __connman_element_get_service(element);
        __connman_service_indicate_state(service,
                                        CONNMAN_SERVICE_STATE_READY);
+       connman_element_set_enabled(element, TRUE);
+       emit_default_signal(element);
 
        if (gateway == NULL)
                return 0;
 
-       if (find_gateway(element->index, gateway) != NULL) {
-               DBG("previous gateway still present");
-               goto done;
-       }
+       active_gateway = find_active_gateway();
+       new_gateway = add_gateway(element->index, gateway);
 
-       if (g_slist_length(gateway_list) > 0) {
-               DBG("default gateway already present");
+       if (active_gateway == NULL) {
+               set_default_gateway(new_gateway);
                return 0;
        }
 
-       if (set_route(element, gateway) < 0)
+       if (new_gateway->order >= active_gateway->order) {
+               del_route(active_gateway->element, active_gateway->gateway);
                return 0;
-
-done:
-       service = __connman_element_get_service(element);
-       __connman_service_indicate_default(service);
-
-       connman_element_set_enabled(element, TRUE);
-       emit_default_signal(element);
+       }
 
        return 0;
 }
@@ -549,6 +555,7 @@ static void connection_remove(struct connman_element *element)
 {
        struct connman_service *service;
        const char *gateway = NULL;
+       struct gateway_data *data = NULL;
 
        DBG("element %p name %s", element, element->name);
 
@@ -556,6 +563,9 @@ static void connection_remove(struct connman_element *element)
        __connman_service_indicate_state(service,
                                        CONNMAN_SERVICE_STATE_DISCONNECT);
 
+       connman_element_set_enabled(element, FALSE);
+       emit_default_signal(element);
+
        unregister_interface(element);
 
        connman_element_get_value(element,
@@ -566,12 +576,11 @@ static void connection_remove(struct connman_element *element)
        if (gateway == NULL)
                return;
 
-       remove_gateway(element->index, gateway);
-
-       connman_element_set_enabled(element, FALSE);
-       emit_default_signal(element);
+       data = find_gateway(element->index, gateway);
+       if (data == NULL)
+               return;
 
-       del_route(element, gateway);
+       remove_gateway(data);
 }
 
 static struct connman_driver connection_driver = {
@@ -621,3 +630,30 @@ void __connman_connection_cleanup(void)
 
        dbus_connection_unref(connection);
 }
+
+static void update_order(void)
+{
+       GSList *list = NULL;
+
+       for (list = gateway_list; list; list = list->next) {
+               struct gateway_data *data = list->data;
+               struct connman_service *service;
+
+               service = __connman_element_get_service(data->element);
+
+               data->order = __connman_service_get_order(service);
+       }
+}
+
+void __connman_connection_update_gateway(void)
+{
+       struct gateway_data *active_gateway, *default_gateway;
+
+       update_order();
+
+       active_gateway = find_active_gateway();
+       default_gateway = find_default_gateway();
+
+       if (active_gateway != default_gateway)
+               del_route(active_gateway->element, active_gateway->gateway);
+}
index 6c82e51..497d0fe 100644 (file)
@@ -175,6 +175,8 @@ void __connman_ipv4_cleanup(void);
 int __connman_connection_init(void);
 void __connman_connection_cleanup(void);
 
+void __connman_connection_update_gateway(void);
+
 #ifdef HAVE_UDEV
 int __connman_udev_init(void);
 void __connman_udev_cleanup(void);