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.
 
 
 
 

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