Update service domain name when connection becomes active
[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                 /* vpn gateway going away no changes in services */
166                 return;
167         }
168
169         if (g_strcmp0(data->ipv4_gateway, "0.0.0.0") == 0) {
170                 if (connman_inet_set_gateway_interface(element->index) < 0)
171                         return;
172                 goto done;
173         }
174
175         connman_inet_set_ipv6_gateway_address(element->index,
176                                                 data->ipv6_gateway);
177         if (connman_inet_set_gateway_address(element->index,
178                                         data->ipv4_gateway) < 0)
179                 return;
180
181 done:
182         service = __connman_element_get_service(element);
183         __connman_service_indicate_default(service);
184 }
185
186 static struct gateway_data *find_default_gateway(void)
187 {
188         struct gateway_data *found = NULL;
189         unsigned int order = 0;
190         GSList *list;
191
192         for (list = gateway_list; list; list = list->next) {
193                 struct gateway_data *data = list->data;
194
195                 if (found == NULL || data->order > order) {
196                         found = data;
197                         order = data->order;
198                 }
199         }
200
201         return found;
202 }
203
204 static int remove_gateway(struct gateway_data *data)
205 {
206         int err;
207
208         DBG("gateway %s", data->ipv4_gateway);
209
210         gateway_list = g_slist_remove(gateway_list, data);
211
212         if (data->active == TRUE)
213                 err = del_routes(data);
214         else
215                 err = 0;
216
217         g_free(data->ipv4_gateway);
218         g_free(data->vpn_ip);
219         g_free(data);
220
221         return err;
222 }
223
224 static void connection_delgateway(int index, const char *gateway)
225 {
226         struct gateway_data *data;
227
228         DBG("index %d gateway %s", index, gateway);
229
230         data = find_gateway(index, gateway);
231         if (data != NULL)
232                 data->active = FALSE;
233
234         data = find_default_gateway();
235         if (data != NULL)
236                 set_default_gateway(data);
237 }
238
239 static struct connman_rtnl connection_rtnl = {
240         .name           = "connection",
241         .newgateway     = connection_newgateway,
242         .delgateway     = connection_delgateway,
243 };
244
245 static struct gateway_data *find_active_gateway(void)
246 {
247         GSList *list;
248
249         DBG("");
250
251         for (list = gateway_list; list; list = list->next) {
252                 struct gateway_data *data = list->data;
253
254                 if (data->active == TRUE)
255                         return data;
256         }
257
258         return NULL;
259 }
260
261 static int connection_probe(struct connman_element *element)
262 {
263         struct connman_service *service = NULL;
264         const char *gateway = NULL, *ipv6_gateway = NULL;
265         const char *vpn_ip = NULL;
266         const char *domainname = NULL;
267         struct gateway_data *active_gateway = NULL;
268         struct gateway_data *new_gateway = NULL;
269
270         DBG("element %p name %s", element, element->name);
271
272         if (element->parent == NULL)
273                 return -ENODEV;
274
275         /* FIXME: Remove temporarily for the static gateway support */
276         /* if (element->parent->type != CONNMAN_ELEMENT_TYPE_IPV4)
277                 return -ENODEV; */
278
279         connman_element_get_value(element,
280                                 CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
281         connman_element_get_value(element,
282                         CONNMAN_PROPERTY_ID_IPV6_GATEWAY, &ipv6_gateway);
283
284         connman_element_get_value(element,
285                         CONNMAN_PROPERTY_ID_IPV4_ADDRESS, &vpn_ip);
286
287         connman_element_get_value(element,
288                         CONNMAN_PROPERTY_ID_DOMAINNAME, &domainname);
289
290         DBG("ipv4 gateway %s ipv6 gateway %s domainname %s",
291                                 gateway, ipv6_gateway, domainname);
292
293         /*
294          * If gateway is NULL, it's a point to point link and the default
295          * gateway is 0.0.0.0, meaning the interface.
296          */
297         if (gateway == NULL) {
298                 gateway = "0.0.0.0";
299                 element->ipv4.gateway = g_strdup(gateway);
300         }
301
302         connman_element_set_enabled(element, TRUE);
303
304         active_gateway = find_active_gateway();
305         new_gateway = add_gateway(element->index, gateway, ipv6_gateway);
306         if (new_gateway == NULL)
307                 return 0;
308
309         service = __connman_element_get_service(element);
310
311         connman_inet_add_ipv6_host_route(element->index,
312                                         new_gateway->ipv6_gateway, NULL);
313         connman_inet_add_host_route(element->index,
314                                         new_gateway->ipv4_gateway, NULL);
315         __connman_service_nameserver_add_routes(service,
316                                                 new_gateway->ipv4_gateway);
317         __connman_service_set_domainname(service, domainname);
318
319         __connman_service_indicate_state(service, CONNMAN_SERVICE_STATE_READY);
320
321         if (service == NULL) {
322                 new_gateway->vpn = TRUE;
323                 new_gateway->vpn_ip = g_strdup(vpn_ip);
324                 /* make sure vpn gateway are at higher priority */
325                 new_gateway->order = 10;
326                 if (active_gateway)
327                         new_gateway->vpn_phy_index = active_gateway->index;
328         } else
329                 new_gateway->vpn = FALSE;
330
331         if (active_gateway == NULL) {
332                 set_default_gateway(new_gateway);
333                 return 0;
334         }
335
336         if (new_gateway->vpn == TRUE) {
337                 connman_inet_add_host_route(active_gateway->index,
338                                                 new_gateway->ipv4_gateway,
339                                                 active_gateway->ipv4_gateway);
340         }
341
342         if (new_gateway->order >= active_gateway->order) {
343                 del_routes(active_gateway);
344                 return 0;
345         }
346
347         return 0;
348 }
349
350 static void connection_remove(struct connman_element *element)
351 {
352         struct connman_service *service;
353         const char *gateway = NULL;
354         struct gateway_data *data = NULL;
355         gboolean set_default = FALSE;
356         int err;
357
358         DBG("element %p name %s", element, element->name);
359
360         service = __connman_element_get_service(element);
361         __connman_service_nameserver_del_routes(service);
362         __connman_service_indicate_state(service,
363                                         CONNMAN_SERVICE_STATE_DISCONNECT);
364
365         connman_element_set_enabled(element, FALSE);
366
367         connman_element_get_value(element,
368                                 CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
369
370         DBG("gateway %s", gateway);
371
372         if (gateway == NULL)
373                 return;
374
375         data = find_gateway(element->index, gateway);
376         if (data == NULL)
377                 return;
378
379         set_default = data->vpn;
380
381         if (data->vpn == TRUE && data->vpn_phy_index >= 0)
382                 connman_inet_del_host_route(data->vpn_phy_index,
383                                                 data->ipv4_gateway);
384         err = remove_gateway(data);
385
386         /* with vpn this will be called after the network was deleted,
387          * we need to call set_default here because we will not recieve any
388          * gateway delete notification.
389          * We hit the same issue if remove_gateway() fails.
390          */
391         if (set_default || err < 0) {
392                 data = find_default_gateway();
393                 if (data != NULL)
394                         set_default_gateway(data);
395         }
396 }
397
398 static struct connman_driver connection_driver = {
399         .name           = "connection",
400         .type           = CONNMAN_ELEMENT_TYPE_CONNECTION,
401         .priority       = CONNMAN_DRIVER_PRIORITY_LOW,
402         .probe          = connection_probe,
403         .remove         = connection_remove,
404 };
405
406 int __connman_connection_init(void)
407 {
408         DBG("");
409
410         if (connman_rtnl_register(&connection_rtnl) < 0)
411                 connman_error("Failed to setup RTNL gateway driver");
412
413         return connman_driver_register(&connection_driver);
414 }
415
416 void __connman_connection_cleanup(void)
417 {
418         GSList *list;
419
420         DBG("");
421
422         connman_driver_unregister(&connection_driver);
423
424         connman_rtnl_unregister(&connection_rtnl);
425
426         for (list = gateway_list; list; list = list->next) {
427                 struct gateway_data *data = list->data;
428
429                 DBG("index %d gateway %s", data->index, data->ipv4_gateway);
430
431                 g_free(data->ipv4_gateway);
432                 g_free(data);
433                 list->data = NULL;
434         }
435
436         g_slist_free(gateway_list);
437         gateway_list = NULL;
438 }
439
440 static void update_order(void)
441 {
442         GSList *list = NULL;
443
444         for (list = gateway_list; list; list = list->next) {
445                 struct gateway_data *data = list->data;
446                 struct connman_service *service;
447
448                 /* vpn gataway is not attached to a service. */
449                 if (data->vpn)
450                         continue;
451
452                 service = __connman_element_get_service(data->element);
453                 data->order = __connman_service_get_order(service);
454         }
455 }
456
457 gboolean __connman_connection_update_gateway(void)
458 {
459         struct gateway_data *active_gateway, *default_gateway;
460         gboolean updated = FALSE;
461
462         update_order();
463
464         active_gateway = find_active_gateway();
465         default_gateway = find_default_gateway();
466
467         if (active_gateway && active_gateway != default_gateway) {
468                 del_routes(active_gateway);
469                 updated = TRUE;
470         }
471
472         return updated;
473 }