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