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