/*
* IPv6-specific functions for netcfg.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "netcfg.h"
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <debian-installer.h>
/* Obsessively watch the network configuration for the given interface,
* waiting for it to be properly configured. If the +interface+ struct has
* an address configured, we'll wait until that address is available,
* otherwise we're just interested in any global address.
*/
void nc_v6_wait_for_complete_configuration(const struct netcfg_interface *interface)
{
while (!nc_v6_interface_configured(interface, 0)) {
usleep(250000);
}
}
/* Obsessively watch the network configuration for the given interface,
* waiting for it to have completed configuration of a link-local address.
*/
void nc_v6_wait_for_link_local(const struct netcfg_interface *interface)
{
while (!nc_v6_interface_configured(interface, 1)) {
usleep(250000);
}
}
/* Inspect the live configuration of the given interface, and return a boolean
* indicating whether it's configuration is complete. "Complete" is defined
* as having an IPv6 address properly assigned (ie not "tentative"). If
* +link_local+ is true, then we'll be satisfied with a link-scope address;
* if +link_local+ is false, then we require a global scope address.
*
* If an IP address is specified in the +interface+ struct, then we require that
* address to have been configured.
*/
int nc_v6_interface_configured(const struct netcfg_interface *interface, const int link_local)
{
FILE *cmdfd;
char l[256];
char cmd[512];
int address_found = 0;
di_debug("nc_v6_interface_configured(%s, scope %s)", interface->name, link_local ? "local" : "global");
#if defined(__FreeBSD_kernel__)
snprintf(cmd, 512, "ifconfig %s", interface->name);
#else
snprintf(cmd, 512, "ip addr show %s", interface->name);
#endif
di_debug("Running %s to look for address", cmd);
if ((cmdfd = popen(cmd, "r")) != NULL) {
while (fgets(l, 256, cmdfd) != NULL) {
di_debug("ip line: %s", l);
/* Aah, string manipulation in C. What fun. */
#if defined(__FreeBSD_kernel__)
if (strncmp("\tinet6 ", l, 7)) {
continue;
}
/* An address with a scopeid isn't a global
* address, apparently
*/
if (!link_local && strstr(l, " scopeid")) {
continue;
}
#else
if (strncmp(" inet6 ", l, 10)) {
continue;
}
if (!link_local && !strstr(l, " scope global")) {
continue;
}
#endif
if (!empty_str(interface->ipaddress)) {
if (!strstr(l, interface->ipaddress)) {
continue;
}
}
if (strstr(l, " tentative")) {
continue;
}
/* The address is in the interface and not tentative.
* Good enough for me.
*/
di_debug("Configured address found");
address_found = 1;
}
pclose(cmdfd);
}
return address_found;
}
/* Discover if the ManageConfig and/or OtherConfig flags are set in the RAs
* that we're receiving.
*
* Calls out to rdisc6 to get the data we're looking for. If we get a good
* response out of rdisc6, we set the v6_stateful_config and v6_stateless_config
* flags to true/false (1/0) as appropriate and return true. If ndisc6
* doesn't give us anything, we set the flags to unknown (-1) and return
* false.
*
* We call out to rdisc6 multiple times, as it seems like it can take a little
* while for radvd to get into gear. Yay for progress bars.
*/
int nc_v6_get_config_flags(struct debconfclient *client, struct netcfg_interface *interface)
{
FILE *cmdfd;
char l[512], cmd[512];
const int RDISC6_TRIES = 12;
int count, ll_ok = 0;
/* First things first... we need to have a link-local address before
* we can send/receive RAs... and those can take some time to
* appear due to DAD
*/
debconf_capb(client, "progresscancel");
debconf_progress_start(client, 0, RDISC6_TRIES, "netcfg/ipv6_link_local_wait_title");
for (count = 0; count < RDISC6_TRIES; count++) {
usleep(250000);
if (debconf_progress_step(client, 1) == 30) {
/* User cancel */
break;
}
if (nc_v6_interface_configured(interface, 1)) {
/* We got a useful response */
debconf_progress_set(client, RDISC6_TRIES);
ll_ok = 1;
break;
}
}
debconf_progress_stop(client);
if (!ll_ok) {
di_info("No IPv6 support found... how does that happen?");
return 0;
}
snprintf(cmd, sizeof(cmd), "rdisc6 -1 -r 1 -w 500 -n %s", interface->name);
di_debug("Running %s to get IPv6 config flags", cmd);
interface->v6_stateful_config = -1;
interface->v6_stateless_config = -1;
debconf_capb(client, "progresscancel");
debconf_progress_start(client, 0, RDISC6_TRIES, "netcfg/ipv6_config_flags_wait_title");
for (count = 0; count < RDISC6_TRIES; count++) {
if ((cmdfd = popen(cmd, "r")) != NULL) {
while (fgets(l, sizeof(l), cmdfd) != NULL) {
di_debug("rdisc6 line: %s", l);
if (strncmp("Stateful address conf", l, 21) == 0) {
di_debug("Got stateful address line");
/* stateful_config flag */
if (strstr(l, " No")) {
di_debug("stateful=0");
interface->v6_stateful_config = 0;
} else if (strstr(l, " Yes")) {
di_debug("stateful=1");
interface->v6_stateful_config = 1;
}
} else if (strncmp("Stateful other conf", l, 19) == 0) {
/* other_config flag */
if (strstr(l, " No")) {
di_debug("stateless=0");
interface->v6_stateless_config = 0;
} else if (strstr(l, " Yes")) {
di_debug("stateless=1");
interface->v6_stateless_config = 1;
}
}
}
di_debug("rdisc6 parsing finished");
pclose(cmdfd);
}
if (debconf_progress_step(client, 1) == 30) {
/* User cancel */
break;
}
if (interface->v6_stateful_config != -1 &&
interface->v6_stateless_config != -1) {
/* We got a useful response */
debconf_progress_set(client, RDISC6_TRIES);
break;
}
}
/* In theory managed and other are independent of each other. In
* practise both being present means that addresses and configuration
* are available via DHCPv6. Hence set stateless_config to 0.
* Otherwise the autoconfiguration logic will only spawn a stateless
* client.
*/
if (interface->v6_stateful_config == 1 &&
interface->v6_stateless_config == 1) {
interface->v6_stateless_config = 0;
}
debconf_progress_stop(client);
if (interface->v6_stateful_config != -1 &&
interface->v6_stateless_config != -1) {
return 1;
} else {
return 0;
}
}