static int setup_prefix_delegation(struct connman_service *service) { struct connman_ipconfig *ipconfig; char *interface; int err = 0, ifindex; if (service == NULL) { /* * We do not have uplink connection. We just wait until * default interface is updated. */ return -EINPROGRESS; } interface = connman_service_get_interface(service); DBG("interface %s bridge_index %d", interface, bridge_index); if (default_interface != NULL) { stop_ra(bridge_index); ifindex = connman_inet_ifindex(default_interface); __connman_dhcpv6_stop_pd(ifindex); } g_free(default_interface); ipconfig = __connman_service_get_ip6config(service); if (__connman_ipconfig_ipv6_is_enabled(ipconfig) == FALSE) { g_free(interface); default_interface = NULL; return -EPFNOSUPPORT; } default_interface = interface; if (default_interface != NULL) { ifindex = connman_inet_ifindex(default_interface); /* * Try to get a IPv6 prefix so we can start to advertise it. */ err = __connman_dhcpv6_start_pd(ifindex, prefixes, dhcpv6_callback); if (err < 0) DBG("prefix delegation %d/%s", err, strerror(-err)); } return err; }
static GDHCPServer *dhcp_server_start(const char *bridge, const char *router, const char* subnet, const char *start_ip, const char *end_ip, unsigned int lease_time, const char *dns) { GDHCPServerError error; GDHCPServer *dhcp_server; int index; DBG(""); index = connman_inet_ifindex(bridge); if (index < 0) return NULL; dhcp_server = g_dhcp_server_new(G_DHCP_IPV4, index, &error); if (dhcp_server == NULL) { dhcp_server_error(error); return NULL; } g_dhcp_server_set_debug(dhcp_server, dhcp_server_debug, "DHCP server"); g_dhcp_server_set_lease_time(dhcp_server, lease_time); g_dhcp_server_set_option(dhcp_server, G_DHCP_SUBNET, subnet); g_dhcp_server_set_option(dhcp_server, G_DHCP_ROUTER, router); g_dhcp_server_set_option(dhcp_server, G_DHCP_DNS_SERVER, dns); g_dhcp_server_set_ip_range(dhcp_server, start_ip, end_ip); g_dhcp_server_start(dhcp_server); return dhcp_server; }
static connman_bool_t pan_connect(struct bluetooth_pan *pan, const char *iface) { int index; if (iface == NULL) { if (proxy_get_bool(pan->btnetwork_proxy, "Connected") == FALSE) return FALSE; iface = proxy_get_string(pan->btnetwork_proxy, "Interface"); } if (iface == NULL) return FALSE; index = connman_inet_ifindex(iface); if (index < 0) { DBG("network %p invalid index %d", pan->network, index); return FALSE; } connman_network_set_index(pan->network, index); connman_network_set_connected(pan->network, TRUE); return TRUE; }
static void update_ipconfig(struct connman_service *service, struct connman_ipconfig *ipconfig) { if (service == NULL || service != __connman_service_get_default()) return; if (ipconfig != __connman_service_get_ip6config(service)) return; if (__connman_ipconfig_ipv6_is_enabled(ipconfig) == FALSE) { if (default_interface != NULL) { int ifindex; ifindex = connman_inet_ifindex(default_interface); __connman_dhcpv6_stop_pd(ifindex); g_free(default_interface); default_interface = NULL; } DBG("No IPv6 support for interface %s", __connman_ipconfig_get_ifname(ipconfig)); return; } /* * Did we had PD activated already? If not, then start it. */ if (default_interface == NULL) { DBG("IPv6 ipconfig %p changed for interface %s", ipconfig, __connman_ipconfig_get_ifname(ipconfig)); setup_prefix_delegation(service); } }
static gboolean resolver_refresh_cb(gpointer user_data) { struct entry_data *entry = user_data; int index; unsigned int interval; struct connman_service *service = NULL; /* Round up what we have left from lifetime */ interval = entry->lifetime * (1 - RESOLVER_LIFETIME_REFRESH_THRESHOLD) + 1.0; DBG("RDNSS start interface %s domain %s " "server %s remaining lifetime %d", entry->interface, entry->domain, entry->server, interval); entry->timeout = g_timeout_add_seconds(interval, resolver_expire_cb, entry); index = connman_inet_ifindex(entry->interface); if (index >= 0) { service = __connman_service_lookup_from_index(index); if (service != NULL) { /* * Send Router Solicitation to refresh RDNSS entries * before their lifetime expires */ __connman_refresh_rs_ipv6( __connman_service_get_network(service), index); } } return FALSE; }
void __connman_ipv6pd_cleanup(void) { int ifindex; if (connman_inet_is_ipv6_supported() == FALSE) return; connman_notifier_unregister(&pd_notifier); __connman_inet_ipv6_stop_recv_rs(rs_context); rs_context = NULL; cleanup(); stop_ra(bridge_index); if (default_interface != NULL) { ifindex = connman_inet_ifindex(default_interface); __connman_dhcpv6_stop_pd(ifindex); g_free(default_interface); default_interface = NULL; } bridge_index = -1; }
static void connect_reply(DBusPendingCall *call, void *user_data) { char *path = user_data; struct connman_network *network; DBusMessage *reply; DBusError error; const char *interface = NULL; int index; network = g_hash_table_lookup(bluetooth_networks, path); if (!network) return; DBG("network %p", network); reply = dbus_pending_call_steal_reply(call); dbus_error_init(&error); if (dbus_set_error_from_message(&error, reply)) { connman_error("%s", error.message); dbus_error_free(&error); goto err; } if (!dbus_message_get_args(reply, &error, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) { if (dbus_error_is_set(&error)) { connman_error("%s", error.message); dbus_error_free(&error); } else connman_error("Wrong arguments for connect"); goto err; } if (!interface) goto err; DBG("interface %s", interface); index = connman_inet_ifindex(interface); connman_network_set_index(network, index); connman_network_set_connected(network, true); dbus_message_unref(reply); dbus_pending_call_unref(call); return; err: connman_network_set_connected(network, false); dbus_message_unref(reply); dbus_pending_call_unref(call); }
void __connman_tethering_set_disabled(void) { int index; DBG("enabled %d", tethering_enabled - 1); __connman_ipv6pd_cleanup(); index = connman_inet_ifindex(BRIDGE_NAME); __connman_dnsproxy_remove_listener(index); if (__sync_fetch_and_sub(&tethering_enabled, 1) != 1) return; __connman_nat_disable(BRIDGE_NAME); dhcp_server_stop(tethering_dhcp_server); tethering_dhcp_server = NULL; __connman_bridge_disable(BRIDGE_NAME); __connman_ippool_unref(dhcp_ippool); __connman_bridge_remove(BRIDGE_NAME); g_free(private_network_primary_dns); private_network_primary_dns = NULL; g_free(private_network_secondary_dns); private_network_secondary_dns = NULL; DBG("tethering stopped"); }
static bool pan_connect(struct bluetooth_pan *pan, const char *iface) { int index; if (!iface) { if (!proxy_get_bool(pan->btnetwork_proxy, "Connected")) return false; iface = proxy_get_string(pan->btnetwork_proxy, "Interface"); } if (!iface) return false; index = connman_inet_ifindex(iface); if (index < 0) { DBG("network %p invalid index %d", pan->network, index); return false; } connman_network_set_index(pan->network, index); connman_network_set_connected(pan->network, true); return true; }
static int append_resolver(const char *interface, const char *domain, const char *server, unsigned int lifetime, unsigned int flags) { struct entry_data *entry; unsigned int interval; DBG("interface %s domain %s server %s lifetime %d flags %d", interface, domain, server, lifetime, flags); if (server == NULL && domain == NULL) return -EINVAL; entry = g_try_new0(struct entry_data, 1); if (entry == NULL) return -ENOMEM; entry->interface = g_strdup(interface); entry->domain = g_strdup(domain); entry->server = g_strdup(server); entry->flags = flags; entry->lifetime = lifetime; if (server != NULL) entry->family = connman_inet_check_ipaddress(server); if (lifetime) { int index; interval = lifetime * RESOLVER_LIFETIME_REFRESH_THRESHOLD; DBG("RDNSS start interface %s domain %s " "server %s lifetime threshold %d", interface, domain, server, interval); entry->timeout = g_timeout_add_seconds(interval, resolver_refresh_cb, entry); /* * We update the service only for those nameservers * that are automagically added via netlink (lifetime > 0) */ index = connman_inet_ifindex(interface); if (server != NULL && index >= 0) { struct connman_service *service; service = __connman_service_lookup_from_index(index); if (service != NULL) __connman_service_nameserver_append(service, server, TRUE); } } entry_list = g_slist_append(entry_list, entry); if (dnsproxy_enabled == TRUE) __connman_dnsproxy_append(interface, domain, server); else __connman_resolvfile_append(interface, domain, server); return 0; }
static void connect_reply(DBusPendingCall *call, void *user_data) { struct connman_network *network = user_data; DBusMessage *reply; DBusError error; const char *interface = NULL; int index; DBG("network %p", network); reply = dbus_pending_call_steal_reply(call); dbus_error_init(&error); if (dbus_set_error_from_message(&error, reply) == TRUE) { connman_error("%s", error.message); dbus_error_free(&error); goto err; } if (dbus_message_get_args(reply, &error, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID) == FALSE) { if (dbus_error_is_set(&error) == TRUE) { connman_error("%s", error.message); dbus_error_free(&error); } else connman_error("Wrong arguments for connect"); goto err; } if (interface == NULL) goto err; DBG("interface %s", interface); index = connman_inet_ifindex(interface); connman_network_set_index(network, index); connman_network_set_connected(network, TRUE); dbus_message_unref(reply); dbus_pending_call_unref(call); return; err: connman_network_set_connected(network, FALSE); dbus_message_unref(reply); dbus_pending_call_unref(call); }
int __connman_bridge_disable(const char *name) { int index; index = connman_inet_ifindex(name); if (index < 0) return index; return connman_inet_ifdown(index); }
static int enable_bridge(const char *name) { int err, index; index = connman_inet_ifindex(name); if (index < 0) return index; err = __connman_inet_modify_address(RTM_NEWADDR, NLM_F_REPLACE | NLM_F_ACK, index, AF_INET, BRIDGE_IP, NULL, 24, BRIDGE_BCAST); if (err < 0) return err; return connman_inet_ifup(index); }
static int append_resolver(const char *interface, const char *domain, const char *server, unsigned int lifetime, unsigned int flags) { struct entry_data *entry; DBG("interface %s domain %s server %s lifetime %d flags %d", interface, domain, server, lifetime, flags); if (server == NULL && domain == NULL) return -EINVAL; entry = g_try_new0(struct entry_data, 1); if (entry == NULL) return -ENOMEM; entry->interface = g_strdup(interface); entry->domain = g_strdup(domain); entry->server = g_strdup(server); entry->flags = flags; if (lifetime) { int index; entry->timeout = g_timeout_add_seconds(lifetime, resolver_expire_cb, entry); /* * We update the service only for those nameservers * that are automagically added via netlink (lifetime > 0) */ index = connman_inet_ifindex(interface); if (index >= 0) { struct connman_service *service; service = __connman_service_lookup_from_index(index); if (service != NULL) __connman_service_nameserver_append(service, server, TRUE); } } entry_list = g_slist_append(entry_list, entry); if (dnsproxy_enabled == TRUE) __connman_dnsproxy_append(interface, domain, server); else __connman_resolvfile_append(interface, domain, server); return 0; }
static void cleanup_devices(void) { /* * Check what interfaces are currently up and if connman is * suppose to handle the interface, then cleanup the mess * related to that interface. There might be weird routes etc * that are related to that interface and that might confuse * connmand. So in this case we just turn the interface down * so that kernel removes routes/addresses automatically and * then proceed the startup. * * Note that this cleanup must be done before rtnl/detect code * has activated interface watches. */ char **interfaces; int i; interfaces = __connman_inet_get_running_interfaces(); if (interfaces == NULL) return; for (i = 0; interfaces[i] != NULL; i++) { connman_bool_t filtered; int index; filtered = __connman_device_isfiltered(interfaces[i]); if (filtered == TRUE) continue; index = connman_inet_ifindex(interfaces[i]); if (index < 0) continue; DBG("cleaning up %s index %d", interfaces[i], index); connman_inet_ifdown(index); /* * ConnMan will turn the interface UP automatically so * no need to do it here. */ } g_strfreev(interfaces); }
int __connman_bridge_enable(const char *name, const char *gateway, const char *broadcast) { int err, index; index = connman_inet_ifindex(name); if (index < 0) return index; err = __connman_inet_modify_address(RTM_NEWADDR, NLM_F_REPLACE | NLM_F_ACK, index, AF_INET, gateway, NULL, 24, broadcast); if (err < 0) return err; return connman_inet_ifup(index); }
int __connman_ipv6pd_setup(const char *bridge) { struct connman_service *service; int err; if (connman_inet_is_ipv6_supported() == FALSE) return -EPFNOSUPPORT; if (bridge_index >= 0) { DBG("Prefix delegation already running"); return -EALREADY; } err = connman_notifier_register(&pd_notifier); if (err < 0) return err; bridge_index = connman_inet_ifindex(bridge); timer_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, cleanup_timer); err = __connman_inet_ipv6_start_recv_rs(bridge_index, rs_received, NULL, &rs_context); if (err < 0) DBG("Cannot receive router solicitation %d/%s", err, strerror(-err)); service = __connman_service_get_default(); if (service != NULL) { /* * We have an uplink connection already, try to use it. */ return setup_prefix_delegation(service); } /* * The prefix delegation is started after have got the uplink * connection up i.e., when the default service is setup in which * case the update_default_interface() will be called. */ return -EINPROGRESS; }
int vpn_set_ifname(struct connman_provider *provider, const char *ifname) { struct vpn_data *data = connman_provider_get_data(provider); int index; if (ifname == NULL || data == NULL) return -EIO; index = connman_inet_ifindex(ifname); if (index < 0) return -EIO; if (data->if_name != NULL) g_free(data->if_name); data->if_name = (char *)g_strdup(ifname); connman_provider_set_index(provider, index); return 0; }
static gboolean resolver_expire_cb(gpointer user_data) { struct entry_data *entry = user_data; GSList *list; int index; DBG("interface %s domain %s server %s", entry->interface, entry->domain, entry->server); list = g_slist_append(NULL, entry); index = connman_inet_ifindex(entry->interface); if (index >= 0) { struct connman_service *service; service = __connman_service_lookup_from_index(index); if (service != NULL) __connman_service_nameserver_remove(service, entry->server, TRUE); } remove_entries(list); return FALSE; }
static int vpn_create_tun(struct connman_provider *provider) { struct vpn_data *data = connman_provider_get_data(provider); struct ifreq ifr; int i, fd, index; int ret = 0; if (data == NULL) return -EISCONN; fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC); if (fd < 0) { i = -errno; connman_error("Failed to open /dev/net/tun: %s", strerror(errno)); ret = i; goto exist_err; } memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TUN | IFF_NO_PI; for (i = 0; i < 256; i++) { sprintf(ifr.ifr_name, "vpn%d", i); if (!ioctl(fd, TUNSETIFF, (void *)&ifr)) break; } if (i == 256) { connman_error("Failed to find available tun device"); close(fd); ret = -ENODEV; goto exist_err; } data->if_name = (char *)g_strdup(ifr.ifr_name); if (data->if_name == NULL) { connman_error("Failed to allocate memory"); close(fd); ret = -ENOMEM; goto exist_err; } if (ioctl(fd, TUNSETPERSIST, 1)) { i = -errno; connman_error("Failed to set tun persistent: %s", strerror(errno)); close(fd); ret = i; goto exist_err; } close(fd); index = connman_inet_ifindex(data->if_name); if (index < 0) { connman_error("Failed to get tun ifindex"); stop_vpn(provider); ret = -EIO; goto exist_err; } connman_provider_set_index(provider, index); return 0; exist_err: return ret; }
static int wispr_portal_detect(struct connman_wispr_portal_context *wp_context) { enum connman_service_proxy_method proxy_method; enum connman_service_type service_type; char *interface = NULL; char **nameservers = NULL; int if_index; int err = 0; int i; DBG("wispr/portal context %p service %p", wp_context, wp_context->service); service_type = connman_service_get_type(wp_context->service); switch (service_type) { case CONNMAN_SERVICE_TYPE_ETHERNET: case CONNMAN_SERVICE_TYPE_WIFI: case CONNMAN_SERVICE_TYPE_BLUETOOTH: case CONNMAN_SERVICE_TYPE_CELLULAR: case CONNMAN_SERVICE_TYPE_GADGET: break; case CONNMAN_SERVICE_TYPE_UNKNOWN: case CONNMAN_SERVICE_TYPE_SYSTEM: case CONNMAN_SERVICE_TYPE_GPS: case CONNMAN_SERVICE_TYPE_VPN: case CONNMAN_SERVICE_TYPE_P2P: return -EOPNOTSUPP; } interface = connman_service_get_interface(wp_context->service); if (!interface) return -EINVAL; DBG("interface %s", interface); if_index = connman_inet_ifindex(interface); if (if_index < 0) { DBG("Could not get ifindex"); err = -EINVAL; goto done; } nameservers = connman_service_get_nameservers(wp_context->service); if (!nameservers) { DBG("Could not get nameservers"); err = -EINVAL; goto done; } wp_context->web = g_web_new(if_index); if (!wp_context->web) { DBG("Could not set up GWeb"); err = -ENOMEM; goto done; } if (getenv("CONNMAN_WEB_DEBUG")) g_web_set_debug(wp_context->web, web_debug, "WEB"); if (wp_context->type == CONNMAN_IPCONFIG_TYPE_IPV4) { g_web_set_address_family(wp_context->web, AF_INET); wp_context->status_url = STATUS_URL_IPV4; } else { g_web_set_address_family(wp_context->web, AF_INET6); wp_context->status_url = STATUS_URL_IPV6; } for (i = 0; nameservers[i]; i++) g_web_add_nameserver(wp_context->web, nameservers[i]); proxy_method = connman_service_get_proxy_method(wp_context->service); if (proxy_method != CONNMAN_SERVICE_PROXY_METHOD_DIRECT) { wp_context->token = connman_proxy_lookup(interface, wp_context->status_url, wp_context->service, proxy_callback, wp_context); if (wp_context->token == 0) { err = -EINVAL; free_connman_wispr_portal_context(wp_context); } } else if (wp_context->timeout == 0) { wp_context->timeout = g_idle_add(no_proxy_callback, wp_context); } done: g_strfreev(nameservers); g_free(interface); return err; }
int __connman_private_network_request(DBusMessage *msg, const char *owner) { struct connman_private_network *pn; char *iface = NULL; char *path = NULL; int index, fd, err; if (DBUS_TYPE_UNIX_FD < 0) return -EINVAL; fd = connman_inet_create_tunnel(&iface); if (fd < 0) return fd; path = g_strdup_printf("/tethering/%s", iface); pn = g_hash_table_lookup(pn_hash, path); if (pn) { g_free(path); g_free(iface); close(fd); return -EEXIST; } index = connman_inet_ifindex(iface); if (index < 0) { err = -ENODEV; goto error; } DBG("interface %s", iface); err = connman_inet_set_mtu(index, DEFAULT_MTU); pn = g_try_new0(struct connman_private_network, 1); if (pn == NULL) { err = -ENOMEM; goto error; } pn->owner = g_strdup(owner); pn->path = path; pn->watch = g_dbus_add_disconnect_watch(connection, pn->owner, owner_disconnect, pn, NULL); pn->msg = msg; pn->reply = dbus_message_new_method_return(pn->msg); if (pn->reply == NULL) goto error; pn->fd = fd; pn->interface = iface; pn->index = index; pn->server_ip = PRIVATE_NETWORK_IP; pn->peer_ip = PRIVATE_NETWORK_PEER_IP; pn->primary_dns = PRIVATE_NETWORK_PRIMARY_DNS; pn->secondary_dns = PRIVATE_NETWORK_SECONDARY_DNS; pn->iface_watch = connman_rtnl_add_newlink_watch(index, setup_tun_interface, pn); g_hash_table_insert(pn_hash, pn->path, pn); return 0; error: close(fd); g_free(iface); g_free(path); g_free(pn); return err; }
static void cleanup_devices(void) { /* * Check what interfaces are currently up and if connman is * suppose to handle the interface, then cleanup the mess * related to that interface. There might be weird routes etc * that are related to that interface and that might confuse * connmand. So in this case we just turn the interface down * so that kernel removes routes/addresses automatically and * then proceed the startup. * * Note that this cleanup must be done before rtnl/detect code * has activated interface watches. */ char **interfaces; int i; interfaces = __connman_inet_get_running_interfaces(); if (!interfaces) return; for (i = 0; interfaces[i]; i++) { bool filtered; int index; struct sockaddr_in sin_addr, sin_mask; filtered = __connman_device_isfiltered(interfaces[i]); if (filtered) continue; index = connman_inet_ifindex(interfaces[i]); if (index < 0) continue; if (!__connman_inet_get_address_netmask(index, &sin_addr, &sin_mask)) { char *address = g_strdup(inet_ntoa(sin_addr.sin_addr)); char *netmask = g_strdup(inet_ntoa(sin_mask.sin_addr)); if (__connman_config_address_provisioned(address, netmask)) { DBG("Skip %s which is already provisioned " "with %s/%s", interfaces[i], address, netmask); g_free(address); g_free(netmask); continue; } g_free(address); g_free(netmask); } DBG("cleaning up %s index %d", interfaces[i], index); connman_inet_ifdown(index); /* * ConnMan will turn the interface UP automatically so * no need to do it here. */ } g_strfreev(interfaces); }
void __connman_tethering_set_enabled(void) { int index; int err; const char *gateway; const char *broadcast; const char *subnet_mask; const char *start_ip; const char *end_ip; const char *dns; unsigned char prefixlen; char **ns; DBG("enabled %d", tethering_enabled + 1); if (__sync_fetch_and_add(&tethering_enabled, 1) != 0) return; err = __connman_bridge_create(BRIDGE_NAME); if (err < 0) { __sync_fetch_and_sub(&tethering_enabled, 1); return; } index = connman_inet_ifindex(BRIDGE_NAME); dhcp_ippool = __connman_ippool_create(index, 2, 252, tethering_restart, NULL); if (dhcp_ippool == NULL) { connman_error("Fail to create IP pool"); __connman_bridge_remove(BRIDGE_NAME); __sync_fetch_and_sub(&tethering_enabled, 1); return; } gateway = __connman_ippool_get_gateway(dhcp_ippool); broadcast = __connman_ippool_get_broadcast(dhcp_ippool); subnet_mask = __connman_ippool_get_subnet_mask(dhcp_ippool); start_ip = __connman_ippool_get_start_ip(dhcp_ippool); end_ip = __connman_ippool_get_end_ip(dhcp_ippool); err = __connman_bridge_enable(BRIDGE_NAME, gateway, __connman_ipaddress_netmask_prefix_len(subnet_mask), broadcast); if (err < 0 && err != -EALREADY) { __connman_ippool_unref(dhcp_ippool); __connman_bridge_remove(BRIDGE_NAME); __sync_fetch_and_sub(&tethering_enabled, 1); return; } ns = connman_setting_get_string_list("FallbackNameservers"); if (ns != NULL) { if (ns[0] != NULL) { g_free(private_network_primary_dns); private_network_primary_dns = g_strdup(ns[0]); } if (ns[1] != NULL) { g_free(private_network_secondary_dns); private_network_secondary_dns = g_strdup(ns[1]); } DBG("Fallback ns primary %s secondary %s", private_network_primary_dns, private_network_secondary_dns); } dns = gateway; if (__connman_dnsproxy_add_listener(index) < 0) { connman_error("Can't add listener %s to DNS proxy", BRIDGE_NAME); dns = private_network_primary_dns; DBG("Serving %s nameserver to clients", dns); } tethering_dhcp_server = dhcp_server_start(BRIDGE_NAME, gateway, subnet_mask, start_ip, end_ip, 24 * 3600, dns); if (tethering_dhcp_server == NULL) { __connman_bridge_disable(BRIDGE_NAME); __connman_ippool_unref(dhcp_ippool); __connman_bridge_remove(BRIDGE_NAME); __sync_fetch_and_sub(&tethering_enabled, 1); return; } prefixlen = __connman_ipaddress_netmask_prefix_len(subnet_mask); __connman_nat_enable(BRIDGE_NAME, start_ip, prefixlen); err = __connman_ipv6pd_setup(BRIDGE_NAME); if (err < 0 && err != -EINPROGRESS) DBG("Cannot setup IPv6 prefix delegation %d/%s", err, strerror(-err)); DBG("tethering started"); }