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