#ifndef lint
static char ocopyright[] =
-"$Id: dhc6.c,v 1.5 2007/05/19 18:47:13 dhankins Exp $ Copyright (c) 2006-2007 Internet Systems Consortium. All rights reserved.\n";
+"$Id: dhc6.c,v 1.6 2007/06/06 22:57:31 each Exp $ Copyright (c) 2006-2007 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
void reply_handler(struct packet *packet, struct client_state *client);
static isc_result_t dhc6_add_ia(struct client_state *client,
struct data_string *packet,
- struct dhc6_lease *lease);
+ struct dhc6_lease *lease,
+ u_int8_t message);
static void dhc6_merge_lease(struct dhc6_lease *src, struct dhc6_lease *dst);
void do_select6(void *input);
void do_refresh6(void *input);
+static void do_release6(void *input);
static void start_bound(struct client_state *client);
void bound_handler(struct packet *packet, struct client_state *client);
void start_renew6(void *input);
&dhcpv6_universe);
/* Append IA's. */
- memset(&ia_data, 0, sizeof(ia_data));
- memset(&addr_data, 0, sizeof(addr_data));
- for (ia = client->active_lease->bindings ; ia != NULL ;
- ia = ia->next) {
- if (!buffer_allocate(&ia_data.buffer, 12, MDL)) {
- log_error("Unable to allocate memory for IA_NA.");
- data_string_forget(&ds, MDL);
- return;
- }
+ if (dhc6_add_ia(client, &ds, client->active_lease,
+ DHCPV6_CONFIRM) != ISC_R_SUCCESS) {
+ data_string_forget(&ds, MDL);
+ return;
+ }
- /* Copy the IAID into the packet buffer. */
- memcpy(ia_data.buffer->data, ia->iaid, 4);
- /* Set t1 and t2 to zero (RFC3315 section 17.1.3) */
- memset(ia_data.buffer->data + 4, 0, 8);
+ /* Transmit and wait. */
- log_debug("XMT: X-- IA_NA %s",
- print_hex_1(4, ia_data.buffer->data, 55));
+ log_info("XMT: Confirm on %s, interval %ld.",
+ client->name ? client->name : client->interface->name,
+ client->RT);
- for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
- if (addr->address.len != 16) {
- log_error("Illegal IPv6 address length (%d), "
- "ignoring. (%s:%d)",
- addr->address.len, MDL);
- continue;
- }
+ send_ret = send_packet6(client->interface, ds.data, ds.len,
+ &DHCPv6DestAddr);
+ if (send_ret != ds.len) {
+ log_error("dhc6: sendpacket6() sent %d of %d bytes",
+ send_ret, ds.len);
+ }
- if (!buffer_allocate(&addr_data.buffer, 24, MDL)) {
- log_error("Unable to allocate memory for "
- "IAADDR.");
- data_string_forget(&ia_data, MDL);
- data_string_forget(&ds, MDL);
- return;
- }
+ data_string_forget(&ds, MDL);
- /* Copy the address into the packet buffer. */
- memcpy(addr_data.buffer->data, addr->address.iabuf,
- 16);
+ add_timeout(cur_time + client->RT, do_confirm6, client, NULL, NULL);
- /* Set preferred and max life to zero, per 17.1.3. */
- memset(addr_data.buffer->data + 16, 0, 8);
+ dhc6_retrans_advance(client);
+}
- log_debug("XMT: | X-- Confirm Address %s",
- piaddr(addr->address));
+/*
+ * Release addresses.
+ */
+void
+start_release6(struct client_state *client)
+{
+ struct data_string packet;
- append_option(&ia_data, &dhcpv6_universe,
- iaaddr_option, &addr_data);
+ /* Cancel any pending transmissions */
+ cancel_timeout(do_confirm6, client);
+ cancel_timeout(do_select6, client);
+ cancel_timeout(do_refresh6, client);
+ cancel_timeout(do_release6, client);
+ client->state = S_STOPPED;
+
+ /*
+ * It is written: "The client MUST NOT use any of the addresses it
+ * is releasing as the source address in the Release message or in
+ * any subsequently transmitted message." So unconfigure now.
+ */
+ unconfigure6(client, "RELEASE6");
+
+ /* Fetch a 24-bit transaction ID. */
+ dhc6_new_xid(client);
+
+ /* Set timers per RFC3315 section 18.1.1. */
+ client->IRT = REL_TIMEOUT;
+ client->MRT = 0;
+ client->MRC = REL_MAX_RC;
+ client->MRD = 0;
- data_string_forget(&addr_data, MDL);
+ dhc6_retrans_init(client);
+ client->v6_handler = reply_handler;
+
+ /* ("re")transmit the first packet. */
+ do_release6(client);
+}
+/*
+ * do_release6() creates a Release packet and transmits it.
+ */
+static void
+do_release6(void *input)
+{
+ struct sockaddr_in6 unicast, *dest_addr = &DHCPv6DestAddr;
+ struct client_state *client;
+ struct dhc6_ia *ia;
+ struct dhc6_addr *addr;
+ struct data_string ds;
+ struct data_string ia_data;
+ struct data_string addr_data;
+ struct option_cache *oc;
+ struct dhc6_lease *lease;
+ int send_ret;
+
+ client = input;
+
+ if (client->active_lease == NULL)
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ if ((client->MRC != 0) && (client->txcount > client->MRC)) {
+ log_info("Max retransmission count exceeded.");
+ return;
+ }
+
+ /*
+ * Check whether the server has sent a unicast option; if so, we can
+ * use the address it specified.
+ */
+ oc = lookup_option(&dhcpv6_universe, lease->options, D6O_UNICAST);
+ if (oc && evaluate_option_cache(&ds, NULL, NULL, NULL,
+ lease->options, NULL, &global_scope,
+ oc, MDL)) {
+ if (ds.len < 16) {
+ log_error("Invalid unicast option length %d.", ds.len);
+ } else {
+ memset(&unicast, 0, sizeof(DHCPv6DestAddr));
+ unicast.sin6_family = AF_INET6;
+ unicast.sin6_port = remote_port;
+ memcpy(&unicast.sin6_addr, ds.data, 16);
+ dest_addr = &unicast;
}
- append_option(&ds, &dhcpv6_universe, ia_na_option, &ia_data);
+ data_string_forget(&ds, MDL);
+ }
- data_string_forget(&ia_data, MDL);
+ memset(&ds, 0, sizeof(ds));
+ if (!buffer_allocate(&ds.buffer, 4, MDL)) {
+ log_error("Unable to allocate memory for Release.");
+ return;
}
- /* Transmit and wait. */
+ ds.data = ds.buffer->data;
+ ds.len = 4;
+ ds.buffer->data[0] = DHCPV6_RELEASE;
+ memcpy(ds.buffer->data + 1, client->dhcpv6_transaction_id, 3);
- log_info("XMT: Confirm on %s, interval %ld.",
+ log_debug("XMT: Forming Release.");
+ make_client6_options(client, &client->sent_options,
+ client->active_lease, DHCPV6_RELEASE);
+ dhcpv6_universe.encapsulate(&ds, NULL, NULL, client, NULL,
+ client->sent_options, &global_scope,
+ &dhcpv6_universe);
+
+ /* Append IA's. */
+ if (dhc6_add_ia(client, &ds, client->active_lease,
+ DHCPV6_RELEASE) != ISC_R_SUCCESS) {
+ data_string_forget(&ds, MDL);
+ return;
+ }
+
+ /* Transmit and wait. */
+ log_info("XMT: Release on %s, interval %ld.",
client->name ? client->name : client->interface->name,
client->RT);
data_string_forget(&ds, MDL);
- add_timeout(cur_time + client->RT, do_confirm6, client, NULL, NULL);
-
+ add_timeout(cur_time + client->RT, do_release6, client, NULL, NULL);
dhc6_retrans_advance(client);
}
&dhcpv6_universe);
/* Now append any IA_NA's, and within them any IAADDRs. */
- if (dhc6_add_ia(client, &ds, lease) != ISC_R_SUCCESS) {
+ if (dhc6_add_ia(client, &ds, lease, DHCPV6_REQUEST) != ISC_R_SUCCESS) {
data_string_forget(&ds, MDL);
return;
}
}
/* For each IA in the lease, for each address in the IA, append that
- * information onto the packet-so-far in a "what I would like to have
- * please" fashion.
+ * information onto the packet-so-far.
*/
static isc_result_t
dhc6_add_ia(struct client_state *client, struct data_string *packet,
- struct dhc6_lease *lease)
+ struct dhc6_lease *lease, u_int8_t message)
{
struct data_string iads;
struct data_string addrds;
struct dhc6_ia *ia;
isc_result_t rval = ISC_R_SUCCESS;
TIME t1, t2;
-
- /* Now appended any IA_NA's, and within them any IAADDRs. */
+
memset(&iads, 0, sizeof(iads));
memset(&addrds, 0, sizeof(addrds));
- for (ia = lease->bindings ; ia ; ia = ia->next) {
+ for (ia = lease->bindings;
+ ia != NULL && rval == ISC_R_SUCCESS;
+ ia = ia->next) {
if (!buffer_allocate(&iads.buffer, 12, MDL)) {
- log_error("Unable to allocate memory for IA.");
+ log_error("Unable to allocate memory for IA_NA.");
+ rval = ISC_R_NOMEMORY;
break;
}
- iads.data = iads.buffer->data;
- iads.len = 12;
+ /* Copy the IAID into the packet buffer. */
memcpy(iads.buffer->data, ia->iaid, 4);
+ iads.data = iads.buffer->data;
+ iads.len = 12;
- t1 = client->config->requested_lease / 2;
- t2 = t1 + (t1 / 2);
+ switch (message) {
+ case DHCPV6_REQUEST:
+ case DHCPV6_RENEW:
+ case DHCPV6_REBIND:
+
+ t1 = client->config->requested_lease / 2;
+ t2 = t1 + (t1 / 2);
#if MAX_TIME > 0xffffffff
- if (t1 > 0xffffffff)
- t1 = 0xffffffff;
- if (t2 > 0xffffffff)
- t2 = 0xffffffff;
+ if (t1 > 0xffffffff)
+ t1 = 0xffffffff;
+ if (t2 > 0xffffffff)
+ t2 = 0xffffffff;
#endif
- putULong(iads.buffer->data + 4, t1);
- putULong(iads.buffer->data + 8, t2);
+ putULong(iads.buffer->data + 4, t1);
+ putULong(iads.buffer->data + 8, t2);
+
+ log_debug("XMT: X-- IA_NA %s",
+ print_hex_1(4, iads.data, 59));
+ log_debug("XMT: | X-- Requested renew +%u",
+ (unsigned) t1);
+ log_debug("XMT: | X-- Requested rebind +%u",
+ (unsigned) t2);
+ break;
+
+ case DHCPV6_CONFIRM:
+ case DHCPV6_RELEASE:
+ /* Set t1 and t2 to zero; server will ignore them */
+ memset(iads.buffer->data + 4, 0, 8);
+ log_debug("XMT: X-- IA_NA %s",
+ print_hex_1(4, iads.buffer->data, 55));
+
+ break;
+
+ default:
+ log_fatal("Impossible condition at %s:%d.", MDL);
+ }
- log_debug("XMT: X-- IA_NA %s",
- print_hex_1(4, iads.data, 59));
- log_debug("XMT: | X-- Requested renew +%u", (unsigned)t1);
- log_debug("XMT: | X-- Requested rebind +%u", (unsigned)t2);
+ for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
+ if (addr->address.len != 16) {
+ log_error("Illegal IPv6 address length (%d), "
+ "ignoring. (%s:%d)",
+ addr->address.len, MDL);
+ continue;
+ }
- for (addr = ia->addrs ; addr ; addr = addr->next) {
if (!buffer_allocate(&addrds.buffer, 24, MDL)) {
log_error("Unable to allocate memory for "
"IAADDR.");
+ rval = ISC_R_NOMEMORY;
break;
}
+
addrds.data = addrds.buffer->data;
addrds.len = 24;
+ /* Copy the address into the packet buffer. */
memcpy(addrds.buffer->data, addr->address.iabuf, 16);
- t1 = client->config->requested_lease;
- t2 = t1 + 300;
- putULong(addrds.buffer->data + 16, t1);
- putULong(addrds.buffer->data + 20, t2);
- log_debug("XMT: | | X-- IAADDR %s",
- piaddr(addr->address));
- log_debug("XMT: | | | X-- Preferred lifetime +%u",
- (unsigned)t1);
- log_debug("XMT: | | | X-- Max lifetime +%u",
- (unsigned)t2);
+ /* Copy in additional information as appropriate */
+ switch (message) {
+ case DHCPV6_REQUEST:
+ case DHCPV6_RENEW:
+ case DHCPV6_REBIND:
+ t1 = client->config->requested_lease;
+ t2 = t1 + 300;
+ putULong(addrds.buffer->data + 16, t1);
+ putULong(addrds.buffer->data + 20, t2);
+
+ log_debug("XMT: | | X-- IAADDR %s",
+ piaddr(addr->address));
+ log_debug("XMT: | | | X-- Preferred "
+ "lifetime +%u", (unsigned)t1);
+ log_debug("XMT: | | | X-- Max lifetime +%u",
+ (unsigned)t2);
+
+ break;
+
+ case DHCPV6_CONFIRM:
+ /*
+ * Set preferred and max life to zero,
+ * per 17.1.3.
+ */
+ memset(addrds.buffer->data + 16, 0, 8);
+ log_debug("XMT: | X-- Confirm Address %s",
+ piaddr(addr->address));
+ break;
+
+ case DHCPV6_RELEASE:
+ /* Preferred and max life are irrelevant */
+ memset(addrds.buffer->data + 16, 0, 8);
+ log_debug("XMT: | X-- Release Address %s",
+ piaddr(addr->address));
+ break;
+
+ default:
+ log_fatal("Impossible condition at %s:%d.",
+ MDL);
+ }
append_option(&iads, &dhcpv6_universe, iaaddr_option,
- &addrds);
+ &addrds);
data_string_forget(&addrds, MDL);
}
- /* It doesn't make sense to make a request without an
+ /*
+ * It doesn't make sense to make a request without an
* address.
*/
- if (ia->addrs != NULL) {
+ if (ia->addrs == NULL) {
+ log_debug("!!!: V IA_NA has no IAADDRs - removed.");
+ rval = ISC_R_FAILURE;
+ } else if (rval == ISC_R_SUCCESS) {
log_debug("XMT: V IA_NA appended.");
append_option(packet, &dhcpv6_universe, ia_na_option,
&iads);
- } else {
- log_debug("!!!: V IA_NA has no IAADDRs - removed.");
- rval = ISC_R_FAILURE;
}
data_string_forget(&iads, MDL);
}
- return rval;
+ return rval;
}
/* reply_handler() accepts a Reply while we're attempting Select or Renew or
cancel_timeout(do_confirm6, client);
cancel_timeout(do_select6, client);
cancel_timeout(do_refresh6, client);
+ cancel_timeout(do_release6, client);
/* Action was taken, so now that we've torn down our scheduled
* retransmissions, return.
client->selected_lease = NULL;
}
+ /* If this is in response to a Release, clean up and return. */
+ if (client->state == S_STOPPED) {
+ if (client->active_lease == NULL)
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ dhc6_lease_destroy(client->active_lease, MDL);
+ client->active_lease = NULL;
+ return;
+ }
+
/* If this is in response to a confirm, we use the lease we've
* already got, not the reply we were sent.
*/
client->sent_options, &global_scope,
&dhcpv6_universe);
- if (dhc6_add_ia(client, &ds, lease) != ISC_R_SUCCESS) {
+ /* Append IA's */
+ if (dhc6_add_ia(client, &ds, lease,
+ client->refresh_type) != ISC_R_SUCCESS) {
data_string_forget(&ds, MDL);
return;
}
dhc6_check_times(client);
}
+/*
+ * 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.
+ */
+void
+unconfigure6(struct client_state *client, const char *reason)
+{
+ struct dhc6_lease *lease;
+ struct dhc6_ia *ia;
+ struct dhc6_addr *addr;
+
+ if (client->active_lease == NULL)
+ return;
+
+ for (ia = client->active_lease->bindings ; ia != NULL ; ia = ia->next) {
+ for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
+ script_init(client, reason, NULL);
+ dhc6_marshall_values("old_", client, lease, ia, addr);
+ script_go(client);
+
+ if (client->config->do_forward_update)
+ client_dns_update(client, 0, 0, &addr->address);
+ }
+ }
+}
+
/* 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.
#ifndef lint
static char ocopyright[] =
-"$Id: dhclient.c,v 1.151 2007/05/21 18:16:54 dhankins Exp $ Copyright (c) 2004-2007 Internet Systems Consortium. All rights reserved.\n";
+"$Id: dhclient.c,v 1.152 2007/06/06 22:57:32 each Exp $ Copyright (c) 2004-2007 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
static void usage PROTO ((void));
-void do_release(struct client_state *);
-
static isc_result_t write_duid(struct data_string *duid);
int
unsigned seed;
char *server = (char *)0;
isc_result_t status;
+ int exit_mode = 0;
int release_mode = 0;
omapi_object_t *listener;
isc_result_t result;
} else if (!strcmp(argv[i], "-r")) {
release_mode = 1;
no_daemon = 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 ();
log_fatal("Impossible condition at %s:%d.", MDL);
/* first kill of any currently running client */
- if (release_mode) {
+ if (release_mode || exit_mode) {
FILE *pidfd;
pid_t oldpid;
long temp;
log_info ("No broadcast interfaces found - exiting.");
exit (0);
}
- } else if (!release_mode) {
+ } else if (!release_mode && !exit_mode) {
/* Call the script with the list of interfaces. */
for (ip = interfaces; ip; ip = ip -> next) {
/* If interfaces were specified, don't configure
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 we have a previous binding, Confirm
* that we can (or can't) still use it.
*/
ip->flags |= INTERFACE_RUNNING;
for (client = ip->client ; client ;
client = client->next) {
- if (release_mode)
+ if (exit_mode)
+ state_stop(client);
+ else if (release_mode)
do_release(client);
else {
client->state = S_INIT;
}
}
- if (release_mode)
+ if (release_mode || exit_mode)
return 0;
/* Start up a listener for the object management API protocol. */
log_info (arr);
log_info (url);
- log_error ("Usage: dhclient [-1dvr] [-nw] [-p <port>] %s",
+ log_error ("Usage: dhclient [-1dvrx] [-nw] [-p <port>] %s",
"[-s server]");
log_error (" [-cf config-file] [-lf lease-file]%s",
"[-pf pid-file] [-e VAR=val]");
void *cpp;
{
struct client_state *client = cpp;
- int i;
/* Cancel all timeouts. */
- cancel_timeout (state_selecting, client);
- cancel_timeout (send_discover, client);
- cancel_timeout (send_request, client);
- cancel_timeout (state_bound, client);
+ cancel_timeout(state_selecting, client);
+ cancel_timeout(send_discover, client);
+ cancel_timeout(send_request, client);
+ cancel_timeout(state_bound, client);
/* If we have an address, unconfigure it. */
- if (client -> active) {
- script_init (client, "STOP", client -> active -> medium);
- script_write_params (client, "old_", client -> active);
- if (client -> alias)
- script_write_params (client, "alias_",
- client -> alias);
- script_go (client);
+ if (client->active) {
+ script_init(client, "STOP", client->active->medium);
+ script_write_params(client, "old_", client->active);
+ if (client->alias)
+ script_write_params(client, "alias_", client->alias);
+ script_go(client);
}
}
}
}
}
+
if (newstate == server_shutdown)
add_timeout (cur_time + 1, shutdown_exit, 0, 0, 0);
return ISC_R_SUCCESS;