Commit 7285af30 authored by David Hankins's avatar David Hankins
Browse files

- DDNS updates state information are now stored in 'binding scopes' that

  follow the leases through their lifecycles.  This enables DDNS teardowns
  on leases that are assigned and expired inbetween a server restart (the
  state is recovered from dhcpd.leases).  Arbitrary user-specified binding
  scopes ('set var = "value";') are not yet supported.
parent 23863258
......@@ -84,6 +84,12 @@ suggested fixes to <dhcp-users@isc.org>.
- A core dump during expired lease cleanup has been repaired.
- DDNS updates state information are now stored in 'binding scopes' that
follow the leases through their lifecycles. This enables DDNS teardowns
on leases that are assigned and expired inbetween a server restart (the
state is recovered from dhcpd.leases). Arbitrary user-specified binding
scopes ('set var = "value";') are not yet supported.
Changes since 4.0.0a2
- Fix for startup where there are no IPv4 addresses on an interface.
......
......@@ -289,7 +289,10 @@ int
next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
struct LIFREQ *p;
struct LIFREQ tmp;
#if defined(sun) || defined(__linux)
/* Pointer used to remove interface aliases. */
char *s;
#endif
do {
if (ifaces->next >= ifaces->num) {
......
......@@ -36,6 +36,9 @@
static unsigned char global_host_once = 1;
static int parse_binding_value(struct parse *cfile,
struct binding_value *value);
#if defined (TRACING)
trace_type_t *trace_readconf_type;
trace_type_t *trace_readleases_type;
......@@ -2775,9 +2778,9 @@ int parse_lease_declaration (struct lease **lp, struct parse *cfile)
struct executable_statement *on;
int lose;
TIME t;
char *s;
int noequal, newbinding;
struct binding *binding;
struct binding_value *nv;
isc_result_t status;
struct option_cache *oc;
pair *p;
......@@ -3117,7 +3120,7 @@ int parse_lease_declaration (struct lease **lp, struct parse *cfile)
}
executable_statement_dereference (&on, MDL);
break;
case OPTION:
case SUPERSEDE:
noequal = 0;
......@@ -3167,6 +3170,7 @@ int parse_lease_declaration (struct lease **lp, struct parse *cfile)
binding = find_binding (lease -> scope, val);
else
binding = (struct binding *)0;
if (!binding) {
if (!lease -> scope)
if (!(binding_scope_allocate
......@@ -3185,14 +3189,11 @@ int parse_lease_declaration (struct lease **lp, struct parse *cfile)
strcpy (binding -> name, val);
newbinding = 1;
} else {
if (binding -> value)
binding_value_dereference (&binding -> value,
MDL);
newbinding = 0;
newbinding = 0;
}
if (!binding_value_allocate (&binding -> value, MDL))
log_fatal ("no memory for binding value.");
if (!binding_value_allocate(&nv, MDL))
log_fatal("no memory for binding value.");
if (!noequal) {
token = next_token (&val, (unsigned *)0, cfile);
......@@ -3203,91 +3204,28 @@ int parse_lease_declaration (struct lease **lp, struct parse *cfile)
}
}
token = peek_token (&val, (unsigned *)0, cfile);
if (token == STRING) {
token = next_token (&val, &buflen, cfile);
binding -> value -> type = binding_data;
binding -> value -> value.data.len = buflen;
if (!(buffer_allocate
(&binding -> value -> value.data.buffer,
buflen + 1, MDL)))
log_fatal ("No memory for binding.");
memcpy ((char *)
(binding -> value ->
value.data.buffer -> data),
val, buflen + 1);
binding -> value -> value.data.data =
binding -> value -> value.data.buffer -> data;
binding -> value -> value.data.terminated = 1;
} else if (token == NUMBER_OR_NAME) {
binding -> value -> type = binding_data;
s = ((char *)
(parse_numeric_aggregate
(cfile, (unsigned char *)0,
&binding -> value -> value.data.len,
':', 16, 8)));
if (!s) {
binding_value_dereference
(&binding -> value, MDL);
lease_dereference (&lease, MDL);
return 0;
}
if (binding -> value -> value.data.len) {
if (!(buffer_allocate
(&binding -> value -> value.data.buffer,
binding -> value -> value.data.len + 1,
MDL)))
log_fatal ("No memory for binding.");
memcpy ((binding -> value ->
value.data.buffer -> data), s,
binding -> value -> value.data.len);
dfree (s, MDL);
binding -> value -> value.data.data =
binding -> value -> value.data.buffer -> data;
}
} else if (token == PERCENT) {
token = next_token (&val, (unsigned *)0, cfile);
token = next_token (&val, (unsigned *)0, cfile);
if (token != NUMBER) {
parse_warn (cfile,
"expecting decimal number.");
if (token != SEMI)
skip_to_semi (cfile);
binding_value_dereference
(&binding -> value, MDL);
lease_dereference (&lease, MDL);
return 0;
}
binding -> value -> type = binding_numeric;
binding -> value -> value.intval = atol (val);
} else if (token == NAME) {
token = next_token (&val,
(unsigned *)0, cfile);
binding -> value -> type = binding_boolean;
if (!strcasecmp (val, "true"))
binding -> value -> value.boolean = 1;
else if (!strcasecmp (val, "false"))
binding -> value -> value.boolean = 0;
else
goto badbool;
} else {
badbool:
parse_warn (cfile,
"expecting a constant value.");
skip_to_semi (cfile);
binding_value_dereference (&binding -> value,
MDL);
lease_dereference (&lease, MDL);
if (!parse_binding_value(cfile, nv)) {
binding_value_dereference(&nv, MDL);
lease_dereference(&lease, MDL);
return 0;
}
if (newbinding) {
binding -> next = lease -> scope -> bindings;
lease -> scope -> bindings = binding;
binding_value_reference(&binding->value,
nv, MDL);
binding->next = lease->scope->bindings;
lease->scope->bindings = binding;
} else {
binding_value_dereference(&binding->value, MDL);
binding_value_reference(&binding->value,
nv, MDL);
}
parse_semi (cfile);
binding_value_dereference(&nv, MDL);
parse_semi(cfile);
break;
/* case NAME: */
default:
if (!strcasecmp (val, "ddns-fwd-name")) {
seenbit = 4096;
......@@ -3297,7 +3235,9 @@ int parse_lease_declaration (struct lease **lp, struct parse *cfile)
seenbit = 8192;
noequal = 1;
goto special_set;
}
} else
parse_warn(cfile, "Unexpected configuration "
"directive.");
skip_to_semi (cfile);
seenbit = 0;
lease_dereference (&lease, MDL);
......@@ -3343,6 +3283,95 @@ int parse_lease_declaration (struct lease **lp, struct parse *cfile)
return 1;
}
/* Parse the right side of a 'binding value'.
*
* set foo = "bar"; is a string
* set foo = false; is a boolean
* set foo = %31; is a numeric value.
*/
static int
parse_binding_value(struct parse *cfile, struct binding_value *value)
{
struct data_string *data;
unsigned char *s;
const char *val;
unsigned buflen;
int token;
if ((cfile == NULL) || (value == NULL))
log_fatal("Invalid arguments at %s:%d.", MDL);
token = peek_token(&val, NULL, cfile);
if (token == STRING) {
token = next_token(&val, &buflen, cfile);
value->type = binding_data;
value->value.data.len = buflen;
data = &value->value.data;
if (!buffer_allocate(&data->buffer, buflen + 1, MDL))
log_fatal ("No memory for binding.");
memcpy(data->buffer->data, val, buflen + 1);
data->data = data->buffer->data;
data->terminated = 1;
} else if (token == NUMBER_OR_NAME) {
value->type = binding_data;
data = &value->value.data;
s = parse_numeric_aggregate(cfile, NULL, &data->len,
':', 16, 8);
if (s == NULL) {
skip_to_semi(cfile);
return 0;
}
if (data->len) {
if (!buffer_allocate(&data->buffer, data->len + 1,
MDL))
log_fatal("No memory for binding.");
memcpy(data->buffer->data, s, data->len);
data->data = data->buffer->data;
dfree (s, MDL);
}
} else if (token == PERCENT) {
token = next_token(&val, NULL, cfile);
token = next_token(&val, NULL, cfile);
if (token != NUMBER) {
parse_warn(cfile, "expecting decimal number.");
if (token != SEMI)
skip_to_semi(cfile);
return 0;
}
value->type = binding_numeric;
value->value.intval = atol(val);
} else if (token == NAME) {
token = next_token(&val, NULL, cfile);
value->type = binding_boolean;
if (!strcasecmp(val, "true"))
value->value.boolean = 1;
else if (!strcasecmp(val, "false"))
value->value.boolean = 0;
else {
parse_warn(cfile, "expecting true or false");
if (token != SEMI)
skip_to_semi(cfile);
return 0;
}
} else {
parse_warn (cfile, "expecting a constant value.");
if (token != SEMI)
skip_to_semi (cfile);
return 0;
}
return 1;
}
/* address-range-declaration :== ip-address ip-address SEMI
| DYNAMIC_BOOTP ip-address ip-address SEMI */
......@@ -3758,6 +3787,10 @@ parse_ia_na_declaration(struct parse *cfile) {
struct iaaddr *iaaddr;
struct ipv6_pool *pool;
char addr_buf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
isc_boolean_t newbinding;
struct binding_scope *scope=NULL;
struct binding *bnd;
struct binding_value *nv=NULL;
token = next_token(&val, &len, cfile);
if (token != STRING) {
......@@ -3818,7 +3851,9 @@ parse_ia_na_declaration(struct parse *cfile) {
token = next_token(&val, NULL, cfile);
if (token == RBRACE) break;
if (token == BINDING) {
switch(token) {
/* Lease binding state. */
case BINDING:
token = next_token(&val, NULL, cfile);
if (token != STATE) {
parse_warn(cfile, "corrupt lease file; "
......@@ -3859,15 +3894,100 @@ parse_ia_na_declaration(struct parse *cfile) {
"expecting "
"semicolon.");
}
break;
} else if (token == ENDS) {
/* Lease expiration time. */
case ENDS:
end_time = parse_date(cfile);
} else {
break;
/* Lease binding scopes. */
case TOKEN_SET:
token = next_token(&val, NULL, cfile);
if ((token != NAME) &&
(token != NUMBER_OR_NAME)) {
parse_warn(cfile, "%s is not a valid "
"variable name",
val);
skip_to_semi(cfile);
continue;
}
if (scope != NULL)
bnd = find_binding(scope, val);
else {
if (!binding_scope_allocate(&scope,
MDL)) {
log_fatal("Out of memory for "
"lease binding "
"scope.");
}
bnd = NULL;
}
if (bnd == NULL) {
bnd = dmalloc(sizeof(*bnd),
MDL);
if (bnd == NULL) {
log_fatal("No memory for "
"lease binding.");
}
bnd->name = dmalloc(strlen(val) + 1,
MDL);
if (bnd->name == NULL) {
log_fatal("No memory for "
"binding name.");
}
strcpy(bnd->name, val);
newbinding = ISC_TRUE;
} else {
newbinding = ISC_FALSE;
}
if (!binding_value_allocate(&nv, MDL)) {
log_fatal("no memory for binding "
"value.");
}
token = next_token(NULL, NULL, cfile);
if (token != EQUAL) {
parse_warn(cfile, "expecting '=' in "
"set statement.");
goto binding_err;
}
if (!parse_binding_value(cfile, nv)) {
binding_err:
binding_value_dereference(&nv, MDL);
binding_scope_dereference(&scope, MDL);
return;
}
if (newbinding) {
binding_value_reference(&bnd->value,
nv, MDL);
bnd->next = scope->bindings;
scope->bindings = bnd;
} else {
binding_value_dereference(&bnd->value,
MDL);
binding_value_reference(&bnd->value,
nv, MDL);
}
binding_value_dereference(&nv, MDL);
parse_semi(cfile);
break;
default:
parse_warn(cfile, "corrupt lease file; "
"expecting binding or ends, "
"expecting ia_na contents, "
"got '%s'", val);
skip_to_semi(cfile);
return;
continue;
}
}
......@@ -3890,6 +4010,11 @@ parse_ia_na_declaration(struct parse *cfile) {
iaaddr->state = state;
iaaddr->valid_lifetime_end_time = end_time;
if (scope != NULL) {
binding_scope_reference(&iaaddr->scope, scope, MDL);
binding_scope_dereference(&scope, MDL);
}
/* add to our various structures */
ia_na_add_iaaddr(ia_na, iaaddr, MDL);
pool = NULL;
......
......@@ -36,6 +36,9 @@
#include <ctype.h>
#include <errno.h>
static isc_result_t write_binding_scope(FILE *db_file, struct binding *bnd,
char *prepend);
FILE *db_file;
static int counting = 0;
......@@ -43,6 +46,58 @@ static int count = 0;
TIME write_time;
int lease_file_is_corrupt = 0;
/* Write a single binding scope value in parsable format.
*/
static isc_result_t
write_binding_scope(FILE *db_file, struct binding *bnd, char *prepend) {
char *s;
if ((db_file == NULL) || (bnd == NULL) || (prepend == NULL))
return ISC_R_INVALIDARG;
if (bnd->value->type == binding_data) {
if (bnd->value->value.data.data != NULL) {
s = quotify_buf(bnd->value->value.data.data,
bnd->value->value.data.len, MDL);
if (s != NULL) {
errno = 0;
fprintf(db_file, "%sset %s = \"%s\";",
prepend, bnd->name, s);
if (errno)
return ISC_R_FAILURE;
dfree(s, MDL);
} else {
return ISC_R_FAILURE;
}
}
} else if (bnd->value->type == binding_numeric) {
errno = 0;
fprintf(db_file, "%sset %s = %%%ld;", prepend,
bnd->name, bnd->value->value.intval);
if (errno)
return ISC_R_FAILURE;
} else if (bnd->value->type == binding_boolean) {
errno = 0;
fprintf(db_file, "%sset %s = %s;", prepend, bnd->name,
bnd->value->value.intval ? "true" : "false");
if (errno)
return ISC_R_FAILURE;
} else if (bnd->value->type == binding_dns) {
log_error("%s: persistent dns values not supported.",
bnd->name);
} else if (bnd->value->type == binding_function) {
log_error("%s: persistent functions not supported.",
bnd->name);
} else {
log_fatal("%s: unknown binding type %d", bnd->name,
bnd->value->type);
}
return ISC_R_SUCCESS;
}
/* Write the specified lease to the current lease database file. */
int write_lease (lease)
......@@ -152,49 +207,17 @@ int write_lease (lease)
} else
++errors;
}
if (lease -> scope) {
for (b = lease -> scope -> bindings; b; b = b -> next) {
if (!b -> value)
if (lease->scope != NULL) {
for (b = lease->scope->bindings; b; b = b->next) {
if (!b->value)
continue;
if (b -> value -> type == binding_data) {
if (b -> value -> value.data.data) {
s = quotify_buf (b -> value -> value.data.data,
b -> value -> value.data.len, MDL);
if (s) {
errno = 0;
fprintf (db_file, "\n set %s = \"%s\";",
b -> name, s);
if (errno)
++errors;
dfree (s, MDL);
} else
++errors;
}
} else if (b -> value -> type == binding_numeric) {
errno = 0;
fprintf (db_file, "\n set %s = %%%ld;",
b -> name, b -> value -> value.intval);
if (errno)
if (write_binding_scope(db_file, b, "\n ") != ISC_R_SUCCESS)
++errors;
} else if (b -> value -> type == binding_boolean) {
errno = 0;
fprintf (db_file, "\n set %s = %s;",
b -> name,
b -> value -> value.intval ? "true" : "false");
if (errno)
++errors;
} else if (b -> value -> type == binding_dns) {
log_error ("%s: persistent dns values not supported.",
b -> name);
} else if (b -> value -> type == binding_function) {
log_error ("%s: persistent functions not supported.",
b -> name);
} else {
log_error ("%s: unknown binding type %d",
b -> name, b -> value -> type);
}
}
}
if (lease -> agent_options) {
struct option_cache *oc;
struct data_string ds;
......@@ -479,6 +502,7 @@ int write_group (group)
int
write_ia_na(const struct ia_na *ia_na) {
struct iaaddr *iaaddr;
struct binding *bnd;
int i;
char addr_buf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff.255.255.255.255")];
const char *binding_state;
......@@ -526,6 +550,10 @@ write_ia_na(const struct ia_na *ia_na) {
binding_state) < 0) {
goto error_exit;
}
/* Note that from here on out, the \n is prepended to the
* next write, rather than appended to the current write.
*/
tval = print_time(iaaddr->valid_lifetime_end_time);
if (tval == NULL) {
goto error_exit;
......@@ -533,6 +561,28 @@ write_ia_na(const struct ia_na *ia_na) {
if (fprintf(db_file, " ends %s", tval) < 0) {
goto error_exit;
}
/* Write out any binding scopes: note that 'ends' above does
* not have \n on the end! We want that.
*/
if (iaaddr->scope != NULL)
bnd = iaaddr->scope->bindings;
else
bnd = NULL;
for (; bnd != NULL ; bnd = bnd->next) {
if (bnd->value == NULL)
continue;
/* We don't do a regular error_exit because the
* lease db is not corrupt in this case.
*/
if (write_binding_scope(db_file, bnd,
"\n ") != ISC_R_SUCCESS)
goto error_exit;
}
if (fprintf(db_file, "\n }\n") < 0)
goto error_exit;
}
......
......@@ -1597,8 +1597,6 @@ lease_to_client(struct data_string *reply_ret,
ia_na->iaid_duid.data,
ia_na->iaid_duid.len,
ia_na, MDL);
write_ia_na(ia_na);
schedule_lease_timeout(lease->ipv6_pool);
/* If this constitutes a binding, and we
* are performing ddns updates, then give
......@@ -1622,6 +1620,9 @@ lease_to_client(struct data_string *reply_ret,
lease, /* XXX */ NULL,
opt_state);
}
write_ia_na(ia_na);
schedule_lease_timeout(lease->ipv6_pool);
/*
* On SOLICIT, we want to forget this lease since we're
* not actually doing anything with it.
......