Add support for switching default route based on service order
[platform/upstream/connman.git] / src / connection.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2009  Intel Corporation. All rights reserved.
6  *
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.
10  *
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.
15  *
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
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <errno.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <sys/ioctl.h>
30 #include <arpa/inet.h>
31 #include <net/if.h>
32 #include <net/route.h>
33
34 #include <gdbus.h>
35
36 #include "connman.h"
37
38 struct gateway_data {
39         int index;
40         char *gateway;
41         struct connman_element *element;
42         unsigned int order;
43         gboolean active;
44 };
45
46 static GSList *gateway_list = NULL;
47
48 static struct gateway_data *find_gateway(int index, const char *gateway)
49 {
50         GSList *list;
51
52         if (gateway == NULL)
53                 return NULL;
54
55         for (list = gateway_list; list; list = list->next) {
56                 struct gateway_data *data = list->data;
57
58                 if (data->gateway == NULL)
59                         continue;
60
61                 if (data->index == index &&
62                                 g_str_equal(data->gateway, gateway) == TRUE)
63                         return data;
64         }
65
66         return NULL;
67 }
68
69 static int set_route(struct connman_element *element, const char *gateway)
70 {
71         struct ifreq ifr;
72         struct rtentry rt;
73         struct sockaddr_in *addr;
74         int sk, err;
75
76         DBG("element %p", element);
77
78         sk = socket(PF_INET, SOCK_DGRAM, 0);
79         if (sk < 0)
80                 return -1;
81
82         memset(&ifr, 0, sizeof(ifr));
83         ifr.ifr_ifindex = element->index;
84
85         if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
86                 close(sk);
87                 return -1;
88         }
89
90         DBG("ifname %s", ifr.ifr_name);
91
92         memset(&rt, 0, sizeof(rt));
93         rt.rt_flags = RTF_UP | RTF_HOST;
94
95         addr = (struct sockaddr_in *) &rt.rt_dst;
96         addr->sin_family = AF_INET;
97         addr->sin_addr.s_addr = inet_addr(gateway);
98
99         addr = (struct sockaddr_in *) &rt.rt_gateway;
100         addr->sin_family = AF_INET;
101         addr->sin_addr.s_addr = INADDR_ANY;
102
103         addr = (struct sockaddr_in *) &rt.rt_genmask;
104         addr->sin_family = AF_INET;
105         addr->sin_addr.s_addr = INADDR_ANY;
106
107         rt.rt_dev = ifr.ifr_name;
108
109         err = ioctl(sk, SIOCADDRT, &rt);
110         if (err < 0)
111                 connman_error("Setting host gateway route failed (%s)",
112                                                         strerror(errno));
113
114         memset(&rt, 0, sizeof(rt));
115         rt.rt_flags = RTF_UP | RTF_GATEWAY;
116
117         addr = (struct sockaddr_in *) &rt.rt_dst;
118         addr->sin_family = AF_INET;
119         addr->sin_addr.s_addr = INADDR_ANY;
120
121         addr = (struct sockaddr_in *) &rt.rt_gateway;
122         addr->sin_family = AF_INET;
123         addr->sin_addr.s_addr = inet_addr(gateway);
124
125         addr = (struct sockaddr_in *) &rt.rt_genmask;
126         addr->sin_family = AF_INET;
127         addr->sin_addr.s_addr = INADDR_ANY;
128
129         err = ioctl(sk, SIOCADDRT, &rt);
130         if (err < 0)
131                 connman_error("Setting default route failed (%s)",
132                                                         strerror(errno));
133
134         close(sk);
135
136         return err;
137 }
138
139 static int del_route(struct connman_element *element, const char *gateway)
140 {
141         struct ifreq ifr;
142         struct rtentry rt;
143         struct sockaddr_in *addr;
144         int sk, err;
145
146         DBG("element %p", element);
147
148         sk = socket(PF_INET, SOCK_DGRAM, 0);
149         if (sk < 0)
150                 return -1;
151
152         memset(&ifr, 0, sizeof(ifr));
153         ifr.ifr_ifindex = element->index;
154
155         if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
156                 close(sk);
157                 return -1;
158         }
159
160         DBG("ifname %s", ifr.ifr_name);
161
162         memset(&rt, 0, sizeof(rt));
163         rt.rt_flags = RTF_UP | RTF_GATEWAY;
164
165         addr = (struct sockaddr_in *) &rt.rt_dst;
166         addr->sin_family = AF_INET;
167         addr->sin_addr.s_addr = INADDR_ANY;
168
169         addr = (struct sockaddr_in *) &rt.rt_gateway;
170         addr->sin_family = AF_INET;
171         addr->sin_addr.s_addr = inet_addr(gateway);
172
173         addr = (struct sockaddr_in *) &rt.rt_genmask;
174         addr->sin_family = AF_INET;
175         addr->sin_addr.s_addr = INADDR_ANY;
176
177         err = ioctl(sk, SIOCDELRT, &rt);
178         if (err < 0)
179                 connman_error("Removing default route failed (%s)",
180                                                         strerror(errno));
181
182         close(sk);
183
184         return err;
185 }
186
187 static DBusConnection *connection;
188
189 static void emit_default_signal(struct connman_element *element)
190 {
191         DBusMessage *signal;
192         DBusMessageIter entry, value;
193         const char *key = "Default";
194
195         signal = dbus_message_new_signal(element->path,
196                         CONNMAN_CONNECTION_INTERFACE, "PropertyChanged");
197         if (signal == NULL)
198                 return;
199
200         dbus_message_iter_init_append(signal, &entry);
201
202         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
203
204         dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
205                                         DBUS_TYPE_BOOLEAN_AS_STRING, &value);
206         dbus_message_iter_append_basic(&value, DBUS_TYPE_BOOLEAN,
207                                                         &element->enabled);
208         dbus_message_iter_close_container(&entry, &value);
209
210         g_dbus_send_message(connection, signal);
211 }
212
213 static void find_element(struct connman_element *element, gpointer user_data)
214 {
215         struct gateway_data *data = user_data;
216
217         DBG("element %p name %s", element, element->name);
218
219         if (data->element != NULL)
220                 return;
221
222         if (element->index != data->index)
223                 return;
224
225         data->element = element;
226 }
227
228 static struct gateway_data *add_gateway(int index, const char *gateway)
229 {
230         struct gateway_data *data;
231         struct connman_service *service;
232
233         data = g_try_new0(struct gateway_data, 1);
234         if (data == NULL)
235                 return NULL;
236
237         data->index = index;
238         data->gateway = g_strdup(gateway);
239         data->active = FALSE;
240         data->element = NULL;
241
242         __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_CONNECTION,
243                                                         find_element, data);
244
245         service = __connman_element_get_service(data->element);
246         if (service != NULL)
247                 data->order = __connman_service_get_order(service);
248
249         gateway_list = g_slist_append(gateway_list, data);
250
251         return data;
252 }
253
254 static void connection_newgateway(int index, const char *gateway)
255 {
256         struct gateway_data *data;
257
258         DBG("index %d gateway %s", index, gateway);
259
260         data = find_gateway(index, gateway);
261         if (data == NULL)
262                 return;
263
264         data->active = TRUE;
265 }
266
267 static void set_default_gateway(struct gateway_data *data)
268 {
269         struct connman_element *element = data->element;
270         struct connman_service *service = NULL;
271
272         DBG("gateway %s", data->gateway);
273
274         if (set_route(element, data->gateway) < 0)
275                 return;
276
277         service = __connman_element_get_service(element);
278         __connman_service_indicate_default(service);
279 }
280
281 static struct gateway_data *find_default_gateway(void)
282 {
283         struct gateway_data *found = NULL;
284         GSList *list;
285
286         for (list = gateway_list; list; list = list->next) {
287                 struct gateway_data *data = list->data;
288                 /* just return the last one for now */
289                 found = data;
290         }
291
292         return found;
293 }
294
295 static void remove_gateway(struct gateway_data *data)
296 {
297         DBG("gateway %s", data->gateway);
298
299         gateway_list = g_slist_remove(gateway_list, data);
300
301         if (data->active == TRUE)
302                 del_route(data->element, data->gateway);
303
304         g_free(data->gateway);
305         g_free(data);
306 }
307
308 static void connection_delgateway(int index, const char *gateway)
309 {
310         struct gateway_data *data;
311
312         DBG("index %d gateway %s", index, gateway);
313
314         data = find_gateway(index, gateway);
315         if (data != NULL)
316                 data->active = FALSE;
317
318         data = find_default_gateway();
319         if (data != NULL)
320                 set_default_gateway(data);
321 }
322
323 static struct connman_rtnl connection_rtnl = {
324         .name           = "connection",
325         .newgateway     = connection_newgateway,
326         .delgateway     = connection_delgateway,
327 };
328
329 static DBusMessage *get_properties(DBusConnection *conn,
330                                         DBusMessage *msg, void *data)
331 {
332         struct connman_element *element = data;
333         DBusMessage *reply;
334         DBusMessageIter array, dict;
335         connman_uint8_t strength;
336         const char *device, *network;
337         const char *type;
338
339         DBG("conn %p", conn);
340
341         if (__connman_security_check_privilege(msg,
342                                         CONNMAN_SECURITY_PRIVILEGE_PUBLIC) < 0)
343                 return __connman_error_permission_denied(msg);
344
345         reply = dbus_message_new_method_return(msg);
346         if (reply == NULL)
347                 return NULL;
348
349         dbus_message_iter_init_append(reply, &array);
350
351         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
352                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
353                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
354                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
355
356         type = connman_element_get_string(element, "Type");
357         if (type != NULL)
358                 connman_dbus_dict_append_variant(&dict, "Type",
359                                                 DBUS_TYPE_STRING, &type);
360
361         strength = connman_element_get_uint8(element, "Strength");
362         if (strength > 0)
363                 connman_dbus_dict_append_variant(&dict, "Strength",
364                                                 DBUS_TYPE_BYTE, &strength);
365
366         if (element->devname != NULL)
367                 connman_dbus_dict_append_variant(&dict, "Interface",
368                                         DBUS_TYPE_STRING, &element->devname);
369
370         connman_dbus_dict_append_variant(&dict, "Default",
371                                         DBUS_TYPE_BOOLEAN, &element->enabled);
372
373         device = __connman_element_get_device_path(element);
374         if (device != NULL)
375                 connman_dbus_dict_append_variant(&dict, "Device",
376                                         DBUS_TYPE_OBJECT_PATH, &device);
377
378         network = __connman_element_get_network_path(element);
379         if (network != NULL)
380                 connman_dbus_dict_append_variant(&dict, "Network",
381                                         DBUS_TYPE_OBJECT_PATH, &network);
382
383         __connman_element_append_ipv4(element, &dict);
384
385         dbus_message_iter_close_container(&array, &dict);
386
387         return reply;
388 }
389
390 static DBusMessage *set_property(DBusConnection *conn,
391                                         DBusMessage *msg, void *data)
392 {
393         DBusMessageIter iter, value;
394         const char *name;
395         int type;
396
397         DBG("conn %p", conn);
398
399         if (dbus_message_iter_init(msg, &iter) == FALSE)
400                 return __connman_error_invalid_arguments(msg);
401
402         dbus_message_iter_get_basic(&iter, &name);
403         dbus_message_iter_next(&iter);
404         dbus_message_iter_recurse(&iter, &value);
405
406         if (__connman_security_check_privilege(msg,
407                                         CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
408                 return __connman_error_permission_denied(msg);
409
410         type = dbus_message_iter_get_arg_type(&value);
411
412         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
413 }
414
415 static GDBusMethodTable connection_methods[] = {
416         { "GetProperties", "",   "a{sv}", get_properties },
417         { "SetProperty",   "sv", "",      set_property   },
418         { },
419 };
420
421 static GDBusSignalTable connection_signals[] = {
422         { "PropertyChanged", "sv" },
423         { },
424 };
425
426 static void append_connections(DBusMessageIter *entry)
427 {
428         DBusMessageIter value, iter;
429         const char *key = "Connections";
430
431         dbus_message_iter_append_basic(entry, DBUS_TYPE_STRING, &key);
432
433         dbus_message_iter_open_container(entry, DBUS_TYPE_VARIANT,
434                 DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING,
435                                                                 &value);
436
437         dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
438                                 DBUS_TYPE_OBJECT_PATH_AS_STRING, &iter);
439         __connman_element_list(NULL, CONNMAN_ELEMENT_TYPE_CONNECTION, &iter);
440         dbus_message_iter_close_container(&value, &iter);
441
442         dbus_message_iter_close_container(entry, &value);
443 }
444
445 static void emit_connections_signal(void)
446 {
447         DBusMessage *signal;
448         DBusMessageIter entry;
449
450         DBG("");
451
452         signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
453                                 CONNMAN_MANAGER_INTERFACE, "PropertyChanged");
454         if (signal == NULL)
455                 return;
456
457         dbus_message_iter_init_append(signal, &entry);
458
459         append_connections(&entry);
460
461         g_dbus_send_message(connection, signal);
462 }
463
464 static int register_interface(struct connman_element *element)
465 {
466         DBG("element %p name %s path %s",
467                                 element, element->name, element->path);
468
469         if (g_dbus_register_interface(connection, element->path,
470                                         CONNMAN_CONNECTION_INTERFACE,
471                                         connection_methods, connection_signals,
472                                         NULL, element, NULL) == FALSE) {
473                 connman_error("Failed to register %s connection", element->path);
474                 return -EIO;
475         }
476
477         emit_connections_signal();
478
479         return 0;
480 }
481
482 static void unregister_interface(struct connman_element *element)
483 {
484         DBG("element %p name %s", element, element->name);
485
486         emit_connections_signal();
487
488         g_dbus_unregister_interface(connection, element->path,
489                                                 CONNMAN_CONNECTION_INTERFACE);
490 }
491
492 static struct gateway_data *find_active_gateway(void)
493 {
494         GSList *list;
495
496         DBG("");
497
498         for (list = gateway_list; list; list = list->next) {
499                 struct gateway_data *data = list->data;
500                 if (data->active == TRUE)
501                         return data;
502         }
503
504         return NULL;
505 }
506
507 static int connection_probe(struct connman_element *element)
508 {
509         struct connman_service *service = NULL;
510         const char *gateway = NULL;
511         struct gateway_data *active_gateway = NULL;
512         struct gateway_data *new_gateway = NULL;
513
514         DBG("element %p name %s", element, element->name);
515
516         if (element->parent == NULL)
517                 return -ENODEV;
518
519         if (element->parent->type != CONNMAN_ELEMENT_TYPE_IPV4)
520                 return -ENODEV;
521
522         connman_element_get_value(element,
523                                 CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
524
525         DBG("gateway %s", gateway);
526
527         if (register_interface(element) < 0)
528                 return -ENODEV;
529         service = __connman_element_get_service(element);
530         __connman_service_indicate_state(service,
531                                         CONNMAN_SERVICE_STATE_READY);
532         connman_element_set_enabled(element, TRUE);
533         emit_default_signal(element);
534
535         if (gateway == NULL)
536                 return 0;
537
538         active_gateway = find_active_gateway();
539         new_gateway = add_gateway(element->index, gateway);
540
541         if (active_gateway == NULL) {
542                 set_default_gateway(new_gateway);
543                 return 0;
544         }
545
546         if (new_gateway->order >= active_gateway->order) {
547                 del_route(active_gateway->element, active_gateway->gateway);
548                 return 0;
549         }
550
551         return 0;
552 }
553
554 static void connection_remove(struct connman_element *element)
555 {
556         struct connman_service *service;
557         const char *gateway = NULL;
558         struct gateway_data *data = NULL;
559
560         DBG("element %p name %s", element, element->name);
561
562         service = __connman_element_get_service(element);
563         __connman_service_indicate_state(service,
564                                         CONNMAN_SERVICE_STATE_DISCONNECT);
565
566         connman_element_set_enabled(element, FALSE);
567         emit_default_signal(element);
568
569         unregister_interface(element);
570
571         connman_element_get_value(element,
572                                 CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
573
574         DBG("gateway %s", gateway);
575
576         if (gateway == NULL)
577                 return;
578
579         data = find_gateway(element->index, gateway);
580         if (data == NULL)
581                 return;
582
583         remove_gateway(data);
584 }
585
586 static struct connman_driver connection_driver = {
587         .name           = "connection",
588         .type           = CONNMAN_ELEMENT_TYPE_CONNECTION,
589         .priority       = CONNMAN_DRIVER_PRIORITY_LOW,
590         .probe          = connection_probe,
591         .remove         = connection_remove,
592 };
593
594 int __connman_connection_init(void)
595 {
596         DBG("");
597
598         connection = connman_dbus_get_connection();
599
600         if (connman_rtnl_register(&connection_rtnl) < 0)
601                 connman_error("Failed to setup RTNL gateway driver");
602
603         connman_rtnl_send_getroute();
604
605         return connman_driver_register(&connection_driver);
606 }
607
608 void __connman_connection_cleanup(void)
609 {
610         GSList *list;
611
612         DBG("");
613
614         connman_driver_unregister(&connection_driver);
615
616         connman_rtnl_unregister(&connection_rtnl);
617
618         for (list = gateway_list; list; list = list->next) {
619                 struct gateway_data *data = list->data;
620
621                 DBG("index %d gateway %s", data->index, data->gateway);
622
623                 g_free(data->gateway);
624                 g_free(data);
625                 list->data = NULL;
626         }
627
628         g_slist_free(gateway_list);
629         gateway_list = NULL;
630
631         dbus_connection_unref(connection);
632 }
633
634 static void update_order(void)
635 {
636         GSList *list = NULL;
637
638         for (list = gateway_list; list; list = list->next) {
639                 struct gateway_data *data = list->data;
640                 struct connman_service *service;
641
642                 service = __connman_element_get_service(data->element);
643
644                 data->order = __connman_service_get_order(service);
645         }
646 }
647
648 void __connman_connection_update_gateway(void)
649 {
650         struct gateway_data *active_gateway, *default_gateway;
651
652         update_order();
653
654         active_gateway = find_active_gateway();
655         default_gateway = find_default_gateway();
656
657         if (active_gateway != default_gateway)
658                 del_route(active_gateway->element, active_gateway->gateway);
659 }