From: David Hankins Date: Fri, 5 Oct 2007 22:29:51 +0000 (+0000) Subject: - DDNS updates state information are now stored in 'binding scopes' that X-Git-Tag: v4_0_0b1~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=7285af3087709728f9a1917dcf7449ffca5b2f5f;p=thirdparty%2Fdhcp.git - 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. --- diff --git a/RELNOTES b/RELNOTES index 6ea594cf1..4c86c815c 100644 --- a/RELNOTES +++ b/RELNOTES @@ -84,6 +84,12 @@ suggested fixes to . - 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. diff --git a/common/discover.c b/common/discover.c index 350dc4768..8138d4243 100644 --- a/common/discover.c +++ b/common/discover.c @@ -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) { diff --git a/server/confpars.c b/server/confpars.c index 975fe7fcc..ed8244e52 100644 --- a/server/confpars.c +++ b/server/confpars.c @@ -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; diff --git a/server/db.c b/server/db.c index 24e2269c8..3c92c5fa9 100644 --- a/server/db.c +++ b/server/db.c @@ -36,6 +36,9 @@ #include #include +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; } diff --git a/server/dhcpv6.c b/server/dhcpv6.c index 13ac3e4e3..327114a38 100644 --- a/server/dhcpv6.c +++ b/server/dhcpv6.c @@ -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. diff --git a/server/mdb6.c b/server/mdb6.c index 938abe9d9..87269ee70 100644 --- a/server/mdb6.c +++ b/server/mdb6.c @@ -828,6 +828,14 @@ move_lease_to_inactive(struct ipv6_pool *pool, struct iaaddr *addr, old_heap_index = addr->heap_index; insert_result = isc_heap_insert(pool->inactive_timeouts, addr); if (insert_result == ISC_R_SUCCESS) { + /* Process events upon expiration. */ + ddns_removals(NULL, addr); + + /* Binding scopes are no longer valid after expiry or + * release. + */ + binding_scope_dereference(&addr->scope, MDL); + iaaddr_hash_delete(pool->addrs, &addr->addr, sizeof(addr->addr), MDL); isc_heap_delete(pool->active_timeouts, old_heap_index);