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