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