X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;ds=sidebyside;f=src%2Fsession.c;h=6000b6d9f130984088b5e4eb344b453c4f79aac1;hb=c647a4b6f1132684c9d8b8ad71ec38d81147b278;hp=fbb53b845b5b67764646e031605fc84ef6b4cc86;hpb=632313cc4eb082d54cb33769ca86f57483a3125e;p=platform%2Fupstream%2Fconnman.git diff --git a/src/session.c b/src/session.c index fbb53b8..6000b6d 100644 --- a/src/session.c +++ b/src/session.c @@ -2,8 +2,8 @@ * * Connection Manager * - * Copyright (C) 2007-2010 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 @@ -32,67 +32,13 @@ static DBusConnection *connection; static GHashTable *session_hash; -static connman_bool_t sessionmode; -static struct session_info *ecall_info; - -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_PERIODIC = 4, - CONNMAN_SESSION_TRIGGER_SERVICE = 5, - CONNMAN_SESSION_TRIGGER_ECALL = 6, -}; - -enum connman_session_reason { - CONNMAN_SESSION_REASON_UNKNOWN = 0, - CONNMAN_SESSION_REASON_CONNECT = 1, - CONNMAN_SESSION_REASON_DISCONNECT = 2, - CONNMAN_SESSION_REASON_FREE_RIDE = 3, - CONNMAN_SESSION_REASON_PERIODIC = 4, -}; - -enum connman_session_state { - CONNMAN_SESSION_STATE_DISCONNECTED = 0, - CONNMAN_SESSION_STATE_CONNECTED = 1, - CONNMAN_SESSION_STATE_ONLINE = 2, -}; - -enum connman_session_roaming_policy { - CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN = 0, - CONNMAN_SESSION_ROAMING_POLICY_DEFAULT = 1, - CONNMAN_SESSION_ROAMING_POLICY_ALWAYS = 2, - CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN = 3, - CONNMAN_SESSION_ROAMING_POLICY_NATIONAL = 4, - CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL = 5, -}; - -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 GHashTable *service_hash; +static struct connman_session *ecall_session; +static uint32_t session_mark = 256; struct session_info { + struct connman_session_config config; enum connman_session_state state; - connman_bool_t priority; - GSList *allowed_bearers; - connman_bool_t avoid_handover; - connman_bool_t stay_connected; - unsigned int periodic_connect; - unsigned int idle_timeout; - connman_bool_t ecall; - enum connman_session_roaming_policy roaming_policy; - unsigned int marker; - - struct service_entry *entry; - enum connman_session_reason reason; }; struct connman_session { @@ -101,58 +47,55 @@ struct connman_session { char *notify_path; guint notify_watch; - 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; +}; - GSequence *service_list; - GHashTable *service_hash; +struct connman_service_info { + struct connman_service *service; + GSList *sessions; }; -struct bearer_info { - char *name; - connman_bool_t match_all; - enum connman_service_type service_type; +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_PERIODIC: - return "periodic"; - 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_DISCONNECT: - return "disconnect"; - case CONNMAN_SESSION_REASON_FREE_RIDE: - return "free-ride"; - case CONNMAN_SESSION_REASON_PERIODIC: - return "periodic"; - } + 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) @@ -169,27 +112,23 @@ static const char *state2string(enum connman_session_state state) return NULL; } -static const char *roamingpolicy2string(enum connman_session_roaming_policy policy) -{ - switch (policy) { - case CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN: - return "unknown"; - case CONNMAN_SESSION_ROAMING_POLICY_DEFAULT: - return "default"; - case CONNMAN_SESSION_ROAMING_POLICY_ALWAYS: - return "always"; - case CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN: - return "forbidden"; - case CONNMAN_SESSION_ROAMING_POLICY_NATIONAL: - return "national"; - case CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL: - return "international"; +static const char *type2string(enum connman_session_type type) +{ + switch (type) { + case CONNMAN_SESSION_TYPE_UNKNOWN: + return ""; + case CONNMAN_SESSION_TYPE_ANY: + return "any"; + case CONNMAN_SESSION_TYPE_LOCAL: + return "local"; + case CONNMAN_SESSION_TYPE_INTERNET: + return "internet"; } return NULL; } -static enum connman_session_roaming_policy string2roamingpolicy(const char *policy) +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; @@ -205,25 +144,36 @@ static enum connman_session_roaming_policy string2roamingpolicy(const char *poli return CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN; } -static enum connman_service_type bearer2service(const char *bearer) +enum connman_session_type connman_session_parse_connection_type(const char *type) { - if (bearer == NULL) - return CONNMAN_SERVICE_TYPE_UNKNOWN; + if (g_strcmp0(type, "any") == 0) + return CONNMAN_SESSION_TYPE_ANY; + if (g_strcmp0(type, "local") == 0) + return CONNMAN_SESSION_TYPE_LOCAL; + else if (g_strcmp0(type, "internet") == 0) + return CONNMAN_SESSION_TYPE_INTERNET; + return CONNMAN_SESSION_TYPE_UNKNOWN; +} + +static int bearer2service(const char *bearer, enum connman_service_type *type) +{ 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) @@ -231,1006 +181,961 @@ 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 void cleanup_bearer_info(gpointer data, gpointer user_data) +static struct fw_snat *fw_snat_lookup(int index, const char *addr) { - struct bearer_info *info = data; + struct fw_snat *fw_snat; + GSList *list; - g_free(info->name); - g_free(info); + for (list = fw_snat_list; list; list = list->next) { + fw_snat = list->data; + + if (fw_snat->index == index) { + if (g_strcmp0(addr, fw_snat->addr) != 0) + continue; + return fw_snat; + } + } + return NULL; } -static GSList *session_parse_allowed_bearers(DBusMessageIter *iter) +static int fw_snat_create(struct connman_session *session, + int index, const char *ifname, const char *addr) { - struct bearer_info *info; - DBusMessageIter array; - GSList *list = NULL; - - dbus_message_iter_recurse(iter, &array); + struct fw_snat *fw_snat; + int err; - while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) { - char *bearer = NULL; + fw_snat = g_new0(struct fw_snat, 1); - dbus_message_iter_get_basic(&array, &bearer); + fw_snat->fw = __connman_firewall_create(); + fw_snat->index = index; + fw_snat->addr = g_strdup(addr); - info = g_try_new0(struct bearer_info, 1); - if (info == NULL) { - g_slist_foreach(list, cleanup_bearer_info, NULL); - g_slist_free(list); + 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 NULL; - } + fw_snat_list = g_slist_prepend(fw_snat_list, fw_snat); + fw_snat->sessions = g_slist_prepend(fw_snat->sessions, session); - info->name = g_strdup(bearer); - info->service_type = bearer2service(info->name); + return 0; +err: + __connman_firewall_destroy(fw_snat->fw); + g_free(fw_snat->addr); + g_free(fw_snat); + return err; +} - if (info->service_type == CONNMAN_SERVICE_TYPE_UNKNOWN && - g_strcmp0(info->name, "*") == 0) { - info->match_all = TRUE; - } else { - info->match_all = FALSE; - } +static void fw_snat_ref(struct connman_session *session, + struct fw_snat *fw_snat) +{ + if (g_slist_find(fw_snat->sessions, session)) + return; + fw_snat->sessions = g_slist_prepend(fw_snat->sessions, session); +} - list = g_slist_append(list, info); +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; - dbus_message_iter_next(&array); - } + fw_snat_list = g_slist_remove(fw_snat_list, fw_snat); - return list; + __connman_firewall_disable_snat(fw_snat->fw); + __connman_firewall_destroy(fw_snat->fw); + g_free(fw_snat); } -static GSList *session_allowed_bearers_any(void) +static int init_firewall_session(struct connman_session *session) { - struct bearer_info *info; - GSList *list = NULL; + struct firewall_context *fw; + int err; + struct connman_ipconfig *ipconfig = NULL; + const char *addr = NULL; - info = g_try_new0(struct bearer_info, 1); - if (info == NULL) { - g_slist_free(list); + if (session->policy_config->id_type == CONNMAN_SESSION_ID_TYPE_UNKNOWN && + !session->info->config.source_ip_rule) + return 0; - return NULL; + DBG(""); + + 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; } - info->name = g_strdup(""); - info->match_all = TRUE; - info->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN; + fw = __connman_firewall_create(); + if (!fw) + return -ENOMEM; + + if (session->info->config.source_ip_rule && ipconfig) { + addr = __connman_ipconfig_get_local(ipconfig); + } - list = g_slist_append(list, info); + 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 list; + return 0; } -static void append_allowed_bearers(DBusMessageIter *iter, void *user_data) +static void cleanup_firewall_session(struct connman_session *session) { - struct session_info *info = user_data; - GSList *list; + if (!session->fw) + return; - for (list = info->allowed_bearers; - list != NULL; list = list->next) { - struct bearer_info *info = list->data; + __connman_firewall_disable_marking(session->fw); + __connman_firewall_disable_snat(session->fw); + __connman_firewall_destroy(session->fw); - dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, - &info->name); - } + session->fw = NULL; } -static void append_ipconfig_ipv4(DBusMessageIter *iter, void *user_data) +static int init_routing_table(struct connman_session *session) { - struct connman_service *service = user_data; - struct connman_ipconfig *ipconfig_ipv4; + int err; - if (service == NULL) - return; + if (session->policy_config->id_type == CONNMAN_SESSION_ID_TYPE_UNKNOWN && + !session->info->config.source_ip_rule) + return 0; - ipconfig_ipv4 = __connman_service_get_ip4config(service); - if (ipconfig_ipv4 == NULL) + if (!session->service) + return 0; + + DBG(""); + + err = __connman_inet_add_fwmark_rule(session->mark, + AF_INET, session->mark); + if (err < 0) + return err; + + 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; + + return err; +} + +static void del_default_route(struct connman_session *session) +{ + if (!session->gateway) return; - __connman_ipconfig_append_ipv4(ipconfig_ipv4, iter); + DBG("index %d routing table %d default gateway %s/%u", + session->index, session->mark, session->gateway, session->prefixlen); + + __connman_inet_del_subnet_from_table(session->mark, + session->index, session->gateway, session->prefixlen); + + __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 void append_ipconfig_ipv6(DBusMessageIter *iter, void *user_data) +static void add_default_route(struct connman_session *session) { - struct connman_service *service = user_data; - struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6; + struct connman_ipconfig *ipconfig; + int err; + struct in_addr addr = { INADDR_ANY }; - if (service == NULL) + if (!session->service) return; - ipconfig_ipv4 = __connman_service_get_ip4config(service); - ipconfig_ipv6 = __connman_service_get_ip6config(service); - if (ipconfig_ipv6 == NULL) - return; + ipconfig = __connman_service_get_ip4config(session->service); + session->index = __connman_ipconfig_get_index(ipconfig); + session->gateway = g_strdup(__connman_ipconfig_get_gateway(ipconfig)); - __connman_ipconfig_append_ipv6(ipconfig_ipv6, iter, ipconfig_ipv4); + if (!session->gateway) + session->gateway = g_strdup(inet_ntoa(addr)); + + session->prefixlen = __connman_ipconfig_get_prefixlen(ipconfig); + + DBG("index %d routing table %d default gateway %s/%u", + session->index, session->mark, session->gateway, session->prefixlen); + + 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 void append_notify(DBusMessageIter *dict, - struct connman_session *session) +static void del_nat_rules(struct connman_session *session) { - struct session_info *info = session->info; - struct session_info *info_last = session->info_last; - const char *policy; - 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); + struct fw_snat *fw_snat; - connman_dbus_dict_append_basic(dict, "State", - DBUS_TYPE_STRING, - &state); - info_last->state = info->state; - } + if (!session->snat_enabled) + return; - 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; - } + session->snat_enabled = false; + fw_snat = fw_snat_lookup(session->index, session->addr); - connman_dbus_dict_append_basic(dict, "Name", - DBUS_TYPE_STRING, - &name); + if (!fw_snat) + return; - connman_dbus_dict_append_dict(dict, "IPv4", - append_ipconfig_ipv4, - service); + fw_snat_unref(session, fw_snat); +} - connman_dbus_dict_append_dict(dict, "IPv6", - append_ipconfig_ipv6, - service); +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; - connman_dbus_dict_append_basic(dict, "Interface", - DBUS_TYPE_STRING, - &ifname); + if (!session->service) + return; - connman_dbus_dict_append_basic(dict, "Bearer", - DBUS_TYPE_STRING, - &bearer); + ipconfig = __connman_service_get_ip4config(session->service); + index = __connman_ipconfig_get_index(ipconfig); + ifname = connman_inet_ifname(index); + addr = __connman_ipconfig_get_local(ipconfig); - info_last->entry = info->entry; - } + if (!addr) + return; + g_free(session->addr); + session->addr = g_strdup(addr); - if (session->append_all == TRUE || - info->priority != info_last->priority) { - connman_dbus_dict_append_basic(dict, "Priority", - DBUS_TYPE_BOOLEAN, - &info->priority); - info_last->priority = info->priority; + session->snat_enabled = true; + fw_snat = fw_snat_lookup(index, session->addr); + if (fw_snat) { + fw_snat_ref(session, fw_snat); + return; } - if (session->append_all == TRUE || - info->allowed_bearers != info_last->allowed_bearers) { - connman_dbus_dict_append_array(dict, "AllowedBearers", - DBUS_TYPE_STRING, - append_allowed_bearers, - info); - info_last->allowed_bearers = info->allowed_bearers; + err = fw_snat_create(session, index, ifname, addr); + if (err < 0) { + DBG("failed to add SNAT rule"); + session->snat_enabled = false; } - if (session->append_all == TRUE || - info->avoid_handover != info_last->avoid_handover) { - connman_dbus_dict_append_basic(dict, "AvoidHandover", - DBUS_TYPE_BOOLEAN, - &info->avoid_handover); - info_last->avoid_handover = info->avoid_handover; - } + g_free(ifname); +} - if (session->append_all == TRUE || - info->stay_connected != info_last->stay_connected) { - connman_dbus_dict_append_basic(dict, "StayConnected", - DBUS_TYPE_BOOLEAN, - &info->stay_connected); - info_last->stay_connected = info->stay_connected; - } +uint32_t connman_session_firewall_get_fwmark(struct connman_session *session) +{ + return session->mark; +} - if (session->append_all == TRUE || - info->periodic_connect != info_last->periodic_connect) { - connman_dbus_dict_append_basic(dict, "PeriodicConnect", - DBUS_TYPE_UINT32, - &info->periodic_connect); - info_last->periodic_connect = info->periodic_connect; - } +static void cleanup_routing_table(struct connman_session *session) +{ + DBG(""); - if (session->append_all == TRUE || - info->idle_timeout != info_last->idle_timeout) { - connman_dbus_dict_append_basic(dict, "IdleTimeout", - DBUS_TYPE_UINT32, - &info->idle_timeout); - info_last->idle_timeout = info->idle_timeout; - } + if (session->policy_routing) { + __connman_inet_del_fwmark_rule(session->mark, + AF_INET6, session->mark); - if (session->append_all == TRUE || - info->ecall != info_last->ecall) { - connman_dbus_dict_append_basic(dict, "EmergencyCall", - DBUS_TYPE_BOOLEAN, - &info->ecall); - info_last->ecall = info->ecall; + __connman_inet_del_fwmark_rule(session->mark, + AF_INET, session->mark); + session->policy_routing = false; } - if (session->append_all == TRUE || - info->roaming_policy != info_last->roaming_policy) { - policy = roamingpolicy2string(info->roaming_policy); - connman_dbus_dict_append_basic(dict, "RoamingPolicy", - DBUS_TYPE_STRING, - &policy); - info_last->roaming_policy = info->roaming_policy; - } + del_default_route(session); +} - if (session->append_all == TRUE || - info->marker != info_last->marker) { - connman_dbus_dict_append_basic(dict, "SessionMarker", - DBUS_TYPE_UINT32, - &info->marker); - info_last->marker = info->marker; +static void update_firewall(struct connman_session *session) +{ + cleanup_firewall_session(session); + init_firewall_session(session); +} + +static void update_routing_table(struct connman_session *session) +{ + cleanup_routing_table(session); + init_routing_table(session); + add_default_route(session); +} + +static void cleanup_nat_rules(struct connman_session *session) +{ + del_nat_rules(session); +} + +static void destroy_policy_config(struct connman_session *session) +{ + if (!policy) { + g_free(session->policy_config); + return; } - session->append_all = FALSE; + policy->destroy(session); } -static connman_bool_t compute_notifiable_changes(struct connman_session *session) +static void free_session(struct connman_session *session) { - struct session_info *info_last = session->info_last; - struct session_info *info = session->info; + if (!session) + return; + + if (session->notify_watch > 0) + g_dbus_remove_watch(connection, session->notify_watch); - if (session->append_all == TRUE) - return TRUE; + g_dbus_unregister_interface(connection, session->session_path, + CONNMAN_SESSION_INTERFACE); - if (info->state != info_last->state) - return TRUE; + 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); - if (info->entry != info_last->entry && - info->state >= CONNMAN_SESSION_STATE_CONNECTED) - return TRUE; - - if (info->periodic_connect != info_last->periodic_connect || - info->allowed_bearers != info_last->allowed_bearers || - info->avoid_handover != info_last->avoid_handover || - info->stay_connected != info_last->stay_connected || - info->roaming_policy != info_last->roaming_policy || - info->idle_timeout != info_last->idle_timeout || - info->priority != info_last->priority || - info->marker != info_last->marker || - info->ecall != info_last->ecall) - return TRUE; + g_free(session); +} - return FALSE; +static void set_active_session(struct connman_session *session, bool enable) +{ + + 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 gboolean session_notify(gpointer user_data) +static void cleanup_session(gpointer user_data) { struct connman_session *session = user_data; - DBusMessage *msg; - DBusMessageIter array, dict; - if (compute_notifiable_changes(session) == FALSE) - return FALSE; + DBG("remove %s", session->session_path); - DBG("session %p owner %s notify_path %s", session, - session->owner, session->notify_path); + cleanup_nat_rules(session); + cleanup_routing_table(session); + cleanup_firewall_session(session); - msg = dbus_message_new_method_call(session->owner, session->notify_path, - CONNMAN_NOTIFICATION_INTERFACE, - "Update"); - if (msg == NULL) - return FALSE; + if (session->active) + set_active_session(session, false); - dbus_message_iter_init_append(msg, &array); - connman_dbus_dict_open(&array, &dict); + session_deactivate(session); + update_session_state(session); - append_notify(&dict, session); + g_slist_free(session->user_allowed_bearers); + g_free(session->user_allowed_interface); - connman_dbus_dict_close(&array, &dict); + free_session(session); +} - g_dbus_send_message(connection, msg); +struct creation_data { + DBusMessage *pending; + struct connman_session *session; - return FALSE; -} + /* user config */ + enum connman_session_type type; + GSList *allowed_bearers; + char *allowed_interface; + bool source_ip_rule; + char *context_identifier; +}; -static void ipconfig_ipv4_changed(struct connman_session *session) +static void cleanup_creation_data(struct creation_data *creation_data) { - struct session_info *info = session->info; + if (!creation_data) + return; - connman_dbus_setting_changed_dict(session->owner, session->notify_path, - "IPv4", append_ipconfig_ipv4, - info->entry->service); + if (creation_data->pending) + dbus_message_unref(creation_data->pending); + if (creation_data->context_identifier) + g_free(creation_data->context_identifier); + + g_slist_free(creation_data->allowed_bearers); + g_free(creation_data->allowed_interface); + g_free(creation_data); } -static void ipconfig_ipv6_changed(struct connman_session *session) +static int create_policy_config(struct connman_session *session, + connman_session_config_func_t cb, + struct creation_data *creation_data) { - struct session_info *info = session->info; + struct connman_session_config *config; - connman_dbus_setting_changed_dict(session->owner, session->notify_path, - "IPv6", append_ipconfig_ipv6, - info->entry->service); + if (!policy) { + config = connman_session_create_default_config(); + if (!config) { + free_session(session); + cleanup_creation_data(creation_data); + return -ENOMEM; + } + + return cb(session, config, creation_data, 0); + } + + return policy->create(session, cb, creation_data); } -static connman_bool_t service_type_match(struct connman_session *session, - struct connman_service *service) +int connman_session_policy_register(struct connman_session_policy *plugin) { - struct session_info *info = session->info; - GSList *list; + if (policy) + return -EINVAL; - for (list = info->allowed_bearers; - list != NULL; list = list->next) { - struct bearer_info *info = list->data; - enum connman_service_type service_type; + if (!plugin->create || !plugin->destroy) + return -EINVAL; - if (info->match_all == TRUE) - return TRUE; + DBG("name %s", plugin->name); - service_type = connman_service_get_type(service); - if (info->service_type == service_type) - return TRUE; - } + policy = plugin; - return FALSE; + return 0; } -static connman_bool_t service_match(struct connman_session *session, - struct connman_service *service) +void connman_session_policy_unregister(struct connman_session_policy *plugin) { - if (service_type_match(session, service) == FALSE) - return FALSE; + if (plugin != policy) + return; - return TRUE; + DBG("name %s", policy->name); + + policy = NULL; } -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 - */ +static int default_bearers[] = { + CONNMAN_SERVICE_TYPE_ETHERNET, + CONNMAN_SERVICE_TYPE_WIFI, + CONNMAN_SERVICE_TYPE_BLUETOOTH, + CONNMAN_SERVICE_TYPE_CELLULAR, + CONNMAN_SERVICE_TYPE_GADGET, +}; - 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; - } +static void add_default_bearer_types(GSList **list) +{ + unsigned int i; - return 0; + for (i = 0; i < G_N_ELEMENTS(default_bearers); i++) + *list = g_slist_append(*list, + GINT_TO_POINTER(default_bearers[i])); } -static gint sort_allowed_bearers(struct connman_service *service_a, - struct connman_service *service_b, - struct connman_session *session) +void connman_session_set_default_config(struct connman_session_config *config) { - struct session_info *info = session->info; - GSList *list; - enum connman_service_type type_a, type_b; - int weight_a, weight_b; + config->id_type = CONNMAN_SESSION_ID_TYPE_UNKNOWN; + g_free(config->id); + config->id = NULL; - type_a = connman_service_get_type(service_a); - type_b = connman_service_get_type(service_b); - - for (list = info->allowed_bearers; - list != NULL; list = list->next) { - struct bearer_info *info = list->data; - - if (info->match_all == TRUE) { - if (type_a != type_b) { - weight_a = service_type_weight(type_a); - weight_b = service_type_weight(type_b); - - if (weight_a > weight_b) - return -1; - - if (weight_a < weight_b) - return 1; - - return 0; - } - } + config->priority = FALSE; + config->roaming_policy = CONNMAN_SESSION_ROAMING_POLICY_DEFAULT; + config->type = CONNMAN_SESSION_TYPE_ANY; + config->ecall = FALSE; - if (type_a == info->service_type && - type_b == info->service_type) { - return 0; - } - - if (type_a == info->service_type && - type_b != info->service_type) { - return -1; - } - - if (type_a != info->service_type && - type_b == info->service_type) { - return 1; - } - } - - return 0; + g_slist_free(config->allowed_bearers); + config->allowed_bearers = NULL; + add_default_bearer_types(&config->allowed_bearers); } -static gint sort_services(gconstpointer a, gconstpointer b, gpointer user_data) +struct connman_session_config *connman_session_create_default_config(void) { - struct service_entry *entry_a = (void *)a; - struct service_entry *entry_b = (void *)b; - struct connman_session *session = user_data; + struct connman_session_config *config; + + config = g_new0(struct connman_session_config, 1); + connman_session_set_default_config(config); - return sort_allowed_bearers(entry_a->service, entry_b->service, - session); + return config; } -static void cleanup_session(gpointer user_data) +static enum connman_session_type apply_policy_on_type( + enum connman_session_type policy, + enum connman_session_type type) { - struct connman_session *session = user_data; - struct session_info *info = session->info; + if (type == CONNMAN_SESSION_TYPE_UNKNOWN) + return CONNMAN_SESSION_TYPE_UNKNOWN; - DBG("remove %s", session->session_path); - - g_hash_table_destroy(session->service_hash); - g_sequence_free(session->service_list); - - if (info->entry != NULL && - info->entry->reason == CONNMAN_SESSION_REASON_CONNECT) { - __connman_service_disconnect(info->entry->service); - } - - g_slist_foreach(info->allowed_bearers, cleanup_bearer_info, NULL); - g_slist_free(info->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); -} + if (policy == CONNMAN_SESSION_TYPE_ANY) + return type; -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; - } + if (policy == CONNMAN_SESSION_TYPE_LOCAL) + return CONNMAN_SESSION_TYPE_LOCAL; - return CONNMAN_SESSION_STATE_DISCONNECTED; + return CONNMAN_SESSION_TYPE_INTERNET; } -static connman_bool_t is_connected(enum connman_service_state state) +int connman_session_parse_bearers(const char *token, 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; - } + enum connman_service_type bearer; + int err; - return FALSE; -} + if (g_strcmp0(token, "") == 0) + return 0; -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; + if (g_strcmp0(token, "*") == 0) { + add_default_bearer_types(list); + return 0; } - return FALSE; -} + err = bearer2service(token, &bearer); + if (err < 0) + return err; -static connman_bool_t explicit_connect(enum connman_session_reason reason) -{ - switch (reason) { - case CONNMAN_SESSION_REASON_UNKNOWN: - case CONNMAN_SESSION_REASON_FREE_RIDE: - case CONNMAN_SESSION_REASON_DISCONNECT: - break; - case CONNMAN_SESSION_REASON_CONNECT: - case CONNMAN_SESSION_REASON_PERIODIC: - return TRUE; - } + *list = g_slist_append(*list, GINT_TO_POINTER(bearer)); - return FALSE; + return 0; } -static connman_bool_t explicit_disconnect(struct session_info *info) +static int parse_bearers(DBusMessageIter *iter, GSList **list) { - if (info->entry == NULL) - return FALSE; + DBusMessageIter array; + int type, err; - DBG("reason %s service %p state %d", - reason2string(info->entry->reason), - info->entry->service, info->entry->state); + dbus_message_iter_recurse(iter, &array); - if (info->entry->reason == CONNMAN_SESSION_REASON_UNKNOWN) - return FALSE; + *list = NULL; - if (explicit_connect(info->entry->reason) == FALSE) - return FALSE; + while ((type = dbus_message_iter_get_arg_type(&array)) != + DBUS_TYPE_INVALID) { + char *bearer_name = NULL; - if (__connman_service_session_dec(info->entry->service) == FALSE) - return FALSE; + if (type != DBUS_TYPE_STRING) { + g_slist_free(*list); + *list = NULL; + return -EINVAL; + } - if (ecall_info != NULL && ecall_info != info) - return FALSE; + dbus_message_iter_get_basic(&array, &bearer_name); - return TRUE; -} - -struct pending_data { - unsigned int timeout; - struct service_entry *entry; - gboolean (*cb)(gpointer); -}; + err = connman_session_parse_bearers(bearer_name, list); + if (err < 0) { + g_slist_free(*list); + *list = NULL; + return err; + } -static void pending_timeout_free(gpointer data, gpointer user_data) -{ - struct pending_data *pending = data; + dbus_message_iter_next(&array); + } - DBG("pending %p timeout %d", pending, pending->timeout); - g_source_remove(pending->timeout); - g_free(pending); + return 0; } -static void pending_timeout_remove_all(struct service_entry *entry) +static void filter_bearer(GSList *bearers, + enum connman_service_type policy, + GSList **list) { - DBG(""); + enum connman_service_type bearer; + GSList *it; - g_slist_foreach(entry->pending_timeouts, pending_timeout_free, NULL); - g_slist_free(entry->pending_timeouts); - entry->pending_timeouts = NULL; -} + if (!bearers) + return; -static gboolean pending_timeout_cb(gpointer data) -{ - struct pending_data *pending = data; - struct service_entry *entry = pending->entry; - gboolean ret; + for (it = bearers; it; it = it->next) { + bearer = GPOINTER_TO_INT(it->data); - DBG("pending %p timeout %d", pending, pending->timeout); + if (policy != bearer) + continue; - ret = pending->cb(pending->entry); - if (ret == FALSE) { - entry->pending_timeouts = - g_slist_remove(entry->pending_timeouts, - pending); - g_free(pending); + *list = g_slist_append(*list, GINT_TO_POINTER(bearer)); + return; } - return ret; } -static connman_bool_t pending_timeout_add(unsigned int seconds, - gboolean (*cb)(gpointer), - struct service_entry *entry) +static void apply_policy_on_bearers(GSList *policy_bearers, GSList *bearers, + GSList **list) { - struct pending_data *pending = g_try_new0(struct pending_data, 1); + enum connman_service_type policy_bearer; + GSList *it; - if (pending == NULL || cb == NULL || entry == NULL) { - g_free(pending); - return FALSE; - } - - 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); + *list = NULL; - DBG("pending %p entry %p timeout id %d", pending, entry, - pending->timeout); + for (it = policy_bearers; it; it = it->next) { + policy_bearer = GPOINTER_TO_INT(it->data); - return TRUE; + filter_bearer(bearers, policy_bearer, list); + } } -static gboolean call_disconnect(gpointer user_data) +static char * apply_policy_on_interface(const char *policy_interface, + const char *user_interface) { - 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); + if (policy_interface) + return g_strdup(policy_interface); + else if (user_interface) + return g_strdup(user_interface); + else + return NULL; +} - return FALSE; +const char *connman_session_get_owner(struct connman_session *session) +{ + return session->owner; } -static gboolean call_connect(gpointer user_data) +static void append_allowed_bearers(DBusMessageIter *iter, void *user_data) { - struct service_entry *entry = user_data; - struct connman_service *service = entry->service; + struct session_info *info = user_data; + GSList *list; - DBG("connect service %p", service); - __connman_service_connect(service); + 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); - return FALSE; + if (!name) + name = ""; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, + &name); + } } -static void deselect_service(struct session_info *info) +static void append_ipconfig_ipv4(DBusMessageIter *iter, void *user_data) { - struct service_entry *entry; - connman_bool_t disconnect, connected; + struct connman_service *service = user_data; + struct connman_ipconfig *ipconfig_ipv4; - DBG(""); + if (!service) + return; - if (info->entry == NULL) + if (!__connman_service_is_connected_state(service, + CONNMAN_IPCONFIG_TYPE_IPV4)) return; - disconnect = explicit_disconnect(info); + ipconfig_ipv4 = __connman_service_get_ip4config(service); + if (!ipconfig_ipv4) + return; - connected = is_connecting(info->entry->state) == TRUE || - is_connected(info->entry->state) == TRUE; + __connman_ipconfig_append_ipv4(ipconfig_ipv4, iter); +} - info->state = CONNMAN_SESSION_STATE_DISCONNECTED; - info->entry->reason = CONNMAN_SESSION_REASON_UNKNOWN; +static void append_ipconfig_ipv6(DBusMessageIter *iter, void *user_data) +{ + struct connman_service *service = user_data; + struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6; - entry = info->entry; - info->entry = NULL; + if (!service) + return; - DBG("disconnect %d connected %d", disconnect, connected); + if (!__connman_service_is_connected_state(service, + CONNMAN_IPCONFIG_TYPE_IPV6)) + return; - if (disconnect == TRUE && connected == TRUE) - pending_timeout_add(0, call_disconnect, entry); + ipconfig_ipv4 = __connman_service_get_ip4config(service); + ipconfig_ipv6 = __connman_service_get_ip6config(service); + if (!ipconfig_ipv6) + return; + + __connman_ipconfig_append_ipv6(ipconfig_ipv6, iter, ipconfig_ipv4); } -static void deselect_and_disconnect(struct connman_session *session, - enum connman_session_reason reason) +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; + enum connman_service_type type; + const char *name, *bearer; + char *ifname; + int idx; - deselect_service(info); - - info->reason = reason; -} + if (session->append_all || info->state != info_last->state) { + const char *state = state2string(info->state); -static void select_connected_service(struct session_info *info, - struct service_entry *entry) -{ - info->state = service_to_session_state(entry->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) - info->state = service_to_session_state(info->entry->state); - - switch (trigger) { - case CONNMAN_SESSION_TRIGGER_UNKNOWN: - DBG("ignore session changed event"); - return; - case CONNMAN_SESSION_TRIGGER_SETTING: - if (info->allowed_bearers != info_last->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, info->reason); - } - - g_hash_table_remove_all(service_hash_last); - g_sequence_free(service_list_last); + 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 (info->state == CONNMAN_SESSION_STATE_DISCONNECTED) { - select_and_connect(session, - CONNMAN_SESSION_REASON_FREE_RIDE); - } + session->id_type = session->policy_config->id_type; + } - break; - 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; - } + apply_policy_on_bearers( + session->policy_config->allowed_bearers, + session->user_allowed_bearers, + &allowed_bearers); - if (info->entry != NULL && - is_connecting(info->entry->state) == TRUE) { - break; - } + allowed_interface = apply_policy_on_interface( + session->policy_config->allowed_interface, + session->user_allowed_interface); - select_and_connect(session, - CONNMAN_SESSION_REASON_CONNECT); + if (session->active) + set_active_session(session, false); - break; - case CONNMAN_SESSION_TRIGGER_DISCONNECT: - deselect_and_disconnect(session, - CONNMAN_SESSION_REASON_DISCONNECT); + session->active = false; + session_deactivate(session); - break; - case CONNMAN_SESSION_TRIGGER_PERIODIC: - if (info->state >= CONNMAN_SESSION_STATE_CONNECTED) { - info->entry->reason = CONNMAN_SESSION_REASON_PERIODIC; - __connman_service_session_inc(info->entry->service); - break; - } + g_slist_free(info->config.allowed_bearers); + info->config.allowed_bearers = allowed_bearers; - select_and_connect(session, - CONNMAN_SESSION_REASON_PERIODIC); + g_free(info->config.allowed_interface); + info->config.allowed_interface = allowed_interface; - break; - case CONNMAN_SESSION_TRIGGER_SERVICE: - if (info->entry != NULL && - (is_connecting(info->entry->state) == TRUE || - is_connected(info->entry->state) == TRUE)) { - break; - } + session_activate(session); - deselect_and_disconnect(session, info->reason); + info->config.type = apply_policy_on_type( + session->policy_config->type, + info->config.type); - if (info->reason == CONNMAN_SESSION_REASON_FREE_RIDE || - info->stay_connected == TRUE) { - select_and_connect(session, info->reason); - } + info->config.roaming_policy = session->policy_config->roaming_policy; - break; - case CONNMAN_SESSION_TRIGGER_ECALL: - if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED && - info->entry != NULL && - info->entry->service != NULL) { - deselect_and_disconnect(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, DBusMessage *msg, void *user_data) { struct connman_session *session = user_data; - struct session_info *info = session->info; DBG("session %p", session); - if (ecall_info != NULL && ecall_info != info) - return __connman_error_failed(msg, EBUSY); + if (ecall_session) { + if (ecall_session->ecall && ecall_session != session) + return __connman_error_failed(msg, EBUSY); + + session->ecall = true; + } + + if (!session->active) { + session->active = true; + set_active_session(session, true); + } - session_changed(session, CONNMAN_SESSION_TRIGGER_CONNECT); + session_activate(session); + + __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_SESSION); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } @@ -1239,68 +1144,25 @@ static DBusMessage *disconnect_session(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct connman_session *session = user_data; - struct session_info *info = session->info; DBG("session %p", session); - if (ecall_info != NULL && ecall_info != info) - return __connman_error_failed(msg, EBUSY); - - session_changed(session, CONNMAN_SESSION_TRIGGER_DISCONNECT); - - return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); -} - -static void update_ecall_sessions(struct connman_session *session) -{ - struct session_info *info = session->info; - struct connman_session *session_iter; - GHashTableIter iter; - gpointer key, value; - - g_hash_table_iter_init(&iter, session_hash); - - while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) { - session_iter = value; - - if (session_iter == session) - continue; + if (ecall_session) { + if (ecall_session->ecall && ecall_session != session) + return __connman_error_failed(msg, EBUSY); - session_iter->info->ecall = info->ecall; - - session_changed(session_iter, CONNMAN_SESSION_TRIGGER_ECALL); + session->ecall = false; } -} - -static void update_ecall(struct connman_session *session) -{ - struct session_info *info = session->info; - struct session_info *info_last = session->info_last; - - DBG("session %p ecall_info %p ecall %d -> %d", session, - ecall_info, info_last->ecall, info->ecall); - - if (ecall_info == NULL) { - if (!(info_last->ecall == FALSE && info->ecall == TRUE)) - goto err; - - ecall_info = info; - } else if (ecall_info == info) { - if (!(info_last->ecall == TRUE && info->ecall == FALSE)) - goto err; - ecall_info = NULL; - } else { - goto err; + if (session->active) { + session->active = false; + set_active_session(session, false); } - update_ecall_sessions(session); + session_deactivate(session); + update_session_state(session); - return; - -err: - /* not a valid transition */ - info->ecall = info_last->ecall; + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static DBusMessage *change_session(DBusConnection *conn, @@ -1310,73 +1172,90 @@ static DBusMessage *change_session(DBusConnection *conn, struct session_info *info = session->info; DBusMessageIter iter, value; const char *name; + const char *val; GSList *allowed_bearers; + 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) return __connman_error_invalid_arguments(msg); dbus_message_iter_get_basic(&iter, &name); dbus_message_iter_next(&iter); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) + return __connman_error_invalid_arguments(msg); + dbus_message_iter_recurse(&iter, &value); switch (dbus_message_iter_get_arg_type(&value)) { case DBUS_TYPE_ARRAY: - if (g_str_equal(name, "AllowedBearers") == TRUE) { - allowed_bearers = session_parse_allowed_bearers(&value); + if (g_str_equal(name, "AllowedBearers")) { + err = parse_bearers(&value, &allowed_bearers); + if (err < 0) + return __connman_error_failed(msg, -err); - g_slist_foreach(info->allowed_bearers, - cleanup_bearer_info, NULL); - g_slist_free(info->allowed_bearers); + if (session->active) + set_active_session(session, false); - if (allowed_bearers == NULL) { - allowed_bearers = session_allowed_bearers_any(); + session->active = false; + session_deactivate(session); + update_session_state(session); - if (allowed_bearers == NULL) - return __connman_error_failed(msg, ENOMEM); - } + g_slist_free(info->config.allowed_bearers); + session->user_allowed_bearers = allowed_bearers; - info->allowed_bearers = allowed_bearers; - } else { - goto err; - } - break; - case DBUS_TYPE_BOOLEAN: - if (g_str_equal(name, "Priority") == TRUE) { - dbus_message_iter_get_basic(&value, - &info->priority); - } else if (g_str_equal(name, "AvoidHandover") == TRUE) { - dbus_message_iter_get_basic(&value, - &info->avoid_handover); - } else if (g_str_equal(name, "StayConnected") == TRUE) { - dbus_message_iter_get_basic(&value, - &info->stay_connected); - } else if (g_str_equal(name, "EmergencyCall") == TRUE) { - dbus_message_iter_get_basic(&value, - &info->ecall); - - update_ecall(session); + apply_policy_on_bearers( + session->policy_config->allowed_bearers, + session->user_allowed_bearers, + &info->config.allowed_bearers); + + session_activate(session); } else { goto err; } break; - case DBUS_TYPE_UINT32: - if (g_str_equal(name, "PeriodicConnect") == TRUE) { - dbus_message_iter_get_basic(&value, - &info->periodic_connect); - } else if (g_str_equal(name, "IdleTimeout") == TRUE) { - dbus_message_iter_get_basic(&value, - &info->idle_timeout); + case DBUS_TYPE_STRING: + if (g_str_equal(name, "ConnectionType")) { + dbus_message_iter_get_basic(&value, &val); + info->config.type = apply_policy_on_type( + session->policy_config->type, + 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_STRING: - if (g_str_equal(name, "RoamingPolicy") == TRUE) { - const char *val; - dbus_message_iter_get_basic(&value, &val); - info->roaming_policy = - string2roamingpolicy(val); + 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; } @@ -1385,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); @@ -1410,88 +1289,184 @@ 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); + dbus_message_set_no_reply(message, TRUE); + + g_dbus_send_message(connection, message); +} + +static int session_disconnect(struct connman_session *session) +{ + DBG("session %p, %s", session, session->owner); + + if (session->notify_watch > 0) + g_dbus_remove_watch(connection, session->notify_watch); + + g_dbus_unregister_interface(connection, session->session_path, + CONNMAN_SESSION_INTERFACE); + + g_hash_table_remove(session_hash, session->session_path); + + return 0; +} + +static void owner_disconnect(DBusConnection *conn, void *user_data) +{ + struct connman_session *session = user_data; + + DBG("session %p, %s died", session, session->owner); + + session_disconnect(session); +} + +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; - g_dbus_send_message(connection, message); -} + apply_policy_on_bearers( + session->policy_config->allowed_bearers, + session->user_allowed_bearers, + &info->config.allowed_bearers); -static int session_disconnect(struct connman_session *session) -{ - DBG("session %p, %s", session, session->owner); + info->config.allowed_interface = apply_policy_on_interface( + session->policy_config->allowed_interface, + session->user_allowed_interface); - if (session->notify_watch > 0) - g_dbus_remove_watch(connection, session->notify_watch); + g_hash_table_replace(session_hash, session->session_path, session); - g_dbus_unregister_interface(connection, session->session_path, - CONNMAN_SESSION_INTERFACE); + DBG("add %s", session->session_path); - deselect_and_disconnect(session, - CONNMAN_SESSION_REASON_DISCONNECT); + 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; + } - g_hash_table_remove(session_hash, session->session_path); + 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; - return 0; -} + 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; -static void owner_disconnect(DBusConnection *conn, void *user_data) -{ - struct connman_session *session = user_data; + session->append_all = true; - DBG("session %p, %s died", session, session->owner); + cleanup_creation_data(creation_data); - session_disconnect(session); -} + session_activate(session); -static DBusMessage *destroy_session(DBusConnection *conn, - DBusMessage *msg, void *user_data) -{ - struct connman_session *session = user_data; - struct session_info *info = session->info; + return 0; - DBG("session %p", session); +err: + cleanup_session(session); - if (ecall_info != NULL && ecall_info != info) - return __connman_error_failed(msg, EBUSY); +err_notify: + reply = __connman_error_failed(creation_data->pending, -err); + g_dbus_send_message(connection, reply); + creation_data->pending = NULL; - session_disconnect(session); + cleanup_creation_data(creation_data); - return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); + return err; } -static GDBusMethodTable session_methods[] = { - { "Destroy", "", "", destroy_session }, - { "Connect", "", "", connect_session }, - { "Disconnect", "", "", disconnect_session }, - { "Change", "sv", "", change_session }, - { }, -}; - int __connman_session_create(DBusMessage *msg) { const char *owner, *notify_path; char *session_path = NULL; DBusMessageIter iter, array; struct connman_session *session = NULL; - struct session_info *info, *info_last; - - connman_bool_t priority = FALSE, avoid_handover = FALSE; - connman_bool_t stay_connected = FALSE, ecall = FALSE; - enum connman_session_roaming_policy roaming_policy = - CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN; - GSList *allowed_bearers = NULL; - unsigned int periodic_connect = 0; - unsigned int idle_timeout = 0; - - 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_info != NULL) { + if (ecall_session && ecall_session->ecall) { /* * If there is an emergency call already going on, * ignore session creation attempt @@ -1500,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); @@ -1515,186 +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) { - allowed_bearers = - session_parse_allowed_bearers(&value); + if (g_str_equal(key, "AllowedBearers")) { + err = parse_bearers(&value, + &creation_data->allowed_bearers); + if (err < 0) + goto err; + + user_allowed_bearers = true; } else { - return -EINVAL; + err = -EINVAL; + goto err; } break; - case DBUS_TYPE_BOOLEAN: - if (g_str_equal(key, "Priority") == TRUE) { - dbus_message_iter_get_basic(&value, - &priority); - } else if (g_str_equal(key, "AvoidHandover") == TRUE) { - dbus_message_iter_get_basic(&value, - &avoid_handover); - } else if (g_str_equal(key, "StayConnected") == TRUE) { - dbus_message_iter_get_basic(&value, - &stay_connected); - } else if (g_str_equal(key, "EmergencyCall") == TRUE) { - dbus_message_iter_get_basic(&value, - &ecall); + case DBUS_TYPE_STRING: + 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); + creation_data->allowed_interface = g_strdup(val); } else { - return -EINVAL; + err = -EINVAL; + goto err; } break; - case DBUS_TYPE_UINT32: - if (g_str_equal(key, "PeriodicConnect") == TRUE) { - dbus_message_iter_get_basic(&value, - &periodic_connect); - } else if (g_str_equal(key, "IdleTimeout") == TRUE) { - dbus_message_iter_get_basic(&value, - &idle_timeout); + 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; - case DBUS_TYPE_STRING: - if (g_str_equal(key, "RoamingPolicy") == TRUE) { - dbus_message_iter_get_basic(&value, &val); - roaming_policy = string2roamingpolicy(val); - } else { - return -EINVAL; - } + 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); - info->state = CONNMAN_SESSION_STATE_DISCONNECTED; - info->priority = priority; - info->avoid_handover = avoid_handover; - info->stay_connected = stay_connected; - info->periodic_connect = periodic_connect; - info->idle_timeout = idle_timeout; - info->ecall = ecall; - info->roaming_policy = roaming_policy; - info->entry = NULL; - info->marker = 0; - - if (allowed_bearers == NULL) { - info->allowed_bearers = - session_allowed_bearers_any(); - - if (info->allowed_bearers == NULL) { - err = -ENOMEM; - goto err; - } - } else { - info->allowed_bearers = allowed_bearers; - } - - 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); - if (info->ecall == TRUE) { - ecall_info = info; - update_ecall_sessions(session); - } - - info_last->state = info->state; - info_last->priority = info->priority; - info_last->avoid_handover = info->avoid_handover; - info_last->stay_connected = info->stay_connected; - info_last->periodic_connect = info->periodic_connect; - info_last->idle_timeout = info->idle_timeout; - info_last->ecall = info->ecall; - info_last->roaming_policy = info->roaming_policy; - info_last->entry = info->entry; - info_last->marker = info->marker; - info_last->allowed_bearers = info->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); + + cleanup_creation_data(creation_data); + return err; +} - g_free(session_path); +bool __connman_session_policy_autoconnect(enum connman_service_connect_reason reason) +{ + if (!policy || !policy->autoconnect) + return true; - g_slist_foreach(allowed_bearers, cleanup_bearer_info, NULL); - g_slist_free(allowed_bearers); + return policy->autoconnect(reason); +} - return err; +void connman_session_destroy(struct connman_session *session) +{ + DBG("session %p", session); + + session_disconnect(session); } int __connman_session_destroy(DBusMessage *msg) @@ -1708,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 *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; - iter = g_hash_table_lookup(session->service_hash, service); - if (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(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); } } @@ -1851,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) @@ -1867,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, }; @@ -1882,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); @@ -1894,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; } @@ -1902,7 +2046,7 @@ void __connman_session_cleanup(void) { DBG(""); - if (connection == NULL) + if (!connection) return; connman_notifier_unregister(&session_notifier); @@ -1910,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); }