X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fsession.c;h=6000b6d9f130984088b5e4eb344b453c4f79aac1;hb=refs%2Fchanges%2F29%2F267429%2F2;hp=d7f015ad6ed327190f9d71cb484d801e2cc4b201;hpb=9b1a20b1cf7fcfb080c353120bfc796fda56bf8a;p=platform%2Fupstream%2Fconnman.git diff --git a/src/session.c b/src/session.c index d7f015a..6000b6d 100644 --- a/src/session.c +++ b/src/session.c @@ -2,8 +2,8 @@ * * Connection Manager * - * Copyright (C) 2007-2012 Intel Corporation. All rights reserved. - * Copyright (C) 2011 BWM CarIT GmbH. All rights reserved. + * Copyright (C) 2007-2014 Intel Corporation. All rights reserved. + * Copyright (C) 2011-2014 BMW Car IT GmbH. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -28,53 +28,17 @@ #include -#include - #include "connman.h" static DBusConnection *connection; static GHashTable *session_hash; -static connman_bool_t sessionmode; +static GHashTable *service_hash; static struct connman_session *ecall_session; -static GSList *policy_list; - -enum connman_session_trigger { - CONNMAN_SESSION_TRIGGER_UNKNOWN = 0, - CONNMAN_SESSION_TRIGGER_SETTING = 1, - CONNMAN_SESSION_TRIGGER_CONNECT = 2, - CONNMAN_SESSION_TRIGGER_DISCONNECT = 3, - CONNMAN_SESSION_TRIGGER_SERVICE = 4, - CONNMAN_SESSION_TRIGGER_ECALL = 5, -}; - -enum connman_session_reason { - CONNMAN_SESSION_REASON_UNKNOWN = 0, - CONNMAN_SESSION_REASON_CONNECT = 1, - CONNMAN_SESSION_REASON_FREE_RIDE = 2, -}; - -enum connman_session_state { - CONNMAN_SESSION_STATE_DISCONNECTED = 0, - CONNMAN_SESSION_STATE_CONNECTED = 1, - CONNMAN_SESSION_STATE_ONLINE = 2, -}; - -struct service_entry { - /* track why this service was selected */ - enum connman_session_reason reason; - enum connman_service_state state; - const char *name; - struct connman_service *service; - char *ifname; - const char *bearer; - GSList *pending_timeouts; -}; +static uint32_t session_mark = 256; struct session_info { struct connman_session_config config; enum connman_session_state state; - struct service_entry *entry; - enum connman_session_reason reason; }; struct connman_session { @@ -83,51 +47,55 @@ struct connman_session { char *notify_path; guint notify_watch; - struct connman_session_policy *policy; - - connman_bool_t append_all; + bool active; + bool append_all; struct session_info *info; struct session_info *info_last; + struct connman_service *service; + struct connman_service *service_last; struct connman_session_config *policy_config; + GSList *user_allowed_bearers; + char *user_allowed_interface; + + bool ecall; + + enum connman_session_id_type id_type; + struct firewall_context *fw; + uint32_t mark; + int index; + char *addr; + char *gateway; + unsigned char prefixlen; + bool policy_routing; + bool snat_enabled; +}; - connman_bool_t ecall; +struct connman_service_info { + struct connman_service *service; + GSList *sessions; +}; - GSequence *service_list; - GHashTable *service_hash; +struct fw_snat { + GSList *sessions; + int id; + int index; + char *addr; + struct firewall_context *fw; }; -static const char *trigger2string(enum connman_session_trigger trigger) -{ - switch (trigger) { - case CONNMAN_SESSION_TRIGGER_UNKNOWN: - break; - case CONNMAN_SESSION_TRIGGER_SETTING: - return "setting"; - case CONNMAN_SESSION_TRIGGER_CONNECT: - return "connect"; - case CONNMAN_SESSION_TRIGGER_DISCONNECT: - return "disconnect"; - case CONNMAN_SESSION_TRIGGER_SERVICE: - return "service"; - case CONNMAN_SESSION_TRIGGER_ECALL: - return "ecall"; - } +GSList *fw_snat_list; - return NULL; -} +static struct connman_session_policy *policy; +static void session_activate(struct connman_session *session); +static void session_deactivate(struct connman_session *session); +static void update_session_state(struct connman_session *session); -static const char *reason2string(enum connman_session_reason reason) +static void cleanup_service(gpointer data) { - switch (reason) { - case CONNMAN_SESSION_REASON_UNKNOWN: - return "unknown"; - case CONNMAN_SESSION_REASON_CONNECT: - return "connect"; - case CONNMAN_SESSION_REASON_FREE_RIDE: - return "free-ride"; - } + struct connman_service_info *info = data; - return NULL; + g_slist_free(info->sessions); + g_free(info); } static const char *state2string(enum connman_session_state state) @@ -160,7 +128,23 @@ static const char *type2string(enum connman_session_type type) return NULL; } -static enum connman_session_type string2type(const char *type) +enum connman_session_roaming_policy connman_session_parse_roaming_policy(const char *policy) +{ + if (g_strcmp0(policy, "default") == 0) + return CONNMAN_SESSION_ROAMING_POLICY_DEFAULT; + else if (g_strcmp0(policy, "always") == 0) + return CONNMAN_SESSION_ROAMING_POLICY_ALWAYS; + else if (g_strcmp0(policy, "forbidden") == 0) + return CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN; + else if (g_strcmp0(policy, "national") == 0) + return CONNMAN_SESSION_ROAMING_POLICY_NATIONAL; + else if (g_strcmp0(policy, "international") == 0) + return CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL; + else + return CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN; +} + +enum connman_session_type connman_session_parse_connection_type(const char *type) { if (g_strcmp0(type, "any") == 0) return CONNMAN_SESSION_TYPE_ANY; @@ -172,25 +156,24 @@ static enum connman_session_type string2type(const char *type) return CONNMAN_SESSION_TYPE_UNKNOWN; } -static enum connman_service_type bearer2service(const char *bearer) +static int bearer2service(const char *bearer, enum connman_service_type *type) { - if (bearer == NULL) - return CONNMAN_SERVICE_TYPE_UNKNOWN; - if (g_strcmp0(bearer, "ethernet") == 0) - return CONNMAN_SERVICE_TYPE_ETHERNET; + *type = CONNMAN_SERVICE_TYPE_ETHERNET; else if (g_strcmp0(bearer, "wifi") == 0) - return CONNMAN_SERVICE_TYPE_WIFI; - else if (g_strcmp0(bearer, "wimax") == 0) - return CONNMAN_SERVICE_TYPE_WIMAX; + *type = CONNMAN_SERVICE_TYPE_WIFI; + else if (g_strcmp0(bearer, "gadget") == 0) + *type = CONNMAN_SERVICE_TYPE_GADGET; else if (g_strcmp0(bearer, "bluetooth") == 0) - return CONNMAN_SERVICE_TYPE_BLUETOOTH; + *type = CONNMAN_SERVICE_TYPE_BLUETOOTH; else if (g_strcmp0(bearer, "cellular") == 0) - return CONNMAN_SERVICE_TYPE_CELLULAR; + *type = CONNMAN_SERVICE_TYPE_CELLULAR; else if (g_strcmp0(bearer, "vpn") == 0) - return CONNMAN_SERVICE_TYPE_VPN; + *type = CONNMAN_SERVICE_TYPE_VPN; else - return CONNMAN_SERVICE_TYPE_UNKNOWN; + return -EINVAL; + + return 0; } static char *service2bearer(enum connman_service_type type) @@ -198,1205 +181,937 @@ static char *service2bearer(enum connman_service_type type) switch (type) { case CONNMAN_SERVICE_TYPE_ETHERNET: return "ethernet"; + case CONNMAN_SERVICE_TYPE_GADGET: + return "gadget"; case CONNMAN_SERVICE_TYPE_WIFI: return "wifi"; - case CONNMAN_SERVICE_TYPE_WIMAX: - return "wimax"; case CONNMAN_SERVICE_TYPE_BLUETOOTH: return "bluetooth"; case CONNMAN_SERVICE_TYPE_CELLULAR: return "cellular"; case CONNMAN_SERVICE_TYPE_VPN: return "vpn"; - case CONNMAN_SERVICE_TYPE_UNKNOWN: case CONNMAN_SERVICE_TYPE_SYSTEM: case CONNMAN_SERVICE_TYPE_GPS: - case CONNMAN_SERVICE_TYPE_GADGET: + case CONNMAN_SERVICE_TYPE_P2P: + case CONNMAN_SERVICE_TYPE_UNKNOWN: +#if defined TIZEN_EXT_WIFI_MESH + case CONNMAN_SERVICE_TYPE_MESH: +#endif return ""; } return ""; } -static int assign_policy_plugin(struct connman_session *session) +static struct fw_snat *fw_snat_lookup(int index, const char *addr) { - if (session->policy != NULL) - return -EALREADY; - - if (policy_list == NULL) - return 0; + struct fw_snat *fw_snat; + GSList *list; - session->policy = policy_list->data; + for (list = fw_snat_list; list; list = list->next) { + fw_snat = list->data; - return 0; + if (fw_snat->index == index) { + if (g_strcmp0(addr, fw_snat->addr) != 0) + continue; + return fw_snat; + } + } + return NULL; } -static int create_policy_config(struct connman_session *session, - connman_session_config_cb callback, - void *user_data) +static int fw_snat_create(struct connman_session *session, + int index, const char *ifname, const char *addr) { - struct connman_session_config *config; + struct fw_snat *fw_snat; + int err; - if (session->policy == NULL) { - config = connman_session_create_default_config(); - if (config == NULL) - return -ENOMEM; + fw_snat = g_new0(struct fw_snat, 1); - session->policy_config = config; + fw_snat->fw = __connman_firewall_create(); + fw_snat->index = index; + fw_snat->addr = g_strdup(addr); - return 0; + fw_snat->id = __connman_firewall_enable_snat(fw_snat->fw, + index, ifname, addr); + if (fw_snat->id < 0) { + err = fw_snat->id; + goto err; } - return (*session->policy->create)(session, callback, user_data); + fw_snat_list = g_slist_prepend(fw_snat_list, fw_snat); + fw_snat->sessions = g_slist_prepend(fw_snat->sessions, session); + + return 0; +err: + __connman_firewall_destroy(fw_snat->fw); + g_free(fw_snat->addr); + g_free(fw_snat); + return err; } -static void destroy_policy_config(struct connman_session *session) +static void fw_snat_ref(struct connman_session *session, + struct fw_snat *fw_snat) { - if (session->policy == NULL) { - connman_session_free_bearers( - session->policy_config->allowed_bearers); - g_free(session->policy_config); - } else { - (*session->policy->destroy)(session); - } + if (g_slist_find(fw_snat->sessions, session)) + return; + fw_snat->sessions = g_slist_prepend(fw_snat->sessions, session); } -static void probe_policy(struct connman_session_policy *policy) +static void fw_snat_unref(struct connman_session *session, + struct fw_snat *fw_snat) { + fw_snat->sessions = g_slist_remove(fw_snat->sessions, session); + if (fw_snat->sessions) + return; - GHashTableIter iter; - gpointer key, value; - struct connman_session *session; - - DBG("policy %p name %s", policy, policy->name); - - g_hash_table_iter_init(&iter, session_hash); - - while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) { - session = value; - - if (session->policy != NULL) - continue; + fw_snat_list = g_slist_remove(fw_snat_list, fw_snat); - assign_policy_plugin(session); - } + __connman_firewall_disable_snat(fw_snat->fw); + __connman_firewall_destroy(fw_snat->fw); + g_free(fw_snat); } -static void remove_policy(struct connman_session_policy *policy) +static int init_firewall_session(struct connman_session *session) { - GHashTableIter iter; - gpointer key, value; - struct connman_session *session; + struct firewall_context *fw; + int err; + struct connman_ipconfig *ipconfig = NULL; + const char *addr = NULL; - DBG("policy %p name %s", policy, policy->name); + if (session->policy_config->id_type == CONNMAN_SESSION_ID_TYPE_UNKNOWN && + !session->info->config.source_ip_rule) + return 0; - g_hash_table_iter_init(&iter, session_hash); + DBG(""); - while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) { - session = value; + if (session->info->config.source_ip_rule) { + ipconfig = __connman_service_get_ip4config(session->service); + if (session->policy_config->id_type == CONNMAN_SESSION_ID_TYPE_UNKNOWN && !ipconfig) + return 0; + } - if (session->policy != policy) - continue; + fw = __connman_firewall_create(); + if (!fw) + return -ENOMEM; + + if (session->info->config.source_ip_rule && ipconfig) { + addr = __connman_ipconfig_get_local(ipconfig); + } - session->policy = NULL; - assign_policy_plugin(session); + err =__connman_firewall_enable_marking(fw, + session->policy_config->id_type, + session->policy_config->id, + addr, session->mark); + if (err < 0) { + __connman_firewall_destroy(fw); + return err; } + session->id_type = session->policy_config->id_type; + session->fw = fw; + + return 0; } -static gint compare_priority(gconstpointer a, gconstpointer b) +static void cleanup_firewall_session(struct connman_session *session) { - const struct connman_session_policy *policy1 = a; - const struct connman_session_policy *policy2 = b; + if (!session->fw) + return; - return policy2->priority - policy1->priority; -} + __connman_firewall_disable_marking(session->fw); + __connman_firewall_disable_snat(session->fw); + __connman_firewall_destroy(session->fw); + session->fw = NULL; +} -int connman_session_policy_register(struct connman_session_policy *policy) +static int init_routing_table(struct connman_session *session) { - DBG("name %s", policy->name); - - if (policy->create == NULL || policy->destroy == NULL) - return -EINVAL; + int err; - policy_list = g_slist_insert_sorted(policy_list, policy, - compare_priority); + if (session->policy_config->id_type == CONNMAN_SESSION_ID_TYPE_UNKNOWN && + !session->info->config.source_ip_rule) + return 0; - probe_policy(policy); + if (!session->service) + return 0; - return 0; -} + DBG(""); -void connman_session_policy_unregister(struct connman_session_policy *policy) -{ - DBG("name %s", policy->name); + err = __connman_inet_add_fwmark_rule(session->mark, + AF_INET, session->mark); + if (err < 0) + return err; - policy_list = g_slist_remove(policy_list, policy); + err = __connman_inet_add_fwmark_rule(session->mark, + AF_INET6, session->mark); + if (err < 0) + __connman_inet_del_fwmark_rule(session->mark, + AF_INET, session->mark); + session->policy_routing = true; - remove_policy(policy); + return err; } -struct connman_session_config *connman_session_create_default_config(void) +static void del_default_route(struct connman_session *session) { - struct connman_session_config *config; + if (!session->gateway) + return; - config = g_try_new0(struct connman_session_config, 1); - if (config == NULL) - return NULL; + DBG("index %d routing table %d default gateway %s/%u", + session->index, session->mark, session->gateway, session->prefixlen); - config->priority = FALSE; - config->roaming_policy = CONNMAN_SESSION_ROAMING_POLICY_DEFAULT; - config->type = CONNMAN_SESSION_TYPE_ANY; - config->ecall = FALSE; - config->allowed_bearers = connman_session_allowed_bearers_any(); - if (config->allowed_bearers == NULL) { - g_free(config); - return NULL; - } + __connman_inet_del_subnet_from_table(session->mark, + session->index, session->gateway, session->prefixlen); - return config; + __connman_inet_del_default_from_table(session->mark, + session->index, session->gateway); + g_free(session->gateway); + session->gateway = NULL; + session->prefixlen = 0; + session->index = -1; } -static enum connman_session_type apply_policy_on_type( - enum connman_session_type policy, - enum connman_session_type type) +static void add_default_route(struct connman_session *session) { - if (type == CONNMAN_SESSION_TYPE_UNKNOWN) - return CONNMAN_SESSION_TYPE_UNKNOWN; + struct connman_ipconfig *ipconfig; + int err; + struct in_addr addr = { INADDR_ANY }; - if (policy == CONNMAN_SESSION_TYPE_ANY) - return type; + if (!session->service) + return; - if (policy == CONNMAN_SESSION_TYPE_LOCAL) - return CONNMAN_SESSION_TYPE_LOCAL; + ipconfig = __connman_service_get_ip4config(session->service); + session->index = __connman_ipconfig_get_index(ipconfig); + session->gateway = g_strdup(__connman_ipconfig_get_gateway(ipconfig)); - return CONNMAN_SESSION_TYPE_INTERNET; -} + if (!session->gateway) + session->gateway = g_strdup(inet_ntoa(addr)); -static void cleanup_bearer(gpointer data) -{ - struct connman_session_bearer *bearer = data; + session->prefixlen = __connman_ipconfig_get_prefixlen(ipconfig); - g_free(bearer); -} + DBG("index %d routing table %d default gateway %s/%u", + session->index, session->mark, session->gateway, session->prefixlen); -void connman_session_free_bearers(GSList *bearers) -{ - g_slist_free_full(bearers, cleanup_bearer); + err = __connman_inet_add_default_to_table(session->mark, + session->index, session->gateway); + if (err < 0) + DBG("session %p %s", session, strerror(-err)); + + err = __connman_inet_add_subnet_to_table(session->mark, + session->index, session->gateway, session->prefixlen); + if (err < 0) + DBG("session add subnet route %p %s", session, strerror(-err)); } -static int session_parse_allowed_bearers(DBusMessageIter *iter, GSList **list) +static void del_nat_rules(struct connman_session *session) { - struct connman_session_bearer *bearer; - DBusMessageIter array; + struct fw_snat *fw_snat; - dbus_message_iter_recurse(iter, &array); + if (!session->snat_enabled) + return; - *list = NULL; + session->snat_enabled = false; + fw_snat = fw_snat_lookup(session->index, session->addr); - while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) { - char *bearer_name = NULL; + if (!fw_snat) + return; - dbus_message_iter_get_basic(&array, &bearer_name); + fw_snat_unref(session, fw_snat); +} - bearer = g_try_new0(struct connman_session_bearer, 1); - if (bearer == NULL) { - connman_session_free_bearers(*list); - *list = NULL; - return -ENOMEM; - } +static void add_nat_rules(struct connman_session *session) +{ + struct connman_ipconfig *ipconfig; + struct fw_snat *fw_snat; + const char *addr; + int index, err; + char *ifname; - bearer->service_type = bearer2service(bearer_name); + if (!session->service) + return; - if (bearer->service_type == CONNMAN_SERVICE_TYPE_UNKNOWN && - g_strcmp0(bearer_name, "*") == 0) { - bearer->match_all = TRUE; - } else { - bearer->match_all = FALSE; - } + ipconfig = __connman_service_get_ip4config(session->service); + index = __connman_ipconfig_get_index(ipconfig); + ifname = connman_inet_ifname(index); + addr = __connman_ipconfig_get_local(ipconfig); - *list = g_slist_append(*list, bearer); + if (!addr) + return; - dbus_message_iter_next(&array); + g_free(session->addr); + session->addr = g_strdup(addr); + + session->snat_enabled = true; + fw_snat = fw_snat_lookup(index, session->addr); + if (fw_snat) { + fw_snat_ref(session, fw_snat); + return; } - return 0; + err = fw_snat_create(session, index, ifname, addr); + if (err < 0) { + DBG("failed to add SNAT rule"); + session->snat_enabled = false; + } + + g_free(ifname); } -static struct connman_session_bearer *clone_bearer( - struct connman_session_bearer *orig) +uint32_t connman_session_firewall_get_fwmark(struct connman_session *session) { - struct connman_session_bearer *bearer; + return session->mark; +} - bearer = g_try_new0(struct connman_session_bearer, 1); - if (bearer == NULL) - return NULL; +static void cleanup_routing_table(struct connman_session *session) +{ + DBG(""); - bearer->match_all = orig->match_all; - bearer->service_type = orig->service_type; + if (session->policy_routing) { + __connman_inet_del_fwmark_rule(session->mark, + AF_INET6, session->mark); - return bearer; + __connman_inet_del_fwmark_rule(session->mark, + AF_INET, session->mark); + session->policy_routing = false; + } + + del_default_route(session); } -static int filter_bearer(GSList *policy_bearers, - struct connman_session_bearer *bearer, - GSList **list) +static void update_firewall(struct connman_session *session) { - struct connman_session_bearer *policy, *tmp; - GSList *it; - - if (policy_bearers == NULL) - goto clone; + cleanup_firewall_session(session); + init_firewall_session(session); +} - for (it = policy_bearers; it != NULL; it = it->next) { - policy = it->data; +static void update_routing_table(struct connman_session *session) +{ + cleanup_routing_table(session); + init_routing_table(session); + add_default_route(session); +} - if (policy->match_all == FALSE && - bearer->service_type != policy->service_type) - continue; +static void cleanup_nat_rules(struct connman_session *session) +{ + del_nat_rules(session); +} - goto clone; +static void destroy_policy_config(struct connman_session *session) +{ + if (!policy) { + g_free(session->policy_config); + return; } - *list = NULL; + policy->destroy(session); +} - return 0; +static void free_session(struct connman_session *session) +{ + if (!session) + return; -clone: - tmp = clone_bearer(bearer); - if (tmp == NULL) { - connman_session_free_bearers(*list); - *list = NULL; - return -ENOMEM; - } - *list = g_slist_append(*list, tmp); + if (session->notify_watch > 0) + g_dbus_remove_watch(connection, session->notify_watch); - return 0; + g_dbus_unregister_interface(connection, session->session_path, + CONNMAN_SESSION_INTERFACE); + + destroy_policy_config(session); + g_slist_free(session->info->config.allowed_bearers); + g_free(session->info->config.allowed_interface); + g_free(session->owner); + g_free(session->session_path); + g_free(session->notify_path); + g_free(session->info); + g_free(session->info_last); + g_free(session->gateway); + g_free(session->addr); + + g_free(session); } -static connman_bool_t is_bearer_valid(struct connman_session_bearer *bearer) +static void set_active_session(struct connman_session *session, bool enable) { - if (bearer->match_all == FALSE && - bearer->service_type == CONNMAN_SERVICE_TYPE_UNKNOWN) - return FALSE; - return TRUE; + if (policy && policy->session_changed) + policy->session_changed(session, enable, + session->info->config.allowed_bearers); + + __connman_service_set_active_session(enable, + session->info->config.allowed_bearers); } -static int apply_policy_on_bearers(GSList *policy_bearers, GSList *bearers, - GSList **list) +static void cleanup_session(gpointer user_data) { - struct connman_session_bearer *bearer; - GSList *it; - int err; + struct connman_session *session = user_data; - *list = NULL; + DBG("remove %s", session->session_path); - for (it = bearers; it != NULL; it = it->next) { - bearer = it->data; + cleanup_nat_rules(session); + cleanup_routing_table(session); + cleanup_firewall_session(session); - if (is_bearer_valid(bearer) == FALSE) - continue; + if (session->active) + set_active_session(session, false); - err = filter_bearer(policy_bearers, bearer, list); - if (err < 0) - return err; - } + session_deactivate(session); + update_session_state(session); - return 0; + g_slist_free(session->user_allowed_bearers); + g_free(session->user_allowed_interface); + + free_session(session); } -GSList *connman_session_allowed_bearers_any(void) -{ - struct connman_session_bearer *bearer; - GSList *list = NULL; +struct creation_data { + DBusMessage *pending; + struct connman_session *session; - bearer = g_try_new0(struct connman_session_bearer, 1); - if (bearer == NULL) - return NULL; + /* user config */ + enum connman_session_type type; + GSList *allowed_bearers; + char *allowed_interface; + bool source_ip_rule; + char *context_identifier; +}; - bearer->match_all = TRUE; - bearer->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN; +static void cleanup_creation_data(struct creation_data *creation_data) +{ + if (!creation_data) + return; - list = g_slist_append(list, bearer); + if (creation_data->pending) + dbus_message_unref(creation_data->pending); + if (creation_data->context_identifier) + g_free(creation_data->context_identifier); - return list; + g_slist_free(creation_data->allowed_bearers); + g_free(creation_data->allowed_interface); + g_free(creation_data); } -static void append_allowed_bearers(DBusMessageIter *iter, void *user_data) +static int create_policy_config(struct connman_session *session, + connman_session_config_func_t cb, + struct creation_data *creation_data) { - struct session_info *info = user_data; - GSList *list; - - for (list = info->config.allowed_bearers; - list != NULL; list = list->next) { - struct connman_session_bearer *bearer = list->data; - const char *name = - __connman_service_type2string(bearer->service_type); + struct connman_session_config *config; - if (name == NULL) - name = "*"; + if (!policy) { + config = connman_session_create_default_config(); + if (!config) { + free_session(session); + cleanup_creation_data(creation_data); + return -ENOMEM; + } - dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, - &name); + return cb(session, config, creation_data, 0); } + + return policy->create(session, cb, creation_data); } -static void append_ipconfig_ipv4(DBusMessageIter *iter, void *user_data) +int connman_session_policy_register(struct connman_session_policy *plugin) { - struct connman_service *service = user_data; - struct connman_ipconfig *ipconfig_ipv4; + if (policy) + return -EINVAL; - if (service == NULL) - return; + if (!plugin->create || !plugin->destroy) + return -EINVAL; - if (__connman_service_is_connected_state(service, - CONNMAN_IPCONFIG_TYPE_IPV4) == FALSE) { - return; - } + DBG("name %s", plugin->name); - ipconfig_ipv4 = __connman_service_get_ip4config(service); - if (ipconfig_ipv4 == NULL) - return; + policy = plugin; - __connman_ipconfig_append_ipv4(ipconfig_ipv4, iter); + return 0; } -static void append_ipconfig_ipv6(DBusMessageIter *iter, void *user_data) +void connman_session_policy_unregister(struct connman_session_policy *plugin) { - struct connman_service *service = user_data; - struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6; - - if (service == NULL) + if (plugin != policy) return; - if (__connman_service_is_connected_state(service, - CONNMAN_IPCONFIG_TYPE_IPV6) == FALSE) { - return; - } + DBG("name %s", policy->name); - ipconfig_ipv4 = __connman_service_get_ip4config(service); - ipconfig_ipv6 = __connman_service_get_ip6config(service); - if (ipconfig_ipv6 == NULL) - return; - - __connman_ipconfig_append_ipv6(ipconfig_ipv6, iter, ipconfig_ipv4); + policy = NULL; } -static void append_notify(DBusMessageIter *dict, - struct connman_session *session) -{ - struct session_info *info = session->info; - struct session_info *info_last = session->info_last; - struct connman_service *service; - const char *name, *ifname, *bearer; - - if (session->append_all == TRUE || - info->state != info_last->state) { - const char *state = state2string(info->state); - - connman_dbus_dict_append_basic(dict, "State", - DBUS_TYPE_STRING, - &state); - info_last->state = info->state; - } - - if (session->append_all == TRUE || - info->entry != info_last->entry) { - if (info->entry == NULL) { - name = ""; - ifname = ""; - service = NULL; - bearer = ""; - } else { - name = info->entry->name; - ifname = info->entry->ifname; - service = info->entry->service; - bearer = info->entry->bearer; - } - - connman_dbus_dict_append_basic(dict, "Name", - DBUS_TYPE_STRING, - &name); - - connman_dbus_dict_append_dict(dict, "IPv4", - append_ipconfig_ipv4, - service); - - connman_dbus_dict_append_dict(dict, "IPv6", - append_ipconfig_ipv6, - service); - - connman_dbus_dict_append_basic(dict, "Interface", - DBUS_TYPE_STRING, - &ifname); - - connman_dbus_dict_append_basic(dict, "Bearer", - DBUS_TYPE_STRING, - &bearer); - - info_last->entry = info->entry; - } - - if (session->append_all == TRUE || - info->config.type != info_last->config.type) { - const char *type = type2string(info->config.type); - - connman_dbus_dict_append_basic(dict, "ConnectionType", - DBUS_TYPE_STRING, - &type); - info_last->config.type = info->config.type; - } - - if (session->append_all == TRUE || - info->config.allowed_bearers != info_last->config.allowed_bearers) { - connman_dbus_dict_append_array(dict, "AllowedBearers", - DBUS_TYPE_STRING, - append_allowed_bearers, - info); - info_last->config.allowed_bearers = info->config.allowed_bearers; - } - - session->append_all = FALSE; -} +static int default_bearers[] = { + CONNMAN_SERVICE_TYPE_ETHERNET, + CONNMAN_SERVICE_TYPE_WIFI, + CONNMAN_SERVICE_TYPE_BLUETOOTH, + CONNMAN_SERVICE_TYPE_CELLULAR, + CONNMAN_SERVICE_TYPE_GADGET, +}; -static connman_bool_t is_type_matching_state(enum connman_session_state *state, - enum connman_session_type type) +static void add_default_bearer_types(GSList **list) { - switch (type) { - case CONNMAN_SESSION_TYPE_UNKNOWN: - return FALSE; - case CONNMAN_SESSION_TYPE_ANY: - return TRUE; - case CONNMAN_SESSION_TYPE_LOCAL: - if (*state >= CONNMAN_SESSION_STATE_CONNECTED) { - *state = CONNMAN_SESSION_STATE_CONNECTED; - return TRUE; - } + unsigned int i; - break; - case CONNMAN_SESSION_TYPE_INTERNET: - if (*state == CONNMAN_SESSION_STATE_ONLINE) - return TRUE; - break; - } - - return FALSE; + for (i = 0; i < G_N_ELEMENTS(default_bearers); i++) + *list = g_slist_append(*list, + GINT_TO_POINTER(default_bearers[i])); } -static connman_bool_t compute_notifiable_changes(struct connman_session *session) +void connman_session_set_default_config(struct connman_session_config *config) { - struct session_info *info_last = session->info_last; - struct session_info *info = session->info; - - if (session->append_all == TRUE) - return TRUE; + config->id_type = CONNMAN_SESSION_ID_TYPE_UNKNOWN; + g_free(config->id); + config->id = NULL; - if (info->state != info_last->state) - return TRUE; - - if (info->entry != info_last->entry && - info->state >= CONNMAN_SESSION_STATE_CONNECTED) - return TRUE; - - if (info->config.allowed_bearers != info_last->config.allowed_bearers || - info->config.type != info_last->config.type) - return TRUE; + config->priority = FALSE; + config->roaming_policy = CONNMAN_SESSION_ROAMING_POLICY_DEFAULT; + config->type = CONNMAN_SESSION_TYPE_ANY; + config->ecall = FALSE; - return FALSE; + g_slist_free(config->allowed_bearers); + config->allowed_bearers = NULL; + add_default_bearer_types(&config->allowed_bearers); } -static gboolean session_notify(gpointer user_data) +struct connman_session_config *connman_session_create_default_config(void) { - struct connman_session *session = user_data; - DBusMessage *msg; - DBusMessageIter array, dict; - - if (compute_notifiable_changes(session) == FALSE) - return FALSE; - - DBG("session %p owner %s notify_path %s", session, - session->owner, session->notify_path); - - msg = dbus_message_new_method_call(session->owner, session->notify_path, - CONNMAN_NOTIFICATION_INTERFACE, - "Update"); - if (msg == NULL) - return FALSE; - - dbus_message_iter_init_append(msg, &array); - connman_dbus_dict_open(&array, &dict); - - append_notify(&dict, session); + struct connman_session_config *config; - connman_dbus_dict_close(&array, &dict); + config = g_new0(struct connman_session_config, 1); + connman_session_set_default_config(config); - g_dbus_send_message(connection, msg); - - return FALSE; + return config; } -static void ipconfig_ipv4_changed(struct connman_session *session) +static enum connman_session_type apply_policy_on_type( + enum connman_session_type policy, + enum connman_session_type type) { - struct session_info *info = session->info; + if (type == CONNMAN_SESSION_TYPE_UNKNOWN) + return CONNMAN_SESSION_TYPE_UNKNOWN; - connman_dbus_setting_changed_dict(session->owner, session->notify_path, - "IPv4", append_ipconfig_ipv4, - info->entry->service); -} + if (policy == CONNMAN_SESSION_TYPE_ANY) + return type; -static void ipconfig_ipv6_changed(struct connman_session *session) -{ - struct session_info *info = session->info; + if (policy == CONNMAN_SESSION_TYPE_LOCAL) + return CONNMAN_SESSION_TYPE_LOCAL; - connman_dbus_setting_changed_dict(session->owner, session->notify_path, - "IPv6", append_ipconfig_ipv6, - info->entry->service); + return CONNMAN_SESSION_TYPE_INTERNET; } -static connman_bool_t service_type_match(struct connman_session *session, - struct connman_service *service) +int connman_session_parse_bearers(const char *token, GSList **list) { - struct session_info *info = session->info; - GSList *list; - - for (list = info->config.allowed_bearers; - list != NULL; list = list->next) { - struct connman_session_bearer *bearer = list->data; - enum connman_service_type service_type; + enum connman_service_type bearer; + int err; - if (bearer->match_all == TRUE) - return TRUE; + if (g_strcmp0(token, "") == 0) + return 0; - service_type = connman_service_get_type(service); - if (bearer->service_type == service_type) - return TRUE; + if (g_strcmp0(token, "*") == 0) { + add_default_bearer_types(list); + return 0; } - return FALSE; -} - -static connman_bool_t service_match(struct connman_session *session, - struct connman_service *service) -{ - if (service_type_match(session, service) == FALSE) - return FALSE; - - return TRUE; -} + err = bearer2service(token, &bearer); + if (err < 0) + return err; -static int service_type_weight(enum connman_service_type type) -{ - /* - * The session doesn't care which service - * to use. Nevertheless we have to sort them - * according their type. The ordering is - * - * 1. Ethernet - * 2. Bluetooth - * 3. WiFi/WiMAX - * 4. Cellular - */ - - switch (type) { - case CONNMAN_SERVICE_TYPE_ETHERNET: - return 4; - case CONNMAN_SERVICE_TYPE_BLUETOOTH: - return 3; - case CONNMAN_SERVICE_TYPE_WIFI: - case CONNMAN_SERVICE_TYPE_WIMAX: - return 2; - case CONNMAN_SERVICE_TYPE_CELLULAR: - return 1; - case CONNMAN_SERVICE_TYPE_UNKNOWN: - case CONNMAN_SERVICE_TYPE_SYSTEM: - case CONNMAN_SERVICE_TYPE_GPS: - case CONNMAN_SERVICE_TYPE_VPN: - case CONNMAN_SERVICE_TYPE_GADGET: - break; - } + *list = g_slist_append(*list, GINT_TO_POINTER(bearer)); return 0; } -static gint sort_allowed_bearers(struct connman_service *service_a, - struct connman_service *service_b, - struct connman_session *session) +static int parse_bearers(DBusMessageIter *iter, GSList **list) { - struct session_info *info = session->info; - GSList *list; - enum connman_service_type type_a, type_b; - int weight_a, weight_b; - - type_a = connman_service_get_type(service_a); - type_b = connman_service_get_type(service_b); - - for (list = info->config.allowed_bearers; - list != NULL; list = list->next) { - struct connman_session_bearer *bearer = list->data; + DBusMessageIter array; + int type, err; - if (bearer->match_all == TRUE) { - if (type_a != type_b) { - weight_a = service_type_weight(type_a); - weight_b = service_type_weight(type_b); + dbus_message_iter_recurse(iter, &array); - if (weight_a > weight_b) - return -1; + *list = NULL; - if (weight_a < weight_b) - return 1; + while ((type = dbus_message_iter_get_arg_type(&array)) != + DBUS_TYPE_INVALID) { + char *bearer_name = NULL; - return 0; - } + if (type != DBUS_TYPE_STRING) { + g_slist_free(*list); + *list = NULL; + return -EINVAL; } - if (type_a == bearer->service_type && - type_b == bearer->service_type) { - return 0; - } + dbus_message_iter_get_basic(&array, &bearer_name); - if (type_a == bearer->service_type && - type_b != bearer->service_type) { - return -1; + err = connman_session_parse_bearers(bearer_name, list); + if (err < 0) { + g_slist_free(*list); + *list = NULL; + return err; } - if (type_a != bearer->service_type && - type_b == bearer->service_type) { - return 1; - } + dbus_message_iter_next(&array); } return 0; } -static gint sort_services(gconstpointer a, gconstpointer b, gpointer user_data) -{ - struct service_entry *entry_a = (void *)a; - struct service_entry *entry_b = (void *)b; - struct connman_session *session = user_data; - - return sort_allowed_bearers(entry_a->service, entry_b->service, - session); -} - -static void free_session(struct connman_session *session) -{ - destroy_policy_config(session); - connman_session_free_bearers(session->info->config.allowed_bearers); - g_free(session->owner); - g_free(session->session_path); - g_free(session->notify_path); - g_free(session->info); - g_free(session->info_last); - - g_free(session); -} - -static void cleanup_session(gpointer user_data) +static void filter_bearer(GSList *bearers, + enum connman_service_type policy, + GSList **list) { - struct connman_session *session = user_data; - struct session_info *info = session->info; + enum connman_service_type bearer; + GSList *it; - DBG("remove %s", session->session_path); + if (!bearers) + return; - g_hash_table_destroy(session->service_hash); - g_sequence_free(session->service_list); + for (it = bearers; it; it = it->next) { + bearer = GPOINTER_TO_INT(it->data); - if (info->entry != NULL && - info->entry->reason == CONNMAN_SESSION_REASON_CONNECT) { - __connman_service_disconnect(info->entry->service); - } - - free_session(session); -} + if (policy != bearer) + continue; -static enum connman_session_state service_to_session_state(enum connman_service_state state) -{ - switch (state) { - case CONNMAN_SERVICE_STATE_UNKNOWN: - case CONNMAN_SERVICE_STATE_IDLE: - case CONNMAN_SERVICE_STATE_ASSOCIATION: - case CONNMAN_SERVICE_STATE_CONFIGURATION: - case CONNMAN_SERVICE_STATE_DISCONNECT: - case CONNMAN_SERVICE_STATE_FAILURE: - break; - case CONNMAN_SERVICE_STATE_READY: - return CONNMAN_SESSION_STATE_CONNECTED; - case CONNMAN_SERVICE_STATE_ONLINE: - return CONNMAN_SESSION_STATE_ONLINE; + *list = g_slist_append(*list, GINT_TO_POINTER(bearer)); + return; } - - return CONNMAN_SESSION_STATE_DISCONNECTED; } -static connman_bool_t is_connected(enum connman_service_state state) +static void apply_policy_on_bearers(GSList *policy_bearers, GSList *bearers, + GSList **list) { - switch (state) { - case CONNMAN_SERVICE_STATE_UNKNOWN: - case CONNMAN_SERVICE_STATE_IDLE: - case CONNMAN_SERVICE_STATE_ASSOCIATION: - case CONNMAN_SERVICE_STATE_CONFIGURATION: - case CONNMAN_SERVICE_STATE_DISCONNECT: - case CONNMAN_SERVICE_STATE_FAILURE: - break; - case CONNMAN_SERVICE_STATE_READY: - case CONNMAN_SERVICE_STATE_ONLINE: - return TRUE; - } - - return FALSE; -} + enum connman_service_type policy_bearer; + GSList *it; -static connman_bool_t is_connecting(enum connman_service_state state) -{ - switch (state) { - case CONNMAN_SERVICE_STATE_UNKNOWN: - case CONNMAN_SERVICE_STATE_IDLE: - break; - case CONNMAN_SERVICE_STATE_ASSOCIATION: - case CONNMAN_SERVICE_STATE_CONFIGURATION: - return TRUE; - case CONNMAN_SERVICE_STATE_DISCONNECT: - case CONNMAN_SERVICE_STATE_FAILURE: - case CONNMAN_SERVICE_STATE_READY: - case CONNMAN_SERVICE_STATE_ONLINE: - break; - } + *list = NULL; - return FALSE; -} + for (it = policy_bearers; it; it = it->next) { + policy_bearer = GPOINTER_TO_INT(it->data); -static connman_bool_t explicit_connect(enum connman_session_reason reason) -{ - switch (reason) { - case CONNMAN_SESSION_REASON_UNKNOWN: - case CONNMAN_SESSION_REASON_FREE_RIDE: - break; - case CONNMAN_SESSION_REASON_CONNECT: - return TRUE; + filter_bearer(bearers, policy_bearer, list); } - - return FALSE; } -static connman_bool_t explicit_disconnect(struct session_info *info) +static char * apply_policy_on_interface(const char *policy_interface, + const char *user_interface) { - if (info->entry == NULL) - return FALSE; - - DBG("reason %s service %p state %d", - reason2string(info->entry->reason), - info->entry->service, info->entry->state); - - if (info->entry->reason == CONNMAN_SESSION_REASON_UNKNOWN) - return FALSE; - - if (explicit_connect(info->entry->reason) == FALSE) - return FALSE; - - if (__connman_service_session_dec(info->entry->service) == FALSE) - return FALSE; - - return TRUE; -} - -struct pending_data { - unsigned int timeout; - struct service_entry *entry; - gboolean (*cb)(gpointer); -}; - -static void pending_timeout_free(gpointer data, gpointer user_data) -{ - struct pending_data *pending = data; - - DBG("pending %p timeout %d", pending, pending->timeout); - g_source_remove(pending->timeout); - g_free(pending); + if (policy_interface) + return g_strdup(policy_interface); + else if (user_interface) + return g_strdup(user_interface); + else + return NULL; } -static void pending_timeout_remove_all(struct service_entry *entry) +const char *connman_session_get_owner(struct connman_session *session) { - DBG(""); - - g_slist_foreach(entry->pending_timeouts, pending_timeout_free, NULL); - g_slist_free(entry->pending_timeouts); - entry->pending_timeouts = NULL; + return session->owner; } -static gboolean pending_timeout_cb(gpointer data) +static void append_allowed_bearers(DBusMessageIter *iter, void *user_data) { - struct pending_data *pending = data; - struct service_entry *entry = pending->entry; - gboolean ret; - - DBG("pending %p timeout %d", pending, pending->timeout); + struct session_info *info = user_data; + GSList *list; - ret = pending->cb(pending->entry); - if (ret == FALSE) { - entry->pending_timeouts = - g_slist_remove(entry->pending_timeouts, - pending); - g_free(pending); - } - return ret; -} + for (list = info->config.allowed_bearers; + list; list = list->next) { + enum connman_service_type bearer = GPOINTER_TO_INT(list->data); + const char *name = __connman_service_type2string(bearer); -static connman_bool_t pending_timeout_add(unsigned int seconds, - gboolean (*cb)(gpointer), - struct service_entry *entry) -{ - struct pending_data *pending = g_try_new0(struct pending_data, 1); + if (!name) + name = ""; - if (pending == NULL || cb == NULL || entry == NULL) { - g_free(pending); - return FALSE; + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, + &name); } - - pending->cb = cb; - pending->entry = entry; - pending->timeout = g_timeout_add_seconds(seconds, pending_timeout_cb, - pending); - entry->pending_timeouts = g_slist_prepend(entry->pending_timeouts, - pending); - - DBG("pending %p entry %p timeout id %d", pending, entry, - pending->timeout); - - return TRUE; } -static gboolean call_disconnect(gpointer user_data) +static void append_ipconfig_ipv4(DBusMessageIter *iter, void *user_data) { - struct service_entry *entry = user_data; - struct connman_service *service = entry->service; - - /* - * TODO: We should mark this entry as pending work. In case - * disconnect fails we just unassign this session from the - * service and can't do anything later on it - */ - DBG("disconnect service %p", service); - __connman_service_disconnect(service); + struct connman_service *service = user_data; + struct connman_ipconfig *ipconfig_ipv4; - return FALSE; -} + if (!service) + return; -static gboolean call_connect(gpointer user_data) -{ - struct service_entry *entry = user_data; - struct connman_service *service = entry->service; + if (!__connman_service_is_connected_state(service, + CONNMAN_IPCONFIG_TYPE_IPV4)) + return; - DBG("connect service %p", service); - __connman_service_connect(service); + ipconfig_ipv4 = __connman_service_get_ip4config(service); + if (!ipconfig_ipv4) + return; - return FALSE; + __connman_ipconfig_append_ipv4(ipconfig_ipv4, iter); } -static void deselect_service(struct session_info *info) +static void append_ipconfig_ipv6(DBusMessageIter *iter, void *user_data) { - struct service_entry *entry; - connman_bool_t disconnect, connected; - - DBG(""); + struct connman_service *service = user_data; + struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6; - if (info->entry == NULL) + if (!service) return; - disconnect = explicit_disconnect(info); - - connected = is_connecting(info->entry->state) == TRUE || - is_connected(info->entry->state) == TRUE; - - info->state = CONNMAN_SESSION_STATE_DISCONNECTED; - info->entry->reason = CONNMAN_SESSION_REASON_UNKNOWN; - - entry = info->entry; - info->entry = NULL; - - DBG("disconnect %d connected %d", disconnect, connected); - - if (disconnect == TRUE && connected == TRUE) - pending_timeout_add(0, call_disconnect, entry); -} - -static void deselect_and_disconnect(struct connman_session *session) -{ - struct session_info *info = session->info; + if (!__connman_service_is_connected_state(service, + CONNMAN_IPCONFIG_TYPE_IPV6)) + return; - deselect_service(info); + ipconfig_ipv4 = __connman_service_get_ip4config(service); + ipconfig_ipv6 = __connman_service_get_ip6config(service); + if (!ipconfig_ipv6) + return; - info->reason = CONNMAN_SESSION_REASON_FREE_RIDE; + __connman_ipconfig_append_ipv6(ipconfig_ipv6, iter, ipconfig_ipv4); } -static void select_connected_service(struct session_info *info, - struct service_entry *entry) +static void append_notify(DBusMessageIter *dict, + struct connman_session *session) { - enum connman_session_state state; + struct session_info *info = session->info; + struct session_info *info_last = session->info_last; + struct connman_service *service; + enum connman_service_type type; + const char *name, *bearer; + char *ifname; + int idx; - state = service_to_session_state(entry->state); - if (is_type_matching_state(&state, info->config.type) == FALSE) - return; + if (session->append_all || info->state != info_last->state) { + const char *state = state2string(info->state); - info->state = state; + connman_dbus_dict_append_basic(dict, "State", + DBUS_TYPE_STRING, + &state); + info_last->state = info->state; + } - info->entry = entry; - info->entry->reason = info->reason; + if (session->append_all || session->service != session->service_last) { + if (session->service) { + service = session->service; + name = __connman_service_get_name(service); + idx = __connman_service_get_index(service); - if (explicit_connect(info->reason) == FALSE) - return; + ifname = connman_inet_ifname(idx); + if (!ifname) + ifname = g_strdup(""); - __connman_service_session_inc(info->entry->service); -} + type = connman_service_get_type(service); + bearer = service2bearer(type); + } else { + service = NULL; + name = ""; + ifname = g_strdup(""); + bearer = ""; + } -static void select_offline_service(struct session_info *info, - struct service_entry *entry) -{ - if (explicit_connect(info->reason) == FALSE) - return; + connman_dbus_dict_append_basic(dict, "Name", + DBUS_TYPE_STRING, + &name); - info->state = service_to_session_state(entry->state); + connman_dbus_dict_append_dict(dict, "IPv4", + append_ipconfig_ipv4, + service); - info->entry = entry; - info->entry->reason = info->reason; + connman_dbus_dict_append_dict(dict, "IPv6", + append_ipconfig_ipv6, + service); - __connman_service_session_inc(info->entry->service); - pending_timeout_add(0, call_connect, entry); -} + connman_dbus_dict_append_basic(dict, "Interface", + DBUS_TYPE_STRING, + &ifname); -static void select_service(struct session_info *info, - struct service_entry *entry) -{ - DBG("service %p", entry->service); + connman_dbus_dict_append_basic(dict, "Bearer", + DBUS_TYPE_STRING, + &bearer); - if (is_connected(entry->state) == TRUE) - select_connected_service(info, entry); - else - select_offline_service(info, entry); -} + g_free(ifname); -static void select_and_connect(struct connman_session *session, - enum connman_session_reason reason) -{ - struct session_info *info = session->info; - struct service_entry *entry = NULL; - GSequenceIter *iter; + session->service_last = session->service; + } - DBG("session %p reason %s", session, reason2string(reason)); + if (session->append_all || + info->config.type != info_last->config.type) { + const char *type = type2string(info->config.type); - info->reason = reason; + connman_dbus_dict_append_basic(dict, "ConnectionType", + DBUS_TYPE_STRING, + &type); + info_last->config.type = info->config.type; + } - iter = g_sequence_get_begin_iter(session->service_list); + if (session->append_all || + info->config.allowed_bearers != info_last->config.allowed_bearers) { + connman_dbus_dict_append_array(dict, "AllowedBearers", + DBUS_TYPE_STRING, + append_allowed_bearers, + info); + info_last->config.allowed_bearers = info->config.allowed_bearers; + } - while (g_sequence_iter_is_end(iter) == FALSE) { - entry = g_sequence_get(iter); + if (session->append_all || + info->config.allowed_interface != info_last->config.allowed_interface) { + char *ifname = info->config.allowed_interface; + if (!ifname) + ifname = "*"; + connman_dbus_dict_append_basic(dict, "AllowedInterface", + DBUS_TYPE_STRING, + &ifname); + info_last->config.allowed_interface = info->config.allowed_interface; + } - switch (entry->state) { - case CONNMAN_SERVICE_STATE_ASSOCIATION: - case CONNMAN_SERVICE_STATE_CONFIGURATION: - case CONNMAN_SERVICE_STATE_READY: - case CONNMAN_SERVICE_STATE_ONLINE: - case CONNMAN_SERVICE_STATE_IDLE: - case CONNMAN_SERVICE_STATE_DISCONNECT: - select_service(info, entry); - return; - case CONNMAN_SERVICE_STATE_UNKNOWN: - case CONNMAN_SERVICE_STATE_FAILURE: - break; - } + if (session->append_all || + info->config.context_identifier != info_last->config.context_identifier) { + char *ifname = info->config.context_identifier; + if (!ifname) + ifname = ""; + connman_dbus_dict_append_basic(dict, "ContextIdentifier", + DBUS_TYPE_STRING, + &ifname); + info_last->config.context_identifier = info->config.context_identifier; + } - iter = g_sequence_iter_next(iter); + if (session->append_all || + info->config.source_ip_rule != info_last->config.source_ip_rule) { + dbus_bool_t source_ip_rule = FALSE; + if (info->config.source_ip_rule) + source_ip_rule = TRUE; + connman_dbus_dict_append_basic(dict, "SourceIPRule", + DBUS_TYPE_BOOLEAN, + &source_ip_rule); + info_last->config.source_ip_rule = info->config.source_ip_rule; } + + session->append_all = false; } -static struct service_entry *create_service_entry(struct connman_service *service, - const char *name, - enum connman_service_state state) +static bool compute_notifiable_changes(struct connman_session *session) { - struct service_entry *entry; - enum connman_service_type type; - int idx; + struct session_info *info_last = session->info_last; + struct session_info *info = session->info; - entry = g_try_new0(struct service_entry, 1); - if (entry == NULL) - return entry; + if (session->append_all) + return true; - entry->reason = CONNMAN_SESSION_REASON_UNKNOWN; - entry->state = state; - if (name != NULL) - entry->name = name; - else - entry->name = ""; - entry->service = service; + if (info->state != info_last->state) + return true; - idx = __connman_service_get_index(entry->service); - entry->ifname = connman_inet_ifname(idx); - if (entry->ifname == NULL) - entry->ifname = g_strdup(""); + if (session->service != session->service_last && + info->state >= CONNMAN_SESSION_STATE_CONNECTED) + return true; - type = connman_service_get_type(entry->service); - entry->bearer = service2bearer(type); + if (info->config.allowed_bearers != info_last->config.allowed_bearers || + info->config.type != info_last->config.type || + info->config.allowed_interface != info_last->config.allowed_interface || + info->config.source_ip_rule != info_last->config.source_ip_rule) + return true; - return entry; + return false; } -static void destroy_service_entry(gpointer data) +static gboolean session_notify(gpointer user_data) { - struct service_entry *entry = data; + struct connman_session *session = user_data; + DBusMessage *msg; + DBusMessageIter array, dict; - pending_timeout_remove_all(entry); - g_free(entry->ifname); + if (!compute_notifiable_changes(session)) + return FALSE; - g_free(entry); -} + DBG("session %p owner %s notify_path %s", session, + session->owner, session->notify_path); -static void populate_service_list(struct connman_session *session) -{ - struct service_entry *entry; - GSequenceIter *iter; + msg = dbus_message_new_method_call(session->owner, session->notify_path, + CONNMAN_NOTIFICATION_INTERFACE, + "Update"); + if (!msg) + return FALSE; - session->service_hash = - g_hash_table_new_full(g_direct_hash, g_direct_equal, - NULL, NULL); - session->service_list = __connman_service_get_list(session, - service_match, - create_service_entry, - destroy_service_entry); + dbus_message_iter_init_append(msg, &array); + connman_dbus_dict_open(&array, &dict); - g_sequence_sort(session->service_list, sort_services, session); + append_notify(&dict, session); - iter = g_sequence_get_begin_iter(session->service_list); + connman_dbus_dict_close(&array, &dict); - while (g_sequence_iter_is_end(iter) == FALSE) { - entry = g_sequence_get(iter); + g_dbus_send_message(connection, msg); - DBG("service %p type %s name %s", entry->service, - service2bearer(connman_service_get_type(entry->service)), - entry->name); + return FALSE; +} - g_hash_table_replace(session->service_hash, - entry->service, iter); +static void ipconfig_ipv4_changed(struct connman_session *session) +{ + connman_dbus_setting_changed_dict(session->owner, session->notify_path, + "IPv4", append_ipconfig_ipv4, + session->service); +} - iter = g_sequence_iter_next(iter); - } +static void ipconfig_ipv6_changed(struct connman_session *session) +{ + connman_dbus_setting_changed_dict(session->owner, session->notify_path, + "IPv6", append_ipconfig_ipv6, + session->service); } -static void session_changed(struct connman_session *session, - enum connman_session_trigger trigger) +int connman_session_config_update(struct connman_session *session) { struct session_info *info = session->info; - struct session_info *info_last = session->info_last; - GSequenceIter *service_iter = NULL, *service_iter_last = NULL; - GSequence *service_list_last; - GHashTable *service_hash_last; + GSList *allowed_bearers; + char *allowed_interface; + int err; + + DBG("session %p", session); /* - * TODO: This only a placeholder for the 'real' algorithm to - * play a bit around. So we are going to improve it step by step. + * We update all configuration even though only one entry + * might have changed. We can still optimize this later. */ - DBG("session %p trigger %s reason %s", session, trigger2string(trigger), - reason2string(info->reason)); - - if (info->entry != NULL) { - enum connman_session_state state; - - state = service_to_session_state(info->entry->state); + if (session->id_type != session->policy_config->id_type) { + cleanup_firewall_session(session); + err = init_firewall_session(session); + if (err < 0) { + connman_session_destroy(session); + return err; + } - if (is_type_matching_state(&state, info->config.type) == TRUE) - info->state = state; + session->id_type = session->policy_config->id_type; } - switch (trigger) { - case CONNMAN_SESSION_TRIGGER_UNKNOWN: - DBG("ignore session changed event"); - return; - case CONNMAN_SESSION_TRIGGER_SETTING: - if (info->config.allowed_bearers != info_last->config.allowed_bearers) { - - service_hash_last = session->service_hash; - service_list_last = session->service_list; - - populate_service_list(session); - - if (info->entry != NULL) { - service_iter_last = g_hash_table_lookup( - service_hash_last, - info->entry->service); - service_iter = g_hash_table_lookup( - session->service_hash, - info->entry->service); - } - - if (service_iter == NULL && service_iter_last != NULL) { - /* - * The currently selected service is - * not part of this session anymore. - */ - deselect_and_disconnect(session); - } - - g_hash_table_remove_all(service_hash_last); - g_sequence_free(service_list_last); - } + apply_policy_on_bearers( + session->policy_config->allowed_bearers, + session->user_allowed_bearers, + &allowed_bearers); - if (info->config.type != info_last->config.type) { - if (info->state >= CONNMAN_SESSION_STATE_CONNECTED && - is_type_matching_state(&info->state, - info->config.type) == FALSE) - deselect_and_disconnect(session); - } + allowed_interface = apply_policy_on_interface( + session->policy_config->allowed_interface, + session->user_allowed_interface); - if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED) { - select_and_connect(session, - CONNMAN_SESSION_REASON_FREE_RIDE); - } + if (session->active) + set_active_session(session, false); - break; - case CONNMAN_SESSION_TRIGGER_ECALL: - /* - * For the time beeing we fallback to normal connect - * strategy. - */ - case CONNMAN_SESSION_TRIGGER_CONNECT: - if (info->state >= CONNMAN_SESSION_STATE_CONNECTED) { - if (info->entry->reason == CONNMAN_SESSION_REASON_CONNECT) - break; - info->entry->reason = CONNMAN_SESSION_REASON_CONNECT; - __connman_service_session_inc(info->entry->service); - break; - } + session->active = false; + session_deactivate(session); - if (info->entry != NULL && - is_connecting(info->entry->state) == TRUE) { - break; - } + g_slist_free(info->config.allowed_bearers); + info->config.allowed_bearers = allowed_bearers; - select_and_connect(session, - CONNMAN_SESSION_REASON_CONNECT); + g_free(info->config.allowed_interface); + info->config.allowed_interface = allowed_interface; - break; - case CONNMAN_SESSION_TRIGGER_DISCONNECT: - deselect_and_disconnect(session); + session_activate(session); - break; - case CONNMAN_SESSION_TRIGGER_SERVICE: - if (info->entry != NULL && - (is_connecting(info->entry->state) == TRUE || - is_connected(info->entry->state) == TRUE)) { - break; - } + info->config.type = apply_policy_on_type( + session->policy_config->type, + info->config.type); - deselect_and_disconnect(session); + info->config.roaming_policy = session->policy_config->roaming_policy; - if (info->reason == CONNMAN_SESSION_REASON_FREE_RIDE) { - select_and_connect(session, info->reason); - } + info->config.ecall = session->policy_config->ecall; + if (info->config.ecall) + ecall_session = session; - break; - } + info->config.priority = session->policy_config->priority; session_notify(session); + + return 0; } static DBusMessage *connect_session(DBusConnection *conn, @@ -1406,14 +1121,21 @@ static DBusMessage *connect_session(DBusConnection *conn, DBG("session %p", session); - if (ecall_session != NULL) { - if (ecall_session->ecall == TRUE && ecall_session != session) + if (ecall_session) { + if (ecall_session->ecall && ecall_session != session) return __connman_error_failed(msg, EBUSY); - session->ecall = TRUE; - session_changed(session, CONNMAN_SESSION_TRIGGER_ECALL); - } else - session_changed(session, CONNMAN_SESSION_TRIGGER_CONNECT); + session->ecall = true; + } + + if (!session->active) { + session->active = true; + set_active_session(session, true); + } + + session_activate(session); + + __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_SESSION); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } @@ -1425,14 +1147,20 @@ static DBusMessage *disconnect_session(DBusConnection *conn, DBG("session %p", session); - if (ecall_session != NULL) { - if (ecall_session->ecall == TRUE && ecall_session != session) + if (ecall_session) { + if (ecall_session->ecall && ecall_session != session) return __connman_error_failed(msg, EBUSY); - session->ecall = FALSE; + session->ecall = false; } - session_changed(session, CONNMAN_SESSION_TRIGGER_DISCONNECT); + if (session->active) { + session->active = false; + set_active_session(session, false); + } + + session_deactivate(session); + update_session_state(session); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } @@ -1449,7 +1177,7 @@ static DBusMessage *change_session(DBusConnection *conn, int err; DBG("session %p", session); - if (dbus_message_iter_init(msg, &iter) == FALSE) + if (!dbus_message_iter_init(msg, &iter)) return __connman_error_invalid_arguments(msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) @@ -1465,31 +1193,69 @@ static DBusMessage *change_session(DBusConnection *conn, switch (dbus_message_iter_get_arg_type(&value)) { case DBUS_TYPE_ARRAY: - if (g_str_equal(name, "AllowedBearers") == TRUE) { - err = session_parse_allowed_bearers(&value, - &allowed_bearers); + if (g_str_equal(name, "AllowedBearers")) { + err = parse_bearers(&value, &allowed_bearers); if (err < 0) - return __connman_error_failed(msg, err); + return __connman_error_failed(msg, -err); + + if (session->active) + set_active_session(session, false); + + session->active = false; + session_deactivate(session); + update_session_state(session); - connman_session_free_bearers(info->config.allowed_bearers); - err = apply_policy_on_bearers( + g_slist_free(info->config.allowed_bearers); + session->user_allowed_bearers = allowed_bearers; + + apply_policy_on_bearers( session->policy_config->allowed_bearers, - allowed_bearers, + session->user_allowed_bearers, &info->config.allowed_bearers); - connman_session_free_bearers(allowed_bearers); - if (err < 0) - return __connman_error_failed(msg, err); + session_activate(session); } else { goto err; } break; case DBUS_TYPE_STRING: - if (g_str_equal(name, "ConnectionType") == TRUE) { + if (g_str_equal(name, "ConnectionType")) { dbus_message_iter_get_basic(&value, &val); info->config.type = apply_policy_on_type( session->policy_config->type, - string2type(val)); + connman_session_parse_connection_type(val)); + } else if (g_str_equal(name, "AllowedInterface")) { + dbus_message_iter_get_basic(&value, &val); + if (session->active) + set_active_session(session, false); + + session->active = false; + session_deactivate(session); + update_session_state(session); + + g_free(session->user_allowed_interface); + /* empty string means allow any interface */ + if (!g_strcmp0(val, "")) + session->user_allowed_interface = NULL; + else + session->user_allowed_interface = g_strdup(val); + + info->config.allowed_interface = apply_policy_on_interface( + session->policy_config->allowed_interface, + session->user_allowed_interface); + + session_activate(session); + } else { + goto err; + } + break; + case DBUS_TYPE_BOOLEAN: + if (g_str_equal(name, "SourceIPRule")) { + dbus_bool_t source_ip_rule; + dbus_message_iter_get_basic(&value, &source_ip_rule); + + info->config.source_ip_rule = source_ip_rule; + update_session_state(session); } else { goto err; } @@ -1498,7 +1264,7 @@ static DBusMessage *change_session(DBusConnection *conn, goto err; } - session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING); + session_notify(session); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); @@ -1523,7 +1289,7 @@ static void release_session(gpointer key, gpointer value, gpointer user_data) session->notify_path, CONNMAN_NOTIFICATION_INTERFACE, "Release"); - if (message == NULL) + if (!message) return; dbus_message_set_no_reply(message, TRUE); @@ -1541,8 +1307,6 @@ static int session_disconnect(struct connman_session *session) g_dbus_unregister_interface(connection, session->session_path, CONNMAN_SESSION_INTERFACE); - deselect_and_disconnect(session); - g_hash_table_remove(session_hash, session->session_path); return 0; @@ -1557,42 +1321,133 @@ static void owner_disconnect(DBusConnection *conn, void *user_data) session_disconnect(session); } -static DBusMessage *destroy_session(DBusConnection *conn, - DBusMessage *msg, void *user_data) -{ - struct connman_session *session = user_data; +static DBusMessage *destroy_session(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + struct connman_session *session = user_data; + + DBG("session %p", session); + + if (ecall_session && ecall_session != session) + return __connman_error_failed(msg, EBUSY); + + session_disconnect(session); + + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} + +static const GDBusMethodTable session_methods[] = { + { GDBUS_METHOD("Destroy", NULL, NULL, destroy_session) }, + { GDBUS_METHOD("Connect", NULL, NULL, connect_session) }, + { GDBUS_METHOD("Disconnect", NULL, NULL, + disconnect_session ) }, + { GDBUS_METHOD("Change", + GDBUS_ARGS({ "name", "s" }, { "value", "v" }), + NULL, change_session) }, + { }, +}; + +static int session_policy_config_cb(struct connman_session *session, + struct connman_session_config *config, + void *user_data, int err) +{ + struct creation_data *creation_data = user_data; + struct session_info *info, *info_last; + DBusMessage *reply; + + DBG("session %p config %p", session, config); + + if (err < 0) + goto err; + + session->policy_config = config; + session->info->config.source_ip_rule = creation_data->source_ip_rule; + + session->mark = session_mark++; + session->index = -1; + + err = init_firewall_session(session); + if (err < 0) + goto err; + + err = init_routing_table(session); + if (err < 0) + goto err; + + info = session->info; + info_last = session->info_last; + + if (session->policy_config->ecall) + ecall_session = session; + + info->state = CONNMAN_SESSION_STATE_DISCONNECTED; + info->config.type = apply_policy_on_type( + session->policy_config->type, + creation_data->type); + info->config.priority = session->policy_config->priority; + info->config.roaming_policy = session->policy_config->roaming_policy; + + session->user_allowed_bearers = creation_data->allowed_bearers; + creation_data->allowed_bearers = NULL; + + session->user_allowed_interface = creation_data->allowed_interface; + creation_data->allowed_interface = NULL; + + apply_policy_on_bearers( + session->policy_config->allowed_bearers, + session->user_allowed_bearers, + &info->config.allowed_bearers); + + info->config.allowed_interface = apply_policy_on_interface( + session->policy_config->allowed_interface, + session->user_allowed_interface); + + g_hash_table_replace(session_hash, session->session_path, session); + + DBG("add %s", session->session_path); + + if (!g_dbus_register_interface(connection, session->session_path, + CONNMAN_SESSION_INTERFACE, + session_methods, NULL, NULL, + session, NULL)) { + connman_error("Failed to register %s", session->session_path); + g_hash_table_remove(session_hash, session->session_path); + err = -EINVAL; + goto err_notify; + } + + reply = g_dbus_create_reply(creation_data->pending, + DBUS_TYPE_OBJECT_PATH, &session->session_path, + DBUS_TYPE_INVALID); + g_dbus_send_message(connection, reply); + creation_data->pending = NULL; + + info_last->state = info->state; + info_last->config.priority = info->config.priority; + info_last->config.roaming_policy = info->config.roaming_policy; + info_last->config.allowed_bearers = info->config.allowed_bearers; + info_last->config.allowed_interface = info->config.allowed_interface; + info_last->config.source_ip_rule = info->config.source_ip_rule; - DBG("session %p", session); + session->append_all = true; - if (ecall_session != NULL && ecall_session != session) - return __connman_error_failed(msg, EBUSY); + cleanup_creation_data(creation_data); - session_disconnect(session); + session_activate(session); - return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); -} + return 0; -static const GDBusMethodTable session_methods[] = { - { GDBUS_METHOD("Destroy", NULL, NULL, destroy_session) }, - { GDBUS_METHOD("Connect", NULL, NULL, connect_session) }, - { GDBUS_METHOD("Disconnect", NULL, NULL, - disconnect_session ) }, - { GDBUS_METHOD("Change", - GDBUS_ARGS({ "name", "s" }, { "value", "v" }), - NULL, change_session) }, - { }, -}; +err: + cleanup_session(session); -static void session_create_cb(struct connman_session *session, - struct connman_session_config *config, - void *user_data, int err) -{ - DBG("session %p config %p", session, config); +err_notify: + reply = __connman_error_failed(creation_data->pending, -err); + g_dbus_send_message(connection, reply); + creation_data->pending = NULL; - if (err != 0) - return; + cleanup_creation_data(creation_data); - session->policy_config = config; + return err; } int __connman_session_create(DBusMessage *msg) @@ -1601,18 +1456,17 @@ int __connman_session_create(DBusMessage *msg) char *session_path = NULL; DBusMessageIter iter, array; struct connman_session *session = NULL; - struct session_info *info, *info_last; - enum connman_session_type type = CONNMAN_SESSION_TYPE_ANY; - GSList *allowed_bearers = NULL; - connman_bool_t allowed_bearers_valid = FALSE; - connman_bool_t type_valid = FALSE; - int err; + struct creation_data *creation_data = NULL; + bool user_allowed_bearers = false; + bool user_connection_type = false; + int err, i; + char *str; owner = dbus_message_get_sender(msg); DBG("owner %s", owner); - if (ecall_session != NULL && ecall_session->ecall == TRUE) { + if (ecall_session && ecall_session->ecall) { /* * If there is an emergency call already going on, * ignore session creation attempt @@ -1621,6 +1475,14 @@ int __connman_session_create(DBusMessage *msg) goto err; } + creation_data = g_try_new0(struct creation_data, 1); + if (!creation_data) { + err = -ENOMEM; + goto err; + } + + creation_data->pending = dbus_message_ref(msg); + dbus_message_iter_init(msg, &iter); dbus_message_iter_recurse(&iter, &array); @@ -1636,164 +1498,157 @@ int __connman_session_create(DBusMessage *msg) switch (dbus_message_iter_get_arg_type(&value)) { case DBUS_TYPE_ARRAY: - if (g_str_equal(key, "AllowedBearers") == TRUE) { - err = session_parse_allowed_bearers(&value, - &allowed_bearers); + if (g_str_equal(key, "AllowedBearers")) { + err = parse_bearers(&value, + &creation_data->allowed_bearers); if (err < 0) goto err; - allowed_bearers_valid = TRUE; + user_allowed_bearers = true; } else { - return -EINVAL; + err = -EINVAL; + goto err; } break; case DBUS_TYPE_STRING: - if (g_str_equal(key, "ConnectionType") == TRUE) { + if (g_str_equal(key, "ConnectionType")) { + dbus_message_iter_get_basic(&value, &val); + creation_data->type = + connman_session_parse_connection_type(val); + + user_connection_type = true; + } else if (g_str_equal(key, "ContextIdentifier")) { + dbus_message_iter_get_basic(&value, &val); + creation_data->context_identifier = g_strdup(val); + } else if (g_str_equal(key, "AllowedInterface")) { dbus_message_iter_get_basic(&value, &val); - type = string2type(val); - type_valid = TRUE; + creation_data->allowed_interface = g_strdup(val); + } else { + err = -EINVAL; + goto err; + } + break; + case DBUS_TYPE_BOOLEAN: + if (g_str_equal(key, "SourceIPRule")) { + dbus_bool_t source_ip_rule; + dbus_message_iter_get_basic(&value, &source_ip_rule); + creation_data->source_ip_rule = source_ip_rule; } else { - return -EINVAL; + err = -EINVAL; + goto err; } + break; + default: + err = -EINVAL; + goto err; } + dbus_message_iter_next(&array); } + /* + * If the user hasn't provided a configuration, we set + * the default configuration. + * + * For AllowedBearers this is '*', ... + */ + if (!user_allowed_bearers) { + add_default_bearer_types(&creation_data->allowed_bearers); + if (!creation_data->allowed_bearers) { + err = -ENOMEM; + goto err; + } + } + + /* ... and for ConnectionType it is 'any'. */ + if (!user_connection_type) + creation_data->type = CONNMAN_SESSION_TYPE_ANY; + dbus_message_iter_next(&iter); dbus_message_iter_get_basic(&iter, ¬ify_path); - if (notify_path == NULL) { + if (!notify_path) { err = -EINVAL; goto err; } - session_path = g_strdup_printf("/sessions%s", notify_path); - if (session_path == NULL) { + str = g_strdup(owner); + for (i = 0; str[i] != '\0'; i++) + if (str[i] == ':' || str[i] == '.') + str[i] = '_'; + session_path = g_strdup_printf("/sessions/%s%s", str, notify_path); + g_free(str); + + if (!session_path) { err = -ENOMEM; goto err; } session = g_hash_table_lookup(session_hash, session_path); - if (session != NULL) { + if (session) { + g_free(session_path); session = NULL; err = -EEXIST; goto err; } session = g_try_new0(struct connman_session, 1); - if (session == NULL) { + if (!session) { + g_free(session_path); err = -ENOMEM; goto err; } + creation_data->session = session; + session->session_path = session_path; + session->info = g_try_new0(struct session_info, 1); - if (session->info == NULL) { + if (!session->info) { err = -ENOMEM; goto err; } session->info_last = g_try_new0(struct session_info, 1); - if (session->info_last == NULL) { + if (!session->info_last) { err = -ENOMEM; goto err; } - info = session->info; - info_last = session->info_last; - session->owner = g_strdup(owner); - session->session_path = session_path; session->notify_path = g_strdup(notify_path); session->notify_watch = g_dbus_add_disconnect_watch(connection, session->owner, owner_disconnect, session, NULL); - err = assign_policy_plugin(session); - if (err < 0) - goto err; - err = create_policy_config(session, session_create_cb, NULL); - if (err < 0) - goto err; - - if (session->policy_config->ecall == TRUE) - ecall_session = session; - - info->state = CONNMAN_SESSION_STATE_DISCONNECTED; - if (type_valid == FALSE) - type = CONNMAN_SESSION_TYPE_ANY; - info->config.type = apply_policy_on_type( - session->policy_config->type, - type); - info->config.priority = session->policy_config->priority; - info->config.roaming_policy = session->policy_config->roaming_policy; - info->entry = NULL; - - if (allowed_bearers_valid == FALSE) { - allowed_bearers = connman_session_allowed_bearers_any(); - if (allowed_bearers == NULL) { - err = -ENOMEM; - goto err; - } - } - - err = apply_policy_on_bearers( - session->policy_config->allowed_bearers, - allowed_bearers, - &info->config.allowed_bearers); - if (err < 0) - goto err; - - g_hash_table_replace(session_hash, session->session_path, session); - - DBG("add %s", session->session_path); - - if (g_dbus_register_interface(connection, session->session_path, - CONNMAN_SESSION_INTERFACE, - session_methods, NULL, - NULL, session, NULL) == FALSE) { - connman_error("Failed to register %s", session->session_path); - g_hash_table_remove(session_hash, session->session_path); - session = NULL; - - err = -EINVAL; - goto err; - } - - g_dbus_send_reply(connection, msg, - DBUS_TYPE_OBJECT_PATH, &session->session_path, - DBUS_TYPE_INVALID); - - - populate_service_list(session); - - info_last->state = info->state; - info_last->config.priority = info->config.priority; - info_last->config.roaming_policy = info->config.roaming_policy; - info_last->entry = info->entry; - info_last->config.allowed_bearers = info->config.allowed_bearers; - - session->append_all = TRUE; - - session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING); + err = create_policy_config(session, session_policy_config_cb, + creation_data); + if (err < 0 && err != -EINPROGRESS) + return err; - return 0; + return -EINPROGRESS; err: connman_error("Failed to create session"); - if (session != NULL) { - if (session->info_last != NULL) - g_free(session->info_last); - if (session->info != NULL) - g_free(session->info); - g_free(session); - } + free_session(session); - g_free(session_path); + cleanup_creation_data(creation_data); + return err; +} - connman_session_free_bearers(allowed_bearers); +bool __connman_session_policy_autoconnect(enum connman_service_connect_reason reason) +{ + if (!policy || !policy->autoconnect) + return true; - return err; + return policy->autoconnect(reason); +} + +void connman_session_destroy(struct connman_session *session) +{ + DBG("session %p", session); + + session_disconnect(session); } int __connman_session_destroy(DBusMessage *msg) @@ -1807,131 +1662,320 @@ int __connman_session_destroy(DBusMessage *msg) dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &session_path, DBUS_TYPE_INVALID); - if (session_path == NULL) + if (!session_path) return -EINVAL; session = g_hash_table_lookup(session_hash, session_path); - if (session == NULL) + if (!session) return -EINVAL; if (g_strcmp0(owner, session->owner) != 0) return -EACCES; - session_disconnect(session); + connman_session_destroy(session); return 0; } -connman_bool_t __connman_session_mode() +int connman_session_connect(struct connman_service *service) +{ + DBG("service %p name %s", service, __connman_service_get_name(service)); + + return __connman_service_connect(service, + CONNMAN_SERVICE_CONNECT_REASON_SESSION); +} + +int connman_session_disconnect(struct connman_service *service) +{ + DBG("service %p", service); + + return __connman_service_disconnect(service); +} + +static enum connman_session_state service_to_session_state( + enum connman_service_state state) +{ + switch (state) { + case CONNMAN_SERVICE_STATE_UNKNOWN: + case CONNMAN_SERVICE_STATE_IDLE: + case CONNMAN_SERVICE_STATE_ASSOCIATION: + case CONNMAN_SERVICE_STATE_CONFIGURATION: + case CONNMAN_SERVICE_STATE_DISCONNECT: + case CONNMAN_SERVICE_STATE_FAILURE: + break; + case CONNMAN_SERVICE_STATE_READY: + return CONNMAN_SESSION_STATE_CONNECTED; + case CONNMAN_SERVICE_STATE_ONLINE: + return CONNMAN_SESSION_STATE_ONLINE; + } + + return CONNMAN_SESSION_STATE_DISCONNECTED; +} + +static void update_session_state(struct connman_session *session) { - return sessionmode; + enum connman_service_state service_state; + enum connman_session_state state = CONNMAN_SESSION_STATE_DISCONNECTED; + + if (session->service) { + service_state = connman_service_get_state(session->service); + state = service_to_session_state(service_state); + session->info->state = state; + } + session->info->state = state; + + DBG("session %p state %s", session, state2string(state)); + + update_firewall(session); + del_nat_rules(session); + update_routing_table(session); + add_nat_rules(session); + + if (policy && policy->update_session_state) + policy->update_session_state(session, state); + + session_notify(session); } -void __connman_session_set_mode(connman_bool_t enable) +static bool session_match_service(struct connman_session *session, + struct connman_service *service) { - DBG("enable %d", enable); + enum connman_service_type bearer_type; + enum connman_service_type service_type; + enum connman_service_type current_service_type; + GSList *list; + char *ifname; + + if (policy && policy->allowed) + return policy->allowed(session, service); + + current_service_type = connman_service_get_type(session->service); + + for (list = session->info->config.allowed_bearers; list; list = list->next) { + bearer_type = GPOINTER_TO_INT(list->data); + service_type = connman_service_get_type(service); + ifname = connman_service_get_interface(service); + + +#if defined TIZEN_EXT + if (bearer_type == current_service_type) { + g_free(ifname); + return false; + } +#else + if (bearer_type == current_service_type) + return false; +#endif + + if (bearer_type == service_type && + (session->info->config.allowed_interface == NULL || + !g_strcmp0(session->info->config.allowed_interface, "*") || + !g_strcmp0(session->info->config.allowed_interface, ifname))) { + g_free(ifname); + return true; + } + g_free(ifname); + } + + return false; +} - if (sessionmode != enable) { - sessionmode = enable; +static bool is_session_connected(struct connman_session *session, + enum connman_service_state state) - connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH, - CONNMAN_MANAGER_INTERFACE, "SessionMode", - DBUS_TYPE_BOOLEAN, &sessionmode); +{ + switch (state) { + case CONNMAN_SERVICE_STATE_UNKNOWN: + case CONNMAN_SERVICE_STATE_IDLE: + case CONNMAN_SERVICE_STATE_ASSOCIATION: + case CONNMAN_SERVICE_STATE_CONFIGURATION: + case CONNMAN_SERVICE_STATE_DISCONNECT: + case CONNMAN_SERVICE_STATE_FAILURE: + break; + case CONNMAN_SERVICE_STATE_READY: + if (session->info->config.type == CONNMAN_SESSION_TYPE_INTERNET) + return false; + /* fall through */ + case CONNMAN_SERVICE_STATE_ONLINE: + return true; } - if (sessionmode == TRUE) - __connman_service_disconnect_all(); + return false; } -static void service_add(struct connman_service *service, - const char *name) +static void session_activate(struct connman_session *session) { GHashTableIter iter; - GSequenceIter *iter_service_list; gpointer key, value; - struct connman_session *session; - struct service_entry *entry; - DBG("service %p", service); + if (!service_hash) + return; - g_hash_table_iter_init(&iter, session_hash); + if (policy && policy->get_service_for_session) { + struct connman_service *service; + struct connman_service_info *info; + GSList *service_list = NULL; + enum connman_service_state state = CONNMAN_SESSION_STATE_DISCONNECTED; - while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) { - session = value; + g_hash_table_iter_init(&iter, service_hash); - if (service_match(session, service) == FALSE) - continue; + while (g_hash_table_iter_next(&iter, &key, &value)) { + struct connman_service_info *info = value; + state = connman_service_get_state(info->service); - entry = create_service_entry(service, name, - CONNMAN_SERVICE_STATE_IDLE); - if (entry == NULL) - continue; + if (is_session_connected(session, state)) + service_list = g_slist_prepend(service_list, + info->service); + } + + service_list = g_slist_reverse(service_list); + service = policy->get_service_for_session(session, service_list); + + if (service) { + info = g_hash_table_lookup(service_hash, service); + DBG("session %p add service %p", session, info->service); - iter_service_list = - g_sequence_insert_sorted(session->service_list, - entry, sort_services, + info->sessions = g_slist_prepend(info->sessions, session); + session->service = info->service; + update_session_state(session); + } + + g_slist_free(service_list); + return; + } - g_hash_table_replace(session->service_hash, service, - iter_service_list); + g_hash_table_iter_init(&iter, service_hash); + while (g_hash_table_iter_next(&iter, &key, &value)) { + struct connman_service_info *info = value; + enum connman_service_state state; - session_changed(session, CONNMAN_SESSION_TRIGGER_SERVICE); + state = connman_service_get_state(info->service); + + if (is_session_connected(session, state) && + session_match_service(session, info->service)) { + DBG("session %p add service %p", session, info->service); + + info->sessions = g_slist_prepend(info->sessions, + session); + session->service = info->service; + update_session_state(session); + + return; + } } + + session_notify(session); } -static void service_remove(struct connman_service *service) +static void session_deactivate(struct connman_session *session) { + struct connman_service_info *info; + if (!service_hash) + return; + + if (!session->service) + return; + + info = g_hash_table_lookup(service_hash, session->service); + if (!info) + return; + + info->sessions = g_slist_remove(info->sessions, session); + session->service = NULL; + + session->info->state = CONNMAN_SESSION_STATE_DISCONNECTED; +} + +static void handle_service_state_online(struct connman_service *service, + enum connman_service_state state, + struct connman_service_info *info) +{ GHashTableIter iter; gpointer key, value; - struct connman_session *session; - struct session_info *info; - - DBG("service %p", service); g_hash_table_iter_init(&iter, session_hash); + while (g_hash_table_iter_next(&iter, &key, &value)) { + struct connman_session *session = value; + bool connected; - while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) { - GSequenceIter *seq_iter; - session = value; - info = session->info; + connected = is_session_connected(session, state); + + if (session->service == service) { + if (!connected) { + DBG("session %p remove service %p", session, service); + info->sessions = g_slist_remove(info->sessions, + session); + session->service = NULL; + update_session_state(session); + } + } else if (connected && session_match_service(session, service)) { + DBG("session %p add service %p", session, service); + + info->sessions = g_slist_prepend(info->sessions, + session); + session->service = service; + update_session_state(session); + } + } +} + +static void handle_service_state_offline(struct connman_service *service, + struct connman_service_info *info) +{ + GSList *list; - seq_iter = g_hash_table_lookup(session->service_hash, service); - if (seq_iter == NULL) + for (list = info->sessions; list; list = list->next) { + struct connman_session *session = list->data; + + if (session->service != service) { + connman_warn("session %p should have session %p assigned", + session, service); continue; + } - g_sequence_remove(seq_iter); + DBG("session %p remove service %p", session, service); - if (info->entry != NULL && info->entry->service == service) - info->entry = NULL; - session_changed(session, CONNMAN_SESSION_TRIGGER_SERVICE); + session->service = NULL; + update_session_state(session); + session_activate(session); } } static void service_state_changed(struct connman_service *service, - enum connman_service_state state) + enum connman_service_state state) { - GHashTableIter iter; - gpointer key, value; + struct connman_service_info *info; DBG("service %p state %d", service, state); - g_hash_table_iter_init(&iter, session_hash); + info = g_hash_table_lookup(service_hash, service); - while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) { - struct connman_session *session = value; - GSequenceIter *service_iter; + switch (state) { + case CONNMAN_SERVICE_STATE_UNKNOWN: + case CONNMAN_SERVICE_STATE_IDLE: + case CONNMAN_SERVICE_STATE_ASSOCIATION: + case CONNMAN_SERVICE_STATE_CONFIGURATION: + case CONNMAN_SERVICE_STATE_FAILURE: + case CONNMAN_SERVICE_STATE_DISCONNECT: + if (!info) + return; + + handle_service_state_offline(service, info); - service_iter = g_hash_table_lookup(session->service_hash, service); - if (service_iter != NULL) { - struct service_entry *entry; + g_hash_table_remove(service_hash, service); - entry = g_sequence_get(service_iter); - entry->state = state; + return; + case CONNMAN_SERVICE_STATE_READY: + case CONNMAN_SERVICE_STATE_ONLINE: + if (!info) { + info = g_new0(struct connman_service_info, 1); + g_hash_table_replace(service_hash, service, info); } - session_changed(session, - CONNMAN_SESSION_TRIGGER_SERVICE); + info->service = service; + handle_service_state_online(service, state, info); } } @@ -1950,14 +1994,16 @@ static void ipconfig_changed(struct connman_service *service, g_hash_table_iter_init(&iter, session_hash); - while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) { + while (g_hash_table_iter_next(&iter, &key, &value)) { session = value; info = session->info; if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED) continue; - if (info->entry != NULL && info->entry->service == service) { + if (session->service && session->service == service) { + update_session_state(session); + if (type == CONNMAN_IPCONFIG_TYPE_IPV4) ipconfig_ipv4_changed(session); else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) @@ -1966,10 +2012,8 @@ static void ipconfig_changed(struct connman_service *service, } } -static struct connman_notifier session_notifier = { +static const struct connman_notifier session_notifier = { .name = "session", - .service_add = service_add, - .service_remove = service_remove, .service_state_changed = service_state_changed, .ipconfig_changed = ipconfig_changed, }; @@ -1981,7 +2025,7 @@ int __connman_session_init(void) DBG(""); connection = connman_dbus_get_connection(); - if (connection == NULL) + if (!connection) return -1; err = connman_notifier_register(&session_notifier); @@ -1993,7 +2037,8 @@ int __connman_session_init(void) session_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, cleanup_session); - sessionmode = FALSE; + service_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, cleanup_service); return 0; } @@ -2001,7 +2046,7 @@ void __connman_session_cleanup(void) { DBG(""); - if (connection == NULL) + if (!connection) return; connman_notifier_unregister(&session_notifier); @@ -2009,6 +2054,8 @@ void __connman_session_cleanup(void) g_hash_table_foreach(session_hash, release_session, NULL); g_hash_table_destroy(session_hash); session_hash = NULL; + g_hash_table_destroy(service_hash); + service_hash = NULL; dbus_connection_unref(connection); }