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.
 
 
 
 

707 lines
23 KiB

  1. /*
  2. * DHCP module for netcfg/netcfg-dhcp.
  3. *
  4. * Licensed under the terms of the GNU General Public License
  5. */
  6. #include "netcfg.h"
  7. #include <errno.h>
  8. #include <stdlib.h>
  9. #include <unistd.h>
  10. #include <debian-installer.h>
  11. #include <stdio.h>
  12. #include <assert.h>
  13. #include <sys/param.h>
  14. #include <sys/socket.h>
  15. #include <sys/stat.h>
  16. #include <sys/ioctl.h>
  17. #include <sys/utsname.h>
  18. #include <sys/wait.h>
  19. #include <arpa/inet.h>
  20. #include <net/if.h>
  21. #include <time.h>
  22. #include <netdb.h>
  23. #define DHCP_OPTION_LEN 1236 /* pump 0.8.24 defines a max option size of 57,
  24. dhcp 2.0pl5 uses 1222, dhcp3 3.0.6 uses 1236 */
  25. const char* dhclient_request_options_dhclient[] = { "subnet-mask",
  26. "broadcast-address",
  27. "time-offset",
  28. "routers",
  29. "domain-name",
  30. "domain-name-servers",
  31. "host-name",
  32. "ntp-servers", /* extra */
  33. NULL };
  34. const char* dhclient_request_options_udhcpc[] = { "subnet",
  35. "broadcast",
  36. "router",
  37. "domain",
  38. "hostname",
  39. "dns",
  40. "ntpsrv", /* extra */
  41. NULL };
  42. static int dhcp_exit_status = 1;
  43. static pid_t dhcp_pid = -1;
  44. /*
  45. * Add DHCP-related lines to /etc/network/interfaces
  46. */
  47. static void netcfg_write_dhcp (char *iface, char *dhostname)
  48. {
  49. FILE *fp;
  50. if ((fp = file_open(INTERFACES_FILE, "a"))) {
  51. fprintf(fp, "\n# The primary network interface\n");
  52. if (!iface_is_hotpluggable(iface) && !find_in_stab(iface))
  53. fprintf(fp, "auto %s\n", iface);
  54. else
  55. fprintf(fp, "allow-hotplug %s\n", iface);
  56. fprintf(fp, "iface %s inet dhcp\n", iface);
  57. if (dhostname) {
  58. fprintf(fp, "\thostname %s\n", dhostname);
  59. }
  60. if (is_wireless_iface(iface)) {
  61. fprintf(fp, "\t# wireless-* options are implemented by the wireless-tools package\n");
  62. fprintf(fp, "\twireless-mode %s\n",
  63. (mode == MANAGED) ? "managed" : "ad-hoc");
  64. fprintf(fp, "\twireless-essid %s\n",
  65. (essid && *essid) ? essid : "any");
  66. if (wepkey != NULL)
  67. fprintf(fp, "\twireless-key1 %s\n", wepkey);
  68. }
  69. fclose(fp);
  70. }
  71. }
  72. /* Returns 1 if no default route is available */
  73. static short no_default_route (void)
  74. {
  75. #if defined(__FreeBSD_kernel__)
  76. int status;
  77. status = system("exec /lib/freebsd/route show default >/dev/null 2>&1");
  78. return WEXITSTATUS(status) != 0;
  79. #else
  80. FILE* iproute = NULL;
  81. char buf[256] = { 0 };
  82. if ((iproute = popen("ip route", "r")) != NULL) {
  83. while (fgets (buf, 256, iproute) != NULL) {
  84. if (buf[0] == 'd' && strstr (buf, "default via ")) {
  85. pclose(iproute);
  86. return 0;
  87. }
  88. }
  89. pclose(iproute);
  90. }
  91. return 1;
  92. #endif
  93. }
  94. /*
  95. * Signal handler for DHCP client child
  96. *
  97. * When the child exits (either because it failed to obtain a
  98. * lease or because it succeeded and daemonized itself), this
  99. * gets the child's exit status and sets dhcp_pid to -1
  100. */
  101. static void dhcp_client_sigchld(int sig __attribute__ ((unused)))
  102. {
  103. di_debug("dhcp_client_sigchld() called");
  104. if (dhcp_pid <= 0)
  105. /* Already cleaned up */
  106. return;
  107. /*
  108. * I hope it's OK to call waitpid() from the SIGCHLD signal handler
  109. */
  110. di_debug("Waiting for dhcp_pid = %i", dhcp_pid);
  111. waitpid(dhcp_pid, &dhcp_exit_status, WNOHANG);
  112. if (WIFEXITED(dhcp_exit_status)) {
  113. dhcp_pid = -1;
  114. }
  115. }
  116. /*
  117. * This function will start whichever DHCP client is available
  118. * using the provided DHCP hostname, if supplied
  119. *
  120. * The client's PID is stored in dhcp_pid.
  121. */
  122. int start_dhcp_client (struct debconfclient *client, char* dhostname)
  123. {
  124. FILE *dc = NULL;
  125. const char **ptr;
  126. char **arguments;
  127. int options_count;
  128. enum { DHCLIENT, PUMP, UDHCPC } dhcp_client;
  129. int dhcp_seconds;
  130. char dhcp_seconds_str[16];
  131. if (access("/sbin/dhclient", F_OK) == 0)
  132. dhcp_client = DHCLIENT;
  133. else if (access("/sbin/pump", F_OK) == 0)
  134. dhcp_client = PUMP;
  135. else if (access("/sbin/udhcpc", F_OK) == 0)
  136. dhcp_client = UDHCPC;
  137. else {
  138. debconf_input(client, "critical", "netcfg/no_dhcp_client");
  139. debconf_go(client);
  140. exit(1);
  141. }
  142. debconf_get(client, "netcfg/dhcp_timeout");
  143. dhcp_seconds = atoi(client->value);
  144. snprintf(dhcp_seconds_str, sizeof dhcp_seconds_str, "%d", dhcp_seconds-1);
  145. if ((dhcp_pid = fork()) == 0) { /* child */
  146. /* disassociate from debconf */
  147. fclose(client->out);
  148. /* get dhcp lease */
  149. switch (dhcp_client) {
  150. case PUMP:
  151. if (dhostname)
  152. execlp("pump", "pump", "-i", interface, "-h", dhostname, NULL);
  153. else
  154. execlp("pump", "pump", "-i", interface, NULL);
  155. break;
  156. case DHCLIENT:
  157. /* First, set up dhclient.conf */
  158. if ((dc = file_open(DHCLIENT_CONF, "w"))) {
  159. fprintf(dc, "send vendor-class-identifier \"d-i\";\n" );
  160. fprintf(dc, "request ");
  161. for (ptr = dhclient_request_options_dhclient; *ptr; ptr++) {
  162. fprintf(dc, *ptr);
  163. /* look ahead to see if it is the last entry */
  164. if (*(ptr + 1))
  165. fprintf(dc, ", ");
  166. else
  167. fprintf(dc, ";\n");
  168. }
  169. if (dhostname) {
  170. fprintf(dc, "send host-name \"%s\";\n", dhostname);
  171. }
  172. fprintf(dc, "timeout %d;\n", dhcp_seconds);
  173. fclose(dc);
  174. }
  175. execlp("dhclient", "dhclient", "-1", interface, NULL);
  176. break;
  177. case UDHCPC:
  178. /* figure how many options we have */
  179. options_count = 0;
  180. for (ptr = dhclient_request_options_udhcpc; *ptr; ptr++)
  181. options_count++;
  182. arguments = malloc((options_count * 2 /* -O <option> repeatedly */
  183. + 9 /* Other arguments (listed below) */
  184. + 2 /* dhostname (maybe) */
  185. + 1 /* NULL */
  186. ) * sizeof(char **));
  187. /* set the command options */
  188. options_count = 0;
  189. arguments[options_count++] = "udhcpc";
  190. arguments[options_count++] = "-i";
  191. arguments[options_count++] = interface;
  192. arguments[options_count++] = "-V";
  193. arguments[options_count++] = "d-i";
  194. arguments[options_count++] = "-T";
  195. arguments[options_count++] = "1";
  196. arguments[options_count++] = "-t";
  197. arguments[options_count++] = dhcp_seconds_str;
  198. for (ptr = dhclient_request_options_udhcpc; *ptr; ptr++) {
  199. arguments[options_count++] = "-O";
  200. arguments[options_count++] = (char *)*ptr;
  201. }
  202. if (dhostname) {
  203. arguments[options_count++] = "-H";
  204. arguments[options_count++] = dhostname;
  205. }
  206. arguments[options_count] = NULL;
  207. execvp("udhcpc", arguments);
  208. free(arguments);
  209. break;
  210. }
  211. if (errno != 0)
  212. di_error("Could not exec dhcp client: %s", strerror(errno));
  213. return 1; /* should NEVER EVER get here */
  214. }
  215. else if (dhcp_pid == -1) {
  216. di_warning("DHCP fork failed; this is unlikely to end well");
  217. return 1;
  218. } else {
  219. /* dhcp_pid contains the child's PID */
  220. di_warning("Started DHCP client; PID is %i", dhcp_pid);
  221. signal(SIGCHLD, &dhcp_client_sigchld);
  222. return 0;
  223. }
  224. }
  225. static int kill_dhcp_client(void)
  226. {
  227. system("killall.sh");
  228. return 0;
  229. }
  230. /*
  231. * Poll the started DHCP client for netcfg/dhcp_timeout seconds (def. 15)
  232. * and return 0 if a lease is known to have been acquired,
  233. * 1 otherwise.
  234. *
  235. * The client should be run such that it exits once a lease is acquired
  236. * (although its child continues to run as a daemon)
  237. *
  238. * This function will NOT kill the child if time runs out. This allows
  239. * the user to choose to wait longer for the lease to be acquired.
  240. */
  241. int poll_dhcp_client (struct debconfclient *client)
  242. {
  243. int seconds_slept = 0;
  244. int ret = 1;
  245. int dhcp_seconds;
  246. debconf_get(client, "netcfg/dhcp_timeout");
  247. dhcp_seconds = atoi(client->value);
  248. /* show progress bar */
  249. debconf_capb(client, "backup progresscancel");
  250. debconf_progress_start(client, 0, dhcp_seconds, "netcfg/dhcp_progress");
  251. if (debconf_progress_info(client, "netcfg/dhcp_progress_note") == 30)
  252. goto stop;
  253. netcfg_progress_displayed = 1;
  254. /* wait between 2 and dhcp_seconds seconds for a DHCP lease */
  255. while ( ((dhcp_pid > 0) || (seconds_slept < 2))
  256. && (seconds_slept < dhcp_seconds) ) {
  257. sleep(1);
  258. seconds_slept++; /* Not exact but close enough */
  259. if (debconf_progress_step(client, 1) == 30)
  260. goto stop;
  261. }
  262. /* Either the client exited or time ran out */
  263. /* got a lease? display a success message */
  264. if (!(dhcp_pid > 0) && (dhcp_exit_status == 0)) {
  265. ret = 0;
  266. debconf_capb(client, "backup"); /* stop displaying cancel button */
  267. if (debconf_progress_set(client, dhcp_seconds) == 30)
  268. goto stop;
  269. if (debconf_progress_info(client, "netcfg/dhcp_success_note") == 30)
  270. goto stop;
  271. sleep(2);
  272. }
  273. stop:
  274. /* stop progress bar */
  275. debconf_progress_stop(client);
  276. debconf_capb(client, "backup");
  277. netcfg_progress_displayed = 0;
  278. return ret;
  279. }
  280. #define REPLY_RETRY_AUTOCONFIG 0
  281. #define REPLY_RETRY_WITH_HOSTNAME 1
  282. #define REPLY_CONFIGURE_MANUALLY 2
  283. #define REPLY_DONT_CONFIGURE 3
  284. #define REPLY_RECONFIGURE_WIFI 4
  285. #define REPLY_LOOP_BACK 5
  286. #define REPLY_CHECK_DHCP 6
  287. #define REPLY_ASK_OPTIONS 7
  288. int ask_dhcp_options (struct debconfclient *client)
  289. {
  290. int ret;
  291. if (is_wireless_iface(interface)) {
  292. debconf_metaget(client, "netcfg/internal-wifireconf", "description");
  293. debconf_subst(client, "netcfg/dhcp_options", "wifireconf", client->value);
  294. }
  295. else /* blank from last time */
  296. debconf_subst(client, "netcfg/dhcp_options", "wifireconf", "");
  297. /* critical, we don't want to enter a loop */
  298. debconf_input(client, "critical", "netcfg/dhcp_options");
  299. ret = debconf_go(client);
  300. if (ret == 30)
  301. return GO_BACK;
  302. debconf_get(client, "netcfg/dhcp_options");
  303. /* strcmp sucks */
  304. if (client->value[0] == 'R') { /* _R_etry ... or _R_econfigure ... */
  305. size_t len = strlen(client->value);
  306. if (client->value[len - 1] == 'e') /* ... with DHCP hostnam_e_ */
  307. return REPLY_RETRY_WITH_HOSTNAME;
  308. else if (client->value[len - 1] == 'k') /* ... wireless networ_k_ */
  309. return REPLY_RECONFIGURE_WIFI;
  310. else
  311. return REPLY_RETRY_AUTOCONFIG;
  312. }
  313. else if (client->value[0] == 'C') /* _C_onfigure ... */
  314. return REPLY_CONFIGURE_MANUALLY;
  315. else if (empty_str(client->value))
  316. return REPLY_LOOP_BACK;
  317. else
  318. return REPLY_DONT_CONFIGURE;
  319. }
  320. int ask_wifi_configuration (struct debconfclient *client)
  321. {
  322. enum { ABORT, DONE, ESSID, WEP } wifistate = ESSID;
  323. for (;;) {
  324. switch (wifistate) {
  325. case ESSID:
  326. wifistate = (netcfg_wireless_set_essid(client, interface, "high") == GO_BACK) ?
  327. ABORT : WEP;
  328. break;
  329. case WEP:
  330. wifistate = (netcfg_wireless_set_wep(client, interface) == GO_BACK) ?
  331. ESSID : DONE;
  332. break;
  333. case ABORT:
  334. return REPLY_ASK_OPTIONS;
  335. break;
  336. case DONE:
  337. return REPLY_CHECK_DHCP;
  338. break;
  339. }
  340. }
  341. }
  342. int netcfg_activate_dhcp (struct debconfclient *client)
  343. {
  344. char* dhostname = NULL;
  345. enum { START, POLL, ASK_OPTIONS, DHCP_HOSTNAME, HOSTNAME, DOMAIN, HOSTNAME_SANS_NETWORK } state = START;
  346. kill_dhcp_client();
  347. loop_setup();
  348. for (;;) {
  349. switch (state) {
  350. case START:
  351. if (start_dhcp_client(client, dhostname))
  352. netcfg_die(client); /* change later */
  353. else
  354. state = POLL;
  355. break;
  356. case POLL:
  357. if (poll_dhcp_client(client)) {
  358. /* could not get a lease, show the error, present options */
  359. debconf_capb(client, "");
  360. debconf_input(client, "critical", "netcfg/dhcp_failed");
  361. debconf_go(client);
  362. debconf_capb(client, "backup");
  363. state = ASK_OPTIONS;
  364. } else {
  365. /* got a lease */
  366. /*
  367. * That means that the DHCP client has exited, although its
  368. * child is still running as a daemon
  369. */
  370. /* Before doing anything else, check for a default route */
  371. if (no_default_route()) {
  372. debconf_input(client, "critical", "netcfg/no_default_route");
  373. debconf_go(client);
  374. debconf_get(client, "netcfg/no_default_route");
  375. if (!strcmp(client->value, "false")) {
  376. state = ASK_OPTIONS;
  377. break;
  378. }
  379. }
  380. /*
  381. * Set defaults for domain name and hostname
  382. */
  383. char buf[MAXHOSTNAMELEN + 1] = { 0 };
  384. char *ptr = NULL;
  385. FILE *d = NULL;
  386. have_domain = 0;
  387. /*
  388. * Default to the domain name returned via DHCP, if any
  389. */
  390. if ((d = fopen(DOMAIN_FILE, "r")) != NULL) {
  391. char domain[_UTSNAME_LENGTH + 1] = { 0 };
  392. fgets(domain, _UTSNAME_LENGTH, d);
  393. fclose(d);
  394. unlink(DOMAIN_FILE);
  395. if (!empty_str(domain) && valid_domain(domain)) {
  396. debconf_set(client, "netcfg/get_domain", domain);
  397. have_domain = 1;
  398. }
  399. }
  400. /*
  401. * Record any ntp server information from DHCP for later
  402. * verification and use by clock-setup
  403. */
  404. if ((d = fopen(NTP_SERVER_FILE, "r")) != NULL) {
  405. char ntpservers[DHCP_OPTION_LEN + 1] = { 0 };
  406. fgets(ntpservers, DHCP_OPTION_LEN, d);
  407. fclose(d);
  408. unlink(NTP_SERVER_FILE);
  409. if (!empty_str(ntpservers)) {
  410. debconf_set(client, "netcfg/dhcp_ntp_servers",
  411. ntpservers);
  412. }
  413. }
  414. /*
  415. * Default to the hostname returned via DHCP, if any,
  416. * otherwise to the requested DHCP hostname
  417. * otherwise to the hostname found in DNS for the IP address
  418. * of the interface
  419. */
  420. if (gethostname(buf, sizeof(buf)) == 0
  421. && !empty_str(buf)
  422. && strcmp(buf, "(none)")
  423. && valid_domain(buf)
  424. ) {
  425. di_info("DHCP hostname: \"%s\"", buf);
  426. debconf_set(client, "netcfg/get_hostname", buf);
  427. }
  428. else if (dhostname) {
  429. debconf_set(client, "netcfg/get_hostname", dhostname);
  430. } else {
  431. struct ifreq ifr;
  432. struct in_addr d_ipaddr = { 0 };
  433. ifr.ifr_addr.sa_family = AF_INET;
  434. strncpy(ifr.ifr_name, interface, IFNAMSIZ);
  435. if (ioctl(skfd, SIOCGIFADDR, &ifr) == 0) {
  436. d_ipaddr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr;
  437. seed_hostname_from_dns(client, &d_ipaddr);
  438. }
  439. else
  440. di_warning("ioctl failed (%s)", strerror(errno));
  441. }
  442. /*
  443. * Default to the domain name that is the domain part
  444. * of the hostname, if any
  445. */
  446. if (have_domain == 0 && (ptr = strchr(buf, '.')) != NULL) {
  447. debconf_set(client, "netcfg/get_domain", ptr + 1);
  448. have_domain = 1;
  449. }
  450. /* Make sure we have NS going if the DHCP server didn't serve it up */
  451. if (resolv_conf_entries() <= 0) {
  452. char *nameservers = NULL;
  453. if (netcfg_get_nameservers (client, &nameservers) == GO_BACK) {
  454. state = ASK_OPTIONS;
  455. break;
  456. }
  457. netcfg_nameservers_to_array (nameservers, nameserver_array);
  458. netcfg_write_resolv (domain, nameserver_array);
  459. }
  460. state = HOSTNAME;
  461. }
  462. break;
  463. case ASK_OPTIONS:
  464. /* DHCP client may still be running */
  465. switch (ask_dhcp_options (client)) {
  466. case GO_BACK:
  467. kill_dhcp_client();
  468. return 10;
  469. case REPLY_RETRY_WITH_HOSTNAME:
  470. state = DHCP_HOSTNAME;
  471. break;
  472. case REPLY_CONFIGURE_MANUALLY:
  473. kill_dhcp_client();
  474. return 15;
  475. break;
  476. case REPLY_DONT_CONFIGURE:
  477. kill_dhcp_client();
  478. netcfg_write_loopback();
  479. state = HOSTNAME_SANS_NETWORK;
  480. break;
  481. case REPLY_RETRY_AUTOCONFIG:
  482. if (dhcp_pid > 0)
  483. state = POLL;
  484. else {
  485. kill_dhcp_client();
  486. state = START;
  487. }
  488. break;
  489. case REPLY_RECONFIGURE_WIFI:
  490. if (ask_wifi_configuration(client) == REPLY_CHECK_DHCP) {
  491. if (dhcp_pid > 0)
  492. state = POLL;
  493. else {
  494. kill_dhcp_client();
  495. state = START;
  496. }
  497. }
  498. else
  499. state = ASK_OPTIONS;
  500. break;
  501. }
  502. break;
  503. case DHCP_HOSTNAME:
  504. /* DHCP client may still be running */
  505. if (netcfg_get_hostname(client, "netcfg/dhcp_hostname", &dhostname, 0))
  506. state = ASK_OPTIONS;
  507. else {
  508. if (empty_str(dhostname)) {
  509. free(dhostname);
  510. dhostname = NULL;
  511. }
  512. kill_dhcp_client();
  513. state = START;
  514. }
  515. break;
  516. case HOSTNAME:
  517. if (netcfg_get_hostname (client, "netcfg/get_hostname", &hostname, 1)) {
  518. /*
  519. * Going back to POLL wouldn't make much sense.
  520. * However, it does make sense to go to the retry
  521. * screen where the user can elect to retry DHCP with
  522. * a requested DHCP hostname, etc.
  523. */
  524. state = ASK_OPTIONS;
  525. }
  526. else
  527. state = DOMAIN;
  528. break;
  529. case DOMAIN:
  530. if (!have_domain && netcfg_get_domain (client, &domain))
  531. state = HOSTNAME;
  532. else {
  533. netcfg_write_common(ipaddress, hostname, domain);
  534. netcfg_write_dhcp(interface, dhostname);
  535. /* If the resolv.conf was written by udhcpc, then nameserver_array
  536. * will be empty and we'll need to populate it. If we asked for
  537. * the nameservers, then it'll be full, but nobody will care if we
  538. * refill it.
  539. */
  540. if (read_resolv_conf_nameservers(nameserver_array))
  541. netcfg_write_resolv(domain, nameserver_array);
  542. else
  543. printf("Error reading resolv.conf for nameservers\n");
  544. return 0;
  545. }
  546. break;
  547. case HOSTNAME_SANS_NETWORK:
  548. if (netcfg_get_hostname (client, "netcfg/get_hostname", &hostname, 0))
  549. state = ASK_OPTIONS;
  550. else {
  551. struct in_addr null_ipaddress;
  552. null_ipaddress.s_addr = 0;
  553. netcfg_write_common(null_ipaddress, hostname, NULL);
  554. return 0;
  555. }
  556. break;
  557. }
  558. }
  559. }
  560. /* returns number of 'nameserver' entries in resolv.conf */
  561. int resolv_conf_entries (void)
  562. {
  563. FILE *f;
  564. int count = 0;
  565. if ((f = fopen(RESOLV_FILE, "r")) != NULL) {
  566. char buf[256];
  567. while (fgets(buf, 256, f) != NULL) {
  568. char *ptr;
  569. if ((ptr = strchr(buf, ' ')) != NULL) {
  570. *ptr = '\0';
  571. if (strcmp(buf, "nameserver") == 0)
  572. count++;
  573. }
  574. }
  575. fclose(f);
  576. }
  577. else
  578. count = -1;
  579. return count;
  580. }
  581. /* Read the nameserver entries out of resolv.conf and stick them into
  582. * nameservers_array, so we can write out a newer, shinier resolv.conf
  583. */
  584. int read_resolv_conf_nameservers(struct in_addr array[])
  585. {
  586. FILE *f;
  587. int i = 0;
  588. if ((f = fopen(RESOLV_FILE, "r")) != NULL) {
  589. char buf[256];
  590. while (fgets(buf, 256, f) != NULL) {
  591. char *ptr;
  592. if (strncmp(buf, "nameserver ", strlen("nameserver ")) == 0) {
  593. /* Chop off trailing \n */
  594. if (buf[strlen(buf)-1] == '\n')
  595. buf[strlen(buf)-1] = '\0';
  596. ptr = buf + strlen("nameserver ");
  597. inet_pton(AF_INET, ptr, &array[i++]);
  598. if (i == 3) {
  599. /* We can only hold so many nameservers, and we've reached
  600. * our limit. Sorry.
  601. */
  602. break;
  603. }
  604. }
  605. }
  606. fclose(f);
  607. array[i].s_addr = 0;
  608. return 1;
  609. }
  610. else
  611. return 0;
  612. }