Fix resource leak
[platform/upstream/connman.git] / src / ipv6pd.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2013  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 <errno.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30 #include <stdio.h>
31
32 #include "connman.h"
33
34 #include <gdhcp/gdhcp.h>
35
36 #define DEFAULT_ROUTER_LIFETIME 180  /* secs */
37 #define DEFAULT_RA_INTERVAL 120  /* secs */
38
39 static int bridge_index = -1;
40 static guint timer_uplink;
41 static guint timer_ra;
42 static char *default_interface;
43 static GSList *prefixes;
44 static void *rs_context;
45
46 static int setup_prefix_delegation(struct connman_service *service);
47 static void dhcpv6_callback(struct connman_network *network,
48                         enum __connman_dhcpv6_status status, gpointer data);
49
50 static int enable_ipv6_forward(bool enable)
51 {
52         FILE *f;
53
54         f = fopen("/proc/sys/net/ipv6/ip_forward", "r+");
55         if (!f)
56                 return -errno;
57
58         if (enable)
59                 fprintf(f, "1");
60         else
61                 fprintf(f, "0");
62
63         fclose(f);
64
65         return 0;
66 }
67
68 static gboolean send_ra(gpointer data)
69 {
70         __connman_inet_ipv6_send_ra(bridge_index, NULL, prefixes,
71                                         DEFAULT_ROUTER_LIFETIME);
72
73         return TRUE;
74 }
75
76 static void start_ra(int ifindex, GSList *prefix)
77 {
78         if (prefixes)
79                 g_slist_free_full(prefixes, g_free);
80
81         prefixes = g_dhcpv6_copy_prefixes(prefix);
82
83         enable_ipv6_forward(true);
84
85         if (timer_ra > 0)
86                 g_source_remove(timer_ra);
87
88         send_ra(NULL);
89
90         timer_ra = g_timeout_add_seconds(DEFAULT_RA_INTERVAL, send_ra, NULL);
91 }
92
93 static void stop_ra(int ifindex)
94 {
95         __connman_inet_ipv6_send_ra(ifindex, NULL, prefixes, 0);
96
97         if (timer_ra > 0) {
98                 g_source_remove(timer_ra);
99                 timer_ra = 0;
100         }
101
102         enable_ipv6_forward(false);
103
104         if (prefixes) {
105                 g_slist_free_full(prefixes, g_free);
106                 prefixes = NULL;
107         }
108 }
109
110 static void rs_received(struct nd_router_solicit *reply,
111                         unsigned int length, void *user_data)
112 {
113         GDHCPIAPrefix *prefix;
114         GSList *list;
115
116         if (!prefixes)
117                 return;
118
119         DBG("");
120
121         for (list = prefixes; list; list = list->next) {
122                 prefix = list->data;
123
124                 prefix->valid -= time(NULL) - prefix->expire;
125                 prefix->preferred -= time(NULL) - prefix->expire;
126         }
127
128         __connman_inet_ipv6_send_ra(bridge_index, NULL, prefixes,
129                                         DEFAULT_ROUTER_LIFETIME);
130 }
131
132 static gboolean do_setup(gpointer data)
133 {
134         int ret;
135
136         timer_uplink = 0;
137
138         if (!default_interface)
139                 DBG("No uplink connection, retrying prefix delegation");
140
141         ret = setup_prefix_delegation(connman_service_get_default());
142         if (ret < 0 && ret != -EINPROGRESS)
143                 return TRUE; /* delegation error, try again */
144
145         return FALSE;
146 }
147
148 static void dhcpv6_renew_callback(struct connman_network *network,
149                                 enum __connman_dhcpv6_status status,
150                                 gpointer data)
151 {
152         DBG("network %p status %d data %p", network, status, data);
153
154         if (status == CONNMAN_DHCPV6_STATUS_SUCCEED)
155                 dhcpv6_callback(network, status, data);
156         else
157                 setup_prefix_delegation(connman_service_get_default());
158 }
159
160 static void cleanup(void)
161 {
162         if (timer_uplink != 0) {
163                 g_source_remove(timer_uplink);
164                 timer_uplink = 0;
165         }
166
167         if (prefixes) {
168                 g_slist_free_full(prefixes, g_free);
169                 prefixes = NULL;
170         }
171 }
172
173 static void dhcpv6_callback(struct connman_network *network,
174                         enum __connman_dhcpv6_status status, gpointer data)
175 {
176         GSList *prefix_list = data;
177
178         DBG("network %p status %d data %p", network, status, data);
179
180         if (status == CONNMAN_DHCPV6_STATUS_FAIL) {
181                 DBG("Prefix delegation request failed");
182                 cleanup();
183                 return;
184         }
185
186         if (!prefix_list) {
187                 DBG("No prefixes, retrying");
188                 if (timer_uplink == 0)
189                         timer_uplink = g_timeout_add_seconds(10, do_setup,
190                                                                         NULL);
191                 return;
192         }
193
194         /*
195          * After we have got a list of prefixes, we can start to send router
196          * advertisements (RA) to tethering interface.
197          */
198         start_ra(bridge_index, prefix_list);
199
200         if (__connman_dhcpv6_start_pd_renew(network,
201                                         dhcpv6_renew_callback) == -ETIMEDOUT)
202                 dhcpv6_renew_callback(network, CONNMAN_DHCPV6_STATUS_FAIL,
203                                                                         NULL);
204 }
205
206 static int setup_prefix_delegation(struct connman_service *service)
207 {
208         struct connman_ipconfig *ipconfig;
209         char *interface;
210         int err = 0, ifindex;
211
212         if (!service) {
213                 /*
214                  * We do not have uplink connection. We just wait until
215                  * default interface is updated.
216                  */
217                 return -EINPROGRESS;
218         }
219
220         interface = connman_service_get_interface(service);
221
222         DBG("interface %s bridge_index %d", interface, bridge_index);
223
224         if (default_interface) {
225                 stop_ra(bridge_index);
226
227                 ifindex = connman_inet_ifindex(default_interface);
228                 __connman_dhcpv6_stop_pd(ifindex);
229         }
230
231         g_free(default_interface);
232
233         ipconfig = __connman_service_get_ip6config(service);
234         if (!__connman_ipconfig_ipv6_is_enabled(ipconfig)) {
235                 g_free(interface);
236                 default_interface = NULL;
237                 return -EPFNOSUPPORT;
238         }
239
240         default_interface = interface;
241
242         if (default_interface) {
243                 ifindex = connman_inet_ifindex(default_interface);
244
245                 /*
246                  * Try to get a IPv6 prefix so we can start to advertise it.
247                  */
248                 err = __connman_dhcpv6_start_pd(ifindex, prefixes,
249                                                 dhcpv6_callback);
250                 if (err < 0)
251                         DBG("prefix delegation %d/%s", err, strerror(-err));
252         }
253
254         return err;
255 }
256
257 static void update_default_interface(struct connman_service *service)
258 {
259         setup_prefix_delegation(service);
260 }
261
262 static void update_ipconfig(struct connman_service *service,
263                                 struct connman_ipconfig *ipconfig)
264 {
265         if (!service || service != connman_service_get_default())
266                 return;
267
268         if (ipconfig != __connman_service_get_ip6config(service))
269                 return;
270
271         if (!__connman_ipconfig_ipv6_is_enabled(ipconfig)) {
272                 if (default_interface) {
273                         int ifindex;
274
275                         ifindex = connman_inet_ifindex(default_interface);
276                         __connman_dhcpv6_stop_pd(ifindex);
277
278                         g_free(default_interface);
279                         default_interface = NULL;
280                 }
281
282                 DBG("No IPv6 support for interface index %d",
283                         __connman_ipconfig_get_index(ipconfig));
284                 return;
285         }
286
287         /*
288          * Did we had PD activated already? If not, then start it.
289          */
290         if (!default_interface) {
291                 DBG("IPv6 ipconfig %p changed for interface index %d", ipconfig,
292                         __connman_ipconfig_get_index(ipconfig));
293
294                 setup_prefix_delegation(service);
295         }
296 }
297
298 static const struct connman_notifier pd_notifier = {
299         .name                   = "IPv6 prefix delegation",
300         .default_changed        = update_default_interface,
301         .ipconfig_changed       = update_ipconfig,
302 };
303
304 int __connman_ipv6pd_setup(const char *bridge)
305 {
306         struct connman_service *service;
307         int err;
308
309         if (!connman_inet_is_ipv6_supported())
310                 return -EPFNOSUPPORT;
311
312         if (bridge_index >= 0) {
313                 DBG("Prefix delegation already running");
314                 return -EALREADY;
315         }
316
317         err = connman_notifier_register(&pd_notifier);
318         if (err < 0)
319                 return err;
320
321         bridge_index = connman_inet_ifindex(bridge);
322
323         err = __connman_inet_ipv6_start_recv_rs(bridge_index, rs_received,
324                                                 NULL, &rs_context);
325         if (err < 0)
326                 DBG("Cannot receive router solicitation %d/%s",
327                         err, strerror(-err));
328
329         service = connman_service_get_default();
330         if (service) {
331                 /*
332                  * We have an uplink connection already, try to use it.
333                  */
334                 return setup_prefix_delegation(service);
335         }
336
337         /*
338          * The prefix delegation is started after have got the uplink
339          * connection up i.e., when the default service is setup in which
340          * case the update_default_interface() will be called.
341          */
342         return -EINPROGRESS;
343 }
344
345 void __connman_ipv6pd_cleanup(void)
346 {
347         int ifindex;
348
349         if (!connman_inet_is_ipv6_supported())
350                 return;
351
352         connman_notifier_unregister(&pd_notifier);
353
354         __connman_inet_ipv6_stop_recv_rs(rs_context);
355         rs_context = NULL;
356
357         cleanup();
358
359         stop_ra(bridge_index);
360
361         if (default_interface) {
362                 ifindex = connman_inet_ifindex(default_interface);
363                 __connman_dhcpv6_stop_pd(ifindex);
364                 g_free(default_interface);
365                 default_interface = NULL;
366         }
367
368         bridge_index = -1;
369 }