]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
- DDNS updates state information are now stored in 'binding scopes' that
authorDavid Hankins <dhankins@isc.org>
Fri, 5 Oct 2007 22:29:51 +0000 (22:29 +0000)
committerDavid Hankins <dhankins@isc.org>
Fri, 5 Oct 2007 22:29:51 +0000 (22:29 +0000)
  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.

RELNOTES
common/discover.c
server/confpars.c
server/db.c
server/dhcpv6.c
server/mdb6.c

index 6ea594cf1caec718101b0a632af5eaac855baed7..4c86c815c2624da4f340c6b17d0365c990847236 100644 (file)
--- a/RELNOTES
+++ b/RELNOTES
@@ -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.
index 350dc476867059ee4a64a4bffd6b683f4b152e4a..8138d424375ab0d0e4710f0c8eb9d565e5596cf4 100644 (file)
@@ -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) {
index 975fe7fccc520203c5644b71e22bd15f5eca5bad..ed8244e52d1a2d7364ba58f77d0040dcd980e729 100644 (file)
@@ -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;
index 24e2269c8c99e9d0b78da1778610602ff9343cfd..3c92c5fa901ec940c23ace7c197f6088ffb30558 100644 (file)
@@ -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;
        }
index 13ac3e4e3f422127ca3f709e58f16fea07f4a668..327114a38228ad19cf32e45ae454d153b86ef5c5 100644 (file)
@@ -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.
index 938abe9d90f64d928575d9918ffc6f587e7c93dd..87269ee705f699bb3a8b58cc619b67a942ac0990 100644 (file)
@@ -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);