Annotation of embedaddon/dhcp/client/dhc6.c, revision 1.1
1.1 ! misho 1: /* dhc6.c - DHCPv6 client routines. */
! 2:
! 3: /*
! 4: * Copyright (c) 2006-2010 by Internet Systems Consortium, Inc. ("ISC")
! 5: *
! 6: * Permission to use, copy, modify, and distribute this software for any
! 7: * purpose with or without fee is hereby granted, provided that the above
! 8: * copyright notice and this permission notice appear in all copies.
! 9: *
! 10: * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
! 11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
! 13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
! 16: * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 17: *
! 18: * Internet Systems Consortium, Inc.
! 19: * 950 Charter Street
! 20: * Redwood City, CA 94063
! 21: * <info@isc.org>
! 22: * https://www.isc.org/
! 23: */
! 24:
! 25: #include "dhcpd.h"
! 26:
! 27: #ifdef DHCPv6
! 28:
! 29: struct sockaddr_in6 DHCPv6DestAddr;
! 30:
! 31: /*
! 32: * Option definition structures that are used by the software - declared
! 33: * here once and assigned at startup to save lookups.
! 34: */
! 35: struct option *clientid_option = NULL;
! 36: struct option *elapsed_option = NULL;
! 37: struct option *ia_na_option = NULL;
! 38: struct option *ia_ta_option = NULL;
! 39: struct option *ia_pd_option = NULL;
! 40: struct option *iaaddr_option = NULL;
! 41: struct option *iaprefix_option = NULL;
! 42: struct option *oro_option = NULL;
! 43: struct option *irt_option = NULL;
! 44:
! 45: static struct dhc6_lease *dhc6_dup_lease(struct dhc6_lease *lease,
! 46: const char *file, int line);
! 47: static struct dhc6_ia *dhc6_dup_ia(struct dhc6_ia *ia,
! 48: const char *file, int line);
! 49: static struct dhc6_addr *dhc6_dup_addr(struct dhc6_addr *addr,
! 50: const char *file, int line);
! 51: static void dhc6_ia_destroy(struct dhc6_ia **src, const char *file, int line);
! 52: static isc_result_t dhc6_parse_ia_na(struct dhc6_ia **pia,
! 53: struct packet *packet,
! 54: struct option_state *options);
! 55: static isc_result_t dhc6_parse_ia_ta(struct dhc6_ia **pia,
! 56: struct packet *packet,
! 57: struct option_state *options);
! 58: static isc_result_t dhc6_parse_ia_pd(struct dhc6_ia **pia,
! 59: struct packet *packet,
! 60: struct option_state *options);
! 61: static isc_result_t dhc6_parse_addrs(struct dhc6_addr **paddr,
! 62: struct packet *packet,
! 63: struct option_state *options);
! 64: static isc_result_t dhc6_parse_prefixes(struct dhc6_addr **ppref,
! 65: struct packet *packet,
! 66: struct option_state *options);
! 67: static struct dhc6_ia *find_ia(struct dhc6_ia *head,
! 68: u_int16_t type, const char *id);
! 69: static struct dhc6_addr *find_addr(struct dhc6_addr *head,
! 70: struct iaddr *address);
! 71: static struct dhc6_addr *find_pref(struct dhc6_addr *head,
! 72: struct iaddr *prefix, u_int8_t plen);
! 73: void init_handler(struct packet *packet, struct client_state *client);
! 74: void info_request_handler(struct packet *packet, struct client_state *client);
! 75: void rapid_commit_handler(struct packet *packet, struct client_state *client);
! 76: void do_init6(void *input);
! 77: void do_info_request6(void *input);
! 78: void do_confirm6(void *input);
! 79: void reply_handler(struct packet *packet, struct client_state *client);
! 80: static isc_result_t dhc6_add_ia_na(struct client_state *client,
! 81: struct data_string *packet,
! 82: struct dhc6_lease *lease,
! 83: u_int8_t message);
! 84: static isc_result_t dhc6_add_ia_ta(struct client_state *client,
! 85: struct data_string *packet,
! 86: struct dhc6_lease *lease,
! 87: u_int8_t message);
! 88: static isc_result_t dhc6_add_ia_pd(struct client_state *client,
! 89: struct data_string *packet,
! 90: struct dhc6_lease *lease,
! 91: u_int8_t message);
! 92: static isc_boolean_t stopping_finished(void);
! 93: static void dhc6_merge_lease(struct dhc6_lease *src, struct dhc6_lease *dst);
! 94: void do_select6(void *input);
! 95: void do_refresh6(void *input);
! 96: static void do_release6(void *input);
! 97: static void start_bound(struct client_state *client);
! 98: static void start_informed(struct client_state *client);
! 99: void informed_handler(struct packet *packet, struct client_state *client);
! 100: void bound_handler(struct packet *packet, struct client_state *client);
! 101: void start_renew6(void *input);
! 102: void start_rebind6(void *input);
! 103: void do_depref(void *input);
! 104: void do_expire(void *input);
! 105: static void make_client6_options(struct client_state *client,
! 106: struct option_state **op,
! 107: struct dhc6_lease *lease, u_int8_t message);
! 108: static void script_write_params6(struct client_state *client,
! 109: const char *prefix,
! 110: struct option_state *options);
! 111: static isc_boolean_t active_prefix(struct client_state *client);
! 112:
! 113: static int check_timing6(struct client_state *client, u_int8_t msg_type,
! 114: char *msg_str, struct dhc6_lease *lease,
! 115: struct data_string *ds);
! 116:
! 117: extern int onetry;
! 118: extern int stateless;
! 119:
! 120: /*
! 121: * The "best" default DUID, since we cannot predict any information
! 122: * about the system (such as whether or not the hardware addresses are
! 123: * integrated into the motherboard or similar), is the "LLT", link local
! 124: * plus time, DUID. For real stateless "LL" is better.
! 125: *
! 126: * Once generated, this duid is stored into the state database, and
! 127: * retained across restarts.
! 128: *
! 129: * For the time being, there is probably a different state database for
! 130: * every daemon, so this winds up being a per-interface identifier...which
! 131: * is not how it is intended. Upcoming rearchitecting the client should
! 132: * address this "one daemon model."
! 133: */
! 134: void
! 135: form_duid(struct data_string *duid, const char *file, int line)
! 136: {
! 137: struct interface_info *ip;
! 138: int len;
! 139:
! 140: /* For now, just use the first interface on the list. */
! 141: ip = interfaces;
! 142:
! 143: if (ip == NULL)
! 144: log_fatal("Impossible condition at %s:%d.", MDL);
! 145:
! 146: if ((ip->hw_address.hlen == 0) ||
! 147: (ip->hw_address.hlen > sizeof(ip->hw_address.hbuf)))
! 148: log_fatal("Impossible hardware address length at %s:%d.", MDL);
! 149:
! 150: /*
! 151: * 2 bytes for the 'duid type' field.
! 152: * 2 bytes for the 'htype' field.
! 153: * (not stateless) 4 bytes for the 'current time'.
! 154: * enough bytes for the hardware address (note that hw_address has
! 155: * the 'htype' on byte zero).
! 156: */
! 157: len = 4 + (ip->hw_address.hlen - 1);
! 158: if (!stateless)
! 159: len += 4;
! 160: if (!buffer_allocate(&duid->buffer, len, MDL))
! 161: log_fatal("no memory for default DUID!");
! 162: duid->data = duid->buffer->data;
! 163: duid->len = len;
! 164:
! 165: /* Basic Link Local Address type of DUID. */
! 166: if (!stateless) {
! 167: putUShort(duid->buffer->data, DUID_LLT);
! 168: putUShort(duid->buffer->data + 2, ip->hw_address.hbuf[0]);
! 169: putULong(duid->buffer->data + 4, cur_time - DUID_TIME_EPOCH);
! 170: memcpy(duid->buffer->data + 8, ip->hw_address.hbuf + 1,
! 171: ip->hw_address.hlen - 1);
! 172: } else {
! 173: putUShort(duid->buffer->data, DUID_LL);
! 174: putUShort(duid->buffer->data + 2, ip->hw_address.hbuf[0]);
! 175: memcpy(duid->buffer->data + 4, ip->hw_address.hbuf + 1,
! 176: ip->hw_address.hlen - 1);
! 177: }
! 178: }
! 179:
! 180: /*
! 181: * Assign DHCPv6 port numbers as a client.
! 182: */
! 183: void
! 184: dhcpv6_client_assignments(void)
! 185: {
! 186: struct servent *ent;
! 187: unsigned code;
! 188:
! 189: if (path_dhclient_pid == NULL)
! 190: path_dhclient_pid = _PATH_DHCLIENT6_PID;
! 191: if (path_dhclient_db == NULL)
! 192: path_dhclient_db = _PATH_DHCLIENT6_DB;
! 193:
! 194: if (local_port == 0) {
! 195: ent = getservbyname("dhcpv6-client", "udp");
! 196: if (ent == NULL)
! 197: local_port = htons(546);
! 198: else
! 199: local_port = ent->s_port;
! 200: }
! 201:
! 202: if (remote_port == 0) {
! 203: ent = getservbyname("dhcpv6-server", "udp");
! 204: if (ent == NULL)
! 205: remote_port = htons(547);
! 206: else
! 207: remote_port = ent->s_port;
! 208: }
! 209:
! 210: memset(&DHCPv6DestAddr, 0, sizeof(DHCPv6DestAddr));
! 211: DHCPv6DestAddr.sin6_family = AF_INET6;
! 212: DHCPv6DestAddr.sin6_port = remote_port;
! 213: inet_pton(AF_INET6, All_DHCP_Relay_Agents_and_Servers,
! 214: &DHCPv6DestAddr.sin6_addr);
! 215:
! 216: code = D6O_CLIENTID;
! 217: if (!option_code_hash_lookup(&clientid_option,
! 218: dhcpv6_universe.code_hash, &code, 0, MDL))
! 219: log_fatal("Unable to find the CLIENTID option definition.");
! 220:
! 221: code = D6O_ELAPSED_TIME;
! 222: if (!option_code_hash_lookup(&elapsed_option,
! 223: dhcpv6_universe.code_hash, &code, 0, MDL))
! 224: log_fatal("Unable to find the ELAPSED_TIME option definition.");
! 225:
! 226: code = D6O_IA_NA;
! 227: if (!option_code_hash_lookup(&ia_na_option, dhcpv6_universe.code_hash,
! 228: &code, 0, MDL))
! 229: log_fatal("Unable to find the IA_NA option definition.");
! 230:
! 231: code = D6O_IA_TA;
! 232: if (!option_code_hash_lookup(&ia_ta_option, dhcpv6_universe.code_hash,
! 233: &code, 0, MDL))
! 234: log_fatal("Unable to find the IA_TA option definition.");
! 235:
! 236: code = D6O_IA_PD;
! 237: if (!option_code_hash_lookup(&ia_pd_option, dhcpv6_universe.code_hash,
! 238: &code, 0, MDL))
! 239: log_fatal("Unable to find the IA_PD option definition.");
! 240:
! 241: code = D6O_IAADDR;
! 242: if (!option_code_hash_lookup(&iaaddr_option, dhcpv6_universe.code_hash,
! 243: &code, 0, MDL))
! 244: log_fatal("Unable to find the IAADDR option definition.");
! 245:
! 246: code = D6O_IAPREFIX;
! 247: if (!option_code_hash_lookup(&iaprefix_option,
! 248: dhcpv6_universe.code_hash,
! 249: &code, 0, MDL))
! 250: log_fatal("Unable to find the IAPREFIX option definition.");
! 251:
! 252: code = D6O_ORO;
! 253: if (!option_code_hash_lookup(&oro_option, dhcpv6_universe.code_hash,
! 254: &code, 0, MDL))
! 255: log_fatal("Unable to find the ORO option definition.");
! 256:
! 257: code = D6O_INFORMATION_REFRESH_TIME;
! 258: if (!option_code_hash_lookup(&irt_option, dhcpv6_universe.code_hash,
! 259: &code, 0, MDL))
! 260: log_fatal("Unable to find the IRT option definition.");
! 261:
! 262: #ifndef __CYGWIN32__ /* XXX */
! 263: endservent();
! 264: #endif
! 265: }
! 266:
! 267: /*
! 268: * Instead of implementing RFC3315 RAND (section 14) as a float "between"
! 269: * -0.1 and 0.1 non-inclusive, we implement it as an integer.
! 270: *
! 271: * The result is expected to follow this table:
! 272: *
! 273: * split range answer
! 274: * - ERROR - base <= 0
! 275: * 0 1 0..0 1 <= base <= 10
! 276: * 1 3 -1..1 11 <= base <= 20
! 277: * 2 5 -2..2 21 <= base <= 30
! 278: * 3 7 -3..3 31 <= base <= 40
! 279: * ...
! 280: *
! 281: * XXX: For this to make sense, we really need to do timing on a
! 282: * XXX: usec scale...we currently can assume zero for any value less than
! 283: * XXX: 11, which are very common in early stages of transmission for most
! 284: * XXX: messages.
! 285: */
! 286: static TIME
! 287: dhc6_rand(TIME base)
! 288: {
! 289: TIME rval;
! 290: TIME range;
! 291: TIME split;
! 292:
! 293: /*
! 294: * A zero or less timeout is a bad thing...we don't want to
! 295: * DHCP-flood anyone.
! 296: */
! 297: if (base <= 0)
! 298: log_fatal("Impossible condition at %s:%d.", MDL);
! 299:
! 300: /*
! 301: * The first thing we do is count how many random integers we want
! 302: * in either direction (best thought of as the maximum negative
! 303: * integer, as we will subtract this potentially from a random 0).
! 304: */
! 305: split = (base - 1) / 10;
! 306:
! 307: /* Don't bother with the rest of the math if we know we'll get 0. */
! 308: if (split == 0)
! 309: return 0;
! 310:
! 311: /*
! 312: * Then we count the total number of integers in this set. This
! 313: * is twice the number of integers in positive and negative
! 314: * directions, plus zero (-1, 0, 1 is 3, -2..2 adds 2 to 5, so forth).
! 315: */
! 316: range = (split * 2) + 1;
! 317:
! 318: /* Take a random number from [0..(range-1)]. */
! 319: rval = random();
! 320: rval %= range;
! 321:
! 322: /* Offset it to uncover potential negative values. */
! 323: rval -= split;
! 324:
! 325: return rval;
! 326: }
! 327:
! 328: /* Initialize message exchange timers (set RT from Initial-RT). */
! 329: static void
! 330: dhc6_retrans_init(struct client_state *client)
! 331: {
! 332: int xid;
! 333:
! 334: /* Initialize timers. */
! 335: client->txcount = 0;
! 336: client->RT = client->IRT + dhc6_rand(client->IRT);
! 337:
! 338: /* Generate a new random 24-bit transaction ID for this exchange. */
! 339:
! 340: #if (RAND_MAX >= 0x00ffffff)
! 341: xid = random();
! 342: #elif (RAND_MAX >= 0x0000ffff)
! 343: xid = (random() << 16) ^ random();
! 344: #elif (RAND_MAX >= 0x000000ff)
! 345: xid = (random() << 16) ^ (random() << 8) ^ random();
! 346: #else
! 347: # error "Random number generator of less than 8 bits not supported."
! 348: #endif
! 349:
! 350: client->dhcpv6_transaction_id[0] = (xid >> 16) & 0xff;
! 351: client->dhcpv6_transaction_id[1] = (xid >> 8) & 0xff;
! 352: client->dhcpv6_transaction_id[2] = xid & 0xff;
! 353: }
! 354:
! 355: /* Advance the DHCPv6 retransmission state once. */
! 356: static void
! 357: dhc6_retrans_advance(struct client_state *client)
! 358: {
! 359: struct timeval elapsed;
! 360:
! 361: /* elapsed = cur - start */
! 362: elapsed.tv_sec = cur_tv.tv_sec - client->start_time.tv_sec;
! 363: elapsed.tv_usec = cur_tv.tv_usec - client->start_time.tv_usec;
! 364: if (elapsed.tv_usec < 0) {
! 365: elapsed.tv_sec -= 1;
! 366: elapsed.tv_usec += 1000000;
! 367: }
! 368: /* retrans_advance is called after consuming client->RT. */
! 369: /* elapsed += RT */
! 370: elapsed.tv_sec += client->RT / 100;
! 371: elapsed.tv_usec += (client->RT % 100) * 10000;
! 372: if (elapsed.tv_usec >= 1000000) {
! 373: elapsed.tv_sec += 1;
! 374: elapsed.tv_usec -= 1000000;
! 375: }
! 376:
! 377: /*
! 378: * RT for each subsequent message transmission is based on the previous
! 379: * value of RT:
! 380: *
! 381: * RT = 2*RTprev + RAND*RTprev
! 382: */
! 383: client->RT += client->RT + dhc6_rand(client->RT);
! 384:
! 385: /*
! 386: * MRT specifies an upper bound on the value of RT (disregarding the
! 387: * randomization added by the use of RAND). If MRT has a value of 0,
! 388: * there is no upper limit on the value of RT. Otherwise:
! 389: *
! 390: * if (RT > MRT)
! 391: * RT = MRT + RAND*MRT
! 392: */
! 393: if ((client->MRT != 0) && (client->RT > client->MRT))
! 394: client->RT = client->MRT + dhc6_rand(client->MRT);
! 395:
! 396: /*
! 397: * Further, if there's an MRD, we should wake up upon reaching
! 398: * the MRD rather than at some point after it.
! 399: */
! 400: if (client->MRD == 0) {
! 401: /* Done. */
! 402: client->txcount++;
! 403: return;
! 404: }
! 405: /* elapsed += client->RT */
! 406: elapsed.tv_sec += client->RT / 100;
! 407: elapsed.tv_usec += (client->RT % 100) * 10000;
! 408: if (elapsed.tv_usec >= 1000000) {
! 409: elapsed.tv_sec += 1;
! 410: elapsed.tv_usec -= 1000000;
! 411: }
! 412: if (elapsed.tv_sec >= client->MRD) {
! 413: /*
! 414: * wake at RT + cur = start + MRD
! 415: */
! 416: client->RT = client->MRD +
! 417: (client->start_time.tv_sec - cur_tv.tv_sec);
! 418: client->RT = client->RT * 100 +
! 419: (client->start_time.tv_usec - cur_tv.tv_usec) / 10000;
! 420: }
! 421: client->txcount++;
! 422: }
! 423:
! 424: /* Quick validation of DHCPv6 ADVERTISE packet contents. */
! 425: static int
! 426: valid_reply(struct packet *packet, struct client_state *client)
! 427: {
! 428: struct data_string sid, cid;
! 429: struct option_cache *oc;
! 430: int rval = ISC_TRUE;
! 431:
! 432: memset(&sid, 0, sizeof(sid));
! 433: memset(&cid, 0, sizeof(cid));
! 434:
! 435: if (!lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID)) {
! 436: log_error("Response without a server identifier received.");
! 437: rval = ISC_FALSE;
! 438: }
! 439:
! 440: oc = lookup_option(&dhcpv6_universe, packet->options, D6O_CLIENTID);
! 441: if (!oc ||
! 442: !evaluate_option_cache(&sid, packet, NULL, client, packet->options,
! 443: client->sent_options, &global_scope, oc,
! 444: MDL)) {
! 445: log_error("Response without a client identifier.");
! 446: rval = ISC_FALSE;
! 447: }
! 448:
! 449: oc = lookup_option(&dhcpv6_universe, client->sent_options,
! 450: D6O_CLIENTID);
! 451: if (!oc ||
! 452: !evaluate_option_cache(&cid, packet, NULL, client,
! 453: client->sent_options, NULL, &global_scope,
! 454: oc, MDL)) {
! 455: log_error("Local client identifier is missing!");
! 456: rval = ISC_FALSE;
! 457: }
! 458:
! 459: if (sid.len == 0 ||
! 460: sid.len != cid.len ||
! 461: memcmp(sid.data, cid.data, sid.len)) {
! 462: log_error("Advertise with matching transaction ID, but "
! 463: "mismatching client id.");
! 464: rval = ISC_FALSE;
! 465: }
! 466:
! 467: return rval;
! 468: }
! 469:
! 470: /*
! 471: * Create a complete copy of a DHCPv6 lease structure.
! 472: */
! 473: static struct dhc6_lease *
! 474: dhc6_dup_lease(struct dhc6_lease *lease, const char *file, int line)
! 475: {
! 476: struct dhc6_lease *copy;
! 477: struct dhc6_ia **insert_ia, *ia;
! 478:
! 479: copy = dmalloc(sizeof(*copy), file, line);
! 480: if (copy == NULL) {
! 481: log_error("Out of memory for v6 lease structure.");
! 482: return NULL;
! 483: }
! 484:
! 485: data_string_copy(©->server_id, &lease->server_id, file, line);
! 486: copy->pref = lease->pref;
! 487:
! 488: memcpy(copy->dhcpv6_transaction_id, lease->dhcpv6_transaction_id,
! 489: sizeof(copy->dhcpv6_transaction_id));
! 490:
! 491: option_state_reference(©->options, lease->options, file, line);
! 492:
! 493: insert_ia = ©->bindings;
! 494: for (ia = lease->bindings ; ia != NULL ; ia = ia->next) {
! 495: *insert_ia = dhc6_dup_ia(ia, file, line);
! 496:
! 497: if (*insert_ia == NULL) {
! 498: dhc6_lease_destroy(©, file, line);
! 499: return NULL;
! 500: }
! 501:
! 502: insert_ia = &(*insert_ia)->next;
! 503: }
! 504:
! 505: return copy;
! 506: }
! 507:
! 508: /*
! 509: * Duplicate an IA structure.
! 510: */
! 511: static struct dhc6_ia *
! 512: dhc6_dup_ia(struct dhc6_ia *ia, const char *file, int line)
! 513: {
! 514: struct dhc6_ia *copy;
! 515: struct dhc6_addr **insert_addr, *addr;
! 516:
! 517: copy = dmalloc(sizeof(*ia), file, line);
! 518:
! 519: memcpy(copy->iaid, ia->iaid, sizeof(copy->iaid));
! 520:
! 521: copy->ia_type = ia->ia_type;
! 522: copy->starts = ia->starts;
! 523: copy->renew = ia->renew;
! 524: copy->rebind = ia->rebind;
! 525:
! 526: insert_addr = ©->addrs;
! 527: for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
! 528: *insert_addr = dhc6_dup_addr(addr, file, line);
! 529:
! 530: if (*insert_addr == NULL) {
! 531: dhc6_ia_destroy(©, file, line);
! 532: return NULL;
! 533: }
! 534:
! 535: insert_addr = &(*insert_addr)->next;
! 536: }
! 537:
! 538: if (ia->options != NULL)
! 539: option_state_reference(©->options, ia->options,
! 540: file, line);
! 541:
! 542: return copy;
! 543: }
! 544:
! 545: /*
! 546: * Duplicate an IAADDR or IAPREFIX structure.
! 547: */
! 548: static struct dhc6_addr *
! 549: dhc6_dup_addr(struct dhc6_addr *addr, const char *file, int line)
! 550: {
! 551: struct dhc6_addr *copy;
! 552:
! 553: copy = dmalloc(sizeof(*addr), file, line);
! 554:
! 555: if (copy == NULL)
! 556: return NULL;
! 557:
! 558: memcpy(©->address, &addr->address, sizeof(copy->address));
! 559:
! 560: copy->plen = addr->plen;
! 561: copy->flags = addr->flags;
! 562: copy->starts = addr->starts;
! 563: copy->preferred_life = addr->preferred_life;
! 564: copy->max_life = addr->max_life;
! 565:
! 566: if (addr->options != NULL)
! 567: option_state_reference(©->options, addr->options,
! 568: file, line);
! 569:
! 570: return copy;
! 571: }
! 572:
! 573: /*
! 574: * Form a DHCPv6 lease structure based upon packet contents. Creates and
! 575: * populates IA's and any IAADDR/IAPREFIX's they contain.
! 576: * Parsed options are deleted in order to not save them in the lease file.
! 577: */
! 578: static struct dhc6_lease *
! 579: dhc6_leaseify(struct packet *packet)
! 580: {
! 581: struct data_string ds;
! 582: struct dhc6_lease *lease;
! 583: struct option_cache *oc;
! 584:
! 585: lease = dmalloc(sizeof(*lease), MDL);
! 586: if (lease == NULL) {
! 587: log_error("Out of memory for v6 lease structure.");
! 588: return NULL;
! 589: }
! 590:
! 591: memcpy(lease->dhcpv6_transaction_id, packet->dhcpv6_transaction_id, 3);
! 592: option_state_reference(&lease->options, packet->options, MDL);
! 593:
! 594: memset(&ds, 0, sizeof(ds));
! 595:
! 596: /* Determine preference (default zero). */
! 597: oc = lookup_option(&dhcpv6_universe, lease->options, D6O_PREFERENCE);
! 598: if (oc &&
! 599: evaluate_option_cache(&ds, packet, NULL, NULL, lease->options,
! 600: NULL, &global_scope, oc, MDL)) {
! 601: if (ds.len != 1) {
! 602: log_error("Invalid length of DHCPv6 Preference option "
! 603: "(%d != 1)", ds.len);
! 604: data_string_forget(&ds, MDL);
! 605: dhc6_lease_destroy(&lease, MDL);
! 606: return NULL;
! 607: } else {
! 608: lease->pref = ds.data[0];
! 609: log_debug("RCV: X-- Preference %u.",
! 610: (unsigned)lease->pref);
! 611: }
! 612:
! 613: data_string_forget(&ds, MDL);
! 614: }
! 615: delete_option(&dhcpv6_universe, lease->options, D6O_PREFERENCE);
! 616:
! 617: /*
! 618: * Dig into recursive DHCPv6 pockets for IA_NA and contained IAADDR
! 619: * options.
! 620: */
! 621: if (dhc6_parse_ia_na(&lease->bindings, packet,
! 622: lease->options) != ISC_R_SUCCESS) {
! 623: /* Error conditions are logged by the caller. */
! 624: dhc6_lease_destroy(&lease, MDL);
! 625: return NULL;
! 626: }
! 627: /*
! 628: * Dig into recursive DHCPv6 pockets for IA_TA and contained IAADDR
! 629: * options.
! 630: */
! 631: if (dhc6_parse_ia_ta(&lease->bindings, packet,
! 632: lease->options) != ISC_R_SUCCESS) {
! 633: /* Error conditions are logged by the caller. */
! 634: dhc6_lease_destroy(&lease, MDL);
! 635: return NULL;
! 636: }
! 637: /*
! 638: * Dig into recursive DHCPv6 pockets for IA_PD and contained IAPREFIX
! 639: * options.
! 640: */
! 641: if (dhc6_parse_ia_pd(&lease->bindings, packet,
! 642: lease->options) != ISC_R_SUCCESS) {
! 643: /* Error conditions are logged by the caller. */
! 644: dhc6_lease_destroy(&lease, MDL);
! 645: return NULL;
! 646: }
! 647:
! 648: /*
! 649: * This is last because in the future we may want to make a different
! 650: * key based upon additional information from the packet (we may need
! 651: * to allow multiple leases in one client state per server, but we're
! 652: * not sure based on what additional keys now).
! 653: */
! 654: oc = lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID);
! 655: if (!evaluate_option_cache(&lease->server_id, packet, NULL, NULL,
! 656: lease->options, NULL, &global_scope,
! 657: oc, MDL) ||
! 658: lease->server_id.len == 0) {
! 659: /* This should be impossible due to validation checks earlier.
! 660: */
! 661: log_error("Invalid SERVERID option cache.");
! 662: dhc6_lease_destroy(&lease, MDL);
! 663: return NULL;
! 664: } else {
! 665: log_debug("RCV: X-- Server ID: %s",
! 666: print_hex_1(lease->server_id.len,
! 667: lease->server_id.data, 52));
! 668: }
! 669:
! 670: return lease;
! 671: }
! 672:
! 673: static isc_result_t
! 674: dhc6_parse_ia_na(struct dhc6_ia **pia, struct packet *packet,
! 675: struct option_state *options)
! 676: {
! 677: struct data_string ds;
! 678: struct dhc6_ia *ia;
! 679: struct option_cache *oc;
! 680: isc_result_t result;
! 681:
! 682: memset(&ds, 0, sizeof(ds));
! 683:
! 684: oc = lookup_option(&dhcpv6_universe, options, D6O_IA_NA);
! 685: for ( ; oc != NULL ; oc = oc->next) {
! 686: ia = dmalloc(sizeof(*ia), MDL);
! 687: if (ia == NULL) {
! 688: log_error("Out of memory allocating IA_NA structure.");
! 689: return ISC_R_NOMEMORY;
! 690: } else if (evaluate_option_cache(&ds, packet, NULL, NULL,
! 691: options, NULL,
! 692: &global_scope, oc, MDL) &&
! 693: ds.len >= 12) {
! 694: memcpy(ia->iaid, ds.data, 4);
! 695: ia->ia_type = D6O_IA_NA;
! 696: ia->starts = cur_time;
! 697: ia->renew = getULong(ds.data + 4);
! 698: ia->rebind = getULong(ds.data + 8);
! 699:
! 700: log_debug("RCV: X-- IA_NA %s",
! 701: print_hex_1(4, ia->iaid, 59));
! 702: /* XXX: This should be the printed time I think. */
! 703: log_debug("RCV: | X-- starts %u",
! 704: (unsigned)ia->starts);
! 705: log_debug("RCV: | X-- t1 - renew +%u", ia->renew);
! 706: log_debug("RCV: | X-- t2 - rebind +%u", ia->rebind);
! 707:
! 708: /*
! 709: * RFC3315 section 22.4, discard IA_NA's that
! 710: * have t1 greater than t2, and both not zero.
! 711: * Since RFC3315 defines this behaviour, it is not
! 712: * an error - just normal operation.
! 713: *
! 714: * Note that RFC3315 says we MUST honor these values
! 715: * if they are not zero. So insane values are
! 716: * totally OK.
! 717: */
! 718: if ((ia->renew > 0) && (ia->rebind > 0) &&
! 719: (ia->renew > ia->rebind)) {
! 720: log_debug("RCV: | !-- INVALID renew/rebind "
! 721: "times, IA_NA discarded.");
! 722: dfree(ia, MDL);
! 723: data_string_forget(&ds, MDL);
! 724: continue;
! 725: }
! 726:
! 727: if (ds.len > 12) {
! 728: log_debug("RCV: | X-- [Options]");
! 729:
! 730: if (!option_state_allocate(&ia->options,
! 731: MDL)) {
! 732: log_error("Out of memory allocating "
! 733: "IA_NA option state.");
! 734: dfree(ia, MDL);
! 735: data_string_forget(&ds, MDL);
! 736: return ISC_R_NOMEMORY;
! 737: }
! 738:
! 739: if (!parse_option_buffer(ia->options,
! 740: ds.data + 12,
! 741: ds.len - 12,
! 742: &dhcpv6_universe)) {
! 743: log_error("Corrupt IA_NA options.");
! 744: option_state_dereference(&ia->options,
! 745: MDL);
! 746: dfree(ia, MDL);
! 747: data_string_forget(&ds, MDL);
! 748: return ISC_R_BADPARSE;
! 749: }
! 750: }
! 751: data_string_forget(&ds, MDL);
! 752:
! 753: if (ia->options != NULL) {
! 754: result = dhc6_parse_addrs(&ia->addrs, packet,
! 755: ia->options);
! 756: if (result != ISC_R_SUCCESS) {
! 757: option_state_dereference(&ia->options,
! 758: MDL);
! 759: dfree(ia, MDL);
! 760: return result;
! 761: }
! 762: }
! 763:
! 764: while (*pia != NULL)
! 765: pia = &(*pia)->next;
! 766: *pia = ia;
! 767: pia = &ia->next;
! 768: } else {
! 769: log_error("Invalid IA_NA option cache.");
! 770: dfree(ia, MDL);
! 771: if (ds.len != 0)
! 772: data_string_forget(&ds, MDL);
! 773: return ISC_R_UNEXPECTED;
! 774: }
! 775: }
! 776: delete_option(&dhcpv6_universe, options, D6O_IA_NA);
! 777:
! 778: return ISC_R_SUCCESS;
! 779: }
! 780:
! 781: static isc_result_t
! 782: dhc6_parse_ia_ta(struct dhc6_ia **pia, struct packet *packet,
! 783: struct option_state *options)
! 784: {
! 785: struct data_string ds;
! 786: struct dhc6_ia *ia;
! 787: struct option_cache *oc;
! 788: isc_result_t result;
! 789:
! 790: memset(&ds, 0, sizeof(ds));
! 791:
! 792: oc = lookup_option(&dhcpv6_universe, options, D6O_IA_TA);
! 793: for ( ; oc != NULL ; oc = oc->next) {
! 794: ia = dmalloc(sizeof(*ia), MDL);
! 795: if (ia == NULL) {
! 796: log_error("Out of memory allocating IA_TA structure.");
! 797: return ISC_R_NOMEMORY;
! 798: } else if (evaluate_option_cache(&ds, packet, NULL, NULL,
! 799: options, NULL,
! 800: &global_scope, oc, MDL) &&
! 801: ds.len >= 4) {
! 802: memcpy(ia->iaid, ds.data, 4);
! 803: ia->ia_type = D6O_IA_TA;
! 804: ia->starts = cur_time;
! 805:
! 806: log_debug("RCV: X-- IA_TA %s",
! 807: print_hex_1(4, ia->iaid, 59));
! 808: /* XXX: This should be the printed time I think. */
! 809: log_debug("RCV: | X-- starts %u",
! 810: (unsigned)ia->starts);
! 811:
! 812: if (ds.len > 4) {
! 813: log_debug("RCV: | X-- [Options]");
! 814:
! 815: if (!option_state_allocate(&ia->options,
! 816: MDL)) {
! 817: log_error("Out of memory allocating "
! 818: "IA_TA option state.");
! 819: dfree(ia, MDL);
! 820: data_string_forget(&ds, MDL);
! 821: return ISC_R_NOMEMORY;
! 822: }
! 823:
! 824: if (!parse_option_buffer(ia->options,
! 825: ds.data + 4,
! 826: ds.len - 4,
! 827: &dhcpv6_universe)) {
! 828: log_error("Corrupt IA_TA options.");
! 829: option_state_dereference(&ia->options,
! 830: MDL);
! 831: dfree(ia, MDL);
! 832: data_string_forget(&ds, MDL);
! 833: return ISC_R_BADPARSE;
! 834: }
! 835: }
! 836: data_string_forget(&ds, MDL);
! 837:
! 838: if (ia->options != NULL) {
! 839: result = dhc6_parse_addrs(&ia->addrs, packet,
! 840: ia->options);
! 841: if (result != ISC_R_SUCCESS) {
! 842: option_state_dereference(&ia->options,
! 843: MDL);
! 844: dfree(ia, MDL);
! 845: return result;
! 846: }
! 847: }
! 848:
! 849: while (*pia != NULL)
! 850: pia = &(*pia)->next;
! 851: *pia = ia;
! 852: pia = &ia->next;
! 853: } else {
! 854: log_error("Invalid IA_TA option cache.");
! 855: dfree(ia, MDL);
! 856: if (ds.len != 0)
! 857: data_string_forget(&ds, MDL);
! 858: return ISC_R_UNEXPECTED;
! 859: }
! 860: }
! 861: delete_option(&dhcpv6_universe, options, D6O_IA_TA);
! 862:
! 863: return ISC_R_SUCCESS;
! 864: }
! 865:
! 866: static isc_result_t
! 867: dhc6_parse_ia_pd(struct dhc6_ia **pia, struct packet *packet,
! 868: struct option_state *options)
! 869: {
! 870: struct data_string ds;
! 871: struct dhc6_ia *ia;
! 872: struct option_cache *oc;
! 873: isc_result_t result;
! 874:
! 875: memset(&ds, 0, sizeof(ds));
! 876:
! 877: oc = lookup_option(&dhcpv6_universe, options, D6O_IA_PD);
! 878: for ( ; oc != NULL ; oc = oc->next) {
! 879: ia = dmalloc(sizeof(*ia), MDL);
! 880: if (ia == NULL) {
! 881: log_error("Out of memory allocating IA_PD structure.");
! 882: return ISC_R_NOMEMORY;
! 883: } else if (evaluate_option_cache(&ds, packet, NULL, NULL,
! 884: options, NULL,
! 885: &global_scope, oc, MDL) &&
! 886: ds.len >= 12) {
! 887: memcpy(ia->iaid, ds.data, 4);
! 888: ia->ia_type = D6O_IA_PD;
! 889: ia->starts = cur_time;
! 890: ia->renew = getULong(ds.data + 4);
! 891: ia->rebind = getULong(ds.data + 8);
! 892:
! 893: log_debug("RCV: X-- IA_PD %s",
! 894: print_hex_1(4, ia->iaid, 59));
! 895: /* XXX: This should be the printed time I think. */
! 896: log_debug("RCV: | X-- starts %u",
! 897: (unsigned)ia->starts);
! 898: log_debug("RCV: | X-- t1 - renew +%u", ia->renew);
! 899: log_debug("RCV: | X-- t2 - rebind +%u", ia->rebind);
! 900:
! 901: /*
! 902: * RFC3633 section 9, discard IA_PD's that
! 903: * have t1 greater than t2, and both not zero.
! 904: * Since RFC3633 defines this behaviour, it is not
! 905: * an error - just normal operation.
! 906: */
! 907: if ((ia->renew > 0) && (ia->rebind > 0) &&
! 908: (ia->renew > ia->rebind)) {
! 909: log_debug("RCV: | !-- INVALID renew/rebind "
! 910: "times, IA_PD discarded.");
! 911: dfree(ia, MDL);
! 912: data_string_forget(&ds, MDL);
! 913: continue;
! 914: }
! 915:
! 916: if (ds.len > 12) {
! 917: log_debug("RCV: | X-- [Options]");
! 918:
! 919: if (!option_state_allocate(&ia->options,
! 920: MDL)) {
! 921: log_error("Out of memory allocating "
! 922: "IA_PD option state.");
! 923: dfree(ia, MDL);
! 924: data_string_forget(&ds, MDL);
! 925: return ISC_R_NOMEMORY;
! 926: }
! 927:
! 928: if (!parse_option_buffer(ia->options,
! 929: ds.data + 12,
! 930: ds.len - 12,
! 931: &dhcpv6_universe)) {
! 932: log_error("Corrupt IA_PD options.");
! 933: option_state_dereference(&ia->options,
! 934: MDL);
! 935: dfree(ia, MDL);
! 936: data_string_forget(&ds, MDL);
! 937: return ISC_R_BADPARSE;
! 938: }
! 939: }
! 940: data_string_forget(&ds, MDL);
! 941:
! 942: if (ia->options != NULL) {
! 943: result = dhc6_parse_prefixes(&ia->addrs,
! 944: packet,
! 945: ia->options);
! 946: if (result != ISC_R_SUCCESS) {
! 947: option_state_dereference(&ia->options,
! 948: MDL);
! 949: dfree(ia, MDL);
! 950: return result;
! 951: }
! 952: }
! 953:
! 954: while (*pia != NULL)
! 955: pia = &(*pia)->next;
! 956: *pia = ia;
! 957: pia = &ia->next;
! 958: } else {
! 959: log_error("Invalid IA_PD option cache.");
! 960: dfree(ia, MDL);
! 961: if (ds.len != 0)
! 962: data_string_forget(&ds, MDL);
! 963: return ISC_R_UNEXPECTED;
! 964: }
! 965: }
! 966: delete_option(&dhcpv6_universe, options, D6O_IA_PD);
! 967:
! 968: return ISC_R_SUCCESS;
! 969: }
! 970:
! 971:
! 972: static isc_result_t
! 973: dhc6_parse_addrs(struct dhc6_addr **paddr, struct packet *packet,
! 974: struct option_state *options)
! 975: {
! 976: struct data_string ds;
! 977: struct option_cache *oc;
! 978: struct dhc6_addr *addr;
! 979:
! 980: memset(&ds, 0, sizeof(ds));
! 981:
! 982: oc = lookup_option(&dhcpv6_universe, options, D6O_IAADDR);
! 983: for ( ; oc != NULL ; oc = oc->next) {
! 984: addr = dmalloc(sizeof(*addr), MDL);
! 985: if (addr == NULL) {
! 986: log_error("Out of memory allocating "
! 987: "address structure.");
! 988: return ISC_R_NOMEMORY;
! 989: } else if (evaluate_option_cache(&ds, packet, NULL, NULL,
! 990: options, NULL, &global_scope,
! 991: oc, MDL) &&
! 992: (ds.len >= 24)) {
! 993:
! 994: addr->address.len = 16;
! 995: memcpy(addr->address.iabuf, ds.data, 16);
! 996: addr->starts = cur_time;
! 997: addr->preferred_life = getULong(ds.data + 16);
! 998: addr->max_life = getULong(ds.data + 20);
! 999:
! 1000: log_debug("RCV: | | X-- IAADDR %s",
! 1001: piaddr(addr->address));
! 1002: log_debug("RCV: | | | X-- Preferred lifetime %u.",
! 1003: addr->preferred_life);
! 1004: log_debug("RCV: | | | X-- Max lifetime %u.",
! 1005: addr->max_life);
! 1006:
! 1007: /*
! 1008: * RFC 3315 section 22.6 says we must discard
! 1009: * addresses whose pref is later than valid.
! 1010: */
! 1011: if ((addr->preferred_life > addr->max_life)) {
! 1012: log_debug("RCV: | | | !-- INVALID lifetimes, "
! 1013: "IAADDR discarded. Check your "
! 1014: "server configuration.");
! 1015: dfree(addr, MDL);
! 1016: data_string_forget(&ds, MDL);
! 1017: continue;
! 1018: }
! 1019:
! 1020: /*
! 1021: * Fortunately this is the last recursion in the
! 1022: * protocol.
! 1023: */
! 1024: if (ds.len > 24) {
! 1025: if (!option_state_allocate(&addr->options,
! 1026: MDL)) {
! 1027: log_error("Out of memory allocating "
! 1028: "IAADDR option state.");
! 1029: dfree(addr, MDL);
! 1030: data_string_forget(&ds, MDL);
! 1031: return ISC_R_NOMEMORY;
! 1032: }
! 1033:
! 1034: if (!parse_option_buffer(addr->options,
! 1035: ds.data + 24,
! 1036: ds.len - 24,
! 1037: &dhcpv6_universe)) {
! 1038: log_error("Corrupt IAADDR options.");
! 1039: option_state_dereference(&addr->options,
! 1040: MDL);
! 1041: dfree(addr, MDL);
! 1042: data_string_forget(&ds, MDL);
! 1043: return ISC_R_BADPARSE;
! 1044: }
! 1045: }
! 1046:
! 1047: if (addr->options != NULL)
! 1048: log_debug("RCV: | | | X-- "
! 1049: "[Options]");
! 1050:
! 1051: data_string_forget(&ds, MDL);
! 1052:
! 1053: *paddr = addr;
! 1054: paddr = &addr->next;
! 1055: } else {
! 1056: log_error("Invalid IAADDR option cache.");
! 1057: dfree(addr, MDL);
! 1058: if (ds.len != 0)
! 1059: data_string_forget(&ds, MDL);
! 1060: return ISC_R_UNEXPECTED;
! 1061: }
! 1062: }
! 1063: delete_option(&dhcpv6_universe, options, D6O_IAADDR);
! 1064:
! 1065: return ISC_R_SUCCESS;
! 1066: }
! 1067:
! 1068: static isc_result_t
! 1069: dhc6_parse_prefixes(struct dhc6_addr **ppfx, struct packet *packet,
! 1070: struct option_state *options)
! 1071: {
! 1072: struct data_string ds;
! 1073: struct option_cache *oc;
! 1074: struct dhc6_addr *pfx;
! 1075:
! 1076: memset(&ds, 0, sizeof(ds));
! 1077:
! 1078: oc = lookup_option(&dhcpv6_universe, options, D6O_IAPREFIX);
! 1079: for ( ; oc != NULL ; oc = oc->next) {
! 1080: pfx = dmalloc(sizeof(*pfx), MDL);
! 1081: if (pfx == NULL) {
! 1082: log_error("Out of memory allocating "
! 1083: "prefix structure.");
! 1084: return ISC_R_NOMEMORY;
! 1085: } else if (evaluate_option_cache(&ds, packet, NULL, NULL,
! 1086: options, NULL, &global_scope,
! 1087: oc, MDL) &&
! 1088: (ds.len >= 25)) {
! 1089:
! 1090: pfx->preferred_life = getULong(ds.data);
! 1091: pfx->max_life = getULong(ds.data + 4);
! 1092: pfx->plen = getUChar(ds.data + 8);
! 1093: pfx->address.len = 16;
! 1094: memcpy(pfx->address.iabuf, ds.data + 9, 16);
! 1095: pfx->starts = cur_time;
! 1096:
! 1097: log_debug("RCV: | | X-- IAPREFIX %s/%d",
! 1098: piaddr(pfx->address), (int)pfx->plen);
! 1099: log_debug("RCV: | | | X-- Preferred lifetime %u.",
! 1100: pfx->preferred_life);
! 1101: log_debug("RCV: | | | X-- Max lifetime %u.",
! 1102: pfx->max_life);
! 1103:
! 1104: /* Sanity check over the prefix length */
! 1105: if ((pfx->plen < 4) || (pfx->plen > 128)) {
! 1106: log_debug("RCV: | | | !-- INVALID prefix "
! 1107: "length, IAPREFIX discarded. "
! 1108: "Check your server configuration.");
! 1109: dfree(pfx, MDL);
! 1110: data_string_forget(&ds, MDL);
! 1111: continue;
! 1112: }
! 1113: /*
! 1114: * RFC 3633 section 10 says we must discard
! 1115: * prefixes whose pref is later than valid.
! 1116: */
! 1117: if ((pfx->preferred_life > pfx->max_life)) {
! 1118: log_debug("RCV: | | | !-- INVALID lifetimes, "
! 1119: "IAPREFIX discarded. Check your "
! 1120: "server configuration.");
! 1121: dfree(pfx, MDL);
! 1122: data_string_forget(&ds, MDL);
! 1123: continue;
! 1124: }
! 1125:
! 1126: /*
! 1127: * Fortunately this is the last recursion in the
! 1128: * protocol.
! 1129: */
! 1130: if (ds.len > 25) {
! 1131: if (!option_state_allocate(&pfx->options,
! 1132: MDL)) {
! 1133: log_error("Out of memory allocating "
! 1134: "IAPREFIX option state.");
! 1135: dfree(pfx, MDL);
! 1136: data_string_forget(&ds, MDL);
! 1137: return ISC_R_NOMEMORY;
! 1138: }
! 1139:
! 1140: if (!parse_option_buffer(pfx->options,
! 1141: ds.data + 25,
! 1142: ds.len - 25,
! 1143: &dhcpv6_universe)) {
! 1144: log_error("Corrupt IAPREFIX options.");
! 1145: option_state_dereference(&pfx->options,
! 1146: MDL);
! 1147: dfree(pfx, MDL);
! 1148: data_string_forget(&ds, MDL);
! 1149: return ISC_R_BADPARSE;
! 1150: }
! 1151: }
! 1152:
! 1153: if (pfx->options != NULL)
! 1154: log_debug("RCV: | | | X-- "
! 1155: "[Options]");
! 1156:
! 1157: data_string_forget(&ds, MDL);
! 1158:
! 1159: *ppfx = pfx;
! 1160: ppfx = &pfx->next;
! 1161: } else {
! 1162: log_error("Invalid IAPREFIX option cache.");
! 1163: dfree(pfx, MDL);
! 1164: if (ds.len != 0)
! 1165: data_string_forget(&ds, MDL);
! 1166: return ISC_R_UNEXPECTED;
! 1167: }
! 1168: }
! 1169: delete_option(&dhcpv6_universe, options, D6O_IAPREFIX);
! 1170:
! 1171: return ISC_R_SUCCESS;
! 1172: }
! 1173:
! 1174: /* Clean up a lease object, deallocate all its parts, and set it to NULL. */
! 1175: void
! 1176: dhc6_lease_destroy(struct dhc6_lease **src, const char *file, int line)
! 1177: {
! 1178: struct dhc6_ia *ia, *nia;
! 1179: struct dhc6_lease *lease;
! 1180:
! 1181: if (src == NULL || *src == NULL) {
! 1182: log_error("Attempt to destroy null lease.");
! 1183: return;
! 1184: }
! 1185: lease = *src;
! 1186:
! 1187: if (lease->server_id.len != 0)
! 1188: data_string_forget(&lease->server_id, file, line);
! 1189:
! 1190: for (ia = lease->bindings ; ia != NULL ; ia = nia) {
! 1191: nia = ia->next;
! 1192:
! 1193: dhc6_ia_destroy(&ia, file, line);
! 1194: }
! 1195:
! 1196: if (lease->options != NULL)
! 1197: option_state_dereference(&lease->options, file, line);
! 1198:
! 1199: dfree(lease, file, line);
! 1200: *src = NULL;
! 1201: }
! 1202:
! 1203: /*
! 1204: * Traverse the addresses list, and destroy their contents, and NULL the
! 1205: * list pointer.
! 1206: */
! 1207: static void
! 1208: dhc6_ia_destroy(struct dhc6_ia **src, const char *file, int line)
! 1209: {
! 1210: struct dhc6_addr *addr, *naddr;
! 1211: struct dhc6_ia *ia;
! 1212:
! 1213: if (src == NULL || *src == NULL) {
! 1214: log_error("Attempt to destroy null IA.");
! 1215: return;
! 1216: }
! 1217: ia = *src;
! 1218:
! 1219: for (addr = ia->addrs ; addr != NULL ; addr = naddr) {
! 1220: naddr = addr->next;
! 1221:
! 1222: if (addr->options != NULL)
! 1223: option_state_dereference(&addr->options, file, line);
! 1224:
! 1225: dfree(addr, file, line);
! 1226: }
! 1227:
! 1228: if (ia->options != NULL)
! 1229: option_state_dereference(&ia->options, file, line);
! 1230:
! 1231: dfree(ia, file, line);
! 1232: *src = NULL;
! 1233: }
! 1234:
! 1235: /*
! 1236: * For a given lease, insert it into the tail of the lease list. Upon
! 1237: * finding a duplicate by server id, remove it and take over its position.
! 1238: */
! 1239: static void
! 1240: insert_lease(struct dhc6_lease **head, struct dhc6_lease *new)
! 1241: {
! 1242: while (*head != NULL) {
! 1243: if ((*head)->server_id.len == new->server_id.len &&
! 1244: memcmp((*head)->server_id.data, new->server_id.data,
! 1245: new->server_id.len) == 0) {
! 1246: new->next = (*head)->next;
! 1247: dhc6_lease_destroy(head, MDL);
! 1248: break;
! 1249: }
! 1250:
! 1251: head= &(*head)->next;
! 1252: }
! 1253:
! 1254: *head = new;
! 1255: return;
! 1256: }
! 1257:
! 1258: /*
! 1259: * Not really clear what to do here yet.
! 1260: */
! 1261: static int
! 1262: dhc6_score_lease(struct client_state *client, struct dhc6_lease *lease)
! 1263: {
! 1264: struct dhc6_ia *ia;
! 1265: struct dhc6_addr *addr;
! 1266: struct option **req;
! 1267: int i;
! 1268:
! 1269: if (lease->score)
! 1270: return lease->score;
! 1271:
! 1272: lease->score = 1;
! 1273:
! 1274: /* If this lease lacks a required option, dump it. */
! 1275: /* XXX: we should be able to cache the failure... */
! 1276: req = client->config->required_options;
! 1277: if (req != NULL) {
! 1278: for (i = 0 ; req[i] != NULL ; i++) {
! 1279: if (lookup_option(&dhcpv6_universe, lease->options,
! 1280: req[i]->code) == NULL) {
! 1281: lease->score = 0;
! 1282: return lease->score;
! 1283: }
! 1284: }
! 1285: }
! 1286:
! 1287: /* If this lease contains a requested option, improve its score. */
! 1288: req = client->config->requested_options;
! 1289: if (req != NULL) {
! 1290: for (i = 0 ; req[i] != NULL ; i++) {
! 1291: if (lookup_option(&dhcpv6_universe, lease->options,
! 1292: req[i]->code) != NULL)
! 1293: lease->score++;
! 1294: }
! 1295: }
! 1296:
! 1297: for (ia = lease->bindings ; ia != NULL ; ia = ia->next) {
! 1298: lease->score += 50;
! 1299:
! 1300: for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
! 1301: lease->score += 100;
! 1302: }
! 1303: }
! 1304:
! 1305: return lease->score;
! 1306: }
! 1307:
! 1308: /*
! 1309: * start_init6() kicks off the process, transmitting a packet and
! 1310: * scheduling a retransmission event.
! 1311: */
! 1312: void
! 1313: start_init6(struct client_state *client)
! 1314: {
! 1315: struct timeval tv;
! 1316:
! 1317: log_debug("PRC: Soliciting for leases (INIT).");
! 1318: client->state = S_INIT;
! 1319:
! 1320: /* Initialize timers, RFC3315 section 17.1.2. */
! 1321: client->IRT = SOL_TIMEOUT * 100;
! 1322: client->MRT = SOL_MAX_RT * 100;
! 1323: client->MRC = 0;
! 1324: /* Default is 0 (no max) but -1 changes this. */
! 1325: if (!onetry)
! 1326: client->MRD = 0;
! 1327: else
! 1328: client->MRD = client->config->timeout;
! 1329:
! 1330: dhc6_retrans_init(client);
! 1331:
! 1332: /*
! 1333: * RFC3315 section 17.1.2 goes out of its way:
! 1334: * Also, the first RT MUST be selected to be strictly greater than IRT
! 1335: * by choosing RAND to be strictly greater than 0.
! 1336: */
! 1337: /* if RAND < 0 then RAND = -RAND */
! 1338: if (client->RT <= client->IRT)
! 1339: client->RT = client->IRT + (client->IRT - client->RT);
! 1340: /* if RAND == 0 then RAND = 1 */
! 1341: if (client->RT <= client->IRT)
! 1342: client->RT = client->IRT + 1;
! 1343:
! 1344: client->v6_handler = init_handler;
! 1345:
! 1346: /*
! 1347: * RFC3315 section 17.1.2 says we MUST start the first packet
! 1348: * between 0 and SOL_MAX_DELAY seconds. The good news is
! 1349: * SOL_MAX_DELAY is 1.
! 1350: */
! 1351: tv.tv_sec = cur_tv.tv_sec;
! 1352: tv.tv_usec = cur_tv.tv_usec;
! 1353: tv.tv_usec += (random() % (SOL_MAX_DELAY * 100)) * 10000;
! 1354: if (tv.tv_usec >= 1000000) {
! 1355: tv.tv_sec += 1;
! 1356: tv.tv_usec -= 1000000;
! 1357: }
! 1358: add_timeout(&tv, do_init6, client, NULL, NULL);
! 1359:
! 1360: if (nowait)
! 1361: go_daemon();
! 1362: }
! 1363:
! 1364: /*
! 1365: * start_info_request6() kicks off the process, transmitting an info
! 1366: * request packet and scheduling a retransmission event.
! 1367: */
! 1368: void
! 1369: start_info_request6(struct client_state *client)
! 1370: {
! 1371: struct timeval tv;
! 1372:
! 1373: log_debug("PRC: Requesting information (INIT).");
! 1374: client->state = S_INIT;
! 1375:
! 1376: /* Initialize timers, RFC3315 section 18.1.5. */
! 1377: client->IRT = INF_TIMEOUT * 100;
! 1378: client->MRT = INF_MAX_RT * 100;
! 1379: client->MRC = 0;
! 1380: /* Default is 0 (no max) but -1 changes this. */
! 1381: if (!onetry)
! 1382: client->MRD = 0;
! 1383: else
! 1384: client->MRD = client->config->timeout;
! 1385:
! 1386: dhc6_retrans_init(client);
! 1387:
! 1388: client->v6_handler = info_request_handler;
! 1389:
! 1390: /*
! 1391: * RFC3315 section 18.1.5 says we MUST start the first packet
! 1392: * between 0 and INF_MAX_DELAY seconds. The good news is
! 1393: * INF_MAX_DELAY is 1.
! 1394: */
! 1395: tv.tv_sec = cur_tv.tv_sec;
! 1396: tv.tv_usec = cur_tv.tv_usec;
! 1397: tv.tv_usec += (random() % (INF_MAX_DELAY * 100)) * 10000;
! 1398: if (tv.tv_usec >= 1000000) {
! 1399: tv.tv_sec += 1;
! 1400: tv.tv_usec -= 1000000;
! 1401: }
! 1402: add_timeout(&tv, do_info_request6, client, NULL, NULL);
! 1403:
! 1404: if (nowait)
! 1405: go_daemon();
! 1406: }
! 1407:
! 1408: /*
! 1409: * start_confirm6() kicks off an "init-reboot" version of the process, at
! 1410: * startup to find out if old bindings are 'fair' and at runtime whenever
! 1411: * a link cycles state we'll eventually want to do this.
! 1412: */
! 1413: void
! 1414: start_confirm6(struct client_state *client)
! 1415: {
! 1416: struct timeval tv;
! 1417:
! 1418: /* If there is no active lease, there is nothing to check. */
! 1419: if ((client->active_lease == NULL) ||
! 1420: !active_prefix(client) ||
! 1421: client->active_lease->released) {
! 1422: start_init6(client);
! 1423: return;
! 1424: }
! 1425:
! 1426: log_debug("PRC: Confirming active lease (INIT-REBOOT).");
! 1427: client->state = S_REBOOTING;
! 1428:
! 1429: /* Initialize timers, RFC3315 section 17.1.3. */
! 1430: client->IRT = CNF_TIMEOUT * 100;
! 1431: client->MRT = CNF_MAX_RT * 100;
! 1432: client->MRC = 0;
! 1433: client->MRD = CNF_MAX_RD;
! 1434:
! 1435: dhc6_retrans_init(client);
! 1436:
! 1437: client->v6_handler = reply_handler;
! 1438:
! 1439: /*
! 1440: * RFC3315 section 18.1.2 says we MUST start the first packet
! 1441: * between 0 and CNF_MAX_DELAY seconds. The good news is
! 1442: * CNF_MAX_DELAY is 1.
! 1443: */
! 1444: tv.tv_sec = cur_tv.tv_sec;
! 1445: tv.tv_usec = cur_tv.tv_usec;
! 1446: tv.tv_usec += (random() % (CNF_MAX_DELAY * 100)) * 10000;
! 1447: if (tv.tv_usec >= 1000000) {
! 1448: tv.tv_sec += 1;
! 1449: tv.tv_usec -= 1000000;
! 1450: }
! 1451: if (wanted_ia_pd != 0) {
! 1452: client->state = S_REBINDING;
! 1453: client->refresh_type = DHCPV6_REBIND;
! 1454: add_timeout(&tv, do_refresh6, client, NULL, NULL);
! 1455: } else
! 1456: add_timeout(&tv, do_confirm6, client, NULL, NULL);
! 1457: }
! 1458:
! 1459: /*
! 1460: * check_timing6() check on the timing for sending a v6 message
! 1461: * and then do the basic initialization for a v6 message.
! 1462: */
! 1463: #define CHK_TIM_SUCCESS 0
! 1464: #define CHK_TIM_MRC_EXCEEDED 1
! 1465: #define CHK_TIM_MRD_EXCEEDED 2
! 1466: #define CHK_TIM_ALLOC_FAILURE 3
! 1467:
! 1468: int
! 1469: check_timing6 (struct client_state *client, u_int8_t msg_type,
! 1470: char *msg_str, struct dhc6_lease *lease,
! 1471: struct data_string *ds)
! 1472: {
! 1473: struct timeval elapsed;
! 1474:
! 1475: /*
! 1476: * Start_time starts at the first transmission.
! 1477: */
! 1478: if (client->txcount == 0) {
! 1479: client->start_time.tv_sec = cur_tv.tv_sec;
! 1480: client->start_time.tv_usec = cur_tv.tv_usec;
! 1481: } else if ((client->MRC != 0) && (client->txcount > client->MRC)) {
! 1482: log_info("Max retransmission count exceeded.");
! 1483: return(CHK_TIM_MRC_EXCEEDED);
! 1484: }
! 1485:
! 1486: /* elapsed = cur - start */
! 1487: elapsed.tv_sec = cur_tv.tv_sec - client->start_time.tv_sec;
! 1488: elapsed.tv_usec = cur_tv.tv_usec - client->start_time.tv_usec;
! 1489: if (elapsed.tv_usec < 0) {
! 1490: elapsed.tv_sec -= 1;
! 1491: elapsed.tv_usec += 1000000;
! 1492: }
! 1493:
! 1494: /* Check if finished (-1 argument). */
! 1495: if ((client->MRD != 0) && (elapsed.tv_sec > client->MRD)) {
! 1496: log_info("Max retransmission duration exceeded.");
! 1497: return(CHK_TIM_MRD_EXCEEDED);
! 1498: }
! 1499:
! 1500: memset(ds, 0, sizeof(*ds));
! 1501: if (!buffer_allocate(&(ds->buffer), 4, MDL)) {
! 1502: log_error("Unable to allocate memory for %s.", msg_str);
! 1503: return(CHK_TIM_ALLOC_FAILURE);
! 1504: }
! 1505: ds->data = ds->buffer->data;
! 1506: ds->len = 4;
! 1507:
! 1508: ds->buffer->data[0] = msg_type;
! 1509: memcpy(ds->buffer->data + 1, client->dhcpv6_transaction_id, 3);
! 1510:
! 1511: /* Form an elapsed option. */
! 1512: /* Maximum value is 65535 1/100s coded as 0xffff. */
! 1513: if ((elapsed.tv_sec < 0) || (elapsed.tv_sec > 655) ||
! 1514: ((elapsed.tv_sec == 655) && (elapsed.tv_usec > 350000))) {
! 1515: client->elapsed = 0xffff;
! 1516: } else {
! 1517: client->elapsed = elapsed.tv_sec * 100;
! 1518: client->elapsed += elapsed.tv_usec / 10000;
! 1519: }
! 1520:
! 1521: if (client->elapsed == 0)
! 1522: log_debug("XMT: Forming %s, 0 ms elapsed.", msg_str);
! 1523: else
! 1524: log_debug("XMT: Forming %s, %u0 ms elapsed.", msg_str,
! 1525: (unsigned)client->elapsed);
! 1526:
! 1527: client->elapsed = htons(client->elapsed);
! 1528:
! 1529: make_client6_options(client, &client->sent_options, lease, msg_type);
! 1530:
! 1531: return(CHK_TIM_SUCCESS);
! 1532: }
! 1533:
! 1534: /*
! 1535: * do_init6() marshals and transmits a solicit.
! 1536: */
! 1537: void
! 1538: do_init6(void *input)
! 1539: {
! 1540: struct client_state *client;
! 1541: struct dhc6_ia *old_ia;
! 1542: struct dhc6_addr *old_addr;
! 1543: struct data_string ds;
! 1544: struct data_string ia;
! 1545: struct data_string addr;
! 1546: struct timeval tv;
! 1547: u_int32_t t1, t2;
! 1548: int i, idx, len, send_ret;
! 1549:
! 1550: client = input;
! 1551:
! 1552: /*
! 1553: * In RFC3315 section 17.1.2, the retransmission timer is
! 1554: * used as the selecting timer.
! 1555: */
! 1556: if (client->advertised_leases != NULL) {
! 1557: start_selecting6(client);
! 1558: return;
! 1559: }
! 1560:
! 1561: switch(check_timing6(client, DHCPV6_SOLICIT, "Solicit", NULL, &ds)) {
! 1562: case CHK_TIM_MRC_EXCEEDED:
! 1563: case CHK_TIM_ALLOC_FAILURE:
! 1564: return;
! 1565: case CHK_TIM_MRD_EXCEEDED:
! 1566: client->state = S_STOPPED;
! 1567: if (client->active_lease != NULL) {
! 1568: dhc6_lease_destroy(&client->active_lease, MDL);
! 1569: client->active_lease = NULL;
! 1570: }
! 1571: /* Stop if and only if this is the last client. */
! 1572: if (stopping_finished())
! 1573: exit(2);
! 1574: return;
! 1575: }
! 1576:
! 1577: /*
! 1578: * Fetch any configured 'sent' options (includes DUID) in wire format.
! 1579: */
! 1580: dhcpv6_universe.encapsulate(&ds, NULL, NULL, client,
! 1581: NULL, client->sent_options, &global_scope,
! 1582: &dhcpv6_universe);
! 1583:
! 1584: /* Use a specific handler with rapid-commit. */
! 1585: if (lookup_option(&dhcpv6_universe, client->sent_options,
! 1586: D6O_RAPID_COMMIT) != NULL) {
! 1587: client->v6_handler = rapid_commit_handler;
! 1588: }
! 1589:
! 1590: /* Append IA_NA. */
! 1591: for (i = 0; i < wanted_ia_na; i++) {
! 1592: /*
! 1593: * XXX: maybe the IA_NA('s) should be put into the sent_options
! 1594: * cache. They'd have to be pulled down as they also contain
! 1595: * different option caches in the same universe...
! 1596: */
! 1597: memset(&ia, 0, sizeof(ia));
! 1598: if (!buffer_allocate(&ia.buffer, 12, MDL)) {
! 1599: log_error("Unable to allocate memory for IA_NA.");
! 1600: data_string_forget(&ds, MDL);
! 1601: return;
! 1602: }
! 1603: ia.data = ia.buffer->data;
! 1604: ia.len = 12;
! 1605:
! 1606: /*
! 1607: * A simple IAID is the last 4 bytes
! 1608: * of the hardware address.
! 1609: */
! 1610: if (client->interface->hw_address.hlen > 4) {
! 1611: idx = client->interface->hw_address.hlen - 4;
! 1612: len = 4;
! 1613: } else {
! 1614: idx = 0;
! 1615: len = client->interface->hw_address.hlen;
! 1616: }
! 1617: memcpy(ia.buffer->data,
! 1618: client->interface->hw_address.hbuf + idx,
! 1619: len);
! 1620: if (i)
! 1621: ia.buffer->data[3] += i;
! 1622:
! 1623: t1 = client->config->requested_lease / 2;
! 1624: t2 = t1 + (t1 / 2);
! 1625: putULong(ia.buffer->data + 4, t1);
! 1626: putULong(ia.buffer->data + 8, t2);
! 1627:
! 1628: log_debug("XMT: X-- IA_NA %s",
! 1629: print_hex_1(4, ia.buffer->data, 55));
! 1630: log_debug("XMT: | X-- Request renew in +%u", (unsigned)t1);
! 1631: log_debug("XMT: | X-- Request rebind in +%u", (unsigned)t2);
! 1632:
! 1633: if ((client->active_lease != NULL) &&
! 1634: ((old_ia = find_ia(client->active_lease->bindings,
! 1635: D6O_IA_NA,
! 1636: (char *)ia.buffer->data)) != NULL)) {
! 1637: /*
! 1638: * For each address in the old IA_NA,
! 1639: * request a binding.
! 1640: */
! 1641: memset(&addr, 0, sizeof(addr));
! 1642: for (old_addr = old_ia->addrs ; old_addr != NULL ;
! 1643: old_addr = old_addr->next) {
! 1644: if (old_addr->address.len != 16) {
! 1645: log_error("Invalid IPv6 address "
! 1646: "length %d. "
! 1647: "Ignoring. (%s:%d)",
! 1648: old_addr->address.len,
! 1649: MDL);
! 1650: continue;
! 1651: }
! 1652:
! 1653: if (!buffer_allocate(&addr.buffer, 24, MDL)) {
! 1654: log_error("Unable to allocate memory "
! 1655: "for IAADDR.");
! 1656: data_string_forget(&ia, MDL);
! 1657: data_string_forget(&ds, MDL);
! 1658: return;
! 1659: }
! 1660: addr.data = addr.buffer->data;
! 1661: addr.len = 24;
! 1662:
! 1663: memcpy(addr.buffer->data,
! 1664: old_addr->address.iabuf,
! 1665: 16);
! 1666:
! 1667: t1 = client->config->requested_lease;
! 1668: t2 = t1 + (t1 / 2);
! 1669: putULong(addr.buffer->data + 16, t1);
! 1670: putULong(addr.buffer->data + 20, t2);
! 1671:
! 1672: log_debug("XMT: | X-- Request address %s.",
! 1673: piaddr(old_addr->address));
! 1674: log_debug("XMT: | | X-- Request "
! 1675: "preferred in +%u",
! 1676: (unsigned)t1);
! 1677: log_debug("XMT: | | X-- Request valid "
! 1678: "in +%u",
! 1679: (unsigned)t2);
! 1680:
! 1681: append_option(&ia, &dhcpv6_universe,
! 1682: iaaddr_option,
! 1683: &addr);
! 1684:
! 1685: data_string_forget(&addr, MDL);
! 1686: }
! 1687: }
! 1688:
! 1689: append_option(&ds, &dhcpv6_universe, ia_na_option, &ia);
! 1690: data_string_forget(&ia, MDL);
! 1691: }
! 1692:
! 1693: /* Append IA_TA. */
! 1694: for (i = 0; i < wanted_ia_ta; i++) {
! 1695: /*
! 1696: * XXX: maybe the IA_TA('s) should be put into the sent_options
! 1697: * cache. They'd have to be pulled down as they also contain
! 1698: * different option caches in the same universe...
! 1699: */
! 1700: memset(&ia, 0, sizeof(ia));
! 1701: if (!buffer_allocate(&ia.buffer, 4, MDL)) {
! 1702: log_error("Unable to allocate memory for IA_TA.");
! 1703: data_string_forget(&ds, MDL);
! 1704: return;
! 1705: }
! 1706: ia.data = ia.buffer->data;
! 1707: ia.len = 4;
! 1708:
! 1709: /*
! 1710: * A simple IAID is the last 4 bytes
! 1711: * of the hardware address.
! 1712: */
! 1713: if (client->interface->hw_address.hlen > 4) {
! 1714: idx = client->interface->hw_address.hlen - 4;
! 1715: len = 4;
! 1716: } else {
! 1717: idx = 0;
! 1718: len = client->interface->hw_address.hlen;
! 1719: }
! 1720: memcpy(ia.buffer->data,
! 1721: client->interface->hw_address.hbuf + idx,
! 1722: len);
! 1723: if (i)
! 1724: ia.buffer->data[3] += i;
! 1725:
! 1726: log_debug("XMT: X-- IA_TA %s",
! 1727: print_hex_1(4, ia.buffer->data, 55));
! 1728:
! 1729: if ((client->active_lease != NULL) &&
! 1730: ((old_ia = find_ia(client->active_lease->bindings,
! 1731: D6O_IA_TA,
! 1732: (char *)ia.buffer->data)) != NULL)) {
! 1733: /*
! 1734: * For each address in the old IA_TA,
! 1735: * request a binding.
! 1736: */
! 1737: memset(&addr, 0, sizeof(addr));
! 1738: for (old_addr = old_ia->addrs ; old_addr != NULL ;
! 1739: old_addr = old_addr->next) {
! 1740: if (old_addr->address.len != 16) {
! 1741: log_error("Invalid IPv6 address "
! 1742: "length %d. "
! 1743: "Ignoring. (%s:%d)",
! 1744: old_addr->address.len,
! 1745: MDL);
! 1746: continue;
! 1747: }
! 1748:
! 1749: if (!buffer_allocate(&addr.buffer, 24, MDL)) {
! 1750: log_error("Unable to allocate memory "
! 1751: "for IAADDR.");
! 1752: data_string_forget(&ia, MDL);
! 1753: data_string_forget(&ds, MDL);
! 1754: return;
! 1755: }
! 1756: addr.data = addr.buffer->data;
! 1757: addr.len = 24;
! 1758:
! 1759: memcpy(addr.buffer->data,
! 1760: old_addr->address.iabuf,
! 1761: 16);
! 1762:
! 1763: t1 = client->config->requested_lease;
! 1764: t2 = t1 + (t1 / 2);
! 1765: putULong(addr.buffer->data + 16, t1);
! 1766: putULong(addr.buffer->data + 20, t2);
! 1767:
! 1768: log_debug("XMT: | X-- Request address %s.",
! 1769: piaddr(old_addr->address));
! 1770: log_debug("XMT: | | X-- Request "
! 1771: "preferred in +%u",
! 1772: (unsigned)t1);
! 1773: log_debug("XMT: | | X-- Request valid "
! 1774: "in +%u",
! 1775: (unsigned)t2);
! 1776:
! 1777: append_option(&ia, &dhcpv6_universe,
! 1778: iaaddr_option,
! 1779: &addr);
! 1780:
! 1781: data_string_forget(&addr, MDL);
! 1782: }
! 1783: }
! 1784:
! 1785: append_option(&ds, &dhcpv6_universe, ia_ta_option, &ia);
! 1786: data_string_forget(&ia, MDL);
! 1787: }
! 1788:
! 1789: /* Append IA_PD. */
! 1790: for (i = 0; i < wanted_ia_pd; i++) {
! 1791: /*
! 1792: * XXX: maybe the IA_PD('s) should be put into the sent_options
! 1793: * cache. They'd have to be pulled down as they also contain
! 1794: * different option caches in the same universe...
! 1795: */
! 1796: memset(&ia, 0, sizeof(ia));
! 1797: if (!buffer_allocate(&ia.buffer, 12, MDL)) {
! 1798: log_error("Unable to allocate memory for IA_PD.");
! 1799: data_string_forget(&ds, MDL);
! 1800: return;
! 1801: }
! 1802: ia.data = ia.buffer->data;
! 1803: ia.len = 12;
! 1804:
! 1805: /*
! 1806: * A simple IAID is the last 4 bytes
! 1807: * of the hardware address.
! 1808: */
! 1809: if (client->interface->hw_address.hlen > 4) {
! 1810: idx = client->interface->hw_address.hlen - 4;
! 1811: len = 4;
! 1812: } else {
! 1813: idx = 0;
! 1814: len = client->interface->hw_address.hlen;
! 1815: }
! 1816: memcpy(ia.buffer->data,
! 1817: client->interface->hw_address.hbuf + idx,
! 1818: len);
! 1819: if (i)
! 1820: ia.buffer->data[3] += i;
! 1821:
! 1822: t1 = client->config->requested_lease / 2;
! 1823: t2 = t1 + (t1 / 2);
! 1824: putULong(ia.buffer->data + 4, t1);
! 1825: putULong(ia.buffer->data + 8, t2);
! 1826:
! 1827: log_debug("XMT: X-- IA_PD %s",
! 1828: print_hex_1(4, ia.buffer->data, 55));
! 1829: log_debug("XMT: | X-- Request renew in +%u", (unsigned)t1);
! 1830: log_debug("XMT: | X-- Request rebind in +%u", (unsigned)t2);
! 1831:
! 1832: if ((client->active_lease != NULL) &&
! 1833: ((old_ia = find_ia(client->active_lease->bindings,
! 1834: D6O_IA_PD,
! 1835: (char *)ia.buffer->data)) != NULL)) {
! 1836: /*
! 1837: * For each prefix in the old IA_PD,
! 1838: * request a binding.
! 1839: */
! 1840: memset(&addr, 0, sizeof(addr));
! 1841: for (old_addr = old_ia->addrs ; old_addr != NULL ;
! 1842: old_addr = old_addr->next) {
! 1843: if (old_addr->address.len != 16) {
! 1844: log_error("Invalid IPv6 prefix, "
! 1845: "Ignoring. (%s:%d)",
! 1846: MDL);
! 1847: continue;
! 1848: }
! 1849:
! 1850: if (!buffer_allocate(&addr.buffer, 25, MDL)) {
! 1851: log_error("Unable to allocate memory "
! 1852: "for IAPREFIX.");
! 1853: data_string_forget(&ia, MDL);
! 1854: data_string_forget(&ds, MDL);
! 1855: return;
! 1856: }
! 1857: addr.data = addr.buffer->data;
! 1858: addr.len = 25;
! 1859:
! 1860: t1 = client->config->requested_lease;
! 1861: t2 = t1 + (t1 / 2);
! 1862: putULong(addr.buffer->data, t1);
! 1863: putULong(addr.buffer->data + 4, t2);
! 1864:
! 1865: putUChar(addr.buffer->data + 8,
! 1866: old_addr->plen);
! 1867: memcpy(addr.buffer->data + 9,
! 1868: old_addr->address.iabuf,
! 1869: 16);
! 1870:
! 1871: log_debug("XMT: | X-- Request prefix %s/%u.",
! 1872: piaddr(old_addr->address),
! 1873: (unsigned) old_addr->plen);
! 1874: log_debug("XMT: | | X-- Request "
! 1875: "preferred in +%u",
! 1876: (unsigned)t1);
! 1877: log_debug("XMT: | | X-- Request valid "
! 1878: "in +%u",
! 1879: (unsigned)t2);
! 1880:
! 1881: append_option(&ia, &dhcpv6_universe,
! 1882: iaprefix_option,
! 1883: &addr);
! 1884:
! 1885: data_string_forget(&addr, MDL);
! 1886: }
! 1887: }
! 1888:
! 1889: append_option(&ds, &dhcpv6_universe, ia_pd_option, &ia);
! 1890: data_string_forget(&ia, MDL);
! 1891: }
! 1892:
! 1893: /* Transmit and wait. */
! 1894:
! 1895: log_info("XMT: Solicit on %s, interval %ld0ms.",
! 1896: client->name ? client->name : client->interface->name,
! 1897: (long int)client->RT);
! 1898:
! 1899: send_ret = send_packet6(client->interface,
! 1900: ds.data, ds.len, &DHCPv6DestAddr);
! 1901: if (send_ret != ds.len) {
! 1902: log_error("dhc6: send_packet6() sent %d of %d bytes",
! 1903: send_ret, ds.len);
! 1904: }
! 1905:
! 1906: data_string_forget(&ds, MDL);
! 1907:
! 1908: /* Wait RT */
! 1909: tv.tv_sec = cur_tv.tv_sec + client->RT / 100;
! 1910: tv.tv_usec = cur_tv.tv_usec + (client->RT % 100) * 10000;
! 1911: if (tv.tv_usec >= 1000000) {
! 1912: tv.tv_sec += 1;
! 1913: tv.tv_usec -= 1000000;
! 1914: }
! 1915: add_timeout(&tv, do_init6, client, NULL, NULL);
! 1916:
! 1917: dhc6_retrans_advance(client);
! 1918: }
! 1919:
! 1920: /* do_info_request6() marshals and transmits an information-request. */
! 1921: void
! 1922: do_info_request6(void *input)
! 1923: {
! 1924: struct client_state *client;
! 1925: struct data_string ds;
! 1926: struct timeval tv;
! 1927: int send_ret;
! 1928:
! 1929: client = input;
! 1930:
! 1931: switch(check_timing6(client, DHCPV6_INFORMATION_REQUEST,
! 1932: "Info-Request", NULL, &ds)) {
! 1933: case CHK_TIM_MRC_EXCEEDED:
! 1934: case CHK_TIM_ALLOC_FAILURE:
! 1935: return;
! 1936: case CHK_TIM_MRD_EXCEEDED:
! 1937: exit(2);
! 1938: case CHK_TIM_SUCCESS:
! 1939: break;
! 1940: }
! 1941:
! 1942: /* Fetch any configured 'sent' options (includes DUID) in wire format.
! 1943: */
! 1944: dhcpv6_universe.encapsulate(&ds, NULL, NULL, client,
! 1945: NULL, client->sent_options, &global_scope,
! 1946: &dhcpv6_universe);
! 1947:
! 1948: /* Transmit and wait. */
! 1949:
! 1950: log_info("XMT: Info-Request on %s, interval %ld0ms.",
! 1951: client->name ? client->name : client->interface->name,
! 1952: (long int)client->RT);
! 1953:
! 1954: send_ret = send_packet6(client->interface,
! 1955: ds.data, ds.len, &DHCPv6DestAddr);
! 1956: if (send_ret != ds.len) {
! 1957: log_error("dhc6: send_packet6() sent %d of %d bytes",
! 1958: send_ret, ds.len);
! 1959: }
! 1960:
! 1961: data_string_forget(&ds, MDL);
! 1962:
! 1963: /* Wait RT */
! 1964: tv.tv_sec = cur_tv.tv_sec + client->RT / 100;
! 1965: tv.tv_usec = cur_tv.tv_usec + (client->RT % 100) * 10000;
! 1966: if (tv.tv_usec >= 1000000) {
! 1967: tv.tv_sec += 1;
! 1968: tv.tv_usec -= 1000000;
! 1969: }
! 1970: add_timeout(&tv, do_info_request6, client, NULL, NULL);
! 1971:
! 1972: dhc6_retrans_advance(client);
! 1973: }
! 1974:
! 1975: /* do_confirm6() creates a Confirm packet and transmits it. This function
! 1976: * is called on every timeout to (re)transmit.
! 1977: */
! 1978: void
! 1979: do_confirm6(void *input)
! 1980: {
! 1981: struct client_state *client;
! 1982: struct data_string ds;
! 1983: int send_ret;
! 1984: struct timeval tv;
! 1985:
! 1986: client = input;
! 1987:
! 1988: if (client->active_lease == NULL)
! 1989: log_fatal("Impossible condition at %s:%d.", MDL);
! 1990:
! 1991: /* In section 17.1.3, it is said:
! 1992: *
! 1993: * If the client receives no responses before the message
! 1994: * transmission process terminates, as described in section 14,
! 1995: * the client SHOULD continue to use any IP addresses, using the
! 1996: * last known lifetimes for those addresses, and SHOULD continue
! 1997: * to use any other previously obtained configuration parameters.
! 1998: *
! 1999: * So if confirm times out, we go active.
! 2000: *
! 2001: * XXX: Should we reduce all IA's t1 to 0, so that we renew and
! 2002: * stick there until we get a reply?
! 2003: */
! 2004:
! 2005: switch(check_timing6(client, DHCPV6_CONFIRM, "Confirm",
! 2006: client->active_lease, &ds)) {
! 2007: case CHK_TIM_MRC_EXCEEDED:
! 2008: case CHK_TIM_MRD_EXCEEDED:
! 2009: start_bound(client);
! 2010: return;
! 2011: case CHK_TIM_ALLOC_FAILURE:
! 2012: return;
! 2013: case CHK_TIM_SUCCESS:
! 2014: break;
! 2015: }
! 2016:
! 2017: /* Fetch any configured 'sent' options (includes DUID') in wire format.
! 2018: */
! 2019: dhcpv6_universe.encapsulate(&ds, NULL, NULL, client, NULL,
! 2020: client->sent_options, &global_scope,
! 2021: &dhcpv6_universe);
! 2022:
! 2023: /* Append IA's. */
! 2024: if (wanted_ia_na &&
! 2025: dhc6_add_ia_na(client, &ds, client->active_lease,
! 2026: DHCPV6_CONFIRM) != ISC_R_SUCCESS) {
! 2027: data_string_forget(&ds, MDL);
! 2028: return;
! 2029: }
! 2030: if (wanted_ia_ta &&
! 2031: dhc6_add_ia_ta(client, &ds, client->active_lease,
! 2032: DHCPV6_CONFIRM) != ISC_R_SUCCESS) {
! 2033: data_string_forget(&ds, MDL);
! 2034: return;
! 2035: }
! 2036:
! 2037: /* Transmit and wait. */
! 2038:
! 2039: log_info("XMT: Confirm on %s, interval %ld0ms.",
! 2040: client->name ? client->name : client->interface->name,
! 2041: (long int)client->RT);
! 2042:
! 2043: send_ret = send_packet6(client->interface, ds.data, ds.len,
! 2044: &DHCPv6DestAddr);
! 2045: if (send_ret != ds.len) {
! 2046: log_error("dhc6: sendpacket6() sent %d of %d bytes",
! 2047: send_ret, ds.len);
! 2048: }
! 2049:
! 2050: data_string_forget(&ds, MDL);
! 2051:
! 2052: /* Wait RT */
! 2053: tv.tv_sec = cur_tv.tv_sec + client->RT / 100;
! 2054: tv.tv_usec = cur_tv.tv_usec + (client->RT % 100) * 10000;
! 2055: if (tv.tv_usec >= 1000000) {
! 2056: tv.tv_sec += 1;
! 2057: tv.tv_usec -= 1000000;
! 2058: }
! 2059: add_timeout(&tv, do_confirm6, client, NULL, NULL);
! 2060:
! 2061: dhc6_retrans_advance(client);
! 2062: }
! 2063:
! 2064: /*
! 2065: * Release addresses.
! 2066: */
! 2067: void
! 2068: start_release6(struct client_state *client)
! 2069: {
! 2070: /* Cancel any pending transmissions */
! 2071: cancel_timeout(do_confirm6, client);
! 2072: cancel_timeout(do_select6, client);
! 2073: cancel_timeout(do_refresh6, client);
! 2074: cancel_timeout(do_release6, client);
! 2075: client->state = S_STOPPED;
! 2076:
! 2077: /*
! 2078: * It is written: "The client MUST NOT use any of the addresses it
! 2079: * is releasing as the source address in the Release message or in
! 2080: * any subsequently transmitted message." So unconfigure now.
! 2081: */
! 2082: unconfigure6(client, "RELEASE6");
! 2083:
! 2084: /* Note this in the lease file. */
! 2085: if (client->active_lease == NULL)
! 2086: return;
! 2087: client->active_lease->released = ISC_TRUE;
! 2088: write_client6_lease(client, client->active_lease, 0, 1);
! 2089:
! 2090: /* Set timers per RFC3315 section 18.1.6. */
! 2091: client->IRT = REL_TIMEOUT * 100;
! 2092: client->MRT = 0;
! 2093: client->MRC = REL_MAX_RC;
! 2094: client->MRD = 0;
! 2095:
! 2096: dhc6_retrans_init(client);
! 2097: client->v6_handler = reply_handler;
! 2098:
! 2099: do_release6(client);
! 2100: }
! 2101: /*
! 2102: * do_release6() creates a Release packet and transmits it.
! 2103: */
! 2104: static void
! 2105: do_release6(void *input)
! 2106: {
! 2107: struct client_state *client;
! 2108: struct data_string ds;
! 2109: int send_ret;
! 2110: struct timeval tv;
! 2111:
! 2112: client = input;
! 2113:
! 2114: if ((client->active_lease == NULL) || !active_prefix(client))
! 2115: return;
! 2116:
! 2117: switch(check_timing6(client, DHCPV6_RELEASE, "Release",
! 2118: client->active_lease, &ds)) {
! 2119: case CHK_TIM_MRC_EXCEEDED:
! 2120: case CHK_TIM_ALLOC_FAILURE:
! 2121: case CHK_TIM_MRD_EXCEEDED:
! 2122: goto release_done;
! 2123: case CHK_TIM_SUCCESS:
! 2124: break;
! 2125: }
! 2126:
! 2127: /*
! 2128: * Don't use unicast as we don't know if we still have an
! 2129: * available address with enough scope.
! 2130: */
! 2131:
! 2132: dhcpv6_universe.encapsulate(&ds, NULL, NULL, client, NULL,
! 2133: client->sent_options, &global_scope,
! 2134: &dhcpv6_universe);
! 2135:
! 2136: /* Append IA's (but don't release temporary addresses). */
! 2137: if (wanted_ia_na &&
! 2138: dhc6_add_ia_na(client, &ds, client->active_lease,
! 2139: DHCPV6_RELEASE) != ISC_R_SUCCESS) {
! 2140: data_string_forget(&ds, MDL);
! 2141: goto release_done;
! 2142: }
! 2143: if (wanted_ia_pd &&
! 2144: dhc6_add_ia_pd(client, &ds, client->active_lease,
! 2145: DHCPV6_RELEASE) != ISC_R_SUCCESS) {
! 2146: data_string_forget(&ds, MDL);
! 2147: goto release_done;
! 2148: }
! 2149:
! 2150: /* Transmit and wait. */
! 2151: log_info("XMT: Release on %s, interval %ld0ms.",
! 2152: client->name ? client->name : client->interface->name,
! 2153: (long int)client->RT);
! 2154:
! 2155: send_ret = send_packet6(client->interface, ds.data, ds.len,
! 2156: &DHCPv6DestAddr);
! 2157: if (send_ret != ds.len) {
! 2158: log_error("dhc6: sendpacket6() sent %d of %d bytes",
! 2159: send_ret, ds.len);
! 2160: }
! 2161:
! 2162: data_string_forget(&ds, MDL);
! 2163:
! 2164: /* Wait RT */
! 2165: tv.tv_sec = cur_tv.tv_sec + client->RT / 100;
! 2166: tv.tv_usec = cur_tv.tv_usec + (client->RT % 100) * 10000;
! 2167: if (tv.tv_usec >= 1000000) {
! 2168: tv.tv_sec += 1;
! 2169: tv.tv_usec -= 1000000;
! 2170: }
! 2171: add_timeout(&tv, do_release6, client, NULL, NULL);
! 2172: dhc6_retrans_advance(client);
! 2173: return;
! 2174:
! 2175: release_done:
! 2176: dhc6_lease_destroy(&client->active_lease, MDL);
! 2177: client->active_lease = NULL;
! 2178: if (stopping_finished())
! 2179: exit(0);
! 2180: }
! 2181:
! 2182: /* status_log() just puts a status code into displayable form and logs it
! 2183: * to info level.
! 2184: */
! 2185: static void
! 2186: status_log(int code, const char *scope, const char *additional, int len)
! 2187: {
! 2188: const char *msg = NULL;
! 2189:
! 2190: switch(code) {
! 2191: case STATUS_Success:
! 2192: msg = "Success";
! 2193: break;
! 2194:
! 2195: case STATUS_UnspecFail:
! 2196: msg = "UnspecFail";
! 2197: break;
! 2198:
! 2199: case STATUS_NoAddrsAvail:
! 2200: msg = "NoAddrsAvail";
! 2201: break;
! 2202:
! 2203: case STATUS_NoBinding:
! 2204: msg = "NoBinding";
! 2205: break;
! 2206:
! 2207: case STATUS_NotOnLink:
! 2208: msg = "NotOnLink";
! 2209: break;
! 2210:
! 2211: case STATUS_UseMulticast:
! 2212: msg = "UseMulticast";
! 2213: break;
! 2214:
! 2215: case STATUS_NoPrefixAvail:
! 2216: msg = "NoPrefixAvail";
! 2217: break;
! 2218:
! 2219: default:
! 2220: msg = "UNKNOWN";
! 2221: break;
! 2222: }
! 2223:
! 2224: if (len > 0)
! 2225: log_info("%s status code %s: %s", scope, msg,
! 2226: print_hex_1(len,
! 2227: (const unsigned char *)additional, 50));
! 2228: else
! 2229: log_info("%s status code %s.", scope, msg);
! 2230: }
! 2231:
! 2232: /* Acquire a status code.
! 2233: */
! 2234: static isc_result_t
! 2235: dhc6_get_status_code(struct option_state *options, unsigned *code,
! 2236: struct data_string *msg)
! 2237: {
! 2238: struct option_cache *oc;
! 2239: struct data_string ds;
! 2240: isc_result_t rval = ISC_R_SUCCESS;
! 2241:
! 2242: if ((options == NULL) || (code == NULL))
! 2243: return ISC_R_INVALIDARG;
! 2244:
! 2245: if ((msg != NULL) && (msg->len != 0))
! 2246: return ISC_R_INVALIDARG;
! 2247:
! 2248: memset(&ds, 0, sizeof(ds));
! 2249:
! 2250: /* Assume success if there is no option. */
! 2251: *code = STATUS_Success;
! 2252:
! 2253: oc = lookup_option(&dhcpv6_universe, options, D6O_STATUS_CODE);
! 2254: if ((oc != NULL) &&
! 2255: evaluate_option_cache(&ds, NULL, NULL, NULL, options,
! 2256: NULL, &global_scope, oc, MDL)) {
! 2257: if (ds.len < 2) {
! 2258: log_error("Invalid status code length %d.", ds.len);
! 2259: rval = ISC_R_FORMERR;
! 2260: } else
! 2261: *code = getUShort(ds.data);
! 2262:
! 2263: if ((msg != NULL) && (ds.len > 2)) {
! 2264: data_string_copy(msg, &ds, MDL);
! 2265: msg->data += 2;
! 2266: msg->len -= 2;
! 2267: }
! 2268:
! 2269: data_string_forget(&ds, MDL);
! 2270: return rval;
! 2271: }
! 2272:
! 2273: return ISC_R_NOTFOUND;
! 2274: }
! 2275:
! 2276: /* Look at status codes in an advertise, and reform the return value.
! 2277: */
! 2278: static isc_result_t
! 2279: dhc6_check_status(isc_result_t rval, struct option_state *options,
! 2280: const char *scope, unsigned *code)
! 2281: {
! 2282: struct data_string msg;
! 2283: isc_result_t status;
! 2284:
! 2285: if ((scope == NULL) || (code == NULL))
! 2286: return ISC_R_INVALIDARG;
! 2287:
! 2288: /* If we don't find a code, we assume success. */
! 2289: *code = STATUS_Success;
! 2290:
! 2291: /* If there is no options cache, then there is no code. */
! 2292: if (options != NULL) {
! 2293: memset(&msg, 0, sizeof(msg));
! 2294: status = dhc6_get_status_code(options, code, &msg);
! 2295:
! 2296: if (status == ISC_R_SUCCESS) {
! 2297: status_log(*code, scope, (char *)msg.data, msg.len);
! 2298: data_string_forget(&msg, MDL);
! 2299:
! 2300: if (*code != STATUS_Success)
! 2301: rval = ISC_R_FAILURE;
! 2302:
! 2303: } else if (status != ISC_R_NOTFOUND)
! 2304: rval = status;
! 2305: }
! 2306:
! 2307: return rval;
! 2308: }
! 2309:
! 2310: /* Look in the packet, any IA's, and any IAADDR's within those IA's to find
! 2311: * status code options that are not SUCCESS.
! 2312: */
! 2313: static isc_result_t
! 2314: dhc6_check_advertise(struct dhc6_lease *lease)
! 2315: {
! 2316: struct dhc6_ia *ia;
! 2317: struct dhc6_addr *addr;
! 2318: isc_result_t rval = ISC_R_SUCCESS;
! 2319: int have_addrs = ISC_FALSE;
! 2320: unsigned code;
! 2321: const char *scope;
! 2322:
! 2323: rval = dhc6_check_status(rval, lease->options, "message", &code);
! 2324:
! 2325: for (ia = lease->bindings ; ia != NULL ; ia = ia->next) {
! 2326: switch (ia->ia_type) {
! 2327: case D6O_IA_NA:
! 2328: scope = "IA_NA";
! 2329: break;
! 2330: case D6O_IA_TA:
! 2331: scope = "IA_TA";
! 2332: break;
! 2333: case D6O_IA_PD:
! 2334: scope = "IA_PD";
! 2335: break;
! 2336: default:
! 2337: log_error("dhc6_check_advertise: no type.");
! 2338: return ISC_R_FAILURE;
! 2339: }
! 2340: rval = dhc6_check_status(rval, ia->options, scope, &code);
! 2341:
! 2342: for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
! 2343: if (ia->ia_type != D6O_IA_PD)
! 2344: scope = "IAADDR";
! 2345: else
! 2346: scope = "IAPREFIX";
! 2347: rval = dhc6_check_status(rval, addr->options,
! 2348: scope, &code);
! 2349: have_addrs = ISC_TRUE;
! 2350: }
! 2351: }
! 2352:
! 2353: if (have_addrs != ISC_TRUE)
! 2354: rval = ISC_R_ADDRNOTAVAIL;
! 2355:
! 2356: return rval;
! 2357: }
! 2358:
! 2359: /* status code <-> action matrix for the client in INIT state
! 2360: * (rapid/commit). Returns always false as no action is defined.
! 2361: */
! 2362: static isc_boolean_t
! 2363: dhc6_init_action(struct client_state *client, isc_result_t *rvalp,
! 2364: unsigned code)
! 2365: {
! 2366: if (rvalp == NULL)
! 2367: log_fatal("Impossible condition at %s:%d.", MDL);
! 2368:
! 2369: if (client == NULL) {
! 2370: *rvalp = ISC_R_INVALIDARG;
! 2371: return ISC_FALSE;
! 2372: }
! 2373:
! 2374: if (*rvalp == ISC_R_SUCCESS)
! 2375: return ISC_FALSE;
! 2376:
! 2377: /* No possible action in any case... */
! 2378: return ISC_FALSE;
! 2379: }
! 2380:
! 2381: /* status code <-> action matrix for the client in SELECT state
! 2382: * (request/reply). Returns true if action was taken (and the
! 2383: * packet should be ignored), or false if no action was taken.
! 2384: */
! 2385: static isc_boolean_t
! 2386: dhc6_select_action(struct client_state *client, isc_result_t *rvalp,
! 2387: unsigned code)
! 2388: {
! 2389: struct dhc6_lease *lease;
! 2390: isc_result_t rval;
! 2391:
! 2392: if (rvalp == NULL)
! 2393: log_fatal("Impossible condition at %s:%d.", MDL);
! 2394:
! 2395: if (client == NULL) {
! 2396: *rvalp = ISC_R_INVALIDARG;
! 2397: return ISC_FALSE;
! 2398: }
! 2399: rval = *rvalp;
! 2400:
! 2401: if (rval == ISC_R_SUCCESS)
! 2402: return ISC_FALSE;
! 2403:
! 2404: switch (code) {
! 2405: /* We may have an earlier failure status code (so no
! 2406: * success rval), and a success code now. This
! 2407: * doesn't upgrade the rval to success, but it does
! 2408: * mean we take no action here.
! 2409: */
! 2410: case STATUS_Success:
! 2411: /* Gimpy server, or possibly an attacker. */
! 2412: case STATUS_NoBinding:
! 2413: case STATUS_UseMulticast:
! 2414: /* Take no action. */
! 2415: return ISC_FALSE;
! 2416:
! 2417: /* If the server can't deal with us, either try the
! 2418: * next advertised server, or continue retrying if there
! 2419: * weren't any.
! 2420: */
! 2421: default:
! 2422: case STATUS_UnspecFail:
! 2423: if (client->advertised_leases != NULL) {
! 2424: dhc6_lease_destroy(&client->selected_lease, MDL);
! 2425: client->selected_lease = NULL;
! 2426:
! 2427: start_selecting6(client);
! 2428:
! 2429: break;
! 2430: } else /* Take no action - continue to retry. */
! 2431: return ISC_FALSE;
! 2432:
! 2433: /* If the server has no addresses, try other servers if
! 2434: * we got some, otherwise go to INIT to hope for more
! 2435: * servers.
! 2436: */
! 2437: case STATUS_NoAddrsAvail:
! 2438: case STATUS_NoPrefixAvail:
! 2439: if (client->state == S_REBOOTING)
! 2440: return ISC_FALSE;
! 2441:
! 2442: if (client->selected_lease == NULL)
! 2443: log_fatal("Impossible case at %s:%d.", MDL);
! 2444:
! 2445: dhc6_lease_destroy(&client->selected_lease, MDL);
! 2446: client->selected_lease = NULL;
! 2447:
! 2448: if (client->advertised_leases != NULL)
! 2449: start_selecting6(client);
! 2450: else
! 2451: start_init6(client);
! 2452:
! 2453: break;
! 2454:
! 2455: /* If we got a NotOnLink from a Confirm, then we're not
! 2456: * on link. Kill the old-active binding and start over.
! 2457: *
! 2458: * If we got a NotOnLink from our Request, something weird
! 2459: * happened. Start over from scratch anyway.
! 2460: */
! 2461: case STATUS_NotOnLink:
! 2462: if (client->state == S_REBOOTING) {
! 2463: if (client->active_lease == NULL)
! 2464: log_fatal("Impossible case at %s:%d.", MDL);
! 2465:
! 2466: dhc6_lease_destroy(&client->active_lease, MDL);
! 2467: } else {
! 2468: if (client->selected_lease == NULL)
! 2469: log_fatal("Impossible case at %s:%d.", MDL);
! 2470:
! 2471: dhc6_lease_destroy(&client->selected_lease, MDL);
! 2472: client->selected_lease = NULL;
! 2473:
! 2474: while (client->advertised_leases != NULL) {
! 2475: lease = client->advertised_leases;
! 2476: client->advertised_leases = lease->next;
! 2477:
! 2478: dhc6_lease_destroy(&lease, MDL);
! 2479: }
! 2480: }
! 2481:
! 2482: start_init6(client);
! 2483: break;
! 2484: }
! 2485:
! 2486: return ISC_TRUE;
! 2487: }
! 2488:
! 2489: static void
! 2490: dhc6_withdraw_lease(struct client_state *client)
! 2491: {
! 2492: struct dhc6_ia *ia;
! 2493: struct dhc6_addr *addr;
! 2494:
! 2495: if ((client == NULL) || (client->active_lease == NULL))
! 2496: return;
! 2497:
! 2498: for (ia = client->active_lease->bindings ; ia != NULL ;
! 2499: ia = ia->next) {
! 2500: for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
! 2501: addr->max_life = addr->preferred_life = 0;
! 2502: }
! 2503: }
! 2504:
! 2505: /* Perform expiry. */
! 2506: do_expire(client);
! 2507: }
! 2508:
! 2509: /* status code <-> action matrix for the client in BOUND state
! 2510: * (request/reply). Returns true if action was taken (and the
! 2511: * packet should be ignored), or false if no action was taken.
! 2512: */
! 2513: static isc_boolean_t
! 2514: dhc6_reply_action(struct client_state *client, isc_result_t *rvalp,
! 2515: unsigned code)
! 2516: {
! 2517: isc_result_t rval;
! 2518:
! 2519: if (rvalp == NULL)
! 2520: log_fatal("Impossible condition at %s:%d.", MDL);
! 2521:
! 2522: if (client == NULL) {
! 2523: *rvalp = ISC_R_INVALIDARG;
! 2524: return ISC_FALSE;
! 2525: }
! 2526: rval = *rvalp;
! 2527:
! 2528: if (rval == ISC_R_SUCCESS)
! 2529: return ISC_FALSE;
! 2530:
! 2531: switch (code) {
! 2532: /* It's possible an earlier status code set rval to a failure
! 2533: * code, and we've encountered a later success.
! 2534: */
! 2535: case STATUS_Success:
! 2536: /* In "refreshes" (where we get replies), we probably
! 2537: * still have a valid lease. So "take no action" and
! 2538: * the upper levels will keep retrying until the lease
! 2539: * expires (or we rebind).
! 2540: */
! 2541: case STATUS_UnspecFail:
! 2542: /* For unknown codes...it's a soft (retryable) error. */
! 2543: default:
! 2544: return ISC_FALSE;
! 2545:
! 2546: /* The server is telling us to use a multicast address, so
! 2547: * we have to delete the unicast option from the active
! 2548: * lease, then allow retransmission to occur normally.
! 2549: * (XXX: It might be preferable in this case to retransmit
! 2550: * sooner than the current interval, but for now we don't.)
! 2551: */
! 2552: case STATUS_UseMulticast:
! 2553: if (client->active_lease != NULL)
! 2554: delete_option(&dhcp_universe,
! 2555: client->active_lease->options,
! 2556: D6O_UNICAST);
! 2557: return ISC_FALSE;
! 2558:
! 2559: /* "When the client receives a NotOnLink status from the
! 2560: * server in response to a Request, the client can either
! 2561: * re-issue the Request without specifying any addresses
! 2562: * or restart the DHCP server discovery process."
! 2563: *
! 2564: * This is strange. If competing server evaluation is
! 2565: * useful (and therefore in the protocol), then why would
! 2566: * a client's first reaction be to request from the same
! 2567: * server on a different link? Surely you'd want to
! 2568: * re-evaluate your server selection.
! 2569: *
! 2570: * Well, I guess that's the answer.
! 2571: */
! 2572: case STATUS_NotOnLink:
! 2573: /* In this case, we need to rescind all current active
! 2574: * bindings (just 'expire' them all normally, if early).
! 2575: * They're no use to us on the wrong link. Then head back
! 2576: * to init, redo server selection and get new addresses.
! 2577: */
! 2578: dhc6_withdraw_lease(client);
! 2579: break;
! 2580:
! 2581: /* "If the status code is NoAddrsAvail, the client has
! 2582: * received no usable addresses in the IA and may choose
! 2583: * to try obtaining addresses for the IA from another
! 2584: * server."
! 2585: */
! 2586: case STATUS_NoAddrsAvail:
! 2587: case STATUS_NoPrefixAvail:
! 2588: /* Head back to init, keeping any active bindings (!). */
! 2589: start_init6(client);
! 2590: break;
! 2591:
! 2592: /* - sends a Request message if the IA contained a Status
! 2593: * Code option with the NoBinding status (and does not
! 2594: * send any additional Renew/Rebind messages)
! 2595: */
! 2596: case STATUS_NoBinding:
! 2597: if (client->advertised_leases != NULL)
! 2598: log_fatal("Impossible condition at %s:%d.", MDL);
! 2599:
! 2600: client->advertised_leases =
! 2601: dhc6_dup_lease(client->active_lease, MDL);
! 2602: start_selecting6(client);
! 2603: break;
! 2604: }
! 2605:
! 2606: return ISC_TRUE;
! 2607: }
! 2608:
! 2609: /* status code <-> action matrix for the client in STOPPED state
! 2610: * (release/decline). Returns true if action was taken (and the
! 2611: * packet should be ignored), or false if no action was taken.
! 2612: * NoBinding is translated into Success.
! 2613: */
! 2614: static isc_boolean_t
! 2615: dhc6_stop_action(struct client_state *client, isc_result_t *rvalp,
! 2616: unsigned code)
! 2617: {
! 2618: isc_result_t rval;
! 2619:
! 2620: if (rvalp == NULL)
! 2621: log_fatal("Impossible condition at %s:%d.", MDL);
! 2622:
! 2623: if (client == NULL) {
! 2624: *rvalp = ISC_R_INVALIDARG;
! 2625: return ISC_FALSE;
! 2626: }
! 2627: rval = *rvalp;
! 2628:
! 2629: if (rval == ISC_R_SUCCESS)
! 2630: return ISC_FALSE;
! 2631:
! 2632: switch (code) {
! 2633: /* It's possible an earlier status code set rval to a failure
! 2634: * code, and we've encountered a later success.
! 2635: */
! 2636: case STATUS_Success:
! 2637: /* For unknown codes...it's a soft (retryable) error. */
! 2638: case STATUS_UnspecFail:
! 2639: default:
! 2640: return ISC_FALSE;
! 2641:
! 2642: /* NoBinding is not an error */
! 2643: case STATUS_NoBinding:
! 2644: if (rval == ISC_R_FAILURE)
! 2645: *rvalp = ISC_R_SUCCESS;
! 2646: return ISC_FALSE;
! 2647:
! 2648: /* Should not happen */
! 2649: case STATUS_NoAddrsAvail:
! 2650: case STATUS_NoPrefixAvail:
! 2651: break;
! 2652:
! 2653: /* Give up on it */
! 2654: case STATUS_NotOnLink:
! 2655: break;
! 2656:
! 2657: /* The server is telling us to use a multicast address, so
! 2658: * we have to delete the unicast option from the active
! 2659: * lease, then allow retransmission to occur normally.
! 2660: * (XXX: It might be preferable in this case to retransmit
! 2661: * sooner than the current interval, but for now we don't.)
! 2662: */
! 2663: case STATUS_UseMulticast:
! 2664: if (client->active_lease != NULL)
! 2665: delete_option(&dhcp_universe,
! 2666: client->active_lease->options,
! 2667: D6O_UNICAST);
! 2668: return ISC_FALSE;
! 2669: }
! 2670:
! 2671: return ISC_TRUE;
! 2672: }
! 2673:
! 2674: /* Look at a new and old lease, and make sure the new information is not
! 2675: * losing us any state.
! 2676: */
! 2677: static isc_result_t
! 2678: dhc6_check_reply(struct client_state *client, struct dhc6_lease *new)
! 2679: {
! 2680: isc_boolean_t (*action)(struct client_state *,
! 2681: isc_result_t *, unsigned);
! 2682: struct dhc6_ia *ia;
! 2683: struct dhc6_addr *addr;
! 2684: isc_result_t rval = ISC_R_SUCCESS;
! 2685: unsigned code;
! 2686: const char *scope;
! 2687: int nscore, sscore;
! 2688:
! 2689: if ((client == NULL) || (new == NULL))
! 2690: return ISC_R_INVALIDARG;
! 2691:
! 2692: switch (client->state) {
! 2693: case S_INIT:
! 2694: action = dhc6_init_action;
! 2695: break;
! 2696:
! 2697: case S_SELECTING:
! 2698: case S_REBOOTING:
! 2699: action = dhc6_select_action;
! 2700: break;
! 2701:
! 2702: case S_RENEWING:
! 2703: case S_REBINDING:
! 2704: action = dhc6_reply_action;
! 2705: break;
! 2706:
! 2707: case S_STOPPED:
! 2708: action = dhc6_stop_action;
! 2709: break;
! 2710:
! 2711: default:
! 2712: log_fatal("Impossible condition at %s:%d.", MDL);
! 2713: return ISC_R_CANCELED;
! 2714: }
! 2715:
! 2716: /* If there is a code to extract, and if there is some
! 2717: * action to take based on that code, then take the action
! 2718: * and do not continue.
! 2719: */
! 2720: rval = dhc6_check_status(rval, new->options, "message", &code);
! 2721: if (action(client, &rval, code))
! 2722: return ISC_R_CANCELED;
! 2723:
! 2724: for (ia = new->bindings ; ia != NULL ; ia = ia->next) {
! 2725: switch (ia->ia_type) {
! 2726: case D6O_IA_NA:
! 2727: scope = "IA_NA";
! 2728: break;
! 2729: case D6O_IA_TA:
! 2730: scope = "IA_TA";
! 2731: break;
! 2732: case D6O_IA_PD:
! 2733: scope = "IA_PD";
! 2734: break;
! 2735: default:
! 2736: log_error("dhc6_check_reply: no type.");
! 2737: return ISC_R_INVALIDARG;
! 2738: }
! 2739: rval = dhc6_check_status(rval, ia->options,
! 2740: scope, &code);
! 2741: if (action(client, &rval, code))
! 2742: return ISC_R_CANCELED;
! 2743:
! 2744: for (addr = ia->addrs ; addr != NULL ;
! 2745: addr = addr->next) {
! 2746: if (ia->ia_type != D6O_IA_PD)
! 2747: scope = "IAADDR";
! 2748: else
! 2749: scope = "IAPREFIX";
! 2750: rval = dhc6_check_status(rval, addr->options,
! 2751: scope, &code);
! 2752: if (action(client, &rval, code))
! 2753: return ISC_R_CANCELED;
! 2754: }
! 2755: }
! 2756:
! 2757: /* A Confirm->Reply is unsuitable for comparison to the old lease. */
! 2758: if (client->state == S_REBOOTING)
! 2759: return rval;
! 2760:
! 2761: /* No old lease in rapid-commit. */
! 2762: if (client->state == S_INIT)
! 2763: return rval;
! 2764:
! 2765: switch (client->state) {
! 2766: case S_SELECTING:
! 2767: /* Compare the new lease with the selected lease to make
! 2768: * sure there is no risky business.
! 2769: */
! 2770: nscore = dhc6_score_lease(client, new);
! 2771: sscore = dhc6_score_lease(client, client->selected_lease);
! 2772: if ((client->advertised_leases != NULL) &&
! 2773: (nscore < (sscore / 2))) {
! 2774: /* XXX: An attacker might reply this way to make
! 2775: * XXX: sure we latch onto their configuration.
! 2776: * XXX: We might want to ignore the packet and
! 2777: * XXX: schedule re-selection at the next timeout?
! 2778: */
! 2779: log_error("PRC: BAIT AND SWITCH detected. Score of "
! 2780: "supplied lease (%d) is substantially "
! 2781: "smaller than the advertised score (%d). "
! 2782: "Trying other servers.",
! 2783: nscore, sscore);
! 2784:
! 2785: dhc6_lease_destroy(&client->selected_lease, MDL);
! 2786: client->selected_lease = NULL;
! 2787:
! 2788: start_selecting6(client);
! 2789:
! 2790: return ISC_R_CANCELED;
! 2791: }
! 2792: break;
! 2793:
! 2794: case S_RENEWING:
! 2795: case S_REBINDING:
! 2796: /* This leaves one RFC3315 status check unimplemented:
! 2797: *
! 2798: * - sends a Renew/Rebind if the IA is not in the Reply
! 2799: * message
! 2800: *
! 2801: * We rely on the scheduling system to note that the IA has
! 2802: * not left Renewal/Rebinding/whatever since it still carries
! 2803: * old times from the last successful binding. So this is
! 2804: * implemented actually, just not explicitly.
! 2805: */
! 2806: break;
! 2807:
! 2808: case S_STOPPED:
! 2809: /* Nothing critical to do at this stage. */
! 2810: break;
! 2811:
! 2812: default:
! 2813: log_fatal("REALLY impossible condition at %s:%d.", MDL);
! 2814: return ISC_R_CANCELED;
! 2815: }
! 2816:
! 2817: return rval;
! 2818: }
! 2819:
! 2820: /* While in init state, we only collect advertisements. If there happens
! 2821: * to be an advertisement with a preference option of 255, that's an
! 2822: * automatic exit. Otherwise, we collect advertisements until our timeout
! 2823: * expires (client->RT).
! 2824: */
! 2825: void
! 2826: init_handler(struct packet *packet, struct client_state *client)
! 2827: {
! 2828: struct dhc6_lease *lease;
! 2829:
! 2830: /* In INIT state, we send solicits, we only expect to get
! 2831: * advertises (rapid commit has its own handler).
! 2832: */
! 2833: if (packet->dhcpv6_msg_type != DHCPV6_ADVERTISE)
! 2834: return;
! 2835:
! 2836: /* RFC3315 section 15.3 validation (same as 15.10 since we
! 2837: * always include a client id).
! 2838: */
! 2839: if (!valid_reply(packet, client)) {
! 2840: log_error("Invalid Advertise - rejecting.");
! 2841: return;
! 2842: }
! 2843:
! 2844: lease = dhc6_leaseify(packet);
! 2845:
! 2846: if (dhc6_check_advertise(lease) != ISC_R_SUCCESS) {
! 2847: log_debug("PRC: Lease failed to satisfy.");
! 2848: dhc6_lease_destroy(&lease, MDL);
! 2849: return;
! 2850: }
! 2851:
! 2852: insert_lease(&client->advertised_leases, lease);
! 2853:
! 2854: /* According to RFC3315 section 17.1.2, the client MUST wait for
! 2855: * the first RT before selecting a lease. But on the 400th RT,
! 2856: * we dont' want to wait the full timeout if we finally get an
! 2857: * advertise. We could probably wait a second, but ohwell,
! 2858: * RFC3315 doesn't say so.
! 2859: *
! 2860: * If the lease is highest possible preference, 255, RFC3315 claims
! 2861: * we should continue immediately even on the first RT. We probably
! 2862: * should not if the advertise contains less than one IA and address.
! 2863: */
! 2864: if ((client->txcount > 1) ||
! 2865: ((lease->pref == 255) &&
! 2866: (dhc6_score_lease(client, lease) > 150))) {
! 2867: log_debug("RCV: Advertisement immediately selected.");
! 2868: cancel_timeout(do_init6, client);
! 2869: start_selecting6(client);
! 2870: } else
! 2871: log_debug("RCV: Advertisement recorded.");
! 2872: }
! 2873:
! 2874: /* info_request_handler() accepts a Reply to an Info-request.
! 2875: */
! 2876: void
! 2877: info_request_handler(struct packet *packet, struct client_state *client)
! 2878: {
! 2879: isc_result_t check_status;
! 2880: unsigned code;
! 2881:
! 2882: if (packet->dhcpv6_msg_type != DHCPV6_REPLY)
! 2883: return;
! 2884:
! 2885: /* RFC3315 section 15.10 validation (same as 15.3 since we
! 2886: * always include a client id).
! 2887: */
! 2888: if (!valid_reply(packet, client)) {
! 2889: log_error("Invalid Reply - rejecting.");
! 2890: return;
! 2891: }
! 2892:
! 2893: check_status = dhc6_check_status(ISC_R_SUCCESS, packet->options,
! 2894: "message", &code);
! 2895: if (check_status != ISC_R_SUCCESS) {
! 2896: /* If no action was taken, but there is an error, then
! 2897: * we wait for a retransmission.
! 2898: */
! 2899: if (check_status != ISC_R_CANCELED)
! 2900: return;
! 2901: }
! 2902:
! 2903: /* We're done retransmitting at this point. */
! 2904: cancel_timeout(do_info_request6, client);
! 2905:
! 2906: /* Action was taken, so now that we've torn down our scheduled
! 2907: * retransmissions, return.
! 2908: */
! 2909: if (check_status == ISC_R_CANCELED)
! 2910: return;
! 2911:
! 2912: /* Cleanup if a previous attempt to go bound failed. */
! 2913: if (client->old_lease != NULL) {
! 2914: dhc6_lease_destroy(&client->old_lease, MDL);
! 2915: client->old_lease = NULL;
! 2916: }
! 2917:
! 2918: /* Cache options in the active_lease. */
! 2919: if (client->active_lease != NULL)
! 2920: client->old_lease = client->active_lease;
! 2921: client->active_lease = dmalloc(sizeof(struct dhc6_lease), MDL);
! 2922: if (client->active_lease == NULL)
! 2923: log_fatal("Out of memory for v6 lease structure.");
! 2924: option_state_reference(&client->active_lease->options,
! 2925: packet->options, MDL);
! 2926:
! 2927: start_informed(client);
! 2928: }
! 2929:
! 2930: /* Specific version of init_handler() for rapid-commit.
! 2931: */
! 2932: void
! 2933: rapid_commit_handler(struct packet *packet, struct client_state *client)
! 2934: {
! 2935: struct dhc6_lease *lease;
! 2936: isc_result_t check_status;
! 2937:
! 2938: /* On ADVERTISE just fall back to the init_handler().
! 2939: */
! 2940: if (packet->dhcpv6_msg_type == DHCPV6_ADVERTISE) {
! 2941: init_handler(packet, client);
! 2942: return;
! 2943: } else if (packet->dhcpv6_msg_type != DHCPV6_REPLY)
! 2944: return;
! 2945:
! 2946: /* RFC3315 section 15.10 validation (same as 15.3 since we
! 2947: * always include a client id).
! 2948: */
! 2949: if (!valid_reply(packet, client)) {
! 2950: log_error("Invalid Reply - rejecting.");
! 2951: return;
! 2952: }
! 2953:
! 2954: /* A rapid-commit option MUST be here. */
! 2955: if (lookup_option(&dhcpv6_universe, packet->options,
! 2956: D6O_RAPID_COMMIT) == 0) {
! 2957: log_error("Reply without Rapid-Commit - rejecting.");
! 2958: return;
! 2959: }
! 2960:
! 2961: lease = dhc6_leaseify(packet);
! 2962:
! 2963: /* This is an out of memory condition...hopefully a temporary
! 2964: * problem. Returning now makes us try to retransmit later.
! 2965: */
! 2966: if (lease == NULL)
! 2967: return;
! 2968:
! 2969: check_status = dhc6_check_reply(client, lease);
! 2970: if (check_status != ISC_R_SUCCESS) {
! 2971: dhc6_lease_destroy(&lease, MDL);
! 2972: return;
! 2973: }
! 2974:
! 2975: /* Jump to the selecting state. */
! 2976: cancel_timeout(do_init6, client);
! 2977: client->state = S_SELECTING;
! 2978:
! 2979: /* Merge any bindings in the active lease (if there is one) into
! 2980: * the new active lease.
! 2981: */
! 2982: dhc6_merge_lease(client->active_lease, lease);
! 2983:
! 2984: /* Cleanup if a previous attempt to go bound failed. */
! 2985: if (client->old_lease != NULL) {
! 2986: dhc6_lease_destroy(&client->old_lease, MDL);
! 2987: client->old_lease = NULL;
! 2988: }
! 2989:
! 2990: /* Make this lease active and BIND to it. */
! 2991: if (client->active_lease != NULL)
! 2992: client->old_lease = client->active_lease;
! 2993: client->active_lease = lease;
! 2994:
! 2995: /* We're done with the ADVERTISEd leases, if any. */
! 2996: while(client->advertised_leases != NULL) {
! 2997: lease = client->advertised_leases;
! 2998: client->advertised_leases = lease->next;
! 2999:
! 3000: dhc6_lease_destroy(&lease, MDL);
! 3001: }
! 3002:
! 3003: start_bound(client);
! 3004: }
! 3005:
! 3006: /* Find the 'best' lease in the cache of advertised leases (usually). From
! 3007: * RFC3315 Section 17.1.3:
! 3008: *
! 3009: * Upon receipt of one or more valid Advertise messages, the client
! 3010: * selects one or more Advertise messages based upon the following
! 3011: * criteria.
! 3012: *
! 3013: * - Those Advertise messages with the highest server preference value
! 3014: * are preferred over all other Advertise messages.
! 3015: *
! 3016: * - Within a group of Advertise messages with the same server
! 3017: * preference value, a client MAY select those servers whose
! 3018: * Advertise messages advertise information of interest to the
! 3019: * client. For example, the client may choose a server that returned
! 3020: * an advertisement with configuration options of interest to the
! 3021: * client.
! 3022: *
! 3023: * - The client MAY choose a less-preferred server if that server has a
! 3024: * better set of advertised parameters, such as the available
! 3025: * addresses advertised in IAs.
! 3026: *
! 3027: * Note that the first and third contradict each other. The third should
! 3028: * probably be taken to mean that the client should prefer answers that
! 3029: * offer bindings, even if that violates the preference rule.
! 3030: *
! 3031: * The above also isn't deterministic where there are ties. So the final
! 3032: * tiebreaker we add, if all other values are equal, is to compare the
! 3033: * server identifiers and to select the numerically lower one.
! 3034: */
! 3035: static struct dhc6_lease *
! 3036: dhc6_best_lease(struct client_state *client, struct dhc6_lease **head)
! 3037: {
! 3038: struct dhc6_lease **rpos, *rval, **candp, *cand;
! 3039: int cscore, rscore;
! 3040:
! 3041: if (head == NULL || *head == NULL)
! 3042: return NULL;
! 3043:
! 3044: rpos = head;
! 3045: rval = *rpos;
! 3046: rscore = dhc6_score_lease(client, rval);
! 3047: candp = &rval->next;
! 3048: cand = *candp;
! 3049:
! 3050: log_debug("PRC: Considering best lease.");
! 3051: log_debug("PRC: X-- Initial candidate %s (s: %d, p: %u).",
! 3052: print_hex_1(rval->server_id.len,
! 3053: rval->server_id.data, 48),
! 3054: rscore, (unsigned)rval->pref);
! 3055:
! 3056: for (; cand != NULL ; candp = &cand->next, cand = *candp) {
! 3057: cscore = dhc6_score_lease(client, cand);
! 3058:
! 3059: log_debug("PRC: X-- Candidate %s (s: %d, p: %u).",
! 3060: print_hex_1(cand->server_id.len,
! 3061: cand->server_id.data, 48),
! 3062: cscore, (unsigned)cand->pref);
! 3063:
! 3064: /* Above you'll find quoted RFC3315 Section 17.1.3.
! 3065: *
! 3066: * The third clause tells us to give up on leases that
! 3067: * have no bindings even if their preference is better.
! 3068: * So where our 'selected' lease's score is less than 150
! 3069: * (1 ia + 1 addr), choose any candidate >= 150.
! 3070: *
! 3071: * The first clause tells us to make preference the primary
! 3072: * deciding factor. So if it's lower, reject, if it's
! 3073: * higher, select.
! 3074: *
! 3075: * The second clause tells us where the preference is
! 3076: * equal, we should use 'our judgement' of what we like
! 3077: * to see in an advertisement primarily.
! 3078: *
! 3079: * But there can still be a tie. To make this deterministic,
! 3080: * we compare the server identifiers and select the binary
! 3081: * lowest.
! 3082: *
! 3083: * Since server id's are unique in this list, there is
! 3084: * no further tie to break.
! 3085: */
! 3086: if ((rscore < 150) && (cscore >= 150)) {
! 3087: log_debug("PRC: | X-- Selected, has bindings.");
! 3088: } else if (cand->pref < rval->pref) {
! 3089: log_debug("PRC: | X-- Rejected, lower preference.");
! 3090: continue;
! 3091: } else if (cand->pref > rval->pref) {
! 3092: log_debug("PRC: | X-- Selected, higher preference.");
! 3093: } else if (cscore > rscore) {
! 3094: log_debug("PRC: | X-- Selected, equal preference, "
! 3095: "higher score.");
! 3096: } else if (cscore < rscore) {
! 3097: log_debug("PRC: | X-- Rejected, equal preference, "
! 3098: "lower score.");
! 3099: continue;
! 3100: } else if ((cand->server_id.len < rval->server_id.len) ||
! 3101: ((cand->server_id.len == rval->server_id.len) &&
! 3102: (memcmp(cand->server_id.data,
! 3103: rval->server_id.data,
! 3104: cand->server_id.len) < 0))) {
! 3105: log_debug("PRC: | X-- Selected, equal preference, "
! 3106: "equal score, binary lesser server ID.");
! 3107: } else {
! 3108: log_debug("PRC: | X-- Rejected, equal preference, "
! 3109: "equal score, binary greater server ID.");
! 3110: continue;
! 3111: }
! 3112:
! 3113: rpos = candp;
! 3114: rval = cand;
! 3115: rscore = cscore;
! 3116: }
! 3117:
! 3118: /* Remove the selected lease from the chain. */
! 3119: *rpos = rval->next;
! 3120:
! 3121: return rval;
! 3122: }
! 3123:
! 3124: /* Select a lease out of the advertised leases and setup state to try and
! 3125: * acquire that lease.
! 3126: */
! 3127: void
! 3128: start_selecting6(struct client_state *client)
! 3129: {
! 3130: struct dhc6_lease *lease;
! 3131:
! 3132: if (client->advertised_leases == NULL) {
! 3133: log_error("Can not enter DHCPv6 SELECTING state with no "
! 3134: "leases to select from!");
! 3135: return;
! 3136: }
! 3137:
! 3138: log_debug("PRC: Selecting best advertised lease.");
! 3139: client->state = S_SELECTING;
! 3140:
! 3141: lease = dhc6_best_lease(client, &client->advertised_leases);
! 3142:
! 3143: if (lease == NULL)
! 3144: log_fatal("Impossible error at %s:%d.", MDL);
! 3145:
! 3146: client->selected_lease = lease;
! 3147:
! 3148: /* Set timers per RFC3315 section 18.1.1. */
! 3149: client->IRT = REQ_TIMEOUT * 100;
! 3150: client->MRT = REQ_MAX_RT * 100;
! 3151: client->MRC = REQ_MAX_RC;
! 3152: client->MRD = 0;
! 3153:
! 3154: dhc6_retrans_init(client);
! 3155:
! 3156: client->v6_handler = reply_handler;
! 3157:
! 3158: /* ("re")transmit the first packet. */
! 3159: do_select6(client);
! 3160: }
! 3161:
! 3162: /* Transmit a Request to select a lease offered in Advertisements. In
! 3163: * the event of failure, either move on to the next-best advertised lease,
! 3164: * or head back to INIT state if there are none.
! 3165: */
! 3166: void
! 3167: do_select6(void *input)
! 3168: {
! 3169: struct client_state *client;
! 3170: struct dhc6_lease *lease;
! 3171: struct data_string ds;
! 3172: struct timeval tv;
! 3173: int send_ret;
! 3174:
! 3175: client = input;
! 3176:
! 3177: /* 'lease' is fewer characters to type. */
! 3178: lease = client->selected_lease;
! 3179: if (lease == NULL || lease->bindings == NULL) {
! 3180: log_error("Illegal to attempt selection without selecting "
! 3181: "a lease.");
! 3182: return;
! 3183: }
! 3184:
! 3185: switch(check_timing6(client, DHCPV6_REQUEST, "Request", lease, &ds)) {
! 3186: case CHK_TIM_MRC_EXCEEDED:
! 3187: case CHK_TIM_MRD_EXCEEDED:
! 3188: log_debug("PRC: Lease %s failed.",
! 3189: print_hex_1(lease->server_id.len,
! 3190: lease->server_id.data, 56));
! 3191:
! 3192: /* Get rid of the lease that timed/counted out. */
! 3193: dhc6_lease_destroy(&lease, MDL);
! 3194: client->selected_lease = NULL;
! 3195:
! 3196: /* If there are more leases great. If not, get more. */
! 3197: if (client->advertised_leases != NULL)
! 3198: start_selecting6(client);
! 3199: else
! 3200: start_init6(client);
! 3201: return;
! 3202: case CHK_TIM_ALLOC_FAILURE:
! 3203: return;
! 3204: case CHK_TIM_SUCCESS:
! 3205: break;
! 3206: }
! 3207:
! 3208: /* Now make a packet that looks suspiciously like the one we
! 3209: * got from the server. But different.
! 3210: *
! 3211: * XXX: I guess IAID is supposed to be something the client
! 3212: * indicates and uses as a key to its internal state. It is
! 3213: * kind of odd to ask the server for IA's whose IAID the client
! 3214: * did not manufacture. We first need a formal dhclient.conf
! 3215: * construct for the iaid, then we can delve into this matter
! 3216: * more properly. In the time being, this will work.
! 3217: */
! 3218:
! 3219: /* Fetch any configured 'sent' options (includes DUID) in wire format.
! 3220: */
! 3221: dhcpv6_universe.encapsulate(&ds, NULL, NULL, client,
! 3222: NULL, client->sent_options, &global_scope,
! 3223: &dhcpv6_universe);
! 3224:
! 3225: /* Now append any IA's, and within them any IAADDR/IAPREFIXs. */
! 3226: if (wanted_ia_na &&
! 3227: dhc6_add_ia_na(client, &ds, lease,
! 3228: DHCPV6_REQUEST) != ISC_R_SUCCESS) {
! 3229: data_string_forget(&ds, MDL);
! 3230: return;
! 3231: }
! 3232: if (wanted_ia_ta &&
! 3233: dhc6_add_ia_ta(client, &ds, lease,
! 3234: DHCPV6_REQUEST) != ISC_R_SUCCESS) {
! 3235: data_string_forget(&ds, MDL);
! 3236: return;
! 3237: }
! 3238: if (wanted_ia_pd &&
! 3239: dhc6_add_ia_pd(client, &ds, lease,
! 3240: DHCPV6_REQUEST) != ISC_R_SUCCESS) {
! 3241: data_string_forget(&ds, MDL);
! 3242: return;
! 3243: }
! 3244:
! 3245: log_info("XMT: Request on %s, interval %ld0ms.",
! 3246: client->name ? client->name : client->interface->name,
! 3247: (long int)client->RT);
! 3248:
! 3249: send_ret = send_packet6(client->interface,
! 3250: ds.data, ds.len, &DHCPv6DestAddr);
! 3251: if (send_ret != ds.len) {
! 3252: log_error("dhc6: send_packet6() sent %d of %d bytes",
! 3253: send_ret, ds.len);
! 3254: }
! 3255:
! 3256: data_string_forget(&ds, MDL);
! 3257:
! 3258: /* Wait RT */
! 3259: tv.tv_sec = cur_tv.tv_sec + client->RT / 100;
! 3260: tv.tv_usec = cur_tv.tv_usec + (client->RT % 100) * 10000;
! 3261: if (tv.tv_usec >= 1000000) {
! 3262: tv.tv_sec += 1;
! 3263: tv.tv_usec -= 1000000;
! 3264: }
! 3265: add_timeout(&tv, do_select6, client, NULL, NULL);
! 3266:
! 3267: dhc6_retrans_advance(client);
! 3268: }
! 3269:
! 3270: /* For each IA_NA in the lease, for each address in the IA_NA,
! 3271: * append that information onto the packet-so-far.
! 3272: */
! 3273: static isc_result_t
! 3274: dhc6_add_ia_na(struct client_state *client, struct data_string *packet,
! 3275: struct dhc6_lease *lease, u_int8_t message)
! 3276: {
! 3277: struct data_string iads;
! 3278: struct data_string addrds;
! 3279: struct dhc6_addr *addr;
! 3280: struct dhc6_ia *ia;
! 3281: isc_result_t rval = ISC_R_SUCCESS;
! 3282: TIME t1, t2;
! 3283:
! 3284: memset(&iads, 0, sizeof(iads));
! 3285: memset(&addrds, 0, sizeof(addrds));
! 3286: for (ia = lease->bindings;
! 3287: ia != NULL && rval == ISC_R_SUCCESS;
! 3288: ia = ia->next) {
! 3289: if (ia->ia_type != D6O_IA_NA)
! 3290: continue;
! 3291:
! 3292: if (!buffer_allocate(&iads.buffer, 12, MDL)) {
! 3293: log_error("Unable to allocate memory for IA_NA.");
! 3294: rval = ISC_R_NOMEMORY;
! 3295: break;
! 3296: }
! 3297:
! 3298: /* Copy the IAID into the packet buffer. */
! 3299: memcpy(iads.buffer->data, ia->iaid, 4);
! 3300: iads.data = iads.buffer->data;
! 3301: iads.len = 12;
! 3302:
! 3303: switch (message) {
! 3304: case DHCPV6_REQUEST:
! 3305: case DHCPV6_RENEW:
! 3306: case DHCPV6_REBIND:
! 3307:
! 3308: t1 = client->config->requested_lease / 2;
! 3309: t2 = t1 + (t1 / 2);
! 3310: #if MAX_TIME > 0xffffffff
! 3311: if (t1 > 0xffffffff)
! 3312: t1 = 0xffffffff;
! 3313: if (t2 > 0xffffffff)
! 3314: t2 = 0xffffffff;
! 3315: #endif
! 3316: putULong(iads.buffer->data + 4, t1);
! 3317: putULong(iads.buffer->data + 8, t2);
! 3318:
! 3319: log_debug("XMT: X-- IA_NA %s",
! 3320: print_hex_1(4, iads.data, 59));
! 3321: log_debug("XMT: | X-- Requested renew +%u",
! 3322: (unsigned) t1);
! 3323: log_debug("XMT: | X-- Requested rebind +%u",
! 3324: (unsigned) t2);
! 3325: break;
! 3326:
! 3327: case DHCPV6_CONFIRM:
! 3328: case DHCPV6_RELEASE:
! 3329: case DHCPV6_DECLINE:
! 3330: /* Set t1 and t2 to zero; server will ignore them */
! 3331: memset(iads.buffer->data + 4, 0, 8);
! 3332: log_debug("XMT: X-- IA_NA %s",
! 3333: print_hex_1(4, iads.buffer->data, 55));
! 3334:
! 3335: break;
! 3336:
! 3337: default:
! 3338: log_fatal("Impossible condition at %s:%d.", MDL);
! 3339: }
! 3340:
! 3341: for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
! 3342: /*
! 3343: * Do not confirm expired addresses, do not request
! 3344: * expired addresses (but we keep them around for
! 3345: * solicit).
! 3346: */
! 3347: if (addr->flags & DHC6_ADDR_EXPIRED)
! 3348: continue;
! 3349:
! 3350: if (addr->address.len != 16) {
! 3351: log_error("Illegal IPv6 address length (%d), "
! 3352: "ignoring. (%s:%d)",
! 3353: addr->address.len, MDL);
! 3354: continue;
! 3355: }
! 3356:
! 3357: if (!buffer_allocate(&addrds.buffer, 24, MDL)) {
! 3358: log_error("Unable to allocate memory for "
! 3359: "IAADDR.");
! 3360: rval = ISC_R_NOMEMORY;
! 3361: break;
! 3362: }
! 3363:
! 3364: addrds.data = addrds.buffer->data;
! 3365: addrds.len = 24;
! 3366:
! 3367: /* Copy the address into the packet buffer. */
! 3368: memcpy(addrds.buffer->data, addr->address.iabuf, 16);
! 3369:
! 3370: /* Copy in additional information as appropriate */
! 3371: switch (message) {
! 3372: case DHCPV6_REQUEST:
! 3373: case DHCPV6_RENEW:
! 3374: case DHCPV6_REBIND:
! 3375: t1 = client->config->requested_lease;
! 3376: t2 = t1 + 300;
! 3377: putULong(addrds.buffer->data + 16, t1);
! 3378: putULong(addrds.buffer->data + 20, t2);
! 3379:
! 3380: log_debug("XMT: | | X-- IAADDR %s",
! 3381: piaddr(addr->address));
! 3382: log_debug("XMT: | | | X-- Preferred "
! 3383: "lifetime +%u", (unsigned)t1);
! 3384: log_debug("XMT: | | | X-- Max lifetime +%u",
! 3385: (unsigned)t2);
! 3386:
! 3387: break;
! 3388:
! 3389: case DHCPV6_CONFIRM:
! 3390: /*
! 3391: * Set preferred and max life to zero,
! 3392: * per 17.1.3.
! 3393: */
! 3394: memset(addrds.buffer->data + 16, 0, 8);
! 3395: log_debug("XMT: | X-- Confirm Address %s",
! 3396: piaddr(addr->address));
! 3397: break;
! 3398:
! 3399: case DHCPV6_RELEASE:
! 3400: /* Preferred and max life are irrelevant */
! 3401: memset(addrds.buffer->data + 16, 0, 8);
! 3402: log_debug("XMT: | X-- Release Address %s",
! 3403: piaddr(addr->address));
! 3404: break;
! 3405:
! 3406: case DHCPV6_DECLINE:
! 3407: /* Preferred and max life are irrelevant */
! 3408: memset(addrds.buffer->data + 16, 0, 8);
! 3409: log_debug("XMT: | X-- Decline Address %s",
! 3410: piaddr(addr->address));
! 3411: break;
! 3412:
! 3413: default:
! 3414: log_fatal("Impossible condition at %s:%d.",
! 3415: MDL);
! 3416: }
! 3417:
! 3418: append_option(&iads, &dhcpv6_universe, iaaddr_option,
! 3419: &addrds);
! 3420: data_string_forget(&addrds, MDL);
! 3421: }
! 3422:
! 3423: /*
! 3424: * It doesn't make sense to make a request without an
! 3425: * address.
! 3426: */
! 3427: if (ia->addrs == NULL) {
! 3428: log_debug("!!!: V IA_NA has no IAADDRs - removed.");
! 3429: rval = ISC_R_FAILURE;
! 3430: } else if (rval == ISC_R_SUCCESS) {
! 3431: log_debug("XMT: V IA_NA appended.");
! 3432: append_option(packet, &dhcpv6_universe, ia_na_option,
! 3433: &iads);
! 3434: }
! 3435:
! 3436: data_string_forget(&iads, MDL);
! 3437: }
! 3438:
! 3439: return rval;
! 3440: }
! 3441:
! 3442: /* For each IA_TA in the lease, for each address in the IA_TA,
! 3443: * append that information onto the packet-so-far.
! 3444: */
! 3445: static isc_result_t
! 3446: dhc6_add_ia_ta(struct client_state *client, struct data_string *packet,
! 3447: struct dhc6_lease *lease, u_int8_t message)
! 3448: {
! 3449: struct data_string iads;
! 3450: struct data_string addrds;
! 3451: struct dhc6_addr *addr;
! 3452: struct dhc6_ia *ia;
! 3453: isc_result_t rval = ISC_R_SUCCESS;
! 3454: TIME t1, t2;
! 3455:
! 3456: memset(&iads, 0, sizeof(iads));
! 3457: memset(&addrds, 0, sizeof(addrds));
! 3458: for (ia = lease->bindings;
! 3459: ia != NULL && rval == ISC_R_SUCCESS;
! 3460: ia = ia->next) {
! 3461: if (ia->ia_type != D6O_IA_TA)
! 3462: continue;
! 3463:
! 3464: if (!buffer_allocate(&iads.buffer, 4, MDL)) {
! 3465: log_error("Unable to allocate memory for IA_TA.");
! 3466: rval = ISC_R_NOMEMORY;
! 3467: break;
! 3468: }
! 3469:
! 3470: /* Copy the IAID into the packet buffer. */
! 3471: memcpy(iads.buffer->data, ia->iaid, 4);
! 3472: iads.data = iads.buffer->data;
! 3473: iads.len = 4;
! 3474:
! 3475: log_debug("XMT: X-- IA_TA %s",
! 3476: print_hex_1(4, iads.buffer->data, 55));
! 3477:
! 3478: for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
! 3479: /*
! 3480: * Do not confirm expired addresses, do not request
! 3481: * expired addresses (but we keep them around for
! 3482: * solicit).
! 3483: */
! 3484: if (addr->flags & DHC6_ADDR_EXPIRED)
! 3485: continue;
! 3486:
! 3487: if (addr->address.len != 16) {
! 3488: log_error("Illegal IPv6 address length (%d), "
! 3489: "ignoring. (%s:%d)",
! 3490: addr->address.len, MDL);
! 3491: continue;
! 3492: }
! 3493:
! 3494: if (!buffer_allocate(&addrds.buffer, 24, MDL)) {
! 3495: log_error("Unable to allocate memory for "
! 3496: "IAADDR.");
! 3497: rval = ISC_R_NOMEMORY;
! 3498: break;
! 3499: }
! 3500:
! 3501: addrds.data = addrds.buffer->data;
! 3502: addrds.len = 24;
! 3503:
! 3504: /* Copy the address into the packet buffer. */
! 3505: memcpy(addrds.buffer->data, addr->address.iabuf, 16);
! 3506:
! 3507: /* Copy in additional information as appropriate */
! 3508: switch (message) {
! 3509: case DHCPV6_REQUEST:
! 3510: case DHCPV6_RENEW:
! 3511: case DHCPV6_REBIND:
! 3512: t1 = client->config->requested_lease;
! 3513: t2 = t1 + 300;
! 3514: putULong(addrds.buffer->data + 16, t1);
! 3515: putULong(addrds.buffer->data + 20, t2);
! 3516:
! 3517: log_debug("XMT: | | X-- IAADDR %s",
! 3518: piaddr(addr->address));
! 3519: log_debug("XMT: | | | X-- Preferred "
! 3520: "lifetime +%u", (unsigned)t1);
! 3521: log_debug("XMT: | | | X-- Max lifetime +%u",
! 3522: (unsigned)t2);
! 3523:
! 3524: break;
! 3525:
! 3526: case DHCPV6_CONFIRM:
! 3527: /*
! 3528: * Set preferred and max life to zero,
! 3529: * per 17.1.3.
! 3530: */
! 3531: memset(addrds.buffer->data + 16, 0, 8);
! 3532: log_debug("XMT: | X-- Confirm Address %s",
! 3533: piaddr(addr->address));
! 3534: break;
! 3535:
! 3536: case DHCPV6_RELEASE:
! 3537: /* Preferred and max life are irrelevant */
! 3538: memset(addrds.buffer->data + 16, 0, 8);
! 3539: log_debug("XMT: | X-- Release Address %s",
! 3540: piaddr(addr->address));
! 3541: break;
! 3542:
! 3543: default:
! 3544: log_fatal("Impossible condition at %s:%d.",
! 3545: MDL);
! 3546: }
! 3547:
! 3548: append_option(&iads, &dhcpv6_universe, iaaddr_option,
! 3549: &addrds);
! 3550: data_string_forget(&addrds, MDL);
! 3551: }
! 3552:
! 3553: /*
! 3554: * It doesn't make sense to make a request without an
! 3555: * address.
! 3556: */
! 3557: if (ia->addrs == NULL) {
! 3558: log_debug("!!!: V IA_TA has no IAADDRs - removed.");
! 3559: rval = ISC_R_FAILURE;
! 3560: } else if (rval == ISC_R_SUCCESS) {
! 3561: log_debug("XMT: V IA_TA appended.");
! 3562: append_option(packet, &dhcpv6_universe, ia_ta_option,
! 3563: &iads);
! 3564: }
! 3565:
! 3566: data_string_forget(&iads, MDL);
! 3567: }
! 3568:
! 3569: return rval;
! 3570: }
! 3571:
! 3572: /* For each IA_PD in the lease, for each prefix in the IA_PD,
! 3573: * append that information onto the packet-so-far.
! 3574: */
! 3575: static isc_result_t
! 3576: dhc6_add_ia_pd(struct client_state *client, struct data_string *packet,
! 3577: struct dhc6_lease *lease, u_int8_t message)
! 3578: {
! 3579: struct data_string iads;
! 3580: struct data_string prefds;
! 3581: struct dhc6_addr *pref;
! 3582: struct dhc6_ia *ia;
! 3583: isc_result_t rval = ISC_R_SUCCESS;
! 3584: TIME t1, t2;
! 3585:
! 3586: memset(&iads, 0, sizeof(iads));
! 3587: memset(&prefds, 0, sizeof(prefds));
! 3588: for (ia = lease->bindings;
! 3589: ia != NULL && rval == ISC_R_SUCCESS;
! 3590: ia = ia->next) {
! 3591: if (ia->ia_type != D6O_IA_PD)
! 3592: continue;
! 3593:
! 3594: if (!buffer_allocate(&iads.buffer, 12, MDL)) {
! 3595: log_error("Unable to allocate memory for IA_PD.");
! 3596: rval = ISC_R_NOMEMORY;
! 3597: break;
! 3598: }
! 3599:
! 3600: /* Copy the IAID into the packet buffer. */
! 3601: memcpy(iads.buffer->data, ia->iaid, 4);
! 3602: iads.data = iads.buffer->data;
! 3603: iads.len = 12;
! 3604:
! 3605: switch (message) {
! 3606: case DHCPV6_REQUEST:
! 3607: case DHCPV6_RENEW:
! 3608: case DHCPV6_REBIND:
! 3609:
! 3610: t1 = client->config->requested_lease / 2;
! 3611: t2 = t1 + (t1 / 2);
! 3612: #if MAX_TIME > 0xffffffff
! 3613: if (t1 > 0xffffffff)
! 3614: t1 = 0xffffffff;
! 3615: if (t2 > 0xffffffff)
! 3616: t2 = 0xffffffff;
! 3617: #endif
! 3618: putULong(iads.buffer->data + 4, t1);
! 3619: putULong(iads.buffer->data + 8, t2);
! 3620:
! 3621: log_debug("XMT: X-- IA_PD %s",
! 3622: print_hex_1(4, iads.data, 59));
! 3623: log_debug("XMT: | X-- Requested renew +%u",
! 3624: (unsigned) t1);
! 3625: log_debug("XMT: | X-- Requested rebind +%u",
! 3626: (unsigned) t2);
! 3627: break;
! 3628:
! 3629: case DHCPV6_RELEASE:
! 3630: /* Set t1 and t2 to zero; server will ignore them */
! 3631: memset(iads.buffer->data + 4, 0, 8);
! 3632: log_debug("XMT: X-- IA_PD %s",
! 3633: print_hex_1(4, iads.buffer->data, 55));
! 3634:
! 3635: break;
! 3636:
! 3637: default:
! 3638: log_fatal("Impossible condition at %s:%d.", MDL);
! 3639: }
! 3640:
! 3641: for (pref = ia->addrs ; pref != NULL ; pref = pref->next) {
! 3642: /*
! 3643: * Do not confirm expired prefixes, do not request
! 3644: * expired prefixes (but we keep them around for
! 3645: * solicit).
! 3646: */
! 3647: if (pref->flags & DHC6_ADDR_EXPIRED)
! 3648: continue;
! 3649:
! 3650: if (pref->address.len != 16) {
! 3651: log_error("Illegal IPv6 prefix "
! 3652: "ignoring. (%s:%d)",
! 3653: MDL);
! 3654: continue;
! 3655: }
! 3656:
! 3657: if (pref->plen == 0) {
! 3658: log_info("Null IPv6 prefix, "
! 3659: "ignoring. (%s:%d)",
! 3660: MDL);
! 3661: }
! 3662:
! 3663: if (!buffer_allocate(&prefds.buffer, 25, MDL)) {
! 3664: log_error("Unable to allocate memory for "
! 3665: "IAPREFIX.");
! 3666: rval = ISC_R_NOMEMORY;
! 3667: break;
! 3668: }
! 3669:
! 3670: prefds.data = prefds.buffer->data;
! 3671: prefds.len = 25;
! 3672:
! 3673: /* Copy the prefix into the packet buffer. */
! 3674: putUChar(prefds.buffer->data + 8, pref->plen);
! 3675: memcpy(prefds.buffer->data + 9,
! 3676: pref->address.iabuf,
! 3677: 16);
! 3678:
! 3679: /* Copy in additional information as appropriate */
! 3680: switch (message) {
! 3681: case DHCPV6_REQUEST:
! 3682: case DHCPV6_RENEW:
! 3683: case DHCPV6_REBIND:
! 3684: t1 = client->config->requested_lease;
! 3685: t2 = t1 + 300;
! 3686: putULong(prefds.buffer->data, t1);
! 3687: putULong(prefds.buffer->data + 4, t2);
! 3688:
! 3689: log_debug("XMT: | | X-- IAPREFIX %s/%u",
! 3690: piaddr(pref->address),
! 3691: (unsigned) pref->plen);
! 3692: log_debug("XMT: | | | X-- Preferred "
! 3693: "lifetime +%u", (unsigned)t1);
! 3694: log_debug("XMT: | | | X-- Max lifetime +%u",
! 3695: (unsigned)t2);
! 3696:
! 3697: break;
! 3698:
! 3699: case DHCPV6_RELEASE:
! 3700: /* Preferred and max life are irrelevant */
! 3701: memset(prefds.buffer->data, 0, 8);
! 3702: log_debug("XMT: | X-- Release Prefix %s/%u",
! 3703: piaddr(pref->address),
! 3704: (unsigned) pref->plen);
! 3705: break;
! 3706:
! 3707: default:
! 3708: log_fatal("Impossible condition at %s:%d.",
! 3709: MDL);
! 3710: }
! 3711:
! 3712: append_option(&iads, &dhcpv6_universe,
! 3713: iaprefix_option, &prefds);
! 3714: data_string_forget(&prefds, MDL);
! 3715: }
! 3716:
! 3717: /*
! 3718: * It doesn't make sense to make a request without an
! 3719: * address.
! 3720: */
! 3721: if (ia->addrs == NULL) {
! 3722: log_debug("!!!: V IA_PD has no IAPREFIXs - removed.");
! 3723: rval = ISC_R_FAILURE;
! 3724: } else if (rval == ISC_R_SUCCESS) {
! 3725: log_debug("XMT: V IA_PD appended.");
! 3726: append_option(packet, &dhcpv6_universe,
! 3727: ia_pd_option, &iads);
! 3728: }
! 3729:
! 3730: data_string_forget(&iads, MDL);
! 3731: }
! 3732:
! 3733: return rval;
! 3734: }
! 3735:
! 3736: /* stopping_finished() checks if there is a remaining work to do.
! 3737: */
! 3738: static isc_boolean_t
! 3739: stopping_finished(void)
! 3740: {
! 3741: struct interface_info *ip;
! 3742: struct client_state *client;
! 3743:
! 3744: for (ip = interfaces; ip; ip = ip -> next) {
! 3745: for (client = ip -> client; client; client = client -> next) {
! 3746: if (client->state != S_STOPPED)
! 3747: return ISC_FALSE;
! 3748: if (client->active_lease != NULL)
! 3749: return ISC_FALSE;
! 3750: }
! 3751: }
! 3752: return ISC_TRUE;
! 3753: }
! 3754:
! 3755: /* reply_handler() accepts a Reply while we're attempting Select or Renew or
! 3756: * Rebind. Basically any Reply packet.
! 3757: */
! 3758: void
! 3759: reply_handler(struct packet *packet, struct client_state *client)
! 3760: {
! 3761: struct dhc6_lease *lease;
! 3762: isc_result_t check_status;
! 3763:
! 3764: if (packet->dhcpv6_msg_type != DHCPV6_REPLY)
! 3765: return;
! 3766:
! 3767: /* RFC3315 section 15.10 validation (same as 15.3 since we
! 3768: * always include a client id).
! 3769: */
! 3770: if (!valid_reply(packet, client)) {
! 3771: log_error("Invalid Reply - rejecting.");
! 3772: return;
! 3773: }
! 3774:
! 3775: lease = dhc6_leaseify(packet);
! 3776:
! 3777: /* This is an out of memory condition...hopefully a temporary
! 3778: * problem. Returning now makes us try to retransmit later.
! 3779: */
! 3780: if (lease == NULL)
! 3781: return;
! 3782:
! 3783: check_status = dhc6_check_reply(client, lease);
! 3784: if (check_status != ISC_R_SUCCESS) {
! 3785: dhc6_lease_destroy(&lease, MDL);
! 3786:
! 3787: /* If no action was taken, but there is an error, then
! 3788: * we wait for a retransmission.
! 3789: */
! 3790: if (check_status != ISC_R_CANCELED)
! 3791: return;
! 3792: }
! 3793:
! 3794: /* We're done retransmitting at this point. */
! 3795: cancel_timeout(do_confirm6, client);
! 3796: cancel_timeout(do_select6, client);
! 3797: cancel_timeout(do_refresh6, client);
! 3798: cancel_timeout(do_release6, client);
! 3799:
! 3800: /* If this is in response to a Release/Decline, clean up and return. */
! 3801: if (client->state == S_STOPPED) {
! 3802: if (client->active_lease == NULL)
! 3803: return;
! 3804:
! 3805: dhc6_lease_destroy(&client->active_lease, MDL);
! 3806: client->active_lease = NULL;
! 3807: /* We should never wait for nothing!? */
! 3808: if (stopping_finished())
! 3809: exit(0);
! 3810: return;
! 3811: }
! 3812:
! 3813: /* Action was taken, so now that we've torn down our scheduled
! 3814: * retransmissions, return.
! 3815: */
! 3816: if (check_status == ISC_R_CANCELED)
! 3817: return;
! 3818:
! 3819: if (client->selected_lease != NULL) {
! 3820: dhc6_lease_destroy(&client->selected_lease, MDL);
! 3821: client->selected_lease = NULL;
! 3822: }
! 3823:
! 3824: /* If this is in response to a confirm, we use the lease we've
! 3825: * already got, not the reply we were sent.
! 3826: */
! 3827: if (client->state == S_REBOOTING) {
! 3828: if (client->active_lease == NULL)
! 3829: log_fatal("Impossible condition at %s:%d.", MDL);
! 3830:
! 3831: dhc6_lease_destroy(&lease, MDL);
! 3832: start_bound(client);
! 3833: return;
! 3834: }
! 3835:
! 3836: /* Merge any bindings in the active lease (if there is one) into
! 3837: * the new active lease.
! 3838: */
! 3839: dhc6_merge_lease(client->active_lease, lease);
! 3840:
! 3841: /* Cleanup if a previous attempt to go bound failed. */
! 3842: if (client->old_lease != NULL) {
! 3843: dhc6_lease_destroy(&client->old_lease, MDL);
! 3844: client->old_lease = NULL;
! 3845: }
! 3846:
! 3847: /* Make this lease active and BIND to it. */
! 3848: if (client->active_lease != NULL)
! 3849: client->old_lease = client->active_lease;
! 3850: client->active_lease = lease;
! 3851:
! 3852: /* We're done with the ADVERTISEd leases, if any. */
! 3853: while(client->advertised_leases != NULL) {
! 3854: lease = client->advertised_leases;
! 3855: client->advertised_leases = lease->next;
! 3856:
! 3857: dhc6_lease_destroy(&lease, MDL);
! 3858: }
! 3859:
! 3860: start_bound(client);
! 3861: }
! 3862:
! 3863: /* DHCPv6 packets are a little sillier than they needed to be - the root
! 3864: * packet contains options, then IA's which contain options, then within
! 3865: * that IAADDR's which contain options.
! 3866: *
! 3867: * To sort this out at dhclient-script time (which fetches config parameters
! 3868: * in environment variables), start_bound() iterates over each IAADDR, and
! 3869: * calls this function to marshall an environment variable set that includes
! 3870: * the most-specific option values related to that IAADDR in particular.
! 3871: *
! 3872: * To achieve this, we load environment variables for the root options space,
! 3873: * then the IA, then the IAADDR. Any duplicate option names will be
! 3874: * over-written by the later versions.
! 3875: */
! 3876: static void
! 3877: dhc6_marshall_values(const char *prefix, struct client_state *client,
! 3878: struct dhc6_lease *lease, struct dhc6_ia *ia,
! 3879: struct dhc6_addr *addr)
! 3880: {
! 3881: /* Option cache contents, in descending order of
! 3882: * scope.
! 3883: */
! 3884: if ((lease != NULL) && (lease->options != NULL))
! 3885: script_write_params6(client, prefix, lease->options);
! 3886: if ((ia != NULL) && (ia->options != NULL))
! 3887: script_write_params6(client, prefix, ia->options);
! 3888: if ((addr != NULL) && (addr->options != NULL))
! 3889: script_write_params6(client, prefix, addr->options);
! 3890:
! 3891: /* addr fields. */
! 3892: if (addr != NULL) {
! 3893: if ((ia != NULL) && (ia->ia_type == D6O_IA_PD)) {
! 3894: client_envadd(client, prefix,
! 3895: "ip6_prefix", "%s/%u",
! 3896: piaddr(addr->address),
! 3897: (unsigned) addr->plen);
! 3898: } else {
! 3899: /* Current practice is that all subnets are /64's, but
! 3900: * some suspect this may not be permanent.
! 3901: */
! 3902: client_envadd(client, prefix, "ip6_prefixlen",
! 3903: "%d", 64);
! 3904: client_envadd(client, prefix, "ip6_address",
! 3905: "%s", piaddr(addr->address));
! 3906: }
! 3907: if ((ia != NULL) && (ia->ia_type == D6O_IA_TA)) {
! 3908: client_envadd(client, prefix,
! 3909: "ip6_type", "temporary");
! 3910: }
! 3911: client_envadd(client, prefix, "life_starts", "%d",
! 3912: (int)(addr->starts));
! 3913: client_envadd(client, prefix, "preferred_life", "%d",
! 3914: (int)(addr->preferred_life));
! 3915: client_envadd(client, prefix, "max_life", "%d",
! 3916: (int)(addr->max_life));
! 3917: }
! 3918:
! 3919: /* ia fields. */
! 3920: if (ia != NULL) {
! 3921: client_envadd(client, prefix, "iaid", "%s",
! 3922: print_hex_1(4, ia->iaid, 12));
! 3923: client_envadd(client, prefix, "starts", "%d",
! 3924: (int)(ia->starts));
! 3925: client_envadd(client, prefix, "renew", "%u", ia->renew);
! 3926: client_envadd(client, prefix, "rebind", "%u", ia->rebind);
! 3927: }
! 3928: }
! 3929:
! 3930: /* Look at where the client's active lease is sitting. If it's looking to
! 3931: * time out on renew, rebind, depref, or expiration, do those things.
! 3932: */
! 3933: static void
! 3934: dhc6_check_times(struct client_state *client)
! 3935: {
! 3936: struct dhc6_lease *lease;
! 3937: struct dhc6_ia *ia;
! 3938: struct dhc6_addr *addr;
! 3939: TIME renew=MAX_TIME, rebind=MAX_TIME, depref=MAX_TIME,
! 3940: lo_expire=MAX_TIME, hi_expire=0, tmp;
! 3941: int has_addrs = ISC_FALSE;
! 3942: struct timeval tv;
! 3943:
! 3944: lease = client->active_lease;
! 3945:
! 3946: /* Bit spammy. We should probably keep record of scheduled
! 3947: * events instead.
! 3948: */
! 3949: cancel_timeout(start_renew6, client);
! 3950: cancel_timeout(start_rebind6, client);
! 3951: cancel_timeout(do_depref, client);
! 3952: cancel_timeout(do_expire, client);
! 3953:
! 3954: for(ia = lease->bindings ; ia != NULL ; ia = ia->next) {
! 3955: TIME this_ia_lo_expire, this_ia_hi_expire, use_expire;
! 3956:
! 3957: this_ia_lo_expire = MAX_TIME;
! 3958: this_ia_hi_expire = 0;
! 3959:
! 3960: for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
! 3961: if(!(addr->flags & DHC6_ADDR_DEPREFFED)) {
! 3962: if (addr->preferred_life == 0xffffffff)
! 3963: tmp = MAX_TIME;
! 3964: else
! 3965: tmp = addr->starts +
! 3966: addr->preferred_life;
! 3967:
! 3968: if (tmp < depref)
! 3969: depref = tmp;
! 3970: }
! 3971:
! 3972: if (!(addr->flags & DHC6_ADDR_EXPIRED)) {
! 3973: /* Find EPOCH-relative expiration. */
! 3974: if (addr->max_life == 0xffffffff)
! 3975: tmp = MAX_TIME;
! 3976: else
! 3977: tmp = addr->starts + addr->max_life;
! 3978:
! 3979: /* Make the times ia->starts relative. */
! 3980: tmp -= ia->starts;
! 3981:
! 3982: if (tmp > this_ia_hi_expire)
! 3983: this_ia_hi_expire = tmp;
! 3984: if (tmp < this_ia_lo_expire)
! 3985: this_ia_lo_expire = tmp;
! 3986:
! 3987: has_addrs = ISC_TRUE;
! 3988: }
! 3989: }
! 3990:
! 3991: /* These times are ia->starts relative. */
! 3992: if (this_ia_lo_expire <= (this_ia_hi_expire / 2))
! 3993: use_expire = this_ia_hi_expire;
! 3994: else
! 3995: use_expire = this_ia_lo_expire;
! 3996:
! 3997: /*
! 3998: * If the auto-selected expiration time is "infinite", or
! 3999: * zero, assert a reasonable default.
! 4000: */
! 4001: if ((use_expire == MAX_TIME) || (use_expire <= 1))
! 4002: use_expire = client->config->requested_lease / 2;
! 4003: else
! 4004: use_expire /= 2;
! 4005:
! 4006: /* Don't renew/rebind temporary addresses. */
! 4007: if (ia->ia_type != D6O_IA_TA) {
! 4008:
! 4009: if (ia->renew == 0) {
! 4010: tmp = ia->starts + use_expire;
! 4011: } else if (ia->renew == 0xffffffff)
! 4012: tmp = MAX_TIME;
! 4013: else
! 4014: tmp = ia->starts + ia->renew;
! 4015:
! 4016: if (tmp < renew)
! 4017: renew = tmp;
! 4018:
! 4019: if (ia->rebind == 0) {
! 4020: /* Set rebind to 3/4 expiration interval. */
! 4021: tmp = ia->starts;
! 4022: tmp += use_expire + (use_expire / 2);
! 4023: } else if (ia->renew == 0xffffffff)
! 4024: tmp = MAX_TIME;
! 4025: else
! 4026: tmp = ia->starts + ia->rebind;
! 4027:
! 4028: if (tmp < rebind)
! 4029: rebind = tmp;
! 4030: }
! 4031:
! 4032: /*
! 4033: * Return expiration ranges to EPOCH relative for event
! 4034: * scheduling (add_timeout()).
! 4035: */
! 4036: this_ia_hi_expire += ia->starts;
! 4037: this_ia_lo_expire += ia->starts;
! 4038:
! 4039: if (this_ia_hi_expire > hi_expire)
! 4040: hi_expire = this_ia_hi_expire;
! 4041: if (this_ia_lo_expire < lo_expire)
! 4042: lo_expire = this_ia_lo_expire;
! 4043: }
! 4044:
! 4045: /* If there are no addresses, give up, go to INIT.
! 4046: * Note that if an address is unexpired with a date in the past,
! 4047: * we're scheduling an expiration event to ocurr in the past. We
! 4048: * could probably optimize this to expire now (but then there's
! 4049: * recursion).
! 4050: *
! 4051: * In the future, we may decide that we're done here, or to
! 4052: * schedule a future request (using 4-pkt info-request model).
! 4053: */
! 4054: if (has_addrs == ISC_FALSE) {
! 4055: dhc6_lease_destroy(&client->active_lease, MDL);
! 4056: client->active_lease = NULL;
! 4057:
! 4058: /* Go back to the beginning. */
! 4059: start_init6(client);
! 4060: return;
! 4061: }
! 4062:
! 4063: switch(client->state) {
! 4064: case S_BOUND:
! 4065: /* We'd like to hit renewing, but if rebinding has already
! 4066: * passed (time warp), head straight there.
! 4067: */
! 4068: if ((rebind > cur_time) && (renew < rebind)) {
! 4069: log_debug("PRC: Renewal event scheduled in %d seconds, "
! 4070: "to run for %u seconds.",
! 4071: (int)(renew - cur_time),
! 4072: (unsigned)(rebind - renew));
! 4073: client->next_MRD = rebind;
! 4074: tv.tv_sec = renew;
! 4075: tv.tv_usec = 0;
! 4076: add_timeout(&tv, start_renew6, client, NULL, NULL);
! 4077:
! 4078: break;
! 4079: }
! 4080: /* FALL THROUGH */
! 4081: case S_RENEWING:
! 4082: /* While actively renewing, MRD is bounded by the time
! 4083: * we stop renewing and start rebinding. This helps us
! 4084: * process the state change on time.
! 4085: */
! 4086: client->MRD = rebind - cur_time;
! 4087: if (rebind != MAX_TIME) {
! 4088: log_debug("PRC: Rebind event scheduled in %d seconds, "
! 4089: "to run for %d seconds.",
! 4090: (int)(rebind - cur_time),
! 4091: (int)(hi_expire - rebind));
! 4092: client->next_MRD = hi_expire;
! 4093: tv.tv_sec = rebind;
! 4094: tv.tv_usec = 0;
! 4095: add_timeout(&tv, start_rebind6, client, NULL, NULL);
! 4096: }
! 4097: break;
! 4098:
! 4099: case S_REBINDING:
! 4100: /* For now, we rebind up until the last lease expires. In
! 4101: * the future, we might want to start SOLICITing when we've
! 4102: * depreffed an address.
! 4103: */
! 4104: client->MRD = hi_expire - cur_time;
! 4105: break;
! 4106:
! 4107: default:
! 4108: log_fatal("Impossible condition at %s:%d.", MDL);
! 4109: }
! 4110:
! 4111: /* Separately, set a time at which we will depref and expire
! 4112: * leases. This might happen with multiple addresses while we
! 4113: * keep trying to refresh.
! 4114: */
! 4115: if (depref != MAX_TIME) {
! 4116: log_debug("PRC: Depreference scheduled in %d seconds.",
! 4117: (int)(depref - cur_time));
! 4118: tv.tv_sec = depref;
! 4119: tv.tv_usec = 0;
! 4120: add_timeout(&tv, do_depref, client, NULL, NULL);
! 4121: }
! 4122: if (lo_expire != MAX_TIME) {
! 4123: log_debug("PRC: Expiration scheduled in %d seconds.",
! 4124: (int)(lo_expire - cur_time));
! 4125: tv.tv_sec = lo_expire;
! 4126: tv.tv_usec = 0;
! 4127: add_timeout(&tv, do_expire, client, NULL, NULL);
! 4128: }
! 4129: }
! 4130:
! 4131: /* In a given IA chain, find the IA with the same type and 'iaid'. */
! 4132: static struct dhc6_ia *
! 4133: find_ia(struct dhc6_ia *head, u_int16_t type, const char *id)
! 4134: {
! 4135: struct dhc6_ia *ia;
! 4136:
! 4137: for (ia = head ; ia != NULL ; ia = ia->next) {
! 4138: if (ia->ia_type != type)
! 4139: continue;
! 4140: if (memcmp(ia->iaid, id, 4) == 0)
! 4141: return ia;
! 4142: }
! 4143:
! 4144: return NULL;
! 4145: }
! 4146:
! 4147: /* In a given address chain, find a matching address. */
! 4148: static struct dhc6_addr *
! 4149: find_addr(struct dhc6_addr *head, struct iaddr *address)
! 4150: {
! 4151: struct dhc6_addr *addr;
! 4152:
! 4153: for (addr = head ; addr != NULL ; addr = addr->next) {
! 4154: if ((addr->address.len == address->len) &&
! 4155: (memcmp(addr->address.iabuf, address->iabuf,
! 4156: address->len) == 0))
! 4157: return addr;
! 4158: }
! 4159:
! 4160: return NULL;
! 4161: }
! 4162:
! 4163: /* In a given prefix chain, find a matching prefix. */
! 4164: static struct dhc6_addr *
! 4165: find_pref(struct dhc6_addr *head, struct iaddr *prefix, u_int8_t plen)
! 4166: {
! 4167: struct dhc6_addr *pref;
! 4168:
! 4169: for (pref = head ; pref != NULL ; pref = pref->next) {
! 4170: if ((pref->address.len == prefix->len) &&
! 4171: (pref->plen == plen) &&
! 4172: (memcmp(pref->address.iabuf, prefix->iabuf,
! 4173: prefix->len) == 0))
! 4174: return pref;
! 4175: }
! 4176:
! 4177: return NULL;
! 4178: }
! 4179:
! 4180: /* Merge the bindings from the source lease into the destination lease
! 4181: * structure, where they are missing. We have to copy the stateful
! 4182: * objects rather than move them over, because later code needs to be
! 4183: * able to compare new versus old if they contain any bindings.
! 4184: */
! 4185: static void
! 4186: dhc6_merge_lease(struct dhc6_lease *src, struct dhc6_lease *dst)
! 4187: {
! 4188: struct dhc6_ia *sia, *dia, *tia;
! 4189: struct dhc6_addr *saddr, *daddr, *taddr;
! 4190: int changes = 0;
! 4191:
! 4192: if ((dst == NULL) || (src == NULL))
! 4193: return;
! 4194:
! 4195: for (sia = src->bindings ; sia != NULL ; sia = sia->next) {
! 4196: dia = find_ia(dst->bindings, sia->ia_type, (char *)sia->iaid);
! 4197:
! 4198: if (dia == NULL) {
! 4199: tia = dhc6_dup_ia(sia, MDL);
! 4200:
! 4201: if (tia == NULL)
! 4202: log_fatal("Out of memory merging lease - "
! 4203: "Unable to continue without losing "
! 4204: "state! (%s:%d)", MDL);
! 4205:
! 4206: /* XXX: consider sorting? */
! 4207: tia->next = dst->bindings;
! 4208: dst->bindings = tia;
! 4209: changes = 1;
! 4210: } else {
! 4211: for (saddr = sia->addrs ; saddr != NULL ;
! 4212: saddr = saddr->next) {
! 4213: if (sia->ia_type != D6O_IA_PD)
! 4214: daddr = find_addr(dia->addrs,
! 4215: &saddr->address);
! 4216: else
! 4217: daddr = find_pref(dia->addrs,
! 4218: &saddr->address,
! 4219: saddr->plen);
! 4220:
! 4221: if (daddr == NULL) {
! 4222: taddr = dhc6_dup_addr(saddr, MDL);
! 4223:
! 4224: if (taddr == NULL)
! 4225: log_fatal("Out of memory "
! 4226: "merging lease - "
! 4227: "Unable to continue "
! 4228: "without losing "
! 4229: "state! (%s:%d)",
! 4230: MDL);
! 4231:
! 4232: /* XXX: consider sorting? */
! 4233: taddr->next = dia->addrs;
! 4234: dia->addrs = taddr;
! 4235: changes = 1;
! 4236: }
! 4237: }
! 4238: }
! 4239: }
! 4240:
! 4241: /* If we made changes, reset the score to 0 so it is recalculated. */
! 4242: if (changes)
! 4243: dst->score = 0;
! 4244: }
! 4245:
! 4246: /* We've either finished selecting or succeeded in Renew or Rebinding our
! 4247: * lease. In all cases we got a Reply. Give dhclient-script a tickle
! 4248: * to inform it about the new values, and then lay in wait for the next
! 4249: * event.
! 4250: */
! 4251: static void
! 4252: start_bound(struct client_state *client)
! 4253: {
! 4254: struct dhc6_ia *ia, *oldia;
! 4255: struct dhc6_addr *addr, *oldaddr;
! 4256: struct dhc6_lease *lease, *old;
! 4257: const char *reason;
! 4258: TIME dns_update_offset = 1;
! 4259:
! 4260: lease = client->active_lease;
! 4261: if (lease == NULL) {
! 4262: log_error("Cannot enter bound state unless an active lease "
! 4263: "is selected.");
! 4264: return;
! 4265: }
! 4266: lease->released = ISC_FALSE;
! 4267: old = client->old_lease;
! 4268:
! 4269: client->v6_handler = bound_handler;
! 4270:
! 4271: switch (client->state) {
! 4272: case S_SELECTING:
! 4273: case S_REBOOTING: /* Pretend we got bound. */
! 4274: reason = "BOUND6";
! 4275: break;
! 4276:
! 4277: case S_RENEWING:
! 4278: reason = "RENEW6";
! 4279: break;
! 4280:
! 4281: case S_REBINDING:
! 4282: reason = "REBIND6";
! 4283: break;
! 4284:
! 4285: default:
! 4286: log_fatal("Impossible condition at %s:%d.", MDL);
! 4287: /* Silence compiler warnings. */
! 4288: return;
! 4289: }
! 4290:
! 4291: log_debug("PRC: Bound to lease %s.",
! 4292: print_hex_1(client->active_lease->server_id.len,
! 4293: client->active_lease->server_id.data, 55));
! 4294: client->state = S_BOUND;
! 4295:
! 4296: write_client6_lease(client, lease, 0, 1);
! 4297:
! 4298: oldia = NULL;
! 4299: for (ia = lease->bindings ; ia != NULL ; ia = ia->next) {
! 4300: if (old != NULL)
! 4301: oldia = find_ia(old->bindings,
! 4302: ia->ia_type,
! 4303: (char *)ia->iaid);
! 4304: else
! 4305: oldia = NULL;
! 4306:
! 4307: for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
! 4308: if (oldia != NULL) {
! 4309: if (ia->ia_type != D6O_IA_PD)
! 4310: oldaddr = find_addr(oldia->addrs,
! 4311: &addr->address);
! 4312: else
! 4313: oldaddr = find_pref(oldia->addrs,
! 4314: &addr->address,
! 4315: addr->plen);
! 4316: } else
! 4317: oldaddr = NULL;
! 4318:
! 4319: if ((oldaddr == NULL) && (ia->ia_type == D6O_IA_NA))
! 4320: dhclient_schedule_updates(client,
! 4321: &addr->address,
! 4322: dns_update_offset++);
! 4323:
! 4324: /* Shell out to setup the new binding. */
! 4325: script_init(client, reason, NULL);
! 4326:
! 4327: if (old != NULL)
! 4328: dhc6_marshall_values("old_", client, old,
! 4329: oldia, oldaddr);
! 4330: dhc6_marshall_values("new_", client, lease, ia, addr);
! 4331:
! 4332: script_go(client);
! 4333: }
! 4334:
! 4335: /* XXX: maybe we should loop on the old values instead? */
! 4336: if (ia->addrs == NULL) {
! 4337: script_init(client, reason, NULL);
! 4338:
! 4339: if (old != NULL)
! 4340: dhc6_marshall_values("old_", client, old,
! 4341: oldia,
! 4342: oldia != NULL ?
! 4343: oldia->addrs : NULL);
! 4344:
! 4345: dhc6_marshall_values("new_", client, lease, ia,
! 4346: NULL);
! 4347:
! 4348: script_go(client);
! 4349: }
! 4350: }
! 4351:
! 4352: /* XXX: maybe we should loop on the old values instead? */
! 4353: if (lease->bindings == NULL) {
! 4354: script_init(client, reason, NULL);
! 4355:
! 4356: if (old != NULL)
! 4357: dhc6_marshall_values("old_", client, old,
! 4358: old->bindings,
! 4359: (old->bindings != NULL) ?
! 4360: old->bindings->addrs : NULL);
! 4361:
! 4362: dhc6_marshall_values("new_", client, lease, NULL, NULL);
! 4363:
! 4364: script_go(client);
! 4365: }
! 4366:
! 4367: go_daemon();
! 4368:
! 4369: if (client->old_lease != NULL) {
! 4370: dhc6_lease_destroy(&client->old_lease, MDL);
! 4371: client->old_lease = NULL;
! 4372: }
! 4373:
! 4374: /* Schedule events. */
! 4375: dhc6_check_times(client);
! 4376: }
! 4377:
! 4378: /* While bound, ignore packets. In the future we'll want to answer
! 4379: * Reconfigure-Request messages and the like.
! 4380: */
! 4381: void
! 4382: bound_handler(struct packet *packet, struct client_state *client)
! 4383: {
! 4384: log_debug("RCV: Input packets are ignored once bound.");
! 4385: }
! 4386:
! 4387: /* start_renew6() gets us all ready to go to start transmitting Renew packets.
! 4388: * Note that client->next_MRD must be set before entering this function -
! 4389: * it must be set to the time at which the client should start Rebinding.
! 4390: */
! 4391: void
! 4392: start_renew6(void *input)
! 4393: {
! 4394: struct client_state *client;
! 4395:
! 4396: client = (struct client_state *)input;
! 4397:
! 4398: log_info("PRC: Renewing lease on %s.",
! 4399: client->name ? client->name : client->interface->name);
! 4400: client->state = S_RENEWING;
! 4401:
! 4402: client->v6_handler = reply_handler;
! 4403:
! 4404: /* Times per RFC3315 section 18.1.3. */
! 4405: client->IRT = REN_TIMEOUT * 100;
! 4406: client->MRT = REN_MAX_RT * 100;
! 4407: client->MRC = 0;
! 4408: /* MRD is special in renew - we need to set it by checking timer
! 4409: * state.
! 4410: */
! 4411: client->MRD = client->next_MRD - cur_time;
! 4412:
! 4413: dhc6_retrans_init(client);
! 4414:
! 4415: client->refresh_type = DHCPV6_RENEW;
! 4416: do_refresh6(client);
! 4417: }
! 4418:
! 4419: /* do_refresh6() transmits one DHCPv6 packet, be it a Renew or Rebind, and
! 4420: * gives the retransmission state a bump for the next time. Note that
! 4421: * client->refresh_type must be set before entering this function.
! 4422: */
! 4423: void
! 4424: do_refresh6(void *input)
! 4425: {
! 4426: struct option_cache *oc;
! 4427: struct sockaddr_in6 unicast, *dest_addr = &DHCPv6DestAddr;
! 4428: struct data_string ds;
! 4429: struct client_state *client;
! 4430: struct dhc6_lease *lease;
! 4431: struct timeval elapsed, tv;
! 4432: int send_ret;
! 4433:
! 4434: client = (struct client_state *)input;
! 4435: memset(&ds, 0, sizeof(ds));
! 4436:
! 4437: lease = client->active_lease;
! 4438: if (lease == NULL) {
! 4439: log_error("Cannot renew without an active binding.");
! 4440: return;
! 4441: }
! 4442:
! 4443: /* Ensure we're emitting a valid message type. */
! 4444: switch (client->refresh_type) {
! 4445: case DHCPV6_RENEW:
! 4446: case DHCPV6_REBIND:
! 4447: break;
! 4448:
! 4449: default:
! 4450: log_fatal("Internal inconsistency (%d) at %s:%d.",
! 4451: client->refresh_type, MDL);
! 4452: }
! 4453:
! 4454: /*
! 4455: * Start_time starts at the first transmission.
! 4456: */
! 4457: if (client->txcount == 0) {
! 4458: client->start_time.tv_sec = cur_tv.tv_sec;
! 4459: client->start_time.tv_usec = cur_tv.tv_usec;
! 4460: }
! 4461:
! 4462: /* elapsed = cur - start */
! 4463: elapsed.tv_sec = cur_tv.tv_sec - client->start_time.tv_sec;
! 4464: elapsed.tv_usec = cur_tv.tv_usec - client->start_time.tv_usec;
! 4465: if (elapsed.tv_usec < 0) {
! 4466: elapsed.tv_sec -= 1;
! 4467: elapsed.tv_usec += 1000000;
! 4468: }
! 4469: if (((client->MRC != 0) && (client->txcount > client->MRC)) ||
! 4470: ((client->MRD != 0) && (elapsed.tv_sec >= client->MRD))) {
! 4471: /* We're done. Move on to the next phase, if any. */
! 4472: dhc6_check_times(client);
! 4473: return;
! 4474: }
! 4475:
! 4476: /*
! 4477: * Check whether the server has sent a unicast option; if so, we can
! 4478: * use the address it specified for RENEWs.
! 4479: */
! 4480: oc = lookup_option(&dhcpv6_universe, lease->options, D6O_UNICAST);
! 4481: if (oc && evaluate_option_cache(&ds, NULL, NULL, NULL,
! 4482: lease->options, NULL, &global_scope,
! 4483: oc, MDL)) {
! 4484: if (ds.len < 16) {
! 4485: log_error("Invalid unicast option length %d.", ds.len);
! 4486: } else {
! 4487: memset(&unicast, 0, sizeof(DHCPv6DestAddr));
! 4488: unicast.sin6_family = AF_INET6;
! 4489: unicast.sin6_port = remote_port;
! 4490: memcpy(&unicast.sin6_addr, ds.data, 16);
! 4491: if (client->refresh_type == DHCPV6_RENEW) {
! 4492: dest_addr = &unicast;
! 4493: }
! 4494: }
! 4495:
! 4496: data_string_forget(&ds, MDL);
! 4497: }
! 4498:
! 4499: /* Commence forming a renew packet. */
! 4500: memset(&ds, 0, sizeof(ds));
! 4501: if (!buffer_allocate(&ds.buffer, 4, MDL)) {
! 4502: log_error("Unable to allocate memory for packet.");
! 4503: return;
! 4504: }
! 4505: ds.data = ds.buffer->data;
! 4506: ds.len = 4;
! 4507:
! 4508: ds.buffer->data[0] = client->refresh_type;
! 4509: memcpy(ds.buffer->data + 1, client->dhcpv6_transaction_id, 3);
! 4510:
! 4511: /* Form an elapsed option. */
! 4512: /* Maximum value is 65535 1/100s coded as 0xffff. */
! 4513: if ((elapsed.tv_sec < 0) || (elapsed.tv_sec > 655) ||
! 4514: ((elapsed.tv_sec == 655) && (elapsed.tv_usec > 350000))) {
! 4515: client->elapsed = 0xffff;
! 4516: } else {
! 4517: client->elapsed = elapsed.tv_sec * 100;
! 4518: client->elapsed += elapsed.tv_usec / 10000;
! 4519: }
! 4520:
! 4521: if (client->elapsed == 0)
! 4522: log_debug("XMT: Forming %s, 0 ms elapsed.",
! 4523: dhcpv6_type_names[client->refresh_type]);
! 4524: else
! 4525: log_debug("XMT: Forming %s, %u0 ms elapsed.",
! 4526: dhcpv6_type_names[client->refresh_type],
! 4527: (unsigned)client->elapsed);
! 4528:
! 4529: client->elapsed = htons(client->elapsed);
! 4530:
! 4531: make_client6_options(client, &client->sent_options, lease,
! 4532: client->refresh_type);
! 4533:
! 4534: /* Put in any options from the sent cache. */
! 4535: dhcpv6_universe.encapsulate(&ds, NULL, NULL, client, NULL,
! 4536: client->sent_options, &global_scope,
! 4537: &dhcpv6_universe);
! 4538:
! 4539: /* Append IA's */
! 4540: if (wanted_ia_na &&
! 4541: dhc6_add_ia_na(client, &ds, lease,
! 4542: client->refresh_type) != ISC_R_SUCCESS) {
! 4543: data_string_forget(&ds, MDL);
! 4544: return;
! 4545: }
! 4546: if (wanted_ia_pd &&
! 4547: dhc6_add_ia_pd(client, &ds, lease,
! 4548: client->refresh_type) != ISC_R_SUCCESS) {
! 4549: data_string_forget(&ds, MDL);
! 4550: return;
! 4551: }
! 4552:
! 4553: log_info("XMT: %s on %s, interval %ld0ms.",
! 4554: dhcpv6_type_names[client->refresh_type],
! 4555: client->name ? client->name : client->interface->name,
! 4556: (long int)client->RT);
! 4557:
! 4558: send_ret = send_packet6(client->interface, ds.data, ds.len, dest_addr);
! 4559:
! 4560: if (send_ret != ds.len) {
! 4561: log_error("dhc6: send_packet6() sent %d of %d bytes",
! 4562: send_ret, ds.len);
! 4563: }
! 4564:
! 4565: data_string_forget(&ds, MDL);
! 4566:
! 4567: /* Wait RT */
! 4568: tv.tv_sec = cur_tv.tv_sec + client->RT / 100;
! 4569: tv.tv_usec = cur_tv.tv_usec + (client->RT % 100) * 10000;
! 4570: if (tv.tv_usec >= 1000000) {
! 4571: tv.tv_sec += 1;
! 4572: tv.tv_usec -= 1000000;
! 4573: }
! 4574: add_timeout(&tv, do_refresh6, client, NULL, NULL);
! 4575:
! 4576: dhc6_retrans_advance(client);
! 4577: }
! 4578:
! 4579: /* start_rebind6() gets us all set up to go and rebind a lease. Note that
! 4580: * client->next_MRD must be set before entering this function. In this case,
! 4581: * MRD must be set to the maximum time any address in the packet will
! 4582: * expire.
! 4583: */
! 4584: void
! 4585: start_rebind6(void *input)
! 4586: {
! 4587: struct client_state *client;
! 4588:
! 4589: client = (struct client_state *)input;
! 4590:
! 4591: log_info("PRC: Rebinding lease on %s.",
! 4592: client->name ? client->name : client->interface->name);
! 4593: client->state = S_REBINDING;
! 4594:
! 4595: client->v6_handler = reply_handler;
! 4596:
! 4597: /* Times per RFC3315 section 18.1.4. */
! 4598: client->IRT = REB_TIMEOUT * 100;
! 4599: client->MRT = REB_MAX_RT * 100;
! 4600: client->MRC = 0;
! 4601: /* MRD is special in rebind - it's determined by the timer
! 4602: * state.
! 4603: */
! 4604: client->MRD = client->next_MRD - cur_time;
! 4605:
! 4606: dhc6_retrans_init(client);
! 4607:
! 4608: client->refresh_type = DHCPV6_REBIND;
! 4609: do_refresh6(client);
! 4610: }
! 4611:
! 4612: /* do_depref() runs through a given lease's addresses, for each that has
! 4613: * not yet been depreffed, shells out to the dhclient-script to inform it
! 4614: * of the status change. The dhclient-script should then do...something...
! 4615: * to encourage applications to move off the address and onto one of the
! 4616: * remaining 'preferred' addresses.
! 4617: */
! 4618: void
! 4619: do_depref(void *input)
! 4620: {
! 4621: struct client_state *client;
! 4622: struct dhc6_lease *lease;
! 4623: struct dhc6_ia *ia;
! 4624: struct dhc6_addr *addr;
! 4625:
! 4626: client = (struct client_state *)input;
! 4627:
! 4628: lease = client->active_lease;
! 4629: if (lease == NULL)
! 4630: return;
! 4631:
! 4632: for (ia = lease->bindings ; ia != NULL ; ia = ia->next) {
! 4633: for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
! 4634: if (addr->flags & DHC6_ADDR_DEPREFFED)
! 4635: continue;
! 4636:
! 4637: if (addr->starts + addr->preferred_life <= cur_time) {
! 4638: script_init(client, "DEPREF6", NULL);
! 4639: dhc6_marshall_values("cur_", client, lease,
! 4640: ia, addr);
! 4641: script_go(client);
! 4642:
! 4643: addr->flags |= DHC6_ADDR_DEPREFFED;
! 4644:
! 4645: if (ia->ia_type != D6O_IA_PD)
! 4646: log_info("PRC: Address %s depreferred.",
! 4647: piaddr(addr->address));
! 4648: else
! 4649: log_info("PRC: Prefix %s/%u depreferred.",
! 4650: piaddr(addr->address),
! 4651: (unsigned) addr->plen);
! 4652:
! 4653: /* Remove DDNS bindings at depref time. */
! 4654: if ((ia->ia_type == D6O_IA_NA) &&
! 4655: client->config->do_forward_update)
! 4656: client_dns_update(client, 0, 0,
! 4657: &addr->address);
! 4658: }
! 4659: }
! 4660: }
! 4661:
! 4662: dhc6_check_times(client);
! 4663: }
! 4664:
! 4665: /* do_expire() searches through all the addresses on a given lease, and
! 4666: * expires/removes any addresses that are no longer valid.
! 4667: */
! 4668: void
! 4669: do_expire(void *input)
! 4670: {
! 4671: struct client_state *client;
! 4672: struct dhc6_lease *lease;
! 4673: struct dhc6_ia *ia;
! 4674: struct dhc6_addr *addr;
! 4675: int has_addrs = ISC_FALSE;
! 4676:
! 4677: client = (struct client_state *)input;
! 4678:
! 4679: lease = client->active_lease;
! 4680: if (lease == NULL)
! 4681: return;
! 4682:
! 4683: for (ia = lease->bindings ; ia != NULL ; ia = ia->next) {
! 4684: for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
! 4685: if (addr->flags & DHC6_ADDR_EXPIRED)
! 4686: continue;
! 4687:
! 4688: if (addr->starts + addr->max_life <= cur_time) {
! 4689: script_init(client, "EXPIRE6", NULL);
! 4690: dhc6_marshall_values("old_", client, lease,
! 4691: ia, addr);
! 4692: script_go(client);
! 4693:
! 4694: addr->flags |= DHC6_ADDR_EXPIRED;
! 4695:
! 4696: if (ia->ia_type != D6O_IA_PD)
! 4697: log_info("PRC: Address %s expired.",
! 4698: piaddr(addr->address));
! 4699: else
! 4700: log_info("PRC: Prefix %s/%u expired.",
! 4701: piaddr(addr->address),
! 4702: (unsigned) addr->plen);
! 4703:
! 4704: /* We remove DNS records at depref time, but
! 4705: * it is possible that we might get here
! 4706: * without depreffing.
! 4707: */
! 4708: if ((ia->ia_type == D6O_IA_NA) &&
! 4709: client->config->do_forward_update &&
! 4710: !(addr->flags & DHC6_ADDR_DEPREFFED))
! 4711: client_dns_update(client, 0, 0,
! 4712: &addr->address);
! 4713:
! 4714: continue;
! 4715: }
! 4716:
! 4717: has_addrs = ISC_TRUE;
! 4718: }
! 4719: }
! 4720:
! 4721: /* Clean up empty leases. */
! 4722: if (has_addrs == ISC_FALSE) {
! 4723: log_info("PRC: Bound lease is devoid of active addresses."
! 4724: " Re-initializing.");
! 4725:
! 4726: dhc6_lease_destroy(&lease, MDL);
! 4727: client->active_lease = NULL;
! 4728:
! 4729: start_init6(client);
! 4730: return;
! 4731: }
! 4732:
! 4733: /* Schedule the next run through. */
! 4734: dhc6_check_times(client);
! 4735: }
! 4736:
! 4737: /*
! 4738: * Run client script to unconfigure interface.
! 4739: * Called with reason STOP6 when dhclient -x is run, or with reason
! 4740: * RELEASE6 when server has replied to a Release message.
! 4741: * Stateless is a special case.
! 4742: */
! 4743: void
! 4744: unconfigure6(struct client_state *client, const char *reason)
! 4745: {
! 4746: struct dhc6_ia *ia;
! 4747: struct dhc6_addr *addr;
! 4748:
! 4749: if (stateless) {
! 4750: script_init(client, reason, NULL);
! 4751: if (client->active_lease != NULL)
! 4752: script_write_params6(client, "old_",
! 4753: client->active_lease->options);
! 4754: script_go(client);
! 4755: return;
! 4756: }
! 4757:
! 4758: if (client->active_lease == NULL)
! 4759: return;
! 4760:
! 4761: for (ia = client->active_lease->bindings ; ia != NULL ; ia = ia->next) {
! 4762: if (ia->ia_type == D6O_IA_TA)
! 4763: continue;
! 4764:
! 4765: for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
! 4766: script_init(client, reason, NULL);
! 4767: dhc6_marshall_values("old_", client,
! 4768: client->active_lease, ia, addr);
! 4769: script_go(client);
! 4770:
! 4771: if ((ia->ia_type == D6O_IA_NA) &&
! 4772: client->config->do_forward_update)
! 4773: client_dns_update(client, 0, 0, &addr->address);
! 4774: }
! 4775: }
! 4776: }
! 4777:
! 4778: void
! 4779: refresh_info_request6(void *input)
! 4780: {
! 4781: struct client_state *client;
! 4782:
! 4783: client = (struct client_state *)input;
! 4784: start_info_request6(client);
! 4785: }
! 4786:
! 4787: /* Timeout for Information-Request (using the IRT option).
! 4788: */
! 4789: static void
! 4790: dhc6_check_irt(struct client_state *client)
! 4791: {
! 4792: struct option **req;
! 4793: struct option_cache *oc;
! 4794: TIME expire = MAX_TIME;
! 4795: struct timeval tv;
! 4796: int i;
! 4797: isc_boolean_t found = ISC_FALSE;
! 4798:
! 4799: cancel_timeout(refresh_info_request6, client);
! 4800:
! 4801: req = client->config->requested_options;
! 4802: for (i = 0; req[i] != NULL; i++) {
! 4803: if (req[i] == irt_option) {
! 4804: found = ISC_TRUE;
! 4805: break;
! 4806: }
! 4807: }
! 4808: /* Simply return gives a endless loop waiting for nothing. */
! 4809: if (!found)
! 4810: exit(0);
! 4811:
! 4812: oc = lookup_option(&dhcpv6_universe, client->active_lease->options,
! 4813: D6O_INFORMATION_REFRESH_TIME);
! 4814: if (oc != NULL) {
! 4815: struct data_string irt;
! 4816:
! 4817: memset(&irt, 0, sizeof(irt));
! 4818: if (!evaluate_option_cache(&irt, NULL, NULL, client,
! 4819: client->active_lease->options,
! 4820: NULL, &global_scope, oc, MDL) ||
! 4821: (irt.len < 4)) {
! 4822: log_error("Can't evaluate IRT.");
! 4823: } else {
! 4824: expire = getULong(irt.data);
! 4825: if (expire < IRT_MINIMUM)
! 4826: expire = IRT_MINIMUM;
! 4827: if (expire == 0xffffffff)
! 4828: expire = MAX_TIME;
! 4829: }
! 4830: data_string_forget(&irt, MDL);
! 4831: } else
! 4832: expire = IRT_DEFAULT;
! 4833:
! 4834: if (expire != MAX_TIME) {
! 4835: log_debug("PRC: Refresh event scheduled in %u seconds.",
! 4836: (unsigned) expire);
! 4837: tv.tv_sec = cur_time + expire;
! 4838: tv.tv_usec = 0;
! 4839: add_timeout(&tv, refresh_info_request6, client, NULL, NULL);
! 4840: }
! 4841: }
! 4842:
! 4843: /* We got a Reply. Give dhclient-script a tickle to inform it about
! 4844: * the new values, and then lay in wait for the next event.
! 4845: */
! 4846: static void
! 4847: start_informed(struct client_state *client)
! 4848: {
! 4849: client->v6_handler = informed_handler;
! 4850:
! 4851: log_debug("PRC: Done.");
! 4852:
! 4853: client->state = S_BOUND;
! 4854:
! 4855: script_init(client, "RENEW6", NULL);
! 4856: if (client->old_lease != NULL)
! 4857: script_write_params6(client, "old_",
! 4858: client->old_lease->options);
! 4859: script_write_params6(client, "new_", client->active_lease->options);
! 4860: script_go(client);
! 4861:
! 4862: go_daemon();
! 4863:
! 4864: if (client->old_lease != NULL) {
! 4865: dhc6_lease_destroy(&client->old_lease, MDL);
! 4866: client->old_lease = NULL;
! 4867: }
! 4868:
! 4869: /* Schedule events. */
! 4870: dhc6_check_irt(client);
! 4871: }
! 4872:
! 4873: /* While informed, ignore packets.
! 4874: */
! 4875: void
! 4876: informed_handler(struct packet *packet, struct client_state *client)
! 4877: {
! 4878: log_debug("RCV: Input packets are ignored once bound.");
! 4879: }
! 4880:
! 4881: /* make_client6_options() fetches option caches relevant to the client's
! 4882: * scope and places them into the sent_options cache. This cache is later
! 4883: * used to populate DHCPv6 output packets with options.
! 4884: */
! 4885: static void
! 4886: make_client6_options(struct client_state *client, struct option_state **op,
! 4887: struct dhc6_lease *lease, u_int8_t message)
! 4888: {
! 4889: struct option_cache *oc;
! 4890: struct option **req;
! 4891: struct buffer *buffer;
! 4892: int buflen, i, oro_len;
! 4893:
! 4894: if ((op == NULL) || (client == NULL))
! 4895: return;
! 4896:
! 4897: if (*op)
! 4898: option_state_dereference(op, MDL);
! 4899:
! 4900: /* Create a cache to carry options to transmission. */
! 4901: option_state_allocate(op, MDL);
! 4902:
! 4903: /* Create and store an 'elapsed time' option in the cache. */
! 4904: oc = NULL;
! 4905: if (option_cache_allocate(&oc, MDL)) {
! 4906: const unsigned char *cdata;
! 4907:
! 4908: cdata = (unsigned char *)&client->elapsed;
! 4909:
! 4910: if (make_const_data(&oc->expression, cdata, 2, 0, 0, MDL)) {
! 4911: option_reference(&oc->option, elapsed_option, MDL);
! 4912: save_option(&dhcpv6_universe, *op, oc);
! 4913: }
! 4914:
! 4915: option_cache_dereference(&oc, MDL);
! 4916: }
! 4917:
! 4918: /* Bring in any configured options to send. */
! 4919: if (client->config->on_transmission)
! 4920: execute_statements_in_scope(NULL, NULL, NULL, client,
! 4921: lease ? lease->options : NULL,
! 4922: *op, &global_scope,
! 4923: client->config->on_transmission,
! 4924: NULL);
! 4925:
! 4926: /* Rapid-commit is only for SOLICITs. */
! 4927: if (message != DHCPV6_SOLICIT)
! 4928: delete_option(&dhcpv6_universe, *op, D6O_RAPID_COMMIT);
! 4929:
! 4930: /* See if the user configured a DUID in a relevant scope. If not,
! 4931: * introduce our default manufactured id.
! 4932: */
! 4933: if ((oc = lookup_option(&dhcpv6_universe, *op,
! 4934: D6O_CLIENTID)) == NULL) {
! 4935: if (!option_cache(&oc, &default_duid, NULL, clientid_option,
! 4936: MDL))
! 4937: log_fatal("Failure assembling a DUID.");
! 4938:
! 4939: save_option(&dhcpv6_universe, *op, oc);
! 4940: option_cache_dereference(&oc, MDL);
! 4941: }
! 4942:
! 4943: /* In cases where we're responding to a single server, put the
! 4944: * server's id in the response.
! 4945: *
! 4946: * Note that lease is NULL for SOLICIT or INFO request messages,
! 4947: * and otherwise MUST be present.
! 4948: */
! 4949: if (lease == NULL) {
! 4950: if ((message != DHCPV6_SOLICIT) &&
! 4951: (message != DHCPV6_INFORMATION_REQUEST))
! 4952: log_fatal("Impossible condition at %s:%d.", MDL);
! 4953: } else if ((message != DHCPV6_REBIND) &&
! 4954: (message != DHCPV6_CONFIRM)) {
! 4955: oc = lookup_option(&dhcpv6_universe, lease->options,
! 4956: D6O_SERVERID);
! 4957: if (oc != NULL)
! 4958: save_option(&dhcpv6_universe, *op, oc);
! 4959: }
! 4960:
! 4961: /* 'send dhcp6.oro foo;' syntax we used in 4.0.0a1/a2 has been
! 4962: * deprecated by adjustments to the 'request' syntax also used for
! 4963: * DHCPv4.
! 4964: */
! 4965: if (lookup_option(&dhcpv6_universe, *op, D6O_ORO) != NULL)
! 4966: log_error("'send dhcp6.oro' syntax is deprecated, please "
! 4967: "use the 'request' syntax (\"man dhclient.conf\").");
! 4968:
! 4969: /* Construct and store an ORO (Option Request Option). It is a
! 4970: * fatal error to fail to send an ORO (of at least zero length).
! 4971: *
! 4972: * Discussion: RFC3315 appears to be inconsistent in its statements
! 4973: * of whether or not the ORO is mandatory. In section 18.1.1
! 4974: * ("Creation and Transmission of Request Messages"):
! 4975: *
! 4976: * The client MUST include an Option Request option (see section
! 4977: * 22.7) to indicate the options the client is interested in
! 4978: * receiving. The client MAY include options with data values as
! 4979: * hints to the server about parameter values the client would like
! 4980: * to have returned.
! 4981: *
! 4982: * This MUST is missing from the creation/transmission of other
! 4983: * messages (such as Renew and Rebind), and the section 22.7 ("Option
! 4984: * Request Option" format and definition):
! 4985: *
! 4986: * A client MAY include an Option Request option in a Solicit,
! 4987: * Request, Renew, Rebind, Confirm or Information-request message to
! 4988: * inform the server about options the client wants the server to
! 4989: * send to the client. A server MAY include an Option Request
! 4990: * option in a Reconfigure option to indicate which options the
! 4991: * client should request from the server.
! 4992: *
! 4993: * seems to relax the requirement from MUST to MAY (and still other
! 4994: * language in RFC3315 supports this).
! 4995: *
! 4996: * In lieu of a clarification of RFC3315, we will conform with the
! 4997: * MUST. Instead of an absent ORO, we will if there are no options
! 4998: * to request supply an empty ORO. Theoretically, an absent ORO is
! 4999: * difficult to interpret (does the client want all options or no
! 5000: * options?). A zero-length ORO is intuitively clear: requesting
! 5001: * nothing.
! 5002: */
! 5003: buffer = NULL;
! 5004: oro_len = 0;
! 5005: buflen = 32;
! 5006: if (!buffer_allocate(&buffer, buflen, MDL))
! 5007: log_fatal("Out of memory constructing DHCPv6 ORO.");
! 5008: req = client->config->requested_options;
! 5009: if (req != NULL) {
! 5010: for (i = 0 ; req[i] != NULL ; i++) {
! 5011: if (buflen == oro_len) {
! 5012: struct buffer *tmpbuf = NULL;
! 5013:
! 5014: buflen += 32;
! 5015:
! 5016: /* Shell game. */
! 5017: buffer_reference(&tmpbuf, buffer, MDL);
! 5018: buffer_dereference(&buffer, MDL);
! 5019:
! 5020: if (!buffer_allocate(&buffer, buflen, MDL))
! 5021: log_fatal("Out of memory resizing "
! 5022: "DHCPv6 ORO buffer.");
! 5023:
! 5024: memcpy(buffer->data, tmpbuf->data, oro_len);
! 5025:
! 5026: buffer_dereference(&tmpbuf, MDL);
! 5027: }
! 5028:
! 5029: if (req[i]->universe == &dhcpv6_universe) {
! 5030: /* Append the code to the ORO. */
! 5031: putUShort(buffer->data + oro_len,
! 5032: req[i]->code);
! 5033: oro_len += 2;
! 5034: }
! 5035: }
! 5036: }
! 5037:
! 5038: oc = NULL;
! 5039: if (make_const_option_cache(&oc, &buffer, NULL, oro_len,
! 5040: oro_option, MDL)) {
! 5041: save_option(&dhcpv6_universe, *op, oc);
! 5042: } else {
! 5043: log_fatal("Unable to create ORO option cache.");
! 5044: }
! 5045:
! 5046: /*
! 5047: * Note: make_const_option_cache() consumes the buffer, we do not
! 5048: * need to dereference it (XXX).
! 5049: */
! 5050: option_cache_dereference(&oc, MDL);
! 5051: }
! 5052:
! 5053: /* A clone of the DHCPv4 script_write_params() minus the DHCPv4-specific
! 5054: * filename, server-name, etc specifics.
! 5055: *
! 5056: * Simply, store all values present in all universes of the option state
! 5057: * (probably derived from a DHCPv6 packet) into environment variables
! 5058: * named after the option names (and universe names) but with the 'prefix'
! 5059: * prepended.
! 5060: *
! 5061: * Later, dhclient-script may compare for example "new_time_servers" and
! 5062: * "old_time_servers" for differences, and only upon detecting a change
! 5063: * bother to rewrite ntp.conf and restart it. Or something along those
! 5064: * generic lines.
! 5065: */
! 5066: static void
! 5067: script_write_params6(struct client_state *client, const char *prefix,
! 5068: struct option_state *options)
! 5069: {
! 5070: struct envadd_state es;
! 5071: int i;
! 5072:
! 5073: if (options == NULL)
! 5074: return;
! 5075:
! 5076: es.client = client;
! 5077: es.prefix = prefix;
! 5078:
! 5079: for (i = 0 ; i < options->universe_count ; i++) {
! 5080: option_space_foreach(NULL, NULL, client, NULL, options,
! 5081: &global_scope, universes[i], &es,
! 5082: client_option_envadd);
! 5083: }
! 5084: }
! 5085:
! 5086: /*
! 5087: * Check if there is something not fully defined in the active lease.
! 5088: */
! 5089: static isc_boolean_t
! 5090: active_prefix(struct client_state *client)
! 5091: {
! 5092: struct dhc6_lease *lease;
! 5093: struct dhc6_ia *ia;
! 5094: struct dhc6_addr *pref;
! 5095: char zeros[16];
! 5096:
! 5097: lease = client->active_lease;
! 5098: if (lease == NULL)
! 5099: return ISC_FALSE;
! 5100: memset(zeros, 0, 16);
! 5101: for (ia = lease->bindings; ia != NULL; ia = ia->next) {
! 5102: if (ia->ia_type != D6O_IA_PD)
! 5103: continue;
! 5104: for (pref = ia->addrs; pref != NULL; pref = pref->next) {
! 5105: if (pref->plen == 0)
! 5106: return ISC_FALSE;
! 5107: if (pref->address.len != 16)
! 5108: return ISC_FALSE;
! 5109: if (memcmp(pref->address.iabuf, zeros, 16) == 0)
! 5110: return ISC_FALSE;
! 5111: }
! 5112: }
! 5113: return ISC_TRUE;
! 5114: }
! 5115: #endif /* DHCPv6 */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>