From 3dbe22465b8d37681dc59e18c0c0d3807e09069d Mon Sep 17 00:00:00 2001 From: Francis Dupont Date: Fri, 23 May 2008 13:22:23 +0000 Subject: [PATCH] merge 17621 --- client/clparse.c | 3 +- client/dhc6.c | 354 ++++++++++++++++++++++++++++++++++++++++++++-- client/dhclient.c | 294 ++++++++++++++++++++++++++------------ includes/dhcp6.h | 6 + includes/dhcpd.h | 1 + 5 files changed, 550 insertions(+), 108 deletions(-) diff --git a/client/clparse.c b/client/clparse.c index e5f4e81ce..52a7db251 100644 --- a/client/clparse.c +++ b/client/clparse.c @@ -1439,8 +1439,7 @@ parse_client6_lease_statement(struct parse *cfile) if (iface == NULL) parse_warn(cfile, "Lease has no interface designation."); - - if (!has_name && (client == NULL)) { + else if (!has_name && (client == NULL)) { for (client = iface->client ; client != NULL ; client = client->next) { if (client->name == NULL) diff --git a/client/dhc6.c b/client/dhc6.c index 19917e838..00f30e615 100644 --- a/client/dhc6.c +++ b/client/dhc6.c @@ -39,6 +39,7 @@ struct option *ia_pd_option = NULL; struct option *iaaddr_option = NULL; struct option *iaprefix_option = NULL; struct option *oro_option = NULL; +struct option *irt_option = NULL; static struct dhc6_lease *dhc6_dup_lease(struct dhc6_lease *lease, const char *file, int line); @@ -66,8 +67,10 @@ static struct dhc6_ia *find_ia_na(struct dhc6_ia *head, const char *id); static struct dhc6_addr *find_addr(struct dhc6_addr *head, struct iaddr *address); void init_handler(struct packet *packet, struct client_state *client); +void info_request_handler(struct packet *packet, struct client_state *client); void rapid_commit_handler(struct packet *packet, struct client_state *client); void do_init6(void *input); +void do_info_request6(void *input); void do_confirm6(void *input); void reply_handler(struct packet *packet, struct client_state *client); static isc_result_t dhc6_add_ia_na(struct client_state *client, @@ -80,6 +83,8 @@ void do_select6(void *input); void do_refresh6(void *input); static void do_release6(void *input); static void start_bound(struct client_state *client); +static void start_informed(struct client_state *client); +void informed_handler(struct packet *packet, struct client_state *client); void bound_handler(struct packet *packet, struct client_state *client); void start_renew6(void *input); void start_rebind6(void *input); @@ -92,10 +97,12 @@ static void script_write_params6(struct client_state *client, const char *prefix, struct option_state *options); +extern int stateless; + /* The "best" default DUID, since we cannot predict any information * about the system (such as whether or not the hardware addresses are * integrated into the motherboard or similar), is the "LLT", link local - * plus time, DUID. + * plus time, DUID. For real stateless "LL" is better. * * Once generated, this duid is stored into the state database, and * retained across restarts. @@ -123,22 +130,31 @@ form_duid(struct data_string *duid, const char *file, int line) /* 2 bytes for the 'duid type' field. * 2 bytes for the 'htype' field. - * 4 bytes for the 'current time'. + * (not stateless) 4 bytes for the 'current time'. * enough bytes for the hardware address (note that hw_address has * the 'htype' on byte zero). */ - len = 8 + (ip->hw_address.hlen - 1); + len = 4 + (ip->hw_address.hlen - 1); + if (!stateless) + len += 4; if (!buffer_allocate(&duid->buffer, len, MDL)) log_fatal("no memory for default DUID!"); duid->data = duid->buffer->data; duid->len = len; /* Basic Link Local Address type of DUID. */ - putUShort(duid->buffer->data, DUID_LLT); - putUShort(duid->buffer->data + 2, ip->hw_address.hbuf[0]); - putULong(duid->buffer->data + 4, cur_time - DUID_TIME_EPOCH); - memcpy(duid->buffer->data + 8, ip->hw_address.hbuf + 1, - ip->hw_address.hlen - 1); + if (!stateless) { + putUShort(duid->buffer->data, DUID_LLT); + putUShort(duid->buffer->data + 2, ip->hw_address.hbuf[0]); + putULong(duid->buffer->data + 4, cur_time - DUID_TIME_EPOCH); + memcpy(duid->buffer->data + 8, ip->hw_address.hbuf + 1, + ip->hw_address.hlen - 1); + } else { + putUShort(duid->buffer->data, DUID_LL); + putUShort(duid->buffer->data + 2, ip->hw_address.hbuf[0]); + memcpy(duid->buffer->data + 4, ip->hw_address.hbuf + 1, + ip->hw_address.hlen - 1); + } } /* Assign DHCPv6 port numbers as a client. @@ -217,6 +233,11 @@ dhcpv6_client_assignments(void) &code, 0, MDL)) log_fatal("Unable to find the ORO option definition."); + code = D6O_INFORMATION_REFRESH_TIME; + if (!option_code_hash_lookup(&irt_option, dhcpv6_universe.code_hash, + &code, 0, MDL)) + log_fatal("Unable to find the IRT option definition."); + #ifndef __CYGWIN32__ /* XXX */ endservent(); #endif @@ -1253,8 +1274,9 @@ start_init6(struct client_state *client) client->MRD = 0; dhc6_retrans_init(client); - /* RFC3315 section 17.1.2 goes out of its way: - * + + /* + * RFC3315 section 17.1.2 goes out of its way: * Also, the first RT MUST be selected to be strictly greater than IRT * by choosing RAND to be strictly greater than 0. */ @@ -1284,7 +1306,45 @@ start_init6(struct client_state *client) go_daemon(); } -/* start_init6() kicks off an "init-reboot" version of the process, at +/* start_info_request6() kicks off the process, transmitting an info + * request packet and scheduling a retransmission event. + */ +void +start_info_request6(struct client_state *client) +{ + struct timeval tv; + + log_debug("PRC: Requesting information (INIT)."); + client->state = S_INIT; + + /* Initialize timers, RFC3315 section 18.1.5. */ + client->IRT = INF_TIMEOUT * 100; + client->MRT = INF_MAX_RT * 100; + client->MRC = 0; + client->MRD = 0; + + dhc6_retrans_init(client); + + client->v6_handler = info_request_handler; + + /* RFC3315 section 18.1.5 says we MUST start the first packet + * between 0 and INF_MAX_DELAY seconds. The good news is + * INF_MAX_DELAY is 1. + */ + tv.tv_sec = cur_tv.tv_sec; + tv.tv_usec = cur_tv.tv_usec; + tv.tv_usec += (random() % (INF_MAX_DELAY * 100)) * 10000; + if (tv.tv_usec >= 1000000) { + tv.tv_sec += 1; + tv.tv_usec -= 1000000; + } + add_timeout(&tv, do_info_request6, client, NULL, NULL); + + if (nowait) + go_daemon(); +} + +/* start_confirm6() kicks off an "init-reboot" version of the process, at * startup to find out if old bindings are 'fair' and at runtime whenever * a link cycles state we'll eventually want to do this. */ @@ -1529,6 +1589,107 @@ do_init6(void *input) dhc6_retrans_advance(client); } +/* do_info_request6() marshals and transmits an information-request. */ +void +do_info_request6(void *input) +{ + struct client_state *client; + struct data_string ds; + struct timeval elapsed, tv; + int send_ret; + + client = input; + + if ((client->MRC != 0) && (client->txcount > client->MRC)) { + log_info("Max retransmission count exceeded."); + return; + } + + /* + * Start_time starts at the first transmission. + */ + if (client->txcount == 0) { + client->start_time.tv_sec = cur_tv.tv_sec; + client->start_time.tv_usec = cur_tv.tv_usec; + } + + /* elapsed = cur - start */ + elapsed.tv_sec = cur_tv.tv_sec - client->start_time.tv_sec; + elapsed.tv_usec = cur_tv.tv_usec - client->start_time.tv_usec; + if (elapsed.tv_usec < 0) { + elapsed.tv_sec -= 1; + elapsed.tv_usec += 1000000; + } + if ((client->MRD != 0) && (elapsed.tv_sec > client->MRD)) { + log_info("Max retransmission duration exceeded."); + return; + } + + memset(&ds, 0, sizeof(ds)); + if (!buffer_allocate(&ds.buffer, 4, MDL)) { + log_error("Unable to allocate memory for INFO-REQUEST."); + return; + } + ds.data = ds.buffer->data; + ds.len = 4; + + ds.buffer->data[0] = DHCPV6_INFORMATION_REQUEST; + memcpy(ds.buffer->data + 1, client->dhcpv6_transaction_id, 3); + + /* Form an elapsed option. */ + /* Maximum value is 65535 1/100s coded as 0xffff. */ + if ((elapsed.tv_sec < 0) || (elapsed.tv_sec > 655) || + ((elapsed.tv_sec == 655) && (elapsed.tv_usec > 350000))) { + client->elapsed = 0xffff; + } else { + client->elapsed = elapsed.tv_sec * 100; + client->elapsed += elapsed.tv_usec / 10000; + } + + if (client->elapsed == 0) + log_debug("XMT: Forming Info-Request, 0 ms elapsed."); + else + log_debug("XMT: Forming Info-Request, %u0 ms elapsed.", + (unsigned)client->elapsed); + + client->elapsed = htons(client->elapsed); + + make_client6_options(client, &client->sent_options, NULL, + DHCPV6_INFORMATION_REQUEST); + + /* Fetch any configured 'sent' options (includes DUID) in wire format. + */ + dhcpv6_universe.encapsulate(&ds, NULL, NULL, client, + NULL, client->sent_options, &global_scope, + &dhcpv6_universe); + + /* Transmit and wait. */ + + log_info("XMT: Info-Request on %s, interval %ld0ms.", + client->name ? client->name : client->interface->name, + (long int)client->RT); + + send_ret = send_packet6(client->interface, + ds.data, ds.len, &DHCPv6DestAddr); + if (send_ret != ds.len) { + log_error("dhc6: send_packet6() sent %d of %d bytes", + send_ret, ds.len); + } + + data_string_forget(&ds, MDL); + + /* Wait RT */ + tv.tv_sec = cur_tv.tv_sec + client->RT / 100; + tv.tv_usec = cur_tv.tv_usec + (client->RT % 100) * 10000; + if (tv.tv_usec >= 1000000) { + tv.tv_sec += 1; + tv.tv_usec -= 1000000; + } + add_timeout(&tv, do_info_request6, client, NULL, NULL); + + dhc6_retrans_advance(client); +} + /* do_confirm6() creates a Confirm packet and transmits it. This function * is called on every timeout to (re)transmit. */ @@ -2467,6 +2628,62 @@ init_handler(struct packet *packet, struct client_state *client) log_debug("RCV: Advertisement recorded."); } +/* info_request_handler() accepts a Reply to an Info-request. + */ +void +info_request_handler(struct packet *packet, struct client_state *client) +{ + isc_result_t check_status; + unsigned code; + + if (packet->dhcpv6_msg_type != DHCPV6_REPLY) + return; + + /* RFC3315 section 15.10 validation (same as 15.3 since we + * always include a client id). + */ + if (!valid_reply(packet, client)) { + log_error("Invalid Reply - rejecting."); + return; + } + + check_status = dhc6_check_status(ISC_R_SUCCESS, packet->options, + "message", &code); + if (check_status != ISC_R_SUCCESS) { + /* If no action was taken, but there is an error, then + * we wait for a retransmission. + */ + if (check_status != ISC_R_CANCELED) + return; + } + + /* We're done retransmitting at this point. */ + cancel_timeout(do_info_request6, client); + + /* Action was taken, so now that we've torn down our scheduled + * retransmissions, return. + */ + if (check_status == ISC_R_CANCELED) + return; + + /* Cleanup if a previous attempt to go bound failed. */ + if (client->old_lease != NULL) { + dhc6_lease_destroy(&client->old_lease, MDL); + client->old_lease = NULL; + } + + /* Cache options in the active_lease. */ + if (client->active_lease != NULL) + client->old_lease = client->active_lease; + client->active_lease = dmalloc(sizeof(struct dhc6_lease), MDL); + if (client->active_lease == NULL) + log_fatal("Out of memory for v6 lease structure."); + option_state_reference(&client->active_lease->options, + packet->options, MDL); + + start_informed(client); +} + /* Specific version of init_handler() for rapid-commit. */ void @@ -3499,7 +3716,7 @@ dhc6_merge_lease(struct dhc6_lease *src, struct dhc6_lease *dst) * to inform it about the new values, and then lay in wait for the next * event. */ -void +static void start_bound(struct client_state *client) { struct dhc6_ia *ia, *oldia; @@ -3974,6 +4191,7 @@ do_expire(void *input) * Run client script to unconfigure interface. * Called with reason STOP6 when dhclient -x is run, or with reason * RELEASE6 when server has replied to a Release message. + * Stateless is a special case. */ void unconfigure6(struct client_state *client, const char *reason) @@ -3981,6 +4199,15 @@ unconfigure6(struct client_state *client, const char *reason) struct dhc6_ia *ia; struct dhc6_addr *addr; + if (stateless) { + script_init(client, reason, NULL); + if (client->active_lease != NULL) + script_write_params6(client, "old_", + client->active_lease->options); + script_go(client); + return; + } + if (client->active_lease == NULL) return; @@ -4000,6 +4227,109 @@ unconfigure6(struct client_state *client, const char *reason) } } +void +refresh_info_request6(void *input) +{ + struct client_state *client; + + client = (struct client_state *)input; + start_info_request6(client); +} + +/* Timeout for Information-Request (using the IRT option). + */ +static void +dhc6_check_irt(struct client_state *client) +{ + struct option **req; + struct option_cache *oc; + TIME expire = MAX_TIME; + struct timeval tv; + int i; + isc_boolean_t found = ISC_FALSE; + + cancel_timeout(refresh_info_request6, client); + + req = client->config->requested_options; + for (i = 0; req[i] != NULL; i++) { + if (req[i] == irt_option) { + found = ISC_TRUE; + break; + } + } + /* Simply return gives a endless loop waiting for nothing. */ + if (!found) + exit(0); + + oc = lookup_option(&dhcpv6_universe, client->active_lease->options, + D6O_INFORMATION_REFRESH_TIME); + if (oc != NULL) { + struct data_string irt; + + memset(&irt, 0, sizeof(irt)); + if (!evaluate_option_cache(&irt, NULL, NULL, client, + client->active_lease->options, + NULL, &global_scope, oc, MDL) || + (irt.len < 4)) { + log_error("Can't evaluate IRT."); + } else { + expire = getULong(irt.data); + if (expire < IRT_MINIMUM) + expire = IRT_MINIMUM; + if (expire == 0xffffffff) + expire = MAX_TIME; + } + data_string_forget(&irt, MDL); + } else + expire = IRT_DEFAULT; + + if (expire != MAX_TIME) { + log_debug("PRC: Refresh event scheduled in %u seconds.", + (unsigned) expire); + tv.tv_sec = cur_time + expire; + tv.tv_usec = 0; + add_timeout(&tv, refresh_info_request6, client, NULL, NULL); + } +} + +/* We got a Reply. Give dhclient-script a tickle to inform it about + * the new values, and then lay in wait for the next event. + */ +static void +start_informed(struct client_state *client) +{ + client->v6_handler = informed_handler; + + log_debug("PRC: Done."); + + client->state = S_BOUND; + + script_init(client, "RENEW6", NULL); + if (client->old_lease != NULL) + script_write_params6(client, "old_", + client->old_lease->options); + script_write_params6(client, "new_", client->active_lease->options); + script_go(client); + + go_daemon(); + + if (client->old_lease != NULL) { + dhc6_lease_destroy(&client->old_lease, MDL); + client->old_lease = NULL; + } + + /* Schedule events. */ + dhc6_check_irt(client); +} + +/* While informed, ignore packets. + */ +void +informed_handler(struct packet *packet, struct client_state *client) +{ + log_debug("RCV: Input packets are ignored once bound."); +} + /* make_client6_options() fetches option caches relevant to the client's * scope and places them into the sent_options cache. This cache is later * used to populate DHCPv6 output packets with options. diff --git a/client/dhclient.c b/client/dhclient.c index 1fb1da83c..d4bc2c907 100644 --- a/client/dhclient.c +++ b/client/dhclient.c @@ -75,13 +75,16 @@ int client_env_count=0; int onetry=0; int quiet=1; int nowait=0; +int stateless=0; char *mockup_relay = NULL; +void run_stateless(int exit_mode); + static void usage PROTO ((void)); static isc_result_t write_duid(struct data_string *duid); -int +int main(int argc, char **argv) { int fd; int i; @@ -90,8 +93,8 @@ main(int argc, char **argv) { unsigned seed; char *server = (char *)0; isc_result_t status; - int exit_mode = 0; - int release_mode = 0; + int exit_mode = 0; + int release_mode = 0; struct timeval tv; omapi_object_t *listener; isc_result_t result; @@ -108,24 +111,24 @@ main(int argc, char **argv) { /* Initialize client globals. */ memset(&default_duid, 0, sizeof(default_duid)); - /* Make sure that file descriptors 0 (stdin), 1, (stdout), and - 2 (stderr) are open. To do this, we assume that when we - open a file the lowest available file descriptor is used. */ - fd = open("/dev/null", O_RDWR); - if (fd == 0) - fd = open("/dev/null", O_RDWR); - if (fd == 1) - fd = open("/dev/null", O_RDWR); - if (fd == 2) - log_perror = 0; /* No sense logging to /dev/null. */ - else if (fd != -1) - close(fd); + /* Make sure that file descriptors 0 (stdin), 1, (stdout), and + 2 (stderr) are open. To do this, we assume that when we + open a file the lowest available file descriptor is used. */ + fd = open("/dev/null", O_RDWR); + if (fd == 0) + fd = open("/dev/null", O_RDWR); + if (fd == 1) + fd = open("/dev/null", O_RDWR); + if (fd == 2) + log_perror = 0; /* No sense logging to /dev/null. */ + else if (fd != -1) + close(fd); openlog ("dhclient", LOG_NDELAY, LOG_DAEMON); #if !(defined (DEBUG) || defined (__CYGWIN32__)) setlogmask (LOG_UPTO (LOG_INFO)); -#endif +#endif /* Set up the OMAPI. */ status = omapi_init (); @@ -142,7 +145,7 @@ main(int argc, char **argv) { dhcp_interface_startup_hook = dhclient_interface_startup_hook; for (i = 1; i < argc; i++) { - if (!strcmp(argv[i], "-r")) { + if (!strcmp(argv[i], "-r")) { release_mode = 1; no_daemon = 1; #ifdef DHCPv6 @@ -159,10 +162,10 @@ main(int argc, char **argv) { local_family_set = 1; local_family = AF_INET6; #endif /* DHCPv6 */ - } else if (!strcmp (argv [i], "-x")) { /* eXit, no release */ - release_mode = 0; - no_daemon = 0; - exit_mode = 1; + } else if (!strcmp (argv [i], "-x")) { /* eXit, no release */ + release_mode = 0; + no_daemon = 0; + exit_mode = 1; } else if (!strcmp (argv [i], "-p")) { if (++i == argc) usage (); @@ -172,25 +175,25 @@ main(int argc, char **argv) { } else if (!strcmp (argv [i], "-d")) { no_daemon = 1; quiet = 0; - } else if (!strcmp (argv [i], "-pf")) { - if (++i == argc) - usage (); - path_dhclient_pid = argv [i]; + } else if (!strcmp (argv [i], "-pf")) { + if (++i == argc) + usage (); + path_dhclient_pid = argv [i]; no_dhclient_pid = 1; - } else if (!strcmp (argv [i], "-cf")) { - if (++i == argc) - usage (); - path_dhclient_conf = argv [i]; + } else if (!strcmp (argv [i], "-cf")) { + if (++i == argc) + usage (); + path_dhclient_conf = argv [i]; no_dhclient_conf = 1; - } else if (!strcmp (argv [i], "-lf")) { - if (++i == argc) - usage (); - path_dhclient_db = argv [i]; + } else if (!strcmp (argv [i], "-lf")) { + if (++i == argc) + usage (); + path_dhclient_db = argv [i]; no_dhclient_db = 1; } else if (!strcmp (argv [i], "-sf")) { if (++i == argc) usage (); - path_dhclient_script = argv [i]; + path_dhclient_script = argv [i]; no_dhclient_script = 1; } else if (!strcmp (argv [i], "-1")) { onetry = 1; @@ -208,7 +211,7 @@ main(int argc, char **argv) { nowait = 1; } else if (!strcmp (argv [i], "-n")) { /* do not start up any interfaces */ - interfaces_requested = 1; + interfaces_requested = -1; } else if (!strcmp (argv [i], "-w")) { /* do not exit if there are no broadcast interfaces. */ persist = 1; @@ -223,18 +226,27 @@ main(int argc, char **argv) { tmp -> next = client_env; client_env = tmp; client_env_count++; + } else if (!strcmp(argv[i], "-S")) { + if (local_family_set && (local_family == AF_INET)) { + usage (); + } + local_family_set = 1; + local_family = AF_INET6; + stateless = 1; } else if (!strcmp(argv[i], "-v")) { quiet = 0; } else if (!strcmp (argv [i], "--version")) { log_info ("isc-dhclient-%s", PACKAGE_VERSION); exit (0); - } else if (argv [i][0] == '-') { - usage (); + } else if (argv [i][0] == '-') { + usage (); + } else if (interfaces_requested < 0) { + usage (); } else { - struct interface_info *tmp = (struct interface_info *)0; + struct interface_info *tmp = (struct interface_info *)0; status = interface_allocate (&tmp, MDL); - if (status != ISC_R_SUCCESS) - log_fatal ("Can't record interface %s:%s", + if (status != ISC_R_SUCCESS) + log_fatal ("Can't record interface %s:%s", argv [i], isc_result_totext (status)); if (strlen(argv[i]) >= sizeof(tmp->name)) log_fatal("%s: interface name too long (is %ld)", @@ -246,9 +258,9 @@ main(int argc, char **argv) { interface_dereference (&interfaces, MDL); } interface_reference (&interfaces, tmp, MDL); - tmp -> flags = INTERFACE_REQUESTED; - interfaces_requested = 1; - } + tmp -> flags = INTERFACE_REQUESTED; + interfaces_requested++; + } } if (!no_dhclient_conf && (s = getenv ("PATH_DHCLIENT_CONF"))) { @@ -289,7 +301,7 @@ main(int argc, char **argv) { if (path_dhclient_db == NULL) log_fatal("%s: %s", path, strerror(errno)); } - + if (path_dhclient_script[0] != '/') { char *path = dmalloc(PATH_MAX, MDL); if (path == NULL) @@ -298,7 +310,7 @@ main(int argc, char **argv) { if (path_dhclient_script == NULL) log_fatal("%s: %s", path, strerror(errno)); } - + /* first kill off any currently running client */ if (release_mode || exit_mode) { FILE *pidfd; @@ -368,6 +380,15 @@ main(int argc, char **argv) { inaddr_any.s_addr = INADDR_ANY; + /* Stateless special case. */ + if (stateless) { + if (release_mode || (interfaces_requested != 1)) { + usage (); + } + run_stateless (exit_mode); + return 0; + } + /* Discover all the network interfaces. */ discover_interfaces (DISCOVER_UNCONFIGURED); @@ -404,7 +425,7 @@ main(int argc, char **argv) { for (ip = interfaces; ip; ip = ip -> next) { /* If interfaces were specified, don't configure interfaces that weren't specified! */ - if (interfaces_requested && + if ((interfaces_requested > 0) && ((ip -> flags & (INTERFACE_REQUESTED | INTERFACE_AUTOMATIC)) != INTERFACE_REQUESTED)) @@ -427,7 +448,7 @@ main(int argc, char **argv) { are relevant should be running, so now we once again call discover_interfaces(), and this time ask it to actually set up the interfaces. */ - discover_interfaces (interfaces_requested + discover_interfaces (interfaces_requested != 0 ? DISCOVER_REQUESTED : DISCOVER_RUNNING); @@ -463,13 +484,13 @@ main(int argc, char **argv) { for (ip = interfaces ; ip != NULL ; ip = ip->next) { for (client = ip->client ; client != NULL ; client = client->next) { - if (release_mode) { - start_release6(client); - continue; - } else if (exit_mode) { - unconfigure6(client, "STOP6"); - continue; - } + if (release_mode) { + start_release6(client); + continue; + } else if (exit_mode) { + unconfigure6(client, "STOP6"); + continue; + } /* If we have a previous binding, Confirm * that we can (or can't) still use it. @@ -481,16 +502,16 @@ main(int argc, char **argv) { start_init6(client); } } - } else + } else #endif /* DHCPv6 */ { for (ip = interfaces ; ip ; ip = ip->next) { ip->flags |= INTERFACE_RUNNING; for (client = ip->client ; client ; client = client->next) { - if (exit_mode) - state_stop(client); - else if (release_mode) + if (exit_mode) + state_stop(client); + else if (release_mode) do_release(client); else { client->state = S_INIT; @@ -575,9 +596,9 @@ static void usage () log_error ("Usage: dhclient %s %s", #ifdef DHCPv6 - "[-4|-6] [-1dvrx] [-nw] [-p ]", + "[-4|-6] [-S1dvrx] [-nw] [-p ]", #else /* DHCPv6 */ - "[-1dvrx] [-nw] [-p ]", + "[-1dvrx] [-nw] [-p ]", #endif /* DHCPv6 */ "[-s server]"); log_error (" [-cf config-file] [-lf lease-file]%s", @@ -585,6 +606,88 @@ static void usage () log_fatal (" [-sf script-file] [interface]"); } +void run_stateless(int exit_mode) +{ + struct client_state *client; + omapi_object_t *listener; + isc_result_t result; + + /* Discover the network interface. */ + discover_interfaces(DISCOVER_REQUESTED); + + if (!interfaces) + usage(); + + /* Parse the dhclient.conf file. */ + read_client_conf(); + + /* Parse the lease database. */ + read_client_leases(); + + /* Establish a default DUID. */ + if (default_duid.len == 0) { + if (default_duid.buffer != NULL) + data_string_forget(&default_duid, MDL); + + form_duid(&default_duid, MDL); + } + + /* Start a configuration state machine. */ + for (client = interfaces->client ; + client != NULL ; + client = client->next) { + if (exit_mode) { + unconfigure6(client, "STOP6"); + continue; + } + start_info_request6(client); + } + if (exit_mode) + return; + + /* Start up a listener for the object management API protocol. */ + if (top_level_config.omapi_port != -1) { + listener = (omapi_object_t *)0; + result = omapi_generic_new(&listener, MDL); + if (result != ISC_R_SUCCESS) + log_fatal("Can't allocate new generic object: %s\n", + isc_result_totext(result)); + result = omapi_protocol_listen(listener, + (unsigned) + top_level_config.omapi_port, + 1); + if (result != ISC_R_SUCCESS) + log_fatal("Can't start OMAPI protocol: %s", + isc_result_totext(result)); + } + + /* Set up the packet handler... */ + dhcpv6_packet_handler = do_packet6; + +#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \ + defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) + dmalloc_cutoff_generation = dmalloc_generation; + dmalloc_longterm = dmalloc_outstanding; + dmalloc_outstanding = 0; +#endif + + /* If we're not supposed to wait before getting the address, + don't. */ + if (nowait) + go_daemon(); + + /* If we're not going to daemonize, write the pid file + now. */ + if (no_daemon || nowait) + write_client_pid_file(); + + /* Start dispatching packets and timeouts... */ + dispatch(); + + /*NOTREACHED*/ + return; +} + isc_result_t find_class (struct class **c, const char *s, const char *file, int line) { @@ -619,7 +722,7 @@ int find_subnet (struct subnet **sp, } /* Individual States: - * + * * Each routine is called from the dhclient_state_machine() in one of * these conditions: * -> entering INIT state @@ -781,7 +884,7 @@ void state_selecting (cpp) /* Add an immediate timeout to send the first DHCPREQUEST packet. */ send_request (client); -} +} /* state_requesting is called when we receive a DHCPACK message after having sent out one or more DHCPREQUEST packets. */ @@ -794,7 +897,7 @@ void dhcpack (packet) struct client_lease *lease; struct option_cache *oc; struct data_string ds; - + /* If we're not receptive to an offer right now, or if the offer has an unrecognizable transaction id, then just drop it. */ for (client = ip -> client; client; client = client -> next) { @@ -854,7 +957,7 @@ void dhcpack (packet) if (!client -> new -> expiry) { log_error ("no expiry time on offered lease."); /* XXX this is going to be bad - if this _does_ - XXX happen, we should probably dynamically + XXX happen, we should probably dynamically XXX disqualify the DHCP server that gave us the XXX bad packet from future selections and XXX then go back into the init state. */ @@ -999,7 +1102,7 @@ void bind_lease (client) if (client->config->do_forward_update) dhclient_schedule_updates(client, &client->active->address, 1); -} +} /* state_bound is called when we've successfully bound to a particular lease, but the renewal time on that lease has expired. We are @@ -1043,7 +1146,7 @@ void state_bound (cpp) /* Send the first packet immediately. */ send_request (client); -} +} /* state_stop is called when we've been told to shut down. We unconfigure the interfaces, and then stop operating until told otherwise. */ @@ -1067,7 +1170,7 @@ void state_stop (cpp) script_write_params(client, "alias_", client->alias); script_go(client); } -} +} int commit_leases () { @@ -1119,7 +1222,7 @@ void bootp (packet) return; } } - + dhcpoffer (packet); } @@ -1175,7 +1278,7 @@ void dhcp (packet) } #ifdef DHCPv6 -void +void dhcpv6(struct packet *packet) { struct iaddrmatchlist *ap; struct client_state *client; @@ -1201,8 +1304,11 @@ dhcpv6(struct packet *packet) { /* Screen out nonsensical messages. */ switch(packet->dhcpv6_msg_type) { case DHCPV6_ADVERTISE: - case DHCPV6_REPLY: case DHCPV6_RECONFIGURE: + if (stateless) + return; + /* Falls through */ + case DHCPV6_REPLY: log_info("RCV: %s message on %s from %s.", dhcpv6_type_names[packet->dhcpv6_msg_type], packet->interface->name, piaddr(packet->client_addr)); @@ -1239,10 +1345,10 @@ void dhcpoffer (packet) const char *name = packet -> packet_type ? "DHCPOFFER" : "BOOTREPLY"; char obuf [1024]; struct timeval tv; - + #ifdef DEBUG_PACKET dump_packet (packet); -#endif +#endif /* Find a client state that matches the xid... */ for (client = ip -> client; client; client = client -> next) @@ -1482,7 +1588,7 @@ struct client_lease *packet_to_lease (packet, client) (struct group *)0); return lease; -} +} void dhcpnak (packet) struct packet *packet; @@ -1592,7 +1698,7 @@ void send_discover (cpp) if (client -> medium) { client -> medium = client -> medium -> next; increase = 0; - } + } if (!client -> medium) { if (fail) log_fatal ("No valid media types for %s!", @@ -1601,7 +1707,7 @@ void send_discover (cpp) client -> config -> media; increase = 1; } - + log_info ("Trying medium \"%s\" %d", client -> medium -> string, increase); script_init (client, "MEDIUM", client -> medium); @@ -1628,7 +1734,7 @@ void send_discover (cpp) + (random() % client->config->backoff_cutoff); } else if (!client->interval) client->interval = client->config->initial_interval; - + /* If the backoff would take us to the panic timeout, just use that as the interval. */ if (cur_time + client -> interval > @@ -1853,7 +1959,7 @@ void send_request (cpp) client -> interval += ((random () >> 2) % (2 * client -> interval)); } - + /* Don't backoff past cutoff. */ if (client -> interval > client -> config -> backoff_cutoff) @@ -2191,7 +2297,7 @@ void make_request (client, lease) (struct lease *)0, client, /* maximum packet size */1500, (struct option_state *)0, - client -> sent_options, + client -> sent_options, /* scope */ &global_scope, /* overload */ 0, /* terminate */0, @@ -2467,7 +2573,7 @@ write_options(struct client_state *client, struct option_state *options, for (i = 0; i < options->universe_count; i++) { option_space_foreach(NULL, NULL, client, NULL, options, - &global_scope, universes[i], + &global_scope, universes[i], (char *)preamble, write_lease_option); } } @@ -2794,7 +2900,7 @@ void script_init (client, reason, medium) } client -> env = (struct string_list *)0; client -> envc = 0; - + if (client -> interface) { client_envadd (client, "", "interface", "%s", client -> interface -> name); @@ -3048,7 +3154,7 @@ int dhcp_option_ev_name (buf, buflen, option) if (option -> universe != &dhcp_universe) { s = option -> universe -> name; i = 0; - } else { + } else { s = option -> name; i = 1; } @@ -3104,9 +3210,9 @@ void go_daemon () pid = setsid (); /* Close standard I/O descriptors. */ - close(0); - close(1); - close(2); + close(0); + close(1); + close(2); /* Reopen them on /dev/null. */ open("/dev/null", O_RDWR); @@ -3115,7 +3221,7 @@ void go_daemon () write_client_pid_file (); - chdir("/"); + chdir("/"); } void write_client_pid_file () @@ -3172,7 +3278,7 @@ void client_location_changed () } } -void do_release(client) +void do_release(client) struct client_state *client; { struct data_string ds; @@ -3209,10 +3315,10 @@ void do_release(client) client -> destination = iaddr_broadcast; client -> first_sending = cur_time; client -> interval = client -> config -> initial_interval; - + /* Zap the medium list... */ client -> medium = (struct string_list *)0; - + /* Send out the first and only DHCPRELEASE packet. */ send_release (client); @@ -3318,8 +3424,8 @@ isc_result_t dhclient_interface_startup_hook (struct interface_info *interface) ip -> client -> alias); script_go (ip -> client); } - - discover_interfaces (interfaces_requested + + discover_interfaces (interfaces_requested != 0 ? DISCOVER_REQUESTED : DISCOVER_RUNNING); @@ -3501,7 +3607,7 @@ isc_result_t client_dns_update (struct client_state *client, int addp, (struct option_state *)0, &global_scope, oc, MDL)) return ISC_R_SUCCESS; - + /* If we set the "server, please update" flag, or didn't set it to false, don't do the update. */ if (!(oc = lookup_option (&fqdn_universe, client -> sent_options, @@ -3512,12 +3618,12 @@ isc_result_t client_dns_update (struct client_state *client, int addp, (struct option_state *)0, &global_scope, oc, MDL)) return ISC_R_SUCCESS; - + /* If no FQDN option was supplied, don't do the update. */ memset (&ddns_fwd_name, 0, sizeof ddns_fwd_name); if (!(oc = lookup_option (&fqdn_universe, client -> sent_options, FQDN_FQDN)) || - !evaluate_option_cache (&ddns_fwd_name, (struct packet *)0, + !evaluate_option_cache (&ddns_fwd_name, (struct packet *)0, (struct lease *)0, client, client -> sent_options, (struct option_state *)0, @@ -3592,7 +3698,7 @@ isc_result_t client_dns_update (struct client_state *client, int addp, &ddns_dhcid); } else rcode = ISC_R_FAILURE; - + data_string_forget (&ddns_fwd_name, MDL); data_string_forget (&ddns_dhcid, MDL); return rcode; diff --git a/includes/dhcp6.h b/includes/dhcp6.h index 4b526b050..7cf7f3aa5 100644 --- a/includes/dhcp6.h +++ b/includes/dhcp6.h @@ -205,3 +205,9 @@ struct dhcpv6_relay_packet { * when the Unix epoch began. */ #define DUID_TIME_EPOCH 946684800 + +/* Information-Request Time option (RFC 4242) */ + +#define IRT_DEFAULT 86400 +#define IRT_MINIMUM 600 + diff --git a/includes/dhcpd.h b/includes/dhcpd.h index 85046d047..7a0741c75 100644 --- a/includes/dhcpd.h +++ b/includes/dhcpd.h @@ -2525,6 +2525,7 @@ void dhcpv6_client_assignments(void); void form_duid(struct data_string *duid, const char *file, int line); void dhc6_lease_destroy(struct dhc6_lease **src, const char *file, int line); void start_init6(struct client_state *client); +void start_info_request6(struct client_state *client); void start_confirm6(struct client_state *client); void start_release6(struct client_state *client); void start_selecting6(struct client_state *client); -- 2.47.3