mirror of
https://gitlab.isc.org/isc-projects/dhcp
synced 2025-09-01 23:05:29 +00:00
Reliable DHCPv6 release
This commit is contained in:
3
RELNOTES
3
RELNOTES
@@ -95,6 +95,9 @@ work on other platforms. Please report any problems and suggested fixes to
|
|||||||
|
|
||||||
- Basic and partial DHCPv6 leasequery support.
|
- Basic and partial DHCPv6 leasequery support.
|
||||||
|
|
||||||
|
- Reliable DHCPv6 release (previous behavior, send release and exit, is
|
||||||
|
still available with dhclient -6 -1 -r).
|
||||||
|
|
||||||
Changes since 4.0.0 (bug fixes)
|
Changes since 4.0.0 (bug fixes)
|
||||||
|
|
||||||
- DHCP now builds on AIX.
|
- DHCP now builds on AIX.
|
||||||
|
215
client/dhc6.c
215
client/dhc6.c
@@ -74,6 +74,7 @@ static isc_result_t dhc6_add_ia_na(struct client_state *client,
|
|||||||
struct data_string *packet,
|
struct data_string *packet,
|
||||||
struct dhc6_lease *lease,
|
struct dhc6_lease *lease,
|
||||||
u_int8_t message);
|
u_int8_t message);
|
||||||
|
static isc_boolean_t stopping_finished(void);
|
||||||
static void dhc6_merge_lease(struct dhc6_lease *src, struct dhc6_lease *dst);
|
static void dhc6_merge_lease(struct dhc6_lease *src, struct dhc6_lease *dst);
|
||||||
void do_select6(void *input);
|
void do_select6(void *input);
|
||||||
void do_refresh6(void *input);
|
void do_refresh6(void *input);
|
||||||
@@ -1683,16 +1684,15 @@ start_release6(struct client_state *client)
|
|||||||
client->active_lease->released = ISC_TRUE;
|
client->active_lease->released = ISC_TRUE;
|
||||||
write_client6_lease(client, client->active_lease, 0, 1);
|
write_client6_lease(client, client->active_lease, 0, 1);
|
||||||
|
|
||||||
/* Set timers per RFC3315 section 18.1.1. */
|
/* Set timers per RFC3315 section 18.1.6. */
|
||||||
client->IRT = REL_TIMEOUT * 100;
|
client->IRT = REL_TIMEOUT * 100;
|
||||||
client->MRT = 0;
|
client->MRT = 0;
|
||||||
client->MRC = REL_MAX_RC * 100;
|
client->MRC = REL_MAX_RC;
|
||||||
client->MRD = 0;
|
client->MRD = 0;
|
||||||
|
|
||||||
dhc6_retrans_init(client);
|
dhc6_retrans_init(client);
|
||||||
client->v6_handler = reply_handler;
|
client->v6_handler = reply_handler;
|
||||||
|
|
||||||
/* ("re")transmit the first packet. */
|
|
||||||
do_release6(client);
|
do_release6(client);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
@@ -1701,10 +1701,8 @@ start_release6(struct client_state *client)
|
|||||||
static void
|
static void
|
||||||
do_release6(void *input)
|
do_release6(void *input)
|
||||||
{
|
{
|
||||||
struct sockaddr_in6 unicast, *dest_addr = &DHCPv6DestAddr;
|
|
||||||
struct client_state *client;
|
struct client_state *client;
|
||||||
struct data_string ds;
|
struct data_string ds;
|
||||||
struct option_cache *oc;
|
|
||||||
int send_ret;
|
int send_ret;
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
|
|
||||||
@@ -1715,7 +1713,7 @@ do_release6(void *input)
|
|||||||
|
|
||||||
if ((client->MRC != 0) && (client->txcount > client->MRC)) {
|
if ((client->MRC != 0) && (client->txcount > client->MRC)) {
|
||||||
log_info("Max retransmission count exceeded.");
|
log_info("Max retransmission count exceeded.");
|
||||||
return;
|
goto release_done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1727,31 +1725,14 @@ do_release6(void *input)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check whether the server has sent a unicast option; if so, we can
|
* Don't use unicast as we don't know if we still have an
|
||||||
* use the address it specified.
|
* available address with enough scope.
|
||||||
*/
|
*/
|
||||||
oc = lookup_option(&dhcpv6_universe,
|
|
||||||
client->active_lease->options, D6O_UNICAST);
|
|
||||||
if (oc && evaluate_option_cache(&ds, NULL, NULL, NULL,
|
|
||||||
client->active_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;
|
|
||||||
}
|
|
||||||
|
|
||||||
data_string_forget(&ds, MDL);
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(&ds, 0, sizeof(ds));
|
memset(&ds, 0, sizeof(ds));
|
||||||
if (!buffer_allocate(&ds.buffer, 4, MDL)) {
|
if (!buffer_allocate(&ds.buffer, 4, MDL)) {
|
||||||
log_error("Unable to allocate memory for Release.");
|
log_error("Unable to allocate memory for Release.");
|
||||||
return;
|
goto release_done;
|
||||||
}
|
}
|
||||||
|
|
||||||
ds.data = ds.buffer->data;
|
ds.data = ds.buffer->data;
|
||||||
@@ -1770,7 +1751,7 @@ do_release6(void *input)
|
|||||||
if (dhc6_add_ia_na(client, &ds, client->active_lease,
|
if (dhc6_add_ia_na(client, &ds, client->active_lease,
|
||||||
DHCPV6_RELEASE) != ISC_R_SUCCESS) {
|
DHCPV6_RELEASE) != ISC_R_SUCCESS) {
|
||||||
data_string_forget(&ds, MDL);
|
data_string_forget(&ds, MDL);
|
||||||
return;
|
goto release_done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Transmit and wait. */
|
/* Transmit and wait. */
|
||||||
@@ -1796,6 +1777,13 @@ do_release6(void *input)
|
|||||||
}
|
}
|
||||||
add_timeout(&tv, do_release6, client, NULL, NULL);
|
add_timeout(&tv, do_release6, client, NULL, NULL);
|
||||||
dhc6_retrans_advance(client);
|
dhc6_retrans_advance(client);
|
||||||
|
return;
|
||||||
|
|
||||||
|
release_done:
|
||||||
|
dhc6_lease_destroy(&client->active_lease, MDL);
|
||||||
|
client->active_lease = NULL;
|
||||||
|
if (stopping_finished())
|
||||||
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* status_log() just puts a status code into displayable form and logs it
|
/* status_log() just puts a status code into displayable form and logs it
|
||||||
@@ -1814,6 +1802,7 @@ status_log(int code, const char *scope, const char *additional, int len)
|
|||||||
case STATUS_UnspecFail:
|
case STATUS_UnspecFail:
|
||||||
msg = "UnspecFail";
|
msg = "UnspecFail";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STATUS_NoAddrsAvail:
|
case STATUS_NoAddrsAvail:
|
||||||
msg = "NoAddrsAvail";
|
msg = "NoAddrsAvail";
|
||||||
break;
|
break;
|
||||||
@@ -1972,13 +1961,18 @@ dhc6_check_advertise(struct dhc6_lease *lease)
|
|||||||
* (rapid/commit). Returns always false as no action is defined.
|
* (rapid/commit). Returns always false as no action is defined.
|
||||||
*/
|
*/
|
||||||
static isc_boolean_t
|
static isc_boolean_t
|
||||||
dhc6_init_action(struct client_state *client, isc_result_t rval,
|
dhc6_init_action(struct client_state *client, isc_result_t *rvalp,
|
||||||
unsigned code)
|
unsigned code)
|
||||||
{
|
{
|
||||||
if (client == NULL)
|
if (rvalp == NULL)
|
||||||
return ISC_R_INVALIDARG;
|
log_fatal("Impossible condition at %s:%d.", MDL);
|
||||||
|
|
||||||
if (rval == ISC_R_SUCCESS)
|
if (client == NULL) {
|
||||||
|
*rvalp = ISC_R_INVALIDARG;
|
||||||
|
return ISC_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*rvalp == ISC_R_SUCCESS)
|
||||||
return ISC_FALSE;
|
return ISC_FALSE;
|
||||||
|
|
||||||
/* No possible action in any case... */
|
/* No possible action in any case... */
|
||||||
@@ -1990,13 +1984,20 @@ dhc6_init_action(struct client_state *client, isc_result_t rval,
|
|||||||
* packet should be ignored), or false if no action was taken.
|
* packet should be ignored), or false if no action was taken.
|
||||||
*/
|
*/
|
||||||
static isc_boolean_t
|
static isc_boolean_t
|
||||||
dhc6_select_action(struct client_state *client, isc_result_t rval,
|
dhc6_select_action(struct client_state *client, isc_result_t *rvalp,
|
||||||
unsigned code)
|
unsigned code)
|
||||||
{
|
{
|
||||||
struct dhc6_lease *lease;
|
struct dhc6_lease *lease;
|
||||||
|
isc_result_t rval;
|
||||||
|
|
||||||
if (client == NULL)
|
if (rvalp == NULL)
|
||||||
return ISC_R_INVALIDARG;
|
log_fatal("Impossible condition at %s:%d.", MDL);
|
||||||
|
|
||||||
|
if (client == NULL) {
|
||||||
|
*rvalp = ISC_R_INVALIDARG;
|
||||||
|
return ISC_FALSE;
|
||||||
|
}
|
||||||
|
rval = *rvalp;
|
||||||
|
|
||||||
if (rval == ISC_R_SUCCESS)
|
if (rval == ISC_R_SUCCESS)
|
||||||
return ISC_FALSE;
|
return ISC_FALSE;
|
||||||
@@ -2110,12 +2111,19 @@ dhc6_withdraw_lease(struct client_state *client)
|
|||||||
* packet should be ignored), or false if no action was taken.
|
* packet should be ignored), or false if no action was taken.
|
||||||
*/
|
*/
|
||||||
static isc_boolean_t
|
static isc_boolean_t
|
||||||
dhc6_reply_action(struct client_state *client, isc_result_t rval,
|
dhc6_reply_action(struct client_state *client, isc_result_t *rvalp,
|
||||||
unsigned code)
|
unsigned code)
|
||||||
{
|
{
|
||||||
|
isc_result_t rval;
|
||||||
|
|
||||||
if (client == NULL)
|
if (rvalp == NULL)
|
||||||
return ISC_R_INVALIDARG;
|
log_fatal("Impossible condition at %s:%d.", MDL);
|
||||||
|
|
||||||
|
if (client == NULL) {
|
||||||
|
*rvalp = ISC_R_INVALIDARG;
|
||||||
|
return ISC_FALSE;
|
||||||
|
}
|
||||||
|
rval = *rvalp;
|
||||||
|
|
||||||
if (rval == ISC_R_SUCCESS)
|
if (rval == ISC_R_SUCCESS)
|
||||||
return ISC_FALSE;
|
return ISC_FALSE;
|
||||||
@@ -2168,7 +2176,7 @@ dhc6_reply_action(struct client_state *client, isc_result_t rval,
|
|||||||
* to init, redo server selection and get new addresses.
|
* to init, redo server selection and get new addresses.
|
||||||
*/
|
*/
|
||||||
dhc6_withdraw_lease(client);
|
dhc6_withdraw_lease(client);
|
||||||
return ISC_TRUE;
|
break;
|
||||||
|
|
||||||
/* "If the status code is NoAddrsAvail, the client has
|
/* "If the status code is NoAddrsAvail, the client has
|
||||||
* received no usable addresses in the IA and may choose
|
* received no usable addresses in the IA and may choose
|
||||||
@@ -2197,13 +2205,78 @@ dhc6_reply_action(struct client_state *client, isc_result_t rval,
|
|||||||
return ISC_TRUE;
|
return ISC_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* status code <-> action matrix for the client in STOPPED state
|
||||||
|
* (release/decline). Returns true if action was taken (and the
|
||||||
|
* packet should be ignored), or false if no action was taken.
|
||||||
|
* NoBinding is translated into Success.
|
||||||
|
*/
|
||||||
|
static isc_boolean_t
|
||||||
|
dhc6_stop_action(struct client_state *client, isc_result_t *rvalp,
|
||||||
|
unsigned code)
|
||||||
|
{
|
||||||
|
isc_result_t rval;
|
||||||
|
|
||||||
|
if (rvalp == NULL)
|
||||||
|
log_fatal("Impossible condition at %s:%d.", MDL);
|
||||||
|
|
||||||
|
if (client == NULL) {
|
||||||
|
*rvalp = ISC_R_INVALIDARG;
|
||||||
|
return ISC_FALSE;
|
||||||
|
}
|
||||||
|
rval = *rvalp;
|
||||||
|
|
||||||
|
if (rval == ISC_R_SUCCESS)
|
||||||
|
return ISC_FALSE;
|
||||||
|
|
||||||
|
switch (code) {
|
||||||
|
/* It's possible an earlier status code set rval to a failure
|
||||||
|
* code, and we've encountered a later success.
|
||||||
|
*/
|
||||||
|
case STATUS_Success:
|
||||||
|
/* For unknown codes...it's a soft (retryable) error. */
|
||||||
|
case STATUS_UnspecFail:
|
||||||
|
default:
|
||||||
|
return ISC_FALSE;
|
||||||
|
|
||||||
|
/* NoBinding is not an error */
|
||||||
|
case STATUS_NoBinding:
|
||||||
|
if (rval == ISC_R_FAILURE)
|
||||||
|
*rvalp = ISC_R_SUCCESS;
|
||||||
|
return ISC_FALSE;
|
||||||
|
|
||||||
|
/* Should not happen */
|
||||||
|
case STATUS_NoAddrsAvail:
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Give up on it */
|
||||||
|
case STATUS_NotOnLink:
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* The server is telling us to use a multicast address, so
|
||||||
|
* we have to delete the unicast option from the active
|
||||||
|
* lease, then allow retransmission to occur normally.
|
||||||
|
* (XXX: It might be preferable in this case to retransmit
|
||||||
|
* sooner than the current interval, but for now we don't.)
|
||||||
|
*/
|
||||||
|
case STATUS_UseMulticast:
|
||||||
|
if (client->active_lease != NULL)
|
||||||
|
delete_option(&dhcp_universe,
|
||||||
|
client->active_lease->options,
|
||||||
|
D6O_UNICAST);
|
||||||
|
return ISC_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ISC_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
/* Look at a new and old lease, and make sure the new information is not
|
/* Look at a new and old lease, and make sure the new information is not
|
||||||
* losing us any state.
|
* losing us any state.
|
||||||
*/
|
*/
|
||||||
static isc_result_t
|
static isc_result_t
|
||||||
dhc6_check_reply(struct client_state *client, struct dhc6_lease *new)
|
dhc6_check_reply(struct client_state *client, struct dhc6_lease *new)
|
||||||
{
|
{
|
||||||
isc_boolean_t (*action)(struct client_state *, isc_result_t, unsigned);
|
isc_boolean_t (*action)(struct client_state *,
|
||||||
|
isc_result_t *, unsigned);
|
||||||
struct dhc6_ia *ia;
|
struct dhc6_ia *ia;
|
||||||
struct dhc6_addr *addr;
|
struct dhc6_addr *addr;
|
||||||
isc_result_t rval = ISC_R_SUCCESS;
|
isc_result_t rval = ISC_R_SUCCESS;
|
||||||
@@ -2229,6 +2302,10 @@ dhc6_check_reply(struct client_state *client, struct dhc6_lease *new)
|
|||||||
action = dhc6_reply_action;
|
action = dhc6_reply_action;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case S_STOPPED:
|
||||||
|
action = dhc6_stop_action;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
log_fatal("Impossible condition at %s:%d.", MDL);
|
log_fatal("Impossible condition at %s:%d.", MDL);
|
||||||
return ISC_R_CANCELED;
|
return ISC_R_CANCELED;
|
||||||
@@ -2239,7 +2316,7 @@ dhc6_check_reply(struct client_state *client, struct dhc6_lease *new)
|
|||||||
* and do not continue.
|
* and do not continue.
|
||||||
*/
|
*/
|
||||||
rval = dhc6_check_status(rval, new->options, "message", &code);
|
rval = dhc6_check_status(rval, new->options, "message", &code);
|
||||||
if (action(client, rval, code))
|
if (action(client, &rval, code))
|
||||||
return ISC_R_CANCELED;
|
return ISC_R_CANCELED;
|
||||||
|
|
||||||
for (ia = new->bindings ; ia != NULL ; ia = ia->next) {
|
for (ia = new->bindings ; ia != NULL ; ia = ia->next) {
|
||||||
@@ -2257,7 +2334,7 @@ dhc6_check_reply(struct client_state *client, struct dhc6_lease *new)
|
|||||||
}
|
}
|
||||||
rval = dhc6_check_status(rval, ia->options,
|
rval = dhc6_check_status(rval, ia->options,
|
||||||
scope, &code);
|
scope, &code);
|
||||||
if (action(client, rval, code))
|
if (action(client, &rval, code))
|
||||||
return ISC_R_CANCELED;
|
return ISC_R_CANCELED;
|
||||||
|
|
||||||
for (addr = ia->addrs ; addr != NULL ;
|
for (addr = ia->addrs ; addr != NULL ;
|
||||||
@@ -2268,7 +2345,7 @@ dhc6_check_reply(struct client_state *client, struct dhc6_lease *new)
|
|||||||
scope = "IAPREFIX";
|
scope = "IAPREFIX";
|
||||||
rval = dhc6_check_status(rval, addr->options,
|
rval = dhc6_check_status(rval, addr->options,
|
||||||
scope, &code);
|
scope, &code);
|
||||||
if (action(client, rval, code))
|
if (action(client, &rval, code))
|
||||||
return ISC_R_CANCELED;
|
return ISC_R_CANCELED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2324,6 +2401,10 @@ dhc6_check_reply(struct client_state *client, struct dhc6_lease *new)
|
|||||||
*/
|
*/
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case S_STOPPED:
|
||||||
|
/* Nothing critical to do at this stage. */
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
log_fatal("REALLY impossible condition at %s:%d.", MDL);
|
log_fatal("REALLY impossible condition at %s:%d.", MDL);
|
||||||
return ISC_R_CANCELED;
|
return ISC_R_CANCELED;
|
||||||
@@ -2824,6 +2905,7 @@ dhc6_add_ia_na(struct client_state *client, struct data_string *packet,
|
|||||||
|
|
||||||
case DHCPV6_CONFIRM:
|
case DHCPV6_CONFIRM:
|
||||||
case DHCPV6_RELEASE:
|
case DHCPV6_RELEASE:
|
||||||
|
case DHCPV6_DECLINE:
|
||||||
/* Set t1 and t2 to zero; server will ignore them */
|
/* Set t1 and t2 to zero; server will ignore them */
|
||||||
memset(iads.buffer->data + 4, 0, 8);
|
memset(iads.buffer->data + 4, 0, 8);
|
||||||
log_debug("XMT: X-- IA_NA %s",
|
log_debug("XMT: X-- IA_NA %s",
|
||||||
@@ -2900,6 +2982,13 @@ dhc6_add_ia_na(struct client_state *client, struct data_string *packet,
|
|||||||
piaddr(addr->address));
|
piaddr(addr->address));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case DHCPV6_DECLINE:
|
||||||
|
/* Preferred and max life are irrelevant */
|
||||||
|
memset(addrds.buffer->data + 16, 0, 8);
|
||||||
|
log_debug("XMT: | X-- Decline Address %s",
|
||||||
|
piaddr(addr->address));
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
log_fatal("Impossible condition at %s:%d.",
|
log_fatal("Impossible condition at %s:%d.",
|
||||||
MDL);
|
MDL);
|
||||||
@@ -2929,6 +3018,25 @@ dhc6_add_ia_na(struct client_state *client, struct data_string *packet,
|
|||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* stopping_finished() checks if there is a remaining work to do.
|
||||||
|
*/
|
||||||
|
static isc_boolean_t
|
||||||
|
stopping_finished(void)
|
||||||
|
{
|
||||||
|
struct interface_info *ip;
|
||||||
|
struct client_state *client;
|
||||||
|
|
||||||
|
for (ip = interfaces; ip; ip = ip -> next) {
|
||||||
|
for (client = ip -> client; client; client = client -> next) {
|
||||||
|
if (client->state != S_STOPPED)
|
||||||
|
return ISC_FALSE;
|
||||||
|
if (client->active_lease != NULL)
|
||||||
|
return ISC_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ISC_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
/* reply_handler() accepts a Reply while we're attempting Select or Renew or
|
/* reply_handler() accepts a Reply while we're attempting Select or Renew or
|
||||||
* Rebind. Basically any Reply packet.
|
* Rebind. Basically any Reply packet.
|
||||||
*/
|
*/
|
||||||
@@ -2974,6 +3082,19 @@ reply_handler(struct packet *packet, struct client_state *client)
|
|||||||
cancel_timeout(do_refresh6, client);
|
cancel_timeout(do_refresh6, client);
|
||||||
cancel_timeout(do_release6, client);
|
cancel_timeout(do_release6, client);
|
||||||
|
|
||||||
|
/* If this is in response to a Release/Decline, clean up and return. */
|
||||||
|
if (client->state == S_STOPPED) {
|
||||||
|
if (client->active_lease == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
dhc6_lease_destroy(&client->active_lease, MDL);
|
||||||
|
client->active_lease = NULL;
|
||||||
|
/* We should never wait for nothing!? */
|
||||||
|
if (stopping_finished())
|
||||||
|
exit(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Action was taken, so now that we've torn down our scheduled
|
/* Action was taken, so now that we've torn down our scheduled
|
||||||
* retransmissions, return.
|
* retransmissions, return.
|
||||||
*/
|
*/
|
||||||
@@ -2985,16 +3106,6 @@ reply_handler(struct packet *packet, struct client_state *client)
|
|||||||
client->selected_lease = NULL;
|
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
|
/* If this is in response to a confirm, we use the lease we've
|
||||||
* already got, not the reply we were sent.
|
* already got, not the reply we were sent.
|
||||||
*/
|
*/
|
||||||
|
@@ -506,8 +506,19 @@ main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (release_mode || exit_mode)
|
if (exit_mode)
|
||||||
return 0;
|
return 0;
|
||||||
|
if (release_mode) {
|
||||||
|
#ifndef DHCPv6
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
|
if (local_family == AF_INET6) {
|
||||||
|
if (onetry)
|
||||||
|
return 0;
|
||||||
|
} else
|
||||||
|
return 0;
|
||||||
|
#endif /* DHCPv6 */
|
||||||
|
}
|
||||||
|
|
||||||
/* Start up a listener for the object management API protocol. */
|
/* Start up a listener for the object management API protocol. */
|
||||||
if (top_level_config.omapi_port != -1) {
|
if (top_level_config.omapi_port != -1) {
|
||||||
|
Reference in New Issue
Block a user