You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

autoconfig.c 15 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. /*
  2. * Interface autoconfiguration functions for netcfg.
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  17. */
  18. #include "netcfg.h"
  19. #include <errno.h>
  20. #include <string.h>
  21. #include <stdlib.h>
  22. #include <sys/types.h>
  23. #include <sys/stat.h>
  24. #include <unistd.h>
  25. #include <ifaddrs.h>
  26. #include <net/if.h>
  27. #include <debian-installer.h>
  28. #define DHCP6C_PIDFILE "/var/run/dhcp6c.pid"
  29. #define DHCP6C_FINISHED "/var/lib/netcfg/dhcp6c-finished"
  30. static enum { DHCLIENT, DHCP6C } dhcpv6_client;
  31. static int dhcpv6_pipe[2] = { -1, -1 };
  32. static pid_t dhcpv6_pid = -1;
  33. static int dhcpv6_exit_status = 1;
  34. /* Signal handler for DHCPv6 client child
  35. *
  36. * When the child exits (either because it failed to obtain a
  37. * lease or because it succeeded and daemonized itself), this
  38. * gets the child's exit status and sets dhcpv6_pid to -1
  39. */
  40. void cleanup_dhcpv6_client(void)
  41. {
  42. if (dhcpv6_pid <= 0)
  43. /* Already cleaned up */
  44. return;
  45. if (waitpid(dhcpv6_pid, &dhcpv6_exit_status, WNOHANG) != dhcpv6_pid)
  46. /* Wasn't us */
  47. return;
  48. if (WIFEXITED(dhcpv6_exit_status) || WIFSIGNALED(dhcpv6_exit_status))
  49. dhcpv6_pid = -1;
  50. }
  51. /* Start whichever DHCPv6 client is available.
  52. *
  53. * The client's PID is stored in dhcpv6_pid.
  54. */
  55. int start_dhcpv6_client(struct debconfclient *client, const struct netcfg_interface *interface)
  56. {
  57. if (access("/sbin/dhclient", F_OK) == 0)
  58. dhcpv6_client = DHCLIENT;
  59. else if (access("/usr/sbin/dhcp6c", F_OK) == 0)
  60. dhcpv6_client = DHCP6C;
  61. else {
  62. /* for now, fall back quietly (no debconf error) to IPv4 */
  63. di_error("No DHCPv6 client found");
  64. return 1;
  65. }
  66. if (pipe(dhcpv6_pipe) < 0) {
  67. di_error("Unable to create pipe: %s", strerror(errno));
  68. return 1;
  69. }
  70. if (dhcpv6_client == DHCP6C) {
  71. unlink(DHCP6C_FINISHED);
  72. if (!interface->v6_stateless_config) {
  73. /* So that poll_dhcpv6_client won't immediately give
  74. * up:
  75. */
  76. FILE *pidfile = fopen(DHCP6C_PIDFILE, "w");
  77. if (pidfile)
  78. fclose(pidfile);
  79. }
  80. }
  81. dhcpv6_pid = fork();
  82. if (dhcpv6_pid == 0) { /* child */
  83. FILE *dc;
  84. const char **arguments;
  85. int i = 0;
  86. /* disassociate from debconf */
  87. fclose(client->out);
  88. close(dhcpv6_pipe[0]);
  89. dup2(dhcpv6_pipe[1], 1);
  90. close(dhcpv6_pipe[1]);
  91. switch (dhcpv6_client) {
  92. case DHCLIENT:
  93. dc = file_open(DHCLIENT6_FILE, "w");
  94. if (!dc)
  95. return 1;
  96. fprintf(dc, "send vendor-class-identifier \"d-i\";\n");
  97. fclose(dc);
  98. arguments = malloc(9 * sizeof(*arguments));
  99. i = 0;
  100. arguments[i++] = "dhclient";
  101. arguments[i++] = "-6";
  102. if (interface->v6_stateless_config)
  103. arguments[i++] = "-S";
  104. arguments[i++] = "-cf";
  105. arguments[i++] = DHCLIENT6_FILE;
  106. arguments[i++] = "-sf";
  107. arguments[i++] = "/lib/netcfg/print-dhcpv6-info";
  108. arguments[i++] = interface->name;
  109. arguments[i] = NULL;
  110. execvp("dhclient", (char **)arguments);
  111. break;
  112. case DHCP6C:
  113. if (!interface->v6_stateless_config) {
  114. /* In stateful mode, dhcp6c needs to stay
  115. * running. However, it daemonises itself
  116. * in such a way as to throw away stdio,
  117. * which interferes with our script
  118. * communication. To work around this,
  119. * daemonise here without throwing away
  120. * stdio and then run dhcp6c in foreground
  121. * mode.
  122. */
  123. if (daemon(0, 1) < 0) {
  124. di_error("daemon() failed: %s", strerror(errno));
  125. _exit(1);
  126. }
  127. }
  128. dc = file_open(DHCP6C_FILE, "w");
  129. if (!dc)
  130. return 1;
  131. fprintf(dc, "interface %s {\n", interface->name);
  132. if (interface->v6_stateless_config)
  133. fprintf(dc, "\tinformation-only;\n");
  134. else
  135. fprintf(dc, "\tsend ia-na 0;\n");
  136. fprintf(dc, "\trequest domain-name-servers;\n");
  137. fprintf(dc, "\trequest domain-name;\n");
  138. fprintf(dc, "\tscript \"/lib/netcfg/print-dhcp6c-info\";\n");
  139. fprintf(dc, "};\n");
  140. if (!interface->v6_stateless_config) {
  141. fprintf(dc, "id-assoc na 0 {\n");
  142. fprintf(dc, "};\n");
  143. }
  144. fclose(dc);
  145. arguments = malloc(6 * sizeof(*arguments));
  146. arguments[i++] = "dhcp6c";
  147. arguments[i++] = "-c";
  148. arguments[i++] = DHCP6C_FILE;
  149. arguments[i++] = "-f";
  150. arguments[i++] = interface->name;
  151. arguments[i] = NULL;
  152. execvp("dhcp6c", (char **)arguments);
  153. break;
  154. }
  155. if (errno)
  156. di_error("Could not exec DHCPv6 client: %s", strerror(errno));
  157. _exit(1); /* should never be reached */
  158. } else if (dhcpv6_pid == -1) {
  159. di_warning("DHCPv6 fork failed; this is unlikely to end well");
  160. close(dhcpv6_pipe[0]);
  161. close(dhcpv6_pipe[1]);
  162. dhcpv6_pipe[0] = dhcpv6_pipe[1] = -1;
  163. return 1;
  164. } else { /* parent */
  165. di_warning("Started DHCPv6 client; PID is %i", dhcpv6_pid);
  166. close(dhcpv6_pipe[1]);
  167. dhcpv6_pipe[1] = -1;
  168. signal(SIGCHLD, &sigchld_handler);
  169. return 0;
  170. }
  171. }
  172. /* Poll the started DHCP client for netcfg/dhcpv6_timeout seconds (def. 15)
  173. * and return true if a lease is known to have been acquired, 0 otherwise.
  174. *
  175. * The client should be run such that it exits once a lease is acquired
  176. * (although its child continues to run as a daemon). Unfortunately, dhcp6c
  177. * can only daemonise immediately rather than waiting for a lease, so we
  178. * have to handle this for the stateful case.
  179. *
  180. * This function will NOT kill the child if time runs out. This allows
  181. * the user to choose to wait longer for the lease to be acquired.
  182. */
  183. static int poll_dhcpv6_client (struct debconfclient *client, const struct netcfg_interface *interface)
  184. {
  185. int seconds_slept = 0;
  186. int dhcpv6_seconds;
  187. int got_lease = -1;
  188. int ret = 0;
  189. debconf_get(client, "netcfg/dhcpv6_timeout");
  190. dhcpv6_seconds = atoi(client->value);
  191. /* show progress bar */
  192. debconf_capb(client, "backup progresscancel");
  193. debconf_progress_start(client, 0, dhcpv6_seconds, "netcfg/dhcpv6_progress");
  194. if (debconf_progress_info(client, "netcfg/dhcp_progress_note") == 30)
  195. goto stop;
  196. for (;;) {
  197. struct stat st;
  198. switch (dhcpv6_client) {
  199. case DHCLIENT:
  200. if (dhcpv6_pid <= 0)
  201. got_lease = (dhcpv6_exit_status == 0);
  202. break;
  203. case DHCP6C:
  204. if (dhcpv6_pid <= 0 && dhcpv6_exit_status != 0) {
  205. got_lease = 0;
  206. break;
  207. }
  208. if (!interface->v6_stateless_config &&
  209. dhcpv6_pid <= 0) {
  210. /* In stateful mode, we run dhcp6c as a
  211. * daemon (because it doesn't wait for a
  212. * lease before daemonising anyway, and this
  213. * lets us set up its file descriptors
  214. * properly), so dhcpv6_pid will exit fairly
  215. * quickly. We can check its pid file to
  216. * find out whether it's really exited, in
  217. * which case we'll have lost the lease.
  218. */
  219. if (stat(DHCP6C_PIDFILE, &st) < 0) {
  220. got_lease = 0;
  221. break;
  222. }
  223. }
  224. /* We write a sentinel file at the end of
  225. * print-dhcp6c-info, which is a solid indication
  226. * that we got useful configuration.
  227. */
  228. if (stat(DHCP6C_FINISHED, &st) == 0) {
  229. got_lease = 1;
  230. break;
  231. }
  232. break;
  233. }
  234. if (got_lease != -1 || seconds_slept++ >= dhcpv6_seconds)
  235. break;
  236. sleep(1);
  237. if (debconf_progress_step(client, 1) == 30)
  238. goto stop;
  239. }
  240. if (got_lease == 1) {
  241. ret = 1;
  242. debconf_capb(client, "backup"); /* stop displaying cancel button */
  243. if (debconf_progress_set(client, dhcpv6_seconds) == 30)
  244. goto stop;
  245. if (debconf_progress_info(client, "netcfg/dhcp_success_note") == 30)
  246. goto stop;
  247. sleep(1);
  248. }
  249. stop:
  250. /* stop progress bar */
  251. debconf_progress_stop(client);
  252. debconf_capb(client, "backup");
  253. /* terminate dhcp6c if there's no lease or information yet */
  254. if (dhcpv6_client == DHCP6C && (got_lease != 1 || dhcpv6_pid > 0)) {
  255. FILE *pidfile = fopen(DHCP6C_PIDFILE, "r");
  256. if (pidfile) {
  257. char *line = NULL;
  258. size_t dummy;
  259. if (getline(&line, &dummy, pidfile) >= 0) {
  260. pid_t pid;
  261. errno = 0;
  262. pid = strtol(line, NULL, 10);
  263. if (errno == 0)
  264. kill(pid, SIGTERM);
  265. }
  266. free(line);
  267. fclose(pidfile);
  268. }
  269. }
  270. if (dhcpv6_client == DHCP6C)
  271. unlink(DHCP6C_FINISHED);
  272. return ret;
  273. }
  274. /* Configure the interface using DHCPv6. */
  275. static int netcfg_dhcpv6(struct debconfclient *client, struct netcfg_interface *interface)
  276. {
  277. FILE *dhcpv6_reader = NULL;
  278. char l[512], *p;
  279. int ns_idx, ntp_idx = 0;
  280. int rv;
  281. if (interface->v6_stateless_config)
  282. di_debug("Stateless DHCPv6 requested");
  283. else
  284. di_debug("Stateful DHCPv6 requested");
  285. /* Append any nameservers obtained via DHCP to the list of
  286. * nameservers in the RA, rather than overwriting them
  287. */
  288. ns_idx = nameserver_count(interface);
  289. if (start_dhcpv6_client(client, interface)) {
  290. di_warning("DHCPv6 client failed to start. Aborting DHCPv6 configuration.");
  291. return 0;
  292. }
  293. rv = poll_dhcpv6_client(client, interface);
  294. dhcpv6_reader = fdopen(dhcpv6_pipe[0], "r");
  295. while (fgets(l, sizeof(l), dhcpv6_reader) != NULL) {
  296. rtrim(l);
  297. di_debug("DHCPv6 line: %s", l);
  298. if (!strncmp("nameserver[", l, 11) && ns_idx < NETCFG_NAMESERVERS_MAX) {
  299. p = strstr(l, "] ") + 2;
  300. strncpy(interface->nameservers[ns_idx], p, sizeof(interface->nameservers[ns_idx])-1);
  301. interface->nameservers[ns_idx][sizeof(interface->nameservers[ns_idx])-1]='\0';
  302. ns_idx++;
  303. } else if (!strncmp("NTP server[", l, 11) && ntp_idx < NETCFG_NTPSERVERS_MAX) {
  304. p = strstr(l, "] ") + 2;
  305. strncpy(interface->ntp_servers[ntp_idx], p, sizeof(interface->ntp_servers[ntp_idx])-1);
  306. interface->ntp_servers[ntp_idx][sizeof(interface->ntp_servers[ntp_idx])-1]='\0';
  307. ntp_idx++;
  308. } else if (!strncmp("Domain search list[0] ", l, 21)) {
  309. p = strstr(l, "] ") + 2;
  310. strncpy(domain, p, sizeof(domain)-1);
  311. domain[sizeof(domain)-1]='\0';
  312. /* Strip trailing . */
  313. if (domain[strlen(domain)-1] == '.') {
  314. domain[strlen(domain)-1] = '\0';
  315. }
  316. have_domain = 1;
  317. } else if (!strcmp("end", l)) {
  318. /* The write end of the pipe won't necessarily be
  319. * closed in the stateful case, so this hack lets us
  320. * break out.
  321. */
  322. break;
  323. }
  324. }
  325. fclose(dhcpv6_reader);
  326. dhcpv6_pipe[0] = -1;
  327. if (dhcpv6_client == DHCP6C &&
  328. interface->v6_stateless_config && dhcpv6_pid > 0)
  329. /* dhcp6c doesn't exit after printing information unless in
  330. * info-req (-i) mode, which is incompatible with supplying
  331. * a configuration; so just kill the client now.
  332. */
  333. kill(dhcpv6_pid, SIGTERM);
  334. /* Empty any other nameservers/NTP servers that might
  335. * have been left over from a previous config run
  336. */
  337. for (; ns_idx < NETCFG_NAMESERVERS_MAX; ns_idx++) {
  338. *(interface->nameservers[ns_idx]) = '\0';
  339. }
  340. for (; ntp_idx < NETCFG_NTPSERVERS_MAX; ntp_idx++) {
  341. *(interface->ntp_servers[ntp_idx]) = '\0';
  342. }
  343. return rv;
  344. }
  345. /* Configure the network using IPv6 router advertisements, and possibly
  346. * stateless DHCPv6 announcements (if appropriate). Return 1 if all
  347. * went well, and 0 otherwise.
  348. */
  349. static int netcfg_slaac(struct debconfclient *client, struct netcfg_interface *interface)
  350. {
  351. const int SLAAC_MAX_WAIT = 5; /* seconds */
  352. int count, rv = 0;
  353. /* STEP 1: Ensure the interface has finished configuring itself */
  354. /* Progress bar... fun! */
  355. debconf_capb(client, "progresscancel");
  356. debconf_progress_start(client, 0, SLAAC_MAX_WAIT * 4, "netcfg/slaac_wait_title");
  357. for (count = 0; count < SLAAC_MAX_WAIT * 4; count++) {
  358. usleep(250000);
  359. if (debconf_progress_step(client, 1) == 30) {
  360. /* User cancel */
  361. rv = 0;
  362. break;
  363. }
  364. if (nc_v6_interface_configured(interface, 0)) {
  365. debconf_progress_set(client, SLAAC_MAX_WAIT * 4);
  366. rv = 1;
  367. break;
  368. }
  369. }
  370. debconf_progress_stop(client);
  371. /* STEP 2: Stateless DHCP? */
  372. if (interface->v6_stateless_config)
  373. netcfg_dhcpv6(client, interface);
  374. return rv;
  375. }
  376. /* This function handles all of the autoconfiguration for the given interface.
  377. *
  378. * Autoconfiguration of an interface in netcfg has grown significantly
  379. * in recent times. From humble beginnings that started with "yeah, just
  380. * fire up udhcpc and see what happens", the scope has expanded to
  381. * include all manner of IPv6 gubbins.
  382. *
  383. * Hence, this function exists to wrap all of that into a single neat
  384. * package. If you want to autoconfigure an interface, just run it through
  385. * this, and if autoconfiguration was successful to at least the point of
  386. * assigning an IP address, we will return a healthy bouncing baby '1' to
  387. * you. Otherwise, we'll give you the bad news with a '0' -- and you'll
  388. * either have to try another interface, or manually configure it.
  389. *
  390. * Note that we only guarantee that you'll have an IP address as a result
  391. * of successful completion. You'll need to check what else has been
  392. * configured (gateway, hostname, etc) and respond to the user appropriately.
  393. * Also, the fields in +interface+ that deal directly with IP address,
  394. * gateway, etc will *not* be populated -- just the flags that talk about
  395. * what sort of autoconfiguration was completed.
  396. */
  397. int netcfg_autoconfig(struct debconfclient *client, struct netcfg_interface *interface)
  398. {
  399. int ipv6;
  400. di_debug("Want link on %s", interface->name);
  401. netcfg_detect_link(client, interface);
  402. di_debug("Commencing network autoconfiguration on %s", interface->name);
  403. interface->dhcp = interface->slaac = interface->dhcpv6 = 0;
  404. /* We need to start rdnssd before anything else, because it never
  405. * sends it's own ND packets, it just watches for ones already
  406. * on the wire. Thankfully, the use of rdisc6 in
  407. * nc_v6_get_config_flags() will send NDs for us.
  408. */
  409. start_rdnssd(client);
  410. /* Now we prod the network to see what is available */
  411. ipv6 = nc_v6_get_config_flags(client, interface);
  412. /* And now we cleanup from rdnssd */
  413. if (ipv6) {
  414. read_rdnssd_nameservers(interface);
  415. if (nameserver_count(interface) > 0) {
  416. di_debug("Not queueing rdnssd installation to make sure not to interfere with network-manager");
  417. }
  418. }
  419. stop_rdnssd();
  420. if (ipv6) {
  421. di_debug("IPv6 found");
  422. if (interface->v6_stateful_config == 1) {
  423. di_debug("IPv6 stateful autoconfig requested");
  424. if (netcfg_dhcpv6(client, interface))
  425. interface->dhcpv6 = 1;
  426. } else {
  427. di_debug("IPv6 stateless autoconfig requested");
  428. if (netcfg_slaac(client, interface))
  429. interface->slaac = 1;
  430. }
  431. }
  432. if (ipv6)
  433. di_debug("Trying IPv4 autoconfig as well");
  434. else
  435. /* No RA was received; assuming that IPv6 is not available
  436. * on this network and falling back to IPv4
  437. */
  438. di_debug("No RA received; attempting IPv4 autoconfig");
  439. if (netcfg_dhcp(client, interface))
  440. interface->dhcp = 1;
  441. return interface->dhcp || interface->slaac || interface->dhcpv6;
  442. }