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