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