/* * DHCP module for netcfg/netcfg-dhcp. * * Licensed under the terms of the GNU General Public License */ #include "netcfg.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DHCP_OPTION_LEN 1236 /* pump 0.8.24 defines a max option size of 57, dhcp 2.0pl5 uses 1222, dhcp3 3.0.6 uses 1236 */ const char* dhclient_request_options_dhclient[] = { "subnet-mask", "broadcast-address", "time-offset", "routers", "domain-name", "domain-name-servers", "host-name", "ntp-servers", /* extra */ NULL }; const char* dhclient_request_options_udhcpc[] = { "subnet", "broadcast", "router", "domain", "hostname", "dns", "ntpsrv", /* extra */ NULL }; static int dhcp_exit_status = 1; static pid_t dhcp_pid = -1; /* Returns 1 if no default route is available */ static short no_default_route (void) { #if defined(__FreeBSD_kernel__) int status4, status6; status4 = system("exec /lib/freebsd/route show default >/dev/null 2>&1"); status6 = system("exec /lib/freebsd/route show -inet6 default >/dev/null 2>&1"); return (WEXITSTATUS(status4) != 0) && (WEXITSTATUS(status6) != 0); #elif defined(__GNU__) FILE* pfinet = NULL; char buf[1024] = { 0 }; pfinet = popen("fsysopts /servers/socket/2", "r"); if (!pfinet) return 1; if (fgets (buf, 1024, pfinet) == NULL) { pclose (pfinet); return 1; } pclose (pfinet); return !strstr (buf, "--gateway="); #else FILE* iproute = NULL; char buf[256] = { 0 }; /* IPv4 default route? */ if ((iproute = popen("ip route", "r")) != NULL) { while (fgets (buf, 256, iproute) != NULL) { if (strncmp(buf, "default via ", 12) == 0) { pclose(iproute); return 0; } } pclose(iproute); } /* IPv6 default route? */ if ((iproute = popen("ip -6 route", "r")) != NULL) { while (fgets (buf, 256, iproute) != NULL) { if (strncmp(buf, "default via ", 12) == 0) { pclose(iproute); return 0; } } pclose(iproute); } return 1; #endif } /* * Signal handler for DHCP client child * * When the child exits (either because it failed to obtain a * lease or because it succeeded and daemonized itself), this * gets the child's exit status and sets dhcp_pid to -1 */ static void cleanup_dhcp_client(void) { if (dhcp_pid <= 0) /* Already cleaned up */ return; waitpid(dhcp_pid, &dhcp_exit_status, WNOHANG); if (WIFEXITED(dhcp_exit_status)) dhcp_pid = -1; } /* Run through the available client process handlers we're running, and tell * them to cleanup if required. */ void sigchld_handler(int sig __attribute__ ((unused))) { cleanup_dhcp_client(); cleanup_rdnssd(); cleanup_dhcpv6_client(); } /* * This function will start whichever DHCP client is available * using the provided DHCP hostname, if supplied * * The client's PID is stored in dhcp_pid. */ int start_dhcp_client (struct debconfclient *client, char* dhostname, const char *if_name) { FILE *dc = NULL; const char **ptr; char **arguments; int options_count; enum { DHCLIENT, PUMP, UDHCPC } dhcp_client; int dhcp_seconds; char dhcp_seconds_str[16]; if (access("/sbin/dhclient", F_OK) == 0) dhcp_client = DHCLIENT; else if (access("/sbin/pump", F_OK) == 0) dhcp_client = PUMP; else if (access("/sbin/udhcpc", F_OK) == 0) dhcp_client = UDHCPC; else { debconf_input(client, "critical", "netcfg/no_dhcp_client"); debconf_go(client); exit(1); } debconf_get(client, "netcfg/dhcp_timeout"); dhcp_seconds = atoi(client->value); snprintf(dhcp_seconds_str, sizeof dhcp_seconds_str, "%d", dhcp_seconds-1); if ((dhcp_pid = fork()) == 0) { /* child */ /* disassociate from debconf */ fclose(client->out); /* get dhcp lease */ switch (dhcp_client) { case PUMP: if (dhostname) execlp("pump", "pump", "-i", if_name, "-h", dhostname, NULL); else execlp("pump", "pump", "-i", if_name, NULL); break; case DHCLIENT: /* First, set up dhclient.conf */ if ((dc = file_open(DHCLIENT_CONF, "w"))) { fprintf(dc, "send vendor-class-identifier \"d-i\";\n" ); fprintf(dc, "request "); for (ptr = dhclient_request_options_dhclient; *ptr; ptr++) { fprintf(dc, "%s", *ptr); /* look ahead to see if it is the last entry */ if (*(ptr + 1)) fprintf(dc, ", "); else fprintf(dc, ";\n"); } if (dhostname) { fprintf(dc, "send host-name \"%s\";\n", dhostname); } fprintf(dc, "timeout %d;\n", dhcp_seconds); fprintf(dc, "initial-interval 1;\n"); fclose(dc); } execlp("dhclient", "dhclient", "-1", if_name, "-cf", DHCLIENT_CONF, NULL); break; case UDHCPC: /* figure how many options we have */ options_count = 0; for (ptr = dhclient_request_options_udhcpc; *ptr; ptr++) options_count++; arguments = malloc((options_count * 2 /* -O