server/mdb6.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2007-2013 by Internet Systems Consortium, Inc. ("ISC")
00003  *
00004  * Permission to use, copy, modify, and distribute this software for any
00005  * purpose with or without fee is hereby granted, provided that the above
00006  * copyright notice and this permission notice appear in all copies.
00007  *
00008  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
00009  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
00010  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
00011  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
00012  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
00013  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
00014  * PERFORMANCE OF THIS SOFTWARE.
00015  */
00016 
00168 #include "config.h"
00169 
00170 #include <sys/types.h>
00171 #include <time.h>
00172 #include <netinet/in.h>
00173 
00174 #include <stdarg.h>
00175 #include "dhcpd.h"
00176 #include "omapip/omapip.h"
00177 #include "omapip/hash.h"
00178 #include <isc/md5.h>
00179 
00180 HASH_FUNCTIONS(ia, unsigned char *, struct ia_xx, ia_hash_t,
00181                ia_reference, ia_dereference, do_string_hash)
00182 
00183 ia_hash_t *ia_na_active;
00184 ia_hash_t *ia_ta_active;
00185 ia_hash_t *ia_pd_active;
00186 
00187 HASH_FUNCTIONS(iasubopt, struct in6_addr *, struct iasubopt, iasubopt_hash_t,
00188                iasubopt_reference, iasubopt_dereference, do_string_hash)
00189 
00190 struct ipv6_pool **pools;
00191 int num_pools;
00192 
00193 /*
00194  * Create a new IAADDR/PREFIX structure.
00195  *
00196  * - iasubopt must be a pointer to a (struct iasubopt *) pointer previously
00197  *   initialized to NULL
00198  */
00199 isc_result_t
00200 iasubopt_allocate(struct iasubopt **iasubopt, const char *file, int line) {
00201         struct iasubopt *tmp;
00202 
00203         if (iasubopt == NULL) {
00204                 log_error("%s(%d): NULL pointer reference", file, line);
00205                 return DHCP_R_INVALIDARG;
00206         }
00207         if (*iasubopt != NULL) {
00208                 log_error("%s(%d): non-NULL pointer", file, line);
00209                 return DHCP_R_INVALIDARG;
00210         }
00211 
00212         tmp = dmalloc(sizeof(*tmp), file, line);
00213         if (tmp == NULL) {
00214                 return ISC_R_NOMEMORY;
00215         }
00216 
00217         tmp->refcnt = 1;
00218         tmp->state = FTS_FREE;
00219         tmp->heap_index = -1;
00220         tmp->plen = 255;
00221 
00222         *iasubopt = tmp;
00223         return ISC_R_SUCCESS;
00224 }
00225 
00226 /*
00227  * Reference an IAADDR/PREFIX structure.
00228  *
00229  * - iasubopt must be a pointer to a (struct iasubopt *) pointer previously
00230  *   initialized to NULL
00231  */
00232 isc_result_t
00233 iasubopt_reference(struct iasubopt **iasubopt, struct iasubopt *src,
00234                  const char *file, int line) {
00235         if (iasubopt == NULL) {
00236                 log_error("%s(%d): NULL pointer reference", file, line);
00237                 return DHCP_R_INVALIDARG;
00238         }
00239         if (*iasubopt != NULL) {
00240                 log_error("%s(%d): non-NULL pointer", file, line);
00241                 return DHCP_R_INVALIDARG;
00242         }
00243         if (src == NULL) {
00244                 log_error("%s(%d): NULL pointer reference", file, line);
00245                 return DHCP_R_INVALIDARG;
00246         }
00247         *iasubopt = src;
00248         src->refcnt++;
00249         return ISC_R_SUCCESS;
00250 }
00251 
00252 
00253 /*
00254  * Dereference an IAADDR/PREFIX structure.
00255  *
00256  * If it is the last reference, then the memory for the 
00257  * structure is freed.
00258  */
00259 isc_result_t
00260 iasubopt_dereference(struct iasubopt **iasubopt, const char *file, int line) {
00261         struct iasubopt *tmp;
00262 
00263         if ((iasubopt == NULL) || (*iasubopt == NULL)) {
00264                 log_error("%s(%d): NULL pointer", file, line);
00265                 return DHCP_R_INVALIDARG;
00266         }
00267 
00268         tmp = *iasubopt;
00269         *iasubopt = NULL;
00270 
00271         tmp->refcnt--;
00272         if (tmp->refcnt < 0) {
00273                 log_error("%s(%d): negative refcnt", file, line);
00274                 tmp->refcnt = 0;
00275         }
00276         if (tmp->refcnt == 0) {
00277                 if (tmp->ia != NULL) {
00278                         ia_dereference(&(tmp->ia), file, line);
00279                 }
00280                 if (tmp->ipv6_pool != NULL) {
00281                         ipv6_pool_dereference(&(tmp->ipv6_pool), file, line);
00282                 }
00283                 if (tmp->scope != NULL) {
00284                         binding_scope_dereference(&tmp->scope, file, line);
00285                 }
00286 
00287                 if (tmp->on_star.on_expiry != NULL) {
00288                         executable_statement_dereference
00289                                 (&tmp->on_star.on_expiry, MDL);
00290                 }
00291                 if (tmp->on_star.on_commit != NULL) {
00292                         executable_statement_dereference
00293                                 (&tmp->on_star.on_commit, MDL);
00294                 }
00295                 if (tmp->on_star.on_release != NULL) {
00296                         executable_statement_dereference
00297                                 (&tmp->on_star.on_release, MDL);
00298                 }
00299 
00300                 dfree(tmp, file, line);
00301         }
00302 
00303         return ISC_R_SUCCESS;
00304 }
00305 
00306 /* 
00307  * Make the key that we use for IA.
00308  */
00309 isc_result_t
00310 ia_make_key(struct data_string *key, u_int32_t iaid,
00311             const char *duid, unsigned int duid_len,
00312             const char *file, int line) {
00313 
00314         memset(key, 0, sizeof(*key));
00315         key->len = duid_len + sizeof(iaid);
00316         if (!buffer_allocate(&(key->buffer), key->len, file, line)) {
00317                 return ISC_R_NOMEMORY;
00318         }
00319         key->data = key->buffer->data;
00320         memcpy((char *)key->data, &iaid, sizeof(iaid));
00321         memcpy((char *)key->data + sizeof(iaid), duid, duid_len);
00322 
00323         return ISC_R_SUCCESS;
00324 }
00325 
00326 /*
00327  * Create a new IA structure.
00328  *
00329  * - ia must be a pointer to a (struct ia_xx *) pointer previously
00330  *   initialized to NULL
00331  * - iaid and duid are values from the client
00332  *
00333  * XXXsk: we don't concern ourself with the byte order of the IAID, 
00334  *        which might be a problem if we transfer this structure 
00335  *        between machines of different byte order
00336  */
00337 isc_result_t
00338 ia_allocate(struct ia_xx **ia, u_int32_t iaid, 
00339             const char *duid, unsigned int duid_len,
00340             const char *file, int line) {
00341         struct ia_xx *tmp;
00342 
00343         if (ia == NULL) {
00344                 log_error("%s(%d): NULL pointer reference", file, line);
00345                 return DHCP_R_INVALIDARG;
00346         }
00347         if (*ia != NULL) {
00348                 log_error("%s(%d): non-NULL pointer", file, line);
00349                 return DHCP_R_INVALIDARG;
00350         }
00351 
00352         tmp = dmalloc(sizeof(*tmp), file, line);
00353         if (tmp == NULL) {
00354                 return ISC_R_NOMEMORY;
00355         }
00356 
00357         if (ia_make_key(&tmp->iaid_duid, iaid, 
00358                         duid, duid_len, file, line) != ISC_R_SUCCESS) {
00359                 dfree(tmp, file, line);
00360                 return ISC_R_NOMEMORY;
00361         }
00362 
00363         tmp->refcnt = 1;
00364 
00365         *ia = tmp;
00366         return ISC_R_SUCCESS;
00367 }
00368 
00369 /*
00370  * Reference an IA structure.
00371  *
00372  * - ia must be a pointer to a (struct ia_xx *) pointer previously
00373  *   initialized to NULL
00374  */
00375 isc_result_t
00376 ia_reference(struct ia_xx **ia, struct ia_xx *src,
00377              const char *file, int line) {
00378         if (ia == NULL) {
00379                 log_error("%s(%d): NULL pointer reference", file, line);
00380                 return DHCP_R_INVALIDARG;
00381         }
00382         if (*ia != NULL) {
00383                 log_error("%s(%d): non-NULL pointer", file, line);
00384                 return DHCP_R_INVALIDARG;
00385         }
00386         if (src == NULL) {
00387                 log_error("%s(%d): NULL pointer reference", file, line);
00388                 return DHCP_R_INVALIDARG;
00389         }
00390         *ia = src;
00391         src->refcnt++;
00392         return ISC_R_SUCCESS;
00393 }
00394 
00395 /*
00396  * Dereference an IA structure.
00397  *
00398  * If it is the last reference, then the memory for the 
00399  * structure is freed.
00400  */
00401 isc_result_t
00402 ia_dereference(struct ia_xx **ia, const char *file, int line) {
00403         struct ia_xx *tmp;
00404         int i;
00405 
00406         if ((ia == NULL) || (*ia == NULL)) {
00407                 log_error("%s(%d): NULL pointer", file, line);
00408                 return DHCP_R_INVALIDARG;
00409         }
00410 
00411         tmp = *ia;
00412         *ia = NULL;
00413 
00414         tmp->refcnt--;
00415         if (tmp->refcnt < 0) {
00416                 log_error("%s(%d): negative refcnt", file, line);
00417                 tmp->refcnt = 0;
00418         }
00419         if (tmp->refcnt == 0) {
00420                 if (tmp->iasubopt != NULL) {
00421                         for (i=0; i<tmp->num_iasubopt; i++) {
00422                                 iasubopt_dereference(&(tmp->iasubopt[i]), 
00423                                                      file, line);
00424                         }
00425                         dfree(tmp->iasubopt, file, line);
00426                 }
00427                 data_string_forget(&(tmp->iaid_duid), file, line);
00428                 dfree(tmp, file, line);
00429         }
00430         return ISC_R_SUCCESS;
00431 }
00432 
00433 
00434 /*
00435  * Add an IAADDR/PREFIX entry to an IA structure.
00436  */
00437 isc_result_t
00438 ia_add_iasubopt(struct ia_xx *ia, struct iasubopt *iasubopt, 
00439                 const char *file, int line) {
00440         int max;
00441         struct iasubopt **new;
00442 
00443         /* 
00444          * Grow our array if we need to.
00445          * 
00446          * Note: we pick 4 as the increment, as that seems a reasonable
00447          *       guess as to how many addresses/prefixes we might expect
00448          *       on an interface.
00449          */
00450         if (ia->max_iasubopt <= ia->num_iasubopt) {
00451                 max = ia->max_iasubopt + 4;
00452                 new = dmalloc(max * sizeof(struct iasubopt *), file, line);
00453                 if (new == NULL) {
00454                         return ISC_R_NOMEMORY;
00455                 }
00456                 memcpy(new, ia->iasubopt, 
00457                        ia->num_iasubopt * sizeof(struct iasubopt *));
00458                 ia->iasubopt = new;
00459                 ia->max_iasubopt = max;
00460         }
00461 
00462         iasubopt_reference(&(ia->iasubopt[ia->num_iasubopt]), iasubopt, 
00463                            file, line);
00464         ia->num_iasubopt++;
00465 
00466         return ISC_R_SUCCESS;
00467 }
00468 
00469 /*
00470  * Remove an IAADDR/PREFIX entry to an IA structure.
00471  *
00472  * Note: if a suboption appears more than once, then only ONE will be removed.
00473  */
00474 void
00475 ia_remove_iasubopt(struct ia_xx *ia, struct iasubopt *iasubopt,
00476                    const char *file, int line) {
00477         int i, j;
00478         if (ia == NULL || iasubopt == NULL)
00479             return;
00480 
00481         for (i=0; i<ia->num_iasubopt; i++) {
00482                 if (ia->iasubopt[i] == iasubopt) {
00483                         /* remove this sub option */
00484                         iasubopt_dereference(&(ia->iasubopt[i]), file, line);
00485                         /* move remaining suboption pointers down one */
00486                         for (j=i+1; j < ia->num_iasubopt; j++) {
00487                                 ia->iasubopt[j-1] = ia->iasubopt[j];
00488                         }
00489                         /* decrease our total count */
00490                         /* remove the back-reference in the suboption itself */
00491                         ia_dereference(&iasubopt->ia, file, line);
00492                         ia->num_iasubopt--;
00493                         return;
00494                 }
00495         }
00496         log_error("%s(%d): IAADDR/PREFIX not in IA", file, line);
00497 }
00498 
00499 /*
00500  * Remove all addresses/prefixes from an IA.
00501  */
00502 void
00503 ia_remove_all_lease(struct ia_xx *ia, const char *file, int line) {
00504         int i;
00505 
00506         for (i=0; i<ia->num_iasubopt; i++) {
00507                 ia_dereference(&(ia->iasubopt[i]->ia), file, line);
00508                 iasubopt_dereference(&(ia->iasubopt[i]), file, line);
00509         }
00510         ia->num_iasubopt = 0;
00511 }
00512 
00513 /*
00514  * Compare two IA.
00515  */
00516 isc_boolean_t
00517 ia_equal(const struct ia_xx *a, const struct ia_xx *b) 
00518 {
00519         isc_boolean_t found;
00520         int i, j;
00521 
00522         /*
00523          * Handle cases where one or both of the inputs is NULL.
00524          */
00525         if (a == NULL) {
00526                 if (b == NULL) {
00527                         return ISC_TRUE;
00528                 } else {
00529                         return ISC_FALSE;
00530                 }
00531         }       
00532 
00533         /*
00534          * Check the type is the same.
00535          */
00536         if (a->ia_type != b->ia_type) {
00537                 return ISC_FALSE;
00538         }
00539 
00540         /*
00541          * Check the DUID is the same.
00542          */
00543         if (a->iaid_duid.len != b->iaid_duid.len) {
00544                 return ISC_FALSE;
00545         }
00546         if (memcmp(a->iaid_duid.data, 
00547                    b->iaid_duid.data, a->iaid_duid.len) != 0) {
00548                 return ISC_FALSE;
00549         }
00550 
00551         /*
00552          * Make sure we have the same number of addresses/prefixes in each.
00553          */
00554         if (a->num_iasubopt != b->num_iasubopt) {
00555                 return ISC_FALSE;
00556         }
00557 
00558         /*
00559          * Check that each address/prefix is present in both.
00560          */
00561         for (i=0; i<a->num_iasubopt; i++) {
00562                 found = ISC_FALSE;
00563                 for (j=0; j<a->num_iasubopt; j++) {
00564                         if (a->iasubopt[i]->plen != b->iasubopt[i]->plen)
00565                                 continue;
00566                         if (memcmp(&(a->iasubopt[i]->addr),
00567                                    &(b->iasubopt[j]->addr), 
00568                                    sizeof(struct in6_addr)) == 0) {
00569                                 found = ISC_TRUE;
00570                                 break;
00571                         }
00572                 }
00573                 if (!found) {
00574                         return ISC_FALSE;
00575                 }
00576         }
00577 
00578         /*
00579          * These are the same in every way we care about.
00580          */
00581         return ISC_TRUE;
00582 }
00583 
00584 /*
00585  * Helper function for lease heaps.
00586  * Makes the top of the heap the oldest lease.
00587  */
00588 static isc_boolean_t 
00589 lease_older(void *a, void *b) {
00590         struct iasubopt *la = (struct iasubopt *)a;
00591         struct iasubopt *lb = (struct iasubopt *)b;
00592 
00593         if (la->hard_lifetime_end_time == lb->hard_lifetime_end_time) {
00594                 return difftime(la->soft_lifetime_end_time,
00595                                 lb->soft_lifetime_end_time) < 0;
00596         } else {
00597                 return difftime(la->hard_lifetime_end_time, 
00598                                 lb->hard_lifetime_end_time) < 0;
00599         }
00600 }
00601 
00602 /*
00603  * Helper function for lease address/prefix heaps.
00604  * Callback when an address's position in the heap changes.
00605  */
00606 static void
00607 lease_index_changed(void *iasubopt, unsigned int new_heap_index) {
00608         ((struct iasubopt *)iasubopt)-> heap_index = new_heap_index;
00609 }
00610 
00611 
00634 isc_result_t
00635 ipv6_pool_allocate(struct ipv6_pool **pool, u_int16_t type,
00636                    const struct in6_addr *start_addr, int bits, 
00637                    int units, const char *file, int line) {
00638         struct ipv6_pool *tmp;
00639 
00640         if (pool == NULL) {
00641                 log_error("%s(%d): NULL pointer reference", file, line);
00642                 return DHCP_R_INVALIDARG;
00643         }
00644         if (*pool != NULL) {
00645                 log_error("%s(%d): non-NULL pointer", file, line);
00646                 return DHCP_R_INVALIDARG;
00647         }
00648 
00649         tmp = dmalloc(sizeof(*tmp), file, line);
00650         if (tmp == NULL) {
00651                 return ISC_R_NOMEMORY;
00652         }
00653 
00654         tmp->refcnt = 1;
00655         tmp->pool_type = type;
00656         tmp->start_addr = *start_addr;
00657         tmp->bits = bits;
00658         tmp->units = units;
00659         if (!iasubopt_new_hash(&tmp->leases, DEFAULT_HASH_SIZE, file, line)) {
00660                 dfree(tmp, file, line);
00661                 return ISC_R_NOMEMORY;
00662         }
00663         if (isc_heap_create(dhcp_gbl_ctx.mctx, lease_older, lease_index_changed,
00664                             0, &(tmp->active_timeouts)) != ISC_R_SUCCESS) {
00665                 iasubopt_free_hash_table(&(tmp->leases), file, line);
00666                 dfree(tmp, file, line);
00667                 return ISC_R_NOMEMORY;
00668         }
00669         if (isc_heap_create(dhcp_gbl_ctx.mctx, lease_older, lease_index_changed,
00670                             0, &(tmp->inactive_timeouts)) != ISC_R_SUCCESS) {
00671                 isc_heap_destroy(&(tmp->active_timeouts));
00672                 iasubopt_free_hash_table(&(tmp->leases), file, line);
00673                 dfree(tmp, file, line);
00674                 return ISC_R_NOMEMORY;
00675         }
00676 
00677         *pool = tmp;
00678         return ISC_R_SUCCESS;
00679 }
00680 
00700 isc_result_t
00701 ipv6_pool_reference(struct ipv6_pool **pool, struct ipv6_pool *src,
00702                     const char *file, int line) {
00703         if (pool == NULL) {
00704                 log_error("%s(%d): NULL pointer reference", file, line);
00705                 return DHCP_R_INVALIDARG;
00706         }
00707         if (*pool != NULL) {
00708                 log_error("%s(%d): non-NULL pointer", file, line);
00709                 return DHCP_R_INVALIDARG;
00710         }
00711         if (src == NULL) {
00712                 log_error("%s(%d): NULL pointer reference", file, line);
00713                 return DHCP_R_INVALIDARG;
00714         }
00715         *pool = src;
00716         src->refcnt++;
00717         return ISC_R_SUCCESS;
00718 }
00719 
00720 /* 
00721  * Note: Each IAADDR/PREFIX in a pool is referenced by the pool. This is needed
00722  * to prevent the lease from being garbage collected out from under the
00723  * pool.
00724  *
00725  * The references are made from the hash and from the heap. The following
00726  * helper functions dereference these when a pool is destroyed.
00727  */
00728 
00729 /*
00730  * Helper function for pool cleanup.
00731  * Dereference each of the hash entries in a pool.
00732  */
00733 static isc_result_t 
00734 dereference_hash_entry(const void *name, unsigned len, void *value) {
00735         struct iasubopt *iasubopt = (struct iasubopt *)value;
00736 
00737         iasubopt_dereference(&iasubopt, MDL);
00738         return ISC_R_SUCCESS;
00739 }
00740 
00741 /*
00742  * Helper function for pool cleanup.
00743  * Dereference each of the heap entries in a pool.
00744  */
00745 static void
00746 dereference_heap_entry(void *value, void *dummy) {
00747         struct iasubopt *iasubopt = (struct iasubopt *)value;
00748 
00749         iasubopt_dereference(&iasubopt, MDL);
00750 }
00751 
00771 isc_result_t
00772 ipv6_pool_dereference(struct ipv6_pool **pool, const char *file, int line) {
00773         struct ipv6_pool *tmp;
00774 
00775         if ((pool == NULL) || (*pool == NULL)) {
00776                 log_error("%s(%d): NULL pointer", file, line);
00777                 return DHCP_R_INVALIDARG;
00778         }
00779 
00780         tmp = *pool;
00781         *pool = NULL;
00782 
00783         tmp->refcnt--;
00784         if (tmp->refcnt < 0) {
00785                 log_error("%s(%d): negative refcnt", file, line);
00786                 tmp->refcnt = 0;
00787         }
00788         if (tmp->refcnt == 0) {
00789                 iasubopt_hash_foreach(tmp->leases, dereference_hash_entry);
00790                 iasubopt_free_hash_table(&(tmp->leases), file, line);
00791                 isc_heap_foreach(tmp->active_timeouts, 
00792                                  dereference_heap_entry, NULL);
00793                 isc_heap_destroy(&(tmp->active_timeouts));
00794                 isc_heap_foreach(tmp->inactive_timeouts, 
00795                                  dereference_heap_entry, NULL);
00796                 isc_heap_destroy(&(tmp->inactive_timeouts));
00797                 dfree(tmp, file, line);
00798         }
00799 
00800         return ISC_R_SUCCESS;
00801 }
00802 
00803 /* 
00804  * Create an address by hashing the input, and using that for
00805  * the non-network part.
00806  */
00807 static void
00808 build_address6(struct in6_addr *addr, 
00809                const struct in6_addr *net_start_addr, int net_bits, 
00810                const struct data_string *input) {
00811         isc_md5_t ctx;
00812         int net_bytes;
00813         int i;
00814         char *str;
00815         const char *net_str;
00816 
00817         /* 
00818          * Use MD5 to get a nice 128 bit hash of the input.
00819          * Yes, we know MD5 isn't cryptographically sound. 
00820          * No, we don't care.
00821          */
00822         isc_md5_init(&ctx);
00823         isc_md5_update(&ctx, input->data, input->len);
00824         isc_md5_final(&ctx, (unsigned char *)addr);
00825 
00826         /*
00827          * Copy the [0..128] network bits over.
00828          */
00829         str = (char *)addr;
00830         net_str = (const char *)net_start_addr;
00831         net_bytes = net_bits / 8;
00832         for (i = 0; i < net_bytes; i++) {
00833                 str[i] = net_str[i];
00834         }
00835         switch (net_bits % 8) {
00836                 case 1: str[i] = (str[i] & 0x7F) | (net_str[i] & 0x80); break;
00837                 case 2: str[i] = (str[i] & 0x3F) | (net_str[i] & 0xC0); break;
00838                 case 3: str[i] = (str[i] & 0x1F) | (net_str[i] & 0xE0); break;
00839                 case 4: str[i] = (str[i] & 0x0F) | (net_str[i] & 0xF0); break;
00840                 case 5: str[i] = (str[i] & 0x07) | (net_str[i] & 0xF8); break;
00841                 case 6: str[i] = (str[i] & 0x03) | (net_str[i] & 0xFC); break;
00842                 case 7: str[i] = (str[i] & 0x01) | (net_str[i] & 0xFE); break;
00843         }
00844 
00845         /*
00846          * Set the universal/local bit ("u bit") to zero for /64s.  The
00847          * individual/group bit ("g bit") is unchanged, because the g-bit
00848          * has no meaning when the u-bit is cleared.
00849          */
00850         if (net_bits == 64)
00851                 str[8] &= ~0x02;
00852 }
00853 
00854 /* 
00855  * Create a temporary address by a variant of RFC 4941 algo.
00856  * Note: this should not be used for prefixes shorter than 64 bits.
00857  */
00858 static void
00859 build_temporary6(struct in6_addr *addr, 
00860                  const struct in6_addr *net_start_addr, int net_bits,
00861                  const struct data_string *input) {
00862         static u_int32_t history[2];
00863         static u_int32_t counter = 0;
00864         isc_md5_t ctx;
00865         unsigned char md[16];
00866 
00867         /*
00868          * First time/time to reseed.
00869          * Please use a good pseudo-random generator here!
00870          */
00871         if (counter == 0) {
00872                 isc_random_get(&history[0]);
00873                 isc_random_get(&history[1]);
00874         }
00875 
00876         /* 
00877          * Use MD5 as recommended by RFC 4941.
00878          */
00879         isc_md5_init(&ctx);
00880         isc_md5_update(&ctx, (unsigned char *)&history[0], 8UL);
00881         isc_md5_update(&ctx, input->data, input->len);
00882         isc_md5_final(&ctx, md);
00883 
00884         /*
00885          * Build the address.
00886          */
00887         if (net_bits == 64) {
00888                 memcpy(&addr->s6_addr[0], &net_start_addr->s6_addr[0], 8);
00889                 memcpy(&addr->s6_addr[8], md, 8);
00890                 addr->s6_addr[8] &= ~0x02;
00891         } else {
00892                 int net_bytes;
00893                 int i;
00894                 char *str;
00895                 const char *net_str;
00896 
00897                 /*
00898                  * Copy the [0..128] network bits over.
00899                  */
00900                 str = (char *)addr;
00901                 net_str = (const char *)net_start_addr;
00902                 net_bytes = net_bits / 8;
00903                 for (i = 0; i < net_bytes; i++) {
00904                         str[i] = net_str[i];
00905                 }
00906                 memcpy(str + net_bytes, md, 16 - net_bytes);
00907                 switch (net_bits % 8) {
00908                 case 1: str[i] = (str[i] & 0x7F) | (net_str[i] & 0x80); break;
00909                 case 2: str[i] = (str[i] & 0x3F) | (net_str[i] & 0xC0); break;
00910                 case 3: str[i] = (str[i] & 0x1F) | (net_str[i] & 0xE0); break;
00911                 case 4: str[i] = (str[i] & 0x0F) | (net_str[i] & 0xF0); break;
00912                 case 5: str[i] = (str[i] & 0x07) | (net_str[i] & 0xF8); break;
00913                 case 6: str[i] = (str[i] & 0x03) | (net_str[i] & 0xFC); break;
00914                 case 7: str[i] = (str[i] & 0x01) | (net_str[i] & 0xFE); break;
00915                 }
00916         }
00917 
00918 
00919         /*
00920          * Save history for the next call.
00921          */
00922         memcpy((unsigned char *)&history[0], md + 8, 8);
00923         counter++;
00924 }
00925 
00926 /* Reserved Subnet Router Anycast ::0:0:0:0. */
00927 static struct in6_addr rtany;
00928 /* Reserved Subnet Anycasts ::fdff:ffff:ffff:ff80-::fdff:ffff:ffff:ffff. */
00929 static struct in6_addr resany;
00930 
00931 /*
00932  * Create a lease for the given address and client duid.
00933  *
00934  * - pool must be a pointer to a (struct ipv6_pool *) pointer previously
00935  *   initialized to NULL
00936  *
00937  * Right now we simply hash the DUID, and if we get a collision, we hash 
00938  * again until we find a free address. We try this a fixed number of times,
00939  * to avoid getting stuck in a loop (this is important on small pools
00940  * where we can run out of space).
00941  *
00942  * We return the number of attempts that it took to find an available
00943  * lease. This tells callers when a pool is are filling up, as
00944  * well as an indication of how full the pool is; statistically the 
00945  * more full a pool is the more attempts must be made before finding
00946  * a free lease. Realistically this will only happen in very full
00947  * pools.
00948  *
00949  * We probably want different algorithms depending on the network size, in
00950  * the long term.
00951  */
00952 isc_result_t
00953 create_lease6(struct ipv6_pool *pool, struct iasubopt **addr, 
00954               unsigned int *attempts,
00955               const struct data_string *uid, time_t soft_lifetime_end_time) {
00956         struct data_string ds;
00957         struct in6_addr tmp;
00958         struct iasubopt *test_iaaddr;
00959         struct data_string new_ds;
00960         struct iasubopt *iaaddr;
00961         isc_result_t result;
00962         isc_boolean_t reserved_iid;
00963         static isc_boolean_t init_resiid = ISC_FALSE;
00964 
00965         /*
00966          * Fill the reserved IIDs.
00967          */
00968         if (!init_resiid) {
00969                 memset(&rtany, 0, 16);
00970                 memset(&resany, 0, 8);
00971                 resany.s6_addr[8] = 0xfd;
00972                 memset(&resany.s6_addr[9], 0xff, 6);
00973                 init_resiid = ISC_TRUE;
00974         }
00975 
00976         /* 
00977          * Use the UID as our initial seed for the hash
00978          */
00979         memset(&ds, 0, sizeof(ds));
00980         data_string_copy(&ds, (struct data_string *)uid, MDL);
00981 
00982         *attempts = 0;
00983         for (;;) {
00984                 /*
00985                  * Give up at some point.
00986                  */
00987                 if (++(*attempts) > 100) {
00988                         data_string_forget(&ds, MDL);
00989                         return ISC_R_NORESOURCES;
00990                 }
00991 
00992                 /* 
00993                  * Build a resource.
00994                  */
00995                 switch (pool->pool_type) {
00996                 case D6O_IA_NA:
00997                         /* address */
00998                         build_address6(&tmp, &pool->start_addr,
00999                                        pool->bits, &ds);
01000                         break;
01001                 case D6O_IA_TA:
01002                         /* temporary address */
01003                         build_temporary6(&tmp, &pool->start_addr,
01004                                          pool->bits, &ds);
01005                         break;
01006                 case D6O_IA_PD:
01007                         /* prefix */
01008                         log_error("create_lease6: prefix pool.");
01009                         return DHCP_R_INVALIDARG;
01010                 default:
01011                         log_error("create_lease6: untyped pool.");
01012                         return DHCP_R_INVALIDARG;
01013                 }
01014 
01015                 /*
01016                  * Avoid reserved interface IDs. (cf. RFC 5453)
01017                  */
01018                 reserved_iid = ISC_FALSE;
01019                 if (memcmp(&tmp.s6_addr[8], &rtany.s6_addr[8], 8) == 0) {
01020                         reserved_iid = ISC_TRUE;
01021                 }
01022                 if (!reserved_iid &&
01023                     (memcmp(&tmp.s6_addr[8], &resany.s6_addr[8], 7) == 0) &&
01024                     ((tmp.s6_addr[15] & 0x80) == 0x80)) {
01025                         reserved_iid = ISC_TRUE;
01026                 }
01027 
01028                 /*
01029                  * If this address is not in use, we're happy with it
01030                  */
01031                 test_iaaddr = NULL;
01032                 if (!reserved_iid &&
01033                     (iasubopt_hash_lookup(&test_iaaddr, pool->leases,
01034                                           &tmp, sizeof(tmp), MDL) == 0)) {
01035                         break;
01036                 }
01037                 if (test_iaaddr != NULL)
01038                         iasubopt_dereference(&test_iaaddr, MDL);
01039 
01040                 /* 
01041                  * Otherwise, we create a new input, adding the address
01042                  */
01043                 memset(&new_ds, 0, sizeof(new_ds));
01044                 new_ds.len = ds.len + sizeof(tmp);
01045                 if (!buffer_allocate(&new_ds.buffer, new_ds.len, MDL)) {
01046                         data_string_forget(&ds, MDL);
01047                         return ISC_R_NOMEMORY;
01048                 }
01049                 new_ds.data = new_ds.buffer->data;
01050                 memcpy(new_ds.buffer->data, ds.data, ds.len);
01051                 memcpy(new_ds.buffer->data + ds.len, &tmp, sizeof(tmp));
01052                 data_string_forget(&ds, MDL);
01053                 data_string_copy(&ds, &new_ds, MDL);
01054                 data_string_forget(&new_ds, MDL);
01055         }
01056 
01057         data_string_forget(&ds, MDL);
01058 
01059         /* 
01060          * We're happy with the address, create an IAADDR
01061          * to hold it.
01062          */
01063         iaaddr = NULL;
01064         result = iasubopt_allocate(&iaaddr, MDL);
01065         if (result != ISC_R_SUCCESS) {
01066                 return result;
01067         }
01068         iaaddr->plen = 0;
01069         memcpy(&iaaddr->addr, &tmp, sizeof(iaaddr->addr));
01070 
01071         /*
01072          * Add the lease to the pool (note state is free, not active?!).
01073          */
01074         result = add_lease6(pool, iaaddr, soft_lifetime_end_time);
01075         if (result == ISC_R_SUCCESS) {
01076                 iasubopt_reference(addr, iaaddr, MDL);
01077         }
01078         iasubopt_dereference(&iaaddr, MDL);
01079         return result;
01080 }
01081 
01082 
01123 isc_result_t
01124 cleanup_lease6(ia_hash_t *ia_table,
01125                struct ipv6_pool *pool,
01126                struct iasubopt *lease,
01127                struct ia_xx *ia) {
01128 
01129         struct iasubopt *test_iasubopt, *tmp_iasubopt;
01130         struct ia_xx *old_ia;
01131         isc_result_t status = ISC_R_SUCCESS;
01132 
01133         test_iasubopt = NULL;
01134         old_ia = NULL;
01135 
01136         /*
01137          * Look up the address - if we don't find a lease
01138          * we don't need to do anything.
01139          */
01140         if (iasubopt_hash_lookup(&test_iasubopt, pool->leases,
01141                                  &lease->addr, sizeof(lease->addr),
01142                                  MDL) == 0) {
01143                 return (ISC_R_SUCCESS);
01144         }
01145 
01146         if (test_iasubopt->ia == NULL) {
01147                 /* no old ia, no work to do */
01148                 iasubopt_dereference(&test_iasubopt, MDL);
01149                 return (status);
01150         }
01151 
01152         ia_reference(&old_ia, test_iasubopt->ia, MDL);
01153 
01154         if ((old_ia->iaid_duid.len == ia->iaid_duid.len) &&
01155             (memcmp((unsigned char *)ia->iaid_duid.data,
01156                     (unsigned char *)old_ia->iaid_duid.data,
01157                     ia->iaid_duid.len) == 0)) {
01158                 /* same IA */
01159                 if ((lease->state == FTS_ACTIVE) ||
01160                     (lease->state == FTS_ABANDONED)) {
01161                         /* still active, no need to delete */
01162                         goto cleanup;
01163                 }
01164         } else {
01165                 /* different IA */
01166                 if ((lease->state != FTS_ACTIVE) &&
01167                     (lease->state != FTS_ABANDONED)) {
01168                         /* new lease isn't active, no work */
01169                         goto cleanup;
01170                 }
01171 
01172                 /*
01173                  * We appear to have two active leases, this shouldn't happen.
01174                  * Before a second lease can be set to active the first lease
01175                  * should be set to inactive (released, expired etc). For now
01176                  * delete the previous lease and indicate a failure to the
01177                  * caller so it can generate a warning.
01178                  * In the future we may try and determine which is the better
01179                  * lease to keep.
01180                  */
01181 
01182                 status = ISC_R_FAILURE;
01183         }
01184 
01185         /*
01186          * Remove the old lease from the active heap and from the hash table
01187          * then remove the lease from the IA and clean up the IA if necessary.
01188          */
01189         isc_heap_delete(pool->active_timeouts, test_iasubopt->heap_index);
01190         pool->num_active--;
01191 
01192         iasubopt_hash_delete(pool->leases, &test_iasubopt->addr,
01193                              sizeof(test_iasubopt->addr), MDL);
01194         ia_remove_iasubopt(old_ia, test_iasubopt, MDL);
01195         if (old_ia->num_iasubopt <= 0) {
01196                 ia_hash_delete(ia_table,
01197                                (unsigned char *)old_ia->iaid_duid.data,
01198                                old_ia->iaid_duid.len, MDL);
01199         }
01200 
01201         /*
01202          * We derefenrece the subopt here as we've just removed it from
01203          * the hash table in the pool.  We need to make a copy as we
01204          * need to derefernece it again later.
01205          */
01206         tmp_iasubopt = test_iasubopt;
01207         iasubopt_dereference(&tmp_iasubopt, MDL);
01208 
01209       cleanup:
01210         ia_dereference(&old_ia, MDL);
01211 
01212         /*
01213          * Clean up the reference, this is in addition to the deference
01214          * above after removing the entry from the hash table
01215          */
01216         iasubopt_dereference(&test_iasubopt, MDL);
01217 
01218         return (status);
01219 }
01220 
01221 /*
01222  * Put a lease in the pool directly. This is intended to be used when
01223  * loading leases from the file.
01224  */
01225 isc_result_t
01226 add_lease6(struct ipv6_pool *pool, struct iasubopt *lease,
01227            time_t valid_lifetime_end_time) {
01228         isc_result_t insert_result;
01229         struct iasubopt *test_iasubopt;
01230         struct iasubopt *tmp_iasubopt;
01231 
01232         /* If a state was not assigned by the caller, assume active. */
01233         if (lease->state == 0)
01234                 lease->state = FTS_ACTIVE;
01235 
01236         ipv6_pool_reference(&lease->ipv6_pool, pool, MDL);
01237 
01238         /*
01239          * If this IAADDR/PREFIX is already in our structures, remove the 
01240          * old one.
01241          */
01242         test_iasubopt = NULL;
01243         if (iasubopt_hash_lookup(&test_iasubopt, pool->leases,
01244                                  &lease->addr, sizeof(lease->addr), MDL)) {
01245                 /* XXX: we should probably ask the lease what heap it is on
01246                  * (as a consistency check).
01247                  * XXX: we should probably have one function to "put this lease
01248                  * on its heap" rather than doing these if's everywhere.  If
01249                  * you add more states to this list, don't.
01250                  */
01251                 if ((test_iasubopt->state == FTS_ACTIVE) ||
01252                     (test_iasubopt->state == FTS_ABANDONED)) {
01253                         isc_heap_delete(pool->active_timeouts,
01254                                         test_iasubopt->heap_index);
01255                         pool->num_active--;
01256                 } else {
01257                         isc_heap_delete(pool->inactive_timeouts,
01258                                         test_iasubopt->heap_index);
01259                         pool->num_inactive--;
01260                 }
01261 
01262                 iasubopt_hash_delete(pool->leases, &test_iasubopt->addr, 
01263                                      sizeof(test_iasubopt->addr), MDL);
01264 
01265                 /*
01266                  * We're going to do a bit of evil trickery here.
01267                  *
01268                  * We need to dereference the entry once to remove our
01269                  * current reference (in test_iasubopt), and then one
01270                  * more time to remove the reference left when the
01271                  * address was added to the pool before.
01272                  */
01273                 tmp_iasubopt = test_iasubopt;
01274                 iasubopt_dereference(&test_iasubopt, MDL);
01275                 iasubopt_dereference(&tmp_iasubopt, MDL);
01276         }
01277 
01278         /* 
01279          * Add IAADDR/PREFIX to our structures.
01280          */
01281         tmp_iasubopt = NULL;
01282         iasubopt_reference(&tmp_iasubopt, lease, MDL);
01283         if ((tmp_iasubopt->state == FTS_ACTIVE) ||
01284             (tmp_iasubopt->state == FTS_ABANDONED)) {
01285                 tmp_iasubopt->hard_lifetime_end_time = valid_lifetime_end_time;
01286                 iasubopt_hash_add(pool->leases, &tmp_iasubopt->addr, 
01287                                   sizeof(tmp_iasubopt->addr), lease, MDL);
01288                 insert_result = isc_heap_insert(pool->active_timeouts,
01289                                                 tmp_iasubopt);
01290                 if (insert_result == ISC_R_SUCCESS)
01291                         pool->num_active++;
01292         } else {
01293                 tmp_iasubopt->soft_lifetime_end_time = valid_lifetime_end_time;
01294                 insert_result = isc_heap_insert(pool->inactive_timeouts,
01295                                                 tmp_iasubopt);
01296                 if (insert_result == ISC_R_SUCCESS)
01297                         pool->num_inactive++;
01298         }
01299         if (insert_result != ISC_R_SUCCESS) {
01300                 iasubopt_hash_delete(pool->leases, &lease->addr, 
01301                                      sizeof(lease->addr), MDL);
01302                 iasubopt_dereference(&tmp_iasubopt, MDL);
01303                 return insert_result;
01304         }
01305 
01306         /* 
01307          * Note: we intentionally leave tmp_iasubopt referenced; there
01308          * is a reference in the heap/hash, after all.
01309          */
01310 
01311         return ISC_R_SUCCESS;
01312 }
01313 
01314 /*
01315  * Determine if an address is present in a pool or not.
01316  */
01317 isc_boolean_t
01318 lease6_exists(const struct ipv6_pool *pool, const struct in6_addr *addr) {
01319         struct iasubopt *test_iaaddr;
01320 
01321         test_iaaddr = NULL;
01322         if (iasubopt_hash_lookup(&test_iaaddr, pool->leases, 
01323                                  (void *)addr, sizeof(*addr), MDL)) {
01324                 iasubopt_dereference(&test_iaaddr, MDL);
01325                 return ISC_TRUE;
01326         } else {
01327                 return ISC_FALSE;
01328         }
01329 }
01330 
01345 isc_boolean_t
01346 lease6_usable(struct iasubopt *lease) {
01347         struct iasubopt *test_iaaddr;
01348         isc_boolean_t status = ISC_TRUE;
01349 
01350         test_iaaddr = NULL;
01351         if (iasubopt_hash_lookup(&test_iaaddr, lease->ipv6_pool->leases,
01352                                  (void *)&lease->addr,
01353                                  sizeof(lease->addr), MDL)) {
01354                 if (test_iaaddr != lease) {
01355                         status = ISC_FALSE;
01356                 }
01357                 iasubopt_dereference(&test_iaaddr, MDL);
01358         }
01359 
01360         return (status);
01361 }
01362 
01363 /*
01364  * Put the lease on our active pool.
01365  */
01366 static isc_result_t
01367 move_lease_to_active(struct ipv6_pool *pool, struct iasubopt *lease) {
01368         isc_result_t insert_result;
01369         int old_heap_index;
01370 
01371         old_heap_index = lease->heap_index;
01372         insert_result = isc_heap_insert(pool->active_timeouts, lease);
01373         if (insert_result == ISC_R_SUCCESS) {
01374                 iasubopt_hash_add(pool->leases, &lease->addr, 
01375                                   sizeof(lease->addr), lease, MDL);
01376                 isc_heap_delete(pool->inactive_timeouts, old_heap_index);
01377                 pool->num_active++;
01378                 pool->num_inactive--;
01379                 lease->state = FTS_ACTIVE;
01380         }
01381         return insert_result;
01382 }
01383 
01414 isc_result_t
01415 renew_lease6(struct ipv6_pool *pool, struct iasubopt *lease) {
01416         time_t old_end_time = lease->hard_lifetime_end_time;
01417         lease->hard_lifetime_end_time = lease->soft_lifetime_end_time;
01418         lease->soft_lifetime_end_time = 0;
01419 
01420         if (lease->state == FTS_ACTIVE) {
01421                 if (old_end_time <= lease->hard_lifetime_end_time) {
01422                         isc_heap_decreased(pool->active_timeouts,
01423                                            lease->heap_index);
01424                 } else {
01425                         isc_heap_increased(pool->active_timeouts,
01426                                            lease->heap_index);
01427                 }
01428                 return ISC_R_SUCCESS;
01429         } else if (lease->state == FTS_ABANDONED) {
01430                 char tmp_addr[INET6_ADDRSTRLEN];
01431                 lease->state = FTS_ACTIVE;
01432                 isc_heap_increased(pool->active_timeouts, lease->heap_index);
01433                 log_info("Reclaiming previously abandoned address %s",
01434                          inet_ntop(AF_INET6, &(lease->addr), tmp_addr,
01435                                    sizeof(tmp_addr)));
01436                 return ISC_R_SUCCESS;
01437         } else {
01438                 return move_lease_to_active(pool, lease);
01439         }
01440 }
01441 
01442 /*
01443  * Put the lease on our inactive pool, with the specified state.
01444  */
01445 static isc_result_t
01446 move_lease_to_inactive(struct ipv6_pool *pool, struct iasubopt *lease, 
01447                        binding_state_t state) {
01448         isc_result_t insert_result;
01449         int old_heap_index;
01450 
01451         old_heap_index = lease->heap_index;
01452         insert_result = isc_heap_insert(pool->inactive_timeouts, lease);
01453         if (insert_result == ISC_R_SUCCESS) {
01454                 /*
01455                  * Handle expire and release statements
01456                  * To get here we must be active and have done a commit so
01457                  * we should run the proper statements if they exist, though
01458                  * that will change when we remove the inactive heap.
01459                  * In addition we get rid of the references for both as we
01460                  * can only do one (expire or release) on a lease
01461                  */
01462                 if (lease->on_star.on_expiry != NULL) {
01463                         if (state == FTS_EXPIRED) {
01464                                 execute_statements(NULL, NULL, NULL,
01465                                                    NULL, NULL, NULL,
01466                                                    &lease->scope,
01467                                                    lease->on_star.on_expiry,
01468                                                    &lease->on_star);
01469                         }
01470                         executable_statement_dereference
01471                                 (&lease->on_star.on_expiry, MDL);
01472                 }
01473 
01474                 if (lease->on_star.on_release != NULL) {
01475                         if (state == FTS_RELEASED) {
01476                                 execute_statements(NULL, NULL, NULL,
01477                                                    NULL, NULL, NULL,
01478                                                    &lease->scope,
01479                                                    lease->on_star.on_release,
01480                                                    &lease->on_star);
01481                         }
01482                         executable_statement_dereference
01483                                 (&lease->on_star.on_release, MDL);
01484                 }
01485 
01486 #if defined (NSUPDATE)
01487                 /* Process events upon expiration. */
01488                 if (pool->pool_type != D6O_IA_PD) {
01489                         (void) ddns_removals(NULL, lease, NULL, ISC_FALSE);
01490                 }
01491 #endif
01492 
01493                 /* Binding scopes are no longer valid after expiry or
01494                  * release.
01495                  */
01496                 if (lease->scope != NULL) {
01497                         binding_scope_dereference(&lease->scope, MDL);
01498                 }
01499 
01500                 iasubopt_hash_delete(pool->leases, 
01501                                      &lease->addr, sizeof(lease->addr), MDL);
01502                 isc_heap_delete(pool->active_timeouts, old_heap_index);
01503                 lease->state = state;
01504                 pool->num_active--;
01505                 pool->num_inactive++;
01506         }
01507         return insert_result;
01508 }
01509 
01510 /*
01511  * Expire the oldest lease if it's lifetime_end_time is 
01512  * older than the given time.
01513  *
01514  * - leasep must be a pointer to a (struct iasubopt *) pointer previously
01515  *   initialized to NULL
01516  *
01517  * On return leasep has a reference to the removed entry. It is left
01518  * pointing to NULL if the oldest lease has not expired.
01519  */
01520 isc_result_t
01521 expire_lease6(struct iasubopt **leasep, struct ipv6_pool *pool, time_t now) {
01522         struct iasubopt *tmp;
01523         isc_result_t result;
01524 
01525         if (leasep == NULL) {
01526                 log_error("%s(%d): NULL pointer reference", MDL);
01527                 return DHCP_R_INVALIDARG;
01528         }
01529         if (*leasep != NULL) {
01530                 log_error("%s(%d): non-NULL pointer", MDL);
01531                 return DHCP_R_INVALIDARG;
01532         }
01533 
01534         if (pool->num_active > 0) {
01535                 tmp = (struct iasubopt *)
01536                                 isc_heap_element(pool->active_timeouts, 1);
01537                 if (now > tmp->hard_lifetime_end_time) {
01538                         result = move_lease_to_inactive(pool, tmp,
01539                                                         FTS_EXPIRED);
01540                         if (result == ISC_R_SUCCESS) {
01541                                 iasubopt_reference(leasep, tmp, MDL);
01542                         }
01543                         return result;
01544                 }
01545         }
01546         return ISC_R_SUCCESS;
01547 }
01548 
01549 
01550 /*
01551  * For a declined lease, leave it on the "active" pool, but mark
01552  * it as declined. Give it an infinite (well, really long) life.
01553  */
01554 isc_result_t
01555 decline_lease6(struct ipv6_pool *pool, struct iasubopt *lease) {
01556         isc_result_t result;
01557 
01558         if ((lease->state != FTS_ACTIVE) &&
01559             (lease->state != FTS_ABANDONED)) {
01560                 result = move_lease_to_active(pool, lease);
01561                 if (result != ISC_R_SUCCESS) {
01562                         return result;
01563                 }
01564         }
01565         lease->state = FTS_ABANDONED;
01566         lease->hard_lifetime_end_time = MAX_TIME;
01567         isc_heap_decreased(pool->active_timeouts, lease->heap_index);
01568         return ISC_R_SUCCESS;
01569 }
01570 
01571 /*
01572  * Put the returned lease on our inactive pool.
01573  */
01574 isc_result_t
01575 release_lease6(struct ipv6_pool *pool, struct iasubopt *lease) {
01576         if (lease->state == FTS_ACTIVE) {
01577                 return move_lease_to_inactive(pool, lease, FTS_RELEASED);
01578         } else {
01579                 return ISC_R_SUCCESS;
01580         }
01581 }
01582 
01583 /* 
01584  * Create a prefix by hashing the input, and using that for
01585  * the part subject to allocation.
01586  */
01587 void
01588 build_prefix6(struct in6_addr *pref, 
01589               const struct in6_addr *net_start_pref,
01590               int pool_bits, int pref_bits,
01591               const struct data_string *input) {
01592         isc_md5_t ctx;
01593         int net_bytes;
01594         int i;
01595         char *str;
01596         const char *net_str;
01597 
01598         /* 
01599          * Use MD5 to get a nice 128 bit hash of the input.
01600          * Yes, we know MD5 isn't cryptographically sound. 
01601          * No, we don't care.
01602          */
01603         isc_md5_init(&ctx);
01604         isc_md5_update(&ctx, input->data, input->len);
01605         isc_md5_final(&ctx, (unsigned char *)pref);
01606 
01607         /*
01608          * Copy the network bits over.
01609          */
01610         str = (char *)pref;
01611         net_str = (const char *)net_start_pref;
01612         net_bytes = pool_bits / 8;
01613         for (i = 0; i < net_bytes; i++) {
01614                 str[i] = net_str[i];
01615         }
01616         i = net_bytes;
01617         switch (pool_bits % 8) {
01618                 case 1: str[i] = (str[i] & 0x7F) | (net_str[i] & 0x80); break;
01619                 case 2: str[i] = (str[i] & 0x3F) | (net_str[i] & 0xC0); break;
01620                 case 3: str[i] = (str[i] & 0x1F) | (net_str[i] & 0xE0); break;
01621                 case 4: str[i] = (str[i] & 0x0F) | (net_str[i] & 0xF0); break;
01622                 case 5: str[i] = (str[i] & 0x07) | (net_str[i] & 0xF8); break;
01623                 case 6: str[i] = (str[i] & 0x03) | (net_str[i] & 0xFC); break;
01624                 case 7: str[i] = (str[i] & 0x01) | (net_str[i] & 0xFE); break;
01625         }
01626         /*
01627          * Zero the remaining bits.
01628          */
01629         net_bytes = pref_bits / 8;
01630         for (i=net_bytes+1; i<16; i++) {
01631                 str[i] = 0;
01632         }
01633         i = net_bytes;
01634         switch (pref_bits % 8) {
01635                 case 0: str[i] &= 0; break;
01636                 case 1: str[i] &= 0x80; break;
01637                 case 2: str[i] &= 0xC0; break;
01638                 case 3: str[i] &= 0xE0; break;
01639                 case 4: str[i] &= 0xF0; break;
01640                 case 5: str[i] &= 0xF8; break;
01641                 case 6: str[i] &= 0xFC; break;
01642                 case 7: str[i] &= 0xFE; break;
01643         }
01644 }
01645 
01646 /*
01647  * Create a lease for the given prefix and client duid.
01648  *
01649  * - pool must be a pointer to a (struct ipv6_pool *) pointer previously
01650  *   initialized to NULL
01651  *
01652  * Right now we simply hash the DUID, and if we get a collision, we hash 
01653  * again until we find a free prefix. We try this a fixed number of times,
01654  * to avoid getting stuck in a loop (this is important on small pools
01655  * where we can run out of space).
01656  *
01657  * We return the number of attempts that it took to find an available
01658  * prefix. This tells callers when a pool is are filling up, as
01659  * well as an indication of how full the pool is; statistically the 
01660  * more full a pool is the more attempts must be made before finding
01661  * a free prefix. Realistically this will only happen in very full
01662  * pools.
01663  *
01664  * We probably want different algorithms depending on the network size, in
01665  * the long term.
01666  */
01667 isc_result_t
01668 create_prefix6(struct ipv6_pool *pool, struct iasubopt **pref, 
01669                unsigned int *attempts,
01670                const struct data_string *uid,
01671                time_t soft_lifetime_end_time) {
01672         struct data_string ds;
01673         struct in6_addr tmp;
01674         struct iasubopt *test_iapref;
01675         struct data_string new_ds;
01676         struct iasubopt *iapref;
01677         isc_result_t result;
01678 
01679         /* 
01680          * Use the UID as our initial seed for the hash
01681          */
01682         memset(&ds, 0, sizeof(ds));
01683         data_string_copy(&ds, (struct data_string *)uid, MDL);
01684 
01685         *attempts = 0;
01686         for (;;) {
01687                 /*
01688                  * Give up at some point.
01689                  */
01690                 if (++(*attempts) > 10) {
01691                         data_string_forget(&ds, MDL);
01692                         return ISC_R_NORESOURCES;
01693                 }
01694 
01695                 /* 
01696                  * Build a prefix
01697                  */
01698                 build_prefix6(&tmp, &pool->start_addr,
01699                               pool->bits, pool->units, &ds);
01700 
01701                 /*
01702                  * If this prefix is not in use, we're happy with it
01703                  */
01704                 test_iapref = NULL;
01705                 if (iasubopt_hash_lookup(&test_iapref, pool->leases,
01706                                          &tmp, sizeof(tmp), MDL) == 0) {
01707                         break;
01708                 }
01709                 iasubopt_dereference(&test_iapref, MDL);
01710 
01711                 /* 
01712                  * Otherwise, we create a new input, adding the prefix
01713                  */
01714                 memset(&new_ds, 0, sizeof(new_ds));
01715                 new_ds.len = ds.len + sizeof(tmp);
01716                 if (!buffer_allocate(&new_ds.buffer, new_ds.len, MDL)) {
01717                         data_string_forget(&ds, MDL);
01718                         return ISC_R_NOMEMORY;
01719                 }
01720                 new_ds.data = new_ds.buffer->data;
01721                 memcpy(new_ds.buffer->data, ds.data, ds.len);
01722                 memcpy(new_ds.buffer->data + ds.len, &tmp, sizeof(tmp));
01723                 data_string_forget(&ds, MDL);
01724                 data_string_copy(&ds, &new_ds, MDL);
01725                 data_string_forget(&new_ds, MDL);
01726         }
01727 
01728         data_string_forget(&ds, MDL);
01729 
01730         /* 
01731          * We're happy with the prefix, create an IAPREFIX
01732          * to hold it.
01733          */
01734         iapref = NULL;
01735         result = iasubopt_allocate(&iapref, MDL);
01736         if (result != ISC_R_SUCCESS) {
01737                 return result;
01738         }
01739         iapref->plen = (u_int8_t)pool->units;
01740         memcpy(&iapref->addr, &tmp, sizeof(iapref->addr));
01741 
01742         /*
01743          * Add the prefix to the pool (note state is free, not active?!).
01744          */
01745         result = add_lease6(pool, iapref, soft_lifetime_end_time);
01746         if (result == ISC_R_SUCCESS) {
01747                 iasubopt_reference(pref, iapref, MDL);
01748         }
01749         iasubopt_dereference(&iapref, MDL);
01750         return result;
01751 }
01752 
01753 /*
01754  * Determine if a prefix is present in a pool or not.
01755  */
01756 isc_boolean_t
01757 prefix6_exists(const struct ipv6_pool *pool,
01758                const struct in6_addr *pref, u_int8_t plen) {
01759         struct iasubopt *test_iapref;
01760 
01761         if ((int)plen != pool->units)
01762                 return ISC_FALSE;
01763 
01764         test_iapref = NULL;
01765         if (iasubopt_hash_lookup(&test_iapref, pool->leases, 
01766                                  (void *)pref, sizeof(*pref), MDL)) {
01767                 iasubopt_dereference(&test_iapref, MDL);
01768                 return ISC_TRUE;
01769         } else {
01770                 return ISC_FALSE;
01771         }
01772 }
01773 
01774 /*
01775  * Mark an IPv6 address/prefix as unavailable from a pool.
01776  *
01777  * This is used for host entries and the addresses of the server itself.
01778  */
01779 isc_result_t
01780 mark_lease_unavailable(struct ipv6_pool *pool, const struct in6_addr *addr) {
01781         struct iasubopt *dummy_iasubopt;
01782         isc_result_t result;
01783 
01784         dummy_iasubopt = NULL;
01785         result = iasubopt_allocate(&dummy_iasubopt, MDL);
01786         if (result == ISC_R_SUCCESS) {
01787                 dummy_iasubopt->addr = *addr;
01788                 iasubopt_hash_add(pool->leases, &dummy_iasubopt->addr,
01789                                   sizeof(*addr), dummy_iasubopt, MDL);
01790         }
01791         return result;
01792 }
01793 
01794 /* 
01795  * Add a pool.
01796  */
01797 isc_result_t
01798 add_ipv6_pool(struct ipv6_pool *pool) {
01799         struct ipv6_pool **new_pools;
01800 
01801         new_pools = dmalloc(sizeof(struct ipv6_pool *) * (num_pools+1), MDL);
01802         if (new_pools == NULL) {
01803                 return ISC_R_NOMEMORY;
01804         }
01805 
01806         if (num_pools > 0) {
01807                 memcpy(new_pools, pools, 
01808                        sizeof(struct ipv6_pool *) * num_pools);
01809                 dfree(pools, MDL);
01810         }
01811         pools = new_pools;
01812 
01813         pools[num_pools] = NULL;
01814         ipv6_pool_reference(&pools[num_pools], pool, MDL);
01815         num_pools++;
01816         return ISC_R_SUCCESS;
01817 }
01818 
01819 static void
01820 cleanup_old_expired(struct ipv6_pool *pool) {
01821         struct iasubopt *tmp;
01822         struct ia_xx *ia;
01823         struct ia_xx *ia_active;
01824         unsigned char *tmpd;
01825         time_t timeout;
01826         
01827         while (pool->num_inactive > 0) {
01828                 tmp = (struct iasubopt *)
01829                                 isc_heap_element(pool->inactive_timeouts, 1);
01830                 if (tmp->hard_lifetime_end_time != 0) {
01831                         timeout = tmp->hard_lifetime_end_time;
01832                         timeout += EXPIRED_IPV6_CLEANUP_TIME;
01833                 } else {
01834                         timeout = tmp->soft_lifetime_end_time;
01835                 }
01836                 if (cur_time < timeout) {
01837                         break;
01838                 }
01839 
01840                 isc_heap_delete(pool->inactive_timeouts, tmp->heap_index);
01841                 pool->num_inactive--;
01842 
01843                 if (tmp->ia != NULL) {
01844                         /*
01845                          * Check to see if this IA is in an active list,
01846                          * but has no remaining resources. If so, remove it
01847                          * from the active list.
01848                          */
01849                         ia = NULL;
01850                         ia_reference(&ia, tmp->ia, MDL);
01851                         ia_remove_iasubopt(ia, tmp, MDL);
01852                         ia_active = NULL;
01853                         tmpd = (unsigned char *)ia->iaid_duid.data;
01854                         if ((ia->ia_type == D6O_IA_NA) &&
01855                             (ia->num_iasubopt <= 0) &&
01856                             (ia_hash_lookup(&ia_active, ia_na_active, tmpd,
01857                                             ia->iaid_duid.len, MDL) == 0) &&
01858                             (ia_active == ia)) {
01859                                 ia_hash_delete(ia_na_active, tmpd, 
01860                                                ia->iaid_duid.len, MDL);
01861                         }
01862                         if ((ia->ia_type == D6O_IA_TA) &&
01863                             (ia->num_iasubopt <= 0) &&
01864                             (ia_hash_lookup(&ia_active, ia_ta_active, tmpd,
01865                                             ia->iaid_duid.len, MDL) == 0) &&
01866                             (ia_active == ia)) {
01867                                 ia_hash_delete(ia_ta_active, tmpd, 
01868                                                ia->iaid_duid.len, MDL);
01869                         }
01870                         if ((ia->ia_type == D6O_IA_PD) &&
01871                             (ia->num_iasubopt <= 0) &&
01872                             (ia_hash_lookup(&ia_active, ia_pd_active, tmpd,
01873                                             ia->iaid_duid.len, MDL) == 0) &&
01874                             (ia_active == ia)) {
01875                                 ia_hash_delete(ia_pd_active, tmpd, 
01876                                                ia->iaid_duid.len, MDL);
01877                         }
01878                         ia_dereference(&ia, MDL);
01879                 }
01880                 iasubopt_dereference(&tmp, MDL);
01881         }
01882 }
01883 
01884 static void
01885 lease_timeout_support(void *vpool) {
01886         struct ipv6_pool *pool;
01887         struct iasubopt *lease;
01888         
01889         pool = (struct ipv6_pool *)vpool;
01890         for (;;) {
01891                 /*
01892                  * Get the next lease scheduled to expire.
01893                  *
01894                  * Note that if there are no leases in the pool, 
01895                  * expire_lease6() will return ISC_R_SUCCESS with 
01896                  * a NULL lease.
01897                  *
01898                  * expire_lease6() will call move_lease_to_inactive() which
01899                  * calls ddns_removals() do we want that on the standard
01900                  * expiration timer or a special 'depref' timer?  Original
01901                  * query from DH, moved here by SAR.
01902                  */
01903                 lease = NULL;
01904                 if (expire_lease6(&lease, pool, cur_time) != ISC_R_SUCCESS) {
01905                         break;
01906                 }
01907                 if (lease == NULL) {
01908                         break;
01909                 }
01910 
01911                 write_ia(lease->ia);
01912 
01913                 iasubopt_dereference(&lease, MDL);
01914         }
01915 
01916         /*
01917          * If appropriate commit and rotate the lease file
01918          * As commit_leases_timed() checks to see if we've done any writes
01919          * we don't bother tracking if this function called write _ia
01920          */
01921         (void) commit_leases_timed();
01922 
01923         /*
01924          * Do some cleanup of our expired leases.
01925          */
01926         cleanup_old_expired(pool);
01927 
01928         /*
01929          * Schedule next round of expirations.
01930          */
01931         schedule_lease_timeout(pool);
01932 }
01933 
01934 /*
01935  * For a given pool, add a timer that will remove the next
01936  * lease to expire.
01937  */
01938 void 
01939 schedule_lease_timeout(struct ipv6_pool *pool) {
01940         struct iasubopt *tmp;
01941         time_t timeout;
01942         time_t next_timeout;
01943         struct timeval tv;
01944 
01945         next_timeout = MAX_TIME;
01946 
01947         if (pool->num_active > 0) {
01948                 tmp = (struct iasubopt *)
01949                                 isc_heap_element(pool->active_timeouts, 1);
01950                 if (tmp->hard_lifetime_end_time < next_timeout) {
01951                         next_timeout = tmp->hard_lifetime_end_time + 1;
01952                 }
01953         }
01954 
01955         if (pool->num_inactive > 0) {
01956                 tmp = (struct iasubopt *)
01957                                 isc_heap_element(pool->inactive_timeouts, 1);
01958                 if (tmp->hard_lifetime_end_time != 0) {
01959                         timeout = tmp->hard_lifetime_end_time;
01960                         timeout += EXPIRED_IPV6_CLEANUP_TIME;
01961                 } else {
01962                         timeout = tmp->soft_lifetime_end_time + 1;
01963                 }
01964                 if (timeout < next_timeout) {
01965                         next_timeout = timeout;
01966                 }
01967         }
01968 
01969         if (next_timeout < MAX_TIME) {
01970                 tv.tv_sec = next_timeout;
01971                 tv.tv_usec = 0;
01972                 add_timeout(&tv, lease_timeout_support, pool,
01973                             (tvref_t)ipv6_pool_reference, 
01974                             (tvunref_t)ipv6_pool_dereference);
01975         }
01976 }
01977 
01978 /*
01979  * Schedule timeouts across all pools.
01980  */
01981 void
01982 schedule_all_ipv6_lease_timeouts(void) {
01983         int i;
01984 
01985         for (i=0; i<num_pools; i++) {
01986                 schedule_lease_timeout(pools[i]);
01987         }
01988 }
01989 
01990 /* 
01991  * Given an address and the length of the network mask, return
01992  * only the network portion.
01993  *
01994  * Examples:
01995  *
01996  *   "fe80::216:6fff:fe49:7d9b", length 64 = "fe80::"
01997  *   "2001:888:1936:2:216:6fff:fe49:7d9b", length 48 = "2001:888:1936::"
01998  */
01999 static void
02000 ipv6_network_portion(struct in6_addr *result, 
02001                      const struct in6_addr *addr, int bits) {
02002         unsigned char *addrp;
02003         int mask_bits;
02004         int bytes;
02005         int extra_bits;
02006         int i;
02007 
02008         static const unsigned char bitmasks[] = {
02009                 0x00, 0xFE, 0xFC, 0xF8, 
02010                 0xF0, 0xE0, 0xC0, 0x80, 
02011         };
02012 
02013         /* 
02014          *  Sanity check our bits. ;)
02015          */
02016         if ((bits < 0) || (bits > 128)) {
02017                 log_fatal("ipv6_network_portion: bits %d not between 0 and 128",
02018                           bits);
02019         }
02020 
02021         /* 
02022          * Copy our address portion.
02023          */
02024         *result = *addr;
02025         addrp = ((unsigned char *)result) + 15;
02026 
02027         /* 
02028          * Zero out masked portion.
02029          */
02030         mask_bits = 128 - bits;
02031         bytes = mask_bits / 8;
02032         extra_bits = mask_bits % 8;
02033 
02034         for (i=0; i<bytes; i++) {
02035                 *addrp = 0;
02036                 addrp--;
02037         }
02038         if (extra_bits) {
02039                 *addrp &= bitmasks[extra_bits];
02040         }
02041 }
02042 
02043 /*
02044  * Determine if the given address/prefix is in the pool.
02045  */
02046 isc_boolean_t
02047 ipv6_in_pool(const struct in6_addr *addr, const struct ipv6_pool *pool) {
02048         struct in6_addr tmp;
02049 
02050         ipv6_network_portion(&tmp, addr, pool->bits);
02051         if (memcmp(&tmp, &pool->start_addr, sizeof(tmp)) == 0) {
02052                 return ISC_TRUE;
02053         } else {
02054                 return ISC_FALSE;
02055         }
02056 }
02057 
02058 /*
02059  * Find the pool that contains the given address.
02060  *
02061  * - pool must be a pointer to a (struct ipv6_pool *) pointer previously
02062  *   initialized to NULL
02063  */
02064 isc_result_t
02065 find_ipv6_pool(struct ipv6_pool **pool, u_int16_t type,
02066                const struct in6_addr *addr) {
02067         int i;
02068 
02069         if (pool == NULL) {
02070                 log_error("%s(%d): NULL pointer reference", MDL);
02071                 return DHCP_R_INVALIDARG;
02072         }
02073         if (*pool != NULL) {
02074                 log_error("%s(%d): non-NULL pointer", MDL);
02075                 return DHCP_R_INVALIDARG;
02076         }
02077 
02078         for (i=0; i<num_pools; i++) {
02079                 if (pools[i]->pool_type != type)
02080                         continue;
02081                 if (ipv6_in_pool(addr, pools[i])) { 
02082                         ipv6_pool_reference(pool, pools[i], MDL);
02083                         return ISC_R_SUCCESS;
02084                 }
02085         }
02086         return ISC_R_NOTFOUND;
02087 }
02088 
02089 /*
02090  * Helper function for the various functions that act across all
02091  * pools.
02092  */
02093 static isc_result_t 
02094 change_leases(struct ia_xx *ia, 
02095               isc_result_t (*change_func)(struct ipv6_pool *,
02096                                           struct iasubopt *)) {
02097         isc_result_t retval;
02098         isc_result_t renew_retval;
02099         struct ipv6_pool *pool;
02100         struct in6_addr *addr;
02101         int i;
02102 
02103         retval = ISC_R_SUCCESS;
02104         for (i=0; i<ia->num_iasubopt; i++) {
02105                 pool = NULL;
02106                 addr = &ia->iasubopt[i]->addr;
02107                 if (find_ipv6_pool(&pool, ia->ia_type,
02108                                    addr) == ISC_R_SUCCESS) {
02109                         renew_retval = change_func(pool, ia->iasubopt[i]);
02110                         if (renew_retval != ISC_R_SUCCESS) {
02111                                 retval = renew_retval;
02112                         }
02113                 }
02114                 /* XXXsk: should we warn if we don't find a pool? */
02115         }
02116         return retval;
02117 }
02118 
02119 /*
02120  * Renew all leases in an IA from all pools.
02121  *
02122  * The new lifetime should be in the soft_lifetime_end_time
02123  * and will be moved to hard_lifetime_end_time by renew_lease6.
02124  */
02125 isc_result_t 
02126 renew_leases(struct ia_xx *ia) {
02127         return change_leases(ia, renew_lease6);
02128 }
02129 
02130 /*
02131  * Release all leases in an IA from all pools.
02132  */
02133 isc_result_t 
02134 release_leases(struct ia_xx *ia) {
02135         return change_leases(ia, release_lease6);
02136 }
02137 
02138 /*
02139  * Decline all leases in an IA from all pools.
02140  */
02141 isc_result_t 
02142 decline_leases(struct ia_xx *ia) {
02143         return change_leases(ia, decline_lease6);
02144 }
02145 
02146 #ifdef DHCPv6
02147 /*
02148  * Helper function to output leases.
02149  */
02150 static int write_error;
02151 
02152 static isc_result_t 
02153 write_ia_leases(const void *name, unsigned len, void *value) {
02154         struct ia_xx *ia = (struct ia_xx *)value;
02155         
02156         if (!write_error) { 
02157                 if (!write_ia(ia)) {
02158                         write_error = 1;
02159                 }
02160         }
02161         return ISC_R_SUCCESS;
02162 }
02163 
02164 /*
02165  * Write all DHCPv6 information.
02166  */
02167 int
02168 write_leases6(void) {
02169         int nas, tas, pds;
02170 
02171         write_error = 0;
02172         write_server_duid();
02173         nas = ia_hash_foreach(ia_na_active, write_ia_leases);
02174         if (write_error) {
02175                 return 0;
02176         }
02177         tas = ia_hash_foreach(ia_ta_active, write_ia_leases);
02178         if (write_error) {
02179                 return 0;
02180         }
02181         pds = ia_hash_foreach(ia_pd_active, write_ia_leases);
02182         if (write_error) {
02183                 return 0;
02184         }
02185 
02186         log_info("Wrote %d NA, %d TA, %d PD leases to lease file.",
02187                  nas, tas, pds);
02188         return 1;
02189 }
02190 #endif /* DHCPv6 */
02191 
02192 static isc_result_t
02193 mark_hosts_unavailable_support(const void *name, unsigned len, void *value) {
02194         struct host_decl *h;
02195         struct data_string fixed_addr;
02196         struct in6_addr addr;
02197         struct ipv6_pool *p;
02198 
02199         h = (struct host_decl *)value;
02200 
02201         /*
02202          * If the host has no address, we don't need to mark anything.
02203          */
02204         if (h->fixed_addr == NULL) {
02205                 return ISC_R_SUCCESS;
02206         }
02207 
02208         /* 
02209          * Evaluate the fixed address.
02210          */
02211         memset(&fixed_addr, 0, sizeof(fixed_addr));
02212         if (!evaluate_option_cache(&fixed_addr, NULL, NULL, NULL, NULL, NULL,
02213                                    &global_scope, h->fixed_addr, MDL)) {
02214                 log_error("mark_hosts_unavailable: "
02215                           "error evaluating host address.");
02216                 return ISC_R_SUCCESS;
02217         }
02218         if (fixed_addr.len != 16) {
02219                 log_error("mark_hosts_unavailable: "
02220                           "host address is not 128 bits.");
02221                 return ISC_R_SUCCESS;
02222         }
02223         memcpy(&addr, fixed_addr.data, 16);
02224         data_string_forget(&fixed_addr, MDL);
02225 
02226         /*
02227          * Find the pool holding this host, and mark the address.
02228          * (I suppose it is arguably valid to have a host that does not
02229          * sit in any pool.)
02230          */
02231         p = NULL;
02232         if (find_ipv6_pool(&p, D6O_IA_NA, &addr) == ISC_R_SUCCESS) {
02233                 mark_lease_unavailable(p, &addr);
02234                 ipv6_pool_dereference(&p, MDL);
02235         } 
02236         if (find_ipv6_pool(&p, D6O_IA_TA, &addr) == ISC_R_SUCCESS) {
02237                 mark_lease_unavailable(p, &addr);
02238                 ipv6_pool_dereference(&p, MDL);
02239         } 
02240 
02241         return ISC_R_SUCCESS;
02242 }
02243 
02244 void
02245 mark_hosts_unavailable(void) {
02246         hash_foreach(host_name_hash, mark_hosts_unavailable_support);
02247 }
02248 
02249 static isc_result_t
02250 mark_phosts_unavailable_support(const void *name, unsigned len, void *value) {
02251         struct host_decl *h;
02252         struct iaddrcidrnetlist *l;
02253         struct in6_addr pref;
02254         struct ipv6_pool *p;
02255 
02256         h = (struct host_decl *)value;
02257 
02258         /*
02259          * If the host has no prefix, we don't need to mark anything.
02260          */
02261         if (h->fixed_prefix == NULL) {
02262                 return ISC_R_SUCCESS;
02263         }
02264 
02265         /* 
02266          * Get the fixed prefixes.
02267          */
02268         for (l = h->fixed_prefix; l != NULL; l = l->next) {
02269                 if (l->cidrnet.lo_addr.len != 16) {
02270                         continue;
02271                 }
02272                 memcpy(&pref, l->cidrnet.lo_addr.iabuf, 16);
02273 
02274                 /*
02275                  * Find the pool holding this host, and mark the prefix.
02276                  * (I suppose it is arguably valid to have a host that does not
02277                  * sit in any pool.)
02278                  */
02279                 p = NULL;
02280                 if (find_ipv6_pool(&p, D6O_IA_PD, &pref) != ISC_R_SUCCESS) {
02281                         continue;
02282                 }
02283                 if (l->cidrnet.bits != p->units) {
02284                         ipv6_pool_dereference(&p, MDL);
02285                         continue;
02286                 }
02287                 mark_lease_unavailable(p, &pref);
02288                 ipv6_pool_dereference(&p, MDL);
02289         } 
02290 
02291         return ISC_R_SUCCESS;
02292 }
02293 
02294 void
02295 mark_phosts_unavailable(void) {
02296         hash_foreach(host_name_hash, mark_phosts_unavailable_support);
02297 }
02298 
02299 void 
02300 mark_interfaces_unavailable(void) {
02301         struct interface_info *ip;
02302         int i;
02303         struct ipv6_pool *p;
02304 
02305         ip = interfaces;
02306         while (ip != NULL) {
02307                 for (i=0; i<ip->v6address_count; i++) {
02308                         p = NULL;
02309                         if (find_ipv6_pool(&p, D6O_IA_NA, &ip->v6addresses[i]) 
02310                                                         == ISC_R_SUCCESS) {
02311                                 mark_lease_unavailable(p, 
02312                                                        &ip->v6addresses[i]);
02313                                 ipv6_pool_dereference(&p, MDL);
02314                         } 
02315                         if (find_ipv6_pool(&p, D6O_IA_TA, &ip->v6addresses[i]) 
02316                                                         == ISC_R_SUCCESS) {
02317                                 mark_lease_unavailable(p, 
02318                                                        &ip->v6addresses[i]);
02319                                 ipv6_pool_dereference(&p, MDL);
02320                         } 
02321                 }
02322                 ip = ip->next;
02323         }
02324 }
02325 
02343 isc_result_t
02344 ipv6_pond_allocate(struct ipv6_pond **pond, const char *file, int line) {
02345         struct ipv6_pond *tmp;
02346 
02347         if (pond == NULL) {
02348                 log_error("%s(%d): NULL pointer reference", file, line);
02349                 return DHCP_R_INVALIDARG;
02350         }
02351         if (*pond != NULL) {
02352                 log_error("%s(%d): non-NULL pointer", file, line);
02353                 return DHCP_R_INVALIDARG;
02354         }
02355 
02356         tmp = dmalloc(sizeof(*tmp), file, line);
02357         if (tmp == NULL) {
02358                 return ISC_R_NOMEMORY;
02359         }
02360 
02361         tmp->refcnt = 1;
02362 
02363         *pond = tmp;
02364         return ISC_R_SUCCESS;
02365 }
02366 
02386 isc_result_t
02387 ipv6_pond_reference(struct ipv6_pond **pond, struct ipv6_pond *src,
02388                     const char *file, int line) {
02389         if (pond == NULL) {
02390                 log_error("%s(%d): NULL pointer reference", file, line);
02391                 return DHCP_R_INVALIDARG;
02392         }
02393         if (*pond != NULL) {
02394                 log_error("%s(%d): non-NULL pointer", file, line);
02395                 return DHCP_R_INVALIDARG;
02396         }
02397         if (src == NULL) {
02398                 log_error("%s(%d): NULL pointer reference", file, line);
02399                 return DHCP_R_INVALIDARG;
02400         }
02401         *pond = src;
02402         src->refcnt++;
02403         return ISC_R_SUCCESS;
02404 }
02405 
02426 isc_result_t
02427 ipv6_pond_dereference(struct ipv6_pond **pond, const char *file, int line) {
02428         struct ipv6_pond *tmp;
02429 
02430         if ((pond == NULL) || (*pond == NULL)) {
02431                 log_error("%s(%d): NULL pointer", file, line);
02432                 return DHCP_R_INVALIDARG;
02433         }
02434 
02435         tmp = *pond;
02436         *pond = NULL;
02437 
02438         tmp->refcnt--;
02439         if (tmp->refcnt < 0) {
02440                 log_error("%s(%d): negative refcnt", file, line);
02441                 tmp->refcnt = 0;
02442         }
02443         if (tmp->refcnt == 0) {
02444                 dfree(tmp, file, line);
02445         }
02446 
02447         return ISC_R_SUCCESS;
02448 }
02449 
02450 /* unittest moved to server/tests/mdb6_unittest.c */

Generated on 5 Apr 2014 for ISC DHCP by  doxygen 1.6.1