Imported Upstream version 1.26
[platform/upstream/connman.git] / src / session.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2014  Intel Corporation. All rights reserved.
6  *  Copyright (C) 2011-2014  BMW Car IT GmbH.
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License version 2 as
10  *  published by the Free Software Foundation.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <errno.h>
28
29 #include <gdbus.h>
30
31 #include <connman/session.h>
32
33 #include "connman.h"
34
35 static DBusConnection *connection;
36 static GHashTable *session_hash;
37 static GHashTable *service_hash;
38 static struct connman_session *ecall_session;
39 static uint32_t session_mark = 256;
40 static struct firewall_context *global_firewall = NULL;
41
42 enum connman_session_state {
43         CONNMAN_SESSION_STATE_DISCONNECTED   = 0,
44         CONNMAN_SESSION_STATE_CONNECTED      = 1,
45         CONNMAN_SESSION_STATE_ONLINE         = 2,
46 };
47
48 struct session_info {
49         struct connman_session_config config;
50         enum connman_session_state state;
51 };
52
53 struct connman_session {
54         char *owner;
55         char *session_path;
56         char *notify_path;
57         guint notify_watch;
58
59         bool active;
60         bool append_all;
61         struct session_info *info;
62         struct session_info *info_last;
63         struct connman_service *service;
64         struct connman_service *service_last;
65         struct connman_session_config *policy_config;
66         GSList *user_allowed_bearers;
67
68         bool ecall;
69
70         enum connman_session_id_type id_type;
71         struct firewall_context *fw;
72         uint32_t mark;
73         int index;
74         char *gateway;
75         bool policy_routing;
76 };
77
78 struct connman_service_info {
79         struct connman_service *service;
80         GSList *sessions;
81 };
82
83 static struct connman_session_policy *policy;
84 static void session_activate(struct connman_session *session);
85 static void session_deactivate(struct connman_session *session);
86 static void update_session_state(struct connman_session *session);
87
88 static void cleanup_service(gpointer data)
89 {
90         struct connman_service_info *info = data;
91
92         g_slist_free(info->sessions);
93         g_free(info);
94 }
95
96 static const char *state2string(enum connman_session_state state)
97 {
98         switch (state) {
99         case CONNMAN_SESSION_STATE_DISCONNECTED:
100                 return "disconnected";
101         case CONNMAN_SESSION_STATE_CONNECTED:
102                 return "connected";
103         case CONNMAN_SESSION_STATE_ONLINE:
104                 return "online";
105         }
106
107         return NULL;
108 }
109
110 static const char *type2string(enum connman_session_type type)
111 {
112         switch (type) {
113         case CONNMAN_SESSION_TYPE_UNKNOWN:
114                 return "";
115         case CONNMAN_SESSION_TYPE_ANY:
116                 return "any";
117         case CONNMAN_SESSION_TYPE_LOCAL:
118                 return "local";
119         case CONNMAN_SESSION_TYPE_INTERNET:
120                 return "internet";
121         }
122
123         return NULL;
124 }
125
126 enum connman_session_roaming_policy connman_session_parse_roaming_policy(const char *policy)
127 {
128         if (g_strcmp0(policy, "default") == 0)
129                 return CONNMAN_SESSION_ROAMING_POLICY_DEFAULT;
130         else if (g_strcmp0(policy, "always") == 0)
131                 return CONNMAN_SESSION_ROAMING_POLICY_ALWAYS;
132         else if (g_strcmp0(policy, "forbidden") == 0)
133                 return CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN;
134         else if (g_strcmp0(policy, "national") == 0)
135                 return CONNMAN_SESSION_ROAMING_POLICY_NATIONAL;
136         else if (g_strcmp0(policy, "international") == 0)
137                 return CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL;
138         else
139                 return CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN;
140 }
141
142 enum connman_session_type connman_session_parse_connection_type(const char *type)
143 {
144         if (g_strcmp0(type, "any") == 0)
145                 return CONNMAN_SESSION_TYPE_ANY;
146         if (g_strcmp0(type, "local") == 0)
147                 return CONNMAN_SESSION_TYPE_LOCAL;
148         else if (g_strcmp0(type, "internet") == 0)
149                 return CONNMAN_SESSION_TYPE_INTERNET;
150
151         return CONNMAN_SESSION_TYPE_UNKNOWN;
152 }
153
154 static int bearer2service(const char *bearer, enum connman_service_type *type)
155 {
156         if (g_strcmp0(bearer, "ethernet") == 0)
157                 *type = CONNMAN_SERVICE_TYPE_ETHERNET;
158         else if (g_strcmp0(bearer, "wifi") == 0)
159                 *type = CONNMAN_SERVICE_TYPE_WIFI;
160         else if (g_strcmp0(bearer, "gadget") == 0)
161                 *type = CONNMAN_SERVICE_TYPE_GADGET;
162         else if (g_strcmp0(bearer, "bluetooth") == 0)
163                 *type = CONNMAN_SERVICE_TYPE_BLUETOOTH;
164         else if (g_strcmp0(bearer, "cellular") == 0)
165                 *type = CONNMAN_SERVICE_TYPE_CELLULAR;
166         else if (g_strcmp0(bearer, "vpn") == 0)
167                 *type = CONNMAN_SERVICE_TYPE_VPN;
168         else
169                 return -EINVAL;
170
171         return 0;
172 }
173
174 static char *service2bearer(enum connman_service_type type)
175 {
176         switch (type) {
177         case CONNMAN_SERVICE_TYPE_ETHERNET:
178                 return "ethernet";
179         case CONNMAN_SERVICE_TYPE_GADGET:
180                 return "gadget";
181         case CONNMAN_SERVICE_TYPE_WIFI:
182                 return "wifi";
183         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
184                 return "bluetooth";
185         case CONNMAN_SERVICE_TYPE_CELLULAR:
186                 return "cellular";
187         case CONNMAN_SERVICE_TYPE_VPN:
188                 return "vpn";
189         case CONNMAN_SERVICE_TYPE_SYSTEM:
190         case CONNMAN_SERVICE_TYPE_GPS:
191         case CONNMAN_SERVICE_TYPE_P2P:
192         case CONNMAN_SERVICE_TYPE_UNKNOWN:
193                 return "";
194         }
195
196         return "";
197 }
198
199 static int init_firewall(void)
200 {
201         struct firewall_context *fw;
202         int err;
203
204         if (global_firewall)
205                 return 0;
206
207         fw = __connman_firewall_create();
208
209         err = __connman_firewall_add_rule(fw, "mangle", "INPUT",
210                                         "-j CONNMARK --restore-mark");
211         if (err < 0)
212                 goto err;
213
214         err = __connman_firewall_add_rule(fw, "mangle", "POSTROUTING",
215                                         "-j CONNMARK --save-mark");
216         if (err < 0)
217                 goto err;
218
219         err = __connman_firewall_enable(fw);
220         if (err < 0)
221                 goto err;
222
223         global_firewall = fw;
224
225         return 0;
226
227 err:
228         __connman_firewall_destroy(fw);
229
230         return err;
231 }
232
233 static void cleanup_firewall(void)
234 {
235         if (!global_firewall)
236                 return;
237
238         __connman_firewall_disable(global_firewall);
239         __connman_firewall_destroy(global_firewall);
240 }
241
242 static int init_firewall_session(struct connman_session *session)
243 {
244         struct firewall_context *fw;
245         int err;
246
247         if (session->policy_config->id_type == CONNMAN_SESSION_ID_TYPE_UNKNOWN)
248                 return 0;
249
250         DBG("");
251
252         err = init_firewall();
253         if (err < 0)
254                 return err;
255
256         fw = __connman_firewall_create();
257         if (!fw)
258                 return -ENOMEM;
259
260         switch (session->policy_config->id_type) {
261         case CONNMAN_SESSION_ID_TYPE_UID:
262                 err = __connman_firewall_add_rule(fw, "mangle", "OUTPUT",
263                                 "-m owner --uid-owner %s -j MARK --set-mark %d",
264                                                 session->policy_config->id,
265                                                 session->mark);
266                 break;
267         case CONNMAN_SESSION_ID_TYPE_GID:
268                 err = __connman_firewall_add_rule(fw, "mangle", "OUTPUT",
269                                 "-m owner --gid-owner %s -j MARK --set-mark %d",
270                                                 session->policy_config->id,
271                                                 session->mark);
272                 break;
273         case CONNMAN_SESSION_ID_TYPE_LSM:
274         default:
275                 err = -EINVAL;
276         }
277
278         if (err < 0)
279                 goto err;
280
281         session->id_type = session->policy_config->id_type;
282
283         err = __connman_firewall_enable(fw);
284         if (err)
285                 goto err;
286
287         session->fw = fw;
288
289         return 0;
290
291 err:
292         __connman_firewall_destroy(fw);
293
294         return err;
295 }
296
297 static void cleanup_firewall_session(struct connman_session *session)
298 {
299         if (!session->fw)
300                 return;
301
302         __connman_firewall_disable(session->fw);
303         __connman_firewall_destroy(session->fw);
304
305         session->fw = NULL;
306 }
307
308 static int init_routing_table(struct connman_session *session)
309 {
310         int err;
311
312         if (session->policy_config->id_type == CONNMAN_SESSION_ID_TYPE_UNKNOWN)
313                 return 0;
314
315         DBG("");
316
317         err = __connman_inet_add_fwmark_rule(session->mark,
318                                                 AF_INET, session->mark);
319         if (err < 0)
320                 return err;
321
322         err = __connman_inet_add_fwmark_rule(session->mark,
323                                                 AF_INET6, session->mark);
324         if (err < 0)
325                 __connman_inet_del_fwmark_rule(session->mark,
326                                                 AF_INET, session->mark);
327         session->policy_routing = true;
328
329         return err;
330 }
331
332 static void del_default_route(struct connman_session *session)
333 {
334         if (!session->gateway)
335                 return;
336
337         DBG("index %d routing table %d default gateway %s",
338                 session->index, session->mark, session->gateway);
339
340         __connman_inet_del_default_from_table(session->mark,
341                                         session->index, session->gateway);
342         g_free(session->gateway);
343         session->gateway = NULL;
344         session->index = -1;
345 }
346
347 static void add_default_route(struct connman_session *session)
348 {
349         struct connman_ipconfig *ipconfig;
350         int err;
351
352         if (!session->service)
353                 return;
354
355         ipconfig = __connman_service_get_ip4config(session->service);
356         session->index = __connman_ipconfig_get_index(ipconfig);
357         session->gateway = g_strdup(__connman_ipconfig_get_gateway(ipconfig));
358
359         DBG("index %d routing table %d default gateway %s",
360                 session->index, session->mark, session->gateway);
361
362         err = __connman_inet_add_default_to_table(session->mark,
363                                         session->index, session->gateway);
364         if (err < 0)
365                 DBG("session %p %s", session, strerror(-err));
366 }
367
368 static void cleanup_routing_table(struct connman_session *session)
369 {
370         DBG("");
371
372         if (session->policy_routing) {
373                 __connman_inet_del_fwmark_rule(session->mark,
374                                         AF_INET6, session->mark);
375
376                 __connman_inet_del_fwmark_rule(session->mark,
377                                         AF_INET, session->mark);
378                 session->policy_routing = false;
379         }
380
381         del_default_route(session);
382 }
383
384 static void update_routing_table(struct connman_session *session)
385 {
386         del_default_route(session);
387         add_default_route(session);
388 }
389
390 static void destroy_policy_config(struct connman_session *session)
391 {
392         if (!policy) {
393                 g_free(session->policy_config);
394                 return;
395         }
396
397         policy->destroy(session);
398 }
399
400 static void free_session(struct connman_session *session)
401 {
402         if (!session)
403                 return;
404
405         if (session->notify_watch > 0)
406                 g_dbus_remove_watch(connection, session->notify_watch);
407
408         destroy_policy_config(session);
409         g_slist_free(session->info->config.allowed_bearers);
410         g_free(session->owner);
411         g_free(session->session_path);
412         g_free(session->notify_path);
413         g_free(session->info);
414         g_free(session->info_last);
415         g_free(session->gateway);
416
417         g_free(session);
418 }
419
420 static void set_active_session(struct connman_session *session, bool enable)
421 {
422
423         if (policy && policy->session_changed)
424                 policy->session_changed(session, enable,
425                                         session->info->config.allowed_bearers);
426
427         __connman_service_set_active_session(enable,
428                                 session->info->config.allowed_bearers);
429 }
430
431 static void cleanup_session(gpointer user_data)
432 {
433         struct connman_session *session = user_data;
434
435         DBG("remove %s", session->session_path);
436
437         cleanup_routing_table(session);
438         cleanup_firewall_session(session);
439
440         if (session->active)
441                 set_active_session(session, false);
442
443         session_deactivate(session);
444         update_session_state(session);
445
446         g_slist_free(session->user_allowed_bearers);
447
448         free_session(session);
449 }
450
451 struct creation_data {
452         DBusMessage *pending;
453         struct connman_session *session;
454
455         /* user config */
456         enum connman_session_type type;
457         GSList *allowed_bearers;
458 };
459
460 static void cleanup_creation_data(struct creation_data *creation_data)
461 {
462         if (!creation_data)
463                 return;
464
465         if (creation_data->pending)
466                 dbus_message_unref(creation_data->pending);
467
468         g_slist_free(creation_data->allowed_bearers);
469         g_free(creation_data);
470 }
471
472 static int create_policy_config(struct connman_session *session,
473                                 connman_session_config_func_t cb,
474                                 struct creation_data *creation_data)
475 {
476         struct connman_session_config *config;
477
478         if (!policy) {
479                 config = connman_session_create_default_config();
480                 if (!config) {
481                         free_session(session);
482                         cleanup_creation_data(creation_data);
483                         return -ENOMEM;
484                 }
485
486                 return cb(session, config, creation_data, 0);
487         }
488
489         return policy->create(session, cb, creation_data);
490 }
491
492 int connman_session_policy_register(struct connman_session_policy *plugin)
493 {
494         if (policy)
495                 return -EINVAL;
496
497         if (!plugin->create || !plugin->destroy)
498                 return -EINVAL;
499
500         DBG("name %s", plugin->name);
501
502         policy = plugin;
503
504         return 0;
505 }
506
507 void connman_session_policy_unregister(struct connman_session_policy *plugin)
508 {
509         if (plugin != policy)
510                 return;
511
512         DBG("name %s", policy->name);
513
514         policy = NULL;
515 }
516
517 static int default_bearers[] = {
518         CONNMAN_SERVICE_TYPE_ETHERNET,
519         CONNMAN_SERVICE_TYPE_WIFI,
520         CONNMAN_SERVICE_TYPE_BLUETOOTH,
521         CONNMAN_SERVICE_TYPE_CELLULAR,
522         CONNMAN_SERVICE_TYPE_GADGET,
523 };
524
525 static void add_default_bearer_types(GSList **list)
526 {
527         unsigned int i;
528
529         for (i = 0; i < G_N_ELEMENTS(default_bearers); i++)
530                 *list = g_slist_append(*list,
531                                 GINT_TO_POINTER(default_bearers[i]));
532 }
533
534 void connman_session_set_default_config(struct connman_session_config *config)
535 {
536         config->id_type = CONNMAN_SESSION_ID_TYPE_UNKNOWN;
537         g_free(config->id);
538         config->id = NULL;
539
540         config->priority = FALSE;
541         config->roaming_policy = CONNMAN_SESSION_ROAMING_POLICY_DEFAULT;
542         config->type = CONNMAN_SESSION_TYPE_ANY;
543         config->ecall = FALSE;
544
545         g_slist_free(config->allowed_bearers);
546         add_default_bearer_types(&config->allowed_bearers);
547 }
548
549 struct connman_session_config *connman_session_create_default_config(void)
550 {
551         struct connman_session_config *config;
552
553         config = g_new0(struct connman_session_config, 1);
554         connman_session_set_default_config(config);
555
556         return config;
557 }
558
559 static enum connman_session_type apply_policy_on_type(
560                         enum connman_session_type policy,
561                         enum connman_session_type type)
562 {
563         if (type == CONNMAN_SESSION_TYPE_UNKNOWN)
564                 return CONNMAN_SESSION_TYPE_UNKNOWN;
565
566         if (policy == CONNMAN_SESSION_TYPE_ANY)
567                 return type;
568
569         if (policy == CONNMAN_SESSION_TYPE_LOCAL)
570                 return CONNMAN_SESSION_TYPE_LOCAL;
571
572         return CONNMAN_SESSION_TYPE_INTERNET;
573 }
574
575 int connman_session_parse_bearers(const char *token, GSList **list)
576 {
577         enum connman_service_type bearer;
578         int err;
579
580         if (g_strcmp0(token, "") == 0)
581                 return 0;
582
583         if (g_strcmp0(token, "*") == 0) {
584                 add_default_bearer_types(list);
585                 return 0;
586         }
587
588         err = bearer2service(token, &bearer);
589         if (err < 0)
590                 return err;
591
592         *list = g_slist_append(*list, GINT_TO_POINTER(bearer));
593
594         return 0;
595 }
596
597 static int parse_bearers(DBusMessageIter *iter, GSList **list)
598 {
599         DBusMessageIter array;
600         int type, err;
601
602         dbus_message_iter_recurse(iter, &array);
603
604         *list = NULL;
605
606         while ((type = dbus_message_iter_get_arg_type(&array)) !=
607                         DBUS_TYPE_INVALID) {
608                 char *bearer_name = NULL;
609
610                 if (type != DBUS_TYPE_STRING) {
611                         g_slist_free(*list);
612                         *list = NULL;
613                         return -EINVAL;
614                 }
615
616                 dbus_message_iter_get_basic(&array, &bearer_name);
617
618                 err = connman_session_parse_bearers(bearer_name, list);
619                 if (err < 0) {
620                         g_slist_free(*list);
621                         *list = NULL;
622                         return err;
623                 }
624
625                 dbus_message_iter_next(&array);
626         }
627
628         return 0;
629 }
630
631 static void filter_bearer(GSList *policy_bearers,
632                                 enum connman_service_type bearer,
633                                 GSList **list)
634 {
635         enum connman_service_type policy;
636         GSList *it;
637
638         if (!policy_bearers)
639                 return;
640
641         for (it = policy_bearers; it; it = it->next) {
642                 policy = GPOINTER_TO_INT(it->data);
643
644                 if (policy != bearer)
645                         continue;
646
647                 *list = g_slist_append(*list, GINT_TO_POINTER(bearer));
648                 return;
649         }
650 }
651
652 static void apply_policy_on_bearers(GSList *policy_bearers, GSList *bearers,
653                                 GSList **list)
654 {
655         enum connman_service_type bearer;
656         GSList *it;
657
658         *list = NULL;
659
660         for (it = bearers; it; it = it->next) {
661                 bearer = GPOINTER_TO_INT(it->data);
662
663                 filter_bearer(policy_bearers, bearer, list);
664         }
665 }
666
667 const char *connman_session_get_owner(struct connman_session *session)
668 {
669         return session->owner;
670 }
671
672 static void append_allowed_bearers(DBusMessageIter *iter, void *user_data)
673 {
674         struct session_info *info = user_data;
675         GSList *list;
676
677         for (list = info->config.allowed_bearers;
678                         list; list = list->next) {
679                 enum connman_service_type bearer = GPOINTER_TO_INT(list->data);
680                 const char *name = __connman_service_type2string(bearer);
681
682                 if (!name)
683                         name = "";
684
685                 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
686                                                 &name);
687         }
688 }
689
690 static void append_ipconfig_ipv4(DBusMessageIter *iter, void *user_data)
691 {
692         struct connman_service *service = user_data;
693         struct connman_ipconfig *ipconfig_ipv4;
694
695         if (!service)
696                 return;
697
698         if (!__connman_service_is_connected_state(service,
699                                                 CONNMAN_IPCONFIG_TYPE_IPV4))
700                 return;
701
702         ipconfig_ipv4 = __connman_service_get_ip4config(service);
703         if (!ipconfig_ipv4)
704                 return;
705
706         __connman_ipconfig_append_ipv4(ipconfig_ipv4, iter);
707 }
708
709 static void append_ipconfig_ipv6(DBusMessageIter *iter, void *user_data)
710 {
711         struct connman_service *service = user_data;
712         struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6;
713
714         if (!service)
715                 return;
716
717         if (!__connman_service_is_connected_state(service,
718                                                 CONNMAN_IPCONFIG_TYPE_IPV6))
719                 return;
720
721         ipconfig_ipv4 = __connman_service_get_ip4config(service);
722         ipconfig_ipv6 = __connman_service_get_ip6config(service);
723         if (!ipconfig_ipv6)
724                 return;
725
726         __connman_ipconfig_append_ipv6(ipconfig_ipv6, iter, ipconfig_ipv4);
727 }
728
729 static void append_notify(DBusMessageIter *dict,
730                                         struct connman_session *session)
731 {
732         struct session_info *info = session->info;
733         struct session_info *info_last = session->info_last;
734         struct connman_service *service;
735         enum connman_service_type type;
736         const char *name, *bearer;
737         char *ifname;
738         int idx;
739
740         if (session->append_all || info->state != info_last->state) {
741                 const char *state = state2string(info->state);
742
743                 connman_dbus_dict_append_basic(dict, "State",
744                                                 DBUS_TYPE_STRING,
745                                                 &state);
746                 info_last->state = info->state;
747         }
748
749         if (session->append_all || session->service != session->service_last) {
750                 if (session->service) {
751                         service = session->service;
752                         name = __connman_service_get_name(service);
753                         idx = __connman_service_get_index(service);
754
755                         ifname = connman_inet_ifname(idx);
756                         if (!ifname)
757                                 ifname = g_strdup("");
758
759                         type = connman_service_get_type(service);
760                         bearer = service2bearer(type);
761                 } else {
762                         service = NULL;
763                         name = "";
764                         ifname = g_strdup("");
765                         bearer = "";
766                 }
767
768                 connman_dbus_dict_append_basic(dict, "Name",
769                                                 DBUS_TYPE_STRING,
770                                                 &name);
771
772                 connman_dbus_dict_append_dict(dict, "IPv4",
773                                                 append_ipconfig_ipv4,
774                                                 service);
775
776                 connman_dbus_dict_append_dict(dict, "IPv6",
777                                                 append_ipconfig_ipv6,
778                                                 service);
779
780                 connman_dbus_dict_append_basic(dict, "Interface",
781                                                 DBUS_TYPE_STRING,
782                                                 &ifname);
783
784                 connman_dbus_dict_append_basic(dict, "Bearer",
785                                                 DBUS_TYPE_STRING,
786                                                 &bearer);
787
788                 g_free(ifname);
789
790                 session->service_last = session->service;
791         }
792
793         if (session->append_all ||
794                         info->config.type != info_last->config.type) {
795                 const char *type = type2string(info->config.type);
796
797                 connman_dbus_dict_append_basic(dict, "ConnectionType",
798                                                 DBUS_TYPE_STRING,
799                                                 &type);
800                 info_last->config.type = info->config.type;
801         }
802
803         if (session->append_all ||
804                         info->config.allowed_bearers != info_last->config.allowed_bearers) {
805                 connman_dbus_dict_append_array(dict, "AllowedBearers",
806                                                 DBUS_TYPE_STRING,
807                                                 append_allowed_bearers,
808                                                 info);
809                 info_last->config.allowed_bearers = info->config.allowed_bearers;
810         }
811
812         session->append_all = false;
813 }
814
815 static bool compute_notifiable_changes(struct connman_session *session)
816 {
817         struct session_info *info_last = session->info_last;
818         struct session_info *info = session->info;
819
820         if (session->append_all)
821                 return true;
822
823         if (info->state != info_last->state)
824                 return true;
825
826         if (session->service != session->service_last &&
827                         info->state >= CONNMAN_SESSION_STATE_CONNECTED)
828                 return true;
829
830         if (info->config.allowed_bearers != info_last->config.allowed_bearers ||
831                         info->config.type != info_last->config.type)
832                 return true;
833
834         return false;
835 }
836
837 static gboolean session_notify(gpointer user_data)
838 {
839         struct connman_session *session = user_data;
840         DBusMessage *msg;
841         DBusMessageIter array, dict;
842
843         if (!compute_notifiable_changes(session))
844                 return FALSE;
845
846         DBG("session %p owner %s notify_path %s", session,
847                 session->owner, session->notify_path);
848
849         msg = dbus_message_new_method_call(session->owner, session->notify_path,
850                                                 CONNMAN_NOTIFICATION_INTERFACE,
851                                                 "Update");
852         if (!msg)
853                 return FALSE;
854
855         dbus_message_iter_init_append(msg, &array);
856         connman_dbus_dict_open(&array, &dict);
857
858         append_notify(&dict, session);
859
860         connman_dbus_dict_close(&array, &dict);
861
862         g_dbus_send_message(connection, msg);
863
864         return FALSE;
865 }
866
867 static void ipconfig_ipv4_changed(struct connman_session *session)
868 {
869         connman_dbus_setting_changed_dict(session->owner, session->notify_path,
870                                                 "IPv4", append_ipconfig_ipv4,
871                                                 session->service);
872 }
873
874 static void ipconfig_ipv6_changed(struct connman_session *session)
875 {
876         connman_dbus_setting_changed_dict(session->owner, session->notify_path,
877                                                 "IPv6", append_ipconfig_ipv6,
878                                                 session->service);
879 }
880
881 int connman_session_config_update(struct connman_session *session)
882 {
883         struct session_info *info = session->info;
884         GSList *allowed_bearers;
885         int err;
886
887         DBG("session %p", session);
888
889         /*
890          * We update all configuration even though only one entry
891          * might have changed. We can still optimize this later.
892          */
893
894         if (session->id_type != session->policy_config->id_type) {
895                 cleanup_firewall_session(session);
896                 err = init_firewall_session(session);
897                 if (err < 0) {
898                         connman_session_destroy(session);
899                         return err;
900                 }
901
902                 session->id_type = session->policy_config->id_type;
903         }
904
905         apply_policy_on_bearers(
906                 session->policy_config->allowed_bearers,
907                 session->user_allowed_bearers,
908                 &allowed_bearers);
909
910         if (session->active)
911                 set_active_session(session, false);
912
913         session->active = false;
914         session_deactivate(session);
915
916         g_slist_free(info->config.allowed_bearers);
917         info->config.allowed_bearers = allowed_bearers;
918
919         session_activate(session);
920
921         info->config.type = apply_policy_on_type(
922                                 session->policy_config->type,
923                                 info->config.type);
924
925         info->config.roaming_policy = session->policy_config->roaming_policy;
926
927         info->config.ecall = session->policy_config->ecall;
928         if (info->config.ecall)
929                 ecall_session = session;
930
931         info->config.priority = session->policy_config->priority;
932
933         session_notify(session);
934
935         return 0;
936 }
937
938 static DBusMessage *connect_session(DBusConnection *conn,
939                                         DBusMessage *msg, void *user_data)
940 {
941         struct connman_session *session = user_data;
942
943         DBG("session %p", session);
944
945         if (ecall_session) {
946                 if (ecall_session->ecall && ecall_session != session)
947                         return __connman_error_failed(msg, EBUSY);
948
949                 session->ecall = true;
950         }
951
952         if (!session->active) {
953                 session->active = true;
954                 set_active_session(session, true);
955         }
956
957         session_activate(session);
958
959         __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_SESSION);
960
961         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
962 }
963
964 static DBusMessage *disconnect_session(DBusConnection *conn,
965                                         DBusMessage *msg, void *user_data)
966 {
967         struct connman_session *session = user_data;
968
969         DBG("session %p", session);
970
971         if (ecall_session) {
972                 if (ecall_session->ecall && ecall_session != session)
973                         return __connman_error_failed(msg, EBUSY);
974
975                 session->ecall = false;
976         }
977
978         if (session->active) {
979                 session->active = false;
980                 set_active_session(session, false);
981         }
982
983         session_deactivate(session);
984         update_session_state(session);
985
986         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
987 }
988
989 static DBusMessage *change_session(DBusConnection *conn,
990                                         DBusMessage *msg, void *user_data)
991 {
992         struct connman_session *session = user_data;
993         struct session_info *info = session->info;
994         DBusMessageIter iter, value;
995         const char *name;
996         const char *val;
997         GSList *allowed_bearers;
998         int err;
999
1000         DBG("session %p", session);
1001         if (!dbus_message_iter_init(msg, &iter))
1002                 return __connman_error_invalid_arguments(msg);
1003
1004         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
1005                 return __connman_error_invalid_arguments(msg);
1006
1007         dbus_message_iter_get_basic(&iter, &name);
1008         dbus_message_iter_next(&iter);
1009
1010         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
1011                 return __connman_error_invalid_arguments(msg);
1012
1013         dbus_message_iter_recurse(&iter, &value);
1014
1015         switch (dbus_message_iter_get_arg_type(&value)) {
1016         case DBUS_TYPE_ARRAY:
1017                 if (g_str_equal(name, "AllowedBearers")) {
1018                         err = parse_bearers(&value, &allowed_bearers);
1019                         if (err < 0)
1020                                 return __connman_error_failed(msg, -err);
1021
1022                         if (session->active)
1023                                 set_active_session(session, false);
1024
1025                         session->active = false;
1026                         session_deactivate(session);
1027
1028                         g_slist_free(info->config.allowed_bearers);
1029                         session->user_allowed_bearers = allowed_bearers;
1030
1031                         apply_policy_on_bearers(
1032                                         session->policy_config->allowed_bearers,
1033                                         session->user_allowed_bearers,
1034                                         &info->config.allowed_bearers);
1035
1036                         session_activate(session);
1037                 } else {
1038                         goto err;
1039                 }
1040                 break;
1041         case DBUS_TYPE_STRING:
1042                 if (g_str_equal(name, "ConnectionType")) {
1043                         dbus_message_iter_get_basic(&value, &val);
1044                         info->config.type = apply_policy_on_type(
1045                                 session->policy_config->type,
1046                                 connman_session_parse_connection_type(val));
1047                 } else {
1048                         goto err;
1049                 }
1050                 break;
1051         default:
1052                 goto err;
1053         }
1054
1055         session_notify(session);
1056
1057         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1058
1059 err:
1060         return __connman_error_invalid_arguments(msg);
1061 }
1062
1063 static void release_session(gpointer key, gpointer value, gpointer user_data)
1064 {
1065         struct connman_session *session = value;
1066         DBusMessage *message;
1067
1068         DBG("owner %s path %s", session->owner, session->notify_path);
1069
1070         if (session->notify_watch > 0)
1071                 g_dbus_remove_watch(connection, session->notify_watch);
1072
1073         g_dbus_unregister_interface(connection, session->session_path,
1074                                                 CONNMAN_SESSION_INTERFACE);
1075
1076         message = dbus_message_new_method_call(session->owner,
1077                                                 session->notify_path,
1078                                                 CONNMAN_NOTIFICATION_INTERFACE,
1079                                                 "Release");
1080         if (!message)
1081                 return;
1082
1083         dbus_message_set_no_reply(message, TRUE);
1084
1085         g_dbus_send_message(connection, message);
1086 }
1087
1088 static int session_disconnect(struct connman_session *session)
1089 {
1090         DBG("session %p, %s", session, session->owner);
1091
1092         if (session->notify_watch > 0)
1093                 g_dbus_remove_watch(connection, session->notify_watch);
1094
1095         g_dbus_unregister_interface(connection, session->session_path,
1096                                                 CONNMAN_SESSION_INTERFACE);
1097
1098         g_hash_table_remove(session_hash, session->session_path);
1099
1100         return 0;
1101 }
1102
1103 static void owner_disconnect(DBusConnection *conn, void *user_data)
1104 {
1105         struct connman_session *session = user_data;
1106
1107         DBG("session %p, %s died", session, session->owner);
1108
1109         session_disconnect(session);
1110 }
1111
1112 static DBusMessage *destroy_session(DBusConnection *conn,
1113                                         DBusMessage *msg, void *user_data)
1114 {
1115         struct connman_session *session = user_data;
1116
1117         DBG("session %p", session);
1118
1119         if (ecall_session && ecall_session != session)
1120                 return __connman_error_failed(msg, EBUSY);
1121
1122         session_disconnect(session);
1123
1124         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1125 }
1126
1127 static const GDBusMethodTable session_methods[] = {
1128         { GDBUS_METHOD("Destroy", NULL, NULL, destroy_session) },
1129         { GDBUS_METHOD("Connect", NULL, NULL, connect_session) },
1130         { GDBUS_METHOD("Disconnect", NULL, NULL,
1131                         disconnect_session ) },
1132         { GDBUS_METHOD("Change",
1133                         GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
1134                         NULL, change_session) },
1135         { },
1136 };
1137
1138 static int session_policy_config_cb(struct connman_session *session,
1139                                 struct connman_session_config *config,
1140                                 void *user_data, int err)
1141 {
1142         struct creation_data *creation_data = user_data;
1143         struct session_info *info, *info_last;
1144         DBusMessage *reply;
1145
1146         DBG("session %p config %p", session, config);
1147
1148         if (err < 0)
1149                 goto err;
1150
1151         session->policy_config = config;
1152
1153         session->mark = session_mark++;
1154         session->index = -1;
1155
1156         err = init_firewall_session(session);
1157         if (err < 0)
1158                 goto err;
1159
1160         err = init_routing_table(session);
1161         if (err < 0)
1162                 goto err;
1163
1164         info = session->info;
1165         info_last = session->info_last;
1166
1167         if (session->policy_config->ecall)
1168                 ecall_session = session;
1169
1170         info->state = CONNMAN_SESSION_STATE_DISCONNECTED;
1171         info->config.type = apply_policy_on_type(
1172                                 session->policy_config->type,
1173                                 creation_data->type);
1174         info->config.priority = session->policy_config->priority;
1175         info->config.roaming_policy = session->policy_config->roaming_policy;
1176
1177         session->user_allowed_bearers = creation_data->allowed_bearers;
1178         creation_data->allowed_bearers = NULL;
1179
1180         apply_policy_on_bearers(
1181                         session->policy_config->allowed_bearers,
1182                         session->user_allowed_bearers,
1183                         &info->config.allowed_bearers);
1184
1185         g_hash_table_replace(session_hash, session->session_path, session);
1186
1187         DBG("add %s", session->session_path);
1188
1189         if (!g_dbus_register_interface(connection, session->session_path,
1190                                         CONNMAN_SESSION_INTERFACE,
1191                                         session_methods, NULL, NULL,
1192                                         session, NULL)) {
1193                 connman_error("Failed to register %s", session->session_path);
1194                 g_hash_table_remove(session_hash, session->session_path);
1195                 err = -EINVAL;
1196                 goto err;
1197         }
1198
1199         reply = g_dbus_create_reply(creation_data->pending,
1200                                 DBUS_TYPE_OBJECT_PATH, &session->session_path,
1201                                 DBUS_TYPE_INVALID);
1202         g_dbus_send_message(connection, reply);
1203         creation_data->pending = NULL;
1204
1205         info_last->state = info->state;
1206         info_last->config.priority = info->config.priority;
1207         info_last->config.roaming_policy = info->config.roaming_policy;
1208         info_last->config.allowed_bearers = info->config.allowed_bearers;
1209
1210         session->append_all = true;
1211
1212         cleanup_creation_data(creation_data);
1213
1214         session_activate(session);
1215
1216         return 0;
1217
1218 err:
1219         reply = __connman_error_failed(creation_data->pending, -err);
1220         g_dbus_send_message(connection, reply);
1221         creation_data->pending = NULL;
1222
1223         cleanup_session(session);
1224         cleanup_creation_data(creation_data);
1225
1226         return err;
1227 }
1228
1229 int __connman_session_create(DBusMessage *msg)
1230 {
1231         const char *owner, *notify_path;
1232         char *session_path = NULL;
1233         DBusMessageIter iter, array;
1234         struct connman_session *session = NULL;
1235         struct creation_data *creation_data = NULL;
1236         bool user_allowed_bearers = false;
1237         bool user_connection_type = false;
1238         int err, i;
1239         char *str;
1240
1241         owner = dbus_message_get_sender(msg);
1242
1243         DBG("owner %s", owner);
1244
1245         if (ecall_session && ecall_session->ecall) {
1246                 /*
1247                  * If there is an emergency call already going on,
1248                  * ignore session creation attempt
1249                  */
1250                 err = -EBUSY;
1251                 goto err;
1252         }
1253
1254         creation_data = g_try_new0(struct creation_data, 1);
1255         if (!creation_data) {
1256                 err = -ENOMEM;
1257                 goto err;
1258         }
1259
1260         creation_data->pending = dbus_message_ref(msg);
1261
1262         dbus_message_iter_init(msg, &iter);
1263         dbus_message_iter_recurse(&iter, &array);
1264
1265         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
1266                 DBusMessageIter entry, value;
1267                 const char *key, *val;
1268
1269                 dbus_message_iter_recurse(&array, &entry);
1270                 dbus_message_iter_get_basic(&entry, &key);
1271
1272                 dbus_message_iter_next(&entry);
1273                 dbus_message_iter_recurse(&entry, &value);
1274
1275                 switch (dbus_message_iter_get_arg_type(&value)) {
1276                 case DBUS_TYPE_ARRAY:
1277                         if (g_str_equal(key, "AllowedBearers")) {
1278                                 err = parse_bearers(&value,
1279                                         &creation_data->allowed_bearers);
1280                                 if (err < 0)
1281                                         goto err;
1282
1283                                 user_allowed_bearers = true;
1284                         } else {
1285                                 err = -EINVAL;
1286                                 goto err;
1287                         }
1288                         break;
1289                 case DBUS_TYPE_STRING:
1290                         if (g_str_equal(key, "ConnectionType")) {
1291                                 dbus_message_iter_get_basic(&value, &val);
1292                                 creation_data->type =
1293                                         connman_session_parse_connection_type(val);
1294
1295                                 user_connection_type = true;
1296                         } else {
1297                                 err = -EINVAL;
1298                                 goto err;
1299                         }
1300                 }
1301                 dbus_message_iter_next(&array);
1302         }
1303
1304         /*
1305          * If the user hasn't provided a configuration, we set
1306          * the default configuration.
1307          *
1308          * For AllowedBearers this is '*', ...
1309          */
1310         if (!user_allowed_bearers) {
1311                 add_default_bearer_types(&creation_data->allowed_bearers);
1312                 if (!creation_data->allowed_bearers) {
1313                         err = -ENOMEM;
1314                         goto err;
1315                 }
1316         }
1317
1318         /* ... and for ConnectionType it is 'any'. */
1319         if (!user_connection_type)
1320                 creation_data->type = CONNMAN_SESSION_TYPE_ANY;
1321
1322         dbus_message_iter_next(&iter);
1323         dbus_message_iter_get_basic(&iter, &notify_path);
1324
1325         if (!notify_path) {
1326                 err = -EINVAL;
1327                 goto err;
1328         }
1329
1330         str = g_strdup(owner);
1331         for (i = 0; str[i] != '\0'; i++)
1332                 if (str[i] == ':' || str[i] == '.')
1333                         str[i] = '_';
1334         session_path = g_strdup_printf("/sessions/%s%s", str, notify_path);
1335         g_free(str);
1336
1337         if (!session_path) {
1338                 err = -ENOMEM;
1339                 goto err;
1340         }
1341
1342         session = g_hash_table_lookup(session_hash, session_path);
1343         if (session) {
1344                 g_free(session_path);
1345                 session = NULL;
1346                 err = -EEXIST;
1347                 goto err;
1348         }
1349
1350         session = g_try_new0(struct connman_session, 1);
1351         if (!session) {
1352                 g_free(session_path);
1353                 err = -ENOMEM;
1354                 goto err;
1355         }
1356
1357         creation_data->session = session;
1358         session->session_path = session_path;
1359
1360         session->info = g_try_new0(struct session_info, 1);
1361         if (!session->info) {
1362                 err = -ENOMEM;
1363                 goto err;
1364         }
1365
1366         session->info_last = g_try_new0(struct session_info, 1);
1367         if (!session->info_last) {
1368                 err = -ENOMEM;
1369                 goto err;
1370         }
1371
1372         session->owner = g_strdup(owner);
1373         session->notify_path = g_strdup(notify_path);
1374         session->notify_watch =
1375                 g_dbus_add_disconnect_watch(connection, session->owner,
1376                                         owner_disconnect, session, NULL);
1377
1378         err = create_policy_config(session, session_policy_config_cb,
1379                                         creation_data);
1380         if (err < 0 && err != -EINPROGRESS)
1381                 return err;
1382
1383         return -EINPROGRESS;
1384
1385 err:
1386         connman_error("Failed to create session");
1387
1388         free_session(session);
1389
1390         cleanup_creation_data(creation_data);
1391         return err;
1392 }
1393
1394 bool __connman_session_policy_autoconnect(enum connman_service_connect_reason reason)
1395 {
1396         if (!policy || !policy->autoconnect)
1397                 return true;
1398
1399         return policy->autoconnect(reason);
1400 }
1401
1402 void connman_session_destroy(struct connman_session *session)
1403 {
1404         DBG("session %p", session);
1405
1406         session_disconnect(session);
1407 }
1408
1409 int __connman_session_destroy(DBusMessage *msg)
1410 {
1411         const char *owner, *session_path;
1412         struct connman_session *session;
1413
1414         owner = dbus_message_get_sender(msg);
1415
1416         DBG("owner %s", owner);
1417
1418         dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &session_path,
1419                                                         DBUS_TYPE_INVALID);
1420         if (!session_path)
1421                 return -EINVAL;
1422
1423         session = g_hash_table_lookup(session_hash, session_path);
1424         if (!session)
1425                 return -EINVAL;
1426
1427         if (g_strcmp0(owner, session->owner) != 0)
1428                 return -EACCES;
1429
1430         connman_session_destroy(session);
1431
1432         return 0;
1433 }
1434
1435 int connman_session_connect(struct connman_service *service)
1436 {
1437         DBG("service %p name %s", service, __connman_service_get_name(service));
1438
1439         return __connman_service_connect(service,
1440                                 CONNMAN_SERVICE_CONNECT_REASON_SESSION);
1441 }
1442
1443 int connman_session_disconnect(struct connman_service *service)
1444 {
1445         DBG("service %p", service);
1446
1447         return __connman_service_disconnect(service);
1448 }
1449
1450 static enum connman_session_state service_to_session_state(
1451                                         enum connman_service_state state)
1452 {
1453         switch (state) {
1454         case CONNMAN_SERVICE_STATE_UNKNOWN:
1455         case CONNMAN_SERVICE_STATE_IDLE:
1456         case CONNMAN_SERVICE_STATE_ASSOCIATION:
1457         case CONNMAN_SERVICE_STATE_CONFIGURATION:
1458         case CONNMAN_SERVICE_STATE_DISCONNECT:
1459         case CONNMAN_SERVICE_STATE_FAILURE:
1460                 break;
1461         case CONNMAN_SERVICE_STATE_READY:
1462                 return CONNMAN_SESSION_STATE_CONNECTED;
1463         case CONNMAN_SERVICE_STATE_ONLINE:
1464                 return CONNMAN_SESSION_STATE_ONLINE;
1465         }
1466
1467         return CONNMAN_SESSION_STATE_DISCONNECTED;
1468 }
1469
1470 static void update_session_state(struct connman_session *session)
1471 {
1472         enum connman_service_state service_state;
1473         enum connman_session_state state = CONNMAN_SESSION_STATE_DISCONNECTED;
1474
1475         if (session->service) {
1476                 service_state = __connman_service_get_state(session->service);
1477                 state = service_to_session_state(service_state);
1478                 session->info->state = state;
1479         }
1480         session->info->state = state;
1481
1482         DBG("session %p state %s", session, state2string(state));
1483
1484         update_routing_table(session);
1485         session_notify(session);
1486 }
1487
1488 static bool session_match_service(struct connman_session *session,
1489                                 struct connman_service *service)
1490 {
1491         enum connman_service_type bearer_type;
1492         enum connman_service_type service_type;
1493         GSList *list;
1494
1495         if (policy && policy->allowed)
1496                 return policy->allowed(session, service);
1497
1498         for (list = session->info->config.allowed_bearers; list; list = list->next) {
1499                 bearer_type = GPOINTER_TO_INT(list->data);
1500                 service_type = connman_service_get_type(service);
1501
1502                 if (bearer_type == service_type)
1503                         return true;
1504         }
1505
1506         return false;
1507 }
1508
1509 static bool is_session_connected(struct connman_session *session,
1510                                 enum connman_service_state state)
1511
1512 {
1513         switch (state) {
1514         case CONNMAN_SERVICE_STATE_UNKNOWN:
1515         case CONNMAN_SERVICE_STATE_IDLE:
1516         case CONNMAN_SERVICE_STATE_ASSOCIATION:
1517         case CONNMAN_SERVICE_STATE_CONFIGURATION:
1518         case CONNMAN_SERVICE_STATE_DISCONNECT:
1519         case CONNMAN_SERVICE_STATE_FAILURE:
1520                           break;
1521         case CONNMAN_SERVICE_STATE_READY:
1522                 if (session->info->config.type == CONNMAN_SESSION_TYPE_INTERNET)
1523                         return false;
1524         case CONNMAN_SERVICE_STATE_ONLINE:
1525                 return true;
1526         }
1527
1528         return false;
1529 }
1530
1531 static void session_activate(struct connman_session *session)
1532 {
1533         GHashTableIter iter;
1534         gpointer key, value;
1535
1536         if (!service_hash)
1537                 return;
1538
1539         g_hash_table_iter_init(&iter, service_hash);
1540         while (g_hash_table_iter_next(&iter, &key, &value)) {
1541                 struct connman_service_info *info = value;
1542                 enum connman_service_state state;
1543
1544                 state = __connman_service_get_state(info->service);
1545
1546                 if (is_session_connected(session, state) &&
1547                                 session_match_service(session, info->service)) {
1548                         DBG("session %p add service %p", session, info->service);
1549
1550                         info->sessions = g_slist_prepend(info->sessions,
1551                                                         session);
1552                         session->service = info->service;
1553                         update_session_state(session);
1554
1555                         return;
1556                 }
1557         }
1558
1559         session_notify(session);
1560 }
1561
1562 static void session_deactivate(struct connman_session *session)
1563 {
1564         struct connman_service_info *info;
1565
1566         if (!service_hash)
1567                 return;
1568
1569         if (!session->service)
1570                 return;
1571
1572         info = g_hash_table_lookup(service_hash, session->service);
1573         if (!info)
1574                 return;
1575
1576         info->sessions = g_slist_remove(info->sessions, session);
1577         session->service = NULL;
1578
1579         session->info->state = CONNMAN_SESSION_STATE_DISCONNECTED;
1580 }
1581
1582 static void handle_service_state_online(struct connman_service *service,
1583                                         enum connman_service_state state,
1584                                         struct connman_service_info *info)
1585 {
1586         GHashTableIter iter;
1587         gpointer key, value;
1588
1589         g_hash_table_iter_init(&iter, session_hash);
1590         while (g_hash_table_iter_next(&iter, &key, &value)) {
1591                 struct connman_session *session = value;
1592                 bool connected;
1593
1594                 connected = is_session_connected(session, state);
1595
1596                 if (session->service == service) {
1597                         if (!connected) {
1598                                 DBG("session %p remove service %p", session, service);
1599                                 info->sessions = g_slist_remove(info->sessions,
1600                                                         session);
1601                                 session->service = NULL;
1602                                 update_session_state(session);
1603                         }
1604                 } else if (connected && session_match_service(session, service)) {
1605                         DBG("session %p add service %p", session, service);
1606
1607                         info->sessions = g_slist_prepend(info->sessions,
1608                                                         session);
1609                         session->service = service;
1610                         update_session_state(session);
1611                 }
1612         }
1613 }
1614
1615 static void handle_service_state_offline(struct connman_service *service,
1616                                         struct connman_service_info *info)
1617 {
1618         GSList *list;
1619
1620         for (list = info->sessions; list; list = list->next) {
1621                 struct connman_session *session = list->data;
1622
1623                 if (session->service != service) {
1624                         connman_warn("session %p should have session %p assigned",
1625                                         session, service);
1626                         continue;
1627                 }
1628
1629                 DBG("session %p remove service %p", session, service);
1630
1631                 session->service = NULL;
1632                 update_session_state(session);
1633         }
1634 }
1635
1636
1637 static void service_state_changed(struct connman_service *service,
1638                                 enum connman_service_state state)
1639 {
1640         struct connman_service_info *info;
1641
1642         DBG("service %p state %d", service, state);
1643
1644         info = g_hash_table_lookup(service_hash, service);
1645
1646         switch (state) {
1647         case CONNMAN_SERVICE_STATE_UNKNOWN:
1648         case CONNMAN_SERVICE_STATE_IDLE:
1649         case CONNMAN_SERVICE_STATE_ASSOCIATION:
1650         case CONNMAN_SERVICE_STATE_CONFIGURATION:
1651         case CONNMAN_SERVICE_STATE_FAILURE:
1652         case CONNMAN_SERVICE_STATE_DISCONNECT:
1653                 if (!info)
1654                         return;
1655
1656                 handle_service_state_offline(service, info);
1657
1658                 g_hash_table_remove(service_hash, service);
1659
1660                 return;
1661         case CONNMAN_SERVICE_STATE_READY:
1662         case CONNMAN_SERVICE_STATE_ONLINE:
1663                 if (!info) {
1664                         info = g_new0(struct connman_service_info, 1);
1665                         g_hash_table_replace(service_hash, service, info);
1666                 }
1667
1668                 info->service = service;
1669                 handle_service_state_online(service, state, info);
1670         }
1671 }
1672
1673 static void ipconfig_changed(struct connman_service *service,
1674                                 struct connman_ipconfig *ipconfig)
1675 {
1676         GHashTableIter iter;
1677         gpointer key, value;
1678         struct connman_session *session;
1679         struct session_info *info;
1680         enum connman_ipconfig_type type;
1681
1682         DBG("service %p ipconfig %p", service, ipconfig);
1683
1684         type = __connman_ipconfig_get_config_type(ipconfig);
1685
1686         g_hash_table_iter_init(&iter, session_hash);
1687
1688         while (g_hash_table_iter_next(&iter, &key, &value)) {
1689                 session = value;
1690                 info = session->info;
1691
1692                 if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED)
1693                         continue;
1694
1695                 if (session->service && session->service == service) {
1696                         update_routing_table(session);
1697
1698                         if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
1699                                 ipconfig_ipv4_changed(session);
1700                         else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
1701                                 ipconfig_ipv6_changed(session);
1702                 }
1703         }
1704 }
1705
1706 static struct connman_notifier session_notifier = {
1707         .name                   = "session",
1708         .service_state_changed  = service_state_changed,
1709         .ipconfig_changed       = ipconfig_changed,
1710 };
1711
1712 int __connman_session_init(void)
1713 {
1714         int err;
1715
1716         DBG("");
1717
1718         connection = connman_dbus_get_connection();
1719         if (!connection)
1720                 return -1;
1721
1722         err = connman_notifier_register(&session_notifier);
1723         if (err < 0) {
1724                 dbus_connection_unref(connection);
1725                 return err;
1726         }
1727
1728         session_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1729                                                 NULL, cleanup_session);
1730
1731         service_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
1732                                                 NULL, cleanup_service);
1733         if (__connman_firewall_is_up()) {
1734                 err = init_firewall();
1735                 if (err < 0)
1736                         return err;
1737         }
1738
1739         return 0;
1740 }
1741
1742 void __connman_session_cleanup(void)
1743 {
1744         DBG("");
1745
1746         if (!connection)
1747                 return;
1748
1749         cleanup_firewall();
1750
1751         connman_notifier_unregister(&session_notifier);
1752
1753         g_hash_table_foreach(session_hash, release_session, NULL);
1754         g_hash_table_destroy(session_hash);
1755         session_hash = NULL;
1756         g_hash_table_destroy(service_hash);
1757         service_hash = NULL;
1758
1759         dbus_connection_unref(connection);
1760 }