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);
457 * If gateway is NULL, it's a point to point link and the default
458 * gateway for ipv4 is 0.0.0.0 and for ipv6 is ::, meaning the
461 if (gateway == NULL && type == CONNMAN_IPCONFIG_TYPE_IPV4)
464 if (gateway == NULL && type == CONNMAN_IPCONFIG_TYPE_IPV6)
467 active_gateway = find_active_gateway();
468 new_gateway = add_gateway(service, index, gateway, type);
469 if (new_gateway == NULL)
472 if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
473 new_gateway->ipv6_gateway != NULL &&
474 g_strcmp0(new_gateway->ipv6_gateway->gateway,
476 connman_inet_add_ipv6_host_route(index,
477 new_gateway->ipv6_gateway->gateway,
480 if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
481 new_gateway->ipv4_gateway != NULL &&
482 g_strcmp0(new_gateway->ipv4_gateway->gateway,
484 connman_inet_add_host_route(index,
485 new_gateway->ipv4_gateway->gateway,
488 if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
489 new_gateway->ipv4_gateway != NULL) {
490 __connman_service_nameserver_add_routes(service,
491 new_gateway->ipv4_gateway->gateway);
492 __connman_service_ipconfig_indicate_state(service,
493 CONNMAN_SERVICE_STATE_READY,
494 CONNMAN_IPCONFIG_TYPE_IPV4);
497 if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
498 new_gateway->ipv6_gateway != NULL) {
499 __connman_service_nameserver_add_routes(service,
500 new_gateway->ipv6_gateway->gateway);
501 __connman_service_ipconfig_indicate_state(service,
502 CONNMAN_SERVICE_STATE_READY,
503 CONNMAN_IPCONFIG_TYPE_IPV6);
506 if (connman_service_get_type(service) == CONNMAN_SERVICE_TYPE_VPN) {
507 if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
508 new_gateway->ipv4_gateway != NULL) {
509 new_gateway->ipv4_gateway->vpn = TRUE;
511 new_gateway->ipv4_gateway->vpn_ip =
513 else if (gateway != NULL)
514 new_gateway->ipv4_gateway->vpn_ip =
517 new_gateway->ipv4_gateway->vpn_phy_index =
518 active_gateway->index;
520 } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
521 new_gateway->ipv6_gateway != NULL) {
522 new_gateway->ipv6_gateway->vpn = TRUE;
524 new_gateway->ipv6_gateway->vpn_ip =
526 else if (gateway != NULL)
527 new_gateway->ipv6_gateway->vpn_ip =
530 new_gateway->ipv6_gateway->vpn_phy_index =
531 active_gateway->index;
534 if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
535 new_gateway->ipv4_gateway != NULL)
536 new_gateway->ipv4_gateway->vpn = FALSE;
538 if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
539 new_gateway->ipv6_gateway != NULL)
540 new_gateway->ipv6_gateway->vpn = FALSE;
543 if (active_gateway == NULL) {
544 set_default_gateway(new_gateway, type);
548 if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
549 new_gateway->ipv4_gateway != NULL &&
550 new_gateway->ipv4_gateway->vpn == TRUE) {
551 connman_inet_add_host_route(active_gateway->index,
552 new_gateway->ipv4_gateway->gateway,
553 active_gateway->ipv4_gateway->gateway);
554 connman_inet_clear_gateway_address(active_gateway->index,
555 active_gateway->ipv4_gateway->gateway);
558 if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
559 new_gateway->ipv6_gateway != NULL &&
560 new_gateway->ipv6_gateway->vpn == TRUE) {
561 connman_inet_add_ipv6_host_route(active_gateway->index,
562 new_gateway->ipv6_gateway->gateway,
563 active_gateway->ipv6_gateway->gateway);
564 connman_inet_clear_ipv6_gateway_address(active_gateway->index,
565 active_gateway->ipv6_gateway->gateway);
571 void __connman_connection_gateway_remove(struct connman_service *service,
572 enum connman_ipconfig_type type)
574 struct gateway_data *data = NULL;
575 gboolean set_default4 = FALSE, set_default6 = FALSE;
576 int do_ipv4 = FALSE, do_ipv6 = FALSE;
579 DBG("service %p type %d", service, type);
581 if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
583 else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
586 do_ipv4 = do_ipv6 = TRUE;
588 __connman_service_nameserver_del_routes(service);
590 data = g_hash_table_lookup(gateway_hash, service);
594 if (do_ipv4 == TRUE && data->ipv4_gateway != NULL)
595 set_default4 = data->ipv4_gateway->vpn;
597 if (do_ipv6 == TRUE && data->ipv6_gateway != NULL)
598 set_default6 = data->ipv6_gateway->vpn;
600 DBG("ipv4 gateway %s ipv6 gateway %s vpn %d/%d",
601 data->ipv4_gateway ? data->ipv4_gateway->gateway : "<null>",
602 data->ipv6_gateway ? data->ipv6_gateway->gateway : "<null>",
603 set_default4, set_default6);
605 if (do_ipv4 == TRUE && data->ipv4_gateway != NULL &&
606 data->ipv4_gateway->vpn == TRUE && data->index >= 0)
607 connman_inet_del_host_route(data->index,
608 data->ipv4_gateway->gateway);
610 if (do_ipv6 == TRUE && data->ipv6_gateway != NULL &&
611 data->ipv6_gateway->vpn == TRUE && data->index >= 0)
612 connman_inet_del_ipv6_host_route(data->index,
613 data->ipv6_gateway->gateway);
615 __connman_service_nameserver_del_routes(service);
617 err = disable_gateway(data, type);
620 * We remove the service from the hash only if all the gateway
621 * settings are to be removed.
623 if (do_ipv4 == do_ipv6 ||
624 (data->ipv4_gateway != NULL && data->ipv6_gateway == NULL
625 && do_ipv4 == TRUE) ||
626 (data->ipv6_gateway != NULL && data->ipv4_gateway == NULL
629 g_hash_table_remove(gateway_hash, service);
631 DBG("Not yet removing gw ipv4 %p/%d ipv6 %p/%d",
632 data->ipv4_gateway, do_ipv4,
633 data->ipv6_gateway, do_ipv6);
635 /* with vpn this will be called after the network was deleted,
636 * we need to call set_default here because we will not recieve any
637 * gateway delete notification.
638 * We hit the same issue if remove_gateway() fails.
640 if (set_default4 || set_default6 || err < 0) {
641 data = find_default_gateway();
643 set_default_gateway(data, type);
647 gboolean __connman_connection_update_gateway(void)
649 struct gateway_data *active_gateway, *default_gateway;
650 gboolean updated = FALSE;
652 if (gateway_hash == NULL)
657 active_gateway = find_active_gateway();
658 default_gateway = find_default_gateway();
660 if (active_gateway && active_gateway != default_gateway)
666 int __connman_connection_init(void)
672 gateway_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
673 NULL, remove_gateway);
675 err = connman_rtnl_register(&connection_rtnl);
677 connman_error("Failed to setup RTNL gateway driver");
682 void __connman_connection_cleanup(void)
689 connman_rtnl_unregister(&connection_rtnl);
691 g_hash_table_iter_init(&iter, gateway_hash);
693 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
694 struct gateway_data *data = value;
696 disable_gateway(data, CONNMAN_IPCONFIG_TYPE_ALL);
699 g_hash_table_destroy(gateway_hash);