Set VPN services as the default ones
[framework/connectivity/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->vpn_ip);
225         g_free(data);
226
227         return err;
228 }
229
230 static void connection_delgateway(int index, const char *gateway)
231 {
232         struct gateway_data *data;
233
234         DBG("index %d gateway %s", index, gateway);
235
236         data = find_gateway(index, gateway);
237         if (data != NULL)
238                 data->active = FALSE;
239
240         data = find_default_gateway();
241         if (data != NULL)
242                 set_default_gateway(data);
243 }
244
245 static struct connman_rtnl connection_rtnl = {
246         .name           = "connection",
247         .newgateway     = connection_newgateway,
248         .delgateway     = connection_delgateway,
249 };
250
251 static struct gateway_data *find_active_gateway(void)
252 {
253         GSList *list;
254
255         DBG("");
256
257         for (list = gateway_list; list; list = list->next) {
258                 struct gateway_data *data = list->data;
259
260                 if (data->active == TRUE)
261                         return data;
262         }
263
264         return NULL;
265 }
266
267 static int connection_probe(struct connman_element *element)
268 {
269         struct connman_service *service = NULL;
270         const char *gateway = NULL, *ipv6_gateway = NULL;
271         const char *vpn_ip = NULL;
272         const char *domainname = NULL;
273         struct gateway_data *active_gateway = NULL;
274         struct gateway_data *new_gateway = NULL;
275
276         DBG("element %p name %s", element, element->name);
277
278         if (element->parent == NULL)
279                 return -ENODEV;
280
281         /* FIXME: Remove temporarily for the static gateway support */
282         /* if (element->parent->type != CONNMAN_ELEMENT_TYPE_IPV4)
283                 return -ENODEV; */
284
285         connman_element_get_value(element,
286                                 CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
287         connman_element_get_value(element,
288                         CONNMAN_PROPERTY_ID_IPV6_GATEWAY, &ipv6_gateway);
289
290         connman_element_get_value(element,
291                         CONNMAN_PROPERTY_ID_IPV4_ADDRESS, &vpn_ip);
292
293         connman_element_get_value(element,
294                         CONNMAN_PROPERTY_ID_DOMAINNAME, &domainname);
295
296         DBG("ipv4 gateway %s ipv6 gateway %s domainname %s",
297                                 gateway, ipv6_gateway, domainname);
298
299         /*
300          * If gateway is NULL, it's a point to point link and the default
301          * gateway is 0.0.0.0, meaning the interface.
302          */
303         if (gateway == NULL) {
304                 gateway = "0.0.0.0";
305                 element->ipv4.gateway = g_strdup(gateway);
306         }
307
308         connman_element_set_enabled(element, TRUE);
309
310         active_gateway = find_active_gateway();
311         new_gateway = add_gateway(element->index, gateway, ipv6_gateway);
312         if (new_gateway == NULL)
313                 return 0;
314
315         service = __connman_element_get_service(element);
316
317         connman_inet_add_ipv6_host_route(element->index,
318                                         new_gateway->ipv6_gateway, NULL);
319         connman_inet_add_host_route(element->index,
320                                         new_gateway->ipv4_gateway, NULL);
321         __connman_service_nameserver_add_routes(service,
322                                                 new_gateway->ipv4_gateway);
323         __connman_service_set_domainname(service, domainname);
324
325         __connman_service_indicate_state(service, CONNMAN_SERVICE_STATE_READY);
326
327         if (service == NULL) {
328                 new_gateway->vpn = TRUE;
329                 new_gateway->vpn_ip = g_strdup(vpn_ip);
330                 /* make sure vpn gateway are at higher priority */
331                 new_gateway->order = 10;
332                 if (active_gateway)
333                         new_gateway->vpn_phy_index = active_gateway->index;
334         } else
335                 new_gateway->vpn = FALSE;
336
337         if (active_gateway == NULL) {
338                 set_default_gateway(new_gateway);
339                 return 0;
340         }
341
342         if (new_gateway->vpn == TRUE) {
343                 connman_inet_add_host_route(active_gateway->index,
344                                                 new_gateway->ipv4_gateway,
345                                                 active_gateway->ipv4_gateway);
346         }
347
348         if (new_gateway->order >= active_gateway->order) {
349                 del_routes(active_gateway);
350                 return 0;
351         }
352
353         return 0;
354 }
355
356 static void connection_remove(struct connman_element *element)
357 {
358         struct connman_service *service;
359         const char *gateway = NULL;
360         struct gateway_data *data = NULL;
361         gboolean set_default = FALSE;
362         int err;
363
364         DBG("element %p name %s", element, element->name);
365
366         service = __connman_element_get_service(element);
367         __connman_service_nameserver_del_routes(service);
368         __connman_service_indicate_state(service,
369                                         CONNMAN_SERVICE_STATE_DISCONNECT);
370
371         connman_element_set_enabled(element, FALSE);
372
373         connman_element_get_value(element,
374                                 CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
375
376         DBG("gateway %s", gateway);
377
378         if (gateway == NULL)
379                 return;
380
381         data = find_gateway(element->index, gateway);
382         if (data == NULL)
383                 return;
384
385         set_default = data->vpn;
386
387         if (data->vpn == TRUE && data->vpn_phy_index >= 0)
388                 connman_inet_del_host_route(data->vpn_phy_index,
389                                                 data->ipv4_gateway);
390         err = remove_gateway(data);
391
392         /* with vpn this will be called after the network was deleted,
393          * we need to call set_default here because we will not recieve any
394          * gateway delete notification.
395          * We hit the same issue if remove_gateway() fails.
396          */
397         if (set_default || err < 0) {
398                 data = find_default_gateway();
399                 if (data != NULL)
400                         set_default_gateway(data);
401         }
402 }
403
404 static struct connman_driver connection_driver = {
405         .name           = "connection",
406         .type           = CONNMAN_ELEMENT_TYPE_CONNECTION,
407         .priority       = CONNMAN_DRIVER_PRIORITY_LOW,
408         .probe          = connection_probe,
409         .remove         = connection_remove,
410 };
411
412 int __connman_connection_init(void)
413 {
414         DBG("");
415
416         if (connman_rtnl_register(&connection_rtnl) < 0)
417                 connman_error("Failed to setup RTNL gateway driver");
418
419         return connman_driver_register(&connection_driver);
420 }
421
422 void __connman_connection_cleanup(void)
423 {
424         GSList *list;
425
426         DBG("");
427
428         connman_driver_unregister(&connection_driver);
429
430         connman_rtnl_unregister(&connection_rtnl);
431
432         for (list = gateway_list; list; list = list->next) {
433                 struct gateway_data *data = list->data;
434
435                 DBG("index %d gateway %s", data->index, data->ipv4_gateway);
436
437                 g_free(data->ipv4_gateway);
438                 g_free(data);
439                 list->data = NULL;
440         }
441
442         g_slist_free(gateway_list);
443         gateway_list = NULL;
444 }
445
446 static void update_order(void)
447 {
448         GSList *list = NULL;
449
450         for (list = gateway_list; list; list = list->next) {
451                 struct gateway_data *data = list->data;
452                 struct connman_service *service;
453                 int index = data->index;
454
455                 if (data->vpn)
456                         service = __connman_service_lookup_from_index(index);
457                 else
458                         service = __connman_element_get_service(data->element);
459
460                 data->order = __connman_service_get_order(service);
461         }
462 }
463
464 gboolean __connman_connection_update_gateway(void)
465 {
466         struct gateway_data *active_gateway, *default_gateway;
467         gboolean updated = FALSE;
468
469         update_order();
470
471         active_gateway = find_active_gateway();
472         default_gateway = find_default_gateway();
473
474         if (active_gateway && active_gateway != default_gateway) {
475                 del_routes(active_gateway);
476                 updated = TRUE;
477         }
478
479         return updated;
480 }