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
34 struct gateway_config {
46 struct connman_service *service;
48 struct gateway_config *ipv4_gateway;
49 struct gateway_config *ipv6_gateway;
52 static GHashTable *gateway_hash = NULL;
54 static struct gateway_config *find_gateway(int index, const char *gateway)
62 g_hash_table_iter_init(&iter, gateway_hash);
64 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
65 struct gateway_data *data = value;
67 if (data->ipv4_gateway != NULL && data->index == index &&
68 g_str_equal(data->ipv4_gateway->gateway,
70 return data->ipv4_gateway;
72 if (data->ipv6_gateway != NULL && data->index == index &&
73 g_str_equal(data->ipv6_gateway->gateway,
75 return data->ipv6_gateway;
81 static int del_routes(struct gateway_data *data,
82 enum connman_ipconfig_type type)
84 int status4 = 0, status6 = 0;
85 int do_ipv4 = FALSE, do_ipv6 = FALSE;
87 if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
89 else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
92 do_ipv4 = do_ipv6 = TRUE;
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);
101 status4 = connman_inet_clear_gateway_address(
103 data->ipv4_gateway->vpn_ip);
105 } else if (g_strcmp0(data->ipv4_gateway->gateway,
107 status4 = connman_inet_clear_gateway_interface(
110 connman_inet_del_host_route(data->index,
111 data->ipv4_gateway->gateway);
112 status4 = connman_inet_clear_gateway_address(
114 data->ipv4_gateway->gateway);
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);
125 status6 = connman_inet_clear_ipv6_gateway_address(
127 data->ipv6_gateway->vpn_ip);
129 } else if (g_strcmp0(data->ipv6_gateway->gateway, "::") == 0) {
130 status6 = connman_inet_clear_ipv6_gateway_interface(
133 connman_inet_del_ipv6_host_route(data->index,
134 data->ipv6_gateway->gateway);
135 status6 = connman_inet_clear_ipv6_gateway_address(
137 data->ipv6_gateway->gateway);
141 return (status4 < 0 ? status4 : status6);
144 static int disable_gateway(struct gateway_data *data,
145 enum connman_ipconfig_type type)
147 gboolean active = FALSE;
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;
158 DBG("type %d active %d", type, active);
161 return del_routes(data, type);
166 static struct gateway_data *add_gateway(struct connman_service *service,
167 int index, const char *gateway,
168 enum connman_ipconfig_type type)
170 struct gateway_data *data, *old;
171 struct gateway_config *config;
173 if (gateway == NULL || strlen(gateway) == 0)
176 data = g_try_new0(struct gateway_data, 1);
182 config = g_try_new0(struct gateway_config, 1);
183 if (config == NULL) {
188 config->gateway = g_strdup(gateway);
189 config->vpn_ip = NULL;
191 config->vpn_phy_index = -1;
192 config->active = FALSE;
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;
199 g_free(config->gateway);
205 data->service = service;
207 data->order = __connman_service_get_order(service);
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.
215 old = g_hash_table_lookup(gateway_hash, service);
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;
229 g_hash_table_replace(gateway_hash, service, data);
234 static void connection_newgateway(int index, const char *gateway)
236 struct gateway_config *config;
238 DBG("index %d gateway %s", index, gateway);
240 config = find_gateway(index, gateway);
244 config->active = TRUE;
247 static void set_default_gateway(struct gateway_data *data,
248 enum connman_ipconfig_type type)
251 int status4 = 0, status6 = 0;
252 int do_ipv4 = FALSE, do_ipv6 = FALSE;
254 if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
256 else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
259 do_ipv4 = do_ipv6 = TRUE;
261 DBG("type %d gateway ipv4 %p ipv6 %p", type, data->ipv4_gateway,
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;
270 __connman_service_indicate_default(data->service);
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;
281 __connman_service_indicate_default(data->service);
286 index = __connman_service_get_index(data->service);
288 if (do_ipv4 == TRUE && data->ipv4_gateway != NULL &&
289 g_strcmp0(data->ipv4_gateway->gateway,
291 if (connman_inet_set_gateway_interface(index) < 0)
296 if (do_ipv6 == TRUE && data->ipv6_gateway != NULL &&
297 g_strcmp0(data->ipv6_gateway->gateway,
299 if (connman_inet_set_ipv6_gateway_interface(index) < 0)
304 if (do_ipv6 == TRUE && data->ipv6_gateway != NULL)
305 status6 = connman_inet_set_ipv6_gateway_address(index,
306 data->ipv6_gateway->gateway);
308 if (do_ipv4 == TRUE && data->ipv4_gateway != NULL)
309 status4 = connman_inet_set_gateway_address(index,
310 data->ipv4_gateway->gateway);
312 if (status4 < 0 || status6 < 0)
316 __connman_service_indicate_default(data->service);
319 static struct gateway_data *find_default_gateway(void)
321 struct gateway_data *found = NULL;
322 unsigned int order = 0;
326 g_hash_table_iter_init(&iter, gateway_hash);
328 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
329 struct gateway_data *data = value;
331 if (found == NULL || data->order > order) {
340 static void remove_gateway(gpointer user_data)
342 struct gateway_data *data = user_data;
344 DBG("gateway ipv4 %p ipv6 %p", data->ipv4_gateway, data->ipv6_gateway);
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);
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);
361 static void connection_delgateway(int index, const char *gateway)
363 struct gateway_config *config;
364 struct gateway_data *data;
366 DBG("index %d gateway %s", index, gateway);
368 config = find_gateway(index, gateway);
370 config->active = FALSE;
372 data = find_default_gateway();
374 set_default_gateway(data, CONNMAN_IPCONFIG_TYPE_ALL);
377 static struct connman_rtnl connection_rtnl = {
378 .name = "connection",
379 .newgateway = connection_newgateway,
380 .delgateway = connection_delgateway,
383 static struct gateway_data *find_active_gateway(void)
390 g_hash_table_iter_init(&iter, gateway_hash);
392 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
393 struct gateway_data *data = value;
395 if (data->ipv4_gateway != NULL &&
396 data->ipv4_gateway->active == TRUE)
399 if (data->ipv6_gateway != NULL &&
400 data->ipv6_gateway->active == TRUE)
407 static void update_order(void)
414 g_hash_table_iter_init(&iter, gateway_hash);
416 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
417 struct gateway_data *data = value;
419 data->order = __connman_service_get_order(data->service);
423 void __connman_connection_gateway_activate(struct connman_service *service,
424 enum connman_ipconfig_type type)
426 struct gateway_data *data = NULL;
428 data = g_hash_table_lookup(gateway_hash, service);
432 DBG("gateway %p/%p type %d", data->ipv4_gateway,
433 data->ipv6_gateway, type);
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;
441 int __connman_connection_gateway_add(struct connman_service *service,
443 enum connman_ipconfig_type type,
446 struct gateway_data *active_gateway = NULL;
447 struct gateway_data *new_gateway = NULL;
450 index = __connman_service_get_index(service);
452 DBG("service %p index %d gateway %s vpn ip %s type %d",
453 service, index, gateway, peer, type);
455 active_gateway = find_active_gateway();
456 new_gateway = add_gateway(service, index, gateway, type);
457 if (new_gateway == NULL)
460 if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
461 new_gateway->ipv6_gateway != NULL &&
462 g_strcmp0(new_gateway->ipv6_gateway->gateway,
464 connman_inet_add_ipv6_host_route(index,
465 new_gateway->ipv6_gateway->gateway,
468 if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
469 new_gateway->ipv4_gateway != NULL &&
470 g_strcmp0(new_gateway->ipv4_gateway->gateway,
472 connman_inet_add_host_route(index,
473 new_gateway->ipv4_gateway->gateway,
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_ipconfig_indicate_state(service,
481 CONNMAN_SERVICE_STATE_READY,
482 CONNMAN_IPCONFIG_TYPE_IPV4);
485 if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
486 new_gateway->ipv6_gateway != NULL) {
487 __connman_service_nameserver_add_routes(service,
488 new_gateway->ipv6_gateway->gateway);
489 __connman_service_ipconfig_indicate_state(service,
490 CONNMAN_SERVICE_STATE_READY,
491 CONNMAN_IPCONFIG_TYPE_IPV6);
494 if (connman_service_get_type(service) == CONNMAN_SERVICE_TYPE_VPN) {
495 if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
496 new_gateway->ipv4_gateway != NULL) {
497 new_gateway->ipv4_gateway->vpn = TRUE;
499 new_gateway->ipv4_gateway->vpn_ip =
501 else if (gateway != NULL)
502 new_gateway->ipv4_gateway->vpn_ip =
505 new_gateway->ipv4_gateway->vpn_phy_index =
506 active_gateway->index;
508 } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
509 new_gateway->ipv6_gateway != NULL) {
510 new_gateway->ipv6_gateway->vpn = TRUE;
512 new_gateway->ipv6_gateway->vpn_ip =
514 else if (gateway != NULL)
515 new_gateway->ipv6_gateway->vpn_ip =
518 new_gateway->ipv6_gateway->vpn_phy_index =
519 active_gateway->index;
522 if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
523 new_gateway->ipv4_gateway != NULL)
524 new_gateway->ipv4_gateway->vpn = FALSE;
526 if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
527 new_gateway->ipv6_gateway != NULL)
528 new_gateway->ipv6_gateway->vpn = FALSE;
531 if (active_gateway == NULL) {
532 set_default_gateway(new_gateway, type);
536 if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
537 new_gateway->ipv4_gateway != NULL &&
538 new_gateway->ipv4_gateway->vpn == TRUE) {
539 connman_inet_add_host_route(active_gateway->index,
540 new_gateway->ipv4_gateway->gateway,
541 active_gateway->ipv4_gateway->gateway);
542 connman_inet_clear_gateway_address(active_gateway->index,
543 active_gateway->ipv4_gateway->gateway);
546 if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
547 new_gateway->ipv6_gateway != NULL &&
548 new_gateway->ipv6_gateway->vpn == TRUE) {
549 connman_inet_add_ipv6_host_route(active_gateway->index,
550 new_gateway->ipv6_gateway->gateway,
551 active_gateway->ipv6_gateway->gateway);
552 connman_inet_clear_ipv6_gateway_address(active_gateway->index,
553 active_gateway->ipv6_gateway->gateway);
559 void __connman_connection_gateway_remove(struct connman_service *service,
560 enum connman_ipconfig_type type)
562 struct gateway_data *data = NULL;
563 gboolean set_default4 = FALSE, set_default6 = FALSE;
564 int do_ipv4 = FALSE, do_ipv6 = FALSE;
567 DBG("service %p type %d", service, type);
569 if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
571 else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
574 do_ipv4 = do_ipv6 = TRUE;
576 __connman_service_nameserver_del_routes(service);
578 data = g_hash_table_lookup(gateway_hash, service);
582 if (do_ipv4 == TRUE && data->ipv4_gateway != NULL)
583 set_default4 = data->ipv4_gateway->vpn;
585 if (do_ipv6 == TRUE && data->ipv6_gateway != NULL)
586 set_default6 = data->ipv6_gateway->vpn;
588 DBG("ipv4 gateway %s ipv6 gateway %s vpn %d/%d",
589 data->ipv4_gateway ? data->ipv4_gateway->gateway : "<null>",
590 data->ipv6_gateway ? data->ipv6_gateway->gateway : "<null>",
591 set_default4, set_default6);
593 if (do_ipv4 == TRUE && data->ipv4_gateway != NULL &&
594 data->ipv4_gateway->vpn == TRUE && data->index >= 0)
595 connman_inet_del_host_route(data->index,
596 data->ipv4_gateway->gateway);
598 if (do_ipv6 == TRUE && data->ipv6_gateway != NULL &&
599 data->ipv6_gateway->vpn == TRUE && data->index >= 0)
600 connman_inet_del_ipv6_host_route(data->index,
601 data->ipv6_gateway->gateway);
603 __connman_service_nameserver_del_routes(service);
605 err = disable_gateway(data, type);
608 * We remove the service from the hash only if all the gateway
609 * settings are to be removed.
611 if (do_ipv4 == do_ipv6 ||
612 (data->ipv4_gateway != NULL && data->ipv6_gateway == NULL
613 && do_ipv4 == TRUE) ||
614 (data->ipv6_gateway != NULL && data->ipv4_gateway == NULL
617 g_hash_table_remove(gateway_hash, service);
619 DBG("Not yet removing gw ipv4 %p/%d ipv6 %p/%d",
620 data->ipv4_gateway, do_ipv4,
621 data->ipv6_gateway, do_ipv6);
623 /* with vpn this will be called after the network was deleted,
624 * we need to call set_default here because we will not recieve any
625 * gateway delete notification.
626 * We hit the same issue if remove_gateway() fails.
628 if (set_default4 || set_default6 || err < 0) {
629 data = find_default_gateway();
631 set_default_gateway(data, type);
635 gboolean __connman_connection_update_gateway(void)
637 struct gateway_data *active_gateway, *default_gateway;
638 gboolean updated = FALSE;
640 if (gateway_hash == NULL)
645 active_gateway = find_active_gateway();
646 default_gateway = find_default_gateway();
648 if (active_gateway && active_gateway != default_gateway)
654 int __connman_connection_init(void)
660 gateway_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
661 NULL, remove_gateway);
663 err = connman_rtnl_register(&connection_rtnl);
665 connman_error("Failed to setup RTNL gateway driver");
670 void __connman_connection_cleanup(void)
677 connman_rtnl_unregister(&connection_rtnl);
679 g_hash_table_iter_init(&iter, gateway_hash);
681 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
682 struct gateway_data *data = value;
684 disable_gateway(data, CONNMAN_IPCONFIG_TYPE_ALL);
687 g_hash_table_destroy(gateway_hash);