Release 0.10
[connman] / 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 };
42
43 static GSList *gateway_list = NULL;
44
45 static struct gateway_data *find_gateway(int index, const char *gateway)
46 {
47         GSList *list;
48
49         if (gateway == NULL)
50                 return NULL;
51
52         for (list = gateway_list; list; list = list->next) {
53                 struct gateway_data *data = list->data;
54
55                 if (data->gateway == NULL)
56                         continue;
57
58                 if (data->index == index &&
59                                 g_str_equal(data->gateway, gateway) == TRUE)
60                         return data;
61         }
62
63         return NULL;
64 }
65
66 static int set_route(struct connman_element *element, const char *gateway)
67 {
68         struct ifreq ifr;
69         struct rtentry rt;
70         struct sockaddr_in *addr;
71         int sk, err;
72
73         DBG("element %p", element);
74
75         sk = socket(PF_INET, SOCK_DGRAM, 0);
76         if (sk < 0)
77                 return -1;
78
79         memset(&ifr, 0, sizeof(ifr));
80         ifr.ifr_ifindex = element->index;
81
82         if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
83                 close(sk);
84                 return -1;
85         }
86
87         DBG("ifname %s", ifr.ifr_name);
88
89         memset(&rt, 0, sizeof(rt));
90         rt.rt_flags = RTF_UP | RTF_GATEWAY;
91
92         addr = (struct sockaddr_in *) &rt.rt_dst;
93         addr->sin_family = AF_INET;
94         addr->sin_addr.s_addr = INADDR_ANY;
95
96         addr = (struct sockaddr_in *) &rt.rt_gateway;
97         addr->sin_family = AF_INET;
98         addr->sin_addr.s_addr = inet_addr(gateway);
99
100         addr = (struct sockaddr_in *) &rt.rt_genmask;
101         addr->sin_family = AF_INET;
102         addr->sin_addr.s_addr = INADDR_ANY;
103
104         err = ioctl(sk, SIOCADDRT, &rt);
105         if (err < 0)
106                 DBG("default route setting failed (%s)", strerror(errno));
107
108         close(sk);
109
110         return err;
111 }
112
113 static int del_route(struct connman_element *element, const char *gateway)
114 {
115         struct ifreq ifr;
116         struct rtentry rt;
117         struct sockaddr_in *addr;
118         int sk, err;
119
120         DBG("element %p", element);
121
122         sk = socket(PF_INET, SOCK_DGRAM, 0);
123         if (sk < 0)
124                 return -1;
125
126         memset(&ifr, 0, sizeof(ifr));
127         ifr.ifr_ifindex = element->index;
128
129         if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
130                 close(sk);
131                 return -1;
132         }
133
134         DBG("ifname %s", ifr.ifr_name);
135
136         memset(&rt, 0, sizeof(rt));
137         rt.rt_flags = RTF_UP | RTF_GATEWAY;
138
139         addr = (struct sockaddr_in *) &rt.rt_dst;
140         addr->sin_family = AF_INET;
141         addr->sin_addr.s_addr = INADDR_ANY;
142
143         addr = (struct sockaddr_in *) &rt.rt_gateway;
144         addr->sin_family = AF_INET;
145         addr->sin_addr.s_addr = inet_addr(gateway);
146
147         addr = (struct sockaddr_in *) &rt.rt_genmask;
148         addr->sin_family = AF_INET;
149         addr->sin_addr.s_addr = INADDR_ANY;
150
151         err = ioctl(sk, SIOCDELRT, &rt);
152         if (err < 0)
153                 DBG("default route removal failed (%s)", strerror(errno));
154
155         close(sk);
156
157         return err;
158 }
159
160 static DBusConnection *connection;
161
162 static void emit_default_signal(struct connman_element *element)
163 {
164         DBusMessage *signal;
165         DBusMessageIter entry, value;
166         const char *key = "Default";
167
168         signal = dbus_message_new_signal(element->path,
169                         CONNMAN_CONNECTION_INTERFACE, "PropertyChanged");
170         if (signal == NULL)
171                 return;
172
173         dbus_message_iter_init_append(signal, &entry);
174
175         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
176
177         dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
178                                         DBUS_TYPE_BOOLEAN_AS_STRING, &value);
179         dbus_message_iter_append_basic(&value, DBUS_TYPE_BOOLEAN,
180                                                         &element->enabled);
181         dbus_message_iter_close_container(&entry, &value);
182
183         g_dbus_send_message(connection, signal);
184 }
185
186 static void set_default(struct connman_element *element, gpointer user_data)
187 {
188         struct gateway_data *data = user_data;
189
190         DBG("element %p name %s", element, element->name);
191
192         if (element->index != data->index)
193                 return;
194
195         if (element->enabled == TRUE)
196                 return;
197
198         connman_element_set_enabled(element, TRUE);
199         emit_default_signal(element);
200 }
201
202 static void del_default(struct connman_element *element, gpointer user_data)
203 {
204         struct gateway_data *data = user_data;
205
206         DBG("element %p name %s", element, element->name);
207
208         if (element->index != data->index)
209                 return;
210
211         if (element->enabled == FALSE)
212                 return;
213
214         connman_element_set_enabled(element, FALSE);
215         emit_default_signal(element);
216 }
217
218 static void new_default(struct connman_element *element, gpointer user_data)
219 {
220         const char *gateway;
221
222         DBG("element %p name %s", element, element->name);
223
224         if (g_slist_length(gateway_list) > 0)
225                 return;
226
227         connman_element_get_value(element,
228                                 CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
229
230         DBG("gateway %s", gateway);
231
232         if (gateway == NULL)
233                 return;
234
235         set_route(element, gateway);
236
237         connman_element_set_enabled(element, TRUE);
238         emit_default_signal(element);
239 }
240
241 static void connection_newgateway(int index, const char *gateway)
242 {
243         struct gateway_data *data;
244
245         DBG("index %d gateway %s", index, gateway);
246
247         data = find_gateway(index, gateway);
248         if (data != NULL)
249                 return;
250
251         data = g_try_new0(struct gateway_data, 1);
252         if (data == NULL)
253                 return;
254
255         data->index = index;
256         data->gateway = g_strdup(gateway);
257
258         gateway_list = g_slist_append(gateway_list, data);
259
260         __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_CONNECTION,
261                                                         set_default, data);
262 }
263
264 static void connection_delgateway(int index, const char *gateway)
265 {
266         struct gateway_data *data;
267
268         DBG("index %d gateway %s", index, gateway);
269
270         data = find_gateway(index, gateway);
271         if (data == NULL)
272                 return;
273
274         gateway_list = g_slist_remove(gateway_list, data);
275
276         __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_CONNECTION,
277                                                         del_default, data);
278
279         g_free(data->gateway);
280         g_free(data);
281
282         if (g_slist_length(gateway_list) > 0)
283                 return;
284
285         DBG("selecting new default gateway");
286
287         __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_CONNECTION,
288                                                         new_default, NULL);
289 }
290
291 static struct connman_rtnl connection_rtnl = {
292         .name           = "connection",
293         .newgateway     = connection_newgateway,
294         .delgateway     = connection_delgateway,
295 };
296
297 static DBusMessage *get_properties(DBusConnection *conn,
298                                         DBusMessage *msg, void *data)
299 {
300         struct connman_element *element = data;
301         DBusMessage *reply;
302         DBusMessageIter array, dict;
303         connman_uint8_t strength = 0;
304         const char *device, *network;
305         const char *type = NULL;
306
307         DBG("conn %p", conn);
308
309         if (__connman_security_check_privilege(msg,
310                                         CONNMAN_SECURITY_PRIVILEGE_PUBLIC) < 0)
311                 return __connman_error_permission_denied(msg);
312
313         reply = dbus_message_new_method_return(msg);
314         if (reply == NULL)
315                 return NULL;
316
317         dbus_message_iter_init_append(reply, &array);
318
319         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
320                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
321                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
322                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
323
324         connman_element_get_static_property(element, "Type", &type);
325
326         if (type != NULL)
327                 connman_dbus_dict_append_variant(&dict, "Type",
328                                                 DBUS_TYPE_STRING, &type);
329
330         connman_element_get_static_property(element, "Strength", &strength);
331         if (strength > 0)
332                 connman_dbus_dict_append_variant(&dict, "Strength",
333                                                 DBUS_TYPE_BYTE, &strength);
334
335         if (element->devname != NULL)
336                 connman_dbus_dict_append_variant(&dict, "Interface",
337                                         DBUS_TYPE_STRING, &element->devname);
338
339         connman_dbus_dict_append_variant(&dict, "Default",
340                                         DBUS_TYPE_BOOLEAN, &element->enabled);
341
342         device = __connman_element_get_device(element);
343         if (device != NULL)
344                 connman_dbus_dict_append_variant(&dict, "Device",
345                                         DBUS_TYPE_OBJECT_PATH, &device);
346
347         network = __connman_element_get_network(element);
348         if (network != NULL)
349                 connman_dbus_dict_append_variant(&dict, "Network",
350                                         DBUS_TYPE_OBJECT_PATH, &network);
351
352         __connman_element_append_ipv4(element, &dict);
353
354         dbus_message_iter_close_container(&array, &dict);
355
356         return reply;
357 }
358
359 static DBusMessage *set_property(DBusConnection *conn,
360                                         DBusMessage *msg, void *data)
361 {
362         DBusMessageIter iter, value;
363         const char *name;
364         int type;
365
366         DBG("conn %p", conn);
367
368         if (dbus_message_iter_init(msg, &iter) == FALSE)
369                 return __connman_error_invalid_arguments(msg);
370
371         dbus_message_iter_get_basic(&iter, &name);
372         dbus_message_iter_next(&iter);
373         dbus_message_iter_recurse(&iter, &value);
374
375         if (__connman_security_check_privilege(msg,
376                                         CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
377                 return __connman_error_permission_denied(msg);
378
379         type = dbus_message_iter_get_arg_type(&value);
380
381         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
382 }
383
384 static GDBusMethodTable connection_methods[] = {
385         { "GetProperties", "",   "a{sv}", get_properties },
386         { "SetProperty",   "sv", "",      set_property   },
387         { },
388 };
389
390 static GDBusSignalTable connection_signals[] = {
391         { "PropertyChanged", "sv" },
392         { },
393 };
394
395 static void append_connections(DBusMessageIter *entry)
396 {
397         DBusMessageIter value, iter;
398         const char *key = "Connections";
399
400         dbus_message_iter_append_basic(entry, DBUS_TYPE_STRING, &key);
401
402         dbus_message_iter_open_container(entry, DBUS_TYPE_VARIANT,
403                 DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING,
404                                                                 &value);
405
406         dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
407                                 DBUS_TYPE_OBJECT_PATH_AS_STRING, &iter);
408         __connman_element_list(NULL, CONNMAN_ELEMENT_TYPE_CONNECTION, &iter);
409         dbus_message_iter_close_container(&value, &iter);
410
411         dbus_message_iter_close_container(entry, &value);
412 }
413
414 static void emit_connections_signal(void)
415 {
416         DBusMessage *signal;
417         DBusMessageIter entry;
418
419         signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
420                                 CONNMAN_MANAGER_INTERFACE, "PropertyChanged");
421         if (signal == NULL)
422                 return;
423
424         dbus_message_iter_init_append(signal, &entry);
425
426         append_connections(&entry);
427
428         g_dbus_send_message(connection, signal);
429 }
430
431 static int register_interface(struct connman_element *element)
432 {
433         DBG("element %p name %s", element, element->name);
434
435         if (g_dbus_register_interface(connection, element->path,
436                                         CONNMAN_CONNECTION_INTERFACE,
437                                         connection_methods, connection_signals,
438                                         NULL, element, NULL) == FALSE) {
439                 connman_error("Failed to register %s connection", element->path);
440                 return -EIO;
441         }
442
443         emit_connections_signal();
444
445         return 0;
446 }
447
448 static void unregister_interface(struct connman_element *element)
449 {
450         DBG("element %p name %s", element, element->name);
451
452         emit_connections_signal();
453
454         g_dbus_unregister_interface(connection, element->path,
455                                                 CONNMAN_CONNECTION_INTERFACE);
456 }
457
458 static int connection_probe(struct connman_element *element)
459 {
460         const char *gateway = NULL;
461
462         DBG("element %p name %s", element, element->name);
463
464         if (element->parent == NULL)
465                 return -ENODEV;
466
467         if (element->parent->type != CONNMAN_ELEMENT_TYPE_IPV4)
468                 return -ENODEV;
469
470         connman_element_get_value(element,
471                                 CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
472
473         DBG("gateway %s", gateway);
474
475         if (register_interface(element) < 0)
476                 return -ENODEV;
477
478         if (gateway == NULL)
479                 return 0;
480
481         if (find_gateway(element->index, gateway) != NULL) {
482                 DBG("previous gateway still present");
483                 goto done;
484         }
485
486         if (g_slist_length(gateway_list) > 0) {
487                 DBG("default gateway already present");
488                 return 0;
489         }
490
491         set_route(element, gateway);
492
493 done:
494         connman_element_set_enabled(element, TRUE);
495         emit_default_signal(element);
496
497         return 0;
498 }
499
500 static void connection_remove(struct connman_element *element)
501 {
502         const char *gateway = NULL;
503
504         DBG("element %p name %s", element, element->name);
505
506         unregister_interface(element);
507
508         connman_element_get_value(element,
509                                 CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
510
511         DBG("gateway %s", gateway);
512
513         if (gateway == NULL)
514                 return;
515
516         connman_element_set_enabled(element, FALSE);
517         emit_default_signal(element);
518
519         del_route(element, gateway);
520 }
521
522 static struct connman_driver connection_driver = {
523         .name           = "connection",
524         .type           = CONNMAN_ELEMENT_TYPE_CONNECTION,
525         .priority       = CONNMAN_DRIVER_PRIORITY_LOW,
526         .probe          = connection_probe,
527         .remove         = connection_remove,
528 };
529
530 int __connman_connection_init(void)
531 {
532         DBG("");
533
534         connection = connman_dbus_get_connection();
535
536         if (connman_rtnl_register(&connection_rtnl) < 0)
537                 connman_error("Failed to setup RTNL gateway driver");
538
539         connman_rtnl_send_getroute();
540
541         return connman_driver_register(&connection_driver);
542 }
543
544 void __connman_connection_cleanup(void)
545 {
546         GSList *list;
547
548         DBG("");
549
550         connman_driver_unregister(&connection_driver);
551
552         connman_rtnl_unregister(&connection_rtnl);
553
554         for (list = gateway_list; list; list = list->next) {
555                 struct gateway_data *data = list->data;
556
557                 DBG("index %d gateway %s", data->index, data->gateway);
558
559                 g_free(data->gateway);
560                 g_free(data);
561                 list->data = NULL;
562         }
563
564         g_slist_free(gateway_list);
565         gateway_list = NULL;
566
567         dbus_connection_unref(connection);
568 }