service: Indicate IP Configuration Ready Event
[platform/upstream/connman.git] / src / connection.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
6  *  Copyright (C) 2011  BMW Car IT GmbH. All rights reserved.
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License version 2 as
10  *  published by the Free Software Foundation.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <string.h>
28 #include <net/if.h>
29
30 #include <gdbus.h>
31
32 #include "connman.h"
33
34 struct gateway_data {
35         int index;
36         struct connman_service *service;
37         char *ipv4_gateway;
38         char *ipv6_gateway;
39         unsigned int order;
40         gboolean active;
41         /* VPN extra data */
42         gboolean vpn;
43         char *vpn_ip;
44         int vpn_phy_index;
45 };
46
47 static GHashTable *gateway_hash = NULL;
48
49 static struct gateway_data *find_gateway(int index, const char *gateway)
50 {
51         GHashTableIter iter;
52         gpointer value, key;
53
54         if (gateway == NULL)
55                 return NULL;
56
57         g_hash_table_iter_init(&iter, gateway_hash);
58
59         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
60                 struct gateway_data *data = value;
61
62                 if (data->ipv4_gateway == NULL)
63                         continue;
64
65                 if (data->index == index &&
66                                 g_str_equal(data->ipv4_gateway, gateway)
67                                                                 == TRUE)
68                         return data;
69         }
70
71         return NULL;
72 }
73
74 static int del_routes(struct gateway_data *data)
75 {
76         if (data->vpn) {
77                 if (data->vpn_phy_index >= 0)
78                         connman_inet_del_host_route(data->vpn_phy_index,
79                                                         data->ipv4_gateway);
80                 return connman_inet_clear_gateway_address(data->index,
81                                                         data->vpn_ip);
82         } else if (g_strcmp0(data->ipv4_gateway, "0.0.0.0") == 0) {
83                 return connman_inet_clear_gateway_interface(data->index);
84         } else {
85                 connman_inet_del_ipv6_host_route(data->index,
86                                                 data->ipv6_gateway);
87                 connman_inet_clear_ipv6_gateway_address(data->index,
88                                                         data->ipv6_gateway);
89                 connman_inet_del_host_route(data->index, data->ipv4_gateway);
90                 return connman_inet_clear_gateway_address(data->index,
91                                                         data->ipv4_gateway);
92         }
93 }
94
95 static struct gateway_data *add_gateway(struct connman_service *service,
96                                         int index, const char *ipv4_gateway,
97                                         const char *ipv6_gateway)
98 {
99         struct gateway_data *data;
100
101         if (strlen(ipv4_gateway) == 0)
102                 return NULL;
103
104         data = g_try_new0(struct gateway_data, 1);
105         if (data == NULL)
106                 return NULL;
107
108         data->index = index;
109         data->ipv4_gateway = g_strdup(ipv4_gateway);
110         data->ipv6_gateway = g_strdup(ipv6_gateway);
111         data->active = FALSE;
112         data->vpn_ip = NULL;
113         data->vpn = FALSE;
114         data->vpn_phy_index = -1;
115         data->service = service;
116
117         data->order = __connman_service_get_order(service);
118
119         g_hash_table_replace(gateway_hash, service, data);
120
121         return data;
122 }
123
124 static void connection_newgateway(int index, const char *gateway)
125 {
126         struct gateway_data *data;
127
128         DBG("index %d gateway %s", index, gateway);
129
130         data = find_gateway(index, gateway);
131         if (data == NULL)
132                 return;
133
134         data->active = TRUE;
135 }
136
137 static void set_default_gateway(struct gateway_data *data)
138 {
139         int index;
140
141         DBG("gateway %s", data->ipv4_gateway);
142
143         if (data->vpn == TRUE) {
144                 connman_inet_set_gateway_address(data->index,
145                                                         data->vpn_ip);
146                 data->active = TRUE;
147
148                 __connman_service_indicate_default(data->service);
149
150                 return;
151         }
152
153         index = __connman_service_get_index(data->service);
154
155         if (g_strcmp0(data->ipv4_gateway, "0.0.0.0") == 0) {
156                 if (connman_inet_set_gateway_interface(index) < 0)
157                         return;
158                 goto done;
159         }
160
161         connman_inet_set_ipv6_gateway_address(index, data->ipv6_gateway);
162         if (connman_inet_set_gateway_address(index, data->ipv4_gateway) < 0)
163                 return;
164
165 done:
166         __connman_service_indicate_default(data->service);
167 }
168
169 static struct gateway_data *find_default_gateway(void)
170 {
171         struct gateway_data *found = NULL;
172         unsigned int order = 0;
173         GHashTableIter iter;
174         gpointer value, key;
175
176         g_hash_table_iter_init(&iter, gateway_hash);
177
178         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
179                 struct gateway_data *data = value;
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 int disable_gateway(struct gateway_data *data)
191 {
192         if (data->active == TRUE)
193                 return del_routes(data);
194
195         return 0;
196 }
197
198 static void remove_gateway(gpointer user_data)
199 {
200         struct gateway_data *data = user_data;
201
202         DBG("gateway %s", data->ipv4_gateway);
203
204         g_free(data->ipv4_gateway);
205         g_free(data->ipv6_gateway);
206         g_free(data->vpn_ip);
207         g_free(data);
208 }
209
210 static void connection_delgateway(int index, const char *gateway)
211 {
212         struct gateway_data *data;
213
214         DBG("index %d gateway %s", index, gateway);
215
216         data = find_gateway(index, gateway);
217         if (data != NULL)
218                 data->active = FALSE;
219
220         data = find_default_gateway();
221         if (data != NULL)
222                 set_default_gateway(data);
223 }
224
225 static struct connman_rtnl connection_rtnl = {
226         .name           = "connection",
227         .newgateway     = connection_newgateway,
228         .delgateway     = connection_delgateway,
229 };
230
231 static struct gateway_data *find_active_gateway(void)
232 {
233         GHashTableIter iter;
234         gpointer value, key;
235
236         DBG("");
237
238         g_hash_table_iter_init(&iter, gateway_hash);
239
240         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
241                 struct gateway_data *data = value;
242
243                 if (data->active == TRUE)
244                         return data;
245         }
246
247         return NULL;
248 }
249
250 static void update_order(void)
251 {
252         GHashTableIter iter;
253         gpointer value, key;
254
255         DBG("");
256
257         g_hash_table_iter_init(&iter, gateway_hash);
258
259         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
260                 struct gateway_data *data = value;
261
262                 data->order = __connman_service_get_order(data->service);
263         }
264 }
265
266 int __connman_connection_gateway_add(struct connman_service *service,
267                                         const char *ipv4_gateway,
268                                         const char *ipv6_gateway,
269                                         const char *peer)
270 {
271         struct gateway_data *active_gateway = NULL;
272         struct gateway_data *new_gateway = NULL;
273         int index;
274
275         index = __connman_service_get_index(service);
276
277         DBG("service %p index %d ipv4 gateway %s ipv6 gateway %s vpn ip %s",
278                 service, index, ipv4_gateway, ipv6_gateway, peer);
279
280         /*
281          * If gateway is NULL, it's a point to point link and the default
282          * gateway is 0.0.0.0, meaning the interface.
283          */
284         if (ipv4_gateway == NULL)
285                 ipv4_gateway = "0.0.0.0";
286
287         active_gateway = find_active_gateway();
288         new_gateway = add_gateway(service, index, ipv4_gateway, ipv6_gateway);
289         if (new_gateway == NULL)
290                 return 0;
291
292         if (new_gateway->ipv6_gateway) {
293                 connman_inet_add_ipv6_host_route(index,
294                                                 new_gateway->ipv6_gateway,
295                                                 NULL);
296         }
297
298         if (g_strcmp0(new_gateway->ipv4_gateway, "0.0.0.0") != 0) {
299                 connman_inet_add_host_route(index,
300                                                 new_gateway->ipv4_gateway,
301                                                 NULL);
302         }
303
304         __connman_service_nameserver_add_routes(service,
305                                                 new_gateway->ipv4_gateway);
306
307         __connman_service_set_ipconfig_ready(service, CONNMAN_IPCONFIG_TYPE_IPV4);
308
309         if (connman_service_get_type(service) == CONNMAN_SERVICE_TYPE_VPN) {
310                 new_gateway->vpn = TRUE;
311                 if (peer != NULL)
312                         new_gateway->vpn_ip = g_strdup(peer);
313                 else if (ipv4_gateway != NULL)
314                         new_gateway->vpn_ip = g_strdup(ipv4_gateway);
315                 else
316                         new_gateway->vpn_ip = g_strdup(ipv6_gateway);
317
318                 if (active_gateway)
319                         new_gateway->vpn_phy_index = active_gateway->index;
320         } else {
321                 new_gateway->vpn = FALSE;
322         }
323
324         if (active_gateway == NULL) {
325                 set_default_gateway(new_gateway);
326                 return 0;
327         }
328
329         if (new_gateway->vpn == TRUE) {
330                 connman_inet_add_host_route(active_gateway->index,
331                                                 new_gateway->ipv4_gateway,
332                                                 active_gateway->ipv4_gateway);
333                 connman_inet_clear_gateway_address(active_gateway->index,
334                                                 active_gateway->ipv4_gateway);
335         }
336
337         return 0;
338 }
339
340 void __connman_connection_gateway_remove(struct connman_service *service)
341 {
342         struct gateway_data *data = NULL;
343         gboolean set_default = FALSE;
344         int err;
345
346         DBG("service %p", service);
347
348         __connman_service_nameserver_del_routes(service);
349
350         data = g_hash_table_lookup(gateway_hash, service);
351         if (data == NULL)
352                 return;
353
354         set_default = data->vpn;
355
356         if (data->vpn == TRUE && data->index >= 0) {
357                 connman_inet_del_host_route(data->index,
358                                                 data->ipv4_gateway);
359         }
360
361         __connman_service_nameserver_del_routes(service);
362
363         err = disable_gateway(data);
364         g_hash_table_remove(gateway_hash, service);
365
366         /* with vpn this will be called after the network was deleted,
367          * we need to call set_default here because we will not recieve any
368          * gateway delete notification.
369          * We hit the same issue if remove_gateway() fails.
370          */
371         if (set_default || err < 0) {
372                 data = find_default_gateway();
373                 if (data != NULL)
374                         set_default_gateway(data);
375         }
376 }
377
378 gboolean __connman_connection_update_gateway(void)
379 {
380         struct gateway_data *active_gateway, *default_gateway;
381         gboolean updated = FALSE;
382
383         if (gateway_hash == NULL)
384                 return updated;
385
386         update_order();
387
388         active_gateway = find_active_gateway();
389         default_gateway = find_default_gateway();
390
391         if (active_gateway && active_gateway != default_gateway)
392                 updated = TRUE;
393
394         return updated;
395 }
396
397 int __connman_connection_init(void)
398 {
399         int err;
400
401         DBG("");
402
403         gateway_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
404                                                         NULL, remove_gateway);
405
406         err = connman_rtnl_register(&connection_rtnl);
407         if (err < 0)
408                 connman_error("Failed to setup RTNL gateway driver");
409
410         return err;
411 }
412
413 void __connman_connection_cleanup(void)
414 {
415         GHashTableIter iter;
416         gpointer value, key;
417
418         DBG("");
419
420         connman_rtnl_unregister(&connection_rtnl);
421
422         g_hash_table_iter_init(&iter, gateway_hash);
423
424         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
425                 struct gateway_data *data = value;
426
427                 disable_gateway(data);
428         }
429
430         g_hash_table_destroy(gateway_hash);
431         gateway_hash = NULL;
432 }