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