Annotation of embedaddon/dhcp/server/dhcpleasequery.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (C) 2006-2007 by Internet Systems Consortium, Inc. ("ISC")
! 3: *
! 4: * Permission to use, copy, modify, and distribute this software for any
! 5: * purpose with or without fee is hereby granted, provided that the above
! 6: * copyright notice and this permission notice appear in all copies.
! 7: *
! 8: * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
! 9: * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
! 10: * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
! 11: * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
! 12: * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
! 13: * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
! 14: * PERFORMANCE OF THIS SOFTWARE.
! 15: */
! 16:
! 17: #include "dhcpd.h"
! 18:
! 19: /*
! 20: * TODO: RFC4388 specifies that the server SHOULD return the same
! 21: * options it would for a DHCREQUEST message, if no Parameter
! 22: * Request List option (option 55) is passed. We do not do that.
! 23: *
! 24: * TODO: RFC4388 specifies the creation of a "non-sensitive options"
! 25: * configuration list, and that these SHOULD be returned. We
! 26: * have no such list.
! 27: *
! 28: * TODO: RFC4388 says the server SHOULD use RFC3118, "Authentication
! 29: * for DHCP Messages".
! 30: *
! 31: * TODO: RFC4388 specifies that you SHOULD insure that you cannot be
! 32: * DoS'ed by DHCPLEASEQUERY message.
! 33: */
! 34:
! 35: /*
! 36: * If you query by hardware address or by client ID, then you may have
! 37: * more than one IP address for your query argument. We need to do two
! 38: * things:
! 39: *
! 40: * 1. Find the most recent lease.
! 41: * 2. Find all additional IP addresses for the query argument.
! 42: *
! 43: * We do this by looking through all of the leases associated with a
! 44: * given hardware address or client ID. We use the cltt (client last
! 45: * transaction time) of the lease, which only has a resolution of one
! 46: * second, so we might not actually give the very latest IP.
! 47: */
! 48:
! 49: static struct lease*
! 50: next_hw(const struct lease *lease) {
! 51: /* INSIST(lease != NULL); */
! 52: return lease->n_hw;
! 53: }
! 54:
! 55: static struct lease*
! 56: next_uid(const struct lease *lease) {
! 57: /* INSIST(lease != NULL); */
! 58: return lease->n_uid;
! 59: }
! 60:
! 61: void
! 62: get_newest_lease(struct lease **retval,
! 63: struct lease *lease,
! 64: struct lease *(*next)(const struct lease *)) {
! 65:
! 66: struct lease *p;
! 67: struct lease *newest;
! 68:
! 69: /* INSIST(newest != NULL); */
! 70: /* INSIST(next != NULL); */
! 71:
! 72: *retval = NULL;
! 73:
! 74: if (lease == NULL) {
! 75: return;
! 76: }
! 77:
! 78: newest = lease;
! 79: for (p=next(lease); p != NULL; p=next(p)) {
! 80: if (newest->binding_state == FTS_ACTIVE) {
! 81: if ((p->binding_state == FTS_ACTIVE) &&
! 82: (p->cltt > newest->cltt)) {
! 83: newest = p;
! 84: }
! 85: } else {
! 86: if (p->ends > newest->ends) {
! 87: newest = p;
! 88: }
! 89: }
! 90: }
! 91:
! 92: lease_reference(retval, newest, MDL);
! 93: }
! 94:
! 95: static int
! 96: get_associated_ips(const struct lease *lease,
! 97: struct lease *(*next)(const struct lease *),
! 98: const struct lease *newest,
! 99: u_int32_t *associated_ips,
! 100: unsigned int associated_ips_size) {
! 101:
! 102: const struct lease *p;
! 103: int cnt;
! 104:
! 105: /* INSIST(next != NULL); */
! 106: /* INSIST(associated_ips != NULL); */
! 107:
! 108: if (lease == NULL) {
! 109: return 0;
! 110: }
! 111:
! 112: cnt = 0;
! 113: for (p=lease; p != NULL; p=next(p)) {
! 114: if ((p->binding_state == FTS_ACTIVE) && (p != newest)) {
! 115: if (cnt < associated_ips_size) {
! 116: memcpy(&associated_ips[cnt],
! 117: p->ip_addr.iabuf,
! 118: sizeof(associated_ips[cnt]));
! 119: }
! 120: cnt++;
! 121: }
! 122: }
! 123: return cnt;
! 124: }
! 125:
! 126:
! 127: void
! 128: dhcpleasequery(struct packet *packet, int ms_nulltp) {
! 129: char msgbuf[256];
! 130: char dbg_info[128];
! 131: struct iaddr cip;
! 132: struct iaddr gip;
! 133: struct data_string uid;
! 134: struct hardware h;
! 135: struct lease *tmp_lease;
! 136: struct lease *lease;
! 137: int want_associated_ip;
! 138: int assoc_ip_cnt;
! 139: u_int32_t assoc_ips[40]; /* XXXSK: arbitrary maximum number of IPs */
! 140: const int nassoc_ips = sizeof(assoc_ips) / sizeof(assoc_ips[0]);
! 141:
! 142: unsigned char dhcpMsgType;
! 143: const char *dhcp_msg_type_name;
! 144: struct subnet *subnet;
! 145: struct group *relay_group;
! 146: struct option_state *options;
! 147: struct option_cache *oc;
! 148: int allow_leasequery;
! 149: int ignorep;
! 150: u_int32_t lease_duration;
! 151: u_int32_t time_renewal;
! 152: u_int32_t time_rebinding;
! 153: u_int32_t time_expiry;
! 154: u_int32_t client_last_transaction_time;
! 155: struct sockaddr_in to;
! 156: struct in_addr siaddr;
! 157: struct data_string prl;
! 158: struct data_string *prl_ptr;
! 159:
! 160: int i;
! 161: struct interface_info *interface;
! 162:
! 163: /* INSIST(packet != NULL); */
! 164:
! 165: /*
! 166: * Prepare log information.
! 167: */
! 168: snprintf(msgbuf, sizeof(msgbuf),
! 169: "DHCPLEASEQUERY from %s", inet_ntoa(packet->raw->giaddr));
! 170:
! 171: /*
! 172: * We can't reply if there is no giaddr field.
! 173: */
! 174: if (!packet->raw->giaddr.s_addr) {
! 175: log_info("%s: missing giaddr, ciaddr is %s, no reply sent",
! 176: msgbuf, inet_ntoa(packet->raw->ciaddr));
! 177: return;
! 178: }
! 179:
! 180: /*
! 181: * Initially we use the 'giaddr' subnet options scope to determine if
! 182: * the giaddr-identified relay agent is permitted to perform a
! 183: * leasequery. The subnet is not required, and may be omitted, in
! 184: * which case we are essentially interrogating the root options class
! 185: * to find a globally permit.
! 186: */
! 187: gip.len = sizeof(packet->raw->giaddr);
! 188: memcpy(gip.iabuf, &packet->raw->giaddr, sizeof(packet->raw->giaddr));
! 189:
! 190: subnet = NULL;
! 191: find_subnet(&subnet, gip, MDL);
! 192: if (subnet != NULL)
! 193: relay_group = subnet->group;
! 194: else
! 195: relay_group = root_group;
! 196:
! 197: subnet_dereference(&subnet, MDL);
! 198:
! 199: options = NULL;
! 200: if (!option_state_allocate(&options, MDL)) {
! 201: log_error("No memory for option state.");
! 202: log_info("%s: out of memory, no reply sent", msgbuf);
! 203: return;
! 204: }
! 205:
! 206: execute_statements_in_scope(NULL,
! 207: packet,
! 208: NULL,
! 209: NULL,
! 210: packet->options,
! 211: options,
! 212: &global_scope,
! 213: relay_group,
! 214: NULL);
! 215:
! 216: for (i=packet->class_count-1; i>=0; i--) {
! 217: execute_statements_in_scope(NULL,
! 218: packet,
! 219: NULL,
! 220: NULL,
! 221: packet->options,
! 222: options,
! 223: &global_scope,
! 224: packet->classes[i]->group,
! 225: relay_group);
! 226: }
! 227:
! 228: /*
! 229: * Because LEASEQUERY has some privacy concerns, default to deny.
! 230: */
! 231: allow_leasequery = 0;
! 232:
! 233: /*
! 234: * See if we are authorized to do LEASEQUERY.
! 235: */
! 236: oc = lookup_option(&server_universe, options, SV_LEASEQUERY);
! 237: if (oc != NULL) {
! 238: allow_leasequery = evaluate_boolean_option_cache(&ignorep,
! 239: packet, NULL, NULL, packet->options,
! 240: options, &global_scope, oc, MDL);
! 241: }
! 242:
! 243: if (!allow_leasequery) {
! 244: log_info("%s: LEASEQUERY not allowed, query ignored", msgbuf);
! 245: option_state_dereference(&options, MDL);
! 246: return;
! 247: }
! 248:
! 249:
! 250: /*
! 251: * Copy out the client IP address.
! 252: */
! 253: cip.len = sizeof(packet->raw->ciaddr);
! 254: memcpy(cip.iabuf, &packet->raw->ciaddr, sizeof(packet->raw->ciaddr));
! 255:
! 256: /*
! 257: * If the client IP address is valid (not all zero), then we
! 258: * are looking for information about that IP address.
! 259: */
! 260: assoc_ip_cnt = 0;
! 261: lease = tmp_lease = NULL;
! 262: if (memcmp(cip.iabuf, "\0\0\0", 4)) {
! 263:
! 264: want_associated_ip = 0;
! 265:
! 266: snprintf(dbg_info, sizeof(dbg_info), "IP %s", piaddr(cip));
! 267: find_lease_by_ip_addr(&lease, cip, MDL);
! 268:
! 269:
! 270: } else {
! 271:
! 272: want_associated_ip = 1;
! 273:
! 274: /*
! 275: * If the client IP address is all zero, then we will
! 276: * either look up by the client identifier (if we have
! 277: * one), or by the MAC address.
! 278: */
! 279:
! 280: memset(&uid, 0, sizeof(uid));
! 281: if (get_option(&uid,
! 282: &dhcp_universe,
! 283: packet,
! 284: NULL,
! 285: NULL,
! 286: packet->options,
! 287: NULL,
! 288: packet->options,
! 289: &global_scope,
! 290: DHO_DHCP_CLIENT_IDENTIFIER,
! 291: MDL)) {
! 292:
! 293: snprintf(dbg_info,
! 294: sizeof(dbg_info),
! 295: "client-id %s",
! 296: print_hex_1(uid.len, uid.data, 60));
! 297:
! 298: find_lease_by_uid(&tmp_lease, uid.data, uid.len, MDL);
! 299: data_string_forget(&uid, MDL);
! 300: get_newest_lease(&lease, tmp_lease, next_uid);
! 301: assoc_ip_cnt = get_associated_ips(tmp_lease,
! 302: next_uid,
! 303: lease,
! 304: assoc_ips,
! 305: nassoc_ips);
! 306:
! 307: } else {
! 308:
! 309: if (packet->raw->hlen+1 > sizeof(h.hbuf)) {
! 310: log_info("%s: hardware length too long, "
! 311: "no reply sent", msgbuf);
! 312: option_state_dereference(&options, MDL);
! 313: return;
! 314: }
! 315:
! 316: h.hlen = packet->raw->hlen + 1;
! 317: h.hbuf[0] = packet->raw->htype;
! 318: memcpy(&h.hbuf[1],
! 319: packet->raw->chaddr,
! 320: packet->raw->hlen);
! 321:
! 322: snprintf(dbg_info,
! 323: sizeof(dbg_info),
! 324: "MAC address %s",
! 325: print_hw_addr(h.hbuf[0],
! 326: h.hlen - 1,
! 327: &h.hbuf[1]));
! 328:
! 329: find_lease_by_hw_addr(&tmp_lease, h.hbuf, h.hlen, MDL);
! 330: get_newest_lease(&lease, tmp_lease, next_hw);
! 331: assoc_ip_cnt = get_associated_ips(tmp_lease,
! 332: next_hw,
! 333: lease,
! 334: assoc_ips,
! 335: nassoc_ips);
! 336:
! 337: }
! 338:
! 339: lease_dereference(&tmp_lease, MDL);
! 340:
! 341: if (lease != NULL) {
! 342: memcpy(&packet->raw->ciaddr,
! 343: lease->ip_addr.iabuf,
! 344: sizeof(packet->raw->ciaddr));
! 345: }
! 346:
! 347: /*
! 348: * Log if we have too many IP addresses associated
! 349: * with this client.
! 350: */
! 351: if (want_associated_ip && (assoc_ip_cnt > nassoc_ips)) {
! 352: log_info("%d IP addresses associated with %s, "
! 353: "only %d sent in reply.",
! 354: assoc_ip_cnt, dbg_info, nassoc_ips);
! 355: }
! 356: }
! 357:
! 358: /*
! 359: * We now know the query target too, so can report this in
! 360: * our log message.
! 361: */
! 362: snprintf(msgbuf, sizeof(msgbuf),
! 363: "DHCPLEASEQUERY from %s for %s",
! 364: inet_ntoa(packet->raw->giaddr), dbg_info);
! 365:
! 366: /*
! 367: * Figure our our return type.
! 368: */
! 369: if (lease == NULL) {
! 370: dhcpMsgType = DHCPLEASEUNKNOWN;
! 371: dhcp_msg_type_name = "DHCPLEASEUNKNOWN";
! 372: } else {
! 373: if (lease->binding_state == FTS_ACTIVE) {
! 374: dhcpMsgType = DHCPLEASEACTIVE;
! 375: dhcp_msg_type_name = "DHCPLEASEACTIVE";
! 376: } else {
! 377: dhcpMsgType = DHCPLEASEUNASSIGNED;
! 378: dhcp_msg_type_name = "DHCPLEASEUNASSIGNED";
! 379: }
! 380: }
! 381:
! 382: /*
! 383: * Set options that only make sense if we have an active lease.
! 384: */
! 385:
! 386: if (dhcpMsgType == DHCPLEASEACTIVE)
! 387: {
! 388: /*
! 389: * RFC 4388 uses the PRL to request options for the agent to
! 390: * receive that are "about" the client. It is confusing
! 391: * because in some cases it wants to know what was sent to
! 392: * the client (lease times, adjusted), and in others it wants
! 393: * to know information the client sent. You're supposed to
! 394: * know this on a case-by-case basis.
! 395: *
! 396: * "Name servers", "domain name", and the like from the relay
! 397: * agent's scope seems less than useful. Our options are to
! 398: * restart the option cache from the lease's best point of view
! 399: * (execute statements from the lease pool's group), or to
! 400: * simply restart the option cache from empty.
! 401: *
! 402: * I think restarting the option cache from empty best
! 403: * approaches RFC 4388's intent; specific options are included.
! 404: */
! 405: option_state_dereference(&options, MDL);
! 406:
! 407: if (!option_state_allocate(&options, MDL)) {
! 408: log_error("%s: out of memory, no reply sent", msgbuf);
! 409: lease_dereference(&lease, MDL);
! 410: return;
! 411: }
! 412:
! 413: /*
! 414: * Set the hardware address fields.
! 415: */
! 416:
! 417: packet->raw->hlen = lease->hardware_addr.hlen - 1;
! 418: packet->raw->htype = lease->hardware_addr.hbuf[0];
! 419: memcpy(packet->raw->chaddr,
! 420: &lease->hardware_addr.hbuf[1],
! 421: sizeof(packet->raw->chaddr));
! 422:
! 423: /*
! 424: * Set client identifier option.
! 425: */
! 426: if (lease->uid_len > 0) {
! 427: if (!add_option(options,
! 428: DHO_DHCP_CLIENT_IDENTIFIER,
! 429: lease->uid,
! 430: lease->uid_len)) {
! 431: option_state_dereference(&options, MDL);
! 432: lease_dereference(&lease, MDL);
! 433: log_info("%s: out of memory, no reply sent",
! 434: msgbuf);
! 435: return;
! 436: }
! 437: }
! 438:
! 439:
! 440: /*
! 441: * Calculate T1 and T2, the times when the client
! 442: * tries to extend its lease on its networking
! 443: * address.
! 444: * These seem to be hard-coded in ISC DHCP, to 0.5 and
! 445: * 0.875 of the lease time.
! 446: */
! 447:
! 448: lease_duration = lease->ends - lease->starts;
! 449: time_renewal = lease->starts +
! 450: (lease_duration / 2);
! 451: time_rebinding = lease->starts +
! 452: (lease_duration / 2) +
! 453: (lease_duration / 4) +
! 454: (lease_duration / 8);
! 455:
! 456: if (time_renewal > cur_time) {
! 457: if (time_renewal < cur_time)
! 458: time_renewal = 0;
! 459: else
! 460: time_renewal = htonl(time_renewal - cur_time);
! 461:
! 462: if (!add_option(options,
! 463: DHO_DHCP_RENEWAL_TIME,
! 464: &time_renewal,
! 465: sizeof(time_renewal))) {
! 466: option_state_dereference(&options, MDL);
! 467: lease_dereference(&lease, MDL);
! 468: log_info("%s: out of memory, no reply sent",
! 469: msgbuf);
! 470: return;
! 471: }
! 472: }
! 473:
! 474: if (time_rebinding > cur_time) {
! 475: time_rebinding = htonl(time_rebinding - cur_time);
! 476:
! 477: if (!add_option(options,
! 478: DHO_DHCP_REBINDING_TIME,
! 479: &time_rebinding,
! 480: sizeof(time_rebinding))) {
! 481: option_state_dereference(&options, MDL);
! 482: lease_dereference(&lease, MDL);
! 483: log_info("%s: out of memory, no reply sent",
! 484: msgbuf);
! 485: return;
! 486: }
! 487: }
! 488:
! 489: if (lease->ends > cur_time) {
! 490: if (time_expiry < cur_time) {
! 491: log_error("Impossible condition at %s:%d.",
! 492: MDL);
! 493:
! 494: option_state_dereference(&options, MDL);
! 495: lease_dereference(&lease, MDL);
! 496: return;
! 497: }
! 498: time_expiry = htonl(lease->ends - cur_time);
! 499: if (!add_option(options,
! 500: DHO_DHCP_LEASE_TIME,
! 501: &time_expiry,
! 502: sizeof(time_expiry))) {
! 503: option_state_dereference(&options, MDL);
! 504: lease_dereference(&lease, MDL);
! 505: log_info("%s: out of memory, no reply sent",
! 506: msgbuf);
! 507: return;
! 508: }
! 509: }
! 510:
! 511: /* Supply the Vendor-Class-Identifier. */
! 512: if (lease->scope != NULL) {
! 513: struct data_string vendor_class;
! 514:
! 515: memset(&vendor_class, 0, sizeof(vendor_class));
! 516:
! 517: if (find_bound_string(&vendor_class, lease->scope,
! 518: "vendor-class-identifier")) {
! 519: if (!add_option(options,
! 520: DHO_VENDOR_CLASS_IDENTIFIER,
! 521: (void *)vendor_class.data,
! 522: vendor_class.len)) {
! 523: option_state_dereference(&options,
! 524: MDL);
! 525: lease_dereference(&lease, MDL);
! 526: log_error("%s: error adding vendor "
! 527: "class identifier, no reply "
! 528: "sent", msgbuf);
! 529: data_string_forget(&vendor_class, MDL);
! 530: return;
! 531: }
! 532: data_string_forget(&vendor_class, MDL);
! 533: }
! 534: }
! 535:
! 536: /*
! 537: * Set the relay agent info.
! 538: *
! 539: * Note that because agent info is appended without regard
! 540: * to the PRL in cons_options(), this will be sent as the
! 541: * last option in the packet whether it is listed on PRL or
! 542: * not.
! 543: */
! 544:
! 545: if (lease->agent_options != NULL) {
! 546: int idx = agent_universe.index;
! 547: struct option_chain_head **tmp1 =
! 548: (struct option_chain_head **)
! 549: &(options->universes[idx]);
! 550: struct option_chain_head *tmp2 =
! 551: (struct option_chain_head *)
! 552: lease->agent_options;
! 553:
! 554: option_chain_head_reference(tmp1, tmp2, MDL);
! 555: }
! 556:
! 557: /*
! 558: * Set the client last transaction time.
! 559: * We check to make sure we have a timestamp. For
! 560: * lease files that were saved before running a
! 561: * timestamp-aware version of the server, this may
! 562: * not be set.
! 563: */
! 564:
! 565: if (lease->cltt != MIN_TIME) {
! 566: if (cur_time > lease->cltt) {
! 567: client_last_transaction_time =
! 568: htonl(cur_time - lease->cltt);
! 569: } else {
! 570: client_last_transaction_time = htonl(0);
! 571: }
! 572: if (!add_option(options,
! 573: DHO_CLIENT_LAST_TRANSACTION_TIME,
! 574: &client_last_transaction_time,
! 575: sizeof(client_last_transaction_time))) {
! 576: option_state_dereference(&options, MDL);
! 577: lease_dereference(&lease, MDL);
! 578: log_info("%s: out of memory, no reply sent",
! 579: msgbuf);
! 580: return;
! 581: }
! 582: }
! 583:
! 584: /*
! 585: * Set associated IPs, if requested and there are some.
! 586: */
! 587: if (want_associated_ip && (assoc_ip_cnt > 0)) {
! 588: if (!add_option(options,
! 589: DHO_ASSOCIATED_IP,
! 590: assoc_ips,
! 591: assoc_ip_cnt * sizeof(assoc_ips[0]))) {
! 592: option_state_dereference(&options, MDL);
! 593: lease_dereference(&lease, MDL);
! 594: log_info("%s: out of memory, no reply sent",
! 595: msgbuf);
! 596: return;
! 597: }
! 598: }
! 599: }
! 600:
! 601: /*
! 602: * Set the message type.
! 603: */
! 604:
! 605: packet->raw->op = BOOTREPLY;
! 606:
! 607: /*
! 608: * Set DHCP message type.
! 609: */
! 610: if (!add_option(options,
! 611: DHO_DHCP_MESSAGE_TYPE,
! 612: &dhcpMsgType,
! 613: sizeof(dhcpMsgType))) {
! 614: option_state_dereference(&options, MDL);
! 615: lease_dereference(&lease, MDL);
! 616: log_info("%s: error adding option, no reply sent", msgbuf);
! 617: return;
! 618: }
! 619:
! 620: /*
! 621: * Log the message we've received.
! 622: */
! 623: log_info("%s", msgbuf);
! 624:
! 625: /*
! 626: * Figure out which address to use to send from.
! 627: */
! 628: get_server_source_address(&siaddr, options, packet);
! 629:
! 630: /*
! 631: * Set up the option buffer.
! 632: */
! 633:
! 634: memset(&prl, 0, sizeof(prl));
! 635: oc = lookup_option(&dhcp_universe, options,
! 636: DHO_DHCP_PARAMETER_REQUEST_LIST);
! 637: if (oc != NULL) {
! 638: evaluate_option_cache(&prl,
! 639: packet,
! 640: NULL,
! 641: NULL,
! 642: packet->options,
! 643: options,
! 644: &global_scope,
! 645: oc,
! 646: MDL);
! 647: }
! 648: if (prl.len > 0) {
! 649: prl_ptr = &prl;
! 650: } else {
! 651: prl_ptr = NULL;
! 652: }
! 653:
! 654: packet->packet_length = cons_options(packet,
! 655: packet->raw,
! 656: lease,
! 657: NULL,
! 658: 0,
! 659: packet->options,
! 660: options,
! 661: &global_scope,
! 662: 0,
! 663: 0,
! 664: 0,
! 665: prl_ptr,
! 666: NULL);
! 667:
! 668: data_string_forget(&prl, MDL); /* SK: safe, even if empty */
! 669: option_state_dereference(&options, MDL);
! 670: lease_dereference(&lease, MDL);
! 671:
! 672: to.sin_family = AF_INET;
! 673: #ifdef HAVE_SA_LEN
! 674: to.sin_len = sizeof(to);
! 675: #endif
! 676: memset(to.sin_zero, 0, sizeof(to.sin_zero));
! 677:
! 678: /*
! 679: * Leasequery packets are be sent to the gateway address.
! 680: */
! 681: to.sin_addr = packet->raw->giaddr;
! 682: if (packet->raw->giaddr.s_addr != htonl(INADDR_LOOPBACK)) {
! 683: to.sin_port = local_port;
! 684: } else {
! 685: to.sin_port = remote_port; /* XXXSK: For debugging. */
! 686: }
! 687:
! 688: /*
! 689: * The fallback_interface lets us send with a real IP
! 690: * address. The packet interface sends from all-zeros.
! 691: */
! 692: if (fallback_interface != NULL) {
! 693: interface = fallback_interface;
! 694: } else {
! 695: interface = packet->interface;
! 696: }
! 697:
! 698: /*
! 699: * Report what we're sending.
! 700: */
! 701: log_info("%s to %s for %s (%d associated IPs)",
! 702: dhcp_msg_type_name,
! 703: inet_ntoa(to.sin_addr), dbg_info, assoc_ip_cnt);
! 704:
! 705: send_packet(interface,
! 706: NULL,
! 707: packet->raw,
! 708: packet->packet_length,
! 709: siaddr,
! 710: &to,
! 711: NULL);
! 712: }
! 713:
! 714: #ifdef DHCPv6
! 715:
! 716: /*
! 717: * TODO: RFC5007 query-by-clientid.
! 718: *
! 719: * TODO: RFC5007 look at the pools according to the link-address.
! 720: *
! 721: * TODO: get fixed leases too.
! 722: *
! 723: * TODO: RFC5007 ORO in query-options.
! 724: *
! 725: * TODO: RFC5007 lq-relay-data.
! 726: *
! 727: * TODO: RFC5007 lq-client-link.
! 728: *
! 729: * Note: the code is still nearly compliant and usable for the target
! 730: * case with these missing features!
! 731: */
! 732:
! 733: /*
! 734: * The structure to handle a leasequery.
! 735: */
! 736: struct lq6_state {
! 737: struct packet *packet;
! 738: struct data_string client_id;
! 739: struct data_string server_id;
! 740: struct data_string lq_query;
! 741: uint8_t query_type;
! 742: struct in6_addr link_addr;
! 743: struct option_state *query_opts;
! 744:
! 745: struct option_state *reply_opts;
! 746: unsigned cursor;
! 747: union reply_buffer {
! 748: unsigned char data[65536];
! 749: struct dhcpv6_packet reply;
! 750: } buf;
! 751: };
! 752:
! 753: /*
! 754: * Options that we want to send.
! 755: */
! 756: static const int required_opts_lq[] = {
! 757: D6O_CLIENTID,
! 758: D6O_SERVERID,
! 759: D6O_STATUS_CODE,
! 760: D6O_CLIENT_DATA,
! 761: D6O_LQ_RELAY_DATA,
! 762: D6O_LQ_CLIENT_LINK,
! 763: 0
! 764: };
! 765: static const int required_opt_CLIENT_DATA[] = {
! 766: D6O_CLIENTID,
! 767: D6O_IAADDR,
! 768: D6O_IAPREFIX,
! 769: D6O_CLT_TIME,
! 770: 0
! 771: };
! 772:
! 773: /*
! 774: * Get the lq-query option from the packet.
! 775: */
! 776: static isc_result_t
! 777: get_lq_query(struct lq6_state *lq)
! 778: {
! 779: struct data_string *lq_query = &lq->lq_query;
! 780: struct packet *packet = lq->packet;
! 781: struct option_cache *oc;
! 782:
! 783: /*
! 784: * Verify our lq_query structure is empty.
! 785: */
! 786: if ((lq_query->data != NULL) || (lq_query->len != 0)) {
! 787: return ISC_R_INVALIDARG;
! 788: }
! 789:
! 790: oc = lookup_option(&dhcpv6_universe, packet->options, D6O_LQ_QUERY);
! 791: if (oc == NULL) {
! 792: return ISC_R_NOTFOUND;
! 793: }
! 794:
! 795: if (!evaluate_option_cache(lq_query, packet, NULL, NULL,
! 796: packet->options, NULL,
! 797: &global_scope, oc, MDL)) {
! 798: return ISC_R_FAILURE;
! 799: }
! 800:
! 801: return ISC_R_SUCCESS;
! 802: }
! 803:
! 804: /*
! 805: * Message validation, RFC 5007 section 4.2.1:
! 806: * dhcpv6.c:valid_client_msg() - unicast + lq-query option.
! 807: */
! 808: static int
! 809: valid_query_msg(struct lq6_state *lq) {
! 810: struct packet *packet = lq->packet;
! 811: int ret_val = 0;
! 812: struct option_cache *oc;
! 813:
! 814: /* INSIST((lq != NULL) || (packet != NULL)); */
! 815:
! 816: switch (get_client_id(packet, &lq->client_id)) {
! 817: case ISC_R_SUCCESS:
! 818: break;
! 819: case ISC_R_NOTFOUND:
! 820: log_debug("Discarding %s from %s; "
! 821: "client identifier missing",
! 822: dhcpv6_type_names[packet->dhcpv6_msg_type],
! 823: piaddr(packet->client_addr));
! 824: goto exit;
! 825: default:
! 826: log_error("Error processing %s from %s; "
! 827: "unable to evaluate Client Identifier",
! 828: dhcpv6_type_names[packet->dhcpv6_msg_type],
! 829: piaddr(packet->client_addr));
! 830: goto exit;
! 831: }
! 832:
! 833: oc = lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID);
! 834: if (oc != NULL) {
! 835: if (evaluate_option_cache(&lq->server_id, packet, NULL, NULL,
! 836: packet->options, NULL,
! 837: &global_scope, oc, MDL)) {
! 838: log_debug("Discarding %s from %s; "
! 839: "server identifier found "
! 840: "(CLIENTID %s, SERVERID %s)",
! 841: dhcpv6_type_names[packet->dhcpv6_msg_type],
! 842: piaddr(packet->client_addr),
! 843: print_hex_1(lq->client_id.len,
! 844: lq->client_id.data, 60),
! 845: print_hex_2(lq->server_id.len,
! 846: lq->server_id.data, 60));
! 847: } else {
! 848: log_debug("Discarding %s from %s; "
! 849: "server identifier found "
! 850: "(CLIENTID %s)",
! 851: dhcpv6_type_names[packet->dhcpv6_msg_type],
! 852: print_hex_1(lq->client_id.len,
! 853: lq->client_id.data, 60),
! 854: piaddr(packet->client_addr));
! 855: }
! 856: goto exit;
! 857: }
! 858:
! 859: switch (get_lq_query(lq)) {
! 860: case ISC_R_SUCCESS:
! 861: break;
! 862: case ISC_R_NOTFOUND:
! 863: log_debug("Discarding %s from %s; lq-query missing",
! 864: dhcpv6_type_names[packet->dhcpv6_msg_type],
! 865: piaddr(packet->client_addr));
! 866: goto exit;
! 867: default:
! 868: log_error("Error processing %s from %s; "
! 869: "unable to evaluate LQ-Query",
! 870: dhcpv6_type_names[packet->dhcpv6_msg_type],
! 871: piaddr(packet->client_addr));
! 872: goto exit;
! 873: }
! 874:
! 875: /* looks good */
! 876: ret_val = 1;
! 877:
! 878: exit:
! 879: if (!ret_val) {
! 880: if (lq->client_id.len > 0) {
! 881: data_string_forget(&lq->client_id, MDL);
! 882: }
! 883: if (lq->server_id.len > 0) {
! 884: data_string_forget(&lq->server_id, MDL);
! 885: }
! 886: if (lq->lq_query.len > 0) {
! 887: data_string_forget(&lq->lq_query, MDL);
! 888: }
! 889: }
! 890: return ret_val;
! 891: }
! 892:
! 893: /*
! 894: * Set an error in a status-code option (from set_status_code).
! 895: */
! 896: static int
! 897: set_error(struct lq6_state *lq, u_int16_t code, const char *message) {
! 898: struct data_string d;
! 899: int ret_val;
! 900:
! 901: memset(&d, 0, sizeof(d));
! 902: d.len = sizeof(code) + strlen(message);
! 903: if (!buffer_allocate(&d.buffer, d.len, MDL)) {
! 904: log_fatal("set_error: no memory for status code.");
! 905: }
! 906: d.data = d.buffer->data;
! 907: putUShort(d.buffer->data, code);
! 908: memcpy(d.buffer->data + sizeof(code), message, d.len - sizeof(code));
! 909: if (!save_option_buffer(&dhcpv6_universe, lq->reply_opts,
! 910: d.buffer, (unsigned char *)d.data, d.len,
! 911: D6O_STATUS_CODE, 0)) {
! 912: log_error("set_error: error saving status code.");
! 913: ret_val = 0;
! 914: } else {
! 915: ret_val = 1;
! 916: }
! 917: data_string_forget(&d, MDL);
! 918: return ret_val;
! 919: }
! 920:
! 921: /*
! 922: * Process a by-address lease query.
! 923: */
! 924: static int
! 925: process_lq_by_address(struct lq6_state *lq) {
! 926: struct packet *packet = lq->packet;
! 927: struct option_cache *oc;
! 928: struct ipv6_pool *pool = NULL;
! 929: struct data_string data;
! 930: struct in6_addr addr;
! 931: struct iasubopt *iaaddr = NULL;
! 932: struct option_state *opt_state = NULL;
! 933: u_int32_t lifetime;
! 934: unsigned opt_cursor;
! 935: int ret_val = 0;
! 936:
! 937: /*
! 938: * Get the IAADDR.
! 939: */
! 940: oc = lookup_option(&dhcpv6_universe, lq->query_opts, D6O_IAADDR);
! 941: if (oc == NULL) {
! 942: if (!set_error(lq, STATUS_MalformedQuery,
! 943: "No OPTION_IAADDR.")) {
! 944: log_error("process_lq_by_address: unable "
! 945: "to set MalformedQuery status code.");
! 946: return 0;
! 947: }
! 948: return 1;
! 949: }
! 950: memset(&data, 0, sizeof(data));
! 951: if (!evaluate_option_cache(&data, packet,
! 952: NULL, NULL,
! 953: lq->query_opts, NULL,
! 954: &global_scope, oc, MDL) ||
! 955: (data.len < IAADDR_OFFSET)) {
! 956: log_error("process_lq_by_address: error evaluating IAADDR.");
! 957: goto exit;
! 958: }
! 959: memcpy(&addr, data.data, sizeof(addr));
! 960: data_string_forget(&data, MDL);
! 961:
! 962: /*
! 963: * Find the lease.
! 964: * Note the RFC 5007 says to use the link-address to find the link
! 965: * or the ia-aadr when it is :: but in any case the ia-addr has
! 966: * to be on the link, so we ignore the link-address here.
! 967: */
! 968: if (find_ipv6_pool(&pool, D6O_IA_NA, &addr) != ISC_R_SUCCESS) {
! 969: if (!set_error(lq, STATUS_NotConfigured,
! 970: "Address not in a pool.")) {
! 971: log_error("process_lq_by_address: unable "
! 972: "to set NotConfigured status code.");
! 973: goto exit;
! 974: }
! 975: ret_val = 1;
! 976: goto exit;
! 977: }
! 978: if (iasubopt_hash_lookup(&iaaddr, pool->leases, &addr,
! 979: sizeof(addr), MDL) == 0) {
! 980: ret_val = 1;
! 981: goto exit;
! 982: }
! 983: if ((iaaddr == NULL) || (iaaddr->state != FTS_ACTIVE) ||
! 984: (iaaddr->ia == NULL) || (iaaddr->ia->iaid_duid.len <= 4)) {
! 985: ret_val = 1;
! 986: goto exit;
! 987: }
! 988:
! 989: /*
! 990: * Build the client-data option (with client-id, ia-addr and clt-time).
! 991: */
! 992: if (!option_state_allocate(&opt_state, MDL)) {
! 993: log_error("process_lq_by_address: "
! 994: "no memory for option state.");
! 995: goto exit;
! 996: }
! 997:
! 998: data_string_copy(&data, &iaaddr->ia->iaid_duid, MDL);
! 999: data.data += 4;
! 1000: data.len -= 4;
! 1001: if (!save_option_buffer(&dhcpv6_universe, opt_state,
! 1002: NULL, (unsigned char *)data.data, data.len,
! 1003: D6O_CLIENTID, 0)) {
! 1004: log_error("process_lq_by_address: error saving client ID.");
! 1005: goto exit;
! 1006: }
! 1007: data_string_forget(&data, MDL);
! 1008:
! 1009: data.len = IAADDR_OFFSET;
! 1010: if (!buffer_allocate(&data.buffer, data.len, MDL)) {
! 1011: log_error("process_lq_by_address: no memory for ia-addr.");
! 1012: goto exit;
! 1013: }
! 1014: data.data = data.buffer->data;
! 1015: memcpy(data.buffer->data, &iaaddr->addr, 16);
! 1016: lifetime = iaaddr->prefer;
! 1017: putULong(data.buffer->data + 16, lifetime);
! 1018: lifetime = iaaddr->valid;
! 1019: putULong(data.buffer->data + 20, lifetime);
! 1020: if (!save_option_buffer(&dhcpv6_universe, opt_state,
! 1021: NULL, (unsigned char *)data.data, data.len,
! 1022: D6O_IAADDR, 0)) {
! 1023: log_error("process_lq_by_address: error saving ia-addr.");
! 1024: goto exit;
! 1025: }
! 1026: data_string_forget(&data, MDL);
! 1027:
! 1028: lifetime = htonl(iaaddr->ia->cltt);
! 1029: if (!save_option_buffer(&dhcpv6_universe, opt_state,
! 1030: NULL, (unsigned char *)&lifetime, 4,
! 1031: D6O_CLT_TIME, 0)) {
! 1032: log_error("process_lq_by_address: error saving clt time.");
! 1033: goto exit;
! 1034: }
! 1035:
! 1036: /*
! 1037: * Store the client-data option.
! 1038: */
! 1039: opt_cursor = lq->cursor;
! 1040: putUShort(lq->buf.data + lq->cursor, (unsigned)D6O_CLIENT_DATA);
! 1041: lq->cursor += 2;
! 1042: /* Skip option length. */
! 1043: lq->cursor += 2;
! 1044:
! 1045: lq->cursor += store_options6((char *)lq->buf.data + lq->cursor,
! 1046: sizeof(lq->buf) - lq->cursor,
! 1047: opt_state, lq->packet,
! 1048: required_opt_CLIENT_DATA, NULL);
! 1049: /* Reset the length. */
! 1050: putUShort(lq->buf.data + opt_cursor + 2,
! 1051: lq->cursor - (opt_cursor + 4));
! 1052:
! 1053: /* Done. */
! 1054: ret_val = 1;
! 1055:
! 1056: exit:
! 1057: if (data.data != NULL)
! 1058: data_string_forget(&data, MDL);
! 1059: if (pool != NULL)
! 1060: ipv6_pool_dereference(&pool, MDL);
! 1061: if (iaaddr != NULL)
! 1062: iasubopt_dereference(&iaaddr, MDL);
! 1063: if (opt_state != NULL)
! 1064: option_state_dereference(&opt_state, MDL);
! 1065: return ret_val;
! 1066: }
! 1067:
! 1068:
! 1069: /*
! 1070: * Process a lease query.
! 1071: */
! 1072: void
! 1073: dhcpv6_leasequery(struct data_string *reply_ret, struct packet *packet) {
! 1074: static struct lq6_state lq;
! 1075: struct option_cache *oc;
! 1076: int allow_lq;
! 1077:
! 1078: /*
! 1079: * Initialize the lease query state.
! 1080: */
! 1081: lq.packet = NULL;
! 1082: memset(&lq.client_id, 0, sizeof(lq.client_id));
! 1083: memset(&lq.server_id, 0, sizeof(lq.server_id));
! 1084: memset(&lq.lq_query, 0, sizeof(lq.lq_query));
! 1085: lq.query_opts = NULL;
! 1086: lq.reply_opts = NULL;
! 1087: packet_reference(&lq.packet, packet, MDL);
! 1088:
! 1089: /*
! 1090: * Validate our input.
! 1091: */
! 1092: if (!valid_query_msg(&lq)) {
! 1093: goto exit;
! 1094: }
! 1095:
! 1096: /*
! 1097: * Prepare our reply.
! 1098: */
! 1099: if (!option_state_allocate(&lq.reply_opts, MDL)) {
! 1100: log_error("dhcpv6_leasequery: no memory for option state.");
! 1101: goto exit;
! 1102: }
! 1103: execute_statements_in_scope(NULL, lq.packet, NULL, NULL,
! 1104: lq.packet->options, lq.reply_opts,
! 1105: &global_scope, root_group, NULL);
! 1106:
! 1107: lq.buf.reply.msg_type = DHCPV6_LEASEQUERY_REPLY;
! 1108:
! 1109: memcpy(lq.buf.reply.transaction_id,
! 1110: lq.packet->dhcpv6_transaction_id,
! 1111: sizeof(lq.buf.reply.transaction_id));
! 1112:
! 1113: /*
! 1114: * Because LEASEQUERY has some privacy concerns, default to deny.
! 1115: */
! 1116: allow_lq = 0;
! 1117:
! 1118: /*
! 1119: * See if we are authorized to do LEASEQUERY.
! 1120: */
! 1121: oc = lookup_option(&server_universe, lq.reply_opts, SV_LEASEQUERY);
! 1122: if (oc != NULL) {
! 1123: allow_lq = evaluate_boolean_option_cache(NULL,
! 1124: lq.packet,
! 1125: NULL, NULL,
! 1126: lq.packet->options,
! 1127: lq.reply_opts,
! 1128: &global_scope,
! 1129: oc, MDL);
! 1130: }
! 1131:
! 1132: if (!allow_lq) {
! 1133: log_info("dhcpv6_leasequery: not allowed, query ignored.");
! 1134: goto exit;
! 1135: }
! 1136:
! 1137: /*
! 1138: * Same than transmission of REPLY message in RFC 3315:
! 1139: * server-id
! 1140: * client-id
! 1141: */
! 1142:
! 1143: oc = lookup_option(&dhcpv6_universe, lq.reply_opts, D6O_SERVERID);
! 1144: if (oc == NULL) {
! 1145: /* If not already in options, get from query then global. */
! 1146: if (lq.server_id.data == NULL)
! 1147: copy_server_duid(&lq.server_id, MDL);
! 1148: if (!save_option_buffer(&dhcpv6_universe,
! 1149: lq.reply_opts,
! 1150: NULL,
! 1151: (unsigned char *)lq.server_id.data,
! 1152: lq.server_id.len,
! 1153: D6O_SERVERID,
! 1154: 0)) {
! 1155: log_error("dhcpv6_leasequery: "
! 1156: "error saving server identifier.");
! 1157: goto exit;
! 1158: }
! 1159: }
! 1160:
! 1161: if (!save_option_buffer(&dhcpv6_universe,
! 1162: lq.reply_opts,
! 1163: lq.client_id.buffer,
! 1164: (unsigned char *)lq.client_id.data,
! 1165: lq.client_id.len,
! 1166: D6O_CLIENTID,
! 1167: 0)) {
! 1168: log_error("dhcpv6_leasequery: "
! 1169: "error saving client identifier.");
! 1170: goto exit;
! 1171: }
! 1172:
! 1173: lq.cursor = 4;
! 1174:
! 1175: /*
! 1176: * Decode the lq-query option.
! 1177: */
! 1178:
! 1179: if (lq.lq_query.len <= LQ_QUERY_OFFSET) {
! 1180: if (!set_error(&lq, STATUS_MalformedQuery,
! 1181: "OPTION_LQ_QUERY too short.")) {
! 1182: log_error("dhcpv6_leasequery: unable "
! 1183: "to set MalformedQuery status code.");
! 1184: goto exit;
! 1185: }
! 1186: goto done;
! 1187: }
! 1188:
! 1189: lq.query_type = lq.lq_query.data [0];
! 1190: memcpy(&lq.link_addr, lq.lq_query.data + 1, sizeof(lq.link_addr));
! 1191: switch (lq.query_type) {
! 1192: case LQ6QT_BY_ADDRESS:
! 1193: break;
! 1194: case LQ6QT_BY_CLIENTID:
! 1195: if (!set_error(&lq, STATUS_UnknownQueryType,
! 1196: "QUERY_BY_CLIENTID not supported.")) {
! 1197: log_error("dhcpv6_leasequery: unable to "
! 1198: "set UnknownQueryType status code.");
! 1199: goto exit;
! 1200: }
! 1201: goto done;
! 1202: default:
! 1203: if (!set_error(&lq, STATUS_UnknownQueryType,
! 1204: "Unknown query-type.")) {
! 1205: log_error("dhcpv6_leasequery: unable to "
! 1206: "set UnknownQueryType status code.");
! 1207: goto exit;
! 1208: }
! 1209: goto done;
! 1210: }
! 1211:
! 1212: if (!option_state_allocate(&lq.query_opts, MDL)) {
! 1213: log_error("dhcpv6_leasequery: no memory for option state.");
! 1214: goto exit;
! 1215: }
! 1216: if (!parse_option_buffer(lq.query_opts,
! 1217: lq.lq_query.data + LQ_QUERY_OFFSET,
! 1218: lq.lq_query.len - LQ_QUERY_OFFSET,
! 1219: &dhcpv6_universe)) {
! 1220: log_error("dhcpv6_leasequery: error parsing query-options.");
! 1221: if (!set_error(&lq, STATUS_MalformedQuery,
! 1222: "Bad query-options.")) {
! 1223: log_error("dhcpv6_leasequery: unable "
! 1224: "to set MalformedQuery status code.");
! 1225: goto exit;
! 1226: }
! 1227: goto done;
! 1228: }
! 1229:
! 1230: /* Do it. */
! 1231: if (!process_lq_by_address(&lq))
! 1232: goto exit;
! 1233:
! 1234: done:
! 1235: /* Store the options. */
! 1236: lq.cursor += store_options6((char *)lq.buf.data + lq.cursor,
! 1237: sizeof(lq.buf) - lq.cursor,
! 1238: lq.reply_opts,
! 1239: lq.packet,
! 1240: required_opts_lq,
! 1241: NULL);
! 1242:
! 1243: /* Return our reply to the caller. */
! 1244: reply_ret->len = lq.cursor;
! 1245: reply_ret->buffer = NULL;
! 1246: if (!buffer_allocate(&reply_ret->buffer, lq.cursor, MDL)) {
! 1247: log_fatal("dhcpv6_leasequery: no memory to store Reply.");
! 1248: }
! 1249: memcpy(reply_ret->buffer->data, lq.buf.data, lq.cursor);
! 1250: reply_ret->data = reply_ret->buffer->data;
! 1251:
! 1252: exit:
! 1253: /* Cleanup. */
! 1254: if (lq.packet != NULL)
! 1255: packet_dereference(&lq.packet, MDL);
! 1256: if (lq.client_id.data != NULL)
! 1257: data_string_forget(&lq.client_id, MDL);
! 1258: if (lq.server_id.data != NULL)
! 1259: data_string_forget(&lq.server_id, MDL);
! 1260: if (lq.lq_query.data != NULL)
! 1261: data_string_forget(&lq.lq_query, MDL);
! 1262: if (lq.query_opts != NULL)
! 1263: option_state_dereference(&lq.query_opts, MDL);
! 1264: if (lq.reply_opts != NULL)
! 1265: option_state_dereference(&lq.reply_opts, MDL);
! 1266: }
! 1267:
! 1268: #endif /* DHCPv6 */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>