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 void find_element(struct connman_element *element, gpointer user_data)
198 struct gateway_data *data = user_data;
200 DBG("element %p name %s", element, element->name);
202 if (data->element != NULL)
205 if (element->index != data->index)
208 data->element = element;
211 static struct gateway_data *add_gateway(int index, const char *gateway)
213 struct gateway_data *data;
214 struct connman_service *service;
216 data = g_try_new0(struct gateway_data, 1);
221 data->gateway = g_strdup(gateway);
222 data->active = FALSE;
223 data->element = NULL;
225 __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_CONNECTION,
228 service = __connman_element_get_service(data->element);
229 data->order = __connman_service_get_order(service);
231 gateway_list = g_slist_append(gateway_list, data);
236 static void connection_newgateway(int index, const char *gateway)
238 struct gateway_data *data;
240 DBG("index %d gateway %s", index, gateway);
242 data = find_gateway(index, gateway);
249 static void set_default_gateway(struct gateway_data *data)
251 struct connman_element *element = data->element;
252 struct connman_service *service = NULL;
254 DBG("gateway %s", data->gateway);
256 if (set_route(element, data->gateway) < 0)
259 service = __connman_element_get_service(element);
260 __connman_service_indicate_default(service);
263 static struct gateway_data *find_default_gateway(void)
265 struct gateway_data *found = NULL;
266 unsigned int order = 0;
269 for (list = gateway_list; list; list = list->next) {
270 struct gateway_data *data = list->data;
272 if (found == NULL || data->order > order) {
281 static void remove_gateway(struct gateway_data *data)
283 DBG("gateway %s", data->gateway);
285 gateway_list = g_slist_remove(gateway_list, data);
287 if (data->active == TRUE)
288 del_route(data->element, data->gateway);
290 g_free(data->gateway);
294 static void connection_delgateway(int index, const char *gateway)
296 struct gateway_data *data;
298 DBG("index %d gateway %s", index, gateway);
300 data = find_gateway(index, gateway);
302 data->active = FALSE;
304 data = find_default_gateway();
306 set_default_gateway(data);
309 static struct connman_rtnl connection_rtnl = {
310 .name = "connection",
311 .newgateway = connection_newgateway,
312 .delgateway = connection_delgateway,
315 static struct gateway_data *find_active_gateway(void)
321 for (list = gateway_list; list; list = list->next) {
322 struct gateway_data *data = list->data;
323 if (data->active == TRUE)
330 static int connection_probe(struct connman_element *element)
332 struct connman_service *service = NULL;
333 const char *gateway = NULL;
334 struct gateway_data *active_gateway = NULL;
335 struct gateway_data *new_gateway = NULL;
337 DBG("element %p name %s", element, element->name);
339 if (element->parent == NULL)
342 if (element->parent->type != CONNMAN_ELEMENT_TYPE_IPV4)
345 connman_element_get_value(element,
346 CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
348 DBG("gateway %s", gateway);
350 service = __connman_element_get_service(element);
351 __connman_service_indicate_state(service,
352 CONNMAN_SERVICE_STATE_READY);
354 connman_element_set_enabled(element, TRUE);
359 active_gateway = find_active_gateway();
360 new_gateway = add_gateway(element->index, gateway);
362 if (active_gateway == NULL) {
363 set_default_gateway(new_gateway);
367 if (new_gateway->order >= active_gateway->order) {
368 del_route(active_gateway->element, active_gateway->gateway);
375 static void connection_remove(struct connman_element *element)
377 struct connman_service *service;
378 const char *gateway = NULL;
379 struct gateway_data *data = NULL;
381 DBG("element %p name %s", element, element->name);
383 service = __connman_element_get_service(element);
384 __connman_service_indicate_state(service,
385 CONNMAN_SERVICE_STATE_DISCONNECT);
387 connman_element_set_enabled(element, FALSE);
389 connman_element_get_value(element,
390 CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
392 DBG("gateway %s", gateway);
397 data = find_gateway(element->index, gateway);
401 remove_gateway(data);
404 static struct connman_driver connection_driver = {
405 .name = "connection",
406 .type = CONNMAN_ELEMENT_TYPE_CONNECTION,
407 .priority = CONNMAN_DRIVER_PRIORITY_LOW,
408 .probe = connection_probe,
409 .remove = connection_remove,
412 int __connman_connection_init(void)
416 if (connman_rtnl_register(&connection_rtnl) < 0)
417 connman_error("Failed to setup RTNL gateway driver");
419 return connman_driver_register(&connection_driver);
422 void __connman_connection_cleanup(void)
428 connman_driver_unregister(&connection_driver);
430 connman_rtnl_unregister(&connection_rtnl);
432 for (list = gateway_list; list; list = list->next) {
433 struct gateway_data *data = list->data;
435 DBG("index %d gateway %s", data->index, data->gateway);
437 g_free(data->gateway);
442 g_slist_free(gateway_list);
446 static void update_order(void)
450 for (list = gateway_list; list; list = list->next) {
451 struct gateway_data *data = list->data;
452 struct connman_service *service;
454 service = __connman_element_get_service(data->element);
455 data->order = __connman_service_get_order(service);
459 gboolean __connman_connection_update_gateway(void)
461 struct gateway_data *active_gateway, *default_gateway;
462 gboolean updated = FALSE;
466 active_gateway = find_active_gateway();
467 default_gateway = find_default_gateway();
469 if (active_gateway && active_gateway != default_gateway) {
470 del_route(active_gateway->element, active_gateway->gateway);