connection: Add gateway host routes for non NULL gateways
[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_PEER, &vpn_ip);
292
293         if (vpn_ip == NULL)
294                 connman_element_get_value(element,
295                                 CONNMAN_PROPERTY_ID_IPV4_ADDRESS, &vpn_ip);
296
297         DBG("vpn_ip %s", vpn_ip);
298
299         connman_element_get_value(element,
300                         CONNMAN_PROPERTY_ID_DOMAINNAME, &domainname);
301
302         DBG("ipv4 gateway %s ipv6 gateway %s domainname %s",
303                                 gateway, ipv6_gateway, domainname);
304
305         /*
306          * If gateway is NULL, it's a point to point link and the default
307          * gateway is 0.0.0.0, meaning the interface.
308          */
309         if (gateway == NULL) {
310                 gateway = "0.0.0.0";
311                 element->ipv4.gateway = g_strdup(gateway);
312         }
313
314         connman_element_set_enabled(element, TRUE);
315
316         active_gateway = find_active_gateway();
317         new_gateway = add_gateway(element->index, gateway, ipv6_gateway);
318         if (new_gateway == NULL)
319                 return 0;
320
321         service = __connman_element_get_service(element);
322
323         if (new_gateway->ipv6_gateway)
324                 connman_inet_add_ipv6_host_route(element->index,
325                                         new_gateway->ipv6_gateway, NULL);
326
327         if (g_strcmp0(new_gateway->ipv4_gateway, "0.0.0.0"))
328                 connman_inet_add_host_route(element->index,
329                                         new_gateway->ipv4_gateway, NULL);
330         __connman_service_nameserver_add_routes(service,
331                                                 new_gateway->ipv4_gateway);
332         __connman_service_set_domainname(service, domainname);
333
334         __connman_service_indicate_state(service, CONNMAN_SERVICE_STATE_READY);
335
336         if (service == NULL) {
337                 new_gateway->vpn = TRUE;
338                 new_gateway->vpn_ip = g_strdup(vpn_ip);
339                 /* make sure vpn gateway are at higher priority */
340                 new_gateway->order = 10;
341                 if (active_gateway)
342                         new_gateway->vpn_phy_index = active_gateway->index;
343         } else
344                 new_gateway->vpn = FALSE;
345
346         if (active_gateway == NULL) {
347                 set_default_gateway(new_gateway);
348                 return 0;
349         }
350
351         if (new_gateway->vpn == TRUE) {
352                 connman_inet_add_host_route(active_gateway->index,
353                                                 new_gateway->ipv4_gateway,
354                                                 active_gateway->ipv4_gateway);
355         }
356
357         if (new_gateway->order >= active_gateway->order) {
358                 del_routes(active_gateway);
359                 return 0;
360         }
361
362         return 0;
363 }
364
365 static void connection_remove(struct connman_element *element)
366 {
367         struct connman_service *service;
368         const char *gateway = NULL;
369         struct gateway_data *data = NULL;
370         gboolean set_default = FALSE;
371         int err;
372
373         DBG("element %p name %s", element, element->name);
374
375         service = __connman_element_get_service(element);
376         __connman_service_nameserver_del_routes(service);
377         __connman_service_indicate_state(service,
378                                         CONNMAN_SERVICE_STATE_DISCONNECT);
379
380         connman_element_set_enabled(element, FALSE);
381
382         connman_element_get_value(element,
383                                 CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
384
385         DBG("gateway %s", gateway);
386
387         if (gateway == NULL)
388                 return;
389
390         data = find_gateway(element->index, gateway);
391         if (data == NULL)
392                 return;
393
394         set_default = data->vpn;
395
396         if (data->vpn == TRUE && data->vpn_phy_index >= 0)
397                 connman_inet_del_host_route(data->vpn_phy_index,
398                                                 data->ipv4_gateway);
399         err = remove_gateway(data);
400
401         /* with vpn this will be called after the network was deleted,
402          * we need to call set_default here because we will not recieve any
403          * gateway delete notification.
404          * We hit the same issue if remove_gateway() fails.
405          */
406         if (set_default || err < 0) {
407                 data = find_default_gateway();
408                 if (data != NULL)
409                         set_default_gateway(data);
410         }
411 }
412
413 static struct connman_driver connection_driver = {
414         .name           = "connection",
415         .type           = CONNMAN_ELEMENT_TYPE_CONNECTION,
416         .priority       = CONNMAN_DRIVER_PRIORITY_LOW,
417         .probe          = connection_probe,
418         .remove         = connection_remove,
419 };
420
421 int __connman_connection_init(void)
422 {
423         DBG("");
424
425         if (connman_rtnl_register(&connection_rtnl) < 0)
426                 connman_error("Failed to setup RTNL gateway driver");
427
428         return connman_driver_register(&connection_driver);
429 }
430
431 void __connman_connection_cleanup(void)
432 {
433         GSList *list;
434
435         DBG("");
436
437         connman_driver_unregister(&connection_driver);
438
439         connman_rtnl_unregister(&connection_rtnl);
440
441         for (list = gateway_list; list; list = list->next) {
442                 struct gateway_data *data = list->data;
443
444                 DBG("index %d gateway %s", data->index, data->ipv4_gateway);
445
446                 g_free(data->ipv4_gateway);
447                 g_free(data);
448                 list->data = NULL;
449         }
450
451         g_slist_free(gateway_list);
452         gateway_list = NULL;
453 }
454
455 static void update_order(void)
456 {
457         GSList *list = NULL;
458
459         for (list = gateway_list; list; list = list->next) {
460                 struct gateway_data *data = list->data;
461                 struct connman_service *service;
462                 int index = data->index;
463
464                 if (data->vpn)
465                         service = __connman_service_lookup_from_index(index);
466                 else
467                         service = __connman_element_get_service(data->element);
468
469                 data->order = __connman_service_get_order(service);
470         }
471 }
472
473 gboolean __connman_connection_update_gateway(void)
474 {
475         struct gateway_data *active_gateway, *default_gateway;
476         gboolean updated = FALSE;
477
478         update_order();
479
480         active_gateway = find_active_gateway();
481         default_gateway = find_default_gateway();
482
483         if (active_gateway && active_gateway != default_gateway) {
484                 del_routes(active_gateway);
485                 updated = TRUE;
486         }
487
488         return updated;
489 }