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