connection: Fix service refcount
[platform/upstream/connman.git] / src / connection.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
6  *  Copyright (C) 2011  BMW Car IT GmbH. All rights reserved.
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 #include <string.h>
29 #include <net/if.h>
30
31 #include <gdbus.h>
32
33 #include "connman.h"
34
35 struct gateway_config {
36         gboolean active;
37         char *gateway;
38
39         /* VPN extra data */
40         gboolean vpn;
41         char *vpn_ip;
42         int vpn_phy_index;
43         char *vpn_phy_ip;
44 };
45
46 struct gateway_data {
47         int index;
48         struct connman_service *service;
49         unsigned int order;
50         struct gateway_config *ipv4_gateway;
51         struct gateway_config *ipv6_gateway;
52 };
53
54 static GHashTable *gateway_hash = NULL;
55
56 static struct gateway_config *find_gateway(int index, const char *gateway)
57 {
58         GHashTableIter iter;
59         gpointer value, key;
60
61         if (gateway == NULL)
62                 return NULL;
63
64         g_hash_table_iter_init(&iter, gateway_hash);
65
66         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
67                 struct gateway_data *data = value;
68
69                 if (data->ipv4_gateway != NULL && data->index == index &&
70                                 g_str_equal(data->ipv4_gateway->gateway,
71                                         gateway) == TRUE)
72                         return data->ipv4_gateway;
73
74                 if (data->ipv6_gateway != NULL && data->index == index &&
75                                 g_str_equal(data->ipv6_gateway->gateway,
76                                         gateway) == TRUE)
77                         return data->ipv6_gateway;
78         }
79
80         return NULL;
81 }
82
83 static int del_routes(struct gateway_data *data,
84                         enum connman_ipconfig_type type)
85 {
86         int status4 = 0, status6 = 0;
87         int do_ipv4 = FALSE, do_ipv6 = FALSE;
88
89         if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
90                 do_ipv4 = TRUE;
91         else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
92                 do_ipv6 = TRUE;
93         else
94                 do_ipv4 = do_ipv6 = TRUE;
95
96         if (do_ipv4 == TRUE && data->ipv4_gateway != NULL) {
97                 if (data->ipv4_gateway->vpn == TRUE) {
98                         if (data->ipv4_gateway->vpn_phy_index >= 0)
99                                 connman_inet_del_host_route(
100                                         data->ipv4_gateway->vpn_phy_index,
101                                         data->ipv4_gateway->gateway);
102
103                         status4 = connman_inet_clear_gateway_address(
104                                                 data->index,
105                                                 data->ipv4_gateway->vpn_ip);
106
107                 } else if (g_strcmp0(data->ipv4_gateway->gateway,
108                                                         "0.0.0.0") == 0) {
109                         status4 = connman_inet_clear_gateway_interface(
110                                                                 data->index);
111                 } else {
112                         connman_inet_del_host_route(data->index,
113                                                 data->ipv4_gateway->gateway);
114                         status4 = connman_inet_clear_gateway_address(
115                                                 data->index,
116                                                 data->ipv4_gateway->gateway);
117                 }
118         }
119
120         if (do_ipv6 == TRUE && data->ipv6_gateway != NULL) {
121                 if (data->ipv6_gateway->vpn == TRUE) {
122                         if (data->ipv6_gateway->vpn_phy_index >= 0)
123                                 connman_inet_del_host_route(
124                                         data->ipv6_gateway->vpn_phy_index,
125                                         data->ipv6_gateway->gateway);
126
127                         status6 = connman_inet_clear_ipv6_gateway_address(
128                                                 data->index,
129                                                 data->ipv6_gateway->vpn_ip);
130
131                 } else if (g_strcmp0(data->ipv6_gateway->gateway, "::") == 0) {
132                         status6 = connman_inet_clear_ipv6_gateway_interface(
133                                                                 data->index);
134                 } else {
135                         connman_inet_del_ipv6_host_route(data->index,
136                                                 data->ipv6_gateway->gateway);
137                         status6 = connman_inet_clear_ipv6_gateway_address(
138                                                 data->index,
139                                                 data->ipv6_gateway->gateway);
140                 }
141         }
142
143         return (status4 < 0 ? status4 : status6);
144 }
145
146 static int disable_gateway(struct gateway_data *data,
147                         enum connman_ipconfig_type type)
148 {
149         gboolean active = FALSE;
150
151         if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
152                 if (data->ipv4_gateway != NULL)
153                         active = data->ipv4_gateway->active;
154         } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
155                 if (data->ipv6_gateway != NULL)
156                         active = data->ipv6_gateway->active;
157         } else
158                 active = TRUE;
159
160         DBG("type %d active %d", type, active);
161
162         if (active == TRUE)
163                 return del_routes(data, type);
164
165         return 0;
166 }
167
168 static struct gateway_data *add_gateway(struct connman_service *service,
169                                         int index, const char *gateway,
170                                         enum connman_ipconfig_type type)
171 {
172         struct gateway_data *data, *old;
173         struct gateway_config *config;
174
175         if (gateway == NULL || strlen(gateway) == 0)
176                 return NULL;
177
178         data = g_try_new0(struct gateway_data, 1);
179         if (data == NULL)
180                 return NULL;
181
182         data->index = index;
183
184         config = g_try_new0(struct gateway_config, 1);
185         if (config == NULL) {
186                 g_free(data);
187                 return NULL;
188         }
189
190         config->gateway = g_strdup(gateway);
191         config->vpn_ip = NULL;
192         config->vpn_phy_ip = NULL;
193         config->vpn = FALSE;
194         config->vpn_phy_index = -1;
195         config->active = FALSE;
196
197         if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
198                 data->ipv4_gateway = config;
199         else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
200                 data->ipv6_gateway = config;
201         else {
202                 g_free(config->gateway);
203                 g_free(config);
204                 g_free(data);
205                 return NULL;
206         }
207
208         data->service = service;
209
210         data->order = __connman_service_get_order(service);
211
212         /*
213          * If the service is already in the hash, then we
214          * must not replace it blindly but disable the gateway
215          * of the type we are replacing and take the other type
216          * from old gateway settings.
217          */
218         old = g_hash_table_lookup(gateway_hash, service);
219         if (old != NULL) {
220                 DBG("Replacing gw %p ipv4 %p ipv6 %p", old,
221                         old->ipv4_gateway, old->ipv6_gateway);
222                 disable_gateway(old, type);
223                 if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
224                         data->ipv6_gateway = old->ipv6_gateway;
225                         old->ipv6_gateway = NULL;
226                 } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
227                         data->ipv4_gateway = old->ipv4_gateway;
228                         old->ipv4_gateway = NULL;
229                 }
230         }
231
232         connman_service_ref(service);
233         g_hash_table_replace(gateway_hash, service, data);
234
235         return data;
236 }
237
238 static void connection_newgateway(int index, const char *gateway)
239 {
240         struct gateway_config *config;
241
242         DBG("index %d gateway %s", index, gateway);
243
244         config = find_gateway(index, gateway);
245         if (config == NULL)
246                 return;
247
248         config->active = TRUE;
249 }
250
251 static void set_default_gateway(struct gateway_data *data,
252                                 enum connman_ipconfig_type type)
253 {
254         int index;
255         int status4 = 0, status6 = 0;
256         int do_ipv4 = FALSE, do_ipv6 = FALSE;
257
258         if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
259                 do_ipv4 = TRUE;
260         else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
261                 do_ipv6 = TRUE;
262         else
263                 do_ipv4 = do_ipv6 = TRUE;
264
265         DBG("type %d gateway ipv4 %p ipv6 %p", type, data->ipv4_gateway,
266                                                 data->ipv6_gateway);
267
268         if (do_ipv4 == TRUE && data->ipv4_gateway != NULL &&
269                                         data->ipv4_gateway->vpn == TRUE) {
270                 connman_inet_set_gateway_address(data->index,
271                                                 data->ipv4_gateway->vpn_ip);
272                 connman_inet_add_host_route(data->ipv4_gateway->vpn_phy_index,
273                                         data->ipv4_gateway->vpn_ip,
274                                         data->ipv4_gateway->vpn_phy_ip);
275                 data->ipv4_gateway->active = TRUE;
276
277                 __connman_service_indicate_default(data->service);
278
279                 return;
280         }
281
282         if (do_ipv6 == TRUE && data->ipv6_gateway != NULL &&
283                                         data->ipv6_gateway->vpn == TRUE) {
284                 connman_inet_set_ipv6_gateway_address(data->index,
285                                                 data->ipv6_gateway->vpn_ip);
286                 connman_inet_add_ipv6_host_route(
287                                         data->ipv6_gateway->vpn_phy_index,
288                                         data->ipv6_gateway->vpn_ip,
289                                         data->ipv6_gateway->vpn_phy_ip);
290                 data->ipv6_gateway->active = TRUE;
291
292                 __connman_service_indicate_default(data->service);
293
294                 return;
295         }
296
297         index = __connman_service_get_index(data->service);
298
299         if (do_ipv4 == TRUE && data->ipv4_gateway != NULL &&
300                         g_strcmp0(data->ipv4_gateway->gateway,
301                                                         "0.0.0.0") == 0) {
302                 if (connman_inet_set_gateway_interface(index) < 0)
303                         return;
304                 goto done;
305         }
306
307         if (do_ipv6 == TRUE && data->ipv6_gateway != NULL &&
308                         g_strcmp0(data->ipv6_gateway->gateway,
309                                                         "::") == 0) {
310                 if (connman_inet_set_ipv6_gateway_interface(index) < 0)
311                         return;
312                 goto done;
313         }
314
315         if (do_ipv6 == TRUE && data->ipv6_gateway != NULL)
316                 status6 = connman_inet_set_ipv6_gateway_address(index,
317                                                 data->ipv6_gateway->gateway);
318
319         if (do_ipv4 == TRUE && data->ipv4_gateway != NULL)
320                 status4 = connman_inet_set_gateway_address(index,
321                                                 data->ipv4_gateway->gateway);
322
323         if (status4 < 0 || status6 < 0)
324                 return;
325
326 done:
327         __connman_service_indicate_default(data->service);
328 }
329
330 static void unset_default_gateway(struct gateway_data *data,
331                                 enum connman_ipconfig_type type)
332 {
333         int index;
334         int do_ipv4 = FALSE, do_ipv6 = FALSE;
335
336         if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
337                 do_ipv4 = TRUE;
338         else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
339                 do_ipv6 = TRUE;
340         else
341                 do_ipv4 = do_ipv6 = TRUE;
342
343         DBG("type %d gateway ipv4 %p ipv6 %p", type, data->ipv4_gateway,
344                                                 data->ipv6_gateway);
345
346         if (do_ipv4 == TRUE && data->ipv4_gateway != NULL &&
347                                         data->ipv4_gateway->vpn == TRUE) {
348                 connman_inet_del_host_route(data->index,
349                                                 data->ipv4_gateway->vpn_ip);
350                 connman_inet_clear_gateway_address(data->index,
351                                                 data->ipv4_gateway->vpn_ip);
352                 data->ipv4_gateway->active = FALSE;
353
354                 return;
355         }
356
357         if (do_ipv6 == TRUE && data->ipv6_gateway != NULL &&
358                                         data->ipv6_gateway->vpn == TRUE) {
359                 connman_inet_del_ipv6_host_route(data->index,
360                                                 data->ipv6_gateway->vpn_ip);
361                 connman_inet_clear_ipv6_gateway_address(data->index,
362                                                 data->ipv6_gateway->vpn_ip);
363                 data->ipv6_gateway->active = FALSE;
364
365                 return;
366         }
367
368         index = __connman_service_get_index(data->service);
369
370         if (do_ipv4 == TRUE && data->ipv4_gateway != NULL &&
371                         g_strcmp0(data->ipv4_gateway->gateway,
372                                                         "0.0.0.0") == 0) {
373                 connman_inet_clear_gateway_interface(index);
374                 return;
375         }
376
377         if (do_ipv6 == TRUE && data->ipv6_gateway != NULL &&
378                         g_strcmp0(data->ipv6_gateway->gateway,
379                                                         "::") == 0) {
380                 connman_inet_clear_ipv6_gateway_interface(index);
381                 return;
382         }
383
384         if (do_ipv6 == TRUE && data->ipv6_gateway != NULL)
385                 connman_inet_clear_ipv6_gateway_address(index,
386                                                 data->ipv6_gateway->gateway);
387
388         if (do_ipv4 == TRUE && data->ipv4_gateway != NULL)
389                 connman_inet_clear_gateway_address(index,
390                                                 data->ipv4_gateway->gateway);
391 }
392
393 static struct gateway_data *find_default_gateway(void)
394 {
395         struct gateway_data *found = NULL;
396         unsigned int order = 0;
397         GHashTableIter iter;
398         gpointer value, key;
399
400         g_hash_table_iter_init(&iter, gateway_hash);
401
402         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
403                 struct gateway_data *data = value;
404
405                 if (found == NULL || data->order > order) {
406                         found = data;
407                         order = data->order;
408                 }
409         }
410
411         return found;
412 }
413
414 static void remove_gateway(gpointer user_data)
415 {
416         struct gateway_data *data = user_data;
417
418         DBG("gateway ipv4 %p ipv6 %p", data->ipv4_gateway, data->ipv6_gateway);
419
420         if (data->ipv4_gateway != NULL) {
421                 g_free(data->ipv4_gateway->gateway);
422                 g_free(data->ipv4_gateway->vpn_ip);
423                 g_free(data->ipv4_gateway->vpn_phy_ip);
424                 g_free(data->ipv4_gateway);
425         }
426
427         if (data->ipv6_gateway != NULL) {
428                 g_free(data->ipv6_gateway->gateway);
429                 g_free(data->ipv6_gateway->vpn_ip);
430                 g_free(data->ipv6_gateway->vpn_phy_ip);
431                 g_free(data->ipv6_gateway);
432         }
433
434         g_free(data);
435 }
436
437 static void connection_delgateway(int index, const char *gateway)
438 {
439         struct gateway_config *config;
440         struct gateway_data *data;
441
442         DBG("index %d gateway %s", index, gateway);
443
444         config = find_gateway(index, gateway);
445         if (config != NULL)
446                 config->active = FALSE;
447
448         data = find_default_gateway();
449         if (data != NULL)
450                 set_default_gateway(data, CONNMAN_IPCONFIG_TYPE_ALL);
451 }
452
453 static struct connman_rtnl connection_rtnl = {
454         .name           = "connection",
455         .newgateway     = connection_newgateway,
456         .delgateway     = connection_delgateway,
457 };
458
459 static struct gateway_data *find_active_gateway(void)
460 {
461         GHashTableIter iter;
462         gpointer value, key;
463
464         DBG("");
465
466         g_hash_table_iter_init(&iter, gateway_hash);
467
468         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
469                 struct gateway_data *data = value;
470
471                 if (data->ipv4_gateway != NULL &&
472                                 data->ipv4_gateway->active == TRUE)
473                         return data;
474
475                 if (data->ipv6_gateway != NULL &&
476                                 data->ipv6_gateway->active == TRUE)
477                         return data;
478         }
479
480         return NULL;
481 }
482
483 static void update_order(void)
484 {
485         GHashTableIter iter;
486         gpointer value, key;
487
488         DBG("");
489
490         g_hash_table_iter_init(&iter, gateway_hash);
491
492         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
493                 struct gateway_data *data = value;
494
495                 data->order = __connman_service_get_order(data->service);
496         }
497 }
498
499 void __connman_connection_gateway_activate(struct connman_service *service,
500                                         enum connman_ipconfig_type type)
501 {
502         struct gateway_data *data = NULL;
503
504         data = g_hash_table_lookup(gateway_hash, service);
505         if (data == NULL)
506                 return;
507
508         DBG("gateway %p/%p type %d", data->ipv4_gateway,
509                                         data->ipv6_gateway, type);
510
511         if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
512                 data->ipv4_gateway->active = TRUE;
513         else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
514                 data->ipv6_gateway->active = TRUE;
515 }
516
517 int __connman_connection_gateway_add(struct connman_service *service,
518                                         const char *gateway,
519                                         enum connman_ipconfig_type type,
520                                         const char *peer)
521 {
522         struct gateway_data *active_gateway = NULL;
523         struct gateway_data *new_gateway = NULL;
524         int index;
525
526         index = __connman_service_get_index(service);
527
528         DBG("service %p index %d gateway %s vpn ip %s type %d",
529                 service, index, gateway, peer, type);
530
531         /*
532          * If gateway is NULL, it's a point to point link and the default
533          * gateway for ipv4 is 0.0.0.0 and for ipv6 is ::, meaning the
534          * interface
535          */
536         if (gateway == NULL && type == CONNMAN_IPCONFIG_TYPE_IPV4)
537                 gateway = "0.0.0.0";
538
539         if (gateway == NULL && type == CONNMAN_IPCONFIG_TYPE_IPV6)
540                 gateway = "::";
541
542         active_gateway = find_active_gateway();
543         new_gateway = add_gateway(service, index, gateway, type);
544         if (new_gateway == NULL)
545                 return -EINVAL;
546
547         if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
548                         new_gateway->ipv6_gateway != NULL &&
549                         g_strcmp0(new_gateway->ipv6_gateway->gateway,
550                                                                 "::") != 0)
551                 connman_inet_add_ipv6_host_route(index,
552                                         new_gateway->ipv6_gateway->gateway,
553                                         NULL);
554
555         if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
556                         new_gateway->ipv4_gateway != NULL &&
557                         g_strcmp0(new_gateway->ipv4_gateway->gateway,
558                                                         "0.0.0.0") != 0)
559                 connman_inet_add_host_route(index,
560                                         new_gateway->ipv4_gateway->gateway,
561                                         NULL);
562
563         if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
564                                 new_gateway->ipv4_gateway != NULL) {
565                 __connman_service_nameserver_add_routes(service,
566                                         new_gateway->ipv4_gateway->gateway);
567                 __connman_service_ipconfig_indicate_state(service,
568                                                 CONNMAN_SERVICE_STATE_READY,
569                                                 CONNMAN_IPCONFIG_TYPE_IPV4);
570         }
571
572         if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
573                                 new_gateway->ipv6_gateway != NULL) {
574                 __connman_service_nameserver_add_routes(service,
575                                         new_gateway->ipv6_gateway->gateway);
576                 __connman_service_ipconfig_indicate_state(service,
577                                                 CONNMAN_SERVICE_STATE_READY,
578                                                 CONNMAN_IPCONFIG_TYPE_IPV6);
579         }
580
581         if (connman_service_get_type(service) == CONNMAN_SERVICE_TYPE_VPN) {
582                 if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
583                                         new_gateway->ipv4_gateway != NULL) {
584                         new_gateway->ipv4_gateway->vpn = TRUE;
585                         if (peer != NULL)
586                                 new_gateway->ipv4_gateway->vpn_ip =
587                                                         g_strdup(peer);
588                         else if (gateway != NULL)
589                                 new_gateway->ipv4_gateway->vpn_ip =
590                                                         g_strdup(gateway);
591                         if (active_gateway) {
592                                 const char *new_ipv4_gateway;
593
594                                 new_ipv4_gateway =
595                                         active_gateway->ipv4_gateway->gateway;
596                                 if (new_ipv4_gateway != NULL &&
597                                          g_strcmp0(new_ipv4_gateway,
598                                                         "0.0.0.0") != 0)
599                                         new_gateway->ipv4_gateway->vpn_phy_ip =
600                                                 g_strdup(new_ipv4_gateway);
601
602                                 new_gateway->ipv4_gateway->vpn_phy_index =
603                                                         active_gateway->index;
604                         }
605
606                 } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
607                                         new_gateway->ipv6_gateway != NULL) {
608                         new_gateway->ipv6_gateway->vpn = TRUE;
609                         if (peer != NULL)
610                                 new_gateway->ipv6_gateway->vpn_ip =
611                                                         g_strdup(peer);
612                         else if (gateway != NULL)
613                                 new_gateway->ipv6_gateway->vpn_ip =
614                                                         g_strdup(gateway);
615                         if (active_gateway) {
616                                 const char *new_ipv6_gateway;
617
618                                 new_ipv6_gateway =
619                                         active_gateway->ipv6_gateway->gateway;
620                                 if (new_ipv6_gateway != NULL &&
621                                         g_strcmp0(new_ipv6_gateway, "::") != 0)
622                                         new_gateway->ipv6_gateway->vpn_phy_ip =
623                                                 g_strdup(new_ipv6_gateway);
624
625                                 new_gateway->ipv6_gateway->vpn_phy_index =
626                                                         active_gateway->index;
627                         }
628                 }
629         } else {
630                 if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
631                                         new_gateway->ipv4_gateway != NULL)
632                         new_gateway->ipv4_gateway->vpn = FALSE;
633
634                 if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
635                                         new_gateway->ipv6_gateway != NULL)
636                         new_gateway->ipv6_gateway->vpn = FALSE;
637         }
638
639         if (active_gateway == NULL) {
640                 set_default_gateway(new_gateway, type);
641                 return 0;
642         }
643
644         if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
645                                 new_gateway->ipv4_gateway != NULL &&
646                                 new_gateway->ipv4_gateway->vpn == TRUE) {
647                 connman_inet_add_host_route(active_gateway->index,
648                                         new_gateway->ipv4_gateway->gateway,
649                                         active_gateway->ipv4_gateway->gateway);
650                 connman_inet_clear_gateway_address(active_gateway->index,
651                                         active_gateway->ipv4_gateway->gateway);
652         }
653
654         if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
655                                 new_gateway->ipv6_gateway != NULL &&
656                                 new_gateway->ipv6_gateway->vpn == TRUE) {
657                 connman_inet_add_ipv6_host_route(active_gateway->index,
658                                         new_gateway->ipv6_gateway->gateway,
659                                         active_gateway->ipv6_gateway->gateway);
660                 connman_inet_clear_ipv6_gateway_address(active_gateway->index,
661                                         active_gateway->ipv6_gateway->gateway);
662         }
663
664         return 0;
665 }
666
667 void __connman_connection_gateway_remove(struct connman_service *service,
668                                         enum connman_ipconfig_type type)
669 {
670         struct gateway_data *data = NULL;
671         gboolean set_default4 = FALSE, set_default6 = FALSE;
672         int do_ipv4 = FALSE, do_ipv6 = FALSE;
673         int err;
674
675         DBG("service %p type %d", service, type);
676
677         if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
678                 do_ipv4 = TRUE;
679         else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
680                 do_ipv6 = TRUE;
681         else
682                 do_ipv4 = do_ipv6 = TRUE;
683
684         __connman_service_nameserver_del_routes(service);
685
686         data = g_hash_table_lookup(gateway_hash, service);
687         if (data == NULL)
688                 return;
689
690         if (do_ipv4 == TRUE && data->ipv4_gateway != NULL)
691                 set_default4 = data->ipv4_gateway->vpn;
692
693         if (do_ipv6 == TRUE && data->ipv6_gateway != NULL)
694                 set_default6 = data->ipv6_gateway->vpn;
695
696         DBG("ipv4 gateway %s ipv6 gateway %s vpn %d/%d",
697                 data->ipv4_gateway ? data->ipv4_gateway->gateway : "<null>",
698                 data->ipv6_gateway ? data->ipv6_gateway->gateway : "<null>",
699                 set_default4, set_default6);
700
701         if (do_ipv4 == TRUE && data->ipv4_gateway != NULL &&
702                         data->ipv4_gateway->vpn == TRUE && data->index >= 0)
703                 connman_inet_del_host_route(data->index,
704                                                 data->ipv4_gateway->gateway);
705
706         if (do_ipv6 == TRUE && data->ipv6_gateway != NULL &&
707                         data->ipv6_gateway->vpn == TRUE && data->index >= 0)
708                 connman_inet_del_ipv6_host_route(data->index,
709                                                 data->ipv6_gateway->gateway);
710
711         __connman_service_nameserver_del_routes(service);
712
713         err = disable_gateway(data, type);
714
715         /*
716          * We may refcount service twice seperately for ipv4 and ipv6
717          * then we need to unref accordingly.
718          */
719         if (do_ipv4 == do_ipv6 && data->ipv4_gateway != NULL &&
720                                         data->ipv6_gateway != NULL)
721                 connman_service_unref(service);
722
723         /*
724          * We remove the service from the hash only if all the gateway
725          * settings are to be removed.
726          */
727         if (do_ipv4 == do_ipv6 ||
728                 (data->ipv4_gateway != NULL && data->ipv6_gateway == NULL
729                         && do_ipv4 == TRUE) ||
730                 (data->ipv6_gateway != NULL && data->ipv4_gateway == NULL
731                         && do_ipv6 == TRUE)
732                 ) {
733                 connman_service_unref(service);
734                 g_hash_table_remove(gateway_hash, service);
735         } else
736                 DBG("Not yet removing gw ipv4 %p/%d ipv6 %p/%d",
737                         data->ipv4_gateway, do_ipv4,
738                         data->ipv6_gateway, do_ipv6);
739
740         /* with vpn this will be called after the network was deleted,
741          * we need to call set_default here because we will not recieve any
742          * gateway delete notification.
743          * We hit the same issue if remove_gateway() fails.
744          */
745         if (set_default4 || set_default6 || err < 0) {
746                 data = find_default_gateway();
747                 if (data != NULL)
748                         set_default_gateway(data, type);
749         }
750 }
751
752 gboolean __connman_connection_update_gateway(void)
753 {
754         struct gateway_data *active_gateway, *default_gateway;
755         gboolean updated = FALSE;
756
757         if (gateway_hash == NULL)
758                 return updated;
759
760         active_gateway = find_active_gateway();
761
762         update_order();
763
764         default_gateway = find_default_gateway();
765
766         if (active_gateway && active_gateway != default_gateway) {
767                 updated = TRUE;
768
769                 if (active_gateway->ipv4_gateway)
770                         unset_default_gateway(active_gateway,
771                                         CONNMAN_IPCONFIG_TYPE_IPV4);
772
773                 if (active_gateway->ipv6_gateway)
774                         unset_default_gateway(active_gateway,
775                                         CONNMAN_IPCONFIG_TYPE_IPV6);
776
777                 if (default_gateway) {
778                         if (default_gateway->ipv4_gateway)
779                                 set_default_gateway(default_gateway,
780                                                 CONNMAN_IPCONFIG_TYPE_IPV4);
781
782                         if (default_gateway->ipv6_gateway)
783                                 set_default_gateway(default_gateway,
784                                                 CONNMAN_IPCONFIG_TYPE_IPV6);
785                 }
786         }
787
788         return updated;
789 }
790
791 int __connman_connection_init(void)
792 {
793         int err;
794
795         DBG("");
796
797         gateway_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
798                                                         NULL, remove_gateway);
799
800         err = connman_rtnl_register(&connection_rtnl);
801         if (err < 0)
802                 connman_error("Failed to setup RTNL gateway driver");
803
804         return err;
805 }
806
807 void __connman_connection_cleanup(void)
808 {
809         GHashTableIter iter;
810         gpointer value, key;
811
812         DBG("");
813
814         connman_rtnl_unregister(&connection_rtnl);
815
816         g_hash_table_iter_init(&iter, gateway_hash);
817
818         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
819                 struct gateway_data *data = value;
820
821                 disable_gateway(data, CONNMAN_IPCONFIG_TYPE_ALL);
822         }
823
824         g_hash_table_destroy(gateway_hash);
825         gateway_hash = NULL;
826 }