service: Split service state to IPv4 and IPv6 parts
[platform/upstream/connman.git] / src / connection.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  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 <string.h>
27 #include <net/if.h>
28
29 #include <gdbus.h>
30
31 #include "connman.h"
32
33 struct gateway_data {
34         int index;
35         char *ipv4_gateway;
36         char *ipv6_gateway;
37         struct connman_element *element;
38         unsigned int order;
39         gboolean active;
40         /* VPN extra data */
41         gboolean vpn;
42         char *vpn_ip;
43         int vpn_phy_index;
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->ipv4_gateway == NULL)
59                         continue;
60
61                 if (data->index == index &&
62                                 g_str_equal(data->ipv4_gateway, gateway)
63                                                                 == TRUE)
64                         return data;
65         }
66
67         return NULL;
68 }
69
70 static int del_routes(struct gateway_data *data)
71 {
72         if (data->vpn) {
73                 if (data->vpn_phy_index >= 0)
74                         connman_inet_del_host_route(data->vpn_phy_index,
75                                                         data->ipv4_gateway);
76                 return connman_inet_clear_gateway_address(data->index,
77                                                         data->vpn_ip);
78         } else if (g_strcmp0(data->ipv4_gateway, "0.0.0.0") == 0) {
79                 return connman_inet_clear_gateway_interface(data->index);
80         } else {
81                 connman_inet_del_ipv6_host_route(data->index,
82                                                 data->ipv6_gateway);
83                 connman_inet_clear_ipv6_gateway_address(data->index,
84                                                         data->ipv6_gateway);
85                 connman_inet_del_host_route(data->index, data->ipv4_gateway);
86                 return connman_inet_clear_gateway_address(data->index,
87                                                         data->ipv4_gateway);
88         }
89 }
90
91 static void find_element(struct connman_element *element, gpointer user_data)
92 {
93         struct gateway_data *data = user_data;
94
95         DBG("element %p name %s", element, element->name);
96
97         if (data->element != NULL)
98                 return;
99
100         if (element->index != data->index)
101                 return;
102
103         data->element = element;
104 }
105
106 static struct gateway_data *add_gateway(int index, const char *gateway,
107                                                 const char *ipv6_gateway)
108 {
109         struct gateway_data *data;
110         struct connman_service *service;
111
112         DBG("index %d ipv4 gateway %s ipv6 gateway %s", index, gateway,
113                                                         ipv6_gateway);
114
115         if (strlen(gateway) == 0)
116                 return NULL;
117
118         data = g_try_new0(struct gateway_data, 1);
119         if (data == NULL)
120                 return NULL;
121
122         data->index = index;
123         data->ipv4_gateway = g_strdup(gateway);
124         data->ipv6_gateway = g_strdup(ipv6_gateway);
125         data->active = FALSE;
126         data->element = NULL;
127         data->vpn_ip = NULL;
128         data->vpn = FALSE;
129         data->vpn_phy_index = -1;
130
131         __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_CONNECTION,
132                                                         find_element, data);
133
134         service = __connman_element_get_service(data->element);
135         data->order = __connman_service_get_order(service);
136
137         gateway_list = g_slist_append(gateway_list, data);
138
139         return data;
140 }
141
142 static void connection_newgateway(int index, const char *gateway)
143 {
144         struct gateway_data *data;
145
146         DBG("index %d gateway %s", index, gateway);
147
148         data = find_gateway(index, gateway);
149         if (data == NULL)
150                 return;
151
152         data->active = TRUE;
153 }
154
155 static void set_default_gateway(struct gateway_data *data)
156 {
157         struct connman_element *element = data->element;
158         struct connman_service *service = NULL;
159
160         DBG("gateway %s", data->ipv4_gateway);
161
162         if (data->vpn == TRUE) {
163                 connman_inet_set_gateway_address(data->index, data->vpn_ip);
164                 data->active = TRUE;
165
166                 service = __connman_service_lookup_from_index(data->index);
167                 if (service == NULL)
168                         return;
169
170                 __connman_service_indicate_default(service);
171
172                 return;
173         }
174
175         if (g_strcmp0(data->ipv4_gateway, "0.0.0.0") == 0) {
176                 if (connman_inet_set_gateway_interface(element->index) < 0)
177                         return;
178                 goto done;
179         }
180
181         connman_inet_set_ipv6_gateway_address(element->index,
182                                                 data->ipv6_gateway);
183         if (connman_inet_set_gateway_address(element->index,
184                                         data->ipv4_gateway) < 0)
185                 return;
186
187 done:
188         service = __connman_element_get_service(element);
189         __connman_service_indicate_default(service);
190 }
191
192 static struct gateway_data *find_default_gateway(void)
193 {
194         struct gateway_data *found = NULL;
195         unsigned int order = 0;
196         GSList *list;
197
198         for (list = gateway_list; list; list = list->next) {
199                 struct gateway_data *data = list->data;
200
201                 if (found == NULL || data->order > order) {
202                         found = data;
203                         order = data->order;
204                 }
205         }
206
207         return found;
208 }
209
210 static int remove_gateway(struct gateway_data *data)
211 {
212         int err;
213
214         DBG("gateway %s", data->ipv4_gateway);
215
216         gateway_list = g_slist_remove(gateway_list, data);
217
218         if (data->active == TRUE)
219                 err = del_routes(data);
220         else
221                 err = 0;
222
223         g_free(data->ipv4_gateway);
224         g_free(data->ipv6_gateway);
225         g_free(data->vpn_ip);
226         g_free(data);
227
228         return err;
229 }
230
231 static void connection_delgateway(int index, const char *gateway)
232 {
233         struct gateway_data *data;
234
235         DBG("index %d gateway %s", index, gateway);
236
237         data = find_gateway(index, gateway);
238         if (data != NULL)
239                 data->active = FALSE;
240
241         data = find_default_gateway();
242         if (data != NULL)
243                 set_default_gateway(data);
244 }
245
246 static struct connman_rtnl connection_rtnl = {
247         .name           = "connection",
248         .newgateway     = connection_newgateway,
249         .delgateway     = connection_delgateway,
250 };
251
252 static struct gateway_data *find_active_gateway(void)
253 {
254         GSList *list;
255
256         DBG("");
257
258         for (list = gateway_list; list; list = list->next) {
259                 struct gateway_data *data = list->data;
260
261                 if (data->active == TRUE)
262                         return data;
263         }
264
265         return NULL;
266 }
267
268 static int connection_probe(struct connman_element *element)
269 {
270         struct connman_service *service = NULL;
271         const char *gateway = NULL, *ipv6_gateway = NULL;
272         const char *vpn_ip = NULL;
273         const char *domainname = NULL;
274         struct gateway_data *active_gateway = NULL;
275         struct gateway_data *new_gateway = NULL;
276
277         DBG("element %p name %s", element, element->name);
278
279         if (element->parent == NULL)
280                 return -ENODEV;
281
282         /* FIXME: Remove temporarily for the static gateway support */
283         /* if (element->parent->type != CONNMAN_ELEMENT_TYPE_IPV4)
284                 return -ENODEV; */
285
286         connman_element_get_value(element,
287                                 CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
288         connman_element_get_value(element,
289                         CONNMAN_PROPERTY_ID_IPV6_GATEWAY, &ipv6_gateway);
290
291         connman_element_get_value(element,
292                         CONNMAN_PROPERTY_ID_IPV4_PEER, &vpn_ip);
293
294         if (vpn_ip == NULL)
295                 connman_element_get_value(element,
296                                 CONNMAN_PROPERTY_ID_IPV4_ADDRESS, &vpn_ip);
297
298         DBG("vpn_ip %s", vpn_ip);
299
300         connman_element_get_value(element,
301                         CONNMAN_PROPERTY_ID_DOMAINNAME, &domainname);
302
303         DBG("ipv4 gateway %s ipv6 gateway %s domainname %s",
304                                 gateway, ipv6_gateway, domainname);
305
306         /*
307          * If gateway is NULL, it's a point to point link and the default
308          * gateway is 0.0.0.0, meaning the interface.
309          */
310         if (gateway == NULL) {
311                 gateway = "0.0.0.0";
312                 element->ipv4.gateway = g_strdup(gateway);
313         }
314
315         connman_element_set_enabled(element, TRUE);
316
317         active_gateway = find_active_gateway();
318         new_gateway = add_gateway(element->index, gateway, ipv6_gateway);
319         if (new_gateway == NULL)
320                 return 0;
321
322         service = __connman_element_get_service(element);
323
324         if (new_gateway->ipv6_gateway)
325                 connman_inet_add_ipv6_host_route(element->index,
326                                         new_gateway->ipv6_gateway, NULL);
327
328         if (g_strcmp0(new_gateway->ipv4_gateway, "0.0.0.0"))
329                 connman_inet_add_host_route(element->index,
330                                         new_gateway->ipv4_gateway, NULL);
331         __connman_service_nameserver_add_routes(service,
332                                                 new_gateway->ipv4_gateway);
333         __connman_service_set_domainname(service, domainname);
334
335         __connman_service_indicate_state(service, CONNMAN_SERVICE_STATE_READY,
336                                                 CONNMAN_IPCONFIG_TYPE_IPV4);
337
338         if (service == NULL) {
339                 new_gateway->vpn = TRUE;
340                 new_gateway->vpn_ip = g_strdup(vpn_ip);
341                 /* make sure vpn gateway are at higher priority */
342                 new_gateway->order = 10;
343                 if (active_gateway)
344                         new_gateway->vpn_phy_index = active_gateway->index;
345         } else
346                 new_gateway->vpn = FALSE;
347
348         if (active_gateway == NULL) {
349                 set_default_gateway(new_gateway);
350                 return 0;
351         }
352
353         if (new_gateway->vpn == TRUE) {
354                 connman_inet_add_host_route(active_gateway->index,
355                                                 new_gateway->ipv4_gateway,
356                                                 active_gateway->ipv4_gateway);
357         }
358
359         if (new_gateway->order >= active_gateway->order) {
360                 del_routes(active_gateway);
361                 return 0;
362         }
363
364         return 0;
365 }
366
367 static void connection_remove(struct connman_element *element)
368 {
369         struct connman_service *service;
370         const char *gateway = NULL;
371         struct gateway_data *data = NULL;
372         gboolean set_default = FALSE;
373         int err;
374
375         DBG("element %p name %s", element, element->name);
376
377         service = __connman_element_get_service(element);
378         __connman_service_nameserver_del_routes(service);
379         __connman_service_indicate_state(service,
380                                         CONNMAN_SERVICE_STATE_DISCONNECT,
381                                         CONNMAN_IPCONFIG_TYPE_IPV4);
382
383         connman_element_set_enabled(element, FALSE);
384
385         connman_element_get_value(element,
386                                 CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
387
388         DBG("gateway %s", gateway);
389
390         if (gateway == NULL)
391                 return;
392
393         data = find_gateway(element->index, gateway);
394         if (data == NULL)
395                 return;
396
397         set_default = data->vpn;
398
399         if (data->vpn == TRUE && data->vpn_phy_index >= 0)
400                 connman_inet_del_host_route(data->vpn_phy_index,
401                                                 data->ipv4_gateway);
402         err = remove_gateway(data);
403
404         /* with vpn this will be called after the network was deleted,
405          * we need to call set_default here because we will not recieve any
406          * gateway delete notification.
407          * We hit the same issue if remove_gateway() fails.
408          */
409         if (set_default || err < 0) {
410                 data = find_default_gateway();
411                 if (data != NULL)
412                         set_default_gateway(data);
413         }
414
415         connman_element_unref(element);
416 }
417
418 static struct connman_driver connection_driver = {
419         .name           = "connection",
420         .type           = CONNMAN_ELEMENT_TYPE_CONNECTION,
421         .priority       = CONNMAN_DRIVER_PRIORITY_LOW,
422         .probe          = connection_probe,
423         .remove         = connection_remove,
424 };
425
426 int __connman_connection_init(void)
427 {
428         DBG("");
429
430         if (connman_rtnl_register(&connection_rtnl) < 0)
431                 connman_error("Failed to setup RTNL gateway driver");
432
433         return connman_driver_register(&connection_driver);
434 }
435
436 void __connman_connection_cleanup(void)
437 {
438         GSList *list;
439
440         DBG("");
441
442         connman_driver_unregister(&connection_driver);
443
444         connman_rtnl_unregister(&connection_rtnl);
445
446         for (list = gateway_list; list; list = list->next) {
447                 struct gateway_data *data = list->data;
448
449                 DBG("index %d gateway %s", data->index, data->ipv4_gateway);
450
451                 g_free(data->ipv4_gateway);
452                 g_free(data);
453                 list->data = NULL;
454         }
455
456         g_slist_free(gateway_list);
457         gateway_list = NULL;
458 }
459
460 static void update_order(void)
461 {
462         GSList *list = NULL;
463
464         for (list = gateway_list; list; list = list->next) {
465                 struct gateway_data *data = list->data;
466                 struct connman_service *service;
467                 int index = data->index;
468
469                 if (data->vpn)
470                         service = __connman_service_lookup_from_index(index);
471                 else
472                         service = __connman_element_get_service(data->element);
473
474                 data->order = __connman_service_get_order(service);
475         }
476 }
477
478 gboolean __connman_connection_update_gateway(void)
479 {
480         struct gateway_data *active_gateway, *default_gateway;
481         gboolean updated = FALSE;
482
483         update_order();
484
485         active_gateway = find_active_gateway();
486         default_gateway = find_default_gateway();
487
488         if (active_gateway && active_gateway != default_gateway) {
489                 del_routes(active_gateway);
490                 updated = TRUE;
491         }
492
493         return updated;
494 }