5 * Copyright (C) 2007-2009 Intel Corporation. All rights reserved.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
29 #include <sys/ioctl.h>
30 #include <arpa/inet.h>
32 #include <net/route.h>
41 struct connman_element *element;
46 static GSList *gateway_list = NULL;
48 static struct gateway_data *find_gateway(int index, const char *gateway)
55 for (list = gateway_list; list; list = list->next) {
56 struct gateway_data *data = list->data;
58 if (data->gateway == NULL)
61 if (data->index == index &&
62 g_str_equal(data->gateway, gateway) == TRUE)
69 static int set_route(struct connman_element *element, const char *gateway)
73 struct sockaddr_in addr;
76 DBG("element %p", element);
78 sk = socket(PF_INET, SOCK_DGRAM, 0);
82 memset(&ifr, 0, sizeof(ifr));
83 ifr.ifr_ifindex = element->index;
85 if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
90 DBG("ifname %s", ifr.ifr_name);
92 memset(&rt, 0, sizeof(rt));
93 rt.rt_flags = RTF_UP | RTF_HOST;
95 memset(&addr, 0, sizeof(addr));
96 addr.sin_family = AF_INET;
97 addr.sin_addr.s_addr = inet_addr(gateway);
98 memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst));
100 memset(&addr, 0, sizeof(addr));
101 addr.sin_family = AF_INET;
102 addr.sin_addr.s_addr = INADDR_ANY;
103 memcpy(&rt.rt_gateway, &addr, sizeof(rt.rt_gateway));
105 memset(&addr, 0, sizeof(addr));
106 addr.sin_family = AF_INET;
107 addr.sin_addr.s_addr = INADDR_ANY;
108 memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask));
110 rt.rt_dev = ifr.ifr_name;
112 err = ioctl(sk, SIOCADDRT, &rt);
114 connman_error("Setting host gateway route failed (%s)",
117 memset(&rt, 0, sizeof(rt));
118 rt.rt_flags = RTF_UP | RTF_GATEWAY;
120 memset(&addr, 0, sizeof(addr));
121 addr.sin_family = AF_INET;
122 addr.sin_addr.s_addr = INADDR_ANY;
123 memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst));
125 memset(&addr, 0, sizeof(addr));
126 addr.sin_family = AF_INET;
127 addr.sin_addr.s_addr = inet_addr(gateway);
128 memcpy(&rt.rt_gateway, &addr, sizeof(rt.rt_gateway));
130 memset(&addr, 0, sizeof(addr));
131 addr.sin_family = AF_INET;
132 addr.sin_addr.s_addr = INADDR_ANY;
133 memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask));
135 err = ioctl(sk, SIOCADDRT, &rt);
137 connman_error("Setting default route failed (%s)",
145 static int del_route(struct connman_element *element, const char *gateway)
149 struct sockaddr_in addr;
152 DBG("element %p", element);
154 sk = socket(PF_INET, SOCK_DGRAM, 0);
158 memset(&ifr, 0, sizeof(ifr));
159 ifr.ifr_ifindex = element->index;
161 if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
166 DBG("ifname %s", ifr.ifr_name);
168 memset(&rt, 0, sizeof(rt));
169 rt.rt_flags = RTF_UP | RTF_GATEWAY;
171 memset(&addr, 0, sizeof(addr));
172 addr.sin_family = AF_INET;
173 addr.sin_addr.s_addr = INADDR_ANY;
174 memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst));
176 memset(&addr, 0, sizeof(addr));
177 addr.sin_family = AF_INET;
178 addr.sin_addr.s_addr = inet_addr(gateway);
179 memcpy(&rt.rt_gateway, &addr, sizeof(rt.rt_gateway));
181 memset(&addr, 0, sizeof(addr));
182 addr.sin_family = AF_INET;
183 addr.sin_addr.s_addr = INADDR_ANY;
184 memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask));
186 err = ioctl(sk, SIOCDELRT, &rt);
188 connman_error("Removing default route failed (%s)",
196 static DBusConnection *connection;
198 static void emit_default_signal(struct connman_element *element)
201 DBusMessageIter entry, value;
202 const char *key = "Default";
204 signal = dbus_message_new_signal(element->path,
205 CONNMAN_CONNECTION_INTERFACE, "PropertyChanged");
209 dbus_message_iter_init_append(signal, &entry);
211 dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
213 dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
214 DBUS_TYPE_BOOLEAN_AS_STRING, &value);
215 dbus_message_iter_append_basic(&value, DBUS_TYPE_BOOLEAN,
217 dbus_message_iter_close_container(&entry, &value);
219 g_dbus_send_message(connection, signal);
222 static void find_element(struct connman_element *element, gpointer user_data)
224 struct gateway_data *data = user_data;
226 DBG("element %p name %s", element, element->name);
228 if (data->element != NULL)
231 if (element->index != data->index)
234 data->element = element;
237 static struct gateway_data *add_gateway(int index, const char *gateway)
239 struct gateway_data *data;
240 struct connman_service *service;
242 data = g_try_new0(struct gateway_data, 1);
247 data->gateway = g_strdup(gateway);
248 data->active = FALSE;
249 data->element = NULL;
251 __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_CONNECTION,
254 service = __connman_element_get_service(data->element);
255 data->order = __connman_service_get_order(service);
257 gateway_list = g_slist_append(gateway_list, data);
262 static void connection_newgateway(int index, const char *gateway)
264 struct gateway_data *data;
266 DBG("index %d gateway %s", index, gateway);
268 data = find_gateway(index, gateway);
275 static void set_default_gateway(struct gateway_data *data)
277 struct connman_element *element = data->element;
278 struct connman_service *service = NULL;
280 DBG("gateway %s", data->gateway);
282 if (set_route(element, data->gateway) < 0)
285 service = __connman_element_get_service(element);
286 __connman_service_indicate_default(service);
289 static struct gateway_data *find_default_gateway(void)
291 struct gateway_data *found = NULL;
292 unsigned int order = 0;
295 for (list = gateway_list; list; list = list->next) {
296 struct gateway_data *data = list->data;
298 if (found == NULL || data->order > order) {
307 static void remove_gateway(struct gateway_data *data)
309 DBG("gateway %s", data->gateway);
311 gateway_list = g_slist_remove(gateway_list, data);
313 if (data->active == TRUE)
314 del_route(data->element, data->gateway);
316 g_free(data->gateway);
320 static void connection_delgateway(int index, const char *gateway)
322 struct gateway_data *data;
324 DBG("index %d gateway %s", index, gateway);
326 data = find_gateway(index, gateway);
328 data->active = FALSE;
330 data = find_default_gateway();
332 set_default_gateway(data);
335 static struct connman_rtnl connection_rtnl = {
336 .name = "connection",
337 .newgateway = connection_newgateway,
338 .delgateway = connection_delgateway,
341 static DBusMessage *get_properties(DBusConnection *conn,
342 DBusMessage *msg, void *data)
344 struct connman_element *element = data;
346 DBusMessageIter array, dict;
347 connman_uint8_t strength;
348 const char *device, *network;
351 DBG("conn %p", conn);
353 if (__connman_security_check_privilege(msg,
354 CONNMAN_SECURITY_PRIVILEGE_PUBLIC) < 0)
355 return __connman_error_permission_denied(msg);
357 reply = dbus_message_new_method_return(msg);
361 dbus_message_iter_init_append(reply, &array);
363 dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
364 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
365 DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
366 DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
368 type = connman_element_get_string(element, "Type");
370 connman_dbus_dict_append_variant(&dict, "Type",
371 DBUS_TYPE_STRING, &type);
373 strength = connman_element_get_uint8(element, "Strength");
375 connman_dbus_dict_append_variant(&dict, "Strength",
376 DBUS_TYPE_BYTE, &strength);
378 if (element->devname != NULL)
379 connman_dbus_dict_append_variant(&dict, "Interface",
380 DBUS_TYPE_STRING, &element->devname);
382 connman_dbus_dict_append_variant(&dict, "Default",
383 DBUS_TYPE_BOOLEAN, &element->enabled);
385 device = __connman_element_get_device_path(element);
387 connman_dbus_dict_append_variant(&dict, "Device",
388 DBUS_TYPE_OBJECT_PATH, &device);
390 network = __connman_element_get_network_path(element);
392 connman_dbus_dict_append_variant(&dict, "Network",
393 DBUS_TYPE_OBJECT_PATH, &network);
395 __connman_element_append_ipv4(element, &dict);
397 dbus_message_iter_close_container(&array, &dict);
402 static DBusMessage *set_property(DBusConnection *conn,
403 DBusMessage *msg, void *data)
405 DBusMessageIter iter, value;
409 DBG("conn %p", conn);
411 if (dbus_message_iter_init(msg, &iter) == FALSE)
412 return __connman_error_invalid_arguments(msg);
414 dbus_message_iter_get_basic(&iter, &name);
415 dbus_message_iter_next(&iter);
416 dbus_message_iter_recurse(&iter, &value);
418 if (__connman_security_check_privilege(msg,
419 CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
420 return __connman_error_permission_denied(msg);
422 type = dbus_message_iter_get_arg_type(&value);
424 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
427 static GDBusMethodTable connection_methods[] = {
428 { "GetProperties", "", "a{sv}", get_properties },
429 { "SetProperty", "sv", "", set_property },
433 static GDBusSignalTable connection_signals[] = {
434 { "PropertyChanged", "sv" },
438 static void append_connections(DBusMessageIter *entry)
440 DBusMessageIter value, iter;
441 const char *key = "Connections";
443 dbus_message_iter_append_basic(entry, DBUS_TYPE_STRING, &key);
445 dbus_message_iter_open_container(entry, DBUS_TYPE_VARIANT,
446 DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING,
449 dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
450 DBUS_TYPE_OBJECT_PATH_AS_STRING, &iter);
451 __connman_element_list(NULL, CONNMAN_ELEMENT_TYPE_CONNECTION, &iter);
452 dbus_message_iter_close_container(&value, &iter);
454 dbus_message_iter_close_container(entry, &value);
457 static void emit_connections_signal(void)
460 DBusMessageIter entry;
464 signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
465 CONNMAN_MANAGER_INTERFACE, "PropertyChanged");
469 dbus_message_iter_init_append(signal, &entry);
471 append_connections(&entry);
473 g_dbus_send_message(connection, signal);
476 static int register_interface(struct connman_element *element)
478 DBG("element %p name %s path %s",
479 element, element->name, element->path);
481 if (g_dbus_register_interface(connection, element->path,
482 CONNMAN_CONNECTION_INTERFACE,
483 connection_methods, connection_signals,
484 NULL, element, NULL) == FALSE) {
485 connman_error("Failed to register %s connection", element->path);
489 emit_connections_signal();
494 static void unregister_interface(struct connman_element *element)
496 DBG("element %p name %s", element, element->name);
498 emit_connections_signal();
500 g_dbus_unregister_interface(connection, element->path,
501 CONNMAN_CONNECTION_INTERFACE);
504 static struct gateway_data *find_active_gateway(void)
510 for (list = gateway_list; list; list = list->next) {
511 struct gateway_data *data = list->data;
512 if (data->active == TRUE)
519 static int connection_probe(struct connman_element *element)
521 struct connman_service *service = NULL;
522 const char *gateway = NULL;
523 struct gateway_data *active_gateway = NULL;
524 struct gateway_data *new_gateway = NULL;
526 DBG("element %p name %s", element, element->name);
528 if (element->parent == NULL)
531 if (element->parent->type != CONNMAN_ELEMENT_TYPE_IPV4)
534 connman_element_get_value(element,
535 CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
537 DBG("gateway %s", gateway);
539 if (register_interface(element) < 0)
542 service = __connman_element_get_service(element);
543 __connman_service_indicate_state(service,
544 CONNMAN_SERVICE_STATE_READY);
546 connman_element_set_enabled(element, TRUE);
547 emit_default_signal(element);
552 active_gateway = find_active_gateway();
553 new_gateway = add_gateway(element->index, gateway);
555 if (active_gateway == NULL) {
556 set_default_gateway(new_gateway);
560 if (new_gateway->order >= active_gateway->order) {
561 del_route(active_gateway->element, active_gateway->gateway);
568 static void connection_remove(struct connman_element *element)
570 struct connman_service *service;
571 const char *gateway = NULL;
572 struct gateway_data *data = NULL;
574 DBG("element %p name %s", element, element->name);
576 service = __connman_element_get_service(element);
577 __connman_service_indicate_state(service,
578 CONNMAN_SERVICE_STATE_DISCONNECT);
580 connman_element_set_enabled(element, FALSE);
581 emit_default_signal(element);
583 unregister_interface(element);
585 connman_element_get_value(element,
586 CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
588 DBG("gateway %s", gateway);
593 data = find_gateway(element->index, gateway);
597 remove_gateway(data);
600 static struct connman_driver connection_driver = {
601 .name = "connection",
602 .type = CONNMAN_ELEMENT_TYPE_CONNECTION,
603 .priority = CONNMAN_DRIVER_PRIORITY_LOW,
604 .probe = connection_probe,
605 .remove = connection_remove,
608 int __connman_connection_init(void)
612 connection = connman_dbus_get_connection();
614 if (connman_rtnl_register(&connection_rtnl) < 0)
615 connman_error("Failed to setup RTNL gateway driver");
617 return connman_driver_register(&connection_driver);
620 void __connman_connection_cleanup(void)
626 connman_driver_unregister(&connection_driver);
628 connman_rtnl_unregister(&connection_rtnl);
630 for (list = gateway_list; list; list = list->next) {
631 struct gateway_data *data = list->data;
633 DBG("index %d gateway %s", data->index, data->gateway);
635 g_free(data->gateway);
640 g_slist_free(gateway_list);
643 dbus_connection_unref(connection);
646 static void update_order(void)
650 for (list = gateway_list; list; list = list->next) {
651 struct gateway_data *data = list->data;
652 struct connman_service *service;
654 service = __connman_element_get_service(data->element);
655 data->order = __connman_service_get_order(service);
659 gboolean __connman_connection_update_gateway(void)
661 struct gateway_data *active_gateway, *default_gateway;
662 gboolean updated = FALSE;
666 active_gateway = find_active_gateway();
667 default_gateway = find_default_gateway();
669 if (active_gateway && active_gateway != default_gateway) {
670 del_route(active_gateway->element, active_gateway->gateway);