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