File: ipv6.c

package info (click to toggle)
netcfg 1.160
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 3,532 kB
  • sloc: ansic: 5,361; sh: 183; makefile: 77
file content (236 lines) | stat: -rw-r--r-- 7,118 bytes parent folder | download | duplicates (9)
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;
	}
}