5 * Copyright (C) 2013 Intel Corporation. All rights reserved.
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.
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.
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
27 #include <sys/types.h>
34 #include <gdhcp/gdhcp.h>
36 #define DEFAULT_ROUTER_LIFETIME 180 /* secs */
37 #define DEFAULT_RA_INTERVAL 120 /* secs */
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;
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);
51 static int enable_ipv6_forward(bool enable)
55 f = fopen("/proc/sys/net/ipv6/ip_forward", "r+");
69 static gboolean send_ra(gpointer data)
71 __connman_inet_ipv6_send_ra(bridge_index, NULL, prefixes,
72 DEFAULT_ROUTER_LIFETIME);
77 static void start_ra(int ifindex, GSList *prefix)
80 g_slist_free_full(prefixes, g_free);
82 prefixes = g_dhcpv6_copy_prefixes(prefix);
84 enable_ipv6_forward(true);
87 g_source_remove(timer_ra);
91 timer_ra = g_timeout_add_seconds(DEFAULT_RA_INTERVAL, send_ra, NULL);
94 static void stop_ra(int ifindex)
96 __connman_inet_ipv6_send_ra(ifindex, NULL, prefixes, 0);
99 g_source_remove(timer_ra);
103 enable_ipv6_forward(false);
106 g_slist_free_full(prefixes, g_free);
111 static void rs_received(struct nd_router_solicit *reply,
112 unsigned int length, void *user_data)
114 GDHCPIAPrefix *prefix;
122 for (list = prefixes; list; list = list->next) {
125 prefix->valid -= time(NULL) - prefix->expire;
126 prefix->preferred -= time(NULL) - prefix->expire;
129 __connman_inet_ipv6_send_ra(bridge_index, NULL, prefixes,
130 DEFAULT_ROUTER_LIFETIME);
133 static gboolean do_setup(gpointer data)
139 if (!default_interface)
140 DBG("No uplink connection, retrying prefix delegation");
142 ret = setup_prefix_delegation(__connman_service_get_default());
143 if (ret < 0 && ret != -EINPROGRESS)
144 return TRUE; /* delegation error, try again */
149 static void dhcpv6_renew_callback(struct connman_network *network,
150 enum __connman_dhcpv6_status status,
153 DBG("network %p status %d data %p", network, status, data);
155 if (status == CONNMAN_DHCPV6_STATUS_SUCCEED)
156 dhcpv6_callback(network, status, data);
158 setup_prefix_delegation(__connman_service_get_default());
161 static void cleanup(void)
163 if (timer_uplink != 0) {
164 g_source_remove(timer_uplink);
169 g_hash_table_destroy(timer_hash);
174 g_slist_free_full(prefixes, g_free);
179 static void dhcpv6_callback(struct connman_network *network,
180 enum __connman_dhcpv6_status status, gpointer data)
182 GSList *prefix_list = data;
184 DBG("network %p status %d data %p", network, status, data);
186 if (status == CONNMAN_DHCPV6_STATUS_FAIL) {
187 DBG("Prefix delegation request failed");
193 DBG("No prefixes, retrying");
194 if (timer_uplink == 0)
195 timer_uplink = g_timeout_add_seconds(10, do_setup,
201 * After we have got a list of prefixes, we can start to send router
202 * advertisements (RA) to tethering interface.
204 start_ra(bridge_index, prefix_list);
206 if (__connman_dhcpv6_start_pd_renew(network,
207 dhcpv6_renew_callback) == -ETIMEDOUT)
208 dhcpv6_renew_callback(network, CONNMAN_DHCPV6_STATUS_FAIL,
212 static int setup_prefix_delegation(struct connman_service *service)
214 struct connman_ipconfig *ipconfig;
216 int err = 0, ifindex;
220 * We do not have uplink connection. We just wait until
221 * default interface is updated.
226 interface = connman_service_get_interface(service);
228 DBG("interface %s bridge_index %d", interface, bridge_index);
230 if (default_interface) {
231 stop_ra(bridge_index);
233 ifindex = connman_inet_ifindex(default_interface);
234 __connman_dhcpv6_stop_pd(ifindex);
237 g_free(default_interface);
239 ipconfig = __connman_service_get_ip6config(service);
240 if (!__connman_ipconfig_ipv6_is_enabled(ipconfig)) {
242 default_interface = NULL;
243 return -EPFNOSUPPORT;
246 default_interface = interface;
248 if (default_interface) {
249 ifindex = connman_inet_ifindex(default_interface);
252 * Try to get a IPv6 prefix so we can start to advertise it.
254 err = __connman_dhcpv6_start_pd(ifindex, prefixes,
257 DBG("prefix delegation %d/%s", err, strerror(-err));
263 static void cleanup_timer(gpointer user_data)
265 guint timer = GPOINTER_TO_UINT(user_data);
267 g_source_remove(timer);
270 static void update_default_interface(struct connman_service *service)
272 setup_prefix_delegation(service);
275 static void update_ipconfig(struct connman_service *service,
276 struct connman_ipconfig *ipconfig)
278 if (!service || service != __connman_service_get_default())
281 if (ipconfig != __connman_service_get_ip6config(service))
284 if (!__connman_ipconfig_ipv6_is_enabled(ipconfig)) {
285 if (default_interface) {
288 ifindex = connman_inet_ifindex(default_interface);
289 __connman_dhcpv6_stop_pd(ifindex);
291 g_free(default_interface);
292 default_interface = NULL;
295 DBG("No IPv6 support for interface index %d",
296 __connman_ipconfig_get_index(ipconfig));
301 * Did we had PD activated already? If not, then start it.
303 if (!default_interface) {
304 DBG("IPv6 ipconfig %p changed for interface index %d", ipconfig,
305 __connman_ipconfig_get_index(ipconfig));
307 setup_prefix_delegation(service);
311 static struct connman_notifier pd_notifier = {
312 .name = "IPv6 prefix delegation",
313 .default_changed = update_default_interface,
314 .ipconfig_changed = update_ipconfig,
317 int __connman_ipv6pd_setup(const char *bridge)
319 struct connman_service *service;
322 if (!connman_inet_is_ipv6_supported())
323 return -EPFNOSUPPORT;
325 if (bridge_index >= 0) {
326 DBG("Prefix delegation already running");
330 err = connman_notifier_register(&pd_notifier);
334 bridge_index = connman_inet_ifindex(bridge);
336 timer_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
337 NULL, cleanup_timer);
339 err = __connman_inet_ipv6_start_recv_rs(bridge_index, rs_received,
342 DBG("Cannot receive router solicitation %d/%s",
343 err, strerror(-err));
345 service = __connman_service_get_default();
348 * We have an uplink connection already, try to use it.
350 return setup_prefix_delegation(service);
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.
361 void __connman_ipv6pd_cleanup(void)
365 if (!connman_inet_is_ipv6_supported())
368 connman_notifier_unregister(&pd_notifier);
370 __connman_inet_ipv6_stop_recv_rs(rs_context);
375 stop_ra(bridge_index);
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;