Add support for storing default gateway information
[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         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));
99
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));
104
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));
109
110         rt.rt_dev = ifr.ifr_name;
111
112         err = ioctl(sk, SIOCADDRT, &rt);
113         if (err < 0)
114                 connman_error("Setting host gateway route failed (%s)",
115                                                         strerror(errno));
116
117         memset(&rt, 0, sizeof(rt));
118         rt.rt_flags = RTF_UP | RTF_GATEWAY;
119
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));
124
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));
129
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));
134
135         err = ioctl(sk, SIOCADDRT, &rt);
136         if (err < 0)
137                 connman_error("Setting default route failed (%s)",
138                                                         strerror(errno));
139
140         close(sk);
141
142         return err;
143 }
144
145 static int del_route(struct connman_element *element, const char *gateway)
146 {
147         struct ifreq ifr;
148         struct rtentry rt;
149         struct sockaddr_in addr;
150         int sk, err;
151
152         DBG("element %p", element);
153
154         sk = socket(PF_INET, SOCK_DGRAM, 0);
155         if (sk < 0)
156                 return -1;
157
158         memset(&ifr, 0, sizeof(ifr));
159         ifr.ifr_ifindex = element->index;
160
161         if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
162                 close(sk);
163                 return -1;
164         }
165
166         DBG("ifname %s", ifr.ifr_name);
167
168         memset(&rt, 0, sizeof(rt));
169         rt.rt_flags = RTF_UP | RTF_GATEWAY;
170
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));
175
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));
180
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));
185
186         err = ioctl(sk, SIOCDELRT, &rt);
187         if (err < 0)
188                 connman_error("Removing default route failed (%s)",
189                                                         strerror(errno));
190
191         close(sk);
192
193         return err;
194 }
195
196 static DBusConnection *connection;
197
198 static void emit_default_signal(struct connman_element *element)
199 {
200         DBusMessage *signal;
201         DBusMessageIter entry, value;
202         const char *key = "Default";
203
204         signal = dbus_message_new_signal(element->path,
205                         CONNMAN_CONNECTION_INTERFACE, "PropertyChanged");
206         if (signal == NULL)
207                 return;
208
209         dbus_message_iter_init_append(signal, &entry);
210
211         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
212
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,
216                                                         &element->enabled);
217         dbus_message_iter_close_container(&entry, &value);
218
219         g_dbus_send_message(connection, signal);
220 }
221
222 static void find_element(struct connman_element *element, gpointer user_data)
223 {
224         struct gateway_data *data = user_data;
225
226         DBG("element %p name %s", element, element->name);
227
228         if (data->element != NULL)
229                 return;
230
231         if (element->index != data->index)
232                 return;
233
234         data->element = element;
235 }
236
237 static struct gateway_data *add_gateway(int index, const char *gateway)
238 {
239         struct gateway_data *data;
240         struct connman_service *service;
241
242         data = g_try_new0(struct gateway_data, 1);
243         if (data == NULL)
244                 return NULL;
245
246         data->index = index;
247         data->gateway = g_strdup(gateway);
248         data->active = FALSE;
249         data->element = NULL;
250
251         __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_CONNECTION,
252                                                         find_element, data);
253
254         service = __connman_element_get_service(data->element);
255         data->order = __connman_service_get_order(service);
256
257         gateway_list = g_slist_append(gateway_list, data);
258
259         return data;
260 }
261
262 static void connection_newgateway(int index, const char *gateway)
263 {
264         struct gateway_data *data;
265
266         DBG("index %d gateway %s", index, gateway);
267
268         data = find_gateway(index, gateway);
269         if (data == NULL)
270                 return;
271
272         data->active = TRUE;
273 }
274
275 static void set_default_gateway(struct gateway_data *data)
276 {
277         struct connman_element *element = data->element;
278         struct connman_service *service = NULL;
279
280         DBG("gateway %s", data->gateway);
281
282         if (set_route(element, data->gateway) < 0)
283                 return;
284
285         service = __connman_element_get_service(element);
286         __connman_service_indicate_default(service);
287 }
288
289 static struct gateway_data *find_default_gateway(void)
290 {
291         struct gateway_data *found = NULL;
292         unsigned int order = 0;
293         GSList *list;
294
295         for (list = gateway_list; list; list = list->next) {
296                 struct gateway_data *data = list->data;
297
298                 if (found == NULL || data->order > order) {
299                         found = data;
300                         order = data->order;
301                 }
302         }
303
304         return found;
305 }
306
307 static void remove_gateway(struct gateway_data *data)
308 {
309         DBG("gateway %s", data->gateway);
310
311         gateway_list = g_slist_remove(gateway_list, data);
312
313         if (data->active == TRUE)
314                 del_route(data->element, data->gateway);
315
316         g_free(data->gateway);
317         g_free(data);
318 }
319
320 static void connection_delgateway(int index, const char *gateway)
321 {
322         struct gateway_data *data;
323
324         DBG("index %d gateway %s", index, gateway);
325
326         data = find_gateway(index, gateway);
327         if (data != NULL)
328                 data->active = FALSE;
329
330         data = find_default_gateway();
331         if (data != NULL)
332                 set_default_gateway(data);
333 }
334
335 static struct connman_rtnl connection_rtnl = {
336         .name           = "connection",
337         .newgateway     = connection_newgateway,
338         .delgateway     = connection_delgateway,
339 };
340
341 static DBusMessage *get_properties(DBusConnection *conn,
342                                         DBusMessage *msg, void *data)
343 {
344         struct connman_element *element = data;
345         DBusMessage *reply;
346         DBusMessageIter array, dict;
347         connman_uint8_t strength;
348         const char *device, *network;
349         const char *type;
350
351         DBG("conn %p", conn);
352
353         if (__connman_security_check_privilege(msg,
354                                         CONNMAN_SECURITY_PRIVILEGE_PUBLIC) < 0)
355                 return __connman_error_permission_denied(msg);
356
357         reply = dbus_message_new_method_return(msg);
358         if (reply == NULL)
359                 return NULL;
360
361         dbus_message_iter_init_append(reply, &array);
362
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);
367
368         type = connman_element_get_string(element, "Type");
369         if (type != NULL)
370                 connman_dbus_dict_append_variant(&dict, "Type",
371                                                 DBUS_TYPE_STRING, &type);
372
373         strength = connman_element_get_uint8(element, "Strength");
374         if (strength > 0)
375                 connman_dbus_dict_append_variant(&dict, "Strength",
376                                                 DBUS_TYPE_BYTE, &strength);
377
378         if (element->devname != NULL)
379                 connman_dbus_dict_append_variant(&dict, "Interface",
380                                         DBUS_TYPE_STRING, &element->devname);
381
382         connman_dbus_dict_append_variant(&dict, "Default",
383                                         DBUS_TYPE_BOOLEAN, &element->enabled);
384
385         device = __connman_element_get_device_path(element);
386         if (device != NULL)
387                 connman_dbus_dict_append_variant(&dict, "Device",
388                                         DBUS_TYPE_OBJECT_PATH, &device);
389
390         network = __connman_element_get_network_path(element);
391         if (network != NULL)
392                 connman_dbus_dict_append_variant(&dict, "Network",
393                                         DBUS_TYPE_OBJECT_PATH, &network);
394
395         __connman_element_append_ipv4(element, &dict);
396
397         dbus_message_iter_close_container(&array, &dict);
398
399         return reply;
400 }
401
402 static DBusMessage *set_property(DBusConnection *conn,
403                                         DBusMessage *msg, void *data)
404 {
405         DBusMessageIter iter, value;
406         const char *name;
407         int type;
408
409         DBG("conn %p", conn);
410
411         if (dbus_message_iter_init(msg, &iter) == FALSE)
412                 return __connman_error_invalid_arguments(msg);
413
414         dbus_message_iter_get_basic(&iter, &name);
415         dbus_message_iter_next(&iter);
416         dbus_message_iter_recurse(&iter, &value);
417
418         if (__connman_security_check_privilege(msg,
419                                         CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
420                 return __connman_error_permission_denied(msg);
421
422         type = dbus_message_iter_get_arg_type(&value);
423
424         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
425 }
426
427 static GDBusMethodTable connection_methods[] = {
428         { "GetProperties", "",   "a{sv}", get_properties },
429         { "SetProperty",   "sv", "",      set_property   },
430         { },
431 };
432
433 static GDBusSignalTable connection_signals[] = {
434         { "PropertyChanged", "sv" },
435         { },
436 };
437
438 static void append_connections(DBusMessageIter *entry)
439 {
440         DBusMessageIter value, iter;
441         const char *key = "Connections";
442
443         dbus_message_iter_append_basic(entry, DBUS_TYPE_STRING, &key);
444
445         dbus_message_iter_open_container(entry, DBUS_TYPE_VARIANT,
446                 DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING,
447                                                                 &value);
448
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);
453
454         dbus_message_iter_close_container(entry, &value);
455 }
456
457 static void emit_connections_signal(void)
458 {
459         DBusMessage *signal;
460         DBusMessageIter entry;
461
462         DBG("");
463
464         signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
465                                 CONNMAN_MANAGER_INTERFACE, "PropertyChanged");
466         if (signal == NULL)
467                 return;
468
469         dbus_message_iter_init_append(signal, &entry);
470
471         append_connections(&entry);
472
473         g_dbus_send_message(connection, signal);
474 }
475
476 static int register_interface(struct connman_element *element)
477 {
478         DBG("element %p name %s path %s",
479                                 element, element->name, element->path);
480
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);
486                 return -EIO;
487         }
488
489         emit_connections_signal();
490
491         return 0;
492 }
493
494 static void unregister_interface(struct connman_element *element)
495 {
496         DBG("element %p name %s", element, element->name);
497
498         emit_connections_signal();
499
500         g_dbus_unregister_interface(connection, element->path,
501                                                 CONNMAN_CONNECTION_INTERFACE);
502 }
503
504 static struct gateway_data *find_active_gateway(void)
505 {
506         GSList *list;
507
508         DBG("");
509
510         for (list = gateway_list; list; list = list->next) {
511                 struct gateway_data *data = list->data;
512                 if (data->active == TRUE)
513                         return data;
514         }
515
516         return NULL;
517 }
518
519 static int connection_probe(struct connman_element *element)
520 {
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;
525
526         DBG("element %p name %s", element, element->name);
527
528         if (element->parent == NULL)
529                 return -ENODEV;
530
531         if (element->parent->type != CONNMAN_ELEMENT_TYPE_IPV4)
532                 return -ENODEV;
533
534         connman_element_get_value(element,
535                                 CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
536
537         DBG("gateway %s", gateway);
538
539         if (register_interface(element) < 0)
540                 return -ENODEV;
541
542         service = __connman_element_get_service(element);
543         __connman_service_indicate_state(service,
544                                         CONNMAN_SERVICE_STATE_READY);
545
546         connman_element_set_enabled(element, TRUE);
547         emit_default_signal(element);
548
549         if (gateway == NULL)
550                 return 0;
551
552         active_gateway = find_active_gateway();
553         new_gateway = add_gateway(element->index, gateway);
554
555         if (active_gateway == NULL) {
556                 set_default_gateway(new_gateway);
557                 return 0;
558         }
559
560         if (new_gateway->order >= active_gateway->order) {
561                 del_route(active_gateway->element, active_gateway->gateway);
562                 return 0;
563         }
564
565         return 0;
566 }
567
568 static void connection_remove(struct connman_element *element)
569 {
570         struct connman_service *service;
571         const char *gateway = NULL;
572         struct gateway_data *data = NULL;
573
574         DBG("element %p name %s", element, element->name);
575
576         service = __connman_element_get_service(element);
577         __connman_service_indicate_state(service,
578                                         CONNMAN_SERVICE_STATE_DISCONNECT);
579
580         connman_element_set_enabled(element, FALSE);
581         emit_default_signal(element);
582
583         unregister_interface(element);
584
585         connman_element_get_value(element,
586                                 CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
587
588         DBG("gateway %s", gateway);
589
590         if (gateway == NULL)
591                 return;
592
593         data = find_gateway(element->index, gateway);
594         if (data == NULL)
595                 return;
596
597         remove_gateway(data);
598 }
599
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,
606 };
607
608 int __connman_connection_init(void)
609 {
610         DBG("");
611
612         connection = connman_dbus_get_connection();
613
614         if (connman_rtnl_register(&connection_rtnl) < 0)
615                 connman_error("Failed to setup RTNL gateway driver");
616
617         return connman_driver_register(&connection_driver);
618 }
619
620 void __connman_connection_cleanup(void)
621 {
622         GSList *list;
623
624         DBG("");
625
626         connman_driver_unregister(&connection_driver);
627
628         connman_rtnl_unregister(&connection_rtnl);
629
630         for (list = gateway_list; list; list = list->next) {
631                 struct gateway_data *data = list->data;
632
633                 DBG("index %d gateway %s", data->index, data->gateway);
634
635                 g_free(data->gateway);
636                 g_free(data);
637                 list->data = NULL;
638         }
639
640         g_slist_free(gateway_list);
641         gateway_list = NULL;
642
643         dbus_connection_unref(connection);
644 }
645
646 static void update_order(void)
647 {
648         GSList *list = NULL;
649
650         for (list = gateway_list; list; list = list->next) {
651                 struct gateway_data *data = list->data;
652                 struct connman_service *service;
653
654                 service = __connman_element_get_service(data->element);
655                 data->order = __connman_service_get_order(service);
656         }
657 }
658
659 gboolean __connman_connection_update_gateway(void)
660 {
661         struct gateway_data *active_gateway, *default_gateway;
662         gboolean updated = FALSE;
663
664         update_order();
665
666         active_gateway = find_active_gateway();
667         default_gateway = find_default_gateway();
668
669         if (active_gateway && active_gateway != default_gateway) {
670                 del_route(active_gateway->element, active_gateway->gateway);
671                 updated = TRUE;
672         }
673
674         return updated;
675 }