1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
|
/*
* 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;
}
}
|