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