2
0
mirror of https://gitlab.isc.org/isc-projects/dhcp synced 2025-08-22 18:07:25 +00:00

- The server will now include multiple IA_NA's and multiple IAADDRs within

them, if advertised by the client.  It still only seeks to allocate one
  new address. [ISC-Bugs #17254]
This commit is contained in:
David Hankins 2007-11-14 23:44:46 +00:00
parent 8b1cb22681
commit bead14ea8b
6 changed files with 221 additions and 137 deletions

View File

@ -63,6 +63,10 @@ suggested fixes to <dhcp-users@isc.org>.
see 'man dhcpd.conf' for more information on allow/deny 'after time' syntax. see 'man dhcpd.conf' for more information on allow/deny 'after time' syntax.
Thanks to a patch from Christof Chen. Thanks to a patch from Christof Chen.
- The server will now include multiple IA_NA's and multiple IAADDRs within
them, if advertised by the client. It still only seeks to allocate one
new address.
Changes since 4.0.0b1 Changes since 4.0.0b1
- Use different paths for PID and lease files when running in DHCPv4 - Use different paths for PID and lease files when running in DHCPv4

View File

@ -1177,8 +1177,7 @@ void parse_client_lease_declaration (cfile, lease, ipp, clientp)
case OPTION: case OPTION:
oc = (struct option_cache *)0; oc = (struct option_cache *)0;
if (parse_option_decl (&oc, cfile)) { if (parse_option_decl (&oc, cfile)) {
save_option (oc -> option -> universe, save_option(oc->option->universe, lease->options, oc);
lease -> options, oc);
option_cache_dereference (&oc, MDL); option_cache_dereference (&oc, MDL);
} }
return; return;

View File

@ -43,6 +43,10 @@ static int pretty_text(char **, char *, const unsigned char **,
const unsigned char *, int); const unsigned char *, int);
static int pretty_domain(char **, char *, const unsigned char **, static int pretty_domain(char **, char *, const unsigned char **,
const unsigned char *); const unsigned char *);
static int prepare_option_buffer(struct universe *universe, struct buffer *bp,
unsigned char *buffer, unsigned length,
unsigned code, int terminatep,
struct option_cache **opp);
/* Parse all available options out of the specified packet. */ /* Parse all available options out of the specified packet. */
@ -950,14 +954,18 @@ store_options6(char *buf, int buflen,
continue; continue;
} }
memset(&ds, 0, sizeof(ds)); memset(&ds, 0, sizeof(ds));
for (; oc != NULL ; oc = oc->next) {
if (evaluate_option_cache(&ds, packet, NULL, if (evaluate_option_cache(&ds, packet, NULL,
NULL, opt_state, NULL, NULL, opt_state,
&global_scope, oc, MDL)) { NULL, &global_scope,
if ((ds.len + 4) <= (buflen - bufpos)) { oc, MDL)) {
tmp = (unsigned char *)buf + bufpos; if ((ds.len + 4) <=
(buflen - bufpos)) {
tmp = (unsigned char *)buf;
tmp += bufpos;
/* option tag */ /* option tag */
putUShort(tmp, required_opts[i]); putUShort(tmp,
required_opts[i]);
/* option length */ /* option length */
putUShort(tmp+2, ds.len); putUShort(tmp+2, ds.len);
/* option data */ /* option data */
@ -965,7 +973,8 @@ store_options6(char *buf, int buflen,
/* update position */ /* update position */
bufpos += (4 + ds.len); bufpos += (4 + ds.len);
} else { } else {
log_debug("No space for option %d", log_debug("No space for "
"option %d",
required_opts[i]); required_opts[i]);
} }
data_string_forget(&ds, MDL); data_string_forget(&ds, MDL);
@ -975,6 +984,7 @@ store_options6(char *buf, int buflen,
} }
} }
} }
}
if (oro == NULL) { if (oro == NULL) {
oro_size = 0; oro_size = 0;
@ -1013,12 +1023,11 @@ store_options6(char *buf, int buflen,
* Not already added, find this option. * Not already added, find this option.
*/ */
oc = lookup_option(&dhcpv6_universe, opt_state, code); oc = lookup_option(&dhcpv6_universe, opt_state, code);
if (oc == NULL) {
continue;
}
memset(&ds, 0, sizeof(ds)); memset(&ds, 0, sizeof(ds));
if (evaluate_option_cache(&ds, packet, NULL, NULL, opt_state, for (; oc != NULL ; oc = oc->next) {
NULL, &global_scope, oc, MDL)) { if (evaluate_option_cache(&ds, packet, NULL, NULL,
opt_state, NULL,
&global_scope, oc, MDL)) {
if ((ds.len + 4) <= (buflen - bufpos)) { if ((ds.len + 4) <= (buflen - bufpos)) {
tmp = (unsigned char *)buf + bufpos; tmp = (unsigned char *)buf + bufpos;
/* option tag */ /* option tag */
@ -1030,13 +1039,15 @@ store_options6(char *buf, int buflen,
/* update position */ /* update position */
bufpos += (4 + ds.len); bufpos += (4 + ds.len);
} else { } else {
log_debug("No space for option %d", code); log_debug("No space for option %d",
code);
} }
data_string_forget(&ds, MDL); data_string_forget(&ds, MDL);
} else { } else {
log_error("Error evaluating option %d", code); log_error("Error evaluating option %d", code);
} }
} }
}
if (vsio_wanted) { if (vsio_wanted) {
for (i=0; i < opt_state->universe_count; i++) { for (i=0; i < opt_state->universe_count; i++) {
@ -2143,15 +2154,64 @@ struct option_cache *lookup_hashed_option (universe, options, code)
return (struct option_cache *)0; return (struct option_cache *)0;
} }
int save_option_buffer (struct universe *universe, /* Save a specified buffer into an option cache. */
struct option_state *options, int
struct buffer *bp, save_option_buffer(struct universe *universe, struct option_state *options,
unsigned char *buffer, unsigned length, struct buffer *bp, unsigned char *buffer, unsigned length,
unsigned code, int tp) unsigned code, int terminatep)
{ {
struct buffer *lbp = (struct buffer *)0; struct option_cache *op = NULL;
struct option_cache *op = (struct option_cache *)0; int status = 1;
status = prepare_option_buffer(universe, bp, buffer, length, code,
terminatep, &op);
if (status == 0)
goto cleanup;
save_option(universe, options, op);
cleanup:
if (op != NULL)
option_cache_dereference(&op, MDL);
return status;
}
/* Append a specified buffer onto the tail of an option cache. */
int
append_option_buffer(struct universe *universe, struct option_state *options,
struct buffer *bp, unsigned char *buffer, unsigned length,
unsigned code, int terminatep)
{
struct option_cache *op = NULL;
int status = 1;
status = prepare_option_buffer(universe, bp, buffer, length, code,
terminatep, &op);
if (status == 0)
goto cleanup;
also_save_option(universe, options, op);
cleanup:
if (op != NULL)
option_cache_dereference(&op, MDL);
return status;
}
/* Create/copy a buffer into a new option cache. */
static int
prepare_option_buffer(struct universe *universe, struct buffer *bp,
unsigned char *buffer, unsigned length, unsigned code,
int terminatep, struct option_cache **opp)
{
struct buffer *lbp = NULL;
struct option *option = NULL; struct option *option = NULL;
struct option_cache *op;
int status = 1;
/* Code sizes of 8, 16, and 32 bits are allowed. */ /* Code sizes of 8, 16, and 32 bits are allowed. */
switch(universe->tag_size) { switch(universe->tag_size) {
@ -2199,26 +2259,28 @@ int save_option_buffer (struct universe *universe,
option->refcnt = 1; option->refcnt = 1;
} }
if (!option_cache_allocate (&op, MDL)) { if (!option_cache_allocate (opp, MDL)) {
log_error("No memory for option code %s.%s.", log_error("No memory for option code %s.%s.",
universe->name, option->name); universe->name, option->name);
option_dereference(&option, MDL); status = 0;
return 0; goto cleanup;
} }
/* Pointer rather than double pointer makes for less parens. */
op = *opp;
option_reference(&op->option, option, MDL); option_reference(&op->option, option, MDL);
/* If we weren't passed a buffer in which the data are saved and /* If we weren't passed a buffer in which the data are saved and
refcounted, allocate one now. */ refcounted, allocate one now. */
if (!bp) { if (!bp) {
if (!buffer_allocate (&lbp, length + tp, MDL)) { if (!buffer_allocate (&lbp, length + terminatep, MDL)) {
log_error ("no memory for option buffer."); log_error ("no memory for option buffer.");
option_cache_dereference (&op, MDL); status = 0;
option_dereference(&option, MDL); goto cleanup;
return 0;
} }
memcpy (lbp -> data, buffer, length + tp); memcpy (lbp -> data, buffer, length + terminatep);
bp = lbp; bp = lbp;
buffer = &bp -> data [0]; /* Refer to saved buffer. */ buffer = &bp -> data [0]; /* Refer to saved buffer. */
} }
@ -2231,7 +2293,7 @@ int save_option_buffer (struct universe *universe,
op -> data.data = buffer; op -> data.data = buffer;
op -> data.len = length; op -> data.len = length;
if (tp) { if (terminatep) {
/* NUL terminate (we can get away with this because we (or /* NUL terminate (we can get away with this because we (or
the caller!) allocated one more than the buffer size, and the caller!) allocated one more than the buffer size, and
because the byte following the end of an option is always because the byte following the end of an option is always
@ -2257,11 +2319,8 @@ int save_option_buffer (struct universe *universe,
} }
} }
/* Now store the option. */
save_option (universe, options, op);
/* And let go of our references. */ /* And let go of our references. */
option_cache_dereference (&op, MDL); cleanup:
option_dereference(&option, MDL); option_dereference(&option, MDL);
return 1; return 1;
@ -2298,6 +2357,10 @@ collect_oro(struct option_cache *oc,
oro->len += 2; oro->len += 2;
} }
/* build_server_oro() is presently unusued, but may be used at a future date
* with support for Reconfigure messages (as a hint to the client about new
* option value contents).
*/
void void
build_server_oro(struct data_string *server_oro, build_server_oro(struct data_string *server_oro,
struct option_state *options, struct option_state *options,
@ -2362,20 +2425,31 @@ build_server_oro(struct data_string *server_oro,
} }
} }
void save_option (struct universe *universe, /* Wrapper function to put an option cache into an option state. */
struct option_state *options, struct option_cache *oc) void
save_option(struct universe *universe, struct option_state *options,
struct option_cache *oc)
{ {
if (universe -> save_func) if (universe->save_func)
(*universe -> save_func) (universe, options, oc); (*universe->save_func)(universe, options, oc, ISC_FALSE);
else else
log_error ("can't store options in %s space.", log_error("can't store options in %s space.", universe->name);
universe -> name);
} }
void save_hashed_option (universe, options, oc) /* Wrapper function to append an option cache into an option state's list. */
struct universe *universe; void
struct option_state *options; also_save_option(struct universe *universe, struct option_state *options,
struct option_cache *oc; struct option_cache *oc)
{
if (universe->save_func)
(*universe->save_func)(universe, options, oc, ISC_TRUE);
else
log_error("can't store options in %s space.", universe->name);
}
void
save_hashed_option(struct universe *universe, struct option_state *options,
struct option_cache *oc, isc_boolean_t appendp)
{ {
int hashix; int hashix;
pair bptr; pair bptr;
@ -2407,12 +2481,23 @@ void save_hashed_option (universe, options, oc)
break; break;
} }
/* If we find one, dereference it and put the new one /* Deal with collisions on the hash list. */
in its place. */
if (bptr) { if (bptr) {
ocloc = (struct option_cache **)&bptr->car; ocloc = (struct option_cache **)&bptr->car;
/*
* If appendp is set, append it onto the tail of the
* ->next list. If it is not set, rotate it into
* position at the head of the list.
*/
if (appendp) {
do {
ocloc = &(*ocloc)->next;
} while (*ocloc != NULL);
} else {
option_cache_dereference(ocloc, MDL); option_cache_dereference(ocloc, MDL);
}
option_cache_reference(ocloc, oc, MDL); option_cache_reference(ocloc, oc, MDL);
return; return;
} }
@ -3053,44 +3138,35 @@ int fqdn_option_space_encapsulate (result, packet, lease, client_state,
return status; return status;
} }
/* Shill to the DHCPv4 fqdn option cache any lookups in the fqdn6 universe. /*
* * Trap invalid attempts to inspect FQND6 contents.
* XXX: Is this necessary? There shouldn't be any lookups directly...
*/ */
struct option_cache * struct option_cache *
lookup_fqdn6_option(struct universe *universe, struct option_state *options, lookup_fqdn6_option(struct universe *universe, struct option_state *options,
unsigned code) unsigned code)
{ {
log_fatal("Impossible condition at %s:%d.", MDL); log_fatal("Impossible condition at %s:%d.", MDL);
return NULL;
return fqdn_universe.lookup_func(&fqdn_universe, options, code);
} }
/* Shill to the DHCPv4 fqdn option cache any direct saves to the fqdn6 /*
* universe. * Trap invalid attempts to save options directly to FQDN6 rather than FQDN.
*
* XXX: Should this even be possible? Never excercised code?
*/ */
void void
save_fqdn6_option(struct universe *universe, struct option_state *options, save_fqdn6_option(struct universe *universe, struct option_state *options,
struct option_cache *oc) struct option_cache *oc, isc_boolean_t appendp)
{ {
log_fatal("Impossible condition at %s:%d.", MDL); log_fatal("Impossible condition at %s:%d.", MDL);
fqdn_universe.save_func(&fqdn_universe, options, oc);
} }
/* Shill to the DHCPv4 fqdn option cache any attempts to remove entries. /*
* * Trap invalid attempts to delete an option out of the FQDN6 universe.
* XXX: Again...should this even be possible?
*/ */
void void
delete_fqdn6_option(struct universe *universe, struct option_state *options, delete_fqdn6_option(struct universe *universe, struct option_state *options,
int code) int code)
{ {
log_fatal("Impossible condition at %s:%d.", MDL); log_fatal("Impossible condition at %s:%d.", MDL);
fqdn_universe.delete_func(&fqdn_universe, options, code);
} }
/* Shill to the DHCPv4 fqdn option cache any attempts to traverse the /* Shill to the DHCPv4 fqdn option cache any attempts to traverse the
@ -3395,10 +3471,9 @@ void hashed_option_space_foreach (struct packet *packet, struct lease *lease,
} }
} }
void save_linked_option (universe, options, oc) void
struct universe *universe; save_linked_option(struct universe *universe, struct option_state *options,
struct option_state *options; struct option_cache *oc, isc_boolean_t appendp)
struct option_cache *oc;
{ {
pair *tail; pair *tail;
struct option_chain_head *head; struct option_chain_head *head;
@ -3422,7 +3497,13 @@ void save_linked_option (universe, options, oc)
ocloc = (struct option_cache **)&(*tail)->car; ocloc = (struct option_cache **)&(*tail)->car;
if (oc->option->code == (*ocloc)->option->code) { if (oc->option->code == (*ocloc)->option->code) {
if (appendp) {
do {
ocloc = &(*ocloc)->next;
} while (*ocloc != NULL);
} else {
option_cache_dereference(ocloc, MDL); option_cache_dereference(ocloc, MDL);
}
option_cache_reference(ocloc, oc, MDL); option_cache_reference(ocloc, oc, MDL);
return; return;
} }

View File

@ -1387,7 +1387,7 @@ lookup_fqdn6_option(struct universe *universe, struct option_state *options,
unsigned code); unsigned code);
void void
save_fqdn6_option(struct universe *universe, struct option_state *options, save_fqdn6_option(struct universe *universe, struct option_state *options,
struct option_cache *oc); struct option_cache *oc, isc_boolean_t appendp);
void void
delete_fqdn6_option(struct universe *universe, struct option_state *options, delete_fqdn6_option(struct universe *universe, struct option_state *options,
int code); int code);
@ -1457,12 +1457,17 @@ struct option_cache *next_hashed_option(struct universe *,
int save_option_buffer (struct universe *, struct option_state *, int save_option_buffer (struct universe *, struct option_state *,
struct buffer *, unsigned char *, unsigned, struct buffer *, unsigned char *, unsigned,
unsigned, int); unsigned, int);
int append_option_buffer(struct universe *, struct option_state *,
struct buffer *, unsigned char *, unsigned,
unsigned, int);
void build_server_oro(struct data_string *, struct option_state *, void build_server_oro(struct data_string *, struct option_state *,
const char *, int); const char *, int);
void save_option PROTO ((struct universe *, void save_option(struct universe *, struct option_state *,
struct option_state *, struct option_cache *)); struct option_cache *);
void save_hashed_option PROTO ((struct universe *, void also_save_option(struct universe *, struct option_state *,
struct option_state *, struct option_cache *)); struct option_cache *);
void save_hashed_option(struct universe *, struct option_state *,
struct option_cache *, isc_boolean_t appendp);
void delete_option PROTO ((struct universe *, struct option_state *, int)); void delete_option PROTO ((struct universe *, struct option_state *, int));
void delete_hashed_option PROTO ((struct universe *, void delete_hashed_option PROTO ((struct universe *,
struct option_state *, int)); struct option_state *, int));
@ -1549,8 +1554,8 @@ int linked_option_get PROTO ((struct data_string *, struct universe *,
int linked_option_state_dereference PROTO ((struct universe *, int linked_option_state_dereference PROTO ((struct universe *,
struct option_state *, struct option_state *,
const char *, int)); const char *, int));
void save_linked_option (struct universe *, struct option_state *, void save_linked_option(struct universe *, struct option_state *,
struct option_cache *); struct option_cache *, isc_boolean_t appendp);
void linked_option_space_foreach (struct packet *, struct lease *, void linked_option_space_foreach (struct packet *, struct lease *,
struct client_state *, struct client_state *,
struct option_state *, struct option_state *,

View File

@ -304,7 +304,7 @@ struct universe {
struct option_state *, struct option_state *,
unsigned); unsigned);
void (*save_func) (struct universe *, struct option_state *, void (*save_func) (struct universe *, struct option_state *,
struct option_cache *); struct option_cache *, isc_boolean_t);
void (*foreach) (struct packet *, void (*foreach) (struct packet *,
struct lease *, struct client_state *, struct lease *, struct client_state *,
struct option_state *, struct option_state *, struct option_state *, struct option_state *,

View File

@ -756,7 +756,6 @@ start_reply(struct packet *packet,
struct dhcpv6_packet *reply) struct dhcpv6_packet *reply)
{ {
struct option_cache *oc; struct option_cache *oc;
struct data_string server_oro;
const unsigned char *server_id_data; const unsigned char *server_id_data;
int server_id_len; int server_id_len;
@ -877,21 +876,6 @@ start_reply(struct packet *packet,
} }
} }
/*
* Set the ORO for the main packet.
*/
build_server_oro(&server_oro, *opt_state, MDL);
if (!save_option_buffer(&dhcpv6_universe, *opt_state,
server_oro.buffer,
(unsigned char *)server_oro.data,
server_oro.len, D6O_ORO, 0)) {
log_error("start_reply: error saving server ORO.");
data_string_forget(&server_oro, MDL);
option_state_dereference(opt_state, MDL);
return 0;
}
data_string_forget(&server_oro, MDL);
return 1; return 1;
} }
@ -1160,6 +1144,7 @@ reply_process_ia(struct reply_state *reply, struct option_cache *ia) {
packet_ia = NULL; packet_ia = NULL;
memset(&ia_data, 0, sizeof(ia_data)); memset(&ia_data, 0, sizeof(ia_data));
memset(&data, 0, sizeof(data)); memset(&data, 0, sizeof(data));
/* Note that find_client_address() may set reply->lease. */
/* Make sure there is at least room for the header. */ /* Make sure there is at least room for the header. */
if ((reply->cursor + IA_NA_OFFSET + 4) > sizeof(reply->buf)) { if ((reply->cursor + IA_NA_OFFSET + 4) > sizeof(reply->buf)) {
@ -1434,11 +1419,10 @@ reply_process_ia(struct reply_state *reply, struct option_cache *ia) {
NULL, NULL, NULL, NULL,
reply->packet->options, reply->packet->options,
reply->opt_state, reply->opt_state,
&reply->lease->scope, &tmp->scope,
oc, MDL)) { oc, MDL)) {
ddns_updates(reply->packet, NULL, NULL, ddns_updates(reply->packet, NULL, NULL,
reply->lease, NULL, tmp, NULL, reply->opt_state);
reply->opt_state);
} }
} }
@ -1472,6 +1456,8 @@ reply_process_ia(struct reply_state *reply, struct option_cache *ia) {
ia_na_dereference(&reply->ia_na, MDL); ia_na_dereference(&reply->ia_na, MDL);
if (reply->old_ia != NULL) if (reply->old_ia != NULL)
ia_na_dereference(&reply->old_ia, MDL); ia_na_dereference(&reply->old_ia, MDL);
if (reply->lease != NULL)
iaaddr_dereference(&reply->lease, MDL);
if (reply->fixed.data != NULL) if (reply->fixed.data != NULL)
data_string_forget(&reply->fixed, MDL); data_string_forget(&reply->fixed, MDL);
@ -1523,9 +1509,7 @@ reply_process_addr(struct reply_state *reply, struct option_cache *addr) {
goto cleanup; goto cleanup;
} }
tmp_addr.len = 16; /* The first 16 bytes are the IPv6 address. */
memcpy(tmp_addr.iabuf, iaaddr.data, 16);
pref_life = getULong(iaaddr.data + 16); pref_life = getULong(iaaddr.data + 16);
valid_life = getULong(iaaddr.data + 20); valid_life = getULong(iaaddr.data + 20);
@ -1541,11 +1525,16 @@ reply_process_addr(struct reply_state *reply, struct option_cache *addr) {
* Clients may choose to send :: as an address, with the idea to give * Clients may choose to send :: as an address, with the idea to give
* hints about preferred-lifetime or valid-lifetime. * hints about preferred-lifetime or valid-lifetime.
*/ */
tmp_addr.len = 16;
memset(tmp_addr.iabuf, 0, 16);
if (!memcmp(iaaddr.data, tmp_addr.iabuf, 16)) { if (!memcmp(iaaddr.data, tmp_addr.iabuf, 16)) {
/* Status remains success; we just ignore this one. */ /* Status remains success; we just ignore this one. */
goto cleanup; goto cleanup;
} }
/* tmp_addr len remains 16 */
memcpy(tmp_addr.iabuf, iaaddr.data, 16);
/* /*
* Verify that this address is on the client's network. * Verify that this address is on the client's network.
*/ */
@ -1764,6 +1753,7 @@ address_is_owned(struct reply_state *reply, struct iaddr *addr) {
*/ */
static isc_result_t static isc_result_t
reply_process_try_addr(struct reply_state *reply, struct iaddr *addr) { reply_process_try_addr(struct reply_state *reply, struct iaddr *addr) {
isc_result_t status = ISC_R_FAILURE;
struct ipv6_pool *pool; struct ipv6_pool *pool;
int i; int i;
struct data_string data_addr; struct data_string data_addr;
@ -1778,14 +1768,16 @@ reply_process_try_addr(struct reply_state *reply, struct iaddr *addr) {
data_addr.data = addr->iabuf; data_addr.data = addr->iabuf;
for (i = 0 ; (pool = reply->shared->ipv6_pools[i]) != NULL ; i++) { for (i = 0 ; (pool = reply->shared->ipv6_pools[i]) != NULL ; i++) {
if (try_client_v6_address(&reply->lease, pool, &data_addr) == status = try_client_v6_address(&reply->lease, pool,
ISC_R_SUCCESS) &data_addr);
if (status == ISC_R_SUCCESS)
break; break;
} }
/* Note that this is just pedantry. There is no allocation to free. */ /* Note that this is just pedantry. There is no allocation to free. */
data_string_forget(&data_addr, MDL); data_string_forget(&data_addr, MDL);
return ISC_R_SUCCESS; /* Return just the most recent status... */
return status;
} }
/* Look around for an address to give the client. First, look through the /* Look around for an address to give the client. First, look through the
@ -1951,11 +1943,14 @@ reply_process_is_addressed(struct reply_state *reply,
reply->valid = reply->send_valid; reply->valid = reply->send_valid;
#if 0 #if 0
/* XXX: Old 4.0.0 alpha code would change the host {} record /*
* XXX: uid upon lease assignment. I think this was an error; * XXX: Old 4.0.0 alpha code would change the host {} record
* XXX: it doesn't make sense to me now in retrospect to change * XXX: uid upon lease assignment. This was intended to cover the
* XXX: what is essentially configuration state with network * XXX: case where a client first identifies itself using vendor
* XXX: supplied values. * XXX: options in a solicit, or request, but later neglects to include
* XXX: these options in a Renew or Rebind. It is not clear that this
* XXX: is required, and has some startling ramnifications (such as
* XXX: how to recover this dynamic host {} state across restarts).
*/ */
if (reply->host != NULL) if (reply->host != NULL)
change_host_uid(host, reply->client_id->data, change_host_uid(host, reply->client_id->data,
@ -2021,7 +2016,7 @@ reply_process_send_addr(struct reply_state *reply, struct iaddr *addr) {
putULong(data.buffer->data + 16, reply->send_prefer); putULong(data.buffer->data + 16, reply->send_prefer);
putULong(data.buffer->data + 20, reply->send_valid); putULong(data.buffer->data + 20, reply->send_valid);
if (!save_option_buffer(&dhcpv6_universe, reply->reply_ia, if (!append_option_buffer(&dhcpv6_universe, reply->reply_ia,
data.buffer, data.buffer->data, data.buffer, data.buffer->data,
data.len, D6O_IAADDR, 0)) { data.len, D6O_IAADDR, 0)) {
log_error("reply_process_ia: unable to save IAADDR " log_error("reply_process_ia: unable to save IAADDR "