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