5 * Copyright (C) 2007-2010 Intel Corporation. All rights reserved.
6 * Copyright (C) 2011 BMW Car IT GmbH. All rights reserved.
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.
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.
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
35 struct gateway_config {
47 struct connman_service *service;
49 struct gateway_config *ipv4_gateway;
50 struct gateway_config *ipv6_gateway;
53 static GHashTable *gateway_hash = NULL;
55 static struct gateway_config *find_gateway(int index, const char *gateway)
63 g_hash_table_iter_init(&iter, gateway_hash);
65 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
66 struct gateway_data *data = value;
68 if (data->ipv4_gateway != NULL && data->index == index &&
69 g_str_equal(data->ipv4_gateway->gateway,
71 return data->ipv4_gateway;
73 if (data->ipv6_gateway != NULL && data->index == index &&
74 g_str_equal(data->ipv6_gateway->gateway,
76 return data->ipv6_gateway;
82 static int del_routes(struct gateway_data *data,
83 enum connman_ipconfig_type type)
85 int status4 = 0, status6 = 0;
86 int do_ipv4 = FALSE, do_ipv6 = FALSE;
88 if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
90 else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
93 do_ipv4 = do_ipv6 = TRUE;
95 if (do_ipv4 == TRUE && data->ipv4_gateway != NULL) {
96 if (data->ipv4_gateway->vpn == TRUE) {
97 if (data->ipv4_gateway->vpn_phy_index >= 0)
98 connman_inet_del_host_route(
99 data->ipv4_gateway->vpn_phy_index,
100 data->ipv4_gateway->gateway);
102 status4 = connman_inet_clear_gateway_address(
104 data->ipv4_gateway->vpn_ip);
106 } else if (g_strcmp0(data->ipv4_gateway->gateway,
108 status4 = connman_inet_clear_gateway_interface(
111 connman_inet_del_host_route(data->index,
112 data->ipv4_gateway->gateway);
113 status4 = connman_inet_clear_gateway_address(
115 data->ipv4_gateway->gateway);
119 if (do_ipv6 == TRUE && data->ipv6_gateway != NULL) {
120 if (data->ipv6_gateway->vpn == TRUE) {
121 if (data->ipv6_gateway->vpn_phy_index >= 0)
122 connman_inet_del_host_route(
123 data->ipv6_gateway->vpn_phy_index,
124 data->ipv6_gateway->gateway);
126 status6 = connman_inet_clear_ipv6_gateway_address(
128 data->ipv6_gateway->vpn_ip);
130 } else if (g_strcmp0(data->ipv6_gateway->gateway, "::") == 0) {
131 status6 = connman_inet_clear_ipv6_gateway_interface(
134 connman_inet_del_ipv6_host_route(data->index,
135 data->ipv6_gateway->gateway);
136 status6 = connman_inet_clear_ipv6_gateway_address(
138 data->ipv6_gateway->gateway);
142 return (status4 < 0 ? status4 : status6);
145 static int disable_gateway(struct gateway_data *data,
146 enum connman_ipconfig_type type)
148 gboolean active = FALSE;
150 if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
151 if (data->ipv4_gateway != NULL)
152 active = data->ipv4_gateway->active;
153 } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
154 if (data->ipv6_gateway != NULL)
155 active = data->ipv6_gateway->active;
159 DBG("type %d active %d", type, active);
162 return del_routes(data, type);
167 static struct gateway_data *add_gateway(struct connman_service *service,
168 int index, const char *gateway,
169 enum connman_ipconfig_type type)
171 struct gateway_data *data, *old;
172 struct gateway_config *config;
174 if (gateway == NULL || strlen(gateway) == 0)
177 data = g_try_new0(struct gateway_data, 1);
183 config = g_try_new0(struct gateway_config, 1);
184 if (config == NULL) {
189 config->gateway = g_strdup(gateway);
190 config->vpn_ip = NULL;
192 config->vpn_phy_index = -1;
193 config->active = FALSE;
195 if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
196 data->ipv4_gateway = config;
197 else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
198 data->ipv6_gateway = config;
200 g_free(config->gateway);
206 data->service = service;
208 data->order = __connman_service_get_order(service);
211 * If the service is already in the hash, then we
212 * must not replace it blindly but disable the gateway
213 * of the type we are replacing and take the other type
214 * from old gateway settings.
216 old = g_hash_table_lookup(gateway_hash, service);
218 DBG("Replacing gw %p ipv4 %p ipv6 %p", old,
219 old->ipv4_gateway, old->ipv6_gateway);
220 disable_gateway(old, type);
221 if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
222 data->ipv6_gateway = old->ipv6_gateway;
223 old->ipv6_gateway = NULL;
224 } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
225 data->ipv4_gateway = old->ipv4_gateway;
226 old->ipv4_gateway = NULL;
230 g_hash_table_replace(gateway_hash, service, data);
235 static void connection_newgateway(int index, const char *gateway)
237 struct gateway_config *config;
239 DBG("index %d gateway %s", index, gateway);
241 config = find_gateway(index, gateway);
245 config->active = TRUE;
248 static void set_default_gateway(struct gateway_data *data,
249 enum connman_ipconfig_type type)
252 int status4 = 0, status6 = 0;
253 int do_ipv4 = FALSE, do_ipv6 = FALSE;
255 if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
257 else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
260 do_ipv4 = do_ipv6 = TRUE;
262 DBG("type %d gateway ipv4 %p ipv6 %p", type, data->ipv4_gateway,
265 if (do_ipv4 == TRUE && data->ipv4_gateway != NULL &&
266 data->ipv4_gateway->vpn == TRUE) {
267 connman_inet_set_gateway_address(data->index,
268 data->ipv4_gateway->vpn_ip);
269 data->ipv4_gateway->active = TRUE;
271 __connman_service_indicate_default(data->service);
276 if (do_ipv6 == TRUE && data->ipv6_gateway != NULL &&
277 data->ipv6_gateway->vpn == TRUE) {
278 connman_inet_set_ipv6_gateway_address(data->index,
279 data->ipv6_gateway->vpn_ip);
280 data->ipv6_gateway->active = TRUE;
282 __connman_service_indicate_default(data->service);
287 index = __connman_service_get_index(data->service);
289 if (do_ipv4 == TRUE && data->ipv4_gateway != NULL &&
290 g_strcmp0(data->ipv4_gateway->gateway,
292 if (connman_inet_set_gateway_interface(index) < 0)
297 if (do_ipv6 == TRUE && data->ipv6_gateway != NULL &&
298 g_strcmp0(data->ipv6_gateway->gateway,
300 if (connman_inet_set_ipv6_gateway_interface(index) < 0)
305 if (do_ipv6 == TRUE && data->ipv6_gateway != NULL)
306 status6 = connman_inet_set_ipv6_gateway_address(index,
307 data->ipv6_gateway->gateway);
309 if (do_ipv4 == TRUE && data->ipv4_gateway != NULL)
310 status4 = connman_inet_set_gateway_address(index,
311 data->ipv4_gateway->gateway);
313 if (status4 < 0 || status6 < 0)
317 __connman_service_indicate_default(data->service);
320 static struct gateway_data *find_default_gateway(void)
322 struct gateway_data *found = NULL;
323 unsigned int order = 0;
327 g_hash_table_iter_init(&iter, gateway_hash);
329 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
330 struct gateway_data *data = value;
332 if (found == NULL || data->order > order) {
341 static void remove_gateway(gpointer user_data)
343 struct gateway_data *data = user_data;
345 DBG("gateway ipv4 %p ipv6 %p", data->ipv4_gateway, data->ipv6_gateway);
347 if (data->ipv4_gateway != NULL) {
348 g_free(data->ipv4_gateway->gateway);
349 g_free(data->ipv4_gateway->vpn_ip);
350 g_free(data->ipv4_gateway);
353 if (data->ipv6_gateway != NULL) {
354 g_free(data->ipv6_gateway->gateway);
355 g_free(data->ipv6_gateway->vpn_ip);
356 g_free(data->ipv6_gateway);
362 static void connection_delgateway(int index, const char *gateway)
364 struct gateway_config *config;
365 struct gateway_data *data;
367 DBG("index %d gateway %s", index, gateway);
369 config = find_gateway(index, gateway);
371 config->active = FALSE;
373 data = find_default_gateway();
375 set_default_gateway(data, CONNMAN_IPCONFIG_TYPE_ALL);
378 static struct connman_rtnl connection_rtnl = {
379 .name = "connection",
380 .newgateway = connection_newgateway,
381 .delgateway = connection_delgateway,
384 static struct gateway_data *find_active_gateway(void)
391 g_hash_table_iter_init(&iter, gateway_hash);
393 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
394 struct gateway_data *data = value;
396 if (data->ipv4_gateway != NULL &&
397 data->ipv4_gateway->active == TRUE)
400 if (data->ipv6_gateway != NULL &&
401 data->ipv6_gateway->active == TRUE)
408 static void update_order(void)
415 g_hash_table_iter_init(&iter, gateway_hash);
417 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
418 struct gateway_data *data = value;
420 data->order = __connman_service_get_order(data->service);
424 void __connman_connection_gateway_activate(struct connman_service *service,
425 enum connman_ipconfig_type type)
427 struct gateway_data *data = NULL;
429 data = g_hash_table_lookup(gateway_hash, service);
433 DBG("gateway %p/%p type %d", data->ipv4_gateway,
434 data->ipv6_gateway, type);
436 if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
437 data->ipv4_gateway->active = TRUE;
438 else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
439 data->ipv6_gateway->active = TRUE;
442 int __connman_connection_gateway_add(struct connman_service *service,
444 enum connman_ipconfig_type type,
447 struct gateway_data *active_gateway = NULL;
448 struct gateway_data *new_gateway = NULL;
451 index = __connman_service_get_index(service);
453 DBG("service %p index %d gateway %s vpn ip %s type %d",
454 service, index, gateway, peer, type);
456 active_gateway = find_active_gateway();
457 new_gateway = add_gateway(service, index, gateway, type);
458 if (new_gateway == NULL)
461 if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
462 new_gateway->ipv6_gateway != NULL &&
463 g_strcmp0(new_gateway->ipv6_gateway->gateway,
465 connman_inet_add_ipv6_host_route(index,
466 new_gateway->ipv6_gateway->gateway,
469 if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
470 new_gateway->ipv4_gateway != NULL &&
471 g_strcmp0(new_gateway->ipv4_gateway->gateway,
473 connman_inet_add_host_route(index,
474 new_gateway->ipv4_gateway->gateway,
477 if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
478 new_gateway->ipv4_gateway != NULL) {
479 __connman_service_nameserver_add_routes(service,
480 new_gateway->ipv4_gateway->gateway);
481 __connman_service_ipconfig_indicate_state(service,
482 CONNMAN_SERVICE_STATE_READY,
483 CONNMAN_IPCONFIG_TYPE_IPV4);
486 if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
487 new_gateway->ipv6_gateway != NULL) {
488 __connman_service_nameserver_add_routes(service,
489 new_gateway->ipv6_gateway->gateway);
490 __connman_service_ipconfig_indicate_state(service,
491 CONNMAN_SERVICE_STATE_READY,
492 CONNMAN_IPCONFIG_TYPE_IPV6);
495 if (connman_service_get_type(service) == CONNMAN_SERVICE_TYPE_VPN) {
496 if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
497 new_gateway->ipv4_gateway != NULL) {
498 new_gateway->ipv4_gateway->vpn = TRUE;
500 new_gateway->ipv4_gateway->vpn_ip =
502 else if (gateway != NULL)
503 new_gateway->ipv4_gateway->vpn_ip =
506 new_gateway->ipv4_gateway->vpn_phy_index =
507 active_gateway->index;
509 } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
510 new_gateway->ipv6_gateway != NULL) {
511 new_gateway->ipv6_gateway->vpn = TRUE;
513 new_gateway->ipv6_gateway->vpn_ip =
515 else if (gateway != NULL)
516 new_gateway->ipv6_gateway->vpn_ip =
519 new_gateway->ipv6_gateway->vpn_phy_index =
520 active_gateway->index;
523 if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
524 new_gateway->ipv4_gateway != NULL)
525 new_gateway->ipv4_gateway->vpn = FALSE;
527 if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
528 new_gateway->ipv6_gateway != NULL)
529 new_gateway->ipv6_gateway->vpn = FALSE;
532 if (active_gateway == NULL) {
533 set_default_gateway(new_gateway, type);
537 if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
538 new_gateway->ipv4_gateway != NULL &&
539 new_gateway->ipv4_gateway->vpn == TRUE) {
540 connman_inet_add_host_route(active_gateway->index,
541 new_gateway->ipv4_gateway->gateway,
542 active_gateway->ipv4_gateway->gateway);
543 connman_inet_clear_gateway_address(active_gateway->index,
544 active_gateway->ipv4_gateway->gateway);
547 if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
548 new_gateway->ipv6_gateway != NULL &&
549 new_gateway->ipv6_gateway->vpn == TRUE) {
550 connman_inet_add_ipv6_host_route(active_gateway->index,
551 new_gateway->ipv6_gateway->gateway,
552 active_gateway->ipv6_gateway->gateway);
553 connman_inet_clear_ipv6_gateway_address(active_gateway->index,
554 active_gateway->ipv6_gateway->gateway);
560 void __connman_connection_gateway_remove(struct connman_service *service,
561 enum connman_ipconfig_type type)
563 struct gateway_data *data = NULL;
564 gboolean set_default4 = FALSE, set_default6 = FALSE;
565 int do_ipv4 = FALSE, do_ipv6 = FALSE;
568 DBG("service %p type %d", service, type);
570 if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
572 else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
575 do_ipv4 = do_ipv6 = TRUE;
577 __connman_service_nameserver_del_routes(service);
579 data = g_hash_table_lookup(gateway_hash, service);
583 if (do_ipv4 == TRUE && data->ipv4_gateway != NULL)
584 set_default4 = data->ipv4_gateway->vpn;
586 if (do_ipv6 == TRUE && data->ipv6_gateway != NULL)
587 set_default6 = data->ipv6_gateway->vpn;
589 DBG("ipv4 gateway %s ipv6 gateway %s vpn %d/%d",
590 data->ipv4_gateway ? data->ipv4_gateway->gateway : "<null>",
591 data->ipv6_gateway ? data->ipv6_gateway->gateway : "<null>",
592 set_default4, set_default6);
594 if (do_ipv4 == TRUE && data->ipv4_gateway != NULL &&
595 data->ipv4_gateway->vpn == TRUE && data->index >= 0)
596 connman_inet_del_host_route(data->index,
597 data->ipv4_gateway->gateway);
599 if (do_ipv6 == TRUE && data->ipv6_gateway != NULL &&
600 data->ipv6_gateway->vpn == TRUE && data->index >= 0)
601 connman_inet_del_ipv6_host_route(data->index,
602 data->ipv6_gateway->gateway);
604 __connman_service_nameserver_del_routes(service);
606 err = disable_gateway(data, type);
609 * We remove the service from the hash only if all the gateway
610 * settings are to be removed.
612 if (do_ipv4 == do_ipv6 ||
613 (data->ipv4_gateway != NULL && data->ipv6_gateway == NULL
614 && do_ipv4 == TRUE) ||
615 (data->ipv6_gateway != NULL && data->ipv4_gateway == NULL
618 g_hash_table_remove(gateway_hash, service);
620 DBG("Not yet removing gw ipv4 %p/%d ipv6 %p/%d",
621 data->ipv4_gateway, do_ipv4,
622 data->ipv6_gateway, do_ipv6);
624 /* with vpn this will be called after the network was deleted,
625 * we need to call set_default here because we will not recieve any
626 * gateway delete notification.
627 * We hit the same issue if remove_gateway() fails.
629 if (set_default4 || set_default6 || err < 0) {
630 data = find_default_gateway();
632 set_default_gateway(data, type);
636 gboolean __connman_connection_update_gateway(void)
638 struct gateway_data *active_gateway, *default_gateway;
639 gboolean updated = FALSE;
641 if (gateway_hash == NULL)
646 active_gateway = find_active_gateway();
647 default_gateway = find_default_gateway();
649 if (active_gateway && active_gateway != default_gateway)
655 int __connman_connection_init(void)
661 gateway_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
662 NULL, remove_gateway);
664 err = connman_rtnl_register(&connection_rtnl);
666 connman_error("Failed to setup RTNL gateway driver");
671 void __connman_connection_cleanup(void)
678 connman_rtnl_unregister(&connection_rtnl);
680 g_hash_table_iter_init(&iter, gateway_hash);
682 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
683 struct gateway_data *data = value;
685 disable_gateway(data, CONNMAN_IPCONFIG_TYPE_ALL);
688 g_hash_table_destroy(gateway_hash);